]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - plugins/entity/eclassmodel.cpp
refactored vector classes to avoid reinterpret_cast
[xonotic/netradiant.git] / plugins / entity / eclassmodel.cpp
1 /*
2 Copyright (C) 2001-2006, William Joseph.
3 All Rights Reserved.
4
5 This file is part of GtkRadiant.
6
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 GtkRadiant is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20 */
21
22 ///\file
23 ///\brief Represents any entity which has a fixed size specified in its entity-definition and displays a model (e.g. ammo_bfg).
24 ///
25 /// This entity displays the model specified in its entity-definition.
26 /// The "origin" and "angle" keys directly control the entity's local-to-parent transform.
27 /// The "rotation" key directly controls the entity's local-to-parent transform for Doom3 only.
28
29 #include "eclassmodel.h"
30
31 #include "cullable.h"
32 #include "renderable.h"
33 #include "editable.h"
34
35 #include "selectionlib.h"
36 #include "instancelib.h"
37 #include "transformlib.h"
38 #include "traverselib.h"
39 #include "entitylib.h"
40 #include "render.h"
41 #include "eclasslib.h"
42 #include "pivot.h"
43
44 #include "targetable.h"
45 #include "origin.h"
46 #include "angle.h"
47 #include "rotation.h"
48 #include "model.h"
49 #include "filters.h"
50 #include "namedentity.h"
51 #include "keyobservers.h"
52 #include "namekeys.h"
53 #include "modelskinkey.h"
54
55 #include "entity.h"
56
57 class EclassModel :
58   public Snappable
59 {
60   MatrixTransform m_transform;
61   EntityKeyValues m_entity;
62   KeyObserverMap m_keyObservers;
63
64   OriginKey m_originKey;
65   Vector3 m_origin;
66   AngleKey m_angleKey;
67   float m_angle;
68   RotationKey m_rotationKey;
69   Float9 m_rotation;
70   SingletonModel m_model;
71
72   ClassnameFilter m_filter;
73   NamedEntity m_named;
74   NameKeys m_nameKeys;
75   RenderablePivot m_renderOrigin;
76   RenderableNamedEntity m_renderName;
77   ModelSkinKey m_skin;
78
79   Callback m_transformChanged;
80   Callback m_evaluateTransform;
81
82   void construct()
83   {
84     default_rotation(m_rotation);
85
86     m_keyObservers.insert("classname", ClassnameFilter::ClassnameChangedCaller(m_filter));
87     m_keyObservers.insert(Static<KeyIsName>::instance().m_nameKey, NamedEntity::IdentifierChangedCaller(m_named));
88     if(g_gameType == eGameTypeDoom3)
89     {
90       m_keyObservers.insert("angle", RotationKey::AngleChangedCaller(m_rotationKey));
91       m_keyObservers.insert("rotation", RotationKey::RotationChangedCaller(m_rotationKey));
92     }
93     else
94     {
95       m_keyObservers.insert("angle", AngleKey::AngleChangedCaller(m_angleKey));
96     }
97     m_keyObservers.insert("origin", OriginKey::OriginChangedCaller(m_originKey));
98   }
99
100   void updateTransform()
101   {
102     m_transform.localToParent() = g_matrix4_identity;
103     matrix4_translate_by_vec3(m_transform.localToParent(), m_origin);
104
105     if(g_gameType == eGameTypeDoom3)
106     {
107       matrix4_multiply_by_matrix4(m_transform.localToParent(), rotation_toMatrix(m_rotation));
108     }
109     else
110     {
111       matrix4_multiply_by_matrix4(m_transform.localToParent(), matrix4_rotation_for_z_degrees(m_angle));
112     }
113
114     m_transformChanged();
115   }
116   typedef MemberCaller<EclassModel, &EclassModel::updateTransform> UpdateTransformCaller;
117
118   void originChanged()
119   {
120     m_origin = m_originKey.m_origin;
121     updateTransform();
122   }
123   typedef MemberCaller<EclassModel, &EclassModel::originChanged> OriginChangedCaller;
124   void angleChanged()
125   {
126     m_angle = m_angleKey.m_angle;
127     updateTransform();
128   }
129   typedef MemberCaller<EclassModel, &EclassModel::angleChanged> AngleChangedCaller;
130   void rotationChanged()
131   {
132     rotation_assign(m_rotation, m_rotationKey.m_rotation);
133     updateTransform();
134   }
135   typedef MemberCaller<EclassModel, &EclassModel::rotationChanged> RotationChangedCaller;
136
137   void skinChanged()
138   {
139     scene::Node* node = m_model.getNode();
140     if(node != 0)
141     {
142       Node_modelSkinChanged(*node);
143     }
144   }
145   typedef MemberCaller<EclassModel, &EclassModel::skinChanged> SkinChangedCaller;
146
147 public:
148
149   EclassModel(EntityClass* eclass, scene::Node& node, const Callback& transformChanged, const Callback& evaluateTransform) :
150     m_entity(eclass),
151     m_originKey(OriginChangedCaller(*this)),
152     m_origin(ORIGINKEY_IDENTITY),
153     m_angleKey(AngleChangedCaller(*this)),
154     m_angle(ANGLEKEY_IDENTITY),
155     m_rotationKey(RotationChangedCaller(*this)),
156     m_filter(m_entity, node),
157     m_named(m_entity),
158     m_nameKeys(m_entity),
159     m_renderName(m_named, g_vector3_identity),
160     m_skin(SkinChangedCaller(*this)),
161     m_transformChanged(transformChanged),
162     m_evaluateTransform(evaluateTransform)
163   {
164     construct();
165   }
166   EclassModel(const EclassModel& other, scene::Node& node, const Callback& transformChanged, const Callback& evaluateTransform) :
167     m_entity(other.m_entity),
168     m_originKey(OriginChangedCaller(*this)),
169     m_origin(ORIGINKEY_IDENTITY),
170     m_angleKey(AngleChangedCaller(*this)),
171     m_angle(ANGLEKEY_IDENTITY),
172     m_rotationKey(RotationChangedCaller(*this)),
173     m_filter(m_entity, node),
174     m_named(m_entity),
175     m_nameKeys(m_entity),
176     m_renderName(m_named, g_vector3_identity),
177     m_skin(SkinChangedCaller(*this)),
178     m_transformChanged(transformChanged),
179     m_evaluateTransform(evaluateTransform)
180   {
181     construct();
182   }
183
184   InstanceCounter m_instanceCounter;
185   void instanceAttach(const scene::Path& path)
186   {
187     if(++m_instanceCounter.m_count == 1)
188     {
189       m_filter.instanceAttach();
190       m_entity.instanceAttach(path_find_mapfile(path.begin(), path.end()));
191       m_entity.attach(m_keyObservers);
192       m_model.modelChanged(m_entity.getEntityClass().modelpath());
193       m_skin.skinChanged(m_entity.getEntityClass().skin());
194     }
195   }
196   void instanceDetach(const scene::Path& path)
197   {
198     if(--m_instanceCounter.m_count == 0)
199     {
200       m_skin.skinChanged("");
201       m_model.modelChanged("");
202       m_entity.detach(m_keyObservers);
203       m_entity.instanceDetach(path_find_mapfile(path.begin(), path.end()));
204       m_filter.instanceDetach();
205     }
206   }
207
208   EntityKeyValues& getEntity()
209   {
210     return m_entity;
211   }
212   const EntityKeyValues& getEntity() const
213   {
214     return m_entity;
215   }
216
217   scene::Traversable& getTraversable()
218   {
219     return m_model.getTraversable();
220   }
221   Namespaced& getNamespaced()
222   {
223     return m_nameKeys;
224   }
225   Nameable& getNameable()
226   {
227     return m_named;
228   }
229   TransformNode& getTransformNode()
230   {
231     return m_transform;
232   }
233   ModelSkin& getModelSkin()
234   {
235     return m_skin.get();
236   }
237
238   void attach(scene::Traversable::Observer* observer)
239   {
240     m_model.attach(observer);
241   }
242   void detach(scene::Traversable::Observer* observer)
243   {
244     m_model.detach(observer);
245   }
246
247   void renderSolid(Renderer& renderer, const VolumeTest& volume, const Matrix4& localToWorld, bool selected) const
248   {
249     if(selected)
250     {
251       m_renderOrigin.render(renderer, volume, localToWorld);
252     }
253
254     renderer.SetState(m_entity.getEntityClass().m_state_wire, Renderer::eWireframeOnly);
255   }
256   void renderWireframe(Renderer& renderer, const VolumeTest& volume, const Matrix4& localToWorld, bool selected) const
257   {
258     renderSolid(renderer, volume, localToWorld, selected);
259     if(g_showNames)
260     {
261       renderer.addRenderable(m_renderName, localToWorld);
262     }
263   }
264
265   void translate(const Vector3& translation)
266   {
267     m_origin = origin_translated(m_origin, translation);
268   }
269   void rotate(const Quaternion& rotation)
270   {
271     if(g_gameType == eGameTypeDoom3)
272     {
273       rotation_rotate(m_rotation, rotation);
274     }
275     else
276     {
277       m_angle = angle_rotated(m_angle, rotation);
278     }
279   }
280   void snapto(float snap)
281   {
282     m_originKey.m_origin = origin_snapped(m_originKey.m_origin, snap);
283     m_originKey.write(&m_entity);
284   }
285   void revertTransform()
286   {
287     m_origin = m_originKey.m_origin;
288     if(g_gameType == eGameTypeDoom3)
289     {
290       rotation_assign(m_rotation, m_rotationKey.m_rotation);
291     }
292     else
293     {
294       m_angle = m_angleKey.m_angle;
295     }
296   }
297   void freezeTransform()
298   {
299     m_originKey.m_origin = m_origin;
300     m_originKey.write(&m_entity);
301     if(g_gameType == eGameTypeDoom3)
302     {
303       rotation_assign(m_rotationKey.m_rotation, m_rotation);
304       m_rotationKey.write(&m_entity);
305     }
306     else
307     {
308       m_angleKey.m_angle = m_angle;
309       m_angleKey.write(&m_entity);
310     }
311   }
312   void transformChanged()
313   {
314     revertTransform();
315     m_evaluateTransform();
316     updateTransform();
317   }
318   typedef MemberCaller<EclassModel, &EclassModel::transformChanged> TransformChangedCaller;
319 };
320
321 class EclassModelInstance : public TargetableInstance, public TransformModifier, public Renderable
322 {
323   class TypeCasts
324   {
325     InstanceTypeCastTable m_casts;
326   public:
327     TypeCasts()
328     {
329       m_casts = TargetableInstance::StaticTypeCasts::instance().get();
330       InstanceStaticCast<EclassModelInstance, Renderable>::install(m_casts);
331       InstanceStaticCast<EclassModelInstance, Transformable>::install(m_casts);
332       InstanceIdentityCast<EclassModelInstance>::install(m_casts);
333     }
334     InstanceTypeCastTable& get()
335     {
336       return m_casts;
337     }
338   };
339
340   EclassModel& m_contained;
341 public:
342   typedef LazyStatic<TypeCasts> StaticTypeCasts;
343
344   STRING_CONSTANT(Name, "EclassModelInstance");
345
346   EclassModelInstance(const scene::Path& path, scene::Instance* parent, EclassModel& contained) :
347     TargetableInstance(path, parent, this, StaticTypeCasts::instance().get(), contained.getEntity(), *this),
348     TransformModifier(EclassModel::TransformChangedCaller(contained), ApplyTransformCaller(*this)),
349     m_contained(contained)
350   {
351     m_contained.instanceAttach(Instance::path());
352
353     StaticRenderableConnectionLines::instance().attach(*this);
354   }
355   ~EclassModelInstance()
356   {
357     StaticRenderableConnectionLines::instance().detach(*this);
358
359     m_contained.instanceDetach(Instance::path());
360   }
361   void renderSolid(Renderer& renderer, const VolumeTest& volume) const
362   {
363     m_contained.renderSolid(renderer, volume, Instance::localToWorld(), getSelectable().isSelected());
364   }
365   void renderWireframe(Renderer& renderer, const VolumeTest& volume) const
366   {
367     m_contained.renderWireframe(renderer, volume, Instance::localToWorld(), getSelectable().isSelected());
368   }
369
370   void evaluateTransform()
371   {
372     if(getType() == TRANSFORM_PRIMITIVE)
373     {
374       m_contained.translate(getTranslation());
375       m_contained.rotate(getRotation());
376     }
377   }
378   void applyTransform()
379   {
380     m_contained.revertTransform();
381     evaluateTransform();
382     m_contained.freezeTransform();
383   }
384   typedef MemberCaller<EclassModelInstance, &EclassModelInstance::applyTransform> ApplyTransformCaller;
385 };
386
387 class EclassModelNode :
388   public scene::Node::Symbiot,
389   public scene::Instantiable,
390   public scene::Cloneable,
391   public scene::Traversable::Observer
392 {
393   class TypeCasts
394   {
395     NodeTypeCastTable m_casts;
396   public:
397     TypeCasts()
398     {
399       NodeStaticCast<EclassModelNode, scene::Instantiable>::install(m_casts);
400       NodeStaticCast<EclassModelNode, scene::Cloneable>::install(m_casts);
401       NodeContainedCast<EclassModelNode, scene::Traversable>::install(m_casts);
402       NodeContainedCast<EclassModelNode, Snappable>::install(m_casts);
403       NodeContainedCast<EclassModelNode, TransformNode>::install(m_casts);
404       NodeContainedCast<EclassModelNode, Entity>::install(m_casts);
405       NodeContainedCast<EclassModelNode, Nameable>::install(m_casts);
406       NodeContainedCast<EclassModelNode, Namespaced>::install(m_casts);
407       NodeContainedCast<EclassModelNode, ModelSkin>::install(m_casts);
408     }
409     NodeTypeCastTable& get()
410     {
411       return m_casts;
412     }
413   };
414
415
416   scene::Node m_node;
417   InstanceSet m_instances;
418   EclassModel m_contained;
419
420   void construct()
421   {
422     m_contained.attach(this);
423   }
424   void destroy()
425   {
426     m_contained.detach(this);
427   }
428
429 public:
430   typedef LazyStatic<TypeCasts> StaticTypeCasts;
431
432   scene::Traversable& get(NullType<scene::Traversable>)
433   {
434     return m_contained.getTraversable();
435   }
436   Snappable& get(NullType<Snappable>)
437   {
438     return m_contained;
439   }
440   TransformNode& get(NullType<TransformNode>)
441   {
442     return m_contained.getTransformNode();
443   }
444   Entity& get(NullType<Entity>)
445   {
446     return m_contained.getEntity();
447   }
448   Nameable& get(NullType<Nameable>)
449   {
450     return m_contained.getNameable();
451   }
452   Namespaced& get(NullType<Namespaced>)
453   {
454     return m_contained.getNamespaced();
455   }
456   ModelSkin& get(NullType<ModelSkin>)
457   {
458     return m_contained.getModelSkin();
459   }
460
461   EclassModelNode(EntityClass* eclass) :
462     m_node(this, this, StaticTypeCasts::instance().get()),
463     m_contained(eclass, m_node, InstanceSet::TransformChangedCaller(m_instances), InstanceSetEvaluateTransform<EclassModelInstance>::Caller(m_instances))
464   {
465     construct();
466   }
467   EclassModelNode(const EclassModelNode& other) :
468     scene::Node::Symbiot(other),
469     scene::Instantiable(other),
470     scene::Cloneable(other),
471     scene::Traversable::Observer(other),
472     m_node(this, this, StaticTypeCasts::instance().get()),
473     m_contained(other.m_contained, m_node, InstanceSet::TransformChangedCaller(m_instances), InstanceSetEvaluateTransform<EclassModelInstance>::Caller(m_instances))
474   {
475     construct();
476   }
477   ~EclassModelNode()
478   {
479     destroy();
480   }
481   void release()
482   {
483     delete this;
484   }
485   scene::Node& node()
486   {
487     return m_node;
488   }
489
490   void insert(scene::Node& child)
491   {
492     m_instances.insert(child);
493   }
494   void erase(scene::Node& child)
495   {
496     m_instances.erase(child);
497   }
498
499   scene::Node& clone() const
500   {
501     return (new EclassModelNode(*this))->node();
502   }
503
504   scene::Instance* create(const scene::Path& path, scene::Instance* parent)
505   {
506     return new EclassModelInstance(path, parent, m_contained);
507   }
508   void forEachInstance(const scene::Instantiable::Visitor& visitor)
509   {
510     m_instances.forEachInstance(visitor);
511   }
512   void insert(scene::Instantiable::Observer* observer, const scene::Path& path, scene::Instance* instance)
513   {
514     m_instances.insert(observer, path, instance);
515   }
516   scene::Instance* erase(scene::Instantiable::Observer* observer, const scene::Path& path)
517   {
518     return m_instances.erase(observer, path);
519   }
520 };
521
522 scene::Node& New_EclassModel(EntityClass* eclass)
523 {
524   return (new EclassModelNode(eclass))->node();
525 }
526