Ok I'll admit I've had a go on Morrowind, it's my parental duty to make sure that content is suitable for my impressionable youngster :grin:
It's a wonderful game, but reminds me of Trespasser in that it's a bit slow...
Unfortunately if I spent all my time playing other peoples games, I'd never get around to writing my own games (or anything else), and I have to admit getting a lot more satisfaction from the latter than the former.

Here's another version of FrameNode_Load which is handling the loading of the Mesh, textures and materials for our animated SkinMesh models..
If you look down below it , you'll find the necessary function which performs all that.

FrameNode_Load PROC pData:DWORD, ParentFrame:DWORD, pSkinMeshOwner:DWORD
local pSubObj:DWORD
local pSubData:DWORD
local pDataRef:DWORD
local pType:LPGUID
local NameBuf[256]:BYTE
local ErrBuf[256]:BYTE
local pmeshnode:LPMESHNODE
local panimationset:LPANIMATIONNODE
local dwSize:DWORD
local hFrame:LPFRAMENODE
local pmat:LPD3DXMATRIX
local dwsize:DWORD
local hname:DWORD
;local parentframe:DWORD
;===============================================================================
;Initialize locals (DX can be finicky - some output args can also be input args !!)
;===============================================================================
xor eax,eax
mov pSubObj,eax
mov pSubData,eax
mov pDataRef,eax
mov pType,eax
mov NameBuf[0],al
mov dwSize,eax
mov hFrame,eax
mov pmeshnode,eax
mov panimationset,eax
;m2m parentframe,ParentFrame
;===============================================================================
invoke IdentifyGUID, pData
;===============================================================================
.if eax==Frame
mov hFrame, $invoke (LinkedLists_AppendSibling,ParentFrame,NULL, sizeof FrameNode)
.if hFrame==NULL
Errr CTXT("Error - Failed to create FrameNode")
.endif
.if $mcall (pData,IDirectXFileData_GetName, NULL, addr dwSize)!=D3D_OK
Errr CTXT("Failed to get Template Name Length")
.elseif dwSize!=0
mcall ,IDirectXFileData_GetName, addr NameBuf, addr dwSize
.endif
.if NameBuf[0]== NULL ; // Give a default name if none found
invoke lstrcpy,addr NameBuf, CTXT("$NamelessTemplate$")
.endif
invoke LinkedObjects_SetName, hFrame, addr NameBuf
;===============================================================================
.elseif eax==FrameXFormMatrix
; // Read the Matrix data straight into our buffer(s)
mov esi,$invoke (GlobalLock,ParentFrame)
assume esi:nothing
assume esi:ptr FrameNode
lea ebx,.OriginalMatrix
m2m pmat, ebx
mcall ,IDirectXFileData_GetData, NULL, addr dwsize, addr pmat
.if eax==DXFILE_OK
lea ebx,.TransformationMatrix
m2m pmat, ebx
mcall ,IDirectXFileData_GetData, NULL, addr dwsize, addr pmat
.if eax != DXFILE_OK
invoke GlobalUnlock,ParentFrame
Errr CTXT ("Couldn't read frame transformation matrix from an x file")
.endif
.else
invoke GlobalUnlock,ParentFrame
Errr CTXT ("Couldn't read frame transformation matrix from an x file")
.endif
invoke GlobalUnlock,ParentFrame
;===============================================================================
.elseif eax==Mesh

;** Create a meshnode, append it to the parent frame's list of meshes, and load the mesh into it
;** Note the usage of NULL for target object indicates it needs to be created as well as linked
mov esi,$invoke (GlobalLock,ParentFrame)
assume esi:nothing
assume esi:ptr FrameNode
mov pmeshnode,$invoke (LinkedLists_Append, .pMeshes, NULL, sizeof MeshNode)
.if .pMeshes==NULL ;is the list's root object NULL meaning no existing entries?
m2m .pMeshes,pmeshnode ;if it is, then this meshnode is the new Root (top of List)
.endif
mov esi,pmeshnode
assume esi:nothing
assume esi:LPMESHNODE
mov .pD3DXSkinMesh,NULL
mov .pD3DXBlendedMesh,NULL
invoke MeshNode_Create, pmeshnode, pData ;ok now attempt to load the mesh (may also be hierarchy!)
invoke GlobalUnlock,ParentFrame
invoke MessageBox,0,CTXT("MESH PARSED OK"),CTXT("kk"),MB_OK
;------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
.endif
;------------------------------------------------------------------------------------------------------------------------------------------------------------------------------





; Scan for embedded templates

mcall ,IDirectXFileData_GetNextObject, ADDR pSubObj
.while eax!=DXFILEERR_NOMOREOBJECTS
.if eax==DXFILEERR_BADVALUE
Errr CTXT("Parser Error - DXFILEERR_BADVALUE - in Frames Parser")
.endif

.if ($mcall(pSubObj,IDirectXFileObject_QueryInterface, ADDR IID_IDirectXFileDataReference, ADDR pDataRef)) == DXFILE_OK
.if ($mcall(pDataRef,IDirectXFileDataReference_Resolve,addr pSubData))==DXFILE_OK
invoke IdentifyGUID,pSubData
invoke MessageBox,0,CTXT("REFOBJECT IDENTIFIED"),CTXT("YAYYYYYY"),MB_OK
invoke FrameNode_Load,pSubData, hFrame,pSkinMeshOwner
_saferelease pSubData
.endif
_saferelease pDataRef
.endif

.if ($mcall (pSubObj, IDirectXFileObject_QueryInterface,addr IID_IDirectXFileData, addr pSubData)) == DXFILE_OK
invoke FrameNode_Load,pSubData, hFrame,pSkinMeshOwner
_saferelease pSubData
.endif


_saferelease pSubObj
mcall ,IDirectXFileData_GetNextObject, ADDR pSubObj
.endw
ret
FrameNode_Load ENDP











(next please, Homer!)










MeshNode_Create PROC pThis:ptr MeshNode, pxofobj:ptr
local pmaterialsbuf:LPD3DXBUFFER
local axmaterial:LPD3DXMATERIAL
local padjacencybuf:LPD3DXBUFFER
local pboneoffsetbuf:LPD3DXBUFFER
local pbones:ptr
local padjacencyin:ptr
local pname:ptr
local dwnamelength:DWORD
local dwfacescount:DWORD
local dwnumbones:DWORD
local buffysize:DWORD
local UrMomma:DWORD
local namebuf[256]:BYTE
local ErrBuf[256]:BYTE
;=====================================================================================
xor eax,eax ;initialize locals
mov pboneoffsetbuf,eax
mov padjacencyin,eax
mov padjacencybuf,eax
mov pmaterialsbuf,eax

