|
@@ -0,0 +1,190 @@
|
|
|
+import * as THREE from "three";
|
|
|
+import {
|
|
|
+ AxesHelper,
|
|
|
+ BoxGeometry,
|
|
|
+ Mesh,
|
|
|
+ MeshLambertMaterial,
|
|
|
+ PerspectiveCamera,
|
|
|
+ Vector2,
|
|
|
+} from "three";
|
|
|
+import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
|
|
|
+import { EffectComposer } from "three/examples/jsm/postprocessing/EffectComposer";
|
|
|
+import { RenderPass } from "three/examples/jsm/postprocessing/RenderPass";
|
|
|
+import { OutlinePass } from "three/examples/jsm/postprocessing/OutlinePass";
|
|
|
+import { FBXLoader } from "three/examples/jsm/loaders/FBXLoader";
|
|
|
+
|
|
|
+export default class ThreeHelper {
|
|
|
+ width: number = 0;
|
|
|
+ height: number = 0;
|
|
|
+ group: THREE.Group;
|
|
|
+ geometry: THREE.BoxGeometry;
|
|
|
+ material: THREE.MeshLambertMaterial;
|
|
|
+ mesh: THREE.Mesh;
|
|
|
+ camera: THREE.PerspectiveCamera;
|
|
|
+ axesHelper: THREE.AxesHelper;
|
|
|
+ scene: THREE.Scene;
|
|
|
+ light: THREE.PointLight;
|
|
|
+ renderer: THREE.WebGLRenderer | null = null;
|
|
|
+
|
|
|
+ constructor() {
|
|
|
+ this.group = new THREE.Group();
|
|
|
+
|
|
|
+ this.geometry = new BoxGeometry(60, 90, 80);
|
|
|
+ this.material = new MeshLambertMaterial({
|
|
|
+ color: 0x009900,
|
|
|
+ transparent: true,
|
|
|
+ opacity: 0.5,
|
|
|
+ });
|
|
|
+ this.mesh = new Mesh(this.geometry, this.material);
|
|
|
+ this.group.add(this.mesh);
|
|
|
+
|
|
|
+ this.camera = this.creatCamera();
|
|
|
+ this.axesHelper = new AxesHelper(400);
|
|
|
+ this.light = this.creatLight();
|
|
|
+
|
|
|
+ this.scene = new THREE.Scene();
|
|
|
+ this.scene.add(this.group);
|
|
|
+ this.scene.add(this.axesHelper);
|
|
|
+ this.scene.add(this.light);
|
|
|
+ }
|
|
|
+
|
|
|
+ creatLight = () => {
|
|
|
+ const light = new THREE.PointLight(0xffffff, 1.0);
|
|
|
+ light.intensity = 300.0; //光照强度
|
|
|
+ light.decay = 0.0; //设置光源不随距离衰减
|
|
|
+ light.position.set(1000, 1000, 1000);
|
|
|
+ return light;
|
|
|
+ };
|
|
|
+
|
|
|
+ creatRenderer = (dom: HTMLElement) => {
|
|
|
+ this.width = dom.clientWidth;
|
|
|
+ this.height = dom.clientHeight;
|
|
|
+
|
|
|
+ const renderer: THREE.WebGLRenderer = new THREE.WebGLRenderer({
|
|
|
+ antialias: true,
|
|
|
+ });
|
|
|
+ renderer.setSize(this.width, this.height);
|
|
|
+ renderer.setClearColor(0x000000, 0.4);
|
|
|
+ this.renderer = renderer;
|
|
|
+
|
|
|
+ // 改变相机观察目标
|
|
|
+ this.camera.aspect = this.width / this.height;
|
|
|
+ this.camera.updateProjectionMatrix();
|
|
|
+
|
|
|
+ // 注意相机控件OrbitControls会影响lookAt设置,注意手动设置OrbitControls的目标参数
|
|
|
+
|
|
|
+ const controls = new OrbitControls(this.camera, this.renderer.domElement);
|
|
|
+ // controls.target = mesh.position;
|
|
|
+ // controls.update();
|
|
|
+ controls.addEventListener("change", () => {
|
|
|
+ this.renderer!.render(this.scene, this.camera);
|
|
|
+ });
|
|
|
+
|
|
|
+ // console.log(this.renderer, dom);
|
|
|
+
|
|
|
+ dom.appendChild(renderer.domElement);
|
|
|
+
|
|
|
+ this.renderer.render(this.scene, this.camera);
|
|
|
+ };
|
|
|
+
|
|
|
+ loadFBX = (src: string, finish: () => void) => {
|
|
|
+ const loader = new FBXLoader();
|
|
|
+ loader.load(
|
|
|
+ src,
|
|
|
+ (fbx) => {
|
|
|
+ console.log("loadFBX", fbx);
|
|
|
+ this.group.add(fbx);
|
|
|
+ this.renderer!.render(this.scene, this.camera);
|
|
|
+ finish();
|
|
|
+ },
|
|
|
+ (event) => {
|
|
|
+ console.log(
|
|
|
+ "loadFBX" + (event.loaded / event.total) * 100 + "% loaded"
|
|
|
+ );
|
|
|
+ },
|
|
|
+ (error) => {
|
|
|
+ console.log("loadFBX", error);
|
|
|
+ finish();
|
|
|
+ }
|
|
|
+ );
|
|
|
+ };
|
|
|
+
|
|
|
+ addOutline = (
|
|
|
+ width: number = this.width,
|
|
|
+ height: number = this.height,
|
|
|
+ model?: THREE.Object3D
|
|
|
+ ) => {
|
|
|
+ const composer = new EffectComposer(this.renderer!);
|
|
|
+ const renderPass = new RenderPass(this.scene, this.camera);
|
|
|
+ const outlinePass = new OutlinePass(
|
|
|
+ new Vector2(width, height),
|
|
|
+ this.scene,
|
|
|
+ this.camera
|
|
|
+ );
|
|
|
+ //模型描边颜色,默认白色
|
|
|
+ outlinePass.visibleEdgeColor.set(0xffff00);
|
|
|
+ //高亮发光描边厚度
|
|
|
+ outlinePass.edgeThickness = 4;
|
|
|
+ //高亮描边发光强度
|
|
|
+ outlinePass.edgeStrength = 6;
|
|
|
+ //模型闪烁频率控制,默认0不闪烁
|
|
|
+ outlinePass.pulsePeriod = 2;
|
|
|
+ composer.addPass(renderPass);
|
|
|
+ composer.addPass(outlinePass);
|
|
|
+ // composer.render();
|
|
|
+
|
|
|
+ const that = this;
|
|
|
+
|
|
|
+ this.renderer!.domElement.addEventListener("click", function (event) {
|
|
|
+ // .offsetY、.offsetX以canvas画布左上角为坐标原点,单位px
|
|
|
+ const px = event.offsetX;
|
|
|
+ const py = event.offsetY;
|
|
|
+ //屏幕坐标px、py转WebGL标准设备坐标x、y
|
|
|
+ //width、height表示canvas画布宽高度
|
|
|
+ const x = (px / width) * 2 - 1;
|
|
|
+ const y = -(py / height) * 2 + 1;
|
|
|
+ //创建一个射线投射器`Raycaster`
|
|
|
+ const raycaster = new THREE.Raycaster();
|
|
|
+ //.setFromCamera()计算射线投射器`Raycaster`的射线属性.ray
|
|
|
+ // 形象点说就是在点击位置创建一条射线,射线穿过的模型代表选中
|
|
|
+ raycaster.setFromCamera(new THREE.Vector2(x, y), that.camera);
|
|
|
+ //.intersectObjects([mesh1, mesh2, mesh3])对参数中的网格模型对象进行射线交叉计算
|
|
|
+ // 未选中对象返回空数组[],选中一个对象,数组1个元素,选中两个对象,数组两个元素
|
|
|
+ const toAddModelArray = [];
|
|
|
+ if (model) {
|
|
|
+ toAddModelArray.push(model);
|
|
|
+ }
|
|
|
+ const intersects = raycaster.intersectObjects([
|
|
|
+ that.mesh,
|
|
|
+ ...toAddModelArray,
|
|
|
+ ]);
|
|
|
+ console.log("射线器返回的对象", intersects);
|
|
|
+ // intersects.length大于0说明,说明选中了模型
|
|
|
+ if (intersects.length > 0) {
|
|
|
+ outlinePass.selectedObjects = [intersects[0].object];
|
|
|
+ composer.render();
|
|
|
+ }
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ resizeRender = (dom: HTMLElement) => {
|
|
|
+ this.width = dom.clientWidth;
|
|
|
+ this.height = dom.clientHeight;
|
|
|
+ this.renderer!.setSize(this.width, this.height);
|
|
|
+ this.camera.aspect = this.width / this.height;
|
|
|
+ this.camera.updateProjectionMatrix();
|
|
|
+ this.renderer!.render(this.scene, this.camera);
|
|
|
+ };
|
|
|
+
|
|
|
+ private creatCamera = () => {
|
|
|
+ const camera = new PerspectiveCamera(
|
|
|
+ 75,
|
|
|
+ window.innerWidth / window.innerHeight,
|
|
|
+ 0.1,
|
|
|
+ 3000
|
|
|
+ );
|
|
|
+ camera.position.set(1000, 1000, 1000);
|
|
|
+ camera.lookAt(0, 0, 0);
|
|
|
+ return camera;
|
|
|
+ };
|
|
|
+}
|