threeCubeDetail.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. import * as THREE from 'three';
  2. import texture1 from "../../assets/tex_white_8.png";
  3. import texture2 from "../../assets/max_texture1.png";
  4. export const ThreeCubeDetailClass = {
  5. constructor(options) {
  6. this.webgl = options.webgl;
  7. this.view = options.view;
  8. this.points = options.points;
  9. this.mesh = options.mesh;
  10. this.edgesReferences = options.edgesReferences;
  11. this._camera = options._camera;
  12. this.lastSelectedIndex = options.lastSelectedIndex;
  13. this.selectedIndex = options.selectedIndex;
  14. this.raycaster = options.raycaster; // 射线投射器
  15. this.mouse = options.mouse; // 鼠标位置
  16. this.callBackFunction = options.callBackFunction; // 回调函数
  17. this.mouseClickEvent = null;
  18. this.depthTest = options.depthTest; //false是优先级低,true是优先级高
  19. this.deepOpacity = options.deepOpacity;
  20. },
  21. initialize() {
  22. this.renderer = new THREE.WebGLRenderer({
  23. context: this.gl,
  24. premultipliedAlpha: false
  25. });
  26. this.renderer.setPixelRatio(window.devicePixelRatio);
  27. this.renderer.setViewport(0, 0, this.view.width, this.view.height);
  28. this.renderer.autoClear = false;
  29. this.renderer.autoClearDepth = false;
  30. this.renderer.autoClearColor = false;
  31. let originalSetRenderTarget = this.renderer.setRenderTarget.bind(this.renderer);
  32. this.renderer.setRenderTarget = function (target) {
  33. originalSetRenderTarget(target);
  34. if (target == null) {
  35. context.bindRenderTarget();
  36. }
  37. };
  38. // 创建场景和相机
  39. this.scene = new THREE.Scene();
  40. let cam = this.camera;
  41. this._camera = new THREE.PerspectiveCamera(cam.fovY, cam.aspect, 0.1, 1000000000);
  42. // 创建坐标轴和网格辅助工具
  43. const axesHelper = new THREE.AxesHelper(1);
  44. axesHelper.position.copy(1000000, 100000, 100000);
  45. this.scene.add(axesHelper);
  46. let grid = new THREE.GridHelper(30, 10, 0xf0f0f0, 0xffffff);
  47. this.scene.add(grid);
  48. this.createInstancedMesh();
  49. this.addPoints([]);
  50. // 灯光
  51. const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
  52. this.scene.add(ambientLight);
  53. const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5);
  54. directionalLight.position.set(10, 10, 10);
  55. this.scene.add(directionalLight);
  56. this.mouseClickEvent = this.view.on("click", this.onMouseClick.bind(this));
  57. },
  58. createInstancedMesh() {
  59. this.points = [];
  60. this.currentCount = 0; // 已绘制点的数量
  61. const boxGeometry = new THREE.BoxGeometry(this.size.z, this.size.x, this.size.y);
  62. boxGeometry.computeVertexNormals();
  63. let texture;
  64. if(this.deepOpacity){ //深度贴图
  65. texture = new THREE.TextureLoader().load(texture2);
  66. }else{
  67. texture = new THREE.TextureLoader().load(texture1);
  68. }
  69. //const texture = new THREE.TextureLoader().load(texture1);
  70. // texture.wrapS = THREE.RepeatWrapping; // 设置U轴的重复模式
  71. // texture.wrapT = THREE.RepeatWrapping; // 设置V轴的重复模式
  72. // texture.repeat.set(1, 1); // 设置重复次数
  73. const material = new THREE.MeshPhongMaterial({
  74. map: texture,
  75. transparent: true,
  76. opacity: 0.5,
  77. depthTest: this.depthTest,
  78. side: THREE.DoubleSide,
  79. depthWrite: false,
  80. });
  81. material.onBeforeCompile = (shader) => {
  82. shader.vertexShader = `
  83. attribute vec3 instanceColor;
  84. varying vec3 vColor;
  85. ` + shader.vertexShader;
  86. shader.vertexShader = shader.vertexShader.replace(
  87. `#include <begin_vertex>`,
  88. `
  89. #include <begin_vertex>
  90. vColor = instanceColor;
  91. `
  92. );
  93. shader.fragmentShader = `
  94. varying vec3 vColor;
  95. ` + shader.fragmentShader;
  96. shader.fragmentShader = shader.fragmentShader.replace(
  97. `#include <dithering_fragment>`,
  98. `
  99. #include <dithering_fragment>
  100. gl_FragColor.rgb = vColor;
  101. `
  102. );
  103. };
  104. this.mesh = new THREE.InstancedMesh(boxGeometry, material, 500000); // 预设容量
  105. const instanceColors = new Float32Array(500000 * 3);
  106. boxGeometry.setAttribute('instanceColor', new THREE.InstancedBufferAttribute(instanceColors, 3));
  107. this.scene.add(this.mesh);
  108. this.edgesReferences = [];
  109. },
  110. addPoints(newPoints) {
  111. // 更新 this.points 数据
  112. this.points.push(...newPoints);
  113. // 如果实例容量不足,扩展 InstancedMesh
  114. const totalPoints = this.points.length;
  115. if (totalPoints > this.mesh.count) {
  116. const oldMesh = this.mesh;
  117. const newMesh = new THREE.InstancedMesh(
  118. oldMesh.geometry,
  119. oldMesh.material,
  120. totalPoints
  121. );
  122. newMesh.instanceMatrix.copy(oldMesh.instanceMatrix); // 复制已有矩阵数据
  123. newMesh.geometry.setAttribute(
  124. 'instanceColor',
  125. oldMesh.geometry.attributes.instanceColor.clone()
  126. );
  127. this.scene.remove(oldMesh);
  128. this.mesh = newMesh;
  129. this.scene.add(this.mesh);
  130. }
  131. const dummy = new THREE.Object3D();
  132. const instanceColors = this.mesh.geometry.attributes.instanceColor.array;
  133. for (let i = this.currentCount; i < totalPoints; i++) {
  134. const { x, y, z, color } = this.points[i];
  135. const renderPos = [];
  136. this.webgl.toRenderCoordinates(
  137. this.view,
  138. [x, y, z],
  139. 0,
  140. this.view.spatialReference,
  141. renderPos,
  142. 0,
  143. 1
  144. );
  145. dummy.position.set(renderPos[0], renderPos[1], renderPos[2]);
  146. dummy.updateMatrix();
  147. this.mesh.setMatrixAt(i, dummy.matrix);
  148. instanceColors[i * 3] = color[0];
  149. instanceColors[i * 3 + 1] = color[1];
  150. instanceColors[i * 3 + 2] = color[2];
  151. // 创建新增点的边框
  152. const edgesGeometry = new THREE.EdgesGeometry(this.mesh.geometry);
  153. const glowMaterial = new THREE.LineBasicMaterial({
  154. color: new THREE.Color(color[0], color[1], color[2]),
  155. opacity: 0.2,
  156. linewidth: 1,
  157. transparent: true,
  158. side: THREE.DoubleSide,
  159. });
  160. const edgeLine = new THREE.LineSegments(edgesGeometry, glowMaterial);
  161. edgeLine.position.copy(dummy.position);
  162. // this.scene.add(edgeLine);
  163. // this.edgesReferences.push(edgeLine);
  164. }
  165. // 更新实例属性
  166. this.mesh.instanceMatrix.needsUpdate = true;
  167. this.mesh.geometry.attributes.instanceColor.needsUpdate = true;
  168. // 更新当前点位数
  169. this.currentCount = totalPoints;
  170. },
  171. // 点击事件处理函数
  172. onMouseClick(event) {
  173. // 获取点击位置的屏幕坐标
  174. let mouse = []
  175. this.webgl.toRenderCoordinates(
  176. this.view,
  177. [event.mapPoint.x, event.mapPoint.y, event.mapPoint.z],
  178. 0,
  179. this.view.spatialReference,
  180. mouse,
  181. 0,
  182. 1
  183. );
  184. const mousePoint = new THREE.Vector3(mouse[0], mouse[1], mouse[2]);
  185. // 获取摄像机位置
  186. const cameraPosition = new THREE.Vector3().setFromMatrixPosition(this._camera.matrixWorld);
  187. // 计算射线方向
  188. const rayDirection = new THREE.Vector3().subVectors(mousePoint, cameraPosition).normalize();
  189. // 设置射线起点和方向
  190. this.raycaster.set(cameraPosition, rayDirection);
  191. //this.visualizeRay()
  192. // 检测射线与网格的相交
  193. const intersects = this.raycaster.intersectObject(this.mesh);
  194. if (this.lastSelectedIndex !== null && this.lastSelectedIndex !== undefined) {
  195. this.unHighlightCube(this.lastSelectedIndex);
  196. }
  197. if (intersects.length > 0) {
  198. const instanceIndex = intersects[0].instanceId;
  199. const selectedCube = this.points[instanceIndex];
  200. this.callBackFunction(selectedCube);
  201. // 高亮选中格子
  202. this.highlightCube(instanceIndex);
  203. } else {
  204. console.log("未选中任何格子");
  205. }
  206. },
  207. visualizeRay() {
  208. // 清理上次绘制的射线(避免重复绘制)
  209. if (this.rayHelper) {
  210. this.scene.remove(this.rayHelper);
  211. }
  212. // 获取射线的起点和方向
  213. const rayOrigin = this.raycaster.ray.origin;
  214. const rayDirection = this.raycaster.ray.direction.clone().multiplyScalar(10000000); // 射线长度为10
  215. // 创建射线的几何体
  216. const rayGeometry = new THREE.BufferGeometry().setFromPoints([
  217. rayOrigin,
  218. rayOrigin.clone().add(rayDirection),
  219. ]);
  220. // 创建射线的材质
  221. const rayMaterial = new THREE.LineBasicMaterial({
  222. color: 0xff0000, // 红色射线
  223. linewidth: 2, // 线宽
  224. });
  225. // 创建射线的线条对象
  226. this.rayHelper = new THREE.Line(rayGeometry, rayMaterial);
  227. // 将射线添加到场景
  228. this.scene.add(this.rayHelper);
  229. console.log("Ray Origin:", rayOrigin);
  230. console.log("Ray Direction:", rayDirection);
  231. },
  232. highlightCube(instanceIndex) {
  233. const color = new THREE.Color(0xffff); // 红色高亮
  234. this.setCubeStatus(instanceIndex,color);
  235. this.lastSelectedIndex = instanceIndex;
  236. },
  237. unHighlightCube(instanceIndex){
  238. const color = new THREE.Color(); // 红色高亮
  239. color.r = this.points[instanceIndex].color[0];
  240. color.g = this.points[instanceIndex].color[1];
  241. color.b = this.points[instanceIndex].color[2];
  242. this.setCubeStatus(instanceIndex,color);
  243. },
  244. setCubeStatus(instanceIndex,color){
  245. const instanceColors = this.mesh.geometry.attributes.instanceColor.array;
  246. instanceColors[instanceIndex * 3] = color.r;
  247. instanceColors[instanceIndex * 3 + 1] = color.g;
  248. instanceColors[instanceIndex * 3 + 2] = color.b;
  249. this.mesh.geometry.attributes.instanceColor.needsUpdate = true;
  250. // 修改边框颜色
  251. const selectedEdge = this.edgesReferences[instanceIndex];
  252. if (selectedEdge) {
  253. selectedEdge.material.color.set(color);
  254. selectedEdge.material.needsUpdate = true;
  255. }
  256. },
  257. render() {
  258. let cam = this.camera;
  259. this._camera.position.set(cam.eye[0], cam.eye[1], cam.eye[2]);
  260. this._camera.up.set(cam.up[0], cam.up[1], cam.up[2]);
  261. this._camera.lookAt(new THREE.Vector3(cam.center[0], cam.center[1], cam.center[2]));
  262. this._camera.projectionMatrix.fromArray(cam.projectionMatrix);
  263. this.renderer.state.reset();
  264. this.bindRenderTarget();
  265. this.renderer.render(this.scene, this._camera);
  266. this.requestRender();
  267. this.resetWebGLState();
  268. }
  269. };