⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 renderer.cpp

📁 3D数学基础:图形与游戏开发书籍源码,里面有很多实用的代码,对做3D的同志很有意义
💻 CPP
📖 第 1 页 / 共 5 页
字号:
/////////////////////////////////////////////////////////////////////////////
//
// 3D Math Primer for Games and Graphics Development
//
// Renderer.cpp - Very simple low-level 3D renderer interface
//
// Visit gamemath.com for the latest version of this file.
//
// --------------------------------------------------------------------------
//
// This file implements a very simple 3D renderer via Direct3D.  Our goal was
// to provide a simple, cross-platform rendering engine.  If you do not want
// to learn DirectX (and there are MANY people who work in graphics related
// firlds who have no need to know DirectX) then you don't need to look
// at the inside of this file - you can just get the benefits of a simple,
// clean rendering API without digging through mountains of DirectX
// documentation.
//
// <soapbox>
//
// Nowadays lots of people want to learn Direct3D, before they know graphics.
// As is typical of Microsoft, some people act as if the two are the same,
// like DirectX is "how things are done."  Graphics is an academic pursuit
// unto itself, and many researchers spent many years investigating
// techniques and interfaces for rendering, long before Microsoft got
// involved in DirectX.  It is more important to learn how graphics work
// from a platform- and API-independent standpoint than it is to learn how
// DirectX works, with all its quirks and shortcomings.  And DirectX changes
// every year - sometimes radically.  Most serious projects do most of their
// high-level graphics work using a graphics abstraction (such as this one)
// and leave the messy details hidden in a lower level.  Certainly any
// cross-platform project must do this, but even ones only destinated for
// one platform can make use of this insulation.  Please don't put DirectX
// code anywhere in your high level code.  In fact you may notice that all
// of the files on gamemath.com can be used to make a platform-independent
// game loop, with the "game" files (the project-specific files) never
// including a big huge mess of stuff like <windows.h> or <xtl.h>.
//
// Many other books or Internet sources provide so-called "wrappers"
// around DirectX, which are designed to make it "easy" to write DirectX
// applications.  Supposedly, you can use their code without knowing any
// DirectX.  However, let us differentiate between a "wrapper" and an
// "abstraction layer."  A wrapper is a very thin translation layer,
// usually designed to do nothing more than translate calls.  It often
// can decrease compile times, facilitate DLL linkage, or provide shorthand
// for commonly used operations.  However, a "wrapper" really doesn't
// help insulate you from the implementation details any or provide any
// platform independence.  An abstraction layer, on the other hand,
// is designed to provide insulation from implementation details and/or
// platform independance.  We have provided an abstraction layer, not a
// wrapper.  This is evidenced by the fact that the header file does
// not contain a single platform independent element and could be ported
// to any number of platforms without a line of code being changed.
//
// Many wrappers or abstraction layers provided in books and on the Internet
// are useless because they are just as complicated or have as many quirks
// (or bugs) as the "messy" internals they proport to hide you from.
// (And what's more - they are usually more poorly documented!)
// Therefore, you not only have to learn DirectX, for example, but you have
// to learn their weird API as well.  We have tried to design our interface
// layer so that it provides basic graphics functionality in the most
// straightforward manner possible, truly hiding DirectX details.  Of course,
// with any interface, there is a learning curve associated with it.  We
// hope that our design and comments make this curve as short as possible.
//
// </soapbox>
//
// So, the interface is completely platform independent, and much of the
// implentation is as well.  All that said, however, eventually you have to
// impement the interface on a particular platform.  It is possible, and
// in many ways advantageous, to make this file sit atop different
// lower level layers - i.e. one for DirectX, one for OpenGL, one for
// PlayStationII, etc.  For simplicity, we have not done this.  We have
// chosen one API (Direct3D) and made calls to it directly in this file.
// This makes it basically impossible to support multiple platforms or
// APIs easily without duplciating the entire contents of the file, but the
// purpose of this code is ease of understanding the API most people are
// most interested in learning - DirectX.
//
// We have also not concerned ourselved with optimization too much.  This
// is an "engine" designed for you to look at the guts and learn how things
// work.  Optimization would complicate the internals significantly and
// make things more difficult to understand.
//
// There is only one instance of class Renderer, a global variable
// gRenderer.  (It is a "singleton" in C++ terminology.)  Some internal
// state variables are declared here as private members.  Other internal
// state variables are declared statically in the various C++ files -
// declaring all the variables here as private would be a drain on compile
// times, since it would require declaring more structures and including
// other files that are not necessary.  C++ has the unfortunate property
// that all class internal members are visible to clients of the class, even
// if they are not accessible.  In other words, they must be processed by
// the compiler, and if they are changed, seemingly unrelated files are
// often recompiled.  Putting state variables statically, rather than in
// the private section, avoids this problem.  A C++ purist might object,
// but a person waiting on the compiler will not.
//
/////////////////////////////////////////////////////////////////////////////

