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