diff --git a/src/common/Compiler.h b/src/common/Compiler.h index 3add821f49..25516fe008 100644 --- a/src/common/Compiler.h +++ b/src/common/Compiler.h @@ -64,12 +64,15 @@ inline int CountTrailingZeroes(unsigned long long x) { unsigned long ans; #ifdef _WIN64 - _BitScanForward64(&ans, x); return ans; + _BitScanForward64(&ans, x); + return ans; #else bool nonzero = _BitScanForward(&ans, static_cast(x)); - if (!nonzero) { _BitScanForward(&ans, x >> 32); } - #endif + if (!nonzero) { + _BitScanForward(&ans, x >> 32); + } return ans; + #endif } #else inline int CountTrailingZeroes(unsigned int x) diff --git a/src/engine/RefAPI.h b/src/engine/RefAPI.h index 79b7b2be8f..311c7f185b 100644 --- a/src/engine/RefAPI.h +++ b/src/engine/RefAPI.h @@ -113,6 +113,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 ); void ( *AddPolyToScene )( qhandle_t hShader, int numVerts, const polyVert_t* verts ); void ( *AddPolysToScene )( qhandle_t hShader, int numVerts, const polyVert_t* verts, int numPolys ); @@ -139,7 +141,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/client/cg_msgdef.h b/src/engine/client/cg_msgdef.h index 9e32cd0cff..2fa99c8a98 100644 --- a/src/engine/client/cg_msgdef.h +++ b/src/engine/client/cg_msgdef.h @@ -91,22 +91,82 @@ 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 ); } + 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>(); + 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) @@ -168,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, @@ -181,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_REGISTERANIMATION, @@ -325,10 +386,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< @@ -381,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 71504477c5..d716c90b0b 100644 --- a/src/engine/client/cl_cgame.cpp +++ b/src/engine/client/cl_cgame.cpp @@ -1141,6 +1141,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; @@ -1257,12 +1264,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()); @@ -1633,6 +1634,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 c81158aef7..5f84d35477 100644 --- a/src/engine/null/null_renderer.cpp +++ b/src/engine/null/null_renderer.cpp @@ -79,6 +79,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 {}; +} void RE_AddPolyToScene( qhandle_t, int, const polyVert_t* ) { } void RE_AddPolysToScene( qhandle_t, int, const polyVert_t*, int ) { } void RE_AddLightToScene( const vec3_t, float, float, float, float, int ) { } @@ -203,6 +207,8 @@ refexport_t *GetRefAPI( int, refimport_t* ) re.ClearScene = RE_ClearScene; re.AddRefEntityToScene = RE_AddRefEntityToScene; + re.SyncRefEntities = RE_SyncRefEntities; + re.SyncLerpTags = RE_SyncLerpTags; re.AddPolyToScene = RE_AddPolyToScene; // Ridah @@ -224,7 +230,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..5b0c17c842 --- /dev/null +++ b/src/engine/renderer/EntityCache.cpp @@ -0,0 +1,312 @@ +/* +=========================================================================== + +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] {}; + +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 == BONE_ROTATE ) { + QuatMultiply2( ent->skeleton.bones[boneMod.index].t.rot, boneMod.rotation ); + } + } + } + + refSkeleton_t skel {}; + + /* This follows the original weird and buggy code in cgame + This is used to make alien attack animations blend with the movement animation + Unlike human models, aliens use the same refEntity_t for all their animations (referred to as `legs` in cgame) + The intent seems to have been to blend last and current movement animation, then blend them with the attack animation + (the attack animation isn't blended with the one from the previous frame for whatever reason) */ + for ( const BoneMod& boneMod : ent->e.boneMods ) { + if ( boneMod.type == BUILD_EXTRA_BLEND_SKELETON ) { + if ( ent->e.animationHandle ) { + RE_BuildSkeleton( &skel, boneMod.animationHandle, boneMod.startFrame, boneMod.endFrame, + boneMod.lerp, ent->e.clearOrigin ); + RE_BlendSkeleton( &ent->skeleton, &skel, boneMod.blendLerp ); + } + + break; + } + } + + 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 ) { + if ( boneMod.type == BONE_ROTATE ) { + 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 == BUILD_EXTRA_SKELETON ) { + if ( ent->e.animationHandle ) { + RE_BuildSkeleton( &skel, boneMod.animationHandle, boneMod.startFrame, boneMod.endFrame, + boneMod.lerp, ent->e.clearOrigin ); + } + } else if ( boneMod.type == BONE_FROM_EXTRA_SKELETON ) { + ent->skeleton.bones[boneMod.index] = skel.bones[boneMod.index]; + } + } + + 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 ) { + // FIXME: for some reason using == here breaks jet animation + 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 ); + + const vec4_t& dynamicLight = ent->e.dynamicLight; + + if ( dynamicLight[3] ) { + RE_AddDynamicLightToScene( ent->e.origin, dynamicLight[3], dynamicLight[0], dynamicLight[1], dynamicLight[2], 0 ); + } + + block &= offset == 63 ? 0 : ( UINT64_MAX << ( offset + 1 ) ); + + highestFound = offset + i * 64; + } + } + + highestActiveID = highestFound; +} + +void ClearEntityCache() { + highestActiveID = 0; + + memset( blocks, 0, blockCount * sizeof( uint64_t ) ); + + for ( trRefEntity_t& ent : entities ) { + ent = {}; + } +} + +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..9540d8ca2e --- /dev/null +++ b/src/engine/renderer/EntityCache.h @@ -0,0 +1,59 @@ +/* +=========================================================================== + +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 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/src.cmake b/src/engine/renderer/src.cmake index 98ef800b31..d333417831 100644 --- a/src/engine/renderer/src.cmake +++ b/src/engine/renderer/src.cmake @@ -14,6 +14,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/renderer/tr_animation.cpp b/src/engine/renderer/tr_animation.cpp index f0dd4670b5..fa758258e6 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; } } @@ -676,7 +676,7 @@ void R_AddMD5Surfaces( trRefEntity_t *ent ) } 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; @@ -993,6 +993,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 @@ -1148,6 +1174,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 f1921b96c6..2b6c2791b4 100644 --- a/src/engine/renderer/tr_backend.cpp +++ b/src/engine/renderer/tr_backend.cpp @@ -31,6 +31,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #endif backEndData_t *backEndData[ SMP_FRAMES ]; +trRefEntity_t backEndEntities[SMP_FRAMES][MAX_REF_ENTITIES]; + backEndState_t backEnd; static Cvar::Cvar r_clear( "r_clear", "Clear screen before painting over it on every frame", Cvar::NONE, false ); @@ -1843,9 +1845,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 8d496d1b47..e5a13fb05b 100644 --- a/src/engine/renderer/tr_init.cpp +++ b/src/engine/renderer/tr_init.cpp @@ -28,6 +28,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" { @@ -1317,6 +1318,8 @@ ScreenshotCmd screenshotPNGRegistration("screenshotPNG", ssFormat_t::SSF_PNG, "p ResetStruct( backEnd ); ResetStruct( tess ); + ClearEntityCache(); + tr.convertFloatFromSRGB = convertFloatFromSRGB_NOP; tr.convertColorFromSRGB = convertColorFromSRGB_NOP; @@ -1666,12 +1669,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 68328fde7d..748522d89e 100644 --- a/src/engine/renderer/tr_local.h +++ b/src/engine/renderer/tr_local.h @@ -443,9 +443,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 ]; @@ -1433,10 +1436,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 + @@ -2086,14 +2089,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; @@ -3325,6 +3328,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 ); @@ -3365,6 +3369,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 ); @@ -3589,11 +3594,10 @@ void GLimp_LogComment_( std::string comment ); drawSurf_t drawSurfs[ MAX_DRAWSURFS ]; 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; @@ -3606,6 +3610,8 @@ void GLimp_LogComment_( std::string comment ); }; extern backEndData_t *backEndData[ SMP_FRAMES ]; // the second one may not be allocated + // Outside of backEndData because refEntity_t contains std::vector and std::string, while backEndData is dynamically allocated + extern trRefEntity_t backEndEntities[SMP_FRAMES][MAX_REF_ENTITIES]; extern volatile bool renderThreadActive; diff --git a/src/engine/renderer/tr_main.cpp b/src/engine/renderer/tr_main.cpp index 8afd91fc3f..8211b92036 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; @@ -1750,18 +1751,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 -- @@ -1781,19 +1780,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 ); break; + } case refEntityType_t::RT_MODEL: // we must set up parts of tr.or for model culling @@ -1807,8 +1806,7 @@ void R_AddEntitySurfaces() } else { - switch ( tr.currentModel->type ) - { + switch ( tr.currentModel->type ) { case modtype_t::MOD_MESH: R_AddMDVSurfaces( ent ); break; @@ -1827,17 +1825,15 @@ 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 ); - R_AddDrawSurf( &entitySurface, tr.defaultShader, -1 ); + VectorClear( ent->localBounds[0] ); + VectorClear( ent->localBounds[1] ); + VectorClear( ent->worldBounds[0] ); + VectorClear( ent->worldBounds[1] ); + R_AddDrawSurf( &entitySurface, tr.defaultShader, -1, 0 ); break; default: 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 eb7feb11b2..a3b35fc0aa 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_scene.cpp b/src/engine/renderer/tr_scene.cpp index 4bd3e5b34a..e95c50c898 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; @@ -290,7 +291,28 @@ void RE_AddRefEntityToScene( const refEntity_t *ent ) Sys::Drop("RE_AddRefEntityToScene: bad reType %s", Util::enum_str(ent->reType)); } - backEndData[ tr.smpFrame ]->entities[ r_numEntities ].e = *ent; + backEndEntities[ tr.smpFrame ][ r_numEntities ].e = *ent; + + TransformEntity( &backEndEntities[tr.smpFrame][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 ) ); + } + + backEndEntities[tr.smpFrame][r_numEntities] = *ent; r_numEntities++; } @@ -547,11 +569,13 @@ 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; tr.refdef.numEntities = r_numEntities - r_firstSceneEntity; - tr.refdef.entities = &backEndData[ tr.smpFrame ]->entities[ r_firstSceneEntity ]; + tr.refdef.entities = &backEndEntities[ tr.smpFrame ][ r_firstSceneEntity ]; tr.refdef.numLights = r_numLights - r_firstSceneLight; tr.refdef.lights = &backEndData[ tr.smpFrame ]->lights[ r_firstSceneLight ]; diff --git a/src/engine/renderer/tr_surface.cpp b/src/engine/renderer/tr_surface.cpp index be89199b85..328675776f 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++, @@ -1151,15 +1151,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++, @@ -1210,7 +1210,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; } @@ -1348,7 +1348,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; #pragma omp parallel for for ( int i = 0; i < surf->num_vertexes; i++ ) @@ -1483,19 +1483,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 eb0b068c56..624ab5e964 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 @@ -57,12 +57,21 @@ using bool8_t = uint8_t; #define MAX_ENTITIES MAX_REF_ENTITIES // RB: for compatibility // renderfx flags -#define RF_THIRD_PERSON 0x000002 // don't draw through eyes, only mirrors (player bodies, chat sprites) -#define RF_FIRST_PERSON 0x000004 // only draw through eyes (view weapon, damage blood blob) -#define RF_DEPTHHACK 0x000008 // for view weapon Z crunching -#define RF_NOSHADOW 0x000010 // don't add stencil shadows +enum RenderFx : uint8_t { + RF_THIRD_PERSON = 0x000001, // don't draw through eyes, only mirrors (player bodies, chat sprites) + RF_FIRST_PERSON = 0x000002, // only draw through eyes (view weapon, damage blood blob) + RF_DEPTHHACK = 0x000004, // for view weapon Z crunching + RF_NOSHADOW = 0x000008, // don't add stencil shadows + RF_SWAPCULL = 0x000010 // swap CT_FRONT_SIDED and CT_BACK_SIDED +}; + +inline RenderFx operator|( const RenderFx& lhs, const RenderFx& rhs ) { + return ( RenderFx ) ( ( uint8_t ) lhs | ( uint8_t ) rhs ); +} -#define RF_SWAPCULL 0x000040 // swap CT_FRONT_SIDED and CT_BACK_SIDED +inline RenderFx operator|=( const RenderFx& lhs, const RenderFx& rhs ) { + return ( RenderFx ) ( ( uint8_t ) lhs | ( uint8_t ) rhs ); +} // refdef flags #define RDF_NOWORLDMODEL ( 1 << 0 ) // used for player configuration screen @@ -120,7 +129,7 @@ struct poly_t polyVert_t *verts; }; -enum class refEntityType_t +enum class refEntityType_t : int8_t { RT_MODEL, @@ -154,6 +163,28 @@ enum class refSkeletonType_t SK_ABSOLUTE }; +enum BoneModType { + BONE_ROTATE, // Rotates the bone at BoneMod::index by BoneMod::rotation + /* Builds an extra skeleton from refEntity_t::animationHandle, use animationHandle2 for the main skeleton, + * Used to combine torso and legs sketons for human models since they use the same refEntity */ + BUILD_EXTRA_SKELETON, + BONE_FROM_EXTRA_SKELETON, // Use bone BoneMod::animationHandle from the extra skeleton instead of the main one + BUILD_EXTRA_BLEND_SKELETON +}; + +struct BoneMod { + int index; + vec3_t translation; + quat_t rotation; + int type = 0; + int count; + int animationHandle; + int startFrame; + int endFrame; + float lerp; + float blendLerp; +}; + struct alignas(16) refSkeleton_t { refSkeletonType_t type; // skeleton has been reset @@ -168,22 +199,21 @@ struct alignas(16) refSkeleton_t // XreaL END +enum EntityTag : uint8_t { + NONE, + ON_TAG, + ON_TAG_ROTATED +}; + struct refEntity_t { - refEntityType_t reType; - int renderfx; - - qhandle_t hModel; // opaque type outside refresh + qhandle_t hModel; // opaque type outside refresh // most recent data - vec3_t axis[ 3 ]; // rotation vectors - bool8_t nonNormalizedAxes; // axis are not normalized, i.e. they have scale - vec3_t origin; - int frame; + int16_t frame; // previous data for frame interpolation - vec3_t oldorigin; // also used as MODEL_BEAM's "to" - int oldframe; + int16_t oldframe; float backlerp; // 0.0 = current, 1.0 = old // texturing @@ -192,7 +222,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 @@ -202,11 +231,69 @@ 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; + int16_t startFrame; + int16_t endFrame; + float lerp; + + qhandle_t animationHandle2; + int16_t startFrame2; + int16_t endFrame2; + float lerp2; + + float blendLerp; + float scale; + + // All of the 1-byte types are placed below for better packing + refEntityType_t reType; + + RenderFx renderfx; + + EntityTag positionOnTag; + + int8_t clearOrigin; + int8_t clearOrigin2; + + int8_t boundsAdd; + + int8_t nonNormalizedAxes; // axis are not normalized, i.e. they have scale + + int8_t active; + + uint16_t attachmentEntity; + + uint16_t padding; // for better address alignment of shaderRGBA + + Color::Color32Bit shaderRGBA; // colors used by rgbgen entity shaders + + vec4_t dynamicLight; // r, g, b, radius; pre-multiplied by intensity + + vec3_t axis[3]; // rotation vectors + + vec3_t origin; + vec3_t oldorigin; // also used as MODEL_BEAM's "to" + + 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 a489ebafac..da4c679f54 100644 --- a/src/shared/client/cg_api.cpp +++ b/src/shared/client/cg_api.cpp @@ -295,9 +295,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 ) @@ -362,6 +380,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); } @@ -403,13 +422,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; } diff --git a/src/shared/client/cg_api.h b/src/shared/client/cg_api.h index e78b4ae2d7..69a2087035 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 );