Add some debug rendering options. (#4950)

* [WIP] Adding rendering options

Signed-off-by: rhhong <rhhong@amazon.com>

* code review feedback. Also add the renderflag as qsettings.

Signed-off-by: rhhong <rhhong@amazon.com>

* fix broken test

Signed-off-by: rhhong <rhhong@amazon.com>

* fix linux build

Signed-off-by: rhhong <rhhong@amazon.com>
monroegm-disable-blank-issue-2
Roman 4 years ago committed by GitHub
parent 7ab0871327
commit 77d02ea657
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,5 +1,6 @@
<RCC>
<qresource prefix="/EMotionFXAtom">
<file>Camera_category.svg</file>
<file>Visualization.svg</file>
</qresource>
</RCC>

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 60 (88103) - https://sketch.com -->
<title>Icons / System / View</title>
<desc>Created with Sketch.</desc>
<g id="Icons-/-System-/-View" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<path d="M11.9842667,6 C14.2018891,6 17.5404669,8 22,12 C17.5404669,16 14.2018891,18 11.9842667,18 C9.76664426,18 6.43855537,16 2,12 C6.43855537,8 9.76664426,6 11.9842667,6 Z M12,7.5 C9.51471863,7.5 7.5,9.51471863 7.5,12 C7.5,14.4852814 9.51471863,16.5 12,16.5 C14.4852814,16.5 16.5,14.4852814 16.5,12 C16.5,9.51471863 14.4852814,7.5 12,7.5 Z M12,9 C13.6568542,9 15,10.3431458 15,12 C15,13.6568542 13.6568542,15 12,15 C10.3431458,15 9,13.6568542 9,12 C9,10.3431458 10.3431458,9 12,9 Z" id="Combined-Shape" fill="#FFFFFF"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 977 B

@ -0,0 +1,415 @@
/*
* 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 <AtomActorDebugDraw.h>
#include <Atom/RPI.Public/Scene.h>
#include <Atom/RPI.Public/AuxGeom/AuxGeomDraw.h>
#include <Atom/RPI.Public/AuxGeom/AuxGeomFeatureProcessorInterface.h>
#include <Integration/Rendering/RenderActorInstance.h>
#include <EMotionFX/Source/ActorInstance.h>
#include <EMotionFX/Source/DebugDraw.h>
#include <EMotionFX/Source/EMotionFXManager.h>
#include <EMotionFX/Source/SubMesh.h>
#include <EMotionFX/Source/TransformData.h>
#include <EMotionFX/Source/Mesh.h>
#include <EMotionFX/Source/Node.h>
namespace AZ::Render
{
AtomActorDebugDraw::AtomActorDebugDraw(AZ::EntityId entityId)
{
m_auxGeomFeatureProcessor = RPI::Scene::GetFeatureProcessorForEntity<RPI::AuxGeomFeatureProcessorInterface>(entityId);
}
void AtomActorDebugDraw::DebugDraw(const EMotionFX::ActorRenderFlagBitset& renderFlags, EMotionFX::ActorInstance* instance)
{
if (!m_auxGeomFeatureProcessor || !instance)
{
return;
}
RPI::AuxGeomDrawPtr auxGeom = m_auxGeomFeatureProcessor->GetDrawQueue();
if (!auxGeom)
{
return;
}
// Render aabb
if (renderFlags[EMotionFX::ActorRenderFlag::RENDER_AABB])
{
RenderAABB(instance);
}
// Render skeleton
if (renderFlags[EMotionFX::ActorRenderFlag::RENDER_LINESKELETON])
{
RenderSkeleton(instance);
}
// Render internal EMFX debug lines.
if (renderFlags[EMotionFX::ActorRenderFlag::RENDER_EMFX_DEBUG])
{
RenderEMFXDebugDraw(instance);
}
// Render vertex normal, face normal, tagent and wireframe.
const bool renderVertexNormals = renderFlags[EMotionFX::ActorRenderFlag::RENDER_VERTEXNORMALS];
const bool renderFaceNormals = renderFlags[EMotionFX::ActorRenderFlag::RENDER_FACENORMALS];
const bool renderTangents = renderFlags[EMotionFX::ActorRenderFlag::RENDER_TANGENTS];
const bool renderWireframe = renderFlags[EMotionFX::ActorRenderFlag::RENDER_WIREFRAME];
if (renderVertexNormals || renderFaceNormals || renderTangents || renderWireframe)
{
// Iterate through all enabled nodes
const EMotionFX::Pose* pose = instance->GetTransformData()->GetCurrentPose();
const size_t geomLODLevel = instance->GetLODLevel();
const size_t numEnabled = instance->GetNumEnabledNodes();
for (size_t i = 0; i < numEnabled; ++i)
{
EMotionFX::Node* node = instance->GetActor()->GetSkeleton()->GetNode(instance->GetEnabledNode(i));
EMotionFX::Mesh* mesh = instance->GetActor()->GetMesh(geomLODLevel, node->GetNodeIndex());
const AZ::Transform globalTM = pose->GetWorldSpaceTransform(node->GetNodeIndex()).ToAZTransform();
m_currentMesh = nullptr;
if (!mesh)
{
continue;
}
RenderNormals(mesh, globalTM, renderVertexNormals, renderFaceNormals);
if (renderTangents)
{
RenderTangents(mesh, globalTM);
}
}
}
}
void AtomActorDebugDraw::PrepareForMesh(EMotionFX::Mesh* mesh, const AZ::Transform& worldTM)
{
// Check if we have already prepared for the given mesh
if (m_currentMesh == mesh)
{
return;
}
// Set our new current mesh
m_currentMesh = mesh;
// Get the number of vertices and the data
const uint32 numVertices = m_currentMesh->GetNumVertices();
AZ::Vector3* positions = (AZ::Vector3*)m_currentMesh->FindVertexData(EMotionFX::Mesh::ATTRIB_POSITIONS);
// Check if the vertices fits in our buffer
if (m_worldSpacePositions.size() < numVertices)
{
m_worldSpacePositions.resize(numVertices);
}
// Pre-calculate the world space positions
for (uint32 i = 0; i < numVertices; ++i)
{
m_worldSpacePositions[i] = worldTM.TransformPoint(positions[i]);
}
}
void AtomActorDebugDraw::RenderAABB(EMotionFX::ActorInstance* instance)
{
RPI::AuxGeomDrawPtr auxGeom = m_auxGeomFeatureProcessor->GetDrawQueue();
const AZ::Aabb& aabb = instance->GetAabb();
auxGeom->DrawAabb(aabb, AZ::Color(0.0f, 1.0f, 1.0f, 1.0f), RPI::AuxGeomDraw::DrawStyle::Line);
}
void AtomActorDebugDraw::RenderSkeleton(EMotionFX::ActorInstance* instance)
{
RPI::AuxGeomDrawPtr auxGeom = m_auxGeomFeatureProcessor->GetDrawQueue();
const EMotionFX::TransformData* transformData = instance->GetTransformData();
const EMotionFX::Skeleton* skeleton = instance->GetActor()->GetSkeleton();
const EMotionFX::Pose* pose = transformData->GetCurrentPose();
const size_t lodLevel = instance->GetLODLevel();
const size_t numJoints = skeleton->GetNumNodes();
m_auxVertices.clear();
m_auxVertices.reserve(numJoints * 2);
for (size_t jointIndex = 0; jointIndex < numJoints; ++jointIndex)
{
const EMotionFX::Node* joint = skeleton->GetNode(jointIndex);
if (!joint->GetSkeletalLODStatus(lodLevel))
{
continue;
}
const size_t parentIndex = joint->GetParentIndex();
if (parentIndex == InvalidIndex)
{
continue;
}
const AZ::Vector3 parentPos = pose->GetWorldSpaceTransform(parentIndex).m_position;
m_auxVertices.emplace_back(parentPos);
const AZ::Vector3 bonePos = pose->GetWorldSpaceTransform(jointIndex).m_position;
m_auxVertices.emplace_back(bonePos);
}
const AZ::Color skeletonColor(0.604f, 0.804f, 0.196f, 1.0f);
RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments lineArgs;
lineArgs.m_verts = m_auxVertices.data();
lineArgs.m_vertCount = static_cast<uint32_t>(m_auxVertices.size());
lineArgs.m_colors = &skeletonColor;
lineArgs.m_colorCount = 1;
lineArgs.m_depthTest = RPI::AuxGeomDraw::DepthTest::Off;
auxGeom->DrawLines(lineArgs);
}
void AtomActorDebugDraw::RenderEMFXDebugDraw(EMotionFX::ActorInstance* instance)
{
RPI::AuxGeomDrawPtr auxGeom = m_auxGeomFeatureProcessor->GetDrawQueue();
EMotionFX::DebugDraw& debugDraw = EMotionFX::GetDebugDraw();
debugDraw.Lock();
EMotionFX::DebugDraw::ActorInstanceData* actorInstanceData = debugDraw.GetActorInstanceData(instance);
actorInstanceData->Lock();
const AZStd::vector<EMotionFX::DebugDraw::Line>& lines = actorInstanceData->GetLines();
if (lines.empty())
{
actorInstanceData->Unlock();
debugDraw.Unlock();
return;
}
m_auxVertices.clear();
m_auxVertices.reserve(lines.size() * 2);
m_auxColors.clear();
m_auxColors.reserve(m_auxVertices.size());
for (const EMotionFX::DebugDraw::Line& line : actorInstanceData->GetLines())
{
m_auxVertices.emplace_back(line.m_start);
m_auxColors.emplace_back(line.m_startColor);
m_auxVertices.emplace_back(line.m_end);
m_auxColors.emplace_back(line.m_endColor);
}
AZ_Assert(m_auxVertices.size() == m_auxColors.size(), "Number of vertices and number of colors need to match.");
actorInstanceData->Unlock();
debugDraw.Unlock();
RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments lineArgs;
lineArgs.m_verts = m_auxVertices.data();
lineArgs.m_vertCount = static_cast<uint32_t>(m_auxVertices.size());
lineArgs.m_colors = m_auxColors.data();
lineArgs.m_colorCount = static_cast<uint32_t>(m_auxColors.size());
lineArgs.m_depthTest = RPI::AuxGeomDraw::DepthTest::Off;
auxGeom->DrawLines(lineArgs);
}
void AtomActorDebugDraw::RenderNormals(EMotionFX::Mesh* mesh, const AZ::Transform& worldTM, bool vertexNormals, bool faceNormals)
{
if (!mesh)
{
return;
}
if (!vertexNormals && !faceNormals)
{
return;
}
RPI::AuxGeomDrawPtr auxGeom = m_auxGeomFeatureProcessor->GetDrawQueue();
if (!auxGeom)
{
return;
}
// TODO: Move line color to a render setting.
const float faceNormalsScale = 0.01f;
const AZ::Color colorFaceNormals = AZ::Colors::Lime;
const float vertexNormalsScale = 0.01f;
const AZ::Color colorVertexNormals = AZ::Colors::Orange;
PrepareForMesh(mesh, worldTM);
AZ::Vector3* normals = (AZ::Vector3*)mesh->FindVertexData(EMotionFX::Mesh::ATTRIB_NORMALS);
// Render face normals
if (faceNormals)
{
const size_t numSubMeshes = mesh->GetNumSubMeshes();
for (uint32 subMeshIndex = 0; subMeshIndex < numSubMeshes; ++subMeshIndex)
{
EMotionFX::SubMesh* subMesh = mesh->GetSubMesh(subMeshIndex);
const uint32 numTriangles = subMesh->GetNumPolygons();
const uint32 startVertex = subMesh->GetStartVertex();
const uint32* indices = subMesh->GetIndices();
m_auxVertices.clear();
m_auxVertices.reserve(numTriangles * 2);
m_auxColors.clear();
m_auxColors.reserve(m_auxVertices.size());
for (uint32 triangleIndex = 0; triangleIndex < numTriangles; ++triangleIndex)
{
const uint32 triangleStartIndex = triangleIndex * 3;
const uint32 indexA = indices[triangleStartIndex + 0] + startVertex;
const uint32 indexB = indices[triangleStartIndex + 1] + startVertex;
const uint32 indexC = indices[triangleStartIndex + 2] + startVertex;
const AZ::Vector3& posA = m_worldSpacePositions[indexA];
const AZ::Vector3& posB = m_worldSpacePositions[indexB];
const AZ::Vector3& posC = m_worldSpacePositions[indexC];
const AZ::Vector3 normalDir = (posB - posA).Cross(posC - posA).GetNormalized();
// Calculate the center pos
const AZ::Vector3 normalPos = (posA + posB + posC) * (1.0f / 3.0f);
m_auxVertices.emplace_back(normalPos);
m_auxColors.emplace_back(colorFaceNormals);
m_auxVertices.emplace_back(normalPos + (normalDir * faceNormalsScale));
m_auxColors.emplace_back(colorFaceNormals);
}
}
RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments lineArgs;
lineArgs.m_verts = m_auxVertices.data();
lineArgs.m_vertCount = static_cast<uint32_t>(m_auxVertices.size());
lineArgs.m_colors = m_auxColors.data();
lineArgs.m_colorCount = static_cast<uint32_t>(m_auxColors.size());
lineArgs.m_depthTest = RPI::AuxGeomDraw::DepthTest::Off;
auxGeom->DrawLines(lineArgs);
}
// render vertex normals
if (vertexNormals)
{
const size_t numSubMeshes = mesh->GetNumSubMeshes();
for (uint32 subMeshIndex = 0; subMeshIndex < numSubMeshes; ++subMeshIndex)
{
EMotionFX::SubMesh* subMesh = mesh->GetSubMesh(subMeshIndex);
const uint32 numVertices = subMesh->GetNumVertices();
const uint32 startVertex = subMesh->GetStartVertex();
m_auxVertices.clear();
m_auxVertices.reserve(numVertices * 2);
m_auxColors.clear();
m_auxColors.reserve(m_auxVertices.size());
for (uint32 j = 0; j < numVertices; ++j)
{
const uint32 vertexIndex = j + startVertex;
const AZ::Vector3& position = m_worldSpacePositions[vertexIndex];
const AZ::Vector3 normal = worldTM.TransformVector(normals[vertexIndex]).GetNormalizedSafe() * vertexNormalsScale;
m_auxVertices.emplace_back(position);
m_auxColors.emplace_back(colorFaceNormals);
m_auxVertices.emplace_back(position + normal);
m_auxColors.emplace_back(colorFaceNormals);
}
}
RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments lineArgs;
lineArgs.m_verts = m_auxVertices.data();
lineArgs.m_vertCount = static_cast<uint32_t>(m_auxVertices.size());
lineArgs.m_colors = m_auxColors.data();
lineArgs.m_colorCount = static_cast<uint32_t>(m_auxColors.size());
lineArgs.m_depthTest = RPI::AuxGeomDraw::DepthTest::Off;
auxGeom->DrawLines(lineArgs);
}
}
void AtomActorDebugDraw::RenderTangents(EMotionFX::Mesh* mesh, const AZ::Transform& worldTM)
{
if (!mesh)
{
return;
}
RPI::AuxGeomDrawPtr auxGeom = m_auxGeomFeatureProcessor->GetDrawQueue();
if (!auxGeom)
{
return;
}
// TODO: Move line color to a render setting.
const AZ::Color colorTangents = AZ::Colors::Red;
const AZ::Color mirroredBitangentColor = AZ::Colors::Yellow;
const AZ::Color colorBitangents = AZ::Colors::White;
const float scale = 0.01f;
// Get the tangents and check if this mesh actually has tangents
AZ::Vector4* tangents = static_cast<AZ::Vector4*>(mesh->FindVertexData(EMotionFX::Mesh::ATTRIB_TANGENTS));
if (!tangents)
{
return;
}
AZ::Vector3* bitangents = static_cast<AZ::Vector3*>(mesh->FindVertexData(EMotionFX::Mesh::ATTRIB_BITANGENTS));
PrepareForMesh(mesh, worldTM);
AZ::Vector3* normals = (AZ::Vector3*)mesh->FindVertexData(EMotionFX::Mesh::ATTRIB_NORMALS);
const uint32 numVertices = mesh->GetNumVertices();
m_auxVertices.clear();
m_auxVertices.reserve(numVertices * 2);
m_auxColors.clear();
m_auxColors.reserve(m_auxVertices.size());
// Render the tangents and bitangents
AZ::Vector3 orgTangent, tangent, bitangent;
for (uint32 i = 0; i < numVertices; ++i)
{
orgTangent.Set(tangents[i].GetX(), tangents[i].GetY(), tangents[i].GetZ());
tangent = (worldTM.TransformVector(orgTangent)).GetNormalized();
if (bitangents)
{
bitangent = bitangents[i];
}
else
{
bitangent = tangents[i].GetW() * normals[i].Cross(orgTangent);
}
bitangent = (worldTM.TransformVector(bitangent)).GetNormalizedSafe();
m_auxVertices.emplace_back(m_worldSpacePositions[i]);
m_auxColors.emplace_back(colorTangents);
m_auxVertices.emplace_back(m_worldSpacePositions[i] + (tangent * scale));
m_auxColors.emplace_back(colorTangents);
if (tangents[i].GetW() < 0.0f)
{
m_auxVertices.emplace_back(m_worldSpacePositions[i]);
m_auxColors.emplace_back(mirroredBitangentColor);
m_auxVertices.emplace_back(m_worldSpacePositions[i] + (bitangent * scale));
m_auxColors.emplace_back(mirroredBitangentColor);
}
else
{
m_auxVertices.emplace_back(m_worldSpacePositions[i]);
m_auxColors.emplace_back(colorBitangents);
m_auxVertices.emplace_back(m_worldSpacePositions[i] + (bitangent * scale));
m_auxColors.emplace_back(colorBitangents);
}
}
RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments lineArgs;
lineArgs.m_verts = m_auxVertices.data();
lineArgs.m_vertCount = static_cast<uint32_t>(m_auxVertices.size());
lineArgs.m_colors = m_auxColors.data();
lineArgs.m_colorCount = static_cast<uint32_t>(m_auxColors.size());
lineArgs.m_depthTest = RPI::AuxGeomDraw::DepthTest::Off;
auxGeom->DrawLines(lineArgs);
}
} // namespace AZ::Render

@ -0,0 +1,56 @@
/*
* 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/std/containers/vector.h>
#include <AzCore/Math/Color.h>
#include <Integration/Rendering/RenderFlag.h>
#include <Integration/Rendering/RenderActorInstance.h>
namespace EMotionFX
{
class Mesh;
class ActorInstance;
}
namespace AZ::RPI
{
class AuxGeomDraw;
class AuxGeomFeatureProcessorInterface;
}
namespace AZ::Render
{
// Ultility class for atom debug render on actor
class AtomActorDebugDraw
{
public:
AtomActorDebugDraw(AZ::EntityId entityId);
void DebugDraw(const EMotionFX::ActorRenderFlagBitset& renderFlags, EMotionFX::ActorInstance* instance);
private:
void PrepareForMesh(EMotionFX::Mesh* mesh, const AZ::Transform& worldTM);
void RenderAABB(EMotionFX::ActorInstance* instance);
void RenderSkeleton(EMotionFX::ActorInstance* instance);
void RenderEMFXDebugDraw(EMotionFX::ActorInstance* instance);
void RenderNormals(EMotionFX::Mesh* mesh, const AZ::Transform& worldTM, bool vertexNormals, bool faceNormals);
void RenderTangents(EMotionFX::Mesh* mesh, const AZ::Transform& worldTM);
EMotionFX::Mesh* m_currentMesh = nullptr; /**< A pointer to the mesh whose world space positions are in the pre-calculated positions buffer.
NULL in case we haven't pre-calculated any positions yet. */
AZStd::vector<AZ::Vector3> m_worldSpacePositions; /**< The buffer used to store world space positions for rendering normals
tangents and the wireframe. */
RPI::AuxGeomFeatureProcessorInterface* m_auxGeomFeatureProcessor = nullptr;
AZStd::vector<AZ::Vector3> m_auxVertices;
AZStd::vector<AZ::Color> m_auxColors;
};
}

@ -8,6 +8,7 @@
#include <AtomActorInstance.h>
#include <AtomActor.h>
#include <AtomActorDebugDraw.h>
#include <ActorAsset.h>
#include <Atom/Feature/SkinnedMesh/SkinnedMeshInputBuffers.h>
@ -59,7 +60,7 @@ namespace AZ
AzFramework::BoundsRequestBus::Handler::BusConnect(m_entityId);
}
m_auxGeomFeatureProcessor = RPI::Scene::GetFeatureProcessorForEntity<RPI::AuxGeomFeatureProcessorInterface>(m_entityId);
m_atomActorDebugDraw = AZStd::make_unique<AtomActorDebugDraw>(entityId);
}
AtomActorInstance::~AtomActorInstance()
@ -78,6 +79,11 @@ namespace AZ
UpdateBounds();
}
void AtomActorInstance::DebugDraw(const EMotionFX::ActorRenderFlagBitset& renderFlags)
{
m_atomActorDebugDraw->DebugDraw(renderFlags, m_actorInstance);
}
void AtomActorInstance::UpdateBounds()
{
// Update RenderActorInstance world bounding box
@ -99,116 +105,6 @@ namespace AZ
AZ::Interface<AzFramework::IEntityBoundsUnion>::Get()->RefreshEntityLocalBoundsUnion(m_entityId);
}
void AtomActorInstance::DebugDraw(const DebugOptions& debugOptions)
{
if (m_auxGeomFeatureProcessor)
{
if (RPI::AuxGeomDrawPtr auxGeom = m_auxGeomFeatureProcessor->GetDrawQueue())
{
if (debugOptions.m_drawAABB)
{
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)
{
RenderSkeleton(auxGeom.get());
}
if (debugOptions.m_emfxDebugDraw)
{
RenderEMFXDebugDraw(auxGeom.get());
}
}
}
}
void AtomActorInstance::RenderSkeleton(RPI::AuxGeomDraw* auxGeom)
{
AZ_Assert(m_actorInstance, "Valid actor instance required.");
const EMotionFX::TransformData* transformData = m_actorInstance->GetTransformData();
const EMotionFX::Skeleton* skeleton = m_actorInstance->GetActor()->GetSkeleton();
const EMotionFX::Pose* pose = transformData->GetCurrentPose();
const size_t lodLevel = m_actorInstance->GetLODLevel();
const size_t numJoints = skeleton->GetNumNodes();
m_auxVertices.clear();
m_auxVertices.reserve(numJoints * 2);
for (size_t jointIndex = 0; jointIndex < numJoints; ++jointIndex)
{
const EMotionFX::Node* joint = skeleton->GetNode(jointIndex);
if (!joint->GetSkeletalLODStatus(lodLevel))
{
continue;
}
const size_t parentIndex = joint->GetParentIndex();
if (parentIndex == InvalidIndex)
{
continue;
}
const AZ::Vector3 parentPos = pose->GetWorldSpaceTransform(parentIndex).m_position;
m_auxVertices.emplace_back(parentPos);
const AZ::Vector3 bonePos = pose->GetWorldSpaceTransform(jointIndex).m_position;
m_auxVertices.emplace_back(bonePos);
}
const AZ::Color skeletonColor(0.604f, 0.804f, 0.196f, 1.0f);
RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments lineArgs;
lineArgs.m_verts = m_auxVertices.data();
lineArgs.m_vertCount = static_cast<uint32_t>(m_auxVertices.size());
lineArgs.m_colors = &skeletonColor;
lineArgs.m_colorCount = 1;
lineArgs.m_depthTest = RPI::AuxGeomDraw::DepthTest::Off;
auxGeom->DrawLines(lineArgs);
}
void AtomActorInstance::RenderEMFXDebugDraw(RPI::AuxGeomDraw* auxGeom)
{
EMotionFX::DebugDraw& debugDraw = EMotionFX::GetDebugDraw();
debugDraw.Lock();
EMotionFX::DebugDraw::ActorInstanceData* actorInstanceData = debugDraw.GetActorInstanceData(m_actorInstance);
actorInstanceData->Lock();
const AZStd::vector<EMotionFX::DebugDraw::Line>& lines = actorInstanceData->GetLines();
if (lines.empty())
{
actorInstanceData->Unlock();
debugDraw.Unlock();
return;
}
m_auxVertices.clear();
m_auxVertices.reserve(lines.size() * 2);
m_auxColors.clear();
m_auxColors.reserve(m_auxVertices.size());
for (const EMotionFX::DebugDraw::Line& line : actorInstanceData->GetLines())
{
m_auxVertices.emplace_back(line.m_start);
m_auxColors.emplace_back(line.m_startColor);
m_auxVertices.emplace_back(line.m_end);
m_auxColors.emplace_back(line.m_endColor);
}
AZ_Assert(m_auxVertices.size() == m_auxColors.size(),
"Number of vertices and number of colors need to match.");
actorInstanceData->Unlock();
debugDraw.Unlock();
RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments lineArgs;
lineArgs.m_verts = m_auxVertices.data();
lineArgs.m_vertCount = static_cast<uint32_t>(m_auxVertices.size());
lineArgs.m_colors = m_auxColors.data();
lineArgs.m_colorCount = static_cast<uint32_t>(m_auxColors.size());
lineArgs.m_depthTest = RPI::AuxGeomDraw::DepthTest::Off;
auxGeom->DrawLines(lineArgs);
}
AZ::Aabb AtomActorInstance::GetWorldBounds()
{
return m_worldAABB;

@ -53,6 +53,7 @@ namespace AZ
class SkinnedMeshInputBuffers;
class MeshFeatureProcessorInterface;
class AtomActor;
class AtomActorDebugDraw;
//! Render node for managing and rendering actor instances. Each Actor Component
//! creates an ActorRenderNode. The render node is responsible for drawing meshes and
@ -85,8 +86,8 @@ namespace AZ
// RenderActorInstance overrides ...
void OnTick(float timeDelta) override;
void DebugDraw(const EMotionFX::ActorRenderFlagBitset& renderFlags);
void UpdateBounds() override;
void DebugDraw(const DebugOptions& debugOptions) override;
void SetMaterials(const EMotionFX::Integration::ActorAsset::MaterialList& materialPerLOD) override { AZ_UNUSED(materialPerLOD); };
void SetSkinningMethod(EMotionFX::Integration::SkinningMethod emfxSkinningMethod) override;
SkinningMethod GetAtomSkinningMethod() const;
@ -184,12 +185,8 @@ namespace AZ
void InitWrinkleMasks();
void UpdateWrinkleMasks();
// Helper and debug geometry rendering
void RenderSkeleton(RPI::AuxGeomDraw* auxGeom);
void RenderEMFXDebugDraw(RPI::AuxGeomDraw* auxGeom);
RPI::AuxGeomFeatureProcessorInterface* m_auxGeomFeatureProcessor = nullptr;
AZStd::vector<AZ::Vector3> m_auxVertices;
AZStd::vector<AZ::Color> m_auxColors;
// Debug geometry rendering
AZStd::unique_ptr<AtomActorDebugDraw> m_atomActorDebugDraw;
AZStd::intrusive_ptr<AZ::Render::SkinnedMeshInputBuffers> m_skinnedMeshInputBuffers = nullptr;
AZStd::intrusive_ptr<SkinnedMeshInstance> m_skinnedMeshInstance;

@ -206,6 +206,20 @@ namespace EMStudio
return result;
}
void AnimViewportRenderer::UpdateActorRenderFlag(EMotionFX::ActorRenderFlagBitset renderFlags)
{
for (AZ::Entity* entity : m_actorEntities)
{
EMotionFX::Integration::ActorComponent* actorComponent = entity->FindComponent<EMotionFX::Integration::ActorComponent>();
if (!actorComponent)
{
AZ_Assert(false, "Found entity without actor component in the actor entity list.");
continue;
}
actorComponent->SetRenderFlag(renderFlags);
}
}
void AnimViewportRenderer::ResetEnvironment()
{
// Reset environment

@ -52,6 +52,8 @@ namespace EMStudio
//! Return the center position of the existing objects.
AZ::Vector3 GetCharacterCenter() const;
void UpdateActorRenderFlag(EMotionFX::ActorRenderFlagBitset renderFlags);
private:
// This function resets the light, camera and other environment settings.
@ -79,10 +81,6 @@ namespace EMStudio
AZ::Entity* m_postProcessEntity = nullptr;
AZ::Entity* m_iblEntity = nullptr;
AZ::Entity* m_cameraEntity = nullptr;
AZ::Component* m_cameraComponent = nullptr;
AZ::Entity* m_modelEntity = nullptr;
AZ::Data::AssetId m_modelAssetId;
AZ::Entity* m_gridEntity = nullptr;
AZStd::vector<AZ::Entity*> m_actorEntities;

@ -8,7 +8,7 @@
#pragma once
#include <AzCore/EBus/EBus.h>
#include <Integration/Rendering/RenderFlag.h>
namespace EMStudio
{
@ -35,6 +35,9 @@ namespace EMStudio
//! Set the camera view mode.
virtual void SetCameraViewMode(CameraViewMode mode) = 0;
//! Toggle render option flag
virtual void ToggleRenderFlag(EMotionFX::ActorRenderFlag flag) = 0;
};
using AnimViewportRequestBus = AZ::EBus<AnimViewportRequests>;

@ -13,7 +13,6 @@
#include <AzCore/std/string/string.h>
#include <AzQtComponents/Components/Widgets/ToolBar.h>
namespace EMStudio
{
AnimViewportToolBar::AnimViewportToolBar(QWidget* parent)
@ -21,40 +20,107 @@ namespace EMStudio
{
AzQtComponents::ToolBar::addMainToolBarStyle(this);
// Add the render view options button
QToolButton* renderOptionsButton = new QToolButton(this);
{
QMenu* contextMenu = new QMenu(renderOptionsButton);
renderOptionsButton->setText("Render Options");
renderOptionsButton->setMenu(contextMenu);
renderOptionsButton->setPopupMode(QToolButton::InstantPopup);
renderOptionsButton->setVisible(true);
renderOptionsButton->setIcon(QIcon(":/EMotionFXAtom/Visualization.svg"));
addWidget(renderOptionsButton);
CreateViewOptionEntry(contextMenu, "Solid", EMotionFX::ActorRenderFlag::RENDER_SOLID);
CreateViewOptionEntry(contextMenu, "Wireframe", EMotionFX::ActorRenderFlag::RENDER_WIREFRAME);
CreateViewOptionEntry(contextMenu, "Lighting", EMotionFX::ActorRenderFlag::RENDER_LIGHTING);
CreateViewOptionEntry(contextMenu, "Backface Culling", EMotionFX::ActorRenderFlag::RENDER_BACKFACECULLING);
contextMenu->addSeparator();
CreateViewOptionEntry(contextMenu, "Vertex Normals", EMotionFX::ActorRenderFlag::RENDER_VERTEXNORMALS);
CreateViewOptionEntry(contextMenu, "Face Normals", EMotionFX::ActorRenderFlag::RENDER_FACENORMALS);
CreateViewOptionEntry(contextMenu, "Tangents", EMotionFX::ActorRenderFlag::RENDER_TANGENTS);
CreateViewOptionEntry(contextMenu, "Actor Bounding Boxes", EMotionFX::ActorRenderFlag::RENDER_AABB);
contextMenu->addSeparator();
CreateViewOptionEntry(contextMenu, "Line Skeleton", EMotionFX::ActorRenderFlag::RENDER_LINESKELETON);
CreateViewOptionEntry(contextMenu, "Solid Skeleton", EMotionFX::ActorRenderFlag::RENDER_SKELETON);
CreateViewOptionEntry(contextMenu, "Joint Names", EMotionFX::ActorRenderFlag::RENDER_NODENAMES);
CreateViewOptionEntry(contextMenu, "Joint Orientations", EMotionFX::ActorRenderFlag::RENDER_NODEORIENTATION);
CreateViewOptionEntry(contextMenu, "Actor Bind Pose", EMotionFX::ActorRenderFlag::RENDER_ACTORBINDPOSE);
contextMenu->addSeparator();
}
// Add the camera button
QToolButton* cameraButton = new QToolButton(this);
QMenu* cameraMenu = new QMenu(cameraButton);
{
QMenu* cameraMenu = new QMenu(cameraButton);
// Add the camera option
const AZStd::vector<AZStd::pair<CameraViewMode, AZStd::string>> cameraOptionNames = {
{ CameraViewMode::FRONT, "Front" }, { CameraViewMode::BACK, "Back" }, { CameraViewMode::TOP, "Top" },
{ CameraViewMode::BOTTOM, "Bottom" }, { CameraViewMode::LEFT, "Left" }, { CameraViewMode::RIGHT, "Right" },
};
// Add the camera option
const AZStd::vector<AZStd::pair<CameraViewMode, AZStd::string>> cameraOptionNames = {
{ CameraViewMode::FRONT, "Front" }, { CameraViewMode::BACK, "Back" }, { CameraViewMode::TOP, "Top" },
{ CameraViewMode::BOTTOM, "Bottom" }, { CameraViewMode::LEFT, "Left" }, { CameraViewMode::RIGHT, "Right" },
};
for (const auto& pair : cameraOptionNames)
{
CameraViewMode mode = pair.first;
for (const auto& pair : cameraOptionNames)
{
CameraViewMode mode = pair.first;
cameraMenu->addAction(
pair.second.c_str(),
[mode]()
{
// Send the reset camera event.
AnimViewportRequestBus::Broadcast(&AnimViewportRequestBus::Events::SetCameraViewMode, mode);
});
}
cameraMenu->addSeparator();
cameraMenu->addAction(
pair.second.c_str(),
[mode]()
"Reset Camera",
[]()
{
// Send the reset camera event.
AnimViewportRequestBus::Broadcast(&AnimViewportRequestBus::Events::SetCameraViewMode, mode);
AnimViewportRequestBus::Broadcast(&AnimViewportRequestBus::Events::ResetCamera);
});
cameraButton->setMenu(cameraMenu);
cameraButton->setText("Camera Option");
cameraButton->setPopupMode(QToolButton::InstantPopup);
cameraButton->setVisible(true);
cameraButton->setIcon(QIcon(":/EMotionFXAtom/Camera_category.svg"));
addWidget(cameraButton);
}
}
cameraMenu->addSeparator();
cameraMenu->addAction("Reset Camera",
[]()
void AnimViewportToolBar::CreateViewOptionEntry(
QMenu* menu, const char* menuEntryName, uint32_t actionIndex, bool visible, char* iconFileName)
{
QAction* action = menu->addAction(
menuEntryName,
[actionIndex]()
{
// Send the reset camera event.
AnimViewportRequestBus::Broadcast(&AnimViewportRequestBus::Events::ResetCamera);
AnimViewportRequestBus::Broadcast(
&AnimViewportRequestBus::Events::ToggleRenderFlag, (EMotionFX::ActorRenderFlag)actionIndex);
});
cameraButton->setMenu(cameraMenu);
cameraButton->setText("Camera Option");
cameraButton->setPopupMode(QToolButton::InstantPopup);
cameraButton->setVisible(true);
cameraButton->setIcon(QIcon(":/EMotionFXAtom/Camera_category.svg"));
addWidget(cameraButton);
action->setCheckable(true);
action->setVisible(visible);
if (iconFileName)
{
action->setIcon(QIcon(iconFileName));
}
m_actions[actionIndex] = action;
}
void AnimViewportToolBar::SetRenderFlags(EMotionFX::ActorRenderFlagBitset renderFlags)
{
for (size_t i = 0; i < renderFlags.size(); ++i)
{
QAction* action = m_actions[i];
if (action)
{
action->setChecked(renderFlags[i]);
}
}
}
} // namespace EMStudio

@ -11,8 +11,11 @@
#if !defined(Q_MOC_RUN)
#include <QAction>
#include <QToolBar>
#include <QMenu>
#endif
#include <Integration/Rendering/RenderFlag.h>
namespace EMStudio
{
class AnimViewportToolBar : public QToolBar
@ -20,5 +23,13 @@ namespace EMStudio
public:
AnimViewportToolBar(QWidget* parent = nullptr);
~AnimViewportToolBar() = default;
void SetRenderFlags(EMotionFX::ActorRenderFlagBitset renderFlags);
private:
void CreateViewOptionEntry(
QMenu* menu, const char* menuEntryName, uint32_t actionIndex, bool visible = true, char* iconFileName = nullptr);
QAction* m_actions[EMotionFX::ActorRenderFlag::NUM_RENDERFLAGS] = { nullptr };
};
}

@ -11,6 +11,7 @@
#include <AzFramework/Viewport/CameraInput.h>
#include <Atom/RPI.Public/ViewportContext.h>
#include <AtomToolsFramework/Viewport/ModularViewportCameraController.h>
#include <EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/EMStudioManager.h>
#include <EMStudio/AnimViewportWidget.h>
#include <EMStudio/AnimViewportRenderer.h>
@ -32,6 +33,7 @@ namespace EMStudio
m_renderer = AZStd::make_unique<AnimViewportRenderer>(GetViewportContext());
LoadRenderFlags();
SetupCameras();
SetupCameraController();
Reinit();
@ -41,6 +43,7 @@ namespace EMStudio
AnimViewportWidget::~AnimViewportWidget()
{
SaveRenderFlags();
AnimViewportRequestBus::Handler::BusDisconnect();
}
@ -50,7 +53,14 @@ namespace EMStudio
{
ResetCamera();
}
m_renderer->Reinit();
m_renderer->UpdateActorRenderFlag(m_renderFlags);
}
EMotionFX::ActorRenderFlagBitset AnimViewportWidget::GetRenderFlags() const
{
return m_renderFlags;
}
void AnimViewportWidget::SetupCameras()
@ -123,7 +133,7 @@ namespace EMStudio
SetCameraViewMode(CameraViewMode::DEFAULT);
}
void AnimViewportWidget::SetCameraViewMode([[maybe_unused]]CameraViewMode mode)
void AnimViewportWidget::SetCameraViewMode(CameraViewMode mode)
{
// Set the camera view mode.
const AZ::Vector3 targetPosition = m_renderer->GetCharacterCenter();
@ -155,4 +165,38 @@ namespace EMStudio
}
GetViewportContext()->SetCameraTransform(AZ::Transform::CreateLookAt(cameraPosition, targetPosition));
}
void AnimViewportWidget::ToggleRenderFlag(EMotionFX::ActorRenderFlag flag)
{
m_renderFlags[flag] = !m_renderFlags[flag];
m_renderer->UpdateActorRenderFlag(m_renderFlags);
}
void AnimViewportWidget::LoadRenderFlags()
{
AZStd::string renderFlagsFilename(EMStudioManager::GetInstance()->GetAppDataFolder());
renderFlagsFilename += "AnimViewportRenderFlags.cfg";
QSettings settings(renderFlagsFilename.c_str(), QSettings::IniFormat, this);
for (uint32 i = 0; i < EMotionFX::ActorRenderFlag::NUM_RENDERFLAGS; ++i)
{
QString name = QString(i);
const bool isEnabled = settings.value(name).toBool();
m_renderFlags[i] = isEnabled;
}
m_renderer->UpdateActorRenderFlag(m_renderFlags);
}
void AnimViewportWidget::SaveRenderFlags()
{
AZStd::string renderFlagsFilename(EMStudioManager::GetInstance()->GetAppDataFolder());
renderFlagsFilename += "AnimViewportRenderFlags.cfg";
QSettings settings(renderFlagsFilename.c_str(), QSettings::IniFormat, this);
for (uint32 i = 0; i < EMotionFX::ActorRenderFlag::NUM_RENDERFLAGS; ++i)
{
QString name = QString(i);
settings.setValue(name, (bool)m_renderFlags[i]);
}
}
} // namespace EMStudio

