www.pudn.com > tomohide_fur.03.13.02.zip > NormalMap.cpp


//----------------------------------------------------------------------------- 
// File: NormalMap.cpp 
// 
// Copyright (C) 2001-2002 Tomohide Kano. All rights reserved. 
//----------------------------------------------------------------------------- 
 
#include  
#include  
#include "extensions.h" 
#include "FurMesh.h" 
#include "NormalMap.h" 
 
 
//----------------------------------------------------------------------------- 
// NormalMap 
//----------------------------------------------------------------------------- 
NormalMap::NormalMap() 
{ 
	m_Width          = 0; 
	m_Height         = 0; 
	m_texOffset      = 0; 
	m_texNormal      = 0; 
	m_texClamp       = 0; 
	m_texNormalize   = 0; 
	m_vsUpdateOffset = 0; 
	m_fsUpdateOffset = 0; 
	m_vsUpdateNormal = 0; 
	m_fsUpdateNormal = 0; 
 
	m_PrevVel        = D3DXVECTOR4(0, 0, 0, 0); 
	m_Accel          = D3DXVECTOR4(0, 0, 0, 0); 
	m_PrevOmega      = D3DXVECTOR4(0, 0, 0, 0); 
	m_OmegaAccel     = D3DXVECTOR4(0, 0, 0, 0); 
 
	I_FORCE   = 0; 
	I_OMEGA   = 0; 
	I_DAMPING = 0; 
} 
 
 
//----------------------------------------------------------------------------- 
// ~NormalMap 
//----------------------------------------------------------------------------- 
NormalMap::~NormalMap() 
{ 
	glDeleteTextures(1, &m_texOffset); 
	glDeleteTextures(1, &m_texNormal); 
	glDeleteTextures(1, &m_texClamp); 
	glDeleteTextures(1, &m_texNormalize); 
	glDeleteVertexShaderEXT(m_vsUpdateOffset); 
	glDeleteVertexShaderEXT(m_vsUpdateNormal); 
	glDeleteFragmentShaderATI(m_fsUpdateOffset); 
	glDeleteFragmentShaderATI(m_fsUpdateNormal); 
} 
 
 
//----------------------------------------------------------------------------- 
// Init 
//----------------------------------------------------------------------------- 
bool NormalMap::Init(GLuint width, GLuint height) 
{ 
	m_Width  = width; 
	m_Height = height; 
	if (!InitTextures()) return false; 
	if (!InitShaders()) return false; 
	return true; 
} 
 
 
//----------------------------------------------------------------------------- 
// InitTextures 
//----------------------------------------------------------------------------- 
bool NormalMap::InitTextures() 
{ 
	// offset and normal maps 
	{ 
		GLubyte *pixels = new GLubyte[m_Width*m_Height*4]; 
		FillMemory(pixels, m_Width*m_Height*4, 128); 
 
		// offset map 
		glGenTextures(1, &m_texOffset); 
		glBindTexture(GL_TEXTURE_2D, m_texOffset); 
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, m_Width, m_Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); 
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); 
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); 
 
		// normal map 
		glGenTextures(1, &m_texNormal); 
		glBindTexture(GL_TEXTURE_2D, m_texNormal); 
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, m_Width, m_Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); 
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); 
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); 
 
		delete [] pixels; 
	} 
 
	// texture map for clamping 
	{ 
		int size = 64; 
		GLubyte *pixels = new GLubyte[size*size*4]; 
		GLubyte *p = pixels; 
 
		for (int j = 0; j < size; j++) 
		for (int i = 0; i < size; i++) 
		{ 
			D3DXVECTOR2 vec; 
			vec.x = -1 + 2 * i / float(size - 1); 
			vec.y = -1 + 2 * j / float(size - 1); 
			float len = D3DXVec2Length(&vec); 
			if (len > 1.0) vec /= len; 
			*p++ = GLubyte(128 + 127 * vec.x); 
			*p++ = GLubyte(128 + 127 * vec.y); 
			*p++ = 0; 
			*p++ = 0; 
		} 
 
		glGenTextures(1, &m_texClamp); 
		glBindTexture(GL_TEXTURE_2D, m_texClamp); 
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); 
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE_EXT); 
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE_EXT); 
 
		delete [] pixels; 
	} 
 
	// texture map for normalization 
	{ 
		int size = 64; 
		GLubyte *pixels = new GLubyte[size*size*4]; 
		GLubyte *p = pixels; 
 
		for (int j = 0; j < size; j++) 
		for (int i = 0; i < size; i++) 
		{ 
			D3DXVECTOR3 n; 
			n.x = -1 + 2 * i / float(size - 1); 
			n.y = -1 + 2 * j / float(size - 1); 
			n.z = 0; 
			float len = D3DXVec3Length(&n); 
			if (len > 1.0) n /= len; 
			n.z = 1.0; 
			D3DXVec3Normalize(&n, &n); 
			*p++ = GLubyte(128 + 127 * n.x); 
			*p++ = GLubyte(128 + 127 * n.y); 
			*p++ = GLubyte(128 + 127 * n.z); 
			*p++ = 0; 
		} 
 
		glGenTextures(1, &m_texNormalize); 
		glBindTexture(GL_TEXTURE_2D, m_texNormalize); 
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); 
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE_EXT); 
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE_EXT); 
 
		delete [] pixels; 
	} 
 
	if (glGetError()) 
		return false; 
	return true; 
} 
 
 
//----------------------------------------------------------------------------- 
// InitShaders 
//----------------------------------------------------------------------------- 
bool NormalMap::InitShaders() 
{ 
	// variants 
	GLuint V_POS    = glBindParameterEXT(GL_CURRENT_VERTEX_EXT); 
	GLuint V_TEX    = glBindTextureUnitParameterEXT(GL_TEXTURE0_ARB, GL_CURRENT_TEXTURE_COORDS); 
	GLuint V_DS     = glBindTextureUnitParameterEXT(GL_TEXTURE4_ARB, GL_CURRENT_TEXTURE_COORDS); 
	GLuint V_DT     = glBindTextureUnitParameterEXT(GL_TEXTURE5_ARB, GL_CURRENT_TEXTURE_COORDS); 
	GLuint V_NORMAL = glBindParameterEXT(GL_CURRENT_NORMAL); 
 
	// invariants 
	I_FORCE   = glGenSymbolsEXT(GL_VECTOR_EXT, GL_INVARIANT_EXT, GL_FULL_RANGE_EXT, 1); 
	I_OMEGA   = glGenSymbolsEXT(GL_VECTOR_EXT, GL_INVARIANT_EXT, GL_FULL_RANGE_EXT, 1); 
	I_DAMPING = glGenSymbolsEXT(GL_SCALAR_EXT, GL_INVARIANT_EXT, GL_FULL_RANGE_EXT, 1); 
 
	////////////////////////////////////////////////////////////////////// 
	// vertex shader for updating offset map 
	m_vsUpdateOffset = glGenVertexShadersEXT(1); 
	glBindVertexShaderEXT(m_vsUpdateOffset); 
	glBeginVertexShaderEXT(); 
	{ 
		// temporary 
		GLuint R_TEMPS = glGenSymbolsEXT(GL_SCALAR_EXT, GL_LOCAL_EXT, GL_FULL_RANGE_EXT, 1); 
		GLuint R_TEMPV = glGenSymbolsEXT(GL_VECTOR_EXT, GL_LOCAL_EXT, GL_FULL_RANGE_EXT, 1); 
 
		// vertex position 
		GLuint C_SCALE = glGenSymbolsEXT(GL_VECTOR_EXT, GL_LOCAL_CONSTANT_EXT, GL_FULL_RANGE_EXT, 1); 
		GLuint C_BIAS  = glGenSymbolsEXT(GL_VECTOR_EXT, GL_LOCAL_CONSTANT_EXT, GL_FULL_RANGE_EXT, 1); 
		glSetLocalConstantEXT(C_SCALE, GL_FLOAT, D3DXVECTOR4( 2,  2, 0, 1)); 
		glSetLocalConstantEXT(C_BIAS,  GL_FLOAT, D3DXVECTOR4(-1, -1, 0, 0)); 
		glShaderOp3EXT(GL_OP_MADD_EXT, GL_OUTPUT_VERTEX_EXT, V_TEX, C_SCALE, C_BIAS); 
 
		// texture coord for the previous offset map 
		glShaderOp1EXT(GL_OP_MOV_EXT, GL_OUTPUT_TEXTURE_COORD0_EXT, V_TEX); 
 
		// force vector 
		GLuint R_FORCE = glGenSymbolsEXT(GL_VECTOR_EXT, GL_LOCAL_EXT, GL_FULL_RANGE_EXT, 1); 
		glShaderOp2EXT(GL_OP_CROSS_PRODUCT_EXT, R_TEMPV, I_OMEGA, V_POS); 
		glShaderOp2EXT(GL_OP_SUB_EXT, R_FORCE, I_FORCE, R_TEMPV); 
 
		// transform the force vector into local texture space 
		// (texture space is not necessarily orthonormal) 
		{ 
			float one = 1.0; 
			GLuint C_ONE = glGenSymbolsEXT(GL_SCALAR_EXT, GL_LOCAL_CONSTANT_EXT, GL_FULL_RANGE_EXT, 1); 
			glSetLocalConstantEXT(C_ONE, GL_FLOAT, &one); 
 
			GLuint R_FS  = glGenSymbolsEXT(GL_SCALAR_EXT, GL_LOCAL_EXT, GL_FULL_RANGE_EXT, 1); 
			GLuint R_FT  = glGenSymbolsEXT(GL_SCALAR_EXT, GL_LOCAL_EXT, GL_FULL_RANGE_EXT, 1); 
			GLuint R_ST  = glGenSymbolsEXT(GL_SCALAR_EXT, GL_LOCAL_EXT, GL_FULL_RANGE_EXT, 1); 
			GLuint R_DET = glGenSymbolsEXT(GL_SCALAR_EXT, GL_LOCAL_EXT, GL_FULL_RANGE_EXT, 1); 
 
			glShaderOp2EXT(GL_OP_DOT3_EXT, R_FS, R_FORCE, V_DS); 
			glShaderOp2EXT(GL_OP_DOT3_EXT, R_FT, R_FORCE, V_DT); 
			glShaderOp2EXT(GL_OP_DOT3_EXT, R_ST, V_DS, V_DT); 
 
			glShaderOp2EXT(GL_OP_MUL_EXT, R_TEMPS, R_ST, R_ST); 
			glShaderOp2EXT(GL_OP_SUB_EXT, R_TEMPS, C_ONE, R_TEMPS); 
			glShaderOp1EXT(GL_OP_RECIP_EXT, R_DET, R_TEMPS); 
 
			glShaderOp2EXT(GL_OP_MUL_EXT, R_TEMPS, R_FT, R_ST); 
			glShaderOp2EXT(GL_OP_SUB_EXT, R_TEMPS, R_FS, R_TEMPS); 
			glShaderOp2EXT(GL_OP_MUL_EXT, R_TEMPS, R_TEMPS, R_DET); 
			glInsertComponentEXT(GL_OUTPUT_TEXTURE_COORD1_EXT, R_TEMPS, 0); 
 
			glShaderOp2EXT(GL_OP_MUL_EXT, R_TEMPS, R_FS, R_ST); 
			glShaderOp2EXT(GL_OP_SUB_EXT, R_TEMPS, R_FT, R_TEMPS); 
			glShaderOp2EXT(GL_OP_MUL_EXT, R_TEMPS, R_TEMPS, R_DET); 
			glInsertComponentEXT(GL_OUTPUT_TEXTURE_COORD1_EXT, R_TEMPS, 1); 
		} 
 
		// damping factor 
		glShaderOp1EXT(GL_OP_MOV_EXT, GL_OUTPUT_TEXTURE_COORD2_EXT, I_DAMPING); 
	} 
	glEndVertexShaderEXT(); 
 
	////////////////////////////////////////////////////////////////////// 
	// fragment shader for updating offset map 
	m_fsUpdateOffset = glGenFragmentShadersATI(1); 
	glBindFragmentShaderATI(m_fsUpdateOffset); 
	glBeginFragmentShaderATI(); 
	{ 
		////////// first pass ////////// 
		glSampleMapATI(GL_REG_0_ATI, GL_TEXTURE0_ARB, GL_SWIZZLE_STR_ATI);    // offset 
		glPassTexCoordATI(GL_REG_1_ATI, GL_TEXTURE1_ARB, GL_SWIZZLE_STR_ATI); // force 
		glPassTexCoordATI(GL_REG_2_ATI, GL_TEXTURE2_ARB, GL_SWIZZLE_STR_ATI); // damping 
 
		// REG_2 = 0.5 * force + 0.5 
		glSetFragmentShaderConstantATI(GL_CON_0_ATI, D3DXVECTOR4(0.5, 0.5, 0, 0)); 
		glColorFragmentOp3ATI(GL_MAD_ATI,	GL_REG_1_ATI, GL_NONE, GL_NONE, 
											GL_REG_1_ATI, GL_NONE, GL_NONE, 
											GL_CON_0_ATI, GL_NONE, GL_NONE, 
											GL_CON_0_ATI, GL_NONE, GL_NONE); 
 
		// REG_0 = damping * REG_2 + (1 - damping) * offset; 
		glColorFragmentOp3ATI(GL_LERP_ATI,	GL_REG_0_ATI, GL_NONE, GL_NONE, 
											GL_REG_2_ATI, GL_NONE, GL_NONE, 
											GL_REG_1_ATI, GL_NONE, GL_NONE, 
											GL_REG_0_ATI, GL_NONE, GL_NONE); 
 
		////////// second pass ////////// 
		glSampleMapATI(GL_REG_1_ATI, GL_REG_0_ATI, GL_SWIZZLE_STR_ATI); // clamp 
		glColorFragmentOp1ATI(GL_MOV_ATI,	GL_REG_0_ATI, GL_NONE, GL_NONE, 
											GL_REG_1_ATI, GL_NONE, GL_NONE); 
	} 
	glEndFragmentShaderATI(); 
 
	////////////////////////////////////////////////////////////////////// 
	// vertex shader for updating normal map 
	m_vsUpdateNormal = glGenVertexShadersEXT(1); 
	glBindVertexShaderEXT(m_vsUpdateNormal); 
	glBeginVertexShaderEXT(); 
	{ 
		glShaderOp1EXT(GL_OP_MOV_EXT, GL_OUTPUT_VERTEX_EXT, V_POS); 
		glShaderOp1EXT(GL_OP_MOV_EXT, GL_OUTPUT_TEXTURE_COORD0_EXT, V_TEX); 
	} 
	glEndVertexShaderEXT(); 
 
	////////////////////////////////////////////////////////////////////// 
	// fragment shader for updating normal map 
	m_fsUpdateNormal = glGenFragmentShadersATI(1); 
	glBindFragmentShaderATI(m_fsUpdateNormal); 
	glBeginFragmentShaderATI(); 
	{ 
		////////// first pass ////////// 
		glSampleMapATI(GL_REG_0_ATI, GL_TEXTURE0_ARB, GL_SWIZZLE_STR_ATI); // offset 
		glColorFragmentOp1ATI(GL_MOV_ATI,	GL_REG_0_ATI, GL_NONE, GL_NONE, 
											GL_REG_0_ATI, GL_NONE, GL_NONE); 
		////////// second pass ////////// 
		glSampleMapATI(GL_REG_1_ATI, GL_REG_0_ATI, GL_SWIZZLE_STR_ATI); // normalize 
		glColorFragmentOp1ATI(GL_MOV_ATI,	GL_REG_0_ATI, GL_NONE, GL_NONE, 
											GL_REG_1_ATI, GL_NONE, GL_NONE); 
	} 
	glEndFragmentShaderATI(); 
 
	if (glGetError()) 
		return false; 
	return true; 
} 
 
 
//----------------------------------------------------------------------------- 
// Update 
//----------------------------------------------------------------------------- 
void NormalMap::Update(FurMesh *pMesh, float dt, const D3DXVECTOR4& gravity, 
					   const D3DXVECTOR4& velocity, const D3DXVECTOR4& omega) 
{ 
	// compute invariants 
	{ 
		// filter coefficient for calculating m_Acceleration 
		float filter = min(0.25, dt/0.1); 
 
		// translational m_Acceleration 
		m_Accel = (1.0 - filter) * m_Accel + filter * ((velocity - m_PrevVel) / dt); 
		m_PrevVel = velocity; 
 
		// translational force = gravity + air resistance + inertia 
		// (coefficients were determined experimentally) 
		glSetInvariantEXT(I_FORCE, GL_FLOAT, (void*)&(gravity - 0.12*velocity - 0.018*m_Accel)); 
 
		// angular m_Acceleration 
		m_OmegaAccel = (1.0 - filter) * m_OmegaAccel + filter * ((omega - m_PrevOmega) / dt); 
		m_PrevOmega = omega; 
 
		// angular force = -CrossProduct(I_OMEGA, vertex_pos) 
		glSetInvariantEXT(I_OMEGA, GL_FLOAT, (void*)&(0.12*omega + 0.018*m_OmegaAccel)); 
 
		// damping factor 
		float damping = max(0.0625, min(0.5, dt/0.3)); 
		glSetInvariantEXT(I_DAMPING, GL_FLOAT, &damping); 
	} 
 
	glPushAttrib(GL_ENABLE_BIT | GL_VIEWPORT_BIT); 
 
	glViewport(0, 0, m_Width, m_Height); 
	glDisable(GL_DEPTH_TEST); 
	glEnable(GL_VERTEX_SHADER_EXT); 
	glEnable(GL_FRAGMENT_SHADER_ATI); 
 
	// update offset map 
	{ 
		glPushAttrib(GL_TEXTURE_BIT); 
 
		glBindVertexShaderEXT(m_vsUpdateOffset); 
		glBindFragmentShaderATI(m_fsUpdateOffset); 
 
		glActiveTextureARB(GL_TEXTURE0_ARB); 
		glEnable(GL_TEXTURE_2D); 
		glBindTexture(GL_TEXTURE_2D, m_texOffset); 
		glActiveTextureARB(GL_TEXTURE1_ARB); 
		glEnable(GL_TEXTURE_2D); 
		glBindTexture(GL_TEXTURE_2D, m_texClamp); 
		glActiveTextureARB(GL_TEXTURE0_ARB); 
 
		pMesh->Draw(); 
		glFinish(); 
 
		glBindTexture(GL_TEXTURE_2D, m_texOffset); 
		glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, m_Width, m_Height); 
 
		glPopAttrib(); 
	} 
 
	// update normal map 
	{ 
		glPushAttrib(GL_TEXTURE_BIT); 
 
		glBindVertexShaderEXT(m_vsUpdateNormal); 
		glBindFragmentShaderATI(m_fsUpdateNormal); 
 
		glActiveTextureARB(GL_TEXTURE0_ARB); 
		glEnable(GL_TEXTURE_2D); 
		glBindTexture(GL_TEXTURE_2D, m_texOffset); 
		glActiveTextureARB(GL_TEXTURE1_ARB); 
		glEnable(GL_TEXTURE_2D); 
		glBindTexture(GL_TEXTURE_2D, m_texNormalize); 
		glActiveTextureARB(GL_TEXTURE0_ARB); 
 
		glBegin(GL_QUADS); 
		glTexCoord2f(0, 0); glVertex2f(-1, -1); 
		glTexCoord2f(1, 0); glVertex2f( 1, -1); 
		glTexCoord2f(1, 1); glVertex2f( 1,  1); 
		glTexCoord2f(0, 1); glVertex2f(-1,  1); 
		glEnd(); 
		glFinish(); 
 
		glBindTexture(GL_TEXTURE_2D, m_texNormal); 
		glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, m_Width, m_Height); 
 
		glPopAttrib(); 
	} 
 
	glPopAttrib(); 
} 
 
 
//----------------------------------------------------------------------------- 
// ResetInertia 
//----------------------------------------------------------------------------- 
void NormalMap::ResetInertia() 
{ 
	m_PrevVel    = D3DXVECTOR4(0, 0, 0, 0); 
	m_Accel      = D3DXVECTOR4(0, 0, 0, 0); 
	m_PrevOmega  = D3DXVECTOR4(0, 0, 0, 0); 
	m_OmegaAccel = D3DXVECTOR4(0, 0, 0, 0); 
} 
 
 
//----------------------------------------------------------------------------- 
// Bind 
//----------------------------------------------------------------------------- 
void NormalMap::Bind() const 
{ 
	glBindTexture(GL_TEXTURE_2D, m_texNormal); 
} 
 
//----------------------------------------------------------------------------- 
// BindOffsetMap 
//----------------------------------------------------------------------------- 
// binds offset map (only used for viewing) 
void NormalMap::BindOffsetMap() const 
{ 
	glBindTexture(GL_TEXTURE_2D, m_texOffset); 
}