Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LevelLocals.SphericalCoords: Compute spherical coordinates from one point in the world to another #553

Merged
merged 1 commit into from
Aug 21, 2018

Conversation

argv-minus-one
Copy link
Contributor

@argv-minus-one argv-minus-one commented Aug 21, 2018

This acts on static map data only, so it should be client-server safe. Useful for checking whether one actor is inside another actor's view cone.

Here's the documentation for this new method (which I'll add to the wiki if this PR is merged):

LevelLocals.SphericalCoords

Vector3 SphericalCoords(Vector3 viewpoint, Vector3 targetPos [, Vector2 viewAngles] [, bool absolute])

Usage

Computes spherical coordinates pointing to targetPos from viewpoint.

Parameters

  • viewpoint: Origin point (in world x,y,z coordinates). Computed spherical coordinates will be relative to this origin point.
  • targetPos: A distant point (in world x,y,z coordinates). Computed spherical coordinates will point from viewpoint to targetPos.
  • viewAngles: Vector2 containing the viewpoint's yaw and pitch angles. This corresponds to the actor fields Angle and Pitch. Default is (0, 0).
  • absolute: If true, the calculation will ignore portals. Default is false, meaning portals will be accounted for.

Return value

A Vector3, with these components:

  • X: How far viewAngles.X (the viewpoint's yaw) must turn left (if negative) or right (if positive) to face targetPos, in degrees. This is the azimuthal angle or φ (phi).
  • Y: How far viewAngles.Y (the viewpoint's pitch) must pitch down (if negative) or up (if positive) to face targetPos, in degrees. This is the polar angle or θ (theta).
  • Z: How far targetPos is from viewpoint, in world units. This is the radius or r.

Note that the Z coordinate is the same as you'd get from LevelLocals.Vec3Diff(viewpoint, targetPos).Length(), and similar to a.Distance3D(b) (where a and b are actors). Distance3D only computes distances between the bottoms of actors, however, and does not account for their heights or viewpoints.

Example

You can use this to determine how close an actor is to the player's crosshairs.

PlayerPawn viewer;
Actor other;

// Get the world position (x,y,z) of the player's eyes.
Vector3 viewpoint = viewer.Pos;
viewpoint.Z = viewer.Player.viewz;

// Get the world position of the middle of the other actor.
// Actor.Pos is normally at the very bottom of the actor, so we'll take
// other.Pos and add half of its height.
Vector3 otherCenter = other.Pos;
otherCenter.Z += other.Height * .5;

// Get the player actor's yaw and pitch.
Vector2 viewAngles = (viewer.Angle, viewer.Pitch);

// Compute how close the other actor is to the player's crosshairs.
Vector3 sphericalCoords = LevelLocals.SphericalCoords(viewpoint, otherCenter, viewAngles);
double distanceFromCenter = sphericalCoords.XY.Length();

if (distanceFromCenter <= 20)
{
	// This block only runs if `other` is no more than 20° from the center
	// of the player's view.
}

Here's a more complete example: an inventory item that tells you what you're aiming at.

class SimpleActorIdentifier : Inventory {
	override void Tick() {
		if (!Owner) {
			Target = null;
			return;
		}
		
		// Get the world position (x,y,z) of the owner's eyes.
		Vector3 viewpoint = Owner.Pos;
		
		if (Owner.Player)
			viewpoint.Z = Owner.Player.viewz;
		else
			viewpoint.Z += Owner.GetCameraHeight();
		
		// Find the owner's view angles (yaw and pitch).
		Vector2 viewAngles = (Owner.Angle, Owner.Pitch);
		
		// Find the actor that's closest to the owner's crosshairs.
		// Ignore actors that are more than 20° away from the center.
		Actor closest = null;
		double closestDistFromCenter = 20;
		
		for (let i = BlockThingsIterator.Create(Owner, 512); i.Next();) {
			Actor other = i.thing;
			
			// The owner can't aim at itself, of course.
			if (other == Owner)
				continue;
			
			// Get the world position of the middle of the other actor.
			// Actor.Pos is normally at the very bottom of the actor, so we'll
			// take other.Pos and add half of its height.
			Vector3 otherCenter = other.Pos;
			otherCenter.Z += other.Height * .5;
			
			// Compute how close the other actor is to the player's crosshairs.
			Vector3 sphericalCoords = LevelLocals.SphericalCoords(viewpoint, otherCenter, viewAngles);
			double distanceFromCenter = sphericalCoords.XY.Length();
			
			// Is it closer?
			if (distanceFromCenter < closestDistFromCenter) {
				closest = other;
				closestDistFromCenter = distanceFromCenter;
			}
		}
		
		// Now, decide on what message to log, if any.
		String msg = "";
		
		if (!closest && Target)
			// Was aiming at something, but no longer is.
			msg = "Not aiming at anything";
		else if (closest != Target)
			// Was aiming at something else (or nothing).
			msg = String.Format("Now aiming at %s", closest.GetTag(closest.GetClassName()));
		
		// Log the message, if any.
		if (msg)
			Owner.A_Log(msg);
		
		// Update the Target pointer to whatever was chosen.
		// Next tic, we'll check again whether the owner is still aiming at
		// the same thing.
		Target = closest;
	}
}

It computes spherical coordinates from one point in the world to another. Useful for checking whether one actor is inside another actor's view cone.
@coelckers coelckers merged commit 1d930b4 into ZDoom:master Aug 21, 2018
@argv-minus-one
Copy link
Contributor Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants