Heya all.
Thought I'd start a new thread to post my current work in progress,
which involves translating parts of the m$ SkinnedMeshes example.
It should marry neatly with Scronty's work on the FrameHierarchy loader and our joint work with the LinkedList (LL) include.
My work involves translating the structures and support-code for Framed skinned animated mesh objects.

I am going to post the structures in logical order, not the order they should be defined in (the reverse order), beginning with the highest order structure that I have encountered in this translation at this moment, which is
the AnimatedMesh object.

;top layer, encapsulates everything to know about loading and drawing a mesh
AnimatedMesh STRUCT
m_pmcSelectedMesh LPMESHCONTAINER ?
m_pframeSelected LPMESHFRAME ?
m_pdeSelected LPDRAWELEMENT ?
m_ptcTextureCacheHead LPTEXTURECACHE ? ;Root of TextureCache (shared)
m_method DWORD ?
m_fRadius FLOAT ?
m_Center D3DXVECTOR3 <>
m_pfxEffect LPD3DXEFFECT ?
AnimatedMesh ENDS

LO is our LinkedObject Header. It represents support for LL and naming of objects. Expect to see a lot more of it :)
You can compare this to the manim.h file from the SkinnedMeshes example.
I've translated all the required structs and supprt code already, but I think they could still be cleaned up, and I'm open to suggestions.
I will spread out the postings however.
Next will be the MeshFrame structure, ex SFrame.

Posted on 2002-09-04 03:44:30 by Homer
The next instalment, as promised :)
This is the container structure for an animated skinned mesh object or list, MeshContainer, as used by the previous MeshFrame, which was the structure for a Frame containing an animated skinned mesh object or list.
There's also some functions supporting the MeshContainer object.
The support code is a quick rip and needs correction based on implementation.

Next time we'll do the DrawElement structure, and we'll follow up with the support structs for animated skinned mesh objects.
Finally, we'll put it all together with cleaned up support code.

;Helper macro (Should be added to LinkedList include)
invoke KillEntryPlusChildren,mStruct

NONE equ 0

;skinned mesh container, used by MeshFrame for animatedmesh :)
MeshContainer STRUCT
rgMaterials LPD3DMATERIAL8 ?
cpattr DWORD ?
cMaterials DWORD ?
iAttrSplit DWORD ?
; pmcNext LPMESHCONTAINER ? ;Redundant - use LO header field
; char *szName ; Redundant - use LO header field
rgdwAdjacency DWORD ?
rgdwAdjacencyTemp DWORD ?

; // Skin info
m_pBoneMatrix ptr LPD3DXMATRIX
m_pBoneNamesBuf LPD3DXBUFFER ?
m_pBoneOffsetBuf LPD3DXBUFFER ?
m_pBoneOffsetMat LPD3DXMATRIX ?
m_numBoneComb DWORD ?
m_maxFaceInfl DWORD ?
m_pBoneCombinationBuf LPD3DXBUFFER ?
m_Method DWORD ?
MeshContainer ENDS

InitMeshContainer PROC lpThis:DWORD
push esi
mov esi,lpThis
mov .MeshContainter.pMesh,NULL
mov .MeshContainter. pMeshHW,NULL
mov .MeshContainter.pMeshSW,NULL
mov .MeshContainter.rgMaterials,NULL
mov .MeshContainter.pTextures,NULL
mov .MeshContainter.cpattr,0
mov .MeshContainter.iAttrSplit,0
mov .MeshContainter.cMaterials,0
mov .MeshContainter.pmcNext,NULL
mov .MeshContainter.m_pSkinMesh,NULL
mov .MeshContainter.m_pAttrTable,NULL
mov .MeshContainter.m_pBoneMatrix,NULL
mov .MeshContainter.m_pBoneNamesBuf,NULL
mov .MeshContainter.m_pBoneOffsetBuf,NULL
mov .MeshContainter.m_pBoneOffsetMat,NULL
mov .MeshContainter.rgdwAdjacency,NULL
mov .MeshContainter.rgdwAdjacencyTemp,NULL
mov .MeshContainter.m_numBoneComb,0
mov .MeshContainter.m_maxFaceInfl,0
mov .MeshContainter.m_pBoneCombinationBuf,NULL
mov .MeshContainter.m_Method,NONE
mov eax,esi
pop esi
InitMeshContainer ENDP

NewMeshContainer PROC lpParent:DWORD
push esi
mov esi,lpParent
invoke AddChildEntry,esi,sizeof MeshContainer
push eax
.if eax!=NULL
invoke InitMeshContainer,eax
pop eax
pop esi
NewMeshContainer ENDP

