Show kpath.cpp syntax highlighted
/*
Copyright (C) 2003, 2004, 2005 by Luca Cappa
Written by Luca Cappa groton@users.sourceforge.net
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "cssysdef.h"
#include "csutil/sysfunc.h"
#include "csutil/cscolor.h"
#include "csutil/cmdhelp.h"
#include "csutil/cspmeter.h"
#include "csutil/csstring.h"
#include "csutil/scfstr.h"
#include "csutil/flags.h"
#include "csutil/xmltiny.h"
#include "csutil/array.h"
#include "csutil/dirtyaccessarray.h"
#include "cstool/csview.h"
#include "cstool/initapp.h"
#include "cstool/collider.h"
#include "iutil/vfs.h"
#include "iutil/eventq.h"
#include "iutil/event.h"
#include "iutil/objreg.h"
#include "iutil/csinput.h"
#include "iutil/virtclk.h"
#include "iutil/plugin.h"
#include "iutil/string.h"
#include "iengine/sector.h"
#include "iengine/engine.h"
#include "iengine/camera.h"
#include "iengine/light.h"
#include "iengine/texture.h"
#include "iengine/mesh.h"
#include "iengine/movable.h"
#include "iengine/material.h"
#include "iengine/rview.h"
#include "imesh/thing.h"
#include "imesh/object.h"
#include "imesh/sprite3d.h"
#include "imesh/ball.h"
#include "csgfx/renderbuffer.h"
#include "csgfx/shadervarcontext.h"
#include "ivideo/graph3d.h"
#include "ivideo/graph2d.h"
#include "ivideo/natwin.h"
#include "ivideo/txtmgr.h"
#include "ivideo/texture.h"
#include "ivideo/material.h"
#include "ivideo/fontserv.h"
#include "igraphic/imageio.h"
#include "imap/loader.h"
#include "ivaria/reporter.h"
#include "ivaria/stdrep.h"
#include "ivaria/conout.h"
#include "ivaria/reporter.h"
#include "ivaria/stdrep.h"
#include "ivaria/collider.h"
#include "csgeom/quaterni.h"
#include "csgeom/transfrm.h"
#include "csgeom/math3d_d.h"
#include "csgeom/math3d.h"
#include "csgeom/math2d.h"
#include "csgeom/path.h"
#include "igeom/polymesh.h"
#include "igeom/objmodel.h"
#include "iaws/aws.h"
#include "iaws/awscnvs.h"
//
#include "ske.h"
#include "klandmark.h"
#include "ksprite3d.h"
#include "kobject3d.h"
//Include file for this file
#include "kpath.h"
SCF_IMPLEMENT_IBASE (KPath)
SCF_IMPLEMENTS_INTERFACE (iMeshObject)
SCF_IMPLEMENTS_INTERFACE (iKXMLReader)
SCF_IMPLEMENTS_INTERFACE (iKXMLWriter)
SCF_IMPLEMENTS_EMBEDDED_INTERFACE (iObjectModel)
SCF_IMPLEMENT_IBASE_END
SCF_IMPLEMENT_EMBEDDED_IBASE (KPath::ObjectModel)
SCF_IMPLEMENTS_INTERFACE (iObjectModel)
SCF_IMPLEMENT_EMBEDDED_IBASE_END
KPath::KPath (): mName (0), m_invisible (true), m_ballsInvisible (true),
m_path (0), csMeshObject (g_ske->m_engine),//??
m_preparedForDrawing (false), mMaterial (0),
mLineDefaultMaterialName (0)
{
SCF_CONSTRUCT_IBASE (0);
SCF_CONSTRUCT_EMBEDDED_IBASE (scfiObjectModel);
m_loader = CS_QUERY_REGISTRY (g_objReg, iLoader);
m_engine = CS_QUERY_REGISTRY (g_objReg, iEngine);
m_g3d = CS_QUERY_REGISTRY (g_objReg, iGraphics3D);
m_logparent = 0;
//?? UGLY! Read this from a configuration file please.
//Load the default material for drawing lines.
if (!m_loader->LoadTexture ("startingBall", "/SKE/texture/startingBall.jpg"))
{
SKE::Report (CS_REPORTER_SEVERITY_ERROR,
"Error loading 'startingBall' texture!");
}//if
if (!m_loader->LoadTexture ("yellow", "/SKE/texture/yellow.gif"))
{
SKE::Report (CS_REPORTER_SEVERITY_ERROR,
"Error loading 'yellow' texture!");
}//if
//Set the default material name for drawing lines.
SetDefaultMaterialForLineDrawing ("startingBall");
m_mW = (m_engine->CreateMeshWrapper (this, "_@Path@_"));
m_mW->SetRenderPriority (m_engine->GetRenderPriority ("alpha"));
m_mW->GetMovable ()->SetSector (g_ske->m_world);//??
m_mW->GetMovable ()->UpdateMove ();
//??JUST A TEST!m_mW->SetZBufMode (CS_ZBUF_NONE);
csRef<iStringSet> lStrings = CS_QUERY_REGISTRY_TAG_INTERFACE (g_objReg,
"crystalspace.shared.stringset", iStringSet);
mString_object2world = lStrings->Request ("object2world transform");
};
KPath::~KPath ()
{
//Remove all ball the meshes created.
Clear ();
//The mesh is removed from the engine upon destruction.
m_engine->GetMeshes ()->Remove (m_mW);
//Destroy the path.
delete m_path;
}
void KPath::AddPoint (float p_x, float p_z)
{
m_preparedForDrawing = false;
const char* classId = "crystalspace.mesh.object.ball";
csRef<iMeshFactoryWrapper> mesh_fact (m_engine->CreateMeshFactory (classId,
"ballFact"));
csVector3 l_pos (p_x, 0.3f, p_z);
csRef<iMeshWrapper> l_ballMW = m_engine->CreateMeshWrapper (mesh_fact,
"", g_ske->m_world, l_pos);//??
l_ballMW->SetRenderPriority (m_engine->GetRenderPriority ("alpha"));
l_ballMW->SetZBufMode(CS_ZBUF_USE);
csVector3 l_radius (0.15f, 0.15f, 0.15f);
csRef<iBallState> l_ballState (SCF_QUERY_INTERFACE (l_ballMW->GetMeshObject(),
iBallState));
l_ballState->SetRadius (l_radius.x, l_radius.y, l_radius.z);
l_ballState->SetShift (0, 0, 0);
l_ballState->SetRimVertices (16);
l_ballState->SetMaterialWrapper (SKE::CreateMaterial ("blue", 0.0f ,0.0f ,1.0f) );
//??ballstate->SetMixMode (CS_FX_SETALPHA(0.5));
l_ballState->SetMixMode (CS_FX_COPY);
l_ballState->SetReversed (false);
l_ballState->SetTopOnly (false);
l_ballState->SetLighting (true);
//
//Put a different texture in the first ball.
if (m_balls.Length () == 0)
{
iMaterialWrapper* lMatW = m_engine->GetMaterialList ()->
FindByName ("startingBall");
/*//??if (!lMatW->GetMaterialHandle ())
lMatW->Register (m_g3d->GetTextureManager ());//???????*/
l_ballState->SetMaterialWrapper (lMatW);
l_ballState->SetMixMode (CS_FX_COPY);
}//if
//Add the ball created onto the array.
m_balls.Push (l_ballMW);
//
//Set a name to the ball mesh in the form postpending
//a number after the this path name.
csString lName (mName ? mName->GetData () : "unnamed_KPath");
//-1 'cause starting from 0.
csString lS = Integer2String (m_balls.Length () - 1);
lName.Append (lS);
l_ballMW->QueryObject ()->SetName (lName);
}
void KPath::GetPoints (csDirtyAccessArray<csVector3>& p_points)
{
p_points.Empty ();
size_t i;
for (i = 0; i < m_balls.Length (); i++)
p_points.Push (m_balls.Get (i)->GetMovable ()->GetPosition ());
}
void KPath::SetInverted (bool pInverted)
{
mInverted = pInverted;
}
bool KPath::GetInverted ()
{
return mInverted;
}
void KPath::GetSmoothedPointsAndNormals (csDirtyAccessArray<csVector3>& p_points,
csDirtyAccessArray<csVector3>& p_normals)
{
p_points.Empty ();
p_normals.Empty ();
//If there is only one ball, nothing to do!
if (m_balls.Length () < 2)
return;
csVector3 l_oldPos = m_balls.Get (0)->GetMovable ()->GetPosition ();
size_t i;
size_t l_count = m_balls.Length ();
for (i = 0; i < l_count - 1; i++)
{
csVector3 l_pos = m_balls.Get (i + 1)->GetMovable ()->GetPosition ();
if (i == 0)//Put the first point.
{
p_points.Push (l_oldPos);
p_normals.Push (l_pos - l_oldPos);
}//if
//
//Put 2 different points for each pair of points (ie for
//each segment).
csVector3 l_1stNormal = (l_pos - l_oldPos).Unit ();
csVector3 l_2ndNormal = (l_pos - l_oldPos).Unit ();
if (i + 2 < m_balls.Length ())
{
float l_angle = 0.07f;
csVector2 l_a; l_a.x = l_oldPos.x; l_a.y = l_oldPos.z;
csVector2 l_b; l_b.x = l_pos.x; l_b.y = l_pos.z;
csVector2 l_c; l_c.x = m_balls.Get (i + 2)->GetMovable ()->GetPosition ().x;
l_c.y = m_balls.Get (i + 2)->GetMovable ()->GetPosition ().z;
if (csMath2::Right (l_a, l_b, l_c))
l_angle = -l_angle;
l_1stNormal = csYRotMatrix3 (-l_angle) * l_1stNormal;
l_2ndNormal = csYRotMatrix3 (l_angle) * l_2ndNormal;
}//if
csVector3 l_1st = l_oldPos + (l_pos - l_oldPos) * 0.15f;
p_points.Push (l_1st);
p_normals.Push (l_1stNormal);
csVector3 l_2nd = l_oldPos + (l_pos - l_oldPos) * 0.85f;
p_points.Push (l_2nd);
p_normals.Push (l_2ndNormal);
if (i == l_count - 2)//Put the last point.
{
p_points.Push (l_pos);
p_normals.Push (l_pos - l_oldPos);
}//if
l_oldPos = l_pos;
}//for
}
void KPath::GetPositionAtTime (csVector3*const p_pos, csVector3*const p_forward,
csVector3*const p_up, float p_time)
{
m_path->Calculate (p_time);
if (p_pos)
m_path->GetInterpolatedPosition (*p_pos);
if (p_forward)
m_path->GetInterpolatedForward (*p_forward);
if (p_up)
m_path->GetInterpolatedUp (*p_up);
}
float KPath::GetMeters ()
{
float l_meters = 0;
csDirtyAccessArray<csVector3> l_points;
csDirtyAccessArray<csVector3> l_normals;
GetSmoothedPointsAndNormals (l_points, l_normals);
int l_count = l_points.Length ();
int i;
for (i = 1; i < l_count; i++)
l_meters += (l_points[i] - l_points[i - 1]).Norm ();
return l_meters;
}
float KPath::GetDurationTime (float p_velocity)
{
return GetMeters () / p_velocity;
}
bool KPath::IsEmpty ()
{
return (m_balls.Length () <= 1);
}
void KPath::Init (float p_velocity)
{
m_velocity = p_velocity;
csDirtyAccessArray<csVector3> l_points;
csDirtyAccessArray<csVector3> l_normals;
GetSmoothedPointsAndNormals (l_points, l_normals);
//Delete the old path object.
delete m_path;
//
//Create the new path.
int l_count = l_points.Length ();
m_path = new csPath (l_count);
//
//Calculate the time values of the passing throught the points of the path.
csDirtyAccessArray<float> l_timeVals;
l_timeVals.Push (0);//1st time value.
int i;
for (i = 1; i < l_count; i++)
{
if ((i != 0) && (i != 1) && (i != l_count - 1) &&
(i % 2 == 1))
{
//This 'if' make the velocity slower when changing direction along the path.
csVector3 l_p1 (0,0,0); csVector3 l_p2 (0,0,0);
csVector3 l_p3 (0,0,0); csVector3 l_p4 (0,0,0);
l_p1.x = l_points[i - 2].x; l_p1.z = l_points[i - 2].z;
l_p2.x = l_points[i - 1].x; l_p2.z = l_points[i - 1].z;
l_p3.x = l_points[i].x; l_p3.z = l_points[i].z;
l_p4.x = l_points[i + 1].x; l_p4.z = l_points[i + 1].z;
csVector3 l_a (l_p1 - l_p2); csVector3 l_b (l_p4 - l_p3);
float l_cos = (l_a.Unit () * l_b.Unit ());
float l_angle = Acos (l_cos);
float l_percValue = ABS ((3.15f - ABS(l_angle)) / 3.15f) * (p_velocity - 0.05f);
l_timeVals.Push (((l_points[i] - l_points[i - 1]).Norm () /
(p_velocity - l_percValue)) + l_timeVals[i - 1]);
}//if
else
l_timeVals.Push (((l_points[i] - l_points[i - 1]).Norm () /
p_velocity) + l_timeVals[i - 1]);
}//for
csVector3* l_poss = new csVector3 [l_count] ();
for (i = 0; i< l_count; i++)
l_poss[i] = l_points[i];
csVector3* l_fors = new csVector3 [l_count] ();
for (i = 0; i< l_count; i++)
l_fors[i] = l_normals[i];
csVector3* l_ups = new csVector3 [l_count] ();
for (i = 0; i< l_count; i++)
l_ups[i] = csVector3 (0, 1, 0);
m_path->SetPositionVectors (l_poss);
m_path->SetForwardVectors (l_fors);
m_path->SetUpVectors (l_ups);
delete l_poss;
delete l_fors;
delete l_ups;
float* l_tvs = new float [l_count];
for (i = 0; i < l_count; i++)
l_tvs[i] = l_timeVals[i] / l_timeVals[l_count - 1];
m_path->SetTimeValues (l_tvs);
delete l_tvs;
//
//Create a very smoothed line.
csVector3 l_pos;
float l_segments = GetMeters () * 15;//15 segments for each meters.
csDirtyAccessArray<csVector3> l_spline;
float j;
for (j = 0.0f ; j <= 1.0f; j += 1.f / l_segments)
{
GetPositionAtTime (&l_pos, 0, 0 , j);
l_spline.Push (l_pos);
}//for
//
//Create the m_vertexBuffer.
m_vertexBuffer = csRenderBuffer::CreateRenderBuffer (
l_spline.Length (), CS_BUF_STATIC, CS_BUFCOMP_FLOAT, 3);
m_vertexBuffer->CopyInto (l_spline.GetArray (), l_spline.Length ());
//
//Allocate the m_indexBuffer.
m_indexBuffer = csRenderBuffer::CreateIndexRenderBuffer (
l_spline.Length (), CS_BUF_STATIC, CS_BUFCOMP_UNSIGNED_INT,
0, l_spline.Length () - 1);
//Fill it.
static unsigned int* s = 0;
delete s;
s = new unsigned int [l_spline.Length ()];
for (size_t i = 0; i < l_spline.Length (); i++)
s[i] = i;
m_indexBuffer->CopyInto (s, l_spline.Length ());
if (m_bufferHolder == 0)
m_bufferHolder.AttachNew (new csRenderBufferHolder ());
m_bufferHolder->SetRenderBuffer (CS_BUFFER_POSITION, m_vertexBuffer);
m_bufferHolder->SetRenderBuffer (CS_BUFFER_INDEX, m_indexBuffer);
m_preparedForDrawing = true;
}
void KPath::Clear ()
{
//
//Clear all the balls from the engine.
size_t i;
for (i = 0 ; i < m_balls.Length (); i++)
{
iMeshWrapper* l_mW = m_balls.Get (i);
m_engine->GetMeshes ()->Remove (l_mW);
}//for
//
//Empty the balls array.
m_balls.DeleteAll ();
m_preparedForDrawing = false;
}
//Returns the old state.
bool KPath::SetInvisible (bool p_invisible)
{
bool l_old = m_invisible;
m_invisible = p_invisible;
SetBallInvisible (m_invisible);
return l_old;
}
void KPath::SetDefaultMaterialForLineDrawing (char const* pName)
{
delete [] mLineDefaultMaterialName;
mLineDefaultMaterialName = csStrNew (pName);
}
bool KPath::LoadDefaultMaterialForLineDrawing (iMaterialWrapper*& pMaterial)
{
pMaterial = m_engine->GetMaterialList ()->FindByName (mLineDefaultMaterialName);
if (!pMaterial)
return false;
/*//??if (!pMaterial->GetMaterialHandle ())
pMaterial->Register (m_g3d->GetTextureManager ());//???????*/
return true;
}
//Returns the old state.
bool KPath::SetBallInvisible (bool p_ballsInvisible)
{
size_t i;
bool l_old = m_ballsInvisible;
m_ballsInvisible = p_ballsInvisible;
for (i = 0 ; i < m_balls.Length (); i++)
{
iMeshWrapper* l_mW = m_balls.Get (i);
iMovable* l_tMMovable= l_mW->GetMovable ();
l_tMMovable->SetSector (p_ballsInvisible ? g_ske->m_hiddenWorld :
g_ske->m_world);
}//for
return l_old;
}
csRenderMesh** KPath::GetRenderMeshes (int& p_count, iRenderView* p_rView,
iMovable* p_movable, uint32 p_frustumMask)
{
p_count = 0;
if (this->m_invisible || !this->m_preparedForDrawing)
return 0;
//????????? if (vis_cb) if (!vis_cb->BeforeDrawing (this, rview)) return false;
iCamera* l_cam = p_rView->GetCamera ();
// First create the transformation from object to camera space directly:
// W = Mow * O - Vow;
// C = Mwc * (W - Vwc)
// ->
// C = Mwc * (Mow * O - Vow - Vwc)
// C = Mwc * Mow * O - Mwc * (Vow + Vwc)
csReversibleTransform m_o2c;
csReversibleTransform lFT = p_movable->GetFullTransform ();
m_o2c = l_cam->GetTransform ();
if (!p_movable->IsFullTransformIdentity ())
m_o2c /= lFT;
int clip_portal, clip_plane, clip_z_plane;
p_rView->CalculateClipSettings (p_frustumMask, clip_portal, clip_plane,
clip_z_plane);
csVector3 camera_origin = m_o2c.GetT2OTranslation ();
if (mMaterial == 0)
{
LoadDefaultMaterialForLineDrawing (mMaterial);
}
bool rmCreated;
csRenderMesh*& meshPtr = m_rMHolder.GetUnusedMesh (rmCreated,
p_rView->GetCurrentFrameNumber ());
meshPtr->clip_portal = clip_portal;
meshPtr->clip_plane = clip_plane;
meshPtr->clip_z_plane = clip_z_plane;
meshPtr->do_mirror = l_cam->IsMirrored ();
meshPtr->indexend = m_indexBuffer->GetSize () / sizeof (unsigned int);//??UGLY HACK!
meshPtr->worldspace_origin = lFT.GetOrigin ();
if (rmCreated)//Al the constant data does not need to be copied everytime.
{
meshPtr->meshtype = CS_MESHTYPE_LINESTRIP;
meshPtr->indexstart = 0;
meshPtr->buffers = m_bufferHolder;
meshPtr->mixmode = CS_FX_COPY;//MixMode;
meshPtr->material = mMaterial;
meshPtr->geometryInstance = 0;
if (meshPtr->variablecontext == 0)
meshPtr->variablecontext.AttachNew (new csShaderVariableContext);
meshPtr->variablecontext->GetVariableAdd
(mString_object2world)->SetValue (lFT);
}//if
p_count = 1;
return &meshPtr;
}
/*
<paths>
<path name="">
<point index="" x="" z=""/>
</path>
......
</paths>*/
/**
* Accepts the <paths> node as argument. The <path> will be created as child of that node.
*/
bool KPath::Write (iDocumentNode* p_parent) const
{
csRef<iDocumentNode> l_pathNode =
p_parent->CreateNodeBefore (CS_NODE_ELEMENT, 0);
l_pathNode->SetValue ("path");
const char* l_name = (GetName () == 0 ? "noname" : GetName ());
l_pathNode->SetAttribute ("name", l_name);
size_t l_count = m_balls.Length ();
size_t i;
for (i = 0; i < l_count; i++)
{
csVector3 l_pos = m_balls.Get (i)->GetMovable ()->GetPosition ();
//
//Create the node for the <point>.
csRef<iDocumentNode> l_pointNode = l_pathNode->CreateNodeBefore
(CS_NODE_ELEMENT, 0);
l_pointNode->SetValue ("point");
l_pointNode->SetAttributeAsInt ("index", i);
l_pointNode->SetAttributeAsFloat ("x", l_pos.x);
l_pointNode->SetAttributeAsFloat ("z", l_pos.z);
}//for
return true;
}
//
//Accepts the <path> node as argument.
bool KPath::Read (iDocumentNode* p_parent)
{
if (strcmpi (p_parent->GetValue (), "path") != 0)
return false;
this->Clear ();
//
//Set the name of the path.
SetName (p_parent->GetAttributeValue ("name"));
csRef<iDocumentNodeIterator> l_it2 = p_parent->GetNodes ();
while (l_it2->HasNext ())
{
csRef<iDocumentNode> l_pointNode = l_it2->Next ();
if (l_pointNode->GetType () != CS_NODE_ELEMENT)
continue;
const char* value = l_pointNode->GetValue ();
if (!strcmpi (value, "point"))
{
const char* l_index = l_pointNode->GetAttributeValue ("index");
float l_x = l_pointNode->GetAttributeValueAsFloat ("x");
float l_z = l_pointNode->GetAttributeValueAsFloat ("z");
//???
//BUG: we should take care of the 'index', i.e. be sure the iteration order is
//of <point>s nodes right.
AddPoint (l_x, l_z);
}//if
}//while
return true;
}
See more files for this project here