The GtkRadiant sources as originally released under the GPL license.

This commit is contained in:
Travis Bradshaw
2012-01-31 15:20:35 -06:00
commit 0991a5ce8b
1590 changed files with 431941 additions and 0 deletions

23
plugins/entity/angle.cpp Normal file
View File

@@ -0,0 +1,23 @@
/*
Copyright (C) 2001-2006, William Joseph.
All Rights Reserved.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "angle.h"

101
plugins/entity/angle.h Normal file
View File

@@ -0,0 +1,101 @@
/*
Copyright (C) 2001-2006, William Joseph.
All Rights Reserved.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#if !defined(INCLUDED_ANGLE_H)
#define INCLUDED_ANGLE_H
#include "ientity.h"
#include "math/quaternion.h"
#include "generic/callback.h"
#include "stringio.h"
const float ANGLEKEY_IDENTITY = 0;
inline void default_angle(float& angle)
{
angle = ANGLEKEY_IDENTITY;
}
inline void normalise_angle(float& angle)
{
angle = static_cast<float>(float_mod(angle, 360.0));
}
inline void read_angle(float& angle, const char* value)
{
if(!string_parse_float(value, angle))
{
angle = 0;
}
else
{
normalise_angle(angle);
}
}
inline void write_angle(float angle, Entity* entity)
{
if(angle == 0)
{
entity->setKeyValue("angle", "");
}
else
{
char value[64];
sprintf(value, "%g", angle);
entity->setKeyValue("angle", value);
}
}
class AngleKey
{
Callback m_angleChanged;
public:
float m_angle;
AngleKey(const Callback& angleChanged)
: m_angleChanged(angleChanged), m_angle(ANGLEKEY_IDENTITY)
{
}
void angleChanged(const char* value)
{
read_angle(m_angle, value);
m_angleChanged();
}
typedef MemberCaller1<AngleKey, const char*, &AngleKey::angleChanged> AngleChangedCaller;
void write(Entity* entity) const
{
write_angle(m_angle, entity);
}
};
inline float angle_rotated(float angle, const Quaternion& rotation)
{
return matrix4_get_rotation_euler_xyz_degrees(
matrix4_multiplied_by_matrix4(
matrix4_rotation_for_z_degrees(angle),
matrix4_rotation_for_quaternion_quantised(rotation)
)
).z();
}
#endif

23
plugins/entity/angles.cpp Normal file
View File

@@ -0,0 +1,23 @@
/*
Copyright (C) 2001-2006, William Joseph.
All Rights Reserved.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "angles.h"

140
plugins/entity/angles.h Normal file
View File

@@ -0,0 +1,140 @@
/*
Copyright (C) 2001-2006, William Joseph.
All Rights Reserved.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#if !defined(INCLUDED_ANGLES_H)
#define INCLUDED_ANGLES_H
#include "ientity.h"
#include "math/quaternion.h"
#include "generic/callback.h"
#include "stringio.h"
#include "angle.h"
const Vector3 ANGLESKEY_IDENTITY = Vector3(0, 0, 0);
inline void default_angles(Vector3& angles)
{
angles = ANGLESKEY_IDENTITY;
}
inline void normalise_angles(Vector3& angles)
{
angles[0] = static_cast<float>(float_mod(angles[0], 360));
angles[1] = static_cast<float>(float_mod(angles[1], 360));
angles[2] = static_cast<float>(float_mod(angles[2], 360));
}
inline void read_angle(Vector3& angles, const char* value)
{
if(!string_parse_float(value, angles[2]))
{
default_angles(angles);
}
else
{
angles[0] = 0;
angles[1] = 0;
normalise_angles(angles);
}
}
inline void read_angles(Vector3& angles, const char* value)
{
if(!string_parse_vector3(value, angles))
{
default_angles(angles);
}
else
{
angles = Vector3(angles[2], angles[0], angles[1]);
normalise_angles(angles);
}
}
inline void write_angles(const Vector3& angles, Entity* entity)
{
if(angles[0] == 0
&& angles[1] == 0
&& angles[2] == 0)
{
entity->setKeyValue("angle", "");
entity->setKeyValue("angles", "");
}
else
{
char value[64];
if(angles[0] == 0 && angles[1] == 0)
{
entity->setKeyValue("angles", "");
write_angle(angles[2], entity);
}
else
{
sprintf(value, "%g %g %g", angles[1], angles[2], angles[0]);
entity->setKeyValue("angle", "");
entity->setKeyValue("angles", value);
}
}
}
inline Vector3 angles_rotated(const Vector3& angles, const Quaternion& rotation)
{
return matrix4_get_rotation_euler_xyz_degrees(
matrix4_multiplied_by_matrix4(
matrix4_rotation_for_euler_xyz_degrees(angles),
matrix4_rotation_for_quaternion_quantised(rotation)
)
);
}
class AnglesKey
{
Callback m_anglesChanged;
public:
Vector3 m_angles;
AnglesKey(const Callback& anglesChanged)
: m_anglesChanged(anglesChanged), m_angles(ANGLESKEY_IDENTITY)
{
}
void angleChanged(const char* value)
{
read_angle(m_angles, value);
m_anglesChanged();
}
typedef MemberCaller1<AnglesKey, const char*, &AnglesKey::angleChanged> AngleChangedCaller;
void anglesChanged(const char* value)
{
read_angles(m_angles, value);
m_anglesChanged();
}
typedef MemberCaller1<AnglesKey, const char*, &AnglesKey::anglesChanged> AnglesChangedCaller;
void write(Entity* entity) const
{
write_angles(m_angles, entity);
}
};
#endif

23
plugins/entity/colour.cpp Normal file
View File

@@ -0,0 +1,23 @@
/*
Copyright (C) 2001-2006, William Joseph.
All Rights Reserved.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "colour.h"

102
plugins/entity/colour.h Normal file
View File

@@ -0,0 +1,102 @@
/*
Copyright (C) 2001-2006, William Joseph.
All Rights Reserved.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#if !defined(INCLUDED_COLOUR_H)
#define INCLUDED_COLOUR_H
#include "ientity.h"
#include "irender.h"
#include "math/vector.h"
#include "eclasslib.h"
#include "generic/callback.h"
#include "stringio.h"
inline void default_colour(Vector3& colour)
{
colour = Vector3(1, 1, 1);
}
inline void read_colour(Vector3& colour, const char* value)
{
if(!string_parse_vector3(value, colour))
{
default_colour(colour);
}
}
inline void write_colour(const Vector3& colour, Entity* entity)
{
char value[64];
sprintf(value, "%f %f %f", colour[0], colour[1], colour[2]);
entity->setKeyValue("_color", value);
}
class Colour
{
Callback m_colourChanged;
Shader* m_state;
void capture_state()
{
m_state = colour_capture_state_fill(m_colour);
}
void release_state()
{
colour_release_state_fill(m_colour);
}
public:
Vector3 m_colour;
Colour(const Callback& colourChanged)
: m_colourChanged(colourChanged)
{
default_colour(m_colour);
capture_state();
}
~Colour()
{
release_state();
}
void colourChanged(const char* value)
{
release_state();
read_colour(m_colour, value);
capture_state();
m_colourChanged();
}
typedef MemberCaller1<Colour, const char*, &Colour::colourChanged> ColourChangedCaller;
void write(Entity* entity) const
{
write_colour(m_colour, entity);
}
Shader* state() const
{
return m_state;
}
};
#endif

23
plugins/entity/curve.cpp Normal file
View File

@@ -0,0 +1,23 @@
/*
Copyright (C) 2001-2006, William Joseph.
All Rights Reserved.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "curve.h"

521
plugins/entity/curve.h Normal file
View File

@@ -0,0 +1,521 @@
/*
Copyright (C) 2001-2006, William Joseph.
All Rights Reserved.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#if !defined(INCLUDED_CURVE_H)
#define INCLUDED_CURVE_H
#include "ientity.h"
#include "selectable.h"
#include "renderable.h"
#include <set>
#include "math/curve.h"
#include "stream/stringstream.h"
#include "selectionlib.h"
#include "render.h"
#include "stringio.h"
class RenderableCurve : public OpenGLRenderable
{
public:
std::vector<PointVertex> m_vertices;
void render(RenderStateFlags state) const
{
pointvertex_gl_array(&m_vertices.front());
glDrawArrays(GL_LINE_STRIP, 0, GLsizei(m_vertices.size()));
}
};
inline void plotBasisFunction(std::size_t numSegments, int point, int degree)
{
Knots knots;
KnotVector_openUniform(knots, 4, degree);
globalOutputStream() << "plotBasisFunction point " << point << " of 4, knot vector:";
for(Knots::iterator i = knots.begin(); i != knots.end(); ++i)
{
globalOutputStream() << " " << *i;
}
globalOutputStream() << "\n";
globalOutputStream() << "t=0 basis=" << BSpline_basis(knots, point, degree, 0.0) << "\n";
for(std::size_t i = 1; i < numSegments; ++i)
{
double t = (1.0 / double(numSegments)) * double(i);
globalOutputStream() << "t=" << t << " basis=" << BSpline_basis(knots, point, degree, t) << "\n";
}
globalOutputStream() << "t=1 basis=" << BSpline_basis(knots, point, degree, 1.0) << "\n";
}
inline bool ControlPoints_parse(ControlPoints& controlPoints, const char* value)
{
StringTokeniser tokeniser(value, " ");
std::size_t size;
if(!string_parse_size(tokeniser.getToken(), size))
{
return false;
}
if(size < 3)
{
return false;
}
controlPoints.resize(size);
if(!string_equal(tokeniser.getToken(), "("))
{
return false;
}
for(ControlPoints::iterator i = controlPoints.begin(); i != controlPoints.end(); ++i)
{
if(!string_parse_float(tokeniser.getToken(), (*i).x())
|| !string_parse_float(tokeniser.getToken(), (*i).y())
|| !string_parse_float(tokeniser.getToken(), (*i).z()))
{
return false;
}
}
if(!string_equal(tokeniser.getToken(), ")"))
{
return false;
}
return true;
}
inline void ControlPoints_write(const ControlPoints& controlPoints, StringOutputStream& value)
{
value << Unsigned(controlPoints.size()) << " (";
for(ControlPoints::const_iterator i = controlPoints.begin(); i != controlPoints.end(); ++i)
{
value << " " << (*i).x() << " " << (*i).y() << " " << (*i).z() << " ";
}
value << ")";
}
inline void ControlPoint_testSelect(const Vector3& point, ObservedSelectable& selectable, Selector& selector, SelectionTest& test)
{
SelectionIntersection best;
test.TestPoint(point, best);
if(best.valid())
{
Selector_add(selector, selectable, best);
}
}
class ControlPointTransform
{
const Matrix4& m_matrix;
public:
ControlPointTransform(const Matrix4& matrix) : m_matrix(matrix)
{
}
void operator()(Vector3& point) const
{
matrix4_transform_point(m_matrix, point);
}
};
class ControlPointSnap
{
float m_snap;
public:
ControlPointSnap(float snap) : m_snap(snap)
{
}
void operator()(Vector3& point) const
{
vector3_snap(point, m_snap);
}
};
const Colour4b colour_vertex(0, 255, 0, 255);
const Colour4b colour_selected(0, 0, 255, 255);
class ControlPointAdd
{
RenderablePointVector& m_points;
public:
ControlPointAdd(RenderablePointVector& points) : m_points(points)
{
}
void operator()(const Vector3& point) const
{
m_points.push_back(PointVertex(vertex3f_for_vector3(point), colour_vertex));
}
};
class ControlPointAddSelected
{
RenderablePointVector& m_points;
public:
ControlPointAddSelected(RenderablePointVector& points) : m_points(points)
{
}
void operator()(const Vector3& point) const
{
m_points.push_back(PointVertex(vertex3f_for_vector3(point), colour_selected));
}
};
class CurveEditType
{
public:
Shader* m_controlsShader;
Shader* m_selectedShader;
};
inline void ControlPoints_write(ControlPoints& controlPoints, const char* key, Entity& entity)
{
StringOutputStream value(256);
if(!controlPoints.empty())
{
ControlPoints_write(controlPoints, value);
}
entity.setKeyValue(key, value.c_str());
}
class CurveEdit
{
SelectionChangeCallback m_selectionChanged;
ControlPoints& m_controlPoints;
typedef Array<ObservedSelectable> Selectables;
Selectables m_selectables;
RenderablePointVector m_controlsRender;
mutable RenderablePointVector m_selectedRender;
public:
typedef Static<CurveEditType> Type;
CurveEdit(ControlPoints& controlPoints, const SelectionChangeCallback& selectionChanged) :
m_selectionChanged(selectionChanged),
m_controlPoints(controlPoints),
m_controlsRender(GL_POINTS),
m_selectedRender(GL_POINTS)
{
}
template<typename Functor>
const Functor& forEachSelected(const Functor& functor)
{
ASSERT_MESSAGE(m_controlPoints.size() == m_selectables.size(), "curve instance mismatch");
ControlPoints::iterator p = m_controlPoints.begin();
for(Selectables::iterator i = m_selectables.begin(); i != m_selectables.end(); ++i, ++p)
{
if((*i).isSelected())
{
functor(*p);
}
}
return functor;
}
template<typename Functor>
const Functor& forEachSelected(const Functor& functor) const
{
ASSERT_MESSAGE(m_controlPoints.size() == m_selectables.size(), "curve instance mismatch");
ControlPoints::const_iterator p = m_controlPoints.begin();
for(Selectables::const_iterator i = m_selectables.begin(); i != m_selectables.end(); ++i, ++p)
{
if((*i).isSelected())
{
functor(*p);
}
}
return functor;
}
template<typename Functor>
const Functor& forEach(const Functor& functor) const
{
for(ControlPoints::const_iterator i = m_controlPoints.begin(); i != m_controlPoints.end(); ++i)
{
functor(*i);
}
return functor;
}
void testSelect(Selector& selector, SelectionTest& test)
{
ASSERT_MESSAGE(m_controlPoints.size() == m_selectables.size(), "curve instance mismatch");
ControlPoints::const_iterator p = m_controlPoints.begin();
for(Selectables::iterator i = m_selectables.begin(); i != m_selectables.end(); ++i, ++p)
{
ControlPoint_testSelect(*p, *i, selector, test);
}
}
bool isSelected() const
{
for(Selectables::const_iterator i = m_selectables.begin(); i != m_selectables.end(); ++i)
{
if((*i).isSelected())
{
return true;
}
}
return false;
}
void setSelected(bool selected)
{
for(Selectables::iterator i = m_selectables.begin(); i != m_selectables.end(); ++i)
{
(*i).setSelected(selected);
}
}
void write(const char* key, Entity& entity)
{
ControlPoints_write(m_controlPoints, key, entity);
}
void transform(const Matrix4& matrix)
{
forEachSelected(ControlPointTransform(matrix));
}
void snapto(float snap)
{
forEachSelected(ControlPointSnap(snap));
}
void updateSelected() const
{
m_selectedRender.clear();
forEachSelected(ControlPointAddSelected(m_selectedRender));
}
void renderComponents(Renderer& renderer, const VolumeTest& volume, const Matrix4& localToWorld) const
{
renderer.SetState(Type::instance().m_controlsShader, Renderer::eWireframeOnly);
renderer.SetState(Type::instance().m_controlsShader, Renderer::eFullMaterials);
renderer.addRenderable(m_controlsRender, localToWorld);
}
void renderComponentsSelected(Renderer& renderer, const VolumeTest& volume, const Matrix4& localToWorld) const
{
updateSelected();
if(!m_selectedRender.empty())
{
renderer.Highlight(Renderer::ePrimitive, false);
renderer.SetState(Type::instance().m_selectedShader, Renderer::eWireframeOnly);
renderer.SetState(Type::instance().m_selectedShader, Renderer::eFullMaterials);
renderer.addRenderable(m_selectedRender, localToWorld);
}
}
void curveChanged()
{
m_selectables.resize(m_controlPoints.size(), m_selectionChanged);
m_controlsRender.clear();
m_controlsRender.reserve(m_controlPoints.size());
forEach(ControlPointAdd(m_controlsRender));
m_selectedRender.reserve(m_controlPoints.size());
}
typedef MemberCaller<CurveEdit, &CurveEdit::curveChanged> CurveChangedCaller;
};
const int NURBS_degree = 3;
class NURBSCurve
{
typedef std::set<Callback> Callbacks;
Callbacks m_curveChanged;
Callback m_boundsChanged;
public:
ControlPoints m_controlPoints;
ControlPoints m_controlPointsTransformed;
NURBSWeights m_weights;
Knots m_knots;
RenderableCurve m_renderCurve;
AABB m_bounds;
NURBSCurve(const Callback& boundsChanged) : m_boundsChanged(boundsChanged)
{
}
void attach(const Callback& curveChanged)
{
m_curveChanged.insert(curveChanged);
curveChanged();
}
void detach(const Callback& curveChanged)
{
m_curveChanged.erase(curveChanged);
}
void notify()
{
std::for_each(m_curveChanged.begin(), m_curveChanged.end(), CallbackInvoke());
}
void tesselate()
{
if(!m_controlPointsTransformed.empty())
{
const std::size_t numSegments = (m_controlPointsTransformed.size() - 1) * 16;
m_renderCurve.m_vertices.resize(numSegments + 1);
m_renderCurve.m_vertices[0].vertex = vertex3f_for_vector3(m_controlPointsTransformed[0]);
for(std::size_t i = 1; i < numSegments; ++i)
{
m_renderCurve.m_vertices[i].vertex = vertex3f_for_vector3(NURBS_evaluate(m_controlPointsTransformed, m_weights, m_knots, NURBS_degree, (1.0 / double(numSegments)) * double(i)));
}
m_renderCurve.m_vertices[numSegments].vertex = vertex3f_for_vector3(m_controlPointsTransformed[m_controlPointsTransformed.size() - 1]);
}
else
{
m_renderCurve.m_vertices.clear();
}
}
void curveChanged()
{
tesselate();
m_bounds = AABB();
for(ControlPoints::iterator i = m_controlPointsTransformed.begin(); i != m_controlPointsTransformed.end(); ++i)
{
aabb_extend_by_point_safe(m_bounds, (*i));
}
m_boundsChanged();
notify();
}
bool parseCurve(const char* value)
{
if(!ControlPoints_parse(m_controlPoints, value))
{
return false;
}
m_weights.resize(m_controlPoints.size());
for(NURBSWeights::iterator i = m_weights.begin(); i != m_weights.end(); ++i)
{
(*i) = 1;
}
KnotVector_openUniform(m_knots, m_controlPoints.size(), NURBS_degree);
//plotBasisFunction(8, 0, NURBS_degree);
return true;
}
void curveChanged(const char* value)
{
if(string_empty(value) || !parseCurve(value))
{
m_controlPoints.resize(0);
m_knots.resize(0);
m_weights.resize(0);
}
m_controlPointsTransformed = m_controlPoints;
curveChanged();
}
typedef MemberCaller1<NURBSCurve, const char*, &NURBSCurve::curveChanged> CurveChangedCaller;
};
class CatmullRomSpline
{
typedef std::set<Callback> Callbacks;
Callbacks m_curveChanged;
Callback m_boundsChanged;
public:
ControlPoints m_controlPoints;
ControlPoints m_controlPointsTransformed;
RenderableCurve m_renderCurve;
AABB m_bounds;
CatmullRomSpline(const Callback& boundsChanged) : m_boundsChanged(boundsChanged)
{
}
void attach(const Callback& curveChanged)
{
m_curveChanged.insert(curveChanged);
curveChanged();
}
void detach(const Callback& curveChanged)
{
m_curveChanged.erase(curveChanged);
}
void notify()
{
std::for_each(m_curveChanged.begin(), m_curveChanged.end(), CallbackInvoke());
}
void tesselate()
{
if(!m_controlPointsTransformed.empty())
{
const std::size_t numSegments = (m_controlPointsTransformed.size() - 1) * 16;
m_renderCurve.m_vertices.resize(numSegments + 1);
m_renderCurve.m_vertices[0].vertex = vertex3f_for_vector3(m_controlPointsTransformed[0]);
for(std::size_t i = 1; i < numSegments; ++i)
{
m_renderCurve.m_vertices[i].vertex = vertex3f_for_vector3(CatmullRom_evaluate(m_controlPointsTransformed, (1.0 / double(numSegments)) * double(i)));
}
m_renderCurve.m_vertices[numSegments].vertex = vertex3f_for_vector3(m_controlPointsTransformed[m_controlPointsTransformed.size() - 1]);
}
else
{
m_renderCurve.m_vertices.clear();
}
}
bool parseCurve(const char* value)
{
return ControlPoints_parse(m_controlPoints, value);
}
void curveChanged()
{
tesselate();
m_bounds = AABB();
for(ControlPoints::iterator i = m_controlPointsTransformed.begin(); i != m_controlPointsTransformed.end(); ++i)
{
aabb_extend_by_point_safe(m_bounds, (*i));
}
m_boundsChanged();
notify();
}
void curveChanged(const char* value)
{
if(string_empty(value) || !parseCurve(value))
{
m_controlPoints.resize(0);
}
m_controlPointsTransformed = m_controlPoints;
curveChanged();
}
typedef MemberCaller1<CatmullRomSpline, const char*, &CatmullRomSpline::curveChanged> CurveChangedCaller;
};
const char* const curve_Nurbs = "curve_Nurbs";
const char* const curve_CatmullRomSpline = "curve_CatmullRomSpline";
#endif

View File

@@ -0,0 +1,809 @@
/*
Copyright (C) 2001-2006, William Joseph.
All Rights Reserved.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
///\file
///\brief Represents any Doom3 entity which does not have a fixed size specified in its entity-definition (e.g. func_static).
///
/// This entity behaves as a group only when the "model" key is empty or is the same as the "name" key. Otherwise it behaves as a model.
/// When behaving as a group, the "origin" key is the translation to be applied to all brushes (not patches) grouped under this entity.
/// When behaving as a model, the "origin", "angle" and "rotation" keys directly control the entity's local-to-parent transform.
/// When either the "curve_Nurbs" or "curve_CatmullRomSpline" keys define a curve, the curve is rendered and can be edited.
#include "doom3group.h"
#include "cullable.h"
#include "renderable.h"
#include "editable.h"
#include "modelskin.h"
#include "selectionlib.h"
#include "instancelib.h"
#include "transformlib.h"
#include "traverselib.h"
#include "entitylib.h"
#include "render.h"
#include "eclasslib.h"
#include "stream/stringstream.h"
#include "pivot.h"
#include "targetable.h"
#include "origin.h"
#include "angle.h"
#include "rotation.h"
#include "model.h"
#include "filters.h"
#include "namedentity.h"
#include "keyobservers.h"
#include "namekeys.h"
#include "curve.h"
#include "modelskinkey.h"
#include "entity.h"
inline void PointVertexArray_testSelect(PointVertex* first, std::size_t count, SelectionTest& test, SelectionIntersection& best)
{
test.TestLineStrip(
VertexPointer(
reinterpret_cast<VertexPointer::pointer>(&first->vertex),
sizeof(PointVertex)
),
IndexPointer::index_type(count),
best
);
}
class Doom3Group :
public Bounded,
public Snappable
{
EntityKeyValues m_entity;
KeyObserverMap m_keyObservers;
TraversableNodeSet m_traverse;
MatrixTransform m_transform;
SingletonModel m_model;
OriginKey m_originKey;
Vector3 m_origin;
RotationKey m_rotationKey;
Float9 m_rotation;
ClassnameFilter m_filter;
NamedEntity m_named;
NameKeys m_nameKeys;
TraversableObserverPairRelay m_traverseObservers;
Doom3GroupOrigin m_funcStaticOrigin;
RenderablePivot m_renderOrigin;
RenderableNamedEntity m_renderName;
ModelSkinKey m_skin;
public:
NURBSCurve m_curveNURBS;
CatmullRomSpline m_curveCatmullRom;
private:
mutable AABB m_curveBounds;
Callback m_transformChanged;
Callback m_evaluateTransform;
CopiedString m_name;
CopiedString m_modelKey;
bool m_isModel;
scene::Traversable* m_traversable;
void construct()
{
default_rotation(m_rotation);
m_keyObservers.insert("classname", ClassnameFilter::ClassnameChangedCaller(m_filter));
m_keyObservers.insert(Static<KeyIsName>::instance().m_nameKey, NamedEntity::IdentifierChangedCaller(m_named));
m_keyObservers.insert("model", Doom3Group::ModelChangedCaller(*this));
m_keyObservers.insert("origin", OriginKey::OriginChangedCaller(m_originKey));
m_keyObservers.insert("angle", RotationKey::AngleChangedCaller(m_rotationKey));
m_keyObservers.insert("rotation", RotationKey::RotationChangedCaller(m_rotationKey));
m_keyObservers.insert("name", NameChangedCaller(*this));
m_keyObservers.insert(curve_Nurbs, NURBSCurve::CurveChangedCaller(m_curveNURBS));
m_keyObservers.insert(curve_CatmullRomSpline, CatmullRomSpline::CurveChangedCaller(m_curveCatmullRom));
m_keyObservers.insert("skin", ModelSkinKey::SkinChangedCaller(m_skin));
m_traverseObservers.attach(m_funcStaticOrigin);
m_isModel = false;
m_nameKeys.setKeyIsName(keyIsNameDoom3Doom3Group);
attachTraverse();
m_entity.attach(m_keyObservers);
}
void destroy()
{
m_entity.detach(m_keyObservers);
if(isModel())
{
detachModel();
}
else
{
detachTraverse();
}
m_traverseObservers.detach(m_funcStaticOrigin);
}
void attachModel()
{
m_traversable = &m_model.getTraversable();
m_model.attach(&m_traverseObservers);
}
void detachModel()
{
m_traversable = 0;
m_model.detach(&m_traverseObservers);
}
void attachTraverse()
{
m_traversable = &m_traverse;
m_traverse.attach(&m_traverseObservers);
}
void detachTraverse()
{
m_traversable = 0;
m_traverse.detach(&m_traverseObservers);
}
bool isModel() const
{
return m_isModel;
}
void setIsModel(bool newValue)
{
if(newValue && !m_isModel)
{
detachTraverse();
attachModel();
m_nameKeys.setKeyIsName(Static<KeyIsName>::instance().m_keyIsName);
m_model.modelChanged(m_modelKey.c_str());
}
else if(!newValue && m_isModel)
{
detachModel();
attachTraverse();
m_nameKeys.setKeyIsName(keyIsNameDoom3Doom3Group);
}
m_isModel = newValue;
updateTransform();
}
void updateIsModel()
{
setIsModel(!string_empty(m_modelKey.c_str()) && !string_equal(m_modelKey.c_str(), m_name.c_str()));
}
void nameChanged(const char* value)
{
m_name = value;
updateIsModel();
}
typedef MemberCaller1<Doom3Group, const char*, &Doom3Group::nameChanged> NameChangedCaller;
void modelChanged(const char* value)
{
m_modelKey = value;
updateIsModel();
if(isModel())
{
m_model.modelChanged(value);
}
else
{
m_model.modelChanged("");
}
}
typedef MemberCaller1<Doom3Group, const char*, &Doom3Group::modelChanged> ModelChangedCaller;
void updateTransform()
{
m_transform.localToParent() = g_matrix4_identity;
if(isModel())
{
matrix4_translate_by_vec3(m_transform.localToParent(), m_originKey.m_origin);
matrix4_multiply_by_matrix4(m_transform.localToParent(), rotation_toMatrix(m_rotationKey.m_rotation));
}
m_transformChanged();
if(!isModel())
{
m_funcStaticOrigin.originChanged();
}
}
typedef MemberCaller<Doom3Group, &Doom3Group::updateTransform> UpdateTransformCaller;
void originChanged()
{
m_origin = m_originKey.m_origin;
updateTransform();
}
typedef MemberCaller<Doom3Group, &Doom3Group::originChanged> OriginChangedCaller;
void rotationChanged()
{
rotation_assign(m_rotation, m_rotationKey.m_rotation);
updateTransform();
}
typedef MemberCaller<Doom3Group, &Doom3Group::rotationChanged> RotationChangedCaller;
void skinChanged()
{
if(isModel())
{
scene::Node* node = m_model.getNode();
if(node != 0)
{
Node_modelSkinChanged(*node);
}
}
}
typedef MemberCaller<Doom3Group, &Doom3Group::skinChanged> SkinChangedCaller;
public:
Doom3Group(EntityClass* eclass, scene::Node& node, const Callback& transformChanged, const Callback& boundsChanged, const Callback& evaluateTransform) :
m_entity(eclass),
m_originKey(OriginChangedCaller(*this)),
m_origin(ORIGINKEY_IDENTITY),
m_rotationKey(RotationChangedCaller(*this)),
m_filter(m_entity, node),
m_named(m_entity),
m_nameKeys(m_entity),
m_funcStaticOrigin(m_traverse, m_origin),
m_renderName(m_named, g_vector3_identity),
m_skin(SkinChangedCaller(*this)),
m_curveNURBS(boundsChanged),
m_curveCatmullRom(boundsChanged),
m_transformChanged(transformChanged),
m_evaluateTransform(evaluateTransform),
m_traversable(0)
{
construct();
}
Doom3Group(const Doom3Group& other, scene::Node& node, const Callback& transformChanged, const Callback& boundsChanged, const Callback& evaluateTransform) :
m_entity(other.m_entity),
m_originKey(OriginChangedCaller(*this)),
m_origin(ORIGINKEY_IDENTITY),
m_rotationKey(RotationChangedCaller(*this)),
m_filter(m_entity, node),
m_named(m_entity),
m_nameKeys(m_entity),
m_funcStaticOrigin(m_traverse, m_origin),
m_renderName(m_named, g_vector3_identity),
m_skin(SkinChangedCaller(*this)),
m_curveNURBS(boundsChanged),
m_curveCatmullRom(boundsChanged),
m_transformChanged(transformChanged),
m_evaluateTransform(evaluateTransform),
m_traversable(0)
{
construct();
}
~Doom3Group()
{
destroy();
}
InstanceCounter m_instanceCounter;
void instanceAttach(const scene::Path& path)
{
if(++m_instanceCounter.m_count == 1)
{
m_filter.instanceAttach();
m_entity.instanceAttach(path_find_mapfile(path.begin(), path.end()));
m_traverse.instanceAttach(path_find_mapfile(path.begin(), path.end()));
m_funcStaticOrigin.enable();
}
}
void instanceDetach(const scene::Path& path)
{
if(--m_instanceCounter.m_count == 0)
{
m_funcStaticOrigin.disable();
m_traverse.instanceDetach(path_find_mapfile(path.begin(), path.end()));
m_entity.instanceDetach(path_find_mapfile(path.begin(), path.end()));
m_filter.instanceDetach();
}
}
EntityKeyValues& getEntity()
{
return m_entity;
}
const EntityKeyValues& getEntity() const
{
return m_entity;
}
scene::Traversable& getTraversable()
{
return *m_traversable;
}
Namespaced& getNamespaced()
{
return m_nameKeys;
}
Nameable& getNameable()
{
return m_named;
}
TransformNode& getTransformNode()
{
return m_transform;
}
ModelSkin& getModelSkin()
{
return m_skin.get();
}
void attach(scene::Traversable::Observer* observer)
{
m_traverseObservers.attach(*observer);
}
void detach(scene::Traversable::Observer* observer)
{
m_traverseObservers.detach(*observer);
}
const AABB& localAABB() const
{
m_curveBounds = m_curveNURBS.m_bounds;
aabb_extend_by_aabb_safe(m_curveBounds, m_curveCatmullRom.m_bounds);
return m_curveBounds;
}
void renderSolid(Renderer& renderer, const VolumeTest& volume, const Matrix4& localToWorld, bool selected) const
{
if(isModel() && selected)
{
m_renderOrigin.render(renderer, volume, localToWorld);
}
renderer.SetState(m_entity.getEntityClass().m_state_wire, Renderer::eWireframeOnly);
renderer.SetState(m_entity.getEntityClass().m_state_wire, Renderer::eFullMaterials);
if(!m_curveNURBS.m_renderCurve.m_vertices.empty())
{
renderer.addRenderable(m_curveNURBS.m_renderCurve, localToWorld);
}
if(!m_curveCatmullRom.m_renderCurve.m_vertices.empty())
{
renderer.addRenderable(m_curveCatmullRom.m_renderCurve, localToWorld);
}
}
void renderWireframe(Renderer& renderer, const VolumeTest& volume, const Matrix4& localToWorld, bool selected) const
{
renderSolid(renderer, volume, localToWorld, selected);
if(g_showNames && isModel())
{
renderer.addRenderable(m_renderName, localToWorld);
}
}
void testSelect(Selector& selector, SelectionTest& test, SelectionIntersection& best)
{
PointVertexArray_testSelect(&m_curveNURBS.m_renderCurve.m_vertices[0], m_curveNURBS.m_renderCurve.m_vertices.size(), test, best);
PointVertexArray_testSelect(&m_curveCatmullRom.m_renderCurve.m_vertices[0], m_curveCatmullRom.m_renderCurve.m_vertices.size(), test, best);
}
void translate(const Vector3& translation)
{
m_origin = origin_translated(m_origin, translation);
}
void rotate(const Quaternion& rotation)
{
rotation_rotate(m_rotation, rotation);
}
void snapto(float snap)
{
m_originKey.m_origin = origin_snapped(m_originKey.m_origin, snap);
m_originKey.write(&m_entity);
}
void revertTransform()
{
m_origin = m_originKey.m_origin;
rotation_assign(m_rotation, m_rotationKey.m_rotation);
m_curveNURBS.m_controlPointsTransformed = m_curveNURBS.m_controlPoints;
m_curveCatmullRom.m_controlPointsTransformed = m_curveCatmullRom.m_controlPoints;
}
void freezeTransform()
{
m_originKey.m_origin = m_origin;
m_originKey.write(&m_entity);
rotation_assign(m_rotationKey.m_rotation, m_rotation);
m_rotationKey.write(&m_entity);
m_curveNURBS.m_controlPoints = m_curveNURBS.m_controlPointsTransformed;
ControlPoints_write(m_curveNURBS.m_controlPoints, curve_Nurbs, m_entity);
m_curveCatmullRom.m_controlPoints = m_curveCatmullRom.m_controlPointsTransformed;
ControlPoints_write(m_curveCatmullRom.m_controlPoints, curve_CatmullRomSpline, m_entity);
}
void transformChanged()
{
revertTransform();
m_evaluateTransform();
updateTransform();
m_curveNURBS.curveChanged();
m_curveCatmullRom.curveChanged();
}
typedef MemberCaller<Doom3Group, &Doom3Group::transformChanged> TransformChangedCaller;
};
class ControlPointAddBounds
{
AABB& m_bounds;
public:
ControlPointAddBounds(AABB& bounds) : m_bounds(bounds)
{
}
void operator()(const Vector3& point) const
{
aabb_extend_by_point_safe(m_bounds, point);
}
};
class Doom3GroupInstance :
public TargetableInstance,
public TransformModifier,
public Renderable,
public SelectionTestable,
public ComponentSelectionTestable,
public ComponentEditable,
public ComponentSnappable
{
class TypeCasts
{
InstanceTypeCastTable m_casts;
public:
TypeCasts()
{
m_casts = TargetableInstance::StaticTypeCasts::instance().get();
InstanceContainedCast<Doom3GroupInstance, Bounded>::install(m_casts);
InstanceStaticCast<Doom3GroupInstance, Renderable>::install(m_casts);
InstanceStaticCast<Doom3GroupInstance, SelectionTestable>::install(m_casts);
InstanceStaticCast<Doom3GroupInstance, ComponentSelectionTestable>::install(m_casts);
InstanceStaticCast<Doom3GroupInstance, ComponentEditable>::install(m_casts);
InstanceStaticCast<Doom3GroupInstance, ComponentSnappable>::install(m_casts);
InstanceStaticCast<Doom3GroupInstance, Transformable>::install(m_casts);
InstanceIdentityCast<Doom3GroupInstance>::install(m_casts);
}
InstanceTypeCastTable& get()
{
return m_casts;
}
};
Doom3Group& m_contained;
CurveEdit m_curveNURBS;
CurveEdit m_curveCatmullRom;
mutable AABB m_aabb_component;
public:
typedef LazyStatic<TypeCasts> StaticTypeCasts;
Bounded& get(NullType<Bounded>)
{
return m_contained;
}
STRING_CONSTANT(Name, "Doom3GroupInstance");
Doom3GroupInstance(const scene::Path& path, scene::Instance* parent, Doom3Group& contained) :
TargetableInstance(path, parent, this, StaticTypeCasts::instance().get(), contained.getEntity(), *this),
TransformModifier(Doom3Group::TransformChangedCaller(contained), ApplyTransformCaller(*this)),
m_contained(contained),
m_curveNURBS(m_contained.m_curveNURBS.m_controlPointsTransformed, SelectionChangedComponentCaller(*this)),
m_curveCatmullRom(m_contained.m_curveCatmullRom.m_controlPointsTransformed, SelectionChangedComponentCaller(*this))
{
m_contained.instanceAttach(Instance::path());
m_contained.m_curveNURBS.attach(CurveEdit::CurveChangedCaller(m_curveNURBS));
m_contained.m_curveCatmullRom.attach(CurveEdit::CurveChangedCaller(m_curveCatmullRom));
StaticRenderableConnectionLines::instance().attach(*this);
}
~Doom3GroupInstance()
{
StaticRenderableConnectionLines::instance().detach(*this);
m_contained.m_curveCatmullRom.detach(CurveEdit::CurveChangedCaller(m_curveCatmullRom));
m_contained.m_curveNURBS.detach(CurveEdit::CurveChangedCaller(m_curveNURBS));
m_contained.instanceDetach(Instance::path());
}
void renderSolid(Renderer& renderer, const VolumeTest& volume) const
{
m_contained.renderSolid(renderer, volume, Instance::localToWorld(), getSelectable().isSelected());
m_curveNURBS.renderComponentsSelected(renderer, volume, localToWorld());
m_curveCatmullRom.renderComponentsSelected(renderer, volume, localToWorld());
}
void renderWireframe(Renderer& renderer, const VolumeTest& volume) const
{
m_contained.renderWireframe(renderer, volume, Instance::localToWorld(), getSelectable().isSelected());
m_curveNURBS.renderComponentsSelected(renderer, volume, localToWorld());
m_curveCatmullRom.renderComponentsSelected(renderer, volume, localToWorld());
}
void renderComponents(Renderer& renderer, const VolumeTest& volume) const
{
if(GlobalSelectionSystem().ComponentMode() == SelectionSystem::eVertex)
{
m_curveNURBS.renderComponents(renderer, volume, localToWorld());
m_curveCatmullRom.renderComponents(renderer, volume, localToWorld());
}
}
void testSelect(Selector& selector, SelectionTest& test)
{
test.BeginMesh(localToWorld());
SelectionIntersection best;
m_contained.testSelect(selector, test, best);
if(best.valid())
{
Selector_add(selector, getSelectable(), best);
}
}
bool isSelectedComponents() const
{
return m_curveNURBS.isSelected() || m_curveCatmullRom.isSelected();
}
void setSelectedComponents(bool selected, SelectionSystem::EComponentMode mode)
{
if(mode == SelectionSystem::eVertex)
{
m_curveNURBS.setSelected(selected);
m_curveCatmullRom.setSelected(selected);
}
}
void testSelectComponents(Selector& selector, SelectionTest& test, SelectionSystem::EComponentMode mode)
{
if(mode == SelectionSystem::eVertex)
{
test.BeginMesh(localToWorld());
m_curveNURBS.testSelect(selector, test);
m_curveCatmullRom.testSelect(selector, test);
}
}
void transformComponents(const Matrix4& matrix)
{
if(m_curveNURBS.isSelected())
{
m_curveNURBS.transform(matrix);
}
if(m_curveCatmullRom.isSelected())
{
m_curveCatmullRom.transform(matrix);
}
}
const AABB& getSelectedComponentsBounds() const
{
m_aabb_component = AABB();
m_curveNURBS.forEachSelected(ControlPointAddBounds(m_aabb_component));
m_curveCatmullRom.forEachSelected(ControlPointAddBounds(m_aabb_component));
return m_aabb_component;
}
void snapComponents(float snap)
{
if(m_curveNURBS.isSelected())
{
m_curveNURBS.snapto(snap);
m_curveNURBS.write(curve_Nurbs, m_contained.getEntity());
}
if(m_curveCatmullRom.isSelected())
{
m_curveCatmullRom.snapto(snap);
m_curveCatmullRom.write(curve_CatmullRomSpline, m_contained.getEntity());
}
}
void evaluateTransform()
{
if(getType() == TRANSFORM_PRIMITIVE)
{
m_contained.translate(getTranslation());
m_contained.rotate(getRotation());
}
else
{
transformComponents(calculateTransform());
}
}
void applyTransform()
{
m_contained.revertTransform();
evaluateTransform();
m_contained.freezeTransform();
}
typedef MemberCaller<Doom3GroupInstance, &Doom3GroupInstance::applyTransform> ApplyTransformCaller;
void selectionChangedComponent(const Selectable& selectable)
{
GlobalSelectionSystem().getObserver(SelectionSystem::eComponent)(selectable);
GlobalSelectionSystem().onComponentSelection(*this, selectable);
}
typedef MemberCaller1<Doom3GroupInstance, const Selectable&, &Doom3GroupInstance::selectionChangedComponent> SelectionChangedComponentCaller;
};
class Doom3GroupNode :
public scene::Node::Symbiot,
public scene::Instantiable,
public scene::Cloneable,
public scene::Traversable::Observer
{
class TypeCasts
{
NodeTypeCastTable m_casts;
public:
TypeCasts()
{
NodeStaticCast<Doom3GroupNode, scene::Instantiable>::install(m_casts);
NodeStaticCast<Doom3GroupNode, scene::Cloneable>::install(m_casts);
NodeContainedCast<Doom3GroupNode, scene::Traversable>::install(m_casts);
NodeContainedCast<Doom3GroupNode, Snappable>::install(m_casts);
NodeContainedCast<Doom3GroupNode, TransformNode>::install(m_casts);
NodeContainedCast<Doom3GroupNode, Entity>::install(m_casts);
NodeContainedCast<Doom3GroupNode, Nameable>::install(m_casts);
NodeContainedCast<Doom3GroupNode, Namespaced>::install(m_casts);
NodeContainedCast<Doom3GroupNode, ModelSkin>::install(m_casts);
}
NodeTypeCastTable& get()
{
return m_casts;
}
};
scene::Node m_node;
InstanceSet m_instances;
Doom3Group m_contained;
void construct()
{
m_contained.attach(this);
}
void destroy()
{
m_contained.detach(this);
}
public:
typedef LazyStatic<TypeCasts> StaticTypeCasts;
scene::Traversable& get(NullType<scene::Traversable>)
{
return m_contained.getTraversable();
}
Snappable& get(NullType<Snappable>)
{
return m_contained;
}
TransformNode& get(NullType<TransformNode>)
{
return m_contained.getTransformNode();
}
Entity& get(NullType<Entity>)
{
return m_contained.getEntity();
}
Nameable& get(NullType<Nameable>)
{
return m_contained.getNameable();
}
Namespaced& get(NullType<Namespaced>)
{
return m_contained.getNamespaced();
}
ModelSkin& get(NullType<ModelSkin>)
{
return m_contained.getModelSkin();
}
Doom3GroupNode(EntityClass* eclass) :
m_node(this, this, StaticTypeCasts::instance().get()),
m_contained(eclass, m_node, InstanceSet::TransformChangedCaller(m_instances), InstanceSet::BoundsChangedCaller(m_instances), InstanceSetEvaluateTransform<Doom3GroupInstance>::Caller(m_instances))
{
construct();
}
Doom3GroupNode(const Doom3GroupNode& other) :
scene::Node::Symbiot(other),
scene::Instantiable(other),
scene::Cloneable(other),
scene::Traversable::Observer(other),
m_node(this, this, StaticTypeCasts::instance().get()),
m_contained(other.m_contained, m_node, InstanceSet::TransformChangedCaller(m_instances), InstanceSet::BoundsChangedCaller(m_instances), InstanceSetEvaluateTransform<Doom3GroupInstance>::Caller(m_instances))
{
construct();
}
~Doom3GroupNode()
{
destroy();
}
void release()
{
delete this;
}
scene::Node& node()
{
return m_node;
}
scene::Node& clone() const
{
return (new Doom3GroupNode(*this))->node();
}
void insert(scene::Node& child)
{
m_instances.insert(child);
}
void erase(scene::Node& child)
{
m_instances.erase(child);
}
scene::Instance* create(const scene::Path& path, scene::Instance* parent)
{
return new Doom3GroupInstance(path, parent, m_contained);
}
void forEachInstance(const scene::Instantiable::Visitor& visitor)
{
m_instances.forEachInstance(visitor);
}
void insert(scene::Instantiable::Observer* observer, const scene::Path& path, scene::Instance* instance)
{
m_instances.insert(observer, path, instance);
}
scene::Instance* erase(scene::Instantiable::Observer* observer, const scene::Path& path)
{
return m_instances.erase(observer, path);
}
};
void Doom3Group_construct()
{
CurveEdit::Type::instance().m_controlsShader = GlobalShaderCache().capture("$POINT");
CurveEdit::Type::instance().m_selectedShader = GlobalShaderCache().capture("$SELPOINT");
}
void Doom3Group_destroy()
{
GlobalShaderCache().release("$SELPOINT");
GlobalShaderCache().release("$POINT");
}
scene::Node& New_Doom3Group(EntityClass* eclass)
{
return (new Doom3GroupNode(eclass))->node();
}

View File

@@ -0,0 +1,35 @@
/*
Copyright (C) 2001-2006, William Joseph.
All Rights Reserved.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#if !defined(INCLUDED_STATIC_H)
#define INCLUDED_STATIC_H
namespace scene
{
class Node;
}
class EntityClass;
void Doom3Group_construct();
void Doom3Group_destroy();
scene::Node& New_Doom3Group(EntityClass* eclass);
#endif

View File

@@ -0,0 +1,526 @@
/*
Copyright (C) 2001-2006, William Joseph.
All Rights Reserved.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
///\file
///\brief Represents any entity which has a fixed size specified in its entity-definition and displays a model (e.g. ammo_bfg).
///
/// This entity displays the model specified in its entity-definition.
/// The "origin" and "angle" keys directly control the entity's local-to-parent transform.
/// The "rotation" key directly controls the entity's local-to-parent transform for Doom3 only.
#include "eclassmodel.h"
#include "cullable.h"
#include "renderable.h"
#include "editable.h"
#include "selectionlib.h"
#include "instancelib.h"
#include "transformlib.h"
#include "traverselib.h"
#include "entitylib.h"
#include "render.h"
#include "eclasslib.h"
#include "pivot.h"
#include "targetable.h"
#include "origin.h"
#include "angle.h"
#include "rotation.h"
#include "model.h"
#include "filters.h"
#include "namedentity.h"
#include "keyobservers.h"
#include "namekeys.h"
#include "modelskinkey.h"
#include "entity.h"
class EclassModel :
public Snappable
{
MatrixTransform m_transform;
EntityKeyValues m_entity;
KeyObserverMap m_keyObservers;
OriginKey m_originKey;
Vector3 m_origin;
AngleKey m_angleKey;
float m_angle;
RotationKey m_rotationKey;
Float9 m_rotation;
SingletonModel m_model;
ClassnameFilter m_filter;
NamedEntity m_named;
NameKeys m_nameKeys;
RenderablePivot m_renderOrigin;
RenderableNamedEntity m_renderName;
ModelSkinKey m_skin;
Callback m_transformChanged;
Callback m_evaluateTransform;
void construct()
{
default_rotation(m_rotation);
m_keyObservers.insert("classname", ClassnameFilter::ClassnameChangedCaller(m_filter));
m_keyObservers.insert(Static<KeyIsName>::instance().m_nameKey, NamedEntity::IdentifierChangedCaller(m_named));
if(g_gameType == eGameTypeDoom3)
{
m_keyObservers.insert("angle", RotationKey::AngleChangedCaller(m_rotationKey));
m_keyObservers.insert("rotation", RotationKey::RotationChangedCaller(m_rotationKey));
}
else
{
m_keyObservers.insert("angle", AngleKey::AngleChangedCaller(m_angleKey));
}
m_keyObservers.insert("origin", OriginKey::OriginChangedCaller(m_originKey));
}
void updateTransform()
{
m_transform.localToParent() = g_matrix4_identity;
matrix4_translate_by_vec3(m_transform.localToParent(), m_origin);
if(g_gameType == eGameTypeDoom3)
{
matrix4_multiply_by_matrix4(m_transform.localToParent(), rotation_toMatrix(m_rotation));
}
else
{
matrix4_multiply_by_matrix4(m_transform.localToParent(), matrix4_rotation_for_z_degrees(m_angle));
}
m_transformChanged();
}
typedef MemberCaller<EclassModel, &EclassModel::updateTransform> UpdateTransformCaller;
void originChanged()
{
m_origin = m_originKey.m_origin;
updateTransform();
}
typedef MemberCaller<EclassModel, &EclassModel::originChanged> OriginChangedCaller;
void angleChanged()
{
m_angle = m_angleKey.m_angle;
updateTransform();
}
typedef MemberCaller<EclassModel, &EclassModel::angleChanged> AngleChangedCaller;
void rotationChanged()
{
rotation_assign(m_rotation, m_rotationKey.m_rotation);
updateTransform();
}
typedef MemberCaller<EclassModel, &EclassModel::rotationChanged> RotationChangedCaller;
void skinChanged()
{
scene::Node* node = m_model.getNode();
if(node != 0)
{
Node_modelSkinChanged(*node);
}
}
typedef MemberCaller<EclassModel, &EclassModel::skinChanged> SkinChangedCaller;
public:
EclassModel(EntityClass* eclass, scene::Node& node, const Callback& transformChanged, const Callback& evaluateTransform) :
m_entity(eclass),
m_originKey(OriginChangedCaller(*this)),
m_origin(ORIGINKEY_IDENTITY),
m_angleKey(AngleChangedCaller(*this)),
m_angle(ANGLEKEY_IDENTITY),
m_rotationKey(RotationChangedCaller(*this)),
m_filter(m_entity, node),
m_named(m_entity),
m_nameKeys(m_entity),
m_renderName(m_named, g_vector3_identity),
m_skin(SkinChangedCaller(*this)),
m_transformChanged(transformChanged),
m_evaluateTransform(evaluateTransform)
{
construct();
}
EclassModel(const EclassModel& other, scene::Node& node, const Callback& transformChanged, const Callback& evaluateTransform) :
m_entity(other.m_entity),
m_originKey(OriginChangedCaller(*this)),
m_origin(ORIGINKEY_IDENTITY),
m_angleKey(AngleChangedCaller(*this)),
m_angle(ANGLEKEY_IDENTITY),
m_rotationKey(RotationChangedCaller(*this)),
m_filter(m_entity, node),
m_named(m_entity),
m_nameKeys(m_entity),
m_renderName(m_named, g_vector3_identity),
m_skin(SkinChangedCaller(*this)),
m_transformChanged(transformChanged),
m_evaluateTransform(evaluateTransform)
{
construct();
}
InstanceCounter m_instanceCounter;
void instanceAttach(const scene::Path& path)
{
if(++m_instanceCounter.m_count == 1)
{
m_filter.instanceAttach();
m_entity.instanceAttach(path_find_mapfile(path.begin(), path.end()));
m_entity.attach(m_keyObservers);
m_model.modelChanged(m_entity.getEntityClass().modelpath());
m_skin.skinChanged(m_entity.getEntityClass().skin());
}
}
void instanceDetach(const scene::Path& path)
{
if(--m_instanceCounter.m_count == 0)
{
m_skin.skinChanged("");
m_model.modelChanged("");
m_entity.detach(m_keyObservers);
m_entity.instanceDetach(path_find_mapfile(path.begin(), path.end()));
m_filter.instanceDetach();
}
}
EntityKeyValues& getEntity()
{
return m_entity;
}
const EntityKeyValues& getEntity() const
{
return m_entity;
}
scene::Traversable& getTraversable()
{
return m_model.getTraversable();
}
Namespaced& getNamespaced()
{
return m_nameKeys;
}
Nameable& getNameable()
{
return m_named;
}
TransformNode& getTransformNode()
{
return m_transform;
}
ModelSkin& getModelSkin()
{
return m_skin.get();
}
void attach(scene::Traversable::Observer* observer)
{
m_model.attach(observer);
}
void detach(scene::Traversable::Observer* observer)
{
m_model.detach(observer);
}
void renderSolid(Renderer& renderer, const VolumeTest& volume, const Matrix4& localToWorld, bool selected) const
{
if(selected)
{
m_renderOrigin.render(renderer, volume, localToWorld);
}
renderer.SetState(m_entity.getEntityClass().m_state_wire, Renderer::eWireframeOnly);
}
void renderWireframe(Renderer& renderer, const VolumeTest& volume, const Matrix4& localToWorld, bool selected) const
{
renderSolid(renderer, volume, localToWorld, selected);
if(g_showNames)
{
renderer.addRenderable(m_renderName, localToWorld);
}
}
void translate(const Vector3& translation)
{
m_origin = origin_translated(m_origin, translation);
}
void rotate(const Quaternion& rotation)
{
if(g_gameType == eGameTypeDoom3)
{
rotation_rotate(m_rotation, rotation);
}
else
{
m_angle = angle_rotated(m_angle, rotation);
}
}
void snapto(float snap)
{
m_originKey.m_origin = origin_snapped(m_originKey.m_origin, snap);
m_originKey.write(&m_entity);
}
void revertTransform()
{
m_origin = m_originKey.m_origin;
if(g_gameType == eGameTypeDoom3)
{
rotation_assign(m_rotation, m_rotationKey.m_rotation);
}
else
{
m_angle = m_angleKey.m_angle;
}
}
void freezeTransform()
{
m_originKey.m_origin = m_origin;
m_originKey.write(&m_entity);
if(g_gameType == eGameTypeDoom3)
{
rotation_assign(m_rotationKey.m_rotation, m_rotation);
m_rotationKey.write(&m_entity);
}
else
{
m_angleKey.m_angle = m_angle;
m_angleKey.write(&m_entity);
}
}
void transformChanged()
{
revertTransform();
m_evaluateTransform();
updateTransform();
}
typedef MemberCaller<EclassModel, &EclassModel::transformChanged> TransformChangedCaller;
};
class EclassModelInstance : public TargetableInstance, public TransformModifier, public Renderable
{
class TypeCasts
{
InstanceTypeCastTable m_casts;
public:
TypeCasts()
{
m_casts = TargetableInstance::StaticTypeCasts::instance().get();
InstanceStaticCast<EclassModelInstance, Renderable>::install(m_casts);
InstanceStaticCast<EclassModelInstance, Transformable>::install(m_casts);
InstanceIdentityCast<EclassModelInstance>::install(m_casts);
}
InstanceTypeCastTable& get()
{
return m_casts;
}
};
EclassModel& m_contained;
public:
typedef LazyStatic<TypeCasts> StaticTypeCasts;
STRING_CONSTANT(Name, "EclassModelInstance");
EclassModelInstance(const scene::Path& path, scene::Instance* parent, EclassModel& contained) :
TargetableInstance(path, parent, this, StaticTypeCasts::instance().get(), contained.getEntity(), *this),
TransformModifier(EclassModel::TransformChangedCaller(contained), ApplyTransformCaller(*this)),
m_contained(contained)
{
m_contained.instanceAttach(Instance::path());
StaticRenderableConnectionLines::instance().attach(*this);
}
~EclassModelInstance()
{
StaticRenderableConnectionLines::instance().detach(*this);
m_contained.instanceDetach(Instance::path());
}
void renderSolid(Renderer& renderer, const VolumeTest& volume) const
{
m_contained.renderSolid(renderer, volume, Instance::localToWorld(), getSelectable().isSelected());
}
void renderWireframe(Renderer& renderer, const VolumeTest& volume) const
{
m_contained.renderWireframe(renderer, volume, Instance::localToWorld(), getSelectable().isSelected());
}
void evaluateTransform()
{
if(getType() == TRANSFORM_PRIMITIVE)
{
m_contained.translate(getTranslation());
m_contained.rotate(getRotation());
}
}
void applyTransform()
{
m_contained.revertTransform();
evaluateTransform();
m_contained.freezeTransform();
}
typedef MemberCaller<EclassModelInstance, &EclassModelInstance::applyTransform> ApplyTransformCaller;
};
class EclassModelNode :
public scene::Node::Symbiot,
public scene::Instantiable,
public scene::Cloneable,
public scene::Traversable::Observer
{
class TypeCasts
{
NodeTypeCastTable m_casts;
public:
TypeCasts()
{
NodeStaticCast<EclassModelNode, scene::Instantiable>::install(m_casts);
NodeStaticCast<EclassModelNode, scene::Cloneable>::install(m_casts);
NodeContainedCast<EclassModelNode, scene::Traversable>::install(m_casts);
NodeContainedCast<EclassModelNode, Snappable>::install(m_casts);
NodeContainedCast<EclassModelNode, TransformNode>::install(m_casts);
NodeContainedCast<EclassModelNode, Entity>::install(m_casts);
NodeContainedCast<EclassModelNode, Nameable>::install(m_casts);
NodeContainedCast<EclassModelNode, Namespaced>::install(m_casts);
NodeContainedCast<EclassModelNode, ModelSkin>::install(m_casts);
}
NodeTypeCastTable& get()
{
return m_casts;
}
};
scene::Node m_node;
InstanceSet m_instances;
EclassModel m_contained;
void construct()
{
m_contained.attach(this);
}
void destroy()
{
m_contained.detach(this);
}
public:
typedef LazyStatic<TypeCasts> StaticTypeCasts;
scene::Traversable& get(NullType<scene::Traversable>)
{
return m_contained.getTraversable();
}
Snappable& get(NullType<Snappable>)
{
return m_contained;
}
TransformNode& get(NullType<TransformNode>)
{
return m_contained.getTransformNode();
}
Entity& get(NullType<Entity>)
{
return m_contained.getEntity();
}
Nameable& get(NullType<Nameable>)
{
return m_contained.getNameable();
}
Namespaced& get(NullType<Namespaced>)
{
return m_contained.getNamespaced();
}
ModelSkin& get(NullType<ModelSkin>)
{
return m_contained.getModelSkin();
}
EclassModelNode(EntityClass* eclass) :
m_node(this, this, StaticTypeCasts::instance().get()),
m_contained(eclass, m_node, InstanceSet::TransformChangedCaller(m_instances), InstanceSetEvaluateTransform<EclassModelInstance>::Caller(m_instances))
{
construct();
}
EclassModelNode(const EclassModelNode& other) :
scene::Node::Symbiot(other),
scene::Instantiable(other),
scene::Cloneable(other),
scene::Traversable::Observer(other),
m_node(this, this, StaticTypeCasts::instance().get()),
m_contained(other.m_contained, m_node, InstanceSet::TransformChangedCaller(m_instances), InstanceSetEvaluateTransform<EclassModelInstance>::Caller(m_instances))
{
construct();
}
~EclassModelNode()
{
destroy();
}
void release()
{
delete this;
}
scene::Node& node()
{
return m_node;
}
void insert(scene::Node& child)
{
m_instances.insert(child);
}
void erase(scene::Node& child)
{
m_instances.erase(child);
}
scene::Node& clone() const
{
return (new EclassModelNode(*this))->node();
}
scene::Instance* create(const scene::Path& path, scene::Instance* parent)
{
return new EclassModelInstance(path, parent, m_contained);
}
void forEachInstance(const scene::Instantiable::Visitor& visitor)
{
m_instances.forEachInstance(visitor);
}
void insert(scene::Instantiable::Observer* observer, const scene::Path& path, scene::Instance* instance)
{
m_instances.insert(observer, path, instance);
}
scene::Instance* erase(scene::Instantiable::Observer* observer, const scene::Path& path)
{
return m_instances.erase(observer, path);
}
};
scene::Node& New_EclassModel(EntityClass* eclass)
{
return (new EclassModelNode(eclass))->node();
}

View File

@@ -0,0 +1,35 @@
/*
Copyright (C) 2001-2006, William Joseph.
All Rights Reserved.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#if !defined(INCLUDED_ECLASSMODEL_H)
#define INCLUDED_ECLASSMODEL_H
namespace scene
{
class Node;
};
class EntityClass;
scene::Node& New_EclassModel(EntityClass* eclass);
#include "entity.h"
#endif

383
plugins/entity/entity.cpp Normal file
View File

@@ -0,0 +1,383 @@
/*
Copyright (C) 2001-2006, William Joseph.
All Rights Reserved.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "entity.h"
#include "ifilter.h"
#include "selectable.h"
#include "namespace.h"
#include "scenelib.h"
#include "entitylib.h"
#include "eclasslib.h"
#include "pivot.h"
#include "targetable.h"
#include "uniquenames.h"
#include "namekeys.h"
#include "stream/stringstream.h"
#include "filters.h"
#include "miscmodel.h"
#include "light.h"
#include "group.h"
#include "eclassmodel.h"
#include "generic.h"
#include "doom3group.h"
EGameType g_gameType;
inline scene::Node& entity_for_eclass(EntityClass* eclass)
{
if(classname_equal(eclass->name(), "misc_model")
|| classname_equal(eclass->name(), "misc_gamemodel")
|| classname_equal(eclass->name(), "model_static"))
{
return New_MiscModel(eclass);
}
else if(classname_equal(eclass->name(), "light")
|| classname_equal(eclass->name(), "lightJunior"))
{
return New_Light(eclass);
}
if(!eclass->fixedsize)
{
if(g_gameType == eGameTypeDoom3)
{
return New_Doom3Group(eclass);
}
else
{
return New_Group(eclass);
}
}
else if(!string_empty(eclass->modelpath()))
{
return New_EclassModel(eclass);
}
else
{
return New_GenericEntity(eclass);
}
}
void Entity_setName(Entity& entity, const char* name)
{
entity.setKeyValue("name", name);
}
typedef ReferenceCaller1<Entity, const char*, Entity_setName> EntitySetNameCaller;
inline Namespaced* Node_getNamespaced(scene::Node& node)
{
return NodeTypeCast<Namespaced>::cast(node);
}
inline scene::Node& node_for_eclass(EntityClass* eclass)
{
scene::Node& node = entity_for_eclass(eclass);
Node_getEntity(node)->setKeyValue("classname", eclass->name());
if(g_gameType == eGameTypeDoom3
&& string_not_empty(eclass->name())
&& !string_equal(eclass->name(), "worldspawn")
&& !string_equal(eclass->name(), "UNKNOWN_CLASS"))
{
char buffer[1024];
strcpy(buffer, eclass->name());
strcat(buffer, "_1");
GlobalNamespace().makeUnique(buffer, EntitySetNameCaller(*Node_getEntity(node)));
}
Namespaced* namespaced = Node_getNamespaced(node);
if(namespaced != 0)
{
namespaced->setNamespace(GlobalNamespace());
}
return node;
}
EntityCreator::KeyValueChangedFunc EntityKeyValues::m_entityKeyValueChanged = 0;
EntityCreator::KeyValueChangedFunc KeyValue::m_entityKeyValueChanged = 0;
Counter* EntityKeyValues::m_counter = 0;
bool g_showNames = true;
bool g_showAngles = true;
bool g_newLightDraw = true;
bool g_lightRadii = false;
class ConnectEntities
{
public:
Entity* m_e1;
Entity* m_e2;
ConnectEntities(Entity* e1, Entity* e2) : m_e1(e1), m_e2(e2)
{
}
void connect(const char* name)
{
m_e1->setKeyValue("target", name);
m_e2->setKeyValue("targetname", name);
}
typedef MemberCaller1<ConnectEntities, const char*, &ConnectEntities::connect> ConnectCaller;
};
inline Entity* ScenePath_getEntity(const scene::Path& path)
{
Entity* entity = Node_getEntity(path.top());
if(entity == 0)
{
entity = Node_getEntity(path.parent());
}
return entity;
}
class Quake3EntityCreator : public EntityCreator
{
public:
scene::Node& createEntity(EntityClass* eclass)
{
return node_for_eclass(eclass);
}
void setKeyValueChangedFunc(KeyValueChangedFunc func)
{
EntityKeyValues::setKeyValueChangedFunc(func);
}
void setCounter(Counter* counter)
{
EntityKeyValues::setCounter(counter);
}
void connectEntities(const scene::Path& path, const scene::Path& targetPath)
{
Entity* e1 = ScenePath_getEntity(path);
Entity* e2 = ScenePath_getEntity(targetPath);
if(e1 == 0 || e2 == 0)
{
globalErrorStream() << "entityConnectSelected: both of the selected instances must be an entity\n";
return;
}
if(e1 == e2)
{
globalErrorStream() << "entityConnectSelected: the selected instances must not both be from the same entity\n";
return;
}
UndoableCommand undo("entityConnectSelected");
if(g_gameType == eGameTypeDoom3)
{
StringOutputStream key(16);
for(unsigned int i = 0; ; ++i)
{
key << "target";
if(i != 0)
{
key << i;
}
const char* value = e1->getKeyValue(key.c_str());
if(string_empty(value))
{
e1->setKeyValue(key.c_str(), e2->getKeyValue("name"));
break;
}
key.clear();
}
}
else
{
ConnectEntities connector(e1, e2);
const char* value = e2->getKeyValue("targetname");
if(string_empty(value))
{
value = e1->getKeyValue("target");
}
if(!string_empty(value))
{
connector.connect(value);
}
else
{
const char* type = e2->getKeyValue("classname");
if(string_empty(type))
{
type = "t";
}
StringOutputStream key(64);
key << type << "1";
GlobalNamespace().makeUnique(key.c_str(), ConnectEntities::ConnectCaller(connector));
}
}
SceneChangeNotify();
}
void setLightRadii(bool lightRadii)
{
g_lightRadii = lightRadii;
}
bool getLightRadii()
{
return g_lightRadii;
}
void setShowNames(bool showNames)
{
g_showNames = showNames;
}
bool getShowNames()
{
return g_showNames;
}
void setShowAngles(bool showAngles)
{
g_showAngles = showAngles;
}
bool getShowAngles()
{
return g_showAngles;
}
};
Quake3EntityCreator g_Quake3EntityCreator;
EntityCreator& GetEntityCreator()
{
return g_Quake3EntityCreator;
}
class filter_entity_classname : public EntityFilter
{
const char* m_classname;
public:
filter_entity_classname(const char* classname) : m_classname(classname)
{
}
bool filter(const Entity& entity) const
{
return string_equal(entity.getKeyValue("classname"), m_classname);
}
};
class filter_entity_classgroup : public EntityFilter
{
const char* m_classgroup;
std::size_t m_length;
public:
filter_entity_classgroup(const char* classgroup) : m_classgroup(classgroup), m_length(string_length(m_classgroup))
{
}
bool filter(const Entity& entity) const
{
return string_equal_n(entity.getKeyValue("classname"), m_classgroup, m_length);
}
};
filter_entity_classname g_filter_entity_world("worldspawn");
filter_entity_classname g_filter_entity_func_group("func_group");
filter_entity_classname g_filter_entity_light("light");
filter_entity_classname g_filter_entity_misc_model("misc_model");
filter_entity_classgroup g_filter_entity_trigger("trigger_");
filter_entity_classgroup g_filter_entity_path("path_");
class filter_entity_doom3model : public EntityFilter
{
public:
bool filter(const Entity& entity) const
{
return string_equal(entity.getKeyValue("classname"), "func_static")
&& !string_equal(entity.getKeyValue("model"), entity.getKeyValue("name"));
}
};
filter_entity_doom3model g_filter_entity_doom3model;
void Entity_InitFilters()
{
add_entity_filter(g_filter_entity_world, EXCLUDE_WORLD);
add_entity_filter(g_filter_entity_func_group, EXCLUDE_WORLD);
add_entity_filter(g_filter_entity_world, EXCLUDE_ENT, true);
add_entity_filter(g_filter_entity_trigger, EXCLUDE_TRIGGERS);
add_entity_filter(g_filter_entity_misc_model, EXCLUDE_MODELS);
add_entity_filter(g_filter_entity_doom3model, EXCLUDE_MODELS);
add_entity_filter(g_filter_entity_light, EXCLUDE_LIGHTS);
add_entity_filter(g_filter_entity_path, EXCLUDE_PATHS);
}
#include "preferencesystem.h"
void Entity_Construct(EGameType gameType)
{
g_gameType = gameType;
if(g_gameType == eGameTypeDoom3)
{
g_targetable_nameKey = "name";
Static<KeyIsName>::instance().m_keyIsName = keyIsNameDoom3;
Static<KeyIsName>::instance().m_nameKey = "name";
}
else
{
Static<KeyIsName>::instance().m_keyIsName = keyIsNameQuake3;
Static<KeyIsName>::instance().m_nameKey = "targetname";
}
GlobalPreferenceSystem().registerPreference("SI_ShowNames", BoolImportStringCaller(g_showNames), BoolExportStringCaller(g_showNames));
GlobalPreferenceSystem().registerPreference("SI_ShowAngles", BoolImportStringCaller(g_showAngles), BoolExportStringCaller(g_showAngles));
GlobalPreferenceSystem().registerPreference("NewLightStyle", BoolImportStringCaller(g_newLightDraw), BoolExportStringCaller(g_newLightDraw));
GlobalPreferenceSystem().registerPreference("LightRadiuses", BoolImportStringCaller(g_lightRadii), BoolExportStringCaller(g_lightRadii));
Entity_InitFilters();
LightType lightType = LIGHTTYPE_DEFAULT;
if(g_gameType == eGameTypeRTCW)
{
lightType = LIGHTTYPE_RTCW;
}
else if(g_gameType == eGameTypeDoom3)
{
lightType = LIGHTTYPE_DOOM3;
}
Light_Construct(lightType);
MiscModel_construct();
Doom3Group_construct();
RenderablePivot::StaticShader::instance() = GlobalShaderCache().capture("$PIVOT");
GlobalShaderCache().attachRenderable(StaticRenderableConnectionLines::instance());
}
void Entity_Destroy()
{
GlobalShaderCache().detachRenderable(StaticRenderableConnectionLines::instance());
GlobalShaderCache().release("$PIVOT");
Doom3Group_destroy();
MiscModel_destroy();
Light_Destroy();
}

230
plugins/entity/entity.dsp Normal file
View File

@@ -0,0 +1,230 @@
# Microsoft Developer Studio Project File - Name="entity" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 6.00
# ** DO NOT EDIT **
# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
CFG=entity - Win32 Debug
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
!MESSAGE use the Export Makefile command and run
!MESSAGE
!MESSAGE NMAKE /f "entity.mak".
!MESSAGE
!MESSAGE You can specify a configuration when running NMAKE
!MESSAGE by defining the macro CFG on the command line. For example:
!MESSAGE
!MESSAGE NMAKE /f "entity.mak" CFG="entity - Win32 Debug"
!MESSAGE
!MESSAGE Possible choices for configuration are:
!MESSAGE
!MESSAGE "entity - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
!MESSAGE "entity - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
!MESSAGE
# Begin Project
# PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName "entity"
# PROP Scc_LocalPath "..\.."
CPP=cl.exe
MTL=midl.exe
RSC=rc.exe
!IF "$(CFG)" == "entity - Win32 Release"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir "Release"
# PROP BASE Intermediate_Dir "Release"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir "Release"
# PROP Intermediate_Dir "Release"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
F90=df.exe
# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "MAP_EXPORTS" /YX /FD /c
# ADD CPP /nologo /MD /W3 /O2 /I "..\..\..\libxml2\include\\" /I "..\..\include" /I "..\..\..\gtk2-win32\include\glib-2.0" /I "..\..\..\gtk2-win32\lib\glib-2.0\include" /I "..\common" /I "..\..\libs" /I "..\..\libs\nvtristrip" /I "..\..\..\STLPort\stlport" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "MAP_EXPORTS" /YX /FD /c
# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
# ADD BASE RSC /l 0x40c /d "NDEBUG"
# ADD RSC /l 0x40c /d "NDEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
# ADD LINK32 mathlib.lib glib-2.0.lib /nologo /dll /machine:I386 /def:".\entity.def" /libpath:"..\..\libs\mathlib\release" /libpath:"..\..\..\gtk2-win32\lib\\"
# SUBTRACT LINK32 /pdb:none
# Begin Special Build Tool
SOURCE="$(InputPath)"
PostBuild_Desc=Copy to dir...
PostBuild_Cmds=copy Release\entity.dll "../../install/modules"
# End Special Build Tool
!ELSEIF "$(CFG)" == "entity - Win32 Debug"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
# PROP BASE Output_Dir "Debug"
# PROP BASE Intermediate_Dir "Debug"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
# PROP Output_Dir "Debug"
# PROP Intermediate_Dir "Debug"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
F90=df.exe
# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "MAP_EXPORTS" /YX /FD /GZ /c
# ADD CPP /nologo /MDd /W3 /Gm /ZI /Od /I "..\..\shared" /I "..\..\..\libxml2\include" /I "..\..\include" /I "..\..\..\gtk2-win32\include\glib-2.0" /I "..\..\..\gtk2-win32\lib\glib-2.0\include" /I "..\common" /I "..\..\libs" /I "..\..\libs\nvtristrip" /I "..\..\..\STLPort\stlport" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "MAP_EXPORTS" /FR /YX /FD /GZ /c
# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
# ADD BASE RSC /l 0x40c /d "_DEBUG"
# ADD RSC /l 0x40c /d "_DEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
# ADD LINK32 mathlib.lib glib-2.0.lib /nologo /dll /debug /machine:I386 /def:".\entity.def" /pdbtype:sept /libpath:"..\..\libs\mathlib\debug" /libpath:"..\..\..\gtk2-win32\lib\\"
# SUBTRACT LINK32 /pdb:none
# Begin Special Build Tool
SOURCE="$(InputPath)"
PostBuild_Desc=Copy to dir...
PostBuild_Cmds=copy Debug\entity.dll "../../install/modules"
# End Special Build Tool
!ENDIF
# Begin Target
# Name "entity - Win32 Release"
# Name "entity - Win32 Debug"
# Begin Group "Source Files"
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
# Begin Source File
SOURCE=.\eclassmodel.cpp
# End Source File
# Begin Source File
SOURCE=.\entity.cpp
# End Source File
# Begin Source File
SOURCE=.\entity.def
# PROP Exclude_From_Build 1
# End Source File
# Begin Source File
SOURCE=.\generic.cpp
# End Source File
# Begin Source File
SOURCE=.\group.cpp
# End Source File
# Begin Source File
SOURCE=.\light.cpp
# End Source File
# Begin Source File
SOURCE=.\miscmodel.cpp
# End Source File
# Begin Source File
SOURCE=.\plugin.cpp
# End Source File
# Begin Source File
SOURCE=.\targetable.cpp
# End Source File
# End Group
# Begin Group "Header Files"
# PROP Default_Filter "h;hpp;hxx;hm;inl"
# Begin Group "API"
# PROP Default_Filter ""
# Begin Source File
SOURCE=..\..\include\ifilesystem.h
# End Source File
# Begin Source File
SOURCE=..\..\include\igl.h
# End Source File
# Begin Source File
SOURCE=..\..\include\imodel.h
# End Source File
# Begin Source File
SOURCE=..\..\include\ishaders.h
# End Source File
# Begin Source File
SOURCE=..\..\include\isurface.h
# End Source File
# Begin Source File
SOURCE=..\..\include\qerplugin.h
# End Source File
# Begin Source File
SOURCE=..\..\include\qertypes.h
# End Source File
# End Group
# Begin Source File
SOURCE=.\angle.h
# End Source File
# Begin Source File
SOURCE=.\angles.h
# End Source File
# Begin Source File
SOURCE=.\colour.h
# End Source File
# Begin Source File
SOURCE=.\entity.h
# End Source File
# Begin Source File
SOURCE=.\light.h
# End Source File
# Begin Source File
SOURCE=.\model.h
# End Source File
# Begin Source File
SOURCE=.\origin.h
# End Source File
# Begin Source File
SOURCE=.\plugin.h
# End Source File
# Begin Source File
SOURCE=.\scale.h
# End Source File
# Begin Source File
SOURCE=.\targetable.h
# End Source File
# End Group
# Begin Group "Resource Files"
# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
# End Group
# Begin Source File
SOURCE=.\Conscript
# End Source File
# End Target
# End Project

46
plugins/entity/entity.h Normal file
View File

@@ -0,0 +1,46 @@
/*
Copyright (C) 2001-2006, William Joseph.
All Rights Reserved.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#if !defined(INCLUDED_ENTITY_H)
#define INCLUDED_ENTITY_H
class EntityCreator;
EntityCreator& GetEntityCreator();
enum EGameType
{
eGameTypeQuake3,
eGameTypeRTCW,
eGameTypeDoom3,
};
extern EGameType g_gameType;
class FilterSystem;
void Entity_Construct(EGameType gameType = eGameTypeQuake3);
void Entity_Destroy();
extern bool g_showNames;
extern bool g_showAngles;
extern bool g_newLightDraw;
extern bool g_lightRadii;
#endif

View File

@@ -0,0 +1,7 @@
; entityq3.def : Declares the module parameters for the DLL.
LIBRARY "ENTITYQ3"
EXPORTS
; Explicit exports can go here
Radiant_RegisterModules @1

View File

@@ -0,0 +1,343 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="7.10"
Name="entityq3"
ProjectGUID="{49C5823A-5E50-4029-ACEE-1627EBB79E47}"
RootNamespace="entityq3"
Keyword="Win32Proj">
<Platforms>
<Platform
Name="Win32"/>
</Platforms>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="Debug"
IntermediateDirectory="Debug"
ConfigurationType="2"
CharacterSet="2">
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="../../include;../../libs;&quot;../../../STLPort-4.6/stlport&quot;"
PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;ENTITYQ3_EXPORTS"
StringPooling="TRUE"
MinimalRebuild="TRUE"
ExceptionHandling="FALSE"
BasicRuntimeChecks="0"
RuntimeLibrary="3"
BufferSecurityCheck="FALSE"
ForceConformanceInForLoopScope="TRUE"
UsePrecompiledHeader="0"
BrowseInformation="0"
WarningLevel="3"
Detect64BitPortabilityProblems="TRUE"
DebugInformationFormat="3"
DisableSpecificWarnings="4610;4510;4512;4505;4100;4127"/>
<Tool
Name="VCCustomBuildTool"/>
<Tool
Name="VCLinkerTool"
OutputFile="$(OutDir)/entityq3.dll"
LinkIncremental="1"
IgnoreDefaultLibraryNames="msvcprtd.lib"
ModuleDefinitionFile="$(ProjectName).def"
GenerateDebugInformation="TRUE"
ProgramDatabaseFile="$(OutDir)/entityq3.pdb"
SubSystem="2"
ImportLibrary="$(OutDir)/entityq3.lib"
TargetMachine="1"/>
<Tool
Name="VCMIDLTool"/>
<Tool
Name="VCPostBuildEventTool"
CommandLine="copy &quot;$(TargetPath)&quot; &quot;$(SolutionDir)install\modules&quot;
copy &quot;$(TargetDir)$(TargetName).pdb&quot; &quot;$(SolutionDir)install\modules&quot;
"/>
<Tool
Name="VCPreBuildEventTool"/>
<Tool
Name="VCPreLinkEventTool"/>
<Tool
Name="VCResourceCompilerTool"/>
<Tool
Name="VCWebServiceProxyGeneratorTool"/>
<Tool
Name="VCXMLDataGeneratorTool"/>
<Tool
Name="VCWebDeploymentTool"/>
<Tool
Name="VCManagedWrapperGeneratorTool"/>
<Tool
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="Release"
IntermediateDirectory="Release"
ConfigurationType="2"
CharacterSet="2"
WholeProgramOptimization="TRUE">
<Tool
Name="VCCLCompilerTool"
GlobalOptimizations="TRUE"
InlineFunctionExpansion="2"
EnableIntrinsicFunctions="TRUE"
FavorSizeOrSpeed="1"
OptimizeForWindowsApplication="FALSE"
AdditionalIncludeDirectories="../../include;../../libs;&quot;../../../STLPort-4.6/stlport&quot;"
PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;ENTITYQ3_EXPORTS"
StringPooling="TRUE"
ExceptionHandling="FALSE"
RuntimeLibrary="2"
BufferSecurityCheck="FALSE"
ForceConformanceInForLoopScope="TRUE"
UsePrecompiledHeader="0"
WarningLevel="4"
Detect64BitPortabilityProblems="TRUE"
DebugInformationFormat="3"
DisableSpecificWarnings="4610;4510;4512;4505;4100;4127"/>
<Tool
Name="VCCustomBuildTool"/>
<Tool
Name="VCLinkerTool"
OutputFile="$(OutDir)/entityq3.dll"
LinkIncremental="1"
IgnoreDefaultLibraryNames="msvcprt.lib"
ModuleDefinitionFile="$(ProjectName).def"
GenerateDebugInformation="TRUE"
SubSystem="2"
OptimizeReferences="2"
EnableCOMDATFolding="2"
ImportLibrary="$(OutDir)/entityq3.lib"
TargetMachine="1"
FixedBaseAddress="0"/>
<Tool
Name="VCMIDLTool"/>
<Tool
Name="VCPostBuildEventTool"
CommandLine="copy &quot;$(TargetPath)&quot; &quot;$(SolutionDir)install\modules&quot;
copy &quot;$(TargetDir)$(TargetName).pdb&quot; &quot;$(SolutionDir)install\modules&quot;
"/>
<Tool
Name="VCPreBuildEventTool"/>
<Tool
Name="VCPreLinkEventTool"/>
<Tool
Name="VCResourceCompilerTool"/>
<Tool
Name="VCWebServiceProxyGeneratorTool"/>
<Tool
Name="VCXMLDataGeneratorTool"/>
<Tool
Name="VCWebDeploymentTool"/>
<Tool
Name="VCManagedWrapperGeneratorTool"/>
<Tool
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="src"
Filter="">
<File
RelativePath=".\plugin.cpp">
</File>
<File
RelativePath=".\plugin.h">
</File>
<Filter
Name="entities"
Filter="">
<File
RelativePath=".\doom3group.cpp">
</File>
<File
RelativePath=".\doom3group.h">
</File>
<File
RelativePath=".\eclassmodel.cpp">
</File>
<File
RelativePath=".\eclassmodel.h">
</File>
<File
RelativePath=".\generic.cpp">
</File>
<File
RelativePath=".\generic.h">
</File>
<File
RelativePath=".\group.cpp">
</File>
<File
RelativePath=".\group.h">
</File>
<File
RelativePath=".\light.cpp">
</File>
<File
RelativePath=".\light.h">
</File>
<File
RelativePath=".\miscmodel.cpp">
</File>
<File
RelativePath=".\miscmodel.h">
</File>
</Filter>
<Filter
Name="modules"
Filter="">
<File
RelativePath=".\entity.cpp">
</File>
<File
RelativePath=".\entity.h">
</File>
<File
RelativePath=".\skincache.cpp">
</File>
<File
RelativePath=".\skincache.h">
</File>
</Filter>
<Filter
Name="components"
Filter="">
<File
RelativePath=".\angle.cpp">
</File>
<File
RelativePath=".\angle.h">
</File>
<File
RelativePath=".\angles.cpp">
</File>
<File
RelativePath=".\angles.h">
</File>
<File
RelativePath=".\colour.cpp">
</File>
<File
RelativePath=".\colour.h">
</File>
<File
RelativePath=".\curve.cpp">
</File>
<File
RelativePath=".\curve.h">
</File>
<File
RelativePath=".\filters.cpp">
</File>
<File
RelativePath=".\filters.h">
</File>
<File
RelativePath=".\keyobservers.cpp">
</File>
<File
RelativePath=".\keyobservers.h">
</File>
<File
RelativePath=".\model.cpp">
</File>
<File
RelativePath=".\model.h">
</File>
<File
RelativePath=".\modelskinkey.cpp">
</File>
<File
RelativePath=".\modelskinkey.h">
</File>
<File
RelativePath=".\namedentity.cpp">
</File>
<File
RelativePath=".\namedentity.h">
</File>
<File
RelativePath=".\namekeys.cpp">
</File>
<File
RelativePath=".\namekeys.h">
</File>
<File
RelativePath=".\origin.cpp">
</File>
<File
RelativePath=".\origin.h">
</File>
<File
RelativePath=".\rotation.cpp">
</File>
<File
RelativePath=".\rotation.h">
</File>
<File
RelativePath=".\scale.cpp">
</File>
<File
RelativePath=".\scale.h">
</File>
<File
RelativePath=".\targetable.cpp">
</File>
<File
RelativePath=".\targetable.h">
</File>
</Filter>
</Filter>
<File
RelativePath="..\..\debug.py">
<FileConfiguration
Name="Debug|Win32">
<Tool
Name="VCCustomBuildTool"
CommandLine="python &quot;$(SolutionDir)debug.py&quot;
"
AdditionalDependencies="&quot;$(SolutionDir)install\modules\$(TargetName).pdb&quot;"
Outputs="&quot;$(TargetDir)$(TargetName).pdb&quot;"/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32">
<Tool
Name="VCCustomBuildTool"
CommandLine="python &quot;$(SolutionDir)debug.py&quot;
"
AdditionalDependencies="&quot;$(SolutionDir)install\modules\$(TargetName).pdb&quot;"
Outputs="&quot;$(TargetDir)$(TargetName).pdb&quot;"/>
</FileConfiguration>
</File>
<File
RelativePath=".\entityq3.def">
<FileConfiguration
Name="Debug|Win32">
<Tool
Name="VCCustomBuildTool"
CommandLine="python &quot;$(SolutionDir)touch.py&quot; &quot;$(TargetPath)&quot;
"
AdditionalDependencies="&quot;$(SolutionDir)install\modules\$(TargetFileName)&quot;"
Outputs="&quot;$(TargetPath)&quot;"/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32">
<Tool
Name="VCCustomBuildTool"
CommandLine="python &quot;$(SolutionDir)touch.py&quot; &quot;$(TargetPath)&quot;
"
AdditionalDependencies="&quot;$(SolutionDir)install\modules\$(TargetFileName)&quot;"
Outputs="&quot;$(TargetPath)&quot;"/>
</FileConfiguration>
</File>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@@ -0,0 +1,72 @@
/*
Copyright (C) 2001-2006, William Joseph.
All Rights Reserved.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "filters.h"
#include "ifilter.h"
#include <list>
class EntityFilterWrapper : public Filter
{
bool m_active;
bool m_invert;
EntityFilter& m_filter;
public:
EntityFilterWrapper(EntityFilter& filter, bool invert) : m_invert(invert), m_filter(filter)
{
}
void setActive(bool active)
{
m_active = active;
}
bool active()
{
return m_active;
}
bool filter(const Entity& entity)
{
return m_invert ^ m_filter.filter(entity);
}
};
typedef std::list<EntityFilterWrapper> EntityFilters;
EntityFilters g_entityFilters;
void add_entity_filter(EntityFilter& filter, int mask, bool invert)
{
g_entityFilters.push_back(EntityFilterWrapper(filter, invert));
GlobalFilterSystem().addFilter(g_entityFilters.back(), mask);
}
bool entity_filtered(Entity& entity)
{
for(EntityFilters::iterator i = g_entityFilters.begin(); i != g_entityFilters.end(); ++i)
{
if((*i).active() && (*i).filter(entity))
{
return true;
}
}
return false;
}

82
plugins/entity/filters.h Normal file
View File

@@ -0,0 +1,82 @@
/*
Copyright (C) 2001-2006, William Joseph.
All Rights Reserved.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#if !defined(INCLUDED_FILTERS_H)
#define INCLUDED_FILTERS_H
#include "ifilter.h"
#include "generic/callback.h"
#include "scenelib.h"
class Entity;
class EntityFilter
{
public:
virtual bool filter(const Entity& entity) const = 0;
};
bool entity_filtered(Entity& entity);
void add_entity_filter(EntityFilter& filter, int mask, bool invert = false);
class ClassnameFilter : public Filterable
{
scene::Node& m_node;
public:
Entity& m_entity;
ClassnameFilter(Entity& entity, scene::Node& node) : m_node(node), m_entity(entity)
{
}
~ClassnameFilter()
{
}
void instanceAttach()
{
GlobalFilterSystem().registerFilterable(*this);
}
void instanceDetach()
{
GlobalFilterSystem().unregisterFilterable(*this);
}
void updateFiltered()
{
if(entity_filtered(m_entity))
{
m_node.enable(scene::Node::eFiltered);
}
else
{
m_node.disable(scene::Node::eFiltered);
}
}
void classnameChanged(const char* value)
{
updateFiltered();
}
typedef MemberCaller1<ClassnameFilter, const char*, &ClassnameFilter::classnameChanged> ClassnameChangedCaller;
};
#endif

494
plugins/entity/generic.cpp Normal file
View File

@@ -0,0 +1,494 @@
/*
Copyright (C) 2001-2006, William Joseph.
All Rights Reserved.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
///\file
///\brief Represents any entity which has a fixed size specified in its entity-definition and does not display a model (e.g. info_player_start).
///
/// This entity displays an axis-aligned bounding box of the size and colour specified in its entity-definition.
/// The "origin" key directly controls the entity's local-to-parent transform.
/// An arrow is drawn to visualise the "angle" key.
#include "cullable.h"
#include "renderable.h"
#include "editable.h"
#include "math/frustum.h"
#include "selectionlib.h"
#include "instancelib.h"
#include "transformlib.h"
#include "entitylib.h"
#include "render.h"
#include "eclasslib.h"
#include "math/line.h"
#include "targetable.h"
#include "origin.h"
#include "angle.h"
#include "filters.h"
#include "namedentity.h"
#include "keyobservers.h"
#include "namekeys.h"
#include "rotation.h"
#include "entity.h"
class RenderableArrow : public OpenGLRenderable
{
const Ray& m_ray;
public:
RenderableArrow(const Ray& ray)
: m_ray(ray)
{
}
void render(RenderStateFlags state) const
{
arrow_draw(m_ray.origin, m_ray.direction);
}
};
inline void read_aabb(AABB& aabb, const EntityClass& eclass)
{
aabb = aabb_for_minmax(eclass.mins, eclass.maxs);
}
class GenericEntity :
public Cullable,
public Bounded,
public Snappable
{
EntityKeyValues m_entity;
KeyObserverMap m_keyObservers;
MatrixTransform m_transform;
OriginKey m_originKey;
Vector3 m_origin;
AngleKey m_angleKey;
float m_angle;
ClassnameFilter m_filter;
NamedEntity m_named;
NameKeys m_nameKeys;
AABB m_aabb_local;
Ray m_ray;
RenderableArrow m_arrow;
RenderableSolidAABB m_aabb_solid;
RenderableWireframeAABB m_aabb_wire;
RenderableNamedEntity m_renderName;
Callback m_transformChanged;
Callback m_evaluateTransform;
void construct()
{
read_aabb(m_aabb_local, m_entity.getEntityClass());
m_ray.origin = m_aabb_local.origin;
m_ray.direction[0] = 1;
m_ray.direction[1] = 0;
m_ray.direction[2] = 0;
m_keyObservers.insert("classname", ClassnameFilter::ClassnameChangedCaller(m_filter));
m_keyObservers.insert(Static<KeyIsName>::instance().m_nameKey, NamedEntity::IdentifierChangedCaller(m_named));
m_keyObservers.insert("angle", AngleKey::AngleChangedCaller(m_angleKey));
m_keyObservers.insert("origin", OriginKey::OriginChangedCaller(m_originKey));
}
void updateTransform()
{
m_transform.localToParent() = g_matrix4_identity;
matrix4_translate_by_vec3(m_transform.localToParent(), m_origin);
m_ray.direction = matrix4_transformed_direction(matrix4_rotation_for_z(degrees_to_radians(m_angle)), Vector3(1, 0, 0));
m_transformChanged();
}
typedef MemberCaller<GenericEntity, &GenericEntity::updateTransform> UpdateTransformCaller;
void originChanged()
{
m_origin = m_originKey.m_origin;
updateTransform();
}
typedef MemberCaller<GenericEntity, &GenericEntity::originChanged> OriginChangedCaller;
void angleChanged()
{
m_angle = m_angleKey.m_angle;
updateTransform();
}
typedef MemberCaller<GenericEntity, &GenericEntity::angleChanged> AngleChangedCaller;
public:
GenericEntity(EntityClass* eclass, scene::Node& node, const Callback& transformChanged, const Callback& evaluateTransform) :
m_entity(eclass),
m_originKey(OriginChangedCaller(*this)),
m_origin(ORIGINKEY_IDENTITY),
m_angleKey(AngleChangedCaller(*this)),
m_angle(ANGLEKEY_IDENTITY),
m_filter(m_entity, node),
m_named(m_entity),
m_nameKeys(m_entity),
m_arrow(m_ray),
m_aabb_solid(m_aabb_local),
m_aabb_wire(m_aabb_local),
m_renderName(m_named, g_vector3_identity),
m_transformChanged(transformChanged),
m_evaluateTransform(evaluateTransform)
{
construct();
}
GenericEntity(const GenericEntity& other, scene::Node& node, const Callback& transformChanged, const Callback& evaluateTransform) :
m_entity(other.m_entity),
m_originKey(OriginChangedCaller(*this)),
m_origin(ORIGINKEY_IDENTITY),
m_angleKey(AngleChangedCaller(*this)),
m_angle(ANGLEKEY_IDENTITY),
m_filter(m_entity, node),
m_named(m_entity),
m_nameKeys(m_entity),
m_arrow(m_ray),
m_aabb_solid(m_aabb_local),
m_aabb_wire(m_aabb_local),
m_renderName(m_named, g_vector3_identity),
m_transformChanged(transformChanged),
m_evaluateTransform(evaluateTransform)
{
construct();
}
InstanceCounter m_instanceCounter;
void instanceAttach(const scene::Path& path)
{
if(++m_instanceCounter.m_count == 1)
{
m_filter.instanceAttach();
m_entity.instanceAttach(path_find_mapfile(path.begin(), path.end()));
m_entity.attach(m_keyObservers);
}
}
void instanceDetach(const scene::Path& path)
{
if(--m_instanceCounter.m_count == 0)
{
m_entity.detach(m_keyObservers);
m_entity.instanceDetach(path_find_mapfile(path.begin(), path.end()));
m_filter.instanceDetach();
}
}
EntityKeyValues& getEntity()
{
return m_entity;
}
const EntityKeyValues& getEntity() const
{
return m_entity;
}
Namespaced& getNamespaced()
{
return m_nameKeys;
}
Nameable& getNameable()
{
return m_named;
}
TransformNode& getTransformNode()
{
return m_transform;
}
const AABB& localAABB() const
{
return m_aabb_local;
}
VolumeIntersectionValue intersectVolume(const VolumeTest& volume, const Matrix4& localToWorld) const
{
return volume.TestAABB(localAABB(), localToWorld);
}
void renderArrow(Renderer& renderer, const VolumeTest& volume, const Matrix4& localToWorld) const
{
if(g_showAngles)
{
renderer.addRenderable(m_arrow, localToWorld);
}
}
void renderSolid(Renderer& renderer, const VolumeTest& volume, const Matrix4& localToWorld) const
{
renderer.SetState(m_entity.getEntityClass().m_state_fill, Renderer::eFullMaterials);
renderer.addRenderable(m_aabb_solid, localToWorld);
renderArrow(renderer, volume, localToWorld);
}
void renderWireframe(Renderer& renderer, const VolumeTest& volume, const Matrix4& localToWorld) const
{
renderer.SetState(m_entity.getEntityClass().m_state_wire, Renderer::eWireframeOnly);
renderer.addRenderable(m_aabb_wire, localToWorld);
renderArrow(renderer, volume, localToWorld);
if(g_showNames)
{
renderer.addRenderable(m_renderName, localToWorld);
}
}
void testSelect(Selector& selector, SelectionTest& test, const Matrix4& localToWorld)
{
test.BeginMesh(localToWorld);
SelectionIntersection best;
aabb_testselect(m_aabb_local, test, best);
if(best.valid())
{
selector.addIntersection(best);
}
}
void translate(const Vector3& translation)
{
m_origin = origin_translated(m_origin, translation);
}
void rotate(const Quaternion& rotation)
{
m_angle = angle_rotated(m_angle, rotation);
}
void snapto(float snap)
{
m_originKey.m_origin = origin_snapped(m_originKey.m_origin, snap);
m_originKey.write(&m_entity);
}
void revertTransform()
{
m_origin = m_originKey.m_origin;
m_angle = m_angleKey.m_angle;
}
void freezeTransform()
{
m_originKey.m_origin = m_origin;
m_originKey.write(&m_entity);
m_angleKey.m_angle = m_angle;
m_angleKey.write(&m_entity);
}
void transformChanged()
{
revertTransform();
m_evaluateTransform();
updateTransform();
}
typedef MemberCaller<GenericEntity, &GenericEntity::transformChanged> TransformChangedCaller;
};
class GenericEntityInstance :
public TargetableInstance,
public TransformModifier,
public Renderable,
public SelectionTestable
{
class TypeCasts
{
InstanceTypeCastTable m_casts;
public:
TypeCasts()
{
m_casts = TargetableInstance::StaticTypeCasts::instance().get();
InstanceContainedCast<GenericEntityInstance, Bounded>::install(m_casts);
InstanceContainedCast<GenericEntityInstance, Cullable>::install(m_casts);
InstanceStaticCast<GenericEntityInstance, Renderable>::install(m_casts);
InstanceStaticCast<GenericEntityInstance, SelectionTestable>::install(m_casts);
InstanceStaticCast<GenericEntityInstance, Transformable>::install(m_casts);
InstanceIdentityCast<GenericEntityInstance>::install(m_casts);
}
InstanceTypeCastTable& get()
{
return m_casts;
}
};
GenericEntity& m_contained;
mutable AABB m_bounds;
public:
typedef LazyStatic<TypeCasts> StaticTypeCasts;
Bounded& get(NullType<Bounded>)
{
return m_contained;
}
Cullable& get(NullType<Cullable>)
{
return m_contained;
}
STRING_CONSTANT(Name, "GenericEntityInstance");
GenericEntityInstance(const scene::Path& path, scene::Instance* parent, GenericEntity& contained) :
TargetableInstance(path, parent, this, StaticTypeCasts::instance().get(), contained.getEntity(), *this),
TransformModifier(GenericEntity::TransformChangedCaller(contained), ApplyTransformCaller(*this)),
m_contained(contained)
{
m_contained.instanceAttach(Instance::path());
StaticRenderableConnectionLines::instance().attach(*this);
}
~GenericEntityInstance()
{
StaticRenderableConnectionLines::instance().detach(*this);
m_contained.instanceDetach(Instance::path());
}
void renderSolid(Renderer& renderer, const VolumeTest& volume) const
{
m_contained.renderSolid(renderer, volume, Instance::localToWorld());
}
void renderWireframe(Renderer& renderer, const VolumeTest& volume) const
{
m_contained.renderWireframe(renderer, volume, Instance::localToWorld());
}
void testSelect(Selector& selector, SelectionTest& test)
{
m_contained.testSelect(selector, test, Instance::localToWorld());
}
void evaluateTransform()
{
if(getType() == TRANSFORM_PRIMITIVE)
{
m_contained.translate(getTranslation());
m_contained.rotate(getRotation());
}
}
void applyTransform()
{
m_contained.revertTransform();
evaluateTransform();
m_contained.freezeTransform();
}
typedef MemberCaller<GenericEntityInstance, &GenericEntityInstance::applyTransform> ApplyTransformCaller;
};
class GenericEntityNode :
public scene::Node::Symbiot,
public scene::Instantiable,
public scene::Cloneable
{
class TypeCasts
{
NodeTypeCastTable m_casts;
public:
TypeCasts()
{
NodeStaticCast<GenericEntityNode, scene::Instantiable>::install(m_casts);
NodeStaticCast<GenericEntityNode, scene::Cloneable>::install(m_casts);
NodeContainedCast<GenericEntityNode, Snappable>::install(m_casts);
NodeContainedCast<GenericEntityNode, TransformNode>::install(m_casts);
NodeContainedCast<GenericEntityNode, Entity>::install(m_casts);
NodeContainedCast<GenericEntityNode, Nameable>::install(m_casts);
NodeContainedCast<GenericEntityNode, Namespaced>::install(m_casts);
}
NodeTypeCastTable& get()
{
return m_casts;
}
};
InstanceSet m_instances;
scene::Node m_node;
GenericEntity m_contained;
public:
typedef LazyStatic<TypeCasts> StaticTypeCasts;
Snappable& get(NullType<Snappable>)
{
return m_contained;
}
TransformNode& get(NullType<TransformNode>)
{
return m_contained.getTransformNode();
}
Entity& get(NullType<Entity>)
{
return m_contained.getEntity();
}
Nameable& get(NullType<Nameable>)
{
return m_contained.getNameable();
}
Namespaced& get(NullType<Namespaced>)
{
return m_contained.getNamespaced();
}
GenericEntityNode(EntityClass* eclass) :
m_node(this, this, StaticTypeCasts::instance().get()),
m_contained(eclass, m_node, InstanceSet::TransformChangedCaller(m_instances), InstanceSetEvaluateTransform<GenericEntityInstance>::Caller(m_instances))
{
}
GenericEntityNode(const GenericEntityNode& other) :
scene::Node::Symbiot(other),
scene::Instantiable(other),
scene::Cloneable(other),
m_node(this, this, StaticTypeCasts::instance().get()),
m_contained(other.m_contained, m_node, InstanceSet::TransformChangedCaller(m_instances), InstanceSetEvaluateTransform<GenericEntityInstance>::Caller(m_instances))
{
}
void release()
{
delete this;
}
scene::Node& node()
{
return m_node;
}
scene::Node& clone() const
{
return (new GenericEntityNode(*this))->node();
}
scene::Instance* create(const scene::Path& path, scene::Instance* parent)
{
return new GenericEntityInstance(path, parent, m_contained);
}
void forEachInstance(const scene::Instantiable::Visitor& visitor)
{
m_instances.forEachInstance(visitor);
}
void insert(scene::Instantiable::Observer* observer, const scene::Path& path, scene::Instance* instance)
{
m_instances.insert(observer, path, instance);
}
scene::Instance* erase(scene::Instantiable::Observer* observer, const scene::Path& path)
{
return m_instances.erase(observer, path);
}
};
scene::Node& New_GenericEntity(EntityClass* eclass)
{
return (new GenericEntityNode(eclass))->node();
}

27
plugins/entity/generic.h Normal file
View File

@@ -0,0 +1,27 @@
/*
Copyright (C) 2001-2006, William Joseph.
All Rights Reserved.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#if !defined(INCLUDED_GENERIC_H)
#define INCLUDED_GENERIC_H
scene::Node& New_GenericEntity(EntityClass* eclass);
#endif

444
plugins/entity/group.cpp Normal file
View File

@@ -0,0 +1,444 @@
/*
Copyright (C) 2001-2006, William Joseph.
All Rights Reserved.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
///\file
///\brief Represents any entity which does not have a fixed size specified in its entity-definition (except misc_model).
///
/// This entity behaves as a group, i.e. it contains brushes.
#include "cullable.h"
#include "renderable.h"
#include "editable.h"
#include "selectionlib.h"
#include "instancelib.h"
#include "transformlib.h"
#include "traverselib.h"
#include "entitylib.h"
#include "render.h"
#include "eclasslib.h"
#include "targetable.h"
#include "origin.h"
#include "angles.h"
#include "scale.h"
#include "filters.h"
#include "namedentity.h"
#include "keyobservers.h"
#include "namekeys.h"
#include "entity.h"
class Group
{
EntityKeyValues m_entity;
KeyObserverMap m_keyObservers;
MatrixTransform m_transform;
TraversableNodeSet m_traverse;
ClassnameFilter m_filter;
NamedEntity m_named;
NameKeys m_nameKeys;
RenderableNamedEntity m_renderName;
Callback m_transformChanged;
void construct()
{
m_keyObservers.insert("classname", ClassnameFilter::ClassnameChangedCaller(m_filter));
m_keyObservers.insert(Static<KeyIsName>::instance().m_nameKey, NamedEntity::IdentifierChangedCaller(m_named));
}
public:
Group(EntityClass* eclass, scene::Node& node, const Callback& transformChanged) :
m_entity(eclass),
m_filter(m_entity, node),
m_named(m_entity),
m_nameKeys(m_entity),
m_renderName(m_named, g_vector3_identity),
m_transformChanged(transformChanged)
{
construct();
}
Group(const Group& other, scene::Node& node, const Callback& transformChanged) :
m_entity(other.m_entity),
m_filter(m_entity, node),
m_named(m_entity),
m_nameKeys(m_entity),
m_renderName(m_named, g_vector3_identity),
m_transformChanged(transformChanged)
{
construct();
}
InstanceCounter m_instanceCounter;
void instanceAttach(const scene::Path& path)
{
if(++m_instanceCounter.m_count == 1)
{
m_filter.instanceAttach();
m_entity.instanceAttach(path_find_mapfile(path.begin(), path.end()));
m_traverse.instanceAttach(path_find_mapfile(path.begin(), path.end()));
m_entity.attach(m_keyObservers);
}
}
void instanceDetach(const scene::Path& path)
{
if(--m_instanceCounter.m_count == 0)
{
m_entity.detach(m_keyObservers);
m_traverse.instanceDetach(path_find_mapfile(path.begin(), path.end()));
m_entity.instanceDetach(path_find_mapfile(path.begin(), path.end()));
m_filter.instanceDetach();
}
}
EntityKeyValues& getEntity()
{
return m_entity;
}
const EntityKeyValues& getEntity() const
{
return m_entity;
}
scene::Traversable& getTraversable()
{
return m_traverse;
}
Namespaced& getNamespaced()
{
return m_nameKeys;
}
Nameable& getNameable()
{
return m_named;
}
TransformNode& getTransformNode()
{
return m_transform;
}
void attach(scene::Traversable::Observer* observer)
{
m_traverse.attach(observer);
}
void detach(scene::Traversable::Observer* observer)
{
m_traverse.detach(observer);
}
void renderSolid(Renderer& renderer, const VolumeTest& volume, const Matrix4& localToWorld) const
{
renderer.SetState(m_entity.getEntityClass().m_state_wire, Renderer::eWireframeOnly);
}
void renderWireframe(Renderer& renderer, const VolumeTest& volume, const Matrix4& localToWorld) const
{
renderSolid(renderer, volume, localToWorld);
#if 0
if(g_showNames)
{
renderer.addRenderable(m_renderName, g_matrix4_identity);
}
#endif
}
};
#if 0
class TransformableSetTranslation
{
Translation m_value;
public:
TransformableSetTranslation(const Translation& value) : m_value(value)
{
}
void operator()(Transformable& transformable) const
{
transformable.setTranslation(m_value);
}
};
class TransformableSetRotation
{
Rotation m_value;
public:
TransformableSetRotation(const Rotation& value) : m_value(value)
{
}
void operator()(Transformable& transformable) const
{
transformable.setRotation(m_value);
}
};
class TransformableSetScale
{
Scale m_value;
public:
TransformableSetScale(const Scale& value) : m_value(value)
{
}
void operator()(Transformable& transformable) const
{
transformable.setScale(m_value);
}
};
class TransformableSetType
{
TransformModifierType m_value;
public:
TransformableSetType(const TransformModifierType& value) : m_value(value)
{
}
void operator()(Transformable& transformable) const
{
transformable.setType(m_value);
}
};
class TransformableFreezeTransform
{
TransformModifierType m_value;
public:
void operator()(Transformable& transformable) const
{
transformable.freezeTransform();
}
};
template<typename Functor>
inline void Scene_forEachChildTransformable(const Functor& functor, const scene::Path& path)
{
GlobalSceneGraph().traverse_subgraph(ChildInstanceWalker< InstanceApply<Transformable, Functor> >(functor), path);
}
#endif
class GroupInstance :
public TargetableInstance,
#if 0
public Transformable,
#endif
public Renderable
{
class TypeCasts
{
InstanceTypeCastTable m_casts;
public:
TypeCasts()
{
m_casts = TargetableInstance::StaticTypeCasts::instance().get();
InstanceStaticCast<GroupInstance, Renderable>::install(m_casts);
#if 0
InstanceStaticCast<GroupInstance, Transformable>::install(m_casts);
#endif
}
InstanceTypeCastTable& get()
{
return m_casts;
}
};
Group& m_contained;
public:
typedef LazyStatic<TypeCasts> StaticTypeCasts;
GroupInstance(const scene::Path& path, scene::Instance* parent, Group& group) :
TargetableInstance(path, parent, this, StaticTypeCasts::instance().get(), group.getEntity(), *this),
m_contained(group)
{
m_contained.instanceAttach(Instance::path());
StaticRenderableConnectionLines::instance().attach(*this);
}
~GroupInstance()
{
StaticRenderableConnectionLines::instance().detach(*this);
m_contained.instanceDetach(Instance::path());
}
void renderSolid(Renderer& renderer, const VolumeTest& volume) const
{
m_contained.renderSolid(renderer, volume, Instance::localToWorld());
}
void renderWireframe(Renderer& renderer, const VolumeTest& volume) const
{
m_contained.renderWireframe(renderer, volume, Instance::localToWorld());
}
#if 0
void setType(TransformModifierType type)
{
Scene_forEachChildTransformable(TransformableSetType(type), Instance::path());
}
void setTranslation(const Translation& value)
{
Scene_forEachChildTransformable(TransformableSetTranslation(value), Instance::path());
}
void setRotation(const Rotation& value)
{
Scene_forEachChildTransformable(TransformableSetRotation(value), Instance::path());
}
void setScale(const Scale& value)
{
Scene_forEachChildTransformable(TransformableSetScale(value), Instance::path());
}
void freezeTransform()
{
Scene_forEachChildTransformable(TransformableFreezeTransform(), Instance::path());
}
void evaluateTransform()
{
}
#endif
};
class GroupNode :
public scene::Node::Symbiot,
public scene::Instantiable,
public scene::Cloneable,
public scene::Traversable::Observer
{
class TypeCasts
{
NodeTypeCastTable m_casts;
public:
TypeCasts()
{
NodeStaticCast<GroupNode, scene::Instantiable>::install(m_casts);
NodeStaticCast<GroupNode, scene::Cloneable>::install(m_casts);
NodeContainedCast<GroupNode, scene::Traversable>::install(m_casts);
NodeContainedCast<GroupNode, TransformNode>::install(m_casts);
NodeContainedCast<GroupNode, Entity>::install(m_casts);
NodeContainedCast<GroupNode, Nameable>::install(m_casts);
NodeContainedCast<GroupNode, Namespaced>::install(m_casts);
}
NodeTypeCastTable& get()
{
return m_casts;
}
};
scene::Node m_node;
InstanceSet m_instances;
Group m_contained;
void construct()
{
m_contained.attach(this);
}
void destroy()
{
m_contained.detach(this);
}
public:
typedef LazyStatic<TypeCasts> StaticTypeCasts;
scene::Traversable& get(NullType<scene::Traversable>)
{
return m_contained.getTraversable();
}
TransformNode& get(NullType<TransformNode>)
{
return m_contained.getTransformNode();
}
Entity& get(NullType<Entity>)
{
return m_contained.getEntity();
}
Nameable& get(NullType<Nameable>)
{
return m_contained.getNameable();
}
Namespaced& get(NullType<Namespaced>)
{
return m_contained.getNamespaced();
}
GroupNode(EntityClass* eclass) :
m_node(this, this, StaticTypeCasts::instance().get()),
m_contained(eclass, m_node, InstanceSet::TransformChangedCaller(m_instances))
{
construct();
}
GroupNode(const GroupNode& other) :
scene::Node::Symbiot(other),
scene::Instantiable(other),
scene::Cloneable(other),
scene::Traversable::Observer(other),
m_node(this, this, StaticTypeCasts::instance().get()),
m_contained(other.m_contained, m_node, InstanceSet::TransformChangedCaller(m_instances))
{
construct();
}
~GroupNode()
{
destroy();
}
void release()
{
delete this;
}
scene::Node& node()
{
return m_node;
}
scene::Node& clone() const
{
return (new GroupNode(*this))->node();
}
void insert(scene::Node& child)
{
m_instances.insert(child);
}
void erase(scene::Node& child)
{
m_instances.erase(child);
}
scene::Instance* create(const scene::Path& path, scene::Instance* parent)
{
return new GroupInstance(path, parent, m_contained);
}
void forEachInstance(const scene::Instantiable::Visitor& visitor)
{
m_instances.forEachInstance(visitor);
}
void insert(scene::Instantiable::Observer* observer, const scene::Path& path, scene::Instance* instance)
{
m_instances.insert(observer, path, instance);
}
scene::Instance* erase(scene::Instantiable::Observer* observer, const scene::Path& path)
{
return m_instances.erase(observer, path);
}
};
scene::Node& New_Group(EntityClass* eclass)
{
return (new GroupNode(eclass))->node();
}

27
plugins/entity/group.h Normal file
View File

@@ -0,0 +1,27 @@
/*
Copyright (C) 2001-2006, William Joseph.
All Rights Reserved.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#if !defined(INCLUDED_GROUP_H)
#define INCLUDED_GROUP_H
scene::Node& New_Group(EntityClass* eclass);
#endif

View File

@@ -0,0 +1,23 @@
/*
Copyright (C) 2001-2006, William Joseph.
All Rights Reserved.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "keyobservers.h"

View File

@@ -0,0 +1,53 @@
/*
Copyright (C) 2001-2006, William Joseph.
All Rights Reserved.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#if !defined(INCLUDED_KEYOBSERVERS_H)
#define INCLUDED_KEYOBSERVERS_H
#include "entitylib.h"
#include <map>
class KeyObserverMap : public EntityKeyValues::Observer
{
typedef std::multimap<const char*, KeyObserver, RawStringLess> KeyObservers;
KeyObservers m_keyObservers;
public:
void insert(const char* key, const KeyObserver& observer)
{
m_keyObservers.insert(KeyObservers::value_type(key, observer));
}
void insert(const char* key, EntityKeyValues::Value& value)
{
for(KeyObservers::const_iterator i = m_keyObservers.find(key); i != m_keyObservers.end() && string_equal((*i).first, key); ++i)
{
value.attach((*i).second);
}
}
void erase(const char* key, EntityKeyValues::Value& value)
{
for(KeyObservers::const_iterator i = m_keyObservers.find(key); i != m_keyObservers.end() && string_equal((*i).first, key); ++i)
{
value.detach((*i).second);
}
}
};
#endif

1839
plugins/entity/light.cpp Normal file

File diff suppressed because it is too large Load Diff

41
plugins/entity/light.h Normal file
View File

@@ -0,0 +1,41 @@
/*
Copyright (C) 2001-2006, William Joseph.
All Rights Reserved.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#if !defined(INCLUDED_LIGHT_H)
#define INCLUDED_LIGHT_H
namespace scene
{
class Node;
};
class EntityClass;
scene::Node& New_Light(EntityClass* eclass);
enum LightType
{
LIGHTTYPE_DEFAULT,
LIGHTTYPE_RTCW,
LIGHTTYPE_DOOM3
};
void Light_Construct(LightType lightType);
void Light_Destroy();
#endif

View File

@@ -0,0 +1,470 @@
/*
Copyright (C) 2001-2006, William Joseph.
All Rights Reserved.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
///\file
///\brief Represents the Quake3 misc_model entity.
///
/// This entity displays the model specified in its "model" key.
/// The "origin", "angles" and "modelscale*" keys directly control the entity's local-to-parent transform.
#include "cullable.h"
#include "renderable.h"
#include "editable.h"
#include "selectionlib.h"
#include "instancelib.h"
#include "transformlib.h"
#include "traverselib.h"
#include "entitylib.h"
#include "eclasslib.h"
#include "render.h"
#include "pivot.h"
#include "targetable.h"
#include "origin.h"
#include "angles.h"
#include "scale.h"
#include "model.h"
#include "filters.h"
#include "namedentity.h"
#include "keyobservers.h"
#include "namekeys.h"
#include "entity.h"
class MiscModel :
public Snappable
{
EntityKeyValues m_entity;
KeyObserverMap m_keyObservers;
MatrixTransform m_transform;
OriginKey m_originKey;
Vector3 m_origin;
AnglesKey m_anglesKey;
Vector3 m_angles;
ScaleKey m_scaleKey;
Vector3 m_scale;
SingletonModel m_model;
ClassnameFilter m_filter;
NamedEntity m_named;
NameKeys m_nameKeys;
RenderablePivot m_renderOrigin;
RenderableNamedEntity m_renderName;
Callback m_transformChanged;
Callback m_evaluateTransform;
void construct()
{
m_keyObservers.insert("classname", ClassnameFilter::ClassnameChangedCaller(m_filter));
m_keyObservers.insert(Static<KeyIsName>::instance().m_nameKey, NamedEntity::IdentifierChangedCaller(m_named));
m_keyObservers.insert("model", SingletonModel::ModelChangedCaller(m_model));
m_keyObservers.insert("origin", OriginKey::OriginChangedCaller(m_originKey));
m_keyObservers.insert("angle", AnglesKey::AngleChangedCaller(m_anglesKey));
m_keyObservers.insert("angles", AnglesKey::AnglesChangedCaller(m_anglesKey));
m_keyObservers.insert("modelscale", ScaleKey::UniformScaleChangedCaller(m_scaleKey));
m_keyObservers.insert("modelscale_vec", ScaleKey::ScaleChangedCaller(m_scaleKey));
}
void updateTransform()
{
m_transform.localToParent() = g_matrix4_identity;
matrix4_transform_by_euler_xyz_degrees(m_transform.localToParent(), m_origin, m_angles, m_scale);
m_transformChanged();
}
void originChanged()
{
m_origin = m_originKey.m_origin;
updateTransform();
}
typedef MemberCaller<MiscModel, &MiscModel::originChanged> OriginChangedCaller;
void anglesChanged()
{
m_angles = m_anglesKey.m_angles;
updateTransform();
}
typedef MemberCaller<MiscModel, &MiscModel::anglesChanged> AnglesChangedCaller;
void scaleChanged()
{
m_scale = m_scaleKey.m_scale;
updateTransform();
}
typedef MemberCaller<MiscModel, &MiscModel::scaleChanged> ScaleChangedCaller;
public:
MiscModel(EntityClass* eclass, scene::Node& node, const Callback& transformChanged, const Callback& evaluateTransform) :
m_entity(eclass),
m_originKey(OriginChangedCaller(*this)),
m_origin(ORIGINKEY_IDENTITY),
m_anglesKey(AnglesChangedCaller(*this)),
m_angles(ANGLESKEY_IDENTITY),
m_scaleKey(ScaleChangedCaller(*this)),
m_scale(SCALEKEY_IDENTITY),
m_filter(m_entity, node),
m_named(m_entity),
m_nameKeys(m_entity),
m_renderName(m_named, g_vector3_identity),
m_transformChanged(transformChanged),
m_evaluateTransform(evaluateTransform)
{
construct();
}
MiscModel(const MiscModel& other, scene::Node& node, const Callback& transformChanged, const Callback& evaluateTransform) :
m_entity(other.m_entity),
m_originKey(OriginChangedCaller(*this)),
m_origin(ORIGINKEY_IDENTITY),
m_anglesKey(AnglesChangedCaller(*this)),
m_angles(ANGLESKEY_IDENTITY),
m_scaleKey(ScaleChangedCaller(*this)),
m_scale(SCALEKEY_IDENTITY),
m_filter(m_entity, node),
m_named(m_entity),
m_nameKeys(m_entity),
m_renderName(m_named, g_vector3_identity),
m_transformChanged(transformChanged),
m_evaluateTransform(evaluateTransform)
{
construct();
}
InstanceCounter m_instanceCounter;
void instanceAttach(const scene::Path& path)
{
if(++m_instanceCounter.m_count == 1)
{
m_filter.instanceAttach();
m_entity.instanceAttach(path_find_mapfile(path.begin(), path.end()));
m_entity.attach(m_keyObservers);
}
}
void instanceDetach(const scene::Path& path)
{
if(--m_instanceCounter.m_count == 0)
{
m_entity.detach(m_keyObservers);
m_entity.instanceDetach(path_find_mapfile(path.begin(), path.end()));
m_filter.instanceDetach();
}
}
EntityKeyValues& getEntity()
{
return m_entity;
}
const EntityKeyValues& getEntity() const
{
return m_entity;
}
scene::Traversable& getTraversable()
{
return m_model.getTraversable();
}
Namespaced& getNamespaced()
{
return m_nameKeys;
}
Nameable& getNameable()
{
return m_named;
}
TransformNode& getTransformNode()
{
return m_transform;
}
void attach(scene::Traversable::Observer* observer)
{
m_model.attach(observer);
}
void detach(scene::Traversable::Observer* observer)
{
m_model.detach(observer);
}
void renderSolid(Renderer& renderer, const VolumeTest& volume, const Matrix4& localToWorld, bool selected) const
{
if(selected)
{
m_renderOrigin.render(renderer, volume, localToWorld);
}
renderer.SetState(m_entity.getEntityClass().m_state_wire, Renderer::eWireframeOnly);
}
void renderWireframe(Renderer& renderer, const VolumeTest& volume, const Matrix4& localToWorld, bool selected) const
{
renderSolid(renderer, volume, localToWorld, selected);
if(g_showNames)
{
renderer.addRenderable(m_renderName, localToWorld);
}
}
void translate(const Vector3& translation)
{
m_origin = origin_translated(m_origin, translation);
}
void rotate(const Quaternion& rotation)
{
m_angles = angles_rotated(m_angles, rotation);
}
void scale(const Vector3& scaling)
{
m_scale = scale_scaled(m_scale, scaling);
}
void snapto(float snap)
{
m_originKey.m_origin = origin_snapped(m_originKey.m_origin, snap);
m_originKey.write(&m_entity);
}
void revertTransform()
{
m_origin = m_originKey.m_origin;
m_angles = m_anglesKey.m_angles;
m_scale = m_scaleKey.m_scale;
}
void freezeTransform()
{
m_originKey.m_origin = m_origin;
m_originKey.write(&m_entity);
m_anglesKey.m_angles = m_angles;
m_anglesKey.write(&m_entity);
m_scaleKey.m_scale = m_scale;
m_scaleKey.write(&m_entity);
}
void transformChanged()
{
revertTransform();
m_evaluateTransform();
updateTransform();
}
typedef MemberCaller<MiscModel, &MiscModel::transformChanged> TransformChangedCaller;
};
class MiscModelInstance : public TargetableInstance, public TransformModifier, public Renderable
{
class TypeCasts
{
InstanceTypeCastTable m_casts;
public:
TypeCasts()
{
m_casts = TargetableInstance::StaticTypeCasts::instance().get();
InstanceStaticCast<MiscModelInstance, Renderable>::install(m_casts);
InstanceStaticCast<MiscModelInstance, Transformable>::install(m_casts);
InstanceIdentityCast<MiscModelInstance>::install(m_casts);
}
InstanceTypeCastTable& get()
{
return m_casts;
}
};
MiscModel& m_contained;
public:
typedef LazyStatic<TypeCasts> StaticTypeCasts;
STRING_CONSTANT(Name, "MiscModelInstance");
MiscModelInstance(const scene::Path& path, scene::Instance* parent, MiscModel& miscmodel) :
TargetableInstance(path, parent, this, StaticTypeCasts::instance().get(), miscmodel.getEntity(), *this),
TransformModifier(MiscModel::TransformChangedCaller(miscmodel), ApplyTransformCaller(*this)),
m_contained(miscmodel)
{
m_contained.instanceAttach(Instance::path());
StaticRenderableConnectionLines::instance().attach(*this);
}
~MiscModelInstance()
{
StaticRenderableConnectionLines::instance().detach(*this);
m_contained.instanceDetach(Instance::path());
}
void renderSolid(Renderer& renderer, const VolumeTest& volume) const
{
m_contained.renderSolid(renderer, volume, Instance::localToWorld(), getSelectable().isSelected());
}
void renderWireframe(Renderer& renderer, const VolumeTest& volume) const
{
m_contained.renderWireframe(renderer, volume, Instance::localToWorld(), getSelectable().isSelected());
}
void evaluateTransform()
{
if(getType() == TRANSFORM_PRIMITIVE)
{
m_contained.translate(getTranslation());
m_contained.rotate(getRotation());
m_contained.scale(getScale());
}
}
void applyTransform()
{
m_contained.revertTransform();
evaluateTransform();
m_contained.freezeTransform();
}
typedef MemberCaller<MiscModelInstance, &MiscModelInstance::applyTransform> ApplyTransformCaller;
};
class MiscModelNode :
public scene::Node::Symbiot,
public scene::Instantiable,
public scene::Cloneable,
public scene::Traversable::Observer
{
class TypeCasts
{
NodeTypeCastTable m_casts;
public:
TypeCasts()
{
NodeStaticCast<MiscModelNode, scene::Instantiable>::install(m_casts);
NodeStaticCast<MiscModelNode, scene::Cloneable>::install(m_casts);
NodeContainedCast<MiscModelNode, scene::Traversable>::install(m_casts);
NodeContainedCast<MiscModelNode, Snappable>::install(m_casts);
NodeContainedCast<MiscModelNode, TransformNode>::install(m_casts);
NodeContainedCast<MiscModelNode, Entity>::install(m_casts);
NodeContainedCast<MiscModelNode, Nameable>::install(m_casts);
NodeContainedCast<MiscModelNode, Namespaced>::install(m_casts);
}
NodeTypeCastTable& get()
{
return m_casts;
}
};
scene::Node m_node;
InstanceSet m_instances;
MiscModel m_contained;
void construct()
{
m_contained.attach(this);
}
void destroy()
{
m_contained.detach(this);
}
public:
typedef LazyStatic<TypeCasts> StaticTypeCasts;
scene::Traversable& get(NullType<scene::Traversable>)
{
return m_contained.getTraversable();
}
Snappable& get(NullType<Snappable>)
{
return m_contained;
}
TransformNode& get(NullType<TransformNode>)
{
return m_contained.getTransformNode();
}
Entity& get(NullType<Entity>)
{
return m_contained.getEntity();
}
Nameable& get(NullType<Nameable>)
{
return m_contained.getNameable();
}
Namespaced& get(NullType<Namespaced>)
{
return m_contained.getNamespaced();
}
MiscModelNode(EntityClass* eclass) :
m_node(this, this, StaticTypeCasts::instance().get()),
m_contained(eclass, m_node, InstanceSet::TransformChangedCaller(m_instances), InstanceSetEvaluateTransform<MiscModelInstance>::Caller(m_instances))
{
construct();
}
MiscModelNode(const MiscModelNode& other) :
scene::Node::Symbiot(other),
scene::Instantiable(other),
scene::Cloneable(other),
scene::Traversable::Observer(other),
m_node(this, this, StaticTypeCasts::instance().get()),
m_contained(other.m_contained, m_node, InstanceSet::TransformChangedCaller(m_instances), InstanceSetEvaluateTransform<MiscModelInstance>::Caller(m_instances))
{
construct();
}
~MiscModelNode()
{
destroy();
}
void release()
{
delete this;
}
scene::Node& node()
{
return m_node;
}
scene::Node& clone() const
{
return (new MiscModelNode(*this))->node();
}
void insert(scene::Node& child)
{
m_instances.insert(child);
}
void erase(scene::Node& child)
{
m_instances.erase(child);
}
scene::Instance* create(const scene::Path& path, scene::Instance* parent)
{
return new MiscModelInstance(path, parent, m_contained);
}
void forEachInstance(const scene::Instantiable::Visitor& visitor)
{
m_instances.forEachInstance(visitor);
}
void insert(scene::Instantiable::Observer* observer, const scene::Path& path, scene::Instance* instance)
{
m_instances.insert(observer, path, instance);
}
scene::Instance* erase(scene::Instantiable::Observer* observer, const scene::Path& path)
{
return m_instances.erase(observer, path);
}
};
scene::Node& New_MiscModel(EntityClass* eclass)
{
return (new MiscModelNode(eclass))->node();
}
void MiscModel_construct()
{
}
void MiscModel_destroy()
{
}

View File

@@ -0,0 +1,29 @@
/*
Copyright (C) 2001-2006, William Joseph.
All Rights Reserved.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#if !defined(INCLUDED_MISCMODEL_H)
#define INCLUDED_MISCMODEL_H
scene::Node& New_MiscModel(EntityClass* eclass);
void MiscModel_construct();
void MiscModel_destroy();
#endif

23
plugins/entity/model.cpp Normal file
View File

@@ -0,0 +1,23 @@
/*
Copyright (C) 2001-2006, William Joseph.
All Rights Reserved.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "model.h"

124
plugins/entity/model.h Normal file
View File

@@ -0,0 +1,124 @@
/*
Copyright (C) 2001-2006, William Joseph.
All Rights Reserved.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#if !defined(INCLUDED_MODEL_H)
#define INCLUDED_MODEL_H
#include "entitylib.h"
#include "traverselib.h"
#include "generic/callback.h"
#include "stream/stringstream.h"
#include "os/path.h"
#include "moduleobserver.h"
class Model : public ModuleObserver
{
ResourceReference m_resource;
scene::Traversable& m_traverse;
scene::Node* m_node;
Callback m_modelChanged;
public:
Model(scene::Traversable& traversable, const Callback& modelChanged)
: m_resource(""), m_traverse(traversable), m_node(0), m_modelChanged(modelChanged)
{
m_resource.attach(*this);
}
~Model()
{
m_resource.detach(*this);
}
void realise()
{
m_resource.get()->load();
m_node = m_resource.get()->getNode();
if(m_node != 0)
{
m_traverse.insert(*m_node);
}
}
void unrealise()
{
if(m_node != 0)
{
m_traverse.erase(*m_node);
}
}
void modelChanged(const char* value)
{
StringOutputStream cleaned(string_length(value));
cleaned << PathCleaned(value);
m_resource.detach(*this);
m_resource.setName(cleaned.c_str());
m_resource.attach(*this);
m_modelChanged();
}
typedef MemberCaller1<Model, const char*, &Model::modelChanged> ModelChangedCaller;
const char* getName() const
{
return m_resource.getName();
}
scene::Node* getNode() const
{
return m_node;
}
};
class SingletonModel
{
TraversableNode m_traverse;
Model m_model;
public:
SingletonModel()
: m_model(m_traverse, Callback())
{
}
void attach(scene::Traversable::Observer* observer)
{
m_traverse.attach(observer);
}
void detach(scene::Traversable::Observer* observer)
{
m_traverse.detach(observer);
}
scene::Traversable& getTraversable()
{
return m_traverse;
}
void modelChanged(const char* value)
{
m_model.modelChanged(value);
}
typedef MemberCaller1<SingletonModel, const char*, &SingletonModel::modelChanged> ModelChangedCaller;
scene::Node* getNode() const
{
return m_model.getNode();
}
};
#endif

View File

@@ -0,0 +1,23 @@
/*
Copyright (C) 2001-2006, William Joseph.
All Rights Reserved.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "modelskinkey.h"

View File

@@ -0,0 +1,111 @@
/*
Copyright (C) 2001-2006, William Joseph.
All Rights Reserved.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#if !defined(INCLUDED_MODELSKINKEY_H)
#define INCLUDED_MODELSKINKEY_H
#include "modelskin.h"
#include "os/path.h"
#include "stream/stringstream.h"
#include "moduleobserver.h"
#include "entitylib.h"
#include "traverselib.h"
inline void parseTextureName(CopiedString& name, const char* token)
{
StringOutputStream cleaned(256);
cleaned << PathCleaned(token);
name = CopiedString(cleaned.c_str(), path_get_filename_base_end(cleaned.c_str())); // remove extension
}
class ModelSkinKey : public ModuleObserver
{
CopiedString m_name;
ModelSkin* m_skin;
Callback m_skinChangedCallback;
ModelSkinKey(const ModelSkinKey&);
ModelSkinKey operator=(const ModelSkinKey&);
void construct()
{
m_skin = &GlobalModelSkinCache().capture(m_name.c_str());
m_skin->attach(*this);
}
void destroy()
{
m_skin->detach(*this);
GlobalModelSkinCache().release(m_name.c_str());
}
public:
ModelSkinKey(const Callback& skinChangedCallback) : m_skinChangedCallback(skinChangedCallback)
{
construct();
}
~ModelSkinKey()
{
destroy();
}
ModelSkin& get() const
{
return *m_skin;
}
void skinChanged(const char* value)
{
destroy();
parseTextureName(m_name, value);
construct();
}
typedef MemberCaller1<ModelSkinKey, const char*, &ModelSkinKey::skinChanged> SkinChangedCaller;
void realise()
{
m_skinChangedCallback();
}
void unrealise()
{
}
};
class InstanceSkinChanged : public scene::Instantiable::Visitor
{
public:
void visit(scene::Instance& instance) const
{
//\todo don't do this for instances that are not children of the entity setting the skin
SkinnedModel* skinned = InstanceTypeCast<SkinnedModel>::cast(instance);
if(skinned != 0)
{
skinned->skinChanged();
}
}
};
inline void Node_modelSkinChanged(scene::Node& node)
{
scene::Instantiable* instantiable = Node_getInstantiable(node);
ASSERT_NOTNULL(instantiable);
instantiable->forEachInstance(InstanceSkinChanged());
}
#endif

View File

@@ -0,0 +1,23 @@
/*
Copyright (C) 2001-2006, William Joseph.
All Rights Reserved.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "namedentity.h"

View File

@@ -0,0 +1,113 @@
/*
Copyright (C) 2001-2006, William Joseph.
All Rights Reserved.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#if !defined(INCLUDED_NAMEDENTITY_H)
#define INCLUDED_NAMEDENTITY_H
#include "entitylib.h"
#include "eclasslib.h"
#include "generic/callback.h"
#include "nameable.h"
#include <set>
class NameCallbackSet
{
typedef std::set<NameCallback> NameCallbacks;
NameCallbacks m_callbacks;
public:
void insert(const NameCallback& callback)
{
m_callbacks.insert(callback);
}
void erase(const NameCallback& callback)
{
m_callbacks.erase(callback);
}
void changed(const char* name) const
{
for(NameCallbacks::const_iterator i = m_callbacks.begin(); i != m_callbacks.end(); ++i)
{
(*i)(name);
}
}
};
class NamedEntity : public Nameable
{
EntityKeyValues& m_entity;
NameCallbackSet m_changed;
CopiedString m_name;
public:
NamedEntity(EntityKeyValues& entity) : m_entity(entity)
{
}
const char* name() const
{
if(string_empty(m_name.c_str()))
{
return m_entity.getEntityClass().name();
}
return m_name.c_str();
}
void attach(const NameCallback& callback)
{
m_changed.insert(callback);
}
void detach(const NameCallback& callback)
{
m_changed.erase(callback);
}
void identifierChanged(const char* value)
{
if(string_empty(value))
{
m_changed.changed(m_entity.getEntityClass().name());
}
else
{
m_changed.changed(value);
}
m_name = value;
}
typedef MemberCaller1<NamedEntity, const char*, &NamedEntity::identifierChanged> IdentifierChangedCaller;
};
class RenderableNamedEntity : public OpenGLRenderable
{
const NamedEntity& m_named;
const Vector3& m_position;
public:
RenderableNamedEntity(const NamedEntity& named, const Vector3& position)
: m_named(named), m_position(position)
{
}
void render(RenderStateFlags state) const
{
glRasterPos3fv(vector3_to_array(m_position));
GlobalOpenGL().drawString(m_named.name());
}
};
#endif

View File

@@ -0,0 +1,23 @@
/*
Copyright (C) 2001-2006, William Joseph.
All Rights Reserved.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "namekeys.h"

148
plugins/entity/namekeys.h Normal file
View File

@@ -0,0 +1,148 @@
/*
Copyright (C) 2001-2006, William Joseph.
All Rights Reserved.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#if !defined(INCLUDED_NAMEKEYS_H)
#define INCLUDED_NAMEKEYS_H
#include <stdio.h>
#include <map>
#include "generic/static.h"
#include "entitylib.h"
#include "namespace.h"
inline bool string_is_integer(const char* string)
{
strtol(string, const_cast<char**>(&string), 10);
return *string == '\0';
}
typedef bool (*KeyIsNameFunc)(const char* key);
class KeyIsName
{
public:
KeyIsNameFunc m_keyIsName;
const char* m_nameKey;
KeyIsName()
{
}
};
typedef MemberCaller1<KeyValue, const char*, &KeyValue::assign> KeyValueAssignCaller;
typedef MemberCaller1<KeyValue, const KeyObserver&, &KeyValue::attach> KeyValueAttachCaller;
typedef MemberCaller1<KeyValue, const KeyObserver&, &KeyValue::detach> KeyValueDetachCaller;
class NameKeys : public EntityKeyValues::Observer, public Namespaced
{
Namespace* m_namespace;
EntityKeyValues& m_entity;
KeyIsNameFunc m_keyIsName;
NameKeys(const NameKeys& other);
NameKeys& operator=(const NameKeys& other);
typedef std::map<CopiedString, EntityKeyValues::Value*> KeyValues;
KeyValues m_keyValues;
void insertName(const char* key, EntityKeyValues::Value& value)
{
if(m_namespace != 0 && m_keyIsName(key))
{
//globalOutputStream() << "insert " << key << "\n";
m_namespace->attach(KeyValueAssignCaller(value), KeyValueAttachCaller(value));
}
}
void eraseName(const char* key, EntityKeyValues::Value& value)
{
if(m_namespace != 0 && m_keyIsName(key))
{
//globalOutputStream() << "erase " << key << "\n";
m_namespace->detach(KeyValueAssignCaller(value), KeyValueDetachCaller(value));
}
}
void insertAll()
{
for(KeyValues::iterator i = m_keyValues.begin(); i != m_keyValues.end(); ++i)
{
insertName((*i).first.c_str(), *(*i).second);
}
}
void eraseAll()
{
for(KeyValues::iterator i = m_keyValues.begin(); i != m_keyValues.end(); ++i)
{
eraseName((*i).first.c_str(), *(*i).second);
}
}
public:
NameKeys(EntityKeyValues& entity) : m_namespace(0), m_entity(entity), m_keyIsName(Static<KeyIsName>::instance().m_keyIsName)
{
m_entity.attach(*this);
}
~NameKeys()
{
m_entity.detach(*this);
}
void setNamespace(Namespace& space)
{
eraseAll();
m_namespace = &space;
insertAll();
}
void setKeyIsName(KeyIsNameFunc keyIsName)
{
eraseAll();
m_keyIsName = keyIsName;
insertAll();
}
void insert(const char* key, EntityKeyValues::Value& value)
{
m_keyValues.insert(KeyValues::value_type(key, &value));
insertName(key, value);
}
void erase(const char* key, EntityKeyValues::Value& value)
{
eraseName(key, value);
m_keyValues.erase(key);
}
};
inline bool keyIsNameDoom3(const char* key)
{
return string_equal(key, "target")
|| (string_equal_n(key, "target", 6) && string_is_integer(key + 6))
|| string_equal(key, "name");
}
inline bool keyIsNameDoom3Doom3Group(const char* key)
{
return keyIsNameDoom3(key)
|| string_equal(key, "model");
}
inline bool keyIsNameQuake3(const char* key)
{
return string_equal(key, "target")
|| string_equal(key, "targetname");
}
#endif

23
plugins/entity/origin.cpp Normal file
View File

@@ -0,0 +1,23 @@
/*
Copyright (C) 2001-2006, William Joseph.
All Rights Reserved.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "origin.h"

169
plugins/entity/origin.h Normal file
View File

@@ -0,0 +1,169 @@
/*
Copyright (C) 2001-2006, William Joseph.
All Rights Reserved.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#if !defined(INCLUDED_ORIGIN_H)
#define INCLUDED_ORIGIN_H
#include "ientity.h"
#include "math/matrix.h"
#include "generic/callback.h"
#include "stringio.h"
const Vector3 ORIGINKEY_IDENTITY = Vector3(0, 0, 0);
inline void default_origin(Vector3& origin)
{
origin = ORIGINKEY_IDENTITY;
}
inline void read_origin(Vector3& origin, const char* value)
{
if(!string_parse_vector3(value, origin))
{
default_origin(origin);
}
}
inline void write_origin(const Vector3& origin, Entity* entity, const char* key)
{
char value[64];
sprintf(value, "%g %g %g", origin[0], origin[1], origin[2]);
entity->setKeyValue(key, value);
}
inline Vector3 origin_translated(const Vector3& origin, const Vector3& translation)
{
return matrix4_get_translation_vec3(
matrix4_multiplied_by_matrix4(
matrix4_translation_for_vec3(origin),
matrix4_translation_for_vec3(translation)
)
);
}
inline Vector3 origin_snapped(const Vector3& origin, float snap)
{
return vector3_snapped(origin, snap);
}
class OriginKey
{
Callback m_originChanged;
public:
Vector3 m_origin;
OriginKey(const Callback& originChanged)
: m_originChanged(originChanged), m_origin(ORIGINKEY_IDENTITY)
{
}
void originChanged(const char* value)
{
read_origin(m_origin, value);
m_originChanged();
}
typedef MemberCaller1<OriginKey, const char*, &OriginKey::originChanged> OriginChangedCaller;
void write(Entity* entity) const
{
write_origin(m_origin, entity, "origin");
}
};
#include "scenelib.h"
inline BrushDoom3* Node_getBrushDoom3(scene::Node& node)
{
return NodeTypeCast<BrushDoom3>::cast(node);
}
inline void BrushDoom3_setDoom3GroupOrigin(scene::Node& node, const Vector3& origin)
{
BrushDoom3* brush = Node_getBrushDoom3(node);
if(brush != 0)
{
brush->setDoom3GroupOrigin(origin);
}
}
class SetDoom3GroupOriginWalker : public scene::Traversable::Walker
{
const Vector3& m_origin;
public:
SetDoom3GroupOriginWalker(const Vector3& origin) : m_origin(origin)
{
}
bool pre(scene::Node& node) const
{
BrushDoom3_setDoom3GroupOrigin(node, m_origin);
return true;
}
};
class Doom3GroupOrigin : public scene::Traversable::Observer
{
scene::Traversable& m_set;
const Vector3& m_origin;
bool m_enabled;
public:
Doom3GroupOrigin(scene::Traversable& set, const Vector3& origin) : m_set(set), m_origin(origin), m_enabled(false)
{
}
void enable()
{
m_enabled = true;
originChanged();
}
void disable()
{
m_enabled = false;
}
void originChanged()
{
if(m_enabled)
{
m_set.traverse(SetDoom3GroupOriginWalker(m_origin));
}
}
void insert(scene::Node& node)
{
if(m_enabled)
{
BrushDoom3_setDoom3GroupOrigin(node, m_origin);
}
}
void erase(scene::Node& node)
{
if(m_enabled)
{
BrushDoom3_setDoom3GroupOrigin(node, Vector3(0, 0, 0));
}
}
};
#endif

163
plugins/entity/plugin.cpp Normal file
View File

@@ -0,0 +1,163 @@
/*
Copyright (C) 2001-2006, William Joseph.
All Rights Reserved.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "plugin.h"
#include "debugging/debugging.h"
#include "iscenegraph.h"
#include "irender.h"
#include "iselection.h"
#include "ientity.h"
#include "iundo.h"
#include "ieclass.h"
#include "igl.h"
#include "ireference.h"
#include "ifilter.h"
#include "preferencesystem.h"
#include "qerplugin.h"
#include "namespace.h"
#include "modelskin.h"
#include "typesystem.h"
#include "entity.h"
#include "skincache.h"
#include "modulesystem/singletonmodule.h"
class EntityDependencies :
public GlobalRadiantModuleRef,
public GlobalOpenGLModuleRef,
public GlobalUndoModuleRef,
public GlobalSceneGraphModuleRef,
public GlobalShaderCacheModuleRef,
public GlobalSelectionModuleRef,
public GlobalReferenceModuleRef,
public GlobalFilterModuleRef,
public GlobalPreferenceSystemModuleRef,
public GlobalNamespaceModuleRef,
public GlobalModelSkinCacheModuleRef
{
};
class EntityQ3API : public TypeSystemRef
{
EntityCreator* m_entityq3;
public:
typedef EntityCreator Type;
STRING_CONSTANT(Name, "quake3");
EntityQ3API()
{
Entity_Construct();
m_entityq3 = &GetEntityCreator();
GlobalReferenceCache().setEntityCreator(*m_entityq3);
}
~EntityQ3API()
{
Entity_Destroy();
}
EntityCreator* getTable()
{
return m_entityq3;
}
};
typedef SingletonModule<EntityQ3API, EntityDependencies> EntityQ3Module;
EntityQ3Module g_EntityQ3Module;
class EntityWolfAPI : public TypeSystemRef
{
EntityCreator* m_entitywolf;
public:
typedef EntityCreator Type;
STRING_CONSTANT(Name, "wolf");
EntityWolfAPI()
{
Entity_Construct(eGameTypeRTCW);
m_entitywolf = &GetEntityCreator();
GlobalReferenceCache().setEntityCreator(*m_entitywolf);
}
~EntityWolfAPI()
{
Entity_Destroy();
}
EntityCreator* getTable()
{
return m_entitywolf;
}
};
typedef SingletonModule<EntityWolfAPI, EntityDependencies> EntityWolfModule;
EntityWolfModule g_EntityWolfModule;
class EntityDoom3API : public TypeSystemRef
{
EntityCreator* m_entitydoom3;
public:
typedef EntityCreator Type;
STRING_CONSTANT(Name, "doom3");
EntityDoom3API()
{
Entity_Construct(eGameTypeDoom3);
m_entitydoom3 = &GetEntityCreator();
GlobalReferenceCache().setEntityCreator(*m_entitydoom3);
}
~EntityDoom3API()
{
Entity_Destroy();
}
EntityCreator* getTable()
{
return m_entitydoom3;
}
};
typedef SingletonModule<EntityDoom3API, EntityDependencies> EntityDoom3Module;
EntityDoom3Module g_EntityDoom3Module;
extern "C" void RADIANT_DLLEXPORT Radiant_RegisterModules(ModuleServer& server)
{
GlobalErrorStream::instance().setOutputStream(server.getErrorStream());
GlobalOutputStream::instance().setOutputStream(server.getOutputStream());
GlobalDebugMessageHandler::instance().setHandler(server.getDebugMessageHandler());
GlobalModuleServer::instance().set(server);
g_EntityQ3Module.selfRegister();
g_EntityWolfModule.selfRegister();
g_EntityDoom3Module.selfRegister();
Doom3ModelSkinCacheModule_selfRegister(server);
}

25
plugins/entity/plugin.h Normal file
View File

@@ -0,0 +1,25 @@
/*
Copyright (C) 2001-2006, William Joseph.
All Rights Reserved.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#if !defined(INCLUDED_PLUGIN_H)
#define INCLUDED_PLUGIN_H
#endif

View File

@@ -0,0 +1,23 @@
/*
Copyright (C) 2001-2006, William Joseph.
All Rights Reserved.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "rotation.h"

199
plugins/entity/rotation.h Normal file
View File

@@ -0,0 +1,199 @@
/*
Copyright (C) 2001-2006, William Joseph.
All Rights Reserved.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#if !defined(INCLUDED_ROTATION_H)
#define INCLUDED_ROTATION_H
#include "ientity.h"
#include "stream/stringstream.h"
#include "math/quaternion.h"
#include "generic/callback.h"
#include "stringio.h"
#include "angle.h"
typedef float Float9[9];
inline void default_rotation(Float9 rotation)
{
rotation[0] = 1;
rotation[1] = 0;
rotation[2] = 0;
rotation[3] = 0;
rotation[4] = 1;
rotation[5] = 0;
rotation[6] = 0;
rotation[7] = 0;
rotation[8] = 1;
}
inline void write_rotation(const Float9 rotation, Entity* entity, const char* key = "rotation")
{
if(rotation[0] == 1
&& rotation[1] == 0
&& rotation[2] == 0
&& rotation[3] == 0
&& rotation[4] == 1
&& rotation[5] == 0
&& rotation[6] == 0
&& rotation[7] == 0
&& rotation[8] == 1)
{
entity->setKeyValue(key, "");
}
else
{
StringOutputStream value(256);
value << rotation[0] << ' '
<< rotation[1] << ' '
<< rotation[2] << ' '
<< rotation[3] << ' '
<< rotation[4] << ' '
<< rotation[5] << ' '
<< rotation[6] << ' '
<< rotation[7] << ' '
<< rotation[8];
entity->setKeyValue(key, value.c_str());
}
}
inline void read_rotation(Float9 rotation, const char* value)
{
if(!string_parse_vector(value, rotation, rotation + 9))
{
default_rotation(rotation);
}
}
inline Matrix4 rotation_toMatrix(const Float9 rotation)
{
return Matrix4(
rotation[0],
rotation[1],
rotation[2],
0,
rotation[3],
rotation[4],
rotation[5],
0,
rotation[6],
rotation[7],
rotation[8],
0,
0,
0,
0,
1
);
}
inline void rotation_fromMatrix(Float9 rotation, const Matrix4& matrix)
{
rotation[0] = matrix.xx();
rotation[1] = matrix.xy();
rotation[2] = matrix.xz();
rotation[3] = matrix.yx();
rotation[4] = matrix.yy();
rotation[5] = matrix.yz();
rotation[6] = matrix.zx();
rotation[7] = matrix.zy();
rotation[8] = matrix.zz();
}
inline void rotation_assign(Float9 rotation, const Float9 other)
{
rotation[0] = other[0];
rotation[1] = other[1];
rotation[2] = other[2];
rotation[3] = other[3];
rotation[4] = other[4];
rotation[5] = other[5];
rotation[6] = other[6];
rotation[7] = other[7];
rotation[8] = other[8];
}
inline void rotation_rotate(Float9 rotation, const Quaternion& rotate)
{
rotation_fromMatrix(rotation,
matrix4_multiplied_by_matrix4(
rotation_toMatrix(rotation),
matrix4_rotation_for_quaternion_quantised(rotate)
)
);
}
inline void read_angle(Float9 rotation, const char* value)
{
float angle;
if(!string_parse_float(value, angle))
{
default_rotation(rotation);
}
else
{
rotation_fromMatrix(rotation, matrix4_rotation_for_z_degrees(angle));
}
}
class RotationKey
{
Callback m_rotationChanged;
public:
Float9 m_rotation;
RotationKey(const Callback& rotationChanged)
: m_rotationChanged(rotationChanged)
{
default_rotation(m_rotation);
}
void angleChanged(const char* value)
{
read_angle(m_rotation, value);
m_rotationChanged();
}
typedef MemberCaller1<RotationKey, const char*, &RotationKey::angleChanged> AngleChangedCaller;
void rotationChanged(const char* value)
{
read_rotation(m_rotation, value);
m_rotationChanged();
}
typedef MemberCaller1<RotationKey, const char*, &RotationKey::rotationChanged> RotationChangedCaller;
void write(Entity* entity) const
{
Vector3 euler = matrix4_get_rotation_euler_xyz_degrees(rotation_toMatrix(m_rotation));
if(euler[0] == 0 && euler[1] == 0)
{
entity->setKeyValue("rotation", "");
write_angle(euler[2], entity);
}
else
{
entity->setKeyValue("angle", "");
write_rotation(m_rotation, entity);
}
}
};
#endif

23
plugins/entity/scale.cpp Normal file
View File

@@ -0,0 +1,23 @@
/*
Copyright (C) 2001-2006, William Joseph.
All Rights Reserved.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "scale.h"

128
plugins/entity/scale.h Normal file
View File

@@ -0,0 +1,128 @@
/*
Copyright (C) 2001-2006, William Joseph.
All Rights Reserved.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#if !defined(INCLUDED_SCALE_H)
#define INCLUDED_SCALE_H
#include "ientity.h"
#include "math/matrix.h"
#include "generic/callback.h"
#include "stringio.h"
const Vector3 SCALEKEY_IDENTITY = Vector3(1, 1, 1);
inline void default_scale(Vector3& scale)
{
scale = SCALEKEY_IDENTITY;
}
inline void read_scale(Vector3& scalevec, const char* value)
{
float scale;
if(!string_parse_float(value, scale)
|| scale == 0)
{
default_scale(scalevec);
}
else
{
scalevec = Vector3(scale, scale, scale);
}
}
inline void read_scalevec(Vector3& scale, const char* value)
{
if(!string_parse_vector3(value, scale)
|| scale[0] == 0
|| scale[1] == 0
|| scale[2] == 0)
default_scale(scale);
}
inline void write_scale(const Vector3& scale, Entity* entity)
{
if(scale[0] == 1 && scale[1] == 1 && scale[2] == 1)
{
entity->setKeyValue("modelscale", "");
entity->setKeyValue("modelscale_vec", "");
}
else
{
char value[64];
if(scale[0] == scale[1] && scale[0] == scale[2])
{
sprintf(value, "%g", scale[0]);
entity->setKeyValue("modelscale_vec", "");
entity->setKeyValue("modelscale", value);
}
else
{
sprintf(value, "%g %g %g", scale[0], scale[1], scale[2]);
entity->setKeyValue("modelscale", "");
entity->setKeyValue("modelscale_vec", value);
}
}
}
inline Vector3 scale_scaled(const Vector3& scale, const Vector3& scaling)
{
return matrix4_get_scale_vec3(
matrix4_multiplied_by_matrix4(
matrix4_scale_for_vec3(scale),
matrix4_scale_for_vec3(scaling)
)
);
}
class ScaleKey
{
Callback m_scaleChanged;
public:
Vector3 m_scale;
ScaleKey(const Callback& scaleChanged)
: m_scaleChanged(scaleChanged), m_scale(SCALEKEY_IDENTITY)
{
}
void uniformScaleChanged(const char* value)
{
read_scale(m_scale, value);
m_scaleChanged();
}
typedef MemberCaller1<ScaleKey, const char*, &ScaleKey::uniformScaleChanged> UniformScaleChangedCaller;
void scaleChanged(const char* value)
{
read_scalevec(m_scale, value);
m_scaleChanged();
}
typedef MemberCaller1<ScaleKey, const char*, &ScaleKey::scaleChanged> ScaleChangedCaller;
void write(Entity* entity) const
{
write_scale(m_scale, entity);
}
};
#endif

View File

@@ -0,0 +1,348 @@
/*
Copyright (C) 2001-2006, William Joseph.
All Rights Reserved.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "skincache.h"
#include "ifilesystem.h"
#include "iscriplib.h"
#include "iarchive.h"
#include "modelskin.h"
#include <map>
#include "stream/stringstream.h"
#include "generic/callback.h"
#include "container/cache.h"
#include "container/hashfunc.h"
#include "os/path.h"
#include "moduleobservers.h"
#include "modulesystem/singletonmodule.h"
#include "stringio.h"
void parseShaderName(CopiedString& name, const char* token)
{
StringOutputStream cleaned(256);
cleaned << PathCleaned(token);
name = cleaned.c_str();
}
class Doom3ModelSkin
{
typedef std::map<CopiedString, CopiedString> Remaps;
Remaps m_remaps;
public:
bool parseTokens(Tokeniser& tokeniser)
{
RETURN_FALSE_IF_FAIL(Tokeniser_parseToken(tokeniser, "{"));
tokeniser.nextLine();
for(;;)
{
const char* token = tokeniser.getToken();
if(token == 0)
{
return false;
}
if(string_equal(token, "}"))
{
tokeniser.nextLine();
return true;
}
else if(string_equal(token, "model"))
{
//const char* model =
tokeniser.getToken();
}
else
{
CopiedString from, to;
parseShaderName(from, token);
tokeniser.nextLine(); // hack to handle badly formed skins
parseShaderName(to, tokeniser.getToken());
if(!string_equal(from.c_str(), to.c_str()))
{
m_remaps.insert(Remaps::value_type(from, to));
}
}
tokeniser.nextLine();
}
}
const char* getRemap(const char* name) const
{
Remaps::const_iterator i = m_remaps.find(name);
if(i != m_remaps.end())
{
return (*i).second.c_str();
}
return "";
}
void forEachRemap(const SkinRemapCallback& callback) const
{
for(Remaps::const_iterator i = m_remaps.begin(); i != m_remaps.end(); ++i)
{
callback(SkinRemap((*i).first.c_str(), (*i).second.c_str()));
}
}
};
class GlobalSkins
{
public:
typedef std::map<CopiedString, Doom3ModelSkin> SkinMap;
SkinMap m_skins;
Doom3ModelSkin g_nullSkin;
Doom3ModelSkin& getSkin(const char* name)
{
SkinMap::iterator i = m_skins.find(name);
if(i != m_skins.end())
{
return (*i).second;
}
return g_nullSkin;
}
bool parseTokens(Tokeniser& tokeniser)
{
tokeniser.nextLine();
for(;;)
{
const char* token = tokeniser.getToken();
if(token == 0)
{
// end of token stream
return true;
}
if(!string_equal(token, "skin"))
{
Tokeniser_unexpectedError(tokeniser, token, "skin");
return false;
}
const char* other = tokeniser.getToken();
if(other == 0)
{
Tokeniser_unexpectedError(tokeniser, token, "#string");
return false;
}
CopiedString name;
parseShaderName(name, other);
Doom3ModelSkin& skin = m_skins[name];
RETURN_FALSE_IF_FAIL(skin.parseTokens(tokeniser));
}
}
void parseFile(const char* name)
{
StringOutputStream relativeName(64);
relativeName << "skins/" << name;
ArchiveTextFile* file = GlobalFileSystem().openTextFile(relativeName.c_str());
if(file != 0)
{
globalOutputStream() << "parsing skins from " << makeQuoted(name) << "\n";
{
Tokeniser& tokeniser = GlobalScriptLibrary().m_pfnNewSimpleTokeniser(file->getInputStream());
parseTokens(tokeniser);
tokeniser.release();
}
file->release();
}
else
{
globalErrorStream() << "failed to open " << makeQuoted(name) << "\n";
}
}
typedef MemberCaller1<GlobalSkins, const char*, &GlobalSkins::parseFile> ParseFileCaller;
void construct()
{
GlobalFileSystem().forEachFile("skins/", "skin", ParseFileCaller(*this));
}
void destroy()
{
m_skins.clear();
}
void realise()
{
construct();
}
void unrealise()
{
destroy();
}
};
GlobalSkins g_skins;
class Doom3ModelSkinCacheElement : public ModelSkin
{
ModuleObservers m_observers;
Doom3ModelSkin* m_skin;
public:
Doom3ModelSkinCacheElement() : m_skin(0)
{
}
void attach(ModuleObserver& observer)
{
m_observers.attach(observer);
if(realised())
{
observer.realise();
}
}
void detach(ModuleObserver& observer)
{
if(realised())
{
observer.unrealise();
}
m_observers.detach(observer);
}
bool realised() const
{
return m_skin != 0;
}
void realise(const char* name)
{
ASSERT_MESSAGE(!realised(), "Doom3ModelSkinCacheElement::realise: already realised");
m_skin = &g_skins.getSkin(name);
m_observers.realise();
}
void unrealise()
{
ASSERT_MESSAGE(realised(), "Doom3ModelSkinCacheElement::unrealise: not realised");
m_observers.unrealise();
m_skin = 0;
}
const char* getRemap(const char* name) const
{
ASSERT_MESSAGE(realised(), "Doom3ModelSkinCacheElement::getRemap: not realised");
return m_skin->getRemap(name);
}
void forEachRemap(const SkinRemapCallback& callback) const
{
ASSERT_MESSAGE(realised(), "Doom3ModelSkinCacheElement::forEachRemap: not realised");
m_skin->forEachRemap(callback);
}
};
class Doom3ModelSkinCache : public ModelSkinCache, public ModuleObserver
{
class CreateDoom3ModelSkin
{
Doom3ModelSkinCache& m_cache;
public:
explicit CreateDoom3ModelSkin(Doom3ModelSkinCache& cache)
: m_cache(cache)
{
}
Doom3ModelSkinCacheElement* construct(const CopiedString& name)
{
Doom3ModelSkinCacheElement* skin = new Doom3ModelSkinCacheElement;
if(m_cache.realised())
{
skin->realise(name.c_str());
}
return skin;
}
void destroy(Doom3ModelSkinCacheElement* skin)
{
if(m_cache.realised())
{
skin->unrealise();
}
delete skin;
}
};
typedef HashedCache<CopiedString, Doom3ModelSkinCacheElement, HashString, std::equal_to<CopiedString>, CreateDoom3ModelSkin> Cache;
Cache m_cache;
bool m_realised;
public:
typedef ModelSkinCache Type;
STRING_CONSTANT(Name, "*");
ModelSkinCache* getTable()
{
return this;
}
Doom3ModelSkinCache() : m_cache(CreateDoom3ModelSkin(*this)), m_realised(false)
{
GlobalFileSystem().attach(*this);
}
~Doom3ModelSkinCache()
{
GlobalFileSystem().detach(*this);
}
ModelSkin& capture(const char* name)
{
return *m_cache.capture(name);
}
void release(const char* name)
{
m_cache.release(name);
}
bool realised() const
{
return m_realised;
}
void realise()
{
g_skins.realise();
m_realised = true;
for(Cache::iterator i = m_cache.begin(); i != m_cache.end(); ++i)
{
(*i).value->realise((*i).key.c_str());
}
}
void unrealise()
{
m_realised = false;
for(Cache::iterator i = m_cache.begin(); i != m_cache.end(); ++i)
{
(*i).value->unrealise();
}
g_skins.unrealise();
}
};
class Doom3ModelSkinCacheDependencies : public GlobalFileSystemModuleRef, public GlobalScripLibModuleRef
{
};
typedef SingletonModule<Doom3ModelSkinCache, Doom3ModelSkinCacheDependencies> Doom3ModelSkinCacheModule;
Doom3ModelSkinCacheModule g_Doom3ModelSkinCacheModule;
void Doom3ModelSkinCacheModule_selfRegister(ModuleServer& server)
{
g_Doom3ModelSkinCacheModule.selfRegister();
}

View File

@@ -0,0 +1,28 @@
/*
Copyright (C) 2001-2006, William Joseph.
All Rights Reserved.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#if !defined(INCLUDED_SKINCACHE_H)
#define INCLUDED_SKINCACHE_H
class ModuleServer;
void Doom3ModelSkinCacheModule_selfRegister(ModuleServer& server);
#endif

View File

@@ -0,0 +1,37 @@
/*
Copyright (C) 2001-2006, William Joseph.
All Rights Reserved.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "targetable.h"
typedef std::map<CopiedString, targetables_t> targetnames_t;
const char* g_targetable_nameKey = "targetname";
targetnames_t g_targetnames;
targetables_t* getTargetables(const char* targetname)
{
if(targetname[0] == '\0')
return 0;
return &g_targetnames[targetname];
}
Shader* RenderableTargetingEntity::m_state;

452
plugins/entity/targetable.h Normal file
View File

@@ -0,0 +1,452 @@
/*
Copyright (C) 2001-2006, William Joseph.
All Rights Reserved.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#if !defined(INCLUDED_TARGETABLE_H)
#define INCLUDED_TARGETABLE_H
#include <set>
#include <map>
#include "cullable.h"
#include "renderable.h"
#include "math/line.h"
#include "render.h"
#include "generic/callback.h"
#include "selectionlib.h"
#include "entitylib.h"
#include "eclasslib.h"
#include "stringio.h"
class Targetable
{
public:
virtual const Vector3& world_position() const = 0;
};
typedef std::set<Targetable*> targetables_t;
extern const char* g_targetable_nameKey;
targetables_t* getTargetables(const char* targetname);
class EntityConnectionLine : public OpenGLRenderable
{
public:
Vector3 start;
Vector3 end;
void render(RenderStateFlags state) const
{
float s1[2], s2[2];
Vector3 dir(vector3_subtracted(end, start));
double len = vector3_length(dir);
vector3_scale(dir, 8.0 * (1.0 / len));
s1[0] = dir[0] - dir[1];
s1[1] = dir[0] + dir[1];
s2[0] = dir[0] + dir[1];
s2[1] = -dir[0] + dir[1];
glBegin(GL_LINES);
glVertex3fv(vector3_to_array(start));
glVertex3fv(vector3_to_array(end));
len*=0.0625; // half / 8
Vector3 arrow(start);
for (unsigned int i = 0, count = (len<32)? 1 : static_cast<unsigned int>(len*0.0625); i < count; i++)
{
vector3_add(arrow, vector3_scaled(dir, (len<32)?len:32));
glVertex3fv(vector3_to_array(arrow));
glVertex3f(arrow[0]+s1[0], arrow[1]+s1[1], arrow[2]+dir[2]);
glVertex3fv(vector3_to_array(arrow));
glVertex3f(arrow[0]+s2[0], arrow[1]+s2[1], arrow[2]+dir[2]);
}
glEnd();
}
};
class TargetedEntity
{
Targetable& m_targetable;
targetables_t* m_targets;
void construct()
{
if(m_targets != 0)
m_targets->insert(&m_targetable);
}
void destroy()
{
if(m_targets != 0)
m_targets->erase(&m_targetable);
}
public:
TargetedEntity(Targetable& targetable)
: m_targetable(targetable), m_targets(getTargetables(""))
{
construct();
}
~TargetedEntity()
{
destroy();
}
void targetnameChanged(const char* name)
{
destroy();
m_targets = getTargetables(name);
construct();
}
typedef MemberCaller1<TargetedEntity, const char*, &TargetedEntity::targetnameChanged> TargetnameChangedCaller;
};
class TargetingEntity
{
targetables_t* m_targets;
public:
TargetingEntity() :
m_targets(getTargetables(""))
{
}
void targetChanged(const char* target)
{
m_targets = getTargetables(target);
}
typedef MemberCaller1<TargetingEntity, const char*, &TargetingEntity::targetChanged> TargetChangedCaller;
typedef targetables_t::iterator iterator;
iterator begin() const
{
if(m_targets == 0)
{
return iterator();
}
return m_targets->begin();
}
iterator end() const
{
if(m_targets == 0)
{
return iterator();
}
return m_targets->end();
}
size_t size() const
{
if(m_targets == 0)
{
return 0;
}
return m_targets->size();
}
bool empty() const
{
return m_targets == 0 || m_targets->empty();
}
};
template<typename Functor>
void TargetingEntity_forEach(const TargetingEntity& targets, const Functor& functor)
{
for(TargetingEntity::iterator i = targets.begin(); i != targets.end(); ++i)
{
functor((*i)->world_position());
}
}
typedef std::map<std::size_t, TargetingEntity> TargetingEntities;
template<typename Functor>
void TargetingEntities_forEach(const TargetingEntities& targetingEntities, const Functor& functor)
{
for(TargetingEntities::const_iterator i = targetingEntities.begin(); i != targetingEntities.end(); ++i)
{
TargetingEntity_forEach((*i).second, functor);
}
}
class TargetLinesPushBack
{
RenderablePointVector& m_targetLines;
const Vector3& m_worldPosition;
const VolumeTest& m_volume;
public:
TargetLinesPushBack(RenderablePointVector& targetLines, const Vector3& worldPosition, const VolumeTest& volume) :
m_targetLines(targetLines), m_worldPosition(worldPosition), m_volume(volume)
{
}
void operator()(const Vector3& worldPosition) const
{
if(m_volume.TestLine(segment_for_startend(m_worldPosition, worldPosition)))
{
m_targetLines.push_back(PointVertex(reinterpret_cast<const Vertex3f&>(m_worldPosition)));
m_targetLines.push_back(PointVertex(reinterpret_cast<const Vertex3f&>(worldPosition)));
}
}
};
class TargetKeys : public EntityKeyValues::Observer
{
TargetingEntities m_targetingEntities;
Callback m_targetsChanged;
bool readTargetKey(const char* key, std::size_t& index)
{
if(string_equal_n(key, "target", 6))
{
index = 0;
if(string_empty(key + 6) || string_parse_size(key + 6, index))
{
return true;
}
}
return false;
}
public:
void setTargetsChanged(const Callback& targetsChanged)
{
m_targetsChanged = targetsChanged;
}
void targetsChanged()
{
m_targetsChanged();
}
void insert(const char* key, EntityKeyValues::Value& value)
{
std::size_t index;
if(readTargetKey(key, index))
{
TargetingEntities::iterator i = m_targetingEntities.insert(TargetingEntities::value_type(index, TargetingEntity())).first;
value.attach(TargetingEntity::TargetChangedCaller((*i).second));
targetsChanged();
}
}
void erase(const char* key, EntityKeyValues::Value& value)
{
std::size_t index;
if(readTargetKey(key, index))
{
TargetingEntities::iterator i = m_targetingEntities.find(index);
value.detach(TargetingEntity::TargetChangedCaller((*i).second));
m_targetingEntities.erase(i);
targetsChanged();
}
}
const TargetingEntities& get() const
{
return m_targetingEntities;
}
};
class RenderableTargetingEntity
{
TargetingEntity& m_targets;
mutable RenderablePointVector m_target_lines;
public:
static Shader* m_state;
RenderableTargetingEntity(TargetingEntity& targets)
: m_targets(targets), m_target_lines(GL_LINES)
{
}
void compile(const VolumeTest& volume, const Vector3& world_position) const
{
m_target_lines.clear();
m_target_lines.reserve(m_targets.size() * 2);
TargetingEntity_forEach(m_targets, TargetLinesPushBack(m_target_lines, world_position, volume));
}
void render(Renderer& renderer, const VolumeTest& volume, const Vector3& world_position) const
{
if(!m_targets.empty())
{
compile(volume, world_position);
if(!m_target_lines.empty())
{
renderer.addRenderable(m_target_lines, g_matrix4_identity);
}
}
}
};
class RenderableTargetingEntities
{
const TargetingEntities& m_targets;
mutable RenderablePointVector m_target_lines;
public:
static Shader* m_state;
RenderableTargetingEntities(const TargetingEntities& targets)
: m_targets(targets), m_target_lines(GL_LINES)
{
}
void compile(const VolumeTest& volume, const Vector3& world_position) const
{
m_target_lines.clear();
TargetingEntities_forEach(m_targets, TargetLinesPushBack(m_target_lines, world_position, volume));
}
void render(Renderer& renderer, const VolumeTest& volume, const Vector3& world_position) const
{
if(!m_targets.empty())
{
compile(volume, world_position);
if(!m_target_lines.empty())
{
renderer.addRenderable(m_target_lines, g_matrix4_identity);
}
}
}
};
class TargetableInstance :
public SelectableInstance,
public Targetable,
public EntityKeyValues::Observer
{
mutable Vertex3f m_position;
EntityKeyValues& m_entity;
TargetKeys m_targeting;
TargetedEntity m_targeted;
RenderableTargetingEntities m_renderable;
public:
TargetableInstance(
const scene::Path& path,
scene::Instance* parent,
void* instance,
InstanceTypeCastTable& casts,
EntityKeyValues& entity,
Targetable& targetable
) :
SelectableInstance(path, parent, instance, casts),
m_entity(entity),
m_targeted(targetable),
m_renderable(m_targeting.get())
{
m_entity.attach(*this);
m_entity.attach(m_targeting);
}
~TargetableInstance()
{
m_entity.detach(m_targeting);
m_entity.detach(*this);
}
void setTargetsChanged(const Callback& targetsChanged)
{
m_targeting.setTargetsChanged(targetsChanged);
}
void targetsChanged()
{
m_targeting.targetsChanged();
}
void insert(const char* key, EntityKeyValues::Value& value)
{
if(string_equal(key, g_targetable_nameKey))
{
value.attach(TargetedEntity::TargetnameChangedCaller(m_targeted));
}
}
void erase(const char* key, EntityKeyValues::Value& value)
{
if(string_equal(key, g_targetable_nameKey))
{
value.detach(TargetedEntity::TargetnameChangedCaller(m_targeted));
}
}
const Vector3& world_position() const
{
#if 1
const AABB& bounds = Instance::worldAABB();
if(aabb_valid(bounds))
{
return bounds.origin;
}
#else
const AABB& childBounds = Instance::childBounds();
if(aabb_valid(childBounds))
{
return childBounds.origin;
}
#endif
return localToWorld().t();
}
void render(Renderer& renderer, const VolumeTest& volume) const
{
renderer.SetState(m_entity.getEntityClass().m_state_wire, Renderer::eWireframeOnly);
renderer.SetState(m_entity.getEntityClass().m_state_wire, Renderer::eFullMaterials);
m_renderable.render(renderer, volume, world_position());
}
const TargetingEntities& getTargeting() const
{
return m_targeting.get();
}
};
class RenderableConnectionLines : public Renderable
{
typedef std::set<TargetableInstance*> TargetableInstances;
TargetableInstances m_instances;
public:
void attach(TargetableInstance& instance)
{
ASSERT_MESSAGE(m_instances.find(&instance) == m_instances.end(), "cannot attach instance");
m_instances.insert(&instance);
}
void detach(TargetableInstance& instance)
{
ASSERT_MESSAGE(m_instances.find(&instance) != m_instances.end(), "cannot detach instance");
m_instances.erase(&instance);
}
void renderSolid(Renderer& renderer, const VolumeTest& volume) const
{
for(TargetableInstances::const_iterator i = m_instances.begin(); i != m_instances.end(); ++i)
{
if((*i)->path().top().get().visible())
{
(*i)->render(renderer, volume);
}
}
}
void renderWireframe(Renderer& renderer, const VolumeTest& volume) const
{
renderSolid(renderer, volume);
}
};
typedef Static<RenderableConnectionLines> StaticRenderableConnectionLines;
#endif