webgl+canvas实现交互式城市顶部鸟瞰俯视效果代码
代码语言:html
所属分类:其他
代码描述:webgl+canvas实现交互式城市顶部鸟瞰俯视效果代码
代码标签: webgl canvas 交互式 城市 顶部 鸟瞰 俯视
下面为部分代码预览,完整代码请点击下载或在bfwstudio webide中打开
<!DOCTYPE html> <html lang="en" > <head> <meta charset="UTF-8"> <style> *, *::before, *::after { margin: 0; padding: 0; border: 0; box-sizing: border-box; } *:focus { outline: none; } body { display: flex; align-items: center; justify-content: center; height: 100vh; background-color: rgb( 0, 0, 0 ); } canvas { touch-action: none; } </style> </head> <body translate="no"> <script > 'use strict'; //--- console.clear(); //--- let w = 0; let h = 0; let initialWidth = w; let initialHeight = h; let animationFrame = null; let isTouchDevice = false; const fov = 600; const lightVector = { x: -fov * 0.25, y: 0, z: -fov * 0.5 }; const cameraVector = { x: 0, y: 0, z: -fov }; const canvas = document.createElement('canvas'); const gl = canvas.getContext('webgl2') || canvas.getContext('experimental-webgl2'); const center = { x: w / 2, y: h / 2 }; const border = { left: 1, top: 1, right: w, bottom: h }; const borderDistance = 0; const pointerDistance = 25; let pointer = { x: 0, y: 0 }; let pointerInitialPos = { x: 0, y: 0 }; let pointerPos = { x: center.x, y: center.y }; let pointerDownButton = -1; let pointerActive = false; //--- let shaderProgram = null; let webgl_vertices = []; let webgl_faces = []; let webgl_uvs = []; let webgl_layers = []; const buffers = {}; //--- const vertexCode = `#version 300 es in vec2 a_position; in vec2 a_texcoord; in float a_layer; uniform vec2 u_resolution; out vec2 v_texcoord; out float v_layer; void main(void) { v_texcoord = a_texcoord; v_layer = a_layer; vec2 pos2d = a_position.xy; vec2 zeroToOne = pos2d / u_resolution; vec2 zeroToTwo = zeroToOne * 2.0; vec2 clipSpace = zeroToTwo - 1.0; gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1); } `; const fragmentCode = `#version 300 es precision lowp float; precision lowp sampler2DArray; in vec2 v_texcoord; in float v_layer; uniform sampler2DArray u_textureArray; out vec4 fragColor; void main(void) { fragColor = texture(u_textureArray, vec3(v_texcoord, v_layer)); } `; //--- let gridTileSize = 0; let gridTileSizeHalf = 0; let gridStartPositionX = 0; let gridStartPositionY = 0; let gridEndPositionX = 0; let gridEndPositionY = 0; let gridDotsPerRow = 0; let gridDotsPerColumn = 0; let gridWidth = 0; let gridHeight = 0; let gridMaxTileSize = 4; let gridBuildingTileSizes = [{ x: 4, y: 4 }, { x: 3, y: 4 }, { x: 2, y: 4 }, { x: 4, y: 3 }, { x: 4, y: 2 }, { x: 3, y: 3 }, { x: 2, y: 3 }, { x: 3, y: 2 }, { x: 1, y: 3 }, { x: 3, y: 1 }, { x: 2, y: 2 }, { x: 1, y: 2 }, { x: 2, y: 1 }, { x: 1, y: 1 }]; let gridStreetTileTypes = ['empty', '1x1_vertical', '1x1_horizontal', '1x1_crossing', '2x1l_vertical', '2x1r_vertical', '1x2t_horizontal', '1x2b_horizontal', '1x2r_crossing', '1x2l_crossing', '2x1t_crossing', '2x1b_crossing', '2x2tl_crossing', '2x2tr_crossing', '2x2bl_crossing', '2x2br_crossing', '1x1l_horizontal_crosswalk', '1x1r_horizontal_crosswalk', '1x1b_vertical_crosswalk', '1x1t_vertical_crosswalk', '1x2lt_horizontal_crosswalk', '1x2lb_horizontal_crosswalk', '1x2rt_horizontal_crosswalk', '1x2rb_horizontal_crosswalk', '2x1tl_vertical_crosswalk', '2x1tr_vertical_crosswalk', '2x1bl_vertical_crosswalk', '2x1br_vertical_crosswalk']; let gridTileHolder = []; let gridCubeHolder = []; let gridStreetHolder = []; let gridMovementSaveXPos = 0; let gridMovementSaveYPos = 0; const dotsRadius = 1; const dotsDistance = 10; const dotsDiameter = dotsRadius * 2; let dotsHolder = []; let streetsCountVertical = 0; let streetsCountHorizontal = 0; let debugBorderElement = null; let debugBorderElementColor = 'transparent'; //--- const texturesBuildingsCount = 64; const textureWidth = 64; const textureHeight = 64; let textureHolder = []; let promiseHolder = []; let textureAtlas = {}; let textureArray = null; let texture = null; const texturesBuildingRoofsTiles = 9; let texturesCountBuildingRoofs = 0; let texturesCountBuildingWalls = 0; let texturesCountStreets = 0; const textureCubeNormals = [ { x: 0, y: 0, z: -1 }, // front { x: -1, y: 0, z: 0 }, // left { x: 0, y: -1, z: 0 }, // top { x: 1, y: 0, z: 0 }, // right { x: 0, y: 1, z: 0 } // bottom ]; const textureBuildingColors = [ { cWa: { r: 254, g: 236, b: 214 }, cWi: { r: 67, g: 49, b: 37 } }, { cWa: { r: 244, g: 250, b: 240 }, cWi: { r: 13, g: 39, b: 52 } }, { cWa: { r: 244, g: 250, b: 240 }, cWi: { r: 26, g: 36, b: 48 } }, { cWa: { r: 228, g: 231, b: 222 }, cWi: { r: 73, g: 79, b: 79 } }, { cWa: { r: 228, g: 231, b: 222 }, cWi: { r: 103, g: 93, b: 94 } }, { cWa: { r: 184, g: 187, b: 196 }, cWi: { r: 38, g: 56, b: 66 } }, { cWa: { r: 219, g: 226, b: 230 }, cWi: { r: 32, g: 45, b: 54 } }, { cWa: { r: 249, g: 244, b: 225 }, cWi: { r: 63, g: 53, b: 61 } }, { cWa: { r: 240, g: 243, b: 234 }, cWi: { r: 19, g: 36, b: 46 } }, { cWa: { r: 255, g: 255, b: 255 }, cWi: { r: 22, g: 34, b: 40 } }, { cWa: { r: 230, g: 214, b: 201 }, cWi: { r: 63, g: 68, b: 74 } }, { cWa: { r: 213, g: 212, b: 202 }, cWi: { r: 59, g: 75, b: 72 } }, { cWa: { r: 243, g: 237, b: 237 }, cWi: { r: 94, g: 92, b: 81 } }, { cWa: { r: 228, g: 228, b: 203 }, cWi: { r: 63, g: 52, b: 44 } }, { cWa: { r: 233, g: 222, b: 216 }, cWi: { r: 39, g: 46, b: 54 } }, { cWa: { r: 238, g: 219, b: 192 }, cWi: { r: 77, g: 87, b: 88 } }, { cWa: { r: 238, g: 230, b: 225 }, cWi: { r: 55, g: 71, b: 84 } }, { cWa: { r: 239, g: 211, b: 196 }, cWi: { r: 95, g: 87, b: 85 } }, { cWa: { r: 243, g: 242, b: 238 }, cWi: { r: 32, g: 42, b: 51 } }, { cWa: { r: 243, g: 233, b: 223 }, cWi: { r: 61, g: 72, b: 74 } }, { cWa: { r: 230, g: 228, b: 225 }, cWi: { r: 30, g: 47, b: 55 } }, { cWa: { r: 218, g: 226, b: 230 }, cWi: { r: 15, g: 29, b: 38 } }, { cWa: { r: 244, g: 238, b: 224 }, cWi: { r: 99, g: 92, b: 89 } }, { cWa: { r: 242, g: 219, b: 191 }, cWi: { r: 120, g: 111, b: 73 } }, { cWa: { r: 233, g: 239, b: 245 }, cWi: { r: 80, g: 82, b: 91 } }, { cWa: { r: 237, g: 225, b: 211 }, cWi: { r: 47, g: 40, b: 32 } }, { cWa: { r: 211, g: 207, b: 198 }, cWi: { r: 55, g: 46, b: 39 } }, { cWa: { r: 223, g: 222, b: 213 }, cWi: { r: 41, g: 41, b: 39 } }]; //--- function init() { gl.enable(gl.SCISSOR_TEST); shaderProgram = createShaderProgram(gl, vertexCode, fragmentCode); //--- isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0 || navigator.msMaxTouchPoints > 0; if (isTouchDevice === true) { canvas.addEventListener('touchmove', cursorMoveHandler, false); canvas.addEventListener('touchend', cursorLeaveHandler, false); canvas.addEventListener('touchcancel ', cursorLeaveHandler, false); } else { canvas.addEventListener('pointermove', cursorMoveHandler, false); canvas.addEventListener('pointerdown', cursorDownHandler, false); canvas.addEventListener('pointerup', cursorUpHandler, false); canvas.addEventListener('pointerleave', cursorLeaveHandler, false); } //--- document.body.appendChild(canvas); //--- debugBorderElement = document.createElement('div'); debugBorderElement.style.border = '1px solid ' + debugBorderElementColor; debugBorderElement.style.position = 'absolute'; debugBorderElement.style.margin = 'auto'; debugBorderElement.style.pointerEvents = 'none'; document.body.appendChild(debugBorderElement); //--- createTextures().then(() => { window.addEventListener('resize', onResize, false); restart(); }); } function onResize(event) { restart(); } function restart() { const innerWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth; const innerHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight; //--- w = innerWidth; h = innerHeight; //--- canvas.width = w; canvas.height = h; gl.viewport(0, 0, w, h); //--- center.x = w / 2; center.y = h / 2; pointerPos.x = center.x; pointerPos.y = center.y; pointer.x = center.x + pointerDistance; pointer.y = center.y - pointerDistance; pointerInitialPos.x = center.x + pointerDistance; pointerInitialPos.y = center.y - pointerDistance; //--- border.left = borderDistance; border.top = borderDistance; border.right = w - borderDistance; border.bottom = h - borderDistance; console.log('border: ', border, w, h); //--- debugBorderElement.style.left = borderDistance + 'px'; debugBorderElement.style.right = borderDistance + 'px'; debugBorderElement.style.top = borderDistance + 'px'; debugBorderElement.style.bottom = borderDistance + 'px'; //--- gl.scissor(border.left, border.top, border.right - border.left, border.bottom - border.top); buffers.positionAttributeLocation = gl.getAttribLocation(shaderProgram, 'a_position'); buffers.texcoordAttributeLocation = gl.getAttribLocation(shaderProgram, 'a_texcoord'); buffers.resolutionUniformLocation = gl.getUniformLocation(shaderProgram, 'u_resolution'); buffers.layerAttributeLocation = gl.getAttribLocation(shaderProgram, 'a_layer'); gl.enableVertexAttribArray(buffers.positionAttributeLocation); gl.enableVertexAttribArray(buffers.texcoordAttributeLocation); gl.enableVertexAttribArray(buffers.layerAttributeLocation); gl.vertexAttribPointer(buffers.positionAttributeLocation, 2, gl.FLOAT, false, 0, 0); gl.vertexAttribPointer(buffers.texcoordAttributeLocation, 2, gl.FLOAT, false, 0, 0); gl.vertexAttribPointer(buffers.layerAttributeLocation, 1, gl.FLOAT, false, 0, 0); gl.uniform2f(buffers.resolutionUniformLocation, w, h); //--- gridTileHolder = []; gridCubeHolder = []; gridStreetHolder = []; webgl_vertices = []; webgl_faces = []; webgl_uvs = []; webgl_layers = []; textureHolder = []; promiseHolder = []; //--- removeGrid(); addGrid(); //--- if (animationFrame != null) { cancelAnimFrame(animationFrame); } render(); } //--- function createTextures() { return new Promise(async (resolve, reject) => { texturesCountBuildingRoofs = 0; for (let i = 0; i < texturesBuildingsCount; i++) { const color = addColorVariance(textureBuildingColors[Math.floor(Math.random() * textureBuildingColors.length)].cWa, 2); const colorWall = calcFaceNormalColor(color, textureCubeNormals[0]); const textureRowsAndCols = Math.sqrt(texturesBuildingRoofsTiles); const textures = createRoofTexture(0, colorWall, textureWidth, textureHeight, textureRowsAndCols, textureRowsAndCols); for (let j = 0; j < texturesBuildingRoofsTiles; j++) { const texture = textures[j]; textureHolder.push(texture.image); promiseHolder.push(texture.promise); texturesCountBuildingRoofs++; } } //--- texturesCountBuildingWalls = 0; const wallTexturesCount = Math.floor(texturesBuildingsCount / 6); const createWalls = (tP, cWa, cWi, wR, rX, rY, p) => { for (let i = 1, l = textureCubeNormals.length; i < l; i++) { const cubeNormal = textureCubeNormals[i]; const colorWall = calcFaceNormalColor(cWa, cubeNormal); const colorWindows = calcFaceNormalColor(cWi, cubeNormal); const texture = createWallTexture(tP, colorWall, colorWindows, wR, rX, rY, p, textureWidth, textureHeight); textureHolder.push(texture.image); promiseHolder.push(texture.promise); texturesCountBuildingWalls++; } }; for (let i = 0; i < wallTexturesCount * 0.5; i++) { const colors = textureBuildingColors[Math.floor(Math.random() * textureBuildingColors.length)]; const cWa = addColorVariance(colors.cWa, 3); const cWi = addColorVariance(colors.cWi, 3); const windowRatio = Math.random() * 0.3 + 0.1; const repeatX = 1; const repeatY = Math.floor(Math.random() * 1) + 3; const padding = 0; createWalls(0, cWa, cWi, windowRatio, repeatX, repeatY, padding); } for (let i = 0; i < wallTexturesCount * 0.5; i++) { const colors = textureBuildingColors[Math.floor(Math.random() * textureBuildingColors.length)]; const cWa = addColorVariance(colors.cWa, 3); const cWi = addColorVariance(colors.cWi, 3); const windowRatio = Math.random() * 0.5 + 0.15; const repeatX = Math.floor(Math.random() * 1) + 4; const repeatY = 1; const padding = 0; createWalls(1, cWa, cWi, windowRatio, repeatX, repeatY, padding); } for (let i = 0; i < wallTexturesCount; i++) { const colors = textureBuildingColors[Math.floor(Math.random() * textureBuildingColors.length)]; const cWa = addColorVariance(colors.cWa, 3); const cWi = addColorVariance(colors.cWi, 3); const windowRatio = Math.random() * 0.5 + 0.25; const repeatX = Math.floor(Math.random() * 3) + 2; const repeatY = Math.floor(Math.random() * 2) + 3; const padding = (repeatX + repeatY) * (Math.random() * 0.25 + 0.5); createWalls(2, cWa, cWi, windowRatio, repeatX, repeatY, padding); } for (let i = 0; i < wallTexturesCount * 0.5; i++) { const colors = textureBuildingColors[Math.floor(Math.random() * textureBuildingColors.length)]; const cWa = addColorVariance(colors.cWa, 3); const cWi = addColorVariance(colors.cWi, 3); const windowRatio = 1; const repeatX = Math.floor(Math.random() * 2) + 1; const repeatY = repeatX === 1 ? 3 : Math.floor(Math.random() * 2) + 1; const padding = 1; createWalls(3, cWa, cWi, windowRatio, repeatX, repeatY, padding); } for (let i = 0; i < wallTexturesCount * 0.5; i++) { const colors = textureBuildingColors[Math.floor(Math.random() * textureBuildingColors.length)]; const cWa = addColorVariance(colors.cWa, 3); const cWi = addColorVariance(colors.cWi, 3); const windowRatio = 1; const repeatX = Math.round(Math.random() * 1) + 1; const repeatY = repeatX === 2 ? Math.floor(Math.random() * 1) + 3 : Math.floor(Math.random() * 1) + 1; const padding = Math.random() * 0.5; createWalls(4, cWa, cWi, windowRatio, repeatX, repeatY, padding); } for (let i = 0; i < wallTexturesCount * 0.5; i++) { const colors = textureBuildingColors[Math.floor(Math.random() * textureBuildingColors.length)]; const cWa = addColorVariance(colors.cWa, 3); const cWi = addColorVariance(colors.cWi, 3); const windowRatio = 1; const repeatX = 2; const repeatY = Math.round(Math.random() * 2) + 1; const padding = Math.floor(Math.random() * 4) + 6; createWalls(4, cWa, cWi, windowRatio, repeatX, repeatY, padding); } for (let i = 0; i < wallTexturesCount; i++) { const colors = textureBuildingColors[Math.floor(Math.random() * textureBuildingColors.length)]; const cWa = addColorVariance(colors.cWa, 3); const cWi = addColorVariance(colors.cWi, 3); const windowRatio = 1; const repeatX = Math.round(Math.random() * 2) + 2; const repeatY = repeatX === 2 ? 3 : Math.round(Math.random() * 1) + 2; const padding = 1; createWalls(5, cWa, cWi, windowRatio, repeatX, repeatY, padding); } for (let i = 0; i < wallTexturesCount; i++) { const colors = textureBuildingColors[Math.floor(Math.random() * textureBuildingColors.length)]; const cWa = addColorVariance(colors.cWa, 3); const cWi = addColorVariance(colors.cWi, 3); const windowRatio = 1; const repeatX = 2; const repeatY = Math.round(Math.random() * 2) + 1; const padding = Math.floor(Math.random() * 4) + 6; createWalls(6, cWa, cWi, windowRatio, repeatX, repeatY, padding); } for (let i = 0; i < wallTexturesCount * 0.5; i++) { const colors = textureBuildingColors[Math.floor(Math.random() * textureBuildingColors.length)]; const cWa = addColorVariance(colors.cWa, 3); const cWi = addColorVariance(colors.cWi, 3); const windowRatio = 1; const repeatX = Math.floor(Math.random() * 1) + 1; const repeatY = repeatX * 0.5; const padding = Math.random() * 0.3 + 0.2; createWalls(7, cWa, cWi, windowRatio, repeatX, repeatY, padding); } //--- texturesCountStreets = 0; for (let i = 0; i < gridStreetTileTypes.length; i++) { const streetTileType = gridStreetTileTypes[i]; let texture = createStreetTexture(streetTileType, textureWidth, textureHeight); textureHolder.push(texture.image); promiseHolder.push(texture.promise); texturesCountStreets++; } //--- await Promise.all(promiseHolder); //--- textureArray = createTextureArray(gl, textureWidth, textureHeight, textureHolder.length); for (let i = 0; i < textureHolder.length; i++) { const texture = textureHolder[i]; loadTextureIntoArray(gl, textureArray, i, texture, textureWidth, textureHeight); } gl.generateMipmap(gl.TEXTURE_2D_ARRAY); //--- resolve(); }); } //--- function addGrid() { gridTileSize = dotsDiameter + dotsDistance; gridTileSizeHalf = gridTileSize * 0.5; gridDotsPerRow = Math.ceil((border.right - border.left) / gridTileSize) + gridMaxTileSize * 2; gridDotsPerColumn = Math.ceil((border.bottom - border.top) / gridTileSize) + gridMaxTileSize * 2; gridWidth = gridDotsPerRow * gridTileSize; gridHeight = gridDotsPerColumn * gridTileSize; gridStartPositionX = gridWidth * -0.5; gridStartPositionY = gridHeight * -0.5; gridEndPositionX = gridStartPositionX + gridWidth; gridEndPositionY = gridStartPositionY + gridHeight; //--- const streetsH = Math.floor(gridDotsPerRow / 10); const streetsV = Math.floor(gridDotsPerColumn / 10); streetsCountHorizontal = Math.floor(Math.random() * (streetsH * 0.75) + streetsH * 0.5) + 1; streetsCountVertical = Math.floor(Math.random() * (streetsV * 0.75) + streetsV * 0.5) + 1; const streetRows = getRandomUniqueIndices(gridDotsPerRow, streetsCountHorizontal); const streetColumns = getRandomUniqueIndices(gridDotsPerColumn, streetsCountVertical); //--- for (let i = 0; i < gridDotsPerColumn; i++) { for (let j = 0; j < gridDotsPerRow; j++) { const x = gridStartPositionX + j * (dotsDistance + dotsDiameter); const y = gridStartPositionY + i * (dotsDistance + dotsDiameter); const rowIndex = j; const colIndex = i; const dot = addDot(x, y, rowIndex, colIndex); dotsHolder.push(dot); } } //--- for (let i = 0; i < gridDotsPerColumn; i++) { for (let j = 0; j < gridDotsPerRow; j++) { const index = i * gridDotsPerRow + j; const dot = dotsHolder[index]; const neighborRightIndex = (j + 1) % gridDotsPerRow + i * gridDotsPerRow; const neighborBottomIndex = (i + 1) % gridDotsPerColumn * gridDotsPerRow + j; const neighborRightBottomIndex = (i + 1) % gridDotsPerColumn * gridDotsPerRow + (j + 1) % gridDotsPerRow; const neighborLeftIndex = (j - 1 + gridDotsPerRow) % gridDotsPerRow + i * gridDotsPerRow; const neighborTopIndex = (i - 1 + gridDotsPerColumn) % gridDotsPerColumn * gridDotsPerRow + j; dot.neighborRight = dotsHolder[neighborRightIndex]; dot.neighborBottom = dotsHolder[neighborBottomIndex]; dot.neighborRightBottom = dotsHolder[neighborRightBottomIndex]; dot.neighborLeft = dotsHolder[neighborLeftIndex]; dot.neighborTop = dotsHolder[neighborTopIndex]; } } //--- for (let i = 0; i < gridDotsPerColumn; i++) { for (let j = 0; j < gridDotsPerRow; j++) { const dot = dotsHolder[i * gridDotsPerRow + j]; if (streetColumns.includes(i) || streetRows.includes(j)) { dot.isStreet = true; dot.inUse = true; } } } for (let i = 0; i < dotsHolder.length; i++) { const dot = dotsHolder[i]; if (dot.isStreet === true) { addTile([dot], 1, 1, 'street'); } } //--- let buildingCounter = 0; let buildingHolder = []; //--- for (let i = 0; i < gridBuildingTileSizes.length; i++) { const gridBuildingTileSize = gridBuildingTileSizes[i]; if (i < gridBuildingTileSizes.length - 1) { const randomBuildingCountPreset = Math.floor(gridDotsPerRow * gridDotsPerColumn / (gridBuildingTileSizes.length - i)); const randomBuildingCount = randomBuildingCountPreset * ((i + 1) * 0.5 * (gridBuildingTileSizes.length * 0.5 / 100)); const dotsHolderIndicesHolder = Array.from({ length: dotsHolder.length }, (v, k) => k); const dotsHolderRandomIndicesHolder = arrayShuffle(dotsHolderIndicesHolder).slice(0, randomBuildingCount); for (let j = 0, l = dotsHolderRandomIndicesHolder.length; j < l; j++) { const dot = dotsHolder[dotsHolderRandomIndicesHolder[j]]; if (areDotsAvailable(dot, gridBuildingTileSize.x, gridBuildingTileSize.y) === true) { buildingCounter++; dot.inUse = true; dot.cubeIndex = buildingCounter; dot.depth = calcTileDepth(gridBuildingTileSize.x, gridBuildingTileSize.y); const gridTileDots = setDotsInUse(dot, gridBuildingTileSize.x, gridBuildingTileSize.y, buildingCounter, dot.depth); buildingHolder.push({ dots: gridTileDots, width: gridBuildingTileSize.x, height: gridBuildingTileSize.y, depth: dot.depth }); } } } else { for (let j = 0, l = dotsHolder.length; j < l; j++) { const dot = dotsHolder[j]; if (dot.inUse === false) { buildingCounter++; dot.inUse = true; dot.cubeIndex = buildingCounter; dot.depth = calcTileDepth(1, 1); buildingHolder.push({ dots: [dot], width: 1, height: 1, depth: dot.depth }); } } } } for (let i = 0; i < buildingHolder.length; i++) { const building = buildingHolder[i]; addTile(building.dots, building.width, building.height, 'building', building.depth); } //--- sortCubes(); } function areDotsAvailable(dot, width, height) { const rowIndex = dot.rowIndex; const colIndex = dot.colIndex; for (let i = 0; i < height; i++) { for (let j = 0; j < width; j++) { const neighborRowIndex = (colIndex + i) % gridDotsPerColumn; const neighborColIndex = (rowIndex + j) % gridDotsPerRow; const neighborIndex = neighborRowIndex * gridDotsPerRow + neighborColIndex; if (dotsHolder[neighborIndex].inUse === true) { return false; } } } return true; } function setDotsInUse(dot, width, height, buildingIndex, depth) { const rowIndex = dot.rowIndex; const colIndex = dot.colIndex; const dotsInUse = []; for (let i = 0; i < height; i++) { for (let j = 0; j < width; j++) { const neighborRowIndex = (colIndex + i) % gridDotsPerColumn; const neighborColIndex = (rowIndex + j) % gridDotsPerRow; const neighborIndex = neighborRowIndex * gridDotsPerRow + neighborColIndex; dotsHolder[neighborIndex].inUse = true; dotsHolder[neighborIndex].cubeIndex = buildingIndex; dotsHolder[neighborIndex].depth = depth; dotsInUse.push(dotsHolder[neighborIndex]); } } return dotsInUse; } function arrayShuffle(array) { for (let i = array.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [array[i], array[j]] = [array[j], array[i]]; } return array; } function addColorVariance(color, variance) { const clamp = (value, min, max) => { return Math.min(Math.max(value, min), max); }; return { r: clamp(color.r + Math.floor(Math.random() * variance * 2 - variance), 0, 255), g: clamp(color.g + Math.floor(Math.random() * variance * 2 - variance), 0, 255), b: clamp(color.b + Math.floor(Math.random() * variance * 2 - variance), 0, 255) }; } function calcTileDepth(w, h) { const width = w * gridTileSize; const height = h * gridTileSize; const areaFactor = w <= 1 && h <= 1 ? 0.15 : 0.05; const area = width * height * areaFactor; const depth = Math.random() * area + area; return depth; } function calcFaceNormalColor(color, faceNormal) { const lightBrightness = 1; const dotProductLight = faceNormal.x * lightVector.x + faceNormal.y * lightVector.y + faceNormal.z * lightVector.z; const normalMagnitude = Math.sqrt(faceNormal.x * faceNormal.x + faceNormal.y * faceNormal.y + faceNormal.z * faceNormal.z); const lightMagnitude = Math.sqrt(lightVector.x * lightVector.x + lightVector.y * lightVector.y + lightVector.z * lightVector.z); const lightFactor = Math.acos(dotProductLight / (normalMagnitude * lightMagnitude)) / Math.PI * lightBrightness; const colorValueR = Math.abs(color.r - Math.floor(color.r * lightFactor)); const colorValueG = Math.abs(color.g - Math.floor(color.g * lightFactor)); const colorValueB = Math.abs(color.b - Math.floor(color.b * lightFactor)); return { r: colorValueR, g: colorValueG, b: colorValueB }; } function addTile(gridTileDots, width, height, type, depth = 0, colorIndex = 0) { const tile = {}; tile.dots = gridTileDots; tile.w = width; tile.h = height; tile.type = type; //--- tile.width = width * gridTileSize; tile.height = height * gridTileSize; tile.depth = 0; //--- if (tile.type === 'street') { const dot = tile.dots[0]; const dTL = dot; const dTR = dot.neighborRight; const dBR = dot.neighborRightBottom; const dBL = dot.neighborBottom; const dT = dot.neighborTop; const dB = dot.neighborBottom; const dL = dot.neighborLeft; const dR = dot.neighborRight; let streetTileIndex = 0; if (dT.isStreet === true && dB.isStreet === true && dL.isStreet === false && dR.isStreet === false) { streetTileIndex = 1; } if (dL.isStreet === true && dR.isStreet === true && dT.isStreet === false && dB.isStreet === false) { streetTileIndex = 2; } if (dL.isStreet === true && dR.isStreet === true && dT.isStreet === true && dB.isStreet === true) { streetTileIndex = 3; } //--- if (dT.isStreet === true && dB.isStreet =.........完整代码请登录后点击上方下载按钮下载查看
网友评论0