Converting a smooth ChaseCamera class from XNA to Irrlicht.

If you are a new Irrlicht Engine user, and have a newbie-question, this is the forum for you. You may also post general programming questions here.
Post Reply
jimburnettva
Posts: 13
Joined: Fri Feb 19, 2010 5:07 pm
Contact:

Converting a smooth ChaseCamera class from XNA to Irrlicht.

Post by jimburnettva »

I'm like 95% there....

I'm not sure if I am posting this in the right section or not but I figured sense I'm a total noob I'd start here.

A few months ago I tested a ChaseCamera class for the XNA framework, and it compiled and worked well out of the box. It had some nice dampening effects as well as smooth transitions.

Now, I've switched from XNA to Irrlicht and I am trying to convert this class to Irrlicht as there doesn't seem to be many working examples of a chase camera (3Rd person and CockpitClass don't count as they are not able to fly in 3D space correctly). XNA has many of the same concepts and methods which made it pretty easy for my to switch too. I was/am also in no way an experienced XNA or 3D developer as well.

So here is my conversion so far. It compiles just fine but I think some of my transformations are off. XNA uses transform.Up(Vect3D) and transform.Right(Vect3d) but my understanding of transformation is still rusty.

Would anyone be so kind to assist me?

C#/XNA Class:
Taken from here: http://creators.xna.com/en-US/sample/chasecamera

Code: Select all

#region File Description
//-----------------------------------------------------------------------------
// ChaseCamera.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion

#region Using Statements
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
#endregion

namespace ChaseCameraSample
{
    public class ChaseCamera
    {
        #region Chased object properties (set externally each frame)

        /// <summary>
        /// Position of object being chased.
        /// </summary>
        public Vector3 ChasePosition
        {
            get { return chasePosition; }
            set { chasePosition = value; }
        }
        private Vector3 chasePosition;

        /// <summary>
        /// Direction the chased object is facing.
        /// </summary>
        public Vector3 ChaseDirection
        {
            get { return chaseDirection; }
            set { chaseDirection = value; }
        }
        private Vector3 chaseDirection;

        /// <summary>
        /// Chased object's Up vector.
        /// </summary>
        public Vector3 Up
        {
            get { return up; }
            set { up = value; }
        }
        private Vector3 up = Vector3.Up;

        #endregion

        #region Desired camera positioning (set when creating camera or changing view)

        /// <summary>
        /// Desired camera position in the chased object's coordinate system.
        /// </summary>
        public Vector3 DesiredPositionOffset
        {
            get { return desiredPositionOffset; }
            set { desiredPositionOffset = value; }
        }
        private Vector3 desiredPositionOffset = new Vector3(0, 2.0f, 2.0f);

        /// <summary>
        /// Desired camera position in world space.
        /// </summary>
        public Vector3 DesiredPosition
        {
            get
            {
                // Ensure correct value even if update has not been called this frame
                UpdateWorldPositions();

                return desiredPosition;
            }
        }
        private Vector3 desiredPosition;

        /// <summary>
        /// Look at point in the chased object's coordinate system.
        /// </summary>
        public Vector3 LookAtOffset
        {
            get { return lookAtOffset; }
            set { lookAtOffset = value; }
        }
        private Vector3 lookAtOffset = new Vector3(0, 2.8f, 0);

        /// <summary>
        /// Look at point in world space.
        /// </summary>
        public Vector3 LookAt
        {
            get
            {
                // Ensure correct value even if update has not been called this frame
                UpdateWorldPositions();

                return lookAt;
            }
        }
        private Vector3 lookAt;

        #endregion

        #region Camera physics (typically set when creating camera)

        /// <summary>
        /// Physics coefficient which controls the influence of the camera's position
        /// over the spring force. The stiffer the spring, the closer it will stay to
        /// the chased object.
        /// </summary>
        public float Stiffness
        {
            get { return stiffness; }
            set { stiffness = value; }
        }
        private float stiffness = 1800.0f;

        /// <summary>
        /// Physics coefficient which approximates internal friction of the spring.
        /// Sufficient damping will prevent the spring from oscillating infinitely.
        /// </summary>
        public float Damping
        {
            get { return damping; }
            set { damping = value; }
        }
        private float damping = 600.0f;

        /// <summary>
        /// Mass of the camera body. Heaver objects require stiffer springs with less
        /// damping to move at the same rate as lighter objects.
        /// </summary>
        public float Mass
        {
            get { return mass; }
            set { mass = value; }
        }
        private float mass = 50.0f;

        #endregion

        #region Current camera properties (updated by camera physics)

        /// <summary>
        /// Position of camera in world space.
        /// </summary>
        public Vector3 Position
        {
            get { return position; }
        }
        private Vector3 position;

        /// <summary>
        /// Velocity of camera.
        /// </summary>
        public Vector3 Velocity
        {
            get { return velocity; }
        }
        private Vector3 velocity;

        #endregion


        #region Perspective properties

        /// <summary>
        /// Perspective aspect ratio. Default value should be overriden by application.
        /// </summary>
        public float AspectRatio
        {
            get { return aspectRatio; }
            set { aspectRatio = value; }
        }
        private float aspectRatio = 4.0f / 3.0f;

        /// <summary>
        /// Perspective field of view.
        /// </summary>
        public float FieldOfView
        {
            get { return fieldOfView; }
            set { fieldOfView = value; }
        }
        private float fieldOfView = MathHelper.ToRadians(45.0f);

        /// <summary>
        /// Distance to the near clipping plane.
        /// </summary>
        public float NearPlaneDistance
        {
            get { return nearPlaneDistance; }
            set { nearPlaneDistance = value; }
        }
        private float nearPlaneDistance = 1.0f;

        /// <summary>
        /// Distance to the far clipping plane.
        /// </summary>
        public float FarPlaneDistance
        {
            get { return farPlaneDistance; }
            set { farPlaneDistance = value; }
        }
        private float farPlaneDistance = 100000.0f;

        #endregion

        #region Matrix properties

        /// <summary>
        /// View transform matrix.
        /// </summary>
        public Matrix View
        {
            get { return view; }
        }
        private Matrix view;

        /// <summary>
        /// Projecton transform matrix.
        /// </summary>
        public Matrix Projection
        {
            get { return projection; }
        }
        private Matrix projection;

        #endregion


        #region Methods

        /// <summary>
        /// Rebuilds object space values in world space. Invoke before publicly
        /// returning or privately accessing world space values.
        /// </summary>
        private void UpdateWorldPositions()
        {
            // Construct a matrix to transform from object space to worldspace
            Matrix transform = Matrix.Identity;
            transform.Forward = ChaseDirection;
            transform.Up = Up;
            transform.Right = Vector3.Cross(Up, ChaseDirection);

            // Calculate desired camera properties in world space
            desiredPosition = ChasePosition +
                Vector3.TransformNormal(DesiredPositionOffset, transform);
            lookAt = ChasePosition +
                Vector3.TransformNormal(LookAtOffset, transform);
        }

        /// <summary>
        /// Rebuilds camera's view and projection matricies.
        /// </summary>
        private void UpdateMatrices()
        {
            view = Matrix.CreateLookAt(this.Position, this.LookAt, this.Up);
            projection = Matrix.CreatePerspectiveFieldOfView(FieldOfView,
                AspectRatio, NearPlaneDistance, FarPlaneDistance);
        }

        /// <summary>
        /// Forces camera to be at desired position and to stop moving. The is useful
        /// when the chased object is first created or after it has been teleported.
        /// Failing to call this after a large change to the chased object's position
        /// will result in the camera quickly flying across the world.
        /// </summary>
        public void Reset()
        {
            UpdateWorldPositions();

            // Stop motion
            velocity = Vector3.Zero;

            // Force desired position
            position = desiredPosition;

            UpdateMatrices();
        }

        /// <summary>
        /// Animates the camera from its current position towards the desired offset
        /// behind the chased object. The camera's animation is controlled by a simple
        /// physical spring attached to the camera and anchored to the desired position.
        /// </summary>
        public void Update(GameTime gameTime)
        {
            if (gameTime == null)
                throw new ArgumentNullException("gameTime");

            UpdateWorldPositions();

            float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;

            // Calculate spring force
            Vector3 stretch = position - desiredPosition;
            Vector3 force = -stiffness * stretch - damping * velocity;

            // Apply acceleration
            Vector3 acceleration = force / mass;
            velocity += acceleration * elapsed;

            // Apply velocity
            position += velocity * elapsed;

            UpdateMatrices();
        }

        #endregion
    }
}
Irrlicht C++ Class

Code: Select all

/*
 * ChaseCamera.cpp
 *
 *  Created on: Sep 3, 2010
 *      Author: jim
 */
#include "ChaseCamera.h"


//ChaseCamera::~ChaseCamera() {
	// TODO Auto-generated destructor stub
//}
float stiffness = 1800.0f;
float damping = 600.0f;
float mass = 50.0f;

float fieldOfView = irr::core::degToRad(45.0f);

irr::core::matrix4 view;
irr::core::matrix4 projection;
irr::core::vector3df desiredPositionOffset = irr::core::vector3df(0, 2.0f, 2.0f);


core::vector3df ChaseCamera::lookAtVector3df()
{
	UpdateWorldPositions();
	return lookAt;
}
void ChaseCamera::UpdateWorldPositions()
{
	irr::core::matrix4 transform = irr::core::IdentityMatrix;

	//irr::core::vector3df forward = ChasePosition;
	transform.transformVect(ChasePosition);
	transform.transformVect(Up);
    irr::core::vector3df Right = Up.crossProduct(ChasePosition);
    transform.transformVect(Right);
//from C#    desiredPosition = ChasePosition + Vector3.TransformNormal(DesiredPositionOffset, transform);
       desiredPositionOffset.normalize();
       desiredPosition = ChasePosition + desiredPositionOffset;
}


void ChaseCamera::UpdateMatrices()
{
	irr::core::matrix4 m;
	view = m.buildCameraLookAtMatrixRH(position,lookAtVector3df(),Up);
    projection = m.buildProjectionMatrixPerspectiveFovRH(fieldOfView,aspectRatio,nearPlaneDistance,farPlaneDistance);
}

void ChaseCamera::Reset()
{
	UpdateWorldPositions();
	velocity = irr::core::vector3df(0,0,0);
	UpdateMatrices();
}

//elapsed is in seconds.
void ChaseCamera::Update(float elapsed)
{
	UpdateWorldPositions();
	irr::core::vector3df stretch = position - desiredPosition;
	irr::core::vector3df force = -stiffness * stretch - damping * velocity;
	irr::core::vector3df acceleration = force / mass;
	velocity == acceleration * elapsed;
	UpdateMatrices();
}

ChaseCamera::ChaseCamera() {
	// TODO Auto-generated constructor stub
	desiredPositionOffset = core::vector3df(0, 2.0f, 2.0f);
	lookAtOffset = core::vector3df(0, 2.8f, 0);
	nearPlaneDistance = 1.0f;
	farPlaneDistance = 10000.0f;
	 aspectRatio = 4.0f / 3.0f;
}
/* header file for ChaseCamera.cpp */

Code: Select all

/*
 * ChaseCamera.h
 *
 *  Created on: Sep 3, 2010
 *      Author: jim
 */
#include <irrlicht.h>
using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;
#ifndef CHASECAMERA_H_
#define CHASECAMERA_H_

class ChaseCamera {
public:
	core::vector3df ChasePosition;
	core::vector3df ChaseDirection;
	core::vector3df desiredPosition;
	core::vector3df lookAtOffset;
	core::vector3df position;
	core::vector3df velocity;
	core::vector3df Up;
	core::vector3df Right;
	core::vector3df desiredPositionOffset;
	core::vector3df lookAt;
	float nearPlaneDistance;
	float farPlaneDistance;
	float aspectRatio;
    void UpdateWorldPositions();
    void UpdateMatrices();
    void Reset();
    void Update(float elapsed);
    irr::core::vector3df lookAtVector3df();
    ChaseCamera();


};

#endif /* CHASECAMERA_H_ */
vitek
Bug Slayer
Posts: 3919
Joined: Mon Jan 16, 2006 10:52 am
Location: Corvallis, OR

Post by vitek »

jimburnettva wrote:I'm like 95% there....

Code: Select all

private void UpdateWorldPositions()
{
  // Construct a matrix to transform from object space to worldspace
  Matrix transform = Matrix.Identity;
  transform.Forward = ChaseDirection;
  transform.Up = Up;
  transform.Right = Vector3.Cross(Up, ChaseDirection);

  // Calculate desired camera properties in world space
  desiredPosition = ChasePosition +
    Vector3.TransformNormal(DesiredPositionOffset, transform);
  lookAt = ChasePosition +
    Vector3.TransformNormal(LookAtOffset, transform);
}

private void UpdateMatrices() 
{ 
  view = Matrix.CreateLookAt(this.Position, this.LookAt, this.Up); 
  projection = Matrix.CreatePerspectiveFieldOfView(FieldOfView, 
                AspectRatio, NearPlaneDistance, FarPlaneDistance); 
}

public void Update(GameTime gameTime) 
{ 
  if (gameTime == null) 
    throw new ArgumentNullException("gameTime"); 

  UpdateWorldPositions(); 

  float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds; 

  // Calculate spring force 
  Vector3 stretch = position - desiredPosition; 
  Vector3 force = -stiffness * stretch - damping * velocity; 

  // Apply acceleration 
  Vector3 acceleration = force / mass; 
  velocity += acceleration * elapsed; 

  // Apply velocity 
  position += velocity * elapsed; 

  UpdateMatrices(); 
} 
I'm pretty sure you have problems in each of your implementations of the three above functions. I believe that the following are correct interpretations.

Code: Select all

irr::core::matrix4 matrix4_buildFromZY(const irr::core::vector3df& zaxis, const irr::core::vector3df& yaxis)
{
  const irr::core::vector3df xaxis = yaxis.crossProduct(zaxis);

  irr::core::matrix4 xform;

  xform[ 0] = xaxis.X;
  xform[ 4] = xaxis.Y;
  xform[ 8] = xaxis.Z;

  xform[ 1] = yaxis.X;
  xform[ 5] = yaxis.Y;
  xform[ 9] = yaxis.Z;

  xform[ 2] = zaxis.X;
  xform[ 6] = zaxis.Y;
  xform[10] = zaxis.Z;

  xform[3] = 0.f;
  xform[7] = 0.f;
  xform[11] = 0.f;

  xform[12] = 0.f;
  xform[13] = 0.f;
  xform[14] = 0.f;
  xform[15] = 1.f;

  return xform;
}


void ChaseCamera::UpdateWorldPositions() 
{ 
  cont irr::core::matrix4 transform = matrix4_buildFromZY(ChaseDirection, Up);

  irr::core::vector3df desiredPositionOffset2 = desiredPositionOffset;
  irr::core::vector3df lookAtOffset2 = lookAtOffset;

  //// may need to make a normalize the copy
  //desiredPositionOffset2.normalize();
  //lookAtOffset2.normalize();

  desiredPosition = ChasePosition +
    transform.transformVect(desiredPositionOffset2);
  lookAt = ChasePosition +
    transform.transformVect(lookAtOffset2);
} 

void ChaseCamera::Update(float elapsed) 
{ 
   UpdateWorldPositions(); 

   const irr::core::vector3df stretch = position - desiredPosition; 
   const irr::core::vector3df force = -stiffness * stretch - damping * velocity; 

   const irr::core::vector3df acceleration = force / mass; 
   velocity += acceleration * elapsed; 

   position += velocity * elapsed;

   UpdateMatrices(); 
} 

void ChaseCamera::UpdateMatrices() 
{ 
   view.buildCameraLookAtMatrixLH(
        position,
        lookAt,
        Up); 

   projection.buildProjectionMatrixPerspectiveFovLH(
        fieldOfView,
        aspectRatio,
        nearPlaneDistance,
        farPlaneDistance); 
}
Travis
Post Reply