X-Git-Url: https://de.git.xonotic.org/?a=blobdiff_plain;f=plugins%2Fentity%2Fmiscmodel.cpp;h=e6197d099e43e323fecc08594e6b3bc4cac4d927;hb=7fc621fc78d0e040dc2c12f38dc53dd9048215dc;hp=106fa17c9b97784b02353a455ae332688042f9a8;hpb=33efc9089296fc4e5f54d43581a0db81576ba848;p=xonotic%2Fnetradiant.git diff --git a/plugins/entity/miscmodel.cpp b/plugins/entity/miscmodel.cpp index 106fa17c..e6197d09 100644 --- a/plugins/entity/miscmodel.cpp +++ b/plugins/entity/miscmodel.cpp @@ -1,254 +1,412 @@ /* -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. + + 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" -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 - -#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 () +class MiscModel : + public Snappable { - if(m_cachereq.GetBuffer()[0] != ':' - && m_version.c_str()[0] != '\0') - GetModelCache()->DeleteByID(m_cachereq.GetBuffer(), m_version.c_str()); +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::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 OriginChangedCaller; +void anglesChanged(){ + m_angles = m_anglesKey.m_angles; + updateTransform(); +} +typedef MemberCaller AnglesChangedCaller; +void scaleChanged(){ + m_scale = m_scaleKey.m_scale; + updateTransform(); +} +typedef MemberCaller 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 ); + } +} +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(); + } +} - ray_transform(&ray_local, m_inverse_transform); +EntityKeyValues& getEntity(){ + return m_entity; +} +const EntityKeyValues& getEntity() const { + return m_entity; +} - if(m_model && m_model->pSelect) - { - if(m_model->pSelect->TestRay(&ray_local, &dist_local)) - *dist = dist_local; - } - else *dist = dist_local; +scene::Traversable& getTraversable(){ + return m_model.getTraversable(); +} +Namespaced& getNamespaced(){ + return m_nameKeys; +} +Nameable& getNameable(){ + return m_named; +} +TransformNode& getTransformNode(){ + return m_transform; +} - return *dist < dist_start; +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 ); + } -//IEdit + 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 CEntityMiscModel::Translate(const vec3_t translation) -{ - VectorIncrement(translation, m_translate); - UpdateCachedData(); +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 TransformChangedCaller; +}; -void CEntityMiscModel::Rotate(const vec3_t pivot, const vec3_t rotation) +class MiscModelInstance : public TargetableInstance, public TransformModifier, public Renderable { - m4x4_t rotation_matrix; +class TypeCasts +{ +InstanceTypeCastTable m_casts; +public: +TypeCasts(){ + m_casts = TargetableInstance::StaticTypeCasts::instance().get(); + InstanceStaticCast::install( m_casts ); + InstanceStaticCast::install( m_casts ); + InstanceIdentityCast::install( m_casts ); +} +InstanceTypeCastTable& get(){ + return m_casts; +} +}; - m4x4_identity(rotation_matrix); - m4x4_pivoted_rotate_by_vec3(rotation_matrix, rotation, eXYZ, pivot); - m4x4_transform_point(rotation_matrix, m_translate); +MiscModel& m_contained; +public: +typedef LazyStatic StaticTypeCasts; - VectorIncrement(rotation, m_euler); +STRING_CONSTANT( Name, "MiscModelInstance" ); - UpdateCachedData(); +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::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(); - } - 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")); - } -} - -// -// CEntityMiscModel -// - -// private: - -void CEntityMiscModel::BuildCacheRequestString(const char *name) +~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 ApplyTransformCaller; +}; + +class MiscModelNode : + public scene::Node::Symbiot, + public scene::Instantiable, + public scene::Cloneable, + public scene::Traversable::Observer { - bool hasRemaps = false; - - m_cachereq.Format( "%s:%i", name, IntForKey(m_entity,"_frame") ); - - 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; - } - } -} - -void CEntityMiscModel::SetName(const char *name) +class TypeCasts { - Str m_oldcachereq = m_cachereq; - - if( name[0] == '\0' ) { - return; - } +NodeTypeCastTable m_casts; +public: +TypeCasts(){ + NodeStaticCast::install( m_casts ); + NodeStaticCast::install( m_casts ); + NodeContainedCast::install( m_casts ); + NodeContainedCast::install( m_casts ); + NodeContainedCast::install( m_casts ); + NodeContainedCast::install( m_casts ); + NodeContainedCast::install( m_casts ); + NodeContainedCast::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 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){ + return m_contained.getTraversable(); +} +Snappable& get( NullType){ + return m_contained; +} +TransformNode& get( NullType){ + return m_contained.getTransformNode(); +} +Entity& get( NullType){ + return m_contained.getEntity(); +} +Nameable& get( NullType){ + return m_contained.getNameable(); +} +Namespaced& get( NullType){ + 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::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::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(){ }