.if pThis==NULL
Errr CTXT("Null Parent when creating MeshNode")
.endif
push esi ;shove esi on the Stack for the remainder of this procedure
mov esi,$invoke (GlobalLock,pThis) ;(and this is why - we're going to use it as our "object pointer")
assume esi:nothing
assume esi:LPMESHNODE
mov .dwNumMaterials,NULL ;just for safety in next call
mov .pBoneNamesBuf,NULL
mov .pD3DXSkinMesh,NULL

;=====================================================================================
; ;*** Assume that the MeshNode was named when it was created ***
;=====================================================================================

;*** load the mesh ***
invoke D3DXLoadSkinMeshFromXof, pxofobj, D3DXMESH_MANAGED, lpd3dDevice , addr padjacencybuf,
addr pmaterialsbuf, addr .dwNumMaterials, addr .pBoneNamesBuf, addr pboneoffsetbuf, addr .pD3DXSkinMesh
.if eax!=D3D_OK
invoke GlobalUnlock,pThis
pop esi
Errr CTXT( "Couldn't load skin mesh" )
.endif
mov dwfacescount , $mcall (.pD3DXSkinMesh,ID3DXSkinMesh_GetNumFaces)
mov dwnumbones , $mcall (.pD3DXSkinMesh,ID3DXSkinMesh_GetNumBones)

ifdef _DEBUG_
push esi
pushad
invoke wsprintf,addr ErrBuf,CTXT("SkinMesh Geometry consists of:",13,10,"%lu Faces",13,10,"%lu Bones",13,10,"%lu Materials"),dwfacescount ,dwnumbones,.dwNumMaterials
invoke MessageBox,0,addr ErrBuf,CTXT("LOADED SKINMESH GEOMETRY"),MB_OK
popad
pop esi
endif
;=====================================================================================
.if dwnumbones>0 ;*** IF BONES ARE FOUND, FETCH BONE INFO AND CONVERT SKINMESH TO BLENDED MESH
;push esi
;pushad
;invoke MessageBox,0,CTXT("BONES were found - converting SkinMesh to BlendedMesh."),CTXT("PROCESSING SKINMESH GEOMETRY"),MB_OK
;popad
;pop esi
mov eax,dwnumbones
mov ebx,sizeof D3DXMATRIX
mul ebx ;Calculating size of BoneMatrices Buffer(s)
mov buffysize,eax ;buffysize = "enough room for #BONES * sizeof one Matrix"

mov eax,dwfacescount
mov ebx,3*sizeof DWORD
mul ebx ;Calculating size of Adjacency Index Buffer
mov UrMomma,eax ;UrMomma = #faces * (3 dwords per face)
mov .pAdjacency,$invoke (GlobalAlloc, GPTR, UrMomma) ;Allocate memory for Adjacency Buffer
mov .pBoneMatrix , $invoke (GlobalAlloc, GPTR, buffysize) ;Allocate memory for Bone Matrix Array
mov .pBoneOffsetMatrix ,$invoke (GlobalAlloc, GPTR, buffysize) ;Allocate memory for Bone Offset Matrix Array

mov pbones, $mcall (pboneoffsetbuf,ID3DXBuffer_GetBufferPointer) ;Get access to the actual buffer data
mov padjacencyin, $mcall (padjacencybuf,ID3DXBuffer_GetBufferPointer) ;for both these two buffers
mov dwnumbones, $mcall (.pD3DXSkinMesh,ID3DXSkinMesh_GetNumBones)
mov .NumberOfBones,eax

invoke RtlMoveMemory, .pBoneOffsetMatrix, pbones, buffysize ;Copy the BoneOffsetMatrices
invoke RtlMoveMemory, .pAdjacency, padjacencyin, UrMomma ;Copy Adjacency info

invoke MeshNode_GenerateMesh, pThis ;Convert SkinMesh to BlendedMesh

;=====================================================================================
.else ;*** IF NO BONES ARE FOUND, BLEND THE MESH AND USE AS A STATIC MESH (NOT ANIMATED)
mcall .pD3DXSkinMesh,ID3DXSkinMesh_GetOriginalMesh,addr .pD3DXBlendedMesh
_saferelease .pD3DXSkinMesh
m2m .dwAttrCount , .dwNumMaterials
.endif
;=====================================================================================
;=====================================================================================
;=====================================================================================
;=====================================================================================
;=====================================================================================
;=====================================================================================
;THE FOLLOWING CODE IS EXECUTED WHETHER THE MESH HAS BONES OR NOT
;// check for materials
;=====================================================================================
;Case - Materials were NOT found
;=====================================================================================
.if( (pmaterialsbuf == NULL) || ( .dwNumMaterials== 0)) ;** IF no materials are found, create a default material
mov .pMeshMaterials , $invoke (GlobalAlloc, GPTR, sizeof D3DMATERIAL8)
mov .dwNumMaterials,1
mov .pMeshTextures,NULL

invoke RtlZeroMemory, .pMeshMaterials, sizeof D3DMATERIAL8
mov ebx,.pMeshMaterials
assume ebx:ptr D3DMATERIAL8
fld fphalf
fst .Diffuse.r
fst .Diffuse.g
fst .Diffuse.b
fst .Specular.r
fst .Specular.g
fst .Specular.b
assume ebx:nothing
;=====================================================================================
;Case - One or more Materials have been found and must be processed...
;=====================================================================================
.else ;** IF Materials were found, we will load them
;push esi
;pushad
;invoke MessageBox,0,CTXT("Processing MATERIALS."),CTXT("Processing Mesh Geometry"),MB_OK
;popad
;pop esi
mov eax,sizeof D3DMATERIAL8
mul .dwNumMaterials
mov .pMeshMaterials, $invoke (GlobalAlloc, GPTR,EAX)
mov ebx,.dwNumMaterials
shl ebx,2
push esi
push ebx
mov .pMeshTextures, $invoke (GlobalAlloc, GPTR,ebx)
pop ebx
pop esi
push esi
invoke RtlZeroMemory, .pMeshTextures,ebx
; invoke MessageBox,0,CTXT("Materials and Textures buffers were allocated"),CTXT("Looking Good"),MB_OK
mov axmaterial , $mcall (,ID3DXBuffer_GetBufferPointer)
; invoke MessageBox,0,CTXT("Got Pointer to Materials Buffer"),CTXT("Looking Good"),MB_OK
pop esi

mov ecx,0
.while ecx<.dwNumMaterials; for( int i = 0; i < dwMaterialCount; i++ )
push ecx ;(preserve loop counter during loop)
push esi

mov edi,.pMeshMaterials ;theres a subtle difference between these
mov esi,axmaterial ;Calculate source and destination

mov ebx,sizeof D3DXMATERIAL ;for copying the current Material
imul ebx,ecx ;note that we are copying from
add esi,ebx ;a D3DXMATERIAL into a D3DMATERIAL
mov ebx,sizeof D3DMATERIAL8 ;Do you know what it is?
imul ebx,ecx
add edi,ebx ; aMaterial = axmaterial.MatD3D = Copy one material
assume esi:LPD3DXMATERIAL
m2m pname, .pTextureFilename
assume esi:nothing
push esi
invoke RtlMoveMemory, edi, esi, sizeof D3DMATERIAL8
pop esi
; invoke wsprintf,addr namebuf,CTXT("Try to load %s"), pname
; invoke MessageBox,0,addr namebuf,CTXT("TEXTURE LOADER"),MB_OK

;** DOES This Material require a Texture to be loaded ?? Huh? Huh? Does it ??
.if pname != NULL ;YES so load the texture
pop esi
pop ecx
push ecx
push esi
shl ecx,2
assume esi:LPMESHNODE
mov esi,.pMeshTextures
add esi,ecx
push esi
invoke D3DXCreateTextureFromFileEx, lpd3dDevice, pname,
D3DX_DEFAULT, D3DX_DEFAULT,D3DX_DEFAULT, 0, D3DFMT_UNKNOWN, D3DPOOL_MANAGED,
D3DX_DEFAULT, D3DX_DEFAULT, 0, NULL, NULL, esi
pop esi
.if eax==D3D_OK
; invoke wsprintf,addr namebuf,CTXT("W00T - Texture = %s"), pname
; invoke MessageBox,0,addr namebuf,CTXT("TEXTURE LOADED"),MB_OK
jmp @F
.endif
.endif
pop esi
pop ecx
push ecx
push esi
shl ecx,2
assume esi:LPMESHNODE
lea esi,.pMeshTextures
add esi,ecx
mov dword ptr , NULL

@@:
; invoke MessageBox,0,CTXT("Next Material"),CTXT("MESHLOADER"),MB_OK
pop esi
pop ecx ;** RESTORE LoopCounter
inc ecx ; and increment it for the next Material
.endw ;and LOOP until we have processed ALL MATERIALS

; invoke MessageBox,0,CTXT("All Materials (and Textures) have been processed and loaded."),CTXT("MESHLOADER"),MB_OK

;=====================================================================================
.endif ;(no more cases)
;=====================================================================================
_saferelease padjacencybuf
_saferelease pmaterialsbuf
_saferelease pboneoffsetbuf
;=====================================================================================
invoke GlobalUnlock,pThis
invoke MessageBox,0,CTXT("AWESOME"),CTXT("MESHNODE CREATED"),MB_OK
;=====================================================================================
pop esi
return S_OK
MeshNode_Create ENDP


(please disregard my debug messages, the function is working fine :))



Next I'll re-enable the AnimationSet parsing code (also driven by FrameNode_Load) as it stands at the moment, then maybe you (maelstrom) might be inclined to help me improve the node hierarchy for storing them all.
I'm afraid it may not be quite flexible enough just yet. Hopefully by the end of the week, we'll be able to rend looped animations by name :)
Posted on 2003-10-19 06:22:18 by Homer
Just incase more than two humans are reading this, and haven't bothered downloading the (quite broken) early zip of the entire module...

;=====================================================================================
MeshNode STRUCT
LO LinkedObject <>
; // mesh variables
pD3DXSkinMesh LPD3DXSKINMESH ? ;< Pointer to the D3DX skin mesh
pD3DXBlendedMesh LPD3DXMESH ? ;< Pointer to the blended version
pAdjacency DWORD ? ;< The adjacency array
; // materials
pMeshMaterials DWORD ? ;< Pointer to the array of material structs
pMeshTextures DWORD ? ;< The array of pTexture dwords
dwNumMaterials DWORD ? ;< The number of materials
NumberOfBones DWORD ?
; // skinning info
dwAttrCount DWORD ? ;< The attribute count
dwMaxFaceInfluence DWORD ? ;< Maximum face influence
pBoneMatrix LPD3DXMATRIX ? ;< Bones' matrices array
pBoneNamesBuf LPD3DXBUFFER ? ;< Bones' names buffer
pBoneOffsetMatrix LPD3DXMATRIX ? ;< Bones' offset matrices
pBoneCombinationBuf LPD3DXBUFFER ? ;< Bone combinations buffer
MeshNode ENDS
LPMESHNODE typedef ptr MeshNode
;=====================================================================================
Posted on 2003-10-19 06:24:42 by Homer
MeshNode is meant to be able to be used for not just animated SkinMesh, but also for non-animated (dynamically animated) SkinMesh as well as animated unskinned meshes and plain old nonanimated unskinned meshes.

