This update includes all the changes I've outlined.
One mesh is loaded from one xfile as a test.
We don't create or render any instances of it yet, however code has been included for these functions.

Let's just clarify what's going on...
D3DApp owns the ResourceManager (formerly the TextureManager).
All ResourceManager functionality is available to D3DApp and to any class which inherits from D3DApp.
Our D3DGame instance inherits from D3DApp, so we can say that D3DGame owns ResourceManager.

ResourceManager is responsible for loading and unloading filebased resources (currently Textures and Meshes), which it stores in two collections. One contains Texture objects, the other contains D3DMesh objects.

D3DMesh objects are REFERENCE objects representing a loaded mesh.
Each D3DMesh object owns a collection of D3DMeshInstance objects, ie, a Reference object is responsible for managing its own Instances.

Does that sound reasonable? :)

There's one particular aspect of the latest code additions which I'd like to point out.
We never have to unload D3DMesh objects !!!
Since they are managed by ResourceManager, and since ResourceManager ITSELF is cleaned up via our GameUnloadVolatiles callback, we can totally forget about cleaning up Meshes :)

Attachments:
Posted on 2006-07-28 21:23:49 by Homer
Ultrano - see the D3DMesh.inc in Tutorial#24..
I've used a simple struct to define D3DMeshInstance  (a runtime instance of a reference mesh, with stuff like position and orientation).
I've defined the D3DMesh reference meshes as an Object which owns the pMesh, but which also owns and manages a collection of D3DMeshInstance.
Finally, the D3DMesh objects are managed in a similar collection by ResourceManager.

So - we ask ResourceManager to load an xfile, it will check whether that file is already loaded, and if not, it will load it into a new D3DMesh object... D3DMesh represents a loaded Reference object.
Then we can ask a D3DMesh object to add one or more instances to its collection of D3DMeshInstance structs.
D3DMeshInstance represents a Reference to a D3DMesh object, and are grouped under that particular D3DMesh.

Note that the meshloader uses ResourceManager's texture management code, so that mesh textures are universally shared like any other.

I have to agree with you regarding the untidy float declarations, I thought about implenting a macro for that, but I felt it would obscure what's going on.. and decided to leave such niceties for later.
Similarly, I usually use macros such as 'SafeRelease', 'SafeFree', etc, which I have omitted for the same reason.

Posted on 2006-07-28 21:37:04 by Homer
Another day, another update :)

This time we're actually creating an Instance of our demo mesh and Rendering all Instances (we only made one).

Note that we could create a million instances of the mesh , at random locations in the World, IF we wanted to.

I've disabled the rendering of the Sphere, and placed once instance of Tiger.x where the Sphere used to be, and modified the Camera view somewhat so that you can more easily see stuff.

Several minor bugs were identified in D3DMesh during the implementation of Instance rendering, and were fixed immediately.

When I first executed the bugfree code, I still could not see my Tiger, but I could see a speck of insect poo where the Tiger should be.
This bothered me - so I looked in the Tiger.x file (its plaintext ascii) at the values of its Vertices, and found that they were extremely small.
My response was to add a few more Methods to D3DMesh.
They are :

CalcBounds - measures the BoundingSphere of a mesh and returns a Radius value. A boundingsphere is the smallest theoretical sphere that surrounds a given object, we'll learn how to use them later.

ScaleMesh - applies a given Scaling factor to the mesh vertices.
Sure, we COULD do this with a Transformation matrix, but then we'd have to apply that matrix OFTEN... much better to scale the vertices ONCE, and forget about it.

ResizeMesh - calculates the scaling factor required in order to make our mesh a given Size, and applies it via ScaleMesh. Optionally, it can ALSO find the 'true center' of the mesh, and shift the vertices such that the 'true center' coincides with the mesh's local origin (ie, 'centralize' the mesh).

Having added those methods, I was then able to resize the Tiger mesh at loadtime so its the same size as our Boxes.

