Jump to content

  • Log In with Google      Sign In   
  • Create Account


EngineCoder

Member Since 02 May 2009
Offline Last Active Nov 02 2012 09:43 AM

Topics I've Started

What's wrong with my tangent calculation?

14 May 2012 - 10:13 AM

Some of my triangles render as if they're pointing away from the light. I can fix them by rotating their UV map upside down.
void Mesh::generateTangents()
{
	for (size_t v = 0; v < m_vertices.size(); ++v)
	{
		m_vertices[ v ].tangent = Vec4( 0.0f, 0.0f, 0.0f, 0.0f );
	}
  
	m_tangents.resize  ( m_indices.size() );
	m_bitangents.resize( m_indices.size() );
	bool degenerateFound = false;
	for (size_t f = 0; f < m_indices.size(); ++f)
	{
		const Vec3& p1 = m_vertices[ m_indices[ f ].a ].position;
		const Vec3& p2 = m_vertices[ m_indices[ f ].b ].position;
		const Vec3& p3 = m_vertices[ m_indices[ f ].c ].position;
	  
		const TexCoord& uv1 = m_vertices[ m_indices[ f ].a ].texCoord;
		const TexCoord& uv2 = m_vertices[ m_indices[ f ].b ].texCoord;
		const TexCoord& uv3 = m_vertices[ m_indices[ f ].c ].texCoord;
			  
		const TexCoord deltaUV1 = uv2 - uv1;
		const TexCoord deltaUV2 = uv3 - uv1;
	  
		const float area = (deltaUV1.u * deltaUV2.v - deltaUV1.v * deltaUV2.u);
		Vec3 tangent;
		Vec3 bitangent;
		if (std::fabs( area ) > 0.00001f)
		{
			const Vec3 deltaP1 = p2 - p1;
			const Vec3 deltaP2 = p3 - p1;
			tangent   = (deltaP1 * deltaUV2.v - deltaP2 * deltaUV1.v) / area;
			bitangent = (deltaP1 * deltaUV2.u - deltaP2 * deltaUV1.u) / area;
		}
		else
		{
			degenerateFound = true;
		}
		Vec4 tangent4( tangent.x, tangent.y, tangent.z, 0.0f );
		m_tangents[ f ]   = tangent4;
		m_bitangents[ f ] = bitangent;
	}
  
	if (degenerateFound)
	{
		std::cout << std::endl << "Warning: Degenerate UV map in " << name << ".  Author needs to separate texture points in mesh's UV map." << std::endl;		  
	}
	m_vbitangents.resize( m_vertices.size() );
	// For every vertex, check how many faces touches
	// it and calculate the average of their tangents.
	for (size_t vertInd = 0; vertInd < m_vertices.size(); ++vertInd)
	{
		Vec4 tangent( 0.0f, 0.0f, 0.0f, 0.0f );
		Vec3 bitangent;
		for (size_t faceInd = 0; faceInd < m_indices.size(); ++faceInd)
		{		  
			if (m_indices[ faceInd ].a == vertInd ||
				m_indices[ faceInd ].b == vertInd ||
				m_indices[ faceInd ].c == vertInd)
			{
				tangent   += m_tangents  [ faceInd ];
				bitangent += m_bitangents[ faceInd ];
			}
		}
		m_vertices[ vertInd ].tangent = tangent;
		m_vbitangents[ vertInd ] = bitangent;
	}
  
	for (size_t v = 0; v < m_vertices.size(); ++v)
	{
		Vec4& tangent = m_vertices[ v ].tangent;
		const Vec3& normal = m_vertices[ v ].normal;
		const Vec4 normal4( normal.x, normal.y, normal.z, 0.0 );
	  
		// Gram-Schmidt orthonormalization.  
		tangent -= normal4 * normal4.dot( tangent );
		tangent.normalize();
		// Handedness. TBN must form a right-handed coordinate system,
		// i.e. cross(n,t) must have the same orientation than b.
		const Vec3 cp = normal.cross( Vec3( tangent.x, tangent.y, tangent.z ) );
		tangent.w = cp.dot( m_vbitangents[v] ) > 0.0f ? 1.0f : -1.0f;
	}
}

I'm calculating my bitangents in the vertex shader and multiplying them with the handedness sign. I'm transforming my camera and point light position from world space to object space by multiplying them with inverse model matrix before transforming them to tangent space. If I visualize my TBN vectors, normals are pointing away from the surface and tangents and bitangents are orthogonal.

void main()
{
	gl_Position = u_modelViewProjectionMatrix * vec4( a_position, 1.0 );
	v_texCoord = a_texCoord;
	vec3 bitangent = normalize( cross( a_normal, a_tangent.xyz ) ) * a_tangent.w; // .w is the handedness.

	mat3 toTangentSpace = mat3(
							   a_tangent.x, bitangent.x, a_normal.x,
							   a_tangent.y, bitangent.y, a_normal.y,
							   a_tangent.z, bitangent.z, a_normal.z
							   );

	vec3 mDirToLight  = normalize( u_lightPosition.xyz  - a_position ); // Light position is in object-space.
	vec3 mDirToCamera = normalize( u_cameraPosition.xyz - a_position ); // Camera position is in object-space.

	v_dirToLight = toTangentSpace * mDirToLight;
	v_viewDir	= toTangentSpace * mDirToCamera;
}

Cyberjack (iOS Universal) action/puzzler

19 April 2012 - 08:33 AM

Posted Image

http://itunes.apple....73873?ls=1&mt=8

In Cyberjack you can shoot bots, disarm explosives, hack computers and upgrade your character with augs like stealth and shield. I made the game alone in 10 months using mostly open source tools. Any questions or feedback is appreciated!

[SOLVED] Skeletal animation rotation problems

28 May 2010 - 07:49 AM

I'm trying to render COLLADA animation exported from Blender. My test animation moves a human's hand up and left. I got the basic skinning working fine, but the animation rotates to the wrong direction. The animation is split to 4 animations: translate, rotateX, rotateY and rotateZ. I assume that I must combine those values to form a key frame's joint matrix and I must multiply it with the joint's parent's world matrix and store the result to joint's world matrix. Here's some code: Methods from Mesh class:
/**
 * Applies matrices to skeleton. Should start from the root node.
 * Joint's world transformation is parent's world transformation * local trans.
 **/
void applySkin( Joint* joint )
{
    assert( joint );

    for (size_t c = 0; c < joint->children.size(); ++c) {
        Math::multiplyMatrix( joint->children[ c ].transform,
                              joint->worldTransform,
                              joint->children[ c ].worldTransform );
        applySkin( &joint->children[ c ] );
    }
}
 
    // Replaces joint named "name"'s transform matrix with matrix. Should start searching from root.
    void replaceJointMatrix( const std::string& name, Joint* j, float matrix[ 16 ] )
    {
        assert( j );
        assert( matrix );

        if (name == j->name) {
            for (int i = 0; i < 16; ++i) {
                j->transform[ i ] = matrix[ i ];
            }
            return;
        }
        else {
            for (size_t c = 0; c < j->children.size(); ++c) {
                replaceJointMatrix( name, &j->children[ c ], matrix );
            }
        }
    }

    // Animations change rotX, rotY, rotZ, posX, posY and posZ. From these values the new joint transform matrix is constructed.
    void applyAnimation()
    {
        for (std::map< std::string, Animation >::iterator a = animations.begin(); a != animations.end(); ++a) {
            // Skips this animation if it's not running.
            if (a->second.stepStartTime == 0) { continue; }

            // If true, proceed to the next time step.
            if ((SDL_GetTicks() - a->second.stepStartTime) / 1000.0f >= a->second.input[ a->second.inputIndex ]) {
                ++a->second.inputIndex;
                a->second.stepStartTime = SDL_GetTicks();
            }
            // If true, the animation is finished.
            if (a->second.inputIndex == (int)a->second.input.size()) {
                animations.erase( a->first );
                continue;
            }

            Joint joint;
            findJoint( a->second.joint, &skeleton[ 0 ], &joint );

            float t = (SDL_GetTicks() - a->second.stepStartTime) / 1000.0f;
            int ind = a->second.inputIndex;
            t /= a->second.input[ ind ];

            float animRotationMatrix[ 16 ];
            Math::identityMatrix( animRotationMatrix );

            if (a->second.operation == "translate") {
                float xv = (a->second.output[ ind - 1 == -1? 0 : (ind - 1)*3+0 ]);
                posX = interpolate( xv, a->second.output[ ind * 3 + 0 ], t );

                float yv = (a->second.output[ ind - 1 == -1? 1 : (ind - 1)*3+1 ]);
                posY = interpolate( yv, a->second.output[ ind * 3 + 1 ], t );

                float zv = (a->second.output[ ind - 1 == -1? 2 : (ind - 1)*3+2 ]);
                posZ = interpolate( zv, a->second.output[ ind * 3 + 2 ], t );

                Math::makeRotationMatrix4x4( animRotationMatrix, rotX, rotY, rotZ );
                float transMatrix[ 16 ];
                Math::identityMatrix( transMatrix );
                transMatrix[ 3 ] = posX;
                transMatrix[ 7 ] = posY;
                transMatrix[11 ] = posZ;

                Math::multiplyMatrix( transMatrix, animRotationMatrix, animRotationMatrix );
                replaceJointMatrix( a->second.joint, &skeleton[ 0 ],
                                    animRotationMatrix );
            }
           // Output values are changing X angle.
            if (a->second.operation == "rotateX.ANGLE") {
                float val1 = a->second.output[ ind - 1 == -1? 0 : ind - 1 ];
                float val2 = a->second.output[ ind ];

                rotX = interpolate( val1, val2, t );
                Math::makeRotationMatrix4x4( animRotationMatrix, rotX, rotY, rotZ );
                float transMatrix[ 16 ];
                Math::identityMatrix( transMatrix );
                transMatrix[ 3 ] = posX;
                transMatrix[ 7 ] = posY;
                transMatrix[11 ] = posZ;

                Math::multiplyMatrix( transMatrix, animRotationMatrix, animRotationMatrix );

                replaceJointMatrix( a->second.joint, &skeleton[ 0 ],
                                    animRotationMatrix );
            }

// Y and Z-angles are changed the same way, no need to paste them here.

        vertexDeformed.clear();

        applySkin( &skeleton[ 0 ] );

        // Applies the deform.
        // From COLLADA spec: outv = sum{ (v * BSM) * IBMi * JMi * JW }
        for (size_t v = 0; v < influences.size(); ++v) {
            Influence& i = influences[ v ];
            vertexDeformed.push_back( Vector() );
            Vector4 temp;
            Vector4 v4 = Vector4( vertex[ v ].x, vertex[ v ].y, vertex[ v ].z, 1.0f );
            Math::vectorMultMatrix4x4( v4, bindShapeMatrix, &temp ); // v * BSM

            for (size_t j = 0; j < i.jointIndices.size(); ++j) {
                // Finds a joint for this influence.
                std::string jointName = jointArray[ i.jointIndices[ j ] ];
                Joint joint;
                findJoint( jointName, &skeleton[ 0 ], &joint );
                Vector4 temp2;
                Math::vectorMultMatrix4x4( temp, joint.ibm, &temp2 ); // * IBMi

                Vector4 temp3;
                Math::vectorMultMatrix4x4( temp2, joint.worldTransform, &temp3 ); // * JMi

                temp3 *= weights[ i.weightIndices[ j ] ]; // * JW
                vertexDeformed.back() += Vector( temp3.x, temp3.y, temp3.z ); // outv
            }
        }
    }


I noticed that if I manually change a joint's translation (joint.transform[3, 7 and 11] ) nothing seems to change. From the rendered animation I think that my animation's pivot point is wrong. edit: I solved this problem. Solution in the last post. [Edited by - EngineCoder on May 29, 2010 6:33:16 AM]

[SOLVED] Problems when moving from GL_STENCIL_TEST to GL_STENCIL_TEST_TWO_SIDE_EXT

22 December 2009 - 06:57 AM

My z-fail shadow volume rendering works fine, but when I try to convert it to use GL_STENCIL_TEST_TWO_SIDE_EXT my scene renders completely lit. Old, working code:
        // Prior to this code the scene is rendered using ambient lighting.
        glColorMask( GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE );     
        glDepthMask( GL_FALSE );
        glDepthFunc( GL_LEQUAL );
        glDisable( GL_TEXTURE_2D );
        glDisable( GL_LIGHTING );

        glEnable( GL_POLYGON_OFFSET_FILL );
        glPolygonOffset( 0.0f, 100.0f );

        glEnable( GL_STENCIL_TEST );

        // Renders shadow volumes.
        for (unsigned int i = 0; i < scene->light.size(); ++i) {
            // First pass, renders volume's back faces.
            glCullFace( GL_FRONT );

            glStencilFunc( GL_ALWAYS, stencilClear, ~0 );
            glStencilOp( GL_KEEP, GL_INCR, GL_KEEP ); // stencilClear is 128
            glStencilMask( ~0 );
    
            renderAmbient = false;
            renderOpaque = true;
            activeShadowVolume = i;

            glPushMatrix();
            renderNode( scene->root ); // Opaque objects.
            glPopMatrix();

            renderOpaque = false;    
            glPushMatrix();
            renderNode( scene->root ); // Translucent objects.
            glPopMatrix();

            // Second pass, renders volume's front faces.
            glCullFace( GL_BACK );
            glStencilOp( GL_KEEP, GL_DECR, GL_KEEP );

            renderOpaque = true;
            glPushMatrix();
            renderNode( scene->root ); // Opaque objects.
            glPopMatrix();

            renderOpaque = false;    
            glPushMatrix();
            renderNode( scene->root ); // Translucent objects.
            glPopMatrix();
        }
        // Renders the scene normally.
        float black[4] = { 0, 0, 0, 1 };
        glLightModelfv( GL_LIGHT_MODEL_AMBIENT, black );
        glDisable( GL_POLYGON_OFFSET_FILL );    
        glDepthFunc( GL_EQUAL );
        glEnable( GL_BLEND );
        glBlendFunc( GL_ONE, GL_ONE );
        glStencilFunc( GL_EQUAL, stencilClear, ~0 );
        glStencilOp( GL_KEEP, GL_KEEP, GL_KEEP );
        glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE ); 
        glEnable( GL_TEXTURE_2D );
        glEnable( GL_LIGHTING );

        renderAmbient = false;    
        renderOpaque = true;
        activeShadowVolume = -1;
        glPushMatrix();
        renderNode( scene->root ); // Opaque objects.
        glPopMatrix();

        renderOpaque = false;
        glPushMatrix();
        renderNode( scene->root ); // Translucent objects.
        glPopMatrix();


