Jump to content

  • Log In with Google      Sign In   
  • Create Account

Lauris Kaplinski

Member Since 02 Aug 2006
Offline Last Active Feb 18 2013 02:33 AM

#4862010 Need help with my animation system.

Posted by Lauris Kaplinski on 15 September 2011 - 04:52 AM

I think little mnemonics would help you - it is really hard to dig through all those matrix inversions etc...

  • Bones form a hierarchy of transformations, where each bone has its own coordinate transformation relative to its parent. Let's call this transformation matrix B2P matrix (BoneToParent) - normally it has both translation and rotation parts
  • Animation keys usually define a new animated transformation relative to parent (normally only rotation part, but the idea is the same). Let's call it AB2P (AnimatedBoneToParent)
  • Now for each bone you'll want to find it's full transformation relative to animated object (mesh) itself. Let's call it B2M (BoneToMesh)
  • And also the same transformation while animated. Let's call it AB2M
Now you can do the following:

  • While loading mesh, store B2P for each bone (I think this is your mesh->bones[bone]->matrix)
  • At the same time, calculate B2M for each bone: bone.B2M = parent.B2M * bone.B2P
  • While updating bone animations, calculate AB2P from your animation keys
  • Calculate AB2M for the bone: bone.AB2M = parent.AB2M * bone.AB2P
You can do all these calcualtion with full matrices - no need to extract translation and rotation parts and apply these separately. The only exception is while extracting animation keys. If you have keys only for rotation, you have to compose AB2P from that rotation and the translation of the original bone matrix (B2P).

If you need the relative transformation of animated bone, compared to bone in rest pose, it is:

AB2B = B2P(-1) * AB2P

Sometimes you need the relative transformation of animated skin compared to skin bind pose, for this bone (i.e. if doing weighted skinnining)

AS2S = AB2M * B2M(-1)


#4861488 So what do you think of this DRM scheme?

Posted by Lauris Kaplinski on 14 September 2011 - 04:49 AM


I think it's a good idea. But you must make sure that legitimate customers aren't affects by this. Put in failsafes and give users the benefit of the doubt (ie. apply very generous tolerance margins to your detection). Some people do really stupid things without realizing it, these may trigger anti-piracy detection even though they purchased a legal copy.

Once you are sure that you are dealing with a pirated copy beyond reasonable doubt (eg. because it is running on a known pirated product key), then have fun. You outlined a rather nice and friendly approach. I personally would go farther than just a 'friendly' nag screen. If someone stole your car, you wouldn't let him get away with it on a friendly reminder to not do it again either. Just make sure to not do anything illegal or to touch any system resources outside of your own game.

I would change the game mechanics in not so subtle ways. Blatantly put the player into impossible situations. Retroactively modify their savegames. Make the game downright unfair. When they inevitably die/lose from the artificially induced unfairness, show them a screen telling them that the game will treat them just the way they treated the developer by pirating it.


Hehe that sounds fun. I was thinking of (depending on the game) an additional level where you can't finish it if you've pirated it, and it rains fecal matter on you or something like that. But messing with the game mechanics sounds like a lot of fun... reversing gravity occasionally... and screwing with their saved games... now THAT sounds fun haha.


Yes, it would be fun :D:) (for you, that is) - but think the following way:

How much time and resources would designing and writing the DRM and special "pirate" level take (including extra artwork and debugging, server time and so on)?

How much more enjoyable could you make your game for legitimate customers using the same resources? Don't they deserve the maximum amount of fun from your game?


#4858555 Models for exercises

Posted by Lauris Kaplinski on 07 September 2011 - 05:22 AM

TurboSquid has plenty of free models of various complexity.

If you want to write your own loaders, try OBJ format first - it is text based and quite straightforward to parse.

3DS is also simple file format.

You can find descriptions of both file formats by googling.




#4833445 Character Programming

Posted by Lauris Kaplinski on 10 July 2011 - 02:35 PM

I think the best way would be to download some free game engine (actual game engine, not simply rendering engine like Ogre) and look, how such things are implemented.

