Nebula是德国一套开源的3d引擎,具体怎样这里不作评价,相关资料可从网上搜索.
官方网站参见: http://www.radonlabs.de
使用Nebula也算蛮久了, 为了给网站充数点内容,现在来说说它所用到的一些c++相关技术
如何实现一套与脚本紧密联系的对象管理系统(将在后面给出自己仿照nebula而写的完整可运行的程序):
一.基本的思想:
1. 程序中所产生的对象用类似于windows操作系统中的目录结构来管理,
也就是每个对象是一个结点(有对应的名称),每个结点下可以有多个子结点(也有各自对应的名称).如此循环...
2. 程序中有了以上的"目录结构式的对象管理系统", 脚本可通过与程序结合来实现在脚本中调用相应方法来创建
各目录及对象,并可在脚本中通过相应的目录结构名称来访问每一个对象,以及在各对象之间传递消息.当然也可以通过目录
结构名来删除对象(包括其目录下的子对象也会自动删除).
3. 有了以上的一切就有了一套与脚本紧密联系的对象管理系统.下面给出一个使用tcl脚本来撰写的例子参考如下
(这个例子只供参考,具体可实际编译运行通过的例子将在后面详解后给出):
#--------------------------------------------------------------------------
# 创建:
new
nd3d8server
/sys/servers/gfx
# 访问:
sel /sys/servers/gfx
.setdisplaymode
"-type(win)-w(800)-h(600)"
.setclearcolor
0.0 0.0 0.0 0
.setperspective
45 1.33 1.0 10000.0
sel ..
# 删除:
delete /sys/servers/gfx
#--------------------------------------------------------------------------
[说明:]
前面带"#"号的行表示注释.
new 是nebula实现的方法, 可以根据给出的字符串参数产生对应的类型对象
sel 是nebula实现的方法, 可以根据给出的字符串参数选择对应的类型对象, 当参数为".."时表示退到上一层目录
二.具体实现:
首先我们分析要实现如上的脚本目录对象系统所需要的技术:
1. 程序中要实现目录结构的对象管理系统.
2. 要有一套脚本系统,这可以用现成开源的.这里我们选择nebula习惯所用的tcl(当然你可以选择其它)
3. 程序与脚本结合(交互).
下面我们先分别讨论一下nebula是如何如何实现上述各点的:
-- 1.先看如何实现第1点:
a. 首先nebula有一个叫nRoot的类,它是所有对象类的基类,nRoot中有一个名字变量,有一个parent指针指向它的父结点,
还有一个childlist链表保存它所拥有的子结点.因为结点也是nRoot类,所有结点的子结点中也可能有子结点,
这样就形成了一个树形目录结构.nRoot中有一个nRoot* Find( const char* name )方法,功能很简单,就是遍历自已的
childlist链表对比每一个nRoot结点对象的名字,如果相同就返回些对象的指针.
b. 然后nebula还有一个叫nKernelServer的核心管理类,
1) 一开始nKernelServer就产生了一个全局可见的单件对象(单件就是只有一件,也就是全局唯一,你可以把它想象成一个全局的静态对象)
2) 这个nKernelServer对象有一个根据类名字符串来创建对象的方法: CheckCreatePath( const char* szClassName, const char *szPath );
第一个参数是类名字符串,第2个参数就是目录对象名.例如你有一个图形管理类:nGfxServer,你可以创建它的一个对象并起名为"/sys/servers/gfx",
如: ks-CheckCreatePath( "nGfxServer", "/sys/servers/gfx" );[注:ks即nKernelServer的单件对象]因为传的是类名字符串,所以从脚本中写下:
new nGfxServer /sys/servers/gfx
这样的语句时,tcl会把"nGfxServer"字符串返回给nebula的tclcmd_New函数(这个函数是nebula在初始tcl时注册告知tcl的),
然后tclcmd_New通过调用单件ks的CheckCreatePath( "nGfxServer", "/sys/servers/gfx" );方法来创建了这么一个对象.
3) 另外nKernelServer中有一个nRoot* Lookup( const char* szPath )的函数, 功能是你传入目录对象名,它会查到并返回对应对象指针.
4) 当然nKernelServer中还有一个nRoot *cwd变量, 它是用来指向当前目录(或是对象)的,例如,你在脚本中写下:
sel /sys/servers/gfx
这样的语句时,tcl分析到sel时就调用nebula告诉它的tclcmd_Sel函数(如何注册函数告知tcl这是我们在后面第2点的内容), 而tclcmd_Sel函数
会调用上述所说ks-Lookup函数来找到"/sys/servers/gfx"对象的地址,并把它赋给nKernelServer的cwd变量,以后,写下:
.setdisplaymode
"-type(win)-w(800)-h(600)"
.setclearcolor
0.0 0.0 0.0 0
.setperspective
45 1.33 1.0 10000.0
[注意:前面带"."号]
这时,tcl脚本没有这样的用法,于是它就把这些字符串传给一个叫tclcmd_Unknown的函数,nebula在其中分析字符串,把"."与后面的字符串一一分开,
并把"."后的第一个字符串(如:"setdisplaymode")作为nKernelServer的cwd所指向的对象的方法来调用,并传给它接着后面的参数
(如:"-type(win)-w(800)-h(600)").当然要让cwd对象把"setdisplaymode"字符串当成它的函数来调用是比较麻烦的.nebula是如何实现的呢?
我们将在后面第3点祥细说明:), 说了这么多,现在还是让我们来看看一个实现上述目录对象管理系统的代码:
[注: 这是我仿nebula写的简单程序,不考虑效率等问题,只作说明用]
// tnd.cpp : Defines the entry point for the console application.
//
#include
#include
#include
#pragma warning(disable: 4786)
using namespace std;
#ifndef MAX_PATH
# define MAX_PATH 1024
#endif
//----------------------------------------------------------------
class nRoot
{
string
m_strName;
nRoot
*m_pParent;
vector m_vecChild;
public:
nRoot(const char *szName) : m_strName(szName), m_pParent(0){}
nRoot* Find( const char *szName ){
for( unsigned int i=0; i
nRoot *pRoot = m_vecChild[i];
if( 0 == pRoot-m_strName.compare( szName ) ){
return m_vecChild[i];
}
}
return 0;
}
void SetName( const char *szName ){
m_strName = szName;
}
void AddChild( nRoot *pChild ){
m_vecChild.push_back( pChild );
}
};
//----------------------------------------------------------------
class nGfxServer : public nRoot
{
public:
nGfxServer(const char *szName) : nRoot(szName){}
void Test(){
__asm int 3 // 这里是测试看是否到这里中断(vc下支持的写法,其它编译器请用类似std::cout方法代替)
}
};
//----------------------------------------------------------------
class nKernelServer
{
nRoot *m_pRoot;
nRoot *m_pCWD;
nRoot* NewObject( const char *szClassName, const char *szName );
public:
nKernelServer(){
this-m_pRoot = new nRoot("/");
this-m_pCWD = this-m_pRoot;
}
nRoot* CheckCreatePath( const char* szClassName, const char *szPath );
nRoot* Lookup( const char* szPath );
void SetCwd( nRoot *o ){
this-m_pCWD = o ? o : this-m_pRoot;
}
};
static nKernelServer g_ks;
//----------------------------------------------------------------
//----------------------------------------------------------------
nRoot* nKernelServer::CheckCreatePath( const char* szClassName, const char *szPath )
{
nRoot* parent = 0;
nRoot* child
= 0;
if( '/' == szPath[0] ){ // AbsolutePath
parent = this-m_pRoot;
}else{
parent = this-m_pCWD;
}
char strBuf[MAX_PATH];
strcpy(strBuf, szPath);
char *pNextPathComponent = NULL;
char *pCurrPathComponent = strtok(strBuf, "/");
if( pCurrPathComponent )
{
// for each directory path component
while( (pNextPathComponent = strtok(NULL, "/") ))
{
child = parent-Find( pCurrPathComponent );
if (!child)
{
child = new nRoot( pCurrPathComponent );
parent-AddChild( child );
}
parent = child;
pCurrPathComponent = pNextPathComponent;
}
}
// curPathComponent is now name of last path component
child = parent-Find( pCurrPathComponent );
if (!child)
{
child = (nRoot*) this-NewObject( szClassName, pCurrPathComponent );