diff --git a/ref/dx2/dllmain.cpp b/ref/dx2/dllmain.cpp new file mode 100644 index 00000000..5fb3caa0 --- /dev/null +++ b/ref/dx2/dllmain.cpp @@ -0,0 +1,19 @@ +// dllmain.cpp : Defines the entry point for the DLL application. +#include "windows.h" + +BOOL APIENTRY DllMain( HMODULE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved + ) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} + diff --git a/ref/dx2/r_context.c b/ref/dx2/r_context.c new file mode 100644 index 00000000..0b85322b --- /dev/null +++ b/ref/dx2/r_context.c @@ -0,0 +1,1039 @@ +/* +r_context.c -- dx2 renderer context +Copyright (C) 2023-2024 a1batross +Copyright (C) 2025 lewa_j + +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 +(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 General Public License for more details. +*/ + +#define INITGUID +#include "r_local.h" +#include +#include "xash3d_types.h" +#include "const.h" +#include "cvardef.h" +#include "xash3d_mathlib.h" +#include "ref_api.h" +#include "com_strings.h" +#include "ref_params.h" + +//#pragma comment(lib, "ddraw.lib") +//TODO COM_GetProcAddress + +ref_api_t gEngfuncs; +static ref_globals_t *gpGlobals; +ref_client_t *gp_cl; + +poolhandle_t r_temppool; + +gl_globals_t tr; +ref_instance_t RI; + +static void R_SimpleStub( void ) +{ + ; +} + +static void R_SimpleStubInt( int unused ) +{ + ; +} + +static void R_SimpleStubUInt( unsigned int unused ) +{ + ; +} + +static void R_SimpleStubBool( qboolean unused ) +{ + ; +} + +const char *dxResultToStr( HRESULT r ) +{ + if( r == DD_OK ) + return "DD_OK"; + if( r == DDERR_INVALIDPARAMS ) + return "DDERR_INVALIDPARAMS"; + if( r == DDERR_INVALIDOBJECT ) + return "DDERR_INVALIDOBJECT"; + if( r == DDERR_UNSUPPORTED ) + return "DDERR_UNSUPPORTED"; + if( r == DDERR_NOCOOPERATIVELEVELSET ) + return "DDERR_NOCOOPERATIVELEVELSET"; + if( r == DDERR_INVALIDCAPS ) + return "DDERR_INVALIDCAPS"; + if( r == DDERR_INCOMPATIBLEPRIMARY ) + return "DDERR_INCOMPATIBLEPRIMARY"; + if( r == DDERR_NOEXCLUSIVEMODE ) + return "DDERR_NOEXCLUSIVEMODE"; + if( r == DDERR_NOFLIPHW ) + return "DDERR_NOFLIPHW"; + if( r == DDERR_OUTOFVIDEOMEMORY ) + return "DDERR_OUTOFVIDEOMEMORY"; + if( r == DDERR_PRIMARYSURFACEALREADYEXISTS ) + return "DDERR_PRIMARYSURFACEALREADYEXISTS"; + if( r == DDERR_UNSUPPORTEDMODE ) + return "DDERR_UNSUPPORTEDMODE"; + if( r == DDERR_NOTFOUND ) + return "DDERR_NOTFOUND"; + if( r == DDERR_INVALIDRECT ) + return "DDERR_INVALIDRECT"; + if( r == D3DERR_VIEWPORTDATANOTSET ) + return "D3DERR_VIEWPORTDATANOTSET"; + if( r == D3DERR_VIEWPORTHASNODEVICE ) + return "D3DERR_VIEWPORTHASNODEVICE"; + if( r == D3DERR_SURFACENOTINVIDMEM ) + return "D3DERR_SURFACENOTINVIDMEM"; + + return "UNKNOWN"; +} + +dx_context_t dxc; + +static qboolean DD_CreatePrimarySurface() +{ + DDSURFACEDESC ddsd = { 0 }; + memset(&ddsd, 0, sizeof(ddsd)); + ddsd.dwSize = sizeof(ddsd); + ddsd.dwFlags = DDSD_CAPS; + ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; + + DXCheck(IDirectDraw_CreateSurface(dxc.pdd, &ddsd, &dxc.pddsPrimary, NULL)); + printf("IDirectDraw::CreateSurface(PRIMARY) %p\n", dxc.pddsPrimary); + if (!dxc.pddsPrimary) + return false; + + DXCheck(IDirectDraw_CreateClipper(dxc.pdd, 0, &dxc.pddClipper, NULL)); + gEngfuncs.Con_Printf("IDirectDraw::CreateClipper %p\n", dxc.pddClipper); + if (!dxc.pddClipper) + return false; + + DXCheck(IDirectDrawClipper_SetHWnd(dxc.pddClipper, 0, dxc.window)); + DXCheck(IDirectDrawSurface_SetClipper(dxc.pddsPrimary, dxc.pddClipper)); + + return true; +} + +static qboolean DD_CreateBackSurface() +{ + DDSURFACEDESC ddsd = { 0 }; + memset(&ddsd, 0, sizeof(ddsd)); + ddsd.dwSize = sizeof(ddsd); + ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT; + ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_3DDEVICE | DDSCAPS_VIDEOMEMORY; + ddsd.dwWidth = dxc.windowWidth; + ddsd.dwHeight = dxc.windowHeight; + + DXCheck(IDirectDraw_CreateSurface(dxc.pdd, &ddsd, &dxc.pddsBack, NULL)); + gEngfuncs.Con_Printf("IDirectDraw::CreateSurface(BACK) %p\n", dxc.pddsBack); + if (!dxc.pddsBack) + return false; + + DXCheck(IDirectDraw_CreateClipper(dxc.pdd, 0, &dxc.pddBackClipper, NULL)); + gEngfuncs.Con_Printf("IDirectDraw::CreateClipper(BACK) %p\n", dxc.pddBackClipper); + if (!dxc.pddBackClipper) + return false; + + struct region_t { + RGNDATAHEADER rdh; + RECT rect; + } region; + region.rdh.dwSize = sizeof(region.rdh); + region.rdh.iType = RDH_RECTANGLES; + region.rdh.nCount = 1; + region.rdh.nRgnSize = sizeof(RECT); + region.rdh.rcBound.left = 0; + region.rdh.rcBound.top = 0; + region.rdh.rcBound.right = dxc.windowWidth; + region.rdh.rcBound.bottom = dxc.windowHeight; + memcpy(®ion.rect, ®ion.rdh.rcBound, sizeof(region.rdh.rcBound)); + + DXCheck(IDirectDrawClipper_SetClipList(dxc.pddBackClipper, (RGNDATA *)®ion, 0)); + DXCheck(IDirectDrawSurface_SetClipper(dxc.pddsBack, dxc.pddBackClipper)); + + return true; +} + +DEFINE_ENGINE_SHARED_CVAR_LIST() + +static qboolean R_Init( void ) +{ + memset(&dxc, 0, sizeof(dxc)); + + RETRIEVE_ENGINE_SHARED_CVAR_LIST(); + + // init draw stack + tr.draw_list = &tr.draw_stack[0]; + tr.draw_stack_pos = 0; + + r_temppool = Mem_AllocPool("Render Zone"); + + // create the window + if (!gEngfuncs.R_Init_Video(REF_D3D)) + { + Mem_FreePool(&r_temppool); + return false; + } + + void *handle = NULL; + ref_window_type_t wt = gEngfuncs.R_GetWindowHandle( &handle, REF_WINDOW_TYPE_WIN32 ); + gEngfuncs.Con_Printf( "R_Init: WindowHandle %p type %d\n", handle, wt ); + if (wt != REF_WINDOW_TYPE_WIN32 || !handle) + { + Mem_FreePool(&r_temppool); + return false; + } + dxc.window = (HWND)handle; + + RECT dstRect = {}; + GetClientRect(dxc.window, &dstRect); + dxc.windowWidth = dstRect.right - dstRect.left; + dxc.windowHeight = dstRect.bottom - dstRect.top; + + DXCheck(DirectDrawCreate(NULL, &dxc.pdd, NULL)); + gEngfuncs.Con_Reportf("DirectDrawCreate %p\n", dxc.pdd); + if (!dxc.pdd) + { + Mem_FreePool(&r_temppool); + return false; + } + + DXCheck(IDirectDraw_SetCooperativeLevel(dxc.pdd, dxc.window, DDSCL_NORMAL)); + + if (!DD_CreatePrimarySurface()) + { + Mem_FreePool(&r_temppool); + return false; + } + + if (!DD_CreateBackSurface()) + { + Mem_FreePool(&r_temppool); + return false; + } + + dxc.renderMode = kRenderNormal; + + R_InitImages(); + + return true; +} + +void R_Shutdown(void) +{ + Mem_FreePool(&r_temppool); + + gEngfuncs.R_Free_Video(); +} + +static void GL_InitExtensions( void ) +{ + ; +} + +static void GL_ClearExtensions( void ) +{ + ; +} + +void R_AllowFog(qboolean allowed) +{ + +} + +void GL_SetRenderMode(int mode) +{ + dxc.renderMode = mode; +} + +static const char *R_GetConfigName( void ) +{ + return "ref_dx2"; +} + +static qboolean R_SetDisplayTransform( ref_screen_rotation_t rotate, int offset_x, int offset_y, float scale_x, float scale_y ) +{ + return false; + + qboolean ret = true; + if (rotate > 0) + { + gEngfuncs.Con_Printf("rotation transform not supported\n"); + ret = false; + } + + if (offset_x || offset_y) + { + gEngfuncs.Con_Printf("offset transform not supported\n"); + ret = false; + } + + if (scale_x != 1.0f || scale_y != 1.0f) + { + gEngfuncs.Con_Printf("scale transform not supported\n"); + ret = false; + } + + return ret; +} + +static void GL_SetupAttributes( int safegl ) +{ + ; +} + +static void R_ClearScene(void) +{ + tr.draw_list->num_solid_entities = 0; + tr.draw_list->num_trans_entities = 0; + tr.draw_list->num_beam_entities = 0; + + // clear the scene befor start new frame + if (gEngfuncs.drawFuncs->R_ClearScene != NULL) + gEngfuncs.drawFuncs->R_ClearScene(); +} + +static qboolean R_AddEntity( struct cl_entity_s *clent, int type ) +{ + return true; +} + +static void CL_AddCustomBeam( cl_entity_t *pEnvBeam ) +{ + ; +} + +static void R_ProcessEntData( qboolean allocate, cl_entity_t *entities, unsigned int max_entities ) +{ + if (!allocate) + { + tr.draw_list->num_solid_entities = 0; + tr.draw_list->num_trans_entities = 0; + tr.draw_list->num_beam_entities = 0; + + tr.max_entities = 0; + tr.entities = NULL; + } + else + { + tr.max_entities = max_entities; + tr.entities = entities; + } + + if (gEngfuncs.drawFuncs->R_ProcessEntData) + gEngfuncs.drawFuncs->R_ProcessEntData(allocate); +} + +static void R_SetupSky( int *skytextures ) +{ + ; +} + +static void R_Set2DMode(qboolean enable) +{ + +} + +static void R_DrawStretchRaw( float x, float y, float w, float h, int cols, int rows, const byte *data, qboolean dirty ) +{ + ; +} + +static void R_DrawStretchPic( float x, float y, float w, float h, float s1, float t1, float s2, float t2, int texnum ) +{ + if (texnum <= 0 || texnum >= MAX_TEXTURES) + return; + + dx_texture_t *tex = R_GetTexture(texnum); + if (!tex->dds) + return; + + RECT dstRect = { x, y, x + w, y + h }; + RECT srcRect = { s1 * tex->width, t1 * tex->height, s2 * tex->width, t2 * tex->height}; + + if (dxc.renderMode == kRenderNormal) + { + DXCheck(IDirectDrawSurface_Blt(dxc.pddsBack, &dstRect, tex->dds, &srcRect, DDBLT_WAIT, NULL)); + } + else + { + DDCOLORKEY ddck; + ddck.dwColorSpaceLowValue = 0; + ddck.dwColorSpaceHighValue = ddck.dwColorSpaceLowValue; + DXCheck(IDirectDrawSurface_SetColorKey(tex->dds, DDCKEY_SRCBLT, &ddck)); + + DXCheck(IDirectDrawSurface_Blt(dxc.pddsBack, &dstRect, tex->dds, &srcRect, DDBLT_WAIT | DDBLT_KEYSRC, NULL)); + } +} + +static void FillRGBA( int rendermode, float x, float y, float w, float h, byte r, byte g, byte b, byte a ) +{ + RECT dstRect = { x, y, x + w, y + h }; + DDBLTFX bltFx = { 0 }; + bltFx.dwSize = sizeof(bltFx); + bltFx.dwFillColor = RGBA_MAKE(r, g, b, a); + DXCheck(IDirectDrawSurface_Blt(dxc.pddsBack, &dstRect, NULL, NULL, DDBLT_WAIT | DDBLT_COLORFILL, &bltFx)); +} + +static int WorldToScreen( const vec3_t world, vec3_t screen ) +{ + VectorClear( screen ); + return 0; +} + +static qboolean VID_ScreenShot( const char *filename, int shot_type ) +{ + return false; +} + +static qboolean VID_CubemapShot( const char *base, uint size, const float *vieworg, qboolean skyshot ) +{ + return false; +} + +static colorVec R_LightPoint( const float *p ) +{ + colorVec c = { 0 }; + return c; +} + +static void R_DecalShoot( int textureIndex, int entityIndex, int modelIndex, vec3_t pos, int flags, float scale ) +{ + ; +} + +static int R_CreateDecalList( struct decallist_s *pList ) +{ + return 0; +} + +static float R_StudioEstimateFrame( cl_entity_t *e, mstudioseqdesc_t *pseqdesc, double time ) +{ + return 0.0f; +} + +static void R_StudioLerpMovement( cl_entity_t *e, double time, vec3_t origin, vec3_t angles ) +{ + ; +} + +void CL_InitStudioAPI(void) +{ + ; +} + +static void R_SetSkyCloudsTextures( int solidskyTexture, int alphaskyTexture ) +{ + ; +} + +static void GL_SubdivideSurface( model_t *mod, msurface_t *fa ) +{ + ; +} + +static void CL_RunLightStyles( lightstyle_t *ls ) +{ + +} + +static void R_GetSpriteParms( int *frameWidth, int *frameHeight, int *numFrames, int currentFrame, const model_t *pSprite ) +{ + if( frameWidth ) + *frameWidth = 0; + + if( frameHeight ) + *frameHeight = 0; + + if( numFrames ) + *numFrames = 0; +} + +static int R_GetSpriteTexture( const model_t *m_pSpriteModel, int frame ) +{ + return 0; +} + +static qboolean Mod_ProcessRenderData( model_t *mod, qboolean create, const byte *buffer, size_t buffersize ) +{ + gEngfuncs.Con_Printf("Mod_ProcessRenderData(%p(%s), %d, %p, %zu)\n", mod, mod->name, create, buffer, buffersize); + return true; +} + +static void Mod_StudioLoadTextures( model_t *mod, void *data ) +{ + ; +} + +static void CL_DrawParticles( double frametime, particle_t *particles, float partsize ) +{ + ; +} + +static void CL_DrawTracers( double frametime, particle_t *tracers ) +{ + ; +} + +static void CL_DrawBeams( int fTrans, BEAM *beams ) +{ + ; +} + +static qboolean R_BeamCull( const vec3_t start, const vec3_t end, qboolean pvsOnly ) +{ + return false; +} + +static int RefGetParm(int parm, int arg) +{ + dx_texture_t *glt; + + switch (parm) + { + case PARM_TEX_WIDTH: + glt = R_GetTexture(arg); + return glt->width; + case PARM_TEX_HEIGHT: + glt = R_GetTexture(arg); + return glt->height; + case PARM_TEX_SRC_WIDTH: + glt = R_GetTexture(arg); + return glt->srcWidth; + case PARM_TEX_SRC_HEIGHT: + glt = R_GetTexture(arg); + return glt->srcHeight; + case PARM_TEX_GLFORMAT: + glt = R_GetTexture(arg); + return 0; + case PARM_TEX_ENCODE: + glt = R_GetTexture(arg); + return 0; + case PARM_TEX_MIPCOUNT: + glt = R_GetTexture(arg); + return glt->numMips; + case PARM_TEX_DEPTH: + glt = R_GetTexture(arg); + return 1; +#if 0 + case PARM_TEX_SKYBOX: + Assert(arg >= 0 && arg < 6); + return tr.skyboxTextures[arg]; + case PARM_TEX_SKYTEXNUM: + return tr.skytexturenum; + case PARM_TEX_LIGHTMAP: + arg = bound(0, arg, MAX_LIGHTMAPS - 1); + return tr.lightmapTextures[arg]; +#endif + case PARM_TEX_TARGET: + glt = R_GetTexture(arg); + return 0; + case PARM_TEX_TEXNUM: + glt = R_GetTexture(arg); + return arg; + case PARM_TEX_FLAGS: + glt = R_GetTexture(arg); + return glt->flags; +#if 0 + case PARM_TEX_MEMORY: + return GL_TexMemory(); + case PARM_ACTIVE_TMU: + return glState.activeTMU; + case PARM_LIGHTSTYLEVALUE: + arg = bound(0, arg, MAX_LIGHTSTYLES - 1); + return tr.lightstylevalue[arg]; + case PARM_MAX_IMAGE_UNITS: + return GL_MaxTextureUnits(); + case PARM_REBUILD_GAMMA: + return glConfig.softwareGammaUpdate; + case PARM_GL_CONTEXT_TYPE: + return glConfig.context; + case PARM_GLES_WRAPPER: + return glConfig.wrapper; + case PARM_STENCIL_ACTIVE: + return glState.stencilEnabled; +#endif + case PARM_TEX_FILTERING: + return 0; +#if 0 + if (arg < 0) + return gl_texture_nearest.value == 0.0f; + + return GL_TextureFilteringEnabled(R_GetTexture(arg)); +#endif + default: + return (*gEngfuncs.EngineGetParm)(parm, arg); + } + return 0; +} + + +static float GetFrameTime( void ) +{ + return 0.0f; +} + +static void R_SetCurrentEntity( struct cl_entity_s *ent ) +{ + ; +} + +static void R_SetCurrentModel( struct model_s *mod ) +{ + ; +} + +static void DrawSingleDecal( struct decal_s *pDecal, struct msurface_s *fa ) +{ + ; +} + +static float *R_DecalSetupVerts( struct decal_s *pDecal, struct msurface_s *surf, int texture, int *outCount ) +{ + return NULL; +} + +static void R_EntityRemoveDecals( struct model_s *mod ) +{ + ; +} + +static void AVI_UploadRawFrame( int texture, int cols, int rows, int width, int height, const byte *data ) +{ + ; +} + +static void GL_Bind( int tmu, unsigned int texnum ) +{ + ; +} + +static void GL_LoadTextureMatrix( const float *glmatrix ) +{ + ; +} + +static void GL_TexGen( unsigned int coord, unsigned int mode ) +{ + ; +} + +static void GL_UpdateTexSize( int texnum, int width, int height, int depth ) +{ + ; +} + +static void GL_DrawParticles( const struct ref_viewpass_s *rvp, qboolean trans_pass, float frametime ) +{ + ; +} + +static colorVec LightVec( const float *start, const float *end, float *lightspot, float *lightvec ) +{ + colorVec c = { 0 }; + return c; +} + +static struct mstudiotex_s *StudioGetTexture( struct cl_entity_s *e ) +{ + return NULL; +} + +void R_BeginFrame(qboolean clearScene) +{ + + gEngfuncs.CL_ExtraUpdate(); +} + +void R_EndFrame(void) +{ + //swap + if (!dxc.window) + return; + + if( dxc.pddsBack ) + { + RECT dstRect = {}; + POINT point = { 0, 0 }; + ClientToScreen( dxc.window, &point ); + GetClientRect( dxc.window, &dstRect ); + OffsetRect( &dstRect, point.x, point.y ); + + RECT srcRect = { 0, 0, dxc.windowWidth, dxc.windowHeight }; + DXCheck( IDirectDrawSurface_Blt( dxc.pddsPrimary, &dstRect, dxc.pddsBack, &srcRect, DDBLT_WAIT, NULL ) ); + } + + //resize + RECT rect = {}; + GetClientRect(dxc.window, &rect); + int w = rect.right - rect.left; + int h = rect.bottom - rect.top; + + if (w != dxc.windowWidth || h != dxc.windowHeight) + { + dxc.windowWidth = w; + dxc.windowHeight = h; + if( dxc.pddsBack ) + DXCheck(IDirectDrawSurface_Release(dxc.pddsBack)); + dxc.pddsBack = NULL; + if( dxc.pddBackClipper ) + DXCheck(IDirectDrawClipper_Release(dxc.pddBackClipper)); + dxc.pddBackClipper = NULL; + if (!DD_CreateBackSurface()) + dxc.window = 0; + } +} + +void GL_BackendStartFrame(void) +{ + +} + +void GL_BackendEndFrame(void) +{ + +} + + +static void R_SetupRefParams(const ref_viewpass_t *rvp) +{ +// RI.params = RP_NONE; + RI.drawWorld = FBitSet(rvp->flags, RF_DRAW_WORLD); + RI.onlyClientDraw = FBitSet(rvp->flags, RF_ONLY_CLIENTDRAW); + RI.farClip = 0; + + if (!FBitSet(rvp->flags, RF_DRAW_CUBEMAP)) + RI.drawOrtho = FBitSet(rvp->flags, RF_DRAW_OVERVIEW); + else RI.drawOrtho = false; + + // setup viewport + RI.viewport[0] = rvp->viewport[0]; + RI.viewport[1] = rvp->viewport[1]; + RI.viewport[2] = rvp->viewport[2]; + RI.viewport[3] = rvp->viewport[3]; + + // calc FOV + RI.fov_x = rvp->fov_x; + RI.fov_y = rvp->fov_y; + + VectorCopy(rvp->vieworigin, RI.vieworg); + VectorCopy(rvp->viewangles, RI.viewangles); + VectorCopy(rvp->vieworigin, RI.pvsorigin); +} + +static void R_RenderScene(void) +{ + model_t *worldmodel = gp_cl->models[1]; + if (!worldmodel && RI.drawWorld) + gEngfuncs.Host_Error("%s: NULL worldmodel\n", __func__); + + // frametime is valid only for normal pass + if (FBitSet(RI.params, RP_ENVVIEW) == 0) + tr.frametime = gp_cl->time - gp_cl->oldtime; + else tr.frametime = 0.0; + + // begin a new frame + tr.framecount++; + +#if 0 + R_PushDlights(); + + R_SetupFrustum(); + R_SetupFrame(); + R_SetupGL(true); + R_Clear(~0); + + R_MarkLeaves(); + R_DrawFog(); + if (RI.drawWorld) + R_AnimateRipples(); + + R_CheckGLFog(); + R_DrawWorld(); + R_CheckFog(); + + gEngfuncs.CL_ExtraUpdate(); // don't let sound get messed up if going slow + + R_DrawEntitiesOnList(); + + R_DrawWaterSurfaces(); +#endif +} + +static void R_RenderFrame( const struct ref_viewpass_s *rvp ) +{ + if (r_norefresh->value) + return; + + // setup the initial render params + R_SetupRefParams(rvp); + + // completely override rendering + if (gEngfuncs.drawFuncs->GL_RenderFrame != NULL) + { + tr.fCustomRendering = true; + + if (gEngfuncs.drawFuncs->GL_RenderFrame(rvp)) + { + //R_GatherPlayerLight(); + tr.realframecount++; + tr.fResetVis = true; + return; + } + } + + tr.fCustomRendering = false; + //if (!RI.onlyClientDraw) + // R_RunViewmodelEvents(); + + tr.realframecount++; // right called after viewmodel events + R_RenderScene(); +} + +static void GL_OrthoBounds( const float *mins, const float *maxs ) +{ + ; +} + +static qboolean R_SpeedsMessage( char *out, size_t size ) +{ + return false; +} + +static byte *Mod_GetCurrentVis( void ) +{ + return NULL; +} + +static void R_NewMap(void) +{ + + if (gEngfuncs.drawFuncs->R_NewMap != NULL) + gEngfuncs.drawFuncs->R_NewMap(); +} + +static void *R_GetProcAddress( const char *name ) +{ + return NULL; +} + +static void Color4f( float r, float g, float b, float a ) +{ + ; +} + +static void Color4ub( unsigned char r, unsigned char g, unsigned char b, unsigned char a ) +{ + ; +} + +static void TexCoord2f( float u, float v ) +{ + ; +} + +static void Vertex3fv( const float *worldPnt ) +{ + ; +} + +static void Vertex3f( float x, float y, float z ) +{ + ; +} + +static void Fog( float flFogColor[3], float flStart, float flEnd, int bOn ) +{ + ; +} + +static void ScreenToWorld( const float *screen, float *world ) +{ + ; +} + +static void GetMatrix( const int pname, float *matrix ) +{ + ; +} + +static void FogParams( float flDensity, int iFogSkybox ) +{ + ; +} + +static void CullFace( TRICULLSTYLE mode ) +{ + ; +} + +static void VGUI_SetupDrawing( qboolean rect ) +{ + ; +} + +static void VGUI_UploadTextureBlock( int drawX, int drawY, const byte *rgba, int blockWidth, int blockHeight ) +{ + ; +} + +static const ref_interface_t gReffuncs = +{ + .R_Init = R_Init, + .R_Shutdown = R_Shutdown, + .R_GetConfigName = R_GetConfigName, + .R_SetDisplayTransform = R_SetDisplayTransform, + + .GL_SetupAttributes = GL_SetupAttributes, + .GL_InitExtensions = GL_InitExtensions, + .GL_ClearExtensions = GL_ClearExtensions, + + .R_GammaChanged = R_SimpleStubBool, + .R_BeginFrame = R_BeginFrame, + .R_RenderScene = R_RenderScene, + .R_EndFrame = R_EndFrame, + .R_PushScene = R_SimpleStub, + .R_PopScene = R_SimpleStub, + .GL_BackendStartFrame = GL_BackendStartFrame, + .GL_BackendEndFrame = GL_BackendEndFrame, + + .R_ClearScreen = R_SimpleStub, + .R_AllowFog = R_AllowFog, + .GL_SetRenderMode = GL_SetRenderMode, + + .R_AddEntity = R_AddEntity, + .CL_AddCustomBeam = CL_AddCustomBeam, + .R_ProcessEntData = R_ProcessEntData, + .R_Flush = R_SimpleStubUInt, + + .R_ShowTextures = R_ShowTextures, + + .R_GetTextureOriginalBuffer = R_GetTextureOriginalBuffer, + .GL_LoadTextureFromBuffer = GL_LoadTextureFromBuffer, + .GL_ProcessTexture = GL_ProcessTexture, + .R_SetupSky = R_SetupSky, + + .R_Set2DMode = R_Set2DMode, + .R_DrawStretchRaw = R_DrawStretchRaw, + .R_DrawStretchPic = R_DrawStretchPic, + .FillRGBA = FillRGBA, + .WorldToScreen = WorldToScreen, + + .VID_ScreenShot = VID_ScreenShot, + .VID_CubemapShot = VID_CubemapShot, + + .R_LightPoint = R_LightPoint, + + .R_DecalShoot = R_DecalShoot, + .R_DecalRemoveAll = R_SimpleStubInt, + .R_CreateDecalList = R_CreateDecalList, + .R_ClearAllDecals = R_SimpleStub, + + .R_StudioEstimateFrame = R_StudioEstimateFrame, + .R_StudioLerpMovement = R_StudioLerpMovement, + .CL_InitStudioAPI = CL_InitStudioAPI, + + .R_SetSkyCloudsTextures = R_SetSkyCloudsTextures, + .GL_SubdivideSurface = GL_SubdivideSurface, + .CL_RunLightStyles = CL_RunLightStyles, + + .R_GetSpriteParms = R_GetSpriteParms, + .R_GetSpriteTexture = R_GetSpriteTexture, + + .Mod_ProcessRenderData = Mod_ProcessRenderData, + .Mod_StudioLoadTextures = Mod_StudioLoadTextures, + + .CL_DrawParticles = CL_DrawParticles, + .CL_DrawTracers = CL_DrawTracers, + .CL_DrawBeams = CL_DrawBeams, + .R_BeamCull = R_BeamCull, + + .RefGetParm = RefGetParm, + .GetDetailScaleForTexture = GetDetailScaleForTexture, + .GetExtraParmsForTexture = GetExtraParmsForTexture, + .GetFrameTime = GetFrameTime, + + .R_SetCurrentEntity = R_SetCurrentEntity, + .R_SetCurrentModel = R_SetCurrentModel, + + .GL_FindTexture = GL_FindTexture, + .GL_TextureName = GL_TextureName, + .GL_TextureData = GL_TextureData, + .GL_LoadTexture = GL_LoadTexture, + .GL_CreateTexture = GL_CreateTexture, + .GL_LoadTextureArray = GL_LoadTextureArray, + .GL_CreateTextureArray = GL_CreateTextureArray, + .GL_FreeTexture = GL_FreeTexture, + .R_OverrideTextureSourceSize = R_OverrideTextureSourceSize, + + .DrawSingleDecal = DrawSingleDecal, + .R_DecalSetupVerts = R_DecalSetupVerts, + .R_EntityRemoveDecals = R_EntityRemoveDecals, + + .AVI_UploadRawFrame = AVI_UploadRawFrame, + + .GL_Bind = GL_Bind, + .GL_SelectTexture = R_SimpleStubInt, + .GL_LoadTextureMatrix = GL_LoadTextureMatrix, + .GL_TexMatrixIdentity = R_SimpleStub, + .GL_CleanUpTextureUnits = R_SimpleStubInt, + .GL_TexGen = GL_TexGen, + .GL_TextureTarget = R_SimpleStubUInt, + .GL_TexCoordArrayMode = R_SimpleStubUInt, + .GL_UpdateTexSize = GL_UpdateTexSize, + .GL_Reserved0 = NULL, + .GL_Reserved1 = NULL, + + .GL_DrawParticles = GL_DrawParticles, + .LightVec = LightVec, + .StudioGetTexture = StudioGetTexture, + + .GL_RenderFrame = R_RenderFrame, + .GL_OrthoBounds = GL_OrthoBounds, + .R_SpeedsMessage = R_SpeedsMessage, + .Mod_GetCurrentVis = Mod_GetCurrentVis, + .R_NewMap = R_NewMap, + .R_ClearScene = R_ClearScene, + .R_GetProcAddress = R_GetProcAddress, + + .TriRenderMode = R_SimpleStubInt, + .Begin = R_SimpleStubInt, + .End = R_SimpleStub, + .Color4f = Color4f, + .Color4ub = Color4ub, + .TexCoord2f = TexCoord2f, + .Vertex3fv = Vertex3fv, + .Vertex3f = Vertex3f, + .Fog = Fog, + .ScreenToWorld = ScreenToWorld, + .GetMatrix = GetMatrix, + .FogParams = FogParams, + .CullFace = CullFace, + + .VGUI_SetupDrawing = VGUI_SetupDrawing, + .VGUI_UploadTextureBlock = VGUI_UploadTextureBlock, +}; + +int EXPORT GetRefAPI( int version, ref_interface_t *funcs, ref_api_t *engfuncs, ref_globals_t *globals ); +int EXPORT GetRefAPI( int version, ref_interface_t *funcs, ref_api_t *engfuncs, ref_globals_t *globals ) +{ + if( version != REF_API_VERSION ) + return 0; + + // fill in our callbacks + *funcs = gReffuncs; + gEngfuncs = *engfuncs; + gpGlobals = globals; + gp_cl = (ref_client_t *)gEngfuncs.EngineGetParm(PARM_GET_CLIENT_PTR, 0); + + return REF_API_VERSION; +} diff --git a/ref/dx2/r_image.c b/ref/dx2/r_image.c new file mode 100644 index 00000000..860f7bc6 --- /dev/null +++ b/ref/dx2/r_image.c @@ -0,0 +1,722 @@ +/* +r_context.c -- dx2 renderer image +Copyright (C) 2025 lewa_j + +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 +(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 General Public License for more details. +*/ + +#include "r_local.h" +#include "com_strings.h" +#include "xash3d_mathlib.h" +#include "crtlib.h" +#include "crclib.h" + +#define Assert(x) if(!( x )) gEngfuncs.Host_Error( "assert failed at %s:%i\n", __FILE__, __LINE__ ) + + +static dx_texture_t dx_textures[MAX_TEXTURES]; +static dx_texture_t *dx_texturesHashTable[TEXTURES_HASH_SIZE]; +static uint dx_numTextures; + +static rgbdata_t *GL_FakeImage(int width, int height, int depth, int flags) +{ + static byte data2D[1024]; // 16x16x4 + static rgbdata_t r_image; + + // also use this for bad textures, but without alpha + r_image.width = Q_max(1, width); + r_image.height = Q_max(1, height); + r_image.depth = Q_max(1, depth); + r_image.flags = flags; + r_image.type = PF_BGRA_32; + r_image.size = r_image.width * r_image.height * r_image.depth * 4; + r_image.buffer = (r_image.size > sizeof(data2D)) ? NULL : data2D; + r_image.palette = NULL; + r_image.numMips = 1; + r_image.encode = 0; + + if (FBitSet(r_image.flags, IMAGE_CUBEMAP)) + r_image.size *= 6; + memset(data2D, 0xFF, sizeof(data2D)); + + return &r_image; +} + +static byte dottexture[8][8] = +{ + {0,1,1,0,0,0,0,0}, + {1,1,1,1,0,0,0,0}, + {1,1,1,1,0,0,0,0}, + {0,1,1,0,0,0,0,0}, + {0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0}, +}; + +static void GL_CreateInternalTextures(void) +{ + int dx2, dy, d; + int x, y; + rgbdata_t *pic; + + // emo-texture from quake1 + pic = GL_FakeImage(16, 16, 1, IMAGE_HAS_COLOR); + + for (y = 0; y < 16; y++) + { + for (x = 0; x < 16; x++) + { + if ((y < 8) ^ (x < 8)) + ((uint *)pic->buffer)[y * 16 + x] = 0xFFFF00FF; + else ((uint *)pic->buffer)[y * 16 + x] = 0xFF000000; + } + } + + tr.defaultTexture = GL_LoadTextureFromBuffer(REF_DEFAULT_TEXTURE, pic, TF_COLORMAP, false); + + // particle texture from quake1 + pic = GL_FakeImage(8, 8, 1, IMAGE_HAS_COLOR | IMAGE_HAS_ALPHA); + + for (x = 0; x < 8; x++) + { + for (y = 0; y < 8; y++) + { + if (dottexture[x][y]) + pic->buffer[(y * 8 + x) * 4 + 3] = 255; + else pic->buffer[(y * 8 + x) * 4 + 3] = 0; + } + } + + tr.particleTexture = GL_LoadTextureFromBuffer(REF_PARTICLE_TEXTURE, pic, TF_CLAMP, false); + + // white texture + pic = GL_FakeImage(4, 4, 1, IMAGE_HAS_COLOR); + for (x = 0; x < 16; x++) + ((uint *)pic->buffer)[x] = 0xFFFFFFFF; + tr.whiteTexture = GL_LoadTextureFromBuffer(REF_WHITE_TEXTURE, pic, TF_COLORMAP, false); + + // gray texture + pic = GL_FakeImage(4, 4, 1, IMAGE_HAS_COLOR); + for (x = 0; x < 16; x++) + ((uint *)pic->buffer)[x] = 0xFF7F7F7F; + tr.grayTexture = GL_LoadTextureFromBuffer(REF_GRAY_TEXTURE, pic, TF_COLORMAP, false); + + // black texture + pic = GL_FakeImage(4, 4, 1, IMAGE_HAS_COLOR); + for (x = 0; x < 16; x++) + ((uint *)pic->buffer)[x] = 0xFF000000; + tr.blackTexture = GL_LoadTextureFromBuffer(REF_BLACK_TEXTURE, pic, TF_COLORMAP, false); + + // cinematic dummy + pic = GL_FakeImage(640, 100, 1, IMAGE_HAS_COLOR); + tr.cinTexture = GL_LoadTextureFromBuffer("*cintexture", pic, TF_NOMIPMAP | TF_CLAMP, false); +} + +void R_InitImages(void) +{ + memset(dx_textures, 0, sizeof(dx_textures)); + memset(dx_texturesHashTable, 0, sizeof(dx_texturesHashTable)); + dx_numTextures = 0; + + // create unused 0-entry + Q_strncpy(dx_textures->name, "*unused*", sizeof(dx_textures->name)); + dx_textures->hashValue = COM_HashKey(dx_textures->name, TEXTURES_HASH_SIZE); + dx_textures->nextHash = dx_texturesHashTable[dx_textures->hashValue]; + dx_texturesHashTable[dx_textures->hashValue] = dx_textures; + dx_numTextures = 1; + + GL_CreateInternalTextures(); +} + +dx_texture_t *R_GetTexture(unsigned int texnum) +{ + Assert(texnum < MAX_TEXTURES); + return &dx_textures[texnum]; +} + +void R_ShowTextures( void ) +{ + +} + +const byte *R_GetTextureOriginalBuffer(unsigned int idx) +{ + return NULL; +} + +static qboolean GL_CheckTexName(const char *name) +{ + int len; + + if( COM_StringEmptyOrNULL( name )) + return false; + + len = Q_strlen(name); + + // because multi-layered textures can exceed name string + if (len >= sizeof(dx_textures->name)) + { + gEngfuncs.Con_Printf(S_ERROR "LoadTexture: too long name %s (%d)\n", name, len); + return false; + } + + return true; +} + +static dx_texture_t *GL_TextureForName(const char *name) +{ + dx_texture_t *tex; + uint hash; + + // find the texture in array + hash = COM_HashKey(name, TEXTURES_HASH_SIZE); + + for (tex = dx_texturesHashTable[hash]; tex != NULL; tex = tex->nextHash) + { + if (!Q_stricmp(tex->name, name)) + return tex; + } + + return NULL; +} + +static dx_texture_t *GL_AllocTexture(const char *name, texFlags_t flags) +{ + dx_texture_t *tex = NULL; + + // find a free texture_t slot + uint i; + + //skip 0 + for (i = 1; i < MAX_TEXTURES; i++) + { + if (dx_textures[i].used) + continue; + + tex = &dx_textures[i]; + break; + } + + if (tex == NULL) + { + gEngfuncs.Host_Error("%s: MAX_TEXTURES limit exceeds\n", __func__); + return NULL; + } + + // copy initial params + Q_strncpy(tex->name, name, sizeof(tex->name)); + tex->used = 1; + tex->flags = flags; + + // increase counter + dx_numTextures = Q_max((tex - dx_textures) + 1, dx_numTextures); + + // add to hash table + tex->hashValue = COM_HashKey(name, TEXTURES_HASH_SIZE); + tex->nextHash = dx_texturesHashTable[tex->hashValue]; + dx_texturesHashTable[tex->hashValue] = tex; + + return tex; +} + +static void GL_ProcessImage(dx_texture_t *tex, rgbdata_t *pic) +{ + uint img_flags = 0; + + // force upload texture as RGB or RGBA (detail textures requires this) + if (tex->flags & TF_FORCE_COLOR) pic->flags |= IMAGE_HAS_COLOR; + if (pic->flags & IMAGE_HAS_ALPHA) tex->flags |= TF_HAS_ALPHA; + + if (ImageCompressed(pic->type)) + { + if (!pic->numMips) + tex->flags |= TF_NOMIPMAP; // disable mipmapping by user request + + // clear all the unsupported flags + tex->flags &= ~TF_KEEP_SOURCE; + } + else + { + // copy flag about luma pixels + if (pic->flags & IMAGE_HAS_LUMA) + tex->flags |= TF_HAS_LUMA; + + if (pic->flags & IMAGE_QUAKEPAL) + tex->flags |= TF_QUAKEPAL; + + // create luma texture from quake texture + if (tex->flags & TF_MAKELUMA) + { + img_flags |= IMAGE_MAKE_LUMA; + tex->flags &= ~TF_MAKELUMA; + } + + if (!FBitSet(tex->flags, TF_IMG_UPLOADED) && FBitSet(tex->flags, TF_KEEP_SOURCE)) + tex->original = gEngfuncs.FS_CopyImage(pic); // because current pic will be expanded to rgba + + // we need to expand image into RGBA buffer + if (pic->type == PF_INDEXED_24 || pic->type == PF_INDEXED_32) + img_flags |= IMAGE_FORCE_RGBA; + + // processing image before uploading (force to rgba, make luma etc) + if (pic->buffer) gEngfuncs.Image_Process(&pic, 0, 0, img_flags, 0); + + if (FBitSet(tex->flags, TF_LUMINANCE)) + ClearBits(pic->flags, IMAGE_HAS_COLOR); + } +} + +static int GL_CalcMipmapCount(dx_texture_t *tex, qboolean haveBuffer) +{ + int width, height; + int mipcount; + + Assert(tex != NULL); + + //TEMP + return 1; + + if (!haveBuffer) + return 1; + + // generate mip-levels by user request + if (FBitSet(tex->flags, TF_NOMIPMAP)) + return 1; + + // mip-maps can't exceeds 16 + for (mipcount = 0; mipcount < 16; mipcount++) + { + width = Q_max(1, (tex->width >> mipcount)); + height = Q_max(1, (tex->height >> mipcount)); + if (width == 1 && height == 1) + break; + } + + return mipcount + 1; +} + +static byte *GL_ResampleTexture(const byte *source, int inWidth, int inHeight, int outWidth, int outHeight, qboolean isNormalMap) +{ + uint frac, fracStep; + uint *in = (uint *)source; + uint p1[0x1000], p2[0x1000]; + byte *pix1, *pix2, *pix3, *pix4; + uint *out, *inRow1, *inRow2; + static byte *scaledImage = NULL; // pointer to a scaled image + vec3_t normal; + int i, x, y; + + if (!source) return NULL; + + scaledImage = Mem_Realloc(r_temppool, scaledImage, outWidth * outHeight * 4); + fracStep = inWidth * 0x10000 / outWidth; + out = (uint *)scaledImage; + + frac = fracStep >> 2; + for (i = 0; i < outWidth; i++) + { + p1[i] = 4 * (frac >> 16); + frac += fracStep; + } + + frac = (fracStep >> 2) * 3; + for (i = 0; i < outWidth; i++) + { + p2[i] = 4 * (frac >> 16); + frac += fracStep; + } + + if (isNormalMap) + { + for (y = 0; y < outHeight; y++, out += outWidth) + { + inRow1 = in + inWidth * (int)(((float)y + 0.25f) * inHeight / outHeight); + inRow2 = in + inWidth * (int)(((float)y + 0.75f) * inHeight / outHeight); + + for (x = 0; x < outWidth; x++) + { + pix1 = (byte *)inRow1 + p1[x]; + pix2 = (byte *)inRow1 + p2[x]; + pix3 = (byte *)inRow2 + p1[x]; + pix4 = (byte *)inRow2 + p2[x]; + + normal[0] = MAKE_SIGNED(pix1[0]) + MAKE_SIGNED(pix2[0]) + MAKE_SIGNED(pix3[0]) + MAKE_SIGNED(pix4[0]); + normal[1] = MAKE_SIGNED(pix1[1]) + MAKE_SIGNED(pix2[1]) + MAKE_SIGNED(pix3[1]) + MAKE_SIGNED(pix4[1]); + normal[2] = MAKE_SIGNED(pix1[2]) + MAKE_SIGNED(pix2[2]) + MAKE_SIGNED(pix3[2]) + MAKE_SIGNED(pix4[2]); + + if (!VectorNormalizeLength(normal)) + VectorSet(normal, 0.5f, 0.5f, 1.0f); + + ((byte *)(out + x))[0] = 128 + (byte)(127.0f * normal[0]); + ((byte *)(out + x))[1] = 128 + (byte)(127.0f * normal[1]); + ((byte *)(out + x))[2] = 128 + (byte)(127.0f * normal[2]); + ((byte *)(out + x))[3] = 255; + } + } + } + else + { + for (y = 0; y < outHeight; y++, out += outWidth) + { + inRow1 = in + inWidth * (int)(((float)y + 0.25f) * inHeight / outHeight); + inRow2 = in + inWidth * (int)(((float)y + 0.75f) * inHeight / outHeight); + + for (x = 0; x < outWidth; x++) + { + pix1 = (byte *)inRow1 + p1[x]; + pix2 = (byte *)inRow1 + p2[x]; + pix3 = (byte *)inRow2 + p1[x]; + pix4 = (byte *)inRow2 + p2[x]; + + ((byte *)(out + x))[0] = (pix1[0] + pix2[0] + pix3[0] + pix4[0]) >> 2; + ((byte *)(out + x))[1] = (pix1[1] + pix2[1] + pix3[1] + pix4[1]) >> 2; + ((byte *)(out + x))[2] = (pix1[2] + pix2[2] + pix3[2] + pix4[2]) >> 2; + ((byte *)(out + x))[3] = (pix1[3] + pix2[3] + pix3[3] + pix4[3]) >> 2; + } + } + } + + return scaledImage; +} + +static void GL_TextureImageRAW(dx_texture_t *tex, int width, int height, int type, const char *data) +{ + if (!data) + return; + + DDSURFACEDESC ddsd = {}; + ZeroMemory(&ddsd, sizeof(ddsd)); + ddsd.dwSize = sizeof(ddsd); + ddsd.dwFlags = DDSD_LPSURFACE; + DXCheck(IDirectDrawSurface_Lock(tex->dds, NULL, &ddsd, DDLOCK_SURFACEMEMORYPTR, NULL)); + + int pitch = ddsd.lPitch; + if (!pitch) + pitch = ddsd.dwWidth * 4; + char *dst = (char *)ddsd.lpSurface; + + for (int j = 0; j < (int)ddsd.dwHeight; j++) + { + //RGBA to BGRA + if (type == PF_RGBA_32) + { + for (int i = 0; i < ddsd.dwWidth; i++) + { + dst[i * 4 + 0] = data[i * 4 + 2]; + dst[i * 4 + 1] = data[i * 4 + 1]; + dst[i * 4 + 2] = data[i * 4 + 0]; + dst[i * 4 + 3] = data[i * 4 + 3]; + } + } + else if (type == PF_BGRA_32) + { + memcpy(dst, data, ddsd.dwWidth * 4); + } + + dst += pitch; + data += ddsd.dwWidth * 4; + } + + DXCheck(IDirectDrawSurface_Unlock(tex->dds, ddsd.lpSurface)); +} + +static qboolean GL_UploadTexture(dx_texture_t *tex, rgbdata_t *pic) +{ + byte *buf, *data; + size_t texsize, size; + uint width, height; + uint i, j, numSides; + qboolean normalMap; + const byte *bufend; + + // dedicated server + if (!dxc.window) + return true; + + Assert(pic != NULL); + Assert(tex != NULL); + + qboolean supported = true; + + if (FBitSet(pic->flags, IMAGE_CUBEMAP)) + supported = false; + else if (FBitSet(pic->flags, IMAGE_MULTILAYER) && pic->depth >= 1) + supported = false; + else if (pic->width > 1 && pic->height > 1 && pic->depth > 1) + supported = false; + else if (FBitSet(tex->flags, TF_RECTANGLE)) + supported = false; + else if (FBitSet(tex->flags, TF_MULTISAMPLE)) + supported = false; + + // make sure what target is correct + if (!supported) + { + gEngfuncs.Con_DPrintf(S_ERROR "%s: %s is not supported\n", __func__, tex->name); + return false; + } + + //if (pic->type == PF_BC6H_SIGNED || pic->type == PF_BC6H_UNSIGNED || pic->type == PF_BC7_UNORM || pic->type == PF_BC7_SRGB) + if (ImageCompressed(pic->type)) + { + gEngfuncs.Con_DPrintf(S_ERROR "%s: compressed textures are not supported\n", __func__); + return false; + } + + // store original sizes + tex->srcWidth = pic->width; + tex->srcHeight = pic->height; + + // set the texture dimensions + { + int step = 0;// (int)gl_round_down.value; + int scaled_width, scaled_height; + + for (scaled_width = 1; scaled_width < pic->width; scaled_width <<= 1); + + if (step > 0 && pic->width < scaled_width && (step == 1 || (scaled_width - pic->width) > (scaled_width >> step))) + scaled_width >>= 1; + + for (scaled_height = 1; scaled_height < pic->height; scaled_height <<= 1); + + if (step > 0 && pic->height < scaled_height && (step == 1 || (scaled_height - pic->height) > (scaled_height >> step))) + scaled_height >>= 1; + + tex->width = Q_max(1, scaled_width); + tex->height = Q_max(1, scaled_height); + } + + DDSURFACEDESC ddsd = {}; + ZeroMemory(&ddsd, sizeof(ddsd)); + ddsd.dwSize = sizeof(ddsd); + ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT; + ddsd.ddsCaps.dwCaps = DDSCAPS_TEXTURE; + //if (pic->numMips > 1) + // ddsd.ddsCaps.dwCaps |= DDSCAPS_MIPMAP | DDSCAPS_COMPLEX; + ddsd.dwWidth = tex->width; + ddsd.dwHeight = tex->height; + +#if 0 + //RGBA8 + ddsd.dwFlags |= DDSD_PIXELFORMAT; + ddsd.ddpfPixelFormat.dwSize = sizeof(ddsd.ddpfPixelFormat); + ddsd.ddpfPixelFormat.dwFlags = DDPF_RGB | DDPF_ALPHAPIXELS; + ddsd.ddpfPixelFormat.dwRGBBitCount = 32; + ddsd.ddpfPixelFormat.dwRBitMask = 0xFF; + ddsd.ddpfPixelFormat.dwGBitMask = 0xFF00; + ddsd.ddpfPixelFormat.dwBBitMask = 0xFF0000; + ddsd.ddpfPixelFormat.dwRGBAlphaBitMask = 0xFF000000; +#endif + + DXCheck(IDirectDraw_CreateSurface(dxc.pdd, &ddsd, &tex->dds, NULL)); + + tex->fogParams[0] = pic->fogParams[0]; + tex->fogParams[1] = pic->fogParams[1]; + tex->fogParams[2] = pic->fogParams[2]; + tex->fogParams[3] = pic->fogParams[3]; + + buf = pic->buffer; + bufend = pic->buffer + pic->size; // total image size include all the layers, cube sides, mipmaps + normalMap = FBitSet(tex->flags, TF_NORMALMAP) ? true : false; + + if (Q_max(1, pic->numMips) > 1) // not-compressed DDS + { + //TEMP + j = 0; + //for (j = 0; j < Q_max(1, pic->numMips); j++) + { + // track the buffer bounds + if (buf != NULL && buf >= bufend) + gEngfuncs.Host_Error("%s: %s image buffer overflow\n", __func__, tex->name); + + width = Q_max(1, (tex->width >> j)); + height = Q_max(1, (tex->height >> j)); + size = gEngfuncs.Image_CalcImageSize(pic->type, width, height, 1); + GL_TextureImageRAW(tex, width, height, pic->type, buf); + buf += size; // move pointer + tex->numMips++; + } + } + else // RGBA32 + { + // track the buffer bounds + if (buf != NULL && buf >= bufend) + gEngfuncs.Host_Error("%s: %s image buffer overflow\n", __func__, tex->name); + + int mipCount = GL_CalcMipmapCount(tex, (buf != NULL)); + + // NOTE: only single uncompressed textures can be resamples, no mips, no layers, no sides + if (((pic->width != tex->width) || (pic->height != tex->height))) + data = GL_ResampleTexture(buf, pic->width, pic->height, tex->width, tex->height, normalMap); + else data = buf; + + //if (!ImageCompressed(pic->type) && !FBitSet(tex->flags, TF_NOMIPMAP) && FBitSet(pic->flags, IMAGE_ONEBIT_ALPHA)) + // data = GL_ApplyFilter(data, tex->width, tex->height); + + // mips will be auto-generated if desired + for (j = 0; j < mipCount; j++) + { + width = Q_max(1, (tex->width >> j)); + height = Q_max(1, (tex->height >> j)); + size = gEngfuncs.Image_CalcImageSize(pic->type, width, height, 1); + GL_TextureImageRAW(tex, width, height, pic->type, data); + //if (mipCount > 1) + // GL_BuildMipMap(data, width, height, tex->depth, tex->flags); + tex->numMips++; + } + } + + SetBits(tex->flags, TF_IMG_UPLOADED); // done + + return true; +} + +int GL_LoadTextureFromBuffer(const char *name, rgbdata_t *pic, texFlags_t flags, qboolean update) +{ + dx_texture_t *tex; + + if (!GL_CheckTexName(name)) + return 0; + + // see if already loaded + if ((tex = GL_TextureForName(name)) && !update) + return (tex - dx_textures); + + // couldn't loading image + if (!pic) return 0; + + if (update) + { + if (tex == NULL) + gEngfuncs.Host_Error("%s: couldn't find texture %s for update\n", __func__, name); + SetBits(tex->flags, flags); + } + else + { + // allocate the new one + tex = GL_AllocTexture(name, flags); + } + + GL_ProcessImage(tex, pic); + + if (!GL_UploadTexture(tex, pic)) + { + memset(tex, 0, sizeof(dx_texture_t)); + return 0; + } + + return (tex - dx_textures); +} + +void GL_ProcessTexture(int texnum, float gamma, int topColor, int bottomColor) +{ + ; +} + +void GetDetailScaleForTexture(int texture, float *xScale, float *yScale) +{ + *xScale = *yScale = 1.0f; +} + +void GetExtraParmsForTexture(int texture, byte *red, byte *green, byte *blue, byte *alpha) +{ + *red = *green = *blue = *alpha = 0; +} + +int GL_FindTexture(const char *name) +{ + gEngfuncs.Con_Printf("GL_FindTexture(%s)\n", name); + return 0; +} + +const char *GL_TextureName(unsigned int texnum) +{ + return NULL; +} + +const byte *GL_TextureData(unsigned int texnum) +{ + return NULL; +} + +int GL_LoadTexture( const char *name, const byte *buf, size_t size, int flags ) +{ + dx_texture_t *tex; + rgbdata_t *pic; + uint picFlags = 0; + + if (!GL_CheckTexName(name)) + return 0; + + // see if already loaded + if ((tex = GL_TextureForName(name))) + return (tex - dx_textures); + + gEngfuncs.Con_Printf("GL_LoadTexture(%s, %p, %zu, %X)\n", name, buf, size, flags); + + if (FBitSet(flags, TF_NOFLIP_TGA)) + SetBits(picFlags, IL_DONTFLIP_TGA); + + if (FBitSet(flags, TF_KEEP_SOURCE) && !FBitSet(flags, TF_EXPAND_SOURCE)) + SetBits(picFlags, IL_KEEP_8BIT); + + // set some image flags + gEngfuncs.Image_SetForceFlags(picFlags); + + pic = gEngfuncs.FS_LoadImage(name, buf, size); + if (!pic) return 0; // couldn't loading image + + // allocate the new one + tex = GL_AllocTexture(name, flags); + GL_ProcessImage(tex, pic); + + if (!GL_UploadTexture(tex, pic)) + { + memset(tex, 0, sizeof(dx_texture_t)); + gEngfuncs.FS_FreeImage(pic); // release source texture + return 0; + } + + //GL_ApplyTextureParams(tex); // update texture filter, wrap etc + gEngfuncs.FS_FreeImage(pic); // release source texture + + // NOTE: always return texnum as index in array or engine will stop work !!! + return tex - dx_textures; +} + +int GL_CreateTexture(const char *name, int width, int height, const void *buffer, texFlags_t flags) +{ + return 0; +} + +int GL_LoadTextureArray(const char **names, int flags) +{ + return 0; +} + +int GL_CreateTextureArray(const char *name, int width, int height, int depth, const void *buffer, texFlags_t flags) +{ + return 0; +} + +void GL_FreeTexture(unsigned int texnum) +{ + if (texnum <= 0 || texnum >= MAX_TEXTURES) + return; + + gEngfuncs.Con_Printf("GL_FreeTexture(%d) %s\n", texnum, dx_textures[texnum].name); +} + +void R_OverrideTextureSourceSize(unsigned int textnum, unsigned int srcWidth, unsigned int srcHeight) +{ + +} diff --git a/ref/dx2/r_local.h b/ref/dx2/r_local.h new file mode 100644 index 00000000..00aadd7d --- /dev/null +++ b/ref/dx2/r_local.h @@ -0,0 +1,220 @@ +#pragma once + +#include "const.h" +#include "cvardef.h" +#include "ref_api.h" +#include "render_api.h" +#include "com_image.h" + +#define DIRECTDRAW_VERSION 0x0200 +#define DIRECT3D_VERSION 0x0500//D3DCOLORMODEL bug +#include +#include + +extern ref_api_t gEngfuncs; + +extern poolhandle_t r_temppool; + + +#define MAX_TEXTURES 8192 +#define TEXTURES_HASH_SIZE (MAX_TEXTURES >> 2) + +// refparams +#define RP_NONE 0 +#define RP_ENVVIEW BIT( 0 ) // used for cubemapshot +#define RP_OLDVIEWLEAF BIT( 1 ) +#define RP_CLIPPLANE BIT( 2 ) + +#define MAX_DRAW_STACK 2 // normal view and menu view + +typedef struct dx_texture_s +{ + char name[256]; // game path, including extension (can be store image programs) + word srcWidth; // keep unscaled sizes + word srcHeight; + word width; // upload width\height + word height; + + byte numMips; // mipmap count + + int used; + texFlags_t flags; + rgba_t fogParams; // some water textures + // contain info about underwater fog + rgbdata_t *original; // keep original image +#if 0 + word depth; // texture depth or count of layers for 2D_ARRAY + + GLuint target; // glTarget + GLuint texnum; // gl texture binding + GLint format; // uploaded format + + // debug info + size_t size; // upload size for debug targets + + // detail textures stuff + float xscale; + float yscale; + +#endif + IDirectDrawSurface *dds; + uint hashValue; + struct dx_texture_s *nextHash; +} dx_texture_t; + +typedef struct +{ + int params; // rendering parameters + + qboolean drawWorld; // ignore world for drawing PlayerModel + qboolean isSkyVisible; // sky is visible + qboolean onlyClientDraw; // disabled by client request + qboolean drawOrtho; // draw world as orthogonal projection + + float fov_x, fov_y; // current view fov + + cl_entity_t *currententity; + model_t *currentmodel; + cl_entity_t *currentbeam; // same as above but for beams + + int viewport[4]; + //gl_frustum_t frustum; + + mleaf_t *viewleaf; + mleaf_t *oldviewleaf; + vec3_t pvsorigin; + vec3_t vieworg; // locked vieworigin + vec3_t viewangles; + vec3_t vforward; + vec3_t vright; + vec3_t vup; + + vec3_t cullorigin; + vec3_t cull_vforward; + vec3_t cull_vright; + vec3_t cull_vup; + + float farClip; + + qboolean fogCustom; + qboolean fogEnabled; + qboolean fogSkybox; + vec4_t fogColor; + float fogDensity; + float fogStart; + float fogEnd; + int cached_contents; // in water + int cached_waterlevel; // was in water + + float skyMins[2][SKYBOX_MAX_SIDES]; + float skyMaxs[2][SKYBOX_MAX_SIDES]; + + matrix4x4 objectMatrix; // currententity matrix + matrix4x4 worldviewMatrix; // modelview for world + matrix4x4 modelviewMatrix; // worldviewMatrix * objectMatrix + + matrix4x4 projectionMatrix; + matrix4x4 worldviewProjectionMatrix; // worldviewMatrix * projectionMatrix + byte visbytes[( MAX_MAP_LEAFS + 7 ) / 8];// actual PVS for current frame + + float viewplanedist; + mplane_t clipPlane; +} ref_instance_t; + +typedef struct +{ + cl_entity_t *solid_entities[MAX_VISIBLE_PACKET]; // opaque moving or alpha brushes + cl_entity_t *trans_entities[MAX_VISIBLE_PACKET]; // translucent brushes + cl_entity_t *beam_entities[MAX_VISIBLE_PACKET]; + uint num_solid_entities; + uint num_trans_entities; + uint num_beam_entities; +} draw_list_t; + +typedef struct +{ + int defaultTexture; // use for bad textures + int particleTexture; + int whiteTexture; + int grayTexture; + int blackTexture; + int cinTexture; // cinematic texture + // entity lists + draw_list_t draw_stack[MAX_DRAW_STACK]; + int draw_stack_pos; + draw_list_t *draw_list; + + int realframecount; // not including viewpasses + int framecount; + + qboolean fCustomRendering; + qboolean fResetVis; + + double frametime; // special frametime for multipass rendering (will set to 0 on a nextview) + + // get from engine + cl_entity_t *entities; + uint max_entities; +} gl_globals_t; + +extern gl_globals_t tr; + +struct dx_context_s +{ + HWND window; + int windowWidth; + int windowHeight; + IDirectDraw *pdd; + IDirectDrawSurface *pddsPrimary; + IDirectDrawSurface *pddsBack; + IDirectDrawClipper *pddClipper; + IDirectDrawClipper *pddBackClipper; + + int renderMode; +}; +typedef struct dx_context_s dx_context_t; + +extern dx_context_t dxc; + +const char *dxResultToStr( HRESULT r ); + +#define DXCheck(CALL) {\ +HRESULT r = CALL;\ +if (r != DD_OK)\ + gEngfuncs.Con_Printf( S_ERROR "%s:%d: DirectX error %X(%d 0x%X %d) %s at \"" #CALL "\"\n", __FUNCTION__, __LINE__, r, ( r >> 31 ) & 1, ( r >> 16 ) & 0x7FFF, r & 0xFFFF, dxResultToStr( r ) ); \ +} + +void GL_SetRenderMode( int mode ); + +//r_image.c +void R_InitImages( void ); +dx_texture_t *R_GetTexture( unsigned int texnum ); + +void R_ShowTextures( void ); +const byte *R_GetTextureOriginalBuffer( unsigned int idx ); +int GL_LoadTextureFromBuffer( const char *name, rgbdata_t *pic, texFlags_t flags, qboolean update ); +void GL_ProcessTexture( int texnum, float gamma, int topColor, int bottomColor ); +void GetDetailScaleForTexture( int texture, float *xScale, float *yScale ); +void GetExtraParmsForTexture( int texture, byte *red, byte *green, byte *blue, byte *alpha ); +int GL_FindTexture( const char *name ); +const char *GL_TextureName( unsigned int texnum ); +const byte *GL_TextureData( unsigned int texnum ); +int GL_LoadTexture( const char *name, const byte *buf, size_t size, int flags ); +int GL_CreateTexture( const char *name, int width, int height, const void *buffer, texFlags_t flags ); +int GL_LoadTextureArray( const char **names, int flags ); +int GL_CreateTextureArray( const char *name, int width, int height, int depth, const void *buffer, texFlags_t flags ); +void GL_FreeTexture( unsigned int texnum ); +void R_OverrideTextureSourceSize( unsigned int textnum, unsigned int srcWidth, unsigned int srcHeight ); + +// +// engine shared convars +// +DECLARE_ENGINE_SHARED_CVAR_LIST( ) + +#define Mem_Malloc( pool, size ) _Mem_Alloc( pool, size, false, __FILE__, __LINE__ ) +#define Mem_Calloc( pool, size ) _Mem_Alloc( pool, size, true, __FILE__, __LINE__ ) +#define Mem_Realloc( pool, ptr, size ) gEngfuncs._Mem_Realloc( pool, ptr, size, true, __FILE__, __LINE__ ) +#define Mem_Free( mem ) _Mem_Free( mem, __FILE__, __LINE__ ) +#define Mem_AllocPool( name ) gEngfuncs._Mem_AllocPool( name, __FILE__, __LINE__ ) +#define Mem_FreePool( pool ) gEngfuncs._Mem_FreePool( pool, __FILE__, __LINE__ ) +#define Mem_EmptyPool( pool ) gEngfuncs._Mem_EmptyPool( pool, __FILE__, __LINE__ ) diff --git a/ref/dx2/wscript b/ref/dx2/wscript new file mode 100644 index 00000000..f879b22f --- /dev/null +++ b/ref/dx2/wscript @@ -0,0 +1,18 @@ +#! /usr/bin/env python +# encoding: utf-8 +# mittorn, 2018 + +def options(opt): + pass + +def configure(conf): + pass + +def build(bld): + bld.shlib( + source = ['r_context.c', 'r_image.c'], + target = 'ref_dx2', + defines = 'REF_DLL', + use = 'engine_includes sdk_includes werror public USER32 DDRAW', + install_path = bld.env.LIBDIR + )