summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
authorJuan Linietsky <reduzio@gmail.com>2014-02-09 22:10:30 -0300
committerJuan Linietsky <reduzio@gmail.com>2014-02-09 22:10:30 -0300
commit0b806ee0fc9097fa7bda7ac0109191c9c5e0a1ac (patch)
tree276c4d099e178eb67fbd14f61d77b05e3808e9e3 /modules
parent0e49da1687bc8192ed210947da52c9e5c5f301bb (diff)
GODOT IS OPEN SOURCE
Diffstat (limited to 'modules')
-rw-r--r--modules/SCsub24
-rw-r--r--modules/gridmap/SCsub6
-rw-r--r--modules/gridmap/config.py11
-rw-r--r--modules/gridmap/grid_map.cpp1532
-rw-r--r--modules/gridmap/grid_map.h271
-rw-r--r--modules/gridmap/grid_map_editor_plugin.cpp1470
-rw-r--r--modules/gridmap/grid_map_editor_plugin.h243
-rw-r--r--modules/gridmap/register_types.cpp47
-rw-r--r--modules/gridmap/register_types.h30
-rw-r--r--modules/register_module_types.h9
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