From ae5072549592d249d43665f0c3295530ae09b578 Mon Sep 17 00:00:00 2001 From: Richard Zink Date: Tue, 6 Oct 2026 16:15:58 +0200 Subject: [PATCH] Simplify rendering --- assets/index.html | 234 +++++++++++++++------------------------------- 1 file changed, 73 insertions(+), 161 deletions(-) diff --git a/assets/index.html b/assets/index.html index 85cd56a..17f469b 100644 --- a/assets/index.html +++ b/assets/index.html @@ -40,19 +40,7 @@ window.addEventListener('resize', resize); resize(); - const camera = { - fov: 800 - }; - - function rotateY(point, angle) { - const cos = Math.cos(angle); - const sin = Math.sin(angle); - return { - x: point.x * cos - point.z * sin, - y: point.y, - z: point.x * sin + point.z * cos - }; - } + const camera = { fov: 800 }; class Point3D { constructor(x, y, z) { @@ -67,66 +55,14 @@ const yIso = (this.x + this.z) * 0.5 - this.y; const x2d = xIso * scale + width / 2; const y2d = yIso * scale + height / 2; - return { x: x2d, y: y2d, scale: scale }; + return { x: x2d, y: y2d }; } } - class TriangleMesh { - constructor(p1, p2, p3, color) { - this.p1 = p1; - this.p2 = p2; - this.p3 = p3; - this.color = color; - } - - calculateNormal() { - const v1 = { x: this.p2.x - this.p1.x, y: this.p2.y - this.p1.y, z: this.p2.z - this.p1.z }; - const v2 = { x: this.p3.x - this.p1.x, y: this.p3.y - this.p1.y, z: this.p3.z - this.p1.z }; - this.normal = { - x: v1.y * v2.z - v1.z * v2.y, - y: v1.z * v2.x - v1.x * v2.z, - z: v1.x * v2.y - v1.y * v2.x - }; - const len = Math.sqrt(this.normal.x**2 + this.normal.y**2 + this.normal.z**2); - if (len > 0) { - this.normal.x /= len; - this.normal.y /= len; - this.normal.z /= len; - } - const light = { x: 0.3, y: 0.5, z: 0.8 }; - const dot = this.normal.x * light.x + this.normal.y * light.y + this.normal.z * light.z; - this.intensity = Math.max(0.15, dot); - } - - isBackface() { - return this.normal.y > 0.5; - } - - draw() { - if (this.isBackface()) return; - - const proj1 = this.p1.project(); - const proj2 = this.p2.project(); - const proj3 = this.p3.project(); - - const colorVal = Math.floor(this.intensity * 200); - const r = this.color.r || 0; - const g = this.color.g || (this.color.isWater ? 100 : colorVal); - const b = this.color.b || (this.color.isWater ? 200 : 0); - const fillColor = `rgba(${Math.floor(r * this.intensity)}, ${Math.floor(g * this.intensity)}, ${Math.floor(b * this.intensity)}, 0.85)`; - - ctx.beginPath(); - ctx.moveTo(proj1.x, proj1.y); - ctx.lineTo(proj2.x, proj2.y); - ctx.lineTo(proj3.x, proj3.y); - ctx.closePath(); - ctx.fillStyle = fillColor; - ctx.fill(); - - ctx.strokeStyle = this.color.isWater ? '#00ccff' : (this.color.r ? '#ff3333' : '#00ff66'); - ctx.lineWidth = 1; - ctx.stroke(); - } + function rotateY(p, angle) { + const cos = Math.cos(angle); + const sin = Math.sin(angle); + return { x: p.x * cos - p.z * sin, y: p.y, z: p.x * sin + p.z * cos }; } class Landscape { @@ -147,7 +83,6 @@ y += Math.cos(nz * 0.7) * 60; y += Math.sin(nx * 1.5 + nz * 0.3) * 30; y += Math.cos(nz * 1.5 - nx * 0.4) * 25; - y += Math.sin(nx * 2.5 - nz * 1.8) * 10; return y; } @@ -161,35 +96,6 @@ this.generateTriangles(); } - generateTriangles() { - this.triangles = []; - const minIdx = -this.gridRange; - const maxIdx = this.gridRange; - - for (let x = minIdx; x <= maxIdx; x++) { - for (let z = minIdx; z <= maxIdx; z++) { - const p1 = this.getPoint(x, z); - const p2 = this.getPoint(x + 1, z); - const p3 = this.getPoint(x + 1, z + 1); - const p4 = this.getPoint(x, z + 1); - - const isWater1 = this.isWater(p1.y); - const color1 = isWater1 ? { isWater: true, r: 0, g: 0, b: 180 } : { r: 0, g: 180, b: 0 }; - const isWater2 = this.isWater(p2.y); - const color2 = isWater2 ? { isWater: true, r: 0, g: 0, b: 180 } : { r: 0, g: 180, b: 0 }; - const isWater3 = this.isWater(p3.y); - const color3 = isWater3 ? { isWater: true, r: 0, g: 0, b: 180 } : { r: 0, g: 180, b: 0 }; - const isWater4 = this.isWater(p4.y); - const color4 = isWater4 ? { isWater: true, r: 0, g: 0, b: 180 } : { r: 0, g: 180, b: 0 }; - - this.triangles.push(new TriangleMesh(p1, p2, p3)); - this.triangles.push(new TriangleMesh(p1, p3, p4)); - } - } - - this.triangles.forEach(t => t.calculateNormal()); - } - getPoint(x, z) { const worldX = x * this.gridSize + this.offsetX; const worldZ = z * this.gridSize + this.offsetZ; @@ -197,13 +103,51 @@ return new Point3D(worldX, y, worldZ); } - render() { - this.triangles.sort((a, b) => { - const za = (a.p1.z + a.p2.z + a.p3.z) / 3; - const zb = (b.p1.z + b.p2.z + b.p3.z) / 3; - return zb - za; - }); - this.triangles.forEach(tri => tri.draw()); + generateTriangles() { + this.triangles = []; + for (let x = -this.gridRange; x <= this.gridRange; x++) { + for (let z = -this.gridRange; z <= this.gridRange; z++) { + const p1 = this.getPoint(x, z); + const p2 = this.getPoint(x + 1, z); + const p3 = this.getPoint(x + 1, z + 1); + const p4 = this.getPoint(x, z + 1); + + const w1 = this.isWater(p1.y); + const w2 = this.isWater(p2.y); + const w3 = this.isWater(p3.y); + const w4 = this.isWater(p4.y); + + this.triangles.push({ p1, p2, p3, isWater: w1 || w2 || w3, depth: (p1.z + p2.z + p3.z) / 3 }); + this.triangles.push({ p1, p3, p4, isWater: w1 || w3 || w4, depth: (p1.z + p3.z + p4.z) / 3 }); + } + } + } + + draw() { + this.triangles.sort((a, b) => b.depth - a.depth); + this.triangles.forEach(tri => { + const proj1 = tri.p1.project(); + const proj2 = tri.p2.project(); + const proj3 = tri.p3.project(); + + const v1 = { x: tri.p2.x - tri.p1.x, y: tri.p2.y - tri.p1.y, z: tri.p2.z - tri.p1.z }; + const v2 = { x: tri.p3.x - tri.p1.x, y: tri.p3.y - tri.p1.y, z: tri.p3.z - tri.p1.z }; + const crossZ = v1.x * v2.y - v1.y * v2.x; + if (crossZ >= 0) return; + + const baseColor = tri.isWater ? 'rgba(0, 150, 220, 0.7)' : 'rgba(0, 200, 0, 0.7)'; + ctx.fillStyle = baseColor; + ctx.beginPath(); + ctx.moveTo(proj1.x, proj1.y); + ctx.lineTo(proj2.x, proj2.y); + ctx.lineTo(proj3.x, proj3.y); + ctx.closePath(); + ctx.fill(); + + ctx.strokeStyle = tri.isWater ? '#00ccff' : '#00ff66'; + ctx.lineWidth = 1; + ctx.stroke(); + }); } } @@ -216,11 +160,10 @@ init() { const verts = []; - const faces = []; const fuselageLength = 80; const fuselageRadius = 8; - const segments = 12; + const segments = 8; for (let i = 0; i < segments; i++) { const angle = (i / segments) * Math.PI * 2; @@ -233,7 +176,7 @@ for (let i = 0; i < segments; i++) { const angle1 = (i / segments) * Math.PI * 2; - const angle2 = ((i + 1) / segments) * Math.PI * 2; + const angle2 = ((i + 1) % segments / segments) * Math.PI * 2; verts.push(new Point3D( Math.cos(angle1) * fuselageRadius, Math.sin(angle1) * fuselageRadius, @@ -246,21 +189,15 @@ )); } + const tipIdx = verts.length; verts.push(new Point3D(0, 0, fuselageLength + 30)); - const baseIdx = 0; - const middleStart = segments; - const middleBase = middleStart; - const tipIdx = verts.length - 1; - for (let i = 0; i < segments; i++) { const next = (i + 1) % segments; const middleStart = segments + i * 2; const middleNext = segments + ((i + 1) % segments) * 2; - faces.push([baseIdx + i, baseIdx + next, tipIdx]); - faces.push([middleStart, middleNext, middleStart + 1]); - faces.push([middleStart + 1, middleNext, middleNext + 1]); + verts.push([baseIdx = i, next].forEach(() => {})); } const wingSpan = 100; @@ -273,30 +210,10 @@ verts.push(new Point3D(wingSpan / 2 - 5, wingY, 35)); verts.push(new Point3D(wingSpan / 2, wingY, 10)); - faces.push([wingBackLeft, wingBackLeft + 1, wingBackLeft + 2]); - faces.push([wingBackLeft, wingBackLeft + 2, wingBackLeft + 3]); - - const tailSpan = 40; - const tailChord = 15; - const tailY = 12; - - const tailBottom = verts.length; - verts.push(new Point3D(-tailSpan / 2, tailY, fuselageLength - 30)); - verts.push(new Point3D(-tailSpan / 2 + 3, tailY, fuselageLength - 5)); - verts.push(new Point3D(tailSpan / 2 - 3, tailY, fuselageLength - 5)); - verts.push(new Point3D(tailSpan / 2, tailY, fuselageLength - 30)); - - faces.push([tailBottom, tailBottom + 1, tailBottom + 2]); - faces.push([tailBottom, tailBottom + 2, tailBottom + 3]); - - const vertFinTop = verts.length; - verts.push(new Point3D(0, tailY + 25, fuselageLength - 25)); - verts.push(new Point3D(0, tailY + 25, fuselageLength - 5)); - - faces.push([vertFinTop, vertFinTop + 1, middleStart]); + this.faces.push([wingBackLeft, wingBackLeft + 1, wingBackLeft + 2]); + this.faces.push([wingBackLeft, wingBackLeft + 2, wingBackLeft + 3]); this.vertices = verts; - this.faces = faces; } update() { @@ -306,32 +223,27 @@ } render() { - const transformedVerts = this.vertices.map(v => { - const rot = rotateY(v, this.yaw); - const cosR = Math.cos(this.roll), sinR = Math.sin(this.roll); - const x1 = rot.x * cosR - rot.y * sinR; - const y1 = rot.x * sinR + rot.y * cosR; - const cosP = Math.cos(this.pitch), sinP = Math.sin(this.pitch); - const y2 = y1 * cosP - rot.z * sinP; - const z2 = y1 * sinP + rot.z * cosP; - return new Point3D(x1, y2, 150 + z2); - }); - ctx.strokeStyle = '#ff0000'; ctx.lineWidth = 2; ctx.shadowBlur = 15; ctx.shadowColor = '#ff0000'; - this.faces.forEach((face) => { - const p1 = transformedVerts[face[0]]; - const p2 = transformedVerts[face[1]]; - const p3 = transformedVerts[face[2]]; - + this.vertices.forEach(v => { + const proj = v.project(); ctx.beginPath(); - ctx.moveTo(p1.project().x, p1.project().y); - ctx.lineTo(p2.project().x, p2.project().y); - ctx.lineTo(p3.project().x, p3.project().y); - ctx.closePath(); + ctx.arc(proj.x, proj.y, 3, 0, Math.PI * 2); + ctx.fillStyle = '#ff0000'; + ctx.fill(); + }); + + this.vertices.forEach((v, i) => { + const nextIdx = (i + 1) % this.vertices.length; + if (nextIdx <= 2) return; + const p1 = v.project(); + const p2 = this.vertices[nextIdx].project(); + ctx.beginPath(); + ctx.moveTo(p1.x, p1.y); + ctx.lineTo(p2.x, p2.y); ctx.stroke(); }); @@ -347,7 +259,7 @@ ctx.fillRect(0, 0, width, height); landscape.update(); - landscape.render(); + landscape.draw(); airplane.update(); airplane.render();