//***************************************************************************************** // Truevision - a 3d modeler for gnome and povray // // objectlayer.cc // // Vincent LE PRINCE // Copyright (C) 2000-2005 Vincent LE PRINCE // This file is part of the TRUEVISION Package // This program 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. // // This program 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 this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ //******************************************************************************************* #include "include/objectlayer.h" #include "include/objectlist.h" #include "include/viewmanager.h" #include #include "include/interface.h" #include "include/tvio.h" //************************************** // Layers - Calques //************************************** // Static members app_objs * ObjectLayer::app_ref = NULL; // pointer to layer list and objects tree GtkListStore *ObjectLayer::list_store = NULL; GtkWidget *ObjectLayer::obj_tree_view = NULL; GtkTreeStore *ObjectLayer::obj_tree_store = NULL; GtkTreeSelection *ObjectLayer::obj_tree_selection = NULL; //************************************** // Constructors & destructor //************************************** // Basic ObjectLayer::ObjectLayer( app_objs * appref ) { app_ref = appref; name = new TvWidget_entry( N_("Name"), "NAME", NULL, app_ref ); visible = new TvWidget_bool( N_("Visible"), "VISI", NULL, app_ref, true ); selectable = new TvWidget_bool( N_("Selectable"), "SELECT", NULL, app_ref, true ); render = new TvWidget_bool( N_("Render"), "RENDER", NULL, app_ref, true ); set_name( "Layer #1" ); edit_box = NULL; obj_tree_store = NULL; } // Copies contructor ObjectLayer::ObjectLayer( ObjectLayer & ref ) { name = new TvWidget_entry( *ref.name ); visible = new TvWidget_bool( *ref.visible ); selectable = new TvWidget_bool( *ref.selectable ); render = new TvWidget_bool( *ref.render ); set_name( (ref.name)->value() ); edit_box = NULL; obj_tree_store = ref.obj_tree_store; OBJLIST_DEF // objects copy for ( unsigned int i = 0 ; i < ref.objects.size() ; i++ ) { if ( ref.objects[i]->get_type() == TV_OBJ3D_CAMERA ) continue; if ( ref.objects[i]->get_category() == TV_OBJ3D_ATMOS1 ) continue; Object3D *obj = ref.objects[i]->duplicate_yourself(); obj->set_layer( this ); objects.push_back( obj ); objlist->add_object( obj ); } } // Destructor ObjectLayer::~ObjectLayer() { //cout << "\ndelete layer " << name->value(); cout.flush(); OBJLIST_DEF objlist->unselect_object(); if ( objects.size() != 0 ) for ( unsigned int i = 0 ; i < objects.size() ; i ++ ) { objlist->remove_object( objects[i] ); delete objects[i]; } delete name; delete visible; delete selectable; delete render; //cout << "\nDone"; cout.flush(); } //************************************************* // Layer name management // // Set layer name // Test name existence in object pointer list managed by ObjectList // recursive function //************************************************* void ObjectLayer::set_name( char *nom ) { OBJLIST_DEF if ( objlist->layer_name_exist( nom, this ) ) { int b; register int a = b = strlen( nom ) - 1; char *tmp = new char[ b + 30 ]; strcpy( tmp, nom ); while ( nom[a] > 47 && nom[a] < 58 ) a--; if ( a == b ) strcat( tmp, " #2" ); else { int num; sscanf( nom + a + 1, "%u", &num ); tmp[a+1] = '\0'; sprintf( tmp + a + 1, "%u", num + 1 ); } set_name( tmp ); delete tmp; } else name->set( nom ); } //******************************************** // Add object // // Add an object to layer // also add it to ObjectList pointer list // Does NOT display anything, use insert_object // to add an object to a selected layer //******************************************** void ObjectLayer::add_object( Object3D *obj, Object3D *selected) { if ( selected != NULL ) { if ( selected->is_group() == true ) { selected->paste_object( obj ); return; } if ( selected->get_parent() != NULL ) if ( selected->get_parent()->is_group() == true ) { selected->get_parent()->paste_object( obj ); return; } } objects.push_back( obj ); obj->set_layer( this ); obj->set_parent( NULL ); OBJLIST_DEF objlist->add_object( obj ); if ( objlist->get_current_layer() == this ) { obj->add_to_tree( obj_tree_view, obj_tree_store, obj_tree_selection, NULL, NULL, NULL ); obj->tree_node_select(); } } //********************************************* // Insert object // Add object with add_object and also add it to tree //********************************************* void ObjectLayer::insert_object( Object3D *obj, int position ) { OBJLIST_DEF objlist->add_object( obj ); obj->set_parent( NULL ); obj->set_layer( this ); vector::iterator it; it = objects.begin() + position; objects.insert( it, obj ); if ( objlist->get_current_layer() == this ) { obj->add_to_tree( obj_tree_view, obj_tree_store, obj_tree_selection, NULL, ( (unsigned int)position == objects.size() - 1 ) ? NULL : objects[position+1]->get_node() ); obj->tree_node_select(); } } //********************************************** // Delete object //********************************************** void ObjectLayer::delete_object( Object3D *obj ) { if ( objects.size() == 0 ) return; if ( obj == NULL ) return; remove_object( obj ); // Don't delete cause we wanna undo if necessary //delete obj; } //********************************************* // Layer list management // Called by ObjectList to manage layers in a clist //********************************************* //********************************************* // Add to list // Add the layer to the clist managed by ObjectList // Set pixmaps for visbility / render / lock //********************************************* void ObjectLayer::add_to_list( GtkListStore *store, int position ) { list_store = store; GdkPixbuf *visible_pixbuf = NULL; if ( visible->value() == true ) { char *pixmap = tv_get_pixmap( "object_visible.xpm" ); visible_pixbuf = gdk_pixbuf_new_from_file( pixmap, NULL ); delete pixmap; } GdkPixbuf *lock_pixbuf = NULL; if ( selectable->value() == false ) { char *pixmap = pixmap = tv_get_pixmap( "lock.xpm" ); lock_pixbuf = gdk_pixbuf_new_from_file( pixmap, NULL ); delete pixmap; } GdkPixbuf *render_pixbuf = NULL; if ( render->value() == true ) { char *pixmap = pixmap = tv_get_pixmap( "render.xpm" ); render_pixbuf = gdk_pixbuf_new_from_file( pixmap, NULL ); delete pixmap; } if ( position != -1 ) gtk_list_store_insert( list_store, &node_iter, position ); else gtk_list_store_append( list_store, &node_iter ); gtk_list_store_set( list_store, &node_iter, 3, name->value(), 0, visible_pixbuf, 1, lock_pixbuf, 2, render_pixbuf, 4, this, -1 ); } //*************************************************** // Move layers : move_up & move_down // move layer in clist //*************************************************** void ObjectLayer::move_before_in_list( GtkTreeIter *sibling ) { gtk_list_store_move_before( list_store, &node_iter, sibling ); } void ObjectLayer::move_after_in_list( GtkTreeIter *sibling ) { gtk_list_store_move_after( list_store, &node_iter, sibling ); } //***************************************************** // Toggle properties // // Callback used when pixmaps in list clicked // set visbility / render & lock values //****************************************************** void ObjectLayer::toggle_property( int col ) { switch ( col ) { case 1: { visible->toggle(); GdkPixbuf *pixbuf = NULL; if ( visible->value() ) { char *pixmap = tv_get_pixmap( "object_visible.xpm" ); pixbuf = gdk_pixbuf_new_from_file( pixmap, NULL ); delete pixmap; } gtk_list_store_set( list_store, &node_iter, 0, pixbuf, -1 ); VMAN_DEF vmanager->refresh(); } break; case 2: { selectable->toggle(); GdkPixbuf *pixbuf = NULL; if ( ! selectable->value() ) { char *pixmap = tv_get_pixmap( "lock.xpm" ); pixbuf = gdk_pixbuf_new_from_file( pixmap, NULL ); delete pixmap; } gtk_list_store_set( list_store, &node_iter, 1, pixbuf, -1 ); } break; case 3: { render->toggle(); GdkPixbuf *pixbuf = NULL; if ( render->value() ) { char *pixmap = tv_get_pixmap( "render.xpm" ); pixbuf = gdk_pixbuf_new_from_file( pixmap, NULL ); delete pixmap; } gtk_list_store_set( list_store, &node_iter, 2, pixbuf, -1 ); } break; } } //********************************************************** // Can be destroyed // Check whether we can destroy this layer // ie if it doesn't contain undeletable objets : camera... //********************************************************** bool ObjectLayer::can_be_destroyed() { for ( unsigned int i = 0 ; i < objects.size() ; i ++ ) if ( objects[i]->get_type() == TV_OBJ3D_CAMERA ) return false; return true; } //*********************************************************** // Edit box // // used to edit layer name //*********************************************************** void ObjectLayer::raise_edit_box() { if ( edit_box != NULL ) { gdk_window_raise( edit_box->window ); return; } edit_box = gtk_dialog_new_with_buttons( _("Layer properties"), NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_OK, GTK_RESPONSE_OK, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL ); g_signal_connect( G_OBJECT(edit_box), "response", G_CALLBACK(sign_objlayer_edit_clicked), this ); g_signal_connect( G_OBJECT(edit_box), "close", G_CALLBACK(sign_objlayer_edit_box_destroy), this ); GtkWidget *hbox = gtk_hbox_new( FALSE, 0 ); gtk_box_pack_start( GTK_BOX(GTK_DIALOG(edit_box)->vbox), hbox, FALSE, TRUE, 5 ); GtkWidget *lab = gtk_label_new( _("Layer name : ") ); gtk_box_pack_start( GTK_BOX(hbox), lab, TRUE, TRUE, 5 ); edit_name_entry = gtk_entry_new(); gtk_entry_set_text( GTK_ENTRY(edit_name_entry), name->value() ); gtk_box_pack_start( GTK_BOX(hbox), edit_name_entry, TRUE, TRUE, 5 ); gtk_signal_connect( GTK_OBJECT(edit_name_entry), "activate", GTK_SIGNAL_FUNC(sign_objlayer_edit_ok_clicked), this ); gtk_widget_show_all( edit_box ); } // Close dialog box void ObjectLayer::destroy_edit_box( int val ) { //OBJLIST_DEF if ( val == GTK_RESPONSE_OK ) { set_name( (char*)gtk_entry_get_text( GTK_ENTRY(edit_name_entry) ) ); gtk_list_store_set( list_store, &node_iter, 3, (gchar*)name->value(), -1 ); } if ( edit_box != NULL ) { gtk_widget_destroy( edit_box ); edit_box = NULL; } } //*************************************************** // Display //*************************************************** //*************************************************** // Display contents // // Display objects owned by this layer in a ctree //*************************************************** void ObjectLayer::display_content( GtkWidget *view, GtkTreeStore *store, GtkTreeSelection *selection ) { obj_tree_view = view; obj_tree_store = store; obj_tree_selection = selection; for ( unsigned int i = 0 ; i < objects.size() ; i++ ) objects[i]->add_to_tree( view, store, selection, NULL, NULL ); if ( objects.size() > 0 ) objects[0]->tree_node_select(); } //*************************************************** // Display // Display objects owned by this layer in OpenGL views //*************************************************** void ObjectLayer::display( glview *view ) { if ( ! visible->value() ) return; if ( ! selectable->value() ) { GLint val = 0; glGetIntegerv( GL_RENDER_MODE, &val ); if ( val == GL_SELECT ) return; } for ( unsigned int i = 0 ; i < objects.size() ; i++ ) { objects[i]->display( view ); } } //************************************************************ // Output & Input //************************************************************ //************************************************************ // Output to povray //************************************************************ void ObjectLayer::output_to_povray_pass1( ofstream & file ) { if ( ! render->value() ) return; file << "\n\n//----------------------\n// Layer " << name->value()<< "\n//----------------------"; for ( unsigned int i = 0 ; i < objects.size() ; i++ ) objects[i]->output_to_povray_pass1( file ); } void ObjectLayer::output_to_povray_pass2( ofstream & file ) { if ( ! render->value() ) return; file << "\n\n//----------------------\n// Layer " << name->value()<< "\n//----------------------"; for ( unsigned int i = 0 ; i < objects.size() ; i++ ) objects[i]->output_to_povray_pass2( file ); } //*********************************************************** // Save //*********************************************************** void ObjectLayer::save( ofstream & file ) { file << "\nLAYER{\n"; name->save( file ); visible->save( file ); selectable->save( file ); render->save( file ); for ( unsigned int i = 0 ; i < objects.size() ; i++ ) { objects[i]->save( file ); } file << "\n}"; } //************************************************************ // Load //************************************************************ bool ObjectLayer::load( ifstream & file, char *ltag ) { if ( strcmp( "LAYER", ltag ) ) return false; OBJLIST_DEF char * tag = NULL; do { tag = tvio_get_next_tag( file ); if ( tag == NULL ) break; if ( name->load( file, tag ) ) { continue; } if ( visible->load( file, tag ) ) continue; if ( selectable->load( file, tag ) ) continue; if ( render->load( file, tag ) ) continue; Object3D *obj = objlist->create_object( tag ); if ( obj != NULL ) { if ( obj->load( file, tag ) ) { add_object( obj, NULL ); if ( obj->get_type() == TV_OBJ3D_CAMERA ) objlist->set_camera( obj ); continue; } else delete obj; } tvio_skip_section(file ); } while ( tag != NULL ); return true; } //*********************************************** // Objects manipulation //*********************************************** //*********************************************** // Remove object // remove an object from the layer and from the ctree // don't delete the object ( need to be stored for undo ) //*********************************************** void ObjectLayer::remove_object( Object3D *obj ) { // Object belong to CSG ? if ( obj->get_parent() != NULL ) { obj->get_parent()->remove_object( obj ); return; } // Finding the object vector::iterator object; unsigned int i = 0; for ( object = objects.begin() ; object != objects.end() ; object++ ) { i++; if ( *object != obj ) continue; Object3D *target = *object; if ( target->get_type() == TV_OBJ3D_CAMERA ) { app_warning( _("You shoudn't delete the camera ;-)") ); return; } OBJLIST_DEF // Managing the tree if ( i < 0 ) i = 0; if ( i > objects.size() - 1 ) i = objects.size() - 2; VMAN_DEF vmanager->freeze(); if ( objlist->get_current_layer() == this ) { target->remove_from_tree(); if ( objects.size() > 1 ) objects[i]->tree_node_select(); else objlist->clear_current_object(); } vmanager->thaw(); // Modifying lists objlist->remove_object( target ); objects.erase( object ); break; } } //**************************************************** // Duplicate object // duplicate the current object and insert the copy under the // current object //**************************************************** void ObjectLayer::duplicate_object( Object3D *obj ) { if ( obj->get_parent() != NULL ) obj->get_parent()->duplicate_object( obj ); else { vector::iterator object; for ( object = objects.begin() ; object != objects.end() ; object++ ) { if ( *object != obj ) continue; if ( obj->get_type() == TV_OBJ3D_CAMERA || obj->get_category() == TV_OBJ3D_ATMOS1 ) { app_warning( _("You can't duplicate this object") ); return; } OBJLIST_DEF Object3D *newobj = obj->duplicate_yourself(); objlist->add_object( newobj ); Object3D *parent = obj->get_parent(); Object3D *target = *(++object); newobj->add_to_tree( obj_tree_view, obj_tree_store, obj_tree_selection, (parent == NULL ) ? NULL : parent->get_node() , ( object != objects.end() ) ? target->get_node() : NULL ); objects.insert( object, newobj ); newobj->tree_node_select(); break; } } } //*************************************************** // Paste object // paste object previously copied and stored in ObjectList //*************************************************** void ObjectLayer::paste_object( Object3D *obj, Object3D* selected ) { if ( selected != NULL ) { if ( selected->is_group() ) { selected->paste_object( obj ); return; } } append_object( obj ); //obj->ctree_node_select(); obj->set_parent( NULL ); obj->set_layer( this ); } //*************************************************** // Move objects up and down //*************************************************** void ObjectLayer::move_object_up( Object3D *obj ) { if ( obj->get_parent() == NULL ) { vector::iterator object; int i = 0; for ( object = objects.begin() ; object != objects.end() ; object++ ) { i++; if ( *object != obj ) continue; if ( i == 1 ) return; Object3D *target = *object; vector::iterator object2 = object; object2--; gtk_tree_store_move_before( obj_tree_store, target->get_node(), (*object2)->get_node() ); objects.erase( object ); objects.insert( object2, target ); break; } } else // Child of another object, let him care about this... obj->get_parent()->move_child_up( obj ); } void ObjectLayer::move_object_down( Object3D *obj ) { if ( obj->get_parent() == NULL ) { vector::iterator object; unsigned int i = 0; for ( object = objects.begin() ; object != objects.end() ; object++ ) { i++; if ( *object != obj ) continue; if ( i == objects.size() ) return; Object3D *target = *object; vector::iterator object2 = object; object2 += 1; //gtk_ctree_move( GTK_CTREE(obj_ctree), (*object2)->get_node(), NULL, target->get_node() ); gtk_tree_store_move_after( obj_tree_store, target->get_node(), (*object2)->get_node() ); objects.erase( object ); objects.insert( object2, target ); break; } } else // Child of another object, let him care about this... obj->get_parent()->move_child_down( obj ); } // ************************************************************************* // Unparent object // // Remove object from a group and move it to group's parent / root //************************************************************************** void ObjectLayer::unparent_object( Object3D *obj ) { Object3D *parent = obj->get_parent(); if ( parent == NULL ) return; parent->remove_object( obj ); Object3D *grand_parent = parent->get_parent(); if ( grand_parent == NULL ) { int pos = get_object_pos( parent ); insert_object( obj, pos ); } else { int pos = grand_parent->get_child_pos( parent ); grand_parent->insert_child( obj, pos ); } }