Yay, we've moved beyond 'primitive geometric entities' into the land of arbitrary meshes - now its time to create a suitable Player mesh.
Whichever program you use to create your Meshes is your affair.
I like MAYA, and that's what I'll use.

Hopefully in the next update, we'll look at our Player object, implement the Player mesh, and hook up some keyboard controls.

In closing, I'd like to point out that the Rotation values for the Mesh are to be given in RADIANS (not degrees).
That means the angles are somewhere between 0 and 2*PI (remember about 2*PI being a full circle?)
An angle of PI is 180 degrees, PI/2 is 90 degrees, etc.
Can you figure out how to convert degrees to radians, and radians to degrees?


Attachments:
Posted on 2006-07-29 02:30:41 by Homer
In D3DMesh.UpdateAllInstances, shouldn't we first rotate, then translate (order of multiplication of the matrices)?
If so, wouldn't a matrix-multiplication be skippable, like:

Matrix4* Matrix4_MakeModelMatrix(Matrix4* out,Vec6* ModelVec6){
short rx,ry,rz;
//-----[ rotate model ]--------------------[
rx = ModelVec6->rx & 1023;
ry = ModelVec6->ry & 1023;
rz = ModelVec6->rz & 1023;
if(rx | ry | rz)Matrix4_MakeRotHPB(out,rx,ry,rz);
else Matrix4_Unit(out);
//-----------------------------------------/
//----[ translate model ]------------------[
out->w.x+= ModelVec6->x;
out->w.y+= ModelVec6->y;
out->w.z+= ModelVec6->z;
//-----------------------------------------/
return out;
}

Posted on 2006-07-29 06:56:51 by Ultrano
Yes - the order of operations was DELIBERATELY wrong.
I hoped someone ELSE would pick up on it.
Not that I have any problem with you, mate... just that I wished for some beginner to notice it.
As for 'skipping' the operation, I've implemented a flag that should be set to TRUE whenever rotation and/or position changes for any Instance of any D3DMesh, this flag means 'I need to recalculate the combined matrix'.

Unless something changes, the matrix need not be recalculated.
Posted on 2006-07-29 18:24:25 by Homer
I'm actually a beginner at matrices, quaternions, and ... all DirectX/OpenGL coding :) . For 2 years, I've been fooling myself with simple code that doesn't use all 3 axis of rotation. Fortunately, my ignorance helped me speed-up my simple 2.5D commercial games, and I didn't experience gimbal-lock and bad rotation. At that time I was just pondering why the heck my light would play tricks at some angles ^^".
Posted on 2006-07-29 19:45:33 by Ultrano
Homer I have some spaceships and buildings modelled in .obj file format, I found dx meshconvert from sdk, but cant get it work to convert to .x files, I also found .x file documentation how it saves animations and want to find a way to contribute with orientalstyle swordfighting animations and other things to your work
I use Hexagon 2, shouldnt export terrain and such  in autocad binary format be best to use when it comes to things you want to manipulate more on lowlevel CUSTOMVERTEX level? its smaller as its not encoded in ascii as most 3dformats are and easier to readin

I'm actually a beginner at matrices, quaternions...

I think you speak for many of us are beginners at  quats/matrices and all asmcoders  love to make tinker with own code that makes very fast, but simplistic solutions
irony SSE was designed to be useful for quats and I instead made SSE code that made use of parallelism of simplistic rotation and RCPPS+MULPS for fast 3dtransform for 2 points simultanously  replaced 4 division Z ops

Posted on 2006-07-30 07:18:42 by daydreamer
In the attached update, we begin to make use of our 'World array object'. I've implemented a new method called 'World.LoadBrickMapFromMemory'.
It takes two params : a pointer to a list of TextureNames to be loaded by BrickManager, and a pointer to a 2D array of Bytes which represent the TextureID and Position of each Brick instance in a 'game level'.
The value 255 is reserved by the new Method to signify 'an empty cell' - ie 'no brick'... any other value identifies the index of one of the textures we loaded into BrickManager.
I've created a demo 'BrickMap' for the demo 10x10 World using only the two Textures we've already been using for Bricks, and I've left the camera view the way it was last time.

