Jump to content

  • Log In with Google      Sign In   
  • Create Account


We have 4 x Pro Licences (valued at $59 each) for 2d modular animation software Spriter to give away in this Thursday's GDNet Direct email newsletter.

Read more in this forum topic or make sure you're signed up (from the right-hand sidebar on the homepage) and read Thursday's newsletter to get in the running!


Member Since 20 Jun 2008
Offline Last Active Jan 10 2014 02:04 PM

#4977455 Assimp and COLLADA bind shape matrix

Posted by teutonicus on 06 September 2012 - 07:45 PM

Odd ... can you verify that the collada file you're using has those transformations in it? If you aren't using the aiProcess_PreTransformVertices flag when reading the file, the transformations for your aiMesh instances should be in the aiNodes that they're attached to. Are you using the aiProcess_OptimizeGraph flag?

#4971735 Addon for Firefox to download Git or Redmine code?

Posted by teutonicus on 21 August 2012 - 12:52 AM

I prefer to use a GUI for version control. I recommend SmartGit, which is free for non-commercial use. It's also available on Windows, OSX and Linux.
Can't help you with redmine as I've never used it before.

#4963097 Validating shader vertex format?

Posted by teutonicus on 25 July 2012 - 04:54 PM

The OpenGL equivalent is glGetActiveAttrib and related functions. The manpage explains it all very well.

#4962844 Assimp baby steps and mesh not completely rendering...

Posted by teutonicus on 25 July 2012 - 01:24 AM

The most likely culprit:
void StaticMesh::Render()
	for(std::vector<ObjectData*>::iterator mesh = m_meshData.begin(); mesh != m_meshData.end(); mesh++)

The second argument to glDrawElements (count) must specify the number of indices to use for the draw call, not the number of primitives. So multiply by three for triangle lists.

From what I could see, everything else looked okay.

In case you're interested in other feedback, here are some things that stood out as I had a quick look at your code:
for(unsigned int face = 0, vertexIndex = 0; face < numFaces; face++, vertexIndex += 3)
		const struct aiFace* meshFace = &thisMesh->mFaces[face];
		memcpy(&indices[vertexIndex],meshFace->mIndices,sizeof(unsigned int) * 3);
This loop assumes that meshFace->mNumIndices == 3, which could cause problems if your mesh is not triangulated

You've written destructors for some of your classes, but no copy constructors or assignment operators. I almost freaked out when I saw standard containers being used to store ObjectDatas but then realised that you were actually storing pointer-to-ObjectDatas ;)

Destructors delete/[]ing things that haven't been initialised. If ObjectData::Init is not called for some reason, bad things will happen when that object's destructor is called. I prefer one-step initialisation, but if that's not an option then be sure to initialise member pointers to nullptr in your constructors.

#4955774 API agnostic systems without massive dynamic casts - possible?

Posted by teutonicus on 04 July 2012 - 04:17 PM

You've only really got one graphics API per platform (except Windows, where you have to worry about D3D9/D3D11), so you switch renderer implementation at compile time. No dynamic casts. Something like:

	 #include "RendererD3D9.h"
#elif defined ZZZ_PLATFORM_OSX
	 #include "RendererGL.h"

class Renderer

class Renderer

Create separate D3D9/D3D11 builds for Windows and select most appropriate in game's launcher.

#4950416 most efficient general rendering strategies for new GPUs

Posted by teutonicus on 18 June 2012 - 05:04 PM

You are talking about memory. I'm specifically talking about overdraw/ trying to draw stuff that is outside the frustum. By grouping everything into 1 vbo, you are wasting time drawing/shading triangles that are outside the frustum or occluded.

A draw call does not have to touch all vertices in a VBO. Read Hodgman's post.

#4945109 Any video formats that are free to use?

Posted by teutonicus on 31 May 2012 - 04:32 PM

Useful blog post about this topic which I read a few days ago:

#4943974 Rapid prototyping when making games

Posted by teutonicus on 28 May 2012 - 05:49 AM

But in addition to that, are there any advices you have for rapid prototyping when developing a game (particularly when the primary language is C, C++, or any other non-dynamic language)? Any advices on clean/beautiful ways to integrate scripting languages (it's one thing to integrate a scripting language; it's another thing to do it well). Because I'd love to hear them.

