Game Development Community

True Weapon Ironsights/Scope - Weapon Camera

by CSMP · in Torque Game Engine · 12/21/2009 (11:11 am) · 19 replies

MPGE Website (Updated Today)
Ok, so I've kinda been on vacation from making the game and recently I've started to get back into it, If you don't already know I've made character animations that attempt to recreate an "IronSights" type weapon view... this works relatively well with AR's, SMG's and LMG's however the Sniper Rifles are a completely differant story.

Point being I've been thinking about how to get a proper view from Sniper Rifles and it seems it may be easiest adding an 'eye' node to the weapons and upon calling the Zoom function it snaps to the weapon 'eye' node and adjusts the FOV accordingly, with this method I may be able to tone down the character "IronSights" animations and setup other weapons with the 'eye' node as-well for a better end result.

Sniper "Zoom/Scoped" Non-Functional View(you can see where the impact explosion occurs but this is 'not' the ideal view for a rifle...)

I've really gotten rusty since my "vacation" and I was wondering if anyone could point me in the right direction.(as if I was any good before) :)

#1
12/21/2009 (2:37 pm)
I originaly posted this in the T3D private forum, but because I refer only to a resource, which is now all public,
heres a video of iron sights, and an explaiation on my iron sights setup,
at 1.50 minutes into the clip, the player changes to a scoped weapon, and uses iron sights, the iron sights are very "flexable", you can set most of the feel, like, how much movement from breathing, which you see in the clip.



the resources I refer to are for tgea, but should work fine in tge

you will notice there are no crosshairs or huds, at all, I want my game like this, it makes you focus more, so the ironsights have to be not only spot on accurate, but also realistic, not pinned to the screen, but moving with the players arms.
this does it

it begins with implementing

this resource, which gives the realistic first person,you can do the same with out it, but the effect will be more rigid, then

this resource, which relies on the fact that you WILL use eyeOffset.
but we dont want to use eyeoffset, cause its too fake, the weapon doesnt move with the player, it moves with the camera, stuck to a corner of the screen.
we want the weapon to move with the player, in his hands by NOT using eyeoffset.
but if we remove/comment out the eyeoffset line, like this, //eyeOffset = "0.2 0.5 -0.25";
we get the weapon in our hands,
but the adjusteyeoffset, that the ironsights resource uses, needs the eyeoffset to be there.
what do we do?
just above the line
eyeOffset = "0.2 0.5 -0.25";
we add
useEyeOffset = false;
this causes the weapon to think that eyeoffset is not there, so we see it in our hands,
but the resource has no idea what
useEyeOffset = false;
means, so when called, it functions, and we have ironsights that move the weapon in line with our eye, but is still mounted in the players hands

and here is the bit of script that makes it all work
mountPoint = 0;
    useEyeOffset = false;// THIS IS THE LINE
    eyeOffset = "0.2 0.5 -0.25"; // 0.46=right/left 0.5=forward/backward, -0.5=up/down

    // Added for adjustable eyeOffset   
    adjustEyeOffset   = true;    
    normalEyeOffset   = "0.2 0.5 -0.25"; //This should always match eyeOffset   
                                           //value listed above   
    adjustedEyeOffset = "-0.0015 0.4 -0.2525"; //This is the eyeOffet value we   
                                                //switch to on zoom   
    // Added for adjustable eyeOffset 

    // When firing from a point offset from the eye, muzzle correction
    // will adjust the muzzle vector to point to the eye LOS point.
    correctMuzzleVector = true;

#2
12/21/2009 (5:08 pm)
Quote:it may be easiest adding an 'eye' node to the weapons and upon calling the Zoom function it snaps to the weapon 'eye' node and adjusts the FOV accordingly
I've been thinking about something similar for scoped weapons, but I haven't even started looking at how I would implement it. If you're still using ShapebaseImages, you could probably hack into getEyeTransform and use the eye node on a mounted image. I'm not planning on using Images, so I think the fix will be more along the lines of separating the camera and control object for a client, and mounting the camera to the weapon shape.
#3
12/21/2009 (11:54 pm)
@Deepscratch: Appreciate the help, Yeah I'm already using the Realistic FPS Resource but I am worried that the correctMuzzleVector and EyeOffset code will affect the rifle shooting straight as the barrel, however I will look into the code right now and see if it can create reasonable results.

@Daniel: Thats actually what I was trying to accomplish, I imagine I would have to duplicate the getEyeTransform into the ShapeBaseImage.cc,.h but any further then that I can only assume that I would change any %player.getEyeTransform with %player.getMountedImage().getEyeTransform?

P.S. Thanks guys! :)
#4
12/22/2009 (1:58 am)
Ok, so I tried out the IronSights resource and found that it sets the weapon to be 'stuck' on the screen instead of in the players arms, not sure if its something I've done wrong... But I'll try it a couple more times and see if I can get it to work.

Yes I am using Images also, I've already setup the weapon model with an 'eye' node and this is the only code I could think of to 'snap' the camera to the weapons 'eye' node.
// setup weapon's EyeNode for "scope" emulation
   %client.camera.setTransform(%curPlayer.getMountedImage($WeaponSlot).getEyeTransform());

