Update index.html
Browse files- index.html +161 -161
index.html
CHANGED
@@ -19,192 +19,192 @@ let renderer = new THREE.WebGLRenderer();
|
|
19 |
renderer.setSize(window.innerWidth, window.innerHeight);
|
20 |
document.body.appendChild(renderer.domElement);
|
21 |
|
22 |
-
//
|
23 |
-
|
24 |
-
let
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
29 |
|
30 |
-
// CLOUDS (just a few
|
31 |
function makeCloud(x, y, z) {
|
32 |
-
let
|
33 |
-
let
|
34 |
-
|
35 |
-
|
36 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
37 |
});
|
38 |
-
let cloud = new THREE.Mesh(
|
39 |
cloud.position.set(x, y, z);
|
40 |
-
cloud.rotation.y = Math.random() * Math.PI * 2; // random rotation
|
41 |
scene.add(cloud);
|
42 |
-
|
43 |
-
let phase = Math.random() * Math.PI * 2;
|
44 |
-
function animateCloud() {
|
45 |
-
requestAnimationFrame(animateCloud);
|
46 |
-
cloud.position.y = y + Math.sin(phase + Date.now() * 0.001) * 0.2;
|
47 |
-
}
|
48 |
-
animateCloud();
|
49 |
-
}
|
50 |
-
function makeCloudCanvas() {
|
51 |
-
let canvas = document.createElement('canvas');
|
52 |
-
canvas.width = 256;
|
53 |
-
canvas.height = 128;
|
54 |
-
let ctx = canvas.getContext('2d');
|
55 |
-
ctx.fillStyle = 'white';
|
56 |
-
ctx.beginPath();
|
57 |
-
for (let i = 0; i < 10; i++) {
|
58 |
-
ctx.arc(Math.random() * 256, Math.random() * 128, Math.random() * 20 + 10, 0, Math.PI * 2);
|
59 |
-
}
|
60 |
-
ctx.fill();
|
61 |
-
return canvas;
|
62 |
}
|
63 |
-
|
64 |
-
makeCloud(-
|
65 |
-
makeCloud(5, 9, -30);
|
66 |
|
67 |
-
// MOUNTAINS (simple low-poly)
|
68 |
-
let
|
69 |
-
let
|
70 |
-
0, 0, 0,
|
71 |
-
|
72 |
-
|
73 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
74 |
]);
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
84 |
|
85 |
-
// ROAD (long thin
|
86 |
-
let
|
87 |
-
let roadMat = new THREE.
|
88 |
-
let road = new THREE.Mesh(
|
89 |
-
road.
|
|
|
90 |
scene.add(road);
|
91 |
-
// dashed line in the middle of the road
|
92 |
-
let lineGeo = new THREE.PlaneGeometry(1000, 0.2);
|
93 |
-
let lineMat = new THREE.MeshBasicMaterial({ color: 'yellow' });
|
94 |
-
let line = new THREE.Mesh(lineGeo, lineMat);
|
95 |
-
line.position.z = -50;
|
96 |
-
line.position.y = 0.11; // on top of road
|
97 |
-
line.rotation.x = -Math.PI / 2;
|
98 |
-
scene.add(line);
|
99 |
-
// make dashes (ugly simple way)
|
100 |
-
for (let d = -500; d < 500; d += 20) {
|
101 |
-
let dash = new THREE.Mesh(new THREE.PlaneGeometry(5, 0.2), lineMat);
|
102 |
-
dash.position.z = -50;
|
103 |
-
dash.position.x = d;
|
104 |
-
dash.position.y = 0.11;
|
105 |
-
dash.rotation.x = -Math.PI / 2;
|
106 |
-
scene.add(dash);
|
107 |
-
}
|
108 |
|
109 |
-
// TREES (green
|
110 |
function makeTree(x, z) {
|
111 |
-
let
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
|
|
|
|
119 |
scene.add(trunk);
|
120 |
-
scene.add(leaf);
|
121 |
}
|
122 |
-
|
123 |
-
|
124 |
-
makeTree(
|
125 |
-
makeTree(5, z);
|
126 |
}
|
127 |
|
128 |
-
//
|
129 |
-
let
|
130 |
-
|
131 |
-
let
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
let
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
let trainAngle = 0;
|
142 |
-
function animateTrain() {
|
143 |
-
requestAnimationFrame(animateTrain);
|
144 |
-
trainAngle += 0.01;
|
145 |
-
trainBody.position.x = 20 * Math.cos(trainAngle);
|
146 |
-
trainBody.position.z = 20 * Math.sin(trainAngle);
|
147 |
-
trainBody.rotation.y = trainAngle + Math.PI / 2;
|
148 |
}
|
149 |
-
|
|
|
|
|
150 |
|
151 |
-
//
|
152 |
-
let
|
153 |
-
let
|
154 |
-
let
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
let
|
159 |
-
|
160 |
-
let
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
carBody.add(carWheelBL);
|
165 |
-
carBody.add(carWheelBR);
|
166 |
-
scene.add(carBody);
|
167 |
-
carBody.position.set(0, 0.8, 0); // start pos
|
168 |
-
let carSpeed = 0;
|
169 |
-
let carAngle = 0;
|
170 |
-
let keys = { ArrowUp: false, ArrowDown: false, ArrowLeft: false, ArrowRight: false,
|
171 |
-
KeyW: false, KeyS: false, KeyA: false, KeyD: false };
|
172 |
-
document.addEventListener('keydown', e => {
|
173 |
-
if (e.code in keys) keys[e.code] = true;
|
174 |
-
});
|
175 |
-
document.addEventListener('keyup', e => {
|
176 |
-
if (e.code in keys) keys[e.code] = false;
|
177 |
-
});
|
178 |
-
function updateCar() {
|
179 |
-
if (keys.ArrowUp || keys.KeyW) carSpeed += 0.01;
|
180 |
-
if (keys.ArrowDown || keys.KeyS) carSpeed -= 0.01;
|
181 |
-
if (keys.ArrowLeft || keys.KeyA) carAngle -= 0.05;
|
182 |
-
if (keys.ArrowRight || keys.KeyD) carAngle += 0.05;
|
183 |
-
carSpeed *= 0.98; // friction
|
184 |
-
carBody.position.x += Math.sin(carAngle) * carSpeed;
|
185 |
-
carBody.position.z += Math.cos(carAngle) * carSpeed;
|
186 |
-
carBody.rotation.y = -carAngle;
|
187 |
-
// keep camera behind car
|
188 |
-
camera.position.x = carBody.position.x - Math.sin(carAngle) * 5;
|
189 |
-
camera.position.z = carBody.position.z - Math.cos(carAngle) * 5;
|
190 |
-
camera.position.y = 3;
|
191 |
-
camera.lookAt(carBody.position);
|
192 |
}
|
|
|
|
|
|
|
193 |
|
194 |
-
//
|
|
|
|
|
|
|
195 |
function animate() {
|
196 |
requestAnimationFrame(animate);
|
197 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
198 |
renderer.render(scene, camera);
|
199 |
}
|
200 |
animate();
|
201 |
-
|
202 |
-
// resize handling
|
203 |
-
window.addEventListener('resize', () => {
|
204 |
-
camera.aspect = window.innerWidth / window.innerHeight;
|
205 |
-
camera.updateProjectionMatrix();
|
206 |
-
renderer.setSize(window.innerWidth, window.innerHeight);
|
207 |
-
});
|
208 |
</script>
|
209 |
</body>
|
210 |
</html>
|
|
|
19 |
renderer.setSize(window.innerWidth, window.innerHeight);
|
20 |
document.body.appendChild(renderer.domElement);
|
21 |
|
22 |
+
// SKYBOX / BACKGROUND (just a simple gradient sky, no cube maps today)
|
23 |
+
let skyGeom = new THREE.SphereGeometry(500, 32, 32);
|
24 |
+
let skyMat = new THREE.MeshBasicMaterial({
|
25 |
+
map: new THREE.CanvasTexture((function(){
|
26 |
+
let c = document.createElement('canvas'), ctx = c.getContext('2d');
|
27 |
+
c.width = c.height = 256;
|
28 |
+
let grd = ctx.createLinearGradient(0, 0, 0, 256);
|
29 |
+
grd.addColorStop(0, '#87CEEB'); // light sky blue top
|
30 |
+
grd.addColorStop(1, '#4682B4'); // steel blue bottom
|
31 |
+
ctx.fillStyle = grd;
|
32 |
+
ctx.fillRect(0, 0, 256, 256);
|
33 |
+
return c;
|
34 |
+
})()),
|
35 |
+
side: THREE.BackSide
|
36 |
+
});
|
37 |
+
let sky = new THREE.Mesh(skyGeom, skyMat);
|
38 |
+
scene.add(sky);
|
39 |
|
40 |
+
// CLOUDS (just a few puffy billow-y things using transparent textures)
|
41 |
function makeCloud(x, y, z) {
|
42 |
+
let cloudGeo = new THREE.SphereGeometry(10 + Math.random() * 5, 16, 16);
|
43 |
+
let cloudMat = new THREE.MeshBasicMaterial({
|
44 |
+
color: 0xffffff,
|
45 |
+
opacity: Math.random() * 0.3 + 0.2,
|
46 |
+
transparent: true,
|
47 |
+
map: new THREE.CanvasTexture((function(){
|
48 |
+
let c = document.createElement('canvas'), ctx = c.getContext('2d');
|
49 |
+
c.width = c.height = 64;
|
50 |
+
ctx.fillStyle = 'white';
|
51 |
+
ctx.beginPath(); ctx.arc(32, 32, 28, 0, Math.PI * 2); ctx.fill();
|
52 |
+
// make edges puffy/feathered
|
53 |
+
let gd = ctx.createRadialGradient(32, 32, 10, 32, 32, 28);
|
54 |
+
gd.addColorStop(0, 'rgba(255,255,255,1)');
|
55 |
+
gd.addColorStop(0.7, 'rgba(255,255,255,0.3)');
|
56 |
+
gd.addColorStop(1, 'rgba(255,255,255,0)');
|
57 |
+
ctx.fillStyle = gd;
|
58 |
+
ctx.fillRect(0, 0, 64, 64);
|
59 |
+
return c;
|
60 |
+
})())
|
61 |
});
|
62 |
+
let cloud = new THREE.Mesh(cloudGeo, cloudMat);
|
63 |
cloud.position.set(x, y, z);
|
|
|
64 |
scene.add(cloud);
|
65 |
+
return cloud;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
66 |
}
|
67 |
+
let clouds = [];
|
68 |
+
for (let i = 0; i < 10; i++) clouds.push(makeCloud(Math.random() * 200 - 100, 30 + Math.random() * 10, Math.random() * 200 - 100));
|
|
|
69 |
|
70 |
+
// MOUNTAINS (simple low-poly with vertex colors)
|
71 |
+
let mountainGeom = new THREE.BufferGeometry();
|
72 |
+
let mVerts = new Float32Array([
|
73 |
+
0, 0, 0,
|
74 |
+
50, 0, 0,
|
75 |
+
25, 40, 0,
|
76 |
+
|
77 |
+
50, 0, 0,
|
78 |
+
100, 0, 0,
|
79 |
+
75, 40, 0,
|
80 |
+
|
81 |
+
0, 0, 0,
|
82 |
+
0, 0, 50,
|
83 |
+
25, 40, 25,
|
84 |
+
|
85 |
+
100, 0, 0,
|
86 |
+
100, 0, 50,
|
87 |
+
75, 40, 25,
|
88 |
]);
|
89 |
+
let mColors = new Float32Array([
|
90 |
+
0.5, 0.4, 0.3, // brown base
|
91 |
+
0.5, 0.4, 0.3,
|
92 |
+
0.7, 0.6, 0.5, // beige peak
|
93 |
+
|
94 |
+
0.5, 0.4, 0.3,
|
95 |
+
0.5, 0.4, 0.3,
|
96 |
+
0.7, 0.6, 0.5,
|
97 |
+
|
98 |
+
0.5, 0.4, 0.3,
|
99 |
+
0.5, 0.4, 0.3,
|
100 |
+
0.7, 0.6, 0.5,
|
101 |
+
|
102 |
+
0.5, 0.4, 0.3,
|
103 |
+
0.5, 0.4, 0.3,
|
104 |
+
0.7, 0.6, 0.5,
|
105 |
+
]);
|
106 |
+
mountainGeom.setAttribute('position', new THREE.BufferAttribute(mVerts, 3));
|
107 |
+
mountainGeom.setAttribute('color', new THREE.BufferAttribute(mColors, 3));
|
108 |
+
let mountainMat = new THREE.MeshBasicMaterial({ vertexColors: true });
|
109 |
+
let mountains = new THREE.Mesh(mountainGeom, mountainMat);
|
110 |
+
mountains.scale.set(4, 4, 4); // bigger
|
111 |
+
mountains.position.z = -100;
|
112 |
+
scene.add(mountains);
|
113 |
|
114 |
+
// ROAD (just a long thin plane)
|
115 |
+
let roadGeom = new THREE.PlaneGeometry(200, 10);
|
116 |
+
let roadMat = new THREE.MeshBasicMaterial({ color: 0x444444 });
|
117 |
+
let road = new THREE.Mesh(roadGeom, roadMat);
|
118 |
+
road.rotation.x = -Math.PI / 2; // flat on ground
|
119 |
+
road.position.y = -0.1; // under car wheels
|
120 |
scene.add(road);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
121 |
|
122 |
+
// TREES (simple green cones)
|
123 |
function makeTree(x, z) {
|
124 |
+
let treeGeo = new THREE.ConeGeometry(4, 10);
|
125 |
+
let treeMat = new THREE.MeshBasicMaterial({ color: 0x228B22 }); // forestgreen
|
126 |
+
let tree = new THREE.Mesh(treeGeo, treeMat);
|
127 |
+
tree.position.set(x, 5, z); // half-height up so it sits on ground
|
128 |
+
scene.add(tree);
|
129 |
+
// quick 'n' dirty trunk
|
130 |
+
let trunkGeo = new THREE.CylinderGeometry(1, 1, 2);
|
131 |
+
let trunkMat = new THREE.MeshBasicMaterial({ color: 0x964B00 }); // brown
|
132 |
+
let trunk = new THREE.Mesh(trunkGeo, trunkMat);
|
133 |
+
trunk.position.set(x, 1, z);
|
134 |
scene.add(trunk);
|
|
|
135 |
}
|
136 |
+
for (let i = -5; i <= 5; i++) {
|
137 |
+
makeTree(i * 15, -50); // left side trees
|
138 |
+
makeTree(i * 15, 50); // right side trees
|
|
|
139 |
}
|
140 |
|
141 |
+
// CAR (just a box with wheels)
|
142 |
+
let carBodyGeom = new THREE.BoxGeometry(10, 4, 6);
|
143 |
+
let carBodyMat = new THREE.MeshBasicMaterial({ color: 0xff0000 }); // red
|
144 |
+
let car = new THREE.Mesh(carBodyGeom, carBodyMat);
|
145 |
+
car.position.y = 2; // up off ground
|
146 |
+
|
147 |
+
let wheelGeo = new THREE.CylinderGeometry(1.5, 1.5, 1);
|
148 |
+
let wheelMat = new THREE.MeshBasicMaterial({ color: 0x333333 });
|
149 |
+
function makeWheel(x, z) {
|
150 |
+
let wheel = new THREE.Mesh(wheelGeo, wheelMat);
|
151 |
+
wheel.rotation.x = Math.PI / 2; // roll it
|
152 |
+
wheel.position.set(x, 0.5, z);
|
153 |
+
car.add(wheel);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
154 |
}
|
155 |
+
makeWheel(-4, -2); makeWheel( 4, -2); // front wheels
|
156 |
+
makeWheel(-4, 2); makeWheel( 4, 2); // rear wheels
|
157 |
+
scene.add(car);
|
158 |
|
159 |
+
// TRAIN (just a long box with wheels, moving in circle)
|
160 |
+
let trainBodyGeom = new THREE.BoxGeometry(30, 4, 6);
|
161 |
+
let trainBodyMat = new THREE.MeshBasicMaterial({ color: 0x0000ff }); // blue
|
162 |
+
let train = new THREE.Mesh(trainBodyGeom, trainBodyMat);
|
163 |
+
train.position.y = 2;
|
164 |
+
train.position.x = 80; // start off to side
|
165 |
+
let trainWheelGeo = new THREE.CylinderGeometry(2, 2, 1.5);
|
166 |
+
let trainWheelMat = new THREE.MeshBasicMaterial({ color: 0x333333 });
|
167 |
+
function makeTrainWheel(x, z) {
|
168 |
+
let wheel = new THREE.Mesh(trainWheelGeo, trainWheelMat);
|
169 |
+
wheel.rotation.x = Math.PI / 2;
|
170 |
+
wheel.position.set(x, 0.5, z);
|
171 |
+
train.add(wheel);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
172 |
}
|
173 |
+
makeTrainWheel(-12, -2.5); makeTrainWheel( 12, -2.5);
|
174 |
+
makeTrainWheel(-12, 2.5); makeTrainWheel( 12, 2.5);
|
175 |
+
scene.add(train);
|
176 |
|
177 |
+
// ANIMATION / PHYSICS
|
178 |
+
let carSpeed = 0.5;
|
179 |
+
let trainAngle = 0;
|
180 |
+
let clock = new THREE.Clock();
|
181 |
function animate() {
|
182 |
requestAnimationFrame(animate);
|
183 |
+
|
184 |
+
// move car forward
|
185 |
+
car.position.z += carSpeed;
|
186 |
+
if (car.position.z > 100) car.position.z = -100; // loop road
|
187 |
+
|
188 |
+
// move train in circle (easy parametric equation)
|
189 |
+
trainAngle += 0.01;
|
190 |
+
train.position.x = Math.cos(trainAngle) * 80;
|
191 |
+
train.position.z = Math.sin(trainAngle) * 80;
|
192 |
+
train.rotation.y = trainAngle + Math.PI / 2; // face direction of travel
|
193 |
+
|
194 |
+
// bob clouds gently up/down
|
195 |
+
clouds.forEach(cloud => {
|
196 |
+
cloud.position.y = 30 + Math.sin(clock.getElapsedTime() + cloud.position.x * 0.01) * 2;
|
197 |
+
});
|
198 |
+
|
199 |
+
// keep camera just behind car
|
200 |
+
camera.position.x = car.position.x;
|
201 |
+
camera.position.z = car.position.z - 15;
|
202 |
+
camera.position.y = 8; // nice view height
|
203 |
+
camera.lookAt(car.position.x, 2, car.position.z); // look at car
|
204 |
+
|
205 |
renderer.render(scene, camera);
|
206 |
}
|
207 |
animate();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
208 |
</script>
|
209 |
</body>
|
210 |
</html>
|