main.js 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. import * as THREE from '../js/three.module.js'
  2. import initTinyUSDZ from '../js/tinyusdz.js'
  3. import { OrbitControls } from '../js/OrbitControls.js'
  4. const urlAll = window.location.href.split('?m=')[1]
  5. // console.log(123456,urlAll);
  6. // const USDZ_FILEPATH = `https://3d-usdz.4dkankan.com/sxz/${urlAll}.usdz`
  7. document.addEventListener('DOMContentLoaded', async () => {
  8. try {
  9. const loadingBar = document.getElementById('loading') // 获取加载条元素
  10. loadingBar.style.display = 'block' // 显示加载条
  11. const usd_res = await fetch(urlAll)
  12. // console.log('-----------', usd_res)
  13. if (usd_res.status !== 200) {
  14. const dom = document.querySelector('.errBox')
  15. dom.innerHTML = '加载错误或找不到模型文件'
  16. dom.style.display = 'flex'
  17. return
  18. }
  19. const totalBytes = parseInt(usd_res.headers.get('Content-Length'), 10) // 获取总字节数
  20. const reader = usd_res.body.getReader() // 获取读取器
  21. const chunks = [] // 存储数据块
  22. let receivedLength = 0 // 已接收字节数
  23. let timeOut = -1
  24. // 更新加载条的函数
  25. const updateLoadingBar = loaded => {
  26. let percentage = (loaded / totalBytes) * 100
  27. if (percentage >= 100) {
  28. percentage = 100
  29. // 隐藏加载条
  30. clearTimeout(timeOut)
  31. timeOut = setTimeout(() => {
  32. const loadingBoxDom = document.querySelector('.loadingBox')
  33. loadingBoxDom.style.opacity = 0
  34. loadingBoxDom.style.pointerEvents = 'none'
  35. }, 300)
  36. }
  37. loadingBar.style.width = `${percentage}%` // 更新加载条宽度
  38. }
  39. // 读取数据流
  40. while (true) {
  41. const { done, value } = await reader.read() // 读取数据
  42. if (done) break // 如果读取完成,退出循环
  43. chunks.push(value) // 存储数据块
  44. receivedLength += value.length // 更新已接收字节数
  45. updateLoadingBar(receivedLength) // 更新加载条
  46. }
  47. const usd_data = new Uint8Array(receivedLength) // 创建最终数据数组
  48. let position = 0
  49. for (const chunk of chunks) {
  50. usd_data.set(chunk, position) // 将数据块写入最终数组
  51. position += chunk.length // 更新位置
  52. }
  53. // const usd_binary = new Uint8Array(usd_data);
  54. // 加载 TinyUSDZ
  55. initTinyUSDZ().then(function (TinyUSDZLoader) {
  56. const usd = new TinyUSDZLoader.TinyUSDZLoader(usd_data)
  57. // console.log(usd.numMeshes());
  58. // 隐藏加载条
  59. loadingBar.style.display = 'none'
  60. const scene = new THREE.Scene()
  61. const camera = new THREE.PerspectiveCamera(
  62. 75,
  63. window.innerWidth / window.innerHeight,
  64. 0.1,
  65. 1000
  66. )
  67. const renderer = new THREE.WebGLRenderer({
  68. alpha: true, // 关键配置,设置背景透明
  69. antialias: true // 可选抗锯齿
  70. })
  71. renderer.setClearColor(0x000000, 0) // 第二个参数为透明度(0表示完全透明)
  72. renderer.setSize(window.innerWidth, window.innerHeight)
  73. renderer.setAnimationLoop(animate)
  74. // 设置输出颜色空间为线性SRGB
  75. renderer.outputColorSpace = THREE.LinearSRGBColorSpace
  76. document.body.appendChild(renderer.domElement)
  77. // First mesh only
  78. const mesh = usd.getMesh(0)
  79. //console.log("usd", usd)
  80. //console.log("mesh", mesh);
  81. //const geometry = new THREE.BoxGeometry( 1, 1, 1 );
  82. const geometry = new THREE.BufferGeometry()
  83. geometry.setAttribute('position', new THREE.BufferAttribute(mesh.points, 3))
  84. // TODO: set normal from mesh
  85. if (mesh.hasOwnProperty('texcoords')) {
  86. // console.log(mesh.texcoords);
  87. geometry.setAttribute('uv', new THREE.BufferAttribute(mesh.texcoords, 2))
  88. }
  89. const usdMaterial = usd.getMaterial(mesh.materialId)
  90. //console.log("usdMat", usdMaterial);
  91. //if (usdMaterial.aaa) {
  92. // console.log("aaa");
  93. //}
  94. var material
  95. if (usdMaterial.hasOwnProperty('diffuseColorTextureId')) {
  96. const diffTex = usd.getTexture(usdMaterial.diffuseColorTextureId)
  97. const img = usd.getImage(diffTex.textureImageId)
  98. // console.log(img);
  99. // assume RGBA for now.
  100. let image8Array = new Uint8ClampedArray(img.data)
  101. let imgData = new ImageData(image8Array, img.width, img.height)
  102. const texture = new THREE.DataTexture(imgData, img.width, img.height)
  103. texture.flipY = true
  104. texture.needsUpdate = true
  105. material = new THREE.MeshBasicMaterial({
  106. map: texture
  107. })
  108. } else {
  109. material = new THREE.MeshNormalMaterial()
  110. }
  111. // Assume triangulated indices.
  112. geometry.setIndex(new THREE.Uint32BufferAttribute(mesh.faceVertexIndices, 1))
  113. geometry.computeVertexNormals()
  114. //const material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
  115. const cube = new THREE.Mesh(geometry, material)
  116. // 缩放模型
  117. cube.scale.set(2, 2, 2)
  118. cube.updateMatrixWorld(true) // 强制更新世界矩阵[3](@ref)
  119. // 计算包围盒
  120. const box = new THREE.Box3().setFromObject(cube)
  121. const center = new THREE.Vector3()
  122. box.getCenter(center)
  123. // 居中模型
  124. cube.position.sub(center)
  125. // 计算尺寸并调整相机
  126. const size = box.getSize(new THREE.Vector3()).length()
  127. camera.near = size / 100
  128. camera.far = size * 100
  129. camera.updateProjectionMatrix()
  130. camera.position.set(size * 0.5, size * 0.5, size * 0.5)
  131. camera.lookAt(0, 0, 0)
  132. scene.add(cube)
  133. const controls = new OrbitControls(camera, renderer.domElement)
  134. controls.enablePan = true // 禁用右键平移功能
  135. controls.enableZoom = true // 必须禁用轨道控制器的默认缩放[1](@ref)
  136. controls.enableDamping = true
  137. controls.dampingFactor = 0.25
  138. controls.screenSpacePanning = false
  139. controls.minPolarAngle = (Math.PI / 180) * 1 // 1 度(约 0.01745 弧度)
  140. controls.maxPolarAngle = Math.PI - (Math.PI / 180) * 1 // 179 度(约 3.124 弧度)
  141. // 兼容鼠标滚轮与触摸屏双指缩放
  142. // 兼容鼠标滚轮与触摸屏双指缩放
  143. // const handleZoom = (delta) => {
  144. // // console.log('--------',delta);
  145. // cube.updateMatrixWorld(true);
  146. // // 计算包围盒
  147. // const box = new THREE.Box3().setFromObject(cube);
  148. // const center = new THREE.Vector3();
  149. // box.getCenter(center);
  150. // // 居中模型
  151. // cube.position.sub(center);
  152. // cube.scale.multiplyScalar(delta);
  153. // cube.scale.clampScalar(0.5, 4); // 限制缩放范围0.5-3倍[1,6](@ref)
  154. // };
  155. // 鼠标滚轮事件
  156. // window.addEventListener(
  157. // "wheel",
  158. // (e) => {
  159. // e.preventDefault();
  160. // const zoomFactor = e.deltaY > 0 ? 0.95 : 1.05;
  161. // handleZoom(zoomFactor);
  162. // },
  163. // { passive: false }
  164. // );
  165. // 触摸屏双指缩放
  166. // let initialDistance = 0;
  167. // window.addEventListener("touchstart", (e) => {
  168. // if (e.touches.length === 2) {
  169. // initialDistance = Math.hypot(
  170. // e.touches[0].pageX - e.touches[1].pageX,
  171. // e.touches[0].pageY - e.touches[1].pageY
  172. // );
  173. // }
  174. // });
  175. // window.addEventListener("touchmove", (e) => {
  176. // if (e.touches.length === 2) {
  177. // const currentDistance = Math.hypot(
  178. // e.touches[0].pageX - e.touches[1].pageX,
  179. // e.touches[0].pageY - e.touches[1].pageY
  180. // );
  181. // const zoomFactor = currentDistance > initialDistance ? 1.01 : 0.99;
  182. // handleZoom(zoomFactor);
  183. // initialDistance = currentDistance;
  184. // }
  185. // });
  186. function animate() {
  187. //cube.rotation.x += 0.01;
  188. // cube.rotation.y += 0.02;
  189. controls.update()
  190. renderer.render(scene, camera)
  191. }
  192. window.addEventListener('resize', () => {
  193. camera.aspect = window.innerWidth / window.innerHeight
  194. camera.updateProjectionMatrix()
  195. renderer.setSize(window.innerWidth, window.innerHeight)
  196. })
  197. })
  198. } catch (error) {
  199. console.log('--------', error)
  200. }
  201. })