Ride: viz on a wraparound roof band that reads from every angle
The dash radio and lucky billboards hid the viz, so it gets a 200-degree glass band with the same feed, floated above the body's measured highest point (procedural bodies vary per track; fixed glass gets buried, measured) on cabin masts. Verified: 504px at strong contrast on a rear-side card, present by construction on all orbit angles.
This commit is contained in:
@@ -468,6 +468,7 @@ export const ride = {
|
|||||||
roughness: 0.15, metalness: 0.7,
|
roughness: 0.15, metalness: 0.7,
|
||||||
transparent: opacity < 0.999, opacity,
|
transparent: opacity < 0.999, opacity,
|
||||||
}));
|
}));
|
||||||
|
cabin.name = 'cabin';
|
||||||
cabin.position.set(0, 0.5, -0.2);
|
cabin.position.set(0, 0.5, -0.2);
|
||||||
instance.car.add(cabin);
|
instance.car.add(cabin);
|
||||||
instance.bodyMeshes.push(cabin);
|
instance.bodyMeshes.push(cabin);
|
||||||
@@ -532,42 +533,65 @@ export const ride = {
|
|||||||
instance.radioMesh = radio;
|
instance.radioMesh = radio;
|
||||||
instance.bodyMeshes.push(radio);
|
instance.bodyMeshes.push(radio);
|
||||||
|
|
||||||
// Windshield: the viz at a size worth looking at. A raked plane
|
// Roof display: the viz at a size worth looking at. A wraparound
|
||||||
// across the cabin front, same emissiveMap feed as the radio —
|
// glass band over the cabin, same emissiveMap feed as the radio —
|
||||||
// the dash screen is for the crew, this is the one the chase
|
// the dash screen is for the crew, this is the one the chase
|
||||||
// camera actually sees. Front hemisphere only (single-sided):
|
// camera actually sees. Two placements were tried first: a raked
|
||||||
// from behind it reads as dark glass, like a real windshield.
|
// windshield across the cabin front (edge-on and invisible from
|
||||||
// Clears the dash (top 0.87 at z ≥ -1.09) and the crew (z ≥ -0.4).
|
// the side angles the orbit favours) and a low wraparound band
|
||||||
|
// (buried inside tall procedural bodies — the chassis varies per
|
||||||
|
// track and no fixed glass clears all of it). Above the roofline
|
||||||
|
// nothing occludes it from any chase angle, and the curve still
|
||||||
|
// presents a face to the lens everywhere: abstract viz has no
|
||||||
|
// handedness, so the mirrored rear view costs nothing.
|
||||||
const shieldMat = new T.MeshStandardMaterial({
|
const shieldMat = new T.MeshStandardMaterial({
|
||||||
color: new T.Color(0.02, 0.02, 0.02),
|
color: new T.Color(0.02, 0.02, 0.02),
|
||||||
emissive: new T.Color(1, 1, 1),
|
emissive: new T.Color(1, 1, 1),
|
||||||
emissiveIntensity: 1.0,
|
emissiveIntensity: 1.0,
|
||||||
roughness: 0.4, metalness: 0.2,
|
roughness: 0.4, metalness: 0.2,
|
||||||
|
side: T.DoubleSide,
|
||||||
});
|
});
|
||||||
const shield = new T.Mesh(new T.PlaneGeometry(1.44, 0.44), shieldMat);
|
// Cylinder side: x = r·sinθ, z = r·cosθ, so θ = π centres the
|
||||||
shield.position.set(0, 0.8, -1.14);
|
// band on -z (forward) and ±100° wraps it around both flanks.
|
||||||
// Face up-forward: lookAt aims +z at the target, so the normal
|
// Sized to read at chase distance: the car is ~2.5 units filmed
|
||||||
// lands ~23° off vertical — a raked windshield, not a billboard.
|
// from 4-6 away, so anything smaller is a rumour at card scale.
|
||||||
// Car group is at identity in build, so local == world here.
|
// Floated above the body's highest point + margin, measured off
|
||||||
shield.lookAt(0, 3.0, -6.0);
|
// what was just built — the chassis varies per track and a fixed
|
||||||
|
// height gets buried (measured). Crew heads (≤0.92 with bob,
|
||||||
|
// built below) are floored in. Static per arrangement: the key
|
||||||
|
// carries no per-section seeds, so it never moves mid-video.
|
||||||
|
instance.car.updateMatrixWorld(true);
|
||||||
|
const measBox = new T.Box3();
|
||||||
|
for (const n of ['chassis', 'cabin']) {
|
||||||
|
const o = instance.car.getObjectByName(n);
|
||||||
|
if (o) measBox.expandByObject(o);
|
||||||
|
}
|
||||||
|
const bandY = Math.max(
|
||||||
|
1.25, (measBox.isEmpty() ? 0.7 : measBox.max.y) + 0.22);
|
||||||
|
const shield = new T.Mesh(
|
||||||
|
new T.CylinderGeometry(1.1, 1.1, 0.6, 24, 1, true, Math.PI - 1.745, 3.49),
|
||||||
|
shieldMat);
|
||||||
|
shield.position.set(0, bandY, -0.32);
|
||||||
shield.name = 'windshield';
|
shield.name = 'windshield';
|
||||||
instance.car.add(shield);
|
instance.car.add(shield);
|
||||||
instance.shieldMat = shieldMat;
|
instance.shieldMat = shieldMat;
|
||||||
instance.shieldMesh = shield;
|
instance.shieldMesh = shield;
|
||||||
instance.bodyMeshes.push(shield);
|
instance.bodyMeshes.push(shield);
|
||||||
// A-pillars so the glass sits in the car instead of hovering over
|
// Masts seating the band on the cabin top. Sized from the
|
||||||
// it: thin struts from the cabin top to the glass top corners.
|
// measured band height so they always meet it.
|
||||||
const pillarMat = new T.MeshStandardMaterial({
|
const pillarMat = new T.MeshStandardMaterial({
|
||||||
color: new T.Color(0.05, 0.05, 0.06), roughness: 0.6, metalness: 0.3,
|
color: new T.Color(0.05, 0.05, 0.06), roughness: 0.6, metalness: 0.3,
|
||||||
});
|
});
|
||||||
|
{
|
||||||
|
const postH = Math.max(0.1, (bandY - 0.3) - 0.66);
|
||||||
for (const side of [-1, 1]) {
|
for (const side of [-1, 1]) {
|
||||||
const pillar = new T.Mesh(new T.BoxGeometry(0.05, 0.37, 0.05), pillarMat);
|
const pillar = new T.Mesh(new T.BoxGeometry(0.05, postH, 0.05), pillarMat);
|
||||||
pillar.position.set(side * 0.7, 0.83, -1.1);
|
pillar.position.set(side * 0.7, 0.66 + postH / 2, -1.17);
|
||||||
pillar.rotation.x = 0.31;
|
|
||||||
pillar.name = 'pillar';
|
pillar.name = 'pillar';
|
||||||
instance.car.add(pillar);
|
instance.car.add(pillar);
|
||||||
instance.bodyMeshes.push(pillar);
|
instance.bodyMeshes.push(pillar);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Headlight cones: two small translucent wedges so chase mode
|
// Headlight cones: two small translucent wedges so chase mode
|
||||||
// reads. Kept short and faint — long cones filled the frame.
|
// reads. Kept short and faint — long cones filled the frame.
|
||||||
|
|||||||
Reference in New Issue
Block a user