forked from trent_larson/crowd-funder-for-time-pwa
add beginning of visualization for statistics, unmodified from blog code
This commit is contained in:
86
src/components/World/World.js
Normal file
86
src/components/World/World.js
Normal file
@@ -0,0 +1,86 @@
|
||||
// from https://medium.com/nicasource/building-an-interactive-web-portfolio-with-vue-three-js-part-three-implementing-three-js-452cb375ef80
|
||||
|
||||
import { createCamera } from "./components/camera.js";
|
||||
import { createLights } from "./components/lights.js";
|
||||
import { createScene } from "./components/scene.js";
|
||||
import { createRenderer } from "./systems/renderer.js";
|
||||
import { Loop } from "./systems/Loop.js";
|
||||
import { Resizer } from "./systems/Resizer.js";
|
||||
import { createControls } from "./systems/controls.js";
|
||||
|
||||
import createTerrain from "./components/objects/terrain.js";
|
||||
|
||||
// These variables are module-scoped: we cannot access them
|
||||
// from outside the module
|
||||
|
||||
const color = "#42b883";
|
||||
const color2 = "#4eac82";
|
||||
|
||||
let camera;
|
||||
let renderer;
|
||||
let scene;
|
||||
let loop;
|
||||
|
||||
class World {
|
||||
constructor(container) {
|
||||
// Instances of camera, scene, and renderer
|
||||
camera = createCamera();
|
||||
scene = createScene(color2);
|
||||
renderer = createRenderer();
|
||||
|
||||
// Initializate Loop
|
||||
loop = new Loop(camera, scene, renderer);
|
||||
|
||||
container.append(renderer.domElement);
|
||||
|
||||
// Orbit Controls
|
||||
const controls = createControls(camera, renderer.domElement);
|
||||
|
||||
// Light Instance, with optional light helper
|
||||
const { light, lightHelper } = createLights(color);
|
||||
|
||||
// Random values for terrain vertices
|
||||
// We could do this on the terrain.js file,
|
||||
// but if we want to have a single random
|
||||
// number array for more than one terrain
|
||||
// instance, then we would be in trouble.
|
||||
const randomVals = [];
|
||||
for (let i = 0; i < 12675; i++) {
|
||||
randomVals.push(Math.random() - 0.5);
|
||||
}
|
||||
|
||||
// Terrain Instance
|
||||
const terrain = createTerrain({
|
||||
color: color,
|
||||
randVertexArr: randomVals,
|
||||
});
|
||||
|
||||
loop.updatables.push(controls);
|
||||
loop.updatables.push(light);
|
||||
loop.updatables.push(terrain);
|
||||
|
||||
scene.add(light, terrain);
|
||||
|
||||
// Responsive handler
|
||||
const resizer = new Resizer(container, camera, renderer);
|
||||
resizer.onResize = () => {
|
||||
this.render();
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
// draw a single frame
|
||||
renderer.render(scene, camera);
|
||||
}
|
||||
|
||||
// Animation handlers
|
||||
start() {
|
||||
loop.start();
|
||||
}
|
||||
|
||||
stop() {
|
||||
loop.stop();
|
||||
}
|
||||
}
|
||||
|
||||
export { World };
|
||||
19
src/components/World/components/camera.js
Normal file
19
src/components/World/components/camera.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import { PerspectiveCamera } from "three";
|
||||
|
||||
function createCamera() {
|
||||
const camera = new PerspectiveCamera(
|
||||
35, // fov = Field Of View
|
||||
1, // aspect ratio (dummy value)
|
||||
0.1, // near clipping plane
|
||||
100 // far clipping plane
|
||||
);
|
||||
|
||||
// move the camera back so we can view the scene
|
||||
camera.position.set(0, 10, 30);
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
camera.tick = (delta) => {};
|
||||
|
||||
return camera;
|
||||
}
|
||||
|
||||
export { createCamera };
|
||||
14
src/components/World/components/lights.js
Normal file
14
src/components/World/components/lights.js
Normal file
@@ -0,0 +1,14 @@
|
||||
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);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
light.tick = (delta) => {};
|
||||
|
||||
return { light, lightHelper };
|
||||
}
|
||||
|
||||
export { createLights };
|
||||
60
src/components/World/components/objects/terrain.js
Normal file
60
src/components/World/components/objects/terrain.js
Normal file
@@ -0,0 +1,60 @@
|
||||
import {
|
||||
PlaneBufferGeometry,
|
||||
MeshStandardMaterial,
|
||||
Mesh,
|
||||
TextureLoader,
|
||||
} from "three";
|
||||
|
||||
export default function createTerrain(props) {
|
||||
const loader = new TextureLoader();
|
||||
const height = loader.load("img/textures/height.png");
|
||||
// w h
|
||||
const geometry = new PlaneBufferGeometry(150, 150, 64, 64);
|
||||
|
||||
const material = new MeshStandardMaterial({
|
||||
color: props.color,
|
||||
flatShading: true,
|
||||
displacementMap: height,
|
||||
displacementScale: 5,
|
||||
});
|
||||
|
||||
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;
|
||||
|
||||
//Randomizing our vertices position
|
||||
const { array } = plane.geometry.attributes.position;
|
||||
for (let i = 0; i < array.length; i++) {
|
||||
props.randVertexArr.push(Math.random());
|
||||
}
|
||||
|
||||
plane.geometry.attributes.position.randomValues = props.randVertexArr;
|
||||
|
||||
let frame = 0;
|
||||
plane.tick = (delta) => {
|
||||
frame += 0.01;
|
||||
// destructuring of the random values, the original position and the current vertex position
|
||||
const { array, originalPosition, randomValues } =
|
||||
plane.geometry.attributes.position;
|
||||
|
||||
// Animation for loop
|
||||
// In our vertex array, we have 3 coordinates: x, y and z. We are
|
||||
// only going to animate on ONE coordinate: the z coordinate.
|
||||
// This means we have to omit the x and y coords, hence the i+=3 in our loop.
|
||||
// Also, to access that y coordinate on each vertex, we have to add 2 to our
|
||||
// current i each time.
|
||||
for (let i = 0; i < array.length; i += 3) {
|
||||
// Accessing the z coord
|
||||
array[i + 2] =
|
||||
// Try switching this numbers up, or using sine instead of cosine, see how the animation changes
|
||||
originalPosition[i + 2] + Math.cos(frame + randomValues[i + 2]) * 0.002;
|
||||
}
|
||||
plane.geometry.attributes.position.needsUpdate = true;
|
||||
};
|
||||
|
||||
return plane;
|
||||
}
|
||||
11
src/components/World/components/scene.js
Normal file
11
src/components/World/components/scene.js
Normal file
@@ -0,0 +1,11 @@
|
||||
import { Color, Scene, Fog } from "three";
|
||||
|
||||
function createScene(color) {
|
||||
const scene = new Scene();
|
||||
|
||||
scene.background = new Color(color);
|
||||
scene.fog = new Fog(color, 60, 90);
|
||||
return scene;
|
||||
}
|
||||
|
||||
export { createScene };
|
||||
33
src/components/World/systems/Loop.js
Normal file
33
src/components/World/systems/Loop.js
Normal file
@@ -0,0 +1,33 @@
|
||||
import { Clock } from "three";
|
||||
|
||||
const clock = new Clock();
|
||||
|
||||
class Loop {
|
||||
constructor(camera, scene, renderer) {
|
||||
this.camera = camera;
|
||||
this.scene = scene;
|
||||
this.renderer = renderer;
|
||||
this.updatables = [];
|
||||
}
|
||||
|
||||
start() {
|
||||
this.renderer.setAnimationLoop(() => {
|
||||
this.tick();
|
||||
// render a frame
|
||||
this.renderer.render(this.scene, this.camera);
|
||||
});
|
||||
}
|
||||
|
||||
stop() {
|
||||
this.renderer.setAnimationLoop(null);
|
||||
}
|
||||
|
||||
tick() {
|
||||
const delta = clock.getDelta();
|
||||
for (const object of this.updatables) {
|
||||
object.tick(delta);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { Loop };
|
||||
26
src/components/World/systems/Resizer.js
Normal file
26
src/components/World/systems/Resizer.js
Normal file
@@ -0,0 +1,26 @@
|
||||
const setSize = (container, camera, renderer) => {
|
||||
camera.aspect = window.innerWidth / window.innerHeight;
|
||||
camera.updateProjectionMatrix();
|
||||
|
||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
renderer.setPixelRatio(window.devicePixelRatio);
|
||||
};
|
||||
|
||||
class Resizer {
|
||||
constructor(container, camera, renderer) {
|
||||
// set initial size on load
|
||||
setSize(container, camera, renderer);
|
||||
|
||||
window.addEventListener("resize", () => {
|
||||
// set the size again if a resize occurs
|
||||
setSize(container, camera, renderer);
|
||||
// perform any custom actions
|
||||
this.onResize();
|
||||
});
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
onResize() {}
|
||||
}
|
||||
|
||||
export { Resizer };
|
||||
36
src/components/World/systems/controls.js
vendored
Normal file
36
src/components/World/systems/controls.js
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
|
||||
import { MathUtils } from "three";
|
||||
|
||||
function createControls(camera, canvas) {
|
||||
const controls = new OrbitControls(camera, canvas);
|
||||
|
||||
//enable controls?
|
||||
controls.enabled = true;
|
||||
controls.autoRotate = true;
|
||||
controls.autoRotateSpeed = 0.2;
|
||||
|
||||
// control limits
|
||||
// It's recommended to set some control boundaries,
|
||||
// to prevent the user from clipping with the objects.
|
||||
|
||||
// y axis
|
||||
controls.minPolarAngle = MathUtils.degToRad(40); // default
|
||||
controls.maxPolarAngle = MathUtils.degToRad(75);
|
||||
|
||||
// x axis
|
||||
// controls.minAzimuthAngle = ...
|
||||
// controls.maxAzimuthAngle = ...
|
||||
|
||||
//smooth camera:
|
||||
// remember to add to loop updatables to work
|
||||
controls.enableDamping = true;
|
||||
|
||||
controls.enableZoom = false;
|
||||
controls.enablePan = false;
|
||||
|
||||
controls.tick = () => controls.update();
|
||||
|
||||
return controls;
|
||||
}
|
||||
|
||||
export { createControls };
|
||||
12
src/components/World/systems/renderer.js
Normal file
12
src/components/World/systems/renderer.js
Normal file
@@ -0,0 +1,12 @@
|
||||
import { WebGLRenderer } from "three";
|
||||
|
||||
function createRenderer() {
|
||||
const renderer = new WebGLRenderer({ antialias: true });
|
||||
|
||||
// turn on the physically correct lighting model
|
||||
renderer.physicallyCorrectLights = true;
|
||||
|
||||
return renderer;
|
||||
}
|
||||
|
||||
export { createRenderer };
|
||||
Reference in New Issue
Block a user