📄 renderer.cpp
字号:
assert(SUCCEEDED(result));
}
}
/////////////////////////////////////////////////////////////////////////////
//
// class Renderer rendering primitives
//
/////////////////////////////////////////////////////////////////////////////
//---------------------------------------------------------------------------
// Renderer::clear
//
// Clear the screen and/or frame buffer
void Renderer::clear(int options) {
// Make sure we have a device
if (pD3DDevice == NULL) {
assert(false);
return;
}
// Did they request to clear anything? I suppose you
// could call this function without requesting to clear anything,
// but this is most likely a bug...
if (!(options & (kClearFrameBuffer | kClearDepthBuffer))) {
assert(false);
return;
}
// Figure out what color to use. By default,
// we use black
unsigned frameBufferARGB = MAKE_ARGB(0,0,0,0);
if (options & kClearToConstantColor) {
frameBufferARGB = constantARGB;
assert(!(options & kClearToFogColor));
} else if (options & kClearToFogColor) {
frameBufferARGB = fogColor;
}
// Always slam alpha to zero, if we have alpha in the frame buffer
frameBufferARGB &= 0x00ffffff;
// Figure out what to clear
DWORD clearWhat = 0;
if (options & kClearFrameBuffer) {
clearWhat |= D3DCLEAR_TARGET;
}
if (options & kClearDepthBuffer) {
clearWhat |= D3DCLEAR_ZBUFFER;
}
// Clear it
HRESULT result = pD3DDevice->Clear(
0,
NULL,
clearWhat,
frameBufferARGB,
1.0F,
0
);
assert(SUCCEEDED(result));
}
//---------------------------------------------------------------------------
// Renderer::renderTriMesh
//
// Render a triangle mesh, given vertex data in various formats.
//
// The input is a vertex list, and a triangle list. The vertex list contains
// vertex data in various formats. (See below.) The triangle list is a list
// of indices into the vertex list.
//
// A few performance problems with this code:
//
// 1. The use of DrawIndexedPrimitiveUP, where "UP" stands for "User
// pointer." For "ad-hoc" dynamic geometry (i.e. that which is generated
// procedurally on the frame, this isn't a major problem. However,
// for "fixed" geometry that does not change from frame to frame - such
// as models, static scenery, etc, it would be much better to use
// vertex buffers. But that requires exposing much more of the internals
// to the higher level code than we would want for this "learning"
// environment.
//
// 2. We set the shader and lighting mode for every primitive. Let's
// hope DirectX is smart enough to not stall needlessly.
void Renderer::renderTriMesh(const RenderVertex *vertexList, int vertexCount, const RenderTri *triList, int triCount) {
HRESULT result;
// Make sure we have something to render
if (!checkMesh(vertexList, vertexCount, triList, triCount)) {
return;
}
// Make sure we have a device
if (pD3DDevice == NULL) {
assert(false);
return;
}
// Enable lighting, if user has enabled it
setD3DRenderState(D3DRS_LIGHTING, lightEnable);
// Set the vertex shader using a flexible vertex format
result = pD3DDevice->SetVertexShader(D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1);
assert(SUCCEEDED(result));
// Render it using "user pointer" data
result = pD3DDevice->DrawIndexedPrimitiveUP(
D3DPT_TRIANGLELIST,
0,
vertexCount,
triCount,
triList,
D3DFMT_INDEX16,
vertexList,
sizeof(vertexList[0])
);
assert(SUCCEEDED(result));
}
void Renderer::renderTriMesh(const RenderVertexL *vertexList, int vertexCount, const RenderTri *triList, int triCount) {
HRESULT result;
// Make sure we have something to render
if (!checkMesh(vertexList, vertexCount, triList, triCount)) {
return;
}
// Make sure we have a device
if (pD3DDevice == NULL) {
assert(false);
return;
}
// These are pre-lit vertices. Disable D3D lighting
setD3DRenderState(D3DRS_LIGHTING, FALSE);
// Set the vertex shader using a flexible vertex format
result = pD3DDevice->SetVertexShader(D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1);
assert(SUCCEEDED(result));
// Render it using "user pointer" data
result = pD3DDevice->DrawIndexedPrimitiveUP(
D3DPT_TRIANGLELIST,
0,
vertexCount,
triCount,
triList,
D3DFMT_INDEX16,
vertexList,
sizeof(vertexList[0])
);
assert(SUCCEEDED(result));
}
void Renderer::renderTriMesh(const RenderVertexTL *vertexList, int vertexCount, const RenderTri *triList, int triCount) {
HRESULT result;
// Make sure we have something to render
if (!checkMesh(vertexList, vertexCount, triList, triCount)) {
return;
}
// Make sure we have a device
if (pD3DDevice == NULL) {
assert(false);
return;
}
// These are pre-lit vertices. Disable D3D lighting
setD3DRenderState(D3DRS_LIGHTING, FALSE);
// Set the vertex shader using a flexible vertex format
result = pD3DDevice->SetVertexShader(D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX1);
assert(SUCCEEDED(result));
// Render it using "user pointer" data
result = pD3DDevice->DrawIndexedPrimitiveUP(
D3DPT_TRIANGLELIST,
0,
vertexCount,
triCount,
triList,
D3DFMT_INDEX16,
vertexList,
sizeof(vertexList[0])
);
assert(SUCCEEDED(result));
}
//---------------------------------------------------------------------------
// Renderer::dot
//
// Plots a 2D dot at the given integer coordinates using the current constant
// color (including alpha) and blending state. There is no lighting, fog,
// or z-buffering.
void Renderer::dot(int x, int y) {
// !KLUDGE! Just draw a 1x1 box. Hey, it works!
boxFill(x, y, x+1, y+1);
}
//---------------------------------------------------------------------------
// Renderer::line
//
// Plots an arbitrarily-oriented line from (x1,y1) to (x2,y2), using the
// current constant color (including alpha) and blending state. There is no
// lighting, fog, or z-buffering.
void Renderer::line(int x1, int y1, int x2, int y2) {
// !FIXME!
assert(false);
}
//---------------------------------------------------------------------------
// Renderer::boxFill
//
// Plots a filled rectangle. The x2 and y2 coordinates specify "one past"
// the last pixel to be plotted. (Similar to the Win32 convention.) In
// other words, the width of the rectangle is given by x2-x1. Some people
// find this convention to be "weird" or "off by one." However, it is
// actually much more elegant and usually avoids countless +1's and -1's that
// eventually cancel each other out. This also more closely matches how
// it would work if floating point coordinates were used.
void Renderer::boxFill(int x1, int y1, int x2, int y2) {
// Clip to window
if (x1 < windowX1) {
x1 = windowX1;
}
if (y1 < windowY1) {
y1 = windowY1;
}
if (x2 > windowX2) {
x2 = windowX2;
}
if (y2 > windowY2) {
y2 = windowY2;
}
// Check for empty rectangle
if (x1 >= x2 || y1 >= y2) {
return;
}
// !FIXME!
assert(false);
}
void Renderer::videoSave() {
}
void Renderer::videoRestore() {
}
/////////////////////////////////////////////////////////////////////////////
//
// class Renderer texture cache functions
//
//---------------------------------------------------------------------------
//
// Some notes about the design of the texture cache:
//
// The texture cache is not actually a "cache" per se, since no texture
// is ever automatically ejected from the cache. It's actually just a
// "manager" which keeps track of all the textures that are loaded.
//
// Each texture in the cache is accessible by a "handle." This is a simple
// integer value that the upper level code knows nothing about, other than
// zero is never returned as a texture handle.
//
// In addition, textures may be given a text "name." If a texture does
// not have a name, it is "anonymous" and is only accessible via handle.
// All named textures must have a unique name - there will never be two
// textures with the same name in the cache. Use the findTexture()
// function to search the cache for a texture with a given name and
// retreive its handle. Be warned that this function is slow. Texture
// names are never case sensitive.
//
// A special texture always exists in the cache with the handle
// kWhiteTexture. This texture is a solid white texture, very useful
// for rendering debugging objects, etc. It effectively can be used
// to render an "untextured" object. (For simplicity, we didn't
// support "untextured" rendering - you always must use a texture.)
//
// You can create a texture manually by calling allocTexture() and then
// setTextureImage(). This way, textures can be made procedurally in code.
// Or, you can load a texture from disk using the cacheTexture() function.
// For disk textures, the "name" of the texture is the exact same
// as the filename (including any path and extension.)
//
// You select a texture into the rendering context by handle. You
// may re-allocate, free, or otherwise manipulate the currently selected
// texture. However, in this case, the graphics system may deselect your
// texture and select another texture (like the white texture).
//
// The "name" and "handle" interfaces provide a simple and relatively flexible
// low-level interface to texture cache. A much easier but less flexible
// interface is also provided using the TextureReference structure. This
// interface is the "lazy man's" interface. The TextureReference struct
// keeps track of the texture name and handle in a single struct. What's
// more, you don't even have to maintain the handle yourself - the graphics
// system will do everything for you. All you have to do is fill in the
// name, and then you can cache or select the texture easily. In fact,
// you don't even have to cache the texture before rendering. When you
// select the texture into the rendering context, if it isn't already
// selected, the graphics system will automatically "demand cache" it
// for you. This is not the best for performance, but it will always
// function properly.
//
// Note that to use TextureReference efficiently, they must be kept around
// between frames. Otherwise, the texture must be located by name every
// time it is used - which is going to be slow. For example, don't do this:
//
// void renderSomething() {
//
// // Fill in local texture structure
//
// TextureReference tex;
// strcpy(tex.name, "mytexture.tga");
//
// // Select the texture. YIKES! This works, but will be
// slow.
//
// gRenderer.selectTexture(tex);
//
// // ... render something here using that texture
// }
//
// For best performance, keep the texture reference around persistently,
// either as a class member, global variable, or static local variable.
//
/////////////////////////////////////////////////////////////////////////////
//---------------------------------------------------------------------------
// Renderer::resetTextureCache
//
// Resets texture cache to default state - only containing a single,
// white texture.
void Renderer::resetTextureCache() {
// Free all textures
freeAllTextures();
// Create a small white texture
const int kSz = 32;
int whiteTextureHandle = allocTexture("white", kSz, kSz);
// It should have gone into the reserved "white texture" slot
assert(whiteTextureHandle == kWhiteTexture);
// Set the data
unsigned *whiteImage = new unsigned[kSz*kSz];
memset(whiteImage, 0xff, kSz*kSz*4);
setTextureImage(whiteTextureHandle, whiteImage);
delete [] whiteImage;
// Select it
currentTextureHandle = 0;
selectTexture(kWhiteTexture);
}
//---------------------------------------------------------------------------
// Renderer::findTexture
//
// Locates a texture, by name. Returns the handle to the texture, or 0
// if not found. The search is not case sensitive. "Anonymous" textures
// (those with no name) cannot be located with this function.
int Renderer::findTexture(const char *name) {
// Can't search using an empty name.
assert(name != NULL);
assert(name[0] != '\0');
// Search list linearly. We could implement some sort
// of hash table or binary search to make this fast,
// but this function probably won't be called in speed
// critical situations, so we're going to keep it simple.
for (int i = 1 ; i < nextTextureSlot ; ++i) {
if (stricmp(name, textureCacheList[i].name) == 0) {
return i;
}
}
// Not found
return 0;
}
//---------------------------------------------------------------------------
// Renderer::allocTexture
//
// Creates a texture and optionally gives it a name. If the name
// is specified, then it must be unique - any texture currently
// existing with that name is replaced, and the same handle is returned.
//
// If no name is specified, then the texture is "anonymous" and may only
// be accessed by handle.
//
// The return value is a handle that you can use to reference the texture.
// Texture handles are always > 0.
//
// Currently, this function never returns failure - if there is a problem,
// it will abort the program.
int Renderer::allocTexture(const char *name, int xSize, int ySize) {
TextureCacheEntry *t;
// Can't do this without a device
assert(pD3DDevice != NULL);
// Verify texture size. We require a power of two,
co
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -