float *getChannelValue(skcHeader_t *h, const char *name, int frameNum) { int channelIndex; skcFrame_t *f; float *values; channelIndex = getChannelIndexInternal(h,name); if(channelIndex == -1) return 0; f = (skcFrame_t *)( (byte *)h + sizeof(*h) + sizeof(*f) * frameNum ); values = (float*)((byte *)h+f->ofsValues); values += (4 * channelIndex); vecIndex++; if(vecIndex >= sizeof(vecBuf) / sizeof(vecBuf[0])) { vecIndex = 0; } QuatCopy(values, vecBuf[vecIndex]); return vecBuf[vecIndex]; }
/* ============== RE_BlendSkeleton ============== */ int RE_BlendSkeleton(refSkeleton_t * skel, const refSkeleton_t * blend, float frac) { int i; vec3_t lerpedOrigin; quat_t lerpedQuat; vec3_t bounds[2]; if(skel->numBones != blend->numBones) { ri.Printf(PRINT_WARNING, "RE_BlendSkeleton: different number of bones %d != %d\n", skel->numBones, blend->numBones); return qfalse; } // lerp between the 2 bone poses for(i = 0; i < skel->numBones; i++) { VectorLerp(skel->bones[i].origin, blend->bones[i].origin, frac, lerpedOrigin); QuatSlerp(skel->bones[i].rotation, blend->bones[i].rotation, frac, lerpedQuat); VectorCopy(lerpedOrigin, skel->bones[i].origin); QuatCopy(lerpedQuat, skel->bones[i].rotation); } // calculate a bounding box in the current coordinate system for(i = 0; i < 3; i++) { bounds[0][i] = skel->bounds[0][i] < blend->bounds[0][i] ? skel->bounds[0][i] : blend->bounds[0][i]; bounds[1][i] = skel->bounds[1][i] > blend->bounds[1][i] ? skel->bounds[1][i] : blend->bounds[1][i]; } VectorCopy(bounds[0], skel->bounds[0]); VectorCopy(bounds[1], skel->bounds[1]); return qtrue; }
ex_value_t * new_quaternion_val (const float *quaternion_val) { ex_value_t val; memset (&val, 0, sizeof (val)); val.type = ev_quat; QuatCopy (quaternion_val, val.v.quaternion_val); return find_value (&val); }
float *findRotChannel(skcHeader_t *h, const char *name, int frameNum, int parentIndex) { #if 0 char channelName[32]; strcpy(channelName,name); strcat(channelName," rot"); return getChannelValue(h,channelName,frameNum); #else static int i = 0; static quat_t qs[1024]; float *q; char channelName[32]; float *f; float len; i++; i %= 1024; q = qs[i]; strcpy(channelName,name); strcat(channelName," rot"); f = getChannelValue(h,channelName,frameNum); if(f == 0) { //return quat_identity; QuatSet(q,0,0,0,-1); } else { QuatCopy(f,q); } //QuatInverse(q); ////if(parentIndex == -1) // QuatInverse(q); len = QuatNormalize(q); if(abs(len-1.f) > 0.1) { T_Error("Non-normalized quat in skc file (%f)\n",len); } #if 1 FixQuatForMD5_P(q); #endif return q; #endif }
Vec3 TurretComponent::AbsoluteAnglesToRelativeAngles(const Vec3 absoluteAngles) const { quat_t torsoRotation; quat_t absoluteRotation; quat_t relativeRotation; vec3_t relativeAngles; AnglesToQuat(TorsoAngles().Data(), torsoRotation); AnglesToQuat(absoluteAngles.Data(), absoluteRotation); // This is the inverse of RelativeAnglesToAbsoluteAngles. See the comment there for details. quat_t inverseTorsoOrientation; QuatCopy(torsoRotation, inverseTorsoOrientation); QuatInverse(inverseTorsoOrientation); QuatMultiply(inverseTorsoOrientation, absoluteRotation, relativeRotation); QuatToAngles(relativeRotation, relativeAngles); /*turretLogger.Debug("AbsoluteAnglesToRelativeAngles: %s → %s. Torso angles: %s.", Utility::Print(absoluteAngles), Utility::Print(Vec3::Load(relativeAngles)), TorsoAngles() );*/ return Vec3::Load(relativeAngles); }
tAnim_t *appendSKC(tModel_t *m, const char *fname, float scale) { int len; skcHeader_t *h; skcFrame_t *f; //, *firstFrame; tAnim_t *out; tFrame_t *of; // const char *c; int i, j; int cFlags[512]; bone_t baseFrame[512]; int numAnimatedComponents; T_Printf("Loading MoHAA skc animation file %s...\n",fname); len = F_LoadBuf(fname,(byte**)&h); if(len == -1) { T_Printf("Cannot open %s\n",fname); return 0; } memset(cFlags,0,sizeof(cFlags)); out = T_Malloc(sizeof(tAnim_t)); out->frameRate = 1.f / h->frameTime; out->numBones = m->numBones; out->numFrames = h->numFrames; out->frames = T_Malloc(sizeof(tFrame_t)*h->numFrames); out->boneData = T_Malloc(sizeof(tAnimBone_t)*m->numBones); // copy frame bounding boxes f = (skcFrame_t *)( (byte *)h + sizeof(*h) ); of = out->frames; for(i = 0; i < h->numFrames; i++,of++,f++) { //anim->frames[i].radius = f->radius; VectorCopy(f->bounds[1],of->maxs); VectorCopy(f->bounds[0],of->mins); } // detect which components changes for(j = 0; j < m->numBones; j++) { float *baseRot, *testRot; float *basePos, *testPos; basePos = findPosChannel(h,m->bones[j].name,0); if(basePos == 0) { VectorSet(baseFrame[j].p,0,0,0); } else { VectorScale(basePos,scale,basePos); VectorCopy(basePos,baseFrame[j].p); for(i = 1; i < h->numFrames; i++) { testPos = findPosChannel(h,m->bones[j].name,i); VectorScale(testPos,scale,testPos); // detect X change if(testPos[0] != basePos[0]) { cFlags[j] |= COMPONENT_BIT_TX; } // detect Y change if(testPos[1] != basePos[1]) { cFlags[j] |= COMPONENT_BIT_TY; } // detect Z change if(testPos[2] != basePos[2]) { cFlags[j] |= COMPONENT_BIT_TZ; } } } baseRot = findRotChannel(h,m->bones[j].name,0,m->bones[j].parent); if(baseRot == 0) { QuatSet(baseFrame[j].q,0,0,0,-1); } else { QuatCopy(baseRot,baseFrame[j].q); for(i = 1; i < h->numFrames; i++) { testRot = findRotChannel(h,m->bones[j].name,i,m->bones[j].parent); // detect X change if(testRot[0] != baseRot[0]) { cFlags[j] |= COMPONENT_BIT_QX; } // detect Y change if(testRot[1] != baseRot[1]) { cFlags[j] |= COMPONENT_BIT_QY; } // detect Z change if(testRot[2] != baseRot[2]) { cFlags[j] |= COMPONENT_BIT_QZ; } // NOTE: quaternion W component is not stored at all in md5 files } } } // count the number of animated components and copy some bone data numAnimatedComponents = 0; for(j = 0; j < m->numBones; j++) { //int c; out->boneData[j].firstComponent = numAnimatedComponents; //c = 0; for(i = 0; i < 6; i++) { if(cFlags[j] & (1 << i)) { numAnimatedComponents++; // c++; } } //out->boneData[j].numAnimatedComponents = c; out->boneData[j].componentBits = cFlags[j]; strcpy(out->boneData[j].name,m->bones[j].name); out->boneData[j].parent = m->bones[j].parent; } // copy results out out->baseFrame = T_Malloc(sizeof(bone_t)*m->numBones); memcpy(out->baseFrame,baseFrame,sizeof(bone_t)*m->numBones); out->numAnimatedComponents = numAnimatedComponents; of = out->frames; for(i = 0; i < h->numFrames; i++,of++) { int c; float *cp; cp = of->components = T_Malloc(numAnimatedComponents*sizeof(float)); //c = 0; for(j = 0; j < m->numBones; j++) { float *pos, *rot; pos = findPosChannel(h,m->bones[j].name,i); if(pos) { VectorScale(pos,scale,pos); // write X change if(cFlags[j] & COMPONENT_BIT_TX) { *cp = pos[0]; cp++; } // write Y change if(cFlags[j] & COMPONENT_BIT_TY) { *cp = pos[1]; cp++; } // write Z change if(cFlags[j] & COMPONENT_BIT_TZ) { *cp = pos[2]; cp++; } } rot = findRotChannel(h,m->bones[j].name,i,m->bones[j].parent); if(rot) { // write X change if(cFlags[j] & COMPONENT_BIT_QX) { *cp = rot[0]; cp++; } // write Y change if(cFlags[j] & COMPONENT_BIT_QY) { *cp = rot[1]; cp++; } // write Z change if(cFlags[j] & COMPONENT_BIT_QZ) { *cp = rot[2]; cp++; } } } c = cp - of->components; assert(c == numAnimatedComponents); } #if 0 // validate generated tAnim_t components for(i = 0; i < out->numFrames; i++) { bone_t *b = setupMD5AnimBones(out,0); for(j = 0; j < m->numBones; j++, b++) { float *o; o = findRotChannel_raw(h,m->bones[j].name,i,m->bones[j].parent); if(o) { T_Printf("Generated: %f %f %f %f, original %f %f %f %f\n",b->q[0],b->q[1],b->q[2],b->q[3], o[0],o[1],o[2],o[3]); } else { T_Printf("Generated: %f %f %f %f, original <none>\n",b->q[0],b->q[1],b->q[2],b->q[3]); } } } #endif // generate baseFrame, but only once, // from the first appended SKC if(m->baseFrame == 0) { #if 0 // FIXME! bone_t *b = setupMD5AnimBones(out,0); //for(i = 0; i < m->numBones; i++) { // QuatInverse(b[i].q); //} #else bone_t b[512]; for(i = 0; i < m->numBones; i++) { float *p, *q; p = findPosChannel(h,m->bones[i].name,0); if(p) { VectorScale(p,scale,b[i].p); } else { VectorSet(b[i].p,0,0,0); } q = findRotChannel(h,m->bones[i].name,0,m->bones[i].parent); if(q) { QuatCopy(q,b[i].q); } else { QuatSet(b[i].q,0,0,0,1); } } #endif md5AnimateBones(m,b); m->baseFrame = T_Malloc(m->numBones*sizeof(bone_t)); memcpy(m->baseFrame,b,m->numBones*sizeof(bone_t)); } F_FreeBuf((byte*)h); T_Printf("Succesfully loaded MoHAA animation %s\n",fname); return out; }
/* ============== RE_BuildSkeleton ============== */ int RE_BuildSkeleton(refSkeleton_t * skel, qhandle_t hAnim, int startFrame, int endFrame, float frac, qboolean clearOrigin) { skelAnimation_t *skelAnim; skelAnim = R_GetAnimationByHandle(hAnim); if(skelAnim->type == AT_MD5 && skelAnim->md5) { int i; md5Animation_t *anim; md5Channel_t *channel; md5Frame_t *newFrame, *oldFrame; vec3_t newOrigin, oldOrigin, lerpedOrigin; quat_t newQuat, oldQuat, lerpedQuat; int componentsApplied; anim = skelAnim->md5; // Validate the frames so there is no chance of a crash. // This will write directly into the entity structure, so // when the surfaces are rendered, they don't need to be // range checked again. /* if((startFrame >= anim->numFrames) || (startFrame < 0) || (endFrame >= anim->numFrames) || (endFrame < 0)) { ri.Printf(PRINT_DEVELOPER, "RE_BuildSkeleton: no such frame %d to %d for '%s'\n", startFrame, endFrame, anim->name); //startFrame = 0; //endFrame = 0; } */ Q_clamp(startFrame, 0, anim->numFrames - 1); Q_clamp(endFrame, 0, anim->numFrames - 1); // compute frame pointers oldFrame = &anim->frames[startFrame]; newFrame = &anim->frames[endFrame]; // calculate a bounding box in the current coordinate system for(i = 0; i < 3; i++) { skel->bounds[0][i] = oldFrame->bounds[0][i] < newFrame->bounds[0][i] ? oldFrame->bounds[0][i] : newFrame->bounds[0][i]; skel->bounds[1][i] = oldFrame->bounds[1][i] > newFrame->bounds[1][i] ? oldFrame->bounds[1][i] : newFrame->bounds[1][i]; } for(i = 0, channel = anim->channels; i < anim->numChannels; i++, channel++) { // set baseframe values VectorCopy(channel->baseOrigin, newOrigin); VectorCopy(channel->baseOrigin, oldOrigin); QuatCopy(channel->baseQuat, newQuat); QuatCopy(channel->baseQuat, oldQuat); componentsApplied = 0; // update tranlation bits if(channel->componentsBits & COMPONENT_BIT_TX) { oldOrigin[0] = oldFrame->components[channel->componentsOffset + componentsApplied]; newOrigin[0] = newFrame->components[channel->componentsOffset + componentsApplied]; componentsApplied++; } if(channel->componentsBits & COMPONENT_BIT_TY) { oldOrigin[1] = oldFrame->components[channel->componentsOffset + componentsApplied]; newOrigin[1] = newFrame->components[channel->componentsOffset + componentsApplied]; componentsApplied++; } if(channel->componentsBits & COMPONENT_BIT_TZ) { oldOrigin[2] = oldFrame->components[channel->componentsOffset + componentsApplied]; newOrigin[2] = newFrame->components[channel->componentsOffset + componentsApplied]; componentsApplied++; } // update quaternion rotation bits if(channel->componentsBits & COMPONENT_BIT_QX) { ((vec_t *) oldQuat)[0] = oldFrame->components[channel->componentsOffset + componentsApplied]; ((vec_t *) newQuat)[0] = newFrame->components[channel->componentsOffset + componentsApplied]; componentsApplied++; } if(channel->componentsBits & COMPONENT_BIT_QY) { ((vec_t *) oldQuat)[1] = oldFrame->components[channel->componentsOffset + componentsApplied]; ((vec_t *) newQuat)[1] = newFrame->components[channel->componentsOffset + componentsApplied]; componentsApplied++; } if(channel->componentsBits & COMPONENT_BIT_QZ) { ((vec_t *) oldQuat)[2] = oldFrame->components[channel->componentsOffset + componentsApplied]; ((vec_t *) newQuat)[2] = newFrame->components[channel->componentsOffset + componentsApplied]; } QuatCalcW(oldQuat); QuatNormalize(oldQuat); QuatCalcW(newQuat); QuatNormalize(newQuat); #if 1 VectorLerp(oldOrigin, newOrigin, frac, lerpedOrigin); QuatSlerp(oldQuat, newQuat, frac, lerpedQuat); #else VectorCopy(newOrigin, lerpedOrigin); QuatCopy(newQuat, lerpedQuat); #endif // copy lerped information to the bone + extra data skel->bones[i].parentIndex = channel->parentIndex; if(channel->parentIndex < 0 && clearOrigin) { VectorClear(skel->bones[i].origin); QuatClear(skel->bones[i].rotation); // move bounding box back VectorSubtract(skel->bounds[0], lerpedOrigin, skel->bounds[0]); VectorSubtract(skel->bounds[1], lerpedOrigin, skel->bounds[1]); } else { VectorCopy(lerpedOrigin, skel->bones[i].origin); } QuatCopy(lerpedQuat, skel->bones[i].rotation); #if defined(REFBONE_NAMES) Q_strncpyz(skel->bones[i].name, channel->name, sizeof(skel->bones[i].name)); #endif } skel->numBones = anim->numChannels; skel->type = SK_RELATIVE; return qtrue; } else if(skelAnim->type == AT_PSA && skelAnim->psa) { int i; psaAnimation_t *anim; axAnimationKey_t *newKey, *oldKey; axReferenceBone_t *refBone; vec3_t newOrigin, oldOrigin, lerpedOrigin; quat_t newQuat, oldQuat, lerpedQuat; refSkeleton_t skeleton; anim = skelAnim->psa; Q_clamp(startFrame, 0, anim->info.numRawFrames - 1); Q_clamp(endFrame, 0, anim->info.numRawFrames - 1); ClearBounds(skel->bounds[0], skel->bounds[1]); skel->numBones = anim->info.numBones; for(i = 0, refBone = anim->bones; i < anim->info.numBones; i++, refBone++) { oldKey = &anim->keys[startFrame * anim->info.numBones + i]; newKey = &anim->keys[endFrame * anim->info.numBones + i]; VectorCopy(newKey->position, newOrigin); VectorCopy(oldKey->position, oldOrigin); QuatCopy(newKey->quat, newQuat); QuatCopy(oldKey->quat, oldQuat); //QuatCalcW(oldQuat); //QuatNormalize(oldQuat); //QuatCalcW(newQuat); //QuatNormalize(newQuat); VectorLerp(oldOrigin, newOrigin, frac, lerpedOrigin); QuatSlerp(oldQuat, newQuat, frac, lerpedQuat); // copy lerped information to the bone + extra data skel->bones[i].parentIndex = refBone->parentIndex; if(refBone->parentIndex < 0 && clearOrigin) { VectorClear(skel->bones[i].origin); QuatClear(skel->bones[i].rotation); // move bounding box back VectorSubtract(skel->bounds[0], lerpedOrigin, skel->bounds[0]); VectorSubtract(skel->bounds[1], lerpedOrigin, skel->bounds[1]); } else { VectorCopy(lerpedOrigin, skel->bones[i].origin); } QuatCopy(lerpedQuat, skel->bones[i].rotation); #if defined(REFBONE_NAMES) Q_strncpyz(skel->bones[i].name, refBone->name, sizeof(skel->bones[i].name)); #endif // calculate absolute values for the bounding box approximation VectorCopy(skel->bones[i].origin, skeleton.bones[i].origin); QuatCopy(skel->bones[i].rotation, skeleton.bones[i].rotation); if(refBone->parentIndex >= 0) { vec3_t rotated; quat_t quat; refBone_t *parent; refBone_t *bone; bone = &skeleton.bones[i]; parent = &skeleton.bones[refBone->parentIndex]; QuatTransformVector(parent->rotation, bone->origin, rotated); VectorAdd(parent->origin, rotated, bone->origin); QuatMultiply1(parent->rotation, bone->rotation, quat); QuatCopy(quat, bone->rotation); AddPointToBounds(bone->origin, skel->bounds[0], skel->bounds[1]); } } skel->numBones = anim->info.numBones; skel->type = SK_RELATIVE; return qtrue; } //ri.Printf(PRINT_WARNING, "RE_BuildSkeleton: bad animation '%s' with handle %i\n", anim->name, hAnim); // FIXME: clear existing bones and bounds? return qfalse; }
/* PR_ExecuteProgram The interpretation main loop */ VISIBLE void PR_ExecuteProgram (progs_t * pr, func_t fnum) { int exitdepth, profile, startprofile; pr_uint_t pointer; dstatement_t *st; edict_t *ed; pr_type_t *ptr; pr_type_t old_val = {0}, *watch = 0; // make a stack frame exitdepth = pr->pr_depth; startprofile = profile = 0; Sys_PushSignalHook (signal_hook, pr); if (!PR_CallFunction (pr, fnum)) { // called a builtin instead of progs code Sys_PopSignalHook (); return; } st = pr->pr_statements + pr->pr_xstatement; if (pr->watch) { watch = pr->watch; old_val = *watch; } while (1) { pr_type_t *op_a, *op_b, *op_c; st++; ++pr->pr_xstatement; if (pr->pr_xstatement != st - pr->pr_statements) PR_RunError (pr, "internal error"); if (++profile > 1000000 && !pr->no_exec_limit) { PR_RunError (pr, "runaway loop error"); } op_a = pr->pr_globals + st->a; op_b = pr->pr_globals + st->b; op_c = pr->pr_globals + st->c; if (pr->pr_trace) PR_PrintStatement (pr, st, 1); switch (st->op) { case OP_ADD_F: OPC.float_var = OPA.float_var + OPB.float_var; break; case OP_ADD_V: VectorAdd (OPA.vector_var, OPB.vector_var, OPC.vector_var); break; case OP_ADD_Q: QuatAdd (OPA.quat_var, OPB.quat_var, OPC.quat_var); break; case OP_ADD_S: OPC.string_var = PR_CatStrings (pr, PR_GetString (pr, OPA.string_var), PR_GetString (pr, OPB.string_var)); break; case OP_SUB_F: OPC.float_var = OPA.float_var - OPB.float_var; break; case OP_SUB_V: VectorSubtract (OPA.vector_var, OPB.vector_var, OPC.vector_var); break; case OP_SUB_Q: QuatSubtract (OPA.quat_var, OPB.quat_var, OPC.quat_var); break; case OP_MUL_F: OPC.float_var = OPA.float_var * OPB.float_var; break; case OP_MUL_V: OPC.float_var = DotProduct (OPA.vector_var, OPB.vector_var); break; case OP_MUL_FV: { // avoid issues with the likes of x = x.x * x; // makes for faster code, too float scale = OPA.float_var; VectorScale (OPB.vector_var, scale, OPC.vector_var); } break; case OP_MUL_VF: { // avoid issues with the likes of x = x * x.x; // makes for faster code, too float scale = OPB.float_var; VectorScale (OPA.vector_var, scale, OPC.vector_var); } break; case OP_MUL_Q: QuatMult (OPA.quat_var, OPB.quat_var, OPC.quat_var); break; case OP_MUL_QV: QuatMultVec (OPA.quat_var, OPB.vector_var, OPC.vector_var); break; case OP_MUL_FQ: { // avoid issues with the likes of x = x.s * x; // makes for faster code, too float scale = OPA.float_var; QuatScale (OPB.quat_var, scale, OPC.quat_var); } break; case OP_MUL_QF: { // avoid issues with the likes of x = x * x.s; // makes for faster code, too float scale = OPB.float_var; QuatScale (OPA.quat_var, scale, OPC.quat_var); } break; case OP_CONJ_Q: QuatConj (OPA.quat_var, OPC.quat_var); break; case OP_DIV_F: OPC.float_var = OPA.float_var / OPB.float_var; break; case OP_BITAND: OPC.float_var = (int) OPA.float_var & (int) OPB.float_var; break; case OP_BITOR: OPC.float_var = (int) OPA.float_var | (int) OPB.float_var; break; case OP_BITXOR_F: OPC.float_var = (int) OPA.float_var ^ (int) OPB.float_var; break; case OP_BITNOT_F: OPC.float_var = ~ (int) OPA.float_var; break; case OP_SHL_F: OPC.float_var = (int) OPA.float_var << (int) OPB.float_var; break; case OP_SHR_F: OPC.float_var = (int) OPA.float_var >> (int) OPB.float_var; break; case OP_SHL_I: OPC.integer_var = OPA.integer_var << OPB.integer_var; break; case OP_SHR_I: OPC.integer_var = OPA.integer_var >> OPB.integer_var; break; case OP_SHR_U: OPC.uinteger_var = OPA.uinteger_var >> OPB.integer_var; break; case OP_GE_F: OPC.float_var = OPA.float_var >= OPB.float_var; break; case OP_LE_F: OPC.float_var = OPA.float_var <= OPB.float_var; break; case OP_GT_F: OPC.float_var = OPA.float_var > OPB.float_var; break; case OP_LT_F: OPC.float_var = OPA.float_var < OPB.float_var; break; case OP_AND: // OPA and OPB have to be float for -0.0 OPC.integer_var = FNZ (OPA) && FNZ (OPB); break; case OP_OR: // OPA and OPB have to be float for -0.0 OPC.integer_var = FNZ (OPA) || FNZ (OPB); break; case OP_NOT_F: OPC.integer_var = !FNZ (OPA); break; case OP_NOT_V: OPC.integer_var = VectorIsZero (OPA.vector_var); break; case OP_NOT_Q: OPC.integer_var = QuatIsZero (OPA.quat_var); break; case OP_NOT_S: OPC.integer_var = !OPA.string_var || !*PR_GetString (pr, OPA.string_var); break; case OP_NOT_FN: OPC.integer_var = !OPA.func_var; break; case OP_NOT_ENT: OPC.integer_var = !OPA.entity_var; break; case OP_EQ_F: OPC.integer_var = OPA.float_var == OPB.float_var; break; case OP_EQ_V: OPC.integer_var = VectorCompare (OPA.vector_var, OPB.vector_var); break; case OP_EQ_Q: OPC.integer_var = QuatCompare (OPA.quat_var, OPB.quat_var); break; case OP_EQ_E: OPC.integer_var = OPA.integer_var == OPB.integer_var; break; case OP_EQ_FN: OPC.integer_var = OPA.func_var == OPB.func_var; break; case OP_NE_F: OPC.integer_var = OPA.float_var != OPB.float_var; break; case OP_NE_V: OPC.integer_var = !VectorCompare (OPA.vector_var, OPB.vector_var); break; case OP_NE_Q: OPC.integer_var = !QuatCompare (OPA.quat_var, OPB.quat_var); break; case OP_LE_S: case OP_GE_S: case OP_LT_S: case OP_GT_S: case OP_NE_S: case OP_EQ_S: { int cmp = strcmp (PR_GetString (pr, OPA.string_var), PR_GetString (pr, OPB.string_var)); switch (st->op) { case OP_LE_S: cmp = (cmp <= 0); break; case OP_GE_S: cmp = (cmp >= 0); break; case OP_LT_S: cmp = (cmp < 0); break; case OP_GT_S: cmp = (cmp > 0); break; case OP_NE_S: break; case OP_EQ_S: cmp = !cmp; break; default: break; } OPC.integer_var = cmp; } break; case OP_NE_E: OPC.integer_var = OPA.integer_var != OPB.integer_var; break; case OP_NE_FN: OPC.integer_var = OPA.func_var != OPB.func_var; break; // ================== case OP_STORE_F: case OP_STORE_ENT: case OP_STORE_FLD: // integers case OP_STORE_S: case OP_STORE_FN: // pointers case OP_STORE_I: case OP_STORE_P: OPB.integer_var = OPA.integer_var; break; case OP_STORE_V: VectorCopy (OPA.vector_var, OPB.vector_var); break; case OP_STORE_Q: QuatCopy (OPA.quat_var, OPB.quat_var); break; case OP_STOREP_F: case OP_STOREP_ENT: case OP_STOREP_FLD: // integers case OP_STOREP_S: case OP_STOREP_FN: // pointers case OP_STOREP_I: case OP_STOREP_P: pointer = OPB.integer_var; if (pr_boundscheck->int_val) { PR_BoundsCheck (pr, pointer, ev_integer); } ptr = pr->pr_globals + pointer; ptr->integer_var = OPA.integer_var; break; case OP_STOREP_V: pointer = OPB.integer_var; if (pr_boundscheck->int_val) { PR_BoundsCheck (pr, pointer, ev_vector); } ptr = pr->pr_globals + pointer; VectorCopy (OPA.vector_var, ptr->vector_var); break; case OP_STOREP_Q: pointer = OPB.integer_var; if (pr_boundscheck->int_val) { PR_BoundsCheck (pr, pointer, ev_quat); } ptr = pr->pr_globals + pointer; QuatCopy (OPA.quat_var, ptr->quat_var); break; case OP_ADDRESS: if (pr_boundscheck->int_val) { if (OPA.entity_var < 0 || OPA.entity_var >= pr->pr_edictareasize) PR_RunError (pr, "Progs attempted to address an out " "of bounds edict"); if (OPA.entity_var == 0 && pr->null_bad) PR_RunError (pr, "assignment to world entity"); if (OPB.uinteger_var >= pr->progs->entityfields) PR_RunError (pr, "Progs attempted to address an " "invalid field in an edict"); } ed = PROG_TO_EDICT (pr, OPA.entity_var); OPC.integer_var = &ed->v[OPB.integer_var] - pr->pr_globals; break; case OP_ADDRESS_VOID: case OP_ADDRESS_F: case OP_ADDRESS_V: case OP_ADDRESS_Q: case OP_ADDRESS_S: case OP_ADDRESS_ENT: case OP_ADDRESS_FLD: case OP_ADDRESS_FN: case OP_ADDRESS_I: case OP_ADDRESS_P: OPC.integer_var = st->a; break; case OP_LOAD_F: case OP_LOAD_FLD: case OP_LOAD_ENT: case OP_LOAD_S: case OP_LOAD_FN: case OP_LOAD_I: case OP_LOAD_P: if (pr_boundscheck->int_val) { if (OPA.entity_var < 0 || OPA.entity_var >= pr->pr_edictareasize) PR_RunError (pr, "Progs attempted to read an out of " "bounds edict number"); if (OPB.uinteger_var >= pr->progs->entityfields) PR_RunError (pr, "Progs attempted to read an invalid " "field in an edict"); } ed = PROG_TO_EDICT (pr, OPA.entity_var); OPC.integer_var = ed->v[OPB.integer_var].integer_var; break; case OP_LOAD_V: if (pr_boundscheck->int_val) { if (OPA.entity_var < 0 || OPA.entity_var >= pr->pr_edictareasize) PR_RunError (pr, "Progs attempted to read an out of " "bounds edict number"); if (OPB.uinteger_var + 2 >= pr->progs->entityfields) PR_RunError (pr, "Progs attempted to read an invalid " "field in an edict"); } ed = PROG_TO_EDICT (pr, OPA.entity_var); memcpy (&OPC, &ed->v[OPB.integer_var], 3 * sizeof (OPC)); break; case OP_LOAD_Q: if (pr_boundscheck->int_val) { if (OPA.entity_var < 0 || OPA.entity_var >= pr->pr_edictareasize) PR_RunError (pr, "Progs attempted to read an out of " "bounds edict number"); if (OPB.uinteger_var + 3 >= pr->progs->entityfields) PR_RunError (pr, "Progs attempted to read an invalid " "field in an edict"); } ed = PROG_TO_EDICT (pr, OPA.entity_var); memcpy (&OPC, &ed->v[OPB.integer_var], 3 * sizeof (OPC)); break; case OP_LOADB_F: case OP_LOADB_S: case OP_LOADB_ENT: case OP_LOADB_FLD: case OP_LOADB_FN: case OP_LOADB_I: case OP_LOADB_P: pointer = OPA.integer_var + OPB.integer_var; if (pr_boundscheck->int_val) { PR_BoundsCheck (pr, pointer, ev_integer); } ptr = pr->pr_globals + pointer; OPC.integer_var = ptr->integer_var; break; case OP_LOADB_V: pointer = OPA.integer_var + OPB.integer_var; if (pr_boundscheck->int_val) { PR_BoundsCheck (pr, pointer, ev_vector); } ptr = pr->pr_globals + pointer; VectorCopy (ptr->vector_var, OPC.vector_var); break; case OP_LOADB_Q: pointer = OPA.integer_var + OPB.integer_var; if (pr_boundscheck->int_val) { PR_BoundsCheck (pr, pointer, ev_quat); } ptr = pr->pr_globals + pointer; QuatCopy (ptr->quat_var, OPC.quat_var); break; case OP_LOADBI_F: case OP_LOADBI_S: case OP_LOADBI_ENT: case OP_LOADBI_FLD: case OP_LOADBI_FN: case OP_LOADBI_I: case OP_LOADBI_P: pointer = OPA.integer_var + (short) st->b; if (pr_boundscheck->int_val) { PR_BoundsCheck (pr, pointer, ev_integer); } ptr = pr->pr_globals + pointer; OPC.integer_var = ptr->integer_var; break; case OP_LOADBI_V: pointer = OPA.integer_var + (short) st->b; if (pr_boundscheck->int_val) { PR_BoundsCheck (pr, pointer, ev_vector); } ptr = pr->pr_globals + pointer; VectorCopy (ptr->vector_var, OPC.vector_var); break; case OP_LOADBI_Q: pointer = OPA.integer_var + (short) st->b; if (pr_boundscheck->int_val) { PR_BoundsCheck (pr, pointer, ev_quat); } ptr = pr->pr_globals + pointer; QuatCopy (ptr->quat_var, OPC.quat_var); break; case OP_LEA: pointer = OPA.integer_var + OPB.integer_var; OPC.integer_var = pointer; break; case OP_LEAI: pointer = OPA.integer_var + (short) st->b; OPC.integer_var = pointer; break; case OP_STOREB_F: case OP_STOREB_S: case OP_STOREB_ENT: case OP_STOREB_FLD: case OP_STOREB_FN: case OP_STOREB_I: case OP_STOREB_P: pointer = OPB.integer_var + OPC.integer_var; if (pr_boundscheck->int_val) { PR_BoundsCheck (pr, pointer, ev_integer); } ptr = pr->pr_globals + pointer; ptr->integer_var = OPA.integer_var; break; case OP_STOREB_V: pointer = OPB.integer_var + OPC.integer_var; if (pr_boundscheck->int_val) { PR_BoundsCheck (pr, pointer, ev_vector); } ptr = pr->pr_globals + pointer; VectorCopy (OPA.vector_var, ptr->vector_var); break; case OP_STOREB_Q: pointer = OPB.integer_var + OPC.integer_var; if (pr_boundscheck->int_val) { PR_BoundsCheck (pr, pointer, ev_quat); } ptr = pr->pr_globals + pointer; QuatCopy (OPA.quat_var, ptr->quat_var); break; case OP_STOREBI_F: case OP_STOREBI_S: case OP_STOREBI_ENT: case OP_STOREBI_FLD: case OP_STOREBI_FN: case OP_STOREBI_I: case OP_STOREBI_P: pointer = OPB.integer_var + (short) st->c; if (pr_boundscheck->int_val) { PR_BoundsCheck (pr, pointer, ev_integer); } ptr = pr->pr_globals + pointer; ptr->integer_var = OPA.integer_var; break; case OP_STOREBI_V: pointer = OPB.integer_var + (short) st->c; if (pr_boundscheck->int_val) { PR_BoundsCheck (pr, pointer, ev_vector); } ptr = pr->pr_globals + pointer; VectorCopy (OPA.vector_var, ptr->vector_var); break; case OP_STOREBI_Q: pointer = OPB.integer_var + (short) st->c; if (pr_boundscheck->int_val) { PR_BoundsCheck (pr, pointer, ev_quat); } ptr = pr->pr_globals + pointer; QuatCopy (OPA.quat_var, ptr->quat_var); break; // ================== case OP_IFNOT: if (!OPA.integer_var) { pr->pr_xstatement += (short)st->b - 1; // offset the st++ st = pr->pr_statements + pr->pr_xstatement; } break; case OP_IF: if (OPA.integer_var) { pr->pr_xstatement += (short)st->b - 1; // offset the st++ st = pr->pr_statements + pr->pr_xstatement; } break; case OP_IFBE: if (OPA.integer_var <= 0) { pr->pr_xstatement += (short)st->b - 1; // offset the st++ st = pr->pr_statements + pr->pr_xstatement; } break; case OP_IFB: if (OPA.integer_var < 0) { pr->pr_xstatement += (short)st->b - 1; // offset the st++ st = pr->pr_statements + pr->pr_xstatement; } break; case OP_IFAE: if (OPA.integer_var >= 0) { pr->pr_xstatement += (short)st->b - 1; // offset the st++ st = pr->pr_statements + pr->pr_xstatement; } break; case OP_IFA: if (OPA.integer_var > 0) { pr->pr_xstatement += (short)st->b - 1; // offset the st++ st = pr->pr_statements + pr->pr_xstatement; } break; case OP_GOTO: pr->pr_xstatement += (short)st->a - 1; // offset the st++ st = pr->pr_statements + pr->pr_xstatement; break; case OP_JUMP: if (pr_boundscheck->int_val && (OPA.uinteger_var >= pr->progs->numstatements)) { PR_RunError (pr, "Invalid jump destination"); } pr->pr_xstatement = OPA.uinteger_var - 1; // offset the st++ st = pr->pr_statements + pr->pr_xstatement; break; case OP_JUMPB: pointer = st->a + OPB.integer_var; if (pr_boundscheck->int_val) { PR_BoundsCheck (pr, pointer, ev_integer); } ptr = pr->pr_globals + pointer; pointer = ptr->integer_var; if (pr_boundscheck->int_val && (pointer >= pr->progs->numstatements)) { PR_RunError (pr, "Invalid jump destination"); } pr->pr_xstatement = pointer - 1; // offset the st++ st = pr->pr_statements + pr->pr_xstatement; break; case OP_RCALL2: case OP_RCALL3: case OP_RCALL4: case OP_RCALL5: case OP_RCALL6: case OP_RCALL7: case OP_RCALL8: pr->pr_params[1] = &OPC; goto op_rcall; case OP_RCALL1: pr->pr_params[1] = pr->pr_real_params[1]; op_rcall: pr->pr_params[0] = &OPB; pr->pr_argc = st->op - OP_RCALL1 + 1; goto op_call; case OP_CALL0: case OP_CALL1: case OP_CALL2: case OP_CALL3: case OP_CALL4: case OP_CALL5: case OP_CALL6: case OP_CALL7: case OP_CALL8: PR_RESET_PARAMS (pr); pr->pr_argc = st->op - OP_CALL0; op_call: pr->pr_xfunction->profile += profile - startprofile; startprofile = profile; PR_CallFunction (pr, OPA.func_var); st = pr->pr_statements + pr->pr_xstatement; break; case OP_DONE: case OP_RETURN: if (!st->a) memset (&R_INT (pr), 0, pr->pr_param_size * sizeof (OPA)); else if (&R_INT (pr) != &OPA.integer_var) memcpy (&R_INT (pr), &OPA, pr->pr_param_size * sizeof (OPA)); // fallthrough case OP_RETURN_V: pr->pr_xfunction->profile += profile - startprofile; startprofile = profile; PR_LeaveFunction (pr); st = pr->pr_statements + pr->pr_xstatement; if (pr->pr_depth == exitdepth) { if (pr->pr_trace && pr->pr_depth <= pr->pr_trace_depth) pr->pr_trace = false; Sys_PopSignalHook (); return; // all done } break; case OP_STATE: ed = PROG_TO_EDICT (pr, *pr->globals.self); ed->v[pr->fields.nextthink].float_var = *pr->globals.time + 0.1; ed->v[pr->fields.frame].float_var = OPA.float_var; ed->v[pr->fields.think].func_var = OPB.func_var; break; case OP_STATE_F: ed = PROG_TO_EDICT (pr, *pr->globals.self); ed->v[pr->fields.nextthink].float_var = *pr->globals.time + OPC.float_var; ed->v[pr->fields.frame].float_var = OPA.float_var; ed->v[pr->fields.think].func_var = OPB.func_var; break; case OP_ADD_I: OPC.integer_var = OPA.integer_var + OPB.integer_var; break; case OP_SUB_I: OPC.integer_var = OPA.integer_var - OPB.integer_var; break; case OP_MUL_I: OPC.integer_var = OPA.integer_var * OPB.integer_var; break; /* case OP_DIV_VF: { float temp = 1.0f / OPB.float_var; VectorScale (OPA.vector_var, temp, OPC.vector_var); } break; */ case OP_DIV_I: OPC.integer_var = OPA.integer_var / OPB.integer_var; break; case OP_MOD_I: OPC.integer_var = OPA.integer_var % OPB.integer_var; break; case OP_MOD_F: OPC.float_var = (int) OPA.float_var % (int) OPB.float_var; break; case OP_CONV_IF: OPC.float_var = OPA.integer_var; break; case OP_CONV_FI: OPC.integer_var = OPA.float_var; break; case OP_BITAND_I: OPC.integer_var = OPA.integer_var & OPB.integer_var; break; case OP_BITOR_I: OPC.integer_var = OPA.integer_var | OPB.integer_var; break; case OP_BITXOR_I: OPC.integer_var = OPA.integer_var ^ OPB.integer_var; break; case OP_BITNOT_I: OPC.integer_var = ~OPA.integer_var; break; case OP_GE_I: case OP_GE_P: OPC.integer_var = OPA.integer_var >= OPB.integer_var; break; case OP_GE_U: OPC.integer_var = OPA.uinteger_var >= OPB.uinteger_var; break; case OP_LE_I: case OP_LE_P: OPC.integer_var = OPA.integer_var <= OPB.integer_var; break; case OP_LE_U: OPC.integer_var = OPA.uinteger_var <= OPB.uinteger_var; break; case OP_GT_I: case OP_GT_P: OPC.integer_var = OPA.integer_var > OPB.integer_var; break; case OP_GT_U: OPC.integer_var = OPA.uinteger_var > OPB.uinteger_var; break; case OP_LT_I: case OP_LT_P: OPC.integer_var = OPA.integer_var < OPB.integer_var; break; case OP_LT_U: OPC.integer_var = OPA.uinteger_var < OPB.uinteger_var; break; case OP_AND_I: OPC.integer_var = OPA.integer_var && OPB.integer_var; break; case OP_OR_I: OPC.integer_var = OPA.integer_var || OPB.integer_var; break; case OP_NOT_I: case OP_NOT_P: OPC.integer_var = !OPA.integer_var; break; case OP_EQ_I: case OP_EQ_P: OPC.integer_var = OPA.integer_var == OPB.integer_var; break; case OP_NE_I: case OP_NE_P: OPC.integer_var = OPA.integer_var != OPB.integer_var; break; case OP_MOVEI: memmove (&OPC, &OPA, st->b * 4); break; case OP_MOVEP: if (pr_boundscheck->int_val) { PR_BoundsCheckSize (pr, OPC.integer_var, OPB.uinteger_var); PR_BoundsCheckSize (pr, OPA.integer_var, OPB.uinteger_var); } memmove (pr->pr_globals + OPC.integer_var, pr->pr_globals + OPA.integer_var, OPB.uinteger_var * 4); break; case OP_MOVEPI: if (pr_boundscheck->int_val) { PR_BoundsCheckSize (pr, OPC.integer_var, st->b); PR_BoundsCheckSize (pr, OPA.integer_var, st->b); } memmove (pr->pr_globals + OPC.integer_var, pr->pr_globals + OPA.integer_var, st->b * 4); break; // LordHavoc: to be enabled when Progs version 7 (or whatever it will be numbered) is finalized /* case OP_BOUNDCHECK: if (OPA.integer_var < 0 || OPA.integer_var >= st->b) { PR_RunError (pr, "Progs boundcheck failed at line number " "%d, value is < 0 or >= %d", st->b, st->c); } break; */ default: PR_RunError (pr, "Bad opcode %i", st->op); } if (watch && watch->integer_var != old_val.integer_var && (!pr->wp_conditional || watch->integer_var == pr->wp_val.integer_var)) PR_RunError (pr, "watchpoint hit: %d -> %d", old_val.integer_var, watch->integer_var); } }
// // 更新 // VOID CGfxBillboard::Update(const CEntityCamera *pCamera, CParticle *pParticleList, INT numParticles) { // // 1. 参数安全检查 // if (pCamera == NULL || pParticleList == NULL || numParticles <= 0) { return; } // // 2. 更新相机矩阵 // if (m_directionType == DIRECTION_CAMERA) { VEC3 direction; VEC3 position, up, target; Vec3Scale(&direction, pCamera->GetForwardDirection(), -1.0f); Vec3Set(&up, 0.0f, 1.0f, 0.0f); Vec3Set(&position, 0.0f, 0.0f, 0.0f); Vec3Ma(&target, &position, &direction, 1.0f); MtxDefLookAt(&m_mtxFaceToCamera, &position, &up, &target); } // // 3. 更新粒子数据 // VERTEX *vertices = (VERTEX *)SAFE_MALLOC(4 * numParticles * sizeof(*vertices), MEMTYPE_STACK); { // Billboard // 0 ___ 3 // | | // |___| // 1 2 INT indexVertex = 0; CParticle *pParticle = pParticleList; while (pParticle) { const VEC3 *parentWorldScale = pParticle->pEmitter->GetWorldScale(); const VEC3 *parentWorldPosition = pParticle->pEmitter->GetWorldPosition(); const QUAT *parentWorldOrientation = pParticle->pEmitter->GetWorldOrientation(); // // 1. 计算粒子位置与朝向 // VEC3 scale; VEC3 position; QUAT orientation; if (pParticle->bKeepLocal && pParticle->pEmitter) { Vec3Mul(&scale, &pParticle->localScale, parentWorldScale); if (m_directionType == DIRECTION_FIXED) { QuatMul(&orientation, &pParticle->localOrientation, parentWorldOrientation); } VEC3 scalePosition; VEC3 scaleOrientationPosition; Vec3Mul(&scalePosition, &pParticle->localPosition, parentWorldScale); Vec3MulQuat(&scaleOrientationPosition, &scalePosition, parentWorldOrientation); Vec3Add(&position, &scaleOrientationPosition, parentWorldPosition); } else { Vec3Copy(&scale, &pParticle->localScale); if (m_directionType == DIRECTION_FIXED) { QuatCopy(&orientation, &pParticle->localOrientation); } Vec3Copy(&position, &pParticle->localPosition); } // // 2. 粒子位置偏移量 // MATRIX4 mtxOrientation; if (m_directionType == DIRECTION_CAMERA) { MtxCopy(&mtxOrientation, &m_mtxFaceToCamera); if (m_offset < -EPSILON_E3 || m_offset > EPSILON_E3) { VEC3 offsetDirection; Vec3Sub(&offsetDirection, pCamera->GetPosition(), &position); Vec3Normalize(&offsetDirection); Vec3Ma(&position, &position, &offsetDirection, m_offset); } } else { QuatToMtxRotation(&mtxOrientation, &orientation); if (m_offset < -EPSILON_E3 || m_offset > EPSILON_E3) { VEC3 localDirection; VEC3 offsetDirection; Vec3Set(&localDirection, 0.0f, 0.0f, 1.0f); Vec3MulQuat(&offsetDirection, &localDirection, &orientation); Vec3Normalize(&offsetDirection); Vec3Ma(&position, &position, &offsetDirection, m_offset); } } // // 3. 计算粒子变换矩阵 // MATRIX4 mtxScale; MATRIX4 mtxRotate; MATRIX4 mtxRotateSelf; MATRIX4 mtxTranslate; MtxDefScale(&mtxScale, scale[0], scale[1], scale[2]); MtxDefTranslate(&mtxTranslate, position[0], position[1], position[2]); MtxDefRotateAxisAngle(&mtxRotateSelf, &axisz, pParticle->radian); MtxMul(&mtxRotate, &mtxRotateSelf, &mtxOrientation); MATRIX4 mtxSR; MATRIX4 mtxTransform; MtxMul(&mtxSR, &mtxScale, &mtxRotate); MtxMul(&mtxTransform, &mtxSR, &mtxTranslate); // // 4. 计算粒子纹理矩阵 // MATRIX4 mtxTexScale; MATRIX4 mtxTexTranslate; MATRIX4 mtxTexTransform; MtxDefScale(&mtxTexScale, pParticle->texSequenceScale[0], pParticle->texSequenceScale[1], 1.0f); MtxDefTranslate(&mtxTexTranslate, pParticle->texSequenceOffset[0] + pParticle->texScrollOffset[0], pParticle->texSequenceOffset[1] + pParticle->texScrollOffset[1], 0.0f); MtxMul(&mtxTexTransform, &mtxTexScale, &mtxTexTranslate); // // 5. 计算粒子顶点 // VEC3 desVertices[4]; VEC3 srcVertices[4] = { VEC3(-1.0f, 1.0f, 0.0f), VEC3(-1.0f, -1.0f, 0.0f), VEC3( 1.0f, -1.0f, 0.0f), VEC3( 1.0f, 1.0f, 0.0f), }; VEC2 texCoords[4] = { VEC2(pParticle->uvOffset[0] + 0.0f, pParticle->uvOffset[1] + 0.0f), VEC2(pParticle->uvOffset[0] + 0.0f, pParticle->uvOffset[1] + 1.0f), VEC2(pParticle->uvOffset[0] + 1.0f, pParticle->uvOffset[1] + 1.0f), VEC2(pParticle->uvOffset[0] + 1.0f, pParticle->uvOffset[1] + 0.0f), }; VEC3 localNormal, localBinormal; VEC3 worldNormal, worldBinormal; Vec3Set(&localNormal, 0.0f, 0.0f, 1.0f); Vec3Set(&localBinormal, 1.0f, 0.0f, 0.0f); Vec3MulMtx3x3(&worldNormal, &localNormal, &mtxTransform); Vec3MulMtx3x3(&worldBinormal, &localBinormal, &mtxTransform); Vec3MulMtx4x4(&desVertices[0], &srcVertices[0], &mtxTransform); Vec3MulMtx4x4(&desVertices[1], &srcVertices[1], &mtxTransform); Vec3MulMtx4x4(&desVertices[2], &srcVertices[2], &mtxTransform); Vec3MulMtx4x4(&desVertices[3], &srcVertices[3], &mtxTransform); Vec3Copy(&vertices[indexVertex].position, &desVertices[0]); Vec3Copy(&vertices[indexVertex].normal, &worldNormal); Vec3Copy(&vertices[indexVertex].binormal, &worldBinormal); Vec4Copy(&vertices[indexVertex].color, &pParticle->color); Vec2MulMtx4x4(&vertices[indexVertex].texCoordDiffuse, &texCoords[0], &mtxTexTransform); indexVertex++; Vec3Copy(&vertices[indexVertex].position, &desVertices[1]); Vec3Copy(&vertices[indexVertex].normal, &worldNormal); Vec3Copy(&vertices[indexVertex].binormal, &worldBinormal); Vec4Copy(&vertices[indexVertex].color, &pParticle->color); Vec2MulMtx4x4(&vertices[indexVertex].texCoordDiffuse, &texCoords[1], &mtxTexTransform); indexVertex++; Vec3Copy(&vertices[indexVertex].position, &desVertices[2]); Vec3Copy(&vertices[indexVertex].normal, &worldNormal); Vec3Copy(&vertices[indexVertex].binormal, &worldBinormal); Vec4Copy(&vertices[indexVertex].color, &pParticle->color); Vec2MulMtx4x4(&vertices[indexVertex].texCoordDiffuse, &texCoords[2], &mtxTexTransform); indexVertex++; Vec3Copy(&vertices[indexVertex].position, &desVertices[3]); Vec3Copy(&vertices[indexVertex].normal, &worldNormal); Vec3Copy(&vertices[indexVertex].binormal, &worldBinormal); Vec4Copy(&vertices[indexVertex].color, &pParticle->color); Vec2MulMtx4x4(&vertices[indexVertex].texCoordDiffuse, &texCoords[3], &mtxTexTransform); indexVertex++; pParticle = pParticle->pNext; } Renderer()->BindVBO(GL_ARRAY_BUFFER, m_vbo); Renderer()->UpdateVBO(GL_ARRAY_BUFFER, 0, 4 * numParticles * sizeof(*vertices), vertices); } SAFE_FREE(vertices); }
/* ============== RE_BuildSkeleton ============== */ int RE_BuildSkeleton( refSkeleton_t *skel, qhandle_t hAnim, int startFrame, int endFrame, float frac, bool clearOrigin ) { skelAnimation_t *skelAnim; skelAnim = R_GetAnimationByHandle( hAnim ); if ( skelAnim->type == animType_t::AT_IQM && skelAnim->iqm ) { return IQMBuildSkeleton( skel, skelAnim, startFrame, endFrame, frac ); } else if ( skelAnim->type == animType_t::AT_MD5 && skelAnim->md5 ) { int i; md5Animation_t *anim; md5Channel_t *channel; md5Frame_t *newFrame, *oldFrame; vec3_t newOrigin, oldOrigin, lerpedOrigin; quat_t newQuat, oldQuat, lerpedQuat; int componentsApplied; anim = skelAnim->md5; // Validate the frames so there is no chance of a crash. // This will write directly into the entity structure, so // when the surfaces are rendered, they don't need to be // range checked again. /* if((startFrame >= anim->numFrames) || (startFrame < 0) || (endFrame >= anim->numFrames) || (endFrame < 0)) { Log::Debug("RE_BuildSkeleton: no such frame %d to %d for '%s'\n", startFrame, endFrame, anim->name); //startFrame = 0; //endFrame = 0; } */ startFrame = Math::Clamp( startFrame, 0, anim->numFrames - 1 ); endFrame = Math::Clamp( endFrame, 0, anim->numFrames - 1 ); // compute frame pointers oldFrame = &anim->frames[ startFrame ]; newFrame = &anim->frames[ endFrame ]; // calculate a bounding box in the current coordinate system for ( i = 0; i < 3; i++ ) { skel->bounds[ 0 ][ i ] = oldFrame->bounds[ 0 ][ i ] < newFrame->bounds[ 0 ][ i ] ? oldFrame->bounds[ 0 ][ i ] : newFrame->bounds[ 0 ][ i ]; skel->bounds[ 1 ][ i ] = oldFrame->bounds[ 1 ][ i ] > newFrame->bounds[ 1 ][ i ] ? oldFrame->bounds[ 1 ][ i ] : newFrame->bounds[ 1 ][ i ]; } for ( i = 0, channel = anim->channels; i < anim->numChannels; i++, channel++ ) { // set baseframe values VectorCopy( channel->baseOrigin, newOrigin ); VectorCopy( channel->baseOrigin, oldOrigin ); QuatCopy( channel->baseQuat, newQuat ); QuatCopy( channel->baseQuat, oldQuat ); componentsApplied = 0; // update tranlation bits if ( channel->componentsBits & COMPONENT_BIT_TX ) { oldOrigin[ 0 ] = oldFrame->components[ channel->componentsOffset + componentsApplied ]; newOrigin[ 0 ] = newFrame->components[ channel->componentsOffset + componentsApplied ]; componentsApplied++; } if ( channel->componentsBits & COMPONENT_BIT_TY ) { oldOrigin[ 1 ] = oldFrame->components[ channel->componentsOffset + componentsApplied ]; newOrigin[ 1 ] = newFrame->components[ channel->componentsOffset + componentsApplied ]; componentsApplied++; } if ( channel->componentsBits & COMPONENT_BIT_TZ ) { oldOrigin[ 2 ] = oldFrame->components[ channel->componentsOffset + componentsApplied ]; newOrigin[ 2 ] = newFrame->components[ channel->componentsOffset + componentsApplied ]; componentsApplied++; } // update quaternion rotation bits if ( channel->componentsBits & COMPONENT_BIT_QX ) { ( ( vec_t * ) oldQuat ) [ 0 ] = oldFrame->components[ channel->componentsOffset + componentsApplied ]; ( ( vec_t * ) newQuat ) [ 0 ] = newFrame->components[ channel->componentsOffset + componentsApplied ]; componentsApplied++; } if ( channel->componentsBits & COMPONENT_BIT_QY ) { ( ( vec_t * ) oldQuat ) [ 1 ] = oldFrame->components[ channel->componentsOffset + componentsApplied ]; ( ( vec_t * ) newQuat ) [ 1 ] = newFrame->components[ channel->componentsOffset + componentsApplied ]; componentsApplied++; } if ( channel->componentsBits & COMPONENT_BIT_QZ ) { ( ( vec_t * ) oldQuat ) [ 2 ] = oldFrame->components[ channel->componentsOffset + componentsApplied ]; ( ( vec_t * ) newQuat ) [ 2 ] = newFrame->components[ channel->componentsOffset + componentsApplied ]; } QuatCalcW( oldQuat ); QuatNormalize( oldQuat ); QuatCalcW( newQuat ); QuatNormalize( newQuat ); #if 1 VectorLerp( oldOrigin, newOrigin, frac, lerpedOrigin ); QuatSlerp( oldQuat, newQuat, frac, lerpedQuat ); #else VectorCopy( newOrigin, lerpedOrigin ); QuatCopy( newQuat, lerpedQuat ); #endif // copy lerped information to the bone + extra data skel->bones[ i ].parentIndex = channel->parentIndex; if ( channel->parentIndex < 0 && clearOrigin ) { VectorClear( skel->bones[ i ].t.trans ); QuatClear( skel->bones[ i ].t.rot ); // move bounding box back VectorSubtract( skel->bounds[ 0 ], lerpedOrigin, skel->bounds[ 0 ] ); VectorSubtract( skel->bounds[ 1 ], lerpedOrigin, skel->bounds[ 1 ] ); } else { VectorCopy( lerpedOrigin, skel->bones[ i ].t.trans ); } QuatCopy( lerpedQuat, skel->bones[ i ].t.rot ); skel->bones[ i ].t.scale = 1.0f; #if defined( REFBONE_NAMES ) Q_strncpyz( skel->bones[ i ].name, channel->name, sizeof( skel->bones[ i ].name ) ); #endif } skel->numBones = anim->numChannels; skel->type = refSkeletonType_t::SK_RELATIVE; return true; } // FIXME: clear existing bones and bounds? return false; }
/* ================= R_LoadPSK ================= */ qboolean R_LoadPSK( model_t *mod, void *buffer, int bufferSize, const char *modName ) { int i, j, k; memStream_t *stream = NULL; axChunkHeader_t chunkHeader; int numPoints; axPoint_t *point; axPoint_t *points = NULL; int numVertexes; axVertex_t *vertex; axVertex_t *vertexes = NULL; //int numSmoothGroups; int numTriangles; axTriangle_t *triangle; axTriangle_t *triangles = NULL; int numMaterials; axMaterial_t *material; axMaterial_t *materials = NULL; int numReferenceBones; axReferenceBone_t *refBone; axReferenceBone_t *refBones = NULL; int numWeights; axBoneWeight_t *axWeight; axBoneWeight_t *axWeights = NULL; md5Model_t *md5; md5Bone_t *md5Bone; vec3_t boneOrigin; quat_t boneQuat; //matrix_t boneMat; int materialIndex, oldMaterialIndex; int numRemaining; growList_t sortedTriangles; growList_t vboVertexes; growList_t vboTriangles; growList_t vboSurfaces; int numBoneReferences; int boneReferences[ MAX_BONES ]; matrix_t unrealToQuake; #define DeallocAll() Com_Dealloc( materials ); \ Com_Dealloc( points ); \ Com_Dealloc( vertexes ); \ Com_Dealloc( triangles ); \ Com_Dealloc( refBones ); \ Com_Dealloc( axWeights ); \ FreeMemStream( stream ); //MatrixSetupScale(unrealToQuake, 1, -1, 1); MatrixFromAngles( unrealToQuake, 0, 90, 0 ); stream = AllocMemStream( (byte*) buffer, bufferSize ); GetChunkHeader( stream, &chunkHeader ); // check indent again if ( Q_strnicmp( chunkHeader.ident, "ACTRHEAD", 8 ) ) { ri.Printf( PRINT_WARNING, "R_LoadPSK: '%s' has wrong chunk indent ('%s' should be '%s')\n", modName, chunkHeader.ident, "ACTRHEAD" ); DeallocAll(); return qfalse; } PrintChunkHeader( &chunkHeader ); mod->type = MOD_MD5; mod->dataSize += sizeof( md5Model_t ); md5 = mod->md5 = (md5Model_t*) ri.Hunk_Alloc( sizeof( md5Model_t ), h_low ); // read points GetChunkHeader( stream, &chunkHeader ); if ( Q_strnicmp( chunkHeader.ident, "PNTS0000", 8 ) ) { ri.Printf( PRINT_WARNING, "R_LoadPSK: '%s' has wrong chunk indent ('%s' should be '%s')\n", modName, chunkHeader.ident, "PNTS0000" ); DeallocAll(); return qfalse; } if ( chunkHeader.dataSize != sizeof( axPoint_t ) ) { ri.Printf( PRINT_WARNING, "R_LoadPSK: '%s' has wrong chunk dataSize ('%i' should be '%i')\n", modName, chunkHeader.dataSize, ( int ) sizeof( axPoint_t ) ); DeallocAll(); return qfalse; } PrintChunkHeader( &chunkHeader ); numPoints = chunkHeader.numData; points = (axPoint_t*) Com_Allocate( numPoints * sizeof( axPoint_t ) ); for ( i = 0, point = points; i < numPoints; i++, point++ ) { point->point[ 0 ] = MemStreamGetFloat( stream ); point->point[ 1 ] = MemStreamGetFloat( stream ); point->point[ 2 ] = MemStreamGetFloat( stream ); #if 0 // Tr3B: HACK convert from Unreal coordinate system to the Quake one MatrixTransformPoint2( unrealToQuake, point->point ); #endif } // read vertices GetChunkHeader( stream, &chunkHeader ); if ( Q_strnicmp( chunkHeader.ident, "VTXW0000", 8 ) ) { ri.Printf( PRINT_WARNING, "R_LoadPSK: '%s' has wrong chunk indent ('%s' should be '%s')\n", modName, chunkHeader.ident, "VTXW0000" ); DeallocAll(); return qfalse; } if ( chunkHeader.dataSize != sizeof( axVertex_t ) ) { ri.Printf( PRINT_WARNING, "R_LoadPSK: '%s' has wrong chunk dataSize ('%i' should be '%i')\n", modName, chunkHeader.dataSize, ( int ) sizeof( axVertex_t ) ); DeallocAll(); return qfalse; } PrintChunkHeader( &chunkHeader ); numVertexes = chunkHeader.numData; vertexes = (axVertex_t*) Com_Allocate( numVertexes * sizeof( axVertex_t ) ); for ( i = 0, vertex = vertexes; i < numVertexes; i++, vertex++ ) { vertex->pointIndex = MemStreamGetShort( stream ); if ( vertex->pointIndex < 0 || vertex->pointIndex >= numPoints ) { ri.Printf( PRINT_WARNING, "R_LoadPSK: '%s' has vertex with point index out of range (%i while max %i)\n", modName, vertex->pointIndex, numPoints ); DeallocAll(); return qfalse; } vertex->unknownA = MemStreamGetShort( stream ); vertex->st[ 0 ] = MemStreamGetFloat( stream ); vertex->st[ 1 ] = MemStreamGetFloat( stream ); vertex->materialIndex = MemStreamGetC( stream ); vertex->reserved = MemStreamGetC( stream ); vertex->unknownB = MemStreamGetShort( stream ); #if 0 ri.Printf( PRINT_ALL, "R_LoadPSK: axVertex_t(%i):\n" "axVertex:pointIndex: %i\n" "axVertex:unknownA: %i\n" "axVertex::st: %f %f\n" "axVertex:materialIndex: %i\n" "axVertex:reserved: %d\n" "axVertex:unknownB: %d\n", i, vertex->pointIndex, vertex->unknownA, vertex->st[ 0 ], vertex->st[ 1 ], vertex->materialIndex, vertex->reserved, vertex->unknownB ); #endif } // read triangles GetChunkHeader( stream, &chunkHeader ); if ( Q_strnicmp( chunkHeader.ident, "FACE0000", 8 ) ) { ri.Printf( PRINT_WARNING, "R_LoadPSK: '%s' has wrong chunk indent ('%s' should be '%s')\n", modName, chunkHeader.ident, "FACE0000" ); DeallocAll(); return qfalse; } if ( chunkHeader.dataSize != sizeof( axTriangle_t ) ) { ri.Printf( PRINT_WARNING, "R_LoadPSK: '%s' has wrong chunk dataSize ('%i' should be '%i')\n", modName, chunkHeader.dataSize, ( int ) sizeof( axTriangle_t ) ); DeallocAll(); return qfalse; } PrintChunkHeader( &chunkHeader ); numTriangles = chunkHeader.numData; triangles = (axTriangle_t*) Com_Allocate( numTriangles * sizeof( axTriangle_t ) ); for ( i = 0, triangle = triangles; i < numTriangles; i++, triangle++ ) { for ( j = 0; j < 3; j++ ) //for(j = 2; j >= 0; j--) { triangle->indexes[ j ] = MemStreamGetShort( stream ); if ( triangle->indexes[ j ] >= numVertexes ) { ri.Printf( PRINT_WARNING, "R_LoadPSK: '%s' has triangle with vertex index out of range (%i while max %i)\n", modName, triangle->indexes[ j ], numVertexes ); DeallocAll(); return qfalse; } } triangle->materialIndex = MemStreamGetC( stream ); triangle->materialIndex2 = MemStreamGetC( stream ); triangle->smoothingGroups = MemStreamGetLong( stream ); } // read materials GetChunkHeader( stream, &chunkHeader ); if ( Q_strnicmp( chunkHeader.ident, "MATT0000", 8 ) ) { ri.Printf( PRINT_WARNING, "R_LoadPSK: '%s' has wrong chunk indent ('%s' should be '%s')\n", modName, chunkHeader.ident, "MATT0000" ); DeallocAll(); return qfalse; } if ( chunkHeader.dataSize != sizeof( axMaterial_t ) ) { ri.Printf( PRINT_WARNING, "R_LoadPSK: '%s' has wrong chunk dataSize ('%i' should be '%i')\n", modName, chunkHeader.dataSize, ( int ) sizeof( axMaterial_t ) ); DeallocAll(); return qfalse; } PrintChunkHeader( &chunkHeader ); numMaterials = chunkHeader.numData; materials = (axMaterial_t*) Com_Allocate( numMaterials * sizeof( axMaterial_t ) ); for ( i = 0, material = materials; i < numMaterials; i++, material++ ) { MemStreamRead( stream, material->name, sizeof( material->name ) ); ri.Printf( PRINT_ALL, "R_LoadPSK: material name: '%s'\n", material->name ); material->shaderIndex = MemStreamGetLong( stream ); material->polyFlags = MemStreamGetLong( stream ); material->auxMaterial = MemStreamGetLong( stream ); material->auxFlags = MemStreamGetLong( stream ); material->lodBias = MemStreamGetLong( stream ); material->lodStyle = MemStreamGetLong( stream ); } for ( i = 0, vertex = vertexes; i < numVertexes; i++, vertex++ ) { if ( vertex->materialIndex < 0 || vertex->materialIndex >= numMaterials ) { ri.Printf( PRINT_WARNING, "R_LoadPSK: '%s' has vertex with material index out of range (%i while max %i)\n", modName, vertex->materialIndex, numMaterials ); DeallocAll(); return qfalse; } } for ( i = 0, triangle = triangles; i < numTriangles; i++, triangle++ ) { if ( triangle->materialIndex < 0 || triangle->materialIndex >= numMaterials ) { ri.Printf( PRINT_WARNING, "R_LoadPSK: '%s' has triangle with material index out of range (%i while max %i)\n", modName, triangle->materialIndex, numMaterials ); DeallocAll(); return qfalse; } } // read reference bones GetChunkHeader( stream, &chunkHeader ); if ( Q_strnicmp( chunkHeader.ident, "REFSKELT", 8 ) ) { ri.Printf( PRINT_WARNING, "R_LoadPSK: '%s' has wrong chunk indent ('%s' should be '%s')\n", modName, chunkHeader.ident, "REFSKELT" ); DeallocAll(); return qfalse; } if ( chunkHeader.dataSize != sizeof( axReferenceBone_t ) ) { ri.Printf( PRINT_WARNING, "R_LoadPSK: '%s' has wrong chunk dataSize ('%i' should be '%i')\n", modName, chunkHeader.dataSize, ( int ) sizeof( axReferenceBone_t ) ); DeallocAll(); return qfalse; } PrintChunkHeader( &chunkHeader ); numReferenceBones = chunkHeader.numData; refBones = (axReferenceBone_t*) Com_Allocate( numReferenceBones * sizeof( axReferenceBone_t ) ); for ( i = 0, refBone = refBones; i < numReferenceBones; i++, refBone++ ) { MemStreamRead( stream, refBone->name, sizeof( refBone->name ) ); //ri.Printf(PRINT_ALL, "R_LoadPSK: reference bone name: '%s'\n", refBone->name); refBone->flags = MemStreamGetLong( stream ); refBone->numChildren = MemStreamGetLong( stream ); refBone->parentIndex = MemStreamGetLong( stream ); GetBone( stream, &refBone->bone ); #if 0 ri.Printf( PRINT_ALL, "R_LoadPSK: axReferenceBone_t(%i):\n" "axReferenceBone_t::name: '%s'\n" "axReferenceBone_t::flags: %i\n" "axReferenceBone_t::numChildren %i\n" "axReferenceBone_t::parentIndex: %i\n" "axReferenceBone_t::quat: %f %f %f %f\n" "axReferenceBone_t::position: %f %f %f\n" "axReferenceBone_t::length: %f\n" "axReferenceBone_t::xSize: %f\n" "axReferenceBone_t::ySize: %f\n" "axReferenceBone_t::zSize: %f\n", i, refBone->name, refBone->flags, refBone->numChildren, refBone->parentIndex, refBone->bone.quat[ 0 ], refBone->bone.quat[ 1 ], refBone->bone.quat[ 2 ], refBone->bone.quat[ 3 ], refBone->bone.position[ 0 ], refBone->bone.position[ 1 ], refBone->bone.position[ 2 ], refBone->bone.length, refBone->bone.xSize, refBone->bone.ySize, refBone->bone.zSize ); #endif } // read bone weights GetChunkHeader( stream, &chunkHeader ); if ( Q_strnicmp( chunkHeader.ident, "RAWWEIGHTS", 10 ) ) { ri.Printf( PRINT_WARNING, "R_LoadPSK: '%s' has wrong chunk indent ('%s' should be '%s')\n", modName, chunkHeader.ident, "RAWWEIGHTS" ); DeallocAll(); return qfalse; } if ( chunkHeader.dataSize != sizeof( axBoneWeight_t ) ) { ri.Printf( PRINT_WARNING, "R_LoadPSK: '%s' has wrong chunk dataSize ('%i' should be '%i')\n", modName, chunkHeader.dataSize, ( int ) sizeof( axBoneWeight_t ) ); DeallocAll(); return qfalse; } PrintChunkHeader( &chunkHeader ); numWeights = chunkHeader.numData; axWeights = (axBoneWeight_t*) Com_Allocate( numWeights * sizeof( axBoneWeight_t ) ); for ( i = 0, axWeight = axWeights; i < numWeights; i++, axWeight++ ) { axWeight->weight = MemStreamGetFloat( stream ); axWeight->pointIndex = MemStreamGetLong( stream ); axWeight->boneIndex = MemStreamGetLong( stream ); #if 0 ri.Printf( PRINT_ALL, "R_LoadPSK: axBoneWeight_t(%i):\n" "axBoneWeight_t::weight: %f\n" "axBoneWeight_t::pointIndex %i\n" "axBoneWeight_t::boneIndex: %i\n", i, axWeight->weight, axWeight->pointIndex, axWeight->boneIndex ); #endif } // // convert the model to an internal MD5 representation // md5->numBones = numReferenceBones; // calc numMeshes <number> /* numSmoothGroups = 0; for(i = 0, triangle = triangles; i < numTriangles; i++, triangle++) { if(triangle->smoothingGroups) { } } */ if ( md5->numBones < 1 ) { ri.Printf( PRINT_WARNING, "R_LoadPSK: '%s' has no bones\n", modName ); DeallocAll(); return qfalse; } if ( md5->numBones > MAX_BONES ) { ri.Printf( PRINT_WARNING, "R_LoadPSK: '%s' has more than %i bones (%i)\n", modName, MAX_BONES, md5->numBones ); DeallocAll(); return qfalse; } //ri.Printf(PRINT_ALL, "R_LoadPSK: '%s' has %i bones\n", modName, md5->numBones); // copy all reference bones md5->bones = (md5Bone_t*) ri.Hunk_Alloc( sizeof( *md5Bone ) * md5->numBones, h_low ); for ( i = 0, md5Bone = md5->bones, refBone = refBones; i < md5->numBones; i++, md5Bone++, refBone++ ) { Q_strncpyz( md5Bone->name, refBone->name, sizeof( md5Bone->name ) ); if ( i == 0 ) { md5Bone->parentIndex = refBone->parentIndex - 1; } else { md5Bone->parentIndex = refBone->parentIndex; } //ri.Printf(PRINT_ALL, "R_LoadPSK: '%s' has bone '%s' with parent index %i\n", modName, md5Bone->name, md5Bone->parentIndex); if ( md5Bone->parentIndex >= md5->numBones ) { DeallocAll(); ri.Error( ERR_DROP, "R_LoadPSK: '%s' has bone '%s' with bad parent index %i while numBones is %i", modName, md5Bone->name, md5Bone->parentIndex, md5->numBones ); } for ( j = 0; j < 3; j++ ) { boneOrigin[ j ] = refBone->bone.position[ j ]; } // Tr3B: I have really no idea why the .psk format stores the first quaternion with inverted quats. // Furthermore only the X and Z components of the first quat are inverted ?!?! if ( i == 0 ) { boneQuat[ 0 ] = refBone->bone.quat[ 0 ]; boneQuat[ 1 ] = -refBone->bone.quat[ 1 ]; boneQuat[ 2 ] = refBone->bone.quat[ 2 ]; boneQuat[ 3 ] = refBone->bone.quat[ 3 ]; } else { boneQuat[ 0 ] = -refBone->bone.quat[ 0 ]; boneQuat[ 1 ] = -refBone->bone.quat[ 1 ]; boneQuat[ 2 ] = -refBone->bone.quat[ 2 ]; boneQuat[ 3 ] = refBone->bone.quat[ 3 ]; } VectorCopy( boneOrigin, md5Bone->origin ); //MatrixTransformPoint(unrealToQuake, boneOrigin, md5Bone->origin); QuatCopy( boneQuat, md5Bone->rotation ); //QuatClear(md5Bone->rotation); #if 0 ri.Printf( PRINT_ALL, "R_LoadPSK: md5Bone_t(%i):\n" "md5Bone_t::name: '%s'\n" "md5Bone_t::parentIndex: %i\n" "md5Bone_t::quat: %f %f %f %f\n" "md5bone_t::position: %f %f %f\n", i, md5Bone->name, md5Bone->parentIndex, md5Bone->rotation[ 0 ], md5Bone->rotation[ 1 ], md5Bone->rotation[ 2 ], md5Bone->rotation[ 3 ], md5Bone->origin[ 0 ], md5Bone->origin[ 1 ], md5Bone->origin[ 2 ] ); #endif if ( md5Bone->parentIndex >= 0 ) { vec3_t rotated; quat_t quat; md5Bone_t *parent; parent = &md5->bones[ md5Bone->parentIndex ]; QuatTransformVector( parent->rotation, md5Bone->origin, rotated ); //QuatTransformVector(md5Bone->rotation, md5Bone->origin, rotated); VectorAdd( parent->origin, rotated, md5Bone->origin ); QuatMultiply1( parent->rotation, md5Bone->rotation, quat ); QuatCopy( quat, md5Bone->rotation ); } #if 0 ri.Printf( PRINT_ALL, "R_LoadPSK: md5Bone_t(%i):\n" "md5Bone_t::name: '%s'\n" "md5Bone_t::parentIndex: %i\n" "md5Bone_t::quat: %f %f %f %f\n" "md5bone_t::position: %f %f %f\n", i, md5Bone->name, md5Bone->parentIndex, md5Bone->rotation[ 0 ], md5Bone->rotation[ 1 ], md5Bone->rotation[ 2 ], md5Bone->rotation[ 3 ], md5Bone->origin[ 0 ], md5Bone->origin[ 1 ], md5Bone->origin[ 2 ] ); #endif } Com_InitGrowList( &vboVertexes, 10000 ); for ( i = 0, vertex = vertexes; i < numVertexes; i++, vertex++ ) { md5Vertex_t *vboVert = (md5Vertex_t*) Com_Allocate( sizeof( *vboVert ) ); for ( j = 0; j < 3; j++ ) { vboVert->position[ j ] = points[ vertex->pointIndex ].point[ j ]; } vboVert->texCoords[ 0 ] = vertex->st[ 0 ]; vboVert->texCoords[ 1 ] = vertex->st[ 1 ]; // find number of associated weights vboVert->numWeights = 0; for ( j = 0, axWeight = axWeights; j < numWeights; j++, axWeight++ ) { if ( axWeight->pointIndex == vertex->pointIndex && axWeight->weight > 0.0f ) { vboVert->numWeights++; } } if ( vboVert->numWeights > MAX_WEIGHTS ) { DeallocAll(); ri.Error( ERR_DROP, "R_LoadPSK: vertex %i requires more weights %i than the maximum of %i in model '%s'", i, vboVert->numWeights, MAX_WEIGHTS, modName ); //ri.Printf(PRINT_WARNING, "R_LoadPSK: vertex %i requires more weights %i than the maximum of %i in model '%s'\n", i, vboVert->numWeights, MAX_WEIGHTS, modName); } for ( j = 0, axWeight = axWeights, k = 0; j < numWeights; j++, axWeight++ ) { if ( axWeight->pointIndex == vertex->pointIndex && axWeight->weight > 0.0f ) { vboVert->boneIndexes[ k ] = axWeight->boneIndex; vboVert->boneWeights[ k ] = axWeight->weight; k++; } } Com_AddToGrowList( &vboVertexes, vboVert ); } ClearBounds( md5->bounds[ 0 ], md5->bounds[ 1 ] ); for ( i = 0, vertex = vertexes; i < numVertexes; i++, vertex++ ) { AddPointToBounds( points[ vertex->pointIndex ].point, md5->bounds[ 0 ], md5->bounds[ 1 ] ); } #if 0 ri.Printf( PRINT_ALL, "R_LoadPSK: AABB (%i %i %i) (%i %i %i)\n", ( int ) md5->bounds[ 0 ][ 0 ], ( int ) md5->bounds[ 0 ][ 1 ], ( int ) md5->bounds[ 0 ][ 2 ], ( int ) md5->bounds[ 1 ][ 0 ], ( int ) md5->bounds[ 1 ][ 1 ], ( int ) md5->bounds[ 1 ][ 2 ] ); #endif // sort triangles qsort( triangles, numTriangles, sizeof( axTriangle_t ), CompareTrianglesByMaterialIndex ); Com_InitGrowList( &sortedTriangles, 1000 ); for ( i = 0, triangle = triangles; i < numTriangles; i++, triangle++ ) { skelTriangle_t *sortTri = (skelTriangle_t*) Com_Allocate( sizeof( *sortTri ) ); for ( j = 0; j < 3; j++ ) { sortTri->indexes[ j ] = triangle->indexes[ j ]; sortTri->vertexes[ j ] = (md5Vertex_t*) Com_GrowListElement( &vboVertexes, triangle->indexes[ j ] ); } sortTri->referenced = qfalse; Com_AddToGrowList( &sortedTriangles, sortTri ); } // calc tangent spaces #if 1 { md5Vertex_t *v0, *v1, *v2; const float *p0, *p1, *p2; const float *t0, *t1, *t2; vec3_t tangent; vec3_t binormal; vec3_t normal; for ( j = 0; j < vboVertexes.currentElements; j++ ) { v0 = (md5Vertex_t*) Com_GrowListElement( &vboVertexes, j ); VectorClear( v0->tangent ); VectorClear( v0->binormal ); VectorClear( v0->normal ); } for ( j = 0; j < sortedTriangles.currentElements; j++ ) { skelTriangle_t *tri = (skelTriangle_t*) Com_GrowListElement( &sortedTriangles, j ); v0 = (md5Vertex_t*) Com_GrowListElement( &vboVertexes, tri->indexes[ 0 ] ); v1 = (md5Vertex_t*) Com_GrowListElement( &vboVertexes, tri->indexes[ 1 ] ); v2 = (md5Vertex_t*) Com_GrowListElement( &vboVertexes, tri->indexes[ 2 ] ); p0 = v0->position; p1 = v1->position; p2 = v2->position; t0 = v0->texCoords; t1 = v1->texCoords; t2 = v2->texCoords; #if 1 R_CalcTangentSpace( tangent, binormal, normal, p0, p1, p2, t0, t1, t2 ); #else R_CalcNormalForTriangle( normal, p0, p1, p2 ); R_CalcTangentsForTriangle( tangent, binormal, p0, p1, p2, t0, t1, t2 ); #endif for ( k = 0; k < 3; k++ ) { float *v; v0 = (md5Vertex_t*) Com_GrowListElement( &vboVertexes, tri->indexes[ k ] ); v = v0->tangent; VectorAdd( v, tangent, v ); v = v0->binormal; VectorAdd( v, binormal, v ); v = v0->normal; VectorAdd( v, normal, v ); } } for ( j = 0; j < vboVertexes.currentElements; j++ ) { v0 = (md5Vertex_t*) Com_GrowListElement( &vboVertexes, j ); VectorNormalize( v0->tangent ); VectorNormalize( v0->binormal ); VectorNormalize( v0->normal ); } } #else { float bb, s, t; vec3_t bary; vec3_t faceNormal; md5Vertex_t *dv[ 3 ]; for ( j = 0; j < sortedTriangles.currentElements; j++ ) { skelTriangle_t *tri = Com_GrowListElement( &sortedTriangles, j ); dv[ 0 ] = Com_GrowListElement( &vboVertexes, tri->indexes[ 0 ] ); dv[ 1 ] = Com_GrowListElement( &vboVertexes, tri->indexes[ 1 ] ); dv[ 2 ] = Com_GrowListElement( &vboVertexes, tri->indexes[ 2 ] ); R_CalcNormalForTriangle( faceNormal, dv[ 0 ]->position, dv[ 1 ]->position, dv[ 2 ]->position ); // calculate barycentric basis for the triangle bb = ( dv[ 1 ]->texCoords[ 0 ] - dv[ 0 ]->texCoords[ 0 ] ) * ( dv[ 2 ]->texCoords[ 1 ] - dv[ 0 ]->texCoords[ 1 ] ) - ( dv[ 2 ]->texCoords[ 0 ] - dv[ 0 ]->texCoords[ 0 ] ) * ( dv[ 1 ]->texCoords[ 1 ] - dv[ 0 ]->texCoords[ 1 ] ); if ( fabs( bb ) < 0.00000001f ) { continue; } // do each vertex for ( k = 0; k < 3; k++ ) { // calculate s tangent vector s = dv[ k ]->texCoords[ 0 ] + 10.0f; t = dv[ k ]->texCoords[ 1 ]; bary[ 0 ] = ( ( dv[ 1 ]->texCoords[ 0 ] - s ) * ( dv[ 2 ]->texCoords[ 1 ] - t ) - ( dv[ 2 ]->texCoords[ 0 ] - s ) * ( dv[ 1 ]->texCoords[ 1 ] - t ) ) / bb; bary[ 1 ] = ( ( dv[ 2 ]->texCoords[ 0 ] - s ) * ( dv[ 0 ]->texCoords[ 1 ] - t ) - ( dv[ 0 ]->texCoords[ 0 ] - s ) * ( dv[ 2 ]->texCoords[ 1 ] - t ) ) / bb; bary[ 2 ] = ( ( dv[ 0 ]->texCoords[ 0 ] - s ) * ( dv[ 1 ]->texCoords[ 1 ] - t ) - ( dv[ 1 ]->texCoords[ 0 ] - s ) * ( dv[ 0 ]->texCoords[ 1 ] - t ) ) / bb; dv[ k ]->tangent[ 0 ] = bary[ 0 ] * dv[ 0 ]->position[ 0 ] + bary[ 1 ] * dv[ 1 ]->position[ 0 ] + bary[ 2 ] * dv[ 2 ]->position[ 0 ]; dv[ k ]->tangent[ 1 ] = bary[ 0 ] * dv[ 0 ]->position[ 1 ] + bary[ 1 ] * dv[ 1 ]->position[ 1 ] + bary[ 2 ] * dv[ 2 ]->position[ 1 ]; dv[ k ]->tangent[ 2 ] = bary[ 0 ] * dv[ 0 ]->position[ 2 ] + bary[ 1 ] * dv[ 1 ]->position[ 2 ] + bary[ 2 ] * dv[ 2 ]->position[ 2 ]; VectorSubtract( dv[ k ]->tangent, dv[ k ]->position, dv[ k ]->tangent ); VectorNormalize( dv[ k ]->tangent ); // calculate t tangent vector (binormal) s = dv[ k ]->texCoords[ 0 ]; t = dv[ k ]->texCoords[ 1 ] + 10.0f; bary[ 0 ] = ( ( dv[ 1 ]->texCoords[ 0 ] - s ) * ( dv[ 2 ]->texCoords[ 1 ] - t ) - ( dv[ 2 ]->texCoords[ 0 ] - s ) * ( dv[ 1 ]->texCoords[ 1 ] - t ) ) / bb; bary[ 1 ] = ( ( dv[ 2 ]->texCoords[ 0 ] - s ) * ( dv[ 0 ]->texCoords[ 1 ] - t ) - ( dv[ 0 ]->texCoords[ 0 ] - s ) * ( dv[ 2 ]->texCoords[ 1 ] - t ) ) / bb; bary[ 2 ] = ( ( dv[ 0 ]->texCoords[ 0 ] - s ) * ( dv[ 1 ]->texCoords[ 1 ] - t ) - ( dv[ 1 ]->texCoords[ 0 ] - s ) * ( dv[ 0 ]->texCoords[ 1 ] - t ) ) / bb; dv[ k ]->binormal[ 0 ] = bary[ 0 ] * dv[ 0 ]->position[ 0 ] + bary[ 1 ] * dv[ 1 ]->position[ 0 ] + bary[ 2 ] * dv[ 2 ]->position[ 0 ]; dv[ k ]->binormal[ 1 ] = bary[ 0 ] * dv[ 0 ]->position[ 1 ] + bary[ 1 ] * dv[ 1 ]->position[ 1 ] + bary[ 2 ] * dv[ 2 ]->position[ 1 ]; dv[ k ]->binormal[ 2 ] = bary[ 0 ] * dv[ 0 ]->position[ 2 ] + bary[ 1 ] * dv[ 1 ]->position[ 2 ] + bary[ 2 ] * dv[ 2 ]->position[ 2 ]; VectorSubtract( dv[ k ]->binormal, dv[ k ]->position, dv[ k ]->binormal ); VectorNormalize( dv[ k ]->binormal ); // calculate the normal as cross product N=TxB #if 0 CrossProduct( dv[ k ]->tangent, dv[ k ]->binormal, dv[ k ]->normal ); VectorNormalize( dv[ k ]->normal ); // Gram-Schmidt orthogonalization process for B // compute the cross product B=NxT to obtain // an orthogonal basis CrossProduct( dv[ k ]->normal, dv[ k ]->tangent, dv[ k ]->binormal ); if ( DotProduct( dv[ k ]->normal, faceNormal ) < 0 ) { VectorInverse( dv[ k ]->normal ); //VectorInverse(dv[k]->tangent); //VectorInverse(dv[k]->binormal); } #else VectorAdd( dv[ k ]->normal, faceNormal, dv[ k ]->normal ); #endif } } #if 1 for ( j = 0; j < vboVertexes.currentElements; j++ ) { dv[ 0 ] = Com_GrowListElement( &vboVertexes, j ); //VectorNormalize(dv[0]->tangent); //VectorNormalize(dv[0]->binormal); VectorNormalize( dv[ 0 ]->normal ); } #endif } #endif #if 0 { md5Vertex_t *v0, *v1; // do another extra smoothing for normals to avoid flat shading for ( j = 0; j < vboVertexes.currentElements; j++ ) { v0 = Com_GrowListElement( &vboVertexes, j ); for ( k = 0; k < vboVertexes.currentElements; k++ ) { if ( j == k ) { continue; } v1 = Com_GrowListElement( &vboVertexes, k ); if ( VectorCompare( v0->position, v1->position ) ) { VectorAdd( v0->position, v1->normal, v0->normal ); } } VectorNormalize( v0->normal ); } } #endif // split the surfaces into VBO surfaces by the maximum number of GPU vertex skinning bones Com_InitGrowList( &vboSurfaces, 10 ); materialIndex = oldMaterialIndex = -1; for ( i = 0; i < numTriangles; i++ ) { triangle = &triangles[ i ]; materialIndex = triangle->materialIndex; if ( materialIndex != oldMaterialIndex ) { oldMaterialIndex = materialIndex; numRemaining = sortedTriangles.currentElements - i; while ( numRemaining ) { numBoneReferences = 0; Com_Memset( boneReferences, 0, sizeof( boneReferences ) ); Com_InitGrowList( &vboTriangles, 1000 ); for ( j = i; j < sortedTriangles.currentElements; j++ ) { skelTriangle_t *sortTri; triangle = &triangles[ j ]; materialIndex = triangle->materialIndex; if ( materialIndex != oldMaterialIndex ) { continue; } sortTri = (skelTriangle_t*) Com_GrowListElement( &sortedTriangles, j ); if ( sortTri->referenced ) { continue; } if ( AddTriangleToVBOTriangleList( &vboTriangles, sortTri, &numBoneReferences, boneReferences ) ) { sortTri->referenced = qtrue; } } for ( j = 0; j < MAX_BONES; j++ ) { if ( boneReferences[ j ] > 0 ) { ri.Printf( PRINT_ALL, "R_LoadPSK: referenced bone: '%s'\n", ( j < numReferenceBones ) ? refBones[ j ].name : NULL ); } } if ( !vboTriangles.currentElements ) { ri.Printf( PRINT_WARNING, "R_LoadPSK: could not add triangles to a remaining VBO surface for model '%s'\n", modName ); break; } // FIXME skinIndex AddSurfaceToVBOSurfacesList2( &vboSurfaces, &vboTriangles, &vboVertexes, md5, vboSurfaces.currentElements, materials[ oldMaterialIndex ].name, numBoneReferences, boneReferences ); numRemaining -= vboTriangles.currentElements; Com_DestroyGrowList( &vboTriangles ); } } } for ( j = 0; j < sortedTriangles.currentElements; j++ ) { skelTriangle_t *sortTri = (skelTriangle_t*) Com_GrowListElement( &sortedTriangles, j ); Com_Dealloc( sortTri ); } Com_DestroyGrowList( &sortedTriangles ); for ( j = 0; j < vboVertexes.currentElements; j++ ) { md5Vertex_t *v = (md5Vertex_t*) Com_GrowListElement( &vboVertexes, j ); Com_Dealloc( v ); } Com_DestroyGrowList( &vboVertexes ); // move VBO surfaces list to hunk md5->numVBOSurfaces = vboSurfaces.currentElements; md5->vboSurfaces = (srfVBOMD5Mesh_t**) ri.Hunk_Alloc( md5->numVBOSurfaces * sizeof( *md5->vboSurfaces ), h_low ); for ( i = 0; i < md5->numVBOSurfaces; i++ ) { md5->vboSurfaces[ i ] = ( srfVBOMD5Mesh_t * ) Com_GrowListElement( &vboSurfaces, i ); } Com_DestroyGrowList( &vboSurfaces ); FreeMemStream( stream ); Com_Dealloc( points ); Com_Dealloc( vertexes ); Com_Dealloc( triangles ); Com_Dealloc( materials ); ri.Printf( PRINT_DEVELOPER, "%i VBO surfaces created for PSK model '%s'\n", md5->numVBOSurfaces, modName ); return qtrue; }
iqmblend_t * Mod_IQMBuildBlendPalette (iqm_t *iqm, int *size) { int i, j; iqmvertexarray *bindices = 0; iqmvertexarray *bweights = 0; iqmblend_t *blend_list; int num_blends; hashtab_t *blend_hash; for (i = 0; i < iqm->num_arrays; i++) { if (iqm->vertexarrays[i].type == IQM_BLENDINDEXES) bindices = &iqm->vertexarrays[i]; if (iqm->vertexarrays[i].type == IQM_BLENDWEIGHTS) bweights = &iqm->vertexarrays[i]; } if (!bindices || !bweights) { // Not necessarily an error: might be a static model with no bones // Either way, no need to make a blend palette Sys_MaskPrintf (SYS_MODEL, "bone index or weight array missing\n"); *size = 0; return 0; } blend_list = calloc (MAX_BLENDS, sizeof (iqmblend_t)); for (i = 0; i < iqm->num_joints; i++) { blend_list[i].indices[0] = i; blend_list[i].weights[0] = 255; } num_blends = iqm->num_joints; blend_hash = Hash_NewTable (1023, 0, 0, 0); Hash_SetHashCompare (blend_hash, blend_get_hash, blend_compare); for (i = 0; i < iqm->num_verts; i++) { byte *vert = iqm->vertices + i * iqm->stride; byte *bi = vert + bindices->offset; byte *bw = vert + bweights->offset; iqmblend_t blend; iqmblend_t *bl; // First, canonicalize vextex bone data: // bone indices are in increasing order // bone weight of zero is never followed by a non-zero weight // bone weight of zero has bone index of zero // if the weight is zero, ensure the index is also zero // also, ensure non-zero weights never follow zero weights for (j = 0; j < 4; j++) { if (!bw[j]) { bi[j] = 0; } else { if (j && !bw[j-1]) { swap_bones (bi, bw, j - 1, j); j = 0; // force a rescan } } } // sort the bones such that the indeces are increasing (unless the // weight is zero) for (j = 0; j < 3; j++) { if (!bw[j+1]) // zero weight == end of list break; if (bi[j] > bi[j+1]) { swap_bones (bi, bw, j, j + 1); j = -1; // force rescan } } // Now that the bone data is canonical, it can be hashed. // However, no need to check other combinations if the vertex has // only one influencing bone: the bone index will only change format. if (!bw[1]) { *(uint32_t *) bi = bi[0]; continue; } QuatCopy (bi, blend.indices); QuatCopy (bw, blend.weights); if ((bl = Hash_FindElement (blend_hash, &blend))) { *(uint32_t *) bi = (bl - blend_list); continue; } if (num_blends >= MAX_BLENDS) Sys_Error ("Too many blends. Tell taniwha to stop being lazy."); blend_list[num_blends] = blend; Hash_AddElement (blend_hash, &blend_list[num_blends]); *(uint32_t *) bi = num_blends; num_blends++; } Hash_DelTable (blend_hash); *size = num_blends; return realloc (blend_list, num_blends * sizeof (iqmblend_t)); }
qboolean R_LoadMD5(model_t *mod, void *buffer, int bufferSize, const char *modName) { int i, j, k; md5Model_t *md5; md5Bone_t *bone; md5Surface_t *surf; srfTriangle_t *tri; md5Vertex_t *v; md5Weight_t *weight; int version; shader_t *sh; char *buf_p = ( char * ) buffer; char *token; vec3_t boneOrigin; quat_t boneQuat; matrix_t boneMat; int numRemaining; growList_t sortedTriangles; growList_t vboTriangles; growList_t vboSurfaces; int numBoneReferences; int boneReferences[MAX_BONES]; // skip MD5Version indent string COM_ParseExt2(&buf_p, qfalse); // check version token = COM_ParseExt2(&buf_p, qfalse); version = atoi(token); if (version != MD5_VERSION) { Ren_Warning("R_LoadMD5: %s has wrong version (%i should be %i)\n", modName, version, MD5_VERSION); return qfalse; } mod->type = MOD_MD5; mod->dataSize += sizeof(md5Model_t); md5 = mod->md5 = ri.Hunk_Alloc(sizeof(md5Model_t), h_low); // skip commandline <arguments string> token = COM_ParseExt2(&buf_p, qtrue); token = COM_ParseExt2(&buf_p, qtrue); // Ren_Print("%s\n", token); // parse numJoints <number> token = COM_ParseExt2(&buf_p, qtrue); if (Q_stricmp(token, "numJoints")) { Ren_Warning("R_LoadMD5: expected 'numJoints' found '%s' in model '%s'\n", token, modName); return qfalse; } token = COM_ParseExt2(&buf_p, qfalse); md5->numBones = atoi(token); // parse numMeshes <number> token = COM_ParseExt2(&buf_p, qtrue); if (Q_stricmp(token, "numMeshes")) { Ren_Warning("R_LoadMD5: expected 'numMeshes' found '%s' in model '%s'\n", token, modName); return qfalse; } token = COM_ParseExt2(&buf_p, qfalse); md5->numSurfaces = atoi(token); //Ren_Print("R_LoadMD5: '%s' has %i surfaces\n", modName, md5->numSurfaces); if (md5->numBones < 1) { Ren_Warning("R_LoadMD5: '%s' has no bones\n", modName); return qfalse; } if (md5->numBones > MAX_BONES) { Ren_Warning("R_LoadMD5: '%s' has more than %i bones (%i)\n", modName, MAX_BONES, md5->numBones); return qfalse; } //Ren_Print("R_LoadMD5: '%s' has %i bones\n", modName, md5->numBones); // parse all the bones md5->bones = ri.Hunk_Alloc(sizeof(*bone) * md5->numBones, h_low); // parse joints { token = COM_ParseExt2(&buf_p, qtrue); if (Q_stricmp(token, "joints")) { Ren_Warning("R_LoadMD5: expected 'joints' found '%s' in model '%s'\n", token, modName); return qfalse; } token = COM_ParseExt2(&buf_p, qfalse); if (Q_stricmp(token, "{")) { Ren_Warning("R_LoadMD5: expected '{' found '%s' in model '%s'\n", token, modName); return qfalse; } for (i = 0, bone = md5->bones; i < md5->numBones; i++, bone++) { token = COM_ParseExt2(&buf_p, qtrue); Q_strncpyz(bone->name, token, sizeof(bone->name)); //Ren_Print("R_LoadMD5: '%s' has bone '%s'\n", modName, bone->name); token = COM_ParseExt2(&buf_p, qfalse); bone->parentIndex = atoi(token); //Ren_Print("R_LoadMD5: '%s' has bone '%s' with parent index %i\n", modName, bone->name, bone->parentIndex); if (bone->parentIndex >= md5->numBones) { Ren_Drop("R_LoadMD5: '%s' has bone '%s' with bad parent index %i while numBones is %i", modName, bone->name, bone->parentIndex, md5->numBones); } // skip ( token = COM_ParseExt2(&buf_p, qfalse); if (Q_stricmp(token, "(")) { Ren_Warning("R_LoadMD5: expected '(' found '%s' in model '%s'\n", token, modName); return qfalse; } for (j = 0; j < 3; j++) { token = COM_ParseExt2(&buf_p, qfalse); boneOrigin[j] = atof(token); } // skip ) token = COM_ParseExt2(&buf_p, qfalse); if (Q_stricmp(token, ")")) { Ren_Warning("R_LoadMD5: expected ')' found '%s' in model '%s'\n", token, modName); return qfalse; } // skip ( token = COM_ParseExt2(&buf_p, qfalse); if (Q_stricmp(token, "(")) { Ren_Warning("R_LoadMD5: expected '(' found '%s' in model '%s'\n", token, modName); return qfalse; } for (j = 0; j < 3; j++) { token = COM_ParseExt2(&buf_p, qfalse); boneQuat[j] = atof(token); } QuatCalcW(boneQuat); MatrixFromQuat(boneMat, boneQuat); VectorCopy(boneOrigin, bone->origin); QuatCopy(boneQuat, bone->rotation); MatrixSetupTransformFromQuat(bone->inverseTransform, boneQuat, boneOrigin); MatrixInverse(bone->inverseTransform); // skip ) token = COM_ParseExt2(&buf_p, qfalse); if (Q_stricmp(token, ")")) { Ren_Warning("R_LoadMD5: expected '(' found '%s' in model '%s'\n", token, modName); return qfalse; } } // parse } token = COM_ParseExt2(&buf_p, qtrue); if (Q_stricmp(token, "}")) { Ren_Warning("R_LoadMD5: expected '}' found '%s' in model '%s'\n", token, modName); return qfalse; } // parse all the surfaces if (md5->numSurfaces < 1) { Ren_Warning("R_LoadMD5: '%s' has no surfaces\n", modName); return qfalse; } //Ren_Print("R_LoadMD5: '%s' has %i surfaces\n", modName, md5->numSurfaces); md5->surfaces = ri.Hunk_Alloc(sizeof(*surf) * md5->numSurfaces, h_low); for (i = 0, surf = md5->surfaces; i < md5->numSurfaces; i++, surf++) { // parse mesh { token = COM_ParseExt2(&buf_p, qtrue); if (Q_stricmp(token, "mesh")) { Ren_Warning("R_LoadMD5: expected 'mesh' found '%s' in model '%s'\n", token, modName); return qfalse; } token = COM_ParseExt2(&buf_p, qfalse); if (Q_stricmp(token, "{")) { Ren_Warning("R_LoadMD5: expected '{' found '%s' in model '%s'\n", token, modName); return qfalse; } // change to surface identifier surf->surfaceType = SF_MD5; // give pointer to model for Tess_SurfaceMD5 surf->model = md5; // parse shader <name> token = COM_ParseExt2(&buf_p, qtrue); if (Q_stricmp(token, "shader")) { Ren_Warning("R_LoadMD5: expected 'shader' found '%s' in model '%s'\n", token, modName); return qfalse; } token = COM_ParseExt2(&buf_p, qfalse); Q_strncpyz(surf->shader, token, sizeof(surf->shader)); //Ren_Print("R_LoadMD5: '%s' uses shader '%s'\n", modName, surf->shader); // FIXME .md5mesh meshes don't have surface names // lowercase the surface name so skin compares are faster //Q_strlwr(surf->name); //Ren_Print("R_LoadMD5: '%s' has surface '%s'\n", modName, surf->name); // register the shaders sh = R_FindShader(surf->shader, SHADER_3D_DYNAMIC, qtrue); if (sh->defaultShader) { surf->shaderIndex = 0; } else { surf->shaderIndex = sh->index; } // parse numVerts <number> token = COM_ParseExt2(&buf_p, qtrue); if (Q_stricmp(token, "numVerts")) { Ren_Warning("R_LoadMD5: expected 'numVerts' found '%s' in model '%s'\n", token, modName); return qfalse; } token = COM_ParseExt2(&buf_p, qfalse); surf->numVerts = atoi(token); if (surf->numVerts > SHADER_MAX_VERTEXES) { Ren_Drop("R_LoadMD5: '%s' has more than %i verts on a surface (%i)", modName, SHADER_MAX_VERTEXES, surf->numVerts); } surf->verts = ri.Hunk_Alloc(sizeof(*v) * surf->numVerts, h_low); for (j = 0, v = surf->verts; j < surf->numVerts; j++, v++) { // skip vert <number> token = COM_ParseExt2(&buf_p, qtrue); if (Q_stricmp(token, "vert")) { Ren_Warning("R_LoadMD5: expected 'vert' found '%s' in model '%s'\n", token, modName); return qfalse; } COM_ParseExt2(&buf_p, qfalse); // skip ( token = COM_ParseExt2(&buf_p, qfalse); if (Q_stricmp(token, "(")) { Ren_Warning("R_LoadMD5: expected '(' found '%s' in model '%s'\n", token, modName); return qfalse; } for (k = 0; k < 2; k++) { token = COM_ParseExt2(&buf_p, qfalse); v->texCoords[k] = atof(token); } // skip ) token = COM_ParseExt2(&buf_p, qfalse); if (Q_stricmp(token, ")")) { Ren_Warning("R_LoadMD5: expected ')' found '%s' in model '%s'\n", token, modName); return qfalse; } token = COM_ParseExt2(&buf_p, qfalse); v->firstWeight = atoi(token); token = COM_ParseExt2(&buf_p, qfalse); v->numWeights = atoi(token); if (v->numWeights > MAX_WEIGHTS) { Ren_Drop("R_LoadMD5: vertex %i requires more than %i weights on surface (%i) in model '%s'", j, MAX_WEIGHTS, i, modName); } } // parse numTris <number> token = COM_ParseExt2(&buf_p, qtrue); if (Q_stricmp(token, "numTris")) { Ren_Warning("R_LoadMD5: expected 'numTris' found '%s' in model '%s'\n", token, modName); return qfalse; } token = COM_ParseExt2(&buf_p, qfalse); surf->numTriangles = atoi(token); if (surf->numTriangles > SHADER_MAX_TRIANGLES) { Ren_Drop("R_LoadMD5: '%s' has more than %i triangles on a surface (%i)", modName, SHADER_MAX_TRIANGLES, surf->numTriangles); } surf->triangles = ri.Hunk_Alloc(sizeof(*tri) * surf->numTriangles, h_low); for (j = 0, tri = surf->triangles; j < surf->numTriangles; j++, tri++) { // skip tri <number> token = COM_ParseExt2(&buf_p, qtrue); if (Q_stricmp(token, "tri")) { Ren_Warning("R_LoadMD5: expected 'tri' found '%s' in model '%s'\n", token, modName); return qfalse; } COM_ParseExt2(&buf_p, qfalse); for (k = 0; k < 3; k++) { token = COM_ParseExt2(&buf_p, qfalse); tri->indexes[k] = atoi(token); } } // parse numWeights <number> token = COM_ParseExt2(&buf_p, qtrue); if (Q_stricmp(token, "numWeights")) { Ren_Warning("R_LoadMD5: expected 'numWeights' found '%s' in model '%s'\n", token, modName); return qfalse; } token = COM_ParseExt2(&buf_p, qfalse); surf->numWeights = atoi(token); surf->weights = ri.Hunk_Alloc(sizeof(*weight) * surf->numWeights, h_low); for (j = 0, weight = surf->weights; j < surf->numWeights; j++, weight++) { // skip weight <number> token = COM_ParseExt2(&buf_p, qtrue); if (Q_stricmp(token, "weight")) { Ren_Warning("R_LoadMD5: expected 'weight' found '%s' in model '%s'\n", token, modName); return qfalse; } COM_ParseExt2(&buf_p, qfalse); token = COM_ParseExt2(&buf_p, qfalse); weight->boneIndex = atoi(token); token = COM_ParseExt2(&buf_p, qfalse); weight->boneWeight = atof(token); // skip ( token = COM_ParseExt2(&buf_p, qfalse); if (Q_stricmp(token, "(")) { Ren_Warning("R_LoadMD5: expected '(' found '%s' in model '%s'\n", token, modName); return qfalse; } for (k = 0; k < 3; k++) { token = COM_ParseExt2(&buf_p, qfalse); weight->offset[k] = atof(token); } // skip ) token = COM_ParseExt2(&buf_p, qfalse); if (Q_stricmp(token, ")")) { Ren_Warning("R_LoadMD5: expected ')' found '%s' in model '%s'\n", token, modName); return qfalse; } } // parse } token = COM_ParseExt2(&buf_p, qtrue); if (Q_stricmp(token, "}")) { Ren_Warning("R_LoadMD5: expected '}' found '%s' in model '%s'\n", token, modName); return qfalse; } // loop trough all vertices and set up the vertex weights for (j = 0, v = surf->verts; j < surf->numVerts; j++, v++) { v->weights = ri.Hunk_Alloc(sizeof(*v->weights) * v->numWeights, h_low); for (k = 0; k < v->numWeights; k++) { v->weights[k] = surf->weights + (v->firstWeight + k); } } } // loading is done now calculate the bounding box and tangent spaces ClearBounds(md5->bounds[0], md5->bounds[1]); for (i = 0, surf = md5->surfaces; i < md5->numSurfaces; i++, surf++) { for (j = 0, v = surf->verts; j < surf->numVerts; j++, v++) { vec3_t tmpVert; md5Weight_t *w; VectorClear(tmpVert); for (k = 0, w = v->weights[0]; k < v->numWeights; k++, w++) { vec3_t offsetVec; bone = &md5->bones[w->boneIndex]; QuatTransformVector(bone->rotation, w->offset, offsetVec); VectorAdd(bone->origin, offsetVec, offsetVec); VectorMA(tmpVert, w->boneWeight, offsetVec, tmpVert); } VectorCopy(tmpVert, v->position); AddPointToBounds(tmpVert, md5->bounds[0], md5->bounds[1]); } // calc tangent spaces #if 1 { const float *v0, *v1, *v2; const float *t0, *t1, *t2; vec3_t tangent; vec3_t binormal; vec3_t normal; for (j = 0, v = surf->verts; j < surf->numVerts; j++, v++) { VectorClear(v->tangent); VectorClear(v->binormal); VectorClear(v->normal); } for (j = 0, tri = surf->triangles; j < surf->numTriangles; j++, tri++) { v0 = surf->verts[tri->indexes[0]].position; v1 = surf->verts[tri->indexes[1]].position; v2 = surf->verts[tri->indexes[2]].position; t0 = surf->verts[tri->indexes[0]].texCoords; t1 = surf->verts[tri->indexes[1]].texCoords; t2 = surf->verts[tri->indexes[2]].texCoords; #if 1 R_CalcTangentSpace(tangent, binormal, normal, v0, v1, v2, t0, t1, t2); #else R_CalcNormalForTriangle(normal, v0, v1, v2); R_CalcTangentsForTriangle(tangent, binormal, v0, v1, v2, t0, t1, t2); #endif for (k = 0; k < 3; k++) { float *v; v = surf->verts[tri->indexes[k]].tangent; VectorAdd(v, tangent, v); v = surf->verts[tri->indexes[k]].binormal; VectorAdd(v, binormal, v); v = surf->verts[tri->indexes[k]].normal; VectorAdd(v, normal, v); } } for (j = 0, v = surf->verts; j < surf->numVerts; j++, v++) { VectorNormalize(v->tangent); VectorNormalize(v->binormal); VectorNormalize(v->normal); } } #else { int k; float bb, s, t; vec3_t bary; vec3_t faceNormal; md5Vertex_t *dv[3]; for (j = 0, tri = surf->triangles; j < surf->numTriangles; j++, tri++) { dv[0] = &surf->verts[tri->indexes[0]]; dv[1] = &surf->verts[tri->indexes[1]]; dv[2] = &surf->verts[tri->indexes[2]]; R_CalcNormalForTriangle(faceNormal, dv[0]->position, dv[1]->position, dv[2]->position); // calculate barycentric basis for the triangle bb = (dv[1]->texCoords[0] - dv[0]->texCoords[0]) * (dv[2]->texCoords[1] - dv[0]->texCoords[1]) - (dv[2]->texCoords[0] - dv[0]->texCoords[0]) * (dv[1]->texCoords[1] - dv[0]->texCoords[1]); if (fabs(bb) < 0.00000001f) { continue; } // do each vertex for (k = 0; k < 3; k++) { // calculate s tangent vector s = dv[k]->texCoords[0] + 10.0f; t = dv[k]->texCoords[1]; bary[0] = ((dv[1]->texCoords[0] - s) * (dv[2]->texCoords[1] - t) - (dv[2]->texCoords[0] - s) * (dv[1]->texCoords[1] - t)) / bb; bary[1] = ((dv[2]->texCoords[0] - s) * (dv[0]->texCoords[1] - t) - (dv[0]->texCoords[0] - s) * (dv[2]->texCoords[1] - t)) / bb; bary[2] = ((dv[0]->texCoords[0] - s) * (dv[1]->texCoords[1] - t) - (dv[1]->texCoords[0] - s) * (dv[0]->texCoords[1] - t)) / bb; dv[k]->tangent[0] = bary[0] * dv[0]->position[0] + bary[1] * dv[1]->position[0] + bary[2] * dv[2]->position[0]; dv[k]->tangent[1] = bary[0] * dv[0]->position[1] + bary[1] * dv[1]->position[1] + bary[2] * dv[2]->position[1]; dv[k]->tangent[2] = bary[0] * dv[0]->position[2] + bary[1] * dv[1]->position[2] + bary[2] * dv[2]->position[2]; VectorSubtract(dv[k]->tangent, dv[k]->position, dv[k]->tangent); VectorNormalize(dv[k]->tangent); // calculate t tangent vector (binormal) s = dv[k]->texCoords[0]; t = dv[k]->texCoords[1] + 10.0f; bary[0] = ((dv[1]->texCoords[0] - s) * (dv[2]->texCoords[1] - t) - (dv[2]->texCoords[0] - s) * (dv[1]->texCoords[1] - t)) / bb; bary[1] = ((dv[2]->texCoords[0] - s) * (dv[0]->texCoords[1] - t) - (dv[0]->texCoords[0] - s) * (dv[2]->texCoords[1] - t)) / bb; bary[2] = ((dv[0]->texCoords[0] - s) * (dv[1]->texCoords[1] - t) - (dv[1]->texCoords[0] - s) * (dv[0]->texCoords[1] - t)) / bb; dv[k]->binormal[0] = bary[0] * dv[0]->position[0] + bary[1] * dv[1]->position[0] + bary[2] * dv[2]->position[0]; dv[k]->binormal[1] = bary[0] * dv[0]->position[1] + bary[1] * dv[1]->position[1] + bary[2] * dv[2]->position[1]; dv[k]->binormal[2] = bary[0] * dv[0]->position[2] + bary[1] * dv[1]->position[2] + bary[2] * dv[2]->position[2]; VectorSubtract(dv[k]->binormal, dv[k]->position, dv[k]->binormal); VectorNormalize(dv[k]->binormal); // calculate the normal as cross product N=TxB #if 0 CrossProduct(dv[k]->tangent, dv[k]->binormal, dv[k]->normal); VectorNormalize(dv[k]->normal); // Gram-Schmidt orthogonalization process for B // compute the cross product B=NxT to obtain // an orthogonal basis CrossProduct(dv[k]->normal, dv[k]->tangent, dv[k]->binormal); if (DotProduct(dv[k]->normal, faceNormal) < 0) { VectorInverse(dv[k]->normal); //VectorInverse(dv[k]->tangent); //VectorInverse(dv[k]->binormal); } #else VectorAdd(dv[k]->normal, faceNormal, dv[k]->normal); #endif } } #if 1 for (j = 0, v = surf->verts; j < surf->numVerts; j++, v++) { //VectorNormalize(v->tangent); //VectorNormalize(v->binormal); VectorNormalize(v->normal); } #endif } #endif #if 0 // do another extra smoothing for normals to avoid flat shading for (j = 0; j < surf->numVerts; j++) { for (k = 0; k < surf->numVerts; k++) { if (j == k) { continue; } if (VectorCompare(surf->verts[j].position, surf->verts[k].position)) { VectorAdd(surf->verts[j].normal, surf->verts[k].normal, surf->verts[j].normal); } } VectorNormalize(surf->verts[j].normal); } #endif } // split the surfaces into VBO surfaces by the maximum number of GPU vertex skinning bones Com_InitGrowList(&vboSurfaces, 10); for (i = 0, surf = md5->surfaces; i < md5->numSurfaces; i++, surf++) { // sort triangles Com_InitGrowList(&sortedTriangles, 1000); for (j = 0, tri = surf->triangles; j < surf->numTriangles; j++, tri++) { skelTriangle_t *sortTri = Com_Allocate(sizeof(*sortTri)); for (k = 0; k < 3; k++) { sortTri->indexes[k] = tri->indexes[k]; sortTri->vertexes[k] = &surf->verts[tri->indexes[k]]; } sortTri->referenced = qfalse; Com_AddToGrowList(&sortedTriangles, sortTri); } //qsort(sortedTriangles.elements, sortedTriangles.currentElements, sizeof(void *), CompareTrianglesByBoneReferences); #if 0 for (j = 0; j < sortedTriangles.currentElements; j++) { int b[MAX_WEIGHTS * 3]; skelTriangle_t *sortTri = Com_GrowListElement(&sortedTriangles, j); for (k = 0; k < 3; k++) { v = sortTri->vertexes[k]; for (l = 0; l < MAX_WEIGHTS; l++) { b[k * 3 + l] = (l < v->numWeights) ? v->weights[l]->boneIndex : 9999; } qsort(b, MAX_WEIGHTS * 3, sizeof(int), CompareBoneIndices); //Ren_Print("bone indices: %i %i %i %i\n", b[k * 3 + 0], b[k * 3 + 1], b[k * 3 + 2], b[k * 3 + 3]); } } #endif numRemaining = sortedTriangles.currentElements; while (numRemaining) { numBoneReferences = 0; Com_Memset(boneReferences, 0, sizeof(boneReferences)); Com_InitGrowList(&vboTriangles, 1000); for (j = 0; j < sortedTriangles.currentElements; j++) { skelTriangle_t *sortTri = Com_GrowListElement(&sortedTriangles, j); if (sortTri->referenced) { continue; } if (AddTriangleToVBOTriangleList(&vboTriangles, sortTri, &numBoneReferences, boneReferences)) { sortTri->referenced = qtrue; } } if (!vboTriangles.currentElements) { Ren_Warning("R_LoadMD5: could not add triangles to a remaining VBO surfaces for model '%s'\n", modName); Com_DestroyGrowList(&vboTriangles); break; } AddSurfaceToVBOSurfacesList(&vboSurfaces, &vboTriangles, md5, surf, i, numBoneReferences, boneReferences); numRemaining -= vboTriangles.currentElements; Com_DestroyGrowList(&vboTriangles); } for (j = 0; j < sortedTriangles.currentElements; j++) { skelTriangle_t *sortTri = Com_GrowListElement(&sortedTriangles, j); Com_Dealloc(sortTri); } Com_DestroyGrowList(&sortedTriangles); } // move VBO surfaces list to hunk md5->numVBOSurfaces = vboSurfaces.currentElements; md5->vboSurfaces = ri.Hunk_Alloc(md5->numVBOSurfaces * sizeof(*md5->vboSurfaces), h_low); for (i = 0; i < md5->numVBOSurfaces; i++) { md5->vboSurfaces[i] = ( srfVBOMD5Mesh_t * ) Com_GrowListElement(&vboSurfaces, i); } Com_DestroyGrowList(&vboSurfaces); return qtrue; }
/* ================= R_LoadMD5 ================= */ qboolean R_LoadMD5( model_t *mod, void *buffer, int bufferSize, const char *modName ) { int i, j, k; md5Model_t *md5; md5Bone_t *bone; md5Surface_t *surf; md5Triangle_t *tri; md5Vertex_t *v; md5Weight_t *weight; int version; shader_t *sh; char *buf_p; char *token; vec3_t boneOrigin; quat_t boneQuat; matrix_t boneMat; buf_p = ( char * ) buffer; // skip MD5Version indent string COM_ParseExt2( &buf_p, qfalse ); // check version token = COM_ParseExt2( &buf_p, qfalse ); version = atoi( token ); if ( version != MD5_VERSION ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: %s has wrong version (%i should be %i)\n", modName, version, MD5_VERSION ); return qfalse; } mod->type = MOD_MD5; mod->dataSize += sizeof( md5Model_t ); md5 = mod->model.md5 = ri.Hunk_Alloc( sizeof( md5Model_t ), h_low ); // skip commandline <arguments string> token = COM_ParseExt2( &buf_p, qtrue ); token = COM_ParseExt2( &buf_p, qtrue ); // ri.Printf(PRINT_ALL, "%s\n", token); // parse numJoints <number> token = COM_ParseExt2( &buf_p, qtrue ); if ( Q_stricmp( token, "numJoints" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected 'numJoints' found '%s' in model '%s'\n", token, modName ); return qfalse; } token = COM_ParseExt2( &buf_p, qfalse ); md5->numBones = atoi( token ); // parse numMeshes <number> token = COM_ParseExt2( &buf_p, qtrue ); if ( Q_stricmp( token, "numMeshes" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected 'numMeshes' found '%s' in model '%s'\n", token, modName ); return qfalse; } token = COM_ParseExt2( &buf_p, qfalse ); md5->numSurfaces = atoi( token ); //ri.Printf(PRINT_ALL, "R_LoadMD5: '%s' has %i surfaces\n", modName, md5->numSurfaces); if ( md5->numBones < 1 ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: '%s' has no bones\n", modName ); return qfalse; } if ( md5->numBones > MAX_BONES ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: '%s' has more than %i bones (%i)\n", modName, MAX_BONES, md5->numBones ); return qfalse; } //ri.Printf(PRINT_ALL, "R_LoadMD5: '%s' has %i bones\n", modName, md5->numBones); // parse all the bones md5->bones = ri.Hunk_Alloc( sizeof( *bone ) * md5->numBones, h_low ); // parse joints { token = COM_ParseExt2( &buf_p, qtrue ); if ( Q_stricmp( token, "joints" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected 'joints' found '%s' in model '%s'\n", token, modName ); return qfalse; } token = COM_ParseExt2( &buf_p, qfalse ); if ( Q_stricmp( token, "{" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected '{' found '%s' in model '%s'\n", token, modName ); return qfalse; } for ( i = 0, bone = md5->bones; i < md5->numBones; i++, bone++ ) { token = COM_ParseExt2( &buf_p, qtrue ); Q_strncpyz( bone->name, token, sizeof( bone->name ) ); //ri.Printf(PRINT_ALL, "R_LoadMD5: '%s' has bone '%s'\n", modName, bone->name); token = COM_ParseExt2( &buf_p, qfalse ); bone->parentIndex = atoi( token ); //ri.Printf(PRINT_ALL, "R_LoadMD5: '%s' has bone '%s' with parent index %i\n", modName, bone->name, bone->parentIndex); if ( bone->parentIndex >= md5->numBones ) { ri.Error( ERR_DROP, "R_LoadMD5: '%s' has bone '%s' with bad parent index %i while numBones is %i\n", modName, bone->name, bone->parentIndex, md5->numBones ); } // skip ( token = COM_ParseExt2( &buf_p, qfalse ); if ( Q_stricmp( token, "(" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected '(' found '%s' in model '%s'\n", token, modName ); return qfalse; } for ( j = 0; j < 3; j++ ) { token = COM_ParseExt2( &buf_p, qfalse ); boneOrigin[ j ] = atof( token ); } // skip ) token = COM_ParseExt2( &buf_p, qfalse ); if ( Q_stricmp( token, ")" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected ')' found '%s' in model '%s'\n", token, modName ); return qfalse; } // skip ( token = COM_ParseExt2( &buf_p, qfalse ); if ( Q_stricmp( token, "(" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected '(' found '%s' in model '%s'\n", token, modName ); return qfalse; } for ( j = 0; j < 3; j++ ) { token = COM_ParseExt2( &buf_p, qfalse ); boneQuat[ j ] = atof( token ); } QuatCalcW( boneQuat ); MatrixFromQuat( boneMat, boneQuat ); VectorCopy( boneOrigin, bone->origin ); QuatCopy( boneQuat, bone->rotation ); MatrixSetupTransformFromQuat( bone->inverseTransform, boneQuat, boneOrigin ); MatrixInverse( bone->inverseTransform ); // skip ) token = COM_ParseExt2( &buf_p, qfalse ); if ( Q_stricmp( token, ")" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected '(' found '%s' in model '%s'\n", token, modName ); return qfalse; } } // parse } token = COM_ParseExt2( &buf_p, qtrue ); if ( Q_stricmp( token, "}" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected '}' found '%s' in model '%s'\n", token, modName ); return qfalse; } // parse all the surfaces if ( md5->numSurfaces < 1 ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: '%s' has no surfaces\n", modName ); return qfalse; } //ri.Printf(PRINT_ALL, "R_LoadMD5: '%s' has %i surfaces\n", modName, md5->numSurfaces); md5->surfaces = ri.Hunk_Alloc( sizeof( *surf ) * md5->numSurfaces, h_low ); for ( i = 0, surf = md5->surfaces; i < md5->numSurfaces; i++, surf++ ) { // parse mesh { token = COM_ParseExt2( &buf_p, qtrue ); if ( Q_stricmp( token, "mesh" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected 'mesh' found '%s' in model '%s'\n", token, modName ); return qfalse; } token = COM_ParseExt2( &buf_p, qfalse ); if ( Q_stricmp( token, "{" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected '{' found '%s' in model '%s'\n", token, modName ); return qfalse; } // change to surface identifier surf->surfaceType = SF_MD5; // give pointer to model for Tess_SurfaceMD5 surf->model = md5; // parse shader <name> token = COM_ParseExt2( &buf_p, qtrue ); if ( Q_stricmp( token, "shader" ) ) { Q_strncpyz( surf->shader, "<default>", sizeof( surf->shader ) ); surf->shaderIndex = 0; } else { token = COM_ParseExt2( &buf_p, qfalse ); Q_strncpyz( surf->shader, token, sizeof( surf->shader ) ); //ri.Printf(PRINT_ALL, "R_LoadMD5: '%s' uses shader '%s'\n", modName, surf->shader); // FIXME .md5mesh meshes don't have surface names // lowercase the surface name so skin compares are faster //Q_strlwr(surf->name); //ri.Printf(PRINT_ALL, "R_LoadMD5: '%s' has surface '%s'\n", modName, surf->name); // register the shaders sh = R_FindShader( surf->shader, LIGHTMAP_NONE, qtrue ); if ( sh->defaultShader ) { surf->shaderIndex = 0; } else { surf->shaderIndex = sh->index; } token = COM_ParseExt2( &buf_p, qtrue ); } // parse numVerts <number> if ( Q_stricmp( token, "numVerts" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected 'numVerts' found '%s' in model '%s'\n", token, modName ); return qfalse; } token = COM_ParseExt2( &buf_p, qfalse ); surf->numVerts = atoi( token ); if ( surf->numVerts > SHADER_MAX_VERTEXES ) { ri.Error( ERR_DROP, "R_LoadMD5: '%s' has more than %i verts on a surface (%i)", modName, SHADER_MAX_VERTEXES, surf->numVerts ); } surf->verts = ri.Hunk_Alloc( sizeof( *v ) * surf->numVerts, h_low ); for ( j = 0, v = surf->verts; j < surf->numVerts; j++, v++ ) { // skip vert <number> token = COM_ParseExt2( &buf_p, qtrue ); if ( Q_stricmp( token, "vert" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected 'vert' found '%s' in model '%s'\n", token, modName ); return qfalse; } COM_ParseExt2( &buf_p, qfalse ); // skip ( token = COM_ParseExt2( &buf_p, qfalse ); if ( Q_stricmp( token, "(" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected '(' found '%s' in model '%s'\n", token, modName ); return qfalse; } for ( k = 0; k < 2; k++ ) { token = COM_ParseExt2( &buf_p, qfalse ); v->texCoords[ k ] = atof( token ); } // skip ) token = COM_ParseExt2( &buf_p, qfalse ); if ( Q_stricmp( token, ")" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected ')' found '%s' in model '%s'\n", token, modName ); return qfalse; } token = COM_ParseExt2( &buf_p, qfalse ); v->firstWeight = atoi( token ); token = COM_ParseExt2( &buf_p, qfalse ); v->numWeights = atoi( token ); if ( v->numWeights > MAX_WEIGHTS ) { ri.Error( ERR_DROP, "R_LoadMD5: vertex %i requires more than %i weights on surface (%i) in model '%s'", j, MAX_WEIGHTS, i, modName ); } } // parse numTris <number> token = COM_ParseExt2( &buf_p, qtrue ); if ( Q_stricmp( token, "numTris" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected 'numTris' found '%s' in model '%s'\n", token, modName ); return qfalse; } token = COM_ParseExt2( &buf_p, qfalse ); surf->numTriangles = atoi( token ); if ( surf->numTriangles > SHADER_MAX_TRIANGLES ) { ri.Error( ERR_DROP, "R_LoadMD5: '%s' has more than %i triangles on a surface (%i)", modName, SHADER_MAX_TRIANGLES, surf->numTriangles ); } surf->triangles = ri.Hunk_Alloc( sizeof( *tri ) * surf->numTriangles, h_low ); for ( j = 0, tri = surf->triangles; j < surf->numTriangles; j++, tri++ ) { // skip tri <number> token = COM_ParseExt2( &buf_p, qtrue ); if ( Q_stricmp( token, "tri" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected 'tri' found '%s' in model '%s'\n", token, modName ); return qfalse; } COM_ParseExt2( &buf_p, qfalse ); for ( k = 0; k < 3; k++ ) { token = COM_ParseExt2( &buf_p, qfalse ); tri->indexes[ k ] = atoi( token ); } } // parse numWeights <number> token = COM_ParseExt2( &buf_p, qtrue ); if ( Q_stricmp( token, "numWeights" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected 'numWeights' found '%s' in model '%s'\n", token, modName ); return qfalse; } token = COM_ParseExt2( &buf_p, qfalse ); surf->numWeights = atoi( token ); surf->weights = ri.Hunk_Alloc( sizeof( *weight ) * surf->numWeights, h_low ); for ( j = 0, weight = surf->weights; j < surf->numWeights; j++, weight++ ) { // skip weight <number> token = COM_ParseExt2( &buf_p, qtrue ); if ( Q_stricmp( token, "weight" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected 'weight' found '%s' in model '%s'\n", token, modName ); return qfalse; } COM_ParseExt2( &buf_p, qfalse ); token = COM_ParseExt2( &buf_p, qfalse ); weight->boneIndex = atoi( token ); token = COM_ParseExt2( &buf_p, qfalse ); weight->boneWeight = atof( token ); // skip ( token = COM_ParseExt2( &buf_p, qfalse ); if ( Q_stricmp( token, "(" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected '(' found '%s' in model '%s'\n", token, modName ); return qfalse; } for ( k = 0; k < 3; k++ ) { token = COM_ParseExt2( &buf_p, qfalse ); weight->offset[ k ] = atof( token ); } // skip ) token = COM_ParseExt2( &buf_p, qfalse ); if ( Q_stricmp( token, ")" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected ')' found '%s' in model '%s'\n", token, modName ); return qfalse; } } // parse } token = COM_ParseExt2( &buf_p, qtrue ); if ( Q_stricmp( token, "}" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected '}' found '%s' in model '%s'\n", token, modName ); return qfalse; } // loop trough all vertices and set up the vertex weights for ( j = 0, v = surf->verts; j < surf->numVerts; j++, v++ ) { v->weights = ri.Hunk_Alloc( sizeof( *v->weights ) * v->numWeights, h_low ); for ( k = 0; k < v->numWeights; k++ ) { v->weights[ k ] = surf->weights + ( v->firstWeight + k ); } } } // loading is done now calculate the bounding box and tangent spaces ClearBounds( md5->bounds[ 0 ], md5->bounds[ 1 ] ); for ( i = 0, surf = md5->surfaces; i < md5->numSurfaces; i++, surf++ ) { for ( j = 0, v = surf->verts; j < surf->numVerts; j++, v++ ) { vec3_t tmpVert; md5Weight_t *w; VectorClear( tmpVert ); for ( k = 0, w = v->weights[ 0 ]; k < v->numWeights; k++, w++ ) { vec3_t offsetVec; bone = &md5->bones[ w->boneIndex ]; QuatTransformVector( bone->rotation, w->offset, offsetVec ); VectorAdd( bone->origin, offsetVec, offsetVec ); VectorMA( tmpVert, w->boneWeight, offsetVec, tmpVert ); } VectorCopy( tmpVert, v->position ); AddPointToBounds( tmpVert, md5->bounds[ 0 ], md5->bounds[ 1 ] ); } // calc normals { const float *v0, *v1, *v2; const float *t0, *t1, *t2; vec3_t normal; for ( j = 0, v = surf->verts; j < surf->numVerts; j++, v++ ) { VectorClear( v->tangent ); VectorClear( v->binormal ); VectorClear( v->normal ); } for ( j = 0, tri = surf->triangles; j < surf->numTriangles; j++, tri++ ) { v0 = surf->verts[ tri->indexes[ 0 ] ].position; v1 = surf->verts[ tri->indexes[ 1 ] ].position; v2 = surf->verts[ tri->indexes[ 2 ] ].position; t0 = surf->verts[ tri->indexes[ 0 ] ].texCoords; t1 = surf->verts[ tri->indexes[ 1 ] ].texCoords; t2 = surf->verts[ tri->indexes[ 2 ] ].texCoords; R_CalcNormalForTriangle( normal, v0, v1, v2 ); for ( k = 0; k < 3; k++ ) { float *v; v = surf->verts[ tri->indexes[ k ] ].normal; VectorAdd( v, normal, v ); } } for ( j = 0, v = surf->verts; j < surf->numVerts; j++, v++ ) { VectorNormalize( v->normal ); } } #if 0 // do another extra smoothing for normals to avoid flat shading for ( j = 0; j < surf->numVerts; j++ ) { for ( k = 0; k < surf->numVerts; k++ ) { if ( j == k ) { continue; } if ( VectorCompare( surf->verts[ j ].position, surf->verts[ k ].position ) ) { VectorAdd( surf->verts[ j ].normal, surf->verts[ k ].normal, surf->verts[ j ].normal ); } } VectorNormalize( surf->verts[ j ].normal ); } #endif } return qtrue; }