In other words, it's a universal Mesh container and loader, which HAPPENS to be called by FrameNode_Load of the SkinMesh Loader, but is designed to be a generic and freely-callable helper function.

Just thought I better mention that :tongue:
Posted on 2003-10-19 06:29:01 by Homer
ah crap - I suppose u'll want this too :alright:

MeshNode_GenerateMesh PROC pThis:LPMESHNODE
local padjacencyin:ptr
local dwfacecount, sizAdj:DWORD
local ErrBuf[256]:BYTE

.if pThis==NULL
Errr CTXT("MORON tried to Generate NULL MESHNODE OBJECT")
.endif
push esi
mov esi,pThis
assume esi:LPMESHNODE
.if .pD3DXSkinMesh==NULL
pop esi
Errr CTXT("MORON tried to Generate NULL pD3DXSkinMesh IN MESHNODE OBJECT")
.endif

mov dwfacecount, $mcall (.pD3DXSkinMesh,ID3DXSkinMesh_GetNumFaces)
mov ebx,3*4
mul ebx
mov sizAdj,eax
mov padjacencyin , $invoke (GlobalAlloc,GPTR, sizAdj) ;Allocate buffer for Adjacency info

push esi
invoke RtlMoveMemory, padjacencyin, .pAdjacency, sizAdj ;copy the adjacency buffer
pop esi


_saferelease .pD3DXBlendedMesh ;Release the defunct pD3DXBlendedMesh, if any
;*** BLEND the mesh
mcall .pD3DXSkinMesh,ID3DXSkinMesh_ConvertToBlendedMesh, D3DXMESH_WRITEONLY, padjacencyin, .pAdjacency, addr .dwAttrCount, addr .pBoneCombinationBuf, 0 , 0, addr .pD3DXBlendedMesh
push eax
invoke GlobalFree, padjacencyin ;Free the temp Adjacency buffer
mov padjacencyin,NULL
pop eax
.if eax!=D3D_OK
pop esi
Errr CTXT( "Couldn't convert SkinMesh to BlendedMesh")
.endif


; CALCULATE the max face influence count
mcall .pD3DXBlendedMesh,ID3DXBaseMesh_GetFVF
and eax, D3DFVF_POSITION_MASK
.if eax != D3DFVF_XYZ
sub eax,D3DFVF_XYZRHW
shr eax,1
inc eax
mov .dwMaxFaceInfluence ,eax ; = ((EAX - D3DFVF_XYZRHW) / 2) +1;
.else
mov .dwMaxFaceInfluence , 1
.endif
ifdef _DEBUG_
pushad
invoke wsprintf,addr ErrBuf,CTXT("-= Converted SkinMesh to BlendedMesh =- ",13,10, "--Max Faces Influencing Vertices = %lu"), .dwMaxFaceInfluence
invoke MessageBox,0,addr ErrBuf,CTXT("MeshNode_GenerateMesh"),MB_OK
popad
endif
pop esi
return S_OK
MeshNode_GenerateMesh ENDP
Posted on 2003-10-19 06:30:39 by Homer
Again, please dismiss debugging messages, overlook the general untidiness of code under review, ignore my ramblings, and please, let me know your thoughts !!


(last night I had the strangest dream, involving skeletons running around with their bones all clearly labelled... I need a vacation!!)
Posted on 2003-10-19 06:32:38 by Homer
Keen as mustard :)

Ok the parser is almost complete now, but I noticed something I did not expect.
I'm testing the code by loading Tiny.x, used in m$'s skinmesh examples.
Tiny.x contains TWO TOPLEVEL OBJECTS.
The first one is the Frames Hierarchy (and Mesh). We might think of this as being the Bones Hierarchy of the SkinMesh model's skeleton, because it is :)
This is where it gets a little weird. The example source I have seen for xfile parsers always expects that any AnimationSet(s) and Animation(s) are found somewhere within the Frames hierarchy. In the case of Tiny.x, that isn't so.
Tiny.x keeps its AnimationSet as TOPLEVEL OBJECT #2.
As a result I'm editing my SkinMesh_Create function (which is the Caller of FrameNode_Load) to check for and parse Animation Frames from there.
I will however leave the support for embedded animation parsing in the FrameNode_Load function. We might get into trouble if we try loading an XFile that contains Animations in both, as my Node structure wasn't designed to have more than one Root. Nevermind, we'll work around it :)
The main thing is that I'm having a good day and grinning like an idiot :grin:
Posted on 2003-10-19 11:11:41 by Homer
Hey Homer

Wow, slow down Homer you might pull something :grin:

then maybe you (maelstrom) might be inclined to help me improve the node hierarchy

Cool, I'd love to help out if I can :alright:
Could you post or email me a zip containing the current project files?
Are you using DX8.1?

last night I had the strangest dream, involving skeletons running around with their bones all clearly labelled... I need a vacation!!

LOL - go play Morrowind for a while :grin:

I'm having a good day and grinning like an idiot

Oh I know that feeling :grin:

Maelstrom
Posted on 2003-10-19 19:53:45 by Maelstrom
This project is built on DX8.1
Due to filesize restrictions, it's quite painful for me to upload the entire project.
In fact, even if I remove all resources (xfiles, bmps etc) and unimplemented code modules (audio engine etc) and then rar and zip it, it's still over 300kb.
I have no choice but to upload separate zips.
This is fine because I keep related files in a folder hierarchy anyway - ie, the source is already bundled into tidy folders.

You will have to create the folders yourself.
The Project folder contains two folders. One called XFILE contains xfiles and their textures. The other called Include contains a bunch of files and a bunch of folders.
I won't name the files in there but the Folders inside the Include folder are as follows: AudioEngine, Camera, SkinMesh, Terrain, Plants and Render.

Of these, only Camera, SkinMesh and Render contain currently-implemented code.

Having said that, I'm going to post the main asm file here now.
Please note my Naming convention - when I code a MASM project, it contains JUST ONE .ASM FILE, all other included files use the extension .INC
This means I can build my project even when I've been drinking too much beer :)

Without further ado: the Win32 Skeleton :alright:
Take a look, let me know how you feel about it, I've spent no time optimizing this yet, and I'm sure there's many improvements to be made.



; #########################################################################
.586p
.MMX
.MODEL FLAT,STDCALL
OPTION SCOPED ; Local labels are enabled, global labels inside
; PROC should be defined with double colons (LABEL::)
OPTION CASEMAP:NONE ; case sensitivity enabled
OPTION LJMP
MAIN_THREAD_STACKSIZE_KB=32
;#########################################################################


;September 15 - been working hard on translating support for animated SkinMesh models :)
;September 17 - successful compile - begin debugging
;September 22 - debugged MeshNode_Create and MeshNode_GenerateMesh I think 100% working
; - have Frames Loader working better but falls over when trying to process Bone info hmmmz
; - conclusion = buggy frames loader = yucky hierarchy

;September 31 - Adopted a new model for use of links in building of frames hierarchy
;October 1 - Eliminated a bug in the Materials Parser of Mesh Loader (esi was being trashed by RtlMoveMemory)
;October 2 - Working on AnimationSet Loader
;October 3 - Disabled call to SkinMesh_FindBones until I have debugged preceding code.
; Tomorrow's job will be isolating the bug.
; I'll begin by tidying up object creation code to properly null fields.
;October 4 - Bug resides somewhere in Animation Loader (no big suprise there)
; Began rebuilding Loader so that template identification happens before FrameNode_Load
; So that Frame templates are treated as a Case rather than given such importance.
;October 5 - MAJOR THINGS HAPPPENING
; Studied Scronty's FramesHierarchy example and noticed something VERY IMPORTANT
; He LOCKS AND UNLOCKS his dynamic objects when he creates them,
; and he LOCKS AND UNLOCKS them again before and after he accesses them !!!
; I've altered the LinkedList code to this end, which has made pLock redundant.
; THAT WAS A BASTARD OF A JOB !!
; I still haven't done the Destructors, and I disabled lots of stuff using LL to get LL working first.
; Next are the generic LL destructors.
;October 6 - Busily altering the ENTIRE CODEBASE to operate with OBJECT HANDLES
; rather than POINTERS. This is most distressing. My hair !! DOH :|
;October 11- Everythings hunky dory again - I just moved the call to LoadSkinMesh before I saved.
;October 12- moved call to skinmesh loader into its own Loader thread, created in WndProc on WM_CREATE.
;October 13- grr what happened to my SkinMesh loader? I'm no longer detecting nonframe templates !!?!
;October 19- SkinMesh loader told me it feels better than James Brown
;October 20- Published this source on Win32asmCommunity.net messageboard

