wind-component.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. import {THREE} from '../three-defs.js';
  2. import * as shaders from '../../game/render/shaders.js';
  3. import * as entity from "../entity.js";
  4. import * as terrain_component from './terrain-component.js';
  5. import MersenneTwister from 'mersenne-twister';
  6. const NUM_BUGS = 8;
  7. const BUG_SPAWN_RANGE = 20.0;
  8. const BUG_MAX_DIST = 50.0;
  9. const M_TMP = new THREE.Matrix4();
  10. const AABB_TMP = new THREE.Box3();
  11. export class WindComponent extends entity.Component {
  12. static CLASS_NAME = 'WindComponent';
  13. get NAME() {
  14. return WindComponent.CLASS_NAME;
  15. }
  16. #params_;
  17. #meshes_;
  18. #group_;
  19. #totalTime_;
  20. #material_;
  21. #geometry_;
  22. constructor(params) {
  23. super();
  24. this.#params_ = params;
  25. this.#meshes_ = [];
  26. this.#group_ = new THREE.Group();
  27. this.#totalTime_ = 0;
  28. this.#geometry_ = null;
  29. }
  30. Destroy() {
  31. for (let m of this.#meshes_) {
  32. m.removeFromParent();
  33. }
  34. this.#group_.removeFromParent();
  35. }
  36. #CreateGeometry_() {
  37. const rng = new MersenneTwister(1);
  38. const offsets = new Float32Array(NUM_BUGS * 3);
  39. for (let i = 0; i < NUM_BUGS; ++i) {
  40. offsets[i*3 + 0] = (rng.random() * 2.0 - 1.0) * (BUG_SPAWN_RANGE / 2);
  41. offsets[i*3 + 1] = rng.random() * 1.0 + 2.0;
  42. offsets[i*3 + 2] = (rng.random() * 2.0 - 1.0) * (BUG_SPAWN_RANGE / 2);
  43. }
  44. const plane = new THREE.PlaneGeometry(1, 1, 1, 1);
  45. const geo = new THREE.InstancedBufferGeometry();
  46. geo.instanceCount = NUM_BUGS;
  47. geo.setAttribute('position', plane.attributes.position);
  48. geo.setAttribute('uv', plane.attributes.uv);
  49. geo.setAttribute('normal', plane.attributes.normal);
  50. geo.setAttribute('offset', new THREE.InstancedBufferAttribute(offsets, 3));
  51. geo.setIndex(plane.index);
  52. geo.boundingSphere = new THREE.Sphere(new THREE.Vector3(0, 0, 0), BUG_SPAWN_RANGE);
  53. return geo;
  54. }
  55. InitEntity() {
  56. const threejs = this.FindEntity('threejs').GetComponent('ThreeJSController');
  57. this.#geometry_ = this.#CreateGeometry_();
  58. const textureLoader = new THREE.TextureLoader();
  59. const albedo = textureLoader.load('./textures/' + 'dust.png');
  60. albedo.colorSpace = THREE.SRGBColorSpace;
  61. this.#material_ = new shaders.ShaderMaterial('WIND', {
  62. uniforms: {
  63. time: { value: 0.0 },
  64. diffuseTexture: { value: albedo },
  65. dustSize: { value: new THREE.Vector2(0.4, 0.4) },
  66. heightmap: { value: this.#params_.heightmap },
  67. heightmapParams: { value: new THREE.Vector3(this.#params_.height, this.#params_.offset, this.#params_.dims) },
  68. }
  69. });
  70. this.#material_.transparent = true;
  71. this.#material_.side = THREE.DoubleSide;
  72. this.#material_.depthWrite = false;
  73. this.#material_.depthTest = true;
  74. threejs.AddSceneObject(this.#group_, {pass: 'transparent'});
  75. }
  76. #CreateMesh_() {
  77. const m = new THREE.Mesh(this.#geometry_, this.#material_);
  78. m.receiveShadow = false;
  79. m.castShadow = false;
  80. m.visible = true;
  81. this.#meshes_.push(m);
  82. this.#group_.add(m);
  83. return m;
  84. }
  85. Update(timeElapsed) {
  86. this.#totalTime_ += timeElapsed;
  87. this.#material_.uniforms.time.value = this.#totalTime_;
  88. const threejs = this.FindEntity('threejs').GetComponent('ThreeJSController');
  89. const camera = threejs.Camera;
  90. const frustum = new THREE.Frustum().setFromProjectionMatrix(M_TMP.copy(camera.projectionMatrix).multiply(camera.matrixWorldInverse));
  91. const meshes = [...this.#meshes_];
  92. const baseCellPos = camera.position.clone();
  93. baseCellPos.divideScalar(BUG_SPAWN_RANGE);
  94. baseCellPos.floor();
  95. baseCellPos.multiplyScalar(BUG_SPAWN_RANGE);
  96. // This is dumb and slow
  97. for (let c of this.#group_.children) {
  98. c.visible = false;
  99. }
  100. const terrain = this.#params_.terrain.GetComponent(terrain_component.TerrainComponent.CLASS_NAME);
  101. const cameraPosXZ = new THREE.Vector3(camera.position.x, 0, camera.position.z);
  102. for (let x = -3; x < 3; x++) {
  103. for (let z = -3; z < 3; z++) {
  104. // Current cell
  105. const currentCell = new THREE.Vector3(
  106. baseCellPos.x + x * BUG_SPAWN_RANGE, 0, baseCellPos.z + z * BUG_SPAWN_RANGE);
  107. currentCell.y = terrain.GetHeight(currentCell.x, currentCell.z);
  108. AABB_TMP.setFromCenterAndSize(currentCell, new THREE.Vector3(BUG_SPAWN_RANGE, 100, BUG_SPAWN_RANGE));
  109. const distToCell = AABB_TMP.distanceToPoint(cameraPosXZ);
  110. if (distToCell > BUG_MAX_DIST) {
  111. continue;
  112. }
  113. if (!frustum.intersectsBox(AABB_TMP)) {
  114. continue;
  115. }
  116. const m = meshes.length > 0 ? meshes.pop() : this.#CreateMesh_();
  117. m.position.copy(currentCell);
  118. m.position.y = 0;
  119. m.visible = true;
  120. }
  121. }
  122. }
  123. }