script.spt
# This is an example of a comment.
teapotColor 0.0 0.0 1.0
cameraPosition 0.0 0.0 -5.0
# These are not used but are here to test loading strings and ints.
totalTeapots 1
description This just a teapot in a 3D scene!
PropertyScript.h
#ifndef _UGP_PROPERTY_SCRIPT_H_
#define _UGP_PROPERTY_SCRIPT_H_
enum enVarType { NULL_VAR = 0, INT_VAR, FLOAT_VAR,
STRING_VAR, VECTOR_VAR };
struct stVector
{
stVector() : x(0), y(0), z(0) {}
float x, y, z;
};
// 用于描述单个属性的类
class CVariable
{
public:
CVariable() : type(0), floatVal(0), intVal(0), stringVal(0)
{
name[0] = '\0';
vecVal.x = vecVal.y = vecVal.z = 0;
}
~CVariable()
{
if(stringVal)
delete[] stringVal;
stringVal = 0;
}
// @t 属性类型
// @n 属性名字
// @data 属性值
void SetData(int t, char* n, void *data);
// @t 属性类型
// @data 属性值
void SetData(int t, void *data);
char *GetName() { return name; }
int GetType() { return type; }
int GetDataAsInt() { return intVal; }
float GetDataAsFloat() { return floatVal; }
char *GetDataAsString() { return stringVal; }
stVector GetDataAsVector() { return vecVal; }
private:
char name[128]; // 属性名字
int type; // 属性类型
// 属性值
int intVal;
float floatVal;
char *stringVal;
stVector vecVal;
};
class CPropertyScript
{
public:
CPropertyScript();
~CPropertyScript();
// 增加CVariable类链表数组(增加其长度)
bool IncreaseVariableList();
bool LoadScriptFile(char *filename);
private:
// 获取当前所读取的一行中某个单独文本,并保存在第2个参数中
// @tempLine 当前行的所有字符
// @varName 保存该行中某个单独的文本
void ParseNext(char *tempLine, char *varName);
public:
// 手动增加一个属性到属性列表中
// @name 属性名字
// @t 属性类型
// @val 属性值
bool AddVariable(char *name, int t, void *val);
// 更改列表中的某个属性
bool SetVariable(char *name, int t, void *val);
int GetVariableAsInt(char *name);
float GetVariableAsFloat(char *name);
char *GetVariableAsString(char *name);
stVector GetVariableAsVector(char *name);
void Shutdown();
private:
CVariable *variableList; // 属性列表
int m_totalVars; // 属性个数
int currentLineChar; // 正在读取的当前字符的个数的计数器
};
#endif
PropertyScript.cpp
#include"PropertyScript.h"
#include<fstream>
using std::ifstream;
// 全局函数
// 确定要处理的属性类型
int DetermineType(int startIndex, char *buffer)
{
int numComponents = 0;
int type = NULL_VAR;
bool decimalFound = false; // 属性值中是否有点
bool charFound = false;
int index = startIndex;
// Loop through the string and get information about it.
while(index < (int)strlen(buffer))
{
// Since there are no new lines we auto add this if we
// get inside this loop.
// 如果全部是数字不统计个数,只将numComponents变成1
if(numComponents == 0)
numComponents++;
if(buffer[index] == ' ')
numComponents++;
if(buffer[index] == '.')
decimalFound = true;
if((buffer[index] >= 'a' && buffer[index] <= 'z') ||
(buffer[index] >= 'A' && buffer[index] <= 'Z') || buffer[index] == '_')
charFound = true;
index++;
}
// If only one variable is show after the name then it can
// be any type other than vector since vector should have 3.
switch(numComponents)
{
case 1:
// If there are any characters then it is a string.
if(charFound)
type = STRING_VAR;
else
type = INT_VAR;
// If there is a decimal and no chars then its a float.
if(decimalFound == true && charFound == false)
type = FLOAT_VAR;
break;
case 3:
// Since vectors are just floats, if we see any characters
// in the group then it must be a string.
if(charFound)
type = STRING_VAR;
else type = VECTOR_VAR;
break;
default:
// If there are more than 1 word after the name
// then it must be a string since strings can have spaces.
if(numComponents > 0)
type = STRING_VAR;
break;
}
return type;
}
void CVariable::SetData(int t, char *n, void *data)
{
if(!name) return;
// Set this variables name then set the type and data.
memcpy(name, n, strlen(n));
name[strlen(n)] = '\0';
SetData(t, data);
}
void CVariable::SetData(int t, void *data)
{
stVector *vec = NULL;
int len = 0;
// Depending on the type will depend where the
// value is stored.
switch(t)
{
case INT_VAR:
intVal = (int)data;
break;
case FLOAT_VAR:
floatVal = *(float*)data;
break;
case STRING_VAR:
len = strlen((char*)data);
stringVal = new char[len + 1];
memcpy(stringVal, (char*)data, len);
stringVal[len] = '\0';
break;
case VECTOR_VAR:
vec = (stVector*)data;
vecVal.x = vec->x;
vecVal.y = vec->y;
vecVal.z = vec->z;
break;
default:
// If we get here then it is a NULL variable.
return;
break;
};
type = t;
}
CPropertyScript::CPropertyScript() : variableList(NULL),
m_totalVars(0), currentLineChar(0)
{
}
CPropertyScript::~CPropertyScript()
{
// Release all resources.
Shutdown();
}
bool CPropertyScript::IncreaseVariableList()
{
if(!variableList)
{
variableList = new CVariable[1];
if(!variableList)
return false;
}
else
{
CVariable *temp;
temp = new CVariable[m_totalVars + 1];
if(!temp)
return false;
memcpy(temp, variableList, sizeof(CVariable) * m_totalVars);
delete[] variableList;
variableList = temp;
}
return true;
}
bool CPropertyScript::LoadScriptFile(char *filename)
{
int totalScriptLines = 0;
ifstream input, input2;
char tempLine[256];
char varName[128];
char param[3072];
int type = 0;
// Open the file to get the number of lines from it.
input.open(filename);
if(!input.is_open())
return false;
// Clear all previous data.
Shutdown();
// Open and get number of lines.
while(!input.eof())
{
input.getline(tempLine, 256, '\n');
totalScriptLines++;
}
input.close();
// Open it this time to get the variables out.
input2.open(filename);
if(!input2.is_open())
return false;
// Loop through each line of the script and get all variables.
for(int i = 0; i < totalScriptLines; i++)
{
// Reset line counter to the beginning.
currentLineChar = 0;
// Read the entire line from the file.
input2.getline(tempLine, 256, '\n');
tempLine[strlen(tempLine)] = '\0';
// 如果不是注释则解析这一行
if(tempLine[0] != '#')
{
// Read the name then determine the type.
// 先将该行总的属性名字读到varName中去,同时移动currentLineChar指针
ParseNext(tempLine, varName);
// 利用改变后的currentLineChar指针,此时它已经指向了属性值的起始位置,
// 判断属性值得类型
type = DetermineType(currentLineChar, tempLine);
// Depending on the type will depend on how many
// words we need to read after the name. For
// ints we need 1, vectors 3, strings 1, etc.
// Once we get the data we convert it to the
// type we need and set it to the variable.
if(type == INT_VAR)
{
if(IncreaseVariableList())
{
// 由于currentLineCha已经指向了属性值的起始位置,此时提取该行的属性值
ParseNext(tempLine, param);
variableList[m_totalVars].SetData(INT_VAR, varName, (void*)atoi(param));
m_totalVars++;
}
}
else if(type == FLOAT_VAR)
{
if(IncreaseVariableList())
{
float fVal = 0;
ParseNext(tempLine, param);
fVal = (float)atof(param);
variableList[m_totalVars].SetData(FLOAT_VAR, varName, &fVal);
m_totalVars++;
}
}
else if(type == STRING_VAR)
{
if(IncreaseVariableList())
{
ParseNext(tempLine, param);
variableList[m_totalVars].SetData(STRING_VAR, varName, (void*)param);
m_totalVars++;
}
}
else if(type == VECTOR_VAR)
{
if(IncreaseVariableList())
{
stVector vecVal;
// 需要提取3个属性值,每个之间空格隔开
ParseNext(tempLine, param);
vecVal.x = (float)atof(param);
ParseNext(tempLine, param);
vecVal.y = (float)atof(param);
ParseNext(tempLine, param);
vecVal.z = (float)atof(param);
variableList[m_totalVars].SetData(VECTOR_VAR, varName, &vecVal);
m_totalVars++;
}
}
}
}
// Close file, return true.
input2.close();
return true;
}
void CPropertyScript::ParseNext(char *tempLine, char *outData)
{
int commandSize = 0;
int paramSize = 0;
// Error checking.
if(!tempLine || !outData) return;
// Init string.
outData[0] = '\0';
// Loop through every character until you find a space or newline.
while(currentLineChar < (int)strlen(tempLine))
{
if(tempLine[currentLineChar] == ' ' || tempLine[currentLineChar] == '\n')
break;
// Save the text in the array.
outData[paramSize] = tempLine[currentLineChar];
paramSize++;
currentLineChar++;
}
// End the out string and move the line char past the next space.
// If there is no space then the system will move to the next line.
outData[paramSize] = '\0';
currentLineChar++;
}
bool CPropertyScript::AddVariable(char *name, int t, void *val)
{
// We can use this to see if the variable exist already.
if(!SetVariable(name, t, val))
{
if(!IncreaseVariableList())
return false;
// Set the variables data then add to the counter.
variableList[m_totalVars].SetData(t, name, val);
m_totalVars++;
}
return true;
}
bool CPropertyScript::SetVariable(char *name, int t, void *val)
{
// Loop through the list and compare names.
// If we find the variable set its data.
for(int i = 0; i < m_totalVars; i++)
{
if(stricmp(variableList[i].GetName(), name) == 0)
{
variableList[i].SetData(t, val);
return true;
}
}
return false;
}
int CPropertyScript::GetVariableAsInt(char *name)
{
// Loop through the list and compare names.
// If we find the variable return its data.
for(int i = 0; i < m_totalVars; i++)
{
if(stricmp(variableList[i].GetName(), name) == 0)
return variableList[i].GetDataAsInt();
}
return 0;
}
float CPropertyScript::GetVariableAsFloat(char *name)
{
// Loop through the list and compare names.
// If we find the variable return its data.
for(int i = 0; i < m_totalVars; i++)
{
if(stricmp(variableList[i].GetName(), name) == 0)
return variableList[i].GetDataAsFloat();
}
return 0;
}
char *CPropertyScript::GetVariableAsString(char *name)
{
// Loop through the list and compare names.
// If we find the variable return its data.
for(int i = 0; i < m_totalVars; i++)
{
if(stricmp(variableList[i].GetName(), name) == 0)
return variableList[i].GetDataAsString();
}
return NULL;
}
stVector CPropertyScript::GetVariableAsVector(char *name)
{
stVector null;
// Loop through the list and compare names.
// If we find the variable return its data.
for(int i = 0; i < m_totalVars; i++)
{
if(stricmp(variableList[i].GetName(), name) == 0)
return variableList[i].GetDataAsVector();
}
return null;
}
void CPropertyScript::Shutdown()
{
// Delete list.
if(variableList)
delete[] variableList;
variableList = NULL;
}
main.cpp
#include<d3d9.h>
#include<d3dx9.h>
#include"PropertyScript.h"
#define WINDOW_CLASS "UGPDX"
#define WINDOW_NAME "Property Scripting"
#define WINDOW_WIDTH 640
#define WINDOW_HEIGHT 480
// Function Prototypes...
bool InitializeD3D(HWND hWnd, bool fullscreen);
bool InitializeObjects();
void RenderScene();
void Shutdown();
// Direct3D object and device.
LPDIRECT3D9 g_D3D = NULL;
LPDIRECT3DDEVICE9 g_D3DDevice = NULL;
// Matrices.
D3DXMATRIX g_projection;
D3DXMATRIX g_ViewMatrix;
D3DXMATRIX g_WorldMatrix;
// Mesh objects
LPD3DXMESH g_teapot = NULL;
D3DMATERIAL9 g_material; // 材质
// Scene light source.
D3DLIGHT9 g_light; // 灯光
LRESULT WINAPI MsgProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
{
switch(msg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
break;
case WM_KEYUP:
if(wp == VK_ESCAPE) PostQuitMessage(0);
break;
}
return DefWindowProc(hWnd, msg, wp, lp);
}
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE ph, LPSTR cmd, int s)
{
// Register the window class
WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L,
GetModuleHandle(NULL), NULL, NULL, NULL, NULL,
WINDOW_CLASS, NULL };
RegisterClassEx(&wc);
// Create the application's window
HWND hWnd = CreateWindow(WINDOW_CLASS, WINDOW_NAME,
WS_OVERLAPPEDWINDOW, 100, 100, WINDOW_WIDTH, WINDOW_HEIGHT,
GetDesktopWindow(), NULL, wc.hInstance, NULL);
// Initialize Direct3D
if(InitializeD3D(hWnd, false))
{
// Show the window
ShowWindow(hWnd, SW_SHOWDEFAULT);
UpdateWindow(hWnd);
// Enter the message loop
MSG msg;
ZeroMemory(&msg, sizeof(msg));
while(msg.message != WM_QUIT)
{
if(PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
RenderScene();
}
}
// Release any and all resources.
Shutdown();
// Unregister our window.
UnregisterClass(WINDOW_CLASS, wc.hInstance);
return 0;
}
bool InitializeD3D(HWND hWnd, bool fullscreen)
{
D3DDISPLAYMODE displayMode;
// Create the D3D object.
g_D3D = Direct3DCreate9(D3D_SDK_VERSION);
if(g_D3D == NULL) return false;
// Get the desktop display mode.
if(FAILED(g_D3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT,
&displayMode))) return false;
// Set up the structure used to create the D3DDevice
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
if(fullscreen)
{
d3dpp.Windowed = FALSE;
d3dpp.BackBufferWidth = WINDOW_WIDTH;
d3dpp.BackBufferHeight = WINDOW_HEIGHT;
}
else
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = displayMode.Format;
// Create the D3DDevice
if(FAILED(g_D3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,
hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp,
&g_D3DDevice))) return false;
// Set the projection matrix.
D3DXMatrixPerspectiveFovLH(&g_projection, 45.0f,
WINDOW_WIDTH/WINDOW_HEIGHT, 0.1f, 1000.0f);
g_D3DDevice->SetTransform(D3DTS_PROJECTION, &g_projection);
// Initialize any objects we will be displaying.
if(!InitializeObjects()) return false;
return true;
}
bool InitializeObjects()
{
// Property scripting system and properties for our object.
CPropertyScript propertyScript;
stVector teapotCol, camPos;
// Load the script.
if(!propertyScript.LoadScriptFile("script.spt"))
return false;
// Get the variables we need.
teapotCol = propertyScript.GetVariableAsVector("teapotColor");
camPos = propertyScript.GetVariableAsVector("cameraPosition");
// Release all resources. This script only needs to run once.
propertyScript.Shutdown();
// Set default rendering states.
g_D3DDevice->SetRenderState(D3DRS_LIGHTING, TRUE);
g_D3DDevice->SetRenderState(D3DRS_AMBIENT, D3DCOLOR_COLORVALUE(0.3f, 0.3f, 0.3f, 1.0f));
// Setup the light source.
g_light.Type = D3DLIGHT_DIRECTIONAL;
g_light.Direction = D3DXVECTOR3(0.0f, 0.0f, 1.0f);
g_light.Diffuse.r = g_light.Diffuse.g = 1;
g_light.Diffuse.b = g_light.Diffuse.a = 1;
g_light.Specular.r = g_light.Specular.g = 1;
g_light.Specular.b = g_light.Specular.a = 1;
// Register the light.
g_D3DDevice->SetLight(0, &g_light);
g_D3DDevice->LightEnable(0, TRUE);
// Setup the material properties for the teapot.
ZeroMemory(&g_material, sizeof(D3DMATERIAL9));
g_material.Ambient.r = 0.6f;
g_material.Ambient.g = 0.6f;
g_material.Ambient.b = 0.7f;
g_material.Diffuse.r = teapotCol.x;
g_material.Diffuse.g = teapotCol.y;
g_material.Diffuse.b = teapotCol.z;
g_material.Specular.r = 0.4f;
g_material.Specular.g = 0.4f;
g_material.Specular.b = 0.4f;
g_material.Power = 8.0f;
// Create the objects.
if(FAILED(D3DXCreateTeapot(g_D3DDevice, &g_teapot, NULL)))
return false;
// Define camera information.
D3DXVECTOR3 cameraPos(camPos.x, camPos.y, camPos.z);
D3DXVECTOR3 lookAtPos(0.0f, 0.0f, 0.0f);
D3DXVECTOR3 upDir(0.0f, 1.0f, 0.0f);
// Build view matrix.
D3DXMatrixLookAtLH(&g_ViewMatrix, &cameraPos, &lookAtPos, &upDir);
return true;
}
void RenderScene()
{
// Clear the backbuffer.
g_D3DDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,0), 1.0f, 0);
// Begin the scene. Start rendering.
g_D3DDevice->BeginScene();
// Apply the view (camera).
g_D3DDevice->SetTransform(D3DTS_VIEW, &g_ViewMatrix);
// Draw teapot.
g_D3DDevice->SetMaterial(&g_material);
g_teapot->DrawSubset(0);
// End the scene. Stop rendering.
g_D3DDevice->EndScene();
// Display the scene.
g_D3DDevice->Present(NULL, NULL, NULL, NULL);
}
void Shutdown()
{
// Release all resources.
if(g_D3DDevice != NULL) g_D3DDevice->Release();
if(g_D3D != NULL) g_D3D->Release();
if(g_teapot != NULL) { g_teapot->Release(); g_teapot = NULL; }
}