@ -7,9 +7,11 @@
*/
#pragma once
#include <QSettings>
#include <AtomToolsFramework/Viewport/RenderViewportWidget.h>
#include <AzFramework/Viewport/CameraInput.h>
#include <EMStudio/AnimViewportRequestBus.h>
#include <Integration/Rendering/RenderFlag.h>
namespace EMStudio
{
@ -25,14 +27,19 @@ namespace EMStudio
AnimViewportRenderer* GetAnimViewportRenderer() { return m_renderer.get(); }
void Reinit(bool resetCamera = true);
EMotionFX::ActorRenderFlagBitset GetRenderFlags() const;
private:
void SetupCameras();
void SetupCameraController();
void LoadRenderFlags();
void SaveRenderFlags();
// AnimViewportRequestBus::Handler overrides
void ResetCamera();
void SetCameraViewMode(CameraViewMode mode);
void ToggleRenderFlag(EMotionFX::ActorRenderFlag flag);
static constexpr float CameraDistance = 2.0f;
@ -40,5 +47,6 @@ namespace EMStudio
AZStd::shared_ptr<AzFramework::RotateCameraInput> m_rotateCamera;
AZStd::shared_ptr<AzFramework::TranslateCameraInput> m_translateCamera;
AZStd::shared_ptr<AzFramework::OrbitDollyScrollCameraInput> m_orbitDollyScrollCamera;
EMotionFX::ActorRenderFlagBitset m_renderFlags;
};
}

@ -90,12 +90,14 @@ namespace EMStudio
verticalLayout->setSpacing(1);
verticalLayout->setMargin(0);
// Add the viewport widget
m_animViewportWidget = new AnimViewportWidget(m_innerWidget);
// Add the tool bar
AnimViewportToolBar* toolBar = new AnimViewportToolBar(m_innerWidget);
verticalLayout->addWidget(toolBar);
toolBar->SetRenderFlags(m_animViewportWidget->GetRenderFlags());
// Add the viewport widget
m_animViewportWidget = new AnimViewportWidget(m_innerWidget);
verticalLayout->addWidget(toolBar);
verticalLayout->addWidget(m_animViewportWidget);
// Register command callbacks.

@ -17,4 +17,6 @@ set(FILES
Source/AtomActor.cpp
Source/AtomActorInstance.h
Source/AtomActorInstance.cpp
Source/AtomActorDebugDraw.h
Source/AtomActorDebugDraw.cpp
)

@ -85,9 +85,6 @@ namespace EMotionFX
/// Detach from parent entity, if attached.
virtual void DetachFromEntity() {}
/// Enables debug-drawing of the actor's root.
virtual void DebugDrawRoot(bool /*enable*/) {}
/// Enables rendering of the actor.
virtual bool GetRenderCharacter() const = 0;
virtual void SetRenderCharacter(bool enable) = 0;

