#pragma once #include #include #include #include #include "Runtime/CToken.hpp" #include "Runtime/Character/CSkinRules.hpp" #include "Runtime/Graphics/CModel.hpp" #include namespace metaforce { class CCharLayoutInfo; class CModel; class CPoseAsTransforms; class CVertexMorphEffect; class IObjectStore; // Originally vert + normal workspaces were allocated together, but here separated for ease of use struct SSkinningWorkspace { std::vector m_vertexWorkspace; std::vector m_normalWorkspace; SSkinningWorkspace(const CSkinRules& rules) { Reset(rules); } void Reset(const CSkinRules& rules) { m_vertexWorkspace.clear(); m_normalWorkspace.clear(); m_vertexWorkspace.reserve(rules.GetVertexCount()); m_normalWorkspace.reserve(rules.GetNormalCount()); } void Clear() { m_vertexWorkspace.clear(); m_normalWorkspace.clear(); } [[nodiscard]] bool IsEmpty() const { return m_vertexWorkspace.empty() || m_normalWorkspace.empty(); } }; // Lambda instead of userdata pointer using FCustomDraw = std::function; using FPointGenerator = std::function; class CSkinnedModel { TLockedToken x4_model; TLockedToken x10_skinRules; TLockedToken x1c_layoutInfo; // rstl::auto_ptr x24_vertWorkspace; // rstl::auto_ptr x2c_normalWorkspace; SSkinningWorkspace m_workspace; bool x34_owned = true; bool x35_disableWorkspaces = false; public: enum class EDataOwnership { Unowned, Owned }; CSkinnedModel(const TLockedToken& model, const TLockedToken& skinRules, const TLockedToken& layoutInfo /*, EDataOwnership ownership*/); CSkinnedModel(IObjectStore& store, CAssetId model, CAssetId skinRules, CAssetId layoutInfo); virtual ~CSkinnedModel() = default; TLockedToken& GetModel() { return x4_model; } const TLockedToken& GetModel() const { return x4_model; } const TLockedToken& GetSkinRules() const { return x10_skinRules; } void SetLayoutInfo(const TLockedToken& inf) { x1c_layoutInfo = inf; } const TLockedToken& GetLayoutInfo() const { return x1c_layoutInfo; } void AllocateStorage(); // Metaforce addition: Originally morphEffect is rstl::optional_object* // This prevents constructing it as a reference to the held pointer in CPatterned, thus in // retail it's copied in every invocation of RenderIceModelWithFlags. void Calculate(const CPoseAsTransforms& pose, CVertexMorphEffect* morphEffect, TConstVectorRef averagedNormals, SSkinningWorkspace* workspace); void Draw(TConstVectorRef verts, TConstVectorRef normals, const CModelFlags& drawFlags); void Draw(const CModelFlags& drawFlags); void DoDrawCallback(const FCustomDraw& func) const; void CalculateDefault(); // Originally returns cloned vertex workspace, with arg for cloned normal workspace SSkinningWorkspace CloneWorkspace(); static void SetPointGeneratorFunc(FPointGenerator func) { g_PointGenFunc = std::move(func); } static void ClearPointGeneratorFunc() { g_PointGenFunc = nullptr; } static FPointGenerator g_PointGenFunc; }; class CSkinnedModelWithAvgNormals : public CSkinnedModel { std::vector x40_averagedNormals; // was rstl::auto_ptr public: CSkinnedModelWithAvgNormals(IObjectStore& store, CAssetId model, CAssetId skinRules, CAssetId layoutInfo); ~CSkinnedModelWithAvgNormals() override = default; TConstVectorRef GetAveragedNormals() const { return &x40_averagedNormals; } }; } // namespace metaforce