v0.4.15: gevelbekleding per woning (rabat/steen, override per wand), stenen trasraam (110 mm, 181 texturen), riet per unit, vloerafwerking, verfkleur binnen, boeideelkleur, gevel tot onder dakrand, binnenhoek-fixes; testontwerp als standaard

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Machiel
2026-09-30 19:47:49 +02:00
co-authored by Claude Opus 5.5
parent 7d91416004
commit 12c2579471
743 changed files with 675 additions and 99 deletions
+57 -2
View File
@@ -213,6 +213,32 @@ window.FloorModel = (() => {
return { calcData, resolvedOpenings, chassisData };
}
// Vloerafwerking (bovenop de OSB). Texturen in assets/textures/vloer/,
// naadloos herhalend; tileMM = maat van één tegel van de textuur.
const FINISHES = {
geen: { label: 'Geen (OSB)' },
visgraat_001: { label: 'Visgraat (Hongaarse punt)', file: 'assets/textures/vloer/visgraat_001.jpg', tileMM: 3000 }
};
const finishMaterials = {};
function finishMaterial(key) {
const f = FINISHES[key];
if (!f || !f.file) return null;
if (finishMaterials[key]) return finishMaterials[key];
const mat = new THREE.MeshStandardMaterial({ color: 0xb89a7c, roughness: 0.7 });
if (typeof document !== 'undefined' && THREE.TextureLoader) {
new THREE.TextureLoader().load(f.file, (tex) => {
tex.wrapS = tex.wrapT = THREE.RepeatWrapping;
tex.repeat.set(1000 / f.tileMM, 1000 / f.tileMM);
tex.anisotropy = 8;
mat.map = tex;
mat.color.set(0xffffff);
mat.needsUpdate = true;
});
}
finishMaterials[key] = mat;
return mat;
}
function createMaterials() {
return {
woodMaterial: new THREE.MeshStandardMaterial({ color: 0xc29b62, roughness: 0.6 }),
@@ -226,7 +252,10 @@ window.FloorModel = (() => {
// Z = -diepte/2), Y = dikte (balkhoogte + OSB). Balken en isolatie zijn
// simpele dozen; de OSB-onderlaag is een geëxtrudeerd Shape met ronde
// uitsparingen voor de doorvoeren.
// opts: { materials, osbVisible }
// opts: { materials, osbVisible, finish }
// finish (optioneel): { key (FINISHES), originX, originZ } — afwerking als
// dun vlak op de OSB; UV's in meters vanaf origin (m, woning-coördinaten
// van het vloermidden), zodat het patroon over units doorloopt.
// ----------------------------------------------------------
function build3D(calcData, resolvedOpenings, chassisData, opts) {
const { woodMaterial, osbMaterial, wheelMaterial } = opts.materials;
@@ -311,6 +340,32 @@ window.FloorModel = (() => {
floorGroup3D.add(mesh);
});
// Vloerafwerking: vlak net boven de OSB, over de hele vloer (onder de
// wanden verdwijnt het), met de doorvoeren uitgespaard.
const fin = opts.finish && finishMaterial(opts.finish.key);
if (fin) {
const shape = new THREE.Shape();
shape.moveTo(-widthM / 2, -depthM / 2);
shape.lineTo(widthM / 2, -depthM / 2);
shape.lineTo(widthM / 2, depthM / 2);
shape.lineTo(-widthM / 2, depthM / 2);
shape.closePath();
resolvedOpenings.filter(o => !o.removed && o.r > 0).forEach(o => {
const hole = new THREE.Path();
hole.absarc(o.x * toM, o.y * toM - depthM / 2, o.r * toM, 0, Math.PI * 2, false);
shape.holes.push(hole);
});
const geo = new THREE.ShapeGeometry(shape);
const ox = opts.finish.originX || 0, oz = opts.finish.originZ || 0;
const pos = geo.attributes.position, uv = geo.attributes.uv;
// Na het kantelen (-90° om X) wordt vorm-y wereld -z.
for (let k = 0; k < pos.count; k++) uv.setXY(k, pos.getX(k) + ox, -pos.getY(k) + oz);
const mesh = new THREE.Mesh(geo, fin);
mesh.rotation.x = -Math.PI / 2;
mesh.position.y = joistHM + underlayM + 0.0005;
mesh.receiveShadow = true;
floorGroup3D.add(mesh);
}
return floorGroup3D;
}
@@ -338,7 +393,7 @@ window.FloorModel = (() => {
}
return {
validateState, computeFrame, topHeight, resolveOpenings: resolveFloorOpenings, computeChassisWheels,
FINISHES, finishMaterial, validateState, computeFrame, topHeight, resolveOpenings: resolveFloorOpenings, computeChassisWheels,
computeSpecs, computeFromState, createMaterials, build3D
};
})();