double GeodesicDist::ComputeAngleBetween2Lines(const DVector3 &v3PtCommon, const DVector3 &v3Pt1, const DVector3 &v3Pt2 ) { DVector3 P ; DVector3 Q ; DVector3Minus( v3Pt1, v3PtCommon, P ) ; DVector3Minus( v3Pt2, v3PtCommon, Q ) ; return acos( DVector3Dot( P, Q ) / ( P.Length() * Q.Length() ) ) ; }
void GeodesicDist::ParameterizePt3ToPt2(const DVector3 &v3Origin, const DVector3 &v3OnePositivePt, const DVector3 &v3Pt, DVector2 &ptRes ) { DVector3 P; DVector3Minus( v3Pt, v3Origin, P ) ; DVector3 Q; DVector3Minus( v3OnePositivePt, v3Origin, Q ) ; double lengthQ = Q.Length() ; DVector3 PCrossQ ; DVector3Cross( P, Q, PCrossQ ) ; ptRes.x = DVector3Dot( P, Q ) / lengthQ ; ptRes.y = PCrossQ.Length() / lengthQ ; }
void GeodesicDist::ComputePtOnLineWithDistance(const DVector3 &v3Pt1, const DVector3 &v3Pt2, const double &dDistanceAwayFromPt1, DVector3 &v3Result ) { DVector3 tmp ; DVector3Minus( v3Pt2, v3Pt1, tmp ) ; DVector3ScalarMul( tmp, 1/tmp.Length()*dDistanceAwayFromPt1 ) ; DVector3Add( tmp, v3Pt1, v3Result ) ; }
static void P_SetSlopesFromVertexHeights(FMapThing *firstmt, FMapThing *lastmt, const int *oldvertextable) { TMap<int, double> vt_heights[2]; FMapThing *mt; bool vt_found = false; for (mt = firstmt; mt < lastmt; ++mt) { if (mt->info != NULL && mt->info->Type == NULL) { if (mt->info->Special == SMT_VertexFloorZ || mt->info->Special == SMT_VertexCeilingZ) { for (int i = 0; i < numvertexes; i++) { if (vertexes[i].fX() == mt->pos.X && vertexes[i].fY() == mt->pos.Y) { if (mt->info->Special == SMT_VertexFloorZ) { vt_heights[0][i] = mt->pos.Z; } else { vt_heights[1][i] = mt->pos.Z; } vt_found = true; } } mt->EdNum = 0; } } } for(int i = 0; i < numvertexdatas; i++) { int ii = oldvertextable == NULL ? i : oldvertextable[i]; if (vertexdatas[i].flags & VERTEXFLAG_ZCeilingEnabled) { vt_heights[1][ii] = vertexdatas[i].zCeiling; vt_found = true; } if (vertexdatas[i].flags & VERTEXFLAG_ZFloorEnabled) { vt_heights[0][ii] = vertexdatas[i].zFloor; vt_found = true; } } // If vertexdata_t is ever extended for non-slope usage, this will obviously have to be deferred or removed. delete[] vertexdatas; vertexdatas = NULL; numvertexdatas = 0; if (vt_found) { for (int i = 0; i < numsectors; i++) { sector_t *sec = §ors[i]; if (sec->linecount != 3) continue; // only works with triangular sectors DVector3 vt1, vt2, vt3, cross; DVector3 vec1, vec2; int vi1, vi2, vi3; vi1 = int(sec->lines[0]->v1 - vertexes); vi2 = int(sec->lines[0]->v2 - vertexes); vi3 = (sec->lines[1]->v1 == sec->lines[0]->v1 || sec->lines[1]->v1 == sec->lines[0]->v2)? int(sec->lines[1]->v2 - vertexes) : int(sec->lines[1]->v1 - vertexes); vt1 = DVector3(vertexes[vi1].fPos(), 0); vt2 = DVector3(vertexes[vi2].fPos(), 0); vt3 = DVector3(vertexes[vi3].fPos(), 0); for(int j=0; j<2; j++) { double *h1 = vt_heights[j].CheckKey(vi1); double *h2 = vt_heights[j].CheckKey(vi2); double *h3 = vt_heights[j].CheckKey(vi3); if (h1 == NULL && h2 == NULL && h3 == NULL) continue; vt1.Z = h1? *h1 : j==0? sec->GetPlaneTexZ(sector_t::floor) : sec->GetPlaneTexZ(sector_t::ceiling); vt2.Z = h2? *h2 : j==0? sec->GetPlaneTexZ(sector_t::floor) : sec->GetPlaneTexZ(sector_t::ceiling); vt3.Z = h3? *h3 : j==0? sec->GetPlaneTexZ(sector_t::floor) : sec->GetPlaneTexZ(sector_t::ceiling); if (P_PointOnLineSidePrecise(vertexes[vi3].fX(), vertexes[vi3].fY(), sec->lines[0]) == 0) { vec1 = vt2 - vt3; vec2 = vt1 - vt3; } else { vec1 = vt1 - vt3; vec2 = vt2 - vt3; } DVector3 cross = vec1 ^ vec2; double len = cross.Length(); if (len == 0) { // Only happens when all vertices in this sector are on the same line. // Let's just ignore this case. continue; } cross /= len; // Fix backward normals if ((cross.Z < 0 && j == 0) || (cross.Z > 0 && j == 1)) { cross = -cross; } secplane_t *plane = j==0? &sec->floorplane : &sec->ceilingplane; double dist = -cross[0] * vertexes[vi3].fX() - cross[1] * vertexes[vi3].fY() - cross[2] * vt3.Z; plane->set(cross[0], cross[1], cross[2], dist); } } } }
bool P_Thing_Projectile (int tid, AActor *source, int type, const char *type_name, DAngle angle, double speed, double vspeed, int dest, AActor *forcedest, int gravity, int newtid, bool leadTarget) { int rtn = 0; PClassActor *kind; AActor *spot, *mobj, *targ = forcedest; FActorIterator iterator (tid); int defflags3; if (type_name == NULL) { kind = P_GetSpawnableType(type); } else { kind = PClass::FindActor(type_name); } if (kind == NULL) { return false; } // Handle decorate replacements. kind = kind->GetReplacement(); defflags3 = GetDefaultByType(kind)->flags3; if ((defflags3 & MF3_ISMONSTER) && ((dmflags & DF_NO_MONSTERS) || (level.flags2 & LEVEL2_NOMONSTERS))) return false; if (tid == 0) { spot = source; } else { spot = iterator.Next(); } while (spot != NULL) { FActorIterator tit (dest); if (dest == 0 || (targ = tit.Next())) { do { double z = spot->Z(); if (defflags3 & MF3_FLOORHUGGER) { z = ONFLOORZ; } else if (defflags3 & MF3_CEILINGHUGGER) { z = ONCEILINGZ; } else if (z != ONFLOORZ) { z -= spot->Floorclip; } mobj = Spawn (kind, spot->PosAtZ(z), ALLOW_REPLACE); if (mobj) { mobj->tid = newtid; mobj->AddToHash (); P_PlaySpawnSound(mobj, spot); if (gravity) { mobj->flags &= ~MF_NOGRAVITY; if (!(mobj->flags3 & MF3_ISMONSTER) && gravity == 1) { mobj->Gravity = 1./8; } } else { mobj->flags |= MF_NOGRAVITY; } mobj->target = spot; if (targ != NULL) { DVector3 aim = mobj->Vec3To(targ); aim.Z += targ->Height / 2; if (leadTarget && speed > 0 && !targ->Vel.isZero()) { // Aiming at the target's position some time in the future // is basically just an application of the law of sines: // a/sin(A) = b/sin(B) // Thanks to all those on the notgod phorum for helping me // with the math. I don't think I would have thought of using // trig alone had I been left to solve it by myself. DVector3 tvel = targ->Vel; if (!(targ->flags & MF_NOGRAVITY) && targ->waterlevel < 3) { // If the target is subject to gravity and not underwater, // assume that it isn't moving vertically. Thanks to gravity, // even if we did consider the vertical component of the target's // velocity, we would still miss more often than not. tvel.Z = 0.0; if (targ->Vel.X == 0 && targ->Vel.Y == 0) { goto nolead; } } double dist = aim.Length(); double targspeed = tvel.Length(); double ydotx = -aim | tvel; double a = g_acos (clamp (ydotx / targspeed / dist, -1.0, 1.0)); double multiplier = double(pr_leadtarget.Random2())*0.1/255+1.1; double sinb = -clamp (targspeed*multiplier * g_sin(a) / speed, -1.0, 1.0); // Use the cross product of two of the triangle's sides to get a // rotation vector. DVector3 rv(tvel ^ aim); // The vector must be normalized. rv.MakeUnit(); // Now combine the rotation vector with angle b to get a rotation matrix. DMatrix3x3 rm(rv, g_cos(g_asin(sinb)), sinb); // And multiply the original aim vector with the matrix to get a // new aim vector that leads the target. DVector3 aimvec = rm * aim; // And make the projectile follow that vector at the desired speed. mobj->Vel = aimvec * (speed / dist); mobj->AngleFromVel(); } else { nolead: mobj->Angles.Yaw = mobj->AngleTo(targ); mobj->Vel = aim.Resized (speed); } if (mobj->flags2 & MF2_SEEKERMISSILE) { mobj->tracer = targ; } } else { mobj->Angles.Yaw = angle; mobj->VelFromAngle(speed); mobj->Vel.Z = vspeed; } // Set the missile's speed to reflect the speed it was spawned at. if (mobj->flags & MF_MISSILE) { mobj->Speed = mobj->VelToSpeed(); } // Hugger missiles don't have any vertical velocity if (mobj->flags3 & (MF3_FLOORHUGGER|MF3_CEILINGHUGGER)) { mobj->Vel.Z = 0; } if (mobj->flags & MF_SPECIAL) { mobj->flags |= MF_DROPPED; } if (mobj->flags & MF_MISSILE) { if (P_CheckMissileSpawn (mobj, spot->radius)) { rtn = true; } } else if (!P_TestMobjLocation (mobj)) { // If this is a monster, subtract it from the total monster // count, because it already added to it during spawning. mobj->ClearCounters(); mobj->Destroy (); } else { // It spawned fine. rtn = 1; } } } while (dest != 0 && (targ = tit.Next())); } spot = iterator.Next(); } return rtn != 0; }
// [MC] Was part of P_Thing_Projectile, now its own function for use in ZScript. // Aims mobj at targ based on speed and targ's velocity. static void VelIntercept(AActor *targ, AActor *mobj, double speed, bool aimpitch = false, bool oldvel = false, bool leadtarget = true) { if (targ == nullptr || mobj == nullptr) return; DVector3 aim = mobj->Vec3To(targ); aim.Z += targ->Height / 2; if (leadtarget && speed > 0 && !targ->Vel.isZero()) { // Aiming at the target's position some time in the future // is basically just an application of the law of sines: // a/sin(A) = b/sin(B) // Thanks to all those on the notgod phorum for helping me // with the math. I don't think I would have thought of using // trig alone had I been left to solve it by myself. DVector3 tvel = targ->Vel; if (!(targ->flags & MF_NOGRAVITY) && targ->waterlevel < 3) { // If the target is subject to gravity and not underwater, // assume that it isn't moving vertically. Thanks to gravity, // even if we did consider the vertical component of the target's // velocity, we would still miss more often than not. tvel.Z = 0.0; if (targ->Vel.X == 0 && targ->Vel.Y == 0) { InterceptDefaultAim(mobj, targ, aim, speed); return; } } double dist = aim.Length(); double targspeed = tvel.Length(); double ydotx = -aim | tvel; double a = g_acos(clamp(ydotx / targspeed / dist, -1.0, 1.0)); double multiplier = double(pr_leadtarget.Random2())*0.1 / 255 + 1.1; double sinb = -clamp(targspeed*multiplier * g_sin(a) / speed, -1.0, 1.0); DVector3 prevel = mobj->Vel; // Use the cross product of two of the triangle's sides to get a // rotation vector. DVector3 rv(tvel ^ aim); // The vector must be normalized. rv.MakeUnit(); // Now combine the rotation vector with angle b to get a rotation matrix. DMatrix3x3 rm(rv, g_cos(g_asin(sinb)), sinb); // And multiply the original aim vector with the matrix to get a // new aim vector that leads the target. DVector3 aimvec = rm * aim; // And make the projectile follow that vector at the desired speed. mobj->Vel = aimvec * (speed / dist); mobj->AngleFromVel(); if (oldvel) { mobj->Vel = prevel; } if (aimpitch) // [MC] Ripped right out of A_FaceMovementDirection { const DVector2 velocity = mobj->Vel.XY(); mobj->Angles.Pitch = -VecToAngle(velocity.Length(), mobj->Vel.Z); } } else { InterceptDefaultAim(mobj, targ, aim, speed); } }