forked from trent_larson/crowd-funder-for-time-pwa
in stats-world, refactor claim-loading code to separate file (no logic changes)
This commit is contained in:
@@ -1,35 +1,23 @@
|
|||||||
// from https://medium.com/nicasource/building-an-interactive-web-portfolio-with-vue-three-js-part-three-implementing-three-js-452cb375ef80
|
// from https://medium.com/nicasource/building-an-interactive-web-portfolio-with-vue-three-js-part-three-implementing-three-js-452cb375ef80
|
||||||
|
|
||||||
import * as TWEEN from "@tweenjs/tween.js";
|
import * as TWEEN from "@tweenjs/tween.js";
|
||||||
import axios from "axios";
|
|
||||||
import * as THREE from "three";
|
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";
|
import { createCamera } from "./components/camera.js";
|
||||||
import { createLights } from "./components/lights.js";
|
import { createLights } from "./components/lights.js";
|
||||||
import { createScene } from "./components/scene.js";
|
import { createScene } from "./components/scene.js";
|
||||||
|
import { loadLandmarks } from "./components/objects/landmarks.js";
|
||||||
import { createTerrain } from "./components/objects/terrain.js";
|
import { createTerrain } from "./components/objects/terrain.js";
|
||||||
import { Loop } from "./systems/Loop.js";
|
import { Loop } from "./systems/Loop.js";
|
||||||
import { Resizer } from "./systems/Resizer.js";
|
import { Resizer } from "./systems/Resizer.js";
|
||||||
import { createControls } from "./systems/controls.js";
|
import { createControls } from "./systems/controls.js";
|
||||||
import { createRenderer } from "./systems/renderer.js";
|
import { createRenderer } from "./systems/renderer.js";
|
||||||
|
|
||||||
const ANIMATION_DURATION_SECS = 10;
|
|
||||||
const BASE32 = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
|
|
||||||
const COLOR1 = "#dddddd";
|
const COLOR1 = "#dddddd";
|
||||||
const COLOR2 = "#0055aa";
|
const COLOR2 = "#0055aa";
|
||||||
const PLATFORM_BORDER = 10;
|
const PLATFORM_BORDER = 10;
|
||||||
const PLATFORM_EDGE_FOR_UNKNOWNS = 10;
|
const PLATFORM_EDGE_FOR_UNKNOWNS = 10;
|
||||||
const PLATFORM_SIZE = 100;
|
const PLATFORM_SIZE = 100; // note that the loadLandmarks calculations may still assume 100
|
||||||
|
|
||||||
function 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
class World {
|
class World {
|
||||||
constructor(container, vue) {
|
constructor(container, vue) {
|
||||||
@@ -72,7 +60,7 @@ class World {
|
|||||||
|
|
||||||
this.scene.add(light, terrain);
|
this.scene.add(light, terrain);
|
||||||
|
|
||||||
this.loadClaims(vue);
|
loadLandmarks(vue, this, this.scene, this.loop);
|
||||||
|
|
||||||
requestAnimationFrame(this.update);
|
requestAnimationFrame(this.update);
|
||||||
|
|
||||||
@@ -95,120 +83,6 @@ class World {
|
|||||||
requestAnimationFrame(this.update);
|
requestAnimationFrame(this.update);
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadClaims(vue) {
|
|
||||||
const endorserApiServer = AppString.DEFAULT_ENDORSER_API_SERVER;
|
|
||||||
try {
|
|
||||||
const url =
|
|
||||||
endorserApiServer + "/api/v2/report/claims?claimType=GiveAction";
|
|
||||||
const headers = { "Content-Type": "application/json" };
|
|
||||||
const resp = await axios.get(url, { headers: headers });
|
|
||||||
if (resp.status === 200) {
|
|
||||||
const minDate = resp.data.data[resp.data.data.length - 1].issuedAt;
|
|
||||||
const maxDate = resp.data.data[0].issuedAt;
|
|
||||||
const minTimeMillis = new Date(minDate).getTime();
|
|
||||||
const fullTimeMillis = new Date(maxDate).getTime() - minTimeMillis;
|
|
||||||
// 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)
|
|
||||||
|
|
||||||
// compute location for this claim
|
|
||||||
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;
|
|
||||||
const light = createLight();
|
|
||||||
light.position.set(x, 20, z);
|
|
||||||
light.target.position.set(x, 0, z);
|
|
||||||
this.loop.updatables.push(light);
|
|
||||||
this.scene.add(light);
|
|
||||||
this.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(() => {
|
|
||||||
this.scene.remove(light);
|
|
||||||
light.dispose();
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.start();
|
|
||||||
this.lights = [...this.lights, light];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.log(
|
|
||||||
"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.log("Got exception contacting server:", error);
|
|
||||||
vue.setAlert(
|
|
||||||
"Error With Server",
|
|
||||||
"There was a problem retrieving your claims from the server."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
// draw a single frame
|
// draw a single frame
|
||||||
this.renderer.render(this.scene, this.camera);
|
this.renderer.render(this.scene, this.camera);
|
||||||
|
|||||||
127
src/components/World/components/objects/landmarks.js
Normal file
127
src/components/World/components/objects/landmarks.js
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
import axios from "axios";
|
||||||
|
import * as THREE from "three";
|
||||||
|
import { GLTFLoader } from "three/addons/loaders/GLTFLoader";
|
||||||
|
import * as SkeletonUtils from "three/addons/utils/SkeletonUtils";
|
||||||
|
import * as TWEEN from "@tweenjs/tween.js";
|
||||||
|
import { AppString } from "@/constants/app";
|
||||||
|
|
||||||
|
const ANIMATION_DURATION_SECS = 10;
|
||||||
|
const BASE32 = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
|
||||||
|
|
||||||
|
export async function loadLandmarks(vue, world, scene, loop) {
|
||||||
|
const endorserApiServer = AppString.DEFAULT_ENDORSER_API_SERVER;
|
||||||
|
try {
|
||||||
|
const url =
|
||||||
|
endorserApiServer + "/api/v2/report/claims?claimType=GiveAction";
|
||||||
|
const headers = { "Content-Type": "application/json" };
|
||||||
|
const resp = await axios.get(url, { headers: headers });
|
||||||
|
if (resp.status === 200) {
|
||||||
|
const minDate = resp.data.data[resp.data.data.length - 1].issuedAt;
|
||||||
|
const maxDate = resp.data.data[0].issuedAt;
|
||||||
|
const minTimeMillis = new Date(minDate).getTime();
|
||||||
|
const fullTimeMillis = new Date(maxDate).getTime() - minTimeMillis;
|
||||||
|
// 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 = world;
|
||||||
|
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)
|
||||||
|
|
||||||
|
// compute location for this claim
|
||||||
|
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;
|
||||||
|
const light = createLight();
|
||||||
|
light.position.set(x, 20, z);
|
||||||
|
light.target.position.set(x, 0, 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.log(
|
||||||
|
"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.log("Got exception contacting server:", error);
|
||||||
|
vue.setAlert(
|
||||||
|
"Error With Server",
|
||||||
|
"There was a problem retrieving your claims from the server."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function 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;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user