diff --git a/Source/Editor/Utilities/ViewportIconsRenderer.cpp b/Source/Editor/Utilities/ViewportIconsRenderer.cpp index c516287bb..dcdc99cd6 100644 --- a/Source/Editor/Utilities/ViewportIconsRenderer.cpp +++ b/Source/Editor/Utilities/ViewportIconsRenderer.cpp @@ -14,14 +14,15 @@ #include "Engine/Level/Actors/Skybox.h" #include "Engine/Level/Actors/Decal.h" #include "Engine/Level/Actors/ExponentialHeightFog.h" -#include "Engine/Audio/AudioListener.h" -#include "Engine/Audio/AudioSource.h" -#include "Engine/Particles/ParticleEffect.h" -#include "Engine/Animations/SceneAnimations/SceneAnimationPlayer.h" -#include "Engine/Engine/EngineService.h" #include "Engine/Level/Actors/Sky.h" #include "Engine/Level/Actors/SkyLight.h" #include "Engine/Level/Actors/SpotLight.h" +#include "Engine/Audio/AudioListener.h" +#include "Engine/Audio/AudioSource.h" +#include "Engine/Particles/ParticleEffect.h" +#include "Engine/Graphics/Models/ModelInstanceEntry.h" +#include "Engine/Animations/SceneAnimations/SceneAnimationPlayer.h" +#include "Engine/Engine/EngineService.h" #include "Engine/Video/VideoPlayer.h" enum class IconTypes diff --git a/Source/Engine/Content/Assets/SkinnedModel.cpp b/Source/Engine/Content/Assets/SkinnedModel.cpp index 965df12f0..5c98853ff 100644 --- a/Source/Engine/Content/Assets/SkinnedModel.cpp +++ b/Source/Engine/Content/Assets/SkinnedModel.cpp @@ -235,6 +235,25 @@ BoundingBox SkinnedModel::GetBox(int32 lodIndex) const return LODs[lodIndex].GetBox(); } +void SkinnedModel::Draw(const RenderContext& renderContext, const SkinnedMeshBones& pose, MaterialBase* material, const Matrix& world, StaticFlags flags, bool receiveDecals, int8 sortOrder) const +{ + if (!CanBeRendered() || !pose) + return; + + // Select a proper LOD index (model may be culled) + const BoundingBox box = GetBox(world); + BoundingSphere sphere; + BoundingSphere::FromBox(box, sphere); + int32 lodIndex = RenderTools::ComputeModelLOD(this, sphere.Center - renderContext.View.Origin, (float)sphere.Radius, renderContext); + if (lodIndex == -1) + return; + lodIndex += renderContext.View.ModelLODBias; + lodIndex = ClampLODIndex(lodIndex); + + // Draw + LODs[lodIndex].Draw(renderContext, pose, material, world, flags, receiveDecals, DrawPass::Default, 0, sortOrder); +} + void SkinnedModel::Draw(const RenderContext& renderContext, const SkinnedMesh::DrawInfo& info) { ModelDraw(this, renderContext, renderContext, info); diff --git a/Source/Engine/Content/Assets/SkinnedModel.h b/Source/Engine/Content/Assets/SkinnedModel.h index 111d4d6cb..1cbd02e8f 100644 --- a/Source/Engine/Content/Assets/SkinnedModel.h +++ b/Source/Engine/Content/Assets/SkinnedModel.h @@ -46,6 +46,24 @@ public: /// True whether the two objects intersected bool Intersects(const Ray& ray, const Transform& transform, Real& distance, Vector3& normal, SkinnedMesh** mesh); + /// + /// Draws the meshes from the model LOD. + /// + /// The rendering context. + /// The Animated Model to use its skeleton bones pose for rendering skinned mesh. + /// The material to use for rendering. + /// The world transformation of the model. + /// The object static flags. + /// True if rendered geometry can receive decals, otherwise false. + /// The draw passes to use for rendering this object. + /// The random per-instance value (normalized to range 0-1). + /// Object sorting key. + API_FUNCTION() void Draw(API_PARAM(Ref) const RenderContext& renderContext, const SkinnedMeshBones& pose, MaterialBase* material, API_PARAM(Ref) const Matrix& world, StaticFlags flags = StaticFlags::None, bool receiveDecals = true, DrawPass drawModes = DrawPass::Default, float perInstanceRandom = 0.0f, int8 sortOrder = 0) const + { + for (int32 i = 0; i < Meshes.Count(); i++) + Meshes.Get()[i].Draw(renderContext, pose, material, world, flags, receiveDecals, drawModes, perInstanceRandom, sortOrder); + } + /// /// Draws the meshes. Binds vertex and index buffers and invokes the draw calls. /// @@ -249,6 +267,18 @@ public: LODs[lodIndex].Render(context); } + /// + /// Draws the model. + /// + /// The rendering context. + /// The skeleton bones pose to use for rendering skinned mesh. + /// The material to use for rendering. + /// The world transformation of the model. + /// The object static flags. + /// True if rendered geometry can receive decals, otherwise false. + /// Object sorting key. + API_FUNCTION() void Draw(API_PARAM(Ref) const RenderContext& renderContext, const SkinnedMeshBones& pose, MaterialBase* material, API_PARAM(Ref) const Matrix& world, StaticFlags flags = StaticFlags::None, bool receiveDecals = true, int8 sortOrder = 0) const; + /// /// Draws the model. /// diff --git a/Source/Engine/Foliage/FoliageType.h b/Source/Engine/Foliage/FoliageType.h index 8b36f618b..2a40c6130 100644 --- a/Source/Engine/Foliage/FoliageType.h +++ b/Source/Engine/Foliage/FoliageType.h @@ -3,9 +3,10 @@ #pragma once #include "Config.h" +#include "Engine/Core/ISerializable.h" #include "Engine/Core/Collections/ChunkedArray.h" #include "Engine/Content/Assets/Model.h" -#include "Engine/Core/ISerializable.h" +#include "Engine/Graphics/Models/ModelInstanceEntry.h" /// /// The foliage instances scaling modes. diff --git a/Source/Engine/Graphics/Models/Mesh.h b/Source/Engine/Graphics/Models/Mesh.h index e7755b61f..7a23e4d3c 100644 --- a/Source/Engine/Graphics/Models/Mesh.h +++ b/Source/Engine/Graphics/Models/Mesh.h @@ -3,12 +3,9 @@ #pragma once #include "MeshBase.h" -#include "ModelInstanceEntry.h" #include "Config.h" #include "Types.h" -class Lightmap; - /// /// Represents part of the model that is made of vertices and can be rendered using custom material and transformation. /// diff --git a/Source/Engine/Graphics/Models/MeshBase.h b/Source/Engine/Graphics/Models/MeshBase.h index 48d076206..dc0430817 100644 --- a/Source/Engine/Graphics/Models/MeshBase.h +++ b/Source/Engine/Graphics/Models/MeshBase.h @@ -19,6 +19,7 @@ struct RenderContext; struct RenderContextBatch; class Task; class ModelBase; +class MaterialBase; class Lightmap; class GPUBuffer; class SkinnedMeshDrawData; diff --git a/Source/Engine/Graphics/Models/SkinnedMesh.cpp b/Source/Engine/Graphics/Models/SkinnedMesh.cpp index 44ed82eee..6dd6e37b1 100644 --- a/Source/Engine/Graphics/Models/SkinnedMesh.cpp +++ b/Source/Engine/Graphics/Models/SkinnedMesh.cpp @@ -309,6 +309,37 @@ bool SkinnedMesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, const Flo return ::UpdateMesh(this, vertexCount, triangleCount, PixelFormat::R16_UInt, vertices, triangles, blendIndices, blendWeights, normals, tangents, uvs, colors); } +void SkinnedMesh::Draw(const RenderContext& renderContext, const SkinnedMeshBones& pose, MaterialBase* material, const Matrix& world, StaticFlags flags, bool receiveDecals, DrawPass drawModes, float perInstanceRandom, int8 sortOrder, uint8 stencilValue) const +{ + if (!material || !material->IsSurface() || !IsInitialized() || !pose) + return; + drawModes &= material->GetDrawModes(); + if (drawModes == DrawPass::None) + return; + + // Setup draw call + DrawCall drawCall; + drawCall.Geometry.IndexBuffer = _indexBuffer; + drawCall.Geometry.VertexBuffers[0] = _vertexBuffers[0]; + drawCall.Draw.IndicesCount = _triangles * 3; + drawCall.InstanceCount = 1; + drawCall.Material = material; + drawCall.World = world; + drawCall.ObjectPosition = drawCall.World.GetTranslation(); + drawCall.ObjectRadius = (float)_sphere.Radius * drawCall.World.GetScaleVector().GetAbsolute().MaxValue(); + drawCall.Surface.GeometrySize = _box.GetSize(); + drawCall.Surface.PrevWorld = world; + drawCall.Surface.Skinning = pose.PrevBonesOffset != 0 ? DrawCall::SkinningMode::WithPrevBones : DrawCall::SkinningMode::Active; + drawCall.Surface.SkinningBones = pose.BoneMatrices; + drawCall.Surface.SkinningBonesOffset = pose.BoneOffset; + drawCall.Surface.PrevBonesOffset = pose.PrevBonesOffset; + drawCall.PerInstanceRandom = perInstanceRandom; + drawCall.StencilValue = stencilValue; + + // Push draw call to the render list + renderContext.List->AddDrawCall(renderContext, drawModes, flags, drawCall, receiveDecals, sortOrder); +} + void SkinnedMesh::Draw(const RenderContext& renderContext, const DrawInfo& info, float lodDitherFactor) const { const auto& entry = info.Buffer->At(_materialSlotIndex); @@ -340,7 +371,6 @@ void SkinnedMesh::Draw(const RenderContext& renderContext, const DrawInfo& info, drawCall.Geometry.VertexBuffers[0] = _vertexBuffers[0]; if (info.Deformation) info.Deformation->RunDeformers(this, MeshBufferType::Vertex0, drawCall.Geometry.VertexBuffers[0]); - drawCall.Draw.StartIndex = 0; drawCall.Draw.IndicesCount = _triangles * 3; drawCall.InstanceCount = 1; drawCall.Material = material; diff --git a/Source/Engine/Graphics/Models/SkinnedMesh.h b/Source/Engine/Graphics/Models/SkinnedMesh.h index ebfd5f278..0c0084eaf 100644 --- a/Source/Engine/Graphics/Models/SkinnedMesh.h +++ b/Source/Engine/Graphics/Models/SkinnedMesh.h @@ -5,6 +5,26 @@ #include "MeshBase.h" #include "BlendShape.h" +/// +/// Contains skeletal bones pose data for skinned mesh rendering. +/// +API_STRUCT(NoDefault) struct FLAXENGINE_API SkinnedMeshBones +{ + DECLARE_SCRIPTING_TYPE_MINIMAL(SkinnedMeshBones); + + // Buffer with skinned mesh bones data (array of Matrix3x4, one per bone). Used for GPU skinning in the vertex shader. + API_FIELD() GPUBuffer* BoneMatrices = nullptr; + // Offset in the bones buffer where data starts (in Matrix3x4s). + API_FIELD() uint32 BoneOffset = 0; + // Offset in the bones buffer where previous-frame bones data starts (in Matrix3x4s). Can be negative, value 0 means it's unsued (single-frame data provided). + API_FIELD() int16 PrevBonesOffset = 0; + + FORCE_INLINE operator bool() const + { + return BoneMatrices != nullptr; + } +}; + /// /// Represents part of the skinned model that is made of vertices and can be rendered using custom material, transformation and skeleton bones hierarchy. /// @@ -124,6 +144,21 @@ public: bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, const Float3* vertices, const uint32* triangles, const Int4* blendIndices, const Float4* blendWeights, const Float3* normals = nullptr, const Float3* tangents = nullptr, const Float2* uvs = nullptr, const Color32* colors = nullptr); public: + /// + /// Draws the mesh. + /// + /// The rendering context. + /// The skeleton bones pose to use for rendering skinned mesh. + /// The material to use for rendering. + /// The world transformation of the model. + /// The object static flags. + /// True if rendered geometry can receive decals, otherwise false. + /// The draw passes to use for rendering this object. + /// The random per-instance value (normalized to range 0-1). + /// Object sorting key. + /// Object stencil value. + API_FUNCTION() void Draw(API_PARAM(Ref) const RenderContext& renderContext, const SkinnedMeshBones& pose, MaterialBase* material, API_PARAM(Ref) const Matrix& world, StaticFlags flags = StaticFlags::None, bool receiveDecals = true, DrawPass drawModes = DrawPass::Default, float perInstanceRandom = 0.0f, int8 sortOrder = 0, uint8 stencilValue = 0) const; + /// /// Draws the mesh. /// diff --git a/Source/Engine/Level/Actors/AnimatedModel.cpp b/Source/Engine/Level/Actors/AnimatedModel.cpp index 1e8d56c8f..cf9e5be3f 100644 --- a/Source/Engine/Level/Actors/AnimatedModel.cpp +++ b/Source/Engine/Level/Actors/AnimatedModel.cpp @@ -357,6 +357,31 @@ void AnimatedModel::PreInitSkinningData() UpdateSockets(); } +SkinnedMeshBones AnimatedModel::GetSkinnedMeshBones() const +{ + SkinnedMeshBones result; + if (_bones.IsAllocated) + { + // Flush skinning data with GPU + // TODO: what if it's called outside the rendering?outside PreDraw/PostDraw on AnimatedModelRenderListExtension? + if (_bones.IsDirty && GPUDevice::Instance->IsRendering()) + const_cast(_bones).Flush(); + + result.BoneMatrices = RenderListExtension.GlobalBuffer; + result.BoneOffset = _bones.GlobalBufferOffset / sizeof(Matrix3x4); + if (_bones.HasPrevBones && _bones.IsPrevFlushed) + { + result.PrevBonesOffset = _bones.BonesCount; + if (_bones.IsPrevBones) + { + result.BoneOffset += result.PrevBonesOffset; + result.PrevBonesOffset = -result.PrevBonesOffset; + } + } + } + return result; +} + void AnimatedModel::GetCurrentPose(Array& nodesTransformation, bool worldSpace) const { if (GraphInstance.NodesPose.IsEmpty()) diff --git a/Source/Engine/Level/Actors/AnimatedModel.h b/Source/Engine/Level/Actors/AnimatedModel.h index a13634568..f7d196f30 100644 --- a/Source/Engine/Level/Actors/AnimatedModel.h +++ b/Source/Engine/Level/Actors/AnimatedModel.h @@ -241,6 +241,11 @@ public: /// API_FUNCTION() void PreInitSkinningData(); + /// + /// Gets the skeleton bones data used for skinning. It contains the reference to GPU buffer with final transformations of the skeleton nodes (bones) that are used for skinning the mesh inside vertex shader. + /// + API_PROPERTY() SkinnedMeshBones GetSkinnedMeshBones() const; + /// /// Gets the per-node final transformations (skeleton pose). ///