Three.CurvePath和自定义标记 [英] Three.CurvePath and custom markers

查看:102
本文介绍了Three.CurvePath和自定义标记的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用Three.JS创建游戏,我已经建模并成功导入了在Sketchup中创建的城市。我现在需要动态添加一些跟我来箭头(根据下面样机中的黄色箭头)。我相信我可能需要使用Three.CurvePath来实现这一点,但我不确定这是否是最好的方法 - 我是否需要手动建模路径并计算每个箭头对象的切线,以便它们自然地指向角落(如每个左转在模型中)?





希望这是有道理的!

解决方案

我可能有一个解决方案。
一段时间没有使用过three.js所以不确定这是否是最优雅的解决方案。
我开始使用形状示例,因为它显示:


  1. 如何在程序上构建路径(使用也处理曲线的命令)

  2. 如何从这样的路径中提取点
  3. li>

所以我把问题分成了:


  1. 生成路径

  2. 遍历路径和插值(位置和旋转)

生成路径
我重复使用圆角矩形定义,它看起来类似于截图的一部分。

  var roundedRectShape = new THREE.Shape(); 

(函数roundedRect(ctx,x,y,width,height,radius){

ctx.moveTo(x,y + radius);
ctx。 lineTo(x,y + height - radius);
ctx.quadraticCurveTo(x,y + height,x + radius,y + height);
ctx.lineTo(x + width - radius,y +高度);
ctx.quadraticCurveTo(x +宽度,y +高度,x +宽度,y +高度 - 半径);
ctx.lineTo(x +宽度,y +半径);
ctx.quadraticCurveTo(x + width,y,x + width - radius,y);
ctx.lineTo(x + radius,y);
ctx.quadraticCurveTo(x,y,x,y + radius);

})(roundedRectShape,0,0,200,200,20);

您的路径可能不是圆角矩形,而是可用的曲线函数类型(quadraticCurveTo,bezierCurveTo, splineThru)非常有用。



想到的另一个想法是使用Ruby脚本将路径坐标从Sketchup导出到three.js。要么从头开始编写,要么使用现有脚本。这是在谷歌上轻松找到的一个



遍历路径



幸运的是,three.js已经通过Path的 getPoint实现了这一点(t)其中t是从0.0到1.0的数字,表示路径上的遍历。所以获得位置是微不足道的,因为在路径上获得下一个插值位置。然后只需使用 Math.atan2()来获得轮换:

  t =(t + s)%1.0; //增加t,同时将其保持在0.0和1.0之间
var p = path.getPoint(t); //指向t
var pn = path.getPoint((t + s)%1.0); //指向下一个迭代

if(p!= null&& pn!= null){
/ /移动到当前位置
arrow.position.x = px;
arrow.position.y = p.y;
//根据下一个位置获取方向
arrow.rotation.z = Math.atan2(pn.y-p.y,pn.x-p.x);

}

总之,下面是一个基本的例子(使用立方体代替一个箭头形状)来说明基于形状示例生成和遍历three.js中的路径:

 <!DOCTYPE HTML> 
< html lang =en>
< head>
< title>路径插值< / title>
< meta charset =utf-8>
< meta name =viewportcontent =width = device-width,user-scalable = no,minimum-scale = 1.0,maximum-scale = 1.0>
< style>
body {
font-family:Monospace;
background-color:#f0f0f0;
保证金:0px;
溢出:隐藏;
}
< / style>
< / head>
< body>
< canvas id =debugstyle =position:absolute; left:100px>< / canvas>

< script src =../ build / three.min.js>< / script>

< script src =js / libs / stats.min.js>< / script>


< script>

var container,stats;

var camera,scene,renderer;

var text,plane;

var targetRotation = 0;
var targetRotationOnMouseDown = 0;

var mouseX = 0;
var mouseXOnMouseDown = 0;

var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;

init();
animate();

var t = 0.0; //路径上的遍历
var s = 0.001; //遍历的速度
var arrow; //要在路径$ b上移动/旋转的网格$ b var path; //要遍历的路径对象

