/*
-Copyright (C) 2001-2006, William Joseph.
-All Rights Reserved.
+Copyright (C) 1999-2007 id Software, Inc. and contributors.
+For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
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 <stdlib.h>
+#include "entity_entitymodel.h"
#include "entity.h"
-class MiscModel :
- public Snappable
+//
+// CEntityMiscModel implementation
+//
+
+CEntityMiscModel::CEntityMiscModel (entity_t *e)
{
- EntityKeyValues m_entity;
- KeyObserverMap m_keyObservers;
- MatrixTransform m_transform;
+ refCount = 1;
+ m_entity = e;
+ m_model = NULL;
+ VectorSet(m_translate, 0,0,0);
+ VectorSet(m_euler, 0,0,0);
+ VectorSet(m_scale, 1,1,1);
+ VectorSet(m_pivot, 0,0,0);
+ m4x4_identity(m_transform);
+ m4x4_identity(m_inverse_transform);
+}
- OriginKey m_originKey;
- Vector3 m_origin;
- AnglesKey m_anglesKey;
- Vector3 m_angles;
- ScaleKey m_scaleKey;
- Vector3 m_scale;
+CEntityMiscModel::~CEntityMiscModel ()
+{
+ if(m_cachereq.GetBuffer()[0] != ':'
+ && m_version.c_str()[0] != '\0')
+ GetModelCache()->DeleteByID(m_cachereq.GetBuffer(), m_version.c_str());
+}
- SingletonModel m_model;
- ClassnameFilter m_filter;
- NamedEntity m_named;
- NameKeys m_nameKeys;
- RenderablePivot m_renderOrigin;
- RenderableNamedEntity m_renderName;
+// IRender
- Callback m_transformChanged;
- Callback m_evaluateTransform;
+void CEntityMiscModel::Draw(int state, int rflags) const
+{
+ // push the current modelview matrix
+ // FIXME: put in a check for stack recursion depth..
+ // or avoid recursion of opengl matrix stack
+ g_QglTable.m_pfn_qglPushMatrix();
+ // apply the parent-to-local transform
+ g_QglTable.m_pfn_qglMultMatrixf(m_transform);
- 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));
- }
+ pivot_draw(m_pivot);
- void updateTransform()
+ // draw children
+ if(m_model && m_model->pRender)
{
- m_transform.localToParent() = g_matrix4_identity;
- matrix4_transform_by_euler_xyz_degrees(m_transform.localToParent(), m_origin, m_angles, m_scale);
- m_transformChanged();
+ m_model->pRender->Draw(state, rflags);
}
-
-// vc 2k5 compiler fix
-#if _MSC_VER >= 1400
- public:
-#endif
- 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();
- }
+ g_QglTable.m_pfn_qglPopMatrix();
+}
- 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();
- }
- }
+// ISelect
- EntityKeyValues& getEntity()
- {
- return m_entity;
- }
- const EntityKeyValues& getEntity() const
- {
- return m_entity;
- }
+bool CEntityMiscModel::TestRay(const ray_t *ray, vec_t *dist) const
+{
+ vec_t dist_start = *dist;
+ vec_t dist_local = *dist;
+ ray_t ray_local = *ray;
- scene::Traversable& getTraversable()
- {
- return m_model.getTraversable();
- }
- Namespaced& getNamespaced()
- {
- return m_nameKeys;
- }
- Nameable& getNameable()
- {
- return m_named;
- }
- TransformNode& getTransformNode()
- {
- return m_transform;
- }
+ if (aabb_test_ray(&m_BBox, ray) == 0)
+ return false;
- void attach(scene::Traversable::Observer* observer)
- {
- m_model.attach(observer);
- }
- void detach(scene::Traversable::Observer* observer)
+ ray_transform(&ray_local, m_inverse_transform);
+
+ if(m_model && m_model->pSelect)
{
- m_model.detach(observer);
+ if(m_model->pSelect->TestRay(&ray_local, &dist_local))
+ *dist = dist_local;
}
+ else *dist = dist_local;
- void renderSolid(Renderer& renderer, const VolumeTest& volume, const Matrix4& localToWorld, bool selected) const
- {
- if(selected)
- {
- m_renderOrigin.render(renderer, volume, localToWorld);
- }
+ return *dist < dist_start;
+}
- 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;
-};
+//IEdit
-class MiscModelInstance : public TargetableInstance, public TransformModifier, public Renderable
+void CEntityMiscModel::Translate(const vec3_t translation)
{
- 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;
- }
- };
+ VectorIncrement(translation, m_translate);
+ UpdateCachedData();
+}
- MiscModel& m_contained;
-public:
- typedef LazyStatic<TypeCasts> StaticTypeCasts;
+void CEntityMiscModel::Rotate(const vec3_t pivot, const vec3_t rotation)
+{
+ m4x4_t rotation_matrix;
- STRING_CONSTANT(Name, "MiscModelInstance");
+ m4x4_identity(rotation_matrix);
+ m4x4_pivoted_rotate_by_vec3(rotation_matrix, rotation, eXYZ, pivot);
+ m4x4_transform_point(rotation_matrix, m_translate);
- 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()
+ VectorIncrement(rotation, m_euler);
+
+ UpdateCachedData();
+}
+
+void CEntityMiscModel::OnKeyValueChanged(entity_t *e, const char *key, const char* value)
+{
+ if(strcmp(key, "model") == 0)
+ SetName(value);
+ else if(strcmp(key, "_frame") == 0)
{
- StaticRenderableConnectionLines::instance().detach(*this);
- m_contained.instanceDetach(Instance::path());
+ SetName(ValueForKey(e, "model"));
}
- void renderSolid(Renderer& renderer, const VolumeTest& volume) const
+ else if(strcmp(key, "angle") == 0)
{
- m_contained.renderSolid(renderer, volume, Instance::localToWorld(), getSelectable().isSelected());
+ VectorSet(m_euler, 0.f, 0.f, 0.f);
+ m_euler[2] = atof(value);
+ UpdateCachedData();
}
- void renderWireframe(Renderer& renderer, const VolumeTest& volume) const
+ else if(strcmp(key, "angles") == 0)
{
- m_contained.renderWireframe(renderer, volume, Instance::localToWorld(), getSelectable().isSelected());
+ VectorSet(m_euler, 0.f, 0.f, 0.f);
+ if (value[0] != '\0')
+ sscanf (value, "%f %f %f", &m_euler[1], &m_euler[2], &m_euler[0]);
+ UpdateCachedData();
}
- void evaluateTransform()
+ else if(strcmp(key, "modelscale") == 0 || strcmp(key,"modelscale_vec") == 0)
{
- if(getType() == TRANSFORM_PRIMITIVE)
+ const char *s;
+ VectorSet(m_scale, 1.f, 1.f, 1.f);
+ s = ValueForKey(e,"modelscale");
+ if (s[0] != '\0')
{
- m_contained.translate(getTranslation());
- m_contained.rotate(getRotation());
- m_contained.scale(getScale());
+ float f = atof(s);
+ if( f != 0 )
+ VectorSet(m_scale, f, f, f);
+ else
+ Sys_FPrintf(SYS_WRN, "WARNING: ignoring 0 modelscale key\n");
}
+ s = ValueForKey(e,"modelscale_vec");
+ if (s[0] != '\0')
+ {
+ sscanf (s, "%f %f %f", &m_scale[0], &m_scale[1], &m_scale[2]);
+ if (m_scale[0] == 0.0 && m_scale[1] == 0.0 && m_scale[2] == 0.0)
+ {
+ VectorSet(m_scale, 1,1,1);
+ Sys_FPrintf(SYS_WRN, "WARNING: ignoring 0 0 0 modelscale_vec key\n");
+ }
+ }
+ UpdateCachedData();
}
- void applyTransform()
+ else if(strcmp(key, "origin") == 0)
{
- m_contained.revertTransform();
- evaluateTransform();
- m_contained.freezeTransform();
+ sscanf(value, "%f %f %f", &m_translate[0], &m_translate[1], &m_translate[2]);
+ UpdateCachedData();
}
- typedef MemberCaller<MiscModelInstance, &MiscModelInstance::applyTransform> ApplyTransformCaller;
-};
-
-class MiscModelNode :
- public scene::Node::Symbiot,
- public scene::Instantiable,
- public scene::Cloneable,
- public scene::Traversable::Observer
-{
- class TypeCasts
+ else if(strncmp(key,"_remap",6) == 0)
{
- 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;
- }
- };
+ SetName(ValueForKey(e, "model"));
+ }
+}
+//
+// CEntityMiscModel
+//
- scene::Node m_node;
- InstanceSet m_instances;
- MiscModel m_contained;
+// private:
- void construct()
- {
- m_contained.attach(this);
- }
- void destroy()
- {
- m_contained.detach(this);
- }
+void CEntityMiscModel::BuildCacheRequestString(const char *name)
+{
+ bool hasRemaps = false;
-public:
- typedef LazyStatic<TypeCasts> StaticTypeCasts;
+ m_cachereq.Format( "%s:%i", name, IntForKey(m_entity,"_frame") );
- scene::Traversable& get(NullType<scene::Traversable>)
- {
- return m_contained.getTraversable();
- }
- Snappable& get(NullType<Snappable>)
- {
- return m_contained;
- }
- TransformNode& get(NullType<TransformNode>)
+ for (epair_t* ep = m_entity->epairs ; ep ; ep=ep->next)
{
- 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();
+ if( strncmp(ep->key,"_remap",6) == 0 )
+ {
+ if( !hasRemaps )
+ {
+ hasRemaps = true;
+ m_cachereq += "?";
+ } else {
+ m_cachereq += "&";
+ }
+ m_cachereq += ep->value;
+ }
}
+}
- 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 CEntityMiscModel::SetName(const char *name)
+{
+ Str m_oldcachereq = m_cachereq;
- void release()
- {
- delete this;
- }
- scene::Node& node()
- {
- return m_node;
+ if( name[0] == '\0' ) {
+ return;
}
- scene::Node& clone() const
- {
- return (new MiscModelNode(*this))->node();
- }
+ BuildCacheRequestString(name);
- void insert(scene::Node& child)
- {
- m_instances.insert(child);
- }
- void erase(scene::Node& child)
- {
- m_instances.erase(child);
- }
+ if(strcmp(m_oldcachereq, m_cachereq) == 0)
+ return;
- 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)
+ if(m_cachereq.GetBuffer()[0] != ':'
+ && m_version.c_str()[0] != '\0')
+ GetModelCache()->DeleteByID(m_cachereq.GetBuffer(), m_version.c_str());
+
+ m_model = NULL;
+
+ if(name[0] != '\0')
{
- return m_instances.erase(observer, path);
+ const char* dot = strrchr(name, '.');
+ if(dot != NULL)
+ {
+ m_version = ++dot;
+ m_model = GetModelCache()->GetByID(m_cachereq.GetBuffer(), m_version.c_str());
+ }
}
-};
-scene::Node& New_MiscModel(EntityClass* eclass)
-{
- return (new MiscModelNode(eclass))->node();
+ UpdateCachedData();
}
-void MiscModel_construct()
-{
-}
-void MiscModel_destroy()
+
+void CEntityMiscModel::UpdateCachedData()
{
+ aabb_t aabb_temp;
+
+ m4x4_identity(m_transform);
+ m4x4_pivoted_transform_by_vec3(m_transform, m_translate, m_euler, eXYZ, m_scale, m_pivot);
+ memcpy(m_inverse_transform, m_transform, sizeof(m4x4_t));
+ m4x4_invert(m_inverse_transform);
+
+ aabb_clear(&aabb_temp);
+
+ if(m_model && m_model->pRender)
+ aabb_extend_by_aabb(&aabb_temp, m_model->pRender->GetAABB());
+ else
+ VectorSet(aabb_temp.extents, 8, 8, 8);
+
+ aabb_for_transformed_aabb(&m_BBox, &aabb_temp, m_transform);
}