import { mat4, vec3, vec4 } from "gl-matrix";
|
|
|
|
import { Corner, GAT, Tile } from "../format/gat";
|
|
import { createProgram, createShader, ShaderType } from "../util/render";
|
|
|
|
/* @ts-ignore */
|
|
import fragmentSource from "./gat.frag";
|
|
/* @ts-ignore */
|
|
import vertexSource from "./gat.vert";
|
|
|
|
const ORIGIN = vec4.fromValues(0, 0, 0, 1);
|
|
const UP = vec3.fromValues(0, 1, 0);
|
|
|
|
const printMat4 = (name: string, m: mat4) => {
|
|
console.log(name);
|
|
console.log([m[0], m[4], m[8], m[12]]);
|
|
console.log([m[1], m[5], m[9], m[13]]);
|
|
console.log([m[2], m[6], m[10], m[11]]);
|
|
console.log([m[3], m[7], m[14], m[15]]);
|
|
};
|
|
|
|
// Function to download data to a file
|
|
function download(data: BlobPart, filename: string, type: string) {
|
|
var file = new Blob([data], { type: type });
|
|
// Others
|
|
var a = document.createElement("a"),
|
|
url = URL.createObjectURL(file);
|
|
a.href = url;
|
|
a.download = filename;
|
|
document.body.appendChild(a);
|
|
a.click();
|
|
setTimeout(function () {
|
|
document.body.removeChild(a);
|
|
window.URL.revokeObjectURL(url);
|
|
}, 0);
|
|
}
|
|
|
|
const exportObj = (vertices: number[]): string => {
|
|
let obj = "";
|
|
for (let i = 0; i < vertices.length / 3; ++i) {
|
|
obj += `v ${vertices[i]} ${vertices[i + 1]} ${vertices[i + 2]}\n`;
|
|
}
|
|
for (let i = 0; i < vertices.length / (3 * 2); ++i) {
|
|
obj += `f ${i + 1} ${i + 2} ${i + 3}\n`;
|
|
obj += `f ${i + 4} ${i + 5} ${i + 6}\n`;
|
|
}
|
|
return obj;
|
|
};
|
|
|
|
export const renderGAT = (gl: WebGLRenderingContext, gat: GAT) => {
|
|
console.log("gat", gat);
|
|
|
|
const fragmentShader = createShader(gl, ShaderType.FRAGMENT, fragmentSource);
|
|
const vertexShader = createShader(gl, ShaderType.VERTEX, vertexSource);
|
|
const program = createProgram(gl, vertexShader, fragmentShader);
|
|
|
|
gl.useProgram(program);
|
|
gl.enable(gl.DEPTH_TEST);
|
|
|
|
const maxAltitude = getMaxAltitude(gat);
|
|
console.log("altitude", maxAltitude);
|
|
|
|
const camera = vec3.fromValues(
|
|
gat.width / 2,
|
|
maxAltitude + 10,
|
|
gat.height / 2
|
|
);
|
|
console.log("camera", camera);
|
|
|
|
const centerX = Math.floor(gat.width / 2);
|
|
const centerZ = Math.floor(gat.height / 2);
|
|
const centerTile = gat.tiles[gat.width * centerZ + centerX];
|
|
const center = vec3.fromValues(centerX, avgTileHeight(centerTile), centerZ);
|
|
console.log("center", center);
|
|
|
|
const model = mat4.create();
|
|
mat4.translate(
|
|
model,
|
|
model,
|
|
vec3.fromValues(-camera[0], -camera[1], -camera[2])
|
|
);
|
|
|
|
const view = mat4.create();
|
|
mat4.rotateX(view, view, -Math.PI / 2);
|
|
// mat4.lookAt(view, camera, center, UP);
|
|
|
|
const modelview = model;
|
|
mat4.multiply(modelview, view, model);
|
|
|
|
const perspective = mat4.create();
|
|
const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
|
|
mat4.perspective(perspective, Math.PI / 4, aspect, -0.5, 1000);
|
|
|
|
const matrix = mat4.create();
|
|
mat4.multiply(matrix, modelview, perspective);
|
|
|
|
const matrixLoc = gl.getUniformLocation(program, "u_matrix");
|
|
gl.uniformMatrix4fv(matrixLoc, false, matrix);
|
|
|
|
printMat4("model", model);
|
|
printMat4("view", view);
|
|
printMat4("view * model", modelview);
|
|
printMat4("projection", perspective);
|
|
printMat4("projection * view * model", matrix);
|
|
|
|
const origin = vec4.create();
|
|
vec4.transformMat4(origin, ORIGIN, model);
|
|
console.log("origin (model)", origin);
|
|
vec4.transformMat4(origin, ORIGIN, modelview);
|
|
console.log("origin (modelview)", origin);
|
|
vec4.transformMat4(origin, ORIGIN, matrix);
|
|
console.log("origin (projection)", origin);
|
|
|
|
const vertices: number[] = [];
|
|
|
|
gat.tiles.forEach((tile, i) => {
|
|
const x = i % gat.width;
|
|
const z = Math.floor(i / gat.width);
|
|
|
|
const topLeft = [x, tile.altitude[Corner.TOP_LEFT], z];
|
|
const topRight = [x + 1, tile.altitude[Corner.TOP_RIGHT], z];
|
|
const bottomLeft = [x, tile.altitude[Corner.BOTTOM_LEFT], z + 1];
|
|
const bottomRight = [x + 1, tile.altitude[Corner.BOTTOM_RIGHT], z + 1];
|
|
|
|
vertices.push(
|
|
...topLeft,
|
|
...bottomLeft,
|
|
...topRight,
|
|
...topRight,
|
|
...bottomLeft,
|
|
...bottomRight
|
|
);
|
|
});
|
|
|
|
console.log("# vertices", vertices.length / 3);
|
|
|
|
// download(exportObj(vertices), "heightmap.obj", "text/plain");
|
|
|
|
let failCount = 0;
|
|
for (let i = 0; i < vertices.length / 3; ++i) {
|
|
const vec = vec4.fromValues(
|
|
vertices[i],
|
|
vertices[i + 1],
|
|
vertices[i + 2],
|
|
1
|
|
);
|
|
const result = checkPixelCoord(vec, matrix);
|
|
if (result) {
|
|
if (failCount < 10) {
|
|
console.log("vertex clipped, (pre-xform)", vec, "(post-xform)", result);
|
|
}
|
|
failCount++;
|
|
}
|
|
}
|
|
console.log("clipCount", failCount);
|
|
console.log("totalCount", vertices.length / 3);
|
|
|
|
const vertexBuffer = gl.createBuffer();
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
|
|
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
|
|
|
|
const positionLoc = gl.getAttribLocation(program, "a_position");
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
|
|
gl.vertexAttribPointer(positionLoc, 3, gl.FLOAT, false, 0, 0);
|
|
gl.enableVertexAttribArray(positionLoc);
|
|
|
|
gl.clearColor(0, 0, 0, 1);
|
|
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
|
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
|
|
|
|
gl.drawArrays(gl.TRIANGLES, 0, vertices.length / 3);
|
|
};
|
|
|
|
const checkPixelCoord = (x: vec4, mat: mat4): vec4 | null => {
|
|
const vec = vec4.create();
|
|
vec4.transformMat4(vec, x, mat);
|
|
for (let i = 0; i < vec.length; ++i) {
|
|
if (Math.abs(vec[i]) >= 1) {
|
|
return vec;
|
|
}
|
|
}
|
|
return null;
|
|
};
|
|
|
|
const getMaxAltitude = (gat: GAT): number =>
|
|
gat.tiles.reduce(
|
|
(max, tile) =>
|
|
Object.values(tile.altitude).reduce(
|
|
(max1, altitude) => Math.max(max1, altitude),
|
|
max
|
|
),
|
|
0
|
|
);
|
|
|
|
const avgTileHeight = (tile: Tile): number => {
|
|
const heights = [...Object.values(tile.altitude)];
|
|
const s = heights.reduce((x, y) => x + y, 0);
|
|
return s / heights.length;
|
|
};
|