+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, m_name_origin ),
+ m_name_origin( 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 AABB& childBounds ) const {
+ renderSolid( renderer, volume, localToWorld, selected );
+
+ if ( g_showNames ) {
+ // draw models as usual
+ if ( !isModel() ) {
+ // don't draw the name for worldspawn
+ if ( !strcmp( m_entity.getEntityClass().name(), "worldspawn" ) ) {
+ return;
+ }
+
+ // place name in the middle of the "children cloud"
+ m_name_origin = childBounds.origin;
+ }
+
+ 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;