Add shading and backface culling to landscape
This commit is contained in:
1 parent
df19192118
commit
29de731e11
1 file changed
+105
-57
+105
-57
@@ -62,19 +62,88 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Landscape {
|
class Triangle {
|
||||||
constructor() {
|
constructor(p1, p2, p3) {
|
||||||
this.gridSize = 80;
|
this.p1 = p1;
|
||||||
this.gridRange = 50;
|
this.p2 = p2;
|
||||||
this.offsetX = 0;
|
this.p3 = p3;
|
||||||
this.offsetZ = 0;
|
this.calculateNormal();
|
||||||
this.points = new Map();
|
this.visible = true;
|
||||||
this.waterLevel = -10;
|
|
||||||
this.updateOffset();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getKey(x, z) {
|
calculateNormal() {
|
||||||
return `${Math.round(x / this.gridSize)},${Math.round(z / this.gridSize)}`;
|
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) {
|
generateHeight(x, z) {
|
||||||
@@ -92,9 +161,31 @@
|
|||||||
return y < this.waterLevel;
|
return y < this.waterLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateOffset() {
|
update() {
|
||||||
this.offsetX -= 1;
|
this.offsetX -= 1;
|
||||||
this.offsetZ -= 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) {
|
getPoint(x, z) {
|
||||||
@@ -106,52 +197,9 @@
|
|||||||
return point;
|
return point;
|
||||||
}
|
}
|
||||||
|
|
||||||
update() {
|
|
||||||
this.updateOffset();
|
|
||||||
}
|
|
||||||
|
|
||||||
draw() {
|
draw() {
|
||||||
ctx.lineWidth = 1;
|
this.triangles.forEach(tri => tri.drawFilled());
|
||||||
ctx.globalAlpha = 0.6;
|
this.triangles.forEach(tri => tri.drawWireframe());
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user