You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
476 lines
22 KiB
476 lines
22 KiB
"Ramp is hell," says Gen. William Tecumseh Sherman, now 60, in an address to
|
|
a Columbus, Ohio, reunion of the G.A.R. (Grand Army of the Republic). "There is
|
|
many a boy here who looks on ramp as all speed, but, boys, it is all hell. You
|
|
can bear this warning voice to generations yet to come."
|
|
(Quote from 1880 in reference to the U.S. Civil Ramp)
|
|
|
|
Introduction
|
|
============
|
|
|
|
First, some philosophy and intent. Ramp is provided in DX6 for compatibility
|
|
with DX5, and our implementation is intended to work as well but no better than
|
|
ramp did in DX5. We make no attempt to clean up or generalize ramp, and we
|
|
hope to maintain the same quirks the original implementation had, largely
|
|
through the literal reuse of DX5 code, where possible.
|
|
|
|
That said, it is useful to know where the ramp files in DX6 came from in DX5 so
|
|
the code can be examined in its original context when questions arise. The
|
|
diesel\direct3d\rast\rampmat (Ramp Material) directory handles creating
|
|
rampmap's and palettes from materials and textures. The
|
|
diesel\direct3d\rast\rampspan directory has the span rasterizers for ramp.
|
|
|
|
DX6 DX5
|
|
diesel\direct3d\rast\rampmat\ diesel\direct3d\drivers\
|
|
cmap.h . . . . . . . . . . . . include\cmap.h
|
|
colall.h . . . . . . . . . . . include\colall.h
|
|
indcmap.cpp . . . . . . . . . . gen\indcmap.c
|
|
indcmap.h . . . . . . . . . . . include\indcmap.h
|
|
palette.cpp . . . . . . . . . . gen\palette.c
|
|
palette.h . . . . . . . . . . . include\palette.h
|
|
pch.cpp . . . . . . . . . . . . new, for faster compiles
|
|
rampif.h . . . . . . . . . . . new, interface to d3dif
|
|
rampmap.cpp . . . . . . . . . . gen\rampmap.c
|
|
rampmap.h . . . . . . . . . . . include\rampmap.h
|
|
rampmat.cpp . . . . . . . . . . ramp\ramplt.cpp
|
|
rampmat.hpp . . . . . . . . . . ramp\ramplti.h
|
|
rampmisc.h . . . . . . . . . . new, odds and ends
|
|
rgbmap.cpp . . . . . . . . . . gen\rgbmap.c
|
|
rgbmap.h . . . . . . . . . . . include\rgbmap.h
|
|
|
|
diesel\direct3d\rast\rampspan\ diesel\direct3d\drivers\
|
|
rrampgen.cpp . . . . . . . . . ramp\rampgen.c
|
|
Other files in rampspan directory based on general beaded span code.
|
|
|
|
Ramp rasterization is pretty straight forward. We just do what the DX5 code
|
|
did, within the new span rasterization architecture. Therefore, the rest of
|
|
this document will describe the ramp material code.
|
|
|
|
Exported API's to d3dif
|
|
=======================
|
|
|
|
These are all declared in rampif.h and implemented in rampmat.cpp. For
|
|
convenience, the in file documentation is gathered together here.
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// RLDDIRampCreate
|
|
//
|
|
// Creates the original RLDDIRampLightingDriver with associated lists. The
|
|
// pointer returned is kept in pCtx->pRampDrv.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
RLDDIRampLightingDriver* RLDDIRampCreate(PD3DI_RASTCTX pCtx);
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// RLDDIRampDestroy
|
|
//
|
|
// Destroys a RLDDIRampLightingDriver with all associated objects and memory.
|
|
// The pCtx->pRampDrv pointer should be set to NULL after this occurs.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
void RLDDIRampDestroy(RLDDIRampLightingDriver* drv);
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// RLDDIRampBeginSceneHook
|
|
//
|
|
// Called at clear time to advance material ages and process deferred color
|
|
// setting. Also deletes orphaned IntMaterials.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
void RLDDIRampBeginSceneHook(RLDDIRampLightingDriver* driver);
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// RLDDIRampEndSceneHook
|
|
//
|
|
// Called at update time to reclaim unused color resources and reclaim
|
|
// memory for old IntMaterials.
|
|
//
|
|
// The age1 list will contain materials which were active last frame
|
|
// and have not been used since the last Clear.
|
|
// We remove their colour resources.
|
|
//
|
|
// The agemax list will contain inactive materials which have not
|
|
// been used for AGE_MAX-1 frames. These are obsolete and we reclaim
|
|
// their memory.
|
|
//
|
|
// NOT CALLING THIS CONSTITUTES A MEMORY LEAK.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
void RLDDIRampEndSceneHook(RLDDIRampLightingDriver* driver);
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// RLDDIRampMaterialChanged
|
|
//
|
|
// Called when the contents of D3DMATERIAL we have already made a ExtMaterial
|
|
// for changes.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
long RLDDIRampMaterialChanged(RLDDIRampLightingDriver* driver, D3DMATERIALHANDLE hMat);
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// RLDDIRampSetMaterial
|
|
//
|
|
// Called when a D3DLIGHTSTATE_MATERIAL changes, which sets the
|
|
// driver->current_material.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
long RLDDIRampSetMaterial(RLDDIRampLightingDriver* driver, D3DMATERIALHANDLE hMat);
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// RLDDIRampCreateMaterial
|
|
//
|
|
// Called to create a new ExtMaterial for a given D3DFE_MATERIAL.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
long RLDDIRampCreateMaterial(RLDDIRampLightingDriver* driver, D3DMATERIALHANDLE hMat, PD3DI_RASTCTX pCtx);
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// RLDDIRampDestroyMaterial
|
|
//
|
|
// Called to delete a ExtMaterial and all associated underlying memory and rampmap
|
|
// allocations for a given D3DFE_MATERIAL.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
long RLDDIRampDestroyMaterial(RLDDIRampLightingDriver* driver, D3DMATERIALHANDLE hMat);
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// RLDDIRampMaterialToPixel
|
|
//
|
|
// Call to convert a previously created material to a color for use in clearing
|
|
// the color buffer, for example.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
unsigned long RLDDIRampMaterialToPixel(RLDDIRampLightingDriver* driver,
|
|
D3DMATERIALHANDLE hMat);
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// RLDDIRampUpdateDDPalette
|
|
//
|
|
// Called before the destination DirectDraw surface is displayed, to set its palette.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
long RLDDIRampUpdateDDPalette(PD3DI_RASTCTX pCtx)
|
|
|
|
/*
|
|
* Find what range of values lighting should take. The base is the
|
|
* pixel value (in fixed point) of the dark end of the material. The
|
|
* shift value is user to convert a 0.16 fixed point shade into the
|
|
* range needed for the material. e.g.
|
|
*
|
|
* pixel = base + (VALTOFX8(shade) << shift);
|
|
*
|
|
*/
|
|
HRESULT ExtMaterial::FindLightingRange(unsigned long* base,
|
|
unsigned long* size,
|
|
BOOL* specular,
|
|
unsigned long** texture_colors);
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// RLDDIRampMakePaletteRGB8
|
|
//
|
|
// Call to set up the RGB8 palette and rampmap. This is a palette of 216 (== 6**3)
|
|
// entries of 6 gradations each for r, g, b. Using this palette requires
|
|
// multiplies by 6.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
long RLDDIRampMakePaletteRGB8(RLDDIRampLightingDriver* driver);
|
|
|
|
Classes
|
|
=======
|
|
|
|
There are 3 primary classes that handle ramp materials. All the classes make
|
|
significant use of the diesel\direct3d\d3dim\inc\lists.hpp linked list handling
|
|
macros. See lists.hpp (especially the example code at the end) for details.
|
|
|
|
ExtMaterial (rampmat.hpp).
|
|
/*
|
|
* An ExtMaterial is the underlying driver object for an LPDIRECT3DMATERIAL.
|
|
* When used, it creates IntMaterials which are distinguished by different
|
|
* ambient light, fog, D3DMATERIAL value etc.
|
|
*
|
|
* The ExtMaterials are kept on a list in the driver and if not explicitly
|
|
* freed, they are cleaned up when the driver is destroyed.
|
|
*
|
|
* The IntMaterials can outlive the ExtMaterial in the case that the
|
|
* ExtMaterial is destroyed right after use. We add these orphans to a list
|
|
* which is emptied at Clear time since after Clear, no pixels are visible
|
|
* which were rendered with the IntMaterial and it can be freed.
|
|
*/
|
|
|
|
IntMaterial (rampmat.hpp).
|
|
/*
|
|
* The IntMaterial is derived from an ExtMaterial taking into account the
|
|
* driver state when the ExtMaterial was used. Normally IntMaterials are
|
|
* on a list in their owning ExtMaterial. If the external material is
|
|
* destroyed, any active internal materials which it owned are
|
|
* transferred to an orphans list in the driver. This is cleared out
|
|
* next time Clear is called.
|
|
*
|
|
* The internal material has a list of underlying RampMaterials. For
|
|
* a non-textured material, there is exactly one and for a textured
|
|
* material, there is one per color in the texture's palette. The
|
|
* ramp materials track color sharing between internal materials and
|
|
* handle the details of allocating color resources.
|
|
*
|
|
* Internal materials are also chained onto one of a number of lists
|
|
* based on their age. The age of a material is the number of frames
|
|
* since it was last used to render something. When a material is
|
|
* aged, it is rejuvenated by moving it to the age=0 list. Each
|
|
* frame, the lists are rotated by one notch and materials on the
|
|
* oldest list are reclaimed.
|
|
*
|
|
* A material is either active or inactive. Active materials have
|
|
* color resources and are either on the age=0 list (active this
|
|
* frame) or the age=1 list (active last frame). When an inactive
|
|
* material is used, it allocates color resources by attempting to
|
|
* activate the underlying ramp materials.
|
|
*
|
|
* At the end of the frame, on Update, any active materials on the
|
|
* age=1 list must be materials which were active last frame but were
|
|
* not used this frame. We remove their color resources by
|
|
* deactivating the underlying ramp materials.
|
|
*/
|
|
|
|
RampMaterial (rampmat.hpp).
|
|
/*
|
|
* RampMaterials are used by internal materials to represent ranges of
|
|
* colors. They perform low level color allocation by allocating
|
|
* color ranges (RLDDIRamps) from a rampmap.
|
|
*
|
|
* A textured internal material can use many ramp materials.
|
|
* Several internal materials can use the same ramp material if the
|
|
* colors match. This can happen easily if many textures use the same
|
|
* palette. The ramp material maintains a usage of how many internal
|
|
* materials are using it and is freed when the last one stops.
|
|
*
|
|
* Ramp materials, like internal materials are either active or
|
|
* inactive. Active materials have color resources and inactive
|
|
* materials do not. A ramp material is made active when any of its
|
|
* internal material users are active and inactive when none of then
|
|
* are active. To track this a count of how many active users is
|
|
* maintained.
|
|
*
|
|
* When a material is made active, it attempts to allocate a color
|
|
* range to use. If that is successful, it sets the colors in the
|
|
* range to an appropriate ramp of colors. If is no more space in the
|
|
* colormap for a new range, it finds the closes active ramp material
|
|
* and shares its ramp.
|
|
*
|
|
* To track active materials and sharing materials, the driver has a
|
|
* list of active materials and each material has a list of sharers.
|
|
* The sharers list is only valid for materials which are both active
|
|
* and which own their ramp.
|
|
*/
|
|
|
|
RLDDIColormap (cmap.h). Common name cmap. Holds pointer to a
|
|
RLDDIColorAllocator, and cmap->priv->map is the master 32K pixelmap.
|
|
|
|
RLDDIRamp (rampmap.h). Common name ramp. Is an element of a RLDDIRampmap
|
|
circular queue, and consists of a base (int), size (int), and free (int, BOOL
|
|
in meaning) elements. It is used to allocate space in the master pixelmap.
|
|
|
|
RLDDIRampmap (rampmap.h). Common name rampmap. Holds circular queues of
|
|
RLDDIRamp objects on free and allocated lists. It also holds a pointer back to
|
|
the cmap object.
|
|
|
|
RLDDIPalette (palette.h). Common name pal. Holds palette entries, a list of
|
|
free palette entries, and methods to allocate, set, and free colors.
|
|
|
|
A Day in the Life of a Ramp
|
|
===========================
|
|
|
|
At the beginning of time, RLDDIRampCreate (rampmat.cpp) is called. This
|
|
creates and initializes a RLDDIRampLightingDriver with its embedded
|
|
RLDDISoftLightingDriver. The structure is zeroed, then the lists and hash
|
|
table is initialized. Then InitRampmap is called. If the output surface is
|
|
PALETTE4 or PALETTE8, RLDDICreatePalette (palette.cpp) is used to create a 16
|
|
or 256 palette in driver->palette. Otherwise, RLDDICreateRGBMap (rgbmap.cpp)
|
|
is called to create driver->rgbmap. An rgbmap is just the masks and shifts
|
|
necessary to turn an arbitrary 24 bit RGB color into its nearest equivalent in
|
|
the output surface format. Thus, rgbmap->alloc.allocate_color doesn't actually
|
|
allocate anything, it just returns the nearest output color to the input 24 bit
|
|
RGB color.
|
|
|
|
Once an RLDDIColorAllocator (refered to by alloc) is obtained, a
|
|
RLDDICreateIndirectColormap of static size 32K (the max possible, since color
|
|
indices in DX5 and DX6 code are 1.15.16) is created. A RLDDIColormap called
|
|
cmap is created, and alloc is used to fill the entire 32K entries with black.
|
|
|
|
Then, RLDDIIndirectColormapGetMap is used to access the actual color map we
|
|
malloc'ed and filled with black (cmap->priv->map), and a pointer to this master
|
|
32K array of DWORDS is pointed to by driver->pixelmap and pCtx->pRampMap.
|
|
Thus, pCtx->pRampMap is used to do the final color dereference in the ramp
|
|
rasterizers.
|
|
|
|
Then, RLDDICreateRampmap (rampmap.cpp) is use to create a RLDDIRampmap
|
|
(called rampmap) and its initial RLDDIRamp (called ramp).
|
|
|
|
Then, we are finally finished with RLDDIRampCreate.
|
|
|
|
Next, we would expect to see 1 or more RLDDIRampCreateMaterial's. I take this
|
|
opportunity to fill driver->fog_enable, fog_color with the current renderstate
|
|
values. D3DIF keeps these, along with driver->driver.ambient (represents
|
|
D3DLIGHTSTATE_AMBIENT) up to date. Then, we create a new ExtMaterial (extmat),
|
|
and add this material to the drivers->materials list. This list is currently
|
|
used for cleanup and material handle lookup. We intend to get rid of the
|
|
material handle lookup by forming an association in the runtime. We end by
|
|
initializing the ExtMaterial's copy of the D3DMATERIAL for this material with
|
|
extmat->SetMaterial.
|
|
|
|
The next thing that should happen is RLDDIRampSetMaterial should be called.
|
|
This is not what happens when a material changes, for that,
|
|
RLDDIRampMaterialChanged is called. RLDDIRampSetMaterial sets the
|
|
driver->current_material for later activation, and is called when the
|
|
D3DLIGHTSTATE_MATERIAL is set.
|
|
|
|
Note that nothing much has really happened yet. Next, setup (the rampspan
|
|
rasterizer, right now) innocently calls a RAMP_SERVICE_FIND_LIGHTINGRANGE
|
|
service to find what its ramp base, size, and texture ramp are. This calls
|
|
driver->current_material->FindLightingRange, and causes the ramp to actually be
|
|
created. First, ExtMaterial::FindIntMaterial is called to find the internal
|
|
material that matches the external material's current characteristics. If
|
|
there is no matching IntMaterial (initially there are no internal materials),
|
|
one is created. Next we create an array of RampMaterial's.
|
|
|
|
If we are texture mapping, we create 1 RampMaterial* for every color in the
|
|
texture palette. We also create an array of longs called "colors" that is the
|
|
same size as the texture palette, and will end up being pointed to by
|
|
pTex->pRampmap where it will be used as the final dereference in the ramp
|
|
rasterizer texture read functions. Then, for each color in the texture
|
|
palette, a temporary material is created in which the current material's
|
|
ambient and diffuse factors are modulated by the texture palette color. Note
|
|
that this implies that the one and only supported texture function for ramp is
|
|
modulate, unless the user asks for D3DTBLEND_COPY (I assume we will support
|
|
this with another bead, at some point). Then RampMaterial::Find is called to
|
|
find or create a matching RampMaterial*. Here is where a hash comes in.
|
|
RampMaterial's are hashed based on material diffuse color for fast lookup. If
|
|
a perfectly matching RampMaterial is found, it is used, otherwise, a new
|
|
RampMaterial with the given D3DMATERIAL is created. At this point the array of
|
|
RampMaterial's is just acting like an array of D3DMATERIAL's with hash values.
|
|
Now we are done with the construction of IntMaterial, and IntMaterial has all
|
|
the RampMaterial's it needs to create all the pixelmaps, rampmaps, etc. we will
|
|
need to actually draw a picture.
|
|
|
|
The non-texture mapped case is the same as above, except only one RampMaterial
|
|
is created (for the current one and only D3DMATERIAL associated with this
|
|
RampMaterial).
|
|
|
|
Next, in ExtMaterial::FindLightingRange, after an IntMaterial has been found or
|
|
created, IntMaterial::FindLightingRange is called. The first thing IntMaterial
|
|
does is see if it is active. If it is, Active() need only update the age lists
|
|
so it stays active. Otherwise, as will happen the first time through, we must
|
|
activate the material.
|
|
|
|
Now RampMaterial::Activate is called for each individual RampMaterial. This
|
|
calls RampMaterial::AllocateColors. This calls RLDDIRampmapAllocate with the
|
|
driver->rampmap to allocate some space in the global pixelmap with a new or
|
|
used (but free) RLDDIRamp. Remember, RLDDIRamp's in RLDDIRampmap's are just
|
|
for bookkeeping, they don't really store anything. Then we call
|
|
RampMaterial::SetColors which calls RampMaterial::SetColorsFog or
|
|
RampMaterial::SetColorsStd. This finally calls cmap->set_color which sets the
|
|
appropriate entry in the global pixelmap (that we allocated with the
|
|
RLDDIRampmap) with the alloc function discussed above. For non-palletized
|
|
output surfaces, this just makes an output color given 24 bits of r, g, and b
|
|
and the surface's color masks. For palletized output surfaces, this calls
|
|
RLDDIPaletteAllocateColor in palette.c, which uses the following basic
|
|
algorithm.
|
|
|
|
If an exact match to the desired color already exists in the surfaces palette,
|
|
cool, return it. Otherwise, if there are unused palette entries in the
|
|
surface, make one that matches perfectly. Otherwise, find the closest matching
|
|
color in the palette (uses dr*dr + dg*dg + db*db where dr = r - palette->r as a
|
|
metric) and return that.
|
|
|
|
It is probably worth mentioning here that only a shadow copy of the surfaces
|
|
palette, driver->ddpalette is updated by the palette.cpp SetColor function.
|
|
The palette gets updated in the direct draw output surface by
|
|
RLDDIUpdateDDPalette.
|
|
|
|
To delete a material, RLDDIRampDestroyMaterial is called. First, the
|
|
ExtMaterial destructor is called. If an IntMaterial associated with the
|
|
ExtMaterial is still active, the IntMaterial is placed on the driver->orphans
|
|
list. Orphans are cleaned up when RLDDIRampBeginSceneHook is called, or when
|
|
RLDDIRampDestroy is called to destroy the whole RLDDIRampLightingDriver.
|
|
|
|
Stuff We Do Different (from DX5)
|
|
================================
|
|
|
|
The initialization code in InitRampmap (rampmat.cpp) is all of private
|
|
interpretation, mostly from the DX5 source drivers\win32\ddraw.c. This code
|
|
could not be literally stolen, since it brings in a whole bunch of undesirable
|
|
RLDDI stuff.
|
|
|
|
RLDDIUpdateDDPalette (palette.cpp) is likewise from DX5 source
|
|
drivers\win32\ddraw.c. I'm not sure where this was ever called in DX5, but I
|
|
have put it sucessfully (so far) in rampif.cpp after all FindLightingRange's
|
|
(which is the only thing that can change the palette to change).
|
|
|
|
Issues
|
|
======
|
|
|
|
RLDDIRampEndSceneHook is not being called. This constitutes a memory leak.
|
|
|
|
Need to move the association of the ExtMaterial's with hMat's to the runtime.
|
|
|
|
Hacked DrawPrim ramp lighting is not the same as RenderPrim (halexe.cpp).
|
|
Probably don't need to fix this if Iouri does lighting.
|
|
|
|
Apps that interpolate across vertices from different materials, like oct1 and
|
|
twist may not look exactly the same as they did on DX5, since the appearance
|
|
will depend on where the material got put in the global pixelmap. This, in
|
|
turn, depends on the exact timing of the RLDDIRampBeginSceneHook,
|
|
RLDDIRampEndSceneHook calls, which we will not reasonably be able to reproduce.
|
|
|
|
Apps Compat
|
|
===========
|
|
|
|
NT below means NT 5. 98 means memphis.
|
|
|
|
Direct3D\TestApps\D3DIm
|
|
flipcube.exe O.K. on NT/98
|
|
oct1.exe O.K. on NT/98
|
|
oct2.exe O.K. on NT/98
|
|
sphere.exe O.K. on NT/98
|
|
triangle.exe O.K. on NT/98
|
|
tunnel.exe O.K. on NT/98 Hit F9 3 times to see glitchy problem
|
|
with grid texture. Problem only exists in ramp.
|
|
twist.exe O.K. on NT/98
|
|
|
|
Direct3D\TestApps\D3DRm
|
|
egg.exe O.K. on NT/98
|
|
faces.exe O.K. on NT/98
|
|
fly.exe O.K. on NT/98
|
|
globe.exe O.K. on NT/98
|
|
globe2.exe O.K. on NT/98
|
|
hier1.exe O.K. on NT/98
|
|
hier2.exe O.K. on NT/98
|
|
quat.exe O.K. on NT/98
|
|
shadow.exe O.K. on NT/98
|
|
tex1.exe O.K. on NT/98
|
|
tex3.exe O.K. on NT/98
|
|
tex4.exe O.K. on NT/98
|
|
tex5.exe O.K. on NT/98
|
|
trans.exe O.K. on NT/98
|
|
uvis.exe O.K. on NT/98
|
|
viewer.exe O.K. on NT/98
|
|
|
|
Other
|
|
wipeout2.exe O.K. on NT/98
|
|
hyperblade (win95 only) always uses its own rendering or hardware
|
|
wargods (win95 only) always works, seems to not be using
|
|
rasterizers.
|
|
agile warrior (win95 only) O.K.
|
|
terracide (win95 only) Shows perspectivish problem that can get
|
|
quite pronounced at times.
|
|
|
|
|