I am not professional developer, so I can only tell, how I am myself implementing things in my project.

In minimum there should be the following layers:

  • Game world with at least minimal physics - i.e. ground height calculation, collision detection (impenetrable objects, active objects, NPC-s), proximity testing (is any enemy close to you) and so on.
  • Rendering code for game world (environment, objects, animated characters)
  • Animation controller - this can play, mix and possibly synthesize required animations. May interact with game world - for example detection of ground height, to adjust walking animation appropriately
  • Character controller - has to interact both with animation controller and game world. Implements high level movement - like walking to certain direction, stopping if movement ahead is not possible, hitting enemy with sword and so on. It gets data from game world, instructions from player/AI and asks animation controller to play specific animations.
I think these layers are normally implemented inside game engine. Scripting is one level above character controller, controlling things like what to do if you meet some specific character, how are your health affected by enemy attack and so on. Of course, good engine implementation allows you to control lower level things by scripting too - like creating specific characters from data (model(s), animations, animation graph definition and so on).




#4833438 How shaders are connected with 3d Materials ?

Posted by Lauris Kaplinski on 10 July 2011 - 02:12 PM

You can think about it in such way:

Material is usually "game world" or "modeller world" description of the appearance of surface (or object). It defines set of physical parameters (texture(s), shininess, translucency and so on) of certain surface.

Shader is GPU program, that implements specific rendering technique.

Normally materials and shaders are somewhat correlated, as vastly different types of surfaces need different GPU programs for rendering. But if two materials only differ by texture image or color, they can share the same shader program and modify rendering parameters.

Also one material may have more than one shader. For example, if surface both is visible and casts shadow, it may have to be rendered in two different stages - as visible object, and as depthmap for shadow mapping. These normally use different shader programs, because calculating full color effects would be huge waste in depthmap rendering stage.

Also, shaders do not only render pixels, but also calculate vertex (triangle) positions (vertex and geometry shaders). Thus they are somewhat wider than only visible material. For example skeletally animated meshes may have special vertex shader part, that transforms their vertices together with skeleton. Static mesh - even if using exactly the same physical material description, does not need that part (only simple transformation)




#4831294 Line-Triangle Intersection

Posted by Lauris Kaplinski on 05 July 2011 - 04:55 AM

There are two approaches:

  • Get plane defined by triangle, get ray/plane intersection point, check whether it is inside triangle
  • Directly check ray/triangle intersection, if intersect get plane and ray/plane intersection point
I prefer the second approach because of clarity (do not know, which one is faster actually).

Let triangle be defined by 3 vertices P0, P1, P2
Let ray be defined by starting point P and direction vector D

vector V0 = P0 - P
vector V1 = P1 - P
vector V2 = P2 - P

vector V01 = V0 X V1
vector V12 = V1 X V2
vector V20 = V2 X V0

float s01 = V01 dot D
float s12 = V12 dot D
float s20 = v20 dot D

  // Line intersects triangle from CCW side only if all dot products are negative
if ((s01 >= 0) || (s12 >= 0) || (s20 >= 0))  return

 // Now find plane (defined by DISTANCE, NORMAL) from three points
vector NORMAL = normalize ((P1 - P0) X (P2 - P0))
float  DISTANCE= -(P0 dot NORMAL)

// Find line/plane intersection
float DEN = NORMAL dot D
// If DEN is 0 line is parallel to plane
if (DEN == 0) return
float T = -((NORMAL dot P) + DISTANCE) / DEN
// T is a relative movement along line until intersection - i.e. smallest T is closest triangle
// Intersection point is P + T * D
You can optimize the line/triangle intersection test by rearranging code a bit (as soon as you know V0 and V1, you can calculate V01 and s01...). Or if your vector operations are inlined, compiler probably will do it for you.

Hope this helps.


#4830460 How to make my freeware engine visible to users?

Posted by Lauris Kaplinski on 02 July 2011 - 02:45 PM

Start development blog ;-)