函数init(){

container = document.createElement('div');
document.body.appendChild(container);

camera = new THREE.PerspectiveCamera(50,window.innerWidth / window.innerHeight,1,1000);
camera.position.set(0,150,500);

scene = new THREE.Scene();

parent = new THREE.Object3D();
parent.position.y = 50;
scene.add(parent);

arrow = new THREE.Mesh(new THREE.CubeGeometry(20,10,10),new THREE.MeshBasicMaterial({color:0x009900}));
parent.add(箭头);
//这对于视觉辅助非常有用但不是至关重要的
函数addShape(形状,拉伸设置,颜色,x,y,z,rx,ry,rz,s){

var points = shape.createPointsGeometry();
var spacedPoints = shape.createSpacedPointsGeometry(50);

//来自等距离采样点的透明线

var line = new THREE.Line(spacedPoints,new THREE.LineBasicMaterial({color:color,opacity:0.2})) ;
line.rotation.set(rx,ry,rz);
parent.add(line);

//等距离采样点

var pgeo = spacedPoints.clone();
var particles2 = new THREE.ParticleSystem(pgeo,new THREE.ParticleBasicMaterial({color:color,size:2,opacity:0.5}));
particles2.rotation.set(rx,ry,rz);
parent.add(particles2);

}


//圆角矩形
//生成路径并填充它是至关重要的难度
var roundedRectShape = new THREE。形状();

(函数roundedRect(ctx,x,y,width,height,radius){

ctx.moveTo(x,y + radius);
ctx。 lineTo(x,y + height - radius);
ctx.quadraticCurveTo(x,y + height,x + radius,y + height);
ctx.lineTo(x + width - radius,y +高度);
ctx.quadraticCurveTo(x +宽度,y +高度,x +宽度,y +高度 - 半径);
ctx.lineTo(x +宽度,y +半径);
ctx.quadraticCurveTo(x + width,y,x + width - radius,y);
ctx.lineTo(x + radius,y);
ctx.quadraticCurveTo(x,y,x,y + radius);

})(roundedRectShape,0,0,200,200,20);
path = roundedRectShape;

var extrudeSettings = {amount:20}; // bevelSegments:2,步骤:2,bevelSegments:5,bevelSize:8,bevelThickness:5
extrudeSettings.bevelEnabled = true;
extrudeSettings.bevelSegments = 2;
extrudeSettings.steps = 2;

addShape(roundedRectShape,extrudeSettings,0x000000,-150,150,0,0,0,0,1);

renderer = new THREE.WebGLRenderer({antialias:true});
renderer.setSize(window.innerWidth,window.innerHeight);

container.appendChild(renderer.domElement);

stats = new Stats();
stats.domElement.style.position ='绝对';
stats.domElement.style.top ='0px';
container.appendChild(stats.domElement);

document.addEventListener('mousedown',onDocumentMouseDown,false);
document.addEventListener('touchstart',onDocumentTouchStart,false);
document.addEventListener('touchmove',onDocumentTouchMove,false);

//

window.addEventListener('resize',onWindowResize,false);

}

函数onWindowResize(){

windowHalfX = window.innerWidth / 2;
windowHalfY = window.innerHeight / 2;

camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();

renderer.setSize(window.innerWidth,window.innerHeight);

}

//

函数onDocumentMouseDown(event){

event.preventDefault();

document.addEventListener('mousemove',onDocumentMouseMove,false);
document.addEventListener('mouseup',onDocumentMouseUp,false);
document.addEventListener('mouseout',onDocumentMouseOut,false);

mouseXOnMouseDown = event.clientX - windowHalfX;
targetRotationOnMouseDown = targetRotation;

}

函数onDocumentMouseMove(事件){

mouseX = event.clientX - windowHalfX;

targetRotation = targetRotationOnMouseDown +(mouseX - mouseXOnMouseDown)* 0.02;

}

函数onDocumentMouseUp(event){

document.removeEventListener('mousemove',onDocumentMouseMove,false);
document.removeEventListener('mouseup',onDocumentMouseUp,false);
document.removeEventListener('mouseout',onDocumentMouseOut,false);

}

