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:
@@ -0,0 +1,91 @@
|
||||
Sprecher 1 00:00:03
|
||||
So, ich mache jetzt hier ein bisschen sein Memo.
|
||||
Bezüglich paar Sachen, die wir noch nicht so gut gefallen.
|
||||
Ich bin gerade am umherlaufen.
|
||||
Hier in der 3 d Visualisierung.
|
||||
Mir schon mal auffällt ist.
|
||||
Die vom Haus.
|
||||
Bei der unteren Fassade, also beim unteren Stock an der Fassade, bei den langen Seiten.
|
||||
Dort ist das Fenster noch falsch, was im unteren Stock ist das Fenster eigentlich ziemlich zentral.
|
||||
Und auch dort vorne, bei dem beim Überdeckten Sitzplatz.
|
||||
Hat es eigentlich dort an dieser langen Fassade oder langen an der Länge quasi beim Haus.
|
||||
Jetzt tut es so.
|
||||
So, guck spät, also du so spät in der Fassade, wo man direkt in einen gedeckten Sitzplatz reinschauen kann.
|
||||
Da müsste man definitiv nur anpassen.
|
||||
Denn.
|
||||
Auf falle ich das.
|
||||
Beim Sichtschutz dort, bei den Travertinelementen, da hat es im Boden ja immer so einen kleinen grünen Punkt auf dem Plan gehabt, wo ich auf ein kleines Gewächs hinhäute.
|
||||
Jetzt hast du mir dort einfach.
|
||||
So kleine grüne Pille hin gemacht.
|
||||
Dann kannst du mir irgendetwas quasi so ein bisschen.
|
||||
Gewechselt in die Höhe wächst also ich so ein bisschen.
|
||||
Lange Grüne, wirklich schön, voller Travertin.
|
||||
Ein bisschen hochwachsen quasi, und dann eben von der Lampe von unten her beleuchtet werden.
|
||||
Dann was mir auch aufgefallen ist, ist.
|
||||
Im Garten ein Paar, wenn man reinkommt.
|
||||
Dort hat es einen *******.
|
||||
Und der Enkelin.
|
||||
Ich liebe dich komisch Power und Sekretär studieren.
|
||||
Stütze des Bänkli also den Fuß vom Bänkle.
|
||||
Der ist wie an einem anderen Ort, ein Bänkli selber.
|
||||
Dann tu bitte mal einfach nochmal den Plan anschauen und die nächsten Debisualisieren.
|
||||
Da kann man sich noch etwas besser machen.
|
||||
Genau.
|
||||
Dann.
|
||||
Wenn wir hinten am Haus an der Seite hinten durchläuft.
|
||||
Also etwa bei dieser langen Seite von der Fassade nach.
|
||||
Dann kommt ja zuerst der Bereich mit dem Grill.
|
||||
Und dann kommt eigentlich ein Kräuterbett.
|
||||
Also die Heimat dort eingezeichnet hast. Die ist noch ein bisschen komisch. Also du hast irgendwie so eine.
|
||||
Die Wiese geht wie nochmals rauf und das ist auch nicht der Fall. Also die Wiese ist wirklich echt.
|
||||
Überall eben. Aber die ganze Fläche.
|
||||
An dieser Seite noch beschrieben.
|
||||
Und dann können sie sogar direkt am Kies kommen. Das ist nachher das Kräuterbett.
|
||||
Das wäre noch etwas besser, schöner einzuzeichnen und wir sollen sie auch.
|
||||
Der Sandkasten kann sie nachher wieder gut.
|
||||
Und dann?
|
||||
Das Spielhaus finde ich auch ganz cool, das ist auch gut gemacht.
|
||||
Was sicher noch fehlt, ist unter dem Spielhaus.
|
||||
Hat sie noch so einen großen grauen Betonklotz?
|
||||
Ich bin mir nicht ganz sicher, ob der auf den Plänen eingezeichnet ist. Aber komm doch mal mit Pläne mal reinschauen, ob du etwas ähnliches finden tust.
|
||||
Es hat wirklich so einen.
|
||||
Das Nahtbett und klopft es eigentlich unter dem Spielhaus?
|
||||
Und das ist eben im Bett, durchläuft. Also heute schon gibt.
|
||||
Wir wollen eben genau irgendwie verticken und darum haben wir eigentlich das Spielhaus. Wieso ein bisschen also?
|
||||
Ich komme obendrauf.
|
||||
Planen kann.
|
||||
Denn.
|
||||
Bei einem Haustiereingang sind fehlt mir noch ein bisschen der der Hauseingang selber. Also.
|
||||
Dort schaut man ja auch einfach Pläne nochmals im Detail an den Hauseingang.
|
||||
Einfach bitte so machen wie auf den Plänen drauf.
|
||||
Die Fenster könntest mir nur.
|
||||
Machen an der Aussenfassade.
|
||||
Das ist noch im unteren Stock.
|
||||
Fenster gemacht.
|
||||
Nova stockt es zwar auch noch 1.
|
||||
Und dann hast du mal hier für den für das Gartenhäuschen.
|
||||
Quasi ist mir einfach ein riesiger Klotz gebaut.
|
||||
Und.
|
||||
Türe ist quasi wirklich hinten raus vom Garten aus, das Macht keinen Sinn.
|
||||
Tu bitte das Gartenhaus.
|
||||
Wiesen fürs Gartenschrank machen also.
|
||||
Quasi einfach ganz hinten in die Ecke, so wie du es auch jetzt schon hast.
|
||||
Das muss wieso eine Art Gartenschrank rein?
|
||||
Auto 1 80.
|
||||
Hochsee ungefähr 10 oder 2 Meter hoch vielleicht.
|
||||
Etwa 1 80 länge.
|
||||
Aber eben nur etwa so.
|
||||
50 Zentimeter tiefe.
|
||||
Ja, ich glaube, das wäre es für das erste Mal.
|
||||
Kannst du mir diese Sachen weiterbauen?
|
||||
Und einfach noch ein letzter Punkt zusammen noch.
|
||||
Wenn man auch wieder beim Hauseingang dran sind.
|
||||
Dann hast du mir den Weg jetzt eigentlich schön eingezeichnet mit dem Flaschenstrahl.
|
||||
Und dann dort, wo das Geländer anfängt, also runter zu den Nachbarn runter, weil dort eben die Treppe noch runter zu den Nachbauern.
|
||||
Also dort, wo das Gelände schon. Du hast schon die Länder gemacht.
|
||||
Aber neben den Geländern gibt es einfach eine Visa.
|
||||
Das ist natürlich falsch. Also es ist nicht eine Wiese dort, sondern halt wirklich.
|
||||
Den gleichen Look. Eigentlich lade ich hier einfach.
|
||||
Die Flaschensteine hören dann wieder vorne auf, dort, wo das Gelände anfängt und dann wieder die Treppe runter zu den Pflastersteinen.
|
||||
Aber für den Nachbarn?
|
||||
Ja.
|
||||
@@ -187,6 +187,26 @@ Ausserhalb aller Zonen wird die Interpolation der nächstgelegenen Zone
|
||||
fortgesetzt, nicht ihr konstanter Ersatzwert – sonst steht der Belag als
|
||||
schwebende Platte über einer Geländekante.
|
||||
|
||||
### Das Haus nach den Fassadenplänen
|
||||
|
||||
Die Öffnungen sind aus den Fassadenplänen von EHLE Architekten (1:100)
|
||||
abgegriffen. Die Pläne benennen die Fassaden nach Himmelsrichtung, das
|
||||
Datenmodell nach Achsrichtung:
|
||||
|
||||
| Plan | Site | Lage |
|
||||
| --- | --- | --- |
|
||||
| Südostfassade | `south`, z +0.96 | lang, zum Garten |
|
||||
| Nordostfassade | `east`, x +0.28 | kurz, Hauszugang |
|
||||
| Südwestfassade | `west`, x −13.82 | kurz, zur Terrasse |
|
||||
| Nordwestfassade | `north`, z −6.87 | lang, zum Nachbarn – im Plansatz nicht gezeichnet, bleibt geschlossen |
|
||||
|
||||
Zum Garten steht im Erdgeschoss **eine** mittige Fensterfront, daneben das
|
||||
Lamellenband vor dem gedeckten Sitzplatz; im Obergeschoss zwei Fenster mit zwei
|
||||
schmalen Lamellen dazwischen. Der gedeckte Sitzplatz ist **nur gegen Westen
|
||||
offen** („Aussensitzplatz“ in der Südwestfassade) – gegen den Garten steht dort
|
||||
eine geschlossene Wand. Ohne sie schaut man von aussen durch die Fassade in den
|
||||
Sitzplatz hinein.
|
||||
|
||||
### Treppen begehen und springen
|
||||
|
||||
Stufen und Treppen stecken nicht im Höhenraster, sondern sind eigene Körper.
|
||||
|
||||
File diff suppressed because one or more lines are too long
+25
-8
@@ -118,7 +118,10 @@ export function buildBuilding(b: Building): Group {
|
||||
const list: Rect[] = b.openings
|
||||
.filter((o) => o.face === face)
|
||||
.map((o) => ({ u0: Math.min(o.a, o.b), u1: Math.max(o.a, o.b), y0: o.sill, y1: o.head }));
|
||||
if (face === 'south') list.push({ u0: b.loggia.x0, u1: b.loggia.x1, y0: b.floorLevel - 0.4, y1: b.loggia.ceiling });
|
||||
// Der gedeckte Sitzplatz ist nur gegen Westen offen - so zeigt es die
|
||||
// Suedwestfassade ("Aussensitzplatz"). Gegen den Garten steht dort eine
|
||||
// geschlossene Wand mit dem Lamellenband davor; ohne sie schaut man von
|
||||
// aussen durch die Fassade in den Sitzplatz.
|
||||
if (face === 'west') list.push({ u0: b.loggia.z0, u1: b.loggia.z1, y0: b.floorLevel - 0.4, y1: b.loggia.ceiling });
|
||||
return list;
|
||||
};
|
||||
@@ -159,6 +162,11 @@ export function buildBuilding(b: Building): Group {
|
||||
holzMat,
|
||||
);
|
||||
loggia.add(back);
|
||||
const front = new Mesh(
|
||||
boxGeometry((lg.x0 + lg.x1) / 2, lg.z1 - 0.08, lg.x1 - lg.x0, 0.16, b.floorLevel - 0.4, lg.ceiling, 0, holz.tile),
|
||||
holzMat,
|
||||
);
|
||||
loggia.add(front);
|
||||
const ceil = new PlaneGeometry(lg.x1 - lg.x0, lg.z1 - lg.z0);
|
||||
ceil.rotateX(Math.PI / 2);
|
||||
const ceilMesh = new Mesh(ceil, new MeshStandardMaterial({ color: 0xd8d4cc, roughness: 0.85 }));
|
||||
@@ -232,14 +240,23 @@ export function buildBuilding(b: Building): Group {
|
||||
group.add(roof);
|
||||
|
||||
// --- Vordach ueber dem Eingang -----------------------------------------
|
||||
// --- Vordach ueber dem Hauszugang --------------------------------------
|
||||
// Es kragt senkrecht zu seiner Fassade aus; a und b laufen in der
|
||||
// Fassadenrichtung (Site-X bei Nord/Sued, Site-Z bei Ost/West).
|
||||
const cp = b.canopy;
|
||||
const canopy = new Mesh(
|
||||
boxGeometry((cp.a + cp.b) / 2, z0 - cp.depth / 2, cp.b - cp.a, cp.depth, cp.y, cp.y + cp.thickness, 0, 2),
|
||||
new MeshStandardMaterial({ color: 0x5a5148, roughness: 0.6 }),
|
||||
);
|
||||
canopy.name = 'Vordach Eingang';
|
||||
canopy.castShadow = true;
|
||||
group.add(canopy);
|
||||
const dachMat = new MeshStandardMaterial({ color: 0x5a5148, roughness: 0.6 });
|
||||
const mitte = (cp.a + cp.b) / 2, laenge = Math.abs(cp.b - cp.a);
|
||||
let dach: Mesh;
|
||||
if (cp.face === 'east' || cp.face === 'west') {
|
||||
const xf = cp.face === 'east' ? x1 + cp.depth / 2 : x0 - cp.depth / 2;
|
||||
dach = new Mesh(boxGeometry(xf, mitte, cp.depth, laenge, cp.y, cp.y + cp.thickness, 0, 2), dachMat);
|
||||
} else {
|
||||
const zf = cp.face === 'south' ? z1 + cp.depth / 2 : z0 - cp.depth / 2;
|
||||
dach = new Mesh(boxGeometry(mitte, zf, laenge, cp.depth, cp.y, cp.y + cp.thickness, 0, 2), dachMat);
|
||||
}
|
||||
dach.name = 'Vordach Hauszugang';
|
||||
dach.castShadow = true;
|
||||
group.add(dach);
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
+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;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
+47
-13
@@ -6,7 +6,7 @@
|
||||
* statt Blattgeometrie - das bleibt auch auf dem Handy fluessig.
|
||||
*/
|
||||
import {
|
||||
BoxGeometry, BufferAttribute, BufferGeometry, Color, CylinderGeometry, DoubleSide,
|
||||
BufferAttribute, BufferGeometry, Color, CylinderGeometry, DoubleSide,
|
||||
Group, IcosahedronGeometry, InstancedMesh, Matrix4, Mesh, MeshStandardMaterial,
|
||||
Object3D, Quaternion, Vector3,
|
||||
} from 'three';
|
||||
@@ -67,12 +67,43 @@ function blob(
|
||||
return g;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ein Grashalm: schmales Band, das sich nach oben verjuengt und ueberhaengt.
|
||||
* Fusspunkt im Ursprung, Hoehe rund 1.10 m - Lage und Neigung kommen aus der
|
||||
* Instanzmatrix, damit die Normalen nicht verzerrt werden.
|
||||
*/
|
||||
function halmGeometry(): BufferGeometry {
|
||||
const SEG = 6, H = 1.10, W = 0.026;
|
||||
const pos: number[] = [];
|
||||
const uv: number[] = [];
|
||||
const pt = (i: number) => {
|
||||
const f = i / SEG;
|
||||
const y = H * f;
|
||||
// Ueberhang: der Halm biegt sich mit dem Quadrat der Hoehe nach vorn.
|
||||
const x = 0.42 * H * f * f;
|
||||
const w = W * (1 - f * 0.92);
|
||||
return { x, y, w };
|
||||
};
|
||||
for (let i = 0; i < SEG; i++) {
|
||||
const a = pt(i), b2 = pt(i + 1);
|
||||
pos.push(a.x, a.y, -a.w, a.x, a.y, a.w, b2.x, b2.y, b2.w);
|
||||
pos.push(a.x, a.y, -a.w, b2.x, b2.y, b2.w, b2.x, b2.y, -b2.w);
|
||||
const v0 = i / SEG, v1 = (i + 1) / SEG;
|
||||
uv.push(0, v0, 1, v0, 1, v1, 0, v0, 1, v1, 0, v1);
|
||||
}
|
||||
const g = new BufferGeometry();
|
||||
g.setAttribute('position', new BufferAttribute(new Float32Array(pos), 3));
|
||||
g.setAttribute('uv', new BufferAttribute(new Float32Array(uv), 2));
|
||||
g.computeVertexNormals();
|
||||
return g;
|
||||
}
|
||||
|
||||
const LAUB = {
|
||||
parrotia: 0x76944f,
|
||||
apfel: 0x7e9c52,
|
||||
eibe: 0x466b3a,
|
||||
strauch: 0x6b8f45,
|
||||
gras: 0xa3b45c,
|
||||
gras: 0x9aad5b,
|
||||
kraut: 0x8ba455,
|
||||
liguster: 0x557a37,
|
||||
};
|
||||
@@ -437,24 +468,27 @@ export function buildVegetation(
|
||||
}), 'Beerensträucher');
|
||||
if (strauchMesh) group.add(strauchMesh);
|
||||
|
||||
// --- Graeser: Buendel schmaler, leicht geneigter Halme -------------------
|
||||
// --- Ziergraeser --------------------------------------------------------
|
||||
// Im Plan stehen zwischen den Travertinstelen kleine gruene Punkte. Das
|
||||
// sind keine Polster, sondern Graeser, die an den Stelen hochwachsen und
|
||||
// abends von den Bodenleuchten angestrahlt werden. Ein Halm ist ein
|
||||
// schmales, sich verjuengendes Band, das nach oben ueberhaengt.
|
||||
const graeser = plants.filter((p) => p.type === 'gras' && p.x !== undefined);
|
||||
if (graeser.length) {
|
||||
// Ein Halm als Einheitsgeometrie mit Fusspunkt im Ursprung; Neigung und
|
||||
// Position kommen aus der Instanzmatrix, nicht aus einer Mesh-Rotation.
|
||||
const blade = new BoxGeometry(0.022, 1, 0.022);
|
||||
blade.translate(0, 0.5, 0);
|
||||
const mesh = new InstancedMesh(blade, foliage(LAUB.gras, 0.98), graeser.length * 9);
|
||||
const halm = halmGeometry();
|
||||
const mesh = new InstancedMesh(halm, foliage(LAUB.gras, 0.96, false), graeser.length * 16);
|
||||
(mesh.material as MeshStandardMaterial).side = DoubleSide;
|
||||
const dummy = new Object3D();
|
||||
let k = 0;
|
||||
for (const p of graeser) {
|
||||
const y = height(p.x!, p.z!);
|
||||
for (let i = 0; i < 9; i++) {
|
||||
for (let i = 0; i < 16; i++) {
|
||||
const a = r() * Math.PI * 2;
|
||||
const rad = (p.r ?? 0.12) * r() * 0.9;
|
||||
const rad = (p.r ?? 0.12) * r() * 0.8;
|
||||
dummy.position.set(p.x! + Math.cos(a) * rad, y, p.z! + Math.sin(a) * rad);
|
||||
dummy.rotation.set((r() - 0.5) * 0.45, r() * Math.PI, (r() - 0.5) * 0.45);
|
||||
dummy.scale.set(1, 0.42 + r() * 0.4, 1);
|
||||
dummy.rotation.set(0, a + (r() - 0.5) * 1.2, 0);
|
||||
const s = 0.85 + r() * 0.55;
|
||||
dummy.scale.set(0.75 + r() * 0.5, s, 1);
|
||||
dummy.updateMatrix();
|
||||
mesh.setMatrixAt(k++, dummy.matrix);
|
||||
}
|
||||
@@ -462,7 +496,7 @@ export function buildVegetation(
|
||||
mesh.count = k;
|
||||
mesh.instanceMatrix.needsUpdate = true;
|
||||
mesh.castShadow = true;
|
||||
mesh.name = 'Gräser';
|
||||
mesh.name = 'Ziergräser';
|
||||
group.add(mesh);
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -147,7 +147,7 @@ export interface Building {
|
||||
parapet: number;
|
||||
outline: Vec2[];
|
||||
loggia: { x0: number; z0: number; x1: number; z1: number; ceiling: number };
|
||||
canopy: { a: number; b: number; depth: number; y: number; thickness: number };
|
||||
canopy: { face: 'north' | 'south' | 'east' | 'west'; a: number; b: number; depth: number; y: number; thickness: number };
|
||||
openings: Opening[];
|
||||
}
|
||||
|
||||
|
||||
+57
-23
@@ -98,7 +98,12 @@ export const LEVELS = [
|
||||
// steht dort "Steigung 9.9%".
|
||||
id: 'weg-ost', plane: [0, 0.1178, 0.1224], types: ['tegula'],
|
||||
note: 'Ostweg als Rampe',
|
||||
polygon: [[2.80, -4.30], [4.55, -4.30], [4.55, 5.05], [2.80, 5.05]],
|
||||
polygon: [[2.05, -4.30], [4.55, -4.30], [4.55, 5.05], [2.05, 5.05]],
|
||||
},
|
||||
{
|
||||
id: 'weg-ost-gelaender', level: -0.45, types: ['tegula'],
|
||||
note: 'Belagstreifen neben dem Gelaender der Osttreppe',
|
||||
polygon: [[2.05, -6.60], [2.92, -6.60], [2.92, -4.15], [2.05, -4.15]],
|
||||
},
|
||||
{
|
||||
id: 'weg-ost-unten', level: -1.57, types: ['tegula'],
|
||||
@@ -118,7 +123,7 @@ export const LEVELS = [
|
||||
// ohne feste Vorgabe zieht die Interpolation den Rasen huegelig hoch.
|
||||
id: 'oben', level: -0.02, flatten: true,
|
||||
note: 'Obere Ebene: Rasen und Kiesfeld, eben auf -0.02',
|
||||
polygon: [[-18.30, 0.90], [-8.60, 0.90], [-8.60, 3.90], [-18.30, 3.90]],
|
||||
polygon: [[-18.30, 0.90], [-4.70, 0.90], [-4.70, 4.00], [-18.30, 4.00]],
|
||||
},
|
||||
{
|
||||
id: 'unten',
|
||||
@@ -218,8 +223,11 @@ export const WALLS = [
|
||||
|
||||
/** Freistehende Bauteile und Moeblierung. rot = Drehung um Y in Grad. */
|
||||
export const PROPS = [
|
||||
{ id: 'gartenhaus', type: 'gartenhaus', x: 1.20, z: -3.60, w: 1.8, d: 1.8, h: 2.15, rot: 0,
|
||||
note: 'Standort Gartenhaus 1,8 x 1,8 m' },
|
||||
// Der Plan reserviert 1.8 x 1.8 m fuer das Gartenhaus. Gebaut wird dort ein
|
||||
// schmaler Gartenschrank: 1.80 m breit, nur 0.50 m tief, rund 2.00 m hoch,
|
||||
// mit dem Ruecken in der Ecke und den Tueren zum Garten.
|
||||
{ id: 'gartenschrank', type: 'gartenhaus', x: 1.87, z: -3.58, w: 0.50, d: 1.80, h: 2.00, rot: 0,
|
||||
note: 'Gartenschrank 1.80 x 0.50 m, 2.00 m hoch' },
|
||||
// Der Plan zeichnet zwei Rechtecke: das Podest 1.81 x 2.70 m (x -0.95 bis
|
||||
// 0.86) und darauf die Huette 1.01 x 2.70 m an der Ostseite (x -0.15 bis
|
||||
// 0.86). Die Kote 1.47 im Podest ist die Hoehe des Bodens - ein Stelzenhaus,
|
||||
@@ -274,7 +282,12 @@ export const EXTRA_SURFACES = [
|
||||
// beginnt die Wiese. Zwei Leuchten stehen daran.
|
||||
type: 'tegula',
|
||||
note: 'Weg an der Ostseite, Rampe oberhalb der Treppe (Steigung rund 12%)',
|
||||
polygon: [[2.86, -4.18], [4.45, -4.18], [4.45, 4.96], [2.86, 4.96]],
|
||||
polygon: [[2.12, -4.18], [4.45, -4.18], [4.45, 4.96], [2.12, 4.96]],
|
||||
},
|
||||
{
|
||||
type: 'tegula',
|
||||
note: 'Belagstreifen neben dem Gelaender, bis an den Kopf der Osttreppe',
|
||||
polygon: [[2.12, -6.55], [2.86, -6.55], [2.86, -4.18], [2.12, -4.18]],
|
||||
},
|
||||
{
|
||||
type: 'tegula',
|
||||
@@ -386,25 +399,46 @@ export const BUILDING = {
|
||||
* der Suedwestecke, oben vom Obergeschoss ueberdeckt.
|
||||
*/
|
||||
loggia: { x0: -13.82, z0: -3.06, x1: -9.06, z1: 0.96, ceiling: 2.75 },
|
||||
/** Vordach ueber dem Hauseingang an der Nordostfassade (siehe Foto). */
|
||||
canopy: { a: -3.20, b: 0.28, depth: 1.35, y: 2.60, thickness: 0.16 },
|
||||
/**
|
||||
* Vordach ueber dem Hauszugang an der Nordostfassade (x = +0.28). Der
|
||||
* Situationsplan zeigt ein Eingangspodest von 1.05 + 1.13 m Tiefe, darueber
|
||||
* das "Vordach (Holzbauer)".
|
||||
*/
|
||||
canopy: { face: 'east', a: -2.95, b: 0.75, depth: 2.20, y: 2.28, thickness: 0.16 },
|
||||
/**
|
||||
* Oeffnungen, abgegriffen aus den Fassadenplaenen von EHLE Architekten
|
||||
* (1:100, 27.04.2021). Die Fassaden heissen dort nach Himmelsrichtung:
|
||||
* Suedostfassade = z +0.96 (lang, zum Garten) -> face 'south'
|
||||
* Nordostfassade = x +0.28 (kurz, Hauszugang) -> face 'east'
|
||||
* Suedwestfassade = x -13.82 (kurz, zur Terrasse) -> face 'west'
|
||||
* Nordwestfassade = z -6.87 (lang, zum Nachbarn) -> face 'north'
|
||||
* Die Nordwestfassade ist im Plansatz nicht gezeichnet; sie steht am
|
||||
* Nachbarbau und bleibt geschlossen.
|
||||
*/
|
||||
openings: [
|
||||
// Suedwestfassade (z = 0.96 / 0.51) - grosse Fensterfronten zum Garten
|
||||
{ face: 'south', a: -4.40, b: -0.20, sill: 0.0, head: 2.45, kind: 'glass' },
|
||||
{ face: 'south', a: -12.90, b: -9.40, sill: 0.0, head: 2.45, kind: 'glass' },
|
||||
{ face: 'south', a: -3.90, b: -0.60, sill: 3.35, head: 5.00, kind: 'glass', upper: true },
|
||||
{ face: 'south', a: -12.60, b: -9.70, sill: 3.35, head: 5.00, kind: 'glass', upper: true },
|
||||
// Westfassade (x = -13.82) - Fenster
|
||||
{ face: 'west', a: -5.84, b: -4.24, sill: 0.85, head: 2.35, kind: 'window' },
|
||||
{ face: 'west', a: -5.60, b: -4.50, sill: 3.75, head: 5.00, kind: 'window', upper: true },
|
||||
// Nordostfassade (z = -6.87) - Eingang und Lamellenfenster
|
||||
{ face: 'north', a: -1.90, b: -0.90, sill: 0.0, head: 2.35, kind: 'door' },
|
||||
{ face: 'north', a: -5.60, b: -3.90, sill: 0.90, head: 2.35, kind: 'window' },
|
||||
{ face: 'north', a: -11.60, b: -10.20, sill: 0.90, head: 2.35, kind: 'slats' },
|
||||
{ face: 'north', a: -5.40, b: -4.10, sill: 3.75, head: 5.10, kind: 'window', upper: true },
|
||||
{ face: 'north', a: -11.40, b: -10.40, sill: 3.75, head: 5.10, kind: 'slats', upper: true },
|
||||
// Ostfassade (x = 0.28)
|
||||
{ face: 'east', a: -5.60, b: -4.30, sill: 0.90, head: 2.35, kind: 'window' },
|
||||
// -- Suedostfassade (z = +0.96), zum Garten ---------------------------
|
||||
// Erdgeschoss: das Lamellenband vor dem gedeckten Sitzplatz und die
|
||||
// grosse, mittig sitzende Fensterfront.
|
||||
{ face: 'south', a: -12.44, b: -9.32, sill: 0.15, head: 2.20, kind: 'slats' },
|
||||
{ face: 'south', a: -8.82, b: -4.67, sill: 0.0, head: 2.19, kind: 'glass' },
|
||||
// Obergeschoss: zwei Fenster aussen, dazwischen zwei schmale Lamellen.
|
||||
{ face: 'south', a: -12.44, b: -9.98, sill: 3.08, head: 4.90, kind: 'window', upper: true },
|
||||
{ face: 'south', a: -8.90, b: -7.67, sill: 3.30, head: 4.90, kind: 'slats', upper: true },
|
||||
{ face: 'south', a: -6.55, b: -5.40, sill: 3.30, head: 4.90, kind: 'slats', upper: true },
|
||||
{ face: 'south', a: -3.60, b: -1.10, sill: 3.08, head: 4.90, kind: 'window', upper: true },
|
||||
|
||||
// -- Nordostfassade (x = +0.28), Hauszugang ---------------------------
|
||||
// Der Zugang ist ein Element aus Tuerfluegel und feststehender
|
||||
// Verglasung, zusammen rund 3.15 m breit.
|
||||
{ face: 'east', a: -0.65, b: 0.48, sill: 0.0, head: 2.20, kind: 'door' },
|
||||
{ face: 'east', a: -2.67, b: -0.71, sill: 0.0, head: 2.20, kind: 'glass' },
|
||||
{ face: 'east', a: -6.40, b: -5.54, sill: 1.19, head: 2.15, kind: 'slats' },
|
||||
{ face: 'east', a: -5.93, b: -3.36, sill: 3.18, head: 4.94, kind: 'window', upper: true },
|
||||
|
||||
// -- Suedwestfassade (x = -13.82), zur Terrasse -----------------------
|
||||
// Der grosse Ausschnitt daneben ist der gedeckte Sitzplatz selbst.
|
||||
{ face: 'west', a: -5.60, b: -3.91, sill: 0.42, head: 2.01, kind: 'slats' },
|
||||
{ face: 'west', a: -5.71, b: -3.07, sill: 3.17, head: 4.97, kind: 'window', upper: true },
|
||||
],
|
||||
};
|
||||
|
||||
|
||||
+12
-1
@@ -173,6 +173,15 @@ for (const st of stelen) {
|
||||
}
|
||||
stelen.sort((a, b) => a.z - b.z || a.x - b.x);
|
||||
|
||||
/**
|
||||
* Die gruenen Punkte, die im Plan zwischen den Travertinstelen im Kiesbett
|
||||
* stehen, sind keine Polster, sondern Ziergraeser - sie wachsen an den
|
||||
* Stelen hoch und werden von den Bodenleuchten angestrahlt.
|
||||
*/
|
||||
function imKiesbett(x, z) {
|
||||
return stelen.some((s) => Math.hypot(s.x - x, s.z - z) < 1.1);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 2. Hecken und Pflanzen aus den Plankreisen
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -187,7 +196,9 @@ for (const s of raw.shapes) {
|
||||
const { cx, cz, r } = s.circle;
|
||||
if (r < 0.06) continue; // Lampensymbole und Punktraster
|
||||
if (treeAt(cx, cz)) continue; // Solitaere kommen aus dem Authoring
|
||||
plants.push({ type, x: round(cx), z: round(cz), r: round(r, 2) });
|
||||
// Kraeuter im Kiesbett des Sichtschutzes sind Ziergraeser.
|
||||
const art = (type === 'kraut' || type === 'gras') && imKiesbett(cx, cz) ? 'gras' : type;
|
||||
plants.push({ type: art, x: round(cx), z: round(cz), r: round(r, 2) });
|
||||
}
|
||||
// Der Ligusterhecken-Streifen ist im Plan eine Flaeche, keine Kreise.
|
||||
for (const s of raw.shapes) {
|
||||
|
||||
Reference in New Issue
Block a user