??xml version="1.0" encoding="utf-8" standalone="yes"?> 不管使用那种方式Q都需要将L点进行插|interpolationQ以便得到^滑\径。这里就牉|C|插值和朝向插倹{? 从插值函C可分Zc:1Q线性;2Q多式Q?Q样条。顾名思义Q线性插值采用线性函敎ͼ多项式插值采用多式Q而样条插值则采用了一l多式l成的分D函数。由于摄像机的关键\径点通常都会大于2个,所以插值方法上必选取hҎ? 一、位|插?/strong> 1.1 hcd选择 在这里我们仅考虑三次h插|因ؓ它们可达到C2q箋。三ơ样条中主要以Bezier、Catmull-Rom、均匀Bh查对象Q它们都h计算开销的优点。可以通过下面公式来定义它们: 化ؓQ? 下面表格列出了三U插值曲U对应的G和MQ? 如下图所C,?个点q行Bezier插值得到的曲线只会有两个点被曲U穿q,而B-Spline插值得到的曲线不会l过控制点,只有Catmull-Rom Spline可以得到I过除v点和l点之间的所有控制点。正因ؓCatmull-Romhq个Ҏ,使得它被q泛地应用在关键帧^滑插gQ因此我们选择了Catmull-Romh作ؓ摄像Z|点的插值算法? 1.2 实现 参考: [1] Mathematics for 3D Game Programming and Computer Graphics 3e by Eric Lengyel [2] http://en.wikipedia.org/wiki/Catmull-Rom_spline#Catmull.E2.80.93Rom_spline [3] http://www.codeproject.com/Articles/30838/Overhauser-Catmull-Rom-Splines-for-Camera-Animatio 二、朝向(旋{Q插?/strong> 2.1 Euler Angles VS Quaternion 三维I间中描q旋转的主要Ҏ有Euler Angles和Quaternion。Euler Angles有三个明昄问题Q?Q三轴上的旋转顺序敏感;2QGimbal Lock现象D旋{自由度丢失;3Q独立地对三个旋转分量进行插|忽略了三轴之间的依赖关系Q导致插值结果不理想。与Euler Angles不同的是QQuaternion没有旋转分解到三个轴向上,而是用一个旋转u和绕该u的旋转角度来描述Q所以从Ҏ上消除了Euler Angles的三大问题。有关Quaternion的详l描q可参考[1]Q在此不再篏q? 2.2 LERP VS SLERP 四元数线性插|Linear Quaternion interpolationQ的计算公式为: 四元数球面线性插|Spherical Linear Quaternion interpolationQ的计算公式为: 其中Qؓ两个四元数的夹角? Z方便展示Q我们考虑?D情况对角度Vq行两次插|两种法在插值效果上存在的差异,如下图(b为LERP、c为SLERPQ: 从图中可以看出LERP其实是对两四元数在圆上的弦进行了{分Q而SLERP则是对圆弧进行等分。由此得出的l论是,SLERP得到了比LERP更^滑的插值结果? Z保证插值曲U的C2q箋性,需要用球面四边Ş插|Spherical Quadrangle interpolationQ方法。例如,对q1和q2插|首先要用q0、q1、q2、q3计算Z个控制点QInner Quadrangle PointQ,公式如下Q? 然后通过下式得到最l插值结果: 2.3 实现 上面代码没有考虑两个四元C间夹角大?80的情c例如,考虑q1->q2的插D度?gt;180Q我们可以让q1->q2反向旋{2π-θQ即旋{-(2π-θ)Q根据四元数的定义[v*sin(θ/2) , cos(θ/2)]Q那么对q2q行处理变ؓ[-v*sin(θ/2) , -cos(θ/2)]。这个处理可以放在AddSplinePoint中来做: 参考: [1] Quaternions, Interpolation and Animation by EB Dam - 1998 内存泄漏的基本步骤是:1Q包装(重蝲Q内存分?释放APIQ?Q进行内存分配时C相关信息Q地址、大、调用栈Q?Q释放时清除之前记录的对应信息;4Q程序退出时Q确保在所有内存释放操作完成之后)Q输出剩下的记录。其中,对进行分配操作是的调用栈回溯是个重点信息Q它能够帮助我们扑և内存泄漏代码? Windows中的Dbghelp库提供了丰富的调试API。StackWalk应该是进行栈回溯最直接的一U接口了Q但是它不够快。如果能先记录下调用栈上的CALL指o地址Q然后在输出日志时解析出W号Q将会大大降低检机制对E序本n性能的媄响。Dbghelp库中提供了Sym*FromAddrpdAPIQ可以通过指o地址获取函数W号Q那么剩下的是如何记录指o地址的问题了。从|上借了一张x86调用栈示意图Q如下: 从图中可以看出,Callee的EBP始终指向Caller的EBPQEBP下面是指向Caller下一条指令(注意x86体系下栈的增长方向是地址Q,因此通过EBP可以回溯整个调用栈了。通过下面代码可以实现此功能: 宏参数frame是个void*指针数组Q数l的大小取决于想要回溯的栈深度。内存分配和回收的包装代码如下: 我们看到Q内存管理系l内部终I还是要使用语言提供的内存分?释放APIQ只要配对实C分配与释攄理,pȝ内部的无泄漏是很Ҏ保证的。在q里着重讲解原理,׃重蝲new/delete operator了。最后看一下调用栈函数W号的回溯代码: 我们用下面代码做试用例Q? 泄漏结果: 参考: [1] Using DbgHelp 我想了两U方案,最初的Ҏ灉|来自光线反射Q如下图所C? 因ؓL格子是u寚w的,通过法向量n可以得到b点的反射向量e'-oQ将|e'-o|限定在一个固定|此时如果e'不在L里面Q就它作ؓ新的l点。该Ҏ的实验结果不太o人满意,当角色离L较近时断l感太明显,因ؓ|e' - b|的长度较短。另外它不能处理e'在阻挡里的情况,被卡住不动的概率依然较大? W二U方案是ҎUd向在一个u向找一个可达试探点Q然后用限制了搜索空_搜烦节点?0以内Q的A*法扑ֈ一条到试探点的路径Q如下图? 设diff_x = l点x - LxQdiff_y = l点y - LyQ当diff_x > diff_yQ认定ؓ向x方向上移动,在这U情况下在终点y轴向上?0个格子内LC个最接近l点的无L点(U它为可达试探点Q。由于试探点与玩家当前点极有可能是直U不q通的Q而且它们不可能太q,所以用了一个将搜烦节点个数限制?0以内A*来得C条\径。该Ҏ大部分情况都能在边缘扑ֈ合理点,但如果玩家垂直面朝阻挡内Ud且不能在限制搜烦范围内找到可辄Q角色就会卡住不动,q种情况只能让玩家自己调整一下朝向了? 需要注意的是,如果是多个参敎ͼLua的压栈顺序是object pointer、参C左到叻I所以栈元素是函数{最双的参数?/p> 核心数据l构我采用链表数l,相同颜色且L的泡串成一个链表。采用这U数据结构,是基于下面考虑Q?Q通过颜色划分~小搜烦区域Q?Q没有排序,没有中间插入节点的需求;3Q节省内存。当Ӟ游戏界面中的泡q是把它对应C个二l数l中Q每个元素存储一个泡对象的指针Q该对象中至包含:颜色、坐标,分倹{如下图所C: 剩下需要考虑的是发射泡颜色如何产生Q如果从I关卡开始,前面可以用随机,后面p使用l计信息了。在每个链表头可以统计该堆泡开闭性、数量等Q利用这些信息可以决定发泡的颜色? 1. 建立源代码烦?/font> WinDbg提供了一套用于管理pdb对应的源代码的工P位于其安装目录的srcsrv下,对VSS、SVN、CVS、Perforce提供了支持,分别对应vssindex.cmd、svnindex.cmd、cvsindex.cmd、p4index.cmdq四个perl脚本。其实,ssindex.cmd才是具体实现Q它Ҏ传入的版本控制系l标识,调用对应的perl module? svnindex.cmd通过/source?symbols参数来指定源代码目录和PDB目录Q?debug可输出处理的详细信息Q?user?pass提供svn账户和密码。PDB文g中有一节专门用于存放源代码文g列表及处理命令,可通过pdbstr -r -p:PdbFileName -s:srcsrv查看? svnindex.cmd /debug /source="E:\CodeBase_SVN\Client\trunk\tools\CutSceneEditor" /symbols="E:\CodeBase_SVN\Client\trunk\bin\Release\CutSceneEditor" /user="user" /pass="pwd" 在执行上面命令前Q确保Perl和Subversion已经被安装且讄了PATH环境变量。该命o提取source code的服务器路径和当前RevisionQ然后写入PDB。下面是通过pdbstr获取的信息: 上面输出是经q格式化的,原始信息可以通过srctool -n查看Q? 可以看出Q原始代码\径后面跟了一条svn cat指oQ由于没有指定sourcepathQ所?targ%~省为当前\径("C:\Program Files\Debugging Tools for Windows (x64)\srcsrv"Q?nbsp; 2. 创徏W号服务?/font> 所谓符h务器Q最单的形式是文g׃n服务器。我们?a target="_blank">symstore命oQ将1中生的pdbdC个文件共享服务器上,如: symstore.exe add /f "E:\CodeBase_SVN\Client\trunk\bin\Release\CutSceneEditor\*.pdb" /s "\\server.com\pub\Symbols" /t "CutSceneEditor" /v "Build 4171" /c "fix memory leak" 该命令会ҎPDB的signature和age产生一个GUIDQƈPDB攄于以改GUID为名字的目录下: 当debugӞUNC路径d到_NT_SYMBOL_PATH中(如:_NT_SYMBOL_PATH=CACHE*F:\Symbols;SRV*http://msdl.microsoft.com/download/symbols;SRV*\\server.com\pub\SymbolsQ,调试器会自动到指定的W号服务器上L索对应的pdb文g? symstore大大化了W号的版本管理问题,关于它的详细介绍可参?a target="_blank">symstore介绍? 值得注意的是Qsymstore没有锁机Ӟq不支持多h同时操作。实际情况中Q也只有自动构徏时才会做此操作? 3. 使用WinDbg分析Dump文g 我们在代码中加入发生异常写MiniDump的功能,在程序崩溃时产生dump文g。在使用WinDbg分析dump文gӞ需要设|Symbol File Path和Source File PathQ也可以直接讄环境变量_NT_SYMBOL_PATH和_NT_SOURCE_PATH。在讄_NT_SOURCE_PATH时?SRV*CachePath"表C启用代码提取功能,执行svn cat写入到CachePath指定的目录,否则用原始\径。此后,通过点击Call Stack中的函数调用便会触发从svnd对应代码的操作(通常会有一个安全警告窗口弹出)? 对于使用WinDbgq行调试在此׃多讲了,推荐一本不错的书《Advanced Windows Debugging》? 参考资?/strong> [1] Source Indexing and Symbol Servers: A Guide to Easier Debugging [2] Source Server Helps You Kill Bugs Dead In Visual Studio 2005 一、A* Path-Finding A*法׃多讲了,可参考: 二、预处理 每个盔R区块QC1和C2Q都有一条由公共边,该边两侧格l成L1和L2Q则q通点集E满下列条gQ? 对上面得到的位于同一区块的点集合使用local A*做连通性测试,下图用直U连接来表示两点互通: 三、寻?/font> 使用区块q通信息,q行区块UA*Q得到区块之间的q接点,如果在预处理时保存了区块内互通点的\径,׃必再q行区块内的local A*了? 实验l果表明Q在未采用区块内预存路径的情况下Q中长距d路用层ơA*后的q_效率是普通A*?倍以上,距离长效率Ҏ明显? A* 93ms HPA* 15ms 从上图中可以看出QHPA*得到的\径ƈ不是最优的Q它是在最优和效率上的折中Q适合作ؓ长距d路的一U优化方案? 四、优化点 一个改q的Ҏ是对q长的边界再做划分: 参考: [1] http://msdn.microsoft.com/en-us/library/bb205419(v=vs.85).aspx [2] http://msdn.microsoft.com/en-us/library/bb205420(v=vs.85).aspx 前段旉遇到q这L问题Q已知一2D地图格子的长宽(w、hQ及每个格子的边长(aQ格子ؓ正方形)Q给定物体的2D坐标Qpos[x , y]Q及半径QrQ,求解物体?D地图格子中所占的格子Q仅考虑n*n的情c大概的求解q程如下Q? 1Q根据半径,定n*n中的n。假定计公式ؓQn = Round(2*r / a) 2Q根?D坐标得到物体的“中心格子”。根据n的奇Ӟ计算公式不同Q如下图所C? n为偶数[1] n为奇数[2] [1]Qgrid(x , y) = Round(pos / a) [2]Qgrid(x , y) = Floor(pos / a) 其中Q格子坐标x >= 0 , y >= 0? 3Q以“中心格子”ؓ基础Q求出物体占据的其他格子。这L描述Q让人容易想到递归Q就像用深度优先Ҏ遍历树那P伪代码算法如下: 虽然Q该法得到了正的求解l果Q但是由于每个格子都会标记周围的8个格子,所以存在大量的重复Q再者如果上面的q程每都进行的话,函数调用开销也是相当可观?/p>
循环自然是不可避免的Q消除重复便成了优化的目标。分析格子图和n??的情况,试图扑և用@环代曉K归的方法,我发C下面一个有的规律Q?/p>
从“中心格子”出发,时针(或逆时针)以上图方式可以走遍所求解的每个格子而不重复。在实现上,每个转角也是有规律的Q可以通过一?*2的{角矩阉|控制Q?/p>
[1 , 0][0 , -1] [0 , 1][-1 , 0] 时针方式的转角?/p>
矩阵中的每个元素代表从当前格子走C个格子在row和col上的变化。加之,在{角之间的路长Q以格子个数计)有每转两ơ递增单位1的规律,法׃隑־CQ下面同样以伪代码示Q?/p>
用MFCE序验证了一下算法的正确性,标号展示了@环的路线Q注意GDI的坐标系中Y的正方向朝下Q: 一、系l调?/p> 通过使用WinExec、ShellExecute或systemQ如Q?/p> 此方法的虽然单,但存在效率问题,因ؓ有创E的巨大开销。此外不同版本的Perforce也会出现不同的执行结果,针对特定的中文会出现操作p|的诡异问题?/p>
二、Perforce SDK Perforce提供有SDK用以扩展或集成到其他应用中,虽然没有详细的文档,但可以通过学习SDK中的sample文g来学习,此方法最E_?/p>
下面代码展示了通过SDK中ClientAPI来递归d指定文g夹下的所有文Ӟ 此方法省M创徏p4q程的开销QQ务执行效率会提高不少Q而且也不会出现执行结果不E_的问题?nbsp; 附一QSDK下蝲地址 ftp://ftp.perforce.com/perforce/ 附二Q附上ANSI转UTF-8代码
]]>
]]>
]]>
A*法的优化可从搜索节点储存和OpenList排序两方面入手?
对在E中且同边的连l格子取其中点,如下图所C:
]]>void D3DXQuaternionSquadSetup(
__out D3DXQUATERNION *pAOut,
__out D3DXQUATERNION *pBOut,
__out D3DXQUATERNION *pCOut,
__in const D3DXQUATERNION *pQ0,
__in const D3DXQUATERNION *pQ1,
__in const D3DXQUATERNION *pQ2,
__in const D3DXQUATERNION *pQ3
);
D3DXQUATERNION* D3DXQuaternionSquad(
__inout D3DXQUATERNION *pOut,
__in const D3DXQUATERNION *pQ1,
__in const D3DXQUATERNION *pA,
__in const D3DXQUATERNION *pB,
__in const D3DXQUATERNION *pC,
__in FLOAT t
);
其中QD3DXQuaternionSquadSetup用于q回内插的控制点。它们具体的实现公式和用法,有兴的同学可以参考MSDN。在此需要说明的是,
D3DXQuaternionSquad使用了Slerp作ؓ内部实现Q会D在两个夹角ؓ180左右的Quaternion之间插g出现断裂的问题。下面代码通过
实现一个考虑了上q情늚Slerp版本Q在q1和q2夹角?或?80Ӟ使用U性内插而非球面Q来解决该问题?/pre>
]]>
]]>if n is even
{
get the index of 'center grid' (row , col)
ExtendHeldGrid(row , col , n)
ExtendHeldGrid(row - 1 , col , n)
ExtendHeldGrid(row - 1 , col - 1 , n)
ExtendHeldGrid(row , col - 1 , n)
}
else
{
get the index of 'center grid' (row , col)
ExtendHeldGrid(row , col , n)
}
function ExtendHeldGrid(row , col , level)
{
if(level <= 0)
return
if((row >= 0 and row < MaxGridWidth) and (col >= 0 and col < MaxGridHeight))
{
mark the grid(row , col)
ExtendHeldGrid(row , col , level - 2)
if(level - 2 > 0)
{
ExtendHeldGrid(row + 1 , col , level - 2)
ExtendHeldGrid(row - 1 , col , level - 2)
ExtendHeldGrid(row , col + 1 , level - 2)
ExtendHeldGrid(row , col - 1 , level - 2)
ExtendHeldGrid(row + 1 , col + 1 , level - 2)
ExtendHeldGrid(row - 1 , col + 1 , level - 2)
ExtendHeldGrid(row + 1 , col - 1 , level - 2)
ExtendHeldGrid(row - 1 , col - 1 , level - 2)
}
}
}
conerMat =
{
{0 , -1} ,
{-1 , 0} ,
{0 , 1} ,
{1 , 0}
}
dir = 0 /// 转角控制Q四个{角顺旉0~3
span = 1 /// 转角间的跨度
count = 1 /// 每两ơ增加一个跨?
rin = 1 /// 下一个{角的循环索引
if n is even
get the index of 'center grid' (row , col)
else
get the index of 'center grid' (row , col)
for(i = 0; i < n * n; ++i)
{
if((row >= 0 and row < MaxGridWidth) and (col >= 0 and col < MaxGridHeight))
mark the grid(row , col)
if(i == rin)
{
dir = (dir + 1) % 4
if(count == 2)
{
++span
count = 1
}
else
++count
rin = i + span
}
row = row + conerMat[dir][0]
col = col + conerMat[dir][1]
}
]]>_snprintf(cmdbuf , 1024 , "p4 -c %s add \"%s\"" , argv[1] , ANSIToUTF8(path.c_str()));
system(cmdbuf);
# include "clientapi.h"
# include "i18napi.h"
# include "CharSetConvertUtil.h"
# include <string>
# include <vector>
# include <list>
# include <io.h>
using namespace std;
// structure to hold a directory and all its filenames.
struct FILELIST
{
string path;
vector<string> theList;
};
void TransverseDirectory(string path, list<FILELIST>& theList)
{
struct _finddatai64_t data;
string fname = path + "\\*.*";
long h = _findfirsti64(fname.c_str(),&data);
if(h >= 0)
{
FILELIST thisList;
theList.push_back(thisList);
list<FILELIST>::iterator it = theList.end();
it--;
(*it).path = path;
do {
if( (data.attrib & _A_SUBDIR) )
{
// make sure we skip "." and "..". Have to use strcmp here because
// some file names can start with a dot, so just testing for the
// first dot is not suffient.
if( strcmp(data.name,".") != 0 &&strcmp(data.name,"..") != 0)
{
// We found a sub-directory, so get the files in it too
fname = path + "\\" + data.name;
// recurrsion here!
TransverseDirectory(fname,theList);
}
}
else
{
// this is just a normal filename. So just add it to our vector
(*it).theList.push_back(data.name);
}
}while( _findnexti64(h,&data) == 0);
_findclose(h);
}
}
int main( int argc, char **argv )
{
list<FILELIST> MyList;
string path;
ClientUser ui;
ClientApi client;
StrBuf msg;
Error e;
if(argc < 4)
{
fprintf( stderr , "P4 Transverse Add: Arguments Error!\n");
return -1;
}
client.SetPort(argv[1]);
client.SetClient(argv[2]);
client.SetTrans(CharSetApi::UTF_8 , CharSetApi::UTF_8 ,
CharSetApi::UTF_8,
CharSetApi::UTF_8);
TransverseDirectory(argv[3],MyList);
// Connect to server
client.Init( &e );
if( e.Test() )
{
e.Fmt( &msg );
fprintf( stderr, "%s\n", msg.Text() );
return -1;
}
list<FILELIST>::iterator it;
for(it = MyList.begin(); it != MyList.end(); it++)
{
vector<string>::iterator its;
for(its = (*it).theList.begin(); its != (*it).theList.end(); its++)
{
path = (*it).path + "\\" + (*its);
char* pText = ANSIToUTF8(path.c_str());
client.SetArgv( 1 , &pText);
client.Run( "add" , &ui );
}
}
// Close connection
client.Final( &e );
if( e.Test() )
{
e.Fmt( &msg );
fprintf( stderr, "%s\n", msg.Text() );
return -1;
}
return 0;
}
wchar_t* ANSIToUnicode( const char* str )
{
int textlen ;
wchar_t * result;
textlen = MultiByteToWideChar( CP_ACP, 0, str,-1, NULL,0 );
result = (wchar_t *)malloc((textlen+1)*sizeof(wchar_t));
memset(result,0,(textlen+1)*sizeof(wchar_t));
MultiByteToWideChar(CP_ACP, 0,str,-1,(LPWSTR)result,textlen );
return result;
}
char* UnicodeToANSI( const wchar_t *str )
{
char * result;
int textlen;
// wide char to multi char
textlen = WideCharToMultiByte( CP_ACP, 0, str, -1, NULL, 0, NULL, NULL );
result =(char *)malloc((textlen+1)*sizeof(char));
memset( result, 0, sizeof(char) * ( textlen + 1 ) );
WideCharToMultiByte( CP_ACP, 0, str, -1, result, textlen, NULL, NULL );
return result;
}
wchar_t* UTF8ToUnicode( const char* str )
{
int textlen ;
wchar_t * result;
textlen = MultiByteToWideChar( CP_UTF8, 0, str,-1, NULL,0 );
result = (wchar_t *)malloc((textlen+1)*sizeof(wchar_t));
memset(result,0,(textlen+1)*sizeof(wchar_t));
MultiByteToWideChar(CP_UTF8, 0,str,-1,(LPWSTR)result,textlen );
return result;
}
char* UnicodeToUTF8( const wchar_t *str )
{
char * result;
int textlen;
// wide char to multi char
textlen = WideCharToMultiByte( CP_UTF8, 0, str, -1, NULL, 0, NULL, NULL );
result =(char *)malloc((textlen+1)*sizeof(char));
memset(result, 0, sizeof(char) * ( textlen + 1 ) );
WideCharToMultiByte( CP_UTF8, 0, str, -1, result, textlen, NULL, NULL );
return result;
}
char* ANSIToUTF8(const char* str)
{
wchar_t* pUnicodeBuff = ANSIToUnicode(str);
char* pUtf8Buff = UnicodeToUTF8(pUnicodeBuff);
free(pUnicodeBuff);
return pUtf8Buff;
}
]]>
1Q灵zR高可配|性,如:多重l合条g触发、多d队列{略Q?
2Q兼容目前几乎所有的行SCM工具Q得多U异构系l能够由CruiseControl.NETl一监控和管理;
3Q完善的使用文档Q上手和维护变得容易;
而该目引以自豪的代码check in触发构徏的功能通过CC.NET也很Ҏ实现。之前用过HudsonQ该pȝ采用M式架构,使用者通过|页让Master控制Slave动作Q由于Master由别的部门管理,出问题后需要联pȝ关h解决Q会有一定的处理延迟Q而且安装在构建机上的Slave服务l常崩溃。而CC.NET直接安装在构建机上,配置IIS后可通过|页直接讉KQ操作维护都较前者容易。由于之前的构徏脚本是用VisualBuild实现的,所以不可能再花旉用CC.NET再写一遍。而通过实验得知Q其实CC.NET能够以ExecutableTask的Ş式很好地与VisualBuild集成Q目前每时定时构徏、NightBuild和构建成功后触发的自动测试都已经攑ֈ了CC.NET上了?
ccnet.config Example:
<cruisecontrol xmlns:cb="urn:ccnet.config.builder"> <queue name="Q1" duplicates="ApplyForceBuildsReAdd" /> <queue name="Q2" duplicates="ApplyForceBuildsReAdd" /> <project name="AgileBuild" queue="Q1" queuePriority="1"> <category>AutoBuild</category> <triggers> <filterTrigger startTime="18:00" endTime="10:00"> <trigger type="intervalTrigger" name="Continuous" seconds="1800" buildCondition="ForceBuild" /> <weekDays> <weekDay>Monday</weekDay> <weekDay>Tuesday</weekDay> <weekDay>Wednesday</weekDay> <weekDay>Thursday</weekDay> <weekDay>Friday</weekDay> </weekDays> </filterTrigger> </triggers> <tasks> <exec> <executable>VisBuildCmd.exe</executable> <buildArgs>E:\GAMEDev\AgileBuilder.bld</buildArgs> <baseDirectory>D:\VisBuildPro7</baseDirectory> <buildTimeoutSeconds>3000</buildTimeoutSeconds> </exec> </tasks> </project> <project name="NightBuild" queue="Q2" queuePriority="1"> <category>AutoBuild</category> <triggers> <scheduleTrigger time="23:00" buildCondition="ForceBuild" name="Scheduled"> <weekDays> <weekDay>Monday</weekDay> <weekDay>Tuesday</weekDay> <weekDay>Wednesday</weekDay> <weekDay>Thursday</weekDay> <weekDay>Friday</weekDay> </weekDays> </scheduleTrigger> </triggers> <tasks> <exec> <executable>VisBuildCmd.exe</executable> <buildArgs>"BUILD_OPTION=clientworldeffect" "PACK_TYPE=RAR" E:\GAMEDev\NightBuilder.bld</buildArgs> <baseDirectory>D:\VisBuildPro7</baseDirectory> <buildTimeoutSeconds>0</buildTimeoutSeconds> </exec> </tasks> </project> <project name="NightAutoTest" queue="Q2" queuePriority="2"> <category>AutoTest</category> <triggers> <projectTrigger serverUri="tcp://heath-builder:21234/CruiseManager.rem" project="NightBuild"> <triggerStatus>Success</triggerStatus> <innerTrigger type="intervalTrigger" seconds="60" buildCondition="ForceBuild" /> </projectTrigger> </triggers> <tasks> <exec> <executable>VisBuildCmd.exe</executable> <buildArgs>E:\GAMEDev\AutoTest.bld</buildArgs> <baseDirectory>D:\VisBuildPro7</baseDirectory> <buildTimeoutSeconds>0</buildTimeoutSeconds> </exec> </tasks> </project> </cruisecontrol>
References
[1] http://cruisecontrol.sourceforge.net/
[2] http://www.kinook.com/VisBuildPro/
Jason Gregory在他的引擎架构设计一书中也用“To SLERP or Not to SLERP (That’s Still the Question)”一节介绍了开发者对Slerp的争论,q介l他在Naughty Dog的同事针对PS3的优化方法,使Slerp可达?0周期/兌Q相比Lerp?6.25周期/兌已经很不错了?/p>
之前实没对Slerp的效率问题作q多的考虑Q引擎的Slerp直接使用的是D3DXQuaternionSlerpQMS应该对其做了优化Q效率怎么栯一下才知道了?/p>