diff options
author | Juan Linietsky <reduzio@gmail.com> | 2014-02-09 22:10:30 -0300 |
---|---|---|
committer | Juan Linietsky <reduzio@gmail.com> | 2014-02-09 22:10:30 -0300 |
commit | 0b806ee0fc9097fa7bda7ac0109191c9c5e0a1ac (patch) | |
tree | 276c4d099e178eb67fbd14f61d77b05e3808e9e3 /modules | |
parent | 0e49da1687bc8192ed210947da52c9e5c5f301bb (diff) |
GODOT IS OPEN SOURCE
Diffstat (limited to 'modules')
-rw-r--r-- | modules/SCsub | 24 | ||||
-rw-r--r-- | modules/gridmap/SCsub | 6 | ||||
-rw-r--r-- | modules/gridmap/config.py | 11 | ||||
-rw-r--r-- | modules/gridmap/grid_map.cpp | 1532 | ||||
-rw-r--r-- | modules/gridmap/grid_map.h | 271 | ||||
-rw-r--r-- | modules/gridmap/grid_map_editor_plugin.cpp | 1470 | ||||
-rw-r--r-- | modules/gridmap/grid_map_editor_plugin.h | 243 | ||||
-rw-r--r-- | modules/gridmap/register_types.cpp | 47 | ||||
-rw-r--r-- | modules/gridmap/register_types.h | 30 | ||||
-rw-r--r-- | modules/register_module_types.h | 9 |
10 files changed, 3643 insertions, 0 deletions
diff --git a/modules/SCsub b/modules/SCsub new file mode 100644 index 0000000000..811bc5e2aa --- /dev/null +++ b/modules/SCsub @@ -0,0 +1,24 @@ +Import('env')
+
+env_modules = env.Clone()
+
+Export('env_modules')
+
+env.modules_sources=[
+ "register_module_types.cpp",
+]
+#env.add_source_files(env.modules_sources,"*.cpp")
+Export('env')
+
+
+for x in env.module_list:
+ if (x in env.disabled_modules):
+ continue
+ env_modules.Append(CPPFLAGS=["-DMODULE_"+x.upper()+"_ENABLED"])
+ SConscript(x+"/SCsub")
+
+lib = env_modules.Library("modules",env.modules_sources)
+
+env.Prepend(LIBS=[lib])
+
+
diff --git a/modules/gridmap/SCsub b/modules/gridmap/SCsub new file mode 100644 index 0000000000..4cb47e7e67 --- /dev/null +++ b/modules/gridmap/SCsub @@ -0,0 +1,6 @@ +Import('env') + +env.add_source_files(env.modules_sources,"*.cpp") + + + diff --git a/modules/gridmap/config.py b/modules/gridmap/config.py new file mode 100644 index 0000000000..f9bd7da08d --- /dev/null +++ b/modules/gridmap/config.py @@ -0,0 +1,11 @@ + + +def can_build(platform): + return True + + +def configure(env): + pass + + + diff --git a/modules/gridmap/grid_map.cpp b/modules/gridmap/grid_map.cpp new file mode 100644 index 0000000000..7ccd85702d --- /dev/null +++ b/modules/gridmap/grid_map.cpp @@ -0,0 +1,1532 @@ +/*************************************************************************/ +/* grid_map.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "grid_map.h" +#include "servers/visual_server.h" +#include "scene/resources/surface_tool.h" +#include "message_queue.h" +#include "scene/3d/light.h" +#include "io/marshalls.h" + + +bool GridMap::_set(const StringName& p_name, const Variant& p_value) { + + String name=p_name; + + if (name=="theme/theme") { + + set_theme(p_value); + } else if (name=="cell/size") { + set_cell_size(p_value); + } else if (name=="cell/octant_size") { + set_octant_size(p_value); + } else if (name=="cell/center_x") { + set_center_x(p_value); + } else if (name=="cell/center_y") { + set_center_y(p_value); + } else if (name=="cell/center_z") { + set_center_z(p_value); + } else if (name=="cell/scale") { + set_cell_scale(p_value); + } else if (name=="theme/bake") { + set_bake(p_value); +/* } else if (name=="cells") { + DVector<int> cells = p_value; + int amount=cells.size(); + DVector<int>::Read r = cells.read(); + ERR_FAIL_COND_V(amount&1,false); // not even + cell_map.clear();; + for(int i=0;i<amount/3;i++) { + + + IndexKey ik; + ik.key=decode_uint64(&r[i*3]); + Cell cell; + cell.cell=uint32_t(r[i*+1]); + cell_map[ik]=cell; + + } + _recreate_octant_data();*/ + } else if (name=="data") { + + Dictionary d = p_value; + + Dictionary baked; + if (d.has("baked")) + baked=d["baked"]; + if (d.has("cells")) { + + DVector<int> cells = d["cells"]; + int amount=cells.size(); + DVector<int>::Read r = cells.read(); + ERR_FAIL_COND_V(amount%3,false); // not even + cell_map.clear();; + for(int i=0;i<amount/3;i++) { + + IndexKey ik; + ik.key=decode_uint64((const uint8_t*)&r[i*3]); + Cell cell; + cell.cell=decode_uint32((const uint8_t*)&r[i*3+2]); + cell_map[ik]=cell; + + } + } + baked_lock=baked.size()!=0; + _recreate_octant_data(); + baked_lock=false; + if (!baked.empty()) { + List<Variant> kl; + baked.get_key_list(&kl); + for (List<Variant>::Element *E=kl.front();E;E=E->next()) { + + Plane ikv = E->get(); + Ref<Mesh> b=baked[ikv]; + ERR_CONTINUE(!b.is_valid()); + OctantKey ok; + ok.x=ikv.normal.x; + ok.y=ikv.normal.y; + ok.z=ikv.normal.z; + ok.area=ikv.d; + + ERR_CONTINUE(!octant_map.has(ok)); + + Octant &g = *octant_map[ok]; + + g.baked=b; + g.bake_instance=VS::get_singleton()->instance_create();; + VS::get_singleton()->instance_set_base(g.bake_instance,g.baked->get_rid()); + } + } + + + } else if (name.begins_with("areas/")) { + int which = name.get_slice("/",1).to_int(); + String what=name.get_slice("/",2); + if (what=="bounds") { + ERR_FAIL_COND_V(area_map.has(which),false); + create_area(which,p_value); + return true; + } + + ERR_FAIL_COND_V(!area_map.has(which),false); + + if (what=="name") + area_set_name(which,p_value); + else if (what=="disable_distance") + area_set_portal_disable_distance(which,p_value); + else if (what=="exterior_portal") + area_set_portal_disable_color(which,p_value); + else + return false; + } else + return false; + + return true; + + +} + +bool GridMap::_get(const StringName& p_name,Variant &r_ret) const { + + String name=p_name; + + if (name=="theme/theme") { + r_ret= get_theme(); + } else if (name=="cell/size") { + r_ret= get_cell_size(); + } else if (name=="cell/octant_size") { + r_ret= get_octant_size(); + } else if (name=="cell/center_x") { + r_ret= get_center_x(); + } else if (name=="cell/center_y") { + r_ret= get_center_y(); + } else if (name=="cell/center_z") { + r_ret= get_center_z(); + } else if (name=="cell/scale") { + r_ret= cell_scale; + } else if (name=="theme/bake") { + r_ret= bake; + } else if (name=="data") { + + Dictionary d; + + DVector<int> cells; + cells.resize(cell_map.size()*3); + { + DVector<int>::Write w = cells.write(); + int i=0; + for (Map<IndexKey,Cell>::Element *E=cell_map.front();E;E=E->next(),i++) { + + encode_uint64(E->key().key,(uint8_t*)&w[i*3]); + encode_uint32(E->get().cell,(uint8_t*)&w[i*3+2]); + } + } + + d["cells"]=cells; + + Dictionary baked; + for(Map<OctantKey,Octant*>::Element *E=octant_map.front();E;E=E->next()) { + + Octant &g=*E->get(); + + if (g.baked.is_valid()) { + + baked[Plane(E->key().x,E->key().y,E->key().z,E->key().area)]=g.baked; + } + + + } + + if (baked.size()) { + d["baked"]=baked; + } + + r_ret= d; + } else if (name.begins_with("areas/")) { + int which = name.get_slice("/",1).to_int(); + String what=name.get_slice("/",2); + if (what=="bounds") + r_ret= area_get_bounds(which); + else if (what=="name") + r_ret= area_get_name(which); + else if (what=="disable_distance") + r_ret= area_get_portal_disable_distance(which); + else if (what=="exterior_portal") + r_ret= area_is_exterior_portal(which); + else + return false; + } else + return false; + + return true; + +} + +void GridMap::_get_property_list( List<PropertyInfo> *p_list) const { + + p_list->push_back( PropertyInfo( Variant::OBJECT, "theme/theme", PROPERTY_HINT_RESOURCE_TYPE, "MeshLibrary")); + p_list->push_back( PropertyInfo( Variant::BOOL, "theme/bake")); + p_list->push_back( PropertyInfo( Variant::REAL, "cell/size",PROPERTY_HINT_RANGE,"0.01,16384,0.01") ); + p_list->push_back( PropertyInfo( Variant::INT, "cell/octant_size",PROPERTY_HINT_RANGE,"1,1024,1") ); + p_list->push_back( PropertyInfo( Variant::BOOL, "cell/center_x") ); + p_list->push_back( PropertyInfo( Variant::BOOL, "cell/center_y") ); + p_list->push_back( PropertyInfo( Variant::BOOL, "cell/center_z") ); + p_list->push_back( PropertyInfo( Variant::REAL, "cell/scale") ); + + p_list->push_back( PropertyInfo( Variant::DICTIONARY, "data", PROPERTY_HINT_NONE,"",PROPERTY_USAGE_STORAGE) ); + + for(const Map<int,Area*>::Element *E=area_map.front();E;E=E->next()) { + + String base="areas/"+itos(E->key())+"/"; + p_list->push_back( PropertyInfo( Variant::_AABB, base+"bounds", PROPERTY_HINT_NONE,"",PROPERTY_USAGE_STORAGE) ); + p_list->push_back( PropertyInfo( Variant::STRING, base+"name", PROPERTY_HINT_NONE,"",PROPERTY_USAGE_STORAGE) ); + p_list->push_back( PropertyInfo( Variant::REAL, base+"disable_distance", PROPERTY_HINT_NONE,"",PROPERTY_USAGE_STORAGE) ); + p_list->push_back( PropertyInfo( Variant::COLOR, base+"disable_color", PROPERTY_HINT_NONE,"",PROPERTY_USAGE_STORAGE) ); + p_list->push_back( PropertyInfo( Variant::BOOL, base+"exterior_portal", PROPERTY_HINT_NONE,"",PROPERTY_USAGE_STORAGE) ); + } +} + + +void GridMap::set_theme(const Ref<MeshLibrary>& p_theme) { + + if (!theme.is_null()) + theme->unregister_owner(this); + theme=p_theme; + if (!theme.is_null()) + theme->register_owner(this); + + _recreate_octant_data(); + _change_notify("theme"); +} + +Ref<MeshLibrary> GridMap::get_theme() const{ + + return theme; +} + +void GridMap::set_cell_size(float p_size){ + + cell_size=p_size; + _recreate_octant_data(); + +} +float GridMap::get_cell_size() const{ + + return cell_size; +} + +void GridMap::set_octant_size(int p_size){ + + octant_size=p_size; + _recreate_octant_data(); +} +int GridMap::get_octant_size() const{ + + return octant_size; +} + +void GridMap::set_center_x(bool p_enable) { + + center_x=p_enable; + _recreate_octant_data(); +} + +bool GridMap::get_center_x() const { + return center_x; +} + +void GridMap::set_center_y(bool p_enable) { + + center_y=p_enable; + _recreate_octant_data(); +} + +bool GridMap::get_center_y() const { + return center_y; +} + +void GridMap::set_center_z(bool p_enable) { + + center_z=p_enable; + _recreate_octant_data(); +} + +bool GridMap::get_center_z() const { + return center_z; +} + + +int GridMap::_find_area(const IndexKey& p_pos) const { + + for(const Map<int,Area*>::Element *E=area_map.front();E;E=E->next()) { + //this should somehow be faster... + const Area& a=*E->get(); + if ( p_pos.x>=a.from.x && p_pos.x<a.to.x && + p_pos.y>=a.from.y && p_pos.y<a.to.y && + p_pos.z>=a.from.z && p_pos.z<a.to.z ) { + return E->key(); + } + } + + return 0; +} +void GridMap::set_cell_item(int p_x,int p_y,int p_z, int p_item,int p_rot){ + + ERR_FAIL_INDEX(ABS(p_x),1<<20); + ERR_FAIL_INDEX(ABS(p_y),1<<20); + ERR_FAIL_INDEX(ABS(p_z),1<<20); + + IndexKey key; + key.x=p_x; + key.y=p_y; + key.z=p_z; + + OctantKey ok; + ok.x=p_x/octant_size; + ok.y=p_y/octant_size; + ok.z=p_z/octant_size; + ok.area = _find_area(key); + + + if (cell_map.has(key)) { + + int prev_item=cell_map[key].item; + + OctantKey octantkey=ok; + + ERR_FAIL_COND(!octant_map.has(octantkey)); + Octant& g = *octant_map[octantkey]; + ERR_FAIL_COND(!g.items.has(prev_item)); + ERR_FAIL_COND(!g.items[prev_item].cells.has(key)); + + + g.items[prev_item].cells.erase(key); + if (g.items[prev_item].cells.size()==0) { + VS::get_singleton()->free(g.items[prev_item].multimesh_instance); + g.items.erase(prev_item); + + } + + if (g.items.empty() || !baked_lock) { + //unbake just in case + if (g.baked.is_valid()) { + VS::get_singleton()->free(g.bake_instance); + g.bake_instance=RID(); + g.baked=Ref<Mesh>(); + } + + + } + if (g.items.empty()) { + + PhysicsServer::get_singleton()->free(g.static_body); + + memdelete(&g); + octant_map.erase(octantkey); + } else { + + + g.dirty=true; + } + cell_map.erase(key); + + _queue_dirty_map(); + } + + if (p_item<0) + return; + + OctantKey octantkey=ok; + + //add later + if (!octant_map.has(octantkey)) { + + + Octant *g = memnew( Octant ); + g->dirty=true; + g->static_body = PhysicsServer::get_singleton()->body_create(PhysicsServer::BODY_MODE_STATIC); + if (is_inside_world()) + PhysicsServer::get_singleton()->body_set_space(g->static_body,get_world()->get_space()); + + octant_map[octantkey]=g; + } + + Octant& g = *octant_map[octantkey]; + if (!g.items.has(p_item)) { + + Octant::ItemInstances ii; + if (theme.is_valid() && theme->has_item(p_item)) { + ii.mesh=theme->get_item_mesh(p_item); + ii.shape=theme->get_item_shape(p_item); + } + ii.multimesh = Ref<MultiMesh>( memnew( MultiMesh ) ); + ii.multimesh->set_mesh(ii.mesh); + ii.multimesh_instance = VS::get_singleton()->instance_create(); + VS::get_singleton()->instance_set_base(ii.multimesh_instance,ii.multimesh->get_rid()); + if (!baked_lock) { + + //unbake just in case + if (g.bake_instance.is_valid()) + VS::get_singleton()->free(g.bake_instance); + g.baked=Ref<Mesh>(); + if (is_inside_world()) { + VS::get_singleton()->instance_set_scenario(ii.multimesh_instance,get_world()->get_scenario()); + if (ok.area) { + VS::get_singleton()->instance_set_room( ii.multimesh_instance,area_map[ok.area]->instance); + } + } + } + g.items[p_item]=ii; + } + + Octant::ItemInstances &ii = g.items[p_item]; + ii.cells.insert(key); + g.dirty=true; + + _queue_dirty_map(); + + cell_map[key]=Cell(); + Cell &c=cell_map[key]; + c.item=p_item; + c.rot=p_rot; + +} + +int GridMap::get_cell_item(int p_x,int p_y,int p_z) const{ + + ERR_FAIL_INDEX_V(ABS(p_x),1<<20,INVALID_CELL_ITEM); + ERR_FAIL_INDEX_V(ABS(p_y),1<<20,INVALID_CELL_ITEM); + ERR_FAIL_INDEX_V(ABS(p_z),1<<20,INVALID_CELL_ITEM); + + + IndexKey key; + key.x=p_x; + key.y=p_y; + key.z=p_z; + + if (!cell_map.has(key)) + return INVALID_CELL_ITEM; + return cell_map[key].item; + +} + +int GridMap::get_cell_item_orientation(int p_x,int p_y,int p_z) const{ + + ERR_FAIL_INDEX_V(ABS(p_x),1<<20,-1); + ERR_FAIL_INDEX_V(ABS(p_y),1<<20,-1); + ERR_FAIL_INDEX_V(ABS(p_z),1<<20,-1); + + IndexKey key; + key.x=p_x; + key.y=p_y; + key.z=p_z; + + if (!cell_map.has(key)) + return -1; + return cell_map[key].rot; + +} + +void GridMap::_octant_enter_world(const OctantKey &p_key) { + + ERR_FAIL_COND(!octant_map.has(p_key)); + Octant&g = *octant_map[p_key]; + PhysicsServer::get_singleton()->body_set_state(g.static_body,PhysicsServer::BODY_STATE_TRANSFORM,get_global_transform()); + PhysicsServer::get_singleton()->body_set_space(g.static_body,get_world()->get_space()); + //print_line("BODYPOS: "+get_global_transform()); + + + if (g.baked.is_valid()) { + + Transform xf = get_global_transform(); + xf.translate(_octant_get_offset(p_key)); + + VS::get_singleton()->instance_set_transform(g.bake_instance,xf); + VS::get_singleton()->instance_set_scenario(g.bake_instance,get_world()->get_scenario()); + if (area_map.has(p_key.area)) { + VS::get_singleton()->instance_set_room(g.bake_instance,area_map[p_key.area]->instance); + + } + } else { + for(Map<int,Octant::ItemInstances>::Element *E=g.items.front();E;E=E->next()) { + + VS::get_singleton()->instance_set_scenario(E->get().multimesh_instance,get_world()->get_scenario()); + VS::get_singleton()->instance_set_transform(E->get().multimesh_instance,get_global_transform()); + //print_line("INSTANCEPOS: "+get_global_transform()); + + if (area_map.has(p_key.area)) { + VS::get_singleton()->instance_set_room(E->get().multimesh_instance,area_map[p_key.area]->instance); + } + } + } + +} + + +void GridMap::_octant_transform(const OctantKey &p_key) { + + ERR_FAIL_COND(!octant_map.has(p_key)); + Octant&g = *octant_map[p_key]; + PhysicsServer::get_singleton()->body_set_state(g.static_body,PhysicsServer::BODY_STATE_TRANSFORM,get_global_transform()); + + if (g.baked.is_valid()) { + + Transform xf = get_global_transform(); + xf.origin+=_octant_get_offset(p_key); + VS::get_singleton()->instance_set_transform(g.bake_instance,xf); + } else { + for(Map<int,Octant::ItemInstances>::Element *E=g.items.front();E;E=E->next()) { + + VS::get_singleton()->instance_set_transform(E->get().multimesh_instance,get_global_transform()); + //print_line("UPDATEPOS: "+get_global_transform()); + } + } + +} + + +void GridMap::_octant_update(const OctantKey &p_key) { + + ERR_FAIL_COND(!octant_map.has(p_key)); + Octant&g = *octant_map[p_key]; + if (!g.dirty) + return; + + Ref<Mesh> mesh; + + PhysicsServer::get_singleton()->body_clear_shapes(g.static_body); + + for(Map<int,Octant::ItemInstances>::Element *E=g.items.front();E;E=E->next()) { + + Octant::ItemInstances &ii=E->get(); + ii.multimesh->set_instance_count(ii.cells.size()); + + + AABB aabb; + AABB mesh_aabb = ii.mesh.is_null()?AABB():ii.mesh->get_aabb(); + + Vector3 ofs(cell_size*0.5*int(center_x),cell_size*0.5*int(center_y),cell_size*0.5*int(center_z)); + + + //print_line("OCTANT, CELLS: "+itos(ii.cells.size())); + int idx=0; + for(Set<IndexKey>::Element *F=ii.cells.front();F;F=F->next()) { + IndexKey ik=F->get(); + Map<IndexKey,Cell>::Element *C=cell_map.find(ik); + ERR_CONTINUE(!C); + + Vector3 cellpos = Vector3(ik.x,ik.y,ik.z ); + + Transform xform; + + if (clip && ( (clip_above && cellpos[clip_axis]>clip_floor) || (!clip_above && cellpos[clip_axis]<clip_floor))) { + + xform.basis.set_zero(); + + } else { + + xform.basis.set_orthogonal_index(C->get().rot); + } + + + xform.set_origin( cellpos*cell_size+ofs); + xform.basis.scale(Vector3(cell_scale,cell_scale,cell_scale)); + + ii.multimesh->set_instance_transform(idx,xform); + ii.multimesh->set_instance_color(idx,Color(1,1,1,1)); + //print_line("MMINST: "+xform); + + + if(idx==0) { + + aabb=xform.xform(mesh_aabb); + } else { + + aabb.merge_with(xform.xform(mesh_aabb)); + } + + if (ii.shape.is_valid()) { + + PhysicsServer::get_singleton()->body_add_shape(g.static_body,ii.shape->get_rid(),xform); + // print_line("PHIS x: "+xform); + + } + + idx++; + } + + ii.multimesh->set_aabb(aabb); + + + } + + g.dirty=false; + +} + + +void GridMap::_octant_exit_world(const OctantKey &p_key) { + + ERR_FAIL_COND(!octant_map.has(p_key)); + Octant&g = *octant_map[p_key]; + PhysicsServer::get_singleton()->body_set_state(g.static_body,PhysicsServer::BODY_STATE_TRANSFORM,get_global_transform()); + PhysicsServer::get_singleton()->body_set_space(g.static_body,RID()); + + + if (g.baked.is_valid()) { + + VS::get_singleton()->instance_set_room(g.bake_instance,RID()); + VS::get_singleton()->instance_set_scenario(g.bake_instance,RID()); + + } + + for(Map<int,Octant::ItemInstances>::Element *E=g.items.front();E;E=E->next()) { + + VS::get_singleton()->instance_set_scenario(E->get().multimesh_instance,RID()); + // VS::get_singleton()->instance_set_transform(E->get().multimesh_instance,get_global_transform()); + VS::get_singleton()->instance_set_room(E->get().multimesh_instance,RID()); + } + +} + +void GridMap::_octant_clear_baked(const OctantKey &p_key) { + + + ERR_FAIL_COND(!octant_map.has(p_key)); + Octant&g = *octant_map[p_key]; + + if (!g.baked.is_valid()) + return; + + VS::get_singleton()->free(g.bake_instance); + g.bake_instance=RID(); + g.baked=Ref<Mesh>(); + + if (is_inside_scene()) + _octant_enter_world(p_key); + g.dirty=true; + _queue_dirty_map(); +} + +void GridMap::_octant_bake(const OctantKey &p_key, const Ref<TriangleMesh>& p_tmesh,const Vector<BakeLight> &p_lights,List<Vector3> *p_prebake) { + + + ERR_FAIL_COND(!octant_map.has(p_key)); + Octant&g = *octant_map[p_key]; + + Ref<TriangleMesh> tm=p_tmesh; + if (!p_prebake && is_inside_world()) + _octant_exit_world(p_key); + + Map< Ref<Material>, Ref<SurfaceTool> > surfaces; + Vector3 ofs(cell_size*0.5*int(center_x),cell_size*0.5*int(center_y),cell_size*0.5*int(center_z)); + Vector3 octant_ofs=_octant_get_offset(p_key); + + for(Map<int,Octant::ItemInstances>::Element *E=g.items.front();E;E=E->next()) { + + Octant::ItemInstances &ii=E->get(); + + if (ii.mesh.is_null()) + continue; + + for(Set<IndexKey>::Element *F=ii.cells.front();F;F=F->next()) { + + IndexKey ik=F->get(); + Map<IndexKey,Cell>::Element *C=cell_map.find(ik); + ERR_CONTINUE(!C); + Vector3 cellpos = Vector3(ik.x,ik.y,ik.z ); + + Transform xform; + xform.basis.set_orthogonal_index(C->get().rot); + xform.set_origin( cellpos*cell_size+ofs); + if (!p_prebake) + xform.origin-=octant_ofs; + + + for(int i=0;i<ii.mesh->get_surface_count();i++) { + + if (p_prebake) { + + if (ii.mesh->surface_get_primitive_type(i)!=Mesh::PRIMITIVE_TRIANGLES) + continue; + Array a = ii.mesh->surface_get_arrays(i); + DVector<Vector3> av=a[VS::ARRAY_VERTEX]; + int avs = av.size(); + DVector<Vector3>::Read vr = av.read(); + + DVector<int> ai=a[VS::ARRAY_INDEX]; + int ais=ai.size(); + if (ais) { + + DVector<int>::Read ir=ai.read(); + for(int j=0;j<ais;j++) { + + p_prebake->push_back(xform.xform(vr[ir[j]])); + //print_line("V SET: "+xform.xform(vr[ir[j]])); + } + + } else { + + for(int j=0;j<avs;j++) { + + p_prebake->push_back(xform.xform(vr[j])); + } + } + + } else { + + Ref<Material> m = ii.mesh->surface_get_material(i); + + Map< Ref<Material>, Ref<SurfaceTool> >::Element *S=surfaces.find(m); + + if (!S) { + + S=surfaces.insert(m,Ref<SurfaceTool>( memnew( SurfaceTool ))); + } + + Ref<SurfaceTool> st = S->get(); + List<SurfaceTool::Vertex>::Element *V=st->get_vertex_array().back(); + st->append_from(ii.mesh,i,xform); + st->set_material(m); + + + if (tm.is_valid()) { + + if (V) + V=V->next(); + else + V=st->get_vertex_array().front();; + int lc = p_lights.size(); + const BakeLight* bl = p_lights.ptr(); + float ofs = cell_size*0.02; + float att = 0.2; + + + for(;V;V=V->next()) { + + SurfaceTool::Vertex &v=V->get(); + + Vector3 vertex = v.vertex + octant_ofs; + //print_line("V GET: "+vertex); + Vector3 normal = tm->get_area_normal( AABB( Vector3(-ofs,-ofs,-ofs)+vertex,Vector3(ofs,ofs,ofs)*2.0)); + if (normal==Vector3()) { + print_line("couldn't find for vertex: "+vertex); + } + ERR_CONTINUE( normal== Vector3()); + + float max_l=1.0; + float max_dist=1.0; + + if (lc) { + + for(int j=0;j<lc;j++) { + const BakeLight &l=bl[j]; + switch(l.type) { + case VS::LIGHT_DIRECTIONAL: { + + Vector3 ray_from=vertex + normal *ofs; + Vector3 ray_to=l.dir*5000; + Vector3 n; + Vector3 p; + if (tm->intersect_segment(ray_from,ray_to,p,n)) { + + float dist = 1.0-l.param[VS::LIGHT_PARAM_SHADOW_DARKENING]; + if (dist<=max_dist) { + max_dist=dist; + max_l=1.0-dist; + } + } + } break; + } + + } + } + + v.color=Color(max_l,max_l,max_l,1.0); + + } + + st->add_to_format(VS::ARRAY_FORMAT_COLOR); + if (m.is_valid()) { + Ref<FixedMaterial> fm = m; + if (fm.is_valid()) + fm->set_fixed_flag(FixedMaterial::FLAG_USE_COLOR_ARRAY,true); + } + } + } + } + } + } + + if (p_prebake) + return; + + g.baked = Ref<Mesh>( memnew( Mesh )); + + for(Map< Ref<Material>, Ref<SurfaceTool> >::Element *E=surfaces.front();E;E=E->next()) { + + Ref<SurfaceTool> st = E->get(); + st->commit(g.baked); + } + + g.bake_instance = VS::get_singleton()->instance_create(); + VS::get_singleton()->instance_set_base(g.bake_instance,g.baked->get_rid()); + + if (is_inside_world()) + _octant_enter_world(p_key); + + g.dirty=true; + _queue_dirty_map(); +} + +void GridMap::_notification(int p_what) { + + + switch(p_what) { + + case NOTIFICATION_ENTER_WORLD: { + + _update_area_instances(); + + for(Map<OctantKey,Octant*>::Element *E=octant_map.front();E;E=E->next()) { +// IndexKey ik; +// ik.key = E->key().indexkey; + _octant_enter_world(E->key()); + _octant_update(E->key()); + } + + awaiting_update=false; + + last_transform=get_global_transform(); + } break; + case NOTIFICATION_TRANSFORM_CHANGED: { + + Transform new_xform = get_global_transform(); + if (new_xform==last_transform) + break; + //update run + for(Map<OctantKey,Octant*>::Element *E=octant_map.front();E;E=E->next()) { + _octant_transform(E->key()); + } + + last_transform=new_xform; + + } break; + case NOTIFICATION_EXIT_WORLD: { + + for(Map<OctantKey,Octant*>::Element *E=octant_map.front();E;E=E->next()) { + _octant_exit_world(E->key()); + } + + //_queue_dirty_map(MAP_DIRTY_INSTANCES|MAP_DIRTY_TRANSFORMS); + //_update_dirty_map_callback(); + //_update_area_instances(); + + } break; + } +} + + + +void GridMap::_queue_dirty_map() { + + if (awaiting_update) + return; + + if (is_inside_world()) { + + MessageQueue::get_singleton()->push_call(this,"_update_dirty_map_callback"); + awaiting_update=true; + } +} + +void GridMap::_recreate_octant_data() { + + Map<IndexKey,Cell> cell_copy=cell_map; + _clear_internal(true); + for (Map<IndexKey,Cell>::Element *E=cell_copy.front();E;E=E->next()) { + + set_cell_item(E->key().x,E->key().y,E->key().z,E->get().item,E->get().rot); + } + +} + +void GridMap::_clear_internal(bool p_keep_areas) { + + for(Map<OctantKey,Octant*>::Element *E=octant_map.front();E;E=E->next()) { + if (is_inside_world()) + _octant_exit_world(E->key()); + + for (Map<int,Octant::ItemInstances>::Element *F=E->get()->items.front();F;F=F->next()) { + + VS::get_singleton()->free(F->get().multimesh_instance); + } + + //unbake just in case + if (E->get()->bake_instance.is_valid()) + VS::get_singleton()->free(E->get()->bake_instance); + + PhysicsServer::get_singleton()->free(E->get()->static_body); + memdelete(E->get()); + + } + + octant_map.clear(); + cell_map.clear(); + + if (p_keep_areas) + return; + + for (Map<int,Area*>::Element *E=area_map.front();E;E=E->next()) { + + + VS::get_singleton()->free(E->get()->base_portal); + VS::get_singleton()->free(E->get()->instance); + for(int i=0;i<E->get()->portals.size();i++) { + VS::get_singleton()->free(E->get()->portals[i].instance); + } + + memdelete(E->get()); + } + +} + +void GridMap::clear() { + + _clear_internal(); + +} + +void GridMap::resource_changed(const RES& p_res) { + + _recreate_octant_data(); +} + + +void GridMap::_update_dirty_map_callback() { + + if (!awaiting_update) + return; + + for(Map<OctantKey,Octant*>::Element *E=octant_map.front();E;E=E->next()) { + _octant_update(E->key()); + } + + + awaiting_update=false; + +} + + +void GridMap::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("set_theme","theme:MeshLibrary"),&GridMap::set_theme); + ObjectTypeDB::bind_method(_MD("get_theme:MeshLibrary"),&GridMap::get_theme); + + ObjectTypeDB::bind_method(_MD("set_bake","enable"),&GridMap::set_bake); + ObjectTypeDB::bind_method(_MD("is_baking_enabled"),&GridMap::is_baking_enabled); + + ObjectTypeDB::bind_method(_MD("set_cell_size","size"),&GridMap::set_cell_size); + ObjectTypeDB::bind_method(_MD("get_cell_size"),&GridMap::get_cell_size); + + ObjectTypeDB::bind_method(_MD("set_octant_size","size"),&GridMap::set_octant_size); + ObjectTypeDB::bind_method(_MD("get_octant_size"),&GridMap::get_octant_size); + + ObjectTypeDB::bind_method(_MD("set_cell_item","x","y","z","item","orientation"),&GridMap::set_cell_item,DEFVAL(0)); + ObjectTypeDB::bind_method(_MD("get_cell_item","x","y","z"),&GridMap::get_cell_item); + ObjectTypeDB::bind_method(_MD("get_cell_item_orientation","x","y","z"),&GridMap::get_cell_item_orientation); + +// ObjectTypeDB::bind_method(_MD("_recreate_octants"),&GridMap::_recreate_octants); + ObjectTypeDB::bind_method(_MD("_update_dirty_map_callback"),&GridMap::_update_dirty_map_callback); + ObjectTypeDB::bind_method(_MD("resource_changed"),&GridMap::resource_changed); + + ObjectTypeDB::bind_method(_MD("set_center_x","enable"),&GridMap::set_center_x); + ObjectTypeDB::bind_method(_MD("get_center_x"),&GridMap::get_center_x); + ObjectTypeDB::bind_method(_MD("set_center_y","enable"),&GridMap::set_center_y); + ObjectTypeDB::bind_method(_MD("get_center_y"),&GridMap::get_center_y); + ObjectTypeDB::bind_method(_MD("set_center_z","enable"),&GridMap::set_center_z); + ObjectTypeDB::bind_method(_MD("get_center_z"),&GridMap::get_center_z); + + ObjectTypeDB::bind_method(_MD("set_clip","enabled","clipabove","floor","axis"),&GridMap::set_clip,DEFVAL(true),DEFVAL(0),DEFVAL(Vector3::AXIS_X)); + + ObjectTypeDB::bind_method(_MD("crate_area","id","area"),&GridMap::create_area); + ObjectTypeDB::bind_method(_MD("area_get_bounds","area","bounds"),&GridMap::area_get_bounds); + ObjectTypeDB::bind_method(_MD("area_set_exterior_portal","area","enable"),&GridMap::area_set_exterior_portal); + ObjectTypeDB::bind_method(_MD("area_set_name","area","name"),&GridMap::area_set_name); + ObjectTypeDB::bind_method(_MD("area_get_name","area"),&GridMap::area_get_name); + ObjectTypeDB::bind_method(_MD("area_is_exterior_portal","area"),&GridMap::area_is_exterior_portal); + ObjectTypeDB::bind_method(_MD("area_set_portal_disable_distance","area","distance"),&GridMap::area_set_portal_disable_distance); + ObjectTypeDB::bind_method(_MD("area_get_portal_disable_distance","area"),&GridMap::area_get_portal_disable_distance); + ObjectTypeDB::bind_method(_MD("area_set_portal_disable_color","area","color"),&GridMap::area_set_portal_disable_color); + ObjectTypeDB::bind_method(_MD("area_get_portal_disable_color","area"),&GridMap::area_get_portal_disable_color); + ObjectTypeDB::bind_method(_MD("erase_area","area"),&GridMap::erase_area); + ObjectTypeDB::bind_method(_MD("get_unused_area_id","area"),&GridMap::get_unused_area_id); + ObjectTypeDB::bind_method(_MD("bake_geometry"),&GridMap::bake_geometry); + + ObjectTypeDB::set_method_flags("GridMap","bake_geometry",METHOD_FLAGS_DEFAULT|METHOD_FLAG_EDITOR); + + ObjectTypeDB::bind_method(_MD("clear"),&GridMap::clear); + + BIND_CONSTANT( INVALID_CELL_ITEM ); + +} + +void GridMap::set_clip(bool p_enabled, bool p_clip_above, int p_floor, Vector3::Axis p_axis) { + + if (!p_enabled && !clip) + return; + if (clip && p_enabled && clip_floor==p_floor && p_clip_above==clip_above && p_axis==clip_axis) + return; + + clip=p_enabled; + clip_floor=p_floor; + clip_axis=p_axis; + clip_above=p_clip_above; + + //make it all update + for(Map<OctantKey,Octant*>::Element *E=octant_map.front();E;E=E->next()) { + + Octant *g=E->get(); + g->dirty=true; + + } + awaiting_update=true; + _update_dirty_map_callback(); +} + + +void GridMap::_update_areas() { + + //clear the portals + for(Map<int,Area*>::Element *E=area_map.front();E;E=E->next()) { + //this should somehow be faster... + Area& a=*E->get(); + a.portals.clear(); + if (a.instance.is_valid()) { + VisualServer::get_singleton()->free(a.instance); + a.instance=RID(); + } + } + + //test all areas against all areas and create portals - this sucks (slow :( ) + for(Map<int,Area*>::Element *E=area_map.front();E;E=E->next()) { + Area& a=*E->get(); + if (a.exterior_portal) //that's pretty much all it does... yes it is + continue; + Vector3 from_a(a.from.x,a.from.y,a.from.z); + Vector3 to_a(a.to.x,a.to.y,a.to.z); + + for(Map<int,Area*>::Element *F=area_map.front();F;F=F->next()) { + + Area& b=*F->get(); + Vector3 from_b(b.from.x,b.from.y,b.from.z); + Vector3 to_b(b.to.x,b.to.y,b.to.z); + + // initially test intersection and discards + int axis=-1; + float sign=0; + bool valid=true; + Vector3 axmin,axmax; + + + for(int i=0;i<3;i++) { + + if (from_a[i]==to_b[i]) { + + if (axis!=-1) { + valid=false; + break; + } + + axis=i; + sign=-1; + } else if (from_b[i]==to_a[i]) { + + if (axis!=-1) { + valid=false; + break; + } + axis=i; + sign=+1; + } + + + if (from_a[i] > to_b[i] || to_a[i] < from_b[i] ) { + valid=false; + break; + } else { + + axmin[i]= ( from_a[i] > from_b[i] ) ? from_a[i] :from_b[i]; + axmax[i]= ( to_a[i] < to_b[i] ) ? to_a[i] :to_b[i]; + + } + + + } + + if (axis==-1 || !valid) + continue; + + Transform xf; + + + for(int i=0;i<3;i++) { + + + + int ax=(axis+i)%3; + Vector3 axis_vec; + float scale = (i==0)?sign:((axmax[ax]-axmin[ax])*cell_size); + axis_vec[ax]=scale; + xf.basis.set_axis((2+i)%3,axis_vec); + xf.origin[i]=axmin[i]*cell_size; + + } + + + + Area::Portal portal; + portal.xform=xf; + a.portals.push_back(portal); + } + } + + _update_area_instances(); + +} + +void GridMap::_update_area_instances() { + + Transform base_xform; + if (_in_tree) + base_xform=get_global_transform(); + + for(Map<int,Area*>::Element *E=area_map.front();E;E=E->next()) { + //this should somehow be faster... + Area& a=*E->get(); + if (a.instance.is_valid()!=_in_tree) { + + if (!_in_tree) { + + for(int i=0;i<a.portals.size();i++) { + + Area::Portal&p=a.portals[i]; + ERR_CONTINUE(!p.instance.is_valid()); + VisualServer::get_singleton()->free(p.instance); + p.instance=RID(); + } + + VisualServer::get_singleton()->free(a.instance); + a.instance=RID(); + + } else { + + //a.instance = VisualServer::get_singleton()->instance_create2(base_room,get_world()->get_scenario()); + for(int i=0;i<a.portals.size();i++) { + + Area::Portal&p=a.portals[i]; + ERR_CONTINUE(p.instance.is_valid()); + p.instance=VisualServer::get_singleton()->instance_create2(a.base_portal,get_world()->get_scenario()); + VisualServer::get_singleton()->instance_set_room(p.instance,a.instance); + } + } + } + + if (a.instance.is_valid()) { + Transform xform; + + Vector3 from_a(a.from.x,a.from.y,a.from.z); + Vector3 to_a(a.to.x,a.to.y,a.to.z); + + for(int i=0;i<3;i++) { + xform.origin[i]=from_a[i]*cell_size; + Vector3 s; + s[i]=(to_a[i]-from_a[i])*cell_size; + xform.basis.set_axis(i,s); + } + + + VisualServer::get_singleton()->instance_set_transform(a.instance,base_xform * xform); + + for(int i=0;i<a.portals.size();i++) { + + Area::Portal&p=a.portals[i]; + ERR_CONTINUE(!p.instance.is_valid()); + + VisualServer::get_singleton()->instance_set_transform(p.instance,base_xform * xform); + + } + + } + } + +} + +Error GridMap::create_area(int p_id,const AABB& p_bounds) { + + ERR_FAIL_COND_V(area_map.has(p_id),ERR_ALREADY_EXISTS); + ERR_EXPLAIN("ID 0 is taken as global area, start from 1"); + ERR_FAIL_COND_V(p_id==0,ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_bounds.has_no_area(),ERR_INVALID_PARAMETER); + + // FIRST VALIDATE AREA + IndexKey from,to; + from.x=p_bounds.pos.x; + from.y=p_bounds.pos.y; + from.z=p_bounds.pos.z; + to.x=p_bounds.pos.x+p_bounds.size.x; + to.y=p_bounds.pos.y+p_bounds.size.y; + to.z=p_bounds.pos.z+p_bounds.size.z; + + + for(Map<int,Area*>::Element *E=area_map.front();E;E=E->next()) { + //this should somehow be faster... + Area& a=*E->get(); + + //does it interset with anything else? + + if ( from.x >= a.to.x || + to.x <= a.from.x || + from.y >= a.to.y || + to.y <= a.from.y || + from.z >= a.to.z || + to.z <= a.from.z ) { + + // all good + } else { + + return ERR_INVALID_PARAMETER; + } + } + + + Area *area = memnew( Area ); + area->from=from; + area->to=to; + area->portal_disable_distance=0; + area->exterior_portal=false; + area->name="Area "+itos(p_id); + area_map[p_id]=area; + _recreate_octant_data(); + return OK; +} + +AABB GridMap::area_get_bounds(int p_area) const { + + ERR_FAIL_COND_V(!area_map.has(p_area),AABB()); + + const Area *a = area_map[p_area]; + AABB aabb; + aabb.pos=Vector3(a->from.x,a->from.y,a->from.z); + aabb.size=Vector3(a->to.x,a->to.y,a->to.z)-aabb.pos; + + return aabb; +} + +void GridMap::area_set_name(int p_area,const String& p_name) { + + ERR_FAIL_COND(!area_map.has(p_area)); + + Area *a = area_map[p_area]; + a->name=p_name; +} + +String GridMap::area_get_name(int p_area) const { + + ERR_FAIL_COND_V(!area_map.has(p_area),""); + + const Area *a = area_map[p_area]; + return a->name; +} + + +void GridMap::area_set_exterior_portal(int p_area,bool p_enable) { + + ERR_FAIL_COND(!area_map.has(p_area)); + + Area *a = area_map[p_area]; + if (a->exterior_portal==p_enable) + return; + a->exterior_portal=p_enable; + + _recreate_octant_data(); +} + +bool GridMap::area_is_exterior_portal(int p_area) const { + + ERR_FAIL_COND_V(!area_map.has(p_area),false); + + const Area *a = area_map[p_area]; + return a->exterior_portal; +} + +void GridMap::area_set_portal_disable_distance(int p_area, float p_distance) { + + ERR_FAIL_COND(!area_map.has(p_area)); + + Area *a = area_map[p_area]; + a->portal_disable_distance=p_distance; + +} + +float GridMap::area_get_portal_disable_distance(int p_area) const { + + ERR_FAIL_COND_V(!area_map.has(p_area),0); + + const Area *a = area_map[p_area]; + return a->portal_disable_distance; +} + +void GridMap::area_set_portal_disable_color(int p_area, Color p_color) { + + ERR_FAIL_COND(!area_map.has(p_area)); + + Area *a = area_map[p_area]; + a->portal_disable_color=p_color; + +} + +Color GridMap::area_get_portal_disable_color(int p_area) const { + + ERR_FAIL_COND_V(!area_map.has(p_area),Color()); + + const Area *a = area_map[p_area]; + return a->portal_disable_color; +} + +void GridMap::get_area_list(List<int> *p_areas) const { + + for(const Map<int,Area*>::Element *E=area_map.front();E;E=E->next()) { + + p_areas->push_back(E->key()); + } + +} + + +GridMap::Area::Portal::~Portal() { + + if (instance.is_valid()) + VisualServer::get_singleton()->free(instance); +} + + +GridMap::Area::Area() { + + base_portal=VisualServer::get_singleton()->portal_create(); + Vector< Point2 > points; + points.push_back( Point2( 0, 1 ) ); + points.push_back( Point2( 1, 1 ) ); + points.push_back( Point2( 1, 0 ) ); + points.push_back( Point2( 0, 0 ) ); + VisualServer::get_singleton()->portal_set_shape(base_portal,points); + +} + + +GridMap::Area::~Area() { + + if (instance.is_valid()) + VisualServer::get_singleton()->free(instance); + VisualServer::get_singleton()->free(base_portal); +} + +void GridMap::erase_area(int p_area) { + + ERR_FAIL_COND(!area_map.has(p_area)); + + Area* a=area_map[p_area]; + memdelete(a); + area_map.erase(p_area); + _recreate_octant_data(); +} + +int GridMap::get_unused_area_id() const { + + if (area_map.empty()) + return 1; + else + return area_map.back()->key()+1; +} + + +void GridMap::set_bake(bool p_bake) { + + bake=p_bake; + if (bake==false) { + for(Map<OctantKey,Octant*>::Element *E=octant_map.front();E;E=E->next()) { + + _octant_clear_baked(E->key()); + } + } +} + +bool GridMap::is_baking_enabled() const { + + return bake; +} + +void GridMap::set_cell_scale(float p_scale) { + + cell_scale=p_scale; + _queue_dirty_map(); +} + +float GridMap::get_cell_scale() const{ + + return cell_scale; +} + + + +void GridMap::bake_geometry() { + + //used to compute vertex occlusion + Ref<TriangleMesh> tmesh; + Vector<BakeLight> lights; + + if (true) { + + List<Vector3> vertices; + + for(Map<OctantKey,Octant*>::Element *E=octant_map.front();E;E=E->next()) { + _octant_bake(E->key(),tmesh,lights,&vertices); + + } + + DVector<Vector3> vv; + vv.fill_with(vertices); + //print_line("TOTAL VERTICES: "+itos(vv.size())); + tmesh = Ref<TriangleMesh>( memnew( TriangleMesh )); + tmesh->create(vv); + + + for(int i=0;i<get_child_count();i++) { + + if (get_child(i)->cast_to<Light>()) { + Light *l = get_child(i)->cast_to<Light>(); + BakeLight bl; + for(int i=0;i<Light::PARAM_MAX;i++) { + bl.param[i]=l->get_parameter(Light::Parameter(i)); + } + Transform t=l->get_global_transform(); + bl.pos=t.origin; + bl.dir=t.basis.get_axis(2); + bl.type=l->get_light_type(); + lights.push_back(bl); + + } + } + } + + int idx=0; + for(Map<OctantKey,Octant*>::Element *E=octant_map.front();E;E=E->next()) { + if (E->get()->baked.is_valid()) + _octant_clear_baked(E->key()); + + _octant_bake(E->key(),tmesh,lights); + print_line("baking "+itos(idx)+"/"+itos(octant_map.size())); + idx++; + } + +} + + + +GridMap::GridMap() { + + cell_size=2; + octant_size=4; + awaiting_update=false; + _in_tree=false; + center_x=true; + center_y=true; + center_z=true; + + clip=false; + clip_floor=0; + clip_axis=Vector3::AXIS_Z; + clip_above=true; + baked_lock=false; + bake=false; + cell_scale=1.0; + + + + +} + + +GridMap::~GridMap() { + + if (!theme.is_null()) + theme->unregister_owner(this); + + clear(); + +} diff --git a/modules/gridmap/grid_map.h b/modules/gridmap/grid_map.h new file mode 100644 index 0000000000..7a13ace143 --- /dev/null +++ b/modules/gridmap/grid_map.h @@ -0,0 +1,271 @@ +/*************************************************************************/ +/* grid_map.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef GRID_MAP_H +#define GRID_MAP_H + + +#include "scene/resources/mesh_library.h" +#include "scene/3d/spatial.h" +#include "scene/resources/multimesh.h" + +//heh heh, godotsphir!! this shares no code and the design is completely different with previous projects i've done.. +//should scale better with hardware that supports instancing + + +class GridMap : public Spatial { + + + OBJ_TYPE( GridMap, Spatial ); + + enum { + MAP_DIRTY_TRANSFORMS=1, + MAP_DIRTY_INSTANCES=2, + }; + + union IndexKey { + + struct { + int16_t x; + int16_t y; + int16_t z; + }; + uint64_t key; + + _FORCE_INLINE_ bool operator<(const IndexKey& p_key) const { + + return key < p_key.key; + } + + IndexKey() { key=0; } + }; + + union Cell { + + struct { + unsigned int item : 16; + unsigned int rot:5; + unsigned int layer:8; + }; + uint32_t cell; + + Cell() { item=0; rot=0; layer=0; } + }; + + struct Octant { + + struct ItemInstances { + + Set<IndexKey> cells; + Ref<Mesh> mesh; + Ref<Shape> shape; + Ref<MultiMesh> multimesh; + RID multimesh_instance; + + }; + + Ref<Mesh> baked; + RID bake_instance; + + bool dirty; + RID static_body; + + Map<int,ItemInstances> items; + + }; + + union OctantKey { + + struct { + int16_t x; + int16_t y; + int16_t z; + int16_t area; + }; + + uint64_t key; + + _FORCE_INLINE_ bool operator<(const OctantKey& p_key) const { + + return key < p_key.key; + } + + //OctantKey(const IndexKey& p_k, int p_item) { indexkey=p_k.key; item=p_item; } + OctantKey() { key=0; } + }; + + Transform last_transform; + + bool _in_tree; + float cell_size; + int octant_size; + bool center_x,center_y,center_z; + bool bake; + float cell_scale; + + + bool clip; + bool clip_above; + int clip_floor; + bool baked_lock; + Vector3::Axis clip_axis; + + + + struct Area { + + String name; + RID base_portal; + RID instance; + IndexKey from; + IndexKey to; + struct Portal { + Transform xform; + RID instance; + ~Portal(); + }; + Vector<Portal> portals; + float portal_disable_distance; + Color portal_disable_color; + bool exterior_portal; + + Area(); + ~Area(); + }; + + Ref<MeshLibrary> theme; + + Map<OctantKey,Octant*> octant_map; + Map<IndexKey,Cell> cell_map; + Map<int,Area*> area_map; + + + + void _recreate_octant_data(); + + struct BakeLight { + + VS::LightType type; + Vector3 pos; + Vector3 dir; + float param[VS::LIGHT_PARAM_MAX]; + }; + + _FORCE_INLINE_ int _find_area(const IndexKey& p_pos) const; + + _FORCE_INLINE_ Vector3 _octant_get_offset(const OctantKey &p_key) const { + + return Vector3(p_key.x,p_key.y,p_key.z)*cell_size*octant_size; + } + + void _octant_enter_world(const OctantKey &p_key); + void _octant_exit_world(const OctantKey &p_key); + void _octant_update(const OctantKey &p_key); + void _octant_transform(const OctantKey &p_key); + void _octant_clear_baked(const OctantKey &p_key); + void _octant_bake(const OctantKey &p_key,const Ref<TriangleMesh>& p_tmesh=RES(),const Vector<BakeLight> &p_lights=Vector<BakeLight>(),List<Vector3> *r_prebake=NULL); + bool awaiting_update; + + void _queue_dirty_map(); + void _update_dirty_map_callback(); + + void resource_changed(const RES& p_res); + + + void _update_areas(); + void _update_area_instances(); + + void _clear_internal(bool p_keep_areas=false); + +protected: + + bool _set(const StringName& p_name, const Variant& p_value); + bool _get(const StringName& p_name,Variant &r_ret) const; + void _get_property_list( List<PropertyInfo> *p_list) const; + + void _notification(int p_what); + static void _bind_methods(); + +public: + + enum { + INVALID_CELL_ITEM=-1 + }; + + void set_theme(const Ref<MeshLibrary>& p_theme); + Ref<MeshLibrary> get_theme() const; + + void set_cell_size(float p_size); + float get_cell_size() const; + + void set_octant_size(int p_size); + int get_octant_size() const; + + + void set_center_x(bool p_enable); + bool get_center_x() const; + void set_center_y(bool p_enable); + bool get_center_y() const; + void set_center_z(bool p_enable); + bool get_center_z() const; + + void set_cell_item(int p_x,int p_y,int p_z, int p_item,int p_orientation=0); + int get_cell_item(int p_x,int p_y,int p_z) const; + int get_cell_item_orientation(int p_x,int p_y,int p_z) const; + + void set_clip(bool p_enabled, bool p_clip_above=true, int p_floor=0, Vector3::Axis p_axis=Vector3::AXIS_X); + + Error create_area(int p_id,const AABB& p_area); + AABB area_get_bounds(int p_area) const; + void area_set_exterior_portal(int p_area,bool p_enable); + void area_set_name(int p_area,const String& p_name); + String area_get_name(int p_area) const; + bool area_is_exterior_portal(int p_area) const; + void area_set_portal_disable_distance(int p_area, float p_distance); + float area_get_portal_disable_distance(int p_area) const; + void area_set_portal_disable_color(int p_area, Color p_color); + Color area_get_portal_disable_color(int p_area) const; + void get_area_list(List<int> *p_areas) const; + void erase_area(int p_area); + int get_unused_area_id() const; + + void set_cell_scale(float p_scale); + float get_cell_scale() const; + + void set_bake(bool p_bake); + bool is_baking_enabled() const; + + void bake_geometry(); + + void clear(); + + GridMap(); + ~GridMap(); +}; + +#endif // CUBE_GRID_MAP_H diff --git a/modules/gridmap/grid_map_editor_plugin.cpp b/modules/gridmap/grid_map_editor_plugin.cpp new file mode 100644 index 0000000000..09e279305c --- /dev/null +++ b/modules/gridmap/grid_map_editor_plugin.cpp @@ -0,0 +1,1470 @@ +/*************************************************************************/ +/* grid_map_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "grid_map_editor_plugin.h" +#include "tools/editor/plugins/spatial_editor_plugin.h" +#include "scene/3d/camera.h" +#include "tools/editor/editor_settings.h" + +#include "os/keyboard.h" +#include "geometry.h" + +void GridMapEditor::_node_removed(Node *p_node) { + + if(p_node==node) { + node=NULL; + hide(); + theme_pallete->hide(); + } + +} + + +void GridMapEditor::_configure() { + + if(!node) + return; + + update_grid(); + +} + +void GridMapEditor::_menu_option(int p_option) { + + + switch(p_option) { + + case MENU_OPTION_CONFIGURE: { + + + } break; + case MENU_OPTION_LOCK_VIEW: { + + int index=options->get_popup()->get_item_index(MENU_OPTION_LOCK_VIEW); + lock_view=!options->get_popup()->is_item_checked(index); + + options->get_popup()->set_item_checked(index,lock_view); + } break; + case MENU_OPTION_CLIP_DISABLED: + case MENU_OPTION_CLIP_ABOVE: + case MENU_OPTION_CLIP_BELOW: { + + clip_mode=ClipMode(p_option-MENU_OPTION_CLIP_DISABLED); + for(int i=0;i<3;i++) { + + int index=options->get_popup()->get_item_index(MENU_OPTION_CLIP_DISABLED+i); + options->get_popup()->set_item_checked(index,i==clip_mode); + + } + + _update_clip(); + } break; + case MENU_OPTION_X_AXIS: + case MENU_OPTION_Y_AXIS: + case MENU_OPTION_Z_AXIS: { + + int new_axis = p_option-MENU_OPTION_X_AXIS; + for(int i=0;i<3;i++) { + int idx=options->get_popup()->get_item_index(MENU_OPTION_X_AXIS+i); + options->get_popup()->set_item_checked(idx,i==new_axis); + } + edit_axis=Vector3::Axis(new_axis); + update_grid(); + _update_clip(); + + } break; + case MENU_OPTION_CURSOR_ROTATE_Y: { + Matrix3 r; + if (input_action==INPUT_DUPLICATE) { + + r.set_orthogonal_index(selection.duplicate_rot); + r.rotate(Vector3(0,1,0),Math_PI/2.0); + selection.duplicate_rot=r.get_orthogonal_index(); + _update_duplicate_indicator(); + break; + } + r.set_orthogonal_index(cursor_rot); + r.rotate(Vector3(0,1,0),Math_PI/2.0); + cursor_rot=r.get_orthogonal_index(); + _update_cursor_transform(); + } break; + case MENU_OPTION_CURSOR_ROTATE_X: { + Matrix3 r; + if (input_action==INPUT_DUPLICATE) { + + r.set_orthogonal_index(selection.duplicate_rot); + r.rotate(Vector3(1,0,0),Math_PI/2.0); + selection.duplicate_rot=r.get_orthogonal_index(); + _update_duplicate_indicator(); + break; + } + + r.set_orthogonal_index(cursor_rot); + r.rotate(Vector3(1,0,0),Math_PI/2.0); + cursor_rot=r.get_orthogonal_index(); + _update_cursor_transform(); + } break; + case MENU_OPTION_CURSOR_ROTATE_Z: { + Matrix3 r; + if (input_action==INPUT_DUPLICATE) { + + r.set_orthogonal_index(selection.duplicate_rot); + r.rotate(Vector3(0,0,1),Math_PI/2.0); + selection.duplicate_rot=r.get_orthogonal_index(); + _update_duplicate_indicator(); + break; + } + + r.set_orthogonal_index(cursor_rot); + r.rotate(Vector3(0,0,1),Math_PI/2.0); + cursor_rot=r.get_orthogonal_index(); + _update_cursor_transform(); + } break; + case MENU_OPTION_CURSOR_BACK_ROTATE_Y: { + Matrix3 r; + r.set_orthogonal_index(cursor_rot); + r.rotate(Vector3(0,1,0),-Math_PI/2.0); + cursor_rot=r.get_orthogonal_index(); + _update_cursor_transform(); + } break; + case MENU_OPTION_CURSOR_BACK_ROTATE_X: { + Matrix3 r; + r.set_orthogonal_index(cursor_rot); + r.rotate(Vector3(1,0,0),-Math_PI/2.0); + cursor_rot=r.get_orthogonal_index(); + _update_cursor_transform(); + } break; + case MENU_OPTION_CURSOR_BACK_ROTATE_Z: { + Matrix3 r; + r.set_orthogonal_index(cursor_rot); + r.rotate(Vector3(0,0,1),-Math_PI/2.0); + cursor_rot=r.get_orthogonal_index(); + _update_cursor_transform(); + } break; + case MENU_OPTION_CURSOR_CLEAR_ROTATION: { + + if (input_action==INPUT_DUPLICATE) { + + + selection.duplicate_rot=0; + _update_duplicate_indicator(); + break; + } + + cursor_rot=0; + _update_cursor_transform(); + } break; + + + case MENU_OPTION_DUPLICATE_SELECTS: { + int idx = options->get_popup()->get_item_index(MENU_OPTION_DUPLICATE_SELECTS); + options->get_popup()->set_item_checked( idx, !options->get_popup()->is_item_checked( idx ) ); + } break; + case MENU_OPTION_SELECTION_MAKE_AREA: + case MENU_OPTION_SELECTION_MAKE_EXTERIOR_CONNECTOR: { + + if (!selection.active) + break; + int area = node->get_unused_area_id(); + Error err = node->create_area(area,AABB(selection.begin,selection.end-selection.begin+Vector3(1,1,1))); + if (err!=OK) { + + + } + if (p_option==MENU_OPTION_SELECTION_MAKE_EXTERIOR_CONNECTOR) { + + node->area_set_exterior_portal(area,true); + } + _update_areas_display(); + update_areas(); + + + } break; + case MENU_OPTION_REMOVE_AREA: { + if (selected_area<1) + return; + node->erase_area(selected_area); + _update_areas_display(); + update_areas(); + } break; + case MENU_OPTION_SELECTION_CLEAR: { + if (!selection.active) + return; + + _delete_selection(); + + + } break; + + + } +} + +void GridMapEditor::_update_cursor_transform() { + + cursor_transform=Transform(); + cursor_transform.origin=cursor_origin; + cursor_transform.basis.set_orthogonal_index(cursor_rot); + cursor_transform = node->get_transform() * cursor_transform; + + + if (cursor_instance.is_valid()) { + VisualServer::get_singleton()->instance_set_transform(cursor_instance,cursor_transform); + VisualServer::get_singleton()->instance_geometry_set_flag(cursor_instance,VS::INSTANCE_FLAG_VISIBLE,cursor_visible); + } + +} + +void GridMapEditor::_update_selection_transform() { + + if (!selection.active) { + + Transform xf; + xf.basis.set_zero(); + VisualServer::get_singleton()->instance_set_transform(selection_instance,xf); + return; + } + + Transform xf; + xf.scale(Vector3(1,1,1)*(Vector3(1,1,1)+(selection.end-selection.begin))*node->get_cell_size()); + xf.origin=selection.begin*node->get_cell_size(); + + VisualServer::get_singleton()->instance_set_transform(selection_instance,node->get_global_transform() * xf); + +} + +void GridMapEditor::_validate_selection() { + + if (!selection.active) + return; + selection.begin=selection.click; + selection.end=selection.current; + + if (selection.begin.x>selection.end.x) + SWAP(selection.begin.x,selection.end.x); + if (selection.begin.y>selection.end.y) + SWAP(selection.begin.y,selection.end.y); + if (selection.begin.z>selection.end.z) + SWAP(selection.begin.z,selection.end.z); + + + _update_selection_transform(); +} + +bool GridMapEditor::do_input_action(Camera* p_camera,const Point2& p_point,bool p_click) { + + if (!spatial_editor) + return false; + + + if (selected_pallete<0 && input_action!=INPUT_COPY && input_action!=INPUT_SELECT && input_action!=INPUT_DUPLICATE) + return false; + Ref<MeshLibrary> theme = node->get_theme(); + if (theme.is_null()) + return false; + if (input_action!=INPUT_COPY && input_action!=INPUT_SELECT && input_action!=INPUT_DUPLICATE && !theme->has_item(selected_pallete)) + return false; + + Camera *camera = p_camera; + Vector3 from = camera->project_ray_origin(p_point); + Vector3 normal = camera->project_ray_normal(p_point); + Transform local_xform = node->get_global_transform().affine_inverse(); + Vector<Plane> planes=camera->get_frustum(); + from=local_xform.xform(from); + normal=local_xform.basis.xform(normal).normalized(); + + + Plane p; + p.normal[edit_axis]=1.0; + p.d=edit_floor[edit_axis]*node->get_cell_size(); + + Vector3 inters; + if (!p.intersects_segment(from,from+normal*500,&inters)) + return false; + + + //make sure the intersection is inside the frustum planes, to avoid + //painting on invisible regions + for(int i=0;i<planes.size();i++) { + + Plane fp = local_xform.xform(planes[i]); + if (fp.is_point_over(inters)) + return false; + } + + + int cell[3]; + float cell_size[3]={node->get_cell_size(),node->get_cell_size(),node->get_cell_size()}; + + last_mouseover=Vector3(-1,-1,-1); + + for(int i=0;i<3;i++) { + + if (i==edit_axis) + cell[i]=edit_floor[i]; + else { + + cell[i]=inters[i]/node->get_cell_size(); + if (inters[i]<0) + cell[i]-=1; //compensate negative + grid_ofs[i]=cell[i]*cell_size[i]; + } + + /*if (cell[i]<0 || cell[i]>=grid_size[i]) { + + cursor_visible=false; + _update_cursor_transform(); + return false; + }*/ + } + + last_mouseover=Vector3(cell[0],cell[1],cell[2]); + VS::get_singleton()->instance_set_transform(grid_instance[edit_axis],Transform(Matrix3(),grid_ofs)); + + + if (cursor_instance.is_valid()) { + + cursor_origin=(Vector3(cell[0],cell[1],cell[2])+Vector3(0.5*node->get_center_x(),0.5*node->get_center_y(),0.5*node->get_center_z()))*node->get_cell_size(); + cursor_visible=true; + + _update_cursor_transform(); + + } + + if (input_action==INPUT_DUPLICATE) { + + selection.current=Vector3(cell[0],cell[1],cell[2]); + _update_duplicate_indicator(); + + } else if (input_action==INPUT_SELECT) { + + selection.current=Vector3(cell[0],cell[1],cell[2]); + if (p_click) + selection.click=selection.current; + selection.active=true; + _validate_selection(); + + return true; + } else if (input_action==INPUT_COPY) { + + int item=node->get_cell_item(cell[0],cell[1],cell[2]); + if (item>=0) { + selected_pallete=item; + update_pallete(); + } + return true; + } if (input_action==INPUT_PAINT) { + SetItem si; + si.pos=Vector3(cell[0],cell[1],cell[2]); + si.new_value=selected_pallete; + si.new_orientation=cursor_rot; + si.old_value=node->get_cell_item(cell[0],cell[1],cell[2]); + si.old_orientation=node->get_cell_item_orientation(cell[0],cell[1],cell[2]); + set_items.push_back(si); + node->set_cell_item(cell[0],cell[1],cell[2],selected_pallete,cursor_rot); + return true; + } else if (input_action==INPUT_ERASE) { + SetItem si; + si.pos=Vector3(cell[0],cell[1],cell[2]); + si.new_value=-1; + si.new_orientation=0; + si.old_value=node->get_cell_item(cell[0],cell[1],cell[2]); + si.old_orientation=node->get_cell_item_orientation(cell[0],cell[1],cell[2]); + set_items.push_back(si); + node->set_cell_item(cell[0],cell[1],cell[2],-1); + return true; + } + + + return false; + +} + +void GridMapEditor::_delete_selection() { + + if (!selection.active) + return; + + undo_redo->create_action("GridMap Delete Selection"); + for(int i=selection.begin.x;i<=selection.end.x;i++) { + + for(int j=selection.begin.y;j<=selection.end.y;j++) { + + for(int k=selection.begin.z;k<=selection.end.z;k++) { + + undo_redo->add_do_method(node,"set_cell_item",i,j,k,GridMap::INVALID_CELL_ITEM); + undo_redo->add_undo_method(node,"set_cell_item",i,j,k,node->get_cell_item(i,j,k),node->get_cell_item_orientation(i,j,k)); + } + + } + } + undo_redo->commit_action(); + + selection.active=false; + _validate_selection(); + +} + +void GridMapEditor::_update_duplicate_indicator() { + + if (!selection.active || input_action!=INPUT_DUPLICATE) { + + Transform xf; + xf.basis.set_zero(); + VisualServer::get_singleton()->instance_set_transform(duplicate_instance,xf); + return; + } + + Transform xf; + xf.scale(Vector3(1,1,1)*(Vector3(1,1,1)+(selection.end-selection.begin))*node->get_cell_size()); + xf.origin=(selection.begin+(selection.current-selection.click))*node->get_cell_size(); + Matrix3 rot; + rot.set_orthogonal_index(selection.duplicate_rot); + xf.basis = rot * xf.basis; + + VisualServer::get_singleton()->instance_set_transform(duplicate_instance,node->get_global_transform() * xf); + + +} + +struct __Item { Vector3 pos; int rot; int item ; }; +void GridMapEditor::_duplicate_paste() { + + if (!selection.active) + return; + + int idx = options->get_popup()->get_item_index(MENU_OPTION_DUPLICATE_SELECTS); + bool reselect = options->get_popup()->is_item_checked( idx ); + + + + List< __Item > items; + + Matrix3 rot; + rot.set_orthogonal_index(selection.duplicate_rot); + + for(int i=selection.begin.x;i<=selection.end.x;i++) { + + for(int j=selection.begin.y;j<=selection.end.y;j++) { + + for(int k=selection.begin.z;k<=selection.end.z;k++) { + + int itm = node->get_cell_item(i,j,k); + if (itm==GridMap::INVALID_CELL_ITEM) + continue; + int orientation = node->get_cell_item_orientation(i,j,k); + __Item item; + Vector3 rel=Vector3(i,j,k)-selection.begin; + rel = rot.xform(rel); + + Matrix3 orm; + orm.set_orthogonal_index(orientation); + orm = rot * orm; + + item.pos=selection.begin+rel; + item.item=itm; + item.rot=orm.get_orthogonal_index(); + items.push_back(item); + } + + } + } + + Vector3 ofs=selection.current-selection.click; + if (items.size()) { + undo_redo->create_action("GridMap Duplicate Selection"); + for(List< __Item >::Element *E=items.front();E;E=E->next()) { + __Item &it=E->get(); + Vector3 pos = it.pos+ofs; + + undo_redo->add_do_method(node,"set_cell_item",pos.x,pos.y,pos.z,it.item,it.rot); + undo_redo->add_undo_method(node,"set_cell_item",pos.x,pos.y,pos.z,node->get_cell_item(pos.x,pos.y,pos.z),node->get_cell_item_orientation(pos.x,pos.y,pos.z)); + + } + undo_redo->commit_action(); + } + + + if (reselect) { + + selection.begin+=ofs; + selection.end+=ofs; + selection.click=selection.begin; + selection.current=selection.end; + _validate_selection(); + } + +} + +bool GridMapEditor::forward_spatial_input_event(Camera* p_camera,const InputEvent& p_event) { + + + if (edit_mode->get_selected()==0) { // regular click + switch (p_event.type) { + case InputEvent::KEY: { + + if (p_event.key.pressed && p_event.key.scancode==KEY_D && p_event.key.mod.shift && selection.active && input_action==INPUT_NONE) { + + if (last_mouseover==Vector3(-1,-1,-1)) //nono mouseovering anythin + return false; + + input_action=INPUT_DUPLICATE; + selection.click=last_mouseover; + selection.current=last_mouseover; + selection.duplicate_rot=0; + _update_duplicate_indicator(); + + + } + + if (p_event.key.pressed && p_event.key.scancode==KEY_DELETE && selection.active) { + + _delete_selection(); + return true; + } + + } break; + case InputEvent::MOUSE_BUTTON: { + + if (p_event.mouse_button.button_index==BUTTON_WHEEL_UP && (p_event.mouse_button.mod.command || p_event.mouse_button.mod.shift)) { + if (p_event.mouse_button.pressed) + floor->set_val( floor->get_val() +1); + + return true; //eaten + } else if (p_event.mouse_button.button_index==BUTTON_WHEEL_DOWN && (p_event.mouse_button.mod.command || p_event.mouse_button.mod.shift)) { + if (p_event.mouse_button.pressed) + floor->set_val( floor->get_val() -1); + return true; + } + + if (p_event.mouse_button.pressed) { + + if (p_event.mouse_button.button_index==BUTTON_LEFT) { + + if (input_action==INPUT_DUPLICATE) { + + //paste + _duplicate_paste(); + input_action=INPUT_NONE; + _update_duplicate_indicator(); + } else if (p_event.mouse_button.mod.shift) { + input_action=INPUT_SELECT; + } else if (p_event.mouse_button.mod.command) + input_action=INPUT_COPY; + else { + input_action=INPUT_PAINT; + set_items.clear(); + } + } else if (p_event.mouse_button.button_index==BUTTON_RIGHT) + if (input_action==INPUT_DUPLICATE) { + + input_action=INPUT_NONE; + _update_duplicate_indicator(); + } else { + input_action=INPUT_ERASE; + set_items.clear(); + } + else + return false; + + return do_input_action(p_camera,Point2(p_event.mouse_button.x,p_event.mouse_button.y),true); + } else { + + + if ( + (p_event.mouse_button.button_index==BUTTON_RIGHT && input_action==INPUT_ERASE) || + (p_event.mouse_button.button_index==BUTTON_LEFT && input_action==INPUT_PAINT) ) { + + if (set_items.size()) { + undo_redo->create_action("GridMap Paint"); + for(List<SetItem>::Element *E=set_items.front();E;E=E->next()) { + + const SetItem &si=E->get(); + undo_redo->add_do_method(node,"set_cell_item",si.pos.x,si.pos.y,si.pos.z,si.new_value,si.new_orientation); + } + for(List<SetItem>::Element *E=set_items.back();E;E=E->prev()) { + + const SetItem &si=E->get(); + undo_redo->add_undo_method(node,"set_cell_item",si.pos.x,si.pos.y,si.pos.z,si.old_value,si.old_orientation); + } + + + undo_redo->commit_action(); + } + set_items.clear(); + input_action=INPUT_NONE; + return true; + + } + + + + if (p_event.mouse_button.button_index==BUTTON_LEFT && input_action!=INPUT_NONE) { + + set_items.clear(); + input_action=INPUT_NONE; + return true; + } + if (p_event.mouse_button.button_index==BUTTON_RIGHT && (input_action==INPUT_ERASE || input_action==INPUT_DUPLICATE)) { + input_action=INPUT_NONE; + return true; + } + } + } break; + case InputEvent::MOUSE_MOTION: { + + return do_input_action(p_camera,Point2(p_event.mouse_motion.x,p_event.mouse_motion.y),false); + } break; + } + + } else if (edit_mode->get_selected()==1) { + //area mode, select an area + + switch (p_event.type) { + case InputEvent::MOUSE_BUTTON: { + + if (p_event.mouse_button.button_index==BUTTON_LEFT && p_event.mouse_button.pressed) { + + Point2 point = Point2(p_event.mouse_motion.x,p_event.mouse_motion.y); + + Camera *camera = p_camera; + Vector3 from = camera->project_ray_origin(point); + Vector3 normal = camera->project_ray_normal(point); + Transform local_xform = node->get_global_transform().affine_inverse(); + from=local_xform.xform(from); + normal=local_xform.basis.xform(normal).normalized(); + + List<int> areas; + node->get_area_list(&areas); + + float min_d=1e10; + int min_area=-1; + + + for(List<int>::Element *E=areas.front();E;E=E->next()) { + + int area = E->get(); + AABB aabb = node->area_get_bounds(area); + aabb.pos*=node->get_cell_size(); + aabb.size*=node->get_cell_size(); + + + Vector3 rclip,rnormal; + if (!aabb.intersects_segment(from,from+normal*10000,&rclip,&rnormal)) + continue; + + float d = normal.dot(rclip); + if (d<min_d) { + min_d=d; + min_area=area; + } + } + + selected_area=min_area; + update_areas(); + + } + } break; + } + + } + + + return false; +} + +struct _CGMEItemSort { + + String name; + int id; + _FORCE_INLINE_ bool operator<(const _CGMEItemSort& r_it) const { return name < r_it.name; } + +}; + +void GridMapEditor::update_pallete() { + + theme_pallete->clear(); + + Ref<MeshLibrary> theme = node->get_theme(); + + if (theme.is_null()) { + last_theme=NULL; + return; + } + + Vector<int> ids; + ids = theme->get_item_list(); + + TreeItem *root = theme_pallete->create_item(NULL); + theme_pallete->set_hide_root(true); + TreeItem *selected=NULL; + + List<_CGMEItemSort> il; + for(int i=0;i<ids.size();i++) { + + _CGMEItemSort is; + is.id=ids[i]; + is.name=theme->get_item_name(ids[i]); + il.push_back(is); + } + il.sort(); + + int col=0; + TreeItem *ti=NULL; + int selected_col=0; + + for(List<_CGMEItemSort>::Element *E=il.front();E;E=E->next()) { + + int id = E->get().id; + + if (col==0) { + ti = theme_pallete->create_item(root); + } + + String name=theme->get_item_name(id); + Ref<Texture> preview = theme->get_item_preview(id); + + if (!preview.is_null()) { + + ti->set_cell_mode(col,TreeItem::CELL_MODE_ICON); + ti->set_icon(col,preview); + ti->set_tooltip(col,name); + } else { + + ti->set_text(col,name); + } + ti->set_metadata(col,id); + + if (selected_pallete==id) { + selected=ti; + selected_col=col; + } + + col++; + if (col==theme_pallete->get_columns()) + col=0; + + } + + if (selected) + selected->select(selected_col); + + last_theme=theme.operator->(); +} + + +void GridMapEditor::_area_renamed() { + + TreeItem * it = area_list->get_selected(); + int area = it->get_metadata(0); + if (area<1) + return; + node->area_set_name(area,it->get_text(0)); +} + + +void GridMapEditor::_area_selected() { + + TreeItem * it = area_list->get_selected(); + int area = it->get_metadata(0); + if (area<1) + return; + selected_area=area; +} + +void GridMapEditor::update_areas() { + + area_list->clear(); + + List<int> areas; + node->get_area_list(&areas); + + TreeItem *root = area_list->create_item(NULL); + area_list->set_hide_root(true); + TreeItem *selected=NULL; + + + for (List<int>::Element *E=areas.front();E;E=E->next()) { + + int area = E->get(); + TreeItem *ti = area_list->create_item(root); + String name=node->area_get_name(area); + + ti->set_metadata(0,area); + ti->set_text(0,name); + ti->set_editable(0,true); + if (area==selected_area) + selected=ti; + } + + + if (selected) + selected->select(0); + +} + +void GridMapEditor::edit(GridMap *p_gridmap) { + + node=p_gridmap; + VS *vs = VS::get_singleton(); + + last_mouseover=Vector3(-1,-1,-1); + spatial_editor = editor->get_editor_plugin_screen()->cast_to<SpatialEditorPlugin>(); + + if (!node) { + set_process(false); + for(int i=0;i<3;i++) { + VisualServer::get_singleton()->instance_geometry_set_flag(grid_instance[i],VS::INSTANCE_FLAG_VISIBLE,false); + + } + _clear_areas(); + + return; + } + + + update_pallete(); + update_areas(); + + set_process(true); + + Vector3 edited_floor = p_gridmap->get_meta("_editor_floor_"); + clip_mode=p_gridmap->has_meta("_editor_clip_")?ClipMode(p_gridmap->get_meta("_editor_clip_").operator int()):CLIP_DISABLED; + + + + for(int i=0;i<3;i++) { + if (vs->mesh_get_surface_count(grid[i])>0) + vs->mesh_remove_surface(grid[i],0); + edit_floor[i]=edited_floor[i]; + + } + + { + + //update grids + RID indicator_mat = VisualServer::get_singleton()->fixed_material_create(); + VisualServer::get_singleton()->material_set_flag( indicator_mat, VisualServer::MATERIAL_FLAG_UNSHADED, true ); + VisualServer::get_singleton()->material_set_flag( indicator_mat, VisualServer::MATERIAL_FLAG_ONTOP, false ); + + VisualServer::get_singleton()->fixed_material_set_param(indicator_mat,VisualServer::FIXED_MATERIAL_PARAM_DIFFUSE,Color(0.8,0.5,0.1)); + VisualServer::get_singleton()->fixed_material_set_flag( indicator_mat, VisualServer::FIXED_MATERIAL_FLAG_USE_ALPHA, true ); + VisualServer::get_singleton()->fixed_material_set_flag( indicator_mat, VisualServer::FIXED_MATERIAL_FLAG_USE_COLOR_ARRAY, true ); + + + Vector<Vector3> grid_points[3]; + Vector<Color> grid_colors[3]; + + float cell_size[3]={p_gridmap->get_cell_size(),p_gridmap->get_cell_size(),p_gridmap->get_cell_size()}; + + for(int i=0;i<3;i++) { + + Vector3 axis; + axis[i]=1; + Vector3 axis_n1; + axis_n1[(i+1)%3]=cell_size[(i+1)%3];; + Vector3 axis_n2; + axis_n2[(i+2)%3]=cell_size[(i+2)%3]; + + for(int j=-GRID_CURSOR_SIZE;j<=GRID_CURSOR_SIZE;j++) { + + for(int k=-GRID_CURSOR_SIZE;k<=GRID_CURSOR_SIZE;k++) { + + Vector3 p = axis_n1*j + axis_n2 *k; + float trans = Math::pow(MAX(0,1.0-(Vector2(j,k).length()/GRID_CURSOR_SIZE)),2); + + Vector3 pj = axis_n1*(j+1) + axis_n2 *k; + float transj = Math::pow(MAX(0,1.0-(Vector2(j+1,k).length()/GRID_CURSOR_SIZE)),2); + + Vector3 pk = axis_n1*j + axis_n2 *(k+1); + float transk = Math::pow(MAX(0,1.0-(Vector2(j,k+1).length()/GRID_CURSOR_SIZE)),2); + + grid_points[i].push_back(p); + grid_points[i].push_back(pk); + grid_colors[i].push_back(Color(1,1,1,trans)); + grid_colors[i].push_back(Color(1,1,1,transk)); + + grid_points[i].push_back(p); + grid_points[i].push_back(pj); + grid_colors[i].push_back(Color(1,1,1,trans)); + grid_colors[i].push_back(Color(1,1,1,transj)); + } + + } + + Array d; + d.resize(VS::ARRAY_MAX); + d[VS::ARRAY_VERTEX]=grid_points[i]; + d[VS::ARRAY_COLOR]=grid_colors[i]; + VisualServer::get_singleton()->mesh_add_surface(grid[i],VisualServer::PRIMITIVE_LINES,d); + VisualServer::get_singleton()->mesh_surface_set_material(grid[i],0,indicator_mat,true); + + + } + + } + + update_grid(); + _update_clip(); + _update_areas_display(); + + +} + +void GridMapEditor::_update_clip() { + + + node->set_meta("_editor_clip_",clip_mode); + if (clip_mode==CLIP_DISABLED) + node->set_clip(false); + else + node->set_clip(true,clip_mode==CLIP_ABOVE,edit_floor[edit_axis],edit_axis); +} + + +void GridMapEditor::update_grid() { + + grid_xform.origin.x-=1; //force update in hackish way.. what do i care + + VS *vs = VS::get_singleton(); + + grid_ofs[edit_axis]=edit_floor[edit_axis]*node->get_cell_size(); + + edit_grid_xform.origin=grid_ofs; + edit_grid_xform.basis=Matrix3(); + + + for(int i=0;i<3;i++) { + VisualServer::get_singleton()->instance_geometry_set_flag(grid_instance[i],VS::INSTANCE_FLAG_VISIBLE,i==edit_axis); + + } + + updating=true; + floor->set_val(edit_floor[edit_axis]); + updating=false; + +} + + + +void GridMapEditor::_notification(int p_what) { + + if (p_what==NOTIFICATION_ENTER_SCENE) { + + theme_pallete->connect("cell_selected", this,"_item_selected_cbk"); + edit_mode->connect("item_selected", this,"_edit_mode_changed"); + area_list->connect("item_edited", this,"_area_renamed"); + area_list->connect("item_selected", this,"_area_selected"); + for(int i=0;i<3;i++) { + + grid[i]=VS::get_singleton()->mesh_create(); + grid_instance[i]=VS::get_singleton()->instance_create2(grid[i],get_scene()->get_root()->get_world()->get_scenario()); + } + + selection_instance = VisualServer::get_singleton()->instance_create2(selection_mesh,get_scene()->get_root()->get_world()->get_scenario()); + duplicate_instance = VisualServer::get_singleton()->instance_create2(duplicate_mesh,get_scene()->get_root()->get_world()->get_scenario()); + + _update_selection_transform(); + _update_duplicate_indicator(); + + } else if (p_what==NOTIFICATION_EXIT_SCENE) { + + for(int i=0;i<3;i++) { + + VS::get_singleton()->free(grid_instance[i]); + VS::get_singleton()->free(grid[i]); + grid_instance[i]=RID(); + grid[i]=RID(); + } + + VisualServer::get_singleton()->free(selection_instance); + VisualServer::get_singleton()->free(duplicate_instance); + selection_instance=RID(); + duplicate_instance=RID(); + + } else if (p_what==NOTIFICATION_PROCESS) { + + Transform xf = node->get_global_transform(); + + if (xf!=grid_xform) { + for(int i=0;i<3;i++) { + + + VS::get_singleton()->instance_set_transform(grid_instance[i],xf * edit_grid_xform); + } + grid_xform=xf; + } + Ref<MeshLibrary> cgmt = node->get_theme(); + if (cgmt.operator->()!=last_theme) + update_pallete(); + + if (lock_view) { + + EditorNode*editor = get_scene()->get_root()->get_child(0)->cast_to<EditorNode>(); + + Plane p; + p.normal[edit_axis]=1.0; + p.d=edit_floor[edit_axis]*node->get_cell_size(); + p = node->get_transform().xform(p); // plane to snap + + SpatialEditorPlugin *sep = editor->get_editor_plugin_screen()->cast_to<SpatialEditorPlugin>(); + if (sep) + sep->snap_cursor_to_plane(p); + //editor->get_editor_plugin_screen()->call("snap_cursor_to_plane",p); + + } + } + +} + +void GridMapEditor::_update_cursor_instance() { + + + if (cursor_instance.is_valid()) + VisualServer::get_singleton()->free(cursor_instance); + cursor_instance=RID(); + + if (selected_pallete>=0) { + + if (node && !node->get_theme().is_null()) { + Ref<Mesh> mesh = node->get_theme()->get_item_mesh(selected_pallete); + if (!mesh.is_null() && mesh->get_rid().is_valid()) { + + cursor_instance=VisualServer::get_singleton()->instance_create2(mesh->get_rid(),get_scene()->get_root()->get_world()->get_scenario()); + VisualServer::get_singleton()->instance_set_transform(cursor_instance,cursor_transform); + } + } + } + +} + +void GridMapEditor::_item_selected_cbk() { + + TreeItem *it = theme_pallete->get_selected(); + if (it) { + + selected_pallete=it->get_metadata(theme_pallete->get_selected_column()); + + } else { + + selected_pallete=-1; + + } + + _update_cursor_instance(); + +} + +void GridMapEditor::_clear_areas() { + + for(int i=0;i<areas.size();i++) { + + VisualServer::get_singleton()->free(areas[i].instance); + VisualServer::get_singleton()->free(areas[i].mesh); + } + + areas.clear(); +} + +void GridMapEditor::_update_areas_display() { + + + _clear_areas(); + List<int> areas; + node->get_area_list(&areas); + + Transform global_xf = node->get_global_transform(); + + for(List<int>::Element *E=areas.front();E;E=E->next()) { + + int area = E->get(); + Color color; + if (node->area_is_exterior_portal(area)) + color=Color(1,1,1,0.2); + else + color.set_hsv(Math::fmod(area*0.37,1),Math::fmod(area*0.75,1),1.0,0.2); + RID material = VisualServer::get_singleton()->fixed_material_create(); + VisualServer::get_singleton()->fixed_material_set_param( material, VS::FIXED_MATERIAL_PARAM_DIFFUSE,color ); + VisualServer::get_singleton()->fixed_material_set_param( material, VS::FIXED_MATERIAL_PARAM_EMISSION,0.5 ); + VisualServer::get_singleton()->fixed_material_set_flag( material, VisualServer::FIXED_MATERIAL_FLAG_USE_ALPHA, true ); + + + RID mesh = VisualServer::get_singleton()->mesh_create(); + + DVector<Plane> planes; + for(int i=0;i<3;i++) { + + Vector3 axis; + axis[i]=1.0; + planes.push_back(Plane(axis,1)); + planes.push_back(Plane(-axis,0)); + } + + VisualServer::get_singleton()->mesh_add_surface_from_planes(mesh,planes); + VisualServer::get_singleton()->mesh_surface_set_material(mesh,0,material,true); + + AreaDisplay ad; + ad.mesh=mesh; + ad.instance = VisualServer::get_singleton()->instance_create2(mesh,node->get_world()->get_scenario()); + Transform xform; + AABB aabb = node->area_get_bounds(area); + xform.origin=aabb.pos * node->get_cell_size(); + xform.basis.scale(aabb.size * node->get_cell_size()); + VisualServer::get_singleton()->instance_set_transform(ad.instance,global_xf * xform); + this->areas.push_back(ad); + + } + +} + +void GridMapEditor::_edit_mode_changed(int p_what) { + + if (p_what==0) { + + theme_pallete->show(); + area_list->hide(); + } else { + + theme_pallete->hide(); + area_list->show(); + + } +} + +void GridMapEditor::_floor_changed(float p_value) { + + + if (updating) + return; + + edit_floor[edit_axis]=p_value; + node->set_meta("_editor_floor_",Vector3(edit_floor[0],edit_floor[1],edit_floor[2])); + update_grid(); + _update_clip(); + +} + +void GridMapEditor::_bind_methods() { + + ObjectTypeDB::bind_method("_menu_option",&GridMapEditor::_menu_option); + ObjectTypeDB::bind_method("_configure",&GridMapEditor::_configure); + ObjectTypeDB::bind_method("_item_selected_cbk",&GridMapEditor::_item_selected_cbk); + ObjectTypeDB::bind_method("_edit_mode_changed",&GridMapEditor::_edit_mode_changed); + ObjectTypeDB::bind_method("_area_renamed",&GridMapEditor::_area_renamed); + ObjectTypeDB::bind_method("_area_selected",&GridMapEditor::_area_selected); + ObjectTypeDB::bind_method("_floor_changed",&GridMapEditor::_floor_changed); + + +} + + + +GridMapEditor::GridMapEditor(EditorNode *p_editor) { + + + input_action=INPUT_NONE; + editor=p_editor; + undo_redo=p_editor->get_undo_redo(); + + int mw = EDITOR_DEF("grid_map/palette_min_width",230); + EmptyControl *ec = memnew( EmptyControl); + ec->set_minsize(Size2(mw,0)); + add_child(ec); + + + spatial_editor_hb = memnew( HBoxContainer ); + SpatialEditor::get_singleton()->add_control_to_menu_panel(spatial_editor_hb); + options = memnew( MenuButton ); + spatial_editor_hb->add_child(options); + spatial_editor_hb->hide(); + + options->set_text("Grid"); + options->get_popup()->add_check_item("Snap View",MENU_OPTION_LOCK_VIEW); + options->get_popup()->add_separator(); + options->get_popup()->add_item("Prev Level ("+keycode_get_string(KEY_MASK_CMD)+"Down Wheel)",MENU_OPTION_PREV_LEVEL); + options->get_popup()->add_item("Next Level ("+keycode_get_string(KEY_MASK_CMD)+"Up Wheel)",MENU_OPTION_NEXT_LEVEL); + options->get_popup()->add_separator(); + options->get_popup()->add_check_item("Clip Disabled",MENU_OPTION_CLIP_DISABLED); + options->get_popup()->set_item_checked( options->get_popup()->get_item_index(MENU_OPTION_CLIP_DISABLED), true ); + options->get_popup()->add_check_item("Clip Above",MENU_OPTION_CLIP_ABOVE); + options->get_popup()->add_check_item("Clip Below",MENU_OPTION_CLIP_BELOW); + options->get_popup()->add_separator(); + options->get_popup()->add_check_item("Edit X Axis",MENU_OPTION_X_AXIS,KEY_Z); + options->get_popup()->add_check_item("Edit Y Axis",MENU_OPTION_Y_AXIS,KEY_X); + options->get_popup()->add_check_item("Edit Z Axis",MENU_OPTION_Z_AXIS,KEY_C); + options->get_popup()->set_item_checked( options->get_popup()->get_item_index(MENU_OPTION_Y_AXIS), true ); + options->get_popup()->add_separator(); + options->get_popup()->add_item("Cursor Rotate X",MENU_OPTION_CURSOR_ROTATE_X,KEY_A); + options->get_popup()->add_item("Cursor Rotate Y",MENU_OPTION_CURSOR_ROTATE_Y,KEY_S); + options->get_popup()->add_item("Cursor Rotate Z",MENU_OPTION_CURSOR_ROTATE_Z,KEY_D); + options->get_popup()->add_item("Cursor Back Rotate X",MENU_OPTION_CURSOR_ROTATE_X,KEY_ALT+KEY_A); + options->get_popup()->add_item("Cursor Back Rotate Y",MENU_OPTION_CURSOR_ROTATE_Y,KEY_ALT+KEY_S); + options->get_popup()->add_item("Cursor Back Rotate Z",MENU_OPTION_CURSOR_ROTATE_Z,KEY_ALT+KEY_D); + options->get_popup()->add_item("Cursor Clear Rotation",MENU_OPTION_CURSOR_CLEAR_ROTATION,KEY_W); + options->get_popup()->add_separator(); + options->get_popup()->add_check_item("Duplicate Selects",MENU_OPTION_DUPLICATE_SELECTS); + options->get_popup()->add_separator(); + options->get_popup()->add_item("Create Area",MENU_OPTION_SELECTION_MAKE_AREA,KEY_CONTROL+KEY_C); + options->get_popup()->add_item("Create Exterior Connector",MENU_OPTION_SELECTION_MAKE_EXTERIOR_CONNECTOR); + options->get_popup()->add_item("Erase Area",MENU_OPTION_REMOVE_AREA); + options->get_popup()->add_separator(); + options->get_popup()->add_item("Selection -> Clear",MENU_OPTION_SELECTION_CLEAR); + //options->get_popup()->add_separator(); + //options->get_popup()->add_item("Configure",MENU_OPTION_CONFIGURE); + + clip_mode=CLIP_DISABLED; + options->get_popup()->connect("item_pressed", this,"_menu_option"); + + + edit_mode = memnew(OptionButton); + edit_mode->set_area_as_parent_rect(); + edit_mode->set_anchor_and_margin(MARGIN_BOTTOM,ANCHOR_BEGIN,24);; + edit_mode->set_anchor_and_margin(MARGIN_RIGHT,ANCHOR_END,14);; + edit_mode->add_item("Tiles"); + edit_mode->add_item("Areas"); + add_child(edit_mode); + + selected_area=-1; + + + theme_pallete = memnew( Tree ); + theme_pallete->set_columns(3); + add_child(theme_pallete); + theme_pallete->set_v_size_flags(SIZE_EXPAND_FILL); + + area_list = memnew( Tree ); + add_child(area_list); + area_list->set_v_size_flags(SIZE_EXPAND_FILL); + area_list->hide(); + + spatial_editor_hb->add_child(memnew(VSeparator)); + Label *fl = memnew(Label); + fl->set_text(" Floor: "); + spatial_editor_hb->add_child(fl); + + floor = memnew( SpinBox ); + floor->set_min(-32767); + floor->set_max(32767); + floor->set_step(1); + floor->get_line_edit()->add_constant_override("minimum_spaces",16); + + spatial_editor_hb->add_child(floor); + floor->connect("value_changed",this,"_floor_changed"); + + + edit_axis=Vector3::AXIS_Y; + edit_floor[0]=-1; + edit_floor[1]=-1; + edit_floor[2]=-1; + + cursor_visible=false; + selected_pallete=-1; + lock_view=false; + cursor_rot=0; + last_mouseover=Vector3(-1,-1,-1); + + selection_mesh = VisualServer::get_singleton()->mesh_create(); + duplicate_mesh = VisualServer::get_singleton()->mesh_create(); + + { + //selection mesh create + + + DVector<Vector3> lines; + DVector<Vector3> triangles; + + for (int i=0;i<6;i++) { + + + Vector3 face_points[4]; + + for (int j=0;j<4;j++) { + + float v[3]; + v[0]=1.0; + v[1]=1-2*((j>>1)&1); + v[2]=v[1]*(1-2*(j&1)); + + for (int k=0;k<3;k++) { + + if (i<3) + face_points[j][(i+k)%3]=v[k]*(i>=3?-1:1); + else + face_points[3-j][(i+k)%3]=v[k]*(i>=3?-1:1); + } + } + + triangles.push_back(face_points[0]*0.5+Vector3(0.5,0.5,0.5)); + triangles.push_back(face_points[1]*0.5+Vector3(0.5,0.5,0.5)); + triangles.push_back(face_points[2]*0.5+Vector3(0.5,0.5,0.5)); + + triangles.push_back(face_points[2]*0.5+Vector3(0.5,0.5,0.5)); + triangles.push_back(face_points[3]*0.5+Vector3(0.5,0.5,0.5)); + triangles.push_back(face_points[0]*0.5+Vector3(0.5,0.5,0.5)); + } + + for(int i=0;i<12;i++) { + + AABB base(Vector3(0,0,0),Vector3(1,1,1)); + Vector3 a,b; + base.get_edge(i,a,b); + lines.push_back(a); + lines.push_back(b); + } + + Array d; + d.resize(VS::ARRAY_MAX); + + RID inner_mat = VisualServer::get_singleton()->fixed_material_create(); + VisualServer::get_singleton()->fixed_material_set_param(inner_mat,VS::FIXED_MATERIAL_PARAM_DIFFUSE,Color(0.7,0.7,1.0,0.3)); + VisualServer::get_singleton()->material_set_flag(inner_mat,VS::MATERIAL_FLAG_ONTOP,true); + VisualServer::get_singleton()->material_set_flag(inner_mat,VS::MATERIAL_FLAG_UNSHADED,true); + VisualServer::get_singleton()->fixed_material_set_flag( inner_mat, VisualServer::FIXED_MATERIAL_FLAG_USE_ALPHA, true ); + + + d[VS::ARRAY_VERTEX]=triangles; + VisualServer::get_singleton()->mesh_add_surface(selection_mesh,VS::PRIMITIVE_TRIANGLES,d); + VisualServer::get_singleton()->mesh_surface_set_material(selection_mesh,0,inner_mat,true); + + RID outer_mat = VisualServer::get_singleton()->fixed_material_create(); + VisualServer::get_singleton()->fixed_material_set_param(outer_mat,VS::FIXED_MATERIAL_PARAM_DIFFUSE,Color(0.7,0.7,1.0,0.8)); + VisualServer::get_singleton()->material_set_line_width(outer_mat,3.0); + VisualServer::get_singleton()->material_set_flag(outer_mat,VS::MATERIAL_FLAG_ONTOP,true); + VisualServer::get_singleton()->material_set_flag(outer_mat,VS::MATERIAL_FLAG_UNSHADED,true); + VisualServer::get_singleton()->fixed_material_set_flag( outer_mat, VisualServer::FIXED_MATERIAL_FLAG_USE_ALPHA, true ); + + + d[VS::ARRAY_VERTEX]=lines; + VisualServer::get_singleton()->mesh_add_surface(selection_mesh,VS::PRIMITIVE_LINES,d); + VisualServer::get_singleton()->mesh_surface_set_material(selection_mesh,1,outer_mat,true); + + + RID inner_mat_dup = VisualServer::get_singleton()->fixed_material_create(); + VisualServer::get_singleton()->fixed_material_set_param(inner_mat_dup,VS::FIXED_MATERIAL_PARAM_DIFFUSE,Color(1.0,0.7,0.7,0.3)); + VisualServer::get_singleton()->material_set_flag(inner_mat_dup,VS::MATERIAL_FLAG_ONTOP,true); + VisualServer::get_singleton()->material_set_flag(inner_mat_dup,VS::MATERIAL_FLAG_UNSHADED,true); + VisualServer::get_singleton()->fixed_material_set_flag( inner_mat_dup, VisualServer::FIXED_MATERIAL_FLAG_USE_ALPHA, true ); + + + d[VS::ARRAY_VERTEX]=triangles; + VisualServer::get_singleton()->mesh_add_surface(duplicate_mesh,VS::PRIMITIVE_TRIANGLES,d); + VisualServer::get_singleton()->mesh_surface_set_material(duplicate_mesh,0,inner_mat_dup,true); + + RID outer_mat_dup = VisualServer::get_singleton()->fixed_material_create(); + VisualServer::get_singleton()->fixed_material_set_param(outer_mat_dup,VS::FIXED_MATERIAL_PARAM_DIFFUSE,Color(1.0,0.7,0.7,0.8)); + VisualServer::get_singleton()->material_set_line_width(outer_mat_dup,3.0); + VisualServer::get_singleton()->material_set_flag(outer_mat_dup,VS::MATERIAL_FLAG_ONTOP,true); + VisualServer::get_singleton()->material_set_flag(outer_mat_dup,VS::MATERIAL_FLAG_UNSHADED,true); + VisualServer::get_singleton()->fixed_material_set_flag( outer_mat_dup, VisualServer::FIXED_MATERIAL_FLAG_USE_ALPHA, true ); + + + d[VS::ARRAY_VERTEX]=lines; + VisualServer::get_singleton()->mesh_add_surface(duplicate_mesh,VS::PRIMITIVE_LINES,d); + VisualServer::get_singleton()->mesh_surface_set_material(duplicate_mesh,1,outer_mat_dup,true); + + } + + selection.active=false; + updating=false; + +} + + + + +GridMapEditor::~GridMapEditor() { + + for(int i=0;i<3;i++) { + + if (grid[i].is_valid()) + VisualServer::get_singleton()->free(grid[i]); + if (grid_instance[i].is_valid()) + VisualServer::get_singleton()->free(grid_instance[i]); + if (cursor_instance) + VisualServer::get_singleton()->free(cursor_instance); + } + + VisualServer::get_singleton()->free(selection_mesh); + if (selection_instance.is_valid()) + VisualServer::get_singleton()->free(selection_instance); + + + VisualServer::get_singleton()->free(duplicate_mesh); + if (duplicate_instance.is_valid()) + VisualServer::get_singleton()->free(duplicate_instance); + + _clear_areas(); +} + +void GridMapEditorPlugin::edit(Object *p_object) { + + + gridmap_editor->edit(p_object?p_object->cast_to<GridMap>():NULL); +} + +bool GridMapEditorPlugin::handles(Object *p_object) const { + + return p_object->is_type("GridMap"); +} + +void GridMapEditorPlugin::make_visible(bool p_visible) { + + if (p_visible) { + gridmap_editor->show(); + gridmap_editor->spatial_editor_hb->show(); + gridmap_editor->set_process(true); + } else { + + gridmap_editor->spatial_editor_hb->hide(); + gridmap_editor->hide(); + gridmap_editor->edit(NULL); + gridmap_editor->set_process(false); + } + +} + + +GridMapEditorPlugin::GridMapEditorPlugin(EditorNode *p_node) { + + editor=p_node; + gridmap_editor = memnew( GridMapEditor(editor) ); + + SpatialEditor::get_singleton()->get_palette_split()->add_child(gridmap_editor); + SpatialEditor::get_singleton()->get_palette_split()->move_child(gridmap_editor,0); + + gridmap_editor->hide(); + + + +} + + +GridMapEditorPlugin::~GridMapEditorPlugin() +{ +} + diff --git a/modules/gridmap/grid_map_editor_plugin.h b/modules/gridmap/grid_map_editor_plugin.h new file mode 100644 index 0000000000..1240d78426 --- /dev/null +++ b/modules/gridmap/grid_map_editor_plugin.h @@ -0,0 +1,243 @@ +/*************************************************************************/ +/* grid_map_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef GRID_MAP_EDITOR_PLUGIN_H +#define GRID_MAP_EDITOR_PLUGIN_H + +#include "tools/editor/editor_plugin.h" +#include "tools/editor/editor_node.h" +#include "grid_map.h" +#include "tools/editor/pane_drag.h" +/** + @author Juan Linietsky <reduzio@gmail.com> +*/ + +class SpatialEditorPlugin; + +class GridMapEditor : public VBoxContainer { + + OBJ_TYPE(GridMapEditor, VBoxContainer ); + + + enum { + + GRID_CURSOR_SIZE=50 + }; + + enum InputAction { + + INPUT_NONE, + INPUT_PAINT, + INPUT_ERASE, + INPUT_COPY, + INPUT_SELECT, + INPUT_DUPLICATE, + }; + + enum ClipMode { + + CLIP_DISABLED, + CLIP_ABOVE, + CLIP_BELOW + }; + + + UndoRedo *undo_redo; + InputAction input_action; + Panel *panel; + MenuButton * options; + SpinBox *floor; + OptionButton *edit_mode; + HBoxContainer *spatial_editor_hb; + + struct SetItem { + + Vector3 pos; + int new_value; + int new_orientation; + int old_value; + int old_orientation; + }; + + List<SetItem> set_items; + + GridMap *node; + MeshLibrary* last_theme; + ClipMode clip_mode; + + bool lock_view; + Transform grid_xform; + Transform edit_grid_xform; + Vector3::Axis edit_axis; + int edit_floor[3]; + Vector3 grid_ofs; + + RID grid[3]; + RID grid_instance[3]; + RID cursor_instance; + RID selection_mesh; + RID selection_instance; + RID duplicate_mesh; + RID duplicate_instance; + + bool updating; + + + struct Selection { + + + Vector3 click; + Vector3 current; + Vector3 begin; + Vector3 end; + int duplicate_rot; + bool active; + } selection; + + bool cursor_visible; + Transform cursor_transform; + + Vector3 cursor_origin; + Vector3 last_mouseover; + + int selected_pallete; + int selected_area; + int cursor_rot; + + + enum Menu { + + MENU_OPTION_CONFIGURE, + MENU_OPTION_NEXT_LEVEL, + MENU_OPTION_PREV_LEVEL, + MENU_OPTION_LOCK_VIEW, + MENU_OPTION_CLIP_DISABLED, + MENU_OPTION_CLIP_ABOVE, + MENU_OPTION_CLIP_BELOW, + MENU_OPTION_X_AXIS, + MENU_OPTION_Y_AXIS, + MENU_OPTION_Z_AXIS, + MENU_OPTION_CURSOR_ROTATE_Y, + MENU_OPTION_CURSOR_ROTATE_X, + MENU_OPTION_CURSOR_ROTATE_Z, + MENU_OPTION_CURSOR_BACK_ROTATE_Y, + MENU_OPTION_CURSOR_BACK_ROTATE_X, + MENU_OPTION_CURSOR_BACK_ROTATE_Z, + MENU_OPTION_CURSOR_CLEAR_ROTATION, + MENU_OPTION_DUPLICATE_SELECTS, + MENU_OPTION_SELECTION_MAKE_AREA, + MENU_OPTION_SELECTION_MAKE_EXTERIOR_CONNECTOR, + MENU_OPTION_SELECTION_CLEAR, + MENU_OPTION_REMOVE_AREA + + + }; + + SpatialEditorPlugin *spatial_editor; + + + struct AreaDisplay { + + RID mesh; + RID instance; + }; + + Vector<AreaDisplay> areas; + + void _update_areas_display(); + void _clear_areas(); + + void update_grid(); + void _configure(); + void _menu_option(int); + void update_pallete(); + Tree *theme_pallete; + Tree *area_list; + void _item_selected_cbk(); + void _update_cursor_transform(); + void _update_cursor_instance(); + void _update_clip(); + + void _update_duplicate_indicator(); + void _duplicate_paste(); + void _update_selection_transform(); + void _validate_selection(); + + void _edit_mode_changed(int p_what); + void _area_renamed(); + void _area_selected(); + + void _floor_changed(float p_value); + + void _delete_selection(); + void update_areas(); + + EditorNode *editor; + bool do_input_action(Camera* p_camera,const Point2& p_point,bool p_click); + +friend class GridMapEditorPlugin; + Panel *theme_panel; + +protected: + void _notification(int p_what); + void _node_removed(Node *p_node); + static void _bind_methods(); +public: + + bool forward_spatial_input_event(Camera* p_camera,const InputEvent& p_event); + + + + void edit(GridMap *p_gridmap); + GridMapEditor() {} + GridMapEditor(EditorNode *p_editor); + ~GridMapEditor(); +}; + +class GridMapEditorPlugin : public EditorPlugin { + + OBJ_TYPE( GridMapEditorPlugin, EditorPlugin ); + + GridMapEditor *gridmap_editor; + EditorNode *editor; + +public: + + virtual bool forward_spatial_input_event(Camera* p_camera,const InputEvent& p_event) { return gridmap_editor->forward_spatial_input_event(p_camera,p_event); } + virtual String get_name() const { return "GridMap"; } + bool has_main_screen() const { return false; } + virtual void edit(Object *p_node); + virtual bool handles(Object *p_node) const; + virtual void make_visible(bool p_visible); + + GridMapEditorPlugin(EditorNode *p_node); + ~GridMapEditorPlugin(); + +}; + +#endif // CUBE_GRID_MAP_EDITOR_PLUGIN_H diff --git a/modules/gridmap/register_types.cpp b/modules/gridmap/register_types.cpp new file mode 100644 index 0000000000..3c3c8aa98f --- /dev/null +++ b/modules/gridmap/register_types.cpp @@ -0,0 +1,47 @@ +/*************************************************************************/ +/* register_types.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "register_types.h" +#include "object_type_db.h" +#include "grid_map.h" +#include "grid_map_editor_plugin.h" + +void register_gridmap_types() { + + ObjectTypeDB::register_type<GridMap>(); +#ifdef TOOLS_ENABLED + EditorPlugins::add_by_type<GridMapEditorPlugin>(); +#endif +} + + + +void unregister_gridmap_types() { + + +} diff --git a/modules/gridmap/register_types.h b/modules/gridmap/register_types.h new file mode 100644 index 0000000000..5cddb96b70 --- /dev/null +++ b/modules/gridmap/register_types.h @@ -0,0 +1,30 @@ +/*************************************************************************/ +/* register_types.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +void register_gridmap_types(); +void unregister_gridmap_types(); diff --git a/modules/register_module_types.h b/modules/register_module_types.h new file mode 100644 index 0000000000..3cc0422d80 --- /dev/null +++ b/modules/register_module_types.h @@ -0,0 +1,9 @@ +#ifndef REGISTER_MODULE_TYPES_H +#define REGISTER_MODULE_TYPES_H + +// + +void register_module_types(); +void unregister_module_types(); + +#endif |