The Abandon Wars

Wednesday, April 25, 2007

Again, not a progress report but still a moment in my gaming history. I have finally played Final Armada!

and... it was crap. Seriously, when I left II it was still reasonably fun and had a lot of potential but it seems to have degenerated since...


Anyway I posted this once and it got corrupted and I couldn't be bothered to post the rest again. Suffice to say that I don't understand how a team like we had could produce the travesty above. All in all it's a very disheartening experience :(

Monday, April 23, 2007

This is isn't a progress post as such, it's actually a quick howto for anyone else who's trying to optimise a D3DVertexBuffer and D3DIndexBuffer and is not using D3DX.

The short answer is one has to use D3DX - but it's not as bad as it sounds. The gist of it is too call OptimizeInplace on a ID3DXMesh. The code below shows how to copy an existing vertex buffer and index buffer into the ID3DXMesh, optimise it and then copy them back again.

ID3DXMesh* pXMesh;
void* pvDestIndexBuffer;
void* pvDestVertexBuffer;
void* pvSrcIndexBuffer = The pointer .Lock() gave you;
void* pvSrcVertexBuffer = The pointer .Lock() gave you;
DWORD* pvAdjacency;

D3DXCreateMeshFVF(iNumTriangles, iNumIndicies, D3DXMESH_SYSTEMMEM, Your FVF here, lpD3DDevice, &pXMesh);

iVertSize = pXMesh->GetNumBytesPerVertex();

pXMesh->LockIndexBuffer(D3DLOCK_NO_DIRTY_UPDATE, &pvDestBaseIndexBuffer);
pXMesh->LockVertexBuffer(D3DLOCK_NO_DIRTY_UPDATE, &pvDestVertexBuffer);
memcpy(pvDestVertexBuffer, pvSrcVertexBuffer, iVertexSize * iNumVerticies);
memcpy(pvDestIndexBuffer, pvSrcIndexBuffer, sizeof(WORD) * iNumVerticies);

Start interesting bit here
pvAdjacency = (DWORD*)malloc(iNumTriangles * 3 * sizeof(DWORD));
pXMesh->GenerateAdjacency(0.0f, pvAdjacency);
pXMesh->OptimizeInplace(D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_DONOTSPLIT, pvAdjacency, NULL, NULL, NULL);
free(pvAdjacency);
End interesting bit

memcpy(pvSrcIndexBuffer, pvDestIndexBuffer, sizeof(WORD) * iNumVertices);
memcpy(pvSrcVertexBuffer, pvDestVertexBuffer, iVertexSize * iNumVerticies);
.Unlock() your buffers

The bad news is that somewhere in typing that I got confused between the number of vertices and the number of indicies but it's not too difficult to work out which is which. They're all called iNumVerticies here.

Obviously for 32 bit indicies one needs to use sizeof(DWORD) instead of sizeof(WORD).

Later!

Sunday, April 22, 2007

Happiness is many things too many people. Too me, it's rendering a 350 000 polygon scene at 400 frames per second.

That's right, I've been on a serious optimisation spree over the past two weeks; and the rendering increase is the smallest of the gains - although it is the most impressive looking.

I've included the screen shot below because I feel there isn't enough nudity on the interweb. Of course if you look closely you'll see NVPerfHud with all the scene statistics.


Internally I've managed to speed up the conversion process by several tens of thousands of times - mostly going from O(n) to O(1) - by caching just about everything. No more hour long waits for me!

I'm currently working on caching the collision- and mesh selection data. It's a big process so I doubt it will be finished this weekend. Once that's done the engine is basically finished. It's been a long and painful journey but the end is in sight. :) I can hardly believe it!

Now back to the grind stone...

Later!

Saturday, April 07, 2007

Like the man said: Just a quickie.

All I have to say is that I finally document how the parser definitions work. I was changing things so often I confused myself and decided that unless I pinned the defintion in text I'd never stop.

And here it is: the parser document in all its minimalist glory.

A defintion is declared with the $ character prefixing the definition name. The one exception is the 'main' definition which is always the first text in the file and is not named.

A node can be explicitly created by using parenthesis. Elements in a node can be separated using one of three separators. The | character chooses only one of the elements in the node eg: (A | B) will parse either A or B, not both.

The & character parses if all the elements parse. It is only valid with the ! modifier and is used to disallow elements eg: (A & !B & !C).

A 'space' - ie: no separator - means the elements must parse in order eg: (A B C). If any elements fail then the node fails.

The ! character modifies element by logically negating the parse result. A node with a ! modified element will fail if the element parses normally (before negation).

A node may be followed by the + or - characters. The + indicates that the node will not be simplified eg: ((A)+) will remain ((A)) and not collapse to (A). The - means the contents of the element should be discarded and not added into the token tree. The node is still created as necessary.

Following the + or - is an optional node name. eg: (A)+MyName or (B)-OtherName. It is possible for the - node to be collapsed at which point the name is lost.

A nodes repetition can be set by the pattern ?-?. eg: (A) 0-1 makes the node A optional. (B) 1-1 makes B compulsory. If ommited the node is 1-1. The right hand digit may be replaced by and X which implies no limit. eg: (C) 0-X means C can be repeated as often as needed or not be present at all.

The . character is the last node option and stops the token tree from rolling back past it. eg: (C | (A B).) This is useful for error reporting as otherwise the entire parse tree will rollback and confusingly report the root node as being in error.

An element can be a string literal eg: "Hello" or character literal eg: 'c'. An element can also be one of the primitive keywords: integer, real, string or char. A string must be surrounded by double quotes. eg: "Yo!" but places no constraint on what the string contains.

A character must be surrounded by single quotes eg: '$'. Escape codes can be used as normal in both strings and chars.

An element can be the keyword 'identifier' which parses c style identifiers: eg: iIndex or _Node38 but
not 74Id or $%.

Lastly an element can be a definition. eg: $DefinitionA can be used as ("Hello" DefinitionA). This replaces 'DefinitionA' with whatever the node $DefinitionA actually contained.


That's it. You may notice that the parser is described as the dumb beast alluded to last week. This has made life much, much simpler - which is better for all of us as I may actually start working on Space Crusade specifics again ;)

Later!

Sunday, April 01, 2007

Another day, another post; my posting is, of course, the metric by which time should be measured. Inconstant periodics aside, I'm still working on my C++ parser.

I realised that the parser was being far too intelligent so I've been taking stuff away and cutting back on it's cleverness. The goal behind this pruning is too stop the parser failing quite so easily. I am now more likely to get a token tree by the end of the parse (even if it is badly formed) than just emptiness.

However as dealing with syntax errors is basically boring (and doesn't make for good screenshots) I've posted a screen shot of the Pillar of Autumn instead.


It has nothing to do with Space Crusade or C++ parsing but whilst I'm waiting for Halo 3 I have to get my fixes in small doses however i can ;)

Back on the programming side I've written one of the worlds more efficient find and replace routines. I don't know why I felt the urge to do it but it was fun - which is good enough for me :)

And that's enough rambling for one evening. So...

Later!