DestroyMeshContainer PROC lpThis:DWORD
push esi
mov esi,lpThis
LLRELEASE .MeshContainer.rgMaterials
mov .MeshContainer.rgMaterials,0

.if .MeshContainer.pTextures != NULL
; for (DWORD i = 0; i < cMaterials; ++i)
xor ecx,ecx
.while ecx < .MeshContainer.cMaterials
push ecx
shl ecx,2 ;x4 for dword sized array elements
LLRELEASE .MeshContainer.pTextures
LLRELEASE .MeshContainer.pEnvironments
pop ecx
inc ecx
LLRELEASE .MeshContainer.pTextures

.if .MeshContainer.pMesh != NULL
mcall ,IDirect3D8_Release
.if eax != NULL
int 3

LLRELEASE .MeshContainer.pMeshHW
LLRELEASE .MeshContainer.pMeshSW
LLRELEASE .MeshContainer.m_pSkinMesh
LLRELEASE .MeshContainer.m_pBoneNamesBuf
LLRELEASE .MeshContainer.m_pBoneOffsetBuf
LLRELEASE .MeshContainer.m_pBoneCombinationBuf
LLRELEASE .MeshContainer.m_pBoneMatrix;
LLRELEASE .MeshContainer.m_pAttrTable
LLRELEASE .MeshContainer.rgdwAdjacency
LLRELEASE .MeshContainer.rgdwAdjacencyTemp
LLRELEASE .MeshContainer.pmcNext
mov eax,esi
pop esi
DestroyMeshContainer ENDP
Posted on 2002-09-05 07:19:38 by Homer
Weird, the MeshFrame post went astray.
Nevermind, here it is.
The MeshFrame object is a special type of container object. This object is a Frame of Reference container for an animated skinned mesh object.
It's "a Frame !!" YAY :)
You'll get support for it soon k :)

;SkinnedAnimatedMeshFrameLL container-object modified from m$ example SFrame object :D
MeshFrame STRUCT
LO LINKEDOBJECT <> ;Standard LL object header - contains nameptr,linkptrs etc
matRot D3DXMATRIX <>
matTrans D3DXMATRIX <>
matRotOrig D3DXMATRIX <>
matCombined D3DXMATRIX <>
m_iLastKey DWORD ?
; // animation information
m_pPositionKeys LPPOSITIONKEY ?
m_cPositionKeys DWORD ?
m_pRotateKeys LPROTATEKEY ?

m_cRotateKeys DWORD ?
m_pScaleKeys LPSCALEKEY ?
m_cScaleKeys DWORD ?
m_pMatrixKeys LPMATRIXKEY ?
m_cMatrixKeys DWORD ?
pframeAnimNext DWORD ? ;LPMFRAME
pframeToAnimate DWORD ? ;LPMFRAME
; pframeSibling DWORD ? ;Redundant - use LinkedObject header field

pframeFirstChild DWORD ? ;Object keeps pointer to its Oldest Child
bAnimationFrame DWORD ? ;Boolean flag
; szText ;Redundant - use LO header field
MeshFrame ENDS
typedef LPMESHFRAME:ptr MeshFrame
Posted on 2002-09-05 07:30:13 by Homer
Heya All.
Here's the next exciting episode, the DrawElement structure.
Included are the quick-rip support functions.
Wonder if anyone really cares about this stuff.
Back soon with the support structures and then we can look at some code.

;skinned draw element
DrawElementLL STRUCT
pframeRoot LPMESHFRAME ?
vCenter D3DXVECTOR3 ?
fRadius FLOAT ?
; animation list
pframeAnimHead LPMESHFRAME ?
fCurTime FLOAT ?
fMaxTime FLOAT ?
DrawElementLL ENDS
typedef LPDRAWELEMENT:ptr DrawElementLL

CreateDrawElementLL PROC lpParent:DWORD
push esi
invoke AddChildEntry,lpParent,sizeof DrawElementLL
mov esi,eax
.if esi!=NULL
mov .DrawElementLL.vCenter.x,NULL
mov .DrawElementLL.vCenter.y,NULL
mov .DrawElementLL.vCenter.z,NULL
fstp .DrawElementLL.fRadius
mov .DrawElementLL.pframeRoot,NULL
mov .DrawElementLL. pframeAnimHead,NULL
mov eax,esi
pop esi
CreateDrawElementLL ENDP

DestroyDrawElementLL lpThis:DWORD
DestroyDrawElementLL ENDP
Posted on 2002-09-06 04:09:39 by Homer
Messy Is as Messy Does :tongue:

Attatched is my Animated Mesh incomplete rip as it stands.
I'll post updates on this as I go.

What it covers:
-Loading and parsing of animated skinned meshes within a Frames hierarchy into a single LL structure.
-Loading Textures, Meshes, Bone data, Animation data and more.
-Dynamic FVF through a dynamic file (anti fvf-cracker whatever that may be ;))
-Lots of support functions and structures like a container object for animated mesh objects, the Frame container for framing amesh containers, support code for killing of parts of the LL database selectively, lots of goodies.
-Code for Rending, code for Animating, Blah Blah and Blah. Just TOO much.

Current issues:
-incorrect encapsulation of some lower functions.
-I need to see some fpcomp conditional example code.

This already represents over 20 hours of work. It's actually beginning to look useful. I know that a lot of people would like to see this completed.
Now, lets have some feedback, pretty please?
Posted on 2002-09-07 02:44:42 by Homer
Wow, I thought I'd have some kind of response to this thread.
Keeping my chin up :)
Posted on 2002-09-08 17:59:52 by Homer
Afternoon, EvilHomer2k.

Thought I'd let ya know that you're not alone:grin: .

I've been (and still am) busy finishing off the Tsunami demo, so I've only had a quick look at the files you're producing.

Might be wise to produce a .chm file to help us get an idea of how everything is glued together. It could also evolve into a tut on how to use the linkedlist and skinnedmeshes. I'll start on one in a few days when I have time again:grin: .

Posted on 2002-09-09 00:51:16 by Scronty
I agree, a tute is definitely in need for this one.
My structs aren't as god awful as they appear.
I've ripped m$'s demo objects almost faithfully, with the inclusion of a LinkedObject Header at every level of the object structure...
Almost all of the code is a pure rip of the Shader Wokshop and SkinnedMesh example. The update attatched should help clear things up a lot.
I've been commenting the code and altering the implementation for N animated meshes.

One of the nicest things I gained from this already was confirmation that D3D does not treat texture objects as COM objects, ie, does not reference them.
It simply instances duplicates which is just tragic.
Included is m$'s variation on texture cache, ripped and modified to use a LinkedList also.
I've gone to town with the LinkedObject header, but in practise its very simple to use, lots more application-specific code has been translated now.
The source will be full of bugs I'm sure, however its progressing nicely, in fact it's almost there now.
Posted on 2002-09-09 03:44:35 by Homer
Heya ..
Another day, another update :)

This is really looking good now, really coming together nicely.
The best part is that I have totally removed the COM encapsulation, without losing the hierarchicality of the support functions.
Ugly Ugly OOP - yuk - HLA all the way :)
We'll look after our own indirection, tyvm - we're asmcoders !!
(has a machinecode background - does it show?:p)
Posted on 2002-09-09 12:49:39 by Homer
Yet Another Update.
Been busy keeping myself up to date in other areas..so this update is not much, just better organized (preparing to put a Loader together), and have begun a CHM helpfile which I've included
Corrected encapsulation of some lower functions etc.
Corrected spurious Interface identifiers.
Added more comments.

- repair the evironmental texture function that m$ left broken in there.
- begin adding info to the chm regarding AMeshes, AFrames etc etc
Posted on 2002-09-13 00:34:26 by Homer
A more complete Helper CHM File.
Still just a beginning, but that's where you start, right? :alright:
Posted on 2002-09-13 22:58:50 by Homer
La de da :)

This update we begin the Loader.
Much debugging has been done.
Currently having fun fixing compiler errors , so yes, it's getting close :alright:
Posted on 2002-09-14 13:34:43 by Homer
I'd like to begin describing the structures and their use.
In order for those who do not fully grasp the concept of hierarchical framework or the power it affords us, I will begin simply, with the global shared texture cache.

The cache is comprised of a LinkedList of "TextureCache" objects.We keep a pointer to the root object in an application variable.
You can think of the TextureCache LinkedList as a line of boxes joined end to end, if that helps.

The TextureCache object looks like this:
;LinkedList TextureCache object
TextureCache STRUCT
LO LinkedObject <>
Count DWORD ?
TextureCache ENDS
LPTEXTURECACHE typedef ptr TextureCache

The TextureCache LinkedList represents an array of loaded textures.The LinkedObject header is used to store the texture filename.The Parent/Child links are employed by the TextureCache, the sibling links are not used.

When something needs to load a texture, we first query the TextureCache.
If the texture is already loaded, we just return pTexture.
If the texture is not loaded, we create a new TextureCache object and chain it.
Either way. we increment Count.
When textures are unloaded by redundant objects, we decrement Count. Should a TextureCache object's Count reach zero, that texture is no longer required and the object can be Killed (links will be patched for us).

Now here is the entry-level code for a LoadTexture procedure which does this.

LoadTexture PROC lpszFilename:DWORD, ppTexture:LPDIRECT3DTEXTURE8,lpDevice:LPDIRECT3DDEVICE8
LOCAL cchFilename:DWORD
;-----------------Filename is required-------------------
.if lpszName==NULL
return S_OK
;-----------------first search the loaded textures--------------------
m2m ptcCur , ptcTextureCacheHead ;Fetch Root of TextureCache
.while ptcCur != NULL
.if .LinkedObject.pName !=NULL
invoke lstrcmp,.LinkedObject.pName, lpszFilename
.if eax == 0 ;!! found !! just return that texture
lea eax, .TextureCache.pTexture ;Fetch object pointer
mov , eax ;Shove value in holder provided
.if eax != NULL ;Ensuring we have a pTexture
inc .TextureCache.Count ;Increment its reference counter
jmp e_Exit ;yay we done!!
.endif ;else if texturename did not match
m2m pLast,ptcCur ;Keep last known objectpointer
m2m ptcCur , .LinkedObject.pChild ;Search children

;---------------if not found, load the texture and add an entry to the cache----------------
invoke CreateTextureCache,pLast,lpszFilename
mov ptcCur,eax ;ptcCur = new TextureCache
.if eax == NULL
jmp e_Exit
;---------------------------------------------Load the Texture object------------------------------------------
invoke D3DXCreateTextureFromFile,lpDevice, lpszFilename, addr .TextureCache.pTexture
mov hr,eax
.if eax==FAILED
mov hr , FAILED
mov .TextureCache.pTexture , NULL
jmp e_Exit

;---------------------------------------Return interface object pointer and add Reference---------------------------------
mov eax, .TextureCache.pTexture
mov ,eax
.if eax != NULL
inc .TextureCache.Count

return hr
LoadTexture ENDP

Also here's a CreateTextureCache procedure as used by the above code to create a new TextureCache linked to the last object in the cache linkedlist.
;The following function creates a new TextureCache for an animatedmesh,
;and sets the filename in the new object.
;This code does not actually load the texture.
;Input params: lpParent is NULL or pointer to parent.
;lpszFileName is a pointer to texture filename.
;Returns pointer to new TextureCache object or else NULL means failure.

CreateTextureCache PROC lpParent:DWORD,lpszFilename:DWORD
push esi
invoke AddChildEntry,lpParent,sizeof TextureCache
mov esi,eax
.if esi!=NULL && lpszFilename!=NULL
mov .TextureCache.pTexture,NULL
invoke NewName,esi,lpszFilename
mov eax,esi
pop esi
CreateTextureCache ENDP

And just to tidy up this package here's a procedure to destroy a redundant TC from the cache without creating pandemonium.

;Simply hand this function the address of a redundant texturecache.
DestroyTextureCache PROC lpThis:LPTEXTURECACHE
invoke KillEntry, lpThis
DestroyTextureCache ENDP

That's plenty of code to implement a solid shared texture array.
Until next time :alright:
Posted on 2002-09-15 17:54:36 by Homer
Thought for the day:

The Animated Skinned Mesh object is a type of hierarchy similar to a scene hierarchy. Loading one from an xfile is also similar to loading a scene hierarchy.
In order to do that we'll be doing the following:

We'll be using functions from the IDirectXFile interface. First we'll use IDirectXFile_CreateEnumObject to access a specific xfile. This returns a "IDirectXFileEnumObject" interface pointer. Using the IDirectXFileEnumObject_GetNextDataObject function we scan all the "toplevel templates" in the xfile scene hierarchy. This returns a "IDirectXFileData" interface pointer. Using functions like IDirectXFileData_GetID and IDirectXFileData_GetName we can query the hierarchy of a toplevel template object further.

Thus the premise of loading a skinned mesh object is no different to loading any other scene hierarchy.
Posted on 2002-09-16 00:38:45 by Homer
Hi EvilHomer2k

I've just been looking at your linked list code and was wondering why you call GlobalLock when
allocating memory as GlobalAlloc returns a ptr when using GPTR.

The SDK docs also recommend using the HeapAlloc/Free functions, but you already know that. ;)

Please correct me if I'm wrong and keep up the great work!

EDIT: I've just read your homerhelp file and the more? link on the Animated Skinned Meshes ( structure ) page doesn't work - no donuts for you :eek:

Posted on 2002-09-21 19:15:30 by Maelstrom
ok here it is, I used to think the way you do as well :)