@ -209,7 +209,6 @@ namespace EMotionFX
->Event("GetJointTransform", &ActorComponentRequestBus::Events::GetJointTransform)
->Event("AttachToEntity", &ActorComponentRequestBus::Events::AttachToEntity)
->Event("DetachFromEntity", &ActorComponentRequestBus::Events::DetachFromEntity)
->Event("DebugDrawRoot", &ActorComponentRequestBus::Events::DebugDrawRoot)
->Event("GetRenderCharacter", &ActorComponentRequestBus::Events::GetRenderCharacter)
->Event("SetRenderCharacter", &ActorComponentRequestBus::Events::SetRenderCharacter)
->Event("GetRenderActorVisible", &ActorComponentRequestBus::Events::GetRenderActorVisible)
@ -238,8 +237,7 @@ namespace EMotionFX
//////////////////////////////////////////////////////////////////////////
ActorComponent::ActorComponent(const Configuration* configuration)
: m_debugDrawRoot(false)
, m_sceneFinishSimHandler([this]([[maybe_unused]] AzPhysics::SceneHandle sceneHandle,
: m_sceneFinishSimHandler([this]([[maybe_unused]] AzPhysics::SceneHandle sceneHandle,
float fixedDeltatime)
{
if (m_actorInstance)
@ -252,6 +250,8 @@ namespace EMotionFX
{
m_configuration = *configuration;
}
m_debugRenderFlags[RENDER_SOLID] = true;
}
//////////////////////////////////////////////////////////////////////////
@ -341,12 +341,6 @@ namespace EMotionFX
}
}
//////////////////////////////////////////////////////////////////////////
void ActorComponent::DebugDrawRoot(bool enable)
{
m_debugDrawRoot = enable;
}
//////////////////////////////////////////////////////////////////////////
bool ActorComponent::GetRenderCharacter() const
{
@ -400,6 +394,11 @@ namespace EMotionFX
return m_sceneFinishSimHandler.IsConnected();
}
void ActorComponent::SetRenderFlag(ActorRenderFlagBitset renderFlags)
{
m_debugRenderFlags = renderFlags;
}
void ActorComponent::CheckActorCreation()
{
// Create actor instance.
@ -573,13 +572,13 @@ namespace EMotionFX
m_actorInstance->SetIsVisible(isInCameraFrustum && m_configuration.m_renderCharacter);
}
RenderActorInstance::DebugOptions debugOptions;
debugOptions.m_drawAABB = m_configuration.m_renderBounds;
debugOptions.m_drawSkeleton = m_configuration.m_renderSkeleton;
debugOptions.m_drawRootTransform = m_debugDrawRoot;
debugOptions.m_rootWorldTransform = GetEntity()->GetTransform()->GetWorldTM();
debugOptions.m_emfxDebugDraw = true;
m_renderActorInstance->DebugDraw(debugOptions);
m_renderActorInstance->SetIsVisible(m_debugRenderFlags[RENDER_SOLID]);
// The configuration stores some debug option. When that is enabled, we override it on top of the render flags.
m_debugRenderFlags[RENDER_AABB] = m_debugRenderFlags[RENDER_AABB] || m_configuration.m_renderBounds;
m_debugRenderFlags[RENDER_SKELETON] = m_debugRenderFlags[RENDER_SKELETON] || m_configuration.m_renderSkeleton;
m_debugRenderFlags[RENDER_EMFX_DEBUG] = true;
m_renderActorInstance->DebugDraw(m_debugRenderFlags);
}
}