;=============================
FAILED EQU 0
SUCCEEDED EQU 0
;=============================
DEFAULT_QUALITY EQU 0
DRAFT_QUALITY EQU 1
PROOF_QUALITY EQU 2
NONANTIALIASED_QUALITY EQU 3
ANTIALIASED_QUALITY EQU 4
;=============================
GS_RUNNING equ 0 ;GameStates
GS_MENU equ 1
GS_LOADING equ 2
GS_SPLASH equ 3
;=============================
GM_MAIN equ 0 ;MenuStates
GM_VIDEO equ 1
GM_AUDIO equ 2
GM_GAME equ 3
GM_BACK equ 4
;=================================================================
;We'll invent an object identification system so we can be lazy with our coding :)
OBJECT_TYPE_NOT_DEFINED equ 0
CHUNKY_3D_TEXT_OBJECT equ 1
;=================================================================

;###################################################################################
.data
include Include\Project.inc ;This contains application data, api includes, structs, typedefs, etc.
include Include\MACRO_DegreesToRadians.inc ;These Helper Macros leave stuff on the FP stack !!
include Include\MACRO_RadiansToDegrees.inc
;###################################################################################
.code
include Include\RandomGen.inc ;A nice Mersenne Twister variant (author unknown - sorry!)
include Include\dxutil_Timer.asm ;Thx Scronty
include Include\LinkedLists_NamedObjects.inc ;Thx Homer - LinkedList management
include Include\LinkedLists_SafeDelete.inc ;Thx Homer - LinkedList management
include Include\SafeRelease_ObjectLL.inc
include Include\SafeRelease_LL.inc
include Include\LinkedLists_FindYoungestSibling.inc
include Include\LinkedLists_AppendOrInsert.inc ;Thx Homer - LinkedList management
include Include\LinkedLists_Append.inc ;Thx Homer - LinkedList management
include Include\LinkedLists_AppendSibling.inc ;Thx Homer - LinkedList management
include Include\LinkedLists_FindEntryByName.inc ;Thx Homer - LinkedList management

include Include\Camera\PlayerObject.inc
include Include\SafeRelease_Player.inc

include Include\Pick_ScreenToVector.inc
include Include\Mouse_PickTest.inc ;Thx Scronty - called from Object_DrawInPosition

include Include\Object_LoadMeshFromXFile.inc
include Include\Object_SafeRelease_MACRO.inc ;Thx Homer - Application LinkedObject management
include Include\SafeRelease_TexObject.inc
include Include\Object_CreateLinked3DText.inc ;Thx Homer - Application LinkedObject management
include Include\Object_CalculateMatrix.inc ;Thx Homer The Wise
include Include\Object_AlterRotationWithTime.inc ;Thx Homer
include Include\Object_DrawInPosition.inc ;Thx Homer

include Include\Plants\HerbGrower.inc
; include Include\Terrain\Terrain_Create.inc

include Include\CmpGUID.inc
include Include\SkinMesh\IdentifyGUID.inc
include Include\SkinMesh\MeshNode.inc
include Include\SkinMesh\AnimationNode.inc
include Include\SkinMesh\FrameNode.inc
include Include\SkinMesh\SkinMesh.inc ;Container support for SkinMeshes

include Include\DrawTextLine.inc
include Include\Render_Text.inc
include Include\D3DUtil_SetDeviceCursor.inc ;Thx Homer
include Include\D3D_CheckHardware.inc
include Include\Create3DAlphabet.inc
include Include\Create2DPanel.inc
include Include\D3D_Initialize.inc ;called from WndProc
include Include\D3D_InitFonts.inc ;called from InitD3D, initialises a couple of D3D Fonts

include Include\ConvertDWColorToD3DXColor.inc ;Thx Homer
include c:\masm32\include\d3dx8math_fkt.def ;Thx Caleb .. contains LERP + much more
include Include\Make3DText.inc ;called from CreateLinked
include Include\SetupCamera.inc ;called from Render, =setup camera view
include Include\SetupLights.inc ;called from Render, creates a cool lightsource
include Include\Render.inc ;called from MessagePump, =main render proc
include Include\CheckDeviceIsGoodToRender.inc

include Include\LoaderThread.inc

;include \masm32\include\winextra.def
;include \masm32\include\oaidl.inc
; include \masm32\include\dmplugin.def ;directmusic includes -->dmusicc.def-->dmusici.def
; include Include\AudioEngine\AudioEngineData.inc
; include Include\AudioEngine\InitializeMusic.inc
; #########################################################################


.code
start:

;============================================================================
;The following code snippet was used to test FP Compare macro
;============================================================================
; fld fp2
; fchs
; fcomp fp1
; __FJL Term1IsLower
; invoke MessageBox,0,CTXT("Term1 is NOT Lower than Term2"),CTXT("FPTEST"),MB_OK
; jmp @F
; Term1IsLower:
; invoke MessageBox,0,CTXT("Term1 is Lower than Term2"),CTXT("FPTEST"),MB_OK
; @@:
;============================================================================

call TRandomInit
mov hInstance, $invoke (GetModuleHandle,NULL)
mov CommandLine, $invoke (GetCommandLine)
mov hIcon, $invoke (LoadIcon,hInstance,500) ; icon ID
mov hCursor, $invoke (LoadCursor,NULL,IDC_ARROW)
mov sWid, $invoke (GetSystemMetrics,SM_CXSCREEN)
mov sHgt, $invoke (GetSystemMetrics,SM_CYSCREEN)
call Main
invoke ExitProcess,eax
invoke InitCommonControls

; #########################################################################
Main proc
SingleInstanceOnly CTXT("More_Ass_Than_Class")
invoke RegisterWinClass,ADDR WndProc,CTXT("More_Ass_Than_Class"),
hIcon,hCursor,COLOR_BTNFACE+1
invoke CreateWindowEx,NULL,
CTXT("More_Ass_Than_Class"),
ADDR szDisplayName,
WS_OVERLAPPED or WS_SYSMENU,
0,0,sWid,sHgt,
NULL,NULL,
hInstance,NULL
mov hWnd,eax
DisplayWindow hWnd,SW_SHOWNORMAL
call MsgLoop
ret
Main endp

; #########################################################################
RegisterWinClass proc lpWndProc:DWORD, lpClassName:DWORD,
Icon:DWORD, Cursor:DWORD, bColor:DWORD
local wc:WNDCLASSEX

mov wc.cbSize, sizeof WNDCLASSEX
mov wc.style, CS_BYTEALIGNCLIENT or \
CS_BYTEALIGNWINDOW
m2m wc.lpfnWndProc, lpWndProc
mov wc.cbClsExtra, NULL
mov wc.cbWndExtra, NULL
m2m wc.hInstance, hInstance
m2m wc.hbrBackground, bColor
mov wc.lpszMenuName, NULL
m2m wc.lpszClassName, lpClassName
m2m wc.hIcon, Icon
m2m wc.hCursor, Cursor
m2m wc.hIconSm, Icon
invoke RegisterClassEx, ADDR wc
ret
RegisterWinClass endp

; ########################################################################

.data
fff FLOAT 0.0f
.code
MsgLoop proc
LOCAL msg:MSG

@@:
invoke PeekMessage,addr msg,NULL,0,-1,PM_NOREMOVE
.if eax==0 ;if its zero, no messages are waiting
invoke CheckDeviceIsGoodToRender
.if OkToRender==TRUE
invoke Render, hWnd, 0FFh
.endif
.else ;if WM's are waiting...
invoke GetMessage,ADDR msg,NULL,0,0
.if eax==0 ;if its zero, its time to die !!
jmp @F
.else
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
invoke CheckDeviceIsGoodToRender
.if OkToRender==TRUE
invoke Render, hWnd, 0FFh
.endif
.endif
.endif
jmp @B
@@:

mov eax, msg.wParam
ret

MsgLoop endp

; #########################################################################

WndProc proc hWin,uMsg,wParam,lParam :DWORD

LOCAL var,caW,caH :DWORD
LOCAL Rct :RECT
LOCAL buffer1,buffer2[128]:BYTE ; these are two spare buffers
LOCAL szDropFileName[260]:BYTE
LOCAL vDist:D3DXVECTOR3
local localColor1:D3DXCOLOR
local localColor2:D3DXCOLOR
local floctemp:FLOAT
local rc:RECT


