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; };