@ -116,7 +116,6 @@ namespace EMotionFX
ActorInstance* GetActorInstance() override { return m_actorInstance.get(); }
void AttachToEntity(AZ::EntityId targetEntityId, AttachmentType attachmentType) override;
void DetachFromEntity() override;
void DebugDrawRoot(bool enable) override;
bool GetRenderCharacter() const override;
void SetRenderCharacter(bool enable) override;
bool GetRenderActorVisible() const override;
@ -181,6 +180,8 @@ namespace EMotionFX
bool IsPhysicsSceneSimulationFinishEventConnected() const;
AZ::Data::Asset<ActorAsset> GetActorAsset() const { return m_configuration.m_actorAsset; }
void SetRenderFlag(ActorRenderFlagBitset renderFlags);
private:
// AZ::TransformNotificationBus::MultiHandler
void OnTransformChanged(const AZ::Transform& local, const AZ::Transform& world) override;
@ -201,7 +202,7 @@ namespace EMotionFX
AZStd::vector<AZ::EntityId> m_attachments;
AZStd::unique_ptr<RenderActorInstance> m_renderActorInstance;
bool m_debugDrawRoot; ///< Enables drawing of actor root and facing.
ActorRenderFlagBitset m_debugRenderFlags; ///< Actor debug render flag
AzPhysics::SceneEvents::OnSceneSimulationFinishHandler m_sceneFinishSimHandler;
};

