|
@@ -1,6 +1,6 @@
|
|
|
import { useEffect, useMemo, useRef, useState } from "react";
|
|
|
-import { Button, Canvas, View } from "@tarojs/components";
|
|
|
-import { FC } from "@tarojs/taro";
|
|
|
+import { Button, View } from "@tarojs/components";
|
|
|
+import Taro, { FC } from "@tarojs/taro";
|
|
|
import { observer } from "mobx-react";
|
|
|
import {
|
|
|
ROTATE_TYPE,
|
|
@@ -17,15 +17,18 @@ import {
|
|
|
Mesh,
|
|
|
MeshStandardMaterial,
|
|
|
PointLight,
|
|
|
+ Vector2,
|
|
|
Vector3,
|
|
|
} from "three-platformize";
|
|
|
import { Easing, Tween } from "@tweenjs/tween.js";
|
|
|
import { cancelAnimationFrame, requestAnimationFrame } from "@tarojs/runtime";
|
|
|
+import { CanvasAdapter } from "../../components";
|
|
|
import "./index.scss";
|
|
|
|
|
|
enum MODEL_STATE {
|
|
|
DEFAULT = 0,
|
|
|
ZOOM_UP = 1,
|
|
|
+ ZOOM_UP_CLICK = 2,
|
|
|
}
|
|
|
|
|
|
interface TweenHandlerOptions<T extends object> {
|
|
@@ -41,8 +44,10 @@ interface TweenHandlerOptions<T extends object> {
|
|
|
delay?: number;
|
|
|
}
|
|
|
|
|
|
+const DEFUALT_SCALE = 20;
|
|
|
const DENGZHAO_MIN_ROTATE = -1.2;
|
|
|
const DENGZHAO_MAX_ROTATE = -2.55;
|
|
|
+const system = Taro.getSystemInfoSync();
|
|
|
|
|
|
const IndexPage: FC = observer(() => {
|
|
|
const clock = useRef(new Clock());
|
|
@@ -62,8 +67,17 @@ const IndexPage: FC = observer(() => {
|
|
|
() => modelState === MODEL_STATE.ZOOM_UP,
|
|
|
[modelState]
|
|
|
);
|
|
|
+ /**
|
|
|
+ * 是否单独展示某个模型
|
|
|
+ */
|
|
|
+ const isSingleModel = useMemo(
|
|
|
+ () => modelState === MODEL_STATE.ZOOM_UP_CLICK,
|
|
|
+ [modelState]
|
|
|
+ );
|
|
|
const hotSeparateAnimArr = useRef<ReticleModel[]>([]);
|
|
|
const tagArr = useRef<TagModel[]>([]);
|
|
|
+ const startMouse = useRef(new Vector2(0, 0));
|
|
|
+ const mouseV2 = useRef(new Vector2(0, 0));
|
|
|
|
|
|
useEffect(() => {
|
|
|
setTimeout(async () => {
|
|
@@ -96,13 +110,8 @@ const IndexPage: FC = observer(() => {
|
|
|
fileType: "fbx",
|
|
|
});
|
|
|
|
|
|
- renderModel.modelMaterialList.forEach((material) => {
|
|
|
+ renderModel.modelMaterialList.forEach((mesh) => {
|
|
|
const meshStandardMaterial = new MeshStandardMaterial();
|
|
|
- // TODO: 暂定 any
|
|
|
- const mesh: any = renderModel.scene.getObjectByProperty(
|
|
|
- "uuid",
|
|
|
- material.uuid
|
|
|
- );
|
|
|
|
|
|
// 设置物体是否投射阴影
|
|
|
mesh.castShadow = true;
|
|
@@ -117,7 +126,7 @@ const IndexPage: FC = observer(() => {
|
|
|
// 设置材质渲染面,2:双面渲染;1:只渲染正面;0:只渲染背面
|
|
|
meshStandardMaterial.side = 2;
|
|
|
|
|
|
- switch (material.name) {
|
|
|
+ switch (mesh.name) {
|
|
|
case "dengzhao1":
|
|
|
case "dengzhao2":
|
|
|
case "dengpan":
|
|
@@ -224,13 +233,7 @@ const IndexPage: FC = observer(() => {
|
|
|
DENGZHAO_MIN_ROTATE +
|
|
|
(DENGZHAO_MAX_ROTATE - DENGZHAO_MIN_ROTATE) * (e - 1) * 0.1;
|
|
|
|
|
|
- renderModel.modelMaterialList.forEach((material) => {
|
|
|
- // TODO: 暂定 any
|
|
|
- const mesh: any = renderModel.scene.getObjectByProperty(
|
|
|
- "uuid",
|
|
|
- material.uuid
|
|
|
- );
|
|
|
-
|
|
|
+ renderModel.modelMaterialList.forEach((mesh) => {
|
|
|
if (e > 5) {
|
|
|
const t = 1 - 10 * (e - 8) * (1 / 30);
|
|
|
if (e >= 8) {
|
|
@@ -264,10 +267,6 @@ const IndexPage: FC = observer(() => {
|
|
|
shadowPlan.current!.visible = show;
|
|
|
};
|
|
|
|
|
|
- const handleClick = () => {
|
|
|
- renderModel.setAutoRotate(ROTATE_TYPE.DELAY);
|
|
|
- };
|
|
|
-
|
|
|
/**
|
|
|
* 拆解/合并模型
|
|
|
*/
|
|
@@ -381,56 +380,47 @@ const IndexPage: FC = observer(() => {
|
|
|
const handleModelSeparateAnimation = (type: boolean, cb?: Function) => {
|
|
|
let loadCount = 0;
|
|
|
|
|
|
- renderModel.model?.traverse((item) => {
|
|
|
- // @ts-ignore
|
|
|
- if (item.isMesh) {
|
|
|
- if (type) {
|
|
|
- const pos = item.position.clone();
|
|
|
- switch (item.name) {
|
|
|
- case "dengguan":
|
|
|
- pos.add(new Vector3(0, 1, 0));
|
|
|
- break;
|
|
|
- case "dengpan":
|
|
|
- pos.add(new Vector3(0, -0.5, 0));
|
|
|
- break;
|
|
|
- case "dengzhao1":
|
|
|
- pos.add(new Vector3(-0.3, 0.2, 0));
|
|
|
- break;
|
|
|
- case "dengzhao2":
|
|
|
- pos.add(new Vector3(0.3, 0.2, 0));
|
|
|
- break;
|
|
|
- case "tongniudengdizuo":
|
|
|
- pos.add(new Vector3(0, -1, 0));
|
|
|
- }
|
|
|
-
|
|
|
- tweenHandler({
|
|
|
- curProps: item.position,
|
|
|
- targetProps: pos,
|
|
|
- cb: () => {
|
|
|
- loadCount++;
|
|
|
-
|
|
|
- if (loadCount === 5) {
|
|
|
- initTag(true);
|
|
|
- addReticleMeshs(true);
|
|
|
- cb && cb();
|
|
|
- }
|
|
|
- },
|
|
|
- });
|
|
|
- } else {
|
|
|
- const model = renderModel.modelMaterialList.find(
|
|
|
- (i) => i.name === item.name
|
|
|
- );
|
|
|
-
|
|
|
- if (model) {
|
|
|
- initTag(false);
|
|
|
- addReticleMeshs(false);
|
|
|
- tweenHandler({
|
|
|
- curProps: item.position,
|
|
|
- targetProps: model.position,
|
|
|
- cb,
|
|
|
- });
|
|
|
- }
|
|
|
+ renderModel.modelMaterialList.forEach((item) => {
|
|
|
+ if (type) {
|
|
|
+ const pos = item.position.clone();
|
|
|
+ switch (item.name) {
|
|
|
+ case "dengguan":
|
|
|
+ pos.add(new Vector3(0, 1, 0));
|
|
|
+ break;
|
|
|
+ case "dengpan":
|
|
|
+ pos.add(new Vector3(0, -0.5, 0));
|
|
|
+ break;
|
|
|
+ case "dengzhao1":
|
|
|
+ pos.add(new Vector3(-0.3, 0.2, 0));
|
|
|
+ break;
|
|
|
+ case "dengzhao2":
|
|
|
+ pos.add(new Vector3(0.3, 0.2, 0));
|
|
|
+ break;
|
|
|
+ case "tongniudengdizuo":
|
|
|
+ pos.add(new Vector3(0, -1, 0));
|
|
|
}
|
|
|
+
|
|
|
+ tweenHandler({
|
|
|
+ curProps: item.position,
|
|
|
+ targetProps: pos,
|
|
|
+ cb: () => {
|
|
|
+ loadCount++;
|
|
|
+
|
|
|
+ if (loadCount === 5) {
|
|
|
+ initTag(true);
|
|
|
+ initReticleMeshs(true);
|
|
|
+ cb && cb();
|
|
|
+ }
|
|
|
+ },
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ initTag(false);
|
|
|
+ initReticleMeshs(false);
|
|
|
+ tweenHandler({
|
|
|
+ curProps: item.position,
|
|
|
+ targetProps: item._position,
|
|
|
+ cb,
|
|
|
+ });
|
|
|
}
|
|
|
});
|
|
|
};
|
|
@@ -474,7 +464,7 @@ const IndexPage: FC = observer(() => {
|
|
|
});
|
|
|
};
|
|
|
|
|
|
- const addReticleMeshs = (visible: boolean) => {
|
|
|
+ const initReticleMeshs = (visible: boolean) => {
|
|
|
if (!hotSeparateAnimArr.current.length) {
|
|
|
const temp: ReticleModel[] = [];
|
|
|
const vectorStack = [
|
|
@@ -517,14 +507,136 @@ const IndexPage: FC = observer(() => {
|
|
|
requestAnimationFrame(animate);
|
|
|
};
|
|
|
|
|
|
+ const clickHandler = (x: number, y: number) => {
|
|
|
+ const v2 = new Vector2(x, y);
|
|
|
+
|
|
|
+ // 比较两个向量是否相等
|
|
|
+ if (v2.equals(startMouse.current)) {
|
|
|
+ setCanvasPosition(x, y);
|
|
|
+
|
|
|
+ const res = renderModel.mouseRaycaster(mouseV2.current);
|
|
|
+ for (let i = 0; i < res.length; i++) {
|
|
|
+ const name = res[i].object.name;
|
|
|
+
|
|
|
+ if (!name) continue;
|
|
|
+
|
|
|
+ console.log("click model:", name);
|
|
|
+
|
|
|
+ if (
|
|
|
+ isSeparate &&
|
|
|
+ res[i].object.visible &&
|
|
|
+ res[i].object.parent?.visible
|
|
|
+ ) {
|
|
|
+ handleModelZoomUp(name);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 单独展示某个模型
|
|
|
+ */
|
|
|
+ const handleModelZoomUp = (name: string) => {
|
|
|
+ renderModel.setAutoRotate(ROTATE_TYPE.FALSE);
|
|
|
+ modelSingleClick(name, () => {
|
|
|
+ renderModel.setControlsStatus(false, true, false, true, false);
|
|
|
+ setModelState(MODEL_STATE.ZOOM_UP_CLICK);
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ const modelSingleClick = (name: string, cb: Function) => {
|
|
|
+ let targetProps = {
|
|
|
+ x: 0,
|
|
|
+ y: 0,
|
|
|
+ z: 0,
|
|
|
+ scale: DEFUALT_SCALE,
|
|
|
+ };
|
|
|
+
|
|
|
+ switch (name) {
|
|
|
+ case "dengguan":
|
|
|
+ targetProps = {
|
|
|
+ x: 0,
|
|
|
+ y: -68,
|
|
|
+ z: 0,
|
|
|
+ scale: 0.6 * DEFUALT_SCALE,
|
|
|
+ };
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (name) {
|
|
|
+ renderModel.modelMaterialList.forEach((model) => {
|
|
|
+ model.visible = model.name.includes(name);
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ initTag(false);
|
|
|
+ initReticleMeshs(false);
|
|
|
+ tweenHandler({
|
|
|
+ curProps: {
|
|
|
+ x: renderModel.model!.position.x,
|
|
|
+ y: renderModel.model!.position.y,
|
|
|
+ z: renderModel.model!.position.z,
|
|
|
+ scale: renderModel.model!.scale.x,
|
|
|
+ },
|
|
|
+ targetProps,
|
|
|
+ onUpdate: (e) => {
|
|
|
+ renderModel.model?.position.set(e.x, e.y, e.z);
|
|
|
+ renderModel.model?.scale.set(e.scale, e.scale, e.scale);
|
|
|
+ },
|
|
|
+ cb: () => {
|
|
|
+ if (name === "") {
|
|
|
+ renderModel.modelMaterialList.forEach((model) => {
|
|
|
+ model.visible = true;
|
|
|
+ });
|
|
|
+ initTag(true);
|
|
|
+ initReticleMeshs(true);
|
|
|
+ }
|
|
|
+
|
|
|
+ cb && cb();
|
|
|
+ },
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ const setCanvasPosition = (x: number, y: number) => {
|
|
|
+ mouseV2.current.x = (x / system.windowWidth) * 2 - 1;
|
|
|
+ mouseV2.current.y = (-y / system.windowHeight) * 2 + 1;
|
|
|
+ };
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 返回分解模型
|
|
|
+ */
|
|
|
+ const backForSeparate = () => {
|
|
|
+ modelSingleClick("", () => {
|
|
|
+ renderModel.setControlsStatus(true, true, false, true, false);
|
|
|
+ setModelState(MODEL_STATE.ZOOM_UP);
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
return (
|
|
|
- <View className="page" onClick={handleClick}>
|
|
|
- <Canvas id="wgl" type="webgl" />
|
|
|
+ <View className="page">
|
|
|
+ <CanvasAdapter
|
|
|
+ onTouchStart={(e) => {
|
|
|
+ // @ts-ignore
|
|
|
+ startMouse.current = new Vector2(e.clientX, e.clientY);
|
|
|
+ renderModel.setAutoRotate(ROTATE_TYPE.DELAY);
|
|
|
+ }}
|
|
|
+ onTouchEnd={(e) => {
|
|
|
+ // @ts-ignore
|
|
|
+ clickHandler(e.clientX, e.clientY);
|
|
|
+ }}
|
|
|
+ />
|
|
|
|
|
|
<View className="toolbar">
|
|
|
- <Button className="btn" onClick={handleSeparate}>
|
|
|
- {!isSeparate ? "拆解" : "合并"}
|
|
|
- </Button>
|
|
|
+ {isSingleModel ? (
|
|
|
+ <Button className="btn" onClick={backForSeparate}>
|
|
|
+ 返回
|
|
|
+ </Button>
|
|
|
+ ) : (
|
|
|
+ <Button className="btn" onClick={handleSeparate}>
|
|
|
+ {!isSeparate ? "拆解" : "合并"}
|
|
|
+ </Button>
|
|
|
+ )}
|
|
|
</View>
|
|
|
</View>
|
|
|
);
|