.if uMsg == WM_SYSCOLORCHANGE

.elseif uMsg == WM_SIZE

;-----------------------------------------------------------------------------------------------------------------------------
.elseif uMsg==WM_CHAR
mov eax,wParam
.if al==" " && AppState==GS_RUNNING
mov AppState,GS_MENU
.elseif al==0Dh && AppState==GS_MENU
.if MenuSelected==1
mov MenuState,GM_VIDEO
.elseif MenuSelected==2
mov MenuState,GM_AUDIO
.elseif MenuSelected==3
mov MenuState,GM_GAME
.elseif MenuSelected==4
.if FadingMenu==FALSE
mov FadingMenu,TRUE
invoke DXUtil_Timer,TIMER_GETAPPTIME
fstp fTimeFadeBegan
mov AppState,GS_RUNNING
.endif
.endif

.elseif al==1Bh ;ESCAPE KEY
.if AppState==GS_RUNNING
jmp dienow
.else
mov MenuState,GM_MAIN
mov AppState,GS_RUNNING
.endif
.endif

.elseif uMsg==WM_KEYDOWN && AppState==GS_MENU
mov eax,wParam
.if MenuState==GM_MAIN
.if eax==VK_DOWN
.if MenuSelected<4
inc MenuSelected
.endif
.elseif eax==VK_UP
.if MenuSelected>1
dec MenuSelected
.endif
.endif
.elseif MenuState==GM_VIDEO
.if eax==VK_DOWN
mov eax,ModesCounter
.if MenuSelected<eax
inc MenuSelected
.endif
.elseif eax==VK_UP
.if MenuSelected>1
dec MenuSelected
.endif
.endif
.endif

.elseif uMsg==WM_KEYDOWN && AppState==GS_RUNNING
mov eax,wParam
.if eax==VK_UP
fld fPlayerSpeed
fistp fptemp
mov eax,fptemp
.if eax>100
fld fMaxSpeed
fstp fPlayerSpeed
.elseif eax==0
fld fp0pt1
fstp fPlayerSpeed
.endif
fld fPlayerSpeed
fmul fp1pt04
fstp fPlayerSpeed
@@:
.elseif eax==VK_DOWN
fld fPlayerSpeed
fsub fp0pt04
fstp fPlayerSpeed
.endif

;-----------------------------------------------------------------------------------------------------------------------------
;-----------------------------------------------------------------------------------------------------------------------------
.elseif uMsg==WM_MOUSEMOVE
.if AppState==GS_RUNNING && RightButtonState==FALSE ;(if the user has not got rightbutton down)
; invoke HandleMouseMove
.endif

;-----------------------------------------------------------------------------------------------------------------------------
.elseif uMsg==WM_LBUTTONUP
mov DisablePickUntilDickWadReleasesTheButton,FALSE
mov DickWadHasDepressedButton,FALSE
invoke ReleaseCapture
return NULL
;-----------------------------------------------------------------------------------------------------------------------------
.elseif uMsg==WM_LBUTTONDOWN
.if AppState==GS_MENU
.if MenuState==GM_MAIN
.if (MouseY>155 && MouseY<174)
.if (MouseX>310 && MouseX<488)
mov MenuSelected,1
mov MenuState,GM_VIDEO
.endif
.elseif (MouseY>185 && MouseY<206)
.if (MouseX>310 && MouseX<488)
mov MenuSelected,2
mov MenuState,GM_AUDIO
.endif
.elseif MouseY>216 && MouseY<237
.if MouseX>229 && MouseX<484
mov MenuSelected,3
mov MenuState,GM_GAME
.endif
.elseif MouseY>248 && MouseY<267
.if MouseX>373 && MouseX<426
mov MenuSelected,4
mov FadingMenu,TRUE
invoke DXUtil_Timer,TIMER_GETAPPTIME
fstp fTimeFadeBegan
mov AppState,GS_RUNNING
.endif
.endif

.elseif MenuState==GM_VIDEO
mov eax,LastV2 ;Render sets these for us
mov ebx,LastV1
.if (MouseY>ebx && MouseY<eax)
.if (MouseX>273 && MouseX<527)
mov AppState,GS_RUNNING
.endif
.endif
.endif

.elseif AppState==GS_RUNNING
invoke SetCapture,hWin ;Our window is to capture mouse activity
mov DickWadHasDepressedButton,TRUE ;this flag triggers the "PickRay" code
return NULL
.endif
;-----------------------------------------------------------------------------------------------------------------------------
.elseif uMsg==WM_RBUTTONDOWN ;Begin "DRAGMODE"
mov RightButtonState,TRUE
invoke GetCapture
.if eax != NULL
invoke Pick_ScreenToVector, hWin, addr vPickRayOrig, addr vPickRayDir
.endif

m2m MouseX_DragBegan,MouseX
m2m MouseY_DragBegan,MouseY

;-----------------------------------------------------------------------------------------------------------------------------
.elseif uMsg==WM_RBUTTONUP
mov RightButtonState,FALSE

;-----------------------------------------------------------------------------------------------------------------------------
.elseif uMsg == WM_PAINT
invoke Paint_Proc,hWin
return 0

.elseif uMsg == WM_CLOSE

.elseif uMsg == WM_DESTROY
dienow:
invoke DXUtil_Timer, TIMER_STOP
fstp fTime
invoke D3DCleanup
invoke PostQuitMessage,NULL
return 0

.elseif uMsg==WM_CREATE
invoke GetClientRect,hWin,addr rc
mov eax,rc.right
sub eax,rc.left
shr eax,1
mov ebx,rc.bottom
sub ebx,rc.top
shr ebx,1
mov MouseX_Centre,eax
mov MouseY_Centre,ebx
invoke SetCursorPos,eax,ebx

.if $invoke (InitD3D,hWin)==S_OK
invoke DXUtil_Timer, TIMER_START
fstp fTime
;----------------------------------------------------------------------------------------------------------------------------------------------------
;This snippet was used to test the LinkedList codebase
; ;----------------------------------------------------------------------------------------------------------------------------------------------------
; mov pSkins,$invoke (LinkedLists_AppendSibling,NULL,NULL,sizeof Object)
; push eax
; invoke LinkedObjects_SetName,eax,CTXT("heh")
; invoke LinkedLists_AppendSibling,eax,NULL,sizeof Object
; invoke LinkedObjects_SetName,eax,CTXT("child1")
; pop eax
; invoke LinkedLists_AppendSibling,eax,NULL,sizeof Object
; invoke LinkedObjects_SetName,eax,CTXT("child2")
; invoke saferelease_LL, pSkins
; mov pSkins,$invoke (LinkedLists_AppendSibling,NULL,NULL,sizeof Object)
; push eax
; invoke LinkedObjects_SetName,eax,CTXT("heh")
; invoke LinkedLists_AppendSibling,eax,NULL,sizeof Object
; invoke LinkedObjects_SetName,eax,CTXT("child1")
; pop eax
; invoke LinkedLists_AppendSibling,eax,NULL,sizeof Object
; invoke LinkedObjects_SetName,eax,CTXT("child2")
; invoke saferelease_LL, pSk
;----------------------------------------------------------------------------------------------------------------------------------------------------

; mov hLoaderThread,$invoke (CreateThread,NULL,NULL,addr LoaderThread,0,NULL,addr LoaderThreadID)
mov pSkins,$invoke (SkinMesh_Create,CTXT("C:\masm32\directxex\xfile\tiny.x"))


.else
invoke MessageBox,0,CTXT("Failed to initialize Direct3D",13,10,"I'm out of here !!"),CTXT("Critical Error !!"),MB_OK+MB_ICONERROR
jmp dienow
.endif
.endif
;----------------------------------------------------------------------------------------------------------------------------------------------------
invoke DefWindowProc,hWin,uMsg,wParam,lParam
ret

WndProc endp

; ########################################################################
TopXY proc wDim:DWORD, sDim:DWORD
shr sDim, 1 ; divide screen dimension by 2
shr wDim, 1 ; divide window dimension by 2
mov eax, wDim ; copy window dimension into eax
sub sDim, eax ; sub half win dimension from half screen dimension
return sDim
TopXY endp
; #########################################################################
Paint_Proc proc hWin:DWORD
LOCAL hDC,btn_hi,btn_lo:DWORD
LOCAL Rct :RECT
LOCAL Ps :PAINTSTRUCT
mov hDC, $invoke (BeginPaint, hWin, addr Ps)
mov btn_hi, $invoke (GetSysColor,COLOR_BTNHIGHLIGHT)
mov btn_lo, $invoke (GetSysColor,COLOR_BTNSHADOW)
return $invoke (EndPaint,hWin,ADDR Ps)
Paint_Proc endp
; ########################################################################

