So you’re looking to dive further into the rendering pipeline and brave that cold D3D world without the overall messiness of D3D programming. This posting is more of an article, I guess, than a tutorial. The difference: a tutorial would be a full project sample. Couple of notes:
Anyhow without further ado, lets begin.
To begin, we need to discuss the basics of Direct3D and how OOP is fundamental to the discussion. I’m sure you have seen the code that has a Direct3D Object and a Direct3D Device object and maybe scratched your head wondering why would you need both? It’s like this: Think of Direct3D as a core object, just as you have a Form class (or for us Delphi people) a TForm class/type. Under (Or in) this core object (Think of it as a container or a cardboard box) you have a device. This device is what is used throughout all of your D3D code. Now as you know, TV3D handles the creation of Direct3D core and device (As well as other optional objects) objects. This GREATLY simplifies working with D3D. One look at just about any DX sample and you’ll see why. Anyhow, if we’re going to utilize D3D in parallel with TV3D we need to first have TV create its internal D3D objects (The core D3D object and the Device as well as all the many minute things that go with the device; i.e the device’s caps (Capabilities), the D3D presentparameters, backbuffers, etc).
Here is some sample code of getting TV to create its internal objects:
TV: TVEngine; TVTexture: TVTextureFactory; TVInput: TVInputEngine; TVGlobals: TVGlobals; TVAtmos: TVAtmosphere; TVMath: TVMathLibrary; TVMyScene: TVScene; TVText: TVScreen2DText; TVMaterial: TVMaterialFactory; LightEngine: TVLightEngine; TVImmediate: TVScreen2DImmediate; TVCam: TVCamera; g_pD3DDevice: ^IDirect3DDevice9; D3DDeviceAddr: Integer; g_pEffect: ID3DXEffect; g_pEffect_Pool: ID3DXEffectPool;
TV.Init3DWindowed(MainRenderWindow.Handle, True); TV.SetSearchDirectory(GetCurrentDir()); TV.SetAngleSystem(TV_ANGLE_DEGREE); TVTexture := CoTVTextureFactory.Create; TVTexture.SetTextureMode(TV_TEXTUREMODE_32BITS); TVInput := CoTVInputEngine.Create(); TVInput.Initialize(true, true); TVGlobals := CoTVGlobals.Create; TV.DisplayFPS(True,TVGlobals.RGBA(1,1,1,1)); TVAtmos := CoTVAtmosphere.Create; TVMath := CoTVMathLibrary.Create; TVMyScene := CoTVScene.Create(); TVMyScene.SetViewFrustum(60, 30000); TVMyScene.SetShadeMode(TV_SHADEMODE_GOURAUD); TVMyScene.SetDepthBuffer(TV_DEPTHBUFFER_24BITS_8STENCILBITS); TVText := CoTVScreen2DText.Create; TVMaterial := CoTVMaterialFactory.Create; LightEngine := CoTVLightEngine.Create; TVImmediate := CoTVScreen2DImmediate.Create; TVCam := CoTVCamera.Create; //Setup stuff for D3D use TVInternals := CoTVInternalObjects.Create;
You may look at that and say, that’s alot (Some of them you may not need) but each of those calls might actually be several lines of code in D3D; multiply each line by even two and you have already halfed the size of your code right there! Anyhow here’s where it gets fun. After your TV init code you can place this in after it:
(You might even want to put this into a procedure like I did)
D3DDeviceAddr := TVInternals.GetDevice3D; g_pD3DDevice := @D3DDeviceAddr;
from here on out you can use g_pD3DDevice just like the D3D code you see out there. The beauty part of this is you don’t need to call CreateDevice, you just blindly go ahead and use the g_pD3DDevice! Now in following with OOP design, D3DEffects are created from the D3DDevice. But first a couple of examples in the usage of g_pD3DDevice:
Example use of D3D Device:
g_pD3DDevice.CreateDepthStencilSurface(SHADOWMAP_SIZE, SHADOWMAP_SIZE, D3DFMT_D24X8, D3DMULTISAMPLE_NONE, 0, TRUE, g_pDSShadow, nil); g_pd3dDevice.Clear(0, nil, D3DCLEAR_TARGET or D3DCLEAR_ZBUFFER, $0000004D, 1.0, 0);
Example creating of D3DEffects:
D3DXCreateEffectPool(g_pEffect_Pool); D3DXCreateEffectFromFile(g_pD3DDevice^, '<path>\<shader_file>.fx', nil, nil, dwShaderFlags, nil, g_pEffect, nil);
<NOTE: I had put the creation of my ShadowMap DX related stuff into another procedure in another unit and g_pD3DDevice.CreateEffectFromFile creates an access violation issue once the EIP (current Instruction Pointer; Assembly jargon i.e. outside the scope of this article) reaches the end of the procedure. Not sure why (I didn’t bother to delve into it further), so I used D3DXCreateEffectFromFile which seems to work just fine.>
Example usage of D3DEffects:
g_pD3DDevice.BeginScene(); g_pEffect._Begin(@cPass, 0); for p := 0 to cPass - 1 do begin g_pEffect.BeginPass(p); ... end; g_pEffect._End(); g_pD3DDevice.EndScene();
Now this was a fairly short discussion on integrating TV and D3D together on the end-programmer layer but hopefully, you gained something out of this and are able to actually apply this to understanding pure DX code better so you can port it into TV. You should be able to see how when working with TV and D3D the two work in PARALLEL with each and how this works out VERY nicely to the programmer. You should be able to start to develop simple theories of how “if I create this in TV, it should be exposed on the D3D level; like RenderSurfaces”.
Some common notation to remember when looking at various DX code:
D3DCaps - Direct3D Device Capabilities. This is usually used in situations when referring to a Video Card’s Graphical Capbilities in terms of Texture Formats, etc. It does not mean Cap, as in a limit! Though I guess you could call it a limit if the video card doesn’t have the specific capability that you need.
LPDIRECT3DDEVICE9 and IDirect3DDevice9* A pointer to a IDirect3DDevice9 class
(In other words LPDIRECT3DDEVICE9 and IDirect3DDevice9 mean the same thing.)
TV Camera methods and DX
TVCamera.GetProjectionMatrix() - Returns D3D Projection matrix
TVMath.TVMatrixInverse(TVCamera.GetMatrix()) - To get the view matrix you inverse the World Matrix (Like we did here on this example line)
TV(Mesh, Actor, Mimimesh, Particle, or Landscape).GetMatrix() - Returns the World Matrix
Internal World Matrix is unaccessible
TV and DX camera method equivalents:
NOTE: _ and m are the same, for example _41 and m41 are the same, for example D3DMatrix._41 and TV_3DMatrix.m41
|cFirstPersonCamera Methods||Equivalent TV Camera Method||Matrix elements used by both|
|GetEyePt||GetPosition||_41, _42, _43|
|<Unknown at present moment>||GetRotation||<Unknown at present moment>|
|GetWorldAhead||GetDirection||_31, _32, _33|
|<Unknown at present moment>||GetLookAt||_14, _24, _34 <Note: not sure>|
NOTE: Table above has not been verified by any of the devs so treat it as probably but not confirmed.