diff --git a/cont/base/springcontent/shaders/GLSL/ModelVertProgGL4.glsl b/cont/base/springcontent/shaders/GLSL/ModelVertProgGL4.glsl index f3212610ef..defd8fa89f 100644 --- a/cont/base/springcontent/shaders/GLSL/ModelVertProgGL4.glsl +++ b/cont/base/springcontent/shaders/GLSL/ModelVertProgGL4.glsl @@ -83,8 +83,13 @@ layout(std140, binding = 1) uniform UniformParamsBuffer { vec4 teamColor[255]; //all team colors }; -layout(std140, binding = 0) readonly buffer MatrixBuffer { - mat4 mat[]; +struct Transform { + vec4 quat; + vec4 trSc; +}; + +layout(std140, binding = 0) readonly buffer TransformBuffer { + Transform transforms[]; }; uniform int cameraMode = 0; @@ -114,7 +119,7 @@ out Data { }; out float gl_ClipDistance[3]; -#line 1117 +#line 1122 void TransformPlayerCam(vec4 worldPos) { gl_Position = cameraViewProj * worldPos; @@ -132,6 +137,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); @@ -146,13 +182,12 @@ void GetModelSpaceVertex(out vec4 msPosition, out vec3 msNormal) ); uint b0 = GetUnpackedValue(bonesInfo.x, 0); //first boneID - mat4 b0BoneMat = mat[instData.x + b0 + uint(!staticModel)]; - mat3 b0NormMat = mat3(b0BoneMat); + Transform b0BoneTra = transforms[instData.x + b0 + uint(!staticModel)]; - weights[0] *= b0BoneMat[3][3]; + weights[0] *= step(0.0, b0BoneTra.trSc.w); - msPosition = b0BoneMat * piecePos; - msNormal = b0NormMat * normal; + msPosition = ApplyTransform(b0BoneTra, piecePos); + msNormal = RotateByQuaternion(b0BoneTra.quat, normal); if (staticModel || weights[0] == 1.0) return; @@ -164,7 +199,7 @@ void GetModelSpaceVertex(out vec4 msPosition, out vec3 msNormal) wSum += weights[0]; uint numPieces = GetUnpackedValue(instData.z, 3); - mat4 bposeMat = mat[instData.w + b0]; + Transform bposeTra = transforms[instData.w + b0]; // Vertex[ModelSpace,BoneX] = PieceMat[BoneX] * InverseBindPosMat[BoneX] * BindPosMat[Bone0] * Vertex[Bone0] for (uint bi = 1; bi < 3; ++bi) { @@ -173,16 +208,15 @@ void GetModelSpaceVertex(out vec4 msPosition, out vec3 msNormal) if (bID == 0xFFu || weights[bi] == 0.0) continue; - mat4 bposeInvMat = mat[instData.w + numPieces + bID]; - mat4 boneMat = mat[instData.x + 1u + bID]; + Transform bposeInvTra = transforms[instData.w + numPieces + bID]; + Transform boneTra = transforms[instData.x + 1u + bID]; - weights[bi] *= boneMat[3][3]; + weights[bi] *= step(0.0, boneTra.trSc.w); - mat4 skinMat = boneMat * bposeInvMat * bposeMat; - mat3 normMat = mat3(skinMat); + Transform skinTra = ApplyTransform(ApplyTransform(boneTra, bposeInvTra), bposeTra); - msPosition += skinMat * piecePos * weights[bi]; - msNormal += normMat * normal * weights[bi]; + msPosition += ApplyTransform(skinTra, piecePos * weights[bi]); + msNormal += RotateByQuaternion(skinTra.quat, normal * weights[bi]); wSum += weights[bi]; } @@ -194,14 +228,15 @@ void main(void) { bool staticModel = (matrixMode > 0); - mat4 worldMatrix = staticModel ? staticModelMatrix : mat[instData.x]; //don't cover ARRAY_MATMODE yet + //mat4 worldMatrix = staticModel ? staticModelMatrix : mat[instData.x]; //don't cover ARRAY_MATMODE yet + Transform worldTra = transforms[instData.x]; //don't cover ARRAY_MATMODE yet vec4 modelPos; vec3 modelNormal; GetModelSpaceVertex(modelPos, modelNormal); - worldPos = worldMatrix * modelPos; - worldNormal = mat3(worldMatrix) * modelNormal; + worldPos = ApplyTransform(worldTra, modelPos); + worldNormal = RotateByQuaternion(worldTra.quat, modelNormal); gl_ClipDistance[0] = dot(modelPos, clipPlane0); //upper construction clip plane gl_ClipDistance[1] = dot(modelPos, clipPlane1); //lower construction clip plane diff --git a/rts/Game/Game.cpp b/rts/Game/Game.cpp index 25ffce47c3..5762646b0e 100644 --- a/rts/Game/Game.cpp +++ b/rts/Game/Game.cpp @@ -736,8 +736,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(); } @@ -997,8 +1000,9 @@ void CGame::KillRendering() icon::iconHandler.Kill(); spring::SafeDelete(geometricObjects); worldDrawer.Kill(); - matrixUploader.Kill(); - modelsUniformsUploader.Kill(); + + transformsUploader.Kill(); + modelUniformsUploader.Kill(); } void CGame::KillInterface() @@ -1041,6 +1045,9 @@ void CGame::KillSimulation() unitHandler.Kill(); projectileHandler.Kill(); + modelUniformsStorage.Kill(); + //transformsMemStorage.Kill(); //Add? + LOG("[Game::%s][3]", __func__); IPathManager::FreeInstance(pathManager); IMapDamage::FreeMapDamage(mapDamage); @@ -1470,8 +1477,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/LuaOpenGL.cpp b/rts/Lua/LuaOpenGL.cpp index b5dd9c979c..154b0b5722 100644 --- a/rts/Lua/LuaOpenGL.cpp +++ b/rts/Lua/LuaOpenGL.cpp @@ -1423,7 +1423,7 @@ static void GLObjectPieceMultMatrix(lua_State* L, const CSolidObject* obj) if (lmp == nullptr) return; - glMultMatrixf(lmp->GetModelSpaceMatrix()); + glMultMatrixf(lmp->GetModelSpaceTransform().ToMatrix()); } static bool GLObjectDrawWithLuaMat(lua_State* L, CSolidObject* obj, LuaObjType objType) diff --git a/rts/Lua/LuaShaders.cpp b/rts/Lua/LuaShaders.cpp index b0b17a5d7b..0f530c0a3f 100644 --- a/rts/Lua/LuaShaders.cpp +++ b/rts/Lua/LuaShaders.cpp @@ -960,7 +960,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/LuaSyncedRead.cpp b/rts/Lua/LuaSyncedRead.cpp index d6164188d6..0ca480c826 100644 --- a/rts/Lua/LuaSyncedRead.cpp +++ b/rts/Lua/LuaSyncedRead.cpp @@ -8245,7 +8245,7 @@ static int GetSolidObjectPieceMatrix(lua_State* L, const CSolidObject* o) if (lmp == nullptr) return 0; - const CMatrix44f& mat = lmp->GetModelSpaceMatrix(); + const CMatrix44f& mat = lmp->GetModelSpaceTransform().ToMatrix(); for (float mi: mat.m) { lua_pushnumber(L, mi); diff --git a/rts/Lua/LuaVBOImpl.cpp b/rts/Lua/LuaVBOImpl.cpp index 8f13e3148c..6d3181b6bd 100644 --- a/rts/Lua/LuaVBOImpl.cpp +++ b/rts/Lua/LuaVBOImpl.cpp @@ -986,8 +986,8 @@ 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 matOffset = 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) { LuaUtils::SolLuaError("[LuaVBOImpl::%s] Invalid data supplied. See infolog for details", __func__); @@ -1003,11 +1003,11 @@ 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); diff --git a/rts/Rendering/CMakeLists.txt b/rts/Rendering/CMakeLists.txt index de01315443..0407151df4 100644 --- a/rts/Rendering/CMakeLists.txt +++ b/rts/Rendering/CMakeLists.txt @@ -123,6 +123,7 @@ set(sources_engine_Rendering "${CMAKE_CURRENT_SOURCE_DIR}/Common/ModelDrawerData.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/Common/ModelDrawerState.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/Common/ModelDrawerHelpers.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/Common/UpdateList.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/Features/FeatureDrawerData.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/Features/FeatureDrawer.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/Units/UnitDrawerData.cpp" diff --git a/rts/Rendering/Common/ModelDrawer.h b/rts/Rendering/Common/ModelDrawer.h index ef4c036341..77a2234dcc 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..59b4eb7758 100644 --- a/rts/Rendering/Common/ModelDrawerData.h +++ b/rts/Rendering/Common/ModelDrawerData.h @@ -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); } } @@ -157,15 +157,13 @@ inline void CModelDrawerDataBase::UpdateObject(const T* co, bool init) template inline void CModelDrawerDataBase::UpdateObjectSMMA(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; diff --git a/rts/Rendering/Common/UpdateList.cpp b/rts/Rendering/Common/UpdateList.cpp new file mode 100644 index 0000000000..df3c72bc0e --- /dev/null +++ b/rts/Rendering/Common/UpdateList.cpp @@ -0,0 +1,101 @@ +#include "UpdateList.h" +#include "System/Misc/TracyDefs.h" + +#include + +CR_BIND(UpdateList, ) +CR_REG_METADATA(UpdateList, ( + CR_MEMBER(updateList), + CR_MEMBER(changed) +)) + + +void UpdateList::SetNeedUpdateAll() +{ + RECOIL_DETAILED_TRACY_ZONE; + std::fill(updateList.begin(), updateList.end(), true); + changed = true; +} + +void UpdateList::ResetNeedUpdateAll() +{ + RECOIL_DETAILED_TRACY_ZONE; + std::fill(updateList.begin(), updateList.end(), false); + changed = false; +} + +void UpdateList::Trim(size_t newLessThanOrEqualSize) +{ + RECOIL_DETAILED_TRACY_ZONE; + assert(newLessThanOrEqualSize <= updateList.size()); + updateList.resize(newLessThanOrEqualSize); + // no need to modify the update status +} + +void UpdateList::SetUpdate(size_t first, size_t count) +{ + RECOIL_DETAILED_TRACY_ZONE; + + auto beg = updateList.begin() + first; + auto end = beg + count; + + SetUpdate(UpdateList::IteratorPair(beg, end)); +} + +void UpdateList::SetUpdate(const UpdateList::IteratorPair& it) +{ + RECOIL_DETAILED_TRACY_ZONE; + std::fill(it.first, it.second, true); + changed = true; +} + +void UpdateList::SetUpdate(size_t offset) +{ + RECOIL_DETAILED_TRACY_ZONE; + assert(offset < updateList.size()); + updateList[offset] = true; + changed = true; +} + +void UpdateList::EmplaceBackUpdate() +{ + RECOIL_DETAILED_TRACY_ZONE; + updateList.emplace_back(true); + changed = true; +} + +void UpdateList::PopBack() +{ + updateList.pop_back(); + changed = true; +} + +void UpdateList::PopBack(size_t N) +{ + while (N-- >= 0) + updateList.pop_back(); + + changed = true; +} + +std::optional UpdateList::GetNext(const std::optional& prev) const +{ + RECOIL_DETAILED_TRACY_ZONE; + auto beg = prev.has_value() ? prev.value().second : updateList.begin(); + beg = std::find(beg, updateList.end(), true); + auto end = std::find(beg, updateList.end(), false); + + if (beg == end) + return std::nullopt; + + return std::make_optional(std::make_pair(beg, end)); +} + +std::pair UpdateList::GetOffsetAndSize(const UpdateList::ConstIteratorPair& it) const +{ + RECOIL_DETAILED_TRACY_ZONE; + return std::make_pair( + std::distance(updateList.begin(), it.first ), + std::distance(it.first , it.second) + ); +} diff --git a/rts/Rendering/Common/UpdateList.h b/rts/Rendering/Common/UpdateList.h new file mode 100644 index 0000000000..8c4789a696 --- /dev/null +++ b/rts/Rendering/Common/UpdateList.h @@ -0,0 +1,50 @@ +#pragma once + +#include +#include +#include +#include "System/creg/creg_cond.h" + +class UpdateList { + CR_DECLARE_STRUCT(UpdateList) +public: + using ConstIteratorPair = std::pair::const_iterator, std::vector::const_iterator>; + using IteratorPair = std::pair::iterator, std::vector::iterator>; +public: + UpdateList() + : updateList() + , changed(false) + {} + UpdateList(size_t initialSize) + : updateList(initialSize) + , changed(initialSize > 0) + {} + + size_t Size() const { return updateList.size(); } + size_t Capacity() const { return updateList.capacity(); } + bool Empty() const { return updateList.empty(); } + + void Trim(size_t newLessThanOrEqualSize); + void Resize(size_t newSize) { updateList.resize(newSize); SetNeedUpdateAll(); } + void Reserve(size_t reservedSize) { updateList.reserve(reservedSize); } + void Clear() { *this = std::move(UpdateList()); } + + void SetUpdate(size_t first, size_t count); + void SetUpdate(const IteratorPair& it); + void SetUpdate(size_t offset); + + void SetNeedUpdateAll(); + void ResetNeedUpdateAll(); + + void EmplaceBackUpdate(); + void PopBack(); + void PopBack(size_t N); + + bool NeedUpdate() const { return changed; } + + std::optional GetNext(const std::optional& prev = std::nullopt) const; + std::pair GetOffsetAndSize(const ConstIteratorPair& it) const; +private: + std::vector updateList; + bool changed; +}; \ No newline at end of file diff --git a/rts/Rendering/DebugColVolDrawer.cpp b/rts/Rendering/DebugColVolDrawer.cpp index b4d62529b1..e15da0b5e2 100644 --- a/rts/Rendering/DebugColVolDrawer.cpp +++ b/rts/Rendering/DebugColVolDrawer.cpp @@ -96,7 +96,7 @@ static void DrawObjectDebugPieces(const CSolidObject* o, const float4& defColor) float4{ (1.0f - (hitDeltaTime / 150.0f)), 0.0f, 0.0f, 1.0f } : defColor; - const CMatrix44f mp = mo * lmp->GetModelSpaceMatrix(); + const CMatrix44f mp = mo * lmp->GetModelSpaceTransform().ToMatrix(); // factors in the volume offsets DrawCollisionVolume(lmpVol, mp, curColor); } diff --git a/rts/Rendering/Env/Decals/GroundDecalHandler.cpp b/rts/Rendering/Env/Decals/GroundDecalHandler.cpp index d0397fafca..27329e8cb1 100644 --- a/rts/Rendering/Env/Decals/GroundDecalHandler.cpp +++ b/rts/Rendering/Env/Decals/GroundDecalHandler.cpp @@ -69,13 +69,6 @@ CR_REG_METADATA_SUB(CGroundDecalHandler, UnitMinMaxHeight, CR_MEMBER(max) )) -CR_BIND(CGroundDecalHandler::DecalUpdateList, ) -CR_REG_METADATA_SUB(CGroundDecalHandler, DecalUpdateList, -( - CR_MEMBER(updateList), - CR_MEMBER(changed) -)) - CR_BIND_DERIVED(CGroundDecalHandler, IGroundDecalDrawer, ) CR_REG_METADATA(CGroundDecalHandler, ( CR_MEMBER_UN(maxUniqueScars), @@ -1673,64 +1666,4 @@ void CGroundDecalHandler::FeatureMoved(const CFeature* feature, const float3& ol void CGroundDecalHandler::UnitLoaded(const CUnit* unit, const CUnit* transport) { ForceRemoveSolidObject(unit); } void CGroundDecalHandler::UnitUnloaded(const CUnit* unit, const CUnit* transport) { AddSolidObject(unit); } -void CGroundDecalHandler::UnitMoved(const CUnit* unit) { AddTrack(unit, unit->pos); } - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void CGroundDecalHandler::DecalUpdateList::SetNeedUpdateAll() -{ - RECOIL_DETAILED_TRACY_ZONE; - std::fill(updateList.begin(), updateList.end(), true); - changed = true; -} - -void CGroundDecalHandler::DecalUpdateList::ResetNeedUpdateAll() -{ - RECOIL_DETAILED_TRACY_ZONE; - std::fill(updateList.begin(), updateList.end(), false); - changed = false; -} - -void CGroundDecalHandler::DecalUpdateList::SetUpdate(const CGroundDecalHandler::DecalUpdateList::IteratorPair& it) -{ - RECOIL_DETAILED_TRACY_ZONE; - std::fill(it.first, it.second, true); - changed = true; -} - -void CGroundDecalHandler::DecalUpdateList::SetUpdate(size_t offset) -{ - RECOIL_DETAILED_TRACY_ZONE; - assert(offset < updateList.size()); - updateList[offset] = true; - changed = true; -} - -void CGroundDecalHandler::DecalUpdateList::EmplaceBackUpdate() -{ - RECOIL_DETAILED_TRACY_ZONE; - updateList.emplace_back(true); - changed = true; -} - -std::optional CGroundDecalHandler::DecalUpdateList::GetNext(const std::optional& prev) -{ - RECOIL_DETAILED_TRACY_ZONE; - auto beg = prev.has_value() ? prev.value().second : updateList.begin(); - beg = std::find(beg, updateList.end(), true); - auto end = std::find(beg, updateList.end(), false); - - if (beg == end) - return std::nullopt; - - return std::make_optional(std::make_pair(beg, end)); -} - -std::pair CGroundDecalHandler::DecalUpdateList::GetOffsetAndSize(const CGroundDecalHandler::DecalUpdateList::IteratorPair& it) -{ - RECOIL_DETAILED_TRACY_ZONE; - return std::make_pair( - std::distance(updateList.begin(), it.first ), - std::distance(it.first , it.second) - ); -} +void CGroundDecalHandler::UnitMoved(const CUnit* unit) { AddTrack(unit, unit->pos); } \ No newline at end of file diff --git a/rts/Rendering/Env/Decals/GroundDecalHandler.h b/rts/Rendering/Env/Decals/GroundDecalHandler.h index f0866479dc..d23d44a554 100644 --- a/rts/Rendering/Env/Decals/GroundDecalHandler.h +++ b/rts/Rendering/Env/Decals/GroundDecalHandler.h @@ -14,6 +14,7 @@ #include "Rendering/GL/VAO.h" #include "Rendering/DepthBufferCopy.h" #include "Rendering/Textures/TextureRenderAtlas.h" +#include "Rendering/Common/UpdateList.h" #include "System/EventClient.h" #include "System/UnorderedMap.hpp" #include "System/creg/creg.h" @@ -36,37 +37,6 @@ class CGroundDecalHandler: public IGroundDecalDrawer, public CEventClient, publi { CR_DECLARE_DERIVED(CGroundDecalHandler) CR_DECLARE_SUB(UnitMinMaxHeight) - CR_DECLARE_SUB(DecalUpdateList) -public: - class DecalUpdateList { - CR_DECLARE_STRUCT(DecalUpdateList) - public: - using IteratorPair = std::pair::iterator, std::vector::iterator>; - public: - DecalUpdateList() - : updateList() - , changed(true) - {} - - void Resize(size_t newSize) { updateList.resize(newSize); SetNeedUpdateAll(); } - void Reserve(size_t reservedSize) { updateList.reserve(reservedSize); } - - void SetUpdate(const IteratorPair& it); - void SetUpdate(size_t offset); - - void SetNeedUpdateAll(); - void ResetNeedUpdateAll(); - - void EmplaceBackUpdate(); - - bool NeedUpdate() const { return changed; } - - std::optional GetNext(const std::optional& prev = std::nullopt); - std::pair GetOffsetAndSize(const IteratorPair& it); - private: - std::vector updateList; - bool changed; - }; public: CGroundDecalHandler(); ~CGroundDecalHandler() override; @@ -195,7 +165,7 @@ class CGroundDecalHandler: public IGroundDecalDrawer, public CEventClient, publi spring::unordered_map idToPos; spring::unordered_map>> idToCmInfo; - DecalUpdateList decalsUpdateList; + UpdateList decalsUpdateList; uint32_t nextId; std::vector freeIds; diff --git a/rts/Rendering/Models/3DModel.cpp b/rts/Rendering/Models/3DModel.cpp index 7584c8bc83..f076cdb2c6 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! )) @@ -78,7 +80,7 @@ void S3DModelPiece::DrawStaticLegacy(bool bind, bool bindPosMat) const if (bindPosMat) { glPushMatrix(); - glMultMatrixf(bposeMatrix); + glMultMatrixf(bposeTransform.ToMatrix()); DrawElements(); glPopMatrix(); } @@ -242,6 +244,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) { @@ -330,7 +404,7 @@ void LocalModel::SetModel(const S3DModel* model, bool initialize) pieces[n].original = omp; } - pieces[0].UpdateChildMatricesRec(true); + pieces[0].UpdateChildTransformRec(true); UpdateBoundingVolume(); return; } @@ -345,7 +419,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); @@ -387,7 +461,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 @@ -411,7 +485,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); @@ -447,7 +521,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()); } @@ -471,7 +545,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; @@ -482,29 +556,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); } } @@ -516,11 +632,21 @@ 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) { + // 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); - if (parent != nullptr) - modelSpaceMat >>= parent->modelSpaceMat; + assert(modelSpaceTra.equals(modelSpaceTra2)); + } } @@ -536,7 +662,7 @@ void LocalModelPiece::Draw() const assert(original); glPushMatrix(); - glMultMatrixf(GetModelSpaceMatrix()); + glMultMatrixf(GetModelSpaceTransform().ToMatrix()); S3DModelHelpers::BindLegacyAttrVBOs(); original->DrawElements(); S3DModelHelpers::UnbindLegacyAttrVBOs(); @@ -553,7 +679,7 @@ void LocalModelPiece::DrawLOD(uint32_t lod) const return; glPushMatrix(); - glMultMatrixf(GetModelSpaceMatrix()); + glMultMatrixf(GetModelSpaceTransform().ToMatrix()); if (const auto ldl = lodDispLists[lod]; ldl == 0) { S3DModelHelpers::BindLegacyAttrVBOs(); original->DrawElements(); @@ -585,8 +711,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; } @@ -629,3 +755,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 a72d82db12..5172ef48e2 100644 --- a/rts/Rendering/Models/3DModel.h +++ b/rts/Rendering/Models/3DModel.h @@ -13,6 +13,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" @@ -146,13 +147,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; @@ -161,7 +162,7 @@ struct S3DModelPiece { indxStart = ~0u; indxCount = ~0u; - hasBakedMat = false; + hasBakedTra = false; } virtual float3 GetEmitPos() const; @@ -177,7 +178,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; @@ -185,37 +186,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; } @@ -240,13 +217,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; @@ -261,7 +238,7 @@ struct S3DModelPiece { S3DModel* model; - bool hasBakedMat; + bool hasBakedTra; public: friend class CAssParser; }; @@ -294,7 +271,7 @@ struct S3DModel , loadStatus(NOTLOADED) , uploaded(false) - , matAlloc(ScopedMatricesMemAlloc()) + , traAlloc(ScopedTransformMemAlloc()) {} S3DModel(const S3DModel& m) = delete; @@ -330,7 +307,7 @@ struct S3DModel loadStatus = m.loadStatus; uploaded = m.uploaded; - std::swap(matAlloc, m.matAlloc); + std::swap(traAlloc, m.traAlloc); return *this; } @@ -355,22 +332,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); @@ -380,7 +342,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 }; @@ -406,7 +368,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; @@ -433,7 +395,7 @@ struct S3DModel LoadStatus loadStatus; bool uploaded; private: - ScopedMatricesMemAlloc matAlloc; + ScopedTransformMemAlloc traAlloc; }; @@ -469,31 +431,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 @@ -508,10 +471,12 @@ 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; } const CollisionVolume* GetCollisionVolume() const { return &colvol; } CollisionVolume* GetCollisionVolume() { return &colvol; } @@ -519,12 +484,14 @@ 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 CollisionVolume colvol; @@ -571,7 +538,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 757379b6c3..ef5b8caebc 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 uint8_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 uint8_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); diff --git a/rts/Rendering/Models/AssParser.cpp b/rts/Rendering/Models/AssParser.cpp index d56f56dee7..6e8c0f9ea4 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; @@ -1043,7 +1050,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; @@ -1118,7 +1125,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; @@ -1284,7 +1291,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); @@ -1401,3 +1409,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 8d10f13ce0..048ad9d27d 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 a33666d5d9..2558fe7104 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(alignof(ModelUniformData) == 16, ""); static_assert(sizeof(ModelUniformData::userDefined) % 4 == 0, ""); //due to GLSL std140 userDefined must be a mutiple of 4 \ No newline at end of file diff --git a/rts/Rendering/ModelsDataUploader.cpp b/rts/Rendering/ModelsDataUploader.cpp index e2992663e6..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 potatoe 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/Misc/CollisionHandler.cpp b/rts/Sim/Misc/CollisionHandler.cpp index fa36406a0c..020121e7e1 100644 --- a/rts/Sim/Misc/CollisionHandler.cpp +++ b/rts/Sim/Misc/CollisionHandler.cpp @@ -279,7 +279,7 @@ bool CCollisionHandler::IntersectPiecesHelper( if (!lmp->GetScriptVisible() || lmpVol->IgnoreHits()) continue; - volMat = m * lmp->GetModelSpaceMatrix(); + volMat = m * lmp->GetModelSpaceTransform().ToMatrix(); volMat.Translate(lmpVol->GetOffsets()); CollisionQuery cqn; diff --git a/rts/Sim/Misc/CollisionVolume.cpp b/rts/Sim/Misc/CollisionVolume.cpp index 491a262e6e..9ff48e0842 100644 --- a/rts/Sim/Misc/CollisionVolume.cpp +++ b/rts/Sim/Misc/CollisionVolume.cpp @@ -262,7 +262,7 @@ float CollisionVolume::GetPointSurfaceDistance( assert(this == lmp->GetCollisionVolume()); // transform into piece-space relative to pos - vm <<= lmp->GetModelSpaceMatrix(); + vm <<= lmp->GetModelSpaceTransform().ToMatrix(); } else { // SObj::GetTransformMatrix does not include this // (its translation component is pos, not midPos) 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 38b426cc4f..4d99b7a965 100644 --- a/rts/Sim/Projectiles/ProjectileHandler.cpp +++ b/rts/Sim/Projectiles/ProjectileHandler.cpp @@ -294,6 +294,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 c1742fdd5b..eea0913505 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; @@ -834,7 +880,7 @@ void CUnitScript::Shatter(int piece, const float3& pos, const float3& speed) const float pieceChance = 1.0f - (projectileHandler.GetCurrentParticles() - (projectileHandler.maxParticles - 2000)) / 2000.0f; if (pieceChance > 0.0f) { - const CMatrix44f m = unit->GetTransformMatrix() * lmp->GetModelSpaceMatrix(); + const CMatrix44f m = unit->GetTransformMatrix() * lmp->GetModelSpaceTransform().ToMatrix(); omp->Shatter(pieceChance, unit->model->type, unit->model->textureType, unit->team, pos, speed, m); } } diff --git a/rts/Sim/Units/Scripts/UnitScript.h b/rts/Sim/Units/Scripts/UnitScript.h index f94d7d84f2..8a983a6065 100644 --- a/rts/Sim/Units/Scripts/UnitScript.h +++ b/rts/Sim/Units/Scripts/UnitScript.h @@ -92,8 +92,8 @@ 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) bool GetEmitDirPos(int scriptPieceNum, float3& pos, float3& dir) const { if (!PieceExists(scriptPieceNum)) @@ -116,9 +116,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 9f1ec62fbd..eecfd05c98 100644 --- a/rts/Sim/Units/Unit.cpp +++ b/rts/Sim/Units/Unit.cpp @@ -731,7 +731,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->GetPieceTransform(tu.piece).ToMatrix(); transportee->SetDirVectors(transMat * pieceMat); } diff --git a/rts/Sim/Units/Unit.h b/rts/Sim/Units/Unit.h index fe4af49685..15051b9a38 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 f5012fc967..80796a285c 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 238d3e8846..ef621d8591 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->GetPieceTransform(weaponPiece).ToMatrix(); const float3 relWeaponPos = weaponMat.GetPos(); const float3 newWeaponDir = owner->GetObjectSpaceVec(float3(weaponMat[2], weaponMat[6], weaponMat[10])); diff --git a/rts/System/CMakeLists.txt b/rts/System/CMakeLists.txt index 6b24caf79c..0b554ffb98 100644 --- a/rts/System/CMakeLists.txt +++ b/rts/System/CMakeLists.txt @@ -34,6 +34,7 @@ make_global_var(sources_engine_System_common "${CMAKE_CURRENT_SOURCE_DIR}/Math/SpringDampers.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/Math/NURBS.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/Matrix44f.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/Quaternion.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/Misc/RectangleOverlapHandler.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/Misc/SpringTime.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/Object.cpp" @@ -66,6 +67,7 @@ make_global_var(sources_engine_System_common "${CMAKE_CURRENT_SOURCE_DIR}/Threading/ThreadPool.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/TimeProfiler.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/TimeUtil.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/Transform.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/UriParser.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/StringUtil.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/type2.cpp" 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 338d88d2eb..653d7dfd6a 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" @@ -24,7 +25,7 @@ CMatrix44f::CMatrix44f(const CMatrix44f& mat) } -CMatrix44f::CMatrix44f(const float3 pos, const float3 x, const float3 y, const float3 z) +CMatrix44f::CMatrix44f(const float3& pos, const float3& x, const float3& y, const float3& z) { // column-major! m[0] = x.x; m[4] = y.x; m[ 8] = z.x; m[12] = pos.x; @@ -33,13 +34,13 @@ CMatrix44f::CMatrix44f(const float3 pos, const float3 x, const float3 y, const f m[3] = 0.0f; m[7] = 0.0f; m[11] = 0.0f; m[15] = 1.0f; } -CMatrix44f::CMatrix44f(const float rotX, const float rotY, const float rotZ) +CMatrix44f::CMatrix44f(float rotX, float rotY, float rotZ) { LoadIdentity(); RotateEulerXYZ(float3(rotX, rotY, rotZ)); } -CMatrix44f::CMatrix44f(const float3 p) +CMatrix44f::CMatrix44f(const float3& p) { LoadIdentity(); SetPos(p); @@ -70,6 +71,22 @@ bool CMatrix44f::IsIdentity() const return (*this) == IDENTITY; } +float CMatrix44f::Det3() const +{ + // triple product == D + return col[0].dot(col[1].cross(col[2])); +} + +float CMatrix44f::Det4() const +{ + return + md[0][3] * md[1][2] * md[2][1] * md[3][0] - md[0][2] * md[1][3] * md[2][1] * md[3][0] - md[0][3] * md[1][1] * md[2][2] * md[3][0] + md[0][1] * md[1][3] * md[2][2] * md[3][0] + + md[0][2] * md[1][1] * md[2][3] * md[3][0] - md[0][1] * md[1][2] * md[2][3] * md[3][0] - md[0][3] * md[1][2] * md[2][0] * md[3][1] + md[0][2] * md[1][3] * md[2][0] * md[3][1] + + md[0][3] * md[1][0] * md[2][2] * md[3][1] - md[0][0] * md[1][3] * md[2][2] * md[3][1] - md[0][2] * md[1][0] * md[2][3] * md[3][1] + md[0][0] * md[1][2] * md[2][3] * md[3][1] + + md[0][3] * md[1][1] * md[2][0] * md[3][2] - md[0][1] * md[1][3] * md[2][0] * md[3][2] - md[0][3] * md[1][0] * md[2][1] * md[3][2] + md[0][0] * md[1][3] * md[2][1] * md[3][2] + + md[0][1] * md[1][0] * md[2][3] * md[3][2] - md[0][0] * md[1][1] * md[2][3] * md[3][2] - md[0][2] * md[1][1] * md[2][0] * md[3][3] + md[0][1] * md[1][2] * md[2][0] * md[3][3] + + md[0][2] * md[1][0] * md[2][1] * md[3][3] - md[0][0] * md[1][2] * md[2][1] * md[3][3] - md[0][1] * md[1][0] * md[2][2] * md[3][3] + md[0][0] * md[1][1] * md[2][2] * md[3][3]; +} CMatrix44f& CMatrix44f::RotateX(float angle) { @@ -196,7 +213,7 @@ CMatrix44f& CMatrix44f::RotateZ(float angle) } -CMatrix44f& CMatrix44f::Rotate(float angle, const float3 axis) +CMatrix44f& CMatrix44f::Rotate(float angle, const float3& axis) { const float sr = math::sin(angle); const float cr = math::cos(angle); @@ -226,7 +243,7 @@ CMatrix44f& CMatrix44f::Rotate(float angle, const float3 axis) } -CMatrix44f& CMatrix44f::RotateEulerXYZ(const float3 angles) +CMatrix44f& CMatrix44f::RotateEulerXYZ(const float3& angles) { // rotate around X first, Y second, Z third (R=R(Z)*R(Y)*R(X)) if (angles[ANGLE_P] != 0.0f) { RotateX(angles[ANGLE_P]); } @@ -235,7 +252,7 @@ CMatrix44f& CMatrix44f::RotateEulerXYZ(const float3 angles) return *this; } -CMatrix44f& CMatrix44f::RotateEulerYXZ(const float3 angles) +CMatrix44f& CMatrix44f::RotateEulerYXZ(const float3& angles) { // rotate around Y first, X second, Z third (R=R(Z)*R(X)*R(Y)) if (angles[ANGLE_Y] != 0.0f) { RotateY(angles[ANGLE_Y]); } @@ -244,7 +261,7 @@ CMatrix44f& CMatrix44f::RotateEulerYXZ(const float3 angles) return *this; } -CMatrix44f& CMatrix44f::RotateEulerZXY(const float3 angles) +CMatrix44f& CMatrix44f::RotateEulerZXY(const float3& angles) { // rotate around Z first, X second, Y third (R=R(Y)*R(X)*R(Z)) if (angles[ANGLE_R] != 0.0f) { RotateZ(angles[ANGLE_R]); } @@ -253,7 +270,7 @@ CMatrix44f& CMatrix44f::RotateEulerZXY(const float3 angles) return *this; } -CMatrix44f& CMatrix44f::RotateEulerZYX(const float3 angles) +CMatrix44f& CMatrix44f::RotateEulerZYX(const float3& angles) { // rotate around Z first, Y second, X third (R=R(X)*R(Y)*R(Z)) if (angles[ANGLE_R] != 0.0f) { RotateZ(angles[ANGLE_R]); } @@ -272,7 +289,7 @@ CMatrix44f& CMatrix44f::RotateEulerZYX(const float3 angles) // [ 0 0 sz 0] // [ 0 0 0 1] // -CMatrix44f& CMatrix44f::Scale(const float3 scales) +CMatrix44f& CMatrix44f::Scale(const float3& scales) { m[ 0] *= scales.x; m[ 1] *= scales.x; @@ -360,6 +377,15 @@ static inline void MatrixMatrixMultiplySSE(const CMatrix44f& m1, const CMatrix44 _mm_store_ps(&mout->md[3][0], moutc4); } +bool CMatrix44f::equals(const CMatrix44f& rhs) const +{ + return + col[0].equals(rhs.col[0]) && + col[1].equals(rhs.col[1]) && + col[2].equals(rhs.col[2]) && + col[3].equals(rhs.col[3]); +} + bool CMatrix44f::operator==(const CMatrix44f& rhs) const { if (this == &rhs) diff --git a/rts/System/Matrix44f.h b/rts/System/Matrix44f.h index 5dcb6ef1a3..2e3e20875c 100644 --- a/rts/System/Matrix44f.h +++ b/rts/System/Matrix44f.h @@ -24,12 +24,16 @@ class CMatrix44f const float m8, const float m9, const float m10, const float m11, const float m12, const float m13, const float m14, const float m15) : m{m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15} {}; - CMatrix44f(const float3 pos, const float3 x, const float3 y, const float3 z); - CMatrix44f(const float rotX, const float rotY, const float rotZ); - explicit CMatrix44f(const float3 pos); + explicit CMatrix44f(const float3& pos, const float3& x, const float3& y, const float3& z); + explicit CMatrix44f(float rotX, float rotY, float rotZ); + explicit CMatrix44f(const float3& pos); bool IsOrthoNormal() const; bool IsIdentity() const; + bool IsRotMatrix() const { return IsOrthoNormal() && math::fabs(1.0f - Det4()) <= float3::cmp_eps(); } + bool IsRotOrRotTranMatrix() const { return IsOrthoNormal() && math::fabs(1.0f - Det3()) <= float3::cmp_eps(); } + float Det3() const; + float Det4() const; CMatrix44f& LoadIdentity() { return (*this = CMatrix44f()); } @@ -37,20 +41,21 @@ class CMatrix44f CMatrix44f& RotateX(float angle); // (pitch) angle in radians CMatrix44f& RotateY(float angle); // ( yaw) angle in radians CMatrix44f& RotateZ(float angle); // ( roll) angle in radians - CMatrix44f& Rotate(float angle, const float3 axis); // assumes axis is normalized - CMatrix44f& RotateEulerXYZ(const float3 angles); // executes Rotate{X,Y,Z} - CMatrix44f& RotateEulerYXZ(const float3 angles); // executes Rotate{Y,X,Z} - CMatrix44f& RotateEulerZXY(const float3 angles); // executes Rotate{Z,X,Y} - CMatrix44f& RotateEulerZYX(const float3 angles); // executes Rotate{Z,Y,X} + CMatrix44f& Rotate(float angle, const float3& axis); // assumes axis is normalized + CMatrix44f& RotateEulerXYZ(const float3& angles); // executes Rotate{X,Y,Z} + CMatrix44f& RotateEulerYXZ(const float3& angles); // executes Rotate{Y,X,Z} + CMatrix44f& RotateEulerZXY(const float3& angles); // executes Rotate{Z,X,Y} + CMatrix44f& RotateEulerZYX(const float3& angles); // executes Rotate{Z,Y,X} CMatrix44f& Translate(const float x, const float y, const float z); - CMatrix44f& Translate(const float3 pos) { return Translate(pos.x, pos.y, pos.z); } - CMatrix44f& Scale(const float3 scales); + CMatrix44f& Translate(const float3& pos) { return Translate(pos.x, pos.y, pos.z); } + CMatrix44f& Scale(float s) { return Scale(s, s, s); } + CMatrix44f& Scale(const float3& scales); CMatrix44f& Scale(float scaleX, float scaleY, float scaleZ) { return Scale(float3{ scaleX, scaleY, scaleZ }); } - void SetPos(const float3 pos) { m[12] = pos.x; m[13] = pos.y; m[14] = pos.z; } - void SetX (const float3 dir) { m[ 0] = dir.x; m[ 1] = dir.y; m[ 2] = dir.z; } - void SetY (const float3 dir) { m[ 4] = dir.x; m[ 5] = dir.y; m[ 6] = dir.z; } - void SetZ (const float3 dir) { m[ 8] = dir.x; m[ 9] = dir.y; m[10] = dir.z; } + void SetPos(const float3& pos) { m[12] = pos.x; m[13] = pos.y; m[14] = pos.z; } + void SetX (const float3& dir) { m[ 0] = dir.x; m[ 1] = dir.y; m[ 2] = dir.z; } + void SetY (const float3& dir) { m[ 4] = dir.x; m[ 5] = dir.y; m[ 6] = dir.z; } + void SetZ (const float3& dir) { m[ 8] = dir.x; m[ 9] = dir.y; m[10] = dir.z; } void SetXYZ(const CMatrix44f& other) { std::copy(&other.m[0], &other.m[0] + 3, &m[0]); std::copy(&other.m[4], &other.m[4] + 3, &m[4]); @@ -92,6 +97,9 @@ class CMatrix44f float3 Mul(const float3 v) const { return ((*this) * v); } float4 Mul(const float4 v) const { return ((*this) * v); } + /// approximately equal + bool equals(const CMatrix44f& rhs) const; + bool operator == (const CMatrix44f& rhs) const; bool operator != (const CMatrix44f& rhs) const { return !(*this == rhs); } /// matrix multiply 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 new file mode 100644 index 0000000000..6cfb04e314 --- /dev/null +++ b/rts/System/Quaternion.cpp @@ -0,0 +1,441 @@ +/* This file is part of the Recoil engine (GPL v2 or later), see LICENSE.html */ + +#include "Quaternion.h" +#include "System/SpringMath.h" + +//contains some code from +// https://github.com/ilmola/gml/blob/master/include/gml/quaternion.hpp +// https://github.com/ilmola/gml/blob/master/include/gml/mat.hpp +// https://github.com/g-truc/glm/blob/master/glm/ext/quaternion_common.inl +// Also nice source https://www.shadertoy.com/view/fdtfWM + +CR_BIND(CQuaternion, ) +CR_REG_METADATA(CQuaternion, ( + CR_MEMBER(x), + CR_MEMBER(y), + CR_MEMBER(z), + CR_MEMBER(r) +)) + +/// +/// Quaternion from Euler PYR/XYZ angles +/// +CQuaternion CQuaternion::FromEulerPYR(const float3& angles) +{ + // CMatrix44f::RotateEulerXYZ defines it as (R=R(Z)*R(Y)*R(X)) + // so R(r)*R(y)*R(p) + + const float sp = math::sin(angles[CMatrix44f::ANGLE_P] * 0.5f); + const float cp = math::cos(angles[CMatrix44f::ANGLE_P] * 0.5f); + const float sy = math::sin(angles[CMatrix44f::ANGLE_Y] * 0.5f); + const float cy = math::cos(angles[CMatrix44f::ANGLE_Y] * 0.5f); + const float sr = math::sin(angles[CMatrix44f::ANGLE_R] * 0.5f); + const float cr = math::cos(angles[CMatrix44f::ANGLE_R] * 0.5f); + + CQuaternion pyrQ{ + cr * cy * sp + cp * sr * sy, + cp * cr * sy - cy * sp * sr, + cp * cy * sr + cr * sp * sy, + cp * cr * cy - sp * sr * sy + }; +#ifdef _DEBUG + static constexpr auto pAxis = float3(1, 0, 0); + static constexpr auto yAxis = float3(0, 1, 0); + static constexpr auto rAxis = float3(0, 0, 1); + auto pyrQ2 = CQuaternion::MakeFrom(angles[CMatrix44f::ANGLE_P], pAxis) * CQuaternion::MakeFrom(angles[CMatrix44f::ANGLE_Y], yAxis) * CQuaternion::MakeFrom(angles[CMatrix44f::ANGLE_R], rAxis); + + CMatrix44f m; m.RotateEulerXYZ(-angles); + CQuaternion pyrQ3; + std::tie(std::ignore, pyrQ3, std::ignore) = DecomposeIntoTRS(m); + + assert(pyrQ.equals(pyrQ2)); + assert(pyrQ.equals(pyrQ3)); +#endif + return AssertNormalized(pyrQ); +} + +/// +/// Quaternion from Euler YPR/YXZ angles +/// +CQuaternion CQuaternion::FromEulerYPR(const float3& angles) +{ + // CMatrix44f::RotateEulerYXZ defines it as (R=R(Z)*R(X)*R(Y)) + // so R(r)*R(p)*R(y) + + const float sp = math::sin(angles[CMatrix44f::ANGLE_P] * 0.5f); + const float cp = math::cos(angles[CMatrix44f::ANGLE_P] * 0.5f); + const float sy = math::sin(angles[CMatrix44f::ANGLE_Y] * 0.5f); + const float cy = math::cos(angles[CMatrix44f::ANGLE_Y] * 0.5f); + const float sr = math::sin(angles[CMatrix44f::ANGLE_R] * 0.5f); + const float cr = math::cos(angles[CMatrix44f::ANGLE_R] * 0.5f); + + CQuaternion yprQ{ + cr* cy* sp + cp * sr * sy, + cp* cr* sy - cy * sp * sr, + cp* cy* sr - cr * sp * sy, + cp* cr* cy + sp * sr * sy + }; +#ifdef _DEBUG + static constexpr auto pAxis = float3(1, 0, 0); + static constexpr auto yAxis = float3(0, 1, 0); + static constexpr auto rAxis = float3(0, 0, 1); + auto yprQ2 = CQuaternion::MakeFrom(angles[CMatrix44f::ANGLE_Y], yAxis) * CQuaternion::MakeFrom(angles[CMatrix44f::ANGLE_P], pAxis) * CQuaternion::MakeFrom(angles[CMatrix44f::ANGLE_R], rAxis); + + CMatrix44f m; m.RotateEulerYXZ(-angles); + CQuaternion yprQ3; + std::tie(std::ignore, yprQ3, std::ignore) = DecomposeIntoTRS(m); + + assert(yprQ.equals(yprQ2)); + assert(yprQ2.equals(yprQ3)); +#endif + return AssertNormalized(yprQ); +} + +/// +/// Quaternion from rotation angle and axis +/// +CQuaternion CQuaternion::MakeFrom(float angle, const float3& axis) +{ + assert(axis.Normalized()); + + const float a = 0.5f * angle; + return CQuaternion(axis * math::sin(a), math::cos(a)); //Normalized if axis.Normalized() +} + +/// +/// Quaternion to rotate from v1 to v2 +/// Expects v1 and v2 to be already normalized +/// +CQuaternion CQuaternion::MakeFrom(const float3& v1, const float3& v2) +{ + assert(v1.Normalized()); + assert(v2.Normalized()); +#if 0 + if unlikely(v1.same(v2)) { + return CQuaternion(v1, 0.0f).Normalize(); + } + else if unlikely(v1.same(-v2)) { + float3 v; + if (v1.x > -float3::cmp_eps() && v1.x < float3::cmp_eps()) // if x ~= 0 + v = { 1.0f, 0.0f, 0.0f }; + else if (v1.y > -float3::cmp_eps() && v1.y < float3::cmp_eps()) // if y ~= 0 + v = { 0.0f, 1.0f, 0.0f }; + else // if z ~= 0 + v = { 0.0f, 0.0f, 1.0f }; + + return CQuaternion(v, math::HALFPI).Normalize(); + } + else { + float3 v = v1.cross(u2); // compute rotation axis + float angle = math::acosf(v1.dot(v2)); // rotation angle + return CQuaternion(v, angle * 0.5f).Normalize(); // half angle + } +#else + // https://raw.org/proof/quaternion-from-two-vectors/ + + const auto dp = v1.dot(v2); + if unlikely(epscmp(dp, -1.0f, float3::cmp_eps())) { + // any perpendicular vector to v1/v2 will suffice + float3 npVec = v1.PickNonParallel(); + const auto cp = v1.cross(npVec).Normalize(); + return CQuaternion(cp, 0.0f); + } + else { + const auto cp = v1.cross(v2); + return CQuaternion(cp, 1.0f + dp).Normalize(); + } +#endif +} + +/// +/// Quaternion from a rotation matrix +/// Should only be called on R or T * R matrices +/// +CQuaternion CQuaternion::MakeFrom(const CMatrix44f& mat) +{ + assert(mat.IsRotOrRotTranMatrix()); + const float trace = mat.md[0][0] + mat.md[1][1] + mat.md[2][2]; + + if (trace > 0.0f) { + const float s = 0.5f * InvSqrt(trace + 1.0f); + + return AssertNormalized(CQuaternion( + s * (mat.md[1][2] - mat.md[2][1]), + s * (mat.md[2][0] - mat.md[0][2]), + s * (mat.md[0][1] - mat.md[1][0]), + 0.25f / s + )); + } + else if (mat.md[0][0] > mat.md[1][1] && mat.md[0][0] > mat.md[2][2]) { + const float s = 2.0f * math::sqrt(1.0f + mat.md[0][0] - mat.md[1][1] - mat.md[2][2]); + const float invs = 1.0f / s; + + return AssertNormalized(CQuaternion( + 0.25f * s, + (mat.md[1][0] + mat.md[0][1]) * invs, + (mat.md[2][0] + mat.md[0][2]) * invs, + (mat.md[1][2] - mat.md[2][1]) * invs + )); + } + else if (mat.md[1][1] > mat.md[2][2]) { + const float s = 2.0f * math::sqrt(1.0f + mat.md[1][1] - mat.md[0][0] - mat.md[2][2]); + const float invs = 1.0f / s; + + return AssertNormalized(CQuaternion( + (mat.md[1][0] + mat.md[0][1]) * invs, + 0.25f * s, + (mat.md[2][1] + mat.md[1][2]) * invs, + (mat.md[2][0] - mat.md[0][2]) * invs + )); + } + else { + const float s = 2.0f * math::sqrt(1.0f + mat.md[2][2] - mat.md[0][0] - mat.md[1][1]); + const float invs = 1.0f / s; + + return AssertNormalized(CQuaternion( + (mat.md[2][0] + mat.md[0][2]) * invs, + (mat.md[2][1] + mat.md[1][2]) * invs, + 0.25f * s, + (mat.md[0][1] - mat.md[1][0]) * invs + )); + } +} + +const CQuaternion& CQuaternion::AssertNormalized(const CQuaternion& q) +{ + assert(q.Normalized()); + return q; +} + +/// +/// Decompose a transformation matrix into translate, rotation (Quaternion), scale components +/// +std::tuple CQuaternion::DecomposeIntoTRS(const CMatrix44f& mat) +{ + CMatrix44f tmpMat = mat; + float4& t0 = tmpMat.col[0]; + float4& t1 = tmpMat.col[1]; + float4& t2 = tmpMat.col[2]; + + const float4& c0 = mat.col[0]; + const float4& c1 = mat.col[1]; + const float4& c2 = mat.col[2]; + + const float d = tmpMat.Det3(); + const float s = Sign(d); + + float3 scaling {s * c0.Length(), c1.Length(), c2.Length()}; + + assert( + !epscmp(scaling[0], 0.0f, float3::cmp_eps()) && + !epscmp(scaling[1], 0.0f, float3::cmp_eps()) && + !epscmp(scaling[2], 0.0f, float3::cmp_eps()) + ); + + t0 /= scaling[0]; + t1 /= scaling[1]; + t2 /= scaling[2]; + + assert(tmpMat.IsRotOrRotTranMatrix()); + + return std::make_tuple( + float3(mat.col[3]), //translate + CQuaternion::MakeFrom(tmpMat), //rotate (quat) + scaling //scale + ); +} + + +bool CQuaternion::Normalized() const +{ + return math::fabs(1.0f - (r * r + x * x + y * y + z * z)) <= float3::cmp_eps(); +} + +CQuaternion& CQuaternion::Normalize() +{ + const float sqn = SqNorm(); + if unlikely(sqn < float3::nrm_eps()) + return *this; + + *this *= InvSqrt(sqn); + + return *this; +} + +/// +/// Find axis and angle equivalent rotation from a quaternion +/// +float4 CQuaternion::ToAxisAndAngle() const +{ + return float4( + float3(x, y, z) * InvSqrt(std::max(0.0f, 1.0f - r * r)), + 2.0f * math::acos(std::clamp(r, -1.0f, 1.0f)) + ); +} + +/// +/// Converts a quaternion to rotational matrix +/// +CMatrix44f CQuaternion::ToRotMatrix() const +{ + const float qxx = x * x; + const float qyy = y * y; + const float qzz = z * z; + const float qxz = x * z; + const float qxy = x * y; + const float qyz = y * z; + const float qrx = r * x; + const float qry = r * y; + const float qrz = r * z; + +#if 0 + return CMatrix44f( + 1.0f - 2.0f * (qyy + qzz), 2.0f * (qxy - qrz) , 2.0f * (qxz + qry) , 0.0f, + 2.0f * (qxy + qrz) , 1.0f - 2.0f * (qxx + qzz), 2.0f * (qyz - qrx) , 0.0f, + 2.0f * (qxz - qry) , 2.0f * (qyz + qrx) , 1.0f - 2.0f * (qxx + qyy), 0.0f, + 0.0f , 0.0f , 0.0f , 1.0f + ); +#else + return CMatrix44f( + 1.0f - 2.0f * (qyy + qzz), 2.0f * (qxy + qrz) , 2.0f * (qxz - qry) , 0.0f, + 2.0f * (qxy - qrz) , 1.0f - 2.0f * (qxx + qzz), 2.0f * (qyz + qrx) , 0.0f, + 2.0f * (qxz + qry) , 2.0f * (qyz - qrx) , 1.0f - 2.0f * (qxx + qyy), 0.0f, + 0.0f , 0.0f , 0.0f , 1.0f + ); +#endif +} + +float3 CQuaternion::Rotate(const float3& v) const +{ + assert(Normalized()); +#if 0 + const auto vRotQ = (*this) * CQuaternion(v, 0.0f) * this->Inverse(); + return float3{ vRotQ.x, vRotQ.y, vRotQ.z }; +#else + const float3 u = float3{ x, y, z }; + return 2.0f * u.dot(v) * u + + (r * r - u.dot(u)) * v + + 2.0f * r * u.cross(v); +#endif +} + +float4 CQuaternion::Rotate(const float4& v) const +{ + return float4{ Rotate(static_cast(v)), v.w }; +} + +bool CQuaternion::equals(const CQuaternion& rhs) const +{ + return + ( + epscmp(x, rhs.x, float3::cmp_eps()) && + epscmp(y, rhs.y, float3::cmp_eps()) && + epscmp(z, rhs.z, float3::cmp_eps()) && + epscmp(r, rhs.r, float3::cmp_eps()) + ) || ( + epscmp(x, -rhs.x, float3::cmp_eps()) && + epscmp(y, -rhs.y, float3::cmp_eps()) && + epscmp(z, -rhs.z, float3::cmp_eps()) && + epscmp(r, -rhs.r, float3::cmp_eps()) + ); +} + +CQuaternion CQuaternion::Inverse() const +{ + CQuaternion inv = *this; + inv.InverseInPlace(); + return inv; +} + +CQuaternion& CQuaternion::InverseInPlace() +{ + const float sqn = SqNorm(); + if unlikely(sqn < float3::nrm_eps()) + return *this; + + *this = Conjugate() / sqn; // aparently not math::sqrt(sqn) + return *this; +}; + +CQuaternion CQuaternion::operator*(const CQuaternion& rhs) const +{ + // *this or rhs can be a vertex from CQuaternion::Rotate(), can't assume either of them is normalized + + std::array crossProduct = { + (y * rhs.z) - (z * rhs.y), + (z * rhs.x) - (x * rhs.z), + (x * rhs.y) - (y * rhs.x) + }; + + return CQuaternion( + r * rhs.x + rhs.r * x + crossProduct[0], + r * rhs.y + rhs.r * y + crossProduct[1], + r * rhs.z + rhs.r * z + crossProduct[2], + r * rhs.r - (x * rhs.x + y * rhs.y + z * rhs.z) + ); +} + +CQuaternion& CQuaternion::operator*=(float f) +{ + x *= f; + y *= f; + z *= f; + r *= f; + + return *this; +} + +float CQuaternion::SqNorm() const { + return (x * x + y * y + z * z + r * r); +} + +float CQuaternion::InvSqrt(float f) +{ +#if 0 + return math::isqrt(f); +#else + return 1.0f / math::sqrt(f); +#endif +} + +CQuaternion CQuaternion::Lerp(const CQuaternion& q1, const CQuaternion& q2, const float a) { + assert(q1.Normalized()); + assert(q2.Normalized()); + return (q1 * (1.0f - a) + (q2 * a)).Normalize(); +} + +CQuaternion CQuaternion::SLerp(const CQuaternion& q1, const CQuaternion& q2_, const float a) { + assert( q1.Normalized()); + assert(q2_.Normalized()); + + if (a == 0.0f) + return q1; + else if (a == 1.0f) + return q2_; + + // dot product + float cosTheta = (q1.x * q2_.x + q1.y * q2_.y + q1.z * q2_.z); + + const float s = Sign(cosTheta); + + CQuaternion q2 = q2_ * s; + cosTheta *= s; + + if unlikely(cosTheta > 1.0f - float3::cmp_eps()) { + // Linear interpolation + return Lerp(q1, q2, a); + } else { + // Essential Mathematics, page 467 + const float angle = math::acos(cosTheta); + const float s1 = math::sin((1.0f - a) * angle); + const float s2 = math::sin(( a) * angle); + + const float invsin = 1.0f / math::sin(angle); + return AssertNormalized(CQuaternion( + (s1 * q1.x + s2 * q2.x) * invsin, + (s1 * q1.y + s2 * q2.y) * invsin, + (s1 * q1.z + s2 * q2.z) * invsin, + (s1 * q1.r + s2 * q2.r) * invsin + )); + } +} \ No newline at end of file diff --git a/rts/System/Quaternion.h b/rts/System/Quaternion.h new file mode 100644 index 0000000000..e915855e8b --- /dev/null +++ b/rts/System/Quaternion.h @@ -0,0 +1,110 @@ +/* This file is part of the Recoil engine (GPL v2 or later), see LICENSE.html */ +#pragma once + +#include +#include + +#include "System/float3.h" +#include "System/float4.h" +#include "System/Matrix44f.h" + +struct Transform; + +class alignas(16) CQuaternion +{ +public: + CR_DECLARE_STRUCT(CQuaternion) +public: + constexpr CQuaternion() + : x(0.0f) + , y(0.0f) + , z(0.0f) + , r(1.0f) + {} + constexpr CQuaternion(const float4& q) + : x(q.x) + , y(q.y) + , z(q.z) + , r(q.w) + {} + constexpr CQuaternion(const float3& imag, float real) + : x(imag.x) + , y(imag.y) + , z(imag.z) + , r(real) + {} + constexpr CQuaternion(float xi, float yi, float zi, float real) + : x(xi) + , y(yi) + , z(zi) + , r(real) + {} + constexpr CQuaternion(const CQuaternion& q) = default; + constexpr CQuaternion(CQuaternion&& q) noexcept = default; +public: + // Euler angles, also known as RotateEulerXYZ, produces same rotation as CMatrix44f::RotateEulerYXZ() + static CQuaternion FromEulerPYRNeg(const float3& angles) { return FromEulerPYR(-angles); }; + // Euler angles, also known as RotateEulerXYZ + static CQuaternion FromEulerPYR(const float3& angles); + + // Euler angles, also known as RotateEulerYXZ, produces same rotation as CMatrix44f::RotateEulerXYZ() + static CQuaternion FromEulerYPRNeg(const float3& angles) { return FromEulerYPR(-angles); }; + // Euler angles, also known as RotateEulerYXZ + static CQuaternion FromEulerYPR(const float3& angles); + + static CQuaternion MakeFrom(float angle, const float3& axis); + static CQuaternion MakeFrom(const float3& v1, const float3& v2); + static CQuaternion MakeFrom(const CMatrix44f& mat); + + static const CQuaternion& AssertNormalized(const CQuaternion& q); + + static std::tuple DecomposeIntoTRS(const CMatrix44f& mat); +public: + bool Normalized() const; + CQuaternion& Normalize(); + constexpr CQuaternion& Conjugate() { x = -x; y = -y; z = -z; return *this; } + CQuaternion Inverse() const; + CQuaternion& InverseInPlace(); + + float4 ToAxisAndAngle() const; + CMatrix44f ToRotMatrix() const; + + float3 Rotate(const float3& v) const; + float4 Rotate(const float4& v) const; + + bool equals(const CQuaternion& rhs) const; +public: + constexpr CQuaternion& operator= (const CQuaternion&) = default; + constexpr CQuaternion& operator= (CQuaternion&&) noexcept = default; + + constexpr CQuaternion operator-() const { return CQuaternion(-x, -y, -z, -r); } + + CQuaternion operator*(float a) const { + return CQuaternion(x * a, y * a, z * a, r * a); + } + CQuaternion operator/(float a) const { + const float ainv = 1.0f / a; + return CQuaternion(x * ainv, y * ainv, z * ainv, r * ainv); + } + + CQuaternion operator+(const CQuaternion& rhs) const { + return CQuaternion(x + rhs.x, y + rhs.y, z + rhs.z, r + rhs.r); + } + CQuaternion operator-(const CQuaternion& rhs) const { + return CQuaternion(x - rhs.x, y - rhs.y, z - rhs.z, r - rhs.r); + } + + CQuaternion operator*(const CQuaternion& rhs) const; + CQuaternion& operator*=(float f); + + bool operator==(const CQuaternion& rhs) const { return equals(rhs); } //aproximate + bool operator!=(const CQuaternion& rhs) const { return !equals(rhs); } //aproximate +private: + float SqNorm() const; + static float InvSqrt(float f); +public: + static CQuaternion Lerp (const CQuaternion& q1, const CQuaternion& q2, const float a); + static CQuaternion SLerp(const CQuaternion& q1, const CQuaternion& q2, const float a); +public: + float x, y, z, r; +}; \ No newline at end of file diff --git a/rts/System/SpringApp.cpp b/rts/System/SpringApp.cpp index 13e94f5ac9..220053fdf4 100644 --- a/rts/System/SpringApp.cpp +++ b/rts/System/SpringApp.cpp @@ -791,7 +791,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); diff --git a/rts/System/Transform.cpp b/rts/System/Transform.cpp new file mode 100644 index 0000000000..b42a199ae0 --- /dev/null +++ b/rts/System/Transform.cpp @@ -0,0 +1,142 @@ +#include "Transform.hpp" + +#include + +#include "System/SpringMath.h" + +CR_BIND(Transform, ) +CR_REG_METADATA(Transform, ( + CR_MEMBER(r), + CR_MEMBER(t), + CR_MEMBER(s) +)) + +static_assert(sizeof (Transform) == 2 * 4 * sizeof(float)); +static_assert(alignof(Transform) == alignof(decltype(Transform::r))); + +const Transform& Transform::Zero() +{ + static const Transform zero{ + CQuaternion{ 0, 0, 0, 0 }, + float3{ 0, 0, 0 }, + 0 + }; + return zero; +} + +void Transform::SetScaleSign(float signSrc) +{ + s = std::copysignf(s, signSrc); +} + +bool Transform::IsIdentity() const +{ + static constexpr Transform Identity; + return this->equals(Identity); +} + +Transform Transform::FromMatrix(const CMatrix44f& mat) +{ + Transform tra; + float3 scale; + std::tie(tra.t, tra.r, scale) = CQuaternion::DecomposeIntoTRS(mat); + assert( + epscmp(scale.x, scale.y, std::max(scale.x, scale.y) * float3::cmp_eps()) && + epscmp(scale.y, scale.z, std::max(scale.y, scale.z) * float3::cmp_eps()) && + epscmp(scale.z, scale.x, std::max(scale.z, scale.x) * float3::cmp_eps()) + ); + // non-uniform scaling is not supported + tra.s = scale.x; +#ifdef _DEBUG + const float3 v{ 100, 200, 300 }; + auto vMat = mat * v; + auto vTra = tra * v; + + auto vMatN = vMat; vMatN.Normalize(); + auto vTraN = vTra; vTraN.Normalize(); + + assert(math::fabs(1.0f - vMatN.dot(vTraN)) < 0.05f); +#endif + return tra; +} + +CMatrix44f Transform::ToMatrix() const +{ + // M = T * R * S; + /* + (r0 * sx, r1 * sx, r2 * sx, 0) + (r3 * sy, r4 * sy, r5 * sy, 0) + (r6 * sz, r7 * sz, r8 * sz, 0) + (tx , ty , tz , 1) + */ + + // therefore + CMatrix44f m = r.ToRotMatrix(); + m.Scale(s); + m.SetPos(t); // m.Translate() will be wrong here + + CMatrix44f ms; ms.Scale(s); + CMatrix44f mr = r.ToRotMatrix(); + CMatrix44f mt; mt.Translate(t); + + CMatrix44f m2 = mt * mr * ms; + + //assert(m == m2); +#ifdef _DEBUG + //auto [t_, r_, s_] = CQuaternion::DecomposeIntoTRS(m); + + const float3 v{ 100, 200, 300 }; + auto vMat = m * v; + auto vTra = (*this) * v; + + auto vMatN = vMat; vMatN.Normalize(); + auto vTraN = vTra; vTraN.Normalize(); + + assert(math::fabs(1.0f - vMatN.dot(vTraN)) < 0.05f); +#endif + return m; +} + +Transform Transform::InvertAffine() const +{ + // TODO check correctness + const auto invR = r.Inverse(); + const auto invS = 1.0f / s; + return Transform{ + invR, + invR.Rotate(-t * invS), + invS, + }; +} + +bool Transform::equals(const Transform& tra) const +{ + return + r.equals(tra.r) && + t.equals(tra.t) && + epscmp(s, tra.s, float3::cmp_eps()); +} + +Transform Transform::operator*(const Transform& childTra) const +{ + // TODO check correctness + + return Transform{ + r * childTra.r, + t + r.Rotate(s * childTra.t), + s * childTra.s + }; +} + +float3 Transform::operator*(const float3& v) const +{ + // Scale, Rotate, Translate + // same order as CMatrix44f's vTra = T * R * S * v; + return r.Rotate(v * s) + t; +} + +float4 Transform::operator*(const float4& v) const +{ + // same as above + return r.Rotate(v * s) + t; +} diff --git a/rts/System/Transform.hpp b/rts/System/Transform.hpp new file mode 100644 index 0000000000..81fda8ad04 --- /dev/null +++ b/rts/System/Transform.hpp @@ -0,0 +1,54 @@ +#pragma once + +#include "Quaternion.h" +#include "float4.h" +#include "creg/creg_cond.h" + +class CMatrix44f; + +struct Transform { + CR_DECLARE_STRUCT(Transform) + CQuaternion r; + float3 t; + float s; + + constexpr Transform() + : r{ CQuaternion{} } + , t{ float3{} } + , s{ 1.0f } + {} + + constexpr Transform(const CQuaternion& r_, const float3& t_, float s_) + : r{ r_ } + , t{ t_ } + , s{ s_ } + {} + + // similar to CMatrix44f::LoadIdentity() + void LoadIdentity() { + r = CQuaternion{}; + t = float3{}; + s = 1.0f; + } + + static const Transform& Zero(); + + // can be used to enable/disable rendering + void SetScaleSign(float signSrc); + + bool IsIdentity() const; + + static Transform FromMatrix(const CMatrix44f& mat); + CMatrix44f ToMatrix() const; + + // similar to CMatrix44f::InvertAffine, except with scale() + Transform InvertAffine() const; + + bool equals(const Transform& tra) const; + + Transform operator*(const Transform& childTra) const; + float3 operator*(const float3& v) const; + float4 operator*(const float4& v) const; + + Transform& operator*=(const Transform& childTra) { *this = (*this) * childTra; return *this; } +}; \ No newline at end of file diff --git a/rts/System/float3.cpp b/rts/System/float3.cpp index 6f65b9a59d..31b7bed25f 100644 --- a/rts/System/float3.cpp +++ b/rts/System/float3.cpp @@ -1,6 +1,9 @@ /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */ #include "System/float3.h" + +#include + #include "System/creg/creg_cond.h" #include "System/SpringMath.h" @@ -11,6 +14,18 @@ CR_REG_METADATA(float3, (CR_MEMBER(x), CR_MEMBER(y), CR_MEMBER(z))) float float3::maxxpos = -1.0f; float float3::maxzpos = -1.0f; +float3 float3::PickNonParallel() const +{ + // https://math.stackexchange.com/questions/3122010/how-to-deterministically-pick-a-vector-that-is-guaranteed-to-be-non-parallel-to + auto [mi, Mi] = std::minmax_element(std::begin(xyz), std::end(xyz), [](const auto& a, const auto& b) { return math::fabs(a) < math::fabs(b); }); + float3 npVec{ 0.0f }; + npVec.xyz[std::distance(std::begin(xyz), mi)] = *Mi; + + // don't normalize as it most likely will go as argument to cross, + // and the cross result will need to be normalized anyway + return npVec; +} + bool float3::IsInBounds() const { assert(maxxpos > 0.0f); // check if initialized diff --git a/rts/System/float3.h b/rts/System/float3.h index 48c297f310..a8ecd8efaa 100644 --- a/rts/System/float3.h +++ b/rts/System/float3.h @@ -728,6 +728,7 @@ class float3 y = 0.0f; return SafeANormalize(); } + /* // Un must be Normalized() float3& PickNonParallel(const float3 Un) { float d2 = Un.SqLength2D(); @@ -748,7 +749,12 @@ class float3 *this = float3(Un.z, Un.y, -Un.x); //y component of Un X (*this) is x1^2 + z1^2, which is non-zero return (*this); } + */ + // deterministically pick a non-parallel vector to the current one + float3 PickNonParallel() const; + + bool Normalized() const { return math::fabs(1.0f - SqLength()) <= cmp_eps(); } static bool CheckNaN(float c) { return (!math::isnan(c) && !math::isinf(c)); } bool CheckNaNs() const { return (CheckNaN(x) && CheckNaN(y) && CheckNaN(z)); } diff --git a/rts/System/float4.h b/rts/System/float4.h index 9df4e4c659..823fb5ba75 100644 --- a/rts/System/float4.h +++ b/rts/System/float4.h @@ -27,12 +27,16 @@ struct float4 : public float3 constexpr float4(const float* f): float3(f[0], f[1], f[2]), w(f[3]) {} constexpr float4(const float x, const float y, const float z, const float w = 0.0f): float3(x, y, z), w(w) {} - float4 operator * (const float4& f) const { return {x * f.x, y * f.y, z * f.z, w * f.w}; } - float4 operator + (const float4& f) const { return {x + f.x, y + f.y, z + f.z, w + f.w}; } - float4 operator - (const float4& f) const { return {x - f.x, y - f.y, z - f.z, w - f.w}; } + constexpr float4 operator- () const { + return float4(-x, -y, -z, -w); + } + + constexpr float4 operator * (const float4& f) const { return {x * f.x, y * f.y, z * f.z, w * f.w}; } + constexpr float4 operator + (const float4& f) const { return {x + f.x, y + f.y, z + f.z, w + f.w}; } + constexpr float4 operator - (const float4& f) const { return {x - f.x, y - f.y, z - f.z, w - f.w}; } - float4 operator * (float s) const { return {x * s, y * s, z * s, w * s}; } - float4 operator / (float s) const { return ((*this) * (1.0f / s)); } + constexpr float4 operator * (float s) const { return {x * s, y * s, z * s, w * s}; } + constexpr float4 operator / (float s) const { return ((*this) * (1.0f / s)); } float4& operator = (const float f[4]) { x = f[0]; y = f[1];