Merge remote-tracking branch 'gzdoom/modern' into hw_postprocess

This commit is contained in:
Magnus Norddahl 2018-06-24 17:57:02 +02:00
commit 32d837cdf1
164 changed files with 23467 additions and 13025 deletions

View file

@ -638,8 +638,8 @@ add_definitions(-DOPNMIDI_USE_LEGACY_EMULATOR)
add_definitions(-DADLMIDI_DISABLE_MUS_SUPPORT -DADLMIDI_DISABLE_XMI_SUPPORT -DADLMIDI_DISABLE_MIDI_SEQUENCER)
add_definitions(-DOPNMIDI_DISABLE_MUS_SUPPORT -DOPNMIDI_DISABLE_XMI_SUPPORT -DOPNMIDI_DISABLE_MIDI_SEQUENCER)
# Disable ADLMIDI's MIDI Sequencer, MUS and XMI converters
add_definitions(-DADLMIDI_DISABLE_MUS_SUPPORT -DADLMIDI_DISABLE_XMI_SUPPORT -DADLMIDI_DISABLE_MIDI_SEQUENCER)
# Disable OPNMIDI's experimental yet emulator (using of it has some issues and missing notes in playback)
add_definitions(-DOPNMIDI_DISABLE_GX_EMULATOR)
# Project files should be aware of the header files. We can GLOB these since
# there's generally a new cpp for every header so this file will get changed
@ -705,7 +705,6 @@ file( GLOB HEADER_FILES
gl/models/*.h
gl/renderer/*.h
gl/scene/*.h
gl/stereo3d/*.h
gl/shaders/*.h
gl/system/*.h
gl/textures/*.h
@ -837,9 +836,11 @@ set( FASTMATH_SOURCES
hwrenderer/scene/hw_bsp.cpp
hwrenderer/scene/hw_fakeflat.cpp
hwrenderer/scene/hw_decal.cpp
hwrenderer/scene/hw_drawinfo.cpp
hwrenderer/scene/hw_drawlist.cpp
hwrenderer/scene/hw_clipper.cpp
hwrenderer/scene/hw_flats.cpp
hwrenderer/scene/hw_portal.cpp
hwrenderer/scene/hw_renderhacks.cpp
hwrenderer/scene/hw_sky.cpp
hwrenderer/scene/hw_sprites.cpp
@ -855,14 +856,25 @@ set( FASTMATH_SOURCES
sound/adlmidi/adlmidi_midiplay.cpp
sound/adlmidi/adlmidi_opl3.cpp
sound/adlmidi/adlmidi_private.cpp
sound/adlmidi/dbopl.cpp
sound/adlmidi/nukedopl3.c
sound/adlmidi/chips/dosbox/dbopl.cpp
sound/adlmidi/chips/dosbox_opl3.cpp
sound/adlmidi/chips/nuked/nukedopl3_174.c
sound/adlmidi/chips/nuked/nukedopl3.c
sound/adlmidi/chips/nuked_opl3.cpp
sound/adlmidi/chips/nuked_opl3_v174.cpp
sound/adlmidi/wopl/wopl_file.c
sound/opnmidi/chips/gens_opn2.cpp
sound/opnmidi/chips/gens/Ym2612_Emu.cpp
sound/opnmidi/chips/mame/mame_ym2612fm.c
sound/opnmidi/chips/mame_opn2.cpp
sound/opnmidi/chips/nuked_opn2.cpp
sound/opnmidi/chips/nuked/ym3438.c
sound/opnmidi/opnmidi.cpp
sound/opnmidi/opnmidi_load.cpp
sound/opnmidi/opnmidi_midiplay.cpp
sound/opnmidi/opnmidi_opn2.cpp
sound/opnmidi/opnmidi_private.cpp
sound/opnmidi/Ym2612_ChipEmu.cpp
)
set (PCH_SOURCES
@ -1041,16 +1053,10 @@ set (PCH_SOURCES
gl/renderer/gl_lightdata.cpp
gl/renderer/gl_postprocess.cpp
gl/renderer/gl_postprocessstate.cpp
gl/renderer/gl_stereo3d.cpp
gl/shaders/gl_shader.cpp
gl/shaders/gl_shaderprogram.cpp
gl/shaders/gl_postprocessshader.cpp
gl/stereo3d/gl_stereo3d.cpp
gl/stereo3d/gl_stereo_cvars.cpp
gl/stereo3d/gl_stereo_leftright.cpp
gl/stereo3d/gl_anaglyph.cpp
gl/stereo3d/gl_quadstereo.cpp
gl/stereo3d/gl_sidebyside3d.cpp
gl/stereo3d/gl_interleaved3d.cpp
gl_load/gl_interface.cpp
gl/system/gl_framebuffer.cpp
gl/system/gl_debug.cpp
@ -1074,6 +1080,7 @@ set (PCH_SOURCES
hwrenderer/utility/hw_cvars.cpp
hwrenderer/utility/hw_lighting.cpp
hwrenderer/utility/hw_shaderpatcher.cpp
hwrenderer/utility/hw_vrmodes.cpp
menu/joystickmenu.cpp
menu/loadsavemenu.cpp
@ -1404,7 +1411,6 @@ source_group("Hardware Renderer\\Models" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SO
source_group("Hardware Renderer\\Postprocessing" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/hwrenderer/postprocessing/.+")
source_group("Hardware Renderer\\Renderer" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/hwrenderer/renderer/.+")
source_group("Hardware Renderer\\Scene" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/hwrenderer/scene/.+")
source_group("Hardware Renderer\\Stereo3D" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/hwrenderer/stereo3d/.+")
source_group("Hardware Renderer\\Shaders" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/hwrenderer/shaders/.+")
source_group("Hardware Renderer\\System" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/hwrenderer/system/.+")
source_group("Hardware Renderer\\Textures" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/hwrenderer/textures/.+")
@ -1416,7 +1422,6 @@ source_group("OpenGL Renderer\\Dynamic Lights" REGULAR_EXPRESSION "^${CMAKE_CURR
source_group("OpenGL Renderer\\Models" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/models/.+")
source_group("OpenGL Renderer\\Renderer" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/renderer/.+")
source_group("OpenGL Renderer\\Scene" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/scene/.+")
source_group("OpenGL Renderer\\Stereo3D" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/stereo3d/.+")
source_group("OpenGL Renderer\\Shaders" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/shaders/.+")
source_group("OpenGL Renderer\\System" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/system/.+")
source_group("OpenGL Renderer\\Textures" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/textures/.+")

View file

@ -2123,13 +2123,14 @@ void AM_drawSubsectors()
double secx;
double secy;
double seczb, seczt;
double cmpz = r_viewpoint.Pos.Z;
auto &vp = r_viewpoint;
double cmpz = vp.Pos.Z;
if (players[consoleplayer].camera && sec == players[consoleplayer].camera->Sector)
{
// For the actual camera sector use the current viewpoint as reference.
secx = r_viewpoint.Pos.X;
secy = r_viewpoint.Pos.Y;
secx = vp.Pos.X;
secy = vp.Pos.Y;
}
else
{

View file

@ -104,6 +104,7 @@
EXTERN_CVAR(Bool, hud_althud)
EXTERN_CVAR(Bool, fullscreen)
EXTERN_CVAR(Int, vr_mode)
void DrawHUD();
void D_DoAnonStats();
@ -662,6 +663,7 @@ void D_Display ()
players[consoleplayer].camera = players[consoleplayer].mo;
}
auto &vp = r_viewpoint;
if (viewactive)
{
DAngle fov = 90.f;
@ -672,7 +674,7 @@ void D_Display ()
fov = cam->player->FOV;
else fov = cam->CameraFOV;
}
R_SetFOV(r_viewpoint, fov);
R_SetFOV(vp, fov);
}
// fullscreen toggle has been requested
@ -694,7 +696,7 @@ void D_Display ()
}
else
{
R_ExecuteSetViewSize (r_viewpoint, r_viewwindow);
R_ExecuteSetViewSize (vp, r_viewwindow);
}
}
@ -705,7 +707,8 @@ void D_Display ()
wipe = false;
wipegamestate = gamestate;
}
else if (gamestate != wipegamestate && gamestate != GS_FULLCONSOLE && gamestate != GS_TITLELEVEL)
// No wipes when in a stereo3D VR mode
else if (gamestate != wipegamestate && gamestate != GS_FULLCONSOLE && gamestate != GS_TITLELEVEL && (vr_mode == 0 || vid_rendermode != 4))
{ // save the current screen if about to wipe
switch (wipegamestate)
{
@ -793,7 +796,7 @@ void D_Display ()
{
StatusBar->DrawCrosshair();
}
StatusBar->CallDraw (HUD_AltHud);
StatusBar->CallDraw (HUD_AltHud, vp.TicFrac);
StatusBar->DrawTopStuff (HUD_AltHud);
}
else
@ -801,13 +804,13 @@ void D_Display ()
{
EHudState state = DrawFSHUD ? HUD_Fullscreen : HUD_None;
StatusBar->DrawBottomStuff (state);
StatusBar->CallDraw (state);
StatusBar->CallDraw (state, vp.TicFrac);
StatusBar->DrawTopStuff (state);
}
else
{
StatusBar->DrawBottomStuff (HUD_StatusBar);
StatusBar->CallDraw (HUD_StatusBar);
StatusBar->CallDraw (HUD_StatusBar, vp.TicFrac);
StatusBar->DrawTopStuff (HUD_StatusBar);
}
//stb.Unclock();

View file

@ -857,12 +857,13 @@ void DStaticEventHandler::WorldTick()
static FRenderEvent E_SetupRenderEvent()
{
FRenderEvent e;
e.ViewPos = r_viewpoint.Pos;
e.ViewAngle = r_viewpoint.Angles.Yaw;
e.ViewPitch = r_viewpoint.Angles.Pitch;
e.ViewRoll = r_viewpoint.Angles.Roll;
e.FracTic = r_viewpoint.TicFrac;
e.Camera = r_viewpoint.camera;
auto &vp = r_viewpoint;
e.ViewPos = vp.Pos;
e.ViewAngle = vp.Angles.Yaw;
e.ViewPitch = vp.Angles.Pitch;
e.ViewRoll = vp.Angles.Roll;
e.FracTic = vp.TicFrac;
e.Camera = vp.camera;
return e;
}

View file

@ -177,9 +177,9 @@ void DEarthquake::Tick ()
//
//==========================================================================
double DEarthquake::GetModWave(double waveMultiplier) const
double DEarthquake::GetModWave(double ticFrac, double waveMultiplier) const
{
double time = m_Countdown - r_viewpoint.TicFrac;
double time = m_Countdown - ticFrac;
return g_sin(waveMultiplier * time * (M_PI * 2 / TICRATE));
}
@ -292,7 +292,7 @@ double DEarthquake::GetFalloff(double dist) const
//
//==========================================================================
int DEarthquake::StaticGetQuakeIntensities(AActor *victim, FQuakeJiggers &jiggers)
int DEarthquake::StaticGetQuakeIntensities(double ticFrac, AActor *victim, FQuakeJiggers &jiggers)
{
if (victim->player != NULL && (victim->player->cheats & CF_NOCLIP))
{
@ -339,12 +339,12 @@ int DEarthquake::StaticGetQuakeIntensities(AActor *victim, FQuakeJiggers &jigger
}
else
{
jiggers.RollWave = r * quake->GetModWave(quake->m_RollWave) * falloff * strength;
jiggers.RollWave = r * quake->GetModWave(ticFrac, quake->m_RollWave) * falloff * strength;
intensity.X *= quake->GetModWave(quake->m_WaveSpeed.X);
intensity.Y *= quake->GetModWave(quake->m_WaveSpeed.Y);
intensity.Z *= quake->GetModWave(quake->m_WaveSpeed.Z);
intensity.X *= quake->GetModWave(ticFrac, quake->m_WaveSpeed.X);
intensity.Y *= quake->GetModWave(ticFrac, quake->m_WaveSpeed.Y);
intensity.Z *= quake->GetModWave(ticFrac, quake->m_WaveSpeed.Z);
intensity *= strength * falloff;
// [RH] This only gives effect to the last sine quake. I would

View file

@ -145,10 +145,10 @@ public:
double m_RollIntensity, m_RollWave;
double GetModIntensity(double intensity, bool fake = false) const;
double GetModWave(double waveMultiplier) const;
double GetModWave(double ticFrac, double waveMultiplier) const;
double GetFalloff(double dist) const;
static int StaticGetQuakeIntensities(AActor *viewer, FQuakeJiggers &jiggers);
static int StaticGetQuakeIntensities(double ticFrac, AActor *viewer, FQuakeJiggers &jiggers);
private:
DEarthquake ();

View file

@ -397,8 +397,8 @@ public:
void SetScale();
virtual void Tick ();
void CallTick();
virtual void Draw (EHudState state);
void CallDraw(EHudState state);
virtual void Draw (EHudState state, double ticFrac);
void CallDraw(EHudState state, double ticFrac);
void DrawBottomStuff (EHudState state);
void DrawTopStuff (EHudState state);
void FlashItem (const PClass *itemtype);

View file

@ -1006,7 +1006,7 @@ void DBaseStatusBar::DrawMessages (int layer, int bottom)
//
//---------------------------------------------------------------------------
void DBaseStatusBar::Draw (EHudState state)
void DBaseStatusBar::Draw (EHudState state, double ticFrac)
{
// HUD_AltHud state is for popups only
if (state == HUD_AltHud)
@ -1048,18 +1048,19 @@ DEFINE_ACTION_FUNCTION(DBaseStatusBar, Draw)
{
PARAM_SELF_PROLOGUE(DBaseStatusBar);
PARAM_INT(state);
self->Draw((EHudState)state);
PARAM_FLOAT(ticFrac);
self->Draw((EHudState)state, ticFrac);
return 0;
}
void DBaseStatusBar::CallDraw(EHudState state)
void DBaseStatusBar::CallDraw(EHudState state, double ticFrac)
{
IFVIRTUAL(DBaseStatusBar, Draw)
{
VMValue params[] = { (DObject*)this, state, r_viewpoint.TicFrac };
VMValue params[] = { (DObject*)this, state, ticFrac };
VMCall(func, params, countof(params), nullptr, 0);
}
else Draw(state);
else Draw(state, ticFrac);
screen->ClearClipRect(); // make sure the scripts don't leave a valid clipping rect behind.
BeginStatusBar(BaseSBarHorizontalResolution, BaseSBarVerticalResolution, BaseRelTop, false);
}

View file

@ -50,7 +50,7 @@ CVAR(Bool, gl_light_models, true, CVAR_ARCHIVE)
VSMatrix FGLModelRenderer::GetViewToWorldMatrix()
{
VSMatrix objectToWorldMatrix;
gl_RenderState.mViewMatrix.inverseMatrix(objectToWorldMatrix);
di->VPUniforms.mViewMatrix.inverseMatrix(objectToWorldMatrix);
return objectToWorldMatrix;
}
@ -65,7 +65,7 @@ void FGLModelRenderer::BeginDrawModel(AActor *actor, FSpriteModelFrame *smf, con
if (!(actor->RenderStyle == LegacyRenderStyles[STYLE_Normal]) && !(smf->flags & MDL_DONTCULLBACKFACES))
{
glEnable(GL_CULL_FACE);
glFrontFace((mirrored ^ GLPortal::isMirrored()) ? GL_CCW : GL_CW);
glFrontFace((mirrored ^ GLRenderer->mPortalState.isMirrored()) ? GL_CCW : GL_CW);
}
gl_RenderState.mModelMatrix = objectToWorldMatrix;
@ -91,7 +91,7 @@ void FGLModelRenderer::BeginDrawHUDModel(AActor *actor, const VSMatrix &objectTo
if (!(actor->RenderStyle == LegacyRenderStyles[STYLE_Normal]))
{
glEnable(GL_CULL_FACE);
glFrontFace((mirrored ^ GLPortal::isMirrored()) ? GL_CW : GL_CCW);
glFrontFace((mirrored ^ GLRenderer->mPortalState.isMirrored()) ? GL_CW : GL_CCW);
}
gl_RenderState.mModelMatrix = objectToWorldMatrix;
@ -314,27 +314,3 @@ void FModelVertexBuffer::SetupFrame(FModelRenderer *renderer, unsigned int frame
}
}
}
//===========================================================================
//
// gl_RenderModel
//
//===========================================================================
void gl_RenderModel(GLSprite * spr, int mli)
{
FGLModelRenderer renderer(mli);
renderer.RenderModel(spr->x, spr->y, spr->z, spr->modelframe, spr->actor);
}
//===========================================================================
//
// gl_RenderHUDModel
//
//===========================================================================
void gl_RenderHUDModel(DPSprite *psp, float ofsX, float ofsY, int mli)
{
FGLModelRenderer renderer(mli);
renderer.RenderHUDModel(psp, ofsX, ofsY);
}

View file

@ -29,12 +29,14 @@
#include "r_data/models/models.h"
class GLSprite;
struct FDrawInfo;
class FGLModelRenderer : public FModelRenderer
{
int modellightindex = -1;
FDrawInfo *di;
public:
FGLModelRenderer(int mli) : modellightindex(mli)
FGLModelRenderer(FDrawInfo *d, int mli) : modellightindex(mli), di(d)
{}
ModelRendererType GetType() const override { return GLModelRendererType; }
void BeginDrawModel(AActor *actor, FSpriteModelFrame *smf, const VSMatrix &objectToWorldMatrix, bool mirrored) override;
@ -51,5 +53,3 @@ public:
void DrawElements(int numIndices, size_t offset) override;
};
void gl_RenderModel(GLSprite * spr, int mli);
void gl_RenderHUDModel(DPSprite *psp, float ofsx, float ofsy, int mli);

View file

@ -173,7 +173,7 @@ void gl_SetFog(int lightlevel, int rellight, bool fullbright, const FColormap *c
}
// Make fog a little denser when inside a skybox
if (GLPortal::inskybox) fogdensity+=fogdensity/2;
if (GLRenderer->mPortalState.inskybox) fogdensity+=fogdensity/2;
// no fog in enhanced vision modes!

View file

@ -2,7 +2,7 @@
#define __GL_LIGHTDATA
#include "v_palette.h"
#include "p_3dfloors.h"
#include "r_defs.h"
#include "r_data/renderstyle.h"
#include "hwrenderer/utility/hw_lighting.h"
#include "r_data/colormaps.h"

View file

@ -43,8 +43,8 @@
#include "hwrenderer/postprocessing/hw_presentshader.h"
#include "hwrenderer/postprocessing/hw_postprocess.h"
#include "hwrenderer/postprocessing/hw_postprocess_cvars.h"
#include "hwrenderer/utility/hw_vrmodes.h"
#include "gl/shaders/gl_postprocessshaderinstance.h"
#include "gl/stereo3d/gl_stereo3d.h"
#include "gl/textures/gl_hwtexture.h"
#include "r_videoscale.h"
@ -239,7 +239,7 @@ void FGLRenderBuffers::RenderEffect(const FString &name)
//
//-----------------------------------------------------------------------------
void FGLRenderer::AmbientOccludeScene()
void FGLRenderer::AmbientOccludeScene(float m5)
{
FGLDebug::PushGroup("AmbientOccludeScene");
@ -252,7 +252,7 @@ void FGLRenderer::AmbientOccludeScene()
float aoStrength = gl_ssao_strength;
//float tanHalfFovy = tan(fovy * (M_PI / 360.0f));
float tanHalfFovy = 1.0f / gl_RenderState.mProjectionMatrix.get()[5];
float tanHalfFovy = 1.0f / m5;
float invFocalLenX = tanHalfFovy * (mBuffers->GetSceneWidth() / (float)mBuffers->GetSceneHeight());
float invFocalLenY = tanHalfFovy;
float nDotVBias = clamp(bias, 0.0f, 1.0f);
@ -272,8 +272,8 @@ void FGLRenderer::AmbientOccludeScene()
mBuffers->BindSceneColorTexture(1);
mLinearDepthShader->Bind(NOQUEUE);
if (gl_multisample > 1) mLinearDepthShader->Uniforms->SampleIndex = 0;
mLinearDepthShader->Uniforms->LinearizeDepthA = 1.0f / GetZFar() - 1.0f / GetZNear();
mLinearDepthShader->Uniforms->LinearizeDepthB = MAX(1.0f / GetZNear(), 1.e-8f);
mLinearDepthShader->Uniforms->LinearizeDepthA = 1.0f / screen->GetZFar() - 1.0f / screen->GetZNear();
mLinearDepthShader->Uniforms->LinearizeDepthB = MAX(1.0f / screen->GetZNear(), 1.e-8f);
mLinearDepthShader->Uniforms->InverseDepthRangeA = 1.0f;
mLinearDepthShader->Uniforms->InverseDepthRangeB = 0.0f;
mLinearDepthShader->Uniforms->Scale = sceneScale;
@ -424,18 +424,18 @@ void FGLRenderer::ApplyFXAA()
void FGLRenderer::Flush()
{
const s3d::Stereo3DMode& stereo3dMode = s3d::Stereo3DMode::getCurrentMode();
auto vrmode = VRMode::GetVRMode(true);
const auto &mSceneViewport = screen->mSceneViewport;
const auto &mScreenViewport = screen->mScreenViewport;
if (stereo3dMode.IsMono())
if (vrmode->mEyeCount == 1)
{
CopyToBackbuffer(nullptr, true);
}
else
{
// Render 2D to eye textures
for (int eye_ix = 0; eye_ix < stereo3dMode.eye_count(); ++eye_ix)
for (int eye_ix = 0; eye_ix < vrmode->mEyeCount; ++eye_ix)
{
FGLDebug::PushGroup("Eye2D");
mBuffers->BindEyeFB(eye_ix);
@ -448,7 +448,9 @@ void FGLRenderer::Flush()
FGLPostProcessState savedState;
FGLDebug::PushGroup("PresentEyes");
stereo3dMode.Present();
// Note: This here is the ONLY place in the entire engine where the OpenGL dependent parts of the Stereo3D code need to be dealt with.
// There's absolutely no need to create a overly complex class hierarchy for just this.
GLRenderer->PresentStereo();
FGLDebug::PopGroup();
}
}

View file

@ -42,6 +42,7 @@
#include "gl_load/gl_interface.h"
#include "gl/system/gl_framebuffer.h"
#include "hwrenderer/utility/hw_cvars.h"
#include "gl/scene/gl_portal.h"
#include "gl/system/gl_debug.h"
#include "gl/renderer/gl_renderer.h"
#include "gl/renderer/gl_lightdata.h"
@ -49,13 +50,11 @@
#include "gl/renderer/gl_renderbuffers.h"
#include "gl/data/gl_vertexbuffer.h"
#include "gl/scene/gl_drawinfo.h"
#include "gl/scene/gl_scenedrawer.h"
#include "hwrenderer/postprocessing/hw_ambientshader.h"
#include "hwrenderer/postprocessing/hw_presentshader.h"
#include "hwrenderer/postprocessing/hw_present3dRowshader.h"
#include "hwrenderer/postprocessing/hw_shadowmapshader.h"
#include "gl/shaders/gl_postprocessshaderinstance.h"
#include "gl/stereo3d/gl_stereo3d.h"
#include "gl/textures/gl_samplers.h"
#include "gl/dynlights/gl_lightbuffer.h"
#include "r_videoscale.h"
@ -80,11 +79,8 @@ extern bool NoInterpolateView;
FGLRenderer::FGLRenderer(OpenGLFrameBuffer *fb)
{
framebuffer = fb;
mCurrentPortal = nullptr;
mMirrorCount = 0;
mPlaneMirrorCount = 0;
mAngles = FRotator(0.f, 0.f, 0.f);
mViewVector = FVector2(0,0);
mVBO = nullptr;
mSkyVBO = nullptr;
mShaderManager = nullptr;
@ -121,6 +117,8 @@ void FGLRenderer::Initialize(int width, int height)
mCustomPostProcessShaders = new FCustomPostProcessShaders();
// needed for the core profile, because someone decided it was a good idea to remove the default VAO.
glGenQueries(1, &PortalQueryObject);
glGenVertexArrays(1, &mVAOID);
glBindVertexArray(mVAOID);
FGLDebug::LabelObject(GL_VERTEX_ARRAY, mVAOID, "FGLRenderer.mVAOID");
@ -135,14 +133,10 @@ void FGLRenderer::Initialize(int width, int height)
SetupLevel();
mShaderManager = new FShaderManager;
mSamplerManager = new FSamplerManager;
GLPortal::Initialize();
}
FGLRenderer::~FGLRenderer()
{
GLPortal::Shutdown();
FlushModels();
AActor::DeleteAllAttachedLights();
FMaterial::FlushAll();
@ -157,6 +151,8 @@ FGLRenderer::~FGLRenderer()
glBindVertexArray(0);
glDeleteVertexArrays(1, &mVAOID);
}
if (PortalQueryObject != 0) glDeleteQueries(1, &PortalQueryObject);
if (swdrawer) delete swdrawer;
if (mBuffers) delete mBuffers;
if (mPresentShader) delete mPresentShader;
@ -195,17 +191,6 @@ void FGLRenderer::SetupLevel()
//
//===========================================================================
void FGLRenderer::FlushTextures()
{
FMaterial::FlushAll();
}
//===========================================================================
//
//
//
//===========================================================================
bool FGLRenderer::StartOffscreen()
{
bool firstBind = (mFBID == 0);
@ -281,10 +266,8 @@ sector_t *FGLRenderer::RenderView(player_t* player)
fovratio = ratio;
}
GLSceneDrawer drawer;
mShadowMap.Update();
retsec = drawer.RenderViewpoint(player->camera, NULL, r_viewpoint.FieldOfView.Degrees, ratio, fovratio, true, true);
retsec = RenderViewpoint(r_viewpoint, player->camera, NULL, r_viewpoint.FieldOfView.Degrees, ratio, fovratio, true, true);
}
All.Unclock();
return retsec;
@ -311,27 +294,70 @@ void FGLRenderer::RenderTextureView(FCanvasTexture *tex, AActor *Viewpoint, doub
bounds.width = FHardwareTexture::GetTexDimension(gltex->GetWidth());
bounds.height = FHardwareTexture::GetTexDimension(gltex->GetHeight());
GLSceneDrawer drawer;
drawer.RenderViewpoint(Viewpoint, &bounds, FOV, (float)width / height, (float)width / height, false, false);
FRenderViewpoint texvp;
RenderViewpoint(texvp, Viewpoint, &bounds, FOV, (float)width / height, (float)width / height, false, false);
EndOffscreen();
tex->SetUpdated();
}
void FGLRenderer::WriteSavePic(player_t *player, FileWriter *file, int width, int height)
//===========================================================================
//
// Render the view to a savegame picture
//
//===========================================================================
void FGLRenderer::WriteSavePic (player_t *player, FileWriter *file, int width, int height)
{
// Todo: This needs to call the software renderer and process the returned image, if so desired.
// This also needs to take out parts of the scene drawer so they can be shared between renderers.
GLSceneDrawer drawer;
drawer.WriteSavePic(player, file, width, height);
IntRect bounds;
bounds.left = 0;
bounds.top = 0;
bounds.width = width;
bounds.height = height;
// if mVBO is persistently mapped we must be sure the GPU finished reading from it before we fill it with new data.
glFinish();
// Switch to render buffers dimensioned for the savepic
mBuffers = mSaveBuffers;
P_FindParticleSubsectors(); // make sure that all recently spawned particles have a valid subsector.
gl_RenderState.SetVertexBuffer(mVBO);
mVBO->Reset();
mLights->Clear();
// This shouldn't overwrite the global viewpoint even for a short time.
FRenderViewpoint savevp;
sector_t *viewsector = RenderViewpoint(savevp, players[consoleplayer].camera, &bounds, r_viewpoint.FieldOfView.Degrees, 1.6f, 1.6f, true, false);
glDisable(GL_STENCIL_TEST);
gl_RenderState.SetSoftLightLevel(-1);
CopyToBackbuffer(&bounds, false);
// strictly speaking not needed as the glReadPixels should block until the scene is rendered, but this is to safeguard against shitty drivers
glFinish();
uint8_t * scr = (uint8_t *)M_Malloc(width * height * 3);
glReadPixels(0,0,width, height,GL_RGB,GL_UNSIGNED_BYTE,scr);
M_CreatePNG (file, scr + ((height-1) * width * 3), NULL, SS_RGB, width, height, -width * 3, Gamma);
M_Free(scr);
// Switch back the screen render buffers
screen->SetViewportRects(nullptr);
mBuffers = mScreenBuffers;
}
//===========================================================================
//
//
//
//===========================================================================
void FGLRenderer::BeginFrame()
{
buffersActive = GLRenderer->mScreenBuffers->Setup(screen->mScreenViewport.width, screen->mScreenViewport.height, screen->mSceneViewport.width, screen->mSceneViewport.height);
buffersActive = mScreenBuffers->Setup(screen->mScreenViewport.width, screen->mScreenViewport.height, screen->mSceneViewport.width, screen->mSceneViewport.height);
if (buffersActive)
buffersActive = GLRenderer->mSaveBuffers->Setup(SAVEPICWIDTH, SAVEPICHEIGHT, SAVEPICWIDTH, SAVEPICHEIGHT);
buffersActive = mSaveBuffers->Setup(SAVEPICWIDTH, SAVEPICHEIGHT, SAVEPICWIDTH, SAVEPICHEIGHT);
}
//===========================================================================
@ -396,9 +422,11 @@ void FGLRenderer::Draw2D(F2DDrawer *drawer)
const auto &mScreenViewport = screen->mScreenViewport;
glViewport(mScreenViewport.left, mScreenViewport.top, mScreenViewport.width, mScreenViewport.height);
gl_RenderState.mViewMatrix.loadIdentity();
gl_RenderState.mProjectionMatrix.ortho(0, screen->GetWidth(), screen->GetHeight(), 0, -1.0f, 1.0f);
gl_RenderState.ApplyMatrices();
HWViewpointUniforms matrices;
matrices.SetDefaults();
matrices.mProjectionMatrix.ortho(0, screen->GetWidth(), screen->GetHeight(), 0, -1.0f, 1.0f);
matrices.CalcDependencies();
GLRenderer->mShaderManager->ApplyMatrices(&matrices, NORMAL_PASS);
glDisable(GL_DEPTH_TEST);
@ -524,7 +552,7 @@ void FGLRenderer::Draw2D(F2DDrawer *drawer)
}
glDisable(GL_SCISSOR_TEST);
gl_RenderState.SetVertexBuffer(GLRenderer->mVBO);
gl_RenderState.SetVertexBuffer(mVBO);
gl_RenderState.EnableTexture(true);
gl_RenderState.EnableBrightmap(true);
gl_RenderState.SetTextureMode(TM_MODULATE);

View file

@ -6,6 +6,7 @@
#include "vectors.h"
#include "r_renderer.h"
#include "r_data/matrix.h"
#include "hwrenderer/scene/hw_portal.h"
#include "gl/dynlights/gl_shadowmap.h"
#include <functional>
@ -37,8 +38,8 @@ class FGL2DDrawer;
class FHardwareTexture;
class FShadowMapShader;
class FCustomPostProcessShaders;
class GLSceneDrawer;
class SWSceneDrawer;
struct FRenderViewpoint;
#define NOQUEUE nullptr // just some token to be used as a placeholder
enum
@ -54,16 +55,14 @@ class FGLRenderer
public:
OpenGLFrameBuffer *framebuffer;
//GLPortal *mClipPortal;
GLPortal *mCurrentPortal;
int mMirrorCount;
int mPlaneMirrorCount;
float mCurrentFoV;
AActor *mViewActor;
FShaderManager *mShaderManager;
FSamplerManager *mSamplerManager;
unsigned int mFBID;
unsigned int mVAOID;
unsigned int PortalQueryObject;
int mOldFBID;
FGLRenderBuffers *mBuffers;
@ -82,21 +81,19 @@ public:
FShadowMap mShadowMap;
FRotator mAngles;
FVector2 mViewVector;
//FRotator mAngles;
FFlatVertexBuffer *mVBO;
FSkyVertexBuffer *mSkyVBO;
FLightBuffer *mLights;
SWSceneDrawer *swdrawer = nullptr;
bool mDrawingScene2D = false;
FPortalSceneState mPortalState;
bool buffersActive = false;
float mSceneClearColor[3];
float mGlobVis = 0.0f;
FGLRenderer(OpenGLFrameBuffer *fb);
~FGLRenderer() ;
@ -104,13 +101,13 @@ public:
void ClearBorders();
void FlushTextures();
void SetupLevel();
void ResetSWScene();
void PresentStereo();
void RenderScreenQuad();
void PostProcessScene(int fixedcm, const std::function<void()> &afterBloomDrawEndScene2D);
void AmbientOccludeScene();
void AmbientOccludeScene(float m5);
void UpdateCameraExposure();
void BloomScene(int fixedcm);
void TonemapScene();
@ -128,15 +125,16 @@ public:
sector_t *RenderView(player_t *player);
void BeginFrame();
void Set3DViewport(bool mainview);
sector_t *RenderViewpoint (FRenderViewpoint &mainvp, AActor * camera, IntRect * bounds, float fov, float ratio, float fovratio, bool mainview, bool toscreen);
bool StartOffscreen();
void EndOffscreen();
void FillSimplePoly(FTexture *texture, FVector2 *points, int npoints,
double originx, double originy, double scalex, double scaley,
DAngle rotation, const FColormap &colormap, PalEntry flatcolor, int lightlevel, int bottomclip);
static float GetZNear() { return 5.f; }
static float GetZFar() { return 65536.f; }
};
#include "hwrenderer/scene/hw_fakeflat.h"

View file

@ -42,7 +42,6 @@ void gl_SetTextureMode(int type);
FRenderState gl_RenderState;
CVAR(Bool, gl_direct_state_change, true, 0)
CVAR(Bool, gl_bandedswlight, false, CVAR_ARCHIVE)
static VSMatrix identityMatrix(1);
@ -62,7 +61,7 @@ static void matrixToGL(const VSMatrix &mat, int loc)
void FRenderState::Reset()
{
mTextureEnabled = true;
mClipLineShouldBeActive = mClipLineEnabled = mSplitEnabled = mBrightmapEnabled = mFogEnabled = mGlowEnabled = false;
mSplitEnabled = mBrightmapEnabled = mFogEnabled = mGlowEnabled = false;
mColorMask[0] = mColorMask[1] = mColorMask[2] = mColorMask[3] = true;
currentColorMask[0] = currentColorMask[1] = currentColorMask[2] = currentColorMask[3] = true;
mFogColor.d = -1;
@ -81,8 +80,6 @@ void FRenderState::Reset()
mLightParms[0] = mLightParms[1] = mLightParms[2] = 0.0f;
mLightParms[3] = -1.f;
mSpecialEffect = EFF_NONE;
mClipHeight = 0.f;
mClipHeightDirection = 0.f;
mGlossiness = 0.0f;
mSpecularLevel = 0.0f;
mShaderTimer = 0.0f;
@ -96,19 +93,15 @@ void FRenderState::Reset()
mInterpolationFactor = 0.0f;
mColor.Set(1.0f, 1.0f, 1.0f, 1.0f);
mCameraPos.Set(0.0f, 0.0f, 0.0f, 0.0f);
mGlowTop.Set(0.0f, 0.0f, 0.0f, 0.0f);
mGlowBottom.Set(0.0f, 0.0f, 0.0f, 0.0f);
mGlowTopPlane.Set(0.0f, 0.0f, 0.0f, 0.0f);
mGlowBottomPlane.Set(0.0f, 0.0f, 0.0f, 0.0f);
mSplitTopPlane.Set(0.0f, 0.0f, 0.0f, 0.0f);
mSplitBottomPlane.Set(0.0f, 0.0f, 0.0f, 0.0f);
mClipLine.Set(0.0f, 0.0f, 0.0f, 0.0f);
mDynColor.Set(0.0f, 0.0f, 0.0f, 0.0f);
mEffectState = 0;
activeShader = nullptr;
mProjectionMatrix.loadIdentity();
mViewMatrix.loadIdentity();
mModelMatrix.loadIdentity();
mTextureMatrix.loadIdentity();
mPassType = NORMAL_PASS;
@ -162,23 +155,17 @@ bool FRenderState::ApplyShader()
activeShader->muDesaturation.Set(mDesaturation / 255.f);
activeShader->muFogEnabled.Set(fogset);
activeShader->muPalLightLevels.Set(static_cast<int>(gl_bandedswlight) | (static_cast<int>(gl_fogmode) << 8));
activeShader->muGlobVis.Set(GLRenderer->mGlobVis / 32.0f);
activeShader->muTextureMode.Set(mTextureMode == TM_MODULATE && mTempTM == TM_OPAQUE ? TM_OPAQUE : mTextureMode);
activeShader->muCameraPos.Set(mCameraPos.vec);
activeShader->muLightParms.Set(mLightParms);
activeShader->muFogColor.Set(mFogColor);
activeShader->muObjectColor.Set(mObjectColor);
activeShader->muObjectColor2.Set(mObjectColor2);
activeShader->muDynLightColor.Set(mDynColor.vec);
activeShader->muInterpolationFactor.Set(mInterpolationFactor);
activeShader->muClipHeight.Set(mClipHeight);
activeShader->muClipHeightDirection.Set(mClipHeightDirection);
activeShader->muTimer.Set((double)(screen->FrameTime - firstFrame) * (double)mShaderTimer / 1000.);
activeShader->muAlphaThreshold.Set(mAlphaThreshold);
activeShader->muLightIndex.Set(-1);
activeShader->muClipSplit.Set(mClipSplit);
activeShader->muViewHeight.Set(viewheight);
activeShader->muSpecularMaterial.Set(mGlossiness, mSpecularLevel);
if (mGlowEnabled)
@ -213,17 +200,6 @@ bool FRenderState::ApplyShader()
activeShader->currentsplitstate = 0;
}
if (mClipLineEnabled)
{
activeShader->muClipLine.Set(mClipLine.vec);
activeShader->currentcliplinestate = 1;
}
else if (activeShader->currentcliplinestate)
{
activeShader->muClipLine.Set(-10000000.0, 0, 0, 0);
activeShader->currentcliplinestate = 0;
}
if (mTextureMatrixEnabled)
{
matrixToGL(mTextureMatrix, activeShader->texturematrix_index);
@ -304,14 +280,6 @@ void FRenderState::ApplyColorMask()
}
}
void FRenderState::ApplyMatrices()
{
if (GLRenderer->mShaderManager != NULL)
{
GLRenderer->mShaderManager->ApplyMatrices(&mProjectionMatrix, &mViewMatrix, mPassType);
}
}
void FRenderState::ApplyLightIndex(int index)
{
if (index > -1 && GLRenderer->mLights->GetBufferType() == GL_UNIFORM_BUFFER)
@ -320,20 +288,3 @@ void FRenderState::ApplyLightIndex(int index)
}
activeShader->muLightIndex.Set(index);
}
void FRenderState::SetClipHeight(float height, float direction)
{
mClipHeight = height;
mClipHeightDirection = direction;
if (gl.flags & RFL_NO_CLIP_PLANES) return;
if (direction != 0.f)
{
glEnable(GL_CLIP_DISTANCE0);
}
else
{
glDisable(GL_CLIP_DISTANCE0); // GL_CLIP_PLANE0 is the same value so no need to make a distinction
}
}

View file

@ -79,8 +79,6 @@ class FRenderState
uint8_t mFogEnabled;
bool mGlowEnabled;
bool mSplitEnabled;
bool mClipLineEnabled;
bool mClipLineShouldBeActive;
bool mBrightmapEnabled;
bool mColorMask[4];
bool currentColorMask[4];
@ -96,18 +94,15 @@ class FRenderState
bool mTextureMatrixEnabled;
bool mLastDepthClamp;
float mInterpolationFactor;
float mClipHeight, mClipHeightDirection;
float mGlossiness, mSpecularLevel;
float mShaderTimer;
FVertexBuffer *mVertexBuffer, *mCurrentVertexBuffer;
FStateVec4 mNormal;
FStateVec4 mColor;
FStateVec4 mCameraPos;
FStateVec4 mGlowTop, mGlowBottom;
FStateVec4 mGlowTopPlane, mGlowBottomPlane;
FStateVec4 mSplitTopPlane, mSplitBottomPlane;
FStateVec4 mClipLine;
PalEntry mFogColor;
PalEntry mObjectColor;
PalEntry mObjectColor2;
@ -131,11 +126,8 @@ class FRenderState
public:
VSMatrix mProjectionMatrix;
VSMatrix mViewMatrix;
VSMatrix mModelMatrix;
VSMatrix mTextureMatrix;
VSMatrix mNormalViewMatrix;
FRenderState()
{
@ -162,7 +154,6 @@ public:
void Apply();
void ApplyColorMask();
void ApplyMatrices();
void ApplyLightIndex(int index);
void SetVertexBuffer(FVertexBuffer *vb)
@ -176,33 +167,6 @@ public:
mCurrentVertexBuffer = NULL;
}
float GetClipHeight()
{
return mClipHeight;
}
float GetClipHeightDirection()
{
return mClipHeightDirection;
}
FStateVec4 &GetClipLine()
{
return mClipLine;
}
bool GetClipLineState()
{
return mClipLineEnabled;
}
bool GetClipLineShouldBeActive()
{
return mClipLineShouldBeActive;
}
void SetClipHeight(float height, float direction);
void SetNormal(FVector3 norm)
{
mNormal.Set(norm.X, norm.Y, norm.Z, 0.f);
@ -307,32 +271,6 @@ public:
}
}
void SetClipLine(line_t *line)
{
mClipLine.Set(line->v1->fX(), line->v1->fY(), line->Delta().X, line->Delta().Y);
}
void EnableClipLine(bool on)
{
if (!(gl.flags & RFL_NO_CLIP_PLANES))
{
mClipLineEnabled = on;
if (on)
{
glEnable(GL_CLIP_DISTANCE0);
}
else
{
glDisable(GL_CLIP_DISTANCE0);
}
}
else
{
// this needs to be flagged because in this case per-sector plane rendering needs to be disabled if a clip plane is active.
mClipLineShouldBeActive = on;
}
}
void EnableBrightmap(bool on)
{
mBrightmapEnabled = on;
@ -348,11 +286,6 @@ public:
mTextureMatrixEnabled = on;
}
void SetCameraPos(float x, float y, float z)
{
mCameraPos.Set(x, z, y, 0);
}
void SetGlowParams(float *t, float *b)
{
mGlowTop.Set(t[0], t[1], t[2], t[3]);

View file

@ -0,0 +1,364 @@
//
//---------------------------------------------------------------------------
//
// Copyright(C) 2015 Christopher Bruns
// All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see http://www.gnu.org/licenses/
//
//--------------------------------------------------------------------------
//
/*
** gl_stereo3d.cpp
** Stereoscopic 3D API
**
*/
#include "gl_load/gl_system.h"
#include "gl/renderer/gl_renderer.h"
#include "gl/renderer/gl_renderbuffers.h"
#include "hwrenderer/utility/hw_vrmodes.h"
#include "gl/system/gl_framebuffer.h"
#include "gl/renderer/gl_postprocessstate.h"
#include "hwrenderer/postprocessing/hw_presentshader.h"
#include "hwrenderer/postprocessing/hw_present3dRowshader.h"
EXTERN_CVAR(Int, vr_mode)
EXTERN_CVAR(Float, vid_saturation)
EXTERN_CVAR(Float, vid_brightness)
EXTERN_CVAR(Float, vid_contrast)
EXTERN_CVAR(Int, gl_satformula)
//==========================================================================
//
//
//
//==========================================================================
static void PresentAnaglyph(bool r, bool g, bool b)
{
GLRenderer->mBuffers->BindOutputFB();
GLRenderer->ClearBorders();
glColorMask(r, g, b, 1);
GLRenderer->mBuffers->BindEyeTexture(0, 0);
GLRenderer->DrawPresentTexture(screen->mOutputLetterbox, true);
glColorMask(!r, !g, !b, 1);
GLRenderer->mBuffers->BindEyeTexture(1, 0);
GLRenderer->DrawPresentTexture(screen->mOutputLetterbox, true);
glColorMask(1, 1, 1, 1);
}
//==========================================================================
//
//
//
//==========================================================================
static void PresentSideBySide()
{
GLRenderer->mBuffers->BindOutputFB();
GLRenderer->ClearBorders();
// Compute screen regions to use for left and right eye views
int leftWidth = screen->mOutputLetterbox.width / 2;
int rightWidth = screen->mOutputLetterbox.width - leftWidth;
IntRect leftHalfScreen = screen->mOutputLetterbox;
leftHalfScreen.width = leftWidth;
IntRect rightHalfScreen = screen->mOutputLetterbox;
rightHalfScreen.width = rightWidth;
rightHalfScreen.left += leftWidth;
GLRenderer->mBuffers->BindEyeTexture(0, 0);
GLRenderer->DrawPresentTexture(leftHalfScreen, true);
GLRenderer->mBuffers->BindEyeTexture(1, 0);
GLRenderer->DrawPresentTexture(rightHalfScreen, true);
}
//==========================================================================
//
//
//
//==========================================================================
static void PresentTopBottom()
{
GLRenderer->mBuffers->BindOutputFB();
GLRenderer->ClearBorders();
// Compute screen regions to use for left and right eye views
int topHeight = screen->mOutputLetterbox.height / 2;
int bottomHeight = screen->mOutputLetterbox.height - topHeight;
IntRect topHalfScreen = screen->mOutputLetterbox;
topHalfScreen.height = topHeight;
topHalfScreen.top = topHeight;
IntRect bottomHalfScreen = screen->mOutputLetterbox;
bottomHalfScreen.height = bottomHeight;
bottomHalfScreen.top = 0;
GLRenderer->mBuffers->BindEyeTexture(0, 0);
GLRenderer->DrawPresentTexture(topHalfScreen, true);
GLRenderer->mBuffers->BindEyeTexture(1, 0);
GLRenderer->DrawPresentTexture(bottomHalfScreen, true);
}
//==========================================================================
//
//
//
//==========================================================================
static void prepareInterleavedPresent(FPresentShaderBase& shader)
{
GLRenderer->mBuffers->BindOutputFB();
GLRenderer->ClearBorders();
// Bind each eye texture, for composition in the shader
GLRenderer->mBuffers->BindEyeTexture(0, 0);
GLRenderer->mBuffers->BindEyeTexture(1, 1);
glActiveTexture(GL_TEXTURE0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glActiveTexture(GL_TEXTURE1);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
const IntRect& box = screen->mOutputLetterbox;
glViewport(box.left, box.top, box.width, box.height);
shader.Bind(NOQUEUE);
if (GLRenderer->framebuffer->IsHWGammaActive())
{
shader.Uniforms->InvGamma = 1.0f;
shader.Uniforms->Contrast = 1.0f;
shader.Uniforms->Brightness = 0.0f;
shader.Uniforms->Saturation = 1.0f;
}
else
{
shader.Uniforms->InvGamma = 1.0f / clamp<float>(Gamma, 0.1f, 4.f);
shader.Uniforms->Contrast = clamp<float>(vid_contrast, 0.1f, 3.f);
shader.Uniforms->Brightness = clamp<float>(vid_brightness, -0.8f, 0.8f);
shader.Uniforms->Saturation = clamp<float>(vid_saturation, -15.0f, 15.0f);
shader.Uniforms->GrayFormula = static_cast<int>(gl_satformula);
}
shader.Uniforms->Scale = {
screen->mScreenViewport.width / (float)GLRenderer->mBuffers->GetWidth(),
screen->mScreenViewport.height / (float)GLRenderer->mBuffers->GetHeight()
};
shader.Uniforms.Set();
}
//==========================================================================
//
//
//
//==========================================================================
static void PresentColumnInterleaved()
{
FGLPostProcessState savedState;
savedState.SaveTextureBindings(2);
prepareInterleavedPresent(*GLRenderer->mPresent3dColumnShader);
// Compute absolute offset from top of screen to top of current display window
// because we need screen-relative, not window-relative, scan line parity
// Todo:
//auto clientoffset = screen->GetClientOffset();
//auto windowHOffset = clientoffset.X % 2;
int windowHOffset = 0;
GLRenderer->mPresent3dColumnShader->Uniforms->WindowPositionParity = windowHOffset;
GLRenderer->mPresent3dColumnShader->Uniforms.Set();
GLRenderer->RenderScreenQuad();
}
//==========================================================================
//
//
//
//==========================================================================
static void PresentRowInterleaved()
{
FGLPostProcessState savedState;
savedState.SaveTextureBindings(2);
prepareInterleavedPresent(*GLRenderer->mPresent3dRowShader);
// Todo:
//auto clientoffset = screen->GetClientOffset();
//auto windowVOffset = clientoffset.Y % 2;
int windowVOffset = 0;
GLRenderer->mPresent3dRowShader->Uniforms->WindowPositionParity =
(windowVOffset
+ screen->mOutputLetterbox.height + 1 // +1 because of origin at bottom
) % 2;
GLRenderer->mPresent3dRowShader->Uniforms.Set();
GLRenderer->RenderScreenQuad();
}
//==========================================================================
//
//
//
//==========================================================================
static void PresentCheckerInterleaved()
{
FGLPostProcessState savedState;
savedState.SaveTextureBindings(2);
prepareInterleavedPresent(*GLRenderer->mPresent3dCheckerShader);
// Compute absolute offset from top of screen to top of current display window
// because we need screen-relative, not window-relative, scan line parity
//auto clientoffset = screen->GetClientOffset();
//auto windowHOffset = clientoffset.X % 2;
//auto windowVOffset = clientoffset.Y % 2;
int windowHOffset = 0;
int windowVOffset = 0;
GLRenderer->mPresent3dCheckerShader->Uniforms->WindowPositionParity =
(windowVOffset
+ windowHOffset
+ screen->mOutputLetterbox.height + 1 // +1 because of origin at bottom
) % 2; // because we want the top pixel offset, but gl_FragCoord.y is the bottom pixel offset
GLRenderer->mPresent3dCheckerShader->Uniforms.Set();
GLRenderer->RenderScreenQuad();
}
//==========================================================================
//
// Sometimes the stereo render context is not ready immediately at start up
//
//==========================================================================
bool QuadStereoCheckInitialRenderContextState()
{
// Keep trying until we see at least one good OpenGL context to render to
static bool bQuadStereoSupported = false;
static bool bDecentContextWasFound = false;
static int contextCheckCount = 0;
if ((!bDecentContextWasFound) && (contextCheckCount < 200))
{
contextCheckCount += 1;
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); // This question is about the main screen display context
GLboolean supportsStereo, supportsBuffered;
glGetBooleanv(GL_DOUBLEBUFFER, &supportsBuffered);
if (supportsBuffered) // Finally, a useful OpenGL context
{
// This block will be executed exactly ONCE during a game run
bDecentContextWasFound = true; // now we can stop checking every frame...
// Now check whether this context supports hardware stereo
glGetBooleanv(GL_STEREO, &supportsStereo);
bQuadStereoSupported = supportsStereo && supportsBuffered;
}
}
return bQuadStereoSupported;
}
//==========================================================================
//
//
//
//==========================================================================
static void PresentQuadStereo()
{
if (QuadStereoCheckInitialRenderContextState())
{
GLRenderer->mBuffers->BindOutputFB();
glDrawBuffer(GL_BACK_LEFT);
GLRenderer->ClearBorders();
GLRenderer->mBuffers->BindEyeTexture(0, 0);
GLRenderer->DrawPresentTexture(screen->mOutputLetterbox, true);
glDrawBuffer(GL_BACK_RIGHT);
GLRenderer->ClearBorders();
GLRenderer->mBuffers->BindEyeTexture(1, 0);
GLRenderer->DrawPresentTexture(screen->mOutputLetterbox, true);
glDrawBuffer(GL_BACK);
}
else
{
GLRenderer->mBuffers->BindOutputFB();
GLRenderer->ClearBorders();
GLRenderer->mBuffers->BindEyeTexture(0, 0);
GLRenderer->DrawPresentTexture(screen->mOutputLetterbox, true);
}
}
void FGLRenderer::PresentStereo()
{
switch (vr_mode)
{
default:
return;
case VR_GREENMAGENTA:
PresentAnaglyph(false, true, false);
break;
case VR_REDCYAN:
PresentAnaglyph(true, false, false);
break;
case VR_AMBERBLUE:
PresentAnaglyph(true, true, false);
break;
case VR_SIDEBYSIDEFULL:
case VR_SIDEBYSIDESQUISHED:
PresentSideBySide();
break;
case VR_TOPBOTTOM:
PresentTopBottom();
break;
case VR_ROWINTERLEAVED:
PresentRowInterleaved();
break;
case VR_COLUMNINTERLEAVED:
PresentColumnInterleaved();
break;
case VR_CHECKERINTERLEAVED:
PresentCheckerInterleaved();
break;
case VR_QUADSTEREO:
PresentQuadStereo();
break;
}
}

View file

@ -37,13 +37,23 @@
#include "gl/data/gl_vertexbuffer.h"
#include "gl/scene/gl_drawinfo.h"
#include "hwrenderer/scene/hw_clipper.h"
#include "gl/scene/gl_portal.h"
#include "gl/scene/gl_scenedrawer.h"
#include "gl/renderer/gl_renderstate.h"
#include "gl/stereo3d/scoped_color_mask.h"
#include "gl/renderer/gl_quaddrawer.h"
#include "gl/dynlights/gl_lightbuffer.h"
class FDrawInfoList
{
public:
TDeletingArray<FDrawInfo *> mList;
FDrawInfo * GetNew();
void Release(FDrawInfo *);
};
static FDrawInfo * gl_drawinfo;
FDrawInfoList di_list;
@ -63,7 +73,7 @@ void FDrawInfo::DoDrawSorted(HWDrawList *dl, SortNode * head)
if (dl->drawitems[head->itemindex].rendertype == GLDIT_FLAT)
{
z = dl->flats[dl->drawitems[head->itemindex].index]->z;
relation = z > r_viewpoint.Pos.Z ? 1 : -1;
relation = z > Viewpoint.Pos.Z ? 1 : -1;
}
@ -164,23 +174,6 @@ void FDrawInfoList::Release(FDrawInfo * di)
mList.Push(di);
}
//==========================================================================
//
//
//
//==========================================================================
FDrawInfo::FDrawInfo()
{
next = NULL;
}
FDrawInfo::~FDrawInfo()
{
ClearBuffers();
}
//==========================================================================
//
// Sets up a new drawinfo struct
@ -190,12 +183,21 @@ FDrawInfo::~FDrawInfo()
// OpenGL has no use for multiple clippers so use the same one for all DrawInfos.
static Clipper staticClipper;
FDrawInfo *FDrawInfo::StartDrawInfo(GLSceneDrawer *drawer)
FDrawInfo *FDrawInfo::StartDrawInfo(FRenderViewpoint &parentvp, HWViewpointUniforms *uniforms)
{
FDrawInfo *di=di_list.GetNew();
di->mDrawer = drawer;
di->mVBO = GLRenderer->mVBO;
di->mClipper = &staticClipper;
di->Viewpoint = parentvp;
if (uniforms)
{
di->VPUniforms = *uniforms;
// The clip planes will never be inherited from the parent drawinfo.
di->VPUniforms.mClipLine.X = -1000001.f;
di->VPUniforms.mClipHeight = 0;
}
else di->VPUniforms.SetDefaults();
di->mClipper->SetViewpoint(di->Viewpoint);
staticClipper.Clear();
di->StartScene();
return di;
@ -205,7 +207,7 @@ void FDrawInfo::StartScene()
{
ClearBuffers();
next = gl_drawinfo;
outer = gl_drawinfo;
gl_drawinfo = this;
for (int i = 0; i < GLDL_TYPES; i++) drawlists[i].Reset();
decals[0].Clear();
@ -213,7 +215,7 @@ void FDrawInfo::StartScene()
hudsprites.Clear();
// Fullbright information needs to be propagated from the main view.
if (next != nullptr) FullbrightFlags = next->FullbrightFlags;
if (outer != nullptr) FullbrightFlags = outer->FullbrightFlags;
else FullbrightFlags = 0;
}
@ -223,15 +225,15 @@ void FDrawInfo::StartScene()
//
//
//==========================================================================
void FDrawInfo::EndDrawInfo()
FDrawInfo *FDrawInfo::EndDrawInfo()
{
FDrawInfo * di = gl_drawinfo;
for(int i=0;i<GLDL_TYPES;i++) di->drawlists[i].Reset();
gl_drawinfo=di->next;
di_list.Release(di);
assert(this == gl_drawinfo);
for(int i=0;i<GLDL_TYPES;i++) drawlists[i].Reset();
gl_drawinfo=static_cast<FDrawInfo*>(outer);
di_list.Release(this);
if (gl_drawinfo == nullptr)
ResetRenderDataAllocator();
return gl_drawinfo;
}
@ -245,14 +247,13 @@ void FDrawInfo::EndDrawInfo()
void FDrawInfo::SetupFloodStencil(wallseg * ws)
{
int recursion = GLPortal::GetRecursion();
int recursion = GLRenderer->mPortalState.GetRecursion();
// Create stencil
glStencilFunc(GL_EQUAL, recursion, ~0); // create stencil
glStencilOp(GL_KEEP, GL_KEEP, GL_INCR); // increment stencil of valid pixels
{
// Use revertible color mask, to avoid stomping on anaglyph 3D state
ScopedColorMask colorMask(0, 0, 0, 0); // glColorMask(0, 0, 0, 0); // don't write to the graphics buffer
glColorMask(0, 0, 0, 0); // don't write to the graphics buffer
gl_RenderState.EnableTexture(false);
gl_RenderState.ResetColor();
glEnable(GL_DEPTH_TEST);
@ -269,7 +270,7 @@ void FDrawInfo::SetupFloodStencil(wallseg * ws)
glStencilFunc(GL_EQUAL, recursion + 1, ~0); // draw sky into stencil
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // this stage doesn't modify the stencil
} // glColorMask(1, 1, 1, 1); // don't write to the graphics buffer
glColorMask(1, 1, 1, 1); // don't write to the graphics buffer
gl_RenderState.EnableTexture(true);
glDisable(GL_DEPTH_TEST);
glDepthMask(false);
@ -277,13 +278,12 @@ void FDrawInfo::SetupFloodStencil(wallseg * ws)
void FDrawInfo::ClearFloodStencil(wallseg * ws)
{
int recursion = GLPortal::GetRecursion();
int recursion = GLRenderer->mPortalState.GetRecursion();
glStencilOp(GL_KEEP,GL_KEEP,GL_DECR);
glStencilOp(GL_KEEP, GL_KEEP, GL_DECR);
gl_RenderState.EnableTexture(false);
{
// Use revertible color mask, to avoid stomping on anaglyph 3D state
ScopedColorMask colorMask(0, 0, 0, 0); // glColorMask(0,0,0,0); // don't write to the graphics buffer
glColorMask(0, 0, 0, 0); // don't write to the graphics buffer
gl_RenderState.ResetColor();
gl_RenderState.Apply();
@ -298,7 +298,7 @@ void FDrawInfo::ClearFloodStencil(wallseg * ws)
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
glStencilFunc(GL_EQUAL, recursion, ~0);
gl_RenderState.EnableTexture(true);
} // glColorMask(1, 1, 1, 1);
glColorMask(1, 1, 1, 1);
glEnable(GL_DEPTH_TEST);
glDepthMask(true);
}
@ -341,9 +341,9 @@ void FDrawInfo::DrawFloodedPlane(wallseg * ws, float planez, sector_t * sec, boo
SetFog(lightlevel, rel, &Colormap, false);
gl_RenderState.SetMaterial(gltexture, CLAMP_NONE, 0, -1, false);
float fviewx = r_viewpoint.Pos.X;
float fviewy = r_viewpoint.Pos.Y;
float fviewz = r_viewpoint.Pos.Z;
float fviewx = Viewpoint.Pos.X;
float fviewy = Viewpoint.Pos.Y;
float fviewz = Viewpoint.Pos.Z;
gl_RenderState.SetPlaneTextureRotation(&plane, gltexture);
gl_RenderState.Apply();
@ -394,7 +394,7 @@ void FDrawInfo::FloodUpperGap(seg_t * seg)
double frontz = fakefsector->ceilingplane.ZatPoint(seg->v1);
if (fakebsector->GetTexture(sector_t::ceiling)==skyflatnum) return;
if (backz < r_viewpoint.Pos.Z) return;
if (backz < Viewpoint.Pos.Z) return;
if (seg->sidedef == seg->linedef->sidedef[0])
{
@ -447,7 +447,7 @@ void FDrawInfo::FloodLowerGap(seg_t * seg)
if (fakebsector->GetTexture(sector_t::floor) == skyflatnum) return;
if (fakebsector->GetPlaneTexZ(sector_t::floor) > r_viewpoint.Pos.Z) return;
if (fakebsector->GetPlaneTexZ(sector_t::floor) > Viewpoint.Pos.Z) return;
if (seg->sidedef == seg->linedef->sidedef[0])
{
@ -478,22 +478,17 @@ void FDrawInfo::FloodLowerGap(seg_t * seg)
ClearFloodStencil(&ws);
}
// This was temporarily moved out of gl_renderhacks.cpp so that the dependency on GLWall could be eliminated until things have progressed a bit.
void FDrawInfo::ProcessLowerMinisegs(TArray<seg_t *> &lowersegs)
{
for(unsigned int j=0;j<lowersegs.Size();j++)
{
seg_t * seg=lowersegs[j];
GLWall wall;
wall.ProcessLowerMiniseg(this, seg, seg->Subsector->render_sector, seg->PartnerSeg->Subsector->render_sector);
rendered_lines++;
}
}
// Same here for the dependency on the portal.
void FDrawInfo::AddSubsectorToPortal(FSectorPortalGroup *portal, subsector_t *sub)
void FDrawInfo::AddSubsectorToPortal(FSectorPortalGroup *ptg, subsector_t *sub)
{
portal->GetRenderState()->AddSubsector(sub);
auto portal = FindPortal(ptg);
if (!portal)
{
portal = new GLScenePortal(&GLRenderer->mPortalState, new HWSectorStackPortal(ptg));
Portals.Push(portal);
}
auto ptl = static_cast<HWSectorStackPortal*>(static_cast<GLScenePortal*>(portal)->mScene);
ptl->AddSubsector(sub);
}
std::pair<FFlatVertex *, unsigned int> FDrawInfo::AllocVertices(unsigned int count)
@ -515,5 +510,10 @@ int FDrawInfo::UploadLights(FDynLightData &data)
return GLRenderer->mLights->UploadLights(data);
}
bool FDrawInfo::SetDepthClamp(bool on)
{
return gl_RenderState.SetDepthClamp(on);
}

View file

@ -4,13 +4,12 @@
#include "gl/renderer/gl_lightdata.h"
#include "hwrenderer/scene/hw_drawlist.h"
#include "hwrenderer/scene/hw_weapon.h"
#include "hwrenderer/scene/hw_viewpointuniforms.h"
#ifdef _MSC_VER
#pragma warning(disable:4244)
#endif
class GLSceneDrawer;
enum DrawListType
{
GLDL_PLAINWALLS,
@ -36,15 +35,11 @@ enum Drawpasses
struct FDrawInfo : public HWDrawInfo
{
GLSceneDrawer *mDrawer;
FDrawInfo * next;
HWDrawList drawlists[GLDL_TYPES];
TArray<HUDSprite> hudsprites; // These may just be stored by value.
TArray<GLDecal *> decals[2]; // the second slot is for mirrors which get rendered in a separate pass.
FDrawInfo();
~FDrawInfo();
void ApplyVPUniforms() override;
void AddWall(GLWall *wall) override;
void AddMirrorSurface(GLWall *w) override;
@ -57,15 +52,6 @@ struct FDrawInfo : public HWDrawInfo
std::pair<FFlatVertex *, unsigned int> AllocVertices(unsigned int count) override;
int UploadLights(FDynLightData &data) override;
// Legacy GL only.
bool PutWallCompat(GLWall *wall, int passflag);
bool PutFlatCompat(GLFlat *flat, bool fog);
void RenderFogBoundaryCompat(GLWall *wall);
void RenderLightsCompat(GLWall *wall, int pass);
void DrawSubsectorLights(GLFlat *flat, subsector_t * sub, int pass);
void DrawLightsCompat(GLFlat *flat, int pass);
void DrawDecal(GLDecal *gldecal);
void DrawDecals();
void DrawDecalsForMirror(GLWall *wall);
@ -103,11 +89,19 @@ struct FDrawInfo : public HWDrawInfo
void DrawSorted(int listindex);
// These two may be moved to the API independent part of the renderer later.
void ProcessLowerMinisegs(TArray<seg_t *> &lowersegs) override;
void AddSubsectorToPortal(FSectorPortalGroup *portal, subsector_t *sub) override;
static FDrawInfo *StartDrawInfo(GLSceneDrawer *drawer);
static void EndDrawInfo();
void CreateScene();
void RenderScene(int recursion);
void RenderTranslucent();
void DrawScene(int drawmode);
void ProcessScene(bool toscreen = false);
void EndDrawScene(sector_t * viewsector);
void DrawEndScene2D(sector_t * viewsector);
bool SetDepthClamp(bool on) override;
static FDrawInfo *StartDrawInfo(FRenderViewpoint &parentvp, HWViewpointUniforms *uniforms);
FDrawInfo *EndDrawInfo();
gl_subsectorrendernode * GetOtherFloorPlanes(unsigned int sector)
{
@ -133,16 +127,6 @@ struct FDrawInfo : public HWDrawInfo
};
class FDrawInfoList
{
TDeletingArray<FDrawInfo *> mList;
public:
FDrawInfo *GetNew();
void Release(FDrawInfo *);
};
void gl_SetRenderStyle(FRenderStyle style, bool drawopaque, bool allowcolorblending);

View file

@ -45,7 +45,6 @@
#include "gl/data/gl_vertexbuffer.h"
#include "gl/dynlights/gl_lightbuffer.h"
#include "gl/scene/gl_drawinfo.h"
#include "gl/scene/gl_scenedrawer.h"
#include "gl/renderer/gl_quaddrawer.h"
//==========================================================================
@ -167,7 +166,7 @@ void FDrawInfo::ProcessLights(GLFlat *flat, bool istrans)
{
flat->dynlightindex = GLRenderer->mLights->GetIndexPtr();
if (flat->sector->ibocount > 0 && !gl_RenderState.GetClipLineShouldBeActive())
if (flat->sector->ibocount > 0 && !ClipLineShouldBeActive())
{
SetupSectorLights(flat, GLPASS_LIGHTSONLY, nullptr);
}
@ -216,7 +215,7 @@ void FDrawInfo::DrawSubsectors(GLFlat *flat, int pass, bool processlights, bool
if (iboindex >= 0)
{
if (vcount > 0 && !gl_RenderState.GetClipLineShouldBeActive())
if (vcount > 0 && !ClipLineShouldBeActive())
{
if (processlights) SetupSectorLights(flat, GLPASS_ALL, &dli);
drawcalls.Clock();

File diff suppressed because it is too large Load diff

View file

@ -37,218 +37,59 @@
#define __GL_PORTAL_H
#include "tarray.h"
#include "r_utility.h"
#include "actor.h"
#include "gl/renderer/gl_renderer.h"
#include "gl/scene/gl_drawinfo.h"
#include "hwrenderer/scene/hw_drawstructs.h"
#include "hwrenderer/scene/hw_portal.h"
extern UniqueList<GLSkyInfo> UniqueSkies;
extern UniqueList<GLHorizonInfo> UniqueHorizons;
extern UniqueList<secplane_t> UniquePlaneMirrors;
struct GLEEHorizonPortal;
class GLSceneDrawer;
class GLPortal : public IPortal
{
static TArray<GLPortal *> portals;
static int recursion;
static unsigned int QueryObject;
protected:
static TArray<float> planestack;
static int MirrorFlag;
static int PlaneMirrorFlag;
static int renderdepth;
public:
static GLSceneDrawer *drawer;
static int PlaneMirrorMode;
static int inupperstack;
static bool inskybox;
private:
void DrawPortalStencil();
DVector3 savedviewpath[2];
DVector3 savedViewPos;
DVector3 savedViewActorPos;
DRotator savedAngles;
bool savedshowviewer;
AActor * savedviewactor;
ActorRenderFlags savedvisibility;
GLPortal *PrevPortal;
TArray<unsigned int> mPrimIndices;
protected:
TArray<GLWall> lines;
int level;
GLPortal(bool local = false) { if (!local) portals.Push(this); }
virtual ~GLPortal() { }
GLPortal(FPortalSceneState *state, bool local = false) : IPortal(state, local) { }
bool Start(bool usestencil, bool doquery, FDrawInfo *outer_di, FDrawInfo **pDi);
void End(bool usestencil);
virtual void DrawContents(FDrawInfo *di)=0;
virtual void * GetSource() const =0; // GetSource MUST be implemented!
void ClearClipper(FDrawInfo *di);
bool Start(bool usestencil, bool doquery, HWDrawInfo *outer_di, HWDrawInfo **pDi) override;
void End(HWDrawInfo *di, bool usestencil) override;
void ClearScreen(HWDrawInfo *di);
};
class GLScenePortal : public GLPortal
{
public:
HWScenePortalBase *mScene;
GLScenePortal(FPortalSceneState *state, HWScenePortalBase *handler) : GLPortal(state)
{
mScene = handler;
handler->SetOwner(this);
}
~GLScenePortal() { delete mScene; }
virtual void * GetSource() const { return mScene->GetSource(); }
virtual const char *GetName() { return mScene->GetName(); }
virtual bool IsSky() { return false; }
virtual bool NeedCap() { return true; }
virtual bool NeedDepthBuffer() { return true; }
void ClearScreen();
virtual const char *GetName() = 0;
virtual void PushState() {}
virtual void PopState() {}
public:
void RenderPortal(bool usestencil, bool doquery, FDrawInfo *outer_di)
virtual void DrawContents(HWDrawInfo *di)
{
// Start may perform an occlusion query. If that returns 0 there
// is no need to draw the stencil's contents and there's also no
// need to restore the affected area becasue there is none!
FDrawInfo *di;
if (Start(usestencil, doquery, outer_di, &di))
if (mScene->Setup(di, di->mClipper))
{
DrawContents(di);
End(usestencil);
static_cast<FDrawInfo*>(di)->DrawScene(DM_PORTAL);
mScene->Shutdown(di);
}
else ClearScreen(di);
}
void AddLine(GLWall * l)
{
lines.Push(*l);
}
static int GetRecursion()
{
return recursion;
}
static bool isMirrored() { return !!((MirrorFlag ^ PlaneMirrorFlag) & 1); }
virtual void RenderAttached(FDrawInfo *di) {}
static void BeginScene();
static void StartFrame();
static bool RenderFirstSkyPortal(int recursion, FDrawInfo *outer_di);
static void EndFrame(FDrawInfo *outer_di);
static GLPortal * FindPortal(const void * src);
static void Initialize();
static void Shutdown();
};
struct GLLinePortal : public GLPortal
{
// this must be the same as at the start of line_t, so that we can pass in this structure directly to P_ClipLineToPortal.
vertex_t *v1, *v2; // vertices, from v1 to v2
DVector2 delta; // precalculated v2 - v1 for side checking
angle_t angv1, angv2; // for quick comparisons with a line or subsector
GLLinePortal(line_t *line)
{
v1 = line->v1;
v2 = line->v2;
CalcDelta();
}
GLLinePortal(FLinePortalSpan *line)
{
if (line->lines[0]->mType != PORTT_LINKED || line->v1 == nullptr)
{
// For non-linked portals we must check the actual linedef.
line_t *lline = line->lines[0]->mDestination;
v1 = lline->v1;
v2 = lline->v2;
}
else
{
// For linked portals we can check the merged span.
v1 = line->v1;
v2 = line->v2;
}
CalcDelta();
}
void CalcDelta()
{
delta = v2->fPos() - v1->fPos();
}
line_t *line()
{
vertex_t **pv = &v1;
return reinterpret_cast<line_t*>(pv);
}
virtual int ClipSeg(seg_t *seg);
virtual int ClipSubsector(subsector_t *sub);
virtual int ClipPoint(const DVector2 &pos);
virtual bool NeedCap() { return false; }
virtual void PushState();
virtual void PopState();
};
struct GLMirrorPortal : public GLLinePortal
{
// mirror portals always consist of single linedefs!
line_t * linedef;
protected:
virtual void DrawContents(FDrawInfo *di);
virtual void * GetSource() const { return linedef; }
virtual const char *GetName();
public:
GLMirrorPortal(line_t * line)
: GLLinePortal(line)
{
linedef=line;
}
};
struct GLLineToLinePortal : public GLLinePortal
{
FLinePortalSpan *glport;
protected:
virtual void DrawContents(FDrawInfo *di);
virtual void * GetSource() const { return glport; }
virtual const char *GetName();
virtual line_t *ClipLine() { return line(); }
virtual void RenderAttached(FDrawInfo *di);
public:
GLLineToLinePortal(FLinePortalSpan *ll)
: GLLinePortal(ll)
{
glport = ll;
}
};
struct GLSkyboxPortal : public GLPortal
{
FSectorPortal * portal;
protected:
virtual void DrawContents(FDrawInfo *di);
virtual void * GetSource() const { return portal; }
virtual bool IsSky() { return true; }
virtual const char *GetName();
public:
GLSkyboxPortal(FSectorPortal * pt)
{
portal=pt;
}
virtual void RenderAttached(HWDrawInfo *di) { return mScene->RenderAttached(di); }
};
@ -258,7 +99,7 @@ struct GLSkyPortal : public GLPortal
friend struct GLEEHorizonPortal;
protected:
virtual void DrawContents(FDrawInfo *di);
virtual void DrawContents(HWDrawInfo *di);
virtual void * GetSource() const { return origin; }
virtual bool IsSky() { return true; }
virtual bool NeedDepthBuffer() { return false; }
@ -267,61 +108,14 @@ protected:
public:
GLSkyPortal(GLSkyInfo * pt, bool local = false)
: GLPortal(local)
GLSkyPortal(FPortalSceneState *state, GLSkyInfo * pt, bool local = false)
: GLPortal(state, local)
{
origin=pt;
}
};
struct GLSectorStackPortal : public GLPortal
{
TArray<subsector_t *> subsectors;
protected:
virtual ~GLSectorStackPortal();
virtual void DrawContents(FDrawInfo *di);
virtual void * GetSource() const { return origin; }
virtual bool IsSky() { return true; } // although this isn't a real sky it can be handled as one.
virtual const char *GetName();
FSectorPortalGroup *origin;
public:
GLSectorStackPortal(FSectorPortalGroup *pt)
{
origin=pt;
}
void SetupCoverage(FDrawInfo *di);
void AddSubsector(subsector_t *sub)
{
subsectors.Push(sub);
}
};
struct GLPlaneMirrorPortal : public GLPortal
{
protected:
virtual void DrawContents(FDrawInfo *di);
virtual void * GetSource() const { return origin; }
virtual const char *GetName();
virtual void PushState();
virtual void PopState();
secplane_t * origin;
public:
GLPlaneMirrorPortal(secplane_t * pt)
{
origin=pt;
}
};
struct GLHorizonPortal : public GLPortal
{
GLHorizonInfo * origin;
@ -330,7 +124,7 @@ struct GLHorizonPortal : public GLPortal
friend struct GLEEHorizonPortal;
protected:
virtual void DrawContents(FDrawInfo *di);
virtual void DrawContents(HWDrawInfo *di);
virtual void * GetSource() const { return origin; }
virtual bool NeedDepthBuffer() { return false; }
virtual bool NeedCap() { return false; }
@ -338,7 +132,7 @@ protected:
public:
GLHorizonPortal(GLHorizonInfo * pt, bool local = false);
GLHorizonPortal(FPortalSceneState *state, GLHorizonInfo * pt, FRenderViewpoint &vp, bool local = false);
};
struct GLEEHorizonPortal : public GLPortal
@ -346,7 +140,7 @@ struct GLEEHorizonPortal : public GLPortal
FSectorPortal * portal;
protected:
virtual void DrawContents(FDrawInfo *di);
virtual void DrawContents(HWDrawInfo *di);
virtual void * GetSource() const { return portal; }
virtual bool NeedDepthBuffer() { return false; }
virtual bool NeedCap() { return false; }
@ -354,7 +148,7 @@ protected:
public:
GLEEHorizonPortal(FSectorPortal *pt)
GLEEHorizonPortal(FPortalSceneState *state, FSectorPortal *pt) : GLPortal(state)
{
portal=pt;
}

View file

@ -50,11 +50,10 @@
#include "gl/renderer/gl_renderbuffers.h"
#include "gl/data/gl_vertexbuffer.h"
#include "hwrenderer/scene/hw_clipper.h"
#include "hwrenderer/scene/hw_portal.h"
#include "gl/scene/gl_drawinfo.h"
#include "gl/scene/gl_portal.h"
#include "gl/scene/gl_scenedrawer.h"
#include "gl/stereo3d/gl_stereo3d.h"
#include "hwrenderer/utility/scoped_view_shifter.h"
#include "hwrenderer/utility/hw_vrmodes.h"
//==========================================================================
//
@ -72,136 +71,25 @@ EXTERN_CVAR (Bool, r_deathcamera)
EXTERN_CVAR (Float, r_visibility)
EXTERN_CVAR (Bool, r_drawvoxels)
//-----------------------------------------------------------------------------
//
// R_FrustumAngle
//
//-----------------------------------------------------------------------------
angle_t GLSceneDrawer::FrustumAngle()
void FDrawInfo::ApplyVPUniforms()
{
float tilt = fabs(GLRenderer->mAngles.Pitch.Degrees);
VPUniforms.CalcDependencies();
GLRenderer->mShaderManager->ApplyMatrices(&VPUniforms, NORMAL_PASS);
// If the pitch is larger than this you can look all around at a FOV of 90°
if (tilt > 46.0f) return 0xffffffff;
// ok, this is a gross hack that barely works...
// but at least it doesn't overestimate too much...
double floatangle = 2.0 + (45.0 + ((tilt / 1.9)))*GLRenderer->mCurrentFoV*48.0 / AspectMultiplier(r_viewwindow.WidescreenRatio) / 90.0;
angle_t a1 = DAngle(floatangle).BAMs();
if (a1 >= ANGLE_180) return 0xffffffff;
return a1;
}
//-----------------------------------------------------------------------------
//
// resets the 3D viewport
//
//-----------------------------------------------------------------------------
void GLSceneDrawer::Reset3DViewport()
{
glViewport(screen->mScreenViewport.left, screen->mScreenViewport.top, screen->mScreenViewport.width, screen->mScreenViewport.height);
}
//-----------------------------------------------------------------------------
//
// sets 3D viewport and initial state
//
//-----------------------------------------------------------------------------
void GLSceneDrawer::Set3DViewport(bool mainview)
{
if (mainview && GLRenderer->buffersActive)
if (!(gl.flags & RFL_NO_CLIP_PLANES))
{
bool useSSAO = (gl_ssao != 0);
GLRenderer->mBuffers->BindSceneFB(useSSAO);
gl_RenderState.SetPassType(useSSAO ? GBUFFER_PASS : NORMAL_PASS);
gl_RenderState.EnableDrawBuffers(gl_RenderState.GetPassDrawBufferCount());
gl_RenderState.Apply();
if (VPUniforms.mClipHeightDirection != 0.f || VPUniforms.mClipLine.X > -10000000.0f)
{
glEnable(GL_CLIP_DISTANCE0);
}
else
{
glDisable(GL_CLIP_DISTANCE0);
}
}
// Always clear all buffers with scissor test disabled.
// This is faster on newer hardware because it allows the GPU to skip
// reading from slower memory where the full buffers are stored.
glDisable(GL_SCISSOR_TEST);
glClearColor(GLRenderer->mSceneClearColor[0], GLRenderer->mSceneClearColor[1], GLRenderer->mSceneClearColor[2], 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
const auto &bounds = screen->mSceneViewport;
glViewport(bounds.left, bounds.top, bounds.width, bounds.height);
glScissor(bounds.left, bounds.top, bounds.width, bounds.height);
glEnable(GL_SCISSOR_TEST);
glEnable(GL_MULTISAMPLE);
glEnable(GL_DEPTH_TEST);
glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_ALWAYS,0,~0); // default stencil
glStencilOp(GL_KEEP,GL_KEEP,GL_REPLACE);
}
//-----------------------------------------------------------------------------
//
// Setup the camera position
//
//-----------------------------------------------------------------------------
void GLSceneDrawer::SetViewAngle(DAngle viewangle)
{
GLRenderer->mAngles.Yaw = float(270.0-viewangle.Degrees);
DVector2 v = r_viewpoint.Angles.Yaw.ToVector();
GLRenderer->mViewVector.X = v.X;
GLRenderer->mViewVector.Y = v.Y;
R_SetViewAngle(r_viewpoint, r_viewwindow);
}
//-----------------------------------------------------------------------------
//
// SetProjection
// sets projection matrix
//
//-----------------------------------------------------------------------------
void GLSceneDrawer::SetProjection(VSMatrix matrix)
{
gl_RenderState.mProjectionMatrix.loadIdentity();
gl_RenderState.mProjectionMatrix.multMatrix(matrix);
}
//-----------------------------------------------------------------------------
//
// Setup the modelview matrix
//
//-----------------------------------------------------------------------------
void GLSceneDrawer::SetViewMatrix(float vx, float vy, float vz, bool mirror, bool planemirror)
{
float mult = mirror? -1:1;
float planemult = planemirror? -level.info->pixelstretch : level.info->pixelstretch;
gl_RenderState.mViewMatrix.loadIdentity();
gl_RenderState.mViewMatrix.rotate(GLRenderer->mAngles.Roll.Degrees, 0.0f, 0.0f, 1.0f);
gl_RenderState.mViewMatrix.rotate(GLRenderer->mAngles.Pitch.Degrees, 1.0f, 0.0f, 0.0f);
gl_RenderState.mViewMatrix.rotate(GLRenderer->mAngles.Yaw.Degrees, 0.0f, mult, 0.0f);
gl_RenderState.mViewMatrix.translate(vx * mult, -vz * planemult , -vy);
gl_RenderState.mViewMatrix.scale(-mult, planemult, 1);
}
//-----------------------------------------------------------------------------
//
// SetupView
// Setup the view rotation matrix for the given viewpoint
//
//-----------------------------------------------------------------------------
void GLSceneDrawer::SetupView(float vx, float vy, float vz, DAngle va, bool mirror, bool planemirror)
{
SetViewAngle(va);
SetViewMatrix(vx, vy, vz, mirror, planemirror);
gl_RenderState.ApplyMatrices();
}
//-----------------------------------------------------------------------------
//
@ -211,48 +99,45 @@ void GLSceneDrawer::SetupView(float vx, float vy, float vz, DAngle va, bool mirr
//
//-----------------------------------------------------------------------------
void GLSceneDrawer::CreateScene(FDrawInfo *di)
void FDrawInfo::CreateScene()
{
const auto &vp = Viewpoint;
angle_t a1 = FrustumAngle();
di->mClipper->SafeAddClipRangeRealAngles(r_viewpoint.Angles.Yaw.BAMs() + a1, r_viewpoint.Angles.Yaw.BAMs() - a1);
mClipper->SafeAddClipRangeRealAngles(vp.Angles.Yaw.BAMs() + a1, vp.Angles.Yaw.BAMs() - a1);
// reset the portal manager
GLPortal::StartFrame();
GLRenderer->mPortalState.StartFrame();
PO_LinkToSubsectors();
ProcessAll.Clock();
// clip the scene and fill the drawlists
for(auto p : level.portalGroups) p->glportal = nullptr;
Bsp.Clock();
GLRenderer->mVBO->Map();
GLRenderer->mLights->Begin();
// Give the DrawInfo the viewpoint in fixed point because that's what the nodes are.
di->viewx = FLOAT2FIXED(r_viewpoint.Pos.X);
di->viewy = FLOAT2FIXED(r_viewpoint.Pos.Y);
viewx = FLOAT2FIXED(vp.Pos.X);
viewy = FLOAT2FIXED(vp.Pos.Y);
validcount++; // used for processing sidedefs only once by the renderer.
di->mAngles = GLRenderer->mAngles;
di->mViewVector = GLRenderer->mViewVector;
di->mViewActor = GLRenderer->mViewActor;
di->mShadowMap = &GLRenderer->mShadowMap;
mShadowMap = &GLRenderer->mShadowMap;
di->RenderBSPNode (level.HeadNode());
di->PreparePlayerSprites(r_viewpoint.sector, di->in_area);
RenderBSPNode (level.HeadNode());
PreparePlayerSprites(vp.sector, in_area);
// Process all the sprites on the current portal's back side which touch the portal.
if (GLRenderer->mCurrentPortal != NULL) GLRenderer->mCurrentPortal->RenderAttached(di);
if (mCurrentPortal != nullptr) mCurrentPortal->RenderAttached(this);
Bsp.Unclock();
// And now the crappy hacks that have to be done to avoid rendering anomalies.
// These cannot be multithreaded when the time comes because all these depend
// on the global 'validcount' variable.
di->HandleMissingTextures(di->in_area); // Missing upper/lower textures
di->HandleHackedSubsectors(); // open sector hacks for deep water
di->ProcessSectorStacks(di->in_area); // merge visplanes of sector stacks
HandleMissingTextures(in_area); // Missing upper/lower textures
HandleHackedSubsectors(); // open sector hacks for deep water
ProcessSectorStacks(in_area); // merge visplanes of sector stacks
GLRenderer->mLights->Finish();
GLRenderer->mVBO->Unmap();
@ -268,36 +153,35 @@ void GLSceneDrawer::CreateScene(FDrawInfo *di)
//
//-----------------------------------------------------------------------------
void GLSceneDrawer::RenderScene(FDrawInfo *di, int recursion)
void FDrawInfo::RenderScene(int recursion)
{
const auto &vp = Viewpoint;
RenderAll.Clock();
glDepthMask(true);
if (!gl_no_skyclear) GLPortal::RenderFirstSkyPortal(recursion, di);
gl_RenderState.SetCameraPos(r_viewpoint.Pos.X, r_viewpoint.Pos.Y, r_viewpoint.Pos.Z);
if (!gl_no_skyclear) GLRenderer->mPortalState.RenderFirstSkyPortal(recursion, this);
gl_RenderState.EnableFog(true);
gl_RenderState.BlendFunc(GL_ONE,GL_ZERO);
if (gl_sort_textures)
{
di->drawlists[GLDL_PLAINWALLS].SortWalls();
di->drawlists[GLDL_PLAINFLATS].SortFlats();
di->drawlists[GLDL_MASKEDWALLS].SortWalls();
di->drawlists[GLDL_MASKEDFLATS].SortFlats();
di->drawlists[GLDL_MASKEDWALLSOFS].SortWalls();
drawlists[GLDL_PLAINWALLS].SortWalls();
drawlists[GLDL_PLAINFLATS].SortFlats();
drawlists[GLDL_MASKEDWALLS].SortWalls();
drawlists[GLDL_MASKEDFLATS].SortFlats();
drawlists[GLDL_MASKEDWALLSOFS].SortWalls();
}
// if we don't have a persistently mapped buffer, we have to process all the dynamic lights up front,
// so that we don't have to do repeated map/unmap calls on the buffer.
if (gl.lightmethod == LM_DEFERRED && level.HasDynamicLights && !di->isFullbrightScene())
if (gl.lightmethod == LM_DEFERRED && level.HasDynamicLights && !isFullbrightScene())
{
GLRenderer->mLights->Begin();
di->drawlists[GLDL_PLAINFLATS].DrawFlats(di, GLPASS_LIGHTSONLY);
di->drawlists[GLDL_MASKEDFLATS].DrawFlats(di, GLPASS_LIGHTSONLY);
di->drawlists[GLDL_TRANSLUCENTBORDER].Draw(di, GLPASS_LIGHTSONLY);
di->drawlists[GLDL_TRANSLUCENT].Draw(di, GLPASS_LIGHTSONLY, true);
drawlists[GLDL_PLAINFLATS].DrawFlats(this, GLPASS_LIGHTSONLY);
drawlists[GLDL_MASKEDFLATS].DrawFlats(this, GLPASS_LIGHTSONLY);
drawlists[GLDL_TRANSLUCENTBORDER].Draw(this, GLPASS_LIGHTSONLY);
drawlists[GLDL_TRANSLUCENT].Draw(this, GLPASS_LIGHTSONLY, true);
GLRenderer->mLights->Finish();
}
@ -310,8 +194,8 @@ void GLSceneDrawer::RenderScene(FDrawInfo *di, int recursion)
gl_RenderState.EnableTexture(gl_texture);
gl_RenderState.EnableBrightmap(true);
di->drawlists[GLDL_PLAINWALLS].DrawWalls(di, pass);
di->drawlists[GLDL_PLAINFLATS].DrawFlats(di, pass);
drawlists[GLDL_PLAINWALLS].DrawWalls(this, pass);
drawlists[GLDL_PLAINFLATS].DrawFlats(this, pass);
// Part 2: masked geometry. This is set up so that only pixels with alpha>gl_mask_threshold will show
@ -321,20 +205,20 @@ void GLSceneDrawer::RenderScene(FDrawInfo *di, int recursion)
gl_RenderState.SetTextureMode(TM_MASK);
}
gl_RenderState.AlphaFunc(GL_GEQUAL, gl_mask_threshold);
di->drawlists[GLDL_MASKEDWALLS].DrawWalls(di, pass);
di->drawlists[GLDL_MASKEDFLATS].DrawFlats(di, pass);
drawlists[GLDL_MASKEDWALLS].DrawWalls(this, pass);
drawlists[GLDL_MASKEDFLATS].DrawFlats(this, pass);
// Part 3: masked geometry with polygon offset. This list is empty most of the time so only waste time on it when in use.
if (di->drawlists[GLDL_MASKEDWALLSOFS].Size() > 0)
if (drawlists[GLDL_MASKEDWALLSOFS].Size() > 0)
{
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(-1.0f, -128.0f);
di->drawlists[GLDL_MASKEDWALLSOFS].DrawWalls(di, pass);
drawlists[GLDL_MASKEDWALLSOFS].DrawWalls(this, pass);
glDisable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(0, 0);
}
di->drawlists[GLDL_MODELS].Draw(di, pass);
drawlists[GLDL_MODELS].Draw(this, pass);
gl_RenderState.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
@ -343,7 +227,7 @@ void GLSceneDrawer::RenderScene(FDrawInfo *di, int recursion)
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(-1.0f, -128.0f);
glDepthMask(false);
di->DrawDecals();
DrawDecals();
gl_RenderState.SetTextureMode(TM_MODULATE);
@ -361,7 +245,7 @@ void GLSceneDrawer::RenderScene(FDrawInfo *di, int recursion)
gl_RenderState.EnableFog(true);
gl_RenderState.AlphaFunc(GL_GEQUAL, 0.f);
gl_RenderState.BlendFunc(GL_ONE,GL_ZERO);
di->DrawUnhandledMissingTextures();
DrawUnhandledMissingTextures();
glDepthMask(true);
glPolygonOffset(0.0f, 0.0f);
@ -378,20 +262,18 @@ void GLSceneDrawer::RenderScene(FDrawInfo *di, int recursion)
//
//-----------------------------------------------------------------------------
void GLSceneDrawer::RenderTranslucent(FDrawInfo *di)
void FDrawInfo::RenderTranslucent()
{
RenderAll.Clock();
gl_RenderState.SetCameraPos(r_viewpoint.Pos.X, r_viewpoint.Pos.Y, r_viewpoint.Pos.Z);
// final pass: translucent stuff
gl_RenderState.AlphaFunc(GL_GEQUAL, gl_mask_sprite_threshold);
gl_RenderState.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
gl_RenderState.EnableBrightmap(true);
di->drawlists[GLDL_TRANSLUCENTBORDER].Draw(di, GLPASS_TRANSLUCENT);
drawlists[GLDL_TRANSLUCENTBORDER].Draw(this, GLPASS_TRANSLUCENT);
glDepthMask(false);
di->DrawSorted(GLDL_TRANSLUCENT);
DrawSorted(GLDL_TRANSLUCENT);
gl_RenderState.EnableBrightmap(false);
@ -411,10 +293,11 @@ void GLSceneDrawer::RenderTranslucent(FDrawInfo *di)
//
//-----------------------------------------------------------------------------
void GLSceneDrawer::DrawScene(FDrawInfo *di, int drawmode)
void FDrawInfo::DrawScene(int drawmode)
{
static int recursion=0;
static int ssao_portals_available = 0;
const auto &vp = Viewpoint;
bool applySSAO = false;
if (drawmode == DM_MAINVIEW)
@ -432,35 +315,35 @@ void GLSceneDrawer::DrawScene(FDrawInfo *di, int drawmode)
ssao_portals_available--;
}
if (r_viewpoint.camera != nullptr)
if (vp.camera != nullptr)
{
ActorRenderFlags savedflags = r_viewpoint.camera->renderflags;
CreateScene(di);
r_viewpoint.camera->renderflags = savedflags;
ActorRenderFlags savedflags = vp.camera->renderflags;
CreateScene();
vp.camera->renderflags = savedflags;
}
else
{
CreateScene(di);
CreateScene();
}
RenderScene(di, recursion);
RenderScene(recursion);
if (applySSAO && gl_RenderState.GetPassType() == GBUFFER_PASS)
{
gl_RenderState.EnableDrawBuffers(1);
GLRenderer->AmbientOccludeScene();
GLRenderer->AmbientOccludeScene(VPUniforms.mProjectionMatrix.get()[5]);
GLRenderer->mBuffers->BindSceneFB(true);
gl_RenderState.EnableDrawBuffers(gl_RenderState.GetPassDrawBufferCount());
gl_RenderState.Apply();
gl_RenderState.ApplyMatrices();
ApplyVPUniforms();
}
// Handle all portals after rendering the opaque objects but before
// doing all translucent stuff
recursion++;
GLPortal::EndFrame(di);
GLRenderer->mPortalState.EndFrame(this);
recursion--;
RenderTranslucent(di);
RenderTranslucent();
}
//-----------------------------------------------------------------------------
@ -470,7 +353,7 @@ void GLSceneDrawer::DrawScene(FDrawInfo *di, int drawmode)
//-----------------------------------------------------------------------------
void GLSceneDrawer::EndDrawScene(FDrawInfo *di, sector_t * viewsector)
void FDrawInfo::EndDrawScene(sector_t * viewsector)
{
gl_RenderState.EnableFog(false);
@ -480,12 +363,11 @@ void GLSceneDrawer::EndDrawScene(FDrawInfo *di, sector_t * viewsector)
{
// [BB] The HUD model should be drawn over everything else already drawn.
glClear(GL_DEPTH_BUFFER_BIT);
di->DrawPlayerSprites(true);
DrawPlayerSprites(true);
}
glDisable(GL_STENCIL_TEST);
Reset3DViewport();
glViewport(screen->mScreenViewport.left, screen->mScreenViewport.top, screen->mScreenViewport.width, screen->mScreenViewport.height);
// Restore standard rendering state
gl_RenderState.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
@ -494,19 +376,19 @@ void GLSceneDrawer::EndDrawScene(FDrawInfo *di, sector_t * viewsector)
glDisable(GL_SCISSOR_TEST);
}
void GLSceneDrawer::DrawEndScene2D(FDrawInfo *di, sector_t * viewsector)
void FDrawInfo::DrawEndScene2D(sector_t * viewsector)
{
const bool renderHUDModel = IsHUDModelForPlayerAvailable(players[consoleplayer].camera->player);
auto vrmode = VRMode::GetVRMode(true);
// This should be removed once all 2D stuff is really done through the 2D interface.
gl_RenderState.mViewMatrix.loadIdentity();
gl_RenderState.mProjectionMatrix.ortho(0, screen->GetWidth(), screen->GetHeight(), 0, -1.0f, 1.0f);
gl_RenderState.ApplyMatrices();
VPUniforms.mViewMatrix.loadIdentity();
VPUniforms.mProjectionMatrix = vrmode->GetHUDSpriteProjection();
ApplyVPUniforms();
glDisable(GL_DEPTH_TEST);
glDisable(GL_MULTISAMPLE);
di->DrawPlayerSprites(false);
DrawPlayerSprites(false);
gl_RenderState.SetSoftLightLevel(-1);
@ -523,152 +405,99 @@ void GLSceneDrawer::DrawEndScene2D(FDrawInfo *di, sector_t * viewsector)
//
//-----------------------------------------------------------------------------
void GLSceneDrawer::ProcessScene(FDrawInfo *di, bool toscreen)
void FDrawInfo::ProcessScene(bool toscreen)
{
iter_dlightf = iter_dlight = draw_dlight = draw_dlightf = 0;
GLPortal::BeginScene();
GLRenderer->mPortalState.BeginScene();
int mapsection = R_PointInSubsector(r_viewpoint.Pos)->mapsection;
di->CurrentMapSections.Set(mapsection);
GLRenderer->mCurrentPortal = nullptr;
DrawScene(di, toscreen ? DM_MAINVIEW : DM_OFFSCREEN);
int mapsection = R_PointInSubsector(Viewpoint.Pos)->mapsection;
CurrentMapSections.Set(mapsection);
DrawScene(toscreen ? DM_MAINVIEW : DM_OFFSCREEN);
}
//-----------------------------------------------------------------------------
//
// sets 3D viewport and initial state
//
//-----------------------------------------------------------------------------
void FGLRenderer::Set3DViewport(bool mainview)
{
if (mainview && buffersActive)
{
bool useSSAO = (gl_ssao != 0);
mBuffers->BindSceneFB(useSSAO);
gl_RenderState.SetPassType(useSSAO ? GBUFFER_PASS : NORMAL_PASS);
gl_RenderState.EnableDrawBuffers(gl_RenderState.GetPassDrawBufferCount());
gl_RenderState.Apply();
}
// Always clear all buffers with scissor test disabled.
// This is faster on newer hardware because it allows the GPU to skip
// reading from slower memory where the full buffers are stored.
glDisable(GL_SCISSOR_TEST);
glClearColor(mSceneClearColor[0], mSceneClearColor[1], mSceneClearColor[2], 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
const auto &bounds = screen->mSceneViewport;
glViewport(bounds.left, bounds.top, bounds.width, bounds.height);
glScissor(bounds.left, bounds.top, bounds.width, bounds.height);
glEnable(GL_SCISSOR_TEST);
glEnable(GL_MULTISAMPLE);
glEnable(GL_DEPTH_TEST);
glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_ALWAYS,0,~0); // default stencil
glStencilOp(GL_KEEP,GL_KEEP,GL_REPLACE);
}
//-----------------------------------------------------------------------------
//
// Renders one viewpoint in a scene
//
//-----------------------------------------------------------------------------
sector_t * GLSceneDrawer::RenderViewpoint (AActor * camera, IntRect * bounds, float fov, float ratio, float fovratio, bool mainview, bool toscreen)
sector_t * FGLRenderer::RenderViewpoint (FRenderViewpoint &mainvp, AActor * camera, IntRect * bounds, float fov, float ratio, float fovratio, bool mainview, bool toscreen)
{
sector_t * lviewsector;
GLRenderer->mSceneClearColor[0] = 0.0f;
GLRenderer->mSceneClearColor[1] = 0.0f;
GLRenderer->mSceneClearColor[2] = 0.0f;
R_SetupFrame (r_viewpoint, r_viewwindow, camera);
GLRenderer->mGlobVis = R_GetGlobVis(r_viewwindow, r_visibility);
// We have to scale the pitch to account for the pixel stretching, because the playsim doesn't know about this and treats it as 1:1.
double radPitch = r_viewpoint.Angles.Pitch.Normalized180().Radians();
double angx = cos(radPitch);
double angy = sin(radPitch) * level.info->pixelstretch;
double alen = sqrt(angx*angx + angy*angy);
GLRenderer->mAngles.Pitch = (float)RAD2DEG(asin(angy / alen));
GLRenderer->mAngles.Roll.Degrees = r_viewpoint.Angles.Roll.Degrees;
if (camera->player && camera->player-players==consoleplayer &&
((camera->player->cheats & CF_CHASECAM) || (r_deathcamera && camera->health <= 0)) && camera==camera->player->mo)
{
GLRenderer->mViewActor=NULL;
}
else
{
GLRenderer->mViewActor=camera;
}
// 'viewsector' will not survive the rendering so it cannot be used anymore below.
lviewsector = r_viewpoint.sector;
R_SetupFrame (mainvp, r_viewwindow, camera);
// Render (potentially) multiple views for stereo 3d
float viewShift[3];
const s3d::Stereo3DMode& stereo3dMode = mainview && toscreen? s3d::Stereo3DMode::getCurrentMode() : s3d::Stereo3DMode::getMonoMode();
stereo3dMode.SetUp();
for (int eye_ix = 0; eye_ix < stereo3dMode.eye_count(); ++eye_ix)
// Fixme. The view offsetting should be done with a static table and not require setup of the entire render state for the mode.
auto vrmode = VRMode::GetVRMode(mainview && toscreen);
for (int eye_ix = 0; eye_ix < vrmode->mEyeCount; ++eye_ix)
{
const s3d::EyePose * eye = stereo3dMode.getEyePose(eye_ix);
eye->SetUp();
const auto &eye = vrmode->mEyes[eye_ix];
screen->SetViewportRects(bounds);
Set3DViewport(mainview);
GLRenderer->mDrawingScene2D = true;
GLRenderer->mCurrentFoV = fov;
FDrawInfo *di = FDrawInfo::StartDrawInfo(this);
FDrawInfo *di = FDrawInfo::StartDrawInfo(mainvp, nullptr);
auto &vp = di->Viewpoint;
di->SetViewArea();
auto cm = di->SetFullbrightFlags(mainview ? r_viewpoint.camera->player : nullptr);
auto cm = di->SetFullbrightFlags(mainview ? vp.camera->player : nullptr);
di->Viewpoint.FieldOfView = fov; // Set the real FOV for the current scene (it's not necessarily the same as the global setting in r_viewpoint)
// Stereo mode specific perspective projection
SetProjection( eye->GetProjection(fov, ratio, fovratio) );
// SetProjection(fov, ratio, fovratio); // switch to perspective mode and set up clipper
SetViewAngle(r_viewpoint.Angles.Yaw);
// Stereo mode specific viewpoint adjustment - temporarily shifts global ViewPos
eye->GetViewShift(GLRenderer->mAngles.Yaw.Degrees, viewShift);
ScopedViewShifter viewShifter(r_viewpoint.Pos, viewShift);
SetViewMatrix(r_viewpoint.Pos.X, r_viewpoint.Pos.Y, r_viewpoint.Pos.Z, false, false);
gl_RenderState.ApplyMatrices();
di->VPUniforms.mProjectionMatrix = eye.GetProjection(fov, ratio, fovratio);
// Stereo mode specific viewpoint adjustment
vp.Pos += eye.GetViewShift(vp.HWAngles.Yaw.Degrees);
di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, false, false);
ProcessScene(di, toscreen);
di->ProcessScene(toscreen);
if (mainview)
{
if (toscreen) EndDrawScene(di, lviewsector); // do not call this for camera textures.
GLRenderer->PostProcessScene(cm, [&]() { DrawEndScene2D(di, lviewsector); });
// This should be done after postprocessing, not before.
GLRenderer->mBuffers->BindCurrentFB();
glViewport(screen->mScreenViewport.left, screen->mScreenViewport.top, screen->mScreenViewport.width, screen->mScreenViewport.height);
if (!toscreen)
{
gl_RenderState.mViewMatrix.loadIdentity();
gl_RenderState.mProjectionMatrix.ortho(screen->mScreenViewport.left, screen->mScreenViewport.width, screen->mScreenViewport.height, screen->mScreenViewport.top, -1.0f, 1.0f);
gl_RenderState.ApplyMatrices();
if (toscreen) di->EndDrawScene(mainvp.sector); // do not call this for camera textures.
PostProcessScene(cm, [&]() { di->DrawEndScene2D(mainvp.sector); });
}
di->EndDrawInfo();
if (vrmode->mEyeCount > 1)
mBuffers->BlitToEyeTexture(eye_ix);
}
FDrawInfo::EndDrawInfo();
GLRenderer->mDrawingScene2D = false;
if (!stereo3dMode.IsMono())
GLRenderer->mBuffers->BlitToEyeTexture(eye_ix);
eye->TearDown();
}
stereo3dMode.TearDown();
interpolator.RestoreInterpolations ();
return lviewsector;
return mainvp.sector;
}
//===========================================================================
//
// Render the view to a savegame picture
//
//===========================================================================
void GLSceneDrawer::WriteSavePic (player_t *player, FileWriter *file, int width, int height)
{
IntRect bounds;
bounds.left = 0;
bounds.top = 0;
bounds.width = width;
bounds.height = height;
// if GLRenderer->mVBO is persistently mapped we must be sure the GPU finished reading from it before we fill it with new data.
glFinish();
// Switch to render buffers dimensioned for the savepic
GLRenderer->mBuffers = GLRenderer->mSaveBuffers;
P_FindParticleSubsectors(); // make sure that all recently spawned particles have a valid subsector.
gl_RenderState.SetVertexBuffer(GLRenderer->mVBO);
GLRenderer->mVBO->Reset();
GLRenderer->mLights->Clear();
sector_t *viewsector = RenderViewpoint(players[consoleplayer].camera, &bounds, r_viewpoint.FieldOfView.Degrees, 1.6f, 1.6f, true, false);
glDisable(GL_STENCIL_TEST);
gl_RenderState.SetSoftLightLevel(-1);
GLRenderer->CopyToBackbuffer(&bounds, false);
// strictly speaking not needed as the glReadPixels should block until the scene is rendered, but this is to safeguard against shitty drivers
glFinish();
uint8_t * scr = (uint8_t *)M_Malloc(width * height * 3);
glReadPixels(0,0,width, height,GL_RGB,GL_UNSIGNED_BYTE,scr);
M_CreatePNG (file, scr + ((height-1) * width * 3), NULL, SS_RGB, width, height, -width * 3, Gamma);
M_Free(scr);
// Switch back the screen render buffers
screen->SetViewportRects(nullptr);
GLRenderer->mBuffers = GLRenderer->mScreenBuffers;
}

View file

@ -1,47 +0,0 @@
#pragma once
#include "r_defs.h"
#include "m_fixed.h"
#include "hwrenderer/scene/hw_clipper.h"
#include "gl_portal.h"
#include "gl/renderer/gl_lightdata.h"
#include "gl/renderer/gl_renderer.h"
#include "r_utility.h"
#include "c_cvars.h"
struct HUDSprite;
class GLSceneDrawer
{
TMap<DPSprite*, int> weapondynlightindex;
void RenderMultipassStuff(FDrawInfo *di);
void RenderScene(FDrawInfo *di, int recursion);
void RenderTranslucent(FDrawInfo *di);
void CreateScene(FDrawInfo *di);
public:
GLSceneDrawer()
{
GLPortal::drawer = this;
}
angle_t FrustumAngle();
void SetViewMatrix(float vx, float vy, float vz, bool mirror, bool planemirror);
void SetupView(float vx, float vy, float vz, DAngle va, bool mirror, bool planemirror);
void SetViewAngle(DAngle viewangle);
void SetProjection(VSMatrix matrix);
void Set3DViewport(bool mainview);
void Reset3DViewport();
void DrawScene(FDrawInfo *di, int drawmode);
void ProcessScene(FDrawInfo *di, bool toscreen = false);
void EndDrawScene(FDrawInfo *di, sector_t * viewsector);
void DrawEndScene2D(FDrawInfo *di, sector_t * viewsector);
sector_t *RenderViewpoint(AActor * camera, IntRect * bounds, float fov, float ratio, float fovratio, bool mainview, bool toscreen);
sector_t *RenderView(player_t *player);
void WriteSavePic(player_t *player, FileWriter *file, int width, int height);
};

View file

@ -32,9 +32,10 @@
#include "gl_load/gl_interface.h"
#include "gl/data/gl_vertexbuffer.h"
#include "gl/renderer/gl_lightdata.h"
#include "gl/renderer/gl_renderer.h"
#include "gl/renderer/gl_renderstate.h"
#include "gl/scene/gl_drawinfo.h"
#include "gl/scene/gl_scenedrawer.h"
#include "gl/scene/gl_portal.h"
#include "gl/shaders/gl_shader.h"
@ -209,9 +210,10 @@ static void RenderBox(FTextureID texno, FMaterial * gltex, float x_offset, bool
//
//
//-----------------------------------------------------------------------------
void GLSkyPortal::DrawContents(FDrawInfo *di)
void GLSkyPortal::DrawContents(HWDrawInfo *di)
{
bool drawBoth = false;
auto &vp = di->Viewpoint;
// We have no use for Doom lighting special handling here, so disable it for this function.
int oldlightmode = ::level.lightmode;
@ -228,8 +230,7 @@ void GLSkyPortal::DrawContents(FDrawInfo *di)
gl_RenderState.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
bool oldClamp = gl_RenderState.SetDepthClamp(true);
gl_MatrixStack.Push(gl_RenderState.mViewMatrix);
drawer->SetupView(0, 0, 0, r_viewpoint.Angles.Yaw, !!(MirrorFlag & 1), !!(PlaneMirrorFlag & 1));
di->SetupView(0, 0, 0, !!(mState->MirrorFlag & 1), !!(mState->PlaneMirrorFlag & 1));
gl_RenderState.SetVertexBuffer(GLRenderer->mSkyVBO);
if (origin->texture[0] && origin->texture[0]->tex->bSkybox)
@ -268,8 +269,7 @@ void GLSkyPortal::DrawContents(FDrawInfo *di)
}
}
gl_RenderState.SetVertexBuffer(GLRenderer->mVBO);
gl_MatrixStack.Pop(gl_RenderState.mViewMatrix);
gl_RenderState.ApplyMatrices();
di->ApplyVPUniforms();
::level.lightmode = oldlightmode;
gl_RenderState.SetDepthClamp(oldClamp);
}

View file

@ -47,7 +47,6 @@
#include "gl/renderer/gl_renderstate.h"
#include "gl/renderer/gl_renderer.h"
#include "gl/scene/gl_drawinfo.h"
#include "gl/scene/gl_scenedrawer.h"
#include "gl/models/gl_models.h"
#include "gl/renderer/gl_quaddrawer.h"
#include "gl/dynlights/gl_lightbuffer.h"
@ -79,6 +78,7 @@ void FDrawInfo::DrawSprite(GLSprite *sprite, int pass)
bool additivefog = false;
bool foglayer = false;
int rel = sprite->fullbright? 0 : getExtraLight();
auto &vp = Viewpoint;
if (pass==GLPASS_TRANSLUCENT)
{
@ -112,7 +112,7 @@ void FDrawInfo::DrawSprite(GLSprite *sprite, int pass)
// fog + fuzz don't work well without some fiddling with the alpha value!
if (!sprite->Colormap.FadeColor.isBlack())
{
float dist=Dist2(r_viewpoint.Pos.X, r_viewpoint.Pos.Y, sprite->x, sprite->y);
float dist=Dist2(vp.Pos.X, vp.Pos.Y, sprite->x, sprite->y);
int fogd = hw_GetFogDensity(sprite->lightlevel, sprite->Colormap.FadeColor, sprite->Colormap.FogDensity);
// this value was determined by trial and error and is scale dependent!
@ -221,7 +221,7 @@ void FDrawInfo::DrawSprite(GLSprite *sprite, int pass)
secplane_t *lowplane = i == (*lightlist).Size() - 1 ? &bottomp : &(*lightlist)[i + 1].plane;
int thislight = (*lightlist)[i].caster != nullptr ? hw_ClampLight(*(*lightlist)[i].p_lightlevel) : sprite->lightlevel;
int thisll = sprite->actor == nullptr? thislight : (uint8_t)sprite->actor->Sector->CheckSpriteGlow(thislight, sprite->actor->InterpolatedPosition(r_viewpoint.TicFrac));
int thisll = sprite->actor == nullptr? thislight : (uint8_t)sprite->actor->Sector->CheckSpriteGlow(thislight, sprite->actor->InterpolatedPosition(vp.TicFrac));
FColormap thiscm;
thiscm.CopyFog(sprite->Colormap);
@ -249,7 +249,8 @@ void FDrawInfo::DrawSprite(GLSprite *sprite, int pass)
FVector3 v[4];
gl_RenderState.SetNormal(0, 0, 0);
if (sprite->CalculateVertices(this, v))
if (sprite->CalculateVertices(this, v, &vp.Pos))
{
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(-1.0f, -128.0f);
@ -276,7 +277,8 @@ void FDrawInfo::DrawSprite(GLSprite *sprite, int pass)
}
else
{
gl_RenderModel(sprite, sprite->dynlightindex);
FGLModelRenderer renderer(this, sprite->dynlightindex);
renderer.RenderModel(sprite->x, sprite->y, sprite->z, sprite->modelframe, sprite->actor, vp.TicFrac);
}
}

View file

@ -37,7 +37,6 @@
#include "gl/dynlights/gl_lightbuffer.h"
#include "gl/scene/gl_drawinfo.h"
#include "gl/scene/gl_portal.h"
#include "gl/scene/gl_scenedrawer.h"
EXTERN_CVAR(Bool, gl_seamless)
@ -94,7 +93,6 @@ void FDrawInfo::RenderMirrorSurface(GLWall *wall)
// we use texture coordinates and texture matrix to pass the normal stuff to the shader so that the default vertex buffer format can be used as is.
gl_RenderState.EnableTextureMatrix(true);
gl_RenderState.mTextureMatrix.computeNormalMatrix(gl_RenderState.mViewMatrix);
// Use sphere mapping for this
gl_RenderState.SetEffect(EFF_SPHEREMAP);
@ -339,7 +337,8 @@ void FDrawInfo::AddMirrorSurface(GLWall *w)
void FDrawInfo::AddPortal(GLWall *wall, int ptype)
{
GLPortal * portal;
auto &pstate = GLRenderer->mPortalState;
IPortal * portal;
wall->MakeVertices(this, false);
switch (ptype)
@ -347,42 +346,63 @@ void FDrawInfo::AddPortal(GLWall *wall, int ptype)
// portals don't go into the draw list.
// Instead they are added to the portal manager
case PORTALTYPE_HORIZON:
wall->horizon = UniqueHorizons.Get(wall->horizon);
portal = GLPortal::FindPortal(wall->horizon);
if (!portal) portal = new GLHorizonPortal(wall->horizon);
wall->horizon = pstate.UniqueHorizons.Get(wall->horizon);
portal = FindPortal(wall->horizon);
if (!portal)
{
portal = new GLHorizonPortal(&pstate, wall->horizon, Viewpoint);
Portals.Push(portal);
}
portal->AddLine(wall);
break;
case PORTALTYPE_SKYBOX:
portal = GLPortal::FindPortal(wall->secportal);
portal = FindPortal(wall->secportal);
if (!portal)
{
// either a regular skybox or an Eternity-style horizon
if (wall->secportal->mType != PORTS_SKYVIEWPOINT) portal = new GLEEHorizonPortal(wall->secportal);
else portal = new GLSkyboxPortal(wall->secportal);
if (wall->secportal->mType != PORTS_SKYVIEWPOINT) portal = new GLEEHorizonPortal(&pstate, wall->secportal);
else
{
portal = new GLScenePortal(&pstate, new HWSkyboxPortal(wall->secportal));
Portals.Push(portal);
}
}
portal->AddLine(wall);
break;
case PORTALTYPE_SECTORSTACK:
portal = wall->portal->GetRenderState();
portal = FindPortal(wall->portal);
if (!portal)
{
portal = new GLScenePortal(&pstate, new HWSectorStackPortal(wall->portal));
Portals.Push(portal);
}
portal->AddLine(wall);
break;
case PORTALTYPE_PLANEMIRROR:
if (GLPortal::PlaneMirrorMode * wall->planemirror->fC() <= 0)
if (pstate.PlaneMirrorMode * wall->planemirror->fC() <= 0)
{
//@sync-portal
wall->planemirror = UniquePlaneMirrors.Get(wall->planemirror);
portal = GLPortal::FindPortal(wall->planemirror);
if (!portal) portal = new GLPlaneMirrorPortal(wall->planemirror);
wall->planemirror = pstate.UniquePlaneMirrors.Get(wall->planemirror);
portal = FindPortal(wall->planemirror);
if (!portal)
{
portal = new GLScenePortal(&pstate, new HWPlaneMirrorPortal(wall->planemirror));
Portals.Push(portal);
}
portal->AddLine(wall);
}
break;
case PORTALTYPE_MIRROR:
portal = GLPortal::FindPortal(wall->seg->linedef);
if (!portal) portal = new GLMirrorPortal(wall->seg->linedef);
portal = FindPortal(wall->seg->linedef);
if (!portal)
{
portal = new GLScenePortal(&pstate, new HWMirrorPortal(wall->seg->linedef));
Portals.Push(portal);
}
portal->AddLine(wall);
if (gl_mirror_envmap)
{
@ -392,23 +412,28 @@ void FDrawInfo::AddPortal(GLWall *wall, int ptype)
break;
case PORTALTYPE_LINETOLINE:
portal = GLPortal::FindPortal(wall->lineportal);
portal = FindPortal(wall->lineportal);
if (!portal)
{
line_t *otherside = wall->lineportal->lines[0]->mDestination;
if (otherside != NULL && otherside->portalindex < level.linePortals.Size())
if (otherside != nullptr && otherside->portalindex < level.linePortals.Size())
{
ProcessActorsInPortal(otherside->getPortal()->mGroup, in_area);
}
portal = new GLLineToLinePortal(wall->lineportal);
portal = new GLScenePortal(&pstate, new HWLineToLinePortal(wall->lineportal));
Portals.Push(portal);
}
portal->AddLine(wall);
break;
case PORTALTYPE_SKY:
wall->sky = UniqueSkies.Get(wall->sky);
portal = GLPortal::FindPortal(wall->sky);
if (!portal) portal = new GLSkyPortal(wall->sky);
wall->sky = pstate.UniqueSkies.Get(wall->sky);
portal = FindPortal(wall->sky);
if (!portal)
{
portal = new GLSkyPortal(&pstate, wall->sky);
Portals.Push(portal);
}
portal->AddLine(wall);
break;
}

View file

@ -37,9 +37,7 @@
#include "gl/renderer/gl_renderstate.h"
#include "gl/data/gl_vertexbuffer.h"
#include "gl/scene/gl_drawinfo.h"
#include "gl/scene/gl_scenedrawer.h"
#include "gl/models/gl_models.h"
#include "gl/stereo3d/gl_stereo3d.h"
#include "gl/dynlights/gl_lightbuffer.h"
//==========================================================================
@ -66,7 +64,8 @@ void FDrawInfo::DrawPSprite (HUDSprite *huds)
if (huds->mframe)
{
gl_RenderState.AlphaFunc(GL_GEQUAL, 0);
gl_RenderHUDModel(huds->weapon, huds->mx, huds->my, huds->lightindex);
FGLModelRenderer renderer(this, huds->lightindex);
renderer.RenderHUDModel(huds->weapon, huds->mx, huds->my);
}
else
{
@ -91,8 +90,6 @@ void FDrawInfo::DrawPSprite (HUDSprite *huds)
void FDrawInfo::DrawPlayerSprites(bool hudModelStep)
{
s3d::Stereo3DMode::getCurrentMode().AdjustPlayerSprites();
int oldlightmode = level.lightmode;
if (!hudModelStep && level.lightmode == 8) level.lightmode = 2; // Software lighting cannot handle 2D content so revert to lightmode 2 for that.
for(auto &hudsprite : hudsprites)

View file

@ -34,6 +34,7 @@
#include "cmdlib.h"
#include "hwrenderer/utility/hw_shaderpatcher.h"
#include "hwrenderer/data/shaderuniforms.h"
#include "hwrenderer/scene/hw_viewpointuniforms.h"
#include "gl_load/gl_interface.h"
#include "gl/system/gl_debug.h"
@ -307,10 +308,7 @@ bool FShader::Load(const char * name, const char * vert_prog_lump, const char *
muDesaturation.Init(hShader, "uDesaturationFactor");
muFogEnabled.Init(hShader, "uFogEnabled");
muPalLightLevels.Init(hShader, "uPalLightLevels");
muGlobVis.Init(hShader, "uGlobVis");
muTextureMode.Init(hShader, "uTextureMode");
muCameraPos.Init(hShader, "uCameraPos");
muLightParms.Init(hShader, "uLightAttr");
muClipSplit.Init(hShader, "uClipSplit");
muLightIndex.Init(hShader, "uLightIndex");
@ -324,13 +322,9 @@ bool FShader::Load(const char * name, const char * vert_prog_lump, const char *
muGlowTopPlane.Init(hShader, "uGlowTopPlane");
muSplitBottomPlane.Init(hShader, "uSplitBottomPlane");
muSplitTopPlane.Init(hShader, "uSplitTopPlane");
muClipLine.Init(hShader, "uClipLine");
muInterpolationFactor.Init(hShader, "uInterpolationFactor");
muClipHeight.Init(hShader, "uClipHeight");
muClipHeightDirection.Init(hShader, "uClipHeightDirection");
muAlphaThreshold.Init(hShader, "uAlphaThreshold");
muSpecularMaterial.Init(hShader, "uSpecularMaterial");
muViewHeight.Init(hShader, "uViewHeight");
muTimer.Init(hShader, "timer");
lights_index = glGetUniformLocation(hShader, "lights");
@ -344,6 +338,13 @@ bool FShader::Load(const char * name, const char * vert_prog_lump, const char *
normalviewmatrix_index = glGetUniformLocation(hShader, "NormalViewMatrix");
normalmodelmatrix_index = glGetUniformLocation(hShader, "NormalModelMatrix");
quadmode_index = glGetUniformLocation(hShader, "uQuadMode");
viewheight_index = glGetUniformLocation(hShader, "uViewHeight");
camerapos_index = glGetUniformLocation(hShader, "uCameraPos");
pallightlevels_index = glGetUniformLocation(hShader, "uPalLightLevels");
globvis_index = glGetUniformLocation(hShader, "uGlobVis");
clipheight_index = glGetUniformLocation(hShader, "uClipHeight");
clipheightdirection_index = glGetUniformLocation(hShader, "uClipHeightDirection");
clipline_index = glGetUniformLocation(hShader, "uClipLine");
if (!(gl.flags & RFL_SHADER_STORAGE_BUFFER))
{
@ -434,12 +435,19 @@ FShader *FShaderCollection::Compile (const char *ShaderName, const char *ShaderP
//
//==========================================================================
void FShader::ApplyMatrices(VSMatrix *proj, VSMatrix *view, VSMatrix *norm)
void FShader::ApplyMatrices(HWViewpointUniforms *u)
{
Bind();
glUniformMatrix4fv(projectionmatrix_index, 1, false, proj->get());
glUniformMatrix4fv(viewmatrix_index, 1, false, view->get());
glUniformMatrix4fv(normalviewmatrix_index, 1, false, norm->get());
glUniformMatrix4fv(projectionmatrix_index, 1, false, u->mProjectionMatrix.get());
glUniformMatrix4fv(viewmatrix_index, 1, false, u->mViewMatrix.get());
glUniformMatrix4fv(normalviewmatrix_index, 1, false, u->mNormalViewMatrix.get());
glUniform4fv(camerapos_index, 1, &u->mCameraPos[0]);
glUniform1i(viewheight_index, u->mViewHeight);
glUniform1i(pallightlevels_index, u->mPalLightLevels);
glUniform1f(globvis_index, u->mGlobVis);
glUniform1f(clipheight_index, u->mClipHeight);
glUniform1f(clipheightdirection_index, u->mClipHeightDirection);
}
//==========================================================================
@ -539,10 +547,10 @@ FShader *FShaderManager::Get(unsigned int eff, bool alphateston, EPassType passT
return nullptr;
}
void FShaderManager::ApplyMatrices(VSMatrix *proj, VSMatrix *view, EPassType passType)
void FShaderManager::ApplyMatrices(HWViewpointUniforms *u, EPassType passType)
{
if (passType < mPassShaders.Size())
mPassShaders[passType]->ApplyMatrices(proj, view);
mPassShaders[passType]->ApplyMatrices(u);
if (mActiveShader)
mActiveShader->Bind();
@ -687,28 +695,25 @@ FShader *FShaderCollection::BindEffect(int effect)
//==========================================================================
EXTERN_CVAR(Int, gl_fuzztype)
void FShaderCollection::ApplyMatrices(VSMatrix *proj, VSMatrix *view)
void FShaderCollection::ApplyMatrices(HWViewpointUniforms *u)
{
VSMatrix norm;
norm.computeNormalMatrix(*view);
for (int i = 0; i < SHADER_NoTexture; i++)
{
mMaterialShaders[i]->ApplyMatrices(proj, view, &norm);
mMaterialShadersNAT[i]->ApplyMatrices(proj, view, &norm);
mMaterialShaders[i]->ApplyMatrices(u);
mMaterialShadersNAT[i]->ApplyMatrices(u);
}
mMaterialShaders[SHADER_NoTexture]->ApplyMatrices(proj, view, &norm);
mMaterialShaders[SHADER_NoTexture]->ApplyMatrices(u);
if (gl_fuzztype != 0)
{
mMaterialShaders[SHADER_NoTexture + gl_fuzztype]->ApplyMatrices(proj, view, &norm);
mMaterialShaders[SHADER_NoTexture + gl_fuzztype]->ApplyMatrices(u);
}
for (unsigned i = FIRST_USER_SHADER; i < mMaterialShaders.Size(); i++)
{
mMaterialShaders[i]->ApplyMatrices(proj, view, &norm);
mMaterialShaders[i]->ApplyMatrices(u);
}
for (int i = 0; i < MAX_EFFECTS; i++)
{
mEffectShaders[i]->ApplyMatrices(proj, view, &norm);
mEffectShaders[i]->ApplyMatrices(u);
}
}

View file

@ -38,6 +38,7 @@ enum
};
class FShaderCollection;
struct HWViewpointUniforms;
//==========================================================================
//
@ -242,10 +243,7 @@ class FShader
FBufferedUniform1f muDesaturation;
FBufferedUniform1i muFogEnabled;
FBufferedUniform1i muPalLightLevels;
FBufferedUniform1f muGlobVis;
FBufferedUniform1i muTextureMode;
FBufferedUniform4f muCameraPos;
FBufferedUniform4f muLightParms;
FBufferedUniform2f muClipSplit;
FBufferedUniform1i muLightIndex;
@ -259,22 +257,27 @@ class FShader
FUniform4f muGlowTopPlane;
FUniform4f muSplitBottomPlane;
FUniform4f muSplitTopPlane;
FUniform4f muClipLine;
FBufferedUniform1f muInterpolationFactor;
FBufferedUniform1f muClipHeight;
FBufferedUniform1f muClipHeightDirection;
FBufferedUniform1f muAlphaThreshold;
FBufferedUniform1i muViewHeight;
FBufferedUniform2f muSpecularMaterial;
FBufferedUniform1f muTimer;
int lights_index;
int projectionmatrix_index;
int viewmatrix_index;
int normalviewmatrix_index;
int modelmatrix_index;
int normalmodelmatrix_index;
int texturematrix_index;
int projectionmatrix_index;
int viewmatrix_index;
int normalviewmatrix_index;
int viewheight_index;
int camerapos_index;
int pallightlevels_index;
int globvis_index;
int clipheight_index;
int clipheightdirection_index;
int clipline_index;
public:
int vertexmatrix_index;
int texcoordmatrix_index;
@ -306,7 +309,7 @@ public:
bool Bind();
unsigned int GetHandle() const { return hShader; }
void ApplyMatrices(VSMatrix *proj, VSMatrix *view, VSMatrix *norm);
void ApplyMatrices(HWViewpointUniforms *u);
};
@ -326,7 +329,7 @@ public:
FShader *BindEffect(int effect, EPassType passType);
FShader *Get(unsigned int eff, bool alphateston, EPassType passType);
void ApplyMatrices(VSMatrix *proj, VSMatrix *view, EPassType passType);
void ApplyMatrices(HWViewpointUniforms *u, EPassType passType);
private:
FShader *mActiveShader = nullptr;
@ -348,7 +351,7 @@ public:
FShader *Compile(const char *ShaderName, const char *ShaderPath, const char *LightModePath, const char *shaderdefines, bool usediscard, EPassType passType);
int Find(const char *mame);
FShader *BindEffect(int effect);
void ApplyMatrices(VSMatrix *proj, VSMatrix *view);
void ApplyMatrices(HWViewpointUniforms *u);
FShader *Get(unsigned int eff, bool alphateston)
{

View file

@ -1,84 +0,0 @@
//
//---------------------------------------------------------------------------
//
// Copyright(C) 2015 Christopher Bruns
// All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see http://www.gnu.org/licenses/
//
//--------------------------------------------------------------------------
//
/*
** gl_anaglyph.cpp
** Color mask based stereoscopic 3D modes for GZDoom
**
*/
#include "gl_anaglyph.h"
#include "gl/renderer/gl_renderbuffers.h"
namespace s3d {
MaskAnaglyph::MaskAnaglyph(const ColorMask& leftColorMask, double ipdMeters)
: leftEye(leftColorMask, ipdMeters), rightEye(leftColorMask.inverse(), ipdMeters)
{
eye_ptrs.Push(&leftEye);
eye_ptrs.Push(&rightEye);
}
void MaskAnaglyph::Present() const
{
GLRenderer->mBuffers->BindOutputFB();
GLRenderer->ClearBorders();
gl_RenderState.SetColorMask(leftEye.GetColorMask().r, leftEye.GetColorMask().g, leftEye.GetColorMask().b, true);
gl_RenderState.ApplyColorMask();
GLRenderer->mBuffers->BindEyeTexture(0, 0);
GLRenderer->DrawPresentTexture(screen->mOutputLetterbox, true);
gl_RenderState.SetColorMask(rightEye.GetColorMask().r, rightEye.GetColorMask().g, rightEye.GetColorMask().b, true);
gl_RenderState.ApplyColorMask();
GLRenderer->mBuffers->BindEyeTexture(1, 0);
GLRenderer->DrawPresentTexture(screen->mOutputLetterbox, true);
gl_RenderState.ResetColorMask();
gl_RenderState.ApplyColorMask();
}
/* static */
const GreenMagenta& GreenMagenta::getInstance(float ipd)
{
static GreenMagenta instance(ipd);
return instance;
}
/* static */
const RedCyan& RedCyan::getInstance(float ipd)
{
static RedCyan instance(ipd);
return instance;
}
/* static */
const AmberBlue& AmberBlue::getInstance(float ipd)
{
static AmberBlue instance(ipd);
return instance;
}
} /* namespace s3d */

View file

@ -1,113 +0,0 @@
//
//---------------------------------------------------------------------------
//
// Copyright(C) 2015 Christopher Bruns
// All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see http://www.gnu.org/licenses/
//
//--------------------------------------------------------------------------
//
/*
** gl_anaglyph.h
** Color mask based stereoscopic 3D modes for GZDoom
**
*/
#ifndef GL_ANAGLYPH_H_
#define GL_ANAGLYPH_H_
#include "gl_stereo3d.h"
#include "gl_stereo_leftright.h"
#include "gl_load/gl_system.h"
#include "gl/renderer/gl_renderstate.h"
namespace s3d {
class ColorMask
{
public:
ColorMask(bool r, bool g, bool b) : r(r), g(g), b(b) {}
ColorMask inverse() const { return ColorMask(!r, !g, !b); }
bool r;
bool g;
bool b;
};
class AnaglyphLeftPose : public LeftEyePose
{
public:
AnaglyphLeftPose(const ColorMask& colorMask, float ipd) : LeftEyePose(ipd), colorMask(colorMask) {}
ColorMask GetColorMask() const { return colorMask; }
private:
ColorMask colorMask;
};
class AnaglyphRightPose : public RightEyePose
{
public:
AnaglyphRightPose(const ColorMask& colorMask, float ipd) : RightEyePose(ipd), colorMask(colorMask) {}
ColorMask GetColorMask() const { return colorMask; }
private:
ColorMask colorMask;
};
class MaskAnaglyph : public Stereo3DMode
{
public:
MaskAnaglyph(const ColorMask& leftColorMask, double ipdMeters);
void Present() const override;
private:
AnaglyphLeftPose leftEye;
AnaglyphRightPose rightEye;
};
class RedCyan : public MaskAnaglyph
{
public:
static const RedCyan& getInstance(float ipd);
RedCyan(float ipd) : MaskAnaglyph(ColorMask(true, false, false), ipd) {}
};
class GreenMagenta : public MaskAnaglyph
{
public:
static const GreenMagenta& getInstance(float ipd);
GreenMagenta(float ipd) : MaskAnaglyph(ColorMask(false, true, false), ipd) {}
};
class AmberBlue : public MaskAnaglyph
{
public:
static const AmberBlue& getInstance(float ipd);
AmberBlue(float ipd) : MaskAnaglyph(ColorMask(true, true, false), ipd) {}
};
// TODO matrix anaglyph
} /* namespace s3d */
#endif /* GL_ANAGLYPH_H_ */

View file

@ -1,232 +0,0 @@
/*
** gl_interleaved3d.cpp
** Interleaved image stereoscopic 3D modes for GZDoom
**
**---------------------------------------------------------------------------
** Copyright 2016 Christopher Bruns
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. 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.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
**---------------------------------------------------------------------------
**
**
*/
#include "gl_interleaved3d.h"
#include "gl/renderer/gl_renderbuffers.h"
#include "gl/renderer/gl_postprocessstate.h"
#include "gl/system/gl_framebuffer.h"
#include "hwrenderer/postprocessing/hw_present3dRowshader.h"
#ifdef _WIN32
#include "hardware.h"
#endif // _WIN32
EXTERN_CVAR(Float, vid_saturation)
EXTERN_CVAR(Float, vid_brightness)
EXTERN_CVAR(Float, vid_contrast)
EXTERN_CVAR(Int, gl_satformula)
EXTERN_CVAR(Bool, fullscreen)
EXTERN_CVAR(Int, win_x) // screen pixel position of left of display window
EXTERN_CVAR(Int, win_y) // screen pixel position of top of display window
namespace s3d {
/* static */
const CheckerInterleaved3D& CheckerInterleaved3D::getInstance(float ipd)
{
static CheckerInterleaved3D instance(ipd);
return instance;
}
/* static */
const ColumnInterleaved3D& ColumnInterleaved3D::getInstance(float ipd)
{
static ColumnInterleaved3D instance(ipd);
return instance;
}
/* static */
const RowInterleaved3D& RowInterleaved3D::getInstance(float ipd)
{
static RowInterleaved3D instance(ipd);
return instance;
}
static void prepareInterleavedPresent(FPresentShaderBase& shader)
{
GLRenderer->mBuffers->BindOutputFB();
GLRenderer->ClearBorders();
// Bind each eye texture, for composition in the shader
GLRenderer->mBuffers->BindEyeTexture(0, 0);
GLRenderer->mBuffers->BindEyeTexture(1, 1);
glActiveTexture(GL_TEXTURE0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glActiveTexture(GL_TEXTURE1);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
const IntRect& box = screen->mOutputLetterbox;
glViewport(box.left, box.top, box.width, box.height);
shader.Bind(NOQUEUE);
if ( GLRenderer->framebuffer->IsHWGammaActive() )
{
shader.Uniforms->InvGamma = 1.0f;
shader.Uniforms->Contrast = 1.0f;
shader.Uniforms->Brightness = 0.0f;
shader.Uniforms->Saturation = 1.0f;
}
else
{
shader.Uniforms->InvGamma = 1.0f / clamp<float>(Gamma, 0.1f, 4.f);
shader.Uniforms->Contrast = clamp<float>(vid_contrast, 0.1f, 3.f);
shader.Uniforms->Brightness = clamp<float>(vid_brightness, -0.8f, 0.8f);
shader.Uniforms->Saturation = clamp<float>(vid_saturation, -15.0f, 15.0f);
shader.Uniforms->GrayFormula = static_cast<int>(gl_satformula);
}
shader.Uniforms->Scale = {
screen->mScreenViewport.width / (float)GLRenderer->mBuffers->GetWidth(),
screen->mScreenViewport.height / (float)GLRenderer->mBuffers->GetHeight()
};
shader.Uniforms.Set();
}
// fixme: I don't know how to get absolute window position on Mac and Linux
// fixme: I don't know how to get window border decoration size anywhere
// So for now I'll hard code the border effect on my test machine.
// Workaround for others is to fuss with vr_swap_eyes CVAR until it looks right.
// Presumably the top/left window border on my test machine has an odd number of pixels
// in the horizontal direction, and an even number in the vertical direction.
#define WINDOW_BORDER_HORIZONTAL_PARITY 1
#define WINDOW_BORDER_VERTICAL_PARITY 0
void CheckerInterleaved3D::Present() const
{
FGLPostProcessState savedState;
savedState.SaveTextureBindings(2);
prepareInterleavedPresent(*GLRenderer->mPresent3dCheckerShader);
// Compute absolute offset from top of screen to top of current display window
// because we need screen-relative, not window-relative, scan line parity
int windowVOffset = 0;
int windowHOffset = 0;
#ifdef _WIN32
/* this needs to be done differently!
if (!fullscreen) {
I_SaveWindowedPos(); // update win_y CVAR
windowHOffset = (win_x + WINDOW_BORDER_HORIZONTAL_PARITY) % 2;
windowVOffset = (win_y + WINDOW_BORDER_VERTICAL_PARITY) % 2;
}
*/
#endif // _WIN32
GLRenderer->mPresent3dCheckerShader->Uniforms->WindowPositionParity =
(windowVOffset
+ windowHOffset
+ screen->mOutputLetterbox.height + 1 // +1 because of origin at bottom
) % 2; // because we want the top pixel offset, but gl_FragCoord.y is the bottom pixel offset
GLRenderer->mPresent3dCheckerShader->Uniforms.Set();
GLRenderer->RenderScreenQuad();
}
void s3d::CheckerInterleaved3D::AdjustViewports() const
{
// decrease the total pixel count by 2, but keep the same aspect ratio
const float sqrt2 = 1.41421356237f;
// Change size of renderbuffer, and align to screen
screen->mSceneViewport.height /= sqrt2;
screen->mSceneViewport.top /= sqrt2;
screen->mSceneViewport.width /= sqrt2;
screen->mSceneViewport.left /= sqrt2;
screen->mScreenViewport.height /= sqrt2;
screen->mScreenViewport.top /= sqrt2;
screen->mScreenViewport.width /= sqrt2;
screen->mScreenViewport.left /= sqrt2;
}
void ColumnInterleaved3D::Present() const
{
FGLPostProcessState savedState;
savedState.SaveTextureBindings(2);
prepareInterleavedPresent(*GLRenderer->mPresent3dColumnShader);
// Compute absolute offset from top of screen to top of current display window
// because we need screen-relative, not window-relative, scan line parity
int windowHOffset = 0;
#ifdef _WIN32
/* this needs to be done differently!
if (!fullscreen) {
I_SaveWindowedPos(); // update win_y CVAR
windowHOffset = (win_x + WINDOW_BORDER_HORIZONTAL_PARITY) % 2;
}
*/
#endif // _WIN32
GLRenderer->mPresent3dColumnShader->Uniforms->WindowPositionParity = windowHOffset;
GLRenderer->mPresent3dColumnShader->Uniforms.Set();
GLRenderer->RenderScreenQuad();
}
void RowInterleaved3D::Present() const
{
FGLPostProcessState savedState;
savedState.SaveTextureBindings(2);
prepareInterleavedPresent(*GLRenderer->mPresent3dRowShader);
// Compute absolute offset from top of screen to top of current display window
// because we need screen-relative, not window-relative, scan line parity
int windowVOffset = 0;
#ifdef _WIN32
/* this needs to be done differently!
if (! fullscreen) {
I_SaveWindowedPos(); // update win_y CVAR
windowVOffset = (win_y + WINDOW_BORDER_VERTICAL_PARITY) % 2;
}
*/
#endif // _WIN32
GLRenderer->mPresent3dRowShader->Uniforms->WindowPositionParity =
(windowVOffset
+ screen->mOutputLetterbox.height + 1 // +1 because of origin at bottom
) % 2;
GLRenderer->mPresent3dRowShader->Uniforms.Set();
GLRenderer->RenderScreenQuad();
}
} /* namespace s3d */

View file

@ -1,75 +0,0 @@
/*
** gl_interleaved3d.h
** Interleaved stereoscopic 3D modes for GZDoom
**
**---------------------------------------------------------------------------
** Copyright 2016 Christopher Bruns
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. 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.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
**---------------------------------------------------------------------------
**
**
*/
#ifndef GL_INTERLEAVED3D_H_
#define GL_INTERLEAVED3D_H_
#include "gl_stereo3d.h"
#include "gl_stereo_leftright.h"
#include "gl_sidebyside3d.h"
#include "gl_load/gl_system.h"
#include "gl/renderer/gl_renderstate.h"
namespace s3d {
class CheckerInterleaved3D : public SideBySideSquished
{
public:
static const CheckerInterleaved3D& getInstance(float ipd);
CheckerInterleaved3D(double ipdMeters) : SideBySideSquished(ipdMeters) {}
void Present() const override;
void AdjustViewports() const override;
};
class ColumnInterleaved3D : public SideBySideSquished
{
public:
static const ColumnInterleaved3D& getInstance(float ipd);
ColumnInterleaved3D(double ipdMeters) : SideBySideSquished(ipdMeters) {}
void Present() const override;
};
class RowInterleaved3D : public TopBottom3D
{
public:
static const RowInterleaved3D& getInstance(float ipd);
RowInterleaved3D(double ipdMeters) : TopBottom3D(ipdMeters) {}
void Present() const override;
};
} /* namespace s3d */
#endif /* GL_INTERLEAVED3D_H_ */

View file

@ -1,115 +0,0 @@
//
//---------------------------------------------------------------------------
//
// Copyright(C) 2015 Christopher Bruns
// All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see http://www.gnu.org/licenses/
//
//--------------------------------------------------------------------------
//
/*
** gl_quadstereo.cpp
** Quad-buffered OpenGL stereoscopic 3D mode for GZDoom
**
*/
#include "gl_quadstereo.h"
#include "gl/renderer/gl_renderbuffers.h"
namespace s3d {
QuadStereo::QuadStereo(double ipdMeters)
: leftEye(ipdMeters), rightEye(ipdMeters)
{
// Check whether quad-buffered stereo is supported in the current context
// We are assuming the OpenGL context is already current at this point,
// i.e. this constructor is called "just in time".
// First initialize to mono-ish initial state
bQuadStereoSupported = leftEye.bQuadStereoSupported = rightEye.bQuadStereoSupported = false;
eye_ptrs.Push(&leftEye); // We ALWAYS want to show at least this one view...
// We will possibly advance to true stereo mode in the Setup() method...
}
// Sometimes the stereo render context is not ready immediately at start up
/* private */
void QuadStereo::checkInitialRenderContextState()
{
// Keep trying until we see at least one good OpenGL context to render to
static bool bDecentContextWasFound = false;
static int contextCheckCount = 0;
if ( (! bDecentContextWasFound) && (contextCheckCount < 200) )
{
contextCheckCount += 1;
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); // This question is about the main screen display context
GLboolean supportsStereo, supportsBuffered;
glGetBooleanv(GL_DOUBLEBUFFER, &supportsBuffered);
if (supportsBuffered) // Finally, a useful OpenGL context
{
// This block will be executed exactly ONCE during a game run
bDecentContextWasFound = true; // now we can stop checking every frame...
// Now check whether this context supports hardware stereo
glGetBooleanv(GL_STEREO, &supportsStereo);
bQuadStereoSupported = supportsStereo && supportsBuffered;
leftEye.bQuadStereoSupported = bQuadStereoSupported;
rightEye.bQuadStereoSupported = bQuadStereoSupported;
if (bQuadStereoSupported)
eye_ptrs.Push(&rightEye); // Use the other eye too, if we can do stereo
}
}
}
void QuadStereo::Present() const
{
if (bQuadStereoSupported)
{
GLRenderer->mBuffers->BindOutputFB();
glDrawBuffer(GL_BACK_LEFT);
GLRenderer->ClearBorders();
GLRenderer->mBuffers->BindEyeTexture(0, 0);
GLRenderer->DrawPresentTexture(screen->mOutputLetterbox, true);
glDrawBuffer(GL_BACK_RIGHT);
GLRenderer->ClearBorders();
GLRenderer->mBuffers->BindEyeTexture(1, 0);
GLRenderer->DrawPresentTexture(screen->mOutputLetterbox, true);
glDrawBuffer(GL_BACK);
}
else
{
GLRenderer->mBuffers->BindOutputFB();
GLRenderer->ClearBorders();
GLRenderer->mBuffers->BindEyeTexture(0, 0);
GLRenderer->DrawPresentTexture(screen->mOutputLetterbox, true);
}
}
void QuadStereo::SetUp() const
{
Stereo3DMode::SetUp();
// Maybe advance to true stereo mode (ONCE), after the stereo context is finally ready
const_cast<QuadStereo*>(this)->checkInitialRenderContextState();
}
/* static */
const QuadStereo& QuadStereo::getInstance(float ipd)
{
static QuadStereo instance(ipd);
return instance;
}
} /* namespace s3d */

View file

@ -1,77 +0,0 @@
//
//---------------------------------------------------------------------------
//
// Copyright(C) 2015 Christopher Bruns
// All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see http://www.gnu.org/licenses/
//
//--------------------------------------------------------------------------
//
/*
** gl_quadstereo.h
** Quad-buffered OpenGL stereoscopic 3D mode for GZDoom
**
*/
#ifndef GL_QUADSTEREO_H_
#define GL_QUADSTEREO_H_
#include "gl_stereo3d.h"
#include "gl_stereo_leftright.h"
#include "gl_load/gl_system.h"
namespace s3d {
class QuadStereoLeftPose : public LeftEyePose
{
public:
QuadStereoLeftPose(float ipd) : LeftEyePose(ipd), bQuadStereoSupported(false) {}
bool bQuadStereoSupported;
};
class QuadStereoRightPose : public RightEyePose
{
public:
QuadStereoRightPose(float ipd) : RightEyePose(ipd), bQuadStereoSupported(false){}
bool bQuadStereoSupported;
};
// To use Quad-buffered stereo mode with nvidia 3d vision glasses,
// you must either:
// A) be using a Quadro series video card, OR
//
// B) be using nvidia driver version 314.07 or later
// AND have your monitor set to 120 Hz refresh rate
// AND have gzdoom in true full screen mode
class QuadStereo : public Stereo3DMode
{
public:
QuadStereo(double ipdMeters);
void Present() const override;
void SetUp() const override;
static const QuadStereo& getInstance(float ipd);
private:
QuadStereoLeftPose leftEye;
QuadStereoRightPose rightEye;
bool bQuadStereoSupported;
void checkInitialRenderContextState();
};
} /* namespace s3d */
#endif /* GL_QUADSTEREO_H_ */

View file

@ -1,149 +0,0 @@
/*
** gl_sidebyside3d.cpp
** Mosaic image stereoscopic 3D modes for GZDoom
**
**---------------------------------------------------------------------------
** Copyright 2016 Christopher Bruns
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. 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.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
**---------------------------------------------------------------------------
**
**
*/
#include "gl_sidebyside3d.h"
#include "gl/renderer/gl_renderbuffers.h"
namespace s3d {
void SideBySideBase::Present() const
{
GLRenderer->mBuffers->BindOutputFB();
GLRenderer->ClearBorders();
// Compute screen regions to use for left and right eye views
int leftWidth = screen->mOutputLetterbox.width / 2;
int rightWidth = screen->mOutputLetterbox.width - leftWidth;
IntRect leftHalfScreen = screen->mOutputLetterbox;
leftHalfScreen.width = leftWidth;
IntRect rightHalfScreen = screen->mOutputLetterbox;
rightHalfScreen.width = rightWidth;
rightHalfScreen.left += leftWidth;
GLRenderer->mBuffers->BindEyeTexture(0, 0);
GLRenderer->DrawPresentTexture(leftHalfScreen, true);
GLRenderer->mBuffers->BindEyeTexture(1, 0);
GLRenderer->DrawPresentTexture(rightHalfScreen, true);
}
// AdjustViewports() is called from within FLGRenderer::SetOutputViewport(...)
void SideBySideBase::AdjustViewports() const
{
// Change size of renderbuffer, and align to screen
screen->mSceneViewport.width /= 2;
screen->mSceneViewport.left /= 2;
screen->mScreenViewport.width /= 2;
screen->mScreenViewport.left /= 2;
}
/* static */
const SideBySideSquished& SideBySideSquished::getInstance(float ipd)
{
static SideBySideSquished instance(ipd);
return instance;
}
SideBySideSquished::SideBySideSquished(double ipdMeters)
: leftEye(ipdMeters), rightEye(ipdMeters)
{
eye_ptrs.Push(&leftEye);
eye_ptrs.Push(&rightEye);
}
/* static */
const SideBySideFull& SideBySideFull::getInstance(float ipd)
{
static SideBySideFull instance(ipd);
return instance;
}
SideBySideFull::SideBySideFull(double ipdMeters)
: leftEye(ipdMeters), rightEye(ipdMeters)
{
eye_ptrs.Push(&leftEye);
eye_ptrs.Push(&rightEye);
}
/* virtual */
void SideBySideFull::AdjustPlayerSprites() const /* override */
{
// Show weapon at double width, so it would appear normal width after rescaling
int w = screen->mScreenViewport.width;
int h = screen->mScreenViewport.height;
gl_RenderState.mProjectionMatrix.ortho(w/2, w + w/2, h, 0, -1.0f, 1.0f);
gl_RenderState.ApplyMatrices();
}
/* static */
const TopBottom3D& TopBottom3D::getInstance(float ipd)
{
static TopBottom3D instance(ipd);
return instance;
}
void TopBottom3D::Present() const
{
GLRenderer->mBuffers->BindOutputFB();
GLRenderer->ClearBorders();
// Compute screen regions to use for left and right eye views
int topHeight = screen->mOutputLetterbox.height / 2;
int bottomHeight = screen->mOutputLetterbox.height - topHeight;
IntRect topHalfScreen = screen->mOutputLetterbox;
topHalfScreen.height = topHeight;
topHalfScreen.top = topHeight;
IntRect bottomHalfScreen = screen->mOutputLetterbox;
bottomHalfScreen.height = bottomHeight;
bottomHalfScreen.top = 0;
GLRenderer->mBuffers->BindEyeTexture(0, 0);
GLRenderer->DrawPresentTexture(topHalfScreen, true);
GLRenderer->mBuffers->BindEyeTexture(1, 0);
GLRenderer->DrawPresentTexture(bottomHalfScreen, true);
}
// AdjustViewports() is called from within FLGRenderer::SetOutputViewport(...)
void TopBottom3D::AdjustViewports() const
{
// Change size of renderbuffer, and align to screen
screen->mSceneViewport.height /= 2;
screen->mSceneViewport.top /= 2;
screen->mScreenViewport.height /= 2;
screen->mScreenViewport.top /= 2;
}
} /* namespace s3d */

View file

@ -1,103 +0,0 @@
/*
** gl_sidebyside3d.h
** Mosaic image stereoscopic 3D modes for GZDoom
**
**---------------------------------------------------------------------------
** Copyright 2016 Christopher Bruns
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. 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.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
**---------------------------------------------------------------------------
**
**
*/
#ifndef GL_SIDEBYSIDE3D_H_
#define GL_SIDEBYSIDE3D_H_
#include "gl_stereo3d.h"
#include "gl_stereo_leftright.h"
#include "gl_load/gl_system.h"
#include "gl/renderer/gl_renderstate.h"
namespace s3d {
class SideBySideBase : public Stereo3DMode
{
public:
void Present() const override;
virtual void AdjustViewports() const override;
};
class SideBySideSquished : public SideBySideBase
{
public:
static const SideBySideSquished& getInstance(float ipd);
SideBySideSquished(double ipdMeters);
private:
LeftEyePose leftEye;
RightEyePose rightEye;
};
class SBSFLeftEyePose : public LeftEyePose {
public:
SBSFLeftEyePose(double ipdMeters) : LeftEyePose(ipdMeters) {}
virtual VSMatrix GetProjection(float fov, float aspectRatio, float fovRatio) const override {
return LeftEyePose::GetProjection(fov, 0.5f * aspectRatio, fovRatio);
}
};
class SBSFRightEyePose : public RightEyePose {
public:
SBSFRightEyePose(double ipdMeters) : RightEyePose(ipdMeters) {}
virtual VSMatrix GetProjection(float fov, float aspectRatio, float fovRatio) const override {
return RightEyePose::GetProjection(fov, 0.5f * aspectRatio, fovRatio);
}
};
class SideBySideFull : public SideBySideBase
{
public:
static const SideBySideFull& getInstance(float ipd);
SideBySideFull(double ipdMeters);
virtual void AdjustPlayerSprites() const override;
private:
SBSFLeftEyePose leftEye;
SBSFRightEyePose rightEye;
};
class TopBottom3D : public SideBySideSquished
{
public:
static const TopBottom3D& getInstance(float ipd);
TopBottom3D(double ipdMeters) : SideBySideSquished(ipdMeters) {}
void Present() const override;
virtual void AdjustViewports() const override;
};
} /* namespace s3d */
#endif /* GL_SIDEBYSIDE3D_H_ */

View file

@ -1,82 +0,0 @@
//
//---------------------------------------------------------------------------
//
// Copyright(C) 2015 Christopher Bruns
// All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see http://www.gnu.org/licenses/
//
//--------------------------------------------------------------------------
//
/*
** gl_stereo3d.cpp
** Stereoscopic 3D API
**
*/
#include "gl_load/gl_system.h"
#include "gl/stereo3d/gl_stereo3d.h"
namespace s3d {
/* virtual */
VSMatrix EyePose::GetProjection(float fov, float aspectRatio, float fovRatio) const
{
VSMatrix result;
// Lifted from gl_scene.cpp FGLRenderer::SetProjection()
float fovy = (float)(2 * RAD2DEG(atan(tan(DEG2RAD(fov) / 2) / fovRatio)));
result.perspective(fovy, aspectRatio, FGLRenderer::GetZNear(), FGLRenderer::GetZFar());
return result;
}
/* virtual */
Viewport EyePose::GetViewport(const Viewport& fullViewport) const
{
return fullViewport;
}
/* virtual */
void EyePose::GetViewShift(float yaw, float outViewShift[3]) const
{
// pass-through for Mono view
outViewShift[0] = 0;
outViewShift[1] = 0;
outViewShift[2] = 0;
}
Stereo3DMode::Stereo3DMode()
{
}
Stereo3DMode::~Stereo3DMode()
{
}
// Avoid static initialization order fiasco by declaring first Mode type (Mono) here in the
// same source file as Stereo3DMode::getCurrentMode()
// https://isocpp.org/wiki/faq/ctors#static-init-order
/* static */
const MonoView& MonoView::getInstance()
{
static MonoView instance;
return instance;
}
} /* namespace s3d */

View file

@ -1,115 +0,0 @@
//
//---------------------------------------------------------------------------
//
// Copyright(C) 2015 Christopher Bruns
// All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see http://www.gnu.org/licenses/
//
//--------------------------------------------------------------------------
//
/*
** gl_stereo3d.h
** Stereoscopic 3D API
**
*/
#ifndef GL_STEREO3D_H_
#define GL_STEREO3D_H_
#include <cstring> // needed for memcpy on linux, which is needed by VSMatrix copy ctor
#include "tarray.h"
#include "r_data/matrix.h"
#include "gl/renderer/gl_renderer.h"
/* stereoscopic 3D API */
namespace s3d {
/* Subregion of current display window */
class Viewport
{
public:
int x, y;
int width, height;
};
/* Viewpoint of one eye */
class EyePose
{
public:
EyePose() {}
virtual ~EyePose() {}
virtual VSMatrix GetProjection(float fov, float aspectRatio, float fovRatio) const;
virtual Viewport GetViewport(const Viewport& fullViewport) const;
virtual void GetViewShift(float yaw, float outViewShift[3]) const;
virtual void SetUp() const {};
virtual void TearDown() const {};
};
/* Base class for stereoscopic 3D rendering modes */
class Stereo3DMode
{
public:
/* static methods for managing the selected stereoscopic view state */
static const Stereo3DMode& getCurrentMode();
static const Stereo3DMode& getMonoMode();
Stereo3DMode();
virtual ~Stereo3DMode();
virtual int eye_count() const { return eye_ptrs.Size(); }
virtual const EyePose * getEyePose(int ix) const { return eye_ptrs(ix); }
/* hooks for setup and cleanup operations for each stereo mode */
virtual void SetUp() const {};
virtual void TearDown() const {};
virtual bool IsMono() const { return false; }
virtual void AdjustViewports() const {};
virtual void AdjustPlayerSprites() const {};
virtual void Present() const = 0;
protected:
TArray<const EyePose *> eye_ptrs;
private:
static Stereo3DMode const * currentStereo3DMode;
static void setCurrentMode(const Stereo3DMode& mode);
};
/**
* Ordinary non-3D rendering
*/
class MonoView : public Stereo3DMode
{
public:
static const MonoView& getInstance();
bool IsMono() const override { return true; }
void Present() const override { }
protected:
MonoView() { eye_ptrs.Push(&centralEye); }
EyePose centralEye;
};
} /* namespace st3d */
#endif /* GL_STEREO3D_H_ */

View file

@ -1,127 +0,0 @@
//
//---------------------------------------------------------------------------
//
// Copyright(C) 2015 Christopher Bruns
// All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see http://www.gnu.org/licenses/
//
//--------------------------------------------------------------------------
//
/*
** gl_stereo_cvars.cpp
** Console variables related to stereoscopic 3D in GZDoom
**
*/
#include "gl/stereo3d/gl_stereo3d.h"
#include "gl/stereo3d/gl_stereo_leftright.h"
#include "gl/stereo3d/gl_anaglyph.h"
#include "gl/stereo3d/gl_quadstereo.h"
#include "gl/stereo3d/gl_sidebyside3d.h"
#include "gl/stereo3d/gl_interleaved3d.h"
#include "version.h"
// Set up 3D-specific console variables:
CVAR(Int, vr_mode, 0, CVAR_GLOBALCONFIG)
// switch left and right eye views
CVAR(Bool, vr_swap_eyes, false, CVAR_GLOBALCONFIG)
// intraocular distance in meters
CVAR(Float, vr_ipd, 0.062f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) // METERS
// distance between viewer and the display screen
CVAR(Float, vr_screendist, 0.80f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) // METERS
// default conversion between (vertical) DOOM units and meters
CVAR(Float, vr_hunits_per_meter, 41.0f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) // METERS
// Manage changing of 3D modes:
namespace s3d {
// Initialize static member
Stereo3DMode const * Stereo3DMode::currentStereo3DMode = 0; // "nullptr" not resolved on linux (presumably not C++11)
/* static */
void Stereo3DMode::setCurrentMode(const Stereo3DMode& mode) {
Stereo3DMode::currentStereo3DMode = &mode;
}
/* static */
const Stereo3DMode& Stereo3DMode::getCurrentMode()
{
// NOTE: Ensure that these vr_mode values correspond to the ones in wadsrc/static/menudef.z
switch (vr_mode)
{
case 1:
setCurrentMode(GreenMagenta::getInstance(vr_ipd));
break;
case 2:
setCurrentMode(RedCyan::getInstance(vr_ipd));
break;
case 3:
setCurrentMode(SideBySideFull::getInstance(vr_ipd));
break;
case 4:
setCurrentMode(SideBySideSquished::getInstance(vr_ipd));
break;
case 5:
setCurrentMode(LeftEyeView::getInstance(vr_ipd));
break;
case 6:
setCurrentMode(RightEyeView::getInstance(vr_ipd));
break;
case 7:
if (screen->enable_quadbuffered) {
setCurrentMode(QuadStereo::getInstance(vr_ipd));
}
else {
setCurrentMode(MonoView::getInstance());
}
break;
// TODO: 8: Oculus Rift
case 9:
setCurrentMode(AmberBlue::getInstance(vr_ipd));
break;
// TODO: 10: HTC Vive/OpenVR
case 11:
setCurrentMode(TopBottom3D::getInstance(vr_ipd));
break;
case 12:
setCurrentMode(RowInterleaved3D::getInstance(vr_ipd));
break;
case 13:
setCurrentMode(ColumnInterleaved3D::getInstance(vr_ipd));
break;
case 14:
setCurrentMode(CheckerInterleaved3D::getInstance(vr_ipd));
break;
case 0:
default:
setCurrentMode(MonoView::getInstance());
break;
}
return *currentStereo3DMode;
}
const Stereo3DMode& Stereo3DMode::getMonoMode()
{
setCurrentMode(MonoView::getInstance());
return *currentStereo3DMode;
}
} /* namespace s3d */

View file

@ -1,116 +0,0 @@
//
//---------------------------------------------------------------------------
//
// Copyright(C) 2015 Christopher Bruns
// All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see http://www.gnu.org/licenses/
//
//--------------------------------------------------------------------------
//
/*
** gl_stereo_leftright.cpp
** Offsets for left and right eye views
**
*/
#include "gl_stereo_leftright.h"
#include "vectors.h" // RAD2DEG
#include "doomtype.h" // M_PI
#include "hwrenderer/utility/hw_cvars.h"
#include "gl_load/gl_system.h"
#include "gl/renderer/gl_renderstate.h"
#include "gl/renderer/gl_renderer.h"
#include "gl/renderer/gl_renderbuffers.h"
EXTERN_CVAR(Float, vr_screendist)
EXTERN_CVAR(Float, vr_hunits_per_meter)
EXTERN_CVAR(Bool, vr_swap_eyes)
namespace s3d {
/* virtual */
VSMatrix ShiftedEyePose::GetProjection(float fov, float aspectRatio, float fovRatio) const
{
double zNear = 5.0;
double zFar = 65536.0;
// For stereo 3D, use asymmetric frustum shift in projection matrix
// Q: shouldn't shift vary with roll angle, at least for desktop display?
// A: No. (lab) roll is not measured on desktop display (yet)
double frustumShift = zNear * getShift() / vr_screendist; // meters cancel, leaving doom units
// double frustumShift = 0; // Turning off shift for debugging
double fH = zNear * tan(DEG2RAD(fov) / 2) / fovRatio;
double fW = fH * aspectRatio;
double left = -fW - frustumShift;
double right = fW - frustumShift;
double bottom = -fH;
double top = fH;
VSMatrix result(1);
result.frustum(left, right, bottom, top, zNear, zFar);
return result;
}
/* virtual */
void ShiftedEyePose::GetViewShift(float yaw, float outViewShift[3]) const
{
float dx = -cos(DEG2RAD(yaw)) * vr_hunits_per_meter * getShift();
float dy = sin(DEG2RAD(yaw)) * vr_hunits_per_meter * getShift();
outViewShift[0] = dx;
outViewShift[1] = dy;
outViewShift[2] = 0;
}
float ShiftedEyePose::getShift() const
{
return vr_swap_eyes ? -shift : shift;
}
/* static */
const LeftEyeView& LeftEyeView::getInstance(float ipd)
{
static LeftEyeView instance(ipd);
instance.setIpd(ipd);
return instance;
}
void LeftEyeView::Present() const
{
GLRenderer->mBuffers->BindOutputFB();
GLRenderer->ClearBorders();
GLRenderer->mBuffers->BindEyeTexture(0, 0);
GLRenderer->DrawPresentTexture(screen->mOutputLetterbox, true);
}
/* static */
const RightEyeView& RightEyeView::getInstance(float ipd)
{
static RightEyeView instance(ipd);
instance.setIpd(ipd);
return instance;
}
void RightEyeView::Present() const
{
GLRenderer->mBuffers->BindOutputFB();
GLRenderer->ClearBorders();
GLRenderer->mBuffers->BindEyeTexture(0, 0);
GLRenderer->DrawPresentTexture(screen->mOutputLetterbox, true);
}
} /* namespace s3d */

View file

@ -1,103 +0,0 @@
//
//---------------------------------------------------------------------------
//
// Copyright(C) 2015 Christopher Bruns
// All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see http://www.gnu.org/licenses/
//
//--------------------------------------------------------------------------
//
/*
** gl_stereo_leftright.h
** Offsets for left and right eye views
**
*/
#ifndef GL_STEREO_LEFTRIGHT_H_
#define GL_STEREO_LEFTRIGHT_H_
#include "gl_stereo3d.h"
namespace s3d {
class ShiftedEyePose : public EyePose
{
public:
ShiftedEyePose(float shift) : shift(shift) {};
float getShift() const;
virtual VSMatrix GetProjection(float fov, float aspectRatio, float fovRatio) const;
virtual void GetViewShift(float yaw, float outViewShift[3]) const;
protected:
void setShift(float shift) { this->shift = shift; }
private:
float shift;
};
class LeftEyePose : public ShiftedEyePose
{
public:
LeftEyePose(float ipd) : ShiftedEyePose( float(-0.5) * ipd) {}
float getIpd() const { return float(fabs(2.0f*getShift())); }
void setIpd(float ipd) { setShift(float(-0.5)*ipd); }
};
class RightEyePose : public ShiftedEyePose
{
public:
RightEyePose(float ipd) : ShiftedEyePose(float(+0.5)*ipd) {}
float getIpd() const { return float(fabs(2.0f*getShift())); }
void setIpd(float ipd) { setShift(float(+0.5)*ipd); }
};
/**
* As if viewed through the left eye only
*/
class LeftEyeView : public Stereo3DMode
{
public:
static const LeftEyeView& getInstance(float ipd);
LeftEyeView(float ipd) : eye(ipd) { eye_ptrs.Push(&eye); }
float getIpd() const { return eye.getIpd(); }
void setIpd(float ipd) { eye.setIpd(ipd); }
void Present() const override;
protected:
LeftEyePose eye;
};
class RightEyeView : public Stereo3DMode
{
public:
static const RightEyeView& getInstance(float ipd);
RightEyeView(float ipd) : eye(ipd) { eye_ptrs.Push(&eye); }
float getIpd() const { return eye.getIpd(); }
void setIpd(float ipd) { eye.setIpd(ipd); }
void Present() const override;
protected:
RightEyePose eye;
};
} /* namespace s3d */
#endif /* GL_STEREO_LEFTRIGHT_H_ */

View file

@ -1,56 +0,0 @@
//
//---------------------------------------------------------------------------
//
// Copyright(C) 2015 Christopher Bruns
// All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see http://www.gnu.org/licenses/
//
//--------------------------------------------------------------------------
//
/*
** scoped_color_mask.h
** Stack-scoped class for temporarily changing the OpenGL color mask setting.
**
*/
#ifndef GL_STEREO3D_SCOPED_COLOR_MASK_H_
#define GL_STEREO3D_SCOPED_COLOR_MASK_H_
#include "gl_load/gl_system.h"
/**
* Temporarily change color mask
*/
class ScopedColorMask
{
public:
ScopedColorMask(bool r, bool g, bool b, bool a)
{
gl_RenderState.GetColorMask(saved[0], saved[1], saved[2], saved[3]);
gl_RenderState.SetColorMask(r, g, b, a);
gl_RenderState.ApplyColorMask();
gl_RenderState.EnableDrawBuffers(1);
}
~ScopedColorMask() {
gl_RenderState.EnableDrawBuffers(gl_RenderState.GetPassDrawBufferCount());
gl_RenderState.SetColorMask(saved[0], saved[1], saved[2], saved[3]);
gl_RenderState.ApplyColorMask();
}
private:
bool saved[4];
};
#endif // GL_STEREO3D_SCOPED_COLOR_MASK_H_

View file

@ -37,10 +37,10 @@
#include "gl/renderer/gl_renderbuffers.h"
#include "gl/textures/gl_samplers.h"
#include "hwrenderer/utility/hw_clock.h"
#include "hwrenderer/utility/hw_vrmodes.h"
#include "gl/data/gl_vertexbuffer.h"
#include "gl/data/gl_uniformbuffer.h"
#include "gl/models/gl_models.h"
#include "gl/stereo3d/gl_stereo3d.h"
#include "gl/shaders/gl_shaderprogram.h"
#include "gl_debug.h"
#include "r_videoscale.h"
@ -342,7 +342,7 @@ IHardwareTexture *OpenGLFrameBuffer::CreateHardwareTexture(FTexture *tex)
FModelRenderer *OpenGLFrameBuffer::CreateModelRenderer(int mli)
{
return new FGLModelRenderer(mli);
return new FGLModelRenderer(nullptr, mli);
}
IUniformBuffer *OpenGLFrameBuffer::CreateUniformBuffer(size_t size, bool staticuse)
@ -361,11 +361,6 @@ void OpenGLFrameBuffer::UnbindTexUnit(int no)
FHardwareTexture::Unbind(no);
}
void OpenGLFrameBuffer::FlushTextures()
{
if (GLRenderer) GLRenderer->FlushTextures();
}
void OpenGLFrameBuffer::TextureFilterChanged()
{
if (GLRenderer != NULL && GLRenderer->mSamplerManager != NULL) GLRenderer->mSamplerManager->SetTextureFilterMode();
@ -380,7 +375,10 @@ void OpenGLFrameBuffer::SetViewportRects(IntRect *bounds)
{
Super::SetViewportRects(bounds);
if (!bounds)
s3d::Stereo3DMode::getCurrentMode().AdjustViewports();
{
auto vrmode = VRMode::GetVRMode(true);
vrmode->AdjustViewport(this);
}
}

View file

@ -37,7 +37,6 @@ public:
IHardwareTexture *CreateHardwareTexture(FTexture *tex) override;
FModelRenderer *CreateModelRenderer(int mli) override;
void UnbindTexUnit(int no) override;
void FlushTextures() override;
void TextureFilterChanged() override;
void BeginFrame() override;
void SetViewportRects(IntRect *bounds) override;

View file

@ -89,7 +89,7 @@ void HWDrawInfo::AddLine (seg_t *seg, bool portalclip)
if (portalclip)
{
int clipres = mClipPortal->ClipSeg(seg);
int clipres = mClipPortal->ClipSeg(seg, Viewpoint.Pos);
if (clipres == PClip_InFront) return;
}
@ -353,6 +353,7 @@ void HWDrawInfo::RenderThings(subsector_t * sub, sector_t * sector)
SetupSprite.Clock();
sector_t * sec=sub->sector;
// Handle all things in sector.
const auto &vp = Viewpoint;
for (auto p = sec->touching_renderthings; p != nullptr; p = p->m_snext)
{
auto thing = p->m_thing;
@ -362,7 +363,7 @@ void HWDrawInfo::RenderThings(subsector_t * sub, sector_t * sector)
FIntCVar *cvar = thing->GetInfo()->distancecheck;
if (cvar != nullptr && *cvar >= 0)
{
double dist = (thing->Pos() - r_viewpoint.Pos).LengthSquared();
double dist = (thing->Pos() - vp.Pos).LengthSquared();
double check = (double)**cvar;
if (dist >= check * check)
{
@ -383,7 +384,7 @@ void HWDrawInfo::RenderThings(subsector_t * sub, sector_t * sector)
FIntCVar *cvar = thing->GetInfo()->distancecheck;
if (cvar != nullptr && *cvar >= 0)
{
double dist = (thing->Pos() - r_viewpoint.Pos).LengthSquared();
double dist = (thing->Pos() - vp.Pos).LengthSquared();
double check = (double)**cvar;
if (dist >= check * check)
{
@ -537,13 +538,13 @@ void HWDrawInfo::DoSubsector(subsector_t * sub)
portal = fakesector->GetPortalGroup(sector_t::ceiling);
if (portal != nullptr)
{
portal->AddSubsector(sub);
AddSubsectorToPortal(portal, sub);
}
portal = fakesector->GetPortalGroup(sector_t::floor);
if (portal != nullptr)
{
portal->AddSubsector(sub);
AddSubsectorToPortal(portal, sub);
}
}
}

View file

@ -373,10 +373,10 @@ angle_t Clipper::AngleToPseudo(angle_t ang)
//
//-----------------------------------------------------------------------------
angle_t R_PointToPseudoAngle(double x, double y)
angle_t Clipper::PointToPseudoAngle(double x, double y)
{
double vecx = x - r_viewpoint.Pos.X;
double vecy = y - r_viewpoint.Pos.Y;
double vecx = x - viewpoint->Pos.X;
double vecy = y - viewpoint->Pos.Y;
if (vecx == 0 && vecy == 0)
{
@ -427,14 +427,15 @@ bool Clipper::CheckBox(const float *bspcoord)
// Find the corners of the box
// that define the edges from current viewpoint.
boxpos = (r_viewpoint.Pos.X <= bspcoord[BOXLEFT] ? 0 : r_viewpoint.Pos.X < bspcoord[BOXRIGHT ] ? 1 : 2) +
(r_viewpoint.Pos.Y >= bspcoord[BOXTOP ] ? 0 : r_viewpoint.Pos.Y > bspcoord[BOXBOTTOM] ? 4 : 8);
auto &vp = viewpoint;
boxpos = (vp->Pos.X <= bspcoord[BOXLEFT] ? 0 : vp->Pos.X < bspcoord[BOXRIGHT ] ? 1 : 2) +
(vp->Pos.Y >= bspcoord[BOXTOP ] ? 0 : vp->Pos.Y > bspcoord[BOXBOTTOM] ? 4 : 8);
if (boxpos == 5) return true;
check = checkcoord[boxpos];
angle1 = R_PointToPseudoAngle (bspcoord[check[0]], bspcoord[check[1]]);
angle2 = R_PointToPseudoAngle (bspcoord[check[2]], bspcoord[check[3]]);
angle1 = PointToPseudoAngle (bspcoord[check[0]], bspcoord[check[1]]);
angle2 = PointToPseudoAngle (bspcoord[check[2]], bspcoord[check[3]]);
return SafeCheckRange(angle2, angle1);
}

View file

@ -6,14 +6,6 @@
#include "r_utility.h"
#include "memarena.h"
angle_t R_PointToPseudoAngle(double x, double y);
// Used to speed up angle calculations during clipping
inline angle_t vertex_t::GetClipAngle()
{
return R_PointToPseudoAngle(p.X, p.Y);
}
class ClipNode
{
friend class Clipper;
@ -37,6 +29,7 @@ class Clipper
ClipNode * clipnodes = nullptr;
ClipNode * cliphead = nullptr;
ClipNode * silhouette = nullptr; // will be preserved even when RemoveClipRange is called
const FRenderViewpoint *viewpoint = nullptr;
bool blocked = false;
static angle_t AngleToPseudo(angle_t ang);
@ -79,6 +72,11 @@ public:
return c;
}
void SetViewpoint(const FRenderViewpoint &vp)
{
viewpoint = &vp;
}
void SetSilhouette();
bool SafeCheckRange(angle_t startAngle, angle_t endAngle)
@ -106,6 +104,13 @@ public:
}
}
void SafeAddClipRange(const vertex_t *v1, const vertex_t *v2)
{
angle_t a2 = PointToPseudoAngle(v1->p.X, v1->p.Y);
angle_t a1 = PointToPseudoAngle(v2->p.X, v2->p.Y);
SafeAddClipRange(a1,a2);
}
void SafeAddClipRangeRealAngles(angle_t startangle, angle_t endangle)
{
SafeAddClipRange(AngleToPseudo(startangle), AngleToPseudo(endangle));
@ -142,12 +147,14 @@ public:
return blocked;
}
angle_t PointToPseudoAngle(double x, double y);
bool CheckBox(const float *bspcoord);
// Used to speed up angle calculations during clipping
inline angle_t GetClipAngle(vertex_t *v)
{
return unsigned(v->angletime) == starttime ? v->viewangle : (v->angletime = starttime, v->viewangle = R_PointToPseudoAngle(v->p.X, v->p.Y));
return unsigned(v->angletime) == starttime ? v->viewangle : (v->angletime = starttime, v->viewangle = PointToPseudoAngle(v->p.X, v->p.Y));
}
};

View file

@ -0,0 +1,284 @@
//
//---------------------------------------------------------------------------
//
// Copyright(C) 2000-2018 Christoph Oelckers
// All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see http://www.gnu.org/licenses/
//
//--------------------------------------------------------------------------
//
/*
** gl_drawinfo.cpp
** Basic scene draw info management class
**
*/
#include "a_sharedglobal.h"
#include "r_utility.h"
#include "r_sky.h"
#include "d_player.h"
#include "g_levellocals.h"
#include "hw_fakeflat.h"
#include "hw_drawinfo.h"
#include "hw_portal.h"
#include "hwrenderer/utility/hw_clock.h"
#include "hwrenderer/utility/hw_cvars.h"
EXTERN_CVAR(Float, r_visibility)
CVAR(Bool, gl_bandedswlight, false, CVAR_ARCHIVE)
sector_t * hw_FakeFlat(sector_t * sec, sector_t * dest, area_t in_area, bool back);
//==========================================================================
//
//
//
//==========================================================================
void HWDrawInfo::ClearBuffers()
{
for(unsigned int i=0;i< otherfloorplanes.Size();i++)
{
gl_subsectorrendernode * node = otherfloorplanes[i];
while (node)
{
gl_subsectorrendernode * n = node;
node = node->next;
delete n;
}
}
otherfloorplanes.Clear();
for(unsigned int i=0;i< otherceilingplanes.Size();i++)
{
gl_subsectorrendernode * node = otherceilingplanes[i];
while (node)
{
gl_subsectorrendernode * n = node;
node = node->next;
delete n;
}
}
otherceilingplanes.Clear();
// clear all the lists that might not have been cleared already
MissingUpperTextures.Clear();
MissingLowerTextures.Clear();
MissingUpperSegs.Clear();
MissingLowerSegs.Clear();
SubsectorHacks.Clear();
CeilingStacks.Clear();
FloorStacks.Clear();
HandledSubsectors.Clear();
spriteindex = 0;
CurrentMapSections.Resize(level.NumMapSections);
CurrentMapSections.Zero();
sectorrenderflags.Resize(level.sectors.Size());
ss_renderflags.Resize(level.subsectors.Size());
no_renderflags.Resize(level.subsectors.Size());
memset(&sectorrenderflags[0], 0, level.sectors.Size() * sizeof(sectorrenderflags[0]));
memset(&ss_renderflags[0], 0, level.subsectors.Size() * sizeof(ss_renderflags[0]));
memset(&no_renderflags[0], 0, level.nodes.Size() * sizeof(no_renderflags[0]));
mClipPortal = nullptr;
mCurrentPortal = nullptr;
}
//==========================================================================
//
//
//
//==========================================================================
void HWDrawInfo::UpdateCurrentMapSection()
{
const int mapsection = R_PointInSubsector(Viewpoint.Pos)->mapsection;
CurrentMapSections.Set(mapsection);
}
//-----------------------------------------------------------------------------
//
// Sets the area the camera is in
//
//-----------------------------------------------------------------------------
void HWDrawInfo::SetViewArea()
{
auto &vp = Viewpoint;
// The render_sector is better suited to represent the current position in GL
vp.sector = R_PointInSubsector(vp.Pos)->render_sector;
// Get the heightsec state from the render sector, not the current one!
if (vp.sector->GetHeightSec())
{
in_area = vp.Pos.Z <= vp.sector->heightsec->floorplane.ZatPoint(vp.Pos) ? area_below :
(vp.Pos.Z > vp.sector->heightsec->ceilingplane.ZatPoint(vp.Pos) &&
!(vp.sector->heightsec->MoreFlags&SECMF_FAKEFLOORONLY)) ? area_above : area_normal;
}
else
{
in_area = level.HasHeightSecs ? area_default : area_normal; // depends on exposed lower sectors, if map contains heightsecs.
}
}
//-----------------------------------------------------------------------------
//
//
//
//-----------------------------------------------------------------------------
int HWDrawInfo::SetFullbrightFlags(player_t *player)
{
FullbrightFlags = 0;
// check for special colormaps
player_t * cplayer = player? player->camera->player : nullptr;
if (cplayer)
{
int cm = CM_DEFAULT;
if (cplayer->extralight == INT_MIN)
{
cm = CM_FIRSTSPECIALCOLORMAP + INVERSECOLORMAP;
Viewpoint.extralight = 0;
FullbrightFlags = Fullbright;
// This does never set stealth vision.
}
else if (cplayer->fixedcolormap != NOFIXEDCOLORMAP)
{
cm = CM_FIRSTSPECIALCOLORMAP + cplayer->fixedcolormap;
FullbrightFlags = Fullbright;
if (gl_enhanced_nv_stealth > 2) FullbrightFlags |= StealthVision;
}
else if (cplayer->fixedlightlevel != -1)
{
auto torchtype = PClass::FindActor(NAME_PowerTorch);
auto litetype = PClass::FindActor(NAME_PowerLightAmp);
for (AInventory * in = cplayer->mo->Inventory; in; in = in->Inventory)
{
//PalEntry color = in->CallGetBlend();
// Need special handling for light amplifiers
if (in->IsKindOf(torchtype))
{
FullbrightFlags = Fullbright;
if (gl_enhanced_nv_stealth > 1) FullbrightFlags |= StealthVision;
}
else if (in->IsKindOf(litetype))
{
FullbrightFlags = Fullbright;
if (gl_enhanced_nightvision) FullbrightFlags |= Nightvision;
if (gl_enhanced_nv_stealth > 0) FullbrightFlags |= StealthVision;
}
}
}
return cm;
}
else
{
return CM_DEFAULT;
}
}
//-----------------------------------------------------------------------------
//
// R_FrustumAngle
//
//-----------------------------------------------------------------------------
angle_t HWDrawInfo::FrustumAngle()
{
float tilt = fabs(Viewpoint.HWAngles.Pitch.Degrees);
// If the pitch is larger than this you can look all around at a FOV of 90°
if (tilt > 46.0f) return 0xffffffff;
// ok, this is a gross hack that barely works...
// but at least it doesn't overestimate too much...
double floatangle = 2.0 + (45.0 + ((tilt / 1.9)))*Viewpoint.FieldOfView.Degrees*48.0 / AspectMultiplier(r_viewwindow.WidescreenRatio) / 90.0;
angle_t a1 = DAngle(floatangle).BAMs();
if (a1 >= ANGLE_180) return 0xffffffff;
return a1;
}
//-----------------------------------------------------------------------------
//
// Setup the modelview matrix
//
//-----------------------------------------------------------------------------
void HWDrawInfo::SetViewMatrix(const FRotator &angles, float vx, float vy, float vz, bool mirror, bool planemirror)
{
float mult = mirror ? -1.f : 1.f;
float planemult = planemirror ? -level.info->pixelstretch : level.info->pixelstretch;
VPUniforms.mViewMatrix.loadIdentity();
VPUniforms.mViewMatrix.rotate(angles.Roll.Degrees, 0.0f, 0.0f, 1.0f);
VPUniforms.mViewMatrix.rotate(angles.Pitch.Degrees, 1.0f, 0.0f, 0.0f);
VPUniforms.mViewMatrix.rotate(angles.Yaw.Degrees, 0.0f, mult, 0.0f);
VPUniforms.mViewMatrix.translate(vx * mult, -vz * planemult, -vy);
VPUniforms.mViewMatrix.scale(-mult, planemult, 1);
}
//-----------------------------------------------------------------------------
//
// SetupView
// Setup the view rotation matrix for the given viewpoint
//
//-----------------------------------------------------------------------------
void HWDrawInfo::SetupView(float vx, float vy, float vz, bool mirror, bool planemirror)
{
auto &vp = Viewpoint;
vp.SetViewAngle(r_viewwindow);
SetViewMatrix(vp.HWAngles, vx, vy, vz, mirror, planemirror);
SetCameraPos(vp.Pos);
ApplyVPUniforms();
}
//-----------------------------------------------------------------------------
//
//
//
//-----------------------------------------------------------------------------
IPortal * HWDrawInfo::FindPortal(const void * src)
{
int i = Portals.Size() - 1;
while (i >= 0 && Portals[i] && Portals[i]->GetSource() != src) i--;
return i >= 0 ? Portals[i] : nullptr;
}
//-----------------------------------------------------------------------------
//
//
//
//-----------------------------------------------------------------------------
void HWViewpointUniforms::SetDefaults()
{
mProjectionMatrix.loadIdentity();
mViewMatrix.loadIdentity();
mNormalViewMatrix.loadIdentity();
mViewHeight = viewheight;
mGlobVis = (float)R_GetGlobVis(r_viewwindow, r_visibility) / 32.f;
mPalLightLevels = static_cast<int>(gl_bandedswlight) | (static_cast<int>(gl_fogmode) << 8);
mClipLine.X = -10000000.0f;
}

View file

@ -1,7 +1,11 @@
#pragma once
#include <atomic>
#include "vectors.h"
#include "r_defs.h"
#include "r_utility.h"
#include "hw_viewpointuniforms.h"
#include "v_video.h"
struct FSectorPortalGroup;
@ -18,6 +22,8 @@ struct HUDSprite;
class Clipper;
class IPortal;
class FFlatVertexGenerator;
class IRenderQueue;
class HWScenePortalBase;
//==========================================================================
//
@ -53,7 +59,10 @@ enum EPortalClip
struct HWDrawInfo
{
virtual ~HWDrawInfo() {}
virtual ~HWDrawInfo()
{
ClearBuffers();
}
struct wallseg
{
@ -91,14 +100,17 @@ struct HWDrawInfo
bool isNightvision() const { return !!(FullbrightFlags & Nightvision); }
bool isStealthVision() const { return !!(FullbrightFlags & StealthVision); }
HWDrawInfo * outer = nullptr;
int FullbrightFlags;
std::atomic<int> spriteindex;
IPortal *mClipPortal;
FRotator mAngles;
FVector2 mViewVector;
AActor *mViewActor;
HWScenePortalBase *mClipPortal;
IPortal *mCurrentPortal;
//FRotator mAngles;
IShadowMap *mShadowMap;
Clipper *mClipper;
FRenderViewpoint Viewpoint;
HWViewpointUniforms VPUniforms; // per-viewpoint uniform state
TArray<IPortal *> Portals;
TArray<MissingTextureInfo> MissingUpperTextures;
TArray<MissingTextureInfo> MissingLowerTextures;
@ -138,6 +150,7 @@ private:
sector_t fakesec; // this is a struct member because it gets used in recursively called functions so it cannot be put on the stack.
void UnclipSubsector(subsector_t *sub);
void AddLine(seg_t *seg, bool portalclip);
void PolySubsector(subsector_t * sub);
@ -148,6 +161,31 @@ private:
void RenderThings(subsector_t * sub, sector_t * sector);
void DoSubsector(subsector_t * sub);
public:
void SetCameraPos(const DVector3 &pos)
{
VPUniforms.mCameraPos = { (float)pos.X, (float)pos.Z, (float)pos.Y, 0.f };
}
void SetClipHeight(float h, float d)
{
VPUniforms.mClipHeight = h;
VPUniforms.mClipHeightDirection = d;
VPUniforms.mClipLine.X = -1000001.f;
}
void SetClipLine(line_t *line)
{
VPUniforms.mClipLine = { (float)line->v1->fX(), (float)line->v1->fY(), (float)line->Delta().X, (float)line->Delta().Y };
VPUniforms.mClipHeight = 0;
}
bool ClipLineShouldBeActive()
{
return (screen->hwcaps & RFL_NO_CLIP_PLANES) && VPUniforms.mClipLine.X > -1000000.f;
}
IPortal * FindPortal(const void * src);
void RenderBSPNode(void *node);
void ClearBuffers();
@ -188,6 +226,9 @@ public:
void PrepareTargeterSprites();
void UpdateCurrentMapSection();
void SetViewMatrix(const FRotator &angles, float vx, float vy, float vz, bool mirror, bool planemirror);
void SetupView(float vx, float vy, float vz, bool mirror, bool planemirror);
angle_t FrustumAngle();
virtual void DrawWall(GLWall *wall, int pass) = 0;
virtual void DrawFlat(GLFlat *flat, int pass, bool trans) = 0;
@ -195,7 +236,7 @@ public:
virtual void FloodUpperGap(seg_t * seg) = 0;
virtual void FloodLowerGap(seg_t * seg) = 0;
virtual void ProcessLowerMinisegs(TArray<seg_t *> &lowersegs) = 0;
void ProcessLowerMinisegs(TArray<seg_t *> &lowersegs);
virtual void AddSubsectorToPortal(FSectorPortalGroup *portal, subsector_t *sub) = 0;
virtual void AddWall(GLWall *w) = 0;
@ -206,6 +247,8 @@ public:
virtual void AddHUDSprite(HUDSprite *huds) = 0;
virtual int UploadLights(FDynLightData &data) = 0;
virtual void ApplyVPUniforms() = 0;
virtual bool SetDepthClamp(bool on) = 0;
virtual GLDecal *AddDecal(bool onmirror) = 0;
virtual std::pair<FFlatVertex *, unsigned int> AllocVertices(unsigned int count) = 0;

View file

@ -269,7 +269,7 @@ void HWDrawList::SortWallIntoPlane(SortNode * head, SortNode * sort)
GLFlat * fh = flats[drawitems[head->itemindex].index];
GLWall * ws = walls[drawitems[sort->itemindex].index];
bool ceiling = fh->z > r_viewpoint.Pos.Z;
bool ceiling = fh->z > SortZ;
if ((ws->ztop[0] > fh->z || ws->ztop[1] > fh->z) && (ws->zbottom[0] < fh->z || ws->zbottom[1] < fh->z))
{
@ -327,7 +327,7 @@ void HWDrawList::SortSpriteIntoPlane(SortNode * head, SortNode * sort)
GLFlat * fh = flats[drawitems[head->itemindex].index];
GLSprite * ss = sprites[drawitems[sort->itemindex].index];
bool ceiling = fh->z > r_viewpoint.Pos.Z;
bool ceiling = fh->z > SortZ;
auto hiz = ss->z1 > ss->z2 ? ss->z1 : ss->z2;
auto loz = ss->z1 < ss->z2 ? ss->z1 : ss->z2;
@ -688,6 +688,7 @@ SortNode * HWDrawList::DoSort(HWDrawInfo *di, SortNode * head)
//==========================================================================
void HWDrawList::Sort(HWDrawInfo *di)
{
SortZ = di->Viewpoint.Pos.Z;
MakeSortList();
sorted = DoSort(di, SortNodes[SortNodeStart]);
}

View file

@ -61,6 +61,7 @@ struct HWDrawList
TArray<GLSprite*> sprites;
TArray<GLDrawItem> drawitems;
int SortNodeStart;
float SortZ;
SortNode * sorted;
public:

View file

@ -384,7 +384,7 @@ public:
void SplitSprite(HWDrawInfo *di, sector_t * frontsector, bool translucent);
void PerformSpriteClipAdjustment(AActor *thing, const DVector2 &thingpos, float spriteheight);
bool CalculateVertices(HWDrawInfo *di, FVector3 *v);
bool CalculateVertices(HWDrawInfo *di, FVector3 *v, DVector3 *vp);
public:

View file

@ -386,79 +386,3 @@ sector_t * hw_FakeFlat(sector_t * sec, sector_t * dest, area_t in_area, bool bac
}
return dest;
}
//-----------------------------------------------------------------------------
//
// Sets the area the camera is in
//
//-----------------------------------------------------------------------------
void HWDrawInfo::SetViewArea()
{
// The render_sector is better suited to represent the current position in GL
r_viewpoint.sector = R_PointInSubsector(r_viewpoint.Pos)->render_sector;
// Get the heightsec state from the render sector, not the current one!
if (r_viewpoint.sector->GetHeightSec())
{
in_area = r_viewpoint.Pos.Z <= r_viewpoint.sector->heightsec->floorplane.ZatPoint(r_viewpoint.Pos) ? area_below :
(r_viewpoint.Pos.Z > r_viewpoint.sector->heightsec->ceilingplane.ZatPoint(r_viewpoint.Pos) &&
!(r_viewpoint.sector->heightsec->MoreFlags&SECMF_FAKEFLOORONLY)) ? area_above : area_normal;
}
else
{
in_area = level.HasHeightSecs ? area_default : area_normal; // depends on exposed lower sectors, if map contains heightsecs.
}
}
int HWDrawInfo::SetFullbrightFlags(player_t *player)
{
FullbrightFlags = 0;
// check for special colormaps
player_t * cplayer = player? player->camera->player : nullptr;
if (cplayer)
{
int cm = CM_DEFAULT;
if (cplayer->extralight == INT_MIN)
{
cm = CM_FIRSTSPECIALCOLORMAP + INVERSECOLORMAP;
r_viewpoint.extralight = 0;
FullbrightFlags = Fullbright;
// This does never set stealth vision.
}
else if (cplayer->fixedcolormap != NOFIXEDCOLORMAP)
{
cm = CM_FIRSTSPECIALCOLORMAP + cplayer->fixedcolormap;
FullbrightFlags = Fullbright;
if (gl_enhanced_nv_stealth > 2) FullbrightFlags |= StealthVision;
}
else if (cplayer->fixedlightlevel != -1)
{
auto torchtype = PClass::FindActor(NAME_PowerTorch);
auto litetype = PClass::FindActor(NAME_PowerLightAmp);
for (AInventory * in = cplayer->mo->Inventory; in; in = in->Inventory)
{
//PalEntry color = in->CallGetBlend();
// Need special handling for light amplifiers
if (in->IsKindOf(torchtype))
{
FullbrightFlags = Fullbright;
if (gl_enhanced_nv_stealth > 1) FullbrightFlags |= StealthVision;
}
else if (in->IsKindOf(litetype))
{
FullbrightFlags = Fullbright;
if (gl_enhanced_nightvision) FullbrightFlags |= Nightvision;
if (gl_enhanced_nv_stealth > 0) FullbrightFlags |= StealthVision;
}
}
}
return cm;
}
else
{
return CM_DEFAULT;
}
}

View file

@ -9,7 +9,7 @@ enum area_t : int
};
// Global functions. Make them members of GLRenderer later?
// Global functions.
bool hw_CheckClip(side_t * sidedef, sector_t * frontsector, sector_t * backsector);
sector_t * hw_FakeFlat(sector_t * sec, sector_t * dest, area_t in_area, bool back);
area_t hw_CheckViewArea(vertex_t *v1, vertex_t *v2, sector_t *frontsector, sector_t *backsector);

View file

@ -306,6 +306,7 @@ void GLFlat::ProcessSector(HWDrawInfo *di, sector_t * frontsector)
dynlightindex = -1;
uint8_t &srf = di->sectorrenderflags[sector->sectornum];
const auto &vp = di->Viewpoint;
//
//
@ -314,7 +315,7 @@ void GLFlat::ProcessSector(HWDrawInfo *di, sector_t * frontsector)
//
//
//
if (frontsector->floorplane.ZatPoint(r_viewpoint.Pos) <= r_viewpoint.Pos.Z)
if (frontsector->floorplane.ZatPoint(vp.Pos) <= vp.Pos.Z)
{
// process the original floor first.
@ -374,7 +375,7 @@ void GLFlat::ProcessSector(HWDrawInfo *di, sector_t * frontsector)
//
//
//
if (frontsector->ceilingplane.ZatPoint(r_viewpoint.Pos) >= r_viewpoint.Pos.Z)
if (frontsector->ceilingplane.ZatPoint(vp.Pos) >= vp.Pos.Z)
{
// process the original ceiling first.
@ -465,7 +466,7 @@ void GLFlat::ProcessSector(HWDrawInfo *di, sector_t * frontsector)
double ff_top = rover->top.plane->ZatPoint(sector->centerspot);
if (ff_top < lastceilingheight)
{
if (r_viewpoint.Pos.Z <= rover->top.plane->ZatPoint(r_viewpoint.Pos))
if (vp.Pos.Z <= rover->top.plane->ZatPoint(vp.Pos))
{
SetFrom3DFloor(rover, true, !!(rover->flags&FF_FOG));
Colormap.FadeColor = frontsector->Colormap.FadeColor;
@ -479,7 +480,7 @@ void GLFlat::ProcessSector(HWDrawInfo *di, sector_t * frontsector)
double ff_bottom = rover->bottom.plane->ZatPoint(sector->centerspot);
if (ff_bottom < lastceilingheight)
{
if (r_viewpoint.Pos.Z <= rover->bottom.plane->ZatPoint(r_viewpoint.Pos))
if (vp.Pos.Z <= rover->bottom.plane->ZatPoint(vp.Pos))
{
SetFrom3DFloor(rover, false, !(rover->flags&FF_FOG));
Colormap.FadeColor = frontsector->Colormap.FadeColor;
@ -505,7 +506,7 @@ void GLFlat::ProcessSector(HWDrawInfo *di, sector_t * frontsector)
double ff_bottom = rover->bottom.plane->ZatPoint(sector->centerspot);
if (ff_bottom > lastfloorheight || (rover->flags&FF_FIX))
{
if (r_viewpoint.Pos.Z >= rover->bottom.plane->ZatPoint(r_viewpoint.Pos))
if (vp.Pos.Z >= rover->bottom.plane->ZatPoint(vp.Pos))
{
SetFrom3DFloor(rover, false, !(rover->flags&FF_FOG));
Colormap.FadeColor = frontsector->Colormap.FadeColor;
@ -526,7 +527,7 @@ void GLFlat::ProcessSector(HWDrawInfo *di, sector_t * frontsector)
double ff_top = rover->top.plane->ZatPoint(sector->centerspot);
if (ff_top > lastfloorheight)
{
if (r_viewpoint.Pos.Z >= rover->top.plane->ZatPoint(r_viewpoint.Pos))
if (vp.Pos.Z >= rover->top.plane->ZatPoint(vp.Pos))
{
SetFrom3DFloor(rover, true, !!(rover->flags&FF_FOG));
Colormap.FadeColor = frontsector->Colormap.FadeColor;

View file

@ -0,0 +1,628 @@
//
//---------------------------------------------------------------------------
//
// Copyright(C) 2004-2018 Christoph Oelckers
// All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see http://www.gnu.org/licenses/
//
//--------------------------------------------------------------------------
//
/*
** hw_portal.cpp
** portal maintenance classes for skyboxes, horizons etc. (API independent parts)
**
*/
#include "c_dispatch.h"
#include "portal.h"
#include "p_maputl.h"
#include "hw_portal.h"
#include "hw_clipper.h"
#include "actor.h"
#include "g_levellocals.h"
EXTERN_CVAR(Int, r_mirror_recursions)
//-----------------------------------------------------------------------------
//
// StartFrame
//
//-----------------------------------------------------------------------------
void FPortalSceneState::StartFrame()
{
if (renderdepth == 0)
{
inskybox = false;
screen->instack[sector_t::floor] = screen->instack[sector_t::ceiling] = 0;
}
renderdepth++;
}
//-----------------------------------------------------------------------------
//
// printing portal info
//
//-----------------------------------------------------------------------------
static bool gl_portalinfo;
CCMD(gl_portalinfo)
{
gl_portalinfo = true;
}
static FString indent;
//-----------------------------------------------------------------------------
//
// EndFrame
//
//-----------------------------------------------------------------------------
void FPortalSceneState::EndFrame(HWDrawInfo *di)
{
IPortal * p;
if (gl_portalinfo)
{
Printf("%s%d portals, depth = %d\n%s{\n", indent.GetChars(), di->Portals.Size(), renderdepth, indent.GetChars());
indent += " ";
}
// Only use occlusion query if there are more than 2 portals.
// Otherwise there's too much overhead.
// (And don't forget to consider the separating null pointers!)
bool usequery = di->Portals.Size() > 2 + (unsigned)renderdepth;
while (di->Portals.Pop(p) && p)
{
if (gl_portalinfo)
{
Printf("%sProcessing %s, depth = %d, query = %d\n", indent.GetChars(), p->GetName(), renderdepth, usequery);
}
if (p->lines.Size() > 0)
{
p->RenderPortal(true, usequery, di);
}
delete p;
}
renderdepth--;
if (gl_portalinfo)
{
indent.Truncate(long(indent.Len()-2));
Printf("%s}\n", indent.GetChars());
if (indent.Len() == 0) gl_portalinfo = false;
}
}
//-----------------------------------------------------------------------------
//
// Renders one sky portal without a stencil.
// In more complex scenes using a stencil for skies can severely stall
// the GPU and there's rarely more than one sky visible at a time.
//
//-----------------------------------------------------------------------------
bool FPortalSceneState::RenderFirstSkyPortal(int recursion, HWDrawInfo *outer_di)
{
IPortal * p;
IPortal * best = nullptr;
unsigned bestindex = 0;
// Find the one with the highest amount of lines.
// Normally this is also the one that saves the largest amount
// of time by drawing it before the scene itself.
auto &portals = outer_di->Portals;
for (int i = portals.Size() - 1; i >= 0; --i)
{
p = portals[i];
if (p->lines.Size() > 0 && p->IsSky())
{
// Cannot clear the depth buffer inside a portal recursion
if (recursion && p->NeedDepthBuffer()) continue;
if (!best || p->lines.Size() > best->lines.Size())
{
best = p;
bestindex = i;
}
}
}
if (best)
{
portals.Delete(bestindex);
best->RenderPortal(false, false, outer_di);
delete best;
return true;
}
return false;
}
//-----------------------------------------------------------------------------
//
//
//
//-----------------------------------------------------------------------------
void HWScenePortalBase::ClearClipper(HWDrawInfo *di, Clipper *clipper)
{
auto outer_di = di->outer;
DAngle angleOffset = deltaangle(outer_di->Viewpoint.Angles.Yaw, di->Viewpoint.Angles.Yaw);
clipper->Clear();
auto &lines = mOwner->lines;
// Set the clipper to the minimal visible area
clipper->SafeAddClipRange(0, 0xffffffff);
for (unsigned int i = 0; i < lines.Size(); i++)
{
DAngle startAngle = (DVector2(lines[i].glseg.x2, lines[i].glseg.y2) - outer_di->Viewpoint.Pos).Angle() + angleOffset;
DAngle endAngle = (DVector2(lines[i].glseg.x1, lines[i].glseg.y1) - outer_di->Viewpoint.Pos).Angle() + angleOffset;
if (deltaangle(endAngle, startAngle) < 0)
{
clipper->SafeRemoveClipRangeRealAngles(startAngle.BAMs(), endAngle.BAMs());
}
}
// and finally clip it to the visible area
angle_t a1 = di->FrustumAngle();
if (a1 < ANGLE_180) clipper->SafeAddClipRangeRealAngles(di->Viewpoint.Angles.Yaw.BAMs() + a1, di->Viewpoint.Angles.Yaw.BAMs() - a1);
// lock the parts that have just been clipped out.
clipper->SetSilhouette();
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//
//
// Common code for line to line and mirror portals
//
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
int HWLinePortal::ClipSeg(seg_t *seg, const DVector3 &viewpos)
{
line_t *linedef = seg->linedef;
if (!linedef)
{
return PClip_Inside; // should be handled properly.
}
return P_ClipLineToPortal(linedef, line(), viewpos) ? PClip_InFront : PClip_Inside;
}
int HWLinePortal::ClipSubsector(subsector_t *sub)
{
// this seg is completely behind the mirror
for (unsigned int i = 0; i<sub->numlines; i++)
{
if (P_PointOnLineSidePrecise(sub->firstline[i].v1->fPos(), line()) == 0) return PClip_Inside;
}
return PClip_InFront;
}
int HWLinePortal::ClipPoint(const DVector2 &pos)
{
if (P_PointOnLineSidePrecise(pos, line()))
{
return PClip_InFront;
}
return PClip_Inside;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//
//
// Mirror Portal
//
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//
//
//
//-----------------------------------------------------------------------------
bool HWMirrorPortal::Setup(HWDrawInfo *di, Clipper *clipper)
{
auto state = mOwner->mState;
if (state->renderdepth > r_mirror_recursions)
{
return false;
}
auto &vp = di->Viewpoint;
di->UpdateCurrentMapSection();
di->mClipPortal = this;
DAngle StartAngle = vp.Angles.Yaw;
DVector3 StartPos = vp.Pos;
vertex_t *v1 = linedef->v1;
vertex_t *v2 = linedef->v2;
// the player is always visible in a mirror.
vp.showviewer = true;
// Reflect the current view behind the mirror.
if (linedef->Delta().X == 0)
{
// vertical mirror
vp.Pos.X = 2 * v1->fX() - StartPos.X;
// Compensation for reendering inaccuracies
if (StartPos.X < v1->fX()) vp.Pos.X -= 0.1;
else vp.Pos.X += 0.1;
}
else if (linedef->Delta().Y == 0)
{
// horizontal mirror
vp.Pos.Y = 2 * v1->fY() - StartPos.Y;
// Compensation for reendering inaccuracies
if (StartPos.Y < v1->fY()) vp.Pos.Y -= 0.1;
else vp.Pos.Y += 0.1;
}
else
{
// any mirror--use floats to avoid integer overflow.
// Use doubles to avoid losing precision which is very important here.
double dx = v2->fX() - v1->fX();
double dy = v2->fY() - v1->fY();
double x1 = v1->fX();
double y1 = v1->fY();
double x = StartPos.X;
double y = StartPos.Y;
// the above two cases catch len == 0
double r = ((x - x1)*dx + (y - y1)*dy) / (dx*dx + dy * dy);
vp.Pos.X = (x1 + r * dx) * 2 - x;
vp.Pos.Y = (y1 + r * dy) * 2 - y;
// Compensation for reendering inaccuracies
FVector2 v(-dx, dy);
v.MakeUnit();
vp.Pos.X += v[1] * state->renderdepth / 2;
vp.Pos.Y += v[0] * state->renderdepth / 2;
}
vp.Angles.Yaw = linedef->Delta().Angle() * 2. - StartAngle;
vp.ViewActor = nullptr;
state->MirrorFlag++;
di->SetClipLine(linedef);
di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(state->MirrorFlag & 1), !!(state->PlaneMirrorFlag & 1));
clipper->Clear();
angle_t af = di->FrustumAngle();
if (af < ANGLE_180) clipper->SafeAddClipRangeRealAngles(vp.Angles.Yaw.BAMs() + af, vp.Angles.Yaw.BAMs() - af);
clipper->SafeAddClipRange(linedef->v1, linedef->v2);
return true;
}
void HWMirrorPortal::Shutdown(HWDrawInfo *di)
{
mOwner->mState->MirrorFlag--;
}
const char *HWMirrorPortal::GetName() { return "Mirror"; }
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//
//
// Line to line Portal
//
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//
//
//
//-----------------------------------------------------------------------------
bool HWLineToLinePortal::Setup(HWDrawInfo *di, Clipper *clipper)
{
// TODO: Handle recursion more intelligently
auto &state = mOwner->mState;
if (state->renderdepth>r_mirror_recursions)
{
return false;
}
auto &vp = di->Viewpoint;
di->mClipPortal = this;
line_t *origin = glport->lines[0]->mOrigin;
P_TranslatePortalXY(origin, vp.Pos.X, vp.Pos.Y);
P_TranslatePortalXY(origin, vp.ActorPos.X, vp.ActorPos.Y);
P_TranslatePortalAngle(origin, vp.Angles.Yaw);
P_TranslatePortalZ(origin, vp.Pos.Z);
P_TranslatePortalXY(origin, vp.Path[0].X, vp.Path[0].Y);
P_TranslatePortalXY(origin, vp.Path[1].X, vp.Path[1].Y);
if (!vp.showviewer && vp.camera != nullptr && P_PointOnLineSidePrecise(vp.Path[0], glport->lines[0]->mDestination) != P_PointOnLineSidePrecise(vp.Path[1], glport->lines[0]->mDestination))
{
double distp = (vp.Path[0] - vp.Path[1]).Length();
if (distp > EQUAL_EPSILON)
{
double dist1 = (vp.Pos - vp.Path[0]).Length();
double dist2 = (vp.Pos - vp.Path[1]).Length();
if (dist1 + dist2 < distp + 1)
{
vp.camera->renderflags |= RF_MAYBEINVISIBLE;
}
}
}
auto &lines = mOwner->lines;
for (unsigned i = 0; i < lines.Size(); i++)
{
line_t *line = lines[i].seg->linedef->getPortalDestination();
subsector_t *sub;
if (line->sidedef[0]->Flags & WALLF_POLYOBJ)
sub = R_PointInSubsector(line->v1->fixX(), line->v1->fixY());
else sub = line->frontsector->subsectors[0];
di->CurrentMapSections.Set(sub->mapsection);
}
vp.ViewActor = nullptr;
di->SetClipLine(glport->lines[0]->mDestination);
di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(state->MirrorFlag & 1), !!(state->PlaneMirrorFlag & 1));
ClearClipper(di, clipper);
return true;
}
void HWLineToLinePortal::RenderAttached(HWDrawInfo *di)
{
di->ProcessActorsInPortal(glport, di->in_area);
}
const char *HWLineToLinePortal::GetName() { return "LineToLine"; }
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//
//
// Skybox Portal
//
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//
// GLSkyboxPortal::DrawContents
//
//-----------------------------------------------------------------------------
bool HWSkyboxPortal::Setup(HWDrawInfo *di, Clipper *clipper)
{
auto state = mOwner->mState;
old_pm = state->PlaneMirrorMode;
if (mOwner->mState->skyboxrecursion >= 3)
{
return false;
}
auto &vp = di->Viewpoint;
state->skyboxrecursion++;
state->PlaneMirrorMode = 0;
state->inskybox = true;
AActor *origin = portal->mSkybox;
portal->mFlags |= PORTSF_INSKYBOX;
vp.extralight = 0;
oldclamp = di->SetDepthClamp(false);
vp.Pos = origin->InterpolatedPosition(vp.TicFrac);
vp.ActorPos = origin->Pos();
vp.Angles.Yaw += (origin->PrevAngles.Yaw + deltaangle(origin->PrevAngles.Yaw, origin->Angles.Yaw) * vp.TicFrac);
// Don't let the viewpoint be too close to a floor or ceiling
double floorh = origin->Sector->floorplane.ZatPoint(origin->Pos());
double ceilh = origin->Sector->ceilingplane.ZatPoint(origin->Pos());
if (vp.Pos.Z < floorh + 4) vp.Pos.Z = floorh + 4;
if (vp.Pos.Z > ceilh - 4) vp.Pos.Z = ceilh - 4;
vp.ViewActor = origin;
di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(state->MirrorFlag & 1), !!(state->PlaneMirrorFlag & 1));
di->SetViewArea();
ClearClipper(di, clipper);
di->UpdateCurrentMapSection();
return true;
}
void HWSkyboxPortal::Shutdown(HWDrawInfo *di)
{
auto state = mOwner->mState;
portal->mFlags &= ~PORTSF_INSKYBOX;
di->SetDepthClamp(oldclamp);
state->inskybox = false;
state->skyboxrecursion--;
state->PlaneMirrorMode = old_pm;
}
const char *HWSkyboxPortal::GetName() { return "Skybox"; }
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//
//
// Sector stack Portal
//
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//
// GLSectorStackPortal::SetupCoverage
//
//-----------------------------------------------------------------------------
static uint8_t SetCoverage(HWDrawInfo *di, void *node)
{
if (level.nodes.Size() == 0)
{
return 0;
}
if (!((size_t)node & 1)) // Keep going until found a subsector
{
node_t *bsp = (node_t *)node;
uint8_t coverage = SetCoverage(di, bsp->children[0]) | SetCoverage(di, bsp->children[1]);
di->no_renderflags[bsp->Index()] = coverage;
return coverage;
}
else
{
subsector_t *sub = (subsector_t *)((uint8_t *)node - 1);
return di->ss_renderflags[sub->Index()] & SSRF_SEEN;
}
}
void HWSectorStackPortal::SetupCoverage(HWDrawInfo *di)
{
for (unsigned i = 0; i<subsectors.Size(); i++)
{
subsector_t *sub = subsectors[i];
int plane = origin->plane;
for (int j = 0; j<sub->portalcoverage[plane].sscount; j++)
{
subsector_t *dsub = &::level.subsectors[sub->portalcoverage[plane].subsectors[j]];
di->CurrentMapSections.Set(dsub->mapsection);
di->ss_renderflags[dsub->Index()] |= SSRF_SEEN;
}
}
SetCoverage(di, ::level.HeadNode());
}
//-----------------------------------------------------------------------------
//
// GLSectorStackPortal::DrawContents
//
//-----------------------------------------------------------------------------
bool HWSectorStackPortal::Setup(HWDrawInfo *di, Clipper *clipper)
{
auto state = mOwner->mState;
FSectorPortalGroup *portal = origin;
auto &vp = di->Viewpoint;
vp.Pos += origin->mDisplacement;
vp.ActorPos += origin->mDisplacement;
vp.ViewActor = nullptr;
// avoid recursions!
if (origin->plane != -1) screen->instack[origin->plane]++;
di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(state->MirrorFlag & 1), !!(state->PlaneMirrorFlag & 1));
SetupCoverage(di);
ClearClipper(di, clipper);
// If the viewpoint is not within the portal, we need to invalidate the entire clip area.
// The portal will re-validate the necessary parts when its subsectors get traversed.
subsector_t *sub = R_PointInSubsector(vp.Pos);
if (!(di->ss_renderflags[sub->Index()] & SSRF_SEEN))
{
clipper->SafeAddClipRange(0, ANGLE_MAX);
clipper->SetBlocked(true);
}
return true;
}
void HWSectorStackPortal::Shutdown(HWDrawInfo *di)
{
if (origin->plane != -1) screen->instack[origin->plane]--;
}
const char *HWSectorStackPortal::GetName() { return "Sectorstack"; }
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//
//
// Plane Mirror Portal
//
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//
// GLPlaneMirrorPortal::DrawContents
//
//-----------------------------------------------------------------------------
bool HWPlaneMirrorPortal::Setup(HWDrawInfo *di, Clipper *clipper)
{
auto state = mOwner->mState;
if (state->renderdepth > r_mirror_recursions)
{
return false;
}
// A plane mirror needs to flip the portal exclusion logic because inside the mirror, up is down and down is up.
std::swap(screen->instack[sector_t::floor], screen->instack[sector_t::ceiling]);
auto &vp = di->Viewpoint;
old_pm = state->PlaneMirrorMode;
// the player is always visible in a mirror.
vp.showviewer = true;
double planez = origin->ZatPoint(vp.Pos);
vp.Pos.Z = 2 * planez - vp.Pos.Z;
vp.ViewActor = nullptr;
state->PlaneMirrorMode = origin->fC() < 0 ? -1 : 1;
state->PlaneMirrorFlag++;
di->SetClipHeight(planez, state->PlaneMirrorMode < 0 ? -1.f : 1.f);
di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(state->MirrorFlag & 1), !!(state->PlaneMirrorFlag & 1));
ClearClipper(di, clipper);
di->UpdateCurrentMapSection();
return true;
}
void HWPlaneMirrorPortal::Shutdown(HWDrawInfo *di)
{
auto state = mOwner->mState;
state->PlaneMirrorFlag--;
state->PlaneMirrorMode = old_pm;
std::swap(screen->instack[sector_t::floor], screen->instack[sector_t::ceiling]);
}
const char *HWPlaneMirrorPortal::GetName() { return origin->fC() < 0? "Planemirror ceiling" : "Planemirror floor"; }

View file

@ -1,6 +1,8 @@
#pragma once
#include "portal.h"
#include "hw_drawinfo.h"
#include "hw_drawstructs.h"
#include "hwrenderer/textures/hw_material.h"
struct GLSkyInfo
@ -33,12 +35,278 @@ struct GLHorizonInfo
PalEntry specialcolor;
};
struct FPortalSceneState;
class IPortal
{
friend struct FPortalSceneState;
public:
FPortalSceneState * mState;
TArray<GLWall> lines;
IPortal(FPortalSceneState *s, bool local);
virtual ~IPortal() {}
virtual int ClipSeg(seg_t *seg) { return PClip_Inside; }
virtual void * GetSource() const = 0; // GetSource MUST be implemented!
virtual const char *GetName() = 0;
virtual bool IsSky() { return false; }
virtual bool NeedCap() { return true; }
virtual bool NeedDepthBuffer() { return true; }
virtual void DrawContents(HWDrawInfo *di) = 0;
virtual void RenderAttached(HWDrawInfo *di) {}
virtual bool Start(bool usestencil, bool doquery, HWDrawInfo *outer_di, HWDrawInfo **pDi) = 0;
virtual void End(HWDrawInfo *di, bool usestencil) = 0;
void AddLine(GLWall * l)
{
lines.Push(*l);
}
void RenderPortal(bool usestencil, bool doquery, HWDrawInfo *outer_di)
{
// Start may perform an occlusion query. If that returns 0 there
// is no need to draw the stencil's contents and there's also no
// need to restore the affected area becasue there is none!
HWDrawInfo *di;
if (Start(usestencil, doquery, outer_di, &di))
{
DrawContents(di);
End(di, usestencil);
}
}
};
struct FPortalSceneState
{
int recursion = 0;
int MirrorFlag = 0;
int PlaneMirrorFlag = 0;
int renderdepth = 0;
int PlaneMirrorMode = 0;
bool inskybox = 0;
UniqueList<GLSkyInfo> UniqueSkies;
UniqueList<GLHorizonInfo> UniqueHorizons;
UniqueList<secplane_t> UniquePlaneMirrors;
int skyboxrecursion = 0;
void BeginScene()
{
UniqueSkies.Clear();
UniqueHorizons.Clear();
UniquePlaneMirrors.Clear();
}
int GetRecursion() const
{
return recursion;
}
bool isMirrored() const
{
return !!((MirrorFlag ^ PlaneMirrorFlag) & 1);
}
void StartFrame();
bool RenderFirstSkyPortal(int recursion, HWDrawInfo *outer_di);
void EndFrame(HWDrawInfo *outer_di);
};
inline IPortal::IPortal(FPortalSceneState *s, bool local) : mState(s)
{
//if (!local) s->portals.Push(this);
}
class HWScenePortalBase
{
protected:
IPortal *mOwner;
public:
HWScenePortalBase() {}
virtual ~HWScenePortalBase() {}
void SetOwner(IPortal *p) { mOwner = p; }
void ClearClipper(HWDrawInfo *di, Clipper *clipper);
virtual int ClipSeg(seg_t *seg, const DVector3 &viewpos) { return PClip_Inside; }
virtual int ClipSubsector(subsector_t *sub) { return PClip_Inside; }
virtual int ClipPoint(const DVector2 &pos) { return PClip_Inside; }
virtual line_t *ClipLine() { return nullptr; }
virtual bool IsSky() { return false; }
virtual bool NeedCap() { return false; }
virtual bool NeedDepthBuffer() { return true; }
virtual void * GetSource() const = 0; // GetSource MUST be implemented!
virtual const char *GetName() = 0;
virtual bool Setup(HWDrawInfo *di, Clipper *clipper) = 0;
virtual void Shutdown(HWDrawInfo *di) {}
virtual void RenderAttached(HWDrawInfo *di) {}
};
struct HWLinePortal : public HWScenePortalBase
{
// this must be the same as at the start of line_t, so that we can pass in this structure directly to P_ClipLineToPortal.
vertex_t *v1, *v2; // vertices, from v1 to v2
DVector2 delta; // precalculated v2 - v1 for side checking
angle_t angv1, angv2; // for quick comparisons with a line or subsector
HWLinePortal(line_t *line)
{
v1 = line->v1;
v2 = line->v2;
CalcDelta();
}
HWLinePortal(FLinePortalSpan *line)
{
if (line->lines[0]->mType != PORTT_LINKED || line->v1 == nullptr)
{
// For non-linked portals we must check the actual linedef.
line_t *lline = line->lines[0]->mDestination;
v1 = lline->v1;
v2 = lline->v2;
}
else
{
// For linked portals we can check the merged span.
v1 = line->v1;
v2 = line->v2;
}
CalcDelta();
}
void CalcDelta()
{
delta = v2->fPos() - v1->fPos();
}
line_t *line()
{
vertex_t **pv = &v1;
return reinterpret_cast<line_t*>(pv);
}
int ClipSeg(seg_t *seg, const DVector3 &viewpos) override;
int ClipSubsector(subsector_t *sub) override;
int ClipPoint(const DVector2 &pos);
bool NeedCap() override { return false; }
};
struct HWMirrorPortal : public HWLinePortal
{
// mirror portals always consist of single linedefs!
line_t * linedef;
protected:
bool Setup(HWDrawInfo *di, Clipper *clipper) override;
void Shutdown(HWDrawInfo *di) override;
void * GetSource() const override { return linedef; }
const char *GetName() override;
public:
HWMirrorPortal(line_t * line)
: HWLinePortal(line)
{
linedef = line;
}
};
struct HWLineToLinePortal : public HWLinePortal
{
FLinePortalSpan *glport;
protected:
bool Setup(HWDrawInfo *di, Clipper *clipper) override;
virtual void * GetSource() const override { return glport; }
virtual const char *GetName() override;
virtual line_t *ClipLine() override { return line(); }
virtual void RenderAttached(HWDrawInfo *di) override;
public:
HWLineToLinePortal(FLinePortalSpan *ll)
: HWLinePortal(ll)
{
glport = ll;
}
};
struct HWSkyboxPortal : public HWScenePortalBase
{
bool oldclamp;
int old_pm;
FSectorPortal * portal;
protected:
bool Setup(HWDrawInfo *di, Clipper *clipper) override;
void Shutdown(HWDrawInfo *di) override;
virtual void * GetSource() const { return portal; }
virtual bool IsSky() { return true; }
virtual const char *GetName();
public:
HWSkyboxPortal(FSectorPortal * pt)
{
portal = pt;
}
};
struct HWSectorStackPortal : public HWScenePortalBase
{
TArray<subsector_t *> subsectors;
protected:
bool Setup(HWDrawInfo *di, Clipper *clipper) override;
void Shutdown(HWDrawInfo *di) override;
virtual void * GetSource() const { return origin; }
virtual bool IsSky() { return true; } // although this isn't a real sky it can be handled as one.
virtual const char *GetName();
FSectorPortalGroup *origin;
public:
HWSectorStackPortal(FSectorPortalGroup *pt)
{
origin = pt;
}
void SetupCoverage(HWDrawInfo *di);
void AddSubsector(subsector_t *sub)
{
subsectors.Push(sub);
}
};
struct HWPlaneMirrorPortal : public HWScenePortalBase
{
int old_pm;
protected:
bool Setup(HWDrawInfo *di, Clipper *clipper) override;
void Shutdown(HWDrawInfo *di) override;
virtual void * GetSource() const { return origin; }
virtual const char *GetName();
secplane_t * origin;
public:
HWPlaneMirrorPortal(secplane_t * pt)
{
origin = pt;
}
};

View file

@ -30,68 +30,12 @@
#include "r_sky.h"
#include "g_levellocals.h"
#include "hwrenderer/scene/hw_drawinfo.h"
#include "hw_drawinfo.h"
#include "hw_drawstructs.h"
#include "hwrenderer/utility/hw_clock.h"
sector_t * hw_FakeFlat(sector_t * sec, sector_t * dest, area_t in_area, bool back);
void HWDrawInfo::ClearBuffers()
{
for(unsigned int i=0;i< otherfloorplanes.Size();i++)
{
gl_subsectorrendernode * node = otherfloorplanes[i];
while (node)
{
gl_subsectorrendernode * n = node;
node = node->next;
delete n;
}
}
otherfloorplanes.Clear();
for(unsigned int i=0;i< otherceilingplanes.Size();i++)
{
gl_subsectorrendernode * node = otherceilingplanes[i];
while (node)
{
gl_subsectorrendernode * n = node;
node = node->next;
delete n;
}
}
otherceilingplanes.Clear();
// clear all the lists that might not have been cleared already
MissingUpperTextures.Clear();
MissingLowerTextures.Clear();
MissingUpperSegs.Clear();
MissingLowerSegs.Clear();
SubsectorHacks.Clear();
CeilingStacks.Clear();
FloorStacks.Clear();
HandledSubsectors.Clear();
spriteindex = 0;
CurrentMapSections.Resize(level.NumMapSections);
CurrentMapSections.Zero();
sectorrenderflags.Resize(level.sectors.Size());
ss_renderflags.Resize(level.subsectors.Size());
no_renderflags.Resize(level.subsectors.Size());
memset(&sectorrenderflags[0], 0, level.sectors.Size() * sizeof(sectorrenderflags[0]));
memset(&ss_renderflags[0], 0, level.subsectors.Size() * sizeof(ss_renderflags[0]));
memset(&no_renderflags[0], 0, level.nodes.Size() * sizeof(no_renderflags[0]));
mClipPortal = nullptr;
}
void HWDrawInfo::UpdateCurrentMapSection()
{
const int mapsection = R_PointInSubsector(r_viewpoint.Pos)->mapsection;
CurrentMapSections.Set(mapsection);
}
//==========================================================================
//
// Adds a subsector plane to a sector's render list
@ -484,7 +428,7 @@ void HWDrawInfo::HandleMissingTextures(area_t in_area)
HandledSubsectors.Clear();
validcount++;
if (MissingUpperTextures[i].Planez > r_viewpoint.Pos.Z)
if (MissingUpperTextures[i].Planez > Viewpoint.Pos.Z)
{
// close the hole only if all neighboring sectors are an exact height match
// Otherwise just fill in the missing textures.
@ -556,7 +500,7 @@ void HWDrawInfo::HandleMissingTextures(area_t in_area)
HandledSubsectors.Clear();
validcount++;
if (MissingLowerTextures[i].Planez < r_viewpoint.Pos.Z)
if (MissingLowerTextures[i].Planez < Viewpoint.Pos.Z)
{
// close the hole only if all neighboring sectors are an exact height match
// Otherwise just fill in the missing textures.
@ -642,7 +586,7 @@ void HWDrawInfo::DrawUnhandledMissingTextures()
// already done!
if (seg->linedef->validcount == validcount) continue; // already done
seg->linedef->validcount = validcount;
if (seg->frontsector->GetPlaneTexZ(sector_t::ceiling) < r_viewpoint.Pos.Z) continue; // out of sight
if (seg->frontsector->GetPlaneTexZ(sector_t::ceiling) < Viewpoint.Pos.Z) continue; // out of sight
// FIXME: The check for degenerate subsectors should be more precise
if (seg->PartnerSeg && (seg->PartnerSeg->Subsector->flags & SSECF_DEGENERATE)) continue;
@ -664,7 +608,7 @@ void HWDrawInfo::DrawUnhandledMissingTextures()
if (seg->linedef->validcount == validcount) continue; // already done
seg->linedef->validcount = validcount;
if (!(sectorrenderflags[seg->backsector->sectornum] & SSRF_RENDERFLOOR)) continue;
if (seg->frontsector->GetPlaneTexZ(sector_t::floor) > r_viewpoint.Pos.Z) continue; // out of sight
if (seg->frontsector->GetPlaneTexZ(sector_t::floor) > Viewpoint.Pos.Z) continue; // out of sight
if (seg->backsector->transdoor) continue;
if (seg->backsector->GetTexture(sector_t::floor) == skyflatnum) continue;
if (seg->backsector->ValidatePortal(sector_t::floor) != NULL) continue;
@ -757,7 +701,7 @@ bool HWDrawInfo::CollectSubsectorsFloor(subsector_t * sub, sector_t * anchor)
sub->render_sector->GetPlaneTexZ(sector_t::floor) != anchor->GetPlaneTexZ(sector_t::floor) ||
sub->render_sector->GetFloorLight() != anchor->GetFloorLight())
{
if (sub == viewsubsector && r_viewpoint.Pos.Z < anchor->GetPlaneTexZ(sector_t::floor)) inview = true;
if (sub == viewsubsector && Viewpoint.Pos.Z < anchor->GetPlaneTexZ(sector_t::floor)) inview = true;
HandledSubsectors.Push(sub);
}
}
@ -901,9 +845,21 @@ bool HWDrawInfo::CollectSubsectorsCeiling(subsector_t * sub, sector_t * anchor)
//
//==========================================================================
void HWDrawInfo::ProcessLowerMinisegs(TArray<seg_t *> &lowersegs)
{
for(unsigned int j=0;j<lowersegs.Size();j++)
{
seg_t * seg=lowersegs[j];
GLWall wall;
wall.ProcessLowerMiniseg(this, seg, seg->Subsector->render_sector, seg->PartnerSeg->Subsector->render_sector);
rendered_lines++;
}
}
void HWDrawInfo::HandleHackedSubsectors()
{
viewsubsector = R_PointInSubsector(r_viewpoint.Pos);
viewsubsector = R_PointInSubsector(Viewpoint.Pos);
// Each subsector may only be processed once in this loop!
validcount++;

View file

@ -154,8 +154,9 @@ void GLWall::SkyPlane(HWDrawInfo *di, sector_t *sector, int plane, bool allowref
}
else if (allowreflect && sector->GetReflect(plane) > 0)
{
if ((plane == sector_t::ceiling && r_viewpoint.Pos.Z > sector->ceilingplane.fD()) ||
(plane == sector_t::floor && r_viewpoint.Pos.Z < -sector->floorplane.fD())) return;
auto vpz = di->Viewpoint.Pos.Z;
if ((plane == sector_t::ceiling && vpz > sector->ceilingplane.fD()) ||
(plane == sector_t::floor && vpz < -sector->floorplane.fD())) return;
ptype = PORTALTYPE_PLANEMIRROR;
planemirror = plane == sector_t::ceiling ? &sector->ceilingplane : &sector->floorplane;
}
@ -340,7 +341,7 @@ void GLWall::SkyBottom(HWDrawInfo *di, seg_t * seg,sector_t * fs,sector_t * bs,v
else
{
// Special hack for Vrack2b
if (bs->floorplane.ZatPoint(r_viewpoint.Pos) > r_viewpoint.Pos.Z) return;
if (bs->floorplane.ZatPoint(di->Viewpoint.Pos) > di->Viewpoint.Pos.Z) return;
}
}
zbottom[0]=zbottom[1]=-32768.0f;

View file

@ -289,7 +289,7 @@ void FSkyDomeCreator::SetupMatrices(FMaterial *tex, float x_offset, float y_offs
// smaller sky textures must be tiled. We restrict it to 128 sky pixels, though
modelMatrix.translate(0.f, -1250.f, 0.f);
modelMatrix.scale(1.f, 128 / 230.f, 1.f);
yscale = 128 / texh; // intentionally left as integer.
yscale = float(128 / texh); // intentionally left as integer.
}
else if (texh < 200)
{

View file

@ -68,8 +68,9 @@ EXTERN_CVAR(Float, transsouls)
//
//==========================================================================
bool GLSprite::CalculateVertices(HWDrawInfo *di, FVector3 *v)
bool GLSprite::CalculateVertices(HWDrawInfo *di, FVector3 *v, DVector3 *vp)
{
const auto &HWAngles = di->Viewpoint.HWAngles;
if (actor != nullptr && (actor->renderflags & RF_SPRITETYPEMASK) == RF_FLATSPRITE)
{
Matrix3x4 mat;
@ -146,10 +147,10 @@ bool GLSprite::CalculateVertices(HWDrawInfo *di, FVector3 *v)
{
// [CMB] Rotate relative to camera XY position, not just camera direction,
// which is nicer in VR
float xrel = xcenter - r_viewpoint.Pos.X;
float yrel = ycenter - r_viewpoint.Pos.Y;
float xrel = xcenter - vp->X;
float yrel = ycenter - vp->Y;
float absAngleDeg = RAD2DEG(atan2(-yrel, xrel));
float counterRotationDeg = 270. - di->mAngles.Yaw.Degrees; // counteracts existing sprite rotation
float counterRotationDeg = 270. - HWAngles.Yaw.Degrees; // counteracts existing sprite rotation
float relAngleDeg = counterRotationDeg + absAngleDeg;
mat.Rotate(0, 1, 0, relAngleDeg);
@ -157,7 +158,7 @@ bool GLSprite::CalculateVertices(HWDrawInfo *di, FVector3 *v)
// [fgsfds] calculate yaw vectors
float yawvecX = 0, yawvecY = 0, rollDegrees = 0;
float angleRad = (270. - di->mAngles.Yaw).Radians();
float angleRad = (270. - HWAngles.Yaw).Radians();
if (actor) rollDegrees = Angles.Roll.Degrees;
if (isFlatSprite)
{
@ -181,7 +182,7 @@ bool GLSprite::CalculateVertices(HWDrawInfo *di, FVector3 *v)
if (useOffsets) mat.Translate(xx, zz, yy);
if (drawWithXYBillboard)
{
mat.Rotate(-sin(angleRad), 0, cos(angleRad), -di->mAngles.Pitch.Degrees);
mat.Rotate(-sin(angleRad), 0, cos(angleRad), -HWAngles.Pitch.Degrees);
}
mat.Rotate(cos(angleRad), 0, sin(angleRad), rollDegrees);
if (useOffsets) mat.Translate(-xx, -zz, -yy);
@ -191,7 +192,7 @@ bool GLSprite::CalculateVertices(HWDrawInfo *di, FVector3 *v)
// Rotate the sprite about the vector starting at the center of the sprite
// triangle strip and with direction orthogonal to where the player is looking
// in the x/y plane.
mat.Rotate(-sin(angleRad), 0, cos(angleRad), -di->mAngles.Pitch.Degrees);
mat.Rotate(-sin(angleRad), 0, cos(angleRad), -HWAngles.Pitch.Degrees);
}
mat.Translate(-xcenter, -zcenter, -ycenter); // retreat from sprite center
@ -403,7 +404,8 @@ void GLSprite::Process(HWDrawInfo *di, AActor* thing, sector_t * sector, area_t
return;
}
AActor *camera = r_viewpoint.camera;
const auto &vp = di->Viewpoint;
AActor *camera = vp.camera;
if (thing->renderflags & RF_INVISIBLE || !thing->RenderStyle.IsVisible(thing->Alpha))
{
@ -425,26 +427,26 @@ void GLSprite::Process(HWDrawInfo *di, AActor* thing, sector_t * sector, area_t
}
// [RH] Interpolate the sprite's position to make it look smooth
DVector3 thingpos = thing->InterpolatedPosition(r_viewpoint.TicFrac);
DVector3 thingpos = thing->InterpolatedPosition(vp.TicFrac);
if (thruportal == 1) thingpos += level.Displacements.getOffset(thing->Sector->PortalGroup, sector->PortalGroup);
// Some added checks if the camera actor is not supposed to be seen. It can happen that some portal setup has this actor in view in which case it may not be skipped here
if (thing == camera && !r_viewpoint.showviewer)
if (thing == camera && !vp.showviewer)
{
DVector3 thingorigin = thing->Pos();
if (thruportal == 1) thingorigin += level.Displacements.getOffset(thing->Sector->PortalGroup, sector->PortalGroup);
if (fabs(thingorigin.X - r_viewpoint.ActorPos.X) < 2 && fabs(thingorigin.Y - r_viewpoint.ActorPos.Y) < 2) return;
if (fabs(thingorigin.X - vp.ActorPos.X) < 2 && fabs(thingorigin.Y - vp.ActorPos.Y) < 2) return;
}
// Thing is invisible if close to the camera.
if (thing->renderflags & RF_MAYBEINVISIBLE)
{
if (fabs(thingpos.X - r_viewpoint.Pos.X) < 32 && fabs(thingpos.Y - r_viewpoint.Pos.Y) < 32) return;
if (fabs(thingpos.X - vp.Pos.X) < 32 && fabs(thingpos.Y - vp.Pos.Y) < 32) return;
}
// Too close to the camera. This doesn't look good if it is a sprite.
if (fabs(thingpos.X - r_viewpoint.Pos.X) < 2 && fabs(thingpos.Y - r_viewpoint.Pos.Y) < 2)
if (fabs(thingpos.X - vp.Pos.X) < 2 && fabs(thingpos.Y - vp.Pos.Y) < 2)
{
if (r_viewpoint.Pos.Z >= thingpos.Z - 2 && r_viewpoint.Pos.Z <= thingpos.Z + thing->Height + 2)
if (vp.Pos.Z >= thingpos.Z - 2 && vp.Pos.Z <= thingpos.Z + thing->Height + 2)
{
// exclude vertically moving objects from this check.
if (!thing->Vel.isZero())
@ -460,13 +462,13 @@ void GLSprite::Process(HWDrawInfo *di, AActor* thing, sector_t * sector, area_t
// don't draw first frame of a player missile
if (thing->flags&MF_MISSILE)
{
if (!(thing->flags7 & MF7_FLYCHEAT) && thing->target == di->mViewActor && di->mViewActor != nullptr)
if (!(thing->flags7 & MF7_FLYCHEAT) && thing->target == vp.ViewActor && vp.ViewActor != nullptr)
{
double speed = thing->Vel.Length();
if (speed >= thing->target->radius / 2)
{
double clipdist = clamp(thing->Speed, thing->target->radius, thing->target->radius * 2);
if ((thingpos - r_viewpoint.Pos).LengthSquared() < clipdist * clipdist) return;
if ((thingpos - vp.Pos).LengthSquared() < clipdist * clipdist) return;
}
}
thing->flags7 |= MF7_FLYCHEAT; // do this only once for the very first frame, but not if it gets into range again.
@ -479,7 +481,7 @@ void GLSprite::Process(HWDrawInfo *di, AActor* thing, sector_t * sector, area_t
}
// disabled because almost none of the actual game code is even remotely prepared for this. If desired, use the INTERPOLATE flag.
if (thing->renderflags & RF_INTERPOLATEANGLES)
Angles = thing->InterpolatedAngles(r_viewpoint.TicFrac);
Angles = thing->InterpolatedAngles(vp.TicFrac);
else
Angles = thing->Angles;
@ -506,7 +508,7 @@ void GLSprite::Process(HWDrawInfo *di, AActor* thing, sector_t * sector, area_t
// [RH] Make floatbobbing a renderer-only effect.
if (thing->flags2 & MF2_FLOATBOB)
{
float fz = thing->GetBobOffset(r_viewpoint.TicFrac);
float fz = thing->GetBobOffset(vp.TicFrac);
z += fz;
}
@ -514,7 +516,7 @@ void GLSprite::Process(HWDrawInfo *di, AActor* thing, sector_t * sector, area_t
if (!modelframe)
{
bool mirror;
DAngle ang = (thingpos - r_viewpoint.Pos).Angle();
DAngle ang = (thingpos - vp.Pos).Angle();
FTextureID patch;
// [ZZ] add direct picnum override
if (isPicnumOverride)
@ -531,7 +533,7 @@ void GLSprite::Process(HWDrawInfo *di, AActor* thing, sector_t * sector, area_t
int rot;
if (!(thing->renderflags & RF_FLATSPRITE) || thing->flags7 & MF7_SPRITEANGLE)
{
sprangle = thing->GetSpriteAngle(ang, r_viewpoint.TicFrac);
sprangle = thing->GetSpriteAngle(ang, vp.TicFrac);
rot = -1;
}
else
@ -596,8 +598,8 @@ void GLSprite::Process(HWDrawInfo *di, AActor* thing, sector_t * sector, area_t
switch (spritetype)
{
case RF_FACESPRITE:
viewvecX = di->mViewVector.X;
viewvecY = di->mViewVector.Y;
viewvecX = vp.ViewVector.X;
viewvecY = vp.ViewVector.Y;
x1 = x - viewvecY*leftfac;
x2 = x - viewvecY*rightfac;
@ -632,7 +634,7 @@ void GLSprite::Process(HWDrawInfo *di, AActor* thing, sector_t * sector, area_t
gltexture = nullptr;
}
depth = FloatToFixed((x - r_viewpoint.Pos.X) * r_viewpoint.TanCos + (y - r_viewpoint.Pos.Y) * r_viewpoint.TanSin);
depth = FloatToFixed((x - vp.Pos.X) * vp.TanCos + (y - vp.Pos.Y) * vp.TanSin);
// light calculation
@ -923,7 +925,8 @@ void GLSprite::ProcessParticle (HWDrawInfo *di, particle_t *particle, sector_t *
}
}
double timefrac = r_viewpoint.TicFrac;
const auto &vp = di->Viewpoint;
double timefrac = vp.TicFrac;
if (paused || bglobal.freeze || (level.flags2 & LEVEL2_FROZEN))
timefrac = 0.;
float xvf = (particle->Vel.X) * timefrac;
@ -940,8 +943,8 @@ void GLSprite::ProcessParticle (HWDrawInfo *di, particle_t *particle, sector_t *
else factor = 1 / 7.f;
float scalefac=particle->size * factor;
float viewvecX = di->mViewVector.X;
float viewvecY = di->mViewVector.Y;
float viewvecX = vp.ViewVector.X;
float viewvecY = vp.ViewVector.Y;
x1=x+viewvecY*scalefac;
x2=x-viewvecY*scalefac;
@ -950,7 +953,7 @@ void GLSprite::ProcessParticle (HWDrawInfo *di, particle_t *particle, sector_t *
z1=z-scalefac;
z2=z+scalefac;
depth = FloatToFixed((x - r_viewpoint.Pos.X) * r_viewpoint.TanCos + (y - r_viewpoint.Pos.Y) * r_viewpoint.TanSin);
depth = FloatToFixed((x - vp.Pos.X) * vp.TanCos + (y - vp.Pos.Y) * vp.TanSin);
actor=nullptr;
this->particle=particle;
@ -980,6 +983,7 @@ void HWDrawInfo::ProcessActorsInPortal(FLinePortalSpan *glport, area_t in_area)
TMap<AActor*, bool> processcheck;
if (glport->validcount == validcount) return; // only process once per frame
glport->validcount = validcount;
const auto &vp = Viewpoint;
for (auto port : glport->lines)
{
line_t *line = port->mOrigin;
@ -1003,9 +1007,9 @@ void HWDrawInfo::ProcessActorsInPortal(FLinePortalSpan *glport, area_t in_area)
DVector3 newpos = savedpos;
sector_t fakesector;
if (!r_viewpoint.showviewer && th == r_viewpoint.camera)
if (!vp.showviewer && th == vp.camera)
{
if (fabs(savedpos.X - r_viewpoint.ActorPos.X) < 2 && fabs(savedpos.Y - r_viewpoint.ActorPos.Y) < 2)
if (fabs(savedpos.X - vp.ActorPos.X) < 2 && fabs(savedpos.Y - vp.ActorPos.Y) < 2)
{
continue;
}

View file

@ -0,0 +1,30 @@
#pragma once
#include "r_data/matrix.h"
#include "r_utility.h"
struct HWViewpointUniforms
{
VSMatrix mProjectionMatrix;
VSMatrix mViewMatrix;
VSMatrix mNormalViewMatrix;
FVector4 mCameraPos;
FVector4 mClipLine;
float mGlobVis = 1.f;
int mPalLightLevels = 0;
int mViewHeight = 0;
float mClipHeight = 0.f;
float mClipHeightDirection = 0.f;
void CalcDependencies()
{
mNormalViewMatrix.computeNormalMatrix(mViewMatrix);
}
void SetDefaults();
};

View file

@ -169,7 +169,7 @@ void GLWall::PutWall(HWDrawInfo *di, bool translucent)
if (translucent)
{
flags |= GLWF_TRANSLUCENT;
ViewDistance = (r_viewpoint.Pos - (seg->linedef->v1->fPos() + seg->linedef->Delta() / 2)).XY().LengthSquared();
ViewDistance = (di->Viewpoint.Pos - (seg->linedef->v1->fPos() + seg->linedef->Delta() / 2)).XY().LengthSquared();
}
if (di->isFullbrightScene())
@ -443,10 +443,11 @@ bool GLWall::DoHorizon(HWDrawInfo *di, seg_t * seg,sector_t * fs, vertex_t * v1,
ztop[1] = ztop[0] = fs->GetPlaneTexZ(sector_t::ceiling);
zbottom[1] = zbottom[0] = fs->GetPlaneTexZ(sector_t::floor);
if (r_viewpoint.Pos.Z < fs->GetPlaneTexZ(sector_t::ceiling))
auto vpz = di->Viewpoint.Pos.Z;
if (vpz < fs->GetPlaneTexZ(sector_t::ceiling))
{
if (r_viewpoint.Pos.Z > fs->GetPlaneTexZ(sector_t::floor))
zbottom[1] = zbottom[0] = r_viewpoint.Pos.Z;
if (vpz > fs->GetPlaneTexZ(sector_t::floor))
zbottom[1] = zbottom[0] = vpz;
if (fs->GetTexture(sector_t::ceiling) == skyflatnum)
{
@ -474,7 +475,7 @@ bool GLWall::DoHorizon(HWDrawInfo *di, seg_t * seg,sector_t * fs, vertex_t * v1,
ztop[1] = ztop[0] = zbottom[0];
}
if (r_viewpoint.Pos.Z > fs->GetPlaneTexZ(sector_t::floor))
if (vpz > fs->GetPlaneTexZ(sector_t::floor))
{
zbottom[1] = zbottom[0] = fs->GetPlaneTexZ(sector_t::floor);
if (fs->GetTexture(sector_t::floor) == skyflatnum)
@ -1457,7 +1458,7 @@ void GLWall::Process(HWDrawInfo *di, seg_t *seg, sector_t * frontsector, sector_
sector_t * segback;
#ifdef _DEBUG
if (seg->linedef->Index() == 10)
if (seg->linedef->Index() == 3407)
{
int a = 0;
}

View file

@ -76,10 +76,10 @@ static bool isBright(DPSprite *psp)
//
//==========================================================================
static WeaponPosition GetWeaponPosition(player_t *player)
static WeaponPosition GetWeaponPosition(player_t *player, double ticFrac)
{
WeaponPosition w;
P_BobWeapon(player, &w.bobx, &w.boby, r_viewpoint.TicFrac);
P_BobWeapon(player, &w.bobx, &w.boby, ticFrac);
// Interpolate the main weapon layer once so as to be able to add it to other layers.
if ((w.weapon = player->FindPSprite(PSP_WEAPON)) != nullptr)
@ -91,8 +91,8 @@ static WeaponPosition GetWeaponPosition(player_t *player)
}
else
{
w.wx = (float)(w.weapon->oldx + (w.weapon->x - w.weapon->oldx) * r_viewpoint.TicFrac);
w.wy = (float)(w.weapon->oldy + (w.weapon->y - w.weapon->oldy) * r_viewpoint.TicFrac);
w.wx = (float)(w.weapon->oldx + (w.weapon->x - w.weapon->oldx) * ticFrac);
w.wy = (float)(w.weapon->oldy + (w.weapon->y - w.weapon->oldy) * ticFrac);
}
}
else
@ -109,7 +109,7 @@ static WeaponPosition GetWeaponPosition(player_t *player)
//
//==========================================================================
static FVector2 BobWeapon(WeaponPosition &weap, DPSprite *psp)
static FVector2 BobWeapon(WeaponPosition &weap, DPSprite *psp, double ticFrac)
{
if (psp->firstTic)
{ // Can't interpolate the first tic.
@ -118,8 +118,8 @@ static FVector2 BobWeapon(WeaponPosition &weap, DPSprite *psp)
psp->oldy = psp->y;
}
float sx = float(psp->oldx + (psp->x - psp->oldx) * r_viewpoint.TicFrac);
float sy = float(psp->oldy + (psp->y - psp->oldy) * r_viewpoint.TicFrac);
float sx = float(psp->oldx + (psp->x - psp->oldx) * ticFrac);
float sy = float(psp->oldy + (psp->y - psp->oldy) * ticFrac);
if (psp->Flags & PSPF_ADDBOB)
{
@ -169,14 +169,14 @@ static WeaponLighting GetWeaponLighting(sector_t *viewsector, const DVector3 &po
if (i<lightlist.Size() - 1)
{
lightbottom = lightlist[i + 1].plane.ZatPoint(r_viewpoint.Pos);
lightbottom = lightlist[i + 1].plane.ZatPoint(pos);
}
else
{
lightbottom = viewsector->floorplane.ZatPoint(r_viewpoint.Pos);
lightbottom = viewsector->floorplane.ZatPoint(pos);
}
if (lightbottom<r_viewpoint.Pos.Z)
if (lightbottom < pos.Z)
{
l.cm = lightlist[i].extra_colormap;
l.lightlevel = hw_ClampLight(*lightlist[i].p_lightlevel);
@ -427,7 +427,9 @@ void HWDrawInfo::PreparePlayerSprites(sector_t * viewsector, area_t in_area)
player_t * player = playermo->player;
const bool hudModelStep = IsHUDModelForPlayerAvailable(player);
AActor *camera = r_viewpoint.camera;
const auto &vp = Viewpoint;
AActor *camera = vp.camera;
// this is the same as the software renderer
if (!player ||
@ -437,8 +439,8 @@ void HWDrawInfo::PreparePlayerSprites(sector_t * viewsector, area_t in_area)
(r_deathcamera && camera->health <= 0))
return;
WeaponPosition weap = GetWeaponPosition(camera->player);
WeaponLighting light = GetWeaponLighting(viewsector, r_viewpoint.Pos, isFullbrightScene(), in_area, camera->Pos());
WeaponPosition weap = GetWeaponPosition(camera->player, vp.TicFrac);
WeaponLighting light = GetWeaponLighting(viewsector, vp.Pos, isFullbrightScene(), in_area, camera->Pos());
// hack alert! Rather than changing everything in the underlying lighting code let's just temporarily change
// light mode here to draw the weapon sprite.
@ -460,7 +462,7 @@ void HWDrawInfo::PreparePlayerSprites(sector_t * viewsector, area_t in_area)
if (!hudsprite.GetWeaponRenderStyle(psp, camera, viewsector, light)) continue;
FVector2 spos = BobWeapon(weap, psp);
FVector2 spos = BobWeapon(weap, psp, vp.TicFrac);
hudsprite.dynrgb[0] = hudsprite.dynrgb[1] = hudsprite.dynrgb[2] = 0;
hudsprite.lightindex = -1;
@ -505,7 +507,7 @@ void HWDrawInfo::PrepareTargeterSprites()
{
AActor * playermo = players[consoleplayer].camera;
player_t * player = playermo->player;
AActor *camera = r_viewpoint.camera;
AActor *camera = Viewpoint.camera;
// this is the same as above
if (!player ||

View file

@ -8,6 +8,7 @@ class AActor;
enum area_t : int;
struct FSpriteModelFrame;
struct HWDrawInfo;
class FMaterial;
struct WeaponPosition

View file

@ -160,8 +160,9 @@ void CheckBench()
FString compose;
auto &vp = r_viewpoint;
compose.Format("Map %s: \"%s\",\nx = %1.4f, y = %1.4f, z = %1.4f, angle = %1.4f, pitch = %1.4f\n",
level.MapName.GetChars(), level.LevelName.GetChars(), r_viewpoint.Pos.X, r_viewpoint.Pos.Y, r_viewpoint.Pos.Z, r_viewpoint.Angles.Yaw.Degrees, r_viewpoint.Angles.Pitch.Degrees);
level.MapName.GetChars(), level.LevelName.GetChars(), vp.Pos.X, vp.Pos.Y, vp.Pos.Z, vp.Angles.Yaw.Degrees, vp.Angles.Pitch.Degrees);
AppendRenderStats(compose);
AppendRenderTimes(compose);

View file

@ -25,6 +25,7 @@
#include "c_dispatch.h"
#include "v_video.h"
#include "hw_cvars.h"
#include "hwrenderer/textures/hw_material.h"
#include "menu/menu.h"
@ -91,7 +92,7 @@ CUSTOM_CVAR(Float,gl_texture_filter_anisotropic,8.0f,CVAR_ARCHIVE|CVAR_GLOBALCON
CCMD(gl_flush)
{
screen->FlushTextures();
FMaterial::FlushAll();
}
CUSTOM_CVAR(Int, gl_texture_filter, 4, CVAR_ARCHIVE|CVAR_GLOBALCONFIG|CVAR_NOINITCALL)
@ -102,7 +103,7 @@ CUSTOM_CVAR(Int, gl_texture_filter, 4, CVAR_ARCHIVE|CVAR_GLOBALCONFIG|CVAR_NOINI
CUSTOM_CVAR(Bool, gl_texture_usehires, true, CVAR_ARCHIVE|CVAR_NOINITCALL)
{
screen->FlushTextures();
FMaterial::FlushAll();
}
CVAR(Bool, gl_precache, false, CVAR_ARCHIVE)

View file

@ -0,0 +1,176 @@
//
//---------------------------------------------------------------------------
//
// Copyright(C) 2015 Christopher Bruns
// All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see http://www.gnu.org/licenses/
//
//--------------------------------------------------------------------------
//
/*
** gl_stereo_leftright.cpp
** Offsets for left and right eye views
**
*/
#include "vectors.h" // RAD2DEG
#include "doomtype.h" // M_PI
#include "hwrenderer/utility/hw_cvars.h"
#include "hw_vrmodes.h"
#include "v_video.h"
// Set up 3D-specific console variables:
CVAR(Int, vr_mode, 0, CVAR_GLOBALCONFIG)
// switch left and right eye views
CVAR(Bool, vr_swap_eyes, false, CVAR_GLOBALCONFIG)
// intraocular distance in meters
CVAR(Float, vr_ipd, 0.062f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) // METERS
// distance between viewer and the display screen
CVAR(Float, vr_screendist, 0.80f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) // METERS
// default conversion between (vertical) DOOM units and meters
CVAR(Float, vr_hunits_per_meter, 41.0f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) // METERS
#define isqrt2 0.7071067812f
static VRMode vrmi_mono = { 1, 1.f, 1.f, 1.f,{ { 0.f, 1.f },{ 0.f, 0.f } } };
static VRMode vrmi_stereo = { 2, 1.f, 1.f, 1.f,{ { -.5f, 1.f },{ .5f, 1.f } } };
static VRMode vrmi_sbsfull = { 2, .5f, 1.f, 2.f,{ { -.5f, .5f },{ .5f, .5f } } };
static VRMode vrmi_sbssquished = { 2, .5f, 1.f, 1.f,{ { -.5f, 1.f },{ .5f, 1.f } } };
static VRMode vrmi_lefteye = { 1, 1.f, 1.f, 1.f, { { -.5f, 1.f },{ 0.f, 0.f } } };
static VRMode vrmi_righteye = { 1, 1.f, 1.f, 1.f,{ { .5f, 1.f },{ 0.f, 0.f } } };
static VRMode vrmi_topbottom = { 2, 1.f, .5f, 1.f,{ { -.5f, 1.f },{ .5f, 1.f } } };
static VRMode vrmi_checker = { 2, isqrt2, isqrt2, 1.f,{ { -.5f, 1.f },{ .5f, 1.f } } };
const VRMode *VRMode::GetVRMode(bool toscreen)
{
switch (toscreen && vid_rendermode == 4 ? vr_mode : 0)
{
default:
case VR_MONO:
return &vrmi_mono;
case VR_GREENMAGENTA:
case VR_REDCYAN:
case VR_QUADSTEREO:
case VR_AMBERBLUE:
return &vrmi_stereo;
case VR_SIDEBYSIDESQUISHED:
case VR_COLUMNINTERLEAVED:
return &vrmi_sbssquished;
case VR_SIDEBYSIDEFULL:
return &vrmi_sbsfull;
case VR_TOPBOTTOM:
case VR_ROWINTERLEAVED:
return &vrmi_topbottom;
case VR_LEFTEYEVIEW:
return &vrmi_lefteye;
case VR_RIGHTEYEVIEW:
return &vrmi_righteye;
case VR_CHECKERINTERLEAVED:
return &vrmi_checker;
}
}
void VRMode::AdjustViewport(DFrameBuffer *screen) const
{
screen->mSceneViewport.height = (int)(screen->mSceneViewport.height * mVerticalViewportScale);
screen->mSceneViewport.top = (int)(screen->mSceneViewport.top * mVerticalViewportScale);
screen->mSceneViewport.width = (int)(screen->mSceneViewport.width * mHorizontalViewportScale);
screen->mSceneViewport.left = (int)(screen->mSceneViewport.left * mHorizontalViewportScale);
screen->mScreenViewport.height = (int)(screen->mScreenViewport.height * mVerticalViewportScale);
screen->mScreenViewport.top = (int)(screen->mScreenViewport.top * mVerticalViewportScale);
screen->mScreenViewport.width = (int)(screen->mScreenViewport.width * mHorizontalViewportScale);
screen->mScreenViewport.left = (int)(screen->mScreenViewport.left * mHorizontalViewportScale);
}
VSMatrix VRMode::GetHUDSpriteProjection() const
{
VSMatrix mat;
int w = screen->GetWidth();
int h = screen->GetHeight();
float scaled_w = w / mWeaponProjectionScale;
float left_ofs = (w - scaled_w) / 2.f;
mat.ortho(left_ofs, left_ofs + scaled_w, (float)h, 0, -1.0f, 1.0f);
return mat;
}
float VREyeInfo::getShift() const
{
auto res = mShiftFactor * vr_ipd;
return vr_swap_eyes ? -res : res;
}
VSMatrix VREyeInfo::GetProjection(float fov, float aspectRatio, float fovRatio) const
{
VSMatrix result;
if (mShiftFactor == 0)
{
float fovy = (float)(2 * RAD2DEG(atan(tan(DEG2RAD(fov) / 2) / fovRatio)));
result.perspective(fovy, aspectRatio, screen->GetZNear(), screen->GetZFar());
return result;
}
else
{
double zNear = screen->GetZNear();
double zFar = screen->GetZFar();
// For stereo 3D, use asymmetric frustum shift in projection matrix
// Q: shouldn't shift vary with roll angle, at least for desktop display?
// A: No. (lab) roll is not measured on desktop display (yet)
double frustumShift = zNear * getShift() / vr_screendist; // meters cancel, leaving doom units
// double frustumShift = 0; // Turning off shift for debugging
double fH = zNear * tan(DEG2RAD(fov) / 2) / fovRatio;
double fW = fH * aspectRatio * mScaleFactor;
double left = -fW - frustumShift;
double right = fW - frustumShift;
double bottom = -fH;
double top = fH;
VSMatrix result(1);
result.frustum((float)left, (float)right, (float)bottom, (float)top, (float)zNear, (float)zFar);
return result;
}
}
/* virtual */
DVector3 VREyeInfo::GetViewShift(float yaw) const
{
if (mShiftFactor == 0)
{
// pass-through for Mono view
return { 0,0,0 };
}
else
{
double dx = -cos(DEG2RAD(yaw)) * vr_hunits_per_meter * getShift();
double dy = sin(DEG2RAD(yaw)) * vr_hunits_per_meter * getShift();
return { dx, dy, 0 };
}
}

View file

@ -0,0 +1,47 @@
#pragma once
#include "r_data/matrix.h"
class DFrameBuffer;
enum
{
VR_MONO = 0,
VR_GREENMAGENTA = 1,
VR_REDCYAN = 2,
VR_SIDEBYSIDEFULL = 3,
VR_SIDEBYSIDESQUISHED = 4,
VR_LEFTEYEVIEW = 5,
VR_RIGHTEYEVIEW = 6,
VR_QUADSTEREO = 7,
VR_AMBERBLUE = 9,
VR_TOPBOTTOM = 11,
VR_ROWINTERLEAVED = 12,
VR_COLUMNINTERLEAVED = 13,
VR_CHECKERINTERLEAVED = 14
};
struct VREyeInfo
{
float mShiftFactor;
float mScaleFactor;
VSMatrix GetProjection(float fov, float aspectRatio, float fovRatio) const;
DVector3 GetViewShift(float yaw) const;
private:
float getShift() const;
};
struct VRMode
{
int mEyeCount;
float mHorizontalViewportScale;
float mVerticalViewportScale;
float mWeaponProjectionScale;
VREyeInfo mEyes[2];
static const VRMode *GetVRMode(bool toscreen = true);
void AdjustViewport(DFrameBuffer *fb) const;
VSMatrix GetHUDSpriteProjection() const;
};

View file

@ -1,62 +0,0 @@
//
//---------------------------------------------------------------------------
//
// Copyright(C) 2015 Christopher Bruns
// All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see http://www.gnu.org/licenses/
//
//--------------------------------------------------------------------------
//
/*
** scoped_view_shifter.h
** Stack-scoped class for temporarily changing camera viewpoint
** Used for stereoscopic 3D.
**
*/
#ifndef GL_STEREO3D_SCOPED_VIEW_SHIFTER_H_
#define GL_STEREO3D_SCOPED_VIEW_SHIFTER_H_
#include "basictypes.h"
#include "vectors.h"
/**
* Temporarily shift
*/
class ScopedViewShifter
{
public:
ScopedViewShifter(DVector3 &var, float dxyz[3]) // in meters
{
// save original values
mVar = &var;
cachedView = var;
// modify values
var += DVector3(dxyz[0], dxyz[1], dxyz[2]);
}
~ScopedViewShifter()
{
// restore original values
*mVar = cachedView;
}
private:
DVector3 *mVar;
DVector3 cachedView;
};
#endif // GL_STEREO3D_SCOPED_VIEW_SHIFTER_H_

View file

@ -36,6 +36,25 @@
struct MapData
{
private:
struct ResourceHolder
{
FResourceFile *data = nullptr;
~ResourceHolder()
{
delete data;
}
ResourceHolder &operator=(FResourceFile *other) { data = other; return *this; }
FResourceFile *operator->() { return data; }
operator FResourceFile *() const { return data; }
};
// The order of members here is important
// Resource should be destructed after MapLumps as readers may share FResourceLump objects
// For example, this is the case when map .wad is loaded from .pk3 file
ResourceHolder resource;
struct MapLump
{
char Name[8] = { 0 };
@ -48,13 +67,6 @@ public:
bool isText = false;
bool InWad = false;
int lumpnum = -1;
FResourceFile * resource = nullptr;
~MapData()
{
if (resource != nullptr) delete resource;
resource = nullptr;
}
/*
void Seek(unsigned int lumpindex)

View file

@ -37,7 +37,7 @@ void PolyRenderModel(PolyRenderThread *thread, const Mat4f &worldToClip, uint32_
{
PolyModelRenderer renderer(thread, worldToClip, stencilValue);
renderer.AddLights(actor);
renderer.RenderModel(x, y, z, smf, actor);
renderer.RenderModel(x, y, z, smf, actor, r_viewpoint.TicFrac);
PolyTriangleDrawer::SetModelVertexShader(thread->DrawQueue, -1, -1, 0.0f);
}

View file

@ -88,7 +88,7 @@ void PolyDrawSectorPortal::SaveGlobals()
viewpoint.camera = nullptr;
viewpoint.sector = Portal->mDestination;
R_SetViewAngle(viewpoint, viewwindow);
viewpoint.SetViewAngle(viewwindow);
Portal->mFlags |= PORTSF_INSKYBOX;
if (Portal->mPartner > 0) level.sectorPortals[Portal->mPartner].mFlags |= PORTSF_INSKYBOX;
@ -114,7 +114,7 @@ void PolyDrawSectorPortal::RestoreGlobals()
//PolyRenderer::Instance()->Light.SetVisibility(savedvisibility);
R_SetViewAngle(viewpoint, viewwindow);
viewpoint.SetViewAngle(viewwindow);
}
/////////////////////////////////////////////////////////////////////////////
@ -236,7 +236,7 @@ void PolyDrawLinePortal::SaveGlobals()
viewpoint.camera = nullptr;
viewpoint.sector = R_PointInSubsector(viewpoint.Pos)->sector;
R_SetViewAngle(viewpoint, viewwindow);
viewpoint.SetViewAngle(viewwindow);
}
void PolyDrawLinePortal::RestoreGlobals()
@ -254,5 +254,5 @@ void PolyDrawLinePortal::RestoreGlobals()
viewpoint.camera->renderflags &= ~RF_INVISIBLE;
}
R_SetViewAngle(viewpoint, viewwindow);
viewpoint.SetViewAngle(viewwindow);
}

View file

@ -254,11 +254,6 @@ struct FSectorPortalGroup
{
DVector2 mDisplacement;
int plane;
GLSectorStackPortal *glportal; // for quick access to the render data. This is only valid during BSP traversal!
GLSectorStackPortal *GetRenderState();
void AddSubsector(subsector_t *sub);
};

View file

@ -36,6 +36,14 @@
#include "v_video.h"
#ifdef __OBJC__
@class NSCursor;
@class CocoaWindow;
#else
typedef struct objc_object NSCursor;
typedef struct objc_object CocoaWindow;
#endif
class SystemFrameBuffer : public DFrameBuffer
{
public:
@ -50,8 +58,18 @@ public:
int GetClientHeight() override;
void ToggleFullscreen(bool yes) override;
void SetMode(bool fullscreen, bool hiDPI);
static void UseHiDPI(bool hiDPI);
static void SetCursor(NSCursor* cursor);
static void SetWindowVisible(bool visible);
static void SetWindowTitle(const char* title);
protected:
bool UpdatePending;
CocoaWindow* m_window;
bool m_fullscreen;
bool m_hiDPI;
static const uint32_t GAMMA_CHANNEL_SIZE = 256;
static const uint32_t GAMMA_CHANNEL_COUNT = 3;
@ -60,7 +78,10 @@ protected:
bool m_supportsGamma;
uint16_t m_originalGamma[GAMMA_TABLE_SIZE];
SystemFrameBuffer();
SystemFrameBuffer() {}
void SetFullscreenMode();
void SetWindowedMode();
void InitializeState();

View file

@ -487,11 +487,6 @@ void NSEventToGameMousePosition(NSEvent* inEvent, event_t* outEvent)
outEvent->data1 = static_cast<int16_t>( viewPos.x);
outEvent->data2 = static_cast<int16_t>(frameHeight - viewPos.y);
// Compensate letterbox adjustment done by cross-platform code
// More elegant solution is a bit problematic due to HiDPI/Retina support
// What does this do? Add 0?
//outEvent->data2 += (screen->GetTrueHeight() - screen->ClientHeight()) / 2;
screen->ScaleCoordsFromWindow(outEvent->data1, outEvent->data2);
}

View file

@ -2,7 +2,7 @@
** i_video.mm
**
**---------------------------------------------------------------------------
** Copyright 2012-2015 Alexey Lysiuk
** Copyright 2012-2018 Alexey Lysiuk
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
@ -49,7 +49,6 @@
#include "st_console.h"
#include "v_text.h"
#include "version.h"
#include "videomodes.h"
#include "gl/renderer/gl_renderer.h"
#include "gl/system/gl_framebuffer.h"
@ -96,6 +95,11 @@ EXTERN_CVAR(Int, vid_defwidth)
EXTERN_CVAR(Int, vid_defheight)
EXTERN_CVAR(Bool, fullscreen)
CVAR(Int, win_x, -1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
CVAR(Int, win_y, -1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
CVAR(Int, win_w, -1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
CVAR(Int, win_h, -1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
CUSTOM_CVAR(Bool, vid_autoswitch, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL)
{
Printf("You must restart " GAMENAME " to apply graphics switching mode\n");
@ -154,6 +158,15 @@ namespace
[super setTitle:m_title];
}
- (void)frameDidChange:(NSNotification*)notification
{
const NSRect frame = [self frame];
win_x = frame.origin.x;
win_y = frame.origin.y;
win_w = frame.size.width;
win_h = frame.size.height;
}
@end
@ -200,28 +213,15 @@ namespace
class CocoaVideo : public IVideo
{
public:
CocoaVideo();
virtual DFrameBuffer* CreateFrameBuffer() override
{
auto fb = new OpenGLFrameBuffer(nullptr, fullscreen);
virtual DFrameBuffer* CreateFrameBuffer();
fb->SetMode(fullscreen, vid_hidpi);
fb->SetSize(fb->GetClientWidth(), fb->GetClientHeight());
static void ToggleFullscreen(bool yes);
static bool IsFullscreen();
static void UseHiDPI(bool hiDPI);
static void SetCursor(NSCursor* cursor);
static void SetWindowVisible(bool visible);
static void SetWindowTitle(const char* title);
private:
CocoaWindow* m_window;
bool m_fullscreen;
bool m_hiDPI;
void SetFullscreenMode();
void SetWindowedMode();
void SetMode(bool fullscreen, bool hiDPI);
static CocoaVideo* GetInstance();
return fb;
}
};
@ -264,13 +264,10 @@ extern id appCtrl;
namespace
{
CocoaWindow* CreateCocoaWindow(const NSUInteger styleMask)
CocoaWindow* CreateWindow(const NSUInteger styleMask)
{
static const CGFloat TEMP_WIDTH = VideoModes[0].width - 1;
static const CGFloat TEMP_HEIGHT = VideoModes[0].height - 1;
CocoaWindow* const window = [CocoaWindow alloc];
[window initWithContentRect:NSMakeRect(0, 0, TEMP_WIDTH, TEMP_HEIGHT)
[window initWithContentRect:NSMakeRect(0, 0, vid_defwidth, vid_defheight)
styleMask:styleMask
backing:NSBackingStoreBuffered
defer:NO];
@ -278,6 +275,16 @@ CocoaWindow* CreateCocoaWindow(const NSUInteger styleMask)
[window makeFirstResponder:appCtrl];
[window setAcceptsMouseMovedEvents:YES];
NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
[nc addObserver:window
selector:@selector(frameDidChange:)
name:NSWindowDidEndLiveResizeNotification
object:nil];
[nc addObserver:window
selector:@selector(frameDidChange:)
name:NSWindowDidMoveNotification
object:nil];
return window;
}
@ -312,11 +319,14 @@ NSOpenGLPixelFormat* CreatePixelFormat(const NSOpenGLPixelFormatAttribute profil
// ---------------------------------------------------------------------------
CocoaVideo::CocoaVideo()
: m_window(CreateCocoaWindow(STYLE_MASK_WINDOWED))
SystemFrameBuffer::SystemFrameBuffer(void*, const bool fullscreen)
: DFrameBuffer(vid_defwidth, vid_defheight)
, m_window(CreateWindow(STYLE_MASK_WINDOWED))
, m_fullscreen(false)
, m_hiDPI(false)
{
SetFlash(0, 0);
// Create OpenGL pixel format
NSOpenGLPixelFormatAttribute defaultProfile = NSOpenGLProfileVersion3_2Core;
@ -356,183 +366,8 @@ CocoaVideo::CocoaVideo()
[m_window setContentView:glView];
FConsoleWindow::GetInstance().Show(false);
}
// Create table for system-wide gamma correction
DFrameBuffer* CocoaVideo::CreateFrameBuffer()
{
PalEntry flashColor = 0;
int flashAmount = 0;
DFrameBuffer* fb = NULL;
fb = new OpenGLFrameBuffer(NULL, fullscreen);
fb->SetFlash(flashColor, flashAmount);
SetMode(fullscreen, vid_hidpi);
return fb;
}
void CocoaVideo::ToggleFullscreen(bool yes)
{
if (CocoaVideo* const video = GetInstance())
{
video->SetMode(yes, video->m_hiDPI);
}
}
bool CocoaVideo::IsFullscreen()
{
CocoaVideo* const video = GetInstance();
return NULL == video
? false
: video->m_fullscreen;
}
void CocoaVideo::UseHiDPI(const bool hiDPI)
{
if (CocoaVideo* const video = GetInstance())
{
video->SetMode(video->m_fullscreen, hiDPI);
}
}
void CocoaVideo::SetCursor(NSCursor* cursor)
{
if (CocoaVideo* const video = GetInstance())
{
NSWindow* const window = video->m_window;
CocoaView* const view = [window contentView];
[view setCursor:cursor];
[window invalidateCursorRectsForView:view];
}
}
void CocoaVideo::SetWindowVisible(bool visible)
{
if (CocoaVideo* const video = GetInstance())
{
if (visible)
{
[video->m_window orderFront:nil];
}
else
{
[video->m_window orderOut:nil];
}
I_SetNativeMouse(!visible);
}
}
void CocoaVideo::SetWindowTitle(const char* title)
{
if (CocoaVideo* const video = GetInstance())
{
NSString* const nsTitle = nullptr == title ? nil :
[NSString stringWithCString:title encoding:NSISOLatin1StringEncoding];
[video->m_window setTitle:nsTitle];
}
}
void CocoaVideo::SetFullscreenMode()
{
NSScreen* screen = [m_window screen];
const NSRect screenFrame = [screen frame];
const NSRect displayRect = vid_hidpi
? [screen convertRectToBacking:screenFrame]
: screenFrame;
if (!m_fullscreen)
{
[m_window setLevel:LEVEL_FULLSCREEN];
[m_window setStyleMask:STYLE_MASK_FULLSCREEN];
[m_window setHidesOnDeactivate:YES];
}
[m_window setFrame:screenFrame display:YES];
}
void CocoaVideo::SetWindowedMode()
{
const NSSize windowPixelSize = NSMakeSize(vid_defwidth, vid_defheight);
const NSSize windowSize = vid_hidpi
? [[m_window contentView] convertSizeFromBacking:windowPixelSize]
: windowPixelSize;
if (m_fullscreen)
{
[m_window setLevel:LEVEL_WINDOWED];
[m_window setStyleMask:STYLE_MASK_WINDOWED];
[m_window setHidesOnDeactivate:NO];
}
[m_window setContentSize:windowSize];
[m_window center];
[m_window enterFullscreenOnZoom];
[m_window exitAppOnClose];
}
void CocoaVideo::SetMode(const bool fullscreen, const bool hiDPI)
{
if (fullscreen == m_fullscreen
&& hiDPI == m_hiDPI)
{
return;
}
NSOpenGLView* const glView = [m_window contentView];
[glView setWantsBestResolutionOpenGLSurface:hiDPI];
if (fullscreen)
{
SetFullscreenMode();
}
else
{
SetWindowedMode();
}
const NSSize viewSize = I_GetContentViewSize(m_window);
glViewport(0, 0, static_cast<GLsizei>(viewSize.width), static_cast<GLsizei>(viewSize.height));
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
[[NSOpenGLContext currentContext] flushBuffer];
[m_window updateTitle];
if (![m_window isKeyWindow])
{
[m_window makeKeyAndOrderFront:nil];
}
m_fullscreen = fullscreen;
m_hiDPI = hiDPI;
}
CocoaVideo* CocoaVideo::GetInstance()
{
return static_cast<CocoaVideo*>(Video);
}
// ---------------------------------------------------------------------------
SystemFrameBuffer::SystemFrameBuffer(void*, const bool fullscreen)
: DFrameBuffer(vid_defwidth, vid_defheight)
, UpdatePending(false)
{
CGGammaValue gammaTable[GAMMA_TABLE_SIZE];
uint32_t actualChannelSize;
@ -547,24 +382,29 @@ SystemFrameBuffer::SystemFrameBuffer(void*, const bool fullscreen)
m_originalGamma[i] = static_cast<uint16_t>(gammaTable[i] * 65535.0f);
}
}
}
SystemFrameBuffer::SystemFrameBuffer()
{
FConsoleWindow::GetInstance().Show(false);
}
SystemFrameBuffer::~SystemFrameBuffer()
{
NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
[nc removeObserver:m_window
name:NSWindowDidMoveNotification
object:nil];
[nc removeObserver:m_window
name:NSWindowDidEndLiveResizeNotification
object:nil];
}
bool SystemFrameBuffer::IsFullscreen()
{
return CocoaVideo::IsFullscreen();
return m_fullscreen;
}
void SystemFrameBuffer::ToggleFullscreen(bool yes)
{
CocoaVideo::ToggleFullscreen(yes);
SetMode(yes, m_hiDPI);
}
@ -610,23 +450,141 @@ void SystemFrameBuffer::ResetGammaTable()
}
}
int SystemFrameBuffer::GetClientWidth()
{
NSView *view = [[NSOpenGLContext currentContext] view];
NSRect backingBounds = [view convertRectToBacking: [view bounds]];
int clientWidth = (int)backingBounds.size.width;
const int clientWidth = I_GetContentViewSize(m_window).width;
return clientWidth > 0 ? clientWidth : GetWidth();
}
int SystemFrameBuffer::GetClientHeight()
{
NSView *view = [[NSOpenGLContext currentContext] view];
NSRect backingBounds = [view convertRectToBacking: [view bounds]];
int clientHeight = (int)backingBounds.size.height;
const int clientHeight = I_GetContentViewSize(m_window).height;
return clientHeight > 0 ? clientHeight : GetHeight();
}
void SystemFrameBuffer::SetFullscreenMode()
{
if (!m_fullscreen)
{
[m_window setLevel:LEVEL_FULLSCREEN];
[m_window setStyleMask:STYLE_MASK_FULLSCREEN];
[m_window setHidesOnDeactivate:YES];
}
const NSRect screenFrame = [[m_window screen] frame];
[m_window setFrame:screenFrame display:YES];
}
void SystemFrameBuffer::SetWindowedMode()
{
if (m_fullscreen)
{
[m_window setLevel:LEVEL_WINDOWED];
[m_window setStyleMask:STYLE_MASK_WINDOWED];
[m_window setHidesOnDeactivate:NO];
}
const bool isFrameValid = win_x >= 0 && win_y >= 0 && win_w > 320 && win_h > 200;
const NSRect frameSize = isFrameValid
? NSMakeRect(win_x, win_y, win_w, win_h)
: NSMakeRect(0, 0, vid_defwidth, vid_defheight);
[m_window setFrame:frameSize display:YES];
[m_window enterFullscreenOnZoom];
[m_window exitAppOnClose];
}
void SystemFrameBuffer::SetMode(const bool fullscreen, const bool hiDPI)
{
NSOpenGLView* const glView = [m_window contentView];
[glView setWantsBestResolutionOpenGLSurface:hiDPI];
if (fullscreen)
{
SetFullscreenMode();
}
else
{
SetWindowedMode();
}
const NSSize viewSize = I_GetContentViewSize(m_window);
glViewport(0, 0, static_cast<GLsizei>(viewSize.width), static_cast<GLsizei>(viewSize.height));
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
[[NSOpenGLContext currentContext] flushBuffer];
[m_window updateTitle];
if (![m_window isKeyWindow])
{
[m_window makeKeyAndOrderFront:nil];
}
m_fullscreen = fullscreen;
m_hiDPI = hiDPI;
}
static SystemFrameBuffer* GetSystemFrameBuffer()
{
return static_cast<SystemFrameBuffer*>(screen);
}
void SystemFrameBuffer::UseHiDPI(const bool hiDPI)
{
if (auto fb = GetSystemFrameBuffer())
{
fb->SetMode(fb->m_fullscreen, hiDPI);
}
}
void SystemFrameBuffer::SetCursor(NSCursor* cursor)
{
if (auto fb = GetSystemFrameBuffer())
{
NSWindow* const window = fb->m_window;
CocoaView* const view = [window contentView];
[view setCursor:cursor];
[window invalidateCursorRectsForView:view];
}
}
void SystemFrameBuffer::SetWindowVisible(bool visible)
{
if (auto fb = GetSystemFrameBuffer())
{
if (visible)
{
[fb->m_window orderFront:nil];
}
else
{
[fb->m_window orderOut:nil];
}
I_SetNativeMouse(!visible);
}
}
void SystemFrameBuffer::SetWindowTitle(const char* title)
{
if (auto fb = GetSystemFrameBuffer())
{
NSString* const nsTitle = nullptr == title ? nil :
[NSString stringWithCString:title encoding:NSISOLatin1StringEncoding];
[fb->m_window setTitle:nsTitle];
}
}
// ---------------------------------------------------------------------------
@ -667,9 +625,9 @@ void I_SetFPSLimit(int limit)
{
}
CUSTOM_CVAR(Bool, vid_hidpi, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
CUSTOM_CVAR(Bool, vid_hidpi, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL)
{
CocoaVideo::UseHiDPI(self);
SystemFrameBuffer::UseHiDPI(self);
}
@ -730,7 +688,7 @@ bool I_SetCursor(FTexture* cursorpic)
hotSpot:NSMakePoint(0.0f, 0.0f)];
}
CocoaVideo::SetCursor(cursor);
SystemFrameBuffer::SetCursor(cursor);
[pool release];
@ -750,11 +708,11 @@ NSSize I_GetContentViewSize(const NSWindow* const window)
void I_SetMainWindowVisible(bool visible)
{
CocoaVideo::SetWindowVisible(visible);
SystemFrameBuffer::SetWindowVisible(visible);
}
// each platform has its own specific version of this function.
void I_SetWindowTitle(const char* title)
{
CocoaVideo::SetWindowTitle(title);
SystemFrameBuffer::SetWindowTitle(title);
}

View file

View file

@ -43,7 +43,6 @@
#include "c_console.h"
#include "c_dispatch.h"
#include "dikeys.h"
#include "s_sound.h"
#include "events.h"
static void I_CheckGUICapture ();
@ -57,7 +56,6 @@ extern int paused;
CVAR (Bool, use_mouse, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR (Bool, m_noprescale, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR (Bool, m_filter, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR (Bool, i_soundinbackground, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
EXTERN_CVAR (Bool, fullscreen)
@ -305,20 +303,8 @@ void MessagePump (const SDL_Event &sev)
exit (0);
case SDL_WINDOWEVENT:
switch (sev.window.event)
{
extern bool AppActive;
case SDL_WINDOWEVENT_FOCUS_GAINED:
S_SetSoundPaused(1);
AppActive = true;
break;
case SDL_WINDOWEVENT_FOCUS_LOST:
S_SetSoundPaused(i_soundinbackground);
AppActive = false;
break;
}
extern void ProcessSDLWindowEvent(const SDL_WindowEvent &);
ProcessSDLWindowEvent(sev.window);
break;
case SDL_MOUSEBUTTONDOWN:

View file

@ -169,10 +169,11 @@ static int DoomSpecificInfo (char *buffer, char *end)
}
else
{
p += snprintf (buffer+p, size-p, "\n\nviewx = %f", r_viewpoint.Pos.X);
p += snprintf (buffer+p, size-p, "\nviewy = %f", r_viewpoint.Pos.Y);
p += snprintf (buffer+p, size-p, "\nviewz = %f", r_viewpoint.Pos.Z);
p += snprintf (buffer+p, size-p, "\nviewangle = %f", r_viewpoint.Angles.Yaw.Degrees);
auto &vp = r_viewpoint;
p += snprintf (buffer+p, size-p, "\n\nviewx = %f", vp.Pos.X);
p += snprintf (buffer+p, size-p, "\nviewy = %f", vp.Pos.Y);
p += snprintf (buffer+p, size-p, "\nviewz = %f", vp.Pos.Z);
p += snprintf (buffer+p, size-p, "\nviewangle = %f", vp.Angles.Yaw.Degrees);
}
}
buffer[p++] = '\n';

View file

@ -41,6 +41,7 @@
#include "v_video.h"
#include "version.h"
#include "c_console.h"
#include "s_sound.h"
#include "videomodes.h"
#include "hardware.h"
@ -84,6 +85,13 @@ CUSTOM_CVAR(Bool, gl_es, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCA
Printf("This won't take effect until " GAMENAME " is restarted.\n");
}
CVAR(Int, win_x, -1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
CVAR(Int, win_y, -1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
CVAR(Int, win_w, -1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
CVAR(Int, win_h, -1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
CVAR(Bool, i_soundinbackground, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
CVAR (Int, vid_adapter, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
// PRIVATE DATA DEFINITIONS ------------------------------------------------
@ -111,7 +119,6 @@ SDLGLVideo::SDLGLVideo (int parm)
SDLGLVideo::~SDLGLVideo ()
{
if (GLRenderer != NULL) GLRenderer->FlushTextures();
}
DFrameBuffer *SDLGLVideo::CreateFrameBuffer ()
@ -227,6 +234,20 @@ SystemFrameBuffer::SystemFrameBuffer (void *, bool fullscreen)
m_supportsGamma = -1 != SDL_GetWindowGammaRamp(Screen,
m_origGamma[0], m_origGamma[1], m_origGamma[2]
);
if (!fullscreen)
{
if (win_x >= 0 && win_y >= 0)
{
SDL_SetWindowPosition(Screen, win_x, win_y);
}
if (win_h > 320 && win_w > 200)
{
SDL_SetWindowSize(Screen, win_w, win_h);
}
}
return;
}
@ -329,6 +350,41 @@ int SystemFrameBuffer::GetClientHeight()
}
void ProcessSDLWindowEvent(const SDL_WindowEvent &event)
{
switch (event.event)
{
extern bool AppActive;
case SDL_WINDOWEVENT_FOCUS_GAINED:
S_SetSoundPaused(1);
AppActive = true;
break;
case SDL_WINDOWEVENT_FOCUS_LOST:
S_SetSoundPaused(i_soundinbackground);
AppActive = false;
break;
case SDL_WINDOWEVENT_MOVED:
if (!fullscreen)
{
win_x = event.data1;
win_y = event.data2;
}
break;
case SDL_WINDOWEVENT_RESIZED:
if (!fullscreen)
{
win_w = event.data1;
win_h = event.data2;
}
break;
}
}
// each platform has its own specific version of this function.
void I_SetWindowTitle(const char* caption)
{

View file

@ -54,7 +54,7 @@ extern TDeletingArray<FVoxelDef *> VoxelDefs;
DeletingModelArray Models;
void FModelRenderer::RenderModel(float x, float y, float z, FSpriteModelFrame *smf, AActor *actor)
void FModelRenderer::RenderModel(float x, float y, float z, FSpriteModelFrame *smf, AActor *actor, double ticFrac)
{
// Setup transformation.
@ -128,7 +128,7 @@ void FModelRenderer::RenderModel(float x, float y, float z, FSpriteModelFrame *s
if (actor->renderflags & RF_INTERPOLATEANGLES)
{
// [Nash] use interpolated angles
DRotator Angles = actor->InterpolatedAngles(r_viewpoint.TicFrac);
DRotator Angles = actor->InterpolatedAngles(ticFrac);
angle = Angles.Yaw.Degrees;
}

View file

@ -60,7 +60,7 @@ class FModelRenderer
public:
virtual ~FModelRenderer() { }
void RenderModel(float x, float y, float z, FSpriteModelFrame *modelframe, AActor *actor);
void RenderModel(float x, float y, float z, FSpriteModelFrame *modelframe, AActor *actor, double ticFrac);
void RenderHUDModel(DPSprite *psp, float ofsx, float ofsy);
virtual ModelRendererType GetType() const = 0;

View file

@ -363,7 +363,6 @@ static void GroupSectorPortals()
FSectorPortalGroup *portal = new FSectorPortalGroup;
portal->mDisplacement = pair->Key.mDisplacement;
portal->plane = (i == 1 ? sector_t::floor : sector_t::ceiling); /**/
portal->glportal = NULL;
level.portalGroups.Push(portal);
for (unsigned j = 0; j < pair->Value.Size(); j++)
{

View file

@ -180,8 +180,6 @@ struct vertex_t
{
p.Zero();
}
angle_t GetClipAngle();
};
// Forward of LineDefs, for Sectors.

View file

@ -599,16 +599,23 @@ void R_ResetViewInterpolation ()
//==========================================================================
//
// R_SetViewAngle
// sets all values derived from the view angle.
//
//==========================================================================
void R_SetViewAngle (FRenderViewpoint &viewpoint, const FViewWindow &viewwindow)
void FRenderViewpoint::SetViewAngle (const FViewWindow &viewwindow)
{
viewpoint.Sin = viewpoint.Angles.Yaw.Sin();
viewpoint.Cos = viewpoint.Angles.Yaw.Cos();
Sin = Angles.Yaw.Sin();
Cos = Angles.Yaw.Cos();
TanSin = viewwindow.FocalTangent * Sin;
TanCos = viewwindow.FocalTangent * Cos;
DVector2 v = Angles.Yaw.ToVector();
ViewVector.X = v.X;
ViewVector.Y = v.Y;
HWAngles.Yaw = float(270.0 - Angles.Yaw.Degrees);
viewpoint.TanSin = viewwindow.FocalTangent * viewpoint.Sin;
viewpoint.TanCos = viewwindow.FocalTangent * viewpoint.Cos;
}
//==========================================================================
@ -844,7 +851,7 @@ void R_SetupFrame (FRenderViewpoint &viewpoint, FViewWindow &viewwindow, AActor
}
R_InterpolateView (viewpoint, player, viewpoint.TicFrac, iview);
R_SetViewAngle (viewpoint, viewwindow);
viewpoint.SetViewAngle (viewwindow);
interpolator.DoInterpolations (viewpoint.TicFrac);
@ -872,7 +879,7 @@ void R_SetupFrame (FRenderViewpoint &viewpoint, FViewWindow &viewwindow, AActor
FQuakeJiggers jiggers;
memset(&jiggers, 0, sizeof(jiggers));
if (DEarthquake::StaticGetQuakeIntensities(viewpoint.camera, jiggers) > 0)
if (DEarthquake::StaticGetQuakeIntensities(viewpoint.TicFrac, viewpoint.camera, jiggers) > 0)
{
double quakefactor = r_quakeintensity;
DAngle an;
@ -1009,6 +1016,35 @@ void R_SetupFrame (FRenderViewpoint &viewpoint, FViewWindow &viewwindow, AActor
screen->SetClearColor(color);
SWRenderer->SetClearColor(color);
}
else
{
screen->SetClearColor(GPalette.BlackIndex);
}
// And finally some info that is needed for the hardware renderer
// Scale the pitch to account for the pixel stretching, because the playsim doesn't know about this and treats it as 1:1.
// However, to set up a projection matrix this needs to be adjusted.
double radPitch = viewpoint.Angles.Pitch.Normalized180().Radians();
double angx = cos(radPitch);
double angy = sin(radPitch) * level.info->pixelstretch;
double alen = sqrt(angx*angx + angy*angy);
viewpoint.HWAngles.Pitch = RAD2DEG((float)asin(angy / alen));
viewpoint.HWAngles.Roll.Degrees = (float)viewpoint.Angles.Roll.Degrees; // copied for convenience.
// ViewActor only gets set, if the camera actor should not be rendered
if (actor->player && actor->player - players == consoleplayer &&
((actor->player->cheats & CF_CHASECAM) || (r_deathcamera && actor->health <= 0)) && actor == actor->player->mo)
{
viewpoint.ViewActor = nullptr;
}
else
{
viewpoint.ViewActor = actor;
}
}

View file

@ -5,6 +5,7 @@
#include "vectors.h"
class FSerializer;
struct FViewWindow;
//
// Stuff from r_main.h that's needed outside the rendering code.
@ -20,6 +21,9 @@ struct FRenderViewpoint
DVector3 Pos; // Camera position
DVector3 ActorPos; // Camera actor's position
DRotator Angles; // Camera angles
FRotator HWAngles; // Actual rotation angles for the hardware renderer
DVector2 ViewVector; // HWR only: direction the camera is facing.
AActor *ViewActor; // either the same as camera or nullptr
DVector3 Path[2]; // View path for portal calculations
double Cos; // cos(Angles.Yaw)
@ -36,6 +40,10 @@ struct FRenderViewpoint
int extralight; // extralight to be added to this viewpoint
bool showviewer; // show the camera actor?
void SetViewAngle(const FViewWindow &viewwindow);
};
extern FRenderViewpoint r_viewpoint;

File diff suppressed because it is too large Load diff

View file

@ -24,8 +24,16 @@
#ifndef ADLDATA_H
#define ADLDATA_H
#include <string.h>
#include <stdint.h>
#pragma pack(push, 1)
#define ADLDATA_BYTE_COMPARABLE(T) \
inline bool operator==(const T &a, const T &b) \
{ return !memcmp(&a, &b, sizeof(T)); } \
inline bool operator!=(const T &a, const T &b) \
{ return !operator==(a, b); }
extern const struct adldata
{
uint32_t modulator_E862, carrier_E862; // See below
@ -34,10 +42,12 @@ extern const struct adldata
int8_t finetune;
} adl[];
ADLDATA_BYTE_COMPARABLE(struct adldata)
enum { adlDefaultNumber = 189 };
extern const struct adlinsdata
{
enum { Flag_Pseudo4op = 0x01, Flag_NoSound = 0x02 };
enum { Flag_Pseudo4op = 0x01, Flag_NoSound = 0x02, Flag_Real4op = 0x04 };
uint16_t adlno1, adlno2;
uint8_t tone;
@ -46,10 +56,32 @@ extern const struct adlinsdata
uint16_t ms_sound_koff;
double voice2_fine_tune;
} adlins[];
ADLDATA_BYTE_COMPARABLE(struct adlinsdata)
int maxAdlBanks();
extern const unsigned short banks[][256];
extern const char* const banknames[];
enum { adlNoteOnMaxTime = 40000 };
/**
* @brief Instrument data with operators included
*/
struct adlinsdata2
{
adldata adl[2];
uint8_t tone;
uint8_t flags;
uint16_t ms_sound_kon; // Number of milliseconds it produces sound;
uint16_t ms_sound_koff;
double voice2_fine_tune;
adlinsdata2() {}
explicit adlinsdata2(const adlinsdata &d);
};
ADLDATA_BYTE_COMPARABLE(struct adlinsdata2)
#undef ADLDATA_BYTE_COMPARABLE
#pragma pack(pop)
/**
* @brief Bank global setup
*/
@ -62,4 +94,26 @@ extern const struct AdlBankSetup
bool scaleModulators;
} adlbanksetup[];
/**
* @brief Conversion of storage formats
*/
inline adlinsdata2::adlinsdata2(const adlinsdata &d)
: tone(d.tone), flags(d.flags),
ms_sound_kon(d.ms_sound_kon), ms_sound_koff(d.ms_sound_koff),
voice2_fine_tune(d.voice2_fine_tune)
{
adl[0] = ::adl[d.adlno1];
adl[1] = ::adl[d.adlno2];
}
/**
* @brief Convert external instrument to internal instrument
*/
void cvt_ADLI_to_FMIns(adlinsdata2 &dst, const struct ADL_Instrument &src);
/**
* @brief Convert internal instrument to external instrument
*/
void cvt_FMIns_to_ADLI(struct ADL_Instrument &dst, const adlinsdata2 &src);
#endif //ADLDATA_H

View file

@ -37,6 +37,13 @@ static ADL_Version adl_version = {
ADLMIDI_VERSION_PATCHLEVEL
};
static const ADLMIDI_AudioFormat adl_DefaultAudioFormat =
{
ADLMIDI_SampleType_S16,
sizeof(int16_t),
2 * sizeof(int16_t),
};
/*---------------------------EXPORTS---------------------------*/
ADLMIDI_EXPORT struct ADL_MIDIPlayer *adl_init(long sample_rate)
@ -67,7 +74,12 @@ ADLMIDI_EXPORT int adl_setNumChips(ADL_MIDIPlayer *device, int numCards)
return -2;
MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer);
#ifdef ADLMIDI_HW_OPL
(void)numCards;
play->m_setup.NumCards = 1;
#else
play->m_setup.NumCards = static_cast<unsigned int>(numCards);
#endif
if(play->m_setup.NumCards < 1 || play->m_setup.NumCards > MaxCards)
{
play->setErrorString("number of chips may only be 1.." MaxCards_STR ".\n");
@ -108,7 +120,7 @@ ADLMIDI_EXPORT int adl_setBank(ADL_MIDIPlayer *device, int bank)
if(static_cast<uint32_t>(bankno) >= NumBanks)
{
char errBuf[150];
snprintf(errBuf, 150, "Embedded bank number may only be 0..%u!\n", (NumBanks - 1));
snprintf(errBuf, 150, "Embedded bank number may only be 0..%u!\n", static_cast<unsigned int>(NumBanks - 1));
play->setErrorString(errBuf);
return -1;
}
@ -131,6 +143,142 @@ ADLMIDI_EXPORT const char *const *adl_getBankNames()
return banknames;
}
ADLMIDI_EXPORT int adl_reserveBanks(ADL_MIDIPlayer *device, unsigned banks)
{
if(!device)
return -1;
MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer);
OPL3::BankMap &map = play->opl.dynamic_banks;
map.reserve(banks);
return (int)map.capacity();
}
ADLMIDI_EXPORT int adl_getBank(ADL_MIDIPlayer *device, const ADL_BankId *idp, int flags, ADL_Bank *bank)
{
if(!device || !idp || !bank)
return -1;
ADL_BankId id = *idp;
if(id.lsb > 127 || id.msb > 127 || id.percussive > 1)
return -1;
unsigned idnumber = (id.msb << 8) | id.lsb | (id.percussive ? OPL3::PercussionTag : 0);
MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer);
OPL3::BankMap &map = play->opl.dynamic_banks;
OPL3::BankMap::iterator it;
if(!(flags & ADLMIDI_Bank_Create))
{
it = map.find(idnumber);
if(it == map.end())
return -1;
}
else
{
std::pair<uint16_t, OPL3::Bank> value;
value.first = idnumber;
memset(&value.second, 0, sizeof(value.second));
for (unsigned i = 0; i < 128; ++i)
value.second.ins[i].flags = adlinsdata::Flag_NoSound;
std::pair<OPL3::BankMap::iterator, bool> ir;
if(flags & ADLMIDI_Bank_CreateRt)
{
ir = map.insert(value, OPL3::BankMap::do_not_expand_t());
if(ir.first == map.end())
return -1;
}
else
ir = map.insert(value);
it = ir.first;
}
it.to_ptrs(bank->pointer);
return 0;
}
ADLMIDI_EXPORT int adl_getBankId(ADL_MIDIPlayer *device, const ADL_Bank *bank, ADL_BankId *id)
{
if(!device || !bank)
return -1;
OPL3::BankMap::iterator it = OPL3::BankMap::iterator::from_ptrs(bank->pointer);
unsigned idnumber = it->first;
id->msb = (idnumber >> 8) & 127;
id->lsb = idnumber & 127;
id->percussive = (idnumber & OPL3::PercussionTag) ? 1 : 0;
return 0;
}
ADLMIDI_EXPORT int adl_removeBank(ADL_MIDIPlayer *device, ADL_Bank *bank)
{
if(!device || !bank)
return -1;
MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer);
OPL3::BankMap &map = play->opl.dynamic_banks;
OPL3::BankMap::iterator it = OPL3::BankMap::iterator::from_ptrs(bank->pointer);
size_t size = map.size();
map.erase(it);
return (map.size() != size) ? 0 : -1;
}
ADLMIDI_EXPORT int adl_getFirstBank(ADL_MIDIPlayer *device, ADL_Bank *bank)
{
if(!device)
return -1;
MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer);
OPL3::BankMap &map = play->opl.dynamic_banks;
OPL3::BankMap::iterator it = map.begin();
if(it == map.end())
return -1;
it.to_ptrs(bank->pointer);
return 0;
}
ADLMIDI_EXPORT int adl_getNextBank(ADL_MIDIPlayer *device, ADL_Bank *bank)
{
if(!device)
return -1;
MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer);
OPL3::BankMap &map = play->opl.dynamic_banks;
OPL3::BankMap::iterator it = OPL3::BankMap::iterator::from_ptrs(bank->pointer);
if(++it == map.end())
return -1;
it.to_ptrs(bank->pointer);
return 0;
}
ADLMIDI_EXPORT int adl_getInstrument(ADL_MIDIPlayer *device, const ADL_Bank *bank, unsigned index, ADL_Instrument *ins)
{
if(!device || !bank || index > 127 || !ins)
return 1;
OPL3::BankMap::iterator it = OPL3::BankMap::iterator::from_ptrs(bank->pointer);
cvt_FMIns_to_ADLI(*ins, it->second.ins[index]);
ins->version = 0;
return 0;
}
ADLMIDI_EXPORT int adl_setInstrument(ADL_MIDIPlayer *device, ADL_Bank *bank, unsigned index, const ADL_Instrument *ins)
{
if(!device || !bank || index > 127 || !ins)
return 1;
if(ins->version != 0)
return 1;
OPL3::BankMap::iterator it = OPL3::BankMap::iterator::from_ptrs(bank->pointer);
cvt_ADLI_to_FMIns(it->second.ins[index], *ins);
return 0;
}
ADLMIDI_EXPORT int adl_setNumFourOpsChn(ADL_MIDIPlayer *device, int ops4)
{
if(!device)
@ -165,7 +313,9 @@ ADLMIDI_EXPORT void adl_setPercMode(ADL_MIDIPlayer *device, int percmod)
if(!device) return;
MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer);
play->m_setup.AdlPercussionMode = percmod;
play->opl.AdlPercussionMode = play->m_setup.AdlPercussionMode;
play->opl.AdlPercussionMode = play->m_setup.AdlPercussionMode < 0 ?
play->opl.dynamic_bank_setup.adLibPercussions :
(play->m_setup.AdlPercussionMode != 0);
play->opl.updateFlags();
}
@ -174,7 +324,9 @@ ADLMIDI_EXPORT void adl_setHVibrato(ADL_MIDIPlayer *device, int hvibro)
if(!device) return;
MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer);
play->m_setup.HighVibratoMode = hvibro;
play->opl.HighVibratoMode = play->m_setup.HighVibratoMode;
play->opl.HighVibratoMode = play->m_setup.HighVibratoMode < 0 ?
play->opl.dynamic_bank_setup.deepVibrato :
(play->m_setup.HighVibratoMode != 0);
play->opl.updateDeepFlags();
}
@ -183,7 +335,9 @@ ADLMIDI_EXPORT void adl_setHTremolo(ADL_MIDIPlayer *device, int htremo)
if(!device) return;
MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer);
play->m_setup.HighTremoloMode = htremo;
play->opl.HighTremoloMode = play->m_setup.HighTremoloMode;
play->opl.HighTremoloMode = play->m_setup.HighTremoloMode < 0 ?
play->opl.dynamic_bank_setup.deepTremolo :
(play->m_setup.HighTremoloMode != 0);
play->opl.updateDeepFlags();
}
@ -192,7 +346,16 @@ ADLMIDI_EXPORT void adl_setScaleModulators(ADL_MIDIPlayer *device, int smod)
if(!device) return;
MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer);
play->m_setup.ScaleModulators = smod;
play->opl.ScaleModulators = play->m_setup.ScaleModulators;
play->opl.ScaleModulators = play->m_setup.ScaleModulators < 0 ?
play->opl.dynamic_bank_setup.scaleModulators :
(play->m_setup.ScaleModulators != 0);
}
ADLMIDI_EXPORT void adl_setFullRangeBrightness(struct ADL_MIDIPlayer *device, int fr_brightness)
{
if(!device) return;
MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer);
play->m_setup.fullRangeBrightnessCC74 = (fr_brightness != 0);
}
ADLMIDI_EXPORT void adl_setLoopEnabled(ADL_MIDIPlayer *device, int loopEn)
@ -202,12 +365,16 @@ ADLMIDI_EXPORT void adl_setLoopEnabled(ADL_MIDIPlayer *device, int loopEn)
play->m_setup.loopingIsEnabled = (loopEn != 0);
}
/* !!!DEPRECATED!!! */
ADLMIDI_EXPORT void adl_setLogarithmicVolumes(struct ADL_MIDIPlayer *device, int logvol)
{
if(!device) return;
MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer);
play->m_setup.LogarithmicVolumes = logvol;
play->opl.LogarithmicVolumes = play->m_setup.LogarithmicVolumes;
play->m_setup.LogarithmicVolumes = (logvol != 0);
if(play->m_setup.LogarithmicVolumes)
play->opl.ChangeVolumeRangesModel(ADLMIDI_VolumeModel_NativeOPL3);
else
play->opl.ChangeVolumeRangesModel(static_cast<ADLMIDI_VolumeModels>(play->opl.m_volumeScale));
}
ADLMIDI_EXPORT void adl_setVolumeRangeModel(struct ADL_MIDIPlayer *device, int volumeModel)
@ -215,6 +382,9 @@ ADLMIDI_EXPORT void adl_setVolumeRangeModel(struct ADL_MIDIPlayer *device, int v
if(!device) return;
MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer);
play->m_setup.VolumeModel = volumeModel;
if(play->m_setup.VolumeModel == ADLMIDI_VolumeModel_AUTO)//Use bank default volume model
play->opl.m_volumeScale = (OPL3::VolumesScale)play->opl.dynamic_bank_setup.volumeModel;
else
play->opl.ChangeVolumeRangesModel(static_cast<ADLMIDI_VolumeModels>(volumeModel));
}
@ -312,16 +482,64 @@ ADLMIDI_EXPORT int adl_openData(ADL_MIDIPlayer *device, const void *mem, unsigne
ADLMIDI_EXPORT const char *adl_emulatorName()
{
#ifdef ADLMIDI_USE_DOSBOX_OPL
return "DosBox";
#else
return "Nuked";
#endif
return "<adl_emulatorName() is deprecated! Use adl_chipEmulatorName() instead!>";
}
ADLMIDI_EXPORT const char *adl_chipEmulatorName(struct ADL_MIDIPlayer *device)
{
if(device)
{
#ifndef ADLMIDI_HW_OPL
MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer);
if(play && !play->opl.cardsOP2.empty())
return play->opl.cardsOP2[0]->emulatorName();
#else
return "Hardware OPL3 chip on 0x330";
#endif
}
return "Unknown";
}
ADLMIDI_EXPORT int adl_switchEmulator(struct ADL_MIDIPlayer *device, int emulator)
{
if(device)
{
MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer);
if(play && (emulator >= 0) && (emulator < ADLMIDI_EMU_end))
{
play->m_setup.emulator = emulator;
adl_reset(device);
return 0;
}
play->setErrorString("OPL3 MIDI: Unknown emulation core!");
}
return -1;
}
ADLMIDI_EXPORT int adl_setRunAtPcmRate(ADL_MIDIPlayer *device, int enabled)
{
if(device)
{
MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer);
if(play)
{
play->m_setup.runAtPcmRate = (enabled != 0);
adl_reset(device);
return 0;
}
}
return -1;
}
ADLMIDI_EXPORT const char *adl_linkedLibraryVersion()
{
#if !defined(ADLMIDI_ENABLE_HQ_RESAMPLER)
return ADLMIDI_VERSION;
#else
return ADLMIDI_VERSION " (HQ)";
#endif
}
ADLMIDI_EXPORT const ADL_Version *adl_linkedVersion()
@ -373,9 +591,10 @@ ADLMIDI_EXPORT void adl_reset(struct ADL_MIDIPlayer *device)
return;
MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer);
play->m_setup.tick_skip_samples_delay = 0;
play->opl.Reset(play->m_setup.PCM_RATE);
play->opl.runAtPcmRate = play->m_setup.runAtPcmRate;
play->opl.Reset(play->m_setup.emulator, play->m_setup.PCM_RATE, play);
play->ch.clear();
play->ch.resize(play->opl.NumChannels);
play->ch.resize((size_t)play->opl.NumChannels);
}
ADLMIDI_EXPORT double adl_totalTimeLength(struct ADL_MIDIPlayer *device)
@ -568,31 +787,156 @@ ADLMIDI_EXPORT void adl_setDebugMessageHook(struct ADL_MIDIPlayer *device, ADL_D
play->hooks.onDebugMessage_userData = userData;
}
inline static void SendStereoAudio(int &samples_requested,
ssize_t &in_size,
short *_in,
ssize_t out_pos,
short *_out)
#ifndef ADLMIDI_HW_OPL
template <class Dst>
static void CopySamplesRaw(ADL_UInt8 *dstLeft, ADL_UInt8 *dstRight, const int32_t *src,
size_t frameCount, unsigned sampleOffset)
{
if(!in_size)
return;
size_t offset = static_cast<size_t>(out_pos);
size_t inSamples = static_cast<size_t>(in_size * 2);
size_t maxSamples = static_cast<size_t>(samples_requested) - offset;
size_t toCopy = std::min(maxSamples, inSamples);
std::memcpy(_out + out_pos, _in, toCopy * sizeof(short));
for(size_t i = 0; i < frameCount; ++i) {
*(Dst *)(dstLeft + (i * sampleOffset)) = src[2 * i];
*(Dst *)(dstRight + (i * sampleOffset)) = src[(2 * i) + 1];
}
}
template <class Dst, class Ret>
static void CopySamplesTransformed(ADL_UInt8 *dstLeft, ADL_UInt8 *dstRight, const int32_t *src,
size_t frameCount, unsigned sampleOffset,
Ret(&transform)(int32_t))
{
for(size_t i = 0; i < frameCount; ++i) {
*(Dst *)(dstLeft + (i * sampleOffset)) = (Dst)transform(src[2 * i]);
*(Dst *)(dstRight + (i * sampleOffset)) = (Dst)transform(src[(2 * i) + 1]);
}
}
ADLMIDI_EXPORT int adl_play(ADL_MIDIPlayer *device, int sampleCount, short *out)
static int SendStereoAudio(int samples_requested,
ssize_t in_size,
int32_t *_in,
ssize_t out_pos,
ADL_UInt8 *left,
ADL_UInt8 *right,
const ADLMIDI_AudioFormat *format)
{
if(!in_size)
return 0;
size_t outputOffset = static_cast<size_t>(out_pos);
size_t inSamples = static_cast<size_t>(in_size * 2);
size_t maxSamples = static_cast<size_t>(samples_requested) - outputOffset;
size_t toCopy = std::min(maxSamples, inSamples);
ADLMIDI_SampleType sampleType = format->type;
const unsigned containerSize = format->containerSize;
const unsigned sampleOffset = format->sampleOffset;
left += (outputOffset / 2) * sampleOffset;
right += (outputOffset / 2) * sampleOffset;
typedef int32_t(&pfnConvert)(int32_t);
typedef float(&ffnConvert)(int32_t);
typedef double(&dfnConvert)(int32_t);
switch(sampleType) {
case ADLMIDI_SampleType_S8:
case ADLMIDI_SampleType_U8:
{
pfnConvert cvt = (sampleType == ADLMIDI_SampleType_S8) ? adl_cvtS8 : adl_cvtU8;
switch(containerSize) {
case sizeof(int8_t):
CopySamplesTransformed<int8_t>(left, right, _in, toCopy / 2, sampleOffset, cvt);
break;
case sizeof(int16_t):
CopySamplesTransformed<int16_t>(left, right, _in, toCopy / 2, sampleOffset, cvt);
break;
case sizeof(int32_t):
CopySamplesTransformed<int32_t>(left, right, _in, toCopy / 2, sampleOffset, cvt);
break;
default:
return -1;
}
break;
}
case ADLMIDI_SampleType_S16:
case ADLMIDI_SampleType_U16:
{
pfnConvert cvt = (sampleType == ADLMIDI_SampleType_S16) ? adl_cvtS16 : adl_cvtU16;
switch(containerSize) {
case sizeof(int16_t):
CopySamplesTransformed<int16_t>(left, right, _in, toCopy / 2, sampleOffset, cvt);
break;
case sizeof(int32_t):
CopySamplesRaw<int32_t>(left, right, _in, toCopy / 2, sampleOffset);
break;
default:
return -1;
}
break;
}
case ADLMIDI_SampleType_S24:
case ADLMIDI_SampleType_U24:
{
pfnConvert cvt = (sampleType == ADLMIDI_SampleType_S24) ? adl_cvtS24 : adl_cvtU24;
switch(containerSize) {
case sizeof(int32_t):
CopySamplesTransformed<int32_t>(left, right, _in, toCopy / 2, sampleOffset, cvt);
break;
default:
return -1;
}
break;
}
case ADLMIDI_SampleType_S32:
case ADLMIDI_SampleType_U32:
{
pfnConvert cvt = (sampleType == ADLMIDI_SampleType_S32) ? adl_cvtS32 : adl_cvtU32;
switch(containerSize) {
case sizeof(int32_t):
CopySamplesTransformed<int32_t>(left, right, _in, toCopy / 2, sampleOffset, cvt);
break;
default:
return -1;
}
break;
}
case ADLMIDI_SampleType_F32:
{
if(containerSize != sizeof(float))
return -1;
ffnConvert cvt = adl_cvtReal<float>;
CopySamplesTransformed<float>(left, right, _in, toCopy / 2, sampleOffset, cvt);
break;
}
case ADLMIDI_SampleType_F64:
{
if(containerSize != sizeof(double))
return -1;
dfnConvert cvt = adl_cvtReal<double>;
CopySamplesTransformed<double>(left, right, _in, toCopy / 2, sampleOffset, cvt);
break;
}
default:
return -1;
}
return 0;
}
#endif
ADLMIDI_EXPORT int adl_play(struct ADL_MIDIPlayer *device, int sampleCount, short *out)
{
return adl_playFormat(device, sampleCount, (ADL_UInt8 *)out, (ADL_UInt8 *)(out + 1), &adl_DefaultAudioFormat);
}
ADLMIDI_EXPORT int adl_playFormat(ADL_MIDIPlayer *device, int sampleCount,
ADL_UInt8 *out_left, ADL_UInt8 *out_right,
const ADLMIDI_AudioFormat *format)
{
#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER
#ifdef ADLMIDI_HW_OPL
(void)device;
(void)sampleCount;
(void)out;
(void)out_left;
(void)out_right;
(void)format;
return 0;
#else
sampleCount -= sampleCount % 2; //Avoid even sample requests
@ -646,33 +990,23 @@ ADLMIDI_EXPORT int adl_play(ADL_MIDIPlayer *device, int sampleCount, short *out)
ssize_t in_generatedPhys = in_generatedStereo * 2;
//! Unsigned total sample count
//fill buffer with zeros
int16_t *out_buf = player->outBuf;
std::memset(out_buf, 0, static_cast<size_t>(in_generatedPhys) * sizeof(int16_t));
int32_t *out_buf = player->outBuf;
std::memset(out_buf, 0, static_cast<size_t>(in_generatedPhys) * sizeof(out_buf[0]));
unsigned int chips = player->opl.NumCards;
if(chips == 1)
{
#ifdef ADLMIDI_USE_DOSBOX_OPL
player->opl.cards[0].GenerateArr(out_buf, &in_generatedStereo);
in_generatedPhys = in_generatedStereo * 2;
#else
OPL3_GenerateStream(&player->opl.cards[0], out_buf, static_cast<Bit32u>(in_generatedStereo));
#endif
player->opl.cardsOP2[0]->generate32(out_buf, (size_t)in_generatedStereo);
}
else if(n_periodCountStereo > 0)
{
/* Generate data from every chip and mix result */
for(unsigned card = 0; card < chips; ++card)
{
#ifdef ADLMIDI_USE_DOSBOX_OPL
player->opl.cards[card].GenerateArrMix(out_buf, &in_generatedStereo);
in_generatedPhys = in_generatedStereo * 2;
#else
OPL3_GenerateStreamMix(&player->opl.cards[card], out_buf, static_cast<Bit32u>(in_generatedStereo));
#endif
}
for(size_t card = 0; card < chips; ++card)
player->opl.cardsOP2[card]->generateAndMix32(out_buf, (size_t)in_generatedStereo);
}
/* Process it */
SendStereoAudio(sampleCount, in_generatedStereo, out_buf, gotten_len, out);
if(SendStereoAudio(sampleCount, in_generatedStereo, out_buf, gotten_len, out_left, out_right, format) == -1)
return 0;
left -= (int)in_generatedPhys;
gotten_len += (in_generatedPhys) /* - setup.stored_samples*/;
@ -698,11 +1032,20 @@ ADLMIDI_EXPORT int adl_play(ADL_MIDIPlayer *device, int sampleCount, short *out)
ADLMIDI_EXPORT int adl_generate(struct ADL_MIDIPlayer *device, int sampleCount, short *out)
{
return adl_generateFormat(device, sampleCount, (ADL_UInt8 *)out, (ADL_UInt8 *)(out + 1), &adl_DefaultAudioFormat);
}
ADLMIDI_EXPORT int adl_generateFormat(struct ADL_MIDIPlayer *device, int sampleCount,
ADL_UInt8 *out_left, ADL_UInt8 *out_right,
const ADLMIDI_AudioFormat *format)
{
#ifdef ADLMIDI_HW_OPL
(void)device;
(void)sampleCount;
(void)out;
(void)out_left;
(void)out_right;
(void)format;
return 0;
#else
sampleCount -= sampleCount % 2; //Avoid even sample requests
@ -739,33 +1082,20 @@ ADLMIDI_EXPORT int adl_generate(struct ADL_MIDIPlayer *device, int sampleCount,
ssize_t in_generatedPhys = in_generatedStereo * 2;
//! Unsigned total sample count
//fill buffer with zeros
int16_t *out_buf = player->outBuf;
std::memset(out_buf, 0, static_cast<size_t>(in_generatedPhys) * sizeof(int16_t));
int32_t *out_buf = player->outBuf;
std::memset(out_buf, 0, static_cast<size_t>(in_generatedPhys) * sizeof(out_buf[0]));
unsigned int chips = player->opl.NumCards;
if(chips == 1)
{
#ifdef ADLMIDI_USE_DOSBOX_OPL
player->opl.cards[0].GenerateArr(out_buf, &in_generatedStereo);
in_generatedPhys = in_generatedStereo * 2;
#else
OPL3_GenerateStream(&player->opl.cards[0], out_buf, static_cast<Bit32u>(in_generatedStereo));
#endif
}
player->opl.cardsOP2[0]->generate32(out_buf, (size_t)in_generatedStereo);
else if(n_periodCountStereo > 0)
{
/* Generate data from every chip and mix result */
for(unsigned card = 0; card < chips; ++card)
{
#ifdef ADLMIDI_USE_DOSBOX_OPL
player->opl.cards[card].GenerateArrMix(out_buf, &in_generatedStereo);
in_generatedPhys = in_generatedStereo * 2;
#else
OPL3_GenerateStreamMix(&player->opl.cards[card], out_buf, static_cast<Bit32u>(in_generatedStereo));
#endif
}
player->opl.cardsOP2[card]->generateAndMix32(out_buf, (size_t)in_generatedStereo);
}
/* Process it */
SendStereoAudio(sampleCount, in_generatedStereo, out_buf, gotten_len, out);
if(SendStereoAudio(sampleCount, in_generatedStereo, out_buf, gotten_len, out_left, out_right, format) == -1)
return 0;
left -= (int)in_generatedPhys;
gotten_len += (in_generatedPhys) /* - setup.stored_samples*/;

View file

@ -30,13 +30,14 @@ extern "C" {
#define ADLMIDI_VERSION_MAJOR 1
#define ADLMIDI_VERSION_MINOR 3
#define ADLMIDI_VERSION_PATCHLEVEL 2
#define ADLMIDI_VERSION_PATCHLEVEL 3
#define ADLMIDI_TOSTR(s) #s
#define ADLMIDI_TOSTR_I(s) #s
#define ADLMIDI_TOSTR(s) ADLMIDI_TOSTR_I(s)
#define ADLMIDI_VERSION \
ADLMIDI_TOSTR(OPNMIDI_VERSION_MAJOR) "." \
ADLMIDI_TOSTR(OPNMIDI_VERSION_MINOR) "." \
ADLMIDI_TOSTR(OPNMIDI_VERSION_PATCHLEVEL)
ADLMIDI_TOSTR(ADLMIDI_VERSION_MAJOR) "." \
ADLMIDI_TOSTR(ADLMIDI_VERSION_MINOR) "." \
ADLMIDI_TOSTR(ADLMIDI_VERSION_PATCHLEVEL)
#include <stddef.h>
@ -57,11 +58,34 @@ typedef short ADL_SInt16;
enum ADLMIDI_VolumeModels
{
ADLMIDI_VolumeModel_AUTO = 0,
ADLMIDI_VolumeModel_Generic,
ADLMIDI_VolumeModel_CMF,
ADLMIDI_VolumeModel_DMX,
ADLMIDI_VolumeModel_APOGEE,
ADLMIDI_VolumeModel_9X
ADLMIDI_VolumeModel_Generic = 1,
ADLMIDI_VolumeModel_NativeOPL3 = 2,
ADLMIDI_VolumeModel_CMF = ADLMIDI_VolumeModel_NativeOPL3,
ADLMIDI_VolumeModel_DMX = 3,
ADLMIDI_VolumeModel_APOGEE = 4,
ADLMIDI_VolumeModel_9X = 5
};
enum ADLMIDI_SampleType
{
ADLMIDI_SampleType_S16 = 0, /* signed PCM 16-bit */
ADLMIDI_SampleType_S8, /* signed PCM 8-bit */
ADLMIDI_SampleType_F32, /* float 32-bit */
ADLMIDI_SampleType_F64, /* float 64-bit */
ADLMIDI_SampleType_S24, /* signed PCM 24-bit */
ADLMIDI_SampleType_S32, /* signed PCM 32-bit */
ADLMIDI_SampleType_U8, /* unsigned PCM 8-bit */
ADLMIDI_SampleType_U16, /* unsigned PCM 16-bit */
ADLMIDI_SampleType_U24, /* unsigned PCM 24-bit */
ADLMIDI_SampleType_U32, /* unsigned PCM 32-bit */
ADLMIDI_SampleType_Count,
};
struct ADLMIDI_AudioFormat
{
enum ADLMIDI_SampleType type; /* type of sample */
unsigned containerSize; /* size in bytes of the storage type */
unsigned sampleOffset; /* distance in bytes between consecutive samples */
};
struct ADL_MIDIPlayer
@ -87,6 +111,46 @@ extern int adl_getBanksCount();
/* Returns pointer to array of names of every bank */
extern const char *const *adl_getBankNames();
/* Reference to dynamic bank */
typedef struct ADL_Bank
{
void *pointer[3];
} ADL_Bank;
/* Identifier of dynamic bank */
typedef struct ADL_BankId
{
ADL_UInt8 percussive, msb, lsb;
} ADL_BankId;
/* Flags for dynamic bank access */
enum ADL_BankAccessFlags
{
ADLMIDI_Bank_Create = 1, /* create bank, allocating memory as needed */
ADLMIDI_Bank_CreateRt = 1|2, /* create bank, never allocating memory */
};
typedef struct ADL_Instrument ADL_Instrument;
#if defined(ADLMIDI_UNSTABLE_API)
/* Preallocates a minimum number of bank slots. Returns the actual capacity. */
extern int adl_reserveBanks(struct ADL_MIDIPlayer *device, unsigned banks);
/* Gets the bank designated by the identifier, optionally creating if it does not exist. */
extern int adl_getBank(struct ADL_MIDIPlayer *device, const ADL_BankId *id, int flags, ADL_Bank *bank);
/* Gets the identifier of a bank. */
extern int adl_getBankId(struct ADL_MIDIPlayer *device, const ADL_Bank *bank, ADL_BankId *id);
/* Removes a bank. */
extern int adl_removeBank(struct ADL_MIDIPlayer *device, ADL_Bank *bank);
/* Gets the first bank. */
extern int adl_getFirstBank(struct ADL_MIDIPlayer *device, ADL_Bank *bank);
/* Iterates to the next bank. */
extern int adl_getNextBank(struct ADL_MIDIPlayer *device, ADL_Bank *bank);
/* Gets the nth intrument in the bank [0..127]. */
extern int adl_getInstrument(struct ADL_MIDIPlayer *device, const ADL_Bank *bank, unsigned index, ADL_Instrument *ins);
/* Sets the nth intrument in the bank [0..127]. */
extern int adl_setInstrument(struct ADL_MIDIPlayer *device, ADL_Bank *bank, unsigned index, const ADL_Instrument *ins);
#endif /* defined(ADLMIDI_UNSTABLE_API) */
/*Sets number of 4-operator channels between all chips.
By default, it is automatically re-calculating every bank change.
If you want to specify custom number of four operator channels,
@ -109,10 +173,16 @@ extern void adl_setHTremolo(struct ADL_MIDIPlayer *device, int htremo);
/*Override Enable(1) or Disable(0) scaling of modulator volumes. -1 - use bank default scaling of modulator volumes*/
extern void adl_setScaleModulators(struct ADL_MIDIPlayer *device, int smod);
/*Enable(1) or Disable(0) full-range brightness (MIDI CC74 used in XG music to filter result sounding) scaling.
By default, brightness affects sound between 0 and 64.
When this option is enabled, the range will use a full range from 0 up to 127.
*/
extern void adl_setFullRangeBrightness(struct ADL_MIDIPlayer *device, int fr_brightness);
/*Enable or disable built-in loop (built-in loop supports 'loopStart' and 'loopEnd' tags to loop specific part)*/
extern void adl_setLoopEnabled(struct ADL_MIDIPlayer *device, int loopEn);
/*Enable or disable Logariphmic volume changer */
/* !!!DEPRECATED!!! Enable or disable Logariphmic volume changer */
extern void adl_setLogarithmicVolumes(struct ADL_MIDIPlayer *device, int logvol);
/*Set different volume range model */
@ -125,15 +195,33 @@ extern int adl_openBankFile(struct ADL_MIDIPlayer *device, const char *filePath)
extern int adl_openBankData(struct ADL_MIDIPlayer *device, const void *mem, unsigned long size);
/*Returns name of currently used OPL3 emulator*/
/* DEPRECATED */
extern const char *adl_emulatorName();
/*Returns chip emulator name string*/
extern const char *adl_chipEmulatorName(struct ADL_MIDIPlayer *device);
enum ADL_Emulator
{
ADLMIDI_EMU_NUKED = 0,
ADLMIDI_EMU_NUKED_174,
ADLMIDI_EMU_DOSBOX,
ADLMIDI_EMU_end
};
/* Switch the emulation core */
extern int adl_switchEmulator(struct ADL_MIDIPlayer *device, int emulator);
typedef struct {
ADL_UInt16 major;
ADL_UInt16 minor;
ADL_UInt16 patch;
} ADL_Version;
/*Run emulator with PCM rate to reduce CPU usage on slow devices. May decrease sounding accuracy.*/
extern int adl_setRunAtPcmRate(struct ADL_MIDIPlayer *device, int enabled);
/*Returns string which contains a version number*/
extern const char *adl_linkedLibraryVersion();
@ -213,11 +301,17 @@ extern struct Adl_MarkerEntry adl_metaMarker(struct ADL_MIDIPlayer *device, size
/*Take a sample buffer and iterate MIDI timers */
extern int adl_play(struct ADL_MIDIPlayer *device, int sampleCount, short out[]);
extern int adl_play(struct ADL_MIDIPlayer *device, int sampleCount, short *out);
/*Take a sample buffer and iterate MIDI timers */
extern int adl_playFormat(struct ADL_MIDIPlayer *device, int sampleCount, ADL_UInt8 *left, ADL_UInt8 *right, const struct ADLMIDI_AudioFormat *format);
/*Generate audio output from chip emulators without iteration of MIDI timers.*/
extern int adl_generate(struct ADL_MIDIPlayer *device, int sampleCount, short *out);
/*Generate audio output from chip emulators without iteration of MIDI timers.*/
extern int adl_generateFormat(struct ADL_MIDIPlayer *device, int sampleCount, ADL_UInt8 *left, ADL_UInt8 *right, const struct ADLMIDI_AudioFormat *format);
/**
* @brief Periodic tick handler.
* @param device
@ -286,6 +380,70 @@ extern void adl_setNoteHook(struct ADL_MIDIPlayer *device, ADL_NoteHook noteHook
/* Set debug message hook */
extern void adl_setDebugMessageHook(struct ADL_MIDIPlayer *device, ADL_DebugMessageHook debugMessageHook, void *userData);
/**Instrument structures**/
enum
{
ADLMIDI_InstrumentVersion = 0,
};
typedef enum ADL_InstrumentFlags
{
/* Is two-operator single-voice instrument (no flags) */
ADLMIDI_Ins_2op = 0x00,
/* Is true four-operator instrument */
ADLMIDI_Ins_4op = 0x01,
/* Is pseudo four-operator (two 2-operator voices) instrument */
ADLMIDI_Ins_Pseudo4op = 0x02,
/* Is a blank instrument entry */
ADLMIDI_Ins_IsBlank = 0x04,
/* Mask of the flags range */
ADLMIDI_Ins_ALL_MASK = 0x07,
} ADL_InstrumentFlags;
typedef struct ADL_Operator
{
/* AM/Vib/Env/Ksr/FMult characteristics */
ADL_UInt8 avekf_20;
/* Key Scale Level / Total level register data */
ADL_UInt8 ksl_l_40;
/* Attack / Decay */
ADL_UInt8 atdec_60;
/* Systain and Release register data */
ADL_UInt8 susrel_80;
/* Wave form */
ADL_UInt8 waveform_E0;
} ADL_Operator;
typedef struct ADL_Instrument
{
/* Version of the instrument object */
int version;
/* MIDI note key (half-tone) offset for an instrument (or a first voice in pseudo-4-op mode) */
ADL_SInt16 note_offset1;
/* MIDI note key (half-tone) offset for a second voice in pseudo-4-op mode */
ADL_SInt16 note_offset2;
/* MIDI note velocity offset (taken from Apogee TMB format) */
ADL_SInt8 midi_velocity_offset;
/* Second voice detune level (taken from DMX OP2) */
ADL_SInt8 second_voice_detune;
/* Percussion MIDI base tone number at which this drum will be played */
ADL_UInt8 percussion_key_number;
/* Enum ADL_InstrumentFlags */
ADL_UInt8 inst_flags;
/* Feedback&Connection register for first and second operators */
ADL_UInt8 fb_conn1_C0;
/* Feedback&Connection register for third and fourth operators */
ADL_UInt8 fb_conn2_C0;
/* Operators register data */
ADL_Operator operators[4];
/* Millisecond delay of sounding while key is on */
ADL_UInt16 delay_on_ms;
/* Millisecond delay of sounding after key off */
ADL_UInt16 delay_off_ms;
} ADL_Instrument;
#ifdef __cplusplus
}
#endif

View file

@ -24,24 +24,15 @@
#ifndef ADLMIDI_HPP
#define ADLMIDI_HPP
#include "adlmidi.h"
#include <stdint.h>
#include <vector>
class OPL3;
class MIDIplay;
struct ADL_MIDIPlayer;
class AdlInstrumentTester
{
uint32_t cur_gm;
uint32_t ins_idx;
std::vector<uint32_t> adl_ins_list;
OPL3 *opl;
MIDIplay * play;
struct Impl;
Impl *P;
public:
AdlInstrumentTester(ADL_MIDIPlayer *device);
explicit AdlInstrumentTester(ADL_MIDIPlayer *device);
virtual ~AdlInstrumentTester();
// Find list of adlib instruments that supposedly implement this GM
@ -51,6 +42,10 @@ public:
void NextGM(int offset);
void NextAdl(int offset);
bool HandleInputChar(char ch);
private:
AdlInstrumentTester(const AdlInstrumentTester &);
AdlInstrumentTester &operator=(const AdlInstrumentTester &);
};
#endif //ADLMIDI_HPP

View file

@ -0,0 +1,127 @@
/*
* libADLMIDI is a free MIDI to WAV conversion library with OPL3 emulation
*
* Original ADLMIDI code: Copyright (c) 2010-2014 Joel Yliluoma <bisqwit@iki.fi>
* ADLMIDI Library API: Copyright (c) 2015-2018 Vitaly Novichkov <admin@wohlnet.ru>
*
* Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation:
* http://iki.fi/bisqwit/source/adlmidi.html
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ADLMIDI_BANKMAP_H
#define ADLMIDI_BANKMAP_H
#include <list>
#include <utility>
#include <stdint.h>
#include <stddef.h>
#include "adlmidi_ptr.hpp"
/**
* A simple hash map which accepts bank numbers as keys, can be reserved to a
* fixed size, offers O(1) search and insertion, has a hash function to
* optimize for the worst case, and has some good cache locality properties.
*/
template <class T>
class BasicBankMap
{
public:
typedef uint16_t key_type; /* the bank identifier */
typedef T mapped_type;
typedef std::pair<key_type, T> value_type;
BasicBankMap();
void reserve(size_t capacity);
size_t size() const
{ return m_size; }
size_t capacity() const
{ return m_capacity; }
bool empty() const
{ return m_size == 0; }
class iterator;
iterator begin() const;
iterator end() const;
struct do_not_expand_t {};
iterator find(key_type key);
void erase(iterator it);
std::pair<iterator, bool> insert(const value_type &value);
std::pair<iterator, bool> insert(const value_type &value, do_not_expand_t);
void clear();
T &operator[](key_type key);
private:
struct Slot;
enum { minimum_allocation = 4 };
enum
{
hash_bits = 8, /* worst case # of collisions: 128^2/2^hash_bits */
hash_buckets = 1 << hash_bits,
};
public:
class iterator
{
public:
iterator();
value_type &operator*() const { return slot->value; }
value_type *operator->() const { return &slot->value; }
iterator &operator++();
bool operator==(const iterator &o) const;
bool operator!=(const iterator &o) const;
void to_ptrs(void *ptrs[3]);
static iterator from_ptrs(void *const ptrs[3]);
private:
Slot **buckets;
Slot *slot;
size_t index;
iterator(Slot **buckets, Slot *slot, size_t index);
#ifdef _MSC_VER
template<class _T>
friend class BasicBankMap;
#else
friend class BasicBankMap<T>;
#endif
};
private:
struct Slot {
Slot *next, *prev;
value_type value;
Slot() : next(NULL), prev(NULL) {}
};
AdlMIDI_SPtrArray<Slot *> m_buckets;
std::list< AdlMIDI_SPtrArray<Slot> > m_allocations;
Slot *m_freeslots;
size_t m_size;
size_t m_capacity;
static size_t hash(key_type key);
Slot *allocate_slot();
Slot *ensure_allocate_slot();
void free_slot(Slot *slot);
Slot *bucket_find(size_t index, key_type key);
void bucket_add(size_t index, Slot *slot);
void bucket_remove(size_t index, Slot *slot);
};
#include "adlmidi_bankmap.tcc"
#endif // ADLMIDI_BANKMAP_H

View file

@ -0,0 +1,283 @@
/*
* libADLMIDI is a free MIDI to WAV conversion library with OPL3 emulation
*
* Original ADLMIDI code: Copyright (c) 2010-2014 Joel Yliluoma <bisqwit@iki.fi>
* ADLMIDI Library API: Copyright (c) 2015-2018 Vitaly Novichkov <admin@wohlnet.ru>
*
* Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation:
* http://iki.fi/bisqwit/source/adlmidi.html
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "adlmidi_bankmap.h"
#include <cassert>
template <class T>
inline BasicBankMap<T>::BasicBankMap()
: m_freeslots(NULL),
m_size(0),
m_capacity(0)
{
m_buckets.reset(new Slot *[hash_buckets]());
}
template <class T>
inline size_t BasicBankMap<T>::hash(key_type key)
{
// disregard the 0 high bit in LSB
key = (key & 127) | ((key >> 8) << 7);
// take low part as hash value
return key & (hash_buckets - 1);
}
template <class T>
void BasicBankMap<T>::reserve(size_t capacity)
{
if(m_capacity >= capacity)
return;
size_t need = capacity - m_capacity;
const size_t minalloc = static_cast<size_t>(minimum_allocation);
need = (need < minalloc) ? minalloc : need;
AdlMIDI_SPtrArray<Slot> slotz;
slotz.reset(new Slot[need]);
m_allocations.push_back(slotz);
m_capacity += need;
for(size_t i = need; i-- > 0;)
free_slot(&slotz[i]);
}
template <class T>
typename BasicBankMap<T>::iterator
BasicBankMap<T>::begin() const
{
iterator it(m_buckets.get(), NULL, 0);
while(it.index < hash_buckets && !(it.slot = m_buckets[it.index]))
++it.index;
return it;
}
template <class T>
typename BasicBankMap<T>::iterator
BasicBankMap<T>::end() const
{
iterator it(m_buckets.get(), NULL, hash_buckets);
return it;
}
template <class T>
typename BasicBankMap<T>::iterator BasicBankMap<T>::find(key_type key)
{
size_t index = hash(key);
Slot *slot = bucket_find(index, key);
if(!slot)
return end();
return iterator(m_buckets.get(), slot, index);
}
template <class T>
void BasicBankMap<T>::erase(iterator it)
{
bucket_remove(it.index, it.slot);
free_slot(it.slot);
--m_size;
}
template <class T>
inline BasicBankMap<T>::iterator::iterator()
: buckets(NULL), slot(NULL), index(0)
{
}
template <class T>
inline BasicBankMap<T>::iterator::iterator(Slot **buckets, Slot *slot, size_t index)
: buckets(buckets), slot(slot), index(index)
{
}
template <class T>
typename BasicBankMap<T>::iterator &
BasicBankMap<T>::iterator::operator++()
{
if(slot->next)
slot = slot->next;
else {
Slot *slot = NULL;
++index;
while(index < hash_buckets && !(slot = buckets[index]))
++index;
this->slot = slot;
}
return *this;
}
template <class T>
bool BasicBankMap<T>::iterator::operator==(const iterator &o) const
{
return buckets == o.buckets && slot == o.slot && index == o.index;
}
template <class T>
inline bool BasicBankMap<T>::iterator::operator!=(const iterator &o) const
{
return !operator==(o);
}
template <class T>
void BasicBankMap<T>::iterator::to_ptrs(void *ptrs[3])
{
ptrs[0] = buckets;
ptrs[1] = slot;
ptrs[2] = (void *)index;
}
template <class T>
typename BasicBankMap<T>::iterator
BasicBankMap<T>::iterator::from_ptrs(void *const ptrs[3])
{
iterator it;
it.buckets = (Slot **)ptrs[0];
it.slot = (Slot *)ptrs[1];
it.index = (size_t)ptrs[2];
return it;
}
template <class T>
std::pair<typename BasicBankMap<T>::iterator, bool>
BasicBankMap<T>::insert(const value_type &value)
{
size_t index = hash(value.first);
Slot *slot = bucket_find(index, value.first);
if(slot)
return std::make_pair(iterator(m_buckets.get(), slot, index), false);
slot = allocate_slot();
if(!slot) {
reserve(m_capacity + minimum_allocation);
slot = ensure_allocate_slot();
}
slot->value = value;
bucket_add(index, slot);
++m_size;
return std::make_pair(iterator(m_buckets.get(), slot, index), true);
}
template <class T>
std::pair<typename BasicBankMap<T>::iterator, bool>
BasicBankMap<T>::insert(const value_type &value, do_not_expand_t)
{
size_t index = hash(value.first);
Slot *slot = bucket_find(index, value.first);
if(slot)
return std::make_pair(iterator(m_buckets.get(), slot, index), false);
slot = allocate_slot();
if(!slot)
return std::make_pair(end(), false);
slot->value = value;
bucket_add(index, slot);
++m_size;
return std::make_pair(iterator(m_buckets.get(), slot, index), true);
}
template <class T>
void BasicBankMap<T>::clear()
{
for(size_t i = 0; i < hash_buckets; ++i) {
Slot *slot = m_buckets[i];
while (Slot *cur = slot) {
slot = slot->next;
free_slot(cur);
}
m_buckets[i] = NULL;
}
m_size = 0;
}
template <class T>
inline T &BasicBankMap<T>::operator[](key_type key)
{
return insert(value_type(key, T())).first->second;
}
template <class T>
typename BasicBankMap<T>::Slot *
BasicBankMap<T>::allocate_slot()
{
Slot *slot = m_freeslots;
if(!slot)
return NULL;
Slot *next = slot->next;
if(next)
next->prev = NULL;
m_freeslots = next;
return slot;
}
template <class T>
inline typename BasicBankMap<T>::Slot *
BasicBankMap<T>::ensure_allocate_slot()
{
Slot *slot = allocate_slot();
assert(slot);
return slot;
}
template <class T>
void BasicBankMap<T>::free_slot(Slot *slot)
{
Slot *next = m_freeslots;
if(next)
next->prev = slot;
slot->prev = NULL;
slot->next = next;
m_freeslots = slot;
m_freeslots->value.second = T();
}
template <class T>
typename BasicBankMap<T>::Slot *
BasicBankMap<T>::bucket_find(size_t index, key_type key)
{
Slot *slot = m_buckets[index];
while(slot && slot->value.first != key)
slot = slot->next;
return slot;
}
template <class T>
void BasicBankMap<T>::bucket_add(size_t index, Slot *slot)
{
assert(slot);
Slot *next = m_buckets[index];
if(next)
next->prev = slot;
slot->next = next;
m_buckets[index] = slot;
}
template <class T>
void BasicBankMap<T>::bucket_remove(size_t index, Slot *slot)
{
assert(slot);
Slot *prev = slot->prev;
Slot *next = slot->next;
if(!prev)
m_buckets[index] = next;
else
prev->next = next;
if(next)
next->prev = prev;
}

View file

@ -22,6 +22,7 @@
*/
#include "adlmidi_private.hpp"
#include "wopl/wopl_file.h"
#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER
# ifndef ADLMIDI_DISABLE_MUS_SUPPORT
@ -32,6 +33,7 @@
# endif//XMI
#endif //ADLMIDI_DISABLE_MIDI_SEQUENCER
#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER
uint64_t MIDIplay::ReadBEint(const void *buffer, size_t nbytes)
{
uint64_t result = 0;
@ -54,6 +56,8 @@ uint64_t MIDIplay::ReadLEint(const void *buffer, size_t nbytes)
return result;
}
#endif
bool MIDIplay::LoadBank(const std::string &filename)
{
fileReader file;
@ -68,282 +72,223 @@ bool MIDIplay::LoadBank(const void *data, size_t size)
return LoadBank(file);
}
/* WOPL-needed misc functions */
static uint16_t toUint16LE(const uint8_t *arr)
template <class WOPLI>
static void cvt_generic_to_FMIns(adlinsdata2 &ins, const WOPLI &in)
{
uint16_t num = arr[0];
num |= ((arr[1] << 8) & 0xFF00);
return num;
}
static uint16_t toUint16BE(const uint8_t *arr)
{
uint16_t num = arr[1];
num |= ((arr[0] << 8) & 0xFF00);
return num;
}
static int16_t toSint16BE(const uint8_t *arr)
{
int16_t num = *reinterpret_cast<const int8_t *>(&arr[0]);
num *= 1 << 8;
num |= arr[1];
return num;
}
static const char *wopl3_magic = "WOPL3-BANK\0";
static const uint16_t wopl_latest_version = 3;
#define WOPL_INST_SIZE_V2 62
#define WOPL_INST_SIZE_V3 66
enum WOPL_InstrumentFlags
{
WOPL_Flags_NONE = 0,
WOPL_Flag_Enable4OP = 0x01,
WOPL_Flag_Pseudo4OP = 0x02,
WOPL_Flag_NoSound = 0x04,
};
struct WOPL_Inst
{
bool fourOps;
char padding[7];
struct adlinsdata adlins;
struct adldata op[2];
uint16_t ms_sound_kon;
uint16_t ms_sound_koff;
};
static bool readInstrument(MIDIplay::fileReader &file, WOPL_Inst &ins, uint16_t &version, bool isPercussion = false)
{
uint8_t idata[WOPL_INST_SIZE_V3];
if(version >= 3)
{
if(file.read(idata, 1, WOPL_INST_SIZE_V3) != WOPL_INST_SIZE_V3)
return false;
}
else
{
if(file.read(idata, 1, WOPL_INST_SIZE_V2) != WOPL_INST_SIZE_V2)
return false;
}
//strncpy(ins.name, char_p(idata), 32);
ins.op[0].finetune = (int8_t)toSint16BE(idata + 32);
ins.op[1].finetune = (int8_t)toSint16BE(idata + 34);
//ins.velocity_offset = int8_t(idata[36]);
ins.adlins.voice2_fine_tune = 0.0;
int8_t voice2_fine_tune = int8_t(idata[37]);
ins.voice2_fine_tune = 0.0;
int8_t voice2_fine_tune = in.second_voice_detune;
if(voice2_fine_tune != 0)
{
if(voice2_fine_tune == 1)
ins.adlins.voice2_fine_tune = 0.000025;
ins.voice2_fine_tune = 0.000025;
else if(voice2_fine_tune == -1)
ins.adlins.voice2_fine_tune = -0.000025;
ins.voice2_fine_tune = -0.000025;
else
ins.adlins.voice2_fine_tune = ((voice2_fine_tune * 15.625) / 1000.0);
ins.voice2_fine_tune = voice2_fine_tune * (15.625 / 1000.0);
}
ins.adlins.tone = isPercussion ? idata[38] : 0;
ins.tone = in.percussion_key_number;
ins.flags = (in.inst_flags & WOPL_Ins_4op) && (in.inst_flags & WOPL_Ins_Pseudo4op) ? adlinsdata::Flag_Pseudo4op : 0;
ins.flags|= (in.inst_flags & WOPL_Ins_4op) && ((in.inst_flags & WOPL_Ins_Pseudo4op) == 0) ? adlinsdata::Flag_Real4op : 0;
ins.flags|= (in.inst_flags & WOPL_Ins_IsBlank) ? adlinsdata::Flag_NoSound : 0;
uint8_t flags = idata[39];
ins.adlins.flags = (flags & WOPL_Flag_Enable4OP) && (flags & WOPL_Flag_Pseudo4OP) ? adlinsdata::Flag_Pseudo4op : 0;
ins.adlins.flags|= (flags & WOPL_Flag_NoSound) ? adlinsdata::Flag_NoSound : 0;
ins.fourOps = (flags & WOPL_Flag_Enable4OP) || (flags & WOPL_Flag_Pseudo4OP);
ins.op[0].feedconn = (idata[40]);
ins.op[1].feedconn = (idata[41]);
for(size_t op = 0, slt = 0; op < 4; op++, slt++)
bool fourOps = (in.inst_flags & WOPL_Ins_4op) || (in.inst_flags & WOPL_Ins_Pseudo4op);
for(size_t op = 0, slt = 0; op < static_cast<size_t>(fourOps ? 4 : 2); op++, slt++)
{
size_t off = 42 + size_t(op) * 5;
// ins.setAVEKM(op, idata[off + 0]);//AVEKM
// ins.setAtDec(op, idata[off + 2]);//AtDec
// ins.setSusRel(op, idata[off + 3]);//SusRel
// ins.setWaveForm(op, idata[off + 4]);//WaveForm
// ins.setKSLL(op, idata[off + 1]);//KSLL
ins.op[slt].carrier_E862 =
((static_cast<uint32_t>(idata[off + 4]) << 24) & 0xFF000000) //WaveForm
| ((static_cast<uint32_t>(idata[off + 3]) << 16) & 0x00FF0000) //SusRel
| ((static_cast<uint32_t>(idata[off + 2]) << 8) & 0x0000FF00) //AtDec
| ((static_cast<uint32_t>(idata[off + 0]) << 0) & 0x000000FF); //AVEKM
ins.op[slt].carrier_40 = idata[off + 1];//KSLL
ins.adl[slt].carrier_E862 =
((static_cast<uint32_t>(in.operators[op].waveform_E0) << 24) & 0xFF000000) //WaveForm
| ((static_cast<uint32_t>(in.operators[op].susrel_80) << 16) & 0x00FF0000) //SusRel
| ((static_cast<uint32_t>(in.operators[op].atdec_60) << 8) & 0x0000FF00) //AtDec
| ((static_cast<uint32_t>(in.operators[op].avekf_20) << 0) & 0x000000FF); //AVEKM
ins.adl[slt].carrier_40 = in.operators[op].ksl_l_40;//KSLL
op++;
off = 42 + size_t(op) * 5;
ins.op[slt].modulator_E862 =
((static_cast<uint32_t>(idata[off + 4]) << 24) & 0xFF000000) //WaveForm
| ((static_cast<uint32_t>(idata[off + 3]) << 16) & 0x00FF0000) //SusRel
| ((static_cast<uint32_t>(idata[off + 2]) << 8) & 0x0000FF00) //AtDec
| ((static_cast<uint32_t>(idata[off + 0]) << 0) & 0x000000FF); //AVEKM
ins.op[slt].modulator_40 = idata[off + 1];//KSLL
ins.adl[slt].modulator_E862 =
((static_cast<uint32_t>(in.operators[op].waveform_E0) << 24) & 0xFF000000) //WaveForm
| ((static_cast<uint32_t>(in.operators[op].susrel_80) << 16) & 0x00FF0000) //SusRel
| ((static_cast<uint32_t>(in.operators[op].atdec_60) << 8) & 0x0000FF00) //AtDec
| ((static_cast<uint32_t>(in.operators[op].avekf_20) << 0) & 0x000000FF); //AVEKM
ins.adl[slt].modulator_40 = in.operators[op].ksl_l_40;//KSLL
}
if(version >= 3)
ins.adl[0].finetune = static_cast<int8_t>(in.note_offset1);
ins.adl[0].feedconn = in.fb_conn1_C0;
if(!fourOps)
ins.adl[1] = ins.adl[0];
else
{
ins.ms_sound_kon = toUint16BE(idata + 62);
ins.ms_sound_koff = toUint16BE(idata + 64);
ins.adl[1].finetune = static_cast<int8_t>(in.note_offset2);
ins.adl[1].feedconn = in.fb_conn2_C0;
}
ins.ms_sound_kon = in.delay_on_ms;
ins.ms_sound_koff = in.delay_off_ms;
}
template <class WOPLI>
static void cvt_FMIns_to_generic(WOPLI &ins, const adlinsdata2 &in)
{
ins.second_voice_detune = 0;
double voice2_fine_tune = in.voice2_fine_tune;
if(voice2_fine_tune != 0)
{
if(voice2_fine_tune > 0 && voice2_fine_tune <= 0.000025)
ins.second_voice_detune = 1;
else if(voice2_fine_tune < 0 && voice2_fine_tune >= -0.000025)
ins.second_voice_detune = -1;
else
{
long value = lround(voice2_fine_tune * (1000.0 / 15.625));
value = (value < -128) ? -128 : value;
value = (value > +127) ? +127 : value;
ins.second_voice_detune = static_cast<int8_t>(value);
}
}
ins.percussion_key_number = in.tone;
bool fourOps = (in.flags & adlinsdata::Flag_Pseudo4op) || in.adl[0] != in.adl[1];
ins.inst_flags = fourOps ? WOPL_Ins_4op : 0;
ins.inst_flags|= (in.flags & adlinsdata::Flag_Pseudo4op) ? WOPL_Ins_Pseudo4op : 0;
ins.inst_flags|= (in.flags & adlinsdata::Flag_NoSound) ? WOPL_Ins_IsBlank : 0;
for(size_t op = 0, slt = 0; op < static_cast<size_t>(fourOps ? 4 : 2); op++, slt++)
{
ins.operators[op].waveform_E0 = static_cast<uint8_t>(in.adl[slt].carrier_E862 >> 24);
ins.operators[op].susrel_80 = static_cast<uint8_t>(in.adl[slt].carrier_E862 >> 16);
ins.operators[op].atdec_60 = static_cast<uint8_t>(in.adl[slt].carrier_E862 >> 8);
ins.operators[op].avekf_20 = static_cast<uint8_t>(in.adl[slt].carrier_E862 >> 0);
ins.operators[op].ksl_l_40 = in.adl[slt].carrier_40;
op++;
ins.operators[op].waveform_E0 = static_cast<uint8_t>(in.adl[slt].carrier_E862 >> 24);
ins.operators[op].susrel_80 = static_cast<uint8_t>(in.adl[slt].carrier_E862 >> 16);
ins.operators[op].atdec_60 = static_cast<uint8_t>(in.adl[slt].carrier_E862 >> 8);
ins.operators[op].avekf_20 = static_cast<uint8_t>(in.adl[slt].carrier_E862 >> 0);
ins.operators[op].ksl_l_40 = in.adl[slt].carrier_40;
}
ins.note_offset1 = in.adl[0].finetune;
ins.fb_conn1_C0 = in.adl[0].feedconn;
if(!fourOps)
{
ins.operators[2] = ins.operators[0];
ins.operators[3] = ins.operators[1];
}
else
{
ins.ms_sound_kon = 1000;
ins.ms_sound_koff = 500;
ins.note_offset2 = in.adl[1].finetune;
ins.fb_conn2_C0 = in.adl[1].feedconn;
}
return true;
ins.delay_on_ms = in.ms_sound_kon;
ins.delay_off_ms = in.ms_sound_koff;
}
void cvt_ADLI_to_FMIns(adlinsdata2 &ins, const ADL_Instrument &in)
{
return cvt_generic_to_FMIns(ins, in);
}
void cvt_FMIns_to_ADLI(ADL_Instrument &ins, const adlinsdata2 &in)
{
cvt_FMIns_to_generic(ins, in);
}
bool MIDIplay::LoadBank(MIDIplay::fileReader &fr)
{
int err = 0;
WOPLFile *wopl = NULL;
char *raw_file_data = NULL;
size_t fsize;
ADL_UNUSED(fsize);
if(!fr.isValid())
{
errorStringOut = "Custom bank: Invalid data stream!";
return false;
}
char magic[32];
std::memset(magic, 0, 32);
uint16_t version = 0;
uint16_t count_melodic_banks = 1;
uint16_t count_percusive_banks = 1;
if(fr.read(magic, 1, 11) != 11)
// Read complete bank file into the memory
fr.seek(0, SEEK_END);
fsize = fr.tell();
fr.seek(0, SEEK_SET);
// Allocate necessary memory block
raw_file_data = (char*)malloc(fsize);
if(!raw_file_data)
{
errorStringOut = "Custom bank: Can't read magic number!";
errorStringOut = "Custom bank: Out of memory before of read!";
return false;
}
fr.read(raw_file_data, 1, fsize);
if(std::strncmp(magic, wopl3_magic, 11) != 0)
// Parse bank file from the memory
wopl = WOPL_LoadBankFromMem((void*)raw_file_data, fsize, &err);
//Free the buffer no more needed
free(raw_file_data);
// Check for any erros
if(!wopl)
{
errorStringOut = "Custom bank: Invalid magic number!";
switch(err)
{
case WOPL_ERR_BAD_MAGIC:
errorStringOut = "Custom bank: Invalid magic!";
return false;
case WOPL_ERR_UNEXPECTED_ENDING:
errorStringOut = "Custom bank: Unexpected ending!";
return false;
case WOPL_ERR_INVALID_BANKS_COUNT:
errorStringOut = "Custom bank: Invalid banks count!";
return false;
case WOPL_ERR_NEWER_VERSION:
errorStringOut = "Custom bank: Version is newer than supported by this library!";
return false;
case WOPL_ERR_OUT_OF_MEMORY:
errorStringOut = "Custom bank: Out of memory!";
return false;
default:
errorStringOut = "Custom bank: Unknown error!";
return false;
}
uint8_t version_raw[2];
if(fr.read(version_raw, 1, 2) != 2)
{
errorStringOut = "Custom bank: Can't read version!";
return false;
}
version = toUint16LE(version_raw);
if(version > wopl_latest_version)
{
errorStringOut = "Custom bank: Unsupported WOPL version!";
return false;
}
uint8_t head[6];
std::memset(head, 0, 6);
if(fr.read(head, 1, 6) != 6)
{
errorStringOut = "Custom bank: Can't read header!";
return false;
}
count_melodic_banks = toUint16BE(head);
count_percusive_banks = toUint16BE(head + 2);
if((count_melodic_banks < 1) || (count_percusive_banks < 1))
{
errorStringOut = "Custom bank: Too few banks in this file!";
return false;
}
/*UNUSED YET*/
bool default_deep_vibrato = ((head[4]>>0) & 0x01);
bool default_deep_tremolo = ((head[4]>>1) & 0x01);
//5'th byte reserved for Deep-Tremolo and Deep-Vibrato flags
m_setup.HighTremoloMode = default_deep_tremolo;
m_setup.HighVibratoMode = default_deep_vibrato;
//6'th byte reserved for ADLMIDI's default volume model
m_setup.VolumeModel = (int)head[5];
opl.dynamic_melodic_banks.clear();
opl.dynamic_percussion_banks.clear();
opl.dynamic_bank_setup.adLibPercussions = false;
opl.dynamic_bank_setup.scaleModulators = false;
opl.dynamic_bank_setup.deepTremolo = (wopl->opl_flags & WOPL_FLAG_DEEP_TREMOLO) != 0;
opl.dynamic_bank_setup.deepVibrato = (wopl->opl_flags & WOPL_FLAG_DEEP_VIBRATO) != 0;
opl.dynamic_bank_setup.volumeModel = wopl->volume_model;
m_setup.HighTremoloMode = -1;
m_setup.HighVibratoMode = -1;
m_setup.VolumeModel = ADLMIDI_VolumeModel_AUTO;
opl.setEmbeddedBank(m_setup.AdlBank);
if(version >= 2)//Read bank meta-entries
{
for(uint16_t i = 0; i < count_melodic_banks; i++)
{
uint8_t bank_meta[34];
if(fr.read(bank_meta, 1, 34) != 34)
{
errorStringOut = "Custom bank: Fail to read melodic bank meta-data!";
return false;
}
uint16_t bank = uint16_t(bank_meta[33]) * 256 + uint16_t(bank_meta[32]);
size_t offset = opl.dynamic_melodic_banks.size();
opl.dynamic_melodic_banks[bank] = offset;
//strncpy(bankMeta.name, char_p(bank_meta), 32);
}
uint16_t slots_counts[2] = {wopl->banks_count_melodic, wopl->banks_count_percussion};
WOPLBank *slots_src_ins[2] = { wopl->banks_melodic, wopl->banks_percussive };
for(uint16_t i = 0; i < count_percusive_banks; i++)
for(unsigned ss = 0; ss < 2; ss++)
{
uint8_t bank_meta[34];
if(fr.read(bank_meta, 1, 34) != 34)
for(unsigned i = 0; i < slots_counts[ss]; i++)
{
errorStringOut = "Custom bank: Fail to read percussion bank meta-data!";
return false;
}
uint16_t bank = uint16_t(bank_meta[33]) * 256 + uint16_t(bank_meta[32]);
size_t offset = opl.dynamic_percussion_banks.size();
opl.dynamic_percussion_banks[bank] = offset;
//strncpy(bankMeta.name, char_p(bank_meta), 32);
unsigned bankno =
(slots_src_ins[ss][i].bank_midi_msb * 256) +
slots_src_ins[ss][i].bank_midi_lsb +
(ss ? OPL3::PercussionTag : 0);
OPL3::Bank &bank = opl.dynamic_banks[bankno];
for(int j = 0; j < 128; j++)
{
adlinsdata2 &ins = bank.ins[j];
std::memset(&ins, 0, sizeof(adlinsdata2));
WOPLInstrument &inIns = slots_src_ins[ss][i].ins[j];
cvt_generic_to_FMIns(ins, inIns);
}
}
uint16_t total = 128 * count_melodic_banks;
bool readPercussion = false;
tryAgain:
for(uint16_t i = 0; i < total; i++)
{
WOPL_Inst ins;
std::memset(&ins, 0, sizeof(WOPL_Inst));
if(!readInstrument(fr, ins, version, readPercussion))
{
opl.setEmbeddedBank(m_setup.AdlBank);
errorStringOut = "Custom bank: Fail to read instrument!";
return false;
}
ins.adlins.ms_sound_kon = ins.ms_sound_kon;
ins.adlins.ms_sound_koff = ins.ms_sound_koff;
ins.adlins.adlno1 = static_cast<uint16_t>(opl.dynamic_instruments.size() | opl.DynamicInstrumentTag);
opl.dynamic_instruments.push_back(ins.op[0]);
ins.adlins.adlno2 = ins.adlins.adlno1;
if(ins.fourOps)
{
ins.adlins.adlno2 = static_cast<uint16_t>(opl.dynamic_instruments.size() | opl.DynamicInstrumentTag);
opl.dynamic_instruments.push_back(ins.op[1]);
}
opl.dynamic_metainstruments.push_back(ins.adlins);
}
if(!readPercussion)
{
total = 128 * count_percusive_banks;
readPercussion = true;
goto tryAgain;
}
opl.AdlBank = ~0u; // Use dynamic banks!
//Percussion offset is count of instruments multipled to count of melodic banks
opl.dynamic_percussion_offset = 128 * count_melodic_banks;
applySetup();
WOPL_Free(wopl);
return true;
}
@ -373,7 +318,7 @@ bool MIDIplay::LoadMIDI(MIDIplay::fileReader &fr)
errorString.clear();
#ifdef DISABLE_EMBEDDED_BANKS
if((opl.AdlBank != ~0u) || (opl.dynamic_metainstruments.size() < 256))
if((opl.AdlBank != ~0u) || opl.dynamic_banks.empty())
{
errorStringOut = "Bank is not set! Please load any instruments bank by using of adl_openBankFile() or adl_openBankData() functions!";
return false;
@ -497,8 +442,7 @@ riffskip:
#endif //ADLMIDI_DISABLE_XMI_SUPPORT
else if(std::memcmp(HeaderBuf, "CTMF", 4) == 0)
{
opl.dynamic_instruments.clear();
opl.dynamic_metainstruments.clear();
opl.dynamic_banks.clear();
// Creative Music Format (CMF).
// When playing CTMF files, use the following commandline:
// adlmidi song8.ctmf -p -v 1 1 0
@ -520,13 +464,19 @@ riffskip:
//std::printf("%u instruments\n", ins_count);
for(unsigned i = 0; i < ins_count; ++i)
{
unsigned bank = i / 256;
bank = (bank & 127) + ((bank >> 7) << 8);
if(bank > 127 + (127 << 8))
break;
bank += (i % 256 < 128) ? 0 : OPL3::PercussionTag;
unsigned char InsData[16];
fr.read(InsData, 1, 16);
/*std::printf("Ins %3u: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n",
i, InsData[0],InsData[1],InsData[2],InsData[3], InsData[4],InsData[5],InsData[6],InsData[7],
InsData[8],InsData[9],InsData[10],InsData[11], InsData[12],InsData[13],InsData[14],InsData[15]);*/
struct adldata adl;
struct adlinsdata adlins;
adlinsdata2 &adlins = opl.dynamic_banks[bank].ins[i % 128];
adldata adl;
adl.modulator_E862 =
((static_cast<uint32_t>(InsData[8] & 0x07) << 24) & 0xFF000000) //WaveForm
| ((static_cast<uint32_t>(InsData[6]) << 16) & 0x00FF0000) //Sustain/Release
@ -541,15 +491,13 @@ riffskip:
adl.carrier_40 = InsData[3];
adl.feedconn = InsData[10] & 0x0F;
adl.finetune = 0;
adlins.adlno1 = static_cast<uint16_t>(opl.dynamic_instruments.size() | opl.DynamicInstrumentTag);
adlins.adlno2 = adlins.adlno1;
adlins.adl[0] = adl;
adlins.adl[1] = adl;
adlins.ms_sound_kon = 1000;
adlins.ms_sound_koff = 500;
adlins.tone = 0;
adlins.flags = 0;
adlins.voice2_fine_tune = 0.0;
opl.dynamic_metainstruments.push_back(adlins);
opl.dynamic_instruments.push_back(adl);
}
fr.seeku(mus_start, SEEK_SET);
@ -557,10 +505,9 @@ riffskip:
DeltaTicks = (size_t)ticks;
opl.AdlBank = ~0u; // Ignore AdlBank number, use dynamic banks instead
//std::printf("CMF deltas %u ticks %u, basictempo = %u\n", deltas, ticks, basictempo);
opl.LogarithmicVolumes = true;
opl.AdlPercussionMode = true;
opl.m_musicMode = OPL3::MODE_CMF;
opl.m_volumeScale = OPL3::VOLUME_CMF;
opl.m_volumeScale = OPL3::VOLUME_NATIVE;
}
else
{
@ -575,10 +522,9 @@ riffskip:
fr.seek(0x7D, SEEK_SET);
TrackCount = 1;
DeltaTicks = 60;
opl.LogarithmicVolumes = true;
//opl.CartoonersVolumes = true;
opl.m_musicMode = OPL3::MODE_RSXX;
opl.m_volumeScale = OPL3::VOLUME_CMF;
opl.m_volumeScale = OPL3::VOLUME_NATIVE;
}
}
@ -636,11 +582,11 @@ riffskip:
TrackData.clear();
TrackData.resize(TrackCount, std::vector<uint8_t>());
//CurrentPosition.track.clear();
//CurrentPosition.track.resize(TrackCount);
InvDeltaTicks = fraction<uint64_t>(1, 1000000l * static_cast<uint64_t>(DeltaTicks));
//Tempo = 1000000l * InvDeltaTicks;
if(is_CMF || is_RSXX)
Tempo = fraction<uint64_t>(1, static_cast<uint64_t>(DeltaTicks));
else
Tempo = fraction<uint64_t>(1, static_cast<uint64_t>(DeltaTicks) * 2);
static const unsigned char EndTag[4] = {0xFF, 0x2F, 0x00, 0x00};
size_t totalGotten = 0;
@ -761,14 +707,14 @@ riffskip:
return false;
}
//Build new MIDI events table (ALPHA!!!)
//Build new MIDI events table
if(!buildTrackData())
{
errorStringOut = fr._fileName + ": MIDI data parsing error has occouped!\n" + errorString;
return false;
}
opl.Reset(m_setup.PCM_RATE); // Reset AdLib
opl.Reset(m_setup.emulator, m_setup.PCM_RATE, this); // Reset OPL3 chip
//opl.Reset(); // ...twice (just in case someone misprogrammed OPL3 previously)
ch.clear();
ch.resize(opl.NumChannels);

File diff suppressed because it is too large Load diff

View file

@ -25,6 +25,21 @@
#ifdef ADLMIDI_HW_OPL
static const unsigned OPLBase = 0x388;
#else
# if defined(ADLMIDI_DISABLE_NUKED_EMULATOR) && defined(ADLMIDI_DISABLE_DOSBOX_EMULATOR)
# error "No emulators enabled. You must enable at least one emulator to use this library!"
# endif
// Nuked OPL3 emulator, Most accurate, but requires the powerful CPU
# ifndef ADLMIDI_DISABLE_NUKED_EMULATOR
# include "chips/nuked_opl3.h"
# include "chips/nuked_opl3_v174.h"
# endif
// DosBox 0.74 OPL3 emulator, Well-accurate and fast
# ifndef ADLMIDI_DISABLE_DOSBOX_EMULATOR
# include "chips/dosbox_opl3.h"
# endif
#endif
#ifdef DISABLE_EMBEDDED_BANKS
@ -114,87 +129,56 @@ static const unsigned short Channels[23] =
Ports: ???
*/
const adlinsdata &OPL3::GetAdlMetaIns(size_t n)
{
return (n & DynamicMetaInstrumentTag) ?
dynamic_metainstruments[n & ~DynamicMetaInstrumentTag]
: adlins[n];
}
size_t OPL3::GetAdlMetaNumber(size_t midiins)
{
return (AdlBank == ~0u) ?
(midiins | DynamicMetaInstrumentTag)
: banks[AdlBank][midiins];
}
const adldata &OPL3::GetAdlIns(size_t insno)
{
return (insno & DynamicInstrumentTag)
? dynamic_instruments[insno & ~DynamicInstrumentTag]
: adl[insno];
}
void OPL3::setEmbeddedBank(unsigned int bank)
{
AdlBank = bank;
//Embedded banks are supports 128:128 GM set only
dynamic_percussion_offset = 128;
dynamic_melodic_banks.clear();
dynamic_percussion_banks.clear();
dynamic_banks.clear();
if(bank >= static_cast<unsigned int>(maxAdlBanks()))
return;
Bank *bank_pair[2] =
{
&dynamic_banks[0],
&dynamic_banks[PercussionTag]
};
for(unsigned i = 0; i < 256; ++i)
{
size_t meta = banks[bank][i];
adlinsdata2 &ins = bank_pair[i / 128]->ins[i % 128];
ins = adlinsdata2(adlins[meta]);
}
}
static adlinsdata2 makeEmptyInstrument()
{
adlinsdata2 ins;
memset(&ins, 0, sizeof(adlinsdata2));
ins.flags = adlinsdata::Flag_NoSound;
return ins;
}
const adlinsdata2 OPL3::emptyInstrument = makeEmptyInstrument();
OPL3::OPL3() :
dynamic_percussion_offset(128),
DynamicInstrumentTag(0x8000u),
DynamicMetaInstrumentTag(0x4000000u),
NumCards(1),
AdlBank(0),
NumFourOps(0),
HighTremoloMode(false),
HighVibratoMode(false),
AdlPercussionMode(false),
LogarithmicVolumes(false),
//CartoonersVolumes(false),
m_musicMode(MODE_MIDI),
m_volumeScale(VOLUME_Generic)
{}
void OPL3::Poke(size_t card, uint32_t index, uint32_t value)
{
#ifdef ADLMIDI_HW_OPL
(void)card;
unsigned o = index >> 8;
unsigned port = OPLBase + o * 2;
#ifdef __DJGPP__
outportb(port, index);
for(unsigned c = 0; c < 6; ++c) inportb(port);
outportb(port + 1, value);
for(unsigned c = 0; c < 35; ++c) inportb(port);
#endif//__DJGPP__
#ifdef __WATCOMC__
outp(port, index);
for(uint16_t c = 0; c < 6; ++c) inp(port);
outp(port + 1, value);
for(uint16_t c = 0; c < 35; ++c) inp(port);
#endif//__WATCOMC__
#else//ADLMIDI_HW_OPL
#ifdef ADLMIDI_USE_DOSBOX_OPL
cards[card].WriteReg(index, static_cast<uint8_t>(value));
#else
OPL3_WriteReg(&cards[card], static_cast<Bit16u>(index), static_cast<Bit8u>(value));
#endif
#endif//ADLMIDI_HW_OPL
#ifdef DISABLE_EMBEDDED_BANKS
AdlBank = ~0u;
#else
setEmbeddedBank(0);
#endif
}
void OPL3::PokeN(size_t card, uint16_t index, uint8_t value)
void OPL3::Poke(size_t card, uint16_t index, uint8_t value)
{
#ifdef ADLMIDI_HW_OPL
(void)card;
@ -216,11 +200,7 @@ void OPL3::PokeN(size_t card, uint16_t index, uint8_t value)
#endif//__WATCOMC__
#else
#ifdef ADLMIDI_USE_DOSBOX_OPL
cards[card].WriteReg(static_cast<Bit32u>(index), value);
#else
OPL3_WriteReg(&cards[card], index, value);
#endif
cardsOP2[card]->writeReg(index, value);
#endif
}
@ -277,10 +257,9 @@ void OPL3::Touch_Real(unsigned c, unsigned volume, uint8_t brightness)
volume = 63;
size_t card = c / 23, cc = c % 23;
size_t i = ins[c];
const adldata &adli = ins[c];
uint16_t o1 = Operators[cc * 2 + 0];
uint16_t o2 = Operators[cc * 2 + 1];
const adldata &adli = GetAdlIns(i);
uint8_t x = adli.modulator_40, y = adli.carrier_40;
uint16_t mode = 1; // 2-op AM
@ -290,22 +269,22 @@ void OPL3::Touch_Real(unsigned c, unsigned volume, uint8_t brightness)
}
else if(four_op_category[c] == 1 || four_op_category[c] == 2)
{
size_t i0, i1;
const adldata *i0, *i1;
if(four_op_category[c] == 1)
{
i0 = i;
i1 = ins[c + 3];
i0 = &adli;
i1 = &ins[c + 3];
mode = 2; // 4-op xx-xx ops 1&2
}
else
{
i0 = ins[c - 3];
i1 = i;
i0 = &ins[c - 3];
i1 = &adli;
mode = 6; // 4-op xx-xx ops 3&4
}
mode += (GetAdlIns(i0).feedconn & 1) + (GetAdlIns(i1).feedconn & 1) * 2;
mode += (i0->feedconn & 1) + (i1->feedconn & 1) * 2;
}
static const bool do_ops[10][2] =
@ -372,14 +351,13 @@ void OPL3::Touch(unsigned c, unsigned volume) // Volume maxes at 127*127*127
}
}*/
void OPL3::Patch(uint16_t c, size_t i)
void OPL3::Patch(uint16_t c, const adldata &adli)
{
uint16_t card = c / 23, cc = c % 23;
static const uint8_t data[4] = {0x20, 0x60, 0x80, 0xE0};
ins[c] = i;
ins[c] = adli;
uint16_t o1 = Operators[cc * 2 + 0];
uint16_t o2 = Operators[cc * 2 + 1];
const adldata &adli = GetAdlIns(i);
unsigned x = adli.modulator_E862, y = adli.carrier_E862;
for(unsigned a = 0; a < 4; ++a, x >>= 8, y >>= 8)
@ -395,7 +373,7 @@ void OPL3::Pan(unsigned c, unsigned value)
unsigned card = c / 23, cc = c % 23;
if(Channels[cc] != 0xFFF)
Poke(card, 0xC0 + Channels[cc], GetAdlIns(ins[c]).feedconn | value);
Poke(card, 0xC0 + Channels[cc], ins[c].feedconn | value);
}
void OPL3::Silence() // Silence all OPL channels.
@ -479,9 +457,8 @@ void OPL3::ChangeVolumeRangesModel(ADLMIDI_VolumeModels volumeModel)
m_volumeScale = OPL3::VOLUME_Generic;
break;
case ADLMIDI_VolumeModel_CMF:
LogarithmicVolumes = true;
m_volumeScale = OPL3::VOLUME_CMF;
case ADLMIDI_VolumeModel_NativeOPL3:
m_volumeScale = OPL3::VOLUME_NATIVE;
break;
case ADLMIDI_VolumeModel_DMX:
@ -498,26 +475,36 @@ void OPL3::ChangeVolumeRangesModel(ADLMIDI_VolumeModels volumeModel)
}
}
void OPL3::Reset(unsigned long PCM_RATE)
#ifndef ADLMIDI_HW_OPL
void OPL3::ClearChips()
{
#ifndef ADLMIDI_HW_OPL
#ifdef ADLMIDI_USE_DOSBOX_OPL
DBOPL::Handler emptyChip; //Constructors inside are will initialize necessary fields
#else
_opl3_chip emptyChip;
std::memset(&emptyChip, 0, sizeof(_opl3_chip));
#endif
cards.clear();
#endif
for(size_t i = 0; i < cardsOP2.size(); i++)
cardsOP2[i].reset(NULL);
cardsOP2.clear();
}
#endif
void OPL3::Reset(int emulator, unsigned long PCM_RATE, void *audioTickHandler)
{
#ifndef ADLMIDI_HW_OPL
ClearChips();
#else
(void)emulator;
(void)PCM_RATE;
#endif
#if !defined(ADLMIDI_AUDIO_TICK_HANDLER)
(void)audioTickHandler;
#endif
ins.clear();
pit.clear();
regBD.clear();
#ifndef ADLMIDI_HW_OPL
cards.resize(NumCards, emptyChip);
#endif
#ifndef ADLMIDI_HW_OPL
cardsOP2.resize(NumCards, AdlMIDI_SPtr<OPLChipBase>());
#endif
NumChannels = NumCards * 23;
ins.resize(NumChannels, 189);
ins.resize(NumChannels, adl[adlDefaultNumber]);
pit.resize(NumChannels, 0);
regBD.resize(NumCards, 0);
four_op_category.resize(NumChannels, 0);
@ -536,24 +523,45 @@ void OPL3::Reset(unsigned long PCM_RATE)
};
unsigned fours = NumFourOps;
for(unsigned card = 0; card < NumCards; ++card)
for(size_t i = 0; i < NumCards; ++i)
{
#ifndef ADLMIDI_HW_OPL
# ifdef ADLMIDI_USE_DOSBOX_OPL
cards[card].Init(PCM_RATE);
# else
OPL3_Reset(&cards[card], static_cast<Bit32u>(PCM_RATE));
# endif
#endif
#ifndef ADLMIDI_HW_OPL
OPLChipBase *chip;
switch(emulator)
{
default:
#ifndef ADLMIDI_DISABLE_NUKED_EMULATOR
case ADLMIDI_EMU_NUKED: /* Latest Nuked OPL3 */
chip = new NukedOPL3;
break;
case ADLMIDI_EMU_NUKED_174: /* Old Nuked OPL3 1.4.7 modified and optimized */
chip = new NukedOPL3v174;
break;
#endif
#ifndef ADLMIDI_DISABLE_DOSBOX_EMULATOR
case ADLMIDI_EMU_DOSBOX:
chip = new DosBoxOPL3;
break;
#endif
}
cardsOP2[i].reset(chip);
chip->setChipId((uint32_t)i);
chip->setRate((uint32_t)PCM_RATE);
if(runAtPcmRate)
chip->setRunningAtPcmRate(true);
# if defined(ADLMIDI_AUDIO_TICK_HANDLER)
chip->setAudioTickHandlerInstance(audioTickHandler);
# endif
#endif // ADLMIDI_HW_OPL
for(unsigned a = 0; a < 18; ++a) Poke(card, 0xB0 + Channels[a], 0x00);
for(unsigned a = 0; a < 18; ++a) Poke(i, 0xB0 + Channels[a], 0x00);
for(unsigned a = 0; a < sizeof(data) / sizeof(*data); a += 2)
PokeN(card, data[a], static_cast<uint8_t>(data[a + 1]));
Poke(card, 0x0BD, regBD[card] = (HighTremoloMode * 0x80
Poke(i, data[a], static_cast<uint8_t>(data[a + 1]));
Poke(i, 0x0BD, regBD[i] = (HighTremoloMode * 0x80
+ HighVibratoMode * 0x40
+ AdlPercussionMode * 0x20));
unsigned fours_this_card = std::min(fours, 6u);
Poke(card, 0x104, (1 << fours_this_card) - 1);
Poke(i, 0x104, (1 << fours_this_card) - 1);
//fprintf(stderr, "Card %u: %u four-ops.\n", card, fours_this_card);
fours -= fours_this_card;
}

Some files were not shown because too many files have changed in this diff Show more