Write some small demos or games with your engine. At the same time, describe in your blog what you are doing - preferably in tutorial form. Sooner or later you will accidentally hit some popular search topic and people will start to find your blog.

Hang in forums. Help newbies. Discreetly show them, how something is done in your engine, or can be done more easily with your engine.




#4829072 Perfect float->string and string->float

Posted by Lauris Kaplinski on 29 June 2011 - 07:52 AM

Think a little what happens when you write/read floating point numbers as string. Working solution should be easy for floats (it is trickier for doubles):

  • Say you have unrepresentable in binary number like 0.2
  • In 32 bit float the actual value will be something like 0.199999X
  • You can convert it to double without losing any data, extra bits will be filled with zeroes
  • Set the 26th bit of double (add the original value divided by 2e26)
  • Convert this double to string using at least 10 significant digits
  • While reading back, read the value as double
  • It is guaranteed to have at least 24 bits identical to the original (if rounded up, some bits >24 are set, if rounded down, 26th bit is clear)
  • Convert it to float by rounding towards zero
  • You will get the original float value



#4827514 Animation: vertex_weights

Posted by Lauris Kaplinski on 25 June 2011 - 04:03 AM

It should be the index into the same array of vertices that your <polylist> or <triangles> or other primitive list is indexing. So I think you are correct ;-)


#4826734 Shader error

Posted by Lauris Kaplinski on 23 June 2011 - 05:57 AM

Sorry for neglecting the basic.

The error occurs on these 2 lines.

Line 50: float ndl = dot( normalize(normal), normalize(vec3(0,1,0)));
Line 98: float NdotL = max(dot(normalize(normal), normalize(vec3(-5000000.0,10000000.0,5000000.0))),0.0);

I am speculating, but maybe Intel GLSL compiler has problem applying normalize to constant vector?

normalize(vec3(0,1,0)) is pointless anyways ;-)


#4825241 Help with unusual projection

Posted by Lauris Kaplinski on 19 June 2011 - 04:01 PM

This seems ordinary isometric projection to me.

You need two matrices to describe your camera: View matrix and projection matrix.

The projection matrix is isometric projection - look at glOrtho or similar documentation.

You can construct view matrix with gluLookAt, or check for the lookAt matrix implementation code. Basically your camera is placed at little left and bottom of the figure (player?), certain distance above ground and looking at the player position. The up vector (that is needed for lookAt construction) is the same as the up direction for the player.




#4824417 cross platform distribution

Posted by Lauris Kaplinski on 17 June 2011 - 04:19 AM

Have you Linux installed? If not, you can install Ubuntu (at moment the most popular distro) into VirtualBox to experiment with it (I hope that SDL works properly in VirtualBox).

There is Code::Blocks available for Linux, so you should first try it out - although I am afraid, that the build systems are too different, so you cannot use the same project files. In such case, try to make new project file with the same source files.

Eventually you may want to move to some generic project generator, like CMake (it can generate Makefiles, Code::Blocks projects, Visual Studio projects and more), but at first try familiarize yourself with building under Linux.

You can distribute Linux binaries in zipfile too, but you have to decide, how to manage the libraries used by your project. Usually it is preferable to use libraries from system packages - but then you have to be precise, which libraries the user has to install to run your program (installation packages do it automatically). Or you have to provide libraries yourself - but then you risk with incompatibility problems between the system and your version of library.

Having installation packages (.deb, .rpm) is much easier to end users. But I usually distrust packages from unknown sources - if built badly they may mess up with system setup. So it may be benficial to have zipfiles too.

If you are serious about Linux distribution, you should probably compile your program separately for at least for some most popular distributions (and both 32 and 64 bit versions).

And also - as soon as you start to compile your program on different systems, you need some source control system (SVN, git...). Otherwise you'll mess surces up sooner or later :wink:


#4824228 General question for rotation and relevant axis

Posted by Lauris Kaplinski on 16 June 2011 - 03:12 PM

So I know that a 3x3 rotation matrix is relative to the world origin's axis. In a game engine, you'll usually have something though for yaw/pitch/roll. Are these also rotation about the world's origin or the local axis of our model? In terms of quaternion, what axis we are rotating about is irrelevant (correct?) since we can for example translate around [4 3 1] in local space or convert [4 3 1] into world space and have the same rotation.

