// Interface functions (written in C++) for // Direct3D immediate mode system // Must link to C code in main engine system extern "C" { // Mysterious definition required by objbase.h // (included via one of the include files below) // to start definition of obscure unique in the // universe IDs required by Direct3D before it // will deign to cough up with anything useful... #define INITGUID #include "3dc.h" #include "awTexLd.h" #include "dxlog.h" #include "module.h" #include "inline.h" #include "d3_func.h" #include "d3dmacs.h" #include "string.h" #include "kshape.h" #include "eax.h" #include "vmanpset.h" extern "C++" { #include "chnktexi.h" #include "chnkload.hpp" // c++ header which ignores class definitions/member functions if __cplusplus is not defined ? #include "r2base.h" } #define UseLocalAssert No #include "ourasert.h" // FIXME!!! Structures in d3d structure // never have any size field set!!! // This is how it's done in Microsoft's // demo code --- but ARE THEY LYING??? // As far as I know the execute buffer should always be in // system memory on any configuration, but this may // eventually have to be changed to something that reacts // to the caps bit in the driver, once drivers have reached // the point where we can safely assume that such bits will be valid. #define ForceExecuteBufferIntoSystemMemory Yes // To define TBLEND mode --- at present // it must be on for ramp textures and // off for evrything else... #define ForceTBlendCopy No // Set to Yes for debugging, to No for normal // operations (i.e. if we need a palettised // file for an accelerator, load it from // pre-palettised data, using code not yet // written as of 27 / 8/ 96) #define QuantiseOnLoad Yes // Set to Yes to make default texture filter bilinear averaging rather // than nearest BOOL BilinearTextureFilter = 1; extern LPDIRECTDRAW lpDD; #if 0// // Externs extern int VideoMode; extern int DXMemoryMode; extern int ZBufferRequestMode; extern int RasterisationRequestMode; extern int SoftwareScanDrawRequestMode; extern SCREENDESCRIPTORBLOCK ScreenDescriptorBlock; extern VIEWDESCRIPTORBLOCK* Global_VDB_Ptr; extern IMAGEHEADER ImageHeaderArray[]; extern BOOL MMXAvailable; //Globals int D3DDriverMode; static unsigned char DefaultD3DTextureFilterMin; static unsigned char DefaultD3DTextureFilterMax; #if SuppressWarnings static int* itemptr_tmp; #ifdef __WATCOMC__ #pragma warning 389 5 #pragma message("Note: Disabled Warning W389 'Integral value may be truncated...'") #endif #endif #endif // HRESULT LastError; int ExBufSize; LPDIRECT3DEXECUTEBUFFER lpD3DExecCmdBuf; LPDIRECTDRAWSURFACE lpZBuffer; extern LPDIRECTDRAWSURFACE lpDDSBack; extern LPDIRECTDRAWSURFACE lpDDSPrimary; extern LPDIRECTDRAWPALETTE lpDDPal[]; // DirectDraw palette D3DINFO d3d; BOOL D3DHardwareAvailable; int StartDriver; int StartFormat; static int devZBufDepth; extern int WindowMode; extern int ZBufferMode; extern int ScanDrawMode; extern int VideoModeColourDepth; extern enum TexFmt { D3TF_4BIT, D3TF_8BIT, D3TF_16BIT, D3TF_32BIT, D3TF_MAX } d3d_desired_tex_fmt; // Callback function to enumerate devices present // on system (required so that device interface GUID // can be retrieved if for no other reason). Device // information is copied into instance of structure // defined in d3_func.h HRESULT WINAPI DeviceEnumerator(LPGUID lpGuid, LPSTR lpDeviceDescription, LPSTR lpDeviceName, LPD3DDEVICEDESC lpHWDesc, LPD3DDEVICEDESC lpHELDesc, LPVOID lpContext) { int *lpStartDriver = (int *)lpContext; /* Record the D3D driver's description of itself. */ memcpy(&d3d.Driver[d3d.NumDrivers].Guid, lpGuid, sizeof(GUID)); strcpy(d3d.Driver[d3d.NumDrivers].About, lpDeviceDescription); strcpy(d3d.Driver[d3d.NumDrivers].Name, lpDeviceName); /* Is this a hardware device or software emulation? Checking the color model for a valid model works. */ if (lpHWDesc->dcmColorModel) { D3DHardwareAvailable = Yes; d3d.Driver[d3d.NumDrivers].Hardware = Yes; memcpy(&d3d.Driver[d3d.NumDrivers].Desc, lpHWDesc, sizeof(D3DDEVICEDESC)); } else { d3d.Driver[d3d.NumDrivers].Hardware = No; memcpy(&d3d.Driver[d3d.NumDrivers].Desc, lpHELDesc, sizeof(D3DDEVICEDESC)); } /* Does this driver do texture mapping? */ d3d.Driver[d3d.NumDrivers].Textures = (d3d.Driver[d3d.NumDrivers].Desc.dpcTriCaps.dwTextureCaps & D3DPTEXTURECAPS_PERSPECTIVE) ? TRUE : FALSE; /* Can this driver use a z-buffer? */ d3d.Driver[d3d.NumDrivers].ZBuffer = d3d.Driver[d3d.NumDrivers].Desc.dwDeviceZBufferBitDepth & (DDBD_16 | DDBD_24 | DDBD_32) ? TRUE : FALSE; // The driver description is recorded here, // and some basic things like ZBuffering // availability are noted separately. Other // things such as hardware acceleration for // translucency could potentially be noted here // for use in the Write functions to decide // whether e.g. iflag_transparent should be // treated as valid. Obviously this will // require modification of the D3DDRIVERINFO // structure in d3_func.h *lpStartDriver = d3d.NumDrivers; d3d.NumDrivers++; if (d3d.NumDrivers == MAX_D3D_DRIVERS) return (D3DENUMRET_CANCEL); else return (D3DENUMRET_OK); } // This function is called from // InitialiseDirect3DImmediateMode, to // insert and execute opcodes which cannot // be run during a "real" scene because of // some obscure feature of Direct3D. // Initialise Direct3D immediate mode system BOOL InitialiseDirect3DImmediateMode(void) { BOOL RetVal; // to tell device enum function that it has not been called before StartDriver = -1; // to tell texture enum function that it has not been called before StartFormat = -1; // default is no hardware // note that we are still resetting // this here just in case the test from // InitialiseSystem failed and things aren't // what we thought... D3DHardwareAvailable = No; // Zero d3d structure memset(&d3d, 0, sizeof(D3DINFO)); // Set up Direct3D interface object LastError = lpDD->QueryInterface(IID_IDirect3D, (LPVOID*) &d3d.lpD3D); LOGDXERR(LastError); if (LastError != DD_OK) return FALSE; // Use callback function to enumerate available devices on system // and acquire device GUIDs etc // note that we are still resetting // this here just in case the test from // InitialiseSystem failed and things aren't // what we thought... LastError = d3d.lpD3D->EnumDevices(DeviceEnumerator, (LPVOID)&StartDriver); LOGDXERR(LastError); if (LastError != D3D_OK) return FALSE; // Must be run as soon as possible in the // initialisation sequence, but after the // device enumeration (and obviously after // DirectDraw object and surface initialisation). SelectD3DDriverAndDrawMode(); d3d.ThisDriver = d3d.Driver[d3d.CurrentDriver].Desc; // Test!!! Release D3D object if we don't need it and do an // early exit. Will this fix the banding problem on some // accelerators in palettised modes?? // Evidently not. Still, probably a good thing to do... // But!!! The whole banding problem appears to be a // modeX emulation problem on some 3D accelerator cards... #if 1 if (ScanDrawMode == ScanDrawDirectDraw) { ReleaseDirect3DNotDDOrImages(); return TRUE; } #endif // Note that this must be done BEFORE the D3D device object is created #if SupportZBuffering if (ZBufferMode != ZBufferOff) { RetVal = CreateD3DZBuffer(); if (RetVal == FALSE) return FALSE; } #endif // Set up Direct3D device object (must be linked to back buffer) LastError = lpDDSBack->QueryInterface(d3d.Driver[d3d.CurrentDriver].Guid, (LPVOID*)&d3d.lpD3DDevice); LOGDXERR(LastError); if (LastError != DD_OK) return FALSE; AW_TL_ERC awErr = AwSetD3DDevice(d3d.lpD3DDevice); GLOBALASSERT(AW_TLE_OK==awErr); // Enumerate texture formats and pick one // (palettised if possible). d3d.NumTextureFormats = 0; d3d_desired_tex_fmt=D3TF_8BIT; LastError = d3d.lpD3DDevice->EnumTextureFormats (TextureFormatsEnumerator, (LPVOID)&StartFormat); LOGDXERR(LastError); if (LastError != D3D_OK) #if debug { ReleaseDirect3D(); exit(LastError); } #else return FALSE; #endif d3d.CurrentTextureFormat = StartFormat; // NEW NEW NEW // Note: we are NOT restricted to only one texture format awErr = AwSetTextureFormat(&d3d.TextureFormat[StartFormat].ddsd); GLOBALASSERT(AW_TLE_OK==awErr); // Create viewport LastError = d3d.lpD3D->CreateViewport(&d3d.lpD3DViewport, NULL); LOGDXERR(LastError); if (LastError != D3D_OK) return FALSE; // Add viewport to desired device LastError = d3d.lpD3DDevice->AddViewport(d3d.lpD3DViewport); LOGDXERR(LastError); if (LastError != D3D_OK) return FALSE; // Set up viewport data // Note that the viewport is always set to the // SDB limits because internal clipping is handled // within the main engine code using the VDB // system. { // Configure viewport here D3DVIEWPORT viewPort; memset(&viewPort, 0, sizeof(D3DVIEWPORT)); viewPort.dwSize = sizeof(D3DVIEWPORT); viewPort.dwX = 0; // origins x and y viewPort.dwY = 0; viewPort.dwWidth = ScreenDescriptorBlock.SDB_Width; viewPort.dwHeight = ScreenDescriptorBlock.SDB_Height; viewPort.dvScaleX = D3DVAL((float)viewPort.dwWidth / 2.0); // ????was 2.0 viewPort.dvScaleY = D3DVAL((float)viewPort.dwHeight / 2.0); // ????was 2.0 viewPort.dvMaxX = D3DVAL(D3DDivide(D3DVAL(viewPort.dwWidth), D3DVAL(2 * viewPort.dvScaleX))); // ???? viewPort.dvMaxY = D3DVAL(D3DDivide(D3DVAL(viewPort.dwHeight), D3DVAL(2 * viewPort.dvScaleY))); // ???? // And actually set viewport LastError = d3d.lpD3DViewport->SetViewport(&viewPort); LOGDXERR(LastError); if (LastError != D3D_OK) return FALSE; } // At present we will not set a background material for the // viewport, staying instead with the blit code in backdrop.c // Also, we are not currently adding default lights to the // viewport, since the engine lighting system is completely // disconnected from the immediate mode lighting module. // Create execute buffer { // Locals for execute buffer D3DEXECUTEBUFFERDESC d3dexDesc; // Set up structure to initialise buffer // Note some instructions (e.g. lines) may be smaller than a // triangle, but none can be larger ExBufSize = ((sizeof(D3DINSTRUCTION) + sizeof(D3DTRIANGLE)) * MaxD3DInstructions) + (sizeof(D3DTLVERTEX) * MaxD3DVertices); memset(&d3dexDesc, 0, sizeof(D3DEXECUTEBUFFERDESC)); d3dexDesc.dwSize = sizeof(D3DEXECUTEBUFFERDESC); d3dexDesc.dwFlags = D3DDEB_BUFSIZE | D3DDEB_CAPS; d3dexDesc.dwBufferSize = ExBufSize; #if ForceExecuteBufferIntoSystemMemory d3dexDesc.dwCaps = D3DDEBCAPS_SYSTEMMEMORY; #else d3dexDesc.dwCaps = D3DDEBCAPS_MEM; // untested!!! #endif // Create buffer LastError = d3d.lpD3DDevice->CreateExecuteBuffer (&d3dexDesc, &lpD3DExecCmdBuf, NULL); LOGDXERR(LastError); if (LastError != D3D_OK) return FALSE; } // Temporary patch here because the defaults function // buggers palette setting in ScanDrawDirectDraw // for some really obscure reason... if (ScanDrawMode != ScanDrawDirectDraw) SetExecuteBufferDefaults(); return TRUE; } #if 1 // Note that error conditions have been removed // on the grounds that an early exit will prevent // EndScene being run if this function is used, // which screws up all subsequent buffers BOOL RenderD3DScene(void) { // Begin scene // My theory is that the functionality of this // thing must invoke a DirectDraw surface lock // on the back buffer without telling you. However, // we shall see... LastError = d3d.lpD3DDevice->BeginScene(); LOGDXERR(LastError); // if (LastError != D3D_OK) // return FALSE; // Execute buffer LastError = d3d.lpD3DDevice->Execute(lpD3DExecCmdBuf, d3d.lpD3DViewport, D3DEXECUTE_UNCLIPPED); LOGDXERR(LastError); // if (LastError != D3D_OK) // return FALSE; // End scene LastError = d3d.lpD3DDevice->EndScene(); LOGDXERR(LastError); // if (LastError != D3D_OK) // return FALSE; return TRUE; } #else int Time1, Time2, Time3, Time4; BOOL RenderD3DScene(void) { // Begin scene // My theory is that the functionality of this // thing must invoke a DirectDraw surface lock // on the back buffer without telling you. However, // we shall see... Time1 = GetWindowsTickCount(); LastError = d3d.lpD3DDevice->BeginScene(); LOGDXERR(LastError); if (LastError != D3D_OK) return FALSE; Time2 = GetWindowsTickCount(); // Execute buffer #if 1 LastError = d3d.lpD3DDevice->Execute(lpD3DExecCmdBuf, d3d.lpD3DViewport, D3DEXECUTE_UNCLIPPED); LOGDXERR(LastError); #else LastError = d3d.lpD3DDevice->Execute(lpD3DExecCmdBuf, d3d.lpD3DViewport, D3DEXECUTE_CLIPPED); LOGDXERR(LastError); #endif if (LastError != D3D_OK) return FALSE; Time3 = GetWindowsTickCount(); // End scene LastError = d3d.lpD3DDevice->EndScene(); LOGDXERR(LastError); if (LastError != D3D_OK) return FALSE; Time4 = GetWindowsTickCount(); return TRUE; #endif // With a bit of luck this should automatically // release all the Direct3D and DirectDraw // objects using their own functionality. // A separate call to finiObjects // is not required. // NOTE!!! This depends on Microsoft macros // in d3dmacs.h, which is in the win95 directory // and must be upgraded from sdk upgrades!!! void ReleaseDirect3D(void) { DeallocateAllImages(); RELEASE(d3d.lpD3DViewport); RELEASE(d3d.lpD3DDevice); #if SupportZBuffering RELEASE(lpZBuffer); #endif RELEASE(lpDDPal[0]); RELEASE(lpDDSBack); RELEASE(lpDDSPrimary); RELEASE(d3d.lpD3D); RELEASE(lpDD); /* release Direct Input stuff */ ReleaseDirectKeyboard(); ReleaseDirectMouse(); ReleaseDirectInput(); // Reset windows palette entry allocation if ((VideoModeColourDepth == 8) && (WindowMode == WindowModeSubWindow)) { HDC hdc = GetDC(NULL); SetSystemPaletteUse(hdc, SYSPAL_STATIC); ReleaseDC(NULL, hdc); } } // Release all Direct3D objects // but not DirectDraw void ReleaseDirect3DNotDDOrImages(void) { RELEASE(d3d.lpD3DViewport); RELEASE(d3d.lpD3DDevice); #if SupportZBuffering RELEASE(lpZBuffer); #endif RELEASE(d3d.lpD3D); } void ReleaseDirect3DNotDD(void) { DeallocateAllImages(); RELEASE(d3d.lpD3DViewport); RELEASE(d3d.lpD3DDevice); #if SupportZBuffering RELEASE(lpZBuffer); #endif RELEASE(d3d.lpD3D); } // NOTE!!! These functions depend on Microsoft macros // in d3dmacs.h, which is in the win95 directory // and must be upgraded from sdk upgrades!!! // ALSO NOTE!!! All this stuff involves heavy // use of floating point assembler in software // emulation, probably hand parallelised between // the FPU and the processor or something bizarre, // implying that this stuff SHOULD NOT be used // one anything below a Pentium. // AND AGAIN!!! Due to the nature of the item // format, the rasterisation module MUST RECEIVE // repeated vertices in the data area. Tough shit, // Microsoft, that's what I say... void WritePolygonToExecuteBuffer(int* itemptr) { } void WriteGouraudPolygonToExecuteBuffer(int* itemptr) { } void Write2dTexturedPolygonToExecuteBuffer(int* itemptr) { } void WriteGouraud2dTexturedPolygonToExecuteBuffer(int* itemptr) { } void Write3dTexturedPolygonToExecuteBuffer(int* itemptr) { } void WriteGouraud3dTexturedPolygonToExecuteBuffer(int* itemptr) { } #if SupportZBuffering // To make effective use of 16 bit z buffers (common // on hardware accelerators) we must have a z coordinate // which has been transformed into screen space in such a // way that linearity and planearity are preserved, i.e. as // if we had done a homogeneous transformation. For the case // in which the far clipping plane is much further away than the // near one (effectively true for 3dc, especially as there is // no far clipping plane as such), the appropriate transformation // can be reduced to zScreen = (ZWorld - ZNear) / ZWorld. This // calculation is therefore (unfortunately) done for each vertex // for z buffered items, taking ZNear to be the current // VDB_ClipZ * GlobalScale. void WriteZBPolygonToExecuteBuffer(int* itemptr) { } void WriteZBGouraudPolygonToExecuteBuffer(int* itemptr) { } void WriteZB2dTexturedPolygonToExecuteBuffer(int* itemptr) { } void WriteZBGouraud2dTexturedPolygonToExecuteBuffer(int* itemptr) { } void WriteZB3dTexturedPolygonToExecuteBuffer(int* itemptr) { } void WriteZBGouraud3dTexturedPolygonToExecuteBuffer(int* itemptr) { } #endif void WriteBackdrop2dTexturedPolygonToExecuteBuffer(int* itemptr) { } // Same as ordinary 2d textured draw at present, but may e.g. // require different texture wrapping behaviour or // w values. void WriteBackdrop3dTexturedPolygonToExecuteBuffer(int* itemptr) { } // Note that this is all dead crap and deeply unoptimised // But then... a) it's really only a test and // b) it's for the tools group anyway... void DirectWriteD3DLine(VECTOR2D* LineStart, VECTOR2D* LineEnd, int LineColour) { } // reload D3D image -- assumes a base pointer points to the image loaded // from disc, in a suitable format void ReloadImageIntoD3DImmediateSurface(IMAGEHEADER* iheader) { void *reloadedTexturePtr = ReloadImageIntoD3DTexture(iheader); LOCALASSERT(reloadedTexturePtr != NULL); int gotTextureHandle = GetTextureHandle(iheader); LOCALASSERT(gotTextureHandle == TRUE); } void* ReloadImageIntoD3DTexture(IMAGEHEADER* iheader) { // NOTE FIXME BUG HACK // what if the image was a DD surface ?? if (iheader->hBackup) { iheader->D3DTexture = AwCreateTexture("rf",AW_TLF_PREVSRC|AW_TLF_COMPRESS); return iheader->D3DTexture; } else return NULL; } int GetTextureHandle(IMAGEHEADER *imageHeaderPtr) { LPDIRECT3DTEXTURE Texture = (LPDIRECT3DTEXTURE) imageHeaderPtr->D3DTexture; LastError = Texture->GetHandle(d3d.lpD3DDevice, (D3DTEXTUREHANDLE*)&(imageHeaderPtr->D3DHandle)); if (LastError != D3D_OK) return FALSE; return TRUE; } void ReleaseD3DTexture(void* D3DTexture) { LPDIRECT3DTEXTURE lpTexture; lpTexture = (LPDIRECT3DTEXTURE) D3DTexture; RELEASE(lpTexture); } #if SupportZBuffering BOOL CreateD3DZBuffer(void) { DDSURFACEDESC ddsd; // For safety, kill any existing z buffer #if SupportZBuffering RELEASE(lpZBuffer); #endif // If we do not have z buffering support // on this driver, give up now if (!(d3d.Driver[d3d.CurrentDriver].ZBuffer)) return FALSE; memset(&ddsd,0,sizeof(DDSURFACEDESC)); ddsd.dwSize = sizeof(DDSURFACEDESC); ddsd.dwFlags = (DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS | DDSD_ZBUFFERBITDEPTH); ddsd.dwHeight = ScreenDescriptorBlock.SDB_Height; ddsd.dwWidth = ScreenDescriptorBlock.SDB_Width; ddsd.ddsCaps.dwCaps = DDSCAPS_ZBUFFER; // If we are on a hardware driver, then the z buffer // MUST be in video memory. Otherwise, it MUST be // in system memory. I think. if (d3d.Driver[d3d.CurrentDriver].Hardware) ddsd.ddsCaps.dwCaps |= DDSCAPS_VIDEOMEMORY; else ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY; // Get the Z buffer bit depth from this driver's // D3D device description and add it to the description // of the surface we want to create devZBufDepth = d3d.Driver[d3d.CurrentDriver].Desc.dwDeviceZBufferBitDepth; if (devZBufDepth & DDBD_32) ddsd.dwZBufferBitDepth = 32; else if (devZBufDepth & DDBD_24) ddsd.dwZBufferBitDepth = 24; else if (devZBufDepth & DDBD_16) ddsd.dwZBufferBitDepth = 16; else if (devZBufDepth & DDBD_8) ddsd.dwZBufferBitDepth = 8; else { #if debug ReleaseDirect3D(); exit(0x511621); #else return FALSE; #endif } // Eight-bit z buffer? Fuck off. if (ddsd.dwZBufferBitDepth == 8) return FALSE; // Now we must actually make the z buffer LastError = lpDD->CreateSurface(&ddsd,&lpZBuffer, NULL); if (LastError != DD_OK) { RELEASE(lpZBuffer); return FALSE; } LastError = lpDDSBack->AddAttachedSurface(lpZBuffer); if (LastError != DD_OK) { RELEASE(lpZBuffer); return FALSE; } return TRUE; } #define ZFlushVal 0xffffffff // At present we are using my z flush function, with the // (undocumented) addition of a fill colour for the // actual depth fill, since it seems to work and at least // I know what it does. If it starts failing we'll probably // have to go back to invoking the viewport clear through // Direct3D. void FlushD3DZBuffer(void) { DDBLTFX ddbltfx; memset(&ddbltfx, 0, sizeof(ddbltfx)); ddbltfx.dwSize = sizeof(ddbltfx); ddbltfx.dwFillDepth = devZBufDepth; ddbltfx.dwFillColor = ZFlushVal; /* lets blt a color to the surface*/ LastError = lpZBuffer->Blt(NULL, NULL, NULL, DDBLT_DEPTHFILL | DDBLT_WAIT, &ddbltfx); } void SecondFlushD3DZBuffer(void) { #if 1 { WriteEndCodeToExecuteBuffer(); UnlockExecuteBufferAndPrepareForUse(); ExecuteBuffer(); LockExecuteBuffer(); } DDBLTFX ddbltfx; memset(&ddbltfx, 0, sizeof(ddbltfx)); ddbltfx.dwSize = sizeof(ddbltfx); ddbltfx.dwFillDepth = devZBufDepth; ddbltfx.dwFillColor = ZFlushVal; /* lets blt a color to the surface*/ LastError = lpZBuffer->Blt(NULL, NULL, NULL, DDBLT_DEPTHFILL | DDBLT_WAIT, &ddbltfx); #else extern void ClearZBufferWithPolygon(void); ClearZBufferWithPolygon(); #endif } #endif void FlushZB(void) { HRESULT hRes; D3DRECT d3dRect; d3dRect.lX1 = 0; d3dRect.lX2 = 640; d3dRect.lY1 = 0; d3dRect.lY2 = 480; hRes = d3d.lpD3DViewport->Clear(1, &d3dRect, D3DCLEAR_ZBUFFER); } // For extern "C" };