end start

:alright: :alright: :alright:

ok here's the Project.inc file which is basically holds all our named variables and general application data...


LPOBJECT typedef ptr Object
; #########################################################################

; api include files
; ~~~~~~~~~~~~~
include c:\masm32\include\windows.inc
include c:\masm32\include\masm32.inc
include c:\masm32\include\winmm.inc
include c:\masm32\include\gdi32.inc
include c:\masm32\include\user32.inc
include c:\masm32\include\kernel32.inc
include c:\masm32\include\Comctl32.inc
include c:\masm32\include\comdlg32.inc
include c:\masm32\include\shell32.inc
include c:\masm32\include\oleaut32.inc
include c:\masm32\include\advapi32.inc

; api libraries
; ~~~~~~~~~
includelib c:\masm32\lib\masm32.lib
includelib c:\masm32\lib\winmm.lib
includelib c:\masm32\lib\gdi32.lib
includelib c:\masm32\lib\user32.lib
includelib c:\masm32\lib\kernel32.lib
includelib c:\masm32\lib\Comctl32.lib
includelib c:\masm32\lib\comdlg32.lib
includelib c:\masm32\lib\shell32.lib
includelib c:\masm32\lib\oleaut32.lib
includelib c:\masm32\lib\advapi32.lib




; D3D CRAP
;==========
include c:\masm32\include\d3d8types.inc
include c:\masm32\include\d3dx8.inc
includelib c:\masm32\lib\d3dx8.lib
includelib c:\masm32\lib\d3d8.lib
includelib c:\masm32\lib\d3dx8d.lib
include c:\masm32\include\dxfile.inc
includelib c:\masm32\lib\d3dxof.lib
includelib c:\masm32\lib\dxguid.lib
include c:\masm32\include\d3dx8.inc
include c:\masm32\dx81include\d3dx8math.inc
include c:\masm32\include\d3dx8math_fkt.def
includelib c:\masm32\lib\msvcrt.lib
include c:\masm32\include\ole32.inc
includelib c:\masm32\lib\ole32.lib
include c:\masm32\include\winextra.def

;Any objects to be stored in LinkedLists should begin with the following data structure...
;#########################################################################
LPLINKEDOBJECT typedef ptr LinkedObjectHeader
LinkedObjectHeader STRUCT
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
hName DWORD ? ;Handle to the name of this linkedobject
hChild LPLINKEDOBJECT ? ;Handle to my child if I have one
hSibling LPLINKEDOBJECT ? ;Handle to my younger sibling if I have one
LinkedObjectHeader ENDS
LinkedObject typedef LinkedObjectHeader
;#########################################################################
LPOBJECT typedef ptr Object
Object STRUCT
LO LinkedObject <> ;LinkedList support structure
ObjectType DWORD ? ;We just might wanna know this if we have more than 1 kind
bSelected DWORD FALSE
dwNumMaterials DWORD ?
vLocation D3DVECTOR <> ;where in 3D space this object is currently located
fYaw FLOAT ? ;Three angles = the 3D rotation (orientation) of the object
fPitch FLOAT ?
fRoll FLOAT ?
vCenter D3DVECTOR <> ;Vector from ObjectOrigin to "Best Center"
fRadius FLOAT ? ;Radius of Object Bounding Sphere
matLocal D3DXMATRIX <> ;will be used to manipulate the object according to the above info
pMesh DWORD ? ;Pointer to this object's Mesh
pMeshMaterials DWORD ? ;Pointer to array of D3DMATERIAL8 structs
pMeshTextures DWORD ? ;Pointer to array of pTexture dwords
Object ENDS
;#########################################################################

; #########################################################################

;=====================
; Some crappy prototypes
;=====================
WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD
WndProc PROTO :DWORD,:DWORD,:DWORD,:DWORD
TopXY PROTO :DWORD,:DWORD
Paint_Proc PROTO :DWORD
RegisterWinClass PROTO :DWORD,:DWORD,:DWORD,:DWORD,:DWORD
MsgLoop PROTO
Main PROTO
InitFonts PROTO :DWORD
Create3DTextObject PROTO :DWORD, :DWORD, :DWORD, :DWORD
Make3DText PROTO :DWORD, :DWORD, :DWORD, :DWORD, :DWORD, :DWORD,:DWORD,:DWORD
EnumDisplayModes PROTO
wsprintfA PROTO C :DWORD,:VARARG
wsprintf equ <wsprintfA>



;=====================
; Some crappy macros
;=====================
include macros.inc

; --------------------------------------------
; equates to use directly in the message loop
; --------------------------------------------
m_hWnd equ <msg.hwnd>
m_Msg equ <msg.message>
m_wParam equ <msg.wParam>
m_lParam equ <msg.lParam>

.data
szDisplayName db "Directxex",0
szFont db 'Comic Sans MS',0
AppState dd GS_SPLASH
MenuState dd GM_MAIN
MenuSelected dd 1
pDisplayModesRoot dd NULL ;LinkedList Root Pointers
pLLObjectsRoot dd NULL
hPlayerObject dd NULL ;handle to Root of PlayerObjects (first Player=Local Player)
pExtended dd NULL ;Temp pointer to extended page
lpD3D dd NULL
lpd3dDevice dd NULL
OkToRender dd FALSE
fTime FLOAT 0.0f
ModesCounter dd NULL ;#Video Modes
PlayerName db 256 dup(0)
DickWadHasDepressedButton dd FALSE
DisablePickUntilDickWadReleasesTheButton dd FALSE
FadingMenu dd FALSE
fTimeFadeBegan FLOAT 0.0f
LoadingText db 256 dup(0)

.data?
hInstance dd ?
CommandLine dd ?
hIcon dd ?
hCursor dd ?
sWid dd ?
sHgt dd ?
hWnd dd ?
dwNumOfVertices dd ?
dwNumOfFaces dd ?
FaceIndex dd ? ;Index of Hit Face for MousePick
vPickRayDir D3DXVECTOR3 <>
vPickRayOrig D3DXVECTOR3 <>
pFontLarge dd ?
lp3DFont dd ?
lp3DFontUL dd ?
MouseX dd ?
MouseY dd ?
MouseX_Centre dd ?
MouseY_Centre dd ?
MouseX_DragBegan dd ?
MouseY_DragBegan dd ?
LastV1 dd ? ;Vertical Position - Beginning
LastV2 dd ? ;Vertical Position - End
matIdentity D3DXMATRIX <>
matView D3DMATRIX <>
matProj D3DMATRIX <>

;#########################################################################
;We couldn't define this till now becuz we needed d3dx8.inc for D3DXVECTOR3...
;Nevermind, we'll do it now - no harm done :)
;#########################################################################
; A structure for our custom vertex type
CUSTOMVERTEX STRUCT
x FLOAT ?
y FLOAT ?
z FLOAT ?
color DWORD ?
u FLOAT ?
v FLOAT ?
CUSTOMVERTEX ENDS
sizofCUSTOMVERTEX EQU (sizeof CUSTOMVERTEX)

D3DVERTEX struct DWORD
position D3DXVECTOR3 <> ; // The position
normal D3DXVECTOR3 <> ; // The normal
tu FLOAT 0.0f ; // The texture coordinates
tv FLOAT 0.0f ; // The texture coordinates
D3DVERTEX ENDS
sizeofD3DVERTEX EQU (sizeof D3DVERTEX)

;This FVF is used for drawing 2D Textured Quads ("billboards")

TEXQUADVERTEX struct
position D3DXVECTOR3 <> ; // The position
color D3DCOLOR NULL ; // The color
tu FLOAT 0.0f ; // The texture coordinates
tv FLOAT 0.0f ; // The texture coordinates
TEXQUADVERTEX ends

;D3DFVF_TEXQUADVERTEX equ D3DFVF_XYZRHW or D3DFVF_DIFFUSE or D3DFVF_TEX1
D3DFVF_TEXQUADVERTEX equ D3DFVF_XYZ or D3DFVF_DIFFUSE or D3DFVF_TEX1

;#########################################################################
; Our custom FVF, which describes our custom vertex structure
D3DFVF_CUSTOMVERTEX EQU (D3DFVF_DIFFUSE OR D3DFVF_XYZ OR D3DFVF_NORMAL)
D3DFVF_VERTEX EQU (D3DFVF_XYZ OR D3DFVF_NORMAL OR D3DFVF_TEX1)

;#########################################################################

;Floating Point constants we wanna use, with nice simple naming convention.
.data
RightButtonState dd FALSE