函数onDocumentMouseOut(event){

document.removeEventListener('mousemove',onDocumentMouseMove,false);
document.removeEventListener('mouseup',onDocumentMouseUp,false);
document.removeEventListener('mouseout',onDocumentMouseOut,false);

}

函数onDocumentTouchStart(event){

if(event.touches.length == 1){

event.preventDefault();

mouseXOnMouseDown = event.touches [0] .pageX - windowHalfX;
targetRotationOnMouseDown = targetRotation;

}

}

函数onDocumentTouchMove(event){

if(event.touches.length == 1 ){

event.preventDefault();

mouseX = event.touches [0] .pageX - windowHalfX;
targetRotation = targetRotationOnMouseDown +(mouseX - mouseXOnMouseDown)* 0.05;

}

}

//

函数animate(){

requestAnimationFrame (动画);

render();
stats.update();

}

函数render(){
t =(t + s)%1.0; //增加t,同时将其保持在0.0和1.0
之间var p = path.getPoint(t); //指向t
var pn = path.getPoint((t + s)%1.0); //指向下一个t迭代

if(p!= null&& pn!= null){
//移动到当前位置
arrow.position.x = px;
arrow.position.y = p.y;
//根据下一个位置获取方向
arrow.rotation.z = Math.atan2(pn.y-p.y,pn.x-p.x);

}

parent.rotation.y + =(targetRotation - parent.rotation.y)* 0.05;
renderer.render(场景,相机);

}

< / script>

< / body>
< / html>

