downtown demo scene: paid Downtown City MegaKit block (texture-crushed to 512), kerbside quaternius vehicles, dusk fog lighting
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
2a5449cc24
commit
f32747d2ce
BIN
assets/city/Building_Large_2.glb
Normal file
BIN
assets/city/Building_Large_2.glb
Normal file
Binary file not shown.
BIN
assets/city/Building_Medium_2_001.glb
Normal file
BIN
assets/city/Building_Medium_2_001.glb
Normal file
Binary file not shown.
BIN
assets/city/Building_Small_1.glb
Normal file
BIN
assets/city/Building_Small_1.glb
Normal file
Binary file not shown.
BIN
assets/city/Cop.glb
Normal file
BIN
assets/city/Cop.glb
Normal file
Binary file not shown.
BIN
assets/city/NormalCar1.glb
Normal file
BIN
assets/city/NormalCar1.glb
Normal file
Binary file not shown.
BIN
assets/city/Prop_Bollard.glb
Normal file
BIN
assets/city/Prop_Bollard.glb
Normal file
Binary file not shown.
BIN
assets/city/Prop_Planter_Single.glb
Normal file
BIN
assets/city/Prop_Planter_Single.glb
Normal file
Binary file not shown.
BIN
assets/city/Sidewalk_Straight_3m.glb
Normal file
BIN
assets/city/Sidewalk_Straight_3m.glb
Normal file
Binary file not shown.
BIN
assets/city/Street_2Lane.glb
Normal file
BIN
assets/city/Street_2Lane.glb
Normal file
Binary file not shown.
BIN
assets/city/Street_4WayIntersection.glb
Normal file
BIN
assets/city/Street_4WayIntersection.glb
Normal file
Binary file not shown.
BIN
assets/city/Street_Asphalt_9x9.glb
Normal file
BIN
assets/city/Street_Asphalt_9x9.glb
Normal file
Binary file not shown.
BIN
assets/city/Taxi.glb
Normal file
BIN
assets/city/Taxi.glb
Normal file
Binary file not shown.
124
src/main.js
124
src/main.js
@ -4,7 +4,7 @@ import { GLTFLoader } from '../vendor/GLTFLoader.js';
|
|||||||
import { OrbitControls } from '../vendor/OrbitControls.js';
|
import { OrbitControls } from '../vendor/OrbitControls.js';
|
||||||
import { RetroScreen, retroState, retrofyObject, patchRetroMaterial } from './retro.js';
|
import { RetroScreen, retroState, retrofyObject, patchRetroMaterial } from './retro.js';
|
||||||
|
|
||||||
const FOG_COLOR = 0x2a2036;
|
const FOG_COLOR = 0x8d84a8;
|
||||||
|
|
||||||
const renderer = new THREE.WebGLRenderer({ antialias: false });
|
const renderer = new THREE.WebGLRenderer({ antialias: false });
|
||||||
renderer.setPixelRatio(1);
|
renderer.setPixelRatio(1);
|
||||||
@ -12,70 +12,98 @@ document.getElementById('view').appendChild(renderer.domElement);
|
|||||||
|
|
||||||
const scene = new THREE.Scene();
|
const scene = new THREE.Scene();
|
||||||
scene.background = new THREE.Color(FOG_COLOR);
|
scene.background = new THREE.Color(FOG_COLOR);
|
||||||
scene.fog = new THREE.Fog(FOG_COLOR, 6, 26);
|
scene.fog = new THREE.Fog(FOG_COLOR, 14, 55);
|
||||||
|
|
||||||
const camera = new THREE.PerspectiveCamera(60, 4 / 3, 0.1, 100);
|
const camera = new THREE.PerspectiveCamera(60, 4 / 3, 0.1, 100);
|
||||||
camera.position.set(3.2, 2.2, 4.2);
|
camera.position.set(6.5, 3.2, 9.5);
|
||||||
const controls = new OrbitControls(camera, renderer.domElement);
|
const controls = new OrbitControls(camera, renderer.domElement);
|
||||||
controls.target.set(0, 1, 0);
|
controls.target.set(0, 1, 0);
|
||||||
controls.enableDamping = true;
|
controls.enableDamping = true;
|
||||||
|
|
||||||
scene.add(new THREE.HemisphereLight(0xcabfe8, 0x4a3c5e, 1.6));
|
scene.add(new THREE.HemisphereLight(0xe8e2f5, 0x8a7a95, 2.4));
|
||||||
const sun = new THREE.DirectionalLight(0xffeedd, 2.2);
|
const sun = new THREE.DirectionalLight(0xfff2dd, 3.2);
|
||||||
sun.position.set(4, 6, 3);
|
sun.position.set(4, 6, 3);
|
||||||
scene.add(sun);
|
scene.add(sun);
|
||||||
|
|
||||||
const screen = new RetroScreen(renderer);
|
const screen = new RetroScreen(renderer);
|
||||||
|
|
||||||
// --- procedural retro textures (canvas, nearest) ------------------------------
|
// --- downtown block (paid Downtown City MegaKit, texture-crushed) --------------
|
||||||
function canvasTexture(draw, size = 64) {
|
const cityLoader = new GLTFLoader();
|
||||||
const c = document.createElement('canvas');
|
function loadPiece(name) {
|
||||||
c.width = c.height = size;
|
return new Promise((res, rej) =>
|
||||||
draw(c.getContext('2d'), size);
|
cityLoader.load('assets/city/' + name + '.glb', (g) => {
|
||||||
const t = new THREE.CanvasTexture(c);
|
retrofyObject(g.scene);
|
||||||
t.magFilter = THREE.NearestFilter;
|
res(g.scene);
|
||||||
t.minFilter = THREE.NearestFilter;
|
}, undefined, rej));
|
||||||
t.generateMipmaps = false;
|
|
||||||
t.wrapS = t.wrapT = THREE.RepeatWrapping;
|
|
||||||
return t;
|
|
||||||
}
|
}
|
||||||
const checkerTex = canvasTexture((g, s) => {
|
function sizeOf(obj) {
|
||||||
for (let y = 0; y < 8; y++) for (let x = 0; x < 8; x++) {
|
const b = new THREE.Box3().setFromObject(obj);
|
||||||
g.fillStyle = (x + y) % 2 ? '#4a3f66' : '#5d5080';
|
return { size: b.getSize(new THREE.Vector3()), min: b.min, max: b.max };
|
||||||
g.fillRect(x * s / 8, y * s / 8, s / 8, s / 8);
|
}
|
||||||
|
function put(proto, x, z, ry = 0) {
|
||||||
|
const c = proto.clone(true);
|
||||||
|
c.position.set(x, 0, z);
|
||||||
|
c.rotation.y = ry;
|
||||||
|
scene.add(c);
|
||||||
|
return c;
|
||||||
}
|
}
|
||||||
});
|
|
||||||
checkerTex.repeat.set(12, 12);
|
|
||||||
const brickTex = canvasTexture((g, s) => {
|
|
||||||
g.fillStyle = '#6e5a4e'; g.fillRect(0, 0, s, s);
|
|
||||||
g.fillStyle = '#8a7263';
|
|
||||||
for (let y = 0; y < 4; y++) for (let x = 0; x < 4; x++)
|
|
||||||
g.fillRect(x * 16 + (y % 2 ? 8 : 0) + 1, y * 16 + 1, 14, 14);
|
|
||||||
});
|
|
||||||
|
|
||||||
const ground = new THREE.Mesh(
|
// dark asphalt base plane so there are no holes at the fog line
|
||||||
new THREE.PlaneGeometry(40, 40),
|
const base = new THREE.Mesh(
|
||||||
patchRetroMaterial(new THREE.MeshLambertMaterial({ map: checkerTex }))
|
new THREE.PlaneGeometry(120, 120),
|
||||||
|
patchRetroMaterial(new THREE.MeshLambertMaterial({ color: 0x2b2b30 }))
|
||||||
);
|
);
|
||||||
ground.rotation.x = -Math.PI / 2;
|
base.rotation.x = -Math.PI / 2;
|
||||||
scene.add(ground);
|
base.position.y = -0.03;
|
||||||
|
scene.add(base);
|
||||||
|
|
||||||
// scatter some chunky geometry so the wobble has something to chew on
|
(async () => {
|
||||||
const brickMat = patchRetroMaterial(new THREE.MeshLambertMaterial({ map: brickTex }));
|
try {
|
||||||
const shapes = [];
|
const [street, walk, bSmall, bMed, bLarge, bollard, planter, taxi, car, cop] =
|
||||||
for (let i = 0; i < 14; i++) {
|
await Promise.all([
|
||||||
const kind = i % 3;
|
loadPiece('Street_2Lane'), loadPiece('Sidewalk_Straight_3m'),
|
||||||
const geo = kind === 0 ? new THREE.BoxGeometry(1.4, 1 + (i % 4), 1.4)
|
loadPiece('Building_Small_1'), loadPiece('Building_Medium_2_001'),
|
||||||
: kind === 1 ? new THREE.ConeGeometry(0.9, 1.8, 6)
|
loadPiece('Building_Large_2'), loadPiece('Prop_Bollard'),
|
||||||
: new THREE.CylinderGeometry(0.6, 0.6, 1.4, 8);
|
loadPiece('Prop_Planter_Single'), loadPiece('Taxi'),
|
||||||
const m = new THREE.Mesh(geo, brickMat);
|
loadPiece('NormalCar1'), loadPiece('Cop'),
|
||||||
const a = (i / 14) * Math.PI * 2;
|
]);
|
||||||
const r = 6 + (i % 3) * 3;
|
const st = sizeOf(street);
|
||||||
m.position.set(Math.cos(a) * r, geo.parameters.height ? geo.parameters.height / 2 : 0.7, Math.sin(a) * r);
|
const stepX = st.size.x || 3; // tile the road along X
|
||||||
m.rotation.y = a;
|
const roadHalf = (st.size.z || 9) / 2;
|
||||||
scene.add(m);
|
for (let i = -5; i <= 5; i++) put(street, i * stepX, 0);
|
||||||
shapes.push(m);
|
const wk = sizeOf(walk);
|
||||||
|
const wkStep = wk.size.x || 3;
|
||||||
|
for (let i = -10; i <= 10; i++) {
|
||||||
|
put(walk, i * wkStep, roadHalf + wk.size.z / 2);
|
||||||
|
put(walk, i * wkStep, -(roadHalf + wk.size.z / 2), Math.PI);
|
||||||
}
|
}
|
||||||
|
// buildings lining both sides, fronts to the road
|
||||||
|
const row = roadHalf + wk.size.z + 0.2;
|
||||||
|
const north = [bSmall, bMed, bLarge, bSmall];
|
||||||
|
let x = -22;
|
||||||
|
for (const b of north) {
|
||||||
|
const bs = sizeOf(b);
|
||||||
|
put(b, x + bs.size.x / 2, row + bs.size.z / 2, Math.PI);
|
||||||
|
x += bs.size.x + 1.2;
|
||||||
|
}
|
||||||
|
const south = [bLarge, bSmall, bMed];
|
||||||
|
x = -18;
|
||||||
|
for (const b of south) {
|
||||||
|
const bs = sizeOf(b);
|
||||||
|
put(b, x + bs.size.x / 2, -(row + bs.size.z / 2));
|
||||||
|
x += bs.size.x + 1.5;
|
||||||
|
}
|
||||||
|
// kerbside cars + props
|
||||||
|
put(taxi, -4, roadHalf - 1.2, Math.PI);
|
||||||
|
put(car, 5, roadHalf - 1.2, Math.PI);
|
||||||
|
put(cop, 10, -(roadHalf - 1.2));
|
||||||
|
for (let i = -3; i <= 3; i++) put(bollard, i * 6 + 1.5, roadHalf + 0.6);
|
||||||
|
put(planter, -8, roadHalf + 1.4);
|
||||||
|
put(planter, 8, roadHalf + 1.4);
|
||||||
|
} catch (e) {
|
||||||
|
console.error('city load failed', e);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
// --- character ----------------------------------------------------------------
|
// --- character ----------------------------------------------------------------
|
||||||
let mixer = null;
|
let mixer = null;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user