DirectDraw Lighting? |
Amrazek | Don't go and explode on me! I'm perfectly aware that DirectDraw doesn't support lighting... Natively. I want to write my own functions to do it for me, using GetLockedArray() and modifying the memory.
However, before I continue with what I already have, I'd like to get assured on a few things:
1) The longs in the array returned from GetLockedArray() are NOT normal colors, and I have to somehow convert them.
2) The array from GetLockedArray() will have the same dimensions as the screen, right?
3) Some of the lighting code from www.Directx4vb.com will work, with slight modification.
4) I CANNOT make DLL calls while I have a surface locked, correct?
Thanks!
-Amrazek |
Eric Coleman | I'm not really sure about most of that, but for question 4, once you have the array of data, you could pass a pointer to the array to a DLL call to do some fast color manipulation. |
Amrazek | quote: the debugger will not work whilst you are processing memory. Between the Surface.Lock and Surface.Unlock methods windows ceases execution, this is because directdraw takes the win16mutex which basically suspends windows. Because windows is effectively shut down you must do NOTHING complicated or invloved with other components. ie. No DLL calls, no file access and no running of other applications
That's straight from www.DirectX4VB.com... So I guess I must either settle for VB's pathetic speed or ... not heed this warning. I suppose there are other ways, as well, but I want to think the entire thing through. I guess I'm going to try my current code now and pray that Windows doesn't crash, but the only way not to be surprised is to prepare for the worst [}:)]
I'll post anything I find. I'm going to start by attempting to answer my own questions... |
Eric Coleman | If you read the SDK, then you'll see that you're supposed to include the DDLOCK_NOSYSLOCK flag when locking a surface. This prevents the win16mutex problem, which only occurs on 95,98, and ME. And the only time the win16mutex causes a problem is if you lock a surface and then fail to unlock the surface, or if you lock a surface too often. If you lock your surface a few times per frame, then you shouldn't have any problems. [:)] If you lock the surface too many times per frame, then you'll end up slowing down windows, and your program will be effected, thus slowed down too. |
Amrazek | Interesting. Is there any downside to using that flag? I want it to be as fast as possible, because I really can't afford to do all the game math, rendering, misc engine stuff, and THEN do lighting. Not to mention AI. I'm double-checking my code now before I run it, as it may lock up my computer if I screwed up even one little detail. |
Eric Coleman | Have you looked at the "fire" source in the SDK? I'm sure that will tell you all you need to know to access the memory directly.
For lighting, accessing the surface directly will be slow. To speed things up, you might want to consider precalculating the light. |
Amrazek | That was the idea -- just hold all the calculated data in an array, and have the program that's accessing my engine tell it when it needs to recalculate. That way, if a light on, say, the floor tiles doesn't affect a character or things moving over it, it wouldn't ever have to be calculated again. |
AT | Looking at your last few posts ... making a diablo-style game ? |
Eric Coleman | I have a suggestion on how you can do lighting easily in a tiled direct draw game. The first requirement is that the game use tiles. Then, you create a light map, and then alpha blend the light map over tiles that need lighting. As long as the lights don't move, you can alpha blend the light map onto tiles once, and then use the lit tile for speed.
If you use 16 bit color, then you could use the vbdabl.dll to do the alpha blending. There is the .dll, source, and example programs at VoodooVB.
If you want to use 8 bit alpha blending, then you'll need to use lookup tables. There is a very good example of 8 bit palleted alpha blending at [url="http://www.ur.co.nz/urcorp/default.asp"]Unlimited Realities[/url] |
Amrazek | Good suggestion -- I'll certainly give it a shot. And AT, I have been looking at starting a Diablo-style game... I've been getting into it a lot recently, for entertainment, as well as ideas. Some of the things done in Diablo are quite clever, and since I'm looking into being in the game industry, being 'up on it' sounded like a good idea. But before I begin, I've firmly decided to finish at least one game first. I'll need the experience, as well as the ideas, to work on such a ridiculously difficult genre. Not to mention artists... Which, somehow, has brought up another diablo-ish question: how do they do their terrain tiles? It seems almost as if they are all unique, but anybody that looks closely will notice the pattern. Realism is something that I believe many VB RPGs miss, and every little bit helps. I would guess that the designers draw up some sort of sketchy map, and the artists look at it and draw it themselves by hand -- or do they draw the entire thing out, and then break it up into smaller tiles? I know it's professionally done, but knowing the method may make my own come out better. |
AT | By tiling things at an angle instead of simply horizontal/vertical types you can hide the effect to some extent. Again - if you can get at the tile graphics in diablo you could have a look.
I do think the tiles are different sizes (so you might have a couple of 32x32 tiles alongside a 256x256 graphic) which helps to break things up. As long as you can't see obvious tiling in any one screen - you tend not to notice that the shrub a couple of screens down looks just like the one you passed a minute ago.
As for maps - the diablo maps are partially random. But usually artists won't make tiles specifically for each bit of a map, but instead generic tiles (all the types required to go into the editor - ground, walls, trees, etc) - plus any specific objects needed. The designer should specify this - e.g. 'I want grass, mud, stone, some wooden huts, and one big wooden hut with purple light shining out of the windows'.
Getting into the games industry is a pain in the a*** ... |
Amrazek |
Different tile sizes? Interesting. I DO know that they have several larger objects, and populate the basic map with them. This seems to be mainly pillars, interactive objects, and torches, but it varies with the type of map.
quote: Getting into the games industry is a pain in the a*** ...
I've had that general feeling for a while now [:)] Why, in your opinion, is it difficult? |
AT | As far as different tile sizes go - think multiples (essentially just a group of tiles which are put down together, because they don't 'tile' with other graphics otherwise).
If you have starcraft - take a look at the tiles in the editor. There is a good chance that diablo (another blizzard game) has a similar system.
http://www.hyperfictions.com/groundtile1.jpg - good artists are pretty damn important unless you want to make another rpg2000 lookalike.
As for the games industry - too many people for too few jobs. Junior positions are rare and experience is very important (not helpful if you are trying to start out). |
Amrazek |
I keep getting an automation error when attempting to unlock a surface. I think I'll have to take a closer look at that fire example.
On a side note, I've managed to get DirectDraw to do alphablending and rotation [8D] |
Amrazek | I've got memory access working, at least, but I'm still trying to figure out how it all works. I'm about 50% done now. |
Eric Coleman | Are you using the rotation.dll? |
Amrazek | Nope -- I got tricky and added in Direct3D to the mix. You can't actually draw in 3D, granted, (at least, I havent tried) but you can create two triangles and texture them. Direct3D also does the alphablending for me. |
Eric Coleman | Ah, there are two different examples of using direct 3D in the Files section here at VB gamer if you haven't looked yet. But since you have it working already, then you don't need them [:)] |
Amrazek |
I feel like I've run into a brick wall. I'm using GetLockedArray() to get a pointer to the [backbuffer's] surface memory, and so far all that works. Changes are reflected in the surface, as they should be.
The problem is that I cannot obtain the 'true' color of a pixel, nor specify my own color. This is because I cannot determine the RGB of the pixel -- it's in a format that I don't know how to change. How can I convert the raw data into a usable RGB format?
Mind as well hit two birds with one stone... Is there a 'darkening' formula I can use? Just changing the RGB of a pixel to a lower value doesn't darken it; it changes the color completely.
Thanks!
Amrazek |
Eric Coleman | check out the discussion about color [url="http://www.mwgames.com/voodoovb/phpBB2/viewtopic.php?t=271&postdays=0&postorder=asc&start=15"]here[/url]
To sum it all up, you need to find the color masks for your surface, and then use those to extract the data returned from GetLockedArray. If you have the individual R,G,B color components, then you can decrease each one individualy, or if you want to be really accurate, you could convert the RGB color to a different color space and change the luminance or brightness.
This is what YCC conversion would look like using RGB values 0 to 255,
[code]
'Convert R,G,B to YCC
Y = 0.299 * R + 0.587 * G + 0.114 * B
Cb = -0.1687 * R - 0.3313 * G + 0.5 * B + 128
Cr = 0.5 * R - 0.4187 * G - 0.0813 * B + 128
'Decrease the brightness by 10%
Y = Y * 0.9
'Convert back to RGB color
R = Y + 1.402 * (Cr - 128)
G = Y - 0.34414 * (Cb - 128) - 0.71414 * (Cr - 128)
B = Y + 1.772 * (Cb - 128)
[/code]
|
Amrazek | I put this project off a little bit, for lack of understanding, but my interest has been rekindled. So far, I've got the code to set the correct color implemented and working, but the second part is returning a color back to its original state. I've lost the source for this project, but I had added a few capabilites to my general game engine that will allow me to rebuild it in about a half hour. I've got a couple theories going; I'll add a reply on anything that I've found.
-Amrazek |
Amrazek | I'm having difficulty returning the color of a pixel... I just can't seem to figure out how to change the DirectDraw long value back into RGB... |
Eric Coleman | Look at that link I provided in my last message. For GetLockedArray(), I think you are supposed to use an array of Bytes. |
Amrazek |
I've got the changing my RGB color into a DXRGB color conversion working. However, I cannot change a DXRGB color back into RGB... I've tried .ColorGetRed / Blue / Green, but that requires a value of 0-255. As I understand it, the values in GetLockedArray ARE up to 255, but it's in a different format... There are 3 bytes per pixel... Unless... Just maybe... I've got an idea. I'll have to go check it out right now.
-Amrazek |
Amrazek | My hopeful idea didn't work... I was thinking that .GetLockedArray returned the width of the surface * 3, for BGR... However, .ColorGetRed/Green/Blue didn't work... Red returned 1, the others returned 0. Frustrating [}:)] |
Eric Coleman | .GetColorRed/Green/Blue expects a LONG, the data from the byte array is a BYTE. If you are in 16bit mode, two bytes make a color for each pixel, in 24bit mode, then its 3 bytes to a pixel, and in 32 bit mode, its 4 bytes to a pixel.
A 32bit example would look something like this after you use GetLockedArray
[code]
Dim B(0 to 3) as Byte, L as Long
B(0)= LckdArray(0+0,0)
B(1)= LckdArray(0+1,0)
B(2)= LckdArray(0+2,0)
B(3)= LckdArray(0+3,0)
CopyMemory(L, B(0), 4) 'copy 4 bytes to the long
Debug.Print dx.ColorGetRed(L)
Debug.Print dx.ColorGetGreen(L)
Debug.Print dx.ColorGetBlue(L)
[/code]
A 16bit example would be only two bytes to the byte array, B(0 to 1), and then copymemory would be CopyMemory(L, B(0)), 2).
Of course, there are faster ways to do this. I'm just trying to show you how the bytes are arranged. The pixel at location (0,0) would be LckdArray(0,0), LckdArray(1,0), LckdArray(2,0), LckdArray(3,0), the pixel at location (1,0) would be LckdArray(4,0), LckdArray(5,0), LckdArray(6,0), LckdArray(7,0). |
Amrazek | I believe I understand now -- didn't know that it was only 2 bytes and that you could use CopyMemory to combine them. That helps a lot =)
However, the value returned is still incorrect... Perhaps I need to do some kind of a conversion on it? |
Eric Coleman | Try using some solid color surfaces first to help you get a better idea of how the bytes are laid out in the byte array. You could try an all blue surface, and then output a few bytes in HEX, then you could easily combine them with the windows Calculator to see how things are organized. For example, if you use Debug.print to get something like this... 0 1F 0 1F, you would use the number "001F001F" in Calculator to convert to a binary of "0000 0001 1111 0000 0000 0001 1111", and that would show you the color is only using 6 Bits. If you explore a bit more, you'll understand how things are packed together. |
Amrazek | The pixel format is beginning to make sense. I was using this:
[code]
'\\ retrieve pixel (0,0)
Dim pBytes(0 To 1) As Byte
pBytes(0) = BytMem(0 * 2, 0)
pBytes(1) = BytMem(0 * 2 + 1, 0)
CopyMemory pDest, pBytes(0), 2
'\\ Output the data
Debug.Print "Output:"
Debug.Print " Returned: "; CStr(pDest)
Debug.Print " Hex: "; Hex(pDest)
Debug.Print " Byte1: "; CStr(pBytes(0))
Debug.Print " Byte2: "; CStr(pBytes(1))
Debug.Print " retR: "; GfxEngine.GetColorRed(pDest)
Debug.Print " retG: "; GfxEngine.GetColorGreen(pDest)
Debug.Print " retB: "; GfxEngine.GetColorBlue(pDest)
[/code]
I made a solid blue rectangle on the screen and tried various things with it. The results that were outputted were:
[code]
Output:
Returned: 31
Hex: 1F
Byte1: 31
Byte2: 0
retR: 0
retG: 0
retB: 0.1215686
[/code]
I see that the blue channel is at least returning something, but I don't know what it means... I have no idea of what format retB is in.
-Amrazek |
Eric Coleman | Did you write the "GetColor" functions in the GfxEngine class? A value of "h31" should translate to value of "1.0" in 16 bit color. |
Amrazek | Hmm... Interesting... I'll post the portion of GfxEngine that contains that:
[code]
'; Return the R component of a DD long
Public Function GetColorRed(lngDXColor As Long) As Single
GetColorRed = DX.ColorGetRed(lngDXColor)
End Function
'; Return the G component of a DD long
Public Function GetColorGreen(lngDXColor As Long) As Single
GetColorGreen = DX.ColorGetGreen(lngDXColor)
End Function
'; Return the B component of a DD long
Public Function GetColorBlue(lngDXColor As Long) As Single
GetColorBlue = DX.ColorGetBlue(lngDXColor)
End Function
[/code]
That looks alright to me... Perhaps something in the initialize sub is wrong?
[code]
Private Sub DDInit(ByRef myForm As Long, ByRef iScreenWidth As Integer, ByRef _
iScreenHeight As Integer, ByRef iBPP As Integer, ByRef _
iColorKey As Integer, ByRef bUseVideoMemory As Boolean, _
ByRef bytFontRed As Byte, ByRef bytFontGreen As Byte, _
ByRef bytFontBlue As Byte, ByRef bSetFontTransparent As _
Boolean, ByRef iBackBufferCount As Integer, ByRef _
bGammaController As Boolean, ByRef bytGammaRed As Byte, _
ByRef bytGammaGreen As Byte, ByRef bytGammaBlue As Byte)
On Error Resume Next
Set DX = New DirectX7
Set DD = DX.DirectDrawCreate("")
'Full screen exclusive mode always
Call DD.SetCooperativeLevel(myForm, DDSCL_FULLSCREEN Or DDSCL_ALLOWMODEX Or DDSCL_EXCLUSIVE)
DD.SetDisplayMode iScreenWidth, iScreenHeight, iBPP, 0, DDSDM_DEFAULT
'Fill out primary surface description
Ddsd1.lFlags = DDSD_CAPS Or DDSD_BACKBUFFERCOUNT
If Not bSetFontTransparent = True Then
Ddsd1.ddsCaps.lCaps = DDSCAPS_PRIMARYSURFACE Or DDSCAPS_FLIP Or DDSCAPS_COMPLEX Or DDSCAPS_SYSTEMMEMORY
Else
Ddsd1.ddsCaps.lCaps = DDSCAPS_PRIMARYSURFACE Or DDSCAPS_FLIP Or DDSCAPS_COMPLEX
End If
'\\ Allow D3D capability
Ddsd1.ddsCaps.lCaps = DDSCAPS_PRIMARYSURFACE Or DDSCAPS_3DDEVICE Or DDSCAPS_FLIP Or DDSCAPS_COMPLEX Or DDSCAPS_VIDEOMEMORY
Ddsd1.lBackBufferCount = iBackBufferCount 'Could be more than one backbuffer
'ddsFront is the primary surface
Set ddsFront = DD.CreateSurface(Ddsd1)
InitializeLighting
If bGammaController Then
Set GammaController = ddsFront.GetDirectDrawGammaControl
GammaController.GetGammaRamp DDSGR_DEFAULT, OriginalRamp
End If
'Attach the backbuffer
Dim Caps As DDSCAPS2
If Not bUseVideoMemory = True Then
Caps.lCaps = DDSCAPS_BACKBUFFER Or DDSCAPS_SYSTEMMEMORY
Else
Caps.lCaps = DDSCAPS_BACKBUFFER
End If
Set ddsBack = ddsFront.GetAttachedSurface(Caps)
ddsBack.GetSurfaceDesc Ddsd2
Set D3D = DD.GetDirect3D
Set DEV = D3D.CreateDevice("IID_IDirect3DHALDevice", ddsBack)
DDSetFont "Calligraphic 421 BT", 12, RGB(bytFontRed, bytFontGreen, bytFontBlue)
If bSetFontTransparent Then
ddsBack.SetFontTransparency True
End If
'\\ set up screen variables
With Screen
.nWidth = iScreenWidth
.nHeight = iScreenHeight
.nBPP = iBPP
.bytFontRed = bytFontRed
.bytFontGreen = bytFontGreen
.bytFontBlue = bytFontBlue
.bFontTransparent = bSetFontTransparent
.iColorKey = iColorKey
.bUseVideoMemory = bUseVideoMemory
.iBackBufferCount = iBackBufferCount
.bGammaController = bGammaController
.bytGammaRed = bytGammaRed
.bytGammaGreen = bytGammaGreen
.bytGammaBlue = bytGammaBlue
End With
If bGammaController Then
SetGamma bytGammaRed, bytGammaGreen, bytGammaBlue
End If
[/code]
Also of note: I'm using 2 backbuffers, and am using the gamma controller. BPP = 16, and the ColorKey is black. Another thing: GfxEngine is actually a whole DirectDraw engine I wrote, and is a DLL.
This is more difficult than I initially thought... [:)] Oh well, much knowledge to be gained.
-Amrazek |
Amrazek | I downloaded the source code to the Dx/DD fullscreen tutorial and tried this out, and got the same results, so it's probably not the initialization... |
Amrazek | According to the DirectX7 SDK, the long argument in the ColorGet functions is supposed to be between 0 and 255...
color
Color from which the red component is retrieved. A value between 0 and 255.
? |