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>
This commit is contained in:
@@ -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 camera button
|
||||
QToolButton* cameraButton = new QToolButton(this);
|
||||
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" },
|
||||
};
|
||||
|
||||
for (const auto& pair : cameraOptionNames)
|
||||
// Add the render view options button
|
||||
QToolButton* renderOptionsButton = new QToolButton(this);
|
||||
{
|
||||
CameraViewMode mode = pair.first;
|
||||
cameraMenu->addAction(
|
||||
pair.second.c_str(),
|
||||
[mode]()
|
||||
{
|
||||
// Send the reset camera event.
|
||||
AnimViewportRequestBus::Broadcast(&AnimViewportRequestBus::Events::SetCameraViewMode, mode);
|
||||
});
|
||||
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();
|
||||
}
|
||||
|
||||
cameraMenu->addSeparator();
|
||||
cameraMenu->addAction("Reset Camera",
|
||||
[]()
|
||||
// Add the camera button
|
||||
QToolButton* cameraButton = new QToolButton(this);
|
||||
{
|
||||
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" },
|
||||
};
|
||||
|
||||
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(
|
||||
"Reset Camera",
|
||||
[]()
|
||||
{
|
||||
// Send the reset camera event.
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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 tool bar
|
||||
AnimViewportToolBar* toolBar = new AnimViewportToolBar(m_innerWidget);
|
||||
verticalLayout->addWidget(toolBar);
|
||||
|
||||
// Add the viewport widget
|
||||
m_animViewportWidget = new AnimViewportWidget(m_innerWidget);
|
||||
|
||||
// Add the tool bar
|
||||
AnimViewportToolBar* toolBar = new AnimViewportToolBar(m_innerWidget);
|
||||
toolBar->SetRenderFlags(m_animViewportWidget->GetRenderFlags());
|
||||
|
||||
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&));
|
||||
|
||||
Reference in New Issue
Block a user