New code using two-sided stencil test:
        ...
        glEnable( GL_STENCIL_TEST );
        glEnable( GL_STENCIL_TEST_TWO_SIDE_EXT );

        // Renders shadow volumes.
        for (unsigned int i = 0; i < scene->light.size(); ++i) {
            aglActiveStencilFaceEXT( GL_FRONT );
            glStencilOp( GL_KEEP, GL_DECR, GL_KEEP );    
            glStencilFunc( GL_ALWAYS, stencilClear, ~0 ); // stencilClear is 128
            glStencilMask( ~0 );

            aglActiveStencilFaceEXT( GL_BACK );
            glStencilOp( GL_KEEP, GL_INCR, GL_KEEP );    
            glStencilFunc( GL_ALWAYS, stencilClear, ~0 ); // stencilClear is 128
            glStencilMask( ~0 );
    
            renderAmbient = false;
            renderOpaque = true;
            activeShadowVolume = i;

            glPushMatrix();
            renderNode( scene->root ); // Opaque objects.
            glPopMatrix();

            renderOpaque = false;    
            glPushMatrix();
            renderNode( scene->root ); // Translucent objects.
            glPopMatrix();
        }
        // Renders the scene normally.
        float black[4] = { 0, 0, 0, 1 };
        glLightModelfv( GL_LIGHT_MODEL_AMBIENT, black );
        glDisable( GL_POLYGON_OFFSET_FILL );    
        glShadeModel( GL_SMOOTH );
        glDepthFunc( GL_EQUAL );
        glEnable( GL_BLEND );
        glEnable( GL_CULL_FACE );
        glBlendFunc( GL_ONE, GL_ONE );
        glDisable( GL_STENCIL_TEST_TWO_SIDE_EXT );
        glEnable( GL_STENCIL_TEST );
        glStencilFunc( GL_EQUAL, stencilClear, ~0 ); // stencilClear is 128
        glStencilOp( GL_KEEP, GL_KEEP, GL_KEEP );
        glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE ); 
        glEnable( GL_TEXTURE_2D );
        glEnable( GL_LIGHTING );

        renderAmbient = false;    
        renderOpaque = true;
        activeShadowVolume = -1;
        glPushMatrix();
        renderNode( scene->root ); // Opaque objects.
        glPopMatrix();

        renderOpaque = false;
        glPushMatrix();
        renderNode( scene->root ); // Translucent objects.
        glPopMatrix();


edit: It's funny, I solved the problem almost right after making this thread. Not the first time. The solution was to put the block with aglActiveStencilFaceEXT( GL_BACK ); abowe the block with aglActiveStencilFaceEXT( GL_FRONT ); Merry Xmas! [Edited by - EngineCoder on December 22, 2009 1:31:29 PM]

Detecting software fallback?

13 December 2009 - 10:53 PM

How can I know whether my program has caused OpenGL driver to switch to software rendering mode and the reason for the fallback? I'm using OpenGL over SDL on Linux/Windows/OS X. OpenGL debuggers are out of question, because my app should be able to sense the fallback on its own.

PARTNERS