|
@@ -0,0 +1,258 @@
|
|
|
+<template>
|
|
|
+ <div>
|
|
|
+ <div id="container"></div>
|
|
|
+ <canvas id="canvas"></canvas>
|
|
|
+ <div class="top">
|
|
|
+ <el-select v-model="value" size="mini" placeholder="请选择">
|
|
|
+ <el-option
|
|
|
+ v-for="item in options"
|
|
|
+ :key="item.value"
|
|
|
+ :label="item.label"
|
|
|
+ :value="item.value"
|
|
|
+ >
|
|
|
+ </el-option>
|
|
|
+ </el-select>
|
|
|
+ <el-select v-model="value" size="mini" placeholder="请选择">
|
|
|
+ <el-option
|
|
|
+ v-for="item in options"
|
|
|
+ :key="item.value"
|
|
|
+ :label="item.label"
|
|
|
+ :value="item.value"
|
|
|
+ >
|
|
|
+ </el-option>
|
|
|
+ </el-select>
|
|
|
+ </div>
|
|
|
+ <div class="rt">
|
|
|
+ <el-select v-model="value" size="mini" placeholder="请选择">
|
|
|
+ <el-option
|
|
|
+ v-for="item in options"
|
|
|
+ :key="item.value"
|
|
|
+ :label="item.label"
|
|
|
+ :value="item.value"
|
|
|
+ >
|
|
|
+ </el-option>
|
|
|
+ </el-select>
|
|
|
+ </div>
|
|
|
+ <div class="menu">
|
|
|
+ <el-button
|
|
|
+ v-if="playStatu"
|
|
|
+ icon="el-icon-video-play"
|
|
|
+ @click="paly"
|
|
|
+ circle
|
|
|
+ ></el-button>
|
|
|
+ <el-button
|
|
|
+ v-else
|
|
|
+ icon="el-icon-video-pause"
|
|
|
+ @click="paly"
|
|
|
+ circle
|
|
|
+ ></el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+import { FBXLoader } from "three/examples/jsm/loaders/FBXLoader";
|
|
|
+import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
|
|
|
+import * as Three from "three";
|
|
|
+import { SwitchSprite } from "../../public/static/three_sprite_utils";
|
|
|
+import { GridHelper, Raycaster, Vector2 } from "three";
|
|
|
+var camera = null;
|
|
|
+var scene = null;
|
|
|
+var renderer = null;
|
|
|
+var clock = null;
|
|
|
+var orbitControls = null;
|
|
|
+var animationMixer = null;
|
|
|
+var walkAction = null;
|
|
|
+var sprite = null;
|
|
|
+var group = null;
|
|
|
+export default {
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ publicPath: process.env.BASE_URL,
|
|
|
+ playStatu: true,
|
|
|
+ options: [{
|
|
|
+ value: '选项1',
|
|
|
+ label: '黄金糕'
|
|
|
+ }, {
|
|
|
+ value: '选项2',
|
|
|
+ label: '双皮奶'
|
|
|
+ }, {
|
|
|
+ value: '选项3',
|
|
|
+ label: '蚵仔煎'
|
|
|
+ }, {
|
|
|
+ value: '选项4',
|
|
|
+ label: '龙须面'
|
|
|
+ }, {
|
|
|
+ value: '选项5',
|
|
|
+ label: '北京烤鸭'
|
|
|
+ }],
|
|
|
+ value: ''
|
|
|
+ };
|
|
|
+ },
|
|
|
+ mounted() {
|
|
|
+ this.element = document.getElementById("container");
|
|
|
+ // this.animate();
|
|
|
+ this.init();
|
|
|
+ // this.spriteText()
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ paly() {
|
|
|
+ this.playStatu == true ? walkAction.play() : walkAction.stop();
|
|
|
+ this.playStatu = !this.playStatu;
|
|
|
+ },
|
|
|
+ //初始化
|
|
|
+ init() {
|
|
|
+ scene = new Three.Scene();
|
|
|
+ clock = new Three.Clock();
|
|
|
+ camera = new Three.PerspectiveCamera(
|
|
|
+ 70,
|
|
|
+ window.innerWidth / window.innerHeight,
|
|
|
+ 1,
|
|
|
+ 1000
|
|
|
+ );
|
|
|
+ camera.position.set(5, 21, -1);
|
|
|
+ renderer = new Three.WebGLRenderer({ antialias: true });
|
|
|
+ orbitControls = new OrbitControls(camera, renderer.domElement);
|
|
|
+ orbitControls.target = new Three.Vector3(0, 19, 0);
|
|
|
+ animationMixer = new Three.AnimationMixer(scene);
|
|
|
+ const container = document.getElementById("container");
|
|
|
+ renderer.setSize(window.innerWidth, window.innerHeight);
|
|
|
+ container.appendChild(renderer.domElement);
|
|
|
+ window.addEventListener("resize", () => this.onWindowResize());
|
|
|
+ // PolarGridHelper( radius:标网格的半径, radials:径向线的数量, circles:圆圈数,
|
|
|
+ // divisions:每个圆圈使用的线段数, color1:用于网格元素的第一种颜色, color2:用于网格元素的第一种颜色 )
|
|
|
+ var radius = 20;
|
|
|
+ var radials = 15;
|
|
|
+ var circles = 15;
|
|
|
+ var divisions = 100;
|
|
|
+ var helper02 = new Three.PolarGridHelper(
|
|
|
+ radius,
|
|
|
+ radials,
|
|
|
+ circles,
|
|
|
+ divisions,
|
|
|
+ "#6adcff",
|
|
|
+ "#6adcff"
|
|
|
+ );
|
|
|
+ // helper02.position.set(0, -20, 0)
|
|
|
+ scene.add(helper02);
|
|
|
+
|
|
|
+ let that = this;
|
|
|
+ var loader = new FBXLoader();
|
|
|
+ loader.load(`${that.publicPath}static/fan1.fbx`, async (fbx3d) => {
|
|
|
+ await console.log(fbx3d);
|
|
|
+
|
|
|
+ fbx3d.position.set(0, 12.4, -0.6);
|
|
|
+ fbx3d.scale.set(0.2, 0.2, 0.2);
|
|
|
+ fbx3d.children[9].material = fbx3d.children[6].material.clone();
|
|
|
+ fbx3d.children[6].material = fbx3d.children[6].material.clone();
|
|
|
+ fbx3d.children[9].material.transparent = true;
|
|
|
+ fbx3d.children[9].material.opacity = 0.3;
|
|
|
+ // fbx3d.children[9].material.wireframe = true;
|
|
|
+ // fbx3d.children[9].material.wireframeLinewidth = 20
|
|
|
+ // fbx3d.children[6].material.wireframe = true;
|
|
|
+ // fbx3d.children[9].material.color.set("#00bfff");
|
|
|
+ // fbx3d.children[6].material.color.set("#00bfff");
|
|
|
+ fbx3d.children[2].materialnew = new Three.MeshPhysicalMaterial({
|
|
|
+ color: 0xff0000,
|
|
|
+ // 材质像金属的程度. 非金属材料,如木材或石材,使用0.0,金属使用1.0,中间没有(通常).
|
|
|
+ // 默认 0.5. 0.0到1.0之间的值可用于生锈的金属外观
|
|
|
+ metalness: 1.0,
|
|
|
+ // 材料的粗糙程度. 0.0表示平滑的镜面反射,1.0表示完全漫反射. 默认 0.5
|
|
|
+ roughness: 0.6,
|
|
|
+ // 设置环境贴图
|
|
|
+ // 反射程度, 从 0.0 到1.0.默认0.5.
|
|
|
+ // 这模拟了非金属材料的反射率。 当metalness为1.0时无效
|
|
|
+ // reflectivity: 0.5,
|
|
|
+ });
|
|
|
+ scene.add(fbx3d);
|
|
|
+ scene.add(new Three.AmbientLight(0xffffff, 1.5));
|
|
|
+ const animationClip = fbx3d.animations.find(
|
|
|
+ (animationClip) => animationClip.name === "Take 001"
|
|
|
+ );
|
|
|
+ walkAction = animationMixer.clipAction(animationClip);
|
|
|
+ });
|
|
|
+
|
|
|
+ //射线
|
|
|
+ renderer.domElement.addEventListener("click", (event) => {
|
|
|
+ const { offsetX, offsetY } = event;
|
|
|
+ const x = (offsetX / window.innerWidth) * 2 - 1;
|
|
|
+ const y = -(offsetY / window.innerHeight) * 2 + 1;
|
|
|
+ const mousePoint = new Vector2(x, y);
|
|
|
+ const raycaster = new Raycaster();
|
|
|
+ raycaster.params = { sprite: {} };
|
|
|
+ raycaster.setFromCamera(mousePoint, camera);
|
|
|
+ let intersects = raycaster.intersectObjects(scene.children, true);
|
|
|
+ intersects = intersects.filter(
|
|
|
+ (intersect) => !(intersect.object instanceof GridHelper)
|
|
|
+ );
|
|
|
+ console.log(intersects)
|
|
|
+ var selectedMesh = intersects[0].object;
|
|
|
+ var newSprite = SwitchSprite(
|
|
|
+ selectedMesh.name || "aa",
|
|
|
+ selectedMesh.position,
|
|
|
+ selectedMesh
|
|
|
+ );
|
|
|
+ if (newSprite != null) scene.add(newSprite);
|
|
|
+ });
|
|
|
+
|
|
|
+ this.render();
|
|
|
+ },
|
|
|
+ render() {
|
|
|
+ // 更新动画
|
|
|
+ animationMixer.update(clock.getDelta());
|
|
|
+ renderer.render(scene, camera);
|
|
|
+ orbitControls.update();
|
|
|
+ window.requestAnimationFrame(() => this.render());
|
|
|
+ },
|
|
|
+ onWindowResize() {
|
|
|
+ renderer.setSize(window.innerWidth, window.innerHeight);
|
|
|
+ camera.aspect = window.innerWidth / window.innerHeight;
|
|
|
+ camera.updateProjectionMatrix();
|
|
|
+ },
|
|
|
+ },
|
|
|
+ beforeDestroy() {
|
|
|
+ camera = null;
|
|
|
+ scene = null;
|
|
|
+ renderer = null;
|
|
|
+ clock = null;
|
|
|
+ orbitControls = null;
|
|
|
+ animationMixer = null;
|
|
|
+ walkAction = null;
|
|
|
+ },
|
|
|
+};
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+#container {
|
|
|
+ width: 1920px;
|
|
|
+ height: 100vh;
|
|
|
+}
|
|
|
+.menu {
|
|
|
+ position: fixed;
|
|
|
+ left: 20px;
|
|
|
+ top: 20px;
|
|
|
+ background-color: #fff;
|
|
|
+ border: 1px solid #fff;
|
|
|
+ border-radius: 20px;
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-around;
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+.top{
|
|
|
+ width: 60%;
|
|
|
+ display: flex;
|
|
|
+ position: fixed;
|
|
|
+ justify-content: space-around;
|
|
|
+ top: 20px;
|
|
|
+ left: 10%;
|
|
|
+}
|
|
|
+.rt {
|
|
|
+ width: 200px;
|
|
|
+ min-height: 200px;
|
|
|
+ position: fixed;
|
|
|
+ right: 20px;
|
|
|
+ top: 20px;
|
|
|
+ // border: 1px solid #fff;
|
|
|
+ // border-radius: 20px;
|
|
|
+}
|
|
|
+</style>
|