Have fun :)

daydreamer - I've written an OBJ file import/export tool in the past, but that sourcecode was destroyed. If you want help with an OBJ<-->X converter, I'm willing to help.
I'll appreciate any contribution you wish to make in regards to this project framework, especially once the first simple demo game is working.
Attachments:
Posted on 2006-07-31 03:33:01 by Homer
Today's update shows the loading and rendering of a Player mesh (from xfile).
I've implemented this via ResourceManager, which means that we can create N instances of the Player mesh (ie multiplayer), it means that resources are cleaned up for us, and it means that lazyboy only had to add a few lines of code to implement this new entity :P

Note: I've moved the camera back somewhat so you can see more easily, and I am yet to hook up the player controls.

The player mesh (which I created in Maya) is a very simple geometry.
It's a deformed cube.. I chose this geometry for two reasons: it's suitable for the proposed demo game, and it allows me to begin talking about a rather advanced topic that we'll be covering soon.

You're going to have to use your imagination here, but at least you have a visual aid.
The 'deformed cube' we're seeing is an example of a Frustum..we typically use a Frustum to describe what the camera is able to see.
Anything inside this frustum thingy is potentially visible, and anything not inside it is OFFSCREEN.
Imagine that the smaller face at the back of that frustum is your monitor screen..thats the Near Plane.. the distance to the larger face at the front is the viewing depth.. the larger flat face at the front is the Far Plane. Imagine that our Camera was always located in the center of the Near Plane, and that the Frustum was locked to move and rotate with the Camera.. is it starting to make sense yet?
What the heck do we care about this for?

We will be implementing something called Frustum Culling.
We will attach a frustum to our Camera (actually, we'll be extracting the frustum from the camera view anytime the view is updated, but anyhoo..)
We'll determine what stuff is partially or fully inside the Frustum, and only render that stuff.. basically we won't render whatever is definitely not visible, like stuff thats behind the camera, or very far away stuff.
This can dramatically improve our rendering speed when the world is large and/or has a lot of stuff in it.
We're going to take it one step further too, instead of simply checking what stuff is inside the frustum, we'll check what 'World Cells' are inside the frustum (because we can test these quickly).
If a World Cell is not visible, the stuff inside it cannot possibly be visible either.. this is known as HIERARCHICAL CULLING.

In the next update, we'll hook up the Player controls and if I can spare the time from my schedule we'll begin looking at collision detection basics and maybe if we're lucky we'll add simple motion to the Sphere.
Another thing we'll need very soon is a SKYBOX, so maybe we can add that too.


Attachments:
Posted on 2006-08-02 03:44:22 by Homer
Now I've hooked up some keyboard controls.
You can use the left and right cursors to control the 'local Player'.
I've left the up and down cursors controlling the Camera.

The ResourceManager doesn't really give us very good access to manipulate the Mesh instances individually, so rather than adding special methods for this purpose, I've added one line to the D3DMesh.AddInstance method so that it returns a ptr to the new D3DMeshInstance, which we can store for quicker access (rather than having to 'dig through pointers' to get to it).

It's time to begin talking about basic collision detection (we'll discuss collision response when it becomes appropriate).
The most primitive kind of collision testing we can perform involves testing the position of a Point in 3D space.
Points are infinitely small, they give position and nothing else.
Most of the more complex collision tests we can perform are based on point testing, for example, testing the collision of a Line segment can be thought of in terms of testing two points. In the field of 3D programming, we typically refer to a 3D line between two 3D points as an EDGE.
We can consider the Vertices of our 3D entities as Points, and we can consider our 3D entities in general as being constructed from Points, Edges and Triangular Surfaces ('faces').
The great thing about Triangular surfaces is that they are always truly FLAT, ie, they can't be warped or twisted like more complex surfaces such as rectangles can be.
We can say that a Triangle always rests on a Plane.
What's a Plane? A plane is a theoretical flat surface in 3D space which stretches to infinity, but is always flat.
Planes are very important for collision testing, and we'll certainly be learning more about them as we go.. but for now, let's concentrate on the basics.