fPlayerSpeed FLOAT 0.0f
fMaxSpeed FLOAT 100.0f
fp0 FLOAT 0.0f
fp0pt999 FLOAT 0.999f
fp0pt04 FLOAT 0.040f
fp0pt1 FLOAT 0.10f
fp0pt2 FLOAT 0.20f
fpsmall FLOAT 0.10f
fp0pt4 FLOAT 0.40f
fphalf FLOAT 0.50f
fp1 FLOAT 1.0f
fp1pt04 FLOAT 1.040f
fp1pt8 FLOAT 1.8f
fp2 FLOAT 2.0f
fp3 FLOAT 3.0f
fp4 FLOAT 4.0f
fp5 FLOAT 5.0f
fp10 FLOAT 10.0f
fp30 FLOAT 30.0f
fp45 FLOAT 45.0f
fpn10 REAL4 -10.0f
fp100 FLOAT 20.0f
fp255 FLOAT 255.0f
fp300 FLOAT 300.0f
fp360 FLOAT 360.0f
fp361 FLOAT 361.0f
fp400 FLOAT 400.0f
fp600 FLOAT 600.0f
fp800 FLOAT 800.0f
fp1000 FLOAT 1000.0f
fDisplayCamPitch FLOAT 0.0f
fDisplayCamYaw FLOAT 0.0f

;#########################################################################


Ok, I'll post some more of it in a bit, in Zips as requested.
That gives you something to look at in the interim.

Have a nice day :)
Posted on 2003-10-19 23:26:37 by Homer
(hey - I think that might be the longest post I ever made here :tongue: )
I had something to say, but I forgot - too much beer makes Homer a dull Homer...
Posted on 2003-10-20 12:27:39 by Homer
I just wanna make a note here for my own sanity's sake...
SkinMesh_Create is our entrypoint to the Frames parser.
The Frames parser in turn will call MeshNode_Create whose job is to load the Mesh and process any Materials/Textures in there.
Having done all that, we find ourselves back in SkinMesh_Create, where we have one last job to do - "find the Bones of our MeshNode"...

SkinMesh_Create calls FrameNode_FindBones, passing it a handle to the Root Frame of our SkinMesh.
FrameNode_FindBones attempts to find any Frames which own a pMeshes field (no its not a pointer, my naming convention needs fixing since I changed the LinkedList codebase) and for any Frame that contains a pMeshes field, it calls MeshNode_FindBones.

MeshNode_FindBones, having been handed a handle to a MeshNode, attempts to gain access to that meshnode's BoneNamesBuffer. Having done so, for every named Bone, it attempts to find the Frame of the same name, and then fetch a pointer to that frame's CombinedMatrix and copy it into an array of "pointers to Matrices" kept in the owner MeshNode under the handle pBoneMatrices.
What we are really doing is constructing in the MeshNode an array of matrixpointers, one per Bone.

Anyone got a square to spare?

This all seems a little e-tarded to me.
For starters, MeshNode is meant to be generic, how can it be if it supports an array of bonematrix pointers? It seems more logical to place this array in the owner SkinMesh, rather than in some far-flung meshnode of that SkinMesh, since the number of Bones won't be changing.
Furthermore, it seems crass to parse the BoneNamesBuffer of the MeshNode since we already HAVE the BoneNames in our Frames Hierarchy.
It occurs to me that this all could have been done DURING FRAMEPARSING ie when we find a Frame Transform Matrix we ideally should whack a pointer to it into our owner SKinMesh's BoneMatrix Pointers array, in the order of discovery during parsing. Of course, we are assuming that the Bone Names in the MeshNode's Bone Names Buffer are in the SAME ORDER OF APPEARANCE as the Frames of the same name. This seems to be the case.

Your thoughts?
Posted on 2003-10-21 09:25:37 by Homer
Hey Homer

This is the way I'd look at it :-

The skinmesh object encapsulates our model skin.
The model skin can be made up of 1 or more meshes.
Each mesh can be animated by 1 or more bones.

It depends on what you want to do with the skin as to where you store the bone information.

I want to be able to create models like lego, plug in an arm here, a leg there, a modular system.
- I want to chop off some dudes arm and beat him with it :grin:
- I want to blow off a leg and replace it with a bloody stump :eek:
- I want to drink a potion and have my arm change into a tenticle :rolleyes:

This would require the mesh to store its bone and animation information, but I could be completely wrong.