but obviously I keep getting this error:
common/scripts/server/commands.cs (729): Unknown command getEyeTransform.
  Object BarrettImage(610) BarrettImage -> WeaponImage -> ShapeBaseImageData -> GameBaseData -> SimDataBlock -> SimObject

I've been looking through shapebase and shapeimage for clues on how to setup a getEyeTransform function but I have to say I'm failry clueless because I'm not exactly sure what code I would need to invoke for the weapons 'eye' node to take over for "ZoomIn" and reset on "ZoomOut"
#5
12/22/2009 (7:54 am)
Quote:I imagine I would have to duplicate the getEyeTransform into the ShapeBaseImage.cc,.h
I was actually thinking of modifying Player::getEyeTransform. If a certain flag is set on the player object, return the transform of a mounted image's eye node (I think getImageTransform already allows you to specify a node name) - otherwise return the transform of our own eye node. It might take a bit more infrastructure than that (for example, specifying which image slot to use).
#6
12/22/2009 (8:08 am)
Yeah, that actually seems more logical... I just had no idea where to start and thats a great idea on how to do it.

I'll take a look in the code and see what I can come up with.
#7
12/22/2009 (10:00 am)
Ok, so far I've looked into player,shapebase and shapeimage files...

I've added:
(ShapeBase.h)
S32 WpnEyeNode;      ///< Weapon Eye node ID.
and
(ShapeImage.cc)
WpnEyeNode = shape->findNode("eyePoint");

Because of the other weapon related nodes I figured this should be accessible as well, now i'm not exactly sure which getImageTransform(there are 3!?) I would use to call my new node.(would I still need to use this considering I already declared my node with WpnEyeNode?)

And I also have no idea how I would flag source code dynamically, specifically how i would toggle between:(Player::getEyeTransform)
if (mDataBlock->eyeNode != -1)
   {
      sp = mShapeInstance->mNodeTransforms[mDataBlock->eyeNode];
   }
and
if (mDataBlock->wpneyeNode != -1)
   {
      sp = mShapeInstance->mNodeTransforms[mDataBlock->wpneyeNode];
   }

not too mention how the script would invoke the dynamic toggle of the nodes.

Edit: Are there any identical functions I could look at that could lead me to the correct changes needed?
#8
12/22/2009 (5:25 pm)
Quote:Edit: Are there any identical functions I could look at that could lead me to the correct changes needed?
I suspect not - at least I haven't seen anything too similar.

S32 WpnEyeNode;      ///< Weapon Eye node ID.
You're declaring this as part of ShapeBaseImageData, right? Just checking.

Quote:now i'm not exactly sure which getImageTransform(there are 3!?) I would use to call my new node
The one you need should be
void ShapeBase::getImageTransform(U32 imageSlot,S32 node,MatrixF* mat)
This one lets you specify a node by number: WpnEyeNode should be your number. There's also a function which lets you input the node name - so you could just do that without worrying about storing WpnEyeNode. But that's going to require the eye node to be looked up every frame, which is a waste.

Quote:And I also have no idea how I would flag source code dynamically
I think I would either add a new member to the Player class, like mAiming or something, which toggles this functionality (and has all the associated networking, console commands, etc.), or hack into the existing move struct (maybe hijack one of the triggers that isn't currently used). It depends on how you're already controlling iron sights.
if (mDataBlock->wpneyeNode != -1)  
   {  
      sp = mShapeInstance->mNodeTransforms[mDataBlock->wpneyeNode];  
   }
I'm going to say there are problems with this, but I think you can work out what they are ;).
I would actually use the aiming toggle (whatever it ended up being) to completely replace the contents of Player::getEyeTransform. If you're aiming, and the weapon has an eye node and all that, use your own transform - otherwise, perform the method normally.
#9
12/24/2009 (7:43 am)
Yeah, I was kinda going out on a limb for those changes... I just need to look into the source code some more and figure out how things are done a little bit better before I try "major"(for me, lol) changes like what I am trying to accomplish.

Thanks for the pointers, I'll try again a.s.a.p. and I'll come back with good news! :)
#10
04/17/2010 (8:38 am)
OK, I see what you are talking about using Player::getEyeTransform with a flag...

I don't understand what the current function is doing or how I would get the weapons eyeNode though.

Function in question:
void Player::getEyeTransform(MatrixF* mat)
{
   // Eye transform in world space.  We only use the eye position
   // from the animation and supply our own rotation.
   MatrixF pmat,xmat,zmat;
   xmat.set(EulerF(mHead.x, 0.0f, 0.0f));
   zmat.set(EulerF(0.0f, 0.0f, mHead.z));
   pmat.mul(zmat,xmat);

   F32 *dp = pmat;
   F32* sp;
   if (mDataBlock->eyeNode != -1)
   {
      sp = mShapeInstance->mNodeTransforms[mDataBlock->eyeNode];
   }
   else
   {
      Point3F center;
      mObjBox.getCenter(&center);
      MatrixF eyeMat(true);
      eyeMat.setPosition(center);

      sp = eyeMat;
   }

   const Point3F& scale = getScale();
   dp[3] = sp[3] * scale.x;
   dp[7] = sp[7] * scale.y;
   dp[11] = sp[11] * scale.z;
   mat->mul(getRenderTransform(), pmat);

}

