/* Copyright 2003-2007 ROBLOX Corporation, All Rights Reserved */ #include "stdafx.h" #if 1 // disable until we are ready for new joint schema #include "V8DataModel/Attachment.h" #include "V8DataModel/PartInstance.h" #include "V8DataModel/Surface.h" #include "V8DataModel/Workspace.h" #include "AppDraw/Draw.h" #include "AppDraw/DrawAdorn.h" #include "GfxBase/Adorn.h" #include "V8World/Primitive.h" #include "Util/Color.h" // #define ENABLE_AXES_API namespace RBX { using namespace Reflection; const char *const sAttachment = "Attachment"; REFLECTION_BEGIN() // Streaming Reflection::PropDescriptor Attachment::prop_Frame("CFrame",category_Data, &Attachment::getFrameInPart, &Attachment::setFrameInPart, PropertyDescriptor::STANDARD); // UI static const PropDescriptor prop_Position("Position", category_Data, &Attachment::getPivotInPart, &Attachment::setPivotInPart, PropertyDescriptor::UI); static const PropDescriptor prop_Rotation("Rotation", category_Data, &Attachment::getEulerAnglesInPart, &Attachment::setEulerAnglesInPart, PropertyDescriptor::UI); static const PropDescriptor prop_WorldPosition("WorldPosition", "Derived Data", &Attachment::getPivotInWorld, NULL, PropertyDescriptor::UI); static const PropDescriptor prop_WorldRotation("WorldRotation", "Derived Data", &Attachment::getEulerAnglesInWorld, NULL, PropertyDescriptor::UI); #ifdef ENABLE_AXES_API static const PropDescriptor prop_Axis("Axis", "Derived Data", &Attachment::getAxisInPart, NULL, PropertyDescriptor::UI); static const PropDescriptor prop_SecondaryAxis("SecondaryAxis", "Derived Data", &Attachment::getSecondaryAxisInPart, NULL, PropertyDescriptor::UI); static const PropDescriptor prop_WorldAxis("WorldAxis", "Derived Data", &Attachment::getAxisInWorld, NULL, PropertyDescriptor::UI); static const PropDescriptor prop_WorldSecondaryAxis("WorldSecondaryAxis", "Derived Data", &Attachment::getSecondaryAxisInWorld, NULL, PropertyDescriptor::UI); static BoundFuncDesc func_setAxes(&Attachment::setAxes, "SetAxes", "axis0", "axis1", Security::None); static BoundFuncDesc func_setAxis(&Attachment::setAxisInPart, "SetAxis", "axis", Security::None); #endif #ifdef RBX_ATTACHMENT_LOCKING const PropDescriptor Attachment::prop_Locked("Locked", category_Behavior, &Attachment::getLocked, &Attachment::setLocked, PropertyDescriptor::UI); #endif REFLECTION_END() float Attachment::toolAdornHandleRadius = 0.2f; float Attachment::toolAdornMajorAxisSize = 0.4f; float Attachment::toolAdornMajorAxisRadius = 0.07f; float Attachment::toolAdornMinorAxisSize = 0.3f; float Attachment::toolAdornMinorAxisRadius = 0.04f; float Attachment::adornRadius = 0.2f; Attachment::Attachment() : visible(false) , locked(false) , axisDirectionInPart(1, 0, 0) , secondaryAxisDirectionInPart(0, 1, 0) , pivotPositionInPart(1, 0, 0) { setName("Attachment"); } void Attachment::setVisible(bool value) { if (value != visible) { visible = value; } } Vector3 Attachment::getEulerAnglesInPart() const { Matrix3 o = getOrientationInPart(); Vector3 r; o.toEulerAnglesXYZ(r.x,r.y,r.z); return Vector3( Math::radiansToDegrees(r.x), Math::radiansToDegrees(r.y), Math::radiansToDegrees(r.z) ); } Vector3 Attachment::getEulerAnglesInWorld() const { Matrix3 o = getOrientationInWorld(); Vector3 r; o.toEulerAnglesXYZ(r.x,r.y,r.z); return Vector3( Math::radiansToDegrees(r.x), Math::radiansToDegrees(r.y), Math::radiansToDegrees(r.z) ); } void Attachment::setEulerAnglesInPart( const Vector3& v ) { Matrix3 o = Matrix3::fromEulerAnglesXYZ(Math::degreesToRadians(v.x),Math::degreesToRadians(v.y),Math::degreesToRadians(v.z)); setOrientationInPartInternal( o ); raiseChanged(prop_Frame); } void Attachment::setOrientationInPartInternal( const Matrix3& o ) { setAxisInPartInternal( o.column(0) ); setSecondaryAxisInPartInternal( o.column(1) ); raiseChanged(prop_Rotation); raiseChanged(prop_WorldRotation); } Matrix3 Attachment::getOrientationInPart( ) const { Matrix3 o; o.setColumn( 0, axisDirectionInPart ); o.setColumn( 1, secondaryAxisDirectionInPart ); o.setColumn( 2, o.column(0).cross( o.column(1) ) ); return o; } Matrix3 Attachment::getOrientationInWorld( ) const { Matrix3 o; CoordinateFrame partCF = getParentFrame(); o.setColumn( 0, partCF.vectorToWorldSpace( axisDirectionInPart ) ); o.setColumn( 1, partCF.vectorToWorldSpace( secondaryAxisDirectionInPart ) ); o.setColumn( 2, o.column(0).cross( o.column(1) ) ); return o; } void Attachment::setPivotInPart( const Vector3& pivot ) { pivotPositionInPart = pivot; raiseChanged(prop_Position); raiseChanged(prop_WorldPosition); raiseChanged(prop_Frame); } Vector3 Attachment::getPivotInPart() const { return pivotPositionInPart; } Vector3 Attachment::getPivotInWorld(void) const { return getParentFrame().pointToWorldSpace( pivotPositionInPart ); } void Attachment::setAxisInPartInternal( const Vector3& _axis ) { if( _axis.magnitude() < 0.0001f ) { return; } axisDirectionInPart = _axis; axisDirectionInPart.unitize(); #ifdef ENABLE_AXES_API raiseChanged( prop_Axis ); raiseChanged( prop_WorldAxis ); #endif // This will orthonormalize the secondary axis to the axis setSecondaryAxisInPartInternal(secondaryAxisDirectionInPart); } void Attachment::setAxisInPart( Vector3 _axis ) { setAxisInPartInternal( _axis ); raiseChanged( prop_Rotation ); raiseChanged( prop_WorldRotation ); raiseChanged( prop_Frame ); } Vector3 Attachment::getAxisInPart( void ) const { return axisDirectionInPart; } Vector3 Attachment::getAxisInWorld(void) const { return getParentFrame().vectorToWorldSpace( axisDirectionInPart ); } Vector3 Attachment::getSecondaryAxisInWorld(void) const { return getParentFrame().vectorToWorldSpace( secondaryAxisDirectionInPart ); } Vector3 Attachment::getSecondaryAxisInPart( void ) const { return secondaryAxisDirectionInPart; } void Attachment::setSecondaryAxisInPartInternal( const Vector3& _axis2 ) { if( _axis2.magnitude() < 0.00001f ) { return; } Vector3 axis2 = _axis2; Vector3 axis; axis = axisDirectionInPart; axis2.unitize(); if( fabsf( axis2.dot( axis ) ) < 0.999f ) { // The input secondary axis is not completely parallel to the axis, we can project axis2 = axis2 - axis2.dot( axis ) * axis; } else { // The secondary axis is parallel with the axis, we need to make a choice if( fabsf( axis.dot( Vector3::unitY() ) ) < sqrtf(2.0f)*0.5f ) { // If the axis makes more than 45deg angle with Y axis, project the Y axis on the orthogonal plane of the axis Vector3 t = Vector3::unitY().cross( axis ); axis2 = axis.cross( t ); } else { if( fabsf( axis.x ) + 0.1 > fabsf( axis.z ) ) { Vector3 t = Vector3::unitZ().cross( axis ); axis2 = axis.cross( t ); } else { Vector3 t = Vector3::unitX().cross( axis ); axis2 = axis.cross( t ); } } } axis2.unitize(); RBXASSERT( fabsf( axis2.dot(axis) ) < 0.00001f ); secondaryAxisDirectionInPart = axis2; #ifdef ENABLE_AXES_API raiseChanged( prop_SecondaryAxis ); raiseChanged( prop_WorldSecondaryAxis ); #endif } void Attachment::setAxes( Vector3 axis, Vector3 axis2 ) { setAxisInPartInternal( axis ); setSecondaryAxisInPartInternal( axis2 ); raiseChanged( prop_Rotation ); raiseChanged( prop_WorldRotation ); raiseChanged( prop_Frame ); } CoordinateFrame Attachment::getFrameInPart() const { CoordinateFrame cf; cf.translation = getPivotInPart(); cf.rotation = getOrientationInPart(); return cf; } void Attachment::setFrameInPart( const CoordinateFrame& frame ) { setOrientationInPartInternal( frame.rotation ); setPivotInPart( frame.translation ); raiseChanged( prop_Frame ); } CoordinateFrame Attachment::getFrameInWorld() const { CoordinateFrame cf; cf.translation = getPivotInWorld(); cf.rotation = getOrientationInWorld(); return cf; } CoordinateFrame Attachment::getParentFrame() const { if( !locked ) { const PartInstance* part = Instance::fastDynamicCast(getParent()); if( part ) { return part->getCoordinateFrame(); } } return CoordinateFrame(); } void Attachment::setLocked( bool value ) { if( !locked && value ) { Vector3 pivotInWorld = getPivotInWorld(); Vector3 axisInWorld = getAxisInWorld(); Vector3 secondaryAxisInWorld = getSecondaryAxisInWorld(); locked = true; pivotPositionInPart = pivotInWorld; axisDirectionInPart = axisInWorld; secondaryAxisDirectionInPart = secondaryAxisInWorld; #ifdef RBX_ATTACHMENT_LOCKING raiseChanged( prop_Locked ); #endif raiseChanged(prop_Position); raiseChanged(prop_WorldPosition); raiseChanged(prop_Rotation); raiseChanged(prop_WorldRotation); raiseChanged(prop_Frame); } if( locked && !value ) { Vector3 pivotInPart = getPivotInPart(); Vector3 axisInPart = getAxisInPart(); Vector3 secondaryAxisInPart = getSecondaryAxisInPart(); locked = false; CoordinateFrame partFrame = getParentFrame(); pivotPositionInPart = partFrame.pointToObjectSpace( pivotInPart ); axisDirectionInPart = partFrame.vectorToObjectSpace( axisInPart ); secondaryAxisDirectionInPart = partFrame.vectorToObjectSpace( secondaryAxisInPart ); #ifdef RBX_ATTACHMENT_LOCKING raiseChanged( prop_Locked ); #endif raiseChanged(prop_Position); raiseChanged(prop_WorldPosition); raiseChanged(prop_Rotation); raiseChanged(prop_WorldRotation); raiseChanged( prop_Frame ); } } void Attachment::render3dAdorn(Adorn* adorn) { if( !visible ) return; adorn->setMaterial( Adorn::Material_Default ); adorn->setObjectToWorldMatrix( getFrameInWorld() ); Sphere sphere; sphere.radius = adornRadius; adorn->sphere(sphere, Color3::green()); } static void renderToolAdornAxes( Adorn* adorn, const CoordinateFrame& cf ) { { CoordinateFrame cf1 = cf; cf1.translation += 0.5f * Attachment::toolAdornMajorAxisSize * cf.rotation.column(0); DrawAdorn::cylinder( adorn, cf1, 0, Attachment::toolAdornMajorAxisSize, Attachment::toolAdornMajorAxisRadius, Color3::yellow() ); } { CoordinateFrame cf1 = cf; cf1.translation += 0.5f * Attachment::toolAdornMinorAxisSize * cf.rotation.column(1); DrawAdorn::cylinder( adorn, cf1, 1, Attachment::toolAdornMinorAxisSize, Attachment::toolAdornMinorAxisRadius, Color3::red() ); } } void Attachment::render3dToolAdorn(Adorn* adorn, Attachment::SelectState selectState) { float alpha = 1.0f; static float dimming = 0.7f; float intensity = dimming; Color3 color = ( selectState & SelectState_Paired ) ? Color3::green() : Color3::orange(); adorn->setMaterial( Adorn::Material_SelfLit ); if( selectState & SelectState_Normal ) { intensity = 1.0f; } if( selectState & SelectState_Hovered ) { intensity = 1.0f; adorn->setMaterial( Adorn::Material_SelfLitHighlight ); } if( selectState & SelectState_Hidden ) { alpha = 0.1f; } if( ( selectState & SelectState_Normal ) || ( selectState & SelectState_Hovered ) ) { renderToolAdornAxes( adorn, getFrameInWorld() ); } adorn->setObjectToWorldMatrix( getFrameInWorld() ); Sphere sphere; sphere.radius = toolAdornHandleRadius; adorn->sphere( sphere, Color4( intensity * color, alpha ) ); } float Attachment::intersectAdornWithRay( const RbxRay& r ) { Sphere s; s.center = getPivotInWorld(); s.radius = toolAdornHandleRadius; return r.intersectionTime( s ); } void Attachment::verifySetParent(const Instance* instance) const { Super::verifySetParent(instance); if( instance != NULL && instance->fastDynamicCast< PartInstance >() == NULL && instance->fastDynamicCast< Workspace >() == NULL ) { throw RBX::runtime_error("Attachments can only be parented to parts or the workspace"); } } void Attachment::verifyAddChild(const Instance* newChild) const { Super::verifyAddChild( newChild ); if( newChild != NULL /*&& newChild->fastDynamicCast< Constraints::Constraint >() == NULL*/ ) { throw RBX::runtime_error("Attachments can't have children."); } } } // namespace #endif