@ -180,6 +180,7 @@ namespace EMotionFX
, m_lodLevel(0)
, m_actorAsset(AZ::Data::AssetLoadBehavior::NoLoad)
{
m_debugRenderFlags[RENDER_SOLID] = true;
}
//////////////////////////////////////////////////////////////////////////
@ -604,11 +605,10 @@ namespace EMotionFX
m_renderActorInstance->OnTick(deltaTime);
m_renderActorInstance->UpdateBounds();
RenderActorInstance::DebugOptions debugOptions;
debugOptions.m_drawAABB = m_renderBounds;
debugOptions.m_drawSkeleton = m_renderSkeleton;
debugOptions.m_emfxDebugDraw = true;
m_renderActorInstance->DebugDraw(debugOptions);
m_debugRenderFlags[RENDER_AABB] = m_renderBounds;
m_debugRenderFlags[RENDER_SKELETON] = m_renderSkeleton;
m_debugRenderFlags[RENDER_EMFX_DEBUG] = true;
m_renderActorInstance->DebugDraw(m_debugRenderFlags);
}
}
@ -951,5 +951,10 @@ namespace EMotionFX
LmbrCentral::AttachmentComponentRequestBus::Event(attachment, &LmbrCentral::AttachmentComponentRequestBus::Events::Reattach, true);
}
}
void EditorActorComponent::SetRenderFlag(ActorRenderFlagBitset renderFlags)
{
m_debugRenderFlags = renderFlags;
}
} //namespace Integration
} // namespace EMotionFX