以为我会在此页面上直接添加一个可运行的代码段:



  var container; var camera,scene,renderer; var text,plane; var targetRotation = 0; var targetRotationOnMouseDown = 0; var mouseX = 0; var mouseXOnMouseDown = 0; var windowHalfX = window.innerWidth / 2; var windowHalfY = window.innerHeight / 2;在里面();动画(); var t = 0.0; //路径上的遍历var s = 0.001; //遍历var箭头的速度; //在路径var路径上移动/旋转的网格; //遍历函数的路径对象init(){container = document。 createElement('div'); document.body.appendChild(container); camera = new THREE.PerspectiveCamera(50,window.innerWidth / window.innerHeight,1,1000); camera.position.set(0,150,500); scene = new THREE.Scene(); parent = new THREE.Object3D(); parent.position.y = 50; scene.add(parent); arrows = []; for(var i = 0; i< 50; i ++){arrows [i] = new THREE.Mesh(new THREE.CubeGeometry(10,5,5),new THREE.MeshBasicMaterial({color:0x009900})); parent.add(箭头[I]); } //这对于视觉辅助有用,但不是关键函数addShape(shape,extrudeSettings,color,x,y,z,rx,ry,rz,s){var points = shape.createPointsGeometry(); var spacedPoints = shape.createSpacedPointsGeometry(50); //等距离采样点的透明线var line = new THREE.Line(spacedPoints,new THREE.LineBasicMaterial({color:color,opacity:0.2})); line.rotation.set(rx,ry,rz); parent.add(line); //等距离采样点var pgeo = spacedPoints.clone(); var particles2 = new THREE.ParticleSystem(pgeo,new THREE.ParticleBasicMaterial({color:color,size:2,opacity:0.5})); particles2.rotation.set(rx,ry,rz); parent.add(particles2); } //圆角矩形//生成路径并填充它是至关重要的难以改变的圆形RectShape = new THREE.Shape(); (函数roundedRect(ctx,x,y,width,height,radius){ctx.moveTo(x,y + radius); ctx.lineTo(x,y + height  -  radius); ctx.quadraticCurveTo(x,y + height) ,x +半径,y +高度); ctx.lineTo(x + width  -  radius,y + height); ctx.quadraticCurveTo(x + width,y + height,x + width,y + height  -  radius); ctx。 lineTo(x + width,y + radius); ctx.quadraticCurveTo(x + width,y,x + width  -  radius,y); ctx.lineTo(x + radius,y); ctx.quadraticCurveTo(x,y,x ,y + radius);})(roundedRectShape,0,0,200,200,20); path = roundedRectShape; var extrudeSettings = {amount:20}; // bevelSegments:2,步骤:2,bevelSegments:5,bevelSize:8,bevelThickness:5 extrudeSettings.bevelEnabled = true; extrudeSettings.bevelSegments = 2; extrudeSettings.steps = 2; addShape(roundedRectShape,extrudeSettings,0x000000,-150,150,0,0,0,0,1); renderer = new THREE.WebGLRenderer({antialias:true}); renderer.setSize(window.innerWidth,window.innerHeight); container.appendChild(renderer.domElement); document.addEventListener('mousedown',onDocumentMouseDown,false); document.addEventListener('touchstart',onDocumentTouchStart,false); document.addEventListener('touchmove',onDocumentTouchMove,false); // window.addEventListener('resize',onWindowResize,false);函数onWindowResize(){windowHalfX = window.innerWidth / 2; windowHalfY = window.innerHeight / 2; camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth,window.innerHeight); } // function onDocumentMouseDown(event){event.preventDefault(); document.addEventListener('mousemove',onDocumentMouseMove,false); document.addEventListener('mouseup',onDocumentMouseUp,false); document.addEventListener('mouseout',onDocumentMouseOut,false); mouseXOnMouseDown = event.clientX  -  windowHalfX; targetRotationOnMouseDown = targetRotation;函数onDocumentMouseMove(event){mouseX = event.clientX  -  windowHalfX; targetRotation = targetRotationOnMouseDown +(mouseX  -  mouseXOnMouseDown)* 0.02; function onDocumentMouseUp(event){document.removeEventListener('mousemove',onDocumentMouseMove,false); document.removeEventListener('mouseup',onDocumentMouseUp,false); document.removeEventListener('mouseout',onDocumentMouseOut,false);函数onDocumentMouseOut(event){document.removeEventListener('mousemove',onDocumentMouseMove,false); document.removeEventListener('mouseup',onDocumentMouseUp,false); document.removeEventListener('mouseout',onDocumentMouseOut,false); function onDocumentTouchStart(event){if(event.touches.length == 1){event.preventDefault(); mouseXOnMouseDown = event.touches [0] .pageX  -  windowHalfX; targetRotationOnMouseDown = targetRotation; function onDocumentTouchMove(event){if(event.touches.length == 1){event.preventDefault(); mouseX = event.touches [0] .pageX  -  windowHalfX; targetRotation = targetRotationOnMouseDown +(mouseX  -  mouseXOnMouseDown)* 0.05; }} // function animate(){requestAnimationFrame(animate);渲染();函数render(){t =(t + s)%1.0; //在保持介于0.0和1.0之间时增加t  - 可以将鼠标x位置/窗口宽度映射为乐趣:) for(var i = 0; i< 50; i ++){//对于每个框var ti =((i / 50.0)+ t)%1.0; //计算遍历,包括路径上每个框自己的偏移量var p = path.getPoint(ti); //指向t var pn = path.getPoint((ti + s)%1.0); //指向下一个t迭代if(p!= null&& pn!= null){//移动到当前位置箭头[我] .position.x = px;箭头[i] .position.y = p.y; //根据下一个位置箭头获取方向[i] .rotation.z = Math.atan2(pn.y-p.y,pn.x-p.x); parent.rotation.y + =(targetRotation  -  parent.rotation.y)* 0.05; renderer.render(场景,相机); }  

  body {font-family:Monospace; background-color:#f0f0f0;保证金:0px;溢出:隐藏; }  

 < script src =https:// cdnjs .cloudflare.com / AJAX /库/ three.js所/ R71 / three.min.js>< /脚本>  


I am in the process of creating a game using Three.JS and I have modeled and successfully imported a city created in Sketchup. I now need to dynamically add some "follow me" arrows (as per the yellow arrows in the mockup below). I believe I might need to use Three.CurvePath to achieve this but am not sure if this is the best approach - do I need to manually model the path and calculate the tangent for each of the arrow objects so they point naturally around corners (as per left turn in the mockup)?

Hope this makes sense!

解决方案

I might have a solution. Haven't used three.js in a while so not sure if this is the most elegant solution. I started with the Shapes Example since it shows:

  1. How to build a path procedurally (using commands that also handle curves)
  2. How to extract points from such a path

So I've split the problem into:

  1. Generating the path
  2. Traversing the path and interpolation (position and rotation)

Generating the path I've reused the rounded rectangle definition which looks similar to a part of your screenshot.

var roundedRectShape = new THREE.Shape();

                ( function roundedRect( ctx, x, y, width, height, radius ){

                    ctx.moveTo( x, y + radius );
                    ctx.lineTo( x, y + height - radius );
                    ctx.quadraticCurveTo( x, y + height, x + radius, y + height );
                    ctx.lineTo( x + width - radius, y + height) ;
                    ctx.quadraticCurveTo( x + width, y + height, x + width, y + height - radius );
                    ctx.lineTo( x + width, y + radius );
                    ctx.quadraticCurveTo( x + width, y, x + width - radius, y );
                    ctx.lineTo( x + radius, y );
                    ctx.quadraticCurveTo( x, y, x, y + radius );

                } )( roundedRectShape, 0, 0, 200, 200, 20 );

Your path might not be a rounded rectangle, but the available types of curve functions(quadraticCurveTo,bezierCurveTo,splineThru) are really useful.

Another idea that comes to mind is using a Ruby script to export path coordinates from Sketchup to three.js. Either you write that from scratch or use existing scripts. Here's one easily found on google.

Traversing the path

Luckily three.js already implements this through Path's getPoint(t) where t is a number from 0.0 to 1.0 representing the traversal on the path. So getting the position is trivial as getting the next interpolated position on the path. Then it's just a matter of using Math.atan2() to get the rotation:

t = (t + s)%1.0;//increment t while maintaining it between 0.0 and 1.0
                var p = path.getPoint(t);//point at t
                var pn = path.getPoint((t+s)%1.0);//point at next t iteration

                if(p != null && pn != null){
                    //move to current position
                    arrow.position.x = p.x;
                    arrow.position.y = p.y;
                    //get orientation based on next position
                    arrow.rotation.z = Math.atan2(pn.y-p.y,pn.x-p.x);

                }

In conclusion bellow is a basic example (using a cube instead of an arrow shape) to illustrate generating and traversing a path in three.js based on the shapes example:

<!DOCTYPE html>
<html lang="en">
    <head>
        <title>path interpolation</title>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
        <style>
            body {
                font-family: Monospace;
                background-color: #f0f0f0;
                margin: 0px;
                overflow: hidden;
            }
        </style>
    </head>
    <body>
        <canvas id="debug" style="position:absolute; left:100px"></canvas>

        <script src="../build/three.min.js"></script>

        <script src="js/libs/stats.min.js"></script>


        <script>

            var container, stats;

            var camera, scene, renderer;

            var text, plane;

            var targetRotation = 0;
            var targetRotationOnMouseDown = 0;

            var mouseX = 0;
            var mouseXOnMouseDown = 0;

            var windowHalfX = window.innerWidth / 2;
            var windowHalfY = window.innerHeight / 2;

            init();
            animate();

            var t = 0.0;//traversal on path
            var s = 0.001;//speed of traversal
            var arrow;//mesh to move/rotate on path
            var path;//Path object to traverse

            function init() {

                container = document.createElement( 'div' );
                document.body.appendChild( container );

                camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 1, 1000 );
                camera.position.set( 0, 150, 500 );

                scene = new THREE.Scene();

                parent = new THREE.Object3D();
                parent.position.y = 50;
                scene.add( parent );

                arrow = new THREE.Mesh( new THREE.CubeGeometry(20,10,10),new THREE.MeshBasicMaterial({color: 0x009900}));
                parent.add(arrow);
                //this is helpful as a visual aid but not crucial
                function addShape( shape, extrudeSettings, color, x, y, z, rx, ry, rz, s ) {

                    var points = shape.createPointsGeometry();
                    var spacedPoints = shape.createSpacedPointsGeometry( 50 );

                    // transparent line from equidistance sampled points

                    var line = new THREE.Line( spacedPoints, new THREE.LineBasicMaterial( { color: color, opacity: 0.2 } ) );
                    line.rotation.set( rx, ry, rz );
                    parent.add( line );

                    // equidistance sampled points

                    var pgeo = spacedPoints.clone();
                    var particles2 = new THREE.ParticleSystem( pgeo, new THREE.ParticleBasicMaterial( { color: color, size: 2, opacity: 0.5 } ) );
                    particles2.rotation.set( rx, ry, rz );
                    parent.add( particles2 );

                }


                // Rounded rectangle
                //generating the path and populating it is crucial tough
                var roundedRectShape = new THREE.Shape();

                ( function roundedRect( ctx, x, y, width, height, radius ){

                    ctx.moveTo( x, y + radius );
                    ctx.lineTo( x, y + height - radius );
                    ctx.quadraticCurveTo( x, y + height, x + radius, y + height );
                    ctx.lineTo( x + width - radius, y + height) ;
                    ctx.quadraticCurveTo( x + width, y + height, x + width, y + height - radius );
                    ctx.lineTo( x + width, y + radius );
                    ctx.quadraticCurveTo( x + width, y, x + width - radius, y );
                    ctx.lineTo( x + radius, y );
                    ctx.quadraticCurveTo( x, y, x, y + radius );

                } )( roundedRectShape, 0, 0, 200, 200, 20 );
                path = roundedRectShape;

                var extrudeSettings = { amount: 20 }; // bevelSegments: 2, steps: 2 , bevelSegments: 5, bevelSize: 8, bevelThickness:5
                extrudeSettings.bevelEnabled = true;
                extrudeSettings.bevelSegments = 2;
                extrudeSettings.steps = 2;

                addShape( roundedRectShape, extrudeSettings, 0x000000, -150, 150, 0, 0, 0, 0, 1 );

                renderer = new THREE.WebGLRenderer( { antialias: true } );
                renderer.setSize( window.innerWidth, window.innerHeight );

                container.appendChild( renderer.domElement );

                stats = new Stats();
                stats.domElement.style.position = 'absolute';
                stats.domElement.style.top = '0px';
                container.appendChild( stats.domElement );

                document.addEventListener( 'mousedown', onDocumentMouseDown, false );
                document.addEventListener( 'touchstart', onDocumentTouchStart, false );
                document.addEventListener( 'touchmove', onDocumentTouchMove, false );

                //

                window.addEventListener( 'resize', onWindowResize, false );

            }

            function onWindowResize() {

                windowHalfX = window.innerWidth / 2;
                windowHalfY = window.innerHeight / 2;

                camera.aspect = window.innerWidth / window.innerHeight;
                camera.updateProjectionMatrix();

                renderer.setSize( window.innerWidth, window.innerHeight );

            }

            //

            function onDocumentMouseDown( event ) {

                event.preventDefault();

                document.addEventListener( 'mousemove', onDocumentMouseMove, false );
                document.addEventListener( 'mouseup', onDocumentMouseUp, false );
                document.addEventListener( 'mouseout', onDocumentMouseOut, false );

                mouseXOnMouseDown = event.clientX - windowHalfX;
                targetRotationOnMouseDown = targetRotation;

            }

            function onDocumentMouseMove( event ) {

                mouseX = event.clientX - windowHalfX;

                targetRotation = targetRotationOnMouseDown + ( mouseX - mouseXOnMouseDown ) * 0.02;

            }

            function onDocumentMouseUp( event ) {

                document.removeEventListener( 'mousemove', onDocumentMouseMove, false );
                document.removeEventListener( 'mouseup', onDocumentMouseUp, false );
                document.removeEventListener( 'mouseout', onDocumentMouseOut, false );

            }

            function onDocumentMouseOut( event ) {

                document.removeEventListener( 'mousemove', onDocumentMouseMove, false );
                document.removeEventListener( 'mouseup', onDocumentMouseUp, false );
                document.removeEventListener( 'mouseout', onDocumentMouseOut, false );

            }

            function onDocumentTouchStart( event ) {

                if ( event.touches.length == 1 ) {

                    event.preventDefault();

                    mouseXOnMouseDown = event.touches[ 0 ].pageX - windowHalfX;
                    targetRotationOnMouseDown = targetRotation;

                }

            }

            function onDocumentTouchMove( event ) {

                if ( event.touches.length == 1 ) {

                    event.preventDefault();

                    mouseX = event.touches[ 0 ].pageX - windowHalfX;
                    targetRotation = targetRotationOnMouseDown + ( mouseX - mouseXOnMouseDown ) * 0.05;

                }

            }

            //

            function animate() {

                requestAnimationFrame( animate );

                render();
                stats.update();

            }

            function render() {
                t = (t + s)%1.0;//increment t while maintaining it between 0.0 and 1.0
                var p = path.getPoint(t);//point at t
                var pn = path.getPoint((t+s)%1.0);//point at next t iteration

                if(p != null && pn != null){
                    //move to current position
                    arrow.position.x = p.x;
                    arrow.position.y = p.y;
                    //get orientation based on next position
                    arrow.rotation.z = Math.atan2(pn.y-p.y,pn.x-p.x);

                }

                parent.rotation.y += ( targetRotation - parent.rotation.y ) * 0.05;
                renderer.render( scene, camera );

            }

        </script>

    </body>