Our first collision testing will involve Spheres.
The collision test for Spheres is ALMOST exactly the same as testing a Point.. assuming that we know the Radius of the Sphere, and the Position of the sphere's center in 3D space, we can perform a simple point test, but give the point some 'thickness' equal to the radius.
For example: for a Sphere with a radius of 5 units, if we perform a point test using the Sphere's center and find that the point is 4 units away from the entity we're testing against, we can say YES, theres possibly a collision here, and the possible penetration depth is 1.
If the sphere's center was exactly 5 units away, it's rubbing up against the other entity, and if its further away than 5, theres NO COLLISION.
See what I mean about giving the point some 'thickness'?

So what I'm really saying is that testing a Sphere is the same as testing a Point, except that there is a known 'Tolerance' (margin of error) that is equal to the Sphere radius.

Our first mission will be to detect collisions between our 'game ball' and our 'bricks'.
We won't need to perform 'Plane intersection tests' or anything like that just yet, because we can take advantage of the fact that the Bricks are axially-aligned (ie never rotated) and are of a fixed size.
Essentially, I'm saying that we can test a Sphere against a World Cell's boundaries very quickly, and we DO want our collision tests to be as fast as possible, we're learning good habits that will help us to avoid struggling with the problems that arise later in a game's development due to inefficient algorithms and poor planning.

Next we'll learn another relatively advanced technique (but absolutely easy to implement) which is called Point Constraint.
Basically, we're going to make the game ball 'stick to the player' until the user decides to LAUNCH the ball toward the bricks.
As soon as we can launch the ball, we'll want our first simplistic collision testing, otherwise the ball will just sail right through the bricks, leave the playing area, and continue travelling 'to infinity and beyond'!!!

Attachments:
Posted on 2006-08-03 00:45:54 by Homer
Minor changes were made to the D3DApp.Run method.
Today I've added support for a fourth user callback to D3DApp (and thus to D3DGame).
Its job is to periodically update the position and orientation of our entities over time.
More specifically: our user callback will be executed at fixed timesteps (which just happen to equal our "Desired FPS" ie 1/30 of a second  in the current democode).
We'll call it our "Physics updater", and we'll call the fixed timestep our "Physics TimeStep".

I've desynchronized the Physics updates from the Rendering updates.
There's a number of very good reasons why the physics updates should occur at a fixed interval, among which:  it allows for very accurate prediction, and it is not prone to numerical drift caused by compounded error in elapsed time evaluation (like variable timestepping schemes are).

Note that our Rendering updates merely approximate a fixed timestep - the actual time elapsed between frame renderings is subject to a little drift.
Conversely, the Physics updates are time-corrected to an EXACT timestep.

When we want to move some entity over time, whether we realize it or not, we are in fact implementing some crude physics.
It's better to accept this fact right up front, and design the framework accordingly.

The position and orientation of our entities describe their 'Physics State'.
We now have a place to update the Physics State of our entities using a fixed timestep and without regard to actual framerate.

Later we'll be adding a Runge-Kutta (RK4) numerical integrator for maintaining the physics states, then we'll have 'real physics simulation' and not just some slapped-together simple physics approximation.
RK4 in particular hates a variable timestep, so we're planning ahead.
Sure, THIS DEMO GAME doesn't really require nice physics, but future projects probably will, and our framework will support it.

I'm not going to post an update just yet, because there's no appreciable changes (visually) in the demo, so it's no fun yet.

