diff --git a/src/gl/data/gl_vertexbuffer.h b/src/gl/data/gl_vertexbuffer.h index b1bc60f0d..8f4b22550 100644 --- a/src/gl/data/gl_vertexbuffer.h +++ b/src/gl/data/gl_vertexbuffer.h @@ -293,8 +293,23 @@ struct FModelVertex } }; +class FModelRenderer; -class FModelVertexBuffer : public FVertexBuffer +class IModelVertexBuffer +{ +public: + virtual ~IModelVertexBuffer() { } + + virtual FModelVertex *LockVertexBuffer(unsigned int size) = 0; + virtual void UnlockVertexBuffer() = 0; + + virtual unsigned int *LockIndexBuffer(unsigned int size) = 0; + virtual void UnlockIndexBuffer() = 0; + + virtual void SetupFrame(FModelRenderer *renderer, unsigned int frame1, unsigned int frame2, unsigned int size) = 0; +}; + +class FModelVertexBuffer : public FVertexBuffer, public IModelVertexBuffer { int mIndexFrame[2]; FModelVertex *vbo_ptr; @@ -305,14 +320,14 @@ public: FModelVertexBuffer(bool needindex, bool singleframe); ~FModelVertexBuffer(); - FModelVertex *LockVertexBuffer(unsigned int size); - void UnlockVertexBuffer(); + FModelVertex *LockVertexBuffer(unsigned int size) override; + void UnlockVertexBuffer() override; - unsigned int *LockIndexBuffer(unsigned int size); - void UnlockIndexBuffer(); + unsigned int *LockIndexBuffer(unsigned int size) override; + void UnlockIndexBuffer() override; - unsigned int SetupFrame(unsigned int frame1, unsigned int frame2, unsigned int size); - void BindVBO(); + void SetupFrame(FModelRenderer *renderer, unsigned int frame1, unsigned int frame2, unsigned int size) override; + void BindVBO() override; }; #define VMO ((FModelVertex*)NULL) diff --git a/src/gl/models/gl_models.cpp b/src/gl/models/gl_models.cpp index fcad2a95b..24ad00845 100644 --- a/src/gl/models/gl_models.cpp +++ b/src/gl/models/gl_models.cpp @@ -52,11 +52,6 @@ #include "gl/renderer/gl_renderstate.h" #include "gl/shaders/gl_shader.h" -static inline float GetTimeFloat() -{ - return (float)I_MSTime() * (float)TICRATE / 1000.0f; -} - CVAR(Bool, gl_interpolate_model_frames, true, CVAR_ARCHIVE) CVAR(Bool, gl_light_models, true, CVAR_ARCHIVE) EXTERN_CVAR(Int, gl_fogmode) @@ -66,6 +61,332 @@ extern TDeletingArray VoxelDefs; DeletingModelArray Models; +void FModelRenderer::RenderModel(float x, float y, float z, FSpriteModelFrame *smf, AActor *actor) +{ + // Setup transformation. + + int translation = 0; + if (!(smf->flags & MDL_IGNORETRANSLATION)) + translation = actor->Translation; + + // y scale for a sprite means height, i.e. z in the world! + float scaleFactorX = actor->Scale.X * smf->xscale; + float scaleFactorY = actor->Scale.X * smf->yscale; + float scaleFactorZ = actor->Scale.Y * smf->zscale; + float pitch = 0; + float roll = 0; + float rotateOffset = 0; + float angle = actor->Angles.Yaw.Degrees; + + // [BB] Workaround for the missing pitch information. + if ((smf->flags & MDL_PITCHFROMMOMENTUM)) + { + const double x = actor->Vel.X; + const double y = actor->Vel.Y; + const double z = actor->Vel.Z; + + if (actor->Vel.LengthSquared() > EQUAL_EPSILON) + { + // [BB] Calculate the pitch using spherical coordinates. + if (z || x || y) pitch = float(atan(z / sqrt(x*x + y*y)) / M_PI * 180); + + // Correcting pitch if model is moving backwards + if (fabs(x) > EQUAL_EPSILON || fabs(y) > EQUAL_EPSILON) + { + if ((x * cos(angle * M_PI / 180) + y * sin(angle * M_PI / 180)) / sqrt(x * x + y * y) < 0) pitch *= -1; + } + else pitch = fabs(pitch); + } + } + + if (smf->flags & MDL_ROTATING) + { + const float time = smf->rotationSpeed*GetTimeFloat() / 200.f; + rotateOffset = float((time - xs_FloorToInt(time)) *360.f); + } + + // Added MDL_USEACTORPITCH and MDL_USEACTORROLL flags processing. + // If both flags MDL_USEACTORPITCH and MDL_PITCHFROMMOMENTUM are set, the pitch sums up the actor pitch and the velocity vector pitch. + if (smf->flags & MDL_USEACTORPITCH) + { + double d = actor->Angles.Pitch.Degrees; + if (smf->flags & MDL_BADROTATION) pitch += d; + else pitch -= d; + } + if (smf->flags & MDL_USEACTORROLL) roll += actor->Angles.Roll.Degrees; + + VSMatrix objectToWorldMatrix; + objectToWorldMatrix.loadIdentity(); + + // Model space => World space + objectToWorldMatrix.translate(x, z, y); + + // [Nash] take SpriteRotation into account + angle += actor->SpriteRotation.Degrees; + + if (actor->renderflags & RF_INTERPOLATEANGLES) + { + // [Nash] use interpolated angles + DRotator Angles = actor->InterpolatedAngles(r_viewpoint.TicFrac); + angle = Angles.Yaw.Degrees; + } + + // Applying model transformations: + // 1) Applying actor angle, pitch and roll to the model + objectToWorldMatrix.rotate(-angle, 0, 1, 0); + objectToWorldMatrix.rotate(pitch, 0, 0, 1); + objectToWorldMatrix.rotate(-roll, 1, 0, 0); + + // 2) Applying Doomsday like rotation of the weapon pickup models + // The rotation angle is based on the elapsed time. + + if (smf->flags & MDL_ROTATING) + { + objectToWorldMatrix.translate(smf->rotationCenterX, smf->rotationCenterY, smf->rotationCenterZ); + objectToWorldMatrix.rotate(rotateOffset, smf->xrotate, smf->yrotate, smf->zrotate); + objectToWorldMatrix.translate(-smf->rotationCenterX, -smf->rotationCenterY, -smf->rotationCenterZ); + } + + // 3) Scaling model. + objectToWorldMatrix.scale(scaleFactorX, scaleFactorZ, scaleFactorY); + + // 4) Aplying model offsets (model offsets do not depend on model scalings). + objectToWorldMatrix.translate(smf->xoffset / smf->xscale, smf->zoffset / smf->zscale, smf->yoffset / smf->yscale); + + // 5) Applying model rotations. + objectToWorldMatrix.rotate(-smf->angleoffset, 0, 1, 0); + objectToWorldMatrix.rotate(smf->pitchoffset, 0, 0, 1); + objectToWorldMatrix.rotate(-smf->rolloffset, 1, 0, 0); + + // consider the pixel stretching. For non-voxels this must be factored out here + float stretch = (smf->modelIDs[0] != -1 ? Models[smf->modelIDs[0]]->getAspectFactor() : 1.f) / level.info->pixelstretch; + objectToWorldMatrix.scale(1, stretch, 1); + + BeginDrawModel(actor, smf, objectToWorldMatrix); + RenderFrameModels(smf, actor->state, actor->tics, actor->GetClass(), nullptr, translation); + EndDrawModel(actor, smf); +} + +void FModelRenderer::RenderHUDModel(DPSprite *psp, float ofsX, float ofsY) +{ + AActor * playermo = players[consoleplayer].camera; + FSpriteModelFrame *smf = gl_FindModelFrame(playermo->player->ReadyWeapon->GetClass(), psp->GetState()->sprite, psp->GetState()->GetFrame(), false); + + // [BB] No model found for this sprite, so we can't render anything. + if (smf == nullptr) + return; + + // The model position and orientation has to be drawn independently from the position of the player, + // but we need to position it correctly in the world for light to work properly. + VSMatrix objectToWorldMatrix = GetViewToWorldMatrix(); + + // Scaling model (y scale for a sprite means height, i.e. z in the world!). + objectToWorldMatrix.scale(smf->xscale, smf->zscale, smf->yscale); + + // Aplying model offsets (model offsets do not depend on model scalings). + objectToWorldMatrix.translate(smf->xoffset / smf->xscale, smf->zoffset / smf->zscale, smf->yoffset / smf->yscale); + + // [BB] Weapon bob, very similar to the normal Doom weapon bob. + objectToWorldMatrix.rotate(ofsX / 4, 0, 1, 0); + objectToWorldMatrix.rotate((ofsY - WEAPONTOP) / -4., 1, 0, 0); + + // [BB] For some reason the jDoom models need to be rotated. + objectToWorldMatrix.rotate(90.f, 0, 1, 0); + + // Applying angleoffset, pitchoffset, rolloffset. + objectToWorldMatrix.rotate(-smf->angleoffset, 0, 1, 0); + objectToWorldMatrix.rotate(smf->pitchoffset, 0, 0, 1); + objectToWorldMatrix.rotate(-smf->rolloffset, 1, 0, 0); + + BeginDrawHUDModel(playermo, objectToWorldMatrix); + RenderFrameModels(smf, psp->GetState(), psp->GetTics(), playermo->player->ReadyWeapon->GetClass(), nullptr, 0); + EndDrawHUDModel(playermo); +} + +void FModelRenderer::RenderFrameModels(const FSpriteModelFrame *smf, + const FState *curState, + const int curTics, + const PClass *ti, + Matrix3x4 *normaltransform, + int translation) +{ + // [BB] Frame interpolation: Find the FSpriteModelFrame smfNext which follows after smf in the animation + // and the scalar value inter ( element of [0,1) ), both necessary to determine the interpolated frame. + FSpriteModelFrame * smfNext = nullptr; + double inter = 0.; + if (gl_interpolate_model_frames && !(smf->flags & MDL_NOINTERPOLATION)) + { + FState *nextState = curState->GetNextState(); + if (curState != nextState && nextState) + { + // [BB] To interpolate at more than 35 fps we take tic fractions into account. + float ticFraction = 0.; + // [BB] In case the tic counter is frozen we have to leave ticFraction at zero. + if (ConsoleState == c_up && menuactive != MENU_On && !(level.flags2 & LEVEL2_FROZEN)) + { + float time = GetTimeFloat(); + ticFraction = (time - static_cast(time)); + } + inter = static_cast(curState->Tics - curTics - ticFraction) / static_cast(curState->Tics); + + // [BB] For some actors (e.g. ZPoisonShroom) spr->actor->tics can be bigger than curState->Tics. + // In this case inter is negative and we need to set it to zero. + if (inter < 0.) + inter = 0.; + else + { + // [BB] Workaround for actors that use the same frame twice in a row. + // Most of the standard Doom monsters do this in their see state. + if ((smf->flags & MDL_INTERPOLATEDOUBLEDFRAMES)) + { + const FState *prevState = curState - 1; + if ((curState->sprite == prevState->sprite) && (curState->Frame == prevState->Frame)) + { + inter /= 2.; + inter += 0.5; + } + if ((curState->sprite == nextState->sprite) && (curState->Frame == nextState->Frame)) + { + inter /= 2.; + nextState = nextState->GetNextState(); + } + } + if (inter != 0.0) + smfNext = gl_FindModelFrame(ti, nextState->sprite, nextState->Frame, false); + } + } + } + + for (int i = 0; imodelIDs[i] != -1) + { + FModel * mdl = Models[smf->modelIDs[i]]; + FTexture *tex = smf->skinIDs[i].isValid() ? TexMan(smf->skinIDs[i]) : nullptr; + mdl->BuildVertexBuffer(this); + SetVertexBuffer(mdl->mVBuf); + + mdl->PushSpriteMDLFrame(smf, i); + + if (smfNext && smf->modelframes[i] != smfNext->modelframes[i]) + mdl->RenderFrame(this, tex, smf->modelframes[i], smfNext->modelframes[i], inter, translation); + else + mdl->RenderFrame(this, tex, smf->modelframes[i], smf->modelframes[i], 0.f, translation); + + ResetVertexBuffer(); + } + } +} + +///////////////////////////////////////////////////////////////////////////// + +extern int modellightindex; + +VSMatrix FGLModelRenderer::GetViewToWorldMatrix() +{ + VSMatrix objectToWorldMatrix; + gl_RenderState.mViewMatrix.inverseMatrix(objectToWorldMatrix); + return objectToWorldMatrix; +} + +void FGLModelRenderer::BeginDrawModel(AActor *actor, FSpriteModelFrame *smf, const VSMatrix &objectToWorldMatrix) +{ + glDepthFunc(GL_LEQUAL); + gl_RenderState.EnableTexture(true); + // [BB] In case the model should be rendered translucent, do back face culling. + // This solves a few of the problems caused by the lack of depth sorting. + // [Nash] Don't do back face culling if explicitly specified in MODELDEF + // TO-DO: Implement proper depth sorting. + if (!(actor->RenderStyle == LegacyRenderStyles[STYLE_Normal]) && !(smf->flags & MDL_DONTCULLBACKFACES)) + { + glEnable(GL_CULL_FACE); + glFrontFace(GL_CW); + } + + gl_RenderState.mModelMatrix = objectToWorldMatrix; + gl_RenderState.EnableModelMatrix(true); +} + +void FGLModelRenderer::EndDrawModel(AActor *actor, FSpriteModelFrame *smf) +{ + gl_RenderState.EnableModelMatrix(false); + + glDepthFunc(GL_LESS); + if (!(actor->RenderStyle == LegacyRenderStyles[STYLE_Normal]) && !(smf->flags & MDL_DONTCULLBACKFACES)) + glDisable(GL_CULL_FACE); +} + +void FGLModelRenderer::BeginDrawHUDModel(AActor *actor, const VSMatrix &objectToWorldMatrix) +{ + glDepthFunc(GL_LEQUAL); + + // [BB] In case the model should be rendered translucent, do back face culling. + // This solves a few of the problems caused by the lack of depth sorting. + // TO-DO: Implement proper depth sorting. + if (!(actor->RenderStyle == LegacyRenderStyles[STYLE_Normal])) + { + glEnable(GL_CULL_FACE); + glFrontFace(GL_CCW); + } + + gl_RenderState.mModelMatrix = objectToWorldMatrix; + gl_RenderState.EnableModelMatrix(true); +} + +void FGLModelRenderer::EndDrawHUDModel(AActor *actor) +{ + gl_RenderState.EnableModelMatrix(false); + + glDepthFunc(GL_LESS); + if (!(actor->RenderStyle == LegacyRenderStyles[STYLE_Normal])) + glDisable(GL_CULL_FACE); +} + +IModelVertexBuffer *FGLModelRenderer::CreateVertexBuffer(bool needindex, bool singleframe) +{ + return new FModelVertexBuffer(needindex, singleframe); +} + +void FGLModelRenderer::SetVertexBuffer(IModelVertexBuffer *buffer) +{ + gl_RenderState.SetVertexBuffer((FModelVertexBuffer*)buffer); +} + +void FGLModelRenderer::ResetVertexBuffer() +{ + gl_RenderState.SetVertexBuffer(GLRenderer->mVBO); +} + +void FGLModelRenderer::SetInterpolation(double inter) +{ + gl_RenderState.SetInterpolationFactor((float)inter); +} + +void FGLModelRenderer::SetMaterial(FTexture *skin, int clampmode, int translation) +{ + FMaterial * tex = FMaterial::ValidateTexture(skin, false); + gl_RenderState.SetMaterial(tex, clampmode, translation, -1, false); + + gl_RenderState.Apply(); + if (modellightindex != -1) gl_RenderState.ApplyLightIndex(modellightindex); +} + +void FGLModelRenderer::DrawArrays(int primitiveType, int start, int count) +{ + glDrawArrays(primitiveType, start, count); +} + +void FGLModelRenderer::DrawElements(int primitiveType, int numIndices, int elementType, size_t offset) +{ + glDrawElements(primitiveType, numIndices, elementType, (void*)(intptr_t)offset); +} + +float FGLModelRenderer::GetTimeFloat() +{ + return (float)I_MSTime() * (float)TICRATE / 1000.0f; +} + +///////////////////////////////////////////////////////////////////////////// void gl_LoadModels() { @@ -238,7 +559,7 @@ void FModelVertexBuffer::UnlockIndexBuffer() //=========================================================================== static TArray iBuffer; -unsigned int FModelVertexBuffer::SetupFrame(unsigned int frame1, unsigned int frame2, unsigned int size) +void FModelVertexBuffer::SetupFrame(FModelRenderer *renderer, unsigned int frame1, unsigned int frame2, unsigned int size) { glBindBuffer(GL_ARRAY_BUFFER, vbo_id); if (vbo_id > 0) @@ -276,7 +597,6 @@ unsigned int FModelVertexBuffer::SetupFrame(unsigned int frame1, unsigned int fr iBuffer[i].z = vbo_ptr[frame1 + i].z * (1.f - frac) + vbo_ptr[frame2 + i].z * frac; } } - return frame1; } //=========================================================================== @@ -868,207 +1188,12 @@ FSpriteModelFrame * gl_FindModelFrame(const PClass * ti, int sprite, int frame, // //=========================================================================== -void gl_RenderFrameModels( const FSpriteModelFrame *smf, - const FState *curState, - const int curTics, - const PClass *ti, - Matrix3x4 *normaltransform, - int translation) -{ - // [BB] Frame interpolation: Find the FSpriteModelFrame smfNext which follows after smf in the animation - // and the scalar value inter ( element of [0,1) ), both necessary to determine the interpolated frame. - FSpriteModelFrame * smfNext = nullptr; - double inter = 0.; - if( gl_interpolate_model_frames && !(smf->flags & MDL_NOINTERPOLATION) ) - { - FState *nextState = curState->GetNextState( ); - if( curState != nextState && nextState ) - { - // [BB] To interpolate at more than 35 fps we take tic fractions into account. - float ticFraction = 0.; - // [BB] In case the tic counter is frozen we have to leave ticFraction at zero. - if ( ConsoleState == c_up && menuactive != MENU_On && !(level.flags2 & LEVEL2_FROZEN) ) - { - float time = GetTimeFloat(); - ticFraction = (time - static_cast(time)); - } - inter = static_cast(curState->Tics - curTics - ticFraction)/static_cast(curState->Tics); - - // [BB] For some actors (e.g. ZPoisonShroom) spr->actor->tics can be bigger than curState->Tics. - // In this case inter is negative and we need to set it to zero. - if ( inter < 0. ) - inter = 0.; - else - { - // [BB] Workaround for actors that use the same frame twice in a row. - // Most of the standard Doom monsters do this in their see state. - if ( (smf->flags & MDL_INTERPOLATEDOUBLEDFRAMES) ) - { - const FState *prevState = curState - 1; - if ( (curState->sprite == prevState->sprite) && ( curState->Frame == prevState->Frame) ) - { - inter /= 2.; - inter += 0.5; - } - if ( (curState->sprite == nextState->sprite) && ( curState->Frame == nextState->Frame) ) - { - inter /= 2.; - nextState = nextState->GetNextState( ); - } - } - if ( inter != 0.0 ) - smfNext = gl_FindModelFrame(ti, nextState->sprite, nextState->Frame, false); - } - } - } - - for(int i=0; imodelIDs[i] != -1) - { - FModel * mdl = Models[smf->modelIDs[i]]; - FTexture *tex = smf->skinIDs[i].isValid()? TexMan(smf->skinIDs[i]) : nullptr; - mdl->BuildVertexBuffer(); - gl_RenderState.SetVertexBuffer(mdl->mVBuf); - - mdl->PushSpriteMDLFrame(smf, i); - - if ( smfNext && smf->modelframes[i] != smfNext->modelframes[i] ) - mdl->RenderFrame(tex, smf->modelframes[i], smfNext->modelframes[i], inter, translation); - else - mdl->RenderFrame(tex, smf->modelframes[i], smf->modelframes[i], 0.f, translation); - - gl_RenderState.SetVertexBuffer(GLRenderer->mVBO); - } - } -} - void gl_RenderModel(GLSprite * spr) { - FSpriteModelFrame * smf = spr->modelframe; - - - // Setup transformation. - glDepthFunc(GL_LEQUAL); - gl_RenderState.EnableTexture(true); - // [BB] In case the model should be rendered translucent, do back face culling. - // This solves a few of the problems caused by the lack of depth sorting. - // [Nash] Don't do back face culling if explicitly specified in MODELDEF - // TO-DO: Implement proper depth sorting. - if (!(spr->actor->RenderStyle == LegacyRenderStyles[STYLE_Normal]) && !(smf->flags & MDL_DONTCULLBACKFACES)) - { - glEnable(GL_CULL_FACE); - glFrontFace(GL_CW); - } - - int translation = 0; - if ( !(smf->flags & MDL_IGNORETRANSLATION) ) - translation = spr->actor->Translation; - - - // y scale for a sprite means height, i.e. z in the world! - float scaleFactorX = spr->actor->Scale.X * smf->xscale; - float scaleFactorY = spr->actor->Scale.X * smf->yscale; - float scaleFactorZ = spr->actor->Scale.Y * smf->zscale; - float pitch = 0; - float roll = 0; - float rotateOffset = 0; - float angle = spr->actor->Angles.Yaw.Degrees; - - // [BB] Workaround for the missing pitch information. - if ( (smf->flags & MDL_PITCHFROMMOMENTUM) ) - { - const double x = spr->actor->Vel.X; - const double y = spr->actor->Vel.Y; - const double z = spr->actor->Vel.Z; - - if (spr->actor->Vel.LengthSquared() > EQUAL_EPSILON) - { - // [BB] Calculate the pitch using spherical coordinates. - if (z || x || y) pitch = float(atan(z / sqrt(x*x + y*y)) / M_PI * 180); - - // Correcting pitch if model is moving backwards - if (fabs(x) > EQUAL_EPSILON || fabs(y) > EQUAL_EPSILON) - { - if ((x * cos(angle * M_PI / 180) + y * sin(angle * M_PI / 180)) / sqrt(x * x + y * y) < 0) pitch *= -1; - } - else pitch = fabs(pitch); - } - } - - if( smf->flags & MDL_ROTATING ) - { - const float time = smf->rotationSpeed*GetTimeFloat()/200.f; - rotateOffset = float((time - xs_FloorToInt(time)) *360.f ); - } - - // Added MDL_USEACTORPITCH and MDL_USEACTORROLL flags processing. - // If both flags MDL_USEACTORPITCH and MDL_PITCHFROMMOMENTUM are set, the pitch sums up the actor pitch and the velocity vector pitch. - if (smf->flags & MDL_USEACTORPITCH) - { - double d = spr->actor->Angles.Pitch.Degrees; - if (smf->flags & MDL_BADROTATION) pitch += d; - else pitch -= d; - } - if(smf->flags & MDL_USEACTORROLL) roll += spr->actor->Angles.Roll.Degrees; - - gl_RenderState.mModelMatrix.loadIdentity(); - - // Model space => World space - gl_RenderState.mModelMatrix.translate(spr->x, spr->z, spr->y ); - - // [Nash] take SpriteRotation into account - angle += spr->actor->SpriteRotation.Degrees; - - if (spr->actor->renderflags & RF_INTERPOLATEANGLES) - { - // [Nash] use interpolated angles - DRotator Angles = spr->actor->InterpolatedAngles(r_viewpoint.TicFrac); - angle = Angles.Yaw.Degrees; - } - - // Applying model transformations: - // 1) Applying actor angle, pitch and roll to the model - gl_RenderState.mModelMatrix.rotate(-angle, 0, 1, 0); - gl_RenderState.mModelMatrix.rotate(pitch, 0, 0, 1); - gl_RenderState.mModelMatrix.rotate(-roll, 1, 0, 0); - - // 2) Applying Doomsday like rotation of the weapon pickup models - // The rotation angle is based on the elapsed time. - - if( smf->flags & MDL_ROTATING ) - { - gl_RenderState.mModelMatrix.translate(smf->rotationCenterX, smf->rotationCenterY, smf->rotationCenterZ); - gl_RenderState.mModelMatrix.rotate(rotateOffset, smf->xrotate, smf->yrotate, smf->zrotate); - gl_RenderState.mModelMatrix.translate(-smf->rotationCenterX, -smf->rotationCenterY, -smf->rotationCenterZ); - } - - // 3) Scaling model. - gl_RenderState.mModelMatrix.scale(scaleFactorX, scaleFactorZ, scaleFactorY); - - // 4) Aplying model offsets (model offsets do not depend on model scalings). - gl_RenderState.mModelMatrix.translate(smf->xoffset / smf->xscale, smf->zoffset / smf->zscale, smf->yoffset / smf->yscale); - - // 5) Applying model rotations. - gl_RenderState.mModelMatrix.rotate(-smf->angleoffset, 0, 1, 0); - gl_RenderState.mModelMatrix.rotate(smf->pitchoffset, 0, 0, 1); - gl_RenderState.mModelMatrix.rotate(-smf->rolloffset, 1, 0, 0); - - // consider the pixel stretching. For non-voxels this must be factored out here - float stretch = (smf->modelIDs[0] != -1 ? Models[smf->modelIDs[0]]->getAspectFactor() : 1.f) / level.info->pixelstretch; - gl_RenderState.mModelMatrix.scale(1, stretch, 1); - - - gl_RenderState.EnableModelMatrix(true); - gl_RenderFrameModels( smf, spr->actor->state, spr->actor->tics, spr->actor->GetClass(), nullptr, translation ); - gl_RenderState.EnableModelMatrix(false); - - glDepthFunc(GL_LESS); - if (!( spr->actor->RenderStyle == LegacyRenderStyles[STYLE_Normal] )) - glDisable(GL_CULL_FACE); + FGLModelRenderer renderer; + renderer.RenderModel(spr->x, spr->y, spr->z, spr->modelframe, spr->actor); } - //=========================================================================== // // gl_RenderHUDModel @@ -1077,55 +1202,8 @@ void gl_RenderModel(GLSprite * spr) void gl_RenderHUDModel(DPSprite *psp, float ofsX, float ofsY) { - AActor * playermo=players[consoleplayer].camera; - FSpriteModelFrame *smf = gl_FindModelFrame(playermo->player->ReadyWeapon->GetClass(), psp->GetState()->sprite, psp->GetState()->GetFrame(), false); - - // [BB] No model found for this sprite, so we can't render anything. - if ( smf == nullptr ) - return; - - glDepthFunc(GL_LEQUAL); - - // [BB] In case the model should be rendered translucent, do back face culling. - // This solves a few of the problems caused by the lack of depth sorting. - // TO-DO: Implement proper depth sorting. - if (!( playermo->RenderStyle == LegacyRenderStyles[STYLE_Normal] )) - { - glEnable(GL_CULL_FACE); - glFrontFace(GL_CCW); - } - - // The model position and orientation has to be drawn independently from the position of the player, - // but we need to position it correctly in the world for light to work properly. - VSMatrix objectToWorldMatrix; - gl_RenderState.mViewMatrix.inverseMatrix(objectToWorldMatrix); - - // Scaling model (y scale for a sprite means height, i.e. z in the world!). - objectToWorldMatrix.scale(smf->xscale, smf->zscale, smf->yscale); - - // Aplying model offsets (model offsets do not depend on model scalings). - objectToWorldMatrix.translate(smf->xoffset / smf->xscale, smf->zoffset / smf->zscale, smf->yoffset / smf->yscale); - - // [BB] Weapon bob, very similar to the normal Doom weapon bob. - objectToWorldMatrix.rotate(ofsX/4, 0, 1, 0); - objectToWorldMatrix.rotate((ofsY-WEAPONTOP)/-4., 1, 0, 0); - - // [BB] For some reason the jDoom models need to be rotated. - objectToWorldMatrix.rotate(90.f, 0, 1, 0); - - // Applying angleoffset, pitchoffset, rolloffset. - objectToWorldMatrix.rotate(-smf->angleoffset, 0, 1, 0); - objectToWorldMatrix.rotate(smf->pitchoffset, 0, 0, 1); - objectToWorldMatrix.rotate(-smf->rolloffset, 1, 0, 0); - - gl_RenderState.mModelMatrix = objectToWorldMatrix; - gl_RenderState.EnableModelMatrix(true); - gl_RenderFrameModels( smf, psp->GetState(), psp->GetTics(), playermo->player->ReadyWeapon->GetClass(), nullptr, 0 ); - gl_RenderState.EnableModelMatrix(false); - - glDepthFunc(GL_LESS); - if (!( playermo->RenderStyle == LegacyRenderStyles[STYLE_Normal] )) - glDisable(GL_CULL_FACE); + FGLModelRenderer renderer; + renderer.RenderHUDModel(psp, ofsX, ofsY); } //=========================================================================== diff --git a/src/gl/models/gl_models.h b/src/gl/models/gl_models.h index a1ed4014d..84e331e31 100644 --- a/src/gl/models/gl_models.h +++ b/src/gl/models/gl_models.h @@ -45,6 +45,56 @@ FTextureID LoadSkin(const char * path, const char * fn); // [JM] Necessary forward declaration typedef struct FSpriteModelFrame FSpriteModelFrame; +class FModelRenderer +{ +public: + virtual ~FModelRenderer() { } + + void RenderModel(float x, float y, float z, FSpriteModelFrame *modelframe, AActor *actor); + void RenderHUDModel(DPSprite *psp, float ofsx, float ofsy); + + virtual void BeginDrawModel(AActor *actor, FSpriteModelFrame *smf, const VSMatrix &objectToWorldMatrix) = 0; + virtual void EndDrawModel(AActor *actor, FSpriteModelFrame *smf) = 0; + + virtual IModelVertexBuffer *CreateVertexBuffer(bool needindex, bool singleframe) = 0; + + virtual void SetVertexBuffer(IModelVertexBuffer *buffer) = 0; + virtual void ResetVertexBuffer() = 0; + + virtual VSMatrix GetViewToWorldMatrix() = 0; + + virtual void BeginDrawHUDModel(AActor *actor, const VSMatrix &objectToWorldMatrix) = 0; + virtual void EndDrawHUDModel(AActor *actor) = 0; + + virtual void SetInterpolation(double interpolation) = 0; + virtual void SetMaterial(FTexture *skin, int clampmode, int translation) = 0; + virtual void DrawArrays(int primitiveType, int start, int count) = 0; + virtual void DrawElements(int primitiveType, int numIndices, int elementType, size_t offset) = 0; + + virtual float GetTimeFloat() = 0; + +private: + void RenderFrameModels(const FSpriteModelFrame *smf, const FState *curState, const int curTics, const PClass *ti, Matrix3x4 *normaltransform, int translation); +}; + +class FGLModelRenderer : public FModelRenderer +{ +public: + void BeginDrawModel(AActor *actor, FSpriteModelFrame *smf, const VSMatrix &objectToWorldMatrix) override; + void EndDrawModel(AActor *actor, FSpriteModelFrame *smf) override; + IModelVertexBuffer *CreateVertexBuffer(bool needindex, bool singleframe) override; + void SetVertexBuffer(IModelVertexBuffer *buffer) override; + void ResetVertexBuffer() override; + VSMatrix GetViewToWorldMatrix() override; + void BeginDrawHUDModel(AActor *actor, const VSMatrix &objectToWorldMatrix) override; + void EndDrawHUDModel(AActor *actor) override; + void SetInterpolation(double interpolation) override; + void SetMaterial(FTexture *skin, int clampmode, int translation) override; + void DrawArrays(int primitiveType, int start, int count) override; + void DrawElements(int primitiveType, int numIndices, int elementType, size_t offset) override; + float GetTimeFloat() override; +}; + class FModel { public: @@ -57,8 +107,8 @@ public: virtual bool Load(const char * fn, int lumpnum, const char * buffer, int length) = 0; virtual int FindFrame(const char * name) = 0; - virtual void RenderFrame(FTexture * skin, int frame, int frame2, double inter, int translation=0) = 0; - virtual void BuildVertexBuffer() = 0; + virtual void RenderFrame(FModelRenderer *renderer, FTexture * skin, int frame, int frame2, double inter, int translation=0) = 0; + virtual void BuildVertexBuffer(FModelRenderer *renderer) = 0; virtual void AddSkins(uint8_t *hitlist) = 0; void DestroyVertexBuffer() { @@ -71,7 +121,7 @@ public: int curMDLIndex; void PushSpriteMDLFrame(const FSpriteModelFrame *smf, int index) { curSpriteMDLFrame = smf; curMDLIndex = index; }; - FModelVertexBuffer *mVBuf; + IModelVertexBuffer *mVBuf; FString mFileName; }; @@ -183,12 +233,12 @@ public: virtual bool Load(const char * fn, int lumpnum, const char * buffer, int length); virtual int FindFrame(const char * name); - virtual void RenderFrame(FTexture * skin, int frame, int frame2, double inter, int translation=0); + virtual void RenderFrame(FModelRenderer *renderer, FTexture * skin, int frame, int frame2, double inter, int translation=0); virtual void LoadGeometry(); virtual void AddSkins(uint8_t *hitlist); void UnloadGeometry(); - void BuildVertexBuffer(); + void BuildVertexBuffer(FModelRenderer *renderer); }; @@ -289,9 +339,9 @@ public: virtual bool Load(const char * fn, int lumpnum, const char * buffer, int length); virtual int FindFrame(const char * name); - virtual void RenderFrame(FTexture * skin, int frame, int frame2, double inter, int translation=0); + virtual void RenderFrame(FModelRenderer *renderer, FTexture * skin, int frame, int frame2, double inter, int translation=0); void LoadGeometry(); - void BuildVertexBuffer(); + void BuildVertexBuffer(FModelRenderer *renderer); virtual void AddSkins(uint8_t *hitlist); }; @@ -344,10 +394,10 @@ public: bool Load(const char * fn, int lumpnum, const char * buffer, int length); void Initialize(); virtual int FindFrame(const char * name); - virtual void RenderFrame(FTexture * skin, int frame, int frame2, double inter, int translation=0); + virtual void RenderFrame(FModelRenderer *renderer, FTexture * skin, int frame, int frame2, double inter, int translation=0); virtual void AddSkins(uint8_t *hitlist); FTextureID GetPaletteTexture() const { return mPalette; } - void BuildVertexBuffer(); + void BuildVertexBuffer(FModelRenderer *renderer); float getAspectFactor(); }; diff --git a/src/gl/models/gl_models_md2.cpp b/src/gl/models/gl_models_md2.cpp index 7f7930ac9..b666c2e1c 100644 --- a/src/gl/models/gl_models_md2.cpp +++ b/src/gl/models/gl_models_md2.cpp @@ -40,8 +40,6 @@ #include "gl/shaders/gl_shader.h" #include "gl/data/gl_vertexbuffer.h" -extern int modellightindex; - static float avertexnormals[NUMVERTEXNORMALS][3] = { #include "tab_anorms.h" }; @@ -286,7 +284,7 @@ FDMDModel::~FDMDModel() // //=========================================================================== -void FDMDModel::BuildVertexBuffer() +void FDMDModel::BuildVertexBuffer(FModelRenderer *renderer) { if (mVBuf == NULL) { @@ -295,7 +293,7 @@ void FDMDModel::BuildVertexBuffer() int VertexBufferSize = info.numFrames * lodInfo[0].numTriangles * 3; unsigned int vindex = 0; - mVBuf = new FModelVertexBuffer(false, info.numFrames == 1); + mVBuf = renderer->CreateVertexBuffer(false, info.numFrames == 1); FModelVertex *vertptr = mVBuf->LockVertexBuffer(VertexBufferSize); for (int i = 0; i < info.numFrames; i++) @@ -364,7 +362,7 @@ int FDMDModel::FindFrame(const char * name) // //=========================================================================== -void FDMDModel::RenderFrame(FTexture * skin, int frameno, int frameno2, double inter, int translation) +void FDMDModel::RenderFrame(FModelRenderer *renderer, FTexture * skin, int frameno, int frameno2, double inter, int translation) { if (frameno >= info.numFrames || frameno2 >= info.numFrames) return; @@ -375,16 +373,11 @@ void FDMDModel::RenderFrame(FTexture * skin, int frameno, int frameno2, double i if (!skin) return; } - FMaterial * tex = FMaterial::ValidateTexture(skin, false); - - gl_RenderState.SetMaterial(tex, CLAMP_NONE, translation, -1, false); - gl_RenderState.SetInterpolationFactor((float)inter); - - gl_RenderState.Apply(); - if (modellightindex != -1) gl_RenderState.ApplyLightIndex(modellightindex); - mVBuf->SetupFrame(frames[frameno].vindex, frames[frameno2].vindex, lodInfo[0].numTriangles * 3); - glDrawArrays(GL_TRIANGLES, 0, lodInfo[0].numTriangles * 3); - gl_RenderState.SetInterpolationFactor(0.f); + renderer->SetInterpolation(inter); + renderer->SetMaterial(skin, CLAMP_NONE, translation); + mVBuf->SetupFrame(renderer, frames[frameno].vindex, frames[frameno2].vindex, lodInfo[0].numTriangles * 3); + renderer->DrawArrays(GL_TRIANGLES, 0, lodInfo[0].numTriangles * 3); + renderer->SetInterpolation(0.f); } diff --git a/src/gl/models/gl_models_md3.cpp b/src/gl/models/gl_models_md3.cpp index 9573b83c1..a1954ce5c 100644 --- a/src/gl/models/gl_models_md3.cpp +++ b/src/gl/models/gl_models_md3.cpp @@ -35,8 +35,6 @@ #define MAX_QPATH 64 -extern int modellightindex; - //=========================================================================== // // decode the lat/lng normal to a 3 float normal @@ -240,7 +238,7 @@ void FMD3Model::LoadGeometry() // //=========================================================================== -void FMD3Model::BuildVertexBuffer() +void FMD3Model::BuildVertexBuffer(FModelRenderer *renderer) { if (mVBuf == nullptr) { @@ -256,7 +254,7 @@ void FMD3Model::BuildVertexBuffer() ibufsize += 3 * surf->numTriangles; } - mVBuf = new FModelVertexBuffer(true, numFrames == 1); + mVBuf = renderer->CreateVertexBuffer(true, numFrames == 1); FModelVertex *vertptr = mVBuf->LockVertexBuffer(vbufsize); unsigned int *indxptr = mVBuf->LockIndexBuffer(ibufsize); @@ -343,11 +341,11 @@ int FMD3Model::FindFrame(const char * name) // //=========================================================================== -void FMD3Model::RenderFrame(FTexture * skin, int frameno, int frameno2, double inter, int translation) +void FMD3Model::RenderFrame(FModelRenderer *renderer, FTexture * skin, int frameno, int frameno2, double inter, int translation) { if (frameno>=numFrames || frameno2>=numFrames) return; - gl_RenderState.SetInterpolationFactor((float)inter); + renderer->SetInterpolation(inter); for(int i=0;iSetupFrame(surf->vindex + frameno * surf->numVertices, surf->vindex + frameno2 * surf->numVertices, surf->numVertices); - glDrawElements(GL_TRIANGLES, surf->numTriangles * 3, GL_UNSIGNED_INT, (void*)(intptr_t)(surf->iindex * sizeof(unsigned int))); + renderer->SetMaterial(surfaceSkin, CLAMP_NONE, translation); + mVBuf->SetupFrame(renderer, surf->vindex + frameno * surf->numVertices, surf->vindex + frameno2 * surf->numVertices, surf->numVertices); + renderer->DrawElements(GL_TRIANGLES, surf->numTriangles * 3, GL_UNSIGNED_INT, surf->iindex * sizeof(unsigned int)); } - gl_RenderState.SetInterpolationFactor(0.f); + renderer->SetInterpolation(0.f); } //=========================================================================== diff --git a/src/gl/models/gl_voxels.cpp b/src/gl/models/gl_voxels.cpp index 0b97e87f8..839218f2e 100644 --- a/src/gl/models/gl_voxels.cpp +++ b/src/gl/models/gl_voxels.cpp @@ -50,8 +50,6 @@ #include "gl/utility/gl_convert.h" #include "gl/renderer/gl_renderstate.h" -extern int modellightindex; - //=========================================================================== // // Creates a 16x16 texture from the palette so that we can @@ -364,13 +362,13 @@ void FVoxelModel::Initialize() // //=========================================================================== -void FVoxelModel::BuildVertexBuffer() +void FVoxelModel::BuildVertexBuffer(FModelRenderer *renderer) { if (mVBuf == NULL) { Initialize(); - mVBuf = new FModelVertexBuffer(true, true); + mVBuf = renderer->CreateVertexBuffer(true, true); FModelVertex *vertptr = mVBuf->LockVertexBuffer(mVertices.Size()); unsigned int *indxptr = mVBuf->LockIndexBuffer(mIndices.Size()); @@ -440,14 +438,10 @@ float FVoxelModel::getAspectFactor() // //=========================================================================== -void FVoxelModel::RenderFrame(FTexture * skin, int frame, int frame2, double inter, int translation) +void FVoxelModel::RenderFrame(FModelRenderer *renderer, FTexture * skin, int frame, int frame2, double inter, int translation) { - FMaterial * tex = FMaterial::ValidateTexture(skin, false); - gl_RenderState.SetMaterial(tex, CLAMP_NOFILTER, translation, -1, false); - - gl_RenderState.Apply(); - if (modellightindex != -1) gl_RenderState.ApplyLightIndex(modellightindex); - mVBuf->SetupFrame(0, 0, 0); - glDrawElements(GL_TRIANGLES, mNumIndices, GL_UNSIGNED_INT, (void*)(intptr_t)0); + renderer->SetMaterial(skin, CLAMP_NOFILTER, translation); + mVBuf->SetupFrame(renderer, 0, 0, 0); + renderer->DrawElements(GL_TRIANGLES, mNumIndices, GL_UNSIGNED_INT, 0); } diff --git a/src/gl/textures/gl_texture.cpp b/src/gl/textures/gl_texture.cpp index 51425d40d..ddfd7ecc9 100644 --- a/src/gl/textures/gl_texture.cpp +++ b/src/gl/textures/gl_texture.cpp @@ -890,10 +890,11 @@ void gl_PrecacheTexture(uint8_t *texhitlist, TMap &actorhitl } // cache all used models + FGLModelRenderer renderer; for (unsigned i = 0; i < Models.Size(); i++) { if (modellist[i]) - Models[i]->BuildVertexBuffer(); + Models[i]->BuildVertexBuffer(&renderer); } }