#include <assert.h>

#include "CommonStuff.h"
#include "Renderer.h"
#include "WinMain.h"
#include "MathUtil.h"
#include "Bitmap.h"

#include <d3d8.h>

/////////////////////////////////////////////////////////////////////////////
//
// local data
//
/////////////////////////////////////////////////////////////////////////////

// Direct3D interface object

static LPDIRECT3D8 		pD3D = NULL;

// Direct3D device interface

static LPDIRECT3DDEVICE8 	pD3DDevice = NULL;

// List of video modes

static int		videoModeCount;
static VideoMode	*videoModeList;

// The clip matrix.  This transforms camera space points to clip
// space, aka "canonical view volume space."  it is computed
// by computeClipMatrix().  D3D calls this matrix the "projection"
// matrix

static D3DMATRIX	clipMatrix;

// Instance stack system.  This is an OpenGL-like system to manage the
// current reference frame.  For example, by default, without instancing,
// all 3D coordinates submitted to the rendering system are assumed to be
// in world space.  Now, let's say we "instance" into an object's local
// reference frame, by specifying the position and orientation of the
// object.  Now any 3D coordinates we submit will be transformed from
// local space to world space and then into camera space.  Instancing
// can be performed multiple times, for example, to render a tire within a
// car.

struct InstanceInfo {

	// The model->world matrix

	Matrix4x3	modelToWorldMatrix;
};

const int	kMaxInstanceDepth = 8;

static int		instanceStackPtr = 0;
static InstanceInfo	instanceStack[kMaxInstanceDepth];

// The model->clip matrix.  This matrix takes a point in the current
// reference frame, and transforms it to clip space.  Note that this is a
// 4x4 matrix.  We could have our own nice 4x4 matrix class, but since they
// are actually not used very much outside of the graphcis internals, we just
// use D3D's here

static D3DMATRIX	modelToClipMatrix;

// The modelToClipMatrix is only used by the software vertex processing
// routines.  it's relatively expeisive to compute.  So we won't compute
// it every time.  We'll just keep track of if it's valid or not,
// and compute it on demand whenever it is needed.  This flag keeps
// tradck of whether the modelToClipMatrix is "dirty" and needs to be
// recomputed.

static bool	needToComputeModelToClipMatrix = true;

// Model->camera matrix.  This matrix takes a point in the current
// reference frame, and transforms it to camera space.  This
// transform does not contain the zoom or perspective projection, and so
// we are using our 4x3 matrix class.  This matrix is computed when
// the modelToClipMatrix is computed

static Matrix4x3	modelToCameraMatrix;

// The current D3D "material" which controls material properties for
// the standard local illumunation model.  We will only be using a few
// of these values.

static D3DMATERIAL8 d3dMaterial;

// Floating point projection values, for software projection

static float	windowCenterX, windowCenterY;
static float	halfWindowSizeX, halfWindowSizeY;

// Precomputed far fog distance, as a clip-space z value.

static float	farFogClipSpaceZ;

// The D3D directional light

static D3DLIGHT8 d3dDirectionalLight;

// Texture cache variables.  See the notes above the resetTextureCache()
// for more details.

struct TextureCacheEntry {

	// Symbolic name of the texture (usually a filename)

	char	name[kMaxTextureNameChars];

	// Size

	int	xSize, ySize;

	// Direct3D interface object.  We're going to let D3D manage
	// the memory

	LPDIRECT3DTEXTURE8	d3dTexture;
};

// max number of textures - you may need more than this

const int	kTextureCacheSize = 512;

// next available texture slot

static int	nextTextureSlot;

// The texture cache bookeeping info

static TextureCacheEntry	textureCacheList[kTextureCacheSize];

/////////////////////////////////////////////////////////////////////////////
//
// local utility helper functions
//
/////////////////////////////////////////////////////////////////////////////

//---------------------------------------------------------------------------
// setD3DRenderState
//
// Basically just a thin wrapper around pD3DDevice->SetRenderState,
// with added pointer safety check and validation of the return code,
// for debugging.
//
// For speed, we could cache the states to prevent setting states
// redundantly.  However, most of the higher level code should already be
// doing that, and hopefully Microsoft is doing the same as well.

static void	setD3DRenderState(D3DRENDERSTATETYPE state, unsigned value) {

	// Make sure device exists

	if (pD3DDevice == NULL) {
		assert(false);
		return;
	}

	// !SPEED! We could check if the state actually changed here.
	// Or, we could assume that the good folks in Redmond will
	// do this for us.  Ahem.....

	// Set the state

	HRESULT result = pD3DDevice->SetRenderState(state, value);

	// Check for error

	assert(SUCCEEDED(result));
}

//---------------------------------------------------------------------------
// setD3DDirectionalLight
//
// Install the directional light into D3D render context