Maelstrom
Posted on 2003-10-21 17:23:16 by Maelstrom
Please refer to my old Subdivided SkinMesh thread.
Here's a link to it
Basically a SkinMesh cannot (should not) contain more than one Mesh.
Frames on the other hand are quite capable of this, in the context of a scene hierarchy - note that I have NEVER seen an xfile for a skinmesh model that contained more than one mesh, but I have placed multiple single-mesh models in a scene and saved the entire scene to an xfile, but our Parser isn't smart enough to handle that yet, and I don't intend to try loading scenefiles with the skinmesh loader, I'll use some of the code in there through a different entrypoint if I wish to implement a full blooded scene loader. It really makes little sense to put all your eggs in one basket anyway, far better to keep things modular, even as far as xfiles are concerned.
Meshes are capable of being designed with multiple Materials, but normally nobody bothers, they generally just use a single texture. The mechanism for multiple Materials allows us to rend a SkinMesh in SubSets. Should we take the time to create our models with multiple Materials, and use one Material per limb that we'd like to tear off, we can then flag our Render function to simply not draw some of the subsets, which would be an issue if we did not also create some hidden (occluded) surfaces - "stump surfaces". Ok, so we can draw our dude with arms and legs and stuff missing, what about splitting them from his body?
Imagine we ripped off an arm, and want to draw it on the ground at the feet of the model. We simply create a second instance of the entire model (subsequent instances are cheap - it's just a reference) and flag all its limbs EXCEPT the arm to be skipped during Rending, and we attach this second instance of the model to its own Frame, so it can be transformed separately.
This way, we can still animate the arm all by itself if we like, and can cheaply do things like chop someone in half.

In conclusion, this proposed system of Subdivided SkinMeshes supports stump texturing, allows for twitching, wriggling limbs on the ground, is cheap to implement, allows us to stick to highlevel render functions, and gives us a reason to implement proper Instancing of our loaded models which in turn leads to polymorphism (think about a crowd of people, all animated separately and textured separately, but all instances of a single loaded SkinMesh).
Posted on 2003-10-22 01:13:17 by Homer
On a side note, I added a quicky codemodule called Debug (oddly enough) which consists of a couple of procs to create, write to and close a logfile, and a macro to drive calls to the write function.
About time I got my act together, really.
Spurious MessageBoxen being replaced judiciously with new Debug macro.
I was getting sick of commenting and uncommenting them all the time.
Also I'm considering altering the Linked Objects support code to use Heap objects instead of using GlobalAlloc, with a gut feeling that I can get rid of all the zillions of Lock and Unlock calls that have crept in since I last reworked it.
This may also grow into a Paged Object Manager, where object heaps are separate and are grown on demand by a MemoryManager class - your thoughts?
Posted on 2003-10-22 01:29:30 by Homer
Hey Homer

Ahh I forgot about that thread, now I remember where your going with the skinmesh.
Doing it that way would definitely be faster to render than dozens of smaller meshes.
The only drawback is that you can't change the mesh, replace an arm with a tenticle etc, but this isn't a major and could probably be worked around anyway.
I was also wondering about how a severed limb would react since it's part of the whole mesh but it should work ok.
Will be interesting to see in action.

Am I correct in assuming the mesh vertices/faces/indices will be grouped by material?
What about bounding boxes, 1 per material group?
You could use the material to determine regional impact correct? find the impacted face and check its material = instant region?

If your going to have a single mesh then I agree with your earlier thoughts about moving the bones information into the skinmesh structure.

Moving to heap memory functions is a good idea, even MS suggests using them since the Global/Local functions are now obsolete.
A memory manager is always a good idea IMHO, allows you to easily track every byte of memory you use.

Maelstrom
Posted on 2003-10-22 02:27:05 by Maelstrom
My project is very broken again lol (implementing Heap based memory model)

;October 22- Support Macros were added for creating, growing and destroying HEAPS.
; - SkinMesh Heap is being created and destroyed in Main procedure,
; created before the window is created, and destroyed when we return from the MessagePump loop.
; - We also create/destroy a heap for storing all Strings in Main procedure.
; - LinkedList functions "SetName", "KillName" and "SafeRelease_LL" were modified for Heap memory

When you call a DX mesh loading function, it always sorts the faces into per-Material collections,if that mesh contain multiple Materials. Handy, huh?
Well, m$ make little mention of this fact, and not many appreciate that you can do a bit more than just render texture groups with SubSet rendering functions.
I'm going to aim for a single-tex, single-material skinmesh first, then try for multi-tex (multi-Material) version, which is just a breath away from my notion of SubDivided SkinMesh.

For the record, here are the new macros I'm using to implement Heaps and heap-based Objects...

HeapObject MACRO hHeap, pReturn, objSize
mov pReturn,$invoke (HeapAlloc,hHeap,HEAP_NO_SERIALIZE or HEAP_ZERO_MEMORY or HEAP_GENERATE_EXCEPTIONS,objSize)
ENDM

ResizeObject MACRO hHeap,pHeap, newSize
mov pHeap, $invoke (HeapReAlloc,hHeap, HEAP_NO_SERIALIZE or HEAP_ZERO_MEMORY or HEAP_GENERATE_EXCEPTIONS, pHeap, newSize)
ENDM

FreeObject MACRO hHeap, pObject
invoke HeapFree,hHeap,HEAP_NO_SERIALIZE,pObject
ENDM

CreateHeap MACRO hHeap, pHeap, initialSize
mov hHeap,$invoke (HeapCreate, HEAP_NO_SERIALIZE or HEAP_GENERATE_EXCEPTIONS,initialSize,0)
HeapObject hHeap, pHeap, initialSize
ENDM

DestroyHeap MACRO hHeap, pHeap
FreeObject hHeap, pHeap
invoke HeapDestroy,hHeap
ENDM

Damn, I hope this works out, lol.
I understand that internally, these functions rely on kernel-based CriticalSection functions, which has to be cheaper than Locking and Unlocking allocated memory.
Also I am led to believe there's a minor transitory bug in m$'s GlobalAlloc function where it sometimes fails to access the process heap. In English, that means that it wigs out when it feels like it, and without warning (possibly a timing glitch in the Kernel itself??) I'm told this method is more stable and easier to trace.

As for Bounding geometry, I intend to firstly use a BoundingSphere around the entire model, and use BoundingBoxes around vertex groupings.
Of course, we nominally have to manually precalculate the latter by Locking the vertexbuffer.
This is made much easier if we have loaded the SkinMesh model already, since we can use the BoneCombination information to determine which vertices belong to (are influenced by) a given Bone. I expect MaxInfluences to be greater than one (Vertices affected by more than one Bone), which means it makes sense to transform the BoundingBoxes whenever we transform Bones, so we may as well have one BoundingBox per Bone, thus they will actually overlap.
Conclusion is that we should have one BoundingBox per Bone, and manipulate them at the same time, meaning BoundingBoxes are distorted during animation.
What good are they then, you might be wondering?
Well, if we wanted to know if a point was within a distorted box, we distort the point to suit the box. Same for a ray. Am I making sense or rambling here? lol
Posted on 2003-10-22 04:43:14 by Homer
I really must stress the point that I don't intend to implement SubDiv until I have a basic animated model doing its thing on my screen.
Except fot the fact that I'm currently recoding LinkedObject support for the fourth time to support Heap mem, the battleplan is as follows:

-Get SkinMesh Loader's FramesParser working (completed)
-Get SkinMesh Loader's MeshLoader working (completed)
-Get SkinMesh Loader's AnimationsParser working (95% complete)
-Implement static Rending of the SkinMesh (not even close)
-Implement Time-Based animation code (matrix applicator, haven't really started)
-Implement Time-Based animation code (animated Render, haven't really started)
-Implement multiple named AnimationSets in a single model's xfile (not attempted)
-Implement Hit-Detection (Model with Terrain, unimplemented)
-Implement Hit-Detection (Model with Ray, unimplemented)
-Implement Hit-Detection (Model with ParticleMesh, unimplemented)
-Implement Hit-Detection (Model with Model, unimplemented)

It looks like I have a lot of work left, doesn't it?
In actuality I'm well over 60% towards completion :)
Posted on 2003-10-22 05:56:34 by Homer
Hey, I'm going to have to start a separate thread to talk about related topics like my Organic Algorithm-based hierarchical vegetation !! (I got that idea from Vice City, the palmtrees sway in the wind, looks cool...)
I intend to populate the terrain with trees, shrubs and other plants which grow and die over Time, thus the landscape will constantly evolve.
I plan on using the structures and code developed for SkinMesh to achieve it, but animate them myself using a simple matrix hierarchy.
I'm considering applying the same logic to SkinMeshes themselves, altering the Scale over time so that the animated creatures grow too.
Posted on 2003-10-22 06:05:31 by Homer
Did I mention that speed was like REALLY important to me?
I want a world that is complex and engaging !!
Posted on 2003-10-22 09:11:44 by Homer
October 23-functions "LinkedLists_Append", "AppendSibling", "FindEntryByName" were modified for Heap memory,
also "SafeRelease_PlayerObject" , "Object_LoadMeshFromXFile" , "SafeRelease_TexObject" , "SafeRelease_ObjectLL" , "AnimationNode_Load , _Destroy , _FindBone , _SetTime" and lots more

(I'm tired! Can you tell?)


What I've decided to do is implement several Heaps, not just one big whopping Heap. Later this will become the basis of a Paged memory manager, because each Heap will be comprised of N objects of a fixed size.
Therefore, the Manager Class functions are being altered to expect a Heap Handle and an Object Pointer.
We'll put all our chickens with our chickens, and our ducks with our ducks.
Birds of a Feather !!

The Manager functions have been placed in a separate folder now, which tidies up the Include folder a heck of a lot.

Give me another day, maybe two, and I'll be back where we were before, with a more robust and methodical Object Management Class of functions which supports polymorphism. Note that this OOP implementation is NOT following the standard, it's not REALLY a "Class" in the C sense, since my Objects do not contain virtual function pointers or anything sexy like that, they are just LinkedList-style objects and the set of Manager functions is external and global, but supports polymorphism through the use of a variable Heap handle.
Posted on 2003-10-23 09:28:13 by Homer
Just for the sake of making a progress report (and keeping up my post ratio lol) here's the current state of my EntryPoint code block ...


.code
start:
call TRandomInit
invoke Debug_CreateLogFile, CTXT("DEBUG_LOG.TXT")
mov hInstance, $invoke (GetModuleHandle,NULL)
mov CommandLine, $invoke (GetCommandLine)
mov hIcon, $invoke (LoadIcon,hInstance,500) ; icon ID
mov hCursor, $invoke (LoadCursor,NULL,IDC_ARROW)
mov sWid, $invoke (GetSystemMetrics,SM_CXSCREEN)
mov sHgt, $invoke (GetSystemMetrics,SM_CYSCREEN)
call Main
invoke Debug_CloseLogFile
invoke ExitProcess,eax
invoke InitCommonControls

; #########################################################################

Main proc
STRING MyClass,"More_Ass_Than_Class"
SingleInstanceOnly addr MyClass
invoke RegisterWinClass,ADDR WndProc,addr MyClass,
hIcon,hCursor,COLOR_BTNFACE+1
CreateHeap hHeapOfPlayers, pPlayers, sizeof PlayerObject ;Create Heap for Players (who knows...maybe MP)
CreateHeap hSkinMeshHeap, pSkins, sizeof SkinMesh ;Create Heap for SkinMesh
CreateHeap hHeapOfStrings, pStringsMemory, 0 ;Create Heap for Strings
CreateHeap hMeshNodeHeap, pMeshNodes,sizeof MeshNode ;Create Heap for MeshNodes
CreateHeap hFrameNodeHeap, pFrameNodes, sizeof FrameNode ;Create Heap for FrameNodes
CreateHeap hAnimNodeHeap, pAnimationNodes, sizeof AnimationNode ; " " AnimationNodes
CreateHeap hHeapOfObjects, pObjectNodes, sizeof Object ;blah
CreateHeap hHeapOfDisplayModes, pDisplayModesRoot, 0
Debug CTXT("Heaps Created.",13,10)
invoke CreateWindowEx,NULL,addr MyClass,ADDR szDisplayName,
WS_OVERLAPPED or WS_SYSMENU,
0,0,sWid,sHgt,
NULL,NULL,hInstance,NULL
mov hWnd,eax
DisplayWindow hWnd,SW_SHOWNORMAL
call MsgLoop
DestroyHeap hHeapOfDisplayModes, pDisplayModesRoot
DestroyHeap hHeapOfObjects, pObjectNodes
DestroyHeap hAnimNodeHeap, pAnimationNodes
DestroyHeap hFrameNodeHeap, pFrameNodes
DestroyHeap hMeshNodeHeap, pMeshNodes
DestroyHeap hHeapOfStrings, pStringsMemory
DestroyHeap hSkinMeshHeap,pSkins
DestroyHeap hHeapOfPlayers, pPlayers
ret
Main endp
Posted on 2003-10-25 07:40:21 by Homer