@ -104,6 +104,8 @@ namespace EMotionFX
ActorComponent::GetRequiredServices(required);
}
void SetRenderFlag(ActorRenderFlagBitset renderFlags);
static void Reflect(AZ::ReflectContext* context);
private:
@ -162,6 +164,7 @@ namespace EMotionFX
size_t m_lodLevel;
ActorComponent::BoundingBoxConfiguration m_bboxConfig;
bool m_forceUpdateJointsOOV = false;
ActorRenderFlagBitset m_debugRenderFlags; ///< Actor debug render flag
// \todo attachmentTarget node nr
// Note: LOD work in progress. For now we use one material instead of a list of material, because we don't have the support for LOD with multiple scene files.

@ -16,6 +16,7 @@
#include <Integration/ActorComponentBus.h>
#include <Integration/Assets/ActorAsset.h>
#include <Integration/Rendering/RenderFlag.h>
namespace EMotionFX
{
@ -33,16 +34,7 @@ namespace EMotionFX
virtual ~RenderActorInstance() = default;
virtual void OnTick(float timeDelta) = 0;
struct DebugOptions
{
bool m_drawAABB = false;
bool m_drawSkeleton = false;
bool m_drawRootTransform = false;
AZ::Transform m_rootWorldTransform = AZ::Transform::CreateIdentity();
bool m_emfxDebugDraw = false;
};
virtual void DebugDraw(const DebugOptions& debugOptions) = 0;
virtual void DebugDraw(const EMotionFX::ActorRenderFlagBitset& renderFlags) = 0;
SkinningMethod GetSkinningMethod() const;
virtual void SetSkinningMethod(SkinningMethod skinningMethod);

@ -0,0 +1,44 @@
/*
* 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/std/containers/bitset.h>
namespace EMotionFX
{
enum ActorRenderFlag
{
RENDER_SOLID = 0,
RENDER_WIREFRAME = 1,
RENDER_LIGHTING = 2,
RENDER_SHADOWS = 3,
RENDER_FACENORMALS = 4,
RENDER_VERTEXNORMALS = 5,
RENDER_TANGENTS = 6,
RENDER_AABB = 7,
RENDER_SKELETON = 8,
RENDER_LINESKELETON = 9,
RENDER_NODEORIENTATION = 10,
RENDER_NODENAMES = 11,
RENDER_GRID = 12,
RENDER_BACKFACECULLING = 13,
RENDER_ACTORBINDPOSE = 14,
RENDER_RAGDOLL_COLLIDERS = 15,
RENDER_RAGDOLL_JOINTLIMITS = 16,
RENDER_HITDETECTION_COLLIDERS = 17,
RENDER_USE_GRADIENTBACKGROUND = 18,
RENDER_MOTIONEXTRACTION = 19,
RENDER_CLOTH_COLLIDERS = 20,
RENDER_SIMULATEDOBJECT_COLLIDERS = 21,
RENDER_SIMULATEJOINTS = 22,
RENDER_EMFX_DEBUG = 23,
NUM_RENDERFLAGS = 24
};
using ActorRenderFlagBitset = AZStd::bitset<ActorRenderFlag::NUM_RENDERFLAGS>;
}

@ -11,6 +11,7 @@
#include <Integration/Assets/ActorAsset.h>
#include <Integration/Rendering/RenderBackend.h>
#include <Integration/Rendering/RenderActor.h>
#include <Integration/Rendering/RenderFlag.h>
#include <Integration/Rendering/RenderActorInstance.h>
#include <Integration/Rendering/RenderBackendManager.h>
#include <Integration/System/SystemCommon.h>
@ -64,7 +65,7 @@ namespace EMotionFX
}
MOCK_METHOD1(OnTick, void(float));
MOCK_METHOD1(DebugDraw, void(const DebugOptions&));
MOCK_METHOD1(DebugDraw, void(const EMotionFX::ActorRenderFlagBitset&));
MOCK_CONST_METHOD0(IsVisible, bool());
MOCK_METHOD1(SetIsVisible, void(bool));
MOCK_METHOD1(SetMaterials, void(const ActorAsset::MaterialList&));

Loading…
Cancel
Save