static void	setD3DDirectionalLight() {

	// Make sure device exists

	if (pD3DDevice == NULL) {
		assert(false);
		return;
	}

	// Set light to D3D at light index 0

	HRESULT result = pD3DDevice->SetLight(0, &d3dDirectionalLight);

	// Check for error

	assert(SUCCEEDED(result));
}

//---------------------------------------------------------------------------
// checkMesh
//
// Debug utility function to check that a triangle mesh is valid.
//
// Returns true if the mesh is OK to be rendered

static bool checkMesh(const void *vertexList, int vertexCount, const RenderTri *triList, int triCount) {

	// No triangles?

	if (triCount < 1) {
		return false;
	}

	// NULL pointers?

	if ((vertexList == NULL) || (triList == NULL)) {
		assert(false);
		return false;
	}

	// Check in a debug build that all the indices are valid

	#ifdef _DEBUG
		for (int i = 0 ; i < triCount ; ++i) {
			for (int j = 0 ; j < 3 ; ++j) {
				int	index = triList[i].index[j];
				if ((index < 0) || (index >= vertexCount)) {
					assert(false);
					return false;
				}
			}
		}
	#endif

	// Mesh is OK

	return true;
}

/////////////////////////////////////////////////////////////////////////////
//
// class Renderer master init stuff
//
/////////////////////////////////////////////////////////////////////////////

// Global renderer class object

Renderer	gRenderer;

//---------------------------------------------------------------------------
// Renderer::Renderer
//
// Constructor - Reset internal state variables

Renderer::Renderer() {

	// Slam some internal variables

	screenX = 0;
	screenY = 0;
	cameraPos.zero();
	cameraOrient.identity();
	zoomX = 1.0f; // 90 degree field of view
	zoomY = 0.0f; // auto-compute
	nearClipPlane = 1.0f;
	farClipPlane = 1000.0f;
	windowX1 = 0;
	windowY1 = 0;
	windowX2 = 0;
	windowY2 = 0;
	windowSizeX = 0;
	windowSizeY = 0;
	depthBufferRead = true;
	depthBufferWrite = true;
	blendEnable = true;
	sourceBlendMode = eSourceBlendModeSrcAlpha;
	destBlendMode = eDestBlendModeInvSrcAlpha;
	constantARGB = MAKE_ARGB(255,0,0,0);
	constantOpacity = 1.0f;
	fogEnable = false;
	fogColor = MAKE_RGB(255,255,255);
	fogNear = 0.0f;
	fogFar = 1000.0f;
	lightEnable = true;
	ambientLightColor = MAKE_RGB(64,64,64);
	directionalLightVector.x = .707f;
	directionalLightVector.y = -.707f;
	directionalLightVector.z = 0.0f;
	directionalLightColor = MAKE_RGB(192,192,192);
	backfaceMode = eBackfaceModeCCW;
	currentTextureHandle = 0;
	textureClamp = false;

	// And now set the camera, to force some stuff to be
	// recomputed

	setCamera(kZeroVector, kEulerAnglesIdentity);

	// Set level 0 instance (the world) reference frame

	instanceStack[0].modelToWorldMatrix.identity();
}

//---------------------------------------------------------------------------
// Renderer::getVideoModeCount
//
// Returns the number of viewo modes available, or 0 if 3D is no workey

int	Renderer::getVideoModeCount() {

	// Check if we already know

	if (videoModeCount > 0) {
		return videoModeCount;
	}

	// List has not yet been created.  Nothing should be allocated yet

	assert(pD3D == NULL);
	assert(pD3DDevice == NULL);
	
	// Create a Direct3D object

	pD3D = Direct3DCreate8(D3D_SDK_VERSION);
	if (pD3D == NULL) {
		videoModeCount = 0;
		return 0;
	}

	// Enumerate the adapter modes in two passes.  On the first pass,
	// we'll just count the number of modes.  On the second pass,
	// we'll actually fill in the mode list

	for (int pass = 0 ; pass < 2 ; ++pass) {

		// Enumerate modes

		int	modeIndex = 0;
		while (modeIndex < pD3D->GetAdapterModeCount(D3DADAPTER_DEFAULT)) {

			// Enumerate the next mode.

			D3DDISPLAYMODE mode;
			HRESULT result = pD3D->EnumAdapterModes(
				D3DADAPTER_DEFAULT,
				modeIndex,
				&mode
			);
			if (FAILED(result)) {
				break;
			}
			++modeIndex;

			// Convert D3D mode structure to our own

			VideoMode ourMode;
			ourMode.xRes = mode.Width;
			ourMode.yRes = mode.Height;
			ourMode.refreshHz = mode.RefreshRate;
			switch (mode.Format) {
				case D3DFMT_A8R8G8B8:
					ourMode.bitsPerPixel = 32;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -