Added automatic bounding box expansion, fixed several bugs in the aabb calculations and removed node OBBs (#2871)

Merge pull request #2871 from aws-lumberyard-dev/jillich/EmfxAabbImprovements
This commit is contained in:
Benjamin Jillich
2021-08-09 01:19:42 -07:00
committed by GitHub
45 changed files with 341 additions and 1916 deletions
@@ -83,10 +83,10 @@ namespace AZ
// Update RenderActorInstance world bounding box
// The bounding box is moving with the actor instance.
// The entity and actor transforms are kept in sync already.
m_worldAABB = AZ::Aabb::CreateFromMinMax(m_actorInstance->GetAABB().GetMin(), m_actorInstance->GetAABB().GetMax());
m_worldAABB = m_actorInstance->GetAabb();
// Update RenderActorInstance local bounding box
// NB: computing the local bbox from the world bbox makes the local bbox artifically larger than it should be
// NB: computing the local bbox from the world bbox makes the local bbox artificially larger than it should be
// instead EMFX should support getting the local bbox from the actor instance directly
m_localAABB = m_worldAABB.GetTransformedAabb(m_transformInterface->GetWorldTM().GetInverse());
@@ -107,9 +107,8 @@ namespace AZ
{
if (debugOptions.m_drawAABB)
{
const MCore::AABB emfxAabb = m_actorInstance->GetAABB();
const AZ::Aabb azAabb = AZ::Aabb::CreateFromMinMax(emfxAabb.GetMin(), emfxAabb.GetMax());
auxGeom->DrawAabb(azAabb, AZ::Color(0.0f, 1.0f, 1.0f, 1.0f), RPI::AuxGeomDraw::DrawStyle::Line);
const AZ::Aabb& aabb = m_actorInstance->GetAabb();
auxGeom->DrawAabb(aabb, AZ::Color(0.0f, 1.0f, 1.0f, 1.0f), RPI::AuxGeomDraw::DrawStyle::Line);
}
if (debugOptions.m_drawSkeleton)
@@ -1063,11 +1063,10 @@ namespace CommandSystem
continue;
}
MCore::AABB newAABB;
actorInstance->SetStaticBasedAABB(actor->GetStaticAABB()); // this is needed as the CalcStaticBasedAABB uses the current AABB as starting point
actorInstance->CalcStaticBasedAABB(&newAABB);
actorInstance->SetStaticBasedAABB(newAABB);
//actorInstance->UpdateVisualizeScale();
actorInstance->SetStaticBasedAabb(actor->GetStaticAabb()); // this is needed as the CalcStaticBasedAabb uses the current AABB as starting point
AZ::Aabb newAabb;
actorInstance->CalcStaticBasedAabb(&newAabb);
actorInstance->SetStaticBasedAabb(newAabb);
const float factor = (float)MCore::Distance::GetConversionFactor(beforeUnitType, targetUnitType);
actorInstance->SetVisualizeScale(actorInstance->GetVisualizeScale() * factor);
@@ -18,21 +18,6 @@
namespace ExporterLib
{
void WriteObbToNodeChunk(EMotionFX::FileFormat::Actor_Node& nodeChunk, const MCore::OBB& obb)
{
AZ::Transform obbMatrix = obb.GetTransformation();
obbMatrix.GetBasisX().StoreToFloat3(nodeChunk.mOBB);
nodeChunk.mOBB[3] = 0.0f;
obbMatrix.GetBasisY().StoreToFloat3(nodeChunk.mOBB + 4);
nodeChunk.mOBB[7] = 0.0f;
obbMatrix.GetBasisZ().StoreToFloat3(nodeChunk.mOBB + 8);
nodeChunk.mOBB[11] = 0.0f;
nodeChunk.mOBB[12] = 0.0f;
nodeChunk.mOBB[13] = 0.0f;
nodeChunk.mOBB[14] = 0.0f;
nodeChunk.mOBB[15] = 1.0f;
}
void SaveNode(MCore::Stream* file, EMotionFX::Actor* actor, EMotionFX::Node* node, MCore::Endian::EEndianType targetEndianType)
{
MCORE_ASSERT(file);
@@ -47,7 +32,7 @@ namespace ExporterLib
const uint32 numChilds = node->GetNumChildNodes();
const EMotionFX::Transform& transform = actor->GetBindPose()->GetLocalSpaceTransform(nodeIndex);
AZ::PackedVector3f position = AZ::PackedVector3f(transform.mPosition);
AZ::Quaternion rotation = transform.mRotation.GetNormalized();;
AZ::Quaternion rotation = transform.mRotation.GetNormalized();
#ifndef EMFX_SCALE_DISABLED
AZ::PackedVector3f scale = AZ::PackedVector3f(transform.mScale);
@@ -56,14 +41,13 @@ namespace ExporterLib
#endif
// create the node chunk and copy over the information
EMotionFX::FileFormat::Actor_Node nodeChunk;
memset(&nodeChunk, 0, sizeof(EMotionFX::FileFormat::Actor_Node));
EMotionFX::FileFormat::Actor_Node2 nodeChunk;
memset(&nodeChunk, 0, sizeof(EMotionFX::FileFormat::Actor_Node2));
CopyVector(nodeChunk.mLocalPos, position);
CopyQuaternion(nodeChunk.mLocalQuat, rotation);
CopyVector(nodeChunk.mLocalScale, scale);
//nodeChunk.mImportanceFactor = FLT_MAX;//importance;
nodeChunk.mNumChilds = numChilds;
nodeChunk.mParentIndex = parentIndex;
@@ -98,10 +82,6 @@ namespace ExporterLib
nodeChunk.mNodeFlags &= ~EMotionFX::Node::ENodeFlags::FLAG_CRITICAL;
}
// OBB
WriteObbToNodeChunk(nodeChunk, actor->GetNodeOBB(node->GetNodeIndex()));
// log the node chunk information
MCore::LogDetailedInfo("- Node: name='%s' index=%i", actor->GetSkeleton()->GetNode(nodeIndex)->GetName(), nodeIndex);
if (parentIndex == MCORE_INVALIDINDEX32)
@@ -140,19 +120,13 @@ namespace ExporterLib
ConvertUnsignedInt(&nodeChunk.mNumChilds, targetEndianType);
ConvertUnsignedInt(&nodeChunk.mSkeletalLODs, targetEndianType);
for (uint32 j = 0; j < 16; ++j)
{
ConvertFloat(&nodeChunk.mOBB[j], targetEndianType);
}
// write it
file->Write(&nodeChunk, sizeof(EMotionFX::FileFormat::Actor_Node));
file->Write(&nodeChunk, sizeof(EMotionFX::FileFormat::Actor_Node2));
// write the name of the node and parent
SaveString(node->GetName(), file, targetEndianType);
}
void SaveNodes(MCore::Stream* file, EMotionFX::Actor* actor, MCore::Endian::EEndianType targetEndianType)
{
uint32 i;
@@ -167,10 +141,10 @@ namespace ExporterLib
// chunk information
EMotionFX::FileFormat::FileChunk chunkHeader;
chunkHeader.mChunkID = EMotionFX::FileFormat::ACTOR_CHUNK_NODES;
chunkHeader.mVersion = 1;
chunkHeader.mVersion = 2;
// get the nodes chunk size
chunkHeader.mSizeInBytes = sizeof(EMotionFX::FileFormat::Actor_Nodes) + numNodes * sizeof(EMotionFX::FileFormat::Actor_Node);
chunkHeader.mSizeInBytes = sizeof(EMotionFX::FileFormat::Actor_Nodes2) + numNodes * sizeof(EMotionFX::FileFormat::Actor_Node2);
for (i = 0; i < numNodes; i++)
{
chunkHeader.mSizeInBytes += GetStringChunkSize(actor->GetSkeleton()->GetNode(i)->GetName());
@@ -181,23 +155,15 @@ namespace ExporterLib
file->Write(&chunkHeader, sizeof(EMotionFX::FileFormat::FileChunk));
// nodes chunk
EMotionFX::FileFormat::Actor_Nodes nodesChunk;
EMotionFX::FileFormat::Actor_Nodes2 nodesChunk;
nodesChunk.mNumNodes = numNodes;
nodesChunk.mNumRootNodes = actor->GetSkeleton()->GetNumRootNodes();
nodesChunk.mStaticBoxMin.mX = actor->GetStaticAABB().GetMin().GetX();
nodesChunk.mStaticBoxMin.mY = actor->GetStaticAABB().GetMin().GetY();
nodesChunk.mStaticBoxMin.mZ = actor->GetStaticAABB().GetMin().GetZ();
nodesChunk.mStaticBoxMax.mX = actor->GetStaticAABB().GetMax().GetX();
nodesChunk.mStaticBoxMax.mY = actor->GetStaticAABB().GetMax().GetY();
nodesChunk.mStaticBoxMax.mZ = actor->GetStaticAABB().GetMax().GetZ();
// endian conversion and write it
ConvertUnsignedInt(&nodesChunk.mNumNodes, targetEndianType);
ConvertUnsignedInt(&nodesChunk.mNumRootNodes, targetEndianType);
ConvertFileVector3(&nodesChunk.mStaticBoxMin, targetEndianType);
ConvertFileVector3(&nodesChunk.mStaticBoxMax, targetEndianType);
file->Write(&nodesChunk, sizeof(EMotionFX::FileFormat::Actor_Nodes));
file->Write(&nodesChunk, sizeof(EMotionFX::FileFormat::Actor_Nodes2));
// write the nodes
for (uint32 n = 0; n < numNodes; n++)
@@ -272,7 +272,7 @@ namespace EMotionFX
// Post create actor
actor->SetUnitType(MCore::Distance::UNITTYPE_METERS);
actor->SetFileUnitType(MCore::Distance::UNITTYPE_METERS);
actor->PostCreateInit(/*makeGeomLodsCompatibleWithSkeletalLODs=*/false, /*generateOBBs=*/false, /*convertUnitType=*/false);
actor->PostCreateInit(/*makeGeomLodsCompatibleWithSkeletalLODs=*/false, /*convertUnitType=*/false);
// Only enable joints that are used for skinning (and their parents).
// On top of that, enable all joints marked as critical joints.
@@ -48,7 +48,8 @@ namespace EMotionFX
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<ActorGroupExporter, AZ::SceneAPI::SceneCore::ExportingComponent>()->Version(2);
// Increasing the version number of the actor group exporter will make sure all actor products will be force re-generated.
serializeContext->Class<ActorGroupExporter, AZ::SceneAPI::SceneCore::ExportingComponent>()->Version(3);
}
}
@@ -11,6 +11,7 @@
#include <AzCore/Math/Matrix4x4.h>
#include <AzCore/Math/Vector2.h>
#include <MCore/Source/AABB.h>
#include <MCore/Source/Vector.h>
#include <MCore/Source/Ray.h>
#include "MCommonConfig.h"
@@ -198,7 +198,7 @@ namespace MCommon
// render the current bounding box of the given actor instance
void RenderUtil::RenderAABB(const MCore::AABB& box, const MCore::RGBAColor& color, bool directlyRender)
void RenderUtil::RenderAabb(const AZ::Aabb& box, const MCore::RGBAColor& color, bool directlyRender)
{
AZ::Vector3 min = box.GetMin();
AZ::Vector3 max = box.GetMax();
@@ -238,26 +238,28 @@ namespace MCommon
// render selection gizmo around the given AABB
void RenderUtil::RenderSelection(const MCore::AABB& box, const MCore::RGBAColor& color, bool directlyRender)
void RenderUtil::RenderSelection(const AZ::Aabb& box, const MCore::RGBAColor& color, bool directlyRender)
{
//const Vector3 center = box.CalcMiddle();
const AZ::Vector3 min = box.GetMin();// + (box.GetMin()-center).Normalized()*0.005f;
const AZ::Vector3 max = box.GetMax();// + (box.GetMax()-center).Normalized()*0.005f;
const float scale = box.CalcRadius() * 0.1f;
const AZ::Vector3& min = box.GetMin();
const AZ::Vector3& max = box.GetMax();
const float radius = AZ::Vector3(box.GetMax() - box.GetMin()).GetLength() * 0.5f;
const float scale = radius * 0.1f;
const AZ::Vector3 up = AZ::Vector3(0.0f, 1.0f, 0.0f) * scale;
const AZ::Vector3 right = AZ::Vector3(1.0f, 0.0f, 0.0f) * scale;
const AZ::Vector3 front = AZ::Vector3(0.0f, 0.0f, 1.0f) * scale;
// generate our vertices
AZ::Vector3 p[8];
p[0].Set(min.GetX(), min.GetY(), min.GetZ());
p[1].Set(max.GetX(), min.GetY(), min.GetZ());
p[2].Set(max.GetX(), min.GetY(), max.GetZ());
p[3].Set(min.GetX(), min.GetY(), max.GetZ());
p[4].Set(min.GetX(), max.GetY(), min.GetZ());
p[5].Set(max.GetX(), max.GetY(), min.GetZ());
p[6].Set(max.GetX(), max.GetY(), max.GetZ());
p[7].Set(min.GetX(), max.GetY(), max.GetZ());
const AZStd::array p
{
AZ::Vector3{min.GetX(), min.GetY(), min.GetZ()},
AZ::Vector3{max.GetX(), min.GetY(), min.GetZ()},
AZ::Vector3{max.GetX(), min.GetY(), max.GetZ()},
AZ::Vector3{min.GetX(), min.GetY(), max.GetZ()},
AZ::Vector3{min.GetX(), max.GetY(), min.GetZ()},
AZ::Vector3{max.GetX(), max.GetY(), min.GetZ()},
AZ::Vector3{max.GetX(), max.GetY(), max.GetZ()},
AZ::Vector3{min.GetX(), max.GetY(), max.GetZ()},
};
// render the box
RenderLine(p[0], p[0] + up, color);
@@ -304,46 +306,29 @@ namespace MCommon
{
mNodeBasedAABB = true;
mMeshBasedAABB = true;
mCollisionMeshBasedAABB = true;
mStaticBasedAABB = true;
mStaticBasedColor = MCore::RGBAColor(0.0f, 0.7f, 0.7f);
mNodeBasedColor = MCore::RGBAColor(1.0f, 0.0f, 0.0f);
mCollisionMeshBasedColor = MCore::RGBAColor(0.0f, 0.7f, 0.0f);
mMeshBasedColor = MCore::RGBAColor(0.0f, 0.0f, 0.7f);
}
// render the given types of AABBs of a actor instance
void RenderUtil::RenderAABBs(EMotionFX::ActorInstance* actorInstance, const AABBRenderSettings& renderSettings, bool directlyRender)
void RenderUtil::RenderAabbs(EMotionFX::ActorInstance* actorInstance, const AABBRenderSettings& renderSettings, bool directlyRender)
{
// get the current LOD level
const uint32 lodLevel = actorInstance->GetLODLevel();
// handle the collision mesh based AABB
if (renderSettings.mCollisionMeshBasedAABB)
{
// calculate the collision mesh based AABB
MCore::AABB box;
actorInstance->CalcCollisionMeshBasedAABB(lodLevel, &box);
// render the aabb
if (box.CheckIfIsValid())
{
RenderAABB(box, renderSettings.mCollisionMeshBasedColor);
}
}
// handle the node based AABB
if (renderSettings.mNodeBasedAABB)
{
// calculate the node based AABB
MCore::AABB box;
actorInstance->CalcNodeBasedAABB(&box);
AZ::Aabb box;
actorInstance->CalcNodeBasedAabb(&box);
// render the aabb
if (box.CheckIfIsValid())
if (box.IsValid())
{
RenderAABB(box, renderSettings.mNodeBasedColor);
RenderAabb(box, renderSettings.mNodeBasedColor);
}
}
@@ -351,26 +336,26 @@ namespace MCommon
if (renderSettings.mMeshBasedAABB)
{
// calculate the mesh based AABB
MCore::AABB box;
actorInstance->CalcMeshBasedAABB(lodLevel, &box);
AZ::Aabb box;
actorInstance->CalcMeshBasedAabb(lodLevel, &box);
// render the aabb
if (box.CheckIfIsValid())
if (box.IsValid())
{
RenderAABB(box, renderSettings.mMeshBasedColor);
RenderAabb(box, renderSettings.mMeshBasedColor);
}
}
if (renderSettings.mStaticBasedAABB)
{
// calculate the static based AABB
MCore::AABB box;
actorInstance->CalcStaticBasedAABB(&box);
AZ::Aabb box;
actorInstance->CalcStaticBasedAabb(&box);
// render the aabb
if (box.CheckIfIsValid())
if (box.IsValid())
{
RenderAABB(box, renderSettings.mStaticBasedColor);
RenderAabb(box, renderSettings.mStaticBasedColor);
}
}
@@ -420,77 +405,6 @@ namespace MCommon
}
}
// render object orientated bounding boxes for all enabled nodes inside the actor instance
void RenderUtil::RenderOBBs(EMotionFX::ActorInstance* actorInstance, const AZStd::unordered_set<AZ::u32>* visibleJointIndices, const AZStd::unordered_set<AZ::u32>* selectedJointIndices, const MCore::RGBAColor& color, const MCore::RGBAColor& selectedColor, bool directlyRender)
{
AZ::Vector3 p[8];
// get the actor it is an instance from
const EMotionFX::Actor* actor = actorInstance->GetActor();
const EMotionFX::Skeleton* skeleton = actor->GetSkeleton();
const EMotionFX::Pose* pose = actorInstance->GetTransformData()->GetCurrentPose();
// iterate through all enabled nodes
MCore::RGBAColor tempColor;
const uint32 numEnabled = actorInstance->GetNumEnabledNodes();
for (uint32 i = 0; i < numEnabled; ++i)
{
const EMotionFX::Node* joint = skeleton->GetNode(actorInstance->GetEnabledNode(i));
const AZ::u32 jointIndex = joint->GetNodeIndex();
if (!visibleJointIndices || visibleJointIndices->empty() ||
(visibleJointIndices->find(jointIndex) != visibleJointIndices->end()))
{
const MCore::OBB& obb = actor->GetNodeOBB(jointIndex);
EMotionFX::Transform worldTransform = pose->GetWorldSpaceTransform(jointIndex);
// skip the OBB if it isn't valid
if (obb.CheckIfIsValid() == false)
{
continue;
}
// check if the current bone is selected and set the color according to it
if (selectedJointIndices && selectedJointIndices->find(jointIndex) != selectedJointIndices->end())
{
tempColor = selectedColor;
}
else
{
tempColor = color;
}
obb.CalcCornerPoints(p);
for (uint32 a = 0; a < 8; a++)
{
p[a] = worldTransform.TransformPoint(p[a]);
}
// render
RenderLine(p[0], p[1], tempColor);
RenderLine(p[1], p[2], tempColor);
RenderLine(p[2], p[3], tempColor);
RenderLine(p[0], p[3], tempColor);
RenderLine(p[1], p[5], tempColor);
RenderLine(p[3], p[7], tempColor);
RenderLine(p[2], p[6], tempColor);
RenderLine(p[0], p[4], tempColor);
RenderLine(p[4], p[5], tempColor);
RenderLine(p[4], p[7], tempColor);
RenderLine(p[6], p[7], tempColor);
RenderLine(p[6], p[5], tempColor);
}
}
if (directlyRender)
{
RenderLines();
}
}
// render wireframe mesh
void RenderUtil::RenderWireframe(EMotionFX::Mesh* mesh, const AZ::Transform& worldTM, const MCore::RGBAColor& color, bool directlyRender, float offsetScale)
{
@@ -1639,7 +1553,7 @@ namespace MCommon
// calculate the intersection points with the ground plane and create an AABB around those
// if there is no intersection point then use the ray target as point, which is the projection onto the far plane basically
MCore::AABB aabb;
AZ::Aabb aabb = AZ::Aabb::CreateNull();
AZ::Vector3 intersectionPoint;
const AZ::Plane groundPlane = AZ::Plane::CreateFromNormalAndPoint(AZ::Vector3(0.0f, 0.0f, 1.0f), AZ::Vector3::CreateZero());
for (AZ::u32 i = 0; i < 4; ++i)
@@ -1649,7 +1563,7 @@ namespace MCommon
corners[i] = intersectionPoint;
}
aabb.Encapsulate(corners[i]);
aabb.AddPoint(corners[i]);
}
// set the grid start and end values
@@ -1665,9 +1579,9 @@ namespace MCommon
// get aabb which includes all actor instances
MCore::AABB RenderUtil::CalcSceneAABB()
AZ::Aabb RenderUtil::CalcSceneAabb()
{
MCore::AABB finalAABB;
AZ::Aabb finalAABB = AZ::Aabb::CreateNull();
// get the number of actor instances and iterate through them
const uint32 numActorInstances = EMotionFX::GetActorManager().GetNumActorInstances();
@@ -1685,17 +1599,17 @@ namespace MCommon
actorInstance->UpdateMeshDeformers(0.0f);
// get the mesh based bounding box
MCore::AABB boundingBox;
actorInstance->CalcMeshBasedAABB(actorInstance->GetLODLevel(), &boundingBox);
AZ::Aabb boundingBox;
actorInstance->CalcMeshBasedAabb(actorInstance->GetLODLevel(), &boundingBox);
// in case there aren't any meshes, use the node based bounding box
if (boundingBox.CheckIfIsValid() == false)
if (!boundingBox.IsValid())
{
actorInstance->CalcNodeBasedAABB(&boundingBox);
actorInstance->CalcNodeBasedAabb(&boundingBox);
}
// make sure the actor instance is covered in our world bounding box
finalAABB.Encapsulate(boundingBox);
finalAABB.AddAabb(boundingBox);
}
return finalAABB;
@@ -6,10 +6,9 @@
*
*/
#ifndef __MCOMMON_RENDERUTIL_H
#define __MCOMMON_RENDERUTIL_H
#pragma once
// include required headers
#include <AzCore/Math/Aabb.h>
#include <AzCore/Math/Vector2.h>
#include <MCore/Source/AzCoreConversions.h>
#include <MCore/Source/Vector.h>
@@ -111,7 +110,7 @@ namespace MCommon
* Render tangents and bitangents of the mesh.
* @param mesh A pointer to the mesh which will be rendered.
* @param worldTM The world space transformation matrix of the node to which the given mesh belongs to.
* @param scale This parameter controls the length of the tangents and bitangentss. The default size of the tangents and bitangents is one unit.
* @param scale This parameter controls the length of the tangents and bitangents. The default size of the tangents and bitangents is one unit.
* @param colorTangents The color of the tangents.
* @param mirroredBitangentColor The color of the mirrored bitangents, so the ones that have a w value of -1.
* @param colorBitangent The color of the face bitangents.
@@ -127,7 +126,7 @@ namespace MCommon
* @param directlyRender Will call the RenderLines() function internally in case it is set to true. If false
* you have to make sure to call RenderLines() manually at the end of your custom render frame function.
*/
void RenderAABB(const MCore::AABB& box, const MCore::RGBAColor& color, bool directlyRender = false);
void RenderAabb(const AZ::Aabb& box, const MCore::RGBAColor& color, bool directlyRender = false);
/**
* Render a selection gizmo around the given axis aligned bounding box.
@@ -136,7 +135,7 @@ namespace MCommon
* @param directlyRender Will call the RenderLines() function internally in case it is set to true. If false
* you have to make sure to call RenderLines() manually at the end of your custom render frame function.
*/
void RenderSelection(const MCore::AABB& box, const MCore::RGBAColor& color, bool directlyRender = false);
void RenderSelection(const AZ::Aabb& box, const MCore::RGBAColor& color, bool directlyRender = false);
/**
* The render settings used to enable the different AABB types of an actor instance.
@@ -152,11 +151,9 @@ namespace MCommon
bool mNodeBasedAABB; /**< Enable in case you want to render the node based AABB (default=true). */
bool mMeshBasedAABB; /**< Enable in case you want to render the mesh based AABB (default=true). */
bool mCollisionMeshBasedAABB; /**< Enable in case you want to render the collision mesh based AABB (default=true). */
bool mStaticBasedAABB; /**< Enable in case you want to render the static based AABB (default=true). */
MCore::RGBAColor mNodeBasedColor; /**< The color of the node based AABB. */
MCore::RGBAColor mMeshBasedColor; /**< The color of the mesh based AABB. */
MCore::RGBAColor mCollisionMeshBasedColor; /**< The color of the collision mesh based AABB. */
MCore::RGBAColor mStaticBasedColor; /**< The color of the static based AABB. */
};
@@ -168,19 +165,7 @@ namespace MCommon
* @param directlyRender Will call the RenderLines() function internally in case it is set to true. If false
* you have to make sure to call RenderLines() manually at the end of your custom render frame function.
*/
void RenderAABBs(EMotionFX::ActorInstance* actorInstance, const AABBRenderSettings& renderSettings = AABBRenderSettings(), bool directlyRender = false);
/**
* Render OBB for all enabled nodes inside the actor instance.
* @param actorInstance A pointer to the actor instance which will be rendered.
* @param[in] visibleJointIndices List of visible joint indices. nullptr in case all joints should be rendered.
* @param[in] selectedJointIndices List of selected joint indices. nullptr in case selection should not be considered.
* @param[in] color The color of the OBBs.
* @param[in] selectedColor The color of the selected OBBs.
* @param[in] directlyRender Will call the RenderLines() function internally in case it is set to true. If false
* you have to make sure to call RenderLines() manually at the end of your custom render frame function.
*/
void RenderOBBs(EMotionFX::ActorInstance* actorInstance, const AZStd::unordered_set<AZ::u32>* visibleJointIndices = nullptr, const AZStd::unordered_set<AZ::u32>* selectedJointIndices = nullptr, const MCore::RGBAColor& color = MCore::RGBAColor(1.0f, 1.0f, 0.0f, 1.0f), const MCore::RGBAColor& selectedColor = MCore::RGBAColor(1.0f, 0.647f, 0.0f), bool directlyRender = false);
void RenderAabbs(EMotionFX::ActorInstance* actorInstance, const AABBRenderSettings& renderSettings = AABBRenderSettings(), bool directlyRender = false);
/**
* Render a simple line based skeleton for all enabled nodes of the actor instance.
@@ -615,7 +600,7 @@ namespace MCommon
* Calculate the aabb which includes all actor instances.
* @return The aabb which includes all actor instances.
*/
MCore::AABB CalcSceneAABB();
AZ::Aabb CalcSceneAabb();
struct TrajectoryPathParticle
{
@@ -722,7 +707,7 @@ namespace MCommon
/**
* Change the shape of a given arrow head util mesh. This method can be used to adjust an already allocated arrow head util mesh.
* For example this can be usedful if you need to change the radius or the height of an arrow head.
* For example this can be useful if you need to change the radius or the height of an arrow head.
* @param mesh A pointer to the arrow head util mesh. Note that this mesh has to be created using CreateArrowHead().
* @param height The height of the arrow head from the base to the head.
* @param radius The radius of the base of the arrow head.
@@ -735,7 +720,7 @@ namespace MCommon
* @param radius The radius of the sphere.
* @return A pointer to the newly created util sphere mesh.
*/
static UtilMesh* CreateSphere(float radius, uint32 numSegments = 8);
static UtilMesh* CreateSphere(float radius, uint32 numSegments = 5);
/**
* Create an util mesh we can use to render cubes.
@@ -831,6 +816,3 @@ namespace MCommon
static uint32 mNumMaxTriangleVertices; /**< The maximum capacity of the triangle vertex buffer */
};
} // namespace MCommon
#endif
@@ -8,7 +8,7 @@
#pragma once
// include the Core system
#include <MCore/Source/AABB.h>
#include <MCore/Source/Vector.h>
#include <MCore/Source/BoundingSphere.h>
#include <MCore/Source/Ray.h>
@@ -8,7 +8,7 @@
#pragma once
// include the Core system
#include <MCore/Source/AABB.h>
#include <MCore/Source/Vector.h>
#include <MCore/Source/Ray.h>
#include "MCommonConfig.h"
@@ -9,7 +9,7 @@
#ifndef __MCOMMON_TRANSLATEMANIPULATOR_H
#define __MCOMMON_TRANSLATEMANIPULATOR_H
// include the Core system
#include <MCore/Source/AABB.h>
#include <MCore/Source/Vector.h>
#include <MCore/Source/Ray.h>
#include "MCommonConfig.h"
+19 -134
View File
@@ -42,7 +42,6 @@
#include <MCore/Source/IDGenerator.h>
#include <MCore/Source/Compare.h>
#include <MCore/Source/LogManager.h>
#include <MCore/Source/OBB.h>
#include <Atom/RPI.Reflect/Model/MorphTargetDelta.h>
@@ -50,11 +49,6 @@ namespace EMotionFX
{
AZ_CLASS_ALLOCATOR_IMPL(Actor, ActorAllocator, 0)
Actor::NodeInfo::NodeInfo()
{
mOBB.Init();
}
Actor::LODLevel::LODLevel()
{
}
@@ -97,6 +91,7 @@ namespace EMotionFX
mID = MCore::GetIDGenerator().GenerateID();
mUnitType = GetEMotionFX().GetUnitType();
mFileUnitType = mUnitType;
m_staticAabb = AZ::Aabb::CreateNull();
mUsedForVisualization = false;
mDirtyFlag = false;
@@ -148,7 +143,7 @@ namespace EMotionFX
result->mMotionExtractionNode = mMotionExtractionNode;
result->mUnitType = mUnitType;
result->mFileUnitType = mFileUnitType;
result->mStaticAABB = mStaticAABB;
result->m_staticAabb = m_staticAabb;
result->mRetargetRootNode = mRetargetRootNode;
result->mInvBindPoseTransforms = mInvBindPoseTransforms;
result->m_optimizeSkeleton = m_optimizeSkeleton;
@@ -187,7 +182,6 @@ namespace EMotionFX
result->mSkeleton = mSkeleton->Clone();
// clone lod data
result->mNodeInfos = mNodeInfos;
const uint32 numNodes = mSkeleton->GetNumNodes();
const size_t numLodLevels = m_meshLodData.m_lodLevels.size();
@@ -997,18 +991,6 @@ namespace EMotionFX
}
}
// update the bounding volumes
void Actor::UpdateNodeBindPoseOBBs(uint32 lodLevel)
{
// for all nodes
const uint32 numNodes = mSkeleton->GetNumNodes();
for (uint32 i = 0; i < numNodes; ++i)
{
CalcOBBFromBindPose(lodLevel, i);
}
}
// remove all node groups
void Actor::RemoveAllNodeGroups()
{
@@ -1352,9 +1334,8 @@ namespace EMotionFX
}
}
// post init
void Actor::PostCreateInit(bool makeGeomLodsCompatibleWithSkeletalLODs, bool generateOBBs, bool convertUnitType)
void Actor::PostCreateInit(bool makeGeomLodsCompatibleWithSkeletalLODs, bool convertUnitType)
{
if (mThreadIndex == MCORE_INVALIDINDEX32)
{
@@ -1387,11 +1368,6 @@ namespace EMotionFX
mSkeleton->GetBindPose()->ForceUpdateFullModelSpacePose();
mSkeleton->GetBindPose()->ZeroMorphWeights();
if (generateOBBs)
{
UpdateNodeBindPoseOBBs(0);
}
if (!GetHasMirrorInfo())
{
AllocateNodeMirrorInfos();
@@ -1405,10 +1381,6 @@ namespace EMotionFX
m_simulatedObjectSetup->InitAfterLoad(this);
// build the static axis aligned bounding box by creating an actor instance (needed to perform cpu skinning mesh deforms and mesh scaling etc)
// then copy it over to the actor
UpdateStaticAABB();
// rescale all content if needed
if (convertUnitType)
{
@@ -1526,6 +1498,10 @@ namespace EMotionFX
mMorphSetups[i] = nullptr;
}
}
// build the static axis aligned bounding box by creating an actor instance (needed to perform cpu skinning mesh deforms and mesh scaling etc)
// then copy it over to the actor
UpdateStaticAabb();
}
m_isReady = true;
@@ -1534,16 +1510,13 @@ namespace EMotionFX
}
// update the static AABB (very heavy as it has to create an actor instance, update mesh deformers, calculate the mesh based bounds etc)
void Actor::UpdateStaticAABB()
void Actor::UpdateStaticAabb()
{
if (!mStaticAABB.CheckIfIsValid())
{
ActorInstance* actorInstance = ActorInstance::Create(this, nullptr, mThreadIndex);
//actorInstance->UpdateMeshDeformers(0.0f);
//actorInstance->UpdateStaticBasedAABBDimensions();
actorInstance->GetStaticBasedAABB(&mStaticAABB);
actorInstance->Destroy();
}
ActorInstance* actorInstance = ActorInstance::Create(this, nullptr, mThreadIndex);
actorInstance->UpdateMeshDeformers(0.0f);
actorInstance->UpdateStaticBasedAabbDimensions();
actorInstance->GetStaticBasedAabb(&m_staticAabb);
actorInstance->Destroy();
}
@@ -1885,7 +1858,6 @@ namespace EMotionFX
void Actor::SetNumNodes(uint32 numNodes)
{
mSkeleton->SetNumNodes(numNodes);
mNodeInfos.resize(numNodes);
AZStd::vector<LODLevel>& lodLevels = m_meshLodData.m_lodLevels;
for (LODLevel& lodLevel : lodLevels)
@@ -1903,7 +1875,6 @@ namespace EMotionFX
mSkeleton->GetBindPose()->LinkToActor(this, Pose::FLAG_LOCALTRANSFORMREADY, false);
// initialize the LOD data
mNodeInfos.emplace_back();
AZStd::vector<LODLevel>& lodLevels = m_meshLodData.m_lodLevels;
for (LODLevel& lodLevel : lodLevels)
{
@@ -1934,7 +1905,6 @@ namespace EMotionFX
void Actor::RemoveNode(uint32 nr, bool delMem)
{
mSkeleton->RemoveNode(nr, delMem);
mNodeInfos.erase(mNodeInfos.begin() + nr);
AZStd::vector<LODLevel>& lodLevels = m_meshLodData.m_lodLevels;
for (LODLevel& lodLevel : lodLevels)
@@ -1946,7 +1916,6 @@ namespace EMotionFX
void Actor::DeleteAllNodes()
{
mSkeleton->RemoveAllNodes();
mNodeInfos.clear();
AZStd::vector<LODLevel>& lodLevels = m_meshLodData.m_lodLevels;
for (LODLevel& lodLevel : lodLevels)
@@ -2206,14 +2175,14 @@ namespace EMotionFX
#endif
}
const MCore::AABB& Actor::GetStaticAABB() const
const AZ::Aabb& Actor::GetStaticAabb() const
{
return mStaticAABB;
return m_staticAabb;
}
void Actor::SetStaticAABB(const MCore::AABB& box)
void Actor::SetStaticAabb(const AZ::Aabb& aabb)
{
mStaticAABB = box;
m_staticAabb = aabb;
}
//---------------------------------
@@ -2265,82 +2234,6 @@ namespace EMotionFX
return (stack->CheckIfHasDeformerOfType(SoftSkinDeformer::TYPE_ID) || stack->CheckIfHasDeformerOfType(DualQuatSkinDeformer::TYPE_ID));
}
// calculate the OBB for a given node
void Actor::CalcOBBFromBindPose(uint32 lodLevel, uint32 nodeIndex)
{
AZStd::vector<AZ::Vector3> points;
// if there is a mesh
Mesh* mesh = GetMesh(lodLevel, nodeIndex);
if (mesh)
{
// if the mesh is not skinned
if (mesh->FindSharedVertexAttributeLayer(SkinningInfoVertexAttributeLayer::TYPE_ID) == nullptr)
{
mesh->ExtractOriginalVertexPositions(points);
}
}
else // there is no mesh, so maybe this is a bone
{
const Transform& invBindPoseTransform = GetInverseBindPoseTransform(nodeIndex);
// for all nodes inside the actor where this node belongs to
const uint32 numNodes = mSkeleton->GetNumNodes();
for (uint32 n = 0; n < numNodes; ++n)
{
Mesh* loopMesh = GetMesh(lodLevel, n);
if (loopMesh == nullptr)
{
continue;
}
// get the vertex positions in bind pose
const uint32 numVerts = loopMesh->GetNumVertices();
points.reserve(numVerts * 2);
AZ::Vector3* positions = (AZ::Vector3*)loopMesh->FindOriginalVertexData(Mesh::ATTRIB_POSITIONS);
SkinningInfoVertexAttributeLayer* skinLayer = (SkinningInfoVertexAttributeLayer*)loopMesh->FindSharedVertexAttributeLayer(SkinningInfoVertexAttributeLayer::TYPE_ID);
if (skinLayer)
{
// iterate over all skinning influences and see if this node number is used
// if so, add it to the list of points
const uint32* orgVertices = (uint32*)loopMesh->FindVertexData(Mesh::ATTRIB_ORGVTXNUMBERS);
for (uint32 v = 0; v < numVerts; ++v)
{
// get the original vertex number
const uint32 orgVtx = orgVertices[v];
// for all skinning influences for this vertex
const size_t numInfluences = skinLayer->GetNumInfluences(orgVtx);
for (size_t i = 0; i < numInfluences; ++i)
{
// get the node used by this influence
const uint32 nodeNr = skinLayer->GetInfluence(orgVtx, i)->GetNodeNr();
// if this is the same node as we are updating the bounds for, add the vertex position to the list
if (nodeNr == nodeIndex)
{
const AZ::Vector3 tempPos(positions[v]);
points.emplace_back(invBindPoseTransform.TransformPoint(tempPos));
}
} // for all influences
} // for all vertices
} // if there is skinning info
} // for all nodes
}
// init from the set of points
if (!points.empty())
{
GetNodeOBB(nodeIndex).InitFromPoints(&points[0], static_cast<uint32>(points.size()));
}
else
{
GetNodeOBB(nodeIndex).Init();
}
}
// remove the mesh for a given node in a given LOD
void Actor::RemoveNodeMeshForLOD(uint32 lodLevel, uint32 nodeIndex, bool destroyMesh)
{
@@ -2413,17 +2306,9 @@ namespace EMotionFX
mInvBindPoseTransforms[i] = bindPose->GetModelSpaceTransform(i).Inversed();
}
// update node obbs
for (uint32 i = 0; i < numNodes; ++i)
{
MCore::OBB& box = GetNodeOBB(i);
box.SetExtents(box.GetExtents() * scaleFactor);
box.SetCenter(box.GetCenter() * scaleFactor);
}
// update static aabb
mStaticAABB.SetMin(mStaticAABB.GetMin() * scaleFactor);
mStaticAABB.SetMax(mStaticAABB.GetMax() * scaleFactor);
m_staticAabb.SetMin(m_staticAabb.GetMin() * scaleFactor);
m_staticAabb.SetMax(m_staticAabb.GetMax() * scaleFactor);
// update mesh data for all LOD levels
const uint32 numLODs = GetNumLODLevels();
+6 -78
View File
@@ -16,15 +16,14 @@
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzCore/std/string/string.h>
#include <AzCore/std/typetraits/integral_constant.h>
#include <AzCore/Math/Aabb.h>
#include <AzCore/Math/Vector3.h>
#include <AzCore/Math/Color.h>
// include MCore related files
#include <MCore/Source/AABB.h>
#include <MCore/Source/Vector.h>
#include <MCore/Source/Array.h>
#include <MCore/Source/SmallArray.h>
#include <MCore/Source/OBB.h>
#include <MCore/Source/Distance.h>
// include required headers
@@ -564,17 +563,6 @@ namespace EMotionFX
*/
void SetMorphSetup(uint32 lodLevel, MorphSetup* setup);
/**
* Update the oriented bounding volumes (OBB) of all the nodes inside this actor.
* This is a very heavy calculation and must NOT be performed on a per-frame basis but only as pre-process step.
* The OBBs of the nodes are already being calculated at export time, so you shouldn't really need to use this method.
* Only when the bind pose geometry has changed you can update the node OBBs by calling this method.
* For more information about how the bounds are calculated please see the Node::GetOBB() and Node::CalcOBBFromBindPose() methods.
* The calculations performed by this method are automatically spread over multiple threads to improve the performance.
* @param lodLevel The geometry LOD level to use while calculating the object oriented bounds per node.
*/
void UpdateNodeBindPoseOBBs(uint32 lodLevel);
/**
* Get the number of node groups inside this actor object.
* @result The number of node groups.
@@ -758,7 +746,7 @@ namespace EMotionFX
void MakeGeomLODsCompatibleWithSkeletalLODs();
void ReinitializeMeshDeformers();
void PostCreateInit(bool makeGeomLodsCompatibleWithSkeletalLODs = true, bool generateOBBs = true, bool convertUnitType = true);
void PostCreateInit(bool makeGeomLodsCompatibleWithSkeletalLODs = true, bool convertUnitType = true);
void AutoDetectMirrorAxes();
const MCore::Array<NodeMirrorInfo>& GetNodeMirrorInfos() const;
@@ -781,9 +769,9 @@ namespace EMotionFX
void ResizeTransformData();
void CopyTransformsFrom(const Actor* other);
const MCore::AABB& GetStaticAABB() const;
void SetStaticAABB(const MCore::AABB& box);
void UpdateStaticAABB(); // VERY heavy operation, you shouldn't call this ever (internally creates an actor instance, updates mesh deformers, calcs a mesh based aabb, destroys the actor instance again)
const AZ::Aabb& GetStaticAabb() const;
void SetStaticAabb(const AZ::Aabb& aabb);
void UpdateStaticAabb(); // VERY heavy operation, you shouldn't call this ever (internally creates an actor instance, updates mesh deformers, calcs a mesh based aabb, destroys the actor instance again)
void SetThreadIndex(uint32 index) { mThreadIndex = index; }
uint32 GetThreadIndex() const { return mThreadIndex; }
@@ -808,57 +796,6 @@ namespace EMotionFX
bool CheckIfHasMorphDeformer(uint32 lodLevel, uint32 nodeIndex) const;
bool CheckIfHasSkinningDeformer(uint32 lodLevel, uint32 nodeIndex) const;
/**
* Calculate the object oriented box for a given LOD level.
* This will try to fit the tightest bounding box around the mesh of a node.
* If the node has no mesh and acts as bone inside skinning deformations the resulting box will contain
* all the vertices that are influenced by this given node/bone.
* Calculating this box is already done at export time. But you can use this to recalculate it if the mesh data changed.
* This method is relatively slow and not meant for per-frame calculations but only for preprocessing.
* You can use the GetOBB() method to retrieve the calculated box at any time.
* Nodes that do not have a mesh and not act as bone will have invalid OBB bounds, as they have no volume. You can check whether
* this is the case or not by using the MCore::OBB::IsValid() method.
* The box is stored in local space of the node.
* @param lodLevel The geometry LOD level to generate the OBBs from.
* @param nodeIndex The node to calculate the OBB for.
*/
void CalcOBBFromBindPose(uint32 lodLevel, uint32 nodeIndex);
/**
* Get the object oriented bounding box for this node.
* The box is in local space. In order to convert it into world space you have to multiply the corner points of the box
* with the world space matrix of this node.
* Nodes that do not have a mesh and do not act as bone will have invalid bounds. You can use the MCore::OBB::CheckIfIsValid() method to check if
* the bounds are valid bounds or not. If it is not, then it means there was nothing to calculate the box from.
* Object Oriented Boxes for the nodes are calculated at export time by using the Actor::UpdateNodeBindPoseOBBs() and Node::CalcOBBFromBindPose() methods.
* @param nodeIndex The index of the node to get the OBB for.
* @result The object oriented bounding box that has been calculated before already.
*/
MCore::OBB& GetNodeOBB(uint32 nodeIndex) { return mNodeInfos[nodeIndex].mOBB; }
/**
* Get the object oriented bounding box for this node.
* The box is in local space. In order to convert it into world space you have to multiply the corner points of the box
* with the world space matrix of this node.
* Nodes that do not have a mesh and do not act as bone will have invalid bounds. You can use the MCore::OBB::CheckIfIsValid() method to check if
* the bounds are valid bounds or not. If it is not, then it means there was nothing to calculate the box from.
* Object Oriented Boxes for the nodes are calculated at export time by using the Actor::UpdateNodeBindPoseOBBs() and Node::CalcOBBFromBindPose() methods.
* @param nodeIndex The index of the node to get the OBB for.
* @result The object oriented bounding box that has been calculated before already.
*/
const MCore::OBB& GetNodeOBB(uint32 nodeIndex) const { return mNodeInfos[nodeIndex].mOBB; }
/**
* Set the object oriented bounding box for this node.
* The box is in local space. In order to convert it into world space you have to multiply the corner points of the box
* with the world space matrix of this node.
* Nodes that do not have a mesh and do not act as bone will have invalid bounds. You can use the MCore::OBB::CheckIfIsValid() method to check if
* the bounds are valid bounds or not. If it is not, then it means there was nothing to calculate the box from.
* @param nodeIndex The index of the node to set the OBB for.
* @param obb The object oriented bounding box that has been calculated before already.
*/
void SetNodeOBB(uint32 nodeIndex, const MCore::OBB& obb) { mNodeInfos[nodeIndex].mOBB = obb; }
void RemoveNodeMeshForLOD(uint32 lodLevel, uint32 nodeIndex, bool destroyMesh = true);
void SetNumNodes(uint32 numNodes);
@@ -917,14 +854,6 @@ namespace EMotionFX
Node* FindJointByMeshName(const AZStd::string_view meshName) const;
// per node info (shared between lods)
struct EMFX_API NodeInfo
{
MCore::OBB mOBB;
NodeInfo();
};
// data per node, per lod
struct EMFX_API NodeLODInfo
{
@@ -968,7 +897,6 @@ namespace EMotionFX
Skeleton* mSkeleton; /**< The skeleton, containing the nodes and bind pose. */
MCore::Array<Dependency> mDependencies; /**< The dependencies on other actors (shared meshes and transforms). */
AZStd::vector<NodeInfo> mNodeInfos; /**< The per node info, shared between lods. */
AZStd::string mName; /**< The name of the actor. */
AZStd::string mFileName; /**< The filename of the actor. */
MCore::Array<NodeMirrorInfo> mNodeMirrorInfos; /**< The array of node mirror info. */
@@ -985,7 +913,7 @@ namespace EMotionFX
uint32 mRetargetRootNode; /**< The retarget root node, which controls the height displacement of the character. This is most likely the hip or pelvis node. */
uint32 mID; /**< The unique identification number for the actor. */
uint32 mThreadIndex; /**< The thread number we are running on, which is a value starting at 0, up to the number of threads in the job system. */
MCore::AABB mStaticAABB; /**< The static AABB. */
AZ::Aabb m_staticAabb; /**< The static AABB. */
bool mDirtyFlag; /**< The dirty flag which indicates whether the user has made changes to the actor since the last file save operation. */
bool mUsedForVisualization; /**< Indicates if the actor is used for visualization specific things and is not used as a normal in-game actor. */
bool m_optimizeSkeleton; /**< Indicates if we should perform/ */
@@ -67,7 +67,7 @@ namespace EMotionFX
mMotionSamplingTimer = 0.0f;
mTrajectoryDelta.IdentityWithZeroScale();
mStaticAABB.Init();
m_staticAabb = AZ::Aabb::CreateNull();
mAnimGraphInstance = nullptr;
@@ -137,15 +137,15 @@ namespace EMotionFX
UpdateDependencies();
// update the static based AABB dimensions
mStaticAABB = mActor->GetStaticAABB();
if (mStaticAABB.CheckIfIsValid() == false)
m_staticAabb = mActor->GetStaticAabb();
if (!m_staticAabb.IsValid())
{
UpdateMeshDeformers(0.0f, true); // TODO: not really thread safe because of shared meshes, although it probably will output correctly
UpdateStaticBasedAABBDimensions();
UpdateStaticBasedAabbDimensions();
}
// update the bounds
UpdateBounds(0, mBoundsUpdateType, 1);
UpdateBounds(/*lodLevel=*/0, mBoundsUpdateType);
// register it
GetActorManager().RegisterActorInstance(this);
@@ -254,12 +254,12 @@ namespace EMotionFX
UpdateAttachments(); // update the attachment parent matrices
// update the bounds when needed
if (GetBoundsUpdateEnabled() && mBoundsUpdateType != BOUNDS_MESH_BASED)
if (GetBoundsUpdateEnabled())
{
mBoundsUpdatePassedTime += timePassedInSeconds;
if (mBoundsUpdatePassedTime >= mBoundsUpdateFrequency)
{
UpdateBounds(mLODLevel, BOUNDS_NODE_BASED, mBoundsUpdateItemFreq);
UpdateBounds(mLODLevel, mBoundsUpdateType, mBoundsUpdateItemFreq);
mBoundsUpdatePassedTime = 0.0f;
}
}
@@ -354,7 +354,7 @@ namespace EMotionFX
}
// update the bounds when needed
if (GetBoundsUpdateEnabled() && mBoundsUpdateType != BOUNDS_MESH_BASED)
if (GetBoundsUpdateEnabled())
{
mBoundsUpdatePassedTime += timePassedInSeconds;
if (mBoundsUpdatePassedTime >= mBoundsUpdateFrequency)
@@ -407,18 +407,6 @@ namespace EMotionFX
stack->Update(this, node, timePassedInSeconds, processDisabledDeformers);
}
}
// Update the bounds when we are set to use mesh based bounds.
if (GetBoundsUpdateEnabled() &&
GetBoundsUpdateType() == BOUNDS_MESH_BASED)
{
mBoundsUpdatePassedTime += timePassedInSeconds;
if (mBoundsUpdatePassedTime >= mBoundsUpdateFrequency)
{
UpdateBounds(mLODLevel, mBoundsUpdateType, mBoundsUpdateItemFreq);
mBoundsUpdatePassedTime = 0.0f;
}
}
}
// Update the mesh morph deformers, which updates the vertex positions on the CPU, so performing CPU morphing.
@@ -439,18 +427,6 @@ namespace EMotionFX
stack->UpdateByModifierType(this, node, timePassedInSeconds, MorphMeshDeformer::TYPE_ID, true, processDisabledDeformers);
}
}
// Update the bounds when we are set to use mesh based bounds.
if (GetBoundsUpdateEnabled() &&
GetBoundsUpdateType() == BOUNDS_MESH_BASED)
{
mBoundsUpdatePassedTime += timePassedInSeconds;
if (mBoundsUpdatePassedTime >= mBoundsUpdateFrequency)
{
UpdateBounds(mLODLevel, mBoundsUpdateType, mBoundsUpdateItemFreq);
mBoundsUpdatePassedTime = 0.0f;
}
}
}
void ActorInstance::PostPhysicsUpdate(float timePassedInSeconds)
@@ -639,118 +615,40 @@ namespace EMotionFX
{
// calculate the static based AABB
case BOUNDS_STATIC_BASED:
CalcStaticBasedAABB(&mAABB);
CalcStaticBasedAabb(&m_aabb);
break;
// based on the world space positions of the nodes (least accurate, but fastest)
case BOUNDS_NODE_BASED:
CalcNodeBasedAABB(&mAABB, itemFrequency);
break;
// based on the world space positions of the vertices of the collision meshes (faster and more accurate than mesh based)
case BOUNDS_COLLISIONMESH_BASED:
CalcCollisionMeshBasedAABB(geomLODLevel, &mAABB, itemFrequency);
CalcNodeBasedAabb(&m_aabb, itemFrequency);
break;
// based on the world space positions of the vertices of the meshes (most accurate)
case BOUNDS_MESH_BASED:
CalcMeshBasedAABB(geomLODLevel, &mAABB, itemFrequency);
break;
// based on the world space positions of the vertices of the meshes (most accurate)
case BOUNDS_NODEOBB_BASED:
CalcNodeOBBBasedAABB(&mAABB, itemFrequency);
break;
case BOUNDS_NODEOBBFAST_BASED:
CalcNodeOBBBasedAABBFast(&mAABB, itemFrequency);
UpdateMeshDeformers(0.0f);
CalcMeshBasedAabb(geomLODLevel, &m_aabb, itemFrequency);
break;
// when we're dealing with an unspecified bounding volume update method
default:
MCore::LogInfo("*** EMotionFX::ActorInstance::UpdateBounds() - Unknown boundsType specified! (%d) ***", (uint32)boundsType);
}
}
// calculate the axis aligned bounding box that contains the object oriented boxes of all nodes
void ActorInstance::CalcNodeOBBBasedAABBFast(MCore::AABB* outResult, uint32 nodeFrequency)
{
// init the axis aligned bounding box
outResult->Init();
const Pose* pose = mTransformData->GetCurrentPose();
const Skeleton* skeleton = mActor->GetSkeleton();
// for all nodes, encapsulate the world space positions
uint16 nodeNr;
const uint32 numNodes = GetNumEnabledNodes();
for (uint32 i = 0; i < numNodes; i += nodeFrequency)
// Expand the bounding volume by a tolerance area in case set.
if (!AZ::IsClose(m_boundsExpandBy, 0.0f))
{
nodeNr = GetEnabledNode(i);
Node* node = skeleton->GetNode(nodeNr);
if (node->GetIncludeInBoundsCalc())
{
const MCore::OBB& obb = mActor->GetNodeOBB(nodeNr);
if (obb.CheckIfIsValid() == false)
{
continue;
}
// calculate the corner points of the node in local space
AZ::Vector3 minPoint, maxPoint;
obb.CalcMinMaxPoints(&minPoint, &maxPoint);
// encapsulate the results in the AABB box
const Transform worldTransform = pose->GetWorldSpaceTransform(nodeNr);
outResult->Encapsulate(worldTransform.TransformPoint(minPoint));
outResult->Encapsulate(worldTransform.TransformPoint(maxPoint));
}
}
}
// more accurate node obb based method that uses the 8 corner points of the obb
void ActorInstance::CalcNodeOBBBasedAABB(MCore::AABB* outResult, uint32 nodeFrequency)
{
// init the axis aligned bounding box
outResult->Init();
const Pose* pose = mTransformData->GetCurrentPose();
const Skeleton* skeleton = mActor->GetSkeleton();
// for all nodes, encapsulate the world space positions
AZ::Vector3 cornerPoints[8];
uint16 nodeNr;
const uint32 numNodes = GetNumEnabledNodes();
for (uint32 i = 0; i < numNodes; i += nodeFrequency)
{
nodeNr = GetEnabledNode(i);
Node* node = skeleton->GetNode(nodeNr);
if (node->GetIncludeInBoundsCalc())
{
const MCore::OBB& obb = mActor->GetNodeOBB(nodeNr);
if (obb.CheckIfIsValid() == false)
{
continue;
}
// calculate the 8 corner points
obb.CalcCornerPoints(cornerPoints);
const Transform worldTransform = pose->GetWorldSpaceTransform(nodeNr);
// encapsulate all OBB world space corner points inside the AABB
for (uint32 p = 0; p < 8; ++p)
{
outResult->Encapsulate(worldTransform.TransformPoint(cornerPoints[p]));
}
}
const AZ::Vector3 center = m_aabb.GetCenter();
const AZ::Vector3 halfExtents = m_aabb.GetExtents() * 0.5f;
const AZ::Vector3 scaledHalfExtents = halfExtents * (1.0f + m_boundsExpandBy);
m_aabb.SetMin(center - scaledHalfExtents);
m_aabb.SetMax(center + scaledHalfExtents);
}
}
// calculate the axis aligned bounding box based on the world space positions of the nodes
void ActorInstance::CalcNodeBasedAABB(MCore::AABB* outResult, uint32 nodeFrequency)
void ActorInstance::CalcNodeBasedAabb(AZ::Aabb* outResult, uint32 nodeFrequency)
{
outResult->Init();
*outResult = AZ::Aabb::CreateNull();
const Pose* pose = mTransformData->GetCurrentPose();
const Skeleton* skeleton = mActor->GetSkeleton();
@@ -763,16 +661,15 @@ namespace EMotionFX
nodeNr = GetEnabledNode(i);
if (skeleton->GetNode(nodeNr)->GetIncludeInBoundsCalc())
{
outResult->Encapsulate(pose->GetWorldSpaceTransform(nodeNr).mPosition);
outResult->AddPoint(pose->GetWorldSpaceTransform(nodeNr).mPosition);
}
}
}
// calculate the AABB that contains all world space vertices of all meshes
void ActorInstance::CalcMeshBasedAABB(uint32 geomLODLevel, MCore::AABB* outResult, uint32 vertexFrequency)
void ActorInstance::CalcMeshBasedAabb(uint32 geomLODLevel, AZ::Aabb* outResult, uint32 vertexFrequency)
{
// init the axis aligned bounding box
outResult->Init();
*outResult = AZ::Aabb::CreateNull();
const Pose* pose = mTransformData->GetCurrentPose();
const Skeleton* skeleton = mActor->GetSkeleton();
@@ -800,52 +697,9 @@ namespace EMotionFX
const Transform worldTransform = pose->GetMeshNodeWorldSpaceTransform(geomLODLevel, nodeNr);
// calculate and encapsulate the mesh bounds inside the total mesh box
MCore::AABB meshBox;
mesh->CalcAABB(&meshBox, worldTransform, vertexFrequency);
outResult->Encapsulate(meshBox);
}
}
void ActorInstance::CalcCollisionMeshBasedAABB(uint32 geomLODLevel, MCore::AABB* outResult, uint32 vertexFrequency)
{
// init the axis aligned bounding box
outResult->Init();
const Pose* pose = mTransformData->GetCurrentPose();
const Skeleton* skeleton = mActor->GetSkeleton();
// for all nodes, encapsulate the world space positions
uint16 nodeNr;
const uint32 numNodes = GetNumEnabledNodes();
for (uint32 i = 0; i < numNodes; ++i)
{
nodeNr = GetEnabledNode(i);
Node* node = skeleton->GetNode(nodeNr);
// skip nodes without collision meshes
Mesh* mesh = mActor->GetMesh(geomLODLevel, nodeNr);
if (mesh == nullptr)
{
continue;
}
if (mesh->GetIsCollisionMesh() == false)
{
continue;
}
// if this node should be excluded
if (node->GetIncludeInBoundsCalc() == false)
{
continue;
}
const Transform worldTransform = pose->GetMeshNodeWorldSpaceTransform(geomLODLevel, nodeNr);
// calculate and encapsulate the mesh bounds inside the total mesh box
MCore::AABB meshBox;
mesh->CalcAABB(&meshBox, worldTransform, vertexFrequency);
outResult->Encapsulate(meshBox);
AZ::Aabb meshBox;
mesh->CalcAabb(&meshBox, worldTransform, vertexFrequency);
outResult->AddAabb(meshBox);
}
}
@@ -1567,111 +1421,45 @@ namespace EMotionFX
}
// update the static based aabb dimensions
void ActorInstance::UpdateStaticBasedAABBDimensions()
void ActorInstance::UpdateStaticBasedAabbDimensions()
{
// backup the transform
Transform orgTransform = GetLocalSpaceTransform();
//-------------------------------------
// reset position and scale
SetLocalSpacePosition(AZ::Vector3::CreateZero());
EMFX_SCALECODE(SetLocalSpaceScale(AZ::Vector3(1.0f, 1.0f, 1.0f));)
EMFX_SCALECODE(
SetLocalSpaceScale(AZ::Vector3(1.0f, 1.0f, 1.0f));)
UpdateTransformations(0.0f, true);
UpdateMeshDeformers(0.0f);
// rotate over x, y and z axis
AZ::Vector3 boxMin(FLT_MAX, FLT_MAX, FLT_MAX);
AZ::Vector3 boxMax(-FLT_MAX, -FLT_MAX, -FLT_MAX);
for (uint32 axis = 0; axis < 3; axis++)
// calculate the aabb of this
if (mActor->CheckIfHasMeshes(0))
{
for (uint32 i = 0; i < 360; i += 45) // steps of 45 degrees
{
// rotate a given amount of degrees over the axis we are currently testing
AZ::Vector3 axisVector(0.0f, 0.0f, 0.0f);
axisVector.SetElement(axis, 1.0f);
const float angle = static_cast<float>(i);
SetLocalSpaceRotation(MCore::CreateFromAxisAndAngle(axisVector, MCore::Math::DegreesToRadians(angle)));
UpdateTransformations(0.0f, true);
UpdateMeshDeformers(0.0f);
// calculate the aabb of this
if (mActor->CheckIfHasMeshes(0))
{
CalcMeshBasedAABB(0, &mStaticAABB);
}
else
{
CalcNodeBasedAABB(&mStaticAABB);
}
// find the minimum and maximum
const AZ::Vector3& curMin = mStaticAABB.GetMin();
const AZ::Vector3& curMax = mStaticAABB.GetMax();
if (curMin.GetX() < boxMin.GetX())
{
boxMin.SetX(curMin.GetX());
}
if (curMin.GetY() < boxMin.GetY())
{
boxMin.SetY(curMin.GetY());
}
if (curMin.GetZ() < boxMin.GetZ())
{
boxMin.SetZ(curMin.GetZ());
}
if (curMax.GetX() > boxMax.GetX())
{
boxMax.SetX(curMax.GetX());
}
if (curMax.GetY() > boxMax.GetY())
{
boxMax.SetY(curMax.GetY());
}
if (curMax.GetZ() > boxMax.GetZ())
{
boxMax.SetZ(curMax.GetZ());
}
}
CalcMeshBasedAabb(0, &m_staticAabb);
}
else
{
CalcNodeBasedAabb(&m_staticAabb);
}
mStaticAABB.SetMin(boxMin);
mStaticAABB.SetMax(boxMax);
/*
// calculate the center point of the box
const AZ::Vector3 center = mStaticAABB.CalcMiddle();
// find the maximum of the width, height and depth
const float maxDim = MCore::Max3<float>( mStaticAABB.CalcWidth(), mStaticAABB.CalcHeight(), mStaticAABB.CalcDepth() ) * 0.5f;
// make width, height and depth the same as its maximum
mStaticAABB.SetMin( center + AZ::Vector3(-maxDim, -maxDim, -maxDim) );
mStaticAABB.SetMax( center + AZ::Vector3( maxDim, maxDim, maxDim) );
*/
//-------------------------------------
// restore the transform
mLocalTransform = orgTransform;
}
// calculate the moved static based aabb
void ActorInstance::CalcStaticBasedAABB(MCore::AABB* outResult)
void ActorInstance::CalcStaticBasedAabb(AZ::Aabb* outResult)
{
if (GetIsSkinAttachment())
{
mSelfAttachment->GetAttachToActorInstance()->CalcStaticBasedAABB(outResult);
mSelfAttachment->GetAttachToActorInstance()->CalcStaticBasedAabb(outResult);
return;
}
*outResult = mStaticAABB;
*outResult = m_staticAabb;
EMFX_SCALECODE(
outResult->SetMin(mStaticAABB.GetMin() * mWorldTransform.mScale);
outResult->SetMax(mStaticAABB.GetMax() * mWorldTransform.mScale);)
outResult->SetMin(m_staticAabb.GetMin() * mWorldTransform.mScale);
outResult->SetMax(m_staticAabb.GetMax() * mWorldTransform.mScale);)
outResult->Translate(mWorldTransform.mPosition);
}
// adjust the animgraph instance
// adjust the anim graph instance
void ActorInstance::SetAnimGraphInstance(AnimGraphInstance* instance)
{
mAnimGraphInstance = instance;
@@ -1774,29 +1562,29 @@ namespace EMotionFX
SetFlag(BOOL_BOUNDSUPDATEENABLED, enable);
}
void ActorInstance::SetStaticBasedAABB(const MCore::AABB& aabb)
void ActorInstance::SetStaticBasedAabb(const AZ::Aabb& aabb)
{
mStaticAABB = aabb;
m_staticAabb = aabb;
}
void ActorInstance::GetStaticBasedAABB(MCore::AABB* outAABB)
void ActorInstance::GetStaticBasedAabb(AZ::Aabb* outAabb)
{
*outAABB = mStaticAABB;
*outAabb = m_staticAabb;
}
const MCore::AABB& ActorInstance::GetStaticBasedAABB() const
const AZ::Aabb& ActorInstance::GetStaticBasedAabb() const
{
return mStaticAABB;
return m_staticAabb;
}
const MCore::AABB& ActorInstance::GetAABB() const
const AZ::Aabb& ActorInstance::GetAabb() const
{
return mAABB;
return m_aabb;
}
void ActorInstance::SetAABB(const MCore::AABB& aabb)
void ActorInstance::SetAabb(const AZ::Aabb& aabb)
{
mAABB = aabb;
m_aabb = aabb;
}
uint32 ActorInstance::GetNumAttachments() const
@@ -2018,23 +1806,20 @@ namespace EMotionFX
mVisualizeScale = 0.0f;
UpdateMeshDeformers(0.0f);
MCore::AABB box;
CalcCollisionMeshBasedAABB(0, &box);
if (box.CheckIfIsValid())
AZ::Aabb box = AZ::Aabb::CreateNull();
CalcNodeBasedAabb(&box);
if (box.IsValid())
{
mVisualizeScale = MCore::Max<float>(mVisualizeScale, box.CalcRadius());
const float boxRadius = AZ::Vector3(box.GetMax() - box.GetMin()).GetLength() * 0.5f;
mVisualizeScale = MCore::Max<float>(mVisualizeScale, boxRadius);
}
CalcNodeBasedAABB(&box);
if (box.CheckIfIsValid())
CalcMeshBasedAabb(0, &box);
if (box.IsValid())
{
mVisualizeScale = MCore::Max<float>(mVisualizeScale, box.CalcRadius());
}
CalcMeshBasedAABB(0, &box);
if (box.CheckIfIsValid())
{
mVisualizeScale = MCore::Max<float>(mVisualizeScale, box.CalcRadius());
const float boxRadius = AZ::Vector3(box.GetMax() - box.GetMin()).GetLength() * 0.5f;
mVisualizeScale = MCore::Max<float>(mVisualizeScale, boxRadius);
}
mVisualizeScale *= 0.01f;
@@ -60,9 +60,6 @@ namespace EMotionFX
{
BOUNDS_NODE_BASED = 0, /**< Calculate the bounding volumes based on the world space node positions. */
BOUNDS_MESH_BASED = 1, /**< Calculate the bounding volumes based on the world space vertex positions. */
BOUNDS_COLLISIONMESH_BASED = 2, /**< Calculate the bounding volumes based on the world space collision mesh vertex positions. */
BOUNDS_NODEOBB_BASED = 3, /**< Calculate the bounding volumes based on the oriented bounding boxes of the nodes. Uses all 8 corner points of the individual node OBB boxes. */
BOUNDS_NODEOBBFAST_BASED = 4, /**< Calculate the bounding volumes based on the oriented bounding boxes of the nodes. Uses the min and max point of the individual node OBB boxes. This is less accurate but faster. */
BOUNDS_STATIC_BASED = 5 /**< Calculate the bounding volumes based on an approximate box, based on the mesh bounds, and move this box along with the actor instance position. */
};
@@ -348,6 +345,14 @@ namespace EMotionFX
*/
EBoundsType GetBoundsUpdateType() const;
/**
* Get the normalized percentage that the calculated bounding box is expanded with.
* This can be used to add a tolerance area to the calculated bounding box to avoid clipping the character too early.
* A static bounding box together with the expansion is the recommended way for maximum performance.
* @result A value of 1.0 means that the calculated bounding box won't be expanded at all, while 2.0 means it is twice the size.
*/
float GetExpandBoundsBy() const { return m_boundsExpandBy; }
/**
* Get the bounding volume auto-update item frequency.
* A value of 1 would mean every node or vertex will be taken into account in the bounds calculation.
@@ -376,11 +381,19 @@ namespace EMotionFX
/**
* Set the bounding volume auto-update type.
* This can be either based on the node's world space positions, the mesh vertex world space positions, or the
* collision mesh vertex world space postitions.
* collision mesh vertex world space positions.
* @param bType The bounding volume update type.
*/
void SetBoundsUpdateType(EBoundsType bType);
/**
* Set the normalized percentage that the calculated bounding box should be expanded with.
* This can be used to add a tolerance area to the calculated bounding box to avoid clipping the character too early.
* A static bounding box together with the expansion is the recommended way for maximum performance.
* @param[in] expandBy A value of 1.0 means that the calculated bounding box won't be expanded at all, while 2.0 means it will be twice the size.
*/
void SetExpandBoundsBy(float expandBy) { m_boundsExpandBy = expandBy; }
/**
* Set the bounding volume auto-update item frequency.
* A value of 1 would mean every node or vertex will be taken into account in the bounds calculation.
@@ -420,11 +433,11 @@ namespace EMotionFX
* This function is generally only executed once, when creating the actor instance.
* The CalcStaticBasedAABB function then simply translates this box along with the actor instance's position.
*/
void UpdateStaticBasedAABBDimensions();
void UpdateStaticBasedAabbDimensions();
void SetStaticBasedAABB(const MCore::AABB& aabb);
void GetStaticBasedAABB(MCore::AABB* outAABB);
const MCore::AABB& GetStaticBasedAABB() const;
void SetStaticBasedAabb(const AZ::Aabb& aabb);
void GetStaticBasedAabb(AZ::Aabb* outAabb);
const AZ::Aabb& GetStaticBasedAabb() const;
/**
* Calculate an axis aligned bounding box that can be used as static AABB. It is static in the way that the volume does not change. It can however be translated as it will move
@@ -434,7 +447,7 @@ namespace EMotionFX
* If there are no meshes present, a widened node based box will be used instead as basis.
* @param outResult The resulting bounding box, moved along with the actor instance's position.
*/
void CalcStaticBasedAABB(MCore::AABB* outResult);
void CalcStaticBasedAabb(AZ::Aabb* outResult);
/**
* Calculate the axis aligned bounding box based on the world space positions of the nodes.
@@ -442,7 +455,7 @@ namespace EMotionFX
* @param nodeFrequency This will include every "nodeFrequency"-th node. So a value of 1 will include all nodes. A value of 2 would
* process every second node, meaning that half of the nodes will be skipped. A value of 4 would process every 4th node, etc.
*/
void CalcNodeBasedAABB(MCore::AABB* outResult, uint32 nodeFrequency = 1);
void CalcNodeBasedAabb(AZ::Aabb* outResult, uint32 nodeFrequency = 1);
/**
* Calculate the axis aligned bounding box based on the world space vertex coordinates of the meshes.
@@ -452,43 +465,7 @@ namespace EMotionFX
* @param vertexFrequency This includes every "vertexFrequency"-th vertex. So for example a value of 2 would skip every second vertex and
* so will process half of the vertices. A value of 4 would process only each 4th vertex, etc.
*/
void CalcMeshBasedAABB(uint32 geomLODLevel, MCore::AABB* outResult, uint32 vertexFrequency = 1);
/**
* Calculate the axis aligned bounding box based on the world space vertex coordinates of the collision meshes.
* If the actor has no collision meshes, the created box will be invalid.
* @param geomLODLevel The geometry LOD level to calculate the box for.
* @param outResult The AABB where this method should store the resulting box in.
* @param vertexFrequency This includes every "vertexFrequency"-th vertex. So for example a value of 2 would skip every second vertex and
* so will process half of the vertices. A value of 4 would process only each 4th vertex, etc.
*/
void CalcCollisionMeshBasedAABB(uint32 geomLODLevel, MCore::AABB* outResult, uint32 vertexFrequency = 1);
/**
* Calculate the axis aligned bounding box that contains the object oriented boxes of all nodes.
* The OBB (oriented bounding box) of each node is calculated by fitting an OBB to its mesh.
* The OBB of nodes that act as bones and have no meshes themselves are fit to the set of vertices that are influenced by the given bone.
* This method will give more accurate results than the CalcNodeBasedAABB method in trade for a bit lower performance.
* Also one big advantage of this method is that you can use these bounds for hit detection, without having artists setup collision meshes.
* @param outResult The AABB where this method should store the resulting box in.
* @param nodeFrequency This will include every "nodeFrequency"-th node. So a value of 1 will include all nodes. A value of 2 would
* process every second node, meaning that half of the nodes will be skipped. A value of 4 would process every 4th node, etc.
*/
void CalcNodeOBBBasedAABB(MCore::AABB* outResult, uint32 nodeFrequency = 1);
/**
* Calculate the axis aligned bounding box that contains the object oriented boxes of all nodes.
* The OBB (oriented bounding box) of each node is calculated by fitting an OBB to its mesh.
* The OBB of nodes that act as bones and have no meshes themselves are fit to the set of vertices that are influenced by the given bone.
* This method will give more accurate results than the CalcNodeBasedAABB method in trade for a bit lower performance.
* Also one big advantage of this method is that you can use these bounds for hit detection, without having artists setup collision meshes.
* NOTE: this is a faster variant from the CalcNodeOBBBasedAABB method. The difference is that this method only transforms the min and max point of the box in local space.
* Therefore it is less accurate, but it might still be enough. The original CalcNodeOBBBasedAABB method calculates the 8 corner points of the node obb boxes.
* @param outResult The AABB where this method should store the resulting box in.
* @param nodeFrequency This will include every "nodeFrequency"-th node. So a value of 1 will include all nodes. A value of 2 would
* process every second node, meaning that half of the nodes will be skipped. A value of 4 would process every 4th node, etc.
*/
void CalcNodeOBBBasedAABBFast(MCore::AABB* outResult, uint32 nodeFrequency = 1);
void CalcMeshBasedAabb(uint32 geomLODLevel, AZ::Aabb* outResult, uint32 vertexFrequency = 1);
/**
* Get the axis aligned bounding box.
@@ -496,14 +473,14 @@ namespace EMotionFX
* That method is also called automatically when the bounds auto-update feature is enabled.
* @result The axis aligned bounding box.
*/
const MCore::AABB& GetAABB() const;
const AZ::Aabb& GetAabb() const;
/**
* Set the axis aligned bounding box.
* Please beware that this box will get automatically overwritten when automatic bounds update is enabled.
* @param aabb The axis aligned bounding box to store.
*/
void SetAABB(const MCore::AABB& aabb);
void SetAabb(const AZ::Aabb& aabb);
//-------------------------------------------------------------------------------------------
@@ -887,8 +864,8 @@ namespace EMotionFX
private:
TransformData* mTransformData; /**< The transformation data for this instance. */
MCore::AABB mAABB; /**< The axis aligned bounding box. */
MCore::AABB mStaticAABB; /**< A static pre-calculated bounding box, which we can move along with the position of the actor instance, and use for visibility checks. */
AZ::Aabb m_aabb; /**< The axis aligned bounding box. */
AZ::Aabb m_staticAabb; /**< A static pre-calculated bounding box, which we can move along with the position of the actor instance, and use for visibility checks. */
Transform mLocalTransform = Transform::CreateIdentity();
Transform mWorldTransform = Transform::CreateIdentity();
@@ -907,7 +884,7 @@ namespace EMotionFX
MotionSystem* mMotionSystem; /**< The motion system, that handles all motion playback and blending etc. */
AnimGraphInstance* mAnimGraphInstance; /**< A pointer to the anim graph instance, which can be nullptr when there is no anim graph instance. */
AZStd::unique_ptr<RagdollInstance> m_ragdollInstance;
MCore::Mutex mLock; /**< The multithread lock. */
MCore::Mutex mLock; /**< The multi-thread lock. */
void* mCustomData; /**< A pointer to custom data for this actor. This could be a pointer to your engine or game object for example. */
AZ::Entity* m_entity; /**< The entity to which the actor instance belongs to. */
float mBoundsUpdateFrequency; /**< The bounds update frequency. Which is a time value in seconds. */
@@ -920,7 +897,8 @@ namespace EMotionFX
uint32 mBoundsUpdateItemFreq; /**< The bounds update item counter step size. A value of 1 means every vertex/node, a value of 2 means every second vertex/node, etc. */
uint32 mID; /**< The unique identification number for the actor instance. */
uint32 mThreadIndex; /**< The thread index. This specifies the thread number this actor instance is being processed in. */
EBoundsType mBoundsUpdateType; /**< The bounds update type (node based, mesh based or colliison mesh based). */
EBoundsType mBoundsUpdateType; /**< The bounds update type (node based, mesh based or collision mesh based). */
float m_boundsExpandBy = 0.25f; /**< Expand bounding box by normalized percentage. (Default: 25% greater than the calculated bounding box) */
uint8 mNumAttachmentRefs; /**< Specifies how many actor instances use this actor instance as attachment. */
uint8 mBoolFlags; /**< Boolean flags. */
@@ -105,7 +105,6 @@ namespace EMotionFX
}
// If both x and y inputs have connections
//MCore::Quaternion x = MCore::AzQuatToEmfxQuat(m_defaultValue);
AZ::Quaternion x = m_defaultValue;
AZ::Quaternion y = x;
if (mConnections.size() == 2)
@@ -108,7 +108,7 @@ namespace EMotionFX
// a node header
// (not aligned)
struct Actor_Node
struct Actor_Node2
{
FileQuaternion mLocalQuat; // the local rotation (before hierarchy)
FileVector3 mLocalPos; // the local translation (before hierarchy)
@@ -117,7 +117,6 @@ namespace EMotionFX
uint32 mParentIndex;// parent node number, or 0xFFFFFFFF in case of a root node
uint32 mNumChilds; // the number of child nodes
uint8 mNodeFlags; // #1 bit boolean specifies whether we have to include this node in the bounds calculation or not
float mOBB[16];
// followed by:
// string : node name (the unique name of the node)
@@ -200,14 +199,11 @@ namespace EMotionFX
// uint16 [mNumNodes]
};
// (aligned)
struct Actor_Nodes
struct Actor_Nodes2
{
uint32 mNumNodes;
uint32 mNumRootNodes;
FileVector3 mStaticBoxMin;
FileVector3 mStaticBoxMax;
// followed by Actor_Node4[mNumNodes] or Actor_NODE5[mNumNodes] (for v2)
};
@@ -355,12 +355,9 @@ namespace EMotionFX
return mLoggingActive;
}
//=================================================================================================
// a chunk that contains all nodes in one chunk
bool ChunkProcessorActorNodes::Process(MCore::File* file, Importer::ImportParameters& importParams)
bool ChunkProcessorActorNodes2::Process(MCore::File* file, Importer::ImportParameters& importParams)
{
const MCore::Endian::EEndianType endianType = importParams.mEndianType;
Actor* actor = importParams.mActor;
@@ -369,28 +366,12 @@ namespace EMotionFX
MCORE_ASSERT(actor);
Skeleton* skeleton = actor->GetSkeleton();
FileFormat::Actor_Nodes nodesHeader;
file->Read(&nodesHeader, sizeof(FileFormat::Actor_Nodes));
FileFormat::Actor_Nodes2 nodesHeader;
file->Read(&nodesHeader, sizeof(FileFormat::Actor_Nodes2));
// convert endian
MCore::Endian::ConvertUnsignedInt32(&nodesHeader.mNumNodes, endianType);
MCore::Endian::ConvertUnsignedInt32(&nodesHeader.mNumRootNodes, endianType);
MCore::Endian::ConvertFloat(&nodesHeader.mStaticBoxMin.mX, endianType);
MCore::Endian::ConvertFloat(&nodesHeader.mStaticBoxMin.mY, endianType);
MCore::Endian::ConvertFloat(&nodesHeader.mStaticBoxMin.mZ, endianType);
MCore::Endian::ConvertFloat(&nodesHeader.mStaticBoxMax.mX, endianType);
MCore::Endian::ConvertFloat(&nodesHeader.mStaticBoxMax.mY, endianType);
MCore::Endian::ConvertFloat(&nodesHeader.mStaticBoxMax.mZ, endianType);
// convert endian and coord system of the static box
AZ::Vector3 boxMin(nodesHeader.mStaticBoxMin.mX, nodesHeader.mStaticBoxMin.mY, nodesHeader.mStaticBoxMin.mZ);
AZ::Vector3 boxMax(nodesHeader.mStaticBoxMax.mX, nodesHeader.mStaticBoxMax.mY, nodesHeader.mStaticBoxMax.mZ);
// build the box and set it
MCore::AABB staticBox;
staticBox.SetMin(boxMin);
staticBox.SetMax(boxMax);
actor->SetStaticAABB(staticBox);
// pre-allocate space for the nodes
actor->SetNumNodes(nodesHeader.mNumNodes);
@@ -410,8 +391,8 @@ namespace EMotionFX
for (uint32 n = 0; n < nodesHeader.mNumNodes; ++n)
{
// read the node header
FileFormat::Actor_Node nodeChunk;
file->Read(&nodeChunk, sizeof(FileFormat::Actor_Node));
FileFormat::Actor_Node2 nodeChunk;
file->Read(&nodeChunk, sizeof(FileFormat::Actor_Node2));
// read the node name
const char* nodeName = SharedHelperData::ReadString(file, importParams.mSharedData, endianType);
@@ -420,7 +401,6 @@ namespace EMotionFX
MCore::Endian::ConvertUnsignedInt32(&nodeChunk.mParentIndex, endianType);
MCore::Endian::ConvertUnsignedInt32(&nodeChunk.mSkeletalLODs, endianType);
MCore::Endian::ConvertUnsignedInt32(&nodeChunk.mNumChilds, endianType);
MCore::Endian::ConvertFloat(&nodeChunk.mOBB[0], endianType, 16);
// show the name of the node, the parent and the number of children
if (GetLogging())
@@ -453,11 +433,6 @@ namespace EMotionFX
ConvertScale(&scale, endianType);
ConvertQuaternion(&rot, endianType);
// make sure the input data is normalized
// TODO: this isn't really needed as we already normalized?
//rot.FastNormalize();
//scaleRot.FastNormalize();
// set the local transform
Transform bindTransform;
bindTransform.mPosition = pos;
@@ -503,23 +478,6 @@ namespace EMotionFX
skeleton->AddRootNode(nodeIndex);
}
// OBB
AZ::Matrix4x4 obbMatrix4x4 = AZ::Matrix4x4::CreateFromRowMajorFloat16(nodeChunk.mOBB);
const AZ::Vector3 obbCenter = obbMatrix4x4.GetTranslation();
const AZ::Vector3 obbExtents = obbMatrix4x4.GetRowAsVector3(3);
// initialize the OBB
MCore::OBB obb;
obb.SetCenter(obbCenter);
obb.SetExtents(obbExtents);
// need to transpose to go from row major to column major
const AZ::Matrix3x3 obbMatrix3x3 = AZ::Matrix3x3::CreateFromMatrix4x4(obbMatrix4x4).GetTranspose();
const AZ::Transform obbTransform = AZ::Transform::CreateFromMatrix3x3AndTranslation(obbMatrix3x3, obbExtents);
obb.SetTransformation(obbTransform);
actor->SetNodeOBB(nodeIndex, obb);
if (GetLogging())
{
MCore::LogDetailedInfo(" - Position: x=%f, y=%f, z=%f",
@@ -256,7 +256,6 @@ namespace EMotionFX
virtual ~ChunkProcessor();
};
//-------------------------------------------------------------------------------------------------
/**
@@ -287,7 +286,7 @@ namespace EMotionFX
EMFX_CHUNKPROCESSOR(ChunkProcessorActorInfo3, FileFormat::ACTOR_CHUNK_INFO, 3)
EMFX_CHUNKPROCESSOR(ChunkProcessorActorProgMorphTarget, FileFormat::ACTOR_CHUNK_STDPROGMORPHTARGET, 1)
EMFX_CHUNKPROCESSOR(ChunkProcessorActorNodeGroups, FileFormat::ACTOR_CHUNK_NODEGROUPS, 1)
EMFX_CHUNKPROCESSOR(ChunkProcessorActorNodes, FileFormat::ACTOR_CHUNK_NODES, 1)
EMFX_CHUNKPROCESSOR(ChunkProcessorActorNodes2, FileFormat::ACTOR_CHUNK_NODES, 2)
EMFX_CHUNKPROCESSOR(ChunkProcessorActorProgMorphTargets, FileFormat::ACTOR_CHUNK_STDPMORPHTARGETS, 1)
EMFX_CHUNKPROCESSOR(ChunkProcessorActorProgMorphTargets2, FileFormat::ACTOR_CHUNK_STDPMORPHTARGETS, 2)
EMFX_CHUNKPROCESSOR(ChunkProcessorActorNodeMotionSources, FileFormat::ACTOR_CHUNK_NODEMOTIONSOURCES, 1)
@@ -352,7 +352,7 @@ namespace EMotionFX
}
// post create init
actor->PostCreateInit(actorSettings.mMakeGeomLODsCompatibleWithSkeletalLODs, false, actorSettings.mUnitTypeConvert);
actor->PostCreateInit(actorSettings.mMakeGeomLODsCompatibleWithSkeletalLODs, actorSettings.mUnitTypeConvert);
}
// close the file and return a pointer to the actor we loaded
@@ -846,7 +846,7 @@ namespace EMotionFX
RegisterChunkProcessor(aznew ChunkProcessorActorInfo3());
RegisterChunkProcessor(aznew ChunkProcessorActorProgMorphTarget());
RegisterChunkProcessor(aznew ChunkProcessorActorNodeGroups());
RegisterChunkProcessor(aznew ChunkProcessorActorNodes());
RegisterChunkProcessor(aznew ChunkProcessorActorNodes2());
RegisterChunkProcessor(aznew ChunkProcessorActorProgMorphTargets());
RegisterChunkProcessor(aznew ChunkProcessorActorProgMorphTargets2());
RegisterChunkProcessor(aznew ChunkProcessorActorNodeMotionSources());
@@ -1372,20 +1372,17 @@ namespace EMotionFX
}
void Mesh::CalcAABB(MCore::AABB* outBoundingBox, const Transform& transform, uint32 vertexFrequency)
void Mesh::CalcAabb(AZ::Aabb* outBoundingBox, const Transform& transform, uint32 vertexFrequency)
{
MCORE_ASSERT(vertexFrequency >= 1);
*outBoundingBox = AZ::Aabb::CreateNull();
// init the bounding box
outBoundingBox->Init();
// get the position data
AZ::Vector3* positions = (AZ::Vector3*)FindVertexData(ATTRIB_POSITIONS);
const uint32 numVerts = GetNumVertices();
for (uint32 i = 0; i < numVerts; i += vertexFrequency)
{
outBoundingBox->Encapsulate(transform.TransformPoint(positions[i]));
outBoundingBox->AddPoint(transform.TransformPoint(positions[i]));
}
}
+2 -1
View File
@@ -9,6 +9,7 @@
#pragma once
#include "EMotionFXConfig.h"
#include <AzCore/Math/Aabb.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/std/string/string.h>
@@ -571,7 +572,7 @@ namespace EMotionFX
* @param vertexFrequency This is the for loop increase counter value. A value of 1 means every vertex will be processed
* while a value of 2 means every second vertex, etc. The value must be 1 or higher.
*/
void CalcAABB(MCore::AABB* outBoundingBox, const Transform& transform, uint32 vertexFrequency = 1);
void CalcAabb(AZ::Aabb* outBoundingBox, const Transform& transform, uint32 vertexFrequency = 1);
/**
* The mesh type used to indicate if a mesh is either static, like a cube or building, cpu deformed, if it needs to be processed on the CPU, or GPU deformed if it can be processed fully on the GPU.
@@ -98,7 +98,6 @@ namespace EMotionFX
result->mChildIndices = mChildIndices;
//result->mImportanceFactor = mImportanceFactor;
result->mNodeFlags = mNodeFlags;
result->mOBB = mOBB;
result->mSemanticNameID = mSemanticNameID;
// copy the node attributes
@@ -55,8 +55,6 @@ namespace EMStudio
const char* RenderOptions::s_nodeAABBColorOptionName = "nodeAABBColor";
const char* RenderOptions::s_staticAABBColorOptionName = "staticAABBColor";
const char* RenderOptions::s_meshAABBColorOptionName = "meshAABBColor";
const char* RenderOptions::s_collisionMeshAABBColorOptionName = "collisionMeshAABBColor";
const char* RenderOptions::s_OBBsColorOptionName = "OBBsColor";
const char* RenderOptions::s_lineSkeletonColorOptionName = "lineSkeletonColor_v2";
const char* RenderOptions::s_skeletonColorOptionName = "skeletonColor";
const char* RenderOptions::s_selectionColorOptionName = "selectionColor";
@@ -108,8 +106,6 @@ namespace EMStudio
, m_nodeAABBColor(1.0f, 0.0f, 0.0f, 1.0f)
, m_staticAABBColor(0.0f, 0.7f, 0.7f, 1.0f)
, m_meshAABBColor(0.0f, 0.0f, 0.7f, 1.0f)
, m_collisionMeshAABBColor(0.0f, 0.7f, 0.0f, 1.0f)
, m_OBBsColor(1.0f, 1.0f, 0.0f, 1.0f)
, m_lineSkeletonColor(0.33333f, 1.0f, 0.0f, 1.0f)
, m_skeletonColor(0.19f, 0.58f, 0.19f, 1.0f)
, m_selectionColor(1.0f, 1.0f, 1.0f, 1.0f)
@@ -169,8 +165,6 @@ namespace EMStudio
SetNodeAABBColor(other.GetNodeAABBColor());
SetStaticAABBColor(other.GetStaticAABBColor());
SetMeshAABBColor(other.GetMeshAABBColor());
SetCollisionMeshAABBColor(other.GetCollisionMeshAABBColor());
SetOBBsColor(other.GetOBBsColor());
SetLineSkeletonColor(other.GetLineSkeletonColor());
SetSkeletonColor(other.GetSkeletonColor());
SetSelectionColor(other.GetSelectionColor());
@@ -206,9 +200,7 @@ namespace EMStudio
settings->setValue(s_nodeAABBColorOptionName, ColorToString(m_nodeAABBColor));
settings->setValue(s_staticAABBColorOptionName, ColorToString(m_staticAABBColor));
settings->setValue(s_meshAABBColorOptionName, ColorToString(m_meshAABBColor));
settings->setValue(s_collisionMeshAABBColorOptionName, ColorToString(m_collisionMeshAABBColor));
settings->setValue(s_collisionMeshColorOptionName, ColorToString(m_collisionMeshColor));
settings->setValue(s_OBBsColorOptionName, ColorToString(m_OBBsColor));
settings->setValue(s_lineSkeletonColorOptionName, ColorToString(m_lineSkeletonColor));
settings->setValue(s_skeletonColorOptionName, ColorToString(m_skeletonColor));
settings->setValue(s_selectionColorOptionName, ColorToString(m_selectionColor));
@@ -275,9 +267,7 @@ namespace EMStudio
options.m_nodeAABBColor = StringToColor(settings->value(s_nodeAABBColorOptionName, ColorToString(options.m_nodeAABBColor)).toString());
options.m_staticAABBColor = StringToColor(settings->value(s_staticAABBColorOptionName, ColorToString(options.m_staticAABBColor)).toString());
options.m_meshAABBColor = StringToColor(settings->value(s_meshAABBColorOptionName, ColorToString(options.m_meshAABBColor)).toString());
options.m_collisionMeshAABBColor = StringToColor(settings->value(s_collisionMeshAABBColorOptionName, ColorToString(options.m_collisionMeshAABBColor)).toString());
options.m_collisionMeshColor = StringToColor(settings->value(s_collisionMeshColorOptionName, ColorToString(options.m_collisionMeshColor)).toString());
options.m_OBBsColor = StringToColor(settings->value(s_OBBsColorOptionName, ColorToString(options.m_OBBsColor)).toString());
options.m_lineSkeletonColor = StringToColor(settings->value(s_lineSkeletonColorOptionName, ColorToString(options.m_lineSkeletonColor)).toString());
options.m_skeletonColor = StringToColor(settings->value(s_skeletonColorOptionName, ColorToString(options.m_skeletonColor)).toString());
options.m_selectionColor = StringToColor(settings->value(s_selectionColorOptionName, ColorToString(options.m_selectionColor)).toString());
@@ -393,8 +383,6 @@ namespace EMStudio
->Field(s_nodeAABBColorOptionName, &RenderOptions::m_nodeAABBColor)
->Field(s_staticAABBColorOptionName, &RenderOptions::m_staticAABBColor)
->Field(s_meshAABBColorOptionName, &RenderOptions::m_meshAABBColor)
->Field(s_collisionMeshAABBColorOptionName, &RenderOptions::m_collisionMeshAABBColor)
->Field(s_OBBsColorOptionName, &RenderOptions::m_OBBsColor)
->Field(s_lineSkeletonColorOptionName, &RenderOptions::m_lineSkeletonColor)
->Field(s_skeletonColorOptionName, &RenderOptions::m_skeletonColor)
->Field(s_selectionColorOptionName, &RenderOptions::m_selectionColor)
@@ -552,12 +540,6 @@ namespace EMStudio
->DataElement(AZ::Edit::UIHandlers::Default, &RenderOptions::m_meshAABBColor, "Mesh based AABB color",
"Color for the runtime-updated AABB calculated based on the deformed meshes.")
->Attribute(AZ::Edit::Attributes::ChangeNotify, &RenderOptions::OnMeshAABBColorChangedCallback)
->DataElement(AZ::Edit::UIHandlers::Default, &RenderOptions::m_collisionMeshAABBColor, "CollisionMesh based AABB color",
"Color for the runtime-updated AABB calculated based on the deformed collision meshes.")
->Attribute(AZ::Edit::Attributes::ChangeNotify, &RenderOptions::OnCollisionMeshAABBColorChangedCallback)
->DataElement(AZ::Edit::UIHandlers::Default, &RenderOptions::m_OBBsColor, "Joint OBB color",
"Color used for the pre-calculated joint oriented bounding boxes.")
->Attribute(AZ::Edit::Attributes::ChangeNotify, &RenderOptions::OnOBBsColorChangedCallback)
->DataElement(AZ::Edit::UIHandlers::Default, &RenderOptions::m_lineSkeletonColor, "Line based skeleton color",
"Line-based skeleton color.")
->Attribute(AZ::Edit::Attributes::ChangeNotify, &RenderOptions::OnLineSkeletonColorChangedCallback)
@@ -903,24 +885,6 @@ namespace EMStudio
}
}
void RenderOptions::SetCollisionMeshAABBColor(const AZ::Color& collisionMeshAABBColor)
{
if (!collisionMeshAABBColor.IsClose(m_collisionMeshAABBColor))
{
m_collisionMeshAABBColor = collisionMeshAABBColor;
OnCollisionMeshAABBColorChangedCallback();
}
}
void RenderOptions::SetOBBsColor(const AZ::Color& OBBsColor)
{
if (!OBBsColor.IsClose(m_OBBsColor))
{
m_OBBsColor = OBBsColor;
OnOBBsColorChangedCallback();
}
}
void RenderOptions::SetLineSkeletonColor(const AZ::Color& lineSkeletonColor)
{
if (!lineSkeletonColor.IsClose(m_lineSkeletonColor))
@@ -1258,16 +1222,6 @@ namespace EMStudio
PluginOptionsNotificationsBus::Event(s_meshAABBColorOptionName, &PluginOptionsNotificationsBus::Events::OnOptionChanged, s_meshAABBColorOptionName);
}
void RenderOptions::OnCollisionMeshAABBColorChangedCallback() const
{
PluginOptionsNotificationsBus::Event(s_collisionMeshAABBColorOptionName, &PluginOptionsNotificationsBus::Events::OnOptionChanged, s_collisionMeshAABBColorOptionName);
}
void RenderOptions::OnOBBsColorChangedCallback() const
{
PluginOptionsNotificationsBus::Event(s_OBBsColorOptionName, &PluginOptionsNotificationsBus::Events::OnOptionChanged, s_OBBsColorOptionName);
}
void RenderOptions::OnLineSkeletonColorChangedCallback() const
{
PluginOptionsNotificationsBus::Event(s_lineSkeletonColorOptionName, &PluginOptionsNotificationsBus::Events::OnOptionChanged, s_lineSkeletonColorOptionName);
@@ -59,8 +59,6 @@ namespace EMStudio
static const char* s_nodeAABBColorOptionName;
static const char* s_staticAABBColorOptionName;
static const char* s_meshAABBColorOptionName;
static const char* s_collisionMeshAABBColorOptionName;
static const char* s_OBBsColorOptionName;
static const char* s_lineSkeletonColorOptionName;
static const char* s_skeletonColorOptionName;
static const char* s_selectionColorOptionName;
@@ -191,12 +189,6 @@ namespace EMStudio
AZ::Color GetMeshAABBColor() const { return m_meshAABBColor; }
void SetMeshAABBColor(const AZ::Color& meshAABBColor);
AZ::Color GetCollisionMeshAABBColor() const { return m_collisionMeshAABBColor; }
void SetCollisionMeshAABBColor(const AZ::Color& collisionMeshAABBColor);
AZ::Color GetOBBsColor() const { return m_OBBsColor; }
void SetOBBsColor(const AZ::Color& OBBsColor);
AZ::Color GetLineSkeletonColor() const { return m_lineSkeletonColor; }
void SetLineSkeletonColor(const AZ::Color& lineSkeletonColor);
@@ -303,8 +295,6 @@ namespace EMStudio
void OnNodeAABBColorChangedCallback() const;
void OnStaticAABBColorChangedCallback() const;
void OnMeshAABBColorChangedCallback() const;
void OnCollisionMeshAABBColorChangedCallback() const;
void OnOBBsColorChangedCallback() const;
void OnLineSkeletonColorChangedCallback() const;
void OnSkeletonColorChangedCallback() const;
void OnSelectionColorChangedCallback() const;
@@ -361,8 +351,6 @@ namespace EMStudio
AZ::Color m_nodeAABBColor;
AZ::Color m_staticAABBColor;
AZ::Color m_meshAABBColor;
AZ::Color m_collisionMeshAABBColor;
AZ::Color m_OBBsColor;
AZ::Color m_lineSkeletonColor;
AZ::Color m_skeletonColor;
AZ::Color m_selectionColor;
@@ -266,8 +266,7 @@ namespace EMStudio
return;
}
MCore::AABB aabb;
aabb.Init();
AZ::Aabb aabb = AZ::Aabb::CreateNull();
const EMotionFX::Actor* actor = actorInstance->GetActor();
const EMotionFX::Skeleton* skeleton = actor->GetSkeleton();
@@ -276,21 +275,20 @@ namespace EMStudio
for (const EMotionFX::Node* joint : joints)
{
const AZ::Vector3 jointPosition = pose->GetWorldSpaceTransform(joint->GetNodeIndex()).mPosition;
aabb.Encapsulate(jointPosition);
aabb.AddPoint(jointPosition);
const AZ::u32 childCount = joint->GetNumChildNodes();
for (AZ::u32 i = 0; i < childCount; ++i)
{
EMotionFX::Node* childJoint = skeleton->GetNode(joint->GetChildIndex(i));
const AZ::Vector3 childPosition = pose->GetWorldSpaceTransform(childJoint->GetNodeIndex()).mPosition;
aabb.Encapsulate(childPosition);
aabb.AddPoint(childPosition);
}
}
if (aabb.CheckIfIsValid())
if (aabb.IsValid())
{
aabb.Widen(aabb.CalcRadius());
aabb.Expand(AZ::Vector3(aabb.GetExtents().GetLength() * 0.5f));
bool isFollowModeActive = false;
for (const RenderViewWidget* viewWidget : m_viewWidgets)
@@ -619,35 +617,30 @@ namespace EMStudio
EMotionFX::ActorInstance* actorInstance = EMotionFX::ActorInstance::Create(mActor);
actorInstance->UpdateMeshDeformers(0.0f, true);
MCore::AABB aabb;
actorInstance->CalcMeshBasedAABB(0, &aabb);
AZ::Aabb aabb;
actorInstance->CalcMeshBasedAabb(0, &aabb);
if (aabb.CheckIfIsValid() == false)
if (!aabb.IsValid())
{
actorInstance->CalcNodeOBBBasedAABB(&aabb);
actorInstance->CalcNodeBasedAabb(&aabb);
}
if (aabb.CheckIfIsValid() == false)
{
actorInstance->CalcNodeBasedAABB(&aabb);
}
mCharacterHeight = aabb.CalcHeight();
mCharacterHeight = aabb.GetExtents().GetZ();
mOffsetFromTrajectoryNode = aabb.GetMin().GetY() + (mCharacterHeight * 0.5f);
actorInstance->Destroy();
// scale the normals down to 1% of the character size, that looks pretty nice on all models
mNormalsScaleMultiplier = aabb.CalcRadius() * 0.01f;
const float radius = AZ::Vector3(aabb.GetMax() - aabb.GetMin()).GetLength() * 0.5f;
mNormalsScaleMultiplier = radius * 0.01f;
}
// zoom to characters
void RenderPlugin::ViewCloseup(bool selectedInstancesOnly, RenderWidget* renderWidget, float flightTime)
{
const MCore::AABB sceneAABB = GetSceneAABB(selectedInstancesOnly);
if (sceneAABB.CheckIfIsValid())
const AZ::Aabb sceneAabb = GetSceneAabb(selectedInstancesOnly);
if (sceneAabb.IsValid())
{
// in case the given view widget parameter is nullptr apply it on all view widgets
if (!renderWidget)
@@ -655,13 +648,13 @@ namespace EMStudio
for (RenderViewWidget* viewWidget : m_viewWidgets)
{
RenderWidget* current = viewWidget->GetRenderWidget();
current->ViewCloseup(sceneAABB, flightTime);
current->ViewCloseup(sceneAabb, flightTime);
}
}
// only apply it to the given view widget
else
{
renderWidget->ViewCloseup(sceneAABB, flightTime);
renderWidget->ViewCloseup(sceneAabb, flightTime);
}
}
}
@@ -882,9 +875,9 @@ namespace EMStudio
// get the AABB containing all actor instances in the scene
MCore::AABB RenderPlugin::GetSceneAABB(bool selectedInstancesOnly)
AZ::Aabb RenderPlugin::GetSceneAabb(bool selectedInstancesOnly)
{
MCore::AABB finalAABB;
AZ::Aabb finalAabb = AZ::Aabb::CreateNull();
CommandSystem::SelectionList& selection = GetCommandManager()->GetCurrentSelection();
if (mUpdateCallback)
@@ -922,20 +915,28 @@ namespace EMStudio
}
// get the mesh based AABB
MCore::AABB aabb;
actorInstance->CalcMeshBasedAABB(0, &aabb);
AZ::Aabb aabb = AZ::Aabb::CreateNull();
actorInstance->CalcMeshBasedAabb(0, &aabb);
// get the node based AABB
if (aabb.CheckIfIsValid() == false)
if (!aabb.IsValid())
{
actorInstance->CalcNodeBasedAABB(&aabb);
actorInstance->CalcNodeBasedAabb(&aabb);
}
// make sure the actor instance is covered in our global bounding box
finalAABB.Encapsulate(aabb);
if (aabb.IsValid())
{
finalAabb.AddAabb(aabb);
}
}
return finalAABB;
if (!finalAabb.IsValid())
{
finalAabb.Set(AZ::Vector3(-1.0f, -1.0f, 0.0f), AZ::Vector3(1.0f, 1.0f, 0.0f));
}
return finalAabb;
}
@@ -1155,24 +1156,19 @@ namespace EMStudio
settings.mNodeBasedColor = renderOptions->GetNodeAABBColor();
settings.mStaticBasedColor = renderOptions->GetStaticAABBColor();
settings.mMeshBasedColor = renderOptions->GetMeshAABBColor();
settings.mCollisionMeshBasedColor = renderOptions->GetCollisionMeshAABBColor();
renderUtil->RenderAABBs(actorInstance, settings);
renderUtil->RenderAabbs(actorInstance, settings);
}
if (widget->GetRenderFlag(RenderViewWidget::RENDER_OBB))
{
renderUtil->RenderOBBs(actorInstance, &visibleJointIndices, &selectedJointIndices, renderOptions->GetOBBsColor(), renderOptions->GetSelectedObjectColor());
}
if (widget->GetRenderFlag(RenderViewWidget::RENDER_LINESKELETON))
{
const MCommon::Camera* camera = widget->GetRenderWidget()->GetCamera();
const AZ::Vector3& cameraPos = camera->GetPosition();
MCore::AABB aabb;
actorInstance->CalcNodeBasedAABB(&aabb);
const AZ::Vector3 aabbMid = aabb.CalcMiddle();
const float aabbRadius = aabb.CalcRadius();
AZ::Aabb aabb;
actorInstance->CalcNodeBasedAabb(&aabb);
const AZ::Vector3 aabbMid = aabb.GetCenter();
const float aabbRadius = AZ::Vector3(aabb.GetMax() - aabb.GetMin()).GetLength() * 0.5f;
const float camDistance = fabs((cameraPos - aabbMid).GetLength());
// Avoid rendering too big joint spheres when zooming in onto a joint.
@@ -1185,7 +1181,7 @@ namespace EMStudio
// Scale the joint spheres based on the character's extents, to avoid really large joint spheres
// on small characters and too small spheres on large characters.
static const float baseRadius = 0.005f;
const float jointSphereRadius = aabb.CalcRadius() * scaleMultiplier * baseRadius;
const float jointSphereRadius = aabbRadius * scaleMultiplier * baseRadius;
renderUtil->RenderSimpleSkeleton(actorInstance, &visibleJointIndices, &selectedJointIndices,
renderOptions->GetLineSkeletonColor(), renderOptions->GetSelectedObjectColor(), jointSphereRadius);
@@ -1268,8 +1264,8 @@ namespace EMStudio
// render the selection
if (renderOptions->GetRenderSelectionBox() && EMotionFX::GetActorManager().GetNumActorInstances() != 1 && GetCurrentSelection()->CheckIfHasActorInstance(actorInstance))
{
MCore::AABB aabb = actorInstance->GetAABB();
aabb.Widen(aabb.CalcRadius() * 0.005f);
AZ::Aabb aabb = actorInstance->GetAabb();
aabb.Expand(AZ::Vector3(0.005f));
renderUtil->RenderSelection(aabb, renderOptions->GetSelectionColor());
}
@@ -154,7 +154,7 @@ namespace EMStudio
MCORE_INLINE CommandSystem::SelectionList* GetCurrentSelection() const { return mCurrentSelection; }
MCORE_INLINE MCommon::RenderUtil* GetRenderUtil() const { return mRenderUtil; }
MCore::AABB GetSceneAABB(bool selectedInstancesOnly);
AZ::Aabb GetSceneAabb(bool selectedInstancesOnly);
MCommon::RenderUtil::TrajectoryTracePath* FindTracePath(EMotionFX::ActorInstance* actorInstance);
void ResetSelectedTrajectoryPaths();
@@ -170,15 +170,10 @@ namespace EMStudio
settings.mNodeBasedColor = renderOptions->GetNodeAABBColor();
settings.mStaticBasedColor = renderOptions->GetStaticAABBColor();
settings.mMeshBasedColor = renderOptions->GetMeshAABBColor();
settings.mCollisionMeshBasedColor = renderOptions->GetCollisionMeshAABBColor();
renderUtil->RenderAABBs(actorInstance, settings);
renderUtil->RenderAabbs(actorInstance, settings);
}
if (widget->GetRenderFlag(RenderViewWidget::RENDER_OBB))
{
renderUtil->RenderOBBs(actorInstance, &visibleJointIndices, &selectedJointIndices, renderOptions->GetOBBsColor(), renderOptions->GetSelectedObjectColor());
}
if (widget->GetRenderFlag(RenderViewWidget::RENDER_LINESKELETON))
{
renderUtil->RenderSimpleSkeleton(actorInstance, &visibleJointIndices, &selectedJointIndices, renderOptions->GetLineSkeletonColor(), renderOptions->GetSelectedObjectColor());
@@ -260,8 +255,8 @@ namespace EMStudio
// render the selection
if (renderOptions->GetRenderSelectionBox() && EMotionFX::GetActorManager().GetNumActorInstances() != 1 && mPlugin->GetCurrentSelection()->CheckIfHasActorInstance(actorInstance))
{
MCore::AABB aabb = actorInstance->GetAABB();
aabb.Widen(aabb.CalcRadius() * 0.005f);
AZ::Aabb aabb = actorInstance->GetAabb();
aabb.Expand(aabb.GetExtents() * 0.005f);
renderUtil->RenderSelection(aabb, renderOptions->GetSelectionColor());
}
@@ -108,7 +108,6 @@ namespace EMStudio
CreateViewOptionEntry(contextMenu, "Face Normals", RENDER_FACENORMALS);
CreateViewOptionEntry(contextMenu, "Tangents", RENDER_TANGENTS);
CreateViewOptionEntry(contextMenu, "Actor Bounding Boxes", RENDER_AABB);
CreateViewOptionEntry(contextMenu, "Joint OBBs", RENDER_OBB, false);
CreateViewOptionEntry(contextMenu, "Collision Meshes", RENDER_COLLISIONMESHES, false);
contextMenu->addSeparator();
CreateViewOptionEntry(contextMenu, "Line Skeleton", RENDER_LINESKELETON);
@@ -233,7 +232,6 @@ namespace EMStudio
SetRenderFlag(RENDER_TANGENTS, false);
SetRenderFlag(RENDER_AABB, false);
SetRenderFlag(RENDER_OBB, false);
SetRenderFlag(RENDER_COLLISIONMESHES, false);
SetRenderFlag(RENDER_RAGDOLL_COLLIDERS, true);
SetRenderFlag(RENDER_RAGDOLL_JOINTLIMITS, true);
@@ -410,7 +408,6 @@ namespace EMStudio
}
// Override some settings as we removed those from the menu.
SetRenderFlag(RENDER_OBB, false);
SetRenderFlag(RENDER_COLLISIONMESHES, false);
SetRenderFlag(RENDER_TEXTURING, false);
@@ -52,7 +52,6 @@ namespace EMStudio
RENDER_VERTEXNORMALS = 6,
RENDER_TANGENTS = 7,
RENDER_AABB = 8,
RENDER_OBB = 9,
RENDER_COLLISIONMESHES = 10,
RENDER_SKELETON = 11,
RENDER_LINESKELETON = 12,
@@ -6,7 +6,6 @@
*
*/
// include the required headers
#include "RenderWidget.h"
#include "RenderPlugin.h"
#include <EMotionFX/Rendering/Common/OrbitCamera.h>
@@ -21,6 +20,7 @@
#include "../EMStudioManager.h"
#include "../MainWindow.h"
#include <MCore/Source/AzCoreConversions.h>
#include <MCore/Source/AABB.h>
namespace EMStudio
@@ -72,9 +72,8 @@ namespace EMStudio
}
// start view closeup flight
void RenderWidget::ViewCloseup(const MCore::AABB& aabb, float flightTime, uint32 viewCloseupWaiting)
void RenderWidget::ViewCloseup(const AZ::Aabb& aabb, float flightTime, uint32 viewCloseupWaiting)
{
//LogError("ViewCloseup: AABB: Pos=(%.3f, %.3f, %.3f), Width=%.3f, Height=%.3f, Depth=%.3f", aabb.CalcMiddle().x, aabb.CalcMiddle().y, aabb.CalcMiddle().z, aabb.CalcWidth(), aabb.CalcHeight(), aabb.CalcDepth());
mViewCloseupWaiting = viewCloseupWaiting;
mViewCloseupAABB = aabb;
mViewCloseupFlightTime = flightTime;
@@ -82,9 +81,8 @@ namespace EMStudio
void RenderWidget::ViewCloseup(bool selectedInstancesOnly, float flightTime, uint32 viewCloseupWaiting)
{
//LogError("ViewCloseup: AABB: Pos=(%.3f, %.3f, %.3f), Width=%.3f, Height=%.3f, Depth=%.3f", aabb.CalcMiddle().x, aabb.CalcMiddle().y, aabb.CalcMiddle().z, aabb.CalcWidth(), aabb.CalcHeight(), aabb.CalcDepth());
mViewCloseupWaiting = viewCloseupWaiting;
mViewCloseupAABB = mPlugin->GetSceneAABB(selectedInstancesOnly);
mViewCloseupAABB = mPlugin->GetSceneAabb(selectedInstancesOnly);
mViewCloseupFlightTime = flightTime;
}
@@ -603,14 +601,15 @@ namespace EMStudio
if (actor->CheckIfHasMeshes(actorInstance->GetLODLevel()) == false)
{
// calculate the node based AABB
MCore::AABB box;
actorInstance->CalcNodeBasedAABB(&box);
AZ::Aabb box;
actorInstance->CalcNodeBasedAabb(&box);
// render the aabb
if (box.CheckIfIsValid())
if (box.IsValid())
{
const MCore::AABB mcoreAabb(box.GetMin(), box.GetMax());
AZ::Vector3 ii, n;
if (ray.Intersects(box, &ii, &n))
if (ray.Intersects(mcoreAabb, &ii, &n))
{
selectedActorInstance = actorInstance;
oldIntersectionPoint = ii;
@@ -1169,8 +1168,7 @@ namespace EMStudio
mViewCloseupWaiting--;
if (mViewCloseupWaiting == 0)
{
mCamera->ViewCloseup(mViewCloseupAABB, mViewCloseupFlightTime);
//mViewCloseupWaiting = 0;
mCamera->ViewCloseup(MCore::AABB(mViewCloseupAABB.GetMin(), mViewCloseupAABB.GetMax()), mViewCloseupFlightTime);
}
}
@@ -6,11 +6,10 @@
*
*/
#ifndef __EMSTUDIO_RENDERWIDGET_H
#define __EMSTUDIO_RENDERWIDGET_H
#pragma once
//
#if !defined(Q_MOC_RUN)
#include <AzCore/Math/Aabb.h>
#include <MCore/Source/StandardHeaders.h>
#include "../EMStudioConfig.h"
#include <EMotionFX/Rendering/Common/Camera.h>
@@ -117,7 +116,7 @@ namespace EMStudio
MCORE_INLINE MCommon::Camera* GetCamera() const { return mCamera; }
MCORE_INLINE CameraMode GetCameraMode() const { return mCameraMode; }
MCORE_INLINE void SetSkipFollowCalcs(bool skipFollowCalcs) { mSkipFollowCalcs = skipFollowCalcs; }
void ViewCloseup(const MCore::AABB& aabb, float flightTime, uint32 viewCloseupWaiting = 5);
void ViewCloseup(const AZ::Aabb& aabb, float flightTime, uint32 viewCloseupWaiting = 5);
void ViewCloseup(bool selectedInstancesOnly, float flightTime, uint32 viewCloseupWaiting = 5);
void SwitchCamera(CameraMode mode);
@@ -161,7 +160,7 @@ namespace EMStudio
// used for closeup camera flights
uint32 mViewCloseupWaiting;
MCore::AABB mViewCloseupAABB;
AZ::Aabb mViewCloseupAABB;
float mViewCloseupFlightTime;
// manipulator helper data
@@ -175,6 +174,3 @@ namespace EMStudio
int32 mPixelsMovedSinceRightClick;
};
} // namespace EMStudio
#endif
-638
View File
@@ -1,638 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
// include required headers
#include "OBB.h"
#include "AABB.h"
#include <AzCore/Jobs/JobFunction.h>
#include <AzCore/Jobs/JobCompletion.h>
#include <AzCore/Jobs/JobContext.h>
#include <MCore/Source/AzCoreConversions.h>
namespace MCore
{
// check if the box contains a given point
bool OBB::Contains(const AZ::Vector3& p) const
{
// translate to box space
AZ::Vector3 relPoint = p - mCenter;
// convert the box into box space and test each axis
float f = mRotation.GetBasisX().Dot(relPoint);
if (f >= mExtents.GetX() || f <= -mExtents.GetX())
{
return false;
}
f = mRotation.GetBasisY().Dot(relPoint);
if (f >= mExtents.GetY() || f <= -mExtents.GetY())
{
return false;
}
f = mRotation.GetBasisZ().Dot(relPoint);
if (f >= mExtents.GetZ() || f <= -mExtents.GetZ())
{
return false;
}
return true;
}
void OBB::Create(const AABB& aabb, const AZ::Transform& mat)
{
// calculate the center and extents
mCenter = aabb.CalcMiddle();
mExtents = aabb.CalcExtents();
// transform the center
mCenter = mat.TransformPoint(mCenter);
// set the rotation
mRotation = mat;
}
void OBB::Transform(const AZ::Transform& transMatrix)
{
mCenter = transMatrix.TransformPoint(mCenter);
mRotation = transMatrix * mRotation;
}
void OBB::Transformed(const AZ::Transform& transMatrix, OBB* outOBB) const
{
outOBB->mExtents = mExtents;
outOBB->mCenter = transMatrix.TransformPoint(mCenter);
outOBB->mRotation = transMatrix * mRotation;
}
bool OBB::CheckIfIsInside(const OBB& box) const
{
// make a 4x4 from the box & inverse it
AZ::Transform M0 = box.mRotation;
M0.SetTranslation(box.mCenter);
AZ::Transform M0Inv = M0.GetInverse();
// with our inversed 4x4, create box1 in space of box0
OBB _1in0;
Transformed(M0Inv, &_1in0);
// this should cancel out box0's rotation, i.e. it's now an AABB
// the two boxes are in the same space so now we can compare them
// create the AABB of (box1 in space of box0)
const AZ::Transform& mtx = _1in0.mRotation;
AZ::Vector3 transformedAxisX = mtx.GetUniformScale() * (mtx.GetRotation().GetConjugate().TransformVector(AZ::Vector3::CreateAxisX()));
AZ::Vector3 transformedAxisY = mtx.GetUniformScale() * (mtx.GetRotation().GetConjugate().TransformVector(AZ::Vector3::CreateAxisY()));
AZ::Vector3 transformedAxisZ = mtx.GetUniformScale() * (mtx.GetRotation().GetConjugate().TransformVector(AZ::Vector3::CreateAxisZ()));
float f = transformedAxisX.GetAbs().Dot(mExtents) - box.mExtents.GetX();
if (f > _1in0.mCenter.GetX())
{
return false;
}
if (-f < _1in0.mCenter.GetX())
{
return false;
}
f = transformedAxisY.GetAbs().Dot(mExtents) - box.mExtents.GetY();
if (f > _1in0.mCenter.GetY())
{
return false;
}
if (-f < _1in0.mCenter.GetY())
{
return false;
}
f = transformedAxisZ.GetAbs().Dot(mExtents) - box.mExtents.GetZ();
if (f > _1in0.mCenter.GetZ())
{
return false;
}
if (-f < _1in0.mCenter.GetZ())
{
return false;
}
return true;
}
// calculate the corner points for the OBB
void OBB::CalcCornerPoints(AZ::Vector3* outPoints) const
{
MCORE_ASSERT(outPoints);
MCORE_ASSERT(CheckIfIsValid());
AZ::Vector3 right = MCore::GetRight(mRotation);
AZ::Vector3 up = MCore::GetUp(mRotation);
AZ::Vector3 forward = MCore::GetForward(mRotation);
right *= mExtents.GetX();
up *= mExtents.GetZ();
forward *= mExtents.GetY();
// 7+------+6
// /| /|
// / | / |
// / 4+---/--+5
// 3+------+2 /
// | / | /
// |/ |/
// 0+------+1
outPoints[0] = mCenter - right - up - forward;
outPoints[1] = mCenter + right - up - forward;
outPoints[2] = mCenter + right + up - forward;
outPoints[3] = mCenter - right + up - forward;
outPoints[4] = mCenter - right - up + forward;
outPoints[5] = mCenter + right - up + forward;
outPoints[6] = mCenter + right + up + forward;
outPoints[7] = mCenter - right + up + forward;
}
//----------------------------------------------------------------------------------------------------------
// calculate the 3 eigen vectors
void OBB::GetRealSymmetricEigenvectors(const float A[6], AZ::Vector3& v1, AZ::Vector3& v2, AZ::Vector3& v3)
{
// compute coefficients for cubic equation
const float c2 = A[0] + A[3] + A[5];
const float a12sq = A[1] * A[1];
const float a13sq = A[2] * A[2];
const float a23sq = A[4] * A[4];
const float a11a22 = A[0] * A[3];
const float c1 = a11a22 - a12sq + A[0] * A[5] - a13sq + A[3] * A[5] - a23sq;
const float c0 = a11a22 * A[5] + 2.0f * A[1] * A[2] * A[4] - A[0] * a23sq - A[3] * a13sq - A[5] * a12sq;
// compute intermediate values for root solving
const float c2sq = c2 * c2;
const float a = (3.0f * c1 - c2sq) / 3.0f;
const float b = (9.0f * c1 * c2 - 2.0f * c2sq * c2 - 27.f * c0) / 27.0f;
const float halfb = b * 0.5f;
const float halfb2 = halfb * halfb;
const float Q = halfb2 + a * a * a / 27.0f;
// determine type of eigenspaces
if (Q > 1.0e-6f)
{
// one eigenvalue, use standard basis
v1.Set(1.0f, 0.0f, 0.0f);
v2.Set(0.0f, 1.0f, 0.0f);
v3.Set(0.0f, 0.0f, 1.0f);
return;
}
else
if (Q < -1.0e-6f)
{
// three distinct eigenvalues
// intermediate terms
const float theta_3 = Math::ATan2(Math::Sqrt(-Q), -halfb) / 3.0f;
float rho = Math::Sqrt(halfb2 - Q);
const float c2_3 = c2 / 3.0f;
float rho_13 = powf(Math::Abs(rho), 1.0f / 3.0f);
if (rho < 0.0f)
{
rho_13 = -rho_13;
}
float ct_3, st_3;
const float sqrt3 = Math::Sqrt(3.0f);
ct_3 = Math::Cos(theta_3);
st_3 = Math::Sin(theta_3);
// compute each eigenvalue and eigenvector
// sort from largest to smallest
float lambda1 = c2_3 + 2.0f * rho_13 * ct_3;
CalcSymmetricEigenVector(A, lambda1, v1);
float lambda2 = c2_3 - rho_13 * (ct_3 + sqrt3 * st_3);
if (lambda2 > lambda1)
{
v2 = v1;
float temp = lambda2;
lambda2 = lambda1;
lambda1 = temp;
CalcSymmetricEigenVector(A, lambda2, v1);
}
else
{
CalcSymmetricEigenVector(A, lambda2, v2);
}
float lambda3 = c2_3 - rho_13 * (ct_3 - sqrt3 * st_3);
if (lambda3 > lambda1)
{
v3 = v2;
v2 = v1;
CalcSymmetricEigenVector(A, lambda3, v1);
}
else
if (lambda3 > lambda2)
{
v3 = v2;
CalcSymmetricEigenVector(A, lambda3, v2);
}
else
{
CalcSymmetricEigenVector(A, lambda3, v3);
}
}
else
{
// two distinct eigenvalues
// intermediate terms
float c2_3 = c2 / 3.0f;
float halfb_13 = Math::Pow(Math::Abs(halfb), 1.0f / 3.0f);
if (halfb < 0.0f)
{
halfb_13 = -halfb_13;
}
// compute each eigenvalue and eigenvector
// sort from largest to smallest
float lambda1 = c2_3 + halfb_13;
CalcSymmetricEigenPair(A, lambda1, v1, v2);
float lambda2 = c2_3 - 2.0f * halfb_13;
if (lambda2 > lambda1)
{
v3 = v2;
v2 = v1;
CalcSymmetricEigenVector(A, lambda2, v1);
}
else
{
CalcSymmetricEigenVector(A, lambda2, v3);
}
}
v1.Normalize();
v2.Normalize();
v3.Normalize();
if ((v1.Cross(v2)).Dot(v3) < 0.0f)
{
v3 = -v3;
}
}
// calculate the eigen vector from a symmetric matrix in combination with a given eigen value
void OBB::CalcSymmetricEigenVector(const float A[6], float eigenValue, AZ::Vector3& v1)
{
const float m11 = A[0] - eigenValue;
const float m12 = A[1];
const float m13 = A[2];
const float m22 = A[3] - eigenValue;
const float m23 = A[4];
const float m33 = A[5] - eigenValue;
// compute cross product matrix, and find column with maximal entry
const float u11 = m22 * m33 - m23 * m23;
float max = Math::Abs(u11);
int c = 1;
const float u12 = m13 * m23 - m12 * m33;
if (Math::Abs(u12) > max)
{
max = Math::Abs(u12);
c = 2;
}
const float u13 = m12 * m23 - m13 * m22;
if (Math::Abs(u13) > max)
{
max = Math::Abs(u13);
c = 3;
}
const float u22 = m11 * m33 - m13 * m13;
if (Math::Abs(u22) > max)
{
max = Math::Abs(u22);
c = 2;
}
const float u23 = m12 * m13 - m23 * m11;
if (Math::Abs(u23) > max)
{
max = Math::Abs(u23);
c = 3;
}
const float u33 = m11 * m22 - m12 * m12;
if (Math::Abs(u33) > max)
{
max = Math::Abs(u33);
c = 3;
}
// return column with maximal entry
if (c == 1)
{
v1.Set(u11, u12, u13);
}
else
if (c == 2)
{
v1.Set(u12, u22, u23);
}
else
{
v1.Set(u13, u23, u33);
}
}
//-------------------------------------------------------------------------------
// Given symmetric matrix A and eigenvalue l, returns eigenvector pair
// Assumes that order of eigenvalue is 2
//-------------------------------------------------------------------------------
void OBB::CalcSymmetricEigenPair(const float A[6], float eigenValue, AZ::Vector3& v1, AZ::Vector3& v2)
{
// find maximal entry in M
const float m11 = A[0] - eigenValue;
float max = Math::Abs(m11);
int r = 1, c = 1;
if (Math::Abs(A[1]) > max)
{
max = Math::Abs(A[1]);
r = 1;
c = 2;
}
if (Math::Abs(A[2]) > max)
{
max = Math::Abs(A[2]);
r = 1;
c = 3;
}
const float m22 = A[3] - eigenValue;
if (Math::Abs(m22) > max)
{
max = Math::Abs(m22);
r = 2;
c = 2;
}
if (Math::Abs(A[4]) > max)
{
max = Math::Abs(A[4]);
r = 2;
c = 3;
}
const float m33 = A[5] - eigenValue;
if (Math::Abs(m33) > max)
{
r = 3;
c = 3;
}
// compute eigenvectors for each case
if (r == 1)
{
if (c == 3)
{
v1.Set(A[2], 0.0f, -m11);
v2.Set(-A[1] * m11, m11 * m11 + A[2] * A[2], -A[1] * A[2]);
}
else
{
v1.Set(-A[1], m11, 0.0f);
v2.Set(-A[2] * m11, -A[2] * A[1], m11 * m11 + A[1] * A[1]);
}
}
else
if (r == 2)
{
v1.Set(0.0f, -A[4], m22);
v2.Set(m22 * m22 + A[4] * A[4], -A[1] * m22, -A[1] * A[4]);
}
else
if (r == 3)
{
v1.Set(0.0f, -m33, A[4]);
v2.Set(A[4] * A[4] + m33 * m33, -A[2] * A[4], -A[2] * m33);
}
}
//-----------------------
//-------------------------------------------------------------------------------
// Compute covariance matrix for set of points
// Returns centroid and unique values of matrix
//-------------------------------------------------------------------------------
void OBB::CovarianceMatrix(const AZ::Vector3* points, uint32 numPoints, AZ::Vector3& mean, float C[6])
{
uint32 i;
// compute mean
mean = points[0];
for (i = 1; i < numPoints; ++i)
{
mean += points[i];
}
mean *= 1.0f / numPoints;
// compute each element of matrix
memset(C, 0, sizeof(float) * 6);
for (i = 0; i < numPoints; ++i)
{
const AZ::Vector3 diff = points[i] - mean;
C[0] += diff.GetX() * diff.GetX();
C[1] += diff.GetX() * diff.GetY();
C[2] += diff.GetX() * diff.GetZ();
C[3] += diff.GetY() * diff.GetY();
C[4] += diff.GetY() * diff.GetZ();
C[5] += diff.GetZ() * diff.GetZ();
}
// normalize the matrix values
float maxC = 0.0f;
for (i = 0; i < 6; ++i)
{
if (Math::Abs(C[i]) > maxC)
{
maxC = Math::Abs(C[i]);
}
}
for (i = 0; i < 6; ++i)
{
C[i] /= maxC;
}
}
// calc the best fit for a given x rotation slice
void OBB::InitFromPointsRange(const AZ::Vector3* points, uint32 numPoints, float xDegrees, float* outMinArea, AABB* outMinBox, AZ::Transform* outMinMatrix)
{
// calculate the x rotation matrix
AZ::Transform rotMatrix = AZ::Transform::CreateRotationX(Math::DegreesToRadians(xDegrees));
// try the same over the z axis
for (float z = -180.0f; z < 180.0f; z += 5.0f)
{
// calculate the final rotation matrix
rotMatrix = AZ::Transform::CreateRotationZ(Math::DegreesToRadians(z)) * rotMatrix;
// calculate the inverse so we can transform the point set into space of this current rotation
AZ::Transform invMatrix = rotMatrix.GetInverse();
// rotate the points into the space of the current rotation
AABB box;
box.Init();
for (uint32 i = 0; i < numPoints; ++i)
{
box.Encapsulate(invMatrix.TransformPoint(points[i]));
}
// check if the surface area of this box is smaller than the smallest one we have
const float area = box.CalcSurfaceArea();
if (area < *outMinArea)
{
*outMinArea = area;
*outMinBox = box;
*outMinMatrix = rotMatrix;
}
}
}
// Compute bounding box for set of points
void OBB::InitFromPoints(const AZ::Vector3* points, uint32 numPoints)
{
// if we have no points, just init
if (numPoints == 0)
{
Init();
return;
}
// some values we need
const uint32 MAX_NUM = (360 / 5) + 1;
AABB minBoxes[MAX_NUM];
AZ::Transform minRotMatrices[MAX_NUM];
float minAreas[MAX_NUM];
for (uint32 i = 0; i < MAX_NUM; ++i)
{
minAreas[i] = FLT_MAX;
}
// try all rotation on the x axis (multithreaded)
AZ::JobCompletion jobCompletion;
uint32 index = 0;
for (float x = -180.0f; x < 180.0f; x += 5.0f)
{
MCORE_ASSERT(index < MAX_NUM);
// create the job and add it
AZ::JobContext* jobContext = nullptr;
AZ::Job* job = AZ::CreateJobFunction([this, &minAreas, &minBoxes, &minRotMatrices, &numPoints, &points, x, index]()
{
InitFromPointsRange(points, numPoints, x, &minAreas[index], &minBoxes[index], &minRotMatrices[index]);
}, true, jobContext);
job->SetDependent(&jobCompletion);
job->Start();
index++;
}
jobCompletion.StartAndWaitForCompletion();
// find the real minimum value (single threaded lookup)
float minimumArea = FLT_MAX;
uint32 minimumIndex = 0;
for (uint32 i = 0; i < MAX_NUM; ++i)
{
if (minAreas[i] < minimumArea)
{
minimumArea = minAreas[i];
minimumIndex = i;
}
}
// update
mRotation = minRotMatrices[minimumIndex];
mCenter = mRotation.TransformPoint(minBoxes[minimumIndex].CalcMiddle());
mExtents = minBoxes[minimumIndex].CalcExtents();
/*
// compute covariance matrix
float C[6];
CovarianceMatrix( points, numPoints, mCenter, C );
// get principle axes
Vector3 basis[3];
GetRealSymmetricEigenvectors( C, basis[0], basis[1], basis[2] );
// init the min and max vectors
Vector3 minVec;
Vector3 maxVec;
minVec.Set(FLT_MAX, FLT_MAX, FLT_MAX);
maxVec.Set(-FLT_MAX, -FLT_MAX, -FLT_MAX);
// find the min and max
for (uint32 i=0; i<numPoints; ++i)
{
Vector3 diff = points[i] - mCenter;
for (int32 j=0; j<3; ++j)
{
const float length = diff.Dot( basis[j] );
if (length > maxVec[j])
maxVec[j] = length;
else
if (length < minVec[j])
minVec[j] = length;
}
}
// build the matrix from the calculated basis vectors
mRotation.Identity();
mRotation.SetRow(0, basis[0]);
mRotation.SetRow(1, basis[1]);
mRotation.SetRow(2, basis[2]);
// calculate the extents
mExtents = (maxVec - minVec) * 0.5f;
*/
}
// calculate the minimum and maximum point
void OBB::CalcMinMaxPoints(AZ::Vector3* outMin, AZ::Vector3* outMax) const
{
AZ::Transform rotation = mRotation;
rotation.SetTranslation(AZ::Vector3::CreateZero());
AZ::Vector3 rotatedExtents = rotation.TransformPoint(mExtents);
*outMax = mCenter + rotatedExtents;
*outMin = mCenter - rotatedExtents;
// +------+MAX
// /| /|
// / | / |
// / +---/--+
// +------+ /
// | / | /
// |/ |/
//MIN+------+
}
} // namespace MCore
-235
View File
@@ -1,235 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/Math/Transform.h>
#include <AzCore/Math/Vector3.h>
#include "StandardHeaders.h"
namespace MCore
{
// forward declarations
class AABB;
/**
* 3D Oriented Bounding Box (OBB) template.
* This is basically a AABB with an arbitrary rotation.
*/
class MCORE_API OBB
{
public:
/**
* The constructor.
* This automatically initializes the box. After initialization the box is invalid since it basically has no size yet.
* The IsValid() method will return false.
*/
MCORE_INLINE OBB() { Init(); }
/**
* Construct the OBB from a given axis aligned bounding box and a transformation.
* @param aabb The axis aligned bounding box.
* @param transformation The transformation of the box.
*/
MCORE_INLINE OBB(const AABB& aabb, const AZ::Transform& transformation) { Create(aabb, transformation); }
/**
* Construct the OBB from a center, extends and a rotation.
* @param center The center of the box.
* @param extents The extents of the box, which start at the center of the box.
* @param rot The matrix, representing the transformation of the box.
*/
MCORE_INLINE OBB(const AZ::Vector3& center, const AZ::Vector3& extents, const AZ::Transform& rot)
: mRotation(rot)
, mExtents(extents)
, mCenter(center) {}
/**
* Reset the OBB with as center 0,0,0, infinite negative extents and no rotation.
* This makes the box an invalid box as well, because the extents have not been set.
*/
MCORE_INLINE void Init();
/**
* Initialize the box from a set of points.
* This uses the covariant matrix and eigen vectors to fit the box to the set of points.
* @param points The set of points to fit the box to.
* @param numPoints The number of points inside array specified as first parameter.
*/
void InitFromPoints(const AZ::Vector3* points, uint32 numPoints);
/**
* Check if this box OBB contains a given point or not.
* @param p The point to check.
* @result Returns true when the point is inside this box, otherwise false is returned.
*/
bool Contains(const AZ::Vector3& p) const;
/**
* Check if this OBB is inside another specified box.
* @param box The OBB to check.
* @result Returns true when this OBB is inside the box specified as parameter.
*/
bool CheckIfIsInside(const OBB& box) const;
/**
* Create the OBB from a given AABB and a matrix.
* @param aabb The axis aligned bounding box.
* @param mat The matrix, which represents the orientation of the box.
*/
void Create(const AABB& aabb, const AZ::Transform& mat);
/**
* Transform this OBB with a given matrix.
* This means the transformation specified as parameter will be applied to the current transformation of the OBB.
* So the transformation specified is NOT an absolute rotation, but a relative transformation.
* @param transMatrix The relative transformation matrix, to be applied to the current transformation.
*/
void Transform(const AZ::Transform& transMatrix);
/**
* Calculate the transformed version of this OBB.
* @param rotMatrix The transformation matrix to be applied to the rotation of this OBB, so not an absolute rotation!
* @param outOBB A pointer to the OBB to fill with the rotated version of this OBB.
*/
void Transformed(const AZ::Transform& rotMatrix, OBB* outOBB) const;
/**
* Check if this is a valid OBB or not.
* The box is only valid if the extents are non-negative.
* @result Returns true when the OBB is valid, otherwise false is returned.
*/
MCORE_INLINE bool CheckIfIsValid() const;
/**
* Set the center of the box.
* @param center The new center of the box.
*/
MCORE_INLINE void SetCenter(const AZ::Vector3& center) { mCenter = center; }
/**
* Set the extents of the box.
* @param extents The new extents of the box.
*/
MCORE_INLINE void SetExtents(const AZ::Vector3& extents) { mExtents = extents; }
/**
* Set the transformation of the box.
* @param transform The new transformation of the box.
*/
MCORE_INLINE void SetTransformation(const AZ::Transform& transform) { mRotation = transform; }
/**
* Get the center of the box.
* @result The center point of the box.
*/
MCORE_INLINE const AZ::Vector3& GetCenter() const { return mCenter; }
/**
* Get the extents of the box.
* @result The extents of the box, which start at the center.
*/
MCORE_INLINE const AZ::Vector3& GetExtents() const { return mExtents; }
/**
* Get the transformation of the box.
* @result The transformation of the box.
*/
MCORE_INLINE const AZ::Transform& GetTransformation() const { return mRotation; }
/**
* Calculate the 8 corner points of the box.
* The layout is as follows:
* <pre>
*
* 7+------+6
* /| /|
* / | / |
* / 4+---/--+5
* 3+------+2 /
* | / | /
* |/ |/
* 0+------+1
*
* </pre>
* @param outPoints the array of at least 8 vectors to write the points in.
*/
void CalcCornerPoints(AZ::Vector3* outPoints) const;
/**
* Calculate the rotated minimum and maximum points of the box.
* After rotation it is possible that the min point is not really the min anymore though. The same goes for max.
* But the main use for this method however is to quickly approximate an AABB from this OBB, without having to
* calculate all 8 corner points.
* <pre>
*
* +------+MAX
* /| /|
* / | / |
* / +---/--+
* +------+ /
* | / | /
* |/ |/
* MIN+------+
*
* </pre>
* @param outMin The vector that we will write the minimum point to.
* @param outMax The vector that we will write the maximum point to.
*/
void CalcMinMaxPoints(AZ::Vector3* outMin, AZ::Vector3* outMax) const;
private:
AZ::Transform mRotation; /**< The rotation of the box. */ // TODO: store the center inside the translation component and extents inside last column?
AZ::Vector3 mExtents; /**< The extents of the box. */
AZ::Vector3 mCenter; /**< The center of the box. */
/**
* Calculate the three eigen vectors for a symmetric matrix.
* @param A The symmetric matrix values.
* @param v1 The first output eigen vector.
* @param v2 The second output eigen vector.
* @param v3 The third output eigen vector.
*/
void GetRealSymmetricEigenvectors(const float A[6], AZ::Vector3& v1, AZ::Vector3& v2, AZ::Vector3& v3);
/**
* Calculate the eigen vector from a symmetric matrix.
* This assumes that the specified eigenvalue is of order 1.
* @param A The symmetric matrix values.
* @param eigenValue The eigen value.
* @param v1 The output eigen vector.
*/
void CalcSymmetricEigenVector(const float A[6], float eigenValue, AZ::Vector3& v1);
/**
* Calculate the pair of eigen vectors from a symmetric matrix.
* This assumes that the specified eigen value is of order 2.
* @param A The symmetric matrix values.
* @param eigenValue The eigen value.
* @param v1 The first output eigen vector.
* @param v2 The second output eigen vector.
*/
void CalcSymmetricEigenPair(const float A[6], float eigenValue, AZ::Vector3& v1, AZ::Vector3& v2);
/**
* Calculate the covariance matrix from a set of points.
* @param points The set of points to calculate the covariance matrix from.
* @param numPoints The number of points inside the specified set of points.
* @param mean The statistical mean will be output in this vector.
* @param C The covariance matrix values that will be written to. Since the matrix is symmetric we only output one triangle of the 3x3 matrix.
*/
void CovarianceMatrix(const AZ::Vector3 * points, uint32 numPoints, AZ::Vector3 & mean, float C[6]);
void InitFromPointsRange(const AZ::Vector3* points, uint32 numPoints, float xDegrees, float* outMinArea, AABB* outMinBox, AZ::Transform* outMinMatrix);
};
// include the inline code
#include "OBB.inl"
} // namespace MCore
-36
View File
@@ -1,36 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
// initialize the box
// this creates an invalid box (with negative extents) so the IsValid method will return false
MCORE_INLINE void OBB::Init()
{
mCenter = AZ::Vector3::CreateZero();
mExtents.Set(-FLT_MAX, -FLT_MAX, -FLT_MAX);
mRotation = AZ::Transform::CreateIdentity();
}
// check if the OBB is valid
MCORE_INLINE bool OBB::CheckIfIsValid() const
{
if (mExtents.GetX() < 0.0f)
{
return false;
}
if (mExtents.GetY() < 0.0f)
{
return false;
}
if (mExtents.GetZ() < 0.0f)
{
return false;
}
return true;
}
@@ -101,9 +101,6 @@ set(FILES
Source/MemoryTracker.cpp
Source/MemoryTracker.h
Source/MultiThreadManager.h
Source/OBB.cpp
Source/OBB.h
Source/OBB.inl
Source/PlaneEq.cpp
Source/PlaneEq.h
Source/PlaneEq.inl
@@ -58,27 +58,32 @@ namespace EMotionFX
};
//////////////////////////////////////////////////////////////////////////
void ActorComponent::BoundingBoxConfiguration::Set(ActorInstance* actor) const
void ActorComponent::BoundingBoxConfiguration::Set(ActorInstance* actorInstance) const
{
actorInstance->SetExpandBoundsBy(m_expandBy * 0.01f); // Normalize percentage for internal use. (1% == 0.01f)
if (m_autoUpdateBounds)
{
actor->SetupAutoBoundsUpdate(m_updateTimeFrequency, m_boundsType, m_updateItemFrequency);
actorInstance->SetupAutoBoundsUpdate(m_updateTimeFrequency, m_boundsType, m_updateItemFrequency);
}
else
{
actor->SetBoundsUpdateType(m_boundsType);
actor->SetBoundsUpdateEnabled(false);
actorInstance->SetBoundsUpdateType(m_boundsType);
actorInstance->SetBoundsUpdateEnabled(false);
}
}
void ActorComponent::BoundingBoxConfiguration::SetAndUpdate(ActorInstance* actor) const
void ActorComponent::BoundingBoxConfiguration::SetAndUpdate(ActorInstance* actorInstance) const
{
Set(actor);
const AZ::u32 freq = actor->GetBoundsUpdateEnabled() ? actor->GetBoundsUpdateItemFrequency() : 1;
actor->UpdateBounds(0, actor->GetBoundsUpdateType(), freq);
Set(actorInstance);
const AZ::u32 updateFrequency = actorInstance->GetBoundsUpdateEnabled() ? actorInstance->GetBoundsUpdateItemFrequency() : 1;
const ActorInstance::EBoundsType boundUpdateType = actorInstance->GetBoundsUpdateType();
actorInstance->UpdateBounds(actorInstance->GetLODLevel(), boundUpdateType, updateFrequency);
}
void ActorComponent::BoundingBoxConfiguration::Reflect(AZ::ReflectContext * context)
void ActorComponent::BoundingBoxConfiguration::Reflect(AZ::ReflectContext* context)
{
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
@@ -105,10 +110,26 @@ namespace EMotionFX
->Field("m_autoUpdateBounds", &BoundingBoxConfiguration::m_autoUpdateBounds)
->Field("m_updateTimeFrequency", &BoundingBoxConfiguration::m_updateTimeFrequency)
->Field("m_updateItemFrequency", &BoundingBoxConfiguration::m_updateItemFrequency)
->Field("expandBy", &BoundingBoxConfiguration::m_expandBy)
;
}
}
AZ::Crc32 ActorComponent::BoundingBoxConfiguration::GetVisibilityAutoUpdate() const
{
return m_boundsType != EMotionFX::ActorInstance::BOUNDS_STATIC_BASED ? AZ::Edit::PropertyVisibility::Show : AZ::Edit::PropertyVisibility::Hide;
}
AZ::Crc32 ActorComponent::BoundingBoxConfiguration::GetVisibilityAutoUpdateSettings() const
{
if (m_boundsType == EMotionFX::ActorInstance::BOUNDS_STATIC_BASED || m_autoUpdateBounds == false)
{
return AZ::Edit::PropertyVisibility::Hide;
}
return AZ::Edit::PropertyVisibility::Show;
}
//////////////////////////////////////////////////////////////////////////
void ActorComponent::Configuration::Reflect(AZ::ReflectContext* context)
{
@@ -45,24 +45,29 @@ namespace EMotionFX
AZ_COMPONENT(ActorComponent, "{BDC97E7F-A054-448B-A26F-EA2B5D78E377}");
friend class EditorActorComponent;
struct BoundingBoxConfiguration
class BoundingBoxConfiguration
{
public:
AZ_TYPE_INFO(BoundingBoxConfiguration, "{EBCFF975-00A5-4578-85C7-59909F52067C}");
BoundingBoxConfiguration() = default;
EMotionFX::ActorInstance::EBoundsType m_boundsType = EMotionFX::ActorInstance::BOUNDS_STATIC_BASED;
bool m_autoUpdateBounds = true;
float m_updateTimeFrequency = 0.f;
AZ::u32 m_updateItemFrequency = 1;
EMotionFX::ActorInstance::EBoundsType m_boundsType = EMotionFX::ActorInstance::BOUNDS_STATIC_BASED;
float m_expandBy = 25.0f; ///< Expand the bounding volume by the given percentage.
bool m_autoUpdateBounds = true;
float m_updateTimeFrequency = 0.0f;
AZ::u32 m_updateItemFrequency = 1;
// Set the bounding box configuration of the given actor instance to the parameters given by `this'. The actor instance must not be null (this is not checked).
void Set(ActorInstance* inst) const;
// Set the bounding box configuration of the given actor instance to the parameters given by 'this'. The actor instance must not be null (this is not checked).
void Set(ActorInstance* actorInstance) const;
// Set the bounding box configuration, then update the bounds of the actor instance
void SetAndUpdate(ActorInstance* inst) const;
void SetAndUpdate(ActorInstance* actorInstance) const;
static void Reflect(AZ::ReflectContext* context);
AZ::Crc32 GetVisibilityAutoUpdate() const;
AZ::Crc32 GetVisibilityAutoUpdateSettings() const;
};
/**
@@ -62,39 +62,40 @@ namespace EMotionFX
{
editContext->Class<ActorComponent::BoundingBoxConfiguration>("Actor Bounding Box Config", "")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->DataElement(AZ::Edit::UIHandlers::ComboBox, &ActorComponent::BoundingBoxConfiguration::m_boundsType,
"Bounds type",
"The method used to compute the Actor bounding box. NOTE: ordered by least expensive to compute to most expensive to compute."
)
->EnumAttribute(ActorInstance::BOUNDS_STATIC_BASED, "Static bounds (source-asset bounds)")
->EnumAttribute(ActorInstance::BOUNDS_NODE_BASED, "Bone position-based")
->EnumAttribute(ActorInstance::BOUNDS_NODEOBB_BASED, "Bone local bounding box-based")
->EnumAttribute(ActorInstance::BOUNDS_MESH_BASED, "Render mesh vertex position-based (VERY EXPENSIVE)")
->DataElement(0, &ActorComponent::BoundingBoxConfiguration::m_autoUpdateBounds,
"The method used to compute the Actor bounding box. NOTE: ordered by least expensive to compute to most expensive to compute.")
->EnumAttribute(ActorInstance::BOUNDS_STATIC_BASED, "Static (Recommended)")
->EnumAttribute(ActorInstance::BOUNDS_NODE_BASED, "Bone position-based")
->EnumAttribute(ActorInstance::BOUNDS_MESH_BASED, "Mesh vertex-based (VERY EXPENSIVE)")
->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree)
->DataElement(AZ::Edit::UIHandlers::Default, &ActorComponent::BoundingBoxConfiguration::m_expandBy,
"Expand by",
"Percentage that the calculated bounding box should be automatically expanded with. "
"This can be used to add a tolerance area to the calculated bounding box to avoid clipping the character too early. "
"A static bounding box together with the expansion is the recommended way for maximum performance. (Default = 25%)")
->Attribute(AZ::Edit::Attributes::Suffix, " %")
->Attribute(AZ::Edit::Attributes::Min, -100.0f + AZ::Constants::Tolerance)
->DataElement(AZ::Edit::UIHandlers::Default, &ActorComponent::BoundingBoxConfiguration::m_autoUpdateBounds,
"Automatically update bounds?",
"If true, bounds are automatically updated based on some frequency. Otherwise bounds are computed only at creation or when triggered manually"
)
->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::AttributesAndValues)
->DataElement(0, &ActorComponent::BoundingBoxConfiguration::m_updateTimeFrequency,
"If true, bounds are automatically updated based on some frequency. Otherwise bounds are computed only at creation or when triggered manually")
->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree)
->Attribute(AZ::Edit::Attributes::Visibility, &ActorComponent::BoundingBoxConfiguration::GetVisibilityAutoUpdate)
->DataElement(AZ::Edit::UIHandlers::Default, &ActorComponent::BoundingBoxConfiguration::m_updateTimeFrequency,
"Update frequency",
"How often to update bounds automatically"
)
->Attribute(AZ::Edit::Attributes::Suffix, " Hz")
->Attribute(AZ::Edit::Attributes::Min, 0.f)
->Attribute(AZ::Edit::Attributes::Step, 0.001f)
->Attribute(AZ::Edit::Attributes::Visibility, &ActorComponent::BoundingBoxConfiguration::m_autoUpdateBounds)
->DataElement(0, &ActorComponent::BoundingBoxConfiguration::m_updateItemFrequency,
"How often to update bounds automatically")
->Attribute(AZ::Edit::Attributes::Suffix, " Hz")
->Attribute(AZ::Edit::Attributes::Min, 0.0f)
->Attribute(AZ::Edit::Attributes::Max, FLT_MAX)
->Attribute(AZ::Edit::Attributes::Step, 0.1f)
->Attribute(AZ::Edit::Attributes::Visibility, &ActorComponent::BoundingBoxConfiguration::GetVisibilityAutoUpdateSettings)
->DataElement(AZ::Edit::UIHandlers::Default, &ActorComponent::BoundingBoxConfiguration::m_updateItemFrequency,
"Update item skip factor",
"How many items (bones or vertices) to skip when automatically updating bounds."
" <br> i.e. =1 uses every single item, =2 uses every 2nd item, =3 uses every 3rd item... "
)
->Attribute(AZ::Edit::Attributes::Suffix, " items")
->Attribute(AZ::Edit::Attributes::Min, (AZ::u32)1)
->Attribute(AZ::Edit::Attributes::Visibility, &ActorComponent::BoundingBoxConfiguration::m_autoUpdateBounds)
" <br> i.e. =1 uses every single item, =2 uses every 2nd item, =3 uses every 3rd item...")
->Attribute(AZ::Edit::Attributes::Suffix, " items")
->Attribute(AZ::Edit::Attributes::Min, (AZ::u32)1)
->Attribute(AZ::Edit::Attributes::Visibility, &ActorComponent::BoundingBoxConfiguration::GetVisibilityAutoUpdateSettings)
;
editContext->Class<EditorActorComponent>("Actor", "The Actor component manages an instance of an Actor")
@@ -45,7 +45,7 @@ namespace EMotionFX
ConstructActor();
ASSERT_TRUE(m_actor) << "Construct actor did not build a valid actor.";
m_actor->ResizeTransformData();
m_actor->PostCreateInit(/*makeGeomLodsCompatibleWithSkeletalLODs=*/ false, /*generateOBBs=*/ false, /*convertUnitType=*/ false);
m_actor->PostCreateInit(/*makeGeomLodsCompatibleWithSkeletalLODs=*/ false, /*convertUnitType=*/ false);
}
{
m_motionSet = aznew MotionSet("testMotionSet");
@@ -40,7 +40,7 @@ namespace EMotionFX
}
// Ensure the Actor is correct
ASSERT_TRUE(GetActorManager().FindActorByName("rinactor"));
ASSERT_TRUE(GetActorManager().FindActorByName("rinActor"));
EXPECT_EQ(GetActorManager().GetNumActors(), 1);
}
} // namespace EMotionFX
@@ -84,7 +84,7 @@ namespace EMotionFX
// Without this call, the bind pose does not know about newly added
// morph target (mMorphWeights.GetLength() == 0)
m_actor->ResizeTransformData();
m_actor->PostCreateInit(/*makeGeomLodsCompatibleWithSkeletalLODs=*/false, /*generateOBBs=*/false, /*convertUnitType=*/false);
m_actor->PostCreateInit(/*makeGeomLodsCompatibleWithSkeletalLODs=*/false, /*convertUnitType=*/false);
m_animGraph = AZStd::make_unique<AnimGraph>();
@@ -22,7 +22,7 @@ namespace EMotionFX
actor->SetID(0);
actor->GetSkeleton()->UpdateNodeIndexValues(0);
actor->ResizeTransformData();
actor->PostCreateInit(/*makeGeomLodsCompatibleWithSkeletalLODs=*/false, /*generateOBBs=*/false, /*convertUnitType=*/false);
actor->PostCreateInit(/*makeGeomLodsCompatibleWithSkeletalLODs=*/false, /*convertUnitType=*/false);
return actor;
}
};
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7db74f39a261bb70e1fdbdd546c337107809cdbdd1fc52568a0d30358b0f83d7
size 30005
oid sha256:55ecbc78a913c808cd007326b51dfc98b943b196b77fd6dc16aff0892b112e74
size 16948
@@ -68,7 +68,7 @@ namespace EMotionFX
// Without this call, the bind pose does not know about newly added morph target (mMorphWeights.GetLength() == 0)
m_actor->ResizeTransformData();
m_actor->PostCreateInit(/*makeGeomLodsCompatibleWithSkeletalLODs=*/false, /*generateOBBs=*/false, /*convertUnitType=*/false);
m_actor->PostCreateInit(/*makeGeomLodsCompatibleWithSkeletalLODs=*/false, /*convertUnitType=*/false);
m_animGraph = AZStd::make_unique<AnimGraph>();