diff --git a/assets/index.html b/assets/index.html index 40bb252..c30194f 100644 --- a/assets/index.html +++ b/assets/index.html @@ -62,19 +62,88 @@ } } - class Landscape { - constructor() { - this.gridSize = 80; - this.gridRange = 50; - this.offsetX = 0; - this.offsetZ = 0; - this.points = new Map(); - this.waterLevel = -10; - this.updateOffset(); + class Triangle { + constructor(p1, p2, p3) { + this.p1 = p1; + this.p2 = p2; + this.p3 = p3; + this.calculateNormal(); + this.visible = true; } - getKey(x, z) { - return `${Math.round(x / this.gridSize)},${Math.round(z / this.gridSize)}`; + 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.5, y: 0.5, z: 1 }; + const dot = this.normal.x * light.x + this.normal.y * light.y + this.normal.z * light.z; + this.intensity = Math.max(0, dot); + } + + isBackfaceCulled() { + 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 }; + const crossZ = v1.x * v2.y - v1.y * v2.x; + return crossZ >= 0; + } + + drawFilled() { + const proj1 = this.p1.project(); + const proj2 = this.p2.project(); + const proj3 = this.p3.project(); + + if (this.isBackfaceCulled()) return; + + const colorVal = Math.floor(30 + this.intensity * 150); + const color = `rgba(0, ${colorVal}, 0, 0.8)`; + + ctx.beginPath(); + ctx.moveTo(proj1.x, proj1.y); + ctx.lineTo(proj2.x, proj2.y); + ctx.lineTo(proj3.x, proj3.y); + ctx.closePath(); + ctx.fillStyle = color; + ctx.fill(); + } + + drawWireframe() { + const proj1 = this.p1.project(); + const proj2 = this.p2.project(); + const proj3 = this.p3.project(); + + if (this.isBackfaceCulled()) return; + + const color = this.p1.isWater || this.p2.isWater || this.p3.isWater ? '#00ccff' : '#00ff00'; + + ctx.strokeStyle = color; + ctx.lineWidth = 1; + ctx.beginPath(); + ctx.moveTo(proj1.x, proj1.y); + ctx.lineTo(proj2.x, proj2.y); + ctx.lineTo(proj3.x, proj3.y); + ctx.closePath(); + ctx.stroke(); + } + } + + class Landscape { + constructor() { + this.gridSize = 60; + this.gridRange = 40; + this.offsetX = 0; + this.offsetZ = 0; + this.triangles = []; + this.waterLevel = -10; } generateHeight(x, z) { @@ -92,9 +161,31 @@ return y < this.waterLevel; } - updateOffset() { + update() { this.offsetX -= 1; this.offsetZ -= 1; + 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 tri1 = new Triangle(p1, p2, p3); + const tri2 = new Triangle(p1, p3, p4); + + this.triangles.push(tri1); + this.triangles.push(tri2); + } + } } getPoint(x, z) { @@ -106,52 +197,9 @@ return point; } - update() { - this.updateOffset(); - } - draw() { - ctx.lineWidth = 1; - ctx.globalAlpha = 0.6; - - 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, z + 1); - - const proj1 = p1.project(); - const proj2 = p2.project(); - const proj3 = p3.project(); - - if (p1.isWater || p2.isWater || p3.isWater) { - ctx.strokeStyle = '#00ccff'; - ctx.globalAlpha = 0.5; - } else { - ctx.strokeStyle = '#00ff00'; - ctx.globalAlpha = 0.6; - } - - if (x < maxIdx) { - ctx.beginPath(); - ctx.moveTo(proj1.x, proj1.y); - ctx.lineTo(proj2.x, proj2.y); - ctx.stroke(); - } - - if (z < maxIdx) { - ctx.beginPath(); - ctx.moveTo(proj1.x, proj1.y); - ctx.lineTo(proj3.x, proj3.y); - ctx.stroke(); - } - } - } - - ctx.globalAlpha = 1; + this.triangles.forEach(tri => tri.drawFilled()); + this.triangles.forEach(tri => tri.drawWireframe()); } }