Matthew Raymer
1 year ago
8 changed files with 276 additions and 4 deletions
@ -0,0 +1,3 @@ |
|||
[submodule "src/types"] |
|||
path = src/types |
|||
url = ssh://git@173.199.124.46:222/trent_larson/endorser-types.git |
@ -0,0 +1,28 @@ |
|||
import { PlaneGeometry, MeshLambertMaterial, Mesh, TextureLoader, Color } from "three"; |
|||
|
|||
class Terrain { |
|||
constructor(props: { width: number; height: number; color: Color }) { |
|||
const loader = new TextureLoader(); |
|||
const heightTexture = loader.load("img/textures/leafy-autumn-forest-floor.jpg"); |
|||
const geometry = new PlaneGeometry(props.width, props.height, 64, 64); |
|||
|
|||
const material = new MeshLambertMaterial({ |
|||
color: props.color, |
|||
flatShading: true, |
|||
map: heightTexture, |
|||
}); |
|||
|
|||
const plane = new Mesh(geometry, material); |
|||
plane.position.set(0, 0, 0); |
|||
plane.rotation.x -= Math.PI * 0.5; |
|||
|
|||
// Storing our original vertices position on a new attribute
|
|||
plane.geometry.attributes.position.originalPosition = plane.geometry.attributes.position.array; |
|||
|
|||
plane.tick = () => {}; |
|||
|
|||
return plane; |
|||
} |
|||
} |
|||
|
|||
export { Terrain }; |
@ -0,0 +1,137 @@ |
|||
import { GLTF, GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'; |
|||
import { SkeletonUtils } from 'three/examples/jsm/utils/SkeletonUtils'; |
|||
import axios from 'axios'; |
|||
import TWEEN from '@tweenjs/tween.js'; |
|||
import * as THREE from 'three' |
|||
|
|||
class LandmarksLoader { |
|||
async load(vue, world, scene, loop, token) { |
|||
vue.setWorldProperty("animationDurationSeconds", ANIMATION_DURATION_SECS); |
|||
|
|||
try { |
|||
const url = apiServer + "/api/v2/report/claims?claimType=GiveAction"; |
|||
const headers = { |
|||
"Content-Type": "application/json", |
|||
Authorization: "Bearer " + token, |
|||
}; |
|||
const resp = await axios.get(url, { headers: headers }); |
|||
if (resp.status === 200) { |
|||
const landmarks = resp.data.data; |
|||
|
|||
const minDate = landmarks[landmarks.length - 1].issuedAt; |
|||
const maxDate = landmarks[0].issuedAt; |
|||
|
|||
world.setExposedWorldProperties("startTime", minDate.replace("T", " ")); |
|||
world.setExposedWorldProperties("endTime", maxDate.replace("T", " ")); |
|||
|
|||
const minTimeMillis = new Date(minDate).getTime(); |
|||
const fullTimeMillis = |
|||
maxDate > minDate ? new Date(maxDate).getTime() - minTimeMillis : 1; // avoid divide by zero
|
|||
// 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
|
|||
const 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;
|
|||
|
|||
// calculate positions for each claim, especially because some are random
|
|||
const locations = landmarks.map((claim: GiveServerRecord) => |
|||
locForGive(claim, world.PLATFORM_SIZE, world.PLATFORM_EDGE_FOR_UNKNOWNS) |
|||
); |
|||
|
|||
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|||
loader.load( |
|||
modelLoc, |
|||
function (gltf: GLTF) { |
|||
gltf.scene.scale.set(0, 0, 0); |
|||
for (let i = 0; i < landmarks.length; i++) { |
|||
// claim is a GiveServerRecord (see endorserServer.ts)
|
|||
const claim = landmarks[i]; |
|||
const newPlant = SkeletonUtils.clone(gltf.scene); |
|||
|
|||
const loc = locations[i]; |
|||
newPlant.position.set(loc.x, 0, loc.z); |
|||
|
|||
world.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(); |
|||
world.bushes = [...world.bushes, newPlant]; |
|||
} |
|||
}, |
|||
undefined, |
|||
function (error: ErrorEvent) { |
|||
console.error(error); |
|||
} |
|||
); |
|||
|
|||
// calculate when lights shine on appearing claim area
|
|||
for (let i = 0; i < landmarks.length; i++) { |
|||
// claim is a GiveServerRecord (see endorserServer.ts)
|
|||
const claim = landmarks[i]; |
|||
|
|||
const loc = locations[i]; |
|||
const light = this.createLight(); |
|||
light.position.set(loc.x, 20, loc.z); |
|||
light.target.position.set(loc.x, 0, loc.z); |
|||
loop.updatables.push(light); |
|||
scene.add(light); |
|||
scene.add(light.target); |
|||
|
|||
// now figure out the timing and shine a light
|
|||
const timeDelayMillis = |
|||
fakeRealRatio * (new Date(claim.issuedAt).getTime() - minTimeMillis); |
|||
new TWEEN.Tween(light) |
|||
.delay(timeDelayMillis) |
|||
.to({ intensity: 100 }, 10) |
|||
.chain( |
|||
new TWEEN.Tween(light.position) |
|||
.to({ y: 5 }, 5000) |
|||
.onComplete(() => { |
|||
scene.remove(light); |
|||
light.dispose(); |
|||
}) |
|||
) |
|||
.start(); |
|||
world.lights = [...world.lights, light]; |
|||
} |
|||
} else { |
|||
console.error( |
|||
"Got bad server response status & data of", |
|||
resp.status, |
|||
resp.data |
|||
); |
|||
vue.setAlert( |
|||
"Error With Server", |
|||
"There was an error retrieving your claims from the server." |
|||
); |
|||
} |
|||
} catch (error) { |
|||
console.error("Got exception contacting server:", error); |
|||
vue.setAlert( |
|||
"Error With Server", |
|||
"There was a problem retrieving your claims from the server." |
|||
); |
|||
} |
|||
} |
|||
|
|||
private createLight() { |
|||
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; |
|||
} |
|||
|
|||
} |
Loading…
Reference in new issue