Also, euler rotation is the only one where the axis of rotation don't change along with it right? Meaning, x moves but y and z remain stationary versus matrices where all axis move at once.

mmmm... you seem to have some concepts messed up.

Think about in such a way:

You have two coordinate systems (or more, but let's go with two) - world coordinate system and local coordinate system.

There exists a transformation (a function) that converts coordinates from local system to world system. Transformation can involve rotation, translation, (non-uniform) scaling and shearing.

The most generic way to specify such transformation is 4x3 matrix that is usually represented as 4x4 matrix with bottom row being 0,0,0,1

There are other ways to describe transformations - like 3x3 matrix, axis-angle, transformation vector, quaternion, euler angles and the combinations of those. Any combination of those can be converted to 4x4 matrix, but the opposite is not always possible - it depends, whether certain transformation components are able to represent actual transformation involved (for example quaternion cannot represent translation, non-uniform scaling and shearing). All such partial transformations can be represented by their own 4x4 matrixes and the composite effect can be represented by multiplying those matrices together.

Transformations are NOT commutative. I.e. first translating and then rotating gives different result as the reverse order. Thus whether your 3x3 matrix or quaternion or set of Euler angles represents rotation around world origin or local origin depends on it's position in transformation pipeline. I.e.

T = Tt * Tr      rotation around local axis
T = Tr * Tt      rotation around world axis

T - full transformation
Tr - rotation
Tt - translation

Euler angles are simply a certain way to describe arbitrary rotation using three sequential rotations around the axes of coordinate system (either local or global). Every rotation matrix can be described by at least one triplet of Euler angles and each triplet of Euler angles can be described by rotation matrix. And each component of Euler angle triplet can be represented by its own rotation matrix. The full rotation matrix can then be found by multiplying together component matrixes describing individual Euler rotations.

Tr = Rx * Ry * Rz

Rx - rotation around X axis
Ry - rotation around Y axis
Rz - rotation around Z axis



#4822039 Sharing Transformations

Posted by Lauris Kaplinski on 11 June 2011 - 04:26 AM

It depends how similar are your skeletons. You said, that they have the same number and topology of bones, but do they have the same bone local rotations (for the identical poses) - i.e. are simply differently scaled variants of each other. And whether you need things like expanding libs (hopefully not).
If the local transforms are the same between skeletons, simply break your animation matrices into rotation and translation parts and only apply rotations to other skeleton.
Say you have the transformation of animated bone in skeleton 1 relative to parent bone AB2P1 (AnimatedBoneToParent), then:

(1) AB2P1 = B2P1 * AB2B1


Where B2P1 (BoneToParent) is the local transformation of bone in rest pose (T-pose) and AB2B1 (AnimatedBoneToBone) is relative animation of the bone (compared to the rest pose).
AB2B1 should be pure rotation matrix (unless stretching or scaling of body parts is involved). So for the other skeleton:

(2) AB2P2 = B2P2 * AB2B1

But as I said, this only works, if you have skeletons, that have similar local bone transformations (for same poses). If the bone transformations are different, then you have to go through object/mesh space.

(3) AM2M1 = AP2M1 * B2P * AB2B1 * M2B1
AM2M1 (AnimatedMeshToMesh)
AP2M1 (AnimatedParentToMesh)
M2B1 (MeshToBone)

For each bone AM2M can be broken to translation and rotation parts (AM2M1_t and AM2M1_r), thus:
(4) AM2M1 = AM2M1_t * AM2M1_r
And:
(5) AM2M1_t = translation of AP2M1 * B2P1

The rotation parts of animations of both skeletons should be identical in mesh space, thus:

(6) AM2M2 = AM2M2_t * AM2M1_r

From those formulas you should be able to compose the needed transformations of the other skeleton. There will be lots of matrix calculations initially, but fortunately much of these can be precalculated or optimized out later.




PARTNERS