I'm going to add support for Instancing of the Sphere next, so that we can have multiple game balls in play, and so that we can attach game balls to players at the start of the game.
Since spheres are a geometric primitive, we'll add support to the ResourceManager for watching over them.

Sound fair enough?

Well people, the progress has been a little slow, but we're getting there.. Does anyone have questions, concerns, suggestions, etc?
Posted on 2006-08-03 22:21:25 by Homer
I've created a new Object called D3DSphere.
It's a lot like D3DMesh, it represents a reference object which manages its own collection of D3DSphereInstance structures.
D3DSphere supports unique Radius and geometric refinement (number of slices and stacks).
D3DSphereInstance supports unique position and color.

Like D3DMesh, I've implemented D3DSphere underneath ResourceManager.

There's a fair bit of code in there, so it probably warrants another update to the zip, even though we still have nothing new to see at runtime.

Note that I have not yet replaced the original sphere-related code in the main asm file, nor have I implemented code which actually USES the new managed Spheres.

Anyway, you can now create reference spheres (of fixed radius), and their instances (pretty colored balls everywhere).

In terms of this demo game, I'll be associating one instance of a sphere with each Player... a Player will initially 'Own' a sphere instance such that the position of the sphere may be forced to some Player-relative position using a point constraint (ie each player will initially have a game ball attached to them).
Once the Player has 'launched the ball', this association will not be enforced, and so neither will the point constraint.

Our resourcemanager is becoming more powerful :)
Attachments:
Posted on 2006-08-03 23:47:05 by Homer

Tutorial number 30 already - a milestone?

The code related to Spheres in the main asm file was replaced with code which implements the same, but using the new Managed spheres.

We're ready to implement our first Point Constraint :)
Attachments:
Posted on 2006-08-04 03:45:05 by Homer
I've implemented a Point Constraint, the position of the 'game ball' is being constrained to the position of the local player, until you press SPACEBAR to 'launch the ball'... obviously, I've added a keyboard callback which is called in response to the spacebar being pressed.

A new field was added to the Player object which is used to associate a D3DSphereInstance with a Player. The reason I implemented the association 'loosely' the way I have is because I didn't want the Player to 'strictly own' a ball.. once the ball has been  'launched' by the player, the association between them is broken, nobody 'owns' a ball which is in play. This way we can support multiple players and multiple balls. Everyone can start with a ball, and everyone can play with anyone's balls - now get your mind out of the gutter :P

The code which instances a local Player was modified to correctly store the instance in the Players collection.

I found a problem with the keyboard-checking method in D3DApp, the spacebar was generating 7 different keycodes, and not just the ascii 32 we'd expect.

I've reworked it to use GetKeyState instead of GetKeyboardState, so now we're only checking the keys which have callbacks set up, which is an improvement over the previous method anyway.

PS: I noticed that at some point, I had commented out one very important line of code in D3DGame.Done, which is simple "ACall Done" - ie, a call to D3DApp.Done. I've just uncommented it but that change isn't in the zip I'd already uploaded. The reason why it's so important is  that the D3DApp.Done method is responsible for saving our video settings to configfile, and since we weren't doing that, the demo would 'forget' what video settings we were using last time... d'oh!!!
Attachments:
Posted on 2006-08-06 01:22:27 by Homer

Now I've added linear motion to the game ball.
When you launch it, it will travel away from you.
It will sail away, right through the Bricks, and off into infinity.

In fact, I added a new method to the Sphere class called ApplyVelocityToAllInstances, its job is to update the Position of each D3DSphereInstance according to its unique 3D velocity.
It gets called from our GameUpdateEntityPhysics callback procedure.

We can start to get fancy about some of the similarities between different instanceable primitive structures, for example we might decide to make the assumption that all these structs have Position as the first field, and utilize a universal physics updater which follows said assumption.

But this is a tutorial, and assumptions are bad form.
Linear motion is kind of boring,right?
It's time for collision testing :)



Attachments:
Posted on 2006-08-06 12:33:01 by Homer
Hey Homer

