/* * Mod_AliasBuildMeshesForFrame0 */ static void Mod_AliasBuildMeshesForFrame0( model_t *mod ) { int i, j, k; size_t size; maliasframe_t *frame; maliasmodel_t *aliasmodel = ( maliasmodel_t * )mod->extradata; frame = &aliasmodel->frames[0]; for( k = 0; k < aliasmodel->nummeshes; k++ ) { maliasmesh_t *mesh = &aliasmodel->meshes[k]; size = sizeof( vec3_t ) + sizeof( vec3_t ); // xyz and normals size += sizeof( vec4_t ); // s-vectors size *= mesh->numverts; mesh->xyzArray = ( vec3_t * )Mod_Malloc( mod, size ); mesh->normalsArray = ( vec3_t * )( ( qbyte * )mesh->xyzArray + mesh->numverts * sizeof( vec3_t ) ); mesh->sVectorsArray = ( vec4_t * )( ( qbyte * )mesh->normalsArray + mesh->numverts * sizeof( vec3_t ) ); for( i = 0; i < mesh->numverts; i++ ) { for( j = 0; j < 3; j++ ) mesh->xyzArray[i][j] = frame->translate[j] + frame->scale[j] * mesh->vertexes[i].point[j]; R_LatLongToNorm( mesh->vertexes[i].latlong, mesh->normalsArray[i] ); } R_BuildTangentVectors( mesh->numverts, mesh->xyzArray, mesh->normalsArray, mesh->stArray, mesh->numtris, mesh->elems, mesh->sVectorsArray ); if( glConfig.ext.vertex_buffer_object ) { // build a static vertex buffer object to be used for rendering simple models, such as items Mod_AliasBuildStaticVBOForMesh( mesh ); } } }
/* * R_RenderMeshGLSL_Material */ static void R_RenderMeshGLSL_Material( r_glslfeat_t programFeatures ) { int i; int tcgen, rgbgen; int state; int program, object; image_t *base, *normalmap, *glossmap, *decalmap, *entdecalmap; mat4x4_t unused; vec3_t lightDir = { 0.0f, 0.0f, 0.0f }; vec4_t ambient = { 0.0f, 0.0f, 0.0f, 0.0f }, diffuse = { 0.0f, 0.0f, 0.0f, 0.0f }; float offsetmappingScale, glossExponent; const superLightStyle_t *lightStyle = NULL; const mfog_t *fog = r_back.colorFog; shaderpass_t *pass = r_back.accumPasses[0]; qboolean applyDecal; // handy pointers base = pass->anim_frames[0]; normalmap = pass->anim_frames[1]; glossmap = pass->anim_frames[2]; decalmap = pass->anim_frames[3]; entdecalmap = pass->anim_frames[4]; tcgen = pass->tcgen; // store the original tcgen rgbgen = pass->rgbgen.type; // store the original rgbgen assert( normalmap ); if( normalmap->samples == 4 ) offsetmappingScale = r_offsetmapping_scale->value * r_back.currentShader->offsetmapping_scale; else // no alpha in normalmap, don't bother with offset mapping offsetmappingScale = 0; if( r_back.currentShader->gloss_exponent ) glossExponent = r_back.currentShader->gloss_exponent; else glossExponent = r_lighting_glossexponent->value; applyDecal = decalmap != NULL; if( ri.params & RP_CLIPPLANE ) programFeatures |= GLSL_COMMON_APPLY_CLIPPING; if( pass->flags & SHADERPASS_GRAYSCALE ) programFeatures |= GLSL_COMMON_APPLY_GRAYSCALE; if( fog ) { programFeatures |= GLSL_COMMON_APPLY_FOG; if( fog != ri.fog_eye ) programFeatures |= GLSL_COMMON_APPLY_FOG2; if( GL_IsAlphaBlending( pass->flags & GLSTATE_SRCBLEND_MASK, pass->flags & GLSTATE_DSTBLEND_MASK ) ) programFeatures |= GLSL_COMMON_APPLY_COLOR_FOG_ALPHA; } if( r_back.currentMeshBuffer->infokey > 0 && ( rgbgen != RGB_GEN_LIGHTING_DIFFUSE ) ) { if( !( r_offsetmapping->integer & 1 ) ) { offsetmappingScale = 0; } if( ri.params & RP_LIGHTMAP ) { programFeatures |= GLSL_MATERIAL_APPLY_BASETEX_ALPHA_ONLY; } if( ( ri.params & RP_DRAWFLAT ) && !( r_back.currentShader->flags & SHADER_NODRAWFLAT ) ) { programFeatures |= GLSL_COMMON_APPLY_DRAWFLAT|GLSL_MATERIAL_APPLY_BASETEX_ALPHA_ONLY; } } else if( ( r_back.currentMeshBuffer->sortkey & 3 ) == MB_POLY ) { // polys if( !( r_offsetmapping->integer & 2 ) ) offsetmappingScale = 0; R_BuildTangentVectors( r_backacc.numVerts, vertsArray, normalsArray, coordsArray, r_backacc.numElems/3, elemsArray, inSVectorsArray ); } else { // models and world lightingDiffuse materials if( r_back.currentMeshBuffer->infokey > 0 ) { if( !( r_offsetmapping->integer & 1 ) ) offsetmappingScale = 0; pass->rgbgen.type = RGB_GEN_VERTEX; } else { // models if( !( r_offsetmapping->integer & 4 ) ) offsetmappingScale = 0; #ifdef CELLSHADEDMATERIAL programFeatures |= GLSL_MATERIAL_APPLY_CELLSHADING; #endif #ifdef HALFLAMBERTLIGHTING programFeatures |= GLSL_MATERIAL_APPLY_HALFLAMBERT; #endif } } // add dynamic lights if( r_back.currentDlightBits ) { programFeatures |= R_DlightbitsToProgramFeatures(); } pass->tcgen = TC_GEN_BASE; R_BindShaderpass( pass, base, 0, &programFeatures ); // calculate vertex color R_ModifyColor( pass, applyDecal, r_back.currentMeshVBO != NULL ); // since R_ModifyColor has forcefully generated RGBA for the first vertex, // set the proper number of color elements here, so GL_COLOR_ARRAY will be enabled // before the DrawElements call if( !(pass->flags & SHADERPASS_NOCOLORARRAY) ) r_backacc.numColors = r_backacc.numVerts; // convert rgbgen and alphagen to GLSL feature defines programFeatures |= R_RGBAlphaGenToProgramFeatures( pass->rgbgen.type, pass->alphagen.type ); GL_TexEnv( GL_MODULATE ); // set shaderpass state (blending, depthwrite, etc) state = r_back.currentShaderState | ( pass->flags & r_back.currentShaderPassMask ) | GLSTATE_BLEND_MTEX; GL_SetState( state ); #if 1 // don't waste time on processing GLSL programs with zero colormask if( ( ri.params & RP_SHADOWMAPVIEW ) & !(programFeatures & GLSL_COMMON_APPLY_BONETRANSFORMS) ) { pass->tcgen = tcgen; // restore original tcgen R_FlushArrays(); return; } #endif // we only send S-vectors to GPU and recalc T-vectors as cross product // in vertex shader pass->tcgen = TC_GEN_SVECTORS; GL_Bind( 1, normalmap ); // normalmap GL_SetTexCoordArrayMode( GL_TEXTURE_COORD_ARRAY ); R_VertexTCBase( pass, 1, unused, NULL ); if( glossmap && r_lighting_glossintensity->value ) { programFeatures |= GLSL_MATERIAL_APPLY_SPECULAR; GL_Bind( 2, glossmap ); // gloss GL_SetTexCoordArrayMode( 0 ); } if( applyDecal ) { programFeatures |= GLSL_MATERIAL_APPLY_DECAL; if( ri.params & RP_LIGHTMAP ) { decalmap = r_blacktexture; programFeatures |= GLSL_MATERIAL_APPLY_DECAL_ADD; } else { // if no alpha, use additive blending if( decalmap->samples == 3 ) programFeatures |= GLSL_MATERIAL_APPLY_DECAL_ADD; } GL_Bind( 3, decalmap ); // decal GL_SetTexCoordArrayMode( 0 ); } if( entdecalmap ) { programFeatures |= GLSL_MATERIAL_APPLY_ENTITY_DECAL; // if no alpha, use additive blending if( entdecalmap->samples == 3 ) programFeatures |= GLSL_MATERIAL_APPLY_ENTITY_DECAL_ADD; GL_Bind( 4, entdecalmap ); // decal GL_SetTexCoordArrayMode( 0 ); } if( offsetmappingScale > 0 ) programFeatures |= r_offsetmapping_reliefmapping->integer ? GLSL_MATERIAL_APPLY_RELIEFMAPPING : GLSL_MATERIAL_APPLY_OFFSETMAPPING; if( r_back.currentMeshBuffer->infokey > 0 && ( rgbgen != RGB_GEN_LIGHTING_DIFFUSE ) ) { // world surface if( r_back.superLightStyle && r_back.superLightStyle->lightmapNum[0] >= 0 ) { lightStyle = r_back.superLightStyle; // bind lightmap textures and set program's features for lightstyles pass->tcgen = TC_GEN_LIGHTMAP; for( i = 0; i < MAX_LIGHTMAPS && lightStyle->lightmapStyles[i] != 255; i++ ) { r_back.lightmapStyleNum[i+4] = i; GL_Bind( i+4, r_worldbrushmodel->lightmapImages[lightStyle->lightmapNum[i]] ); // lightmap GL_SetTexCoordArrayMode( GL_TEXTURE_COORD_ARRAY ); R_VertexTCBase( pass, i+4, unused, NULL ); } programFeatures |= ( i * GLSL_MATERIAL_APPLY_LIGHTSTYLE0 ); if( i == 1 && !mapConfig.lightingIntensity ) { vec_t *rgb = r_lightStyles[lightStyle->lightmapStyles[0]].rgb; // GLSL_MATERIAL_APPLY_FB_LIGHTMAP indicates that there's no need to renormalize // the lighting vector for specular (saves 3 adds, 3 muls and 1 normalize per pixel) if( rgb[0] == 1 && rgb[1] == 1 && rgb[2] == 1 ) programFeatures |= GLSL_MATERIAL_APPLY_FB_LIGHTMAP; } if( !VectorCompare( mapConfig.ambient, vec3_origin ) ) { VectorCopy( mapConfig.ambient, ambient ); programFeatures |= GLSL_MATERIAL_APPLY_AMBIENT_COMPENSATION; } } } else { vec3_t temp; programFeatures |= GLSL_MATERIAL_APPLY_DIRECTIONAL_LIGHT; if( ( r_back.currentMeshBuffer->sortkey & 3 ) == MB_POLY ) { VectorCopy( r_polys[-r_back.currentMeshBuffer->infokey-1].normal, lightDir ); Vector4Set( ambient, 0, 0, 0, 0 ); Vector4Set( diffuse, 1, 1, 1, 1 ); } else if( ri.currententity ) { if( ri.currententity->flags & RF_FULLBRIGHT ) { Vector4Set( ambient, 1, 1, 1, 1 ); Vector4Set( diffuse, 1, 1, 1, 1 ); } else { if( r_back.currentMeshBuffer->infokey > 0 ) { programFeatures |= GLSL_MATERIAL_APPLY_DIRECTIONAL_LIGHT_MIX; if( r_back.overBrightBits ) programFeatures |= GLSL_COMMON_APPLY_OVERBRIGHT_SCALING; } if( ri.currententity->model && ri.currententity != r_worldent ) { // get weighted incoming direction of world and dynamic lights R_LightForOrigin( ri.currententity->lightingOrigin, temp, ambient, diffuse, ri.currententity->model->radius * ri.currententity->scale); } else { VectorSet( temp, 0.1f, 0.2f, 0.7f ); } if( ri.currententity->flags & RF_MINLIGHT ) { if( ambient[0] <= 0.1f || ambient[1] <= 0.1f || ambient[2] <= 0.1f ) VectorSet( ambient, 0.1f, 0.1f, 0.1f ); } // rotate direction Matrix_TransformVector( ri.currententity->axis, temp, lightDir ); } } } pass->tcgen = tcgen; // restore original tcgen pass->rgbgen.type = rgbgen; // restore original rgbgen program = R_RegisterGLSLProgram( pass->program_type, pass->program, NULL, r_back.currentShader->name, r_back.currentShader->deforms, r_back.currentShader->numdeforms, programFeatures ); object = R_GetProgramObject( program ); if( object ) { qglUseProgramObjectARB( object ); // update uniforms R_UpdateProgramUniforms( program, ri.viewOrigin, vec3_origin, lightDir, ambient, diffuse, lightStyle, qtrue, 0, 0, 0, offsetmappingScale, glossExponent, colorArrayCopy[0], r_back.overBrightBits, r_back.currentShaderTime, r_back.entityColor ); if( programFeatures & GLSL_COMMON_APPLY_FOG ) { cplane_t fogPlane, vpnPlane; R_TransformFogPlanes( fog, fogPlane.normal, &fogPlane.dist, vpnPlane.normal, &vpnPlane.dist ); R_UpdateProgramFogParams( program, fog->shader->fog_color, fog->shader->fog_clearDist, fog->shader->fog_dist, &fogPlane, &vpnPlane, ri.fog_dist_to_eye[fog-r_worldbrushmodel->fogs] ); } // submit animation data if( programFeatures & GLSL_COMMON_APPLY_BONETRANSFORMS ) { R_UpdateProgramBonesParams( program, r_back.currentAnimData->numBones, r_back.currentAnimData->dualQuats ); } // dynamic lights if( r_back.currentDlightBits ) { R_UpdateProgramLightsParams( program, ri.currententity->origin, ri.currententity->axis, r_back.currentDlightBits ); r_back.doDynamicLightsPass = qfalse; } // r_drawflat if( programFeatures & GLSL_COMMON_APPLY_DRAWFLAT ) { R_UpdateDrawFlatParams( program, r_front.wallColor, r_front.floorColor ); } R_FlushArrays(); qglUseProgramObjectARB( 0 ); } }
/* * Mod_LoadSkeletalModel */ void Mod_LoadSkeletalModel( model_t *mod, const model_t *parent, void *buffer, bspFormatDesc_t *unused ) { unsigned int i, j, k; size_t filesize; qbyte *pbase; size_t memsize; qbyte *pmem; iqmheader_t *header; char *texts; iqmvertexarray_t *va; iqmjoint_t *joints; bonepose_t *baseposes; iqmpose_t *poses; unsigned short *framedata; const int *inelems; elem_t *outelems; iqmmesh_t *inmesh; iqmbounds_t *inbounds; float *vposition, *vtexcoord, *vnormal, *vtangent; qbyte *vblendindices_byte, *vblendweights_byte; int *vblendindexes_int; float *vblendweights_float; mskmodel_t *poutmodel; baseposes = NULL; header = ( iqmheader_t * )buffer; // check IQM magic if( memcmp( header->magic, "INTERQUAKEMODEL", 16 ) ) { ri.Com_Printf( S_COLOR_RED "ERROR: %s is not an Inter-Quake Model\n", mod->name ); goto error; } // check header version header->version = LittleLong( header->version ); if( header->version != IQM_VERSION ) { ri.Com_Printf( S_COLOR_RED "ERROR: %s has wrong type number (%i should be %i)\n", mod->name, header->version, IQM_VERSION ); goto error; } // byteswap header #define H_SWAP(s) (header->s = LittleLong( header->s )) H_SWAP( filesize ); H_SWAP( flags ); H_SWAP( num_text ); H_SWAP( ofs_text ); H_SWAP( num_meshes ); H_SWAP( ofs_meshes ); H_SWAP( num_vertexarrays ); H_SWAP( num_vertexes ); H_SWAP( ofs_vertexarrays ); H_SWAP( num_triangles ); H_SWAP( ofs_triangles ); H_SWAP( ofs_adjacency ); H_SWAP( num_joints ); H_SWAP( ofs_joints ); H_SWAP( num_poses ); H_SWAP( ofs_poses ); H_SWAP( num_anims ); H_SWAP( ofs_anims ); H_SWAP( num_frames ); H_SWAP( num_framechannels ); H_SWAP( ofs_frames ); H_SWAP( ofs_bounds ); H_SWAP( num_comment ); H_SWAP( ofs_comment ); H_SWAP( num_extensions ); H_SWAP( ofs_extensions ); #undef H_SWAP if( header->num_triangles < 1 || header->num_vertexes < 3 || header->num_vertexarrays < 1 || header->num_meshes < 1 ) { ri.Com_Printf( S_COLOR_RED "ERROR: %s has no geometry\n", mod->name ); goto error; } if( header->num_frames < 1 || header->num_anims < 1 ) { ri.Com_Printf( S_COLOR_RED "ERROR: %s has no animations\n", mod->name ); goto error; } if( header->num_joints != header->num_poses ) { ri.Com_Printf( S_COLOR_RED "ERROR: %s has an invalid number of poses: %i vs %i\n", mod->name, header->num_joints, header->num_poses ); goto error; } if( !header->ofs_bounds ) { ri.Com_Printf( S_COLOR_RED "ERROR: %s has no frame bounds\n", mod->name ); goto error; } pbase = ( qbyte * )buffer; filesize = header->filesize; // check data offsets against the filesize if( header->ofs_text + header->num_text > filesize || header->ofs_vertexarrays + header->num_vertexarrays * sizeof( iqmvertexarray_t ) > filesize || header->ofs_joints + header->num_joints * sizeof( iqmjoint_t ) > filesize || header->ofs_frames + header->num_frames * header->num_framechannels * sizeof( unsigned short ) > filesize || header->ofs_triangles + header->num_triangles * sizeof( int[3] ) > filesize || header->ofs_meshes + header->num_meshes * sizeof( iqmmesh_t ) > filesize || header->ofs_bounds + header->num_frames * sizeof( iqmbounds_t ) > filesize ) { ri.Com_Printf( S_COLOR_RED "ERROR: %s has invalid size or offset information\n", mod->name ); goto error; } poutmodel = mod->extradata = Mod_Malloc( mod, sizeof( *poutmodel ) ); // load text texts = Mod_Malloc( mod, header->num_text + 1 ); if( header->ofs_text ) { memcpy( texts, (const char *)(pbase + header->ofs_text), header->num_text ); } texts[header->ofs_text] = '\0'; // load vertex arrays vposition = NULL; vtexcoord = NULL; vnormal = NULL; vtangent = NULL; vblendindices_byte = NULL; vblendindexes_int = NULL; vblendweights_byte = NULL; vblendweights_float = NULL; va = ( iqmvertexarray_t * )( pbase + header->ofs_vertexarrays ); for( i = 0; i < header->num_vertexarrays; i++ ) { size_t vsize; va[i].type = LittleLong( va[i].type ); va[i].flags = LittleLong( va[i].flags ); va[i].format = LittleLong( va[i].format ); va[i].size = LittleLong( va[i].size ); va[i].offset = LittleLong( va[i].offset ); vsize = header->num_vertexes*va[i].size; switch( va[i].format ) { case IQM_FLOAT: vsize *= sizeof( float ); break; case IQM_INT: case IQM_UINT: vsize *= sizeof( int ); break; case IQM_BYTE: case IQM_UBYTE: vsize *= sizeof( unsigned char ); break; default: continue; } if( va[i].offset + vsize > filesize ) { continue; } switch( va[i].type ) { case IQM_POSITION: if( va[i].format == IQM_FLOAT && va[i].size == 3 ) { vposition = ( float * )( pbase + va[i].offset ); } break; case IQM_TEXCOORD: if( va[i].format == IQM_FLOAT && va[i].size == 2 ) { vtexcoord = ( float * )( pbase + va[i].offset ); } break; case IQM_NORMAL: if( va[i].format == IQM_FLOAT && va[i].size == 3 ) { vnormal = ( float * )( pbase + va[i].offset ); } break; case IQM_TANGENT: if( va[i].format == IQM_FLOAT && va[i].size == 4 ) { vtangent = ( float * )( pbase + va[i].offset ); } break; case IQM_BLENDINDEXES: if( va[i].size != SKM_MAX_WEIGHTS ) break; if( va[i].format == IQM_BYTE || va[i].format == IQM_UBYTE ) { vblendindices_byte = ( qbyte * )( pbase + va[i].offset ); } else if( va[i].format == IQM_INT || va[i].format == IQM_UINT ) { vblendindexes_int = ( int * )( pbase + va[i].offset ); } break; case IQM_BLENDWEIGHTS: if( va[i].size != SKM_MAX_WEIGHTS ) break; if( va[i].format == IQM_UBYTE ) { vblendweights_byte = ( qbyte * )( pbase + va[i].offset ); } else if( va[i].format == IQM_FLOAT ) { vblendweights_float = ( float * )( pbase + va[i].offset ); } break; default: break; } } if( !vposition || !vtexcoord || !(vblendindices_byte || vblendindexes_int) || !(vblendweights_byte || vblendweights_float) ) { ri.Com_Printf( S_COLOR_RED "ERROR: %s is missing vertex array data\n", mod->name ); goto error; } // load joints memsize = 0; memsize += sizeof( bonepose_t ) * header->num_joints; pmem = Mod_Malloc( mod, memsize ); baseposes = ( void * )pmem; pmem += sizeof( *baseposes ); memsize = 0; memsize += sizeof( mskbone_t ) * header->num_joints; memsize += sizeof( bonepose_t ) * header->num_joints; pmem = Mod_Malloc( mod, memsize ); poutmodel->numbones = header->num_joints; poutmodel->bones = ( void * )pmem; pmem += sizeof( *poutmodel->bones ) * poutmodel->numbones; poutmodel->invbaseposes = ( void * )pmem; pmem += sizeof( *poutmodel->invbaseposes ) * poutmodel->numbones; joints = ( iqmjoint_t * )( pbase + header->ofs_joints ); for( i = 0; i < poutmodel->numbones; i++ ) { joints[i].name = LittleLong( joints[i].name ); joints[i].parent = LittleLong( joints[i].parent ); for( j = 0; j < 3; j++ ) { joints[i].translate[j] = LittleFloat( joints[i].translate[j] ); joints[i].rotate[j] = LittleFloat( joints[i].rotate[j] ); joints[i].scale[j] = LittleFloat( joints[i].scale[j] ); } if( joints[i].parent >= (int)i ) { ri.Com_Printf( S_COLOR_RED "ERROR: %s bone[%i].parent(%i) >= %i\n", mod->name, i, joints[i].parent, i ); goto error; } poutmodel->bones[i].name = texts + joints[i].name; poutmodel->bones[i].parent = joints[i].parent; DualQuat_FromQuat3AndVector( joints[i].rotate, joints[i].translate, baseposes[i].dualquat ); // scale is unused // reconstruct invserse bone pose if( joints[i].parent >= 0 ) { bonepose_t bp, *pbp; bp = baseposes[i]; pbp = &baseposes[joints[i].parent]; DualQuat_Multiply( pbp->dualquat, bp.dualquat, baseposes[i].dualquat ); } DualQuat_Copy( baseposes[i].dualquat, poutmodel->invbaseposes[i].dualquat ); DualQuat_Invert( poutmodel->invbaseposes[i].dualquat ); } // load frames poses = ( iqmpose_t * )( pbase + header->ofs_poses ); for( i = 0; i < header->num_poses; i++ ) { poses[i].parent = LittleLong( poses[i].parent ); poses[i].mask = LittleLong( poses[i].mask ); for( j = 0; j < 10; j++ ) { poses[i].channeloffset[j] = LittleFloat( poses[i].channeloffset[j] ); poses[i].channelscale[j] = LittleFloat( poses[i].channelscale[j] ); } } memsize = 0; memsize += sizeof( mskframe_t ) * header->num_frames; memsize += sizeof( bonepose_t ) * header->num_joints * header->num_frames; pmem = Mod_Malloc( mod, memsize ); poutmodel->numframes = header->num_frames; poutmodel->frames = ( mskframe_t * )pmem; pmem += sizeof( mskframe_t ) * poutmodel->numframes; framedata = ( unsigned short * )( pbase + header->ofs_frames ); for( i = 0; i < header->num_frames; i++ ) { bonepose_t *pbp; vec3_t translate; quat_t rotate; poutmodel->frames[i].boneposes = ( bonepose_t * )pmem; pmem += sizeof( bonepose_t ) * poutmodel->numbones; for( j = 0, pbp = poutmodel->frames[i].boneposes; j < header->num_poses; j++, pbp++ ) { translate[0] = poses[j].channeloffset[0]; if( poses[j].mask & 0x01 ) translate[0] += *framedata++ * poses[j].channelscale[0]; translate[1] = poses[j].channeloffset[1]; if( poses[j].mask & 0x02 ) translate[1] += *framedata++ * poses[j].channelscale[1]; translate[2] = poses[j].channeloffset[2]; if( poses[j].mask & 0x04 ) translate[2] += *framedata++ * poses[j].channelscale[2]; rotate[0] = poses[j].channeloffset[3]; if( poses[j].mask & 0x08 ) rotate[0] += *framedata++ * poses[j].channelscale[3]; rotate[1] = poses[j].channeloffset[4]; if( poses[j].mask & 0x10 ) rotate[1] += *framedata++ * poses[j].channelscale[4]; rotate[2] = poses[j].channeloffset[5]; if( poses[j].mask & 0x20 ) rotate[2] += *framedata++ * poses[j].channelscale[5]; rotate[3] = poses[j].channeloffset[6]; if( poses[j].mask & 0x40 ) rotate[3] += *framedata++ * poses[j].channelscale[6]; if( rotate[3] > 0 ) { Vector4Inverse( rotate ); } Vector4Normalize( rotate ); // scale is unused if( poses[j].mask & 0x80 ) framedata++; if( poses[j].mask & 0x100 ) framedata++; if( poses[j].mask & 0x200 ) framedata++; DualQuat_FromQuatAndVector( rotate, translate, pbp->dualquat ); } } // load triangles memsize = 0; memsize += sizeof( *outelems ) * header->num_triangles * 3; pmem = Mod_Malloc( mod, memsize ); poutmodel->numtris = header->num_triangles; poutmodel->elems = ( elem_t * )pmem; pmem += sizeof( *outelems ) * header->num_triangles * 3; inelems = ( const int * )(pbase + header->ofs_triangles); outelems = poutmodel->elems; for( i = 0; i < header->num_triangles; i++ ) { for( j = 0; j < 3; j++ ) { outelems[j] = LittleLong( inelems[j] ); } inelems += 3; outelems += 3; } // load vertices memsize = 0; memsize += sizeof( *poutmodel->sVectorsArray ) * header->num_vertexes; // 16-bytes aligned memsize += sizeof( *poutmodel->xyzArray ) * header->num_vertexes; memsize += sizeof( *poutmodel->normalsArray ) * header->num_vertexes; memsize += sizeof( *poutmodel->stArray ) * header->num_vertexes; memsize += sizeof( *poutmodel->blendWeights ) * header->num_vertexes * SKM_MAX_WEIGHTS; memsize += sizeof( *poutmodel->blendIndices ) * header->num_vertexes * SKM_MAX_WEIGHTS; pmem = Mod_Malloc( mod, memsize ); poutmodel->numverts = header->num_vertexes; // S-vectors poutmodel->sVectorsArray = ( vec4_t * )pmem; pmem += sizeof( *poutmodel->sVectorsArray ) * header->num_vertexes; if( vtangent ) { for( i = 0; i < header->num_vertexes; i++ ) { for( j = 0; j < 4; j++ ) { poutmodel->sVectorsArray[i][j] = LittleFloat( vtangent[j] ); } vtangent += 4; } } // XYZ positions poutmodel->xyzArray = ( vec4_t * )pmem; pmem += sizeof( *poutmodel->xyzArray ) * header->num_vertexes; for( i = 0; i < header->num_vertexes; i++ ) { for( j = 0; j < 3; j++ ) { poutmodel->xyzArray[i][j] = LittleFloat( vposition[j] ); } poutmodel->xyzArray[i][3] = 1; vposition += 3; } // normals poutmodel->normalsArray = ( vec4_t * )pmem; pmem += sizeof( *poutmodel->normalsArray ) * header->num_vertexes; for( i = 0; i < header->num_vertexes; i++ ) { for( j = 0; j < 3; j++ ) { poutmodel->normalsArray[i][j] = LittleFloat( vnormal[j] ); } poutmodel->normalsArray[i][3] = 0; vnormal += 3; } // texture coordinates poutmodel->stArray = ( vec2_t * )pmem; pmem += sizeof( *poutmodel->stArray ) * header->num_vertexes; for( i = 0; i < header->num_vertexes; i++ ) { for( j = 0; j < 2; j++ ) { poutmodel->stArray[i][j] = LittleFloat( vtexcoord[j] ); } vtexcoord += 2; } if( !vtangent ) { // if the loaded file is missing precomputed S-vectors, compute them now R_BuildTangentVectors( poutmodel->numverts, poutmodel->xyzArray, poutmodel->normalsArray, poutmodel->stArray, poutmodel->numtris, poutmodel->elems, poutmodel->sVectorsArray ); } // blend indices poutmodel->blendIndices = ( qbyte * )pmem; pmem += sizeof( *poutmodel->blendIndices ) * header->num_vertexes * SKM_MAX_WEIGHTS; if( vblendindices_byte ) { memcpy( poutmodel->blendIndices, vblendindices_byte, sizeof( qbyte ) * header->num_vertexes * SKM_MAX_WEIGHTS ); } else if( vblendindexes_int ) { for( j = 0; j < header->num_vertexes * SKM_MAX_WEIGHTS; j++ ) { poutmodel->blendIndices[j] = LittleLong( vblendindexes_int[j] ); } } // blend weights poutmodel->blendWeights = ( qbyte * )pmem; pmem += sizeof( *poutmodel->blendWeights ) * header->num_vertexes * SKM_MAX_WEIGHTS; if( vblendweights_byte ) { memcpy( poutmodel->blendWeights, vblendweights_byte, sizeof( qbyte ) * header->num_vertexes * SKM_MAX_WEIGHTS ); } else if( vblendweights_float ) { for( j = 0; j < header->num_vertexes * SKM_MAX_WEIGHTS; j++ ) { poutmodel->blendWeights[j] = LittleFloat( vblendweights_float[j] ) * 255.0f; } } // blends memsize = 0; memsize += poutmodel->numverts * ( sizeof( mskblend_t ) + sizeof( unsigned int ) ); pmem = Mod_Malloc( mod, memsize ); poutmodel->numblends = 0; poutmodel->blends = ( mskblend_t * )pmem; pmem += sizeof( *poutmodel->blends ) * poutmodel->numverts; poutmodel->vertexBlends = ( unsigned int * )pmem; vblendindices_byte = poutmodel->blendIndices; vblendweights_byte = poutmodel->blendWeights; for( i = 0; i < poutmodel->numverts; i++ ) { mskblend_t blend; for( j = 0; j < SKM_MAX_WEIGHTS; j++ ) { blend.indices[j] = vblendindices_byte[j]; blend.weights[j] = vblendweights_byte[j]; } poutmodel->vertexBlends[i] = Mod_SkeletalModel_AddBlend( poutmodel, &blend ); vblendindices_byte += SKM_MAX_WEIGHTS; vblendweights_byte += SKM_MAX_WEIGHTS; } // meshes memsize = 0; memsize += sizeof( mskmesh_t ) * header->num_meshes; memsize += sizeof( drawSurfaceSkeletal_t ) * header->num_meshes; pmem = Mod_Malloc( mod, memsize ); poutmodel->nummeshes = header->num_meshes; poutmodel->meshes = ( mskmesh_t * )pmem; pmem += sizeof( *poutmodel->meshes ) * header->num_meshes; inmesh = ( iqmmesh_t * )(pbase + header->ofs_meshes); for( i = 0; i < header->num_meshes; i++ ) { inmesh[i].name = LittleLong( inmesh[i].name ); inmesh[i].material = LittleLong( inmesh[i].material ); inmesh[i].first_vertex = LittleLong( inmesh[i].first_vertex ); inmesh[i].num_vertexes = LittleLong( inmesh[i].num_vertexes ); inmesh[i].first_triangle = LittleLong( inmesh[i].first_triangle ); inmesh[i].num_triangles = LittleLong( inmesh[i].num_triangles ); poutmodel->meshes[i].name = texts + inmesh[i].name; Mod_StripLODSuffix( poutmodel->meshes[i].name ); poutmodel->meshes[i].skin.name = texts + inmesh[i].material; poutmodel->meshes[i].skin.shader = R_RegisterSkin( poutmodel->meshes[i].skin.name ); poutmodel->meshes[i].elems = poutmodel->elems + inmesh[i].first_triangle * 3; poutmodel->meshes[i].numtris = inmesh[i].num_triangles; poutmodel->meshes[i].numverts = inmesh[i].num_vertexes; poutmodel->meshes[i].xyzArray = poutmodel->xyzArray + inmesh[i].first_vertex; poutmodel->meshes[i].normalsArray = poutmodel->normalsArray + inmesh[i].first_vertex; poutmodel->meshes[i].stArray = poutmodel->stArray + inmesh[i].first_vertex; poutmodel->meshes[i].sVectorsArray = poutmodel->sVectorsArray + inmesh[i].first_vertex; poutmodel->meshes[i].blendIndices = poutmodel->blendIndices + inmesh[i].first_vertex * SKM_MAX_WEIGHTS; poutmodel->meshes[i].blendWeights = poutmodel->blendWeights + inmesh[i].first_vertex * SKM_MAX_WEIGHTS; poutmodel->meshes[i].vertexBlends = poutmodel->vertexBlends + inmesh[i].first_vertex; // elements are always offset to start vertex 0 for each mesh outelems = poutmodel->meshes[i].elems; for( j = 0; j < poutmodel->meshes[i].numtris; j++ ) { outelems[0] -= inmesh[i].first_vertex; outelems[1] -= inmesh[i].first_vertex; outelems[2] -= inmesh[i].first_vertex; outelems += 3; } poutmodel->meshes[i].maxWeights = 1; vblendweights_byte = poutmodel->meshes[i].blendWeights; for( j = 0; j < poutmodel->meshes[i].numverts; j++ ) { for( k = 1; k < SKM_MAX_WEIGHTS && vblendweights_byte[k]; k++ ); if( k > poutmodel->meshes[i].maxWeights ) { poutmodel->meshes[i].maxWeights = k; if( k == SKM_MAX_WEIGHTS ) { break; } } vblendweights_byte += SKM_MAX_WEIGHTS; } // creating a VBO only makes sense if GLSL is present and the number of bones // we can handle on the GPU is sufficient if( glConfig.ext.vertex_buffer_object && poutmodel->numbones <= glConfig.maxGLSLBones ) { // build a static vertex buffer object for this mesh Mod_SkeletalBuildStaticVBOForMesh( &poutmodel->meshes[i] ); } } poutmodel->drawSurfs = ( drawSurfaceSkeletal_t * )pmem; pmem += sizeof( *poutmodel->drawSurfs ) * header->num_meshes; for( i = 0; i < header->num_meshes; i++ ) { poutmodel->drawSurfs[i].type = ST_SKELETAL; poutmodel->drawSurfs[i].model = mod; poutmodel->drawSurfs[i].mesh = poutmodel->meshes + i; } // bounds ClearBounds( mod->mins, mod->maxs ); inbounds = ( iqmbounds_t * )(pbase + header->ofs_bounds); for( i = 0; i < header->num_frames; i++ ) { for( j = 0; j < 3; j++ ) { inbounds[i].bbmin[j] = LittleFloat( inbounds[i].bbmin[j] ); inbounds[i].bbmax[j] = LittleFloat( inbounds[i].bbmax[j] ); } inbounds[i].radius = LittleFloat( inbounds[i].radius ); inbounds[i].xyradius = LittleFloat( inbounds[i].xyradius ); VectorCopy( inbounds[i].bbmin, poutmodel->frames[i].mins ); VectorCopy( inbounds[i].bbmax, poutmodel->frames[i].maxs ); poutmodel->frames[i].radius = inbounds[i].radius; AddPointToBounds( poutmodel->frames[i].mins, mod->mins, mod->maxs ); AddPointToBounds( poutmodel->frames[i].maxs, mod->mins, mod->maxs ); } mod->radius = RadiusFromBounds( mod->mins, mod->maxs ); mod->type = mod_skeletal; mod->registrationSequence = rsh.registrationSequence; mod->touch = &Mod_TouchSkeletalModel; R_Free( baseposes ); return; error: if( baseposes ) { R_Free( baseposes ); } mod->type = mod_bad; }
/* * R_DrawAliasSurf * * Interpolates between two frames and origins */ qboolean R_DrawAliasSurf( const entity_t *e, const shader_t *shader, const mfog_t *fog, drawSurfaceAlias_t *drawSurf ) { int i; int framenum, oldframenum; float backv[3], frontv[3]; vec3_t normal, oldnormal; qboolean calcVerts, calcNormals, calcSTVectors; vec3_t move; const maliasframe_t *frame, *oldframe; const maliasvertex_t *v, *ov; float backlerp = e->backlerp; const maliasmodel_t *model = ( const maliasmodel_t * )drawSurf->model->extradata; const maliasmesh_t *aliasmesh = drawSurf->mesh; vattribmask_t vattribs; // see what vertex attribs backend needs vattribs = RB_GetVertexAttribs(); framenum = bound( e->frame, 0, model->numframes ); oldframenum = bound( e->oldframe, 0, model->numframes ); frame = model->frames + framenum; oldframe = model->frames + oldframenum; for( i = 0; i < 3; i++ ) move[i] = frame->translate[i] + ( oldframe->translate[i] - frame->translate[i] ) * backlerp; // based on backend's needs calcNormals = calcSTVectors = qfalse; calcNormals = ( ( vattribs & VATTRIB_NORMAL_BIT ) != 0 ) && ( ( framenum != 0 ) || ( oldframenum != 0 ) ); calcSTVectors = ( ( vattribs & VATTRIB_SVECTOR_BIT ) != 0 ) && calcNormals; if( aliasmesh->vbo != NULL && !framenum && !oldframenum ) { RB_BindVBO( aliasmesh->vbo->index, GL_TRIANGLES ); RB_DrawElements( 0, aliasmesh->numverts, 0, aliasmesh->numtris * 3 ); } else { mesh_t *rb_mesh; vec3_t *inVertsArray; vec3_t *inNormalsArray; vec4_t *inSVectorsArray; RB_BindVBO( RB_VBO_STREAM, GL_TRIANGLES ); rb_mesh = RB_MapBatchMesh( aliasmesh->numverts, aliasmesh->numtris * 3 ); if( !rb_mesh ) { ri.Com_DPrintf( S_COLOR_YELLOW "R_DrawAliasSurf: RB_MapBatchMesh returned NULL for (%s)(%s)", drawSurf->model->name, aliasmesh->name ); return qfalse; } inVertsArray = rb_mesh->xyzArray; inNormalsArray = rb_mesh->normalsArray; inSVectorsArray = rb_mesh->sVectorsArray; if( !framenum && !oldframenum ) { calcVerts = qfalse; if( calcNormals ) { v = aliasmesh->vertexes; for( i = 0; i < aliasmesh->numverts; i++, v++ ) R_LatLongToNorm( v->latlong, inNormalsArray[i] ); } } else if( framenum == oldframenum ) { calcVerts = qtrue; for( i = 0; i < 3; i++ ) frontv[i] = frame->scale[i]; v = aliasmesh->vertexes + framenum * aliasmesh->numverts; for( i = 0; i < aliasmesh->numverts; i++, v++ ) { VectorSet( inVertsArray[i], move[0] + v->point[0]*frontv[0], move[1] + v->point[1]*frontv[1], move[2] + v->point[2]*frontv[2] ); if( calcNormals ) R_LatLongToNorm( v->latlong, inNormalsArray[i] ); } } else { calcVerts = qtrue; for( i = 0; i < 3; i++ ) { backv[i] = backlerp * oldframe->scale[i]; frontv[i] = ( 1.0f - backlerp ) * frame->scale[i]; } v = aliasmesh->vertexes + framenum * aliasmesh->numverts; ov = aliasmesh->vertexes + oldframenum * aliasmesh->numverts; for( i = 0; i < aliasmesh->numverts; i++, v++, ov++ ) { VectorSet( inVertsArray[i], move[0] + v->point[0]*frontv[0] + ov->point[0]*backv[0], move[1] + v->point[1]*frontv[1] + ov->point[1]*backv[1], move[2] + v->point[2]*frontv[2] + ov->point[2]*backv[2] ); if( calcNormals ) { R_LatLongToNorm( v->latlong, normal ); R_LatLongToNorm( ov->latlong, oldnormal ); VectorSet( inNormalsArray[i], normal[0] + ( oldnormal[0] - normal[0] ) * backlerp, normal[1] + ( oldnormal[1] - normal[1] ) * backlerp, normal[2] + ( oldnormal[2] - normal[2] ) * backlerp ); } } } if( calcSTVectors ) R_BuildTangentVectors( aliasmesh->numverts, inVertsArray, inNormalsArray, aliasmesh->stArray, aliasmesh->numtris, aliasmesh->elems, inSVectorsArray ); if( !calcVerts ) { rb_mesh->xyzArray = aliasmesh->xyzArray; } rb_mesh->elems = aliasmesh->elems; rb_mesh->numElems = aliasmesh->numtris * 3; rb_mesh->numVerts = aliasmesh->numverts; rb_mesh->stArray = aliasmesh->stArray; if( !calcNormals ) { rb_mesh->normalsArray = aliasmesh->normalsArray; } if( !calcSTVectors ) { rb_mesh->sVectorsArray = aliasmesh->sVectorsArray; } RB_UploadMesh( rb_mesh ); RB_EndBatch(); } return qfalse; }
/* * R_DrawAliasFrameLerp * * Interpolates between two frames and origins */ static void R_DrawAliasFrameLerp( const meshbuffer_t *mb, float backlerp ) { int i, meshnum; int features; float backv[3], frontv[3]; vec3_t normal, oldnormal; qboolean calcVerts, calcNormals, calcSTVectors; vec3_t move; maliasframe_t *frame, *oldframe; maliasmesh_t *mesh; maliasvertex_t *v, *ov; entity_t *e; model_t *mod; maliasmodel_t *model; shader_t *shader; MB_NUM2ENTITY( mb->sortkey, e ); mod = Mod_ForHandle( mb->LODModelHandle ); model = ( maliasmodel_t * )mod->extradata; meshnum = -mb->infokey - 1; if( meshnum < 0 || meshnum >= model->nummeshes ) return; mesh = model->meshes + meshnum; frame = model->frames + e->frame; oldframe = model->frames + e->oldframe; for( i = 0; i < 3; i++ ) move[i] = frame->translate[i] + ( oldframe->translate[i] - frame->translate[i] ) * backlerp; MB_NUM2SHADER( mb->shaderkey, shader ); features = MF_NONBATCHED | shader->features; if( !mb->vboIndex ) { features &= ~MF_HARDWARE; } if( ri.params & RP_SHADOWMAPVIEW ) { features &= ~( MF_COLORS|MF_SVECTORS|MF_ENABLENORMALS ); if( !( shader->features & MF_DEFORMVS ) ) features &= ~MF_NORMALS; } else { if( features & MF_SVECTORS ) features |= MF_NORMALS; #ifdef HARDWARE_OUTLINES if( e->outlineHeight ) features |= MF_NORMALS|(glConfig.ext.GLSL ? MF_ENABLENORMALS : 0); #endif } calcNormals = calcSTVectors = qfalse; calcNormals = ( ( features & MF_NORMALS ) != 0 ) && ( ( e->frame != 0 ) || ( e->oldframe != 0 ) ); calcSTVectors = ( ( features & MF_SVECTORS ) != 0 ) && calcNormals; if( mb->vboIndex != 0 ) { calcVerts = calcNormals = calcSTVectors = qfalse; } else { if( !e->frame && !e->oldframe ) { calcVerts = qfalse; if( calcNormals ) { v = mesh->vertexes; for( i = 0; i < mesh->numverts; i++, v++ ) R_LatLongToNorm( v->latlong, inNormalsArray[i] ); } } else if( e->frame == e->oldframe ) { calcVerts = qtrue; for( i = 0; i < 3; i++ ) frontv[i] = frame->scale[i]; v = mesh->vertexes + e->frame * mesh->numverts; for( i = 0; i < mesh->numverts; i++, v++ ) { Vector4Set( inVertsArray[i], move[0] + v->point[0]*frontv[0], move[1] + v->point[1]*frontv[1], move[2] + v->point[2]*frontv[2], 1 ); if( calcNormals ) R_LatLongToNorm( v->latlong, inNormalsArray[i] ); } } else { calcVerts = qtrue; for( i = 0; i < 3; i++ ) { backv[i] = backlerp * oldframe->scale[i]; frontv[i] = ( 1.0f - backlerp ) * frame->scale[i]; } v = mesh->vertexes + e->frame * mesh->numverts; ov = mesh->vertexes + e->oldframe * mesh->numverts; for( i = 0; i < mesh->numverts; i++, v++, ov++ ) { Vector4Set( inVertsArray[i], move[0] + v->point[0]*frontv[0] + ov->point[0]*backv[0], move[1] + v->point[1]*frontv[1] + ov->point[1]*backv[1], move[2] + v->point[2]*frontv[2] + ov->point[2]*backv[2], 1 ); if( calcNormals ) { R_LatLongToNorm( v->latlong, normal ); R_LatLongToNorm( ov->latlong, oldnormal ); VectorSet( inNormalsArray[i], normal[0] + ( oldnormal[0] - normal[0] ) * backlerp, normal[1] + ( oldnormal[1] - normal[1] ) * backlerp, normal[2] + ( oldnormal[2] - normal[2] ) * backlerp ); } } } if( calcSTVectors ) R_BuildTangentVectors( mesh->numverts, inVertsArray, inNormalsArray, mesh->stArray, mesh->numtris, mesh->elems, inSVectorsArray ); } alias_mesh.xyzArray = calcVerts ? inVertsArray : mesh->xyzArray; alias_mesh.elems = mesh->elems; alias_mesh.numElems = mesh->numtris * 3; alias_mesh.numVerts = mesh->numverts; alias_mesh.stArray = mesh->stArray; if( features & MF_NORMALS ) alias_mesh.normalsArray = calcNormals ? inNormalsArray : mesh->normalsArray; if( features & MF_SVECTORS ) alias_mesh.sVectorsArray = calcSTVectors ? inSVectorsArray : mesh->sVectorsArray; R_RotateForEntity( e ); R_PushMesh( &alias_mesh, mb->vboIndex != 0, features ); R_RenderMeshBuffer( mb, NULL ); }