#11
04/18/2010 (8:06 am)
OK, I may be doing things a little differant than most so I'm going to elaborate what I'm trying to do.

My character models are always visible, the FirstPerson view is nothing more then looking through the characters eyes...

My weapons aim is completely dependant on the Arm animations and position offset of the weapon, there is no reticle/crosshair...
Where it looks like the gun is aimed will be where the bullet hits.(not accounting ballistics)

The method I was thinking of would be adding an eyeNode to each of the weapon's model(where needed) and setting it up into my IronSights system which mainly consists of switching between an idle/run animation or a raised arm animation for "IronSights" view of the weapon(which atm is not as good looking as it sounds), in which case would also switch between the players eyeNode or the weapons eyeNode. (and set the FOV accordingly as well.)
#12
05/21/2010 (3:01 am)
@Deepscratch: I'm having problems getting the weapon to return to the correct offset(in players hands), it seems to be wanting an eyeoffset even though the variable is false and it is returning to the eyeoffset value anyway!?

After alot of time looking into a "weapon cam" I have decided to try this instead and it seems this does not work either.
#13
10/09/2010 (7:01 am)
I stumbled on this thread again this arvo, and I remembered something that might help:
Using a separate camera object

If I'm reading this resource right, this allows you to control one object while viewing through another. So weapon scopes with the camera attached to the weapon should be as simple as setting the camera to be attached to the weapon object.

Caveat - this requires that your weapons are objects, not images :P. Sorry, I should have thought of that. I've done away with images, or nearly so, in favour of making everything an object, so this solution struck me as perfect.
#14
10/09/2010 (10:53 am)
@CSMP,
read my instructions again, I explained about that.
#15
10/16/2010 (9:03 pm)
@Daniel: I'll have to look into that for any ideas.

@Deepscratch: Actually I've decided against using the offsets and currently using animations for the desired effect.

Appreciate the help guys.
#16
03/24/2011 (12:06 pm)
OK, well coming back to this now that I have realized the animation method is good for networked shooting anyway because of the visible feedback of an enemy sighting you, but still not the polished effect needed...

@deepscratch: time and time again I've tried the method given but no matter what no success, I've narrowed my problems being that if eyeOffset is anything other then "0 0 0" it automatically snaps to the screen and if the eyeOffset is "0 0 0" then it snaps back to the players hands.

the only way I have gotten the desired effect is editing the weapon.cs file and exec'ing it from the console, I could use some fresh thoughts on this.
#17
03/27/2011 (6:10 pm)
The animation method is certainly a good idea - the problem is, if the animations are off at all, the player has trouble aiming. I still think the getEyeTransform modification method should serve you well. A basic solution might look something like this:

void Player::getEyeTransform(MatrixF* mat)
{
   if(mAiming)
   {
      getRenderImageTransform(0, "eyePoint", mat);
      return;
   }

   // Eye transform in world space.  We only use the eye position
   // from the animation and supply our own rotation.
   MatrixF pmat,xmat,zmat;
   xmat.set(EulerF(mHead.x, 0.0f, 0.0f));
   zmat.set(EulerF(0.0f, 0.0f, mHead.z));
   pmat.mul(zmat,xmat);

   F32 *dp = pmat;
   F32* sp;
   if (mDataBlock->eyeNode != -1)
   {
      sp = mShapeInstance->mNodeTransforms[mDataBlock->eyeNode];
   }
   else
   {
      Point3F center;
      mObjBox.getCenter(&center);
      MatrixF eyeMat(true);
      eyeMat.setPosition(center);

      sp = eyeMat;
   }

   const Point3F& scale = getScale();
   dp[3] = sp[3] * scale.x;
   dp[7] = sp[7] * scale.y;
   dp[11] = sp[11] * scale.z;
   mat->mul(getRenderTransform(), pmat);
}

This basically short-curcuits the regular calculations if we're aiming, and substitutes a very simple use of the weapon image transform instead. It's so simple because getRenderImageTransform actually returns a result in world-space already, so we don't even have to do any processing. See if that works.

(You'll obviously have to figure out how to handle mAiming yourself, though. ;P For now, I'd hardcode it to a scripted global variable or something.)
#18
03/28/2011 (1:39 pm)
@Daniel: That looks great, I've duplicated the freeLook code and renamed accordingly but I'm still getting this error:

EnginesourceT3Dplayer.cpp(4100) : error C2065: 'Aiming' : undeclared identifier

I'm not sure how to declare it from this point, I'm checking for spelling, syntax and missing code right now.
#19
03/28/2011 (6:24 pm)
As in the freeLook code that's in the Move struct? Problem is, there's no Move struct defined in getEyeTransform. mAiming or your equivalent needs to be a property of the Player object. You only get Moves in the updateMove method, so you'd need to set mAiming there, based on what the move is telling you. Personally, I just use one of the triggers (id 7, I think) to indicate aiming.