|
@@ -0,0 +1,254 @@
|
|
|
|
+import * as THREE from "../js/three.module.js"
|
|
|
|
+
|
|
|
|
+import initTinyUSDZ from "../js/tinyusdz.js"
|
|
|
|
+
|
|
|
|
+import { OrbitControls } from "../js/OrbitControls.js"
|
|
|
|
+
|
|
|
|
+const urlAll = window.location.href.split("?m=")[1]
|
|
|
|
+
|
|
|
|
+// console.log(123456,urlAll);
|
|
|
|
+
|
|
|
|
+// const USDZ_FILEPATH = `https://4dkankan.oss-cn-shenzhen.aliyuncs.com/sxz/${urlAll}.usdz`;
|
|
|
|
+// const USDZ_FILEPATH = `https://us-test.4dkankan.com/20241202dome/Shuiyueguanyin.usdz`;
|
|
|
|
+const USDZ_FILEPATH = `https://swkz-1332577016.cos.ap-guangzhou.myqcloud.com/kanzhan/2025/06/23/data.usdz`;
|
|
|
|
+// const USDZ_FILEPATH = `https://3d-usdz.4dkankan.com/sxz/${urlAll}.usdz`
|
|
|
|
+
|
|
|
|
+document.addEventListener("DOMContentLoaded", async () => {
|
|
|
|
+ const loadingBar = document.getElementById("loading") // 获取加载条元素
|
|
|
|
+ loadingBar.style.display = "block" // 显示加载条
|
|
|
|
+
|
|
|
|
+ const usd_res = await fetch(USDZ_FILEPATH)
|
|
|
|
+ const totalBytes = parseInt(usd_res.headers.get("Content-Length"), 10) // 获取总字节数
|
|
|
|
+ const reader = usd_res.body.getReader() // 获取读取器
|
|
|
|
+ const chunks = [] // 存储数据块
|
|
|
|
+ let receivedLength = 0 // 已接收字节数
|
|
|
|
+
|
|
|
|
+ let timeOut = -1
|
|
|
|
+
|
|
|
|
+ // 更新加载条的函数
|
|
|
|
+ const updateLoadingBar = (loaded) => {
|
|
|
|
+ let percentage = (loaded / totalBytes) * 100
|
|
|
|
+ if (percentage >= 100) {
|
|
|
|
+ percentage = 100
|
|
|
|
+ // 隐藏加载条
|
|
|
|
+
|
|
|
|
+ clearTimeout(timeOut)
|
|
|
|
+ timeOut = setTimeout(() => {
|
|
|
|
+ const loadingBoxDom = document.querySelector(".loadingBox")
|
|
|
|
+ loadingBoxDom.style.opacity = 0
|
|
|
|
+ loadingBoxDom.style.pointerEvents = "none"
|
|
|
|
+ }, 300)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ loadingBar.style.width = `${percentage}%` // 更新加载条宽度
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 读取数据流
|
|
|
|
+ while (true) {
|
|
|
|
+ const { done, value } = await reader.read() // 读取数据
|
|
|
|
+ if (done) break // 如果读取完成,退出循环
|
|
|
|
+ chunks.push(value) // 存储数据块
|
|
|
|
+ receivedLength += value.length // 更新已接收字节数
|
|
|
|
+ updateLoadingBar(receivedLength) // 更新加载条
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ const usd_data = new Uint8Array(receivedLength) // 创建最终数据数组
|
|
|
|
+ let position = 0
|
|
|
|
+ for (const chunk of chunks) {
|
|
|
|
+ usd_data.set(chunk, position) // 将数据块写入最终数组
|
|
|
|
+ position += chunk.length // 更新位置
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // const usd_binary = new Uint8Array(usd_data);
|
|
|
|
+
|
|
|
|
+ // 加载 TinyUSDZ
|
|
|
|
+ initTinyUSDZ().then(function (TinyUSDZLoader) {
|
|
|
|
+ const usd = new TinyUSDZLoader.TinyUSDZLoader(usd_data)
|
|
|
|
+ // console.log(usd.numMeshes());
|
|
|
|
+ // 隐藏加载条
|
|
|
|
+ loadingBar.style.display = "none"
|
|
|
|
+
|
|
|
|
+ const scene = new THREE.Scene()
|
|
|
|
+ // 添加渐变背景
|
|
|
|
+ const bgTexture = new THREE.TextureLoader().load('./bg.png')
|
|
|
|
+ scene.background = bgTexture
|
|
|
|
+ const camera = new THREE.PerspectiveCamera(
|
|
|
|
+ 85,
|
|
|
|
+ window.innerWidth / window.innerHeight,
|
|
|
|
+ 0.1,
|
|
|
|
+ 1000
|
|
|
|
+ )
|
|
|
|
+
|
|
|
|
+ const renderer = new THREE.WebGLRenderer({
|
|
|
|
+ alpha: true, // 关键配置,设置背景透明
|
|
|
|
+ antialias: true, // 可选抗锯齿
|
|
|
|
+ })
|
|
|
|
+
|
|
|
|
+ renderer.setClearColor(0x000000, 1) // 第二个参数为透明度(0表示完全透明)
|
|
|
|
+
|
|
|
|
+ renderer.setSize(window.innerWidth, window.innerHeight)
|
|
|
|
+ renderer.setAnimationLoop(animate)
|
|
|
|
+ // 设置输出颜色空间为线性SRGB
|
|
|
|
+ renderer.outputColorSpace = THREE.LinearSRGBColorSpace
|
|
|
|
+ document.body.appendChild(renderer.domElement)
|
|
|
|
+
|
|
|
|
+ // First mesh only
|
|
|
|
+ const mesh = usd.getMesh(0)
|
|
|
|
+ //console.log("usd", usd)
|
|
|
|
+ //console.log("mesh", mesh);
|
|
|
|
+
|
|
|
|
+ //const geometry = new THREE.BoxGeometry( 1, 1, 1 );
|
|
|
|
+ const geometry = new THREE.BufferGeometry()
|
|
|
|
+ geometry.setAttribute(
|
|
|
|
+ "position",
|
|
|
|
+ new THREE.BufferAttribute(mesh.points, 3)
|
|
|
|
+ )
|
|
|
|
+ // TODO: set normal from mesh
|
|
|
|
+
|
|
|
|
+ if (mesh.hasOwnProperty("texcoords")) {
|
|
|
|
+ // console.log(mesh.texcoords);
|
|
|
|
+ geometry.setAttribute("uv", new THREE.BufferAttribute(mesh.texcoords, 2))
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ const usdMaterial = usd.getMaterial(mesh.materialId)
|
|
|
|
+ //console.log("usdMat", usdMaterial);
|
|
|
|
+ //if (usdMaterial.aaa) {
|
|
|
|
+ // console.log("aaa");
|
|
|
|
+ //}
|
|
|
|
+
|
|
|
|
+ var material
|
|
|
|
+
|
|
|
|
+ if (usdMaterial.hasOwnProperty("diffuseColorTextureId")) {
|
|
|
|
+ const diffTex = usd.getTexture(usdMaterial.diffuseColorTextureId)
|
|
|
|
+
|
|
|
|
+ const img = usd.getImage(diffTex.textureImageId)
|
|
|
|
+ // console.log(img);
|
|
|
|
+
|
|
|
|
+ // assume RGBA for now.
|
|
|
|
+ let image8Array = new Uint8ClampedArray(img.data)
|
|
|
|
+ let imgData = new ImageData(image8Array, img.width, img.height)
|
|
|
|
+
|
|
|
|
+ const texture = new THREE.DataTexture(imgData, img.width, img.height)
|
|
|
|
+ texture.flipY = true
|
|
|
|
+ texture.needsUpdate = true
|
|
|
|
+
|
|
|
|
+ material = new THREE.MeshBasicMaterial({
|
|
|
|
+ map: texture,
|
|
|
|
+ })
|
|
|
|
+ } else {
|
|
|
|
+ material = new THREE.MeshNormalMaterial()
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Assume triangulated indices.
|
|
|
|
+ geometry.setIndex(
|
|
|
|
+ new THREE.Uint32BufferAttribute(mesh.faceVertexIndices, 1)
|
|
|
|
+ )
|
|
|
|
+
|
|
|
|
+ geometry.computeVertexNormals()
|
|
|
|
+
|
|
|
|
+ //const material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
|
|
|
|
+ const cube = new THREE.Mesh(geometry, material)
|
|
|
|
+
|
|
|
|
+ // 缩放模型
|
|
|
|
+ cube.scale.set(7, 7, 7)
|
|
|
|
+ cube.updateMatrixWorld(true) // 强制更新世界矩阵[3](@ref)
|
|
|
|
+
|
|
|
|
+ // 计算包围盒
|
|
|
|
+ const box = new THREE.Box3().setFromObject(cube)
|
|
|
|
+ const center = new THREE.Vector3()
|
|
|
|
+ box.getCenter(center)
|
|
|
|
+
|
|
|
|
+ // 居中模型
|
|
|
|
+ cube.position.sub(center)
|
|
|
|
+
|
|
|
|
+ // 计算尺寸并调整相机
|
|
|
|
+ const size = box.getSize(new THREE.Vector3()).length()
|
|
|
|
+ camera.near = size / 100
|
|
|
|
+ camera.far = size * 100
|
|
|
|
+ camera.updateProjectionMatrix()
|
|
|
|
+ camera.position.set(size * 0.5, size * 0.5, size * 0.5)
|
|
|
|
+ camera.lookAt(0, 0, 0)
|
|
|
|
+ // 向上移动模型
|
|
|
|
+ // cube.position.y += 0.5 // 向上
|
|
|
|
+ scene.add(cube)
|
|
|
|
+
|
|
|
|
+ const controls = new OrbitControls(camera, renderer.domElement)
|
|
|
|
+
|
|
|
|
+ controls.enablePan = true // 禁用右键平移功能
|
|
|
|
+
|
|
|
|
+ controls.enableZoom = true // 必须禁用轨道控制器的默认缩放[1](@ref)
|
|
|
|
+
|
|
|
|
+ controls.enableDamping = true
|
|
|
|
+ controls.dampingFactor = 0.25
|
|
|
|
+ controls.screenSpacePanning = false
|
|
|
|
+ controls.minPolarAngle = Math.PI / 180 * 1 // 1 度(约 0.01745 弧度)
|
|
|
|
+ controls.maxPolarAngle = Math.PI - Math.PI / 180 * 1 // 179 度(约 3.124 弧度)
|
|
|
|
+
|
|
|
|
+ // 兼容鼠标滚轮与触摸屏双指缩放
|
|
|
|
+ // 兼容鼠标滚轮与触摸屏双指缩放
|
|
|
|
+ // const handleZoom = (delta) => {
|
|
|
|
+ // // console.log('--------',delta);
|
|
|
|
+
|
|
|
|
+ // cube.updateMatrixWorld(true);
|
|
|
|
+ // // 计算包围盒
|
|
|
|
+ // const box = new THREE.Box3().setFromObject(cube);
|
|
|
|
+ // const center = new THREE.Vector3();
|
|
|
|
+ // box.getCenter(center);
|
|
|
|
+
|
|
|
|
+ // // 居中模型
|
|
|
|
+ // cube.position.sub(center);
|
|
|
|
+
|
|
|
|
+ // cube.scale.multiplyScalar(delta);
|
|
|
|
+ // cube.scale.clampScalar(0.5, 4); // 限制缩放范围0.5-3倍[1,6](@ref)
|
|
|
|
+ // };
|
|
|
|
+
|
|
|
|
+ // 鼠标滚轮事件
|
|
|
|
+ // window.addEventListener(
|
|
|
|
+ // "wheel",
|
|
|
|
+ // (e) => {
|
|
|
|
+ // e.preventDefault();
|
|
|
|
+ // const zoomFactor = e.deltaY > 0 ? 0.95 : 1.05;
|
|
|
|
+ // handleZoom(zoomFactor);
|
|
|
|
+ // },
|
|
|
|
+ // { passive: false }
|
|
|
|
+ // );
|
|
|
|
+
|
|
|
|
+ // 触摸屏双指缩放
|
|
|
|
+ // let initialDistance = 0;
|
|
|
|
+ // window.addEventListener("touchstart", (e) => {
|
|
|
|
+ // if (e.touches.length === 2) {
|
|
|
|
+ // initialDistance = Math.hypot(
|
|
|
|
+ // e.touches[0].pageX - e.touches[1].pageX,
|
|
|
|
+ // e.touches[0].pageY - e.touches[1].pageY
|
|
|
|
+ // );
|
|
|
|
+ // }
|
|
|
|
+ // });
|
|
|
|
+
|
|
|
|
+ // window.addEventListener("touchmove", (e) => {
|
|
|
|
+ // if (e.touches.length === 2) {
|
|
|
|
+ // const currentDistance = Math.hypot(
|
|
|
|
+ // e.touches[0].pageX - e.touches[1].pageX,
|
|
|
|
+ // e.touches[0].pageY - e.touches[1].pageY
|
|
|
|
+ // );
|
|
|
|
+ // const zoomFactor = currentDistance > initialDistance ? 1.01 : 0.99;
|
|
|
|
+ // handleZoom(zoomFactor);
|
|
|
|
+ // initialDistance = currentDistance;
|
|
|
|
+ // }
|
|
|
|
+ // });
|
|
|
|
+
|
|
|
|
+ function animate() {
|
|
|
|
+ //cube.rotation.x += 0.01;
|
|
|
|
+ // cube.rotation.y += 0.02;
|
|
|
|
+
|
|
|
|
+ controls.update()
|
|
|
|
+
|
|
|
|
+ renderer.render(scene, camera)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ window.addEventListener("resize", () => {
|
|
|
|
+ camera.aspect = window.innerWidth / window.innerHeight
|
|
|
|
+ camera.updateProjectionMatrix()
|
|
|
|
+ renderer.setSize(window.innerWidth, window.innerHeight)
|
|
|
|
+ })
|
|
|
|
+ })
|
|
|
|
+})
|