Skip to content

Commit 3a62067

Browse files
committed
feat: add dimension view and route
1 parent 115b638 commit 3a62067

2 files changed

Lines changed: 311 additions & 4 deletions

File tree

src/pages/index/router/index.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router';
22
import Home from '../views/Home.vue';
33
import TutorialIndex from '../views/tutorial/index.vue';
44
import TutorialHello from '../views/tutorial/Hello.vue';
5-
import TutoialDismantling from '../views/tutorial/Dismantling.vue';
6-
import TutoialDismantling2 from '../views/tutorial/Dismantling2.vue';
5+
import TutorialDismantling from '../views/tutorial/Dismantling.vue';
6+
import TutorialDismantling2 from '../views/tutorial/Dismantling2.vue';
7+
import TutorialDimension from '../views/tutorial/Dimension.vue';
78

89
const routes: Array<RouteRecordRaw> = [
910
{
@@ -25,12 +26,17 @@ const routes: Array<RouteRecordRaw> = [
2526
{
2627
path: 'dismantling',
2728
name: '固定模型拆分效果',
28-
component: TutoialDismantling
29+
component: TutorialDismantling
2930
},
3031
{
3132
path: 'dismantling2',
3233
name: '动态模型拆分效果',
33-
component: TutoialDismantling2
34+
component: TutorialDismantling2
35+
},
36+
{
37+
path: 'dimension',
38+
name: '展示模型尺寸',
39+
component: TutorialDimension
3440
}
3541
]
3642
}
Lines changed: 301 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,301 @@
1+
<template>
2+
<div>
3+
<div class="button-container"><input type="checkbox" v-model="showDimension" /> 显示尺寸</div>
4+
<div class="footer">
5+
Model by&nbsp;<a
6+
href="https://sketchfab.com/3d-models/motorcycle-custom-bike-jawa-low-poly-13771fe558604aedae09b5157029e790"
7+
target="_blank"
8+
>Kreems</a
9+
>
10+
</div>
11+
<canvas
12+
id="container"
13+
ref="container"
14+
style="width: 100vw; height: 100vh; background-color: #222" />
15+
</div>
16+
</template>
17+
<script>
18+
/**
19+
* Created by leon<silenceace@gmail.com> on 22/06/15.
20+
*/
21+
import * as THREE from 'three';
22+
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
23+
import { MTLLoader } from 'three/examples/jsm/loaders/MTLLoader.js';
24+
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader';
25+
26+
const modelInfo = {
27+
name: 'motorcycle',
28+
res: {
29+
obj: 'model/motorcycle/motorcycle.obj',
30+
mtl: 'model/motorcycle/motorcycle.mtl'
31+
}
32+
};
33+
34+
let scene;
35+
36+
export default {
37+
data() {
38+
return {
39+
showDimension: false,
40+
41+
container: undefined,
42+
canvas: undefined,
43+
renderer: undefined,
44+
camera: undefined,
45+
controls: undefined,
46+
activeModel: undefined,
47+
activeModelMaterials: undefined,
48+
actvieModelSize: {
49+
box: undefined,
50+
boxSize: undefined,
51+
boxCenter: undefined,
52+
boxSizeLength: undefined
53+
}
54+
};
55+
},
56+
computed: {},
57+
mounted() {
58+
window.onresize = this.onWindowResize;
59+
60+
this.container = this.$refs.container;
61+
62+
this.rendererCanvas();
63+
this.loadModel(modelInfo);
64+
},
65+
unmounted() {
66+
window.onresize = null;
67+
},
68+
methods: {
69+
onWindowResize() {
70+
this.camera.aspect = this.container.offsetWidt / this.container.offsetHeight; // 重新设置宽高比
71+
this.camera.updateProjectionMatrix(); // 更新相机
72+
this.renderer.setSize(this.container.offsetWidth, this.container.offsetHeight);
73+
},
74+
// 初始化画布
75+
rendererCanvas() {
76+
const canvas = window.document.querySelector('#container');
77+
const renderer = new THREE.WebGLRenderer({
78+
canvas,
79+
// 执行抗锯齿
80+
antialias: true,
81+
// 包含透明度
82+
alpha: true,
83+
// 设置为可缓存
84+
preserveDrawingBuffer: true
85+
});
86+
87+
renderer.setSize(this.container.offsetWidth, this.container.offsetHeight);
88+
renderer.setPixelRatio(window.devicePixelRatio);
89+
90+
this.canvas = canvas;
91+
this.renderer = renderer;
92+
93+
scene = new THREE.Scene();
94+
},
95+
// 根据模型大小计算并设置相机
96+
setCamera() {
97+
const camera = new THREE.PerspectiveCamera(
98+
45,
99+
this.container.offsetWidth / this.container.offsetHeight,
100+
0.1,
101+
1000
102+
);
103+
// 设置相机默认位置
104+
camera.position.set(0, 10, 20);
105+
106+
this.frameArea(
107+
this.actvieModelSize.boxSizeLength * 1.2,
108+
this.actvieModelSize.boxSizeLength,
109+
this.actvieModelSize.boxCenter,
110+
camera
111+
);
112+
this.camera = camera;
113+
114+
this.setLight();
115+
116+
this.setControls();
117+
118+
this.renderer.render(scene, this.camera);
119+
},
120+
setLight() {
121+
const ambientLight = new THREE.AmbientLight(0xffffff, 0.4);
122+
scene.add(ambientLight);
123+
124+
const pointLight = new THREE.PointLight(0xffffff, 0.4);
125+
this.camera.add(pointLight);
126+
127+
scene.add(this.camera);
128+
},
129+
setControls() {
130+
const controls = new OrbitControls(this.camera, this.renderer.domElement);
131+
controls.update();
132+
this.controls = controls;
133+
134+
const animate = () => {
135+
window.requestAnimationFrame(animate);
136+
// required if controls.enableDamping or controls.autoRotate are set to true
137+
this.controls.update();
138+
this.renderer.render(scene, this.camera);
139+
};
140+
animate();
141+
},
142+
// 根据模型尺寸为相机设置合适的位置
143+
frameArea(sizeToFitOnScreen, boxSize, boxCenter, camera) {
144+
const halfSizeToFitOnScreen = sizeToFitOnScreen * 0.6;
145+
const halfFovY = THREE.MathUtils.degToRad(camera.fov * 0.5);
146+
const distance = halfSizeToFitOnScreen / Math.tan(halfFovY);
147+
const direction = new THREE.Vector3()
148+
.subVectors(camera.position, boxCenter)
149+
.multiply(new THREE.Vector3(1, 0, 1))
150+
.normalize();
151+
152+
camera.position.copy(direction.multiplyScalar(distance).add(boxCenter));
153+
154+
camera.near = boxSize / 1000;
155+
camera.far = boxSize * 1000;
156+
157+
camera.updateProjectionMatrix();
158+
159+
camera.lookAt(boxCenter);
160+
},
161+
loadModel(_modelInfo) {
162+
const mtlManager = this.loadMtlManager();
163+
const modelManager = this.loadModelManager();
164+
165+
const mtlLoader = new MTLLoader(mtlManager);
166+
mtlLoader.load(_modelInfo.res.mtl, (materials) => {
167+
materials.preload();
168+
169+
console.log('model materials:', materials);
170+
171+
this.activeModelMaterials = materials;
172+
173+
new OBJLoader(modelManager)
174+
.setMaterials(materials)
175+
.load(
176+
_modelInfo.res.obj,
177+
this.loadModelSuccess,
178+
this.loadModelProcess,
179+
this.loadModelError
180+
);
181+
});
182+
},
183+
// MTL加载控制器
184+
loadMtlManager() {
185+
const manager = new THREE.LoadingManager();
186+
manager.onStart = function (item, loaded, total) {
187+
console.log('Loading Mtl started');
188+
};
189+
// 纹理加载完成清除滚动条
190+
manager.onLoad = function () {
191+
console.log('Loading Mtl complete');
192+
};
193+
194+
manager.onProgress = function (item, loaded, total) {
195+
const percentComplete = (loaded / total) * 100;
196+
// console.log('Mtl loading percent:', percentComplete);
197+
};
198+
199+
manager.onError = function (err) {
200+
console.error('Mtl Load Error.', err);
201+
};
202+
return manager;
203+
},
204+
/**
205+
* 模型加载器
206+
*/
207+
loadModelManager() {
208+
const manager = new THREE.LoadingManager();
209+
manager.onStart = function (item, loaded, total) {
210+
console.log('Loading Model started');
211+
};
212+
// 纹理加载完成清除滚动条
213+
manager.onLoad = function () {
214+
console.log('Loading Model complete');
215+
};
216+
217+
manager.onProgress = function (item, loaded, total) {
218+
const percentComplete = (loaded / total) * 100;
219+
// console.log('Model load percent:', percentComplete);
220+
};
221+
222+
manager.onError = function (url) {
223+
console.error('Model load error:', url);
224+
};
225+
return manager;
226+
},
227+
// 加载成功模型
228+
loadModelSuccess(modelNode) {
229+
console.log('model node', modelNode);
230+
231+
const modelNodeHelp = new THREE.BoxHelper(modelNode, 0xffff00);
232+
233+
scene.add(modelNodeHelp);
234+
scene.add(modelNode);
235+
236+
// 根据模型的尺寸设置相机位置
237+
const box = new THREE.Box3().setFromObject(modelNode);
238+
const boxSize = box.getSize(new THREE.Vector3());
239+
const boxSizeLength = boxSize.length();
240+
const boxCenter = box.getCenter(new THREE.Vector3());
241+
242+
this.actvieModelSize.boxSize = boxSize;
243+
this.actvieModelSize.boxCenter = boxCenter;
244+
this.actvieModelSize.boxSizeLength = boxSizeLength;
245+
246+
// 设置加载模型的名称
247+
modelNode.name = 'mainModel';
248+
249+
this.activeModel = modelNode;
250+
251+
// 设置相机
252+
this.setCamera();
253+
},
254+
255+
loadModelProcess(xhr) {
256+
if (xhr.lengthComputable) {
257+
const percentComplete = (xhr.loaded / xhr.total) * 100;
258+
// console.log(`${Math.round(percentComplete, 2)}% downloaded`);
259+
}
260+
},
261+
loadModelError(err) {
262+
console.error('load model error.', err);
263+
}
264+
},
265+
watch: {}
266+
};
267+
</script>
268+
<style lang="scss" scoped>
269+
a {
270+
color: #409eff;
271+
}
272+
.button-container {
273+
position: absolute;
274+
margin: auto;
275+
width: 100vw;
276+
display: flex;
277+
justify-content: center;
278+
color: #fff;
279+
align-items: center;
280+
}
281+
.button-container > * {
282+
margin: 10px;
283+
font-size: 20px;
284+
background-color: #409eff;
285+
border: none;
286+
color: white;
287+
padding: 10px;
288+
border-radius: 10px;
289+
cursor: pointer;
290+
}
291+
.footer {
292+
position: absolute;
293+
bottom: 0;
294+
box-sizing: border-box;
295+
padding: 0 20px 0 0;
296+
width: 100vw;
297+
height: 30px;
298+
color: white;
299+
text-align: right;
300+
}
301+
</style>

0 commit comments

Comments
 (0)