]> de.git.xonotic.org Git - xonotic/netradiant.git/blobdiff - plugins/entity/miscmodel.cpp
Radiant:
[xonotic/netradiant.git] / plugins / entity / miscmodel.cpp
index 301bc4eb2ddc31dffc5b1188908d2ad04b283511..758152c40bfc11be74ca4594db2acb2256579d5e 100644 (file)
@@ -1,6 +1,6 @@
 /*
-   Copyright (C) 1999-2007 id Software, Inc. and contributors.
-   For a list of contributors, see the accompanying CONTRIBUTORS file.
+   Copyright (C) 2001-2006, William Joseph.
+   All Rights Reserved.
 
    This file is part of GtkRadiant.
 
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 
-#include <stdlib.h>
+///\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_entitymodel.h"
 #include "entity.h"
 
-//
-// CEntityMiscModel implementation
-//
-
-CEntityMiscModel::CEntityMiscModel ( entity_t *e ){
-       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 );
-}
-
-CEntityMiscModel::~CEntityMiscModel (){
-       if ( m_cachereq.GetBuffer()[0] != ':'
-                && m_version.c_str()[0] != '\0' ) {
-               GetModelCache()->DeleteByID( m_cachereq.GetBuffer(), m_version.c_str() );
-       }
+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 ) );
 }
 
-
-// IRender
-
-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 );
-
-       pivot_draw( m_pivot );
-
-       // draw children
-       if ( m_model && m_model->pRender ) {
-               m_model->pRender->Draw( state, rflags );
-       }
-
-       g_QglTable.m_pfn_qglPopMatrix();
+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();
 }
 
-// ISelect
+// vc 2k5 compiler fix
+#if _MSC_VER >= 1400
+public:
+#endif
 
-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;
+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();
+}
 
-       if ( aabb_test_ray( &m_BBox, ray ) == 0 ) {
-               return false;
+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 );
        }
-
-       ray_transform( &ray_local, m_inverse_transform );
-
-       if ( m_model && m_model->pSelect ) {
-               if ( m_model->pSelect->TestRay( &ray_local, &dist_local ) ) {
-                       *dist = dist_local;
-               }
+}
+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();
        }
-       else{ *dist = dist_local; }
-
-       return *dist < dist_start;
 }
 
-
-//IEdit
-
-void CEntityMiscModel::Translate( const vec3_t translation ){
-       VectorIncrement( translation, m_translate );
-       UpdateCachedData();
+EntityKeyValues& getEntity(){
+       return m_entity;
+}
+const EntityKeyValues& getEntity() const {
+       return m_entity;
 }
 
-void CEntityMiscModel::Rotate( const vec3_t pivot, const vec3_t rotation ){
-       m4x4_t rotation_matrix;
-
-       m4x4_identity( rotation_matrix );
-       m4x4_pivoted_rotate_by_vec3( rotation_matrix, rotation, eXYZ, pivot );
-       m4x4_transform_point( rotation_matrix, m_translate );
-
-       VectorIncrement( rotation, m_euler );
+scene::Traversable& getTraversable(){
+       return m_model.getTraversable();
+}
+Namespaced& getNamespaced(){
+       return m_nameKeys;
+}
+Nameable& getNameable(){
+       return m_named;
+}
+TransformNode& getTransformNode(){
+       return m_transform;
+}
 
-       UpdateCachedData();
+void attach( scene::Traversable::Observer* observer ){
+       m_model.attach( observer );
+}
+void detach( scene::Traversable::Observer* observer ){
+       m_model.detach( observer );
 }
 
-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 ) {
-               SetName( ValueForKey( e, "model" ) );
-       }
-       else if ( strcmp( key, "angle" ) == 0 ) {
-               VectorSet( m_euler, 0.f, 0.f, 0.f );
-               m_euler[2] = atof( value );
-               UpdateCachedData();
-       }
-       else if ( strcmp( key, "angles" ) == 0 ) {
-               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();
-       }
-       else if ( strcmp( key, "modelscale" ) == 0 || strcmp( key,"modelscale_vec" ) == 0 ) {
-               const char *s;
-               VectorSet( m_scale, 1.f, 1.f, 1.f );
-               s = ValueForKey( e,"modelscale" );
-               if ( s[0] != '\0' ) {
-                       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 renderSolid( Renderer& renderer, const VolumeTest& volume, const Matrix4& localToWorld, bool selected ) const {
+       if ( selected ) {
+               m_renderOrigin.render( renderer, volume, localToWorld );
        }
-       else if ( strcmp( key, "origin" ) == 0 ) {
-               sscanf( value, "%f %f %f", &m_translate[0], &m_translate[1], &m_translate[2] );
-               UpdateCachedData();
-       }
-       else if ( strncmp( key,"_remap",6 ) == 0 ) {
-               SetName( ValueForKey( e, "model" ) );
+
+       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  && !string_equal( m_named.name(), "misc_model" ) ) {
+               renderer.addRenderable( m_renderName, localToWorld );
        }
 }
 
-//
-// CEntityMiscModel
-//
-
-// private:
+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;
+}
+};
 
-void CEntityMiscModel::BuildCacheRequestString( const char *name ){
-       bool hasRemaps = false;
+MiscModel& m_contained;
+public:
+typedef LazyStatic<TypeCasts> StaticTypeCasts;
 
-       m_cachereq.Format( "%s:%i", name, IntForKey( m_entity,"_frame" ) );
+STRING_CONSTANT( Name, "MiscModelInstance" );
 
-       for ( epair_t* ep = m_entity->epairs ; ep ; ep = ep->next )
-       {
-               if ( strncmp( ep->key,"_remap",6 ) == 0 ) {
-                       if ( !hasRemaps ) {
-                               hasRemaps = true;
-                               m_cachereq += "?";
-                       }
-                       else {
-                               m_cachereq += "&";
-                       }
-                       m_cachereq += ep->value;
-               }
-       }
+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 );
 }
-
-void CEntityMiscModel::SetName( const char *name ){
-       Str m_oldcachereq = m_cachereq;
-
-       if ( name[0] == '\0' ) {
-               return;
+~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;
+}
+};
 
-       BuildCacheRequestString( name );
 
-       if ( strcmp( m_oldcachereq, m_cachereq ) == 0 ) {
-               return;
-       }
+scene::Node m_node;
+InstanceSet m_instances;
+MiscModel m_contained;
 
-       if ( m_cachereq.GetBuffer()[0] != ':'
-                && m_version.c_str()[0] != '\0' ) {
-               GetModelCache()->DeleteByID( m_cachereq.GetBuffer(), m_version.c_str() );
-       }
+void construct(){
+       m_contained.attach( this );
+}
+void destroy(){
+       m_contained.detach( this );
+}
 
-       m_model = NULL;
+public:
+typedef LazyStatic<TypeCasts> StaticTypeCasts;
 
-       if ( name[0] != '\0' ) {
-               const char* dot = strrchr( name, '.' );
-               if ( dot != NULL ) {
-                       m_version = ++dot;
-                       m_model = GetModelCache()->GetByID( m_cachereq.GetBuffer(), m_version.c_str() );
-               }
-       }
+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();
+}
 
-       UpdateCachedData();
+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;
+}
 
-void CEntityMiscModel::UpdateCachedData(){
-       aabb_t aabb_temp;
+scene::Node& clone() const {
+       return ( new MiscModelNode( *this ) )->node();
+}
 
-       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 );
+void insert( scene::Node& child ){
+       m_instances.insert( child );
+}
+void erase( scene::Node& child ){
+       m_instances.erase( child );
+}
 
-       aabb_clear( &aabb_temp );
+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 );
+}
+};
 
-       if ( m_model && m_model->pRender ) {
-               aabb_extend_by_aabb( &aabb_temp, m_model->pRender->GetAABB() );
-       }
-       else{
-               VectorSet( aabb_temp.extents, 8, 8, 8 );
-       }
+scene::Node& New_MiscModel( EntityClass* eclass ){
+       return ( new MiscModelNode( eclass ) )->node();
+}
 
-       aabb_for_transformed_aabb( &m_BBox, &aabb_temp, m_transform );
+void MiscModel_construct(){
+}
+void MiscModel_destroy(){
 }