• three.js 9 TransformControls


    import * as THREE from 'three';
    import { TransformControls } from "three/examples/jsm/controls/TransformControls";
    import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
    
    /**
     * 3d Controls 控制器,变换控制器(TransformControls)
     * https://threejs.org/docs/index.html#examples/zh/controls/TransformControls
     */
    export class ThreeDoc9ControlTransform {
        constructor(canvasId) {
            this.work(canvasId);
        }
    
        work(canvasId) {
            // 创建 3d 场景
            const scene = new THREE.Scene();
            scene.background = new THREE.Color(0x9e9e9e);
    
            const renderer = new THREE.WebGLRenderer();
            renderer.setSize(window.innerWidth, window.innerHeight);
            // 最后一步很重要,我们将renderer(渲染器)的dom元素(renderer.domElement)添加到我们的HTML文档中。这就是渲染器用来显示场景给我们看的<canvas>元素。
            document.body.appendChild(renderer.domElement);
    
            // 点材质(PointsMaterial)
            this.getPointsMaterial(scene);
    
            // AxesHelper  3个坐标轴的对象.
            this.addAxesHelper(scene);
            // 半球光(HemisphereLight)
            this.addHemisphereLight(scene);
            // 网格辅助对象
            let size = 20;
            const gridHelper = new THREE.GridHelper(size, 10, 0x444444, 0xffffff);
            scene.add(gridHelper);
    
    
            const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
            // 设置相机位置
            camera.position.x = 5;
            camera.position.y = 6;
            camera.position.z = 15;
            camera.lookAt(0, 0, 0);
    
            // 添加物体
            let geometry = new THREE.BoxGeometry(2, 2, 2);
            let material = new THREE.MeshStandardMaterial({ color: 0x049EF4 });
            let obj = new THREE.Mesh(geometry, material);
            obj.position.set(0, 1, 0);
            scene.add(obj);
    
            // 添加鼠标操作视图
            let orbC = this.initMouseControl(scene, camera, renderer);
    
            // 添加拖动事件,返回平移控件对象
            let transformControls = this.initDragControl(scene, camera, renderer, orbC);
    
            // 添加操作面板,切换控制器类型
            this.addControlPanel(renderer, scene, camera, transformControls);
    
            // 添加点击事件,设置物体控制器显示
            this.addClickEvent(scene, camera, transformControls, renderer);
    
            renderer.render(scene, camera);
        }
    
        /**
         * AxesHelper
         * 用于简单模拟3个坐标轴的对象.
         * 红色代表 X 轴. 绿色代表 Y 轴. 蓝色代表 Z 轴.
         * AxesHelper( size : Number )
         * size -- (可选的) 表示代表轴的线段长度. 默认为 1.
         */
        addAxesHelper(scene) {
            const axesHelper = new THREE.AxesHelper(12);
            scene.add(axesHelper);
        }
    
        /**
         * 半球光(HemisphereLight) - 喜欢这个光
         * 光源直接放置于场景之上,光照颜色从天空光线颜色渐变到地面光线颜色。
         * 半球光不能投射阴影。
         *
         * HemisphereLight( skyColor : Integer, groundColor : Integer, intensity : Float )
         * skyColor - (可选参数) 天空中发出光线的颜色。 缺省值 0xffffff。
         * groundColor - (可选参数) 地面发出光线的颜色。 缺省值 0xffffff。
         * intensity - (可选参数) 光照强度。 缺省值 1。
         */
        addHemisphereLight(scene) {
            const light = new THREE.HemisphereLight(0xffffbb, 0x080820, 50);
            scene.add(light);
            light.position.set(0, -20, 0);
            // const helper = new THREE.HemisphereLightHelper(light, 3);
            // scene.add(helper);
        }
    
        /**
         * 点材质(PointsMaterial)
         * Points使用的默认材质。
         * PointsMaterial( parameters : Object )
         * parameters - (可选)用于定义材质外观的对象,具有一个或多个属性。 材质的任何属性都可以从此处传入(包括从Material继承的任何属性)。
         *
         * 属性color例外,其可以作为十六进制字符串传递,默认情况下为 0xffffff(白色),内部调用Color.set(color)。
         */
        getPointsMaterial(scene) {
            const vertices = [];
    
            for (let i = 0; i < 10000; i++) {
    
                const x = THREE.MathUtils.randFloatSpread(2000);
                const y = THREE.MathUtils.randFloatSpread(2000);
                const z = THREE.MathUtils.randFloatSpread(2000);
    
                vertices.push(x, y, z);
    
            }
    
            const geometry = new THREE.BufferGeometry();
            geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3));
    
            const material = new THREE.PointsMaterial({ color: 0xffffff });
    
            const points = new THREE.Points(geometry, material);
    
            scene.add(points);
        }
    
        /**
         * 鼠标操作
         */
        initMouseControl(scene, camera, renderer) {
            // 添加鼠标操作
            let controls = new OrbitControls(camera, renderer.domElement);
            controls.addEventListener('change', () => {
                renderer.render(scene, camera);
            });
            return controls;
        }
    
        // 添加拖拽控件
        initDragControl(scene, camera, renderer, orbC) {
    
            /**
             * 变换控制器(TransformControls)
             * 该类可提供一种类似于在数字内容创建工具(例如Blender)中对模型进行交互的方式,来在3D空间中变换物体。 和其他控制器不同的是,变换控制器不倾向于对场景摄像机的变换进行改变。
             * TransformControls 期望其所附加的3D对象是场景图的一部分。
             *
             * TransformControls( camera : Camera, domElement : HTMLDOMElement )
             * camera: 被控制的摄像机。
             * domElement: 用于事件监听的HTML元素。
             * 创建一个新的 TransformControls 实例。
             *
             * 事件
             * change : 如果发生了任何类型的改变(对象或属性的改变)则触发该事件。 属性改变是单独的事件,你也可以为此添加单独的事件监听;
             * 该事件类型为"propertyname-changed"(“属性名称-changed”)。
             * mouseDown : 如果指针(鼠标/触摸)为活动状态则触发该事件。
             * mouseUp : 如果指针(鼠标/触摸)不再为活动状态则触发该事件。
             * objectChange : 如果被控制的3D对象发生改变则触发该事件。。
             *
             * 属性
             * 共有属性请参见其基类Object3D。
             * .axis : String : 当前变换轴。
             * .camera : Camera : 渲染场景的摄像机。
             *
             * .domElement : HTMLDOMElement : 该 HTMLDOMElement 用于监听鼠标/触摸事件,该属性必须在构造函数中传入。
             * 在此处改变它将不会设置新的事件监听。
             *
             * .dragging : Boolean : 当前是否正在拖动。只读属性。
             * .enabled : Boolean : 是否启用控制器。默认为true。
             * .mode : String : 当前的变换模式。可能的值包括"translate"、"rotate" 和 "scale"。默认为translate。
             * .object : Object3D : 正在被控制的3D对象。
             *
             * .rotationSnap : Number : 默认情况下,3D对象是可以被连续旋转的。如果你将该值设为一个数值(弧度),
             * 则你将可以定义每次旋转3D对象时的步幅。 默认为null。
             *
             * .showX : Boolean : x轴手柄是否显示。默认为true。
             * .showY : Boolean : y轴手柄是否显示。默认为true。
             * .showZ : Boolean : z轴手柄是否显示。默认为true。
             * .size : Number : 手柄UI(轴/平面)的大小。默认为1。
             * .space : String : 定义了在哪种坐标空间中进行变换。可能的值有"world" 和 "local"。默认为world。
             * .translationSnap : Number : 默认情况下,3D对象是可以被连续平移的。如果你将该值设为一个数值(世界单位),则你将可以定义每次平移3D对象时的步幅。 默认为null。
             *
             * 方法
             * 共有方法请参见其基类Object3D。
             *
             * .attach ( object : Object3D ) : TransformControls
             * object: 应当变换的3D对象。
             *
             * 设置应当变换的3D对象,并确保控制器UI是可见的。
             *
             * .detach () : TransformControls
             * 从控制器中移除当前3D对象,并确保控制器UI是不可见的。
             *
             * .dispose () : undefined
             * 若不再需要该控制器,则应当调用此函数。
             *
             * .getRaycaster () : Raycaster
             * 返回用于用户交互的 Raycaster 对象。 此对象在所有实例之间共享 变换控件。 如果您设置 TransformControls 的 .layers 属性,您还需要 使用匹配值设置 Raycaster 上的 .layers 属性,否则设置 TransformControls 不会按预期工作。
             *
             * .getMode () : String
             * 返回变换模式。
             *
             * .setMode ( mode : String ) : undefined
             * mode: 变换模式。
             *
             * 设置变换模式。
             *
             * .setRotationSnap ( rotationSnap : Number ) : undefined
             * rotationSnap: 旋转捕捉步幅。
             *
             * 设置旋转捕捉。
             *
             * .setSize ( size : Number ) : undefined
             * size: 手柄UI的大小。
             *
             * 设置手柄UI的大小。
             *
             * .setSpace ( space : String ) : undefined
             * space: 应用变换的坐标空间。
             *
             * 设置应用变换的坐标空间。
             *
             * .setTranslationSnap ( translationSnap : Number ) : undefined
             * translationSnap: 平移捕捉步幅。
             *
             * 设置平移捕捉。
             * @type {TransformControls}
             */
            let transformControls = new TransformControls(camera, renderer.domElement);
            scene.add(transformControls);
            // 监听改变,则更新界面
            transformControls.addEventListener("change", () => {
                renderer.render(scene, camera);
            });
            // 变换控制器监听 mousedown,禁用 鼠标拖拽
            transformControls.addEventListener("mouseDown", () => {
                orbC.enabled = false;
            });
            //
            transformControls.addEventListener("mouseUp", () => {
                orbC.enabled = true;
            });
    
            return transformControls;
        }
    
        /**
         * 添加操作面板,按钮控制光源对象切换,添加平移控件关联
         * @param renderer
         * @param scene
         * @param camera
         * @param transformControls 平移控制器对象
         */
        addControlPanel(renderer, scene, camera, transformControls) {
            let ele = `
                <div class="control-panel">
                    <button class="mode-translate">translate</button>
                    <button class="mode-rotate">rotate</button>
                    <button class="mode-scale">scale</button>
                </div>
            `;
            $('body').append(ele);
            let cols = ['translate', 'rotate', 'scale'];
            for(let i = 0; i< cols.length; i++){
                // 绑定按钮事件
                $('.mode-' + cols[i]).on('click', function (e) {
                    // 控制模式改变:cols[i]
                    transformControls.mode = cols[i];
                    // renderer.render(scene, camera);
                });
            }
        }
    
        /**
         * 添加鼠标点击判断,是否点击了物体
         * @param scene
         * @param camera
         * @param transformControls
         */
        addClickEvent(scene, camera, transformControls, renderer) {
            /**
             * 使用官方的THREE.Raycaster
             * THREE.Raycaster是three.js中的射线类,其实现监听的原理是由相机位置为射线起点,由鼠标位置为射线方向发射射线,
             * 其穿过的所有几何体都会被监测到。
             * @type {*[]}
             */
            let intersects = []; //几何体合集
            const pointer = new THREE.Vector2();
            // 给 canvas 加监听点击事件
            document.getElementsByTagName('canvas')[0].addEventListener('click', meshOnClick);
            let raycaster = new THREE.Raycaster();
            function meshOnClick(event) {
    
                //geometrys 为需要监听的Mesh合集,可以通过这个集合来过滤掉不需要监听的元素例如地面天空
                let geometrys = [];
                for (let i = 0; i < scene.children.length; i++) {
                    console.log(scene.children[i].isLight);
                    // 物体 和 光源都监听点击
                    if (scene.children[i].isMesh || scene.children[i].isLight) {
                        geometrys.push(scene.children[i]);
                    }
                }
                console.log(geometrys);
                pointer.x = (event.clientX / window.innerWidth) * 2 - 1;
                pointer.y = -(event.clientY / window.innerHeight) * 2 + 1;
                raycaster.setFromCamera(pointer, camera);
                // true为不拾取子对象
                intersects = raycaster.intersectObjects(geometrys, true);
                // 被射线穿过的几何体为一个集合,越排在前面说明其位置离端点越近,所以直接取[0]
                console.log(intersects);
                if (intersects.length > 0) {
                    transformControls.attach(intersects[0].object);
                } else {
                    //若没有几何体被监听到,可以做一些取消操作,判断不到光源点击,直接移除控制不可行
                    // transformControls.detach();
                    // 当前控制器操控的对象
                    // console.log(transformControls.object);
                    console.log(1);
                }
                renderer.render(scene, camera);
    
            }
        }
    }
  • 相关阅读:
    练习2
    练习1
    如何生成添加前缀的顺序DIV
    mysql5.7 版本中 timestamp 不能为零日期 以及sql_mode合理设置
    MIME类型大全
    Intel Xeon E5-2620 v4参数
    webgl开发中添加IIS的mime类型
    jquery.validate动态更改校验规则
    mvc4
    asp.net防SQL/JS注入攻击:过滤标记
  • 原文地址:https://www.cnblogs.com/guofan/p/16348880.html
Copyright © 2020-2023  润新知