【发布时间】:2021-03-15 10:51:03
【问题描述】:
我想制作一个“粗箭头”网格,即像 standard Arrow Helper 这样的箭头,但轴由 cylinder 而不是 line 制成。
tldr; 不要复制 Arrow Helper 的设计;请参阅问题末尾的结语部分。
所以我复制并修改了代码以满足我的需要(免除了构造函数和方法)并进行了更改,现在它可以正常工作了:-
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
//= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
//... START of ARROWMAKER SET of FUNCTIONS
// adapted from https://github.com/mrdoob/three.js/blob/master/src/helpers/ArrowHelper.js
//====================================
function F_Arrow_Fat_noDoesLookAt_Make ( dir, origin, length, shaftBaseWidth, shaftTopWidth, color, headLength, headBaseWidth, headTopWidth )
{
//... dir is assumed to be normalized
var thisArrow = new THREE.Object3D();////SW
if ( dir === undefined ) dir = new THREE.Vector3( 0, 0, 1 );
if ( origin === undefined ) origin = new THREE.Vector3( 0, 0, 0 );
if ( length === undefined ) length = 1;
if ( shaftBaseWidth === undefined ) shaftBaseWidth = 0.02 * length;
if ( shaftTopWidth === undefined ) shaftTopWidth = 0.02 * length;
if ( color === undefined ) color = 0xffff00;
if ( headLength === undefined ) headLength = 0.2 * length;
if ( headBaseWidth === undefined ) headBaseWidth = 0.4 * headLength;
if ( headTopWidth === undefined ) headTopWidth = 0.2 * headLength;//... 0.0 for a point.
/* CylinderBufferGeometry parameters from:-
// https://threejs.org/docs/index.html#api/en/geometries/CylinderBufferGeometry
* radiusTop — Radius of the cylinder at the top. Default is 1.
* radiusBottom — Radius of the cylinder at the bottom. Default is 1.
* height — Height of the cylinder. Default is 1.
* radialSegments — Number of segmented faces around the circumference of the cylinder. Default is 8
* heightSegments — Number of rows of faces along the height of the cylinder. Default is 1.
* openEnded — A Boolean indicating whether the ends of the cylinder are open or capped. Default is false, meaning capped.
* thetaStart — Start angle for first segment, default = 0 (three o'clock position).
* thetaLength — The central angle, often called theta, of the circular sector. The default is 2*Pi, which makes for a complete cylinder.
*/
//var shaftGeometry = new THREE.CylinderBufferGeometry( 0.0, 0.5, 1, 8, 1 );//for strongly tapering, pointed shaft
var shaftGeometry = new THREE.CylinderBufferGeometry( 0.1, 0.1, 1, 8, 1 );//shaft is cylindrical
//shaftGeometry.translate( 0, - 0.5, 0 );
shaftGeometry.translate( 0, + 0.5, 0 );
//... for partial doesLookAt capability
//shaftGeometry.applyMatrix( new THREE.Matrix4().makeRotationX( Math.PI / 2 ) );
var headGeometry = new THREE.CylinderBufferGeometry( 0, 0.5, 1, 5, 1 ); //for strongly tapering, pointed head
headGeometry.translate( 0, - 0.5, 0 );
//... for partial doesLookAt capability
//headGeometry.applyMatrix( new THREE.Matrix4().makeRotationX( Math.PI / 2 ) );
thisArrow.position.copy( origin );
/*thisArrow.line = new Line( _lineGeometry, new LineBasicMaterial( { color: color, toneMapped: false } ) );
thisArrow.line.matrixAutoUpdate = false;
thisArrow.add( thisArrow.line ); */
thisArrow.shaft = new THREE.Mesh( shaftGeometry, new THREE.MeshLambertMaterial( { color: color } ) );
thisArrow.shaft.matrixAutoUpdate = false;
thisArrow.add( thisArrow.shaft );
thisArrow.head = new THREE.Mesh( headGeometry, new THREE.MeshLambertMaterial( { color: color } ) );
thisArrow.head.matrixAutoUpdate = false;
thisArrow.add( thisArrow.head );
//thisArrow.setDirection( dir );
//thisArrow.setLength( length, headLength, headTopWidth );
var arkle = new THREE.AxesHelper (2 * length);
thisArrow.add (arkle);
F_Arrow_Fat_noDoesLookAt_setDirection( thisArrow, dir ) ;////SW
F_Arrow_Fat_noDoesLookAt_setLength ( thisArrow, length, headLength, headBaseWidth ) ;////SW
F_Arrow_Fat_noDoesLookAt_setColor ( thisArrow, color ) ;////SW
scene.add ( thisArrow );
//... this screws up for the F_Arrow_Fat_noDoesLookAt kind of Arrow
//thisArrow.lookAt(0,0,0);//...makes the arrow's blue Z axis lookAt Point(x,y,z).
}
//... EOFn F_Arrow_Fat_noDoesLookAt_Make().
//=============================================
function F_Arrow_Fat_noDoesLookAt_setDirection( thisArrow, dir )
{
// dir is assumed to be normalized
if ( dir.y > 0.99999 )
{
thisArrow.quaternion.set( 0, 0, 0, 1 );
} else if ( dir.y < - 0.99999 )
{
thisArrow.quaternion.set( 1, 0, 0, 0 );
} else
{
const _axis = /*@__PURE__*/ new THREE.Vector3();
_axis.set( dir.z, 0, - dir.x ).normalize();
const radians = Math.acos( dir.y );
thisArrow.quaternion.setFromAxisAngle( _axis, radians );
}
}
//... EOFn F_Arrow_Fat_noDoesLookAt_setDirection().
//=========================================
function F_Arrow_Fat_noDoesLookAt_setLength( thisArrow, length, headLength, headBaseWidth )
{
if ( headLength === undefined ) headLength = 0.2 * length;
if ( headBaseWidth === undefined ) headBaseWidth = 0.2 * headLength;
thisArrow.shaft.scale.set( 1, Math.max( 0.0001, length - headLength ), 1 ); // see #17458
//x&z the same, y as per length-headLength
//thisArrow.shaft.position.y = length;//SW ???????
thisArrow.shaft.updateMatrix();
thisArrow.head.scale.set( headBaseWidth, headLength, headBaseWidth ); //x&z the same, y as per length
thisArrow.head.position.y = length;
thisArrow.head.updateMatrix();
}
//...EOFn F_Arrow_Fat_noDoesLookAt_setLength().
//========================================
function F_Arrow_Fat_noDoesLookAt_setColor( thisArrow, color )
{
thisArrow.shaft.material.color.set( color );
thisArrow.head.material.color.set( color );
}
//...EOFn F_Arrow_Fat_noDoesLookAt_setColor().
//... END of ARROWMAKER SET of FUNCTIONS
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
//= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
这适用于固定方向的箭头,可以在构建时提供箭头方向。
但现在我需要随时间改变箭头方向(用于跟踪移动目标)。目前 Object3D.lookAt() 函数还不够,因为箭头指向 Object3D 的 y 轴,而 lookAt() 将 Object3D 的 z 轴定向以查看给定的目标位置。
通过实验,我已经通过使用:-
geometry.applyMatrix( new THREE.Matrix4().makeRotationX( Math.PI / 2 ) );
关于轴和头部的几何形状(这两行在上面的代码摘录中被注释掉了)。这似乎使圆柱体网格指向正确的方向。但问题是网眼形状错误,头部网眼偏离轴网。
通过反复试验,我也许可以调整代码以使箭头适用于我目前的示例。但是(鉴于我对四元数的理解很薄弱)我不相信它会 (a) 足够通用以适用于所有情况,或者 (b) 足以应对 THREE.js 的演变。
因此,对于如何实现此“粗箭头”的 lookAt() 功能的任何解决方案/建议,我将不胜感激。
结语
我的主要观点是不遵循辅助箭头的设计。
正如 TheJim01 和 somehere 的回答所表明的,使用 Object3D.add()“嵌套”函数有一种更简单的方法。
例如:-
(1) 创建两个圆柱网格(用于箭头轴和箭头),默认指向 Y 方向;使几何长度 =1.0 以帮助将来重新缩放。
(2) 将网格添加到父 Object3D 对象。
(3) 使用parent.rotateX(Math.PI/2) 将父级绕X 轴旋转+90 度。
(4) 将父对象添加到祖父对象。
(5) 随后使用grandparent.lookAt(target_point_as_World_position_Vec3_or_x_y_z)。
注意如果父母或祖父母的缩放比例不是(n,n,n),则lookAt() 将无法正常工作。
父对象和祖父对象类型可以是普通的THREE.Object3D、THREE.Group 或THREE.Mesh(如果需要,例如通过设置小尺寸或.visibility=false 使其不可见)
Arrow Helper 可以动态使用,但前提是在使用 lookAt() 之前将内部方向设置为 (0,0,1)。
【问题讨论】:
-
等一下,您的主要问题是您制作了一个向上的箭头并在 z 方向上查看点?在这种情况下,您将竭尽努力解决只需要重新调整心态的问题。只要您设计了一个指向该方向的箭头,lookAt 就会正确地指向该箭头。最重要的是,您可以使用
ArrowHelper来查看如何 这可以构造和工作。该助手还使用lookAt没有任何问题。 -
@somethinghere - 请参阅我添加到问题中的结语。感谢您的意见!
标签: javascript three.js