</html>

Thought I'd add a runnable snippet straight on this page:

            var container;

            var camera, scene, renderer;

            var text, plane;

            var targetRotation = 0;
            var targetRotationOnMouseDown = 0;

            var mouseX = 0;
            var mouseXOnMouseDown = 0;

            var windowHalfX = window.innerWidth / 2;
            var windowHalfY = window.innerHeight / 2;

            init();
            animate();

            var t = 0.0;//traversal on path
            var s = 0.001;//speed of traversal
            var arrows;//mesh to move/rotate on path
            var path;//Path object to traverse

            function init() {

                container = document.createElement( 'div' );
                document.body.appendChild( container );

                camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 1, 1000 );
                camera.position.set( 0, 150, 500 );

                scene = new THREE.Scene();

                parent = new THREE.Object3D();
                parent.position.y = 50;
                scene.add( parent );
                
                arrows = [];
                for(var i = 0 ; i < 50; i++){
                arrows[i] = new THREE.Mesh( new THREE.CubeGeometry(10,5,5),new THREE.MeshBasicMaterial({color: 0x009900}));
                parent.add(arrows[i]);
                }
                //this is helpful as a visual aid but not crucial
                function addShape( shape, extrudeSettings, color, x, y, z, rx, ry, rz, s ) {

                    var points = shape.createPointsGeometry();
                    var spacedPoints = shape.createSpacedPointsGeometry( 50 );

                    // transparent line from equidistance sampled points

                    var line = new THREE.Line( spacedPoints, new THREE.LineBasicMaterial( { color: color, opacity: 0.2 } ) );
                    line.rotation.set( rx, ry, rz );
                    parent.add( line );

                    // equidistance sampled points

                    var pgeo = spacedPoints.clone();
                    var particles2 = new THREE.ParticleSystem( pgeo, new THREE.ParticleBasicMaterial( { color: color, size: 2, opacity: 0.5 } ) );
                    particles2.rotation.set( rx, ry, rz );
                    parent.add( particles2 );

                }


                // Rounded rectangle
                //generating the path and populating it is crucial tough
                var roundedRectShape = new THREE.Shape();

                ( function roundedRect( ctx, x, y, width, height, radius ){

                    ctx.moveTo( x, y + radius );
                    ctx.lineTo( x, y + height - radius );
                    ctx.quadraticCurveTo( x, y + height, x + radius, y + height );
                    ctx.lineTo( x + width - radius, y + height) ;
                    ctx.quadraticCurveTo( x + width, y + height, x + width, y + height - radius );
                    ctx.lineTo( x + width, y + radius );
                    ctx.quadraticCurveTo( x + width, y, x + width - radius, y );
                    ctx.lineTo( x + radius, y );
                    ctx.quadraticCurveTo( x, y, x, y + radius );

                } )( roundedRectShape, 0, 0, 200, 200, 20 );
                path = roundedRectShape;

                var extrudeSettings = { amount: 20 }; // bevelSegments: 2, steps: 2 , bevelSegments: 5, bevelSize: 8, bevelThickness:5
                extrudeSettings.bevelEnabled = true;
                extrudeSettings.bevelSegments = 2;
                extrudeSettings.steps = 2;

                addShape( roundedRectShape, extrudeSettings, 0x000000, -150, 150, 0, 0, 0, 0, 1 );

                renderer = new THREE.WebGLRenderer( { antialias: true } );
                renderer.setSize( window.innerWidth, window.innerHeight );

                container.appendChild( renderer.domElement );

               

                document.addEventListener( 'mousedown', onDocumentMouseDown, false );
                document.addEventListener( 'touchstart', onDocumentTouchStart, false );
                document.addEventListener( 'touchmove', onDocumentTouchMove, false );

                //

                window.addEventListener( 'resize', onWindowResize, false );

            }

            function onWindowResize() {

                windowHalfX = window.innerWidth / 2;
                windowHalfY = window.innerHeight / 2;

                camera.aspect = window.innerWidth / window.innerHeight;
                camera.updateProjectionMatrix();

                renderer.setSize( window.innerWidth, window.innerHeight );

            }

            //

            function onDocumentMouseDown( event ) {

                event.preventDefault();

                document.addEventListener( 'mousemove', onDocumentMouseMove, false );
                document.addEventListener( 'mouseup', onDocumentMouseUp, false );
                document.addEventListener( 'mouseout', onDocumentMouseOut, false );

                mouseXOnMouseDown = event.clientX - windowHalfX;
                targetRotationOnMouseDown = targetRotation;

            }

            function onDocumentMouseMove( event ) {

                mouseX = event.clientX - windowHalfX;

                targetRotation = targetRotationOnMouseDown + ( mouseX - mouseXOnMouseDown ) * 0.02;

            }

            function onDocumentMouseUp( event ) {

                document.removeEventListener( 'mousemove', onDocumentMouseMove, false );
                document.removeEventListener( 'mouseup', onDocumentMouseUp, false );
                document.removeEventListener( 'mouseout', onDocumentMouseOut, false );

            }

            function onDocumentMouseOut( event ) {

                document.removeEventListener( 'mousemove', onDocumentMouseMove, false );
                document.removeEventListener( 'mouseup', onDocumentMouseUp, false );
                document.removeEventListener( 'mouseout', onDocumentMouseOut, false );

            }

            function onDocumentTouchStart( event ) {

                if ( event.touches.length == 1 ) {

                    event.preventDefault();

                    mouseXOnMouseDown = event.touches[ 0 ].pageX - windowHalfX;
                    targetRotationOnMouseDown = targetRotation;

                }

            }

            function onDocumentTouchMove( event ) {

                if ( event.touches.length == 1 ) {

                    event.preventDefault();

                    mouseX = event.touches[ 0 ].pageX - windowHalfX;
                    targetRotation = targetRotationOnMouseDown + ( mouseX - mouseXOnMouseDown ) * 0.05;

                }

            }

            //

            function animate() {

                requestAnimationFrame( animate );

                render();
 
            }

            function render() {
                t = (t + s)%1.0;//increment t while maintaining it between 0.0 and 1.0 - could map mouse x position/window width for fun :)
				for(var i = 0 ; i < 50; i++){//for each box
					var ti = ((i/50.0)+t)%1.0;//compute the traversval including each box's own offset on the path
					
					var p = path.getPoint(ti);//point at t
					var pn = path.getPoint((ti+s)%1.0);//point at next t iteration
				
					if(p != null && pn != null){
						//move to current position
						arrows[i].position.x = p.x;
						arrows[i].position.y = p.y;
						//get orientation based on next position
						arrows[i].rotation.z = Math.atan2(pn.y-p.y,pn.x-p.x);
					
					}
				}
                parent.rotation.y += ( targetRotation - parent.rotation.y ) * 0.05;
                renderer.render( scene, camera );

            }

            body {
                font-family: Monospace;
                background-color: #f0f0f0;
                margin: 0px;
                overflow: hidden;
            }

<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r71/three.min.js"></script>

这篇关于Three.CurvePath和自定义标记的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