diff --git a/cont/base/springcontent/shaders/GLSL/ModelVertProgGL4.glsl b/cont/base/springcontent/shaders/GLSL/ModelVertProgGL4.glsl index 898165ba29..75b5e65184 100644 --- a/cont/base/springcontent/shaders/GLSL/ModelVertProgGL4.glsl +++ b/cont/base/springcontent/shaders/GLSL/ModelVertProgGL4.glsl @@ -83,6 +83,17 @@ layout(std140, binding = 1) uniform UniformParamsBuffer { vec4 teamColor[255]; //all team colors }; +/* +struct Transform { + vec4 quat; + vec4 trSc; +}; + +layout(std140, binding = 0) readonly buffer TransformBuffer { + Transform transforms[]; +}; +*/ + layout(std140, binding = 0) readonly buffer MatrixBuffer { mat4 mat[]; }; @@ -132,6 +143,37 @@ uint GetUnpackedValue(uint packedValue, uint byteNum) { return (packedValue >> (8u * byteNum)) & 0xFFu; } +vec4 MultiplyQuat(vec4 a, vec4 b) +{ + return vec4(a.w * b.w - dot(a.w, b.w), a.w * b.xyz + b.w * a.xyz + cross(a.xyz, b.xyz)); +} + +vec3 RotateByQuaternion(vec4 q, vec3 v) { + return 2.0f * dot(q.xyz, v) * q.xyz + (q.w * q.w - dot(q.xyz, q.xyz)) * v + 2.0 * q.w * cross(q.xyz, v); +} + +vec4 RotateByQuaternion(vec4 q, vec4 v) { + return vec4(RotateByQuaternion(q, v.xyz), v.w); +} + +vec3 ApplyTransform(Transform tra, vec3 v) { + return RotateByQuaternion(tra.quat, v * tra.trSc.w) + tra.trSc.xyz; +} + +vec4 ApplyTransform(Transform tra, vec4 v) { + return vec4(ApplyTransform(tra, v.xyz), v.w); +} + +Transform ApplyTransform(Transform parentTra, Transform childTra) { + return Transform( + MultiplyQuat(parentTra.quat, childTra.quat), + vec4( + parentTra.trSc.xyz + RotateByQuaternion(parentTra.quat, parentTra.trSc.w * childTra.trSc.xyz), + parentTra.trSc.w * childTra.trSc.w + ) + ); +} + void GetModelSpaceVertex(out vec4 msPosition, out vec3 msNormal) { bool staticModel = (matrixMode > 0); @@ -237,4 +279,4 @@ void main(void) TransformPlayerCam(worldPos); break; }; -} \ No newline at end of file +} diff --git a/rts/Game/Game.cpp b/rts/Game/Game.cpp index 15e7ed2656..1809b61b93 100644 --- a/rts/Game/Game.cpp +++ b/rts/Game/Game.cpp @@ -739,8 +739,11 @@ void CGame::PreLoadRendering() geometricObjects = new CGeometricObjects(); // load components that need to exist before PostLoadSimulation - matrixUploader.Init(); - modelsUniformsUploader.Init(); + modelUniformsStorage.Init(); + //transformsMemStorage.Init(); // Add? + + transformsUploader.Init(); + modelUniformsUploader.Init(); worldDrawer.InitPre(); } @@ -1000,8 +1003,9 @@ void CGame::KillRendering() icon::iconHandler.Kill(); spring::SafeDelete(geometricObjects); worldDrawer.Kill(); - matrixUploader.Kill(); - modelsUniformsUploader.Kill(); + + transformsUploader.Kill(); + modelUniformsUploader.Kill(); } void CGame::KillInterface() @@ -1044,6 +1048,9 @@ void CGame::KillSimulation() unitHandler.Kill(); projectileHandler.Kill(); + modelUniformsStorage.Kill(); + //transformsMemStorage.Kill(); //Add? + LOG("[Game::%s][3]", __func__); IPathManager::FreeInstance(pathManager); IMapDamage::FreeMapDamage(mapDamage); @@ -1473,8 +1480,8 @@ bool CGame::UpdateUnsynced(const spring_time currentTime) shadowHandler.Update(); { worldDrawer.Update(newSimFrame); - matrixUploader.Update(); - modelsUniformsUploader.Update(); + transformsUploader.Update(); + modelUniformsUploader.Update(); } mouse->UpdateCursorCameraDir(); // make sure mouse->dir is in sync with camera diff --git a/rts/Lua/LuaShaders.cpp b/rts/Lua/LuaShaders.cpp index 1911cb04ec..f59605fca0 100644 --- a/rts/Lua/LuaShaders.cpp +++ b/rts/Lua/LuaShaders.cpp @@ -1014,7 +1014,7 @@ namespace { if (o == nullptr) luaL_error(L, "gl.%s() Invalid %s id (%d)", func, &spring::TypeToCStr()[1], id); - ModelUniformData& uni = modelsUniformsStorage.GetObjUniformsArray(o); + ModelUniformData& uni = modelUniformsStorage.GetObjUniformsArray(o); std::array floatArray = {0}; int size = LuaUtils::ParseFloatArray(L, 2, floatArray.data(), ModelUniformData::MAX_MODEL_UD_UNIFORMS); diff --git a/rts/Lua/LuaVBOImpl.cpp b/rts/Lua/LuaVBOImpl.cpp index ef634c07e2..8a7f5dcda7 100644 --- a/rts/Lua/LuaVBOImpl.cpp +++ b/rts/Lua/LuaVBOImpl.cpp @@ -1023,10 +1023,10 @@ SInstanceData LuaVBOImpl::InstanceDataFromGetData(int id, int attrID, uint8_t de uint32_t teamID = defTeamID; const TObj* obj = LuaUtils::SolIdToObject(id, __func__); - const uint32_t matOffset = static_cast(matrixUploader.GetElemOffset(obj)); - const uint32_t uniIndex = static_cast(modelsUniformsStorage.GetObjOffset(obj)); //doesn't need to exist for defs and model. Don't check for validity + const uint32_t traOffset = static_cast(transformsUploader.GetElemOffset(obj)); + const uint32_t uniIndex = static_cast(modelUniformsStorage.GetObjOffset(obj)); //doesn't need to exist for defs and model. Don't check for validity - if (matOffset == ~0u) { + if (traOffset == ~0u) { LuaUtils::SolLuaError("[LuaVBOImpl::%s] Invalid data supplied. See infolog for details", __func__); } @@ -1040,14 +1040,14 @@ SInstanceData LuaVBOImpl::InstanceDataFromGetData(int id, int attrID, uint8_t de size_t bposeIndex = 0; if constexpr (std::is_same::value) { numPieces = static_cast(obj->numPieces); - bposeIndex = matrixUploader.GetElemOffset(obj); + bposeIndex = transformsUploader.GetElemOffset(obj); } else { numPieces = static_cast(obj->model->numPieces); - bposeIndex = matrixUploader.GetElemOffset(obj->model); + bposeIndex = transformsUploader.GetElemOffset(obj->model); } - return SInstanceData(matOffset, teamID, drawFlags, numPieces, uniIndex, bposeIndex); + return SInstanceData(traOffset, teamID, drawFlags, numPieces, uniIndex, bposeIndex); } template @@ -1215,7 +1215,7 @@ size_t LuaVBOImpl::ModelsVBO() * Data Layout: * ``` * SInstanceData: - * , matOffset{ matOffset_ } // updated during the following draw frames + * , traOffset{ matOffset_ } // updated during the following draw frames * , uniOffset{ uniOffset_ } // updated during the following draw frames * , info{ teamIndex, drawFlags, 0, 0 } // not updated during the following draw frames * , aux1 { 0u } @@ -1254,7 +1254,7 @@ size_t LuaVBOImpl::InstanceDataFromUnitDefIDs(const sol::stack_table& ids, int a * Data Layout * ``` * SInstanceData: - * , matOffset{ matOffset_ } // updated during the following draw frames + * , traOffset{ matOffset_ } // updated during the following draw frames * , uniOffset{ uniOffset_ } // updated during the following draw frames * , info{ teamIndex, drawFlags, 0, 0 } // not updated during the following draw frames * , aux1 { 0u } @@ -1294,7 +1294,7 @@ size_t LuaVBOImpl::InstanceDataFromFeatureDefIDs(const sol::stack_table& ids, in * * ``` * SInstanceData: - * , matOffset{ matOffset_ } // updated during the following draw frames + * , traOffset{ matOffset_ } // updated during the following draw frames * , uniOffset{ uniOffset_ } // updated during the following draw frames * , info{ teamIndex, drawFlags, 0, 0 } // not updated during the following draw frames * , aux1 { 0u } diff --git a/rts/Rendering/Common/ModelDrawer.h b/rts/Rendering/Common/ModelDrawer.h index ae18ea9e05..dfa2936247 100644 --- a/rts/Rendering/Common/ModelDrawer.h +++ b/rts/Rendering/Common/ModelDrawer.h @@ -21,7 +21,7 @@ namespace GL { struct GeometryBuffer; } template class ScopedModelDrawerImpl; -class ScopedMatricesMemAlloc; +class ScopedTransformMemAlloc; static constexpr const char* ModelDrawerNames[ModelDrawerTypes::MODEL_DRAWER_CNT] = { @@ -110,7 +110,7 @@ class CModelDrawerBase : public CModelDrawerConcept { static bool CanDrawDeferred() { return modelDrawerState->CanDrawDeferred(); } static bool SetTeamColor(int team, const float alpha = 1.0f) { return modelDrawerState->SetTeamColor(team, alpha); } static void SetNanoColor(const float4& color) { modelDrawerState->SetNanoColor(color); } - static const ScopedMatricesMemAlloc& GetMatricesMemAlloc(const ObjType* o) { return const_cast(modelDrawerData)->GetObjectMatricesMemAlloc(o); } + static const ScopedTransformMemAlloc& GetTransformMemAlloc(const ObjType* o) { return const_cast(modelDrawerData)->GetObjectTransformMemAlloc(o); } public: virtual void Update() const = 0; // Draw* diff --git a/rts/Rendering/Common/ModelDrawerData.h b/rts/Rendering/Common/ModelDrawerData.h index f08801e0e6..1b34f9a9bb 100644 --- a/rts/Rendering/Common/ModelDrawerData.h +++ b/rts/Rendering/Common/ModelDrawerData.h @@ -61,7 +61,7 @@ class CModelDrawerDataBase : public CModelDrawerDataConcept void UpdateCommon(T* o); virtual void UpdateObjectDrawFlags(CSolidObject* o) const = 0; private: - void UpdateObjectSMMA(const T* o); + void UpdateObjectTrasform(const T* o); void UpdateObjectUniforms(const T* o); public: const std::vector& GetUnsortedObjects() const { return unsortedObjects; } @@ -69,18 +69,18 @@ class CModelDrawerDataBase : public CModelDrawerDataConcept void ClearPreviousDrawFlags() { for (auto object : unsortedObjects) object->previousDrawFlag = 0; } - const ScopedMatricesMemAlloc& GetObjectMatricesMemAlloc(const T* o) const { - const auto it = matricesMemAllocs.find(const_cast(o)); - return (it != matricesMemAllocs.end()) ? it->second : ScopedMatricesMemAlloc::Dummy(); + const auto& GetObjectTransformMemAlloc(const T* o) const { + const auto it = scTransMemAllocMap.find(const_cast(o)); + return (it != scTransMemAllocMap.end()) ? it->second : ScopedTransformMemAlloc::Dummy(); } - ScopedMatricesMemAlloc& GetObjectMatricesMemAlloc(const T* o) { return matricesMemAllocs[const_cast(o)]; } + auto& GetObjectTransformMemAlloc(const T* o) { return scTransMemAllocMap[const_cast(o)]; } private: static constexpr int MMA_SIZE0 = 2 << 16; protected: std::array, MODELTYPE_CNT> modelRenderers; std::vector unsortedObjects; - std::unordered_map matricesMemAllocs; + std::unordered_map scTransMemAllocMap; bool& mtModelDrawer; }; @@ -100,7 +100,7 @@ inline CModelDrawerDataBase::CModelDrawerDataBase(const std::string& ecName, : CModelDrawerDataConcept(ecName, ecOrder) , mtModelDrawer(mtModelDrawer_) { - matricesMemAllocs.reserve(MMA_SIZE0); + scTransMemAllocMap.reserve(MMA_SIZE0); for (auto& mr : modelRenderers) { mr.Clear(); } } @@ -108,7 +108,7 @@ template inline CModelDrawerDataBase::~CModelDrawerDataBase() { unsortedObjects.clear(); - matricesMemAllocs.clear(); + scTransMemAllocMap.clear(); } template @@ -126,9 +126,9 @@ inline void CModelDrawerDataBase::AddObject(const T* co, bool add) unsortedObjects.emplace_back(o); const uint32_t numMatrices = (o->model ? o->model->numPieces : 0) + 1u; - matricesMemAllocs.emplace(o, ScopedMatricesMemAlloc(numMatrices)); + scTransMemAllocMap.emplace(o, ScopedTransformMemAlloc(numMatrices)); - modelsUniformsStorage.GetObjOffset(co); + modelUniformsStorage.AddObject(co); } template @@ -141,8 +141,8 @@ inline void CModelDrawerDataBase::DelObject(const T* co, bool del) } if (del && spring::VectorErase(unsortedObjects, o)) { - matricesMemAllocs.erase(o); - modelsUniformsStorage.GetObjOffset(co); + scTransMemAllocMap.erase(o); + modelUniformsStorage.DelObject(co); } } @@ -155,17 +155,15 @@ inline void CModelDrawerDataBase::UpdateObject(const T* co, bool init) template -inline void CModelDrawerDataBase::UpdateObjectSMMA(const T* o) +inline void CModelDrawerDataBase::UpdateObjectTrasform(const T* o) { - ScopedMatricesMemAlloc& smma = GetObjectMatricesMemAlloc(o); + ScopedTransformMemAlloc& smma = GetObjectTransformMemAlloc(o); - const auto tmNew = o->GetTransformMatrix(); - const auto& tmOld = const_cast(smma)[0]; + const auto tmNew = Transform::FromMatrix(o->GetTransformMatrix()); // from one point it doesn't worth the comparison, cause units usually move - // but having not updated smma[0] allows for longer solid no-update areas in ModelsUniformsUploader::UpdateDerived() - if (tmNew != tmOld) - smma[0] = tmNew; + // but having not updated smma[0] allows for longer solid no-update areas in ModelUniformsUploader::UpdateDerived() + smma.UpdateIfChanged(0, tmNew); for (int i = 0; i < o->localModel.pieces.size(); ++i) { const LocalModelPiece& lmp = o->localModel.pieces[i]; @@ -175,22 +173,25 @@ inline void CModelDrawerDataBase::UpdateObjectSMMA(const T* o) continue; if unlikely(!lmp.GetScriptVisible()) { - smma[i + 1] = CMatrix44f::Zero(); + //smma[i + 1] = CMatrix44f::Zero(); + smma.UpdateForced(i + 1, Transform::Zero()); continue; } - smma[i + 1] = lmp.GetModelSpaceMatrix(); + // UpdateIfChanged is not needed, wasCustomDirty takes that role + smma.UpdateForced(i + 1, lmp.GetModelSpaceTransform()); } } template inline void CModelDrawerDataBase::UpdateObjectUniforms(const T* o) { - auto& uni = modelsUniformsStorage.GetObjUniformsArray(o); + auto& uni = modelUniformsStorage.GetObjUniformsArray(o); uni.drawFlag = o->drawFlag; if (gu->spectatingFullView || o->IsInLosForAllyTeam(gu->myAllyTeam)) { uni.id = o->id; + // TODO remove drawPos, replace with pos uni.drawPos = float4{ o->drawPos, o->heading * math::PI / SPRING_MAX_HEADING }; uni.speed = o->speed; uni.maxHealth = o->maxHealth; @@ -206,7 +207,7 @@ inline void CModelDrawerDataBase::UpdateCommon(T* o) UpdateObjectDrawFlags(o); if (o->alwaysUpdateMat || (o->drawFlag > DrawFlags::SO_NODRAW_FLAG && o->drawFlag < DrawFlags::SO_DRICON_FLAG)) - UpdateObjectSMMA(o); + UpdateObjectTrasform(o); UpdateObjectUniforms(o); } \ No newline at end of file diff --git a/rts/Rendering/Models/3DModel.cpp b/rts/Rendering/Models/3DModel.cpp index 8933c729c3..a892c7859a 100644 --- a/rts/Rendering/Models/3DModel.cpp +++ b/rts/Rendering/Models/3DModel.cpp @@ -21,7 +21,9 @@ CR_BIND(LocalModelPiece, (nullptr)) CR_REG_METADATA(LocalModelPiece, ( CR_MEMBER(pos), + CR_MEMBER(posSpeed), CR_MEMBER(rot), + CR_MEMBER(rotSpeed), CR_MEMBER(dir), CR_MEMBER(colvol), CR_MEMBER(scriptSetVisible), @@ -37,8 +39,8 @@ CR_REG_METADATA(LocalModelPiece, ( CR_IGNORED(dirty), CR_IGNORED(customDirty), - CR_IGNORED(modelSpaceMat), - CR_IGNORED(pieceSpaceMat), + CR_IGNORED(modelSpaceTra), + CR_IGNORED(pieceSpaceTra), CR_IGNORED(lodDispLists) //FIXME GL idx! )) @@ -79,7 +81,7 @@ void S3DModelPiece::DrawStaticLegacy(bool bind, bool bindPosMat) const if (bindPosMat) { glPushMatrix(); - glMultMatrixf(bposeMatrix); + glMultMatrixf(bposeTransform.ToMatrix()); DrawElements(); glPopMatrix(); } @@ -243,6 +245,78 @@ void S3DModelPiece::Shatter(float pieceChance, int modelType, int texType, int t projectileHandler.AddFlyingPiece(modelType, this, m, pos, speed, pieceParams, renderParams); } +void S3DModelPiece::SetPieceTransform(const Transform& parentTra) +{ + bposeTransform = parentTra * Transform{ + CQuaternion(), + offset, + scale + }; + + bposeInvTransform = bposeTransform.InvertAffine(); +#ifdef _DEBUG + auto bposeMat = bposeTransform.ToMatrix(); + auto bposeInvMat = bposeMat.Invert(); + auto bposeInvTransform2 = Transform::FromMatrix(bposeInvMat); + assert(bposeInvTransform.equals(bposeInvTransform2)); +#endif // _DEBUG + + for (S3DModelPiece* c : children) { + c->SetPieceTransform(bposeTransform); + } +} + +Transform S3DModelPiece::ComposeTransform(const float3& t, const float3& r, float s) const +{ + // TODO: Remove ToMatrix() / FromMatrix() non-sense + + // NOTE: + // ORDER MATTERS (T(baked + script) * R(baked) * R(script) * S(baked)) + // translating + rotating + scaling is faster than matrix-multiplying + // m is identity so m.SetPos(t)==m.Translate(t) but with fewer instrs + Transform tra; + tra.t = t; + + if (hasBakedTra) + tra *= bakedTransform; + + tra *= Transform(CQuaternion::FromEulerYPRNeg(-r), ZeroVector, s); +#ifdef _DEBUG + /* + { + auto rngAngles = guRNG.NextVector(2.0f * math::PI); + auto delMe = CQuaternion::FromEulerPYRNeg(rngAngles); + CMatrix44f mdel; mdel.RotateEulerXYZ(rngAngles); + CQuaternion delMe2; + std::tie(std::ignore, delMe2, std::ignore) = CQuaternion::DecomposeIntoTRS(mdel); + assert(delMe.equals(delMe2)); + } + { + auto rngAngles = guRNG.NextVector(2.0f * math::PI); + auto delMe = CQuaternion::FromEulerYPRNeg(rngAngles); + CMatrix44f mdel; mdel.RotateEulerYXZ(rngAngles); + CQuaternion delMe2; + std::tie(std::ignore, delMe2, std::ignore) = CQuaternion::DecomposeIntoTRS(mdel); + assert(delMe.equals(delMe2)); + } + */ + CMatrix44f m; + m.SetPos(t); + + if (hasBakedTra) + m *= bakedTransform.ToMatrix(); + + // default Spring rotation-order [YPR=Y,X,Z] + m.RotateEulerYXZ(-r); + m.Scale(s); + + auto tra2 = Transform::FromMatrix(m); + + assert(tra.equals(tra2)); +#endif + return tra; +} + void S3DModelPiece::PostProcessGeometry(uint32_t pieceIndex) { @@ -334,7 +408,7 @@ void LocalModel::SetModel(const S3DModel* model, bool initialize) pieces[n].original = omp; } - pieces[0].UpdateChildMatricesRec(true); + pieces[0].UpdateChildTransformRec(true); UpdateBoundingVolume(); return; } @@ -349,7 +423,7 @@ void LocalModel::SetModel(const S3DModel* model, bool initialize) // must recursively update matrices here too: for features // LocalModel::Update is never called, but they might have // baked piece rotations (in the case of .dae) - pieces[0].UpdateChildMatricesRec(false); + pieces[0].UpdateChildTransformRec(false); UpdateBoundingVolume(); assert(pieces.size() == model->numPieces); @@ -391,7 +465,7 @@ void LocalModel::UpdateBoundingVolume() float3 bbMaxs = DEF_MAX_SIZE; for (const auto& lmPiece: pieces) { - const CMatrix44f& matrix = lmPiece.GetModelSpaceMatrix(); + const auto& tra = lmPiece.GetModelSpaceTransform(); const S3DModelPiece* piece = lmPiece.original; // skip empty pieces or bounds will not be sensible @@ -415,7 +489,7 @@ void LocalModel::UpdateBoundingVolume() }; for (const float3& v: verts) { - const float3 vertex = matrix * v; + const float3 vertex = tra * v; bbMins = float3::min(bbMins, vertex); bbMaxs = float3::max(bbMaxs, vertex); @@ -451,7 +525,7 @@ LocalModelPiece::LocalModelPiece(const S3DModelPiece* piece) pos = piece->offset; dir = piece->GetEmitDir(); // warning investigated, seems fake - pieceSpaceMat = CalcPieceSpaceMatrix(pos, rot, original->scales); + pieceSpaceTra = CalcPieceSpaceTransform(pos, rot, original->scale); children.reserve(piece->children.size()); } @@ -475,7 +549,7 @@ bool LocalModelPiece::SetGetCustomDirty(bool cd) const return cd; } -void LocalModelPiece::SetPosOrRot(const float3& src, float3& dst) { +void LocalModelPiece::SetPosOrRot(const float3& src, float3& dst, const float3& srcSpeed, float3& dstSpeed) { RECOIL_DETAILED_TRACY_ZONE; if (blockScriptAnims) return; @@ -486,29 +560,71 @@ void LocalModelPiece::SetPosOrRot(const float3& src, float3& dst) { } dst = src; + dstSpeed = srcSpeed; } -void LocalModelPiece::UpdateChildMatricesRec(bool updateChildMatrices) const +void LocalModelPiece::UpdateChildTransformRec(bool updateChildTransform) const { RECOIL_DETAILED_TRACY_ZONE; + + /* + { + const auto pt = guRNG.NextVector() * 1000.0f; + const auto pr = guRNG.NextVector() * 2.0f * math::PI; + const auto ps = (guRNG.NextFloat24() + 0.5f); + + const auto pTra = Transform(CQuaternion::FromEulerYPRNeg(pr), pt, float3{ ps }); + + const auto ct = guRNG.NextVector() * 1000.0f; + const auto cr = guRNG.NextVector() * 2.0f * math::PI; + const auto cs = (guRNG.NextFloat24() + 0.5f); + + const auto cTra = Transform(CQuaternion::FromEulerYPRNeg(cr), ct, float3{ cs }); + + const auto cTraUpd = pTra * cTra; + + const auto pMat = pTra.ToMatrix(); + const auto cMat = cTra.ToMatrix(); + const auto cMatUpd = pMat * cMat; + const auto [_t, _r, _s] = CQuaternion::DecomposeIntoTRS(cMatUpd); + + const auto cMatUpd2 = cTraUpd.ToMatrix(); + + auto cTraUpd2 = Transform::FromMatrix(cMatUpd); + + //assert(cTraUpd.equals(cTraUpd2)); + + assert(cMatUpd == cMatUpd2); + } + */ + if (dirty) { dirty = false; - updateChildMatrices = true; + updateChildTransform = true; - pieceSpaceMat = CalcPieceSpaceMatrix(pos, rot, original->scales); + pieceSpaceTra = CalcPieceSpaceTransform(pos, rot, original->scale); } - if (updateChildMatrices) { - modelSpaceMat = pieceSpaceMat; + if (updateChildTransform) { + modelSpaceTra = pieceSpaceTra; if (parent != nullptr) { - modelSpaceMat >>= parent->modelSpaceMat; + // TODO remove the non-sense + const auto modelSpaceTraOrig = modelSpaceTra; + modelSpaceTra = parent->modelSpaceTra * modelSpaceTra; + + CMatrix44f thisModelSpaceMat = modelSpaceTraOrig.ToMatrix(); + const CMatrix44f prntModelSpaceMat = parent->modelSpaceTra.ToMatrix(); + thisModelSpaceMat >>= prntModelSpaceMat; + auto modelSpaceTra2 = Transform::FromMatrix(thisModelSpaceMat); + + assert(modelSpaceTra.equals(modelSpaceTra2)); } } for (auto& child : children) { - child->UpdateChildMatricesRec(updateChildMatrices); + child->UpdateChildTransformRec(updateChildTransform); } } @@ -520,11 +636,13 @@ void LocalModelPiece::UpdateParentMatricesRec() const dirty = false; - pieceSpaceMat = CalcPieceSpaceMatrix(pos, rot, original->scales); - modelSpaceMat = pieceSpaceMat; + pieceSpaceTra = CalcPieceSpaceTransform(pos, rot, original->scale); + modelSpaceTra = pieceSpaceTra; - if (parent != nullptr) - modelSpaceMat >>= parent->modelSpaceMat; + if (parent != nullptr) { + modelSpaceTra = parent->modelSpaceTra * modelSpaceTra; + modelSpaceMat = modelSpaceTra.ToMatrix(); + } } @@ -589,8 +707,8 @@ bool LocalModelPiece::GetEmitDirPos(float3& emitPos, float3& emitDir) const return false; // note: actually OBJECT_TO_WORLD but transform is the same - emitPos = GetModelSpaceMatrix() * original->GetEmitPos() * WORLD_TO_OBJECT_SPACE; - emitDir = GetModelSpaceMatrix() * float4(original->GetEmitDir(), 0.0f) * WORLD_TO_OBJECT_SPACE; + emitPos = GetModelSpaceTransform() * original->GetEmitPos() * WORLD_TO_OBJECT_SPACE; + emitDir = GetModelSpaceTransform() * float4(original->GetEmitDir(), 0.0f) * WORLD_TO_OBJECT_SPACE; return true; } @@ -633,3 +751,23 @@ size_t S3DModel::FindPieceOffset(const std::string& name) const return std::distance(pieceObjects.begin(), it); } + +void S3DModel::SetPieceMatrices() +{ + pieceObjects[0]->SetPieceTransform(Transform()); + + // use this occasion and copy bpose matrices + for (size_t i = 0; i < pieceObjects.size(); ++i) { + const auto* po = pieceObjects[i]; + //traAlloc[0 + i] = po->bposeTransform.ToMatrix(); + traAlloc.UpdateForced((0 + i), po->bposeTransform); + } + + // use this occasion and copy inverse bpose matrices + // store them right after all bind pose matrices + for (size_t i = 0; i < pieceObjects.size(); ++i) { + const auto* po = pieceObjects[i]; + //traAlloc[numPieces + i] = po->bposeInvTransform.ToMatrix(); + traAlloc.UpdateForced((numPieces + i), po->bposeInvTransform); + } +} diff --git a/rts/Rendering/Models/3DModel.h b/rts/Rendering/Models/3DModel.h index 5514a92477..6c56c21877 100644 --- a/rts/Rendering/Models/3DModel.h +++ b/rts/Rendering/Models/3DModel.h @@ -14,6 +14,7 @@ #include "Rendering/GL/VBO.h" #include "Sim/Misc/CollisionVolume.h" #include "System/Matrix44f.h" +#include "System/Transform.hpp" #include "System/type2.h" #include "System/float4.h" #include "System/SafeUtil.h" @@ -159,13 +160,13 @@ struct S3DModelPiece { parent = nullptr; colvol = {}; - bposeMatrix.LoadIdentity(); - bposeInvMatrix.LoadIdentity(); - bakedMatrix.LoadIdentity(); + bposeTransform.LoadIdentity(); + bposeInvTransform.LoadIdentity(); + bakedTransform.LoadIdentity(); offset = ZeroVector; goffset = ZeroVector; - scales = OnesVector; + scale = 1.0f; mins = DEF_MIN_SIZE; maxs = DEF_MAX_SIZE; @@ -174,7 +175,7 @@ struct S3DModelPiece { indxStart = ~0u; indxCount = ~0u; - hasBakedMat = false; + hasBakedTra = false; } virtual float3 GetEmitPos() const; @@ -190,7 +191,7 @@ struct S3DModelPiece { void DrawElements(GLuint prim = GL_TRIANGLES) const; static void DrawShatterElements(uint32_t vboIndxStart, uint32_t vboIndxCount, GLuint prim = GL_TRIANGLES); - bool HasBackedMat() const { return hasBakedMat; } + bool HasBackedTra() const { return hasBakedTra; } public: void DrawStaticLegacy(bool bind, bool bindPosMat) const; void DrawStaticLegacyRec() const; @@ -198,37 +199,13 @@ struct S3DModelPiece { void CreateShatterPieces(); void Shatter(float, int, int, int, const float3, const float3, const CMatrix44f&) const; - void SetPieceMatrix(const CMatrix44f& m) { - bposeMatrix = m * ComposeTransform(offset, ZeroVector, scales); - bposeInvMatrix = bposeMatrix.InvertAffine(); - - for (S3DModelPiece* c: children) { - c->SetPieceMatrix(bposeMatrix); - } + void SetPieceTransform(const Transform& parentTra); + void SetBakedTransform(const Transform& tra) { + bakedTransform = tra; + hasBakedTra = !tra.IsIdentity(); } - void SetBakedMatrix(const CMatrix44f& m) { - bakedMatrix = m; - hasBakedMat = !m.IsIdentity(); - assert(m.IsOrthoNormal()); - } - - CMatrix44f ComposeTransform(const float3& t, const float3& r, const float3& s) const { - CMatrix44f m; - // NOTE: - // ORDER MATTERS (T(baked + script) * R(baked) * R(script) * S(baked)) - // translating + rotating + scaling is faster than matrix-multiplying - // m is identity so m.SetPos(t)==m.Translate(t) but with fewer instrs - m.SetPos(t); - - if (hasBakedMat) - m *= bakedMatrix; - - // default Spring rotation-order [YPR=Y,X,Z] - m.RotateEulerYXZ(-r); - m.Scale(s); - return m; - } + Transform ComposeTransform(const float3& t, const float3& r, float s) const; void SetCollisionVolume(const CollisionVolume& cv) { colvol = cv; } const CollisionVolume* GetCollisionVolume() const { return &colvol; } @@ -253,13 +230,13 @@ struct S3DModelPiece { S3DModelPiece* parent = nullptr; CollisionVolume colvol; - CMatrix44f bposeMatrix; /// bind-pose transform, including baked rots - CMatrix44f bposeInvMatrix; /// Inverse of bind-pose transform, including baked rots - CMatrix44f bakedMatrix; /// baked local-space rotations + Transform bposeTransform; /// bind-pose transform, including baked rots + Transform bposeInvTransform; /// Inverse of bind-pose transform, including baked rots + Transform bakedTransform; /// baked local-space rotations - float3 offset; /// local (piece-space) offset wrt. parent piece - float3 goffset; /// global (model-space) offset wrt. root piece - float3 scales = OnesVector; /// baked uniform scaling factors (assimp-only) + float3 offset; /// local (piece-space) offset wrt. parent piece + float3 goffset; /// global (model-space) offset wrt. root piece + float scale{1.0f}; /// baked uniform scaling factor (assimp-only) float3 mins = DEF_MIN_SIZE; float3 maxs = DEF_MAX_SIZE; @@ -274,7 +251,7 @@ struct S3DModelPiece { S3DModel* model; - bool hasBakedMat; + bool hasBakedTra; public: friend class CAssParser; }; @@ -307,7 +284,7 @@ struct S3DModel , loadStatus(NOTLOADED) , uploaded(false) - , matAlloc(ScopedMatricesMemAlloc()) + , traAlloc(ScopedTransformMemAlloc()) {} S3DModel(const S3DModel& m) = delete; @@ -343,7 +320,7 @@ struct S3DModel loadStatus = m.loadStatus; uploaded = m.uploaded; - std::swap(matAlloc, m.matAlloc); + std::swap(traAlloc, m.traAlloc); return *this; } @@ -368,22 +345,7 @@ struct S3DModel S3DModelHelpers::UnbindLegacyAttrVBOs(); } - void SetPieceMatrices() { - pieceObjects[0]->SetPieceMatrix(CMatrix44f()); - - // use this occasion and copy bpose matrices - for (size_t i = 0; i < pieceObjects.size(); ++i) { - const auto* po = pieceObjects[i]; - matAlloc[0 + i] = po->bposeMatrix; - } - - // use this occasion and copy inverse bpose matrices - // store them right after all bind pose matrices - for (size_t i = 0; i < pieceObjects.size(); ++i) { - const auto* po = pieceObjects[i]; - matAlloc[numPieces + i] = po->bposeInvMatrix; - } - } + void SetPieceMatrices(); void FlattenPieceTree(S3DModelPiece* root) { assert(root != nullptr); @@ -393,7 +355,7 @@ struct S3DModel // force mutex just in case this is called from modelLoader.ProcessVertices() // TODO: pass to S3DModel if it is created from LoadModel(ST) or from ProcessVertices(MT) - matAlloc = ScopedMatricesMemAlloc(2 * numPieces); + traAlloc = ScopedTransformMemAlloc(2 * numPieces); std::vector stack = { root }; @@ -419,7 +381,7 @@ struct S3DModel float3 CalcDrawMidPos() const { return ((maxs + mins) * 0.5f); } float3 GetDrawMidPos() const { return relMidPos; } - const ScopedMatricesMemAlloc& GetMatAlloc() const { return matAlloc; } + const ScopedTransformMemAlloc& GetMatAlloc() const { return traAlloc; } public: std::string name; std::array texs; @@ -446,7 +408,7 @@ struct S3DModel LoadStatus loadStatus; bool uploaded; private: - ScopedMatricesMemAlloc matAlloc; + ScopedTransformMemAlloc traAlloc; }; @@ -482,31 +444,32 @@ struct LocalModelPiece // on-demand functions - void UpdateChildMatricesRec(bool updateChildMatrices) const; + void UpdateChildTransformRec(bool updateChildMatrices) const; void UpdateParentMatricesRec() const; - CMatrix44f CalcPieceSpaceMatrixRaw(const float3& p, const float3& r, const float3& s) const { return (original->ComposeTransform(p, r, s)); } - CMatrix44f CalcPieceSpaceMatrix(const float3& p, const float3& r, const float3& s) const { + auto CalcPieceSpaceTransformOrig(const float3& p, const float3& r, float s) const { return original->ComposeTransform(p, r, s); } + auto CalcPieceSpaceTransform(const float3& p, const float3& r, float s) const { if (blockScriptAnims) - return pieceSpaceMat; - return (CalcPieceSpaceMatrixRaw(p, r, s)); + return pieceSpaceTra; + + return CalcPieceSpaceTransformOrig(p, r, s); } // note: actually OBJECT_TO_WORLD but transform is the same - float3 GetAbsolutePos() const { return (GetModelSpaceMatrix().GetPos() * WORLD_TO_OBJECT_SPACE); } + float3 GetAbsolutePos() const { return (GetModelSpaceTransform().t * WORLD_TO_OBJECT_SPACE); } bool GetEmitDirPos(float3& emitPos, float3& emitDir) const; void SetDirty(); bool SetGetCustomDirty(bool cd) const; - void SetPosOrRot(const float3& src, float3& dst); // anim-script only - void SetPosition(const float3& p) { SetPosOrRot(p, pos); } // anim-script only - void SetRotation(const float3& r) { SetPosOrRot(r, rot); } // anim-script only + void SetPosOrRot(const float3& src, float3& dst, const float3& srcSpeed, float3& dstSpeed); // anim-script only + void SetPosition(const float3& p, const float3& dp = float3{}) { SetPosOrRot(p, pos, dp, posSpeed); } // anim-script only + void SetRotation(const float3& r, const float3& dr = float3{}) { SetPosOrRot(r, rot, dr, rotSpeed); } // anim-script only bool SetPieceSpaceMatrix(const CMatrix44f& mat) { if ((blockScriptAnims = (mat.GetX() != ZeroVector))) { - pieceSpaceMat = mat; + pieceSpaceTra = Transform::FromMatrix(mat); // neither of these are used outside of animation scripts, and // GetEulerAngles wants a matrix created by PYR rotation while @@ -521,10 +484,13 @@ struct LocalModelPiece const float3& GetPosition() const { return pos; } const float3& GetRotation() const { return rot; } + const float3& GetPositionSpeed() const { return posSpeed; } + const float3& GetRotationSpeed() const { return rotSpeed; } + const float3& GetDirection() const { return dir; } - const CMatrix44f& GetPieceSpaceMatrix() const { if (dirty) UpdateParentMatricesRec(); return pieceSpaceMat; } - const CMatrix44f& GetModelSpaceMatrix() const { if (dirty) UpdateParentMatricesRec(); return modelSpaceMat; } + Transform GetModelSpaceTransform() const { if (dirty) UpdateParentMatricesRec(); return modelSpaceTra; } + CMatrix44f GetModelSpaceMatrix() const { if (dirty) UpdateParentMatricesRec(); return modelSpaceMat; } const CollisionVolume* GetCollisionVolume() const { return &colvol; } CollisionVolume* GetCollisionVolume() { return &colvol; } @@ -532,12 +498,15 @@ struct LocalModelPiece bool GetScriptVisible() const { return scriptSetVisible; } void SetScriptVisible(bool b) { scriptSetVisible = b; SetGetCustomDirty(true); } private: - float3 pos; // translation relative to parent LMP, *INITIALLY* equal to original->offset - float3 rot; // orientation relative to parent LMP, in radians (updated by scripts) - float3 dir; // cached copy of original->GetEmitDir() + float3 pos; // translation relative to parent LMP, *INITIALLY* equal to original->offset + float3 posSpeed; // the rate of change of pos, used to smooth the animation across the synced frames + float3 rot; // orientation relative to parent LMP, in radians (updated by scripts) + float3 rotSpeed; // the rate of change of rot, used to smooth the animation across the synced frames + float3 dir; // cached copy of original->GetEmitDir() - mutable CMatrix44f pieceSpaceMat; // transform relative to parent LMP (SYNCED), combines and - mutable CMatrix44f modelSpaceMat; // transform relative to root LMP (SYNCED), chained pieceSpaceMat's + mutable Transform pieceSpaceTra; // transform relative to parent LMP (SYNCED), combines and + mutable Transform modelSpaceTra; // transform relative to root LMP (SYNCED), chained pieceSpaceMat's + mutable CMatrix44f modelSpaceMat; // same as above, except matrix CollisionVolume colvol; @@ -584,7 +553,6 @@ struct LocalModel // raw forms, the piece-index must be valid const float3 GetRawPiecePos(int pieceIdx) const { return pieces[pieceIdx].GetAbsolutePos(); } - const CMatrix44f& GetRawPieceMatrix(int pieceIdx) const { return pieces[pieceIdx].GetModelSpaceMatrix(); } // used by all SolidObject's; accounts for piece movement float GetDrawRadius() const { return (boundingVolume.GetBoundingRadius()); } diff --git a/rts/Rendering/Models/3DModelVAO.cpp b/rts/Rendering/Models/3DModelVAO.cpp index 86ec659fdf..d5390022b2 100644 --- a/rts/Rendering/Models/3DModelVAO.cpp +++ b/rts/Rendering/Models/3DModelVAO.cpp @@ -287,24 +287,24 @@ template bool S3DModelVAO::AddToSubmissionImpl(const TObj* obj, uint32_t indexStart, uint32_t indexCount, uint8_t teamID, uint8_t drawFlags) { RECOIL_DETAILED_TRACY_ZONE; - const auto matIndex = matrixUploader.GetElemOffset(obj); - if (matIndex == MatricesMemStorage::INVALID_INDEX) + const auto matIndex = transformsUploader.GetElemOffset(obj); + if (matIndex == TransformsMemStorage::INVALID_INDEX) return false; - const auto uniIndex = modelsUniformsStorage.GetObjOffset(obj); //doesn't need to exist for defs and models. Don't check for validity + const auto uniIndex = modelUniformsStorage.GetObjOffset(obj); //doesn't need to exist for defs and models. Don't check for validity uint16_t numPieces = 0; size_t bposeIndex = 0; if constexpr (std::is_same::value) { numPieces = static_cast(obj->numPieces); - bposeIndex = matrixUploader.GetElemOffset(obj); + bposeIndex = transformsUploader.GetElemOffset(obj); } else { numPieces = static_cast(obj->model->numPieces); - bposeIndex = matrixUploader.GetElemOffset(obj->model); + bposeIndex = transformsUploader.GetElemOffset(obj->model); } - if (bposeIndex == MatricesMemStorage::INVALID_INDEX) + if (bposeIndex == TransformsMemStorage::INVALID_INDEX) return false; auto& modelInstanceData = modelDataToInstance[SIndexAndCount{ indexStart, indexCount }]; @@ -414,21 +414,21 @@ template bool S3DModelVAO::SubmitImmediatelyImpl(const TObj* obj, uint32_t indexStart, uint32_t indexCount, uint8_t teamID, uint8_t drawFlags, GLenum mode, bool bindUnbind) { RECOIL_DETAILED_TRACY_ZONE; - std::size_t matIndex = matrixUploader.GetElemOffset(obj); - if (matIndex == MatricesMemStorage::INVALID_INDEX) + std::size_t matIndex = transformsUploader.GetElemOffset(obj); + if (matIndex == TransformsMemStorage::INVALID_INDEX) return false; - const auto uniIndex = modelsUniformsStorage.GetObjOffset(obj); //doesn't need to exist for defs. Don't check for validity + const auto uniIndex = modelUniformsStorage.GetObjOffset(obj); //doesn't need to exist for defs. Don't check for validity uint16_t numPieces = 0; size_t bposeIndex = 0; if constexpr (std::is_same::value) { numPieces = static_cast(obj->numPieces); - bposeIndex = matrixUploader.GetElemOffset(obj); + bposeIndex = transformsUploader.GetElemOffset(obj); } else { numPieces = static_cast(obj->model->numPieces); - bposeIndex = matrixUploader.GetElemOffset(obj->model); + bposeIndex = transformsUploader.GetElemOffset(obj->model); } SInstanceData instanceData(static_cast(matIndex), teamID, drawFlags, numPieces, uniIndex, bposeIndex); @@ -504,4 +504,4 @@ bool S3DModelVAO::SubmitImmediately(const UnitDef* unitDef, int teamID, GLenum m assert(model); return SubmitImmediatelyImpl(unitDef, model->indxStart, model->indxCount, teamID, 0, mode, bindUnbind); -} \ No newline at end of file +} diff --git a/rts/Rendering/Models/AssParser.cpp b/rts/Rendering/Models/AssParser.cpp index 6de4520ba8..bad9dd0aa8 100644 --- a/rts/Rendering/Models/AssParser.cpp +++ b/rts/Rendering/Models/AssParser.cpp @@ -142,20 +142,18 @@ struct SPseudoAssPiece { S3DModelPiece* parent; - CMatrix44f bposeMatrix; /// bind-pose transform, including baked rots - CMatrix44f bakedMatrix; /// baked local-space rotations + Transform bposeTransform; /// bind-pose transform, including baked rots + Transform bakedTransform; /// baked local-space rotations - float3 offset; /// local (piece-space) offset wrt. parent piece - float3 goffset; /// global (model-space) offset wrt. root piece - float3 scales = OnesVector; /// baked uniform scaling factors (assimp-only) + float3 offset; /// local (piece-space) offset wrt. parent piece + float3 goffset; /// global (model-space) offset wrt. root piece + float scale{1.0f}; /// baked uniform scaling factor (assimp-only) - bool hasBakedMat; + bool hasBakedTra; - // copy of S3DModelPiece::SetBakedMatrix() - void SetBakedMatrix(const CMatrix44f& m) { - bakedMatrix = m; - hasBakedMat = !m.IsIdentity(); - assert(m.IsOrthoNormal()); + // copy of S3DModelPiece::SetBakedTransform() + void SetBakedTransform(const Transform& tra) { + hasBakedTra = !tra.IsIdentity(); } // copy of S3DModelPiece::ComposeTransform() @@ -168,8 +166,8 @@ struct SPseudoAssPiece { // m is identity so m.SetPos(t)==m.Translate(t) but with fewer instrs m.SetPos(t); - if (hasBakedMat) - m *= bakedMatrix; + if (hasBakedTra) + m *= bakedTransform.ToMatrix(); // default Spring rotation-order [YPR=Y,X,Z] m.RotateEulerYXZ(-r); @@ -177,11 +175,9 @@ struct SPseudoAssPiece { return m; } - // copy of S3DModelPiece::SetPieceMatrix() + // copy of S3DModelPiece::SetPieceTransform() // except there's no need to do it recursively - void SetPieceMatrix(const CMatrix44f& parentBPoseMat) { - bposeMatrix = parentBPoseMat * ComposeTransform(offset, ZeroVector, scales); - } + void SetPieceTransform(const Transform& tra); }; ////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -458,21 +454,25 @@ namespace { // process transforms pieceNode->mTransformation.Decompose(aiScaleVec, aiRotateQuat, aiTransVec); + // TODO remove bakedMatrix and do everything with basic transformations const aiMatrix3x3t aiBakedRotMatrix = aiRotateQuat.GetMatrix(); const aiMatrix4x4t aiBakedMatrix = aiMatrix4x4t(aiBakedRotMatrix); CMatrix44f bakedMatrix = aiMatrixToMatrix(aiBakedMatrix); // metadata-scaling - piece->scales = pieceTable.GetFloat3("scale", aiVectorToFloat3(aiScaleVec)); - piece->scales.x = pieceTable.GetFloat("scalex", piece->scales.x); - piece->scales.y = pieceTable.GetFloat("scaley", piece->scales.y); - piece->scales.z = pieceTable.GetFloat("scalez", piece->scales.z); - - if (piece->scales.x != piece->scales.y || piece->scales.y != piece->scales.z) { - // LOG_SL(LOG_SECTION_MODEL, L_WARNING, "Spring doesn't support non-uniform scaling"); - piece->scales.y = piece->scales.x; - piece->scales.z = piece->scales.x; + float3 scales{ 1.0f, 1.0f, 1.0f }; + scales = pieceTable.GetFloat3("scale", aiVectorToFloat3(aiScaleVec)); + scales.x = pieceTable.GetFloat("scalex", scales.x); + scales.y = pieceTable.GetFloat("scaley", scales.y); + scales.z = pieceTable.GetFloat("scalez", scales.z); + + if (!epscmp(scales.x, scales.y, std::max(scales.x, scales.y) * float3::cmp_eps()) || + !epscmp(scales.y, scales.z, std::max(scales.y, scales.z) * float3::cmp_eps()) || + !epscmp(scales.z, scales.x, std::max(scales.z, scales.x) * float3::cmp_eps())) + { + LOG_SL(LOG_SECTION_MODEL, L_WARNING, "Recoil doesn't support non-uniform scaling"); } + piece->scale = scales.x; // metadata-translation piece->offset = pieceTable.GetFloat3("offset", aiVectorToFloat3(aiTransVec)); @@ -502,11 +502,11 @@ namespace { aiScaleVec.x, aiScaleVec.y, aiScaleVec.z ); LOG_SL(LOG_SECTION_PIECE, L_INFO, - "(%d:%s) Relative offset (%f,%f,%f), rotate (%f,%f,%f), scale (%f,%f,%f)", + "(%d:%s) Relative offset (%f,%f,%f), rotate (%f,%f,%f), scale (%f)", model->numPieces, piece->name.c_str(), piece->offset.x, piece->offset.y, piece->offset.z, bakedRotAngles.x, bakedRotAngles.y, bakedRotAngles.z, - piece->scales.x, piece->scales.y, piece->scales.z + piece->scale ); // construct 'baked' piece-space transform @@ -516,7 +516,14 @@ namespace { // and so the baked part reduces to R // // note: for all non-AssImp models this is identity! - piece->SetBakedMatrix(bakedMatrix.RotateEulerYXZ(-bakedRotAngles)); + + Transform bakedTransform(CQuaternion::FromEulerYPRNeg(-bakedRotAngles) * CQuaternion(aiRotateQuat.x, aiRotateQuat.y, aiRotateQuat.z, aiRotateQuat.w), ZeroVector, 1.0f); + piece->SetBakedTransform(bakedTransform); +#ifdef _DEBUG + Transform bakedTransform2 = Transform::FromMatrix(bakedMatrix); + assert(bakedTransform.equals(bakedTransform2)); +#endif // _DEBUG + } } @@ -792,8 +799,8 @@ const std::vector CAssParser::GetMeshBoneMatrices(const aiScene* sce std::vector meshBoneMatrices; for (auto& meshPP : meshPPs) { - meshPP.SetPieceMatrix(meshPP.parent->bposeMatrix); - meshBoneMatrices.emplace_back(meshPP.bposeMatrix); + meshPP.SetPieceTransform(meshPP.parent->bposeTransform); + meshBoneMatrices.emplace_back(meshPP.bposeTransform.ToMatrix()); } return meshBoneMatrices; @@ -1057,7 +1064,7 @@ void CAssParser::ReparentMeshesTrianglesToBones(S3DModel* model, const std::vect if (!piece->HasGeometryData()) continue; - const auto invMat = piece->bposeMatrix.InvertAffine(); + const auto invMat = piece->bposeTransform.ToMatrix().InvertAffine(); for (auto& vert : piece->vertices) { vert.pos = (invMat * float4{ vert.pos , 1.0f }).xyz; vert.normal = (invMat * float4{ vert.normal , 0.0f }).xyz; @@ -1146,7 +1153,7 @@ void CAssParser::ReparentCompleteMeshesToBones(S3DModel* model, const std::vecto if (!piece->HasGeometryData()) continue; - const auto invMat = piece->bposeMatrix.InvertAffine(); + const auto invMat = piece->bposeTransform.ToMatrix().InvertAffine(); for (auto& vert : piece->vertices) { vert.pos = (invMat * float4{ vert.pos , 1.0f }).xyz; vert.normal = (invMat * float4{ vert.normal , 0.0f }).xyz; @@ -1312,7 +1319,8 @@ void CAssParser::BuildPieceHierarchy(S3DModel* model, ModelPieceMap& pieceMap, c // Iterate over the model and calculate its overall dimensions void CAssParser::CalculateModelDimensions(S3DModel* model, S3DModelPiece* piece) { - const CMatrix44f scaleRotMat = piece->ComposeTransform(ZeroVector, ZeroVector, piece->scales); + // TODO fix + const CMatrix44f scaleRotMat = piece->ComposeTransform(ZeroVector, ZeroVector, piece->scale).ToMatrix(); // cannot set this until parent relations are known, so either here or in BuildPieceHierarchy() piece->goffset = scaleRotMat.Mul(piece->offset) + ((piece->parent != nullptr)? piece->parent->goffset: ZeroVector); @@ -1429,3 +1437,11 @@ void CAssParser::FindTextures( model->texs[1] = FindTexture(modelTable.GetString("tex2", ""), modelPath, model->texs[1]); } +void SPseudoAssPiece::SetPieceTransform(const Transform& tra) +{ + bposeTransform = tra * Transform{ + CQuaternion(), + offset, + scale + }; +} \ No newline at end of file diff --git a/rts/Rendering/Models/IModelParser.cpp b/rts/Rendering/Models/IModelParser.cpp index 4db3a80b27..06fb84c918 100644 --- a/rts/Rendering/Models/IModelParser.cpp +++ b/rts/Rendering/Models/IModelParser.cpp @@ -97,7 +97,7 @@ static void LoadDummyModel(S3DModel& model) model.numPieces = 1; // give it one empty piece model.AddPiece(g3DOParser.AllocPiece()); - model.FlattenPieceTree(model.GetRootPiece()); //useless except for setting up matAlloc + model.FlattenPieceTree(model.GetRootPiece()); //useless except for setting up traAlloc model.GetRootPiece()->SetCollisionVolume(CollisionVolume('b', 'z', -UpVector, ZeroVector)); model.loadStatus = S3DModel::LoadStatus::LOADED; } @@ -224,7 +224,7 @@ void CModelLoader::PreloadModel(const std::string& modelName) assert(Threading::IsMainThread() || Threading::IsGameLoadThread()); //NB: do preload in any case - if (ThreadPool::HasThreads()) { + if (ThreadPool::HasThreads() && false) { // if already in cache, thread just returns early // not spawning the thread at all would be better but still diff --git a/rts/Rendering/Models/ModelsMemStorage.cpp b/rts/Rendering/Models/ModelsMemStorage.cpp index c8ffe4242b..a2dcfb4f77 100644 --- a/rts/Rendering/Models/ModelsMemStorage.cpp +++ b/rts/Rendering/Models/ModelsMemStorage.cpp @@ -3,55 +3,126 @@ #include "System/Misc/TracyDefs.h" -MatricesMemStorage matricesMemStorage; -ModelsUniformsStorage modelsUniformsStorage; +TransformsMemStorage transformsMemStorage; +ModelUniformsStorage modelUniformsStorage; -ModelsUniformsStorage::ModelsUniformsStorage() +//////////////////////////////////////////////////////////////////// + +void ModelUniformsStorage::Init() { - RECOIL_DETAILED_TRACY_ZONE; - storage[0] = dummy; - objectsMap.emplace(nullptr, 0); + assert(updateList.Empty()); + assert(objectsMap.empty()); + assert(storage.empty()); + + storage[AddObject(static_cast(nullptr))] = dummy; +} + +void ModelUniformsStorage::Kill() +{ + // Remaining objects are not cleared anywhere (not a good thing) so delete them here + updateList.Clear(); + storage.clear(); + objectsMap.clear(); } -size_t ModelsUniformsStorage::AddObjects(const CWorldObject* o) +void ModelUniformsStorage::Reset() +{ + Kill(); + Init(); +} + +size_t ModelUniformsStorage::AddObject(const CWorldObject* o) { RECOIL_DETAILED_TRACY_ZONE; const size_t idx = storage.Add(ModelUniformData()); objectsMap[const_cast(o)] = idx; + + if (idx + 1 == storage.size()) { + //new item got added to the end of storage + updateList.EmplaceBackUpdate(); + } else { + // storage got updated somewhere in the middle, use updateList.SetUpdate() + updateList.SetUpdate(idx); + } + return idx; } -void ModelsUniformsStorage::DelObjects(const CWorldObject* o) +void ModelUniformsStorage::DelObject(const CWorldObject* o) { RECOIL_DETAILED_TRACY_ZONE; const auto it = objectsMap.find(const_cast(o)); - assert(it != objectsMap.end()); + + if (it == objectsMap.end()) + return; storage.Del(it->second); + + if (storage.size() < updateList.Size()) { + // storage got one element shorter, trim updateList as well + updateList.Trim(it->second); + } else { + // storage got updated somewhere in the middle, use updateList.SetUpdate() + updateList.SetUpdate(it->second); + } + objectsMap.erase(it); } -size_t ModelsUniformsStorage::GetObjOffset(const CWorldObject* o) +size_t ModelUniformsStorage::GetObjOffset(const CWorldObject* o) { RECOIL_DETAILED_TRACY_ZONE; const auto it = objectsMap.find(const_cast(o)); if (it != objectsMap.end()) return it->second; - size_t idx = AddObjects(o); + size_t idx = AddObject(o); return idx; } -ModelUniformData& ModelsUniformsStorage::GetObjUniformsArray(const CWorldObject* o) +ModelUniformsStorage::MyType& ModelUniformsStorage::GetObjUniformsArray(const CWorldObject* o) { RECOIL_DETAILED_TRACY_ZONE; size_t offset = GetObjOffset(o); return storage[offset]; } -void MatricesMemStorage::SetAllDirty() +//////////////////////////////////////////////////////////////////// + +TransformsMemStorage::TransformsMemStorage() + : storage(StablePosAllocator(INIT_NUM_ELEMS)) + , updateList(INIT_NUM_ELEMS) +{} + +void TransformsMemStorage::Reset() { - RECOIL_DETAILED_TRACY_ZONE; assert(Threading::IsMainThread()); - std::fill(dirtyMap.begin(), dirtyMap.end(), BUFFERING); + storage.Reset(); + updateList.Clear(); +} + +size_t TransformsMemStorage::Allocate(size_t numElems) +{ + auto lock = CModelsLock::GetScopedLock(); + + auto res = storage.Allocate(numElems); + updateList.Resize(storage.GetSize()); + + return res; } + +void TransformsMemStorage::Free(size_t firstElem, size_t numElems, const MyType* T0) +{ + auto lock = CModelsLock::GetScopedLock(); + + storage.Free(firstElem, numElems, T0); + updateList.SetUpdate(firstElem, numElems); + updateList.Trim(storage.GetSize()); +} + +const TransformsMemStorage::MyType& TransformsMemStorage::operator[](std::size_t idx) const +{ + auto lock = CModelsLock::GetScopedLock(); + + return storage[idx]; +} \ No newline at end of file diff --git a/rts/Rendering/Models/ModelsMemStorage.h b/rts/Rendering/Models/ModelsMemStorage.h index f58ada71e9..1061913654 100644 --- a/rts/Rendering/Models/ModelsMemStorage.h +++ b/rts/Rendering/Models/ModelsMemStorage.h @@ -1,98 +1,103 @@ #pragma once #include -#include #include #include "ModelsMemStorageDefs.h" #include "ModelsLock.h" -#include "System/Matrix44f.h" +#include "System/Transform.hpp" #include "System/MemPoolTypes.h" #include "System/FreeListMap.h" +#include "System/UnorderedMap.hpp" +#include "System/TypeToStr.h" #include "System/Threading/SpringThreading.h" #include "Sim/Misc/GlobalConstants.h" #include "Sim/Objects/SolidObjectDef.h" +#include "Rendering/Common/UpdateList.h" -class MatricesMemStorage : public StablePosAllocator { +class TransformsMemStorage { public: - explicit MatricesMemStorage() - : StablePosAllocator(INIT_NUM_ELEMS) - , dirtyMap(INIT_NUM_ELEMS, BUFFERING) - {} - void Reset() override { - assert(Threading::IsMainThread()); - StablePosAllocator::Reset(); - dirtyMap.resize(GetSize(), BUFFERING); - } + using MyType = Transform; + using EqualCmpFunctor = bool(*)(const MyType&, const MyType&); +public: + explicit TransformsMemStorage(); + void Reset(); - size_t Allocate(size_t numElems) override { - auto lock = CModelsLock::GetScopedLock(); - size_t res = StablePosAllocator::Allocate(numElems); - dirtyMap.resize(GetSize(), BUFFERING); + size_t Allocate(size_t numElems); - return res; - } - void Free(size_t firstElem, size_t numElems, const CMatrix44f* T0 = nullptr) override { - auto lock = CModelsLock::GetScopedLock(); - StablePosAllocator::Free(firstElem, numElems, T0); - dirtyMap.resize(GetSize(), BUFFERING); - } + void Free(size_t firstElem, size_t numElems, const MyType* T0 = nullptr); - const CMatrix44f& operator[](std::size_t idx) const override - { + const auto& GetData() const { return storage.GetData(); } + const auto GetSize() const { return storage.GetSize(); } + + template // to force universal references + bool UpdateIfChanged(std::size_t idx, MyTypeLike&& newValue, EqualCmpFunctor eqCmp) { auto lock = CModelsLock::GetScopedLock(); - return StablePosAllocator::operator[](idx); + + using DT = StablePosAllocator; + const auto& curValue = static_cast(storage)[idx]; + if (eqCmp(curValue, newValue)) + return false; + + updateList.SetUpdate(idx); + auto& mutValue = static_cast(storage)[idx]; + mutValue = newValue; + + assert(updateList.Size() == storage.GetSize()); + + return true; } - CMatrix44f& operator[](std::size_t idx) override - { + + template // to force universal references + void UpdateForced(std::size_t idx, MyTypeLike&& newValue) { auto lock = CModelsLock::GetScopedLock(); - return StablePosAllocator::operator[](idx); - } -private: - std::vector dirtyMap; -public: - const decltype(dirtyMap)& GetDirtyMap() const - { - return dirtyMap; - } - decltype(dirtyMap)& GetDirtyMap() - { - return dirtyMap; + + updateList.SetUpdate(idx); + auto& mutValue = storage[idx]; + mutValue = newValue; + + assert(updateList.Size() == storage.GetSize()); } - void SetAllDirty(); -public: - //need to update buffer with matrices BUFFERING times, because the actual buffer is made of BUFFERING number of parts - static constexpr uint8_t BUFFERING = 3u; + const MyType& operator[](std::size_t idx) const; + + const auto& GetUpdateList() const { return updateList; } + void SetUpdateListUpdateAll() { updateList.SetNeedUpdateAll(); } + void SetUpdateListReset() { updateList.ResetNeedUpdateAll(); } +private: + StablePosAllocator storage; + UpdateList updateList; private: static constexpr int INIT_NUM_ELEMS = 1 << 16u; +public: + static constexpr auto INVALID_INDEX = StablePosAllocator::INVALID_INDEX; }; -extern MatricesMemStorage matricesMemStorage; +extern TransformsMemStorage transformsMemStorage; //////////////////////////////////////////////////////////////////// -class ScopedMatricesMemAlloc { +class ScopedTransformMemAlloc { public: - ScopedMatricesMemAlloc() : ScopedMatricesMemAlloc(0u) {}; - ScopedMatricesMemAlloc(std::size_t numElems_) + ScopedTransformMemAlloc() : ScopedTransformMemAlloc(0u) {}; + ScopedTransformMemAlloc(std::size_t numElems_) : numElems{numElems_} { - firstElem = matricesMemStorage.Allocate(numElems); + firstElem = transformsMemStorage.Allocate(numElems); } - ScopedMatricesMemAlloc(const ScopedMatricesMemAlloc&) = delete; - ScopedMatricesMemAlloc(ScopedMatricesMemAlloc&& smma) noexcept { *this = std::move(smma); } + ScopedTransformMemAlloc(const ScopedTransformMemAlloc&) = delete; + ScopedTransformMemAlloc(ScopedTransformMemAlloc&& smma) noexcept { *this = std::move(smma); } - ~ScopedMatricesMemAlloc() { - if (firstElem == MatricesMemStorage::INVALID_INDEX) + ~ScopedTransformMemAlloc() { + if (firstElem == TransformsMemStorage::INVALID_INDEX) return; - matricesMemStorage.Free(firstElem, numElems, &CMatrix44f::Zero()); + transformsMemStorage.Free(firstElem, numElems, &Transform::Zero()); } - bool Valid() const { return firstElem != MatricesMemStorage::INVALID_INDEX; } + bool Valid() const { return firstElem != TransformsMemStorage::INVALID_INDEX; } std::size_t GetOffset(bool assertInvalid = true) const { if (assertInvalid) assert(Valid()); @@ -100,8 +105,8 @@ class ScopedMatricesMemAlloc { return firstElem; } - ScopedMatricesMemAlloc& operator= (const ScopedMatricesMemAlloc&) = delete; - ScopedMatricesMemAlloc& operator= (ScopedMatricesMemAlloc&& smma) noexcept { + ScopedTransformMemAlloc& operator= (const ScopedTransformMemAlloc&) = delete; + ScopedTransformMemAlloc& operator= (ScopedTransformMemAlloc&& smma) noexcept { //swap to prevent dealloc on dying object, yet enable destructor to do its thing on valid object std::swap(firstElem, smma.firstElem); std::swap(numElems , smma.numElems ); @@ -109,61 +114,84 @@ class ScopedMatricesMemAlloc { return *this; } - const CMatrix44f& operator[](std::size_t offset) const { - assert(firstElem != MatricesMemStorage::INVALID_INDEX); + const auto& operator[](std::size_t offset) const { + assert(firstElem != TransformsMemStorage::INVALID_INDEX); assert(offset >= 0 && offset < numElems); - return matricesMemStorage[firstElem + offset]; + return transformsMemStorage[firstElem + offset]; } - CMatrix44f& operator[](std::size_t offset) { - assert(firstElem != MatricesMemStorage::INVALID_INDEX); + + template // to force universal references + bool UpdateIfChanged(std::size_t offset, MyTypeLike&& newValue) { + static const auto EqCmp = [](const TransformsMemStorage::MyType& lhs, const TransformsMemStorage::MyType& rhs) { + return lhs.equals(rhs); + }; + + assert(firstElem != TransformsMemStorage::INVALID_INDEX); + assert(offset >= 0 && offset < numElems); + + return transformsMemStorage.UpdateIfChanged(firstElem + offset, std::forward(newValue), EqCmp); + } + + template // to force universal references + void UpdateForced(std::size_t offset, MyTypeLike&& newValue) { + assert(firstElem != TransformsMemStorage::INVALID_INDEX); assert(offset >= 0 && offset < numElems); - matricesMemStorage.GetDirtyMap().at(firstElem + offset) = MatricesMemStorage::BUFFERING; - return matricesMemStorage[firstElem + offset]; + transformsMemStorage.UpdateForced(firstElem + offset, std::forward(newValue)); } public: - static const ScopedMatricesMemAlloc& Dummy() { - static ScopedMatricesMemAlloc dummy; + static const ScopedTransformMemAlloc& Dummy() { + static ScopedTransformMemAlloc dummy; return dummy; }; private: - std::size_t firstElem = MatricesMemStorage::INVALID_INDEX; + std::size_t firstElem = TransformsMemStorage::INVALID_INDEX; std::size_t numElems = 0u; }; //////////////////////////////////////////////////////////////////// class CWorldObject; -class ModelsUniformsStorage { +class ModelUniformsStorage { +private: + using MyType = ModelUniformData; public: - ModelsUniformsStorage(); + void Init(); + void Kill(); + void Reset(); public: - size_t AddObjects(const CWorldObject* o); - void DelObjects(const CWorldObject* o); + size_t AddObject(const CWorldObject* o); size_t GetObjOffset(const CWorldObject* o); - ModelUniformData& GetObjUniformsArray(const CWorldObject* o); + MyType& GetObjUniformsArray(const CWorldObject* o); + void DelObject(const CWorldObject* o); - size_t AddObjects(const SolidObjectDef* o) { return INVALID_INDEX; } - void DelObjects(const SolidObjectDef* o) {} + size_t AddObject(const SolidObjectDef* o) { return INVALID_INDEX; } size_t GetObjOffset(const SolidObjectDef* o) { return INVALID_INDEX; } - ModelUniformData& GetObjUniformsArray(const SolidObjectDef* o) { return dummy; } + MyType& GetObjUniformsArray(const SolidObjectDef* o) { return dummy; } + void DelObject(const SolidObjectDef* o) {} - size_t AddObjects(const S3DModel* o) { return INVALID_INDEX; } - void DelObjects(const S3DModel* o) {} + size_t AddObject(const S3DModel* o) { return INVALID_INDEX; } size_t GetObjOffset(const S3DModel* o) { return INVALID_INDEX; } - ModelUniformData& GetObjUniformsArray(const S3DModel* o) { return dummy; } + MyType& GetObjUniformsArray(const S3DModel* o) { return dummy; } + void DelObject(const S3DModel* o) {} - size_t Size() const { return storage.GetData().size(); } - const std::vector& GetData() const { return storage.GetData(); } + auto GetSize() const { return storage.GetData().size(); } + const auto& GetData() const { return storage.GetData(); } + + const auto& GetUpdateList() const { return updateList; } + void SetUpdateListUpdateAll() { updateList.SetNeedUpdateAll(); } + void SetUpdateListReset() { updateList.ResetNeedUpdateAll(); } +private: + UpdateList updateList; public: static constexpr size_t INVALID_INDEX = 0; private: - inline static ModelUniformData dummy = {}; + inline static MyType dummy = {}; - std::unordered_map objectsMap; - spring::FreeListMap storage; + spring::unordered_map objectsMap; + spring::FreeListMap storage; }; -extern ModelsUniformsStorage modelsUniformsStorage; +extern ModelUniformsStorage modelUniformsStorage; diff --git a/rts/Rendering/Models/ModelsMemStorageDefs.h b/rts/Rendering/Models/ModelsMemStorageDefs.h index 703542e8c9..f276b3c8fb 100644 --- a/rts/Rendering/Models/ModelsMemStorageDefs.h +++ b/rts/Rendering/Models/ModelsMemStorageDefs.h @@ -3,7 +3,7 @@ #include "System/float4.h" #include "Sim/Misc/GlobalConstants.h" -class alignas(4) ModelUniformData { +class alignas(16) ModelUniformData { public: CR_DECLARE_STRUCT(ModelUniformData) static constexpr int MAX_MODEL_UD_UNIFORMS = 16; @@ -39,5 +39,5 @@ class alignas(4) ModelUniformData { }; static_assert(sizeof(ModelUniformData) == 128, ""); -static_assert(alignof(ModelUniformData) == 4, ""); -static_assert(sizeof(ModelUniformData::userDefined) % 4 == 0, ""); //due to GLSL std140 userDefined must be a multiple of 4 \ No newline at end of file +static_assert(alignof(ModelUniformData) == 16, ""); +static_assert(sizeof(ModelUniformData::userDefined) % 4 == 0, ""); //due to GLSL std140 userDefined must be a multiple of 4 diff --git a/rts/Rendering/ModelsDataUploader.cpp b/rts/Rendering/ModelsDataUploader.cpp index c0ea055f70..2fbc101cbb 100644 --- a/rts/Rendering/ModelsDataUploader.cpp +++ b/rts/Rendering/ModelsDataUploader.cpp @@ -32,369 +32,348 @@ //////////////////////////////////////////////////////////////////// -template -void TypedStorageBufferUploader::InitImpl(uint32_t bindingIdx_, uint32_t elemCount0_, uint32_t elemCountIncr_, uint8_t type, bool coherent, uint32_t numBuffers) -{ - if (!globalRendering->haveGL4) - return; +namespace Impl { + template< + typename SSBO, + typename Uploader, + typename MemStorage + > + void UpdateCommon(Uploader& uploader, std::unique_ptr& ssbo, MemStorage& memStorage, const char* className, const char* funcName) + { + //resize + const uint32_t elemCount = uploader.GetElemsCount(); + const uint32_t storageElemCount = memStorage.GetSize(); + if (storageElemCount > elemCount) { + ssbo->UnbindBufferRange(uploader.GetBindingIdx()); + + const uint32_t newElemCount = AlignUp(storageElemCount, uploader.GetElemCountIncr()); + LOG_L(L_DEBUG, "[%s::%s] sizing SSBO %s. New elements count = %u, elemCount = %u, storageElemCount = %u", className, funcName, "up", newElemCount, elemCount, storageElemCount); + ssbo->Resize(newElemCount); + // ssbo->Resize() doesn't copy the data, force the update + memStorage.SetUpdateListUpdateAll(); + } - bindingIdx = bindingIdx_; - elemCount0 = elemCount0_; - elemCountIncr = elemCountIncr_; + const auto& ul = memStorage.GetUpdateList(); + if (!ul.NeedUpdate()) + return; - assert(bindingIdx < -1u); + // may have been already unbound above, not a big deal + ssbo->UnbindBufferRange(uploader.GetBindingIdx()); - IStreamBufferConcept::StreamBufferCreationParams p; - p.target = GL_SHADER_STORAGE_BUFFER; - p.numElems = elemCount0; - p.name = std::string(className); - p.type = static_cast(type); - p.resizeAble = true; - p.coherent = coherent; - p.numBuffers = numBuffers; - p.optimizeForStreaming = true; + // get the data + const auto* clientPtr = memStorage.GetData().data(); - ssbo = IStreamBuffer::CreateInstance(p); - ssbo->BindBufferRange(bindingIdx); -} + // iterate over contiguous regions of values that need update on the GPU + for (auto itPair = ul.GetNext(); itPair.has_value(); itPair = ul.GetNext(itPair)) { + auto [idxOffset, idxSize] = ul.GetOffsetAndSize(itPair.value()); -template -void TypedStorageBufferUploader::KillImpl() -{ - if (!globalRendering->haveGL4) - return; + auto* mappedPtr = ssbo->Map(clientPtr, idxOffset, idxSize); - ssbo->UnbindBufferRange(bindingIdx); - ssbo = nullptr; -} + if (!ssbo->HasClientPtr()) + memcpy(mappedPtr, clientPtr, storageElemCount * sizeof(decltype(*clientPtr))); -template -inline uint32_t TypedStorageBufferUploader::GetElemsCount() const -{ - return ssbo->GetByteSize() / sizeof(T); -} + ssbo->Unmap(); + } -template -std::size_t TypedStorageBufferUploader::GetUnitDefElemOffset(int32_t unitDefID) const -{ - return GetDefElemOffsetImpl(unitDefHandler->GetUnitDefByID(unitDefID)); -} + ssbo->BindBufferRange(uploader.GetBindingIdx()); + ssbo->SwapBuffer(); -template -std::size_t TypedStorageBufferUploader::GetFeatureDefElemOffset(int32_t featureDefID) const -{ - return GetDefElemOffsetImpl(featureDefHandler->GetFeatureDefByID(featureDefID)); -} + memStorage.SetUpdateListReset(); + } -template -std::size_t TypedStorageBufferUploader::GetUnitElemOffset(int32_t unitID) const -{ - return GetElemOffsetImpl(unitHandler.GetUnit(unitID)); -} + template + void InitCommon(std::unique_ptr& ssbo, uint32_t bindingIdx, uint32_t elemCount0, uint32_t elemCountIncr, IStreamBufferConcept::Types type, bool coherent, uint32_t numBuffers, const char* className) + { + if (!globalRendering->haveGL4) + return; + + assert(bindingIdx < -1u); + + IStreamBufferConcept::StreamBufferCreationParams p; + p.target = GL_SHADER_STORAGE_BUFFER; + p.numElems = elemCount0; + p.name = std::string(className); + p.type = type; + p.resizeAble = true; + p.coherent = coherent; + p.numBuffers = numBuffers; + p.optimizeForStreaming = true; + + ssbo = std::move(IStreamBuffer::CreateInstance(p)); + ssbo->BindBufferRange(bindingIdx); + } -template -std::size_t TypedStorageBufferUploader::GetFeatureElemOffset(int32_t featureID) const -{ - return GetElemOffsetImpl(featureHandler.GetFeature(featureID)); -} + template + void KillCommon(std::unique_ptr& ssbo, uint32_t bindingIdx) + { + if (!globalRendering->haveGL4) + return; -template -std::size_t TypedStorageBufferUploader::GetProjectileElemOffset(int32_t syncedProjectileID) const -{ - return GetElemOffsetImpl(projectileHandler.GetProjectileBySyncedID(syncedProjectileID)); + ssbo->UnbindBufferRange(bindingIdx); + ssbo = nullptr; + } } -void MatrixUploader::InitDerived() -{ - if (!globalRendering->haveGL4) - return; +//////////////////////////////////////////////////////////////////// - const auto sbType = globalRendering->supportPersistentMapping - ? IStreamBufferConcept::Types::SB_PERSISTENTMAP - : IStreamBufferConcept::Types::SB_BUFFERSUBDATA; - - InitImpl(MATRIX_SSBO_BINDING_IDX, ELEM_COUNT0, ELEM_COUNTI, sbType, true, MatricesMemStorage::BUFFERING); - if (ssbo->GetBufferImplementation() == IStreamBufferConcept::Types::SB_PERSISTENTMAP && !ssbo->IsValid()) { - // some potato driver overestimated its support for SB_PERSISTENTMAP - // Redo with good old SB_BUFFERSUBDATA - LOG_L(L_ERROR, "[MatrixUploader::%s] OpenGL reported persistent mapping to be available, but initial mapping of buffer failed. Falling back.", __func__); - KillImpl(); - InitImpl(MATRIX_SSBO_BINDING_IDX, ELEM_COUNT0, ELEM_COUNTI, IStreamBufferConcept::Types::SB_BUFFERSUBDATA, true, MatricesMemStorage::BUFFERING); - } -} +TransformsUploader transformsUploader; +ModelUniformsUploader modelUniformsUploader; + +//////////////////////////////////////////////////////////////////// -void MatrixUploader::KillDerived() +void TransformsUploader::Init() { - if (!globalRendering->haveGL4) - return; + Impl::InitCommon( + ssbo, + MATRIX_SSBO_BINDING_IDX, ELEM_COUNT0, ELEM_COUNTI, + IStreamBufferConcept::Types::SB_BUFFERSUBDATA, true, 1, + className + ); +} - KillImpl(); +void TransformsUploader::Kill() +{ + Impl::KillCommon(ssbo, MATRIX_SSBO_BINDING_IDX); } -void MatrixUploader::UpdateDerived() +void TransformsUploader::Update() { if (!globalRendering->haveGL4) return; - SCOPED_TIMER("MatrixUploader::Update"); - ssbo->UnbindBufferRange(bindingIdx); + SCOPED_TIMER("TransformsUploader::Update"); + // TODO why the lock? auto lock = CModelsLock::GetScopedLock(); - //resize - const uint32_t elemCount = GetElemsCount(); - const uint32_t storageElemCount = matricesMemStorage.GetSize(); - if (storageElemCount > elemCount) { - const uint32_t newElemCount = AlignUp(storageElemCount, elemCountIncr); - LOG_L(L_DEBUG, "[%s::%s] sizing SSBO %s. New elements count = %u, elemCount = %u, storageElemCount = %u", className, __func__, "up", newElemCount, elemCount, storageElemCount); - ssbo->Resize(newElemCount); - - if (ssbo->GetBufferImplementation() == IStreamBufferConcept::Types::SB_PERSISTENTMAP && !ssbo->IsValid()) { - LOG_L(L_ERROR, "[MatrixUploader::%s] OpenGL reported persistent mapping to be available, but mapping of buffer of %u size failed. Falling back.", __func__, uint32_t(newElemCount * sizeof(CMatrix44f))); - KillImpl(); - InitImpl(MATRIX_SSBO_BINDING_IDX, newElemCount, ELEM_COUNTI, IStreamBufferConcept::Types::SB_BUFFERSUBDATA, true, MatricesMemStorage::BUFFERING); - } + Impl::UpdateCommon(*this, ssbo, transformsMemStorage, className, __func__); +} - matricesMemStorage.SetAllDirty(); //Resize doesn't copy the data +size_t TransformsUploader::GetElemOffset(const UnitDef* def) const +{ + if (def == nullptr) { + LOG_L(L_ERROR, "[%s::%s] Supplied nullptr UnitDef", className, __func__); + return TransformsMemStorage::INVALID_INDEX; } - //update on the GPU - const CMatrix44f* clientPtr = matricesMemStorage.GetData().data(); - - constexpr bool ENABLE_UPLOAD_OPTIMIZATION = true; - if (ssbo->GetBufferImplementation() == IStreamBufferConcept::Types::SB_PERSISTENTMAP && ENABLE_UPLOAD_OPTIMIZATION) { - const auto stt = matricesMemStorage.GetDirtyMap().begin(); - const auto fin = matricesMemStorage.GetDirtyMap().end(); - - auto beg = matricesMemStorage.GetDirtyMap().begin(); - auto end = matricesMemStorage.GetDirtyMap().begin(); - - static const auto dirtyPred = [](uint8_t m) -> bool { return m > 0u; }; - while (beg != fin) { - beg = std::find_if (beg, fin, dirtyPred); - end = std::find_if_not(beg, fin, dirtyPred); - - if (beg != fin) { - const uint32_t offs = static_cast(std::distance(stt, beg)); - const uint32_t size = static_cast(std::distance(beg, end)); - - CMatrix44f* mappedPtr = ssbo->Map(clientPtr, offs, size); - memcpy(mappedPtr, clientPtr + offs, size * sizeof(CMatrix44f)); - ssbo->Unmap(); - - std::transform(beg, end, beg, [](uint8_t v) { return (v - 1); }); //make it less dirty - } + return GetElemOffset(def->LoadModel()); +} - beg = end; //rewind - } +size_t TransformsUploader::GetElemOffset(const FeatureDef* def) const +{ + if (def == nullptr) { + LOG_L(L_ERROR, "[%s::%s] Supplied nullptr FeatureDef", className, __func__); + return TransformsMemStorage::INVALID_INDEX; } - else { - const CMatrix44f* clientPtr = matricesMemStorage.GetData().data(); - CMatrix44f* mappedPtr = ssbo->Map(clientPtr, 0, storageElemCount); - if (!ssbo->HasClientPtr()) - memcpy(mappedPtr, clientPtr, storageElemCount * sizeof(CMatrix44f)); - - ssbo->Unmap(); - } - ssbo->BindBufferRange(bindingIdx); - ssbo->SwapBuffer(); + return GetElemOffset(def->LoadModel()); } -std::size_t MatrixUploader::GetDefElemOffsetImpl(const S3DModel* model) const +size_t TransformsUploader::GetElemOffset(const S3DModel* model) const { if (model == nullptr) { LOG_L(L_ERROR, "[%s::%s] Supplied nullptr S3DModel", className, __func__); - return MatricesMemStorage::INVALID_INDEX; + return TransformsMemStorage::INVALID_INDEX; } return model->GetMatAlloc().GetOffset(false); } -std::size_t MatrixUploader::GetDefElemOffsetImpl(const UnitDef* def) const +size_t TransformsUploader::GetUnitDefElemOffset(int32_t unitDefID) const { - if (def == nullptr) { - LOG_L(L_ERROR, "[%s::%s] Supplied nullptr UnitDef", className, __func__); - return MatricesMemStorage::INVALID_INDEX; - } - - return GetDefElemOffsetImpl(def->LoadModel()); + return GetElemOffset(unitDefHandler->GetUnitDefByID(unitDefID)); } -std::size_t MatrixUploader::GetDefElemOffsetImpl(const FeatureDef* def) const +size_t TransformsUploader::GetFeatureDefElemOffset(int32_t featureDefID) const { - if (def == nullptr) { - LOG_L(L_ERROR, "[%s::%s] Supplied nullptr FeatureDef", className, __func__); - return MatricesMemStorage::INVALID_INDEX; - } - - return GetDefElemOffsetImpl(def->LoadModel()); + return GetElemOffset(featureDefHandler->GetFeatureDefByID(featureDefID)); } -std::size_t MatrixUploader::GetElemOffsetImpl(const CUnit* unit) const +size_t TransformsUploader::GetElemOffset(const CUnit* unit) const { if (unit == nullptr) { LOG_L(L_ERROR, "[%s::%s] Supplied nullptr CUnit", className, __func__); - return MatricesMemStorage::INVALID_INDEX; + return TransformsMemStorage::INVALID_INDEX; } - if (std::size_t offset = CUnitDrawer::GetMatricesMemAlloc(unit).GetOffset(false); offset != MatricesMemStorage::INVALID_INDEX) { + if (size_t offset = CUnitDrawer::GetTransformMemAlloc(unit).GetOffset(false); offset != TransformsMemStorage::INVALID_INDEX) { return offset; } LOG_L(L_ERROR, "[%s::%s] Supplied invalid CUnit (id:%d)", className, __func__, unit->id); - return MatricesMemStorage::INVALID_INDEX; + return TransformsMemStorage::INVALID_INDEX; } -std::size_t MatrixUploader::GetElemOffsetImpl(const CFeature* feature) const +size_t TransformsUploader::GetElemOffset(const CFeature* feature) const { if (feature == nullptr) { LOG_L(L_ERROR, "[%s::%s] Supplied nullptr CFeature", className, __func__); - return MatricesMemStorage::INVALID_INDEX; + return TransformsMemStorage::INVALID_INDEX; } - if (std::size_t offset = CFeatureDrawer::GetMatricesMemAlloc(feature).GetOffset(false); offset != MatricesMemStorage::INVALID_INDEX) { + if (size_t offset = CFeatureDrawer::GetTransformMemAlloc(feature).GetOffset(false); offset != TransformsMemStorage::INVALID_INDEX) { return offset; } LOG_L(L_ERROR, "[%s::%s] Supplied invalid CFeature (id:%d)", className, __func__, feature->id); - return MatricesMemStorage::INVALID_INDEX; + return TransformsMemStorage::INVALID_INDEX; } -std::size_t MatrixUploader::GetElemOffsetImpl(const CProjectile* p) const +size_t TransformsUploader::GetElemOffset(const CProjectile* p) const { if (p == nullptr) { LOG_L(L_ERROR, "[%s::%s] Supplied nullptr CProjectile", className, __func__); - return MatricesMemStorage::INVALID_INDEX; + return TransformsMemStorage::INVALID_INDEX; } if (!p->synced) { LOG_L(L_ERROR, "[%s::%s] Supplied non-synced CProjectile (id:%d)", className, __func__, p->id); - return MatricesMemStorage::INVALID_INDEX; + return TransformsMemStorage::INVALID_INDEX; } if (!p->weapon || !p->piece) { LOG_L(L_ERROR, "[%s::%s] Supplied non-weapon or non-piece CProjectile (id:%d)", className, __func__, p->id); - return MatricesMemStorage::INVALID_INDEX; + return TransformsMemStorage::INVALID_INDEX; } /* - if (std::size_t offset = p->GetMatAlloc().GetOffset(false); offset != MatricesMemStorage::INVALID_INDEX) { + if (size_t offset = p->GetMatAlloc().GetOffset(false); offset != TransformsMemStorage::INVALID_INDEX) { return offset; } */ LOG_L(L_ERROR, "[%s::%s] Supplied invalid CProjectile (id:%d)", className, __func__, p->id); - return MatricesMemStorage::INVALID_INDEX; + return TransformsMemStorage::INVALID_INDEX; } -void ModelsUniformsUploader::InitDerived() +size_t TransformsUploader::GetUnitElemOffset(int32_t unitID) const { - if (!globalRendering->haveGL4) - return; - - InitImpl(MATUNI_SSBO_BINDING_IDX, ELEM_COUNT0, ELEM_COUNTI, IStreamBufferConcept::Types::SB_BUFFERSUBDATA, true, 3); + return GetElemOffset(unitHandler.GetUnit(unitID)); } -void ModelsUniformsUploader::KillDerived() +size_t TransformsUploader::GetFeatureElemOffset(int32_t featureID) const { - if (!globalRendering->haveGL4) - return; + return GetElemOffset(featureHandler.GetFeature(featureID)); +} - KillImpl(); +size_t TransformsUploader::GetProjectileElemOffset(int32_t syncedProjectileID) const +{ + return GetElemOffset(projectileHandler.GetProjectileBySyncedID(syncedProjectileID)); } -void ModelsUniformsUploader::UpdateDerived() +//////////////////////////////////////////////////////////////////// + +void ModelUniformsUploader::Init() { if (!globalRendering->haveGL4) return; - SCOPED_TIMER("ModelsUniformsUploader::Update"); - ssbo->UnbindBufferRange(bindingIdx); - - //resize - const uint32_t elemCount = GetElemsCount(); - const uint32_t storageElemCount = modelsUniformsStorage.Size(); - if (storageElemCount > elemCount) { - const uint32_t newElemCount = AlignUp(storageElemCount, elemCountIncr); - LOG_L(L_DEBUG, "[%s::%s] sizing SSBO %s. New elements count = %u, elemCount = %u, storageElemCount = %u", className, __func__, "up", newElemCount, elemCount, storageElemCount); - ssbo->Resize(newElemCount); - } + Impl::InitCommon( + ssbo, + MATUNI_SSBO_BINDING_IDX, ELEM_COUNT0, ELEM_COUNTI, + IStreamBufferConcept::Types::SB_BUFFERSUBDATA, true, 1, + className + ); +} - //update on the GPU - const ModelUniformData* clientPtr = modelsUniformsStorage.GetData().data(); - ModelUniformData* mappedPtr = ssbo->Map(clientPtr, 0, storageElemCount); +void ModelUniformsUploader::Kill() +{ + Impl::KillCommon(ssbo, MATUNI_SSBO_BINDING_IDX); +} - if (!ssbo->HasClientPtr()) - memcpy(mappedPtr, clientPtr, storageElemCount * sizeof(ModelUniformData)); +void ModelUniformsUploader::Update() +{ + SCOPED_TIMER("ModelUniformsUploader::Update"); - ssbo->Unmap(); - ssbo->BindBufferRange(bindingIdx); - ssbo->SwapBuffer(); + Impl::UpdateCommon(*this, ssbo, modelUniformsStorage, className, __func__); } -std::size_t ModelsUniformsUploader::GetDefElemOffsetImpl(const S3DModel* model) const +size_t ModelUniformsUploader::GetElemOffset(const UnitDef* def) const { assert(false); LOG_L(L_ERROR, "[%s::%s] Invalid call", className, __func__); - return ModelsUniformsStorage::INVALID_INDEX; + return ModelUniformsStorage::INVALID_INDEX; } -std::size_t ModelsUniformsUploader::GetDefElemOffsetImpl(const UnitDef* def) const +size_t ModelUniformsUploader::GetElemOffset(const FeatureDef* def) const { assert(false); LOG_L(L_ERROR, "[%s::%s] Invalid call", className, __func__); - return ModelsUniformsStorage::INVALID_INDEX; + return ModelUniformsStorage::INVALID_INDEX; } -std::size_t ModelsUniformsUploader::GetDefElemOffsetImpl(const FeatureDef* def) const +size_t ModelUniformsUploader::GetElemOffset(const S3DModel* model) const { assert(false); LOG_L(L_ERROR, "[%s::%s] Invalid call", className, __func__); - return ModelsUniformsStorage::INVALID_INDEX; + return ModelUniformsStorage::INVALID_INDEX; } +size_t ModelUniformsUploader::GetUnitDefElemOffset(int32_t unitDefID) const +{ + return GetElemOffset(unitDefHandler->GetUnitDefByID(unitDefID)); +} -std::size_t ModelsUniformsUploader::GetElemOffsetImpl(const CUnit* unit) const +size_t ModelUniformsUploader::GetFeatureDefElemOffset(int32_t featureDefID) const +{ + return GetElemOffset(featureDefHandler->GetFeatureDefByID(featureDefID)); +} + +size_t ModelUniformsUploader::GetElemOffset(const CUnit* unit) const { if (unit == nullptr) { LOG_L(L_ERROR, "[%s::%s] Supplied nullptr CUnit", className, __func__); - return ModelsUniformsStorage::INVALID_INDEX; + return ModelUniformsStorage::INVALID_INDEX; } - if (std::size_t offset = modelsUniformsStorage.GetObjOffset(unit); offset != std::size_t(-1)) { + if (size_t offset = modelUniformsStorage.GetObjOffset(unit); offset != size_t(-1)) { return offset; } LOG_L(L_ERROR, "[%s::%s] Supplied invalid CUnit (id:%d)", className, __func__, unit->id); - return ModelsUniformsStorage::INVALID_INDEX; + return ModelUniformsStorage::INVALID_INDEX; } -std::size_t ModelsUniformsUploader::GetElemOffsetImpl(const CFeature* feature) const +size_t ModelUniformsUploader::GetElemOffset(const CFeature* feature) const { if (feature == nullptr) { LOG_L(L_ERROR, "[%s::%s] Supplied nullptr CFeature", className, __func__); - return ModelsUniformsStorage::INVALID_INDEX; + return ModelUniformsStorage::INVALID_INDEX; } - if (std::size_t offset = modelsUniformsStorage.GetObjOffset(feature); offset != std::size_t(-1)) { + if (size_t offset = modelUniformsStorage.GetObjOffset(feature); offset != size_t(-1)) { return offset; } LOG_L(L_ERROR, "[%s::%s] Supplied invalid CFeature (id:%d)", className, __func__, feature->id); - return ModelsUniformsStorage::INVALID_INDEX; + return ModelUniformsStorage::INVALID_INDEX; } -std::size_t ModelsUniformsUploader::GetElemOffsetImpl(const CProjectile* p) const +size_t ModelUniformsUploader::GetElemOffset(const CProjectile* p) const { if (p == nullptr) { LOG_L(L_ERROR, "[%s::%s] Supplied nullptr CProjectile", className, __func__); - return ModelsUniformsStorage::INVALID_INDEX; + return ModelUniformsStorage::INVALID_INDEX; } - if (std::size_t offset = modelsUniformsStorage.GetObjOffset(p); offset != std::size_t(-1)) { + if (size_t offset = modelUniformsStorage.GetObjOffset(p); offset != size_t(-1)) { return offset; } LOG_L(L_ERROR, "[%s::%s] Supplied invalid CProjectile (id:%d)", className, __func__, p->id); - return ModelsUniformsStorage::INVALID_INDEX; -} \ No newline at end of file + return ModelUniformsStorage::INVALID_INDEX; +} + +size_t ModelUniformsUploader::GetUnitElemOffset(int32_t unitID) const +{ + return GetElemOffset(unitHandler.GetUnit(unitID)); +} + +size_t ModelUniformsUploader::GetFeatureElemOffset(int32_t featureID) const +{ + return GetElemOffset(featureHandler.GetFeature(featureID)); +} + +size_t ModelUniformsUploader::GetProjectileElemOffset(int32_t syncedProjectileID) const +{ + return GetElemOffset(projectileHandler.GetProjectileBySyncedID(syncedProjectileID)); +} diff --git a/rts/Rendering/ModelsDataUploader.h b/rts/Rendering/ModelsDataUploader.h index 6e335b0045..fa9b53526f 100644 --- a/rts/Rendering/ModelsDataUploader.h +++ b/rts/Rendering/ModelsDataUploader.h @@ -2,15 +2,11 @@ #include #include -#include -#include -#include +#include #include -#include "System/Matrix44f.h" -#include "System/SpringMath.h" +#include "System/Transform.hpp" #include "System/TypeToStr.h" -#include "Rendering/GL/myGL.h" #include "Rendering/GL/StreamBuffer.h" #include "Rendering/Models/ModelsMemStorageDefs.h" @@ -22,88 +18,77 @@ struct UnitDef; struct FeatureDef; struct S3DModel; -template -class TypedStorageBufferUploader { +class TransformsUploader { public: - static Derived& GetInstance() { - static Derived instance; - return instance; - }; + using MyClassName = TransformsUploader; + using MyDataType = Transform; public: - void Init() { static_cast(this)->InitDerived(); } - void Kill() { static_cast(this)->KillDerived(); } - void Update() { static_cast(this)->UpdateDerived(); } + void Init(); + void Kill(); + void Update(); +public: + uint32_t GetElemsCount() const { return ssbo->GetByteSize() / sizeof(MyDataType); } + constexpr uint32_t GetBindingIdx() const { return MATRIX_SSBO_BINDING_IDX; } + constexpr uint32_t GetElemCountIncr() const { return ELEM_COUNTI; } public: // Defs - std::size_t GetElemOffset(const UnitDef* def) const { return GetDefElemOffsetImpl(def); } - std::size_t GetElemOffset(const FeatureDef* def) const { return GetDefElemOffsetImpl(def); } - std::size_t GetElemOffset(const S3DModel* model) const { return GetDefElemOffsetImpl(model); } - std::size_t GetUnitDefElemOffset(int32_t unitDefID) const; - std::size_t GetFeatureDefElemOffset(int32_t featureDefID) const; + size_t GetElemOffset(const UnitDef* def) const; + size_t GetElemOffset(const FeatureDef* def) const; + size_t GetElemOffset(const S3DModel* model) const; + size_t GetUnitDefElemOffset(int32_t unitDefID) const; + size_t GetFeatureDefElemOffset(int32_t featureDefID) const; // Objs - std::size_t GetElemOffset(const CUnit* unit) const { return GetElemOffsetImpl(unit); } - std::size_t GetElemOffset(const CFeature* feature) const { return GetElemOffsetImpl(feature); } - std::size_t GetElemOffset(const CProjectile* proj) const { return GetElemOffsetImpl(proj); } - std::size_t GetUnitElemOffset(int32_t unitID) const; - std::size_t GetFeatureElemOffset(int32_t featureID) const; - std::size_t GetProjectileElemOffset(int32_t syncedProjectileID) const; -protected: - void InitImpl(uint32_t bindingIdx_, uint32_t elemCount0_, uint32_t elemCountIncr_, uint8_t type, bool coherent, uint32_t numBuffers); - void KillImpl(); - - virtual std::size_t GetDefElemOffsetImpl(const S3DModel* model) const = 0; - virtual std::size_t GetDefElemOffsetImpl(const UnitDef* def) const = 0; - virtual std::size_t GetDefElemOffsetImpl(const FeatureDef* def) const = 0; - virtual std::size_t GetElemOffsetImpl(const CUnit* so) const = 0; - virtual std::size_t GetElemOffsetImpl(const CFeature* so) const = 0; - virtual std::size_t GetElemOffsetImpl(const CProjectile* p) const = 0; - - uint32_t GetElemsCount() const; -protected: - uint32_t bindingIdx = -1u; - uint32_t elemCount0 = 0; - uint32_t elemCountIncr = 0; - static constexpr const char* className = spring::TypeToCStr(); - - std::unique_ptr> ssbo; -}; - -class MatrixUploader : public TypedStorageBufferUploader { -public: - void InitDerived(); - void KillDerived(); - void UpdateDerived(); -protected: - std::size_t GetDefElemOffsetImpl(const S3DModel* model) const override; - std::size_t GetDefElemOffsetImpl(const UnitDef* def) const override; - std::size_t GetDefElemOffsetImpl(const FeatureDef* def) const override; - std::size_t GetElemOffsetImpl(const CUnit* so) const override; - std::size_t GetElemOffsetImpl(const CFeature* so) const override; - std::size_t GetElemOffsetImpl(const CProjectile* p) const override; + size_t GetElemOffset(const CUnit* unit) const; + size_t GetElemOffset(const CFeature* feature) const; + size_t GetElemOffset(const CProjectile* proj) const; + size_t GetUnitElemOffset(int32_t unitID) const; + size_t GetFeatureElemOffset(int32_t featureID) const; + size_t GetProjectileElemOffset(int32_t syncedProjectileID) const; +private: + std::unique_ptr> ssbo; private: + static constexpr const char* className = spring::TypeToCStr(); static constexpr uint32_t MATRIX_SSBO_BINDING_IDX = 0; - static constexpr uint32_t ELEM_COUNT0 = 1u << 13; - static constexpr uint32_t ELEM_COUNTI = 1u << 12; + static constexpr uint32_t ELEM_COUNT0 = 1u << 16; + static constexpr uint32_t ELEM_COUNTI = 1u << 14; }; -class ModelsUniformsUploader : public TypedStorageBufferUploader { +class ModelUniformsUploader { public: - void InitDerived(); - void KillDerived(); - void UpdateDerived(); -protected: - virtual std::size_t GetDefElemOffsetImpl(const S3DModel* model) const override; - virtual std::size_t GetDefElemOffsetImpl(const UnitDef* def) const override; - virtual std::size_t GetDefElemOffsetImpl(const FeatureDef* def) const override; - virtual std::size_t GetElemOffsetImpl(const CUnit* so) const override; - virtual std::size_t GetElemOffsetImpl(const CFeature* so) const override; - virtual std::size_t GetElemOffsetImpl(const CProjectile* p) const override; + using MyClassName = ModelUniformsUploader; + using MyDataType = ModelUniformData; +public: + void Init(); + void Kill(); + void Update(); +public: + uint32_t GetElemsCount() const { return ssbo->GetByteSize() / sizeof(MyDataType); } + constexpr uint32_t GetBindingIdx() const { return MATUNI_SSBO_BINDING_IDX; } + constexpr uint32_t GetElemCountIncr() const { return ELEM_COUNTI; } +public: + // Defs + size_t GetElemOffset(const UnitDef* def) const; + size_t GetElemOffset(const FeatureDef* def) const; + size_t GetElemOffset(const S3DModel* model) const; + size_t GetUnitDefElemOffset(int32_t unitDefID) const; + size_t GetFeatureDefElemOffset(int32_t featureDefID) const; + + // Objs + size_t GetElemOffset(const CUnit* unit) const; + size_t GetElemOffset(const CFeature* feature) const; + size_t GetElemOffset(const CProjectile* proj) const; + size_t GetUnitElemOffset(int32_t unitID) const; + size_t GetFeatureElemOffset(int32_t featureID) const; + size_t GetProjectileElemOffset(int32_t syncedProjectileID) const; +private: + std::unique_ptr> ssbo; private: + static constexpr const char* className = spring::TypeToCStr(); static constexpr uint32_t MATUNI_SSBO_BINDING_IDX = 1; static constexpr uint32_t ELEM_COUNT0 = 1u << 12; static constexpr uint32_t ELEM_COUNTI = 1u << 11; }; -#define matrixUploader MatrixUploader::GetInstance() -#define modelsUniformsUploader ModelsUniformsUploader::GetInstance() \ No newline at end of file +extern TransformsUploader transformsUploader; +extern ModelUniformsUploader modelUniformsUploader; \ No newline at end of file diff --git a/rts/Sim/Features/Feature.h b/rts/Sim/Features/Feature.h index 8e49ce5144..2d5912c0d4 100644 --- a/rts/Sim/Features/Feature.h +++ b/rts/Sim/Features/Feature.h @@ -97,7 +97,7 @@ class CFeature: public CSolidObject, public spring::noncopyable // NOTE: // unlike CUnit which recalculates the matrix on each call // (and uses the synced and error args) CFeature caches it - CMatrix44f GetTransformMatrix(bool synced = false, bool fullread = false) const final { return transMatrix[synced]; } + CMatrix44f GetTransformMatrix(bool synced = false, bool fullread = false) const override final { return transMatrix[synced]; } const CMatrix44f& GetTransformMatrixRef(bool synced = false) const { return transMatrix[synced]; } private: diff --git a/rts/Sim/Objects/SolidObject.h b/rts/Sim/Objects/SolidObject.h index fab123caac..f06691d7d9 100644 --- a/rts/Sim/Objects/SolidObject.h +++ b/rts/Sim/Objects/SolidObject.h @@ -190,7 +190,7 @@ class CSolidObject: public CWorldObject { void UpdateDirVectors(const float3& uDir); CMatrix44f ComposeMatrix(const float3& p) const { return (CMatrix44f(p, -rightdir, updir, frontdir)); } - virtual CMatrix44f GetTransformMatrix(bool synced = false, bool fullread = false) const { return CMatrix44f(); }; + virtual CMatrix44f GetTransformMatrix(bool synced = false, bool fullread = false) const = 0; const CollisionVolume* GetCollisionVolume(const LocalModelPiece* lmp) const { if (lmp == nullptr) diff --git a/rts/Sim/Projectiles/ProjectileHandler.cpp b/rts/Sim/Projectiles/ProjectileHandler.cpp index 05fdb4571a..bf79fc1e11 100644 --- a/rts/Sim/Projectiles/ProjectileHandler.cpp +++ b/rts/Sim/Projectiles/ProjectileHandler.cpp @@ -297,6 +297,8 @@ void CProjectileHandler::DestroyProjectile(CProjectile* p) eventHandler.RenderProjectileDestroyed(p); if (p->synced) { + //modelUniformsStorage.DelObject(p); + eventHandler.ProjectileDestroyed(p, p->GetAllyteamID()); projectiles[true].Del(p->id); diff --git a/rts/Sim/Units/Scripts/UnitScript.cpp b/rts/Sim/Units/Scripts/UnitScript.cpp index e39815753f..9130daf69f 100644 --- a/rts/Sim/Units/Scripts/UnitScript.cpp +++ b/rts/Sim/Units/Scripts/UnitScript.cpp @@ -270,6 +270,52 @@ bool CUnitScript::TickAnimFinished(int deltaTime) return (HaveAnimations()); } +bool CUnitScript::TickMoveAnim(int tickRate, LocalModelPiece& lmp, AnimInfo& ai) +{ + float3 pos = lmp.GetPosition(); + + float3 posSpeed = lmp.GetPositionSpeed(); + posSpeed[ai.axis] = ai.speed / tickRate; + + const bool ret = MoveToward(pos[ai.axis], ai.dest, posSpeed[ai.axis]); + + lmp.SetPosition(pos, posSpeed); + + return ret; +} + +bool CUnitScript::TickTurnAnim(int tickRate, LocalModelPiece& lmp, AnimInfo& ai) +{ + float3 rot = lmp.GetRotation(); + + float3 rotSpeed = lmp.GetRotationSpeed(); + rotSpeed[ai.axis] = ai.speed / tickRate; + + rot[ai.axis] = ClampRad(rot[ai.axis]); + + const bool ret = TurnToward(rot[ai.axis], ai.dest, rotSpeed[ai.axis]); + + lmp.SetRotation(rot, rotSpeed); + + return ret; +} + +bool CUnitScript::TickSpinAnim(int tickRate, LocalModelPiece& lmp, AnimInfo& ai) +{ + float3 rot = lmp.GetRotation(); + + rot[ai.axis] = ClampRad(rot[ai.axis]); + const bool ret = DoSpin(rot[ai.axis], ai.dest, ai.speed, ai.accel, tickRate); + + // ai.speed is actually modified by DoSpin(), so do the assignment after the call + float3 rotSpeed = lmp.GetRotationSpeed(); + rotSpeed[ai.axis] = ai.speed / tickRate; + + lmp.SetRotation(rot, rotSpeed); + + return ret; +} + CUnitScript::AnimContainerTypeIt CUnitScript::FindAnim(AnimType type, int piece, int axis) { RECOIL_DETAILED_TRACY_ZONE; diff --git a/rts/Sim/Units/Scripts/UnitScript.h b/rts/Sim/Units/Scripts/UnitScript.h index f94d7d84f2..4e95dafccf 100644 --- a/rts/Sim/Units/Scripts/UnitScript.h +++ b/rts/Sim/Units/Scripts/UnitScript.h @@ -92,8 +92,9 @@ class CUnitScript return (p->PieceFunc()); \ } - SCRIPT_TO_LOCALPIECE_FUNC( float3, GetPiecePos, GetAbsolutePos ) - SCRIPT_TO_LOCALPIECE_FUNC(CMatrix44f, GetPieceMatrix, GetModelSpaceMatrix) + SCRIPT_TO_LOCALPIECE_FUNC( float3, GetPiecePos , GetAbsolutePos ) + SCRIPT_TO_LOCALPIECE_FUNC( Transform, GetPieceTransform, GetModelSpaceTransform) + SCRIPT_TO_LOCALPIECE_FUNC(CMatrix44f, GetPieceMatrix , GetModelSpaceMatrix ) bool GetEmitDirPos(int scriptPieceNum, float3& pos, float3& dir) const { if (!PieceExists(scriptPieceNum)) @@ -116,9 +117,9 @@ class CUnitScript void TickAllAnims(int tickRate); bool TickAnimFinished(int tickRate); // note: must copy-and-set here (LMP dirty flag, etc) - bool TickMoveAnim(int tickRate, LocalModelPiece& lmp, AnimInfo& ai) { float3 pos = lmp.GetPosition(); const bool ret = MoveToward(pos[ai.axis], ai.dest, ai.speed / tickRate); lmp.SetPosition(pos); return ret; } - bool TickTurnAnim(int tickRate, LocalModelPiece& lmp, AnimInfo& ai) { float3 rot = lmp.GetRotation(); rot[ai.axis] = ClampRad(rot[ai.axis]); const bool ret = TurnToward(rot[ai.axis], ai.dest, ai.speed / tickRate ); lmp.SetRotation(rot); return ret; } - bool TickSpinAnim(int tickRate, LocalModelPiece& lmp, AnimInfo& ai) { float3 rot = lmp.GetRotation(); rot[ai.axis] = ClampRad(rot[ai.axis]); const bool ret = DoSpin(rot[ai.axis], ai.dest, ai.speed, ai.accel, tickRate); lmp.SetRotation(rot); return ret; } + bool TickMoveAnim(int tickRate, LocalModelPiece& lmp, AnimInfo& ai); + bool TickTurnAnim(int tickRate, LocalModelPiece& lmp, AnimInfo& ai); + bool TickSpinAnim(int tickRate, LocalModelPiece& lmp, AnimInfo& ai); void TickAnims(int tickRate, const TickAnimFunc& tickAnimFunc, AnimContainerType& liveAnims, AnimContainerType& doneAnims); // animation, used by CCobThread diff --git a/rts/Sim/Units/Unit.cpp b/rts/Sim/Units/Unit.cpp index 6642a5aa28..5d87f3d4ae 100644 --- a/rts/Sim/Units/Unit.cpp +++ b/rts/Sim/Units/Unit.cpp @@ -724,7 +724,7 @@ void CUnit::UpdateTransportees() // slave transportee orientation to piece if (tu.piece >= 0) { const CMatrix44f& transMat = GetTransformMatrix(true); - const CMatrix44f& pieceMat = script->GetPieceMatrix(tu.piece); + const auto pieceMat = script->GetPieceMatrix(tu.piece); transportee->SetDirVectors(transMat * pieceMat); } diff --git a/rts/Sim/Units/Unit.h b/rts/Sim/Units/Unit.h index 73f7d7c42f..ac3ed85075 100644 --- a/rts/Sim/Units/Unit.h +++ b/rts/Sim/Units/Unit.h @@ -112,7 +112,7 @@ class CUnit : public CSolidObject void EnableScriptMoveType(); void DisableScriptMoveType(); - CMatrix44f GetTransformMatrix(bool synced = false, bool fullread = false) const final; + CMatrix44f GetTransformMatrix(bool synced = false, bool fullread = false) const override final; void DependentDied(CObject* o); diff --git a/rts/Sim/Units/UnitTypes/Factory.cpp b/rts/Sim/Units/UnitTypes/Factory.cpp index 4f04f6cfe4..ed04294f44 100644 --- a/rts/Sim/Units/UnitTypes/Factory.cpp +++ b/rts/Sim/Units/UnitTypes/Factory.cpp @@ -219,11 +219,12 @@ void CFactory::UpdateBuild(CUnit* buildee) { const int buildPiece = script->QueryBuildInfo(); const float3& buildPos = CalcBuildPos(buildPiece); - const CMatrix44f& buildPieceMat = script->GetPieceMatrix(buildPiece); + const auto& buildPieceTra = script->GetPieceTransform(buildPiece); // see CMatrix44f::CMatrix44f(const float3 pos, const float3 x, const float3 y, const float3 z) // frontdir.x, frontdir.z - const int buildPieceHeading = GetHeadingFromVector(buildPieceMat[8], buildPieceMat[10]); + const float3 xzVec = buildPieceTra * float3{ math::HALFSQRT2, 0.0f, math::HALFSQRT2 }; + const int buildPieceHeading = GetHeadingFromVector(xzVec.x, xzVec.z); const int buildFaceHeading = GetHeadingFromFacing(buildFacing); float3 buildeePos = buildPos; diff --git a/rts/Sim/Weapons/BeamLaser.cpp b/rts/Sim/Weapons/BeamLaser.cpp index 637b955e13..8f91056340 100644 --- a/rts/Sim/Weapons/BeamLaser.cpp +++ b/rts/Sim/Weapons/BeamLaser.cpp @@ -120,7 +120,7 @@ void CBeamLaser::UpdatePosAndMuzzlePos() RECOIL_DETAILED_TRACY_ZONE; if (sweepFireState.IsSweepFiring()) { const int weaponPiece = owner->script->QueryWeapon(weaponNum); - const CMatrix44f weaponMat = owner->script->GetPieceMatrix(weaponPiece); + const auto weaponMat = owner->script->GetPieceMatrix(weaponPiece); const float3 relWeaponPos = weaponMat.GetPos(); const float3 newWeaponDir = owner->GetObjectSpaceVec(float3(weaponMat[2], weaponMat[6], weaponMat[10])); diff --git a/rts/System/FreeListMap.h b/rts/System/FreeListMap.h index 548f20e60e..9aedf62e7e 100644 --- a/rts/System/FreeListMap.h +++ b/rts/System/FreeListMap.h @@ -53,6 +53,7 @@ namespace spring { values[id] = TVal{}; } + bool empty() const { return values.empty(); } void resize(std::size_t sz) { values.resize(sz); } void reserve(std::size_t sz) { values.reserve(sz); } void clear() { @@ -194,6 +195,7 @@ namespace spring { return it->second; } + bool empty() const { return vault.empty(); } void reserve(std::size_t sz) { vault.reserve(sz); } void clear() { vault.clear(); diff --git a/rts/System/Matrix44f.cpp b/rts/System/Matrix44f.cpp index deb3e998b0..93fa2f0442 100644 --- a/rts/System/Matrix44f.cpp +++ b/rts/System/Matrix44f.cpp @@ -1,6 +1,7 @@ /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */ #include "System/Matrix44f.h" +#include "System/Quaternion.h" #include "System/SpringMath.h" #ifndef UNIT_TEST #include "Rendering/GlobalRendering.h" diff --git a/rts/System/MemPoolTypes.h b/rts/System/MemPoolTypes.h index c1c295e84c..29d58414cc 100644 --- a/rts/System/MemPoolTypes.h +++ b/rts/System/MemPoolTypes.h @@ -376,6 +376,8 @@ using StaticMemPoolT = StaticMemPool), alignof(TypesMem // has gaps management template class StablePosAllocator { +public: + using Type = T; public: static constexpr bool reportWork = false; template diff --git a/rts/System/Quaternion.cpp b/rts/System/Quaternion.cpp index f306b1215f..0a5cc594ed 100644 --- a/rts/System/Quaternion.cpp +++ b/rts/System/Quaternion.cpp @@ -466,4 +466,4 @@ CQuaternion CQuaternion::SLerp(const CQuaternion& q1, const CQuaternion& q2_, co (s1 * q1.r + s2 * q2.r) * invsin )); } -} \ No newline at end of file +} diff --git a/rts/System/SpringApp.cpp b/rts/System/SpringApp.cpp index 62ccd78a5b..514d05b858 100644 --- a/rts/System/SpringApp.cpp +++ b/rts/System/SpringApp.cpp @@ -819,7 +819,8 @@ void SpringApp::Reload(const std::string script) LOG("[SpringApp::%s][10]", __func__); - matricesMemStorage.Reset(); + transformsMemStorage.Reset(); + modelUniformsStorage.Reset(); gu->ResetState(); ENTER_SYNCED_CODE(); diff --git a/rts/System/Sync/DumpState.cpp b/rts/System/Sync/DumpState.cpp index 153548751b..76376bb3ca 100644 --- a/rts/System/Sync/DumpState.cpp +++ b/rts/System/Sync/DumpState.cpp @@ -287,12 +287,16 @@ void DumpState(int newMinFrameNum, int newMaxFrameNum, int newFramePeriod, std:: file << "\t\t\tname: " << p->name << "\n"; file << "\t\t\tchildrenNum: " << p->children.size() << "\n"; file << "\t\t\tparentName: " << (p->parent ? p->parent->name : "(NULL)") << "\n"; - file << "\t\t\thasBakedMat: " << p->HasBackedMat() << "\n"; - file << "\t\t\tbposeMatrix: " << TapFloats(p->bposeMatrix); - file << "\t\t\tbakedMatrix: " << TapFloats(p->bakedMatrix); + file << "\t\t\thasBakedMat: " << p->HasBackedTra() << "\n"; + file << "\t\t\tbposeTransform(t): " << TapFloats(p->bposeTransform.t); + file << "\t\t\tbposeTransform(r): " << TapFloats(float4{ p->bposeTransform.r.x, p->bposeTransform.r.y, p->bposeTransform.r.z, p->bposeTransform.r.r }); + file << "\t\t\tbposeTransform(s): " << TapFloats(p->bposeTransform.s); + file << "\t\t\tbakedTransform(t): " << TapFloats(p->bakedTransform.t); + file << "\t\t\tbakedTransform(r): " << TapFloats(float4{ p->bakedTransform.r.x, p->bakedTransform.r.y, p->bakedTransform.r.z, p->bakedTransform.r.r }); + file << "\t\t\tbakedTransform(s): " << TapFloats(p->bakedTransform.s); file << "\t\t\toffset: " << TapFloats(p->offset); file << "\t\t\tgoffset: " << TapFloats(p->goffset); - file << "\t\t\tscales: " << TapFloats(p->scales); + file << "\t\t\tscales: " << TapFloats(p->scale); file << "\t\t\tscales: " << TapFloats(p->mins); file << "\t\t\tscales: " << TapFloats(p->maxs);