Haus - Oeffnungen aus den Fassadenplaenen von EHLE abgegriffen. Zum Garten steht im Erdgeschoss eine einzige, mittige Fensterfront (x -8.82 bis -4.67), daneben das Lamellenband vor dem gedeckten Sitzplatz; im Obergeschoss zwei Fenster mit zwei schmalen Lamellen dazwischen. - Der gedeckte Sitzplatz ist nur noch gegen Westen offen, so wie es die Suedwestfassade als "Aussensitzplatz" zeigt. Gegen den Garten steht dort eine geschlossene Wand - bisher schaute man durch die Fassade hinein. - Hauszugang an der Nordostfassade statt an der Nordwestfassade: Tuerfluegel und feststehende Verglasung, zusammen 3.15 m, darueber das Vordach. Das Vordach kennt jetzt seine Fassadenseite. - Die Fassadennamen im Datenmodell waren vertauscht; jetzt mit Tabelle im README festgehalten. Garten - Sichtschutz: die gruenen Punkte im Kiesbett sind Ziergraeser, keine Polster. Neue Halmgeometrie - schmales, sich verjuengendes Band, das nach oben ueberhaengt, rund 1.10 m. Die Bodenleuchten sind jetzt Strahler und leuchten Stelen und Graeser von unten an. - Baenkli: die Fuesse standen quer zur Sitzplatte, weil der Versatz in Welt-Z statt entlang der gedrehten Achse gerechnet wurde. - Obere Ebene bis an das Kraeuterbeet eben auf -0.02. Zwischen Grillfeld und Kraeuterbeet stieg der Rasen um gut 30 cm an. - Unter dem Spielhaus steht der Betonklotz aus dem Plan (graues Rechteck 1.01 x 2.70 m); die offene Plattform daneben bleibt auf Stuetzen. - Aus dem Gartenhaus wird ein Gartenschrank: 1.80 x 0.50 m, 2.00 m hoch, mit dem Ruecken in der Ecke und den Doppeltueren zum Garten. - Neben dem Gelaender der Osttreppe lag Rasen; dort liegt jetzt derselbe Belag wie auf dem Weg, bis an den Treppenkopf. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+75
-22
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
import {
|
||||
BoxGeometry, BufferGeometry, CylinderGeometry, Group, Mesh, MeshStandardMaterial,
|
||||
PointLight, type MeshStandardMaterialParameters,
|
||||
PointLight, SpotLight, type MeshStandardMaterialParameters,
|
||||
} from 'three';
|
||||
import type { Lamp, Linear, Prop, Stele } from '../site';
|
||||
import { boxGeometry, mergeGeometries } from './geometry';
|
||||
@@ -80,35 +80,61 @@ function schwebendeStufe(p: Prop, y: number): Group {
|
||||
// Freistehende Objekte
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Gartenschrank: schmaler Schrank mit Doppeltuer und leicht geneigtem Deckel.
|
||||
* Der Plan reserviert an dieser Stelle 1.8 x 1.8 m fuer ein Gartenhaus; gebaut
|
||||
* wird ein Schrank von 1.80 x 0.50 m, rund 2.00 m hoch, mit dem Ruecken in der
|
||||
* Ecke und den Tueren zum Garten.
|
||||
*/
|
||||
function gartenhaus(p: Prop, y: number): Group {
|
||||
const g = new Group();
|
||||
const holz = material(tex.fassadeHolz(), { roughness: 0.8 });
|
||||
const dunkel = new MeshStandardMaterial({ color: 0x3b3934, roughness: 0.62 });
|
||||
const metall = new MeshStandardMaterial({ color: 0x55565a, roughness: 0.45, metalness: 0.6 });
|
||||
const a = (p.rot * Math.PI) / 180;
|
||||
// u laeuft entlang p.w, v entlang p.d - so wie in boxGeometry.
|
||||
const ux = Math.cos(a), uz = Math.sin(a);
|
||||
const vx = -Math.sin(a), vz = Math.cos(a);
|
||||
|
||||
const body = new Mesh(boxGeometry(p.x, p.z, p.w, p.d, y, y + p.h, p.rot, 1.2), holz);
|
||||
body.castShadow = true; body.receiveShadow = true;
|
||||
g.add(body);
|
||||
|
||||
// Deckel mit leichtem Ueberstand
|
||||
const roof = new Mesh(
|
||||
boxGeometry(p.x, p.z, p.w + 0.16, p.d + 0.16, y + p.h, y + p.h + 0.09, p.rot, 2),
|
||||
boxGeometry(p.x + ux * 0.03, p.z + uz * 0.03, p.w + 0.12, p.d + 0.14, y + p.h, y + p.h + 0.06, p.rot, 2),
|
||||
material(tex.beton(), { color: 0x6a6a66, roughness: 0.9 }),
|
||||
);
|
||||
roof.castShadow = true;
|
||||
g.add(roof);
|
||||
// Tuer
|
||||
const door = new Mesh(
|
||||
new BoxGeometry(0.78, 1.95, 0.04),
|
||||
new MeshStandardMaterial({ color: 0x3b3934, roughness: 0.6 }),
|
||||
);
|
||||
door.position.set(p.x, y + 0.98, p.z - p.d / 2 - 0.02);
|
||||
g.add(door);
|
||||
|
||||
// Zwei Tuerfluegel auf der Laengsseite, leicht vor der Schrankfront
|
||||
const front = p.w / 2 + 0.015;
|
||||
const parts: BufferGeometry[] = [];
|
||||
for (const s of [-1, 1]) {
|
||||
const off = s * (p.d / 4);
|
||||
parts.push(boxGeometry(
|
||||
p.x - ux * front + vx * off, p.z - uz * front + vz * off,
|
||||
0.03, p.d / 2 - 0.03, y + 0.06, y + p.h - 0.06, p.rot, 1.2,
|
||||
));
|
||||
}
|
||||
const doors = new Mesh(mergeGeometries(parts), dunkel);
|
||||
doors.castShadow = true;
|
||||
g.add(doors);
|
||||
|
||||
// Zwei schlanke Griffe
|
||||
const griffe: BufferGeometry[] = [];
|
||||
for (const s of [-1, 1]) {
|
||||
const off = s * 0.06;
|
||||
griffe.push(boxGeometry(
|
||||
p.x - ux * (front + 0.035) + vx * off, p.z - uz * (front + 0.035) + vz * off,
|
||||
0.025, 0.025, y + p.h * 0.45, y + p.h * 0.62, p.rot, 1,
|
||||
));
|
||||
}
|
||||
g.add(new Mesh(mergeGeometries(griffe), metall));
|
||||
return g;
|
||||
}
|
||||
|
||||
/** Spielhaus durch Zimmermann: Podest, Bretterwaende, Gelaender, Pultdach. */
|
||||
/**
|
||||
* Spielhaus des Zimmermanns: ein Stelzenhaus. Der Plan zeigt ein Podest von
|
||||
* 1.81 x 2.70 m mit der Kote 1.47 - also rund 0.9 m ueber dem Gelaende - und
|
||||
* darauf an der Ostseite eine Huette von 1.01 x 2.70 m. Der westliche Teil
|
||||
* des Podests bleibt offene Plattform mit Bruestung und Leiter.
|
||||
*/
|
||||
function spielhaus(p: Prop, y: number): Group {
|
||||
const g = new Group();
|
||||
const holz = material(tex.fassadeHolz(), { color: 0xd8b98c, roughness: 0.85 });
|
||||
@@ -122,12 +148,20 @@ function spielhaus(p: Prop, y: number): Group {
|
||||
const plattW = p.w - hutW; // offene Plattform im Westen
|
||||
const plattX = p.x - hw + plattW / 2;
|
||||
|
||||
// Unter der Huette steht ein Betonklotz - im Plan das graue Rechteck von
|
||||
// 1.01 x 2.70 m unter dem Podest. Die offene Plattform daneben steht auf
|
||||
// zwei schlanken Stuetzen.
|
||||
const klotz = new Mesh(
|
||||
boxGeometry(hutX, p.z, hutW, p.d, y - 0.45, deck - 0.06, p.rot, 2),
|
||||
material(tex.beton(), { color: 0x93918c, roughness: 0.92 }),
|
||||
);
|
||||
klotz.name = 'Betonsockel Spielhaus';
|
||||
klotz.castShadow = true; klotz.receiveShadow = true;
|
||||
g.add(klotz);
|
||||
|
||||
const trag: BufferGeometry[] = [];
|
||||
// Vier Stuetzen, etwas eingerueckt
|
||||
for (const dx of [-hw + 0.14, hw - 0.14]) {
|
||||
for (const dz of [-hd + 0.14, hd - 0.14]) {
|
||||
trag.push(boxGeometry(p.x + dx, p.z + dz, 0.11, 0.11, y - 0.35, deck - 0.06, 0, 1));
|
||||
}
|
||||
for (const dz of [-hd + 0.14, hd - 0.14]) {
|
||||
trag.push(boxGeometry(p.x - hw + 0.14, p.z + dz, 0.11, 0.11, y - 0.35, deck - 0.06, 0, 1));
|
||||
}
|
||||
// Podestboden
|
||||
trag.push(boxGeometry(p.x, p.z, p.w, p.d, deck - 0.06, deck, p.rot, 1.2));
|
||||
@@ -242,9 +276,14 @@ function bank(p: Prop, y: number): Group {
|
||||
const seat = new Mesh(boxGeometry(p.x, p.z, p.w, p.d, y + p.h - 0.09, y + p.h, p.rot, belag.tile), stein);
|
||||
seat.castShadow = true; seat.receiveShadow = true;
|
||||
g.add(seat);
|
||||
// Die Fuesse sitzen unter den Enden der Sitzplatte. Der Versatz muss der
|
||||
// gedrehten v-Achse folgen - sonst stehen sie quer daneben.
|
||||
const a = (p.rot * Math.PI) / 180;
|
||||
const vx = -Math.sin(a), vz = Math.cos(a);
|
||||
for (const s of [-1, 1]) {
|
||||
const off = s * (p.d / 2 - 0.22);
|
||||
const m = new Mesh(
|
||||
boxGeometry(p.x, p.z + (s * (p.d / 2 - 0.22)), p.w - 0.08, 0.14, y, y + p.h - 0.09, p.rot, belag.tile),
|
||||
boxGeometry(p.x + vx * off, p.z + vz * off, p.w - 0.08, 0.14, y, y + p.h - 0.09, p.rot, belag.tile),
|
||||
stein,
|
||||
);
|
||||
m.castShadow = true;
|
||||
@@ -381,6 +420,7 @@ export function buildLamps(lamps: Lamp[], height: (x: number, z: number) => numb
|
||||
color: 0xffd9a0, emissive: 0xffc26e, emissiveIntensity: 0, roughness: 0.4,
|
||||
});
|
||||
const pointLights: PointLight[] = [];
|
||||
const spots: SpotLight[] = [];
|
||||
|
||||
for (const l of lamps) {
|
||||
const y = height(l.x, l.z);
|
||||
@@ -405,6 +445,18 @@ export function buildLamps(lamps: Lamp[], height: (x: number, z: number) => numb
|
||||
pl.position.set(l.x, y + (l.kind === 'poller' ? 0.6 : 0.1), l.z);
|
||||
pointLights.push(pl);
|
||||
g.add(pl);
|
||||
|
||||
if (l.kind === 'boden') {
|
||||
// Bodenleuchten sind Strahler: sie leuchten die Travertinstelen und die
|
||||
// Ziergraeser darueber von unten an. Ohne Schatten, sonst wird die
|
||||
// Abendszene mit 11 Strahlern zu teuer.
|
||||
const sp = new SpotLight(0xffd6a2, 0, 5.5, 0.55, 0.55, 1.4);
|
||||
sp.position.set(l.x, y + 0.04, l.z);
|
||||
sp.target.position.set(l.x, y + 3.0, l.z);
|
||||
g.add(sp);
|
||||
g.add(sp.target);
|
||||
spots.push(sp);
|
||||
}
|
||||
group.add(g);
|
||||
}
|
||||
|
||||
@@ -413,6 +465,7 @@ export function buildLamps(lamps: Lamp[], height: (x: number, z: number) => numb
|
||||
setOn(on: boolean) {
|
||||
glowMat.emissiveIntensity = on ? 1.6 : 0;
|
||||
for (const pl of pointLights) pl.intensity = on ? 2.2 : 0;
|
||||
for (const sp of spots) sp.intensity = on ? 5.0 : 0;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user