From 477acc2c609da7dcc092b0c88885315c59f4c348 Mon Sep 17 00:00:00 2001 From: VReaperV Date: Wed, 17 Sep 2025 16:38:47 +0300 Subject: [PATCH] Move skeleton building and blending to engine Also implement a render entity cache to avoid back-and-forth synchronisation. --- src.cmake | 2 + src/engine/client/cg_msgdef.h | 82 +++++++- src/engine/client/cl_cgame.cpp | 19 +- src/engine/null/null_renderer.cpp | 8 +- src/engine/renderer/EntityCache.cpp | 285 +++++++++++++++++++++++++++ src/engine/renderer/EntityCache.h | 60 ++++++ src/engine/renderer/tr_animation.cpp | 35 +++- src/engine/renderer/tr_backend.cpp | 4 +- src/engine/renderer/tr_init.cpp | 7 +- src/engine/renderer/tr_local.h | 25 ++- src/engine/renderer/tr_main.cpp | 34 ++-- src/engine/renderer/tr_model.cpp | 44 ++--- src/engine/renderer/tr_model_iqm.cpp | 4 +- src/engine/renderer/tr_public.h | 3 +- src/engine/renderer/tr_scene.cpp | 28 ++- src/engine/renderer/tr_surface.cpp | 22 +-- src/engine/renderer/tr_types.h | 83 ++++++-- src/shared/client/cg_api.cpp | 31 ++- src/shared/client/cg_api.h | 6 +- 19 files changed, 664 insertions(+), 118 deletions(-) create mode 100644 src/engine/renderer/EntityCache.cpp create mode 100644 src/engine/renderer/EntityCache.h diff --git a/src.cmake b/src.cmake index bfd5f352f6..944a88d2af 100644 --- a/src.cmake +++ b/src.cmake @@ -95,6 +95,8 @@ set(RENDERERLIST ${ENGINE_DIR}/renderer/tr_curve.cpp ${ENGINE_DIR}/renderer/tr_fbo.cpp ${ENGINE_DIR}/renderer/tr_font.cpp + ${ENGINE_DIR}/renderer/EntityCache.cpp + ${ENGINE_DIR}/renderer/EntityCache.h ${ENGINE_DIR}/renderer/GeometryCache.cpp ${ENGINE_DIR}/renderer/GeometryCache.h ${ENGINE_DIR}/renderer/GeometryOptimiser.cpp diff --git a/src/engine/client/cg_msgdef.h b/src/engine/client/cg_msgdef.h index 38b78dbc78..c59ec6f3ab 100644 --- a/src/engine/client/cg_msgdef.h +++ b/src/engine/client/cg_msgdef.h @@ -91,22 +91,84 @@ namespace Util { } }; + template<> struct SerializeTraits> { + static void Write( Writer& stream, const std::vector& boneMods ) { + stream.WriteSize( boneMods.size() ); + stream.WriteData( boneMods.data(), boneMods.size() * sizeof( BoneMod ) ); + } + + static std::vector Read( Reader& stream ) { + std::vector boneMods; + const size_t size = stream.ReadSize(); + boneMods.resize( size ); + stream.ReadData( boneMods.data(), size * sizeof( BoneMod ) ); + return boneMods; + } + }; + // Use that bone optimization for refEntity_t template<> struct SerializeTraits { static void Write(Writer& stream, const refEntity_t& ent) { - stream.WriteData(&ent, offsetof(refEntity_t, skeleton)); - stream.Write(ent.skeleton); + stream.WriteData(&ent, offsetof(refEntity_t, tag)); + stream.Write( ent.tag ); + stream.Write>( ent.boneMods ); + // stream.Write(ent.skeleton); } + static refEntity_t Read(Reader& stream) { refEntity_t ent; - stream.ReadData(&ent, offsetof(refEntity_t, skeleton)); - ent.skeleton = stream.Read(); + stream.ReadData(&ent, offsetof(refEntity_t, tag)); + ent.tag = stream.Read(); + ent.boneMods = stream.Read>(); + // ent.skeleton = stream.Read(); + return ent; + } + }; + + template<> struct SerializeTraits { + static void Write( Writer& stream, const EntityUpdate& ent ) { + stream.Write( ent.ent ); + stream.Write( ent.id ); + } + + static EntityUpdate Read( Reader& stream ) { + EntityUpdate ent; + ent.ent = stream.Read(); + ent.id = stream.Read(); return ent; } }; + template<> struct SerializeTraits { + static void Write( Writer& stream, const LerpTagUpdate& tag ) { + stream.Write( tag.tag ); + stream.Write( tag.id ); + } + + static LerpTagUpdate Read( Reader& stream ) { + LerpTagUpdate tag; + tag.tag = stream.Read(); + tag.id = stream.Read(); + return tag; + } + }; + + template<> struct SerializeTraits { + static void Write( Writer& stream, const LerpTagSync& tag ) { + stream.Write( tag.entityOrientation ); + stream.Write( tag.orientation ); + } + + static LerpTagSync Read( Reader& stream ) { + LerpTagSync tag; + tag.entityOrientation = stream.Read(); + tag.orientation = stream.Read(); + return tag; + } + }; + template<> struct SerializeTraits { static void Write(Writer& stream, const Color::Color& value) @@ -166,6 +228,8 @@ enum cgameImport_t CG_R_REGISTERFONT, CG_R_CLEARSCENE, CG_R_ADDREFENTITYTOSCENE, + CG_R_SYNCREFENTITIES, + CG_R_SYNCLERPTAGS, CG_R_ADDPOLYTOSCENE, CG_R_ADDPOLYSTOSCENE, CG_R_ADDLIGHTTOSCENE, @@ -179,7 +243,6 @@ enum cgameImport_t CG_R_DRAWSTRETCHPIC, CG_R_DRAWROTATEDPIC, CG_R_MODELBOUNDS, - CG_R_LERPTAG, CG_R_REMAP_SHADER, CG_R_BATCHINPVS, CG_R_LIGHTFORPOINT, @@ -319,10 +382,6 @@ namespace Render { IPC::Message, int>, IPC::Reply, std::array> >; - using LerpTagMsg = IPC::SyncMessage< - IPC::Message, refEntity_t, std::string, int>, - IPC::Reply - >; using RemapShaderMsg = IPC::Message, std::string, std::string, std::string>; // TODO not a renderer call, handle in CM in the VM? using BatchInPVSMsg = IPC::SyncMessage< @@ -379,6 +438,11 @@ namespace Render { using ScissorSetMsg = IPC::Message, int, int, int, int>; using ClearSceneMsg = IPC::Message>; using AddRefEntityToSceneMsg = IPC::Message, refEntity_t>; + using SyncRefEntitiesMsg = IPC::Message, std::vector>; + using SyncLerpTagsMsg = IPC::SyncMessage, + std::vector>, + IPC::Reply> + >; using AddPolyToSceneMsg = IPC::Message, int, std::vector>; using AddPolysToSceneMsg = IPC::Message, int, std::vector, int, int>; using AddLightToSceneMsg = IPC::Message, std::array, float, float, float, float, int>; diff --git a/src/engine/client/cl_cgame.cpp b/src/engine/client/cl_cgame.cpp index f23d49f671..1c58f198f8 100644 --- a/src/engine/client/cl_cgame.cpp +++ b/src/engine/client/cl_cgame.cpp @@ -1112,6 +1112,13 @@ void CGameVM::QVMSyscall(int syscallNum, Util::Reader& reader, IPC::Channel& cha }); break; + case CG_R_SYNCLERPTAGS: + IPC::HandleMsg( channel, std::move( reader ), [this]( const std::vector& lerpTags, + std::vector& entityOrientations ) { + entityOrientations = re.SyncLerpTags( lerpTags ); + } ); + break; + case CG_GETCURRENTSNAPSHOTNUMBER: IPC::HandleMsg(channel, std::move(reader), [this] (int& number, int& serverTime) { number = cl.snap.messageNum; @@ -1216,12 +1223,6 @@ void CGameVM::QVMSyscall(int syscallNum, Util::Reader& reader, IPC::Channel& cha }); break; - case CG_R_LERPTAG: - IPC::HandleMsg(channel, std::move(reader), [this] (const refEntity_t& entity, const std::string& tagName, int startIndex, orientation_t& tag, int& res) { - res = re.LerpTag(&tag, &entity, tagName.c_str(), startIndex); - }); - break; - case CG_R_REMAP_SHADER: IPC::HandleMsg(channel, std::move(reader), [this] (const std::string& oldShader, const std::string& newShader, const std::string& timeOffset) { re.RemapShader(oldShader.c_str(), newShader.c_str(), timeOffset.c_str()); @@ -1598,6 +1599,12 @@ void CGameVM::CmdBuffer::HandleCommandBufferSyscall(int major, int minor, Util:: }); break; + case CG_R_SYNCREFENTITIES: + HandleMsg( std::move( reader ), [this]( const std::vector& ents ) { + re.SyncRefEntities( ents ); + } ); + break; + case CG_R_ADDPOLYTOSCENE: HandleMsg(std::move(reader), [this] (int shader, const std::vector& verts) { re.AddPolyToScene(shader, verts.size(), verts.data()); diff --git a/src/engine/null/null_renderer.cpp b/src/engine/null/null_renderer.cpp index a90747c66f..33ab540782 100644 --- a/src/engine/null/null_renderer.cpp +++ b/src/engine/null/null_renderer.cpp @@ -80,6 +80,10 @@ void RE_SetWorldVisData( const byte * ) { } void RE_EndRegistration() { } void RE_ClearScene() { } void RE_AddRefEntityToScene( const refEntity_t * ) { } +void RE_SyncRefEntities( const std::vector& ) {} +std::vector RE_SyncLerpTags( const std::vector& ) { + return {}; +} int R_LightForPoint( vec3_t, vec3_t, vec3_t, vec3_t ) { return 0; @@ -209,6 +213,9 @@ refexport_t *GetRefAPI( int, refimport_t* ) re.ClearScene = RE_ClearScene; re.AddRefEntityToScene = RE_AddRefEntityToScene; + re.SyncRefEntities = RE_SyncRefEntities; + re.SyncLerpTags = RE_SyncLerpTags; + re.LightForPoint = R_LightForPoint; re.AddPolyToScene = RE_AddPolyToScene; @@ -231,7 +238,6 @@ refexport_t *GetRefAPI( int, refimport_t* ) re.MarkFragments = R_MarkFragments; - re.LerpTag = R_LerpTag; re.ModelBounds = R_ModelBounds; re.RemapShader = R_RemapShader; diff --git a/src/engine/renderer/EntityCache.cpp b/src/engine/renderer/EntityCache.cpp new file mode 100644 index 0000000000..abc5c4d999 --- /dev/null +++ b/src/engine/renderer/EntityCache.cpp @@ -0,0 +1,285 @@ +/* +=========================================================================== + +Daemon BSD Source Code +Copyright (c) 2025 Daemon Developers +All rights reserved. + +This file is part of the Daemon BSD Source Code (Daemon Source Code). + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Daemon developers nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL DAEMON DEVELOPERS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +=========================================================================== +*/ +// EntityCache.cpp + +#include "tr_local.h" + +#include "EntityCache.h" + +trRefEntity_t entities[MAX_REF_ENTITIES]; +trRefEntity_t entitiesTransformed[MAX_REF_ENTITIES]; + +static constexpr uint32_t blockCount = PAD( MAX_REF_ENTITIES / 64, 64 ); +static uint16_t highestActiveID = 0; +static uint64_t blocks[blockCount]; + +static void PositionEntityOnTag( trRefEntity_t* entity, const refEntity_t* parent, orientation_t* orientation ) { + // FIXME: allow origin offsets along tag? + VectorCopy( parent->origin, entity->e.origin ); + + for ( int i = 0; i < 3; i++ ) { + VectorMA( entity->e.origin, orientation->origin[i], parent->axis[i], entity->e.origin ); + } + + // had to cast away the const to avoid compiler problems... + AxisMultiply( orientation->axis, ( ( refEntity_t* ) parent )->axis, entity->e.axis ); + entity->e.backlerp = parent->backlerp; +} + +static void PositionRotatedEntityOnTag( trRefEntity_t* entity, const refEntity_t* parent, orientation_t* orientation ) { + // FIXME: allow origin offsets along tag? + VectorCopy( parent->origin, entity->e.origin ); + + for ( int i = 0; i < 3; i++ ) { + VectorMA( entity->e.origin, orientation->origin[i], parent->axis[i], entity->e.origin ); + } + + axis_t tempAxis; + AxisMultiply( axisDefault, orientation->axis, tempAxis ); + AxisMultiply( tempAxis, ( ( refEntity_t* ) parent )->axis, entity->e.axis ); +} + +static void BuildSkeleton( trRefEntity_t* ent ) { + if ( ent->e.scale == 0 ) { + ent->e.scale = 1; + } + + ent->skeleton.scale = ent->e.scale; + + if ( ent->e.animationHandle ) { + RE_BuildSkeleton( &ent->skeleton, ent->e.animationHandle, ent->e.startFrame, ent->e.endFrame, + ent->e.lerp, ent->e.clearOrigin ); + + for ( const BoneMod& boneMod : ent->e.boneMods ) { + if ( boneMod.type == 0 ) { + QuatMultiply2( ent->skeleton.bones[boneMod.index].t.rot, boneMod.rotation ); + } + } + } + + refSkeleton_t skel; + if ( ent->e.animationHandle2 ) { + refSkeleton_t* skeleton2 = ent->e.animationHandle ? &skel : &ent->skeleton; + + RE_BuildSkeleton( skeleton2, ent->e.animationHandle2, ent->e.startFrame2, ent->e.endFrame2, + ent->e.lerp2, ent->e.clearOrigin2 ); + + for ( const BoneMod& boneMod : ent->e.boneMods ) { + QuatMultiply2( skeleton2->bones[boneMod.index].t.rot, boneMod.rotation ); + } + + if ( ent->e.animationHandle && ent->e.blendLerp > 0.0 ) { + RE_BlendSkeleton( &ent->skeleton, skeleton2, ent->e.blendLerp ); + } + } + + if ( ent->e.animationHandle || ent->e.animationHandle2 ) { + for ( const BoneMod& boneMod : ent->e.boneMods ) { + if ( boneMod.type == 1 ) { + if ( ent->e.animationHandle ) { + RE_BuildSkeleton( &skel, boneMod.animationHandle, boneMod.startFrame, boneMod.endFrame, + boneMod.lerp, ent->e.clearOrigin ); + } + } else if ( boneMod.type == 2 ) { + ent->skeleton.bones[boneMod.animationHandle] = skel.bones[boneMod.animationHandle]; + } + } + + R_TransformSkeleton( &ent->skeleton, ent->e.scale ); + } else { + ent->skeleton.type = refSkeletonType_t::SK_ABSOLUTE; + ent->skeleton.numBones = MAX_BONES; + for ( int i = 0; i < MAX_BONES; i++ ) { + ent->skeleton.bones[i].parentIndex = -1; + TransInit( &ent->skeleton.bones[i].t ); + } + } + + if ( ent->e.boundsAdd ) { + matrix_t mat; + vec3_t bounds[2]; + + MatrixFromAngles( mat, ent->e.boundsRotation[0], ent->e.boundsRotation[1], ent->e.boundsRotation[2] ); + MatrixTransformBounds( mat, ent->skeleton.bounds[0], ent->skeleton.bounds[1], bounds[0], bounds[1] ); + BoundsAdd( ent->skeleton.bounds[0], ent->skeleton.bounds[1], bounds[0], bounds[1] ); + } +} + +void TransformEntity( trRefEntity_t* ent ) { + if ( ent->transformFrame == tr.frameCount ) { + return; + } + + switch ( ent->e.positionOnTag ) { + case EntityTag::ON_TAG: + { + TransformEntity( &entities[ent->e.attachmentEntity] ); + + orientation_t orientation; + RE_LerpTagET( &orientation, &entities[ent->e.attachmentEntity], ent->e.tag.c_str(), 0 ); + PositionEntityOnTag( ent, &entities[ent->e.attachmentEntity].e, &orientation ); + break; + } + + case EntityTag::ON_TAG_ROTATED: + { + TransformEntity( &entities[ent->e.attachmentEntity] ); + + orientation_t orientation; + RE_LerpTagET( &orientation, &entities[ent->e.attachmentEntity], ent->e.tag.c_str(), 0 ); + PositionRotatedEntityOnTag( ent, &entities[ent->e.attachmentEntity].e, &orientation ); + break; + } + + case EntityTag::NONE: + default: + break; + } + + switch ( ent->e.reType ) { + case refEntityType_t::RT_PORTALSURFACE: + break; + + case refEntityType_t::RT_SPRITE: + break; + + case refEntityType_t::RT_MODEL: + tr.currentModel = R_GetModelByHandle( ent->e.hModel ); + + switch ( tr.currentModel->type ) { + case modtype_t::MOD_MESH: + break; + + case modtype_t::MOD_MD5: + BuildSkeleton( ent ); + break; + + case modtype_t::MOD_IQM: + BuildSkeleton( ent ); + break; + + case modtype_t::MOD_BSP: + case modtype_t::MOD_BAD: + default: + break; + } + + break; + + default: + Sys::Drop( "TransformEntity: Bad reType" ); + } + + ent->transformFrame = tr.frameCount; +} + +void AddRefEntities() { + uint16_t highestFound = 0; + + for ( uint16_t i = 0; i < highestActiveID / 64 + 1; i++ ) { + uint64_t block = blocks[i]; + + while ( block ) { + uint32_t offset = CountTrailingZeroes( block ); + + trRefEntity_t* ent = &entities[offset + i * 64]; + + TransformEntity( ent ); + RE_AddEntityToScene( ent ); + + block &= offset == 63 ? 0 : ( UINT64_MAX << ( offset + 1 ) ); + + highestFound = offset + i * 64; + } + } + + highestActiveID = highestFound; + + // tr.refdef.numEntities = highestActiveID + 1; +} + +void ClearEntityCache() { + highestActiveID = 0; + + memset( blocks, 0, blockCount * sizeof( uint64_t ) ); + memset( entities, 0, MAX_REF_ENTITIES * sizeof( trRefEntity_t ) ); +} + +std::vector SyncEntityCacheToCGame( const std::vector& lerpTags ) { + std::vector entityOrientations; + entityOrientations.reserve( lerpTags.size() ); + + for ( const LerpTagUpdate& tag : lerpTags ) { + TransformEntity( &entities[tag.id] ); + + orientation_t orientation; + RE_LerpTagET( &orientation, &entities[tag.id], tag.tag.c_str(), 0 ); + + orientation_t entOrientation; + VectorCopy( entities[tag.id].e.origin, entOrientation.origin ); + AxisCopy( entities[tag.id].e.axis, entOrientation.axis ); + + entityOrientations.emplace_back( LerpTagSync { entOrientation, orientation } ); + } + + return entityOrientations; +} + +void SyncEntityCacheFromCGame( const std::vector& ents ) { + for ( const EntityUpdate& ent : ents ) { + bool flip = entities[ent.id].e.active != ent.ent.active; + + if ( ent.ent.positionOnTag == EntityTag::NONE || entities[ent.id].transformFrame != tr.frameCount ) { + entities[ent.id].e = ent.ent; + } else { + vec3_t origin; + VectorCopy( entities[ent.id].e.origin, origin ); + + axis_t axis; + AxisCopy( entities[ent.id].e.axis, axis ); + + entities[ent.id].e = ent.ent; + + VectorCopy( origin, entities[ent.id].e.origin ); + AxisCopy( axis, entities[ent.id].e.axis ); + } + + AxisCopy( ent.ent.axis, entities[ent.id].axis ); + blocks[ent.id / 64] ^= ( 1ull << ( ent.id & 63 ) ) * flip; + + if ( ent.ent.active ) { + highestActiveID = std::max( highestActiveID, ent.id ); + } + } +} \ No newline at end of file diff --git a/src/engine/renderer/EntityCache.h b/src/engine/renderer/EntityCache.h new file mode 100644 index 0000000000..09b3f87477 --- /dev/null +++ b/src/engine/renderer/EntityCache.h @@ -0,0 +1,60 @@ +/* +=========================================================================== + +Daemon BSD Source Code +Copyright (c) 2025 Daemon Developers +All rights reserved. + +This file is part of the Daemon BSD Source Code (Daemon Source Code). + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Daemon developers nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL DAEMON DEVELOPERS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +=========================================================================== +*/ +// EntityCache.h + +#ifndef ENTITY_CACHE_H +#define ENTITY_CACHE_H + +#include +#include + +struct centity_t; +struct refEntity_t; +struct trRefEntity_t; +struct orientation_t; + +extern trRefEntity_t entities[MAX_REF_ENTITIES]; +extern trRefEntity_t entitiesTransformed[MAX_REF_ENTITIES]; + +extern int r_numEntities; + +void AddRefEntities(); +void TransformEntity( trRefEntity_t* ent ); + +void ClearEntityCache(); + +std::vector SyncEntityCacheToCGame( const std::vector& lerpTags ); +void SyncEntityCacheFromCGame( const std::vector& ents ); + +#endif // ENTITY_CACHE_H diff --git a/src/engine/renderer/tr_animation.cpp b/src/engine/renderer/tr_animation.cpp index ea4e57160a..ffea0c8f41 100644 --- a/src/engine/renderer/tr_animation.cpp +++ b/src/engine/renderer/tr_animation.cpp @@ -614,7 +614,7 @@ static cullResult_t R_CullMD5( trRefEntity_t *ent ) { int i; - if ( ent->e.skeleton.type == refSkeletonType_t::SK_INVALID ) + if ( ent->skeleton.type == refSkeletonType_t::SK_INVALID ) { // no properly set skeleton so use the bounding box by the model instead by the animations md5Model_t *model = tr.currentModel->md5; @@ -627,8 +627,8 @@ static cullResult_t R_CullMD5( trRefEntity_t *ent ) // copy a bounding box in the current coordinate system provided by skeleton for ( i = 0; i < 3; i++ ) { - ent->localBounds[ 0 ][ i ] = ent->e.skeleton.bounds[ 0 ][ i ] * ent->e.skeleton.scale; - ent->localBounds[ 1 ][ i ] = ent->e.skeleton.bounds[ 1 ][ i ] * ent->e.skeleton.scale; + ent->localBounds[ 0 ][ i ] = ent->skeleton.bounds[ 0 ][ i ] * ent->skeleton.scale; + ent->localBounds[ 1 ][ i ] = ent->skeleton.bounds[ 1 ][ i ] * ent->skeleton.scale; } } @@ -680,7 +680,7 @@ void R_AddMD5Surfaces( trRefEntity_t *ent ) fogNum = R_FogWorldBox( ent->worldBounds ); if ( !r_vboModels.Get() || !model->numVBOSurfaces || - ( !glConfig.vboVertexSkinningAvailable && ent->e.skeleton.type == refSkeletonType_t::SK_ABSOLUTE ) ) + ( !glConfig.vboVertexSkinningAvailable && ent->skeleton.type == refSkeletonType_t::SK_ABSOLUTE ) ) { shader_t *shader; @@ -997,6 +997,32 @@ static int IQMBuildSkeleton( refSkeleton_t *skel, skelAnimation_t *skelAnim, return true; } +void R_TransformSkeleton( refSkeleton_t* skel, const float scale ) { + skel->scale = scale; + + switch ( skel->type ) { + case refSkeletonType_t::SK_INVALID: + case refSkeletonType_t::SK_ABSOLUTE: + return; + + default: + break; + } + + // calculate absolute transforms + for ( refBone_t* bone = &skel->bones[0]; bone < &skel->bones[skel->numBones]; bone++ ) { + if ( bone->parentIndex >= 0 ) { + refBone_t* parent; + + parent = &skel->bones[bone->parentIndex]; + + TransCombine( &bone->t, &parent->t, &bone->t ); + } + } + + skel->type = refSkeletonType_t::SK_ABSOLUTE; +} + /* ============== RE_BuildSkeleton @@ -1152,6 +1178,7 @@ int RE_BuildSkeleton( refSkeleton_t *skel, qhandle_t hAnim, int startFrame, int } // FIXME: clear existing bones and bounds? + skel->numBones = 0; return false; } diff --git a/src/engine/renderer/tr_backend.cpp b/src/engine/renderer/tr_backend.cpp index 0d976790c1..f01eb76146 100644 --- a/src/engine/renderer/tr_backend.cpp +++ b/src/engine/renderer/tr_backend.cpp @@ -1838,9 +1838,9 @@ static void RB_RenderDebugUtils() skel = nullptr; - if ( ent->e.skeleton.type == refSkeletonType_t::SK_ABSOLUTE ) + if ( ent->skeleton.type == refSkeletonType_t::SK_ABSOLUTE ) { - skel = &ent->e.skeleton; + skel = &ent->skeleton; } else { diff --git a/src/engine/renderer/tr_init.cpp b/src/engine/renderer/tr_init.cpp index 5a73b7f95c..342e73ef46 100644 --- a/src/engine/renderer/tr_init.cpp +++ b/src/engine/renderer/tr_init.cpp @@ -27,6 +27,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include "Material.h" #include "GeometryCache.h" #include "GeometryOptimiser.h" +#include "EntityCache.h" #ifdef _WIN32 extern "C" { @@ -1291,6 +1292,8 @@ ScreenshotCmd screenshotPNGRegistration("screenshotPNG", ssFormat_t::SSF_PNG, "p ResetStruct( backEnd ); ResetStruct( tess ); + ClearEntityCache(); + tr.convertFloatFromSRGB = convertFloatFromSRGB_NOP; tr.convertColorFromSRGB = convertColorFromSRGB_NOP; @@ -1598,12 +1601,12 @@ ScreenshotCmd screenshotPNGRegistration("screenshotPNG", ssFormat_t::SSF_PNG, "p re.MarkFragments = R_MarkFragments; - re.LerpTag = RE_LerpTagET; - re.ModelBounds = R_ModelBounds; re.ClearScene = RE_ClearScene; re.AddRefEntityToScene = RE_AddRefEntityToScene; + re.SyncRefEntities = SyncEntityCacheFromCGame; + re.SyncLerpTags = SyncEntityCacheToCGame; re.AddPolyToScene = RE_AddPolyToSceneET; re.AddPolysToScene = RE_AddPolysToScene; diff --git a/src/engine/renderer/tr_local.h b/src/engine/renderer/tr_local.h index f8d815ea99..0b1a9bf0dc 100644 --- a/src/engine/renderer/tr_local.h +++ b/src/engine/renderer/tr_local.h @@ -418,9 +418,12 @@ enum class ssaoMode { { // public from client game refEntity_t e; + refSkeleton_t skeleton; // local + axis_t axis; float axisLength; // compensate for non-normalized axis + int transformFrame; vec3_t localBounds[ 2 ]; vec3_t worldBounds[ 2 ]; @@ -1422,10 +1425,10 @@ enum // 4. index static const uint64_t SORT_INDEX_BITS = 20; - static const uint64_t SORT_ENTITYNUM_BITS = 13; + static const uint64_t SORT_ENTITYNUM_BITS = 16; static const uint64_t SORT_LIGHTMAP_BITS = 9; static const uint64_t SORT_SHADER_BITS = 16; - static const uint64_t SORT_UNUSED_BITS = 6; + static const uint64_t SORT_UNUSED_BITS = 3; static_assert( SORT_SHADER_BITS + SORT_LIGHTMAP_BITS + @@ -2067,14 +2070,14 @@ enum int numLods; }; - void R_ModelInit(); - model_t *R_GetModelByHandle( qhandle_t hModel ); + void R_ModelInit(); + model_t *R_GetModelByHandle( qhandle_t hModel ); - int RE_LerpTagET( orientation_t *tag, const refEntity_t *refent, const char *tagNameIn, int startIndex ); + int RE_LerpTagET( orientation_t* tag, const trRefEntity_t* ent, const char* tagNameIn, int startIndex ); - int RE_BoneIndex( qhandle_t hModel, const char *boneName ); + int RE_BoneIndex( qhandle_t hModel, const char *boneName ); - void R_ModelBounds( qhandle_t handle, vec3_t mins, vec3_t maxs ); + void R_ModelBounds( qhandle_t handle, vec3_t mins, vec3_t maxs ); //==================================================== extern refimport_t ri; @@ -3312,6 +3315,7 @@ void GLimp_LogComment_( std::string comment ); void RE_ClearScene(); void RE_AddRefEntityToScene( const refEntity_t *ent ); + void RE_AddEntityToScene( const trRefEntity_t* ent ); void RE_AddPolyToSceneET( qhandle_t hShader, int numVerts, const polyVert_t *verts ); void RE_AddPolysToScene( qhandle_t hShader, int numVerts, const polyVert_t *verts, int numPolys ); @@ -3350,6 +3354,7 @@ void GLimp_LogComment_( std::string comment ); int RE_CheckSkeleton( refSkeleton_t *skel, qhandle_t hModel, qhandle_t hAnim ); int RE_BuildSkeleton( refSkeleton_t *skel, qhandle_t anim, int startFrame, int endFrame, float frac, bool clearOrigin ); + void R_TransformSkeleton( refSkeleton_t* skel, const float scale ); int RE_BlendSkeleton( refSkeleton_t *skel, const refSkeleton_t *blend, float frac ); int RE_AnimNumFrames( qhandle_t hAnim ); int RE_AnimFrameRate( qhandle_t hAnim ); @@ -3576,9 +3581,9 @@ void GLimp_LogComment_( std::string comment ); refLight_t lights[ MAX_REF_LIGHTS ]; trRefEntity_t entities[ MAX_REF_ENTITIES ]; - srfPoly_t *polys; //[MAX_POLYS]; - polyVert_t *polyVerts; //[MAX_POLYVERTS]; - int *polyIndexes; //[MAX_POLYVERTS]; + srfPoly_t *polys; + polyVert_t *polyVerts; + int *polyIndexes; // the backend communicates to the frontend through visTestResult_t int numVisTests; diff --git a/src/engine/renderer/tr_main.cpp b/src/engine/renderer/tr_main.cpp index 7638d47d1e..8242cfb317 100644 --- a/src/engine/renderer/tr_main.cpp +++ b/src/engine/renderer/tr_main.cpp @@ -23,6 +23,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA // tr_main.c -- main control flow for each frame #include "tr_local.h" #include "Material.h" +#include "EntityCache.h" trGlobals_t tr; @@ -1832,18 +1833,16 @@ R_AddEntitySurfaces */ void R_AddEntitySurfaces() { - int i; - trRefEntity_t *ent; - shader_t *shader; - if ( !r_drawentities->integer ) { return; } - for ( i = 0; i < tr.refdef.numEntities; i++ ) + // AddRefEntities(); + + for ( int i = 0; i < tr.refdef.numEntities; i++ ) { - ent = tr.currentEntity = &tr.refdef.entities[ i ]; + trRefEntity_t* ent = tr.currentEntity = &tr.refdef.entities[ i ]; // // the weapon model must be handled special -- @@ -1863,19 +1862,19 @@ void R_AddEntitySurfaces() break; // don't draw anything case refEntityType_t::RT_SPRITE: - + { // self blood sprites, talk balloons, etc should not be drawn in the primary // view. We can't just do this check for all entities, because md3 // entities may still want to cast shadows from them if ( ( ent->e.renderfx & RF_THIRD_PERSON ) && - tr.viewParms.portalLevel == 0 ) - { + tr.viewParms.portalLevel == 0 ) { continue; } - shader = R_GetShaderByHandle( ent->e.customShader ); + shader_t* shader = R_GetShaderByHandle( ent->e.customShader ); R_AddDrawSurf( &entitySurface, shader, -1, R_SpriteFogNum( ent ) ); break; + } case refEntityType_t::RT_MODEL: // we must set up parts of tr.or for model culling @@ -1889,8 +1888,7 @@ void R_AddEntitySurfaces() } else { - switch ( tr.currentModel->type ) - { + switch ( tr.currentModel->type ) { case modtype_t::MOD_MESH: R_AddMDVSurfaces( ent ); break; @@ -1909,16 +1907,14 @@ void R_AddEntitySurfaces() case modtype_t::MOD_BAD: // null model axis if ( ( ent->e.renderfx & RF_THIRD_PERSON ) && - tr.viewParms.portalLevel == 0 ) - { + tr.viewParms.portalLevel == 0 ) { break; } - VectorClear( ent->localBounds[ 0 ] ); - VectorClear( ent->localBounds[ 1 ] ); - VectorClear( ent->worldBounds[ 0 ] ); - VectorClear( ent->worldBounds[ 1 ] ); - shader = R_GetShaderByHandle( ent->e.customShader ); + VectorClear( ent->localBounds[0] ); + VectorClear( ent->localBounds[1] ); + VectorClear( ent->worldBounds[0] ); + VectorClear( ent->worldBounds[1] ); R_AddDrawSurf( &entitySurface, tr.defaultShader, -1, 0 ); break; diff --git a/src/engine/renderer/tr_model.cpp b/src/engine/renderer/tr_model.cpp index 1e615b833d..43d10b437c 100644 --- a/src/engine/renderer/tr_model.cpp +++ b/src/engine/renderer/tr_model.cpp @@ -427,45 +427,35 @@ static int R_GetTag( mdvModel_t *model, int frame, const char *_tagName, int sta RE_LerpTag ================ */ -int RE_LerpTagET( orientation_t *tag, const refEntity_t *refent, const char *tagNameIn, int startIndex ) +int RE_LerpTagET( orientation_t* tag, const trRefEntity_t* ent, const char* tagNameIn, int startIndex ) { - mdvTag_t *start, *end; - int i; - float frontLerp, backLerp; - model_t *model; - char tagName[ MAX_QPATH ]; //, *ch; - int retval; - qhandle_t handle; - int startFrame, endFrame; - float frac; - - handle = refent->hModel; - startFrame = refent->oldframe; - endFrame = refent->frame; - frac = 1.0 - refent->backlerp; + qhandle_t handle = ent->e.hModel; + int startFrame = ent->e.oldframe; + int endFrame = ent->e.frame; + char tagName[ MAX_QPATH ]; Q_strncpyz( tagName, tagNameIn, MAX_QPATH ); - model = R_GetModelByHandle( handle ); + model_t* model = R_GetModelByHandle( handle ); - frontLerp = frac; - backLerp = 1.0 - frac; + float frac = 1.0f - ent->e.backlerp; + float frontLerp = frac; + float backLerp = 1.0f - frac; - start = end = nullptr; if ( model->type == modtype_t::MOD_MD5 || model->type == modtype_t::MOD_IQM ) { vec3_t tmp; - retval = RE_BoneIndex( handle, tagName ); + int retval = RE_BoneIndex( handle, tagName ); if ( retval <= 0 ) { return -1; } - VectorScale( refent->skeleton.bones[ retval ].t.trans, - refent->skeleton.scale, tag->origin ); - QuatToAxis( refent->skeleton.bones[ retval ].t.rot, tag->axis ); + VectorScale( ent->skeleton.bones[ retval ].t.trans, + ent->skeleton.scale, tag->origin ); + QuatToAxis( ent->skeleton.bones[ retval ].t.rot, tag->axis ); VectorCopy( tag->axis[ 2 ], tmp ); VectorCopy( tag->axis[ 1 ], tag->axis[ 2 ] ); VectorCopy( tag->axis[ 0 ], tag->axis[ 1 ] ); @@ -478,8 +468,10 @@ int RE_LerpTagET( orientation_t *tag, const refEntity_t *refent, const char *tag else if ( model->type == modtype_t::MOD_MESH ) { // old MD3 style - retval = R_GetTag( model->mdv[ 0 ], startFrame, tagName, startIndex, &start ); - retval = R_GetTag( model->mdv[ 0 ], endFrame, tagName, startIndex, &end ); + mdvTag_t* start = nullptr; + mdvTag_t* end = nullptr; + int retval = R_GetTag( model->mdv[ 0 ], startFrame, tagName, startIndex, &start ); + retval = R_GetTag( model->mdv[ 0 ], endFrame, tagName, startIndex, &end ); if ( !start || !end ) @@ -489,7 +481,7 @@ int RE_LerpTagET( orientation_t *tag, const refEntity_t *refent, const char *tag return -1; } - for ( i = 0; i < 3; i++ ) + for ( int i = 0; i < 3; i++ ) { tag->origin[ i ] = start->origin[ i ] * backLerp + end->origin[ i ] * frontLerp; tag->axis[ 0 ][ i ] = start->axis[ 0 ][ i ] * backLerp + end->axis[ 0 ][ i ] * frontLerp; diff --git a/src/engine/renderer/tr_model_iqm.cpp b/src/engine/renderer/tr_model_iqm.cpp index 938b3d5089..0be23d42c9 100644 --- a/src/engine/renderer/tr_model_iqm.cpp +++ b/src/engine/renderer/tr_model_iqm.cpp @@ -937,7 +937,7 @@ R_CullIQM */ static cullResult_t R_CullIQM( trRefEntity_t *ent ) { vec3_t localBounds[ 2 ]; - float scale = ent->e.skeleton.scale; + float scale = ent->skeleton.scale; IQModel_t *model = tr.currentModel->iqm; IQAnim_t *anim = model->anims; float *bounds; @@ -955,7 +955,7 @@ static cullResult_t R_CullIQM( trRefEntity_t *ent ) { // merge bounding box provided by skeleton BoundsAdd( localBounds[ 0 ], localBounds[ 1 ], - ent->e.skeleton.bounds[ 0 ], ent->e.skeleton.bounds[ 1 ] ); + ent->skeleton.bounds[ 0 ], ent->skeleton.bounds[ 1 ] ); VectorScale( localBounds[0], scale, ent->localBounds[ 0 ] ); VectorScale( localBounds[1], scale, ent->localBounds[ 1 ] ); diff --git a/src/engine/renderer/tr_public.h b/src/engine/renderer/tr_public.h index 2c02e317ce..f1a5a514ca 100644 --- a/src/engine/renderer/tr_public.h +++ b/src/engine/renderer/tr_public.h @@ -214,6 +214,8 @@ struct refexport_t // Nothing is drawn until R_RenderScene is called. void ( *ClearScene )(); void ( *AddRefEntityToScene )( const refEntity_t *re ); + void ( *SyncRefEntities )( const std::vector& ents ); + std::vector ( *SyncLerpTags )( const std::vector& lerpTags ); int ( *LightForPoint )( vec3_t point, vec3_t ambientLight, vec3_t directedLight, vec3_t lightDir ); @@ -240,7 +242,6 @@ struct refexport_t int ( *MarkFragments )( int numPoints, const vec3_t *points, const vec3_t projection, int maxPoints, vec3_t pointBuffer, int maxFragments, markFragment_t *fragmentBuffer ); - int ( *LerpTag )( orientation_t *tag, const refEntity_t *refent, const char *tagName, int startIndex ); void ( *ModelBounds )( qhandle_t model, vec3_t mins, vec3_t maxs ); void ( *RemapShader )( const char *oldShader, const char *newShader, const char *offsetTime ); diff --git a/src/engine/renderer/tr_scene.cpp b/src/engine/renderer/tr_scene.cpp index 2db9d3d3e7..60520420e6 100644 --- a/src/engine/renderer/tr_scene.cpp +++ b/src/engine/renderer/tr_scene.cpp @@ -20,9 +20,10 @@ along with Daemon source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ -// tr_scene.c +// tr_scene.cpp #include "tr_local.h" #include "Material.h" +#include "EntityCache.h" static Cvar::Cvar r_drawDynamicLights( "r_drawDynamicLights", "render dynamic lights (if realtime lighting is enabled)", Cvar::NONE, true ); @@ -32,7 +33,7 @@ static int r_firstSceneDrawSurf; static int r_numLights; static int r_firstSceneLight; -static int r_numEntities; +int r_numEntities; static int r_firstSceneEntity; static int r_numPolys; @@ -273,6 +274,27 @@ void RE_AddRefEntityToScene( const refEntity_t *ent ) backEndData[ tr.smpFrame ]->entities[ r_numEntities ].e = *ent; + TransformEntity( &backEndData[tr.smpFrame]->entities[r_numEntities] ); + + r_numEntities++; +} + +void RE_AddEntityToScene( const trRefEntity_t* ent ) { + if ( !tr.registered ) { + return; + } + + // Tr3B: fixed was ENTITYNUM_WORLD + if ( r_numEntities >= MAX_REF_ENTITIES ) { + return; + } + + if ( ent->e.reType >= refEntityType_t::RT_MAX_REF_ENTITY_TYPE ) { + Sys::Drop( "RE_AddRefEntityToScene: bad reType %s", Util::enum_str( ent->e.reType ) ); + } + + backEndData[tr.smpFrame]->entities[r_numEntities] = *ent; + r_numEntities++; } @@ -540,6 +562,8 @@ void RE_RenderScene( const refdef_t *fd ) tr.refdef.floatTime = float( double( tr.refdef.time ) * 0.001 ); } + AddRefEntities(); + tr.refdef.numDrawSurfs = r_firstSceneDrawSurf; tr.refdef.drawSurfs = backEndData[ tr.smpFrame ]->drawSurfs; diff --git a/src/engine/renderer/tr_surface.cpp b/src/engine/renderer/tr_surface.cpp index 93e10131d3..c94683ae40 100644 --- a/src/engine/renderer/tr_surface.cpp +++ b/src/engine/renderer/tr_surface.cpp @@ -999,15 +999,15 @@ static void Tess_SurfaceMD5( md5Surface_t *srf ) Tess_CheckOverflow( srf->numVerts, numIndexes ); - vec_t entityScale = backEnd.currentEntity->e.skeleton.scale; + vec_t entityScale = backEnd.currentEntity->skeleton.scale; float modelScale = model->internalScale; transform_t *bone = bones; transform_t *lastBone = bones + model->numBones; // Convert bones back to matrices. - if ( backEnd.currentEntity->e.skeleton.type == refSkeletonType_t::SK_ABSOLUTE ) + if ( backEnd.currentEntity->skeleton.type == refSkeletonType_t::SK_ABSOLUTE ) { - refBone_t *entityBone = backEnd.currentEntity->e.skeleton.bones; + refBone_t *entityBone = backEnd.currentEntity->skeleton.bones; md5Bone_t *modelBone = model->bones; for ( ; bone < lastBone; bone++, @@ -1146,15 +1146,15 @@ void Tess_SurfaceIQM( srfIQModel_t *surf ) { int numIndexes = surf->num_triangles * 3; - vec_t entityScale = backEnd.currentEntity->e.skeleton.scale; + vec_t entityScale = backEnd.currentEntity->skeleton.scale; float modelScale = model->internalScale; transform_t *bone = bones; transform_t *lastBone = bones + model->num_joints; // Convert bones back to matrices. - if ( backEnd.currentEntity->e.skeleton.type == refSkeletonType_t::SK_ABSOLUTE ) + if ( backEnd.currentEntity->skeleton.type == refSkeletonType_t::SK_ABSOLUTE ) { - refBone_t *entityBone = backEnd.currentEntity->e.skeleton.bones; + refBone_t *entityBone = backEnd.currentEntity->skeleton.bones; transform_t *modelJoint = model->joints; for ( ; bone < lastBone; bone++, @@ -1205,7 +1205,7 @@ void Tess_SurfaceIQM( srfIQModel_t *surf ) { } else { - TransInitScale( model->internalScale * backEnd.currentEntity->e.skeleton.scale, &tess.bones[ 0 ] ); + TransInitScale( model->internalScale * backEnd.currentEntity->skeleton.scale, &tess.bones[ 0 ] ); tess.numBones = 1; } @@ -1333,7 +1333,7 @@ void Tess_SurfaceIQM( srfIQModel_t *surf ) { } else { - float scale = model->internalScale * backEnd.currentEntity->e.skeleton.scale; + float scale = model->internalScale * backEnd.currentEntity->skeleton.scale; for ( ; tessVertex < lastVertex; tessVertex++, modelPosition += 3, modelNormal += 3, @@ -1462,19 +1462,19 @@ static void Tess_SurfaceVBOMD5Mesh( srfVBOMD5Mesh_t *srf ) tess.numBones = srf->numBoneRemap; tess.vboVertexSkinning = true; - vec_t entityScale = backEnd.currentEntity->e.skeleton.scale; + vec_t entityScale = backEnd.currentEntity->skeleton.scale; float modelScale = model->internalScale; transform_t *bone = tess.bones; transform_t *lastBone = bone + tess.numBones; - if ( backEnd.currentEntity->e.skeleton.type == refSkeletonType_t::SK_ABSOLUTE ) + if ( backEnd.currentEntity->skeleton.type == refSkeletonType_t::SK_ABSOLUTE ) { int *boneRemapInverse = srf->boneRemapInverse; for ( ; bone < lastBone; bone++, boneRemapInverse++ ) { - refBone_t *entityBone = &backEnd.currentEntity->e.skeleton.bones[ *boneRemapInverse ]; + refBone_t *entityBone = &backEnd.currentEntity->skeleton.bones[ *boneRemapInverse ]; md5Bone_t *modelBone = &model->bones[ *boneRemapInverse ]; TransInverse( &modelBone->joint, bone ); diff --git a/src/engine/renderer/tr_types.h b/src/engine/renderer/tr_types.h index deedd1381a..604bdcbb60 100644 --- a/src/engine/renderer/tr_types.h +++ b/src/engine/renderer/tr_types.h @@ -49,7 +49,7 @@ using bool8_t = uint8_t; // XreaL BEGIN #define MAX_REF_LIGHTS 1024 -#define MAX_REF_ENTITIES 8191 // can't be increased without changing drawsurf bit packing +#define MAX_REF_ENTITIES 16384 // can't be increased without changing drawsurf bit packing #define MAX_BONES 256 #define MAX_WEIGHTS 4 // GPU vertex skinning limit, never change this without rewriting many GLSL shaders // XreaL END @@ -153,6 +153,18 @@ enum class refSkeletonType_t SK_ABSOLUTE }; +struct BoneMod { + int index; + vec3_t translation; + quat_t rotation; + int type; + int count; + int animationHandle; + int startFrame; + int endFrame; + float lerp; +}; + struct alignas(16) refSkeleton_t { refSkeletonType_t type; // skeleton has been reset @@ -167,6 +179,12 @@ struct alignas(16) refSkeleton_t // XreaL END +enum class EntityTag { + NONE, + ON_TAG, + ON_TAG_ROTATED +}; + struct refEntity_t { refEntityType_t reType; @@ -175,15 +193,9 @@ struct refEntity_t qhandle_t hModel; // opaque type outside refresh // most recent data - vec3_t lightingOrigin; // so multi-part models can be lit identically (RF_LIGHTING_ORIGIN) - - vec3_t axis[ 3 ]; // rotation vectors - bool8_t nonNormalizedAxes; // axis are not normalized, i.e. they have scale - vec3_t origin; int frame; // previous data for frame interpolation - vec3_t oldorigin; // also used as MODEL_BEAM's "to" int oldframe; float backlerp; // 0.0 = current, 1.0 = old @@ -193,7 +205,6 @@ struct refEntity_t qhandle_t customShader; // use one image for the entire thing // misc - Color::Color32Bit shaderRGBA; // colors used by rgbgen entity shaders float shaderTexCoord[ 2 ]; // texture coordinates used by tcMod entity modifiers float shaderTime; // subtracted from refdef time to control effect start times @@ -203,11 +214,59 @@ struct refEntity_t int altShaderIndex; - // KEEP SKELETON AT THE END OF THE STRUCTURE - // it is to make a serialization hack for refEntity_t easier - // by memcpying up to skeleton and then serializing skeleton - refSkeleton_t skeleton; + // Skeleton information + qhandle_t animationHandle; + int startFrame; + int endFrame; + float lerp; + int clearOrigin; + + qhandle_t animationHandle2; + int startFrame2; + int endFrame2; + float lerp2; + int clearOrigin2; + + float blendLerp; + float scale; + + int boundsAdd; + + EntityTag positionOnTag = EntityTag::NONE; + int attachmentEntity; + + vec3_t lightingOrigin; // so multi-part models can be lit identically (RF_LIGHTING_ORIGIN) + + vec3_t axis[3]; // rotation vectors + bool8_t nonNormalizedAxes; // axis are not normalized, i.e. they have scale + + vec3_t origin; + vec3_t oldorigin; // also used as MODEL_BEAM's "to" + + Color::Color32Bit shaderRGBA; // colors used by rgbgen entity shaders + + bool8_t active; + + vec3_t boundsRotation; + + std::string tag; + + std::vector boneMods; +}; + +struct EntityUpdate { + refEntity_t ent; + uint16_t id; +}; + +struct LerpTagUpdate { + std::string tag; + uint16_t id; +}; +struct LerpTagSync { + orientation_t entityOrientation; + orientation_t orientation; }; // ================================================================================================ diff --git a/src/shared/client/cg_api.cpp b/src/shared/client/cg_api.cpp index dc9bb6cce8..c9ce8db897 100644 --- a/src/shared/client/cg_api.cpp +++ b/src/shared/client/cg_api.cpp @@ -283,9 +283,27 @@ void trap_R_ClearScene() cmdBuffer.SendMsg(); } -void trap_R_AddRefEntityToScene( const refEntity_t *re ) +/* HACK: We need the entityNum to get the correct positions for entities that need to be attached to another entity's bone +This must be equal to the r_numEntities in engine at the time of adding the entity */ +static int entityNum; +int trap_R_AddRefEntityToScene( const refEntity_t *re ) { cmdBuffer.SendMsg(*re); + entityNum++; + return entityNum - 1; +} + +void trap_R_SyncRefEntities( const std::vector& ents ) { + cmdBuffer.SendMsg( ents ); +} + +std::vector trap_R_SyncLerpTags( const std::vector& lerpTags ) { + std::vector out; + out.reserve( lerpTags.size() ); + + VM::SendMsg( lerpTags, out ); + + return out; } void trap_R_AddPolyToScene( qhandle_t hShader, int numVerts, const polyVert_t *verts ) @@ -350,6 +368,7 @@ void trap_R_AddLightToScene( const vec3_t origin, float radius, float intensity, void trap_R_RenderScene( const refdef_t *fd ) { + entityNum = 0; cmdBuffer.SendMsg(*fd); } @@ -391,13 +410,6 @@ void trap_R_ModelBounds( clipHandle_t model, vec3_t mins, vec3_t maxs ) VectorCopy(mymaxs, maxs); } -int trap_R_LerpTag( orientation_t *tag, const refEntity_t *refent, const char *tagName, int startIndex ) -{ - int result; - VM::SendMsg(*refent, tagName, startIndex, *tag, result); - return result; -} - void trap_R_RemapShader( const char *oldShader, const char *newShader, const char *timeOffset ) { VM::SendMsg(oldShader, newShader, timeOffset); @@ -436,6 +448,7 @@ qhandle_t trap_R_RegisterAnimation( const char *name ) int trap_R_BuildSkeleton( refSkeleton_t *skel, qhandle_t anim, int startFrame, int endFrame, float frac, bool clearOrigin ) { int result; + skel->numBones = 0; VM::SendMsg(anim, startFrame, endFrame, frac, clearOrigin, *skel, result); return result; } @@ -448,7 +461,7 @@ int trap_R_BlendSkeleton( refSkeleton_t *skel, const refSkeleton_t *blend, float if ( skel->numBones != blend->numBones ) { - Log::Warn("trap_R_BlendSkeleton: different number of bones %d != %d", skel->numBones, blend->numBones); + // Log::Warn("trap_R_BlendSkeleton: different number of bones %d != %d", skel->numBones, blend->numBones); return false; } diff --git a/src/shared/client/cg_api.h b/src/shared/client/cg_api.h index b748cf11d6..3784c5a0dc 100644 --- a/src/shared/client/cg_api.h +++ b/src/shared/client/cg_api.h @@ -65,7 +65,9 @@ qhandle_t trap_R_RegisterModel( const char *name ); qhandle_t trap_R_RegisterSkin( const char *name ); qhandle_t trap_R_RegisterShader( const char *name, int flags ); void trap_R_ClearScene(); -void trap_R_AddRefEntityToScene( const refEntity_t *re ); +int trap_R_AddRefEntityToScene( const refEntity_t *re ); +void trap_R_SyncRefEntities( const std::vector& ents ); +std::vector trap_R_SyncLerpTags( const std::vector& lerpTags ); void trap_R_AddPolyToScene( qhandle_t hShader, int numVerts, const polyVert_t *verts ); void trap_R_AddPolysToScene( qhandle_t hShader, int numVerts, const polyVert_t *verts, int numPolys ); void trap_R_AddLightToScene( const vec3_t origin, float radius, float intensity, float r, float g, float b, int flags ); @@ -80,7 +82,7 @@ void trap_R_ResetClipRegion(); void trap_R_DrawStretchPic( float x, float y, float w, float h, float s1, float t1, float s2, float t2, qhandle_t hShader ); void trap_R_DrawRotatedPic( float x, float y, float w, float h, float s1, float t1, float s2, float t2, qhandle_t hShader, float angle ); void trap_R_ModelBounds( clipHandle_t model, vec3_t mins, vec3_t maxs ); -int trap_R_LerpTag( orientation_t *tag, const refEntity_t *refent, const char *tagName, int startIndex ); +int trap_R_LerpTag( orientation_t *tag, const refEntity_t* refent, const char *tagName, int startIndex ); void trap_R_GetTextureSize( qhandle_t handle, int *x, int *y ); qhandle_t trap_R_GenerateTexture( const byte *data, int x, int y ); void trap_GetCurrentSnapshotNumber( int *snapshotNumber, int *serverTime );