zoukankan      html  css  js  c++  java
  • Three.js PathControl源码解析

    /**
     * @author alteredq / http://alteredqualia.com/
     */
    
    THREE.PathControls = function ( object, domElement ) {
    
        this.object = object;
        this.domElement = ( domElement !== undefined ) ? domElement : document;
    
        this.id = "PathControls" + THREE.PathControlsIdCounter ++;
    
        // API
    
        this.duration = 10 * 1000; // milliseconds; // 运动时间
        this.waypoints = [];// 轨道上的关键点,一般都是轨道上的转折点,因为非转折点一般可以通过插值算出来
    
        this.useConstantSpeed = true;// 是否匀速
        this.resamplingCoef = 50;
    
        this.debugPath = new THREE.Object3D();// 调试线,有了它可以判断相机是否按照预定轨道来行进
        this.debugDummy = new THREE.Object3D();
    
        this.animationParent = new THREE.Object3D();
    
        this.lookSpeed = 0.005;// 转头速度
        // 下面两个表示相机是否可以上下、左右摆动
        this.lookVertical = true;
        this.lookHorizontal = true;
        
        this.verticalAngleMap   = { srcRange: [ 0, 2 * Math.PI ], dstRange: [ 0, 2 * Math.PI ] };
        this.horizontalAngleMap = { srcRange: [ 0, 2 * Math.PI ], dstRange: [ 0, 2 * Math.PI ] };
    
        // internals
    
        this.target = new THREE.Object3D();
    
        this.mouseX = 0;
        this.mouseY = 0;
    
        this.lat = 0;
        this.lon = 0;
    
        this.phi = 0;
        this.theta = 0;
    
        var PI2 = Math.PI * 2;
        // 在渲染时候,为了计算渲染的视图大小、鼠标所在位置等,我们需要提前知道视图大小;
        // 这里讲视图大小不能出在viewHalfX和viewHalfY变量中,这样当鼠标移动的时候,
        // 我们就可以知道鼠标在窗口中的相对位置了,这样可以根据鼠标来控制相机
        this.viewHalfX = 0;
        this.viewHalfY = 0;
    
        if ( this.domElement !== document ) {
    
            this.domElement.setAttribute( 'tabindex', -1 );
    
        }
    
        // methods
    
        this.handleResize = function () {
    
            if ( this.domElement === document ) {
    
                this.viewHalfX = window.innerWidth / 2;
                this.viewHalfY = window.innerHeight / 2;
    
            } else {
    
                this.viewHalfX = this.domElement.offsetWidth / 2;
                this.viewHalfY = this.domElement.offsetHeight / 2;
    
            }
    
        };
    
        this.update = function ( delta ) {
    
            var srcRange, dstRange;
    
            if( this.lookHorizontal ) this.lon += this.mouseX * this.lookSpeed * delta;
            if( this.lookVertical )   this.lat -= this.mouseY * this.lookSpeed * delta;
    
            this.lon = Math.max( 0, Math.min( 360, this.lon ) );
            this.lat = Math.max( - 85, Math.min( 85, this.lat ) );
    
            this.phi = THREE.Math.degToRad( 90 - this.lat );
            this.theta = THREE.Math.degToRad( this.lon );
    
            this.phi = normalize_angle_rad( this.phi );
    
            // constrain vertical look angle
    
            srcRange = this.verticalAngleMap.srcRange;
            dstRange = this.verticalAngleMap.dstRange;
    
            var tmpPhi = THREE.Math.mapLinear( this.phi, srcRange[ 0 ], srcRange[ 1 ], dstRange[ 0 ], dstRange[ 1 ] );
            var tmpPhiFullRange = dstRange[ 1 ] - dstRange[ 0 ];
            var tmpPhiNormalized = ( tmpPhi - dstRange[ 0 ] ) / tmpPhiFullRange;
    
            this.phi = QuadraticEaseInOut( tmpPhiNormalized ) * tmpPhiFullRange + dstRange[ 0 ];
    
            // constrain horizontal look angle
    
            srcRange = this.horizontalAngleMap.srcRange;
            dstRange = this.horizontalAngleMap.dstRange;
    
            var tmpTheta = THREE.Math.mapLinear( this.theta, srcRange[ 0 ], srcRange[ 1 ], dstRange[ 0 ], dstRange[ 1 ] );
            var tmpThetaFullRange = dstRange[ 1 ] - dstRange[ 0 ];
            var tmpThetaNormalized = ( tmpTheta - dstRange[ 0 ] ) / tmpThetaFullRange;
    
            this.theta = QuadraticEaseInOut( tmpThetaNormalized ) * tmpThetaFullRange + dstRange[ 0 ];
    
            var targetPosition = this.target.position,
                position = this.object.position;
    
            targetPosition.x = 100 * Math.sin( this.phi ) * Math.cos( this.theta );
            targetPosition.y = 100 * Math.cos( this.phi );
            targetPosition.z = 100 * Math.sin( this.phi ) * Math.sin( this.theta );
    
            this.object.lookAt( this.target.position );
    
        };
    
        this.onMouseMove = function ( event ) {
    
            if ( this.domElement === document ) {
    
                this.mouseX = event.pageX - this.viewHalfX;
                this.mouseY = event.pageY - this.viewHalfY;
    
            } else {
    
                this.mouseX = event.pageX - this.domElement.offsetLeft - this.viewHalfX;
                this.mouseY = event.pageY - this.domElement.offsetTop - this.viewHalfY;
    
            }
    
        };
    
        // utils
    
        function normalize_angle_rad( a ) {
    
            var b = a % PI2;
            return b >= 0 ? b : b + PI2;
    
        };
    
        function distance( a, b ) {
    
            var dx = a[ 0 ] - b[ 0 ],
                dy = a[ 1 ] - b[ 1 ],
                dz = a[ 2 ] - b[ 2 ];
    
            return Math.sqrt( dx * dx + dy * dy + dz * dz );
    
        };
    
        function QuadraticEaseInOut ( k ) {
    
            if ( ( k *= 2 ) < 1 ) return 0.5 * k * k;
            return - 0.5 * ( --k * ( k - 2 ) - 1 );
    
        };
    
        function bind( scope, fn ) {
    
            return function () {
    
                fn.apply( scope, arguments );
    
            };
    
        };
    
        function initAnimationPath( parent, spline, name, duration ) {
    
            var animationData = {
    
               name: name,
               fps: 0.6,
               length: duration,
    
               hierarchy: []
    
            };
    
            var i,
                parentAnimation, childAnimation,
                path = spline.getControlPointsArray(),
                sl = spline.getLength(),
                pl = path.length,
                t = 0,
                first = 0,
                last  = pl - 1;
            // 将path路径中的所有点,包括开始点和结束点都转换成为一个个关键帧,
            // 将这些关键帧存到自定义的parentAnimation变量中,关键帧包括相机的位置、缩放、旋转参数等,
            // 将这些关键帧组成的动作,通过THREE.AnimationHandler的add函数,加到动画引擎中;
            // 动画引擎负责在关键帧之间插值,来决定关键帧中间的相机的位置、大小、缩放等。
            parentAnimation = { parent: -1, keys: [] };
            parentAnimation.keys[ first ] = { time: 0,        pos: path[ first ], rot: [ 0, 0, 0, 1 ], scl: [ 1, 1, 1 ] };
            parentAnimation.keys[ last  ] = { time: duration, pos: path[ last ],  rot: [ 0, 0, 0, 1 ], scl: [ 1, 1, 1 ] };
    
            for ( i = 1; i < pl - 1; i++ ) {
    
                // real distance (approximation via linear segments)
    
                t = duration * sl.chunks[ i ] / sl.total;
    
                // equal distance
    
                //t = duration * ( i / pl );
    
                // linear distance
    
                //t += duration * distance( path[ i ], path[ i - 1 ] ) / sl.total;
    
                parentAnimation.keys[ i ] = { time: t, pos: path[ i ] };
    
            }
    
            animationData.hierarchy[ 0 ] = parentAnimation;
            // Animationhandler已经改名为AnimationMixer
            THREE.AnimationHandler.add( animationData );
    
            return new THREE.Animation( parent, name, THREE.AnimationHandler.CATMULLROM_FORWARD, false );
    
        };
    
    
        function createSplineGeometry( spline, n_sub ) {
    
            var i, index, position,
                geometry = new THREE.Geometry();
    
            for ( i = 0; i < spline.points.length * n_sub; i ++ ) {
    
                index = i / ( spline.points.length * n_sub );
                position = spline.getPoint( index );
    
                geometry.vertices[ i ] = new THREE.Vector3( position.x, position.y, position.z );
    
            }
    
            return geometry;
    
        };
    
        function createPath( parent, spline ) {
    
            var lineGeo = createSplineGeometry( spline, 10 ),
                particleGeo = createSplineGeometry( spline, 10 ),
                lineMat = new THREE.LineBasicMaterial( { color: 0xff0000, line 3 } ),
                lineObj = new THREE.Line( lineGeo, lineMat ),
                particleObj = new THREE.ParticleSystem( particleGeo, new THREE.ParticleBasicMaterial( { color: 0xffaa00, size: 3 } ) );
    
            lineObj.scale.set( 1, 1, 1 );
            parent.add( lineObj );
    
            particleObj.scale.set( 1, 1, 1 );
            parent.add( particleObj );
    
            var waypoint,
                geo = new THREE.SphereGeometry( 1, 16, 8 ),
                mat = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
    
            for ( var i = 0; i < spline.points.length; i ++ ) {
    
                waypoint = new THREE.Mesh( geo, mat );
                waypoint.position.copy( spline.points[ i ] );
                parent.add( waypoint );
    
            }
    
        };
    
        this.init = function ( ) {
    
            // constructor
    
            this.spline = new THREE.Spline();// 定义了一条显示路径的线
            this.spline.initFromArray( this.waypoints );// 将关键点复制到线中去
    
            if ( this.useConstantSpeed ) {
                // 如果相机速度时恒定的,对spline进行重采样
                // 比如三个点生成一条直线,那么在转折角处,一定会出现速度不恒定情况,
                // 为解决这个问题,让相机在路径上基本都是匀速运行,我们可以根据三个点拟合出一条曲线,
                // 然后根据采样大小,将拟合后的点重新放入spline中,新采样点一定会比之前多
                // 通过参数开控制采样点多少,参数越小采样点越多
                this.spline.reparametrizeByArcLength( this.resamplingCoef );
    
            }
    
            if ( this.createDebugDummy ) {// 是否绘制一直物体,调试作用
    
                var dummyParentMaterial = new THREE.MeshLambertMaterial( { color: 0x0077ff } ),
                dummyChildMaterial  = new THREE.MeshLambertMaterial( { color: 0x00ff00 } ),
                dummyParentGeo = new THREE.CubeGeometry( 10, 10, 20 ),
                dummyChildGeo  = new THREE.CubeGeometry( 2, 2, 10 );
    
                this.animationParent = new THREE.Mesh( dummyParentGeo, dummyParentMaterial );
    
                var dummyChild = new THREE.Mesh( dummyChildGeo, dummyChildMaterial );
                dummyChild.position.set( 0, 10, 0 );
    
                this.animation = initAnimationPath( this.animationParent, this.spline, this.id, this.duration );
    
                this.animationParent.add( this.object );
                this.animationParent.add( this.target );
                this.animationParent.add( dummyChild );
    
            } else {
    
                this.animation = initAnimationPath( this.animationParent, this.spline, this.id, this.duration );
                this.animationParent.add( this.target );
                this.animationParent.add( this.object );
    
            }
    
            if ( this.createDebugPath ) {
    
                createPath( this.debugPath, this.spline );
    
            }
    
            this.domElement.addEventListener( 'mousemove', bind( this, this.onMouseMove ), false );
    
        };
    
        this.handleResize();
    
    };
    
    THREE.PathControlsIdCounter = 0;
    您可以考虑给树发个小额微信红包以资鼓励
  • 相关阅读:
    Unknown host mirrors.opencas.cn You may need to adjust the proxy settings in Gradle 报错及解决办法
    Design editor is unavaiable until next gradle sync报错及解决办法
    mkdir创建目录失败
    读书笔记之梦断代码(三)
    Android学习——更新数据
    Android学习——添加数据
    Android学习——升级数据库
    Android学习——创建数据库
    开课第十一周周总结
    Android学习——数据库简介
  • 原文地址:https://www.cnblogs.com/dojo-lzz/p/14902175.html
Copyright © 2011-2022 走看看