Index: source/simulation2/components/CCmpVisualActor.cpp =================================================================== --- source/simulation2/components/CCmpVisualActor.cpp +++ source/simulation2/components/CCmpVisualActor.cpp @@ -39,6 +39,8 @@ #include "graphics/Decal.h" #include "graphics/Frustum.h" #include "graphics/Model.h" +#include "graphics/ModelDef.h" +#include "graphics/SkeletonAnimDef.h" #include "graphics/ObjectBase.h" #include "graphics/ObjectEntry.h" #include "graphics/Unit.h" @@ -86,6 +88,11 @@ fixed m_AnimSyncOffsetTime; std::map m_VariantSelections; + /** + * Can be used to retrieve special locations given by the artists + * to place actors with precision on this actor. + */ + std::vector m_PropPointsPositions; u32 m_Seed; // seed used for random variations @@ -448,6 +455,8 @@ return m_AnimName; } + virtual std::vector GetPropPoints() const; + virtual void SelectAnimation(const std::string& name, bool once = false, fixed speed = fixed::FromInt(1)) { m_AnimName = name; @@ -467,7 +476,7 @@ if (!m_Unit || !m_Unit->GetAnimation() || !m_Unit->GetID()) return; - m_Unit->GetAnimation()->SetAnimationState(m_AnimName, m_AnimOnce, m_AnimSpeed.ToFloat(), m_AnimDesync.ToFloat(), m_SoundGroup.c_str()); + m_Unit->GetAnimation()->SetAnimationState(m_AnimName, m_AnimOnce, m_AnimSpeed.ToFloat(), m_AnimDesync.ToFloat(), m_SoundGroup.c_str()); } virtual void SelectMovementAnimation(const std::string& name, fixed speed) @@ -651,6 +660,56 @@ // the model is now responsible for cleaning up the descriptor if (m_ShapeDescriptor != nullptr) m_Unit->GetModel().SetCustomSelectionShape(m_ShapeDescriptor); + + if (!m_Unit || !m_Unit->GetModel().ToCModel()) + return; + + + std::vector& props = m_Unit->GetModel().ToCModel()->GetModelDef()->m_PropPoints; + CBoneState* bones = m_Unit->GetModel().ToCModel()->GetModelDef()->GetBones(); + m_PropPointsPositions.reserve(props.size()); + for (const SPropPoint& prop : props) + { + JsProp jsProp; + jsProp.m_Name = prop.m_Name; + CQuaternion rotation = prop.m_Rotation; + CVector3D eulerRotation = rotation.ToEulerAngles(); + + // See Modeldef.h + if (prop.m_BoneIndex != 0XFF) + { + CVector3D bonePos = bones[prop.m_BoneIndex].m_Translation; + CQuaternion boneRot = bones[prop.m_BoneIndex].m_Rotation; + CVector3D boneRotEuler = boneRot.ToEulerAngles(); + + jsProp.m_Position = CFixedVector3D( + fixed::FromFloat(bonePos.X + prop.m_Position.X), + fixed::FromFloat(bonePos.Y + prop.m_Position.Y), + fixed::FromFloat(bonePos.Z + prop.m_Position.Z) + ); + jsProp.m_Rotation = CFixedVector3D( + fixed::FromFloat(eulerRotation.X + boneRotEuler.X), + fixed::FromFloat(eulerRotation.Y + boneRotEuler.Y), + fixed::FromFloat(eulerRotation.Z + boneRotEuler.Z) + ); + } + else + { + jsProp.m_Position = CFixedVector3D( + fixed::FromFloat(prop.m_Position.X), + fixed::FromFloat(prop.m_Position.Y), + fixed::FromFloat(prop.m_Position.Z) + ); + + jsProp.m_Rotation = CFixedVector3D( + fixed::FromFloat(eulerRotation.X), + fixed::FromFloat(eulerRotation.Y), + fixed::FromFloat(eulerRotation.Z) + ); + } + + m_PropPointsPositions.emplace_back(jsProp); + } } void CCmpVisualActor::InitSelectionShapeDescriptor(const CParamNode& paramNode) @@ -775,3 +834,8 @@ if (!m_AnimSyncOffsetTime.IsZero()) m_Unit->GetAnimation()->SetAnimationSyncOffset(m_AnimSyncOffsetTime.ToFloat()); } + +std::vector CCmpVisualActor::GetPropPoints() const +{ + return m_PropPointsPositions; +} Index: source/simulation2/components/ICmpVisual.h =================================================================== --- source/simulation2/components/ICmpVisual.h +++ source/simulation2/components/ICmpVisual.h @@ -27,8 +27,20 @@ #include "maths/FixedVector3D.h" #include "lib/file/vfs/vfs_path.h" +#include +#include +#include + class CUnit; + +struct JsProp +{ + CStr m_Name; + CFixedVector3D m_Position; + CFixedVector3D m_Rotation; +}; + /** * The visual representation of an entity (typically an actor). */ @@ -74,6 +86,13 @@ */ virtual CFixedVector3D GetProjectileLaunchPoint() const = 0; + /** + * Return all the prop points an entity currently have with their location + * Return type is CFixedVector3D because it is exposed to the JS interface. + * Returns (0,0,0) if no point can be found. + */ + virtual std::vector GetPropPoints() const = 0; + /** * Returns the underlying unit of this visual actor. May return NULL to indicate that no unit exists (e.g. may happen if the * game is started without graphics rendering). Index: source/simulation2/components/ICmpVisual.cpp =================================================================== --- source/simulation2/components/ICmpVisual.cpp +++ source/simulation2/components/ICmpVisual.cpp @@ -26,6 +26,7 @@ DEFINE_INTERFACE_METHOD_CONST_0("GetAnimationName", std::string, ICmpVisual, GetAnimationName) DEFINE_INTERFACE_METHOD_CONST_0("GetProjectileActor", std::wstring, ICmpVisual, GetProjectileActor) DEFINE_INTERFACE_METHOD_CONST_0("GetProjectileLaunchPoint", CFixedVector3D, ICmpVisual, GetProjectileLaunchPoint) +DEFINE_INTERFACE_METHOD_CONST_0("GetPropPoints", std::vector, ICmpVisual, GetPropPoints) DEFINE_INTERFACE_METHOD_3("SelectAnimation", void, ICmpVisual, SelectAnimation, std::string, bool, fixed) DEFINE_INTERFACE_METHOD_1("SetAnimationSyncRepeat", void, ICmpVisual, SetAnimationSyncRepeat, fixed) DEFINE_INTERFACE_METHOD_1("SetAnimationSyncOffset", void, ICmpVisual, SetAnimationSyncOffset, fixed) Index: source/simulation2/scripting/EngineScriptConversions.cpp =================================================================== --- source/simulation2/scripting/EngineScriptConversions.cpp +++ source/simulation2/scripting/EngineScriptConversions.cpp @@ -28,6 +28,7 @@ #include "ps/utf16string.h" #include "simulation2/helpers/CinemaPath.h" #include "simulation2/helpers/Grid.h" +#include "simulation2/components/ICmpVisual.h" #include "simulation2/system/IComponent.h" #include "simulation2/system/ParamNode.h" @@ -339,8 +340,54 @@ return true; } +template<> void ScriptInterface::ToJSVal(JSContext* cx, JS::MutableHandleValue ret, const JsProp& val) +{ + JSAutoRequest rq(cx); + JS::RootedObject obj(cx, JS_NewPlainObject(cx)); + + JS::RootedValue name(cx); + JS::RootedValue position(cx); + JS::RootedValue rotation(cx); + ToJSVal(cx, &name, val.m_Name); + ToJSVal(cx, &position, val.m_Position); + ToJSVal(cx, &rotation, val.m_Rotation); + + ScriptInterface::CreateObject( + cx, + ret, + "name", name, + "rotation", rotation, + "position", position); +} + + +template<> bool ScriptInterface::FromJSVal(JSContext* cx, JS::HandleValue v, JsProp& out) +{ + JSAutoRequest rq(cx); + if (!v.isObject()) + return false; // TODO: report type error + + JS::RootedObject obj(cx, &v.toObject()); + JS::RootedValue name(cx); + JS::RootedValue position(cx); + JS::RootedValue rotation(cx); + + if (!JS_GetProperty(cx, obj, "name", &name)) return false; // TODO: report type errors + if (!FromJSVal(cx, name, out.m_Name)) return false; + + if (!JS_GetProperty(cx, obj, "position", &position)) return false; + if (!FromJSVal(cx, position, out.m_Position)) return false; + + if (!JS_GetProperty(cx, obj, "rotation", &rotation)) return false; + if (!FromJSVal(cx, rotation, out.m_Rotation)) return false; + + return true; +} + + // define vectors JSVAL_VECTOR(CFixedVector2D) +JSVAL_VECTOR(JsProp) #undef FAIL #undef FAIL_VOID