The games I've seen that don't have a dynamic scripting language usually end up with some kind of Quake-style console or 'tweaks menu'. Something that can be easily accessed in-game and gives you the ability to play around with whatever tweakable things you expose to it. If these tweakables are content then saving the changes that you make's a lot simpler too.

You may be interested in checking out this GDC presentation; the Bitsquid guys provide some excellent techniques and ideas for reducing content iteration times. I also like the way that they have integrated Lua into their engine. All of their stuff is worth a read.

#4934604 asset handling library / engine?

Posted by teutonicus on 24 April 2012 - 08:34 PM

Got a chance to give your code a quick look today and got skeletal animation working with my own test model. The issues I found:

1. You are concatenating the transforms in your joint hierarchy (ie. generating model space transforms for your joints) correctly. But you are not then transforming them into bind space (the pose your joint hierarchy/skeleton was in when mesh was bound to it). This is where skinning happens. To do this you want to save the inverse bind transform for each of your joints and use this to transform your joints into bind space. Another way to think of it is 'getting the difference between the bind pose and the current pose'. Applying this to the mesh transforms it from the bind pose to your current pose.

Using Assimp you can get the bind transforms for your joints from aiBone::mOffsetMatrix. Invert this transform to use it as below:
D3DXMATRIX* ::AnimationClass::GetTransforms(...)

D3DXMATRIX inverse_bind_transform = i->second.GetInverseBindTransform();
_result[(*i).first] = inverse_bind_transform * (*i).second.GetCompleteTransform();

2. Your skinning function is only ever using the first joint index for each weight:
skinning += Bones[input.Indices[0]] * input.Weights[0];
skinning += Bones[input.Indices[0]] * input.Weights[1];
skinning += Bones[input.Indices[0]] * input.Weights[2];
skinning += Bones[input.Indices[0]] * (1 - input.Weights[0] - input.Weights[1] - input.Weights[2]);

should probably be:
skinning += Bones[input.Indices[0]] * input.Weights[0];
skinning += Bones[input.Indices[1]] * input.Weights[1];
skinning += Bones[input.Indices[2]] * input.Weights[2];
skinning += Bones[input.Indices[3]] * (1 - input.Weights[0] - input.Weights[1] - input.Weights[2]);

3. In NodeClass::CalculateTransform, you're calling D3DXMatrixScaling rather than D3DXMatrixTranslation to apply translation (near the end of the function).

4. Your vertex declaration does not match the vertex structure being used. You're using a D3DXCOLOR for your joint indices, but declaring it as a D3DCOLOR in your vertex declaration.
D3DXCOLOR = float4

5. Only animated joints are being sent to your vertex shader. If there's an animated joint in your hierarchy that has a parent which isn't animated then it's going to be wrong. Any vertex that is weighted to a joint which is not animated is going to be wrong. I didn't fix this as it would take a bit of time, so I just used a test model with animation on every joint. I've attached it to this post in case you want to use it.

Also I think there's a divide by zero in your animation interpolation code, you'll notice things going a bit wacky when the animation loops around.

Attached Files

  • Attached File  test.zip   22.94KB   36 downloads

#4931613 Smooth animation and frame transition

Posted by teutonicus on 15 April 2012 - 08:22 PM

It took me ~20 minutes to get your code running on my machine but it did eventually run. Some comments and tips from the experience:
- Never use hardcoded absolute paths as they will make compiling/running your code on another machine painful. You'll run into this when you get other programmers working on your code or start distributing your binaries to other people. There were only a couple of includes and libraries with absolute paths in your project, plus the assets.
- From memory I don't think that using #pragma comment(lib, etc) to specify libraries for linking works with GCC or LLVM. I prefer to have libraries specified in project property sheets (or whatever similar technique your IDE uses for configuring projects). This makes porting much easier too ;)
- Include any libraries that your project needs to compile and run with your project. Fortunately I already have the DX SDK and Assimp on my PC, so this wasn't much of a hassle for me.
- Check the return code of every D3D/D3DX function that you call! I wrap every D3D call with something like the below:

#include <DXErr.h> // also link your project to dxerr.lib
void your_logging_function( char const * category, char const * format, ... ) {}
#define LOG_DX_ERROR( hresult ) your_logging_function( "error", "DX error: %s, %s", DXGetErrorString( hresult ), DXGetErrorDescription( hresult ) );
#define CHECK_DX_RESULT( hresult ) if ( hresult != 0 ) { LOG_DX_ERROR( hresult ); }

