// Filename: angularEulerIntegrator.cxx
// Created by:  charles (09Aug00)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University.  All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license.  You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////

#include "angularEulerIntegrator.h"
#include "forceNode.h"
#include "physicalNode.h"
#include "config_physics.h"

////////////////////////////////////////////////////////////////////
//     Function : AngularEulerIntegrator
//       Access : Public
//  Description : constructor
////////////////////////////////////////////////////////////////////
AngularEulerIntegrator::
AngularEulerIntegrator() {
}

////////////////////////////////////////////////////////////////////
//     Function : AngularEulerIntegrator
//       Access : Public
//  Description : destructor
////////////////////////////////////////////////////////////////////
AngularEulerIntegrator::
~AngularEulerIntegrator() {
}

////////////////////////////////////////////////////////////////////
//     Function : Integrate
//       Access : Public
//  Description : Integrate a step of motion (based on dt) by
//                applying every force in force_vec to every object
//                in obj_vec.
////////////////////////////////////////////////////////////////////
void AngularEulerIntegrator::
child_integrate(Physical *physical,
                AngularForceVector& forces,
                PN_stdfloat dt) {
  // Loop through each object in the set.  This processing occurs in
  // O(pf) time, where p is the number of physical objects and f is
  // the number of forces.  Unfortunately, no precomputation of forces
  // can occur, as each force is possibly contingent on such things as
  // the position and velocity of each physicsobject in the set.
  // Accordingly, we have to grunt our way through each one.  wrt
  // caching of the xform matrix should help.
  PhysicsObject::Vector::const_iterator current_object_iter;
  current_object_iter = physical->get_object_vector().begin();
  for (; current_object_iter != physical->get_object_vector().end();
       ++current_object_iter) {
    PhysicsObject *current_object = *current_object_iter;

    // bail out if this object doesn't exist or doesn't want to be
    // processed.
    if (current_object == (PhysicsObject *) NULL) {
      continue;
    }

    if (current_object->get_active() == false) {
      continue;
    }

    LRotation accum_quat(0, 0, 0, 0);

    // set up the traversal stuff.
    AngularForceVector::const_iterator f_cur;

    LRotation f;

    // global forces
    f_cur = forces.begin();
    //    unsigned int index = 0;
    for (; f_cur != forces.end(); ++f_cur) {
      AngularForce *cur_force = *f_cur;

      // make sure the force is turned on.
      if (cur_force->get_active() == false) {
        continue;
      }

      // tally it into the accumulation quaternion
      f = cur_force->get_quat(current_object);
      accum_quat += f;
    }

    LOrientation orientation = current_object->get_orientation();
    // local forces
    f_cur = physical->get_angular_forces().begin();
    for (; f_cur != physical->get_angular_forces().end(); ++f_cur) {
      AngularForce *cur_force = *f_cur;

      // make sure the force is turned on.
      if (cur_force->get_active() == false) {
        continue;
      }

      f = cur_force->get_quat(current_object);

      // tally it into the accumulation quaternion
      // i.e. orientation * f * orientation.conjugate()
      accum_quat += orientation.xform(f);
    }

    // apply the accumulated torque vector to the object's inertial tensor.
    // this matrix represents how much force the object 'wants' applied to it
    // in any direction, among other things.
    accum_quat = current_object->get_inertial_tensor() * accum_quat;

    // derive this into the angular velocity vector.
    LRotation rot_quat = current_object->get_rotation();
    #if 0
    rot_quat += accum_quat * dt;

    if (rot_quat.normalize()) {
      LOrientation old_orientation = current_object->get_orientation();
      LOrientation new_orientation = old_orientation * rot_quat;
      new_orientation.normalize();

      // and write the results back.
      current_object->set_orientation(new_orientation);
      current_object->set_rotation(rot_quat);
    }
    #else
    //accum_quat*=viscosityDamper;
    //LOrientation orientation = current_object->get_orientation();
    
    //accum_quat.normalize();
    // x = x + v * t + 0.5 * a * t * t
    orientation = orientation * ((rot_quat * dt) * (accum_quat * (0.5 * dt * dt)));
    // v = v + a * t
    rot_quat = rot_quat + (accum_quat * dt);

    //if (rot_quat.normalize()) {
    if (orientation.normalize() && rot_quat.normalize()) {
      // and write the results back.
      current_object->set_orientation(orientation);
      current_object->set_rotation(rot_quat);
    }
    #endif
  }
}

////////////////////////////////////////////////////////////////////
//     Function : output
//       Access : Public
//  Description : Write a string representation of this instance to
//                <out>.
////////////////////////////////////////////////////////////////////
void AngularEulerIntegrator::
output(ostream &out) const {
  #ifndef NDEBUG //[
  out<<"AngularEulerIntegrator (id "<<this<<")";
  #endif //] NDEBUG
}

////////////////////////////////////////////////////////////////////
//     Function : write
//       Access : Public
//  Description : Write a string representation of this instance to
//                <out>.
////////////////////////////////////////////////////////////////////
void AngularEulerIntegrator::
write(ostream &out, unsigned int indent) const {
  #ifndef NDEBUG //[
  out.width(indent); out<<""; out<<"AngularEulerIntegrator:\n";
  AngularIntegrator::write(out, indent+2);
  #endif //] NDEBUG
}
