diff --git a/README.md b/README.md index 8e75e13..7648ce3 100644 --- a/README.md +++ b/README.md @@ -189,3 +189,5 @@ export const createAndStoreIdentifier = async (mnemonicPassword) => { ## Kudos * [Máximo Fernández](https://medium.com/@maxfarenas) for the 3D [code](https://github.com/maxfer03/vue-three-ns) and [explanatory post](https://medium.com/nicasource/building-an-interactive-web-portfolio-with-vue-three-js-part-three-implementing-three-js-452cb375ef80) +* [Many libraries]() such as Veramo.io, Vuejs.org, threejs +* [Bush 3D model](https://sketchfab.com/3d-models/lupine-plant-bf30f1110c174d4baedda0ed63778439) diff --git a/public/models/lupine_plant/license.txt b/public/models/lupine_plant/license.txt new file mode 100644 index 0000000..9a6980c --- /dev/null +++ b/public/models/lupine_plant/license.txt @@ -0,0 +1,11 @@ +Model Information: +* title: Lupine Plant +* source: https://sketchfab.com/3d-models/lupine-plant-bf30f1110c174d4baedda0ed63778439 +* author: rufusrockwell (https://sketchfab.com/rufusrockwell) + +Model License: +* license type: CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/) +* requirements: Author must be credited. Commercial use is allowed. + +If you use this 3D model in your project be sure to copy paste this credit wherever you share it: +This work is based on "Lupine Plant" (https://sketchfab.com/3d-models/lupine-plant-bf30f1110c174d4baedda0ed63778439) by rufusrockwell (https://sketchfab.com/rufusrockwell) licensed under CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/) \ No newline at end of file diff --git a/public/models/lupine_plant/scene.bin b/public/models/lupine_plant/scene.bin new file mode 100644 index 0000000..e523e7d Binary files /dev/null and b/public/models/lupine_plant/scene.bin differ diff --git a/public/models/lupine_plant/scene.gltf b/public/models/lupine_plant/scene.gltf new file mode 100644 index 0000000..0ec1997 --- /dev/null +++ b/public/models/lupine_plant/scene.gltf @@ -0,0 +1,229 @@ +{ + "accessors": [ + { + "bufferView": 2, + "componentType": 5126, + "count": 2759, + "max": [ + 41.3074951171875, + 40.37548828125, + 87.85917663574219 + ], + "min": [ + -35.245540618896484, + -36.895416259765625, + -0.9094290137290955 + ], + "type": "VEC3" + }, + { + "bufferView": 2, + "byteOffset": 33108, + "componentType": 5126, + "count": 2759, + "max": [ + 0.9999382495880127, + 0.9986748695373535, + 0.9985831379890442 + ], + "min": [ + -0.9998949766159058, + -0.9975876212120056, + -0.411094069480896 + ], + "type": "VEC3" + }, + { + "bufferView": 3, + "componentType": 5126, + "count": 2759, + "max": [ + 0.9987699389457703, + 0.9998998045921326, + 0.9577858448028564, + 1.0 + ], + "min": [ + -0.9987726807594299, + -0.9990445971488953, + -0.999801516532898, + 1.0 + ], + "type": "VEC4" + }, + { + "bufferView": 1, + "componentType": 5126, + "count": 2759, + "max": [ + 1.0061479806900024, + 0.9993550181388855 + ], + "min": [ + 0.00279300007969141, + 0.0011620000004768372 + ], + "type": "VEC2" + }, + { + "bufferView": 0, + "componentType": 5125, + "count": 6378, + "type": "SCALAR" + } + ], + "asset": { + "extras": { + "author": "rufusrockwell (https://sketchfab.com/rufusrockwell)", + "license": "CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/)", + "source": "https://sketchfab.com/3d-models/lupine-plant-bf30f1110c174d4baedda0ed63778439", + "title": "Lupine Plant" + }, + "generator": "Sketchfab-12.68.0", + "version": "2.0" + }, + "bufferViews": [ + { + "buffer": 0, + "byteLength": 25512, + "name": "floatBufferViews", + "target": 34963 + }, + { + "buffer": 0, + "byteLength": 22072, + "byteOffset": 25512, + "byteStride": 8, + "name": "floatBufferViews", + "target": 34962 + }, + { + "buffer": 0, + "byteLength": 66216, + "byteOffset": 47584, + "byteStride": 12, + "name": "floatBufferViews", + "target": 34962 + }, + { + "buffer": 0, + "byteLength": 44144, + "byteOffset": 113800, + "byteStride": 16, + "name": "floatBufferViews", + "target": 34962 + } + ], + "buffers": [ + { + "byteLength": 157944, + "uri": "scene.bin" + } + ], + "images": [ + { + "uri": "textures/lambert2SG_baseColor.png" + }, + { + "uri": "textures/lambert2SG_normal.png" + } + ], + "materials": [ + { + "alphaCutoff": 0.2, + "alphaMode": "MASK", + "doubleSided": true, + "name": "lambert2SG", + "normalTexture": { + "index": 1 + }, + "pbrMetallicRoughness": { + "baseColorTexture": { + "index": 0 + }, + "metallicFactor": 0.0 + } + } + ], + "meshes": [ + { + "name": "Object_0", + "primitives": [ + { + "attributes": { + "NORMAL": 1, + "POSITION": 0, + "TANGENT": 2, + "TEXCOORD_0": 3 + }, + "indices": 4, + "material": 0, + "mode": 4 + } + ] + } + ], + "nodes": [ + { + "children": [ + 1 + ], + "matrix": [ + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 2.220446049250313e-16, + -1.0, + 0.0, + 0.0, + 1.0, + 2.220446049250313e-16, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0 + ], + "name": "Sketchfab_model" + }, + { + "children": [ + 2 + ], + "name": "LupineSF.obj.cleaner.materialmerger.gles" + }, + { + "mesh": 0, + "name": "Object_2" + } + ], + "samplers": [ + { + "magFilter": 9729, + "minFilter": 9987, + "wrapS": 10497, + "wrapT": 10497 + } + ], + "scene": 0, + "scenes": [ + { + "name": "Sketchfab_Scene", + "nodes": [ + 0 + ] + } + ], + "textures": [ + { + "sampler": 0, + "source": 0 + }, + { + "sampler": 0, + "source": 1 + } + ] +} diff --git a/public/models/lupine_plant/textures/lambert2SG_baseColor.png b/public/models/lupine_plant/textures/lambert2SG_baseColor.png new file mode 100644 index 0000000..211290e Binary files /dev/null and b/public/models/lupine_plant/textures/lambert2SG_baseColor.png differ diff --git a/public/models/lupine_plant/textures/lambert2SG_normal.png b/public/models/lupine_plant/textures/lambert2SG_normal.png new file mode 100644 index 0000000..a4e6ffe Binary files /dev/null and b/public/models/lupine_plant/textures/lambert2SG_normal.png differ diff --git a/src/components/World/World.js b/src/components/World/World.js index a43512a..448ed19 100644 --- a/src/components/World/World.js +++ b/src/components/World/World.js @@ -1,8 +1,10 @@ // from https://medium.com/nicasource/building-an-interactive-web-portfolio-with-vue-three-js-part-three-implementing-three-js-452cb375ef80 -import axios from "axios"; -import { SpotLight } from "three"; import * as TWEEN from "@tweenjs/tween.js"; +import axios from "axios"; +import * as THREE from "three"; +import { GLTFLoader } from "three/addons/loaders/GLTFLoader"; +import * as SkeletonUtils from "three/addons/utils/SkeletonUtils.js"; import { AppString } from "@/constants/app"; import { createCamera } from "./components/camera.js"; @@ -14,16 +16,16 @@ import { Resizer } from "./systems/Resizer.js"; import { createControls } from "./systems/controls.js"; import { createRenderer } from "./systems/renderer.js"; -const ANIMATION_DURATION_SECS = 10; +const ANIMATION_DURATION_SECS = 20; const BASE32 = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"; -const COLOR1 = "#42b883"; +const COLOR1 = "#dddddd"; const COLOR2 = "#0055aa"; const PLATFORM_BORDER = 10; const PLATFORM_EDGE_FOR_UNKNOWNS = 10; const PLATFORM_SIZE = 100; function createLight() { - const light = new SpotLight(0xffffff, 0, 0, Math.PI / 8, 0.5, 0); + const light = new THREE.SpotLight(0xffffff, 0, 0, Math.PI / 8, 0.5, 0); // eslint-disable-next-line @typescript-eslint/no-empty-function light.tick = () => {}; return light; @@ -38,8 +40,12 @@ class World { this.scene = createScene(COLOR2); this.renderer = createRenderer(); + // necessary for models, says https://threejs.org/docs/index.html#examples/en/loaders/GLTFLoader + this.renderer.outputColorSpace = THREE.SRGBColorSpace; + this.light = null; this.lights = []; + this.bushes = []; // Initializae Loop this.loop = new Loop(this.camera, this.scene, this.renderer); @@ -82,6 +88,9 @@ class World { light.updateMatrixWorld(); light.target.updateMatrixWorld(); }); + this.lights.forEach((bush) => { + bush.updateMatrixWorld(); + }); requestAnimationFrame(this.update); } @@ -100,6 +109,50 @@ class World { // ratio of animation time to real time const fakeRealRatio = (ANIMATION_DURATION_SECS * 1000) / fullTimeMillis; + // load plant model first because it takes a second + const loader = new GLTFLoader(); + // choose the right plant + const modelLoc = "/models/lupine_plant/scene.gltf", // push with pokies + modScale = 0.1; + //const modelLoc = "/models/round_bush/scene.gltf", // green & pink + // modScale = 1; + //const modelLoc = "/models/coreopsis-flower.glb", // 3 flowers + // modScale = 2; + //const modelLoc = "/models/a_bush/scene.gltf", // purple leaves + // modScale = 15, + + // eslint-disable-next-line @typescript-eslint/no-this-alias + const parentWorld = this; + loader.load( + modelLoc, + function (gltf) { + gltf.scene.scale.set(0, 0, 0); + for (const claim of resp.data.data) { + const newPlant = SkeletonUtils.clone(gltf.scene); + const randomness = claim.id.substring(10); + const x = + (100 * BASE32.indexOf(randomness.substring(0, 1))) / 32 - 50; + const z = + (100 * BASE32.indexOf(randomness.substring(8, 9))) / 32 - 50; + newPlant.position.set(x, 0, z); + + parentWorld.scene.add(newPlant); + const timeDelayMillis = + fakeRealRatio * + (new Date(claim.issuedAt).getTime() - minTimeMillis); + new TWEEN.Tween(newPlant.scale) + .delay(timeDelayMillis) + .to({ x: modScale, y: modScale, z: modScale }, 5000) + .start(); + parentWorld.bushes = [...parentWorld.bushes, newPlant]; + } + }, + undefined, + function (error) { + console.error(error); + } + ); + // calculate when lights shine on appearing claim area for (const claim of resp.data.data) { // claim is a GiveServerRecord (see endorserServer.ts) @@ -126,7 +179,7 @@ class World { .to({ intensity: 100 }, 10) .chain( new TWEEN.Tween(light.position) - .to({ y: 5 }, 1000) + .to({ y: 5 }, 5000) .onComplete(() => { this.scene.remove(light); light.dispose(); @@ -135,7 +188,6 @@ class World { .start(); this.lights = [...this.lights, light]; } - console.log("done adding cube"); } else { console.log( "Got bad response status & data of", diff --git a/src/components/World/components/lights.js b/src/components/World/components/lights.js index 1628448..bcc38fb 100644 --- a/src/components/World/components/lights.js +++ b/src/components/World/components/lights.js @@ -3,7 +3,7 @@ import { DirectionalLight, DirectionalLightHelper } from "three"; function createLights(color) { const light = new DirectionalLight(color, 4); const lightHelper = new DirectionalLightHelper(light, 0); - light.position.set(0, 30, 30); + light.position.set(60, 60, 30); // eslint-disable-next-line @typescript-eslint/no-empty-function light.tick = () => {}; diff --git a/src/components/World/components/objects/terrain.js b/src/components/World/components/objects/terrain.js index 11f3efd..b7d1f54 100644 --- a/src/components/World/components/objects/terrain.js +++ b/src/components/World/components/objects/terrain.js @@ -1,10 +1,4 @@ -import { - BoxGeometry, - PlaneGeometry, - MeshLambertMaterial, - Mesh, - TextureLoader, -} from "three"; +import { PlaneGeometry, MeshLambertMaterial, Mesh, TextureLoader } from "three"; export function createTerrain(props) { const loader = new TextureLoader();