CHECK_DX_RESULT( _shader.GetEffect()->SetMatrix("world", &_model.worldMatrix) );

This brings me to your first rendering problem. In your main loop:

_shader.GetEffect()->SetMatrix("world", &_model.worldMatrix);
_shader.GetEffect()->SetMatrix("view", _camera.GetView());
_shader.GetEffect()->SetMatrix("proj", _camera.GetProjection());

The handles you are passing here do not match the handles that you are using in your shader. They are case sensitive, so "world" != "World", etc. These functions were returning an error.

Your second problem is with the vector matrix multiplication in your vertex shader:

float4 Position = mul(World, input.Position);
Position = mul(View, Position);
output.Position = mul(Proj, Position);

which needs to be:
float4 Position = mul(input.Position, World);
Position = mul(Position, View);
output.Position = mul(Position, Proj);

I haven't looked into how you're handling your matrices, but they seem to be the transpose of what you're expecting in your shaders. You'll either need to make the change above or find out where your matrices are being transposed.

Keep up the good work Posted Image

#4928712 Is there an ACTUAL luabind tutorial

Posted by teutonicus on 06 April 2012 - 02:38 AM

Your lua/luabind includes look a bit iffy. Read sections 4 and 5 of this.

#4923488 Game Object Management (Deletion)

Posted by teutonicus on 19 March 2012 - 07:46 PM

But would it be correct to say that by accessing via an iterator rather than the [ ] operator you are getting a pointer to that instance of the Enemy class and that is the reason it is necessary to use -> rather than .?

Essentially, yes.

Also am curious as to way you say I do not need to "show" each of the enemies every frame now? How else would I display them? I thought it was bad practice to put the display of the enemy in the update loop. I was told that the best way to structure the game loop was handle input, update everything for movement, collisions etc then finally at the very end display the results. Here is are the current functions for showing and updating the enemies. (Previously there was just and if statement before apply_surface to check if alive)

void Enemy::show()

Oops, my bad. I was assuming incorrectly that 'show' did something else, such as setting an 'I am visible' flag or something, not the rendering of your enemies. Please ignore my comment about it. Your game loop looks fine.

#4923189 Game Object Management (Deletion)

Posted by teutonicus on 18 March 2012 - 09:54 PM

A simple way to do this could involve just removing 'dead' enemies from your enemies array, like so:

typedef std::vector< Enemy > EnemyArray;
EnemyArray enemies;
main loop
	 // your enemy update loop
	 for ( EnemyArray::iterator enemy = enemies.begin(); enemy != enemies.end(); )
		  enemy->update( &level );
		  if ( enemy->isDead() )
			   // enemy is dead, erase it
			   // NOTE: vector::erase will return an iterator to the element following the element it removes. so you do not want to increment it
			   // in this loop iteration; doing so will cause you to skip updating an enemy or even iterate past enemies.end(), which is obviously bad
			   enemy = enemies.erase( enemy );

You'll also no longer need to loop and 'show' all of your enemies every frame with this solution (I'm assuming that 'show' is where the magic logic to hide dead enemies currently happens).

There are possibly better/more efficient ways of doing this (calling vector::erase could call a lot of assignment operators, which could be expensive), such as storing an array of Enemy shared_ptrs rather than Enemy objects, reusing Enemy objects stored in a pool, etc. But when starting out it's usually better to KISS; you will discover/learn these techniques as you actually need them.

#4901894 Outputting an Integer and Manipulating it.

Posted by teutonicus on 11 January 2012 - 10:00 PM

You need to slow down and learn the basics because right now you're just hacking code. Read books, watch lectures, follow tutorials and learn to program. Do this whilst using any language but C++.

#4885740 I need help converting my OBJ loader from stdio to standard template library.

Posted by teutonicus on 19 November 2011 - 06:34 PM

You'll want to do something like:

std::string model_line;
std::getline(your_file_stream, model_line)
if (is_a_face)
	std::stringstream face_parser;
	face_parser << model_line;
	Face temp_face;
	while (face_parser.good())
		face_parser >> vert_ind;
		face_parser >> uv_ind;
		face_parser >> norm_ind;
		temp_face->AddVertex(vert_ind - 1);
		temp_face->AddNormal(normal_ind - 1);
		temp_face->AddUV(uv_ind - 1);
Untested and probably slower than stdio, but should get you started. And if you're loading from .obj then you're probably not too concerned about performance in the first place.