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();