using System;
using System.Collections;
using System.Collections.Specialized;
using RealmForge.Rendering;
using AxAxiom.Core;
using Axiom.MathLib;
using Axiom.Core;
using Axiom.Controllers;
using Axiom.Controllers.Canned;
using RealmForge;
using RealmForge.Physics;
using RealmForge.Scripting;
using MathUtilAxiom.MathLib.MathUtil;
using RealmForge.Scene;
using Axiom.Animating;
using RealmForge.Animation;
namespace RealmForge.SceneObjects{
/// <summary>
/// Scene Node for the Axiom Engine
/// </summary>
public class AxiomSceneNode : SceneNode {
#region Fields
protected Ax.SceneNode node = null;
protected ISceneNode trackTarget = null;
protected bool tracking = false;
protected Vector3 positionBeforeHidingParents;
#endregion
#region Constructors
public AxiomSceneNode(string id) : this(id, AxiomSceneManager.AxiomInstance) {
}
protected internal AxiomSceneNode(string id, Ax.SceneManager scene) : this(id, scene, false){
}
/// <summary>
/// Creates an instance of AxiomSceneNode
/// </summary>
/// <remarks>
/// This constructor would be internal but it needs to be used by custom scene objects that derive from it in plugins
/// and it cant be protected as it needs to be used by the SceneBuilder though we may want to consider it if the SceneBuilder was to use
/// the simple id only constructor
/// </remarks>
/// <param name="scene"></param>
/// <param name="id"></param>
/// <param name="qualifyMovementNodeID">this should be true for classes that inherit from this one and us the unique ID for their own entity for instance</param>
protected internal AxiomSceneNode(string id, Ax.SceneManager scene, bool qualifyMovementNodeID ) : base(id){
if(qualifyMovementNodeID) //##{id}_MovementNode is the format used for nodes used under the hood
node = scene.CreateSceneNode( RF.Scene.GetInternalID(id, "MovementNode") );
else
node = scene.CreateSceneNode(id);
}
#endregion
#region Methods
#region Protected
protected override void InternalAddChild(ISceneNode newChild) {
object obj = newChild.UnderlyingObject;
if(obj is Ax.Node) {
Ax.Node n = (Ax.Node)obj;
//HACK: This should be done by Axiom
if(n.Parent != null)
n.Parent.RemoveChild(n);
node.AddChild((Ax.Node)obj);
}
else{
Ax.SceneNode movementNode = ((AxiomSceneNode)newChild).node;
//HACK: This should be handled by Axiom, setting ParentNode to null wont remove from the parents collection
if(movementNode != null && movementNode.Parent != null)
movementNode.Parent.RemoveChild(movementNode);
this.node.AddChild(movementNode);
}
}
protected override void InternalRemoveChild(ISceneNode child) {
if(child == null)
Errors.ArgumentNull("Child can not be null");
object obj = child.UnderlyingObject;
if(obj is Ax.Node) {
node.RemoveChild((Ax.Node)obj);
}
else{
Ax.Node childNode = ((Ax.SceneObject)obj).ParentNode;
node.RemoveChild(childNode.Name);
}
}
#endregion
#region Create Functions
public ScriptEvent CreateTimeFunctionEvent(string eventName, Script eventHandler, float fireRate, IControllerFunction function) {
ScriptEvent scriptEvent = this.Events.CreateEventIfNeeded(eventName);
if(eventHandler != null)
scriptEvent.AddHandler(eventHandler);
EventTrigger trigger = new TimeFunctionEventTrigger(eventName,fireRate,function);
AddEventTrigger(trigger);
return scriptEvent;
}
public ScriptEvent CreateTimeFunctionEvent(string eventName, Script eventHandler, float fireRate, WaveformType type, float baseVal, float frequency, float phase, float amplitude, bool useDelta) {
WaveformControllerFunction function = new WaveformControllerFunction(type, baseVal, frequency, phase, amplitude, useDelta);
return CreateTimeFunctionEvent(eventName, eventHandler, fireRate, function);
}
public ScriptEvent CreateTimeFunctionEvent(string eventName, Script eventHandler, float fireRate, WaveformType type, float baseVal, float frequency, float phase, float amplitude) {
return CreateTimeFunctionEvent(eventName, eventHandler, fireRate,
type, baseVal, frequency, phase, amplitude, true);
}
public ScriptEvent CreateTimeFunctionEvent(string eventName, Script eventHandler, float fireRate, WaveformType type, float baseVal, float frequency, float phase) {
return CreateTimeFunctionEvent(eventName, eventHandler, fireRate,
type, baseVal, frequency, phase, 1f);
}
public ScriptEvent CreateTimeFunctionEvent(string eventName, Script eventHandler, float fireRate, WaveformType type, float baseVal, float frequency) {
return CreateTimeFunctionEvent(eventName, eventHandler, fireRate,
type, baseVal, frequency, 0f);
}
public ScriptEvent CreateTimeFunctionEvent(string eventName, Script eventHandler, float fireRate, WaveformType type, float baseVal) {
return CreateTimeFunctionEvent(eventName, eventHandler, fireRate,
type, baseVal, 1f);
}
public ScriptEvent CreateTimeFunctionEvent(string eventName, Script eventHandler, float fireRate, WaveformType type) {
return CreateTimeFunctionEvent(eventName, eventHandler, fireRate,
type, 0f);
}
public ScriptEvent CreateTimeFunctionEvent(string eventName, Script eventHandler, float fireRate) {
return CreateTimeFunctionEvent(eventName, eventHandler, fireRate,
WaveformType.Sine);
}
#endregion
#region Public
public override void ShowOnlyThis()
{
if(parent == null)
return;
foreach(ISceneNode node in parent.Children) {
if(node != this)
node.Visible = false;
}
/*
if(node.Parent != null)
node.Position += off = node.Parent.DerivedPosition;
AxiomSceneManager.AxiomInstance.OverrideRootSceneNode(node);
*/
}
public override void StopShowingOnlyThis()
{
if(parent == null)
return;
foreach(ISceneNode node in parent.Children) {
node.Visible = true;
}
/*
node.Position -= off;
AxiomSceneManager.AxiomInstance.RestoreRootSceneNode();
*/
}
public override IAnimation CreateAnimation(string animationName, float timeLength, bool splineInterpolation, bool keyFramesArePositions, params Vector3[] keyFrames) {
Ax.SceneManager scene = AxiomSceneManager.AxiomInstance;
//qualify the name because this animation is only for this node but the name must be globally unique
animationName = ID + '.' + animationName; //qualify
Axiom.Animating.Animation anim = scene.CreateAnimation(animationName, timeLength);
AnimationTrack track = anim.CreateTrack(0, this.node);
//spline is the default for Axion animations
anim.InterpolationMode = (splineInterpolation? InterpolationMode.Spline: InterpolationMode.Linear);
int keyFrameCount = keyFrames.Length; //get the number of key frames to create (one for each translation/movement vector)
if(keyFrameCount == 1)
Errors.Argument("There must be either no keyFrameMovements or more then one");
else if(keyFrameCount != 0) {
float timePerKeyFrame = timeLength / (keyFrameCount - 1); //the time between each frame with one frame at the start and another at the end
Vector3 lastPosition = Vector3.Zero;
for(int i = 0; i<keyFrameCount; i++) {
KeyFrame frame = track.CreateKeyFrame(timePerKeyFrame * i);
if(keyFramesArePositions && i > 0) {
frame.Translate = keyFrames[i] - keyFrames[i-1];//target pos - current = offset to get there
} else
frame.Translate = keyFrames[i];
}
}
return new RealmForge.Animation.AxiomAnimation(this, scene.CreateAnimationState(animationName));
}
public override void LookAt(Vector3 targetAbsolutePosition) {
//world will error if there is no parent node
node.LookAt(targetAbsolutePosition,(parent == null || !this.isShown)? TransformSpace.Local: TransformSpace.World);
if(physBody != null)
this.physBody.Orientation = node.DerivedOrientation;
}
#endregion
#endregion
#region Properties
public Ax.SceneNode MovementNode { get { return this.node; } }
public override bool ShowBoundingBox {
get{ return node.ShowBoundingBox; }
set{ node.ShowBoundingBox = value; }
}
/// <summary>
/// Gets or Sets the offset for tracking a target
/// </summary>
public override Vector3 TrackingOffset {
get {
return node.AutoTrackOffset;
}
set{
node.AutoTrackOffset = value;
}
}
/// <summary>
/// Gets or Sets the target node that is being tracked
/// </summary>
public override ISceneNode TrackingTarget {
get { return trackTarget; }
set{
trackTarget = value;
if(tracking) //if not disabled
node.AutoTrackTarget = (Ax.SceneNode)trackTarget.UnderlyingNode; //set the target movement node
}
}
public override string TrackingTargetID
{
get{ return (trackTarget == null)? null: trackTarget.ID; }
set
{
trackTarget = RF.Scene.GetSceneNode(value);
}
}
/// <summary>
/// Gets or Sets if this camera is tracking a target
/// </summary>
public override bool Tracking {
get {
//use this flag so that Tracking can be set before TrackingTarget yet have this still work
return tracking; //node.AutoTrackTarget != null;
}
set{
tracking = value;
if(tracking && trackTarget != null)
node.AutoTrackTarget = (Ax.SceneNode)trackTarget.UnderlyingNode;
else
node.AutoTrackTarget = null;
}
}
public override object UnderlyingNode { get{ return node; } }
public override object UnderlyingObject {
get { return node; }
set {
node = (Ax.SceneNode)value;
}
}
/// <summary>
/// Gets the axis-alingned bounding box in absolute coordinates that encompasses this node and all of its children
/// </summary>
public override AxisAlignedBox CombinedBoundingBox { get { return node.WorldAABB; } }
/// <summary>
/// Gets the bound sphere in absolute coordinates that encompasses this node and all of its children
/// </summary>
public override Sphere CombinedBoundingSphere { get { return node.WorldBoundingSphere; } }
/// <summary>
/// Gets the radius of the bounding sphere that encompasses this node and all of its children
/// </summary>
public override float CombinedBoundingRadius { get { return node.WorldBoundingSphere.Radius; } }
/// <summary>
///
/// </summary>
/// <remarks>Scale is in relative to the nodes origon similiar to orientation</remarks>
public override Vector3 Scale { get { return node.ScaleFactor; } set { node.ScaleFactor = value; } }
/// <summary>
///
/// </summary>
/// <remarks>Scale is in relative to the nodes origon similiar to orientation</remarks>
public override Vector3 AbsoluteScale {
get{ return node.DerivedScale; }
set{ node.ScaleFactor = value - node.DerivedScale; }
}
/// <summary>
/// If children will be scaled with this node
/// </summary>
/// <remarks>By default children are scaled with this parents
/// You may want to disable this for example to prevent trees being scaled with a landmass or weapons with a characters height
/// This is applied retroactivly for all newly added children</remarks>
public override bool InheritScale {
get{ return node.InheritScale; }
set{ node.InheritScale = value; }
}
/// <summary>
/// Gets or Sets the Orientation or direction faced relative to the parent node
/// </summary>
/// <remarks>Triggers the OrientationChanged event if the value is different from the current one</remarks>
public override Quaternion Orientation {
get { return node.Orientation; }
set {
Quaternion oldOri = node.Orientation;
if(oldOri == value)
return;
node.Orientation = value;
OrientationChanged.Trigger(oldOri);
}
}
/// <summary>
/// Gets or Sets the absolute Orientation or direction faced relative to the world as derived from all its parent nodes
/// </summary>
/// <remarks>Triggers the OrientationChanged event if the value is different from the current one</remarks>
public override Quaternion AbsoluteOrientation {
get { return node.DerivedOrientation; }
set{
Ax.Node parentMovementNode = node.Parent;
//Orientation *= value / node.DerivedOrientation
//orientation is rotated by the difference between the current and the target orientation
if(parentMovementNode == null)
Orientation = value;
else {
Orientation = value * parentMovementNode.DerivedOrientation.Inverse();
}
}
}
/// <summary>
/// Gets or Sets the Position of this node or the offset relative to the parent node
/// </summary>
/// <remarks>Triggers the PositionChanged event if the value is different from the current one</remarks>
public override Vector3 Position {
get { return node.Position; }
set {
Vector3 oldPos = node.Position;
if(oldPos != value) {
node.Position = value;
PositionChanged.Trigger(oldPos);
}
}
}
/// <summary>
/// Gets or Sets the absolute position of this node relative to the world as derived from all its parents relative
/// </summary>
/// <remarks>Triggers the PositionChanged event if the value is different from the current one</remarks>
public override Vector3 AbsolutePosition {
get {
return node.DerivedPosition;
}
set {
Position = value - node.DerivedPosition;
}
}
public override string ID {
get{ return node.Name; }
set{
string old = ID;
if(value != old)
{
if(parent != null)
{//re-key in both axiom and realmforge's child collections
((AxiomSceneNode)parent).RekeyChild(old, value);
}
node.Name = value;
SceneNodeRenamedArgs args = new SceneNodeRenamedArgs(this, old, value);
this.Renamed.Trigger(args);
RF.Scene.SceneObjectRenamed.Trigger(args);
}
}
}
#endregion
}
}
|