******** Direction vector used in the Popgun ******
I am trying to modify the Popgun that can be found in our inventory library so that it can detect whether a target would be hit without actually firing bullets. However I'm completely stuck on the direction vector used in the Popgun.
The algorithm I've implemented is a simple Ray-Sphere Test from a book on Games Programming. It just tests whether a ray from the gun would hit or miss the target sphere.
The algorithm in the book looks simple but in converting it to SL script there are some issues surrounding the direction element of the gun.
The Ray Sphere Test algorithm is:--
RAY is defined as:--
X = Rorg.x + Rdir.x * lamda
Y = Rorg.y + Rdir.y * lamda
Z = Rorg.z + Rdir.z * lamda
SPHERE is defined by:--
(X-CenterX)^2 + (Y-CenterY)^2 + (Z-CenterZ)^2 = Radius^2
the equations to be solved are
1) A = Rdir.x^2 + Rdir.y^2 + Rdir.z^2
2) B = 2 * (Rdir.x^2*(Rorg.x-CenterX) + (Rdir.y^2*(Rorg.y-CenterY) + (Rdir.z^2*
(Rorg.z-CenterZ))
3) C = (Rorg.x-CenterX)^2 + (Rorg.y-CenterY)^2 + (Rorg.z-CenterZ)^2 - Radius^2
often in part 1, A is set to 1 because the ray's direction is normalised.
The above is a quadratic equation with it being necessary to solve the determinant
B^2 - 4*A * C
if the determinant is -ve then the ray missed the sphere, otherwise it hit the sphere.
The above algorithm is implememented in the Popgun script below but it does not work as the gun direction variable is incorrectly set.
CODE
//
// Popgun
//
// This script is a basic gun- it waits for a mouseclick in mouselook and
// then fires a bullet in the direction the user is facing.
// It also animates the avatar holding it, to create a convining hold and
// firing look for the gun.
//
// This script can be used as a good basis for other weapons.
//
vector targetG = <108.0, 226.0, 128.0>; // Coords of sphere - radius = 1
float SPEED = 20.0; // Speed of arrow in meters/sec
integer LIFETIME = 7; // How many seconds will bullets live
// before deleting themselves
float DELAY = 0.2; // Delay between shots to impose
vector directG; // Direction gun points
vector velG; // Used to store velocity of arrow to be shot
vector posG; // Used to store position of arrow to be shot
rotation rotG; // Used to store rotation of arrow to be shot
integer have_permissions = FALSE; // Indicates whether wearer has yet given permission
// to take over their controls and animation.
integer armed = TRUE; // Used to impose a short delay between firings
string instruction_held_1 = "Use Mouselook (press 'M') to shoot me.";
string instruction_held_2 = "Choose 'Detach' from my menu to take me off.";
// Echoed to wearer when they are holding the bow
string instruction_not_held = "Right-click (apple-click) me, and choose More > Wear' from the menu to use me.";
// Echoed to toucher if not worn
fire()
{
//
// This subroutine creates and fires an arrow
//
if (armed)
{
//
// Actually fires the arrow
//
armed = FALSE;
rotG = llGetRot(); // Get current avatar mouselook direction
velG = llRot2Fwd(rotG); // Convert rotation to a direction vector
directG=velG;
posG = llGetPos(); // Get position of avatar to create arrow
posG = posG + velG; // Create arrow slightly in direction of travel
posG.z += 0.75; // Correct creation point upward to eye point
// from hips, so that in mouselook we see arrow
// travelling away from the camera.
velG = velG * SPEED; // Multiply normalized vector by speed
//llStartAnimation("shoot_R_handgun"); // Trigger the bow release animation
llTriggerSound("shoot", 1.0); // Make the sound of the arrow being shot
llRezObject("bullet", posG, velG, rotG, LIFETIME);
// Create the actual arrow from object
// inventory, and set its position, velocity,
// and rotation. Pass a parameter to it to
// tell it how long to live.
if (RaySphereTestFN()<0) llOwnerSay("Missed");
else llOwnerSay("Hit");
llSetTimerEvent(DELAY); // Wait until can fire again
}
}
integer RaySphereTestFN()
{ integer result;
float a;
float b;
float c;
float t1;
float t2;
float t3;
float radius;
float det;
// A
a=(directG.x*directG.x)+(directG.y*directG.y)+(directG.z*directG.z);
// B
t1=(directG.x*directG.x)*(posG.x-targetG.x);
t2=(directG.y*directG.y)*(posG.y-targetG.y);
t3=(directG.z*directG.z)*(posG.z-targetG.z);
b=2*(t1+t2+t3);
// C
t1=(posG.x-targetG.x)*(posG.x-targetG.x);
t2=(posG.y-targetG.y)*(posG.y-targetG.y);
t3=(posG.z-targetG.z)*(posG.z-targetG.z);
radius=1.0;
c=t1+t2+t3-radius;
// Det
det=(b*b)-(4*a*c);
if (det<0) result=-1;
else result=1;
return result;
}
default
{
state_entry()
//
// This routine is called whenever the script is edited and restarted. So if you
// are editing the bow while wearing it, this code will re-request permissions
// to animate and capture controls.
//
{
if (!have_permissions)
{
llRequestPermissions(llGetOwner(),
PERMISSION_TRIGGER_ANIMATION| PERMISSION_TAKE_CONTROLS);
}
}
on_rez(integer param)
{
//
// Called when the gun is created from inventory.
//
llPreloadSound("shoot"); // Preload shooting sound so you hear it
}
run_time_permissions(integer permissions)
{
//
// This routine is called when the user accepts the permissions request
// (sometimes this is automatic)
// so on receiving permissions, start animation and take controls.
//
if (permissions == PERMISSION_TRIGGER_ANIMATION| PERMISSION_TAKE_CONTROLS)
{
if (!have_permissions)
{
llWhisper(0, instruction_held_1);
llWhisper(0, instruction_held_2);
}
llTakeControls(CONTROL_ML_LBUTTON, TRUE, FALSE);
llStartAnimation("hold_R_handgun");
have_permissions = TRUE;
}
}
attach(key attachedAgent)
{
//
// If attached/detached from agent, change behavior
//
if (attachedAgent != NULL_KEY)
{
// Bow has been attached or rezzed from inventory, so
// ask for needed permissions.
llRequestPermissions(llGetOwner(),
PERMISSION_TRIGGER_ANIMATION| PERMISSION_TAKE_CONTROLS);
}
else
{
// Bow has been detached from avatar, so stop animation and release controls
if (have_permissions)
{
llStopAnimation("hold_R_handgun");
llStopAnimation("aim_R_handgun");
llReleaseControls();
llSetRot(<0,0,0,1>);
have_permissions = FALSE;
}
}
}
control(key name, integer levels, integer edges)
{
// This function is called when the mouse button or other controls
// are pressed, and the controls are being captured.
//
// Note the logical AND (single &) used - the levels and edges
// variables passed in are bitmasks, and must be checked with
// logical compare.
//
// Checking for both edge and level means that the button has just
// been pushed down, which is when we want to fire the arrow!
//
if ( ((edges & CONTROL_ML_LBUTTON) == CONTROL_ML_LBUTTON)
&&((levels & CONTROL_ML_LBUTTON) == CONTROL_ML_LBUTTON) )
{
// If left mousebutton is pressed, fire arrow
fire();
}
}
touch_start(integer num)
{
// If touched, remind user how to enter mouselook and shoot
if (have_permissions)
{
llWhisper(0, instruction_held_1);
llWhisper(0, instruction_held_2);
}
else
{
llWhisper(0, instruction_not_held);
}
}
timer()
{
// After timer expires, allow user to shoot bow again
llSetTimerEvent(0.0);
armed = TRUE;
}
}
< target_radius)