geWorld_ModelCollision Fix for Genesis3D 1.0 and 1.1 by Michael R. Brumm me@michaelbrumm.com This fixes geWorld_ModelCollision and makes it work properly. To make geWorld_ModelCollision work, add/change the following code: (notice that I comment out code I change) 1. Change this in Trace.c: [CODE] //===================================================================================== // Trace_ModelCollisionBBox //===================================================================================== static geBoolean Trace_ModelCollisionBBox(geWorld *World, geWorld_Model *Model, const geXForm3d *DXForm, const geVec3d *Mins, const geVec3d *Maxs, const geVec3d *In, geVec3d *ImpactPoint) { //MRB BEGIN // geVec3d NewFront, NewBack, Original; geVec3d NewFront, NewBack; //MRB END assert(World != NULL); assert(Model != NULL); BSPData = &World->CurrentBSP->BSPData; MiscNodes = BSPData->GFXNodes; MiscPlanes = BSPData->GFXPlanes; MiscLeafs = BSPData->GFXLeafs; MiscSides = BSPData->GFXLeafSides; assert(MiscNodes != NULL); assert(MiscPlanes != NULL); assert(MiscLeafs != NULL); assert(MiscSides != NULL); //MRB BEGIN // Original = *In; // Save original //MRB END GMins1 = *Mins; GMaxs1 = *Maxs; // Put point about models origin geVec3d_Subtract(In, &Model->Pivot, &GFront); GBack = GFront; // InverseTransform the points about models center of rotation geXForm3d_TransposeTransform(&Model->XForm, &GFront, &NewFront); // The back gets applied by the dest XForm geXForm3d_TransposeTransform(DXForm, &GBack, &NewBack); // push back into world geVec3d_Add(&NewFront, &Model->Pivot, &GFront); geVec3d_Add(&NewBack , &Model->Pivot, &GBack); // Make out box out of this move so we only check the leafs it intersected with... Trace_GetMoveBox(Mins, Maxs, &GFront, &GBack, &GMins2, &GMaxs2); BestDist = 9999.0f; LeafHit = FALSE; FindClosestLeafIntersection_r(BSPData->GFXModels[Model->GFXModelNum].RootNode[0]); if (LeafHit) { // Rotate the impact plane geXForm3d_Rotate(DXForm, &GlobalPlane.Normal, &GlobalPlane.Normal); // Rotate the impact point //MRB BEGIN // geVec3d_Subtract(&GlobalI, &Model->Pivot, &NewFront); // geXForm3d_Transform(DXForm, &NewFront, &GlobalI); // geVec3d_Add(&GlobalI, &Model->Pivot, &NewFront); // GlobalI = NewFront; geVec3d_Subtract(&GlobalI, &Model->Pivot, &GlobalI); geXForm3d_Transform(DXForm, &GlobalI, &NewFront); geVec3d_Add(&NewFront, &Model->Pivot, &GlobalI); //MRB END // Find the new plane distance based on the new impact point with the new plane GlobalPlane.Dist = geVec3d_DotProduct(&GlobalPlane.Normal, &GlobalI); geVec3d_MA(&GlobalI, ON_EPSILON, &GlobalPlane.Normal, &GlobalI); *ImpactPoint = GlobalI; return GE_TRUE; } return GE_FALSE; } //===================================================================================== // Trace_ModelCollision //===================================================================================== geBoolean Trace_ModelCollision(geWorld *World, geWorld_Model *Model, const geXForm3d *DXForm, GE_Collision *Collision, geVec3d *ImpactPoint) { geExtBox ExtBox; geVec3d Pos; #ifdef MESHES Mesh_RenderQ * CollidableMesh; Mesh_CollidableMeshIterator Iter; #endif memset(Collision, 0, sizeof(GE_Collision)); // Fixed bug that mike pointed out. I was using 0, instead of 0xffffffff #ifdef MESHES CollidableMesh = Mesh_FirstCollidableMesh(World, &Iter, 0xffffffff); while (CollidableMesh) { Mesh_MeshGetBox(World, CollidableMesh->MeshDef, &Mins, &Maxs); Mesh_MeshGetPosition(CollidableMesh, &Pos); if (Trace_ModelCollisionBBox(World, Model, DXForm, &Mins, &Maxs, &Pos, ImpactPoint)) { Collision->Mesh = (geMesh *)CollidableMesh; return GE_TRUE; } CollidableMesh = Mesh_NextCollidableMesh(&Iter, 0xffffffff); } #endif { int i,Count; World_Actor *WA; //MRB BEGIN geActor *BestActor; geFloat BestActorDist; geVec3d PossibleImpactPoint; BestActor = NULL; BestActorDist = 9999.0f; //MRB END Count = World->ActorCount; WA = &(World->ActorArray[0]); for (i=0; i < Count; i++, WA++) { // if it's active (ignore userflags?) if ( (WA->Flags & GE_ACTOR_COLLIDE) ) { //MRB BEGIN // if (geActor_GetExtBox(WA->Actor,&ExtBox)!=GE_FALSE) // { geActor_GetNonWorldExtBox(WA->Actor,&ExtBox); geActor_GetPosition(WA->Actor, &Pos); // if (Trace_ModelCollisionBBox(World, Model, DXForm, &(ExtBox.Min), &(ExtBox.Max), &Pos, ImpactPoint)) if (Trace_ModelCollisionBBox(World, Model, DXForm, &(ExtBox.Min), &(ExtBox.Max), &Pos, &PossibleImpactPoint)) { // Collision->Actor = WA->Actor; if (GlobalPlane.Dist < BestActorDist) { BestActorDist = GlobalPlane.Dist; BestActor = WA->Actor; (*ImpactPoint) = PossibleImpactPoint; Collision->Plane.Normal = GlobalPlane.Normal; Collision->Plane.Dist = GlobalPlane.Dist; Collision->Ratio = geVec3d_DistanceBetween(&DXForm->Translation, &Model->XForm.Translation); } // return GE_TRUE; } // } //MRB END } } //MRB BEGIN if (BestActor) { Collision->Actor = BestActor; return GE_TRUE; } //MRB END } return GE_FALSE; } [/CODE] 2. Add this to actor.h: [CODE] //MRB BEGIN // Unlike geActor_GetExtBox, this gets the bounding box in non-world coordinates. // Whatever you put in with geActor_SetExtBox, you get out with this function. GENESISAPI void GENESISCC geActor_GetNonWorldExtBox(const geActor *A, geExtBox *ExtBox); GENESISAPI void GENESISCC geActor_GetPosition(const geActor *A, geVec3d *Pos); //MRB END [/CODE] 3. Add this to actor.c: [CODE] //MRB BEGIN GENESISAPI void GENESISCC geActor_GetNonWorldExtBox(const geActor *A, geExtBox *ExtBox) { assert( geActor_IsValid(A) != GE_FALSE); assert( ExtBox != NULL ); geVec3d_Copy(&(A->BoundingBoxMinCorner), &(ExtBox->Min)); geVec3d_Copy(&(A->BoundingBoxMaxCorner), &(ExtBox->Max)); } GENESISAPI void GENESISCC geActor_GetPosition(const geActor *A, geVec3d *Pos) { geXForm3d Transform; assert( geActor_IsValid(A) != GE_FALSE); assert( Pos != NULL ); gePose_GetJointTransform( A->Pose, A->BoundingBoxCenterBoneIndex, &Transform); assert ( geXForm3d_IsOrthonormal(&Transform) != GE_FALSE ); geVec3d_Copy(&(Transform.Translation), Pos); } //MRB END [/CODE]