Keep em coming, I'm eager to see my balls bounce :lol: :shock: ;)

Will the demo cover sound effects?

Maelstrom
Posted on 2006-08-06 22:39:56 by Maelstrom
In the attached update, I've implemented a very simple Point-based collision detection scheme for the Sphere instances.
It works on the follwing presumptions:
- we can determine whether a given 3D point is within a World cell
- we can determine whether a given Cell contains a Brick or not.
Since Bricks completely occupy World cells, an intersection with a Cell containing a Brick is equivalent to an intersection with that Brick.

It's easiest to understand if I describe it in the same order it was implemented.

First, I added a method called World.IdentifyCellByCoordinate
This method takes a 2D input coordinate of a point in worldspace which we want to test, ie, in the XZ plane, no Y component.
It converts that coordinate into a pair of integers representing the 2D coordinate of a Cell in the World Array, or it tries to.
If the input coordinate is outside the bounds of the array, it returns NULL.. but if the array coords are legal, it returns a pointer to an array element, ie, a WorldCell structure.

Note : the WorldCell structure was modified to hold a Pointer to a Brick instance, or NULL.

Basically, we can use the IdentifyCellByCoordinate to determine whether a given point is "above a world cell", and if so, which one.

My next move was to add code to check the position of each sphere instance's center using the new method. This code was added to the GameUpdateEntityPhysics callback.
Of course, simply testing for collisions with the sphere center isn't enough - we need to take its Radius into account.
After I got the code working for testing Sphere positions (point testing), I modified it to perform FOUR tests per sphere instance, ie, testing the +X, -X, +Z and -Z boundaries of each sphere instance.

Since our world cells are a regular grid, and since the cells are the same size as our sphere diameter, this was simply a matter of adding or subtracting one Radius when performing the point test already described.

POTENTIAL OPTIMIZATION :
If you think about it, this is really overkill, in this demo we can safely reduce that to one or two axial tests , ie only check the sphere boundaries according to the sign of the axial velocity XZ components ... ie test the sides of the sphere in the direction it's moving, and assume that the 'trailing edges' of the sphere don't need testing.

Now we can tell not only which world cell a sphere is above, but we can tell which world cell(s) the sphere's boundary intersects.

I go on to check the return value for each point tested.
If its not NULL, we have a pointer to a World cell as I mentioned.
That being the case, I check the value of that World cell's pBrickInstance field, to see if the cell contains a brick.
If there's a brick in there, we SHOULD be destroying the brick with a nice little explosion and making the sphere bounce away from it.
Currently I simply spew the Brick instance pointer(s) to debug.

Yay... it's starting to look like a Game :)
Attachments:
Posted on 2006-08-06 22:53:24 by Homer
Sure, I'll cover sound effects, but until now there's been little point.
Sound effects are typically triggered as a response to some in-game event (such as a collision ;) )
Posted on 2006-08-06 22:54:37 by Homer
The collision testing implemented so far is NOT SUFFICIENT.
It does not deal with cases of 'diagonal' intersections.
It would have been sufficient for testing axially-aligned Boxes, but not Spheres.
We need to rethink the test being performed.
Note that this was a deliberate mistake, I wanted to show simplistic tests and the kind of thinking behind them before moving on to more robust tests, and I was attempting to show that collision testing doesn't always have to be sophisticated in order to be effective.
In the next update, we'll improve the collision testing implemented so far, and we'll add code to destroy any Bricks we collide with, but without performing a collision response per se - we won't bounce off them yet.
Posted on 2006-08-06 23:12:57 by Homer

This update contains an improved collision test.
The test between spheres and bricks is performed in 2D as before, but using a more accurate algorithm, which requires more information about the cubes and spheres.
Brick struct now contains a vPosition field like the other instanceable entity structs do.

Next, we need to start messing around with collision response ideas.


Attachments:
Posted on 2006-08-07 09:51:21 by Homer