GlobalAlloc calls HeapAlloc internally, so that's irrelevant. the important thing is that it returns not a memory pointer but a memory object handle, which we are meant to hand to GlobalLock so that we can get our hands on the actual memory pointer itself.
GlobalLock marks the memory chunk within the os memory manager in such a way that it is to be considered part of the application's virtual memory space, and any attempt to write to this memory made from a thread not associated with the application process will result in a particular memory access violation which is trappable by the application itself.

Simply calling HeapAlloc will allocate memory within the application's Heap memory space, which although virtual, is finite and is defined in the PE Header of the executable. This memory is guaranteed to be Owned by the application process and its children, but its finite, and this means that theres just so much to go around.

I assume that GlobalAlloc uses HeapAlloc internally because it stores information pertaining to the memory object it allocated on the Heap, and the handle reurned by GlobalAlloc is probably a pointer to that data on the Heap, but I don't know that for sure. What I do know is that HeapAlloc is dandy for light duties, but not up to the task of massive databasing on its own.
Posted on 2002-09-26 08:47:08 by Homer
ahh I better qualify those remarks ..
GPTR simply combines the GMEM_FIXED and GMEM_ZEROINIT flags.

The win32 api says this..."The GlobalAlloc function allocates the specified number of bytes from the heap. In the linear Win32 API environment, there is no difference between the local heap and the global heap."

But it also says "The HeapAlloc function allocates a block of memory from a heap. The allocated memory is not movable."

And yet we know that we CAN request movable memory using GlobalAlloc.
How can this be if it is calling HeapAlloc?
The answer I believe is that when we allocate memory using GlobalAlloc, tha it allocates it not within the heap's memory space, but outside it, and stores a pointer to it on the heap, thus growing the heap.
To be honest I have never traced the api functions to find out, but that would explain the need for ever Locking memory owned by the process, and also why GlobalAlloc doesn't actually ever return a flat memory address.
Posted on 2002-09-26 09:05:18 by Homer

Win32 api GlobalLock ( under remarks )

Memory objects allocated with GMEM_FIXED always have a lock count of zero. For these objects, the value of the returned pointer is equal to the value of the specified handle.

Wouldn't that mean the value returned by GlobalLock would be the same as the value returned by GlobalAlloc when using GMEM_FIXED or GPTR?
Sorry for the continued pestering but I feel memory management is an important area to understand properly, but if you're correct, the Win32 api is more than a little misleading.

Also I noticed you generally move memory into EAX before moving it into another register, any particlar reason for this?

Posted on 2002-09-26 20:00:09 by Maelstrom
You are correct about the returned values from GlobalAlloc and GlobalLock when using FIXED memory request. Do not assume from this that GlobalLock performs no function !!
We do use EAX extensively in the LinkedList sourcecode as an intermediate. There is no reason for this, the code has been deliberately written to be clear to the reader. It is not optimized. The compiler does a fairly good job of cleaning up this code, but really it should be hand-optimized. I will hand-optimize and release a more complete version of this source in the near future.
I have spent the last couple of weeks teaching myself CGI animation and learning the fine aspects of modelling/animating under Maya 4, having learned that the Tiny.x model used by m$ in their AniMesh example was actually made in Maya.
I wasn't prepared to finish writing the AniMesh Loader until I had seen a lot more realworld examples, and it's a good thing I did, because in GameDev we've advanced from the simple AniMesh to something called "weight-blended nonlinear keyframed sequences" in which more than one range of nonlinear keyframes is blended, for example blending a walk animation with a scratching of the head (left arm) and pointing toward a target (right arm). The three animations here are three sections of the one set of keyframes, blended together with one of them being bound to Time (the walk animation) and the extra animations being bound to it (thus Timing of animations 2 and 3 is blended from the start and end frame information). I guess I'll be reworking the AniMesh object just a little to incorporate the weighting factor(s), although they already support multiple animated sequences, I wasn't figuring on Blending them at that level.
Posted on 2002-10-06 05:55:04 by Homer
Damn I love the crystal clear documentation MS provides :rolleyes:

Anyway, the animation stuff sounds very cool.

I don't know much about this so I apologise in advance if any questions are stupid ;)

1. When you say keyframed, do you mean statically defined meshes, or dynamically generated meshes using a skeleton and weighted vertex blending?
2. Wouldn't a skeletal solution scale better to the users CPU?
3. Will you be using vertex shaders to accomplish the interpolation between animations?
4. How do you intend to support full body meshes, one piece, or built from multiple pieces?

Posted on 2002-10-06 20:53:32 by Maelstrom