diff --git a/SConstruct b/SConstruct new file mode 100644 index 00000000000..28225ca76a3 --- /dev/null +++ b/SConstruct @@ -0,0 +1,301 @@ +EnsureSConsVersion(0,14); + +import os +import os.path +import glob +import sys +import methods + +methods.update_version() + +# scan possible build platforms + +platform_list = [] # list of platforms +platform_opts = {} # options for each platform +platform_flags = {} # flags for each platform + + +active_platforms=[] +active_platform_ids=[] +platform_exporters=[] +global_defaults=[] + +for x in glob.glob("platform/*"): + if (not os.path.isdir(x)): + continue + tmppath="./"+x + + sys.path.append(tmppath) + import detect + + if (os.path.exists(x+"/export/export.cpp")): + platform_exporters.append(x[9:]) + if (os.path.exists(x+"/globals/global_defaults.cpp")): + global_defaults.append(x[9:]) + if (detect.is_active()): + active_platforms.append( detect.get_name() ) + active_platform_ids.append(x); + if (detect.can_build()): + x=x.replace("platform/","") # rest of world + x=x.replace("platform\\","") # win32 + platform_list+=[x] + platform_opts[x]=detect.get_opts() + platform_flags[x]=detect.get_flags() + sys.path.remove(tmppath) + sys.modules.pop('detect') + +module_list=methods.detect_modules() + + +print "Detected Platforms: "+str(platform_list) +print("Detected Modules: "+str(module_list)) + +methods.save_active_platforms(active_platforms,active_platform_ids) + +custom_tools=['default'] + +if (os.name=="posix"): + pass +elif (os.name=="nt"): + if (os.getenv("VSINSTALLDIR")==None): + custom_tools=['mingw'] + +env_base=Environment(tools=custom_tools,ENV = {'PATH' : os.environ['PATH']}); +#env_base=Environment(tools=custom_tools); +env_base.global_defaults=global_defaults +env_base.android_source_modules=[] +env_base.android_source_files=[] +env_base.android_module_libraries=[] +env_base.android_manifest_chunk="" +env_base.disabled_modules=[] + +env_base.__class__.android_module_source = methods.android_module_source +env_base.__class__.android_module_library = methods.android_module_library +env_base.__class__.android_module_file = methods.android_module_file +env_base.__class__.android_module_manifest = methods.android_module_manifest +env_base.__class__.disable_module = methods.disable_module + +env_base.__class__.add_source_files = methods.add_source_files + +customs = ['custom.py'] + +profile = ARGUMENTS.get("profile", False) +if profile: + import os.path + if os.path.isfile(profile): + customs.append(profile) + elif os.path.isfile(profile+".py"): + customs.append(profile+".py") + +opts=Options(customs, ARGUMENTS) +opts.Add('target', 'Compile Target (debug/profile/release).', "debug") +opts.Add('platform','Platform: '+str(platform_list)+'(sfml).',"") +opts.Add('python','Build Python Support: (yes/no)','no') +opts.Add('squirrel','Build Squirrel Support: (yes/no)','no') +opts.Add('tools','Build Tools (Including Editor): (yes/no)','yes') +opts.Add('lua','Build Lua Support: (yes/no)','no') +opts.Add('rfd','Remote Filesystem Driver: (yes/no)','no') +opts.Add('gdscript','Build GDSCript support: (yes/no)','yes') +opts.Add('vorbis','Build Ogg Vorbis Support: (yes/no)','yes') +opts.Add('minizip','Build Minizip Archive Support: (yes/no)','yes') +opts.Add('opengl', 'Build OpenGL Support: (yes/no)', 'yes') +opts.Add('game', 'Game (custom) Code Directory', "") +opts.Add('squish','Squish BC Texture Compression (yes/no)','yes') +opts.Add('theora','Theora Video (yes/no)','yes') +opts.Add('freetype','Freetype support in editor','yes') +opts.Add('speex','Speex Audio (yes/no)','yes') +opts.Add('xml','XML Save/Load support (yes/no)','yes') +opts.Add('png','PNG Image loader support (yes/no)','yes') +opts.Add('jpg','JPG Image loader support (yes/no)','yes') +opts.Add('webp','WEBP Image loader support (yes/no)','yes') +opts.Add('dds','DDS Texture loader support (yes/no)','yes') +opts.Add('pvr','PVR (PowerVR) Texture loader support (yes/no)','yes') +opts.Add('builtin_zlib','Use built-in zlib (yes/no)','yes') +opts.Add('musepack','Musepack Audio (yes/no)','yes') +opts.Add('default_gui_theme','Default GUI theme (yes/no)','yes') +opts.Add("CXX", "Compiler"); +opts.Add("nedmalloc", "Add nedmalloc support", 'yes'); +opts.Add("CCFLAGS", "Custom flags for the C++ compiler"); +opts.Add("CFLAGS", "Custom flags for the C compiler"); +opts.Add("LINKFLAGS", "Custom flags for the linker"); +opts.Add('disable_3d', 'Disable 3D nodes for smaller executable (yes/no)', "no") +opts.Add('disable_advanced_gui', 'Disable advance 3D gui nodes and behaviors (yes/no)', "no") +opts.Add('old_scenes', 'Compatibility with old-style scenes', "yes") + +# add platform specific options + +for k in platform_opts.keys(): + opt_list = platform_opts[k] + for o in opt_list: + opts.Add(o[0],o[1],o[2]) + +for x in module_list: + opts.Add('module_'+x+'_enabled', "Enable module '"+x+"'.", "yes") + +opts.Update(env_base) # update environment +Help(opts.GenerateHelpText(env_base)) # generate help + +# add default include paths + +env_base.Append(CPPPATH=['#core','#core/math','#tools','#drivers','#']) + +# configure ENV for platform +env_base.detect_python=True +env_base.platform_exporters=platform_exporters + +""" +sys.path.append("./platform/"+env_base["platform"]) +import detect +detect.configure(env_base) +sys.path.remove("./platform/"+env_base["platform"]) +sys.modules.pop('detect') +""" + +if (env_base['target']=='debug'): + env_base.Append(CPPFLAGS=['-DDEBUG_MEMORY_ALLOC']); + env_base.Append(CPPFLAGS=['-DSCI_NAMESPACE']) + +env_base.platforms = {} + +for p in platform_list: + + sys.path.append("./platform/"+p) + import detect + if "create" in dir(detect): + env = detect.create(env_base) + else: + env = env_base.Clone() + detect.configure(env) + env['platform'] = p + sys.path.remove("./platform/"+p) + sys.modules.pop('detect') + + flag_list = platform_flags[p] + for f in flag_list: + env[f[0]] = f[1] + + env.module_list=[] + + for x in module_list: + if env['module_'+x+'_enabled'] != "yes": + continue + tmppath="./modules/"+x + sys.path.append(tmppath) + env.current_module=x + import config + if (config.can_build(p)): + config.configure(env) + env.module_list.append(x) + sys.path.remove(tmppath) + sys.modules.pop('config') + + + if (env['musepack']=='yes'): + env.Append(CPPFLAGS=['-DMUSEPACK_ENABLED']); + + if (env["old_scenes"]=='yes'): + env.Append(CPPFLAGS=['-DOLD_SCENE_FORMAT_ENABLED']) + if (env["rfd"]=='yes'): + env.Append(CPPFLAGS=['-DRFD_ENABLED']) + if (env["builtin_zlib"]=='yes'): + env.Append(CPPPATH=['#drivers/builtin_zlib/zlib']) + + if (env['squirrel']=='yes'): + + env.Append(CPPFLAGS=['-DSQUIRREL_ENABLED']) + env.Append(CPPPATH=['#script/squirrel/src']) + + # to test 64 bits compiltion + # env.Append(CPPFLAGS=['-m64']) + + if (env['lua']=='yes'): + + env.Append(CPPFLAGS=['-DLUA_ENABLED']) + env.Append(CPPPATH=['#script/lua/src']) + if (env_base['squish']=='yes'): + env.Append(CPPFLAGS=['-DSQUISH_ENABLED']); + + if (env['vorbis']=='yes'): + env.Append(CPPFLAGS=['-DVORBIS_ENABLED']); + + if (env['theora']=='yes'): + env.Append(CPPFLAGS=['-DTHEORA_ENABLED']); + + if (env['png']=='yes'): + env.Append(CPPFLAGS=['-DPNG_ENABLED']); + if (env['dds']=='yes'): + env.Append(CPPFLAGS=['-DDDS_ENABLED']); + if (env['pvr']=='yes'): + env.Append(CPPFLAGS=['-DPVR_ENABLED']); + if (env['jpg']=='yes'): + env.Append(CPPFLAGS=['-DJPG_ENABLED']); + if (env['webp']=='yes'): + env.Append(CPPFLAGS=['-DWEBP_ENABLED']); + + if (env['speex']=='yes'): + env.Append(CPPFLAGS=['-DSPEEX_ENABLED']); + + if (env['tools']=='yes'): + env.Append(CPPFLAGS=['-DTOOLS_ENABLED']) + if (env['disable_3d']=='yes'): + env.Append(CPPFLAGS=['-D_3D_DISABLED']) + if (env['gdscript']=='yes'): + env.Append(CPPFLAGS=['-DGDSCRIPT_ENABLED']) + if (env['disable_advanced_gui']=='yes'): + env.Append(CPPFLAGS=['-DADVANCED_GUI_DISABLED']) + + if (env['minizip'] == 'yes'): + env.Append(CPPFLAGS=['-DMINIZIP_ENABLED']) + + if (env['xml']=='yes'): + env.Append(CPPFLAGS=['-DXML_ENABLED']) + + if (env['default_gui_theme']=='no'): + env.Append(CPPFLAGS=['-DDEFAULT_THEME_DISABLED']) + + if (env["python"]=='yes'): + detected=False; + if (env.detect_python): + print("Python 3.0 Prefix:"); + pycfg_exec="python3-config" + errorval=os.system(pycfg_exec+" --prefix") + prefix="" + if (not errorval): + #gah, why can't it get both at the same time like pkg-config, sdl-config, etc? + env.ParseConfig(pycfg_exec+" --cflags") + env.ParseConfig(pycfg_exec+" --libs") + detected=True + + if (detected): + env.Append(CPPFLAGS=['-DPYTHON_ENABLED']) + #remove annoying warnings + if ('-Wstrict-prototypes' in env["CCFLAGS"]): + env["CCFLAGS"].remove('-Wstrict-prototypes'); + if ('-fwrapv' in env["CCFLAGS"]): + env["CCFLAGS"].remove('-fwrapv'); + else: + print("Python 3.0 not detected ("+pycfg_exec+") support disabled."); + + #if env['nedmalloc'] == 'yes': + # env.Append(CPPFLAGS = ['-DNEDMALLOC_ENABLED']) + + Export('env') + + #build subdirs, the build order is dependent on link order. + + SConscript("core/SCsub") + SConscript("servers/SCsub") + SConscript("scene/SCsub") + SConscript("tools/SCsub") + SConscript("script/SCsub"); + SConscript("drivers/SCsub") + SConscript("bin/SCsub") + + if env['game']: + SConscript(env['game']+'/SCsub') + + SConscript("modules/SCsub") + SConscript("main/SCsub") + + SConscript("platform/"+p+"/SCsub"); # build selected platform + diff --git a/bin/SCsub b/bin/SCsub new file mode 100644 index 00000000000..db057ed1035 --- /dev/null +++ b/bin/SCsub @@ -0,0 +1,4 @@ +Import('env') +Export('env') + +SConscript('tests/SCsub'); diff --git a/bin/tests/SCsub b/bin/tests/SCsub new file mode 100644 index 00000000000..6613df9c05b --- /dev/null +++ b/bin/tests/SCsub @@ -0,0 +1,14 @@ +Import('env') + +env.tests_sources=[] +env.add_source_files(env.tests_sources,"*.cpp") + +Export('env') + +#SConscript('math/SCsub'); + +lib = env.Library("tests",env.tests_sources) + +env.Prepend(LIBS=[lib]) + + diff --git a/bin/tests/test_containers.cpp b/bin/tests/test_containers.cpp new file mode 100644 index 00000000000..d80dbd1f22a --- /dev/null +++ b/bin/tests/test_containers.cpp @@ -0,0 +1,107 @@ +/*************************************************************************/ +/* test_containers.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 "test_containers.h" +#include "dvector.h" +#include "set.h" +#include "print_string.h" +#include "math_funcs.h" +#include "servers/visual/default_mouse_cursor.xpm" + +#include "variant.h" +#include "list.h" +#include "image.h" + +namespace TestContainers { + +MainLoop * test() { + + + /* + HashMap int_map; + + for (int i=0;i<68000;i++) { + + int num=(int)Math::random(0,1024); + int_map[i]=num; + } + */ + + + { + +// static const int size = 16; + Image img; + img.create(default_mouse_cursor_xpm); + + { + for (int i=0; i<8; i++) { + + Image mipmap; + //img.make_mipmap(mipmap); + img = mipmap; + if (img.get_width() <= 4) break; + }; + }; + + }; + +#if 0 + Set set; + + print_line("Begin Insert"); + for (int i=0;i<1100;i++) { + + int num=i;//(int)Math::random(0,1024); + // print_line("inserting "+itos(num)); + set.insert( num ); + } + + /* + for (int i=0;i<400;i++) { + + int num=(int)Math::random(0,1024); + set.erase(num); + } + */ + //set.print_tree(); + + for(Set::Element *I=set.front();I;I=I->next()) { + + print_line("inserted "+itos(I->get())+" prev is "+itos(I->prev()?I->prev()->get():-100)); + + } + + print_line("depth is "+itos(set.calculate_depth())); + print_line("Insert Success"); +#endif + + return NULL; +} + +} diff --git a/bin/tests/test_containers.h b/bin/tests/test_containers.h new file mode 100644 index 00000000000..7f73d132a12 --- /dev/null +++ b/bin/tests/test_containers.h @@ -0,0 +1,43 @@ +/*************************************************************************/ +/* test_containers.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 TEST_CONTAINERS_H +#define TEST_CONTAINERS_H + +#include "os/main_loop.h" +/** + @author Juan Linietsky +*/ + +namespace TestContainers { + +MainLoop * test(); + +} + +#endif diff --git a/bin/tests/test_detailer.cpp b/bin/tests/test_detailer.cpp new file mode 100644 index 00000000000..7eac6755d7b --- /dev/null +++ b/bin/tests/test_detailer.cpp @@ -0,0 +1,217 @@ +/*************************************************************************/ +/* test_detailer.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 "test_detailer.h" +#include "servers/visual_server.h" +#include "os/main_loop.h" +#include "math_funcs.h" +#include "print_string.h" +#include "geometry.h" +#include "quick_hull.h" +namespace TestMultiMesh { + + +class TestMainLoop : public MainLoop { + + RID instance; + RID camera; + RID viewport; + RID light; + RID mesh; + RID scenario; + +#define MULTIMESH_COUNT 1500 + + float ofs_x,ofs_y; + bool quit; +public: + + + virtual void _update_qh() { + + VisualServer *vs=VisualServer::get_singleton(); + Vector vts; +/* + + static const int s = 20; + for(int i=0;i convex_planes = Geometry::build_cylinder_planes(0.5,0.7,4,Vector3::AXIS_Z); + Geometry::MeshData convex_data = Geometry::build_convex_mesh(convex_planes); + vts=convex_data.vertices; + + Geometry::MeshData md; + Error err = QuickHull::build(vts,md); + print_line("ERR: "+itos(err)); + + vs->mesh_remove_surface(mesh,0); + vs->mesh_add_surface_from_mesh_data(mesh,md); + + + + //vs->scenario_set_debug(scenario,VS::SCENARIO_DEBUG_WIREFRAME); + + /* + RID sm = vs->shader_create(); + //vs->shader_set_fragment_code(sm,"OUT_ALPHA=mod(TIME,1);"); + //vs->shader_set_vertex_code(sm,"OUT_VERTEX=IN_VERTEX*mod(TIME,1);"); + vs->shader_set_fragment_code(sm,"OUT_DIFFUSE=vec3(1,0,1);OUT_GLOW=abs(sin(TIME));"); + RID tcmat = vs->mesh_surface_get_material(test_cube,0); + vs->material_set_shader(tcmat,sm); + */ + + } + + virtual void input_event(const InputEvent& p_event) { + + if (p_event.type==InputEvent::MOUSE_MOTION && p_event.mouse_motion.button_mask&4) { + + ofs_x+=p_event.mouse_motion.relative_y/200.0; + ofs_y+=p_event.mouse_motion.relative_x/200.0; + } + if (p_event.type==InputEvent::MOUSE_BUTTON && p_event.mouse_button.pressed && p_event.mouse_button.button_index==1) { + + QuickHull::debug_stop_after++; + _update_qh(); + } + if (p_event.type==InputEvent::MOUSE_BUTTON && p_event.mouse_button.pressed && p_event.mouse_button.button_index==2) { + + if (QuickHull::debug_stop_after>0) + QuickHull::debug_stop_after--; + _update_qh(); + } + + + } + + virtual void request_quit() { + + quit=true; + } + + + + virtual void init() { + + VisualServer *vs=VisualServer::get_singleton(); + + + mesh = vs->mesh_create(); + + scenario = vs->scenario_create(); + + QuickHull::debug_stop_after=0; + _update_qh(); + + instance = vs->instance_create2(mesh,scenario); + + camera = vs->camera_create(); + + + vs->camera_set_perspective( camera, 60.0,0.1, 100.0 ); + viewport = vs->viewport_create(); + vs->viewport_attach_camera( viewport, camera ); + vs->viewport_attach_to_screen(viewport); + vs->viewport_set_scenario( viewport, scenario ); + + vs->camera_set_transform(camera, Transform( Matrix3(), Vector3(0,0,2 ) ) ); + + RID lightaux = vs->light_create( VisualServer::LIGHT_DIRECTIONAL ); + vs->light_set_color( lightaux, VisualServer::LIGHT_COLOR_AMBIENT, Color(0.3,0.3,0.3) ); + light = vs->instance_create2( lightaux,scenario ); + vs->instance_set_transform(light,Transform(Matrix3(Vector3(0.1,0.4,0.7).normalized(),0.9))); + + ofs_x=0; + ofs_y=0; + quit=false; + } + + virtual bool idle(float p_time) { + return false; + } + + virtual bool iteration(float p_time) { + + VisualServer *vs=VisualServer::get_singleton(); + + Transform tr_camera; + tr_camera.rotate( Vector3(0,1,0), ofs_y ); + tr_camera.rotate( Vector3(1,0,0),ofs_x ); + tr_camera.translate(0,0,10); + + vs->camera_set_transform( camera, tr_camera ); + + return quit; + } + virtual void finish() { + + } + +}; + +MainLoop* test() { + + return memnew(TestMainLoop); + +} + +} diff --git a/bin/tests/test_detailer.h b/bin/tests/test_detailer.h new file mode 100644 index 00000000000..49c206ee933 --- /dev/null +++ b/bin/tests/test_detailer.h @@ -0,0 +1,44 @@ +/*************************************************************************/ +/* test_detailer.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 TEST_MULTIMESH_H +#define TEST_MULTIMESH_H + +/** + @author Juan Linietsky +*/ +#include "os/main_loop.h" + +namespace TestMultiMesh { + +MainLoop* test(); + +} + + +#endif diff --git a/bin/tests/test_gdscript.cpp b/bin/tests/test_gdscript.cpp new file mode 100644 index 00000000000..48f982425b3 --- /dev/null +++ b/bin/tests/test_gdscript.cpp @@ -0,0 +1,1003 @@ +/*************************************************************************/ +/* test_gdscript.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 "test_gdscript.h" + + +#include "os/main_loop.h" +#include "os/os.h" +#include "os/file_access.h" + +#ifdef GDSCRIPT_ENABLED + +#include "script/gdscript/gd_tokenizer.h" +#include "script/gdscript/gd_parser.h" +#include "script/gdscript/gd_compiler.h" +#include "script/gdscript/gd_script.h" + + +namespace TestGDScript { + + +static void _print_indent(int p_ident,const String& p_text) { + + String txt; + for(int i=0;iextends_file)!="") { + txt+="\""+p_class->extends_file+"\""; + if (p_class->extends_class.size()) + txt+="."; + } + + for(int i=0;iextends_class.size();i++) { + + if (i!=0) + txt+="."; + + txt+=p_class->extends_class[i]; + } + + return txt; +} + +static String _parser_expr(const GDParser::Node *p_expr) { + + String txt; + switch(p_expr->type) { + + case GDParser::Node::TYPE_IDENTIFIER: { + + const GDParser::IdentifierNode *id_node = static_cast(p_expr); + txt=id_node->name; + } break; + case GDParser::Node::TYPE_CONSTANT: { + const GDParser::ConstantNode *c_node = static_cast(p_expr); + if (c_node->value.get_type()==Variant::STRING) + txt="\""+String(c_node->value)+"\""; + else + txt=c_node->value; + + } break; + case GDParser::Node::TYPE_SELF: { + txt="self"; + } break; + case GDParser::Node::TYPE_ARRAY: { + const GDParser::ArrayNode *arr_node = static_cast(p_expr); + txt+="["; + for(int i=0;ielements.size();i++) { + + if (i>0) + txt+=", "; + txt+=_parser_expr(arr_node->elements[i]); + } + txt+="]"; + } break; + case GDParser::Node::TYPE_DICTIONARY: { + const GDParser::DictionaryNode *dict_node = static_cast(p_expr); + txt+="{"; + for(int i=0;ielements.size();i++) { + + if (i>0) + txt+=", "; + + const GDParser::DictionaryNode::Pair &p = dict_node->elements[i]; + txt+=_parser_expr(p.key); + txt+=":"; + txt+=_parser_expr(p.value); + } + txt+="}"; + } break; + case GDParser::Node::TYPE_OPERATOR: { + + const GDParser::OperatorNode *c_node = static_cast(p_expr); + switch(c_node->op) { + + case GDParser::OperatorNode::OP_PARENT_CALL: + txt+="."; + case GDParser::OperatorNode::OP_CALL: { + + ERR_FAIL_COND_V(c_node->arguments.size()<1,""); + String func_name; + const GDParser::Node *nfunc = c_node->arguments[0]; + int arg_ofs=0; + if (nfunc->type==GDParser::Node::TYPE_BUILT_IN_FUNCTION) { + + const GDParser::BuiltInFunctionNode *bif_node = static_cast(nfunc); + func_name=GDFunctions::get_func_name(bif_node->function); + arg_ofs=1; + } else if (nfunc->type==GDParser::Node::TYPE_TYPE) { + + const GDParser::TypeNode *t_node = static_cast(nfunc); + func_name=Variant::get_type_name(t_node->vtype); + arg_ofs=1; + } else { + + ERR_FAIL_COND_V(c_node->arguments.size()<2,""); + nfunc = c_node->arguments[1]; + ERR_FAIL_COND_V(nfunc->type!=GDParser::Node::TYPE_IDENTIFIER,""); + + if (c_node->arguments[0]->type!=GDParser::Node::TYPE_SELF) + func_name=_parser_expr(c_node->arguments[0])+"."; + + func_name+=_parser_expr(nfunc); + arg_ofs=2; + } + + txt+=func_name+"("; + + for(int i=arg_ofs;iarguments.size();i++) { + + const GDParser::Node *arg=c_node->arguments[i]; + if (i>arg_ofs) + txt+=", "; + txt+=_parser_expr(arg); + } + + txt+=")"; + + } break; + case GDParser::OperatorNode::OP_INDEX: { + + ERR_FAIL_COND_V(c_node->arguments.size()!=2,""); + + //index with [] + txt=_parser_expr(c_node->arguments[0])+"["+_parser_expr(c_node->arguments[1])+"]"; + + } break; + case GDParser::OperatorNode::OP_INDEX_NAMED: { + + ERR_FAIL_COND_V(c_node->arguments.size()!=2,""); + + txt=_parser_expr(c_node->arguments[0])+"."+_parser_expr(c_node->arguments[1]); + + } break; + case GDParser::OperatorNode::OP_NEG: { txt="-"+_parser_expr(c_node->arguments[0]); } break; + case GDParser::OperatorNode::OP_NOT: { txt="not "+_parser_expr(c_node->arguments[0]); } break; + case GDParser::OperatorNode::OP_BIT_INVERT: { txt="~"+_parser_expr(c_node->arguments[0]); } break; + case GDParser::OperatorNode::OP_PREINC: {} break; + case GDParser::OperatorNode::OP_PREDEC: {} break; + case GDParser::OperatorNode::OP_INC: {} break; + case GDParser::OperatorNode::OP_DEC: {} break; + case GDParser::OperatorNode::OP_IN: { txt=_parser_expr(c_node->arguments[0])+" in "+_parser_expr(c_node->arguments[1]); } break; + case GDParser::OperatorNode::OP_EQUAL: { txt=_parser_expr(c_node->arguments[0])+"=="+_parser_expr(c_node->arguments[1]); } break; + case GDParser::OperatorNode::OP_NOT_EQUAL: { txt=_parser_expr(c_node->arguments[0])+"!="+_parser_expr(c_node->arguments[1]); } break; + case GDParser::OperatorNode::OP_LESS: { txt=_parser_expr(c_node->arguments[0])+"<"+_parser_expr(c_node->arguments[1]); } break; + case GDParser::OperatorNode::OP_LESS_EQUAL: { txt=_parser_expr(c_node->arguments[0])+"<="+_parser_expr(c_node->arguments[1]); } break; + case GDParser::OperatorNode::OP_GREATER: { txt=_parser_expr(c_node->arguments[0])+">"+_parser_expr(c_node->arguments[1]); } break; + case GDParser::OperatorNode::OP_GREATER_EQUAL: { txt=_parser_expr(c_node->arguments[0])+">="+_parser_expr(c_node->arguments[1]); } break; + case GDParser::OperatorNode::OP_AND: { txt=_parser_expr(c_node->arguments[0])+" and "+_parser_expr(c_node->arguments[1]); } break; + case GDParser::OperatorNode::OP_OR: { txt=_parser_expr(c_node->arguments[0])+" or "+_parser_expr(c_node->arguments[1]); } break; + case GDParser::OperatorNode::OP_ADD: { txt=_parser_expr(c_node->arguments[0])+"+"+_parser_expr(c_node->arguments[1]); } break; + case GDParser::OperatorNode::OP_SUB: { txt=_parser_expr(c_node->arguments[0])+"-"+_parser_expr(c_node->arguments[1]); } break; + case GDParser::OperatorNode::OP_MUL: { txt=_parser_expr(c_node->arguments[0])+"*"+_parser_expr(c_node->arguments[1]); } break; + case GDParser::OperatorNode::OP_DIV: { txt=_parser_expr(c_node->arguments[0])+"/"+_parser_expr(c_node->arguments[1]); } break; + case GDParser::OperatorNode::OP_MOD: { txt=_parser_expr(c_node->arguments[0])+"%"+_parser_expr(c_node->arguments[1]); } break; + case GDParser::OperatorNode::OP_SHIFT_LEFT: { txt=_parser_expr(c_node->arguments[0])+"<<"+_parser_expr(c_node->arguments[1]); } break; + case GDParser::OperatorNode::OP_SHIFT_RIGHT: { txt=_parser_expr(c_node->arguments[0])+">>"+_parser_expr(c_node->arguments[1]); } break; + case GDParser::OperatorNode::OP_ASSIGN: { txt=_parser_expr(c_node->arguments[0])+"="+_parser_expr(c_node->arguments[1]); } break; + case GDParser::OperatorNode::OP_ASSIGN_ADD: { txt=_parser_expr(c_node->arguments[0])+"+="+_parser_expr(c_node->arguments[1]); } break; + case GDParser::OperatorNode::OP_ASSIGN_SUB: { txt=_parser_expr(c_node->arguments[0])+"-="+_parser_expr(c_node->arguments[1]); } break; + case GDParser::OperatorNode::OP_ASSIGN_MUL: { txt=_parser_expr(c_node->arguments[0])+"*="+_parser_expr(c_node->arguments[1]); } break; + case GDParser::OperatorNode::OP_ASSIGN_DIV: { txt=_parser_expr(c_node->arguments[0])+"/="+_parser_expr(c_node->arguments[1]); } break; + case GDParser::OperatorNode::OP_ASSIGN_MOD: { txt=_parser_expr(c_node->arguments[0])+"%="+_parser_expr(c_node->arguments[1]); } break; + case GDParser::OperatorNode::OP_ASSIGN_SHIFT_LEFT:{ txt=_parser_expr(c_node->arguments[0])+"<<="+_parser_expr(c_node->arguments[1]); } break; + case GDParser::OperatorNode::OP_ASSIGN_SHIFT_RIGHT: { txt=_parser_expr(c_node->arguments[0])+">>="+_parser_expr(c_node->arguments[1]); } break; + case GDParser::OperatorNode::OP_ASSIGN_BIT_AND: { txt=_parser_expr(c_node->arguments[0])+"&="+_parser_expr(c_node->arguments[1]); } break; + case GDParser::OperatorNode::OP_ASSIGN_BIT_OR: { txt=_parser_expr(c_node->arguments[0])+"|="+_parser_expr(c_node->arguments[1]); } break; + case GDParser::OperatorNode::OP_ASSIGN_BIT_XOR: { txt=_parser_expr(c_node->arguments[0])+"^="+_parser_expr(c_node->arguments[1]); } break; + case GDParser::OperatorNode::OP_BIT_AND: { txt=_parser_expr(c_node->arguments[0])+"&"+_parser_expr(c_node->arguments[1]); } break;; + case GDParser::OperatorNode::OP_BIT_OR: { txt=_parser_expr(c_node->arguments[0])+"|"+_parser_expr(c_node->arguments[1]); } break; + case GDParser::OperatorNode::OP_BIT_XOR: { txt=_parser_expr(c_node->arguments[0])+"^"+_parser_expr(c_node->arguments[1]); } break; + + } + + } break; + case GDParser::Node::TYPE_NEWLINE: { + + //skippie + } break; + default: { + + String error="Parser bug at "+itos(p_expr->line)+", invalid expression type: "+itos(p_expr->type); + ERR_EXPLAIN(error); + ERR_FAIL_V(""); + + } + + } + + return txt; + //return "("+txt+")"; +} + + +static void _parser_show_block(const GDParser::BlockNode *p_block,int p_indent) { + + for(int i=0;istatements.size();i++) { + + const GDParser::Node *statement=p_block->statements[i]; + + switch(statement->type) { + + case GDParser::Node::TYPE_CONTROL_FLOW: { + + const GDParser::ControlFlowNode *cf_node = static_cast(statement); + switch(cf_node->cf_type) { + + case GDParser::ControlFlowNode::CF_IF: { + + ERR_FAIL_COND(cf_node->arguments.size()!=1); + String txt; + txt+="if "; + txt+=_parser_expr(cf_node->arguments[0]); + txt+=":"; + _print_indent(p_indent,txt); + ERR_FAIL_COND(!cf_node->body); + _parser_show_block(cf_node->body,p_indent+1); + if (cf_node->body_else) { + _print_indent(p_indent,"else:"); + _parser_show_block(cf_node->body_else,p_indent+1); + } + + } break; + case GDParser::ControlFlowNode::CF_FOR: { + ERR_FAIL_COND(cf_node->arguments.size()!=2); + String txt; + txt+="for "; + txt+=_parser_expr(cf_node->arguments[0]); + txt+=" in "; + txt+=_parser_expr(cf_node->arguments[1]); + txt+=":"; + _print_indent(p_indent,txt); + ERR_FAIL_COND(!cf_node->body); + _parser_show_block(cf_node->body,p_indent+1); + + } break; + case GDParser::ControlFlowNode::CF_WHILE: { + + ERR_FAIL_COND(cf_node->arguments.size()!=1); + String txt; + txt+="while "; + txt+=_parser_expr(cf_node->arguments[0]); + txt+=":"; + _print_indent(p_indent,txt); + ERR_FAIL_COND(!cf_node->body); + _parser_show_block(cf_node->body,p_indent+1); + + } break; + case GDParser::ControlFlowNode::CF_SWITCH: { + + } break; + case GDParser::ControlFlowNode::CF_CONTINUE: { + + _print_indent(p_indent,"continue"); + } break; + case GDParser::ControlFlowNode::CF_BREAK: { + + _print_indent(p_indent,"break"); + } break; + case GDParser::ControlFlowNode::CF_RETURN: { + + if (cf_node->arguments.size()) + _print_indent(p_indent,"return "+_parser_expr(cf_node->arguments[0])); + else + _print_indent(p_indent,"return "); + } break; + } + + } break; + case GDParser::Node::TYPE_LOCAL_VAR: { + + const GDParser::LocalVarNode *lv_node = static_cast(statement); + _print_indent(p_indent,"var "+String(lv_node->name)); + } break; + default: { + //expression i guess + _print_indent(p_indent,_parser_expr(statement)); + + } + } + } +} + +static void _parser_show_function(const GDParser::FunctionNode *p_func,int p_indent,GDParser::BlockNode *p_initializer=NULL) { + + String txt; + if (p_func->_static) + txt="static "; + txt+="func "; + if (p_func->name=="") // initializer + txt+="[built-in-initializer]"; + else + txt+=String(p_func->name); + txt+="("; + + for(int i=0;iarguments.size();i++) { + + if (i!=0) + txt+=", "; + txt+="var "+String(p_func->arguments[i]); + if (i>=(p_func->arguments.size()-p_func->default_values.size())) { + int defarg = i - (p_func->arguments.size() - p_func->default_values.size()); + txt+="="; + txt+=_parser_expr(p_func->default_values[defarg]); + } + } + + txt+=")"; + + //todo constructor check! + + txt+=":"; + + _print_indent(p_indent,txt); + if (p_initializer) + _parser_show_block(p_initializer,p_indent+1); + _parser_show_block(p_func->body,p_indent+1); +} + +static void _parser_show_class(const GDParser::ClassNode *p_class,int p_indent,const Vector& p_code) { + + if (p_indent==0 && (String(p_class->extends_file)!="" || p_class->extends_class.size())) { + + _print_indent(p_indent,_parser_extends(p_class)); + print_line("\n"); + + } + + for(int i=0;isubclasses.size();i++) { + + + const GDParser::ClassNode *subclass=p_class->subclasses[i]; + String line="class "+subclass->name; + if (String(subclass->extends_file)!="" || subclass->extends_class.size()) + line+=" "+_parser_extends(subclass); + line+=":"; + _print_indent(p_indent,line); + _parser_show_class(subclass,p_indent+1,p_code); + print_line("\n"); + } + + + for(int i=0;iconstant_expressions.size();i++) { + + const GDParser::ClassNode::Constant &constant=p_class->constant_expressions[i]; + _print_indent(p_indent,"const "+String(constant.identifier)+"="+_parser_expr(constant.expression)); + } + + + for(int i=0;ivariables.size();i++) { + + const GDParser::ClassNode::Member &m=p_class->variables[i]; + + _print_indent(p_indent,"var "+String(m.identifier)); + + } + + print_line("\n"); + + for(int i=0;istatic_functions.size();i++) { + + _parser_show_function(p_class->static_functions[i],p_indent); + print_line("\n"); + + } + + for(int i=0;ifunctions.size();i++) { + + if (String(p_class->functions[i]->name)=="_init") { + _parser_show_function(p_class->functions[i],p_indent,p_class->initializer); + } else + _parser_show_function(p_class->functions[i],p_indent); + print_line("\n"); + + } + //_parser_show_function(p_class->initializer,p_indent); + print_line("\n"); + + +} + + +static String _disassemble_addr(const Ref& p_script,const GDFunction& func, int p_addr) { + + int addr=p_addr&GDFunction::ADDR_MASK; + + switch(p_addr>>GDFunction::ADDR_BITS) { + + case GDFunction::ADDR_TYPE_SELF: { + return "self"; + } break; + case GDFunction::ADDR_TYPE_MEMBER: { + + return "member("+p_script->debug_get_member_by_index(addr)+")"; + } break; + case GDFunction::ADDR_TYPE_CLASS_CONSTANT: { + + return "class_const("+func.get_global_name(addr)+")"; + } break; + case GDFunction::ADDR_TYPE_LOCAL_CONSTANT: { + + Variant v=func.get_constant(addr); + String txt; + if (v.get_type()==Variant::STRING || v.get_type()==Variant::NODE_PATH) + txt="\""+String(v)+"\""; + else + txt=v; + return "const("+txt+")"; + } break; + case GDFunction::ADDR_TYPE_STACK: { + + return "stack("+itos(addr)+")"; + } break; + case GDFunction::ADDR_TYPE_STACK_VARIABLE: { + + return "var_stack("+itos(addr)+")"; + } break; + case GDFunction::ADDR_TYPE_GLOBAL: { + + return "global("+func.get_global_name(addr)+")"; + } break; + case GDFunction::ADDR_TYPE_NIL: { + return "nil"; + } break; + } + + return ""; +} + +static void _disassemble_class(const Ref& p_class,const Vector& p_code) { + + + const Map& mf = p_class->debug_get_member_functions(); + + for(const Map::Element *E=mf.front();E;E=E->next()) { + + + const GDFunction &func=E->get(); + const int *code = func.get_code(); + int codelen=func.get_code_size(); + String defargs; + if (func.get_default_argument_count()) { + defargs="defarg at: "; + for(int i=0;i0) + defargs+=","; + defargs+=itos(func.get_default_argument_addr(i)); + } + defargs+=" "; + } + print_line("== function "+String(func.get_name())+"() :: stack size: "+itos(func.get_max_stack_size())+" "+defargs+"=="); + +#define DADDR(m_ip) (_disassemble_addr(p_class,func,code[ip+m_ip])) + + for(int ip=0;ip0) + txt+=", "; + txt+=DADDR(i+3); + } + txt+=")"; + + incr=4+argc; + + } break; + case GDFunction::OPCODE_CONSTRUCT_ARRAY: { + + int argc=code[ip+1]; + txt+=" make_array "; + txt+=DADDR(2+argc); + txt+=" = [ "; + + for(int i=0;i0) + txt+=", "; + txt+=DADDR(2+i); + } + + txt+="]"; + + incr+=3+argc; + + } break; + case GDFunction::OPCODE_CONSTRUCT_DICTIONARY: { + + int argc=code[ip+1]; + txt+=" make_dict "; + txt+=DADDR(2+argc*2); + txt+=" = { "; + + for(int i=0;i0) + txt+=", "; + txt+=DADDR(2+i*2+0); + txt+=":"; + txt+=DADDR(2+i*2+1); + } + + txt+="}"; + + incr+=3+argc*2; + + } break; + + case GDFunction::OPCODE_CALL: + case GDFunction::OPCODE_CALL_RETURN: { + + bool ret=code[ip]==GDFunction::OPCODE_CALL_RETURN; + + if (ret) + txt+=" call-ret "; + else + txt+=" call "; + + + int argc=code[ip+1]; + if (ret) { + txt+=DADDR(4+argc)+"="; + } + + txt+=DADDR(2)+"."; + txt+=String(func.get_global_name(code[ip+3])); + txt+="("; + + for(int i=0;i0) + txt+=", "; + txt+=DADDR(4+i); + } + txt+=")"; + + + incr=5+argc; + + } break; + case GDFunction::OPCODE_CALL_BUILT_IN: { + + txt+=" call-built-in "; + + int argc=code[ip+2]; + txt+=DADDR(3+argc)+"="; + + txt+=GDFunctions::get_func_name(GDFunctions::Function(code[ip+1])); + txt+="("; + + for(int i=0;i0) + txt+=", "; + txt+=DADDR(3+i); + } + txt+=")"; + + + incr=4+argc; + + } break; + case GDFunction::OPCODE_CALL_SELF_BASE: { + + txt+=" call-self-base "; + + int argc=code[ip+2]; + txt+=DADDR(3+argc)+"="; + + txt+=func.get_global_name(code[ip+1]); + txt+="("; + + for(int i=0;i0) + txt+=", "; + txt+=DADDR(3+i); + } + txt+=")"; + + + incr=4+argc; + + } break; + case GDFunction::OPCODE_JUMP: { + + txt+=" jump "; + txt+=itos(code[ip+1]); + + incr=2; + + } break; + case GDFunction::OPCODE_JUMP_IF: { + + + txt+=" jump-if "; + txt+=DADDR(1); + txt+=" to "; + txt+=itos(code[ip+2]); + + incr=3; + } break; + case GDFunction::OPCODE_JUMP_IF_NOT: { + + + txt+=" jump-if-not "; + txt+=DADDR(1); + txt+=" to "; + txt+=itos(code[ip+2]); + + incr=3; + } break; + case GDFunction::OPCODE_JUMP_TO_DEF_ARGUMENT: { + + + txt+=" jump-to-default-argument "; + incr=1; + } break; + case GDFunction::OPCODE_RETURN: { + + txt+=" return "; + txt+=DADDR(1); + + incr=2; + + } break; + case GDFunction::OPCODE_ITERATE_BEGIN: { + + txt+=" for-init "+DADDR(4)+" in "+DADDR(2)+" counter "+DADDR(1)+" end "+itos(code[ip+3]); + incr+=5; + + } break; + case GDFunction::OPCODE_ITERATE: { + + txt+=" for-loop "+DADDR(4)+" in "+DADDR(2)+" counter "+DADDR(1)+" end "+itos(code[ip+3]); + incr+=5; + + } break; + case GDFunction::OPCODE_LINE: { + + + + int line = code[ip+1]-1; + if (line>=0 && line cmdlargs = OS::get_singleton()->get_cmdline_args(); + + if (cmdlargs.empty()) { + //try editor! + return NULL; + } + + String test = cmdlargs.back()->get(); + + FileAccess *fa = FileAccess::open(test,FileAccess::READ); + + if (!fa) { + ERR_EXPLAIN("Could not open file: "+test); + ERR_FAIL_V(NULL); + } + + + Vector buf; + int flen = fa->get_len(); + buf.resize(fa->get_len()+1); + fa->get_buffer(&buf[0],flen); + buf[flen]=0; + + String code; + code.parse_utf8((const char*)&buf[0]); + + Vector lines; + int last=0; + + for(int i=0;i<=code.length();i++) { + + if (code[i]=='\n' || code[i]==0) { + + lines.push_back(code.substr(last,i-last)); + last=i+1; + } + } + + + if (p_test==TEST_TOKENIZER) { + + GDTokenizer tk; + tk.set_code(code); + int line=-1; + while(tk.get_token()!=GDTokenizer::TK_EOF) { + + + String text; + if (tk.get_token()==GDTokenizer::TK_IDENTIFIER) + text="'"+tk.get_token_identifier()+"' (identifier)"; + else if (tk.get_token()==GDTokenizer::TK_CONSTANT) { + Variant c= tk.get_token_constant(); + if (c.get_type()==Variant::STRING) + text="\""+String(c)+"\""; + else + text=c; + + text=text+" ("+Variant::get_type_name(c.get_type())+" constant)"; + } else if (tk.get_token()==GDTokenizer::TK_ERROR) + text="ERROR: "+tk.get_token_error(); + else if (tk.get_token()==GDTokenizer::TK_NEWLINE) + text="newline ("+itos(tk.get_token_line())+") + indent: "+itos(tk.get_token_line_indent()); + else if (tk.get_token()==GDTokenizer::TK_BUILT_IN_FUNC) + text="'"+String(GDFunctions::get_func_name(tk.get_token_built_in_func()))+"' (built-in function)"; + else + text=tk.get_token_name(tk.get_token()); + + + if (tk.get_token_line()!=line) { + int from=line+1; + line = tk.get_token_line();; + + for(int i=from;i<=line;i++) { + int l=i-1; + if (l>=0 && ltype!=GDParser::Node::TYPE_CLASS,NULL); + const GDParser::ClassNode *cnode=static_cast(root); + + _parser_show_class(cnode,0,lines); + + } + + if (p_test==TEST_COMPILER) { + + + GDParser parser; + + Error err = parser.parse(code); + if (err) { + print_line("Parse Error:\n"+itos(parser.get_error_line())+":"+itos(parser.get_error_column())+":"+parser.get_error()); + memdelete(fa); + return NULL; + + } + + GDScript *script = memnew( GDScript ); + + GDCompiler gdc; + err = gdc.compile(&parser,script); + if (err) { + + print_line("Compile Error:\n"+itos(gdc.get_error_line())+":"+itos(gdc.get_error_column())+":"+gdc.get_error()); + memdelete(script); + return NULL; + + } + + + Ref gds =Ref( script ); + + Ref current=gds; + + while(current.is_valid()) { + + print_line("** CLASS **"); + _disassemble_class(current,lines); + + current=current->get_base(); + } + + + + + } + +#if 0 + Parser parser; + Error err = parser.parse(code); + if (err) { + print_line("error:"+itos(parser.get_error_line())+":"+itos(parser.get_error_column())+":"+parser.get_error()); + } else { + print_line("Parse O-K!"); + } +#endif + + + + memdelete(fa); + + return NULL; +} + +} + +#else + +namespace TestGDScript { + +MainLoop* test(TestType p_test) { + + return NULL; +} +} + +#endif diff --git a/bin/tests/test_gdscript.h b/bin/tests/test_gdscript.h new file mode 100644 index 00000000000..c3869abb8e8 --- /dev/null +++ b/bin/tests/test_gdscript.h @@ -0,0 +1,46 @@ +/*************************************************************************/ +/* test_gdscript.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 TEST_GDSCRIPT_H +#define TEST_GDSCRIPT_H + +#include "os/main_loop.h" + +namespace TestGDScript { + +enum TestType { + TEST_TOKENIZER, + TEST_PARSER, + TEST_COMPILER +}; + +MainLoop* test(TestType p_type); + +} + +#endif // TEST_GDSCRIPT_H diff --git a/bin/tests/test_gui.cpp b/bin/tests/test_gui.cpp new file mode 100644 index 00000000000..f45fe358a60 --- /dev/null +++ b/bin/tests/test_gui.cpp @@ -0,0 +1,399 @@ +/*************************************************************************/ +/* test_gui.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. */ +/*************************************************************************/ +#ifndef _3D_DISABLED + +#include "test_gui.h" + +#include "scene/main/scene_main_loop.h" +#include "os/os.h" +#include "scene/gui/control.h" +#include "scene/gui/button.h" +#include "scene/gui/label.h" +#include "scene/gui/line_edit.h" +#include "scene/gui/scroll_bar.h" +#include "scene/gui/popup_menu.h" +#include "scene/gui/option_button.h" +#include "scene/gui/spin_box.h" +#include "scene/gui/menu_button.h" +#include "scene/gui/progress_bar.h" +#include "scene/gui/panel.h" +#include "scene/gui/tab_container.h" +#include "scene/gui/tree.h" +#include "scene/gui/rich_text_label.h" +#include "scene/gui/texture_frame.h" +#include "io/image_loader.h" +#include "print_string.h" +#include "scene/2d/sprite.h" + +#include "scene/main/viewport.h" +#include "scene/3d/camera.h" +#include "scene/3d/test_cube.h" + +namespace TestGUI { + + +class TestMainLoop : public SceneMainLoop { + + + Control *control; + +public: + + virtual void request_quit() { + + quit(); + + } + virtual void init() { + + SceneMainLoop::init(); + + +#if 0 + + + Viewport *vp = memnew( Viewport ); + vp->set_world( Ref( memnew( World ))); + get_root()->add_child(vp); + + vp->set_rect(Rect2(0,0,256,256)); + vp->set_as_render_target(true); + vp->set_render_target_update_mode(Viewport::RENDER_TARGET_UPDATE_ALWAYS); + + + Camera *camera = memnew( Camera ); + vp->add_child(camera); + camera->make_current(); + + TestCube *testcube = memnew( TestCube ); + vp->add_child(testcube); + testcube->set_transform(Transform( Matrix3().rotated(Vector3(0,1,0),Math_PI*0.25), Vector3(0,0,-8))); + + Sprite *sp = memnew( Sprite ); + sp->set_texture( vp->get_render_target_texture() ); +// sp->set_texture( ResourceLoader::load("res://ball.png") ); + sp->set_pos(Point2(300,300)); + get_root()->add_child(sp); + + + return; +#endif + + Panel * frame = memnew( Panel ); + frame->set_anchor( MARGIN_RIGHT, Control::ANCHOR_END ); + frame->set_anchor( MARGIN_BOTTOM, Control::ANCHOR_END ); + frame->set_end( Point2(0,0) ); + + get_root()->add_child( frame ); + + Label *label = memnew( Label ); + + label->set_pos( Point2( 80,90 ) ); + label->set_size( Point2( 170,80 ) ); + label->set_align( Label::ALIGN_FILL ); + //label->set_text("There"); + label->set_text("There was once upon a time a beautiful unicorn that loved to play with little girls..."); + + frame->add_child(label); + + Button *button = memnew( Button ); + + button->set_pos( Point2( 20,20 ) ); + button->set_size( Point2( 1,1 ) ); + button->set_text("This is a biggie button"); + + + frame->add_child( button ); + + +#if 0 + Sprite *tf = memnew( Sprite ); + frame->add_child(tf); + Image img; + ImageLoader::load_image("LarvoClub.png",&img); + + img.resize(512,512); + img.generate_mipmaps(); + img.compress(); + Ref text = memnew( Texture ); + text->create_from_image(img); + tf->set_texture(text); + tf->set_pos(Point2(50,50)); + //tf->set_scale(Point2(0.3,0.3)); + + + return; +#endif + + Tree * tree = memnew( Tree ); + tree->set_columns(2); + + tree->set_pos( Point2( 230,210 ) ); + tree->set_size( Point2( 150,250 ) ); + + + TreeItem *item = tree->create_item(); + item->set_editable(0,true); + item->set_text(0,"root"); + item = tree->create_item( tree->get_root() ); + item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); + item->set_editable(0,true); + item->set_text(0,"check"); + item->set_cell_mode(1, TreeItem::CELL_MODE_CHECK); + item->set_editable(1,true); + item->set_text(1,"check2"); + item = tree->create_item( tree->get_root() ); + item->set_cell_mode(0, TreeItem::CELL_MODE_RANGE); + item->set_editable(0,true); + item->set_range_config(0,0,20,0.1); + item->set_range(0,2); + item->add_button(0,Theme::get_default()->get_icon("folder","FileDialog")); + item->set_cell_mode(1, TreeItem::CELL_MODE_RANGE); + item->set_editable(1,true); + item->set_range_config(1,0,20,0.1); + item->set_range(1,3); + + item = tree->create_item( tree->get_root() ); + item->set_cell_mode(0, TreeItem::CELL_MODE_RANGE); + item->set_editable(0,true); + item->set_text(0,"Have,Many,Several,Options!"); + item->set_range(0,2); + + item = tree->create_item( item ); + item->set_editable(0,true); + item->set_text(0,"Gershwin!"); + + frame->add_child(tree); + + //control = memnew( Control ); + //root->add_child( control ); + + + + LineEdit *line_edit = memnew( LineEdit ); + + line_edit->set_pos( Point2( 30,190 ) ); + line_edit->set_size( Point2( 180,1 ) ); + + frame->add_child(line_edit); + + HScrollBar *hscroll = memnew( HScrollBar ); + + hscroll->set_pos( Point2( 30,290 ) ); + hscroll->set_size( Point2( 180,1 ) ); + hscroll->set_max(10); + hscroll->set_page(4); + + frame->add_child(hscroll); + + + + SpinBox *spin = memnew( SpinBox ); + + spin->set_pos( Point2( 30,260 ) ); + spin->set_size( Point2( 120,1 ) ); + + frame->add_child(spin); + hscroll->share(spin); + + ProgressBar *progress = memnew( ProgressBar ); + + progress->set_pos( Point2( 30,330 ) ); + progress->set_size( Point2( 120,1 ) ); + + frame->add_child(progress); + hscroll->share(progress); + + MenuButton *menu_button = memnew( MenuButton ); + + menu_button->set_text("I'm a menu!"); + menu_button->set_pos( Point2( 30,380 ) ); + menu_button->set_size( Point2( 1,1 ) ); + + frame->add_child(menu_button); + + PopupMenu *popup = menu_button->get_popup(); + + popup->add_item("Hello, testing"); + popup->add_item("My Dearest"); + popup->add_separator(); + popup->add_item("Popup"); + popup->add_check_item("Check Popup"); + popup->set_item_checked(4,true); + + OptionButton *options = memnew( OptionButton ); + + options->add_item("Hello, testing"); + options->add_item("My Dearest"); + + options->set_pos( Point2( 230,180 ) ); + options->set_size( Point2( 1,1 ) ); + + frame->add_child(options); + + /* + Tree * tree = memnew( Tree ); + tree->set_columns(2); + + tree->set_pos( Point2( 230,210 ) ); + tree->set_size( Point2( 150,250 ) ); + + + TreeItem *item = tree->create_item(); + item->set_editable(0,true); + item->set_text(0,"root"); + item = tree->create_item( tree->get_root() ); + item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); + item->set_editable(0,true); + item->set_text(0,"check"); + item = tree->create_item( tree->get_root() ); + item->set_cell_mode(0, TreeItem::CELL_MODE_RANGE); + item->set_editable(0,true); + item->set_range_config(0,0,20,0.1); + item->set_range(0,2); + item->add_button(0,Theme::get_default()->get_icon("folder","FileDialog")); + item = tree->create_item( tree->get_root() ); + item->set_cell_mode(0, TreeItem::CELL_MODE_RANGE); + item->set_editable(0,true); + item->set_text(0,"Have,Many,Several,Options!"); + item->set_range(0,2); + + frame->add_child(tree); +*/ + + + RichTextLabel *richtext = memnew( RichTextLabel ); + + richtext->set_pos( Point2( 600,210 ) ); + richtext->set_size( Point2( 180,250 ) ); + richtext->set_anchor_and_margin(MARGIN_RIGHT,Control::ANCHOR_END,20); + + frame->add_child(richtext); + + + richtext->add_text("Hello, My Friends!\n\nWelcome to the amazing world of "); + + richtext->add_newline(); + richtext->add_newline(); + + richtext->push_color(Color(1,0.5,0.5)); + richtext->add_text("leprechauns"); + richtext->pop(); +#if 0 + richtext->add_text(" and "); + richtext->push_color(Color(0,1.0,0.5)); + richtext->add_text("faeries.\n"); + richtext->pop(); + richtext->add_text("In this new episode, we will attemp to "); + richtext->push_font(richtext->get_font("mono_font","Fonts")); + richtext->push_color(Color(0.7,0.5,1.0)); + richtext->add_text("deliver something nice"); + richtext->pop(); + richtext->pop(); + richtext->add_text(" to all the viewers! Unfortunately, I need to "); + richtext->push_underline(); + richtext->add_text("keep writing a lot of text"); + richtext->pop(); + richtext->add_text(" so the label control overflows and the scrollbar appears.\n"); + //richtext->push_indent(1); + //richtext->add_text("By the way, testing indent levels! Yohohoho! Everything should appear to the right sightly here!\n"); + //richtext->pop(); + richtext->push_meta("http://www.scrollingcapabilities.xz"); + richtext->add_text("This allows to test for the scrolling capabilities "); + richtext->pop(); + richtext->add_text("of the rich text label for huge text (not like this text will really be huge but, you know).\nAs long as it is so long that it will work nicely for a test/demo, then it's welcomed in my book...\nChanging subject, the day is cloudy today and I'm wondering if I'll get che chance to travel somewhere nice. Sometimes, watching the clouds from satellite images may give a nice insight about how pressure zones in our planet work, althogh it also makes it pretty obvious to see why most weather forecasts get it wrong so often.\nClouds are so difficult to predict!\nBut it's pretty cool how our civilization has adapted to having water falling from the sky each time it rains..."); + //richtext->add_text("Hello!\nGorgeous.."); +#endif + + //richtext->push_meta("http://www.scrollingcapabilities.xz"); + ///richtext->add_text("Hello!\n"); + //richtext->pop(); + + richtext->set_anchor(MARGIN_RIGHT,Control::ANCHOR_END); + + + TabContainer * tabc = memnew( TabContainer ); + + Control *ctl= memnew( Control ); + ctl->set_name("tab 1"); + tabc->add_child(ctl); + + ctl= memnew( Control ); + ctl->set_name("tab 2"); + tabc->add_child(ctl); + label = memnew( Label ); + label->set_text("Some Label"); + label->set_pos( Point2(20,20) ); + ctl->add_child(label);; + + ctl= memnew( Control ); + ctl->set_name("tab 3"); + button = memnew( Button ); + button->set_text("Some Button"); + button->set_pos( Point2(30,50) ); + ctl->add_child(button);; + + tabc->add_child(ctl); + + frame->add_child(tabc); + + tabc->set_pos( Point2( 400,210 ) ); + tabc->set_size( Point2( 180,250 ) ); + + + Ref text = memnew( ImageTexture ); + text->load("test_data/concave.png"); + + Sprite* sprite = memnew(Sprite); + sprite->set_texture(text); + sprite->set_pos(Point2(300, 300)); + frame->add_child(sprite); + sprite->show(); + + Sprite* sprite2 = memnew(Sprite); + sprite->set_texture(text); + sprite->add_child(sprite2); + sprite2->set_pos(Point2(50, 50)); + sprite2->show(); + } + + + +}; + + +MainLoop* test() { + + + return memnew( TestMainLoop ); +} + +} + +#endif diff --git a/bin/tests/test_gui.h b/bin/tests/test_gui.h new file mode 100644 index 00000000000..85c334dcebf --- /dev/null +++ b/bin/tests/test_gui.h @@ -0,0 +1,44 @@ +/*************************************************************************/ +/* test_gui.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 TEST_GUI_H +#define TEST_GUI_H + +#include "os/main_loop.h" + +/** + @author Juan Linietsky +*/ +namespace TestGUI { + +MainLoop* test(); + +} + + +#endif diff --git a/bin/tests/test_image.cpp b/bin/tests/test_image.cpp new file mode 100644 index 00000000000..5a25c471362 --- /dev/null +++ b/bin/tests/test_image.cpp @@ -0,0 +1,77 @@ +/*************************************************************************/ +/* test_image.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 "test_image.h" +#include "os/main_loop.h" +#include "math_funcs.h" +#include "print_string.h" +#include "io/image_loader.h" +namespace TestImage { + + +class TestMainLoop : public MainLoop { + + bool quit; +public: + virtual void input_event(const InputEvent& p_event) { + + + } + + virtual void init() { + + quit=false; + } + virtual bool iteration(float p_time) { + + return quit; + } + + virtual bool idle(float p_time) { + return quit; + } + + virtual void finish() { + + } + +}; + + +MainLoop* test() { + + Image img; + ImageLoader::load_image("as1.png",&img); + + img.resize(512,512); + + return memnew( TestMainLoop ); + +} + +} diff --git a/bin/tests/test_image.h b/bin/tests/test_image.h new file mode 100644 index 00000000000..cd6fe458d44 --- /dev/null +++ b/bin/tests/test_image.h @@ -0,0 +1,44 @@ +/*************************************************************************/ +/* test_image.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 TEST_IMAGE_H +#define TEST_IMAGE_H + +/** + @author Juan Linietsky +*/ + +#include "os/main_loop.h" + +namespace TestImage { + +MainLoop* test(); + +} + +#endif diff --git a/bin/tests/test_io.cpp b/bin/tests/test_io.cpp new file mode 100644 index 00000000000..b1d8188c424 --- /dev/null +++ b/bin/tests/test_io.cpp @@ -0,0 +1,208 @@ +/*************************************************************************/ +/* test_io.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 "test_io.h" + +#ifdef MINIZIP_ENABLED + + + +#include "os/main_loop.h" +#include "os/os.h" +#include "scene/resources/texture.h" +#include "print_string.h" +#include "io/resource_loader.h" +#include "io/resource_saver.h" +#include "os/dir_access.h" +#include "core/globals.h" + +#include "io/file_access_memory.h" + +namespace TestIO { + + +class TestMainLoop : public MainLoop { + + + bool quit; + +public: + virtual void input_event(const InputEvent& p_event) { + + + } + virtual bool idle(float p_time) { + return false; + } + + virtual void request_quit() { + + quit=true; + + } + virtual void init() { + + quit=true; + } + virtual bool iteration(float p_time) { + + return quit; + } + virtual void finish() { + + } + + +}; + + +MainLoop* test() { + + print_line("this is test io"); + DirAccess* da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + da->change_dir("."); + print_line("Opening current dir "+ da->get_current_dir()); + String entry; + da->list_dir_begin(); + while ( (entry = da->get_next()) != "") { + + print_line("entry "+entry+" is dir: " + Variant(da->current_is_dir())); + }; + da->list_dir_end(); + + RES texture = ResourceLoader::load("test_data/rock.png"); + ERR_FAIL_COND_V(texture.is_null(), NULL); + + ResourceSaver::save("test_data/rock.xml",texture); + + print_line("localize paths"); + print_line(Globals::get_singleton()->localize_path("algo.xml")); + print_line(Globals::get_singleton()->localize_path("c:\\windows\\algo.xml")); + print_line(Globals::get_singleton()->localize_path(Globals::get_singleton()->get_resource_path()+"/something/something.xml")); + print_line(Globals::get_singleton()->localize_path("somedir/algo.xml")); + + { + + FileAccess* z = FileAccess::open("test_data/archive.zip", FileAccess::READ); + int len = z->get_len(); + Vector zip; + zip.resize(len); + z->get_buffer(&zip[0], len); + z->close(); + memdelete(z); + + FileAccessMemory::register_file("a_package", zip); + FileAccess::make_default(FileAccess::ACCESS_RESOURCES); + FileAccess::make_default(FileAccess::ACCESS_FILESYSTEM); + FileAccess::make_default(FileAccess::ACCESS_USERDATA); + + print_line("archive test"); +#if 0 + Archive arch; + + Archive::get_singleton()->add_package("a_package"); + FileAccessArchive f; + + print_line("opening for read"); + f._open("file.txt", FileAccess::READ); + int pos = f.get_pos(); + printf("file has %i bytes, initial pos %i\n", (int)f.get_len(), pos); + + do { + printf("%c", f.get_8()); + + } while (!f.eof_reached()); + + print_line("opening for stored seek"); + f.open("seek.bin", FileAccess::READ); + pos = f.get_pos(); + printf("byte at pos %i is %i\n", pos, (int)f.get_8()); + f.seek(128); + pos = f.get_pos(); + printf("byte at pos %i is %i\n", pos, (int)f.get_8()); + + print_line("opening for deflated seek"); + f.open("seek_deflated.bin", FileAccess::READ); + pos = f.get_pos(); + printf("byte at pos %i is %i\n", pos, (int)f.get_8()); + f.seek(128); + pos = f.get_pos(); + printf("byte at pos %i is %i\n", pos, (int)f.get_8()); + pos = f.get_pos(); + printf("byte at pos %i is %i\n", pos, (int)f.get_8()); + pos = f.get_pos(); + printf("byte at pos %i is %i\n", pos, (int)f.get_8()); + f.seek(256); + pos = f.get_pos(); + printf("byte at pos %i is %i\n", pos, (int)f.get_8()); + pos = f.get_pos(); + printf("byte at pos %i is %i\n", pos, (int)f.get_8()); + pos = f.get_pos(); + printf("byte at pos %i is %i\n", pos, (int)f.get_8()); + f.seek(4); + pos = f.get_pos(); + printf("byte at pos %i is %i\n", pos, (int)f.get_8()); + pos = f.get_pos(); + printf("byte at pos %i is %i\n", pos, (int)f.get_8()); + pos = f.get_pos(); + printf("byte at pos %i is %i\n", pos, (int)f.get_8()); + + f.close(); + + DirAccessArchive d; + String dir = "../blah1/blah2/blahask/../blah3/.//blah4/"; + printf("changing dir to %s\n", dir.utf8().get_data()); + d.change_dir(dir); + printf("current dir is %s\n", d.get_current_dir().utf8().get_data()); + + FileAccessMemory::cleanup(); +#endif + }; + + print_line("test done"); + + + return memnew( TestMainLoop ); + +} + +} + +#else + +namespace TestIO { + + +MainLoop* test() { + + return NULL; +} + +} +#endif + diff --git a/bin/tests/test_io.h b/bin/tests/test_io.h new file mode 100644 index 00000000000..9184d4bb868 --- /dev/null +++ b/bin/tests/test_io.h @@ -0,0 +1,44 @@ +/*************************************************************************/ +/* test_io.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 TEST_IO_H +#define TEST_IO_H + +/** + @author Juan Linietsky +*/ + +#include "os/main_loop.h" + +namespace TestIO { + +MainLoop* test(); + +} + +#endif diff --git a/bin/tests/test_main.cpp b/bin/tests/test_main.cpp new file mode 100644 index 00000000000..eb63a7af84d --- /dev/null +++ b/bin/tests/test_main.cpp @@ -0,0 +1,192 @@ +/*************************************************************************/ +/* test_main.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 "list.h" +#include "os/main_loop.h" + +#ifdef DEBUG_ENABLED + +#include "test_string.h" +#include "test_containers.h" +#include "test_math.h" +#include "test_gui.h" +#include "test_render.h" +#include "test_sound.h" +#include "test_misc.h" +#include "test_physics.h" +#include "test_physics_2d.h" +#include "test_python.h" +#include "test_io.h" +#include "test_particles.h" +#include "test_detailer.h" +#include "test_shader_lang.h" +#include "test_gdscript.h" +#include "test_image.h" + + +const char ** tests_get_names() { + + static const char* test_names[]={ + "string", + "containers", + "math", + "render", + "particles", + "multimesh", + "gui", + "io", + "shaderlang", + NULL + }; + + return test_names; +} + +MainLoop* test_main(String p_test,const List& p_args) { + + + if (p_test=="string") { + + return TestString::test(); + } + + if (p_test=="containers") { + + return TestContainers::test(); + } + + if (p_test=="math") { + + return TestMath::test(); + } + + if (p_test=="physics") { + + return TestPhysics::test(); + } + + if (p_test=="physics_2d") { + + return TestPhysics2D::test(); + } + + if (p_test=="misc") { + + return TestMisc::test(); + } + + if (p_test=="render") { + + return TestRender::test(); + } + + #ifndef _3D_DISABLED + if (p_test=="gui") { + + return TestGUI::test(); + } + #endif + + if (p_test=="sound") { + + return TestSound::test(); + } + + if (p_test=="io") { + + return TestIO::test(); + } + + if (p_test=="particles") { + + return TestParticles::test(); + } + + if (p_test=="multimesh") { + + return TestMultiMesh::test(); + } + + if (p_test=="shaderlang") { + + return TestShaderLang::test(); + } + + if (p_test=="gd_tokenizer") { + + return TestGDScript::test(TestGDScript::TEST_TOKENIZER); + } + + if (p_test=="gd_parser") { + + return TestGDScript::test(TestGDScript::TEST_PARSER); + } + + if (p_test=="gd_compiler") { + + return TestGDScript::test(TestGDScript::TEST_COMPILER); + } + + if (p_test=="image") { + + return TestImage::test(); + } + + if (p_test=="detailer") { + + return TestMultiMesh::test(); + } + +#ifdef PYTHON_ENABLED + + if (p_test=="python") { + + return TestPython::test(); + } +#endif + + return NULL; +} + +#else + +const char ** tests_get_names() { + + static const char* test_names[]={ + NULL + }; + + return test_names; +} + +MainLoop* test_main(String p_test,const List& p_args) { + + return NULL; +} + +#endif diff --git a/bin/tests/test_main.h b/bin/tests/test_main.h new file mode 100644 index 00000000000..404e528e14d --- /dev/null +++ b/bin/tests/test_main.h @@ -0,0 +1,41 @@ +/*************************************************************************/ +/* test_main.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 TEST_MAIN_H +#define TEST_MAIN_H + +#include "ustring.h" +#include "list.h" + +const char ** tests_get_names(); +MainLoop* test_main(String p_test,const List& p_args); + + +#endif + + diff --git a/bin/tests/test_math.cpp b/bin/tests/test_math.cpp new file mode 100644 index 00000000000..2db945d5fde --- /dev/null +++ b/bin/tests/test_math.cpp @@ -0,0 +1,300 @@ +/*************************************************************************/ +/* test_math.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 "test_math.h" +#include "ustring.h" +#include "print_string.h" +#include "transform.h" +#include "matrix3.h" +#include "math_funcs.h" +#include "camera_matrix.h" +#include "scene/main/node.h" +#include "variant.h" +#include "servers/visual/shader_language.h" +#include "os/keyboard.h" +#include "scene/resources/texture.h" +#include "vmap.h" +#include "os/os.h" +namespace TestMath { + + +void test_vec(Plane p_vec) { + + + CameraMatrix cm; + cm.set_perspective(45,1,0,100); + Plane v0=cm.xform4(p_vec); + + print_line("out: "+v0); + v0.normal.z = (v0.d/100.0 *2.0-1.0) * v0.d; + print_line("out_F: "+v0); + + +/*v0: 0, 0, -0.1, 0.1 +v1: 0, 0, 0, 0.1 +fix: 0, 0, 0, 0.1 +v0: 0, 0, 1.302803, 1.5 +v1: 0, 0, 1.401401, 1.5 +fix: 0, 0, 1.401401, 1.5 +v0: 0, 0, 25.851850, 26 +v1: 0, 0, 25.925926, 26 +fix: 0, 0, 25.925924, 26 +v0: 0, 0, 49.899902, 50 +v1: 0, 0, 49.949947, 50 +fix: 0, 0, 49.949951, 50 +v0: 0, 0, 100, 100 +v1: 0, 0, 100, 100 +fix: 0, 0, 100, 100 +*/ + + +} + + +MainLoop* test() { + + { + + // print_line("NUM: "+itos(237641278346127)); + print_line("NUM: "+itos(-128)); + return NULL; + + } + + + { + Vector3 v(1,2,3); + v.normalize(); + float a=0.3; + + //Quat q(v,a); + Matrix3 m(v,a); + + Vector3 v2(7,3,1); + v2.normalize(); + float a2=0.8; + + //Quat q(v,a); + Matrix3 m2(v2,a2); + + Quat q=m; + Quat q2=m2; + + Matrix3 m3 = m.inverse() * m2; + Quat q3 = (q.inverse() * q2);//.normalized(); + + print_line(Quat(m3)); + print_line(q3); + + print_line("before v: "+v+" a: "+rtos(a)); + q.get_axis_and_angle(v,a); + print_line("after v: "+v+" a: "+rtos(a)); + } + + return NULL; + String ret; + + List args; + args.push_back("-l"); + Error err = OS::get_singleton()->execute("/bin/ls",args,true,NULL,&ret); + print_line("error: "+itos(err)); + print_line(ret); + + return NULL; + Matrix3 m3; + m3.rotate(Vector3(1,0,0),0.2); + m3.rotate(Vector3(0,1,0),1.77); + m3.rotate(Vector3(0,0,1),212); + Matrix3 m32; + m32.set_euler(m3.get_euler()); + print_line("ELEULEEEEEEEEEEEEEEEEEER: "+m3.get_euler()+" vs "+m32.get_euler()); + + + return NULL; + + { + + Dictionary d; + d["momo"]=1; + Dictionary b=d; + b["44"]=4; + } + + + + return NULL; + print_line("inters: "+rtos(Geometry::segment_intersects_circle(Vector2(-5,0),Vector2(-2,0),Vector2(),1.0))); + + + + print_line("cross: "+Vector3(1,2,3).cross(Vector3(4,5,7))); + print_line("dot: "+rtos(Vector3(1,2,3).dot(Vector3(4,5,7)))); + print_line("abs: "+Vector3(-1,2,-3).abs()); + print_line("distance_to: "+rtos(Vector3(1,2,3).distance_to(Vector3(4,5,7)))); + print_line("distance_squared_to: "+rtos(Vector3(1,2,3).distance_squared_to(Vector3(4,5,7)))); + print_line("plus: "+(Vector3(1,2,3)+Vector3(Vector3(4,5,7)))); + print_line("minus: "+(Vector3(1,2,3)-Vector3(Vector3(4,5,7)))); + print_line("mul: "+(Vector3(1,2,3)*Vector3(Vector3(4,5,7)))); + print_line("div: "+(Vector3(1,2,3)/Vector3(Vector3(4,5,7)))); + print_line("mul scalar: "+(Vector3(1,2,3)*2)); + print_line("premul scalar: "+(2*Vector3(1,2,3))); + print_line("div scalar: "+(Vector3(1,2,3)/3.0)); + print_line("length: "+rtos(Vector3(1,2,3).length())); + print_line("length squared: "+rtos(Vector3(1,2,3).length_squared())); + print_line("normalized: "+Vector3(1,2,3).normalized()); + print_line("inverse: "+Vector3(1,2,3).inverse()); + + { + Vector3 v(4,5,7); + v.normalize(); + print_line("normalize: "+v); + } + + { + Vector3 v(4,5,7); + v+=Vector3(1,2,3); + print_line("+=: "+v); + } + + { + Vector3 v(4,5,7); + v-=Vector3(1,2,3); + print_line("-=: "+v); + } + + { + Vector3 v(4,5,7); + v*=Vector3(1,2,3); + print_line("*=: "+v); + } + + { + Vector3 v(4,5,7); + v/=Vector3(1,2,3); + print_line("/=: "+v); + } + + { + Vector3 v(4,5,7); + v*=2.0; + print_line("scalar *=: "+v); + } + + { + Vector3 v(4,5,7); + v/=2.0; + print_line("scalar /=: "+v); + } + + + +#if 0 + print_line(String("C:\\momo\\.\\popo\\..\\gongo").simplify_path()); + print_line(String("res://../popo/..//gongo").simplify_path()); + print_line(String("res://..").simplify_path()); + + + DVector a; + DVector b; + + a.resize(20); + b=a; + b.resize(30); + a=b; +#endif + +#if 0 + String za = String::utf8("á"); + printf("unicode: %x\n",za[0]); + CharString cs=za.utf8(); + for(int i=0;i path; + path.push_back("three"); + path.push_back("two"); + path.push_back("one"); + path.push_back("comeon"); + path.revert(); + + NodePath np(path,true); + + print_line(np); + + + return NULL; + + bool a=2; + + print_line(Variant(a)); + + + Matrix32 mat2_1; + mat2_1.rotate(0.5); + Matrix32 mat2_2; + mat2_2.translate(Vector2(1,2)); + Matrix32 mat2_3 = mat2_1 * mat2_2; + mat2_3.affine_invert(); + + print_line(mat2_3.elements[0]); + print_line(mat2_3.elements[1]); + print_line(mat2_3.elements[2]); + + + + Transform mat3_1; + mat3_1.basis.rotate(Vector3(0,0,1),0.5); + Transform mat3_2; + mat3_2.translate(Vector3(1,2,0)); + Transform mat3_3 = mat3_1 * mat3_2; + mat3_3.affine_invert(); + + print_line(mat3_3.basis.get_axis(0)); + print_line(mat3_3.basis.get_axis(1)); + print_line(mat3_3.origin); + +#endif + return NULL; + +} + +} diff --git a/bin/tests/test_math.h b/bin/tests/test_math.h new file mode 100644 index 00000000000..7b8d154475e --- /dev/null +++ b/bin/tests/test_math.h @@ -0,0 +1,40 @@ +/*************************************************************************/ +/* test_math.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 TEST_MATH_H +#define TEST_MATH_H + +#include "os/main_loop.h" + +namespace TestMath { + +MainLoop* test(); + +} + +#endif diff --git a/bin/tests/test_misc.cpp b/bin/tests/test_misc.cpp new file mode 100644 index 00000000000..32d1bbf0903 --- /dev/null +++ b/bin/tests/test_misc.cpp @@ -0,0 +1,499 @@ +/*************************************************************************/ +/* test_misc.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 "test_misc.h" +#include "servers/visual_server.h" +#include "os/main_loop.h" +#include "math_funcs.h" +#include "print_string.h" + + +namespace TestMisc { + +struct ConvexTestResult +{ + + Vector3 edgeA[2]; + Vector3 edgeB[2]; + bool valid; + Vector3 contactA; + Vector3 contactB; + Vector3 contactNormal; + float depth; + + /* + Vector3 contactA; + Vector3 contactB; + Vector3 contactNormal; + Vector3 contactX; + Vector3 contactY; + Vector3 edgeA[2]; + Vector3 edgeB[2]; + float depth; + bool valid; + bool isEdgeEdge; + bool needTransform; + neBool ComputerEdgeContactPoint(ConvexTestResult & res); + neBool ComputerEdgeContactPoint2(float & au, float & bu); + void Reverse() + { + neSwap(contactA, contactB); + contactNormal *= -1.0f; + }*/ + bool ComputerEdgeContactPoint2(float & au, float & bu); +}; + + + +bool ConvexTestResult::ComputerEdgeContactPoint2(float & au, float & bu) +{ + float d1343, d4321, d1321, d4343, d2121; + float numer, denom; + + Vector3 p13; + Vector3 p43; + Vector3 p21; + Vector3 diff; + + p13 = (edgeA[0]) - (edgeB[0]); + p43 = (edgeB[1]) - (edgeB[0]); + + if ( p43.length_squared() < CMP_EPSILON2 ) + { + valid = false; + goto ComputerEdgeContactPoint2_Exit; + } + + p21 = (edgeA[1]) - (edgeA[0]); + + if ( p21.length_squared()= 1.0f) + { + valid = false; + } + else if (bu < 0.0f || bu >= 1.0f) + { + valid = false; + } + else + { + valid = true; + } + { + Vector3 tmpv; + + tmpv = p21 * au; + contactA = (edgeA[0]) + tmpv; + + tmpv = p43 * bu; + contactB = (edgeB[0]) + tmpv; + } + + diff = contactA - contactB; + + depth = Math::sqrt(diff.dot(diff)); + + return true; + +ComputerEdgeContactPoint2_Exit: + + return false; +} + +struct neCollisionResult { + + float depth; + bool penetrate; + Matrix3 collisionFrame; + Vector3 contactA; + Vector3 contactB; +}; + + +struct TConvex { + + float radius; + float half_height; + float CylinderRadius() const { return radius; } + float CylinderHalfHeight() const { return half_height; } +}; + +float GetDistanceFromLine2(Vector3 v, Vector3 & project, const Vector3 & pointA, const Vector3 & pointB) +{ + Vector3 ba = pointB - pointA; + + float len = ba.length(); + + if (lenCMP_EPSILON) + { + float au, bu; + + cr.ComputerEdgeContactPoint2(au, bu); + + if (cr.valid) + { + float depth = cA.CylinderRadius() + cB.CylinderRadius() - cr.depth; + + if (depth <= 0.0f) + return; + + result.depth = depth; + + result.penetrate = true; + + result.collisionFrame.set_axis(2, (cr.contactA - cr.contactB)*(1.0f / cr.depth)); + + result.contactA = cr.contactA - result.collisionFrame.get_axis(2) * cA.CylinderRadius(); + + result.contactB = cr.contactB + result.collisionFrame.get_axis(2) * cB.CylinderRadius(); + + return; + } + } + result.depth = -1.0e6f; + + int i; + + for (i = 0; i < 2; i++) + { + //project onto edge b + + Vector3 diff = cr.edgeA[i] - cr.edgeB[1]; + + float dot = diff.dot(transB.basis.get_axis(1)); + + if (dot < 0.0f) + { + TestCylinderVertVert(result, cr.edgeA[i], cr.edgeB[1], cA, cB, transA, transB); + } + else if (dot > (2.0f * cB.CylinderHalfHeight())) + { + TestCylinderVertVert(result, cr.edgeA[i], cr.edgeB[0], cA, cB, transA, transB); + } + else + { + TestCylinderVertEdge(result, cr.edgeB[0], cr.edgeB[1], cr.edgeA[i], cB, cA, transB, transA, true); + } + } + for (i = 0; i < 2; i++) + { + //project onto edge b + + Vector3 diff = cr.edgeB[i] - cr.edgeA[1]; + + float dot = diff.dot(transA.basis.get_axis(1)); + + if (dot < 0.0f) + { + TestCylinderVertVert(result, cr.edgeB[i], cr.edgeA[1], cA, cB, transA, transB); + } + else if (dot > (2.0f * cB.CylinderHalfHeight())) + { + TestCylinderVertVert(result, cr.edgeB[i], cr.edgeA[0], cA, cB, transA, transB); + } + else + { + TestCylinderVertEdge(result, cr.edgeA[0], cr.edgeA[1], cr.edgeB[i], cA, cB, transA, transB, false); + } + } +} + + +class TestMainLoop : public MainLoop { + + RID meshA; + RID meshB; + RID poly; + RID instance; + RID camera; + RID viewport; + RID boxA; + RID boxB; + RID scenario; + + Transform rot_a; + Transform rot_b; + + bool quit; +public: + virtual void input_event(const InputEvent& p_event) { + + if (p_event.type==InputEvent::MOUSE_MOTION && p_event.mouse_motion.button_mask&BUTTON_MASK_LEFT) { + + rot_b.origin.y+=-p_event.mouse_motion.relative_y/100.0; + rot_b.origin.x+=p_event.mouse_motion.relative_x/100.0; + } + if (p_event.type==InputEvent::MOUSE_MOTION && p_event.mouse_motion.button_mask&BUTTON_MASK_MIDDLE) { + + //rot_b.origin.x+=-p_event.mouse_motion.relative_y/100.0; + rot_b.origin.z+=p_event.mouse_motion.relative_x/100.0; + } + if (p_event.type==InputEvent::MOUSE_MOTION && p_event.mouse_motion.button_mask&BUTTON_MASK_RIGHT) { + + float rot_x=-p_event.mouse_motion.relative_y/100.0; + float rot_y=p_event.mouse_motion.relative_x/100.0; + rot_b.basis = rot_b.basis * Matrix3(Vector3(1,0,0),rot_x) * Matrix3(Vector3(0,1,0),rot_y); + } + + } + virtual void request_quit() { + + quit=true; + } + virtual void init() { + + VisualServer *vs=VisualServer::get_singleton(); + + camera = vs->camera_create(); + + viewport = vs->viewport_create(); + vs->viewport_attach_to_screen(viewport); + vs->viewport_attach_camera( viewport, camera ); + vs->camera_set_transform(camera, Transform( Matrix3(), Vector3(0,0,3 ) ) ); + + /* CONVEX SHAPE */ + + DVector cylinder_planes = Geometry::build_cylinder_planes(0.5,2,9,Vector3::AXIS_Y); + RID cylinder_material = vs->fixed_material_create(); + vs->fixed_material_set_param( cylinder_material, VisualServer::FIXED_MATERIAL_PARAM_DIFFUSE, Color(0.8,0.2,0.9)); + vs->material_set_flag( cylinder_material, VisualServer::MATERIAL_FLAG_ONTOP,true); + vs->material_set_flag( cylinder_material, VisualServer::MATERIAL_FLAG_WIREFRAME,true); + vs->material_set_flag( cylinder_material, VisualServer::MATERIAL_FLAG_DOUBLE_SIDED,true); + vs->material_set_flag( cylinder_material, VisualServer::MATERIAL_FLAG_UNSHADED,true); + + RID cylinder_mesh = vs->mesh_create(); + Geometry::MeshData cylinder_data = Geometry::build_convex_mesh(cylinder_planes); + vs->mesh_add_surface_from_mesh_data(cylinder_mesh,cylinder_data); + vs->mesh_surface_set_material( cylinder_mesh, 0, cylinder_material ); + + meshA=vs->instance_create2(cylinder_mesh,scenario); + meshB=vs->instance_create2(cylinder_mesh,scenario); + boxA=vs->instance_create2(vs->get_test_cube(),scenario); + boxB=vs->instance_create2(vs->get_test_cube(),scenario); + + /* + RID lightaux = vs->light_create( VisualServer::LIGHT_OMNI ); + vs->light_set_var( lightaux, VisualServer::LIGHT_VAR_RADIUS, 80 ); + vs->light_set_var( lightaux, VisualServer::LIGHT_VAR_ATTENUATION, 1 ); + vs->light_set_var( lightaux, VisualServer::LIGHT_VAR_ENERGY, 1.5 ); + light = vs->instance_create2( lightaux ); + */ + RID lightaux = vs->light_create( VisualServer::LIGHT_DIRECTIONAL ); + vs->light_set_color( lightaux, VisualServer::LIGHT_COLOR_AMBIENT, Color(0.0,0.0,0.0) ); + //vs->light_set_shadow( lightaux, true ); + RID light = vs->instance_create2( lightaux,scenario ); + + //rot_a=Transform(Matrix3(Vector3(1,0,0),Math_PI/2.0),Vector3()); + rot_b=Transform(Matrix3(),Vector3(2,0,0)); + + //rot_x=0; + //rot_y=0; + quit=false; + } + virtual bool idle(float p_time) { + + VisualServer *vs=VisualServer::get_singleton(); + + vs->instance_set_transform(meshA,rot_a); + vs->instance_set_transform(meshB,rot_b); + + + neCollisionResult res; + TConvex a; + a.radius=0.5; + a.half_height=1; + Cylinder2CylinderTest(res,a,rot_a,a,rot_b); + if (res.penetrate) { + + Matrix3 scale; + scale.scale(Vector3(0.1,0.1,0.1)); + vs->instance_set_transform(boxA,Transform(scale,res.contactA)); + vs->instance_set_transform(boxB,Transform(scale,res.contactB)); + print_line("depth: "+rtos(res.depth)); + } else { + + Matrix3 scale; + scale.scale(Vector3()); + vs->instance_set_transform(boxA,Transform(scale,res.contactA)); + vs->instance_set_transform(boxB,Transform(scale,res.contactB)); + + } + print_line("collided: "+itos(res.penetrate)); + + return false; + } + + + virtual bool iteration(float p_time) { + + + + return quit; + } + virtual void finish() { + + } + +}; + + +MainLoop* test() { + + return memnew( TestMainLoop ); + +} + +} + + + diff --git a/bin/tests/test_misc.h b/bin/tests/test_misc.h new file mode 100644 index 00000000000..d6310e5f382 --- /dev/null +++ b/bin/tests/test_misc.h @@ -0,0 +1,40 @@ +/*************************************************************************/ +/* test_misc.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 TEST_MISC_H +#define TEST_MISC_H + +#include "os/main_loop.h" + +namespace TestMisc { + +MainLoop* test(); + +} + +#endif diff --git a/bin/tests/test_particles.cpp b/bin/tests/test_particles.cpp new file mode 100644 index 00000000000..f6f526fb218 --- /dev/null +++ b/bin/tests/test_particles.cpp @@ -0,0 +1,121 @@ +/*************************************************************************/ +/* test_particles.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 "test_particles.h" +#include "servers/visual_server.h" +#include "os/main_loop.h" +#include "math_funcs.h" +#include "print_string.h" + +namespace TestParticles { + + +class TestMainLoop : public MainLoop { + + RID particles; + RID instance; + RID camera; + RID viewport; + RID light; + RID scenario; + + struct InstanceInfo { + + RID instance; + Transform base; + Vector3 rot_axis; + }; + + List instances; + + float ofs; + bool quit; +public: + virtual void input_event(const InputEvent& p_event) { + + + } + virtual void request_quit() { + + quit=true; + } + virtual void init() { + + VisualServer *vs=VisualServer::get_singleton(); + particles = vs->particles_create(); + vs->particles_set_amount(particles,1000); + + instance = vs->instance_create2(particles,scenario); + + + camera = vs->camera_create(); + +// vs->camera_set_perspective( camera, 60.0,0.1, 100.0 ); + viewport = vs->viewport_create(); + vs->viewport_attach_camera( viewport, camera ); + vs->camera_set_transform(camera, Transform( Matrix3(), Vector3(0,0,20 ) ) ); + /* + RID lightaux = vs->light_create( VisualServer::LIGHT_OMNI ); + vs->light_set_var( lightaux, VisualServer::LIGHT_VAR_RADIUS, 80 ); + vs->light_set_var( lightaux, VisualServer::LIGHT_VAR_ATTENUATION, 1 ); + vs->light_set_var( lightaux, VisualServer::LIGHT_VAR_ENERGY, 1.5 ); + light = vs->instance_create2( lightaux ); + */ + RID lightaux = vs->light_create( VisualServer::LIGHT_DIRECTIONAL ); + vs->light_set_color( lightaux, VisualServer::LIGHT_COLOR_AMBIENT, Color(0.0,0.0,0.0) ); + light = vs->instance_create2( lightaux, scenario ); + + ofs=0; + quit=false; + } + virtual bool idle(float p_time) { + return false; + } + + + virtual bool iteration(float p_time) { + +// VisualServer *vs=VisualServer::get_singleton(); + + ofs+=p_time; + return quit; + } + virtual void finish() { + + } + +}; + + +MainLoop* test() { + + return memnew( TestMainLoop ); + +} + +} diff --git a/bin/tests/test_particles.h b/bin/tests/test_particles.h new file mode 100644 index 00000000000..876751a56ff --- /dev/null +++ b/bin/tests/test_particles.h @@ -0,0 +1,43 @@ +/*************************************************************************/ +/* test_particles.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 TEST_PARTICLES_H +#define TEST_PARTICLES_H + +/** + @author Juan Linietsky +*/ +#include "os/main_loop.h" + +namespace TestParticles { + +MainLoop* test(); + +} + +#endif diff --git a/bin/tests/test_physics.cpp b/bin/tests/test_physics.cpp new file mode 100644 index 00000000000..3dd8c5744d0 --- /dev/null +++ b/bin/tests/test_physics.cpp @@ -0,0 +1,662 @@ +/*************************************************************************/ +/* test_physics.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 "test_physics.h" + + +#include "servers/visual_server.h" +#include "servers/physics_server.h" +#include "os/main_loop.h" +#include "math_funcs.h" +#include "print_string.h" +#include "map.h" +#include "os/os.h" +#include "quick_hull.h" + +class TestPhysicsMainLoop : public MainLoop { + + OBJ_TYPE( TestPhysicsMainLoop, MainLoop ); + + enum { + LINK_COUNT = 20, + }; + + RID test_cube; + + RID plane; + RID sphere; + RID light; + RID camera; + RID mover; + RID scenario; + RID space; + + RID character; + + float ofs_x,ofs_y; + + Point2 joy_direction; + + List bodies; + Map type_shape_map; + Map type_mesh_map; + + void body_changed_transform(Object *p_state, RID p_visual_instance) { + + PhysicsDirectBodyState *state = (PhysicsDirectBodyState*)p_state; + VisualServer *vs=VisualServer::get_singleton(); + Transform t=state->get_transform(); + //t.basis.scale( Vector3(1.0,0.5,0.2) ); + vs->instance_set_transform(p_visual_instance,t); + } + + bool quit; + +protected: + + static void _bind_methods() { + + ObjectTypeDB::bind_method("body_changed_transform",&TestPhysicsMainLoop::body_changed_transform); + } + + RID create_body(PhysicsServer::ShapeType p_shape, PhysicsServer::BodyMode p_body,const Transform p_location,bool p_active_default=true,const Transform&p_shape_xform=Transform()) { + + VisualServer *vs=VisualServer::get_singleton(); + PhysicsServer * ps = PhysicsServer::get_singleton(); + + RID mesh_instance = vs->instance_create2(type_mesh_map[p_shape],scenario); + RID body = ps->body_create(p_body,!p_active_default); + ps->body_set_space(body,space); + ps->body_set_param(body,PhysicsServer::BODY_PARAM_BOUNCE,0.5); + //todo set space + ps->body_add_shape(body,type_shape_map[p_shape]); + ps->body_set_force_integration_callback(body,this,"body_changed_transform",mesh_instance); + + ps->body_set_state( body, PhysicsServer::BODY_STATE_TRANSFORM,p_location); + bodies.push_back(body); + + if (p_body==PhysicsServer::BODY_MODE_STATIC) { + + vs->instance_set_transform(mesh_instance,p_location); + } + return body; + } + + RID create_static_plane(const Plane& p_plane) { + + PhysicsServer * ps = PhysicsServer::get_singleton(); + + RID plane_shape = ps->shape_create(PhysicsServer::SHAPE_PLANE);; + ps->shape_set_data( plane_shape, p_plane ); + + RID b = ps->body_create( PhysicsServer::BODY_MODE_STATIC ); + ps->body_set_space(b,space); + //todo set space + ps->body_add_shape(b, plane_shape); + return b; + + } + + void configure_body(RID p_body,float p_mass, float p_friction, float p_bounce) { + + PhysicsServer * ps = PhysicsServer::get_singleton(); + ps->body_set_param( p_body, PhysicsServer::BODY_PARAM_MASS, p_mass ); + ps->body_set_param( p_body, PhysicsServer::BODY_PARAM_FRICTION, p_friction ); + ps->body_set_param( p_body, PhysicsServer::BODY_PARAM_BOUNCE, p_bounce ); + + } + + void init_shapes() { + + VisualServer *vs=VisualServer::get_singleton(); + PhysicsServer * ps = PhysicsServer::get_singleton(); + + /* SPHERE SHAPE */ + RID sphere_mesh = vs->make_sphere_mesh(10,20,0.5); + RID sphere_material = vs->fixed_material_create(); + //vs->material_set_flag( sphere_material, VisualServer::MATERIAL_FLAG_WIREFRAME, true ); + vs->fixed_material_set_param( sphere_material, VisualServer::FIXED_MATERIAL_PARAM_DIFFUSE, Color(0.7,0.8,3.0) ); + vs->mesh_surface_set_material( sphere_mesh, 0, sphere_material ); + type_mesh_map[PhysicsServer::SHAPE_SPHERE]=sphere_mesh; + + RID sphere_shape=ps->shape_create(PhysicsServer::SHAPE_SPHERE); + ps->shape_set_data( sphere_shape, 0.5 ); + type_shape_map[PhysicsServer::SHAPE_SPHERE]=sphere_shape; + + /* BOX SHAPE */ + + DVector box_planes = Geometry::build_box_planes(Vector3(0.5,0.5,0.5)); + RID box_material = vs->fixed_material_create(); + vs->fixed_material_set_param( box_material, VisualServer::FIXED_MATERIAL_PARAM_DIFFUSE, Color(1.0,0.2,0.2) ); + RID box_mesh = vs->mesh_create(); + Geometry::MeshData box_data = Geometry::build_convex_mesh(box_planes); + vs->mesh_add_surface_from_mesh_data(box_mesh,box_data); + vs->mesh_surface_set_material( box_mesh, 0, box_material ); + type_mesh_map[PhysicsServer::SHAPE_BOX]=box_mesh; + + RID box_shape=ps->shape_create(PhysicsServer::SHAPE_BOX); + ps->shape_set_data( box_shape, Vector3(0.5,0.5,0.5) ); + type_shape_map[PhysicsServer::SHAPE_BOX]=box_shape; + + + /* CAPSULE SHAPE */ + + DVector capsule_planes = Geometry::build_capsule_planes(0.5,0.7,12,Vector3::AXIS_Z); + RID capsule_material = vs->fixed_material_create(); + vs->fixed_material_set_param( capsule_material, VisualServer::FIXED_MATERIAL_PARAM_DIFFUSE, Color(0.3,0.4,1.0) ); + + RID capsule_mesh = vs->mesh_create(); + Geometry::MeshData capsule_data = Geometry::build_convex_mesh(capsule_planes); + vs->mesh_add_surface_from_mesh_data(capsule_mesh,capsule_data); + vs->mesh_surface_set_material( capsule_mesh, 0, capsule_material ); + type_mesh_map[PhysicsServer::SHAPE_CAPSULE]=capsule_mesh; + + RID capsule_shape=ps->shape_create(PhysicsServer::SHAPE_CAPSULE); + Dictionary capsule_params; + capsule_params["radius"]=0.5; + capsule_params["height"]=1.4; + ps->shape_set_data( capsule_shape, capsule_params ); + type_shape_map[PhysicsServer::SHAPE_CAPSULE]=capsule_shape; + + /* CONVEX SHAPE */ + + DVector convex_planes = Geometry::build_cylinder_planes(0.5,0.7,5,Vector3::AXIS_Z); + RID convex_material = vs->fixed_material_create(); + vs->fixed_material_set_param( convex_material, VisualServer::FIXED_MATERIAL_PARAM_DIFFUSE, Color(0.8,0.2,0.9)); + + RID convex_mesh = vs->mesh_create(); + Geometry::MeshData convex_data = Geometry::build_convex_mesh(convex_planes); + QuickHull::build(convex_data.vertices,convex_data); + vs->mesh_add_surface_from_mesh_data(convex_mesh,convex_data); + vs->mesh_surface_set_material( convex_mesh, 0, convex_material ); + type_mesh_map[PhysicsServer::SHAPE_CONVEX_POLYGON]=convex_mesh; + + RID convex_shape=ps->shape_create(PhysicsServer::SHAPE_CONVEX_POLYGON); + ps->shape_set_data( convex_shape, convex_data.vertices ); + type_shape_map[PhysicsServer::SHAPE_CONVEX_POLYGON]=convex_shape; + + } + + void make_trimesh(Vector p_faces,const Transform& p_xform=Transform()) { + + VisualServer *vs=VisualServer::get_singleton(); + PhysicsServer * ps = PhysicsServer::get_singleton(); + RID trimesh_shape = ps->shape_create(PhysicsServer::SHAPE_CONCAVE_POLYGON); + ps->shape_set_data(trimesh_shape, p_faces); + p_faces=ps->shape_get_data(trimesh_shape); // optimized one + Vector normals; // for drawing + for (int i=0;imesh_create(); + Array d; + d.resize(VS::ARRAY_MAX); + d[VS::ARRAY_VERTEX]=p_faces; + d[VS::ARRAY_NORMAL]=normals; + vs->mesh_add_surface(trimesh_mesh, VS::PRIMITIVE_TRIANGLES, d ); + RID trimesh_mat = vs->fixed_material_create(); + vs->fixed_material_set_param( trimesh_mat, VisualServer::FIXED_MATERIAL_PARAM_DIFFUSE, Color(1.0,0.5,0.8)); + //vs->material_set_flag( trimesh_mat, VisualServer::MATERIAL_FLAG_UNSHADED,true); + vs->mesh_surface_set_material( trimesh_mesh, 0, trimesh_mat ); + + RID triins = vs->instance_create2(trimesh_mesh,scenario); + + + RID tribody = ps->body_create( PhysicsServer::BODY_MODE_STATIC); + ps->body_set_space(tribody,space); + //todo set space + ps->body_add_shape(tribody, trimesh_shape); + Transform tritrans = p_xform; + ps->body_set_state( tribody, PhysicsServer::BODY_STATE_TRANSFORM, tritrans ); + vs->instance_set_transform( triins, tritrans ); + //RID trimesh_material = vs->fixed_material_create(); + //vs->material_generate( trimesh_material, Color(0.2,0.4,0.6) ); + //vs->mesh_surface_set_material( trimesh_mesh, 0, trimesh_material ); + + } + + void make_grid(int p_width,int p_height,float p_cellsize,float p_cellheight,const Transform& p_xform=Transform()) { + + Vector< Vector< float > > grid; + + grid.resize(p_width); + + for (int i=0;i faces; + + for (int i=1;ibody_get_state(mover,PhysicsServer::BODY_STATE_TRANSFORM); + t.origin+=Vector3(x,y,0); + + ps->body_set_state(mover,PhysicsServer::BODY_STATE_TRANSFORM,t); + } + + } + + if (p_event.type == InputEvent::JOYSTICK_MOTION) { + + if (p_event.joy_motion.axis == 0) { + + joy_direction.x = p_event.joy_motion.axis_value; + }; + + if (p_event.joy_motion.axis == 1) { + + joy_direction.y = p_event.joy_motion.axis_value; + }; + }; + } + + virtual void request_quit() { + + quit=true; + } + virtual void init() { + + ofs_x=ofs_y=0; + init_shapes(); + + PhysicsServer *ps = PhysicsServer::get_singleton(); + space=ps->space_create(); + ps->space_set_active(space,true); + + VisualServer *vs=VisualServer::get_singleton(); + + /* LIGHT */ + RID lightaux = vs->light_create( VisualServer::LIGHT_DIRECTIONAL ); + //vs->light_set_color( lightaux, VisualServer::LIGHT_COLOR_AMBIENT, Color(0.0,0.0,0.0) ); + scenario = vs->scenario_create(); + vs->light_set_shadow(lightaux,true); + light = vs->instance_create2( lightaux,scenario ); + Transform t; + t.rotate(Vector3(1.0,0,0),0.6); + vs->instance_set_transform(light,t); + + + + /* CAMERA */ + + camera = vs->camera_create(); + RID viewport = vs->viewport_create(); + vs->viewport_attach_camera( viewport, camera ); + vs->viewport_attach_to_screen(viewport); + vs->viewport_set_scenario( viewport, scenario ); + + vs->camera_set_perspective(camera,60,0.1,40.0); + vs->camera_set_transform(camera,Transform( Matrix3(), Vector3(0,9,12))); + //vs->scenario_set_debug(scenario,VS::SCENARIO_DEBUG_WIREFRAME); + + Transform gxf; + gxf.basis.scale(Vector3(1.4,0.4,1.4)); + gxf.origin=Vector3(-2,1,-2); + make_grid(5,5,2.5,1,gxf); + // create_body(PhysicsServer::SHAPE_BOX,PhysicsServer::BODY_MODE_STATIC,gxf); + //create_static_plane( Plane( Vector3(0,1,0), -2) ); +// test_joint(); + test_fall(); + //test_joint(); + + +/* + Vector faces; + faces.push_back( Vector3(10,0,-5) ); + faces.push_back( Vector3(0,0,10) ); + faces.push_back( Vector3(-10,-0.2,-5) ); + make_trimesh(faces); +*/ + /* Make Trimesh */ + quit=false; + return; + +#if 0 +#define GRID_SIZE 5 + + float grid[GRID_SIZE][GRID_SIZE]; + + for (int i=0;i faces; + + for (int i=1;ishape_create(); + ps->shape_set_data(trimesh_shape, PhysicsServer::SHAPE_CONCAVE_POLYGON,faces); + faces=ps->shape_get_shape(trimesh_shape, 0); + Vector normals; // for drawing + for (int i=0;imesh_create(); + vs->mesh_add_surface(trimesh_mesh, VS::PRIMITIVE_TRIANGLES, VS::ARRAY_FORMAT_VERTEX|VS::ARRAY_FORMAT_NORMAL, faces.size() ); + vs->mesh_surface_set_array(trimesh_mesh,0,VS::ARRAY_VERTEX, faces ); + vs->mesh_surface_set_array(trimesh_mesh,0,VS::ARRAY_NORMAL, normals ); + RID trimesh_mat = vs->fixed_material_create(); + vs->material_generate( trimesh_mat, Color(1.0,0.5,0.3) ); + vs->mesh_surface_set_material( trimesh_mesh, 0, trimesh_mat ); + + RID triins = vs->instance_create2(trimesh_mesh); + + + + RID tribody = ps->body_create( PhysicsServer::BODY_MODE_STATIC, trimesh_shape); + Transform tritrans = Transform( Matrix3(), Vector3(0,0,-2) ); + ps->body_set_state( tribody, PhysicsServer::BODY_STATE_TRANSFORM, tritrans ); + vs->instance_set_transform( triins, tritrans ); + RID trimesh_material = vs->fixed_material_create(); + vs->material_generate( trimesh_material, Color(0.2,0.4,0.6) ); + vs->mesh_surface_set_material( trimesh_mesh, 0, trimesh_material ); +#endif + } + virtual bool iteration(float p_time) { + + if (mover) { + static float joy_speed = 10; + PhysicsServer * ps = PhysicsServer::get_singleton(); + Transform t = ps->body_get_state(mover,PhysicsServer::BODY_STATE_TRANSFORM); + t.origin+=Vector3(joy_speed * joy_direction.x * p_time, -joy_speed * joy_direction.y * p_time,0); + ps->body_set_state(mover,PhysicsServer::BODY_STATE_TRANSFORM,t); + }; + + + Transform cameratr; + cameratr.rotate(Vector3(0,1,0),ofs_x); + cameratr.rotate(Vector3(1,0,0),-ofs_y); + cameratr.translate(Vector3(0,2,8)); + VisualServer *vs=VisualServer::get_singleton(); + vs->camera_set_transform(camera,cameratr); + + return quit; + } + virtual void finish() { + + } + + void test_joint() { +#if 0 + PhysicsServer * ps = PhysicsServer::get_singleton(); + + mover = create_body(PhysicsServer::SHAPE_BOX,PhysicsServer::BODY_MODE_STATIC,Transform(Matrix3(),Vector3(0,0,-24))); + RID b = create_body(PhysicsServer::SHAPE_CAPSULE,PhysicsServer::BODY_MODE_RIGID,Transform()); + + ps->joint_create_double_pin(b,Vector3(0,0,1.0),mover,Vector3(0,0,0)); + ps->body_add_collision_exception(mover,b); + + + List cmdline = OS::get_singleton()->get_cmdline_args(); + int link_count = LINK_COUNT; + if (cmdline.size() > 0 && cmdline[cmdline.size()-1].to_int()) { + link_count = cmdline[cmdline.size()-1].to_int(); + }; + + for(int i=0;ijoint_create_double_pin(b,Vector3(0,0,-0.7),c,Vector3(0,0,0.7)); + ps->body_add_collision_exception(c,b); + b=c; + } + + + create_static_plane(Plane(Vector3(0,1,0),-8)); +#endif + } + + void test_hinge() { +#if 0 + PhysicsServer * ps = PhysicsServer::get_singleton(); + + + mover = create_body(PhysicsServer::SHAPE_BOX,PhysicsServer::BODY_MODE_STATIC,Transform(Matrix3(),Vector3(0,0,-24))); + RID b = create_body(PhysicsServer::SHAPE_BOX,PhysicsServer::BODY_MODE_RIGID,Transform()); + + ps->joint_create_double_hinge(b,Transform(Matrix3(),Vector3(1,1,1.0)),mover,Transform(Matrix3(),Vector3(0,0,0))); + ps->body_add_collision_exception(mover,b); + +/* + for(int i=0;i<20;i++) { + + RID c = create_body(PhysicsServer::SHAPE_CAPSULE,PhysicsServer::BODY_MODE_RIGID,Transform()); + ps->joint_create_double_hinge(b,Transform(Matrix3(),Vector3(0,0,-0.7)),c,Transform(Matrix3(),Vector3(0,0,0.7))); + ps->body_add_collision_exception(c,b); + b=c; + } + +*/ + //create_static_plane(Plane(Vector3(0,1,0),-8)); +#endif + } + + void test_character() { + + VisualServer *vs=VisualServer::get_singleton(); + PhysicsServer * ps = PhysicsServer::get_singleton(); + + + DVector capsule_planes = Geometry::build_capsule_planes(0.5,1,12,5,Vector3::AXIS_Y); + RID capsule_material = vs->fixed_material_create(); + + vs->fixed_material_set_param( capsule_material, VisualServer::FIXED_MATERIAL_PARAM_DIFFUSE, Color(1,1,1) ); + + + RID capsule_mesh = vs->mesh_create(); + Geometry::MeshData capsule_data = Geometry::build_convex_mesh(capsule_planes); + vs->mesh_add_surface_from_mesh_data(capsule_mesh,capsule_data); + vs->mesh_surface_set_material( capsule_mesh, 0, capsule_material ); + type_mesh_map[PhysicsServer::SHAPE_CAPSULE]=capsule_mesh; + + RID capsule_shape=ps->shape_create(PhysicsServer::SHAPE_CAPSULE); + Dictionary capsule_params; + capsule_params["radius"]=0.5; + capsule_params["height"]=1; + Transform shape_xform; + shape_xform.rotate(Vector3(1,0,0),Math_PI/2.0); + //shape_xform.origin=Vector3(1,1,1); + ps->shape_set_data( capsule_shape, capsule_params); + + + RID mesh_instance = vs->instance_create2(capsule_mesh,scenario); + character = ps->body_create(PhysicsServer::BODY_MODE_CHARACTER); + ps->body_set_space(character,space); + //todo add space + ps->body_add_shape(character,capsule_shape); + + ps->body_set_force_integration_callback(character,this,"body_changed_transform",mesh_instance); + + + ps->body_set_state( character, PhysicsServer::BODY_STATE_TRANSFORM,Transform(Matrix3(),Vector3(-2,5,-2))); + bodies.push_back(character); + + + } + + void test_fall() { + + + for (int i=0;i<35;i++) { + + static const PhysicsServer::ShapeType shape_idx[]={ + PhysicsServer::SHAPE_CAPSULE, + PhysicsServer::SHAPE_BOX, + PhysicsServer::SHAPE_SPHERE, + PhysicsServer::SHAPE_CONVEX_POLYGON + }; + + PhysicsServer::ShapeType type=shape_idx[i%4]; + //type=PhysicsServer::SHAPE_CONVEX_POLYGON; + + Transform t; + + t.origin=Vector3(0.0*i,3.5+1.1*i,0.7+0.0*i); + //t.origin=Vector3(-0.7+0.0*i,0.5+4.1*i,0); + t.basis.rotate(Vector3(0.2,-1,0),Math_PI/2*0.6); + //t.basis.rotate(Vector3(0,-1,0),Math_PI/4*i); + //t.basis.rotate(Vector3(0,-1,0),Math_PI/4*i); + //t.basis.rotate(Vector3(-1,0,0),Math_PI/4*i); + + + RID b = create_body(type,PhysicsServer::BODY_MODE_RIGID,t); + //RID b = create_body(type,i==0?PhysicsServer::BODY_MODE_STATIC:PhysicsServer::BODY_MODE_RIGID,t); + + } + + create_static_plane( Plane( Vector3(0,1,0), -1) ); + + +/* + create_static_plane( Plane( Vector3(1,0,0), -2) ); + create_static_plane( Plane( Vector3(-1,0,0), -2) ); + create_static_plane( Plane( Vector3(0,0,1), -2) ); + create_static_plane( Plane( Vector3(0,0,-1), -2) ); +*/ + + + } + + void test_activate() { + + create_body(PhysicsServer::SHAPE_BOX,PhysicsServer::BODY_MODE_RIGID,Transform(Matrix3(),Vector3(0,2,0)),true); + //create_body(PhysicsServer::SHAPE_SPHERE,PhysicsServer::BODY_MODE_RIGID,Transform(Matrix3(),Vector3(0,6,0)),true); + create_static_plane( Plane( Vector3(0,1,0), -1) ); + + } + + + virtual bool idle(float p_time) { + return false; + } + + + + + + + + TestPhysicsMainLoop() { + + } +}; + +namespace TestPhysics { + +MainLoop* test() { + + return memnew( TestPhysicsMainLoop ); + +} + +} diff --git a/bin/tests/test_physics.h b/bin/tests/test_physics.h new file mode 100644 index 00000000000..ef4f6478296 --- /dev/null +++ b/bin/tests/test_physics.h @@ -0,0 +1,44 @@ +/*************************************************************************/ +/* test_physics.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 TEST_PHYSICS_H +#define TEST_PHYSICS_H + +/** + @author Juan Linietsky +*/ + +#include "os/main_loop.h" + +namespace TestPhysics { + +MainLoop* test(); + +} + +#endif diff --git a/bin/tests/test_physics_2d.cpp b/bin/tests/test_physics_2d.cpp new file mode 100644 index 00000000000..db7f6ea3748 --- /dev/null +++ b/bin/tests/test_physics_2d.cpp @@ -0,0 +1,466 @@ +/*************************************************************************/ +/* test_physics_2d.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 "test_physics_2d.h" +#include "servers/visual_server.h" +#include "servers/physics_2d_server.h" +#include "os/main_loop.h" +#include "print_string.h" +#include "map.h" +#include "scene/resources/texture.h" +#include "os/os.h" + +static const unsigned char convex_png[]={ +0x89,0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,0x0,0x0,0x40,0x0,0x0,0x0,0x40,0x8,0x6,0x0,0x0,0x0,0xaa,0x69,0x71,0xde,0x0,0x0,0x0,0x1,0x73,0x52,0x47,0x42,0x0,0xae,0xce,0x1c,0xe9,0x0,0x0,0x0,0x6,0x62,0x4b,0x47,0x44,0x0,0x0,0x0,0x0,0x0,0x0,0xf9,0x43,0xbb,0x7f,0x0,0x0,0x0,0x9,0x70,0x48,0x59,0x73,0x0,0x0,0xb,0x13,0x0,0x0,0xb,0x13,0x1,0x0,0x9a,0x9c,0x18,0x0,0x0,0x0,0x7,0x74,0x49,0x4d,0x45,0x7,0xdb,0x6,0xa,0x3,0x13,0x31,0x66,0xa7,0xac,0x79,0x0,0x0,0x4,0xef,0x49,0x44,0x41,0x54,0x78,0xda,0xed,0x9b,0xdd,0x4e,0x2a,0x57,0x14,0xc7,0xf7,0x1e,0xc0,0x19,0x38,0x32,0x80,0xa,0x6a,0xda,0x18,0xa3,0xc6,0x47,0x50,0x7b,0xa1,0xd9,0x36,0x27,0x7e,0x44,0xed,0x45,0x4d,0x93,0x3e,0x40,0x1f,0x64,0x90,0xf4,0x1,0xbc,0xf0,0xc2,0x9c,0x57,0x30,0x4d,0xbc,0xa8,0x6d,0xc,0x69,0x26,0xb5,0x68,0x8b,0x35,0x7e,0x20,0xb4,0xf5,0x14,0xbf,0x51,0x3c,0x52,0xe,0xc,0xe,0xc8,0xf0,0xb1,0x7a,0x51,0x3d,0xb1,0x9e,0x19,0x1c,0x54,0x70,0x1c,0xdc,0x9,0x17,0x64,0x8,0xc9,0xff,0xb7,0xd6,0x7f,0xcd,0x3f,0x2b,0xd9,0x8,0xbd,0x9c,0xda,0x3e,0xf8,0x31,0xff,0xc,0x0,0x8,0x42,0x88,0x9c,0x9f,0x9f,0xbf,0xa,0x87,0xc3,0xad,0x7d,0x7d,0x7d,0x7f,0x23,0x84,0x78,0x8c,0x31,0xaf,0x55,0x0,0xc6,0xc7,0x14,0x1e,0x8f,0xc7,0xbf,0x38,0x3c,0x3c,0x6c,0x9b,0x9f,0x9f,0x6f,0xb8,0x82,0x9b,0xee,0xe8,0xe8,0xf8,0x12,0x0,0xbe,0xd3,0x2a,0x8,0xfc,0x50,0xd1,0xf9,0x7c,0x9e,0x8a,0x46,0xa3,0x5f,0x9d,0x9e,0x9e,0x7e,0xb2,0xb0,0xb0,0x60,0xe5,0x79,0x1e,0xf1,0xfc,0x7f,0x3a,0x9,0x21,0x88,0x10,0x82,0x26,0x26,0x26,0xde,0x77,0x75,0x75,0x85,0x59,0x96,0xfd,0x5e,0x6b,0x20,0xf0,0x7d,0x85,0x4b,0x92,0xf4,0xfa,0xe0,0xe0,0xe0,0xd3,0xb9,0xb9,0xb9,0x46,0x49,0x92,0xea,0x6f,0xa,0xbf,0x7d,0x8,0x21,0x68,0x70,0x70,0xb0,0x38,0x39,0x39,0x79,0xd6,0xd9,0xd9,0xb9,0xcf,0x30,0xcc,0xa2,0xd6,0xad,0x21,0x2b,0x1c,0x0,0x38,0x41,0x10,0xfc,0xdb,0xdb,0xdb,0x27,0x1e,0x8f,0x27,0x4b,0x8,0x1,0x84,0x90,0xea,0xf,0x21,0x4,0x3c,0x1e,0x4f,0x76,0x67,0x67,0x67,0x3f,0x9f,0xcf,0xff,0x7c,0x5,0xf3,0xd9,0x0,0xe0,0x2,0x81,0xc0,0xa9,0xdb,0xed,0x2e,0x94,0x2b,0x5c,0xe,0xc4,0xca,0xca,0x8a,0x18,0x8d,0x46,0x3,0x0,0xc0,0x69,0x1e,0x4,0x0,0x90,0x48,0x24,0x12,0xe4,0x38,0xee,0x41,0xc2,0x6f,0x43,0xe0,0x38,0xe,0xfc,0x7e,0xbf,0x10,0x8b,0xc5,0xd6,0x35,0xd,0x22,0x9b,0xcd,0x7a,0x96,0x97,0x97,0x33,0xf,0xad,0x7c,0x29,0x10,0x9b,0x9b,0x9b,0xef,0x2e,0x2e,0x2e,0x7e,0xd5,0x1c,0x8,0x0,0x20,0xe1,0x70,0x38,0xfc,0x98,0xd5,0x57,0x2,0xe1,0x76,0xbb,0xf3,0xa1,0x50,0xe8,0x38,0x9b,0xcd,0xfe,0xa2,0x9,0x8,0x0,0x40,0x2e,0x2f,0x2f,0x7d,0x4b,0x4b,0x4b,0xb9,0x4a,0x54,0x5f,0x9,0xc4,0xd2,0xd2,0x92,0xb4,0xb7,0xb7,0xf7,0x36,0x97,0xcb,0x4d,0x3d,0x29,0x8,0x0,0xe0,0x42,0xa1,0xd0,0x71,0xb5,0xc4,0xdf,0xb6,0xc5,0x93,0xe,0x4a,0x0,0x20,0xa9,0x54,0xea,0x37,0xb7,0xdb,0x5d,0xa8,0xa6,0x78,0x39,0x10,0x6b,0x6b,0x6b,0xf1,0x64,0x32,0xb9,0x5a,0x55,0x10,0x0,0xc0,0x6d,0x6c,0x6c,0x9c,0x57,0xbb,0xfa,0x25,0x40,0x14,0x3,0x81,0x40,0x34,0x93,0xc9,0x2c,0x57,0x1c,0x4,0x0,0x90,0x58,0x2c,0xb6,0x5e,0xe9,0xc1,0x77,0x1f,0x10,0x53,0x53,0x53,0x52,0xc5,0x83,0x14,0x0,0x70,0x7e,0xbf,0x5f,0xd0,0x42,0xf5,0x95,0x40,0xf8,0x7c,0xbe,0xcb,0xa3,0xa3,0xa3,0x3f,0x1e,0xbd,0x1b,0x0,0x80,0x1c,0x1f,0x1f,0x87,0xb4,0x56,0xfd,0xaa,0x5,0x29,0x51,0x14,0xbf,0xf5,0xf9,0x7c,0x97,0x5a,0xad,0xbe,0x12,0x88,0xf5,0xf5,0xf5,0xd8,0x83,0x83,0x54,0xb5,0x42,0x8f,0x66,0x83,0x94,0xd6,0xbd,0x5f,0xce,0x7c,0x38,0x3c,0x3c,0xfc,0xb3,0x50,0x28,0xb8,0xcb,0x2,0x1,0x0,0xdc,0xf4,0xf4,0xf4,0xfe,0x73,0x15,0x2f,0x17,0xa4,0x22,0x91,0x48,0x50,0xb5,0x2d,0x0,0x80,0x9b,0x99,0x99,0x79,0xfb,0xdc,0x1,0xc8,0x5,0xa9,0x44,0x22,0xf1,0xfb,0x9d,0x10,0x0,0x80,0x9b,0x9d,0x9d,0xd,0xea,0x5,0xc0,0xad,0xfd,0x43,0x1a,0x0,0xb8,0xdb,0x9a,0xa9,0x8f,0xb6,0xa4,0x46,0xa3,0xa4,0xb7,0xd5,0x37,0xcf,0xf3,0x68,0x75,0x75,0xf5,0x4c,0xee,0x99,0x1c,0x80,0x9c,0x1e,0xf7,0xff,0x16,0x8b,0x45,0x50,0x5,0xa0,0xb7,0xb7,0xb7,0x85,0x10,0xa2,0x2b,0xf1,0x84,0x10,0xd4,0xdf,0xdf,0x6f,0x57,0x3,0x80,0x37,0x18,0xc,0x5,0x3d,0x2,0xa0,0x69,0x3a,0x8b,0x10,0xe2,0x4b,0x2,0xc0,0x18,0xf3,0xc1,0x60,0x70,0x47,0x8f,0x16,0x38,0x3a,0x3a,0x5a,0x93,0x5b,0xc3,0x7f,0x64,0x81,0xba,0xba,0x3a,0x49,0x8f,0x0,0x1a,0x1a,0x1a,0xd4,0xcd,0x0,0x93,0xc9,0xa4,0xcb,0x21,0xe8,0x74,0x3a,0xd5,0x1,0xa0,0x69,0x5a,0x77,0x1d,0x80,0x31,0x2e,0x38,0x9d,0x4e,0xb1,0x66,0x1,0x30,0xc,0x23,0x28,0x3d,0x93,0x9b,0x1,0xb9,0x9a,0x6,0x60,0x36,0x9b,0x75,0xd7,0x1,0x4a,0x21,0xa8,0x26,0x0,0x94,0xa,0x41,0xb2,0x0,0x18,0x86,0xc9,0xe9,0xd,0x80,0x52,0x8,0x92,0x5,0x60,0xb1,0x58,0x74,0x67,0x1,0xa5,0x10,0xa4,0x4,0x40,0x77,0x43,0xd0,0xe1,0x70,0xa8,0x9f,0x1,0x14,0x45,0x1,0x45,0x51,0x79,0x3d,0x1,0x68,0x6e,0x6e,0x4e,0xaa,0x6,0x80,0x10,0x42,0x6,0x83,0x41,0x37,0x36,0x28,0x15,0x82,0x6a,0x2,0x0,0x4d,0xd3,0xa9,0x52,0xcf,0x95,0x0,0xe8,0x66,0xe,0x98,0xcd,0x66,0xa1,0x6c,0x0,0x7a,0x5a,0x8b,0x59,0x2c,0x96,0x64,0xcd,0x2,0xb8,0x2b,0x4,0xe9,0xde,0x2,0x77,0x85,0xa0,0x9a,0xb0,0x40,0xa9,0x10,0xa4,0x8,0xc0,0x64,0x32,0xe9,0x6,0x40,0xa9,0x10,0x54,0xaa,0x3,0x74,0xf3,0x16,0x70,0xb9,0x5c,0xe5,0x3,0xe8,0xe9,0xe9,0x69,0xd5,0xc3,0x66,0x18,0x63,0x5c,0x68,0x6a,0x6a,0x12,0xcb,0x5,0xa0,0x9b,0xd5,0x38,0x4d,0xd3,0x29,0x8a,0xa2,0xa0,0x2c,0x0,0x18,0x63,0x3e,0x14,0xa,0xfd,0x55,0xb,0x21,0x48,0xd1,0x2,0x7a,0x59,0x8d,0xdf,0x1b,0x80,0x1e,0x56,0xe3,0x84,0x10,0x34,0x30,0x30,0x60,0xbb,0xeb,0x77,0x46,0x5,0xef,0x48,0xcf,0x4d,0xec,0x8d,0x99,0x5,0xf5,0xf5,0xf5,0xef,0x46,0x47,0x47,0xb,0x2e,0x97,0xeb,0xbc,0x54,0x8,0x52,0x4,0xc0,0x30,0x8c,0xf4,0x5c,0x4,0x9b,0x4c,0xa6,0xf4,0xf8,0xf8,0xb8,0xc8,0xb2,0x6c,0x32,0x9d,0x4e,0xff,0xd4,0xdd,0xdd,0x7d,0x66,0x34,0x1a,0x8b,0xd7,0x3,0xfd,0xae,0x5b,0x29,0xb2,0x57,0x66,0xb6,0xb6,0xb6,0xde,0xc4,0xe3,0xf1,0x6f,0xae,0xaf,0xc1,0x28,0x5d,0x85,0x79,0x2,0xc1,0x60,0xb5,0x5a,0xa3,0xa3,0xa3,0xa3,0x45,0xab,0xd5,0x9a,0x2a,0x16,0x8b,0x8b,0x6d,0x6d,0x6d,0xef,0xd5,0x8a,0x55,0xd,0x20,0x91,0x48,0xbc,0x3e,0x38,0x38,0xf8,0xda,0x6e,0xb7,0xf7,0x5f,0x5c,0x5c,0xd4,0x7b,0xbd,0xde,0xbc,0x20,0x8,0xcd,0x85,0x42,0x81,0xfe,0xf0,0xae,0xac,0x10,0x98,0x9b,0xd5,0xc5,0x18,0x17,0x59,0x96,0x3d,0x1d,0x19,0x19,0x1,0x96,0x65,0x5,0x8a,0xa2,0x7e,0x6c,0x69,0x69,0x49,0x3d,0x44,0xb0,0x2a,0x0,0x1f,0xcc,0x74,0x75,0x41,0xea,0xfa,0x7b,0x32,0x99,0x64,0x76,0x77,0x77,0x5d,0xe,0x87,0xa3,0x5f,0x14,0xc5,0x57,0x57,0x60,0x5a,0x8b,0xc5,0xa2,0xf1,0xbe,0x50,0x6e,0xa,0x66,0x18,0x26,0x31,0x36,0x36,0x96,0x65,0x59,0x36,0x29,0x49,0x92,0xb7,0xbd,0xbd,0xfd,0x9f,0x72,0xda,0xf9,0xd1,0x1,0xa8,0x1,0x93,0xcf,0xe7,0xa9,0x93,0x93,0x13,0x1b,0x4d,0xd3,0x9f,0xb,0x82,0x60,0xf5,0x7a,0xbd,0xd9,0x54,0x2a,0xe5,0xcc,0x64,0x32,0xe,0xb9,0x6e,0xb9,0x16,0x8c,0x31,0x2e,0xda,0x6c,0xb6,0xc8,0xd0,0xd0,0x10,0x65,0xb3,0xd9,0x92,0x95,0xa8,0x6e,0xc5,0x0,0xa8,0xe9,0x96,0x68,0x34,0x6a,0xdd,0xdf,0xdf,0x6f,0x76,0xb9,0x5c,0x9f,0x89,0xa2,0x58,0xbf,0xb8,0xb8,0x8,0x26,0x93,0x29,0x3b,0x3c,0x3c,0x8c,0xed,0x76,0x7b,0xd2,0x68,0x34,0xfe,0xd0,0xd8,0xd8,0x98,0xae,0xb6,0xe0,0x8a,0x1,0x50,0xb,0xe6,0xa9,0x5,0xbf,0x9c,0x97,0xf3,0xff,0xf3,0x2f,0x6a,0x82,0x7f,0xf6,0x4e,0xca,0x1b,0xf5,0x0,0x0,0x0,0x0,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82 +}; + + +class TestPhysics2DMainLoop : public MainLoop { + + OBJ_TYPE( TestPhysics2DMainLoop, MainLoop ); + + + RID circle_img; + RID circle_shape; + RID space; + RID canvas; + RID ray; + RID ray_query; + Matrix32 view_xform; + + Vector2 ray_from,ray_to; + + + struct BodyShapeData { + + RID image; + RID shape; + }; + + + BodyShapeData body_shape_data[6]; + + + void _create_body_shape_data() { + VisualServer *vs = VisualServer::get_singleton(); + Physics2DServer *ps = Physics2DServer::get_singleton(); + + // SEGMENT + + { + + DVector pixels; + pixels.resize(32*2*2); + for(int i=0;i<2;i++) { + + for(int j=0;j<32;j++) { + + pixels.set(i*32*2+j*2+0,(j==0)?255:0); + pixels.set(i*32*2+j*2+1,255); + } + } + + Image image(32,2,0,Image::FORMAT_GRAYSCALE_ALPHA,pixels); + + body_shape_data[Physics2DServer::SHAPE_SEGMENT].image=vs->texture_create_from_image(image); + + RID segment_shape = ps->shape_create(Physics2DServer::SHAPE_SEGMENT); + Rect2 sg(Point2(-16,0),Point2(16,0)); + ps->shape_set_data(segment_shape,sg); + + body_shape_data[Physics2DServer::SHAPE_SEGMENT].shape = segment_shape; + + } + // CIRCLE + + { + + DVector pixels; + pixels.resize(32*32*2); + for(int i=0;i<32;i++) { + + for(int j=0;j<32;j++) { + + bool black=Vector2(i-16,j-16).length_squared() < 16*16; + + pixels.set(i*32*2+j*2+0,(i==16 || j==16)?255:0); + pixels.set(i*32*2+j*2+1,black?255:0); + } + } + + Image image(32,32,0,Image::FORMAT_GRAYSCALE_ALPHA,pixels); + + body_shape_data[Physics2DServer::SHAPE_CIRCLE].image=vs->texture_create_from_image(image); + + RID circle_shape = ps->shape_create(Physics2DServer::SHAPE_CIRCLE); + ps->shape_set_data(circle_shape,16); + + body_shape_data[Physics2DServer::SHAPE_CIRCLE].shape = circle_shape; + + } + + // BOX + + { + + DVector pixels; + pixels.resize(32*32*2); + for(int i=0;i<32;i++) { + + for(int j=0;j<32;j++) { + + bool black=i>0 && i<31 && j>0 && j<31; + + pixels.set(i*32*2+j*2+0,black?0:255); + pixels.set(i*32*2+j*2+1,255); + } + } + + Image image(32,32,0,Image::FORMAT_GRAYSCALE_ALPHA,pixels); + + body_shape_data[Physics2DServer::SHAPE_RECTANGLE].image=vs->texture_create_from_image(image); + + RID rectangle_shape = ps->shape_create(Physics2DServer::SHAPE_RECTANGLE); + ps->shape_set_data(rectangle_shape,Vector2(16,16)); + + body_shape_data[Physics2DServer::SHAPE_RECTANGLE].shape = rectangle_shape; + + } + + + // CAPSULE + + { + + DVector pixels; + pixels.resize(32*64*2); + for(int i=0;i<64;i++) { + + for(int j=0;j<32;j++) { + + + int si = i>48 ? i - 32 : (i<16 ? i : 16); + bool black=Vector2(si-16,j-16).length_squared() < 16*16; + + pixels.set(i*32*2+j*2+0,(i==16 || j==16 || i==48)?255:0); + pixels.set(i*32*2+j*2+1,black?255:0); + + } + } + + Image image(32,64,0,Image::FORMAT_GRAYSCALE_ALPHA,pixels); + + body_shape_data[Physics2DServer::SHAPE_CAPSULE].image=vs->texture_create_from_image(image); + + RID capsule_shape = ps->shape_create(Physics2DServer::SHAPE_CAPSULE); + ps->shape_set_data(capsule_shape,Vector2(16,32)); + + body_shape_data[Physics2DServer::SHAPE_CAPSULE].shape = capsule_shape; + + } + + // CONVEX + + { + + + Image image(convex_png); + + body_shape_data[Physics2DServer::SHAPE_CONVEX_POLYGON].image=vs->texture_create_from_image(image); + + RID convex_polygon_shape = ps->shape_create(Physics2DServer::SHAPE_CONVEX_POLYGON); + + DVector arr; + Point2 sb(32,32); + arr.push_back(Point2(20,3)-sb); + arr.push_back(Point2(58,23)-sb); + arr.push_back(Point2(55,54)-sb); + arr.push_back(Point2(27,60)-sb); + arr.push_back(Point2(5,56)-sb); + arr.push_back(Point2(4,20)-sb); + arr.push_back(Point2(11,7)-sb); + ps->shape_set_data(convex_polygon_shape,arr); + + body_shape_data[Physics2DServer::SHAPE_CONVEX_POLYGON].shape = convex_polygon_shape; + + } + + + } + + + + + void _do_ray_query() { + +// Physics2DServer *ps = Physics2DServer::get_singleton(); + // ps->query_intersection_segment(ray_query,ray_from,ray_to); + + } + +protected: + + + void input_event(const InputEvent& p_event) { + + if (p_event.type==InputEvent::MOUSE_BUTTON) { + + const InputEventMouseButton &mb=p_event.mouse_button; + + if (mb.pressed) { + + Point2 p( mb.x, mb.y ); + + if (mb.button_index==1) { + ray_to=p; + _do_ray_query(); + } else if (mb.button_index==2) { + ray_from=p; + _do_ray_query(); + } + + } + } + if (p_event.type==InputEvent::MOUSE_MOTION) { + + const InputEventMouseMotion &mm=p_event.mouse_motion; + + Point2 p( mm.x, mm.y ); + + if (mm.button_mask&BUTTON_MASK_LEFT) { + ray_to=p; + _do_ray_query(); + } else if (mm.button_mask&BUTTON_MASK_RIGHT) { + ray_from=p; + _do_ray_query(); + } + } + } + + RID _add_body(Physics2DServer::ShapeType p_shape, const Matrix32& p_xform) { + + VisualServer *vs = VisualServer::get_singleton(); + Physics2DServer *ps = Physics2DServer::get_singleton(); + + RID body = ps->body_create(); + ps->body_add_shape(body,body_shape_data[p_shape].shape); + ps->body_set_space(body,space); + ps->body_set_state(body,Physics2DServer::BODY_STATE_TRANSFORM,p_xform); + +// print_line("add body with xform: "+p_xform); + RID sprite = vs->canvas_item_create(); + vs->canvas_item_set_parent(sprite,canvas); + vs->canvas_item_set_transform(sprite,p_xform); + Size2 imgsize( vs->texture_get_width(body_shape_data[p_shape].image),vs->texture_get_height(body_shape_data[p_shape].image) ); + vs->canvas_item_add_texture_rect(sprite,Rect2(-imgsize/2.0,imgsize),body_shape_data[p_shape].image); + + ps->body_set_force_integration_callback(body,this,"_body_moved",sprite); +// RID q = ps->query_create(this,"_body_moved",sprite); +// ps->query_body_state(q,body); + + return body; + } + + void _add_plane(const Vector2& p_normal, real_t p_d) { + + Physics2DServer *ps = Physics2DServer::get_singleton(); + + Array arr; + arr.push_back(p_normal); + arr.push_back(p_d); + + RID plane = ps->shape_create(Physics2DServer::SHAPE_LINE); + ps->shape_set_data(plane,arr); + + RID plane_body = ps->body_create(Physics2DServer::BODY_MODE_STATIC); + ps->body_set_space(plane_body,space); + ps->body_add_shape(plane_body,plane); + + } + + void _add_concave(const Vector& p_points,const Matrix32& p_xform=Matrix32()) { + + Physics2DServer *ps = Physics2DServer::get_singleton(); + VisualServer *vs = VisualServer::get_singleton(); + + RID concave = ps->shape_create(Physics2DServer::SHAPE_CONCAVE_POLYGON); + ps->shape_set_data(concave,p_points); + RID body = ps->body_create(Physics2DServer::BODY_MODE_STATIC); + ps->body_set_space(body,space); + ps->body_add_shape(body,concave); + ps->body_set_state(body,Physics2DServer::BODY_STATE_TRANSFORM,p_xform); + + + RID sprite = vs->canvas_item_create(); + vs->canvas_item_set_parent(sprite,canvas); + vs->canvas_item_set_transform(sprite,p_xform); + for(int i=0;icanvas_item_add_line(sprite,p_points[i],p_points[i+1],Color(0,0,0),2); + } + + } + + void _body_moved(Object *p_state,RID p_sprite) { + Physics2DDirectBodyState *state=(Physics2DDirectBodyState *)p_state; + VisualServer::get_singleton()->canvas_item_set_transform(p_sprite,state->get_transform()); + } + + void _ray_query_callback(const RID& p_rid, ObjectID p_id, int p_shape, const Vector2& p_point, const Vector2& p_normal) { + + + Vector2 ray_end; + + if (p_rid.is_valid()) { + ray_end=p_point; + } else { + ray_end=ray_to; + } + + VisualServer *vs = VisualServer::get_singleton(); + + vs->canvas_item_clear(ray); + vs->canvas_item_add_line(ray,ray_from,ray_end,p_rid.is_valid()?Color(0,1,0.4):Color(1,0.4,0),2); + if (p_rid.is_valid()) + vs->canvas_item_add_line(ray,ray_end,ray_end+p_normal*20,p_rid.is_valid()?Color(0,1,0.4):Color(1,0.4,0),2); + + } + + static void _bind_methods() { + + + ObjectTypeDB::bind_method(_MD("_body_moved"),&TestPhysics2DMainLoop::_body_moved); + ObjectTypeDB::bind_method(_MD("_ray_query_callback"),&TestPhysics2DMainLoop::_ray_query_callback); + } + + +public: + + + + virtual void init() { + + VisualServer *vs = VisualServer::get_singleton(); + Physics2DServer *ps = Physics2DServer::get_singleton(); + + + + space=ps->space_create(); + ps->space_set_active(space,true); + ps->set_active(true); + ps->area_set_param(space,Physics2DServer::AREA_PARAM_GRAVITY_VECTOR,Vector2(0,1)); + ps->area_set_param(space,Physics2DServer::AREA_PARAM_GRAVITY,98); + + { + + RID vp = vs->viewport_create(); + canvas = vs->canvas_create(); + vs->viewport_attach_canvas(vp,canvas); + vs->viewport_attach_to_screen(vp); + Matrix32 smaller; + //smaller.scale(Vector2(0.6,0.6)); + //smaller.elements[2]=Vector2(100,0); + + //view_xform = smaller; + vs->viewport_set_canvas_transform(vp,canvas,view_xform); + + } + + ray = vs->canvas_item_create(); + vs->canvas_item_set_parent(ray,canvas); + //ray_query = ps->query_create(this,"_ray_query_callback",Variant()); + //ps->query_intersection(ray_query,space); + + _create_body_shape_data(); + + for(int i=0;i<32;i++) { + + Physics2DServer::ShapeType types[4]={ + Physics2DServer::SHAPE_CIRCLE, + Physics2DServer::SHAPE_CAPSULE, + Physics2DServer::SHAPE_RECTANGLE, + Physics2DServer::SHAPE_CONVEX_POLYGON, + + }; + + Physics2DServer::ShapeType type = types[i%4]; +// type=Physics2DServer::SHAPE_SEGMENT; + RID b = _add_body(type,Matrix32(i*0.8,Point2(152+i*40,100-40*i))); + //if (i==0) + // ps->body_set_mode(b,Physics2DServer::BODY_MODE_STATIC); + } + + //RID b= _add_body(Physics2DServer::SHAPE_CIRCLE,Matrix32(0,Point2(101,140))); + //ps->body_set_mode(b,Physics2DServer::BODY_MODE_STATIC); + + Point2 prev; + + Vector parr; + for(int i=0;i<30;i++) { + + Point2 p(i*60,Math::randf() * 70+340); + if (i>0) { + parr.push_back(prev); + parr.push_back(p); + } + prev=p; + } + + _add_concave(parr); + //_add_plane(Vector2(0.0,-1).normalized(),-300); + //_add_plane(Vector2(1,0).normalized(),50); + //_add_plane(Vector2(-1,0).normalized(),-600); + + } + + virtual bool idle(float p_time) { + + + return false; + } + virtual void finish() { + + + } + + + TestPhysics2DMainLoop() {} + +}; + + +namespace TestPhysics2D { + + +MainLoop* test() { + + + return memnew( TestPhysics2DMainLoop ); +} + + +} diff --git a/bin/tests/test_physics_2d.h b/bin/tests/test_physics_2d.h new file mode 100644 index 00000000000..184c6973a80 --- /dev/null +++ b/bin/tests/test_physics_2d.h @@ -0,0 +1,41 @@ +/*************************************************************************/ +/* test_physics_2d.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 TEST_PHYSICS_2D_H +#define TEST_PHYSICS_2D_H + + +#include "os/main_loop.h" + +namespace TestPhysics2D { + +MainLoop* test(); + +} + +#endif // TEST_PHYSICS_2D_H diff --git a/bin/tests/test_python.cpp b/bin/tests/test_python.cpp new file mode 100644 index 00000000000..0b2bac492ce --- /dev/null +++ b/bin/tests/test_python.cpp @@ -0,0 +1,56 @@ +/*************************************************************************/ +/* test_python.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 "test_python.h" + +#ifdef PYTHON_ENABLED + +#include "Python.h" +#include "print_string.h" + +namespace TestPython { + +void test() { + + print_line("testing python"); + PyRun_SimpleString("import engine\n"); + PyRun_SimpleString("def test(self):\n\tprint(\"noway\")\n"); + PyRun_SimpleString("a=engine.ObjectPtr()\n"); + PyRun_SimpleString("a.noway(22,'hello')\n"); + PyRun_SimpleString("a.normalize()\n"); + PyRun_SimpleString("class Moch(engine.ObjectPtr):\n\tdef mooch(self):\n\t\tprint('muchi')\n"); + PyRun_SimpleString("b=Moch();\n"); + PyRun_SimpleString("b.mooch();\n"); + PyRun_SimpleString("b.meis();\n"); + + +} + +} + +#endif \ No newline at end of file diff --git a/bin/tests/test_python.h b/bin/tests/test_python.h new file mode 100644 index 00000000000..781be1c0cef --- /dev/null +++ b/bin/tests/test_python.h @@ -0,0 +1,43 @@ +/*************************************************************************/ +/* test_python.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 TEST_PYTHON_H +#define TEST_PYTHON_H + +#ifdef PYTHON_ENABLED +/** + @author Juan Linietsky +*/ +namespace TestPython { + +void test(); + +} + +#endif +#endif diff --git a/bin/tests/test_render.cpp b/bin/tests/test_render.cpp new file mode 100644 index 00000000000..b45b356ee3d --- /dev/null +++ b/bin/tests/test_render.cpp @@ -0,0 +1,257 @@ +/*************************************************************************/ +/* test_render.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 "test_render.h" +#include "servers/visual_server.h" +#include "os/main_loop.h" +#include "math_funcs.h" +#include "print_string.h" +#include "os/os.h" +#include "quick_hull.h" +#define OBJECT_COUNT 50 + +namespace TestRender { + + +class TestMainLoop : public MainLoop { + + + RID test_cube; + RID instance; + RID camera; + RID viewport; + RID light; + RID scenario; + + struct InstanceInfo { + + RID instance; + Transform base; + Vector3 rot_axis; + }; + + List instances; + + float ofs; + bool quit; +public: + virtual void input_event(const InputEvent& p_event) { + + + } + + virtual void init() { + + + print_line("INITIALIZING TEST RENDER"); + VisualServer *vs=VisualServer::get_singleton(); + test_cube = vs->get_test_cube(); + scenario = vs->scenario_create(); + + + + + + + Vector vts; + +/* + DVector sp = Geometry::build_sphere_planes(2,5,5); + Geometry::MeshData md2 = Geometry::build_convex_mesh(sp); + vts=md2.vertices; +*/ +/* + + static const int s = 20; + for(int i=0;imesh_create(); + vs->mesh_add_surface_from_mesh_data(test_cube,md); + //vs->scenario_set_debug(scenario,VS::SCENARIO_DEBUG_WIREFRAME); + + /* + RID sm = vs->shader_create(); + //vs->shader_set_fragment_code(sm,"OUT_ALPHA=mod(TIME,1);"); + //vs->shader_set_vertex_code(sm,"OUT_VERTEX=IN_VERTEX*mod(TIME,1);"); + vs->shader_set_fragment_code(sm,"OUT_DIFFUSE=vec3(1,0,1);OUT_GLOW=abs(sin(TIME));"); + RID tcmat = vs->mesh_surface_get_material(test_cube,0); + vs->material_set_shader(tcmat,sm); + */ + + + List cmdline = OS::get_singleton()->get_cmdline_args(); + int object_count = OBJECT_COUNT; + if (cmdline.size() > 0 && cmdline[cmdline.size()-1].to_int()) { + object_count = cmdline[cmdline.size()-1].to_int(); + }; + + for (int i=0;iinstance_create2( test_cube, scenario ); + + + ii.base.translate( Math::random(-20,20), Math::random(-20,20),Math::random(-20,18) ); + ii.base.rotate( Vector3(0,1,0), Math::randf() * Math_PI ); + ii.base.rotate( Vector3(1,0,0), Math::randf() * Math_PI ); + vs->instance_set_transform( ii.instance, ii.base ); + + ii.rot_axis = Vector3( Math::random(-1,1), Math::random(-1,1), Math::random(-1,1) ).normalized(); + + instances.push_back(ii); + + } + + camera = vs->camera_create(); + +// vs->camera_set_perspective( camera, 60.0,0.1, 100.0 ); + + viewport = vs->viewport_create(); + vs->viewport_attach_to_screen(viewport); + vs->viewport_attach_camera( viewport, camera ); + vs->viewport_set_scenario( viewport, scenario ); + vs->camera_set_transform(camera, Transform( Matrix3(), Vector3(0,3,30 ) ) ); + vs->camera_set_perspective( camera, 60, 0.1, 1000); + + + /* + RID lightaux = vs->light_create( VisualServer::LIGHT_OMNI ); + vs->light_set_var( lightaux, VisualServer::LIGHT_VAR_RADIUS, 80 ); + vs->light_set_var( lightaux, VisualServer::LIGHT_VAR_ATTENUATION, 1 ); + vs->light_set_var( lightaux, VisualServer::LIGHT_VAR_ENERGY, 1.5 ); + light = vs->instance_create( lightaux ); + */ + RID lightaux; + + //* + lightaux = vs->light_create( VisualServer::LIGHT_DIRECTIONAL ); + vs->light_set_color( lightaux, VisualServer::LIGHT_COLOR_AMBIENT, Color(0.0,0.0,0.0) ); + vs->light_set_color( lightaux, VisualServer::LIGHT_COLOR_DIFFUSE, Color(1.0,1.0,1.0) ); + //vs->light_set_shadow( lightaux, true ); + light = vs->instance_create2( lightaux, scenario ); + Transform lla; + //lla.set_look_at(Vector3(),Vector3(1,-1,1),Vector3(0,1,0)); + lla.set_look_at(Vector3(),Vector3(-0.000000,-0.836026,-0.548690),Vector3(0,1,0)); + + vs->instance_set_transform( light, lla ); + // */ + + //* + lightaux = vs->light_create( VisualServer::LIGHT_OMNI ); + vs->light_set_color( lightaux, VisualServer::LIGHT_COLOR_AMBIENT, Color(0.0,0.0,1.0) ); + vs->light_set_color( lightaux, VisualServer::LIGHT_COLOR_DIFFUSE, Color(1.0,1.0,0.0) ); + vs->light_set_param( lightaux, VisualServer::LIGHT_PARAM_RADIUS, 4 ); + vs->light_set_param( lightaux, VisualServer::LIGHT_PARAM_ENERGY, 8 ); + //vs->light_set_shadow( lightaux, true ); + //light = vs->instance_create( lightaux ); + // */ + + ofs=0; + quit=false; + } + virtual bool iteration(float p_time) { + + VisualServer *vs=VisualServer::get_singleton(); + //Transform t; + //t.rotate(Vector3(0, 1, 0), ofs); + //t.translate(Vector3(0,0,20 )); + //vs->camera_set_transform(camera, t); + + ofs+=p_time*0.05; + + //return quit; + + for(List::Element *E=instances.front();E;E=E->next()) { + + Transform pre( Matrix3(E->get().rot_axis, ofs), Vector3() ); + vs->instance_set_transform( E->get().instance, pre * E->get().base ); + /* + if( !E->next() ) { + + vs->free( E->get().instance ); + instances.erase(E ); + }*/ + } + + return quit; + } + + virtual bool idle(float p_time) { + return quit; + } + + + virtual void finish() { + + } + +}; + + +MainLoop* test() { + + return memnew( TestMainLoop ); + +} + +} diff --git a/bin/tests/test_render.h b/bin/tests/test_render.h new file mode 100644 index 00000000000..d5d140a090a --- /dev/null +++ b/bin/tests/test_render.h @@ -0,0 +1,44 @@ +/*************************************************************************/ +/* test_render.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 TEST_RENDER_H +#define TEST_RENDER_H + +/** + @author Juan Linietsky +*/ + +#include "os/main_loop.h" + +namespace TestRender { + +MainLoop* test(); + +} + +#endif diff --git a/bin/tests/test_shader_lang.cpp b/bin/tests/test_shader_lang.cpp new file mode 100644 index 00000000000..fbd8be7fb05 --- /dev/null +++ b/bin/tests/test_shader_lang.cpp @@ -0,0 +1,339 @@ +/*************************************************************************/ +/* test_shader_lang.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 "test_shader_lang.h" + + +#include "os/main_loop.h" +#include "os/os.h" +#include "os/file_access.h" + +#include "scene/gui/control.h" +#include "scene/gui/text_edit.h" +#include "print_string.h" +#include "servers/visual/shader_language.h" +#include "drivers/gles2/shader_compiler_gles2.h" + + +typedef ShaderLanguage SL; + +namespace TestShaderLang { + + +static String _mktab(int p_level) { + + String tb; + for(int i=0;i="; + case SL::OP_CMP_LESS: return "<"; + case SL::OP_CMP_GREATER: return ">"; + case SL::OP_CMP_OR: return "||"; + case SL::OP_CMP_AND: return "&&"; + default: return ""; + } + + return ""; +} + +static String dump_node_code(SL::Node *p_node,int p_level) { + + String code; + + switch(p_node->type) { + + case SL::Node::TYPE_PROGRAM: { + + SL::ProgramNode *pnode=(SL::ProgramNode*)p_node; + + for(Map::Element *E=pnode->uniforms.front();E;E=E->next()) { + + String ucode="uniform "; + ucode+=_typestr(E->get().type)+"="+String(E->get().default_value)+"\n"; + code+=ucode; + + } + + for(int i=0;ifunctions.size();i++) { + + SL::FunctionNode *fnode=pnode->functions[i].function; + + String header; + header=_typestr(fnode->return_type)+" "+fnode->name+"("; + for(int i=0;iarguments.size();i++) { + + if (i>0) + header+=", "; + header+=_typestr(fnode->arguments[i].type)+" "+fnode->arguments[i].name; + } + + header+=") {\n"; + code+=header; + code+=dump_node_code(fnode->body,p_level+1); + code+="}\n"; + } + + code+=dump_node_code(pnode->body,p_level); + } break; + case SL::Node::TYPE_FUNCTION: { + + } break; + case SL::Node::TYPE_BLOCK: { + SL::BlockNode *bnode=(SL::BlockNode*)p_node; + + //variables + for(Map::Element *E=bnode->variables.front();E;E=E->next()) { + + code+=_mktab(p_level)+_typestr(E->value())+" "+E->key()+";\n"; + } + + for(int i=0;istatements.size();i++) { + + code+=_mktab(p_level)+dump_node_code(bnode->statements[i],p_level)+";\n"; + } + + + } break; + case SL::Node::TYPE_VARIABLE: { + SL::VariableNode *vnode=(SL::VariableNode*)p_node; + code=vnode->name; + + } break; + case SL::Node::TYPE_CONSTANT: { + SL::ConstantNode *cnode=(SL::ConstantNode*)p_node; + switch(cnode->datatype) { + + + case SL::TYPE_BOOL: code=cnode->value.operator bool()?"true":"false"; break; + case SL::TYPE_FLOAT: code=cnode->value; break; + case SL::TYPE_VEC2: { Vector2 v = cnode->value; code="vec2("+rtos(v.x)+", "+rtos(v.y)+")"; } break; + case SL::TYPE_VEC3: { Vector3 v = cnode->value; code="vec3("+rtos(v.x)+", "+rtos(v.y)+", "+rtos(v.z)+")"; } break; + case SL::TYPE_VEC4: { Plane v = cnode->value; code="vec4("+rtos(v.normal.x)+", "+rtos(v.normal.y)+", "+rtos(v.normal.z)+", "+rtos(v.d)+")"; } break; + case SL::TYPE_MAT3: { Matrix3 x = cnode->value; code="mat3( vec3("+rtos(x.get_axis(0).x)+", "+rtos(x.get_axis(0).y)+", "+rtos(x.get_axis(0).z)+"), vec3("+rtos(x.get_axis(1).x)+", "+rtos(x.get_axis(1).y)+", "+rtos(x.get_axis(1).z)+"), vec3("+rtos(x.get_axis(2).x)+", "+rtos(x.get_axis(2).y)+", "+rtos(x.get_axis(2).z)+"))"; } break; + case SL::TYPE_MAT4: { Transform x = cnode->value; code="mat4( vec3("+rtos(x.basis.get_axis(0).x)+", "+rtos(x.basis.get_axis(0).y)+", "+rtos(x.basis.get_axis(0).z)+"), vec3("+rtos(x.basis.get_axis(1).x)+", "+rtos(x.basis.get_axis(1).y)+", "+rtos(x.basis.get_axis(1).z)+"), vec3("+rtos(x.basis.get_axis(2).x)+", "+rtos(x.basis.get_axis(2).y)+", "+rtos(x.basis.get_axis(2).z)+"), vec3("+rtos(x.origin.x)+", "+rtos(x.origin.y)+", "+rtos(x.origin.z)+"))"; } break; + default: code="value.get_type())+" ("+itos(cnode->datatype)+">"; + } + + } break; + case SL::Node::TYPE_OPERATOR: { + SL::OperatorNode *onode=(SL::OperatorNode*)p_node; + + + switch(onode->op) { + + case SL::OP_ASSIGN: + case SL::OP_ASSIGN_ADD: + case SL::OP_ASSIGN_SUB: + case SL::OP_ASSIGN_MUL: + case SL::OP_ASSIGN_DIV: + code=dump_node_code(onode->arguments[0],p_level)+_opstr(onode->op)+dump_node_code(onode->arguments[1],p_level); + break; + + case SL::OP_ADD: + case SL::OP_SUB: + case SL::OP_MUL: + case SL::OP_DIV: + case SL::OP_CMP_EQ: + case SL::OP_CMP_NEQ: + case SL::OP_CMP_LEQ: + case SL::OP_CMP_GEQ: + case SL::OP_CMP_LESS: + case SL::OP_CMP_GREATER: + case SL::OP_CMP_OR: + case SL::OP_CMP_AND: + + code="("+dump_node_code(onode->arguments[0],p_level)+_opstr(onode->op)+dump_node_code(onode->arguments[1],p_level)+")"; + break; + case SL::OP_NEG: + case SL::OP_NOT: + code=_opstr(onode->op)+dump_node_code(onode->arguments[0],p_level); + break; + case SL::OP_CALL: + case SL::OP_CONSTRUCT: + code=dump_node_code(onode->arguments[0],p_level)+"("; + for(int i=1;iarguments.size();i++) { + if (i>1) + code+=", "; + code+=dump_node_code(onode->arguments[i],p_level); + } + code+=")"; + break; + default: {} + } + + } break; + case SL::Node::TYPE_CONTROL_FLOW: { + SL::ControlFlowNode *cfnode=(SL::ControlFlowNode*)p_node; + if (cfnode->flow_op==SL::FLOW_OP_IF) { + + code+="if ("+dump_node_code(cfnode->statements[0],p_level)+") {\n"; + code+=dump_node_code(cfnode->statements[1],p_level+1); + if (cfnode->statements.size()==3) { + + code+="} else {\n"; + code+=dump_node_code(cfnode->statements[2],p_level+1); + } + + code+="}\n"; + + } else if (cfnode->flow_op==SL::FLOW_OP_RETURN) { + + if (cfnode->statements.size()) { + code="return "+dump_node_code(cfnode->statements[0],p_level); + } else { + code="return"; + } + } + + } break; + case SL::Node::TYPE_MEMBER: { + SL::MemberNode *mnode=(SL::MemberNode*)p_node; + code=dump_node_code(mnode->owner,p_level)+"."+mnode->name; + + } break; + } + + return code; + +} + +static void recreate_code(void *p_str,SL::ProgramNode *p_program) { + + print_line("recr"); + String *str=(String*)p_str; + + *str=dump_node_code(p_program,0); + + +} + + +MainLoop* test() { + + List cmdlargs = OS::get_singleton()->get_cmdline_args(); + + if (cmdlargs.empty()) { + //try editor! + return NULL; + } + + String test = cmdlargs.back()->get(); + + FileAccess *fa = FileAccess::open(test,FileAccess::READ); + + if (!fa) { + memdelete(fa); + ERR_FAIL_V(NULL); + } + + String code; + + while(true) { + CharType c = fa->get_8(); + if (fa->eof_reached()) + break; + code+=c; + } + + int errline; + int errcol; + String error; + print_line(SL::lex_debug(code)); + Error err = SL::compile(code,ShaderLanguage::SHADER_MATERIAL_FRAGMENT,NULL,NULL,&error,&errline,&errcol); + + if (err) { + + print_line("Error: "+itos(errline)+":"+itos(errcol)+" "+error); + return NULL; + } + + print_line("Compile OK! - pretty printing"); + + String rcode; + err = SL::compile(code,ShaderLanguage::SHADER_MATERIAL_FRAGMENT,recreate_code,&rcode,&error,&errline,&errcol); + + if (!err) { + print_line(rcode); + } + + ShaderCompilerGLES2 comp; + String codeline,globalsline; + SL::VarInfo vi; + vi.name="mongs"; + vi.type=SL::TYPE_VEC3; + + + ShaderCompilerGLES2::Flags fl; + comp.compile(code,ShaderLanguage::SHADER_MATERIAL_FRAGMENT,codeline,globalsline,fl); + + return NULL; +} + +} diff --git a/bin/tests/test_shader_lang.h b/bin/tests/test_shader_lang.h new file mode 100644 index 00000000000..a12b45293d7 --- /dev/null +++ b/bin/tests/test_shader_lang.h @@ -0,0 +1,40 @@ +/*************************************************************************/ +/* test_shader_lang.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 TEST_SHADER_LANG_H +#define TEST_SHADER_LANG_H + +#include "os/main_loop.h" + +namespace TestShaderLang { + +MainLoop* test(); + +} + +#endif // TEST_SHADER_LANG_H diff --git a/bin/tests/test_sound.cpp b/bin/tests/test_sound.cpp new file mode 100644 index 00000000000..40800f6fa5a --- /dev/null +++ b/bin/tests/test_sound.cpp @@ -0,0 +1,95 @@ +/*************************************************************************/ +/* test_sound.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 "test_sound.h" +#include "servers/visual_server.h" +#include "os/main_loop.h" +#include "math_funcs.h" +#include "scene/resources/sample.h" +#include "io/resource_loader.h" +#include "print_string.h" +#include "servers/audio_server.h" +#include "os/os.h" +namespace TestSound { + + +class TestMainLoop : public MainLoop { + + bool quit; + Ref sample; + +public: + virtual void input_event(const InputEvent& p_event) { + + + } + virtual void request_quit() { + + quit=true; + } + + virtual void init() { + + List cmdline = OS::get_singleton()->get_cmdline_args(); + quit=false; + if (cmdline.size()) { + + sample=ResourceLoader::load(cmdline.back()->get()); + ERR_FAIL_COND(sample.is_null()); + print_line("Sample loaded OK"); + } + + RID voice = AudioServer::get_singleton()->voice_create(); + AudioServer::get_singleton()->voice_play( voice, sample->get_rid() ); + + + } + + virtual bool idle(float p_time) { + return false; + } + + + virtual bool iteration(float p_time) { + + return quit; + } + virtual void finish() { + + } + +}; + + +MainLoop* test() { + + return memnew( TestMainLoop ); + +} + +} diff --git a/bin/tests/test_sound.h b/bin/tests/test_sound.h new file mode 100644 index 00000000000..fd0dd7a621c --- /dev/null +++ b/bin/tests/test_sound.h @@ -0,0 +1,40 @@ +/*************************************************************************/ +/* test_sound.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 TEST_SOUND_H +#define TEST_SOUND_H + +#include "os/main_loop.h" + +namespace TestSound { + +MainLoop* test(); + +} + +#endif // TEST_SOUND_H diff --git a/bin/tests/test_string.cpp b/bin/tests/test_string.cpp new file mode 100644 index 00000000000..78fb9a9ddbb --- /dev/null +++ b/bin/tests/test_string.cpp @@ -0,0 +1,546 @@ +/*************************************************************************/ +/* test_string.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 "ustring.h" +#include +//#include "math_funcs.h" +#include +#include "os/os.h" +#include "drivers/trex/regex.h" + +#include "test_string.h" + +namespace TestString { + +bool test_1() { + + OS::get_singleton()->print("\n\nTest 1: Assign from cstr\n"); + + String s = "Hello"; + + OS::get_singleton()->print("\tExpected: Hello\n"); + OS::get_singleton()->print("\tResulted: %ls\n",s.c_str()); + + return (wcscmp(s.c_str(),L"Hello")==0); + +} + +bool test_2() { + + OS::get_singleton()->print("\n\nTest 2: Assign from string (operator=)\n"); + + String s = "Dolly"; + String t = s; + + OS::get_singleton()->print("\tExpected: Dolly\n"); + OS::get_singleton()->print("\tResulted: %ls\n",t.c_str()); + + return (wcscmp(t.c_str(),L"Dolly")==0); + +} + +bool test_3() { + + OS::get_singleton()->print("\n\nTest 3: Assign from c-string (copycon)\n"); + + String s("Sheep"); + String t(s); + + OS::get_singleton()->print("\tExpected: Sheep\n"); + OS::get_singleton()->print("\tResulted: %ls\n",t.c_str()); + + return (wcscmp(t.c_str(),L"Sheep")==0); + +} + +bool test_4() { + + OS::get_singleton()->print("\n\nTest 4: Assign from c-widechar (operator=)\n"); + + String s(L"Give me"); + + OS::get_singleton()->print("\tExpected: Give me\n"); + OS::get_singleton()->print("\tResulted: %ls\n",s.c_str()); + + return (wcscmp(s.c_str(),L"Give me")==0); + +} + +bool test_5() { + + OS::get_singleton()->print("\n\nTest 5: Assign from c-widechar (copycon)\n"); + + String s(L"Wool"); + + OS::get_singleton()->print("\tExpected: Wool\n"); + OS::get_singleton()->print("\tResulted: %ls\n",s.c_str()); + + return (wcscmp(s.c_str(),L"Wool")==0); + +} + +bool test_6() { + + OS::get_singleton()->print("\n\nTest 6: comparisons (equal)\n"); + + + String s="Test Compare"; + + OS::get_singleton()->print("\tComparing to \"Test Compare\"\n"); + + if (! ( s=="Test Compare" ) ) + return false; + + if (! ( s==L"Test Compare" ) ) + return false; + + if (! ( s==String("Test Compare") ) ) + return false; + + return true; + +} + +bool test_7() { + + OS::get_singleton()->print("\n\nTest 7: comparisons (unequal)\n"); + + + String s="Test Compare"; + + OS::get_singleton()->print("\tComparing to \"Test Compare\"\n"); + + if (! ( s!="Peanut" ) ) + return false; + + if (! ( s!=L"Coconut" ) ) + return false; + + if (! ( s!=String("Butter") ) ) + return false; + + return true; + +} + +bool test_8() { + + OS::get_singleton()->print("\n\nTest 8: comparisons (operator<)\n"); + + + String s="Bees"; + + OS::get_singleton()->print("\tComparing to \"Bees\"\n"); + + if ( ! (s < "Elephant") ) + return false; + + if ( s < L"Amber" ) + return false; + + if ( s < String("Beatrix") ) + return false; + + return true; + +} + +bool test_9() { + + OS::get_singleton()->print("\n\nTest 9: Concatenation\n"); + + + String s; + + s+="Have"; + s+=' '; + s+='a'; + s+=String(" "); + s = s + L"Nice"; + s = s + " "; + s = s + String("Day"); + + OS::get_singleton()->print("\tComparing to \"Have a Nice Day\"\n"); + + return (s == "Have a Nice Day"); + +} + +bool test_10() { + + OS::get_singleton()->print("\n\nTest 10: Misc funcs (size/length/empty/etc)\n"); + + if (! String("").empty()) + return false; + + if (String("Mellon").size() != 7) + return false; + + if (String("Oranges").length() != 7) + return false; + + return true; + +} + + +bool test_11() { + + OS::get_singleton()->print("\n\nTest 11: Operator[]\n"); + + String a="Kugar Sane"; + + a[0]='S'; + a[6]='C'; + + if (a != "Sugar Cane") + return false; + + if (a[1]!='u') + return false; + + return true; + +} + +bool test_12() { + + OS::get_singleton()->print("\n\nTest 12: case functions\n"); + + + String a="MoMoNgA"; + + if (a.to_upper() != "MOMONGA") + return false; + + if (a.nocasecmp_to("momonga")!=0) + return false; + + return true; + +} + +bool test_13() { + + OS::get_singleton()->print("\n\nTest 13: UTF8\n"); + + /* how can i embed UTF in here? */ + + static const CharType ustr[] = { 0x304A , 0x360F, 0x3088, 0x3046, 0 }; +// static const wchar_t ustr[] = { 'P', 0xCE, 'p',0xD3, 0 }; + String s=ustr; + + OS::get_singleton()->print("\tUnicode: %ls\n",ustr); + s.parse_utf8( s.utf8().get_data() ); + OS::get_singleton()->print("\tConvert/Parse UTF8: %ls\n",s.c_str()); + + return (s==ustr); + +} + +bool test_14() { + + OS::get_singleton()->print("\n\nTest 14: ASCII\n"); + + String s = L"Primero Leche"; + OS::get_singleton()->print("\tAscii: %s\n",s.ascii().get_data()); + + String t=s.ascii().get_data(); + return (s==t); + +} + +bool test_15() { + + OS::get_singleton()->print("\n\nTest 15: substr\n"); + + String s="Killer Baby"; + OS::get_singleton()->print("\tsubstr(3,4) of \"%ls\" is \"%ls\"\n",s.c_str(),s.substr(3,4).c_str()); + + return (s.substr(3,4)=="ler "); + +} + +bool test_16() { + + OS::get_singleton()->print("\n\nTest 16: find\n"); + + String s="Pretty Woman"; + OS::get_singleton()->print("\tString: %ls\n",s.c_str()); + OS::get_singleton()->print("\t\"tty\" is at %i pos.\n",s.find("tty")); + OS::get_singleton()->print("\t\"Revenge of the Monster Truck\" is at %i pos.\n",s.find("Revenge of the Monster Truck")); + + if (s.find("tty")!=3) + return false; + + if (s.find("Revenge of the Monster Truck")!=-1) + return false; + + return true; + +} + +bool test_17() { + + OS::get_singleton()->print("\n\nTest 17: find no case\n"); + + String s="Pretty Whale"; + OS::get_singleton()->print("\tString: %ls\n",s.c_str()); + OS::get_singleton()->print("\t\"WHA\" is at %i pos.\n",s.findn("WHA")); + OS::get_singleton()->print("\t\"Revenge of the Monster SawFish\" is at %i pos.\n",s.findn("Revenge of the Monster Truck")); + + if (s.findn("WHA")!=7) + return false; + + if (s.findn("Revenge of the Monster SawFish")!=-1) + return false; + + return true; + +} + +bool test_18() { + + OS::get_singleton()->print("\n\nTest 18: find no case\n"); + + String s="Pretty Whale"; + OS::get_singleton()->print("\tString: %ls\n",s.c_str()); + OS::get_singleton()->print("\t\"WHA\" is at %i pos.\n",s.findn("WHA")); + OS::get_singleton()->print("\t\"Revenge of the Monster SawFish\" is at %i pos.\n",s.findn("Revenge of the Monster Truck")); + + if (s.findn("WHA")!=7) + return false; + + if (s.findn("Revenge of the Monster SawFish")!=-1) + return false; + + return true; + +} + +bool test_19() { + + OS::get_singleton()->print("\n\nTest 19: Search & replace\n"); + + String s="Happy Birthday, Anna!"; + OS::get_singleton()->print("\tString: %ls\n",s.c_str()); + + s=s.replace("Birthday","Halloween"); + OS::get_singleton()->print("\tReplaced Birthday/Halloween: %ls.\n",s.c_str()); + + return (s=="Happy Halloween, Anna!"); + +} + +bool test_20() { + + OS::get_singleton()->print("\n\nTest 20: Insertion\n"); + + String s="Who is Frederic?"; + + OS::get_singleton()->print("\tString: %ls\n",s.c_str()); + s=s.insert( s.find("?")," Chopin" ); + OS::get_singleton()->print("\tInserted Chopin: %ls.\n",s.c_str()); + + return (s=="Who is Frederic Chopin?"); + +} + +bool test_21() { + + OS::get_singleton()->print("\n\nTest 21: Number -> String\n"); + + OS::get_singleton()->print("\tPi is %f\n",33.141593); + OS::get_singleton()->print("\tPi String is %ls\n",String::num(3.141593).c_str()); + + return String::num(3.141593)=="3.141593"; + +} + +bool test_22() { + + OS::get_singleton()->print("\n\nTest 22: String -> Int\n"); + + static const char* nums[4]={ "1237461283", "- 22", "0", " - 1123412" }; + static const int num[4]={ 1237461283, -22, 0, -1123412 }; + + for (int i=0;i<4;i++) { + OS::get_singleton()->print("\tString: \"%s\" as Int is %i\n",nums[i],String(nums[i]).to_int()); + + if (String(nums[i]).to_int()!=num[i]) + return false; + } + + return true; + +} + +bool test_23() { + + OS::get_singleton()->print("\n\nTest 23: String -> Float\n"); + + static const char* nums[4]={ "-12348298412.2", "0.05", "2.0002", " -0.0001" }; + static const double num[4]={ -12348298412.2, 0.05, 2.0002, -0.0001 }; + + for (int i=0;i<4;i++) { + OS::get_singleton()->print("\tString: \"%s\" as Float is %f\n",nums[i],String(nums[i]).to_double()); + + if ( ABS(String(nums[i]).to_double()-num[i])>0.00001) + return false; + } + + return true; + +} + + +bool test_24() { + + OS::get_singleton()->print("\n\nTest 24: Slicing\n"); + + String s="Mars,Jupiter,Saturn,Uranus"; + + const char*slices[4]={"Mars","Jupiter","Saturn","Uranus"}; + + OS::get_singleton()->print("\tSlicing \"%ls\" by \"%s\"..\n",s.c_str(),","); + + for (int i=0;iprint("\t\t%i- %ls\n",i+1,s.get_slice(",",i).c_str()); + + + if (s.get_slice(",",i)!=slices[i]) + return false; + } + + return true; + +} + +bool test_25() { + + OS::get_singleton()->print("\n\nTest 25: Erasing\n"); + + String s="Josephine is such a cute girl!"; + + OS::get_singleton()->print("\tString: %ls\n",s.c_str()); + OS::get_singleton()->print("\tRemoving \"cute\"\n"); + + s.erase(s.find("cute "),String("cute ").length()); + OS::get_singleton()->print("\tResult: %ls\n",s.c_str()); + + + return (s=="Josephine is such a girl!"); + +} + +bool test_26() { + + OS::get_singleton()->print("\n\nTest 26: RegEx\n"); + RegEx regexp("(.*):(.*)"); + List captures; + + bool match = regexp.match("name:password", &captures); + printf("\tmatch: %s\n", match?"true":"false"); + + printf("\t%i captures:\n", captures.size()); + List::Element *I = captures.front(); + while (I) { + + printf("%ls\n", I->get().c_str()); + + I = I->next(); + }; + return captures.size(); +}; + +typedef bool (*TestFunc)(void); + +TestFunc test_funcs[] = { + + test_1, + test_2, + test_3, + test_4, + test_5, + test_6, + test_7, + test_8, + test_9, + test_10, + test_11, + test_12, + test_13, + test_14, + test_15, + test_16, + test_17, + test_18, + test_19, + test_20, + test_21, + test_22, + test_23, + test_24, + test_25, + test_26, + 0 + +}; + +MainLoop* test() { + + /** A character length != wchar_t may be forced, so the tests wont work */ + + ERR_FAIL_COND_V( sizeof(CharType) != sizeof(wchar_t), NULL ); + + int count=0; + int passed=0; + + while(true) { + if (!test_funcs[count]) + break; + bool pass=test_funcs[count](); + if (pass) + passed++; + OS::get_singleton()->print("\t%s\n",pass?"PASS":"FAILED"); + + count++; + } + + OS::get_singleton()->print("\n\n\n"); + OS::get_singleton()->print("*************\n"); + OS::get_singleton()->print("***TOTALS!***\n"); + OS::get_singleton()->print("*************\n"); + + OS::get_singleton()->print("Passed %i of %i tests\n",count,passed); + + return NULL; +} + +} diff --git a/bin/tests/test_string.h b/bin/tests/test_string.h new file mode 100644 index 00000000000..31cef60014a --- /dev/null +++ b/bin/tests/test_string.h @@ -0,0 +1,44 @@ +/*************************************************************************/ +/* test_string.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 TEST_STRING_H +#define TEST_STRING_H + +#include "ustring.h" +#include "os/main_loop.h" + +namespace TestString { + +MainLoop* test(); + +} + + +#endif + + diff --git a/core/SCsub b/core/SCsub new file mode 100644 index 00000000000..3d7eef0700d --- /dev/null +++ b/core/SCsub @@ -0,0 +1,39 @@ +Import('env') + +env.core_sources=[] + + +gd_call="" +gd_inc="" + +for x in env.global_defaults: + env.core_sources.append("#platform/"+x+"/globals/global_defaults.cpp") + gd_inc+='#include "platform/'+x+'/globals/global_defaults.h"\n' + gd_call+="\tregister_"+x+"_global_defaults();\n" + +gd_cpp='#include "globals.h"\n' +gd_cpp+=gd_inc +gd_cpp+="void Globals::register_global_defaults() {\n"+gd_call+"\n}\n" + +f = open("global_defaults.cpp","wb") +f.write(gd_cpp) +f.close() + + +env.add_source_files(env.core_sources,"*.cpp") + +Export('env') + +import make_binders +env.Command('method_bind.inc', 'make_binders.py', make_binders.run) + +SConscript('os/SCsub'); +SConscript('math/SCsub'); +SConscript('io/SCsub'); +SConscript('bind/SCsub'); + +lib = env.Library("core",env.core_sources) + +env.Prepend(LIBS=[lib]) + + diff --git a/core/allocators.h b/core/allocators.h new file mode 100644 index 00000000000..42eb4effaf9 --- /dev/null +++ b/core/allocators.h @@ -0,0 +1,198 @@ +/*************************************************************************/ +/* allocators.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 ALLOCATORS_H +#define ALLOCATORS_H + +#include "os/memory.h" +template +class BalloonAllocator { + + enum { + + USED_FLAG=(1<<30), + USED_MASK=USED_FLAG-1 + }; + + struct Balloon { + + Balloon *next; + Balloon *prev; + uint32_t hand; + }; + + + struct Hand { + + int used; + int allocated; + Balloon *first; + Balloon *last; + }; + + + Hand hands[MAX_HANDS]; + + + +public: + + void* alloc(size_t p_size) { + + size_t max=(1<max, NULL ); + + unsigned int hand=0; + + while(p_size>(size_t)(1<hand=hand; + if (h.last) { + + b->prev=h.last; + h.last->next=b; + h.last=b; + } else { + + b->prev=NULL; + h.last=b; + h.first=b; + } + } + + h.last->next=NULL; + h.allocated+=PREALLOC_COUNT; + } + + Balloon *pick=h.last; + + ERR_FAIL_COND_V( (pick->hand&USED_FLAG), NULL ); + + // remove last + h.last=h.last->prev; + h.last->next=NULL; + + pick->next=h.first; + h.first->prev=pick; + pick->prev=NULL; + h.first=pick; + h.used++; + pick->hand|=USED_FLAG; + + return (void*)(pick+1); + } + + void free(void* p_ptr) { + + Balloon *b=(Balloon*)p_ptr; + b-=1; + + ERR_FAIL_COND(!(b->hand&USED_FLAG) ); + + b->hand=b->hand&USED_MASK; // not used + int hand=b->hand; + + Hand &h=hands[hand]; + + if (b==h.first) + h.first=b->next; + + if (b->prev) + b->prev->next=b->next; + if (b->next) + b->next->prev=b->prev; + + if (h.last!=b) { + h.last->next=b; + b->prev=h.last; + b->next=NULL; + h.last=b; + } + + h.used--; + + if (h.used<=(h.allocated-(PREALLOC_COUNT*2))) { // this is done to ensure no alloc/free is done constantly + + for(int i=0;ihand& USED_FLAG ); + + Balloon *new_last=h.last->prev; + if (new_last) + new_last->next=NULL; + memfree( h.last ); + h.last=new_last; + } + h.allocated-=PREALLOC_COUNT; + } + } + + BalloonAllocator() { + + for(int i=0;inext; + memfree(b); + } + + hands[i].allocated=0; + hands[i].used=0; + hands[i].first=NULL; + hands[i].last=NULL; + } + } + + ~BalloonAllocator() { + + clear(); + } +}; + + +#endif // ALLOCATORS_H diff --git a/core/array.cpp b/core/array.cpp new file mode 100644 index 00000000000..728ea5941c3 --- /dev/null +++ b/core/array.cpp @@ -0,0 +1,241 @@ +/*************************************************************************/ +/* array.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 "array.h" +#include "vector.h" +#include "hashfuncs.h" +#include "variant.h" +#include "object.h" + +struct ArrayPrivate { + + SafeRefCount refcount; + Vector array; + bool shared; +}; + +void Array::_ref(const Array& p_from) const { + + ArrayPrivate *_fp = p_from._p; + + ERR_FAIL_COND(!_fp); // should NOT happen. + + if (_fp == _p) + return; //wathever it is, nothing to do here move along + + bool success = _fp->refcount.ref(); + + ERR_FAIL_COND(!success); //should really not happen either + + _unref(); + + if (_fp->shared) { + + _p = p_from._p; + + } else { + + _p = memnew( ArrayPrivate ); + _p->shared=false; + _p->refcount.init(); + _p->array=_fp->array; + + if (_fp->refcount.unref()) + memdelete(_fp); + } +} + +void Array::_unref() const { + + if (!_p) + return; + + if (_p->refcount.unref()) { + memdelete(_p); + } + _p=NULL; +} + + +Variant& Array::operator[](int p_idx) { + + return _p->array[p_idx]; +} + +const Variant& Array::operator[](int p_idx) const { + + return _p->array[p_idx]; + +} + +int Array::size() const { + + return _p->array.size(); +} +bool Array::empty() const { + + return _p->array.empty(); +} +void Array::clear() { + + _p->array.clear(); +} + +bool Array::is_shared() const { + + return _p->shared; +} + +bool Array::operator==(const Array& p_array) const { + + return _p==p_array._p; +} + +uint32_t Array::hash() const { + + uint32_t h=hash_djb2_one_32(0); + + for (int i=0;i<_p->array.size();i++) { + + h = hash_djb2_one_32( _p->array[i].hash(), h); + } + return h; +} +void Array::operator=(const Array& p_array) { + + _ref(p_array); +} +void Array::push_back(const Variant& p_value) { + + _p->array.push_back(p_value); +} + +Error Array::resize(int p_new_size) { + + return _p->array.resize(p_new_size); +} + +void Array::insert(int p_pos, const Variant& p_value) { + + _p->array.insert(p_pos,p_value); +} + +void Array::erase(const Variant& p_value) { + + _p->array.erase(p_value); +} + +int Array::find(const Variant& p_value) const { + + return _p->array.find(p_value); +} + +void Array::remove(int p_pos) { + + _p->array.remove(p_pos); +} + + +void Array::set(int p_idx,const Variant& p_value) { + + operator[](p_idx)=p_value; +} + +const Variant& Array::get(int p_idx) const { + + return operator[](p_idx); +} + +struct _ArrayVariantSort { + + _FORCE_INLINE_ bool operator()(const Variant& p_l, const Variant& p_r) const { + bool valid=false; + Variant res; + Variant::evaluate(Variant::OP_LESS,p_l,p_r,res,valid); + if (!valid) + res=false; + return res; + } +}; + +void Array::sort() { + + _p->array.sort_custom<_ArrayVariantSort>(); + +} + +struct _ArrayVariantSortCustom { + + Object *obj; + StringName func; + + _FORCE_INLINE_ bool operator()(const Variant& p_l, const Variant& p_r) const { + + const Variant*args[2]={&p_l,&p_r}; + Variant::CallError err; + bool res = obj->call(func,args,2,err); + if (err.error!=Variant::CallError::CALL_OK) + res=false; + return res; + + } +}; +void Array::sort_custom(Object *p_obj,const StringName& p_function){ + + ERR_FAIL_NULL(p_obj); + + SortArray avs; + avs.compare.obj=p_obj; + avs.compare.func=p_function; + avs.sort(_p->array.ptr(),_p->array.size()); + +} + +void Array::invert(){ + + _p->array.invert(); +} + + + +Array::Array(const Array& p_from) { + + _p=NULL; + _ref(p_from); + +} +Array::Array(bool p_shared) { + + _p = memnew( ArrayPrivate ); + _p->refcount.init(); + _p->shared=p_shared; +} +Array::~Array() { + + _unref(); +} diff --git a/core/array.h b/core/array.h new file mode 100644 index 00000000000..0b8240558f8 --- /dev/null +++ b/core/array.h @@ -0,0 +1,84 @@ +/*************************************************************************/ +/* array.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 ARRAY_H +#define ARRAY_H + +#include "typedefs.h" +class Variant; +class ArrayPrivate; +class Object; +class StringName; + +class Array { + + mutable ArrayPrivate *_p; + void _ref(const Array& p_from) const; + void _unref() const; + +public: + + Variant& operator[](int p_idx); + const Variant& operator[](int p_idx) const; + + void set(int p_idx,const Variant& p_value); + const Variant& get(int p_idx) const; + + int size() const; + bool empty() const; + void clear(); + + bool is_shared() const; + + bool operator==(const Array& p_array) const; + + uint32_t hash() const; + void operator=(const Array& p_array); + + void push_back(const Variant& p_value); + _FORCE_INLINE_ void append(const Variant& p_value) { push_back(p_value); } //for python compatibility + Error resize(int p_new_size); + + void insert(int p_pos, const Variant& p_value); + void remove(int p_pos); + + void sort(); + void sort_custom(Object *p_obj,const StringName& p_function); + void invert(); + + int find(const Variant& p_value) const; + + void erase(const Variant& p_value); + + Array(const Array& p_from); + Array(bool p_shared=false); + ~Array(); + +}; + +#endif // ARRAY_H diff --git a/core/balloon_allocator.h b/core/balloon_allocator.h new file mode 100644 index 00000000000..d99f805f524 --- /dev/null +++ b/core/balloon_allocator.h @@ -0,0 +1,35 @@ +/*************************************************************************/ +/* balloon_allocator.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 BALLOON_ALLOCATOR_H +#define BALLOON_ALLOCATOR_H + +#include "os/memory.h" + +#include "allocators.h" +#endif // BALLOON_ALLOCATOR_H diff --git a/core/bind/SCsub b/core/bind/SCsub new file mode 100644 index 00000000000..c6ba1fa5376 --- /dev/null +++ b/core/bind/SCsub @@ -0,0 +1,7 @@ +Import('env') + +env.add_source_files(env.core_sources,"*.cpp") + +Export('env') + + diff --git a/core/bind/core_bind.cpp b/core/bind/core_bind.cpp new file mode 100644 index 00000000000..61209ecb902 --- /dev/null +++ b/core/bind/core_bind.cpp @@ -0,0 +1,1436 @@ +#include "core_bind.h" +#include "os/os.h" +#include "geometry.h" +#include "io/marshalls.h" +#include "io/base64.h" +#include "core/globals.h" + +_ResourceLoader *_ResourceLoader::singleton=NULL; + +Ref _ResourceLoader::load_interactive(const String& p_path,const String& p_type_hint) { + return ResourceLoader::load_interactive(p_path,p_type_hint); +} + +RES _ResourceLoader::load(const String &p_path,const String& p_type_hint) { + + RES ret = ResourceLoader::load(p_path,p_type_hint); + return ret; +} + +DVector _ResourceLoader::get_recognized_extensions_for_type(const String& p_type) { + + List exts; + ResourceLoader::get_recognized_extensions_for_type(p_type,&exts); + DVector ret; + for(List::Element *E=exts.front();E;E=E->next()) { + + ret.push_back(E->get()); + } + + return ret; +} + +void _ResourceLoader::set_abort_on_missing_resources(bool p_abort) { + + ResourceLoader::set_abort_on_missing_resources(p_abort); +} + +StringArray _ResourceLoader::get_dependencies(const String& p_path) { + + List deps; + ResourceLoader::get_dependencies(p_path, &deps); + + StringArray ret; + for(List::Element *E=deps.front();E;E=E->next()) { + ret.push_back(E->get()); + } + + return ret; +}; + +bool _ResourceLoader::has(const String &p_path) { + + String local_path = Globals::get_singleton()->localize_path(p_path); + return ResourceCache::has(local_path); +}; + +void _ResourceLoader::_bind_methods() { + + + ObjectTypeDB::bind_method(_MD("load_interactive:ResourceInteractiveLoader","path","type_hint"),&_ResourceLoader::load_interactive,DEFVAL("")); + ObjectTypeDB::bind_method(_MD("load:Resource","path","type_hint"),&_ResourceLoader::load,DEFVAL("")); + ObjectTypeDB::bind_method(_MD("get_recognized_extensions_for_type","type"),&_ResourceLoader::get_recognized_extensions_for_type); + ObjectTypeDB::bind_method(_MD("set_abort_on_missing_resources","abort"),&_ResourceLoader::set_abort_on_missing_resources); + ObjectTypeDB::bind_method(_MD("get_dependencies"),&_ResourceLoader::get_dependencies); + ObjectTypeDB::bind_method(_MD("has"),&_ResourceLoader::has); +} + +_ResourceLoader::_ResourceLoader() { + + singleton=this; +} + + +Error _ResourceSaver::save(const String &p_path,const RES& p_resource, uint32_t p_flags) { + + ERR_FAIL_COND_V(p_resource.is_null(),ERR_INVALID_PARAMETER); + return ResourceSaver::save(p_path,p_resource, p_flags); +} + +DVector _ResourceSaver::get_recognized_extensions(const RES& p_resource) { + + ERR_FAIL_COND_V(p_resource.is_null(),DVector()); + List exts; + ResourceSaver::get_recognized_extensions(p_resource,&exts); + DVector ret; + for(List::Element *E=exts.front();E;E=E->next()) { + + ret.push_back(E->get()); + } + return ret; +} + +_ResourceSaver *_ResourceSaver::singleton=NULL; + + +void _ResourceSaver::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("save","path","resource:Resource"),&_ResourceSaver::save, DEFVAL(0)); + ObjectTypeDB::bind_method(_MD("get_recognized_extensions","type"),&_ResourceSaver::get_recognized_extensions); +} + +_ResourceSaver::_ResourceSaver() { + + singleton=this; +} + + +/////////////////OS + + +Point2 _OS::get_mouse_pos() const { + + return OS::get_singleton()->get_mouse_pos(); +} +void _OS::set_window_title(const String& p_title) { + + OS::get_singleton()->set_window_title(p_title); + +} + +int _OS::get_mouse_button_state() const { + + return OS::get_singleton()->get_mouse_button_state(); +} + +String _OS::get_unique_ID() const { + return OS::get_singleton()->get_unique_ID(); +} +bool _OS::has_touchscreen_ui_hint() const { + + return OS::get_singleton()->has_touchscreen_ui_hint(); +} + +void _OS::set_clipboard(const String& p_text) { + + + OS::get_singleton()->set_clipboard(p_text); +} +String _OS::get_clipboard() const { + + return OS::get_singleton()->get_clipboard(); + +} + +void _OS::set_video_mode(const Size2& p_size, bool p_fullscreen,bool p_resizeable,int p_screen) { + + OS::VideoMode vm; + vm.width=p_size.width; + vm.height=p_size.height; + vm.fullscreen=p_fullscreen; + vm.resizable=p_resizeable; + OS::get_singleton()->set_video_mode( vm,p_screen); + + +} +Size2 _OS::get_video_mode(int p_screen) const { + + OS::VideoMode vm; + vm = OS::get_singleton()->get_video_mode(p_screen); + return Size2(vm.width,vm.height); + +} +bool _OS::is_video_mode_fullscreen(int p_screen) const { + + OS::VideoMode vm; + vm = OS::get_singleton()->get_video_mode(p_screen); + return vm.fullscreen; + +} + +bool _OS::is_video_mode_resizable(int p_screen) const { + + OS::VideoMode vm; + vm = OS::get_singleton()->get_video_mode(p_screen); + return vm.resizable; + +} + +Array _OS::get_fullscreen_mode_list(int p_screen) const { + + List vmlist; + OS::get_singleton()->get_fullscreen_mode_list(&vmlist,p_screen); + Array vmarr; + for(List::Element *E=vmlist.front();E;E=E->next() ){ + + vmarr.push_back(Size2(E->get().width,E->get().height)); + } + + return vmarr; +} + +void _OS::set_iterations_per_second(int p_ips) { + + OS::get_singleton()->set_iterations_per_second(p_ips); +} +int _OS::get_iterations_per_second() const { + + return OS::get_singleton()->get_iterations_per_second(); + +} + +void _OS::set_low_processor_usage_mode(bool p_enabled) { + + OS::get_singleton()->set_low_processor_usage_mode(p_enabled); +} +bool _OS::is_in_low_processor_usage_mode() const { + + return OS::get_singleton()->is_in_low_processor_usage_mode(); +} + +String _OS::get_executable_path() const { + + return OS::get_singleton()->get_executable_path(); +} + +Error _OS::shell_open(String p_uri) { + + return OS::get_singleton()->shell_open(p_uri); +}; + + +int _OS::execute(const String& p_path, const Vector & p_arguments,bool p_blocking) { + + OS::ProcessID pid; + List args; + for(int i=0;iexecute(p_path,args,p_blocking,&pid, &pipe); + if (err != OK) + return -1; + else + return pid; + +} +Error _OS::kill(int p_pid) { + + return OS::get_singleton()->kill(p_pid); +} + +bool _OS::has_environment(const String& p_var) const { + + return OS::get_singleton()->has_environment(p_var); +} +String _OS::get_environment(const String& p_var) const { + + return OS::get_singleton()->get_environment(p_var); +} + +String _OS::get_name() const { + + return OS::get_singleton()->get_name(); +} +Vector _OS::get_cmdline_args() { + + List cmdline = OS::get_singleton()->get_cmdline_args(); + Vector cmdlinev; + for(List::Element *E=cmdline.front();E;E=E->next()) { + + cmdlinev.push_back(E->get()); + } + + return cmdlinev; +} + +String _OS::get_locale() const { + + return OS::get_singleton()->get_locale(); +} + +String _OS::get_model_name() const { + + return OS::get_singleton()->get_model_name(); +} + +MainLoop *_OS::get_main_loop() const { + + return OS::get_singleton()->get_main_loop(); +} +/* +enum Weekday { + DAY_SUNDAY, + DAY_MONDAY, + DAY_TUESDAY, + DAY_WEDNESDAY, + DAY_THURSDAY, + DAY_FRIDAY, + DAY_SATURDAY +}; + +enum Month { + MONTH_JANUARY, + MONTH_FEBRUARY, + MONTH_MARCH, + MONTH_APRIL, + MONTH_MAY, + MONTH_JUNE, + MONTH_JULY, + MONTH_AUGUST, + MONTH_SEPTEMBER, + MONTH_OCTOBER, + MONTH_NOVEMBER, + MONTH_DECEMBER +}; +*/ +/* +struct Date { + + int year; + Month month; + int day; + Weekday weekday; + bool dst; +}; + +struct Time { + + int hour; + int min; + int sec; +}; +*/ + +int _OS::get_static_memory_usage() const { + + return OS::get_singleton()->get_static_memory_usage(); + +} + +int _OS::get_static_memory_peak_usage() const { + + return OS::get_singleton()->get_static_memory_peak_usage(); + +} + +int _OS::get_dynamic_memory_usage() const{ + + return OS::get_singleton()->get_dynamic_memory_usage(); +} + + +void _OS::set_icon(const Image& p_icon) { + + OS::get_singleton()->set_icon(p_icon); +} + +Dictionary _OS::get_date() const { + + OS::Date date = OS::get_singleton()->get_date(); + Dictionary dated; + dated["year"]=date.year; + dated["month"]=date.month; + dated["day"]=date.day; + dated["weekday"]=date.weekday; + dated["dst"]=date.dst; + return dated; + + +} +Dictionary _OS::get_time() const { + + OS::Time time = OS::get_singleton()->get_time(); + Dictionary timed; + timed["hour"]=time.hour; + timed["minute"]=time.min; + timed["second"]=time.sec; + return timed; + +} +uint64_t _OS::get_unix_time() const { + + return OS::get_singleton()->get_unix_time(); +}; + +void _OS::delay_usec(uint32_t p_usec) const { + + OS::get_singleton()->delay_usec(p_usec); +} + +void _OS::delay_msec(uint32_t p_msec) const { + + OS::get_singleton()->delay_usec(int64_t(p_msec)*1000); +} + +uint32_t _OS::get_ticks_msec() const { + + return OS::get_singleton()->get_ticks_msec(); +} + +bool _OS::can_draw() const { + + return OS::get_singleton()->can_draw(); +} + +int _OS::get_frames_drawn() { + + return OS::get_singleton()->get_frames_drawn(); +} + +int _OS::get_processor_count() const { + + return OS::get_singleton()->get_processor_count(); +} + +bool _OS::is_stdout_verbose() const { + + return OS::get_singleton()->is_stdout_verbose(); + +} + +void _OS::dump_memory_to_file(const String& p_file) { + + OS::get_singleton()->dump_memory_to_file(p_file.utf8().get_data()); +} + +void _OS::print_all_resources(const String& p_to_file ) { + + OS::get_singleton()->print_all_resources(p_to_file); +} + +void _OS::print_resources_in_use(bool p_short) { + + OS::get_singleton()->print_resources_in_use(p_short); +} + +void _OS::dump_resources_to_file(const String& p_file) { + + OS::get_singleton()->dump_resources_to_file(p_file.utf8().get_data()); +} + +String _OS::get_data_dir() const { + + return OS::get_singleton()->get_data_dir(); +}; + +float _OS::get_frames_per_second() const { + + return OS::get_singleton()->get_frames_per_second(); +} + +String _OS::get_custom_level() const { + + return OS::get_singleton()->get_custom_level(); +} +_OS *_OS::singleton=NULL; + +void _OS::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("get_mouse_pos"),&_OS::get_mouse_pos); + //ObjectTypeDB::bind_method(_MD("is_mouse_grab_enabled"),&_OS::is_mouse_grab_enabled); + + ObjectTypeDB::bind_method(_MD("set_clipboard","clipboard"),&_OS::set_clipboard); + ObjectTypeDB::bind_method(_MD("get_clipboard"),&_OS::get_clipboard); + + ObjectTypeDB::bind_method(_MD("set_video_mode","size","fullscreen","resizable","screen"),&_OS::set_video_mode,DEFVAL(0)); + ObjectTypeDB::bind_method(_MD("get_video_mode_size","screen"),&_OS::get_video_mode,DEFVAL(0)); + ObjectTypeDB::bind_method(_MD("is_video_mode_fullscreen","screen"),&_OS::is_video_mode_fullscreen,DEFVAL(0)); + ObjectTypeDB::bind_method(_MD("is_video_mode_resizable","screen"),&_OS::is_video_mode_resizable,DEFVAL(0)); + ObjectTypeDB::bind_method(_MD("get_fullscreen_mode_list","screen"),&_OS::get_fullscreen_mode_list,DEFVAL(0)); + + ObjectTypeDB::bind_method(_MD("set_iterations_per_second","iterations_per_second"),&_OS::set_iterations_per_second); + ObjectTypeDB::bind_method(_MD("get_iterations_per_second"),&_OS::get_iterations_per_second); + + ObjectTypeDB::bind_method(_MD("has_touchscreen_ui_hint"),&_OS::has_touchscreen_ui_hint); + + + + ObjectTypeDB::bind_method(_MD("set_low_processor_usage_mode","enable"),&_OS::set_low_processor_usage_mode); + ObjectTypeDB::bind_method(_MD("is_in_low_processor_usage_mode"),&_OS::is_in_low_processor_usage_mode); + + ObjectTypeDB::bind_method(_MD("get_processor_count"),&_OS::get_processor_count); + + ObjectTypeDB::bind_method(_MD("get_executable_path"),&_OS::get_executable_path); + ObjectTypeDB::bind_method(_MD("execute","path","arguments","blocking"),&_OS::execute); + ObjectTypeDB::bind_method(_MD("kill","pid"),&_OS::kill); + ObjectTypeDB::bind_method(_MD("shell_open","uri"),&_OS::shell_open); + + ObjectTypeDB::bind_method(_MD("get_environment","environment"),&_OS::get_environment); + ObjectTypeDB::bind_method(_MD("has_environment","environment"),&_OS::has_environment); + + ObjectTypeDB::bind_method(_MD("get_name"),&_OS::get_name); + ObjectTypeDB::bind_method(_MD("get_cmdline_args"),&_OS::get_cmdline_args); + ObjectTypeDB::bind_method(_MD("get_main_loop"),&_OS::get_main_loop); + + ObjectTypeDB::bind_method(_MD("get_date"),&_OS::get_date); + ObjectTypeDB::bind_method(_MD("get_time"),&_OS::get_time); + ObjectTypeDB::bind_method(_MD("get_unix_time"),&_OS::get_unix_time); + + ObjectTypeDB::bind_method(_MD("set_icon"),&_OS::set_icon); + + ObjectTypeDB::bind_method(_MD("delay_usec","usec"),&_OS::delay_usec); + ObjectTypeDB::bind_method(_MD("delay_msec","msec"),&_OS::delay_msec); + ObjectTypeDB::bind_method(_MD("get_ticks_msec"),&_OS::get_ticks_msec); + ObjectTypeDB::bind_method(_MD("get_locale"),&_OS::get_locale); + ObjectTypeDB::bind_method(_MD("get_model_name"),&_OS::get_model_name); + + ObjectTypeDB::bind_method(_MD("get_custom_level"),&_OS::get_custom_level); + + ObjectTypeDB::bind_method(_MD("can_draw"),&_OS::can_draw); + ObjectTypeDB::bind_method(_MD("get_frames_drawn"),&_OS::get_frames_drawn); + ObjectTypeDB::bind_method(_MD("is_stdout_verbose"),&_OS::is_stdout_verbose); + + ObjectTypeDB::bind_method(_MD("get_mouse_button_state"),&_OS::get_mouse_button_state); + + ObjectTypeDB::bind_method(_MD("dump_memory_to_file","file"),&_OS::dump_memory_to_file); + ObjectTypeDB::bind_method(_MD("dump_resources_to_file","file"),&_OS::dump_resources_to_file); + ObjectTypeDB::bind_method(_MD("print_resources_in_use","short"),&_OS::print_resources_in_use,DEFVAL(false)); + ObjectTypeDB::bind_method(_MD("print_all_resources","tofile"),&_OS::print_all_resources,DEFVAL("")); + + ObjectTypeDB::bind_method(_MD("get_static_memory_usage"),&_OS::get_static_memory_usage); + ObjectTypeDB::bind_method(_MD("get_static_memory_peak_usage"),&_OS::get_static_memory_peak_usage); + ObjectTypeDB::bind_method(_MD("get_dynamic_memory_usage"),&_OS::get_dynamic_memory_usage); + + ObjectTypeDB::bind_method(_MD("get_data_dir"),&_OS::get_data_dir); + ObjectTypeDB::bind_method(_MD("get_unique_ID"),&_OS::get_unique_ID); + + ObjectTypeDB::bind_method(_MD("get_frames_per_second"),&_OS::get_frames_per_second); + + BIND_CONSTANT( DAY_SUNDAY ); + BIND_CONSTANT( DAY_MONDAY ); + BIND_CONSTANT( DAY_TUESDAY ); + BIND_CONSTANT( DAY_WEDNESDAY ); + BIND_CONSTANT( DAY_THURSDAY ); + BIND_CONSTANT( DAY_FRIDAY ); + BIND_CONSTANT( DAY_SATURDAY ); + + BIND_CONSTANT( MONTH_JANUARY ); + BIND_CONSTANT( MONTH_FEBRUARY ); + BIND_CONSTANT( MONTH_MARCH ); + BIND_CONSTANT( MONTH_APRIL ); + BIND_CONSTANT( MONTH_MAY ); + BIND_CONSTANT( MONTH_JUNE ); + BIND_CONSTANT( MONTH_JULY ); + BIND_CONSTANT( MONTH_AUGUST ); + BIND_CONSTANT( MONTH_SEPTEMBER ); + BIND_CONSTANT( MONTH_OCTOBER ); + BIND_CONSTANT( MONTH_NOVEMBER ); + BIND_CONSTANT( MONTH_DECEMBER ); + + +} + +_OS::_OS() { + + singleton=this; +} + + +///////////////////// GEOMETRY + + +_Geometry *_Geometry::singleton=NULL; + +_Geometry *_Geometry::get_singleton() { + + return singleton; +} + +DVector _Geometry::build_box_planes(const Vector3& p_extents) { + + return Geometry::build_box_planes(p_extents); +} + +DVector _Geometry::build_cylinder_planes(float p_radius, float p_height, int p_sides, Vector3::Axis p_axis) { + + return Geometry::build_cylinder_planes(p_radius,p_height,p_sides,p_axis); +} +DVector _Geometry::build_capsule_planes(float p_radius, float p_height, int p_sides, int p_lats, Vector3::Axis p_axis) { + + return Geometry::build_capsule_planes(p_radius,p_height,p_sides,p_lats,p_axis); +} + +real_t _Geometry::segment_intersects_circle(const Vector2& p_from, const Vector2& p_to, const Vector2& p_circle_pos, real_t p_circle_radius) { + + return Geometry::segment_intersects_circle(p_from,p_to,p_circle_pos,p_circle_radius); +} + +Variant _Geometry::segment_intersects_segment_2d(const Vector2& p_from_a,const Vector2& p_to_a,const Vector2& p_from_b,const Vector2& p_to_b) { + + Vector2 result; + if (Geometry::segment_intersects_segment_2d(p_from_a, p_to_a, p_from_b, p_to_b, &result)) { + + return result; + } else { + return Variant(); + }; +}; + +DVector _Geometry::get_closest_points_between_segments_2d( const Vector2& p1,const Vector2& q1, const Vector2& p2,const Vector2& q2) { + + Vector2 r1, r2; + Geometry::get_closest_points_between_segments(p1,q1,p2,q2,r1,r2); + DVector r; + r.resize(2); + r.set(0,r1); + r.set(1,r2); + return r; +} + +DVector _Geometry::get_closest_points_between_segments(const Vector3& p1,const Vector3& p2,const Vector3& q1,const Vector3& q2) { + + Vector3 r1, r2; + Geometry::get_closest_points_between_segments(p1,p2,q1,q2,r1,r2); + DVector r; + r.resize(2); + r.set(0,r1); + r.set(1,r2); + return r; + +} +Vector3 _Geometry::get_closest_point_to_segment(const Vector3& p_point, const Vector3& p_a,const Vector3& p_b) { + + Vector3 s[2]={p_a,p_b}; + return Geometry::get_closest_point_to_segment(p_point,s); +} +Variant _Geometry::ray_intersects_triangle( const Vector3& p_from, const Vector3& p_dir, const Vector3& p_v0,const Vector3& p_v1,const Vector3& p_v2) { + + Vector3 res; + if (Geometry::ray_intersects_triangle(p_from,p_dir,p_v0,p_v1,p_v2,&res)) + return res; + else + return Variant(); + + +} +Variant _Geometry::segment_intersects_triangle( const Vector3& p_from, const Vector3& p_to, const Vector3& p_v0,const Vector3& p_v1,const Vector3& p_v2) { + + Vector3 res; + if (Geometry::segment_intersects_triangle(p_from,p_to,p_v0,p_v1,p_v2,&res)) + return res; + else + return Variant(); + +} +DVector _Geometry::segment_intersects_sphere( const Vector3& p_from, const Vector3& p_to, const Vector3& p_sphere_pos,real_t p_sphere_radius) { + + DVector r; + Vector3 res,norm; + if (!Geometry::segment_intersects_sphere(p_from,p_to,p_sphere_pos,p_sphere_radius,&res,&norm)) + return r; + + r.resize(2); + r.set(0,res); + r.set(1,norm); + return r; +} +DVector _Geometry::segment_intersects_cylinder( const Vector3& p_from, const Vector3& p_to, float p_height,float p_radius) { + + DVector r; + Vector3 res,norm; + if (!Geometry::segment_intersects_cylinder(p_from,p_to,p_height,p_radius,&res,&norm)) + return r; + + r.resize(2); + r.set(0,res); + r.set(1,norm); + return r; + +} +DVector _Geometry::segment_intersects_convex(const Vector3& p_from, const Vector3& p_to,const Vector& p_planes) { + + DVector r; + Vector3 res,norm; + if (!Geometry::segment_intersects_convex(p_from,p_to,p_planes.ptr(),p_planes.size(),&res,&norm)) + return r; + + r.resize(2); + r.set(0,res); + r.set(1,norm); + return r; +} + +Vector _Geometry::triangulate_polygon(const Vector& p_polygon) { + + return Geometry::triangulate_polygon(p_polygon); +} + +void _Geometry::_bind_methods() { + + + ObjectTypeDB::bind_method(_MD("build_box_planes","extents"),&_Geometry::build_box_planes); + ObjectTypeDB::bind_method(_MD("build_cylinder_planes","radius","height","sides","axis"),&_Geometry::build_cylinder_planes,DEFVAL(Vector3::AXIS_Z)); + ObjectTypeDB::bind_method(_MD("build_capsule_planes","radius","height","sides","lats","axis"),&_Geometry::build_capsule_planes,DEFVAL(Vector3::AXIS_Z)); + ObjectTypeDB::bind_method(_MD("segment_intersects_circle","segment_from","segment_to","circle_pos","circle_radius"),&_Geometry::segment_intersects_circle); + ObjectTypeDB::bind_method(_MD("segment_intersects_segment_2d","from_a","to_a","from_b","to_b"),&_Geometry::segment_intersects_segment_2d); + + ObjectTypeDB::bind_method(_MD("get_closest_points_between_segments_2d","p1","q1","p2","q2"),&_Geometry::get_closest_points_between_segments_2d); + ObjectTypeDB::bind_method(_MD("get_closest_points_between_segments","p1","p2","q1","q2"),&_Geometry::get_closest_points_between_segments); + + ObjectTypeDB::bind_method(_MD("get_closest_point_to_segment","point","s1","s2"),&_Geometry::get_closest_point_to_segment); + + ObjectTypeDB::bind_method(_MD("ray_intersects_triangle","from","dir","a","b","c"),&_Geometry::ray_intersects_triangle); + ObjectTypeDB::bind_method(_MD("segment_intersects_triangle","from","to","a","b","c"),&_Geometry::segment_intersects_triangle); + ObjectTypeDB::bind_method(_MD("segment_intersects_sphere","from","to","spos","sradius"),&_Geometry::segment_intersects_sphere); + ObjectTypeDB::bind_method(_MD("segment_intersects_cylinder","from","to","height","radius"),&_Geometry::segment_intersects_cylinder); + ObjectTypeDB::bind_method(_MD("segment_intersects_convex","from","to","planes"),&_Geometry::segment_intersects_convex); + + ObjectTypeDB::bind_method(_MD("triangulate_polygon","polygon"),&_Geometry::triangulate_polygon); + +} + + +_Geometry::_Geometry() { + singleton=this; +} + + +///////////////////////// FILE + + +Error _File::open(const String& p_path, int p_mode_flags) { + + close(); + Error err; + f = FileAccess::open(p_path,p_mode_flags,&err); + if (f) + f->set_endian_swap(eswap); + return err; + +} + +void _File::close(){ + + if (f) + memdelete(f); + f=NULL; +} +bool _File::is_open() const{ + + + return f!=NULL; +} + +void _File::seek(int64_t p_position){ + + ERR_FAIL_COND(!f); + f->seek(p_position); + +} +void _File::seek_end(int64_t p_position){ + + + ERR_FAIL_COND(!f); + f->seek_end(p_position); +} +int64_t _File::get_pos() const{ + + + ERR_FAIL_COND_V(!f,0); + return f->get_pos(); +} + +int64_t _File::get_len() const{ + + ERR_FAIL_COND_V(!f,0); + return f->get_len(); +} + +bool _File::eof_reached() const{ + + ERR_FAIL_COND_V(!f,false); + return f->eof_reached(); +} + +uint8_t _File::get_8() const{ + + ERR_FAIL_COND_V(!f,0); + return f->get_8(); + +} +uint16_t _File::get_16() const{ + + ERR_FAIL_COND_V(!f,0); + return f->get_16(); + +} +uint32_t _File::get_32() const{ + + ERR_FAIL_COND_V(!f,0); + return f->get_32(); + +} +uint64_t _File::get_64() const{ + + ERR_FAIL_COND_V(!f,0); + return f->get_64(); + +} + +float _File::get_float() const{ + + ERR_FAIL_COND_V(!f,0); + return f->get_float(); + +} +double _File::get_double() const{ + + + ERR_FAIL_COND_V(!f,0); + return f->get_double(); +} +real_t _File::get_real() const{ + + + ERR_FAIL_COND_V(!f,0); + return f->get_real(); +} + +DVector _File::get_buffer(int p_length) const{ + + DVector data; + ERR_FAIL_COND_V(!f,data); + + ERR_FAIL_COND_V(p_length<0,data); + if (p_length==0) + return data; + Error err = data.resize(p_length); + ERR_FAIL_COND_V(err!=OK,data); + DVector::Write w = data.write(); + int len = f->get_buffer(&w[0],p_length); + ERR_FAIL_COND_V( len < 0 , DVector()); + + w = DVector::Write(); + + if (len < p_length) + data.resize(p_length); + + return data; + +} + + +String _File::get_as_text() const { + + String text; + String l = get_line(); + while(!eof_reached()) { + text+=l+"\n"; + l = get_line(); + } + + return text; + + +} +String _File::get_line() const{ + + ERR_FAIL_COND_V(!f,String()); + return f->get_line(); + +} + +Vector _File::get_csv_line() const { + ERR_FAIL_COND_V(!f,Vector()); + return f->get_csv_line(); +} + +/**< use this for files WRITTEN in _big_ endian machines (ie, amiga/mac) + * It's not about the current CPU type but file formats. + * this flags get reset to false (little endian) on each open + */ + +void _File::set_endian_swap(bool p_swap){ + + + eswap=p_swap; + if (f) + f->set_endian_swap(p_swap); + +} +bool _File::get_endian_swap(){ + + + return eswap; +} + +Error _File::get_error() const{ + + if (!f) + return ERR_UNCONFIGURED; + return f->get_error(); +} + +void _File::store_8(uint8_t p_dest){ + + ERR_FAIL_COND(!f); + + f->store_8(p_dest); +} +void _File::store_16(uint16_t p_dest){ + + ERR_FAIL_COND(!f); + + f->store_16(p_dest); +} +void _File::store_32(uint32_t p_dest){ + + ERR_FAIL_COND(!f); + + f->store_32(p_dest); +} +void _File::store_64(uint64_t p_dest){ + + ERR_FAIL_COND(!f); + + f->store_64(p_dest); +} + +void _File::store_float(float p_dest){ + + ERR_FAIL_COND(!f); + + f->store_float(p_dest); +} +void _File::store_double(double p_dest){ + + ERR_FAIL_COND(!f); + + f->store_double(p_dest); +} +void _File::store_real(real_t p_real){ + + ERR_FAIL_COND(!f); + + f->store_real(p_real); +} + +void _File::store_string(const String& p_string){ + + ERR_FAIL_COND(!f); + + f->store_string(p_string); +} +void _File::store_line(const String& p_string){ + + + ERR_FAIL_COND(!f); + f->store_line(p_string); +} + +void _File::store_buffer(const DVector& p_buffer){ + + ERR_FAIL_COND(!f); + + int len = p_buffer.size(); + if (len==0) + return; + + DVector::Read r = p_buffer.read(); + + f->store_buffer(&r[0],len); +} + +bool _File::file_exists(const String& p_name) const{ + + return FileAccess::exists(p_name); + + +} + +void _File::store_var(const Variant& p_var) { + + ERR_FAIL_COND(!f); + int len; + Error err = encode_variant(p_var,NULL,len); + ERR_FAIL_COND( err != OK ); + + DVector buff; + buff.resize(len); + DVector::Write w = buff.write(); + + err = encode_variant(p_var,&w[0],len); + ERR_FAIL_COND( err != OK ); + w=DVector::Write(); + + store_32(len); + store_buffer(buff); +} + +Variant _File::get_var() const { + + ERR_FAIL_COND_V(!f,Variant()); + uint32_t len = get_32(); + DVector buff = get_buffer(len); + ERR_FAIL_COND_V(buff.size() != len, Variant()); + + DVector::Read r = buff.read(); + + Variant v; + Error err = decode_variant(v,&r[0],len); + ERR_FAIL_COND_V( err!=OK, Variant() ); + + return v; +} + +void _File::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("open","path","flags"),&_File::open); + ObjectTypeDB::bind_method(_MD("close"),&_File::close); + ObjectTypeDB::bind_method(_MD("is_open"),&_File::is_open); + ObjectTypeDB::bind_method(_MD("seek","pos"),&_File::seek); + ObjectTypeDB::bind_method(_MD("seek_end","pos"),&_File::seek_end,DEFVAL(0)); + ObjectTypeDB::bind_method(_MD("get_pos"),&_File::get_pos); + ObjectTypeDB::bind_method(_MD("get_len"),&_File::get_len); + ObjectTypeDB::bind_method(_MD("eof_reached"),&_File::eof_reached); + ObjectTypeDB::bind_method(_MD("get_8"),&_File::get_8); + ObjectTypeDB::bind_method(_MD("get_16"),&_File::get_16); + ObjectTypeDB::bind_method(_MD("get_32"),&_File::get_32); + ObjectTypeDB::bind_method(_MD("get_64"),&_File::get_64); + ObjectTypeDB::bind_method(_MD("get_float"),&_File::get_float); + ObjectTypeDB::bind_method(_MD("get_double"),&_File::get_double); + ObjectTypeDB::bind_method(_MD("get_real"),&_File::get_real); + ObjectTypeDB::bind_method(_MD("get_buffer","len"),&_File::get_buffer); + ObjectTypeDB::bind_method(_MD("get_line"),&_File::get_line); + ObjectTypeDB::bind_method(_MD("get_as_text"),&_File::get_as_text); + ObjectTypeDB::bind_method(_MD("get_endian_swap"),&_File::get_endian_swap); + ObjectTypeDB::bind_method(_MD("set_endian_swap","enable"),&_File::set_endian_swap); + ObjectTypeDB::bind_method(_MD("get_error:Error"),&_File::get_error); + ObjectTypeDB::bind_method(_MD("get_var"),&_File::get_var); + ObjectTypeDB::bind_method(_MD("get_csv_line"),&_File::get_csv_line); + + ObjectTypeDB::bind_method(_MD("store_8","value"),&_File::store_8); + ObjectTypeDB::bind_method(_MD("store_16","value"),&_File::store_16); + ObjectTypeDB::bind_method(_MD("store_32","value"),&_File::store_32); + ObjectTypeDB::bind_method(_MD("store_64","value"),&_File::store_64); + ObjectTypeDB::bind_method(_MD("store_float","value"),&_File::store_float); + ObjectTypeDB::bind_method(_MD("store_double","value"),&_File::store_double); + ObjectTypeDB::bind_method(_MD("store_real","value"),&_File::store_real); + ObjectTypeDB::bind_method(_MD("store_buffer","buffer"),&_File::store_buffer); + ObjectTypeDB::bind_method(_MD("store_line","line"),&_File::store_line); + ObjectTypeDB::bind_method(_MD("store_string","string"),&_File::store_string); + ObjectTypeDB::bind_method(_MD("store_var","value"),&_File::store_var); + + ObjectTypeDB::bind_method(_MD("file_exists","path"),&_File::file_exists); + + BIND_CONSTANT( READ ); + BIND_CONSTANT( WRITE ); + BIND_CONSTANT( READ_WRITE ); +} + +_File::_File(){ + + f = NULL; + eswap=false; + +} + +_File::~_File(){ + + if (f) + memdelete(f); + +} + +/////////////////////////////////////////////////////// + + + + +Error _Directory::open(const String& p_path) { + Error err; + DirAccess *alt=DirAccess::open(p_path,&err); + + if (!alt) + return err; + if (d) + memdelete(d); + d=alt; + + return OK; +} + +bool _Directory::list_dir_begin() { + + ERR_FAIL_COND_V(!d,false); + return d->list_dir_begin(); +} + +String _Directory::get_next(){ + + ERR_FAIL_COND_V(!d,""); + return d->get_next(); +} +bool _Directory::current_is_dir() const{ + + ERR_FAIL_COND_V(!d,false); + return d->current_is_dir(); +} + +void _Directory::list_dir_end(){ + + ERR_FAIL_COND(!d); + return d->list_dir_end(); +} + +int _Directory::get_drive_count(){ + + ERR_FAIL_COND_V(!d,0); + return d->get_drive_count(); +} +String _Directory::get_drive(int p_drive){ + + ERR_FAIL_COND_V(!d,""); + return d->get_drive(p_drive); +} + +Error _Directory::change_dir(String p_dir){ + + ERR_FAIL_COND_V(!d,ERR_UNCONFIGURED); + return d->change_dir(p_dir); +} +String _Directory::get_current_dir() { + + ERR_FAIL_COND_V(!d,""); + return d->get_current_dir(); +} +Error _Directory::make_dir(String p_dir){ + + ERR_FAIL_COND_V(!d,ERR_UNCONFIGURED); + return d->make_dir(p_dir); +} +Error _Directory::make_dir_recursive(String p_dir){ + + ERR_FAIL_COND_V(!d,ERR_UNCONFIGURED); + return d->make_dir_recursive(p_dir); +} + +bool _Directory::file_exists(String p_file){ + + ERR_FAIL_COND_V(!d,false); + return d->file_exists(p_file); +} + + +int _Directory::get_space_left(){ + + ERR_FAIL_COND_V(!d,0); + return d->get_space_left(); +} + +Error _Directory::copy(String p_from,String p_to){ + + ERR_FAIL_COND_V(!d,ERR_UNCONFIGURED); + return d->copy(p_from,p_to); +} +Error _Directory::rename(String p_from, String p_to){ + + ERR_FAIL_COND_V(!d,ERR_UNCONFIGURED); + return d->rename(p_from,p_to); + +} +Error _Directory::remove(String p_name){ + + ERR_FAIL_COND_V(!d,ERR_UNCONFIGURED); + return d->remove(p_name); +} + +void _Directory::_bind_methods() { + + + ObjectTypeDB::bind_method(_MD("open:Error","path"),&_Directory::open); + ObjectTypeDB::bind_method(_MD("list_dir_begin"),&_Directory::list_dir_begin); + ObjectTypeDB::bind_method(_MD("get_next"),&_Directory::get_next); + ObjectTypeDB::bind_method(_MD("current_is_dir"),&_Directory::current_is_dir); + ObjectTypeDB::bind_method(_MD("list_dir_end"),&_Directory::list_dir_end); + ObjectTypeDB::bind_method(_MD("get_drive_count"),&_Directory::get_drive_count); + ObjectTypeDB::bind_method(_MD("get_drive","idx"),&_Directory::get_drive); + ObjectTypeDB::bind_method(_MD("change_dir:Error","todir"),&_Directory::change_dir); + ObjectTypeDB::bind_method(_MD("get_current_dir"),&_Directory::get_current_dir); + ObjectTypeDB::bind_method(_MD("make_dir:Error","name"),&_Directory::make_dir); + ObjectTypeDB::bind_method(_MD("make_dir_recursive:Error","name"),&_Directory::make_dir_recursive); + ObjectTypeDB::bind_method(_MD("file_exists","name"),&_Directory::file_exists); +// ObjectTypeDB::bind_method(_MD("get_modified_time","file"),&_Directory::get_modified_time); + ObjectTypeDB::bind_method(_MD("get_space_left"),&_Directory::get_space_left); + ObjectTypeDB::bind_method(_MD("copy:Error","from","to"),&_Directory::copy); + ObjectTypeDB::bind_method(_MD("rename:Error","from","to"),&_Directory::rename); + ObjectTypeDB::bind_method(_MD("remove:Error","file"),&_Directory::remove); + +} + +_Directory::_Directory() { + + d = DirAccess::create(DirAccess::ACCESS_RESOURCES); +} + +_Directory::~_Directory() { + + if (d) + memdelete(d); +} + +String _Marshalls::variant_to_base64(const Variant& p_var) { + + int len; + Error err = encode_variant(p_var,NULL,len); + ERR_FAIL_COND_V( err != OK, "" ); + + DVector buff; + buff.resize(len); + DVector::Write w = buff.write(); + + err = encode_variant(p_var,&w[0],len); + ERR_FAIL_COND_V( err != OK, "" ); + + int b64len = len / 3 * 4 + 4; + DVector b64buff; + b64buff.resize(b64len); + DVector::Write w64 = b64buff.write(); + + int strlen = base64_encode((char*)(&w64[0]), (char*)(&w[0]), len); + //OS::get_singleton()->print("len is %i, vector size is %i\n", b64len, strlen); + w64[strlen] = 0; + String ret = (char*)&w64[0]; + + return ret; +}; + +Variant _Marshalls::base64_to_variant(const String& p_str) { + + int strlen = p_str.length(); + CharString cstr = p_str.ascii(); + + DVector buf; + buf.resize(strlen / 4 * 3); + DVector::Write w = buf.write(); + + int len = base64_decode((char*)(&w[0]), (char*)cstr.get_data(), strlen); + + Variant v; + Error err = decode_variant(v, &w[0], len); + ERR_FAIL_COND_V( err!=OK, Variant() ); + + return v; +}; + + +void _Marshalls::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("variant_to_base64:String","variant"),&_Marshalls::variant_to_base64); + ObjectTypeDB::bind_method(_MD("base64_to_variant:Variant","base64_str"),&_Marshalls::base64_to_variant); + +}; + + + +//////////////// + + + + +Error _Semaphore::wait() { + + return semaphore->wait(); +} + +Error _Semaphore::post() { + + return semaphore->post(); +} + + +void _Semaphore::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("wait:Error"),&_Semaphore::wait); + ObjectTypeDB::bind_method(_MD("post:Error"),&_Semaphore::post); + +} + + +_Semaphore::_Semaphore() { + + semaphore =Semaphore::create(); +} + +_Semaphore::~_Semaphore(){ + + memdelete(semaphore); +} + + +/////////////// + + +void _Mutex::lock() { + + mutex->lock(); +} + +Error _Mutex::try_lock(){ + + return mutex->try_lock(); +} + +void _Mutex::unlock(){ + + mutex->unlock(); +} + +void _Mutex::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("lock"),&_Mutex::lock); + ObjectTypeDB::bind_method(_MD("try_lock:Error"),&_Mutex::try_lock); + ObjectTypeDB::bind_method(_MD("unlock"),&_Mutex::unlock); + +} + + +_Mutex::_Mutex() { + + mutex =Mutex::create(); +} + +_Mutex::~_Mutex(){ + + memdelete(mutex); +} + + +/////////////// + + + +void _Thread::_start_func(void *ud) { + + _Thread *t=(_Thread*)ud; + Variant::CallError ce; + const Variant* arg[1]={&t->userdata}; + t->ret=t->target_instance->call(t->target_method,arg,1,ce); + if (ce.error!=Variant::CallError::CALL_OK) { + + String reason; + switch(ce.error) { + case Variant::CallError::CALL_ERROR_INVALID_ARGUMENT: { + + reason="Invalid Argument #"+itos(ce.argument); + } break; + case Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS: { + + reason="Too Many Arguments"; + } break; + case Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS: { + + reason="Too Many Arguments"; + } break; + case Variant::CallError::CALL_ERROR_INVALID_METHOD: { + + reason="Method Not Found"; + } break; + default: {} + } + + ERR_EXPLAIN("Could not call function '"+t->target_method.operator String()+"'' starting thread ID: "+t->get_id()+" Reason: "+reason); + ERR_FAIL(); + } + +} + +Error _Thread::start(Object *p_instance,const StringName& p_method,const Variant& p_userdata,int p_priority) { + + ERR_FAIL_COND_V(active,ERR_ALREADY_IN_USE); + ERR_FAIL_COND_V(!p_instance,ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_method==StringName(),ERR_INVALID_PARAMETER); + ERR_FAIL_INDEX_V(p_priority,3,ERR_INVALID_PARAMETER); + + + ret=Variant(); + target_method=p_method; + target_instance=p_instance; + userdata=p_userdata; + active=true; + + Thread::Settings s; + s.priority=(Thread::Priority)p_priority; + thread = Thread::create(_start_func,this,s); + if (!thread) { + active=false; + target_method=StringName(); + target_instance=NULL; + userdata=Variant(); + return ERR_CANT_CREATE; + } + + return OK; +} + +String _Thread::get_id() const { + + if (!thread) + return String(); + + return itos(thread->get_ID()); +} + + +bool _Thread::is_active() const { + + return active; +} +Variant _Thread::wait_to_finish() { + + ERR_FAIL_COND_V(!thread,Variant()); + ERR_FAIL_COND_V(!active,Variant()); + Thread::wait_to_finish(thread); + Variant r = ret; + active=false; + target_method=StringName(); + target_instance=NULL; + userdata=Variant(); + thread=NULL; + + return r; +} + +void _Thread::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("start:Error","instance","method","userdata","priority"),&_Thread::start,DEFVAL(Variant()),DEFVAL(PRIORITY_NORMAL)); + ObjectTypeDB::bind_method(_MD("get_id"),&_Thread::get_id); + ObjectTypeDB::bind_method(_MD("is_active"),&_Thread::is_active); + ObjectTypeDB::bind_method(_MD("wait_to_finish:var"),&_Thread::wait_to_finish); + + BIND_CONSTANT( PRIORITY_LOW ); + BIND_CONSTANT( PRIORITY_NORMAL ); + BIND_CONSTANT( PRIORITY_HIGH ); + +} +_Thread::_Thread() { + + active=false; + thread=NULL; + target_instance=NULL; +} + +_Thread::~_Thread() { + + ERR_FAIL_COND(active==true); +} diff --git a/core/bind/core_bind.h b/core/bind/core_bind.h new file mode 100644 index 00000000000..e47c9c434a6 --- /dev/null +++ b/core/bind/core_bind.h @@ -0,0 +1,417 @@ +#ifndef CORE_BIND_H +#define CORE_BIND_H + +#include "io/resource_loader.h" +#include "io/resource_saver.h" +#include "os/file_access.h" +#include "os/dir_access.h" +#include "os/thread.h" +#include "os/semaphore.h" + + +class _ResourceLoader : public Object { + OBJ_TYPE(_ResourceLoader,Object); + +protected: + + static void _bind_methods(); + static _ResourceLoader *singleton; +public: + + + static _ResourceLoader *get_singleton() { return singleton; } + Ref load_interactive(const String& p_path,const String& p_type_hint=""); + RES load(const String &p_path,const String& p_type_hint=""); + DVector get_recognized_extensions_for_type(const String& p_type); + void set_abort_on_missing_resources(bool p_abort); + StringArray get_dependencies(const String& p_path); + bool has(const String& p_path); + + _ResourceLoader(); +}; + +class _ResourceSaver : public Object { + OBJ_TYPE(_ResourceSaver,Object); + +protected: + + static void _bind_methods(); + static _ResourceSaver *singleton; +public: + + static _ResourceSaver *get_singleton() { return singleton; } + + Error save(const String &p_path,const RES& p_resource, uint32_t p_flags); + DVector get_recognized_extensions(const RES& p_resource); + + + _ResourceSaver(); +}; + +class MainLoop; + +class _OS : public Object { + OBJ_TYPE(_OS,Object); + +protected: + + static void _bind_methods(); + static _OS *singleton; +public: + + enum Weekday { + DAY_SUNDAY, + DAY_MONDAY, + DAY_TUESDAY, + DAY_WEDNESDAY, + DAY_THURSDAY, + DAY_FRIDAY, + DAY_SATURDAY + }; + + enum Month { + MONTH_JANUARY, + MONTH_FEBRUARY, + MONTH_MARCH, + MONTH_APRIL, + MONTH_MAY, + MONTH_JUNE, + MONTH_JULY, + MONTH_AUGUST, + MONTH_SEPTEMBER, + MONTH_OCTOBER, + MONTH_NOVEMBER, + MONTH_DECEMBER + }; + + Point2 get_mouse_pos() const; + void set_window_title(const String& p_title); + int get_mouse_button_state() const; + + + void set_clipboard(const String& p_text); + String get_clipboard() const; + + void set_video_mode(const Size2& p_size, bool p_fullscreen,bool p_resizeable,int p_screen=0); + Size2 get_video_mode(int p_screen=0) const; + bool is_video_mode_fullscreen(int p_screen=0) const; + bool is_video_mode_resizable(int p_screen=0) const; + Array get_fullscreen_mode_list(int p_screen=0) const; + + void set_iterations_per_second(int p_ips); + int get_iterations_per_second() const; + + void set_low_processor_usage_mode(bool p_enabled); + bool is_in_low_processor_usage_mode() const; + + String get_executable_path() const; + int execute(const String& p_path, const Vector & p_arguments,bool p_blocking); + Error kill(int p_pid); + Error shell_open(String p_uri); + + bool has_environment(const String& p_var) const; + String get_environment(const String& p_var) const; + + String get_name() const; + Vector get_cmdline_args(); + + String get_locale() const; + String get_model_name() const; + MainLoop *get_main_loop() const; + + String get_custom_level() const; + + float get_frames_per_second() const; + + void dump_memory_to_file(const String& p_file); + void dump_resources_to_file(const String& p_file); + + void print_resources_in_use(bool p_short=false); + void print_all_resources(const String& p_to_file); + + bool has_touchscreen_ui_hint() const; + + String get_unique_ID() const; + + /* + struct Date { + + int year; + Month month; + int day; + Weekday weekday; + bool dst; + }; + + struct Time { + + int hour; + int min; + int sec; + }; +*/ + + + void set_icon(const Image& p_icon); + Dictionary get_date() const; + Dictionary get_time() const; + uint64_t get_unix_time() const; + + int get_static_memory_usage() const; + int get_static_memory_peak_usage() const; + int get_dynamic_memory_usage() const; + + void delay_usec(uint32_t p_usec) const; + void delay_msec(uint32_t p_msec) const; + uint32_t get_ticks_msec() const; + + + bool can_draw() const; + + int get_frames_drawn(); + + bool is_stdout_verbose() const; + + int get_processor_count() const; + + String get_data_dir() const; + + static _OS *get_singleton() { return singleton; } + + _OS(); +}; + +class _Geometry : public Object { + + OBJ_TYPE(_Geometry, Object); + + static _Geometry *singleton; +protected: + + static void _bind_methods(); +public: + + static _Geometry *get_singleton(); + DVector build_box_planes(const Vector3& p_extents); + DVector build_cylinder_planes(float p_radius, float p_height, int p_sides, Vector3::Axis p_axis=Vector3::AXIS_Z); + DVector build_capsule_planes(float p_radius, float p_height, int p_sides, int p_lats, Vector3::Axis p_axis=Vector3::AXIS_Z); + Variant segment_intersects_segment_2d(const Vector2& p_from_a,const Vector2& p_to_a,const Vector2& p_from_b,const Vector2& p_to_b); + DVector get_closest_points_between_segments_2d( const Vector2& p1,const Vector2& q1, const Vector2& p2,const Vector2& q2); + DVector get_closest_points_between_segments(const Vector3& p1,const Vector3& p2,const Vector3& q1,const Vector3& q2); + Vector3 get_closest_point_to_segment(const Vector3& p_point, const Vector3& p_a,const Vector3& p_b); + Variant ray_intersects_triangle( const Vector3& p_from, const Vector3& p_dir, const Vector3& p_v0,const Vector3& p_v1,const Vector3& p_v2); + Variant segment_intersects_triangle( const Vector3& p_from, const Vector3& p_to, const Vector3& p_v0,const Vector3& p_v1,const Vector3& p_v2); + DVector segment_intersects_sphere( const Vector3& p_from, const Vector3& p_to, const Vector3& p_sphere_pos,real_t p_sphere_radius); + DVector segment_intersects_cylinder( const Vector3& p_from, const Vector3& p_to, float p_height,float p_radius); + DVector segment_intersects_convex(const Vector3& p_from, const Vector3& p_to,const Vector& p_planes); + real_t segment_intersects_circle(const Vector2& p_from, const Vector2& p_to, const Vector2& p_circle_pos, real_t p_circle_radius); + + Vector triangulate_polygon(const Vector& p_polygon); + + _Geometry(); +}; + + + + +class _File : public Reference { + + OBJ_TYPE(_File,Reference); + FileAccess *f; + bool eswap; +protected: + + static void _bind_methods(); +public: + + enum ModeFlags { + + READ=1, + WRITE=2, + READ_WRITE=3, + }; + + Error open(const String& p_path, int p_mode_flags); ///< open a file + void close(); ///< close a file + bool is_open() const; ///< true when file is open + + void seek(int64_t p_position); ///< seek to a given position + void seek_end(int64_t p_position=0); ///< seek from the end of file + int64_t get_pos() const; ///< get position in the file + int64_t get_len() const; ///< get size of the file + + bool eof_reached() const; ///< reading passed EOF + + uint8_t get_8() const; ///< get a byte + uint16_t get_16() const; ///< get 16 bits uint + uint32_t get_32() const; ///< get 32 bits uint + uint64_t get_64() const; ///< get 64 bits uint + + float get_float() const; + double get_double() const; + real_t get_real() const; + + Variant get_var() const; + + DVector get_buffer(int p_length) const; ///< get an array of bytes + String get_line() const; + String get_as_text() const; + + /**< use this for files WRITTEN in _big_ endian machines (ie, amiga/mac) + * It's not about the current CPU type but file formats. + * this flags get reset to false (little endian) on each open + */ + + void set_endian_swap(bool p_swap); + bool get_endian_swap(); + + Error get_error() const; ///< get last error + + void store_8(uint8_t p_dest); ///< store a byte + void store_16(uint16_t p_dest); ///< store 16 bits uint + void store_32(uint32_t p_dest); ///< store 32 bits uint + void store_64(uint64_t p_dest); ///< store 64 bits uint + + void store_float(float p_dest); + void store_double(double p_dest); + void store_real(real_t p_real); + + void store_string(const String& p_string); + void store_line(const String& p_string); + + Vector get_csv_line() const; + + + void store_buffer(const DVector& p_buffer); ///< store an array of bytes + + void store_var(const Variant& p_var); + + bool file_exists(const String& p_name) const; ///< return true if a file exists + + _File(); + virtual ~_File(); + +}; + +class _Directory : public Reference { + + OBJ_TYPE(_Directory,Reference); + DirAccess *d; +protected: + + static void _bind_methods(); +public: + + Error open(const String& p_path); + + bool list_dir_begin(); ///< This starts dir listing + String get_next(); + bool current_is_dir() const; + + void list_dir_end(); ///< + + int get_drive_count(); + String get_drive(int p_drive); + + Error change_dir(String p_dir); ///< can be relative or absolute, return false on success + String get_current_dir(); ///< return current dir location + + Error make_dir(String p_dir); + Error make_dir_recursive(String p_dir); + + bool file_exists(String p_file); + + int get_space_left(); + + Error copy(String p_from,String p_to); + Error rename(String p_from, String p_to); + Error remove(String p_name); + + + _Directory(); + virtual ~_Directory(); + +}; + +class _Marshalls : public Reference { + + OBJ_TYPE(_Marshalls,Reference); + +protected: + + static void _bind_methods(); + + +public: + + String variant_to_base64(const Variant& p_var); + Variant base64_to_variant(const String& p_str); + + _Marshalls() {}; +}; + + +class _Mutex : public Reference { + + OBJ_TYPE(_Mutex,Reference); + Mutex *mutex; + + static void _bind_methods(); +public: + + void lock(); + Error try_lock(); + void unlock(); + + _Mutex(); + ~_Mutex(); +}; + +class _Semaphore : public Reference { + + OBJ_TYPE(_Semaphore,Reference); + Semaphore *semaphore; + + static void _bind_methods(); +public: + + Error wait(); + Error post(); + + _Semaphore(); + ~_Semaphore(); +}; + +class _Thread : public Reference { + + OBJ_TYPE(_Thread,Reference); + +protected: + + Variant ret; + Variant userdata; + volatile bool active; + Object *target_instance; + StringName target_method; + Thread *thread; + static void _bind_methods(); + static void _start_func(void *ud); +public: + + enum Priority { + + PRIORITY_LOW, + PRIORITY_NORMAL, + PRIORITY_HIGH + }; + + Error start(Object *p_instance,const StringName& p_method,const Variant& p_userdata=Variant(),int p_priority=PRIORITY_NORMAL); + String get_id() const; + bool is_active() const; + Variant wait_to_finish(); + + _Thread(); + ~_Thread(); +}; + +#endif // CORE_BIND_H diff --git a/core/color.cpp b/core/color.cpp new file mode 100644 index 00000000000..1528db6aaa5 --- /dev/null +++ b/core/color.cpp @@ -0,0 +1,377 @@ +/*************************************************************************/ +/* color.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 "color.h" +#include "math_funcs.h" +#include "print_string.h" + +uint32_t Color::to_ARGB32() const { + + uint32_t c=(uint8_t)(a*255); + c<<=8; + c|=(uint8_t)(r*255); + c<<=8; + c|=(uint8_t)(g*255); + c<<=8; + c|=(uint8_t)(b*255); + + return c; +} + +uint32_t Color::to_32() const { + + uint32_t c=(uint8_t)(a*255); + c<<=8; + c|=(uint8_t)(r*255); + c<<=8; + c|=(uint8_t)(g*255); + c<<=8; + c|=(uint8_t)(b*255); + + return c; +} + +float Color::get_h() const { + + float min = MIN( r, g ); + min = MIN( min, b ); + float max = MAX( r, g ); + max = MAX( max, b ); + + float delta = max - min; + + if( delta == 0 ) + return 0; + + float h; + if( r == max ) + h = ( g - b ) / delta; // between yellow & magenta + else if( g == max ) + h = 2 + ( b - r ) / delta; // between cyan & yellow + else + h = 4 + ( r - g ) / delta; // between magenta & cyan + + h/=6.0; + if (h<0) + h+=1.0; + + return h; +} + +float Color::get_s() const { + + + float min = MIN( r, g ); + min = MIN( min, b ); + float max = MAX( r, g ); + max = MAX( max, b ); + + float delta = max - min; + + return (max!=0) ? (delta / max) : 0; + +} + +float Color::get_v() const { + + float max = MAX( r, g ); + max = MAX( max, b ); + return max; +} + +void Color::set_hsv(float p_h, float p_s, float p_v, float p_alpha) { + + int i; + float f, p, q, t; + a=p_alpha; + + if( p_s == 0 ) { + // acp_hromatic (grey) + r = g = b = p_v; + return; + } + + p_h *=6.0; + i = Math::floor( p_h ); + f = p_h - i; + p = p_v * ( 1 - p_s ); + q = p_v * ( 1 - p_s * f ); + t = p_v * ( 1 - p_s * ( 1 - f ) ); + + switch( i ) { + case 0: + r = p_v; + g = t; + b = p; + break; + case 1: + r = q; + g = p_v; + b = p; + break; + case 2: + r = p; + g = p_v; + b = t; + break; + case 3: + r = p; + g = q; + b = p_v; + break; + case 4: + r = t; + g = p; + b = p_v; + break; + default: // cap_se 5: + r = p_v; + g = p; + b = q; + break; + } +} + +void Color::invert() { + + r=1.0-r; + g=1.0-g; + g=1.0-b; +} +void Color::contrast() { + + r=Math::fmod(r+0.5,1.0); + g=Math::fmod(g+0.5,1.0); + b=Math::fmod(b+0.5,1.0); +} + +Color Color::hex(uint32_t p_hex) { + + float a = (p_hex&0xFF)/255.0; + p_hex>>=8; + float b = (p_hex&0xFF)/255.0; + p_hex>>=8; + float g = (p_hex&0xFF)/255.0; + p_hex>>=8; + float r = (p_hex&0xFF)/255.0; + + return Color(r,g,b,a); +} + +static float _parse_col(const String& p_str, int p_ofs) { + + int ig=0; + + for(int i=0;i<2;i++) { + + int c=p_str[i+p_ofs]; + int v=0; + + if (c>='0' && c<='9') { + v=c-'0'; + } else if (c>='a' && c<='f') { + v=c-'a'; + v+=10; + } else if (c>='A' && c<='F') { + v=c-'A'; + v+=10; + } else { + return -1; + } + + if (i==0) + ig+=v*16; + else + ig+=v; + + } + + return ig; + +} + +Color Color::inverted() const { + + Color c=*this; + c.invert(); + return c; +} + +Color Color::contrasted() const { + + Color c=*this; + c.contrasted(); + return c; +} + + +Color Color::html(const String& p_color) { + + String color = p_color; + if (color.length()==0) + return Color(); + if (color[0]=='#') + color=color.substr(1,color.length()-1); + + bool alpha=false; + + if (color.length()==8) { + alpha=true; + } else if (color.length()==6) { + alpha=false; + } else { + ERR_EXPLAIN("Invalid Color Code: "+p_color); + ERR_FAIL_V(Color()); + } + + int a=255; + if (alpha) { + a=_parse_col(color,0); + if (a<0) { + ERR_EXPLAIN("Invalid Color Code: "+p_color); + ERR_FAIL_V(Color()); + } + } + + int from=alpha?2:0; + + int r=_parse_col(color,from+0); + if (r<0) { + ERR_EXPLAIN("Invalid Color Code: "+p_color); + ERR_FAIL_V(Color()); + } + int g=_parse_col(color,from+2); + if (g<0) { + ERR_EXPLAIN("Invalid Color Code: "+p_color); + ERR_FAIL_V(Color()); + } + int b=_parse_col(color,from+4); + if (b<0) { + ERR_EXPLAIN("Invalid Color Code: "+p_color); + ERR_FAIL_V(Color()); + } + + return Color(r/255.0,g/255.0,b/255.0,a/255.0); +} + +bool Color::html_is_valid(const String& p_color) { + + String color = p_color; + + if (color.length()==0) + return false; + if (color[0]=='#') + color=color.substr(1,color.length()-1); + + bool alpha=false; + + if (color.length()==8) { + alpha=true; + } else if (color.length()==6) { + alpha=false; + } else { + return false; + } + + int a=255; + if (alpha) { + a=_parse_col(color,0); + if (a<0) { + return false; + } + } + + int from=alpha?2:0; + + int r=_parse_col(color,from+0); + if (r<0) { + return false; + } + int g=_parse_col(color,from+2); + if (g<0) { + return false; + } + int b=_parse_col(color,from+4); + if (b<0) { + return false; + } + + return true; + +} + + + +String _to_hex(float p_val) { + + int v = p_val * 255; + v = CLAMP(v,0,255); + String ret; + + for(int i=0;i<2;i++) { + + CharType c[2]={0,0}; + int lv = v&0xF; + if (lv<10) + c[0]='0'+lv; + else + c[0]='a'+lv-10; + + v>>=4; + String cs=(const CharType*)c; + ret = cs + ret; + } + + return ret; + +} + +String Color::to_html(bool p_alpha) const { + + String txt; + txt+=_to_hex(r); + txt+=_to_hex(g); + txt+=_to_hex(b); + if (p_alpha) + txt=_to_hex(a)+txt; + return txt; + +} + + +float Color::gray() const { + + return (r+g+b)/3.0; +} + +Color::operator String() const { + + return rtos(r)+", "+rtos(g)+", "+rtos(b)+", "+rtos(a); +} + + diff --git a/core/color.h b/core/color.h new file mode 100644 index 00000000000..9b5850f56b6 --- /dev/null +++ b/core/color.h @@ -0,0 +1,136 @@ +/*************************************************************************/ +/* color.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 COLOR_H +#define COLOR_H + +#include "ustring.h" +/** + @author Juan Linietsky +*/ +struct Color { + + union { + + struct { + float r; + float g; + float b; + float a; + }; + float components[4]; + }; + + bool operator==(const Color &p_color) const { return (r==p_color.r && g==p_color.g && b==p_color.b && a==p_color.a ); } + bool operator!=(const Color &p_color) const { return (r!=p_color.r || g!=p_color.g || b!=p_color.b || a!=p_color.a ); } + + uint32_t to_32() const; + uint32_t to_ARGB32() const; + float gray() const; + float get_h() const; + float get_s() const; + float get_v() const; + void set_hsv(float p_h, float p_s, float p_v, float p_alpha=1.0); + + _FORCE_INLINE_ float& operator[](int idx) { + return components[idx]; + } + _FORCE_INLINE_ const float& operator[](int idx) const { + return components[idx]; + } + + void invert(); + void contrast(); + Color inverted() const; + Color contrasted() const; + + _FORCE_INLINE_ Color linear_interpolate(const Color& p_b, float p_t) const { + + Color res=*this; + + res.r+= (p_t * (p_b.r-r)); + res.g+= (p_t * (p_b.g-g)); + res.b+= (p_t * (p_b.b-b)); + res.a+= (p_t * (p_b.a-a)); + + return res; + } + + _FORCE_INLINE_ Color blend(const Color& p_over) const { + + + Color res; + float sa = 1.0 - p_over.a; + res.a = a*sa+p_over.a; + if (res.a==0) { + return Color(0,0,0,0); + } else { + res.r = (r*a*sa + p_over.r * p_over.a)/res.a; + res.g = (g*a*sa + p_over.g * p_over.a)/res.a; + res.b = (b*a*sa + p_over.b * p_over.a)/res.a; + } + return res; + } + + static Color hex(uint32_t p_hex); + static Color html(const String& p_color); + static bool html_is_valid(const String& p_color); + String to_html(bool p_alpha=true) const; + + _FORCE_INLINE_ bool operator<(const Color& p_color) const; //used in set keys + operator String() const; + + /** + * No construct parameters, r=0, g=0, b=0. a=255 + */ + _FORCE_INLINE_ Color() { + r=0; g=0; b=0; a=1.0; + } + + /** + * RGB / RGBA construct parameters. Alpha is optional, but defaults to 1.0 + */ + _FORCE_INLINE_ Color(float p_r,float p_g,float p_b,float p_a=1.0) { r=p_r; g=p_g; b=p_b; a=p_a; } +}; + +bool Color::operator<(const Color& p_color) const { + + if (r==p_color.r) { + if (g==p_color.g) { + if(b==p_color.b) { + return (alock(); +} + +void CommandQueueMT::unlock() { + + if (mutex) + mutex->unlock(); +} + +void CommandQueueMT::wait_for_flush() { + + // wait one millisecond for a flush to happen + OS::get_singleton()->delay_usec(1000); +} + +CommandQueueMT::SyncSemaphore* CommandQueueMT::_alloc_sync_sem() { + + int idx=-1; + + while(true) { + + for(int i=0;i +*/ + +class CommandQueueMT { + + struct SyncSemaphore { + + Semaphore *sem; + bool in_use; + }; + + struct CommandBase { + + virtual void call()=0; + virtual ~CommandBase() {}; + }; + + template + struct Command0 : public CommandBase { + + T*instance; + M method; + + virtual void call() { (instance->*method)(); } + }; + + template + struct Command1 : public CommandBase { + + T*instance; + M method; + typename GetSimpleTypeT::type_t p1; + + virtual void call() { (instance->*method)(p1); } + }; + + template + struct Command2 : public CommandBase { + + T*instance; + M method; + typename GetSimpleTypeT::type_t p1; + typename GetSimpleTypeT::type_t p2; + + virtual void call() { (instance->*method)(p1,p2); } + }; + + template + struct Command3 : public CommandBase { + + T*instance; + M method; + typename GetSimpleTypeT::type_t p1; + typename GetSimpleTypeT::type_t p2; + typename GetSimpleTypeT::type_t p3; + + virtual void call() { (instance->*method)(p1,p2,p3); } + }; + + template + struct Command4 : public CommandBase { + + T*instance; + M method; + typename GetSimpleTypeT::type_t p1; + typename GetSimpleTypeT::type_t p2; + typename GetSimpleTypeT::type_t p3; + typename GetSimpleTypeT::type_t p4; + + virtual void call() { (instance->*method)(p1,p2,p3,p4); } + }; + + template + struct Command5 : public CommandBase { + + T*instance; + M method; + typename GetSimpleTypeT::type_t p1; + typename GetSimpleTypeT::type_t p2; + typename GetSimpleTypeT::type_t p3; + typename GetSimpleTypeT::type_t p4; + typename GetSimpleTypeT::type_t p5; + + virtual void call() { (instance->*method)(p1,p2,p3,p4,p5); } + }; + + template + struct Command6 : public CommandBase { + + T*instance; + M method; + typename GetSimpleTypeT::type_t p1; + typename GetSimpleTypeT::type_t p2; + typename GetSimpleTypeT::type_t p3; + typename GetSimpleTypeT::type_t p4; + typename GetSimpleTypeT::type_t p5; + typename GetSimpleTypeT::type_t p6; + + virtual void call() { (instance->*method)(p1,p2,p3,p4,p5,p6); } + }; + + template + struct Command7 : public CommandBase { + + T*instance; + M method; + typename GetSimpleTypeT::type_t p1; + typename GetSimpleTypeT::type_t p2; + typename GetSimpleTypeT::type_t p3; + typename GetSimpleTypeT::type_t p4; + typename GetSimpleTypeT::type_t p5; + typename GetSimpleTypeT::type_t p6; + typename GetSimpleTypeT::type_t p7; + + virtual void call() { (instance->*method)(p1,p2,p3,p4,p5,p6,p7); } + }; + + /* comands that return */ + + template + struct CommandRet0 : public CommandBase { + + T*instance; + M method; + R* ret; + SyncSemaphore *sync; + + virtual void call() { *ret = (instance->*method)(); sync->sem->post(); sync->in_use=false; ; } + }; + + template + struct CommandRet1 : public CommandBase { + + T*instance; + M method; + typename GetSimpleTypeT::type_t p1; + R* ret; + SyncSemaphore *sync; + + virtual void call() { *ret = (instance->*method)(p1); sync->sem->post(); sync->in_use=false; ; } + }; + + template + struct CommandRet2 : public CommandBase { + + T*instance; + M method; + typename GetSimpleTypeT::type_t p1; + typename GetSimpleTypeT::type_t p2; + R* ret; + SyncSemaphore *sync; + + virtual void call() { *ret = (instance->*method)(p1,p2); sync->sem->post(); sync->in_use=false; ; } + }; + + template + struct CommandRet3 : public CommandBase { + + T*instance; + M method; + typename GetSimpleTypeT::type_t p1; + typename GetSimpleTypeT::type_t p2; + typename GetSimpleTypeT::type_t p3; + R* ret; + SyncSemaphore *sync; + + virtual void call() { *ret = (instance->*method)(p1,p2,p3); sync->sem->post(); sync->in_use=false; ; } + }; + + template + struct CommandRet4 : public CommandBase { + + T*instance; + M method; + typename GetSimpleTypeT::type_t p1; + typename GetSimpleTypeT::type_t p2; + typename GetSimpleTypeT::type_t p3; + typename GetSimpleTypeT::type_t p4; + R* ret; + SyncSemaphore *sync; + + virtual void call() { *ret = (instance->*method)(p1,p2,p3,p4); sync->sem->post(); sync->in_use=false; ; } + }; + + template + struct CommandRet5 : public CommandBase { + + T*instance; + M method; + typename GetSimpleTypeT::type_t p1; + typename GetSimpleTypeT::type_t p2; + typename GetSimpleTypeT::type_t p3; + typename GetSimpleTypeT::type_t p4; + typename GetSimpleTypeT::type_t p5; + R* ret; + SyncSemaphore *sync; + + virtual void call() { *ret = (instance->*method)(p1,p2,p3,p4,p5); sync->sem->post(); sync->in_use=false; ; } + }; + + template + struct CommandRet6 : public CommandBase { + + T*instance; + M method; + typename GetSimpleTypeT::type_t p1; + typename GetSimpleTypeT::type_t p2; + typename GetSimpleTypeT::type_t p3; + typename GetSimpleTypeT::type_t p4; + typename GetSimpleTypeT::type_t p5; + typename GetSimpleTypeT::type_t p6; + R* ret; + SyncSemaphore *sync; + + virtual void call() { *ret = (instance->*method)(p1,p2,p3,p4,p5,p6); sync->sem->post(); sync->in_use=false; ; } + }; + + template + struct CommandRet7 : public CommandBase { + + T*instance; + M method; + typename GetSimpleTypeT::type_t p1; + typename GetSimpleTypeT::type_t p2; + typename GetSimpleTypeT::type_t p3; + typename GetSimpleTypeT::type_t p4; + typename GetSimpleTypeT::type_t p5; + typename GetSimpleTypeT::type_t p6; + typename GetSimpleTypeT::type_t p7; + R* ret; + SyncSemaphore *sync; + + virtual void call() { *ret = (instance->*method)(p1,p2,p3,p4,p5,p6,p7); sync->sem->post(); sync->in_use=false; ; } + }; + + /** commands that don't return but sync */ + + /* comands that return */ + + template + struct CommandSync0 : public CommandBase { + + T*instance; + M method; + + SyncSemaphore *sync; + + virtual void call() { (instance->*method)(); sync->sem->post(); sync->in_use=false; ; } + }; + + template + struct CommandSync1 : public CommandBase { + + T*instance; + M method; + typename GetSimpleTypeT::type_t p1; + + SyncSemaphore *sync; + + virtual void call() { (instance->*method)(p1); sync->sem->post(); sync->in_use=false; ; } + }; + + template + struct CommandSync2 : public CommandBase { + + T*instance; + M method; + typename GetSimpleTypeT::type_t p1; + typename GetSimpleTypeT::type_t p2; + + SyncSemaphore *sync; + + virtual void call() { (instance->*method)(p1,p2); sync->sem->post(); sync->in_use=false; ; } + }; + + template + struct CommandSync3 : public CommandBase { + + T*instance; + M method; + typename GetSimpleTypeT::type_t p1; + typename GetSimpleTypeT::type_t p2; + typename GetSimpleTypeT::type_t p3; + + SyncSemaphore *sync; + + virtual void call() { (instance->*method)(p1,p2,p3); sync->sem->post(); sync->in_use=false; ; } + }; + + template + struct CommandSync4 : public CommandBase { + + T*instance; + M method; + typename GetSimpleTypeT::type_t p1; + typename GetSimpleTypeT::type_t p2; + typename GetSimpleTypeT::type_t p3; + typename GetSimpleTypeT::type_t p4; + + SyncSemaphore *sync; + + virtual void call() { (instance->*method)(p1,p2,p3,p4); sync->sem->post(); sync->in_use=false; ; } + }; + + template + struct CommandSync5 : public CommandBase { + + T*instance; + M method; + typename GetSimpleTypeT::type_t p1; + typename GetSimpleTypeT::type_t p2; + typename GetSimpleTypeT::type_t p3; + typename GetSimpleTypeT::type_t p4; + typename GetSimpleTypeT::type_t p5; + + SyncSemaphore *sync; + + virtual void call() { (instance->*method)(p1,p2,p3,p4,p5); sync->sem->post(); sync->in_use=false; ; } + }; + + template + struct CommandSync6 : public CommandBase { + + T*instance; + M method; + typename GetSimpleTypeT::type_t p1; + typename GetSimpleTypeT::type_t p2; + typename GetSimpleTypeT::type_t p3; + typename GetSimpleTypeT::type_t p4; + typename GetSimpleTypeT::type_t p5; + typename GetSimpleTypeT::type_t p6; + + SyncSemaphore *sync; + + virtual void call() { (instance->*method)(p1,p2,p3,p4,p5,p6); sync->sem->post(); sync->in_use=false; ; } + }; + + template + struct CommandSync7 : public CommandBase { + + T*instance; + M method; + typename GetSimpleTypeT::type_t p1; + typename GetSimpleTypeT::type_t p2; + typename GetSimpleTypeT::type_t p3; + typename GetSimpleTypeT::type_t p4; + typename GetSimpleTypeT::type_t p5; + typename GetSimpleTypeT::type_t p6; + typename GetSimpleTypeT::type_t p7; + + SyncSemaphore *sync; + + virtual void call() { (instance->*method)(p1,p2,p3,p4,p5,p6,p7); sync->sem->post(); sync->in_use=false; ; } + }; + + /***** BASE *******/ + + enum { + COMMAND_MEM_SIZE_KB=256, + COMMAND_MEM_SIZE=COMMAND_MEM_SIZE_KB*1024, + SYNC_SEMAPHORES=8 + }; + + + uint8_t command_mem[COMMAND_MEM_SIZE]; + uint32_t read_ptr; + uint32_t write_ptr; + SyncSemaphore sync_sems[SYNC_SEMAPHORES]; + Mutex *mutex; + Semaphore *sync; + + + template + T* allocate() { + + // alloc size is size+T+safeguard + uint32_t alloc_size=sizeof(T)+sizeof(uint32_t); + + tryagain: + + if (write_ptr < read_ptr) { + // behind read_ptr, check that there is room + if ( (read_ptr-write_ptr) <= alloc_size ) + return NULL; + } else if (write_ptr >= read_ptr) { + // ahead of read_ptr, check that there is room + + + if ( (COMMAND_MEM_SIZE-write_ptr) < alloc_size+4 ) { + // no room at the end, wrap down; + + if (read_ptr==0) // dont want write_ptr to become read_ptr + return NULL; + + // if this happens, it's a bug + ERR_FAIL_COND_V( (COMMAND_MEM_SIZE-write_ptr) < sizeof(uint32_t), NULL ); + // zero means, wrap to begining + + uint32_t * p = (uint32_t*)&command_mem[write_ptr]; + *p=0; + write_ptr=0; + goto tryagain; + } + } + // allocate the size + uint32_t * p = (uint32_t*)&command_mem[write_ptr]; + *p=sizeof(T); + write_ptr+=sizeof(uint32_t); + // allocate the command + T* cmd = memnew_placement( &command_mem[write_ptr], T ); + write_ptr+=sizeof(T); + return cmd; + + } + + template + T* allocate_and_lock() { + + lock(); + T* ret; + + while ( (ret=allocate())==NULL ) { + + unlock(); + // sleep a little until fetch happened and some room is made + wait_for_flush(); + lock(); + + } + + return ret; + } + + + bool flush_one() { + + tryagain: + + // tried to read an empty queue + if (read_ptr == write_ptr ) + return false; + + uint32_t size = *(uint32_t*)( &command_mem[read_ptr] ); + + if (size==0) { + //end of ringbuffer, wrap + read_ptr=0; + goto tryagain; + } + + read_ptr+=sizeof(uint32_t); + + CommandBase *cmd = reinterpret_cast( &command_mem[read_ptr] ); + + cmd->call(); + cmd->~CommandBase(); + + read_ptr+=size; + + return true; + } + + + void lock(); + void unlock(); + void wait_for_flush(); + SyncSemaphore* _alloc_sync_sem(); + + +public: + + /* NORMAL PUSH COMMANDS */ + + template + void push( T * p_instance, M p_method ) { + + Command0 * cmd = allocate_and_lock< Command0 >(); + + cmd->instance=p_instance; + cmd->method=p_method; + + unlock(); + + if (sync) sync->post(); + } + + template + void push( T * p_instance, M p_method, P1 p1 ) { + + Command1 * cmd = allocate_and_lock< Command1 >(); + + cmd->instance=p_instance; + cmd->method=p_method; + cmd->p1=p1; + + unlock(); + + if (sync) sync->post(); + } + + template + void push( T * p_instance, M p_method, P1 p1, P2 p2 ) { + + Command2 * cmd = allocate_and_lock< Command2 >(); + + cmd->instance=p_instance; + cmd->method=p_method; + cmd->p1=p1; + cmd->p2=p2; + + unlock(); + + if (sync) sync->post(); + } + + template + void push( T * p_instance, M p_method, P1 p1, P2 p2, P3 p3 ) { + + Command3 * cmd = allocate_and_lock< Command3 >(); + + cmd->instance=p_instance; + cmd->method=p_method; + cmd->p1=p1; + cmd->p2=p2; + cmd->p3=p3; + + unlock(); + + if (sync) sync->post(); + } + + template + void push( T * p_instance, M p_method, P1 p1, P2 p2, P3 p3, P4 p4 ) { + + Command4 * cmd = allocate_and_lock< Command4 >(); + + cmd->instance=p_instance; + cmd->method=p_method; + cmd->p1=p1; + cmd->p2=p2; + cmd->p3=p3; + cmd->p4=p4; + + unlock(); + + if (sync) sync->post(); + } + + template + void push( T * p_instance, M p_method, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5 ) { + + Command5 * cmd = allocate_and_lock< Command5 >(); + + cmd->instance=p_instance; + cmd->method=p_method; + cmd->p1=p1; + cmd->p2=p2; + cmd->p3=p3; + cmd->p4=p4; + cmd->p5=p5; + + unlock(); + + if (sync) sync->post(); + } + + template + void push( T * p_instance, M p_method, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6 ) { + + Command6 * cmd = allocate_and_lock< Command6 >(); + + cmd->instance=p_instance; + cmd->method=p_method; + cmd->p1=p1; + cmd->p2=p2; + cmd->p3=p3; + cmd->p4=p4; + cmd->p5=p5; + cmd->p6=p6; + + unlock(); + + if (sync) sync->post(); + } + + template + void push( T * p_instance, M p_method, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7 ) { + + Command7 * cmd = allocate_and_lock< Command7 >(); + + cmd->instance=p_instance; + cmd->method=p_method; + cmd->p1=p1; + cmd->p2=p2; + cmd->p3=p3; + cmd->p4=p4; + cmd->p5=p5; + cmd->p6=p6; + cmd->p7=p7; + + unlock(); + + if (sync) sync->post(); + } + /*** PUSH AND RET COMMANDS ***/ + + + template + void push_and_ret( T * p_instance, M p_method, R* r_ret) { + + CommandRet0 * cmd = allocate_and_lock< CommandRet0 >(); + + cmd->instance=p_instance; + cmd->method=p_method; + cmd->ret=r_ret; + SyncSemaphore *ss=_alloc_sync_sem(); + cmd->sync=ss; + + unlock(); + + if (sync) sync->post(); + ss->sem->wait(); + } + + template + void push_and_ret( T * p_instance, M p_method, P1 p1, R* r_ret) { + + CommandRet1 * cmd = allocate_and_lock< CommandRet1 >(); + + cmd->instance=p_instance; + cmd->method=p_method; + cmd->p1=p1; + cmd->ret=r_ret; + SyncSemaphore *ss=_alloc_sync_sem(); + cmd->sync=ss; + + unlock(); + + if (sync) sync->post(); + ss->sem->wait(); + } + + template + void push_and_ret( T * p_instance, M p_method, P1 p1, P2 p2, R* r_ret) { + + CommandRet2 * cmd = allocate_and_lock< CommandRet2 >(); + + cmd->instance=p_instance; + cmd->method=p_method; + cmd->p1=p1; + cmd->p2=p2; + cmd->ret=r_ret; + SyncSemaphore *ss=_alloc_sync_sem(); + cmd->sync=ss; + + unlock(); + + if (sync) sync->post(); + ss->sem->wait(); + } + + template + void push_and_ret( T * p_instance, M p_method, P1 p1, P2 p2, P3 p3, R* r_ret ) { + + CommandRet3 * cmd = allocate_and_lock< CommandRet3 >(); + + cmd->instance=p_instance; + cmd->method=p_method; + cmd->p1=p1; + cmd->p2=p2; + cmd->p3=p3; + cmd->ret=r_ret; + SyncSemaphore *ss=_alloc_sync_sem(); + cmd->sync=ss; + + unlock(); + + if (sync) sync->post(); + ss->sem->wait(); + } + + template + void push_and_ret( T * p_instance, M p_method, P1 p1, P2 p2, P3 p3, P4 p4, R* r_ret ) { + + CommandRet4 * cmd = allocate_and_lock< CommandRet4 >(); + + cmd->instance=p_instance; + cmd->method=p_method; + cmd->p1=p1; + cmd->p2=p2; + cmd->p3=p3; + cmd->p4=p4; + cmd->ret=r_ret; + SyncSemaphore *ss=_alloc_sync_sem(); + cmd->sync=ss; + + unlock(); + + if (sync) sync->post(); + ss->sem->wait(); + } + + template + void push_and_ret( T * p_instance, M p_method, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, R* r_ret ) { + + CommandRet5 * cmd = allocate_and_lock< CommandRet5 >(); + + cmd->instance=p_instance; + cmd->method=p_method; + cmd->p1=p1; + cmd->p2=p2; + cmd->p3=p3; + cmd->p4=p4; + cmd->p5=p5; + cmd->ret=r_ret; + SyncSemaphore *ss=_alloc_sync_sem(); + cmd->sync=ss; + + unlock(); + + if (sync) sync->post(); + ss->sem->wait(); + } + + template + void push_and_ret( T * p_instance, M p_method, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, R* r_ret ) { + + CommandRet6 * cmd = allocate_and_lock< CommandRet6 >(); + + cmd->instance=p_instance; + cmd->method=p_method; + cmd->p1=p1; + cmd->p2=p2; + cmd->p3=p3; + cmd->p4=p4; + cmd->p5=p5; + cmd->p6=p6; + cmd->ret=r_ret; + SyncSemaphore *ss=_alloc_sync_sem(); + cmd->sync=ss; + + unlock(); + + if (sync) sync->post(); + ss->sem->wait(); + } + + template + void push_and_ret( T * p_instance, M p_method, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6,P7 p7, R* r_ret ) { + + CommandRet7 * cmd = allocate_and_lock< CommandRet7 >(); + + cmd->instance=p_instance; + cmd->method=p_method; + cmd->p1=p1; + cmd->p2=p2; + cmd->p3=p3; + cmd->p4=p4; + cmd->p5=p5; + cmd->p6=p6; + cmd->p7=p7; + cmd->ret=r_ret; + SyncSemaphore *ss=_alloc_sync_sem(); + cmd->sync=ss; + + unlock(); + + if (sync) sync->post(); + ss->sem->wait(); + } + + + template + void push_and_sync( T * p_instance, M p_method) { + + CommandSync0 * cmd = allocate_and_lock< CommandSync0 >(); + + cmd->instance=p_instance; + cmd->method=p_method; + + SyncSemaphore *ss=_alloc_sync_sem(); + cmd->sync=ss; + + unlock(); + + if (sync) sync->post(); + ss->sem->wait(); + } + + template + void push_and_sync( T * p_instance, M p_method, P1 p1) { + + CommandSync1 * cmd = allocate_and_lock< CommandSync1 >(); + + cmd->instance=p_instance; + cmd->method=p_method; + cmd->p1=p1; + + SyncSemaphore *ss=_alloc_sync_sem(); + cmd->sync=ss; + + unlock(); + + if (sync) sync->post(); + ss->sem->wait(); + } + + template + void push_and_sync( T * p_instance, M p_method, P1 p1, P2 p2) { + + CommandSync2 * cmd = allocate_and_lock< CommandSync2 >(); + + cmd->instance=p_instance; + cmd->method=p_method; + cmd->p1=p1; + cmd->p2=p2; + + SyncSemaphore *ss=_alloc_sync_sem(); + cmd->sync=ss; + + unlock(); + + if (sync) sync->post(); + ss->sem->wait(); + } + + template + void push_and_sync( T * p_instance, M p_method, P1 p1, P2 p2, P3 p3 ) { + + CommandSync3 * cmd = allocate_and_lock< CommandSync3 >(); + + cmd->instance=p_instance; + cmd->method=p_method; + cmd->p1=p1; + cmd->p2=p2; + cmd->p3=p3; + + SyncSemaphore *ss=_alloc_sync_sem(); + cmd->sync=ss; + + unlock(); + + if (sync) sync->post(); + ss->sem->wait(); + } + + template + void push_and_sync( T * p_instance, M p_method, P1 p1, P2 p2, P3 p3, P4 p4 ) { + + CommandSync4 * cmd = allocate_and_lock< CommandSync4 >(); + + cmd->instance=p_instance; + cmd->method=p_method; + cmd->p1=p1; + cmd->p2=p2; + cmd->p3=p3; + cmd->p4=p4; + + SyncSemaphore *ss=_alloc_sync_sem(); + cmd->sync=ss; + + unlock(); + + if (sync) sync->post(); + ss->sem->wait(); + } + + template + void push_and_sync( T * p_instance, M p_method, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5 ) { + + CommandSync5 * cmd = allocate_and_lock< CommandSync5 >(); + + cmd->instance=p_instance; + cmd->method=p_method; + cmd->p1=p1; + cmd->p2=p2; + cmd->p3=p3; + cmd->p4=p4; + cmd->p5=p5; + + SyncSemaphore *ss=_alloc_sync_sem(); + cmd->sync=ss; + + unlock(); + + if (sync) sync->post(); + ss->sem->wait(); + } + + template + void push_and_sync( T * p_instance, M p_method, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6 ) { + + CommandSync6 * cmd = allocate_and_lock< CommandSync6 >(); + + cmd->instance=p_instance; + cmd->method=p_method; + cmd->p1=p1; + cmd->p2=p2; + cmd->p3=p3; + cmd->p4=p4; + cmd->p5=p5; + cmd->p6=p6; + + SyncSemaphore *ss=_alloc_sync_sem(); + cmd->sync=ss; + + unlock(); + + if (sync) sync->post(); + ss->sem->wait(); + } + + template + void push_and_sync( T * p_instance, M p_method, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6,P7 p7 ) { + + CommandSync7 * cmd = allocate_and_lock< CommandSync7 >(); + + cmd->instance=p_instance; + cmd->method=p_method; + cmd->p1=p1; + cmd->p2=p2; + cmd->p3=p3; + cmd->p4=p4; + cmd->p5=p5; + cmd->p6=p6; + cmd->p7=p7; + + SyncSemaphore *ss=_alloc_sync_sem(); + cmd->sync=ss; + + unlock(); + + if (sync) sync->post(); + ss->sem->wait(); + } + + void wait_and_flush_one() { + ERR_FAIL_COND(!sync); + sync->wait(); + lock(); + flush_one(); + unlock(); + } + + void flush_all() { + + ERR_FAIL_COND(sync); + lock(); + while (true) { + bool exit = !flush_one(); + if (exit) + break; + } + unlock(); + } + + CommandQueueMT(bool p_sync); + ~CommandQueueMT(); + +}; + +#endif diff --git a/core/compressed_translation.cpp b/core/compressed_translation.cpp new file mode 100644 index 00000000000..0c97f459f81 --- /dev/null +++ b/core/compressed_translation.cpp @@ -0,0 +1,534 @@ +/*************************************************************************/ +/* compressed_translation.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 "compressed_translation.h" +#include "pair.h" +#include + +/////////// SMAZ ///////////// + +/* +Copyright (c) 2006-2009, Salvatore Sanfilippo +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of Smaz nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +/* Our compression codebook, used for compression */ +static const char *Smaz_cb[241] = { +"\002s,\266", "\003had\232\002leW", "\003on \216", "", "\001yS", +"\002ma\255\002li\227", "\003or \260", "", "\002ll\230\003s t\277", +"\004fromg\002mel", "", "\003its\332", "\001z\333", "\003ingF", "\001>\336", +"\001 \000\003 (\002nc\344", "\002nd=\003 on\312", +"\002ne\213\003hat\276\003re q", "", "\002ngT\003herz\004have\306\003s o\225", +"", "\003ionk\003s a\254\002ly\352", "\003hisL\003 inN\003 be\252", "", +"\003 fo\325\003 of \003 ha\311", "", "\002of\005", +"\003 co\241\002no\267\003 ma\370", "", "", "\003 cl\356\003enta\003 an7", +"\002ns\300\001\"e", "\003n t\217\002ntP\003s, \205", +"\002pe\320\003 we\351\002om\223", "\002on\037", "", "\002y G", "\003 wa\271", +"\003 re\321\002or*", "", "\002=\"\251\002ot\337", "\003forD\002ou[", +"\003 toR", "\003 th\r", "\003 it\366", +"\003but\261\002ra\202\003 wi\363\002<\346", "\002to\024", "\003arew", "\001d\030", +"\002tr\303", "", "\001\n1\003 a \222", "\003f tv\002veo", "\002un\340", "", +"\003e o\242", "\002a \243\002wa\326\001e\002", "\002ur\226\003e a\274", +"\002us\244\003\n\r\n\247", "\002ut\304\003e c\373", "\002we\221", "", "", +"\002wh\302", "\001f,", "", "", "", "\003d t\206", "", "", "\003th \343", +"\001g;", "", "", "\001\r9\003e s\265", "\003e t\234", "", "\003to Y", +"\003e\r\n\236", "\002d \036\001h\022", "", "\001,Q", "\002 a\031", "\002 b^", +"\002\r\n\025\002 cI", "\002 d\245", "\002 e\253", "\002 fh\001i\b\002e \v", +"", "\002 hU\001-\314", "\002 i8", "", "", "\002 l\315", "\002 m{", +"\002f :\002 n\354", "\002 o\035", "\002 p}\001.n\003\r\n\r\250", "", +"\002 r\275", "\002 s>", "\002 t\016", "", "\002g \235\005which+\003whi\367", +"\002 w5", "\001/\305", "\003as \214", "\003at \207", "", "\003who\331", "", +"\001l\026\002h \212", "", "\002, $", "", "\004withV", "", "", "", "\001m-", "", +"", "\002ac\357", "\002ad\350", "\003TheH", "", "", "\004this\233\001n\t", +"", "\002. y", "", "\002alX\003e, \365", "\003tio\215\002be\\", +"\002an\032\003ver\347", "", "\004that0\003tha\313\001o\006", "\003was2", +"\002arO", "\002as.", "\002at'\003the\001\004they\200\005there\322\005theird", +"\002ce\210", "\004were]", "", "\002ch\231\002l \264\001p<", "", "", +"\003one\256", "", "\003he \023\002dej", "\003ter\270", "\002cou", "", +"\002by\177\002di\201\002eax", "", "\002ec\327", "\002edB", "\002ee\353", "", +"", "\001r\f\002n )", "", "", "", "\002el\262", "", "\003in i\002en3", "", +"\002o `\001s\n", "", "\002er\033", "\003is t\002es6", "", "\002ge\371", +"\004.com\375", "\002fo\334\003our\330", "\003ch \301\001t\003", "\002hab", "", +"\003men\374", "", "\002he\020", "", "", "\001u&", "\002hif", "", +"\003not\204\002ic\203", "\003ed @\002id\355", "", "", "\002ho\273", +"\002r K\001vm", "", "", "", "\003t t\257\002il\360", "\002im\342", +"\003en \317\002in\017", "\002io\220", "\002s \027\001wA", "", "\003er |", +"\003es ~\002is%", "\002it/", "", "\002iv\272", "", +"\002t #\ahttp://C\001x\372", "\002la\211", "\001<\341", "\003, a\224" +}; + +/* Reverse compression codebook, used for decompression */ +static const char *Smaz_rcb[254] = { +" ", "the", "e", "t", "a", "of", "o", "and", "i", "n", "s", "e ", "r", " th", +" t", "in", "he", "th", "h", "he ", "to", "\r\n", "l", "s ", "d", " a", "an", +"er", "c", " o", "d ", "on", " of", "re", "of ", "t ", ", ", "is", "u", "at", +" ", "n ", "or", "which", "f", "m", "as", "it", "that", "\n", "was", "en", +" ", " w", "es", " an", " i", "\r", "f ", "g", "p", "nd", " s", "nd ", "ed ", +"w", "ed", "http://", "for", "te", "ing", "y ", "The", " c", "ti", "r ", "his", +"st", " in", "ar", "nt", ",", " to", "y", "ng", " h", "with", "le", "al", "to ", +"b", "ou", "be", "were", " b", "se", "o ", "ent", "ha", "ng ", "their", "\"", +"hi", "from", " f", "in ", "de", "ion", "me", "v", ".", "ve", "all", "re ", +"ri", "ro", "is ", "co", "f t", "are", "ea", ". ", "her", " m", "er ", " p", +"es ", "by", "they", "di", "ra", "ic", "not", "s, ", "d t", "at ", "ce", "la", +"h ", "ne", "as ", "tio", "on ", "n t", "io", "we", " a ", "om", ", a", "s o", +"ur", "li", "ll", "ch", "had", "this", "e t", "g ", "e\r\n", " wh", "ere", +" co", "e o", "a ", "us", " d", "ss", "\n\r\n", "\r\n\r", "=\"", " be", " e", +"s a", "ma", "one", "t t", "or ", "but", "el", "so", "l ", "e s", "s,", "no", +"ter", " wa", "iv", "ho", "e a", " r", "hat", "s t", "ns", "ch ", "wh", "tr", +"ut", "/", "have", "ly ", "ta", " ha", " on", "tha", "-", " l", "ati", "en ", +"pe", " re", "there", "ass", "si", " fo", "wa", "ec", "our", "who", "its", "z", +"fo", "rs", ">", "ot", "un", "<", "im", "th ", "nc", "ate", "><", "ver", "ad", +" we", "ly", "ee", " n", "id", " cl", "ac", "il", " 1) h2 += in[1]; + if (inlen > 2) h3 = h2^in[2]; + if (j > inlen) j = inlen; + + /* Try to lookup substrings into the hash table, starting from the +* longer to the shorter substrings */ + for (; j > 0; j--) { + switch(j) { + case 1: slot = Smaz_cb[h1%241]; break; + case 2: slot = Smaz_cb[h2%241]; break; + default: slot = Smaz_cb[h3%241]; break; + } + while(slot[0]) { + if (slot[0] == j && memcmp(slot+1,in,j) == 0) { + /* Match found in the hash table, +* prepare a verbatim bytes flush if needed */ + if (verblen) { + needed = (verblen == 1) ? 2 : 2+verblen; + flush = out; + out += needed; + outlen -= needed; + } + /* Emit the byte */ + if (outlen <= 0) return _outlen+1; + out[0] = slot[slot[0]+1]; + out++; + outlen--; + inlen -= j; + in += j; + goto out; + } else { + slot += slot[0]+2; + } + } + } + /* Match not found - add the byte to the verbatim buffer */ + verb[verblen] = in[0]; + verblen++; + inlen--; + in++; +out: + /* Prepare a flush if we reached the flush length limit, and there +* is not already a pending flush operation. */ + if (!flush && (verblen == 256 || (verblen > 0 && inlen == 0))) { + needed = (verblen == 1) ? 2 : 2+verblen; + flush = out; + out += needed; + outlen -= needed; + if (outlen < 0) return _outlen+1; + } + /* Perform a verbatim flush if needed */ + if (flush) { + if (verblen == 1) { + flush[0] = (signed char)254; + flush[1] = verb[0]; + } else { + flush[0] = (signed char)255; + flush[1] = (signed char)(verblen-1); + memcpy(flush+2,verb,verblen); + } + flush = NULL; + verblen = 0; + } + } + return out-_out; +} + +static int smaz_decompress(const char *in, int inlen, char *out, int outlen) { + unsigned char *c = (unsigned char*) in; + char *_out = out; + int _outlen = outlen; + + while(inlen) { + if (*c == 254) { + /* Verbatim byte */ + if (outlen < 1) return _outlen+1; + *out = *(c+1); + out++; + outlen--; + c += 2; + inlen -= 2; + } else if (*c == 255) { + /* Verbatim string */ + int len = (*(c+1))+1; + if (outlen < len) return _outlen+1; + memcpy(out,c+2,len); + out += len; + outlen -= len; + c += 2+len; + inlen -= 2+len; + } else { + /* Codebook entry */ + const char *s = Smaz_rcb[*c]; + int len = strlen(s); + + if (outlen < len) return _outlen+1; + memcpy(out,s,len); + out += len; + outlen -= len; + c++; + inlen--; + } + } + return out-_out; +} + + +/////////// END OF SMAZ ///////////// + +struct _PHashTranslationCmp { + + int orig_len; + CharString compressed; + int offset; +}; + +void PHashTranslation::generate(const Ref &p_from) { +#ifdef TOOLS_ENABLED + List keys; + p_from->get_message_list(&keys); + + int size=Math::larger_prime(keys.size()); + + + print_line("compressing keys: "+itos(keys.size())); + Vector< Vector< Pair > > buckets; + Vector< Map< uint32_t, int > > table; + Vector< uint32_t > hfunc_table; + Vector< _PHashTranslationCmp > compressed; + + table.resize(size); + hfunc_table.resize(size); + buckets.resize(size); + compressed.resize(keys.size()); + + int idx=0; + int total_compression_size=0; + int total_string_size=0; + + for(List::Element *E=keys.front();E;E=E->next()) { + + //hash string + CharString cs = E->get().operator String().utf8(); + uint32_t h = hash(0,cs.get_data()); + Pair p; + p.first=idx; + p.second=cs; + buckets[h % size].push_back(p); + + //compress string + CharString src_s = p_from->get_message(E->get()).operator String().utf8(); + _PHashTranslationCmp ps; + ps.orig_len=src_s.size(); + ps.offset=total_compression_size; + + if (ps.orig_len!=0) { + CharString dst_s; + dst_s.resize(src_s.size()); + int ret = smaz_compress(src_s.get_data(),src_s.size(),&dst_s[0],src_s.size()); + if (ret>=src_s.size()) { + //if compressed is larger than original, just use original + ps.orig_len=src_s.size(); + ps.compressed=src_s; + } else { + dst_s.resize(ret); + //ps.orig_len=; + ps.compressed=dst_s; + } + } else { + ps.orig_len=1; + ps.compressed.resize(1); + ps.compressed[0]=0; + } + + + compressed[idx]=ps; + total_compression_size+=ps.compressed.size(); + total_string_size+=src_s.size(); + idx++; + } + + int bucket_table_size=0; + print_line("total compressed string size: "+itos(total_compression_size)+" ("+itos(total_string_size)+" uncompressed)."); + + for(int i=0;i > &b = buckets[i]; + Map< uint32_t, int > &t=table[i]; + + if (b.size()==0) + continue; + + //print_line("bucket: "+itos(i)+" - elements: "+itos(b.size())); + + int d = 1; + int item =0; + + while(item < b.size()) { + + uint32_t slot = hash(d,b[item].second.get_data()); + if (t.has(slot)) { + + item=0; + d++; + t.clear(); + } else { + t[slot]=b[item].first; + item++; + } + } + + hfunc_table[i]=d; + bucket_table_size+=2+b.size()*4; + + } + + + print_line("bucket table size: "+itos(bucket_table_size*4)); + print_line("hash table size: "+itos(size*4)); + + hash_table.resize(size); + bucket_table.resize(bucket_table_size); + + DVector::Write htwb = hash_table.write(); + DVector::Write btwb = bucket_table.write(); + + uint32_t *htw = (uint32_t*)&htwb[0]; + uint32_t *btw = (uint32_t*)&btwb[0]; + + int btindex=0; + int collisions=0; + + for(int i=0;i &t=table[i]; + if (t.size()==0) { + htw[i]=0xFFFFFFFF; //nothing + continue; + } else if (t.size()>1) { + collisions+=t.size()-1; + } + + htw[i]=btindex; + btw[btindex++]=t.size(); + btw[btindex++]=hfunc_table[i]; + + for( Map< uint32_t, int >::Element *E=t.front();E;E=E->next()) { + + btw[btindex++]=E->key(); + btw[btindex++]=compressed[E->get()].offset; + btw[btindex++]=compressed[E->get()].compressed.size(); + btw[btindex++]=compressed[E->get()].orig_len; + } + + } + + print_line("total collisions: "+itos(collisions)); + + strings.resize(total_compression_size); + DVector::Write cw = strings.write(); + + for(int i=0;iget_locale()); + +#endif +} + +bool PHashTranslation::_set(const StringName& p_name, const Variant& p_value) { + + String name = p_name.operator String(); + if (name=="hash_table") { + hash_table=p_value; + print_line("translation: loaded hash table of size: "+itos(hash_table.size())); + } else if (name=="bucket_table") { + bucket_table=p_value; + print_line("translation: loaded bucket table of size: "+itos(bucket_table.size())); + } else if (name=="strings") { + strings=p_value; + print_line("translation: loaded string table of size: "+itos(strings.size())); + } else if (name=="load_from") { + print_line("generating"); + generate(p_value); + } else + return false; + + return true; + +} + +bool PHashTranslation::_get(const StringName& p_name,Variant &r_ret) const{ + + String name = p_name.operator String(); + if (name=="hash_table") + r_ret=hash_table; + else if (name=="bucket_table") + r_ret=bucket_table; + else if (name=="strings") + r_ret=strings; + else + return false; + + return true; + +} + +StringName PHashTranslation::get_message(const StringName& p_src_text) const { + + int htsize = hash_table.size(); + + if (htsize==0) + return StringName(); + + CharString str = p_src_text.operator String().utf8(); + uint32_t h = hash(0,str.get_data()); + + + DVector::Read htr = hash_table.read(); + const uint32_t *htptr = (const uint32_t*)&htr[0]; + DVector::Read btr = bucket_table.read(); + const uint32_t *btptr = (const uint32_t*)&btr[0]; + DVector::Read sr = strings.read(); + const char *sptr= (const char*)&sr[0]; + + uint32_t p = htptr[ h % htsize]; + + //print_line("String: "+p_src_text.operator String()); + //print_line("Hash: "+itos(p)); + + if (p==0xFFFFFFFF) { +// print_line("GETMSG: Nothing!"); + return StringName(); //nothing + } + + const Bucket &bucket = *(const Bucket*)&btptr[p]; + + h = hash(bucket.func,str.get_data()); + + int idx=-1; + + for(int i=0;i *p_list) const{ + + p_list->push_back( PropertyInfo(Variant::INT_ARRAY, "hash_table")); + p_list->push_back( PropertyInfo(Variant::INT_ARRAY, "bucket_table")); + p_list->push_back( PropertyInfo(Variant::RAW_ARRAY, "strings")); + p_list->push_back( PropertyInfo(Variant::OBJECT, "load_from",PROPERTY_HINT_RESOURCE_TYPE,"Translation",PROPERTY_USAGE_EDITOR)); + +} +void PHashTranslation::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("generate","from:Translation"),&PHashTranslation::generate); +} + +PHashTranslation::PHashTranslation() +{ +} diff --git a/core/compressed_translation.h b/core/compressed_translation.h new file mode 100644 index 00000000000..a0b94116de9 --- /dev/null +++ b/core/compressed_translation.h @@ -0,0 +1,93 @@ +/*************************************************************************/ +/* compressed_translation.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 COMPRESSED_TRANSLATION_H +#define COMPRESSED_TRANSLATION_H + +#include "translation.h" + +class PHashTranslation : public Translation { + + OBJ_TYPE(PHashTranslation,Translation); + + + //this translation uses a sort of modified perfect hash algorithm + //it requieres hashing strings twice and then does a binary search, + //so it's slower, but at the same time it has an extreemly high chance + //of catching untranslated strings + + //load/store friendly types + DVector hash_table; + DVector bucket_table; + DVector strings; + + + struct Bucket { + + int size; + uint32_t func; + + struct Elem { + + uint32_t key; + uint32_t str_offset; + uint32_t comp_size; + uint32_t uncomp_size; + }; + + Elem elem[1]; + }; + + _FORCE_INLINE_ uint32_t hash( uint32_t d, const char *p_str ) const { + + if (d==0) + d=0x1000193; + while(*p_str) { + + d = (d * 0x1000193) ^ uint32_t(*p_str); + p_str++; + } + + return d; + } +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 *p_list) const; + static void _bind_methods(); + +public: + + virtual StringName get_message(const StringName& p_src_text) const; //overridable for other implementations + void generate(const Ref &p_from); + + PHashTranslation(); +}; + +#endif // COMPRESSED_TRANSLATION_H diff --git a/core/core_string_names.cpp b/core/core_string_names.cpp new file mode 100644 index 00000000000..75cb48137b1 --- /dev/null +++ b/core/core_string_names.cpp @@ -0,0 +1,47 @@ +/*************************************************************************/ +/* core_string_names.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 "core_string_names.h" + +CoreStringNames* CoreStringNames::singleton=NULL; + +CoreStringNames::CoreStringNames() { + + _free=StaticCString::create("free"); + changed=StaticCString::create("changed"); + _meta=StaticCString::create("__meta__"); + _script=StaticCString::create("script/script"); + script_changed=StaticCString::create("script_changed"); + ___pdcdata=StaticCString::create("___pdcdata"); + __getvar=StaticCString::create("__getvar"); + _iter_init=StaticCString::create("_iter_init"); + _iter_next=StaticCString::create("_iter_next"); + _iter_get=StaticCString::create("_iter_get"); + + +} diff --git a/core/core_string_names.h b/core/core_string_names.h new file mode 100644 index 00000000000..e2982acc1ab --- /dev/null +++ b/core/core_string_names.h @@ -0,0 +1,62 @@ +/*************************************************************************/ +/* core_string_names.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 CORE_STRING_NAMES_H +#define CORE_STRING_NAMES_H + +#include "string_db.h" + +class CoreStringNames { + +friend void register_core_types(); +friend void unregister_core_types(); + + static CoreStringNames* singleton; + + static void create() { singleton = memnew(CoreStringNames); } + static void free() { memdelete( singleton); singleton=NULL; } + + CoreStringNames(); +public: + _FORCE_INLINE_ static CoreStringNames* get_singleton() { return singleton; } + + + StringName _free; + StringName changed; + StringName _meta; + StringName _script; + StringName script_changed; + StringName ___pdcdata; + StringName __getvar; + StringName _iter_init; + StringName _iter_next; + StringName _iter_get; + +}; + +#endif // SCENE_STRING_NAMES_H diff --git a/core/dictionary.cpp b/core/dictionary.cpp new file mode 100644 index 00000000000..16ee3973824 --- /dev/null +++ b/core/dictionary.cpp @@ -0,0 +1,229 @@ +/*************************************************************************/ +/* dictionary.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 "dictionary.h" +#include "safe_refcount.h" +#include "variant.h" +#include "io/json.h" + +struct _DictionaryVariantHash { + + static _FORCE_INLINE_ uint32_t hash(const Variant &p_variant) { return p_variant.hash(); } +}; + + +struct DictionaryPrivate { + + SafeRefCount refcount; + HashMap variant_map; + bool shared; + +}; + + +void Dictionary::get_key_list( List *p_keys) const { + + _p->variant_map.get_key_list(p_keys); +} + +void Dictionary::_copy_on_write() const { + + //make a copy of what we have + if (_p->shared) + return; + + DictionaryPrivate *p = memnew(DictionaryPrivate); + p->shared=_p->shared; + p->variant_map=_p->variant_map; + p->refcount.init(); + _unref(); + _p=p; +} + +Variant& Dictionary::operator[](const Variant& p_key) { + + _copy_on_write(); + + return _p->variant_map[p_key]; +} + +const Variant& Dictionary::operator[](const Variant& p_key) const { + + return _p->variant_map[p_key]; + +} +const Variant* Dictionary::getptr(const Variant& p_key) const { + + return _p->variant_map.getptr(p_key); +} +Variant* Dictionary::getptr(const Variant& p_key) { + + _copy_on_write(); + return _p->variant_map.getptr(p_key); +} + +Variant Dictionary::get_valid(const Variant& p_key) const { + + const Variant *v = getptr(p_key); + if (!v) + return Variant(); + return *v; +} + + +int Dictionary::size() const { + + return _p->variant_map.size(); + +} +bool Dictionary::empty() const { + + return !_p->variant_map.size(); +} + +bool Dictionary::has(const Variant& p_key) const { + + return _p->variant_map.has(p_key); +} +void Dictionary::erase(const Variant& p_key) { + _copy_on_write(); + _p->variant_map.erase(p_key); +} + +bool Dictionary::operator==(const Dictionary& p_dictionary) const { + + return _p==p_dictionary._p; +} + +void Dictionary::_ref(const Dictionary& p_from) const { + + //make a copy first (thread safe) + if (!p_from._p->refcount.ref()) + return; // couldn't copy + + //if this is the same, unreference the other one + if (p_from._p==_p) { + _p->refcount.unref(); + return; + } + if (_p) + _unref(); + _p=p_from._p; + +} + +void Dictionary::clear() { + + _copy_on_write(); + _p->variant_map.clear(); +} + +bool Dictionary::is_shared() const { + + return _p->shared; +} + + +void Dictionary::_unref() const { + + ERR_FAIL_COND(!_p); + if (_p->refcount.unref()) { + memdelete(_p); + } + _p=NULL; + +} +uint32_t Dictionary::hash() const { + + return hash_djb2_one_64(make_uint64_t(_p)); +} + +Array Dictionary::keys() const { + + Array karr; + karr.resize(size()); + const Variant *K=NULL; + int idx=0; + while((K=next(K))) { + karr[idx++]=(*K); + } + return karr; + +} + +const Variant* Dictionary::next(const Variant* p_key) const { + + return _p->variant_map.next(p_key); +} + + +Error Dictionary::parse_json(const String& p_json) { + + String errstr; + int errline=0; + Error err = JSON::parse(p_json,*this,errstr,errline); + if (err!=OK) { + ERR_EXPLAIN("Error parsing JSON: "+errstr+" at line: "+itos(errline)); + ERR_FAIL_COND_V(err!=OK,err); + } + + return OK; +} + +String Dictionary::to_json() const { + + return JSON::print(*this); +} + + +void Dictionary::operator=(const Dictionary& p_dictionary) { + + _ref(p_dictionary); +} + + + +Dictionary::Dictionary(const Dictionary& p_from) { + _p=NULL; + _ref(p_from); +} + + +Dictionary::Dictionary(bool p_shared) { + + _p=memnew( DictionaryPrivate ); + _p->refcount.init(); + _p->shared=p_shared; + +} +Dictionary::~Dictionary() { + + _unref(); +} + + diff --git a/core/dictionary.h b/core/dictionary.h new file mode 100644 index 00000000000..de5be0fc6b0 --- /dev/null +++ b/core/dictionary.h @@ -0,0 +1,88 @@ +/*************************************************************************/ +/* dictionary.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 DICTIONARY_H +#define DICTIONARY_H + + +#include "list.h" +#include "array.h" +#include "ustring.h" +class Variant; + + +struct DictionaryPrivate; + + +class Dictionary { + + mutable DictionaryPrivate *_p; + + void _copy_on_write() const; + void _ref(const Dictionary& p_from) const; + void _unref() const; +public: + + void get_key_list( List *p_keys) const; + + Variant& operator[](const Variant& p_key); + const Variant& operator[](const Variant& p_key) const; + + const Variant* getptr(const Variant& p_key) const; + Variant* getptr(const Variant& p_key); + + Variant get_valid(const Variant& p_key) const; + + int size() const; + bool empty() const; + void clear(); + + + Error parse_json(const String& p_json); + String to_json() const; + + bool is_shared() const; + + bool has(const Variant& p_key) const; + void erase(const Variant& p_key); + + bool operator==(const Dictionary& p_dictionary) const; + + uint32_t hash() const; + void operator=(const Dictionary& p_dictionary); + + const Variant* next(const Variant* p_key=NULL) const; + + Array keys() const; + + Dictionary(const Dictionary& p_from); + Dictionary(bool p_shared=false); + ~Dictionary(); +}; + +#endif // DICTIONARY_H diff --git a/core/dvector.cpp b/core/dvector.cpp new file mode 100644 index 00000000000..e0a378a4271 --- /dev/null +++ b/core/dvector.cpp @@ -0,0 +1,32 @@ +/*************************************************************************/ +/* dvector.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 "dvector.h" + +Mutex* dvector_lock=NULL; + diff --git a/core/dvector.h b/core/dvector.h new file mode 100644 index 00000000000..72661882cd5 --- /dev/null +++ b/core/dvector.h @@ -0,0 +1,412 @@ +/*************************************************************************/ +/* dvector.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 DVECTOR_H +#define DVECTOR_H + +#include "os/memory.h" + + +/** + @author Juan Linietsky +*/ + + +extern Mutex* dvector_lock; + +template +class DVector { + + mutable MID mem; + + + void copy_on_write() { + + if (!mem.is_valid()) + return; + + if (dvector_lock) + dvector_lock->lock(); + + MID_Lock lock( mem ); + + + if ( *(int*)lock.data() == 1 ) { + // one reference, means no refcount changes + if (dvector_lock) + dvector_lock->unlock(); + return; + } + + MID new_mem= dynalloc( mem.get_size() ); + + if (!new_mem.is_valid()) { + + if (dvector_lock) + dvector_lock->unlock(); + ERR_FAIL_COND( new_mem.is_valid() ); // out of memory + } + + MID_Lock dst_lock( new_mem ); + + int *rc = (int*)dst_lock.data(); + + *rc=1; + + T * dst = (T*)(rc + 1 ); + + T * src =(T*) ((int*)lock.data() + 1 ); + + int count = (mem.get_size() - sizeof(int)) / sizeof(T); + + for (int i=0;iunlock(); + + } + + void reference( const DVector& p_dvector ) { + + unreference(); + + if (dvector_lock) + dvector_lock->lock(); + + if (!p_dvector.mem.is_valid()) { + + if (dvector_lock) + dvector_lock->unlock(); + return; + } + + MID_Lock lock(p_dvector.mem); + + int * rc = (int*)lock.data(); + (*rc)++; + + lock = MID_Lock(); + mem=p_dvector.mem; + + if (dvector_lock) + dvector_lock->unlock(); + + } + + + void unreference() { + + if (dvector_lock) + dvector_lock->lock(); + + if (!mem.is_valid()) { + + if (dvector_lock) + dvector_lock->unlock(); + return; + } + + MID_Lock lock(mem); + + int * rc = (int*)lock.data(); + (*rc)--; + + if (*rc==0) { + // no one else using it, destruct + + T * t= (T*)(rc+1); + int count = (mem.get_size() - sizeof(int)) / sizeof(T); + + for (int i=0;iunlock(); + + } + +public: + + class Read { + friend class DVector; + MID_Lock lock; + const T * mem; + public: + + _FORCE_INLINE_ const T& operator[](int p_index) const { return mem[p_index]; } + _FORCE_INLINE_ const T *ptr() const { return mem; } + + Read() { mem=NULL; } + }; + + class Write { + friend class DVector; + MID_Lock lock; + T * mem; + public: + + _FORCE_INLINE_ T& operator[](int p_index) { return mem[p_index]; } + _FORCE_INLINE_ T *ptr() { return mem; } + + Write() { mem=NULL; } + }; + + + Read read() const { + + Read r; + if (mem.is_valid()) { + r.lock = MID_Lock( mem ); + r.mem = (const T*)((int*)r.lock.data()+1); + } + return r; + } + Write write() { + + Write w; + if (mem.is_valid()) { + copy_on_write(); + w.lock = MID_Lock( mem ); + w.mem = (T*)((int*)w.lock.data()+1); + } + return w; + } + + template + void fill_with(const MC& p_mc) { + + + int c=p_mc.size(); + resize(c); + Write w=write(); + int idx=0; + for(const typename MC::Element *E=p_mc.front();E;E=E->next()) { + + w[idx++]=E->get(); + } + } + + + void remove(int p_index) { + + int s = size(); + ERR_FAIL_INDEX(p_index, s); + Write w = write(); + for (int i=p_index; i& p_arr) { + int ds = p_arr.size(); + if (ds==0) + return; + int bs = size(); + resize( bs + ds); + Write w = write(); + Read r = p_arr.read(); + for(int i=0;i +int DVector::size() const { + + return mem.is_valid() ? ((mem.get_size() - sizeof(int)) / sizeof(T) ) : 0; +} + +template +T DVector::get(int p_index) const { + + return operator[](p_index); +} + +template +void DVector::set(int p_index, const T& p_val) { + + if (p_index<0 || p_index>=size()) { + ERR_FAIL_COND(p_index<0 || p_index>=size()); + } + + Write w = write(); + w[p_index]=p_val; +} + +template +void DVector::push_back(const T& p_val) { + + resize( size() + 1 ); + set( size() -1, p_val ); +} + +template +const T DVector::operator[](int p_index) const { + + if (p_index<0 || p_index>=size()) { + T& aux=*((T*)0); //nullreturn + ERR_FAIL_COND_V(p_index<0 || p_index>=size(),aux); + } + + Read r = read(); + + return r[p_index]; +} + + +template +Error DVector::resize(int p_size) { + + if (dvector_lock) + dvector_lock->lock(); + + bool same = p_size==size(); + + if (dvector_lock) + dvector_lock->unlock(); + // no further locking is necesary because we are supposed to own the only copy of this (using copy on write) + + if (same) + return OK; + + if (p_size == 0 ) { + + unreference(); + return OK; + } + + + copy_on_write(); // make it unique + + ERR_FAIL_COND_V( mem.is_locked(), ERR_LOCKED ); // if after copy on write, memory is locked, fail. + + if (p_size > size() ) { + + int oldsize=size(); + + MID_Lock lock; + + if (oldsize==0) { + + mem = dynalloc( p_size * sizeof(T) + sizeof(int) ); + lock=MID_Lock(mem); + int *rc = ((int*)lock.data()); + *rc=1; + + } else { + + if (dynrealloc( mem, p_size * sizeof(T) + sizeof(int) )!=OK ) { + + ERR_FAIL_V(ERR_OUT_OF_MEMORY); // out of memory + } + + lock=MID_Lock(mem); + } + + + + + T *t = (T*)((int*)lock.data() + 1); + + for (int i=oldsize;iset_last_error(p_err); +} + +void _err_clear_last_error() { + + OS::get_singleton()->clear_last_error(); +} + +void add_error_handler(ErrorHandlerList *p_handler) { + + _global_lock(); + p_handler->next=error_handler_list; + error_handler_list=p_handler; + _global_unlock(); +} + +void remove_error_handler(ErrorHandlerList *p_handler) { + + _global_lock(); + + ErrorHandlerList *prev = NULL; + ErrorHandlerList *l = error_handler_list; + + while(l) { + + if (l==p_handler) { + + if (prev) + prev->next=l->next; + else + error_handler_list=l->next; + break; + } + prev=l; + l=l->next; + + } + + _global_unlock(); + +} + +void _err_print_error(const char* p_function, const char* p_file,int p_line,const char *p_error,ErrorHandlerType p_type) { + + + + OS::get_singleton()->print_error(p_function,p_file,p_line,p_error,_err_error_exists?OS::get_singleton()->get_last_error():"",(OS::ErrorType)p_type); + + _global_lock(); + ErrorHandlerList *l = error_handler_list; + while(l) { + + l->errfunc(l->userdata,p_function,p_file,p_line,p_error,_err_error_exists?OS::get_singleton()->get_last_error():"",p_type); + l=l->next; + } + + _global_unlock(); + + if (_err_error_exists) { + OS::get_singleton()->clear_last_error(); + _err_error_exists=false; + } + +} diff --git a/core/error_macros.h b/core/error_macros.h new file mode 100644 index 00000000000..6cd38755c07 --- /dev/null +++ b/core/error_macros.h @@ -0,0 +1,222 @@ +/*************************************************************************/ +/* error_macros.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 ERROR_MACROS_H +#define ERROR_MACROS_H + + +/** + * Error macros. Unlike exceptions and asserts, these macros try to mantain consistency and stability + * inside the code. It is recommended to always return processable data, so in case of an error, the + * engine can stay working well. + * In most cases, bugs and/or invalid data are not fatal and should never allow a perfectly running application + * to fail or crash. + */ + +/** + * Pointer to the error macro priting function. Reassign to any function to have errors printed + */ + +/** Function used by the error macros */ + +// function, file, line, error, explanation + +enum ErrorHandlerType { + ERR_HANDLER_ERROR, + ERR_HANDLER_WARNING, + ERR_HANDLER_SCRIPT +}; + +typedef void (*ErrorHandlerFunc)(void*,const char*,const char*,int p_line,const char *, const char *,ErrorHandlerType p_type); +void _err_set_last_error(const char* p_err); +void _err_clear_last_error(); + +struct ErrorHandlerList { + + ErrorHandlerFunc errfunc; + void *userdata; + + ErrorHandlerList*next; + + ErrorHandlerList() { errfunc=0; next=0; userdata=0; } +}; + +void add_error_handler(ErrorHandlerList *p_handler); +void remove_error_handler(ErrorHandlerList *p_handler); + +void _err_print_error(const char* p_function,const char* p_file,int p_line,const char *p_error,ErrorHandlerType p_type=ERR_HANDLER_ERROR); + +#ifndef _STR +#define _STR(m_x) #m_x +#define _MKSTR(m_x) _STR(m_x) +#endif + +#define _FNL __FILE__":" + +/** An index has failed if m_index<0 or m_index >=m_size, the function exists */ + +extern bool _err_error_exists; + +#ifdef DEBUG_ENABLED +/** Print a warning string. + */ +#define ERR_EXPLAINC(m_reason) {_err_set_last_error(m_reason); _err_error_exists=true;} +#define ERR_EXPLAIN(m_string) {_err_set_last_error(String(m_string).utf8().get_data()); _err_error_exists=true;} + +#else + +#define ERR_EXPLAIN( m_text ) +#define ERR_EXPLAINC( m_text ) + +#endif + +#ifdef __GNUC__ +//#define FUNCTION_STR __PRETTY_FUNCTION__ - too annoying +#define FUNCTION_STR __FUNCTION__ +#else +#define FUNCTION_STR __FUNCTION__ +#endif + +#define ERR_FAIL_INDEX(m_index,m_size) \ + do {if ((m_index)<0 || (m_index)>=(m_size)) { \ + _err_print_error(FUNCTION_STR,__FILE__,__LINE__,"Index "_STR(m_index)" out of size ("_STR(m_size)")."); \ + return; \ + } else _err_error_exists=false; } while(0); \ + +/** An index has failed if m_index<0 or m_index >=m_size, the function exists. + * This function returns an error value, if returning Error, please select the most + * appropriate error condition from error_macros.h + */ + +#define ERR_FAIL_INDEX_V(m_index,m_size,m_retval) \ + do {if ((m_index)<0 || (m_index)>=(m_size)) { \ + _err_print_error(FUNCTION_STR,__FILE__,__LINE__,"Index "_STR(m_index)" out of size ("_STR(m_size)")."); \ + return m_retval; \ + } else _err_error_exists=false;} while (0); + + /** An error condition happened (m_cond tested true) (WARNING this is the opposite as assert(). + * the function will exit. + */ + + #define ERR_FAIL_NULL(m_param) \ + { if ( !m_param ) { \ + _err_print_error(FUNCTION_STR,__FILE__,__LINE__,"Parameter ' "_STR(m_param)" ' is null."); \ + return; \ + }else _err_error_exists=false; } \ + + +#define ERR_FAIL_NULL_V(m_param,m_retval) \ + { if ( !m_param ) { \ + _err_print_error(FUNCTION_STR,__FILE__,__LINE__,"Parameter ' "_STR(m_param)" ' is null."); \ + return m_retval; \ + }else _err_error_exists=false; } \ + +/** An error condition happened (m_cond tested true) (WARNING this is the opposite as assert(). + * the function will exit. + */ + +#define ERR_FAIL_COND(m_cond) \ + { if ( m_cond ) { \ + _err_print_error(FUNCTION_STR,__FILE__,__LINE__,"Condition ' "_STR(m_cond)" ' is true."); \ + return; \ + }else _err_error_exists=false; } \ + +/** An error condition happened (m_cond tested true) (WARNING this is the opposite as assert(). + * the function will exit. + * This function returns an error value, if returning Error, please select the most + * appropriate error condition from error_macros.h + */ + +#define ERR_FAIL_COND_V(m_cond,m_retval) \ + { if ( m_cond ) { \ + _err_print_error(FUNCTION_STR,__FILE__,__LINE__,"Condition ' "_STR(m_cond)" ' is true. returned: "_STR(m_retval)); \ + return m_retval; \ + }else _err_error_exists=false; } \ + +/** An error condition happened (m_cond tested true) (WARNING this is the opposite as assert(). + * the loop will skip to the next iteration. + */ + +#define ERR_CONTINUE(m_cond) \ + { if ( m_cond ) { \ + _err_print_error(FUNCTION_STR,__FILE__,__LINE__,"Condition ' "_STR(m_cond)" ' is true. Continuing..:"); \ + continue;\ + } else _err_error_exists=false;} \ + +/** An error condition happened (m_cond tested true) (WARNING this is the opposite as assert(). + * the loop will break + */ + +#define ERR_BREAK(m_cond) \ + { if ( m_cond ) { \ + _err_print_error(FUNCTION_STR,__FILE__,__LINE__,"Condition ' "_STR(m_cond)" ' is true. Breaking..:"); \ + break;\ + } else _err_error_exists=false;} \ + +/** Print an error string and return + */ + +#define ERR_FAIL() \ +{ \ + _err_print_error(FUNCTION_STR,__FILE__,__LINE__,"Method/Function Failed."); \ + _err_error_exists=false;\ + return;\ +} \ + +/** Print an error string and return with value + */ + +#define ERR_FAIL_V(m_value) \ +{ \ + _err_print_error(FUNCTION_STR,__FILE__,__LINE__,"Method/Function Failed, returning: "__STR(m_value)); \ + _err_error_exists=false; \ + return m_value;\ +} \ + +/** Print an error string. + */ + +#define ERR_PRINT(m_string) \ + { \ + _err_print_error(FUNCTION_STR,__FILE__,__LINE__,m_string); \ + _err_error_exists=false;\ + } \ + + +/** Print a warning string. + */ + +#define WARN_PRINT(m_string) \ + { \ + _err_print_error(FUNCTION_STR,__FILE__,__LINE__,m_string,ERR_HANDLER_WARNING); \ + _err_error_exists=false;\ + } \ + + + +#endif diff --git a/core/event_queue.cpp b/core/event_queue.cpp new file mode 100644 index 00000000000..cf6e742f79f --- /dev/null +++ b/core/event_queue.cpp @@ -0,0 +1,153 @@ +/*************************************************************************/ +/* event_queue.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 "event_queue.h" + + +Error EventQueue::push_call(uint32_t p_instance_ID, const StringName& p_method, VARIANT_ARG_DECLARE) { + + uint8_t room_needed=sizeof(Event); + int args=0; + if (p_arg5.get_type()!=Variant::NIL) + args=5; + else if (p_arg4.get_type()!=Variant::NIL) + args=4; + else if (p_arg3.get_type()!=Variant::NIL) + args=3; + else if (p_arg2.get_type()!=Variant::NIL) + args=2; + else if (p_arg1.get_type()!=Variant::NIL) + args=1; + else + args=0; + + room_needed+=sizeof(Variant)*args; + + ERR_FAIL_COND_V( (buffer_end+room_needed) >= buffer_size , ERR_OUT_OF_MEMORY ); + Event * ev = memnew_placement( &event_buffer[ buffer_end ], Event ); + ev->args=args; + ev->instance_ID=p_instance_ID; + ev->method=p_method; + + buffer_end+=sizeof(Event); + + if (args==1) { + + Variant * v = memnew_placement( &event_buffer[ buffer_end ], Variant ); + buffer_end+=sizeof(Variant); + *v=p_arg1; + } else if (args==2) { + + Variant * v = memnew_placement( &event_buffer[ buffer_end ], Variant ); + buffer_end+=sizeof(Variant); + *v=p_arg2; + } else if (args==3) { + + Variant * v = memnew_placement( &event_buffer[ buffer_end ], Variant ); + buffer_end+=sizeof(Variant); + *v=p_arg3; + + } else if (args==4) { + + Variant * v = memnew_placement( &event_buffer[ buffer_end ], Variant ); + buffer_end+=sizeof(Variant); + *v=p_arg4; + } else if (args==5) { + + Variant * v = memnew_placement( &event_buffer[ buffer_end ], Variant ); + buffer_end+=sizeof(Variant); + *v=p_arg5; + } + + if (buffer_max_used>buffer_end); + buffer_max_used=buffer_end; + + return OK; +} + +void EventQueue::flush_events() { + + uint32_t read_pos=0; + + while (read_pos < buffer_end ) { + + Event *event = (Event*)&event_buffer[ read_pos ]; + Variant *args= (Variant*)(event+1); + Object *obj = ObjectDB::get_instance(event->instance_ID); + + if (obj) { + // events don't expect a return value + obj->call( event->method, + (event->args>=1) ? args[0] : Variant(), + (event->args>=2) ? args[1] : Variant(), + (event->args>=3) ? args[2] : Variant(), + (event->args>=4) ? args[3] : Variant(), + (event->args>=5) ? args[4] : Variant() ); + } + + if (event->args>=1) args[0].~Variant(); + if (event->args>=2) args[1].~Variant(); + if (event->args>=3) args[2].~Variant(); + if (event->args>=4) args[3].~Variant(); + if (event->args>=5) args[4].~Variant(); + event->~Event(); + + read_pos+=sizeof(Event)+sizeof(Variant)*event->args; + } + + buffer_end=0; // reset buffer +} + +EventQueue::EventQueue(uint32_t p_buffer_size) { + + + buffer_end=0; + buffer_max_used=0; + buffer_size=p_buffer_size; + event_buffer = memnew_arr( uint8_t, buffer_size ); + +} +EventQueue::~EventQueue() { + + uint32_t read_pos=0; + + while (read_pos < buffer_end ) { + + Event *event = (Event*)&event_buffer[ read_pos ]; + Variant *args= (Variant*)(event+1); + for (int i=0;iargs;i++) + args[i].~Variant(); + event->~Event(); + + read_pos+=sizeof(Event)+sizeof(Variant)*event->args; + } + + memdelete_arr(event_buffer); + event_buffer=NULL; +} + diff --git a/core/event_queue.h b/core/event_queue.h new file mode 100644 index 00000000000..0647c02777f --- /dev/null +++ b/core/event_queue.h @@ -0,0 +1,66 @@ +/*************************************************************************/ +/* event_queue.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 EVENT_QUEUE_H +#define EVENT_QUEUE_H + +#include "object.h" +/** + @author Juan Linietsky +*/ +class EventQueue { + + enum { + + DEFAULT_EVENT_QUEUE_SIZE_KB=256 + }; + + struct Event { + + uint32_t instance_ID; + StringName method; + int args; + }; + + + uint8_t *event_buffer; + uint32_t buffer_end; + uint32_t buffer_max_used; + uint32_t buffer_size; +public: + + + Error push_call(uint32_t p_instance_ID, const StringName& p_method, VARIANT_ARG_LIST); + void flush_events(); + + EventQueue(uint32_t p_buffer_size=DEFAULT_EVENT_QUEUE_SIZE_KB*1024); + ~EventQueue(); + +}; + +#endif diff --git a/core/fpstr.cpp b/core/fpstr.cpp new file mode 100644 index 00000000000..25784adb150 --- /dev/null +++ b/core/fpstr.cpp @@ -0,0 +1,28 @@ +/*************************************************************************/ +/* fpstr.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. */ +/*************************************************************************/ diff --git a/core/fpstr.h b/core/fpstr.h new file mode 100644 index 00000000000..d07ae7d5a2e --- /dev/null +++ b/core/fpstr.h @@ -0,0 +1,28 @@ +/*************************************************************************/ +/* fpstr.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. */ +/*************************************************************************/ diff --git a/core/global_constants.cpp b/core/global_constants.cpp new file mode 100644 index 00000000000..efa72b6547b --- /dev/null +++ b/core/global_constants.cpp @@ -0,0 +1,516 @@ +/*************************************************************************/ +/* global_constants.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 "global_constants.h" +#include "variant.h" +#include "os/keyboard.h" +#include "object.h" + +struct _GlobalConstant { + + const char *name; + int value; +}; + +#define BIND_GLOBAL_CONSTANT(m_constant) {#m_constant,m_constant} + + +static _GlobalConstant _global_constants[]={ + +//{ KEY_BACKSPACE, VK_BACK },// (0x08) // backspace + + BIND_GLOBAL_CONSTANT( MARGIN_LEFT ), + BIND_GLOBAL_CONSTANT( MARGIN_TOP ), + BIND_GLOBAL_CONSTANT( MARGIN_RIGHT ), + BIND_GLOBAL_CONSTANT( MARGIN_BOTTOM ), + BIND_GLOBAL_CONSTANT( VERTICAL ), + BIND_GLOBAL_CONSTANT( HORIZONTAL ), + BIND_GLOBAL_CONSTANT( HALIGN_LEFT ), + BIND_GLOBAL_CONSTANT( HALIGN_CENTER ), + BIND_GLOBAL_CONSTANT( HALIGN_RIGHT ), + BIND_GLOBAL_CONSTANT( VALIGN_TOP ), + BIND_GLOBAL_CONSTANT( VALIGN_CENTER ), + BIND_GLOBAL_CONSTANT( VALIGN_BOTTOM ), + + // hueg list of keys + BIND_GLOBAL_CONSTANT( SPKEY ), + + BIND_GLOBAL_CONSTANT( KEY_ESCAPE ), + BIND_GLOBAL_CONSTANT( KEY_TAB ), + BIND_GLOBAL_CONSTANT( KEY_BACKTAB ), + BIND_GLOBAL_CONSTANT( KEY_BACKSPACE ), + BIND_GLOBAL_CONSTANT( KEY_RETURN ), + BIND_GLOBAL_CONSTANT( KEY_ENTER ), + BIND_GLOBAL_CONSTANT( KEY_INSERT ), + BIND_GLOBAL_CONSTANT( KEY_DELETE ), + BIND_GLOBAL_CONSTANT( KEY_PAUSE ), + BIND_GLOBAL_CONSTANT( KEY_PRINT ), + BIND_GLOBAL_CONSTANT( KEY_SYSREQ ), + BIND_GLOBAL_CONSTANT( KEY_CLEAR ), + BIND_GLOBAL_CONSTANT( KEY_HOME ), + BIND_GLOBAL_CONSTANT( KEY_END ), + BIND_GLOBAL_CONSTANT( KEY_LEFT ), + BIND_GLOBAL_CONSTANT( KEY_UP ), + BIND_GLOBAL_CONSTANT( KEY_RIGHT ), + BIND_GLOBAL_CONSTANT( KEY_DOWN ), + BIND_GLOBAL_CONSTANT( KEY_PAGEUP ), + BIND_GLOBAL_CONSTANT( KEY_PAGEDOWN ), + BIND_GLOBAL_CONSTANT( KEY_SHIFT ), + BIND_GLOBAL_CONSTANT( KEY_CONTROL ), + BIND_GLOBAL_CONSTANT( KEY_META ), + BIND_GLOBAL_CONSTANT( KEY_ALT ), + BIND_GLOBAL_CONSTANT( KEY_CAPSLOCK ), + BIND_GLOBAL_CONSTANT( KEY_NUMLOCK ), + BIND_GLOBAL_CONSTANT( KEY_SCROLLLOCK ), + BIND_GLOBAL_CONSTANT( KEY_F1 ), + BIND_GLOBAL_CONSTANT( KEY_F2 ), + BIND_GLOBAL_CONSTANT( KEY_F3 ), + BIND_GLOBAL_CONSTANT( KEY_F4 ), + BIND_GLOBAL_CONSTANT( KEY_F5 ), + BIND_GLOBAL_CONSTANT( KEY_F6 ), + BIND_GLOBAL_CONSTANT( KEY_F7 ), + BIND_GLOBAL_CONSTANT( KEY_F8 ), + BIND_GLOBAL_CONSTANT( KEY_F9 ), + BIND_GLOBAL_CONSTANT( KEY_F10 ), + BIND_GLOBAL_CONSTANT( KEY_F11 ), + BIND_GLOBAL_CONSTANT( KEY_F12 ), + BIND_GLOBAL_CONSTANT( KEY_F13 ), + BIND_GLOBAL_CONSTANT( KEY_F14 ), + BIND_GLOBAL_CONSTANT( KEY_F15 ), + BIND_GLOBAL_CONSTANT( KEY_F16 ), + BIND_GLOBAL_CONSTANT( KEY_KP_ENTER ), + BIND_GLOBAL_CONSTANT( KEY_KP_MULTIPLY ), + BIND_GLOBAL_CONSTANT( KEY_KP_DIVIDE ), + BIND_GLOBAL_CONSTANT( KEY_KP_SUBSTRACT ), + BIND_GLOBAL_CONSTANT( KEY_KP_PERIOD ), + BIND_GLOBAL_CONSTANT( KEY_KP_ADD ), + BIND_GLOBAL_CONSTANT( KEY_KP_0 ), + BIND_GLOBAL_CONSTANT( KEY_KP_1 ), + BIND_GLOBAL_CONSTANT( KEY_KP_2 ), + BIND_GLOBAL_CONSTANT( KEY_KP_3 ), + BIND_GLOBAL_CONSTANT( KEY_KP_4 ), + BIND_GLOBAL_CONSTANT( KEY_KP_5 ), + BIND_GLOBAL_CONSTANT( KEY_KP_6 ), + BIND_GLOBAL_CONSTANT( KEY_KP_7 ), + BIND_GLOBAL_CONSTANT( KEY_KP_8 ), + BIND_GLOBAL_CONSTANT( KEY_KP_9 ), + BIND_GLOBAL_CONSTANT( KEY_SUPER_L ), + BIND_GLOBAL_CONSTANT( KEY_SUPER_R ), + BIND_GLOBAL_CONSTANT( KEY_MENU ), + BIND_GLOBAL_CONSTANT( KEY_HYPER_L ), + BIND_GLOBAL_CONSTANT( KEY_HYPER_R ), + BIND_GLOBAL_CONSTANT( KEY_HELP ), + BIND_GLOBAL_CONSTANT( KEY_DIRECTION_L ), + BIND_GLOBAL_CONSTANT( KEY_DIRECTION_R ), + BIND_GLOBAL_CONSTANT( KEY_BACK ), + BIND_GLOBAL_CONSTANT( KEY_FORWARD ), + BIND_GLOBAL_CONSTANT( KEY_STOP ), + BIND_GLOBAL_CONSTANT( KEY_REFRESH ), + BIND_GLOBAL_CONSTANT( KEY_VOLUMEDOWN ), + BIND_GLOBAL_CONSTANT( KEY_VOLUMEMUTE ), + BIND_GLOBAL_CONSTANT( KEY_VOLUMEUP ), + BIND_GLOBAL_CONSTANT( KEY_BASSBOOST ), + BIND_GLOBAL_CONSTANT( KEY_BASSUP ), + BIND_GLOBAL_CONSTANT( KEY_BASSDOWN ), + BIND_GLOBAL_CONSTANT( KEY_TREBLEUP ), + BIND_GLOBAL_CONSTANT( KEY_TREBLEDOWN ), + BIND_GLOBAL_CONSTANT( KEY_MEDIAPLAY ), + BIND_GLOBAL_CONSTANT( KEY_MEDIASTOP ), + BIND_GLOBAL_CONSTANT( KEY_MEDIAPREVIOUS ), + BIND_GLOBAL_CONSTANT( KEY_MEDIANEXT ), + BIND_GLOBAL_CONSTANT( KEY_MEDIARECORD ), + BIND_GLOBAL_CONSTANT( KEY_HOMEPAGE ), + BIND_GLOBAL_CONSTANT( KEY_FAVORITES ), + BIND_GLOBAL_CONSTANT( KEY_SEARCH ), + BIND_GLOBAL_CONSTANT( KEY_STANDBY ), + BIND_GLOBAL_CONSTANT( KEY_OPENURL ), + BIND_GLOBAL_CONSTANT( KEY_LAUNCHMAIL ), + BIND_GLOBAL_CONSTANT( KEY_LAUNCHMEDIA ), + BIND_GLOBAL_CONSTANT( KEY_LAUNCH0 ), + BIND_GLOBAL_CONSTANT( KEY_LAUNCH1 ), + BIND_GLOBAL_CONSTANT( KEY_LAUNCH2 ), + BIND_GLOBAL_CONSTANT( KEY_LAUNCH3 ), + BIND_GLOBAL_CONSTANT( KEY_LAUNCH4 ), + BIND_GLOBAL_CONSTANT( KEY_LAUNCH5 ), + BIND_GLOBAL_CONSTANT( KEY_LAUNCH6 ), + BIND_GLOBAL_CONSTANT( KEY_LAUNCH7 ), + BIND_GLOBAL_CONSTANT( KEY_LAUNCH8 ), + BIND_GLOBAL_CONSTANT( KEY_LAUNCH9 ), + BIND_GLOBAL_CONSTANT( KEY_LAUNCHA ), + BIND_GLOBAL_CONSTANT( KEY_LAUNCHB ), + BIND_GLOBAL_CONSTANT( KEY_LAUNCHC ), + BIND_GLOBAL_CONSTANT( KEY_LAUNCHD ), + BIND_GLOBAL_CONSTANT( KEY_LAUNCHE ), + BIND_GLOBAL_CONSTANT( KEY_LAUNCHF ), + + BIND_GLOBAL_CONSTANT( KEY_UNKNOWN ), + BIND_GLOBAL_CONSTANT( KEY_SPACE ), + BIND_GLOBAL_CONSTANT( KEY_EXCLAM ), + BIND_GLOBAL_CONSTANT( KEY_QUOTEDBL ), + BIND_GLOBAL_CONSTANT( KEY_NUMBERSIGN ), + BIND_GLOBAL_CONSTANT( KEY_DOLLAR ), + BIND_GLOBAL_CONSTANT( KEY_PERCENT ), + BIND_GLOBAL_CONSTANT( KEY_AMPERSAND ), + BIND_GLOBAL_CONSTANT( KEY_APOSTROPHE ), + BIND_GLOBAL_CONSTANT( KEY_PARENLEFT ), + BIND_GLOBAL_CONSTANT( KEY_PARENRIGHT ), + BIND_GLOBAL_CONSTANT( KEY_ASTERISK ), + BIND_GLOBAL_CONSTANT( KEY_PLUS ), + BIND_GLOBAL_CONSTANT( KEY_COMMA ), + BIND_GLOBAL_CONSTANT( KEY_MINUS ), + BIND_GLOBAL_CONSTANT( KEY_PERIOD ), + BIND_GLOBAL_CONSTANT( KEY_SLASH ), + BIND_GLOBAL_CONSTANT( KEY_0 ), + BIND_GLOBAL_CONSTANT( KEY_1 ), + BIND_GLOBAL_CONSTANT( KEY_2 ), + BIND_GLOBAL_CONSTANT( KEY_3 ), + BIND_GLOBAL_CONSTANT( KEY_4 ), + BIND_GLOBAL_CONSTANT( KEY_5 ), + BIND_GLOBAL_CONSTANT( KEY_6 ), + BIND_GLOBAL_CONSTANT( KEY_7 ), + BIND_GLOBAL_CONSTANT( KEY_8 ), + BIND_GLOBAL_CONSTANT( KEY_9 ), + BIND_GLOBAL_CONSTANT( KEY_COLON ), + BIND_GLOBAL_CONSTANT( KEY_SEMICOLON ), + BIND_GLOBAL_CONSTANT( KEY_LESS ), + BIND_GLOBAL_CONSTANT( KEY_EQUAL ), + BIND_GLOBAL_CONSTANT( KEY_GREATER ), + BIND_GLOBAL_CONSTANT( KEY_QUESTION ), + BIND_GLOBAL_CONSTANT( KEY_AT ), + BIND_GLOBAL_CONSTANT( KEY_A ), + BIND_GLOBAL_CONSTANT( KEY_B ), + BIND_GLOBAL_CONSTANT( KEY_C ), + BIND_GLOBAL_CONSTANT( KEY_D ), + BIND_GLOBAL_CONSTANT( KEY_E ), + BIND_GLOBAL_CONSTANT( KEY_F ), + BIND_GLOBAL_CONSTANT( KEY_G ), + BIND_GLOBAL_CONSTANT( KEY_H ), + BIND_GLOBAL_CONSTANT( KEY_I ), + BIND_GLOBAL_CONSTANT( KEY_J ), + BIND_GLOBAL_CONSTANT( KEY_K ), + BIND_GLOBAL_CONSTANT( KEY_L ), + BIND_GLOBAL_CONSTANT( KEY_M ), + BIND_GLOBAL_CONSTANT( KEY_N ), + BIND_GLOBAL_CONSTANT( KEY_O ), + BIND_GLOBAL_CONSTANT( KEY_P ), + BIND_GLOBAL_CONSTANT( KEY_Q ), + BIND_GLOBAL_CONSTANT( KEY_R ), + BIND_GLOBAL_CONSTANT( KEY_S ), + BIND_GLOBAL_CONSTANT( KEY_T ), + BIND_GLOBAL_CONSTANT( KEY_U ), + BIND_GLOBAL_CONSTANT( KEY_V ), + BIND_GLOBAL_CONSTANT( KEY_W ), + BIND_GLOBAL_CONSTANT( KEY_X ), + BIND_GLOBAL_CONSTANT( KEY_Y ), + BIND_GLOBAL_CONSTANT( KEY_Z ), + BIND_GLOBAL_CONSTANT( KEY_BRACKETLEFT ), + BIND_GLOBAL_CONSTANT( KEY_BACKSLASH ), + BIND_GLOBAL_CONSTANT( KEY_BRACKETRIGHT ), + BIND_GLOBAL_CONSTANT( KEY_ASCIICIRCUM ), + BIND_GLOBAL_CONSTANT( KEY_UNDERSCORE ), + BIND_GLOBAL_CONSTANT( KEY_QUOTELEFT ), + BIND_GLOBAL_CONSTANT( KEY_BRACELEFT ), + BIND_GLOBAL_CONSTANT( KEY_BAR ), + BIND_GLOBAL_CONSTANT( KEY_BRACERIGHT ), + BIND_GLOBAL_CONSTANT( KEY_ASCIITILDE ), + BIND_GLOBAL_CONSTANT( KEY_NOBREAKSPACE ), + BIND_GLOBAL_CONSTANT( KEY_EXCLAMDOWN ), + BIND_GLOBAL_CONSTANT( KEY_CENT ), + BIND_GLOBAL_CONSTANT( KEY_STERLING ), + BIND_GLOBAL_CONSTANT( KEY_CURRENCY ), + BIND_GLOBAL_CONSTANT( KEY_YEN ), + BIND_GLOBAL_CONSTANT( KEY_BROKENBAR ), + BIND_GLOBAL_CONSTANT( KEY_SECTION ), + BIND_GLOBAL_CONSTANT( KEY_DIAERESIS ), + BIND_GLOBAL_CONSTANT( KEY_COPYRIGHT ), + BIND_GLOBAL_CONSTANT( KEY_ORDFEMININE ), + BIND_GLOBAL_CONSTANT( KEY_GUILLEMOTLEFT ), + BIND_GLOBAL_CONSTANT( KEY_NOTSIGN ), + BIND_GLOBAL_CONSTANT( KEY_HYPHEN ), + BIND_GLOBAL_CONSTANT( KEY_REGISTERED ), + BIND_GLOBAL_CONSTANT( KEY_MACRON ), + BIND_GLOBAL_CONSTANT( KEY_DEGREE ), + BIND_GLOBAL_CONSTANT( KEY_PLUSMINUS ), + BIND_GLOBAL_CONSTANT( KEY_TWOSUPERIOR ), + BIND_GLOBAL_CONSTANT( KEY_THREESUPERIOR ), + BIND_GLOBAL_CONSTANT( KEY_ACUTE ), + BIND_GLOBAL_CONSTANT( KEY_MU ), + BIND_GLOBAL_CONSTANT( KEY_PARAGRAPH ), + BIND_GLOBAL_CONSTANT( KEY_PERIODCENTERED ), + BIND_GLOBAL_CONSTANT( KEY_CEDILLA ), + BIND_GLOBAL_CONSTANT( KEY_ONESUPERIOR ), + BIND_GLOBAL_CONSTANT( KEY_MASCULINE ), + BIND_GLOBAL_CONSTANT( KEY_GUILLEMOTRIGHT ), + BIND_GLOBAL_CONSTANT( KEY_ONEQUARTER ), + BIND_GLOBAL_CONSTANT( KEY_ONEHALF ), + BIND_GLOBAL_CONSTANT( KEY_THREEQUARTERS ), + BIND_GLOBAL_CONSTANT( KEY_QUESTIONDOWN ), + BIND_GLOBAL_CONSTANT( KEY_AGRAVE ), + BIND_GLOBAL_CONSTANT( KEY_AACUTE ), + BIND_GLOBAL_CONSTANT( KEY_ACIRCUMFLEX ), + BIND_GLOBAL_CONSTANT( KEY_ATILDE ), + BIND_GLOBAL_CONSTANT( KEY_ADIAERESIS ), + BIND_GLOBAL_CONSTANT( KEY_ARING ), + BIND_GLOBAL_CONSTANT( KEY_AE ), + BIND_GLOBAL_CONSTANT( KEY_CCEDILLA ), + BIND_GLOBAL_CONSTANT( KEY_EGRAVE ), + BIND_GLOBAL_CONSTANT( KEY_EACUTE ), + BIND_GLOBAL_CONSTANT( KEY_ECIRCUMFLEX ), + BIND_GLOBAL_CONSTANT( KEY_EDIAERESIS ), + BIND_GLOBAL_CONSTANT( KEY_IGRAVE ), + BIND_GLOBAL_CONSTANT( KEY_IACUTE ), + BIND_GLOBAL_CONSTANT( KEY_ICIRCUMFLEX ), + BIND_GLOBAL_CONSTANT( KEY_IDIAERESIS ), + BIND_GLOBAL_CONSTANT( KEY_ETH ), + BIND_GLOBAL_CONSTANT( KEY_NTILDE ), + BIND_GLOBAL_CONSTANT( KEY_OGRAVE ), + BIND_GLOBAL_CONSTANT( KEY_OACUTE ), + BIND_GLOBAL_CONSTANT( KEY_OCIRCUMFLEX ), + BIND_GLOBAL_CONSTANT( KEY_OTILDE ), + BIND_GLOBAL_CONSTANT( KEY_ODIAERESIS ), + BIND_GLOBAL_CONSTANT( KEY_MULTIPLY ), + BIND_GLOBAL_CONSTANT( KEY_OOBLIQUE ), + BIND_GLOBAL_CONSTANT( KEY_UGRAVE ), + BIND_GLOBAL_CONSTANT( KEY_UACUTE ), + BIND_GLOBAL_CONSTANT( KEY_UCIRCUMFLEX ), + BIND_GLOBAL_CONSTANT( KEY_UDIAERESIS ), + BIND_GLOBAL_CONSTANT( KEY_YACUTE ), + BIND_GLOBAL_CONSTANT( KEY_THORN ), + BIND_GLOBAL_CONSTANT( KEY_SSHARP ), + + BIND_GLOBAL_CONSTANT( KEY_DIVISION ), + BIND_GLOBAL_CONSTANT( KEY_YDIAERESIS ), + + BIND_GLOBAL_CONSTANT( KEY_CODE_MASK ), + BIND_GLOBAL_CONSTANT( KEY_MODIFIER_MASK ), + + BIND_GLOBAL_CONSTANT( KEY_MASK_SHIFT ), + BIND_GLOBAL_CONSTANT( KEY_MASK_ALT ), + BIND_GLOBAL_CONSTANT( KEY_MASK_META ), + BIND_GLOBAL_CONSTANT( KEY_MASK_CTRL ), + BIND_GLOBAL_CONSTANT( KEY_MASK_KPAD ), + BIND_GLOBAL_CONSTANT( KEY_MASK_GROUP_SWITCH ), + + // joysticks + BIND_GLOBAL_CONSTANT( BUTTON_LEFT ), + BIND_GLOBAL_CONSTANT( BUTTON_RIGHT ), + BIND_GLOBAL_CONSTANT( BUTTON_MIDDLE ), + BIND_GLOBAL_CONSTANT( BUTTON_WHEEL_UP ), + BIND_GLOBAL_CONSTANT( BUTTON_WHEEL_DOWN ), + BIND_GLOBAL_CONSTANT( BUTTON_MASK_LEFT ), + BIND_GLOBAL_CONSTANT( BUTTON_MASK_RIGHT ), + BIND_GLOBAL_CONSTANT( BUTTON_MASK_MIDDLE ), + + BIND_GLOBAL_CONSTANT( JOY_BUTTON_0 ), + BIND_GLOBAL_CONSTANT( JOY_BUTTON_1 ), + BIND_GLOBAL_CONSTANT( JOY_BUTTON_2 ), + BIND_GLOBAL_CONSTANT( JOY_BUTTON_3 ), + BIND_GLOBAL_CONSTANT( JOY_BUTTON_4 ), + BIND_GLOBAL_CONSTANT( JOY_BUTTON_5 ), + BIND_GLOBAL_CONSTANT( JOY_BUTTON_6 ), + BIND_GLOBAL_CONSTANT( JOY_BUTTON_7 ), + BIND_GLOBAL_CONSTANT( JOY_BUTTON_8 ), + BIND_GLOBAL_CONSTANT( JOY_BUTTON_9 ), + BIND_GLOBAL_CONSTANT( JOY_BUTTON_10 ), + BIND_GLOBAL_CONSTANT( JOY_BUTTON_11 ), + BIND_GLOBAL_CONSTANT( JOY_BUTTON_12 ), + BIND_GLOBAL_CONSTANT( JOY_BUTTON_13 ), + BIND_GLOBAL_CONSTANT( JOY_BUTTON_14 ), + BIND_GLOBAL_CONSTANT( JOY_BUTTON_15 ), + BIND_GLOBAL_CONSTANT( JOY_BUTTON_MAX ), + + BIND_GLOBAL_CONSTANT( JOY_SNES_A ), + BIND_GLOBAL_CONSTANT( JOY_SNES_B ), + BIND_GLOBAL_CONSTANT( JOY_SNES_X ), + BIND_GLOBAL_CONSTANT( JOY_SNES_Y ), + + BIND_GLOBAL_CONSTANT( JOY_SONY_CIRCLE ), + BIND_GLOBAL_CONSTANT( JOY_SONY_X ), + BIND_GLOBAL_CONSTANT( JOY_SONY_SQUARE ), + BIND_GLOBAL_CONSTANT( JOY_SONY_TRIANGLE ), + + BIND_GLOBAL_CONSTANT( JOY_SEGA_B ), + BIND_GLOBAL_CONSTANT( JOY_SEGA_A ), + BIND_GLOBAL_CONSTANT( JOY_SEGA_X ), + BIND_GLOBAL_CONSTANT( JOY_SEGA_Y ), + + BIND_GLOBAL_CONSTANT( JOY_XBOX_B ), + BIND_GLOBAL_CONSTANT( JOY_XBOX_A ), + BIND_GLOBAL_CONSTANT( JOY_XBOX_X ), + BIND_GLOBAL_CONSTANT( JOY_XBOX_Y ), + + BIND_GLOBAL_CONSTANT( JOY_DS_A ), + BIND_GLOBAL_CONSTANT( JOY_DS_B ), + BIND_GLOBAL_CONSTANT( JOY_DS_X ), + BIND_GLOBAL_CONSTANT( JOY_DS_Y ), + + BIND_GLOBAL_CONSTANT( JOY_SELECT ), + BIND_GLOBAL_CONSTANT( JOY_START ), + BIND_GLOBAL_CONSTANT( JOY_DPAD_UP ), + BIND_GLOBAL_CONSTANT( JOY_DPAD_DOWN ), + BIND_GLOBAL_CONSTANT( JOY_DPAD_LEFT ), + BIND_GLOBAL_CONSTANT( JOY_DPAD_RIGHT ), + BIND_GLOBAL_CONSTANT( JOY_L ), + BIND_GLOBAL_CONSTANT( JOY_L2 ), + BIND_GLOBAL_CONSTANT( JOY_L3 ), + BIND_GLOBAL_CONSTANT( JOY_R ), + BIND_GLOBAL_CONSTANT( JOY_R2 ), + BIND_GLOBAL_CONSTANT( JOY_R3 ), + + BIND_GLOBAL_CONSTANT( JOY_AXIS_0 ), + BIND_GLOBAL_CONSTANT( JOY_AXIS_1 ), + BIND_GLOBAL_CONSTANT( JOY_AXIS_2 ), + BIND_GLOBAL_CONSTANT( JOY_AXIS_3 ), + BIND_GLOBAL_CONSTANT( JOY_AXIS_4 ), + BIND_GLOBAL_CONSTANT( JOY_AXIS_5 ), + BIND_GLOBAL_CONSTANT( JOY_AXIS_6 ), + BIND_GLOBAL_CONSTANT( JOY_AXIS_7 ), + BIND_GLOBAL_CONSTANT( JOY_AXIS_MAX ), + + BIND_GLOBAL_CONSTANT( JOY_ANALOG_0_X ), + BIND_GLOBAL_CONSTANT( JOY_ANALOG_0_Y ), + + BIND_GLOBAL_CONSTANT( JOY_ANALOG_1_X ), + BIND_GLOBAL_CONSTANT( JOY_ANALOG_1_Y ), + + BIND_GLOBAL_CONSTANT( JOY_ANALOG_2_X ), + BIND_GLOBAL_CONSTANT( JOY_ANALOG_2_Y ), + + + // error list + + BIND_GLOBAL_CONSTANT( OK ), + BIND_GLOBAL_CONSTANT( FAILED ), ///< Generic fail error + BIND_GLOBAL_CONSTANT( ERR_UNAVAILABLE ), ///< What is requested is unsupported/unavailable + BIND_GLOBAL_CONSTANT( ERR_UNCONFIGURED ), ///< The object being used hasnt been properly set up yet + BIND_GLOBAL_CONSTANT( ERR_UNAUTHORIZED ), ///< Missing credentials for requested resource + BIND_GLOBAL_CONSTANT( ERR_PARAMETER_RANGE_ERROR ), ///< Parameter given out of range + BIND_GLOBAL_CONSTANT( ERR_OUT_OF_MEMORY ), ///< Out of memory + BIND_GLOBAL_CONSTANT( ERR_FILE_NOT_FOUND ), + BIND_GLOBAL_CONSTANT( ERR_FILE_BAD_DRIVE ), + BIND_GLOBAL_CONSTANT( ERR_FILE_BAD_PATH ), + BIND_GLOBAL_CONSTANT( ERR_FILE_NO_PERMISSION ), + BIND_GLOBAL_CONSTANT( ERR_FILE_ALREADY_IN_USE ), + BIND_GLOBAL_CONSTANT( ERR_FILE_CANT_OPEN ), + BIND_GLOBAL_CONSTANT( ERR_FILE_CANT_WRITE ), + BIND_GLOBAL_CONSTANT( ERR_FILE_CANT_READ ), + BIND_GLOBAL_CONSTANT( ERR_FILE_UNRECOGNIZED ), + BIND_GLOBAL_CONSTANT( ERR_FILE_CORRUPT ), + BIND_GLOBAL_CONSTANT( ERR_FILE_EOF ), + BIND_GLOBAL_CONSTANT( ERR_CANT_OPEN ), ///< Can't open a resource/socket/file + BIND_GLOBAL_CONSTANT( ERR_CANT_CREATE ), + BIND_GLOBAL_CONSTANT( ERROR_QUERY_FAILED ), + BIND_GLOBAL_CONSTANT( ERR_ALREADY_IN_USE ), + BIND_GLOBAL_CONSTANT( ERR_LOCKED ), ///< resource is locked + BIND_GLOBAL_CONSTANT( ERR_TIMEOUT ), + BIND_GLOBAL_CONSTANT( ERR_CANT_AQUIRE_RESOURCE ), + BIND_GLOBAL_CONSTANT( ERR_INVALID_DATA ), ///< Data passed is invalid + BIND_GLOBAL_CONSTANT( ERR_INVALID_PARAMETER ), ///< Parameter passed is invalid + BIND_GLOBAL_CONSTANT( ERR_ALREADY_EXISTS ), ///< When adding ), item already exists + BIND_GLOBAL_CONSTANT( ERR_DOES_NOT_EXIST ), ///< When retrieving/erasing ), it item does not exist + BIND_GLOBAL_CONSTANT( ERR_DATABASE_CANT_READ ), ///< database is full + BIND_GLOBAL_CONSTANT( ERR_DATABASE_CANT_WRITE ), ///< database is full + BIND_GLOBAL_CONSTANT( ERR_COMPILATION_FAILED ), + BIND_GLOBAL_CONSTANT( ERR_METHOD_NOT_FOUND ), + BIND_GLOBAL_CONSTANT( ERR_LINK_FAILED ), + BIND_GLOBAL_CONSTANT( ERR_SCRIPT_FAILED ), + BIND_GLOBAL_CONSTANT( ERR_CYCLIC_LINK ), + BIND_GLOBAL_CONSTANT( ERR_BUSY ), + BIND_GLOBAL_CONSTANT( ERR_HELP ), ///< user requested help!! + BIND_GLOBAL_CONSTANT( ERR_BUG ), ///< a bug in the software certainly happened ), due to a double check failing or unexpected behavior. + BIND_GLOBAL_CONSTANT( ERR_WTF ), + + BIND_GLOBAL_CONSTANT( PROPERTY_HINT_NONE ), + BIND_GLOBAL_CONSTANT( PROPERTY_HINT_RANGE ), + BIND_GLOBAL_CONSTANT( PROPERTY_HINT_EXP_RANGE ), + BIND_GLOBAL_CONSTANT( PROPERTY_HINT_ENUM ), + BIND_GLOBAL_CONSTANT( PROPERTY_HINT_LENGTH ), + BIND_GLOBAL_CONSTANT( PROPERTY_HINT_FLAGS ), + BIND_GLOBAL_CONSTANT( PROPERTY_HINT_FILE ), + BIND_GLOBAL_CONSTANT( PROPERTY_HINT_DIR ), + BIND_GLOBAL_CONSTANT( PROPERTY_HINT_RESOURCE_TYPE ), + + BIND_GLOBAL_CONSTANT( PROPERTY_USAGE_STORAGE ), + BIND_GLOBAL_CONSTANT( PROPERTY_USAGE_STORAGE ), + BIND_GLOBAL_CONSTANT( PROPERTY_USAGE_EDITOR ), + BIND_GLOBAL_CONSTANT( PROPERTY_USAGE_NETWORK ), + BIND_GLOBAL_CONSTANT( PROPERTY_USAGE_DEFAULT ), + {"TYPE_NIL",Variant::NIL}, + {"TYPE_BOOL",Variant::BOOL}, + {"TYPE_INT",Variant::INT}, + {"TYPE_REAL",Variant::REAL}, + {"TYPE_STRING",Variant::STRING}, + {"TYPE_VECTOR2",Variant::VECTOR2}, // 5 + {"TYPE_RECT2",Variant::RECT2}, + {"TYPE_VECTOR3",Variant::VECTOR3}, + {"TYPE_MATRIX32",Variant::MATRIX32}, + {"TYPE_PLANE",Variant::PLANE}, + {"TYPE_QUAT",Variant::QUAT}, // 10 + {"TYPE_AABB",Variant::_AABB}, //sorry naming convention fail :( not like it's used often + {"TYPE_MATRIX3",Variant::MATRIX3}, + {"TYPE_TRANSFORM",Variant::TRANSFORM}, + {"TYPE_COLOR",Variant::COLOR}, + {"TYPE_IMAGE",Variant::IMAGE}, // 15 + {"TYPE_NODE_PATH",Variant::NODE_PATH}, + {"TYPE_RID",Variant::_RID}, + {"TYPE_OBJECT",Variant::OBJECT}, + {"TYPE_INPUT_EVENT",Variant::INPUT_EVENT}, + {"TYPE_DICTIONARY",Variant::DICTIONARY}, // 20 + {"TYPE_ARRAY",Variant::ARRAY}, + {"TYPE_RAW_ARRAY",Variant::RAW_ARRAY}, + {"TYPE_INT_ARRAY",Variant::INT_ARRAY}, + {"TYPE_REAL_ARRAY",Variant::REAL_ARRAY}, + {"TYPE_STRING_ARRAY",Variant::STRING_ARRAY}, // 25 + {"TYPE_VECTOR2_ARRAY",Variant::VECTOR2_ARRAY}, + {"TYPE_VECTOR3_ARRAY",Variant::VECTOR3_ARRAY}, + {"TYPE_COLOR_ARRAY",Variant::COLOR_ARRAY}, + {"TYPE_MAX",Variant::VARIANT_MAX}, + {NULL,0} + +}; + +int GlobalConstants::get_global_constant_count() { + + int i=0; + while(_global_constants[i].name) + i++; + return i; + +} + +const char *GlobalConstants::get_global_constant_name(int p_idx) { + + return _global_constants[p_idx].name; +} + +int GlobalConstants::get_global_constant_value(int p_idx) { + + return _global_constants[p_idx].value; +} + + diff --git a/core/global_constants.h b/core/global_constants.h new file mode 100644 index 00000000000..cd8304bd454 --- /dev/null +++ b/core/global_constants.h @@ -0,0 +1,41 @@ +/*************************************************************************/ +/* global_constants.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 GLOBAL_CONSTANTS_H +#define GLOBAL_CONSTANTS_H + + +class GlobalConstants { +public: + + static int get_global_constant_count(); + static const char *get_global_constant_name(int p_idx); + static int get_global_constant_value(int p_idx); +}; + +#endif // GLOBAL_CONSTANTS_H diff --git a/core/globals.cpp b/core/globals.cpp new file mode 100644 index 00000000000..85e03ead816 --- /dev/null +++ b/core/globals.cpp @@ -0,0 +1,1447 @@ +/*************************************************************************/ +/* globals.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 "globals.h" +#include "os/dir_access.h" +#include "os/file_access.h" + +#include "os/keyboard.h" +#include "io/marshalls.h" +#include "bind/core_bind.h" +#include "os/os.h" +#include "io/file_access_pack.h" +#include "io/file_access_network.h" + +Globals *Globals::singleton=NULL; + +Globals *Globals::get_singleton() { + + return singleton; +} + +String Globals::get_resource_path() const { + + return resource_path; +}; + +String Globals::localize_path(const String& p_path) const { + + if (resource_path=="") + return p_path; //not initialied yet + + if (p_path.begins_with("res://")) + return p_path.simplify_path(); + + + DirAccess *dir = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + + String path = p_path.replace("\\","/").simplify_path(); + + if (dir->change_dir(path)==OK) { + + String cwd = dir->get_current_dir(); + cwd = cwd.replace("\\","/"); + + memdelete(dir); + + if (!cwd.begins_with(resource_path)) { + return p_path; + }; + + return cwd.replace_first(resource_path, "res:/"); + } else { + + memdelete(dir); + + int sep = path.find_last("/"); + if (sep == -1) { + return "res://"+path; + }; + String parent = path.substr(0, sep); + + String plocal = localize_path(parent); + if (plocal == "") { + return ""; + }; + return plocal + path.substr(sep, path.size() - sep); + }; + +} + +void Globals::set_persisting(const String& p_name, bool p_persist) { + + ERR_FAIL_COND(!props.has(p_name)); + props[p_name].persist=p_persist; +} + +bool Globals::is_persisting(const String& p_name) const { + + ERR_FAIL_COND_V(!props.has(p_name),false); + return props[p_name].persist; + +} + + +String Globals::globalize_path(const String& p_path) const { + + if (p_path.begins_with("res://")) { + + if (resource_path != "") { + + return p_path.replace("res:/",resource_path); + }; + return p_path.replace("res://", ""); + }; + + return p_path; +} + + +bool Globals::_set(const StringName& p_name, const Variant& p_value) { + + _THREAD_SAFE_METHOD_ + + if (p_value.get_type()==Variant::NIL) + props.erase(p_name); + else { + if (props.has(p_name)) { + if (!props[p_name].overrided) + props[p_name].variant=p_value; + } else { + props[p_name]=VariantContainer(p_value,last_order++); + } + } + + if (!disable_platform_override) { + + String s=String(p_name); + int sl = s.find("/"); + int p = s.find("."); + if (p!=-1 && sl!=-1 && p < sl) { + + Vector ps = s.substr(0,sl).split("."); + String prop=s.substr(sl,s.length()-sl); + for(int i=1;iget_name()) { + + String fullprop=ps[0]+prop; + + set(fullprop,p_value); + props[fullprop].overrided=true; + } + } + } + + } + + return true; +} +bool Globals::_get(const StringName& p_name,Variant &r_ret) const { + + _THREAD_SAFE_METHOD_ + + const VariantContainer *v=props.getptr(p_name); + if (!v) + return false; + r_ret=v->variant; + return true; + +} + +struct _VCSort { + + String name; + Variant::Type type; + int order; + int flags; + + bool operator<(const _VCSort& p_vcs) const{ return order==p_vcs.order?name *p_list) const { + + _THREAD_SAFE_METHOD_ + + const String *k=NULL; + Set<_VCSort> vclist; + + while ((k=props.next(k))) { + + const VariantContainer *v=props.getptr(*k); + + if (v->hide_from_editor) + continue; + + _VCSort vc; + vc.name=*k; + vc.order=v->order; + vc.type=v->variant.get_type(); + if (vc.name.begins_with("input/") || vc.name.begins_with("import/") || vc.name.begins_with("export/") || vc.name.begins_with("/remap") || vc.name.begins_with("/locale") || vc.name.begins_with("/autoload")) + vc.flags=PROPERTY_USAGE_CHECKABLE|PROPERTY_USAGE_STORAGE; + else + vc.flags=PROPERTY_USAGE_CHECKABLE|PROPERTY_USAGE_EDITOR|PROPERTY_USAGE_STORAGE; + + if (v->persist) { + vc.flags|=PROPERTY_USAGE_CHECKED; + } + + vclist.insert(vc); + } + + for(Set<_VCSort>::Element *E=vclist.front();E;E=E->next()) { + + if (custom_prop_info.has(E->get().name)) { + PropertyInfo pi=custom_prop_info[E->get().name]; + pi.name=E->get().name; + pi.usage=E->get().flags; + p_list->push_back( pi ); + } else + p_list->push_back( PropertyInfo(E->get().type, E->get().name,PROPERTY_HINT_NONE,"",E->get().flags) ); + } +} + + + +bool Globals::_load_resource_pack(const String& p_pack) { + + if (PackedData::get_singleton()->is_disabled()) + return false; + + bool ok = PackedData::get_singleton()->add_pack(p_pack)==OK; + + if (!ok) + return false; + + //if data.pck is found, all directory access will be from here + DirAccess::make_default(DirAccess::ACCESS_RESOURCES); + + return true; +} + +Error Globals::setup(const String& p_path) { + + //an absolute mess of a function, must be cleaned up and reorganized somehow at some point + + //_load_settings(p_path+"/override.cfg"); + + if (OS::get_singleton()->get_executable_path()!="") { + + if (_load_resource_pack(OS::get_singleton()->get_executable_path())) { + + if (p_path!="") { + resource_path=p_path; + } else { + DirAccess *d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + resource_path=d->get_current_dir(); + memdelete(d); + + } + if (_load_settings("res://engine.cfg")==OK || _load_settings_binary("res://engine.cfb")==OK) { + + _load_settings("res://override.cfg"); + + } + + + + return OK; + } + + } + + + if (FileAccessNetworkClient::get_singleton()) { + + if (_load_settings("res://engine.cfg")==OK || _load_settings_binary("res://engine.cfb")==OK) { + + _load_settings("res://override.cfg"); + + } + + return OK; + } + if (OS::get_singleton()->get_resource_dir()!="") { + //OS will call Globals->get_resource_path which will be empty if not overriden! + //if the OS would rather use somewhere else, then it will not be empty. + resource_path=OS::get_singleton()->get_resource_dir().replace("\\","/"); + if (resource_path.length() && resource_path[ resource_path.length()-1]=='/') + resource_path=resource_path.substr(0,resource_path.length()-1); // chop end + + print_line("has res dir: "+resource_path); + if (!_load_resource_pack("res://data.pck")) + _load_resource_pack("res://data.pcz"); + // make sure this is load from the resource path + print_line("exists engine cfg? "+itos(FileAccess::exists("/engine.cfg"))); + if (_load_settings("res://engine.cfg")==OK || _load_settings_binary("res://engine.cfb")==OK) { + print_line("loaded engine.cfg"); + _load_settings("res://override.cfg"); + + } + + return OK; + } + + DirAccess *d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + if (!d) { + + resource_path = p_path; + + } else { + + d->change_dir(p_path); + + String candidate = d->get_current_dir(); + String current_dir = d->get_current_dir(); + bool found = false; + + while(true) { + //try to load settings in ascending through dirs shape! + + if (_load_resource_pack(current_dir+"/data.pck") || _load_resource_pack(current_dir+"/data.pcz")) { + if (_load_settings("res://engine.cfg")==OK || _load_settings_binary("res://engine.cfb")==OK) { + + _load_settings("res://override.cfg"); + found=true; + + + } + break; + } else if (_load_settings(current_dir+"/engine.cfg")==OK || _load_settings_binary(current_dir+"/engine.cfb")==OK) { + + _load_settings(current_dir+"/override.cfg"); + candidate=current_dir; + found=true; + break; + } + + d->change_dir(".."); + if (d->get_current_dir()==current_dir) + break; //not doing anything useful + current_dir=d->get_current_dir(); + } + + + resource_path=candidate; + resource_path = resource_path.replace("\\","/"); // windows path to unix path just in case + memdelete(d); + + if (!found) + return ERR_FILE_NOT_FOUND; + }; + + + if (resource_path.length() && resource_path[ resource_path.length()-1]=='/') + resource_path=resource_path.substr(0,resource_path.length()-1); // chop end + + return OK; +} + +bool Globals::has(String p_var) const { + + _THREAD_SAFE_METHOD_ + + return props.has(p_var); +} + +static Vector _decode_params(const String& p_string) { + + int begin=p_string.find("("); + ERR_FAIL_COND_V(begin==-1,Vector()); + begin++; + int end=p_string.find(")"); + ERR_FAIL_COND_V(end()); + return p_string.substr(begin,end-begin).split(","); +} + +static String _get_chunk(const String& str,int &pos, int close_pos) { + + + enum { + MIN_COMMA, + MIN_COLON, + MIN_CLOSE, + MIN_QUOTE, + MIN_PARENTHESIS, + MIN_CURLY_OPEN, + MIN_OPEN + }; + + int min_pos=close_pos; + int min_what=MIN_CLOSE; + +#define TEST_MIN(m_how,m_what) \ +{\ +int res = str.find(m_how,pos);\ +if (res!=-1 && res < min_pos) {\ + min_pos=res;\ + min_what=m_what;\ +}\ +}\ + + + TEST_MIN(",",MIN_COMMA); + TEST_MIN("[",MIN_OPEN); + TEST_MIN("{",MIN_CURLY_OPEN); + TEST_MIN("(",MIN_PARENTHESIS); + TEST_MIN("\"",MIN_QUOTE); + + int end=min_pos; + + + switch(min_what) { + + case MIN_COMMA: { + } break; + case MIN_CLOSE: { + //end because it's done + } break; + case MIN_QUOTE: { + end=str.find("\"",min_pos+1)+1; + ERR_FAIL_COND_V(end==-1,Variant()); + + } break; + case MIN_PARENTHESIS: { + + end=str.find(")",min_pos+1)+1; + ERR_FAIL_COND_V(end==-1,Variant()); + + } break; + case MIN_OPEN: { + int level=1; + end++; + while(end params = _decode_params(p_string); + ERR_FAIL_COND_V(params.size()!=1 && params.size()!=2,Variant()); + int scode=0; + + if (params[0].is_numeric()) + scode=params[0].to_int(); + else + scode=find_keycode(params[0]); + + InputEvent ie; + ie.type=InputEvent::KEY; + ie.key.scancode=scode; + + if (params.size()==2) { + String mods=params[1]; + if (mods.findn("C")!=-1) + ie.key.mod.control=true; + if (mods.findn("A")!=-1) + ie.key.mod.alt=true; + if (mods.findn("S")!=-1) + ie.key.mod.shift=true; + if (mods.findn("M")!=-1) + ie.key.mod.meta=true; + } + return ie; + + } + + if (str.begins_with("mbutton")) { + Vector params = _decode_params(p_string); + ERR_FAIL_COND_V(params.size()!=2,Variant()); + + InputEvent ie; + ie.type=InputEvent::MOUSE_BUTTON; + ie.device=params[0].to_int(); + ie.mouse_button.button_index=params[1].to_int(); + + return ie; + } + + if (str.begins_with("jbutton")) { + Vector params = _decode_params(p_string); + ERR_FAIL_COND_V(params.size()!=2,Variant()); + + InputEvent ie; + ie.type=InputEvent::JOYSTICK_BUTTON; + ie.device=params[0].to_int(); + ie.joy_button.button_index=params[1].to_int(); + + return ie; + } + + if (str.begins_with("jaxis")) { + Vector params = _decode_params(p_string); + ERR_FAIL_COND_V(params.size()!=2,Variant()); + + InputEvent ie; + ie.type=InputEvent::JOYSTICK_MOTION; + ie.device=params[0].to_int(); + ie.joy_motion.axis=params[1].to_int(); + + return ie; + } + if (str.begins_with("img")) { + Vector params = _decode_params(p_string); + if (params.size()==0) { + return Image(); + } + + ERR_FAIL_COND_V(params.size()!=5,Image()); + + String format=params[0].strip_edges(); + + Image::Format imgformat; + + if (format=="grayscale") { + imgformat=Image::FORMAT_GRAYSCALE; + } else if (format=="intensity") { + imgformat=Image::FORMAT_INTENSITY; + } else if (format=="grayscale_alpha") { + imgformat=Image::FORMAT_GRAYSCALE_ALPHA; + } else if (format=="rgb") { + imgformat=Image::FORMAT_RGB; + } else if (format=="rgba") { + imgformat=Image::FORMAT_RGBA; + } else if (format=="indexed") { + imgformat=Image::FORMAT_INDEXED; + } else if (format=="indexed_alpha") { + imgformat=Image::FORMAT_INDEXED_ALPHA; + } else if (format=="bc1") { + imgformat=Image::FORMAT_BC1; + } else if (format=="bc2") { + imgformat=Image::FORMAT_BC2; + } else if (format=="bc3") { + imgformat=Image::FORMAT_BC3; + } else if (format=="bc4") { + imgformat=Image::FORMAT_BC4; + } else if (format=="bc5") { + imgformat=Image::FORMAT_BC5; + } else if (format=="custom") { + imgformat=Image::FORMAT_CUSTOM; + } else { + + ERR_FAIL_V( Image() ); + } + + int mipmaps=params[1].to_int(); + int w=params[2].to_int(); + int h=params[3].to_int(); + + if (w == 0 && w == 0) { + //r_v = Image(w, h, imgformat); + return Image(); + }; + + + String data=params[4]; + int datasize=data.length()/2; + DVector pixels; + pixels.resize(datasize); + DVector::Write wb = pixels.write(); + const CharType *cptr=data.c_str(); + + int idx=0; + uint8_t byte; + while( idx='0' && c<='9') || (c>='A' && c<='F') || (c>='a' && c<='f') ) { + + if (idx&1) { + + byte|=HEX2CHR(c); + wb[idx>>1]=byte; + } else { + + byte=HEX2CHR(c)<<4; + } + + idx++; + } + + } + + wb = DVector::Write(); + + return Image(w,h,mipmaps,imgformat,pixels); + } + + if (str.find(",")!=-1) { //vector2 or vector3 + Vector farr = str.split_floats(",",true); + if (farr.size()==2) { + return Point2(farr[0],farr[1]); + } + if (farr.size()==3) { + return Vector3(farr[0],farr[1],farr[2]); + } + ERR_FAIL_V(Variant()); + } + + + return Variant(); +} + + +Error Globals::_load_settings_binary(const String p_path) { + + Error err; + FileAccess *f= FileAccess::open(p_path,FileAccess::READ,&err); + if (err!=OK) { + return err; + } + + + uint8_t hdr[4]; + f->get_buffer(hdr,4); + if (hdr[0]!='E'|| hdr[1]!='C' || hdr[2]!='F' || hdr[3]!='G') { + + memdelete(f); + ERR_EXPLAIN("Corrupted header in binary engine.cfb (not ECFG)"); + ERR_FAIL_V(ERR_FILE_CORRUPT;) + } + + uint32_t count=f->get_32(); + + for(int i=0;iget_32(); + CharString cs; + cs.resize(slen+1); + cs[slen]=0; + f->get_buffer((uint8_t*)cs.ptr(),slen); + String key; + key.parse_utf8(cs.ptr()); + + uint32_t vlen=f->get_32(); + Vector d; + d.resize(vlen); + f->get_buffer(d.ptr(),vlen); + Variant value; + Error err = decode_variant(value,d.ptr(),d.size()); + ERR_EXPLAIN("Error decoding property: "+key); + ERR_CONTINUE(err!=OK); + set(key,value); + set_persisting(key,true); + } + + return OK; +} +Error Globals::_load_settings(const String p_path) { + + + Error err; + FileAccess *f= FileAccess::open(p_path,FileAccess::READ,&err); + + if (err!=OK) { + + return err; + } + + + String line; + String section; + String subpath; + + int line_count = 0; + + while(!f->eof_reached()) { + + String line = f->get_line().strip_edges(); + line_count++; + + if (line=="") + continue; + + // find comments + + { + + int pos=0; + while (true) { + int ret = line.find(";",pos); + if (ret==-1) + break; + + int qc=0; + for(int i=0;i 0) { + ERR_PRINT(String("Syntax error on line "+itos(line_count)+" of file "+p_path).ascii().get_data()); + }; + }; + } + + memdelete(f); + + + return OK; +} + +static String _encode_variant(const Variant& p_variant) { + + switch(p_variant.get_type()) { + + case Variant::BOOL: { + bool val = p_variant; + return (val?"true":"false"); + } break; + case Variant::INT: { + int val = p_variant; + return itos(val); + } break; + case Variant::REAL: { + float val = p_variant; + return rtos(val)+(val==int(val)?".0":""); + } break; + case Variant::STRING: { + String val = p_variant; + return "\""+val.xml_escape()+"\""; + } break; + case Variant::COLOR: { + + Color val = p_variant; + return "#"+val.to_html(); + } break; + case Variant::STRING_ARRAY: + case Variant::INT_ARRAY: + case Variant::REAL_ARRAY: + case Variant::ARRAY: { + Array arr = p_variant; + String str="["; + for(int i=0;i0) + str+=", "; + str+=_encode_variant(arr[i]); + } + str+="]"; + return str; + } break; + case Variant::DICTIONARY: { + Dictionary d = p_variant; + String str="{"; + List keys; + d.get_key_list(&keys); + for(List::Element *E=keys.front();E;E=E->next()) { + + if (E!=keys.front()) + str+=", "; + str+=_encode_variant(E->get()); + str+=":"; + str+=_encode_variant(d[E->get()]); + + } + str+="}"; + return str; + } break; + case Variant::IMAGE: { + String str="img("; + + Image img=p_variant; + if (!img.empty()) { + + String format; + switch(img.get_format()) { + + case Image::FORMAT_GRAYSCALE: format="grayscale"; break; + case Image::FORMAT_INTENSITY: format="intensity"; break; + case Image::FORMAT_GRAYSCALE_ALPHA: format="grayscale_alpha"; break; + case Image::FORMAT_RGB: format="rgb"; break; + case Image::FORMAT_RGBA: format="rgba"; break; + case Image::FORMAT_INDEXED : format="indexed"; break; + case Image::FORMAT_INDEXED_ALPHA: format="indexed_alpha"; break; + case Image::FORMAT_BC1: format="bc1"; break; + case Image::FORMAT_BC2: format="bc2"; break; + case Image::FORMAT_BC3: format="bc3"; break; + case Image::FORMAT_BC4: format="bc4"; break; + case Image::FORMAT_BC5: format="bc5"; break; + case Image::FORMAT_CUSTOM: format="custom custom_size="+itos(img.get_data().size())+""; break; + default: {} + } + + str+=format+", "; + str+=itos(img.get_mipmaps())+", "; + str+=itos(img.get_width())+", "; + str+=itos(img.get_height())+", "; + DVector data = img.get_data(); + int ds=data.size(); + DVector::Read r = data.read(); + for(int i=0;i>4], hex[byte&0xF], 0}; + str+=bstr; + } + } + str+=")"; + return str; + } break; + case Variant::INPUT_EVENT: { + + InputEvent ev = p_variant; + + switch(ev.type) { + + case InputEvent::KEY: { + + String mods; + if (ev.key.mod.control) + mods+="C"; + if (ev.key.mod.shift) + mods+="S"; + if (ev.key.mod.alt) + mods+="A"; + if (ev.key.mod.meta) + mods+="M"; + if (mods!="") + mods=", "+mods; + + return "key("+keycode_get_string(ev.key.scancode)+mods+")"; + } break; + case InputEvent::MOUSE_BUTTON: { + + return "mbutton("+itos(ev.device)+", "+itos(ev.mouse_button.button_index)+")"; + } break; + case InputEvent::JOYSTICK_BUTTON: { + + return "jbutton("+itos(ev.device)+", "+itos(ev.joy_button.button_index)+")"; + } break; + case InputEvent::JOYSTICK_MOTION: { + + return "jaxis("+itos(ev.device)+", "+itos(ev.joy_motion.axis)+")"; + } break; + default: { + + return "nil"; + } break; + + } + } break; + default: {} + } + + return "nil"; //don't know wha to do with this +} + + +int Globals::get_order(const String& p_name) const { + + ERR_FAIL_COND_V(!props.has(p_name),-1); + return props[p_name].order; +} + + +void Globals::set_order(const String& p_name, int p_order){ + + ERR_FAIL_COND(!props.has(p_name)); + props[p_name].order=p_order; +} + +void Globals::clear(const String& p_name) { + + ERR_FAIL_COND(!props.has(p_name)); + props.erase(p_name); +} + +Error Globals::save() { + + return save_custom(get_resource_path()+"/engine.cfg"); +} + +Error Globals::_save_settings_binary(const String& p_file,const Map > &props,const CustomMap& p_custom) { + + + Error err; + FileAccess *file = FileAccess::open(p_file,FileAccess::WRITE,&err); + if (err!=OK) { + + ERR_EXPLAIN("Coudln't save engine.cfb at "+p_file); + ERR_FAIL_COND_V(err,err) + } + + uint8_t hdr[4]={'E','C','F','G'}; + file->store_buffer(hdr,4); + + int count=0; + + for(Map >::Element *E=props.front();E;E=E->next()) { + + for(List::Element *F=E->get().front();F;F=F->next()) { + + count++; + } + } + + file->store_32(count); //store how many properties are saved + + + for(Map >::Element *E=props.front();E;E=E->next()) { + + for(List::Element *F=E->get().front();F;F=F->next()) { + + String key = F->get(); + if (E->key()!="") + key=E->key()+"/"+key; + Variant value; + if (p_custom.has(key)) + value=p_custom[key]; + else + value = get(key); + + file->store_32(key.length()); + file->store_string(key); + + int len; + Error err = encode_variant(value,NULL,len); + if (err!=OK) + memdelete(file); + ERR_FAIL_COND_V( err != OK, ERR_INVALID_DATA ); + + Vector buff; + buff.resize(len); + + err = encode_variant(value,&buff[0],len); + if (err!=OK) + memdelete(file); + ERR_FAIL_COND_V( err != OK, ERR_INVALID_DATA ); + file->store_32(len); + file->store_buffer(buff.ptr(),buff.size()); + } + } + + file->close(); + memdelete(file); + + + return OK; +} + + +Error Globals::_save_settings_text(const String& p_file,const Map > &props,const CustomMap& p_custom) { + + Error err; + FileAccess *file = FileAccess::open(p_file,FileAccess::WRITE,&err); + + if (err) { + ERR_EXPLAIN("Coudln't save engine.cfg - "+p_file); + ERR_FAIL_COND_V(err,err) + } + + for(Map >::Element *E=props.front();E;E=E->next()) { + + if (E!=props.front()) + file->store_string("\n"); + + if (E->key()!="") + file->store_string("["+E->key()+"]\n\n"); + for(List::Element *F=E->get().front();F;F=F->next()) { + + String key = F->get(); + if (E->key()!="") + key=E->key()+"/"+key; + Variant value; + if (p_custom.has(key)) + value=p_custom[key]; + else + value = get(key); + + file->store_string(F->get()+"="+_encode_variant(value)+"\n"); + + } + } + + file->close(); + memdelete(file); + + return OK; +} +Error Globals::save_custom(const String& p_path,const CustomMap& p_custom,const Set& p_ignore_masks) { + + ERR_FAIL_COND_V(p_path=="",ERR_INVALID_PARAMETER); + + const String *k=NULL; + Set<_VCSort> vclist; + + while ((k=props.next(k))) { + + const VariantContainer *v=props.getptr(*k); + + if (v->hide_from_editor) + continue; + + if (p_custom.has(*k)) + continue; + + bool discard=false; + + for(const Set::Element *E=p_ignore_masks.front();E;E=E->next()) { + + if ( (*k).match(E->get())) { + discard=true; + break; + } + } + + if (discard) + continue; + + _VCSort vc; + vc.name=*k; + vc.order=v->order; + vc.type=v->variant.get_type(); + vc.flags=PROPERTY_USAGE_CHECKABLE|PROPERTY_USAGE_EDITOR|PROPERTY_USAGE_STORAGE; + if (!v->persist) + continue; + + + vclist.insert(vc); + } + + for(const Map::Element *E=p_custom.front();E;E=E->next()) { + + + _VCSort vc; + vc.name=E->key(); + vc.order=0xFFFFFFF; + vc.type=E->get().get_type(); + vc.flags=PROPERTY_USAGE_STORAGE; + vclist.insert(vc); + } + + Map > props; + + for(Set<_VCSort>::Element *E=vclist.front();E;E=E->next()) { + + String category = E->get().name; + String name = E->get().name; + + int div = category.find("/"); + + if (div<0) + category=""; + else { + + category=category.substr(0,div); + name=name.substr(div+1,name.size()); + } + props[category].push_back(name); + } + + + + if (p_path.ends_with(".cfg")) + return _save_settings_text(p_path,props,p_custom); + else if (p_path.ends_with(".cfb")) + return _save_settings_binary(p_path,props,p_custom); + else { + + ERR_EXPLAIN("Unknown config file format: "+p_path); + ERR_FAIL_V( ERR_FILE_UNRECOGNIZED ); + } + + return OK; + +#if 0 + Error err = file->open(dst_file,FileAccess::WRITE); + if (err) { + memdelete(file); + ERR_EXPLAIN("Coudln't save engine.cfg"); + ERR_FAIL_COND_V(err,err) + } + + + for(Map >::Element *E=props.front();E;E=E->next()) { + + if (E!=props.front()) + file->store_string("\n"); + + if (E->key()!="") + file->store_string("["+E->key()+"]\n\n"); + for(List::Element *F=E->get().front();F;F=F->next()) { + + String key = F->get(); + if (E->key()!="") + key=E->key()+"/"+key; + Variant value; + + if (p_custom.has(key)) + value=p_custom[key]; + else + value = get(key); + + file->store_string(F->get()+"="+_encode_variant(value)+"\n"); + + } + } + + file->close(); + memdelete(file); + + + return OK; +#endif +} + +Variant _GLOBAL_DEF( const String& p_var, const Variant& p_default) { + + if (Globals::get_singleton()->has(p_var)) + return Globals::get_singleton()->get(p_var); + Globals::get_singleton()->set(p_var,p_default); + return p_default; + +} + +void Globals::add_singleton(const Singleton &p_singleton) { + + singletons.push_back(p_singleton); +} + +Object* Globals::get_singleton_object(const String& p_name) const { + + for(const List::Element *E=singletons.front();E;E=E->next()) { + if (E->get().name == p_name) { + return E->get().ptr; + }; + }; + + return NULL; +}; + +bool Globals::has_singleton(const String& p_name) const { + + return get_singleton_object(p_name) != NULL; +}; + +void Globals::get_singletons(List *p_singletons) { + + for(List::Element *E=singletons.front();E;E=E->next()) + p_singletons->push_back(E->get()); +} + +Vector Globals::get_optimizer_presets() const { + + List pi; + Globals::get_singleton()->get_property_list(&pi); + Vector names; + + for (List::Element *E=pi.front();E;E=E->next()) { + + if (!E->get().name.begins_with("optimizer_presets/")) + continue; + names.push_back(E->get().name.get_slice("/",1)); + } + + names.sort(); + + return names; + +} + +void Globals::set_custom_property_info(const String& p_prop,const PropertyInfo& p_info) { + + ERR_FAIL_COND(!props.has(p_prop)); + custom_prop_info[p_prop]=p_info; + +} + +void Globals::set_disable_platform_override(bool p_disable) { + + disable_platform_override=p_disable; +} + + +void Globals::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("has","name"),&Globals::has); + ObjectTypeDB::bind_method(_MD("set_order","name","pos"),&Globals::set_order); + ObjectTypeDB::bind_method(_MD("get_order","name"),&Globals::get_order); + ObjectTypeDB::bind_method(_MD("set_persisting","name","enable"),&Globals::set_persisting); + ObjectTypeDB::bind_method(_MD("is_persisting","name"),&Globals::is_persisting); + ObjectTypeDB::bind_method(_MD("clear","name"),&Globals::clear); + ObjectTypeDB::bind_method(_MD("localize_path","path"),&Globals::localize_path); + ObjectTypeDB::bind_method(_MD("globalize_path","path"),&Globals::globalize_path); + ObjectTypeDB::bind_method(_MD("save"),&Globals::save); + ObjectTypeDB::bind_method(_MD("has_singleton"),&Globals::has_singleton); + ObjectTypeDB::bind_method(_MD("get_singleton"),&Globals::get_singleton_object); +} + +Globals::Globals() { + + + singleton=this; + last_order=0; + disable_platform_override=false; + + + + Array va; + InputEvent key; + key.type=InputEvent::KEY; + InputEvent joyb; + joyb.type=InputEvent::JOYSTICK_BUTTON; + + + set("application/name","" ); + set("application/main_scene",""); + custom_prop_info["application/main_scene"]=PropertyInfo(Variant::STRING,"application/main_scene",PROPERTY_HINT_FILE,"xml,res,scn,xscn"); + + + key.key.scancode=KEY_RETURN; + va.push_back(key); + key.key.scancode=KEY_ENTER; + va.push_back(key); + key.key.scancode=KEY_SPACE; + va.push_back(key); + joyb.joy_button.button_index=JOY_BUTTON_0; + va.push_back(joyb); + set("input/ui_accept",va); + + va=Array(); + key.key.scancode=KEY_ESCAPE; + va.push_back(key); + joyb.joy_button.button_index=JOY_BUTTON_1; + va.push_back(joyb); + set("input/ui_cancel",va); + + va=Array(); + key.key.scancode=KEY_TAB; + va.push_back(key); + set("input/ui_focus_next",va); + + va=Array(); + key.key.scancode=KEY_TAB; + key.key.mod.shift=true; + va.push_back(key); + set("input/ui_focus_prev",va); + key.key.mod.shift=false; + + va=Array(); + key.key.scancode=KEY_LEFT; + va.push_back(key); + joyb.joy_button.button_index=JOY_DPAD_LEFT; + va.push_back(joyb); + set("input/ui_left",va); + + va=Array(); + key.key.scancode=KEY_RIGHT; + va.push_back(key); + joyb.joy_button.button_index=JOY_DPAD_RIGHT; + va.push_back(joyb); + set("input/ui_right",va); + + va=Array(); + key.key.scancode=KEY_UP; + va.push_back(key); + joyb.joy_button.button_index=JOY_DPAD_UP; + va.push_back(joyb); + set("input/ui_up",va); + + va=Array(); + key.key.scancode=KEY_DOWN; + va.push_back(key); + joyb.joy_button.button_index=JOY_DPAD_DOWN; + va.push_back(joyb); + set("input/ui_down",va); + + + va=Array(); + key.key.scancode=KEY_PAGEUP; + va.push_back(key); + set("input/ui_page_up",va); + + va=Array(); + key.key.scancode=KEY_PAGEDOWN; + va.push_back(key); + set("input/ui_page_down",va); + +// set("display/orientation", "landscape"); + + + custom_prop_info["display/orientation"]=PropertyInfo(Variant::STRING,"display/orientation",PROPERTY_HINT_ENUM,"landscape,portrait,reverse_landscape,reverse_portrait,sensor_landscape,sensor_portrait,sensor"); + custom_prop_info["render/mipmap_policy"]=PropertyInfo(Variant::INT,"render/mipmap_policy",PROPERTY_HINT_ENUM,"Allow,Allow For Po2,Disallow"); + custom_prop_info["render/thread_model"]=PropertyInfo(Variant::INT,"render/thread_model",PROPERTY_HINT_ENUM,"Single-Unsafe,Single-Safe,Multi-Threaded"); + set("display/emulate_touchscreen",false); +} + + +Globals::~Globals() { + + singleton=NULL; +} + + diff --git a/core/globals.h b/core/globals.h new file mode 100644 index 00000000000..375c59a25e2 --- /dev/null +++ b/core/globals.h @@ -0,0 +1,138 @@ +/*************************************************************************/ +/* globals.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 GLOBALS_H +#define GLOBALS_H + +#include "object.h" +#include "set.h" +#include "os/thread_safe.h" +/** + @author Juan Linietsky +*/ + + +class Globals : public Object { + + OBJ_TYPE( Globals, Object ); + _THREAD_SAFE_CLASS_ + +public: + + typedef Map CustomMap; + + struct Singleton { + StringName name; + Object *ptr; + Singleton(const StringName& p_name=StringName(), Object *p_ptr=NULL) { name=p_name; ptr=p_ptr; } + }; + +protected: + + struct VariantContainer { + int order; + bool persist; + Variant variant; + bool hide_from_editor; + bool overrided; + VariantContainer(){ order=0; hide_from_editor=false; persist=false; overrided=false; } + VariantContainer(const Variant& p_variant, int p_order, bool p_persist=false) { variant=p_variant; order=p_order; hide_from_editor=false; persist=p_persist; overrided=false; } + }; + + int last_order; + HashMap props; + String resource_path; + HashMap custom_prop_info; + bool disable_platform_override; + + + 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 *p_list) const; + + static Globals *singleton; + + Error _load_settings(const String p_path); + Error _load_settings_binary(const String p_path); + + Error _save_settings_text(const String& p_file,const Map > &props,const CustomMap& p_custom=CustomMap()); + Error _save_settings_binary(const String& p_file,const Map > &props,const CustomMap& p_custom=CustomMap()); + + List singletons; + + + bool _load_resource_pack(const String& p_pack); + +protected: + + static void _bind_methods(); +public: + + + bool has(String p_var) const; + String localize_path(const String& p_path) const; + String globalize_path(const String& p_path) const; + + void set_persisting(const String& p_name, bool p_persist); + bool is_persisting(const String& p_name) const; + + String get_resource_path() const; + + static Globals *get_singleton(); + + void clear(const String& p_name); + int get_order(const String& p_name) const; + void set_order(const String& p_name, int p_order); + + Error setup(const String& p_path); + + Error save_custom(const String& p_path="",const CustomMap& p_custom=CustomMap(),const Set& p_ignore_masks=Set()); + Error save(); + void set_custom_property_info(const String& p_prop,const PropertyInfo& p_info); + + void add_singleton(const Singleton &p_singleton); + void get_singletons(List *p_singletons); + + bool has_singleton(const String& p_name) const; + + Vector get_optimizer_presets() const; + + void set_disable_platform_override(bool p_disable); + Object* get_singleton_object(const String& p_name) const; + + void register_global_defaults(); + + Globals(); + ~Globals(); + +}; + +//not a macro any longer +Variant _GLOBAL_DEF( const String& p_var, const Variant& p_default); +#define GLOBAL_DEF(m_var,m_value) _GLOBAL_DEF(m_var,m_value) +#endif diff --git a/core/hash_map.h b/core/hash_map.h new file mode 100644 index 00000000000..02a6600c3b9 --- /dev/null +++ b/core/hash_map.h @@ -0,0 +1,630 @@ +/*************************************************************************/ +/* hash_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 HASH_MAP_H +#define HASH_MAP_H + +#include "hashfuncs.h" +#include "error_macros.h" +#include "ustring.h" +#include "os/memory.h" +#include "list.h" + + +class HashMapHahserDefault { +public: + + static _FORCE_INLINE_ uint32_t hash(const String &p_string) { return p_string.hash(); } + static _FORCE_INLINE_ uint32_t hash(const char *p_cstr) { return hash_djb2(p_cstr); } + static _FORCE_INLINE_ uint32_t hash(const uint64_t p_int) { + uint64_t v=p_int; + v = (~v) + (v << 18); // v = (v << 18) - v - 1; + v = v ^ (v >> 31); + v = v * 21; // v = (v + (v << 2)) + (v << 4); + v = v ^ (v >> 11); + v = v + (v << 6); + v = v ^ (v >> 22); + return (int) v; + } + static _FORCE_INLINE_ uint32_t hash(const int64_t p_int) { return hash(uint64_t(p_int)); } + + + static _FORCE_INLINE_ uint32_t hash(const uint32_t p_int) { return p_int; } + static _FORCE_INLINE_ uint32_t hash(const int32_t p_int) { return (uint32_t)p_int; } + static _FORCE_INLINE_ uint32_t hash(const uint16_t p_int) { return p_int; } + static _FORCE_INLINE_ uint32_t hash(const int16_t p_int) { return (uint32_t)p_int; } + static _FORCE_INLINE_ uint32_t hash(const uint8_t p_int) { return p_int; } + static _FORCE_INLINE_ uint32_t hash(const int8_t p_int) { return (uint32_t)p_int; } + static _FORCE_INLINE_ uint32_t hash(const wchar_t p_wchar) { return (uint32_t)p_wchar; } +// static _FORCE_INLINE_ uint32_t hash(const void* p_ptr) { return uint32_t(uint64_t(p_ptr))*(0x9e3779b1L); } +}; + +/** + * @class HashMap + * @author Juan Linietsky + * + * Implementation of a standard Hashing HashMap, for quick lookups of Data associated with a Key. + * The implementation provides hashers for the default types, if you need a special kind of hasher, provide + * your own. + * @param TKey Key, search is based on it, needs to be hasheable. It is unique in this container. + * @param TData Data, data associated with the key + * @param Hasher Hasher object, needs to provide a valid static hash function for TKey + * @param MIN_HASH_TABLE_POWER Miminum size of the hash table, as a power of two. You rarely need to change this parameter. + * @param RELATIONSHIP Relationship at which the hash table is resized. if amount of elements is RELATIONSHIP + * times bigger than the hash table, table is resized to solve this condition. if RELATIONSHIP is zero, table is always MIN_HASH_TABLE_POWER. + * +*/ + +template +class HashMap { +public: + + struct Pair { + + TKey key; + TData data; + + Pair() {} + Pair(const TKey& p_key, const TData& p_data) { key=p_key; data=p_data; } + }; + + +private: + struct Entry { + + uint32_t hash; + Entry *next; + Pair pair; + + Entry() { next=0; } + }; + + Entry **hash_table; + uint8_t hash_table_power; + uint32_t elements; + + void make_hash_table() { + + ERR_FAIL_COND( hash_table ); + + + hash_table = memnew_arr( Entry*, (1< ( (1< ( (1<(int)MIN_HASH_TABLE_POWER) && ((int)elements < ( (1<<(hash_table_power-1)) * RELATIONSHIP ) ) ) { + + /* rehash down */ + new_hash_table_power=hash_table_power-1; + + while( (int)elements < ( (1<<(new_hash_table_power-1)) * RELATIONSHIP ) ) { + + new_hash_table_power--; + } + + if (new_hash_table_power<(int)MIN_HASH_TABLE_POWER) + new_hash_table_power=MIN_HASH_TABLE_POWER; + } + + + if (new_hash_table_power==-1) + return; + + Entry ** new_hash_table = memnew_arr( Entry*, (1<next; + int new_pos = se->hash & ((1<next=new_hash_table[new_pos]; + new_hash_table[new_pos]=se; + } + + } + + if (hash_table) + memdelete_arr( hash_table ); + hash_table=new_hash_table; + hash_table_power=new_hash_table_power; + + } + + + /* I want to have only one function.. */ + _FORCE_INLINE_ const Entry * get_entry( const TKey& p_key ) const { + + uint32_t hash = Hasher::hash( p_key ); + uint32_t index = hash&((1<hash == hash && e->pair.key == p_key ) { + + /* the pair exists in this hashtable, so just update data */ + return e; + } + + e=e->next; + } + + return NULL; + } + + Entry * create_entry(const TKey& p_key) { + + /* if entry doesn't exist, create it */ + Entry *e = memnew( Entry ); + ERR_FAIL_COND_V(!e,NULL); /* out of memory */ + uint32_t hash = Hasher::hash( p_key ); + uint32_t index = hash&((1<next = hash_table[index]; + e->hash = hash; + e->pair.key=p_key; + + hash_table[index]=e; + elements++; + + return e; + } + + + void copy_from(const HashMap& p_t) { + + if (&p_t==this) + return; /* much less bother with that */ + + clear(); + + if (!p_t.hash_table || p_t.hash_table_power==0) + return; /* not copying from empty table */ + + hash_table = memnew_arr(Entry*,1<next=hash_table[i]; + hash_table[i]=le; + + e=e->next; + } + + + } + + + } +public: + + + void set( const TKey& p_key, const TData& p_data ) { + + set( Pair( p_key, p_data ) ); + + } + + void set( const Pair& p_pair ) { + + if (!hash_table) + make_hash_table(); // if no table, make one + else + check_hash_table(); // perform mantenience routine + + /* As said, i want to have only one get_entry */ + Entry *e = const_cast( get_entry(p_pair.key) ); + + /* if we made it up to here, the pair doesn't exist, create and assign */ + + if (!e) { + + e=create_entry(p_pair.key); + if (!e) + return; + } + + e->pair.data = p_pair.data; + + } + + + bool has( const TKey& p_key ) const { + + return getptr(p_key)!=NULL; + } + + /** + * Get a key from data, return a const reference. + * WARNING: this doesn't check errors, use either getptr and check NULL, or check + * first with has(key) + */ + + const TData& get( const TKey& p_key ) const { + + const TData* res = getptr(p_key); + ERR_FAIL_COND_V(!res,*res); + return *res; + } + + TData& get( const TKey& p_key ) { + + TData* res = getptr(p_key); + ERR_FAIL_COND_V(!res,*res); + return *res; + } + + /** + * Same as get, except it can return NULL when item was not found. + * This is mainly used for speed purposes. + */ + + + _FORCE_INLINE_ TData* getptr( const TKey& p_key ) { + + if (!hash_table) + return NULL; + + Entry *e=const_cast(get_entry(p_key )); + + if (e) + return &e->pair.data; + + return NULL; + + } + + _FORCE_INLINE_ const TData* getptr( const TKey& p_key ) const { + + if (!hash_table) + return NULL; + + const Entry *e=const_cast(get_entry(p_key )); + + if (e) + return &e->pair.data; + + return NULL; + + } + + /** + * Same as get, except it can return NULL when item was not found. + * This version is custom, will take a hash and a custom key (that should support operator==() + */ + + template + _FORCE_INLINE_ TData* custom_getptr( C p_custom_key,uint32_t p_custom_hash ) { + + if (!hash_table) + return NULL; + + uint32_t hash = p_custom_hash; + uint32_t index = hash&((1<hash == hash && e->pair.key == p_custom_key ) { + + /* the pair exists in this hashtable, so just update data */ + return &e->pair.data; + } + + e=e->next; + } + + return NULL; + } + + template + _FORCE_INLINE_ const TData* custom_getptr( C p_custom_key,uint32_t p_custom_hash ) const { + + if (!hash_table) + return NULL; + + uint32_t hash = p_custom_hash; + uint32_t index = hash&((1<hash == hash && e->pair.key == p_custom_key ) { + + /* the pair exists in this hashtable, so just update data */ + return &e->pair.data; + } + + e=e->next; + } + + return NULL; + } + + + /** + * Erase an item, return true if erasing was succesful + */ + + bool erase( const TKey& p_key ) { + + if (!hash_table) + return false; + + uint32_t hash = Hasher::hash( p_key ); + uint32_t index = hash&((1<hash == hash && e->pair.key == p_key ) { + + if (p) { + + p->next=e->next; + } else { + //begin of list + hash_table[index]=e->next; + } + + memdelete(e); + elements--; + + if (elements==0) + erase_hash_table(); + else + check_hash_table(); + return true; + } + + p=e; + e=e->next; + } + + + return false; + + } + + inline const TData& operator[](const TKey& p_key) const { //constref + + return get(p_key); + } + inline TData& operator[](const TKey& p_key ) { //assignment + + if (!hash_table) + make_hash_table(); // if no table, make one + else + check_hash_table(); // perform mantenience routine + + Entry *e = const_cast( get_entry(p_key) ); + + /* if we made it up to here, the pair doesn't exist, create */ + if (!e) { + + e=create_entry(p_key); + if (!e) + return *(TData*)NULL; /* panic! */ + } + + return e->pair.data; + + } + + /** + * Get the next key to p_key, and the first key if p_key is null. + * Returns a pointer to the next key if found, NULL otherwise. + * Adding/Removing elements while iterating will, of course, have unexpected results, don't do it. + * + * Example: + * + * const TKey *k=NULL; + * + * while( (k=table.next(k)) ) { + * + * print( *k ); + * } + * + */ + const TKey* next(const TKey* p_key) const { + + if (!hash_table) return NULL; + + if (!p_key) { /* get the first key */ + + for (int i=0;i<(1<pair.key; + } + } + + } else { /* get the next key */ + + const Entry *e = get_entry( *p_key ); + ERR_FAIL_COND_V( !e, NULL ); /* invalid key supplied */ + + if (e->next) { + /* if there is a "next" in the list, return that */ + return &e->next->pair.key; + } else { + /* go to next entries */ + uint32_t index = e->hash&((1<pair.key; + } + } + } + + /* nothing found, was at end */ + + } + + + return NULL; /* nothing found */ + } + + inline unsigned int size() const { + + return elements; + } + + inline bool empty() const { + + return elements==0; + } + + void clear() { + + /* clean up */ + if (hash_table) { + for (int i=0;i<(1<next; + memdelete( e ); + } + } + + memdelete_arr( hash_table ); + } + + hash_table=0; + hash_table_power=0; + elements=0; + } + + + void operator=(const HashMap& p_table) { + + copy_from(p_table); + } + + HashMap() { + hash_table=NULL; + elements=0; + hash_table_power=0; + } + + void get_key_list(List *p_keys) const { + if (!hash_table) + return; + for(int i=0;i<(1<push_back(e->pair.key); + e=e->next; + } + } + } + + HashMap(const HashMap& p_table) { + + hash_table=NULL; + elements=0; + hash_table_power=0; + + copy_from(p_table); + + } + + ~HashMap() { + + clear(); + } + +}; + +#endif diff --git a/core/hashfuncs.h b/core/hashfuncs.h new file mode 100644 index 00000000000..6dae82bc55f --- /dev/null +++ b/core/hashfuncs.h @@ -0,0 +1,116 @@ +/*************************************************************************/ +/* hashfuncs.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 HASHFUNCS_H +#define HASHFUNCS_H + + +#include "typedefs.h" + +/** + * Hashing functions + */ + + +/** + * DJB2 Hash function + * @param C String + * @return 32-bits hashcode + */ +static inline uint32_t hash_djb2(const char *p_cstr) { + + const unsigned char* chr=(const unsigned char*)p_cstr; + uint32_t hash = 5381; + uint32_t c; + + while ((c = *chr++)) + hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ + + return hash; +} + +static inline uint32_t hash_djb2_buffer(uint8_t *p_buff, int p_len) { + + uint32_t hash = 5381; + + for(int i=0;i +static inline uint32_t make_uint32_t(T p_in) { + + union { + T t; + uint32_t _u32; + } _u; + _u._u32=0; + _u.t=p_in; + return _u._u32; +} + + +static inline uint64_t hash_djb2_one_64(uint64_t p_in,uint64_t p_prev=5381) { + + return ((p_prev<<5)+p_prev)+p_in; +} + + +template +static inline uint64_t make_uint64_t(T p_in) { + + union { + T t; + uint64_t _u64; + } _u; + _u._u64=0; // in case p_in is smaller + + _u.t=p_in; + return _u._u64; +} + + + +#endif diff --git a/core/image.cpp b/core/image.cpp new file mode 100644 index 00000000000..a9485feff2a --- /dev/null +++ b/core/image.cpp @@ -0,0 +1,1737 @@ +/*************************************************************************/ +/* image.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 "image.h" +#include "hash_map.h" +#include "core/io/image_loader.h" +#include "core/os/copymem.h" + +#include "print_string.h" +#include + + +void Image::_put_pixel(int p_x,int p_y, const BColor& p_color, unsigned char *p_data) { + + _put_pixelw(p_x,p_y,width,p_color,p_data); + +} + +void Image::_put_pixelw(int p_x,int p_y, int p_width, const BColor& p_color, unsigned char *p_data) { + + + int ofs=p_y*p_width+p_x; + + switch(format) { + case FORMAT_GRAYSCALE: { + + p_data[ofs]=p_color.gray(); + } break; + case FORMAT_INTENSITY: { + + p_data[ofs]=p_color.a; + } break; + case FORMAT_GRAYSCALE_ALPHA: { + + p_data[ofs*2]=p_color.gray(); + p_data[ofs*2+1]=p_color.a; + + } break; + case FORMAT_RGB: { + + p_data[ofs*3+0]=p_color.r; + p_data[ofs*3+1]=p_color.g; + p_data[ofs*3+2]=p_color.b; + + } break; + case FORMAT_RGBA: { + + p_data[ofs*4+0]=p_color.r; + p_data[ofs*4+1]=p_color.g; + p_data[ofs*4+2]=p_color.b; + p_data[ofs*4+3]=p_color.a; + + } break; + case FORMAT_INDEXED: + case FORMAT_INDEXED_ALPHA: { + + ERR_FAIL(); + } break; + default: {}; + + } + +} + + + +void Image::_get_mipmap_offset_and_size(int p_mipmap,int &r_offset, int &r_width,int &r_height) const { + + int w=width; + int h=height; + int ofs=0; + + int pixel_size = get_format_pixel_size(format); + int pixel_rshift = get_format_pixel_rshift(format); + int minw,minh; + _get_format_min_data_size(format,minw,minh); + + for(int i=0;i>=pixel_rshift; + ofs+=s; + w=MAX(minw,w>>1); + h=MAX(minh,h>>1); + } + + r_offset=ofs; + r_width=w; + r_height=h; +} +int Image::get_mipmap_offset(int p_mipmap) const { + + ERR_FAIL_INDEX_V(p_mipmap,(mipmaps+1),-1); + + int ofs,w,h; + _get_mipmap_offset_and_size(p_mipmap,ofs,w,h); + return ofs; +} + +void Image::get_mipmap_offset_and_size(int p_mipmap,int &r_ofs, int &r_size) const { + + int ofs,w,h; + _get_mipmap_offset_and_size(p_mipmap,ofs,w,h); + int ofs2; + _get_mipmap_offset_and_size(p_mipmap+1,ofs2,w,h); + r_ofs=ofs; + r_size=ofs2-ofs; + +} + +void Image::put_pixel(int p_x,int p_y, const Color& p_color,int p_mipmap){ + + ERR_FAIL_INDEX(p_mipmap,mipmaps+1); + int ofs,w,h; + _get_mipmap_offset_and_size(p_mipmap,ofs,w,h); + ERR_FAIL_INDEX(p_x,w); + ERR_FAIL_INDEX(p_y,h); + + DVector::Write wp = data.write(); + unsigned char *data_ptr=wp.ptr(); + + _put_pixelw(p_x,p_y,w,BColor(p_color.r*255,p_color.g*255,p_color.b*255,p_color.a*255),&data_ptr[ofs]); + +} + + +Image::BColor Image::_get_pixel(int p_x,int p_y,const unsigned char *p_data,int p_data_size) const{ + + return _get_pixelw(p_x,p_y,width,p_data,p_data_size); +} +Image::BColor Image::_get_pixelw(int p_x,int p_y,int p_width,const unsigned char *p_data,int p_data_size) const{ + + int ofs=p_y*p_width+p_x; + BColor result(0,0,0,0); + switch(format) { + + case FORMAT_GRAYSCALE: { + + result=BColor(p_data[ofs],p_data[ofs],p_data[ofs],255.0); + } break; + case FORMAT_INTENSITY: { + + result=BColor(255,255,255,p_data[ofs]); + } break; + case FORMAT_GRAYSCALE_ALPHA: { + + result=BColor(p_data[ofs*2],p_data[ofs*2],p_data[ofs*2],p_data[ofs*2+1]); + + } break; + case FORMAT_RGB: { + + result=BColor(p_data[ofs*3],p_data[ofs*3+1],p_data[ofs*3+2]); + + } break; + case FORMAT_RGBA: { + + result=BColor(p_data[ofs*4],p_data[ofs*4+1],p_data[ofs*4+2],p_data[ofs*4+3]); + } break; + case FORMAT_INDEXED_ALPHA: { + + int pitch = 4; + const uint8_t* pal = &p_data[ p_data_size - pitch * 256 ]; + int idx = p_data[ofs]; + result=BColor(pal[idx * pitch + 0] , pal[idx * pitch + 1] , pal[idx * pitch + 2] , pal[idx * pitch + 3] ); + + } break; + case FORMAT_INDEXED: { + + int pitch = 3; + const uint8_t* pal = &p_data[ p_data_size - pitch * 256 ]; + int idx = p_data[ofs]; + result=BColor(pal[idx * pitch + 0] , pal[idx * pitch + 1] , pal[idx * pitch + 2] ,255); + } break; + case FORMAT_YUV_422: { + + int y, u, v; + if (p_x % 2) { + const uint8_t* yp = &p_data[p_width * 2 * p_y + p_x * 2]; + u = *(yp-1); + y = yp[0]; + v = yp[1]; + } else { + + const uint8_t* yp = &p_data[p_width * 2 * p_y + p_x * 2]; + y = yp[0]; + u = yp[1]; + v = yp[3]; + }; + + int32_t r = 1.164 * (y - 16) + 1.596 * (v - 128); + int32_t g = 1.164 * (y - 16) - 0.813 * (v - 128) - 0.391 * (u - 128); + int32_t b = 1.164 * (y - 16) + 2.018 * (u - 128); + result = BColor(CLAMP(r, 0, 255), CLAMP(g, 0, 255), CLAMP(b, 0, 255)); + } break; + case FORMAT_YUV_444: { + + uint8_t y, u, v; + const uint8_t* yp = &p_data[p_width * 3 * p_y + p_x * 3]; + y = yp[0]; + u = yp[1]; + v = yp[2]; + + int32_t r = 1.164 * (y - 16) + 1.596 * (v - 128); + int32_t g = 1.164 * (y - 16) - 0.813 * (v - 128) - 0.391 * (u - 128); + int32_t b = 1.164 * (y - 16) + 2.018 * (u - 128); + result = BColor(CLAMP(r, 0, 255), CLAMP(g, 0, 255), CLAMP(b, 0, 255)); + } break; + default:{} + + } + + return result; + +} + +void Image::put_indexed_pixel(int p_x, int p_y, uint8_t p_idx,int p_mipmap) { + + ERR_FAIL_COND(format != FORMAT_INDEXED && format != FORMAT_INDEXED_ALPHA); + ERR_FAIL_INDEX(p_mipmap,mipmaps+1); + int ofs,w,h; + _get_mipmap_offset_and_size(p_mipmap,ofs,w,h); + ERR_FAIL_INDEX(p_x,w); + ERR_FAIL_INDEX(p_y,h); + + data.set(ofs + p_y * w + p_x, p_idx); +}; + +uint8_t Image::get_indexed_pixel(int p_x, int p_y,int p_mipmap) const { + + ERR_FAIL_COND_V(format != FORMAT_INDEXED && format != FORMAT_INDEXED_ALPHA, 0); + + ERR_FAIL_INDEX_V(p_mipmap,mipmaps+1,0); + int ofs,w,h; + _get_mipmap_offset_and_size(p_mipmap,ofs,w,h); + ERR_FAIL_INDEX_V(p_x,w,0); + ERR_FAIL_INDEX_V(p_y,h,0); + + + return data[ofs + p_y * w + p_x]; +}; + +void Image::set_pallete(const DVector& p_data) { + + + int len = p_data.size(); + + ERR_FAIL_COND(format != FORMAT_INDEXED && format != FORMAT_INDEXED_ALPHA); + ERR_FAIL_COND(format == FORMAT_INDEXED && len!=(256*3)); + ERR_FAIL_COND(format == FORMAT_INDEXED_ALPHA && len!=(256*4)); + + int ofs,w,h; + _get_mipmap_offset_and_size(mipmaps+1,ofs,w,h); + + int pal_ofs = ofs; + data.resize(pal_ofs + p_data.size()); + + DVector::Write wp = data.write(); + unsigned char *dst=wp.ptr() + pal_ofs; + + DVector::Read r = data.read(); + const unsigned char *src=r.ptr(); + + copymem(dst, src, len); +}; + +int Image::get_width() const { + + return width; +} +int Image::get_height() const{ + + return height; +} + +int Image::get_mipmaps() const { + + + return mipmaps; +} + +Color Image::get_pixel(int p_x,int p_y,int p_mipmap) const { + + + ERR_FAIL_INDEX_V(p_mipmap,mipmaps+1,Color()); + int ofs,w,h; + _get_mipmap_offset_and_size(p_mipmap,ofs,w,h); + ERR_FAIL_INDEX_V(p_x,w,Color()); + ERR_FAIL_INDEX_V(p_y,h,Color()); + + + int len = data.size(); + DVector::Read r = data.read(); + const unsigned char*data_ptr=r.ptr(); + BColor c = _get_pixelw(p_x,p_y,w,&data_ptr[ofs],len); + return Color( c.r/255.0,c.g/255.0,c.b/255.0,c.a/255.0 ); +} + +void Image::convert( Format p_new_format ){ + + if (data.size()==0) + return; + + if (p_new_format==format) + return; + + if (format>=FORMAT_BC1 || p_new_format>=FORMAT_BC1) { + + ERR_EXPLAIN("Cannot convert to <-> from compressed/custom image formats (for now)."); + ERR_FAIL(); + } + + if (p_new_format==FORMAT_INDEXED || p_new_format==FORMAT_INDEXED_ALPHA) { + + + return; + } + + + Image new_img(width,height,0,p_new_format); + + int len=data.size(); + + DVector::Read r = data.read(); + DVector::Write w = new_img.data.write(); + + const uint8_t *rptr = r.ptr(); + uint8_t *wptr = w.ptr(); + + if (p_new_format==FORMAT_RGBA && format==FORMAT_INDEXED_ALPHA) { + + //optimized unquantized form + int dataend = len-256*4; + const uint32_t *palpos = (const uint32_t*)&rptr[dataend]; + uint32_t *dst32 = (uint32_t *)wptr; + + for(int i=0;i::Read(); + w = DVector::Write(); + + bool gen_mipmaps=mipmaps>0; + + *this=new_img; + + if (gen_mipmaps) + generate_mipmaps(); + + +} + +Image::Format Image::get_format() const{ + + return format; +} + +template +static void _scale_bilinear(const uint8_t* p_src, uint8_t* p_dst, uint32_t p_src_width, uint32_t p_src_height, uint32_t p_dst_width, uint32_t p_dst_height) { + + enum { + FRAC_BITS=8, + FRAC_LEN=(1<> FRAC_BITS; + + + uint32_t src_yofs_down = (i+1)*p_src_height/p_dst_height; + if (src_yofs_down>=p_src_height) + src_yofs_down=p_src_height-1; + + //src_yofs_up*=CC; + //src_yofs_down*=CC; + + uint32_t y_ofs_up = src_yofs_up * p_src_width * CC; + uint32_t y_ofs_down = src_yofs_down * p_src_width * CC; + + for(uint32_t j=0;j> FRAC_BITS; + uint32_t src_xofs_right = (j+1)*p_src_width/p_dst_width; + if (src_xofs_right>=p_src_width) + src_xofs_right=p_src_width-1; + + src_xofs_left*=CC; + src_xofs_right*=CC; + + for(uint32_t l=0;l>FRAC_BITS); + uint32_t interp_down = p01+(((p11-p01)*src_xofs_frac)>>FRAC_BITS); + uint32_t interp = interp_up+(((interp_down-interp_up)*src_yofs_frac)>>FRAC_BITS); + interp>>=FRAC_BITS; + p_dst[i*p_dst_width*CC+j*CC+l]=interp; + } + } + } +} + + +template +static void _scale_nearest(const uint8_t* p_src, uint8_t* p_dst, uint32_t p_src_width, uint32_t p_src_height, uint32_t p_dst_width, uint32_t p_dst_height) { + + + for(uint32_t i=0;iMAX_WIDTH); + ERR_FAIL_COND(p_height>MAX_HEIGHT); + + + if (p_width==width && p_height==height) + return; + + Image dst( p_width, p_height, 0, format ); + + if (format==FORMAT_INDEXED) + p_interpolation=INTERPOLATE_NEAREST; + + + DVector::Read r = data.read(); + const unsigned char*r_ptr=r.ptr(); + + DVector::Write w = dst.data.write(); + unsigned char*w_ptr=w.ptr(); + + + switch(p_interpolation) { + + case INTERPOLATE_NEAREST: { + + switch(get_format_pixel_size(format)) { + case 1: _scale_nearest<1>(r_ptr,w_ptr,width,height,p_width,p_height); break; + case 2: _scale_nearest<2>(r_ptr,w_ptr,width,height,p_width,p_height); break; + case 3: _scale_nearest<3>(r_ptr,w_ptr,width,height,p_width,p_height); break; + case 4: _scale_nearest<4>(r_ptr,w_ptr,width,height,p_width,p_height); break; + } + } break; + case INTERPOLATE_BILINEAR: { + + switch(get_format_pixel_size(format)) { + case 1: _scale_bilinear<1>(r_ptr,w_ptr,width,height,p_width,p_height); break; + case 2: _scale_bilinear<2>(r_ptr,w_ptr,width,height,p_width,p_height); break; + case 3: _scale_bilinear<3>(r_ptr,w_ptr,width,height,p_width,p_height); break; + case 4: _scale_bilinear<4>(r_ptr,w_ptr,width,height,p_width,p_height); break; + } + + } break; + + } + + r = DVector::Read(); + w = DVector::Write(); + + if (mipmaps>0) + dst.generate_mipmaps(); + + *this=dst; +} +void Image::crop( int p_width, int p_height ) { + + if (!_can_modify(format)) { + ERR_EXPLAIN("Cannot crop in indexed, compressed or custom image formats."); + ERR_FAIL(); + } + ERR_FAIL_COND(p_width<=0); + ERR_FAIL_COND(p_height<=0); + ERR_FAIL_COND(p_width>MAX_WIDTH); + ERR_FAIL_COND(p_height>MAX_HEIGHT); + + /* to save memory, cropping should be done in-place, however, since this function + will most likely either not be used much, or in critical areas, for now it wont, because + it's a waste of time. */ + + if (p_width==width && p_height==height) + return; + + Image dst( p_width, p_height,0, format ); + + + for (int y=0;y=width || y>=height)? Color() : get_pixel(x,y); + dst.put_pixel(x,y,col); + } + } + + if (mipmaps>0) + dst.generate_mipmaps(); + *this=dst; + +} + +void Image::flip_y() { + + if (!_can_modify(format)) { + ERR_EXPLAIN("Cannot flip_y in indexed, compressed or custom image formats."); + ERR_FAIL(); + } + + bool gm=mipmaps; + + if (gm) + clear_mipmaps();; + + + + + for (int y=0;y<(height/2);y++) { + + for (int x=0;x>=pixshift; + + size+=s; + + if (p_mipmaps>=0 && mm==p_mipmaps) + break; + + if (p_mipmaps>=0) { + + w=MAX(minw,w>>1); + h=MAX(minh,h>>1); + } else { + if (w==minw && h==minh) + break; + w=MAX(minw,w>>1); + h=MAX(minh,h>>1); + } + mm++; + }; + + r_mipmaps=mm; + return size; +} + +bool Image::_can_modify(Format p_format) const { + + switch(p_format) { + + //these are OK + case FORMAT_GRAYSCALE: + case FORMAT_INTENSITY: + case FORMAT_GRAYSCALE_ALPHA: + case FORMAT_RGB: + case FORMAT_RGBA: + return true; + default: + return false; + } + + return false; +} + +template +static void _generate_po2_mipmap(const uint8_t* p_src, uint8_t* p_dst, uint32_t p_width, uint32_t p_height) { + + //fast power of 2 mipmap generation + uint32_t dst_w = p_width >> 1; + uint32_t dst_h = p_height >> 1; + + for(uint32_t i=0;i>2; + + } + + dst_ptr+=CC; + rup_ptr+=CC*2; + rdown_ptr+=CC*2; + } + } +} + + +Error Image::generate_mipmaps(int p_mipmaps,bool p_keep_existing) { + + if (!_can_modify(format)) { + ERR_EXPLAIN("Cannot generate mipmaps in indexed, compressed or custom image formats."); + ERR_FAIL_V(ERR_UNAVAILABLE); + + } + + int from_mm=1; + if (p_keep_existing) { + from_mm=mipmaps+1; + } + int size = _get_dst_image_size(width,height,format,mipmaps,p_mipmaps); + + data.resize(size); + + DVector::Write wp=data.write(); + + if (nearest_power_of_2(width)==uint32_t(width) && nearest_power_of_2(height)==uint32_t(height)) { + //use fast code for powers of 2 + int prev_ofs=0; + int prev_h=height; + int prev_w=width; + + for(int i=1;i=from_mm) { + + switch(format) { + + case FORMAT_GRAYSCALE: + case FORMAT_INTENSITY: _generate_po2_mipmap<1>(&wp[prev_ofs], &wp[ofs], prev_w,prev_h); break; + case FORMAT_GRAYSCALE_ALPHA: _generate_po2_mipmap<2>(&wp[prev_ofs], &wp[ofs], prev_w,prev_h); break; + case FORMAT_RGB: _generate_po2_mipmap<3>(&wp[prev_ofs], &wp[ofs], prev_w,prev_h); break; + case FORMAT_RGBA: _generate_po2_mipmap<4>(&wp[prev_ofs], &wp[ofs], prev_w,prev_h); break; + default: {} + } + } + + prev_ofs=ofs; + prev_w=w; + prev_h=h; + } + + + } else { + //use slow code.. + + //use bilinear filtered code for non powers of 2 + int prev_ofs=0; + int prev_h=height; + int prev_w=width; + + for(int i=1;i=from_mm) { + + switch(format) { + + case FORMAT_GRAYSCALE: + case FORMAT_INTENSITY: _scale_bilinear<1>(&wp[prev_ofs], &wp[ofs], prev_w,prev_h,w,h); break; + case FORMAT_GRAYSCALE_ALPHA: _scale_bilinear<2>(&wp[prev_ofs], &wp[ofs], prev_w,prev_h,w,h); break; + case FORMAT_RGB: _scale_bilinear<3>(&wp[prev_ofs], &wp[ofs], prev_w,prev_h,w,h); break; + case FORMAT_RGBA: _scale_bilinear<4>(&wp[prev_ofs], &wp[ofs], prev_w,prev_h,w,h); break; + default: {} + } + } + + prev_ofs=ofs; + prev_w=w; + prev_h=h; + } + + } + + + + + return OK; +} + +void Image::clear_mipmaps() { + + if (mipmaps==0) + return; + + if (format==FORMAT_CUSTOM) { + ERR_EXPLAIN("Cannot clear mipmaps in indexed, compressed or custom image formats."); + ERR_FAIL(); + + } + + if (empty()) + return; + + int ofs,w,h; + _get_mipmap_offset_and_size(1,ofs,w,h); + int palsize = get_format_pallete_size(format); + DVector pallete; + ERR_FAIL_COND(ofs+palsize > data.size()); //bug? + if (palsize) { + + pallete.resize(palsize); + DVector::Read r = data.read(); + DVector::Write w = pallete.write(); + + copymem(&w[0],&r[data.size()-palsize],palsize); + } + + data.resize(ofs+palsize); + + if (palsize) { + + DVector::Read r = pallete.read(); + DVector::Write w = data.write(); + + copymem(&w[ofs],&r[0],palsize); + } + + mipmaps=0; + +} + +void Image::make_normalmap(float p_height_scale) { + + if (!_can_modify(format)) { + ERR_EXPLAIN("Cannot crop in indexed, compressed or custom image formats."); + ERR_FAIL(); + } + + ERR_FAIL_COND( empty() ); + + Image normalmap(width,height,0, FORMAT_RGB); + /* + for (int y=0;y0)?get_pixel(x,y-1).gray()/255.0:center; + float down=(y<(height-1))?get_pixel(x,y+1).gray()/255.0:center; + float left=(x>0)?get_pixel(x-1,y).gray()/255.0:center; + float right=(x<(width-1))?get_pixel(x+1,y).gray()/255.0:center; + + + // uhm, how do i do this? .... + + Color result( (uint8_t)((normal.x+1.0)*127.0), (uint8_t)((normal.y+1.0)*127.0), (uint8_t)((normal.z+1.0)*127.0) ); + + normalmap.put_pixel( x, y, result ); + } + + } + */ + *this=normalmap; +} + +bool Image::empty() const { + + return (data.size()==0); +} + +DVector Image::get_data() const { + + return data; +} + +void Image::create(int p_width, int p_height, bool p_use_mipmaps,Format p_format) { + + + int mm=0; + int size = _get_dst_image_size(p_width,p_height,p_format,mm,p_use_mipmaps?-1:0); + data.resize( size ); + { + DVector::Write w= data.write(); + zeromem(w.ptr(),size); + } + + width=p_width; + height=p_height; + mipmaps=mm; + format=p_format; + + +} + +void Image::create(int p_width, int p_height, int p_mipmaps, Format p_format, const DVector& p_data) { + + ERR_FAIL_INDEX(p_width-1,MAX_WIDTH); + ERR_FAIL_INDEX(p_height-1,MAX_HEIGHT); + + if (p_format < FORMAT_CUSTOM) { + int mm; + int size = _get_dst_image_size(p_width,p_height,p_format,mm,p_mipmaps); + + if (size!=p_data.size()) { + ERR_EXPLAIN("Expected data size of "+itos(size)+" in Image::create()"); + ERR_FAIL_COND(p_data.size()!=size); + } + }; + + height=p_height; + width=p_width; + format=p_format; + data=p_data; + mipmaps=p_mipmaps; +} + + +void Image::create( const char ** p_xpm ) { + + + int size_width,size_height; + int pixelchars=0; + mipmaps=0; + bool has_alpha=false; + + enum Status { + READING_HEADER, + READING_COLORS, + READING_PIXELS, + DONE + }; + + Status status = READING_HEADER; + int line=0; + + HashMap colormap; + int colormap_size; + + while (status!=DONE) { + + const char * line_ptr = p_xpm[line]; + + + switch (status) { + + case READING_HEADER: { + + String line_str=line_ptr; + line_str.replace("\t"," "); + + size_width=line_str.get_slice(" ",0).to_int(); + size_height=line_str.get_slice(" ",1).to_int(); + colormap_size=line_str.get_slice(" ",2).to_int(); + pixelchars=line_str.get_slice(" ",3).to_int(); + ERR_FAIL_COND(colormap_size > 32766); + ERR_FAIL_COND(pixelchars > 5); + ERR_FAIL_COND(size_width > 32767); + ERR_FAIL_COND(size_height > 32767); + status=READING_COLORS; + } break; + case READING_COLORS: { + + String colorstring; + for (int i=0;i='0' && v<='9') + v-='0'; + else if (v>='A' && v<='F') + v=(v-'A')+10; + else if (v>='a' && v<='f') + v=(v-'a')+10; + else + break; + + switch(i) { + case 0: col_r=v<<4; break; + case 1: col_r|=v; break; + case 2: col_g=v<<4; break; + case 3: col_g|=v; break; + case 4: col_b=v<<4; break; + case 5: col_b|=v; break; + }; + + } + + // magenta mask + if (col_r==255 && col_g==0 && col_b==255) { + + colormap[colorstring]=Color(0,0,0,0); + has_alpha=true; + } else { + + colormap[colorstring]=Color(col_r/255.0,col_g/255.0,col_b/255.0,1.0); + } + + } + } + if (line==colormap_size) { + + status=READING_PIXELS; + create(size_width,size_height,0,has_alpha?FORMAT_RGBA:FORMAT_RGB); + } + } break; + case READING_PIXELS: { + + int y=line-colormap_size-1; + for (int x=0;x= FORMAT_YUV_422 && format <= FORMAT_YUV_444) + return ALPHA_NONE; + + int w,h; + _get_mipmap_offset_and_size(1,len,w,h); + + DVector::Read r = data.read(); + const unsigned char *data_ptr=r.ptr(); + + bool bit=false; + bool detected=false; + + switch(format) { + case FORMAT_INTENSITY: { + + for(int i=0;i>1);i++) { + DETECT_ALPHA(data_ptr[(i<<1)+1]); + } + + } break; + case FORMAT_RGBA: { + + for(int i=0;i<(len>>2);i++) { + DETECT_ALPHA(data_ptr[(i<<2)+3]) + } + + } break; + case FORMAT_INDEXED: { + + return ALPHA_NONE; + } break; + case FORMAT_INDEXED_ALPHA: { + + return ALPHA_BLEND; + } break; + case FORMAT_PVRTC2_ALPHA: + case FORMAT_PVRTC4_ALPHA: + case FORMAT_BC2: + case FORMAT_BC3: { + detected=true; + } break; + default: {} + } + + if (detected) + return ALPHA_BLEND; + else if (bit) + return ALPHA_BIT; + else + return ALPHA_NONE; + +} + +Error Image::load(const String& p_path) { + + return ImageLoader::load_image(p_path, this); +} + +bool Image::operator==(const Image& p_image) const { + + if (data.size() == 0 && p_image.data.size() == 0) + return true; + DVector::Read r = data.read(); + DVector::Read pr = p_image.data.read(); + + return r.ptr() == pr.ptr(); +} + + +int Image::get_format_pixel_size(Format p_format) { + + switch(p_format) { + case FORMAT_GRAYSCALE: { + + return 1; + } break; + case FORMAT_INTENSITY: { + + return 1; + } break; + case FORMAT_GRAYSCALE_ALPHA: { + + return 2; + } break; + case FORMAT_RGB: { + + return 3; + } break; + case FORMAT_RGBA: { + + return 4; + } break; + case FORMAT_INDEXED: { + + return 1; + } break; + case FORMAT_INDEXED_ALPHA: { + + return 1; + } break; + case FORMAT_BC1: + case FORMAT_BC2: + case FORMAT_BC3: + case FORMAT_BC4: + case FORMAT_BC5: { + + return 1; + } break; + case FORMAT_PVRTC2: + case FORMAT_PVRTC2_ALPHA: { + + return 1; + } break; + case FORMAT_PVRTC4: + case FORMAT_PVRTC4_ALPHA: { + + return 1; + } break; + case FORMAT_ETC: { + + return 1; + } break; + case FORMAT_YUV_422: { + return 2; + }; + case FORMAT_YUV_444: { + return 3; + } break; + case FORMAT_CUSTOM: { + + ERR_EXPLAIN("pixel size requested for custom image format, and it's unknown obviously"); + ERR_FAIL_V(1); + } break; + default:{ + ERR_EXPLAIN("Cannot obtain pixel size from this format"); + ERR_FAIL_V(1); + + } + } + return 0; +} + +int Image::get_image_data_size(int p_width, int p_height, Format p_format,int p_mipmaps) { + + int mm; + return _get_dst_image_size(p_width,p_height,p_format,mm,p_mipmaps); + +} + +int Image::get_image_required_mipmaps(int p_width, int p_height, Format p_format) { + + int mm; + _get_dst_image_size(p_width,p_height,p_format,mm,-1); + return mm; + +} + +void Image::_get_format_min_data_size(Format p_format,int &r_w, int &r_h) { + + + switch(p_format) { + case FORMAT_BC1: + case FORMAT_BC2: + case FORMAT_BC3: + case FORMAT_BC4: + case FORMAT_BC5: { + r_w=4; + r_h=4; + } break; + case FORMAT_PVRTC2: + case FORMAT_PVRTC2_ALPHA: { + + r_w=16; + r_h=8; + } break; + case FORMAT_PVRTC4_ALPHA: + case FORMAT_PVRTC4: { + + r_w=8; + r_h=8; + } break; + case FORMAT_ETC: { + + r_w=4; + r_h=4; + } break; + default: { + r_w=1; + r_h=1; + } break; + } + +} + + +int Image::get_format_pixel_rshift(Format p_format) { + + if (p_format==FORMAT_BC1 || p_format==FORMAT_BC4 || p_format==FORMAT_PVRTC4 || p_format==FORMAT_PVRTC4_ALPHA || p_format==FORMAT_ETC) + return 1; + else if (p_format==FORMAT_PVRTC2 || p_format==FORMAT_PVRTC2_ALPHA) + return 2; + else + return 0; +} + +int Image::get_format_pallete_size(Format p_format) { + + switch(p_format) { + case FORMAT_GRAYSCALE: { + + return 0; + } break; + case FORMAT_INTENSITY: { + + return 0; + } break; + case FORMAT_GRAYSCALE_ALPHA: { + + return 0; + } break; + case FORMAT_RGB: { + + return 0; + } break; + case FORMAT_RGBA: { + + return 0; + } break; + case FORMAT_INDEXED: { + + return 3*256; + } break; + case FORMAT_INDEXED_ALPHA: { + + return 4*256; + } break; + default:{} + } + return 0; +} + + +void Image::decompress() { + + if (format>=FORMAT_BC1 && format<=FORMAT_BC5 && _image_decompress_bc) + _image_decompress_bc(this); + if (format>=FORMAT_PVRTC2 && format<=FORMAT_PVRTC4_ALPHA && _image_decompress_pvrtc) + _image_decompress_pvrtc(this); + if (format==FORMAT_ETC && _image_decompress_etc) + _image_decompress_etc(this); +} + + +Error Image::compress(CompressMode p_mode) { + + switch(p_mode) { + + case COMPRESS_BC: { + + ERR_FAIL_COND_V(!_image_compress_bc_func, ERR_UNAVAILABLE); + _image_compress_bc_func(this); + } break; + case COMPRESS_PVRTC2: { + + ERR_FAIL_COND_V(!_image_compress_pvrtc2_func, ERR_UNAVAILABLE); + _image_compress_pvrtc2_func(this); + } break; + case COMPRESS_PVRTC4: { + + ERR_FAIL_COND_V(!_image_compress_pvrtc4_func, ERR_UNAVAILABLE); + _image_compress_pvrtc4_func(this); + } break; + case COMPRESS_ETC: { + + ERR_FAIL_COND_V(!_image_compress_etc_func, ERR_UNAVAILABLE); + _image_compress_etc_func(this); + } break; + } + + + return OK; +} + +Image Image::compressed(int p_mode) { + + Image ret = *this; + ret.compress((Image::CompressMode)p_mode); + + return ret; +}; + +Image::Image(const char **p_xpm) { + + width=0; + height=0; + mipmaps=0; + format=FORMAT_GRAYSCALE; + + create(p_xpm); +} + + +Image::Image(int p_width, int p_height,bool p_use_mipmaps, Format p_format) { + + width=0; + height=0; + mipmaps=0; + format=FORMAT_GRAYSCALE; + + create(p_width,p_height,p_use_mipmaps,p_format); + +} + +Image::Image(int p_width, int p_height, int p_mipmaps, Format p_format, const DVector& p_data) { + + width=0; + height=0; + mipmaps=0; + format=FORMAT_GRAYSCALE; + + create(p_width,p_height,p_mipmaps,p_format,p_data); + +} + + +Image Image::brushed(const Image& p_src, const Image& p_brush, const Point2& p_dest) const { + + Image img = *this; + img.brush_transfer(p_src,p_brush,p_dest); + return img; +} + +Rect2 Image::get_used_rect() const { + + if (format==FORMAT_GRAYSCALE || + format==FORMAT_RGB || + format==FORMAT_INDEXED || format>FORMAT_INDEXED_ALPHA) + return Rect2(Point2(),Size2(width,height)); + + int len = data.size(); + + if (len==0) + return Rect2(); + + int data_size = len; + DVector::Read r = data.read(); + const unsigned char *rptr=r.ptr(); + + int minx=0xFFFFFF,miny=0xFFFFFFF; + int maxx=-1,maxy=-1; + for(int i=0;i2; + if (!opaque) + continue; + if (i>maxx) + maxx=i; + if (j>maxy) + maxy=j; + if (i::Write wp = data.write(); + unsigned char *dst_data_ptr=wp.ptr(); + + + int src_data_size = p_src.data.size(); + DVector::Read rp = p_src.data.read(); + const unsigned char *src_data_ptr=rp.ptr(); + + int brush_data_size = p_brush.data.size(); + DVector::Read bp = p_brush.data.read(); + const unsigned char *src_brush_ptr=bp.ptr(); + + int bw = p_brush.get_width(); + int bh = p_brush.get_height(); + int dx=p_dest.x; + int dy=p_dest.y; + + for(int i=dy;i= height) + continue; + for(int j=dx;j=width) + continue; + + BColor src = p_src._get_pixel(j,i,src_data_ptr,src_data_size); + BColor dst = _get_pixel(j,i,dst_data_ptr,dst_data_size); + BColor brush = p_brush._get_pixel(j-dx,i-dy,src_brush_ptr,brush_data_size); + uint32_t mult = brush.r; + dst.r = dst.r + (((int32_t(src.r)-int32_t(dst.r))*mult)>>8); + dst.g = dst.g + (((int32_t(src.g)-int32_t(dst.g))*mult)>>8); + dst.b = dst.b + (((int32_t(src.b)-int32_t(dst.b))*mult)>>8); + dst.a = dst.a + (((int32_t(src.a)-int32_t(dst.a))*mult)>>8); + _put_pixel(j,i,dst,dst_data_ptr); + } + } +} + + +void Image::blit_rect(const Image& p_src, const Rect2& p_src_rect,const Point2& p_dest) { + + int dsize=data.size(); + int srcdsize=p_src.data.size(); + ERR_FAIL_COND( dsize==0 ); + ERR_FAIL_COND( srcdsize==0 ); + + + + Rect2 rrect = Rect2(0,0,p_src.width,p_src.height).clip(p_src_rect); + + DVector::Write wp = data.write(); + unsigned char *dst_data_ptr=wp.ptr(); + + DVector::Read rp = p_src.data.read(); + const unsigned char *src_data_ptr=rp.ptr(); + + if ((format==FORMAT_INDEXED || format == FORMAT_INDEXED_ALPHA) && (p_src.format==FORMAT_INDEXED || p_src.format == FORMAT_INDEXED_ALPHA)) { + + Point2i desti(p_dest.x, p_dest.y); + Point2i srci(rrect.pos.x, rrect.pos.y); + + for(int i=0;i= height) + continue; + for(int j=0;j=width) + continue; + + dst_data_ptr[width * (desti.y + i) + desti.x + j] = src_data_ptr[p_src.width * (srci.y+i) + srci.x+j]; + } + } + + } else { + + for(int i=0;i= height) + continue; + for(int j=0;j=width) + continue; + + _put_pixel(p_dest.x+j,p_dest.y+i,p_src._get_pixel(rrect.pos.x+j,rrect.pos.y+i,src_data_ptr,srcdsize),dst_data_ptr); + } + } + } + +} + + +Image (*Image::_png_mem_loader_func)(const uint8_t*)=NULL; +void (*Image::_image_compress_bc_func)(Image *)=NULL; +void (*Image::_image_compress_pvrtc2_func)(Image *)=NULL; +void (*Image::_image_compress_pvrtc4_func)(Image *)=NULL; +void (*Image::_image_compress_etc_func)(Image *)=NULL; +void (*Image::_image_decompress_pvrtc)(Image *)=NULL; +void (*Image::_image_decompress_bc)(Image *)=NULL; +void (*Image::_image_decompress_etc)(Image *)=NULL; + +DVector (*Image::lossy_packer)(const Image& ,float )=NULL; +Image (*Image::lossy_unpacker)(const DVector& )=NULL; +DVector (*Image::lossless_packer)(const Image& )=NULL; +Image (*Image::lossless_unpacker)(const DVector& )=NULL; + +void Image::set_compress_bc_func(void (*p_compress_func)(Image *)) { + + _image_compress_bc_func=p_compress_func; +} + + +void Image::fix_alpha_edges() { + + if (data.size()==0) + return; + + if (format!=FORMAT_RGBA) + return; //not needed + + DVector dcopy = data; + DVector::Read rp = data.read(); + const uint8_t *rptr=rp.ptr(); + + DVector::Write wp = data.write(); + unsigned char *data_ptr=wp.ptr(); + + const int max_radius=4; + const int alpha_treshold=20; + const int max_dist=0x7FFFFFFF; + + for(int i=0;i=alpha_treshold) + continue; + + int closest_dist=max_dist; + BColor closest_color; + closest_color.a=bc.a; + int from_x = MAX(0,j-max_radius); + int to_x = MIN(width-1,j+max_radius); + int from_y = MAX(0,i-max_radius); + int to_y = MIN(height-1,i+max_radius); + + for(int k=from_y;k<=to_y;k++) { + for(int l=from_x;l<=to_x;l++) { + + int dy = i-k; + int dx = j-l; + int dist = dy*dy+dx*dx; + if (dist>=closest_dist) + continue; + + const uint8_t * rp = &rptr[(k*width+l)<<2]; + + if (rp[3] + * + * Image storage class. This is used to store an image in user memory, as well as + * providing some basic methods for image manipulation. + * Images can be loaded from a file, or registered into the Render object as textures. +*/ + + + +class Image { + + enum { + MAX_WIDTH=4096, // force a limit somehow + MAX_HEIGHT=4096 // force a limit somehow + }; +public: + + enum Format { + FORMAT_GRAYSCALE, ///< one byte per pixel, 0-255 + FORMAT_INTENSITY, ///< one byte per pixel, 0-255 + FORMAT_GRAYSCALE_ALPHA, ///< two bytes per pixel, 0-255. alpha 0-255 + FORMAT_RGB, ///< one byte R, one byte G, one byte B + FORMAT_RGBA, ///< one byte R, one byte G, one byte B, one byte A + FORMAT_INDEXED, ///< index byte 0-256, and after image end, 256*3 bytes of palette + FORMAT_INDEXED_ALPHA, ///< index byte 0-256, and after image end, 256*4 bytes of palette (alpha) + FORMAT_YUV_422, + FORMAT_YUV_444, + FORMAT_BC1, // DXT1 + FORMAT_BC2, // DXT3 + FORMAT_BC3, // DXT5 + FORMAT_BC4, // ATI1 + FORMAT_BC5, // ATI2 + FORMAT_PVRTC2, + FORMAT_PVRTC2_ALPHA, + FORMAT_PVRTC4, + FORMAT_PVRTC4_ALPHA, + FORMAT_ETC, // regular ETC, no transparency + /*FORMAT_ETC2_R, for the future.. + FORMAT_ETC2_RG, + FORMAT_ETC2_RGB, + FORMAT_ETC2_RGBA1, + FORMAT_ETC2_RGBA,*/ + FORMAT_CUSTOM, + + FORMAT_MAX + }; + + enum Interpolation { + + INTERPOLATE_NEAREST, + INTERPOLATE_BILINEAR, + /* INTERPOLATE GAUSS */ + }; + + static Image (*_png_mem_loader_func)(const uint8_t* p_png); + static void (*_image_compress_bc_func)(Image *); + static void (*_image_compress_pvrtc2_func)(Image *); + static void (*_image_compress_pvrtc4_func)(Image *); + static void (*_image_compress_etc_func)(Image *); + static void (*_image_decompress_pvrtc)(Image *); + static void (*_image_decompress_bc)(Image *); + static void (*_image_decompress_etc)(Image *); + + static DVector (*lossy_packer)(const Image& p_image,float p_quality); + static Image (*lossy_unpacker)(const DVector& p_buffer); + static DVector (*lossless_packer)(const Image& p_image); + static Image (*lossless_unpacker)(const DVector& p_buffer); +private: + + //internal byte based color + struct BColor { + union { + uint8_t col[4]; + struct { + uint8_t r,g,b,a; + }; + }; + + bool operator==(const BColor& p_color) const { for(int i=0;i<4;i++) {if (col[i]!=p_color.col[i]) return false; } return true; } + _FORCE_INLINE_ uint8_t gray() const { return (uint16_t(col[0])+uint16_t(col[1])+uint16_t(col[2]))/3; } + _FORCE_INLINE_ BColor() {} + BColor(uint8_t p_r,uint8_t p_g,uint8_t p_b,uint8_t p_a=255) { col[0]=p_r; col[1]=p_g; col[2]=p_b; col[3]=p_a; } + }; + + //median cut classes + + struct BColorPos { + + uint32_t index; + BColor color; + struct SortR { + + bool operator()(const BColorPos& ca, const BColorPos& cb) const { return ca.color.r < cb.color.r; } + }; + + struct SortG { + + bool operator()(const BColorPos& ca, const BColorPos& cb) const { return ca.color.g < cb.color.g; } + }; + + struct SortB { + + bool operator()(const BColorPos& ca, const BColorPos& cb) const { return ca.color.b < cb.color.b; } + }; + + struct SortA { + + bool operator()(const BColorPos& ca, const BColorPos& cb) const { return ca.color.a < cb.color.a; } + }; + }; + + struct SPTree { + + bool leaf; + uint8_t split_plane; + uint8_t split_value; + union { + int left; + int color; + }; + int right; + SPTree() { leaf=true; left=-1; right=-1;} + }; + + struct MCBlock { + + BColorPos min_color,max_color; + BColorPos *colors; + int sp_idx; + int color_count; + int get_longest_axis_index() const; + int get_longest_axis_length() const; + bool operator<(const MCBlock& p_block) const; + void shrink(); + MCBlock(); + MCBlock(BColorPos *p_colors,int p_color_count); + }; + + Format format; + DVector data; + int width,height,mipmaps; + + + + _FORCE_INLINE_ BColor _get_pixel(int p_x,int p_y,const unsigned char *p_data,int p_data_size) const; + _FORCE_INLINE_ BColor _get_pixelw(int p_x,int p_y,int p_width,const unsigned char *p_data,int p_data_size) const; + _FORCE_INLINE_ void _put_pixelw(int p_x,int p_y, int p_width, const BColor& p_color, unsigned char *p_data); + _FORCE_INLINE_ void _put_pixel(int p_x,int p_y, const BColor& p_color, unsigned char *p_data); + _FORCE_INLINE_ void _get_mipmap_offset_and_size(int p_mipmap,int &r_offset, int &r_width, int &r_height) const; //get where the mipmap begins in data + _FORCE_INLINE_ static void _get_format_min_data_size(Format p_format,int &r_w, int &r_h); + + static int _get_dst_image_size(int p_width, int p_height, Format p_format,int &r_mipmaps,int p_mipmaps=-1); + bool _can_modify(Format p_format) const; + + + +public: + + + + int get_width() const; ///< Get image width + int get_height() const; ///< Get image height + int get_mipmaps() const; + + /** + * Get a pixel from the image. for grayscale or indexed formats, use Color::gray to obtain the actual + * value. + */ + Color get_pixel(int p_x,int p_y,int p_mipmap=0) const; + /** + * Set a pixel into the image. for grayscale or indexed formats, a suitable Color constructor. + */ + void put_pixel(int p_x,int p_y, const Color& p_color,int p_mipmap=0); /* alpha and index are averaged */ + + /** + * Convert the image to another format, as close as it can be done. + */ + void convert( Format p_new_format ); + + /** + * Get the current image format. + */ + Format get_format() const; + + int get_mipmap_offset(int p_mipmap) const; //get where the mipmap begins in data + void get_mipmap_offset_and_size(int p_mipmap,int &r_ofs, int &r_size) const; //get where the mipmap begins in data + + /** + * Resize the image, using the prefered interpolation method. + * Indexed-Color images always use INTERPOLATE_NEAREST. + */ + + void resize_to_po2(bool p_square=false); + void resize( int p_width, int p_height, Interpolation p_interpolation=INTERPOLATE_BILINEAR ); + Image resized( int p_width, int p_height, int p_interpolation=INTERPOLATE_BILINEAR ); + /** + * Crop the image to a specific size, if larger, then the image is filled by black + */ + void crop( int p_width, int p_height ); + + + void flip_x(); + void flip_y(); + /** + * Generate a mipmap to an image (creates an image 1/4 the size, with averaging of 4->1) + */ + Error generate_mipmaps(int p_amount=-1,bool p_keep_existing=false); + + void clear_mipmaps(); + + + /** + * Generate a normal map from a grayscale image + */ + + void make_normalmap(float p_height_scale=1.0); + + /** + * Create a new image of a given size and format. Current image will be lost + */ + void create(int p_width, int p_height, bool p_use_mipmaps, Format p_format); + void create(int p_width, int p_height, int p_mipmaps, Format p_format, const DVector& p_data); + + void create( const char ** p_xpm ); + /** + * returns true when the image is empty (0,0) in size + */ + bool empty() const; + + DVector get_data() const; + + Error load(const String& p_path); + + /** + * create an empty image + */ + Image(); + /** + * create an empty image of a specific size and format + */ + Image(int p_width, int p_height, bool p_use_mipmaps, Format p_format); + /** + * import an image of a specific size and format from a pointer + */ + Image(int p_width, int p_height, int p_mipmaps, Format p_format, const DVector& p_data); + + enum AlphaMode { + ALPHA_NONE, + ALPHA_BIT, + ALPHA_BLEND + }; + + AlphaMode detect_alpha() const; + + void put_indexed_pixel(int p_x, int p_y, uint8_t p_idx,int p_mipmap=0); + uint8_t get_indexed_pixel(int p_x, int p_y,int p_mipmap=0) const; + void set_pallete(const DVector& p_data); + + + static int get_format_pixel_size(Format p_format); + static int get_format_pixel_rshift(Format p_format); + static int get_format_pallete_size(Format p_format); + static int get_image_data_size(int p_width, int p_height, Format p_format,int p_mipmaps=0); + static int get_image_required_mipmaps(int p_width, int p_height, Format p_format); + + + + + bool operator==(const Image& p_image) const; + + void quantize(); + + enum CompressMode { + COMPRESS_BC, + COMPRESS_PVRTC2, + COMPRESS_PVRTC4, + COMPRESS_ETC + }; + + Error compress(CompressMode p_mode=COMPRESS_BC); + Image compressed(int p_mode); /* from the Image::CompressMode enum */ + void decompress(); + + void fix_alpha_edges(); + + void blit_rect(const Image& p_src, const Rect2& p_src_rect,const Point2& p_dest); + void brush_transfer(const Image& p_src, const Image& p_brush, const Point2& p_dest); + Image brushed(const Image& p_src, const Image& p_brush, const Point2& p_dest) const; + + Rect2 get_used_rect() const; + Image get_rect(const Rect2& p_area) const; + + static void set_compress_bc_func(void (*p_compress_func)(Image *)); + Image(const uint8_t* p_mem_png); + Image(const char **p_xpm); + ~Image(); + +}; + + +#endif diff --git a/core/image_quantize.cpp b/core/image_quantize.cpp new file mode 100644 index 00000000000..d728a328614 --- /dev/null +++ b/core/image_quantize.cpp @@ -0,0 +1,370 @@ +/*************************************************************************/ +/* image_quantize.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 "image.h" +#include +#include "print_string.h" +#ifdef TOOLS_ENABLED +#include "set.h" +#include "sort.h" +#include "os/os.h" + +//#define QUANTIZE_SPEED_OVER_QUALITY + + +Image::MCBlock::MCBlock() { + + +} + +Image::MCBlock::MCBlock(BColorPos *p_colors,int p_color_count) { + + colors=p_colors; + color_count=p_color_count; + min_color.color=BColor(255,255,255,255); + max_color.color=BColor(0,0,0,0); + shrink(); +} + +int Image::MCBlock::get_longest_axis_index() const { + + int max_dist=-1; + int max_index=0; + + for(int i=0;i<4;i++) { + + int d = max_color.color.col[i]-min_color.color.col[i]; + //printf(" ai:%i - %i\n",i,d); + if (d>max_dist) { + max_index=i; + max_dist=d; + } + } + + return max_index; +} +int Image::MCBlock::get_longest_axis_length() const { + + int max_dist=-1; + int max_index=0; + + for(int i=0;i<4;i++) { + + int d = max_color.color.col[i]-min_color.color.col[i]; + if (d>max_dist) { + max_index=i; + max_dist=d; + } + } + + return max_dist; +} + +bool Image::MCBlock::operator<(const MCBlock& p_block) const { + + int alen = get_longest_axis_length(); + int blen = p_block.get_longest_axis_length(); + if (alen==blen) { + + return colors < p_block.colors; + } else + return alen < blen; + +} + +void Image::MCBlock::shrink() { + + min_color=colors[0]; + max_color=colors[0]; + + for(int i=1;ihas_environment("QUANTIZE_FAST"); + + convert(FORMAT_RGBA); + + ERR_FAIL_COND( format!=FORMAT_RGBA ); + + DVector indexed_data; + + + { + int color_count = data.size()/4; + + ERR_FAIL_COND(color_count==0); + + Set block_queue; + + DVector data_colors; + data_colors.resize(color_count); + + DVector::Write dcw=data_colors.write(); + + DVector::Read dr = data.read(); + const BColor * drptr=(const BColor*)&dr[0]; + BColorPos *bcptr=&dcw[0]; + + + + { + for(int i=0;iget().color_count > 1 ) { + + MCBlock longest = block_queue.back()->get(); + //printf("longest: %i (%i)\n",longest.get_longest_axis_index(),longest.get_longest_axis_length()); + + block_queue.erase(block_queue.back()); + + BColorPos *first = longest.colors; + BColorPos *median = longest.colors + (longest.color_count+1)/2; + BColorPos *end = longest.colors + longest.color_count; + +#if 0 + int lai =longest.get_longest_axis_index(); + switch(lai) { +#if 0 + case 0: { SortArray sort; sort.sort(first,end-first); } break; + case 1: { SortArray sort; sort.sort(first,end-first); } break; + case 2: { SortArray sort; sort.sort(first,end-first); } break; + case 3: { SortArray sort; sort.sort(first,end-first); } break; +#else + case 0: { SortArray sort; sort.nth_element(0,end-first,median-first,first); } break; + case 1: { SortArray sort; sort.nth_element(0,end-first,median-first,first); } break; + case 2: { SortArray sort; sort.nth_element(0,end-first,median-first,first); } break; + case 3: { SortArray sort; sort.nth_element(0,end-first,median-first,first); } break; +#endif + + } + + //avoid same color from being split in 2 + //search forward and flip + BColorPos *median_end=median; + BColorPos *p=median_end+1; + + while(p!=end) { + if (median_end->color==p->color) { + SWAP(*(median_end+1),*p); + median_end++; + } + p++; + } + + //search backward and flip + BColorPos *median_begin=median; + p=median_begin-1; + + while(p!=(first-1)) { + if (median_begin->color==p->color) { + SWAP(*(median_begin-1),*p); + median_begin--; + } + p--; + } + + + if (first < median_begin) { + median=median_begin; + } else if (median_end < end-1) { + median=median_end+1; + } else { + break; //shouldn't have arrived here, since it means all pixels are equal, but wathever + } + + MCBlock left(first,median-first); + MCBlock right(median,end-median); + + block_queue.insert(left); + block_queue.insert(right); + +#else + switch(longest.get_longest_axis_index()) { + case 0: { SortArray sort; sort.nth_element(0,end-first,median-first,first); } break; + case 1: { SortArray sort; sort.nth_element(0,end-first,median-first,first); } break; + case 2: { SortArray sort; sort.nth_element(0,end-first,median-first,first); } break; + case 3: { SortArray sort; sort.nth_element(0,end-first,median-first,first); } break; + + } + + MCBlock left(first,median-first); + MCBlock right(median,end-median); + + block_queue.insert(left); + block_queue.insert(right); + + +#endif + + + } + + while(block_queue.size() > 256) { + + block_queue.erase(block_queue.front());// erase least significant + } + + int res_colors=0; + + int comp_size = (has_alpha?4:3); + indexed_data.resize(color_count + 256*comp_size); + + DVector::Write iw = indexed_data.write(); + uint8_t *iwptr=&iw[0]; + BColor pallete[256]; + + // print_line("applying quantization - res colors "+itos(block_queue.size())); + + while(block_queue.size()) { + + const MCBlock &b = block_queue.back()->get(); + + uint64_t sum[4]={0,0,0,0}; + + for(int i=0;i::Write(); + //dr = DVector::Read(); + //wb = DVector::Write(); + } + + print_line(itos(indexed_data.size())); + data=indexed_data; + format=has_alpha?FORMAT_INDEXED_ALPHA:FORMAT_INDEXED; + + +} //do none + + + +#else + + +void Image::quantize() {} //do none + + +#endif diff --git a/core/input_map.cpp b/core/input_map.cpp new file mode 100644 index 00000000000..296b39350a1 --- /dev/null +++ b/core/input_map.cpp @@ -0,0 +1,209 @@ +/*************************************************************************/ +/* input_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 "input_map.h" +#include "globals.h" + +InputMap *InputMap::singleton=NULL; + +void InputMap::add_action(const StringName& p_action) { + + ERR_FAIL_COND( input_map.has(p_action) ); + input_map[p_action]=Action(); + static int last_id=1; + input_map[p_action].id=last_id; + input_id_map[last_id]=p_action; + last_id++; + + +} + +void InputMap::erase_action(const StringName& p_action) { + + ERR_FAIL_COND( !input_map.has(p_action) ); + input_id_map.erase(input_map[p_action].id); + input_map.erase(p_action); + +} + +StringName InputMap::get_action_from_id(int p_id) const { + + ERR_FAIL_COND_V(!input_id_map.has(p_id),StringName()); + return input_id_map[p_id]; +} + +List::Element *InputMap::_find_event(List &p_list,const InputEvent& p_event) const { + + for (List::Element *E=p_list.front();E;E=E->next()) { + + const InputEvent& e=E->get(); + if(e.type!=p_event.type) + continue; + if (e.type!=InputEvent::KEY && e.device!=p_event.device) + continue; + + bool same=false; + + switch(p_event.type) { + + case InputEvent::KEY: { + + same=(e.key.scancode==p_event.key.scancode && e.key.mod == p_event.key.mod); + + } break; + case InputEvent::JOYSTICK_BUTTON: { + + same=(e.joy_button.button_index==p_event.joy_button.button_index); + + } break; + case InputEvent::MOUSE_BUTTON: { + + same=(e.mouse_button.button_index==p_event.mouse_button.button_index); + + } break; + case InputEvent::JOYSTICK_MOTION: { + + same=(e.joy_motion.axis==p_event.joy_motion.axis); + + } break; + } + + if (same) + return E; + } + + + return NULL; +} + + +bool InputMap::has_action(const StringName& p_action) const { + + return input_map.has(p_action); +} + +void InputMap::action_add_event(const StringName& p_action,const InputEvent& p_event) { + + ERR_FAIL_COND(p_event.type==InputEvent::ACTION); + ERR_FAIL_COND( !input_map.has(p_action) ); + if (_find_event(input_map[p_action].inputs,p_event)) + return; //already gots + + input_map[p_action].inputs.push_back(p_event); + +} + + +int InputMap::get_action_id(const StringName& p_action) const { + + ERR_FAIL_COND_V(!input_map.has(p_action),-1); + return input_map[p_action].id; +} + +bool InputMap::action_has_event(const StringName& p_action,const InputEvent& p_event) { + + ERR_FAIL_COND_V( !input_map.has(p_action), false ); + return (_find_event(input_map[p_action].inputs,p_event)!=NULL); + +} + +void InputMap::action_erase_event(const StringName& p_action,const InputEvent& p_event) { + + ERR_FAIL_COND( !input_map.has(p_action) ); + + List::Element *E=_find_event(input_map[p_action].inputs,p_event); + if (E) + return; //already gots + + input_map[p_action].inputs.erase(E); + +} + +const List *InputMap::get_action_list(const StringName& p_action) { + + const Map::Element *E=input_map.find(p_action); + if (!E) + return NULL; + + return &E->get().inputs; +} + +bool InputMap::event_is_action(const InputEvent& p_event, const StringName& p_action) const { + + + Map::Element *E=input_map.find(p_action); + if(!E) { + ERR_EXPLAIN("Request for unexisting InputMap action: "+String(p_action)); + ERR_FAIL_COND_V(!E,false); + } + + if (p_event.type==InputEvent::ACTION) { + + return p_event.action.action==E->get().id; + } + + return _find_event(E->get().inputs,p_event)!=NULL; +} + +void InputMap::load_from_globals() { + + input_map.clear();; + + List pinfo; + Globals::get_singleton()->get_property_list(&pinfo); + + for(List::Element *E=pinfo.front();E;E=E->next()) { + const PropertyInfo &pi=E->get(); + + if (!pi.name.begins_with("input/")) + continue; + + String name = pi.name.substr(pi.name.find("/")+1,pi.name.length()); + + add_action(name); + + Array va = Globals::get_singleton()->get(pi.name);; + + for(int i=0;i inputs; + }; + mutable Map input_map; + mutable Map input_id_map; + + List::Element *_find_event(List &p_list,const InputEvent& p_event) const; + +public: + + static _FORCE_INLINE_ InputMap *get_singleton() { return singleton; } + + + bool has_action(const StringName& p_action) const; + int get_action_id(const StringName& p_action) const; + StringName get_action_from_id(int p_id) const; + void add_action(const StringName& p_action); + void erase_action(const StringName& p_action); + + void action_add_event(const StringName& p_action,const InputEvent& p_event); + bool action_has_event(const StringName& p_action,const InputEvent& p_event); + void action_erase_event(const StringName& p_action,const InputEvent& p_event); + + const List *get_action_list(const StringName& p_action); + bool event_is_action(const InputEvent& p_event, const StringName& p_action) const; + + + void load_from_globals(); + + InputMap(); +}; + +#endif // INPUT_MAP_H diff --git a/core/int_types.h b/core/int_types.h new file mode 100644 index 00000000000..15ef68e915c --- /dev/null +++ b/core/int_types.h @@ -0,0 +1,56 @@ +/*************************************************************************/ +/* int_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. */ +/*************************************************************************/ + +#ifdef _MSC_VER + +typedef signed __int8 int8_t; +typedef unsigned __int8 uint8_t; +typedef signed __int16 int16_t; +typedef unsigned __int16 uint16_t; +typedef signed __int32 int32_t; +typedef unsigned __int32 uint32_t; +typedef signed __int64 int64_t; +typedef unsigned __int64 uint64_t; + +#else + +#ifdef NO_STDINT_H +typedef unsigned char uint8_t; +typedef signed char int8_t; +typedef unsigned short uint16_t; +typedef signed short int16_t; +typedef unsigned int uint32_t; +typedef signed int int32_t; +typedef long long int64_t; +typedef unsigned long long int64_t; +#else +#include +#endif + +#endif diff --git a/core/io/SCsub b/core/io/SCsub new file mode 100644 index 00000000000..5aecb4b915b --- /dev/null +++ b/core/io/SCsub @@ -0,0 +1,9 @@ +Import('env') + +env.add_source_files(env.core_sources,"*.cpp") +env.add_source_files(env.core_sources,"*.c") +#env.core_sources.append("io/fastlz.c") + +Export('env') + + diff --git a/core/io/base64.c b/core/io/base64.c new file mode 100644 index 00000000000..0c799e9f07a --- /dev/null +++ b/core/io/base64.c @@ -0,0 +1,110 @@ +#include + +char b64string[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +long base64_encode (to, from, len) + char *to, *from; + unsigned int len; +{ + char *fromp = from; + char *top = to; + unsigned char cbyte; + unsigned char obyte; + char end[3]; + + for (; len >= 3; len -= 3) { + cbyte = *fromp++; + *top++ = b64string[(int)(cbyte >> 2)]; + obyte = (cbyte << 4) & 0x30; /* 0011 0000 */ + + cbyte = *fromp++; + obyte |= (cbyte >> 4); /* 0000 1111 */ + *top++ = b64string[(int)obyte]; + obyte = (cbyte << 2) & 0x3C; /* 0011 1100 */ + + cbyte = *fromp++; + obyte |= (cbyte >> 6); /* 0000 0011 */ + *top++ = b64string[(int)obyte]; + *top++ = b64string[(int)(cbyte & 0x3F)];/* 0011 1111 */ + } + + if (len) { + end[0] = *fromp++; + if (--len) end[1] = *fromp++; else end[1] = 0; + end[2] = 0; + + cbyte = end[0]; + *top++ = b64string[(int)(cbyte >> 2)]; + obyte = (cbyte << 4) & 0x30; /* 0011 0000 */ + + cbyte = end[1]; + obyte |= (cbyte >> 4); + *top++ = b64string[(int)obyte]; + obyte = (cbyte << 2) & 0x3C; /* 0011 1100 */ + + if (len) *top++ = b64string[(int)obyte]; + else *top++ = '='; + *top++ = '='; + } + *top = 0; + return top - to; +} + +/* badchar(): check if c is decent; puts either the */ +/* location of c or null into p. */ +#define badchar(c,p) (!(p = memchr(b64string, c, 64))) + +long base64_decode (to, from, len) + char *to, *from; + unsigned int len; +{ + char *fromp = from; + char *top = to; + char *p; + unsigned char cbyte; + unsigned char obyte; + int padding = 0; + + for (; len >= 4; len -= 4) { + if ((cbyte = *fromp++) == '=') cbyte = 0; + else { + if (badchar(cbyte, p)) return -1; + cbyte = (p - b64string); + } + obyte = cbyte << 2; /* 1111 1100 */ + + if ((cbyte = *fromp++) == '=') cbyte = 0; + else { + if (badchar(cbyte, p)) return -1; + cbyte = p - b64string; + } + obyte |= cbyte >> 4; /* 0000 0011 */ + *top++ = obyte; + + obyte = cbyte << 4; /* 1111 0000 */ + if ((cbyte = *fromp++) == '=') { cbyte = 0; padding++; } + else { + padding = 0; + if (badchar (cbyte, p)) return -1; + cbyte = p - b64string; + } + obyte |= cbyte >> 2; /* 0000 1111 */ + *top++ = obyte; + + obyte = cbyte << 6; /* 1100 0000 */ + if ((cbyte = *fromp++) == '=') { cbyte = 0; padding++; } + else { + padding = 0; + if (badchar (cbyte, p)) return -1; + cbyte = p - b64string; + } + obyte |= cbyte; /* 0011 1111 */ + *top++ = obyte; + } + + *top = 0; + if (len) return -1; + return (top - to) - padding; +} + diff --git a/core/io/base64.h b/core/io/base64.h new file mode 100644 index 00000000000..b70b3879839 --- /dev/null +++ b/core/io/base64.h @@ -0,0 +1,11 @@ +#ifndef BASE64_H +#define BASE64_H + +extern "C" { + +uint32_t base64_encode (char* to, char* from, uint32_t len); +uint32_t base64_decode (char* to, char* from, uint32_t len); + +}; + +#endif /* BASE64_H */ diff --git a/core/io/compression.cpp b/core/io/compression.cpp new file mode 100644 index 00000000000..156767d2417 --- /dev/null +++ b/core/io/compression.cpp @@ -0,0 +1,91 @@ +/*************************************************************************/ +/* compression.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 "compression.h" + +#include "fastlz.h" +#include "os/copymem.h" + +int Compression::compress(uint8_t *p_dst, const uint8_t *p_src, int p_src_size,Mode p_mode) { + + switch(p_mode) { + case MODE_FASTLZ: { + + if (p_src_size<16) { + uint8_t src[16]; + zeromem(&src[p_src_size],16-p_src_size); + copymem(src,p_src,p_src_size); + return fastlz_compress(src,16,p_dst); + } else { + return fastlz_compress(p_src,p_src_size,p_dst); + } + + } break; + } + + ERR_FAIL_V(-1); +} + +int Compression::get_max_compressed_buffer_size(int p_src_size,Mode p_mode){ + + switch(p_mode) { + case MODE_FASTLZ: { + + + int ss = p_src_size+p_src_size*6/100; + if (ss<66) + ss=66; + return ss; + + } break; + } + + ERR_FAIL_V(-1); + +} + + + +void Compression::decompress(uint8_t *p_dst, int p_dst_max_size, const uint8_t *p_src, int p_src_size,Mode p_mode){ + + switch(p_mode) { + case MODE_FASTLZ: { + + if (p_dst_max_size<16) { + uint8_t dst[16]; + fastlz_decompress(p_src,p_src_size,dst,16); + copymem(p_dst,dst,p_dst_max_size); + } else { + fastlz_decompress(p_src,p_src_size,p_dst,p_dst_max_size); + } + return; + } break; + } + + ERR_FAIL(); +} diff --git a/core/io/compression.h b/core/io/compression.h new file mode 100644 index 00000000000..70742d42d6b --- /dev/null +++ b/core/io/compression.h @@ -0,0 +1,53 @@ +/*************************************************************************/ +/* compression.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 COMPRESSION_H +#define COMPRESSION_H + +#include "typedefs.h" + +class Compression +{ +public: + + enum Mode { + MODE_FASTLZ, + MODE_DEFLATE + }; + + + static int compress(uint8_t *p_dst, const uint8_t *p_src, int p_src_size,Mode p_mode=MODE_FASTLZ); + static int get_max_compressed_buffer_size(int p_src_size,Mode p_mode=MODE_FASTLZ); + static void decompress(uint8_t *p_dst, int p_dst_max_size, const uint8_t *p_src, int p_src_size,Mode p_mode=MODE_FASTLZ); + + Compression(); +}; + + + +#endif // COMPRESSION_H diff --git a/core/io/config_file.cpp b/core/io/config_file.cpp new file mode 100644 index 00000000000..45e8cf69ab2 --- /dev/null +++ b/core/io/config_file.cpp @@ -0,0 +1,744 @@ +/*************************************************************************/ +/* config_file.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 "config_file.h" +#include "os/keyboard.h" +#include "os/file_access.h" + +StringArray ConfigFile::_get_sections() const { + + List s; + get_sections(&s); + StringArray arr; + arr.resize(s.size()); + int idx=0; + for(const List::Element *E=s.front();E;E=E->next()) { + + arr.set(idx++,E->get()); + } + + return arr; +} + +StringArray ConfigFile::_get_section_keys(const String& p_section) const{ + + List s; + get_section_keys(p_section,&s); + StringArray arr; + arr.resize(s.size()); + int idx=0; + for(const List::Element *E=s.front();E;E=E->next()) { + + arr.set(idx++,E->get()); + } + + return arr; + +} + + +void ConfigFile::set_value(const String& p_section, const String& p_key, const Variant& p_value){ + + if (p_value.get_type()==Variant::NIL) { + //erase + if (!values.has(p_section)) + return; // ? + values[p_section].erase(p_key); + if (values[p_section].empty()) { + values.erase(p_section); + } + + } else { + if (!values.has(p_section)) { + values[p_section]=Map(); + } + + values[p_section][p_key]=p_value; + + } + +} +Variant ConfigFile::get_value(const String& p_section, const String& p_key) const{ + + ERR_FAIL_COND_V(!values.has(p_section),Variant()); + ERR_FAIL_COND_V(!values[p_section].has(p_key),Variant()); + return values[p_section][p_key]; + +} + +bool ConfigFile::has_section(const String& p_section) const { + + return values.has(p_section); +} +bool ConfigFile::has_section_key(const String& p_section,const String& p_key) const { + + if (!values.has(p_section)) + return false; + return values[p_section].has(p_key); +} + +void ConfigFile::get_sections(List *r_sections) const{ + + for(const Map< String, Map >::Element *E=values.front();E;E=E->next()) { + r_sections->push_back(E->key()); + } +} +void ConfigFile::get_section_keys(const String& p_section,List *r_keys) const{ + + ERR_FAIL_COND(!values.has(p_section)); + + for(const Map ::Element *E=values[p_section].front();E;E=E->next()) { + r_keys->push_back(E->key()); + } + +} + +static String _encode_variant(const Variant& p_variant) { + + switch(p_variant.get_type()) { + + case Variant::BOOL: { + bool val = p_variant; + return (val?"true":"false"); + } break; + case Variant::INT: { + int val = p_variant; + return itos(val); + } break; + case Variant::REAL: { + float val = p_variant; + return rtos(val)+(val==int(val)?".0":""); + } break; + case Variant::STRING: { + String val = p_variant; + return "\""+val.xml_escape()+"\""; + } break; + case Variant::COLOR: { + + Color val = p_variant; + return "#"+val.to_html(); + } break; + case Variant::STRING_ARRAY: + case Variant::INT_ARRAY: + case Variant::REAL_ARRAY: + case Variant::ARRAY: { + Array arr = p_variant; + String str="["; + for(int i=0;i0) + str+=", "; + str+=_encode_variant(arr[i]); + } + str+="]"; + return str; + } break; + case Variant::DICTIONARY: { + Dictionary d = p_variant; + String str="{"; + List keys; + d.get_key_list(&keys); + for(List::Element *E=keys.front();E;E=E->next()) { + + if (E!=keys.front()) + str+=", "; + str+=_encode_variant(E->get()); + str+=":"; + str+=_encode_variant(d[E->get()]); + + } + str+="}"; + return str; + } break; + case Variant::IMAGE: { + String str="img("; + + Image img=p_variant; + if (!img.empty()) { + + String format; + switch(img.get_format()) { + + case Image::FORMAT_GRAYSCALE: format="grayscale"; break; + case Image::FORMAT_INTENSITY: format="intensity"; break; + case Image::FORMAT_GRAYSCALE_ALPHA: format="grayscale_alpha"; break; + case Image::FORMAT_RGB: format="rgb"; break; + case Image::FORMAT_RGBA: format="rgba"; break; + case Image::FORMAT_INDEXED : format="indexed"; break; + case Image::FORMAT_INDEXED_ALPHA: format="indexed_alpha"; break; + case Image::FORMAT_BC1: format="bc1"; break; + case Image::FORMAT_BC2: format="bc2"; break; + case Image::FORMAT_BC3: format="bc3"; break; + case Image::FORMAT_BC4: format="bc4"; break; + case Image::FORMAT_BC5: format="bc5"; break; + case Image::FORMAT_CUSTOM: format="custom custom_size="+itos(img.get_data().size())+""; break; + default: {} + } + + str+=format+", "; + str+=itos(img.get_mipmaps())+", "; + str+=itos(img.get_width())+", "; + str+=itos(img.get_height())+", "; + DVector data = img.get_data(); + int ds=data.size(); + DVector::Read r = data.read(); + for(int i=0;i>4], hex[byte&0xF], 0}; + str+=bstr; + } + } + str+=")"; + return str; + } break; + case Variant::INPUT_EVENT: { + + InputEvent ev = p_variant; + + switch(ev.type) { + + case InputEvent::KEY: { + + String mods; + if (ev.key.mod.control) + mods+="C"; + if (ev.key.mod.shift) + mods+="S"; + if (ev.key.mod.alt) + mods+="A"; + if (ev.key.mod.meta) + mods+="M"; + if (mods!="") + mods=", "+mods; + + return "key("+keycode_get_string(ev.key.scancode)+mods+")"; + } break; + case InputEvent::MOUSE_BUTTON: { + + return "mbutton("+itos(ev.device)+", "+itos(ev.mouse_button.button_index)+")"; + } break; + case InputEvent::JOYSTICK_BUTTON: { + + return "jbutton("+itos(ev.device)+", "+itos(ev.joy_button.button_index)+")"; + } break; + case InputEvent::JOYSTICK_MOTION: { + + return "jaxis("+itos(ev.device)+", "+itos(ev.joy_motion.axis)+")"; + } break; + default: { + + return "nil"; + } break; + + } + } break; + default: {} + } + + return "nil"; //don't know wha to do with this +} + + +Error ConfigFile::save(const String& p_path){ + + Error err; + FileAccess *file = FileAccess::open(p_path,FileAccess::WRITE,&err); + + if (err) { + return err; + } + + + for(Map< String, Map >::Element *E=values.front();E;E=E->next()) { + + if (E!=values.front()) + file->store_string("\n"); + file->store_string("["+E->key()+"]\n\n"); + + for(Map::Element *F=E->get().front();F;F=F->next()) { + + file->store_string(F->key()+"="+_encode_variant(F->get())+"\n"); + } + } + + memdelete(file); + + return OK; +} + +static Vector _decode_params(const String& p_string) { + + int begin=p_string.find("("); + ERR_FAIL_COND_V(begin==-1,Vector()); + begin++; + int end=p_string.find(")"); + ERR_FAIL_COND_V(end()); + return p_string.substr(begin,end-begin).split(","); +} + +static String _get_chunk(const String& str,int &pos, int close_pos) { + + + enum { + MIN_COMMA, + MIN_COLON, + MIN_CLOSE, + MIN_QUOTE, + MIN_PARENTHESIS, + MIN_CURLY_OPEN, + MIN_OPEN + }; + + int min_pos=close_pos; + int min_what=MIN_CLOSE; + +#define TEST_MIN(m_how,m_what) \ +{\ +int res = str.find(m_how,pos);\ +if (res!=-1 && res < min_pos) {\ + min_pos=res;\ + min_what=m_what;\ +}\ +}\ + + + TEST_MIN(",",MIN_COMMA); + TEST_MIN("[",MIN_OPEN); + TEST_MIN("{",MIN_CURLY_OPEN); + TEST_MIN("(",MIN_PARENTHESIS); + TEST_MIN("\"",MIN_QUOTE); + + int end=min_pos; + + + switch(min_what) { + + case MIN_COMMA: { + } break; + case MIN_CLOSE: { + //end because it's done + } break; + case MIN_QUOTE: { + end=str.find("\"",min_pos+1)+1; + ERR_FAIL_COND_V(end==-1,Variant()); + + } break; + case MIN_PARENTHESIS: { + + end=str.find(")",min_pos+1)+1; + ERR_FAIL_COND_V(end==-1,Variant()); + + } break; + case MIN_OPEN: { + int level=1; + while(end params = _decode_params(p_string); + ERR_FAIL_COND_V(params.size()!=1 && params.size()!=2,Variant()); + int scode=0; + + if (params[0].is_numeric()) + scode=params[0].to_int(); + else + scode=find_keycode(params[0]); + + InputEvent ie; + ie.type=InputEvent::KEY; + ie.key.scancode=scode; + + if (params.size()==2) { + String mods=params[1]; + if (mods.findn("C")!=-1) + ie.key.mod.control=true; + if (mods.findn("A")!=-1) + ie.key.mod.alt=true; + if (mods.findn("S")!=-1) + ie.key.mod.shift=true; + if (mods.findn("M")!=-1) + ie.key.mod.meta=true; + } + return ie; + + } + + if (str.begins_with("mbutton")) { + Vector params = _decode_params(p_string); + ERR_FAIL_COND_V(params.size()!=2,Variant()); + + InputEvent ie; + ie.type=InputEvent::MOUSE_BUTTON; + ie.device=params[0].to_int(); + ie.mouse_button.button_index=params[1].to_int(); + + return ie; + } + + if (str.begins_with("jbutton")) { + Vector params = _decode_params(p_string); + ERR_FAIL_COND_V(params.size()!=2,Variant()); + + InputEvent ie; + ie.type=InputEvent::JOYSTICK_BUTTON; + ie.device=params[0].to_int(); + ie.joy_button.button_index=params[1].to_int(); + + return ie; + } + + if (str.begins_with("jaxis")) { + Vector params = _decode_params(p_string); + ERR_FAIL_COND_V(params.size()!=2,Variant()); + + InputEvent ie; + ie.type=InputEvent::JOYSTICK_MOTION; + ie.device=params[0].to_int(); + ie.joy_motion.axis=params[1].to_int(); + + return ie; + } + if (str.begins_with("img")) { + Vector params = _decode_params(p_string); + if (params.size()==0) { + return Image(); + } + + ERR_FAIL_COND_V(params.size()!=5,Image()); + + String format=params[0].strip_edges(); + + Image::Format imgformat; + + if (format=="grayscale") { + imgformat=Image::FORMAT_GRAYSCALE; + } else if (format=="intensity") { + imgformat=Image::FORMAT_INTENSITY; + } else if (format=="grayscale_alpha") { + imgformat=Image::FORMAT_GRAYSCALE_ALPHA; + } else if (format=="rgb") { + imgformat=Image::FORMAT_RGB; + } else if (format=="rgba") { + imgformat=Image::FORMAT_RGBA; + } else if (format=="indexed") { + imgformat=Image::FORMAT_INDEXED; + } else if (format=="indexed_alpha") { + imgformat=Image::FORMAT_INDEXED_ALPHA; + } else if (format=="bc1") { + imgformat=Image::FORMAT_BC1; + } else if (format=="bc2") { + imgformat=Image::FORMAT_BC2; + } else if (format=="bc3") { + imgformat=Image::FORMAT_BC3; + } else if (format=="bc4") { + imgformat=Image::FORMAT_BC4; + } else if (format=="bc5") { + imgformat=Image::FORMAT_BC5; + } else if (format=="custom") { + imgformat=Image::FORMAT_CUSTOM; + } else { + + ERR_FAIL_V( Image() ); + } + + int mipmaps=params[1].to_int(); + int w=params[2].to_int(); + int h=params[3].to_int(); + + if (w == 0 && w == 0) { + //r_v = Image(w, h, imgformat); + return Image(); + }; + + + String data=params[4]; + int datasize=data.length()/2; + DVector pixels; + pixels.resize(datasize); + DVector::Write wb = pixels.write(); + const CharType *cptr=data.c_str(); + + int idx=0; + uint8_t byte; + while( idx='0' && c<='9') || (c>='A' && c<='F') || (c>='a' && c<='f') ) { + + if (idx&1) { + + byte|=HEX2CHR(c); + wb[idx>>1]=byte; + } else { + + byte=HEX2CHR(c)<<4; + } + + idx++; + } + + } + + wb = DVector::Write(); + + return Image(w,h,mipmaps,imgformat,pixels); + } + + if (str.find(",")!=-1) { //vector2 or vector3 + Vector farr = str.split_floats(",",true); + if (farr.size()==2) { + return Point2(farr[0],farr[1]); + } + if (farr.size()==3) { + return Vector3(farr[0],farr[1],farr[2]); + } + ERR_FAIL_V(Variant()); + } + + + return Variant(); +} + +Error ConfigFile::load(const String& p_path) { + + Error err; + FileAccess *f= FileAccess::open(p_path,FileAccess::READ,&err); + + if (err!=OK) { + + return err; + } + + + String line; + String section; + String subpath; + + int line_count = 0; + + while(!f->eof_reached()) { + + String line = f->get_line().strip_edges(); + line_count++; + + if (line=="") + continue; + + // find comments + + { + + int pos=0; + while (true) { + int ret = line.find(";",pos); + if (ret==-1) + break; + + int qc=0; + for(int i=0;i 0) { + ERR_PRINT(String("Syntax error on line "+itos(line_count)+" of file "+p_path).ascii().get_data()); + }; + }; + } + + memdelete(f); + + return OK; +} + + + +void ConfigFile::_bind_methods(){ + + ObjectTypeDB::bind_method(_MD("set_value","section","key","value"),&ConfigFile::set_value); + ObjectTypeDB::bind_method(_MD("get_value","section","key"),&ConfigFile::get_value); + + ObjectTypeDB::bind_method(_MD("has_section","section"),&ConfigFile::has_section); + ObjectTypeDB::bind_method(_MD("has_section_key","section","key"),&ConfigFile::has_section_key); + + ObjectTypeDB::bind_method(_MD("get_sections"),&ConfigFile::_get_sections); + ObjectTypeDB::bind_method(_MD("get_section_keys"),&ConfigFile::_get_section_keys); + + ObjectTypeDB::bind_method(_MD("load:Error","path"),&ConfigFile::load); + ObjectTypeDB::bind_method(_MD("save:Error","path"),&ConfigFile::save); + +} + + +ConfigFile::ConfigFile() +{ +} diff --git a/core/io/config_file.h b/core/io/config_file.h new file mode 100644 index 00000000000..e132e46fea4 --- /dev/null +++ b/core/io/config_file.h @@ -0,0 +1,63 @@ +/*************************************************************************/ +/* config_file.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 CONFIG_FILE_H +#define CONFIG_FILE_H + +#include "reference.h" + + +class ConfigFile : public Reference { + + OBJ_TYPE(ConfigFile,Reference); + + Map< String, Map > values; + + StringArray _get_sections() const; + StringArray _get_section_keys(const String& p_section) const; +protected: + + static void _bind_methods(); +public: + + void set_value(const String& p_section, const String& p_key, const Variant& p_value); + Variant get_value(const String& p_section, const String& p_key) const; + + bool has_section(const String& p_section) const; + bool has_section_key(const String& p_section,const String& p_key) const; + + void get_sections(List *r_sections) const; + void get_section_keys(const String& p_section,List *r_keys) const; + + Error save(const String& p_path); + Error load(const String& p_path); + + ConfigFile(); +}; + +#endif // CONFIG_FILE_H diff --git a/core/io/crypt.h b/core/io/crypt.h new file mode 100644 index 00000000000..a01d08d9328 --- /dev/null +++ b/core/io/crypt.h @@ -0,0 +1,131 @@ +/* crypt.h -- base code for crypt/uncrypt ZIPfile + + + Version 1.01e, February 12th, 2005 + + Copyright (C) 1998-2005 Gilles Vollant + + This code is a modified version of crypting code in Infozip distribution + + The encryption/decryption parts of this source code (as opposed to the + non-echoing password parts) were originally written in Europe. The + whole source package can be freely distributed, including from the USA. + (Prior to January 2000, re-export from the US was a violation of US law.) + + This encryption code is a direct transcription of the algorithm from + Roger Schlafly, described by Phil Katz in the file appnote.txt. This + file (appnote.txt) is distributed with the PKZIP program (even in the + version without encryption capabilities). + + If you don't need crypting in your application, just define symbols + NOCRYPT and NOUNCRYPT. + + This code support the "Traditional PKWARE Encryption". + + The new AES encryption added on Zip format by Winzip (see the page + http://www.winzip.com/aes_info.htm ) and PKWare PKZip 5.x Strong + Encryption is not supported. +*/ + +#define CRC32(c, b) ((*(pcrc_32_tab+(((int)(c) ^ (b)) & 0xff))) ^ ((c) >> 8)) + +/*********************************************************************** + * Return the next byte in the pseudo-random sequence + */ +static int decrypt_byte(unsigned long* pkeys, const unsigned long* pcrc_32_tab) +{ + unsigned temp; /* POTENTIAL BUG: temp*(temp^1) may overflow in an + * unpredictable manner on 16-bit systems; not a problem + * with any known compiler so far, though */ + + temp = ((unsigned)(*(pkeys+2)) & 0xffff) | 2; + return (int)(((temp * (temp ^ 1)) >> 8) & 0xff); +} + +/*********************************************************************** + * Update the encryption keys with the next byte of plain text + */ +static int update_keys(unsigned long* pkeys,const unsigned long* pcrc_32_tab,int c) +{ + (*(pkeys+0)) = CRC32((*(pkeys+0)), c); + (*(pkeys+1)) += (*(pkeys+0)) & 0xff; + (*(pkeys+1)) = (*(pkeys+1)) * 134775813L + 1; + { + register int keyshift = (int)((*(pkeys+1)) >> 24); + (*(pkeys+2)) = CRC32((*(pkeys+2)), keyshift); + } + return c; +} + + +/*********************************************************************** + * Initialize the encryption keys and the random header according to + * the given password. + */ +static void init_keys(const char* passwd,unsigned long* pkeys,const unsigned long* pcrc_32_tab) +{ + *(pkeys+0) = 305419896L; + *(pkeys+1) = 591751049L; + *(pkeys+2) = 878082192L; + while (*passwd != '\0') { + update_keys(pkeys,pcrc_32_tab,(int)*passwd); + passwd++; + } +} + +#define zdecode(pkeys,pcrc_32_tab,c) \ + (update_keys(pkeys,pcrc_32_tab,c ^= decrypt_byte(pkeys,pcrc_32_tab))) + +#define zencode(pkeys,pcrc_32_tab,c,t) \ + (t=decrypt_byte(pkeys,pcrc_32_tab), update_keys(pkeys,pcrc_32_tab,c), t^(c)) + +#ifdef INCLUDECRYPTINGCODE_IFCRYPTALLOWED + +#define RAND_HEAD_LEN 12 + /* "last resort" source for second part of crypt seed pattern */ +# ifndef ZCR_SEED2 +# define ZCR_SEED2 3141592654UL /* use PI as default pattern */ +# endif + +static int crypthead(const char* passwd, /* password string */ + unsigned char* buf, /* where to write header */ + int bufSize, + unsigned long* pkeys, + const unsigned long* pcrc_32_tab, + unsigned long crcForCrypting) +{ + int n; /* index in random header */ + int t; /* temporary */ + int c; /* random byte */ + unsigned char header[RAND_HEAD_LEN-2]; /* random header */ + static unsigned calls = 0; /* ensure different random header each time */ + + if (bufSize> 7) & 0xff; + header[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, c, t); + } + /* Encrypt random header (last two bytes is high word of crc) */ + init_keys(passwd, pkeys, pcrc_32_tab); + for (n = 0; n < RAND_HEAD_LEN-2; n++) + { + buf[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, header[n], t); + } + buf[n++] = (unsigned char)zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 16) & 0xff, t); + buf[n++] = (unsigned char)zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 24) & 0xff, t); + return n; +} + +#endif diff --git a/core/io/fastlz.c b/core/io/fastlz.c new file mode 100644 index 00000000000..508f6ea2aea --- /dev/null +++ b/core/io/fastlz.c @@ -0,0 +1,551 @@ + /* + FastLZ - lightning-fast lossless compression library + + Copyright (C) 2007 Ariya Hidayat (ariya@kde.org) + Copyright (C) 2006 Ariya Hidayat (ariya@kde.org) + Copyright (C) 2005 Ariya Hidayat (ariya@kde.org) + + 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. +*/ + +#if !defined(FASTLZ_COMPRESSOR) && !defined(FASTLZ_DECOMPRESSOR) + +/* + * Always check for bound when decompressing. + * Generally it is best to leave it defined. + */ +#define FASTLZ_SAFE + +/* + * Give hints to the compiler for branch prediction optimization. + */ +#if defined(__GNUC__) && (__GNUC__ > 2) +#define FASTLZ_EXPECT_CONDITIONAL(c) (__builtin_expect((c), 1)) +#define FASTLZ_UNEXPECT_CONDITIONAL(c) (__builtin_expect((c), 0)) +#else +#define FASTLZ_EXPECT_CONDITIONAL(c) (c) +#define FASTLZ_UNEXPECT_CONDITIONAL(c) (c) +#endif + +/* + * Use inlined functions for supported systems. + */ +#if defined(__GNUC__) || defined(__DMC__) || defined(__POCC__) || defined(__WATCOMC__) || defined(__SUNPRO_C) +#define FASTLZ_INLINE inline +#elif defined(__BORLANDC__) || defined(_MSC_VER) || defined(__LCC__) +#define FASTLZ_INLINE __inline +#else +#define FASTLZ_INLINE +#endif + +/* + * Prevent accessing more than 8-bit at once, except on x86 architectures. + */ +#if !defined(FASTLZ_STRICT_ALIGN) +#define FASTLZ_STRICT_ALIGN +#if defined(__i386__) || defined(__386) /* GNU C, Sun Studio */ +#undef FASTLZ_STRICT_ALIGN +#elif defined(__i486__) || defined(__i586__) || defined(__i686__) /* GNU C */ +#undef FASTLZ_STRICT_ALIGN +#elif defined(_M_IX86) /* Intel, MSVC */ +#undef FASTLZ_STRICT_ALIGN +#elif defined(__386) +#undef FASTLZ_STRICT_ALIGN +#elif defined(_X86_) /* MinGW */ +#undef FASTLZ_STRICT_ALIGN +#elif defined(__I86__) /* Digital Mars */ +#undef FASTLZ_STRICT_ALIGN +#endif +#endif + +/* + * FIXME: use preprocessor magic to set this on different platforms! + */ +typedef unsigned char flzuint8; +typedef unsigned short flzuint16; +typedef unsigned int flzuint32; + +/* prototypes */ +int fastlz_compress(const void* input, int length, void* output); +int fastlz_compress_level(int level, const void* input, int length, void* output); +int fastlz_decompress(const void* input, int length, void* output, int maxout); + +#define MAX_COPY 32 +#define MAX_LEN 264 /* 256 + 8 */ +#define MAX_DISTANCE 8192 + +#if !defined(FASTLZ_STRICT_ALIGN) +#define FASTLZ_READU16(p) *((const flzuint16*)(p)) +#else +#define FASTLZ_READU16(p) ((p)[0] | (p)[1]<<8) +#endif + +#define HASH_LOG 13 +#define HASH_SIZE (1<< HASH_LOG) +#define HASH_MASK (HASH_SIZE-1) +#define HASH_FUNCTION(v,p) { v = FASTLZ_READU16(p); v ^= FASTLZ_READU16(p+1)^(v>>(16-HASH_LOG));v &= HASH_MASK; } + +#undef FASTLZ_LEVEL +#define FASTLZ_LEVEL 1 + +#undef FASTLZ_COMPRESSOR +#undef FASTLZ_DECOMPRESSOR +#define FASTLZ_COMPRESSOR fastlz1_compress +#define FASTLZ_DECOMPRESSOR fastlz1_decompress +static FASTLZ_INLINE int FASTLZ_COMPRESSOR(const void* input, int length, void* output); +static FASTLZ_INLINE int FASTLZ_DECOMPRESSOR(const void* input, int length, void* output, int maxout); +#include "fastlz.c" + +#undef FASTLZ_LEVEL +#define FASTLZ_LEVEL 2 + +#undef MAX_DISTANCE +#define MAX_DISTANCE 8191 +#define MAX_FARDISTANCE (65535+MAX_DISTANCE-1) + +#undef FASTLZ_COMPRESSOR +#undef FASTLZ_DECOMPRESSOR +#define FASTLZ_COMPRESSOR fastlz2_compress +#define FASTLZ_DECOMPRESSOR fastlz2_decompress +static FASTLZ_INLINE int FASTLZ_COMPRESSOR(const void* input, int length, void* output); +static FASTLZ_INLINE int FASTLZ_DECOMPRESSOR(const void* input, int length, void* output, int maxout); +#include "fastlz.c" + +int fastlz_compress(const void* input, int length, void* output) +{ + /* for short block, choose fastlz1 */ + if(length < 65536) + return fastlz1_compress(input, length, output); + + /* else... */ + return fastlz2_compress(input, length, output); +} + +int fastlz_decompress(const void* input, int length, void* output, int maxout) +{ + /* magic identifier for compression level */ + int level = ((*(const flzuint8*)input) >> 5) + 1; + + if(level == 1) + return fastlz1_decompress(input, length, output, maxout); + if(level == 2) + return fastlz2_decompress(input, length, output, maxout); + + /* unknown level, trigger error */ + return 0; +} + +int fastlz_compress_level(int level, const void* input, int length, void* output) +{ + if(level == 1) + return fastlz1_compress(input, length, output); + if(level == 2) + return fastlz2_compress(input, length, output); + + return 0; +} + +#else /* !defined(FASTLZ_COMPRESSOR) && !defined(FASTLZ_DECOMPRESSOR) */ + +static FASTLZ_INLINE int FASTLZ_COMPRESSOR(const void* input, int length, void* output) +{ + const flzuint8* ip = (const flzuint8*) input; + const flzuint8* ip_bound = ip + length - 2; + const flzuint8* ip_limit = ip + length - 12; + flzuint8* op = (flzuint8*) output; + + const flzuint8* htab[HASH_SIZE]; + const flzuint8** hslot; + flzuint32 hval; + + flzuint32 copy; + + /* sanity check */ + if(FASTLZ_UNEXPECT_CONDITIONAL(length < 4)) + { + if(length) + { + /* create literal copy only */ + *op++ = length-1; + ip_bound++; + while(ip <= ip_bound) + *op++ = *ip++; + return length+1; + } + else + return 0; + } + + /* initializes hash table */ + for (hslot = htab; hslot < htab + HASH_SIZE; hslot++) + *hslot = ip; + + /* we start with literal copy */ + copy = 2; + *op++ = MAX_COPY-1; + *op++ = *ip++; + *op++ = *ip++; + + /* main loop */ + while(FASTLZ_EXPECT_CONDITIONAL(ip < ip_limit)) + { + const flzuint8* ref; + flzuint32 distance; + + /* minimum match length */ + flzuint32 len = 3; + + /* comparison starting-point */ + const flzuint8* anchor = ip; + + /* check for a run */ +#if FASTLZ_LEVEL==2 + if(ip[0] == ip[-1] && FASTLZ_READU16(ip-1)==FASTLZ_READU16(ip+1)) + { + distance = 1; + ip += 3; + ref = anchor - 1 + 3; + goto match; + } +#endif + + /* find potential match */ + HASH_FUNCTION(hval,ip); + hslot = htab + hval; + ref = htab[hval]; + + /* calculate distance to the match */ + distance = anchor - ref; + + /* update hash table */ + *hslot = anchor; + + /* is this a match? check the first 3 bytes */ + if(distance==0 || +#if FASTLZ_LEVEL==1 + (distance >= MAX_DISTANCE) || +#else + (distance >= MAX_FARDISTANCE) || +#endif + *ref++ != *ip++ || *ref++!=*ip++ || *ref++!=*ip++) + goto literal; + +#if FASTLZ_LEVEL==2 + /* far, needs at least 5-byte match */ + if(distance >= MAX_DISTANCE) + { + if(*ip++ != *ref++ || *ip++!= *ref++) + goto literal; + len += 2; + } + + match: +#endif + + /* last matched byte */ + ip = anchor + len; + + /* distance is biased */ + distance--; + + if(!distance) + { + /* zero distance means a run */ + flzuint8 x = ip[-1]; + while(ip < ip_bound) + if(*ref++ != x) break; else ip++; + } + else + for(;;) + { + /* safe because the outer check against ip limit */ + if(*ref++ != *ip++) break; + if(*ref++ != *ip++) break; + if(*ref++ != *ip++) break; + if(*ref++ != *ip++) break; + if(*ref++ != *ip++) break; + if(*ref++ != *ip++) break; + if(*ref++ != *ip++) break; + if(*ref++ != *ip++) break; + while(ip < ip_bound) + if(*ref++ != *ip++) break; + break; + } + + /* if we have copied something, adjust the copy count */ + if(copy) + /* copy is biased, '0' means 1 byte copy */ + *(op-copy-1) = copy-1; + else + /* back, to overwrite the copy count */ + op--; + + /* reset literal counter */ + copy = 0; + + /* length is biased, '1' means a match of 3 bytes */ + ip -= 3; + len = ip - anchor; + + /* encode the match */ +#if FASTLZ_LEVEL==2 + if(distance < MAX_DISTANCE) + { + if(len < 7) + { + *op++ = (len << 5) + (distance >> 8); + *op++ = (distance & 255); + } + else + { + *op++ = (7 << 5) + (distance >> 8); + for(len-=7; len >= 255; len-= 255) + *op++ = 255; + *op++ = len; + *op++ = (distance & 255); + } + } + else + { + /* far away, but not yet in the another galaxy... */ + if(len < 7) + { + distance -= MAX_DISTANCE; + *op++ = (len << 5) + 31; + *op++ = 255; + *op++ = distance >> 8; + *op++ = distance & 255; + } + else + { + distance -= MAX_DISTANCE; + *op++ = (7 << 5) + 31; + for(len-=7; len >= 255; len-= 255) + *op++ = 255; + *op++ = len; + *op++ = 255; + *op++ = distance >> 8; + *op++ = distance & 255; + } + } +#else + + if(FASTLZ_UNEXPECT_CONDITIONAL(len > MAX_LEN-2)) + while(len > MAX_LEN-2) + { + *op++ = (7 << 5) + (distance >> 8); + *op++ = MAX_LEN - 2 - 7 -2; + *op++ = (distance & 255); + len -= MAX_LEN-2; + } + + if(len < 7) + { + *op++ = (len << 5) + (distance >> 8); + *op++ = (distance & 255); + } + else + { + *op++ = (7 << 5) + (distance >> 8); + *op++ = len - 7; + *op++ = (distance & 255); + } +#endif + + /* update the hash at match boundary */ + HASH_FUNCTION(hval,ip); + htab[hval] = ip++; + HASH_FUNCTION(hval,ip); + htab[hval] = ip++; + + /* assuming literal copy */ + *op++ = MAX_COPY-1; + + continue; + + literal: + *op++ = *anchor++; + ip = anchor; + copy++; + if(FASTLZ_UNEXPECT_CONDITIONAL(copy == MAX_COPY)) + { + copy = 0; + *op++ = MAX_COPY-1; + } + } + + /* left-over as literal copy */ + ip_bound++; + while(ip <= ip_bound) + { + *op++ = *ip++; + copy++; + if(copy == MAX_COPY) + { + copy = 0; + *op++ = MAX_COPY-1; + } + } + + /* if we have copied something, adjust the copy length */ + if(copy) + *(op-copy-1) = copy-1; + else + op--; + +#if FASTLZ_LEVEL==2 + /* marker for fastlz2 */ + *(flzuint8*)output |= (1 << 5); +#endif + + return op - (flzuint8*)output; +} + +static FASTLZ_INLINE int FASTLZ_DECOMPRESSOR(const void* input, int length, void* output, int maxout) +{ + const flzuint8* ip = (const flzuint8*) input; + const flzuint8* ip_limit = ip + length; + flzuint8* op = (flzuint8*) output; + flzuint8* op_limit = op + maxout; + flzuint32 ctrl = (*ip++) & 31; + int loop = 1; + + do + { + const flzuint8* ref = op; + flzuint32 len = ctrl >> 5; + flzuint32 ofs = (ctrl & 31) << 8; + + if(ctrl >= 32) + { +#if FASTLZ_LEVEL==2 + flzuint8 code; +#endif + len--; + ref -= ofs; + if (len == 7-1) +#if FASTLZ_LEVEL==1 + len += *ip++; + ref -= *ip++; +#else + do + { + code = *ip++; + len += code; + } while (code==255); + code = *ip++; + ref -= code; + + /* match from 16-bit distance */ + if(FASTLZ_UNEXPECT_CONDITIONAL(code==255)) + if(FASTLZ_EXPECT_CONDITIONAL(ofs==(31 << 8))) + { + ofs = (*ip++) << 8; + ofs += *ip++; + ref = op - ofs - MAX_DISTANCE; + } +#endif + +#ifdef FASTLZ_SAFE + if (FASTLZ_UNEXPECT_CONDITIONAL(op + len + 3 > op_limit)) + return 0; + + if (FASTLZ_UNEXPECT_CONDITIONAL(ref-1 < (flzuint8 *)output)) + return 0; +#endif + + if(FASTLZ_EXPECT_CONDITIONAL(ip < ip_limit)) + ctrl = *ip++; + else + loop = 0; + + if(ref == op) + { + /* optimize copy for a run */ + flzuint8 b = ref[-1]; + *op++ = b; + *op++ = b; + *op++ = b; + for(; len; --len) + *op++ = b; + } + else + { +#if !defined(FASTLZ_STRICT_ALIGN) + const flzuint16* p; + flzuint16* q; +#endif + /* copy from reference */ + ref--; + *op++ = *ref++; + *op++ = *ref++; + *op++ = *ref++; + +#if !defined(FASTLZ_STRICT_ALIGN) + /* copy a byte, so that now it's word aligned */ + if(len & 1) + { + *op++ = *ref++; + len--; + } + + /* copy 16-bit at once */ + q = (flzuint16*) op; + op += len; + p = (const flzuint16*) ref; + for(len>>=1; len > 4; len-=4) + { + *q++ = *p++; + *q++ = *p++; + *q++ = *p++; + *q++ = *p++; + } + for(; len; --len) + *q++ = *p++; +#else + for(; len; --len) + *op++ = *ref++; +#endif + } + } + else + { + ctrl++; +#ifdef FASTLZ_SAFE + if (FASTLZ_UNEXPECT_CONDITIONAL(op + ctrl > op_limit)) + return 0; + if (FASTLZ_UNEXPECT_CONDITIONAL(ip + ctrl > ip_limit)) + return 0; +#endif + + *op++ = *ip++; + for(--ctrl; ctrl; ctrl--) + *op++ = *ip++; + + loop = FASTLZ_EXPECT_CONDITIONAL(ip < ip_limit); + if(loop) + ctrl = *ip++; + } + } + while(FASTLZ_EXPECT_CONDITIONAL(loop)); + + return op - (flzuint8*)output; +} + +#endif /* !defined(FASTLZ_COMPRESSOR) && !defined(FASTLZ_DECOMPRESSOR) */ diff --git a/core/io/fastlz.h b/core/io/fastlz.h new file mode 100644 index 00000000000..f87bc7be31e --- /dev/null +++ b/core/io/fastlz.h @@ -0,0 +1,100 @@ +/* + FastLZ - lightning-fast lossless compression library + + Copyright (C) 2007 Ariya Hidayat (ariya@kde.org) + Copyright (C) 2006 Ariya Hidayat (ariya@kde.org) + Copyright (C) 2005 Ariya Hidayat (ariya@kde.org) + + 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 FASTLZ_H +#define FASTLZ_H + +#define FASTLZ_VERSION 0x000100 + +#define FASTLZ_VERSION_MAJOR 0 +#define FASTLZ_VERSION_MINOR 0 +#define FASTLZ_VERSION_REVISION 0 + +#define FASTLZ_VERSION_STRING "0.1.0" + +#if defined (__cplusplus) +extern "C" { +#endif + +/** + Compress a block of data in the input buffer and returns the size of + compressed block. The size of input buffer is specified by length. The + minimum input buffer size is 16. + + The output buffer must be at least 5% larger than the input buffer + and can not be smaller than 66 bytes. + + If the input is not compressible, the return value might be larger than + length (input buffer size). + + The input buffer and the output buffer can not overlap. +*/ + +int fastlz_compress(const void* input, int length, void* output); + +/** + Decompress a block of compressed data and returns the size of the + decompressed block. If error occurs, e.g. the compressed data is + corrupted or the output buffer is not large enough, then 0 (zero) + will be returned instead. + + The input buffer and the output buffer can not overlap. + + Decompression is memory safe and guaranteed not to write the output buffer + more than what is specified in maxout. + */ + +int fastlz_decompress(const void* input, int length, void* output, int maxout); + +/** + Compress a block of data in the input buffer and returns the size of + compressed block. The size of input buffer is specified by length. The + minimum input buffer size is 16. + + The output buffer must be at least 5% larger than the input buffer + and can not be smaller than 66 bytes. + + If the input is not compressible, the return value might be larger than + length (input buffer size). + + The input buffer and the output buffer can not overlap. + + Compression level can be specified in parameter level. At the moment, + only level 1 and level 2 are supported. + Level 1 is the fastest compression and generally useful for short data. + Level 2 is slightly slower but it gives better compression ratio. + + Note that the compressed data, regardless of the level, can always be + decompressed using the function fastlz_decompress above. +*/ + +int fastlz_compress_level(int level, const void* input, int length, void* output); + +#if defined (__cplusplus) +} +#endif + +#endif /* FASTLZ_H */ diff --git a/core/io/file_access_buffered.cpp b/core/io/file_access_buffered.cpp new file mode 100644 index 00000000000..6927b3772f1 --- /dev/null +++ b/core/io/file_access_buffered.cpp @@ -0,0 +1,184 @@ +/*************************************************************************/ +/* file_access_buffered.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 "file_access_buffered.h" + +#include + +#include "error_macros.h" + +Error FileAccessBuffered::set_error(Error p_error) const { + + return (last_error = p_error); +}; + +void FileAccessBuffered::set_cache_size(int p_size) { + + cache_size = p_size; +}; + +int FileAccessBuffered::get_cache_size() { + + return cache_size; +}; + +int FileAccessBuffered::cache_data_left() const { + + if (file.offset >= file.size) { + return 0; + }; + + if (cache.offset == -1 || file.offset < cache.offset || file.offset >= cache.offset + cache.buffer.size()) { + + return read_data_block(file.offset, cache_size); + + } else { + + return cache.buffer.size() - (file.offset - cache.offset); + }; + + return 0; +}; + +void FileAccessBuffered::seek(size_t p_position) { + + file.offset = p_position; +}; + +void FileAccessBuffered::seek_end(int64_t p_position) { + + file.offset = file.size + p_position; +}; + +size_t FileAccessBuffered::get_pos() const { + + return file.offset; +}; + +size_t FileAccessBuffered::get_len() const { + + return file.size; +}; + +bool FileAccessBuffered::eof_reached() const { + + return file.offset > file.size; +}; + +uint8_t FileAccessBuffered::get_8() const { + + ERR_FAIL_COND_V(!file.open,0); + + uint8_t byte = 0; + if (cache_data_left() >= 1) { + + byte = cache.buffer[file.offset - cache.offset]; + }; + + ++file.offset; + + return byte; +}; + +int FileAccessBuffered::get_buffer(uint8_t *p_dest,int p_elements) const { + + ERR_FAIL_COND_V(!file.open, -1); + + if (p_elements > cache_size) { + + int total_read = 0; + + if (!(cache.offset == -1 || file.offset < cache.offset || file.offset >= cache.offset + cache.buffer.size())) { + + int size = (cache.buffer.size() - (file.offset - cache.offset)); + size = size - (size % 4); + //DVector::Read read = cache.buffer.read(); + //memcpy(p_dest, read.ptr() + (file.offset - cache.offset), size); + memcpy(p_dest, cache.buffer.ptr() + (file.offset - cache.offset), size); + p_dest += size; + p_elements -= size; + file.offset += size; + total_read += size; + }; + + int err = read_data_block(file.offset, p_elements, p_dest); + if (err >= 0) { + total_read += err; + file.offset += err; + }; + + return total_read; + }; + + + int to_read = p_elements; + int total_read = 0; + while (to_read > 0) { + + int left = cache_data_left(); + if (left == 0) { + if (to_read > 0) { + file.offset += to_read; + }; + return total_read; + }; + if (left < 0) { + return left; + }; + + int r = MIN(left, to_read); + //DVector::Read read = cache.buffer.read(); + //memcpy(p_dest+total_read, &read.ptr()[file.offset - cache.offset], r); + memcpy(p_dest+total_read, cache.buffer.ptr() + (file.offset - cache.offset), r); + + file.offset += r; + total_read += r; + to_read -= r; + }; + + return p_elements; +}; + +bool FileAccessBuffered::is_open() const { + + return file.open; +}; + +Error FileAccessBuffered::get_error() const { + + return last_error; +}; + +FileAccessBuffered::FileAccessBuffered() { + + cache_size = DEFAULT_CACHE_SIZE; +}; + +FileAccessBuffered::~FileAccessBuffered(){ + +} diff --git a/core/io/file_access_buffered.h b/core/io/file_access_buffered.h new file mode 100644 index 00000000000..533cc6c7dd0 --- /dev/null +++ b/core/io/file_access_buffered.h @@ -0,0 +1,97 @@ +/*************************************************************************/ +/* file_access_buffered.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 FILE_ACCESS_BUFFERED_H +#define FILE_ACCESS_BUFFERED_H + +#include "os/file_access.h" + +#include "dvector.h" +#include "ustring.h" + +class FileAccessBuffered : public FileAccess { + +public: + enum { + DEFAULT_CACHE_SIZE = 128 * 1024, + }; + +private: + + int cache_size; + + int cache_data_left() const; + mutable Error last_error; + +protected: + + Error set_error(Error p_error) const; + + mutable struct File { + + bool open; + int size; + int offset; + String name; + int access_flags; + } file; + + mutable struct Cache { + + Vector buffer; + int offset; + } cache; + + virtual int read_data_block(int p_offset, int p_size, uint8_t *p_dest = 0) const =0; + + void set_cache_size(int p_size); + int get_cache_size(); + +public: + + virtual size_t get_pos() const; ///< get position in the file + virtual size_t get_len() const; ///< get size of the file + + virtual void seek(size_t p_position); ///< seek to a given position + virtual void seek_end(int64_t p_position=0); ///< seek from the end of file + + virtual bool eof_reached() const; + + virtual uint8_t get_8() const; + virtual int get_buffer(uint8_t *p_dst,int p_length) const; ///< get an array of bytes + + virtual bool is_open() const; + + virtual Error get_error() const; + + FileAccessBuffered(); + virtual ~FileAccessBuffered(); +}; + +#endif + diff --git a/core/io/file_access_buffered_fa.h b/core/io/file_access_buffered_fa.h new file mode 100644 index 00000000000..5de2f66ace0 --- /dev/null +++ b/core/io/file_access_buffered_fa.h @@ -0,0 +1,147 @@ +/*************************************************************************/ +/* file_access_buffered_fa.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 FILE_ACCESS_BUFFERED_FA_H +#define FILE_ACCESS_BUFFERED_FA_H + +#include "core/io/file_access_buffered.h" + +template +class FileAccessBufferedFA : public FileAccessBuffered { + + T f; + + int read_data_block(int p_offset, int p_size, uint8_t *p_dest = 0) const { + + ERR_FAIL_COND_V( !f.is_open(), -1 ); + + ((T*)&f)->seek(p_offset); + + if (p_dest) { + + f.get_buffer(p_dest, p_size); + return p_size; + + } else { + + cache.offset = p_offset; + cache.buffer.resize(p_size); + + // on dvector + //DVector::Write write = cache.buffer.write(); + //f.get_buffer(write.ptr(), p_size); + + // on vector + f.get_buffer(cache.buffer.ptr(), p_size); + + return p_size; + }; + }; + + static FileAccess* create() { + + return memnew( FileAccessBufferedFA() ); + }; + +protected: + virtual void _set_access_type(AccessType p_access) { + f._set_access_type(p_access); + FileAccessBuffered::_set_access_type(p_access); + }; + +public: + + + void store_8(uint8_t p_dest) { + + f.store_8(p_dest); + }; + + void store_buffer(const uint8_t *p_src,int p_length) { + + f.store_buffer(p_src, p_length); + }; + + bool file_exists(const String& p_name) { + + return f.file_exists(p_name); + }; + + Error _open(const String& p_path, int p_mode_flags) { + + close(); + + Error ret = f._open(p_path, p_mode_flags); + if (ret !=OK) + return ret; + //ERR_FAIL_COND_V( ret != OK, ret ); + + file.size = f.get_len(); + file.offset = 0; + file.open = true; + file.name = p_path; + file.access_flags = p_mode_flags; + + cache.buffer.resize(0); + cache.offset = 0; + + return set_error(OK); + }; + + void close() { + + f.close(); + + file.offset = 0; + file.size = 0; + file.open = false; + file.name = ""; + + cache.buffer.resize(0); + cache.offset = 0; + set_error(OK); + }; + +// static void make_default() { + + //FileAccess::create_func = FileAccessBufferedFA::create; +// }; + + virtual uint64_t _get_modified_time(const String& p_file) { + + return f._get_modified_time(p_file); + } + + FileAccessBufferedFA() { + + + }; +}; + + +#endif // FILE_ACCESS_BUFFERED_FA_H diff --git a/core/io/file_access_compressed.cpp b/core/io/file_access_compressed.cpp new file mode 100644 index 00000000000..ec9dce28e20 --- /dev/null +++ b/core/io/file_access_compressed.cpp @@ -0,0 +1,421 @@ +/*************************************************************************/ +/* file_access_compressed.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 "file_access_compressed.h" +#include "print_string.h" +void FileAccessCompressed::configure(const String& p_magic, Compression::Mode p_mode, int p_block_size) { + + magic=p_magic.ascii().get_data(); + if (magic.length()>4) + magic=magic.substr(0,4); + else { + while(magic.length()<4) + magic+=" "; + } + + cmode=p_mode; + block_size=p_block_size; + +} + + +#define WRITE_FIT(m_bytes) \ +{\ +if (write_pos+(m_bytes) > write_max) {\ + write_max=write_pos+(m_bytes);\ +}\ +if (write_max > write_buffer_size) {\ + write_buffer_size = nearest_power_of_2( write_max );\ + buffer.resize(write_buffer_size);\ + write_ptr=buffer.ptr();\ +}\ +} + + +Error FileAccessCompressed::open_after_magic(FileAccess *p_base) { + + + f=p_base; + cmode=(Compression::Mode)f->get_32(); + block_size=f->get_32(); + read_total=f->get_32(); + int bc = (read_total/block_size)+1; + int acc_ofs=f->get_pos()+bc*4; + int max_bs=0; + for(int i=0;iget_32(); + acc_ofs+=rb.csize; + max_bs=MAX(max_bs,rb.csize); + read_blocks.push_back(rb); + + + } + + comp_buffer.resize(max_bs); + buffer.resize(block_size); + read_ptr=buffer.ptr(); + f->get_buffer(comp_buffer.ptr(),read_blocks[0].csize); + at_end=false; + read_eof=false; + read_block_count=bc; + read_block_size=read_blocks.size()==1?read_total:block_size; + + Compression::decompress(buffer.ptr(),read_block_size,comp_buffer.ptr(),read_blocks[0].csize,cmode); + read_block=0; + read_pos=0; + + return OK; + +} + +Error FileAccessCompressed::_open(const String& p_path, int p_mode_flags){ + + ERR_FAIL_COND_V(p_mode_flags==READ_WRITE,ERR_UNAVAILABLE); + + if (f) + close(); + + + Error err; + f = FileAccess::open(p_path,p_mode_flags,&err); + if (err!=OK) { + //not openable + + f=NULL; + return err; + } + + if (p_mode_flags&WRITE) { + + buffer.clear(); + writing=true; + write_pos=0; + write_buffer_size=256; + buffer.resize(256); + write_max=0; + write_ptr=buffer.ptr(); + + + + //don't store anything else unless it's done saving! + } else { + + char rmagic[5]; + f->get_buffer((uint8_t*)rmagic,4); + rmagic[4]=0; + if (magic!=rmagic) { + memdelete(f); + f=NULL; + return ERR_FILE_UNRECOGNIZED; + } + + open_after_magic(f); + + } + + return OK; + +} +void FileAccessCompressed::close(){ + + if (!f) + return; + + + if (writing) { + //save block table and all compressed blocks + + CharString mgc = magic.utf8(); + f->store_buffer((const uint8_t*)mgc.get_data(),mgc.length()); //write header 4 + f->store_32(cmode); //write compression mode 4 + f->store_32(block_size); //write block size 4 + f->store_32(write_max); //max amount of data written 4 + int bc=(write_max/block_size)+1; + + for(int i=0;istore_32(0); //compressed sizes, will update later + } + + + Vector block_sizes; + for(int i=0;i cblock; + cblock.resize(Compression::get_max_compressed_buffer_size(bl,cmode)); + int s = Compression::compress(cblock.ptr(),bp,bl,cmode); + + f->store_buffer(cblock.ptr(),s); + block_sizes.push_back(s); + } + + f->seek(16); //ok write block sizes + for(int i=0;istore_32(block_sizes[i]); + f->seek_end(); + f->store_buffer((const uint8_t*)mgc.get_data(),mgc.length()); //magic at the end too + + buffer.clear(); + + } else { + + + comp_buffer.clear(); + buffer.clear(); + read_blocks.clear(); + } + + memdelete(f); + f=NULL; + +} + +bool FileAccessCompressed::is_open() const{ + + return f!=NULL; +} + +void FileAccessCompressed::seek(size_t p_position){ + + ERR_FAIL_COND(!f); + if (writing) { + + ERR_FAIL_COND(p_position>write_max); + + write_pos=p_position; + + } else { + + ERR_FAIL_COND(p_position>read_total); + if (p_position==read_total) { + at_end=true; + } else { + + int block_idx = p_position/block_size; + if (block_idx!=read_block) { + + read_block=block_idx; + f->seek(read_blocks[read_block].offset); + f->get_buffer(comp_buffer.ptr(),read_blocks[read_block].csize); + Compression::decompress(buffer.ptr(),read_blocks.size()==1?read_total:block_size,comp_buffer.ptr(),read_blocks[read_block].csize,cmode); + read_block_size=read_block==read_block_count-1?read_total%block_size:block_size; + } + + read_pos=p_position%block_size; + } + } +} + + +void FileAccessCompressed::seek_end(int64_t p_position){ + + ERR_FAIL_COND(!f); + if (writing) { + + seek(write_max+p_position); + } else { + + seek(read_total+p_position); + + } + + +} +size_t FileAccessCompressed::get_pos() const{ + + ERR_FAIL_COND_V(!f,0); + if (writing) { + + return write_pos; + } else { + + return read_block*block_size+read_pos; + } + +} +size_t FileAccessCompressed::get_len() const{ + + ERR_FAIL_COND_V(!f,0); + if (writing) { + + return write_max; + } else { + return read_total; + } +} + +bool FileAccessCompressed::eof_reached() const{ + + ERR_FAIL_COND_V(!f,false); + if (writing) { + return false; + } else { + return read_eof; + } +} + +uint8_t FileAccessCompressed::get_8() const{ + + ERR_FAIL_COND_V(writing,0); + ERR_FAIL_COND_V(!f,0); + + if (at_end) { + read_eof=true; + return 0; + } + + uint8_t ret = read_ptr[read_pos]; + + read_pos++; + if (read_pos>=read_block_size) { + read_block++; + + if (read_blockget_buffer(comp_buffer.ptr(),read_blocks[read_block].csize); + Compression::decompress(buffer.ptr(),read_blocks.size()==1?read_total:block_size,comp_buffer.ptr(),read_blocks[read_block].csize,cmode); + read_block_size=read_block==read_block_count-1?read_total%block_size:block_size; + read_pos=0; + + } else { + read_block--; + at_end=true; + ret =0; + } + } + + return ret; + +} +int FileAccessCompressed::get_buffer(uint8_t *p_dst, int p_length) const{ + + ERR_FAIL_COND_V(writing,0); + ERR_FAIL_COND_V(!f,0); + + if (at_end) { + read_eof=true; + return 0; + } + + + for(int i=0;i=read_block_size) { + read_block++; + + if (read_blockget_buffer(comp_buffer.ptr(),read_blocks[read_block].csize); + Compression::decompress(buffer.ptr(),read_blocks.size()==1?read_total:block_size,comp_buffer.ptr(),read_blocks[read_block].csize,cmode); + read_block_size=read_block==read_block_count-1?read_total%block_size:block_size; + read_pos=0; + + } else { + read_block--; + at_end=true; + if (iget_modified_time(p_file); + else + return 0; +} + +FileAccessCompressed::FileAccessCompressed() { + + f=NULL; + magic="GCMP"; + block_size=4096; + cmode=Compression::MODE_FASTLZ; + writing=false; + write_ptr=0; + write_buffer_size=0; + write_max=0; + block_size=0; + read_eof=false; + at_end=false; + read_total=0; + read_ptr=NULL; + read_block=0; + read_block_count=0; + read_block_size=0; + read_pos=0; + +} + +FileAccessCompressed::~FileAccessCompressed(){ + + if (f) + close(); + +} diff --git a/core/io/file_access_compressed.h b/core/io/file_access_compressed.h new file mode 100644 index 00000000000..3039b5c8bec --- /dev/null +++ b/core/io/file_access_compressed.h @@ -0,0 +1,101 @@ +/*************************************************************************/ +/* file_access_compressed.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 FILE_ACCESS_COMPRESSED_H +#define FILE_ACCESS_COMPRESSED_H + +#include "io/compression.h" +#include "os/file_access.h" + +class FileAccessCompressed : public FileAccess { + + Compression::Mode cmode; + bool writing; + int write_pos; + uint8_t*write_ptr; + int write_buffer_size; + int write_max; + int block_size; + mutable bool read_eof; + mutable bool at_end; + + struct ReadBlock { + int csize; + int offset; + }; + + mutable Vector comp_buffer; + uint8_t *read_ptr; + mutable int read_block; + int read_block_count; + mutable int read_block_size; + mutable int read_pos; + Vector read_blocks; + int read_total; + + + + + String magic; + mutable Vector buffer; + FileAccess *f; +public: + + void configure(const String& p_magic, Compression::Mode p_mode=Compression::MODE_FASTLZ, int p_block_size=4096); + + Error open_after_magic(FileAccess *p_base); + + virtual Error _open(const String& p_path, int p_mode_flags); ///< open a file + virtual void close(); ///< close a file + virtual bool is_open() const; ///< true when file is open + + virtual void seek(size_t p_position); ///< seek to a given position + virtual void seek_end(int64_t p_position=0); ///< seek from the end of file + virtual size_t get_pos() const; ///< get position in the file + virtual size_t get_len() const; ///< get size of the file + + virtual bool eof_reached() const; ///< reading passed EOF + + virtual uint8_t get_8() const; ///< get a byte + virtual int get_buffer(uint8_t *p_dst, int p_length) const; + + virtual Error get_error() const; ///< get last error + + virtual void store_8(uint8_t p_dest); ///< store a byte + + virtual bool file_exists(const String& p_name); ///< return true if a file exists + + virtual uint64_t _get_modified_time(const String& p_file); + + + FileAccessCompressed(); + virtual ~FileAccessCompressed(); + +}; + +#endif // FILE_ACCESS_COMPRESSED_H diff --git a/core/io/file_access_memory.cpp b/core/io/file_access_memory.cpp new file mode 100644 index 00000000000..a1dd2e48bbf --- /dev/null +++ b/core/io/file_access_memory.cpp @@ -0,0 +1,188 @@ +/*************************************************************************/ +/* file_access_memory.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 "file_access_memory.h" + +#include "os/dir_access.h" +#include "os/copymem.h" +#include "globals.h" +#include "map.h" + +static Map >* files = NULL; + +void FileAccessMemory::register_file(String p_name, Vector p_data) { + + if (!files) { + files = memnew((Map >)); + }; + + String name; + if (Globals::get_singleton()) + name = Globals::get_singleton()->globalize_path(p_name); + else + name = p_name; + name = DirAccess::normalize_path(name); + + (*files)[name] = p_data; +}; + +void FileAccessMemory::cleanup() { + + if (!files) + return; + + memdelete(files); +}; + + +FileAccess* FileAccessMemory::create() { + + return memnew(FileAccessMemory); +}; + +bool FileAccessMemory::file_exists(const String& p_name) { + + String name = fix_path(p_name); + name = DirAccess::normalize_path(name); + + return files && (files->find(name) != NULL); +}; + + +Error FileAccessMemory::_open(const String& p_path, int p_mode_flags) { + + ERR_FAIL_COND_V(!files, ERR_FILE_NOT_FOUND); + + String name = fix_path(p_path); + name = DirAccess::normalize_path(name); + + Map >::Element* E = files->find(name); + ERR_FAIL_COND_V(!E, ERR_FILE_NOT_FOUND); + + data = &(E->get()[0]); + length = E->get().size(); + pos = 0; + + return OK; +}; + +void FileAccessMemory::close() { + + data = NULL; +}; + +bool FileAccessMemory::is_open() const { + + return data != NULL; +}; + +void FileAccessMemory::seek(size_t p_position) { + + ERR_FAIL_COND(!data); + pos = p_position; +}; + +void FileAccessMemory::seek_end(int64_t p_position) { + + ERR_FAIL_COND(!data); + pos = length + p_position; +}; + +size_t FileAccessMemory::get_pos() const { + + ERR_FAIL_COND_V(!data, 0); + return pos; +}; + +size_t FileAccessMemory::get_len() const { + + ERR_FAIL_COND_V(!data, 0); + return length; +}; + +bool FileAccessMemory::eof_reached() const { + + return pos >= length; +}; + +uint8_t FileAccessMemory::get_8() const { + + uint8_t ret; + if (pos < length) { + ret = data[pos]; + }; + ++pos; + + return ret; +}; + +int FileAccessMemory::get_buffer(uint8_t *p_dst,int p_length) const { + + ERR_FAIL_COND_V(!data, -1); + + int left = length - pos; + int read = MIN(p_length, left); + + if (read < p_length) { + WARN_PRINT("Reading less data than requested"); + }; + + copymem(p_dst, &data[pos], read); + pos += p_length; + + return read; +}; + +Error FileAccessMemory::get_error() const { + + return pos >= length ? ERR_FILE_EOF : OK; +}; + +void FileAccessMemory::store_8(uint8_t p_byte) { + + ERR_FAIL_COND(!data); + ERR_FAIL_COND(pos >= length); + data[pos++] = p_byte; +}; + +void FileAccessMemory::store_buffer(const uint8_t *p_src,int p_length) { + + int left = length - pos; + int write = MIN(p_length, left); + if (write < p_length) { + WARN_PRINT("Writing less data than requested"); + }; + + copymem(&data[pos], p_src, write); + pos += p_length; +}; + +FileAccessMemory::FileAccessMemory() { + + data = NULL; +} diff --git a/core/io/file_access_memory.h b/core/io/file_access_memory.h new file mode 100644 index 00000000000..a02a022a4f8 --- /dev/null +++ b/core/io/file_access_memory.h @@ -0,0 +1,76 @@ +/*************************************************************************/ +/* file_access_memory.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 FILE_ACCESS_MEMORY_H +#define FILE_ACCESS_MEMORY_H + +#include "os/file_access.h" + +class FileAccessMemory : public FileAccess { + + uint8_t* data; + int length; + mutable int pos; + + static FileAccess* create(); + +public: + + static void register_file(String p_name, Vector p_data); + static void cleanup(); + + virtual Error _open(const String& p_path, int p_mode_flags); ///< open a file + virtual void close(); ///< close a file + virtual bool is_open() const; ///< true when file is open + + virtual void seek(size_t p_position); ///< seek to a given position + virtual void seek_end(int64_t p_position); ///< seek from the end of file + virtual size_t get_pos() const; ///< get position in the file + virtual size_t get_len() const; ///< get size of the file + + virtual bool eof_reached() const; ///< reading passed EOF + + virtual uint8_t get_8() const; ///< get a byte + + virtual int get_buffer(uint8_t *p_dst,int p_length) const; ///< get an array of bytes + + virtual Error get_error() const; ///< get last error + + virtual void store_8(uint8_t p_dest); ///< store a byte + virtual void store_buffer(const uint8_t *p_src,int p_length); ///< store an array of bytes + + virtual bool file_exists(const String& p_name); ///< return true if a file exists + + virtual uint64_t _get_modified_time(const String& p_file) { return 0; } + + + + FileAccessMemory(); +}; + +#endif // FILE_ACCESS_MEMORY_H diff --git a/core/io/file_access_network.cpp b/core/io/file_access_network.cpp new file mode 100644 index 00000000000..a5636b137d8 --- /dev/null +++ b/core/io/file_access_network.cpp @@ -0,0 +1,566 @@ +/*************************************************************************/ +/* file_access_network.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 "file_access_network.h" +#include "marshalls.h" +#include "globals.h" +#include "os/os.h" +#include "io/ip.h" + + + +#define DEBUG_PRINT(m_p) print_line(m_p) +//#define DEBUG_TIME(m_what) printf("MS: %s - %lli\n",m_what,OS::get_singleton()->get_ticks_usec()); +//#define DEBUG_PRINT(m_p) +#define DEBUG_TIME(m_what) + + +void FileAccessNetworkClient::lock_mutex() { + + mutex->lock(); + lockcount++; +} + +void FileAccessNetworkClient::unlock_mutex() { + + lockcount--; + mutex->unlock(); + +} + +void FileAccessNetworkClient::put_32(int p_32) { + + uint8_t buf[4]; + encode_uint32(p_32,buf); + client->put_data(buf,4); + DEBUG_PRINT("put32: "+itos(p_32)); +} + +void FileAccessNetworkClient::put_64(int64_t p_64) { + + uint8_t buf[8]; + encode_uint64(p_64,buf); + client->put_data(buf,8); + DEBUG_PRINT("put64: "+itos(p_64)); + +} + +int FileAccessNetworkClient::get_32() { + + uint8_t buf[4]; + client->get_data(buf,4); + return decode_uint32(buf); + +} + +int64_t FileAccessNetworkClient::get_64() { + + uint8_t buf[8]; + client->get_data(buf,8); + return decode_uint64(buf); + +} + +void FileAccessNetworkClient::_thread_func() { + + client->set_nodelay(true); + while(!quit) { + + DEBUG_PRINT("SEM WAIT - "+itos(sem->get())); + Error err = sem->wait(); + DEBUG_TIME("sem_unlock"); + //DEBUG_PRINT("semwait returned "+itos(werr)); + DEBUG_PRINT("MUTEX LOCK "+itos(lockcount)); + DEBUG_PRINT("POPO"); + DEBUG_PRINT("PEPE"); + lock_mutex(); + DEBUG_PRINT("MUTEX PASS"); + + blockrequest_mutex->lock(); + while(block_requests.size()) { + put_32(block_requests.front()->get().id); + put_32(FileAccessNetwork::COMMAND_READ_BLOCK); + put_64(block_requests.front()->get().offset); + put_32(block_requests.front()->get().size); + block_requests.pop_front(); + } + blockrequest_mutex->unlock(); + + DEBUG_PRINT("THREAD ITER"); + + DEBUG_TIME("sem_read"); + int id = get_32(); + + int response = get_32(); + DEBUG_PRINT("GET RESPONSE: "+itos(response)); + + FileAccessNetwork *fa=NULL; + + if (response!=FileAccessNetwork::RESPONSE_DATA) { + ERR_FAIL_COND(!accesses.has(id)); + } + + if (accesses.has(id)) + fa=accesses[id]; + + + switch(response) { + + case FileAccessNetwork::RESPONSE_OPEN: { + + + DEBUG_TIME("sem_open"); + int status = get_32(); + if (status!=OK) { + fa->_respond(0,Error(status)); + } else { + uint64_t len = get_64(); + fa->_respond(len,Error(status)); + } + + fa->sem->post(); + + + } break; + case FileAccessNetwork::RESPONSE_DATA: { + + int64_t offset = get_64(); + uint32_t len = get_32(); + + Vector block; + block.resize(len); + client->get_data(block.ptr(),len); + + if (fa) //may have been queued + fa->_set_block(offset,block); + + } break; + case FileAccessNetwork::RESPONSE_FILE_EXISTS: { + + + int status = get_32(); + fa->exists_modtime=status!=0; + fa->sem->post(); + + + + } break; + case FileAccessNetwork::RESPONSE_GET_MODTIME: { + + + uint64_t status = get_64(); + fa->exists_modtime=status; + fa->sem->post(); + + } break; + + } + + + unlock_mutex(); + } + +} + +void FileAccessNetworkClient::_thread_func(void *s) { + + FileAccessNetworkClient *self =(FileAccessNetworkClient*)s; + + self->_thread_func(); + +} + +Error FileAccessNetworkClient::connect(const String& p_host,int p_port,const String& p_password) { + + IP_Address ip; + + if (p_host.is_valid_ip_address()) { + ip=p_host; + } else { + ip=IP::get_singleton()->resolve_hostname(p_host); + } + + DEBUG_PRINT("IP: "+String(ip)+" port "+itos(p_port)); + Error err = client->connect(ip,p_port); + ERR_FAIL_COND_V(err,err); + while(client->get_status()==StreamPeerTCP::STATUS_CONNECTING) { +//DEBUG_PRINT("trying to connect...."); + OS::get_singleton()->delay_usec(1000); + } + + if (client->get_status()!=StreamPeerTCP::STATUS_CONNECTED) { + return ERR_CANT_CONNECT; + } + + CharString cs = p_password.utf8(); + put_32(cs.length()); + client->put_data((const uint8_t*)cs.ptr(),cs.length()); + + int e = get_32(); + + if (e!=OK) { + return ERR_INVALID_PARAMETER; + } + + thread = Thread::create(_thread_func,this); + + return OK; +} + +FileAccessNetworkClient *FileAccessNetworkClient::singleton=NULL; + + +FileAccessNetworkClient::FileAccessNetworkClient() { + + thread=NULL; + mutex = Mutex::create(); + blockrequest_mutex = Mutex::create(); + quit=false; + singleton=this; + last_id=0; + client = Ref( StreamPeerTCP::create() ); + sem=Semaphore::create(); + lockcount=0; +} + +FileAccessNetworkClient::~FileAccessNetworkClient() { + + if (thread) { + quit=true; + sem->post(); + Thread::wait_to_finish(thread); + } + + memdelete(blockrequest_mutex); + memdelete(mutex); + memdelete(sem); + + +} + +void FileAccessNetwork::_set_block(size_t p_offset,const Vector& p_block) { + + + int page = p_offset/page_size; + ERR_FAIL_INDEX(page,pages.size()); + if (pagelock(); + pages[page].buffer=p_block; + pages[page].queued=false; + buffer_mutex->unlock(); + + if (waiting_on_page==page) { + waiting_on_page=-1; + page_sem->post(); + } +} + + +void FileAccessNetwork::_respond(size_t p_len,Error p_status) { + + DEBUG_PRINT("GOT RESPONSE - len: "+itos(p_len)+" status: "+itos(p_status)); + response=p_status; + if (response!=OK) + return; + opened=true; + total_size=p_len; + int pc = ((total_size-1)/page_size)+1; + pages.resize(pc); + + + + +} + +Error FileAccessNetwork::_open(const String& p_path, int p_mode_flags) { + + ERR_FAIL_COND_V(p_mode_flags!=READ,ERR_UNAVAILABLE); + if (opened) + close(); + FileAccessNetworkClient *nc = FileAccessNetworkClient::singleton; + DEBUG_PRINT("open: "+p_path); + + DEBUG_TIME("open_begin"); + + nc->lock_mutex(); + nc->put_32(id); + nc->accesses[id]=this; + nc->put_32(COMMAND_OPEN_FILE); + CharString cs =p_path.utf8(); + nc->put_32(cs.length()); + nc->client->put_data((const uint8_t*)cs.ptr(),cs.length()); + pos=0; + eof_flag=false; + last_page=-1; + last_page_buff=NULL; + +// buffers.clear(); + nc->unlock_mutex(); + DEBUG_PRINT("OPEN POST"); + DEBUG_TIME("open_post"); + nc->sem->post(); //awaiting answer + DEBUG_PRINT("WAIT..."); + sem->wait(); + DEBUG_TIME("open_end"); + DEBUG_PRINT("WAIT ENDED..."); + + return response; +} + +void FileAccessNetwork::close(){ + + if (!opened) + return; + + FileAccessNetworkClient *nc = FileAccessNetworkClient::singleton; + + DEBUG_PRINT("CLOSE"); + nc->lock_mutex(); + nc->put_32(id); + nc->put_32(COMMAND_CLOSE); + pages.clear(); + opened=false; + nc->unlock_mutex(); + + +} +bool FileAccessNetwork::is_open() const{ + + return opened; +} + +void FileAccessNetwork::seek(size_t p_position){ + + ERR_FAIL_COND(!opened); + eof_flag=p_position>total_size; + + if (p_position>=total_size) { + p_position=total_size; + } + + pos=p_position; +} + +void FileAccessNetwork::seek_end(int64_t p_position){ + + seek(total_size+p_position); + +} +size_t FileAccessNetwork::get_pos() const{ + + ERR_FAIL_COND_V(!opened,0); + return pos; +} +size_t FileAccessNetwork::get_len() const{ + + ERR_FAIL_COND_V(!opened,0); + return total_size; +} + +bool FileAccessNetwork::eof_reached() const{ + + ERR_FAIL_COND_V(!opened,false); + return eof_flag; +} + +uint8_t FileAccessNetwork::get_8() const{ + + uint8_t v; + get_buffer(&v,1); + return v; + +} + + +void FileAccessNetwork::_queue_page(int p_page) const { + + if (p_page>=pages.size()) + return; + if (pages[p_page].buffer.empty() && !pages[p_page].queued) { + + + FileAccessNetworkClient *nc = FileAccessNetworkClient::singleton; + + nc->blockrequest_mutex->lock(); + FileAccessNetworkClient::BlockRequest br; + br.id=id; + br.offset=size_t(p_page)*page_size; + br.size=page_size; + nc->block_requests.push_back(br); + pages[p_page].queued=true; + nc->blockrequest_mutex->unlock(); + DEBUG_PRINT("QUEUE PAGE POST"); + nc->sem->post(); + DEBUG_PRINT("queued "+itos(p_page)); + } + +} + +int FileAccessNetwork::get_buffer(uint8_t *p_dst, int p_length) const{ + + //bool eof=false; + if (pos+p_length>total_size) { + eof_flag=true; + } + if (pos+p_length>=total_size) { + p_length=total_size-pos; + } + +// FileAccessNetworkClient *nc = FileAccessNetworkClient::singleton; + + uint8_t *buff=last_page_buff; + + for(int i=0;ilock(); + if (pages[page].buffer.empty()) { + //fuck + + waiting_on_page=page; + for(int j=0;junlock(); + DEBUG_PRINT("wait"); + page_sem->wait(); + DEBUG_PRINT("done"); + } else { + + for(int j=0;junlock(); + } + + buff=pages[page].buffer.ptr(); + last_page_buff=buff; + last_page=page; + } + + p_dst[i]=buff[pos-uint64_t(page)*page_size]; + pos++; + } + + return p_length; +} + +Error FileAccessNetwork::get_error() const{ + + return pos==total_size?ERR_FILE_EOF:OK; +} + +void FileAccessNetwork::store_8(uint8_t p_dest) { + + ERR_FAIL(); +} + +bool FileAccessNetwork::file_exists(const String& p_path){ + + FileAccessNetworkClient *nc = FileAccessNetworkClient::singleton; + nc->lock_mutex(); + nc->put_32(id); + nc->put_32(COMMAND_FILE_EXISTS); + CharString cs=p_path.utf8(); + nc->put_32(cs.length()); + nc->client->put_data((const uint8_t*)cs.ptr(),cs.length()); + nc->unlock_mutex(); + DEBUG_PRINT("FILE EXISTS POST"); + nc->sem->post(); + sem->wait(); + + return exists_modtime!=0; + +} + +uint64_t FileAccessNetwork::_get_modified_time(const String& p_file){ + + FileAccessNetworkClient *nc = FileAccessNetworkClient::singleton; + nc->lock_mutex(); + nc->put_32(id); + nc->put_32(COMMAND_GET_MODTIME); + CharString cs=p_file.utf8(); + nc->put_32(cs.length()); + nc->client->put_data((const uint8_t*)cs.ptr(),cs.length()); + nc->unlock_mutex(); + DEBUG_PRINT("MODTIME POST"); + nc->sem->post(); + sem->wait(); + + return exists_modtime; + +} + +FileAccessNetwork::FileAccessNetwork() { + + eof_flag=false; + opened=false; + pos=0; + sem=Semaphore::create(); + page_sem=Semaphore::create(); + buffer_mutex=Mutex::create(); + FileAccessNetworkClient *nc = FileAccessNetworkClient::singleton; + nc->lock_mutex(); + id=nc->last_id++; + nc->accesses[id]=this; + nc->unlock_mutex(); + page_size = GLOBAL_DEF("remote_fs/page_size",65536); + read_ahead = GLOBAL_DEF("remote_fs/page_read_ahead",4); + max_pages = GLOBAL_DEF("remote_fs/max_pages",20); + last_activity_val=0; + waiting_on_page=-1; + last_page=-1; + + +} + +FileAccessNetwork::~FileAccessNetwork() { + + close(); + memdelete(sem); + memdelete(page_sem); + memdelete(buffer_mutex); + + FileAccessNetworkClient *nc = FileAccessNetworkClient::singleton; + nc->lock_mutex(); + id=nc->last_id++; + nc->accesses.erase(id); + nc->unlock_mutex(); + +} diff --git a/core/io/file_access_network.h b/core/io/file_access_network.h new file mode 100644 index 00000000000..f338bea43ff --- /dev/null +++ b/core/io/file_access_network.h @@ -0,0 +1,169 @@ +/*************************************************************************/ +/* file_access_network.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 FILE_ACCESS_NETWORK_H +#define FILE_ACCESS_NETWORK_H + +#include "os/file_access.h" +#include "os/semaphore.h" +#include "os/thread.h" +#include "io/stream_peer_tcp.h" + +class FileAccessNetwork; + +class FileAccessNetworkClient { + + + struct BlockRequest { + + int id; + uint64_t offset; + int size; + }; + + int ml; + + List block_requests; + + Semaphore *sem; + Thread *thread; + bool quit; + Mutex *mutex; + Mutex *blockrequest_mutex; + Map accesses; + Ref client; + int last_id; + + Vector block; + + void _thread_func(); + static void _thread_func(void *s); + + void put_32(int p_32); + void put_64(int64_t p_64); + int get_32(); + int64_t get_64(); + int lockcount; + void lock_mutex(); + void unlock_mutex(); + +friend class FileAccessNetwork; + static FileAccessNetworkClient *singleton; + +public: + + static FileAccessNetworkClient *get_singleton() { return singleton; } + + Error connect(const String& p_host,int p_port,const String& p_password=""); + + FileAccessNetworkClient(); + ~FileAccessNetworkClient(); + +}; + +class FileAccessNetwork : public FileAccess { + + Semaphore *sem; + Semaphore *page_sem; + Mutex *buffer_mutex; + bool opened; + size_t total_size; + mutable size_t pos; + int id; + mutable bool eof_flag; + mutable int last_page; + mutable uint8_t *last_page_buff; + + uint32_t page_size; + int read_ahead; + int max_pages; + + mutable int waiting_on_page; + mutable int last_activity_val; + struct Page { + int activity; + bool queued; + Vector buffer; + Page() { activity=0; queued=false; } + }; + + mutable Vector< Page > pages; + + mutable Error response; + + uint64_t exists_modtime; +friend class FileAccessNetworkClient; + void _queue_page(int p_page) const; + void _respond(size_t p_len,Error p_status); + void _set_block(size_t p_offset,const Vector& p_block); + +public: + + enum Command { + COMMAND_OPEN_FILE, + COMMAND_READ_BLOCK, + COMMAND_CLOSE, + COMMAND_FILE_EXISTS, + COMMAND_GET_MODTIME, + }; + + enum Response { + RESPONSE_OPEN, + RESPONSE_DATA, + RESPONSE_FILE_EXISTS, + RESPONSE_GET_MODTIME, + }; + + + virtual Error _open(const String& p_path, int p_mode_flags); ///< open a file + virtual void close(); ///< close a file + virtual bool is_open() const; ///< true when file is open + + virtual void seek(size_t p_position); ///< seek to a given position + virtual void seek_end(int64_t p_position=0); ///< seek from the end of file + virtual size_t get_pos() const; ///< get position in the file + virtual size_t get_len() const; ///< get size of the file + + virtual bool eof_reached() const; ///< reading passed EOF + + virtual uint8_t get_8() const; ///< get a byte + virtual int get_buffer(uint8_t *p_dst, int p_length) const; + + virtual Error get_error() const; ///< get last error + + virtual void store_8(uint8_t p_dest); ///< store a byte + + virtual bool file_exists(const String& p_path); ///< return true if a file exists + + virtual uint64_t _get_modified_time(const String& p_file); + + FileAccessNetwork(); + ~FileAccessNetwork(); +}; + +#endif // FILE_ACCESS_NETWORK_H diff --git a/core/io/file_access_pack.cpp b/core/io/file_access_pack.cpp new file mode 100644 index 00000000000..4443dd3575f --- /dev/null +++ b/core/io/file_access_pack.cpp @@ -0,0 +1,469 @@ +/*************************************************************************/ +/* file_access_pack.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 "file_access_pack.h" +#include "version.h" + +#include + +Error PackedData::add_pack(const String& p_path) { + + for (int i=0; itry_open_pack(p_path)) { + + return OK; + }; + }; + + return ERR_FILE_UNRECOGNIZED; +}; + +void PackedData::add_path(const String& pkg_path, const String& path, uint64_t ofs, uint64_t size, PackSource* p_src) { + + bool exists = files.has(path); + + PackedFile pf; + pf.pack=pkg_path; + pf.offset=ofs; + pf.size=size; + pf.src = p_src; + + files[path]=pf; + + if (!exists) { + //search for dir + String p = path.replace_first("res://",""); + PackedDir *cd=root; + + if (p.find("/")!=-1) { //in a subdir + + Vector ds=p.get_base_dir().split("/"); + + for(int j=0;jsubdirs.has(ds[j])) { + + PackedDir *pd = memnew( PackedDir ); + pd->name=ds[j]; + pd->parent=cd; + cd->subdirs[pd->name]=pd; + cd=pd; + } else { + cd=cd->subdirs[ds[j]]; + } + } + } + cd->files.insert(path.get_file()); + } +} + +void PackedData::add_pack_source(PackSource *p_source) { + + sources.push_back(p_source); +}; + +PackedData *PackedData::singleton=NULL; + +PackedData::PackedData() { + + singleton=this; + root=memnew(PackedDir); + root->parent=NULL; + disabled=false; + + add_pack_source(memnew(PackedSourcePCK)); +} + + +////////////////////////////////////////////////////////////////// + +bool PackedSourcePCK::try_open_pack(const String& p_path) { + + FileAccess *f = FileAccess::open(p_path,FileAccess::READ); + if (!f) + return false; + + uint32_t magic= f->get_32(); + + if (magic != 0x4b435047) { + //maybe at he end.... self contained exe + f->seek_end(); + f->seek( f->get_pos() -4 ); + magic = f->get_32(); + if (magic != 0x4b435047) { + + memdelete(f); + return false; + } + f->seek( f->get_pos() -12 ); + + + uint64_t ds = f->get_64(); + f->seek( f->get_pos() -ds-8 ); + + magic = f->get_32(); + if (magic != 0x4b435047) { + + memdelete(f); + return false; + } + + } + + uint32_t ver_major = f->get_32(); + uint32_t ver_minor = f->get_32(); + uint32_t ver_rev = f->get_32(); + + ERR_EXPLAIN("Pack created with a newer version of the engine: "+itos(ver_major)+"."+itos(ver_minor)+"."+itos(ver_rev)); + ERR_FAIL_COND_V( ver_major > VERSION_MAJOR || (ver_major == VERSION_MAJOR && ver_minor > VERSION_MINOR), ERR_INVALID_DATA); + + for(int i=0;i<16;i++) { + //reserved + f->get_32(); + } + + int file_count = f->get_32(); + + for(int i=0;iget_32(); + CharString cs; + cs.resize(sl+1); + f->get_buffer((uint8_t*)cs.ptr(),sl); + cs[sl]=0; + + String path; + path.parse_utf8(cs.ptr()); + + uint64_t ofs = f->get_64(); + uint64_t size = f->get_64(); + + PackedData::get_singleton()->add_path(p_path, path, ofs, size, this); + }; + + return true; +}; + +FileAccess* PackedSourcePCK::get_file(const String &p_path, PackedData::PackedFile* p_file) { + + return memnew( FileAccessPack(p_path, *p_file)); +}; + +////////////////////////////////////////////////////////////////// + + +Error FileAccessPack::_open(const String& p_path, int p_mode_flags) { + + ERR_FAIL_V(ERR_UNAVAILABLE); + return ERR_UNAVAILABLE; +} + +void FileAccessPack::close() { + + f->close(); +} + +bool FileAccessPack::is_open() const{ + + return f->is_open(); +} + +void FileAccessPack::seek(size_t p_position){ + + if (p_position>pf.size) { + eof=true; + } else { + eof=false; + } + + f->seek(pf.offset+p_position); +} +void FileAccessPack::seek_end(int64_t p_position){ + + seek(pf.size+p_position); + +} +size_t FileAccessPack::get_pos() const { + + return pos; +} +size_t FileAccessPack::get_len() const{ + + return pf.size; +} + +bool FileAccessPack::eof_reached() const{ + + return eof; +} + +uint8_t FileAccessPack::get_8() const { + + if (pos>=pf.size) { + eof=true; + return 0; + } + + pos++; + return f->get_8(); +} + + +int FileAccessPack::get_buffer(uint8_t *p_dst,int p_length) const { + + if (eof) + return 0; + + int64_t to_read=p_length; + if (to_read+pos > pf.size) { + eof=true; + to_read=int64_t(pf.size)-int64_t(pos); + } + + pos+=p_length; + + if (to_read<=0) + return 0; + f->get_buffer(p_dst,to_read); + + return to_read; +} + +void FileAccessPack::set_endian_swap(bool p_swap) { + FileAccess::set_endian_swap(p_swap); + f->set_endian_swap(p_swap); +} + +Error FileAccessPack::get_error() const { + + if (eof) + return ERR_FILE_EOF; + return OK; +} + +void FileAccessPack::store_8(uint8_t p_dest) { + + ERR_FAIL(); + +} + +void FileAccessPack::store_buffer(const uint8_t *p_src,int p_length) { + + ERR_FAIL(); + +} + +bool FileAccessPack::file_exists(const String& p_name) { + + return false; +} + + +FileAccessPack::FileAccessPack(const String& p_path, const PackedData::PackedFile& p_file) { + + pf=p_file; + f=FileAccess::open(pf.pack,FileAccess::READ); + if (!f) { + ERR_EXPLAIN("Can't open pack-referenced file: "+String(pf.pack)); + ERR_FAIL_COND(!f); + } + f->seek(pf.offset); + pos=0; + eof=false; +} + +FileAccessPack::~FileAccessPack() { + if (f) + memdelete(f); +} + + +////////////////////////////////////////////////////////////////////////////////// +// DIR ACCESS +////////////////////////////////////////////////////////////////////////////////// + + +bool DirAccessPack::list_dir_begin() { + + + list_dirs.clear(); + list_files.clear(); + + for (Map::Element *E=current->subdirs.front();E;E=E->next()) { + + list_dirs.push_back(E->key()); + } + + for (Set::Element *E=current->files.front();E;E=E->next()) { + + list_files.push_back(E->get()); + } + + return true; +} + +String DirAccessPack::get_next(){ + + if (list_dirs.size()) { + cdir=true; + String d = list_dirs.front()->get(); + list_dirs.pop_front(); + return d; + } else if (list_files.size()) { + cdir=false; + String f = list_files.front()->get(); + list_files.pop_front(); + return f; + } else { + return String(); + } +} +bool DirAccessPack::current_is_dir() const{ + + return cdir; +} +void DirAccessPack::list_dir_end() { + + list_dirs.clear(); + list_files.clear(); +} + +int DirAccessPack::get_drive_count() { + + return 0; +} +String DirAccessPack::get_drive(int p_drive) { + + return ""; +} + +Error DirAccessPack::change_dir(String p_dir) { + + String nd = p_dir.replace("\\","/"); + bool absolute=false; + if (nd.begins_with("res://")) { + nd=nd.replace_first("res://",""); + absolute=true; + } + + nd=nd.simplify_path(); + + if (nd.begins_with("/")) { + nd=nd.replace_first("/","") ; + absolute=true; + } + + Vector paths = nd.split("/"); + + PackedData::PackedDir *pd; + + if (absolute) + pd = PackedData::get_singleton()->root; + else + pd = current; + + for(int i=0;iparent) { + pd=pd->parent; + } + } else if (pd->subdirs.has(p)) { + + pd=pd->subdirs[p]; + + } else { + + return ERR_INVALID_PARAMETER; + } + } + + current=pd; + + return OK; + + +} + +String DirAccessPack::get_current_dir() { + + String p; + PackedData::PackedDir *pd = current; + while(pd->parent) { + + if (pd!=current) + p="/"+p; + p=p+pd->name; + } + + return "res://"+p; + +} + +bool DirAccessPack::file_exists(String p_file){ + + return current->files.has(p_file); +} + +Error DirAccessPack::make_dir(String p_dir){ + + return ERR_UNAVAILABLE; +} + +Error DirAccessPack::rename(String p_from, String p_to){ + + return ERR_UNAVAILABLE; + +} +Error DirAccessPack::remove(String p_name){ + + return ERR_UNAVAILABLE; + +} + +size_t DirAccessPack::get_space_left(){ + + return 0; +} + +DirAccessPack::DirAccessPack() { + + current=PackedData::get_singleton()->root; + cdir=false; +} + +DirAccessPack::~DirAccessPack() { + + +} + + diff --git a/core/io/file_access_pack.h b/core/io/file_access_pack.h new file mode 100644 index 00000000000..97e236009e7 --- /dev/null +++ b/core/io/file_access_pack.h @@ -0,0 +1,204 @@ +/*************************************************************************/ +/* file_access_pack.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 FILE_ACCESS_PACK_H +#define FILE_ACCESS_PACK_H + +#include "os/file_access.h" +#include "os/dir_access.h" +#include "map.h" +#include "list.h" +#include "print_string.h" + +class PackSource; + +class PackedData { +friend class FileAccessPack; +friend class DirAccessPack; +friend class PackSource; + +public: + struct PackedFile { + + String pack; + uint64_t offset; + uint64_t size; + PackSource* src; + }; + +private: + struct PackedDir { + PackedDir *parent; + String name; + Map subdirs; + Set files; + }; + + + Map files; + Vector sources; + + PackedDir *root; + //Map dirs; + + static PackedData *singleton; + bool disabled; + +public: + + void add_pack_source(PackSource* p_source); + void add_path(const String& pkg_path, const String& path, uint64_t ofs, uint64_t size, PackSource* p_src); // for PackSource + + void set_disabled(bool p_disabled) { disabled=p_disabled; } + _FORCE_INLINE_ bool is_disabled() const { return disabled; } + + static PackedData *get_singleton() { return singleton; } + Error add_pack(const String& p_path); + + _FORCE_INLINE_ FileAccess *try_open_path(const String& p_path); + _FORCE_INLINE_ bool has_path(const String& p_path); + + PackedData(); +}; + +class PackSource { + +public: + + virtual bool try_open_pack(const String& p_path)=0; + virtual FileAccess* get_file(const String& p_path, PackedData::PackedFile* p_file)=0; +}; + +class PackedSourcePCK : public PackSource { + +public: + + virtual bool try_open_pack(const String &p_path); + virtual FileAccess* get_file(const String& p_path, PackedData::PackedFile* p_file); +}; + + +class FileAccessPack : public FileAccess { + + PackedData::PackedFile pf; + + mutable size_t pos; + mutable bool eof; + + FileAccess *f; + virtual Error _open(const String& p_path, int p_mode_flags); + virtual uint64_t _get_modified_time(const String& p_file) { return 0; } + +public: + + + virtual void close(); + virtual bool is_open() const; + + virtual void seek(size_t p_position); + virtual void seek_end(int64_t p_position=0); + virtual size_t get_pos() const; + virtual size_t get_len() const; + + virtual bool eof_reached() const; + + virtual uint8_t get_8() const; + + + virtual int get_buffer(uint8_t *p_dst,int p_length) const; + + virtual void set_endian_swap(bool p_swap); + + virtual Error get_error() const; + + virtual void store_8(uint8_t p_dest); + + virtual void store_buffer(const uint8_t *p_src,int p_length); + + virtual bool file_exists(const String& p_name); + + + FileAccessPack(const String& p_path, const PackedData::PackedFile& p_file); + ~FileAccessPack(); +}; + + +FileAccess *PackedData::try_open_path(const String& p_path) { + + if (files.has(p_path)) { + return files[p_path].src->get_file(p_path, &files[p_path]); + } + + return NULL; +} + +bool PackedData::has_path(const String& p_path) { + + return files.has(p_path); +} + + +class DirAccessPack : public DirAccess { + + + PackedData::PackedDir *current; + + List list_dirs; + List list_files; + bool cdir; + +public: + + virtual bool list_dir_begin(); + virtual String get_next(); + virtual bool current_is_dir() const; + virtual void list_dir_end(); + + virtual int get_drive_count(); + virtual String get_drive(int p_drive); + + virtual Error change_dir(String p_dir); + virtual String get_current_dir(); + + + virtual bool file_exists(String p_file); + + virtual Error make_dir(String p_dir); + + virtual Error rename(String p_from, String p_to); + virtual Error remove(String p_name); + + size_t get_space_left(); + + DirAccessPack(); + ~DirAccessPack(); + +}; + + +#endif // FILE_ACCESS_PACK_H diff --git a/core/io/file_access_zip.cpp b/core/io/file_access_zip.cpp new file mode 100644 index 00000000000..72e929619f1 --- /dev/null +++ b/core/io/file_access_zip.cpp @@ -0,0 +1,367 @@ +/*************************************************************************/ +/* file_access_zip.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. */ +/*************************************************************************/ +#ifdef MINIZIP_ENABLED + +#include "file_access_zip.h" + +#include "core/os/file_access.h" + +ZipArchive* ZipArchive::instance = NULL; + +extern "C" { + +static void* godot_open(void* data, const char* p_fname, int mode) { + + if (mode & ZLIB_FILEFUNC_MODE_WRITE) { + return NULL; + }; + + FileAccess* f = (FileAccess*)data; + f->open(p_fname, FileAccess::READ); + + return f->is_open()?data:NULL; + +}; + +static uLong godot_read(void* data, void* fdata, void* buf, uLong size) { + + FileAccess* f = (FileAccess*)data; + f->get_buffer((uint8_t*)buf, size); + return size; +}; + +static uLong godot_write(voidpf opaque, voidpf stream, const void* buf, uLong size) { + + return 0; +}; + + +static long godot_tell (voidpf opaque, voidpf stream) { + + FileAccess* f = (FileAccess*)opaque; + return f->get_pos(); +}; + +static long godot_seek(voidpf opaque, voidpf stream, uLong offset, int origin) { + + FileAccess* f = (FileAccess*)opaque; + + int pos = offset; + switch (origin) { + + case ZLIB_FILEFUNC_SEEK_CUR: + pos = f->get_pos() + offset; + break; + case ZLIB_FILEFUNC_SEEK_END: + pos = f->get_len() + offset; + break; + default: + break; + }; + + f->seek(pos); + return 0; +}; + + +static int godot_close(voidpf opaque, voidpf stream) { + + FileAccess* f = (FileAccess*)opaque; + f->close(); + return 0; +}; + +static int godot_testerror(voidpf opaque, voidpf stream) { + + FileAccess* f = (FileAccess*)opaque; + return f->get_error()!=OK?1:0; +}; + + +}; + + +void ZipArchive::close_handle(unzFile p_file) const { + + ERR_FAIL_COND(!p_file); + FileAccess* f = (FileAccess*)unzGetOpaque(p_file); + unzCloseCurrentFile(p_file); + unzClose(p_file); + memdelete(f); +}; + +unzFile ZipArchive::get_file_handle(String p_file) const { + + ERR_FAIL_COND_V(!file_exists(p_file), NULL); + File file = files[p_file]; + + FileAccess* f = FileAccess::open(packages[file.package].filename, FileAccess::READ); + ERR_FAIL_COND_V(!f, NULL); + + zlib_filefunc_def io; + + io.opaque = f; + io.zopen_file = godot_open; + io.zread_file = godot_read; + io.zwrite_file = godot_write; + + io.ztell_file = godot_tell; + io.zseek_file = godot_seek; + io.zclose_file = godot_close; + io.zerror_file = godot_testerror; + + unzFile pkg = unzOpen2(packages[file.package].filename.utf8().get_data(), &io); + ERR_FAIL_COND_V(!pkg, NULL); + unzGoToFilePos(pkg, &file.file_pos); + if (unzOpenCurrentFile(pkg) != UNZ_OK) { + + unzClose(pkg); + ERR_FAIL_V(NULL); + }; + + return pkg; +}; + +bool ZipArchive::try_open_pack(const String& p_name) { + + printf("opening pack %ls, %i, %i\n", p_name.c_str(), p_name.extension().nocasecmp_to("zip"), p_name.extension().nocasecmp_to("pcz")); + if (p_name.extension().nocasecmp_to("zip") != 0 && p_name.extension().nocasecmp_to("pcz") != 0) + return false; + + zlib_filefunc_def io; + + FileAccess* f = FileAccess::open(p_name, FileAccess::READ); + if (!f) + return false; + io.opaque = f; + io.zopen_file = godot_open; + io.zread_file = godot_read; + io.zwrite_file = godot_write; + + io.ztell_file = godot_tell; + io.zseek_file = godot_seek; + io.zclose_file = godot_close; + io.zerror_file = godot_testerror; + + unzFile zfile = unzOpen2(p_name.utf8().get_data(), &io); + ERR_FAIL_COND_V(!zfile, false); + + unz_global_info64 gi; + int err = unzGetGlobalInfo64(zfile, &gi); + ERR_FAIL_COND_V(err!=UNZ_OK, false); + + Package pkg; + pkg.filename = p_name; + pkg.zfile = zfile; + packages.push_back(pkg); + int pkg_num = packages.size()-1; + + for (unsigned int i=0;iadd_path(p_name, fname, 0, 0, this); + + if ((i+1)get_file_handle(p_path); + ERR_FAIL_COND_V(!zfile, FAILED); + + int err = unzGetCurrentFileInfo64(zfile,&file_info,NULL,0,NULL,0,NULL,0); + ERR_FAIL_COND_V(err != UNZ_OK, FAILED); + + return OK; +}; + +void FileAccessZip::close() { + + if (!zfile) + return; + + ZipArchive* arch = ZipArchive::get_singleton(); + ERR_FAIL_COND(!arch); + arch->close_handle(zfile); + zfile = NULL; +}; + +bool FileAccessZip::is_open() const { + + return zfile != NULL; +}; + +void FileAccessZip::seek(size_t p_position) { + + ERR_FAIL_COND(!zfile); + unzSeekCurrentFile(zfile, p_position); +}; + +void FileAccessZip::seek_end(int64_t p_position) { + + ERR_FAIL_COND(!zfile); + unzSeekCurrentFile(zfile, get_len() + p_position); +}; + +size_t FileAccessZip::get_pos() const { + + ERR_FAIL_COND_V(!zfile, 0); + return unztell(zfile); +}; + +size_t FileAccessZip::get_len() const { + + ERR_FAIL_COND_V(!zfile, 0); + return file_info.uncompressed_size; +}; + +bool FileAccessZip::eof_reached() const { + + ERR_FAIL_COND_V(!zfile, true); + + return at_eof; +}; + +uint8_t FileAccessZip::get_8() const { + + uint8_t ret = 0; + get_buffer(&ret, 1); + return ret; +}; + +int FileAccessZip::get_buffer(uint8_t *p_dst,int p_length) const { + + ERR_FAIL_COND_V(!zfile, -1); + at_eof = unzeof(zfile); + if (at_eof) + return 0; + int read = unzReadCurrentFile(zfile, p_dst, p_length); + ERR_FAIL_COND_V(read < 0, read); + if (read < p_length) + at_eof = true; + return read; +}; + +Error FileAccessZip::get_error() const { + + if (!zfile) { + + return ERR_UNCONFIGURED; + }; + if (eof_reached()) { + return ERR_FILE_EOF; + }; + + return OK; +}; + +void FileAccessZip::store_8(uint8_t p_dest) { + + ERR_FAIL(); +}; + +bool FileAccessZip::file_exists(const String& p_name) { + + return false; +}; + + +FileAccessZip::FileAccessZip(const String& p_path, const PackedData::PackedFile& p_file) { + + zfile = NULL; + _open(p_path, FileAccess::READ); +}; + +FileAccessZip::~FileAccessZip() { + + close(); +}; + +#endif diff --git a/core/io/file_access_zip.h b/core/io/file_access_zip.h new file mode 100644 index 00000000000..88272e6cfc5 --- /dev/null +++ b/core/io/file_access_zip.h @@ -0,0 +1,125 @@ +/*************************************************************************/ +/* file_access_zip.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. */ +/*************************************************************************/ +#ifdef MINIZIP_ENABLED + +#ifndef FILE_ACCESS_Zip_H +#define FILE_ACCESS_Zip_H + +#include +#include "core/io/file_access_pack.h" +#include "unzip.h" +#include "map.h" + +class ZipArchive : public PackSource { + +public: + + struct File { + + int package; + unz_file_pos file_pos; + File() { + + package = -1; + }; + }; + + +private: + + struct Package { + String filename; + unzFile zfile; + }; + Vector packages; + + Map files; + + static ZipArchive* instance; + + FileAccess::CreateFunc fa_create_func; + +public: + + void close_handle(unzFile p_file) const; + unzFile get_file_handle(String p_file) const; + + Error add_package(String p_name); + + bool file_exists(String p_name) const; + + virtual bool try_open_pack(const String& p_path); + FileAccess* get_file(const String& p_path, PackedData::PackedFile* p_file); + + static ZipArchive* get_singleton(); + + ZipArchive(); + ~ZipArchive(); +}; + + +class FileAccessZip : public FileAccess { + + unzFile zfile; + unz_file_info64 file_info; + + mutable bool at_eof; + + ZipArchive* archive; + +public: + + virtual Error _open(const String& p_path, int p_mode_flags); ///< open a file + virtual void close(); ///< close a file + virtual bool is_open() const; ///< true when file is open + + virtual void seek(size_t p_position); ///< seek to a given position + virtual void seek_end(int64_t p_position=0); ///< seek from the end of file + virtual size_t get_pos() const; ///< get position in the file + virtual size_t get_len() const; ///< get size of the file + + virtual bool eof_reached() const; ///< reading passed EOF + + virtual uint8_t get_8() const; ///< get a byte + virtual int get_buffer(uint8_t *p_dst,int p_length) const; + + virtual Error get_error() const; ///< get last error + + virtual void store_8(uint8_t p_dest); ///< store a byte + virtual bool file_exists(const String& p_name); ///< return true if a file exists + + virtual uint64_t _get_modified_time(const String& p_file) { return 0; } // todo + + FileAccessZip(const String& p_path, const PackedData::PackedFile& p_file); + ~FileAccessZip(); +}; + +#endif // FILE_ACCESS_ZIP_H + +#endif diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp new file mode 100644 index 00000000000..33c5f23f6dc --- /dev/null +++ b/core/io/http_client.cpp @@ -0,0 +1,641 @@ +/*************************************************************************/ +/* http_client.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 "http_client.h" + + +Error HTTPClient::connect_url(const String& p_url) { + + return OK; +} + +Error HTTPClient::connect(const String &p_host,int p_port){ + + close(); + conn_port=p_port; + conn_host=p_host; + + if (conn_host.begins_with("http://")) { + + conn_host=conn_host.replace_first("http://",""); + } else if (conn_host.begins_with("https://")) { + //use https + conn_host=conn_host.replace_first("https://",""); + } + + + connection=tcp_connection; + if (conn_host.is_valid_ip_address()) { + //is ip + Error err = tcp_connection->connect(IP_Address(conn_host),p_port); + if (err) { + status=STATUS_CANT_CONNECT; + return err; + } + + status=STATUS_CONNECTING; + } else { + //is hostname + resolving=IP::get_singleton()->resolve_hostname_queue_item(conn_host); + status=STATUS_RESOLVING; + + } + + return OK; +} + + +void HTTPClient::set_connection(const Ref& p_connection){ + + close(); + connection=p_connection; + +} + + +Error HTTPClient::request( Method p_method, const String& p_url, const Vector& p_headers,const String& p_body) { + + ERR_FAIL_INDEX_V(p_method,METHOD_MAX,ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(status!=STATUS_CONNECTED,ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(connection.is_null(),ERR_INVALID_DATA); + + + static const char* _methods[METHOD_MAX]={ + "GET", + "HEAD", + "POST", + "PUT", + "DELETE", + "OPTIONS", + "TRACE", + "CONNECT"}; + + String request=String(_methods[p_method])+" "+p_url+" HTTP/1.1\r\n"; + request+="Host: "+conn_host+":"+itos(conn_port)+"\r\n"; + for(int i=0;iput_data((const uint8_t*)cs.ptr(),cs.length()); + if (err) { + close(); + status=STATUS_CONNECTION_ERROR; + return err; + } + + status=STATUS_REQUESTING; + + return OK; +} + +Error HTTPClient::send_body_text(const String& p_body){ + + return OK; +} + +Error HTTPClient::send_body_data(const ByteArray& p_body){ + + return OK; +} + +bool HTTPClient::has_response() const { + + return response_headers.size()!=0; +} + +bool HTTPClient::is_response_chunked() const { + + return chunked; +} + +int HTTPClient::get_response_code() const { + + return response_num; +} +Error HTTPClient::get_response_headers(List *r_response) { + + if (!response_headers.size()) + return ERR_INVALID_PARAMETER; + + for(int i=0;ipush_back(response_headers[i]); + } + + response_headers.clear(); + + return OK; +} + + +void HTTPClient::close(){ + + if (tcp_connection->get_status()!=StreamPeerTCP::STATUS_NONE) + tcp_connection->disconnect(); + + connection.unref(); + status=STATUS_DISCONNECTED; + if (resolving!=IP::RESOLVER_INVALID_ID) { + + IP::get_singleton()->erase_resolve_item(resolving); + resolving=IP::RESOLVER_INVALID_ID; + + } + + response_headers.clear(); + response_str.clear(); + body_size=0; + body_left=0; + chunk_left=0; + response_num=0; +} + + +Error HTTPClient::poll(){ + + switch(status) { + + + case STATUS_RESOLVING: { + ERR_FAIL_COND_V(resolving==IP::RESOLVER_INVALID_ID,ERR_BUG); + + IP::ResolverStatus rstatus = IP::get_singleton()->get_resolve_item_status(resolving); + switch(rstatus) { + case IP::RESOLVER_STATUS_WAITING: return OK; //still resolving + + case IP::RESOLVER_STATUS_DONE: { + + IP_Address host = IP::get_singleton()->get_resolve_item_address(resolving); + Error err = tcp_connection->connect(host,conn_port); + IP::get_singleton()->erase_resolve_item(resolving); + resolving=IP::RESOLVER_INVALID_ID; + if (err) { + status=STATUS_CANT_CONNECT; + return err; + } + + status=STATUS_CONNECTING; + } break; + case IP::RESOLVER_STATUS_NONE: + case IP::RESOLVER_STATUS_ERROR: { + + IP::get_singleton()->erase_resolve_item(resolving); + resolving=IP::RESOLVER_INVALID_ID; + close(); + status=STATUS_CANT_RESOLVE; + return ERR_CANT_RESOLVE; + } break; + + } + } break; + case STATUS_CONNECTING: { + + StreamPeerTCP::Status s = tcp_connection->get_status(); + switch(s) { + + case StreamPeerTCP::STATUS_CONNECTING: { + return OK; //do none + } break; + case StreamPeerTCP::STATUS_CONNECTED: { + status=STATUS_CONNECTED; + return OK; + } break; + case StreamPeerTCP::STATUS_ERROR: + case StreamPeerTCP::STATUS_NONE: { + + close(); + status=STATUS_CANT_CONNECT; + return ERR_CANT_CONNECT; + } break; + } + } break; + case STATUS_CONNECTED: { + //request something please + return OK; + } break; + case STATUS_REQUESTING: { + + + while(true) { + uint8_t byte; + int rec=0; + Error err = connection->get_partial_data(&byte,1,rec); + if (err!=OK) { + close(); + status=STATUS_CONNECTION_ERROR; + return ERR_CONNECTION_ERROR; + } + + if (rec==0) + return OK; //keep trying! + + response_str.push_back(byte); + int rs = response_str.size(); + if ( + (rs>=2 && response_str[rs-2]=='\n' && response_str[rs-1]=='\n') || + (rs>=4 && response_str[rs-4]=='\r' && response_str[rs-3]=='\n' && rs>=4 && response_str[rs-2]=='\r' && response_str[rs-1]=='\n') + ) { + + + //end of response, parse. + response_str.push_back(0); + String response; + response.parse_utf8((const char*)response_str.ptr()); + print_line("END OF RESPONSE? :\n"+response+"\n------"); + Vector responses = response.split("\n"); + body_size=0; + chunked=false; + body_left=0; + chunk_left=0; + response_headers.clear(); + response_num = RESPONSE_OK; + + for(int i=0;i rh; + get_response_headers(&rh); + Dictionary ret; + for(const List::Element *E=rh.front();E;E=E->next()) { + String s = E->get(); + int sp = s.find(":"); + if (sp==-1) + continue; + String key = s.substr(0,sp).strip_edges(); + String value = s.substr(sp+1,s.length()).strip_edges(); + ret[key]=value; + + } + + return ret; +} + +StringArray HTTPClient::_get_response_headers() { + + List rh; + get_response_headers(&rh); + StringArray ret; + ret.resize(rh.size()); + int idx=0; + for(const List::Element *E=rh.front();E;E=E->next()) { + ret.set(idx++,E->get()); + } + + return ret; +} + +int HTTPClient::get_response_body_length() const { + + return body_size; +} + +ByteArray HTTPClient::read_response_body_chunk() { + + ERR_FAIL_COND_V( status !=STATUS_BODY, ByteArray() ); + + Error err=OK; + + if (chunked) { + + while(true) { + + if (chunk_left==0) { + //reading len + uint8_t b; + int rec=0; + err = connection->get_partial_data(&b,1,rec); + + if (rec==0) + break; + + chunk.push_back(b); + + if (chunk.size()>32) { + ERR_PRINT("HTTP Invalid chunk hex len"); + status=STATUS_CONNECTION_ERROR; + return ByteArray(); + } + + if (chunk.size()>2 && chunk[chunk.size()-2]=='\r' && chunk[chunk.size()-1]=='\n') { + + int len=0; + for(int i=0;i='0' && c<='9') + v=c-'0'; + else if (c>='a' && c<='f') + v=c-'a'+10; + else if (c>='A' && c<='F') + v=c-'A'+10; + else { + ERR_PRINT("HTTP Chunk len not in hex!!"); + status=STATUS_CONNECTION_ERROR; + return ByteArray(); + } + len<<=4; + len|=v; + if (len>(1<<24)) { + ERR_PRINT("HTTP Chunk too big!! >16mb"); + status=STATUS_CONNECTION_ERROR; + return ByteArray(); + } + + } + + if (len==0) { + //end! + status=STATUS_CONNECTED; + chunk.clear(); + return ByteArray(); + } + + chunk_left=len+2; + chunk.resize(chunk_left); + + } + } else { + + int rec=0; + err = connection->get_partial_data(&chunk[chunk.size()-chunk_left],chunk_left,rec); + if (rec==0) { + break; + } + chunk_left-=rec; + + if (chunk_left==0) { + + if (chunk[chunk.size()-2]!='\r' || chunk[chunk.size()-1]!='\n') { + ERR_PRINT("HTTP Invalid chunk terminator (not \\r\\n)"); + status=STATUS_CONNECTION_ERROR; + return ByteArray(); + } + + ByteArray ret; + ret.resize(chunk.size()-2); + { + ByteArray::Write w = ret.write(); + copymem(w.ptr(),chunk.ptr(),chunk.size()-2); + } + chunk.clear(); + + return ret; + + } + + break; + } + } + + } else { + ByteArray::Write r = tmp_read.write(); + int rec=0; + err = connection->get_partial_data(r.ptr(),MIN(body_left,tmp_read.size()),rec); + if (rec>0) { + ByteArray ret; + ret.resize(rec); + ByteArray::Write w = ret.write(); + copymem(w.ptr(),r.ptr(),rec); + body_left-=rec; + if (body_left==0) { + status=STATUS_CONNECTED; + } + return ret; + } + + } + + + if (err!=OK) { + close(); + if (err==ERR_FILE_EOF) { + + status=STATUS_DISCONNECTED; //server disconnected + } else { + + status=STATUS_CONNECTION_ERROR; + } + } else if (body_left==0 && !chunked) { + + status=STATUS_CONNECTED; + } + + return ByteArray(); +} + +HTTPClient::Status HTTPClient::get_status() const { + + + return status; +} + +void HTTPClient::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("connect:Error","host","port"),&HTTPClient::connect); + ObjectTypeDB::bind_method(_MD("set_connection","connection:StreamPeer"),&HTTPClient::set_connection); + ObjectTypeDB::bind_method(_MD("request","method","url","headers","body"),&HTTPClient::request,DEFVAL(String())); + ObjectTypeDB::bind_method(_MD("send_body_text","body"),&HTTPClient::send_body_text); + ObjectTypeDB::bind_method(_MD("send_body_data","body"),&HTTPClient::send_body_data); + ObjectTypeDB::bind_method(_MD("close"),&HTTPClient::close); + + ObjectTypeDB::bind_method(_MD("has_response"),&HTTPClient::has_response); + ObjectTypeDB::bind_method(_MD("is_response_chunked"),&HTTPClient::is_response_chunked); + ObjectTypeDB::bind_method(_MD("get_response_code"),&HTTPClient::get_response_code); + ObjectTypeDB::bind_method(_MD("get_response_headers"),&HTTPClient::_get_response_headers); + ObjectTypeDB::bind_method(_MD("get_response_headers_as_dictionary"),&HTTPClient::_get_response_headers_as_dictionary); + ObjectTypeDB::bind_method(_MD("get_response_body_length"),&HTTPClient::get_response_body_length); + ObjectTypeDB::bind_method(_MD("read_response_body_chunk"),&HTTPClient::read_response_body_chunk); + + ObjectTypeDB::bind_method(_MD("get_status"),&HTTPClient::get_status); + ObjectTypeDB::bind_method(_MD("poll:Error"),&HTTPClient::poll); + + BIND_CONSTANT( METHOD_GET ); + BIND_CONSTANT( METHOD_HEAD ); + BIND_CONSTANT( METHOD_POST ); + BIND_CONSTANT( METHOD_PUT ); + BIND_CONSTANT( METHOD_DELETE ); + BIND_CONSTANT( METHOD_OPTIONS ); + BIND_CONSTANT( METHOD_TRACE ); + BIND_CONSTANT( METHOD_CONNECT ); + BIND_CONSTANT( METHOD_MAX ); + + BIND_CONSTANT( STATUS_DISCONNECTED ); + BIND_CONSTANT( STATUS_RESOLVING ); //resolving hostname (if passed a hostname) + BIND_CONSTANT( STATUS_CANT_RESOLVE ); + BIND_CONSTANT( STATUS_CONNECTING ); //connecting to ip + BIND_CONSTANT( STATUS_CANT_CONNECT ); + BIND_CONSTANT( STATUS_CONNECTED ); //connected ); requests only accepted here + BIND_CONSTANT( STATUS_REQUESTING ); // request in progress + BIND_CONSTANT( STATUS_BODY ); // request resulted in body ); which must be read + BIND_CONSTANT( STATUS_CONNECTION_ERROR ); + + + BIND_CONSTANT( RESPONSE_CONTINUE ); + BIND_CONSTANT( RESPONSE_SWITCHING_PROTOCOLS ); + BIND_CONSTANT( RESPONSE_PROCESSING ); + + // 2xx successful + BIND_CONSTANT( RESPONSE_OK ); + BIND_CONSTANT( RESPONSE_CREATED ); + BIND_CONSTANT( RESPONSE_ACCEPTED ); + BIND_CONSTANT( RESPONSE_NON_AUTHORITATIVE_INFORMATION ); + BIND_CONSTANT( RESPONSE_NO_CONTENT ); + BIND_CONSTANT( RESPONSE_RESET_CONTENT ); + BIND_CONSTANT( RESPONSE_PARTIAL_CONTENT ); + BIND_CONSTANT( RESPONSE_MULTI_STATUS ); + BIND_CONSTANT( RESPONSE_IM_USED ); + + // 3xx redirection + BIND_CONSTANT( RESPONSE_MULTIPLE_CHOICES ); + BIND_CONSTANT( RESPONSE_MOVED_PERMANENTLY ); + BIND_CONSTANT( RESPONSE_FOUND ); + BIND_CONSTANT( RESPONSE_SEE_OTHER ); + BIND_CONSTANT( RESPONSE_NOT_MODIFIED ); + BIND_CONSTANT( RESPONSE_USE_PROXY ); + BIND_CONSTANT( RESPONSE_TEMPORARY_REDIRECT ); + + // 4xx client error + BIND_CONSTANT( RESPONSE_BAD_REQUEST ); + BIND_CONSTANT( RESPONSE_UNAUTHORIZED ); + BIND_CONSTANT( RESPONSE_PAYMENT_REQUIRED ); + BIND_CONSTANT( RESPONSE_FORBIDDEN ); + BIND_CONSTANT( RESPONSE_NOT_FOUND ); + BIND_CONSTANT( RESPONSE_METHOD_NOT_ALLOWED ); + BIND_CONSTANT( RESPONSE_NOT_ACCEPTABLE ); + BIND_CONSTANT( RESPONSE_PROXY_AUTHENTICATION_REQUIRED ); + BIND_CONSTANT( RESPONSE_REQUEST_TIMEOUT ); + BIND_CONSTANT( RESPONSE_CONFLICT ); + BIND_CONSTANT( RESPONSE_GONE ); + BIND_CONSTANT( RESPONSE_LENGTH_REQUIRED ); + BIND_CONSTANT( RESPONSE_PRECONDITION_FAILED ); + BIND_CONSTANT( RESPONSE_REQUEST_ENTITY_TOO_LARGE ); + BIND_CONSTANT( RESPONSE_REQUEST_URI_TOO_LONG ); + BIND_CONSTANT( RESPONSE_UNSUPPORTED_MEDIA_TYPE ); + BIND_CONSTANT( RESPONSE_REQUESTED_RANGE_NOT_SATISFIABLE ); + BIND_CONSTANT( RESPONSE_EXPECTATION_FAILED ); + BIND_CONSTANT( RESPONSE_UNPROCESSABLE_ENTITY ); + BIND_CONSTANT( RESPONSE_LOCKED ); + BIND_CONSTANT( RESPONSE_FAILED_DEPENDENCY ); + BIND_CONSTANT( RESPONSE_UPGRADE_REQUIRED ); + + // 5xx server error + BIND_CONSTANT( RESPONSE_INTERNAL_SERVER_ERROR ); + BIND_CONSTANT( RESPONSE_NOT_IMPLEMENTED ); + BIND_CONSTANT( RESPONSE_BAD_GATEWAY ); + BIND_CONSTANT( RESPONSE_SERVICE_UNAVAILABLE ); + BIND_CONSTANT( RESPONSE_GATEWAY_TIMEOUT ); + BIND_CONSTANT( RESPONSE_HTTP_VERSION_NOT_SUPPORTED ); + BIND_CONSTANT( RESPONSE_INSUFFICIENT_STORAGE ); + BIND_CONSTANT( RESPONSE_NOT_EXTENDED ); + +} + +HTTPClient::HTTPClient(){ + + tcp_connection = StreamPeerTCP::create(); + resolving = IP::RESOLVER_INVALID_ID; + status=STATUS_DISCONNECTED; + conn_port=80; + body_size=0; + chunked=false; + body_left=0; + chunk_left=0; + response_num=0; + + tmp_read.resize(4096); +} + +HTTPClient::~HTTPClient(){ + + +} + + diff --git a/core/io/http_client.h b/core/io/http_client.h new file mode 100644 index 00000000000..2f22ba1fdee --- /dev/null +++ b/core/io/http_client.h @@ -0,0 +1,188 @@ +/*************************************************************************/ +/* http_client.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 HTTP_CLIENT_H +#define HTTP_CLIENT_H + +#include "io/stream_peer.h" +#include "io/stream_peer_tcp.h" +#include "io/ip.h" +#include "reference.h" + + +class HTTPClient : public Reference { + + OBJ_TYPE(HTTPClient,Reference); +public: + + enum RespondeCode { + + // 1xx informational + RESPONSE_CONTINUE = 100, + RESPONSE_SWITCHING_PROTOCOLS = 101, + RESPONSE_PROCESSING = 102, + + // 2xx successful + RESPONSE_OK = 200, + RESPONSE_CREATED = 201, + RESPONSE_ACCEPTED = 202, + RESPONSE_NON_AUTHORITATIVE_INFORMATION = 203, + RESPONSE_NO_CONTENT = 204, + RESPONSE_RESET_CONTENT = 205, + RESPONSE_PARTIAL_CONTENT = 206, + RESPONSE_MULTI_STATUS = 207, + RESPONSE_IM_USED = 226, + + // 3xx redirection + RESPONSE_MULTIPLE_CHOICES = 300, + RESPONSE_MOVED_PERMANENTLY = 301, + RESPONSE_FOUND = 302, + RESPONSE_SEE_OTHER = 303, + RESPONSE_NOT_MODIFIED = 304, + RESPONSE_USE_PROXY = 305, + RESPONSE_TEMPORARY_REDIRECT = 307, + + // 4xx client error + RESPONSE_BAD_REQUEST = 400, + RESPONSE_UNAUTHORIZED = 401, + RESPONSE_PAYMENT_REQUIRED = 402, + RESPONSE_FORBIDDEN = 403, + RESPONSE_NOT_FOUND = 404, + RESPONSE_METHOD_NOT_ALLOWED = 405, + RESPONSE_NOT_ACCEPTABLE = 406, + RESPONSE_PROXY_AUTHENTICATION_REQUIRED = 407, + RESPONSE_REQUEST_TIMEOUT = 408, + RESPONSE_CONFLICT = 409, + RESPONSE_GONE = 410, + RESPONSE_LENGTH_REQUIRED = 411, + RESPONSE_PRECONDITION_FAILED = 412, + RESPONSE_REQUEST_ENTITY_TOO_LARGE = 413, + RESPONSE_REQUEST_URI_TOO_LONG = 414, + RESPONSE_UNSUPPORTED_MEDIA_TYPE = 415, + RESPONSE_REQUESTED_RANGE_NOT_SATISFIABLE = 416, + RESPONSE_EXPECTATION_FAILED = 417, + RESPONSE_UNPROCESSABLE_ENTITY = 422, + RESPONSE_LOCKED = 423, + RESPONSE_FAILED_DEPENDENCY = 424, + RESPONSE_UPGRADE_REQUIRED = 426, + + // 5xx server error + RESPONSE_INTERNAL_SERVER_ERROR = 500, + RESPONSE_NOT_IMPLEMENTED = 501, + RESPONSE_BAD_GATEWAY = 502, + RESPONSE_SERVICE_UNAVAILABLE = 503, + RESPONSE_GATEWAY_TIMEOUT = 504, + RESPONSE_HTTP_VERSION_NOT_SUPPORTED = 505, + RESPONSE_INSUFFICIENT_STORAGE = 507, + RESPONSE_NOT_EXTENDED = 510, + + }; + + enum Method { + + METHOD_GET, + METHOD_HEAD, + METHOD_POST, + METHOD_PUT, + METHOD_DELETE, + METHOD_OPTIONS, + METHOD_TRACE, + METHOD_CONNECT, + METHOD_MAX + }; + + enum Status { + STATUS_DISCONNECTED, + STATUS_RESOLVING, //resolving hostname (if passed a hostname) + STATUS_CANT_RESOLVE, + STATUS_CONNECTING, //connecting to ip + STATUS_CANT_CONNECT, + STATUS_CONNECTED, //connected, requests only accepted here + STATUS_REQUESTING, // request in progress + STATUS_BODY, // request resulted in body, which must be read + STATUS_CONNECTION_ERROR, + }; + +private: + + Status status; + IP::ResolverID resolving; + int conn_port; + String conn_host; + + Vector response_str; + + bool chunked; + Vector chunk; + int chunk_left; + int body_size; + int body_left; + + Ref tcp_connection; + Ref connection; + + int response_num; + Vector response_headers; + + static void _bind_methods(); + StringArray _get_response_headers(); + Dictionary _get_response_headers_as_dictionary(); + ByteArray tmp_read; +public: + + + Error connect_url(const String& p_url); //connects to a full url and perform request + Error connect(const String &p_host,int p_port); + + void set_connection(const Ref& p_connection); + + Error request( Method p_method, const String& p_url, const Vector& p_headers,const String& p_body=String()); + Error send_body_text(const String& p_body); + Error send_body_data(const ByteArray& p_body); + + void close(); + + Status get_status() const; + + bool has_response() const; + bool is_response_chunked() const; + int get_response_code() const; + Error get_response_headers(List *r_response); + int get_response_body_length() const; + + ByteArray read_response_body_chunk(); // can't get body as partial text because of most encodings UTF8, gzip, etc. + + Error poll(); + + HTTPClient(); + ~HTTPClient(); +}; + +VARIANT_ENUM_CAST(HTTPClient::Method); + +#endif // HTTP_CLIENT_H diff --git a/core/io/image_loader.cpp b/core/io/image_loader.cpp new file mode 100644 index 00000000000..180df8964be --- /dev/null +++ b/core/io/image_loader.cpp @@ -0,0 +1,112 @@ +/*************************************************************************/ +/* image_loader.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 "image_loader.h" + +#include "print_string.h" +bool ImageFormatLoader::recognize(const String& p_extension) const { + + + List extensions; + get_recognized_extensions(&extensions); + for (List::Element *E=extensions.front();E;E=E->next()) { + + if (E->get().nocasecmp_to(p_extension.extension())==0) + return true; + } + + return false; +} + +Error ImageLoader::load_image(String p_file,Image *p_image, FileAccess *p_custom) { + + + FileAccess *f=p_custom; + if (!f) { + Error err; + f=FileAccess::open(p_file,FileAccess::READ,&err); + if (!f) + return err; + } + + String extension = p_file.extension(); + + + for (int i=0;irecognize(extension)) + continue; + Error err = loader[i]->load_image(p_image,f); + if (err!=ERR_FILE_UNRECOGNIZED) { + if (!p_custom) + memdelete(f); + return err; + } + + + } + + if (!p_custom) + memdelete(f); + + return ERR_FILE_UNRECOGNIZED; + +} + +void ImageLoader::get_recognized_extensions(List *p_extensions) { + + for (int i=0;iget_recognized_extensions(p_extensions); + + } +} + +bool ImageLoader::recognize(const String& p_extension) { + + for (int i=0;irecognize(p_extension)) + return true; + + } + + return false; +} + +ImageFormatLoader *ImageLoader::loader[MAX_LOADERS]; +int ImageLoader::loader_count=0; + +void ImageLoader::add_image_format_loader(ImageFormatLoader *p_loader) { + + ERR_FAIL_COND(loader_count >=MAX_LOADERS ); + loader[loader_count++]=p_loader; +} + + + diff --git a/core/io/image_loader.h b/core/io/image_loader.h new file mode 100644 index 00000000000..665cc0b460a --- /dev/null +++ b/core/io/image_loader.h @@ -0,0 +1,91 @@ +/*************************************************************************/ +/* image_loader.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 IMAGE_LOADER_H +#define IMAGE_LOADER_H + +#include "image.h" +#include "ustring.h" +#include "os/file_access.h" +#include "list.h" +/** + @author Juan Linietsky +*/ + + +/** + * @class ImageScanLineLoader + * @author Juan Linietsky + * + + */ +class ImageLoader; + + +/** + * @class ImageLoader + * Base Class and singleton for loading images from disk + * Can load images in one go, or by scanline + */ + + +class ImageFormatLoader { +friend class ImageLoader; +protected: + virtual Error load_image(Image *p_image,FileAccess *p_fileaccess)=0; + virtual void get_recognized_extensions(List *p_extensions) const=0; + bool recognize(const String& p_extension) const; + + +public: + virtual ~ImageFormatLoader() {} +}; + +class ImageLoader { + + enum { + MAX_LOADERS=8 + }; + + static ImageFormatLoader *loader[MAX_LOADERS]; + static int loader_count; + +protected: + + +public: + + static Error load_image(String p_file,Image *p_image, FileAccess *p_custom=NULL); + static void get_recognized_extensions(List *p_extensions) ; + static bool recognize(const String& p_extension) ; + + static void add_image_format_loader(ImageFormatLoader *p_loader); + +}; + +#endif diff --git a/core/io/ioapi.c b/core/io/ioapi.c new file mode 100644 index 00000000000..cc49d775b91 --- /dev/null +++ b/core/io/ioapi.c @@ -0,0 +1,235 @@ +/* ioapi.h -- IO base function header for compress/uncompress .zip + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications for Zip64 support + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + +*/ + +#if (defined(_WIN32)) + #define _CRT_SECURE_NO_WARNINGS +#endif + +#include "ioapi.h" + +voidpf call_zopen64 (const zlib_filefunc64_32_def* pfilefunc,const void*filename,int mode) +{ + if (pfilefunc->zfile_func64.zopen64_file != NULL) + return (*(pfilefunc->zfile_func64.zopen64_file)) (pfilefunc->zfile_func64.opaque,filename,mode); + else + { + return (*(pfilefunc->zopen32_file))(pfilefunc->zfile_func64.opaque,(const char*)filename,mode); + } +} + +long call_zseek64 (const zlib_filefunc64_32_def* pfilefunc,voidpf filestream, ZPOS64_T offset, int origin) +{ + if (pfilefunc->zfile_func64.zseek64_file != NULL) + return (*(pfilefunc->zfile_func64.zseek64_file)) (pfilefunc->zfile_func64.opaque,filestream,offset,origin); + else + { + uLong offsetTruncated = (uLong)offset; + if (offsetTruncated != offset) + return -1; + else + return (*(pfilefunc->zseek32_file))(pfilefunc->zfile_func64.opaque,filestream,offsetTruncated,origin); + } +} + +ZPOS64_T call_ztell64 (const zlib_filefunc64_32_def* pfilefunc,voidpf filestream) +{ + if (pfilefunc->zfile_func64.zseek64_file != NULL) + return (*(pfilefunc->zfile_func64.ztell64_file)) (pfilefunc->zfile_func64.opaque,filestream); + else + { + uLong tell_uLong = (*(pfilefunc->ztell32_file))(pfilefunc->zfile_func64.opaque,filestream); + if ((tell_uLong) == ((uLong)-1)) + return (ZPOS64_T)-1; + else + return tell_uLong; + } +} + +void fill_zlib_filefunc64_32_def_from_filefunc32(zlib_filefunc64_32_def* p_filefunc64_32,const zlib_filefunc_def* p_filefunc32) +{ + p_filefunc64_32->zfile_func64.zopen64_file = NULL; + p_filefunc64_32->zopen32_file = p_filefunc32->zopen_file; + p_filefunc64_32->zfile_func64.zerror_file = p_filefunc32->zerror_file; + p_filefunc64_32->zfile_func64.zread_file = p_filefunc32->zread_file; + p_filefunc64_32->zfile_func64.zwrite_file = p_filefunc32->zwrite_file; + p_filefunc64_32->zfile_func64.ztell64_file = NULL; + p_filefunc64_32->zfile_func64.zseek64_file = NULL; + p_filefunc64_32->zfile_func64.zclose_file = p_filefunc32->zclose_file; + p_filefunc64_32->zfile_func64.zerror_file = p_filefunc32->zerror_file; + p_filefunc64_32->zfile_func64.opaque = p_filefunc32->opaque; + p_filefunc64_32->zseek32_file = p_filefunc32->zseek_file; + p_filefunc64_32->ztell32_file = p_filefunc32->ztell_file; +} + +/* + + +static voidpf ZCALLBACK fopen_file_func OF((voidpf opaque, const char* filename, int mode)); +static uLong ZCALLBACK fread_file_func OF((voidpf opaque, voidpf stream, void* buf, uLong size)); +static uLong ZCALLBACK fwrite_file_func OF((voidpf opaque, voidpf stream, const void* buf,uLong size)); +static ZPOS64_T ZCALLBACK ftell64_file_func OF((voidpf opaque, voidpf stream)); +static long ZCALLBACK fseek64_file_func OF((voidpf opaque, voidpf stream, ZPOS64_T offset, int origin)); +static int ZCALLBACK fclose_file_func OF((voidpf opaque, voidpf stream)); +static int ZCALLBACK ferror_file_func OF((voidpf opaque, voidpf stream)); + +static voidpf ZCALLBACK fopen_file_func (voidpf opaque, const char* filename, int mode) +{ + FILE* file = NULL; + const char* mode_fopen = NULL; + if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ) + mode_fopen = "rb"; + else + if (mode & ZLIB_FILEFUNC_MODE_EXISTING) + mode_fopen = "r+b"; + else + if (mode & ZLIB_FILEFUNC_MODE_CREATE) + mode_fopen = "wb"; + + if ((filename!=NULL) && (mode_fopen != NULL)) + file = fopen(filename, mode_fopen); + return file; +} + +static voidpf ZCALLBACK fopen64_file_func (voidpf opaque, const void* filename, int mode) +{ + FILE* file = NULL; + const char* mode_fopen = NULL; + if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ) + mode_fopen = "rb"; + else + if (mode & ZLIB_FILEFUNC_MODE_EXISTING) + mode_fopen = "r+b"; + else + if (mode & ZLIB_FILEFUNC_MODE_CREATE) + mode_fopen = "wb"; + + if ((filename!=NULL) && (mode_fopen != NULL)) + file = fopen64((const char*)filename, mode_fopen); + return file; +} + +static uLong ZCALLBACK fread_file_func (voidpf opaque, voidpf stream, void* buf, uLong size) +{ + uLong ret; + ret = (uLong)fread(buf, 1, (size_t)size, (FILE *)stream); + return ret; +} + +static uLong ZCALLBACK fwrite_file_func (voidpf opaque, voidpf stream, const void* buf, uLong size) +{ + uLong ret; + ret = (uLong)fwrite(buf, 1, (size_t)size, (FILE *)stream); + return ret; +} + +static long ZCALLBACK ftell_file_func (voidpf opaque, voidpf stream) +{ + long ret; + ret = ftell((FILE *)stream); + return ret; +} + +static ZPOS64_T ZCALLBACK ftell64_file_func (voidpf opaque, voidpf stream) +{ + ZPOS64_T ret; + ret = ftello64((FILE *)stream); + return ret; +} + +static long ZCALLBACK fseek_file_func (voidpf opaque, voidpf stream, uLong offset, int origin) +{ + int fseek_origin=0; + long ret; + switch (origin) + { + case ZLIB_FILEFUNC_SEEK_CUR : + fseek_origin = SEEK_CUR; + break; + case ZLIB_FILEFUNC_SEEK_END : + fseek_origin = SEEK_END; + break; + case ZLIB_FILEFUNC_SEEK_SET : + fseek_origin = SEEK_SET; + break; + default: return -1; + } + ret = 0; + if (fseek((FILE *)stream, offset, fseek_origin) != 0) + ret = -1; + return ret; +} + +static long ZCALLBACK fseek64_file_func (voidpf opaque, voidpf stream, ZPOS64_T offset, int origin) +{ + int fseek_origin=0; + long ret; + switch (origin) + { + case ZLIB_FILEFUNC_SEEK_CUR : + fseek_origin = SEEK_CUR; + break; + case ZLIB_FILEFUNC_SEEK_END : + fseek_origin = SEEK_END; + break; + case ZLIB_FILEFUNC_SEEK_SET : + fseek_origin = SEEK_SET; + break; + default: return -1; + } + ret = 0; + + if(fseeko64((FILE *)stream, offset, fseek_origin) != 0) + ret = -1; + + return ret; +} + + +static int ZCALLBACK fclose_file_func (voidpf opaque, voidpf stream) +{ + int ret; + ret = fclose((FILE *)stream); + return ret; +} + +static int ZCALLBACK ferror_file_func (voidpf opaque, voidpf stream) +{ + int ret; + ret = ferror((FILE *)stream); + return ret; +} + +void fill_fopen_filefunc (pzlib_filefunc_def) + zlib_filefunc_def* pzlib_filefunc_def; +{ + pzlib_filefunc_def->zopen_file = fopen_file_func; + pzlib_filefunc_def->zread_file = fread_file_func; + pzlib_filefunc_def->zwrite_file = fwrite_file_func; + pzlib_filefunc_def->ztell_file = ftell_file_func; + pzlib_filefunc_def->zseek_file = fseek_file_func; + pzlib_filefunc_def->zclose_file = fclose_file_func; + pzlib_filefunc_def->zerror_file = ferror_file_func; + pzlib_filefunc_def->opaque = NULL; +} + +void fill_fopen64_filefunc (zlib_filefunc64_def* pzlib_filefunc_def) +{ + pzlib_filefunc_def->zopen64_file = fopen64_file_func; + pzlib_filefunc_def->zread_file = fread_file_func; + pzlib_filefunc_def->zwrite_file = fwrite_file_func; + pzlib_filefunc_def->ztell64_file = ftell64_file_func; + pzlib_filefunc_def->zseek64_file = fseek64_file_func; + pzlib_filefunc_def->zclose_file = fclose_file_func; + pzlib_filefunc_def->zerror_file = ferror_file_func; + pzlib_filefunc_def->opaque = NULL; +} +*/ diff --git a/core/io/ioapi.h b/core/io/ioapi.h new file mode 100644 index 00000000000..53d01d65c54 --- /dev/null +++ b/core/io/ioapi.h @@ -0,0 +1,199 @@ +/* this file is part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications for Zip64 support + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + + Changes + + Oct-2009 - Defined ZPOS64_T to fpos_t on windows and u_int64_t on linux. (might need to find a better why for this) + Oct-2009 - Change to fseeko64, ftello64 and fopen64 so large files would work on linux. + More if/def section may be needed to support other platforms + Oct-2009 - Defined fxxxx64 calls to normal fopen/ftell/fseek so they would compile on windows. + (but you should use iowin32.c for windows instead) + +*/ + +#ifndef _ZLIBIOAPI64_H +#define _ZLIBIOAPI64_H + +#if (!defined(_WIN32)) && (!defined(WIN32)) + + // Linux needs this to support file operation on files larger then 4+GB + // But might need better if/def to select just the platforms that needs them. + + #ifndef __USE_FILE_OFFSET64 + #define __USE_FILE_OFFSET64 + #endif + #ifndef __USE_LARGEFILE64 + #define __USE_LARGEFILE64 + #endif + #ifndef _LARGEFILE64_SOURCE + #define _LARGEFILE64_SOURCE + #endif + #ifndef _FILE_OFFSET_BIT + #define _FILE_OFFSET_BIT 64 + #endif +#endif + +#include +#include +#include "zlib.h" + +#if defined(USE_FILE32API) +#define fopen64 fopen +#define ftello64 ftell +#define fseeko64 fseek +#else +#ifdef _MSC_VER + #define fopen64 fopen + #if (_MSC_VER >= 1400) && (!(defined(NO_MSCVER_FILE64_FUNC))) + #define ftello64 _ftelli64 + #define fseeko64 _fseeki64 + #else // old MSC + #define ftello64 ftell + #define fseeko64 fseek + #endif +#endif +#endif + +/* +#ifndef ZPOS64_T + #ifdef _WIN32 + #define ZPOS64_T fpos_t + #else + #include + #define ZPOS64_T uint64_t + #endif +#endif +*/ + +#ifdef HAVE_MINIZIP64_CONF_H +#include "mz64conf.h" +#endif + +/* a type choosen by DEFINE */ +#ifdef HAVE_64BIT_INT_CUSTOM +typedef 64BIT_INT_CUSTOM_TYPE ZPOS64_T; +#else +#ifdef HAS_STDINT_H +#include "stdint.h" +typedef uint64_t ZPOS64_T; +#else + + +#if defined(_MSC_VER) || defined(__BORLANDC__) +typedef unsigned __int64 ZPOS64_T; +#else +typedef unsigned long long int ZPOS64_T; +#endif +#endif +#endif + + + +#ifdef __cplusplus +extern "C" { +#endif + + +#define ZLIB_FILEFUNC_SEEK_CUR (1) +#define ZLIB_FILEFUNC_SEEK_END (2) +#define ZLIB_FILEFUNC_SEEK_SET (0) + +#define ZLIB_FILEFUNC_MODE_READ (1) +#define ZLIB_FILEFUNC_MODE_WRITE (2) +#define ZLIB_FILEFUNC_MODE_READWRITEFILTER (3) + +#define ZLIB_FILEFUNC_MODE_EXISTING (4) +#define ZLIB_FILEFUNC_MODE_CREATE (8) + + +#ifndef ZCALLBACK + #if (defined(WIN32) || defined(_WIN32) || defined (WINDOWS) || defined (_WINDOWS)) && defined(CALLBACK) && defined (USEWINDOWS_CALLBACK) + #define ZCALLBACK CALLBACK + #else + #define ZCALLBACK + #endif +#endif + + + + +typedef voidpf (ZCALLBACK *open_file_func) OF((voidpf opaque, const char* filename, int mode)); +typedef uLong (ZCALLBACK *read_file_func) OF((voidpf opaque, voidpf stream, void* buf, uLong size)); +typedef uLong (ZCALLBACK *write_file_func) OF((voidpf opaque, voidpf stream, const void* buf, uLong size)); +typedef int (ZCALLBACK *close_file_func) OF((voidpf opaque, voidpf stream)); +typedef int (ZCALLBACK *testerror_file_func) OF((voidpf opaque, voidpf stream)); + +typedef long (ZCALLBACK *tell_file_func) OF((voidpf opaque, voidpf stream)); +typedef long (ZCALLBACK *seek_file_func) OF((voidpf opaque, voidpf stream, uLong offset, int origin)); + + +/* here is the "old" 32 bits structure structure */ +typedef struct zlib_filefunc_def_s +{ + open_file_func zopen_file; + read_file_func zread_file; + write_file_func zwrite_file; + tell_file_func ztell_file; + seek_file_func zseek_file; + close_file_func zclose_file; + testerror_file_func zerror_file; + voidpf opaque; +} zlib_filefunc_def; + +typedef ZPOS64_T (ZCALLBACK *tell64_file_func) OF((voidpf opaque, voidpf stream)); +typedef long (ZCALLBACK *seek64_file_func) OF((voidpf opaque, voidpf stream, ZPOS64_T offset, int origin)); +typedef voidpf (ZCALLBACK *open64_file_func) OF((voidpf opaque, const void* filename, int mode)); + +typedef struct zlib_filefunc64_def_s +{ + open64_file_func zopen64_file; + read_file_func zread_file; + write_file_func zwrite_file; + tell64_file_func ztell64_file; + seek64_file_func zseek64_file; + close_file_func zclose_file; + testerror_file_func zerror_file; + voidpf opaque; +} zlib_filefunc64_def; + +void fill_fopen64_filefunc OF((zlib_filefunc64_def* pzlib_filefunc_def)); +void fill_fopen_filefunc OF((zlib_filefunc_def* pzlib_filefunc_def)); + +/* now internal definition, only for zip.c and unzip.h */ +typedef struct zlib_filefunc64_32_def_s +{ + zlib_filefunc64_def zfile_func64; + open_file_func zopen32_file; + tell_file_func ztell32_file; + seek_file_func zseek32_file; +} zlib_filefunc64_32_def; + + +#define ZREAD64(filefunc,filestream,buf,size) ((*((filefunc).zfile_func64.zread_file)) ((filefunc).zfile_func64.opaque,filestream,buf,size)) +#define ZWRITE64(filefunc,filestream,buf,size) ((*((filefunc).zfile_func64.zwrite_file)) ((filefunc).zfile_func64.opaque,filestream,buf,size)) +//#define ZTELL64(filefunc,filestream) ((*((filefunc).ztell64_file)) ((filefunc).opaque,filestream)) +//#define ZSEEK64(filefunc,filestream,pos,mode) ((*((filefunc).zseek64_file)) ((filefunc).opaque,filestream,pos,mode)) +#define ZCLOSE64(filefunc,filestream) ((*((filefunc).zfile_func64.zclose_file)) ((filefunc).zfile_func64.opaque,filestream)) +#define ZERROR64(filefunc,filestream) ((*((filefunc).zfile_func64.zerror_file)) ((filefunc).zfile_func64.opaque,filestream)) + +voidpf call_zopen64 OF((const zlib_filefunc64_32_def* pfilefunc,const void*filename,int mode)); +long call_zseek64 OF((const zlib_filefunc64_32_def* pfilefunc,voidpf filestream, ZPOS64_T offset, int origin)); +ZPOS64_T call_ztell64 OF((const zlib_filefunc64_32_def* pfilefunc,voidpf filestream)); + +void fill_zlib_filefunc64_32_def_from_filefunc32(zlib_filefunc64_32_def* p_filefunc64_32,const zlib_filefunc_def* p_filefunc32); + +#define ZOPEN64(filefunc,filename,mode) (call_zopen64((&(filefunc)),(filename),(mode))) +#define ZTELL64(filefunc,filestream) (call_ztell64((&(filefunc)),(filestream))) +#define ZSEEK64(filefunc,filestream,pos,mode) (call_zseek64((&(filefunc)),(filestream),(pos),(mode))) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/core/io/ip.cpp b/core/io/ip.cpp new file mode 100644 index 00000000000..503a009444c --- /dev/null +++ b/core/io/ip.cpp @@ -0,0 +1,270 @@ +/*************************************************************************/ +/* ip.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 "ip.h" +#include "os/thread.h" +#include "os/semaphore.h" +#include "hash_map.h" + + + +/************* RESOLVER ******************/ + + +struct _IP_ResolverPrivate { + + struct QueueItem { + + volatile IP::ResolverStatus status; + IP_Address response; + String hostname; + + void clear() { + status = IP::RESOLVER_STATUS_NONE; + response = IP_Address(); + hostname=""; + }; + + QueueItem() { + clear(); + }; + }; + + QueueItem queue[IP::RESOLVER_MAX_QUERIES]; + + IP::ResolverID find_empty_id() const { + + for(int i=0;iresolve_hostname(queue[i].hostname); + + if (queue[i].response.host==0) + queue[i].status=IP::RESOLVER_STATUS_ERROR; + else + queue[i].status=IP::RESOLVER_STATUS_DONE; + + } + } + + + static void _thread_function(void *self) { + + _IP_ResolverPrivate *ipr=(_IP_ResolverPrivate*)self; + + while(!ipr->thread_abort) { + + ipr->sem->wait(); + GLOBAL_LOCK_FUNCTION; + ipr->resolve_queues(); + + } + + } + + HashMap cache; + +}; + + + +IP_Address IP::resolve_hostname(const String& p_hostname) { + + GLOBAL_LOCK_FUNCTION + + if (resolver->cache.has(p_hostname)) + return resolver->cache[p_hostname]; + + IP_Address res = _resolve_hostname(p_hostname); + resolver->cache[p_hostname]=res; + return res; + +} +IP::ResolverID IP::resolve_hostname_queue_item(const String& p_hostname) { + + GLOBAL_LOCK_FUNCTION + + ResolverID id = resolver->find_empty_id(); + + if (id==RESOLVER_INVALID_ID) { + WARN_PRINT("Out of resolver queries"); + return id; + } + + resolver->queue[id].hostname=p_hostname; + if (resolver->cache.has(p_hostname)) { + resolver->queue[id].response=resolver->cache[p_hostname]; + resolver->queue[id].status=IP::RESOLVER_STATUS_DONE; + } else { + resolver->queue[id].response=IP_Address(); + resolver->queue[id].status=IP::RESOLVER_STATUS_WAITING; + if (resolver->thread) + resolver->sem->post(); + else + resolver->resolve_queues(); + } + + + + + + return id; +} + +IP::ResolverStatus IP::get_resolve_item_status(ResolverID p_id) const { + + ERR_FAIL_INDEX_V(p_id,IP::RESOLVER_MAX_QUERIES,IP::RESOLVER_STATUS_NONE); + + GLOBAL_LOCK_FUNCTION; + ERR_FAIL_COND_V(resolver->queue[p_id].status==IP::RESOLVER_STATUS_NONE,IP::RESOLVER_STATUS_NONE); + + return resolver->queue[p_id].status; + +} +IP_Address IP::get_resolve_item_address(ResolverID p_id) const { + + ERR_FAIL_INDEX_V(p_id,IP::RESOLVER_MAX_QUERIES,IP_Address()); + + GLOBAL_LOCK_FUNCTION; + + if (resolver->queue[p_id].status!=IP::RESOLVER_STATUS_DONE) { + ERR_EXPLAIN("Resolve of '"+resolver->queue[p_id].hostname+"'' didn't complete yet."); + ERR_FAIL_COND_V(resolver->queue[p_id].status!=IP::RESOLVER_STATUS_DONE,IP_Address()); + } + + + return resolver->queue[p_id].response; + +} +void IP::erase_resolve_item(ResolverID p_id) { + + ERR_FAIL_INDEX(p_id,IP::RESOLVER_MAX_QUERIES); + + GLOBAL_LOCK_FUNCTION; + + resolver->queue[p_id].status=IP::RESOLVER_STATUS_DONE; + +} + + +void IP::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("resolve_hostname","host"),&IP::resolve_hostname); + ObjectTypeDB::bind_method(_MD("resolve_hostname_queue_item","host"),&IP::resolve_hostname_queue_item); + ObjectTypeDB::bind_method(_MD("get_resolve_item_status","id"),&IP::get_resolve_item_status); + ObjectTypeDB::bind_method(_MD("get_resolve_item_address","id"),&IP::get_resolve_item_address); + ObjectTypeDB::bind_method(_MD("erase_resolve_item","id"),&IP::erase_resolve_item); + + BIND_CONSTANT( RESOLVER_STATUS_NONE ); + BIND_CONSTANT( RESOLVER_STATUS_WAITING ); + BIND_CONSTANT( RESOLVER_STATUS_DONE ); + BIND_CONSTANT( RESOLVER_STATUS_ERROR ); + + BIND_CONSTANT( RESOLVER_MAX_QUERIES ); + BIND_CONSTANT( RESOLVER_INVALID_ID ); + +} + + +IP*IP::singleton=NULL; + +IP* IP::get_singleton() { + + return singleton; +} + + +IP* (*IP::_create)()=NULL; + +IP* IP::create() { + + ERR_FAIL_COND_V(singleton,NULL); + ERR_FAIL_COND_V(!_create,NULL); + return _create(); +} + +IP::IP() { + + singleton=this; + resolver = memnew( _IP_ResolverPrivate ); + resolver->sem=NULL; + +#ifndef NO_THREADS + + //resolver->sem = Semaphore::create(); + + resolver->sem=NULL; + if (resolver->sem) { + resolver->thread_abort=false; + + resolver->thread = Thread::create( _IP_ResolverPrivate::_thread_function,resolver ); + + if (!resolver->thread) + memdelete(resolver->sem); //wtf + } else { + resolver->thread=NULL; + } +#else + resolver->sem = NULL; + resolver->thread=NULL; +#endif + + +} + +IP::~IP() { + +#ifndef NO_THREADS + if (resolver->thread) { + resolver->thread_abort=true; + resolver->sem->post(); + Thread::wait_to_finish(resolver->thread); + memdelete( resolver->thread ); + memdelete( resolver->sem); + } + memdelete(resolver); + +#endif + +} diff --git a/core/io/ip.h b/core/io/ip.h new file mode 100644 index 00000000000..f1ef5fe794c --- /dev/null +++ b/core/io/ip.h @@ -0,0 +1,91 @@ +/*************************************************************************/ +/* ip.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 IP_H +#define IP_H + + +#include "os/os.h" +#include "io/ip_address.h" + +struct _IP_ResolverPrivate; + +class IP : public Object { + OBJ_TYPE( IP, Object ); + OBJ_CATEGORY("Networking"); +public: + + enum ResolverStatus { + + RESOLVER_STATUS_NONE, + RESOLVER_STATUS_WAITING, + RESOLVER_STATUS_DONE, + RESOLVER_STATUS_ERROR, + }; + + enum { + RESOLVER_MAX_QUERIES = 32, + RESOLVER_INVALID_ID=-1 + }; + + + typedef int ResolverID; + + +private: + + _IP_ResolverPrivate *resolver; +protected: + + static IP*singleton; + static void _bind_methods(); + + virtual IP_Address _resolve_hostname(const String& p_hostname)=0; + + static IP* (*_create)(); +public: + + + IP_Address resolve_hostname(const String& p_hostname); + // async resolver hostname + ResolverID resolve_hostname_queue_item(const String& p_hostname); + ResolverStatus get_resolve_item_status(ResolverID p_id) const; + IP_Address get_resolve_item_address(ResolverID p_id) const; + void erase_resolve_item(ResolverID p_id); + + static IP* get_singleton(); + + static IP* create(); + + IP(); + ~IP(); + + +}; + +#endif // IP_H diff --git a/core/io/ip_address.cpp b/core/io/ip_address.cpp new file mode 100644 index 00000000000..a1400adbb64 --- /dev/null +++ b/core/io/ip_address.cpp @@ -0,0 +1,60 @@ +/*************************************************************************/ +/* ip_address.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 "ip_address.h" +/* +IP_Address::operator Variant() const { + + return operator String(); +}*/ +IP_Address::operator String() const { + + return itos(field[0])+"."+itos(field[1])+"."+itos(field[2])+"."+itos(field[3]); +} + +IP_Address::IP_Address(const String& p_string) { + + host=0; + int slices = p_string.get_slice_count("."); + if (slices!=4) { + ERR_EXPLAIN("Invalid IP Address String: "+p_string); + ERR_FAIL(); + } + for(int i=0;i<4;i++) { + + field[i]=p_string.get_slice(".",i).to_int(); + } +} + +IP_Address::IP_Address(uint8_t p_a,uint8_t p_b,uint8_t p_c,uint8_t p_d) { + + field[0]=p_a; + field[1]=p_b; + field[2]=p_c; + field[3]=p_d; +} diff --git a/core/io/ip_address.h b/core/io/ip_address.h new file mode 100644 index 00000000000..cd39aa6c81d --- /dev/null +++ b/core/io/ip_address.h @@ -0,0 +1,50 @@ +/*************************************************************************/ +/* ip_address.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 IP_ADDRESS_H +#define IP_ADDRESS_H + +#include "ustring.h" + +struct IP_Address { + + union { + uint8_t field[4]; + uint32_t host; + }; + + //operator Variant() const; + operator String() const; + IP_Address(const String& p_string); + IP_Address(uint8_t p_a,uint8_t p_b,uint8_t p_c,uint8_t p_d); + IP_Address() { host=0; } +}; + + + +#endif // IP_ADDRESS_H diff --git a/core/io/json.cpp b/core/io/json.cpp new file mode 100644 index 00000000000..a83d7e4d6e0 --- /dev/null +++ b/core/io/json.cpp @@ -0,0 +1,477 @@ +/*************************************************************************/ +/* json.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 "json.h" +#include "print_string.h" + +const char * JSON::tk_name[TK_MAX] = { + "'{'", + "'}'", + "'['", + "']'", + "identifier", + "string", + "number", + "':'", + "','", + "EOF", +}; + + + +String JSON::_print_var(const Variant& p_var) { + + switch(p_var.get_type()) { + + case Variant::NIL: return "null"; + case Variant::BOOL: return p_var.operator bool() ? "true": "false"; + case Variant::INT: return itos(p_var); + case Variant::REAL: return rtos(p_var); + case Variant::INT_ARRAY: + case Variant::REAL_ARRAY: + case Variant::STRING_ARRAY: + case Variant::ARRAY: { + + String s = "["; + Array a = p_var; + for(int i=0;i0) + s+=", "; + s+=_print_var(a[i]); + } + s+="]"; + return s; + }; + case Variant::DICTIONARY: { + + String s = "{"; + Dictionary d = p_var; + List keys; + d.get_key_list(&keys); + + for (List::Element *E=keys.front();E;E=E->next()) { + + if (E!=keys.front()) + s+=", "; + s+=_print_var(String(E->get())); + s+=":"; + s+=_print_var(d[E->get()]); + } + + s+="}"; + return s; + }; + default: return "\""+String(p_var).c_escape()+"\""; + + } + +} + +String JSON::print(const Dictionary& p_dict) { + + return _print_var(p_dict); +} + + +Error JSON::_get_token(const CharType *p_str, int &idx, int p_len, Token& r_token,int &line,String &r_err_str) { + + while (true) { + switch(p_str[idx]) { + + case '\n': { + + line++; + idx++; + break; + }; + case 0: { + r_token.type=TK_EOF; + return OK; + } break; + case '{': { + + r_token.type=TK_CURLY_BRACKET_OPEN; + idx++; + return OK; + }; + case '}': { + + r_token.type=TK_CURLY_BRACKET_CLOSE; + idx++; + return OK; + }; + case '[': { + + r_token.type=TK_BRACKET_OPEN; + idx++; + return OK; + }; + case ']': { + + r_token.type=TK_BRACKET_CLOSE; + idx++; + return OK; + }; + case ':': { + + r_token.type=TK_COLON; + idx++; + return OK; + }; + case ',': { + + r_token.type=TK_COMMA; + idx++; + return OK; + }; + case '"': { + + idx++; + String str; + while(true) { + if (p_str[idx]==0) { + r_err_str="Unterminated String"; + return ERR_PARSE_ERROR; + } else if (p_str[idx]=='"') { + idx++; + break; + } else if (p_str[idx]=='\\') { + //escaped characters... + idx++; + CharType next = p_str[idx]; + if (next==0) { + r_err_str="Unterminated String"; + return ERR_PARSE_ERROR; + } + CharType res=0; + + switch(next) { + + case 'b': res=8; break; + case 't': res=9; break; + case 'n': res=10; break; + case 'f': res=12; break; + case 'r': res=13; break; + case '\"': res='\"'; break; + case '\\': res='\\'; break; + case '/': res='/'; break; //wtf + case 'u': { + //hexnumbarh - oct is deprecated + + + for(int j=0;j<4;j++) { + CharType c = p_str[idx+j+1]; + if (c==0) { + r_err_str="Unterminated String"; + return ERR_PARSE_ERROR; + } + if (!((c>='0' && c<='9') || (c>='a' && c<='f') || (c>='A' && c<='F'))) { + + r_err_str="Malformed hex constant in string"; + return ERR_PARSE_ERROR; + } + CharType v; + if (c>='0' && c<='9') { + v=c-'0'; + } else if (c>='a' && c<='f') { + v=c-'a'; + v+=10; + } else if (c>='A' && c<='F') { + v=c-'A'; + v+=10; + } else { + ERR_PRINT("BUG"); + v=0; + } + + res<<=4; + res|=v; + + + } + idx+=4; //will add at the end anyway + + + } break; + default: { + + r_err_str="Invalid escape sequence"; + return ERR_PARSE_ERROR; + } break; + } + + str+=res; + + } else { + if (p_str[idx]=='\n') + line++; + str+=p_str[idx]; + } + idx++; + } + + r_token.type=TK_STRING; + r_token.value=str; + return OK; + + } break; + default: { + + if (p_str[idx]<=32) { + idx++; + break; + } + + if (p_str[idx]=='-' || (p_str[idx]>='0' && p_str[idx]<='9')) { + //a number + const CharType *rptr; + double number = String::to_double(&p_str[idx],-1,&rptr); + idx+=(rptr - &p_str[idx]); + r_token.type=TK_NUMBER; + r_token.value=number; + return OK; + + } else if ((p_str[idx]>='A' && p_str[idx]<='Z') || (p_str[idx]>='a' && p_str[idx]<='z')) { + + String id; + + while((p_str[idx]>='A' && p_str[idx]<='Z') || (p_str[idx]>='a' && p_str[idx]<='z')) { + + id+=p_str[idx]; + idx++; + } + + r_token.type=TK_IDENTIFIER; + r_token.value=id; + return OK; + } else { + r_err_str="Unexpected character."; + return ERR_PARSE_ERROR; + } + } + + } + } + + return ERR_PARSE_ERROR; +} + + + +Error JSON::_parse_value(Variant &value,Token& token,const CharType *p_str,int &index, int p_len,int &line,String &r_err_str) { + + + if (token.type==TK_CURLY_BRACKET_OPEN) { + + Dictionary d; + Error err = _parse_object(d,p_str,index,p_len,line,r_err_str); + if (err) + return err; + value=d; + return OK; + } else if (token.type==TK_BRACKET_OPEN) { + + Array a; + Error err = _parse_array(a,p_str,index,p_len,line,r_err_str); + if (err) + return err; + value=a; + return OK; + + } else if (token.type==TK_IDENTIFIER) { + + String id = token.value; + if (id=="true") + value=true; + else if (id=="false") + value=false; + else if (id=="null") + value=Variant(); + else { + r_err_str="Expected 'true','false' or 'null', got '"+id+"'."; + return ERR_PARSE_ERROR; + } + return OK; + + } else if (token.type==TK_NUMBER) { + + value=token.value; + return OK; + } else if (token.type==TK_STRING) { + + value=token.value; + return OK; + } else { + r_err_str="Expected value, got "+String(tk_name[token.type])+"."; + return ERR_PARSE_ERROR; + } + + return ERR_PARSE_ERROR; +} + + +Error JSON::_parse_array(Array &array,const CharType *p_str,int &index, int p_len,int &line,String &r_err_str) { + + Token token; + bool need_comma=false; + + + while(index + +Error decode_variant(Variant& r_variant,const uint8_t *p_buffer, int p_len,int *r_len) { + + const uint8_t * buf=p_buffer; + int len=p_len; + + ERR_FAIL_COND_V(len<4,ERR_INVALID_DATA); + + + uint32_t type=decode_uint32(buf); + + ERR_FAIL_COND_V(type>=Variant::VARIANT_MAX,ERR_INVALID_DATA); + + buf+=4; + len-=4; + if (r_len) + *r_len=4; + + switch(type) { + + case Variant::NIL: { + + r_variant=Variant(); + } break; + case Variant::BOOL: { + + ERR_FAIL_COND_V(len<4,ERR_INVALID_DATA); + bool val = decode_uint32(buf); + r_variant=val; + if (r_len) + (*r_len)+=4; + } break; + case Variant::INT: { + + ERR_FAIL_COND_V(len<4,ERR_INVALID_DATA); + int val = decode_uint32(buf); + r_variant=val; + if (r_len) + (*r_len)+=4; + + } break; + case Variant::REAL: { + + ERR_FAIL_COND_V(len<(int)4,ERR_INVALID_DATA); + float val = decode_float(buf); + r_variant=val; + if (r_len) + (*r_len)+=4; + + } break; + case Variant::STRING: { + + ERR_FAIL_COND_V(len<4,ERR_INVALID_DATA); + uint32_t strlen = decode_uint32(buf); + buf+=4; + len-=4; + ERR_FAIL_COND_V((int)strlen>len,ERR_INVALID_DATA); + + String str; + str.parse_utf8((const char*)buf,strlen); + r_variant=str; + + if (r_len) { + if (strlen%4) + (*r_len)+=4-strlen%4; + (*r_len)+=4+strlen; + + } + + } break; + // math types + + case Variant::VECTOR2: { + + ERR_FAIL_COND_V(len<(int)4*2,ERR_INVALID_DATA); + Vector2 val; + val.x=decode_float(&buf[0]); + val.y=decode_float(&buf[4]); + r_variant=val; + + if (r_len) + (*r_len)+=4*2; + + } break; // 5 + case Variant::RECT2: { + + ERR_FAIL_COND_V(len<(int)4*4,ERR_INVALID_DATA); + Rect2 val; + val.pos.x=decode_float(&buf[0]); + val.pos.y=decode_float(&buf[4]); + val.size.x=decode_float(&buf[8]); + val.size.y=decode_float(&buf[12]); + r_variant=val; + + if (r_len) + (*r_len)+=4*4; + + } break; + case Variant::VECTOR3: { + + ERR_FAIL_COND_V(len<(int)4*3,ERR_INVALID_DATA); + Vector3 val; + val.x=decode_float(&buf[0]); + val.y=decode_float(&buf[4]); + val.z=decode_float(&buf[8]); + r_variant=val; + + if (r_len) + (*r_len)+=4*3; + + } break; + case Variant::PLANE: { + + ERR_FAIL_COND_V(len<(int)4*4,ERR_INVALID_DATA); + Plane val; + val.normal.x=decode_float(&buf[0]); + val.normal.y=decode_float(&buf[4]); + val.normal.z=decode_float(&buf[8]); + val.d=decode_float(&buf[12]); + r_variant=val; + + if (r_len) + (*r_len)+=4*4; + + } break; + case Variant::QUAT: { + + ERR_FAIL_COND_V(len<(int)4*4,ERR_INVALID_DATA); + Quat val; + val.x=decode_float(&buf[0]); + val.y=decode_float(&buf[4]); + val.z=decode_float(&buf[8]); + val.w=decode_float(&buf[12]); + r_variant=val; + + if (r_len) + (*r_len)+=4*4; + + } break; + case Variant::_AABB: { + + ERR_FAIL_COND_V(len<(int)4*6,ERR_INVALID_DATA); + AABB val; + val.pos.x=decode_float(&buf[0]); + val.pos.y=decode_float(&buf[4]); + val.pos.z=decode_float(&buf[8]); + val.size.x=decode_float(&buf[12]); + val.size.y=decode_float(&buf[16]); + val.size.z=decode_float(&buf[20]); + r_variant=val; + + if (r_len) + (*r_len)+=4*6; + + } break; + case Variant::MATRIX3: { + + ERR_FAIL_COND_V(len<(int)4*9,ERR_INVALID_DATA); + Matrix3 val; + for(int i=0;i<3;i++) { + for(int j=0;j<3;j++) { + + val.elements[i][j]=decode_float(&buf[(i*3+j)*4]); + } + } + + r_variant=val; + + if (r_len) + (*r_len)+=4*9; + + } break; + case Variant::TRANSFORM: { + + ERR_FAIL_COND_V(len<(int)4*12,ERR_INVALID_DATA); + Transform val; + for(int i=0;i<3;i++) { + for(int j=0;j<3;j++) { + + val.basis.elements[i][j]=decode_float(&buf[(i*3+j)*4]); + } + } + val.origin[0]=decode_float(&buf[36]); + val.origin[1]=decode_float(&buf[40]); + val.origin[2]=decode_float(&buf[44]); + + r_variant=val; + + if (r_len) + (*r_len)+=4*12; + + } break; + + // misc types + case Variant::COLOR: { + + ERR_FAIL_COND_V(len<(int)4*4,ERR_INVALID_DATA); + Color val; + val.r=decode_float(&buf[0]); + val.g=decode_float(&buf[4]); + val.b=decode_float(&buf[8]); + val.a=decode_float(&buf[12]); + r_variant=val; + + if (r_len) + (*r_len)+=4*4; + + } break; + case Variant::IMAGE: { + + ERR_FAIL_COND_V(len<(int)5*4,ERR_INVALID_DATA); + Image::Format fmt = (Image::Format)decode_uint32(&buf[0]); + ERR_FAIL_INDEX_V( fmt, Image::FORMAT_MAX, ERR_INVALID_DATA); + uint32_t mipmaps = decode_uint32(&buf[4]); + uint32_t w = decode_uint32(&buf[8]); + uint32_t h = decode_uint32(&buf[12]); + uint32_t datalen = decode_uint32(&buf[16]); + + Image img; + if (datalen>0) { + len-=5*4; + ERR_FAIL_COND_V( len < datalen, ERR_INVALID_DATA ); + DVector data; + data.resize(datalen); + DVector::Write wr = data.write(); + copymem(&wr[0],&buf[20],datalen); + wr = DVector::Write(); + + + + img=Image(w,h,mipmaps,fmt,data); + } + + r_variant=img; + if (r_len) + (*r_len)+=4*5+datalen; + + } break; + case Variant::NODE_PATH: { + + ERR_FAIL_COND_V(len<4,ERR_INVALID_DATA); + uint32_t strlen = decode_uint32(buf); + buf+=4; + len-=4; + ERR_FAIL_COND_V((int)strlen>len,ERR_INVALID_DATA); + + + String str; + str.parse_utf8((const char*)buf,strlen); + + r_variant=NodePath(str); + + if (r_len) + (*r_len)+=4+strlen; + + } break; + /*case Variant::RESOURCE: { + + ERR_EXPLAIN("Can't marshallize resources"); + ERR_FAIL_V(ERR_INVALID_DATA); //no, i'm sorry, no go + } break;*/ + case Variant::_RID: { + + r_variant = RID(); + } break; + case Variant::OBJECT: { + + + r_variant = (Object*)NULL; + } break; + case Variant::INPUT_EVENT: { + + InputEvent ie; + + ie.type=decode_uint32(&buf[0]); + ie.device=decode_uint32(&buf[4]); + uint32_t len = decode_uint32(&buf[8])-12; + + if (r_len) + (*r_len)+=12; + + switch(ie.type) { + + case InputEvent::KEY: { + + uint32_t mods=decode_uint32(&buf[12]); + if (mods&KEY_MASK_SHIFT) + ie.key.mod.shift=true; + if (mods&KEY_MASK_CTRL) + ie.key.mod.control=true; + if (mods&KEY_MASK_ALT) + ie.key.mod.alt=true; + if (mods&KEY_MASK_META) + ie.key.mod.meta=true; + ie.key.scancode=decode_uint32(&buf[16]); + + if (r_len) + (*r_len)+=8; + + + } break; + case InputEvent::MOUSE_BUTTON: { + + ie.mouse_button.button_index=decode_uint32(&buf[12]); + if (r_len) + (*r_len)+=4; + + } break; + case InputEvent::JOYSTICK_BUTTON: { + + ie.joy_button.button_index=decode_uint32(&buf[12]); + if (r_len) + (*r_len)+=4; + } break; + case InputEvent::SCREEN_TOUCH: { + + ie.screen_touch.index=decode_uint32(&buf[12]); + if (r_len) + (*r_len)+=4; + } break; + case InputEvent::JOYSTICK_MOTION: { + + ie.joy_motion.axis=decode_uint32(&buf[12]); + if (r_len) + (*r_len)+=4; + } break; + } + + r_variant = ie; + + } break; + case Variant::DICTIONARY: { + + ERR_FAIL_COND_V(len<4,ERR_INVALID_DATA); + uint32_t count = decode_uint32(buf); + bool shared = count&0x80000000; + count&=0x7FFFFFFF; + + buf+=4; + len-=4; + + if (r_len) { + (*r_len)+=4; + } + + Dictionary d(shared); + + for(uint32_t i=0;ilen,ERR_INVALID_DATA); + + + DVector data; + + if (count) { + data.resize(count); + DVector::Write w = data.write(); + for(int i=0;i::Write(); + } + + r_variant=data; + + if (r_len) { + if (count%4) + (*r_len)+=4-count%4; + (*r_len)+=4+count; + } + + + + } break; + case Variant::INT_ARRAY: { + + ERR_FAIL_COND_V(len<4,ERR_INVALID_DATA); + uint32_t count = decode_uint32(buf); + buf+=4; + len-=4; + ERR_FAIL_COND_V((int)count*4>len,ERR_INVALID_DATA); + + DVector data; + + if (count) { + //const int*rbuf=(const int*)buf; + data.resize(count); + DVector::Write w = data.write(); + for(int i=0;i::Write(); + } + r_variant=Variant(data); + if (r_len) { + (*r_len)+=4+count*sizeof(int); + } + + } break; + case Variant::REAL_ARRAY: { + + ERR_FAIL_COND_V(len<4,ERR_INVALID_DATA); + uint32_t count = decode_uint32(buf); + buf+=4; + len-=4; + ERR_FAIL_COND_V((int)count*4>len,ERR_INVALID_DATA); + + DVector data; + + if (count) { + //const float*rbuf=(const float*)buf; + data.resize(count); + DVector::Write w = data.write(); + for(int i=0;i::Write(); + } + r_variant=data; + + if (r_len) { + (*r_len)+=4+count*sizeof(float); + } + + + } break; + case Variant::STRING_ARRAY: { + + ERR_FAIL_COND_V(len<4,ERR_INVALID_DATA); + uint32_t count = decode_uint32(buf); + ERR_FAIL_COND_V(count<0,ERR_INVALID_DATA); + + DVector strings; + buf+=4; + len-=4; + + if (r_len) + (*r_len)+=4; + //printf("string count: %i\n",count); + + for(int i=0;i<(int)count;i++) { + + ERR_FAIL_COND_V(len<4,ERR_INVALID_DATA); + uint32_t strlen = decode_uint32(buf); + + buf+=4; + len-=4; + ERR_FAIL_COND_V((int)strlen>len,ERR_INVALID_DATA); + + //printf("loaded string: %s\n",(const char*)buf); + String str; + str.parse_utf8((const char*)buf,strlen); + + strings.push_back(str); + + buf+=strlen; + len-=strlen; + + if (r_len) + (*r_len)+=4+strlen; + + if (strlen%4) { + int pad = 4-(strlen%4); + buf+=pad; + len-=pad; + if (r_len) { + (*r_len)+=pad; + } + } + + } + + r_variant=strings; + + + } break; + case Variant::VECTOR3_ARRAY: { + + ERR_FAIL_COND_V(len<4,ERR_INVALID_DATA); + uint32_t count = decode_uint32(buf); + ERR_FAIL_COND_V(count<0,ERR_INVALID_DATA); + buf+=4; + len-=4; + + ERR_FAIL_COND_V((int)count*4*3>len,ERR_INVALID_DATA); + DVector varray; + + if (r_len) { + (*r_len)+=4; + } + + if (count) { + varray.resize(count); + DVector::Write w = varray.write(); + const float *r = (const float*)buf; + + for(int i=0;i<(int)count;i++) { + + w[i].x=decode_float(buf+i*4*3+4*0); + w[i].y=decode_float(buf+i*4*3+4*1); + w[i].z=decode_float(buf+i*4*3+4*2); + + } + + int adv = 4*3*count; + + if (r_len) + (*r_len)+=adv; + len-=adv; + buf+=adv; + + } + + r_variant=varray; + + } break; + case Variant::COLOR_ARRAY: { + + ERR_FAIL_COND_V(len<4,ERR_INVALID_DATA); + uint32_t count = decode_uint32(buf); + ERR_FAIL_COND_V(count<0,ERR_INVALID_DATA); + buf+=4; + len-=4; + + ERR_FAIL_COND_V((int)count*4*4>len,ERR_INVALID_DATA); + DVector carray; + + if (r_len) { + (*r_len)+=4; + } + + if (count) { + carray.resize(count); + DVector::Write w = carray.write(); + const float *r = (const float*)buf; + + for(int i=0;i<(int)count;i++) { + + w[i].r=decode_float(buf+i*4*4+4*0); + w[i].g=decode_float(buf+i*4*4+4*1); + w[i].b=decode_float(buf+i*4*4+4*2); + w[i].a=decode_float(buf+i*4*4+4*3); + + } + + int adv = 4*4*count; + + if (r_len) + (*r_len)+=adv; + len-=adv; + buf+=adv; + + } + + r_variant=carray; + + } break; + default: { ERR_FAIL_V(ERR_BUG); } + } + + return OK; +} + +Error encode_variant(const Variant& p_variant, uint8_t *r_buffer, int &r_len) { + + uint8_t * buf=r_buffer; + + r_len=0; + + if (buf) { + encode_uint32(p_variant.get_type(),buf); + buf+=4; + } + r_len+=4; + + switch(p_variant.get_type()) { + + case Variant::NIL: { + + //nothing to do + } break; + case Variant::BOOL: { + + if (buf) { + encode_uint32(p_variant.operator bool(),buf); + } + + r_len+=4; + + } break; + case Variant::INT: { + + if (buf) { + encode_uint32(p_variant.operator int(),buf); + } + + r_len+=4; + + } break; + case Variant::REAL: { + + if (buf) { + encode_float(p_variant.operator float(),buf); + } + + r_len+=4; + + } break; + case Variant::NODE_PATH: + case Variant::STRING: { + + + CharString utf8 = p_variant.operator String().utf8(); + + if (buf) { + encode_uint32(utf8.length(),buf); + buf+=4; + copymem(buf,utf8.get_data(),utf8.length()); + } + + r_len+=4+utf8.length(); + while (r_len%4) + r_len++; //pad + + } break; + // math types + + case Variant::VECTOR2: { + + if (buf) { + Vector2 v2=p_variant; + encode_float(v2.x,&buf[0]); + encode_float(v2.y,&buf[4]); + + } + + r_len+=2*4; + + } break; // 5 + case Variant::RECT2: { + + if (buf) { + Rect2 r2=p_variant; + encode_float(r2.pos.x,&buf[0]); + encode_float(r2.pos.y,&buf[4]); + encode_float(r2.size.x,&buf[8]); + encode_float(r2.size.y,&buf[12]); + } + r_len+=4*4; + + } break; + case Variant::VECTOR3: { + + if (buf) { + Vector3 v3=p_variant; + encode_float(v3.x,&buf[0]); + encode_float(v3.y,&buf[4]); + encode_float(v3.z,&buf[8]); + } + + r_len+=3*4; + + } break; + case Variant::PLANE: { + + if (buf) { + Plane p=p_variant; + encode_float(p.normal.x,&buf[0]); + encode_float(p.normal.y,&buf[4]); + encode_float(p.normal.z,&buf[8]); + encode_float(p.d,&buf[12]); + } + + r_len+=4*4; + + } break; + case Variant::QUAT: { + + if (buf) { + Quat q=p_variant; + encode_float(q.x,&buf[0]); + encode_float(q.y,&buf[4]); + encode_float(q.z,&buf[8]); + encode_float(q.w,&buf[12]); + } + + r_len+=4*4; + + } break; + case Variant::_AABB: { + + if (buf) { + AABB aabb=p_variant; + encode_float(aabb.pos.x,&buf[0]); + encode_float(aabb.pos.y,&buf[4]); + encode_float(aabb.pos.z,&buf[8]); + encode_float(aabb.size.x,&buf[12]); + encode_float(aabb.size.y,&buf[16]); + encode_float(aabb.size.z,&buf[20]); + } + + r_len+=6*4; + + + } break; + case Variant::MATRIX3: { + + if (buf) { + Matrix3 val=p_variant; + for(int i=0;i<3;i++) { + for(int j=0;j<3;j++) { + + copymem(&buf[(i*3+j)*4],&val.elements[i][j],sizeof(float)); + } + } + } + + + r_len+=9*4; + + } break; + case Variant::TRANSFORM: { + + if (buf) { + Transform val=p_variant; + for(int i=0;i<3;i++) { + for(int j=0;j<3;j++) { + + copymem(&buf[(i*3+j)*4],&val.basis.elements[i][j],sizeof(float)); + } + } + + encode_float(val.origin.x,&buf[36]); + encode_float(val.origin.y,&buf[40]); + encode_float(val.origin.z,&buf[44]); + + + } + + r_len+=12*4; + + } break; + + // misc types + case Variant::COLOR: { + + if (buf) { + Color c=p_variant; + encode_float(c.r,&buf[0]); + encode_float(c.g,&buf[4]); + encode_float(c.b,&buf[8]); + encode_float(c.a,&buf[12]); + } + + r_len+=4*4; + + } break; + case Variant::IMAGE: { + + Image image = p_variant; + DVector data=image.get_data(); + + if (buf) { + + encode_uint32(image.get_format(),&buf[0]); + encode_uint32(image.get_mipmaps(),&buf[4]); + encode_uint32(image.get_width(),&buf[8]); + encode_uint32(image.get_height(),&buf[12]); + int ds=data.size(); + encode_uint32(ds,&buf[16]); + DVector::Read r = data.read(); + copymem(&buf[20],&r[0],ds); + } + + r_len+=data.size()+5*4; + + } break; + /*case Variant::RESOURCE: { + + ERR_EXPLAIN("Can't marshallize resources"); + ERR_FAIL_V(ERR_INVALID_DATA); //no, i'm sorry, no go + } break;*/ + case Variant::_RID: + case Variant::OBJECT: { + + + } break; + case Variant::INPUT_EVENT: { + + + InputEvent ie=p_variant; + + if (buf) { + + encode_uint32(ie.type,&buf[0]); + encode_uint32(ie.device,&buf[4]); + encode_uint32(0,&buf[8]); + } + + int llen=12; + + switch(ie.type) { + + case InputEvent::KEY: { + + if (buf) { + + uint32_t mods=0; + if (ie.key.mod.shift) + mods|=KEY_MASK_SHIFT; + if (ie.key.mod.control) + mods|=KEY_MASK_CTRL; + if (ie.key.mod.alt) + mods|=KEY_MASK_ALT; + if (ie.key.mod.meta) + mods|=KEY_MASK_META; + + encode_uint32(mods,&buf[llen]); + encode_uint32(ie.key.scancode,&buf[llen+4]); + } + llen+=8; + + } break; + case InputEvent::MOUSE_BUTTON: { + + if (buf) { + + encode_uint32(ie.mouse_button.button_index,&buf[llen]); + } + llen+=4; + } break; + case InputEvent::JOYSTICK_BUTTON: { + + if (buf) { + + encode_uint32(ie.joy_button.button_index,&buf[llen]); + } + llen+=4; + } break; + case InputEvent::SCREEN_TOUCH: { + + if (buf) { + + encode_uint32(ie.screen_touch.index,&buf[llen]); + } + llen+=4; + } break; + case InputEvent::JOYSTICK_MOTION: { + + if (buf) { + + int axis = ie.joy_motion.axis; + encode_uint32(axis,&buf[llen]); + } + llen+=4; + } break; + } + + if (buf) + encode_uint32(llen,&buf[8]); + r_len+=llen; + + + // not supported + } break; + case Variant::DICTIONARY: { + + Dictionary d = p_variant; + + if (buf) { + encode_uint32(uint32_t(d.size())|(d.is_shared()?0x80000000:0),buf); + buf+=4; + } + r_len+=4; + + List keys; + d.get_key_list(&keys); + + + for(List::Element *E=keys.front();E;E=E->next()) { + + /* + CharString utf8 = E->->utf8(); + + if (buf) { + encode_uint32(utf8.length()+1,buf); + buf+=4; + copymem(buf,utf8.get_data(),utf8.length()+1); + } + + r_len+=4+utf8.length()+1; + while (r_len%4) + r_len++; //pad + */ + int len; + encode_variant(E->get(),buf,len); + ERR_FAIL_COND_V(len%4,ERR_BUG); + r_len+=len; + if (buf) + buf += len; + encode_variant(d[E->get()],buf,len); + ERR_FAIL_COND_V(len%4,ERR_BUG); + r_len+=len; + if (buf) + buf += len; + } + + } break; + case Variant::ARRAY: { + + Array v = p_variant; + + if (buf) { + encode_uint32(uint32_t(v.size())|(v.is_shared()?0x80000000:0),buf); + buf+=4; + } + + r_len+=4; + + for(int i=0;i data = p_variant; + int datalen=data.size(); + int datasize=sizeof(uint8_t); + + if (buf) { + encode_uint32(datalen,buf); + buf+=4; + DVector::Read r = data.read(); + copymem(buf,&r[0],datalen*datasize); + + } + + r_len+=4+datalen*datasize; + while(r_len%4) + r_len++; + + } break; + case Variant::INT_ARRAY: { + + DVector data = p_variant; + int datalen=data.size(); + int datasize=sizeof(int32_t); + + if (buf) { + encode_uint32(datalen,buf); + buf+=4; + DVector::Read r = data.read(); + for(int i=0;i data = p_variant; + int datalen=data.size(); + int datasize=sizeof(real_t); + + if (buf) { + encode_uint32(datalen,buf); + buf+=4; + DVector::Read r = data.read(); + for(int i=0;i data = p_variant; + int len=data.size(); + + if (buf) { + encode_uint32(len,buf); + buf+=4; + } + + r_len+=4; + + for(int i=0;i data = p_variant; + int len=data.size(); + + if (buf) { + encode_uint32(len,buf); + buf+=4; + } + + r_len+=4; + + if (buf) { + + for(int i=0;i data = p_variant; + int len=data.size(); + + if (buf) { + encode_uint32(len,buf); + buf+=4; + } + + r_len+=4; + + if (buf) { + + for(int i=0;i>=8; + } + + return sizeof( uint16_t ); +} + +static inline unsigned int encode_uint32(uint32_t p_uint, uint8_t *p_arr) { + + for (int i=0;i<4;i++) { + + *p_arr=p_uint&0xFF; + p_arr++; p_uint>>=8; + } + + return sizeof( uint32_t ); +} + +static inline unsigned int encode_float(float p_float, uint8_t *p_arr) { + + MarshallFloat mf; + mf.f=p_float; + encode_uint32( mf.i, p_arr ); + + return sizeof( uint32_t ); +} + +static inline unsigned int encode_uint64(uint64_t p_uint, uint8_t *p_arr) { + + for (int i=0;i<8;i++) { + + *p_arr=p_uint&0xFF; + p_arr++; p_uint>>=8; + } + + return sizeof(uint64_t); +} + +static inline unsigned int encode_double(double p_double, uint8_t *p_arr) { + + MarshallDouble md; + md.d=p_double; + encode_uint64( md.l, p_arr ); + + return sizeof(uint64_t); + +} + + +static inline int encode_cstring(const char *p_string, uint8_t * p_data) { + + int len=0; + + while (*p_string) { + + if (p_data) { + + *p_data=(uint8_t)*p_string; + p_data++; + } + p_string++; + len++; + }; + + if (p_data) *p_data = 0; + return len+1; +} + +static inline uint16_t decode_uint16(const uint8_t *p_arr) { + + uint16_t u=0; + + for (int i=0;i<2;i++) { + + uint16_t b = *p_arr; + b<<=(i*8); + u|=b; + p_arr++; + } + + return u; +} + +static inline uint32_t decode_uint32(const uint8_t *p_arr) { + + uint32_t u=0; + + for (int i=0;i<4;i++) { + + uint32_t b = *p_arr; + b<<=(i*8); + u|=b; + p_arr++; + } + + return u; +} + +static inline float decode_float(const uint8_t *p_arr) { + + MarshallFloat mf; + mf.i = decode_uint32(p_arr); + return mf.f; +} + +static inline uint64_t decode_uint64(const uint8_t *p_arr) { + + uint64_t u=0; + + for (int i=0;i<8;i++) { + + uint64_t b = (*p_arr)&0xFF; + b<<=(i*8); + u|=b; + p_arr++; + } + + return u; +} + +static inline double decode_double(const uint8_t *p_arr) { + + MarshallDouble md; + md.l = decode_uint64( p_arr ); + return md.d; + +} + + +Error decode_variant(Variant& r_variant,const uint8_t *p_buffer, int p_len,int *r_len=NULL); +Error encode_variant(const Variant& p_variant, uint8_t *r_buffer, int &r_len); + +#endif diff --git a/core/io/md5.cpp b/core/io/md5.cpp new file mode 100644 index 00000000000..5a88328dd4f --- /dev/null +++ b/core/io/md5.cpp @@ -0,0 +1,269 @@ +#include "md5.h" + +/* + ********************************************************************** + ** md5.c ** + ** RSA Data Security, Inc. MD5 Message Digest Algorithm ** + ** Created: 2/17/90 RLR ** + ** Revised: 1/91 SRD,AJ,BSK,JT Reference C Version ** + ********************************************************************** + */ + +/* + ********************************************************************** + ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. ** + ** ** + ** License to copy and use this software is granted provided that ** + ** it is identified as the "RSA Data Security, Inc. MD5 Message ** + ** Digest Algorithm" in all material mentioning or referencing this ** + ** software or this function. ** + ** ** + ** License is also granted to make and use derivative works ** + ** provided that such works are identified as "derived from the RSA ** + ** Data Security, Inc. MD5 Message Digest Algorithm" in all ** + ** material mentioning or referencing the derived work. ** + ** ** + ** RSA Data Security, Inc. makes no representations concerning ** + ** either the merchantability of this software or the suitability ** + ** of this software for any particular purpose. It is provided "as ** + ** is" without express or implied warranty of any kind. ** + ** ** + ** These notices must be retained in any copies of any part of this ** + ** documentation and/or software. ** + ********************************************************************** + */ + +/* -- include the following line if the md5.h header file is separate -- */ +/* #include "md5.h" */ + +/* forward declaration */ +static void Transform (uint32_t *buf, uint32_t *in); + + +static unsigned char PADDING[64] = { + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* F, G and H are basic MD5 functions: selection, majority, parity */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +/* ROTATE_LEFT rotates x left n bits */ +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4 */ +/* Rotation is separate from addition to prevent recomputation */ +#define FF(a, b, c, d, x, s, ac) \ + {(a) += F ((b), (c), (d)) + (x) + (uint32_t)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define GG(a, b, c, d, x, s, ac) \ + {(a) += G ((b), (c), (d)) + (x) + (uint32_t)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define HH(a, b, c, d, x, s, ac) \ + {(a) += H ((b), (c), (d)) + (x) + (uint32_t)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define II(a, b, c, d, x, s, ac) \ + {(a) += I ((b), (c), (d)) + (x) + (uint32_t)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } + +void MD5Init (MD5_CTX *mdContext) +{ + mdContext->i[0] = mdContext->i[1] = (uint32_t)0; + + /* Load magic initialization constants. + */ + mdContext->buf[0] = (uint32_t)0x67452301; + mdContext->buf[1] = (uint32_t)0xefcdab89; + mdContext->buf[2] = (uint32_t)0x98badcfe; + mdContext->buf[3] = (uint32_t)0x10325476; +} + +void MD5Update (MD5_CTX *mdContext,unsigned char *inBuf,unsigned int inLen) { + uint32_t in[16]; + int mdi; + unsigned int i, ii; + + /* compute number of bytes mod 64 */ + mdi = (int)((mdContext->i[0] >> 3) & 0x3F); + + /* update number of bits */ + if ((mdContext->i[0] + ((uint32_t)inLen << 3)) < mdContext->i[0]) + mdContext->i[1]++; + mdContext->i[0] += ((uint32_t)inLen << 3); + mdContext->i[1] += ((uint32_t)inLen >> 29); + + while (inLen--) { + /* add new character to buffer, increment mdi */ + mdContext->in[mdi++] = *inBuf++; + + /* transform if necessary */ + if (mdi == 0x40) { + for (i = 0, ii = 0; i < 16; i++, ii += 4) + in[i] = (((uint32_t)mdContext->in[ii+3]) << 24) | + (((uint32_t)mdContext->in[ii+2]) << 16) | + (((uint32_t)mdContext->in[ii+1]) << 8) | + ((uint32_t)mdContext->in[ii]); + Transform (mdContext->buf, in); + mdi = 0; + } + } +} + +void MD5Final (MD5_CTX *mdContext) { + uint32_t in[16]; + int mdi; + unsigned int i, ii; + unsigned int padLen; + + /* save number of bits */ + in[14] = mdContext->i[0]; + in[15] = mdContext->i[1]; + + /* compute number of bytes mod 64 */ + mdi = (int)((mdContext->i[0] >> 3) & 0x3F); + + /* pad out to 56 mod 64 */ + padLen = (mdi < 56) ? (56 - mdi) : (120 - mdi); + MD5Update (mdContext, PADDING, padLen); + + /* append length in bits and transform */ + for (i = 0, ii = 0; i < 14; i++, ii += 4) + in[i] = (((uint32_t)mdContext->in[ii+3]) << 24) | + (((uint32_t)mdContext->in[ii+2]) << 16) | + (((uint32_t)mdContext->in[ii+1]) << 8) | + ((uint32_t)mdContext->in[ii]); + Transform (mdContext->buf, in); + + /* store buffer in digest */ + for (i = 0, ii = 0; i < 4; i++, ii += 4) { + mdContext->digest[ii] = (unsigned char)(mdContext->buf[i] & 0xFF); + mdContext->digest[ii+1] = + (unsigned char)((mdContext->buf[i] >> 8) & 0xFF); + mdContext->digest[ii+2] = + (unsigned char)((mdContext->buf[i] >> 16) & 0xFF); + mdContext->digest[ii+3] = + (unsigned char)((mdContext->buf[i] >> 24) & 0xFF); + } +} + +/* Basic MD5 step. Transform buf based on in. + */ +static void Transform (uint32_t *buf, uint32_t *in) { + uint32_t a = buf[0], b = buf[1], c = buf[2], d = buf[3]; + + /* Round 1 */ +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 + FF ( a, b, c, d, in[ 0], S11, 3614090360); /* 1 */ + FF ( d, a, b, c, in[ 1], S12, 3905402710); /* 2 */ + FF ( c, d, a, b, in[ 2], S13, 606105819); /* 3 */ + FF ( b, c, d, a, in[ 3], S14, 3250441966); /* 4 */ + FF ( a, b, c, d, in[ 4], S11, 4118548399); /* 5 */ + FF ( d, a, b, c, in[ 5], S12, 1200080426); /* 6 */ + FF ( c, d, a, b, in[ 6], S13, 2821735955); /* 7 */ + FF ( b, c, d, a, in[ 7], S14, 4249261313); /* 8 */ + FF ( a, b, c, d, in[ 8], S11, 1770035416); /* 9 */ + FF ( d, a, b, c, in[ 9], S12, 2336552879); /* 10 */ + FF ( c, d, a, b, in[10], S13, 4294925233); /* 11 */ + FF ( b, c, d, a, in[11], S14, 2304563134); /* 12 */ + FF ( a, b, c, d, in[12], S11, 1804603682); /* 13 */ + FF ( d, a, b, c, in[13], S12, 4254626195); /* 14 */ + FF ( c, d, a, b, in[14], S13, 2792965006); /* 15 */ + FF ( b, c, d, a, in[15], S14, 1236535329); /* 16 */ + + /* Round 2 */ +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 + GG ( a, b, c, d, in[ 1], S21, 4129170786); /* 17 */ + GG ( d, a, b, c, in[ 6], S22, 3225465664); /* 18 */ + GG ( c, d, a, b, in[11], S23, 643717713); /* 19 */ + GG ( b, c, d, a, in[ 0], S24, 3921069994); /* 20 */ + GG ( a, b, c, d, in[ 5], S21, 3593408605); /* 21 */ + GG ( d, a, b, c, in[10], S22, 38016083); /* 22 */ + GG ( c, d, a, b, in[15], S23, 3634488961); /* 23 */ + GG ( b, c, d, a, in[ 4], S24, 3889429448); /* 24 */ + GG ( a, b, c, d, in[ 9], S21, 568446438); /* 25 */ + GG ( d, a, b, c, in[14], S22, 3275163606); /* 26 */ + GG ( c, d, a, b, in[ 3], S23, 4107603335); /* 27 */ + GG ( b, c, d, a, in[ 8], S24, 1163531501); /* 28 */ + GG ( a, b, c, d, in[13], S21, 2850285829); /* 29 */ + GG ( d, a, b, c, in[ 2], S22, 4243563512); /* 30 */ + GG ( c, d, a, b, in[ 7], S23, 1735328473); /* 31 */ + GG ( b, c, d, a, in[12], S24, 2368359562); /* 32 */ + + /* Round 3 */ +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 + HH ( a, b, c, d, in[ 5], S31, 4294588738); /* 33 */ + HH ( d, a, b, c, in[ 8], S32, 2272392833); /* 34 */ + HH ( c, d, a, b, in[11], S33, 1839030562); /* 35 */ + HH ( b, c, d, a, in[14], S34, 4259657740); /* 36 */ + HH ( a, b, c, d, in[ 1], S31, 2763975236); /* 37 */ + HH ( d, a, b, c, in[ 4], S32, 1272893353); /* 38 */ + HH ( c, d, a, b, in[ 7], S33, 4139469664); /* 39 */ + HH ( b, c, d, a, in[10], S34, 3200236656); /* 40 */ + HH ( a, b, c, d, in[13], S31, 681279174); /* 41 */ + HH ( d, a, b, c, in[ 0], S32, 3936430074); /* 42 */ + HH ( c, d, a, b, in[ 3], S33, 3572445317); /* 43 */ + HH ( b, c, d, a, in[ 6], S34, 76029189); /* 44 */ + HH ( a, b, c, d, in[ 9], S31, 3654602809); /* 45 */ + HH ( d, a, b, c, in[12], S32, 3873151461); /* 46 */ + HH ( c, d, a, b, in[15], S33, 530742520); /* 47 */ + HH ( b, c, d, a, in[ 2], S34, 3299628645); /* 48 */ + + /* Round 4 */ +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + II ( a, b, c, d, in[ 0], S41, 4096336452); /* 49 */ + II ( d, a, b, c, in[ 7], S42, 1126891415); /* 50 */ + II ( c, d, a, b, in[14], S43, 2878612391); /* 51 */ + II ( b, c, d, a, in[ 5], S44, 4237533241); /* 52 */ + II ( a, b, c, d, in[12], S41, 1700485571); /* 53 */ + II ( d, a, b, c, in[ 3], S42, 2399980690); /* 54 */ + II ( c, d, a, b, in[10], S43, 4293915773); /* 55 */ + II ( b, c, d, a, in[ 1], S44, 2240044497); /* 56 */ + II ( a, b, c, d, in[ 8], S41, 1873313359); /* 57 */ + II ( d, a, b, c, in[15], S42, 4264355552); /* 58 */ + II ( c, d, a, b, in[ 6], S43, 2734768916); /* 59 */ + II ( b, c, d, a, in[13], S44, 1309151649); /* 60 */ + II ( a, b, c, d, in[ 4], S41, 4149444226); /* 61 */ + II ( d, a, b, c, in[11], S42, 3174756917); /* 62 */ + II ( c, d, a, b, in[ 2], S43, 718787259); /* 63 */ + II ( b, c, d, a, in[ 9], S44, 3951481745); /* 64 */ + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +/* + ********************************************************************** + ** End of md5.c ** + ******************************* (cut) ******************************** + */ diff --git a/core/io/md5.h b/core/io/md5.h new file mode 100644 index 00000000000..e99d58b4438 --- /dev/null +++ b/core/io/md5.h @@ -0,0 +1,61 @@ +#ifndef MD5_H +#define MD5_H + +/* + ********************************************************************** + ** md5.h -- Header file for implementation of MD5 ** + ** RSA Data Security, Inc. MD5 Message Digest Algorithm ** + ** Created: 2/17/90 RLR ** + ** Revised: 12/27/90 SRD,AJ,BSK,JT Reference C version ** + ** Revised (for MD5): RLR 4/27/91 ** + ** -- G modified to have y&~z instead of y&z ** + ** -- FF, GG, HH modified to add in last register done ** + ** -- Access pattern: round 2 works mod 5, round 3 works mod 3 ** + ** -- distinct additive constant for each step ** + ** -- round 4 added, working mod 7 ** + ********************************************************************** + */ + +/* + ********************************************************************** + ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. ** + ** ** + ** License to copy and use this software is granted provided that ** + ** it is identified as the "RSA Data Security, Inc. MD5 Message ** + ** Digest Algorithm" in all material mentioning or referencing this ** + ** software or this function. ** + ** ** + ** License is also granted to make and use derivative works ** + ** provided that such works are identified as "derived from the RSA ** + ** Data Security, Inc. MD5 Message Digest Algorithm" in all ** + ** material mentioning or referencing the derived work. ** + ** ** + ** RSA Data Security, Inc. makes no representations concerning ** + ** either the merchantability of this software or the suitability ** + ** of this software for any particular purpose. It is provided "as ** + ** is" without express or implied warranty of any kind. ** + ** ** + ** These notices must be retained in any copies of any part of this ** + ** documentation and/or software. ** + ********************************************************************** + */ + +/* NOT typedef a 32 bit type */ + +#include "typedefs.h" + +/* Data structure for MD5 (Message Digest) computation */ +typedef struct { + uint32_t i[2]; /* number of _bits_ handled mod 2^64 */ + uint32_t buf[4]; /* scratch buffer */ + unsigned char in[64]; /* input buffer */ + unsigned char digest[16]; /* actual digest after MD5Final call */ +} MD5_CTX; + +void MD5Init (MD5_CTX *mdContext); +void MD5Update (MD5_CTX *mdContext,unsigned char *inBuf,unsigned int inLen); +void MD5Final (MD5_CTX *mdContext); + + + +#endif // MD5_H diff --git a/core/io/object_format_binary.cpp b/core/io/object_format_binary.cpp new file mode 100644 index 00000000000..c031f6e82b4 --- /dev/null +++ b/core/io/object_format_binary.cpp @@ -0,0 +1,1491 @@ +/*************************************************************************/ +/* object_format_binary.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 "object_format_binary.h" +#include "resource.h" +#include "io/resource_loader.h" +#include "print_string.h" +#include "object_type_db.h" +#include "globals.h" +#include "os/os.h" +#include "version.h" + + +#define print_bl(m_what) +#ifdef OLD_SCENE_FORMAT_ENABLED + + +enum { + + SECTION_RESOURCE=0, + SECTION_OBJECT=1, + SECTION_META_OBJECT=2, + SECTION_PROPERTY=3, + SECTION_END=4, + + //numbering must be different from variant, in case new variant types are added (variant must be always contiguous for jumptable optimization) + VARIANT_NIL=1, + VARIANT_BOOL=2, + VARIANT_INT=3, + VARIANT_REAL=4, + VARIANT_STRING=5, + VARIANT_VECTOR2=10, + VARIANT_RECT2=11, + VARIANT_VECTOR3=12, + VARIANT_PLANE=13, + VARIANT_QUAT=14, + VARIANT_AABB=15, + VARIANT_MATRIX3=16, + VARIANT_TRANSFORM=17, + VARIANT_MATRIX32=18, + VARIANT_COLOR=20, + VARIANT_IMAGE=21, + VARIANT_NODE_PATH=22, + VARIANT_RID=23, + VARIANT_OBJECT=24, + VARIANT_INPUT_EVENT=25, + VARIANT_DICTIONARY=26, + VARIANT_ARRAY=30, + VARIANT_RAW_ARRAY=31, + VARIANT_INT_ARRAY=32, + VARIANT_REAL_ARRAY=33, + VARIANT_STRING_ARRAY=34, + VARIANT_VECTOR3_ARRAY=35, + VARIANT_COLOR_ARRAY=36, + VARIANT_VECTOR2_ARRAY=37, + + IMAGE_ENCODING_EMPTY=0, + IMAGE_ENCODING_RAW=1, + IMAGE_ENCODING_PNG=2, //not yet + IMAGE_ENCODING_JPG=3, + + IMAGE_FORMAT_GRAYSCALE=0, + IMAGE_FORMAT_INTENSITY=1, + IMAGE_FORMAT_GRAYSCALE_ALPHA=2, + IMAGE_FORMAT_RGB=3, + IMAGE_FORMAT_RGBA=4, + IMAGE_FORMAT_INDEXED=5, + IMAGE_FORMAT_INDEXED_ALPHA=6, + IMAGE_FORMAT_BC1=7, + IMAGE_FORMAT_BC2=8, + IMAGE_FORMAT_BC3=9, + IMAGE_FORMAT_BC4=10, + IMAGE_FORMAT_BC5=11, + IMAGE_FORMAT_CUSTOM=12, + + OBJECT_EMPTY=0, + OBJECT_EXTERNAL_RESOURCE=1, + OBJECT_INTERNAL_RESOURCE=2, + + +}; + + +void ObjectFormatSaverBinary::_pad_buffer(int p_bytes) { + + int extra = 4-(p_bytes%4); + if (extra<4) { + for(int i=0;istore_8(0); //pad to 32 + } + +} + + +void ObjectFormatSaverBinary::write_property(int p_idx,const Variant& p_property) { + + f->store_32(SECTION_PROPERTY); + f->store_32(p_idx); + + switch(p_property.get_type()) { + + case Variant::NIL: { + + f->store_32(VARIANT_NIL); + // don't store anything + } break; + case Variant::BOOL: { + + f->store_32(VARIANT_BOOL); + bool val=p_property; + f->store_32(val); + } break; + case Variant::INT: { + + f->store_32(VARIANT_INT); + int val=p_property; + f->store_32(val); + } break; + case Variant::REAL: { + + f->store_32(VARIANT_REAL); + real_t val=p_property; + f->store_real(val); + + } break; + case Variant::STRING: { + + f->store_32(VARIANT_STRING); + String val=p_property; + save_unicode_string(val); + + } break; + case Variant::VECTOR2: { + + f->store_32(VARIANT_VECTOR2); + Vector2 val=p_property; + f->store_real(val.x); + f->store_real(val.y); + + } break; + case Variant::RECT2: { + + f->store_32(VARIANT_RECT2); + Rect2 val=p_property; + f->store_real(val.pos.x); + f->store_real(val.pos.y); + f->store_real(val.size.x); + f->store_real(val.size.y); + + } break; + case Variant::VECTOR3: { + + f->store_32(VARIANT_VECTOR3); + Vector3 val=p_property; + f->store_real(val.x); + f->store_real(val.y); + f->store_real(val.z); + + } break; + case Variant::PLANE: { + + f->store_32(VARIANT_PLANE); + Plane val=p_property; + f->store_real(val.normal.x); + f->store_real(val.normal.y); + f->store_real(val.normal.z); + f->store_real(val.d); + + } break; + case Variant::QUAT: { + + f->store_32(VARIANT_QUAT); + Quat val=p_property; + f->store_real(val.x); + f->store_real(val.y); + f->store_real(val.z); + f->store_real(val.w); + + } break; + case Variant::_AABB: { + + f->store_32(VARIANT_AABB); + AABB val=p_property; + f->store_real(val.pos.x); + f->store_real(val.pos.y); + f->store_real(val.pos.z); + f->store_real(val.size.x); + f->store_real(val.size.y); + f->store_real(val.size.z); + + } break; + case Variant::MATRIX32: { + + f->store_32(VARIANT_MATRIX32); + Matrix32 val=p_property; + f->store_real(val.elements[0].x); + f->store_real(val.elements[0].y); + f->store_real(val.elements[1].x); + f->store_real(val.elements[1].y); + f->store_real(val.elements[2].x); + f->store_real(val.elements[2].y); + + } break; + case Variant::MATRIX3: { + + f->store_32(VARIANT_MATRIX3); + Matrix3 val=p_property; + f->store_real(val.elements[0].x); + f->store_real(val.elements[0].y); + f->store_real(val.elements[0].z); + f->store_real(val.elements[1].x); + f->store_real(val.elements[1].y); + f->store_real(val.elements[1].z); + f->store_real(val.elements[2].x); + f->store_real(val.elements[2].y); + f->store_real(val.elements[2].z); + + } break; + case Variant::TRANSFORM: { + + f->store_32(VARIANT_TRANSFORM); + Transform val=p_property; + f->store_real(val.basis.elements[0].x); + f->store_real(val.basis.elements[0].y); + f->store_real(val.basis.elements[0].z); + f->store_real(val.basis.elements[1].x); + f->store_real(val.basis.elements[1].y); + f->store_real(val.basis.elements[1].z); + f->store_real(val.basis.elements[2].x); + f->store_real(val.basis.elements[2].y); + f->store_real(val.basis.elements[2].z); + f->store_real(val.origin.x); + f->store_real(val.origin.y); + f->store_real(val.origin.z); + + } break; + case Variant::COLOR: { + + f->store_32(VARIANT_COLOR); + Color val=p_property; + f->store_real(val.r); + f->store_real(val.g); + f->store_real(val.b); + f->store_real(val.a); + + } break; + case Variant::IMAGE: { + + f->store_32(VARIANT_IMAGE); + Image val =p_property; + if (val.empty()) { + f->store_32(IMAGE_ENCODING_EMPTY); + break; + } + f->store_32(IMAGE_ENCODING_RAW); //raw encoding + f->store_32(val.get_width()); + f->store_32(val.get_height()); + f->store_32(val.get_mipmaps()); + switch(val.get_format()) { + + case Image::FORMAT_GRAYSCALE: f->store_32(IMAGE_FORMAT_GRAYSCALE ); break; ///< one byte per pixel: f->store_32(IMAGE_FORMAT_ ); break; 0-255 + case Image::FORMAT_INTENSITY: f->store_32(IMAGE_FORMAT_INTENSITY ); break; ///< one byte per pixel: f->store_32(IMAGE_FORMAT_ ); break; 0-255 + case Image::FORMAT_GRAYSCALE_ALPHA: f->store_32(IMAGE_FORMAT_GRAYSCALE_ALPHA ); break; ///< two bytes per pixel: f->store_32(IMAGE_FORMAT_ ); break; 0-255. alpha 0-255 + case Image::FORMAT_RGB: f->store_32(IMAGE_FORMAT_RGB ); break; ///< one byte R: f->store_32(IMAGE_FORMAT_ ); break; one byte G: f->store_32(IMAGE_FORMAT_ ); break; one byte B + case Image::FORMAT_RGBA: f->store_32(IMAGE_FORMAT_RGBA ); break; ///< one byte R: f->store_32(IMAGE_FORMAT_ ); break; one byte G: f->store_32(IMAGE_FORMAT_ ); break; one byte B: f->store_32(IMAGE_FORMAT_ ); break; one byte A + case Image::FORMAT_INDEXED: f->store_32(IMAGE_FORMAT_INDEXED ); break; ///< index byte 0-256: f->store_32(IMAGE_FORMAT_ ); break; and after image end: f->store_32(IMAGE_FORMAT_ ); break; 256*3 bytes of palette + case Image::FORMAT_INDEXED_ALPHA: f->store_32(IMAGE_FORMAT_INDEXED_ALPHA ); break; ///< index byte 0-256: f->store_32(IMAGE_FORMAT_ ); break; and after image end: f->store_32(IMAGE_FORMAT_ ); break; 256*4 bytes of palette (alpha) + case Image::FORMAT_BC1: f->store_32(IMAGE_FORMAT_BC1 ); break; // DXT1 + case Image::FORMAT_BC2: f->store_32(IMAGE_FORMAT_BC2 ); break; // DXT3 + case Image::FORMAT_BC3: f->store_32(IMAGE_FORMAT_BC3 ); break; // DXT5 + case Image::FORMAT_BC4: f->store_32(IMAGE_FORMAT_BC4 ); break; // ATI1 + case Image::FORMAT_BC5: f->store_32(IMAGE_FORMAT_BC5 ); break; // ATI2 + case Image::FORMAT_CUSTOM: f->store_32(IMAGE_FORMAT_CUSTOM ); break; + default: {} + + } + + int dlen = val.get_data().size(); + f->store_32(dlen); + DVector::Read r = val.get_data().read(); + f->store_buffer(r.ptr(),dlen); + _pad_buffer(dlen); + + } break; + case Variant::NODE_PATH: { + f->store_32(VARIANT_NODE_PATH); + save_unicode_string(p_property); + } break; + case Variant::_RID: { + + f->store_32(VARIANT_RID); + WARN_PRINT("Can't save RIDs"); + RID val = p_property; + f->store_32(val.get_id()); + } break; + case Variant::OBJECT: { + + f->store_32(VARIANT_OBJECT); + RES res = p_property; + if (res.is_null()) { + f->store_32(OBJECT_EMPTY); + return; // don't save it + } + + if (res->get_path().length() && res->get_path().find("::")==-1) { + f->store_32(OBJECT_EXTERNAL_RESOURCE); + save_unicode_string(res->get_type()); + String path=relative_paths?local_path.path_to_file(res->get_path()):res->get_path(); + save_unicode_string(path); + } else { + + if (!resource_map.has(res)) { + f->store_32(OBJECT_EMPTY); + ERR_EXPLAIN("Resource was not pre cached for the resource section, bug?"); + ERR_FAIL(); + } + + f->store_32(OBJECT_INTERNAL_RESOURCE); + f->store_32(resource_map[res]); + //internal resource + } + + + } break; + case Variant::INPUT_EVENT: { + + f->store_32(VARIANT_INPUT_EVENT); + WARN_PRINT("Can't save InputEvent (maybe it could..)"); + } break; + case Variant::DICTIONARY: { + + f->store_32(VARIANT_DICTIONARY); + Dictionary d = p_property; + f->store_32(d.size()); + + List keys; + d.get_key_list(&keys); + + for(List::Element *E=keys.front();E;E=E->next()) { + + //if (!_check_type(dict[E->get()])) + // continue; + + write_property(0,E->get()); + write_property(0,d[E->get()]); + } + + + } break; + case Variant::ARRAY: { + + f->store_32(VARIANT_ARRAY); + Array a=p_property; + f->store_32(a.size()); + for(int i=0;istore_32(VARIANT_RAW_ARRAY); + DVector arr = p_property; + int len=arr.size(); + f->store_32(len); + DVector::Read r = arr.read(); + f->store_buffer(r.ptr(),len); + _pad_buffer(len); + + } break; + case Variant::INT_ARRAY: { + + f->store_32(VARIANT_INT_ARRAY); + DVector arr = p_property; + int len=arr.size(); + f->store_32(len); + DVector::Read r = arr.read(); + for(int i=0;istore_32(r[i]); + + } break; + case Variant::REAL_ARRAY: { + + f->store_32(VARIANT_REAL_ARRAY); + DVector arr = p_property; + int len=arr.size(); + f->store_32(len); + DVector::Read r = arr.read(); + for(int i=0;istore_real(r[i]); + } + + } break; + case Variant::STRING_ARRAY: { + + f->store_32(VARIANT_STRING_ARRAY); + DVector arr = p_property; + int len=arr.size(); + f->store_32(len); + DVector::Read r = arr.read(); + for(int i=0;istore_32(VARIANT_VECTOR3_ARRAY); + DVector arr = p_property; + int len=arr.size(); + f->store_32(len); + DVector::Read r = arr.read(); + for(int i=0;istore_real(r[i].x); + f->store_real(r[i].y); + f->store_real(r[i].z); + } + + } break; + case Variant::VECTOR2_ARRAY: { + + f->store_32(VARIANT_VECTOR2_ARRAY); + DVector arr = p_property; + int len=arr.size(); + f->store_32(len); + DVector::Read r = arr.read(); + for(int i=0;istore_real(r[i].x); + f->store_real(r[i].y); + } + + } break; + case Variant::COLOR_ARRAY: { + + f->store_32(VARIANT_COLOR_ARRAY); + DVector arr = p_property; + int len=arr.size(); + f->store_32(len); + DVector::Read r = arr.read(); + for(int i=0;istore_real(r[i].r); + f->store_real(r[i].g); + f->store_real(r[i].b); + f->store_real(r[i].a); + } + + } break; + default: { + + ERR_EXPLAIN("Invalid variant"); + ERR_FAIL(); + } + } +} + + +void ObjectFormatSaverBinary::_find_resources(const Variant& p_variant) { + + + switch(p_variant.get_type()) { + case Variant::OBJECT: { + + + RES res = p_variant.operator RefPtr(); + + if (res.is_null()) + return; + + if (!bundle_resources && res->get_path().length() && res->get_path().find("::") == -1 ) + return; + + if (resource_map.has(res)) + return; + + List property_list; + + res->get_property_list(&property_list); + + for(List::Element *E=property_list.front();E;E=E->next()) { + + if (E->get().usage&PROPERTY_USAGE_STORAGE || (bundle_resources && E->get().usage&PROPERTY_USAGE_BUNDLE)) { + + _find_resources(res->get(E->get().name)); + } + } + + SavedObject *so = memnew( SavedObject ); + _save_obj(res.ptr(),so); + so->meta=res.get_ref_ptr(); + + resource_map[ res ] = saved_resources.size(); + saved_resources.push_back(so); + + } break; + + case Variant::ARRAY: { + + Array varray=p_variant; + int len=varray.size(); + for(int i=0;i keys; + d.get_key_list(&keys); + for(List::Element *E=keys.front();E;E=E->next()) { + + Variant v = d[E->get()]; + _find_resources(v); + } + } break; + default: {} + } + +} +Error ObjectFormatSaverBinary::_save_obj(const Object *p_object,SavedObject *so) { + + if (optimizer.is_valid()) { + //use optimizer + + List props; + optimizer->get_property_list(p_object,&props); + + for(List::Element *E=props.front();E;E=E->next()) { + + if (skip_editor && String(E->get().name).begins_with("__editor")) + continue; + _find_resources(E->get().value); + SavedObject::SavedProperty sp; + + sp.name_idx=get_string_index(E->get().name); + sp.value=E->get().value; + so->properties.push_back(sp); + } + + } else { + //use classic way + List property_list; + p_object->get_property_list( &property_list ); + + for(List::Element *E=property_list.front();E;E=E->next()) { + + if (skip_editor && E->get().name.begins_with("__editor")) + continue; + if (E->get().usage&PROPERTY_USAGE_STORAGE || (bundle_resources && E->get().usage&PROPERTY_USAGE_BUNDLE)) { + + SavedObject::SavedProperty sp; + sp.name_idx=get_string_index(E->get().name); + sp.value = p_object->get(E->get().name); + _find_resources(sp.value); + so->properties.push_back(sp); + } + } + } + + return OK; + +} + +Error ObjectFormatSaverBinary::save(const Object *p_object,const Variant &p_meta) { + + ERR_FAIL_COND_V(!f,ERR_UNCONFIGURED); + ERR_EXPLAIN("write_object should supply either an object, a meta, or both"); + ERR_FAIL_COND_V(!p_object && p_meta.get_type()==Variant::NIL, ERR_INVALID_PARAMETER); + + SavedObject *so = memnew( SavedObject ); + + if (p_object) + so->type=p_object->get_type(); + + _find_resources(p_meta); + so->meta=p_meta; + Error err = _save_obj(p_object,so); + ERR_FAIL_COND_V( err, ERR_INVALID_DATA ); + + saved_objects.push_back(so); + + return OK; +} + +void ObjectFormatSaverBinary::save_unicode_string(const String& p_string) { + + + CharString utf8 = p_string.utf8(); + f->store_32(utf8.length()+1); + f->store_buffer((const uint8_t*)utf8.get_data(),utf8.length()+1); +} + +ObjectFormatSaverBinary::ObjectFormatSaverBinary(FileAccess *p_file,const String& p_magic,const String& p_local_path,uint32_t p_flags,const Ref& p_optimizer) { + + optimizer=p_optimizer; + relative_paths=p_flags&ObjectSaver::FLAG_RELATIVE_PATHS; + skip_editor=p_flags&ObjectSaver::FLAG_OMIT_EDITOR_PROPERTIES; + bundle_resources=p_flags&ObjectSaver::FLAG_BUNDLE_RESOURCES; + big_endian=p_flags&ObjectSaver::FLAG_SAVE_BIG_ENDIAN; + f=p_file; // should be already opened + local_path=p_local_path; + magic=p_magic; + + bin_meta_idx = get_string_index("__bin_meta__"); //is often used, so create +} + +int ObjectFormatSaverBinary::get_string_index(const String& p_string) { + + StringName s=p_string; + if (string_map.has(s)) + return string_map[s]; + + string_map[s]=strings.size(); + strings.push_back(s); + return strings.size()-1; +} + +ObjectFormatSaverBinary::~ObjectFormatSaverBinary() { + + + static const uint8_t header[4]={'O','B','D','B'}; + f->store_buffer(header,4); + if (big_endian) { + f->store_32(1); + f->set_endian_swap(true); + } else + f->store_32(0); + + f->store_32(0); //64 bits file, false for now + f->store_32(VERSION_MAJOR); + f->store_32(VERSION_MINOR); + save_unicode_string(magic); + for(int i=0;i<16;i++) + f->store_32(0); // reserved + + f->store_32(strings.size()); //string table size + for(int i=0;imeta; + ERR_CONTINUE(!resource_map.has(res)); + + f->store_32(SECTION_RESOURCE); + size_t skip_pos = f->get_pos(); + f->store_64(0); // resource skip seek pos + save_unicode_string(res->get_type()); + + if (res->get_path().length() && res->get_path().find("::") == -1 ) + save_unicode_string(res->get_path()); + else + save_unicode_string("local://"+itos(i)); + + + + List::Element *SE = so->properties.front(); + + while(SE) { + + write_property(SE->get().name_idx,SE->get().value); + SE=SE->next(); + } + + f->store_32(SECTION_END); + + size_t end=f->get_pos(); + f->seek(skip_pos); + f->store_64(end); + f->seek_end(); + + memdelete( so ); + } + + if (!saved_objects.empty()) { + + + for(List::Element *E=saved_objects.front();E;E=E->next()) { + + SavedObject *so = E->get(); + + + size_t section_end; + + if (so->type!="") { + f->store_32(SECTION_OBJECT); + section_end=f->get_pos(); + f->store_64(0); //section end + save_unicode_string(so->type); + } else { + f->store_32(SECTION_META_OBJECT); + section_end=f->get_pos(); + f->store_64(0); //section end + } + + + if (so->meta.get_type()!=Variant::NIL) + write_property(bin_meta_idx,so->meta); + + List::Element *SE = so->properties.front(); + + while(SE) { + + write_property(SE->get().name_idx,SE->get().value); + SE=SE->next(); + } + + f->store_32(SECTION_END); + + size_t end=f->get_pos(); + f->seek(section_end); + f->store_64(end); + f->seek_end(); + + memdelete(so); //no longer needed + } + + + } + + f->store_32(SECTION_END); + + f->close(); + memdelete(f); +} + + +ObjectFormatSaver* ObjectFormatSaverInstancerBinary::instance(const String& p_file,const String& p_magic,uint32_t p_flags,const Ref& p_optimizer) { + + FileAccess *f = FileAccess::open(p_file, FileAccess::WRITE); + + ERR_FAIL_COND_V( !f, NULL ); + String local_path = Globals::get_singleton()->localize_path(p_file); + + return memnew( ObjectFormatSaverBinary( f, p_magic,local_path,p_flags,p_optimizer ) ); +} + +void ObjectFormatSaverInstancerBinary::get_recognized_extensions(List *p_extensions) const { + + p_extensions->push_back("bin"); +} + + +ObjectFormatSaverInstancerBinary::~ObjectFormatSaverInstancerBinary() { + + +} + + + +/************************************************/ +/************************************************/ +/************************************************/ +/************************************************/ +/************************************************/ + + +void ObjectFormatLoaderBinary::_advance_padding(uint32_t p_len) { + + uint32_t extra = 4-(p_len%4); + if (extra<4) { + for(uint32_t i=0;iget_8(); //pad to 32 + } + +} + +Error ObjectFormatLoaderBinary::parse_property(Variant& r_v, int &r_index) { + + + uint32_t prop = f->get_32(); + if (prop==SECTION_END) + return ERR_FILE_EOF; + ERR_FAIL_COND_V(prop!=SECTION_PROPERTY,ERR_FILE_CORRUPT); + + r_index = f->get_32(); + + uint32_t type = f->get_32(); + print_bl("find property of type: "+itos(type)); + + + switch(type) { + + case VARIANT_NIL: { + + r_v=Variant(); + } break; + case VARIANT_BOOL: { + + r_v=bool(f->get_32()); + } break; + case VARIANT_INT: { + + r_v=int(f->get_32()); + } break; + case VARIANT_REAL: { + + r_v=f->get_real(); + } break; + case VARIANT_STRING: { + + r_v=get_unicode_string(); + } break; + case VARIANT_VECTOR2: { + + Vector2 v; + v.x=f->get_real(); + v.y=f->get_real(); + r_v=v; + + } break; + case VARIANT_RECT2: { + + Rect2 v; + v.pos.x=f->get_real(); + v.pos.y=f->get_real(); + v.size.x=f->get_real(); + v.size.y=f->get_real(); + r_v=v; + + } break; + case VARIANT_VECTOR3: { + + Vector3 v; + v.x=f->get_real(); + v.y=f->get_real(); + v.z=f->get_real(); + r_v=v; + } break; + case VARIANT_PLANE: { + + Plane v; + v.normal.x=f->get_real(); + v.normal.y=f->get_real(); + v.normal.z=f->get_real(); + v.d=f->get_real(); + r_v=v; + } break; + case VARIANT_QUAT: { + Quat v; + v.x=f->get_real(); + v.y=f->get_real(); + v.z=f->get_real(); + v.w=f->get_real(); + r_v=v; + + } break; + case VARIANT_AABB: { + + AABB v; + v.pos.x=f->get_real(); + v.pos.y=f->get_real(); + v.pos.z=f->get_real(); + v.size.x=f->get_real(); + v.size.y=f->get_real(); + v.size.z=f->get_real(); + r_v=v; + + } break; + case VARIANT_MATRIX32: { + + Matrix32 v; + v.elements[0].x=f->get_real(); + v.elements[0].y=f->get_real(); + v.elements[1].x=f->get_real(); + v.elements[1].y=f->get_real(); + v.elements[2].x=f->get_real(); + v.elements[2].y=f->get_real(); + r_v=v; + + } break; + case VARIANT_MATRIX3: { + + Matrix3 v; + v.elements[0].x=f->get_real(); + v.elements[0].y=f->get_real(); + v.elements[0].z=f->get_real(); + v.elements[1].x=f->get_real(); + v.elements[1].y=f->get_real(); + v.elements[1].z=f->get_real(); + v.elements[2].x=f->get_real(); + v.elements[2].y=f->get_real(); + v.elements[2].z=f->get_real(); + r_v=v; + + } break; + case VARIANT_TRANSFORM: { + + Transform v; + v.basis.elements[0].x=f->get_real(); + v.basis.elements[0].y=f->get_real(); + v.basis.elements[0].z=f->get_real(); + v.basis.elements[1].x=f->get_real(); + v.basis.elements[1].y=f->get_real(); + v.basis.elements[1].z=f->get_real(); + v.basis.elements[2].x=f->get_real(); + v.basis.elements[2].y=f->get_real(); + v.basis.elements[2].z=f->get_real(); + v.origin.x=f->get_real(); + v.origin.y=f->get_real(); + v.origin.z=f->get_real(); + r_v=v; + } break; + case VARIANT_COLOR: { + + Color v; + v.r=f->get_real(); + v.g=f->get_real(); + v.b=f->get_real(); + v.a=f->get_real(); + r_v=v; + + } break; + case VARIANT_IMAGE: { + + + uint32_t encoding = f->get_32(); + if (encoding==IMAGE_ENCODING_EMPTY) { + r_v=Variant(); + break; + } + + if (encoding==IMAGE_ENCODING_RAW) { + uint32_t width = f->get_32(); + uint32_t height = f->get_32(); + uint32_t mipmaps = f->get_32(); + uint32_t format = f->get_32(); + Image::Format fmt; + switch(format) { + + case IMAGE_FORMAT_GRAYSCALE: { fmt=Image::FORMAT_GRAYSCALE; } break; + case IMAGE_FORMAT_INTENSITY: { fmt=Image::FORMAT_INTENSITY; } break; + case IMAGE_FORMAT_GRAYSCALE_ALPHA: { fmt=Image::FORMAT_GRAYSCALE_ALPHA; } break; + case IMAGE_FORMAT_RGB: { fmt=Image::FORMAT_RGB; } break; + case IMAGE_FORMAT_RGBA: { fmt=Image::FORMAT_RGBA; } break; + case IMAGE_FORMAT_INDEXED: { fmt=Image::FORMAT_INDEXED; } break; + case IMAGE_FORMAT_INDEXED_ALPHA: { fmt=Image::FORMAT_INDEXED_ALPHA; } break; + case IMAGE_FORMAT_BC1: { fmt=Image::FORMAT_BC1; } break; + case IMAGE_FORMAT_BC2: { fmt=Image::FORMAT_BC2; } break; + case IMAGE_FORMAT_BC3: { fmt=Image::FORMAT_BC3; } break; + case IMAGE_FORMAT_BC4: { fmt=Image::FORMAT_BC4; } break; + case IMAGE_FORMAT_BC5: { fmt=Image::FORMAT_BC5; } break; + case IMAGE_FORMAT_CUSTOM: { fmt=Image::FORMAT_CUSTOM; } break; + default: { + + ERR_FAIL_V(ERR_FILE_CORRUPT); + } + + } + + + uint32_t datalen = f->get_32(); + + print_bl("width: "+itos(width)); + print_bl("height: "+itos(height)); + print_bl("mipmaps: "+itos(mipmaps)); + print_bl("format: "+itos(format)); + print_bl("datalen: "+itos(datalen)); + + DVector imgdata; + imgdata.resize(datalen); + DVector::Write w = imgdata.write(); + f->get_buffer(w.ptr(),datalen); + _advance_padding(datalen); + w=DVector::Write(); + + r_v=Image(width,height,mipmaps,fmt,imgdata); + } + + + } break; + case VARIANT_NODE_PATH: { + + r_v=NodePath(get_unicode_string()); + } break; + case VARIANT_RID: { + + r_v=f->get_32(); + } break; + case VARIANT_OBJECT: { + + uint32_t type=f->get_32(); + + switch(type) { + + case OBJECT_EMPTY: { + //do none + + } break; + case OBJECT_INTERNAL_RESOURCE: { + uint32_t index=f->get_32(); + String path = local_path+"::"+itos(index); + RES res = ResourceLoader::load(path); + if (res.is_null()) { + WARN_PRINT(String("Couldn't load resource: "+path).utf8().get_data()); + } + r_v=res; + + } break; + case OBJECT_EXTERNAL_RESOURCE: { + + String type = get_unicode_string(); + String path = get_unicode_string(); + + if (path.find("://")==-1 && path.is_rel_path()) { + // path is relative to file being loaded, so convert to a resource path + path=Globals::get_singleton()->localize_path(local_path.get_base_dir()+"/"+path); + + } + + RES res=ResourceLoader::load(path,type); + + if (res.is_null()) { + WARN_PRINT(String("Couldn't load resource: "+path).utf8().get_data()); + } + r_v=res; + + } break; + default: { + + ERR_FAIL_V(ERR_FILE_CORRUPT); + } break; + } + + } break; + case VARIANT_INPUT_EVENT: { + + } break; + case VARIANT_DICTIONARY: { + + int len=f->get_32(); + Dictionary d; + for(int i=0;iget_32(); + Array a; + a.resize(len); + for(int i=0;iget_32(); + + DVector array; + array.resize(len); + DVector::Write w = array.write(); + f->get_buffer(w.ptr(),len); + _advance_padding(len); + w=DVector::Write(); + r_v=array; + + } break; + case VARIANT_INT_ARRAY: { + + uint32_t len = f->get_32(); + + DVector array; + array.resize(len); + DVector::Write w = array.write(); + f->get_buffer((uint8_t*)w.ptr(),len*4); + w=DVector::Write(); + r_v=array; + } break; + case VARIANT_REAL_ARRAY: { + + uint32_t len = f->get_32(); + + DVector array; + array.resize(len); + DVector::Write w = array.write(); + f->get_buffer((uint8_t*)w.ptr(),len*sizeof(real_t)); + w=DVector::Write(); + r_v=array; + } break; + case VARIANT_STRING_ARRAY: { + + uint32_t len = f->get_32(); + DVector array; + array.resize(len); + DVector::Write w = array.write(); + for(int i=0;i::Write(); + r_v=array; + + + } break; + case VARIANT_VECTOR2_ARRAY: { + + uint32_t len = f->get_32(); + + DVector array; + array.resize(len); + DVector::Write w = array.write(); + if (sizeof(Vector2)==8) { + f->get_buffer((uint8_t*)w.ptr(),len*sizeof(real_t)*2); + } else { + ERR_EXPLAIN("Vector2 size is NOT 8!"); + ERR_FAIL_V(ERR_UNAVAILABLE); + } + w=DVector::Write(); + r_v=array; + + } break; + case VARIANT_VECTOR3_ARRAY: { + + uint32_t len = f->get_32(); + + DVector array; + array.resize(len); + DVector::Write w = array.write(); + if (sizeof(Vector3)==12) { + f->get_buffer((uint8_t*)w.ptr(),len*sizeof(real_t)*3); + } else { + ERR_EXPLAIN("Vector3 size is NOT 12!"); + ERR_FAIL_V(ERR_UNAVAILABLE); + } + w=DVector::Write(); + r_v=array; + + } break; + case VARIANT_COLOR_ARRAY: { + + uint32_t len = f->get_32(); + + DVector array; + array.resize(len); + DVector::Write w = array.write(); + if (sizeof(Color)==16) { + f->get_buffer((uint8_t*)w.ptr(),len*sizeof(real_t)*4); + } else { + ERR_EXPLAIN("Color size is NOT 16!"); + ERR_FAIL_V(ERR_UNAVAILABLE); + } + w=DVector::Write(); + r_v=array; + } break; + + default: { + ERR_FAIL_V(ERR_FILE_CORRUPT); + } break; + } + + + + return OK; //never reach anyway + +} + +Error ObjectFormatLoaderBinary::load(Object **p_object,Variant &p_meta) { + + + + while(true) { + + if (f->eof_reached()) { + ERR_EXPLAIN("Premature end of file at: "+local_path); + ERR_FAIL_V(ERR_FILE_CORRUPT); + } + + RES resource; + Object *obj=NULL; + bool meta=false; + + uint32_t section = f->get_32(); + + switch(section) { + + + case SECTION_RESOURCE: { + + print_bl("resource found"); + + size_t section_end = f->get_64(); + print_bl("section end: "+itos(section_end)); + String type = get_unicode_string(); + String path = get_unicode_string(); + print_bl("path: "+path); + + if (path.begins_with("local://")) { + //built-in resource (but really external) + path=path.replace("local://",local_path+"::"); + } + + if (ResourceCache::has(path)) { + f->seek(section_end); + continue; + } + + //load properties + + + obj = ObjectTypeDB::instance(type); + if (!obj) { + ERR_EXPLAIN("Object of unrecognized type '"+type+"' in file: "+type); + } + + ERR_FAIL_COND_V(!obj,ERR_FILE_CORRUPT); + + Resource *r = obj->cast_to(); + if (!r) { + memdelete(obj); //bye + ERR_EXPLAIN("Object type in resource field not a resource, type is: "+obj->get_type()); + ERR_FAIL_COND_V(!obj->cast_to(),ERR_FILE_CORRUPT); + } + + resource = RES( r ); + r->set_path(path); + } break; + case SECTION_META_OBJECT: + meta=true; + print_bl("meta found"); + + case SECTION_OBJECT: { + + uint64_t section_end = f->get_64(); + + if (!meta) { + print_bl("object"); + + String type = get_unicode_string(); + if (ObjectTypeDB::can_instance(type)) { + obj = ObjectTypeDB::instance(type); + if (!obj) { + ERR_EXPLAIN("Object of unrecognized type in file: "+type); + } + ERR_FAIL_COND_V(!obj,ERR_FILE_CORRUPT); + } else { + + f->seek(section_end); + return ERR_SKIP; + }; + + + } + + + } break; + case SECTION_END: { + + + return ERR_FILE_EOF; + } break; + + default: { + + ERR_EXPLAIN("Invalid Section ID '"+itos(section)+"' in file: "+local_path); + ERR_FAIL_V(ERR_FILE_CORRUPT); + + } + + } + + + //load properties + + while(true) { + + int name_idx; + Variant v; + Error err; + err = parse_property(v,name_idx); + + print_bl("prop idx "+itos(name_idx)+" value: "+String(v)); + + if (err==ERR_FILE_EOF) + break; + + if (err!=OK) { + ERR_EXPLAIN("File Corrupted"); + ERR_FAIL_COND_V(err!=OK,ERR_FILE_CORRUPT); + } + + + if (resource.is_null() && name_idx==0) { //0 is __bin_meta__ + + p_meta=v; + continue; + } else if (!obj) { + + ERR_EXPLAIN("Normal property found in meta object."); + ERR_FAIL_V(ERR_FILE_CORRUPT); + } + + Map::Element *E=string_map.find(name_idx); + if (!E) { + ERR_EXPLAIN("Property ID has no matching name: "+itos(name_idx)); + ERR_FAIL_V(ERR_FILE_CORRUPT); + } + + obj->set(E->get(),v); + } + + if (!obj) { + *p_object=NULL; + return OK; // it was a meta object + } + + if (resource.is_null()) { + + //regular object + *p_object=obj; + return OK; + } else { + + resource_cache.push_back(resource); //keep it in mem until finished loading + } + + } +} + + +ObjectFormatLoaderBinary::~ObjectFormatLoaderBinary() { + + if (f) { + if (f->is_open()) + f->close(); + memdelete(f); + } +} + + +String ObjectFormatLoaderBinary::get_unicode_string() { + + uint32_t len = f->get_32(); + if (len>str_buf.size()) { + str_buf.resize(len); + } + f->get_buffer((uint8_t*)&str_buf[0],len); + String s; + s.parse_utf8(&str_buf[0]); + return s; +} + +ObjectFormatLoaderBinary::ObjectFormatLoaderBinary(FileAccess *p_f,bool p_endian_swap,bool p_use64) { + + f=p_f; + endian_swap=p_endian_swap; + use_real64=p_use64; + + //load string table + uint32_t string_table_size = f->get_32(); + print_bl("string table size: "+itos(string_table_size)); + for(int i=0;iget_buffer(header,4); + if (header[0]!='O' || header[1]!='B' || header[2]!='D' || header[3]!='B') { + + ERR_EXPLAIN("File not in valid binary format: "+p_file); + ERR_FAIL_V(NULL); + } + + uint32_t big_endian = f->get_32(); +#ifdef BIG_ENDIAN_ENABLED + bool endian_swap = !big_endian; +#else + bool endian_swap = big_endian; +#endif + + bool use_real64 = f->get_32(); + + f->set_endian_swap(big_endian!=0); //read big endian if saved as big endian + + uint32_t ver_major=f->get_32(); + uint32_t ver_minor=f->get_32(); + + print_bl("big endian: "+itos(big_endian)); + print_bl("endian swap: "+itos(endian_swap)); + print_bl("real64: "+itos(use_real64)); + print_bl("major: "+itos(ver_major)); + print_bl("minor: "+itos(ver_minor)); + + if (ver_major>VERSION_MAJOR || (ver_major==VERSION_MAJOR && ver_minor>VERSION_MINOR)) { + + f->close(); + memdelete(f); + ERR_EXPLAIN("File Format '"+itos(ver_major)+"."+itos(ver_minor)+"' is too new! Please upgrade to a a new engine version: "+p_file); + ERR_FAIL_V(NULL); + + } + + uint32_t magic_len = f->get_32(); + Vector magic; + magic.resize(magic_len); + f->get_buffer((uint8_t*)&magic[0],magic_len); + String magic_str; + magic_str.parse_utf8(&magic[0]); + + print_bl("magic: "+magic_str); + if (magic_str!=p_magic) { + + f->close(); + memdelete(f); + ERR_EXPLAIN("File magic mismatch, found '"+magic_str+"' in : "+p_file); + ERR_FAIL_V(NULL); + } + + print_bl("skipping 32"); + for(int i=0;i<16;i++) + f->get_32(); //skip a few reserved fields + + if (f->eof_reached()) { + + f->close(); + memdelete(f); + ERR_EXPLAIN("Premature End Of File: "+p_file); + ERR_FAIL_V(NULL); + + } + + print_bl("creating loader"); + ObjectFormatLoaderBinary *loader = memnew( ObjectFormatLoaderBinary(f,endian_swap,use_real64) ); + loader->local_path=p_file; + + return loader; +} + +void ObjectFormatLoaderInstancerBinary::get_recognized_extensions(List *p_extensions) const { + + p_extensions->push_back("bin"); +} + + +#endif diff --git a/core/io/object_format_binary.h b/core/io/object_format_binary.h new file mode 100644 index 00000000000..aaf6bf357a7 --- /dev/null +++ b/core/io/object_format_binary.h @@ -0,0 +1,158 @@ +/*************************************************************************/ +/* object_format_binary.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 OBJECT_FORMAT_BINARY_H +#define OBJECT_FORMAT_BINARY_H + +#include "object_loader.h" +#include "object_saver_base.h" +#include "dvector.h" +#include "core/os/file_access.h" + +#ifdef OLD_SCENE_FORMAT_ENABLED +/** + @author Juan Linietsky +*/ + + +class ObjectFormatSaverBinary : public ObjectFormatSaver { + + String local_path; + + + Ref optimizer; + + bool relative_paths; + bool bundle_resources; + bool skip_editor; + bool big_endian; + int bin_meta_idx; + FileAccess *f; + String magic; + Map resource_map; + Map string_map; + Vector strings; + + struct SavedObject { + + Variant meta; + String type; + + + struct SavedProperty { + + int name_idx; + Variant value; + }; + + List properties; + }; + + + int get_string_index(const String& p_string); + void save_unicode_string(const String& p_string); + + List saved_objects; + List saved_resources; + + void _pad_buffer(int p_bytes); + Error _save_obj(const Object *p_object,SavedObject *so); + void _find_resources(const Variant& p_variant); + void write_property(int p_idx,const Variant& p_property); + + +public: + + virtual Error save(const Object *p_object,const Variant &p_meta); + + ObjectFormatSaverBinary(FileAccess *p_file,const String& p_magic,const String& p_local_path,uint32_t p_flags,const Ref& p_optimizer); + ~ObjectFormatSaverBinary(); +}; + +class ObjectFormatSaverInstancerBinary : public ObjectFormatSaverInstancer { +public: + + virtual ObjectFormatSaver* instance(const String& p_file,const String& p_magic,uint32_t p_flags=0,const Ref& p_optimizer=Ref()); + virtual void get_recognized_extensions(List *p_extensions) const; + + virtual ~ObjectFormatSaverInstancerBinary(); +}; + + + + +/***********************************/ +/***********************************/ +/***********************************/ +/***********************************/ + +class ObjectFormatLoaderBinary : public ObjectFormatLoader { + + String local_path; + + FileAccess *f; + + bool endian_swap; + bool use_real64; + + Vector str_buf; + List resource_cache; + + Map string_map; + + String get_unicode_string(); + void _advance_padding(uint32_t p_len); + +friend class ObjectFormatLoaderInstancerBinary; + + + Error parse_property(Variant& r_v, int& r_index); + +public: + + + virtual Error load(Object **p_object,Variant &p_meta); + + ObjectFormatLoaderBinary(FileAccess *f,bool p_endian_swap,bool p_use64); + virtual ~ObjectFormatLoaderBinary(); +}; + +class ObjectFormatLoaderInstancerBinary : public ObjectFormatLoaderInstancer { +public: + + virtual ObjectFormatLoaderBinary* instance(const String& p_file,const String& p_magic); + virtual void get_recognized_extensions(List *p_extensions) const; + + + +}; + + + +#endif // OBJECT_FORMAT_BINARY_H +#endif diff --git a/core/io/object_format_xml.cpp b/core/io/object_format_xml.cpp new file mode 100644 index 00000000000..0a8ce70d5ef --- /dev/null +++ b/core/io/object_format_xml.cpp @@ -0,0 +1,3190 @@ +/*************************************************************************/ +/* object_format_xml.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. */ +/*************************************************************************/ +#ifdef XML_ENABLED +#ifdef OLD_SCENE_FORMAT_ENABLED +#include "object_format_xml.h" +#include "resource.h" +#include "io/resource_loader.h" +#include "print_string.h" +#include "object_type_db.h" +#include "globals.h" +#include "os/os.h" +#include "version.h" + +void ObjectFormatSaverXML::escape(String& p_str) { + + p_str=p_str.replace("&","&"); + p_str=p_str.replace("<",">"); + p_str=p_str.replace(">","<"); + p_str=p_str.replace("'","'"); + p_str=p_str.replace("\"","""); + for (int i=1;i<32;i++) { + + char chr[2]={i,0}; + p_str=p_str.replace(chr,"&#"+String::num(i)+";"); + } + + +} +void ObjectFormatSaverXML::write_tabs(int p_diff) { + + for (int i=0;istore_8('\t'); + } +} + +void ObjectFormatSaverXML::write_string(String p_str,bool p_escape) { + + /* write an UTF8 string */ + if (p_escape) + escape(p_str); + + f->store_string(p_str);; + /* + CharString cs=p_str.utf8(); + const char *data=cs.get_data(); + + while (*data) { + f->store_8(*data); + data++; + }*/ + + +} + +void ObjectFormatSaverXML::enter_tag(const String& p_section,const String& p_args) { + + if (p_args.length()) + write_string("<"+p_section+" "+p_args+">",false); + else + write_string("<"+p_section+">",false); + depth++; +} +void ObjectFormatSaverXML::exit_tag(const String& p_section) { + + depth--; + write_string("",false); + +} + +/* +static bool _check_type(const Variant& p_property) { + + if (p_property.get_type()==Variant::_RID) + return false; + if (p_property.get_type()==Variant::OBJECT) { + RES res = p_property; + if (res.is_null()) + return false; + } + + return true; +}*/ + +void ObjectFormatSaverXML::write_property(const String& p_name,const Variant& p_property,bool *r_ok) { + + if (r_ok) + *r_ok=false; + + String type; + String params; + bool oneliner=true; + + switch( p_property.get_type() ) { + + case Variant::NIL: type="nil"; break; + case Variant::BOOL: type="bool"; break; + case Variant::INT: type="int"; break; + case Variant::REAL: type="real"; break; + case Variant::STRING: type="string"; break; + case Variant::VECTOR2: type="vector2"; break; + case Variant::RECT2: type="rect2"; break; + case Variant::VECTOR3: type="vector3"; break; + case Variant::PLANE: type="plane"; break; + case Variant::_AABB: type="aabb"; break; + case Variant::QUAT: type="quaternion"; break; + case Variant::MATRIX32: type="matrix32"; break; + case Variant::MATRIX3: type="matrix3"; break; + case Variant::TRANSFORM: type="transform"; break; + case Variant::COLOR: type="color"; break; + case Variant::IMAGE: { + type="image"; + Image img=p_property; + if (img.empty()) { + enter_tag(type,"name=\""+p_name+"\""); + exit_tag(type); + if (r_ok) + *r_ok=true; + return; + } + params+="encoding=\"raw\""; + params+=" width=\""+itos(img.get_width())+"\""; + params+=" height=\""+itos(img.get_height())+"\""; + params+=" mipmaps=\""+itos(img.get_mipmaps())+"\""; + + switch(img.get_format()) { + + case Image::FORMAT_GRAYSCALE: params+=" format=\"grayscale\""; break; + case Image::FORMAT_INTENSITY: params+=" format=\"intensity\""; break; + case Image::FORMAT_GRAYSCALE_ALPHA: params+=" format=\"grayscale_alpha\""; break; + case Image::FORMAT_RGB: params+=" format=\"rgb\""; break; + case Image::FORMAT_RGBA: params+=" format=\"rgba\""; break; + case Image::FORMAT_INDEXED : params+=" format=\"indexed\""; break; + case Image::FORMAT_INDEXED_ALPHA: params+=" format=\"indexed_alpha\""; break; + case Image::FORMAT_BC1: params+=" format=\"bc1\""; break; + case Image::FORMAT_BC2: params+=" format=\"bc2\""; break; + case Image::FORMAT_BC3: params+=" format=\"bc3\""; break; + case Image::FORMAT_BC4: params+=" format=\"bc4\""; break; + case Image::FORMAT_BC5: params+=" format=\"bc5\""; break; + case Image::FORMAT_CUSTOM: params+=" format=\"custom\" custom_size=\""+itos(img.get_data().size())+"\""; break; + default: {} + } + } break; + case Variant::NODE_PATH: type="node_path"; break; + case Variant::OBJECT: { + type="resource"; + RES res = p_property; + if (res.is_null()) { + enter_tag(type,"name=\""+p_name+"\""); + exit_tag(type); + if (r_ok) + *r_ok=true; + + return; // don't save it + } + + params="resource_type=\""+res->get_type()+"\""; + + if (res->get_path().length() && res->get_path().find("::")==-1) { + //external resource + String path=relative_paths?local_path.path_to_file(res->get_path()):res->get_path(); + escape(path); + params+=" path=\""+path+"\""; + } else { + + //internal resource + ERR_EXPLAIN("Resource was not pre cached for the resource section, bug?"); + ERR_FAIL_COND(!resource_map.has(res)); + + params+=" path=\"local://"+itos(resource_map[res])+"\""; + } + + } break; + case Variant::INPUT_EVENT: type="input_event"; break; + case Variant::DICTIONARY: type="dictionary" ; oneliner=false; break; + case Variant::ARRAY: type="array"; params="len=\""+itos(p_property.operator Array().size())+"\""; oneliner=false; break; + + case Variant::RAW_ARRAY: type="raw_array"; params="len=\""+itos(p_property.operator DVector < uint8_t >().size())+"\""; break; + case Variant::INT_ARRAY: type="int_array"; params="len=\""+itos(p_property.operator DVector < int >().size())+"\""; break; + case Variant::REAL_ARRAY: type="real_array"; params="len=\""+itos(p_property.operator DVector < real_t >().size())+"\""; break; + case Variant::STRING_ARRAY: type="string_array"; params="len=\""+itos(p_property.operator DVector < String >().size())+"\""; break; + case Variant::VECTOR2_ARRAY: type="vector2_array"; params="len=\""+itos(p_property.operator DVector < Vector2 >().size())+"\""; break; + case Variant::VECTOR3_ARRAY: type="vector3_array"; params="len=\""+itos(p_property.operator DVector < Vector3 >().size())+"\""; break; + case Variant::COLOR_ARRAY: type="color_array"; params="len=\""+itos(p_property.operator DVector < Color >().size())+"\""; break; + default: { + + ERR_PRINT("Unknown Variant type."); + ERR_FAIL(); + } + + } + + write_tabs(); + + if (p_name!="") { + if (params.length()) + enter_tag(type,"name=\""+p_name+"\" "+params); + else + enter_tag(type,"name=\""+p_name+"\""); + } else { + if (params.length()) + enter_tag(type," "+params); + else + enter_tag(type,""); + } + + if (!oneliner) + write_string("\n",false); + else + write_string(" ",false); + + + switch( p_property.get_type() ) { + + case Variant::NIL: { + + } break; + case Variant::BOOL: { + + write_string( p_property.operator bool() ? "True":"False" ); + } break; + case Variant::INT: { + + write_string( itos(p_property.operator int()) ); + } break; + case Variant::REAL: { + + write_string( rtos(p_property.operator real_t()) ); + } break; + case Variant::STRING: { + + String str=p_property; + escape(str); + str="\""+str+"\""; + write_string( str,false ); + } break; + case Variant::VECTOR2: { + + Vector2 v = p_property; + write_string( rtoss(v.x) +", "+rtoss(v.y) ); + } break; + case Variant::RECT2: { + + Rect2 aabb = p_property; + write_string( rtoss(aabb.pos.x) +", "+rtoss(aabb.pos.y) +", "+rtoss(aabb.size.x) +", "+rtoss(aabb.size.y) ); + + } break; + case Variant::VECTOR3: { + + Vector3 v = p_property; + write_string( rtoss(v.x) +", "+rtoss(v.y)+", "+rtoss(v.z) ); + } break; + case Variant::PLANE: { + + Plane p = p_property; + write_string( rtoss(p.normal.x) +", "+rtoss(p.normal.y)+", "+rtoss(p.normal.z)+", "+rtoss(p.d) ); + + } break; + case Variant::_AABB: { + + AABB aabb = p_property; + write_string( rtoss(aabb.pos.x) +", "+rtoss(aabb.pos.y) +", "+rtoss(aabb.pos.z) +", "+rtoss(aabb.size.x) +", "+rtoss(aabb.size.y) +", "+rtoss(aabb.size.z) ); + + } break; + case Variant::QUAT: { + + Quat quat = p_property; + write_string( rtoss(quat.x)+", "+rtoss(quat.y)+", "+rtoss(quat.z)+", "+rtoss(quat.w)+", "); + + } break; + case Variant::MATRIX32: { + + String s; + Matrix32 m3 = p_property; + for (int i=0;i<3;i++) { + for (int j=0;j<2;j++) { + + if (i!=0 || j!=0) + s+=", "; + s+=rtoss( m3.elements[i][j] ); + } + } + + write_string(s); + + } break; + case Variant::MATRIX3: { + + String s; + Matrix3 m3 = p_property; + for (int i=0;i<3;i++) { + for (int j=0;j<3;j++) { + + if (i!=0 || j!=0) + s+=", "; + s+=rtoss( m3.elements[i][j] ); + } + } + + write_string(s); + + } break; + case Variant::TRANSFORM: { + + String s; + Transform t = p_property; + Matrix3 &m3 = t.basis; + for (int i=0;i<3;i++) { + for (int j=0;j<3;j++) { + + if (i!=0 || j!=0) + s+=", "; + s+=rtoss( m3.elements[i][j] ); + } + } + + s=s+", "+rtoss(t.origin.x) +", "+rtoss(t.origin.y)+", "+rtoss(t.origin.z); + + write_string(s); + } break; + + // misc types + case Variant::COLOR: { + + Color c = p_property; + write_string( rtoss(c.r) +", "+rtoss(c.g)+", "+rtoss(c.b)+", "+rtoss(c.a) ); + + } break; + case Variant::IMAGE: { + + String s; + Image img = p_property; + DVector data = img.get_data(); + int len = data.size(); + DVector::Read r = data.read(); + const uint8_t *ptr=r.ptr();; + for (int i=0;i>4], hex[byte&0xF], 0}; + s+=str; + } + + write_string(s); + } break; + case Variant::NODE_PATH: { + + String str=p_property; + escape(str); + str="\""+str+"\""; + write_string( str,false); + + } break; + + case Variant::OBJECT: { + /* this saver does not save resources in here + RES res = p_property; + + if (!res.is_null()) { + + String path=res->get_path(); + if (!res->is_shared() || !path.length()) { + // if no path, or path is from inside a scene + write_object( *res ); + } + + } + */ + + } break; + case Variant::INPUT_EVENT: { + + write_string( p_property.operator String() ); + } break; + case Variant::DICTIONARY: { + + Dictionary dict = p_property; + + + List keys; + dict.get_key_list(&keys); + + for(List::Element *E=keys.front();E;E=E->next()) { + + //if (!_check_type(dict[E->get()])) + // continue; + + bool ok; + write_property("",E->get(),&ok); + ERR_CONTINUE(!ok); + + write_property("",dict[E->get()],&ok); + if (!ok) + write_property("",Variant()); //at least make the file consistent.. + } + + + + + } break; + case Variant::ARRAY: { + + Array array = p_property; + int len=array.size(); + for (int i=0;i data = p_property; + int len = data.size(); + DVector::Read r = data.read(); + const uint8_t *ptr=r.ptr();; + for (int i=0;i>4], hex[byte&0xF], 0}; + s+=str; + } + + write_string(s,false); + + } break; + case Variant::INT_ARRAY: { + + DVector data = p_property; + int len = data.size(); + DVector::Read r = data.read(); + const int *ptr=r.ptr();; + write_tabs(); + + for (int i=0;i0) + write_string(", ",false); + + write_string(itos(ptr[i]),false); + } + + + + } break; + case Variant::REAL_ARRAY: { + + DVector data = p_property; + int len = data.size(); + DVector::Read r = data.read(); + const real_t *ptr=r.ptr();; + write_tabs(); + + for (int i=0;i0) + write_string(", ",false); + write_string(rtoss(ptr[i]),false); + } + + + } break; + case Variant::STRING_ARRAY: { + + DVector data = p_property; + int len = data.size(); + DVector::Read r = data.read(); + const String *ptr=r.ptr();; + String s; + + for (int i=0;i0) + s+=", "; + String str=ptr[i]; + escape(str); + s=s+"\""+str+"\""; + } + + write_string(s,false); + + } break; + case Variant::VECTOR2_ARRAY: { + + DVector data = p_property; + int len = data.size(); + DVector::Read r = data.read(); + const Vector2 *ptr=r.ptr();; + write_tabs(); + + for (int i=0;i0) + write_string(", ",false); + write_string(rtoss(ptr[i].x),false); + write_string(", "+rtoss(ptr[i].y),false); + + } + + + } break; + case Variant::VECTOR3_ARRAY: { + + DVector data = p_property; + int len = data.size(); + DVector::Read r = data.read(); + const Vector3 *ptr=r.ptr();; + write_tabs(); + + for (int i=0;i0) + write_string(", ",false); + write_string(rtoss(ptr[i].x),false); + write_string(", "+rtoss(ptr[i].y),false); + write_string(", "+rtoss(ptr[i].z),false); + + } + + + } break; + case Variant::COLOR_ARRAY: { + + DVector data = p_property; + int len = data.size(); + DVector::Read r = data.read(); + const Color *ptr=r.ptr();; + write_tabs(); + + for (int i=0;i0) + write_string(", ",false); + + write_string(rtoss(ptr[i].r),false); + write_string(", "+rtoss(ptr[i].g),false); + write_string(", "+rtoss(ptr[i].b),false); + write_string(", "+rtoss(ptr[i].a),false); + + } + + } break; + default: {} + + } + if (oneliner) + write_string(" "); + else + write_tabs(-1); + exit_tag(type); + + write_string("\n",false); + + if (r_ok) + *r_ok=true; + +} + + +void ObjectFormatSaverXML::_find_resources(const Variant& p_variant) { + + + switch(p_variant.get_type()) { + case Variant::OBJECT: { + + + + RES res = p_variant.operator RefPtr(); + + if (res.is_null()) + return; + + if (!bundle_resources && res->get_path().length() && res->get_path().find("::") == -1 ) + return; + + if (resource_map.has(res)) + return; + + List property_list; + + res->get_property_list( &property_list ); + + List::Element *I=property_list.front(); + + while(I) { + + PropertyInfo pi=I->get(); + + if (pi.usage&PROPERTY_USAGE_STORAGE || (bundle_resources && pi.usage&PROPERTY_USAGE_BUNDLE)) { + + Variant v=res->get(I->get().name); + _find_resources(v); + } + + I=I->next(); + } + + resource_map[ res ] = resource_map.size(); //saved after, so the childs it needs are available when loaded + saved_resources.push_back(res); + + } break; + + case Variant::ARRAY: { + + Array varray=p_variant; + int len=varray.size(); + for(int i=0;i keys; + d.get_key_list(&keys); + for(List::Element *E=keys.front();E;E=E->next()) { + + Variant v = d[E->get()]; + _find_resources(v); + } + } break; + default: {} + } + +} + + + +Error ObjectFormatSaverXML::save(const Object *p_object,const Variant &p_meta) { + + ERR_FAIL_COND_V(!f,ERR_UNCONFIGURED); + ERR_EXPLAIN("write_object should supply either an object, a meta, or both"); + ERR_FAIL_COND_V(!p_object && p_meta.get_type()==Variant::NIL, ERR_INVALID_PARAMETER); + + SavedObject *so = memnew( SavedObject ); + + if (p_object) + so->type=p_object->get_type(); + + _find_resources(p_meta); + so->meta=p_meta; + + if (p_object) { + + + if (optimizer.is_valid()) { + //use optimizer + + List props; + optimizer->get_property_list(p_object,&props); + + for(List::Element *E=props.front();E;E=E->next()) { + + if (skip_editor && String(E->get().name).begins_with("__editor")) + continue; + _find_resources(E->get().value); + SavedObject::SavedProperty sp; + sp.name=E->get().name; + sp.value=E->get().value; + so->properties.push_back(sp); + } + + } else { + //use classic way + List property_list; + p_object->get_property_list( &property_list ); + + for(List::Element *E=property_list.front();E;E=E->next()) { + + if (skip_editor && E->get().name.begins_with("__editor")) + continue; + if (E->get().usage&PROPERTY_USAGE_STORAGE || (bundle_resources && E->get().usage&PROPERTY_USAGE_BUNDLE)) { + + SavedObject::SavedProperty sp; + sp.name=E->get().name; + sp.value = p_object->get(E->get().name); + _find_resources(sp.value); + so->properties.push_back(sp); + } + } + } + + } + + saved_objects.push_back(so); + + return OK; +} + +ObjectFormatSaverXML::ObjectFormatSaverXML(FileAccess *p_file,const String& p_magic,const String& p_local_path,uint32_t p_flags,const Ref& p_optimizer) { + + optimizer=p_optimizer; + relative_paths=p_flags&ObjectSaver::FLAG_RELATIVE_PATHS; + skip_editor=p_flags&ObjectSaver::FLAG_OMIT_EDITOR_PROPERTIES; + bundle_resources=p_flags&ObjectSaver::FLAG_BUNDLE_RESOURCES; + f=p_file; // should be already opened + depth=0; + local_path=p_local_path; + magic=p_magic; +} +ObjectFormatSaverXML::~ObjectFormatSaverXML() { + + write_string("",false); //no escape + write_string("\n",false); + enter_tag("object_file","magic=\""+magic+"\" "+"version=\""+itos(VERSION_MAJOR)+"."+itos(VERSION_MINOR)+"\" version_name=\""+VERSION_FULL_NAME+"\""); + write_string("\n",false); + + // save resources + + for(List::Element *E=saved_resources.front();E;E=E->next()) { + + RES res = E->get(); + ERR_CONTINUE(!resource_map.has(res)); + + write_tabs(); + if (res->get_path().length() && res->get_path().find("::") == -1 ) + enter_tag("resource","type=\""+res->get_type()+"\" path=\""+res->get_path()+"\""); //bundled + else + enter_tag("resource","type=\""+res->get_type()+"\" path=\"local://"+itos(resource_map[res])+"\""); + + if (optimizer.is_valid()) { + + List props; + optimizer->get_property_list(res.ptr(),&props); + + for(List::Element *E=props.front();E;E=E->next()) { + + if (skip_editor && String(E->get().name).begins_with("__editor")) + continue; + + write_property(E->get().name,E->get().value); + } + + + } else { + + List property_list; + res->get_property_list(&property_list); + for(List::Element *PE = property_list.front();PE;PE=PE->next()) { + + + if (skip_editor && PE->get().name.begins_with("__editor")) + continue; + + if (PE->get().usage&PROPERTY_USAGE_STORAGE || (bundle_resources && PE->get().usage&PROPERTY_USAGE_BUNDLE)) { + + String name = PE->get().name; + Variant value = res->get(name); + write_property(name,value); + } + + + } + + } + write_tabs(-1); + exit_tag("resource"); + write_string("\n",false); + } + + if (!saved_objects.empty()) { + + + for(List::Element *E=saved_objects.front();E;E=E->next()) { + + SavedObject *so = E->get(); + + + + write_tabs(); + if (so->type!="") + enter_tag("object","type=\""+so->type+"\""); + else + enter_tag("object"); + write_string("\n",false); + + if (so->meta.get_type()!=Variant::NIL) { + + write_property("__xml_meta__",so->meta); + + } + + List::Element *SE = so->properties.front(); + + while(SE) { + + write_property(SE->get().name,SE->get().value); + SE=SE->next(); + } + + + write_tabs(-1); + exit_tag("object"); + write_string("\n",false); + memdelete(so); //no longer needed + } + + + } else { + + WARN_PRINT("File contains no saved objects."); + } + + exit_tag("object_file"); + f->close(); + memdelete(f); +} + + +ObjectFormatSaver* ObjectFormatSaverInstancerXML::instance(const String& p_file,const String& p_magic,uint32_t p_flags,const Ref& p_optimizer) { + + Error err; + FileAccess *f = FileAccess::open(p_file, FileAccess::WRITE,&err); + + ERR_FAIL_COND_V( err, NULL ); + String local_path = Globals::get_singleton()->localize_path(p_file); + + return memnew( ObjectFormatSaverXML( f, p_magic,local_path,p_flags,p_optimizer ) ); +} + +void ObjectFormatSaverInstancerXML::get_recognized_extensions(List *p_extensions) const { + + p_extensions->push_back("xml"); +} + + +ObjectFormatSaverInstancerXML::~ObjectFormatSaverInstancerXML() { + + +} + +/************************************************/ +/************************************************/ +/************************************************/ +/************************************************/ +/************************************************/ + + + +#ifdef OPTIMIZED_XML_LOADER + +#define IS_FLOAT_CHAR(m_c) \ + ((m_c>='0' && m_c<='9') || m_c=='e' || m_c=='-' || m_c=='+' || m_c=='.') + +#define XML_FAIL(m_cond,m_err) \ + if (m_cond) {\ + ERR_EXPLAIN(local_path+":"+itos(parser->get_current_line())+": "+String(m_err));\ + ERR_FAIL_COND_V( m_cond, ERR_FILE_CORRUPT );\ + } + + +Error ObjectFormatLoaderXML::_parse_property(Variant& r_v,String& r_name) { + + XML_FAIL( parser->is_empty(), "unexpected empty tag"); + + String type=parser->get_node_name(); + String name=parser->get_attribute_value_safe("name"); + + r_v=Variant(); + r_name=name; + + if (type=="dictionary") { + + Dictionary d; + int reading=0; + Variant key; + while(parser->read()==OK) { + + if (parser->get_node_type()==XMLParser::NODE_ELEMENT) { + Error err; + String tagname; + + if (reading==0) { + + err=_parse_property(key,tagname); + XML_FAIL( err,"error parsing dictionary key: "+name); + reading++; + } else { + + reading=0; + Variant value; + err=_parse_property(value,tagname); + XML_FAIL( err,"error parsing dictionary value: "+name); + d[key]=value; + } + + } else if (parser->get_node_type()==XMLParser::NODE_ELEMENT_END && parser->get_node_name()=="dictionary") { + r_v=d; + return OK; + } + } + + + XML_FAIL( true, "unexpected end of file while reading dictionary: "+name); + + } else if (type=="array") { + + XML_FAIL( !parser->has_attribute("len"), "array missing 'len' attribute"); + + int len=parser->get_attribute_value("len").to_int(); + + Array array; + array.resize(len); + + + Variant v; + String tagname; + int idx=0; + + + while(parser->read()==OK) { + + if (parser->get_node_type()==XMLParser::NODE_ELEMENT) { + + XML_FAIL( idx >= len, "array size mismatch (too many elements)"); + Error err; + String tagname; + Variant key; + + err=_parse_property(key,tagname); + XML_FAIL( err,"error parsing element of array: "+name); + array[idx]=key; + idx++; + + + } else if (parser->get_node_type()==XMLParser::NODE_ELEMENT_END && parser->get_node_name()=="array") { + + XML_FAIL( idx != len, "array size mismatch (not "+itos(len)+"):"+name); + r_v=array; + return OK; + } + } + + XML_FAIL( true, "unexpected end of file while reading dictionary: "+name); + + } else if (type=="resource") { + + + XML_FAIL(!parser->has_attribute("path"),"resource property has no 'path' set (embedding not supported).") + + String path=parser->get_attribute_value("path"); + String hint = parser->get_attribute_value_safe("resource_type"); + + if (path.begins_with("local://")) + path=path.replace("local://",local_path+"::"); + else if (path.find("://")==-1 && path.is_rel_path()) { + // path is relative to file being loaded, so convert to a resource path + path=Globals::get_singleton()->localize_path(local_path.get_base_dir()+"/"+path); + + } + + //take advantage of the resource loader cache. The resource is cached on it, even if + RES res=ResourceLoader::load(path,hint); + + + if (res.is_null()) { + + WARN_PRINT(String("Couldn't load resource: "+path).ascii().get_data()); + } + + r_v=res.get_ref_ptr(); + + } else if (type=="image") { + + if (parser->has_attribute("encoding")) { //there is image data + + String encoding=parser->get_attribute_value("encoding"); + + if (encoding=="raw") { + + //raw image (bytes) + + XML_FAIL( !parser->has_attribute("width"), "missing attribute in raw encoding: 'width'."); + XML_FAIL( !parser->has_attribute("height"), "missing attribute in raw encoding: 'height'."); + XML_FAIL( !parser->has_attribute("format"), "missing attribute in raw encoding: 'format'."); + + String format = parser->get_attribute_value("format"); + String width = parser->get_attribute_value("width"); + String height = parser->get_attribute_value("height"); + + Image::Format imgformat; + int chans=0; + int pal=0; + + if (format=="grayscale") { + imgformat=Image::FORMAT_GRAYSCALE; + chans=1; + } else if (format=="intensity") { + imgformat=Image::FORMAT_INTENSITY; + chans=1; + } else if (format=="grayscale_alpha") { + imgformat=Image::FORMAT_GRAYSCALE_ALPHA; + chans=2; + } else if (format=="rgb") { + imgformat=Image::FORMAT_RGB; + chans=3; + } else if (format=="rgba") { + imgformat=Image::FORMAT_RGBA; + chans=4; + } else if (format=="indexed") { + imgformat=Image::FORMAT_INDEXED; + chans=1; + pal=256*3; + } else if (format=="indexed_alpha") { + imgformat=Image::FORMAT_INDEXED_ALPHA; + chans=1; + pal=256*4; + } else { + + XML_FAIL(true, "invalid format for image: "+format); + + } + + XML_FAIL( chans==0, "invalid number of color channels in image (0)."); + + int w=width.to_int(); + int h=height.to_int(); + + if (w == 0 && w == 0) { //epmty, don't even bother + //r_v = Image(w, h, imgformat); + r_v=Image(); + return OK; + } else { + + //decode hexa + + DVector pixels; + pixels.resize(chans*w*h+pal); + int pixels_size=pixels.size(); + XML_FAIL( pixels_size==0, "corrupt"); + + ERR_FAIL_COND_V(pixels_size==0,ERR_FILE_CORRUPT); + DVector::Write wr=pixels.write(); + uint8_t *bytes=wr.ptr(); + + XML_FAIL( parser->read()!=OK, "error reading" ); + XML_FAIL( parser->get_node_type()!=XMLParser::NODE_TEXT, "expected text!"); + + String text = parser->get_node_data().strip_edges(); + XML_FAIL( text.length()/2 != pixels_size, "unexpected image data size" ); + + for(int i=0;i='0' && c<='9') || (c>='A' && c<='F') || (c>='a' && c<='f') ) { + + if (i&1) { + + byte|=HEX2CHR(c); + bytes[i>>1]=byte; + } else { + + byte=HEX2CHR(c)<<4; + } + + + } + } + + wr=DVector::Write(); + r_v=Image(w,h,imgformat,pixels); + } + } + + } else { + r_v=Image(); // empty image, since no encoding defined + } + + } else if (type=="raw_array") { + + XML_FAIL( !parser->has_attribute("len"), "array missing 'len' attribute"); + + int len=parser->get_attribute_value("len").to_int(); + if (len>0) { + + XML_FAIL( parser->read()!=OK, "error reading" ); + XML_FAIL( parser->get_node_type()!=XMLParser::NODE_TEXT, "expected text!"); + String text = parser->get_node_data(); + + XML_FAIL( text.length() != len*2, "raw array length mismatch" ); + + DVector bytes; + bytes.resize(len); + DVector::Write w=bytes.write(); + uint8_t *bytesptr=w.ptr(); + + + for(int i=0;i='0' && c<='9') || (c>='A' && c<='F') || (c>='a' && c<='f') ) { + + if (i&1) { + + byte|=HEX2CHR(c); + bytesptr[i>>1]=byte; + } else { + + byte=HEX2CHR(c)<<4; + } + } + } + + w=DVector::Write(); + r_v=bytes; + } + + } else if (type=="int_array") { + + int len=parser->get_attribute_value("len").to_int(); + + if (len>0) { + + XML_FAIL( parser->read()!=OK, "error reading" ); + XML_FAIL( parser->get_node_type()!=XMLParser::NODE_TEXT, "expected text!"); + String text = parser->get_node_data(); + + const CharType *c=text.c_str(); + DVector varray; + varray.resize(len); + DVector::Write w = varray.write(); + + int idx=0; + const CharType *from=c-1; + + while(*c) { + + bool ischar = (*c >='0' && *c<='9') || *c=='+' || *c=='-'; + if (!ischar) { + + if (int64_t(c-from)>1) { + + int i = String::to_int(from+1,int64_t(c-from)); + w[idx++]=i; + } + + from=c; + } else { + + XML_FAIL( idx >= len, "array too big"); + } + + c++; + } + + XML_FAIL( idx != len, "array size mismatch"); + + w = varray.write(); + r_v=varray; + } + + + + } else if (type=="real_array") { + + int len=parser->get_attribute_value("len").to_int(); + + if (len>0) { + + XML_FAIL( parser->read()!=OK, "error reading" ); + XML_FAIL( parser->get_node_type()!=XMLParser::NODE_TEXT, "expected text!"); + String text = parser->get_node_data(); + + const CharType *c=text.c_str(); + DVector varray; + varray.resize(len); + DVector::Write w = varray.write(); + + int idx=0; + const CharType *from=c-1; + + while(*c) { + + bool ischar = IS_FLOAT_CHAR((*c)); + if (!ischar) { + + if (int64_t(c-from)>1) { + + real_t f = String::to_double(from+1,int64_t(c-from)); + w[idx++]=f; + } + + from=c; + } else { + + XML_FAIL( idx >= len, "array too big"); + } + + c++; + } + + XML_FAIL( idx != len, "array size mismatch"); + + w = varray.write(); + r_v=varray; + } + + } else if (type=="string_array") { + + + // this is invalid xml, and will have to be fixed at some point.. + + int len=parser->get_attribute_value("len").to_int(); + + if (len>0) { + + XML_FAIL( parser->read()!=OK, "error reading" ); + XML_FAIL( parser->get_node_type()!=XMLParser::NODE_TEXT, "expected text!"); + String text = parser->get_node_data(); + + const CharType *c=text.c_str(); + DVector sarray; + sarray.resize(len); + DVector::Write w = sarray.write(); + + + bool inside=false; + const CharType *from=c; + int idx=0; + + while(*c) { + + if (inside) { + + if (*c == '"') { + inside=false; + String s = String(from,int64_t(c-from)); + w[idx]=s; + idx++; + } + } else { + + if (*c == '"') { + inside=true; + from=c+1; + XML_FAIL( idx>=len, "string array is too big!!: "+name); + } + } + + c++; + } + + XML_FAIL( inside, "unterminated string array: "+name); + XML_FAIL( len != idx, "string array size mismatch: "+name); + + w = DVector::Write(); + + r_v=sarray; + + } + } else if (type=="vector3_array") { + + int len=parser->get_attribute_value("len").to_int(); + + if (len>0) { + + XML_FAIL( parser->read()!=OK, "error reading" ); + XML_FAIL( parser->get_node_type()!=XMLParser::NODE_TEXT, "expected text!"); + String text = parser->get_node_data(); + + const CharType *c=text.c_str(); + DVector varray; + varray.resize(len); + DVector::Write w = varray.write(); + + int idx=0; + int sidx=0; + Vector3 v; + const CharType *from=c-1; + + while(*c) { + + bool ischar = IS_FLOAT_CHAR((*c)); + if (!ischar) { + + if (int64_t(c-from)>1) { + + real_t f = String::to_double(from+1,int64_t(c-from)); + v[sidx++]=f; + if (sidx==3) { + w[idx++]=v; + sidx=0; + + } + } + + from=c; + } else { + + XML_FAIL( idx >= len, "array too big"); + } + + c++; + } + + XML_FAIL( idx != len, "array size mismatch"); + + w = varray.write(); + r_v=varray; + } + + } else if (type=="color_array") { + + int len=parser->get_attribute_value("len").to_int(); + + if (len>0) { + + XML_FAIL( parser->read()!=OK, "error reading" ); + XML_FAIL( parser->get_node_type()!=XMLParser::NODE_TEXT, "expected text!"); + String text = parser->get_node_data(); + + const CharType *c=text.c_str(); + DVector carray; + carray.resize(len); + DVector::Write w = carray.write(); + + int idx=0; + int sidx=0; + Color v; + const CharType *from=c-1; + + while(*c) { + + bool ischar = IS_FLOAT_CHAR((*c)); + if (!ischar) { + + if (int64_t(c-from)>1) { + + real_t f = String::to_double(from+1,int64_t(c-from)); + v[sidx++]=f; + if (sidx==4) { + w[idx++]=v; + sidx=0; + + } + } + + from=c; + } else { + + XML_FAIL( idx >= len, "array too big"); + } + + c++; + } + + XML_FAIL( idx != len, "array size mismatch"); + + w = carray.write(); + r_v=carray; + } + } else { + // simple string parsing code + XML_FAIL( parser->read()!=OK, "can't read data" ); + + String data=parser->get_node_data(); + data=data.strip_edges(); + + if (type=="nil") { + // uh do nothing + + } else if (type=="bool") { + // uh do nothing + if (data.nocasecmp_to("true")==0 || data.to_int()!=0) + r_v=true; + else + r_v=false; + + } else if (type=="int") { + + r_v=data.to_int(); + } else if (type=="real") { + + r_v=data.to_double(); + } else if (type=="string") { + + String str=data; + str=str.substr(1,str.length()-2); + r_v=str; + } else if (type=="vector3") { + + r_v=Vector3( + data.get_slice(",",0).to_double(), + data.get_slice(",",1).to_double(), + data.get_slice(",",2).to_double() + ); + + } else if (type=="vector2") { + + + r_v=Vector2( + data.get_slice(",",0).to_double(), + data.get_slice(",",1).to_double() + ); + + } else if (type=="plane") { + + r_v=Plane( + data.get_slice(",",0).to_double(), + data.get_slice(",",1).to_double(), + data.get_slice(",",2).to_double(), + data.get_slice(",",3).to_double() + ); + + } else if (type=="quaternion") { + + r_v=Quat( + data.get_slice(",",0).to_double(), + data.get_slice(",",1).to_double(), + data.get_slice(",",2).to_double(), + data.get_slice(",",3).to_double() + ); + + } else if (type=="rect2") { + + r_v=Rect2( + Vector2( + data.get_slice(",",0).to_double(), + data.get_slice(",",1).to_double() + ), + Vector2( + data.get_slice(",",2).to_double(), + data.get_slice(",",3).to_double() + ) + ); + + + } else if (type=="aabb") { + + r_v=AABB( + Vector3( + data.get_slice(",",0).to_double(), + data.get_slice(",",1).to_double(), + data.get_slice(",",2).to_double() + ), + Vector3( + data.get_slice(",",3).to_double(), + data.get_slice(",",4).to_double(), + data.get_slice(",",5).to_double() + ) + ); + + + } else if (type=="matrix3") { + + Matrix3 m3; + for (int i=0;i<3;i++) { + for (int j=0;j<3;j++) { + m3.elements[i][j]=data.get_slice(",",i*3+j).to_double(); + } + } + r_v=m3; + + } else if (type=="transform") { + + Transform tr; + for (int i=0;i<3;i++) { + for (int j=0;j<3;j++) { + tr.basis.elements[i][j]=data.get_slice(",",i*3+j).to_double(); + } + + } + tr.origin=Vector3( + data.get_slice(",",9).to_double(), + data.get_slice(",",10).to_double(), + data.get_slice(",",11).to_double() + ); + r_v=tr; + + } else if (type=="color") { + + r_v=Color( + data.get_slice(",",0).to_double(), + data.get_slice(",",1).to_double(), + data.get_slice(",",2).to_double(), + data.get_slice(",",3).to_double() + ); + + } else if (type=="node_path") { + + String str=data; + str=str.substr(1,str.length()-2); + r_v=NodePath( str ); + + } else if (type=="input_event") { + + // ? + } else { + + XML_FAIL(true,"unrecognized property tag: "+type); + } + } + + _close_tag(type); + + return OK; +} + + + + +Error ObjectFormatLoaderXML::_close_tag(const String& p_tag) { + + int c=1; + + while(parser->read()==OK) { + + + if (parser->get_node_type()==XMLParser::NODE_ELEMENT && parser->get_node_name()==p_tag) { + c++; + } else if (parser->get_node_type()==XMLParser::NODE_ELEMENT_END && parser->get_node_name()==p_tag) { + c--; + + if (c==0) + return OK; + } + + } + + return ERR_FILE_CORRUPT; +} + +Error ObjectFormatLoaderXML::load(Object **p_object,Variant &p_meta) { + + *p_object=NULL; + p_meta=Variant(); + + while(parser->read()==OK) { + + if (parser->get_node_type()==XMLParser::NODE_ELEMENT) { + + String name = parser->get_node_name(); + + + XML_FAIL( !parser->has_attribute("type"), "'type' attribute missing." ); + String type = parser->get_attribute_value("type"); + + + Object *obj=NULL; + Ref resource; + if (name=="resource") { + + XML_FAIL( !parser->has_attribute("path"), "'path' attribute missing." ); + String path = parser->get_attribute_value("path"); + + XML_FAIL(!path.begins_with("local://"),"path does not begin with 'local://'"); + + + path=path.replace("local://",local_path+"::"); + + if (ResourceCache::has(path)) { + Error err = _close_tag(name); + XML_FAIL( err, "error skipping resource."); + continue; //it's a resource, and it's already loaded + + } + + obj = ObjectTypeDB::instance(type); + XML_FAIL(!obj,"couldn't instance object of type: '"+type+"'"); + + Resource *r = obj->cast_to(); + XML_FAIL(!obj,"object isn't of type Resource: '"+type+"'"); + + resource = RES( r ); + r->set_path(path); + + + } else if (name=="object") { + + + if (ObjectTypeDB::can_instance(type)) { + obj = ObjectTypeDB::instance(type); + XML_FAIL(!obj,"couldn't instance object of type: '"+type+"'"); + } else { + + _close_tag(name); + return ERR_SKIP; + }; + } else { + XML_FAIL(true,"Unknown main tag: "+parser->get_node_name()); + } + + //load properties + + while (parser->read()==OK) { + + if (parser->get_node_type()==XMLParser::NODE_ELEMENT_END && parser->get_node_name()==name) + break; + else if (parser->get_node_type()==XMLParser::NODE_ELEMENT) { + + String name; + Variant v; + Error err; + err = _parse_property(v,name); + XML_FAIL(err,"Error parsing property: "+name); + + if (resource.is_null() && name=="__xml_meta__") { + + p_meta=v; + continue; + } else { + + XML_FAIL( !obj, "Normal property found in meta object"); + + } + + obj->set(name,v); + + + } + } + + + if (!obj) { + *p_object=NULL; + return OK; // it was a meta object + } + + if (resource.is_null()) { + //regular object + *p_object=obj; + return OK; + } else { + + resource_cache.push_back(resource); //keep it in mem until finished loading and load next + } + + + } else if (parser->get_node_type()==XMLParser::NODE_ELEMENT_END && parser->get_node_name()=="object_file") + return ERR_FILE_EOF; + } + + return OK; //never reach anyway +} + +ObjectFormatLoaderXML* ObjectFormatLoaderInstancerXML::instance(const String& p_file,const String& p_magic) { + + Ref parser = memnew( XMLParser ); + + Error err = parser->open(p_file); + ERR_FAIL_COND_V(err,NULL); + + ObjectFormatLoaderXML *loader = memnew( ObjectFormatLoaderXML ); + + loader->parser=parser; + loader->local_path = Globals::get_singleton()->localize_path(p_file); + + while(parser->read()==OK) { + + if (parser->get_node_type()==XMLParser::NODE_ELEMENT && parser->get_node_name()=="object_file") { + + ERR_FAIL_COND_V( parser->is_empty(), NULL ); + + String version = parser->get_attribute_value_safe("version"); + String magic = parser->get_attribute_value_safe("MAGIC"); + + if (version.get_slice_count(".")!=2) { + + ERR_EXPLAIN("Invalid Version String '"+version+"'' in file: "+p_file); + ERR_FAIL_V(NULL); + } + + int major = version.get_slice(".",0).to_int(); + int minor = version.get_slice(".",1).to_int(); + + if (major>VERSION_MAJOR || (major==VERSION_MAJOR && minor>VERSION_MINOR)) { + + ERR_EXPLAIN("File Format '"+version+"' is too new! Please upgrade to a a new engine version: "+p_file); + ERR_FAIL_V(NULL); + + } + + return loader; + } + + } + + ERR_EXPLAIN("No data found in file!"); + ERR_FAIL_V(NULL); +} + +void ObjectFormatLoaderInstancerXML::get_recognized_extensions(List *p_extensions) const { + + p_extensions->push_back("xml"); +} + + + +#else + +ObjectFormatLoaderXML::Tag* ObjectFormatLoaderXML::parse_tag(bool *r_exit) { + + + while(get_char()!='<' && !f->eof_reached()) {} + if (f->eof_reached()) + return NULL; + + Tag tag; + bool exit=false; + if (r_exit) + *r_exit=false; + + bool complete=false; + while(!f->eof_reached()) { + + CharType c=get_char(); + if (c<33 && tag.name.length() && !exit) { + break; + } else if (c=='>') { + complete=true; + break; + } else if (c=='/') { + exit=true; + } else { + tag.name+=c; + } + } + + if (f->eof_reached()) + return NULL; + + if (exit) { + if (!tag_stack.size()) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Unmatched exit tag "); + ERR_FAIL_COND_V(!tag_stack.size(),NULL); + } + + if (tag_stack.back()->get().name!=tag.name) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Mismatched exit tag. Got , expected get().name+">"); + ERR_FAIL_COND_V(tag_stack.back()->get().name!=tag.name,NULL); + } + + if (!complete) { + while(get_char()!='>' && !f->eof_reached()) {} + if (f->eof_reached()) + return NULL; + } + + if (r_exit) + *r_exit=true; + + tag_stack.pop_back(); + return NULL; + + } + + if (!complete) { + String name; + String value; + bool reading_value=false; + + while(!f->eof_reached()) { + + CharType c=get_char(); + if (c=='>') { + if (value.length()) { + + tag.args[name]=value; + } + break; + + } else if ( ((!reading_value && (c<33)) || c=='=' || c=='"') && tag.name.length()) { + + if (!reading_value && name.length()) { + + reading_value=true; + } else if (reading_value && value.length()) { + + tag.args[name]=value; + name=""; + value=""; + reading_value=false; + } + + } else if (reading_value) { + + value+=c; + } else { + + name+=c; + } + } + + if (f->eof_reached()) + return NULL; + } + + tag_stack.push_back(tag); + + return &tag_stack.back()->get(); +} + + +Error ObjectFormatLoaderXML::close_tag(const String& p_name) { + + int level=0; + bool inside_tag=false; + + while(true) { + + if (f->eof_reached()) { + + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": EOF found while attempting to find "); + ERR_FAIL_COND_V( f->eof_reached(), ERR_FILE_CORRUPT ); + } + + uint8_t c = get_char(); + + if (c == '<') { + + if (inside_tag) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Malformed XML. Already inside Tag."); + ERR_FAIL_COND_V(inside_tag,ERR_FILE_CORRUPT); + } + inside_tag=true; + c = get_char(); + if (c == '/') { + + --level; + } else { + + ++level; + }; + } else if (c == '>') { + + if (!inside_tag) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Malformed XML. Already outside Tag"); + ERR_FAIL_COND_V(!inside_tag,ERR_FILE_CORRUPT); + } + inside_tag=false; + if (level == -1) { + tag_stack.pop_back(); + return OK; + }; + }; + } + + return OK; +} + +void ObjectFormatLoaderXML::unquote(String& p_str) { + + p_str=p_str.strip_edges(); + p_str=p_str.replace("\"",""); + p_str=p_str.replace(">","<"); + p_str=p_str.replace("<",">"); + p_str=p_str.replace("'","'"); + p_str=p_str.replace(""","\""); + for (int i=1;i<32;i++) { + + char chr[2]={i,0}; + p_str=p_str.replace("&#"+String::num(i)+";",chr); + } + p_str=p_str.replace("&","&"); + + //p_str.parse_utf8( p_str.ascii(true).get_data() ); + +} + +Error ObjectFormatLoaderXML::goto_end_of_tag() { + + uint8_t c; + while(true) { + + c=get_char(); + if (c=='>') //closetag + break; + if (f->eof_reached()) { + + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": EOF found while attempting to find close tag."); + ERR_FAIL_COND_V( f->eof_reached(), ERR_FILE_CORRUPT ); + } + + } + tag_stack.pop_back(); + + return OK; +} + + +Error ObjectFormatLoaderXML::parse_property_data(String &r_data) { + + r_data=""; + CharString cs; + while(true) { + + CharType c=get_char(); + if (c=='<') + break; + ERR_FAIL_COND_V(f->eof_reached(),ERR_FILE_CORRUPT); + cs.push_back(c); + } + + cs.push_back(0); + + r_data.parse_utf8(cs.get_data()); + + while(get_char()!='>' && !f->eof_reached()) {} + if (f->eof_reached()) { + + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Malformed XML."); + ERR_FAIL_COND_V( f->eof_reached(), ERR_FILE_CORRUPT ); + } + + r_data=r_data.strip_edges(); + tag_stack.pop_back(); + + return OK; +} + + +Error ObjectFormatLoaderXML::_parse_array_element(Vector &buff,bool p_number_only,FileAccess *f,bool *end) { + + if (buff.empty()) + buff.resize(32); // optimize + + int buff_max=buff.size(); + int buff_size=0; + *end=false; + char *buffptr=&buff[0]; + bool found=false; + bool quoted=false; + + while(true) { + + char c=get_char(); + + if (c==0) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": File corrupt (zero found)."); + ERR_FAIL_V(ERR_FILE_CORRUPT); + } else if (c=='"') { + quoted=!quoted; + } else if ((!quoted && ((p_number_only && c<33) || c==',')) || c=='<') { + + + if (c=='<') { + *end=true; + break; + } + if (c<32 && f->eof_reached()) { + *end=true; + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": File corrupt (unexpected EOF)."); + ERR_FAIL_V(ERR_FILE_CORRUPT); + } + + if (found) + break; + + } else { + + found=true; + if (buff_size>=buff_max) { + + buff_max++; + buff.resize(buff_max); + + } + + buffptr[buff_size]=c; + buff_size++; + } + } + + if (buff_size>=buff_max) { + + buff_max++; + buff.resize(buff_max); + + } + + buff[buff_size]=0; + buff_size++; + + return OK; +} + +Error ObjectFormatLoaderXML::parse_property(Variant& r_v, String &r_name) { + + bool exit; + Tag *tag = parse_tag(&exit); + + if (!tag) { + if (exit) // shouldn't have exited + return ERR_FILE_EOF; + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": File corrupt (No Property Tag)."); + ERR_FAIL_V(ERR_FILE_CORRUPT); + } + + r_v=Variant(); + r_name=""; + + + //ERR_FAIL_COND_V(tag->name!="property",ERR_FILE_CORRUPT); + //ERR_FAIL_COND_V(!tag->args.has("name"),ERR_FILE_CORRUPT); +// ERR_FAIL_COND_V(!tag->args.has("type"),ERR_FILE_CORRUPT); + + //String name=tag->args["name"]; + //ERR_FAIL_COND_V(name=="",ERR_FILE_CORRUPT); + String type=tag->name; + String name=tag->args["name"]; + + if (type=="") { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": 'type' field is empty."); + ERR_FAIL_COND_V(type=="",ERR_FILE_CORRUPT); + } + + if (type=="dictionary") { + + Dictionary d; + + while(true) { + + Error err; + String tagname; + Variant key; + + int dictline = get_current_line(); + + + err=parse_property(key,tagname); + + if (err && err!=ERR_FILE_EOF) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Error parsing dictionary: "+name+" (from line "+itos(dictline)+")"); + ERR_FAIL_COND_V(err && err!=ERR_FILE_EOF,err); + } + //ERR_FAIL_COND_V(tagname!="key",ERR_FILE_CORRUPT); + if (err) + break; + Variant value; + err=parse_property(value,tagname); + if (err) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Error parsing dictionary: "+name+" (from line "+itos(dictline)+")"); + } + + ERR_FAIL_COND_V(err,err); + //ERR_FAIL_COND_V(tagname!="value",ERR_FILE_CORRUPT); + + d[key]=value; + } + + + //err=parse_property_data(name); // skip the rest + //ERR_FAIL_COND_V(err,err); + + r_name=name; + r_v=d; + return OK; + + } else if (type=="array") { + + if (!tag->args.has("len")) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Array missing 'len' field: "+name); + ERR_FAIL_COND_V(!tag->args.has("len"),ERR_FILE_CORRUPT); + } + + + int len=tag->args["len"].to_int(); + + Array array; + array.resize(len); + + Error err; + Variant v; + String tagname; + int idx=0; + while( (err=parse_property(v,tagname))==OK ) { + + ERR_CONTINUE( idx <0 || idx >=len ); + + array.set(idx,v); + idx++; + } + + if (idx!=len) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Error loading array (size mismatch): "+name); + ERR_FAIL_COND_V(idx!=len,err); + } + + if (err!=ERR_FILE_EOF) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Error loading array: "+name); + ERR_FAIL_COND_V(err!=ERR_FILE_EOF,err); + } + + //err=parse_property_data(name); // skip the rest + //ERR_FAIL_COND_V(err,err); + + r_name=name; + r_v=array; + return OK; + + } else if (type=="resource") { + + if (tag->args.has("path")) { + + String path=tag->args["path"]; + String hint; + if (tag->args.has("resource_type")) + hint=tag->args["resource_type"]; + + if (path.begins_with("local://")) + path=path.replace("local://",local_path+"::"); + else if (path.find("://")==-1 && path.is_rel_path()) { + // path is relative to file being loaded, so convert to a resource path + path=Globals::get_singleton()->localize_path(local_path.get_base_dir()+"/"+path); + + } + + //take advantage of the resource loader cache. The resource is cached on it, even if + RES res=ResourceLoader::load(path,hint); + + + if (res.is_null()) { + + WARN_PRINT(String("Couldn't load resource: "+path).ascii().get_data()); + } + + r_v=res.get_ref_ptr(); + } + + + + Error err=goto_end_of_tag(); + if (err) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Error closing tag."); + ERR_FAIL_COND_V(err,err); + } + + + r_name=name; + + return OK; + + } else if (type=="image") { + + if (!tag->args.has("encoding")) { + //empty image + r_v=Image(); + String sdfsdfg; + Error err=parse_property_data(sdfsdfg); + return OK; + } + + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Image missing 'encoding' field."); + ERR_FAIL_COND_V( !tag->args.has("encoding"), ERR_FILE_CORRUPT ); + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Image missing 'width' field."); + ERR_FAIL_COND_V( !tag->args.has("width"), ERR_FILE_CORRUPT ); + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Image missing 'height' field."); + ERR_FAIL_COND_V( !tag->args.has("height"), ERR_FILE_CORRUPT ); + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Image missing 'format' field."); + ERR_FAIL_COND_V( !tag->args.has("format"), ERR_FILE_CORRUPT ); + + String encoding=tag->args["encoding"]; + + if (encoding=="raw") { + String width=tag->args["width"]; + String height=tag->args["height"]; + String format=tag->args["format"]; + int mipmaps=tag->args.has("mipmaps")?int(tag->args["mipmaps"].to_int()):int(0); + int custom_size = tag->args.has("custom_size")?int(tag->args["custom_size"].to_int()):int(0); + + r_name=name; + + Image::Format imgformat; + + + if (format=="grayscale") { + imgformat=Image::FORMAT_GRAYSCALE; + } else if (format=="intensity") { + imgformat=Image::FORMAT_INTENSITY; + } else if (format=="grayscale_alpha") { + imgformat=Image::FORMAT_GRAYSCALE_ALPHA; + } else if (format=="rgb") { + imgformat=Image::FORMAT_RGB; + } else if (format=="rgba") { + imgformat=Image::FORMAT_RGBA; + } else if (format=="indexed") { + imgformat=Image::FORMAT_INDEXED; + } else if (format=="indexed_alpha") { + imgformat=Image::FORMAT_INDEXED_ALPHA; + } else if (format=="bc1") { + imgformat=Image::FORMAT_BC1; + } else if (format=="bc2") { + imgformat=Image::FORMAT_BC2; + } else if (format=="bc3") { + imgformat=Image::FORMAT_BC3; + } else if (format=="bc4") { + imgformat=Image::FORMAT_BC4; + } else if (format=="bc5") { + imgformat=Image::FORMAT_BC5; + } else if (format=="custom") { + imgformat=Image::FORMAT_CUSTOM; + } else { + + ERR_FAIL_V( ERR_FILE_CORRUPT ); + } + + + int datasize; + int w=width.to_int(); + int h=height.to_int(); + + if (w == 0 && w == 0) { + //r_v = Image(w, h, imgformat); + r_v=Image(); + String sdfsdfg; + Error err=parse_property_data(sdfsdfg); + return OK; + }; + + if (imgformat==Image::FORMAT_CUSTOM) { + + datasize=custom_size; + } else { + + datasize = Image::get_image_data_size(h,w,imgformat,mipmaps); + } + + if (datasize==0) { + //r_v = Image(w, h, imgformat); + r_v=Image(); + String sdfsdfg; + Error err=parse_property_data(sdfsdfg); + return OK; + }; + + DVector pixels; + pixels.resize(datasize); + DVector::Write wb = pixels.write(); + + int idx=0; + uint8_t byte; + while( idx='0' && c<='9') || (c>='A' && c<='F') || (c>='a' && c<='f') ) { + + if (idx&1) { + + byte|=HEX2CHR(c); + wb[idx>>1]=byte; + } else { + + byte=HEX2CHR(c)<<4; + } + + idx++; + } + + } + ERR_FAIL_COND_V(f->eof_reached(),ERR_FILE_CORRUPT); + + wb=DVector::Write(); + + r_v=Image(w,h,mipmaps,imgformat,pixels); + String sdfsdfg; + Error err=parse_property_data(sdfsdfg); + ERR_FAIL_COND_V(err,err); + + return OK; + } + + ERR_FAIL_V(ERR_FILE_CORRUPT); + + } else if (type=="raw_array") { + + if (!tag->args.has("len")) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": RawArray missing 'len' field: "+name); + ERR_FAIL_COND_V(!tag->args.has("len"),ERR_FILE_CORRUPT); + } + int len=tag->args["len"].to_int(); + + DVector bytes; + bytes.resize(len); + DVector::Write w=bytes.write(); + uint8_t *bytesptr=w.ptr(); + int idx=0; + uint8_t byte; + while( idx>1]=byte; + } else { + + byte=HEX2CHR(c)<<4; + } + + idx++; + } + + ERR_FAIL_COND_V(f->eof_reached(),ERR_FILE_CORRUPT); + + w=DVector::Write(); + r_v=bytes; + String sdfsdfg; + Error err=parse_property_data(sdfsdfg); + ERR_FAIL_COND_V(err,err); + r_name=name; + + return OK; + + } else if (type=="int_array") { + + if (!tag->args.has("len")) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Array missing 'len' field: "+name); + ERR_FAIL_COND_V(!tag->args.has("len"),ERR_FILE_CORRUPT); + } + int len=tag->args["len"].to_int(); + + DVector ints; + ints.resize(len); + DVector::Write w=ints.write(); + int *intsptr=w.ptr(); + int idx=0; + String str; +#if 0 + while( idxeof_reached(),ERR_FILE_CORRUPT); + + if (c<33 || c==',' || c=='<') { + + if (str.length()) { + + intsptr[idx]=str.to_int(); + str=""; + idx++; + } + + if (c=='<') { + + while(get_char()!='>' && !f->eof_reached()) {} + ERR_FAIL_COND_V(f->eof_reached(),ERR_FILE_CORRUPT); + break; + } + + } else { + + str+=c; + } + } + +#else + + Vector tmpdata; + + while( idx::Write(); + + r_v=ints; + Error err=goto_end_of_tag(); + ERR_FAIL_COND_V(err,err); + r_name=name; + + return OK; + } else if (type=="real_array") { + + if (!tag->args.has("len")) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Array missing 'len' field: "+name); + ERR_FAIL_COND_V(!tag->args.has("len"),ERR_FILE_CORRUPT); + } + int len=tag->args["len"].to_int();; + + DVector reals; + reals.resize(len); + DVector::Write w=reals.write(); + real_t *realsptr=w.ptr(); + int idx=0; + String str; + + +#if 0 + while( idxeof_reached(),ERR_FILE_CORRUPT); + + + if (c<33 || c==',' || c=='<') { + + if (str.length()) { + + realsptr[idx]=str.to_double(); + str=""; + idx++; + } + + if (c=='<') { + + while(get_char()!='>' && !f->eof_reached()) {} + ERR_FAIL_COND_V(f->eof_reached(),ERR_FILE_CORRUPT); + break; + } + + } else { + + str+=c; + } + } + +#else + + + + Vector tmpdata; + + while( idx::Write(); + r_v=reals; + + Error err=goto_end_of_tag(); + ERR_FAIL_COND_V(err,err); + r_name=name; + + return OK; + } else if (type=="string_array") { + + if (!tag->args.has("len")) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Array missing 'len' field: "+name); + ERR_FAIL_COND_V(!tag->args.has("len"),ERR_FILE_CORRUPT); + } + int len=tag->args["len"].to_int(); + + DVector strings; + strings.resize(len); + DVector::Write w=strings.write(); + String *stringsptr=w.ptr(); + int idx=0; + String str; + + bool inside_str=false; + CharString cs; + while( idxeof_reached(),ERR_FILE_CORRUPT); + + + if (c=='"') { + if (inside_str) { + + cs.push_back(0); + String str; + str.parse_utf8(cs.get_data()); + unquote(str); + stringsptr[idx]=str; + cs.clear(); + idx++; + inside_str=false; + } else { + inside_str=true; + } + } else if (c=='<') { + + while(get_char()!='>' && !f->eof_reached()) {} + ERR_FAIL_COND_V(f->eof_reached(),ERR_FILE_CORRUPT); + break; + + + } else if (inside_str){ + + cs.push_back(c); + } + } + w=DVector::Write(); + r_v=strings; + String sdfsdfg; + Error err=parse_property_data(sdfsdfg); + ERR_FAIL_COND_V(err,err); + + r_name=name; + + return OK; + } else if (type=="vector3_array") { + + if (!tag->args.has("len")) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Array missing 'len' field: "+name); + ERR_FAIL_COND_V(!tag->args.has("len"),ERR_FILE_CORRUPT); + } + int len=tag->args["len"].to_int();; + + DVector vectors; + vectors.resize(len); + DVector::Write w=vectors.write(); + Vector3 *vectorsptr=w.ptr(); + int idx=0; + int subidx=0; + Vector3 auxvec; + String str; + +// uint64_t tbegin = OS::get_singleton()->get_ticks_usec(); +#if 0 + while( idxeof_reached(),ERR_FILE_CORRUPT); + + + if (c<33 || c==',' || c=='<') { + + if (str.length()) { + + auxvec[subidx]=str.to_double(); + subidx++; + str=""; + if (subidx==3) { + vectorsptr[idx]=auxvec; + + idx++; + subidx=0; + } + } + + if (c=='<') { + + while(get_char()!='>' && !f->eof_reached()) {} + ERR_FAIL_COND_V(f->eof_reached(),ERR_FILE_CORRUPT); + break; + } + + } else { + + str+=c; + } + } +#else + + Vector tmpdata; + + while( idxget_ticks_usec() - tbegin)/1000000.0; + + + w=DVector::Write(); + r_v=vectors; + String sdfsdfg; + Error err=goto_end_of_tag(); + ERR_FAIL_COND_V(err,err); + r_name=name; + + return OK; + + } else if (type=="vector2_array") { + + if (!tag->args.has("len")) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Array missing 'len' field: "+name); + ERR_FAIL_COND_V(!tag->args.has("len"),ERR_FILE_CORRUPT); + } + int len=tag->args["len"].to_int();; + + DVector vectors; + vectors.resize(len); + DVector::Write w=vectors.write(); + Vector2 *vectorsptr=w.ptr(); + int idx=0; + int subidx=0; + Vector2 auxvec; + String str; + +// uint64_t tbegin = OS::get_singleton()->get_ticks_usec(); +#if 0 + while( idxeof_reached(),ERR_FILE_CORRUPT); + + + if (c<22 || c==',' || c=='<') { + + if (str.length()) { + + auxvec[subidx]=str.to_double(); + subidx++; + str=""; + if (subidx==2) { + vectorsptr[idx]=auxvec; + + idx++; + subidx=0; + } + } + + if (c=='<') { + + while(get_char()!='>' && !f->eof_reached()) {} + ERR_FAIL_COND_V(f->eof_reached(),ERR_FILE_CORRUPT); + break; + } + + } else { + + str+=c; + } + } +#else + + Vector tmpdata; + + while( idxget_ticks_usec() - tbegin)/1000000.0; + + + w=DVector::Write(); + r_v=vectors; + String sdfsdfg; + Error err=goto_end_of_tag(); + ERR_FAIL_COND_V(err,err); + r_name=name; + + return OK; + + } else if (type=="color_array") { + + if (!tag->args.has("len")) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Array missing 'len' field: "+name); + ERR_FAIL_COND_V(!tag->args.has("len"),ERR_FILE_CORRUPT); + } + int len=tag->args["len"].to_int();; + + DVector colors; + colors.resize(len); + DVector::Write w=colors.write(); + Color *colorsptr=w.ptr(); + int idx=0; + int subidx=0; + Color auxcol; + String str; + + while( idxeof_reached(),ERR_FILE_CORRUPT); + + + if (c<33 || c==',' || c=='<') { + + if (str.length()) { + + auxcol[subidx]=str.to_double(); + subidx++; + str=""; + if (subidx==4) { + colorsptr[idx]=auxcol; + idx++; + subidx=0; + } + } + + if (c=='<') { + + while(get_char()!='>' && !f->eof_reached()) {} + ERR_FAIL_COND_V(f->eof_reached(),ERR_FILE_CORRUPT); + break; + } + + } else { + + str+=c; + } + } + w=DVector::Write(); + r_v=colors; + String sdfsdfg; + Error err=parse_property_data(sdfsdfg); + ERR_FAIL_COND_V(err,err); + r_name=name; + + return OK; + } + + + String data; + Error err = parse_property_data(data); + ERR_FAIL_COND_V(err!=OK,err); + + if (type=="nil") { + // uh do nothing + + } else if (type=="bool") { + // uh do nothing + if (data.nocasecmp_to("true")==0 || data.to_int()!=0) + r_v=true; + else + r_v=false; + } else if (type=="int") { + + r_v=data.to_int(); + } else if (type=="real") { + + r_v=data.to_double(); + } else if (type=="string") { + + String str=data; + unquote(str); + r_v=str; + } else if (type=="vector3") { + + + r_v=Vector3( + data.get_slice(",",0).to_double(), + data.get_slice(",",1).to_double(), + data.get_slice(",",2).to_double() + ); + + } else if (type=="vector2") { + + + r_v=Vector2( + data.get_slice(",",0).to_double(), + data.get_slice(",",1).to_double() + ); + + } else if (type=="plane") { + + r_v=Plane( + data.get_slice(",",0).to_double(), + data.get_slice(",",1).to_double(), + data.get_slice(",",2).to_double(), + data.get_slice(",",3).to_double() + ); + + } else if (type=="quaternion") { + + r_v=Quat( + data.get_slice(",",0).to_double(), + data.get_slice(",",1).to_double(), + data.get_slice(",",2).to_double(), + data.get_slice(",",3).to_double() + ); + + } else if (type=="rect2") { + + r_v=Rect2( + Vector2( + data.get_slice(",",0).to_double(), + data.get_slice(",",1).to_double() + ), + Vector2( + data.get_slice(",",2).to_double(), + data.get_slice(",",3).to_double() + ) + ); + + + } else if (type=="aabb") { + + r_v=AABB( + Vector3( + data.get_slice(",",0).to_double(), + data.get_slice(",",1).to_double(), + data.get_slice(",",2).to_double() + ), + Vector3( + data.get_slice(",",3).to_double(), + data.get_slice(",",4).to_double(), + data.get_slice(",",5).to_double() + ) + ); + + } else if (type=="matrix32") { + + Matrix32 m3; + for (int i=0;i<3;i++) { + for (int j=0;j<2;j++) { + m3.elements[i][j]=data.get_slice(",",i*2+j).to_double(); + } + } + r_v=m3; + + } else if (type=="matrix3") { + + Matrix3 m3; + for (int i=0;i<3;i++) { + for (int j=0;j<3;j++) { + m3.elements[i][j]=data.get_slice(",",i*3+j).to_double(); + } + } + r_v=m3; + + } else if (type=="transform") { + + Transform tr; + for (int i=0;i<3;i++) { + for (int j=0;j<3;j++) { + tr.basis.elements[i][j]=data.get_slice(",",i*3+j).to_double(); + } + + } + tr.origin=Vector3( + data.get_slice(",",9).to_double(), + data.get_slice(",",10).to_double(), + data.get_slice(",",11).to_double() + ); + r_v=tr; + + } else if (type=="color") { + + r_v=Color( + data.get_slice(",",0).to_double(), + data.get_slice(",",1).to_double(), + data.get_slice(",",2).to_double(), + data.get_slice(",",3).to_double() + ); + + } else if (type=="node_path") { + + String str=data; + unquote(str); + r_v=NodePath( str ); + } else if (type=="input_event") { + + // ? + } else { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Unrecognized tag in file: "+type); + ERR_FAIL_V(ERR_FILE_CORRUPT); + } + r_name=name; + return OK; +} + + +Error ObjectFormatLoaderXML::load(Object **p_object,Variant &p_meta) { + + *p_object=NULL; + p_meta=Variant(); + + + + while(true) { + + + bool exit; + Tag *tag = parse_tag(&exit); + + + if (!tag) { + if (!exit) // shouldn't have exited + ERR_FAIL_V(ERR_FILE_CORRUPT); + *p_object=NULL; + return ERR_FILE_EOF; + } + + RES resource; + Object *obj=NULL; + + if (tag->name=="resource") { + //loading resource + + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": missing 'len' field."); + ERR_FAIL_COND_V(!tag->args.has("path"),ERR_FILE_CORRUPT); + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": missing 'type' field."); + ERR_FAIL_COND_V(!tag->args.has("type"),ERR_FILE_CORRUPT); + String path=tag->args["path"]; + + if (path.begins_with("local://")) { + //built-in resource (but really external) + path=path.replace("local://",local_path+"::"); + } + + + if (ResourceCache::has(path)) { + Error err = close_tag(tag->name); + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Unable to close tag."); + ERR_FAIL_COND_V( err, err ); + continue; //it's a resource, and it's already loaded + + } + + String type = tag->args["type"]; + + obj = ObjectTypeDB::instance(type); + if (!obj) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Object of unrecognized type in file: "+type); + } + ERR_FAIL_COND_V(!obj,ERR_FILE_CORRUPT); + + Resource *r = obj->cast_to(); + if (!r) { + memdelete(obj); //bye + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Object type in resource field not a resource, type is: "+obj->get_type()); + ERR_FAIL_COND_V(!obj->cast_to(),ERR_FILE_CORRUPT); + } + + resource = RES( r ); + r->set_path(path); + + + + } else if (tag->name=="object") { + + if ( tag->args.has("type") ) { + + ERR_FAIL_COND_V(!ObjectTypeDB::type_exists(tag->args["type"]), ERR_FILE_CORRUPT); + + if (ObjectTypeDB::can_instance(tag->args["type"])) { + obj = ObjectTypeDB::instance(tag->args["type"]); + if (!obj) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Object of unrecognized type in file: "+tag->args["type"]); + } + ERR_FAIL_COND_V(!obj,ERR_FILE_CORRUPT); + } else { + + close_tag(tag->name); + return ERR_SKIP; + }; + } else { + //otherwise it's a meta object + } + + } else { + + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Unknown main tag: "+tag->name); + ERR_FAIL_V( ERR_FILE_CORRUPT ); + } + + //load properties + + while(true) { + + String name; + Variant v; + Error err; + err = parse_property(v,name); + if (err==ERR_FILE_EOF) //tag closed + break; + if (err!=OK) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": XML Parsing aborted."); + ERR_FAIL_COND_V(err!=OK,ERR_FILE_CORRUPT); + } + if (resource.is_null() && name=="__xml_meta__") { + + p_meta=v; + continue; + } else if (!obj) { + + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Normal property found in meta object."); + ERR_FAIL_V(ERR_FILE_CORRUPT); + } + + obj->set(name,v); + } + + if (!obj) { + *p_object=NULL; + return OK; // it was a meta object + } + + if (resource.is_null()) { + + //regular object + *p_object=obj; + return OK; + } else { + + + resource_cache.push_back(resource); //keep it in mem until finished loading + } + + // a resource.. continue! + + } + + + + return OK; //never reach anyway + +} + +int ObjectFormatLoaderXML::get_current_line() const { + + return lines; +} + + +uint8_t ObjectFormatLoaderXML::get_char() const { + + uint8_t c = f->get_8(); + if (c=='\n') + lines++; + return c; + +} + +ObjectFormatLoaderXML::~ObjectFormatLoaderXML() { + + if (f) { + if (f->is_open()) + f->close(); + memdelete(f); + } +} + + + +ObjectFormatLoaderXML* ObjectFormatLoaderInstancerXML::instance(const String& p_file,const String& p_magic) { + + Error err; + FileAccess *f=FileAccess::open(p_file,FileAccess::READ,&err); + if (err!=OK) { + + ERR_FAIL_COND_V(err!=OK,NULL); + } + + ObjectFormatLoaderXML *loader = memnew( ObjectFormatLoaderXML ); + + loader->lines=1; + loader->f=f; + loader->local_path = Globals::get_singleton()->localize_path(p_file); + + ObjectFormatLoaderXML::Tag *tag = loader->parse_tag(); + if (!tag || tag->name!="?xml" || !tag->args.has("version") || !tag->args.has("encoding") || tag->args["encoding"]!="UTF-8") { + + f->close(); + memdelete(loader); + ERR_EXPLAIN("Not a XML:UTF-8 File: "+p_file); + ERR_FAIL_V(NULL); + } + + loader->tag_stack.clear(); + + tag = loader->parse_tag(); + + if (!tag || tag->name!="object_file" || !tag->args.has("magic") || !tag->args.has("version") || tag->args["magic"]!=p_magic) { + + f->close(); + memdelete(loader); + ERR_EXPLAIN("Unrecognized XML File: "+p_file); + ERR_FAIL_V(NULL); + } + + String version = tag->args["version"]; + if (version.get_slice_count(".")!=2) { + + f->close(); + memdelete(loader); + ERR_EXPLAIN("Invalid Version String '"+version+"'' in file: "+p_file); + ERR_FAIL_V(NULL); + } + + int major = version.get_slice(".",0).to_int(); + int minor = version.get_slice(".",1).to_int(); + + if (major>VERSION_MAJOR || (major==VERSION_MAJOR && minor>VERSION_MINOR)) { + + f->close(); + memdelete(loader); + ERR_EXPLAIN("File Format '"+version+"' is too new! Please upgrade to a a new engine version: "+p_file); + ERR_FAIL_V(NULL); + + } + + return loader; +} + +void ObjectFormatLoaderInstancerXML::get_recognized_extensions(List *p_extensions) const { + + p_extensions->push_back("xml"); +} + + +#endif +#endif +#endif diff --git a/core/io/object_format_xml.h b/core/io/object_format_xml.h new file mode 100644 index 00000000000..1169a1de585 --- /dev/null +++ b/core/io/object_format_xml.h @@ -0,0 +1,196 @@ +/*************************************************************************/ +/* object_format_xml.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 OBJECT_FORMAT_XML_H +#define OBJECT_FORMAT_XML_H + +#ifdef XML_ENABLED +#ifdef OLD_SCENE_FORMAT_ENABLED +#include "io/object_loader.h" +#include "io/object_saver.h" +#include "os/file_access.h" +#include "map.h" +#include "resource.h" +#include "xml_parser.h" + +/** + @author Juan Linietsky +*/ + +class ObjectFormatSaverXML : public ObjectFormatSaver { + + String local_path; + + + Ref optimizer; + + bool relative_paths; + bool bundle_resources; + bool skip_editor; + FileAccess *f; + String magic; + int depth; + Map resource_map; + + struct SavedObject { + + Variant meta; + String type; + + + struct SavedProperty { + + String name; + Variant value; + }; + + List properties; + }; + + List saved_resources; + + List saved_objects; + + void enter_tag(const String& p_section,const String& p_args=""); + void exit_tag(const String& p_section); + + void _find_resources(const Variant& p_variant); + void write_property(const String& p_name,const Variant& p_property,bool *r_ok=NULL); + + + void escape(String& p_str); + void write_tabs(int p_diff=0); + void write_string(String p_str,bool p_escape=true); + +public: + + virtual Error save(const Object *p_object,const Variant &p_meta); + + ObjectFormatSaverXML(FileAccess *p_file,const String& p_magic,const String& p_local_path,uint32_t p_flags,const Ref& p_optimizer); + ~ObjectFormatSaverXML(); +}; + +class ObjectFormatSaverInstancerXML : public ObjectFormatSaverInstancer { +public: + + virtual ObjectFormatSaver* instance(const String& p_file,const String& p_magic,uint32_t p_flags=0,const Ref& p_optimizer=Ref()); + virtual void get_recognized_extensions(List *p_extensions) const; + + virtual ~ObjectFormatSaverInstancerXML(); +}; + +/***********************************/ +/***********************************/ +/***********************************/ +/***********************************/ + +//#define OPTIMIZED_XML_LOADER + +#ifdef OPTIMIZED_XML_LOADER + +class ObjectFormatLoaderXML : public ObjectFormatLoader { + + Ref parser; + String local_path; + + Error _close_tag(const String& p_tag); + Error _parse_property(Variant& r_property,String& r_name); + +friend class ObjectFormatLoaderInstancerXML; + + List resource_cache; +public: + + + virtual Error load(Object **p_object,Variant &p_meta); + + +}; + +class ObjectFormatLoaderInstancerXML : public ObjectFormatLoaderInstancer { +public: + + virtual ObjectFormatLoaderXML* instance(const String& p_file,const String& p_magic); + virtual void get_recognized_extensions(List *p_extensions) const; + +}; + +#else + + +class ObjectFormatLoaderXML : public ObjectFormatLoader { + + String local_path; + + FileAccess *f; + + struct Tag { + + String name; + HashMap args; + }; + + _FORCE_INLINE_ Error _parse_array_element(Vector &buff,bool p_number_only,FileAccess *f,bool *end); + + mutable int lines; + uint8_t get_char() const; + int get_current_line() const; + +friend class ObjectFormatLoaderInstancerXML; + List tag_stack; + + List resource_cache; + Tag* parse_tag(bool* r_exit=NULL); + Error close_tag(const String& p_name); + void unquote(String& p_str); + Error goto_end_of_tag(); + Error parse_property_data(String &r_data); + Error parse_property(Variant& r_v, String &r_name); + +public: + + + virtual Error load(Object **p_object,Variant &p_meta); + + virtual ~ObjectFormatLoaderXML(); +}; + +class ObjectFormatLoaderInstancerXML : public ObjectFormatLoaderInstancer { +public: + + virtual ObjectFormatLoaderXML* instance(const String& p_file,const String& p_magic); + virtual void get_recognized_extensions(List *p_extensions) const; + + + +}; + +#endif +#endif +#endif +#endif diff --git a/core/io/object_loader.cpp b/core/io/object_loader.cpp new file mode 100644 index 00000000000..bb42cf73385 --- /dev/null +++ b/core/io/object_loader.cpp @@ -0,0 +1,84 @@ +/*************************************************************************/ +/* object_loader.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 "object_loader.h" + +#ifdef OLD_SCENE_FORMAT_ENABLED + +bool ObjectFormatLoaderInstancer::recognize(const String& p_extension) const { + + + List extensions; + get_recognized_extensions(&extensions); + for (List::Element *E=extensions.front();E;E=E->next()) { + + if (E->get().nocasecmp_to(p_extension)==0) + return true; + } + + return false; +} + +ObjectFormatLoaderInstancer *ObjectLoader::loader[MAX_LOADERS]; +int ObjectLoader::loader_count=0; + + +ObjectFormatLoader *ObjectLoader::instance_format_loader(const String& p_path,const String& p_magic,String p_force_extension) { + + String extension=p_force_extension.length()?p_force_extension:p_path.extension(); + + for (int i=0;irecognize(extension)) + continue; + ObjectFormatLoader *format_loader = loader[i]->instance(p_path,p_magic); + if (format_loader) + return format_loader; + } + + return NULL; +} + +void ObjectLoader::get_recognized_extensions(List *p_extensions) { + + for (int i=0;iget_recognized_extensions(p_extensions); + } +} + + + +void ObjectLoader::add_object_format_loader_instancer(ObjectFormatLoaderInstancer *p_format_loader_instancer) { + + ERR_FAIL_COND(loader_count>=MAX_LOADERS ); + loader[loader_count++]=p_format_loader_instancer; +} + + +#endif diff --git a/core/io/object_loader.h b/core/io/object_loader.h new file mode 100644 index 00000000000..9199313f040 --- /dev/null +++ b/core/io/object_loader.h @@ -0,0 +1,76 @@ +/*************************************************************************/ +/* object_loader.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 OBJECT_LOADER_H +#define OBJECT_LOADER_H + +#include "object.h" + +/** + @author Juan Linietsky +*/ +#ifdef OLD_SCENE_FORMAT_ENABLED +class ObjectFormatLoader { +public: + + virtual Error load(Object **p_object,Variant &p_meta)=0; + + virtual ~ObjectFormatLoader() {} +}; + +class ObjectFormatLoaderInstancer { +public: + + virtual ObjectFormatLoader* instance(const String& p_file,const String& p_magic)=0; + virtual void get_recognized_extensions(List *p_extensions) const=0; + bool recognize(const String& p_extension) const; + + virtual ~ObjectFormatLoaderInstancer() {} +}; + +class ObjectLoader { + + enum { + MAX_LOADERS=64 + }; + + static ObjectFormatLoaderInstancer *loader[MAX_LOADERS]; + static int loader_count; + +public: + + static ObjectFormatLoader *instance_format_loader(const String& p_path,const String& p_magic,String p_force_extension=""); + static void add_object_format_loader_instancer(ObjectFormatLoaderInstancer *p_format_loader_instancer); + static void get_recognized_extensions(List *p_extensions); + + + +}; + +#endif +#endif diff --git a/core/io/object_saver.cpp b/core/io/object_saver.cpp new file mode 100644 index 00000000000..cff2e836a78 --- /dev/null +++ b/core/io/object_saver.cpp @@ -0,0 +1,157 @@ +/*************************************************************************/ +/* object_saver.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 "object_saver.h" +#ifdef OLD_SCENE_FORMAT_ENABLED + +void OptimizedSaver::add_property(const StringName& p_name, const Variant& p_value) { + + ERR_FAIL_COND(!_list); + Property p; + p.name=p_name; + p.value=p_value; + _list->push_back(p); +} + +bool OptimizedSaver::optimize_object(const Object *p_object) { + + return false; //not optimize +} + +void OptimizedSaver::get_property_list(const Object* p_object,List *p_properties) { + + + _list=p_properties; + + bool res = call("optimize_object",p_object); + + if (!res) { + + List plist; + p_object->get_property_list(&plist); + for(List::Element *E=plist.front();E;E=E->next()) { + + PropertyInfo pinfo=E->get(); + if ((pinfo.usage&PROPERTY_USAGE_STORAGE) || (is_bundle_resources_enabled() && pinfo.usage&PROPERTY_USAGE_BUNDLE)) { + + add_property(pinfo.name,p_object->get(pinfo.name)); + } + } + + } + + _list=NULL; +} + +void OptimizedSaver::set_target_platform(const String& p_platform) { + + ERR_FAIL_COND(p_platform!="" && !p_platform.is_valid_identifier()); + platform=p_platform; +} + +String OptimizedSaver::get_target_platform() const { + + return platform; +} + +void OptimizedSaver::set_target_name(const String& p_name) { + + name=p_name; +} + +String OptimizedSaver::get_target_name() const { + + return name; +} + +void OptimizedSaver::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("set_target_platform","name"),&OptimizedSaver::set_target_platform); + ObjectTypeDB::bind_method(_MD("get_target_platform"),&OptimizedSaver::get_target_platform); + ObjectTypeDB::bind_method(_MD("set_target_name","name"),&OptimizedSaver::set_target_name); + ObjectTypeDB::bind_method(_MD("add_property","name","value"),&OptimizedSaver::add_property); + ObjectTypeDB::bind_method(_MD("optimize_object","obj"),&OptimizedSaver::optimize_object); +} + +OptimizedSaver::OptimizedSaver() { + + _list=NULL; +} + +ObjectFormatSaverInstancer *ObjectSaver::saver[MAX_LOADERS]; +int ObjectSaver::saver_count=0; + +bool ObjectFormatSaverInstancer::recognize(const String& p_extension) const { + + + List extensions; + get_recognized_extensions(&extensions); + for (List::Element *E=extensions.front();E;E=E->next()) { + + if (E->get().nocasecmp_to(p_extension.extension())==0) + return true; + } + + return false; +} + +ObjectFormatSaver *ObjectSaver::instance_format_saver(const String& p_path,const String& p_magic,String p_force_extension,uint32_t p_flags,const Ref& p_optimizer) { + + String extension=p_force_extension.length()?p_force_extension:p_path.extension(); + + for (int i=0;irecognize(extension)) + continue; + ObjectFormatSaver *format_saver = saver[i]->instance(p_path,p_magic,p_flags,p_optimizer); + if (format_saver) + return format_saver; + } + + return NULL; +} + +void ObjectSaver::get_recognized_extensions(List *p_extensions) { + + for (int i=0;iget_recognized_extensions(p_extensions); + } +} + + + +void ObjectSaver::add_object_format_saver_instancer(ObjectFormatSaverInstancer *p_format_saver_instancer) { + + ERR_FAIL_COND(saver_count>=MAX_LOADERS ); + saver[saver_count++]=p_format_saver_instancer; +} + + + +#endif diff --git a/core/io/object_saver.h b/core/io/object_saver.h new file mode 100644 index 00000000000..b22f7e05bbf --- /dev/null +++ b/core/io/object_saver.h @@ -0,0 +1,128 @@ +/*************************************************************************/ +/* object_saver.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 OBJECT_SAVER_H +#define OBJECT_SAVER_H + +#include "object.h" +#include "resource.h" +/** + @author Juan Linietsky +*/ + +#ifdef OLD_SCENE_FORMAT_ENABLED + +class OptimizedSaver : public Reference { + + OBJ_TYPE(OptimizedSaver,Reference); +public: + + struct Property { + + StringName name; + Variant value; + }; + +private: + + String name; + String platform; + List *_list; +protected: + + + void set_target_platform(const String& p_platform); + void set_target_name(const String& p_name); + void add_property(const StringName& p_name, const Variant& p_value); + static void _bind_methods(); + + virtual bool optimize_object(const Object *p_object); + +public: + + + virtual bool is_bundle_resources_enabled() const { return false; } + + String get_target_platform() const; + String get_target_name() const; + void get_property_list(const Object* p_object, List *p_properties); + + + OptimizedSaver(); + +}; + + +class ObjectFormatSaver { +public: + + virtual Error save(const Object *p_object,const Variant &p_meta=Variant())=0; + + virtual ~ObjectFormatSaver() {} +}; + +class ObjectFormatSaverInstancer { +public: + + virtual void get_recognized_extensions(List *p_extensions) const=0; + virtual ObjectFormatSaver* instance(const String& p_file,const String& p_magic="",uint32_t p_flags=0,const Ref& p_optimizer=Ref())=0; + bool recognize(const String& p_extension) const; + + virtual ~ObjectFormatSaverInstancer() {} +}; + +class ObjectSaver { + + enum { + MAX_LOADERS=64 + }; + + static ObjectFormatSaverInstancer *saver[MAX_LOADERS]; + static int saver_count; + +public: + + enum SaverFlags { + + FLAG_RELATIVE_PATHS=1, + FLAG_BUNDLE_RESOURCES=2, + FLAG_OMIT_EDITOR_PROPERTIES=4, + FLAG_SAVE_BIG_ENDIAN=8 + }; + + + static ObjectFormatSaver *instance_format_saver(const String& p_path,const String& p_magic,String p_force_extension="",uint32_t p_flags=0,const Ref& p_optimizer=Ref()); + static void get_recognized_extensions(List *p_extensions); + + static void add_object_format_saver_instancer(ObjectFormatSaverInstancer *p_format_saver_instancer); + + +}; + +#endif +#endif diff --git a/core/io/object_saver_base.cpp b/core/io/object_saver_base.cpp new file mode 100644 index 00000000000..94d715de28a --- /dev/null +++ b/core/io/object_saver_base.cpp @@ -0,0 +1,150 @@ +/*************************************************************************/ +/* object_saver_base.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 "object_saver_base.h" +#ifdef OLD_SCENE_FORMAT_ENABLED +void ObjectSaverBase::_find_resources(const Variant& p_variant) { + + switch(p_variant.get_type()) { + case Variant::OBJECT: { + + + RES res = p_variant.operator RefPtr(); + + if (res.is_null() || (res->get_path().length() && res->get_path().find("::") == -1 )) + return; + + if (resource_map.has(res)) + return; + + List property_list; + + res->get_property_list( &property_list ); + + List::Element *I=property_list.front(); + + while(I) { + + PropertyInfo pi=I->get(); + + if (pi.usage&PROPERTY_USAGE_STORAGE) { + + if (pi.type==Variant::OBJECT) { + + Variant v=res->get(I->get().name); + _find_resources(v); + } + } + + I=I->next(); + } + + resource_map[ res ] = resource_map.size(); //saved after, so the childs it needs are available when loaded + saved_resources.push_back(res); + + } break; + + case Variant::ARRAY: { + + Array varray=p_variant; + int len=varray.size(); + for(int i=0;i keys; + d.get_key_list(&keys); + for(List::Element *E=keys.front();E;E=E->next()) { + + Variant v = d[E->get()]; + _find_resources(v); + } + } break; + default: {} + } + +} + + +Error ObjectSaverBase::save(const Object *p_object,const Variant &p_meta) { + + ERR_EXPLAIN("write_object should supply either an object, a meta, or both"); + ERR_FAIL_COND_V(!p_object && p_meta.get_type()==Variant::NIL, ERR_INVALID_PARAMETER); + + SavedObject *so = memnew( SavedObject ); + + if (p_object) { + so->type=p_object->get_type(); + }; + + _find_resources(p_meta); + so->meta=p_meta; + + if (p_object) { + + List property_list; + p_object->get_property_list( &property_list ); + + List::Element *I=property_list.front(); + + while(I) { + + if (I->get().usage&PROPERTY_USAGE_STORAGE) { + + SavedObject::SavedProperty sp; + sp.name=I->get().name; + sp.value = p_object->get(I->get().name); + _find_resources(sp.value); + so->properties.push_back(sp); + } + + I=I->next(); + } + + } + + saved_objects.push_back(so); + + return OK; +} + +ObjectSaverBase::ObjectSaverBase() { + +}; + +ObjectSaverBase::~ObjectSaverBase() { + +}; +#endif diff --git a/core/io/object_saver_base.h b/core/io/object_saver_base.h new file mode 100644 index 00000000000..d9ec4a3abab --- /dev/null +++ b/core/io/object_saver_base.h @@ -0,0 +1,76 @@ +/*************************************************************************/ +/* object_saver_base.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 OBJECT_SAVER_BASE_H +#define OBJECT_SAVER_BASE_H + + +#ifdef OLD_SCENE_FORMAT_ENABLED +#include "object_saver.h" + +#include "map.h" +#include "resource.h" + +class ObjectSaverBase : public ObjectFormatSaver { + +protected: + + Map resource_map; + + struct SavedObject { + + Variant meta; + String type; + + + struct SavedProperty { + + String name; + Variant value; + }; + + List properties; + }; + + List saved_resources; + + List saved_objects; + + void _find_resources(const Variant& p_variant); + + virtual Error write()=0; +public: + + virtual Error save(const Object *p_object,const Variant &p_meta); + + ObjectSaverBase(); + ~ObjectSaverBase(); +}; + +#endif +#endif // OBJECT_SAVER_BASE_H diff --git a/core/io/packet_peer.cpp b/core/io/packet_peer.cpp new file mode 100644 index 00000000000..f67a10df2e1 --- /dev/null +++ b/core/io/packet_peer.cpp @@ -0,0 +1,255 @@ +/*************************************************************************/ +/* packet_peer.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 "packet_peer.h" + +#include "io/marshalls.h" +#include "globals.h" +/* helpers / binders */ + + + +PacketPeer::PacketPeer() { + + +} + +Error PacketPeer::get_packet_buffer(DVector &r_buffer) const { + + const uint8_t *buffer; + int buffer_size; + Error err = get_packet(&buffer,buffer_size); + if (err) + return err; + + r_buffer.resize(buffer_size); + if (buffer_size==0) + return OK; + + DVector::Write w = r_buffer.write(); + for(int i=0;i &p_buffer) { + + int len = p_buffer.size(); + if (len==0) + return OK; + + DVector::Read r = p_buffer.read(); + return put_packet(&r[0],len); + +} + +Error PacketPeer::get_var(Variant &r_variant) const { + + const uint8_t *buffer; + int buffer_size; + Error err = get_packet(&buffer,buffer_size); + if (err) + return err; + + return decode_variant(r_variant,buffer,buffer_size); + +} + +Error PacketPeer::put_var(const Variant& p_packet) { + + int len; + Error err = encode_variant(p_packet,NULL,len); // compute len first + if (err) + return err; + + if (len==0) + return OK; + + uint8_t *buf = (uint8_t*)alloca(len); + ERR_FAIL_COND_V(!buf,ERR_OUT_OF_MEMORY); + err = encode_variant(p_packet,buf,len); + ERR_FAIL_COND_V(err, err); + + return put_packet(buf, len); + +} + +Variant PacketPeer::_bnd_get_var() const { + Variant var; + get_var(var); + + return var; +}; + +void PacketPeer::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("get_var"),&PacketPeer::_bnd_get_var); + ObjectTypeDB::bind_method(_MD("put_var", "var:Variant"),&PacketPeer::put_var); +}; + +/***************/ + + +void PacketPeerStream::_set_stream_peer(REF p_peer) { + + ERR_FAIL_COND(p_peer.is_null()); + set_stream_peer(p_peer); +} + +void PacketPeerStream::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("set_stream_peer","peer:StreamPeer"),&PacketPeerStream::_set_stream_peer); +} + +Error PacketPeerStream::_poll_buffer() const { + + ERR_FAIL_COND_V(peer.is_null(),ERR_UNCONFIGURED); + + int read = 0; + Error err = peer->get_partial_data(&temp_buffer[0], ring_buffer.space_left(), read); + if (err) + return err; + + if (read==0) + return OK; + + int w = ring_buffer.write(&temp_buffer[0],read); + ERR_FAIL_COND_V(w!=read,ERR_BUG); + + return OK; +} + +int PacketPeerStream::get_available_packet_count() const { + + _poll_buffer(); + + uint32_t remaining = ring_buffer.data_left(); + + int ofs=0; + int count=0; + + while(remaining>=4) { + + uint8_t lbuf[4]; + ring_buffer.copy(lbuf,ofs,4); + uint32_t len = decode_uint32(lbuf); + remaining-=4; + ofs+=4; + if (len>remaining) + break; + remaining-=len; + ofs+=len; + count++; + } + + return count; +} + +Error PacketPeerStream::get_packet(const uint8_t **r_buffer,int &r_buffer_size) const { + + ERR_FAIL_COND_V(peer.is_null(),ERR_UNCONFIGURED); + _poll_buffer(); + + int remaining = ring_buffer.data_left(); + ERR_FAIL_COND_V(remaining<4,ERR_UNAVAILABLE); + uint8_t lbuf[4]; + ring_buffer.copy(lbuf,0,4); + remaining-=4; + uint32_t len = decode_uint32(lbuf); + ERR_FAIL_COND_V(remaining<(int)len,ERR_UNAVAILABLE); + + ring_buffer.read(lbuf,4); //get rid of first 4 bytes + ring_buffer.read(&temp_buffer[0],len); // read packet + + *r_buffer=&temp_buffer[0]; + r_buffer_size=len; + return OK; + +} + +Error PacketPeerStream::put_packet(const uint8_t *p_buffer,int p_buffer_size) { + + ERR_FAIL_COND_V(peer.is_null(),ERR_UNCONFIGURED); + Error err = _poll_buffer(); //won't hurt to poll here too + + if (err) + return err; + + if (p_buffer_size==0) + return OK; + + ERR_FAIL_COND_V( p_buffer_size<0, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V( p_buffer_size+4 > temp_buffer.size(), ERR_INVALID_PARAMETER ); + + encode_uint32(p_buffer_size,&temp_buffer[0]); + uint8_t *dst=&temp_buffer[4]; + for(int i=0;iput_data(&temp_buffer[0],p_buffer_size+4); +} + +int PacketPeerStream::get_max_packet_size() const { + + + return temp_buffer.size(); +} + +void PacketPeerStream::set_stream_peer(const Ref &p_peer) { + + ERR_FAIL_COND(p_peer.is_null()); + + if (p_peer.ptr() != peer.ptr()) { + ring_buffer.advance_read(ring_buffer.data_left()); // reset the ring buffer + }; + + peer=p_peer; +} + +void PacketPeerStream::set_input_buffer_max_size(int p_max_size) { + + //warning may lose packets + ERR_EXPLAIN("Buffer in use, resizing would cause loss of data"); + ERR_FAIL_COND(ring_buffer.data_left()); + ring_buffer.resize(nearest_shift(p_max_size+4)); + temp_buffer.resize(nearest_power_of_2(p_max_size+4)); + +} + +PacketPeerStream::PacketPeerStream() { + + + int rbsize=GLOBAL_DEF( "core/packet_stream_peer_max_buffer_po2",(16)); + + ring_buffer.resize(rbsize); + temp_buffer.resize(1< &r_buffer) const; + virtual Error put_packet_buffer(const DVector &p_buffer); + + virtual Error get_var(Variant &r_variant) const; + virtual Error put_var(const Variant& p_packet); + + PacketPeer(); + ~PacketPeer(){} +}; + +class PacketPeerStream : public PacketPeer { + + OBJ_TYPE(PacketPeerStream,PacketPeer); + + //the way the buffers work sucks, will change later + + mutable Ref peer; + mutable RingBuffer ring_buffer; + mutable Vector temp_buffer; + + Error _poll_buffer() const; +protected: + + void _set_stream_peer(REF p_peer); + static void _bind_methods(); +public: + + virtual int get_available_packet_count() const; + virtual Error get_packet(const uint8_t **r_buffer,int &r_buffer_size) const; + virtual Error put_packet(const uint8_t *p_buffer,int p_buffer_size); + + virtual int get_max_packet_size() const; + + void set_stream_peer(const Ref& p_peer); + void set_input_buffer_max_size(int p_max_size); + PacketPeerStream(); + +}; + + +#endif // PACKET_STREAM_H diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp new file mode 100644 index 00000000000..d2461498a61 --- /dev/null +++ b/core/io/resource_format_binary.cpp @@ -0,0 +1,1918 @@ +/*************************************************************************/ +/* resource_format_binary.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 "version.h" +#include "resource_format_binary.h" +#include "globals.h" +#include "io/file_access_compressed.h" +#include "io/marshalls.h" +//#define print_bl(m_what) print_line(m_what) +#define print_bl(m_what) + + +enum { + + //numbering must be different from variant, in case new variant types are added (variant must be always contiguous for jumptable optimization) + VARIANT_NIL=1, + VARIANT_BOOL=2, + VARIANT_INT=3, + VARIANT_REAL=4, + VARIANT_STRING=5, + VARIANT_VECTOR2=10, + VARIANT_RECT2=11, + VARIANT_VECTOR3=12, + VARIANT_PLANE=13, + VARIANT_QUAT=14, + VARIANT_AABB=15, + VARIANT_MATRIX3=16, + VARIANT_TRANSFORM=17, + VARIANT_MATRIX32=18, + VARIANT_COLOR=20, + VARIANT_IMAGE=21, + VARIANT_NODE_PATH=22, + VARIANT_RID=23, + VARIANT_OBJECT=24, + VARIANT_INPUT_EVENT=25, + VARIANT_DICTIONARY=26, + VARIANT_ARRAY=30, + VARIANT_RAW_ARRAY=31, + VARIANT_INT_ARRAY=32, + VARIANT_REAL_ARRAY=33, + VARIANT_STRING_ARRAY=34, + VARIANT_VECTOR3_ARRAY=35, + VARIANT_COLOR_ARRAY=36, + VARIANT_VECTOR2_ARRAY=37, + + IMAGE_ENCODING_EMPTY=0, + IMAGE_ENCODING_RAW=1, + IMAGE_ENCODING_LOSSLESS=2, + IMAGE_ENCODING_LOSSY=3, + + IMAGE_FORMAT_GRAYSCALE=0, + IMAGE_FORMAT_INTENSITY=1, + IMAGE_FORMAT_GRAYSCALE_ALPHA=2, + IMAGE_FORMAT_RGB=3, + IMAGE_FORMAT_RGBA=4, + IMAGE_FORMAT_INDEXED=5, + IMAGE_FORMAT_INDEXED_ALPHA=6, + IMAGE_FORMAT_BC1=7, + IMAGE_FORMAT_BC2=8, + IMAGE_FORMAT_BC3=9, + IMAGE_FORMAT_BC4=10, + IMAGE_FORMAT_BC5=11, + IMAGE_FORMAT_PVRTC2=12, + IMAGE_FORMAT_PVRTC2_ALPHA=13, + IMAGE_FORMAT_PVRTC4=14, + IMAGE_FORMAT_PVRTC4_ALPHA=15, + IMAGE_FORMAT_ETC=16, + IMAGE_FORMAT_CUSTOM=30, + + + OBJECT_EMPTY=0, + OBJECT_EXTERNAL_RESOURCE=1, + OBJECT_INTERNAL_RESOURCE=2, + FORMAT_VERSION=0 + + +}; + + +void ResourceInteractiveLoaderBinary::_advance_padding(uint32_t p_len) { + + uint32_t extra = 4-(p_len%4); + if (extra<4) { + for(uint32_t i=0;iget_8(); //pad to 32 + } + +} + +Error ResourceInteractiveLoaderBinary::parse_variant(Variant& r_v) { + + + uint32_t type = f->get_32(); + print_bl("find property of type: "+itos(type)); + + + switch(type) { + + case VARIANT_NIL: { + + r_v=Variant(); + } break; + case VARIANT_BOOL: { + + r_v=bool(f->get_32()); + } break; + case VARIANT_INT: { + + r_v=int(f->get_32()); + } break; + case VARIANT_REAL: { + + r_v=f->get_real(); + } break; + case VARIANT_STRING: { + + r_v=get_unicode_string(); + } break; + case VARIANT_VECTOR2: { + + Vector2 v; + v.x=f->get_real(); + v.y=f->get_real(); + r_v=v; + + } break; + case VARIANT_RECT2: { + + Rect2 v; + v.pos.x=f->get_real(); + v.pos.y=f->get_real(); + v.size.x=f->get_real(); + v.size.y=f->get_real(); + r_v=v; + + } break; + case VARIANT_VECTOR3: { + + Vector3 v; + v.x=f->get_real(); + v.y=f->get_real(); + v.z=f->get_real(); + r_v=v; + } break; + case VARIANT_PLANE: { + + Plane v; + v.normal.x=f->get_real(); + v.normal.y=f->get_real(); + v.normal.z=f->get_real(); + v.d=f->get_real(); + r_v=v; + } break; + case VARIANT_QUAT: { + Quat v; + v.x=f->get_real(); + v.y=f->get_real(); + v.z=f->get_real(); + v.w=f->get_real(); + r_v=v; + + } break; + case VARIANT_AABB: { + + AABB v; + v.pos.x=f->get_real(); + v.pos.y=f->get_real(); + v.pos.z=f->get_real(); + v.size.x=f->get_real(); + v.size.y=f->get_real(); + v.size.z=f->get_real(); + r_v=v; + + } break; + case VARIANT_MATRIX32: { + + Matrix32 v; + v.elements[0].x=f->get_real(); + v.elements[0].y=f->get_real(); + v.elements[1].x=f->get_real(); + v.elements[1].y=f->get_real(); + v.elements[2].x=f->get_real(); + v.elements[2].y=f->get_real(); + r_v=v; + + } break; + case VARIANT_MATRIX3: { + + Matrix3 v; + v.elements[0].x=f->get_real(); + v.elements[0].y=f->get_real(); + v.elements[0].z=f->get_real(); + v.elements[1].x=f->get_real(); + v.elements[1].y=f->get_real(); + v.elements[1].z=f->get_real(); + v.elements[2].x=f->get_real(); + v.elements[2].y=f->get_real(); + v.elements[2].z=f->get_real(); + r_v=v; + + } break; + case VARIANT_TRANSFORM: { + + Transform v; + v.basis.elements[0].x=f->get_real(); + v.basis.elements[0].y=f->get_real(); + v.basis.elements[0].z=f->get_real(); + v.basis.elements[1].x=f->get_real(); + v.basis.elements[1].y=f->get_real(); + v.basis.elements[1].z=f->get_real(); + v.basis.elements[2].x=f->get_real(); + v.basis.elements[2].y=f->get_real(); + v.basis.elements[2].z=f->get_real(); + v.origin.x=f->get_real(); + v.origin.y=f->get_real(); + v.origin.z=f->get_real(); + r_v=v; + } break; + case VARIANT_COLOR: { + + Color v; + v.r=f->get_real(); + v.g=f->get_real(); + v.b=f->get_real(); + v.a=f->get_real(); + r_v=v; + + } break; + case VARIANT_IMAGE: { + + + uint32_t encoding = f->get_32(); + if (encoding==IMAGE_ENCODING_EMPTY) { + r_v=Variant(); + break; + } else if (encoding==IMAGE_ENCODING_RAW) { + uint32_t width = f->get_32(); + uint32_t height = f->get_32(); + uint32_t mipmaps = f->get_32(); + uint32_t format = f->get_32(); + Image::Format fmt; + switch(format) { + + case IMAGE_FORMAT_GRAYSCALE: { fmt=Image::FORMAT_GRAYSCALE; } break; + case IMAGE_FORMAT_INTENSITY: { fmt=Image::FORMAT_INTENSITY; } break; + case IMAGE_FORMAT_GRAYSCALE_ALPHA: { fmt=Image::FORMAT_GRAYSCALE_ALPHA; } break; + case IMAGE_FORMAT_RGB: { fmt=Image::FORMAT_RGB; } break; + case IMAGE_FORMAT_RGBA: { fmt=Image::FORMAT_RGBA; } break; + case IMAGE_FORMAT_INDEXED: { fmt=Image::FORMAT_INDEXED; } break; + case IMAGE_FORMAT_INDEXED_ALPHA: { fmt=Image::FORMAT_INDEXED_ALPHA; } break; + case IMAGE_FORMAT_BC1: { fmt=Image::FORMAT_BC1; } break; + case IMAGE_FORMAT_BC2: { fmt=Image::FORMAT_BC2; } break; + case IMAGE_FORMAT_BC3: { fmt=Image::FORMAT_BC3; } break; + case IMAGE_FORMAT_BC4: { fmt=Image::FORMAT_BC4; } break; + case IMAGE_FORMAT_BC5: { fmt=Image::FORMAT_BC5; } break; + case IMAGE_FORMAT_PVRTC2: { fmt=Image::FORMAT_PVRTC2; } break; + case IMAGE_FORMAT_PVRTC2_ALPHA: { fmt=Image::FORMAT_PVRTC2_ALPHA; } break; + case IMAGE_FORMAT_PVRTC4: { fmt=Image::FORMAT_PVRTC4; } break; + case IMAGE_FORMAT_PVRTC4_ALPHA: { fmt=Image::FORMAT_PVRTC4_ALPHA; } break; + case IMAGE_FORMAT_ETC: { fmt=Image::FORMAT_ETC; } break; + case IMAGE_FORMAT_CUSTOM: { fmt=Image::FORMAT_CUSTOM; } break; + default: { + + ERR_FAIL_V(ERR_FILE_CORRUPT); + } + + } + + + uint32_t datalen = f->get_32(); + + DVector imgdata; + imgdata.resize(datalen); + DVector::Write w = imgdata.write(); + f->get_buffer(w.ptr(),datalen); + _advance_padding(datalen); + w=DVector::Write(); + + r_v=Image(width,height,mipmaps,fmt,imgdata); + + } else { + //compressed + DVector data; + data.resize(f->get_32()); + DVector::Write w = data.write(); + f->get_buffer(w.ptr(),data.size()); + w = DVector::Write(); + + Image img; + + if (encoding==IMAGE_ENCODING_LOSSY && Image::lossy_unpacker) { + + img = Image::lossy_unpacker(data); + } else if (encoding==IMAGE_ENCODING_LOSSLESS && Image::lossless_unpacker) { + + img = Image::lossless_unpacker(data); + } + _advance_padding(data.size()); + + + r_v=img; + + } + + } break; + case VARIANT_NODE_PATH: { + + Vector names; + Vector subnames; + StringName property; + bool absolute; + + int name_count = f->get_16(); + uint32_t subname_count = f->get_16(); + absolute=subname_count&0x8000; + subname_count&=0x7FFF; + + + for(int i=0;iget_32()]); + for(uint32_t i=0;iget_32()]); + property=string_map[f->get_32()]; + + NodePath np = NodePath(names,subnames,absolute,property); + //print_line("got path: "+String(np)); + + r_v=np; + + } break; + case VARIANT_RID: { + + r_v=f->get_32(); + } break; + case VARIANT_OBJECT: { + + uint32_t type=f->get_32(); + + switch(type) { + + case OBJECT_EMPTY: { + //do none + + } break; + case OBJECT_INTERNAL_RESOURCE: { + uint32_t index=f->get_32(); + String path = res_path+"::"+itos(index); + RES res = ResourceLoader::load(path); + if (res.is_null()) { + WARN_PRINT(String("Couldn't load resource: "+path).utf8().get_data()); + } + r_v=res; + + } break; + case OBJECT_EXTERNAL_RESOURCE: { + + String type = get_unicode_string(); + String path = get_unicode_string(); + + if (path.find("://")==-1 && path.is_rel_path()) { + // path is relative to file being loaded, so convert to a resource path + path=Globals::get_singleton()->localize_path(res_path.get_base_dir()+"/"+path); + + } + + RES res=ResourceLoader::load(path,type); + + if (res.is_null()) { + WARN_PRINT(String("Couldn't load resource: "+path).utf8().get_data()); + } + r_v=res; + + } break; + default: { + + ERR_FAIL_V(ERR_FILE_CORRUPT); + } break; + } + + } break; + case VARIANT_INPUT_EVENT: { + + } break; + case VARIANT_DICTIONARY: { + + uint32_t len=f->get_32(); + Dictionary d(len&0x80000000); //last bit means shared + len&=0x7FFFFFFF; + for(uint32_t i=0;iget_32(); + Array a(len&0x80000000); //last bit means shared + len&=0x7FFFFFFF; + a.resize(len); + for(uint32_t i=0;iget_32(); + + DVector array; + array.resize(len); + DVector::Write w = array.write(); + f->get_buffer(w.ptr(),len); + _advance_padding(len); + w=DVector::Write(); + r_v=array; + + } break; + case VARIANT_INT_ARRAY: { + + uint32_t len = f->get_32(); + + DVector array; + array.resize(len); + DVector::Write w = array.write(); + f->get_buffer((uint8_t*)w.ptr(),len*4); +#ifdef BIG_ENDIAN_ENABLED + { + uint32_t *ptr=(uint32_t*)w.ptr(); + for(int i=0;i::Write(); + r_v=array; + } break; + case VARIANT_REAL_ARRAY: { + + uint32_t len = f->get_32(); + + DVector array; + array.resize(len); + DVector::Write w = array.write(); + f->get_buffer((uint8_t*)w.ptr(),len*sizeof(real_t)); +#ifdef BIG_ENDIAN_ENABLED + { + uint32_t *ptr=(uint32_t*)w.ptr(); + for(int i=0;i::Write(); + r_v=array; + } break; + case VARIANT_STRING_ARRAY: { + + uint32_t len = f->get_32(); + DVector array; + array.resize(len); + DVector::Write w = array.write(); + for(uint32_t i=0;i::Write(); + r_v=array; + + + } break; + case VARIANT_VECTOR2_ARRAY: { + + uint32_t len = f->get_32(); + + DVector array; + array.resize(len); + DVector::Write w = array.write(); + if (sizeof(Vector2)==8) { + f->get_buffer((uint8_t*)w.ptr(),len*sizeof(real_t)*2); +#ifdef BIG_ENDIAN_ENABLED + { + uint32_t *ptr=(uint32_t*)w.ptr(); + for(int i=0;i::Write(); + r_v=array; + + } break; + case VARIANT_VECTOR3_ARRAY: { + + uint32_t len = f->get_32(); + + DVector array; + array.resize(len); + DVector::Write w = array.write(); + if (sizeof(Vector3)==12) { + f->get_buffer((uint8_t*)w.ptr(),len*sizeof(real_t)*3); +#ifdef BIG_ENDIAN_ENABLED + { + uint32_t *ptr=(uint32_t*)w.ptr(); + for(int i=0;i::Write(); + r_v=array; + + } break; + case VARIANT_COLOR_ARRAY: { + + uint32_t len = f->get_32(); + + DVector array; + array.resize(len); + DVector::Write w = array.write(); + if (sizeof(Color)==16) { + f->get_buffer((uint8_t*)w.ptr(),len*sizeof(real_t)*4); +#ifdef BIG_ENDIAN_ENABLED + { + uint32_t *ptr=(uint32_t*)w.ptr(); + for(int i=0;i::Write(); + r_v=array; + } break; + + default: { + ERR_FAIL_V(ERR_FILE_CORRUPT); + } break; + } + + + + return OK; //never reach anyway + +} + + +void ResourceInteractiveLoaderBinary::set_local_path(const String& p_local_path) { + + res_path=p_local_path; +} + +Ref ResourceInteractiveLoaderBinary::get_resource(){ + + + return resource; +} +Error ResourceInteractiveLoaderBinary::poll(){ + + if (error!=OK) + return error; + + + int s = stage; + + if (s=internal_resources.size()) { + + error=ERR_BUG; + ERR_FAIL_COND_V(s>=internal_resources.size(),error); + } + + bool main = s==(internal_resources.size()-1); + + //maybe it is loaded already + String path; + + + + if (!main) { + + path=internal_resources[s].path; + if (path.begins_with("local://")) + path=path.replace("local://",res_path+"::"); + + + + if (ResourceCache::has(path)) { + //already loaded, don't do anything + stage++; + error=OK; + return error; + } + } else { + + path=res_path; + } + + uint64_t offset = internal_resources[s].offset; + + f->seek(offset); + + String t = get_unicode_string(); + + Object *obj = ObjectTypeDB::instance(t); + if (!obj) { + error=ERR_FILE_CORRUPT; + ERR_EXPLAIN(local_path+":Resource of unrecognized type in file: "+t); + } + ERR_FAIL_COND_V(!obj,ERR_FILE_CORRUPT); + + Resource *r = obj->cast_to(); + if (!r) { + error=ERR_FILE_CORRUPT; + memdelete(obj); //bye + ERR_EXPLAIN(local_path+":Resoucre type in resource field not a resource, type is: "+obj->get_type()); + ERR_FAIL_COND_V(!r,ERR_FILE_CORRUPT); + } + + RES res = RES( r ); + + r->set_path(path); + + int pc = f->get_32(); + + //set properties + + for(int i=0;iget_32(); + if (name_idx>=(uint32_t)string_map.size()) { + error=ERR_FILE_CORRUPT; + ERR_FAIL_V(ERR_FILE_CORRUPT); + } + + Variant value; + + error = parse_variant(value); + if (error) + return error; + + res->set(string_map[name_idx],value); + } +#ifdef TOOLS_ENABLED + res->set_edited(false); +#endif + stage++; + + resource_cache.push_back(res); + + if (main) { + if (importmd_ofs) { + + f->seek(importmd_ofs); + Ref imd = memnew( ResourceImportMetadata ); + imd->set_editor(get_unicode_string()); + int sc = f->get_32(); + for(int i=0;iadd_source(src,md5); + } + int pc = f->get_32(); + + for(int i=0;iset_option(name,val); + } + res->set_import_metadata(imd); + + } + f->close(); + resource=res; + error=ERR_FILE_EOF; + + } else { + error=OK; + } + + return OK; + +} +int ResourceInteractiveLoaderBinary::get_stage() const{ + + return stage; +} +int ResourceInteractiveLoaderBinary::get_stage_count() const { + + return external_resources.size()+internal_resources.size(); +} + +String ResourceInteractiveLoaderBinary::get_unicode_string() { + + int len = f->get_32(); + if (len>str_buf.size()) { + str_buf.resize(len); + } + f->get_buffer((uint8_t*)&str_buf[0],len); + String s; + s.parse_utf8(&str_buf[0]); + return s; +} + + + +void ResourceInteractiveLoaderBinary::get_dependencies(FileAccess *p_f,List *p_dependencies) { + + open(p_f); + if (error) + return; + + for(int i=0;ipush_back(external_resources[i].path); + } + +} + + + + +void ResourceInteractiveLoaderBinary::open(FileAccess *p_f) { + + + error=OK; + + f=p_f; + uint8_t header[4]; + f->get_buffer(header,4); + if (header[0]=='R' && header[1]=='S' && header[2]=='C' && header[3]=='C') { + //compressed + FileAccessCompressed *fac = memnew( FileAccessCompressed ); + fac->open_after_magic(f); + f=fac; + + } else if (header[0]!='R' || header[1]!='S' || header[2]!='R' || header[3]!='C') { + //not normal + + error=ERR_FILE_UNRECOGNIZED; + ERR_EXPLAIN("Unrecognized binary resource file: "+local_path); + ERR_FAIL_V(); + } + + bool big_endian = f->get_32(); +#ifdef BIG_ENDIAN_ENABLED + endian_swap = !big_endian; +#else + bool endian_swap = big_endian; +#endif + + bool use_real64 = f->get_32(); + + f->set_endian_swap(big_endian!=0); //read big endian if saved as big endian + + uint32_t ver_major=f->get_32(); + uint32_t ver_minor=f->get_32(); + uint32_t ver_format=f->get_32(); + + print_bl("big endian: "+itos(big_endian)); + print_bl("endian swap: "+itos(endian_swap)); + print_bl("real64: "+itos(use_real64)); + print_bl("major: "+itos(ver_major)); + print_bl("minor: "+itos(ver_minor)); + print_bl("format: "+itos(ver_format)); + + if (ver_formatVERSION_MAJOR || (ver_major==VERSION_MAJOR && ver_minor>VERSION_MINOR)) { + + f->close(); + ERR_EXPLAIN("File Format '"+itos(FORMAT_VERSION)+"."+itos(ver_major)+"."+itos(ver_minor)+"' is too new! Please upgrade to a a new engine version: "+local_path); + ERR_FAIL(); + + } + + type=get_unicode_string(); + + print_bl("type: "+type); + + importmd_ofs = f->get_64(); + for(int i=0;i<14;i++) + f->get_32(); //skip a few reserved fields + + uint32_t string_table_size=f->get_32(); + string_map.resize(string_table_size); + for(uint32_t i=0;iget_32(); + for(uint32_t i=0;iget_32(); + + for(uint32_t i=0;iget_64(); + internal_resources.push_back(ir); + } + + print_bl("int resources: "+itos(int_resources_size)); + + + if (f->eof_reached()) { + + error=ERR_FILE_CORRUPT; + ERR_EXPLAIN("Premature End Of File: "+local_path); + ERR_FAIL(); + } + +} + +String ResourceInteractiveLoaderBinary::recognize(FileAccess *p_f) { + + error=OK; + + + f=p_f; + uint8_t header[4]; + f->get_buffer(header,4); + if (header[0]=='R' && header[1]=='S' && header[2]=='C' && header[3]=='C') { + //compressed + FileAccessCompressed *fac = memnew( FileAccessCompressed ); + fac->open_after_magic(f); + f=fac; + + } else if (header[0]!='R' || header[1]!='S' || header[2]!='R' || header[3]!='C') { + //not normal + return ""; + } + + bool big_endian = f->get_32(); +#ifdef BIG_ENDIAN_ENABLED + endian_swap = !big_endian; +#else + bool endian_swap = big_endian; +#endif + + bool use_real64 = f->get_32(); + + f->set_endian_swap(big_endian!=0); //read big endian if saved as big endian + + uint32_t ver_major=f->get_32(); + uint32_t ver_minor=f->get_32(); + uint32_t ver_format=f->get_32(); + + if (ver_formatVERSION_MAJOR || (ver_major==VERSION_MAJOR && ver_minor>VERSION_MINOR)) { + + f->close(); + return ""; + } + + String type=get_unicode_string(); + + return type; +} + +ResourceInteractiveLoaderBinary::ResourceInteractiveLoaderBinary() { + + f=NULL; + stage=0; + endian_swap=false; + use_real64=false; + error=OK; +} + +ResourceInteractiveLoaderBinary::~ResourceInteractiveLoaderBinary() { + + if (f) + memdelete(f); +} + + +Ref ResourceFormatLoaderBinary::load_interactive(const String &p_path) { + + + Error err; + FileAccess *f = FileAccess::open(p_path,FileAccess::READ,&err); + + if (err!=OK) { + + ERR_FAIL_COND_V(err!=OK,Ref()); + } + + Ref ria = memnew( ResourceInteractiveLoaderBinary ); + ria->local_path=Globals::get_singleton()->localize_path(p_path); + ria->res_path=ria->local_path; +// ria->set_local_path( Globals::get_singleton()->localize_path(p_path) ); + ria->open(f); + + + return ria; +} + +void ResourceFormatLoaderBinary::get_recognized_extensions_for_type(const String& p_type,List *p_extensions) const { + + if (p_type=="") { + get_recognized_extensions(p_extensions); + return; + } + + List extensions; + ObjectTypeDB::get_extensions_for_type(p_type,&extensions); + + extensions.sort(); + + for(List::Element *E=extensions.front();E;E=E->next()) { + String ext = E->get().to_lower(); + if (ext=="res") + continue; +// p_extensions->push_back("x"+ext); + p_extensions->push_back(ext); + } + + p_extensions->push_back("res"); + +} +void ResourceFormatLoaderBinary::get_recognized_extensions(List *p_extensions) const{ + + List extensions; + ObjectTypeDB::get_resource_base_extensions(&extensions); + extensions.sort(); + + for(List::Element *E=extensions.front();E;E=E->next()) { + String ext = E->get().to_lower(); + if (ext=="res") + continue; + p_extensions->push_back(ext); + } + + p_extensions->push_back("res"); +} + +bool ResourceFormatLoaderBinary::handles_type(const String& p_type) const{ + + + return true; //handles all +} + +Error ResourceFormatLoaderBinary::load_import_metadata(const String &p_path, Ref& r_var) const { + + + FileAccess *f = FileAccess::open(p_path,FileAccess::READ); + if (!f) { + return ERR_FILE_CANT_OPEN; + } + + Ref ria = memnew( ResourceInteractiveLoaderBinary ); + ria->local_path=Globals::get_singleton()->localize_path(p_path); + ria->res_path=ria->local_path; +// ria->set_local_path( Globals::get_singleton()->localize_path(p_path) ); + ria->recognize(f); + if(ria->error!=OK) + return ERR_FILE_UNRECOGNIZED; + f=ria->f; + uint64_t imp_ofs = f->get_64(); + + if (imp_ofs==0) + return ERR_UNAVAILABLE; + + f->seek(imp_ofs); + Ref imd = memnew( ResourceImportMetadata ); + imd->set_editor(ria->get_unicode_string()); + int sc = f->get_32(); + for(int i=0;iget_unicode_string(); + String md5 = ria->get_unicode_string(); + imd->add_source(src,md5); + } + int pc = f->get_32(); + + for(int i=0;iget_unicode_string(); + Variant val; + ria->parse_variant(val); + imd->set_option(name,val); + } + + r_var=imd; + + return OK; + +} + + +void ResourceFormatLoaderBinary::get_dependencies(const String& p_path,List *p_dependencies) { + + FileAccess *f = FileAccess::open(p_path,FileAccess::READ); + ERR_FAIL_COND(!f); + + Ref ria = memnew( ResourceInteractiveLoaderBinary ); + ria->local_path=Globals::get_singleton()->localize_path(p_path); + ria->res_path=ria->local_path; +// ria->set_local_path( Globals::get_singleton()->localize_path(p_path) ); + ria->get_dependencies(f,p_dependencies); +} + + +String ResourceFormatLoaderBinary::get_resource_type(const String &p_path) const { + + FileAccess *f = FileAccess::open(p_path,FileAccess::READ); + if (!f) { + return ""; //could not rwead + } + + Ref ria = memnew( ResourceInteractiveLoaderBinary ); + ria->local_path=Globals::get_singleton()->localize_path(p_path); + ria->res_path=ria->local_path; +// ria->set_local_path( Globals::get_singleton()->localize_path(p_path) ); + String r = ria->recognize(f); + return r; + + +} + + + +/////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////// + + +void ResourceFormatSaverBinaryInstance::_pad_buffer(int p_bytes) { + + int extra = 4-(p_bytes%4); + if (extra<4) { + for(int i=0;istore_8(0); //pad to 32 + } + +} + + +void ResourceFormatSaverBinaryInstance::write_variant(const Variant& p_property,const PropertyInfo& p_hint) { + + switch(p_property.get_type()) { + + case Variant::NIL: { + + f->store_32(VARIANT_NIL); + // don't store anything + } break; + case Variant::BOOL: { + + f->store_32(VARIANT_BOOL); + bool val=p_property; + f->store_32(val); + } break; + case Variant::INT: { + + f->store_32(VARIANT_INT); + int val=p_property; + f->store_32(val); + } break; + case Variant::REAL: { + + f->store_32(VARIANT_REAL); + real_t val=p_property; + f->store_real(val); + + } break; + case Variant::STRING: { + + f->store_32(VARIANT_STRING); + String val=p_property; + save_unicode_string(val); + + } break; + case Variant::VECTOR2: { + + f->store_32(VARIANT_VECTOR2); + Vector2 val=p_property; + f->store_real(val.x); + f->store_real(val.y); + + } break; + case Variant::RECT2: { + + f->store_32(VARIANT_RECT2); + Rect2 val=p_property; + f->store_real(val.pos.x); + f->store_real(val.pos.y); + f->store_real(val.size.x); + f->store_real(val.size.y); + + } break; + case Variant::VECTOR3: { + + f->store_32(VARIANT_VECTOR3); + Vector3 val=p_property; + f->store_real(val.x); + f->store_real(val.y); + f->store_real(val.z); + + } break; + case Variant::PLANE: { + + f->store_32(VARIANT_PLANE); + Plane val=p_property; + f->store_real(val.normal.x); + f->store_real(val.normal.y); + f->store_real(val.normal.z); + f->store_real(val.d); + + } break; + case Variant::QUAT: { + + f->store_32(VARIANT_QUAT); + Quat val=p_property; + f->store_real(val.x); + f->store_real(val.y); + f->store_real(val.z); + f->store_real(val.w); + + } break; + case Variant::_AABB: { + + f->store_32(VARIANT_AABB); + AABB val=p_property; + f->store_real(val.pos.x); + f->store_real(val.pos.y); + f->store_real(val.pos.z); + f->store_real(val.size.x); + f->store_real(val.size.y); + f->store_real(val.size.z); + + } break; + case Variant::MATRIX32: { + + f->store_32(VARIANT_MATRIX32); + Matrix32 val=p_property; + f->store_real(val.elements[0].x); + f->store_real(val.elements[0].y); + f->store_real(val.elements[1].x); + f->store_real(val.elements[1].y); + f->store_real(val.elements[2].x); + f->store_real(val.elements[2].y); + + } break; + case Variant::MATRIX3: { + + f->store_32(VARIANT_MATRIX3); + Matrix3 val=p_property; + f->store_real(val.elements[0].x); + f->store_real(val.elements[0].y); + f->store_real(val.elements[0].z); + f->store_real(val.elements[1].x); + f->store_real(val.elements[1].y); + f->store_real(val.elements[1].z); + f->store_real(val.elements[2].x); + f->store_real(val.elements[2].y); + f->store_real(val.elements[2].z); + + } break; + case Variant::TRANSFORM: { + + f->store_32(VARIANT_TRANSFORM); + Transform val=p_property; + f->store_real(val.basis.elements[0].x); + f->store_real(val.basis.elements[0].y); + f->store_real(val.basis.elements[0].z); + f->store_real(val.basis.elements[1].x); + f->store_real(val.basis.elements[1].y); + f->store_real(val.basis.elements[1].z); + f->store_real(val.basis.elements[2].x); + f->store_real(val.basis.elements[2].y); + f->store_real(val.basis.elements[2].z); + f->store_real(val.origin.x); + f->store_real(val.origin.y); + f->store_real(val.origin.z); + + } break; + case Variant::COLOR: { + + f->store_32(VARIANT_COLOR); + Color val=p_property; + f->store_real(val.r); + f->store_real(val.g); + f->store_real(val.b); + f->store_real(val.a); + + } break; + case Variant::IMAGE: { + + f->store_32(VARIANT_IMAGE); + Image val =p_property; + if (val.empty()) { + f->store_32(IMAGE_ENCODING_EMPTY); + break; + } + + int encoding=IMAGE_ENCODING_RAW; + float quality=0.7; + + if (val.get_format() <= Image::FORMAT_INDEXED_ALPHA) { + //can only compress uncompressed stuff + + if (p_hint.hint==PROPERTY_HINT_IMAGE_COMPRESS_LOSSY && Image::lossy_packer) { + encoding=IMAGE_ENCODING_LOSSY; + float qs=p_hint.hint_string.to_double(); + if (qs!=0.0) + quality=qs; + + } else if (p_hint.hint==PROPERTY_HINT_IMAGE_COMPRESS_LOSSLESS && Image::lossless_packer) { + encoding=IMAGE_ENCODING_LOSSLESS; + + } + } + + f->store_32(encoding); //raw encoding + + if (encoding==IMAGE_ENCODING_RAW) { + + + f->store_32(val.get_width()); + f->store_32(val.get_height()); + f->store_32(val.get_mipmaps()); + switch(val.get_format()) { + + case Image::FORMAT_GRAYSCALE: f->store_32(IMAGE_FORMAT_GRAYSCALE ); break; ///< one byte per pixel: f->store_32(IMAGE_FORMAT_ ); break; 0-255 + case Image::FORMAT_INTENSITY: f->store_32(IMAGE_FORMAT_INTENSITY ); break; ///< one byte per pixel: f->store_32(IMAGE_FORMAT_ ); break; 0-255 + case Image::FORMAT_GRAYSCALE_ALPHA: f->store_32(IMAGE_FORMAT_GRAYSCALE_ALPHA ); break; ///< two bytes per pixel: f->store_32(IMAGE_FORMAT_ ); break; 0-255. alpha 0-255 + case Image::FORMAT_RGB: f->store_32(IMAGE_FORMAT_RGB ); break; ///< one byte R: f->store_32(IMAGE_FORMAT_ ); break; one byte G: f->store_32(IMAGE_FORMAT_ ); break; one byte B + case Image::FORMAT_RGBA: f->store_32(IMAGE_FORMAT_RGBA ); break; ///< one byte R: f->store_32(IMAGE_FORMAT_ ); break; one byte G: f->store_32(IMAGE_FORMAT_ ); break; one byte B: f->store_32(IMAGE_FORMAT_ ); break; one byte A + case Image::FORMAT_INDEXED: f->store_32(IMAGE_FORMAT_INDEXED ); break; ///< index byte 0-256: f->store_32(IMAGE_FORMAT_ ); break; and after image end: f->store_32(IMAGE_FORMAT_ ); break; 256*3 bytes of palette + case Image::FORMAT_INDEXED_ALPHA: f->store_32(IMAGE_FORMAT_INDEXED_ALPHA ); break; ///< index byte 0-256: f->store_32(IMAGE_FORMAT_ ); break; and after image end: f->store_32(IMAGE_FORMAT_ ); break; 256*4 bytes of palette (alpha) + case Image::FORMAT_BC1: f->store_32(IMAGE_FORMAT_BC1 ); break; // DXT1 + case Image::FORMAT_BC2: f->store_32(IMAGE_FORMAT_BC2 ); break; // DXT3 + case Image::FORMAT_BC3: f->store_32(IMAGE_FORMAT_BC3 ); break; // DXT5 + case Image::FORMAT_BC4: f->store_32(IMAGE_FORMAT_BC4 ); break; // ATI1 + case Image::FORMAT_BC5: f->store_32(IMAGE_FORMAT_BC5 ); break; // ATI2 + case Image::FORMAT_PVRTC2: f->store_32(IMAGE_FORMAT_PVRTC2 ); break; + case Image::FORMAT_PVRTC2_ALPHA: f->store_32(IMAGE_FORMAT_PVRTC2_ALPHA ); break; + case Image::FORMAT_PVRTC4: f->store_32(IMAGE_FORMAT_PVRTC4 ); break; + case Image::FORMAT_PVRTC4_ALPHA: f->store_32(IMAGE_FORMAT_PVRTC4_ALPHA ); break; + case Image::FORMAT_ETC: f->store_32(IMAGE_FORMAT_ETC); break; + case Image::FORMAT_CUSTOM: f->store_32(IMAGE_FORMAT_CUSTOM ); break; + default: {} + + } + + int dlen = val.get_data().size(); + f->store_32(dlen); + DVector::Read r = val.get_data().read(); + f->store_buffer(r.ptr(),dlen); + _pad_buffer(dlen); + } else { + + DVector data; + if (encoding==IMAGE_ENCODING_LOSSY) { + data=Image::lossy_packer(val,quality); + + } else if (encoding==IMAGE_ENCODING_LOSSLESS) { + data=Image::lossless_packer(val); + + } + + int ds=data.size(); + f->store_32(ds); + if (ds>0) { + DVector::Read r = data.read(); + f->store_buffer(r.ptr(),ds); + + _pad_buffer(ds); + + } + } + + } break; + case Variant::NODE_PATH: { + f->store_32(VARIANT_NODE_PATH); + NodePath np=p_property; + f->store_16(np.get_name_count()); + uint16_t snc = np.get_subname_count(); + if (np.is_absolute()) + snc|=0x8000; + f->store_16(snc); + for(int i=0;istore_32(get_string_index(np.get_name(i))); + for(int i=0;istore_32(get_string_index(np.get_subname(i))); + f->store_32(get_string_index(np.get_property())); + + } break; + case Variant::_RID: { + + f->store_32(VARIANT_RID); + WARN_PRINT("Can't save RIDs"); + RID val = p_property; + f->store_32(val.get_id()); + } break; + case Variant::OBJECT: { + + f->store_32(VARIANT_OBJECT); + RES res = p_property; + if (res.is_null()) { + f->store_32(OBJECT_EMPTY); + return; // don't save it + } + + if (res->get_path().length() && res->get_path().find("::")==-1) { + f->store_32(OBJECT_EXTERNAL_RESOURCE); + save_unicode_string(res->get_save_type()); + String path=relative_paths?local_path.path_to_file(res->get_path()):res->get_path(); + if (no_extensions) + path=path.basename()+".*"; + save_unicode_string(path); + } else { + + if (!resource_map.has(res)) { + f->store_32(OBJECT_EMPTY); + ERR_EXPLAIN("Resource was not pre cached for the resource section, bug?"); + ERR_FAIL(); + } + + f->store_32(OBJECT_INTERNAL_RESOURCE); + f->store_32(resource_map[res]); + //internal resource + } + + + } break; + case Variant::INPUT_EVENT: { + + f->store_32(VARIANT_INPUT_EVENT); + WARN_PRINT("Can't save InputEvent (maybe it could..)"); + } break; + case Variant::DICTIONARY: { + + f->store_32(VARIANT_DICTIONARY); + Dictionary d = p_property; + f->store_32(uint32_t(d.size())|(d.is_shared()?0x80000000:0)); + + List keys; + d.get_key_list(&keys); + + for(List::Element *E=keys.front();E;E=E->next()) { + + //if (!_check_type(dict[E->get()])) + // continue; + + write_variant(E->get()); + write_variant(d[E->get()]); + } + + + } break; + case Variant::ARRAY: { + + f->store_32(VARIANT_ARRAY); + Array a=p_property; + f->store_32(uint32_t(a.size())|(a.is_shared()?0x80000000:0)); + for(int i=0;istore_32(VARIANT_RAW_ARRAY); + DVector arr = p_property; + int len=arr.size(); + f->store_32(len); + DVector::Read r = arr.read(); + f->store_buffer(r.ptr(),len); + _pad_buffer(len); + + } break; + case Variant::INT_ARRAY: { + + f->store_32(VARIANT_INT_ARRAY); + DVector arr = p_property; + int len=arr.size(); + f->store_32(len); + DVector::Read r = arr.read(); + for(int i=0;istore_32(r[i]); + + } break; + case Variant::REAL_ARRAY: { + + f->store_32(VARIANT_REAL_ARRAY); + DVector arr = p_property; + int len=arr.size(); + f->store_32(len); + DVector::Read r = arr.read(); + for(int i=0;istore_real(r[i]); + } + + } break; + case Variant::STRING_ARRAY: { + + f->store_32(VARIANT_STRING_ARRAY); + DVector arr = p_property; + int len=arr.size(); + f->store_32(len); + DVector::Read r = arr.read(); + for(int i=0;istore_32(VARIANT_VECTOR3_ARRAY); + DVector arr = p_property; + int len=arr.size(); + f->store_32(len); + DVector::Read r = arr.read(); + for(int i=0;istore_real(r[i].x); + f->store_real(r[i].y); + f->store_real(r[i].z); + } + + } break; + case Variant::VECTOR2_ARRAY: { + + f->store_32(VARIANT_VECTOR2_ARRAY); + DVector arr = p_property; + int len=arr.size(); + f->store_32(len); + DVector::Read r = arr.read(); + for(int i=0;istore_real(r[i].x); + f->store_real(r[i].y); + } + + } break; + case Variant::COLOR_ARRAY: { + + f->store_32(VARIANT_COLOR_ARRAY); + DVector arr = p_property; + int len=arr.size(); + f->store_32(len); + DVector::Read r = arr.read(); + for(int i=0;istore_real(r[i].r); + f->store_real(r[i].g); + f->store_real(r[i].b); + f->store_real(r[i].a); + } + + } break; + default: { + + ERR_EXPLAIN("Invalid variant"); + ERR_FAIL(); + } + } +} + + +void ResourceFormatSaverBinaryInstance::_find_resources(const Variant& p_variant,bool p_main) { + + + switch(p_variant.get_type()) { + case Variant::OBJECT: { + + + RES res = p_variant.operator RefPtr(); + + if (res.is_null()) + return; + + if (!p_main && (!bundle_resources ) && res->get_path().length() && res->get_path().find("::") == -1 ) { + external_resources.insert(res); + return; + } + + + if (resource_map.has(res)) + return; + + List property_list; + + res->get_property_list(&property_list); + + for(List::Element *E=property_list.front();E;E=E->next()) { + + if (E->get().usage&PROPERTY_USAGE_STORAGE || (bundle_resources && E->get().usage&PROPERTY_USAGE_BUNDLE)) { + + _find_resources(res->get(E->get().name)); + } + } + + resource_map[ res ] = saved_resources.size(); + saved_resources.push_back(res); + + } break; + + case Variant::ARRAY: { + + Array varray=p_variant; + int len=varray.size(); + for(int i=0;i keys; + d.get_key_list(&keys); + for(List::Element *E=keys.front();E;E=E->next()) { + + _find_resources(E->get()); + Variant v = d[E->get()]; + _find_resources(v); + } + } break; + case Variant::NODE_PATH: { + //take the chance and save node path strings + NodePath np = p_variant; + for(int i=0;i property_list; + p_object->get_property_list( &property_list ); + + for(List::Element *E=property_list.front();E;E=E->next()) { + + if (skip_editor && E->get().name.begins_with("__editor")) + continue; + if (E->get().usage&PROPERTY_USAGE_STORAGE || (bundle_resources && E->get().usage&PROPERTY_USAGE_BUNDLE)) { + + SavedObject::SavedProperty sp; + sp.name_idx=get_string_index(E->get().name); + sp.value = p_object->get(E->get().name); + _find_resources(sp.value); + so->properties.push_back(sp); + } + } + + return OK; + +} + + + +Error ResourceFormatSaverBinary::save(const Object *p_object,const Variant &p_meta) { + + ERR_FAIL_COND_V(!f,ERR_UNCONFIGURED); + ERR_EXPLAIN("write_object should supply either an object, a meta, or both"); + ERR_FAIL_COND_V(!p_object && p_meta.get_type()==Variant::NIL, ERR_INVALID_PARAMETER); + + SavedObject *so = memnew( SavedObject ); + + if (p_object) + so->type=p_object->get_type(); + + _find_resources(p_meta); + so->meta=p_meta; + Error err = _save_obj(p_object,so); + ERR_FAIL_COND_V( err, ERR_INVALID_DATA ); + + saved_objects.push_back(so); + + return OK; +} +#endif + +void ResourceFormatSaverBinaryInstance::save_unicode_string(const String& p_string) { + + + CharString utf8 = p_string.utf8(); + f->store_32(utf8.length()+1); + f->store_buffer((const uint8_t*)utf8.get_data(),utf8.length()+1); +} + +int ResourceFormatSaverBinaryInstance::get_string_index(const String& p_string) { + + StringName s=p_string; + if (string_map.has(s)) + return string_map[s]; + + string_map[s]=strings.size(); + strings.push_back(s); + return strings.size()-1; +} + + +Error ResourceFormatSaverBinaryInstance::save(const String &p_path,const RES& p_resource,uint32_t p_flags) { + + Error err; + if (p_flags&ResourceSaver::FLAG_COMPRESS) { + FileAccessCompressed *fac = memnew( FileAccessCompressed ); + fac->configure("RSCC"); + f=fac; + err = fac->_open(p_path,FileAccess::WRITE); + if (err) + memdelete(f); + + } else { + f=FileAccess::open(p_path,FileAccess::WRITE,&err); + } + + + ERR_FAIL_COND_V(err,err); + FileAccessRef _fref(f); + + + relative_paths=p_flags&ResourceSaver::FLAG_RELATIVE_PATHS; + skip_editor=p_flags&ResourceSaver::FLAG_OMIT_EDITOR_PROPERTIES; + bundle_resources=p_flags&ResourceSaver::FLAG_BUNDLE_RESOURCES; + big_endian=p_flags&ResourceSaver::FLAG_SAVE_BIG_ENDIAN; + no_extensions=p_flags&ResourceSaver::FLAG_NO_EXTENSION; + + local_path=p_path.get_base_dir(); + //bin_meta_idx = get_string_index("__bin_meta__"); //is often used, so create + + _find_resources(p_resource,true); + + if (!(p_flags&ResourceSaver::FLAG_COMPRESS)) { + //save header compressed + static const uint8_t header[4]={'R','S','R','C'}; + f->store_buffer(header,4); + } + + if (big_endian) { + f->store_32(1); + f->set_endian_swap(true); + } else + f->store_32(0); + + f->store_32(0); //64 bits file, false for now + f->store_32(VERSION_MAJOR); + f->store_32(VERSION_MINOR); + f->store_32(FORMAT_VERSION); + + //f->store_32(saved_resources.size()+external_resources.size()); // load steps -not needed + save_unicode_string(p_resource->get_type()); + uint64_t md_at = f->get_pos(); + f->store_64(0); //offset to impoty metadata + for(int i=0;i<14;i++) + f->store_32(0); // reserved + + + List resources; + + + { + + + for(List::Element *E=saved_resources.front();E;E=E->next()) { + + + ResourceData &rd = resources.push_back(ResourceData())->get(); + rd.type=E->get()->get_type(); + + List property_list; + E->get()->get_property_list( &property_list ); + + for(List::Element *F=property_list.front();F;F=F->next()) { + + if (skip_editor && F->get().name.begins_with("__editor")) + continue; + if (F->get().usage&PROPERTY_USAGE_STORAGE || (bundle_resources && F->get().usage&PROPERTY_USAGE_BUNDLE)) { + Property p; + p.name_idx=get_string_index(F->get().name); + p.value=E->get()->get(F->get().name); + if (F->get().usage&PROPERTY_USAGE_STORE_IF_NONZERO && p.value.is_zero()) + continue; + p.pi=F->get(); + + rd.properties.push_back(p); + + } + } + + + + } + } + + + f->store_32(strings.size()); //string table size + for(int i=0;istore_32(external_resources.size()); //amount of external resources + for(Set::Element *E=external_resources.front();E;E=E->next()) { + + save_unicode_string(E->get()->get_save_type()); + String path = E->get()->get_path(); + if (no_extensions) + path=path.basename()+".*"; + save_unicode_string(path); + } + // save internal resource table + f->store_32(saved_resources.size()); //amount of internal resources + Vector ofs_pos; + for(List::Element *E=saved_resources.front();E;E=E->next()) { + + RES r = E->get(); + if (r->get_path()=="" || r->get_path().find("::")!=-1) + save_unicode_string("local://"+itos(ofs_pos.size())); + else + save_unicode_string(r->get_path()); //actual external + ofs_pos.push_back(f->get_pos()); + f->store_64(0); //offset in 64 bits + } + + Vector ofs_table; +// int saved_idx=0; + //now actually save the resources + + for(List::Element *E=resources.front();E;E=E->next()) { + + ResourceData & rd = E->get(); + + ofs_table.push_back(f->get_pos()); + save_unicode_string(rd.type); + f->store_32(rd.properties.size()); + + for (List::Element *F=rd.properties.front();F;F=F->next()) { + + Property &p=F->get(); + f->store_32(p.name_idx); + write_variant(p.value,F->get().pi); + } + + } + + for(int i=0;iseek(ofs_pos[i]); + f->store_64(ofs_table[i]); + } + + f->seek_end(); + if (p_resource->get_import_metadata().is_valid()) { + uint64_t md_pos = f->get_pos(); + Ref imd=p_resource->get_import_metadata(); + save_unicode_string(imd->get_editor()); + f->store_32(imd->get_source_count()); + for(int i=0;iget_source_count();i++) { + save_unicode_string(imd->get_source_path(i)); + save_unicode_string(imd->get_source_md5(i)); + } + List options; + imd->get_options(&options); + f->store_32(options.size()); + for(List::Element *E=options.front();E;E=E->next()) { + save_unicode_string(E->get()); + write_variant(imd->get_option(E->get())); + } + + f->seek(md_at); + f->store_64(md_pos); + f->seek_end(); + } + + + f->store_buffer((const uint8_t*)"RSRC",4); //magic at end + + f->close(); + + + return OK; +} + + + +Error ResourceFormatSaverBinary::save(const String &p_path,const RES& p_resource,uint32_t p_flags) { + + + String local_path = Globals::get_singleton()->localize_path(p_path); + ResourceFormatSaverBinaryInstance saver; + return saver.save(local_path,p_resource,p_flags); + +} + + +bool ResourceFormatSaverBinary::recognize(const RES& p_resource) const { + + return true; //all recognized + +} + +void ResourceFormatSaverBinary::get_recognized_extensions(const RES& p_resource,List *p_extensions) const { + + + //here comes the sun, lalalala + String base = p_resource->get_base_extension().to_lower(); + if (base!="res") { + + p_extensions->push_back(base); + } + + p_extensions->push_back("res"); + + +} + diff --git a/core/io/resource_format_binary.h b/core/io/resource_format_binary.h new file mode 100644 index 00000000000..006148f5a8d --- /dev/null +++ b/core/io/resource_format_binary.h @@ -0,0 +1,184 @@ +/*************************************************************************/ +/* resource_format_binary.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 RESOURCE_FORMAT_BINARY_H +#define RESOURCE_FORMAT_BINARY_H + +#include "io/resource_loader.h" +#include "io/resource_saver.h" +#include "os/file_access.h" + + +class ResourceInteractiveLoaderBinary : public ResourceInteractiveLoader { + + String local_path; + String res_path; + String type; + Ref resource; + + FileAccess *f; + + + bool endian_swap; + bool use_real64; + uint64_t importmd_ofs; + + Vector str_buf; + List resource_cache; + + //Map string_map; + Vector string_map; + + struct ExtResoucre { + String path; + String type; + }; + + Vector external_resources; + + struct IntResoucre { + String path; + uint64_t offset; + }; + + Vector internal_resources; + + String get_unicode_string(); + void _advance_padding(uint32_t p_len); + + Error error; + + int stage; + +friend class ResourceFormatLoaderBinary; + + + Error parse_variant(Variant& r_v); + +public: + + virtual void set_local_path(const String& p_local_path); + virtual Ref get_resource(); + virtual Error poll(); + virtual int get_stage() const; + virtual int get_stage_count() const; + + void open(FileAccess *p_f); + String recognize(FileAccess *p_f); + void get_dependencies(FileAccess *p_f,List *p_dependencies); + + + ResourceInteractiveLoaderBinary(); + ~ResourceInteractiveLoaderBinary(); + +}; + +class ResourceFormatLoaderBinary : public ResourceFormatLoader { +public: + + virtual Ref load_interactive(const String &p_path); + virtual void get_recognized_extensions_for_type(const String& p_type,List *p_extensions) const; + virtual void get_recognized_extensions(List *p_extensions) const; + virtual bool handles_type(const String& p_type) const; + virtual String get_resource_type(const String &p_path) const; + virtual void get_dependencies(const String& p_path,List *p_dependencies); + virtual Error load_import_metadata(const String &p_path, Ref& r_var) const; + + + +}; + + + + +class ResourceFormatSaverBinaryInstance { + + String local_path; + + bool no_extensions; + bool relative_paths; + bool bundle_resources; + bool skip_editor; + bool big_endian; + int bin_meta_idx; + FileAccess *f; + String magic; + Map resource_map; + Map string_map; + Vector strings; + + + Set external_resources; + List saved_resources; + + + struct Property { + int name_idx; + Variant value; + PropertyInfo pi; + + }; + + struct ResourceData { + + String type; + List properties; + }; + + + + + void _pad_buffer(int p_bytes); + void write_variant(const Variant& p_property,const PropertyInfo& p_hint=PropertyInfo()); + void _find_resources(const Variant& p_variant,bool p_main=false); + void save_unicode_string(const String& p_string); + int get_string_index(const String& p_string); +public: + + + Error save(const String &p_path,const RES& p_resource,uint32_t p_flags=0); +}; + + + +class ResourceFormatSaverBinary : public ResourceFormatSaver { + + + + +public: + + virtual Error save(const String &p_path,const RES& p_resource,uint32_t p_flags=0); + virtual bool recognize(const RES& p_resource) const; + virtual void get_recognized_extensions(const RES& p_resource,List *p_extensions) const; + + +}; + + +#endif // RESOURCE_FORMAT_BINARY_H diff --git a/core/io/resource_format_xml.cpp b/core/io/resource_format_xml.cpp new file mode 100644 index 00000000000..f175c73e988 --- /dev/null +++ b/core/io/resource_format_xml.cpp @@ -0,0 +1,2557 @@ +/*************************************************************************/ +/* resource_format_xml.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 "resource_format_xml.h" +#include "globals.h" +#include "version.h" + + + +ResourceInteractiveLoaderXML::Tag* ResourceInteractiveLoaderXML::parse_tag(bool *r_exit,bool p_printerr) { + + + while(get_char()!='<' && !f->eof_reached()) {} + if (f->eof_reached()) { + return NULL; + } + + Tag tag; + bool exit=false; + if (r_exit) + *r_exit=false; + + bool complete=false; + while(!f->eof_reached()) { + + CharType c=get_char(); + if (c<33 && tag.name.length() && !exit) { + break; + } else if (c=='>') { + complete=true; + break; + } else if (c=='/') { + exit=true; + } else { + tag.name+=c; + } + } + + if (f->eof_reached()) { + + return NULL; + } + + if (exit) { + if (!tag_stack.size()) { + if (!p_printerr) + return NULL; + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Unmatched exit tag "); + ERR_FAIL_COND_V(!tag_stack.size(),NULL); + } + + if (tag_stack.back()->get().name!=tag.name) { + if (!p_printerr) + return NULL; + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Mismatched exit tag. Got , expected get().name+">"); + ERR_FAIL_COND_V(tag_stack.back()->get().name!=tag.name,NULL); + } + + if (!complete) { + while(get_char()!='>' && !f->eof_reached()) {} + if (f->eof_reached()) + return NULL; + } + + if (r_exit) + *r_exit=true; + + tag_stack.pop_back(); + return NULL; + + } + + if (!complete) { + String name; + String value; + bool reading_value=false; + + while(!f->eof_reached()) { + + CharType c=get_char(); + if (c=='>') { + if (value.length()) { + + tag.args[name]=value; + } + break; + + } else if ( ((!reading_value && (c<33)) || c=='=' || c=='"') && tag.name.length()) { + + if (!reading_value && name.length()) { + + reading_value=true; + } else if (reading_value && value.length()) { + + tag.args[name]=value; + name=""; + value=""; + reading_value=false; + } + + } else if (reading_value) { + + value+=c; + } else { + + name+=c; + } + } + + if (f->eof_reached()) + return NULL; + } + + tag_stack.push_back(tag); + + return &tag_stack.back()->get(); +} + + +Error ResourceInteractiveLoaderXML::close_tag(const String& p_name) { + + int level=0; + bool inside_tag=false; + + while(true) { + + if (f->eof_reached()) { + + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": EOF found while attempting to find "); + ERR_FAIL_COND_V( f->eof_reached(), ERR_FILE_CORRUPT ); + } + + uint8_t c = get_char(); + + if (c == '<') { + + if (inside_tag) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Malformed XML. Already inside Tag."); + ERR_FAIL_COND_V(inside_tag,ERR_FILE_CORRUPT); + } + inside_tag=true; + c = get_char(); + if (c == '/') { + + --level; + } else { + + ++level; + }; + } else if (c == '>') { + + if (!inside_tag) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Malformed XML. Already outside Tag"); + ERR_FAIL_COND_V(!inside_tag,ERR_FILE_CORRUPT); + } + inside_tag=false; + if (level == -1) { + tag_stack.pop_back(); + return OK; + }; + }; + } + + return OK; +} + +void ResourceInteractiveLoaderXML::unquote(String& p_str) { + + p_str=p_str.strip_edges(); + p_str=p_str.replace("\"",""); + p_str=p_str.replace(">","<"); + p_str=p_str.replace("<",">"); + p_str=p_str.replace("'","'"); + p_str=p_str.replace(""","\""); + for (int i=1;i<32;i++) { + + char chr[2]={i,0}; + p_str=p_str.replace("&#"+String::num(i)+";",chr); + } + p_str=p_str.replace("&","&"); + + //p_str.parse_utf8( p_str.ascii(true).get_data() ); + +} + +Error ResourceInteractiveLoaderXML::goto_end_of_tag() { + + uint8_t c; + while(true) { + + c=get_char(); + if (c=='>') //closetag + break; + if (f->eof_reached()) { + + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": EOF found while attempting to find close tag."); + ERR_FAIL_COND_V( f->eof_reached(), ERR_FILE_CORRUPT ); + } + + } + tag_stack.pop_back(); + + return OK; +} + + +Error ResourceInteractiveLoaderXML::parse_property_data(String &r_data) { + + r_data=""; + CharString cs; + while(true) { + + CharType c=get_char(); + if (c=='<') + break; + ERR_FAIL_COND_V(f->eof_reached(),ERR_FILE_CORRUPT); + cs.push_back(c); + } + + cs.push_back(0); + + r_data.parse_utf8(cs.get_data()); + + while(get_char()!='>' && !f->eof_reached()) {} + if (f->eof_reached()) { + + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Malformed XML."); + ERR_FAIL_COND_V( f->eof_reached(), ERR_FILE_CORRUPT ); + } + + r_data=r_data.strip_edges(); + tag_stack.pop_back(); + + return OK; +} + + +Error ResourceInteractiveLoaderXML::_parse_array_element(Vector &buff,bool p_number_only,FileAccess *f,bool *end) { + + if (buff.empty()) + buff.resize(32); // optimi + + int buff_max=buff.size(); + int buff_size=0; + *end=false; + char *buffptr=&buff[0]; + bool found=false; + bool quoted=false; + + while(true) { + + char c=get_char(); + + if (c==0) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": File corrupt (zero found)."); + ERR_FAIL_V(ERR_FILE_CORRUPT); + } else if (c=='"') { + quoted=!quoted; + } else if ((!quoted && ((p_number_only && c<33) || c==',')) || c=='<') { + + + if (c=='<') { + *end=true; + break; + } + if (c<32 && f->eof_reached()) { + *end=true; + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": File corrupt (unexpected EOF)."); + ERR_FAIL_V(ERR_FILE_CORRUPT); + } + + if (found) + break; + + } else { + + found=true; + if (buff_size>=buff_max) { + + buff_max++; + buff.resize(buff_max); + + } + + buffptr[buff_size]=c; + buff_size++; + } + } + + if (buff_size>=buff_max) { + + buff_max++; + buff.resize(buff_max); + + } + + buff[buff_size]=0; + buff_size++; + + return OK; +} + +Error ResourceInteractiveLoaderXML::parse_property(Variant& r_v, String &r_name) { + + bool exit; + Tag *tag = parse_tag(&exit); + + if (!tag) { + if (exit) // shouldn't have exited + return ERR_FILE_EOF; + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": File corrupt (No Property Tag)."); + ERR_FAIL_V(ERR_FILE_CORRUPT); + } + + r_v=Variant(); + r_name=""; + + + //ERR_FAIL_COND_V(tag->name!="property",ERR_FILE_CORRUPT); + //ERR_FAIL_COND_V(!tag->args.has("name"),ERR_FILE_CORRUPT); +// ERR_FAIL_COND_V(!tag->args.has("type"),ERR_FILE_CORRUPT); + + //String name=tag->args["name"]; + //ERR_FAIL_COND_V(name=="",ERR_FILE_CORRUPT); + String type=tag->name; + String name=tag->args["name"]; + + if (type=="") { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": 'type' field is empty."); + ERR_FAIL_COND_V(type=="",ERR_FILE_CORRUPT); + } + + if (type=="dictionary") { + + Dictionary d( tag->args.has("shared") && (String(tag->args["shared"])=="true" || String(tag->args["shared"])=="1")); + + while(true) { + + Error err; + String tagname; + Variant key; + + int dictline = get_current_line(); + + + err=parse_property(key,tagname); + + if (err && err!=ERR_FILE_EOF) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Error parsing dictionary: "+name+" (from line "+itos(dictline)+")"); + ERR_FAIL_COND_V(err && err!=ERR_FILE_EOF,err); + } + //ERR_FAIL_COND_V(tagname!="key",ERR_FILE_CORRUPT); + if (err) + break; + Variant value; + err=parse_property(value,tagname); + if (err) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Error parsing dictionary: "+name+" (from line "+itos(dictline)+")"); + } + + ERR_FAIL_COND_V(err,err); + //ERR_FAIL_COND_V(tagname!="value",ERR_FILE_CORRUPT); + + d[key]=value; + } + + + //err=parse_property_data(name); // skip the rest + //ERR_FAIL_COND_V(err,err); + + r_name=name; + r_v=d; + return OK; + + } else if (type=="array") { + + if (!tag->args.has("len")) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Array missing 'len' field: "+name); + ERR_FAIL_COND_V(!tag->args.has("len"),ERR_FILE_CORRUPT); + } + + + int len=tag->args["len"].to_int(); + bool shared = tag->args.has("shared") && (String(tag->args["shared"])=="true" || String(tag->args["shared"])=="1"); + + Array array(shared); + array.resize(len); + + Error err; + Variant v; + String tagname; + int idx=0; + while( (err=parse_property(v,tagname))==OK ) { + + ERR_CONTINUE( idx <0 || idx >=len ); + + array.set(idx,v); + idx++; + } + + if (idx!=len) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Error loading array (size mismatch): "+name); + ERR_FAIL_COND_V(idx!=len,err); + } + + if (err!=ERR_FILE_EOF) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Error loading array: "+name); + ERR_FAIL_COND_V(err!=ERR_FILE_EOF,err); + } + + //err=parse_property_data(name); // skip the rest + //ERR_FAIL_COND_V(err,err); + + r_name=name; + r_v=array; + return OK; + + } else if (type=="resource") { + + if (tag->args.has("path")) { + + String path=tag->args["path"]; + String hint; + if (tag->args.has("resource_type")) + hint=tag->args["resource_type"]; + + if (path.begins_with("local://")) + path=path.replace("local://",local_path+"::"); + else if (path.find("://")==-1 && path.is_rel_path()) { + // path is relative to file being loaded, so convert to a resource path + path=Globals::get_singleton()->localize_path(local_path.get_base_dir()+"/"+path); + + } + + //take advantage of the resource loader cache. The resource is cached on it, even if + RES res=ResourceLoader::load(path,hint); + + + if (res.is_null()) { + + WARN_PRINT(String("Couldn't load resource: "+path).ascii().get_data()); + } + + r_v=res.get_ref_ptr(); + } + + + + Error err=goto_end_of_tag(); + if (err) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Error closing tag."); + ERR_FAIL_COND_V(err,err); + } + + + r_name=name; + + return OK; + + } else if (type=="image") { + + if (!tag->args.has("encoding")) { + //empty image + r_v=Image(); + String sdfsdfg; + Error err=parse_property_data(sdfsdfg); + return OK; + } + + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Image missing 'encoding' field."); + ERR_FAIL_COND_V( !tag->args.has("encoding"), ERR_FILE_CORRUPT ); + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Image missing 'width' field."); + ERR_FAIL_COND_V( !tag->args.has("width"), ERR_FILE_CORRUPT ); + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Image missing 'height' field."); + ERR_FAIL_COND_V( !tag->args.has("height"), ERR_FILE_CORRUPT ); + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Image missing 'format' field."); + ERR_FAIL_COND_V( !tag->args.has("format"), ERR_FILE_CORRUPT ); + + String encoding=tag->args["encoding"]; + + if (encoding=="raw") { + String width=tag->args["width"]; + String height=tag->args["height"]; + String format=tag->args["format"]; + int mipmaps=tag->args.has("mipmaps")?int(tag->args["mipmaps"].to_int()):int(0); + int custom_size = tag->args.has("custom_size")?int(tag->args["custom_size"].to_int()):int(0); + + r_name=name; + + Image::Format imgformat; + + + if (format=="grayscale") { + imgformat=Image::FORMAT_GRAYSCALE; + } else if (format=="intensity") { + imgformat=Image::FORMAT_INTENSITY; + } else if (format=="grayscale_alpha") { + imgformat=Image::FORMAT_GRAYSCALE_ALPHA; + } else if (format=="rgb") { + imgformat=Image::FORMAT_RGB; + } else if (format=="rgba") { + imgformat=Image::FORMAT_RGBA; + } else if (format=="indexed") { + imgformat=Image::FORMAT_INDEXED; + } else if (format=="indexed_alpha") { + imgformat=Image::FORMAT_INDEXED_ALPHA; + } else if (format=="bc1") { + imgformat=Image::FORMAT_BC1; + } else if (format=="bc2") { + imgformat=Image::FORMAT_BC2; + } else if (format=="bc3") { + imgformat=Image::FORMAT_BC3; + } else if (format=="bc4") { + imgformat=Image::FORMAT_BC4; + } else if (format=="bc5") { + imgformat=Image::FORMAT_BC5; + } else if (format=="pvrtc2") { + imgformat=Image::FORMAT_PVRTC2; + } else if (format=="pvrtc2a") { + imgformat=Image::FORMAT_PVRTC2_ALPHA; + } else if (format=="pvrtc4") { + imgformat=Image::FORMAT_PVRTC4; + } else if (format=="pvrtc4a") { + imgformat=Image::FORMAT_PVRTC4_ALPHA; + } else if (format=="etc") { + imgformat=Image::FORMAT_ETC; + } else if (format=="custom") { + imgformat=Image::FORMAT_CUSTOM; + } else { + + ERR_FAIL_V( ERR_FILE_CORRUPT ); + } + + + int datasize; + int w=width.to_int(); + int h=height.to_int(); + + if (w == 0 && w == 0) { + //r_v = Image(w, h, imgformat); + r_v=Image(); + String sdfsdfg; + Error err=parse_property_data(sdfsdfg); + return OK; + }; + + if (imgformat==Image::FORMAT_CUSTOM) { + + datasize=custom_size; + } else { + + datasize = Image::get_image_data_size(h,w,imgformat,mipmaps); + } + + if (datasize==0) { + //r_v = Image(w, h, imgformat); + r_v=Image(); + String sdfsdfg; + Error err=parse_property_data(sdfsdfg); + return OK; + }; + + DVector pixels; + pixels.resize(datasize); + DVector::Write wb = pixels.write(); + + int idx=0; + uint8_t byte; + while( idx='0' && c<='9') || (c>='A' && c<='F') || (c>='a' && c<='f') ) { + + if (idx&1) { + + byte|=HEX2CHR(c); + wb[idx>>1]=byte; + } else { + + byte=HEX2CHR(c)<<4; + } + + idx++; + } + + } + ERR_FAIL_COND_V(f->eof_reached(),ERR_FILE_CORRUPT); + + wb=DVector::Write(); + + r_v=Image(w,h,mipmaps,imgformat,pixels); + String sdfsdfg; + Error err=parse_property_data(sdfsdfg); + ERR_FAIL_COND_V(err,err); + + return OK; + } + + ERR_FAIL_V(ERR_FILE_CORRUPT); + + } else if (type=="raw_array") { + + if (!tag->args.has("len")) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": RawArray missing 'len' field: "+name); + ERR_FAIL_COND_V(!tag->args.has("len"),ERR_FILE_CORRUPT); + } + int len=tag->args["len"].to_int(); + + DVector bytes; + bytes.resize(len); + DVector::Write w=bytes.write(); + uint8_t *bytesptr=w.ptr(); + int idx=0; + uint8_t byte; + + while( idx>1]=byte; + } else { + + byte=HEX2CHR(c)<<4; + } + + idx++; + } + + ERR_FAIL_COND_V(f->eof_reached(),ERR_FILE_CORRUPT); + + w=DVector::Write(); + r_v=bytes; + String sdfsdfg; + Error err=parse_property_data(sdfsdfg); + ERR_FAIL_COND_V(err,err); + r_name=name; + + return OK; + + } else if (type=="int_array") { + + if (!tag->args.has("len")) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Array missing 'len' field: "+name); + ERR_FAIL_COND_V(!tag->args.has("len"),ERR_FILE_CORRUPT); + } + int len=tag->args["len"].to_int(); + + DVector ints; + ints.resize(len); + DVector::Write w=ints.write(); + int *intsptr=w.ptr(); + int idx=0; + String str; +#if 0 + while( idxeof_reached(),ERR_FILE_CORRUPT); + + if (c<33 || c==',' || c=='<') { + + if (str.length()) { + + intsptr[idx]=str.to_int(); + str=""; + idx++; + } + + if (c=='<') { + + while(get_char()!='>' && !f->eof_reached()) {} + ERR_FAIL_COND_V(f->eof_reached(),ERR_FILE_CORRUPT); + break; + } + + } else { + + str+=c; + } + } + +#else + + Vector tmpdata; + + while( idx::Write(); + + r_v=ints; + Error err=goto_end_of_tag(); + ERR_FAIL_COND_V(err,err); + r_name=name; + + return OK; + } else if (type=="real_array") { + + if (!tag->args.has("len")) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Array missing 'len' field: "+name); + ERR_FAIL_COND_V(!tag->args.has("len"),ERR_FILE_CORRUPT); + } + int len=tag->args["len"].to_int();; + + DVector reals; + reals.resize(len); + DVector::Write w=reals.write(); + real_t *realsptr=w.ptr(); + int idx=0; + String str; + + +#if 0 + while( idxeof_reached(),ERR_FILE_CORRUPT); + + + if (c<33 || c==',' || c=='<') { + + if (str.length()) { + + realsptr[idx]=str.to_double(); + str=""; + idx++; + } + + if (c=='<') { + + while(get_char()!='>' && !f->eof_reached()) {} + ERR_FAIL_COND_V(f->eof_reached(),ERR_FILE_CORRUPT); + break; + } + + } else { + + str+=c; + } + } + +#else + + + + Vector tmpdata; + + while( idx::Write(); + r_v=reals; + + Error err=goto_end_of_tag(); + ERR_FAIL_COND_V(err,err); + r_name=name; + + return OK; + } else if (type=="string_array") { +#if 0 + if (!tag->args.has("len")) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Array missing 'len' field: "+name); + ERR_FAIL_COND_V(!tag->args.has("len"),ERR_FILE_CORRUPT); + } + int len=tag->args["len"].to_int(); + + DVector strings; + strings.resize(len); + DVector::Write w=strings.write(); + String *stringsptr=w.ptr(); + int idx=0; + String str; + + bool inside_str=false; + CharString cs; + while( idxeof_reached(),ERR_FILE_CORRUPT); + + + if (c=='"') { + if (inside_str) { + + cs.push_back(0); + String str; + str.parse_utf8(cs.get_data()); + unquote(str); + stringsptr[idx]=str; + cs.clear(); + idx++; + inside_str=false; + } else { + inside_str=true; + } + } else if (c=='<') { + + while(get_char()!='>' && !f->eof_reached()) {} + ERR_FAIL_COND_V(f->eof_reached(),ERR_FILE_CORRUPT); + break; + + + } else if (inside_str){ + + cs.push_back(c); + } + } + w=DVector::Write(); + r_v=strings; + String sdfsdfg; + Error err=parse_property_data(sdfsdfg); + ERR_FAIL_COND_V(err,err); + + r_name=name; + + return OK; +#endif + if (!tag->args.has("len")) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": String Array missing 'len' field: "+name); + ERR_FAIL_COND_V(!tag->args.has("len"),ERR_FILE_CORRUPT); + } + + + int len=tag->args["len"].to_int(); + + StringArray array; + array.resize(len); + DVector::Write w = array.write(); + + Error err; + Variant v; + String tagname; + int idx=0; + + + while( (err=parse_property(v,tagname))==OK ) { + + ERR_CONTINUE( idx <0 || idx >=len ); + String str = v; //convert back to string + w[idx]=str; + idx++; + } + + if (idx!=len) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Error loading array (size mismatch): "+name); + ERR_FAIL_COND_V(idx!=len,err); + } + + if (err!=ERR_FILE_EOF) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Error loading array: "+name); + ERR_FAIL_COND_V(err!=ERR_FILE_EOF,err); + } + + //err=parse_property_data(name); // skip the rest + //ERR_FAIL_COND_V(err,err); + + r_name=name; + r_v=array; + return OK; + + } else if (type=="vector3_array") { + + if (!tag->args.has("len")) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Array missing 'len' field: "+name); + ERR_FAIL_COND_V(!tag->args.has("len"),ERR_FILE_CORRUPT); + } + int len=tag->args["len"].to_int();; + + DVector vectors; + vectors.resize(len); + DVector::Write w=vectors.write(); + Vector3 *vectorsptr=w.ptr(); + int idx=0; + int subidx=0; + Vector3 auxvec; + String str; + +// uint64_t tbegin = OS::get_singleton()->get_ticks_usec(); +#if 0 + while( idxeof_reached(),ERR_FILE_CORRUPT); + + + if (c<33 || c==',' || c=='<') { + + if (str.length()) { + + auxvec[subidx]=str.to_double(); + subidx++; + str=""; + if (subidx==3) { + vectorsptr[idx]=auxvec; + + idx++; + subidx=0; + } + } + + if (c=='<') { + + while(get_char()!='>' && !f->eof_reached()) {} + ERR_FAIL_COND_V(f->eof_reached(),ERR_FILE_CORRUPT); + break; + } + + } else { + + str+=c; + } + } +#else + + Vector tmpdata; + + while( idxget_ticks_usec() - tbegin)/1000000.0; + + + w=DVector::Write(); + r_v=vectors; + String sdfsdfg; + Error err=goto_end_of_tag(); + ERR_FAIL_COND_V(err,err); + r_name=name; + + return OK; + + } else if (type=="vector2_array") { + + if (!tag->args.has("len")) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Array missing 'len' field: "+name); + ERR_FAIL_COND_V(!tag->args.has("len"),ERR_FILE_CORRUPT); + } + int len=tag->args["len"].to_int();; + + DVector vectors; + vectors.resize(len); + DVector::Write w=vectors.write(); + Vector2 *vectorsptr=w.ptr(); + int idx=0; + int subidx=0; + Vector2 auxvec; + String str; + +// uint64_t tbegin = OS::get_singleton()->get_ticks_usec(); +#if 0 + while( idxeof_reached(),ERR_FILE_CORRUPT); + + + if (c<22 || c==',' || c=='<') { + + if (str.length()) { + + auxvec[subidx]=str.to_double(); + subidx++; + str=""; + if (subidx==2) { + vectorsptr[idx]=auxvec; + + idx++; + subidx=0; + } + } + + if (c=='<') { + + while(get_char()!='>' && !f->eof_reached()) {} + ERR_FAIL_COND_V(f->eof_reached(),ERR_FILE_CORRUPT); + break; + } + + } else { + + str+=c; + } + } +#else + + Vector tmpdata; + + while( idxget_ticks_usec() - tbegin)/1000000.0; + + + w=DVector::Write(); + r_v=vectors; + String sdfsdfg; + Error err=goto_end_of_tag(); + ERR_FAIL_COND_V(err,err); + r_name=name; + + return OK; + + } else if (type=="color_array") { + + if (!tag->args.has("len")) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Array missing 'len' field: "+name); + ERR_FAIL_COND_V(!tag->args.has("len"),ERR_FILE_CORRUPT); + } + int len=tag->args["len"].to_int();; + + DVector colors; + colors.resize(len); + DVector::Write w=colors.write(); + Color *colorsptr=w.ptr(); + int idx=0; + int subidx=0; + Color auxcol; + String str; + + while( idxeof_reached(),ERR_FILE_CORRUPT); + + + if (c<33 || c==',' || c=='<') { + + if (str.length()) { + + auxcol[subidx]=str.to_double(); + subidx++; + str=""; + if (subidx==4) { + colorsptr[idx]=auxcol; + idx++; + subidx=0; + } + } + + if (c=='<') { + + while(get_char()!='>' && !f->eof_reached()) {} + ERR_FAIL_COND_V(f->eof_reached(),ERR_FILE_CORRUPT); + break; + } + + } else { + + str+=c; + } + } + w=DVector::Write(); + r_v=colors; + String sdfsdfg; + Error err=parse_property_data(sdfsdfg); + ERR_FAIL_COND_V(err,err); + r_name=name; + + return OK; + } + + + String data; + Error err = parse_property_data(data); + ERR_FAIL_COND_V(err!=OK,err); + + if (type=="nil") { + // uh do nothing + + } else if (type=="bool") { + // uh do nothing + if (data.nocasecmp_to("true")==0 || data.to_int()!=0) + r_v=true; + else + r_v=false; + } else if (type=="int") { + + r_v=data.to_int(); + } else if (type=="real") { + + r_v=data.to_double(); + } else if (type=="string") { + + String str=data; + unquote(str); + r_v=str; + } else if (type=="vector3") { + + + r_v=Vector3( + data.get_slice(",",0).to_double(), + data.get_slice(",",1).to_double(), + data.get_slice(",",2).to_double() + ); + + } else if (type=="vector2") { + + + r_v=Vector2( + data.get_slice(",",0).to_double(), + data.get_slice(",",1).to_double() + ); + + } else if (type=="plane") { + + r_v=Plane( + data.get_slice(",",0).to_double(), + data.get_slice(",",1).to_double(), + data.get_slice(",",2).to_double(), + data.get_slice(",",3).to_double() + ); + + } else if (type=="quaternion") { + + r_v=Quat( + data.get_slice(",",0).to_double(), + data.get_slice(",",1).to_double(), + data.get_slice(",",2).to_double(), + data.get_slice(",",3).to_double() + ); + + } else if (type=="rect2") { + + r_v=Rect2( + Vector2( + data.get_slice(",",0).to_double(), + data.get_slice(",",1).to_double() + ), + Vector2( + data.get_slice(",",2).to_double(), + data.get_slice(",",3).to_double() + ) + ); + + + } else if (type=="aabb") { + + r_v=AABB( + Vector3( + data.get_slice(",",0).to_double(), + data.get_slice(",",1).to_double(), + data.get_slice(",",2).to_double() + ), + Vector3( + data.get_slice(",",3).to_double(), + data.get_slice(",",4).to_double(), + data.get_slice(",",5).to_double() + ) + ); + + } else if (type=="matrix32") { + + Matrix32 m3; + for (int i=0;i<3;i++) { + for (int j=0;j<2;j++) { + m3.elements[i][j]=data.get_slice(",",i*2+j).to_double(); + } + } + r_v=m3; + + } else if (type=="matrix3") { + + Matrix3 m3; + for (int i=0;i<3;i++) { + for (int j=0;j<3;j++) { + m3.elements[i][j]=data.get_slice(",",i*3+j).to_double(); + } + } + r_v=m3; + + } else if (type=="transform") { + + Transform tr; + for (int i=0;i<3;i++) { + for (int j=0;j<3;j++) { + tr.basis.elements[i][j]=data.get_slice(",",i*3+j).to_double(); + } + + } + tr.origin=Vector3( + data.get_slice(",",9).to_double(), + data.get_slice(",",10).to_double(), + data.get_slice(",",11).to_double() + ); + r_v=tr; + + } else if (type=="color") { + + r_v=Color( + data.get_slice(",",0).to_double(), + data.get_slice(",",1).to_double(), + data.get_slice(",",2).to_double(), + data.get_slice(",",3).to_double() + ); + + } else if (type=="node_path") { + + String str=data; + unquote(str); + r_v=NodePath( str ); + } else if (type=="input_event") { + + // ? + } else { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Unrecognized tag in file: "+type); + ERR_FAIL_V(ERR_FILE_CORRUPT); + } + r_name=name; + return OK; +} + + + +int ResourceInteractiveLoaderXML::get_current_line() const { + + return lines; +} + + +uint8_t ResourceInteractiveLoaderXML::get_char() const { + + uint8_t c = f->get_8(); + if (c=='\n') + lines++; + return c; + +} + + + + +/// + +void ResourceInteractiveLoaderXML::set_local_path(const String& p_local_path) { + + res_path=p_local_path; +} + +Ref ResourceInteractiveLoaderXML::get_resource() { + + return resource; +} +Error ResourceInteractiveLoaderXML::poll() { + + if (error!=OK) + return error; + + + bool exit; + Tag *tag = parse_tag(&exit); + + + if (!tag) { + error=ERR_FILE_CORRUPT; + if (!exit) // shouldn't have exited + ERR_FAIL_V(error); + error=ERR_FILE_EOF; + return error; + } + + RES res; + //Object *obj=NULL; + + bool main; + + if (tag->name=="ext_resource") { + + error=ERR_FILE_CORRUPT; + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": missing 'path' field."); + ERR_FAIL_COND_V(!tag->args.has("path"),ERR_FILE_CORRUPT); + + String type; + if (tag->args.has("type")) + type=tag->args["type"]; + + String path = tag->args["path"]; + + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": can't use a local path, this is a bug?."); + ERR_FAIL_COND_V(path.begins_with("local://"),ERR_FILE_CORRUPT); + + if (path.find("://")==-1 && path.is_rel_path()) { + // path is relative to file being loaded, so convert to a resource path + path=Globals::get_singleton()->localize_path(local_path.get_base_dir()+"/"+path); + } + + + RES res = ResourceLoader::load(path,type); + + if (res.is_null()) { + + if (ResourceLoader::get_abort_on_missing_resources()) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": referenced unexisting resource at: "+path); + ERR_FAIL_V(error); + } else { + ResourceLoader::notify_load_error("Resource Not Found: "+path); + } + } else { + + resource_cache.push_back(res); + } + + Error err = close_tag("ext_resource"); + if (err) + return error; + + + error=OK; + resource_current++; + return error; + + } else if (tag->name=="resource") { + + main=false; + } else if (tag->name=="main_resource") { + main=true; + } else { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": unexpected main tag: "+tag->name); + error=ERR_FILE_CORRUPT; + ERR_FAIL_V(error); + } + + + String type; + String path; + + if (!main) { + //loading resource + + error=ERR_FILE_CORRUPT; + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": missing 'len' field."); + ERR_FAIL_COND_V(!tag->args.has("path"),ERR_FILE_CORRUPT); + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": missing 'type' field."); + ERR_FAIL_COND_V(!tag->args.has("type"),ERR_FILE_CORRUPT); + path=tag->args["path"]; + error=OK; + + if (path.begins_with("local://")) { + //built-in resource (but really external) + path=path.replace("local://",local_path+"::"); + } + + + if (ResourceCache::has(path)) { + Error err = close_tag(tag->name); + if (err) { + error=ERR_FILE_CORRUPT; + } + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Unable to close tag."); + ERR_FAIL_COND_V( err, err ); + resource_current++; + error=OK; + return OK; + } + + type = tag->args["type"]; + } else { + type=resource_type; + } + + Object *obj = ObjectTypeDB::instance(type); + if (!obj) { + error=ERR_FILE_CORRUPT; + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Object of unrecognized type in file: "+type); + } + ERR_FAIL_COND_V(!obj,ERR_FILE_CORRUPT); + + Resource *r = obj->cast_to(); + if (!r) { + error=ERR_FILE_CORRUPT; + memdelete(obj); //bye + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Object type in resource field not a resource, type is: "+obj->get_type()); + ERR_FAIL_COND_V(!r,ERR_FILE_CORRUPT); + } + + res = RES( r ); + if (path!="") + r->set_path(path); + + //load properties + + while(true) { + + String name; + Variant v; + Error err; + err = parse_property(v,name); + if (err==ERR_FILE_EOF) //tag closed + break; + if (err!=OK) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": XML Parsing aborted."); + ERR_FAIL_COND_V(err!=OK,ERR_FILE_CORRUPT); + } + + obj->set(name,v); + } +#ifdef TOOLS_ENABLED + res->set_edited(false); +#endif + resource_cache.push_back(res); //keep it in mem until finished loading + resource_current++; + if (main) { + f->close(); + resource=res; + resource->set_path(res_path); + error=ERR_FILE_EOF; + return error; + + } + error=OK; + return OK; +} + +int ResourceInteractiveLoaderXML::get_stage() const { + + return resource_current; +} +int ResourceInteractiveLoaderXML::get_stage_count() const { + + return resources_total; +} + +ResourceInteractiveLoaderXML::~ResourceInteractiveLoaderXML() { + + memdelete(f); +} + +void ResourceInteractiveLoaderXML::get_dependencies(FileAccess *f,List *p_dependencies) { + + + open(f); + ERR_FAIL_COND(error!=OK); + + while(true) { + bool exit; + Tag *tag = parse_tag(&exit); + + + if (!tag) { + error=ERR_FILE_CORRUPT; + ERR_FAIL_COND(!exit); + error=ERR_FILE_EOF; + return; + } + + if (tag->name!="ext_resource") { + + return; + } + + error=ERR_FILE_CORRUPT; + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": missing 'path' field."); + ERR_FAIL_COND(!tag->args.has("path")); + + String path = tag->args["path"]; + + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": can't use a local path, this is a bug?."); + ERR_FAIL_COND(path.begins_with("local://")); + + if (path.find("://")==-1 && path.is_rel_path()) { + // path is relative to file being loaded, so convert to a resource path + path=Globals::get_singleton()->localize_path(local_path.get_base_dir()+"/"+path); + } + + p_dependencies->push_back(path); + + Error err = close_tag("ext_resource"); + if (err) + return; + + error=OK; + } + +} + + +void ResourceInteractiveLoaderXML::open(FileAccess *p_f) { + + error=OK; + + lines=1; + f=p_f; + + + ResourceInteractiveLoaderXML::Tag *tag = parse_tag(); + if (!tag || tag->name!="?xml" || !tag->args.has("version") || !tag->args.has("encoding") || tag->args["encoding"]!="UTF-8") { + + error=ERR_FILE_CORRUPT; + ResourceLoader::notify_load_error("XML is invalid (missing header tags)"); + ERR_EXPLAIN("Not a XML:UTF-8 File: "+local_path); + ERR_FAIL(); + } + + tag_stack.clear(); + + tag = parse_tag(); + + + if (!tag || tag->name!="resource_file" || !tag->args.has("type") || !tag->args.has("version")) { + + ResourceLoader::notify_load_error(local_path+": XML is not a valid resource file."); + error=ERR_FILE_CORRUPT; + ERR_EXPLAIN("Unrecognized XML File: "+local_path); + ERR_FAIL(); + } + + + if (tag->args.has("subresource_count")) + resources_total=tag->args["subresource_count"].to_int(); + resource_current=0; + resource_type=tag->args["type"]; + + String version = tag->args["version"]; + if (version.get_slice_count(".")!=2) { + + error=ERR_FILE_CORRUPT; + ResourceLoader::notify_load_error(local_path+":XML version string is invalid: "+version); + ERR_EXPLAIN("Invalid Version String '"+version+"'' in file: "+local_path); + ERR_FAIL(); + } + + int major = version.get_slice(".",0).to_int(); + int minor = version.get_slice(".",1).to_int(); + + if (major>VERSION_MAJOR || (major==VERSION_MAJOR && minor>VERSION_MINOR)) { + + error=ERR_FILE_UNRECOGNIZED; + ResourceLoader::notify_load_error(local_path+": File Format '"+version+"' is too new. Please upgrade to a newer engine version."); + ERR_EXPLAIN("File Format '"+version+"' is too new! Please upgrade to a a new engine version: "+local_path); + ERR_FAIL(); + + } + +} + +String ResourceInteractiveLoaderXML::recognize(FileAccess *p_f) { + + error=OK; + + lines=1; + f=p_f; + + ResourceInteractiveLoaderXML::Tag *tag = parse_tag(); + if (!tag || tag->name!="?xml" || !tag->args.has("version") || !tag->args.has("encoding") || tag->args["encoding"]!="UTF-8") { + + + return ""; //unrecognized + } + + tag_stack.clear(); + + tag = parse_tag(); + + if (!tag || tag->name!="resource_file" || !tag->args.has("type") || !tag->args.has("version")) { + + return ""; //unrecognized + } + + return tag->args["type"]; + +} + +///////////////////// + +Ref ResourceFormatLoaderXML::load_interactive(const String &p_path) { + + Error err; + FileAccess *f = FileAccess::open(p_path,FileAccess::READ,&err); + + + if (err!=OK) { + + ERR_FAIL_COND_V(err!=OK,Ref()); + } + + Ref ria = memnew( ResourceInteractiveLoaderXML ); + ria->local_path=Globals::get_singleton()->localize_path(p_path); + ria->res_path=ria->local_path; +// ria->set_local_path( Globals::get_singleton()->localize_path(p_path) ); + ria->open(f); + + return ria; +} + +void ResourceFormatLoaderXML::get_recognized_extensions_for_type(const String& p_type,List *p_extensions) const { + + if (p_type=="") { + get_recognized_extensions(p_extensions); + return; + } + + List extensions; + ObjectTypeDB::get_extensions_for_type(p_type,&extensions); + + extensions.sort(); + + for(List::Element *E=extensions.front();E;E=E->next()) { + String ext = E->get().to_lower(); + if (ext=="res") + continue; + p_extensions->push_back("x"+ext); + p_extensions->push_back(ext); + } + + p_extensions->push_back("xml"); + + +} +void ResourceFormatLoaderXML::get_recognized_extensions(List *p_extensions) const{ + + List extensions; + ObjectTypeDB::get_resource_base_extensions(&extensions); + extensions.sort(); + + for(List::Element *E=extensions.front();E;E=E->next()) { + String ext = E->get().to_lower(); + if (ext=="res") + continue; + p_extensions->push_back("x"+ext); + } + + p_extensions->push_back("xml"); +} + +bool ResourceFormatLoaderXML::handles_type(const String& p_type) const{ + + return true; +} +String ResourceFormatLoaderXML::get_resource_type(const String &p_path) const{ + + + String ext=p_path.extension().to_lower(); + if (!ext.begins_with("x")) //a lie but.. + return ""; + + FileAccess *f = FileAccess::open(p_path,FileAccess::READ); + if (!f) { + + return ""; //could not rwead + } + + Ref ria = memnew( ResourceInteractiveLoaderXML ); + ria->local_path=Globals::get_singleton()->localize_path(p_path); + ria->res_path=ria->local_path; +// ria->set_local_path( Globals::get_singleton()->localize_path(p_path) ); + String r = ria->recognize(f); + return r; +} + + +void ResourceFormatLoaderXML::get_dependencies(const String& p_path,List *p_dependencies) { + + FileAccess *f = FileAccess::open(p_path,FileAccess::READ); + if (!f) { + + ERR_FAIL(); + } + + Ref ria = memnew( ResourceInteractiveLoaderXML ); + ria->local_path=Globals::get_singleton()->localize_path(p_path); + ria->res_path=ria->local_path; +// ria->set_local_path( Globals::get_singleton()->localize_path(p_path) ); + ria->get_dependencies(f,p_dependencies); + + +} + +/****************************************************************************************/ +/****************************************************************************************/ +/****************************************************************************************/ +/****************************************************************************************/ +/****************************************************************************************/ +/****************************************************************************************/ +/****************************************************************************************/ +/****************************************************************************************/ +/****************************************************************************************/ +/****************************************************************************************/ +/****************************************************************************************/ +/****************************************************************************************/ +/****************************************************************************************/ + + + +void ResourceFormatSaverXMLInstance::escape(String& p_str) { + + p_str=p_str.replace("&","&"); + p_str=p_str.replace("<",">"); + p_str=p_str.replace(">","<"); + p_str=p_str.replace("'","'"); + p_str=p_str.replace("\"","""); + for (int i=1;i<32;i++) { + + char chr[2]={i,0}; + p_str=p_str.replace(chr,"&#"+String::num(i)+";"); + } + + +} +void ResourceFormatSaverXMLInstance::write_tabs(int p_diff) { + + for (int i=0;istore_8('\t'); + } +} + +void ResourceFormatSaverXMLInstance::write_string(String p_str,bool p_escape) { + + /* write an UTF8 string */ + if (p_escape) + escape(p_str); + + f->store_string(p_str);; + /* + CharString cs=p_str.utf8(); + const char *data=cs.get_data(); + + while (*data) { + f->store_8(*data); + data++; + }*/ + + +} + +void ResourceFormatSaverXMLInstance::enter_tag(const char* p_tag,const String& p_args) { + + f->store_8('<'); + int cc = 0; + const char *c=p_tag; + while(*c) { + cc++; + c++; + } + f->store_buffer((const uint8_t*)p_tag,cc); + if (p_args.length()) { + f->store_8(' '); + f->store_string(p_args); + } + f->store_8('>'); + depth++; + +} +void ResourceFormatSaverXMLInstance::exit_tag(const char* p_tag) { + + depth--; + f->store_8('<'); + f->store_8('/'); + int cc = 0; + const char *c=p_tag; + while(*c) { + cc++; + c++; + } + f->store_buffer((const uint8_t*)p_tag,cc); + f->store_8('>'); + +} + +/* +static bool _check_type(const Variant& p_property) { + + if (p_property.get_type()==Variant::_RID) + return false; + if (p_property.get_type()==Variant::OBJECT) { + RES res = p_property; + if (res.is_null()) + return false; + } + + return true; +}*/ + +void ResourceFormatSaverXMLInstance::write_property(const String& p_name,const Variant& p_property,bool *r_ok) { + + if (r_ok) + *r_ok=false; + + const char* type; + String params; + bool oneliner=true; + + switch( p_property.get_type() ) { + + case Variant::NIL: type="nil"; break; + case Variant::BOOL: type="bool"; break; + case Variant::INT: type="int"; break; + case Variant::REAL: type="real"; break; + case Variant::STRING: type="string"; break; + case Variant::VECTOR2: type="vector2"; break; + case Variant::RECT2: type="rect2"; break; + case Variant::VECTOR3: type="vector3"; break; + case Variant::PLANE: type="plane"; break; + case Variant::_AABB: type="aabb"; break; + case Variant::QUAT: type="quaternion"; break; + case Variant::MATRIX32: type="matrix32"; break; + case Variant::MATRIX3: type="matrix3"; break; + case Variant::TRANSFORM: type="transform"; break; + case Variant::COLOR: type="color"; break; + case Variant::IMAGE: { + type="image"; + Image img=p_property; + if (img.empty()) { + write_tabs(); + enter_tag(type,"name=\""+p_name+"\""); + exit_tag(type); + if (r_ok) + *r_ok=true; + return; + } + params+="encoding=\"raw\""; + params+=" width=\""+itos(img.get_width())+"\""; + params+=" height=\""+itos(img.get_height())+"\""; + params+=" mipmaps=\""+itos(img.get_mipmaps())+"\""; + + switch(img.get_format()) { + + case Image::FORMAT_GRAYSCALE: params+=" format=\"grayscale\""; break; + case Image::FORMAT_INTENSITY: params+=" format=\"intensity\""; break; + case Image::FORMAT_GRAYSCALE_ALPHA: params+=" format=\"grayscale_alpha\""; break; + case Image::FORMAT_RGB: params+=" format=\"rgb\""; break; + case Image::FORMAT_RGBA: params+=" format=\"rgba\""; break; + case Image::FORMAT_INDEXED : params+=" format=\"indexed\""; break; + case Image::FORMAT_INDEXED_ALPHA: params+=" format=\"indexed_alpha\""; break; + case Image::FORMAT_BC1: params+=" format=\"bc1\""; break; + case Image::FORMAT_BC2: params+=" format=\"bc2\""; break; + case Image::FORMAT_BC3: params+=" format=\"bc3\""; break; + case Image::FORMAT_BC4: params+=" format=\"bc4\""; break; + case Image::FORMAT_BC5: params+=" format=\"bc5\""; break; + case Image::FORMAT_PVRTC2: params+=" format=\"pvrtc2\""; break; + case Image::FORMAT_PVRTC2_ALPHA: params+=" format=\"pvrtc2a\""; break; + case Image::FORMAT_PVRTC4: params+=" format=\"pvrtc4\""; break; + case Image::FORMAT_PVRTC4_ALPHA: params+=" format=\"pvrtc4a\""; break; + case Image::FORMAT_ETC: params+=" format=\"etc\""; break; + case Image::FORMAT_CUSTOM: params+=" format=\"custom\" custom_size=\""+itos(img.get_data().size())+"\""; break; + default: {} + } + } break; + case Variant::NODE_PATH: type="node_path"; break; + case Variant::OBJECT: { + type="resource"; + RES res = p_property; + if (res.is_null()) { + write_tabs(); + enter_tag(type,"name=\""+p_name+"\""); + exit_tag(type); + if (r_ok) + *r_ok=true; + + return; // don't save it + } + + params="resource_type=\""+res->get_save_type()+"\""; + + if (res->get_path().length() && res->get_path().find("::")==-1) { + //external resource + String path=relative_paths?local_path.path_to_file(res->get_path()):res->get_path(); + if (no_extension) + path=path.basename()+".*"; + escape(path); + params+=" path=\""+path+"\""; + } else { + + //internal resource + ERR_EXPLAIN("Resource was not pre cached for the resource section, bug?"); + ERR_FAIL_COND(!resource_map.has(res)); + + params+=" path=\"local://"+itos(resource_map[res])+"\""; + } + + } break; + case Variant::INPUT_EVENT: type="input_event"; break; + case Variant::DICTIONARY: type="dictionary"; params="shared=\""+String(p_property.is_shared()?"true":"false")+"\""; oneliner=false; break; + case Variant::ARRAY: type="array"; params="len=\""+itos(p_property.operator Array().size())+"\" shared=\""+String(p_property.is_shared()?"true":"false")+"\""; oneliner=false; break; + + case Variant::RAW_ARRAY: type="raw_array"; params="len=\""+itos(p_property.operator DVector < uint8_t >().size())+"\""; break; + case Variant::INT_ARRAY: type="int_array"; params="len=\""+itos(p_property.operator DVector < int >().size())+"\""; break; + case Variant::REAL_ARRAY: type="real_array"; params="len=\""+itos(p_property.operator DVector < real_t >().size())+"\""; break; + case Variant::STRING_ARRAY: oneliner=false; type="string_array"; params="len=\""+itos(p_property.operator DVector < String >().size())+"\""; break; + case Variant::VECTOR2_ARRAY: type="vector2_array"; params="len=\""+itos(p_property.operator DVector < Vector2 >().size())+"\""; break; + case Variant::VECTOR3_ARRAY: type="vector3_array"; params="len=\""+itos(p_property.operator DVector < Vector3 >().size())+"\""; break; + case Variant::COLOR_ARRAY: type="color_array"; params="len=\""+itos(p_property.operator DVector < Color >().size())+"\""; break; + default: { + + ERR_PRINT("Unknown Variant type."); + ERR_FAIL(); + } + + } + + write_tabs(); + + if (p_name!="") { + if (params.length()) + enter_tag(type,"name=\""+p_name+"\" "+params); + else + enter_tag(type,"name=\""+p_name+"\""); + } else { + if (params.length()) + enter_tag(type," "+params); + else + enter_tag(type,String()); + } + + if (!oneliner) + f->store_8('\n'); + else + f->store_8(' '); + + + switch( p_property.get_type() ) { + + case Variant::NIL: { + + } break; + case Variant::BOOL: { + + write_string( p_property.operator bool() ? "True":"False" ); + } break; + case Variant::INT: { + + write_string( itos(p_property.operator int()) ); + } break; + case Variant::REAL: { + + write_string( rtos(p_property.operator real_t()) ); + } break; + case Variant::STRING: { + + String str=p_property; + escape(str); + str="\""+str+"\""; + write_string( str,false ); + } break; + case Variant::VECTOR2: { + + Vector2 v = p_property; + write_string( rtoss(v.x) +", "+rtoss(v.y) ); + } break; + case Variant::RECT2: { + + Rect2 aabb = p_property; + write_string( rtoss(aabb.pos.x) +", "+rtoss(aabb.pos.y) +", "+rtoss(aabb.size.x) +", "+rtoss(aabb.size.y) ); + + } break; + case Variant::VECTOR3: { + + Vector3 v = p_property; + write_string( rtoss(v.x) +", "+rtoss(v.y)+", "+rtoss(v.z) ); + } break; + case Variant::PLANE: { + + Plane p = p_property; + write_string( rtoss(p.normal.x) +", "+rtoss(p.normal.y)+", "+rtoss(p.normal.z)+", "+rtoss(p.d) ); + + } break; + case Variant::_AABB: { + + AABB aabb = p_property; + write_string( rtoss(aabb.pos.x) +", "+rtoss(aabb.pos.y) +", "+rtoss(aabb.pos.z) +", "+rtoss(aabb.size.x) +", "+rtoss(aabb.size.y) +", "+rtoss(aabb.size.z) ); + + } break; + case Variant::QUAT: { + + Quat quat = p_property; + write_string( rtoss(quat.x)+", "+rtoss(quat.y)+", "+rtoss(quat.z)+", "+rtoss(quat.w)+", "); + + } break; + case Variant::MATRIX32: { + + String s; + Matrix32 m3 = p_property; + for (int i=0;i<3;i++) { + for (int j=0;j<2;j++) { + + if (i!=0 || j!=0) + s+=", "; + s+=rtoss( m3.elements[i][j] ); + } + } + + write_string(s); + + } break; + case Variant::MATRIX3: { + + String s; + Matrix3 m3 = p_property; + for (int i=0;i<3;i++) { + for (int j=0;j<3;j++) { + + if (i!=0 || j!=0) + s+=", "; + s+=rtoss( m3.elements[i][j] ); + } + } + + write_string(s); + + } break; + case Variant::TRANSFORM: { + + String s; + Transform t = p_property; + Matrix3 &m3 = t.basis; + for (int i=0;i<3;i++) { + for (int j=0;j<3;j++) { + + if (i!=0 || j!=0) + s+=", "; + s+=rtoss( m3.elements[i][j] ); + } + } + + s=s+", "+rtoss(t.origin.x) +", "+rtoss(t.origin.y)+", "+rtoss(t.origin.z); + + write_string(s); + } break; + + // misc types + case Variant::COLOR: { + + Color c = p_property; + write_string( rtoss(c.r) +", "+rtoss(c.g)+", "+rtoss(c.b)+", "+rtoss(c.a) ); + + } break; + case Variant::IMAGE: { + + String s; + Image img = p_property; + DVector data = img.get_data(); + int len = data.size(); + DVector::Read r = data.read(); + const uint8_t *ptr=r.ptr();; + for (int i=0;i>4], hex[byte&0xF], 0}; + s+=str; + } + + write_string(s); + } break; + case Variant::NODE_PATH: { + + String str=p_property; + escape(str); + str="\""+str+"\""; + write_string( str,false); + + } break; + + case Variant::OBJECT: { + /* this saver does not save resources in here + RES res = p_property; + + if (!res.is_null()) { + + String path=res->get_path(); + if (!res->is_shared() || !path.length()) { + // if no path, or path is from inside a scene + write_object( *res ); + } + + } + */ + + } break; + case Variant::INPUT_EVENT: { + + write_string( p_property.operator String() ); + } break; + case Variant::DICTIONARY: { + + Dictionary dict = p_property; + + + List keys; + dict.get_key_list(&keys); + + for(List::Element *E=keys.front();E;E=E->next()) { + + //if (!_check_type(dict[E->get()])) + // continue; + + bool ok; + write_property("",E->get(),&ok); + ERR_CONTINUE(!ok); + + write_property("",dict[E->get()],&ok); + if (!ok) + write_property("",Variant()); //at least make the file consistent.. + } + + + + + } break; + case Variant::ARRAY: { + + Array array = p_property; + int len=array.size(); + for (int i=0;i data = p_property; + int len = data.size(); + DVector::Read r = data.read(); + const uint8_t *ptr=r.ptr();; + for (int i=0;i>4], hex[byte&0xF], 0}; + s+=str; + } + + write_string(s,false); + + } break; + case Variant::INT_ARRAY: { + + DVector data = p_property; + int len = data.size(); + DVector::Read r = data.read(); + const int *ptr=r.ptr();; + write_tabs(); + + for (int i=0;i0) + write_string(", ",false); + + write_string(itos(ptr[i]),false); + } + + + + } break; + case Variant::REAL_ARRAY: { + + DVector data = p_property; + int len = data.size(); + DVector::Read r = data.read(); + const real_t *ptr=r.ptr();; + write_tabs(); + String cm=", " ; + + for (int i=0;i0) + write_string(cm,false); + write_string(rtoss(ptr[i]),false); + } + + + } break; + case Variant::STRING_ARRAY: { + + DVector data = p_property; + int len = data.size(); + DVector::Read r = data.read(); + const String *ptr=r.ptr();; + String s; + //write_string("\n"); + + + + for (int i=0;i \""+str+"\" \n",false); + } + } break; + case Variant::VECTOR2_ARRAY: { + + DVector data = p_property; + int len = data.size(); + DVector::Read r = data.read(); + const Vector2 *ptr=r.ptr();; + write_tabs(); + + for (int i=0;i0) + write_string(", ",false); + write_string(rtoss(ptr[i].x),false); + write_string(", "+rtoss(ptr[i].y),false); + + } + + + } break; + case Variant::VECTOR3_ARRAY: { + + DVector data = p_property; + int len = data.size(); + DVector::Read r = data.read(); + const Vector3 *ptr=r.ptr();; + write_tabs(); + + for (int i=0;i0) + write_string(", ",false); + write_string(rtoss(ptr[i].x),false); + write_string(", "+rtoss(ptr[i].y),false); + write_string(", "+rtoss(ptr[i].z),false); + + } + + + } break; + case Variant::COLOR_ARRAY: { + + DVector data = p_property; + int len = data.size(); + DVector::Read r = data.read(); + const Color *ptr=r.ptr();; + write_tabs(); + + for (int i=0;i0) + write_string(", ",false); + + write_string(rtoss(ptr[i].r),false); + write_string(", "+rtoss(ptr[i].g),false); + write_string(", "+rtoss(ptr[i].b),false); + write_string(", "+rtoss(ptr[i].a),false); + + } + + } break; + default: {} + + } + if (oneliner) + f->store_8(' '); + else + write_tabs(-1); + exit_tag(type); + + f->store_8('\n'); + + if (r_ok) + *r_ok=true; + +} + + +void ResourceFormatSaverXMLInstance::_find_resources(const Variant& p_variant,bool p_main) { + + + switch(p_variant.get_type()) { + case Variant::OBJECT: { + + + RES res = p_variant.operator RefPtr(); + + if (res.is_null()) + return; + + if (!p_main && (!bundle_resources ) && res->get_path().length() && res->get_path().find("::") == -1 ) { + external_resources.insert(res); + return; + } + + if (resource_map.has(res)) + return; + + List property_list; + + res->get_property_list( &property_list ); + + List::Element *I=property_list.front(); + + while(I) { + + PropertyInfo pi=I->get(); + + if (pi.usage&PROPERTY_USAGE_STORAGE || (bundle_resources && pi.usage&PROPERTY_USAGE_BUNDLE)) { + + Variant v=res->get(I->get().name); + _find_resources(v); + } + + I=I->next(); + } + + resource_map[ res ] = resource_map.size(); //saved after, so the childs it needs are available when loaded + saved_resources.push_back(res); + + } break; + case Variant::ARRAY: { + + Array varray=p_variant; + int len=varray.size(); + for(int i=0;i keys; + d.get_key_list(&keys); + for(List::Element *E=keys.front();E;E=E->next()) { + + Variant v = d[E->get()]; + _find_resources(v); + } + } break; + default: {} + } + +} + + + +Error ResourceFormatSaverXMLInstance::save(const String &p_path,const RES& p_resource,uint32_t p_flags) { + + Error err; + f = FileAccess::open(p_path, FileAccess::WRITE,&err); + ERR_FAIL_COND_V( err, ERR_CANT_OPEN ); + FileAccessRef _fref(f); + + local_path = Globals::get_singleton()->localize_path(p_path); + + relative_paths=p_flags&ResourceSaver::FLAG_RELATIVE_PATHS; + skip_editor=p_flags&ResourceSaver::FLAG_OMIT_EDITOR_PROPERTIES; + bundle_resources=p_flags&ResourceSaver::FLAG_BUNDLE_RESOURCES; + no_extension=p_flags&ResourceSaver::FLAG_NO_EXTENSION; + depth=0; + + // save resources + _find_resources(p_resource,true); + + ERR_FAIL_COND_V(err!=OK,err); + + write_string("",false); //no escape + write_string("\n",false); + enter_tag("resource_file","type=\""+p_resource->get_type()+"\" subresource_count=\""+itos(saved_resources.size()+external_resources.size())+"\" version=\""+itos(VERSION_MAJOR)+"."+itos(VERSION_MINOR)+"\" version_name=\""+VERSION_FULL_NAME+"\""); + write_string("\n",false); + + for(Set::Element *E=external_resources.front();E;E=E->next()) { + + write_tabs(); + String p = E->get()->get_path(); + if (no_extension) + p=p.basename()+".*"; + + enter_tag("ext_resource","path=\""+p+"\" type=\""+E->get()->get_save_type()+"\""); //bundled + exit_tag("ext_resource"); //bundled + write_string("\n",false); + } + + + + for(List::Element *E=saved_resources.front();E;E=E->next()) { + + RES res = E->get(); + ERR_CONTINUE(!resource_map.has(res)); + bool main = (E->next()==NULL); + + write_tabs(); + + if (main) + enter_tag("main_resource",""); //bundled + else if (res->get_path().length() && res->get_path().find("::") == -1 ) + enter_tag("resource","type=\""+res->get_type()+"\" path=\""+res->get_path()+"\""); //bundled + else + enter_tag("resource","type=\""+res->get_type()+"\" path=\"local://"+itos(resource_map[res])+"\""); + write_string("\n",false); + + + List property_list; + res->get_property_list(&property_list); + for(List::Element *PE = property_list.front();PE;PE=PE->next()) { + + + if (skip_editor && PE->get().name.begins_with("__editor")) + continue; + + if (PE->get().usage&PROPERTY_USAGE_STORAGE || (bundle_resources && PE->get().usage&PROPERTY_USAGE_BUNDLE)) { + + String name = PE->get().name; + Variant value = res->get(name); + if (PE->get().usage&PROPERTY_USAGE_STORE_IF_NONZERO && value.is_zero()) + continue; + + write_property(name,value); + } + + + } + + write_string("\n",false); + write_tabs(-1); + if (main) + exit_tag("main_resource"); + else + exit_tag("resource"); + + write_string("\n",false); + } + + exit_tag("resource_file"); + f->close(); + //memdelete(f); + + return OK; +} + + + +Error ResourceFormatSaverXML::save(const String &p_path,const RES& p_resource,uint32_t p_flags) { + + ResourceFormatSaverXMLInstance saver; + return saver.save(p_path,p_resource,p_flags); + +} + +bool ResourceFormatSaverXML::recognize(const RES& p_resource) const { + + + return true; // all recognized! +} +void ResourceFormatSaverXML::get_recognized_extensions(const RES& p_resource,List *p_extensions) const { + + + //here comes the sun, lalalala + String base = p_resource->get_base_extension().to_lower(); + p_extensions->push_back("xml"); + if (base!="res") { + + p_extensions->push_back("x"+base); + } + +} diff --git a/core/io/resource_format_xml.h b/core/io/resource_format_xml.h new file mode 100644 index 00000000000..05313ffbd7c --- /dev/null +++ b/core/io/resource_format_xml.h @@ -0,0 +1,155 @@ +/*************************************************************************/ +/* resource_format_xml.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 RESOURCE_FORMAT_XML_H +#define RESOURCE_FORMAT_XML_H + +#include "io/resource_loader.h" +#include "io/resource_saver.h" +#include "os/file_access.h" + + + +class ResourceInteractiveLoaderXML : public ResourceInteractiveLoader { + + String local_path; + String res_path; + + FileAccess *f; + + struct Tag { + + String name; + HashMap args; + }; + + _FORCE_INLINE_ Error _parse_array_element(Vector &buff,bool p_number_only,FileAccess *f,bool *end); + + int resources_total; + int resource_current; + String resource_type; + + mutable int lines; + uint8_t get_char() const; + int get_current_line() const; + +friend class ResourceFormatLoaderXML; + List tag_stack; + + List resource_cache; + Tag* parse_tag(bool* r_exit=NULL,bool p_printerr=true); + Error close_tag(const String& p_name); + void unquote(String& p_str); + Error goto_end_of_tag(); + Error parse_property_data(String &r_data); + Error parse_property(Variant& r_v, String &r_name); + + Error error; + + RES resource; + +public: + + virtual void set_local_path(const String& p_local_path); + virtual Ref get_resource(); + virtual Error poll(); + virtual int get_stage() const; + virtual int get_stage_count() const; + + void open(FileAccess *p_f); + String recognize(FileAccess *p_f); + void get_dependencies(FileAccess *p_f,List *p_dependencies); + + + ~ResourceInteractiveLoaderXML(); + +}; + +class ResourceFormatLoaderXML : public ResourceFormatLoader { +public: + + virtual Ref load_interactive(const String &p_path); + virtual void get_recognized_extensions_for_type(const String& p_type,List *p_extensions) const; + virtual void get_recognized_extensions(List *p_extensions) const; + virtual bool handles_type(const String& p_type) const; + virtual String get_resource_type(const String &p_path) const; + virtual void get_dependencies(const String& p_path,List *p_dependencies); + + +}; + + +//////////////////////////////////////////////////////////////////////////////////////////// + + +class ResourceFormatSaverXMLInstance { + + String local_path; + + + + bool no_extension; + bool relative_paths; + bool bundle_resources; + bool skip_editor; + FileAccess *f; + int depth; + Map resource_map; + List saved_resources; + Set external_resources; + + void enter_tag(const char* p_tag,const String& p_args=String()); + void exit_tag(const char* p_tag); + + void _find_resources(const Variant& p_variant,bool p_main=false); + void write_property(const String& p_name,const Variant& p_property,bool *r_ok=NULL); + + + void escape(String& p_str); + void write_tabs(int p_diff=0); + void write_string(String p_str,bool p_escape=true); + + +public: + + Error save(const String &p_path,const RES& p_resource,uint32_t p_flags=0); + + +}; + +class ResourceFormatSaverXML : public ResourceFormatSaver { +public: + virtual Error save(const String &p_path,const RES& p_resource,uint32_t p_flags=0); + virtual bool recognize(const RES& p_resource) const; + virtual void get_recognized_extensions(const RES& p_resource,List *p_extensions) const; + + +}; + + +#endif // RESOURCE_FORMAT_XML_H diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp new file mode 100644 index 00000000000..3bae6be83c9 --- /dev/null +++ b/core/io/resource_loader.cpp @@ -0,0 +1,378 @@ +/*************************************************************************/ +/* resource_loader.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 "resource_loader.h" +#include "print_string.h" +#include "globals.h" +#include "path_remap.h" +#include "os/file_access.h" +#include "os/os.h" +ResourceFormatLoader *ResourceLoader::loader[MAX_LOADERS]; + +int ResourceLoader::loader_count=0; + + +Error ResourceInteractiveLoader::wait() { + + Error err = poll(); + while (err==OK) { + err=poll(); + } + + return err; +} + + +bool ResourceFormatLoader::recognize(const String& p_extension) const { + + + List extensions; + get_recognized_extensions(&extensions); + for (List::Element *E=extensions.front();E;E=E->next()) { + + if (E->get().nocasecmp_to(p_extension.extension())==0) + return true; + } + + return false; +} + +void ResourceFormatLoader::get_recognized_extensions_for_type(const String& p_type,List *p_extensions) const { + + if (p_type=="" || handles_type(p_type)) + get_recognized_extensions(p_extensions); +} + +void ResourceLoader::get_recognized_extensions_for_type(const String& p_type,List *p_extensions) { + + for (int i=0;iget_recognized_extensions_for_type(p_type,p_extensions); + } + +} + +void ResourceInteractiveLoader::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("get_resource"),&ResourceInteractiveLoader::get_resource); + ObjectTypeDB::bind_method(_MD("poll"),&ResourceInteractiveLoader::poll); + ObjectTypeDB::bind_method(_MD("wait"),&ResourceInteractiveLoader::wait); + ObjectTypeDB::bind_method(_MD("get_stage"),&ResourceInteractiveLoader::get_stage); + ObjectTypeDB::bind_method(_MD("get_stage_count"),&ResourceInteractiveLoader::get_stage_count); +} + +class ResourceInteractiveLoaderDefault : public ResourceInteractiveLoader { + + OBJ_TYPE( ResourceInteractiveLoaderDefault, ResourceInteractiveLoader ); +public: + + Ref resource; + + virtual void set_local_path(const String& p_local_path) { /*scene->set_filename(p_local_path);*/ } + virtual Ref get_resource() { return resource; } + virtual Error poll() { return ERR_FILE_EOF; } + virtual int get_stage() const { return 1; } + virtual int get_stage_count() const { return 1; } + + ResourceInteractiveLoaderDefault() {} +}; + + + +Ref ResourceFormatLoader::load_interactive(const String &p_path) { + + //either this + Ref res = load(p_path); + if (res.is_null()) + return Ref(); + + Ref ril = Ref( memnew( ResourceInteractiveLoaderDefault )); + ril->resource=res; + return ril; +} + +RES ResourceFormatLoader::load(const String &p_path,const String& p_original_path) { + + + //or this must be implemented + Ref ril = load_interactive(p_path); + if (!ril.is_valid()) + return RES(); + ril->set_local_path(p_original_path); + + while(true) { + + Error err = ril->poll(); + + if (err==ERR_FILE_EOF) { + return ril->get_resource(); + } + + ERR_FAIL_COND_V(err!=OK,RES()); + } + + return RES(); + +} + +void ResourceFormatLoader::get_dependencies(const String& p_path,List *p_dependencies) { + + //do nothing by default +} + + +/////////////////////////////////// + + +RES ResourceLoader::load(const String &p_path,const String& p_type_hint,bool p_no_cache) { + + String local_path = Globals::get_singleton()->localize_path(p_path); + + local_path=find_complete_path(p_path,p_type_hint); + ERR_FAIL_COND_V(local_path=="",RES()); + + if (!p_no_cache && ResourceCache::has(local_path)) { + + if (OS::get_singleton()->is_stdout_verbose()) + print_line("load resource: "+local_path+" (cached)"); + + return RES( ResourceCache::get(local_path ) ); + } + + String remapped_path = PathRemap::get_singleton()->get_remap(local_path); + + if (OS::get_singleton()->is_stdout_verbose()) + print_line("load resource: "); + + String extension=remapped_path.extension(); + bool found=false; + + for (int i=0;irecognize(extension)) + continue; + if (p_type_hint!="" && !loader[i]->handles_type(p_type_hint)) + continue; + found=true; + RES res = loader[i]->load(remapped_path,local_path); + if (res.is_null()) + continue; + if (!p_no_cache) + res->set_path(local_path); +#ifdef TOOLS_ENABLED + + res->set_edited(false); + if (timestamp_on_load) { + uint64_t mt = FileAccess::get_modified_time(remapped_path); + //printf("mt %s: %lli\n",remapped_path.utf8().get_data(),mt); + res->set_last_modified_time(mt); + } +#endif + return res; + } + + if (found) { + ERR_EXPLAIN("Failed loading resource: "+p_path); + } else { + ERR_EXPLAIN("No loader found for resource: "+p_path); + } + ERR_FAIL_V(RES()); + return RES(); +} + + +Ref ResourceLoader::load_import_metadata(const String &p_path) { + + + String local_path = Globals::get_singleton()->localize_path(p_path); + + String extension=p_path.extension(); + bool found=false; + Ref ret; + + for (int i=0;irecognize(extension)) + continue; + found=true; + + Error err = loader[i]->load_import_metadata(local_path,ret); + if (err==OK) + break; + } + + return ret; + +} + + + +String ResourceLoader::find_complete_path(const String& p_path,const String& p_type) { + + String local_path = p_path; + if (local_path.ends_with("*")) { + + //find the extension for resource that ends with * + local_path = local_path.substr(0,local_path.length()-1); + List extensions; + get_recognized_extensions_for_type(p_type,&extensions); + List candidates; + + for(List::Element *E=extensions.front();E;E=E->next()) { + + String path = local_path+E->get(); + + if (FileAccess::exists(path)) { + candidates.push_back(path); + } + + } + + + if (candidates.size()==0) { + return ""; + } else if (candidates.size()==1 || p_type=="") { + return candidates.front()->get(); + } else { + + for(List::Element *E=candidates.front();E;E=E->next()) { + + String rt = get_resource_type(E->get()); + if (ObjectTypeDB::is_type(rt,p_type)) { + return E->get(); + } + } + + return ""; + } + } + + return local_path; +} + +Ref ResourceLoader::load_interactive(const String &p_path,const String& p_type_hint,bool p_no_cache) { + + + + String local_path = Globals::get_singleton()->localize_path(p_path); + + local_path=find_complete_path(p_path,p_type_hint); + ERR_FAIL_COND_V(local_path=="",Ref()); + + + + if (!p_no_cache && ResourceCache::has(local_path)) { + + if (OS::get_singleton()->is_stdout_verbose()) + print_line("load resource: "+local_path+" (cached)"); + + return RES( ResourceCache::get(local_path ) ); + } + + if (OS::get_singleton()->is_stdout_verbose()) + print_line("load resource: "); + + String remapped_path = PathRemap::get_singleton()->get_remap(local_path); + + String extension=remapped_path.extension(); + bool found=false; + + for (int i=0;irecognize(extension)) + continue; + if (p_type_hint!="" && !loader[i]->handles_type(p_type_hint)) + continue; + found=true; + Ref ril = loader[i]->load_interactive(remapped_path); + if (ril.is_null()) + continue; + if (!p_no_cache) + ril->set_local_path(local_path); + + return ril; + } + + if (found) { + ERR_EXPLAIN("Failed loading resource: "+p_path); + } else { + ERR_EXPLAIN("No loader found for resource: "+p_path); + } + ERR_FAIL_V(Ref()); + return Ref(); + +} + +void ResourceLoader::add_resource_format_loader(ResourceFormatLoader *p_format_loader) { + + ERR_FAIL_COND( loader_count >= MAX_LOADERS ); + loader[loader_count++]=p_format_loader; +} + +void ResourceLoader::get_dependencies(const String& p_path,List *p_dependencies) { + + String local_path = Globals::get_singleton()->localize_path(p_path); + String remapped_path = PathRemap::get_singleton()->get_remap(local_path); + + String extension=remapped_path.extension(); + + for (int i=0;irecognize(extension)) + continue; + //if (p_type_hint!="" && !loader[i]->handles_type(p_type_hint)) + // continue; + + loader[i]->get_dependencies(remapped_path,p_dependencies); + + } +} + + +String ResourceLoader::get_resource_type(const String &p_path) { + + String local_path = Globals::get_singleton()->localize_path(p_path); + String remapped_path = PathRemap::get_singleton()->get_remap(local_path); + String extension=remapped_path.extension(); + + bool found=false; + for (int i=0;iget_resource_type(local_path); + if (result!="") + return result; + } + + return ""; + +} +ResourceLoadErrorNotify ResourceLoader::err_notify=NULL; +void *ResourceLoader::err_notify_ud=NULL; + +bool ResourceLoader::abort_on_missing_resource=true; +bool ResourceLoader::timestamp_on_load=false; + diff --git a/core/io/resource_loader.h b/core/io/resource_loader.h new file mode 100644 index 00000000000..70b1a795820 --- /dev/null +++ b/core/io/resource_loader.h @@ -0,0 +1,114 @@ +/*************************************************************************/ +/* resource_loader.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 RESOURCE_LOADER_H +#define RESOURCE_LOADER_H + +#include "resource.h" + +/** + @author Juan Linietsky +*/ + +class ResourceInteractiveLoader : public Reference { + + OBJ_TYPE(ResourceInteractiveLoader,Reference); +protected: + + static void _bind_methods(); +public: + + virtual void set_local_path(const String& p_local_path)=0; + virtual Ref get_resource()=0; + virtual Error poll()=0; + virtual int get_stage() const=0; + virtual int get_stage_count() const=0; + virtual Error wait(); + + ResourceInteractiveLoader() {} +}; + + +class ResourceFormatLoader { +public: + + virtual Ref load_interactive(const String &p_path); + virtual RES load(const String &p_path,const String& p_original_path=""); + virtual void get_recognized_extensions(List *p_extensions) const=0; + virtual void get_recognized_extensions_for_type(const String& p_type,List *p_extensions) const; + bool recognize(const String& p_extension) const; + virtual bool handles_type(const String& p_type) const=0; + virtual String get_resource_type(const String &p_path) const=0; + virtual void get_dependencies(const String& p_path,List *p_dependencies); + virtual Error load_import_metadata(const String &p_path, Ref& r_var) const { return ERR_UNAVAILABLE; } + + virtual ~ResourceFormatLoader() {} +}; + + +typedef void (*ResourceLoadErrorNotify)(void *p_ud,const String& p_text); + + +class ResourceLoader { + + enum { + MAX_LOADERS=64 + }; + + static ResourceFormatLoader *loader[MAX_LOADERS]; + static int loader_count; + static bool timestamp_on_load; + + static void* err_notify_ud; + static ResourceLoadErrorNotify err_notify; + static bool abort_on_missing_resource; + + static String find_complete_path(const String& p_path,const String& p_type); +public: + + + + static Ref load_interactive(const String &p_path,const String& p_type_hint="",bool p_no_cache=false); + static RES load(const String &p_path,const String& p_type_hint="",bool p_no_cache=false); + static Ref load_import_metadata(const String &p_path); + + static void get_recognized_extensions_for_type(const String& p_type,List *p_extensions); + static void add_resource_format_loader(ResourceFormatLoader *p_format_loader); + static String get_resource_type(const String &p_path); + static void get_dependencies(const String& p_path,List *p_dependencies); + + + static void set_timestamp_on_load(bool p_timestamp) { timestamp_on_load=p_timestamp; } + + static void notify_load_error(const String& p_err) { if (err_notify) err_notify(err_notify_ud,p_err); } + static void set_error_notify_func(void* p_ud,ResourceLoadErrorNotify p_err_notify) { err_notify=p_err_notify; err_notify_ud=p_ud;} + static void set_abort_on_missing_resources(bool p_abort) { abort_on_missing_resource=p_abort; } + static bool get_abort_on_missing_resources() { return abort_on_missing_resource; } +}; + +#endif diff --git a/core/io/resource_saver.cpp b/core/io/resource_saver.cpp new file mode 100644 index 00000000000..598f517d765 --- /dev/null +++ b/core/io/resource_saver.cpp @@ -0,0 +1,127 @@ +/*************************************************************************/ +/* resource_saver.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 "resource_saver.h" +#include "globals.h" +#include "os/file_access.h" +#include "script_language.h" +#include "resource_loader.h" + +ResourceFormatSaver *ResourceSaver::saver[MAX_SAVERS]; + +int ResourceSaver::saver_count=0; +bool ResourceSaver::timestamp_on_save=false; +ResourceSavedCallback ResourceSaver::save_callback=0; + +Error ResourceSaver::save(const String &p_path,const RES& p_resource,uint32_t p_flags) { + + String extension=p_path.extension(); + Error err=ERR_FILE_UNRECOGNIZED; + + for (int i=0;irecognize(p_resource)) + continue; + + List extensions; + bool recognized=false; + saver[i]->get_recognized_extensions(p_resource,&extensions); + + for (List::Element *E=extensions.front();E;E=E->next()) { + + if (E->get().nocasecmp_to(extension.extension())==0) + recognized=true; + } + + if (!recognized) + continue; + + String old_path=p_resource->get_path(); + + + String local_path=Globals::get_singleton()->localize_path(p_path); + + RES rwcopy = p_resource; + if (p_flags&FLAG_CHANGE_PATH) + rwcopy->set_path(local_path); + + err = saver[i]->save(p_path,p_resource,p_flags); + + if (err == OK ) { + +#ifdef TOOLS_ENABLED + + ((Resource*)p_resource.ptr())->set_edited(false); + if (timestamp_on_save) { + uint64_t mt = FileAccess::get_modified_time(p_path); + + ((Resource*)p_resource.ptr())->set_last_modified_time(mt); + } +#endif + + if (p_flags&FLAG_CHANGE_PATH) + rwcopy->set_path(old_path); + + if (save_callback && p_path.begins_with("res://")) + save_callback(p_path); + + return OK; + } else { + + } + } + + return err; +} + + +void ResourceSaver::set_save_callback(ResourceSavedCallback p_callback) { + + save_callback=p_callback; +} + + +void ResourceSaver::get_recognized_extensions(const RES& p_resource,List *p_extensions) { + + + for (int i=0;iget_recognized_extensions(p_resource,p_extensions); + } + +} + +void ResourceSaver::add_resource_format_saver(ResourceFormatSaver *p_format_saver) { + + ERR_FAIL_COND( saver_count >= MAX_SAVERS ); + saver[saver_count++]=p_format_saver; +} + + + + diff --git a/core/io/resource_saver.h b/core/io/resource_saver.h new file mode 100644 index 00000000000..4b794247e0c --- /dev/null +++ b/core/io/resource_saver.h @@ -0,0 +1,95 @@ +/*************************************************************************/ +/* resource_saver.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 RESOURCE_SAVER_H +#define RESOURCE_SAVER_H + +#include "resource.h" + +/** + @author Juan Linietsky +*/ + + + + + + +class ResourceFormatSaver { +public: + + virtual Error save(const String &p_path,const RES& p_resource,uint32_t p_flags=0)=0; + virtual bool recognize(const RES& p_resource) const=0; + virtual void get_recognized_extensions(const RES& p_resource,List *p_extensions) const=0; + + virtual ~ResourceFormatSaver() {} +}; + +typedef void (*ResourceSavedCallback)(const String& p_path); + +class ResourceSaver { + + enum { + MAX_SAVERS=64 + }; + + static ResourceFormatSaver *saver[MAX_SAVERS]; + static int saver_count; + static bool timestamp_on_save; + static ResourceSavedCallback save_callback; + + +public: + + enum SaverFlags { + + FLAG_RELATIVE_PATHS=1, + FLAG_BUNDLE_RESOURCES=2, + FLAG_CHANGE_PATH=4, + FLAG_OMIT_EDITOR_PROPERTIES=8, + FLAG_SAVE_BIG_ENDIAN=16, + FLAG_COMPRESS=32, + FLAG_NO_EXTENSION=64, + + + }; + + + static Error save(const String &p_path,const RES& p_resource,uint32_t p_flags=0); + static void get_recognized_extensions(const RES& p_resource,List *p_extensions); + static void add_resource_format_saver(ResourceFormatSaver *p_format_saver); + + static void set_timestamp_on_save(bool p_timestamp) { timestamp_on_save=p_timestamp; } + static void set_save_callback(ResourceSavedCallback p_callback); + + + +}; + + +#endif diff --git a/core/io/stream_peer.cpp b/core/io/stream_peer.cpp new file mode 100644 index 00000000000..0eae660373c --- /dev/null +++ b/core/io/stream_peer.cpp @@ -0,0 +1,126 @@ +/*************************************************************************/ +/* stream_peer.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 "stream_peer.h" + + +Error StreamPeer::_put_data(const DVector& p_data) { + + int len = p_data.size(); + if (len==0) + return OK; + DVector::Read r = p_data.read(); + return put_data(&r[0],len); +} + +Array StreamPeer::_put_partial_data(const DVector& p_data) { + + Array ret; + + int len = p_data.size(); + if (len==0) { + ret.push_back(OK); + ret.push_back(0); + return ret; + } + + DVector::Read r = p_data.read(); + int sent; + Error err = put_partial_data(&r[0],len,sent); + + if (err!=OK) { + sent=0; + } + ret.push_back(err); + ret.push_back(sent); + return ret; +} + + +Array StreamPeer::_get_data(int p_bytes) { + + Array ret; + + DVector data; + data.resize(p_bytes); + if (data.size()!=p_bytes) { + + ret.push_back(ERR_OUT_OF_MEMORY); + ret.push_back(DVector()); + return ret; + } + + DVector::Write w = data.write(); + Error err = get_data(&w[0],p_bytes); + w = DVector::Write(); + ret.push_back(err); + ret.push_back(data); + return ret; + +} + +Array StreamPeer::_get_partial_data(int p_bytes) { + + Array ret; + + DVector data; + data.resize(p_bytes); + if (data.size()!=p_bytes) { + + ret.push_back(ERR_OUT_OF_MEMORY); + ret.push_back(DVector()); + return ret; + } + + DVector::Write w = data.write(); + int received; + Error err = get_partial_data(&w[0],p_bytes,received); + w = DVector::Write(); + + if (err!=OK) { + data.resize(0); + } else if (received!=data.size()) { + + data.resize(received); + } + + ret.push_back(err); + ret.push_back(data); + return ret; + +} + + +void StreamPeer::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("put_data","data"),&StreamPeer::_put_data); + ObjectTypeDB::bind_method(_MD("put_partial_data","data"),&StreamPeer::_put_partial_data); + + ObjectTypeDB::bind_method(_MD("get_data","bytes"),&StreamPeer::_get_data); + ObjectTypeDB::bind_method(_MD("get_partial_data","bytes"),&StreamPeer::_get_partial_data); +} diff --git a/core/io/stream_peer.h b/core/io/stream_peer.h new file mode 100644 index 00000000000..84552cfd3ee --- /dev/null +++ b/core/io/stream_peer.h @@ -0,0 +1,58 @@ +/*************************************************************************/ +/* stream_peer.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 STREAM_PEER_H +#define STREAM_PEER_H + +#include "reference.h" + +class StreamPeer : public Reference { + OBJ_TYPE( StreamPeer, Reference ); + OBJ_CATEGORY("Networking"); +protected: + static void _bind_methods(); + + //bind helpers + Error _put_data(const DVector& p_data); + Array _put_partial_data(const DVector& p_data); + + Array _get_data(int p_bytes); + Array _get_partial_data(int p_bytes); + +public: + + virtual Error put_data(const uint8_t* p_data,int p_bytes)=0; ///< put a whole chunk of data, blocking until it sent + virtual Error put_partial_data(const uint8_t* p_data,int p_bytes, int &r_sent)=0; ///< put as much data as possible, without blocking. + + virtual Error get_data(uint8_t* p_buffer, int p_bytes)=0; ///< read p_bytes of data, if p_bytes > available, it will block + virtual Error get_partial_data(uint8_t* p_buffer, int p_bytes,int &r_received)=0; ///< read as much data as p_bytes into buffer, if less was read, return in r_received + + StreamPeer() {} +}; + +#endif // STREAM_PEER_H diff --git a/core/io/stream_peer_tcp.cpp b/core/io/stream_peer_tcp.cpp new file mode 100644 index 00000000000..0fdaab885a8 --- /dev/null +++ b/core/io/stream_peer_tcp.cpp @@ -0,0 +1,56 @@ +/*************************************************************************/ +/* stream_peer_tcp.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 "stream_peer_tcp.h" + +StreamPeerTCP* (*StreamPeerTCP::_create)()=NULL; + +void StreamPeerTCP::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("connect","host","ip"),&StreamPeerTCP::connect); + ObjectTypeDB::bind_method(_MD("is_connected"),&StreamPeerTCP::is_connected); + ObjectTypeDB::bind_method(_MD("get_connected_host"),&StreamPeerTCP::get_connected_host); + ObjectTypeDB::bind_method(_MD("get_connected_port"),&StreamPeerTCP::get_connected_port); + ObjectTypeDB::bind_method(_MD("disconnect"),&StreamPeerTCP::disconnect); +} + +Ref StreamPeerTCP::create() { + + if (!_create) + return Ref(); + return Ref(_create()); +} + +StreamPeerTCP::StreamPeerTCP() { + +} + +StreamPeerTCP::~StreamPeerTCP() { + +}; + diff --git a/core/io/stream_peer_tcp.h b/core/io/stream_peer_tcp.h new file mode 100644 index 00000000000..428ccd3d32b --- /dev/null +++ b/core/io/stream_peer_tcp.h @@ -0,0 +1,75 @@ +/*************************************************************************/ +/* stream_peer_tcp.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 STREAM_PEER_TCP_H +#define STREAM_PEER_TCP_H + +#include "stream_peer.h" + +#include "ip_address.h" + +class StreamPeerTCP : public StreamPeer { + + OBJ_TYPE( StreamPeerTCP, StreamPeer ); + OBJ_CATEGORY("Networking"); + +public: + + enum Status { + + STATUS_NONE, + STATUS_CONNECTING, + STATUS_CONNECTED, + STATUS_ERROR, + }; + +protected: + + static StreamPeerTCP* (*_create)(); + static void _bind_methods(); + +public: + + virtual Error connect(const IP_Address& p_host, uint16_t p_port)=0; + + //read/write from streampeer + + virtual bool is_connected() const=0; + virtual Status get_status() const=0; + virtual void disconnect()=0; + virtual IP_Address get_connected_host() const=0; + virtual uint16_t get_connected_port() const=0; + virtual void set_nodelay(bool p_enabled)=0; + + static Ref create(); + + StreamPeerTCP(); + ~StreamPeerTCP(); +}; + +#endif diff --git a/core/io/tcp_server.cpp b/core/io/tcp_server.cpp new file mode 100644 index 00000000000..06419b9c6b2 --- /dev/null +++ b/core/io/tcp_server.cpp @@ -0,0 +1,62 @@ +/*************************************************************************/ +/* tcp_server.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 "tcp_server.h" + +TCP_Server* (*TCP_Server::_create)()=NULL; + +Ref TCP_Server::create() { + + if (!_create) + return NULL; + return Ref(_create()); +} + +Error TCP_Server::_listen(uint16_t p_port,DVector p_accepted_hosts) { + + List hosts; + for(int i=0;i())); + ObjectTypeDB::bind_method(_MD("is_connection_available"),&TCP_Server::is_connection_available); + ObjectTypeDB::bind_method(_MD("take_connection"),&TCP_Server::take_connection); + ObjectTypeDB::bind_method(_MD("stop"),&TCP_Server::stop); + +} + + +TCP_Server::TCP_Server() +{ +} diff --git a/core/io/tcp_server.h b/core/io/tcp_server.h new file mode 100644 index 00000000000..31949be3b6b --- /dev/null +++ b/core/io/tcp_server.h @@ -0,0 +1,59 @@ +/*************************************************************************/ +/* tcp_server.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 TCP_SERVER_H +#define TCP_SERVER_H + +#include "io/stream_peer.h" +#include "io/ip.h" +#include "stream_peer_tcp.h" + +class TCP_Server : public Reference { + + OBJ_TYPE( TCP_Server, Reference ); +protected: + + static TCP_Server* (*_create)(); + + //bind helper + Error _listen(uint16_t p_port,DVector p_accepted_hosts=DVector()); + static void _bind_methods(); +public: + + virtual Error listen(uint16_t p_port,const List *p_accepted_hosts=NULL)=0; + virtual bool is_connection_available() const=0; + virtual Ref take_connection()=0; + + virtual void stop()=0; //stop listening + + static Ref create(); + + TCP_Server(); +}; + +#endif // TCP_SERVER_H diff --git a/core/io/translation_loader_po.cpp b/core/io/translation_loader_po.cpp new file mode 100644 index 00000000000..0d42cebb419 --- /dev/null +++ b/core/io/translation_loader_po.cpp @@ -0,0 +1,201 @@ +/*************************************************************************/ +/* translation_loader_po.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 "translation_loader_po.h" +#include "os/file_access.h" +#include "translation.h" + +RES TranslationLoaderPO::load(const String &p_path,const String& p_original_path) { + + FileAccess *f=FileAccess::open(p_path,FileAccess::READ); + ERR_FAIL_COND_V(!f,RES()); + + String l = f->get_line(); + + enum Status { + + STATUS_NONE, + STATUS_READING_ID, + STATUS_READING_STRING, + }; + + Status status=STATUS_NONE; + + String msg_id; + String msg_str; + String config; + + Ref translation = Ref( memnew( Translation )); + int line = 1; + + while(true) { + + String l = f->get_line(); + + if (f->eof_reached()) { + + if ( status == STATUS_READING_STRING) { + + if (msg_id!="") + translation->add_message(msg_id,msg_str); + else if (config=="") + config=msg_str; + break; + + } else if ( status==STATUS_NONE) + break; + + memdelete(f); + ERR_EXPLAIN(p_path+":"+itos(line)+" Unexpected EOF while reading 'msgid' at file: "); + ERR_FAIL_V(RES()); + } + + l=l.strip_edges(); + + if (l.begins_with("msgid")) { + + if (status==STATUS_READING_ID) { + + memdelete(f); + ERR_EXPLAIN(p_path+":"+itos(line)+" nexpected 'msgid', was expecting 'msgstr' while parsing: "); + ERR_FAIL_V(RES()); + } + + if (msg_id!="") + translation->add_message(msg_id,msg_str); + else if (config=="") + config=msg_str; + + l=l.substr(5,l.length()).strip_edges(); + status=STATUS_READING_ID; + msg_id=""; + msg_str=""; + } + + if (l.begins_with("msgstr")) { + + if (status!=STATUS_READING_ID) { + + memdelete(f); + ERR_EXPLAIN(p_path+":"+itos(line)+" Unexpected 'msgstr', was expecting 'msgid' while parsing: "); + ERR_FAIL_V(RES()); + } + + l=l.substr(6,l.length()).strip_edges(); + status=STATUS_READING_STRING; + } + + if (l=="" || l.begins_with("#")) { + line++; + continue; //nothing to read or comment + } + + if (!l.begins_with("\"") || status==STATUS_NONE) { + //not a string? failure! + ERR_EXPLAIN(p_path+":"+itos(line)+" Invalid line '"+l+"' while parsing: "); + ERR_FAIL_V(RES()); + + } + + l=l.substr(1,l.length()); + //find final quote + int end_pos=-1; + for(int i=0;iclose(); + memdelete(f); + + if (config=="") { + ERR_EXPLAIN("No config found in file: "+p_path); + ERR_FAIL_V(RES()); + } + + Vector configs = config.split("\n"); + for(int i=0;iset_locale(value); + } + } + + + return translation; + +} + +void TranslationLoaderPO::get_recognized_extensions(List *p_extensions) const{ + + p_extensions->push_back("po"); + //p_extensions->push_back("mo"); //mo in the future... +} +bool TranslationLoaderPO::handles_type(const String& p_type) const{ + + return (p_type=="Translation"); +} + +String TranslationLoaderPO::get_resource_type(const String &p_path) const { + + if (p_path.extension().to_lower()=="po") + return "Translation"; + return ""; +} + +TranslationLoaderPO::TranslationLoaderPO() +{ +} diff --git a/core/io/translation_loader_po.h b/core/io/translation_loader_po.h new file mode 100644 index 00000000000..7a0c4e10dca --- /dev/null +++ b/core/io/translation_loader_po.h @@ -0,0 +1,46 @@ +/*************************************************************************/ +/* translation_loader_po.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 TRANSLATION_LOADER_PO_H +#define TRANSLATION_LOADER_PO_H + +#include "io/resource_loader.h" + +class TranslationLoaderPO : public ResourceFormatLoader { +public: + + virtual RES load(const String &p_path,const String& p_original_path=""); + virtual void get_recognized_extensions(List *p_extensions) const; + virtual bool handles_type(const String& p_type) const; + virtual String get_resource_type(const String &p_path) const; + + + TranslationLoaderPO(); +}; + +#endif // TRANSLATION_LOADER_PO_H diff --git a/core/io/unzip.c b/core/io/unzip.c new file mode 100644 index 00000000000..ac72457f38b --- /dev/null +++ b/core/io/unzip.c @@ -0,0 +1,2216 @@ +/* unzip.c -- IO for uncompress .zip files using zlib + Version 1.1, February 14h, 2010 + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications of Unzip for Zip64 + Copyright (C) 2007-2008 Even Rouault + + Modifications for Zip64 support on both zip and unzip + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + + + ------------------------------------------------------------------------------------ + Decryption code comes from crypt.c by Info-ZIP but has been greatly reduced in terms of + compatibility with older software. The following is from the original crypt.c. + Code woven in by Terry Thorsen 1/2003. + + Copyright (c) 1990-2000 Info-ZIP. All rights reserved. + + See the accompanying file LICENSE, version 2000-Apr-09 or later + (the contents of which are also included in zip.h) for terms of use. + If, for some reason, all these files are missing, the Info-ZIP license + also may be found at: ftp://ftp.info-zip.org/pub/infozip/license.html + + crypt.c (full version) by Info-ZIP. Last revised: [see crypt.h] + + The encryption/decryption parts of this source code (as opposed to the + non-echoing password parts) were originally written in Europe. The + whole source package can be freely distributed, including from the USA. + (Prior to January 2000, re-export from the US was a violation of US law.) + + This encryption code is a direct transcription of the algorithm from + Roger Schlafly, described by Phil Katz in the file appnote.txt. This + file (appnote.txt) is distributed with the PKZIP program (even in the + version without encryption capabilities). + + ------------------------------------------------------------------------------------ + + Changes in unzip.c + + 2007-2008 - Even Rouault - Addition of cpl_unzGetCurrentFileZStreamPos + 2007-2008 - Even Rouault - Decoration of symbol names unz* -> cpl_unz* + 2007-2008 - Even Rouault - Remove old C style function prototypes + 2007-2008 - Even Rouault - Add unzip support for ZIP64 + + Copyright (C) 2007-2008 Even Rouault + + + Oct-2009 - Mathias Svensson - Removed cpl_* from symbol names (Even Rouault added them but since this is now moved to a new project (minizip64) I renamed them again). + Oct-2009 - Mathias Svensson - Fixed problem if uncompressed size was > 4G and compressed size was <4G + should only read the compressed/uncompressed size from the Zip64 format if + the size from normal header was 0xFFFFFFFF + Oct-2009 - Mathias Svensson - Applied some bug fixes from paches recived from Gilles Vollant + Oct-2009 - Mathias Svensson - Applied support to unzip files with compression mathod BZIP2 (bzip2 lib is required) + Patch created by Daniel Borca + + Jan-2010 - back to unzip and minizip 1.0 name scheme, with compatibility layer + + Copyright (C) 1998 - 2010 Gilles Vollant, Even Rouault, Mathias Svensson + +*/ + + +#include +#include +#include + +#ifndef NOUNCRYPT + #define NOUNCRYPT +#endif + +#include "zlib.h" +#include "unzip.h" + +#ifdef STDC +# include +# include +# include +#endif +#ifdef NO_ERRNO_H + extern int errno; +#else +# include +#endif + + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + + +#ifndef CASESENSITIVITYDEFAULT_NO +# if !defined(unix) && !defined(CASESENSITIVITYDEFAULT_YES) +# define CASESENSITIVITYDEFAULT_NO +# endif +#endif + + +#ifndef UNZ_BUFSIZE +#define UNZ_BUFSIZE (16384) +#endif + +#ifndef UNZ_MAXFILENAMEINZIP +#define UNZ_MAXFILENAMEINZIP (256) +#endif + +#ifndef ALLOC +# define ALLOC(size) (malloc(size)) +#endif +#ifndef TRYFREE +# define TRYFREE(p) {if (p) free(p);} +#endif + +#define SIZECENTRALDIRITEM (0x2e) +#define SIZEZIPLOCALHEADER (0x1e) + + +const char unz_copyright[] = + " unzip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll"; + +/* unz_file_info_interntal contain internal info about a file in zipfile*/ +typedef struct unz_file_info64_internal_s +{ + ZPOS64_T offset_curfile;/* relative offset of local header 8 bytes */ +} unz_file_info64_internal; + + +/* file_in_zip_read_info_s contain internal information about a file in zipfile, + when reading and decompress it */ +typedef struct +{ + char *read_buffer; /* internal buffer for compressed data */ + z_stream stream; /* zLib stream structure for inflate */ + +#ifdef HAVE_BZIP2 + bz_stream bstream; /* bzLib stream structure for bziped */ +#endif + + ZPOS64_T pos_in_zipfile; /* position in byte on the zipfile, for fseek*/ + uLong stream_initialised; /* flag set if stream structure is initialised*/ + + ZPOS64_T offset_local_extrafield;/* offset of the local extra field */ + uInt size_local_extrafield;/* size of the local extra field */ + ZPOS64_T pos_local_extrafield; /* position in the local extra field in read*/ + ZPOS64_T total_out_64; + + uLong crc32; /* crc32 of all data uncompressed */ + uLong crc32_wait; /* crc32 we must obtain after decompress all */ + ZPOS64_T rest_read_compressed; /* number of byte to be decompressed */ + ZPOS64_T rest_read_uncompressed;/*number of byte to be obtained after decomp*/ + zlib_filefunc64_32_def z_filefunc; + voidpf filestream; /* io structore of the zipfile */ + uLong compression_method; /* compression method (0==store) */ + ZPOS64_T byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + int raw; + int extra_size; +} file_in_zip64_read_info_s; + + +/* unz64_s contain internal information about the zipfile +*/ +typedef struct +{ + zlib_filefunc64_32_def z_filefunc; + int is64bitOpenFunction; + voidpf filestream; /* io structore of the zipfile */ + unz_global_info64 gi; /* public global information */ + ZPOS64_T byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + ZPOS64_T num_file; /* number of the current file in the zipfile*/ + ZPOS64_T pos_in_central_dir; /* pos of the current file in the central dir*/ + ZPOS64_T current_file_ok; /* flag about the usability of the current file*/ + ZPOS64_T central_pos; /* position of the beginning of the central dir*/ + + ZPOS64_T size_central_dir; /* size of the central directory */ + ZPOS64_T offset_central_dir; /* offset of start of central directory with + respect to the starting disk number */ + + unz_file_info64 cur_file_info; /* public info about the current file in zip*/ + unz_file_info64_internal cur_file_info_internal; /* private info about it*/ + file_in_zip64_read_info_s* pfile_in_zip_read; /* structure about the current + file if we are decompressing it */ + int encrypted; + + int isZip64; + +# ifndef NOUNCRYPT + unsigned long keys[3]; /* keys defining the pseudo-random sequence */ + const unsigned long* pcrc_32_tab; +# endif +} unz64_s; + + +#ifndef NOUNCRYPT +#include "crypt.h" +#endif + +/* =========================================================================== + Read a byte from a gz_stream; update next_in and avail_in. Return EOF + for end of file. + IN assertion: the stream s has been sucessfully opened for reading. +*/ + + +local int unz64local_getByte OF(( + const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + int *pi)); + +local int unz64local_getByte(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, int *pi) +{ + unsigned char c; + int err = (int)ZREAD64(*pzlib_filefunc_def,filestream,&c,1); + if (err==1) + { + *pi = (int)c; + return UNZ_OK; + } + else + { + if (ZERROR64(*pzlib_filefunc_def,filestream)) + return UNZ_ERRNO; + else + return UNZ_EOF; + } +} + + +/* =========================================================================== + Reads a long in LSB order from the given gz_stream. Sets +*/ +local int unz64local_getShort OF(( + const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX)); + +local int unz64local_getShort (const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX) +{ + uLong x ; + int i = 0; + int err; + + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((uLong)i)<<8; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int unz64local_getLong OF(( + const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX)); + +local int unz64local_getLong (const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX) +{ + uLong x ; + int i = 0; + int err; + + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((uLong)i)<<8; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((uLong)i)<<16; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<24; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int unz64local_getLong64 OF(( + const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + ZPOS64_T *pX)); + + +local int unz64local_getLong64 (const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + ZPOS64_T *pX) +{ + ZPOS64_T x ; + int i = 0; + int err; + + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (ZPOS64_T)i; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<8; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<16; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<24; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<32; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<40; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<48; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<56; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +/* My own strcmpi / strcasecmp */ +local int strcmpcasenosensitive_internal (const char* fileName1, const char* fileName2) +{ + for (;;) + { + char c1=*(fileName1++); + char c2=*(fileName2++); + if ((c1>='a') && (c1<='z')) + c1 -= 0x20; + if ((c2>='a') && (c2<='z')) + c2 -= 0x20; + if (c1=='\0') + return ((c2=='\0') ? 0 : -1); + if (c2=='\0') + return 1; + if (c1c2) + return 1; + } +} + + +#ifdef CASESENSITIVITYDEFAULT_NO +#define CASESENSITIVITYDEFAULTVALUE 2 +#else +#define CASESENSITIVITYDEFAULTVALUE 1 +#endif + +#ifndef STRCMPCASENOSENTIVEFUNCTION +#define STRCMPCASENOSENTIVEFUNCTION strcmpcasenosensitive_internal +#endif + +/* + Compare two filename (fileName1,fileName2). + If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) + If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi + or strcasecmp) + If iCaseSenisivity = 0, case sensitivity is defaut of your operating system + (like 1 on Unix, 2 on Windows) + +*/ +extern int ZEXPORT unzStringFileNameCompare (const char* fileName1, + const char* fileName2, + int iCaseSensitivity) + +{ + if (iCaseSensitivity==0) + iCaseSensitivity=CASESENSITIVITYDEFAULTVALUE; + + if (iCaseSensitivity==1) + return strcmp(fileName1,fileName2); + + return STRCMPCASENOSENTIVEFUNCTION(fileName1,fileName2); +} + +#ifndef BUFREADCOMMENT +#define BUFREADCOMMENT (0x400) +#endif + +/* + Locate the Central directory of a zipfile (at the end, just before + the global comment) +*/ +local ZPOS64_T unz64local_SearchCentralDir OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream)); +local ZPOS64_T unz64local_SearchCentralDir(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream) +{ + unsigned char* buf; + ZPOS64_T uSizeFile; + ZPOS64_T uBackRead; + ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */ + ZPOS64_T uPosFound=0; + + if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) + return 0; + + + uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackReaduMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos); + if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) + break; + + if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && + ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) + { + uPosFound = uReadPos+i; + break; + } + + if (uPosFound!=0) + break; + } + TRYFREE(buf); + return uPosFound; +} + + +/* + Locate the Central directory 64 of a zipfile (at the end, just before + the global comment) +*/ +local ZPOS64_T unz64local_SearchCentralDir64 OF(( + const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream)); + +local ZPOS64_T unz64local_SearchCentralDir64(const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream) +{ + unsigned char* buf; + ZPOS64_T uSizeFile; + ZPOS64_T uBackRead; + ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */ + ZPOS64_T uPosFound=0; + uLong uL; + ZPOS64_T relativeOffset; + + if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) + return 0; + + + uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackReaduMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos); + if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) + break; + + if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && + ((*(buf+i+2))==0x06) && ((*(buf+i+3))==0x07)) + { + uPosFound = uReadPos+i; + break; + } + + if (uPosFound!=0) + break; + } + TRYFREE(buf); + if (uPosFound == 0) + return 0; + + /* Zip64 end of central directory locator */ + if (ZSEEK64(*pzlib_filefunc_def,filestream, uPosFound,ZLIB_FILEFUNC_SEEK_SET)!=0) + return 0; + + /* the signature, already checked */ + if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) + return 0; + + /* number of the disk with the start of the zip64 end of central directory */ + if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) + return 0; + if (uL != 0) + return 0; + + /* relative offset of the zip64 end of central directory record */ + if (unz64local_getLong64(pzlib_filefunc_def,filestream,&relativeOffset)!=UNZ_OK) + return 0; + + /* total number of disks */ + if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) + return 0; + if (uL != 1) + return 0; + + /* Goto end of central directory record */ + if (ZSEEK64(*pzlib_filefunc_def,filestream, relativeOffset,ZLIB_FILEFUNC_SEEK_SET)!=0) + return 0; + + /* the signature */ + if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) + return 0; + + if (uL != 0x06064b50) + return 0; + + return relativeOffset; +} + +/* + Open a Zip file. path contain the full pathname (by example, + on a Windows NT computer "c:\\test\\zlib114.zip" or on an Unix computer + "zlib/zlib114.zip". + If the zipfile cannot be opened (file doesn't exist or in not valid), the + return value is NULL. + Else, the return value is a unzFile Handle, usable with other function + of this unzip package. +*/ +local unzFile unzOpenInternal (const void *path, + zlib_filefunc64_32_def* pzlib_filefunc64_32_def, + int is64bitOpenFunction) +{ + unz64_s us; + unz64_s *s; + ZPOS64_T central_pos; + uLong uL; + + uLong number_disk; /* number of the current dist, used for + spaning ZIP, unsupported, always 0*/ + uLong number_disk_with_CD; /* number the the disk with central dir, used + for spaning ZIP, unsupported, always 0*/ + ZPOS64_T number_entry_CD; /* total number of entries in + the central dir + (same than number_entry on nospan) */ + + int err=UNZ_OK; + + if (unz_copyright[0]!=' ') + return NULL; + + us.z_filefunc.zseek32_file = NULL; + us.z_filefunc.ztell32_file = NULL; + if (pzlib_filefunc64_32_def==NULL) + return NULL; // standard i/o not supported + us.z_filefunc = *pzlib_filefunc64_32_def; + us.is64bitOpenFunction = is64bitOpenFunction; + + + + us.filestream = ZOPEN64(us.z_filefunc, + path, + ZLIB_FILEFUNC_MODE_READ | + ZLIB_FILEFUNC_MODE_EXISTING); + if (us.filestream==NULL) { + printf("no stream\n"); + return NULL; + }; + + central_pos = unz64local_SearchCentralDir64(&us.z_filefunc,us.filestream); + if (central_pos) + { + uLong uS; + ZPOS64_T uL64; + + us.isZip64 = 1; + + if (ZSEEK64(us.z_filefunc, us.filestream, + central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) + err=UNZ_ERRNO; + + /* the signature, already checked */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + + /* size of zip64 end of central directory record */ + if (unz64local_getLong64(&us.z_filefunc, us.filestream,&uL64)!=UNZ_OK) + err=UNZ_ERRNO; + + /* version made by */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&uS)!=UNZ_OK) + err=UNZ_ERRNO; + + /* version needed to extract */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&uS)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of this disk */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&number_disk)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of the disk with the start of the central directory */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&number_disk_with_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central directory on this disk */ + if (unz64local_getLong64(&us.z_filefunc, us.filestream,&us.gi.number_entry)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central directory */ + if (unz64local_getLong64(&us.z_filefunc, us.filestream,&number_entry_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + if ((number_entry_CD!=us.gi.number_entry) || + (number_disk_with_CD!=0) || + (number_disk!=0)) + err=UNZ_BADZIPFILE; + + /* size of the central directory */ + if (unz64local_getLong64(&us.z_filefunc, us.filestream,&us.size_central_dir)!=UNZ_OK) + err=UNZ_ERRNO; + + /* offset of start of central directory with respect to the + starting disk number */ + if (unz64local_getLong64(&us.z_filefunc, us.filestream,&us.offset_central_dir)!=UNZ_OK) + err=UNZ_ERRNO; + + us.gi.size_comment = 0; + } + else + { + central_pos = unz64local_SearchCentralDir(&us.z_filefunc,us.filestream); + if (central_pos==0) + err=UNZ_ERRNO; + + us.isZip64 = 0; + + if (ZSEEK64(us.z_filefunc, us.filestream, + central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) + err=UNZ_ERRNO; + + /* the signature, already checked */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of this disk */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&number_disk)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of the disk with the start of the central directory */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&number_disk_with_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central dir on this disk */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + us.gi.number_entry = uL; + + /* total number of entries in the central dir */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + number_entry_CD = uL; + + if ((number_entry_CD!=us.gi.number_entry) || + (number_disk_with_CD!=0) || + (number_disk!=0)) + err=UNZ_BADZIPFILE; + + /* size of the central directory */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + us.size_central_dir = uL; + + /* offset of start of central directory with respect to the + starting disk number */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + us.offset_central_dir = uL; + + /* zipfile comment length */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&us.gi.size_comment)!=UNZ_OK) + err=UNZ_ERRNO; + } + + if ((central_posz_filefunc.zfile_func64.opaque; +}; + +/* + Close a ZipFile opened with unzipOpen. + If there is files inside the .Zip opened with unzipOpenCurrentFile (see later), + these files MUST be closed with unzipCloseCurrentFile before call unzipClose. + return UNZ_OK if there is no problem. */ +extern int ZEXPORT unzClose (unzFile file) +{ + unz64_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + + if (s->pfile_in_zip_read!=NULL) + unzCloseCurrentFile(file); + + ZCLOSE64(s->z_filefunc, s->filestream); + TRYFREE(s); + return UNZ_OK; +} + + +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. */ +extern int ZEXPORT unzGetGlobalInfo64 (unzFile file, unz_global_info64* pglobal_info) +{ + unz64_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + *pglobal_info=s->gi; + return UNZ_OK; +} + +extern int ZEXPORT unzGetGlobalInfo (unzFile file, unz_global_info* pglobal_info32) +{ + unz64_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + /* to do : check if number_entry is not truncated */ + pglobal_info32->number_entry = (uLong)s->gi.number_entry; + pglobal_info32->size_comment = s->gi.size_comment; + return UNZ_OK; +} +/* + Translate date/time from Dos format to tm_unz (readable more easilty) +*/ +local void unz64local_DosDateToTmuDate (ZPOS64_T ulDosDate, tm_unz* ptm) +{ + ZPOS64_T uDate; + uDate = (ZPOS64_T)(ulDosDate>>16); + ptm->tm_mday = (uInt)(uDate&0x1f) ; + ptm->tm_mon = (uInt)((((uDate)&0x1E0)/0x20)-1) ; + ptm->tm_year = (uInt)(((uDate&0x0FE00)/0x0200)+1980) ; + + ptm->tm_hour = (uInt) ((ulDosDate &0xF800)/0x800); + ptm->tm_min = (uInt) ((ulDosDate&0x7E0)/0x20) ; + ptm->tm_sec = (uInt) (2*(ulDosDate&0x1f)) ; +} + +/* + Get Info about the current file in the zipfile, with internal only info +*/ +local int unz64local_GetCurrentFileInfoInternal OF((unzFile file, + unz_file_info64 *pfile_info, + unz_file_info64_internal + *pfile_info_internal, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); + +local int unz64local_GetCurrentFileInfoInternal (unzFile file, + unz_file_info64 *pfile_info, + unz_file_info64_internal + *pfile_info_internal, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize) +{ + unz64_s* s; + unz_file_info64 file_info; + unz_file_info64_internal file_info_internal; + int err=UNZ_OK; + uLong uMagic; + long lSeek=0; + uLong uL; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + if (ZSEEK64(s->z_filefunc, s->filestream, + s->pos_in_central_dir+s->byte_before_the_zipfile, + ZLIB_FILEFUNC_SEEK_SET)!=0) + err=UNZ_ERRNO; + + + /* we check the magic */ + if (err==UNZ_OK) + { + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x02014b50) + err=UNZ_BADZIPFILE; + } + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.version) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.version_needed) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.flag) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.compression_method) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&file_info.dosDate) != UNZ_OK) + err=UNZ_ERRNO; + + unz64local_DosDateToTmuDate(file_info.dosDate,&file_info.tmu_date); + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&file_info.crc) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) + err=UNZ_ERRNO; + file_info.compressed_size = uL; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) + err=UNZ_ERRNO; + file_info.uncompressed_size = uL; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.size_filename) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_extra) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_comment) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.disk_num_start) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.internal_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&file_info.external_fa) != UNZ_OK) + err=UNZ_ERRNO; + + // relative offset of local header + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) + err=UNZ_ERRNO; + file_info_internal.offset_curfile = uL; + + lSeek+=file_info.size_filename; + if ((err==UNZ_OK) && (szFileName!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_filename0) && (fileNameBufferSize>0)) + if (ZREAD64(s->z_filefunc, s->filestream,szFileName,uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + lSeek -= uSizeRead; + } + + // Read extrafield + if ((err==UNZ_OK) && (extraField!=NULL)) + { + ZPOS64_T uSizeRead ; + if (file_info.size_file_extraz_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + } + + if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0)) + if (ZREAD64(s->z_filefunc, s->filestream,extraField,(uLong)uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + + lSeek += file_info.size_file_extra - (uLong)uSizeRead; + } + else + lSeek += file_info.size_file_extra; + + + if ((err==UNZ_OK) && (file_info.size_file_extra != 0)) + { + uLong acc = 0; + + // since lSeek now points to after the extra field we need to move back + lSeek -= file_info.size_file_extra; + + if (lSeek!=0) + { + if (ZSEEK64(s->z_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + } + + while(acc < file_info.size_file_extra) + { + uLong headerId; + uLong dataSize; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&headerId) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&dataSize) != UNZ_OK) + err=UNZ_ERRNO; + + /* ZIP64 extra fields */ + if (headerId == 0x0001) + { + uLong uL; + + if(file_info.uncompressed_size == (ZPOS64_T)(unsigned long)-1) + { + if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info.uncompressed_size) != UNZ_OK) + err=UNZ_ERRNO; + } + + if(file_info.compressed_size == (ZPOS64_T)(unsigned long)-1) + { + if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info.compressed_size) != UNZ_OK) + err=UNZ_ERRNO; + } + + if(file_info_internal.offset_curfile == (ZPOS64_T)(unsigned long)-1) + { + /* Relative Header offset */ + if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info_internal.offset_curfile) != UNZ_OK) + err=UNZ_ERRNO; + } + + if(file_info.disk_num_start == (unsigned long)-1) + { + /* Disk Start Number */ + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) + err=UNZ_ERRNO; + } + + } + else + { + if (ZSEEK64(s->z_filefunc, s->filestream,dataSize,ZLIB_FILEFUNC_SEEK_CUR)!=0) + err=UNZ_ERRNO; + } + + acc += 2 + 2 + dataSize; + } + } + + if ((err==UNZ_OK) && (szComment!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_commentz_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + } + + if ((file_info.size_file_comment>0) && (commentBufferSize>0)) + if (ZREAD64(s->z_filefunc, s->filestream,szComment,uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + lSeek+=file_info.size_file_comment - uSizeRead; + } + else + lSeek+=file_info.size_file_comment; + + + if ((err==UNZ_OK) && (pfile_info!=NULL)) + *pfile_info=file_info; + + if ((err==UNZ_OK) && (pfile_info_internal!=NULL)) + *pfile_info_internal=file_info_internal; + + return err; +} + + + +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. +*/ +extern int ZEXPORT unzGetCurrentFileInfo64 (unzFile file, + unz_file_info64 * pfile_info, + char * szFileName, uLong fileNameBufferSize, + void *extraField, uLong extraFieldBufferSize, + char* szComment, uLong commentBufferSize) +{ + return unz64local_GetCurrentFileInfoInternal(file,pfile_info,NULL, + szFileName,fileNameBufferSize, + extraField,extraFieldBufferSize, + szComment,commentBufferSize); +} + +extern int ZEXPORT unzGetCurrentFileInfo (unzFile file, + unz_file_info * pfile_info, + char * szFileName, uLong fileNameBufferSize, + void *extraField, uLong extraFieldBufferSize, + char* szComment, uLong commentBufferSize) +{ + int err; + unz_file_info64 file_info64; + err = unz64local_GetCurrentFileInfoInternal(file,&file_info64,NULL, + szFileName,fileNameBufferSize, + extraField,extraFieldBufferSize, + szComment,commentBufferSize); + if (err==UNZ_OK) + { + pfile_info->version = file_info64.version; + pfile_info->version_needed = file_info64.version_needed; + pfile_info->flag = file_info64.flag; + pfile_info->compression_method = file_info64.compression_method; + pfile_info->dosDate = file_info64.dosDate; + pfile_info->crc = file_info64.crc; + + pfile_info->size_filename = file_info64.size_filename; + pfile_info->size_file_extra = file_info64.size_file_extra; + pfile_info->size_file_comment = file_info64.size_file_comment; + + pfile_info->disk_num_start = file_info64.disk_num_start; + pfile_info->internal_fa = file_info64.internal_fa; + pfile_info->external_fa = file_info64.external_fa; + + pfile_info->tmu_date = file_info64.tmu_date, + + + pfile_info->compressed_size = (uLong)file_info64.compressed_size; + pfile_info->uncompressed_size = (uLong)file_info64.uncompressed_size; + + } + return err; +} +/* + Set the current file of the zipfile to the first file. + return UNZ_OK if there is no problem +*/ +extern int ZEXPORT unzGoToFirstFile (unzFile file) +{ + int err=UNZ_OK; + unz64_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + s->pos_in_central_dir=s->offset_central_dir; + s->num_file=0; + err=unz64local_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + +/* + Set the current file of the zipfile to the next file. + return UNZ_OK if there is no problem + return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +*/ +extern int ZEXPORT unzGoToNextFile (unzFile file) +{ + unz64_s* s; + int err; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + if (s->gi.number_entry != 0xffff) /* 2^16 files overflow hack */ + if (s->num_file+1==s->gi.number_entry) + return UNZ_END_OF_LIST_OF_FILE; + + s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename + + s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment ; + s->num_file++; + err = unz64local_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + + +/* + Try locate the file szFileName in the zipfile. + For the iCaseSensitivity signification, see unzipStringFileNameCompare + + return value : + UNZ_OK if the file is found. It becomes the current file. + UNZ_END_OF_LIST_OF_FILE if the file is not found +*/ +extern int ZEXPORT unzLocateFile (unzFile file, const char *szFileName, int iCaseSensitivity) +{ + unz64_s* s; + int err; + + /* We remember the 'current' position in the file so that we can jump + * back there if we fail. + */ + unz_file_info64 cur_file_infoSaved; + unz_file_info64_internal cur_file_info_internalSaved; + ZPOS64_T num_fileSaved; + ZPOS64_T pos_in_central_dirSaved; + + + if (file==NULL) + return UNZ_PARAMERROR; + + if (strlen(szFileName)>=UNZ_MAXFILENAMEINZIP) + return UNZ_PARAMERROR; + + s=(unz64_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + /* Save the current state */ + num_fileSaved = s->num_file; + pos_in_central_dirSaved = s->pos_in_central_dir; + cur_file_infoSaved = s->cur_file_info; + cur_file_info_internalSaved = s->cur_file_info_internal; + + err = unzGoToFirstFile(file); + + while (err == UNZ_OK) + { + char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1]; + err = unzGetCurrentFileInfo64(file,NULL, + szCurrentFileName,sizeof(szCurrentFileName)-1, + NULL,0,NULL,0); + if (err == UNZ_OK) + { + if (unzStringFileNameCompare(szCurrentFileName, + szFileName,iCaseSensitivity)==0) + return UNZ_OK; + err = unzGoToNextFile(file); + } + } + + /* We failed, so restore the state of the 'current file' to where we + * were. + */ + s->num_file = num_fileSaved ; + s->pos_in_central_dir = pos_in_central_dirSaved ; + s->cur_file_info = cur_file_infoSaved; + s->cur_file_info_internal = cur_file_info_internalSaved; + return err; +} + + +/* +/////////////////////////////////////////// +// Contributed by Ryan Haksi (mailto://cryogen@infoserve.net) +// I need random access +// +// Further optimization could be realized by adding an ability +// to cache the directory in memory. The goal being a single +// comprehensive file read to put the file I need in a memory. +*/ + +/* +typedef struct unz_file_pos_s +{ + ZPOS64_T pos_in_zip_directory; // offset in file + ZPOS64_T num_of_file; // # of file +} unz_file_pos; +*/ + +extern int ZEXPORT unzGetFilePos64(unzFile file, unz64_file_pos* file_pos) +{ + unz64_s* s; + + if (file==NULL || file_pos==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + file_pos->pos_in_zip_directory = s->pos_in_central_dir; + file_pos->num_of_file = s->num_file; + + return UNZ_OK; +} + +extern int ZEXPORT unzGetFilePos( + unzFile file, + unz_file_pos* file_pos) +{ + unz64_file_pos file_pos64; + int err = unzGetFilePos64(file,&file_pos64); + if (err==UNZ_OK) + { + file_pos->pos_in_zip_directory = (uLong)file_pos64.pos_in_zip_directory; + file_pos->num_of_file = (uLong)file_pos64.num_of_file; + } + return err; +} + +extern int ZEXPORT unzGoToFilePos64(unzFile file, const unz64_file_pos* file_pos) +{ + unz64_s* s; + int err; + + if (file==NULL || file_pos==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + + /* jump to the right spot */ + s->pos_in_central_dir = file_pos->pos_in_zip_directory; + s->num_file = file_pos->num_of_file; + + /* set the current file */ + err = unz64local_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + /* return results */ + s->current_file_ok = (err == UNZ_OK); + return err; +} + +extern int ZEXPORT unzGoToFilePos( + unzFile file, + unz_file_pos* file_pos) +{ + unz64_file_pos file_pos64; + if (file_pos == NULL) + return UNZ_PARAMERROR; + + file_pos64.pos_in_zip_directory = file_pos->pos_in_zip_directory; + file_pos64.num_of_file = file_pos->num_of_file; + return unzGoToFilePos64(file,&file_pos64); +} + +/* +// Unzip Helper Functions - should be here? +/////////////////////////////////////////// +*/ + +/* + Read the local header of the current zipfile + Check the coherency of the local header and info in the end of central + directory about this file + store in *piSizeVar the size of extra info in local header + (filename and size of extra field data) +*/ +local int unz64local_CheckCurrentFileCoherencyHeader (unz64_s* s, uInt* piSizeVar, + ZPOS64_T * poffset_local_extrafield, + uInt * psize_local_extrafield) +{ + uLong uMagic,uData,uFlags; + uLong size_filename; + uLong size_extra_field; + int err=UNZ_OK; + + *piSizeVar = 0; + *poffset_local_extrafield = 0; + *psize_local_extrafield = 0; + + if (ZSEEK64(s->z_filefunc, s->filestream,s->cur_file_info_internal.offset_curfile + + s->byte_before_the_zipfile,ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + + + if (err==UNZ_OK) + { + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x04034b50) + err=UNZ_BADZIPFILE; + } + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) + err=UNZ_ERRNO; +/* + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.wVersion)) + err=UNZ_BADZIPFILE; +*/ + if (unz64local_getShort(&s->z_filefunc, s->filestream,&uFlags) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compression_method)) + err=UNZ_BADZIPFILE; + + if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) && +/* #ifdef HAVE_BZIP2 */ + (s->cur_file_info.compression_method!=Z_BZIP2ED) && +/* #endif */ + (s->cur_file_info.compression_method!=Z_DEFLATED)) + err=UNZ_BADZIPFILE; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* date/time */ + err=UNZ_ERRNO; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* crc */ + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.crc) && ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size compr */ + err=UNZ_ERRNO; + else if (uData != 0xFFFFFFFF && (err==UNZ_OK) && (uData!=s->cur_file_info.compressed_size) && ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size uncompr */ + err=UNZ_ERRNO; + else if (uData != 0xFFFFFFFF && (err==UNZ_OK) && (uData!=s->cur_file_info.uncompressed_size) && ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&size_filename) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (size_filename!=s->cur_file_info.size_filename)) + err=UNZ_BADZIPFILE; + + *piSizeVar += (uInt)size_filename; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&size_extra_field) != UNZ_OK) + err=UNZ_ERRNO; + *poffset_local_extrafield= s->cur_file_info_internal.offset_curfile + + SIZEZIPLOCALHEADER + size_filename; + *psize_local_extrafield = (uInt)size_extra_field; + + *piSizeVar += (uInt)size_extra_field; + + return err; +} + +/* + Open for reading data the current file in the zipfile. + If there is no error and the file is opened, the return value is UNZ_OK. +*/ +extern int ZEXPORT unzOpenCurrentFile3 (unzFile file, int* method, + int* level, int raw, const char* password) +{ + int err=UNZ_OK; + uInt iSizeVar; + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + ZPOS64_T offset_local_extrafield; /* offset of the local extra field */ + uInt size_local_extrafield; /* size of the local extra field */ +# ifndef NOUNCRYPT + char source[12]; +# else + if (password != NULL) + return UNZ_PARAMERROR; +# endif + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + if (!s->current_file_ok) + return UNZ_PARAMERROR; + + if (s->pfile_in_zip_read != NULL) + unzCloseCurrentFile(file); + + if (unz64local_CheckCurrentFileCoherencyHeader(s,&iSizeVar, &offset_local_extrafield,&size_local_extrafield)!=UNZ_OK) + return UNZ_BADZIPFILE; + + pfile_in_zip_read_info = (file_in_zip64_read_info_s*)ALLOC(sizeof(file_in_zip64_read_info_s)); + if (pfile_in_zip_read_info==NULL) + return UNZ_INTERNALERROR; + + pfile_in_zip_read_info->read_buffer=(char*)ALLOC(UNZ_BUFSIZE); + pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield; + pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield; + pfile_in_zip_read_info->pos_local_extrafield=0; + pfile_in_zip_read_info->raw=raw; + + if (pfile_in_zip_read_info->read_buffer==NULL) + { + TRYFREE(pfile_in_zip_read_info); + return UNZ_INTERNALERROR; + } + + pfile_in_zip_read_info->stream_initialised=0; + + if (method!=NULL) + *method = (int)s->cur_file_info.compression_method; + + if (level!=NULL) + { + *level = 6; + switch (s->cur_file_info.flag & 0x06) + { + case 6 : *level = 1; break; + case 4 : *level = 2; break; + case 2 : *level = 9; break; + } + } + + if ((s->cur_file_info.compression_method!=0) && +/* #ifdef HAVE_BZIP2 */ + (s->cur_file_info.compression_method!=Z_BZIP2ED) && +/* #endif */ + (s->cur_file_info.compression_method!=Z_DEFLATED)) + + err=UNZ_BADZIPFILE; + + pfile_in_zip_read_info->crc32_wait=s->cur_file_info.crc; + pfile_in_zip_read_info->crc32=0; + pfile_in_zip_read_info->total_out_64=0; + pfile_in_zip_read_info->compression_method = s->cur_file_info.compression_method; + pfile_in_zip_read_info->filestream=s->filestream; + pfile_in_zip_read_info->z_filefunc=s->z_filefunc; + pfile_in_zip_read_info->byte_before_the_zipfile=s->byte_before_the_zipfile; + + pfile_in_zip_read_info->stream.total_out = 0; + + if ((s->cur_file_info.compression_method==Z_BZIP2ED) && (!raw)) + { +#ifdef HAVE_BZIP2 + pfile_in_zip_read_info->bstream.bzalloc = (void *(*) (void *, int, int))0; + pfile_in_zip_read_info->bstream.bzfree = (free_func)0; + pfile_in_zip_read_info->bstream.opaque = (voidpf)0; + pfile_in_zip_read_info->bstream.state = (voidpf)0; + + pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; + pfile_in_zip_read_info->stream.zfree = (free_func)0; + pfile_in_zip_read_info->stream.opaque = (voidpf)0; + pfile_in_zip_read_info->stream.next_in = (voidpf)0; + pfile_in_zip_read_info->stream.avail_in = 0; + + err=BZ2_bzDecompressInit(&pfile_in_zip_read_info->bstream, 0, 0); + if (err == Z_OK) + pfile_in_zip_read_info->stream_initialised=Z_BZIP2ED; + else + { + TRYFREE(pfile_in_zip_read_info); + return err; + } +#else + pfile_in_zip_read_info->raw=1; +#endif + } + else if ((s->cur_file_info.compression_method==Z_DEFLATED) && (!raw)) + { + pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; + pfile_in_zip_read_info->stream.zfree = (free_func)0; + pfile_in_zip_read_info->stream.opaque = (voidpf)0; + pfile_in_zip_read_info->stream.next_in = 0; + pfile_in_zip_read_info->stream.avail_in = 0; + + err=inflateInit2(&pfile_in_zip_read_info->stream, -MAX_WBITS); + if (err == Z_OK) + pfile_in_zip_read_info->stream_initialised=Z_DEFLATED; + else + { + TRYFREE(pfile_in_zip_read_info); + return err; + } + /* windowBits is passed < 0 to tell that there is no zlib header. + * Note that in this case inflate *requires* an extra "dummy" byte + * after the compressed stream in order to complete decompression and + * return Z_STREAM_END. + * In unzip, i don't wait absolutely Z_STREAM_END because I known the + * size of both compressed and uncompressed data + */ + } + pfile_in_zip_read_info->rest_read_compressed = + s->cur_file_info.compressed_size ; + pfile_in_zip_read_info->rest_read_uncompressed = + s->cur_file_info.uncompressed_size ; + + + pfile_in_zip_read_info->pos_in_zipfile = + s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + + iSizeVar; + + pfile_in_zip_read_info->stream.avail_in = (uInt)0; + pfile_in_zip_read_info->extra_size = iSizeVar; + + s->pfile_in_zip_read = pfile_in_zip_read_info; + s->encrypted = 0; + +# ifndef NOUNCRYPT + if (password != NULL) + { + int i; + s->pcrc_32_tab = get_crc_table(); + init_keys(password,s->keys,s->pcrc_32_tab); + if (ZSEEK64(s->z_filefunc, s->filestream, + s->pfile_in_zip_read->pos_in_zipfile + + s->pfile_in_zip_read->byte_before_the_zipfile, + SEEK_SET)!=0) + return UNZ_INTERNALERROR; + if(ZREAD64(s->z_filefunc, s->filestream,source, 12)<12) + return UNZ_INTERNALERROR; + + for (i = 0; i<12; i++) + zdecode(s->keys,s->pcrc_32_tab,source[i]); + + s->pfile_in_zip_read->pos_in_zipfile+=12; + s->encrypted=1; + } +# endif + + + return UNZ_OK; +} + +extern int ZEXPORT unzSeekCurrentFile(unzFile file, int pos) { + + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + if (pfile_in_zip_read_info->compression_method==Z_BZIP2ED) { // don't know how to support bzip + return UNZ_INTERNALERROR; + }; + + if ((pfile_in_zip_read_info->compression_method==0) || (pfile_in_zip_read_info->raw)) { + + pfile_in_zip_read_info->rest_read_compressed = + s->cur_file_info.compressed_size - pos; + pfile_in_zip_read_info->rest_read_uncompressed = + s->cur_file_info.uncompressed_size - pos; + + pfile_in_zip_read_info->pos_in_zipfile = + s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + + pfile_in_zip_read_info->extra_size + pos; + + pfile_in_zip_read_info->stream.avail_in = (uInt)0; + pfile_in_zip_read_info->stream.total_out = pos; + + return ZSEEK64(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->byte_before_the_zipfile + pfile_in_zip_read_info->pos_in_zipfile, + ZLIB_FILEFUNC_SEEK_SET); + + } else { // gzip + + if (pos < pfile_in_zip_read_info->stream.total_out) { // negative seek, rewind + + pfile_in_zip_read_info->rest_read_compressed = + s->cur_file_info.compressed_size ; + pfile_in_zip_read_info->rest_read_uncompressed = + s->cur_file_info.uncompressed_size ; + + pfile_in_zip_read_info->pos_in_zipfile = + s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + + pfile_in_zip_read_info->extra_size; + + (void)inflateReset(&pfile_in_zip_read_info->stream); + + pfile_in_zip_read_info->stream.avail_in = (uInt)0; + pfile_in_zip_read_info->stream.total_out = 0; + pfile_in_zip_read_info->stream.next_in = 0; + }; + + // not sure where to read, so read on the stack + { + char buf[512]; + int to_read = pos - pfile_in_zip_read_info->stream.total_out; + while (to_read) { + + int len = to_read > sizeof(buf)?sizeof(buf):to_read; + int read = unzReadCurrentFile(file, buf, len); + if (read < 0) { + return read; + }; + to_read -= read; + if (read == UNZ_EOF) { + return pos; + }; + }; + }; + }; + + return pos; +}; + +extern int ZEXPORT unzOpenCurrentFile (unzFile file) +{ + return unzOpenCurrentFile3(file, NULL, NULL, 0, NULL); +} + +extern int ZEXPORT unzOpenCurrentFilePassword (unzFile file, const char* password) +{ + return unzOpenCurrentFile3(file, NULL, NULL, 0, password); +} + +extern int ZEXPORT unzOpenCurrentFile2 (unzFile file, int* method, int* level, int raw) +{ + return unzOpenCurrentFile3(file, method, level, raw, NULL); +} + +/** Addition for GDAL : START */ + +extern ZPOS64_T ZEXPORT unzGetCurrentFileZStreamPos64( unzFile file) +{ + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + s=(unz64_s*)file; + if (file==NULL) + return 0; //UNZ_PARAMERROR; + pfile_in_zip_read_info=s->pfile_in_zip_read; + if (pfile_in_zip_read_info==NULL) + return 0; //UNZ_PARAMERROR; + return pfile_in_zip_read_info->pos_in_zipfile + + pfile_in_zip_read_info->byte_before_the_zipfile; +} + +/** Addition for GDAL : END */ + +/* + Read bytes from the current file. + buf contain buffer where data must be copied + len the size of buf. + + return the number of byte copied if somes bytes are copied + return 0 if the end of file was reached + return <0 with error code if there is an error + (UNZ_ERRNO for IO error, or zLib error for uncompress error) +*/ +extern int ZEXPORT unzReadCurrentFile (unzFile file, voidp buf, unsigned len) +{ + int err=UNZ_OK; + uInt iRead = 0; + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if ((pfile_in_zip_read_info->read_buffer == NULL)) + return UNZ_END_OF_LIST_OF_FILE; + if (len==0) + return 0; + + pfile_in_zip_read_info->stream.next_out = (Bytef*)buf; + + pfile_in_zip_read_info->stream.avail_out = (uInt)len; + + if ((len>pfile_in_zip_read_info->rest_read_uncompressed) && + (!(pfile_in_zip_read_info->raw))) + pfile_in_zip_read_info->stream.avail_out = + (uInt)pfile_in_zip_read_info->rest_read_uncompressed; + + if ((len>pfile_in_zip_read_info->rest_read_compressed+ + pfile_in_zip_read_info->stream.avail_in) && + (pfile_in_zip_read_info->raw)) + pfile_in_zip_read_info->stream.avail_out = + (uInt)pfile_in_zip_read_info->rest_read_compressed+ + pfile_in_zip_read_info->stream.avail_in; + + while (pfile_in_zip_read_info->stream.avail_out>0) + { + if ((pfile_in_zip_read_info->stream.avail_in==0) && + (pfile_in_zip_read_info->rest_read_compressed>0)) + { + uInt uReadThis = UNZ_BUFSIZE; + if (pfile_in_zip_read_info->rest_read_compressedrest_read_compressed; + if (uReadThis == 0) + return UNZ_EOF; + if (ZSEEK64(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->pos_in_zipfile + + pfile_in_zip_read_info->byte_before_the_zipfile, + ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + if (ZREAD64(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->read_buffer, + uReadThis)!=uReadThis) + return UNZ_ERRNO; + + +# ifndef NOUNCRYPT + if(s->encrypted) + { + uInt i; + for(i=0;iread_buffer[i] = + zdecode(s->keys,s->pcrc_32_tab, + pfile_in_zip_read_info->read_buffer[i]); + } +# endif + + + pfile_in_zip_read_info->pos_in_zipfile += uReadThis; + + pfile_in_zip_read_info->rest_read_compressed-=uReadThis; + + pfile_in_zip_read_info->stream.next_in = + (Bytef*)pfile_in_zip_read_info->read_buffer; + pfile_in_zip_read_info->stream.avail_in = (uInt)uReadThis; + } + + if ((pfile_in_zip_read_info->compression_method==0) || (pfile_in_zip_read_info->raw)) + { + uInt uDoCopy,i ; + + if ((pfile_in_zip_read_info->stream.avail_in == 0) && + (pfile_in_zip_read_info->rest_read_compressed == 0)) + return (iRead==0) ? UNZ_EOF : iRead; + + if (pfile_in_zip_read_info->stream.avail_out < + pfile_in_zip_read_info->stream.avail_in) + uDoCopy = pfile_in_zip_read_info->stream.avail_out ; + else + uDoCopy = pfile_in_zip_read_info->stream.avail_in ; + + for (i=0;istream.next_out+i) = + *(pfile_in_zip_read_info->stream.next_in+i); + + pfile_in_zip_read_info->total_out_64 = pfile_in_zip_read_info->total_out_64 + uDoCopy; + + pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32, + pfile_in_zip_read_info->stream.next_out, + uDoCopy); + pfile_in_zip_read_info->rest_read_uncompressed-=uDoCopy; + pfile_in_zip_read_info->stream.avail_in -= uDoCopy; + pfile_in_zip_read_info->stream.avail_out -= uDoCopy; + pfile_in_zip_read_info->stream.next_out += uDoCopy; + pfile_in_zip_read_info->stream.next_in += uDoCopy; + pfile_in_zip_read_info->stream.total_out += uDoCopy; + iRead += uDoCopy; + } + else if (pfile_in_zip_read_info->compression_method==Z_BZIP2ED) + { +#ifdef HAVE_BZIP2 + uLong uTotalOutBefore,uTotalOutAfter; + const Bytef *bufBefore; + uLong uOutThis; + + pfile_in_zip_read_info->bstream.next_in = (char*)pfile_in_zip_read_info->stream.next_in; + pfile_in_zip_read_info->bstream.avail_in = pfile_in_zip_read_info->stream.avail_in; + pfile_in_zip_read_info->bstream.total_in_lo32 = pfile_in_zip_read_info->stream.total_in; + pfile_in_zip_read_info->bstream.total_in_hi32 = 0; + pfile_in_zip_read_info->bstream.next_out = (char*)pfile_in_zip_read_info->stream.next_out; + pfile_in_zip_read_info->bstream.avail_out = pfile_in_zip_read_info->stream.avail_out; + pfile_in_zip_read_info->bstream.total_out_lo32 = pfile_in_zip_read_info->stream.total_out; + pfile_in_zip_read_info->bstream.total_out_hi32 = 0; + + uTotalOutBefore = pfile_in_zip_read_info->bstream.total_out_lo32; + bufBefore = (const Bytef *)pfile_in_zip_read_info->bstream.next_out; + + err=BZ2_bzDecompress(&pfile_in_zip_read_info->bstream); + + uTotalOutAfter = pfile_in_zip_read_info->bstream.total_out_lo32; + uOutThis = uTotalOutAfter-uTotalOutBefore; + + pfile_in_zip_read_info->total_out_64 = pfile_in_zip_read_info->total_out_64 + uOutThis; + + pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32,bufBefore, (uInt)(uOutThis)); + pfile_in_zip_read_info->rest_read_uncompressed -= uOutThis; + iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); + + pfile_in_zip_read_info->stream.next_in = (Bytef*)pfile_in_zip_read_info->bstream.next_in; + pfile_in_zip_read_info->stream.avail_in = pfile_in_zip_read_info->bstream.avail_in; + pfile_in_zip_read_info->stream.total_in = pfile_in_zip_read_info->bstream.total_in_lo32; + pfile_in_zip_read_info->stream.next_out = (Bytef*)pfile_in_zip_read_info->bstream.next_out; + pfile_in_zip_read_info->stream.avail_out = pfile_in_zip_read_info->bstream.avail_out; + pfile_in_zip_read_info->stream.total_out = pfile_in_zip_read_info->bstream.total_out_lo32; + + if (err==BZ_STREAM_END) + return (iRead==0) ? UNZ_EOF : iRead; + if (err!=BZ_OK) + break; +#endif + } // end Z_BZIP2ED + else + { + ZPOS64_T uTotalOutBefore,uTotalOutAfter; + const Bytef *bufBefore; + ZPOS64_T uOutThis; + int flush=Z_SYNC_FLUSH; + + uTotalOutBefore = pfile_in_zip_read_info->stream.total_out; + bufBefore = pfile_in_zip_read_info->stream.next_out; + + /* + if ((pfile_in_zip_read_info->rest_read_uncompressed == + pfile_in_zip_read_info->stream.avail_out) && + (pfile_in_zip_read_info->rest_read_compressed == 0)) + flush = Z_FINISH; + */ + err=inflate(&pfile_in_zip_read_info->stream,flush); + + if ((err>=0) && (pfile_in_zip_read_info->stream.msg!=NULL)) + err = Z_DATA_ERROR; + + uTotalOutAfter = pfile_in_zip_read_info->stream.total_out; + uOutThis = uTotalOutAfter-uTotalOutBefore; + + pfile_in_zip_read_info->total_out_64 = pfile_in_zip_read_info->total_out_64 + uOutThis; + + pfile_in_zip_read_info->crc32 = + crc32(pfile_in_zip_read_info->crc32,bufBefore, + (uInt)(uOutThis)); + + pfile_in_zip_read_info->rest_read_uncompressed -= + uOutThis; + + iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); + + if (err==Z_STREAM_END) + return (iRead==0) ? UNZ_EOF : iRead; + if (err!=Z_OK) + break; + } + } + + if (err==Z_OK) + return iRead; + return err; +} + + +/* + Give the current position in uncompressed data +*/ +extern z_off_t ZEXPORT unztell (unzFile file) +{ + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + return (z_off_t)pfile_in_zip_read_info->stream.total_out; +} + +extern ZPOS64_T ZEXPORT unztell64 (unzFile file) +{ + + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return (ZPOS64_T)-1; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return (ZPOS64_T)-1; + + return pfile_in_zip_read_info->total_out_64; +} + + +/* + return 1 if the end of file was reached, 0 elsewhere +*/ +extern int ZEXPORT unzeof (unzFile file) +{ + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + if (pfile_in_zip_read_info->rest_read_uncompressed == 0) + return 1; + else + return 0; +} + + + +/* +Read extra field from the current file (opened by unzOpenCurrentFile) +This is the local-header version of the extra field (sometimes, there is +more info in the local-header version than in the central-header) + + if buf==NULL, it return the size of the local extra field that can be read + + if buf!=NULL, len is the size of the buffer, the extra header is copied in + buf. + the return value is the number of bytes copied in buf, or (if <0) + the error code +*/ +extern int ZEXPORT unzGetLocalExtrafield (unzFile file, voidp buf, unsigned len) +{ + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + uInt read_now; + ZPOS64_T size_to_read; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + size_to_read = (pfile_in_zip_read_info->size_local_extrafield - + pfile_in_zip_read_info->pos_local_extrafield); + + if (buf==NULL) + return (int)size_to_read; + + if (len>size_to_read) + read_now = (uInt)size_to_read; + else + read_now = (uInt)len ; + + if (read_now==0) + return 0; + + if (ZSEEK64(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->offset_local_extrafield + + pfile_in_zip_read_info->pos_local_extrafield, + ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + + if (ZREAD64(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + buf,read_now)!=read_now) + return UNZ_ERRNO; + + return (int)read_now; +} + +/* + Close the file in zip opened with unzipOpenCurrentFile + Return UNZ_CRCERROR if all the file was read but the CRC is not good +*/ +extern int ZEXPORT unzCloseCurrentFile (unzFile file) +{ + int err=UNZ_OK; + + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if ((pfile_in_zip_read_info->rest_read_uncompressed == 0) && + (!pfile_in_zip_read_info->raw)) + { + if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait) + err=UNZ_CRCERROR; + } + + + TRYFREE(pfile_in_zip_read_info->read_buffer); + pfile_in_zip_read_info->read_buffer = NULL; + if (pfile_in_zip_read_info->stream_initialised == Z_DEFLATED) + inflateEnd(&pfile_in_zip_read_info->stream); +#ifdef HAVE_BZIP2 + else if (pfile_in_zip_read_info->stream_initialised == Z_BZIP2ED) + BZ2_bzDecompressEnd(&pfile_in_zip_read_info->bstream); +#endif + + + pfile_in_zip_read_info->stream_initialised = 0; + TRYFREE(pfile_in_zip_read_info); + + s->pfile_in_zip_read=NULL; + + return err; +} + + +/* + Get the global comment string of the ZipFile, in the szComment buffer. + uSizeBuf is the size of the szComment buffer. + return the number of byte copied or an error code <0 +*/ +extern int ZEXPORT unzGetGlobalComment (unzFile file, char * szComment, uLong uSizeBuf) +{ + unz64_s* s; + uLong uReadThis ; + if (file==NULL) + return (int)UNZ_PARAMERROR; + s=(unz64_s*)file; + + uReadThis = uSizeBuf; + if (uReadThis>s->gi.size_comment) + uReadThis = s->gi.size_comment; + + if (ZSEEK64(s->z_filefunc,s->filestream,s->central_pos+22,ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + + if (uReadThis>0) + { + *szComment='\0'; + if (ZREAD64(s->z_filefunc,s->filestream,szComment,uReadThis)!=uReadThis) + return UNZ_ERRNO; + } + + if ((szComment != NULL) && (uSizeBuf > s->gi.size_comment)) + *(szComment+s->gi.size_comment)='\0'; + return (int)uReadThis; +} + +/* Additions by RX '2004 */ +extern ZPOS64_T ZEXPORT unzGetOffset64(unzFile file) +{ + unz64_s* s; + + if (file==NULL) + return 0; //UNZ_PARAMERROR; + s=(unz64_s*)file; + if (!s->current_file_ok) + return 0; + if (s->gi.number_entry != 0 && s->gi.number_entry != 0xffff) + if (s->num_file==s->gi.number_entry) + return 0; + return s->pos_in_central_dir; +} + +extern uLong ZEXPORT unzGetOffset (unzFile file) +{ + ZPOS64_T offset64; + + if (file==NULL) + return 0; //UNZ_PARAMERROR; + offset64 = unzGetOffset64(file); + return (uLong)offset64; +} + +extern int ZEXPORT unzSetOffset64(unzFile file, ZPOS64_T pos) +{ + unz64_s* s; + int err; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + + s->pos_in_central_dir = pos; + s->num_file = s->gi.number_entry; /* hack */ + err = unz64local_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + +extern int ZEXPORT unzSetOffset (unzFile file, uLong pos) +{ + return unzSetOffset64(file,pos); +} diff --git a/core/io/unzip.h b/core/io/unzip.h new file mode 100644 index 00000000000..fe7ad1ddf56 --- /dev/null +++ b/core/io/unzip.h @@ -0,0 +1,445 @@ +/* unzip.h -- IO for uncompress .zip files using zlib + Version 1.1, February 14h, 2010 + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications of Unzip for Zip64 + Copyright (C) 2007-2008 Even Rouault + + Modifications for Zip64 support on both zip and unzip + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + + --------------------------------------------------------------------------------- + + Condition of use and distribution are the same than zlib : + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + --------------------------------------------------------------------------------- + + Changes + + See header of unzip64.c + +*/ + +#ifndef _unz64_H +#define _unz64_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _ZLIB_H +#include "zlib.h" +#endif + +#ifndef _ZLIBIOAPI_H +#include "ioapi.h" +#endif + +#ifdef HAVE_BZIP2 +#include "bzlib.h" +#endif + +#define Z_BZIP2ED 12 + +#if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP) +/* like the STRICT of WIN32, we define a pointer that cannot be converted + from (void*) without cast */ +typedef struct TagunzFile__ { int unused; } unzFile__; +typedef unzFile__ *unzFile; +#else +typedef voidp unzFile; +#endif + + +#define UNZ_OK (0) +#define UNZ_END_OF_LIST_OF_FILE (-100) +#define UNZ_ERRNO (Z_ERRNO) +#define UNZ_EOF (0) +#define UNZ_PARAMERROR (-102) +#define UNZ_BADZIPFILE (-103) +#define UNZ_INTERNALERROR (-104) +#define UNZ_CRCERROR (-105) + +/* tm_unz contain date/time info */ +typedef struct tm_unz_s +{ + uInt tm_sec; /* seconds after the minute - [0,59] */ + uInt tm_min; /* minutes after the hour - [0,59] */ + uInt tm_hour; /* hours since midnight - [0,23] */ + uInt tm_mday; /* day of the month - [1,31] */ + uInt tm_mon; /* months since January - [0,11] */ + uInt tm_year; /* years - [1980..2044] */ +} tm_unz; + +/* unz_global_info structure contain global data about the ZIPfile + These data comes from the end of central dir */ +typedef struct unz_global_info64_s +{ + ZPOS64_T number_entry; /* total number of entries in + the central dir on this disk */ + uLong size_comment; /* size of the global comment of the zipfile */ +} unz_global_info64; + +typedef struct unz_global_info_s +{ + uLong number_entry; /* total number of entries in + the central dir on this disk */ + uLong size_comment; /* size of the global comment of the zipfile */ +} unz_global_info; + +/* unz_file_info contain information about a file in the zipfile */ +typedef struct unz_file_info64_s +{ + uLong version; /* version made by 2 bytes */ + uLong version_needed; /* version needed to extract 2 bytes */ + uLong flag; /* general purpose bit flag 2 bytes */ + uLong compression_method; /* compression method 2 bytes */ + uLong dosDate; /* last mod file date in Dos fmt 4 bytes */ + uLong crc; /* crc-32 4 bytes */ + ZPOS64_T compressed_size; /* compressed size 8 bytes */ + ZPOS64_T uncompressed_size; /* uncompressed size 8 bytes */ + uLong size_filename; /* filename length 2 bytes */ + uLong size_file_extra; /* extra field length 2 bytes */ + uLong size_file_comment; /* file comment length 2 bytes */ + + uLong disk_num_start; /* disk number start 2 bytes */ + uLong internal_fa; /* internal file attributes 2 bytes */ + uLong external_fa; /* external file attributes 4 bytes */ + + tm_unz tmu_date; +} unz_file_info64; + +typedef struct unz_file_info_s +{ + uLong version; /* version made by 2 bytes */ + uLong version_needed; /* version needed to extract 2 bytes */ + uLong flag; /* general purpose bit flag 2 bytes */ + uLong compression_method; /* compression method 2 bytes */ + uLong dosDate; /* last mod file date in Dos fmt 4 bytes */ + uLong crc; /* crc-32 4 bytes */ + uLong compressed_size; /* compressed size 4 bytes */ + uLong uncompressed_size; /* uncompressed size 4 bytes */ + uLong size_filename; /* filename length 2 bytes */ + uLong size_file_extra; /* extra field length 2 bytes */ + uLong size_file_comment; /* file comment length 2 bytes */ + + uLong disk_num_start; /* disk number start 2 bytes */ + uLong internal_fa; /* internal file attributes 2 bytes */ + uLong external_fa; /* external file attributes 4 bytes */ + + tm_unz tmu_date; +} unz_file_info; + +extern int ZEXPORT unzStringFileNameCompare OF ((const char* fileName1, + const char* fileName2, + int iCaseSensitivity)); +/* + Compare two filename (fileName1,fileName2). + If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) + If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi + or strcasecmp) + If iCaseSenisivity = 0, case sensitivity is defaut of your operating system + (like 1 on Unix, 2 on Windows) +*/ + + +extern unzFile ZEXPORT unzOpen OF((const char *path)); +extern unzFile ZEXPORT unzOpen64 OF((const void *path)); +/* + Open a Zip file. path contain the full pathname (by example, + on a Windows XP computer "c:\\zlib\\zlib113.zip" or on an Unix computer + "zlib/zlib113.zip". + If the zipfile cannot be opened (file don't exist or in not valid), the + return value is NULL. + Else, the return value is a unzFile Handle, usable with other function + of this unzip package. + the "64" function take a const void* pointer, because the path is just the + value passed to the open64_file_func callback. + Under Windows, if UNICODE is defined, using fill_fopen64_filefunc, the path + is a pointer to a wide unicode string (LPCTSTR is LPCWSTR), so const char* + does not describe the reality +*/ + + +extern unzFile ZEXPORT unzOpen2 OF((const char *path, + zlib_filefunc_def* pzlib_filefunc_def)); +/* + Open a Zip file, like unzOpen, but provide a set of file low level API + for read/write the zip file (see ioapi.h) +*/ + +extern unzFile ZEXPORT unzOpen2_64 OF((const void *path, + zlib_filefunc64_def* pzlib_filefunc_def)); +/* + Open a Zip file, like unz64Open, but provide a set of file low level API + for read/write the zip file (see ioapi.h) +*/ + +extern int ZEXPORT unzClose OF((unzFile file)); +/* + Close a ZipFile opened with unzipOpen. + If there is files inside the .Zip opened with unzOpenCurrentFile (see later), + these files MUST be closed with unzipCloseCurrentFile before call unzipClose. + return UNZ_OK if there is no problem. */ + +extern void* unzGetOpaque(unzFile file); + + +extern int ZEXPORT unzGetGlobalInfo OF((unzFile file, + unz_global_info *pglobal_info)); + +extern int ZEXPORT unzGetGlobalInfo64 OF((unzFile file, + unz_global_info64 *pglobal_info)); +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. */ + + +extern int ZEXPORT unzGetGlobalComment OF((unzFile file, + char *szComment, + uLong uSizeBuf)); +/* + Get the global comment string of the ZipFile, in the szComment buffer. + uSizeBuf is the size of the szComment buffer. + return the number of byte copied or an error code <0 +*/ + + +/***************************************************************************/ +/* Unzip package allow you browse the directory of the zipfile */ + +extern int ZEXPORT unzGoToFirstFile OF((unzFile file)); +/* + Set the current file of the zipfile to the first file. + return UNZ_OK if there is no problem +*/ + +extern int ZEXPORT unzGoToNextFile OF((unzFile file)); +/* + Set the current file of the zipfile to the next file. + return UNZ_OK if there is no problem + return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +*/ + +extern int ZEXPORT unzLocateFile OF((unzFile file, + const char *szFileName, + int iCaseSensitivity)); +/* + Try locate the file szFileName in the zipfile. + For the iCaseSensitivity signification, see unzStringFileNameCompare + + return value : + UNZ_OK if the file is found. It becomes the current file. + UNZ_END_OF_LIST_OF_FILE if the file is not found +*/ + + +/* ****************************************** */ +/* Ryan supplied functions */ +/* unz_file_info contain information about a file in the zipfile */ +typedef struct unz_file_pos_s +{ + uLong pos_in_zip_directory; /* offset in zip file directory */ + uLong num_of_file; /* # of file */ +} unz_file_pos; + +extern int ZEXPORT unzGetFilePos( + unzFile file, + unz_file_pos* file_pos); + +extern int ZEXPORT unzGoToFilePos( + unzFile file, + unz_file_pos* file_pos); + +typedef struct unz64_file_pos_s +{ + ZPOS64_T pos_in_zip_directory; /* offset in zip file directory */ + ZPOS64_T num_of_file; /* # of file */ +} unz64_file_pos; + +extern int ZEXPORT unzGetFilePos64( + unzFile file, + unz64_file_pos* file_pos); + +extern int ZEXPORT unzGoToFilePos64( + unzFile file, + const unz64_file_pos* file_pos); + +/* ****************************************** */ + +extern int ZEXPORT unzGetCurrentFileInfo64 OF((unzFile file, + unz_file_info64 *pfile_info, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); + +extern int ZEXPORT unzGetCurrentFileInfo OF((unzFile file, + unz_file_info *pfile_info, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); +/* + Get Info about the current file + if pfile_info!=NULL, the *pfile_info structure will contain somes info about + the current file + if szFileName!=NULL, the filemane string will be copied in szFileName + (fileNameBufferSize is the size of the buffer) + if extraField!=NULL, the extra field information will be copied in extraField + (extraFieldBufferSize is the size of the buffer). + This is the Central-header version of the extra field + if szComment!=NULL, the comment string of the file will be copied in szComment + (commentBufferSize is the size of the buffer) +*/ + + +/** Addition for GDAL : START */ + +extern ZPOS64_T ZEXPORT unzGetCurrentFileZStreamPos64 OF((unzFile file)); + +/** Addition for GDAL : END */ + + +/***************************************************************************/ +/* for reading the content of the current zipfile, you can open it, read data + from it, and close it (you can close it before reading all the file) + */ + +extern int ZEXPORT unzOpenCurrentFile OF((unzFile file)); +/* + Open for reading data the current file in the zipfile. + If there is no error, the return value is UNZ_OK. +*/ + +extern int ZEXPORT unzOpenCurrentFilePassword OF((unzFile file, + const char* password)); +/* + Open for reading data the current file in the zipfile. + password is a crypting password + If there is no error, the return value is UNZ_OK. +*/ + +extern int ZEXPORT unzOpenCurrentFile2 OF((unzFile file, + int* method, + int* level, + int raw)); +/* + Same than unzOpenCurrentFile, but open for read raw the file (not uncompress) + if raw==1 + *method will receive method of compression, *level will receive level of + compression + note : you can set level parameter as NULL (if you did not want known level, + but you CANNOT set method parameter as NULL +*/ + +extern int ZEXPORT unzOpenCurrentFile3 OF((unzFile file, + int* method, + int* level, + int raw, + const char* password)); +/* + Same than unzOpenCurrentFile, but open for read raw the file (not uncompress) + if raw==1 + *method will receive method of compression, *level will receive level of + compression + note : you can set level parameter as NULL (if you did not want known level, + but you CANNOT set method parameter as NULL +*/ + + +extern int ZEXPORT unzCloseCurrentFile OF((unzFile file)); +/* + Close the file in zip opened with unzOpenCurrentFile + Return UNZ_CRCERROR if all the file was read but the CRC is not good +*/ + +extern int ZEXPORT unzReadCurrentFile OF((unzFile file, + voidp buf, + unsigned len)); +/* + Read bytes from the current file (opened by unzOpenCurrentFile) + buf contain buffer where data must be copied + len the size of buf. + + return the number of byte copied if somes bytes are copied + return 0 if the end of file was reached + return <0 with error code if there is an error + (UNZ_ERRNO for IO error, or zLib error for uncompress error) +*/ + +extern int ZEXPORT unzSeekCurrentFile(unzFile file, int pos); +/* + Seek to position in uncompressed data +*/ + +extern z_off_t ZEXPORT unztell OF((unzFile file)); + +extern ZPOS64_T ZEXPORT unztell64 OF((unzFile file)); +/* + Give the current position in uncompressed data +*/ + +extern int ZEXPORT unzeof OF((unzFile file)); +/* + return 1 if the end of file was reached, 0 elsewhere +*/ + +extern int ZEXPORT unzGetLocalExtrafield OF((unzFile file, + voidp buf, + unsigned len)); +/* + Read extra field from the current file (opened by unzOpenCurrentFile) + This is the local-header version of the extra field (sometimes, there is + more info in the local-header version than in the central-header) + + if buf==NULL, it return the size of the local extra field + + if buf!=NULL, len is the size of the buffer, the extra header is copied in + buf. + the return value is the number of bytes copied in buf, or (if <0) + the error code +*/ + +/***************************************************************************/ + +/* Get the current file offset */ +extern ZPOS64_T ZEXPORT unzGetOffset64 (unzFile file); +extern uLong ZEXPORT unzGetOffset (unzFile file); + +/* Set the current file offset */ +extern int ZEXPORT unzSetOffset64 (unzFile file, ZPOS64_T pos); +extern int ZEXPORT unzSetOffset (unzFile file, uLong pos); + + + +#ifdef __cplusplus +} +#endif + +#endif /* _unz64_H */ diff --git a/core/io/xml_parser.cpp b/core/io/xml_parser.cpp new file mode 100644 index 00000000000..150643b2e14 --- /dev/null +++ b/core/io/xml_parser.cpp @@ -0,0 +1,576 @@ +/*************************************************************************/ +/* xml_parser.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 "xml_parser.h" +#include "print_string.h" +//#define DEBUG_XML + +static bool _equalsn(const CharType* str1, const CharType* str2, int len) { + int i; + for(i=0; str1[i] && str2[i] && i < len; ++i) + if (str1[i] != str2[i]) + return false; + + // if one (or both) of the strings was smaller then they + // are only equal if they have the same lenght + return (i == len) || (str1[i] == 0 && str2[i] == 0); +} + + +String XMLParser::_replace_special_characters(const String& origstr) { + + int pos = origstr.find("&"); + int oldPos = 0; + + if (pos == -1) + return origstr; + + String newstr; + + while(pos != -1 && pos < origstr.length()-2) { + // check if it is one of the special characters + + int specialChar = -1; + for (int i=0; i<(int)special_characters.size(); ++i) + { + const CharType* p = &origstr[pos]+1; + + if (_equalsn(&special_characters[i][1], p, special_characters[i].length()-1)) + { + specialChar = i; + break; + } + } + + if (specialChar != -1) + { + newstr+=(origstr.substr(oldPos, pos - oldPos)); + newstr+=(special_characters[specialChar][0]); + pos += special_characters[specialChar].length(); + } + else + { + newstr+=(origstr.substr(oldPos, pos - oldPos + 1)); + pos += 1; + } + + // find next & + oldPos = pos; + pos = origstr.find("&", pos); + } + + if (oldPos < origstr.length()-1) + newstr+=(origstr.substr(oldPos, origstr.length()-oldPos)); + + return newstr; +} + + +static inline bool _is_white_space(char c) +{ + return (c==' ' || c=='\t' || c=='\n' || c=='\r'); +} + + +//! sets the state that text was found. Returns true if set should be set +bool XMLParser::_set_text(char* start, char* end) { + // check if text is more than 2 characters, and if not, check if there is + // only white space, so that this text won't be reported + if (end - start < 3) + { + char* p = start; + for(; p != end; ++p) + if (!_is_white_space(*p)) + break; + + if (p == end) + return false; + } + + // set current text to the parsed text, and replace xml special characters + String s = String::utf8(start, (int)(end - start)); + node_name = _replace_special_characters(s); + + // current XML node type is text + node_type = NODE_TEXT; + + return true; +} + +void XMLParser::_parse_closing_xml_element() { + node_type = NODE_ELEMENT_END; + node_empty = false; + attributes.clear(); + + ++P; + const char* pBeginClose = P; + + while(*P != '>') + ++P; + + node_name = String::utf8(pBeginClose, (int)(P - pBeginClose)); +#ifdef DEBUG_XML + print_line("XML CLOSE: "+node_name); +#endif + ++P; +} + +void XMLParser::_ignore_definition() { + node_type = NODE_UNKNOWN; + + char *F=P; + // move until end marked with '>' reached + while(*P != '>') + ++P; + node_name.parse_utf8(F,P-F); + ++P; +} + +bool XMLParser::_parse_cdata() { + + if (*(P+1) != '[') + return false; + + node_type = NODE_CDATA; + + // skip '' && + (*(P-1) == ']') && + (*(P-2) == ']')) + { + cDataEnd = P - 2; + } + + ++P; + } + + if ( cDataEnd ) + node_name = String::utf8(cDataBegin, (int)(cDataEnd - cDataBegin)); + else + node_name = ""; +#ifdef DEBUG_XML + print_line("XML CDATA: "+node_name); +#endif + + return true; +} + +void XMLParser::_parse_comment() { + + node_type = NODE_COMMENT; + P += 1; + + char *pCommentBegin = P; + + int count = 1; + + // move until end of comment reached + while(count) + { + if (*P == '>') + --count; + else + if (*P == '<') + ++count; + + ++P; + } + + P -= 3; + node_name = String::utf8(pCommentBegin+2, (int)(P - pCommentBegin-2)); + P += 3; +#ifdef DEBUG_XML + print_line("XML COMMENT: "+node_name); +#endif + +} + +void XMLParser::_parse_opening_xml_element() { + + node_type = NODE_ELEMENT; + node_empty = false; + attributes.clear(); + + // find name + const char* startName = P; + + // find end of element + while(*P != '>' && !_is_white_space(*P)) + ++P; + + const char* endName = P; + + // find attributes + while(*P != '>') + { + if (_is_white_space(*P)) + ++P; + else + { + if (*P != '/') + { + // we've got an attribute + + // read the attribute names + const char* attributeNameBegin = P; + + while(!_is_white_space(*P) && *P != '=') + ++P; + + const char* attributeNameEnd = P; + ++P; + + // read the attribute value + // check for quotes and single quotes, thx to murphy + while( (*P != '\"') && (*P != '\'') && *P) + ++P; + + if (!*P) // malformatted xml file + return; + + const char attributeQuoteChar = *P; + + ++P; + const char* attributeValueBegin = P; + + while(*P != attributeQuoteChar && *P) + ++P; + + if (!*P) // malformatted xml file + return; + + const char* attributeValueEnd = P; + ++P; + + Attribute attr; + attr.name = String::utf8(attributeNameBegin, + (int)(attributeNameEnd - attributeNameBegin)); + + String s =String::utf8(attributeValueBegin, + (int)(attributeValueEnd - attributeValueBegin)); + + attr.value = _replace_special_characters(s); + attributes.push_back(attr); + } + else + { + // tag is closed directly + ++P; + node_empty = true; + break; + } + } + } + + // check if this tag is closing directly + if (endName > startName && *(endName-1) == '/') + { + // directly closing tag + node_empty = true; + endName--; + } + + node_name = String::utf8(startName, (int)(endName - startName)); +#ifdef DEBUG_XML + print_line("XML OPEN: "+node_name); +#endif + + ++P; +} + + +void XMLParser::_parse_current_node() { + + char* start = P; + node_offset = P - data; + + // more forward until '<' found + while(*P != '<' && *P) + ++P; + + if (!*P) + return; + + if (P - start > 0) + { + // we found some text, store it + if (_set_text(start, P)) + return; + } + + ++P; + + // based on current token, parse and report next element + switch(*P) + { + case '/': + _parse_closing_xml_element(); + break; + case '?': + _ignore_definition(); + break; + case '!': + if (!_parse_cdata()) + _parse_comment(); + break; + default: + _parse_opening_xml_element(); + break; + } +} + + +uint64_t XMLParser::get_node_offset() const { + + return node_offset; +}; + +Error XMLParser::seek(uint64_t p_pos) { + + ERR_FAIL_COND_V(!data, ERR_FILE_EOF) + ERR_FAIL_COND_V(p_pos >= length, ERR_FILE_EOF); + + P = data + p_pos; + + return read(); +}; + +void XMLParser::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("read"),&XMLParser::read); + ObjectTypeDB::bind_method(_MD("get_node_type"),&XMLParser::get_node_type); + ObjectTypeDB::bind_method(_MD("get_node_name"),&XMLParser::get_node_name); + ObjectTypeDB::bind_method(_MD("get_node_data"),&XMLParser::get_node_data); + ObjectTypeDB::bind_method(_MD("get_node_offset"),&XMLParser::get_node_offset); + ObjectTypeDB::bind_method(_MD("get_attribute_count"),&XMLParser::get_attribute_count); + ObjectTypeDB::bind_method(_MD("get_attribute_name"),&XMLParser::get_attribute_name); + ObjectTypeDB::bind_method(_MD("get_attribute_value"),(String (XMLParser::*)(int) const) &XMLParser::get_attribute_value); + ObjectTypeDB::bind_method(_MD("has_attribute"),&XMLParser::has_attribute); + ObjectTypeDB::bind_method(_MD("get_named_attribute_value"), (String (XMLParser::*)(const String&) const) &XMLParser::get_attribute_value); + ObjectTypeDB::bind_method(_MD("get_named_attribute_value_safe"), &XMLParser::get_attribute_value_safe); + ObjectTypeDB::bind_method(_MD("is_empty"),&XMLParser::is_empty); + ObjectTypeDB::bind_method(_MD("get_current_line"),&XMLParser::get_current_line); + ObjectTypeDB::bind_method(_MD("skip_section"),&XMLParser::skip_section); + ObjectTypeDB::bind_method(_MD("seek"),&XMLParser::seek); + ObjectTypeDB::bind_method(_MD("open"),&XMLParser::open); + + BIND_CONSTANT( NODE_NONE ); + BIND_CONSTANT( NODE_ELEMENT ); + BIND_CONSTANT( NODE_ELEMENT_END ); + BIND_CONSTANT( NODE_TEXT ); + BIND_CONSTANT( NODE_COMMENT ); + BIND_CONSTANT( NODE_CDATA ); + BIND_CONSTANT( NODE_UNKNOWN ); + +}; + + + +Error XMLParser::read() { + + // if not end reached, parse the node + if (P && (P - data) < length - 1 && *P != 0) + { + _parse_current_node(); + return OK; + } + + return ERR_FILE_EOF; +} + +XMLParser::NodeType XMLParser::get_node_type() { + + return node_type; +} +String XMLParser::get_node_data() const { + + ERR_FAIL_COND_V( node_type != NODE_TEXT, ""); + return node_name; +} + +String XMLParser::get_node_name() const { + ERR_FAIL_COND_V( node_type == NODE_TEXT, ""); + return node_name; +} +int XMLParser::get_attribute_count() const { + + return attributes.size(); +} +String XMLParser::get_attribute_name(int p_idx) const { + + ERR_FAIL_INDEX_V(p_idx,attributes.size(),""); + return attributes[p_idx].name; +} +String XMLParser::get_attribute_value(int p_idx) const { + + ERR_FAIL_INDEX_V(p_idx,attributes.size(),""); + return attributes[p_idx].value; +} +bool XMLParser::has_attribute(const String& p_name) const { + + for(int i=0;iget_len(); + ERR_FAIL_COND_V(length<1, ERR_FILE_CORRUPT); + + data = memnew_arr( char, length+1); + file->get_buffer((uint8_t*)data,length); + data[length]=0; + P=data; + + memdelete(file); + + return OK; + +} + +void XMLParser::skip_section() { + + // skip if this element is empty anyway. + if (is_empty()) + return; + + // read until we've reached the last element in this section + int tagcount = 1; + + while(tagcount && read()==OK) + { + if (get_node_type() == XMLParser::NODE_ELEMENT && + !is_empty()) + { + ++tagcount; + } + else + if (get_node_type() == XMLParser::NODE_ELEMENT_END) + --tagcount; + } + +} + +void XMLParser::close() { + + if (data) + memdelete_arr(data); + data=NULL; + length=0; + P=NULL; + node_empty=false; + node_type=NODE_NONE; + node_offset = 0; +} + +int XMLParser::get_current_line() const { + + return 0; +} + +XMLParser::XMLParser() { + + data=NULL; + close(); + special_characters.push_back("&"); + special_characters.push_back("gt;"); + special_characters.push_back("\"quot;"); + special_characters.push_back("'apos;"); + + +} +XMLParser::~XMLParser() { + + + if (data) + memdelete_arr(data); +} diff --git a/core/io/xml_parser.h b/core/io/xml_parser.h new file mode 100644 index 00000000000..3025c041b39 --- /dev/null +++ b/core/io/xml_parser.h @@ -0,0 +1,123 @@ +/*************************************************************************/ +/* xml_parser.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 XML_PARSER_H +#define XML_PARSER_H + +#include "ustring.h" +#include "vector.h" +#include "os/file_access.h" +#include "reference.h" + +/* + Based on irrXML (see their zlib license). Added mainly for compatibility with their Collada loader. +*/ + +class XMLParser : public Reference { + + OBJ_TYPE( XMLParser, Reference ); +public: + //! Enumeration of all supported source text file formats + enum SourceFormat { + SOURCE_ASCII, + SOURCE_UTF8, + SOURCE_UTF16_BE, + SOURCE_UTF16_LE, + SOURCE_UTF32_BE, + SOURCE_UTF32_LE + }; + + enum NodeType { + NODE_NONE, + NODE_ELEMENT, + NODE_ELEMENT_END, + NODE_TEXT, + NODE_COMMENT, + NODE_CDATA, + NODE_UNKNOWN + }; + +private: + + char *data; + char *P; + int length; + void unescape(String& p_str); + Vector special_characters; + String node_name; + bool node_empty; + NodeType node_type; + uint64_t node_offset; + + struct Attribute { + String name; + String value; + }; + + Vector attributes; + + String _replace_special_characters(const String& origstr); + bool _set_text(char* start, char* end); + void _parse_closing_xml_element(); + void _ignore_definition(); + bool _parse_cdata(); + void _parse_comment(); + void _parse_opening_xml_element(); + void _parse_current_node(); + + static void _bind_methods(); + +public: + + + Error read(); + NodeType get_node_type(); + String get_node_name() const; + String get_node_data() const; + uint64_t get_node_offset() const; + int get_attribute_count() const; + String get_attribute_name(int p_idx) const; + String get_attribute_value(int p_idx) const; + bool has_attribute(const String& p_name) const; + String get_attribute_value(const String& p_name) const; + String get_attribute_value_safe(const String& p_name) const; // do not print error if doesn't exist + bool is_empty() const; + int get_current_line() const; + + void skip_section(); + Error seek(uint64_t p_pos); + + Error open(const String& p_path); + void close(); + + XMLParser(); + ~XMLParser(); +}; + +#endif + diff --git a/core/io/zip.c b/core/io/zip.c new file mode 100644 index 00000000000..edf5560ddbe --- /dev/null +++ b/core/io/zip.c @@ -0,0 +1,2004 @@ +/* zip.c -- IO on .zip files using zlib + Version 1.1, February 14h, 2010 + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications for Zip64 support + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + + Changes + Oct-2009 - Mathias Svensson - Remove old C style function prototypes + Oct-2009 - Mathias Svensson - Added Zip64 Support when creating new file archives + Oct-2009 - Mathias Svensson - Did some code cleanup and refactoring to get better overview of some functions. + Oct-2009 - Mathias Svensson - Added zipRemoveExtraInfoBlock to strip extra field data from its ZIP64 data + It is used when recreting zip archive with RAW when deleting items from a zip. + ZIP64 data is automaticly added to items that needs it, and existing ZIP64 data need to be removed. + Oct-2009 - Mathias Svensson - Added support for BZIP2 as compression mode (bzip2 lib is required) + Jan-2010 - back to unzip and minizip 1.0 name scheme, with compatibility layer + +*/ + + +#include +#include +#include +#include +#include "zlib.h" +#include "zip.h" + +#ifdef STDC +# include +# include +# include +#endif +#ifdef NO_ERRNO_H + extern int errno; +#else +# include +#endif + + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +#ifndef VERSIONMADEBY +# define VERSIONMADEBY (0x0) /* platform depedent */ +#endif + +#ifndef Z_BUFSIZE +#define Z_BUFSIZE (64*1024) //(16384) +#endif + +#ifndef Z_MAXFILENAMEINZIP +#define Z_MAXFILENAMEINZIP (256) +#endif + +#ifndef ALLOC +# define ALLOC(size) (malloc(size)) +#endif +#ifndef TRYFREE +# define TRYFREE(p) {if (p) free(p);} +#endif + +/* +#define SIZECENTRALDIRITEM (0x2e) +#define SIZEZIPLOCALHEADER (0x1e) +*/ + +/* I've found an old Unix (a SunOS 4.1.3_U1) without all SEEK_* defined.... */ + + +// NOT sure that this work on ALL platform +#define MAKEULONG64(a, b) ((ZPOS64_T)(((unsigned long)(a)) | ((ZPOS64_T)((unsigned long)(b))) << 32)) + +#ifndef SEEK_CUR +#define SEEK_CUR 1 +#endif + +#ifndef SEEK_END +#define SEEK_END 2 +#endif + +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif + +#ifndef DEF_MEM_LEVEL +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif +#endif +const char zip_copyright[] =" zip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll"; + + +#define SIZEDATA_INDATABLOCK (4096-(4*4)) + +#define LOCALHEADERMAGIC (0x04034b50) +#define CENTRALHEADERMAGIC (0x02014b50) +#define ENDHEADERMAGIC (0x06054b50) +#define ZIP64ENDHEADERMAGIC (0x6064b50) +#define ZIP64ENDLOCHEADERMAGIC (0x7064b50) + +#define FLAG_LOCALHEADER_OFFSET (0x06) +#define CRC_LOCALHEADER_OFFSET (0x0e) + +#define SIZECENTRALHEADER (0x2e) /* 46 */ + +typedef struct linkedlist_datablock_internal_s +{ + struct linkedlist_datablock_internal_s* next_datablock; + uLong avail_in_this_block; + uLong filled_in_this_block; + uLong unused; /* for future use and alignement */ + unsigned char data[SIZEDATA_INDATABLOCK]; +} linkedlist_datablock_internal; + +typedef struct linkedlist_data_s +{ + linkedlist_datablock_internal* first_block; + linkedlist_datablock_internal* last_block; +} linkedlist_data; + + +typedef struct +{ + z_stream stream; /* zLib stream structure for inflate */ +#ifdef HAVE_BZIP2 + bz_stream bstream; /* bzLib stream structure for bziped */ +#endif + + int stream_initialised; /* 1 is stream is initialised */ + uInt pos_in_buffered_data; /* last written byte in buffered_data */ + + ZPOS64_T pos_local_header; /* offset of the local header of the file + currenty writing */ + char* central_header; /* central header data for the current file */ + uLong size_centralExtra; + uLong size_centralheader; /* size of the central header for cur file */ + uLong size_centralExtraFree; /* Extra bytes allocated to the centralheader but that are not used */ + uLong flag; /* flag of the file currently writing */ + + int method; /* compression method of file currenty wr.*/ + int raw; /* 1 for directly writing raw data */ + Byte buffered_data[Z_BUFSIZE];/* buffer contain compressed data to be writ*/ + uLong dosDate; + uLong crc32; + int encrypt; + int zip64; /* Add ZIP64 extened information in the extra field */ + ZPOS64_T pos_zip64extrainfo; + ZPOS64_T totalCompressedData; + ZPOS64_T totalUncompressedData; +#ifndef NOCRYPT + unsigned long keys[3]; /* keys defining the pseudo-random sequence */ + const unsigned long* pcrc_32_tab; + int crypt_header_size; +#endif +} curfile64_info; + +typedef struct +{ + zlib_filefunc64_32_def z_filefunc; + voidpf filestream; /* io structore of the zipfile */ + linkedlist_data central_dir;/* datablock with central dir in construction*/ + int in_opened_file_inzip; /* 1 if a file in the zip is currently writ.*/ + curfile64_info ci; /* info on the file curretly writing */ + + ZPOS64_T begin_pos; /* position of the beginning of the zipfile */ + ZPOS64_T add_position_when_writting_offset; + ZPOS64_T number_entry; + +#ifndef NO_ADDFILEINEXISTINGZIP + char *globalcomment; +#endif + +} zip64_internal; + + +#ifndef NOCRYPT +#define INCLUDECRYPTINGCODE_IFCRYPTALLOWED +#include "crypt.h" +#endif + +local linkedlist_datablock_internal* allocate_new_datablock() +{ + linkedlist_datablock_internal* ldi; + ldi = (linkedlist_datablock_internal*) + ALLOC(sizeof(linkedlist_datablock_internal)); + if (ldi!=NULL) + { + ldi->next_datablock = NULL ; + ldi->filled_in_this_block = 0 ; + ldi->avail_in_this_block = SIZEDATA_INDATABLOCK ; + } + return ldi; +} + +local void free_datablock(linkedlist_datablock_internal* ldi) +{ + while (ldi!=NULL) + { + linkedlist_datablock_internal* ldinext = ldi->next_datablock; + TRYFREE(ldi); + ldi = ldinext; + } +} + +local void init_linkedlist(linkedlist_data* ll) +{ + ll->first_block = ll->last_block = NULL; +} + +local void free_linkedlist(linkedlist_data* ll) +{ + free_datablock(ll->first_block); + ll->first_block = ll->last_block = NULL; +} + + +local int add_data_in_datablock(linkedlist_data* ll, const void* buf, uLong len) +{ + linkedlist_datablock_internal* ldi; + const unsigned char* from_copy; + + if (ll==NULL) + return ZIP_INTERNALERROR; + + if (ll->last_block == NULL) + { + ll->first_block = ll->last_block = allocate_new_datablock(); + if (ll->first_block == NULL) + return ZIP_INTERNALERROR; + } + + ldi = ll->last_block; + from_copy = (unsigned char*)buf; + + while (len>0) + { + uInt copy_this; + uInt i; + unsigned char* to_copy; + + if (ldi->avail_in_this_block==0) + { + ldi->next_datablock = allocate_new_datablock(); + if (ldi->next_datablock == NULL) + return ZIP_INTERNALERROR; + ldi = ldi->next_datablock ; + ll->last_block = ldi; + } + + if (ldi->avail_in_this_block < len) + copy_this = (uInt)ldi->avail_in_this_block; + else + copy_this = (uInt)len; + + to_copy = &(ldi->data[ldi->filled_in_this_block]); + + for (i=0;ifilled_in_this_block += copy_this; + ldi->avail_in_this_block -= copy_this; + from_copy += copy_this ; + len -= copy_this; + } + return ZIP_OK; +} + + + +/****************************************************************************/ + +#ifndef NO_ADDFILEINEXISTINGZIP +/* =========================================================================== + Inputs a long in LSB order to the given file + nbByte == 1, 2 ,4 or 8 (byte, short or long, ZPOS64_T) +*/ + +local int zip64local_putValue OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T x, int nbByte)); +local int zip64local_putValue (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T x, int nbByte) +{ + unsigned char buf[8]; + int n; + for (n = 0; n < nbByte; n++) + { + buf[n] = (unsigned char)(x & 0xff); + x >>= 8; + } + if (x != 0) + { /* data overflow - hack for ZIP64 (X Roche) */ + for (n = 0; n < nbByte; n++) + { + buf[n] = 0xff; + } + } + + if (ZWRITE64(*pzlib_filefunc_def,filestream,buf,nbByte)!=(uLong)nbByte) + return ZIP_ERRNO; + else + return ZIP_OK; +} + +local void zip64local_putValue_inmemory OF((void* dest, ZPOS64_T x, int nbByte)); +local void zip64local_putValue_inmemory (void* dest, ZPOS64_T x, int nbByte) +{ + unsigned char* buf=(unsigned char*)dest; + int n; + for (n = 0; n < nbByte; n++) { + buf[n] = (unsigned char)(x & 0xff); + x >>= 8; + } + + if (x != 0) + { /* data overflow - hack for ZIP64 */ + for (n = 0; n < nbByte; n++) + { + buf[n] = 0xff; + } + } +} + +/****************************************************************************/ + + +local uLong zip64local_TmzDateToDosDate(const tm_zip* ptm) +{ + uLong year = (uLong)ptm->tm_year; + if (year>=1980) + year-=1980; + else if (year>=80) + year-=80; + return + (uLong) (((ptm->tm_mday) + (32 * (ptm->tm_mon+1)) + (512 * year)) << 16) | + ((ptm->tm_sec/2) + (32* ptm->tm_min) + (2048 * (uLong)ptm->tm_hour)); +} + + +/****************************************************************************/ + +local int zip64local_getByte OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, int *pi)); + +local int zip64local_getByte(const zlib_filefunc64_32_def* pzlib_filefunc_def,voidpf filestream,int* pi) +{ + unsigned char c; + int err = (int)ZREAD64(*pzlib_filefunc_def,filestream,&c,1); + if (err==1) + { + *pi = (int)c; + return ZIP_OK; + } + else + { + if (ZERROR64(*pzlib_filefunc_def,filestream)) + return ZIP_ERRNO; + else + return ZIP_EOF; + } +} + + +/* =========================================================================== + Reads a long in LSB order from the given gz_stream. Sets +*/ +local int zip64local_getShort OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong *pX)); + +local int zip64local_getShort (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong* pX) +{ + uLong x ; + int i = 0; + int err; + + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<8; + + if (err==ZIP_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int zip64local_getLong OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong *pX)); + +local int zip64local_getLong (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong* pX) +{ + uLong x ; + int i = 0; + int err; + + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<8; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<16; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<24; + + if (err==ZIP_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int zip64local_getLong64 OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T *pX)); + + +local int zip64local_getLong64 (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T *pX) +{ + ZPOS64_T x; + int i = 0; + int err; + + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (ZPOS64_T)i; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<8; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<16; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<24; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<32; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<40; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<48; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<56; + + if (err==ZIP_OK) + *pX = x; + else + *pX = 0; + + return err; +} + +#ifndef BUFREADCOMMENT +#define BUFREADCOMMENT (0x400) +#endif +/* + Locate the Central directory of a zipfile (at the end, just before + the global comment) +*/ +local ZPOS64_T zip64local_SearchCentralDir OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream)); + +local ZPOS64_T zip64local_SearchCentralDir(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream) +{ + unsigned char* buf; + ZPOS64_T uSizeFile; + ZPOS64_T uBackRead; + ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */ + ZPOS64_T uPosFound=0; + + if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) + return 0; + + + uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackReaduMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos); + if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) + break; + + if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && + ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) + { + uPosFound = uReadPos+i; + break; + } + + if (uPosFound!=0) + break; + } + TRYFREE(buf); + return uPosFound; +} + +/* +Locate the End of Zip64 Central directory locator and from there find the CD of a zipfile (at the end, just before +the global comment) +*/ +local ZPOS64_T zip64local_SearchCentralDir64 OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream)); + +local ZPOS64_T zip64local_SearchCentralDir64(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream) +{ + unsigned char* buf; + ZPOS64_T uSizeFile; + ZPOS64_T uBackRead; + ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */ + ZPOS64_T uPosFound=0; + uLong uL; + ZPOS64_T relativeOffset; + + if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) + return 0; + + uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackReaduMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos); + if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) + break; + + if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + { + // Signature "0x07064b50" Zip64 end of central directory locater + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && ((*(buf+i+2))==0x06) && ((*(buf+i+3))==0x07)) + { + uPosFound = uReadPos+i; + break; + } + } + + if (uPosFound!=0) + break; + } + + TRYFREE(buf); + if (uPosFound == 0) + return 0; + + /* Zip64 end of central directory locator */ + if (ZSEEK64(*pzlib_filefunc_def,filestream, uPosFound,ZLIB_FILEFUNC_SEEK_SET)!=0) + return 0; + + /* the signature, already checked */ + if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK) + return 0; + + /* number of the disk with the start of the zip64 end of central directory */ + if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK) + return 0; + if (uL != 0) + return 0; + + /* relative offset of the zip64 end of central directory record */ + if (zip64local_getLong64(pzlib_filefunc_def,filestream,&relativeOffset)!=ZIP_OK) + return 0; + + /* total number of disks */ + if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK) + return 0; + if (uL != 1) + return 0; + + /* Goto Zip64 end of central directory record */ + if (ZSEEK64(*pzlib_filefunc_def,filestream, relativeOffset,ZLIB_FILEFUNC_SEEK_SET)!=0) + return 0; + + /* the signature */ + if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK) + return 0; + + if (uL != 0x06064b50) // signature of 'Zip64 end of central directory' + return 0; + + return relativeOffset; +} + +int LoadCentralDirectoryRecord(zip64_internal* pziinit) +{ + int err=ZIP_OK; + ZPOS64_T byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + + ZPOS64_T size_central_dir; /* size of the central directory */ + ZPOS64_T offset_central_dir; /* offset of start of central directory */ + ZPOS64_T central_pos; + uLong uL; + + uLong number_disk; /* number of the current dist, used for + spaning ZIP, unsupported, always 0*/ + uLong number_disk_with_CD; /* number the the disk with central dir, used + for spaning ZIP, unsupported, always 0*/ + ZPOS64_T number_entry; + ZPOS64_T number_entry_CD; /* total number of entries in + the central dir + (same than number_entry on nospan) */ + uLong VersionMadeBy; + uLong VersionNeeded; + uLong size_comment; + + int hasZIP64Record = 0; + + // check first if we find a ZIP64 record + central_pos = zip64local_SearchCentralDir64(&pziinit->z_filefunc,pziinit->filestream); + if(central_pos > 0) + { + hasZIP64Record = 1; + } + else if(central_pos == 0) + { + central_pos = zip64local_SearchCentralDir(&pziinit->z_filefunc,pziinit->filestream); + } + +/* disable to allow appending to empty ZIP archive + if (central_pos==0) + err=ZIP_ERRNO; +*/ + + if(hasZIP64Record) + { + ZPOS64_T sizeEndOfCentralDirectory; + if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, central_pos, ZLIB_FILEFUNC_SEEK_SET) != 0) + err=ZIP_ERRNO; + + /* the signature, already checked */ + if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,&uL)!=ZIP_OK) + err=ZIP_ERRNO; + + /* size of zip64 end of central directory record */ + if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream, &sizeEndOfCentralDirectory)!=ZIP_OK) + err=ZIP_ERRNO; + + /* version made by */ + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &VersionMadeBy)!=ZIP_OK) + err=ZIP_ERRNO; + + /* version needed to extract */ + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &VersionNeeded)!=ZIP_OK) + err=ZIP_ERRNO; + + /* number of this disk */ + if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,&number_disk)!=ZIP_OK) + err=ZIP_ERRNO; + + /* number of the disk with the start of the central directory */ + if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,&number_disk_with_CD)!=ZIP_OK) + err=ZIP_ERRNO; + + /* total number of entries in the central directory on this disk */ + if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream, &number_entry)!=ZIP_OK) + err=ZIP_ERRNO; + + /* total number of entries in the central directory */ + if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream,&number_entry_CD)!=ZIP_OK) + err=ZIP_ERRNO; + + if ((number_entry_CD!=number_entry) || (number_disk_with_CD!=0) || (number_disk!=0)) + err=ZIP_BADZIPFILE; + + /* size of the central directory */ + if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream,&size_central_dir)!=ZIP_OK) + err=ZIP_ERRNO; + + /* offset of start of central directory with respect to the + starting disk number */ + if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream,&offset_central_dir)!=ZIP_OK) + err=ZIP_ERRNO; + + // TODO.. + // read the comment from the standard central header. + size_comment = 0; + } + else + { + // Read End of central Directory info + if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) + err=ZIP_ERRNO; + + /* the signature, already checked */ + if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,&uL)!=ZIP_OK) + err=ZIP_ERRNO; + + /* number of this disk */ + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream,&number_disk)!=ZIP_OK) + err=ZIP_ERRNO; + + /* number of the disk with the start of the central directory */ + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream,&number_disk_with_CD)!=ZIP_OK) + err=ZIP_ERRNO; + + /* total number of entries in the central dir on this disk */ + number_entry = 0; + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &uL)!=ZIP_OK) + err=ZIP_ERRNO; + else + number_entry = uL; + + /* total number of entries in the central dir */ + number_entry_CD = 0; + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &uL)!=ZIP_OK) + err=ZIP_ERRNO; + else + number_entry_CD = uL; + + if ((number_entry_CD!=number_entry) || (number_disk_with_CD!=0) || (number_disk!=0)) + err=ZIP_BADZIPFILE; + + /* size of the central directory */ + size_central_dir = 0; + if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream, &uL)!=ZIP_OK) + err=ZIP_ERRNO; + else + size_central_dir = uL; + + /* offset of start of central directory with respect to the starting disk number */ + offset_central_dir = 0; + if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream, &uL)!=ZIP_OK) + err=ZIP_ERRNO; + else + offset_central_dir = uL; + + + /* zipfile global comment length */ + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &size_comment)!=ZIP_OK) + err=ZIP_ERRNO; + } + + if ((central_posz_filefunc, pziinit->filestream); + return ZIP_ERRNO; + } + + if (size_comment>0) + { + pziinit->globalcomment = (char*)ALLOC(size_comment+1); + if (pziinit->globalcomment) + { + size_comment = ZREAD64(pziinit->z_filefunc, pziinit->filestream, pziinit->globalcomment,size_comment); + pziinit->globalcomment[size_comment]=0; + } + } + + byte_before_the_zipfile = central_pos - (offset_central_dir+size_central_dir); + pziinit->add_position_when_writting_offset = byte_before_the_zipfile; + + { + ZPOS64_T size_central_dir_to_read = size_central_dir; + size_t buf_size = SIZEDATA_INDATABLOCK; + void* buf_read = (void*)ALLOC(buf_size); + if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, offset_central_dir + byte_before_the_zipfile, ZLIB_FILEFUNC_SEEK_SET) != 0) + err=ZIP_ERRNO; + + while ((size_central_dir_to_read>0) && (err==ZIP_OK)) + { + ZPOS64_T read_this = SIZEDATA_INDATABLOCK; + if (read_this > size_central_dir_to_read) + read_this = size_central_dir_to_read; + + if (ZREAD64(pziinit->z_filefunc, pziinit->filestream,buf_read,(uLong)read_this) != read_this) + err=ZIP_ERRNO; + + if (err==ZIP_OK) + err = add_data_in_datablock(&pziinit->central_dir,buf_read, (uLong)read_this); + + size_central_dir_to_read-=read_this; + } + TRYFREE(buf_read); + } + pziinit->begin_pos = byte_before_the_zipfile; + pziinit->number_entry = number_entry_CD; + + if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, offset_central_dir+byte_before_the_zipfile,ZLIB_FILEFUNC_SEEK_SET) != 0) + err=ZIP_ERRNO; + + return err; +} + + +#endif /* !NO_ADDFILEINEXISTINGZIP*/ + + +/************************************************************/ +extern zipFile ZEXPORT zipOpen3 (const void *pathname, int append, zipcharpc* globalcomment, zlib_filefunc64_32_def* pzlib_filefunc64_32_def) +{ + zip64_internal ziinit; + zip64_internal* zi; + int err=ZIP_OK; + + ziinit.z_filefunc.zseek32_file = NULL; + ziinit.z_filefunc.ztell32_file = NULL; + if (pzlib_filefunc64_32_def==NULL) { +// fill_fopen64_filefunc(&ziinit.z_filefunc.zfile_func64); + } else + ziinit.z_filefunc = *pzlib_filefunc64_32_def; + + ziinit.filestream = ZOPEN64(ziinit.z_filefunc, + pathname, + (append == APPEND_STATUS_CREATE) ? + (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE | ZLIB_FILEFUNC_MODE_CREATE) : + (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE | ZLIB_FILEFUNC_MODE_EXISTING)); + + if (ziinit.filestream == NULL) + return NULL; + + if (append == APPEND_STATUS_CREATEAFTER) + ZSEEK64(ziinit.z_filefunc,ziinit.filestream,0,SEEK_END); + + ziinit.begin_pos = ZTELL64(ziinit.z_filefunc,ziinit.filestream); + ziinit.in_opened_file_inzip = 0; + ziinit.ci.stream_initialised = 0; + ziinit.number_entry = 0; + ziinit.add_position_when_writting_offset = 0; + init_linkedlist(&(ziinit.central_dir)); + + + + zi = (zip64_internal*)ALLOC(sizeof(zip64_internal)); + if (zi==NULL) + { + ZCLOSE64(ziinit.z_filefunc,ziinit.filestream); + return NULL; + } + + /* now we add file in a zipfile */ +# ifndef NO_ADDFILEINEXISTINGZIP + ziinit.globalcomment = NULL; + if (append == APPEND_STATUS_ADDINZIP) + { + // Read and Cache Central Directory Records + err = LoadCentralDirectoryRecord(&ziinit); + } + + if (globalcomment) + { + *globalcomment = ziinit.globalcomment; + } +# endif /* !NO_ADDFILEINEXISTINGZIP*/ + + if (err != ZIP_OK) + { +# ifndef NO_ADDFILEINEXISTINGZIP + TRYFREE(ziinit.globalcomment); +# endif /* !NO_ADDFILEINEXISTINGZIP*/ + TRYFREE(zi); + return NULL; + } + else + { + *zi = ziinit; + return (zipFile)zi; + } +} + +extern zipFile ZEXPORT zipOpen2 (const char *pathname, int append, zipcharpc* globalcomment, zlib_filefunc_def* pzlib_filefunc32_def) +{ + if (pzlib_filefunc32_def != NULL) + { + zlib_filefunc64_32_def zlib_filefunc64_32_def_fill; + fill_zlib_filefunc64_32_def_from_filefunc32(&zlib_filefunc64_32_def_fill,pzlib_filefunc32_def); + return zipOpen3(pathname, append, globalcomment, &zlib_filefunc64_32_def_fill); + } + else + return zipOpen3(pathname, append, globalcomment, NULL); +} + +extern zipFile ZEXPORT zipOpen2_64 (const void *pathname, int append, zipcharpc* globalcomment, zlib_filefunc64_def* pzlib_filefunc_def) +{ + if (pzlib_filefunc_def != NULL) + { + zlib_filefunc64_32_def zlib_filefunc64_32_def_fill; + zlib_filefunc64_32_def_fill.zfile_func64 = *pzlib_filefunc_def; + zlib_filefunc64_32_def_fill.ztell32_file = NULL; + zlib_filefunc64_32_def_fill.zseek32_file = NULL; + return zipOpen3(pathname, append, globalcomment, &zlib_filefunc64_32_def_fill); + } + else + return zipOpen3(pathname, append, globalcomment, NULL); +} + + + +extern zipFile ZEXPORT zipOpen (const char* pathname, int append) +{ + return zipOpen3((const void*)pathname,append,NULL,NULL); +} + +extern zipFile ZEXPORT zipOpen64 (const void* pathname, int append) +{ + return zipOpen3(pathname,append,NULL,NULL); +} + +int Write_LocalFileHeader(zip64_internal* zi, const char* filename, uInt size_extrafield_local, const void* extrafield_local) +{ + /* write the local header */ + int err; + uInt size_filename = (uInt)strlen(filename); + uInt size_extrafield = size_extrafield_local; + + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)LOCALHEADERMAGIC, 4); + + if (err==ZIP_OK) + { + if(zi->ci.zip64) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)45,2);/* version needed to extract */ + else + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)20,2);/* version needed to extract */ + } + + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.flag,2); + + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.method,2); + + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.dosDate,4); + + // CRC / Compressed size / Uncompressed size will be filled in later and rewritten later + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* crc 32, unknown */ + if (err==ZIP_OK) + { + if(zi->ci.zip64) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0xFFFFFFFF,4); /* compressed size, unknown */ + else + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* compressed size, unknown */ + } + if (err==ZIP_OK) + { + if(zi->ci.zip64) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0xFFFFFFFF,4); /* uncompressed size, unknown */ + else + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* uncompressed size, unknown */ + } + + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_filename,2); + + if(zi->ci.zip64) + { + size_extrafield += 20; + } + + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_extrafield,2); + + if ((err==ZIP_OK) && (size_filename > 0)) + { + if (ZWRITE64(zi->z_filefunc,zi->filestream,filename,size_filename)!=size_filename) + err = ZIP_ERRNO; + } + + if ((err==ZIP_OK) && (size_extrafield_local > 0)) + { + if (ZWRITE64(zi->z_filefunc, zi->filestream, extrafield_local, size_extrafield_local) != size_extrafield_local) + err = ZIP_ERRNO; + } + + + if ((err==ZIP_OK) && (zi->ci.zip64)) + { + // write the Zip64 extended info + short HeaderID = 1; + short DataSize = 16; + ZPOS64_T CompressedSize = 0; + ZPOS64_T UncompressedSize = 0; + + // Remember position of Zip64 extended info for the local file header. (needed when we update size after done with file) + zi->ci.pos_zip64extrainfo = ZTELL64(zi->z_filefunc,zi->filestream); + + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (short)HeaderID,2); + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (short)DataSize,2); + + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (ZPOS64_T)UncompressedSize,8); + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (ZPOS64_T)CompressedSize,8); + } + + return err; +} + +/* + NOTE. + When writing RAW the ZIP64 extended information in extrafield_local and extrafield_global needs to be stripped + before calling this function it can be done with zipRemoveExtraInfoBlock + + It is not done here because then we need to realloc a new buffer since parameters are 'const' and I want to minimize + unnecessary allocations. + */ +extern int ZEXPORT zipOpenNewFileInZip4_64 (zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw, + int windowBits,int memLevel, int strategy, + const char* password, uLong crcForCrypting, + uLong versionMadeBy, uLong flagBase, int zip64) +{ + zip64_internal* zi; + uInt size_filename; + uInt size_comment; + uInt i; + int err = ZIP_OK; + +# ifdef NOCRYPT + if (password != NULL) + return ZIP_PARAMERROR; +# endif + + if (file == NULL) + return ZIP_PARAMERROR; + +#ifdef HAVE_BZIP2 + if ((method!=0) && (method!=Z_DEFLATED) && (method!=Z_BZIP2ED)) + return ZIP_PARAMERROR; +#else + if ((method!=0) && (method!=Z_DEFLATED)) + return ZIP_PARAMERROR; +#endif + + zi = (zip64_internal*)file; + + if (zi->in_opened_file_inzip == 1) + { + err = zipCloseFileInZip (file); + if (err != ZIP_OK) + return err; + } + + if (filename==NULL) + filename="-"; + + if (comment==NULL) + size_comment = 0; + else + size_comment = (uInt)strlen(comment); + + size_filename = (uInt)strlen(filename); + + if (zipfi == NULL) + zi->ci.dosDate = 0; + else + { + if (zipfi->dosDate != 0) + zi->ci.dosDate = zipfi->dosDate; + else + zi->ci.dosDate = zip64local_TmzDateToDosDate(&zipfi->tmz_date); + } + + zi->ci.flag = flagBase; + if ((level==8) || (level==9)) + zi->ci.flag |= 2; + if ((level==2)) + zi->ci.flag |= 4; + if ((level==1)) + zi->ci.flag |= 6; + if (password != NULL) + zi->ci.flag |= 1; + + zi->ci.crc32 = 0; + zi->ci.method = method; + zi->ci.encrypt = 0; + zi->ci.stream_initialised = 0; + zi->ci.pos_in_buffered_data = 0; + zi->ci.raw = raw; + zi->ci.pos_local_header = ZTELL64(zi->z_filefunc,zi->filestream); + + zi->ci.size_centralheader = SIZECENTRALHEADER + size_filename + size_extrafield_global + size_comment; + zi->ci.size_centralExtraFree = 32; // Extra space we have reserved in case we need to add ZIP64 extra info data + + zi->ci.central_header = (char*)ALLOC((uInt)zi->ci.size_centralheader + zi->ci.size_centralExtraFree); + + zi->ci.size_centralExtra = size_extrafield_global; + zip64local_putValue_inmemory(zi->ci.central_header,(uLong)CENTRALHEADERMAGIC,4); + /* version info */ + zip64local_putValue_inmemory(zi->ci.central_header+4,(uLong)versionMadeBy,2); + zip64local_putValue_inmemory(zi->ci.central_header+6,(uLong)20,2); + zip64local_putValue_inmemory(zi->ci.central_header+8,(uLong)zi->ci.flag,2); + zip64local_putValue_inmemory(zi->ci.central_header+10,(uLong)zi->ci.method,2); + zip64local_putValue_inmemory(zi->ci.central_header+12,(uLong)zi->ci.dosDate,4); + zip64local_putValue_inmemory(zi->ci.central_header+16,(uLong)0,4); /*crc*/ + zip64local_putValue_inmemory(zi->ci.central_header+20,(uLong)0,4); /*compr size*/ + zip64local_putValue_inmemory(zi->ci.central_header+24,(uLong)0,4); /*uncompr size*/ + zip64local_putValue_inmemory(zi->ci.central_header+28,(uLong)size_filename,2); + zip64local_putValue_inmemory(zi->ci.central_header+30,(uLong)size_extrafield_global,2); + zip64local_putValue_inmemory(zi->ci.central_header+32,(uLong)size_comment,2); + zip64local_putValue_inmemory(zi->ci.central_header+34,(uLong)0,2); /*disk nm start*/ + + if (zipfi==NULL) + zip64local_putValue_inmemory(zi->ci.central_header+36,(uLong)0,2); + else + zip64local_putValue_inmemory(zi->ci.central_header+36,(uLong)zipfi->internal_fa,2); + + if (zipfi==NULL) + zip64local_putValue_inmemory(zi->ci.central_header+38,(uLong)0,4); + else + zip64local_putValue_inmemory(zi->ci.central_header+38,(uLong)zipfi->external_fa,4); + + if(zi->ci.pos_local_header >= 0xffffffff) + zip64local_putValue_inmemory(zi->ci.central_header+42,(uLong)0xffffffff,4); + else + zip64local_putValue_inmemory(zi->ci.central_header+42,(uLong)zi->ci.pos_local_header - zi->add_position_when_writting_offset,4); + + for (i=0;ici.central_header+SIZECENTRALHEADER+i) = *(filename+i); + + for (i=0;ici.central_header+SIZECENTRALHEADER+size_filename+i) = + *(((const char*)extrafield_global)+i); + + for (i=0;ici.central_header+SIZECENTRALHEADER+size_filename+ + size_extrafield_global+i) = *(comment+i); + if (zi->ci.central_header == NULL) + return ZIP_INTERNALERROR; + + zi->ci.zip64 = zip64; + zi->ci.totalCompressedData = 0; + zi->ci.totalUncompressedData = 0; + zi->ci.pos_zip64extrainfo = 0; + + err = Write_LocalFileHeader(zi, filename, size_extrafield_local, extrafield_local); + +#ifdef HAVE_BZIP2 + zi->ci.bstream.avail_in = (uInt)0; + zi->ci.bstream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.bstream.next_out = (char*)zi->ci.buffered_data; + zi->ci.bstream.total_in_hi32 = 0; + zi->ci.bstream.total_in_lo32 = 0; + zi->ci.bstream.total_out_hi32 = 0; + zi->ci.bstream.total_out_lo32 = 0; +#endif + + zi->ci.stream.avail_in = (uInt)0; + zi->ci.stream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.stream.next_out = zi->ci.buffered_data; + zi->ci.stream.total_in = 0; + zi->ci.stream.total_out = 0; + zi->ci.stream.data_type = Z_BINARY; + +#ifdef HAVE_BZIP2 + if ((err==ZIP_OK) && (zi->ci.method == Z_DEFLATED || zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw)) +#else + if ((err==ZIP_OK) && (zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) +#endif + { + if(zi->ci.method == Z_DEFLATED) + { + zi->ci.stream.zalloc = (alloc_func)0; + zi->ci.stream.zfree = (free_func)0; + zi->ci.stream.opaque = (voidpf)0; + + if (windowBits>0) + windowBits = -windowBits; + + err = deflateInit2(&zi->ci.stream, level, Z_DEFLATED, windowBits, memLevel, strategy); + + if (err==Z_OK) + zi->ci.stream_initialised = Z_DEFLATED; + } + else if(zi->ci.method == Z_BZIP2ED) + { +#ifdef HAVE_BZIP2 + // Init BZip stuff here + zi->ci.bstream.bzalloc = 0; + zi->ci.bstream.bzfree = 0; + zi->ci.bstream.opaque = (voidpf)0; + + err = BZ2_bzCompressInit(&zi->ci.bstream, level, 0,35); + if(err == BZ_OK) + zi->ci.stream_initialised = Z_BZIP2ED; +#endif + } + + } + +# ifndef NOCRYPT + zi->ci.crypt_header_size = 0; + if ((err==Z_OK) && (password != NULL)) + { + unsigned char bufHead[RAND_HEAD_LEN]; + unsigned int sizeHead; + zi->ci.encrypt = 1; + zi->ci.pcrc_32_tab = get_crc_table(); + /*init_keys(password,zi->ci.keys,zi->ci.pcrc_32_tab);*/ + + sizeHead=crypthead(password,bufHead,RAND_HEAD_LEN,zi->ci.keys,zi->ci.pcrc_32_tab,crcForCrypting); + zi->ci.crypt_header_size = sizeHead; + + if (ZWRITE64(zi->z_filefunc,zi->filestream,bufHead,sizeHead) != sizeHead) + err = ZIP_ERRNO; + } +# endif + + if (err==Z_OK) + zi->in_opened_file_inzip = 1; + return err; +} + +extern int ZEXPORT zipOpenNewFileInZip4 (zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw, + int windowBits,int memLevel, int strategy, + const char* password, uLong crcForCrypting, + uLong versionMadeBy, uLong flagBase) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + windowBits, memLevel, strategy, + password, crcForCrypting, versionMadeBy, flagBase, 0); +} + +extern int ZEXPORT zipOpenNewFileInZip3 (zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw, + int windowBits,int memLevel, int strategy, + const char* password, uLong crcForCrypting) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + windowBits, memLevel, strategy, + password, crcForCrypting, VERSIONMADEBY, 0, 0); +} + +extern int ZEXPORT zipOpenNewFileInZip3_64(zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw, + int windowBits,int memLevel, int strategy, + const char* password, uLong crcForCrypting, int zip64) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + windowBits, memLevel, strategy, + password, crcForCrypting, VERSIONMADEBY, 0, zip64); +} + +extern int ZEXPORT zipOpenNewFileInZip2(zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + NULL, 0, VERSIONMADEBY, 0, 0); +} + +extern int ZEXPORT zipOpenNewFileInZip2_64(zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw, int zip64) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + NULL, 0, VERSIONMADEBY, 0, zip64); +} + +extern int ZEXPORT zipOpenNewFileInZip64 (zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void*extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int zip64) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, 0, + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + NULL, 0, VERSIONMADEBY, 0, zip64); +} + +extern int ZEXPORT zipOpenNewFileInZip (zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void*extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, 0, + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + NULL, 0, VERSIONMADEBY, 0, 0); +} + +local int zip64FlushWriteBuffer(zip64_internal* zi) +{ + int err=ZIP_OK; + + if (zi->ci.encrypt != 0) + { +#ifndef NOCRYPT + uInt i; + int t; + for (i=0;ici.pos_in_buffered_data;i++) + zi->ci.buffered_data[i] = zencode(zi->ci.keys, zi->ci.pcrc_32_tab, zi->ci.buffered_data[i],t); +#endif + } + + if (ZWRITE64(zi->z_filefunc,zi->filestream,zi->ci.buffered_data,zi->ci.pos_in_buffered_data) != zi->ci.pos_in_buffered_data) + err = ZIP_ERRNO; + + zi->ci.totalCompressedData += zi->ci.pos_in_buffered_data; + +#ifdef HAVE_BZIP2 + if(zi->ci.method == Z_BZIP2ED) + { + zi->ci.totalUncompressedData += zi->ci.bstream.total_in_lo32; + zi->ci.bstream.total_in_lo32 = 0; + zi->ci.bstream.total_in_hi32 = 0; + } + else +#endif + { + zi->ci.totalUncompressedData += zi->ci.stream.total_in; + zi->ci.stream.total_in = 0; + } + + + zi->ci.pos_in_buffered_data = 0; + + return err; +} + +extern int ZEXPORT zipWriteInFileInZip (zipFile file,const void* buf,unsigned int len) +{ + zip64_internal* zi; + int err=ZIP_OK; + + if (file == NULL) + return ZIP_PARAMERROR; + zi = (zip64_internal*)file; + + if (zi->in_opened_file_inzip == 0) + return ZIP_PARAMERROR; + + zi->ci.crc32 = crc32(zi->ci.crc32,buf,(uInt)len); + +#ifdef HAVE_BZIP2 + if(zi->ci.method == Z_BZIP2ED && (!zi->ci.raw)) + { + zi->ci.bstream.next_in = (void*)buf; + zi->ci.bstream.avail_in = len; + err = BZ_RUN_OK; + + while ((err==BZ_RUN_OK) && (zi->ci.bstream.avail_in>0)) + { + if (zi->ci.bstream.avail_out == 0) + { + if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO) + err = ZIP_ERRNO; + zi->ci.bstream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.bstream.next_out = (char*)zi->ci.buffered_data; + } + + + if(err != BZ_RUN_OK) + break; + + if ((zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw)) + { + uLong uTotalOutBefore_lo = zi->ci.bstream.total_out_lo32; +// uLong uTotalOutBefore_hi = zi->ci.bstream.total_out_hi32; + err=BZ2_bzCompress(&zi->ci.bstream, BZ_RUN); + + zi->ci.pos_in_buffered_data += (uInt)(zi->ci.bstream.total_out_lo32 - uTotalOutBefore_lo) ; + } + } + + if(err == BZ_RUN_OK) + err = ZIP_OK; + } + else +#endif + { + zi->ci.stream.next_in = (Bytef*)buf; + zi->ci.stream.avail_in = len; + + while ((err==ZIP_OK) && (zi->ci.stream.avail_in>0)) + { + if (zi->ci.stream.avail_out == 0) + { + if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO) + err = ZIP_ERRNO; + zi->ci.stream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.stream.next_out = zi->ci.buffered_data; + } + + + if(err != ZIP_OK) + break; + + if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) + { + uLong uTotalOutBefore = zi->ci.stream.total_out; + err=deflate(&zi->ci.stream, Z_NO_FLUSH); + if(uTotalOutBefore > zi->ci.stream.total_out) + { + int bBreak = 0; + bBreak++; + } + + zi->ci.pos_in_buffered_data += (uInt)(zi->ci.stream.total_out - uTotalOutBefore) ; + } + else + { + uInt copy_this,i; + if (zi->ci.stream.avail_in < zi->ci.stream.avail_out) + copy_this = zi->ci.stream.avail_in; + else + copy_this = zi->ci.stream.avail_out; + + for (i = 0; i < copy_this; i++) + *(((char*)zi->ci.stream.next_out)+i) = + *(((const char*)zi->ci.stream.next_in)+i); + { + zi->ci.stream.avail_in -= copy_this; + zi->ci.stream.avail_out-= copy_this; + zi->ci.stream.next_in+= copy_this; + zi->ci.stream.next_out+= copy_this; + zi->ci.stream.total_in+= copy_this; + zi->ci.stream.total_out+= copy_this; + zi->ci.pos_in_buffered_data += copy_this; + } + } + }// while(...) + } + + return err; +} + +extern int ZEXPORT zipCloseFileInZipRaw (zipFile file, uLong uncompressed_size, uLong crc32) +{ + return zipCloseFileInZipRaw64 (file, uncompressed_size, crc32); +} + +extern int ZEXPORT zipCloseFileInZipRaw64 (zipFile file, ZPOS64_T uncompressed_size, uLong crc32) +{ + zip64_internal* zi; + ZPOS64_T compressed_size; + uLong invalidValue = 0xffffffff; + short datasize = 0; + int err=ZIP_OK; + + if (file == NULL) + return ZIP_PARAMERROR; + zi = (zip64_internal*)file; + + if (zi->in_opened_file_inzip == 0) + return ZIP_PARAMERROR; + zi->ci.stream.avail_in = 0; + + if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) + { + while (err==ZIP_OK) + { + uLong uTotalOutBefore; + if (zi->ci.stream.avail_out == 0) + { + if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO) + err = ZIP_ERRNO; + zi->ci.stream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.stream.next_out = zi->ci.buffered_data; + } + uTotalOutBefore = zi->ci.stream.total_out; + err=deflate(&zi->ci.stream, Z_FINISH); + zi->ci.pos_in_buffered_data += (uInt)(zi->ci.stream.total_out - uTotalOutBefore) ; + } + } + else if ((zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw)) + { +#ifdef HAVE_BZIP2 + err = BZ_FINISH_OK; + while (err==BZ_FINISH_OK) + { + uLong uTotalOutBefore; + if (zi->ci.bstream.avail_out == 0) + { + if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO) + err = ZIP_ERRNO; + zi->ci.bstream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.bstream.next_out = (char*)zi->ci.buffered_data; + } + uTotalOutBefore = zi->ci.bstream.total_out_lo32; + err=BZ2_bzCompress(&zi->ci.bstream, BZ_FINISH); + if(err == BZ_STREAM_END) + err = Z_STREAM_END; + + zi->ci.pos_in_buffered_data += (uInt)(zi->ci.bstream.total_out_lo32 - uTotalOutBefore); + } + + if(err == BZ_FINISH_OK) + err = ZIP_OK; +#endif + } + + if (err==Z_STREAM_END) + err=ZIP_OK; /* this is normal */ + + if ((zi->ci.pos_in_buffered_data>0) && (err==ZIP_OK)) + { + if (zip64FlushWriteBuffer(zi)==ZIP_ERRNO) + err = ZIP_ERRNO; + } + + if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) + { + int tmp_err = deflateEnd(&zi->ci.stream); + if (err == ZIP_OK) + err = tmp_err; + zi->ci.stream_initialised = 0; + } +#ifdef HAVE_BZIP2 + else if((zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw)) + { + int tmperr = BZ2_bzCompressEnd(&zi->ci.bstream); + if (err==ZIP_OK) + err = tmperr; + zi->ci.stream_initialised = 0; + } +#endif + + if (!zi->ci.raw) + { + crc32 = (uLong)zi->ci.crc32; + uncompressed_size = zi->ci.totalUncompressedData; + } + compressed_size = zi->ci.totalCompressedData; + +# ifndef NOCRYPT + compressed_size += zi->ci.crypt_header_size; +# endif + + // update Current Item crc and sizes, + if(compressed_size >= 0xffffffff || uncompressed_size >= 0xffffffff || zi->ci.pos_local_header >= 0xffffffff) + { + /*version Made by*/ + zip64local_putValue_inmemory(zi->ci.central_header+4,(uLong)45,2); + /*version needed*/ + zip64local_putValue_inmemory(zi->ci.central_header+6,(uLong)45,2); + + } + + zip64local_putValue_inmemory(zi->ci.central_header+16,crc32,4); /*crc*/ + + + if(compressed_size >= 0xffffffff) + zip64local_putValue_inmemory(zi->ci.central_header+20, invalidValue,4); /*compr size*/ + else + zip64local_putValue_inmemory(zi->ci.central_header+20, compressed_size,4); /*compr size*/ + + /// set internal file attributes field + if (zi->ci.stream.data_type == Z_ASCII) + zip64local_putValue_inmemory(zi->ci.central_header+36,(uLong)Z_ASCII,2); + + if(uncompressed_size >= 0xffffffff) + zip64local_putValue_inmemory(zi->ci.central_header+24, invalidValue,4); /*uncompr size*/ + else + zip64local_putValue_inmemory(zi->ci.central_header+24, uncompressed_size,4); /*uncompr size*/ + + // Add ZIP64 extra info field for uncompressed size + if(uncompressed_size >= 0xffffffff) + datasize += 8; + + // Add ZIP64 extra info field for compressed size + if(compressed_size >= 0xffffffff) + datasize += 8; + + // Add ZIP64 extra info field for relative offset to local file header of current file + if(zi->ci.pos_local_header >= 0xffffffff) + datasize += 8; + + if(datasize > 0) + { + char* p = NULL; + + if((uLong)(datasize + 4) > zi->ci.size_centralExtraFree) + { + // we can not write more data to the buffer that we have room for. + return ZIP_BADZIPFILE; + } + + p = zi->ci.central_header + zi->ci.size_centralheader; + + // Add Extra Information Header for 'ZIP64 information' + zip64local_putValue_inmemory(p, 0x0001, 2); // HeaderID + p += 2; + zip64local_putValue_inmemory(p, datasize, 2); // DataSize + p += 2; + + if(uncompressed_size >= 0xffffffff) + { + zip64local_putValue_inmemory(p, uncompressed_size, 8); + p += 8; + } + + if(compressed_size >= 0xffffffff) + { + zip64local_putValue_inmemory(p, compressed_size, 8); + p += 8; + } + + if(zi->ci.pos_local_header >= 0xffffffff) + { + zip64local_putValue_inmemory(p, zi->ci.pos_local_header, 8); + p += 8; + } + + // Update how much extra free space we got in the memory buffer + // and increase the centralheader size so the new ZIP64 fields are included + // ( 4 below is the size of HeaderID and DataSize field ) + zi->ci.size_centralExtraFree -= datasize + 4; + zi->ci.size_centralheader += datasize + 4; + + // Update the extra info size field + zi->ci.size_centralExtra += datasize + 4; + zip64local_putValue_inmemory(zi->ci.central_header+30,(uLong)zi->ci.size_centralExtra,2); + } + + if (err==ZIP_OK) + err = add_data_in_datablock(&zi->central_dir, zi->ci.central_header, (uLong)zi->ci.size_centralheader); + + free(zi->ci.central_header); + + if (err==ZIP_OK) + { + // Update the LocalFileHeader with the new values. + + ZPOS64_T cur_pos_inzip = ZTELL64(zi->z_filefunc,zi->filestream); + + if (ZSEEK64(zi->z_filefunc,zi->filestream, zi->ci.pos_local_header + 14,ZLIB_FILEFUNC_SEEK_SET)!=0) + err = ZIP_ERRNO; + + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,crc32,4); /* crc 32, unknown */ + + if(uncompressed_size >= 0xffffffff) + { + if(zi->ci.pos_zip64extrainfo > 0) + { + // Update the size in the ZIP64 extended field. + if (ZSEEK64(zi->z_filefunc,zi->filestream, zi->ci.pos_zip64extrainfo + 4,ZLIB_FILEFUNC_SEEK_SET)!=0) + err = ZIP_ERRNO; + + if (err==ZIP_OK) /* compressed size, unknown */ + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, uncompressed_size, 8); + + if (err==ZIP_OK) /* uncompressed size, unknown */ + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, compressed_size, 8); + } + } + else + { + if (err==ZIP_OK) /* compressed size, unknown */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,compressed_size,4); + + if (err==ZIP_OK) /* uncompressed size, unknown */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,uncompressed_size,4); + } + + if (ZSEEK64(zi->z_filefunc,zi->filestream, cur_pos_inzip,ZLIB_FILEFUNC_SEEK_SET)!=0) + err = ZIP_ERRNO; + } + + zi->number_entry ++; + zi->in_opened_file_inzip = 0; + + return err; +} + +extern int ZEXPORT zipCloseFileInZip (zipFile file) +{ + return zipCloseFileInZipRaw (file,0,0); +} + +int Write_Zip64EndOfCentralDirectoryLocator(zip64_internal* zi, ZPOS64_T zip64eocd_pos_inzip) +{ + int err = ZIP_OK; + ZPOS64_T pos = zip64eocd_pos_inzip - zi->add_position_when_writting_offset; + + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)ZIP64ENDLOCHEADERMAGIC,4); + + /*num disks*/ + if (err==ZIP_OK) /* number of the disk with the start of the central directory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); + + /*relative offset*/ + if (err==ZIP_OK) /* Relative offset to the Zip64EndOfCentralDirectory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream, pos,8); + + /*total disks*/ /* Do not support spawning of disk so always say 1 here*/ + if (err==ZIP_OK) /* number of the disk with the start of the central directory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)1,4); + + return err; +} + +int Write_Zip64EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centraldir, ZPOS64_T centraldir_pos_inzip) +{ + int err = ZIP_OK; + + uLong Zip64DataSize = 44; + + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)ZIP64ENDHEADERMAGIC,4); + + if (err==ZIP_OK) /* size of this 'zip64 end of central directory' */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(ZPOS64_T)Zip64DataSize,8); // why ZPOS64_T of this ? + + if (err==ZIP_OK) /* version made by */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)45,2); + + if (err==ZIP_OK) /* version needed */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)45,2); + + if (err==ZIP_OK) /* number of this disk */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); + + if (err==ZIP_OK) /* number of the disk with the start of the central directory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); + + if (err==ZIP_OK) /* total number of entries in the central dir on this disk */ + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, zi->number_entry, 8); + + if (err==ZIP_OK) /* total number of entries in the central dir */ + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, zi->number_entry, 8); + + if (err==ZIP_OK) /* size of the central directory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(ZPOS64_T)size_centraldir,8); + + if (err==ZIP_OK) /* offset of start of central directory with respect to the starting disk number */ + { + ZPOS64_T pos = centraldir_pos_inzip - zi->add_position_when_writting_offset; + err = zip64local_putValue(&zi->z_filefunc,zi->filestream, (ZPOS64_T)pos,8); + } + return err; +} +int Write_EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centraldir, ZPOS64_T centraldir_pos_inzip) +{ + int err = ZIP_OK; + + /*signature*/ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)ENDHEADERMAGIC,4); + + if (err==ZIP_OK) /* number of this disk */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,2); + + if (err==ZIP_OK) /* number of the disk with the start of the central directory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,2); + + if (err==ZIP_OK) /* total number of entries in the central dir on this disk */ + { + { + if(zi->number_entry >= 0xFFFF) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0xffff,2); // use value in ZIP64 record + else + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->number_entry,2); + } + } + + if (err==ZIP_OK) /* total number of entries in the central dir */ + { + if(zi->number_entry >= 0xFFFF) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0xffff,2); // use value in ZIP64 record + else + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->number_entry,2); + } + + if (err==ZIP_OK) /* size of the central directory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_centraldir,4); + + if (err==ZIP_OK) /* offset of start of central directory with respect to the starting disk number */ + { + ZPOS64_T pos = centraldir_pos_inzip - zi->add_position_when_writting_offset; + if(pos >= 0xffffffff) + { + err = zip64local_putValue(&zi->z_filefunc,zi->filestream, (uLong)0xffffffff,4); + } + else + err = zip64local_putValue(&zi->z_filefunc,zi->filestream, (uLong)(centraldir_pos_inzip - zi->add_position_when_writting_offset),4); + } + + return err; +} + +int Write_GlobalComment(zip64_internal* zi, const char* global_comment) +{ + int err = ZIP_OK; + uInt size_global_comment = 0; + + if(global_comment != NULL) + size_global_comment = (uInt)strlen(global_comment); + + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_global_comment,2); + + if (err == ZIP_OK && size_global_comment > 0) + { + if (ZWRITE64(zi->z_filefunc,zi->filestream, global_comment, size_global_comment) != size_global_comment) + err = ZIP_ERRNO; + } + return err; +} + +extern int ZEXPORT zipClose (zipFile file, const char* global_comment) +{ + zip64_internal* zi; + int err = 0; + uLong size_centraldir = 0; + ZPOS64_T centraldir_pos_inzip; + ZPOS64_T pos; + + if (file == NULL) + return ZIP_PARAMERROR; + + zi = (zip64_internal*)file; + + if (zi->in_opened_file_inzip == 1) + { + err = zipCloseFileInZip (file); + } + +#ifndef NO_ADDFILEINEXISTINGZIP + if (global_comment==NULL) + global_comment = zi->globalcomment; +#endif + + centraldir_pos_inzip = ZTELL64(zi->z_filefunc,zi->filestream); + + if (err==ZIP_OK) + { + linkedlist_datablock_internal* ldi = zi->central_dir.first_block; + while (ldi!=NULL) + { + if ((err==ZIP_OK) && (ldi->filled_in_this_block>0)) + { + if (ZWRITE64(zi->z_filefunc,zi->filestream, ldi->data, ldi->filled_in_this_block) != ldi->filled_in_this_block) + err = ZIP_ERRNO; + } + + size_centraldir += ldi->filled_in_this_block; + ldi = ldi->next_datablock; + } + } + free_linkedlist(&(zi->central_dir)); + + pos = centraldir_pos_inzip - zi->add_position_when_writting_offset; + if(pos >= 0xffffffff) + { + ZPOS64_T Zip64EOCDpos = ZTELL64(zi->z_filefunc,zi->filestream); + Write_Zip64EndOfCentralDirectoryRecord(zi, size_centraldir, centraldir_pos_inzip); + + Write_Zip64EndOfCentralDirectoryLocator(zi, Zip64EOCDpos); + } + + if (err==ZIP_OK) + err = Write_EndOfCentralDirectoryRecord(zi, size_centraldir, centraldir_pos_inzip); + + if(err == ZIP_OK) + err = Write_GlobalComment(zi, global_comment); + + if (ZCLOSE64(zi->z_filefunc,zi->filestream) != 0) + if (err == ZIP_OK) + err = ZIP_ERRNO; + +#ifndef NO_ADDFILEINEXISTINGZIP + TRYFREE(zi->globalcomment); +#endif + TRYFREE(zi); + + return err; +} + +extern int ZEXPORT zipRemoveExtraInfoBlock (char* pData, int* dataLen, short sHeader) +{ + char* p = pData; + int size = 0; + char* pNewHeader; + char* pTmp; + short header; + short dataSize; + + int retVal = ZIP_OK; + + if(pData == NULL || *dataLen < 4) + return ZIP_PARAMERROR; + + pNewHeader = (char*)ALLOC(*dataLen); + pTmp = pNewHeader; + + while(p < (pData + *dataLen)) + { + header = *(short*)p; + dataSize = *(((short*)p)+1); + + if( header == sHeader ) // Header found. + { + p += dataSize + 4; // skip it. do not copy to temp buffer + } + else + { + // Extra Info block should not be removed, So copy it to the temp buffer. + memcpy(pTmp, p, dataSize + 4); + p += dataSize + 4; + size += dataSize + 4; + } + + } + + if(size < *dataLen) + { + // clean old extra info block. + memset(pData,0, *dataLen); + + // copy the new extra info block over the old + if(size > 0) + memcpy(pData, pNewHeader, size); + + // set the new extra info size + *dataLen = size; + + retVal = ZIP_OK; + } + else + retVal = ZIP_ERRNO; + + TRYFREE(pNewHeader); + + return retVal; +} diff --git a/core/io/zip.h b/core/io/zip.h new file mode 100644 index 00000000000..3c723f093d2 --- /dev/null +++ b/core/io/zip.h @@ -0,0 +1,361 @@ +/* Version 1.1, February 14h, 2010 + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications for Zip64 support + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + + --------------------------------------------------------------------------- + + Condition of use and distribution are the same than zlib : + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + --------------------------------------------------------------------------- + + Changes + + See header of zip.h + +*/ + +#ifndef _zip12_H +#define _zip12_H + +#ifdef __cplusplus +extern "C" { +#endif + +//#define HAVE_BZIP2 + +#ifndef _ZLIB_H +#include "zlib.h" +#endif + +#ifndef _ZLIBIOAPI_H +#include "ioapi.h" +#endif + +#ifdef HAVE_BZIP2 +#include "bzlib.h" +#endif + +#define Z_BZIP2ED 12 + +#if defined(STRICTZIP) || defined(STRICTZIPUNZIP) +/* like the STRICT of WIN32, we define a pointer that cannot be converted + from (void*) without cast */ +typedef struct TagzipFile__ { int unused; } zipFile__; +typedef zipFile__ *zipFile; +#else +typedef voidp zipFile; +#endif + +#define ZIP_OK (0) +#define ZIP_EOF (0) +#define ZIP_ERRNO (Z_ERRNO) +#define ZIP_PARAMERROR (-102) +#define ZIP_BADZIPFILE (-103) +#define ZIP_INTERNALERROR (-104) + +#ifndef DEF_MEM_LEVEL +# if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +# else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +# endif +#endif +/* default memLevel */ + +/* tm_zip contain date/time info */ +typedef struct tm_zip_s +{ + uInt tm_sec; /* seconds after the minute - [0,59] */ + uInt tm_min; /* minutes after the hour - [0,59] */ + uInt tm_hour; /* hours since midnight - [0,23] */ + uInt tm_mday; /* day of the month - [1,31] */ + uInt tm_mon; /* months since January - [0,11] */ + uInt tm_year; /* years - [1980..2044] */ +} tm_zip; + +typedef struct +{ + tm_zip tmz_date; /* date in understandable format */ + uLong dosDate; /* if dos_date == 0, tmu_date is used */ +/* uLong flag; */ /* general purpose bit flag 2 bytes */ + + uLong internal_fa; /* internal file attributes 2 bytes */ + uLong external_fa; /* external file attributes 4 bytes */ +} zip_fileinfo; + +typedef const char* zipcharpc; + + +#define APPEND_STATUS_CREATE (0) +#define APPEND_STATUS_CREATEAFTER (1) +#define APPEND_STATUS_ADDINZIP (2) + +extern zipFile ZEXPORT zipOpen OF((const char *pathname, int append)); +extern zipFile ZEXPORT zipOpen64 OF((const void *pathname, int append)); +/* + Create a zipfile. + pathname contain on Windows XP a filename like "c:\\zlib\\zlib113.zip" or on + an Unix computer "zlib/zlib113.zip". + if the file pathname exist and append==APPEND_STATUS_CREATEAFTER, the zip + will be created at the end of the file. + (useful if the file contain a self extractor code) + if the file pathname exist and append==APPEND_STATUS_ADDINZIP, we will + add files in existing zip (be sure you don't add file that doesn't exist) + If the zipfile cannot be opened, the return value is NULL. + Else, the return value is a zipFile Handle, usable with other function + of this zip package. +*/ + +/* Note : there is no delete function into a zipfile. + If you want delete file into a zipfile, you must open a zipfile, and create another + Of couse, you can use RAW reading and writing to copy the file you did not want delte +*/ + +extern zipFile ZEXPORT zipOpen2 OF((const char *pathname, + int append, + zipcharpc* globalcomment, + zlib_filefunc_def* pzlib_filefunc_def)); + +extern zipFile ZEXPORT zipOpen2_64 OF((const void *pathname, + int append, + zipcharpc* globalcomment, + zlib_filefunc64_def* pzlib_filefunc_def)); + +extern int ZEXPORT zipOpenNewFileInZip OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level)); + +extern int ZEXPORT zipOpenNewFileInZip64 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int zip64)); + +/* + Open a file in the ZIP for writing. + filename : the filename in zip (if NULL, '-' without quote will be used + *zipfi contain supplemental information + if extrafield_local!=NULL and size_extrafield_local>0, extrafield_local + contains the extrafield data the the local header + if extrafield_global!=NULL and size_extrafield_global>0, extrafield_global + contains the extrafield data the the local header + if comment != NULL, comment contain the comment string + method contain the compression method (0 for store, Z_DEFLATED for deflate) + level contain the level of compression (can be Z_DEFAULT_COMPRESSION) + zip64 is set to 1 if a zip64 extended information block should be added to the local file header. + this MUST be '1' if the uncompressed size is >= 0xffffffff. + +*/ + + +extern int ZEXPORT zipOpenNewFileInZip2 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw)); + + +extern int ZEXPORT zipOpenNewFileInZip2_64 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int zip64)); +/* + Same than zipOpenNewFileInZip, except if raw=1, we write raw file + */ + +extern int ZEXPORT zipOpenNewFileInZip3 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCrypting)); + +extern int ZEXPORT zipOpenNewFileInZip3_64 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCrypting, + int zip64 + )); + +/* + Same than zipOpenNewFileInZip2, except + windowBits,memLevel,,strategy : see parameter strategy in deflateInit2 + password : crypting password (NULL for no crypting) + crcForCrypting : crc of file to compress (needed for crypting) + */ + +extern int ZEXPORT zipOpenNewFileInZip4 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCrypting, + uLong versionMadeBy, + uLong flagBase + )); + + +extern int ZEXPORT zipOpenNewFileInZip4_64 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCrypting, + uLong versionMadeBy, + uLong flagBase, + int zip64 + )); +/* + Same than zipOpenNewFileInZip4, except + versionMadeBy : value for Version made by field + flag : value for flag field (compression level info will be added) + */ + + +extern int ZEXPORT zipWriteInFileInZip OF((zipFile file, + const void* buf, + unsigned len)); +/* + Write data in the zipfile +*/ + +extern int ZEXPORT zipCloseFileInZip OF((zipFile file)); +/* + Close the current file in the zipfile +*/ + +extern int ZEXPORT zipCloseFileInZipRaw OF((zipFile file, + uLong uncompressed_size, + uLong crc32)); + +extern int ZEXPORT zipCloseFileInZipRaw64 OF((zipFile file, + ZPOS64_T uncompressed_size, + uLong crc32)); + +/* + Close the current file in the zipfile, for file opened with + parameter raw=1 in zipOpenNewFileInZip2 + uncompressed_size and crc32 are value for the uncompressed size +*/ + +extern int ZEXPORT zipClose OF((zipFile file, + const char* global_comment)); +/* + Close the zipfile +*/ + + +extern int ZEXPORT zipRemoveExtraInfoBlock OF((char* pData, int* dataLen, short sHeader)); +/* + zipRemoveExtraInfoBlock - Added by Mathias Svensson + + Remove extra information block from a extra information data for the local file header or central directory header + + It is needed to remove ZIP64 extra information blocks when before data is written if using RAW mode. + + 0x0001 is the signature header for the ZIP64 extra information blocks + + usage. + Remove ZIP64 Extra information from a central director extra field data + zipRemoveExtraInfoBlock(pCenDirExtraFieldData, &nCenDirExtraFieldDataLen, 0x0001); + + Remove ZIP64 Extra information from a Local File Header extra field data + zipRemoveExtraInfoBlock(pLocalHeaderExtraFieldData, &nLocalHeaderExtraFieldDataLen, 0x0001); +*/ + +#ifdef __cplusplus +} +#endif + +#endif /* _zip64_H */ diff --git a/core/io/zip_io.h b/core/io/zip_io.h new file mode 100644 index 00000000000..c7d5a701359 --- /dev/null +++ b/core/io/zip_io.h @@ -0,0 +1,129 @@ +/*************************************************************************/ +/* zip_io.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 ZIP_IO_H +#define ZIP_IO_H + +#include "io/zip.h" +#include "io/unzip.h" +#include "os/file_access.h" + +static void* zipio_open(void* data, const char* p_fname, int mode) { + + FileAccess *&f = *(FileAccess**)data; + + if (mode & ZLIB_FILEFUNC_MODE_WRITE) { + f = FileAccess::open(p_fname,FileAccess::WRITE); + } else { + + f = FileAccess::open(p_fname,FileAccess::READ); + } + + if (!f) + return NULL; + + return data; + +}; + +static uLong zipio_read(void* data, void* fdata, void* buf, uLong size) { + + FileAccess* f = *(FileAccess**)data; + return f->get_buffer((uint8_t*)buf, size); + +}; + +static uLong zipio_write(voidpf opaque, voidpf stream, const void* buf, uLong size) { + + FileAccess* f = *(FileAccess**)opaque; + f->store_buffer((uint8_t*)buf, size); + return size; +}; + + +static long zipio_tell (voidpf opaque, voidpf stream) { + + FileAccess* f = *(FileAccess**)opaque; + return f->get_pos(); +}; + +static long zipio_seek(voidpf opaque, voidpf stream, uLong offset, int origin) { + + FileAccess* f = *(FileAccess**)opaque; + + int pos = offset; + switch (origin) { + + case ZLIB_FILEFUNC_SEEK_CUR: + pos = f->get_pos() + offset; + break; + case ZLIB_FILEFUNC_SEEK_END: + pos = f->get_len() + offset; + break; + default: + break; + }; + + f->seek(pos); + return 0; +}; + +static int zipio_close(voidpf opaque, voidpf stream) { + + FileAccess*& f = *(FileAccess**)opaque; + if (f) { + f->close(); + f=NULL; + } + return 0; +}; + +static int zipio_testerror(voidpf opaque, voidpf stream) { + + FileAccess* f = *(FileAccess**)opaque; + return (f && f->get_error()!=OK)?1:0; +}; + + +static zlib_filefunc_def zipio_create_io_from_file(FileAccess **p_file) { + + zlib_filefunc_def io; + io.opaque = p_file; + io.zopen_file = zipio_open; + io.zread_file = zipio_read; + io.zwrite_file = zipio_write; + io.ztell_file = zipio_tell; + io.zseek_file = zipio_seek; + io.zclose_file = zipio_close; + io.zerror_file = zipio_testerror; + return io; +} + + + +#endif // ZIP_IO_H diff --git a/core/list.h b/core/list.h new file mode 100644 index 00000000000..f581feb7356 --- /dev/null +++ b/core/list.h @@ -0,0 +1,634 @@ +/*************************************************************************/ +/* list.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 GLOBALS_LIST_H +#define GLOBALS_LIST_H + +#include "os/memory.h" + + +/** + * Generic Templatized Linked List Implementation. + * The implementation differs from the STL one because + * a compatible preallocated linked list can be written + * using the same API, or features such as erasing an element + * from the iterator. + */ + +template +class List { + struct _Data; +public: + + + class Element { + + private: + friend class List; + + T value; + Element* next_ptr; + Element* prev_ptr; + _Data *data; + public: + + /** + * Get NEXT Element iterator, for constant lists. + */ + _FORCE_INLINE_ const Element* next() const { + + return next_ptr; + }; + /** + * Get NEXT Element iterator, + */ + _FORCE_INLINE_ Element* next() { + + return next_ptr; + }; + + /** + * Get PREV Element iterator, for constant lists. + */ + _FORCE_INLINE_ const Element* prev() const { + + return prev_ptr; + }; + /** + * Get PREV Element iterator, + */ + _FORCE_INLINE_ Element* prev() { + + return prev_ptr; + }; + + /** + * * operator, for using as *iterator, when iterators are defined on stack. + */ + _FORCE_INLINE_ const T& operator *() const { + return value; + }; + /** + * operator->, for using as iterator->, when iterators are defined on stack, for constant lists. + */ + _FORCE_INLINE_ const T* operator->() const { + + return &value; + }; + /** + * * operator, for using as *iterator, when iterators are defined on stack, + */ + _FORCE_INLINE_ T& operator *() { + return value; + }; + /** + * operator->, for using as iterator->, when iterators are defined on stack, for constant lists. + */ + _FORCE_INLINE_ T* operator->() { + return &value; + }; + + /** + * get the value stored in this element. + */ + _FORCE_INLINE_ T& get() { + return value; + }; + /** + * get the value stored in this element, for constant lists + */ + _FORCE_INLINE_ const T& get() const { + return value; + }; + /** + * set the value stored in this element. + */ + _FORCE_INLINE_ void set(const T& p_value) { + value = (T&)p_value; + }; + + void erase() { + + data->erase(this); + } + + _FORCE_INLINE_ Element() { + next_ptr = 0; + prev_ptr = 0; + data=NULL; + }; + }; + +private: + + struct _Data { + + Element* first; + Element* last; + int size_cache; + + + bool erase(const Element* p_I) { + + ERR_FAIL_COND_V(!p_I,false); + ERR_FAIL_COND_V(p_I->data!=this,false); + + if (first==p_I) { + first=p_I->next_ptr; + }; + + if (last==p_I) + last=p_I->prev_ptr; + + if (p_I->prev_ptr) + p_I->prev_ptr->next_ptr=p_I->next_ptr; + + if (p_I->next_ptr) + p_I->next_ptr->prev_ptr=p_I->prev_ptr; + + memdelete_allocator( const_cast(p_I) ); + size_cache--; + + return true; + } + }; + + _Data *_data; + + +public: + + /** + * return an const iterator to the begining of the list. + */ + _FORCE_INLINE_ const Element* front() const { + + return _data?_data->first:0; + }; + + /** + * return an iterator to the begining of the list. + */ + _FORCE_INLINE_ Element* front() { + return _data?_data->first:0; + }; + + /** + * return an const iterator to the last member of the list. + */ + _FORCE_INLINE_ const Element* back() const { + + return _data?_data->last:0; + }; + + /** + * return an iterator to the last member of the list. + */ + _FORCE_INLINE_ Element* back() { + + return _data?_data->last:0; + }; + + /** + * store a new element at the end of the list + */ + Element* push_back(const T& value) { + + if (!_data) { + + _data=memnew_allocator(_Data,A); + _data->first=NULL; + _data->last=NULL; + _data->size_cache=0; + } + + Element* n = memnew_allocator(Element,A); + n->value = (T&)value; + + n->prev_ptr=_data->last; + n->next_ptr=0; + n->data=_data; + + if (_data->last) { + + _data->last->next_ptr=n; + } + + _data->last = n; + + if (!_data->first) + _data->first=n; + + _data->size_cache++; + + return n; + }; + + void pop_back() { + + if (_data && _data->last) + erase(_data->last); + } + + /** + * store a new element at the begining of the list + */ + Element* push_front(const T& value) { + + if (!_data) { + + _data=memnew_allocator(_Data,A); + _data->first=NULL; + _data->last=NULL; + _data->size_cache=0; + } + + Element* n = memnew_allocator(Element,A); + n->value = (T&)value; + n->prev_ptr = 0; + n->next_ptr = _data->first; + n->data=_data; + + if (_data->first) { + + _data->first->prev_ptr=n; + } + + _data->first = n; + + if (!_data->last) + _data->last=n; + + _data->size_cache++; + + return n; + }; + + void pop_front() { + + if (_data && _data->first) + erase(_data->first); + } + + /** + * find an element in the list, + */ + template + Element* find(const T_v& p_val) { + + Element* it = front(); + while (it) { + if (it->value == p_val) return it; + it = it->next(); + }; + + return NULL; + }; + + /** + * erase an element in the list, by iterator pointing to it. Return true if it was found/erased. + */ + bool erase(const Element* p_I) { + + if (_data) { + bool ret = _data->erase(p_I); + + if (_data->size_cache==0) { + memdelete_allocator<_Data,A>(_data); + _data=NULL; + } + + return ret; + } + + return false; + }; + + /** + * erase the first element in the list, that contains value + */ + bool erase(const T& value) { + + Element* I = find(value); + return erase(I); + }; + + /** + * return wether the list is empty + */ + _FORCE_INLINE_ bool empty() const { + + return (!_data || !_data->size_cache); + } + + /** + * clear the list + */ + void clear() { + + while (front()) { + erase(front()); + }; + }; + + _FORCE_INLINE_ int size() const { + + return _data?_data->size_cache:0; + + } + + void swap(Element* p_A, Element *p_B) { + + ERR_FAIL_COND(!p_A || !p_B); + ERR_FAIL_COND(p_A->data!=_data); + ERR_FAIL_COND(p_B->data!=_data); + + Element* A_prev=p_A->prev_ptr; + Element* A_next=p_A->next_ptr; + + p_A->next_ptr=p_B->next_ptr; + p_A->prev_ptr=p_B->prev_ptr; + + p_B->next_ptr=A_next; + p_B->prev_ptr=A_prev; + + if (p_A->prev_ptr) + p_A->prev_ptr->next_ptr=p_A; + if (p_A->next_ptr) + p_A->next_ptr->prev_ptr=p_A; + + if (p_B->prev_ptr) + p_B->prev_ptr->next_ptr=p_B; + if (p_B->next_ptr) + p_B->next_ptr->prev_ptr=p_B; + + } + /** + * copy the list + */ + void operator=(const List& p_list) { + + clear(); + const Element *it=p_list.front(); + while (it) { + + push_back( it->get() ); + it=it->next(); + } + + } + + T& operator[](int p_index) { + + if (p_index<0 || p_index>=size()) { + T& aux=*((T*)0); //nullreturn + ERR_FAIL_COND_V(p_index<0 || p_index>=size(),aux); + } + + Element *I=front(); + int c=0; + while(I) { + + if (c==p_index) { + + return I->get(); + } + I=I->next(); + c++; + } + + ERR_FAIL_V( *((T*)0) ); // bug!! + } + + const T& operator[](int p_index) const { + + if (p_index<0 || p_index>=size()) { + T& aux=*((T*)0); //nullreturn + ERR_FAIL_COND_V(p_index<0 || p_index>=size(),aux); + } + + const Element *I=front(); + int c=0; + while(I) { + + if (c==p_index) { + + return I->get(); + } + I=I->next(); + c++; + } + + ERR_FAIL_V( *((T*)0) ); // bug! + } + + + void move_to_back(Element* p_I) { + + ERR_FAIL_COND(p_I->data!=_data); + if (!p_I->next_ptr) + return; + + if (_data->first==p_I) { + _data->first=p_I->next_ptr; + }; + + if (_data->last==p_I) + _data->last=p_I->prev_ptr; + + if (p_I->prev_ptr) + p_I->prev_ptr->next_ptr=p_I->next_ptr; + + if (p_I->next_ptr) + p_I->next_ptr->prev_ptr=p_I->prev_ptr; + + + _data->last->next_ptr=p_I; + p_I->prev_ptr=_data->last; + p_I->next_ptr=NULL; + _data->last=p_I; + + } + + void invert() { + + int s = size() / 2; + Element *F = front(); + Element *B = back(); + for(int i=0;ivalue, B->value ); + F=F->next(); + B=B->prev(); + } + } + + void move_to_front(Element* p_I) { + + ERR_FAIL_COND(p_I->data!=_data); + if (!p_I->prev_ptr) + return; + + if (_data->first==p_I) { + _data->first=p_I->next_ptr; + }; + + if (_data->last==p_I) + _data->last=p_I->prev_ptr; + + if (p_I->prev_ptr) + p_I->prev_ptr->next_ptr=p_I->next_ptr; + + if (p_I->next_ptr) + p_I->next_ptr->prev_ptr=p_I->prev_ptr; + + _data->first->prev_ptr=p_I; + p_I->next_ptr=_data->first; + p_I->prev_ptr=NULL; + _data->first=p_I; + + } + + void move_before(Element* value, Element* where) { + + if (value->prev_ptr) { + value->prev_ptr->next_ptr = value->next_ptr; + }; + if (value->next_ptr) { + value->next_ptr->prev_ptr = value->prev_ptr; + }; + + value->next_ptr = where; + if (!where) { + value->prev_ptr = _data->last; + _data->last = value; + return; + }; + + value->prev_ptr = where->prev_ptr; + + if (where->prev_ptr) { + where->prev_ptr->next_ptr = value; + } else { + _data->first = value; + }; + + where->prev_ptr = value; + }; + + /** + * simple insertion sort + */ + + void sort() { + + sort_custom< Comparator >(); + } + + template + void sort_custom() { + + if(size()<2) + return; + + Element *from=front(); + Element *current=from; + Element *to=from; + + while(current) { + + Element *next=current->next_ptr; + + //disconnect + current->next_ptr=NULL; + + if (from!=current) { + + current->prev_ptr=NULL; + current->next_ptr=from; + + Element *find=from; + C less; + while( find && less(find->value,current->value) ) { + + current->prev_ptr=find; + current->next_ptr=find->next_ptr; + find=find->next_ptr; + } + + if (current->prev_ptr) + current->prev_ptr->next_ptr=current; + else + from=current; + + if (current->next_ptr) + current->next_ptr->prev_ptr=current; + else + to=current; + } else { + + current->prev_ptr=NULL; + current->next_ptr=NULL; + + } + + current=next; + } + _data->first=from; + _data->last=to; + } + + /** + * copy constructor for the list + */ + List(const List& p_list) { + + _data=NULL; + const Element *it=p_list.front(); + while (it) { + + push_back( it->get() ); + it=it->next(); + } + + } + + List() { + _data=NULL; + }; + ~List() { + clear(); + if (_data) { + + ERR_FAIL_COND(_data->size_cache); + memdelete_allocator<_Data,A>(_data); + } + }; +}; + +#endif diff --git a/core/make_binders.py b/core/make_binders.py new file mode 100644 index 00000000000..5d35dd93376 --- /dev/null +++ b/core/make_binders.py @@ -0,0 +1,241 @@ +# -*- coding: ibm850 -*- + + +template_typed=""" +#ifdef TYPED_METHOD_BIND +template +class MethodBind$argc$$ifret R$$ifconst C$ : public MethodBind { +public: + + $ifret R$ $ifnoret void$ (T::*method)($arg, P@$) $ifconst const$; +#ifdef DEBUG_METHODS_ENABLED + virtual Variant::Type _gen_argument_type(int p_arg) const { return _get_argument_type(p_arg); } + Variant::Type _get_argument_type(int p_argument) const { + $ifret if (p_argument==-1) return Variant::get_type_for();$ + $arg if (p_argument==(@-1)) return Variant::get_type_for(); + $ + return Variant::NIL; + } +#endif + virtual String get_instance_type() const { + return T::get_type_static(); + } + + virtual Variant call(Object* p_object,const Variant** p_args,int p_arg_count, Variant::CallError& r_error) { + + T *instance=p_object->cast_to(); + r_error.error=Variant::CallError::CALL_OK; +#ifdef DEBUG_METHODS_ENABLED + + ERR_FAIL_COND_V(!instance,Variant()); + if (p_arg_count>get_argument_count()) { + r_error.error=Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; + r_error.argument=get_argument_count(); + return Variant(); + + } + if (p_arg_count<(get_argument_count()-get_default_argument_count())) { + + r_error.error=Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument=get_argument_count()-get_default_argument_count(); + return Variant(); + } + $arg CHECK_ARG(@); + $ +#endif + $ifret Variant ret = $(instance->*method)($arg, _VC(@)$); + $ifret return Variant(ret);$ + $ifnoret return Variant();$ + } + + + MethodBind$argc$$ifret R$$ifconst C$ () { +#ifdef DEBUG_METHODS_ENABLED + _set_const($ifconst true$$ifnoconst false$); + _generate_argument_types($argc$); +#else + set_argument_count($argc$); +#endif + }; +}; + +template +MethodBind* create_method_bind($ifret R$ $ifnoret void$ (T::*p_method)($arg, P@$) $ifconst const$ ) { + + MethodBind$argc$$ifret R$$ifconst C$ * a = memnew( (MethodBind$argc$$ifret R$$ifconst C$) ); + a->method=p_method; + return a; +} +#endif +""" + +template=""" +#ifndef TYPED_METHOD_BIND +$iftempl template<$ $ifret class R$ $ifretargs ,$ $arg, class P@$ $iftempl >$ +class MethodBind$argc$$ifret R$$ifconst C$ : public MethodBind { + +public: + + StringName type_name; + $ifret R$ $ifnoret void$ (__UnexistingClass::*method)($arg, P@$) $ifconst const$; + +#ifdef DEBUG_METHODS_ENABLED + virtual Variant::Type _gen_argument_type(int p_arg) const { return _get_argument_type(p_arg); } + + Variant::Type _get_argument_type(int p_argument) const { + $ifret if (p_argument==-1) return Variant::get_type_for();$ + $arg if (p_argument==(@-1)) return Variant::get_type_for(); + $ + return Variant::NIL; + } +#endif + virtual String get_instance_type() const { + return type_name; + } + + virtual Variant call(Object* p_object,const Variant** p_args,int p_arg_count, Variant::CallError& r_error) { + + __UnexistingClass *instance = (__UnexistingClass*)p_object; + + r_error.error=Variant::CallError::CALL_OK; +#ifdef DEBUG_METHODS_ENABLED + + ERR_FAIL_COND_V(!instance,Variant()); + if (p_arg_count>get_argument_count()) { + r_error.error=Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; + r_error.argument=get_argument_count(); + return Variant(); + } + + if (p_arg_count<(get_argument_count()-get_default_argument_count())) { + + r_error.error=Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument=get_argument_count()-get_default_argument_count(); + return Variant(); + } + + $arg CHECK_ARG(@); + $ +#endif + $ifret Variant ret = $(instance->*method)($arg, _VC(@)$); + $ifret return Variant(ret);$ + $ifnoret return Variant();$ + } + + MethodBind$argc$$ifret R$$ifconst C$ () { +#ifdef DEBUG_METHODS_ENABLED + _set_const($ifconst true$$ifnoconst false$); + _generate_argument_types($argc$); +#else + set_argument_count($argc$); +#endif + }; +}; + +template +MethodBind* create_method_bind($ifret R$ $ifnoret void$ (T::*p_method)($arg, P@$) $ifconst const$ ) { + + MethodBind$argc$$ifret R$$ifconst C$ $iftempl <$ $ifret R$ $ifretargs ,$ $arg, P@$ $iftempl >$ * a = memnew( (MethodBind$argc$$ifret R$$ifconst C$ $iftempl <$ $ifret R$ $ifretargs ,$ $arg, P@$ $iftempl >$) ); + union { + + $ifret R$ $ifnoret void$ (T::*sm)($arg, P@$) $ifconst const$; + $ifret R$ $ifnoret void$ (__UnexistingClass::*dm)($arg, P@$) $ifconst const$; + } u; + u.sm=p_method; + a->method=u.dm; + a->type_name=T::get_type_static(); + return a; +} +#endif +""" + + +def make_version(template,nargs,argmax,const,ret): + + intext=template + from_pos=0 + outtext="" + + while(True): + to_pos=intext.find("$",from_pos) + if (to_pos==-1): + outtext+=intext[from_pos:] + break + else: + outtext+=intext[from_pos:to_pos] + end=intext.find("$",to_pos+1) + if (end==-1): + break # ignore + macro=intext[to_pos+1:end] + cmd="" + data="" + + if (macro.find(" ")!=-1): + cmd=macro[0:macro.find(" ")] + data=macro[macro.find(" ")+1:] + else: + cmd=macro + + if (cmd=="argc"): + outtext+=str(nargs) + if (cmd=="ifret" and ret): + outtext+=data + if (cmd=="ifargs" and nargs): + outtext+=data + if (cmd=="ifretargs" and nargs and ret): + outtext+=data + if (cmd=="ifconst" and const): + outtext+=data + elif (cmd=="ifnoconst" and not const): + outtext+=data + elif (cmd=="ifnoret" and not ret): + outtext+=data + elif (cmd=="iftempl" and (nargs>0 or ret)): + outtext+=data + elif (cmd=="arg,"): + for i in range(1,nargs+1): + if (i>1): + outtext+=", " + outtext+=data.replace("@",str(i)) + elif (cmd=="arg"): + for i in range(1,nargs+1): + outtext+=data.replace("@",str(i)) + elif (cmd=="noarg"): + for i in range(nargs+1,argmax+1): + outtext+=data.replace("@",str(i)) + elif (cmd=="noarg"): + for i in range(nargs+1,argmax+1): + outtext+=data.replace("@",str(i)) + + from_pos=end+1 + + return outtext + + +def run(target, source, env): + + versions=5 + text="" + + for i in range(0,versions+1): + + text+=make_version(template,i,5,False,False) + text+=make_version(template_typed,i,5,False,False) + text+=make_version(template,i,5,False,True) + text+=make_version(template_typed,i,5,False,True) + text+=make_version(template,i,5,True,False) + text+=make_version(template_typed,i,5,True,False) + text+=make_version(template,i,5,True,True) + text+=make_version(template_typed,i,5,True,True) + + + f=open(target[0].path,"w") + f.write(text) + f.close() + + + + + + + diff --git a/core/map.h b/core/map.h new file mode 100644 index 00000000000..959dc586617 --- /dev/null +++ b/core/map.h @@ -0,0 +1,708 @@ +/*************************************************************************/ +/* 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 MAP_H +#define MAP_H + +#include "set.h" +/** + @author Juan Linietsky +*/ + +// based on the very nice implementation of rb-trees by: +// http://web.mit.edu/~emin/www/source_code/red_black_tree/index.html + +template ,class A=DefaultAllocator> +class Map { + + enum Color { + RED, + BLACK + }; + struct _Data; +public: + + class Element { + + private: + friend class Map; + //Color color; + int color; + Element* right; + Element* left; + Element* parent; + Element* _next; + Element* _prev; + K _key; + V _value; + + //_Data *data; + + public: + + const Element *next() const { + + return _next; + } + Element *next() { + + return _next; + } + const Element *prev() const { + + return _prev; + } + Element *prev() { + + return _prev; + } + const K& key() const { + return _key; + }; + V& value() { + return _value; + }; + const V& value() const { + return _value; + }; + V& get() { + return _value; + }; + const V& get() const { + return _value; + }; + Element() { + color=RED; + right=NULL; + left=NULL; + parent=NULL; + _next=NULL; + _prev=NULL; + }; + }; + + +private: + + struct _Data { + + Element* _root; + Element* _nil; + int size_cache; + + _Data() { +#ifdef GLOBALNIL_DISABLED + _nil = memnew_allocator( Element, A ); + _nil->parent=_nil->left=_nil->right=_nil; + _nil->color=BLACK; +#else + _nil=(Element*)&_GlobalNilClass::_nil; +#endif + _root=NULL; + size_cache=0; + } + + void _create_root() { + + _root = memnew_allocator( Element,A ); + _root->parent=_root->left=_root->right=_nil; + _root->color=BLACK; + } + + void _free_root() { + + if (_root) { + memdelete_allocator(_root); + _root=NULL; + } + } + + ~_Data() { + + _free_root(); + +#ifdef GLOBALNIL_DISABLED + memdelete_allocator(_nil); +#endif +// memdelete_allocator(_root); + } + }; + + _Data _data; + + inline void _set_color(Element *p_node, int p_color) { + + ERR_FAIL_COND( p_node == _data._nil && p_color == RED ); + p_node->color=p_color; + } + inline void _rotate_left(Element *p_node) { + + Element *r=p_node->right; + p_node->right=r->left; + if (r->left != _data._nil ) + r->left->parent=p_node; + r->parent=p_node->parent; + if (p_node==p_node->parent->left) + p_node->parent->left=r; + else + p_node->parent->right=r; + + r->left=p_node; + p_node->parent=r; + + } + + inline void _rotate_right(Element *p_node) { + + Element *l=p_node->left; + p_node->left=l->right; + if (l->right != _data._nil) + l->right->parent=p_node; + l->parent=p_node->parent; + if (p_node==p_node->parent->right) + p_node->parent->right=l; + else + p_node->parent->left=l; + + l->right=p_node; + p_node->parent=l; + + } + + inline Element* _successor(Element *p_node) const { + + Element *node=p_node; + + if (node->right != _data._nil) { + + node=node->right; + while(node->left != _data._nil) { /* returns the minium of the right subtree of node */ + node=node->left; + } + return node; + } else { + + while(node == node->parent->right) { + node=node->parent; + } + if (node->parent == _data._root) + return NULL; + return node->parent; + } + } + + inline Element* _predecessor(Element *p_node) const { + Element *node=p_node; + + if (node->left != _data._nil) { + + node=node->left; + while(node->right != _data._nil) { /* returns the minium of the left subtree of node */ + node=node->right; + } + return node; + } else { + + while(node == node->parent->left) { + if (node->parent == _data._root) + return NULL; + node=node->parent; + } + return node->parent; + } + } + + + Element *_find(const K& p_key) const { + + Element *node = _data._root->left; + C less; + + while(node!=_data._nil) { + + if (less(p_key,node->_key)) + node=node->left; + else if (less(node->_key,p_key)) + node=node->right; + else + break; // found + } + + return (node!=_data._nil)?node:NULL; + } + + Element *_find_closest(const K& p_key) const { + + Element *node = _data._root->left; + Element *prev = NULL; + C less; + + while(node!=_data._nil) { + prev=node; + + if (less(p_key,node->_key)) + node=node->left; + else if (less(node->_key,p_key)) + node=node->right; + else + break; // found + } + + if (node==_data._nil) { + if (prev==NULL) + return NULL; + if (less(p_key,prev->_key)) { + + prev=prev->_prev; + } + + return prev; + + } else + return node; + + } + + Element *_insert(const K& p_key, bool& r_exists) { + + Element *new_parent=_data._root; + Element *node = _data._root->left; + C less; + + while (node!=_data._nil) { + + new_parent=node; + + if (less(p_key,node->_key)) + node=node->left; + else if (less(node->_key,p_key)) + node=node->right; + else { + r_exists=true; + return node; + } + } + + Element *new_node = memnew_allocator( Element, A ); + + + new_node->parent=new_parent; + new_node->right=_data._nil; + new_node->left=_data._nil; + new_node->_key=p_key; + //new_node->data=_data; + if (new_parent==_data._root || less(p_key,new_parent->_key)) { + + new_parent->left=new_node; + } else { + new_parent->right=new_node; + } + + r_exists=false; + + new_node->_next=_successor(new_node); + new_node->_prev=_predecessor(new_node); + if (new_node->_next) + new_node->_next->_prev=new_node; + if (new_node->_prev) + new_node->_prev->_next=new_node; + + + return new_node; + } + + Element * _insert_rb(const K& p_key, const V& p_value) { + + bool exists=false; + Element *new_node = _insert(p_key,exists); + + if (new_node) { + new_node->_value=p_value; + } + if (exists) + return new_node; + + Element *node=new_node; + _data.size_cache++; + + while(node->parent->color==RED) { + + if (node->parent == node->parent->parent->left) { + + Element *aux=node->parent->parent->right; + + if (aux->color==RED) { + _set_color(node->parent,BLACK); + _set_color(aux,BLACK); + _set_color(node->parent->parent,RED); + node=node->parent->parent; + } else { + if (node == node->parent->right) { + node=node->parent; + _rotate_left(node); + } + _set_color(node->parent,BLACK); + _set_color(node->parent->parent,RED); + _rotate_right(node->parent->parent); + } + } else { + Element *aux=node->parent->parent->left; + + if (aux->color==RED) { + _set_color(node->parent,BLACK); + _set_color(aux,BLACK); + _set_color(node->parent->parent,RED); + node=node->parent->parent; + } else { + if (node == node->parent->left) { + node=node->parent; + _rotate_right(node); + } + _set_color(node->parent,BLACK); + _set_color(node->parent->parent,RED); + _rotate_left(node->parent->parent); + } + } + } + _set_color(_data._root->left,BLACK); + return new_node; + } + + void _erase_fix(Element *p_node) { + + Element *root = _data._root->left; + Element *node=p_node; + + + while( (node->color==BLACK) && (root != node)) { + if (node == node->parent->left) { + Element *aux=node->parent->right; + if (aux->color==RED) { + _set_color(aux,BLACK); + _set_color(node->parent,RED); + _rotate_left(node->parent); + aux=node->parent->right; + } + if ( (aux->right->color==BLACK) && (aux->left->color==BLACK) ) { + _set_color(aux,RED); + node=node->parent; + } else { + if (aux->right->color==BLACK) { + _set_color(aux->left,BLACK); + _set_color(aux,RED); + _rotate_right(aux); + aux=node->parent->right; + } + _set_color(aux,node->parent->color); + _set_color(node->parent,BLACK); + _set_color(aux->right,BLACK); + _rotate_left(node->parent); + node=root; /* this is to exit while loop */ + } + } else { /* the code below is has left and right switched from above */ + Element *aux=node->parent->left; + if (aux->color==RED) { + _set_color(aux,BLACK); + _set_color(node->parent,RED);; + _rotate_right(node->parent); + aux=node->parent->left; + } + if ( (aux->right->color==BLACK) && (aux->left->color==BLACK) ) { + _set_color(aux,RED); + node=node->parent; + } else { + if (aux->left->color==BLACK) { + _set_color(aux->right,BLACK); + _set_color(aux,RED); + _rotate_left(aux); + aux=node->parent->left; + } + _set_color(aux,node->parent->color); + _set_color(node->parent,BLACK); + _set_color(aux->left,BLACK); + _rotate_right(node->parent); + node=root; + } + } + } + + _set_color(node,BLACK); + + ERR_FAIL_COND(_data._nil->color!=BLACK); + } + + void _erase(Element *p_node) { + + + Element *rp= ((p_node->left == _data._nil) || (p_node->right == _data._nil)) ? p_node : _successor(p_node); + if (!rp) + rp=_data._nil; + Element *node= (rp->left == _data._nil) ? rp->right : rp->left; + + if (_data._root == (node->parent=rp->parent) ) { + _data._root->left=node; + } else { + if (rp == rp->parent->left) { + rp->parent->left=node; + } else { + rp->parent->right=node; + } + } + + if (rp != p_node) { + + ERR_FAIL_COND( rp == _data._nil ); + + if (rp->color==BLACK) + _erase_fix(node); + + + rp->left=p_node->left; + rp->right=p_node->right; + rp->parent=p_node->parent; + rp->color=p_node->color; + p_node->left->parent=rp; + p_node->right->parent=rp; + + if (p_node == p_node->parent->left) { + p_node->parent->left=rp; + } else { + p_node->parent->right=rp; + } + } else { + if (p_node->color==BLACK) + _erase_fix(node); + + } + + + if (p_node->_next) + p_node->_next->_prev=p_node->_prev; + if (p_node->_prev) + p_node->_prev->_next=p_node->_next; + + memdelete_allocator(p_node); + _data.size_cache--; + ERR_FAIL_COND( _data._nil->color==RED ); + } + + + void _calculate_depth(Element *p_element,int &max_d,int d) const { + + if (p_element==_data._nil) { + return; + } + _calculate_depth(p_element->left,max_d,d+1); + _calculate_depth(p_element->right,max_d,d+1); + if (d>max_d) + max_d=d; + } + + void _cleanup_tree(Element *p_element) { + + if (p_element==_data._nil) + return; + + _cleanup_tree(p_element->left); + _cleanup_tree(p_element->right); + memdelete_allocator( p_element ); + } + + void _copy_from( const Map& p_map) { + + clear(); + // not the fastest way, but safeset to write. + for(Element *I=p_map.front();I;I=I->next()) { + + insert(I->key(),I->value()); + } + } +public: + + const Element *find(const K& p_key) const { + + if (!_data._root) + return NULL; + + const Element *res=_find(p_key); + return res; + } + + Element *find(const K& p_key) { + + if (!_data._root) + return NULL; + Element *res=_find(p_key); + return res; + } + + const Element *find_closest(const K& p_key) const { + + if (!_data._root) + return NULL; + const Element *res=_find_closest(p_key); + return res; + } + + Element *find_closest(const K& p_key) { + + if (!_data._root) + return NULL; + Element *res=_find_closest(p_key); + return res; + } + + Element *insert(const K& p_key,const V& p_value) { + + if (!_data._root) + _data._create_root(); + return _insert_rb(p_key,p_value); + + } + + void erase(Element* p_element) { + + if (!_data._root) + return; + _erase(p_element); + if (_data.size_cache==0 && _data._root) + _data._free_root(); + } + + bool erase(const K& p_key) { + + if (!_data._root) + return false; + Element *e=find(p_key); + if (!e) + return false; + _erase(e); + return true; + } + + bool has(const K& p_key) const { + + if (!_data._root) + return false; + return find(p_key) != NULL; + } + + const V& operator[](const K& p_key) const { + + ERR_FAIL_COND_V(!_data._root, *(V*)NULL); // crash on purpose + const Element *e=find(p_key); + ERR_FAIL_COND_V(!e, *(V*)NULL); // crash on purpose + return e->_value; + } + V& operator[](const K& p_key) { + + if (!_data._root) + _data._create_root(); + + Element *e=find(p_key); + if (!e) + e=insert(p_key,V()); + + ERR_FAIL_COND_V(!e, *(V*)NULL); // crash on purpose + return e->_value; + } + + Element *front() const { + + if (!_data._root) + return NULL; + + Element *e=_data._root->left; + if (e==_data._nil) + return NULL; + + while(e->left!=_data._nil) + e=e->left; + + return e; + } + + Element *back() const { + + if (!_data._root) + return NULL; + Element *e=_data._root->left; + if (e==_data._nil) + return NULL; + + while(e->right!=_data._nil) + e=e->right; + + return e; + } + + inline bool empty() const { return _data.size_cache==0; } + inline int size() const { return _data.size_cache; } + int calculate_depth() const { + // used for debug mostly + if (!_data._root) + return 0; + int max_d=0; + _calculate_depth(_data._root->left,max_d,0); + return max_d; + } + + void clear() { + + if (!_data._root) + return; + _cleanup_tree(_data._root->left); + _data._root->left=_data._nil; + _data.size_cache=0; + _data._nil->parent=_data._nil; + _data._free_root(); + } + + void operator=(const Map& p_map) { + + _copy_from( p_map ); + } + + Map(const Map& p_map) { + + _copy_from( p_map ); + } + + _FORCE_INLINE_ Map() { + + } + + + ~Map() { + + clear(); + } + +}; + +#endif diff --git a/core/math/SCsub b/core/math/SCsub new file mode 100644 index 00000000000..c6ba1fa5376 --- /dev/null +++ b/core/math/SCsub @@ -0,0 +1,7 @@ +Import('env') + +env.add_source_files(env.core_sources,"*.cpp") + +Export('env') + + diff --git a/core/math/aabb.cpp b/core/math/aabb.cpp new file mode 100644 index 00000000000..0d454cd07db --- /dev/null +++ b/core/math/aabb.cpp @@ -0,0 +1,427 @@ +/*************************************************************************/ +/* aabb.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 "aabb.h" + +#include "print_string.h" + +float AABB::get_area() const { + + return size.x*size.y*size.z; + +} + +bool AABB::operator==(const AABB& p_rval) const { + + return ((pos==p_rval.pos) && (size==p_rval.size)); + +} +bool AABB::operator!=(const AABB& p_rval) const { + + return ((pos!=p_rval.pos) || (size!=p_rval.size)); + +} + +void AABB::merge_with(const AABB& p_aabb) { + + Vector3 beg_1,beg_2; + Vector3 end_1,end_2; + Vector3 min,max; + + beg_1=pos; + beg_2=p_aabb.pos; + end_1=Vector3(size.x,size.y,size.z)+beg_1; + end_2=Vector3(p_aabb.size.x,p_aabb.size.y,p_aabb.size.z)+beg_2; + + min.x=(beg_1.xend_2.x)?end_1.x:end_2.x; + max.y=(end_1.y>end_2.y)?end_1.y:end_2.y; + max.z=(end_1.z>end_2.z)?end_1.z:end_2.z; + + pos=min; + size=max-min; +} + +AABB AABB::intersection(const AABB& p_aabb) const { + + Vector3 src_min=pos; + Vector3 src_max=pos+size; + Vector3 dst_min=p_aabb.pos; + Vector3 dst_max=p_aabb.pos+p_aabb.size; + + Vector3 min,max; + + if (src_min.x > dst_max.x || src_max.x < dst_min.x ) + return AABB(); + else { + + min.x= ( src_min.x > dst_min.x ) ? src_min.x :dst_min.x; + max.x= ( src_max.x < dst_max.x ) ? src_max.x :dst_max.x; + + } + + if (src_min.y > dst_max.y || src_max.y < dst_min.y ) + return AABB(); + else { + + min.y= ( src_min.y > dst_min.y ) ? src_min.y :dst_min.y; + max.y= ( src_max.y < dst_max.y ) ? src_max.y :dst_max.y; + + } + + if (src_min.z > dst_max.z || src_max.z < dst_min.z ) + return AABB(); + else { + + min.z= ( src_min.z > dst_min.z ) ? src_min.z :dst_min.z; + max.z= ( src_max.z < dst_max.z ) ? src_max.z :dst_max.z; + + } + + + return AABB( min, max-min ); +} + +bool AABB::intersects_ray(const Vector3& p_from, const Vector3& p_dir,Vector3* r_clip,Vector3* r_normal) const { + + Vector3 c1, c2; + Vector3 end = pos+size; + float near=-1e20; + float far=1e20; + int axis=0; + + for (int i=0;i<3;i++){ + if (p_dir[i] == 0){ + if ((p_from[i] < pos[i]) || (p_from[i] > end[i])) { + return false; + } + } else { // ray not parallel to planes in this direction + c1[i] = (pos[i] - p_from[i]) / p_dir[i]; + c2[i] = (end[i] - p_from[i]) / p_dir[i]; + + if(c1[i] > c2[i]){ + SWAP(c1,c2); + } + if (c1[i] > near){ + near = c1[i]; + axis=i; + } + if (c2[i] < far){ + far = c2[i]; + } + if( (near > far) || (far < 0) ){ + return false; + } + } + } + + if (r_clip) + *r_clip=c1; + if (r_normal) { + *r_normal=Vector3(); + (*r_normal)[axis]=p_dir[axis]?-1:1; + } + + return true; + +} + + +bool AABB::intersects_segment(const Vector3& p_from, const Vector3& p_to,Vector3* r_clip,Vector3* r_normal) const { + + real_t min=0,max=1; + int axis=0; + float sign=0; + + for(int i=0;i<3;i++) { + real_t seg_from=p_from[i]; + real_t seg_to=p_to[i]; + real_t box_begin=pos[i]; + real_t box_end=box_begin+size[i]; + real_t cmin,cmax; + float csign; + + if (seg_from < seg_to) { + + if (seg_from > box_end || seg_to < box_begin) + return false; + real_t length=seg_to-seg_from; + cmin = (seg_from < box_begin)?((box_begin - seg_from)/length):0; + cmax = (seg_to > box_end)?((box_end - seg_from)/length):1; + csign=-1.0; + + } else { + + if (seg_to > box_end || seg_from < box_begin) + return false; + real_t length=seg_to-seg_from; + cmin = (seg_from > box_end)?(box_end - seg_from)/length:0; + cmax = (seg_to < box_begin)?(box_begin - seg_from)/length:1; + csign=1.0; + } + + if (cmin > min) { + min = cmin; + axis=i; + sign=csign; + } + if (cmax < max) + max = cmax; + if (max < min) + return false; + } + + + Vector3 rel=p_to-p_from; + + if (r_normal) { + Vector3 normal; + normal[axis]=sign; + *r_normal=normal; + } + + if (r_clip) + *r_clip=p_from+rel*min; + + return true; + +} + + +bool AABB::intersects_plane(const Plane &p_plane) const { + + Vector3 points[8] = { + Vector3( pos.x , pos.y , pos.z ), + Vector3( pos.x , pos.y , pos.z+size.z ), + Vector3( pos.x , pos.y+size.y , pos.z ), + Vector3( pos.x , pos.y+size.y , pos.z+size.z ), + Vector3( pos.x+size.x , pos.y , pos.z ), + Vector3( pos.x+size.x , pos.y , pos.z+size.z ), + Vector3( pos.x+size.x , pos.y+size.y , pos.z ), + Vector3( pos.x+size.x , pos.y+size.y , pos.z+size.z ), + }; + + bool over=false; + bool under=false; + + for (int i=0;i<8;i++) { + + if (p_plane.distance_to(points[i])>0) + over=true; + else + under=true; + + } + + return under && over; +} + + + +Vector3 AABB::get_longest_axis() const { + + Vector3 axis(1,0,0); + real_t max_size=size.x; + + if (size.y > max_size ) { + axis=Vector3(0,1,0); + max_size=size.y; + } + + if (size.z > max_size ) { + axis=Vector3(0,0,1); + max_size=size.z; + } + + return axis; +} +int AABB::get_longest_axis_index() const { + + int axis=0; + real_t max_size=size.x; + + if (size.y > max_size ) { + axis=1; + max_size=size.y; + } + + if (size.z > max_size ) { + axis=2; + max_size=size.z; + } + + return axis; +} + + +Vector3 AABB::get_shortest_axis() const { + + Vector3 axis(1,0,0); + real_t max_size=size.x; + + if (size.y < max_size ) { + axis=Vector3(0,1,0); + max_size=size.y; + } + + if (size.z < max_size ) { + axis=Vector3(0,0,1); + max_size=size.z; + } + + return axis; +} +int AABB::get_shortest_axis_index() const { + + int axis=0; + real_t max_size=size.x; + + if (size.y < max_size ) { + axis=1; + max_size=size.y; + } + + if (size.z < max_size ) { + axis=2; + max_size=size.z; + } + + return axis; +} + +AABB AABB::merge(const AABB& p_with) const { + + AABB aabb=*this; + aabb.merge_with(p_with); + return aabb; +} +AABB AABB::expand(const Vector3& p_vector) const { + AABB aabb=*this; + aabb.expand_to(p_vector); + return aabb; + +} +AABB AABB::grow(real_t p_by) const { + + AABB aabb=*this; + aabb.grow_by(p_by); + return aabb; +} +void AABB::grow_by(real_t p_amount) { + + pos.x-=p_amount; + pos.y-=p_amount; + pos.z-=p_amount; + size.x+=2.0*p_amount; + size.y+=2.0*p_amount; + size.z+=2.0*p_amount; +} + +void AABB::get_edge(int p_edge,Vector3& r_from,Vector3& r_to) const { + + ERR_FAIL_INDEX(p_edge,12); + switch(p_edge) { + + case 0:{ + + r_from=Vector3( pos.x+size.x , pos.y , pos.z ); + r_to=Vector3( pos.x , pos.y , pos.z ); + } break; + case 1:{ + + r_from=Vector3( pos.x+size.x , pos.y , pos.z+size.z ); + r_to=Vector3( pos.x+size.x , pos.y , pos.z ); + } break; + case 2:{ + r_from=Vector3( pos.x , pos.y , pos.z+size.z ); + r_to=Vector3( pos.x+size.x , pos.y , pos.z+size.z ); + + } break; + case 3:{ + + r_from=Vector3( pos.x , pos.y , pos.z ); + r_to=Vector3( pos.x , pos.y , pos.z+size.z ); + + } break; + case 4:{ + + r_from=Vector3( pos.x , pos.y+size.y , pos.z ); + r_to=Vector3( pos.x+size.x , pos.y+size.y , pos.z ); + } break; + case 5:{ + + r_from=Vector3( pos.x+size.x , pos.y+size.y , pos.z ); + r_to=Vector3( pos.x+size.x , pos.y+size.y , pos.z+size.z ); + } break; + case 6:{ + r_from=Vector3( pos.x+size.x , pos.y+size.y , pos.z+size.z ); + r_to=Vector3( pos.x , pos.y+size.y , pos.z+size.z ); + + } break; + case 7:{ + + r_from=Vector3( pos.x , pos.y+size.y , pos.z+size.z ); + r_to=Vector3( pos.x , pos.y+size.y , pos.z ); + + } break; + case 8:{ + + r_from=Vector3( pos.x , pos.y , pos.z+size.z ); + r_to=Vector3( pos.x , pos.y+size.y , pos.z+size.z ); + + } break; + case 9:{ + + r_from=Vector3( pos.x , pos.y , pos.z ); + r_to=Vector3( pos.x , pos.y+size.y , pos.z ); + + } break; + case 10:{ + + r_from=Vector3( pos.x+size.x , pos.y , pos.z ); + r_to=Vector3( pos.x+size.x , pos.y+size.y , pos.z ); + + } break; + case 11:{ + + r_from=Vector3( pos.x+size.x , pos.y , pos.z+size.z ); + r_to=Vector3( pos.x+size.x , pos.y+size.y , pos.z+size.z ); + + } break; + + } + +} + +AABB::operator String() const { + + return String()+pos +" - "+ size; +} diff --git a/core/math/aabb.h b/core/math/aabb.h new file mode 100644 index 00000000000..87be03cf160 --- /dev/null +++ b/core/math/aabb.h @@ -0,0 +1,319 @@ +/*************************************************************************/ +/* aabb.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 AABB_H +#define AABB_H + + + +#include "vector3.h" +#include "plane.h" +/** + * AABB / AABB (Axis Aligned Bounding Box) + * This is implemented by a point (pos) and the box size + */ + + + +class AABB { +public: + Vector3 pos; + Vector3 size; + + float get_area() const; /// get area + _FORCE_INLINE_ bool has_no_area() const { + + return (size.x<=CMP_EPSILON || size.y<=CMP_EPSILON || size.z<=CMP_EPSILON); + } + + _FORCE_INLINE_ bool has_no_surface() const { + + return (size.x<=CMP_EPSILON && size.y<=CMP_EPSILON && size.z<=CMP_EPSILON); + } + + const Vector3& get_pos() const { return pos; } + void set_pos(const Vector3& p_pos) { pos=p_pos; } + const Vector3& get_size() const { return size; } + void set_size(const Vector3& p_size) { size=p_size; } + + + bool operator==(const AABB& p_rval) const; + bool operator!=(const AABB& p_rval) const; + + _FORCE_INLINE_ bool intersects(const AABB& p_aabb) const; /// Both AABBs overlap + _FORCE_INLINE_ bool encloses(const AABB & p_aabb) const; /// p_aabb is completely inside this + + AABB merge(const AABB& p_with) const; + void merge_with(const AABB& p_aabb); ///merge with another AABB + AABB intersection(const AABB& p_aabb) const; ///get box where two intersect, empty if no intersection occurs + bool intersects_segment(const Vector3& p_from, const Vector3& p_to,Vector3* r_clip=NULL,Vector3* r_normal=NULL) const; + bool intersects_ray(const Vector3& p_from, const Vector3& p_dir,Vector3* r_clip=NULL,Vector3* r_normal=NULL) const; + _FORCE_INLINE_ bool intersects_convex_shape(const Plane *p_plane, int p_plane_count) const; + bool intersects_plane(const Plane &p_plane) const; + + _FORCE_INLINE_ bool has_point(const Vector3& p_point) const; + _FORCE_INLINE_ Vector3 get_support(const Vector3& p_normal) const; + + + Vector3 get_longest_axis() const; + int get_longest_axis_index() const; + _FORCE_INLINE_ real_t get_longest_axis_size() const; + + Vector3 get_shortest_axis() const; + int get_shortest_axis_index() const; + _FORCE_INLINE_ real_t get_shortest_axis_size() const; + + AABB grow(real_t p_by) const; + void grow_by(real_t p_amount); + + void get_edge(int p_edge,Vector3& r_from,Vector3& r_to) const; + _FORCE_INLINE_ Vector3 get_endpoint(int p_point) const; + + AABB expand(const Vector3& p_vector) const; + _FORCE_INLINE_ void project_range_in_plane(const Plane& p_plane,float &r_min,float& r_max) const; + _FORCE_INLINE_ void expand_to(const Vector3& p_vector); /** expand to contain a point if necesary */ + + operator String() const; + + _FORCE_INLINE_ AABB() {} + inline AABB(const Vector3 &p_pos,const Vector3& p_size) { pos=p_pos; size=p_size; } + + +}; + +inline bool AABB::intersects(const AABB& p_aabb) const { + + if ( pos.x > (p_aabb.pos.x + p_aabb.size.x) ) + return false; + if ( (pos.x+size.x) < p_aabb.pos.x ) + return false; + if ( pos.y > (p_aabb.pos.y + p_aabb.size.y) ) + return false; + if ( (pos.y+size.y) < p_aabb.pos.y ) + return false; + if ( pos.z > (p_aabb.pos.z + p_aabb.size.z) ) + return false; + if ( (pos.z+size.z) < p_aabb.pos.z ) + return false; + + return true; +} + + +inline bool AABB::encloses(const AABB & p_aabb) const { + + Vector3 src_min=pos; + Vector3 src_max=pos+size; + Vector3 dst_min=p_aabb.pos; + Vector3 dst_max=p_aabb.pos+p_aabb.size; + + return ( + (src_min.x <= dst_min.x) && + (src_max.x > dst_max.x) && + (src_min.y <= dst_min.y) && + (src_max.y > dst_max.y) && + (src_min.z <= dst_min.z) && + (src_max.z > dst_max.z) ); + +} + +Vector3 AABB::get_support(const Vector3& p_normal) const { + + Vector3 half_extents = size * 0.5; + Vector3 ofs = pos + half_extents; + + return Vector3( + (p_normal.x>0) ? -half_extents.x : half_extents.x, + (p_normal.y>0) ? -half_extents.y : half_extents.y, + (p_normal.z>0) ? -half_extents.z : half_extents.z + )+ofs; +} + + +Vector3 AABB::get_endpoint(int p_point) const { + + switch(p_point) { + case 0: return Vector3( pos.x , pos.y , pos.z ); + case 1: return Vector3( pos.x , pos.y , pos.z+size.z ); + case 2: return Vector3( pos.x , pos.y+size.y , pos.z ); + case 3: return Vector3( pos.x , pos.y+size.y , pos.z+size.z ); + case 4: return Vector3( pos.x+size.x , pos.y , pos.z ); + case 5: return Vector3( pos.x+size.x , pos.y , pos.z+size.z ); + case 6: return Vector3( pos.x+size.x , pos.y+size.y , pos.z ); + case 7: return Vector3( pos.x+size.x , pos.y+size.y , pos.z+size.z ); + }; + + ERR_FAIL_V(Vector3()); +} + +bool AABB::intersects_convex_shape(const Plane *p_planes, int p_plane_count) const { + +#if 1 + + Vector3 half_extents = size * 0.5; + Vector3 ofs = pos + half_extents; + + for(int i=0;i0) ? -half_extents.x : half_extents.x, + (p.normal.y>0) ? -half_extents.y : half_extents.y, + (p.normal.z>0) ? -half_extents.z : half_extents.z + ); + point+=ofs; + if (p.is_point_over(point)) + return false; + } + + return true; +#else + //cache all points to check against! +// #warning should be easy to optimize, just use the same as when taking the support and use only that point + Vector3 points[8] = { + Vector3( pos.x , pos.y , pos.z ), + Vector3( pos.x , pos.y , pos.z+size.z ), + Vector3( pos.x , pos.y+size.y , pos.z ), + Vector3( pos.x , pos.y+size.y , pos.z+size.z ), + Vector3( pos.x+size.x , pos.y , pos.z ), + Vector3( pos.x+size.x , pos.y , pos.z+size.z ), + Vector3( pos.x+size.x , pos.y+size.y , pos.z ), + Vector3( pos.x+size.x , pos.y+size.y , pos.z+size.z ), + }; + + for (int i=0;ipos.x+size.x) + return false; + if (p_point.y>pos.y+size.y) + return false; + if (p_point.z>pos.z+size.z) + return false; + + return true; +} + + +inline void AABB::expand_to(const Vector3& p_vector) { + + Vector3 begin=pos; + Vector3 end=pos+size; + + if (p_vector.xend.x) + end.x=p_vector.x; + if (p_vector.y>end.y) + end.y=p_vector.y; + if (p_vector.z>end.z) + end.z=p_vector.z; + + pos=begin; + size=end-begin; +} + +void AABB::project_range_in_plane(const Plane& p_plane,float &r_min,float& r_max) const { + + Vector3 half_extents( size.x * 0.5, size.y * 0.5, size.z * 0.5 ); + Vector3 center( pos.x + half_extents.x, pos.y + half_extents.y, pos.z + half_extents.z ); + + float length = p_plane.normal.abs().dot(half_extents); + float distance = p_plane.distance_to( center ); + r_min = distance - length; + r_max = distance + length; +} + +inline real_t AABB::get_longest_axis_size() const { + + real_t max_size=size.x; + + if (size.y > max_size ) { + max_size=size.y; + } + + if (size.z > max_size ) { + max_size=size.z; + } + + return max_size; +} + +inline real_t AABB::get_shortest_axis_size() const { + + real_t max_size=size.x; + + if (size.y < max_size ) { + max_size=size.y; + } + + if (size.z < max_size ) { + max_size=size.z; + } + + return max_size; +} + +typedef AABB Rect3; + +#endif // AABB_H diff --git a/core/math/bezier_curve.cpp b/core/math/bezier_curve.cpp new file mode 100644 index 00000000000..2676804945f --- /dev/null +++ b/core/math/bezier_curve.cpp @@ -0,0 +1,29 @@ +/*************************************************************************/ +/* bezier_curve.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 "bezier_curve.h" diff --git a/core/math/bezier_curve.h b/core/math/bezier_curve.h new file mode 100644 index 00000000000..6cc4c730dbb --- /dev/null +++ b/core/math/bezier_curve.h @@ -0,0 +1,35 @@ +/*************************************************************************/ +/* bezier_curve.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 BEZIER_CURVE_H +#define BEZIER_CURVE_H + + + + +#endif // BEZIER_CURVE_H diff --git a/core/math/bsp_tree.cpp b/core/math/bsp_tree.cpp new file mode 100644 index 00000000000..7f838b12154 --- /dev/null +++ b/core/math/bsp_tree.cpp @@ -0,0 +1,628 @@ +/*************************************************************************/ +/* bsp_tree.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 "bsp_tree.h" +#include "error_macros.h" +#include "print_string.h" + + +void BSP_Tree::from_aabb(const AABB& p_aabb) { + + planes.clear(); + + for(int i=0;i<3;i++) { + + Vector3 n; + n[i]=1; + planes.push_back(Plane(n,p_aabb.pos[i]+p_aabb.size[i])); + planes.push_back(Plane(-n,-p_aabb.pos[i])); + } + + nodes.clear(); + + for(int i=0;i<6;i++) { + + Node n; + n.plane=i; + n.under=(i==0)?UNDER_LEAF:i-1; + n.over=OVER_LEAF; + nodes.push_back(n); + } + + aabb=p_aabb; + error_radius=0; +} + +Vector BSP_Tree::get_nodes() const { + + return nodes; +} +Vector BSP_Tree::get_planes() const { + + return planes; +} + +AABB BSP_Tree::get_aabb() const { + + return aabb; +} + +int BSP_Tree::_get_points_inside(int p_node,const Vector3* p_points,int *p_indices, const Vector3& p_center,const Vector3& p_half_extents,int p_indices_count) const { + + + const Node *node =&nodes[p_node]; + const Plane &p = planes[node->plane]; + + Vector3 min( + (p.normal.x>0) ? -p_half_extents.x : p_half_extents.x, + (p.normal.y>0) ? -p_half_extents.y : p_half_extents.y, + (p.normal.z>0) ? -p_half_extents.z : p_half_extents.z + ); + Vector3 max=-min; + max+=p_center; + min+=p_center; + + float dist_min = p.distance_to(min); + float dist_max = p.distance_to(max); + + if ((dist_min * dist_max) < CMP_EPSILON ) { //intersection, test point by point + + + int under_count=0; + + //sort points, so the are under first, over last + for(int i=0;i0) { + if (node->under==UNDER_LEAF) { + total+=under_count; + } else { + total+=_get_points_inside(node->under,p_points,p_indices,p_center,p_half_extents,under_count); + } + } + + if (under_count!=p_indices_count) { + if (node->over==OVER_LEAF) { + //total+=0 //if they are over an OVER_LEAF, they are outside the model + } else { + total+=_get_points_inside(node->over,p_points,&p_indices[under_count],p_center,p_half_extents,p_indices_count-under_count); + } + } + + return total; + + } else if (dist_min > 0 ) { //all points over plane + + if (node->over==OVER_LEAF) { + + return 0; // all these points are not visible + } + + + return _get_points_inside(node->over,p_points,p_indices,p_center,p_half_extents,p_indices_count); + } else if (dist_min <= 0 ) { //all points behind plane + + if (node->under==UNDER_LEAF) { + + return p_indices_count; // all these points are visible + } + return _get_points_inside(node->under,p_points,p_indices,p_center,p_half_extents,p_indices_count); + } + + return 0; +} + +int BSP_Tree::get_points_inside(const Vector3* p_points,int p_point_count) const { + + + if (nodes.size()==0) + return 0; + +#if 1 +//this version is easier to debug, and and MUCH faster in real world cases + + int pass_count = 0; + const Node *nodesptr=&nodes[0]; + const Plane *planesptr=&planes[0]; + int plane_count=planes.size(); + int node_count=nodes.size(); + + if (node_count==0) // no nodes! + return 0; + + for(int i=0;i=node_count, false ); +#endif + + } + + if (pass) + pass_count++; + } + + return pass_count; + +#else +//this version scales better but it's slower for real world cases + + int *indices = (int*)alloca(p_point_count*sizeof(int)); + AABB bounds; + + for(int i=0;i=node_count, false ); +#endif + + steps++; + } + + return false; +} + + +static int _bsp_find_best_half_plane(const Face3* p_faces,const Vector& p_indices,float p_tolerance) { + + int ic = p_indices.size(); + const int*indices=p_indices.ptr(); + + int best_plane = -1; + float best_plane_cost = 1e20; + + // Loop to find the polygon that best divides the set. + + for (int i=0;ip_tolerance) { + + if (d > 0) + over++; + else + under++; + } + + } + + if (over && under) + num_spanning++; + else if (over) + num_over++; + else + num_under++; + + } + + + + //double split_cost = num_spanning / (double) face_count; + double relation = Math::abs(num_over-num_under) / (double) ic; + + // being honest, i never found a way to add split cost to the mix in a meaninguful way + // in this engine, also, will likely be ignored anyway + + double plane_cost = /*split_cost +*/ relation; + + //printf("plane %i, %i over, %i under, %i spanning, cost is %g\n",i,num_over,num_under,num_spanning,plane_cost); + if (plane_cost& p_indices,Vector &p_planes, Vector &p_nodes,float p_tolerance) { + + ERR_FAIL_COND_V( p_nodes.size() == BSP_Tree::MAX_NODES, -1 ); + + // should not reach here + ERR_FAIL_COND_V( p_indices.size() == 0, -1 ) + + int ic = p_indices.size(); + const int*indices=p_indices.ptr(); + + int divisor_idx = _bsp_find_best_half_plane(p_faces,p_indices,p_tolerance); + + // returned error + ERR_FAIL_COND_V( divisor_idx<0 , -1 ); + + + Vector faces_over; + Vector faces_under; + + Plane divisor_plane=p_faces[ indices[divisor_idx] ].get_plane(); + + for (int i=0;ip_tolerance) { + + if (d > 0) + over_count++; + else + under_count++; + } + } + + if (over_count) + faces_over.push_back( indices[i] ); + if (under_count) + faces_under.push_back( indices[i] ); + + } + + + + uint16_t over_idx=BSP_Tree::OVER_LEAF,under_idx=BSP_Tree::UNDER_LEAF; + + if (faces_over.size()>0) { //have facess above? + + int idx = _bsp_create_node( p_faces, faces_over, p_planes, p_nodes,p_tolerance ); + if (idx>=0) + over_idx=idx; + } + + if (faces_under.size()>0) { //have facess above? + + int idx = _bsp_create_node( p_faces,faces_under, p_planes, p_nodes,p_tolerance ); + if (idx>=0) + under_idx=idx; + } + + /* Create the node */ + + // find existing divisor plane + int divisor_plane_idx=-1; + + + for (int i=0;i plane_values; + plane_values.resize(planes.size()*4); + + for(int i=0;i dst_nodes; + dst_nodes.resize(nodes.size()*3); + + for(int i=0;i src_nodes = d["nodes"]; + ERR_FAIL_COND(src_nodes.size()%3); + + + if (d["planes"].get_type()==Variant::REAL_ARRAY) { + + DVector src_planes=d["planes"]; + int plane_count=src_planes.size(); + ERR_FAIL_COND(plane_count%4); + planes.resize(plane_count/4); + + if (plane_count) { + DVector::Read r = src_planes.read(); + for(int i=0;i::Read r = src_nodes.read(); + + for(int i=0;i& p_faces,float p_error_radius) { + + // compute aabb + + int face_count=p_faces.size(); + DVector::Read faces_r=p_faces.read(); + const Face3 *facesptr = faces_r.ptr(); + + + bool first=true; + + Vector indices; + + for (int i=0;i &p_nodes, const Vector &p_planes, const AABB& p_aabb,float p_error_radius) { + + nodes=p_nodes; + planes=p_planes; + aabb=p_aabb; + error_radius=p_error_radius; + +} + +BSP_Tree::~BSP_Tree() { + + +} diff --git a/core/math/bsp_tree.h b/core/math/bsp_tree.h new file mode 100644 index 00000000000..03bfd947cbb --- /dev/null +++ b/core/math/bsp_tree.h @@ -0,0 +1,141 @@ +/*************************************************************************/ +/* bsp_tree.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 BSP_TREE_H +#define BSP_TREE_H + +#include "plane.h" +#include "aabb.h" +#include "face3.h" +#include "vector.h" +#include "dvector.h" + +#include "variant.h" +/** + @author Juan Linietsky +*/ +class BSP_Tree { +public: + + enum { + + UNDER_LEAF=0xFFFF, + OVER_LEAF=0xFFFE, + MAX_NODES=0xFFFE, + MAX_PLANES=(1<<16) + }; + + struct Node { + + uint16_t plane; + uint16_t under; + uint16_t over; + }; + + +private: + // thanks to the properties of Vector, + // this class can be assigned and passed around between threads + // with no cost. + + Vector nodes; + Vector planes; + AABB aabb; + float error_radius; + + int _get_points_inside(int p_node,const Vector3* p_points,int *p_indices, const Vector3& p_center,const Vector3& p_half_extents,int p_indices_count) const; + + template + bool _test_convex(const Node* p_nodes, const Plane* p_planes,int p_current, const T& p_convex) const; + +public: + + bool is_empty() const { return nodes.size()==0; } + Vector get_nodes() const; + Vector get_planes() const; + AABB get_aabb() const; + + bool point_is_inside(const Vector3& p_point) const; + int get_points_inside(const Vector3* p_points, int p_point_count) const; + template + bool convex_is_inside(const T& p_convex) const; + + operator Variant() const; + + void from_aabb(const AABB& p_aabb); + + BSP_Tree(); + BSP_Tree(const Variant& p_variant); + BSP_Tree(const DVector& p_faces,float p_error_radius=0); + BSP_Tree(const Vector &p_nodes, const Vector &p_planes, const AABB& p_aabb,float p_error_radius=0); + ~BSP_Tree(); + +}; + +template +bool BSP_Tree::_test_convex(const Node* p_nodes, const Plane* p_planes,int p_current, const T& p_convex) const { + + if (p_current==UNDER_LEAF) + return true; + else if (p_current==OVER_LEAF) + return false; + + bool collided=false; + const Node&n=p_nodes[p_current]; + + const Plane& p=p_planes[n.plane]; + + float min,max; + p_convex.project_range(p.normal,min,max); + + bool go_under = min < p.d; + bool go_over = max >= p.d; + + if (go_under && _test_convex(p_nodes,p_planes,n.under,p_convex)) + collided=true; + if (go_over && _test_convex(p_nodes,p_planes,n.over,p_convex)) + collided=true; + + return collided; + +} + +template +bool BSP_Tree::convex_is_inside(const T& p_convex) const { + + int node_count = nodes.size(); + if (node_count==0) + return false; + const Node* nodes=&this->nodes[0]; + const Plane* planes = &this->planes[0]; + + return _test_convex(nodes,planes,node_count-1,p_convex); +} + + +#endif diff --git a/core/math/camera_matrix.cpp b/core/math/camera_matrix.cpp new file mode 100644 index 00000000000..52d77b6ebc0 --- /dev/null +++ b/core/math/camera_matrix.cpp @@ -0,0 +1,596 @@ +/*************************************************************************/ +/* camera_matrix.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 "camera_matrix.h" +#include "math_funcs.h" +#include "print_string.h" + +void CameraMatrix::set_identity() { + + for (int i=0;i<4;i++) { + + for (int j=0;j<4;j++) { + + matrix[i][j]=(i==j)?1:0; + } + } +} + + +void CameraMatrix::set_zero() { + + for (int i=0;i<4;i++) { + + for (int j=0;j<4;j++) { + + matrix[i][j]=0; + } + } +} + + +Plane CameraMatrix::xform4(const Plane& p_vec4) { + + Plane ret; + + ret.normal.x = matrix[0][0] * p_vec4.normal.x + matrix[1][0] * p_vec4.normal.y + matrix[2][0] * p_vec4.normal.z + matrix[3][0] * p_vec4.d; + ret.normal.y = matrix[0][1] * p_vec4.normal.x + matrix[1][1] * p_vec4.normal.y + matrix[2][1] * p_vec4.normal.z + matrix[3][1] * p_vec4.d; + ret.normal.z = matrix[0][2] * p_vec4.normal.x + matrix[1][2] * p_vec4.normal.y + matrix[2][2] * p_vec4.normal.z + matrix[3][2] * p_vec4.d; + ret.d = matrix[0][3] * p_vec4.normal.x + matrix[1][3] * p_vec4.normal.y + matrix[2][3] * p_vec4.normal.z + matrix[3][3] * p_vec4.d; + return ret; +} + +void CameraMatrix::set_perspective(float p_fovy_degrees, float p_aspect, float p_z_near, float p_z_far,bool p_flip_fov) { + + if (p_flip_fov) + p_fovy_degrees=get_fovy(p_fovy_degrees,p_aspect); + + + float sine, cotangent, deltaZ; + float radians = p_fovy_degrees / 2.0 * Math_PI / 180.0; + + deltaZ = p_z_far - p_z_near; + sine = Math::sin(radians); + + if ((deltaZ == 0) || (sine == 0) || (p_aspect == 0)) { + return ; + } + cotangent = Math::cos(radians) / sine; + + set_identity(); + + matrix[0][0] = cotangent / p_aspect; + matrix[1][1] = cotangent; + matrix[2][2] = -(p_z_far + p_z_near) / deltaZ; + matrix[2][3] = -1; + matrix[3][2] = -2 * p_z_near * p_z_far / deltaZ; + matrix[3][3] = 0; + +} + +void CameraMatrix::set_orthogonal(float p_left, float p_right, float p_bottom, float p_top, float p_znear, float p_zfar) { + + + set_identity(); + + matrix[0][0] = 2.0/(p_right-p_left); + matrix[3][0] = -((p_right+p_left)/(p_right-p_left)); + matrix[1][1] = 2.0/(p_top-p_bottom); + matrix[3][1] = -((p_top+p_bottom)/(p_top-p_bottom)); + matrix[2][2] = -2.0/(p_zfar-p_znear); + matrix[3][2] = -((p_zfar+p_znear)/(p_zfar-p_znear)); + matrix[3][3] = 1.0; + +} + +void CameraMatrix::set_orthogonal(float p_size, float p_aspect, float p_znear, float p_zfar,bool p_flip_fov) { + + if (p_flip_fov) { + p_size*=p_aspect; + } + + set_orthogonal(-p_size/2,+p_size/2,-p_size/p_aspect/2,+p_size/p_aspect/2,p_znear,p_zfar); +} + + + +void CameraMatrix::set_frustum(float p_left, float p_right, float p_bottom, float p_top, float p_near, float p_far) { + + ///@TODO, give a check to this. I'm not sure if it's working. + set_identity(); + + matrix[0][0]=(2*p_near) / (p_right-p_left); + matrix[0][2]=(p_right+p_left) / (p_right-p_left); + matrix[1][1]=(2*p_near) / (p_top-p_bottom); + matrix[1][2]=(p_top+p_bottom) / (p_top-p_bottom); + matrix[2][2]=-(p_far+p_near) / ( p_far-p_near); + matrix[2][3]=-(2*p_far*p_near) / (p_far-p_near); + matrix[3][2]=-1; + matrix[3][3]=0; + +} + + +float CameraMatrix::get_z_far() const { + + const float * matrix = (const float*)this->matrix; + Plane new_plane=Plane(matrix[ 3] - matrix[ 2], + matrix[ 7] - matrix[ 6], + matrix[11] - matrix[10], + matrix[15] - matrix[14]); + + new_plane.normal=-new_plane.normal; + new_plane.normalize(); + + return new_plane.d; +} +float CameraMatrix::get_z_near() const { + + const float * matrix = (const float*)this->matrix; + Plane new_plane=Plane(matrix[ 3] + matrix[ 2], + matrix[ 7] + matrix[ 6], + matrix[11] + matrix[10], + -matrix[15] - matrix[14]); + + new_plane.normalize(); + return new_plane.d; +} + +void CameraMatrix::get_viewport_size(float& r_width, float& r_height) const { + + const float * matrix = (const float*)this->matrix; + ///////--- Near Plane ---/////// + Plane near_plane=Plane(matrix[ 3] + matrix[ 2], + matrix[ 7] + matrix[ 6], + matrix[11] + matrix[10], + -matrix[15] - matrix[14]).normalized(); + + ///////--- Right Plane ---/////// + Plane right_plane=Plane(matrix[ 3] - matrix[ 0], + matrix[ 7] - matrix[ 4], + matrix[11] - matrix[ 8], + - matrix[15] + matrix[12]).normalized(); + + Plane top_plane=Plane(matrix[ 3] - matrix[ 1], + matrix[ 7] - matrix[ 5], + matrix[11] - matrix[ 9], + -matrix[15] + matrix[13]).normalized(); + + Vector3 res; + near_plane.intersect_3(right_plane,top_plane,&res); + + r_width=res.x; + r_height=res.y; +} + +bool CameraMatrix::get_endpoints(const Transform& p_transform, Vector3 *p_8points) const { + + const float * matrix = (const float*)this->matrix; + + ///////--- Near Plane ---/////// + Plane near_plane=Plane(matrix[ 3] + matrix[ 2], + matrix[ 7] + matrix[ 6], + matrix[11] + matrix[10], + -matrix[15] - matrix[14]).normalized(); + + ///////--- Far Plane ---/////// + Plane far_plane=Plane(matrix[ 2] - matrix[ 3], + matrix[ 6] - matrix[ 7], + matrix[10] - matrix[11], + matrix[15] - matrix[14]).normalized(); + + + ///////--- Right Plane ---/////// + Plane right_plane=Plane(matrix[ 0] - matrix[ 3], + matrix[ 4] - matrix[ 7], + matrix[8] - matrix[ 11], + - matrix[15] + matrix[12]).normalized(); + + ///////--- Top Plane ---/////// + Plane top_plane=Plane(matrix[ 1] - matrix[ 3], + matrix[ 5] - matrix[ 7], + matrix[9] - matrix[ 11], + -matrix[15] + matrix[13]).normalized(); + + Vector3 near_endpoint; + Vector3 far_endpoint; + + bool res=near_plane.intersect_3(right_plane,top_plane,&near_endpoint); + ERR_FAIL_COND_V(!res,false); + + res=far_plane.intersect_3(right_plane,top_plane,&far_endpoint); + ERR_FAIL_COND_V(!res,false); + + p_8points[0]=p_transform.xform( Vector3( near_endpoint.x, near_endpoint.y, near_endpoint.z ) ); + p_8points[1]=p_transform.xform( Vector3( near_endpoint.x,-near_endpoint.y, near_endpoint.z ) ); + p_8points[2]=p_transform.xform( Vector3(-near_endpoint.x, near_endpoint.y, near_endpoint.z ) ); + p_8points[3]=p_transform.xform( Vector3(-near_endpoint.x,-near_endpoint.y, near_endpoint.z ) ); + p_8points[4]=p_transform.xform( Vector3( far_endpoint.x, far_endpoint.y, far_endpoint.z ) ); + p_8points[5]=p_transform.xform( Vector3( far_endpoint.x,-far_endpoint.y, far_endpoint.z ) ); + p_8points[6]=p_transform.xform( Vector3(-far_endpoint.x, far_endpoint.y, far_endpoint.z ) ); + p_8points[7]=p_transform.xform( Vector3(-far_endpoint.x,-far_endpoint.y, far_endpoint.z ) ); + + return true; +} + +Vector CameraMatrix::get_projection_planes(const Transform& p_transform) const { + + /** Fast Plane Extraction from combined modelview/projection matrices. + * References: + * http://www.markmorley.com/opengl/frustumculling.html + * http://www2.ravensoft.com/users/ggribb/plane%20extraction.pdf + */ + + Vector planes; + + const float * matrix = (const float*)this->matrix; + + Plane new_plane; + + ///////--- Near Plane ---/////// + new_plane=Plane(matrix[ 3] + matrix[ 2], + matrix[ 7] + matrix[ 6], + matrix[11] + matrix[10], + matrix[15] + matrix[14]); + + new_plane.normal=-new_plane.normal; + new_plane.normalize(); + + planes.push_back( p_transform.xform(new_plane) ); + + ///////--- Far Plane ---/////// + new_plane=Plane(matrix[ 3] - matrix[ 2], + matrix[ 7] - matrix[ 6], + matrix[11] - matrix[10], + matrix[15] - matrix[14]); + + new_plane.normal=-new_plane.normal; + new_plane.normalize(); + + planes.push_back( p_transform.xform(new_plane) ); + + + ///////--- Left Plane ---/////// + new_plane=Plane(matrix[ 3] + matrix[ 0], + matrix[ 7] + matrix[ 4], + matrix[11] + matrix[ 8], + matrix[15] + matrix[12]); + + new_plane.normal=-new_plane.normal; + new_plane.normalize(); + + planes.push_back( p_transform.xform(new_plane) ); + + + ///////--- Top Plane ---/////// + new_plane=Plane(matrix[ 3] - matrix[ 1], + matrix[ 7] - matrix[ 5], + matrix[11] - matrix[ 9], + matrix[15] - matrix[13]); + + + new_plane.normal=-new_plane.normal; + new_plane.normalize(); + + planes.push_back( p_transform.xform(new_plane) ); + + + ///////--- Right Plane ---/////// + new_plane=Plane(matrix[ 3] - matrix[ 0], + matrix[ 7] - matrix[ 4], + matrix[11] - matrix[ 8], + matrix[15] - matrix[12]); + + + new_plane.normal=-new_plane.normal; + new_plane.normalize(); + + planes.push_back( p_transform.xform(new_plane) ); + + + ///////--- Bottom Plane ---/////// + new_plane=Plane(matrix[ 3] + matrix[ 1], + matrix[ 7] + matrix[ 5], + matrix[11] + matrix[ 9], + matrix[15] + matrix[13]); + + + new_plane.normal=-new_plane.normal; + new_plane.normalize(); + + planes.push_back( p_transform.xform(new_plane) ); + + return planes; +} + + + +CameraMatrix CameraMatrix::inverse() const { + + CameraMatrix cm = *this; + cm.invert(); + return cm; +} + +void CameraMatrix::invert() { + + int i,j,k; + int pvt_i[4], pvt_j[4]; /* Locations of pivot matrix */ + float pvt_val; /* Value of current pivot element */ + float hold; /* Temporary storage */ + float determinat; /* Determinant */ + + determinat = 1.0; + for (k=0; k<4; k++) { + /** Locate k'th pivot element **/ + pvt_val=matrix[k][k]; /** Initialize for search **/ + pvt_i[k]=k; + pvt_j[k]=k; + for (i=k; i<4; i++) { + for (j=k; j<4; j++) { + if (Math::absd(matrix[i][j]) > Math::absd(pvt_val)) { + pvt_i[k]=i; + pvt_j[k]=j; + pvt_val=matrix[i][j]; + } + } + } + + /** Product of pivots, gives determinant when finished **/ + determinat*=pvt_val; + if (Math::absd(determinat)<1e-7) { + return; //(false); /** Matrix is singular (zero determinant). **/ + } + + /** "Interchange" rows (with sign change stuff) **/ + i=pvt_i[k]; + if (i!=k) { /** If rows are different **/ + for (j=0; j<4; j++) { + hold=-matrix[k][j]; + matrix[k][j]=matrix[i][j]; + matrix[i][j]=hold; + } + } + + /** "Interchange" columns **/ + j=pvt_j[k]; + if (j!=k) { /** If columns are different **/ + for (i=0; i<4; i++) { + hold=-matrix[i][k]; + matrix[i][k]=matrix[i][j]; + matrix[i][j]=hold; + } + } + + /** Divide column by minus pivot value **/ + for (i=0; i<4; i++) { + if (i!=k) matrix[i][k]/=( -pvt_val) ; + } + + /** Reduce the matrix **/ + for (i=0; i<4; i++) { + hold = matrix[i][k]; + for (j=0; j<4; j++) { + if (i!=k && j!=k) matrix[i][j]+=hold*matrix[k][j]; + } + } + + /** Divide row by pivot **/ + for (j=0; j<4; j++) { + if (j!=k) matrix[k][j]/=pvt_val; + } + + /** Replace pivot by reciprocal (at last we can touch it). **/ + matrix[k][k] = 1.0/pvt_val; + } + + /* That was most of the work, one final pass of row/column interchange */ + /* to finish */ + for (k=4-2; k>=0; k--) { /* Don't need to work with 1 by 1 corner*/ + i=pvt_j[k]; /* Rows to swap correspond to pivot COLUMN */ + if (i!=k) { /* If rows are different */ + for(j=0; j<4; j++) { + hold = matrix[k][j]; + matrix[k][j]=-matrix[i][j]; + matrix[i][j]=hold; + } + } + + j=pvt_i[k]; /* Columns to swap correspond to pivot ROW */ + if (j!=k) /* If columns are different */ + for (i=0; i<4; i++) { + hold=matrix[i][k]; + matrix[i][k]=-matrix[i][j]; + matrix[i][j]=hold; + } + } + + +} + +CameraMatrix::CameraMatrix() { + + set_identity(); +} + +CameraMatrix CameraMatrix::operator*(const CameraMatrix& p_matrix) const { + + CameraMatrix new_matrix; + + for( int j = 0; j < 4; j++ ) { + for( int i = 0; i < 4; i++ ) { + real_t ab = 0; + for( int k = 0; k < 4; k++ ) + ab += matrix[k][i] * p_matrix.matrix[j][k] ; + new_matrix.matrix[j][i] = ab; + } + } + + return new_matrix; +} + +void CameraMatrix::set_light_bias() { + + float *m=&matrix[0][0]; + + m[0]=0.5, + m[1]=0.0, + m[2]=0.0, + m[3]=0.0, + m[4]=0.0, + m[5]=0.5, + m[6]=0.0, + m[7]=0.0, + m[8]=0.0, + m[9]=0.0, + m[10]=0.5, + m[11]=0.0, + m[12]=0.5, + m[13]=0.5, + m[14]=0.5, + m[15]=1.0; + +} + +CameraMatrix::operator String() const { + + String str; + for (int i=0;i<4;i++) + for (int j=0;j<4;j++) + str+=String((j>0)?", ":"\n")+rtos(matrix[i][j]); + + return str; +} + +float CameraMatrix::get_aspect() const { + + float w,h; + get_viewport_size(w,h); + return w/h; +} + +float CameraMatrix::get_fov() const { + const float * matrix = (const float*)this->matrix; + + Plane right_plane=Plane(matrix[ 3] - matrix[ 0], + matrix[ 7] - matrix[ 4], + matrix[11] - matrix[ 8], + - matrix[15] + matrix[12]).normalized(); + + return Math::rad2deg(Math::acos(Math::abs(right_plane.normal.x)))*2.0; +} + + +void CameraMatrix::make_scale(const Vector3 &p_scale) { + + set_identity(); + matrix[0][0]=p_scale.x; + matrix[1][1]=p_scale.y; + matrix[2][2]=p_scale.z; + +} + +void CameraMatrix::scale_translate_to_fit(const AABB& p_aabb) { + + Vector3 min = p_aabb.pos; + Vector3 max = p_aabb.pos+p_aabb.size; + + + matrix[0][0]=2/(max.x-min.x); + matrix[1][0]=0; + matrix[2][0]=0; + matrix[3][0]=-(max.x+min.x)/(max.x-min.x); + + matrix[0][1]=0; + matrix[1][1]=2/(max.y-min.y); + matrix[2][1]=0; + matrix[3][1]=-(max.y+min.y)/(max.y-min.y); + + matrix[0][2]=0; + matrix[1][2]=0; + matrix[2][2]=2/(max.z-min.z); + matrix[3][2]=-(max.z+min.z)/(max.z-min.z); + + matrix[0][3]=0; + matrix[1][3]=0; + matrix[2][3]=0; + matrix[3][3]=1; +} + +CameraMatrix::operator Transform() const { + + Transform tr; + const float *m=&matrix[0][0]; + + tr.basis.elements[0][0]=m[0]; + tr.basis.elements[1][0]=m[1]; + tr.basis.elements[2][0]=m[2]; + + tr.basis.elements[0][1]=m[4]; + tr.basis.elements[1][1]=m[5]; + tr.basis.elements[2][1]=m[6]; + + tr.basis.elements[0][2]=m[8]; + tr.basis.elements[1][2]=m[9]; + tr.basis.elements[2][2]=m[10]; + + tr.origin.x=m[12]; + tr.origin.y=m[13]; + tr.origin.z=m[14]; + + return tr; +} + +CameraMatrix::CameraMatrix(const Transform& p_transform) { + + const Transform &tr = p_transform; + float *m=&matrix[0][0]; + + m[0]=tr.basis.elements[0][0]; + m[1]=tr.basis.elements[1][0]; + m[2]=tr.basis.elements[2][0]; + m[3]=0.0; + m[4]=tr.basis.elements[0][1]; + m[5]=tr.basis.elements[1][1]; + m[6]=tr.basis.elements[2][1]; + m[7]=0.0; + m[8]=tr.basis.elements[0][2]; + m[9]=tr.basis.elements[1][2]; + m[10]=tr.basis.elements[2][2]; + m[11]=0.0; + m[12]=tr.origin.x; + m[13]=tr.origin.y; + m[14]=tr.origin.z; + m[15]=1.0; +} + +CameraMatrix::~CameraMatrix() +{ +} + + diff --git a/core/math/camera_matrix.h b/core/math/camera_matrix.h new file mode 100644 index 00000000000..6ffcb0ed0ba --- /dev/null +++ b/core/math/camera_matrix.h @@ -0,0 +1,106 @@ +/*************************************************************************/ +/* camera_matrix.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 CAMERA_MATRIX_H +#define CAMERA_MATRIX_H + +#include "transform.h" +/** + @author Juan Linietsky +*/ + + + +struct CameraMatrix { + + enum Planes { + PLANE_NEAR, + PLANE_FAR, + PLANE_LEFT, + PLANE_TOP, + PLANE_RIGHT, + PLANE_BOTTOM + }; + + float matrix[4][4]; + + + void set_identity(); + void set_zero(); + void set_light_bias(); + void set_perspective(float p_fovy_degrees, float p_aspect, float p_z_near, float p_z_far,bool p_flip_fov=false); + void set_orthogonal(float p_left, float p_right, float p_bottom, float p_top, float p_znear, float p_zfar); + void set_orthogonal(float p_size, float p_aspect, float p_znear, float p_zfar,bool p_flip_fov=false); + void set_frustum(float p_left, float p_right, float p_bottom, float p_top, float p_near, float p_far); + + static float get_fovy(float p_fovx,float p_aspect) { + + return Math::atan(p_aspect * Math::tan(p_fovx * 0.5))*2.0; + } + + float get_z_far() const; + float get_z_near() const; + float get_aspect() const; + float get_fov() const; + + Vector get_projection_planes(const Transform& p_transform) const; + + bool get_endpoints(const Transform& p_transform,Vector3 *p_8points) const; + void get_viewport_size(float& r_width, float& r_height) const; + + void invert(); + CameraMatrix inverse() const; + + CameraMatrix operator*(const CameraMatrix& p_matrix) const; + + Plane xform4(const Plane& p_vec4); + _FORCE_INLINE_ Vector3 xform(const Vector3& p_vec3) const; + + operator String() const; + + void scale_translate_to_fit(const AABB& p_aabb); + void make_scale(const Vector3 &p_scale); + operator Transform() const; + + CameraMatrix(); + CameraMatrix(const Transform& p_transform); + ~CameraMatrix(); + +}; + +Vector3 CameraMatrix::xform(const Vector3& p_vec3) const { + + Vector3 ret; + ret.x = matrix[0][0] * p_vec3.x + matrix[1][0] * p_vec3.y + matrix[2][0] * p_vec3.z + matrix[3][0]; + ret.y = matrix[0][1] * p_vec3.x + matrix[1][1] * p_vec3.y + matrix[2][1] * p_vec3.z + matrix[3][1]; + ret.z = matrix[0][2] * p_vec3.x + matrix[1][2] * p_vec3.y + matrix[2][2] * p_vec3.z + matrix[3][2]; + float w = matrix[0][3] * p_vec3.x + matrix[1][3] * p_vec3.y + matrix[2][3] * p_vec3.z + matrix[3][3]; + return ret/w; +} + +#endif diff --git a/core/math/face3.cpp b/core/math/face3.cpp new file mode 100644 index 00000000000..9cdf31ed843 --- /dev/null +++ b/core/math/face3.cpp @@ -0,0 +1,359 @@ +/*************************************************************************/ +/* face3.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 "face3.h" +#include "geometry.h" + +int Face3::split_by_plane(const Plane& p_plane,Face3 p_res[3],bool p_is_point_over[3]) const { + + ERR_FAIL_COND_V(is_degenerate(),0); + + + Vector3 above[4]; + int above_count=0; + + Vector3 below[4]; + int below_count=0; + + for (int i=0;i<3;i++) { + + if (p_plane.has_point( vertex[i], CMP_EPSILON )) { // point is in plane + + ERR_FAIL_COND_V(above_count>=4,0); + above[above_count++]=vertex[i]; + ERR_FAIL_COND_V(below_count>=4,0); + below[below_count++]=vertex[i]; + + } else { + + if (p_plane.is_point_over( vertex[i])) { + //Point is over + ERR_FAIL_COND_V(above_count>=4,0); + above[above_count++]=vertex[i]; + + } else { + //Point is under + ERR_FAIL_COND_V(below_count>=4,0); + below[below_count++]=vertex[i]; + } + + /* Check for Intersection between this and the next vertex*/ + + Vector3 inters; + if (!p_plane.intersects_segment( vertex[i],vertex[(i+1)%3],&inters)) + continue; + + /* Intersection goes to both */ + ERR_FAIL_COND_V(above_count>=4,0); + above[above_count++]=inters; + ERR_FAIL_COND_V(below_count>=4,0); + below[below_count++]=inters; + } + } + + int polygons_created=0; + + ERR_FAIL_COND_V( above_count>=4 && below_count>=4 , 0 ); //bug in the algo + + if (above_count>=3) { + + p_res[polygons_created]=Face3( above[0], above[1], above[2] ); + p_is_point_over[polygons_created]=true; + polygons_created++; + + if (above_count==4) { + + p_res[polygons_created]=Face3( above[2], above[3], above[0] ); + p_is_point_over[polygons_created]=true; + polygons_created++; + + } + } + + if (below_count>=3) { + + p_res[polygons_created]=Face3( below[0], below[1], below[2] ); + p_is_point_over[polygons_created]=false; + polygons_created++; + + if (below_count==4) { + + p_res[polygons_created]=Face3( below[2], below[3], below[0] ); + p_is_point_over[polygons_created]=false; + polygons_created++; + + } + } + + return polygons_created; +} + + + +bool Face3::intersects_ray(const Vector3& p_from,const Vector3& p_dir,Vector3 * p_intersection) const { + + return Geometry::ray_intersects_triangle(p_from,p_dir,vertex[0],vertex[1],vertex[2],p_intersection); + +} + +bool Face3::intersects_segment(const Vector3& p_from,const Vector3& p_dir,Vector3 * p_intersection) const { + + return Geometry::segment_intersects_triangle(p_from,p_dir,vertex[0],vertex[1],vertex[2],p_intersection); + +} + + +bool Face3::is_degenerate() const { + + Vector3 normal=vec3_cross(vertex[0]-vertex[1], vertex[0]-vertex[2]); + return (normal.length_squared() < CMP_EPSILON2); +} + + +Face3::Side Face3::get_side_of(const Face3& p_face,ClockDirection p_clock_dir) const { + + int over=0,under=0; + + Plane plane=get_plane(p_clock_dir); + + for (int i=0;i<3;i++) { + + const Vector3 &v=p_face.vertex[i]; + + if (plane.has_point(v)) //coplanar, dont bother + continue; + + if (plane.is_point_over(v)) + over++; + else + under++; + + } + + if ( over > 0 && under == 0 ) + return SIDE_OVER; + else if (under > 0 && over ==0 ) + return SIDE_UNDER; + else if (under ==0 && over == 0) + return SIDE_COPLANAR; + else + return SIDE_SPANNING; + +} + +Vector3 Face3::get_random_point_inside() const { + + float a=Math::random(0,1); + float b=Math::random(0,1); + if (a>b) { + SWAP(a,b); + } + + return vertex[0]*a + vertex[1]*(b-a) + vertex[2]*(1.0-b); + +} + +Plane Face3::get_plane(ClockDirection p_dir) const { + + return Plane( vertex[0], vertex[1], vertex[2] , p_dir ); + +} + +Vector3 Face3::get_median_point() const { + + return (vertex[0] + vertex[1] + vertex[2])/3.0; +} + + +real_t Face3::get_area() const { + + return vec3_cross(vertex[0]-vertex[1], vertex[0]-vertex[2]).length(); +} + +ClockDirection Face3::get_clock_dir() const { + + + Vector3 normal=vec3_cross(vertex[0]-vertex[1], vertex[0]-vertex[2]); + //printf("normal is %g,%g,%g x %g,%g,%g- wtfu is %g\n",tofloat(normal.x),tofloat(normal.y),tofloat(normal.z),tofloat(vertex[0].x),tofloat(vertex[0].y),tofloat(vertex[0].z),tofloat( normal.dot( vertex[0] ) ) ); + return ( normal.dot( vertex[0] ) >= 0 ) ? CLOCKWISE : COUNTERCLOCKWISE; + +} + + +bool Face3::intersects_aabb(const AABB& p_aabb) const { + + /** TEST PLANE **/ + if (!p_aabb.intersects_plane( get_plane() )) + return false; + + /** TEST FACE AXIS */ + +#define TEST_AXIS(m_ax)\ + {\ + float aabb_min=p_aabb.pos.m_ax;\ + float aabb_max=p_aabb.pos.m_ax+p_aabb.size.m_ax;\ + float tri_min,tri_max;\ + for (int i=0;i<3;i++) {\ + if (i==0 || vertex[i].m_ax > tri_max)\ + tri_max=vertex[i].m_ax;\ + if (i==0 || vertex[i].m_ax < tri_min)\ + tri_min=vertex[i].m_ax;\ + }\ +\ + if (tri_max r_max) + r_max=d; + + if (i==0 || d < r_min) + r_min=d; + } +} + + + +void Face3::get_support(const Vector3& p_normal,const Transform& p_transform,Vector3 *p_vertices,int* p_count,int p_max) const { + +#define _FACE_IS_VALID_SUPPORT_TRESHOLD 0.98 +#define _EDGE_IS_VALID_SUPPORT_TRESHOLD 0.05 + + if (p_max<=0) + return; + + Vector3 n=p_transform.basis.xform_inv(p_normal); + + /** TEST FACE AS SUPPORT **/ + if (get_plane().normal.dot(n) > _FACE_IS_VALID_SUPPORT_TRESHOLD) { + + *p_count=MIN(3,p_max); + + for (int i=0;i<*p_count;i++) { + + p_vertices[i]=p_transform.xform(vertex[i]); + } + + return; + + } + + /** FIND SUPPORT VERTEX **/ + + int vert_support_idx=-1; + float support_max; + + for (int i=0;i<3;i++) { + + float d=n.dot(vertex[i]); + + if (i==0 || d > support_max) { + support_max=d; + vert_support_idx=i; + } + } + + /** TEST EDGES AS SUPPORT **/ + + for (int i=0;i<3;i++) { + + if (i!=vert_support_idx && i+1!=vert_support_idx) + continue; + + // check if edge is valid as a support + float dot=(vertex[i]-vertex[(i+1)%3]).normalized().dot(n); + dot=ABS(dot); + if (dot < _EDGE_IS_VALID_SUPPORT_TRESHOLD) { + + *p_count=MIN(2,p_max); + + for (int j=0;j<*p_count;j++) + p_vertices[j]=p_transform.xform(vertex[(j+i)%3]); + + return; + } + } + + + *p_count=1; + p_vertices[0]=p_transform.xform(vertex[vert_support_idx]); + +} + + + diff --git a/core/math/face3.h b/core/math/face3.h new file mode 100644 index 00000000000..3a62f7f8126 --- /dev/null +++ b/core/math/face3.h @@ -0,0 +1,97 @@ +/*************************************************************************/ +/* face3.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 FACE3_H +#define FACE3_H + +#include "vector3.h" +#include "plane.h" +#include "aabb.h" +#include "transform.h" + +class Face3 { +public: + + enum Side { + SIDE_OVER, + SIDE_UNDER, + SIDE_SPANNING, + SIDE_COPLANAR + }; + + + Vector3 vertex[3]; + + /** + * + * @param p_plane plane used to split the face + * @param p_res array of at least 3 faces, amount used in functio return + * @param p_is_point_over array of at least 3 booleans, determining which face is over the plane, amount used in functio return + * @param _epsilon constant used for numerical error rounding, to add "thickness" to the plane (so coplanar points can happen) + * @return amount of faces generated by the split, either 0 (means no split possible), 2 or 3 + */ + + int split_by_plane(const Plane& p_plane,Face3 *p_res,bool *p_is_point_over) const; + + Plane get_plane(ClockDirection p_dir=CLOCKWISE) const; + Vector3 get_random_point_inside() const; + + + Side get_side_of(const Face3& p_face,ClockDirection p_clock_dir=CLOCKWISE) const; + + bool is_degenerate() const; + real_t get_area() const; + + Vector3 get_median_point() const; + + bool intersects_ray(const Vector3& p_from,const Vector3& p_dir,Vector3 * p_intersection=0) const; + bool intersects_segment(const Vector3& p_from,const Vector3& p_dir,Vector3 * p_intersection=0) const; + + ClockDirection get_clock_dir() const; ///< todo, test if this is returning the proper clockwisity + + void get_support(const Vector3& p_normal,const Transform& p_transform,Vector3 *p_vertices,int* p_count,int p_max) const; + void project_range(const Vector3& p_normal,const Transform& p_transform,float& r_min, float& r_max) const; + + AABB get_aabb() const { + + AABB aabb( vertex[0], Vector3() ); + aabb.expand_to( vertex[1] ); + aabb.expand_to( vertex[2] ); + return aabb; + } + + bool intersects_aabb(const AABB& p_aabb) const; + operator String() const; + + inline Face3() {} + inline Face3(const Vector3 &p_v1,const Vector3 &p_v2,const Vector3 &p_v3) { vertex[0]=p_v1; vertex[1]=p_v2; vertex[2]=p_v3; } + +}; + + +#endif // FACE3_H diff --git a/core/math/geometry.cpp b/core/math/geometry.cpp new file mode 100644 index 00000000000..cb76b9ed0f8 --- /dev/null +++ b/core/math/geometry.cpp @@ -0,0 +1,1006 @@ +/*************************************************************************/ +/* geometry.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 "geometry.h" +#include "print_string.h" + + + +void Geometry::MeshData::optimize_vertices() { + + Map vtx_remap; + + for(int i=0;i new_vertices; + new_vertices.resize(vtx_remap.size()); + + for(int i=0;i > (*Geometry::_decompose_func)(const Vector& p_polygon)=NULL; + +struct _FaceClassify { + + struct _Link { + + int face; + int edge; + void clear() { face=-1; edge=-1; } + _Link() { face=-1; edge=-1; } + }; + bool valid; + int group; + _Link links[3]; + Face3 face; + _FaceClassify() { + group=-1; + valid=false; + }; +}; + +static bool _connect_faces(_FaceClassify *p_faces, int len, int p_group) { + /* connect faces, error will occur if an edge is shared between more than 2 faces */ + /* clear connections */ + + bool error=false; + + for (int i=0;i=0) + return false; + + p_faces[p_index].group=p_group; + + for (int i=0;i<3;i++) { + + ERR_FAIL_INDEX_V(p_faces[p_index].links[i].face,len,true); + _group_face(p_faces,len,p_faces[p_index].links[i].face,p_group); + } + + return true; +} + + +DVector< DVector< Face3 > > Geometry::separate_objects( DVector< Face3 > p_array ) { + + DVector< DVector< Face3 > > objects; + + int len = p_array.size(); + + DVector::Read r=p_array.read(); + + const Face3* arrayptr = r.ptr(); + + DVector< _FaceClassify> fc; + + fc.resize( len ); + + DVector< _FaceClassify >::Write fcw=fc.write(); + + _FaceClassify * _fcptr = fcw.ptr(); + + for (int i=0;i >() ); // invalid geometry + } + + /* group connected faces in separate objects */ + + int group=0; + for (int i=0;i=0) { + + objects.resize(group); + DVector< DVector >::Write obw=objects.write(); + DVector< Face3 > *group_faces = obw.ptr(); + + for (int i=0;i=0 && _fcptr[i].group1?2:1; + int div_y=len_y>1?2:1; + int div_z=len_z>1?2:1; + +#define _SPLIT(m_i,m_div,m_v,m_len_v,m_new_v,m_new_len_v)\ + if (m_div==1) {\ + m_new_v=m_v;\ + m_new_len_v=1; \ + } else if (m_i==0) {\ + m_new_v=m_v;\ + m_new_len_v=m_len_v/2;\ + } else {\ + m_new_v=m_v+m_len_v/2;\ + m_new_len_v=m_len_v-m_len_v/2; \ + } + + int new_x; + int new_len_x; + int new_y; + int new_len_y; + int new_z; + int new_len_z; + + for (int i=0;i=len_y); + } break; + case _CELL_PREV_Y_NEG: { + y--; + ERR_FAIL_COND(y<0); + } break; + case _CELL_PREV_X_POS: { + x++; + ERR_FAIL_COND(x>=len_x); + } break; + case _CELL_PREV_X_NEG: { + x--; + ERR_FAIL_COND(x<0); + } break; + case _CELL_PREV_Z_POS: { + z++; + ERR_FAIL_COND(z>=len_z); + } break; + case _CELL_PREV_Z_NEG: { + z--; + ERR_FAIL_COND(z<0); + } break; + default: { + ERR_FAIL(); + } + } + continue; + } + + //printf("attempting new cell!\n"); + + int next_x=x,next_y=y,next_z=z; + uint8_t prev=0; + + switch(c&_CELL_STEP_MASK) { + + case _CELL_STEP_Y_POS: { + + next_y++; + prev=_CELL_PREV_Y_NEG; + } break; + case _CELL_STEP_Y_NEG: { + next_y--; + prev=_CELL_PREV_Y_POS; + } break; + case _CELL_STEP_X_POS: { + next_x++; + prev=_CELL_PREV_X_NEG; + } break; + case _CELL_STEP_X_NEG: { + next_x--; + prev=_CELL_PREV_X_POS; + } break; + case _CELL_STEP_Z_POS: { + next_z++; + prev=_CELL_PREV_Z_NEG; + } break; + case _CELL_STEP_Z_NEG: { + next_z--; + prev=_CELL_PREV_Z_POS; + } break; + default: ERR_FAIL(); + + } + + //printf("testing if new cell will be ok...!\n"); + + if (next_x<0 || next_x>=len_x) + continue; + if (next_y<0 || next_y>=len_y) + continue; + if (next_z<0 || next_z>=len_z) + continue; + + //printf("testing if new cell is traversable\n"); + + if (p_cell_status[next_x][next_y][next_z]&3) + continue; + + //printf("move to it\n"); + + x=next_x; + y=next_y; + z=next_z; + p_cell_status[x][y][z]|=prev; + } +} + +static inline void _build_faces(uint8_t*** p_cell_status,int x,int y,int z,int len_x,int len_y,int len_z,DVector& p_faces) { + + ERR_FAIL_INDEX(x,len_x); + ERR_FAIL_INDEX(y,len_y); + ERR_FAIL_INDEX(z,len_z); + + if (p_cell_status[x][y][z]&_CELL_EXTERIOR) + return; + +/* static const Vector3 vertices[8]={ + Vector3(0,0,0), + Vector3(0,0,1), + Vector3(0,1,0), + Vector3(0,1,1), + Vector3(1,0,0), + Vector3(1,0,1), + Vector3(1,1,0), + Vector3(1,1,1), + }; +*/ +#define vert(m_idx) Vector3( (m_idx&4)>>2, (m_idx&2)>>1, m_idx&1 ) + + static const uint8_t indices[6][4]={ + {7,6,4,5}, + {7,3,2,6}, + {7,5,1,3}, + {0,2,3,1}, + {0,1,5,4}, + {0,4,6,2}, + + }; +/* + + {0,1,2,3}, + {0,1,4,5}, + {0,2,4,6}, + {4,5,6,7}, + {2,3,7,6}, + {1,3,5,7}, + + {0,2,3,1}, + {0,1,5,4}, + {0,4,6,2}, + {7,6,4,5}, + {7,3,2,6}, + {7,5,1,3}, +*/ + + for (int i=0;i<6;i++) { + + Vector3 face_points[4]; + int disp_x=x+((i%3)==0?((i<3)?1:-1):0); + int disp_y=y+(((i-1)%3)==0?((i<3)?1:-1):0); + int disp_z=z+(((i-2)%3)==0?((i<3)?1:-1):0); + + bool plot=false; + + if (disp_x<0 || disp_x>=len_x) + plot=true; + if (disp_y<0 || disp_y>=len_y) + plot=true; + if (disp_z<0 || disp_z>=len_z) + plot=true; + + if (!plot && (p_cell_status[disp_x][disp_y][disp_z]&_CELL_EXTERIOR)) + plot=true; + + if (!plot) + continue; + + for (int j=0;j<4;j++) + face_points[j]=vert( indices[i][j] ) + Vector3(x,y,z); + + p_faces.push_back( + Face3( + face_points[0], + face_points[1], + face_points[2] + ) + ); + + p_faces.push_back( + Face3( + face_points[2], + face_points[3], + face_points[0] + ) + ); + + } + +} + +DVector< Face3 > Geometry::wrap_geometry( DVector< Face3 > p_array,float *p_error ) { + +#define _MIN_SIZE 1.0 +#define _MAX_LENGTH 20 + + int face_count=p_array.size(); + DVector::Read facesr=p_array.read(); + const Face3 *faces = facesr.ptr(); + + AABB global_aabb; + + for(int i=0;i wrapped_faces; + + for (int i=0;i::Write wrapped_facesw=wrapped_faces.write(); + Face3* wrapped_faces_ptr=wrapped_facesw.ptr(); + + for(int i=0;i &p_planes) { + + MeshData mesh; + + +#define SUBPLANE_SIZE 1024.0 + + float subplane_size = 1024.0; // should compute this from the actual plane + for (int i=0;i0.95) + ref=Vector3(0.0,0.0,1.0); // change axis + + Vector3 right = p.normal.cross(ref).normalized(); + Vector3 up = p.normal.cross( right ).normalized(); + + Vector< Vector3 > vertices; + + Vector3 center = p.get_any_point(); + // make a quad clockwise + vertices.push_back( center - up * subplane_size + right * subplane_size ); + vertices.push_back( center - up * subplane_size - right * subplane_size ); + vertices.push_back( center + up * subplane_size - right * subplane_size ); + vertices.push_back( center + up * subplane_size + right * subplane_size ); + + for (int j=0;j new_vertices; + Plane clip=p_planes[j]; + + if (clip.normal.dot(p.normal)>0.95) + continue; + + if (vertices.size()<3) + break; + + for(int k=0;k Geometry::build_box_planes(const Vector3& p_extents) { + + DVector planes; + + planes.push_back( Plane( Vector3(1,0,0), p_extents.x ) ); + planes.push_back( Plane( Vector3(-1,0,0), p_extents.x ) ); + planes.push_back( Plane( Vector3(0,1,0), p_extents.y ) ); + planes.push_back( Plane( Vector3(0,-1,0), p_extents.y ) ); + planes.push_back( Plane( Vector3(0,0,1), p_extents.z ) ); + planes.push_back( Plane( Vector3(0,0,-1), p_extents.z ) ); + + return planes; +} + +DVector Geometry::build_cylinder_planes(float p_radius, float p_height, int p_sides, Vector3::Axis p_axis) { + + DVector planes; + + for (int i=0;i Geometry::build_sphere_planes(float p_radius, int p_lats,int p_lons, Vector3::Axis p_axis) { + + + DVector planes; + + Vector3 axis; + axis[p_axis]=1.0; + + Vector3 axis_neg; + axis_neg[(p_axis+1)%3]=1.0; + axis_neg[(p_axis+2)%3]=1.0; + axis_neg[p_axis]=-1.0; + + for (int i=0;i Geometry::build_capsule_planes(float p_radius, float p_height, int p_sides, int p_lats, Vector3::Axis p_axis) { + + DVector planes; + + Vector3 axis; + axis[p_axis]=1.0; + + Vector3 axis_neg; + axis_neg[(p_axis+1)%3]=1.0; + axis_neg[(p_axis+2)%3]=1.0; + axis_neg[p_axis]=-1.0; + + for (int i=0;i +*/ + +class Geometry { + Geometry(); +public: + + + + + static float get_closest_points_between_segments( const Vector2& p1,const Vector2& q1, const Vector2& p2,const Vector2& q2, Vector2& c1, Vector2& c2) { + + Vector2 d1 = q1 - p1; // Direction vector of segment S1 + Vector2 d2 = q2 - p2; // Direction vector of segment S2 + Vector2 r = p1 - p2; + float a = d1.dot(d1); // Squared length of segment S1, always nonnegative + float e = d2.dot(d2); // Squared length of segment S2, always nonnegative + float f = d2.dot(r); + float s,t; + // Check if either or both segments degenerate into points + if (a <= CMP_EPSILON && e <= CMP_EPSILON) { + // Both segments degenerate into points + c1 = p1; + c2 = p2; + return Math::sqrt((c1 - c2).dot(c1 - c2)); + } + if (a <= CMP_EPSILON) { + // First segment degenerates into a point + s = 0.0f; + t = f / e; // s = 0 => t = (b*s + f) / e = f / e + t = CLAMP(t, 0.0f, 1.0f); + } else { + float c = d1.dot(r); + if (e <= CMP_EPSILON) { + // Second segment degenerates into a point + t = 0.0f; + s = CLAMP(-c / a, 0.0f, 1.0f); // t = 0 => s = (b*t - c) / a = -c / a + } else { + // The general nondegenerate case starts here + float b = d1.dot(d2); + float denom = a*e-b*b; // Always nonnegative + // If segments not parallel, compute closest point on L1 to L2 and + // clamp to segment S1. Else pick arbitrary s (here 0) + if (denom != 0.0f) { + s = CLAMP((b*f - c*e) / denom, 0.0f, 1.0f); + } else + s = 0.0f; + // Compute point on L2 closest to S1(s) using + // t = Dot((P1 + D1*s) - P2,D2) / Dot(D2,D2) = (b*s + f) / e + t = (b*s + f) / e; + + //If t in [0,1] done. Else clamp t, recompute s for the new value + // of t using s = Dot((P2 + D2*t) - P1,D1) / Dot(D1,D1)= (t*b - c) / a + // and clamp s to [0, 1] + if (t < 0.0f) { + t = 0.0f; + s = CLAMP(-c / a, 0.0f, 1.0f); + } else if (t > 1.0f) { + t = 1.0f; + s = CLAMP((b - c) / a, 0.0f, 1.0f); + } + } + } + c1 = p1 + d1 * s; + c2 = p2 + d2 * t; + return Math::sqrt((c1 - c2).dot(c1 - c2)); + } + + + static void get_closest_points_between_segments(const Vector3& p1,const Vector3& p2,const Vector3& q1,const Vector3& q2,Vector3& c1, Vector3& c2) + { + //do the function 'd' as defined by pb. I think is is dot product of some sort +#define d_of(m,n,o,p) ( (m.x - n.x) * (o.x - p.x) + (m.y - n.y) * (o.y - p.y) + (m.z - n.z) * (o.z - p.z) ) + + //caluclate the parpametric position on the 2 curves, mua and mub + float mua = ( d_of(p1,q1,q2,q1) * d_of(q2,q1,p2,p1) - d_of(p1,q1,p2,p1) * d_of(q2,q1,q2,q1) ) / ( d_of(p2,p1,p2,p1) * d_of(q2,q1,q2,q1) - d_of(q2,q1,p2,p1) * d_of(q2,q1,p2,p1) ); + float mub = ( d_of(p1,q1,q2,q1) + mua * d_of(q2,q1,p2,p1) ) / d_of(q2,q1,q2,q1); + + //clip the value between [0..1] constraining the solution to lie on the original curves + if (mua < 0) mua = 0; + if (mub < 0) mub = 0; + if (mua > 1) mua = 1; + if (mub > 1) mub = 1; + c1 = p1.linear_interpolate(p2,mua); + c2 = q1.linear_interpolate(q2,mub); + } + + static float get_closest_distance_between_segments( const Vector3& p_from_a,const Vector3& p_to_a, const Vector3& p_from_b,const Vector3& p_to_b) { + Vector3 u = p_to_a - p_from_a; + Vector3 v = p_to_b - p_from_b; + Vector3 w = p_from_a - p_to_a; + real_t a = u.dot(u); // always >= 0 + real_t b = u.dot(v); + real_t c = v.dot(v); // always >= 0 + real_t d = u.dot(w); + real_t e = v.dot(w); + real_t D = a*c - b*b; // always >= 0 + real_t sc, sN, sD = D; // sc = sN / sD, default sD = D >= 0 + real_t tc, tN, tD = D; // tc = tN / tD, default tD = D >= 0 + + // compute the line parameters of the two closest points + if (D < CMP_EPSILON) { // the lines are almost parallel + sN = 0.0; // force using point P0 on segment S1 + sD = 1.0; // to prevent possible division by 0.0 later + tN = e; + tD = c; + } + else { // get the closest points on the infinite lines + sN = (b*e - c*d); + tN = (a*e - b*d); + if (sN < 0.0) { // sc < 0 => the s=0 edge is visible + sN = 0.0; + tN = e; + tD = c; + } + else if (sN > sD) { // sc > 1 => the s=1 edge is visible + sN = sD; + tN = e + b; + tD = c; + } + } + + if (tN < 0.0) { // tc < 0 => the t=0 edge is visible + tN = 0.0; + // recompute sc for this edge + if (-d < 0.0) + sN = 0.0; + else if (-d > a) + sN = sD; + else { + sN = -d; + sD = a; + } + } + else if (tN > tD) { // tc > 1 => the t=1 edge is visible + tN = tD; + // recompute sc for this edge + if ((-d + b) < 0.0) + sN = 0; + else if ((-d + b) > a) + sN = sD; + else { + sN = (-d + b); + sD = a; + } + } + // finally do the division to get sc and tc + sc = (Math::abs(sN) < CMP_EPSILON ? 0.0 : sN / sD); + tc = (Math::abs(tN) < CMP_EPSILON ? 0.0 : tN / tD); + + // get the difference of the two closest points + Vector3 dP = w + (sc * u) - (tc * v); // = S1(sc) - S2(tc) + + return dP.length(); // return the closest distance + } + + static inline bool ray_intersects_triangle( const Vector3& p_from, const Vector3& p_dir, const Vector3& p_v0,const Vector3& p_v1,const Vector3& p_v2,Vector3* r_res=0) { + Vector3 e1=p_v1-p_v0; + Vector3 e2=p_v2-p_v0; + Vector3 h = p_dir.cross(e2); + real_t a =e1.dot(h); + if (a>-CMP_EPSILON && a < CMP_EPSILON) // parallel test + return false; + + real_t f=1.0/a; + + Vector3 s=p_from-p_v0; + real_t u = f * s.dot(h); + + if ( u< 0.0 || u > 1.0) + return false; + + Vector3 q=s.cross(e1); + + real_t v = f * p_dir.dot(q); + + if (v < 0.0 || u + v > 1.0) + return false; + + // at this stage we can compute t to find out where + // the intersection point is on the line + real_t t = f * e2.dot(q); + + if (t > 0.00001) {// ray intersection + if (r_res) + *r_res=p_from+p_dir*t; + return true; + } else // this means that there is a line intersection + // but not a ray intersection + return false; + } + + static inline bool segment_intersects_triangle( const Vector3& p_from, const Vector3& p_to, const Vector3& p_v0,const Vector3& p_v1,const Vector3& p_v2,Vector3* r_res=0) { + + Vector3 rel=p_to-p_from; + Vector3 e1=p_v1-p_v0; + Vector3 e2=p_v2-p_v0; + Vector3 h = rel.cross(e2); + real_t a =e1.dot(h); + if (a>-CMP_EPSILON && a < CMP_EPSILON) // parallel test + return false; + + real_t f=1.0/a; + + Vector3 s=p_from-p_v0; + real_t u = f * s.dot(h); + + if ( u< 0.0 || u > 1.0) + return false; + + Vector3 q=s.cross(e1); + + real_t v = f * rel.dot(q); + + if (v < 0.0 || u + v > 1.0) + return false; + + // at this stage we can compute t to find out where + // the intersection point is on the line + real_t t = f * e2.dot(q); + + if (t > CMP_EPSILON && t<=1.0) {// ray intersection + if (r_res) + *r_res=p_from+rel*t; + return true; + } else // this means that there is a line intersection + // but not a ray intersection + return false; + } + + static inline bool segment_intersects_sphere( const Vector3& p_from, const Vector3& p_to, const Vector3& p_sphere_pos,real_t p_sphere_radius,Vector3* r_res=0,Vector3 *r_norm=0) { + + + Vector3 sphere_pos=p_sphere_pos-p_from; + Vector3 rel=(p_to-p_from); + float rel_l=rel.length(); + if (rel_l=p_sphere_radius) + return false; + + float inters_d2=p_sphere_radius*p_sphere_radius - ray_distance*ray_distance; + float inters_d=sphere_d; + + if (inters_d2>=CMP_EPSILON) + inters_d-=Math::sqrt(inters_d2); + + // check in segment + if (inters_d<0 || inters_d>rel_l) + return false; + + Vector3 result=p_from+normal*inters_d;; + + if (r_res) + *r_res=result; + if (r_norm) + *r_norm=(result-p_sphere_pos).normalized(); + + return true; + } + + static inline bool segment_intersects_cylinder( const Vector3& p_from, const Vector3& p_to, float p_height,float p_radius,Vector3* r_res=0,Vector3 *r_norm=0) { + + Vector3 rel=(p_to-p_from); + float rel_l=rel.length(); + if (rel_l=p_radius) + return false; // too far away + + // convert to 2D + float w2=p_radius*p_radius-dist*dist; + if (w2 box_end || seg_to < box_begin) + return false; + real_t length=seg_to-seg_from; + cmin = (seg_from < box_begin)?((box_begin - seg_from)/length):0; + cmax = (seg_to > box_end)?((box_end - seg_from)/length):1; + + } else { + + if (seg_to > box_end || seg_from < box_begin) + return false; + real_t length=seg_to-seg_from; + cmin = (seg_from > box_end)?(box_end - seg_from)/length:0; + cmax = (seg_to < box_begin)?(box_begin - seg_from)/length:1; + } + + if (cmin > min) { + min = cmin; + axis=i; + } + if (cmax < max) + max = cmax; + if (max < min) + return false; + } + + + // convert to 3D again + Vector3 result = p_from + (rel*min); + Vector3 res_normal = result; + + if (axis==0) { + res_normal.z=0; + } else { + res_normal.x=0; + res_normal.y=0; + } + + + res_normal.normalize(); + + if (r_res) + *r_res=result; + if (r_norm) + *r_norm=res_normal; + + return true; + } + + + static bool segment_intersects_convex(const Vector3& p_from, const Vector3& p_to,const Plane* p_planes, int p_plane_count,Vector3 *p_res, Vector3 *p_norm) { + + real_t min=-1e20,max=1e20; + + Vector3 rel=p_to-p_from; + real_t rel_l=rel.length(); + + if (rel_l0) { + //backwards facing plane + if (distmin) { + min=dist; + min_index=i; + } + } + } + + if (max<=min || min<0 || min>rel_l || min_index==-1) // exit conditions + return false; // no intersection + + if (p_res) + *p_res=p_from+dir*min; + if (p_norm) + *p_norm=p_planes[min_index].normal; + + return true; + } + + static Vector3 get_closest_point_to_segment(const Vector3& p_point, const Vector3 *p_segment) { + + Vector3 p=p_point-p_segment[0]; + Vector3 n=p_segment[1]-p_segment[0]; + float l =n.length(); + if (l<1e-10) + return p_segment[0]; // both points are the same, just give any + n/=l; + + float d=n.dot(p); + + if (d<=0.0) + return p_segment[0]; // before first point + else if (d>=l) + return p_segment[1]; // after first point + else + return p_segment[0]+n*d; // inside + } + + static Vector3 get_closest_point_to_segment_uncapped(const Vector3& p_point, const Vector3 *p_segment) { + + Vector3 p=p_point-p_segment[0]; + Vector3 n=p_segment[1]-p_segment[0]; + float l =n.length(); + if (l<1e-10) + return p_segment[0]; // both points are the same, just give any + n/=l; + + float d=n.dot(p); + + return p_segment[0]+n*d; // inside + } + + static Vector2 get_closest_point_to_segment_2d(const Vector2& p_point, const Vector2 *p_segment) { + + Vector2 p=p_point-p_segment[0]; + Vector2 n=p_segment[1]-p_segment[0]; + float l =n.length(); + if (l<1e-10) + return p_segment[0]; // both points are the same, just give any + n/=l; + + float d=n.dot(p); + + if (d<=0.0) + return p_segment[0]; // before first point + else if (d>=l) + return p_segment[1]; // after first point + else + return p_segment[0]+n*d; // inside + } + static Vector2 get_closest_point_to_segment_uncapped_2d(const Vector2& p_point, const Vector2 *p_segment) { + + Vector2 p=p_point-p_segment[0]; + Vector2 n=p_segment[1]-p_segment[0]; + float l =n.length(); + if (l<1e-10) + return p_segment[0]; // both points are the same, just give any + n/=l; + + float d=n.dot(p); + + return p_segment[0]+n*d; // inside + } + + static bool segment_intersects_segment_2d(const Vector2& p_from_a,const Vector2& p_to_a,const Vector2& p_from_b,const Vector2& p_to_b,Vector2* r_result) { + + Vector2 B = p_to_a-p_from_a; + Vector2 C = p_from_b-p_from_a; + Vector2 D = p_to_b-p_from_a; + + real_t ABlen = B.dot(B); + if (ABlen<=0) + return false; + Vector2 Bn = B/ABlen; + C = Vector2( C.x*Bn.x + C.y*Bn.y, C.y*Bn.x - C.x*Bn.y ); + D = Vector2( D.x*Bn.x + D.y*Bn.y, D.y*Bn.x - D.x*Bn.y ); + + if ((C.y<0 && D.y<0) || (C.y>=0 && D.y>=0)) + return false; + + float ABpos=D.x+(C.x-D.x)*D.y/(D.y-C.y); + + // Fail if segment C-D crosses line A-B outside of segment A-B. + if (ABpos<0 || ABpos>1.0) + return false; + + // (4) Apply the discovered position to line A-B in the original coordinate system. + if (r_result) + *r_result=p_from_a+B*ABpos; + + return true; + } + + + static inline bool point_in_projected_triangle(const Vector3& p_point,const Vector3& p_v1,const Vector3& p_v2,const Vector3& p_v3) { + + + Vector3 face_n = (p_v1-p_v3).cross(p_v1-p_v2); + + Vector3 n1 = (p_point-p_v3).cross(p_point-p_v2); + + if (face_n.dot(n1)<0) + return false; + + Vector3 n2 = (p_v1-p_v3).cross(p_v1-p_point); + + if (face_n.dot(n2)<0) + return false; + + Vector3 n3 = (p_v1-p_point).cross(p_v1-p_v2); + + if (face_n.dot(n3)<0) + return false; + + return true; + + } + + static inline bool triangle_sphere_intersection_test(const Vector3 *p_triangle,const Vector3& p_normal,const Vector3& p_sphere_pos, real_t p_sphere_radius,Vector3& r_triangle_contact,Vector3& r_sphere_contact) { + + float d=p_normal.dot(p_sphere_pos)-p_normal.dot(p_triangle[0]); + + if (d > p_sphere_radius || d < -p_sphere_radius) // not touching the plane of the face, return + return false; + + Vector3 contact=p_sphere_pos - (p_normal*d); + + /** 2nd) TEST INSIDE TRIANGLE **/ + + + if (Geometry::point_in_projected_triangle(contact,p_triangle[0],p_triangle[1],p_triangle[2])) { + r_triangle_contact=contact; + r_sphere_contact=p_sphere_pos-p_normal*p_sphere_radius; + //printf("solved inside triangle\n"); + return true; + } + + /** 3rd TEST INSIDE EDGE CYLINDERS **/ + + const Vector3 verts[4]={p_triangle[0],p_triangle[1],p_triangle[2],p_triangle[0]}; // for() friendly + + for (int i=0;i<3;i++) { + + // check edge cylinder + + Vector3 n1=verts[i]-verts[i+1]; + Vector3 n2=p_sphere_pos-verts[i+1]; + + ///@TODO i could discard by range here to make the algorithm quicker? dunno.. + + // check point within cylinder radius + Vector3 axis =n1.cross(n2).cross(n1); + axis.normalize(); // ugh + + float ad=axis.dot(n2); + + if (ABS(ad)>p_sphere_radius) { + // no chance with this edge, too far away + continue; + } + + // check point within edge capsule cylinder + /** 4th TEST INSIDE EDGE POINTS **/ + + float sphere_at = n1.dot(n2); + + if (sphere_at>=0 && sphere_at= 0 && res1 <= 1) ? res1 : -1; + + } + + + static Vector triangulate_polygon(const Vector& p_polygon) { + + Vector triangles; + if (!Triangulate::triangulate(p_polygon,triangles)) + return Vector(); //fail + return triangles; + } + + static Vector< Vector > (*_decompose_func)(const Vector& p_polygon); + static Vector< Vector > decompose_polygon(const Vector& p_polygon) { + + if (_decompose_func) + return _decompose_func(p_polygon); + + return Vector< Vector >(); + + } + + + static DVector< DVector< Face3 > > separate_objects( DVector< Face3 > p_array ); + + static DVector< Face3 > wrap_geometry( DVector< Face3 > p_array, float *p_error=NULL ); ///< create a "wrap" that encloses the given geometry + + + struct MeshData { + + struct Face { + Plane plane; + Vector indices; + }; + + Vector faces; + + struct Edge { + + int a,b; + }; + + Vector edges; + + Vector< Vector3 > vertices; + + void optimize_vertices(); + }; + + + static MeshData build_convex_mesh(const DVector &p_planes); + static DVector build_sphere_planes(float p_radius, int p_lats, int p_lons, Vector3::Axis p_axis=Vector3::AXIS_Z); + static DVector build_box_planes(const Vector3& p_extents); + static DVector build_cylinder_planes(float p_radius, float p_height, int p_sides, Vector3::Axis p_axis=Vector3::AXIS_Z); + static DVector build_capsule_planes(float p_radius, float p_height, int p_sides, int p_lats, Vector3::Axis p_axis=Vector3::AXIS_Z); + + +}; + + + +#endif diff --git a/core/math/math_2d.cpp b/core/math/math_2d.cpp new file mode 100644 index 00000000000..dbeaea6e757 --- /dev/null +++ b/core/math/math_2d.cpp @@ -0,0 +1,597 @@ +/*************************************************************************/ +/* math_2d.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 "math_2d.h" + + +real_t Vector2::atan2() const { + + return Math::atan2(x,y); +} + +float Vector2::length() const { + + return Math::sqrt( x*x + y*y ); +} + +float Vector2::length_squared() const { + + return x*x + y*y; +} + +void Vector2::normalize() { + + float l = x*x + y*y; + if (l!=0) { + + l=Math::sqrt(l); + x/=l; + y/=l; + } +} + +Vector2 Vector2::normalized() const { + + Vector2 v=*this; + v.normalize(); + return v; +} + +float Vector2::distance_to(const Vector2& p_vector2) const { + + return Math::sqrt( (x-p_vector2.x)*(x-p_vector2.x) + (y-p_vector2.y)*(y-p_vector2.y)); +} + +float Vector2::distance_squared_to(const Vector2& p_vector2) const { + + return (x-p_vector2.x)*(x-p_vector2.x) + (y-p_vector2.y)*(y-p_vector2.y); +} + +float Vector2::angle_to(const Vector2& p_vector2) const { + + return Math::atan2( x-p_vector2.x, y - p_vector2.y ); +} + +float Vector2::dot(const Vector2& p_other) const { + + return x*p_other.x + y*p_other.y; +} + +float Vector2::cross(const Vector2& p_other) const { + + return x*p_other.y - y*p_other.x; +} + +Vector2 Vector2::cross(real_t p_other) const { + + return Vector2(p_other*y,-p_other*x); +} + + +Vector2 Vector2::operator+(const Vector2& p_v) const { + + return Vector2(x+p_v.x,y+p_v.y); +} +void Vector2::operator+=(const Vector2& p_v) { + + x+=p_v.x; y+=p_v.y; +} +Vector2 Vector2::operator-(const Vector2& p_v) const { + + return Vector2(x-p_v.x,y-p_v.y); +} +void Vector2::operator-=(const Vector2& p_v) { + + x-=p_v.x; y-=p_v.y; +} + +Vector2 Vector2::operator*(const Vector2 &p_v1) const { + + return Vector2(x * p_v1.x, y * p_v1.y); +}; + +Vector2 Vector2::operator*(const float &rvalue) const { + + return Vector2(x * rvalue, y * rvalue); +}; +void Vector2::operator*=(const float &rvalue) { + + x *= rvalue; y *= rvalue; +}; + +Vector2 Vector2::operator/(const Vector2 &p_v1) const { + + return Vector2(x / p_v1.x, y / p_v1.y); +}; + +Vector2 Vector2::operator/(const float &rvalue) const { + + return Vector2(x / rvalue, y / rvalue); +}; + +void Vector2::operator/=(const float &rvalue) { + + x /= rvalue; y /= rvalue; +}; + +Vector2 Vector2::operator-() const { + + return Vector2(-x,-y); +} + +bool Vector2::operator==(const Vector2& p_vec2) const { + + return x==p_vec2.x && y==p_vec2.y; +} +bool Vector2::operator!=(const Vector2& p_vec2) const { + + return x!=p_vec2.x || y!=p_vec2.y; +} +Vector2 Vector2::floor() const { + + return Vector2( Math::floor(x), Math::floor(y) ); +} + +Vector2 Vector2::rotated(float p_by) const { + + Vector2 v; + v.set_rotation(atan2()+p_by); + v*=length(); + return v; +} + +Vector2 Vector2::project(const Vector2& p_vec) const { + + Vector2 v1=p_vec; + Vector2 v2=*this; + return v2 * ( v1.dot(v2) / v2.dot(v2)); +} + +Vector2 Vector2::snapped(const Vector2& p_by) const { + + return Vector2( + Math::stepify(x,p_by.x), + Math::stepify(y,p_by.y) + ); +} + +Vector2 Vector2::clamped(real_t p_len) const { + + return *this; + real_t l = length(); + Vector2 v = *this; + if (l>0 && p_len box_end || seg_to < box_begin) + return false; + real_t length=seg_to-seg_from; + cmin = (seg_from < box_begin)?((box_begin - seg_from)/length):0; + cmax = (seg_to > box_end)?((box_end - seg_from)/length):1; + csign=-1.0; + + } else { + + if (seg_to > box_end || seg_from < box_begin) + return false; + real_t length=seg_to-seg_from; + cmin = (seg_from > box_end)?(box_end - seg_from)/length:0; + cmax = (seg_to < box_begin)?(box_begin - seg_from)/length:1; + csign=1.0; + } + + if (cmin > min) { + min = cmin; + axis=i; + sign=csign; + } + if (cmax < max) + max = cmax; + if (max < min) + return false; + } + + + Vector2 rel=p_to-p_from; + + if (r_normal) { + Vector2 normal; + normal[axis]=sign; + *r_normal=normal; + } + + if (r_pos) + *r_pos=p_from+rel*min; + + return true; +} + +/* Point2i */ + +Point2i Point2i::operator+(const Point2i& p_v) const { + + return Point2i(x+p_v.x,y+p_v.y); +} +void Point2i::operator+=(const Point2i& p_v) { + + x+=p_v.x; y+=p_v.y; +} +Point2i Point2i::operator-(const Point2i& p_v) const { + + return Point2i(x-p_v.x,y-p_v.y); +} +void Point2i::operator-=(const Point2i& p_v) { + + x-=p_v.x; y-=p_v.y; +} + +Point2i Point2i::operator*(const Point2i &p_v1) const { + + return Point2i(x * p_v1.x, y * p_v1.y); +}; + +Point2i Point2i::operator*(const int &rvalue) const { + + return Point2i(x * rvalue, y * rvalue); +}; +void Point2i::operator*=(const int &rvalue) { + + x *= rvalue; y *= rvalue; +}; + +Point2i Point2i::operator/(const Point2i &p_v1) const { + + return Point2i(x / p_v1.x, y / p_v1.y); +}; + +Point2i Point2i::operator/(const int &rvalue) const { + + return Point2i(x / rvalue, y / rvalue); +}; + +void Point2i::operator/=(const int &rvalue) { + + x /= rvalue; y /= rvalue; +}; + +Point2i Point2i::operator-() const { + + return Point2i(-x,-y); +} + +bool Point2i::operator==(const Point2i& p_vec2) const { + + return x==p_vec2.x && y==p_vec2.y; +} +bool Point2i::operator!=(const Point2i& p_vec2) const { + + return x!=p_vec2.x || y!=p_vec2.y; +} + +void Matrix32::invert() { + + SWAP(elements[0][1],elements[1][0]); + elements[2] = basis_xform(-elements[2]); +} + +Matrix32 Matrix32::inverse() const { + + Matrix32 inv=*this; + inv.invert(); + return inv; + +} + +void Matrix32::affine_invert() { + + float det = elements[0][0]*elements[1][1] - elements[1][0]*elements[0][1]; + ERR_FAIL_COND(det==0); + float idet = 1.0 / det; + + SWAP( elements[0][0],elements[1][1] ); + elements[0]*=Vector2(idet,-idet); + elements[1]*=Vector2(-idet,idet); + + elements[2] = basis_xform(-elements[2]); + +} + +Matrix32 Matrix32::affine_inverse() const { + + Matrix32 inv=*this; + inv.affine_invert(); + return inv; +} + +void Matrix32::rotate(real_t p_phi) { + + Matrix32 rot(p_phi,Vector2()); + *this *= rot; +} + +real_t Matrix32::get_rotation() const { + + return Math::atan2(elements[1].x,elements[1].y); +} + +void Matrix32::set_rotation(real_t p_rot) { + + real_t cr = Math::cos(p_rot); + real_t sr = Math::sin(p_rot); + elements[0][0]=cr; + elements[1][1]=cr; + elements[0][1]=-sr; + elements[1][0]=sr; +} + +Matrix32::Matrix32(real_t p_rot, const Vector2& p_pos) { + + real_t cr = Math::cos(p_rot); + real_t sr = Math::sin(p_rot); + elements[0][0]=cr; + elements[1][1]=cr; + elements[0][1]=-sr; + elements[1][0]=sr; + elements[2]=p_pos; +} + +Vector2 Matrix32::get_scale() const { + + return Vector2( elements[0].length(), elements[1].length() ); +} + +void Matrix32::scale(const Vector2& p_scale) { + + elements[0]*=p_scale; + elements[1]*=p_scale; + elements[2]*=p_scale; +} +void Matrix32::scale_basis(const Vector2& p_scale) { + + elements[0]*=p_scale; + elements[1]*=p_scale; + +} +void Matrix32::translate( real_t p_tx, real_t p_ty) { + + translate(Vector2(p_tx,p_ty)); +} +void Matrix32::translate( const Vector2& p_translation ) { + + elements[2]+=basis_xform(p_translation); +} + + +void Matrix32::orthonormalize() { + + // Gram-Schmidt Process + + Vector2 x=elements[0]; + Vector2 y=elements[1]; + + x.normalize(); + y = (y-x*(x.dot(y))); + y.normalize(); + + elements[0]=x; + elements[1]=y; +} +Matrix32 Matrix32::orthonormalized() const { + + Matrix32 on=*this; + on.orthonormalize(); + return on; + +} + +bool Matrix32::operator==(const Matrix32& p_transform) const { + + for(int i=0;i<3;i++) { + if (elements[i]!=p_transform.elements[i]) + return false; + } + + return true; +} + +bool Matrix32::operator!=(const Matrix32& p_transform) const { + + for(int i=0;i<3;i++) { + if (elements[i]!=p_transform.elements[i]) + return true; + } + + return false; + +} + +void Matrix32::operator*=(const Matrix32& p_transform) { + + elements[2] = xform(p_transform.elements[2]); + + float x0,x1,y0,y1; +/* + x0 = p_transform.tdotx(elements[0]); + x1 = p_transform.tdoty(elements[0]); + y0 = p_transform.tdotx(elements[1]); + y1 = p_transform.tdoty(elements[1]);*/ + + x0 = tdotx(p_transform.elements[0]); + x1 = tdoty(p_transform.elements[0]); + y0 = tdotx(p_transform.elements[1]); + y1 = tdoty(p_transform.elements[1]); + + elements[0][0]=x0; + elements[0][1]=x1; + elements[1][0]=y0; + elements[1][1]=y1; +} + + +Matrix32 Matrix32::operator*(const Matrix32& p_transform) const { + + Matrix32 t = *this; + t*=p_transform; + return t; + +} + +Matrix32 Matrix32::scaled(const Vector2& p_scale) const { + + Matrix32 copy=*this; + copy.scale(p_scale); + return copy; + +} + +Matrix32 Matrix32::basis_scaled(const Vector2& p_scale) const { + + Matrix32 copy=*this; + copy.scale_basis(p_scale); + return copy; + +} + +Matrix32 Matrix32::untranslated() const { + + Matrix32 copy=*this; + copy.elements[2]=Vector2(); + return copy; +} + +Matrix32 Matrix32::translated(const Vector2& p_offset) const { + + Matrix32 copy=*this; + copy.translate(p_offset); + return copy; + +} + +Matrix32 Matrix32::rotated(float p_phi) const { + + Matrix32 copy=*this; + copy.rotate(p_phi); + return copy; + +} + + +Matrix32 Matrix32::interpolate_with(const Matrix32& p_transform, float p_c) const { + + + return Matrix32(); +} + +Matrix32::operator String() const { + + return String(String()+elements[0]+", "+elements[1]+", "+elements[2]); +} diff --git a/core/math/math_2d.h b/core/math/math_2d.h new file mode 100644 index 00000000000..212d848bcbd --- /dev/null +++ b/core/math/math_2d.h @@ -0,0 +1,670 @@ +/*************************************************************************/ +/* math_2d.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 MATH_2D_H +#define MATH_2D_H + +#include "math_funcs.h" +#include "ustring.h" +/** + @author Juan Linietsky +*/ +enum Margin { + + MARGIN_LEFT, + MARGIN_TOP, + MARGIN_RIGHT, + MARGIN_BOTTOM +}; + +enum Orientation { + + HORIZONTAL, + VERTICAL +}; + +enum HAlign { + + HALIGN_LEFT, + HALIGN_CENTER, + HALIGN_RIGHT +}; + +enum VAlign { + + VALIGN_TOP, + VALIGN_CENTER, + VALIGN_BOTTOM +}; + +struct Vector2 { + + union { + float x; + float width; + }; + union { + float y; + float height; + }; + + + _FORCE_INLINE_ float& operator[](int p_idx) { + return p_idx?y:x; + } + _FORCE_INLINE_ const float& operator[](int p_idx) const { + return p_idx?y:x; + } + + void normalize(); + Vector2 normalized() const; + + float length() const; + float length_squared() const; + + float distance_to(const Vector2& p_vector2) const; + float distance_squared_to(const Vector2& p_vector2) const; + float angle_to(const Vector2& p_vector2) const; + + float dot(const Vector2& p_other) const; + float cross(const Vector2& p_other) const; + Vector2 cross(real_t p_other) const; + Vector2 project(const Vector2& p_vec) const; + + Vector2 plane_project(real_t p_d, const Vector2& p_vec) const; + + Vector2 clamped(real_t p_len) const; + + _FORCE_INLINE_ static Vector2 linear_interpolate(const Vector2& p_a, const Vector2& p_b,float p_t); + _FORCE_INLINE_ Vector2 linear_interpolate(const Vector2& p_b,float p_t) const; + Vector2 cubic_interpolate(const Vector2& p_b,const Vector2& p_pre_a, const Vector2& p_post_b,float p_t) const; + Vector2 cubic_interpolate_soft(const Vector2& p_b,const Vector2& p_pre_a, const Vector2& p_post_b,float p_t) const; + + + Vector2 operator+(const Vector2& p_v) const; + void operator+=(const Vector2& p_v); + Vector2 operator-(const Vector2& p_v) const; + void operator-=(const Vector2& p_v); + Vector2 operator*(const Vector2 &p_v1) const; + + Vector2 operator*(const float &rvalue) const; + void operator*=(const float &rvalue); + void operator*=(const Vector2 &rvalue) { *this = *this * rvalue; } + + Vector2 operator/(const Vector2 &p_v1) const; + + Vector2 operator/(const float &rvalue) const; + + void operator/=(const float &rvalue); + + Vector2 operator-() const; + + bool operator==(const Vector2& p_vec2) const; + bool operator!=(const Vector2& p_vec2) const; + + bool operator<(const Vector2& p_vec2) const { return (x==p_vec2.x)?(y (p_rect.pos.x + p_rect.size.width) ) + return false; + if ( (pos.x+size.width) < p_rect.pos.x ) + return false; + if ( pos.y > (p_rect.pos.y + p_rect.size.height) ) + return false; + if ( (pos.y+size.height) < p_rect.pos.y ) + return false; + + return true; + } + + bool intersects_segment(const Point2& p_from, const Point2& p_to, Point2* r_pos=NULL, Point2* r_normal=NULL) const; + + inline bool encloses(const Rect2& p_rect) const { + + return (p_rect.pos.x>=pos.x) && (p_rect.pos.y>=pos.y) && + ((p_rect.pos.x+p_rect.size.x)<(pos.x+size.x)) && + ((p_rect.pos.y+p_rect.size.y)<(pos.y+size.y)); + + } + + inline bool has_no_area() const { + + return (size.x<=0 || size.y<=0); + + } + inline Rect2 clip(const Rect2& p_rect) const { /// return a clipped rect + + Rect2 new_rect=p_rect; + + if (!intersects( new_rect )) + return Rect2(); + + new_rect.pos.x = MAX( p_rect.pos.x , pos.x ); + new_rect.pos.y = MAX( p_rect.pos.y , pos.y ); + + Point2 p_rect_end=p_rect.pos+p_rect.size; + Point2 end=pos+size; + + new_rect.size.x=MIN(p_rect_end.x,end.x) - new_rect.pos.x; + new_rect.size.y=MIN(p_rect_end.y,end.y) - new_rect.pos.y; + + return new_rect; + } + + inline Rect2 merge(const Rect2& p_rect) const { ///< return a merged rect + + Rect2 new_rect; + + new_rect.pos.x=MIN( p_rect.pos.x , pos.x ); + new_rect.pos.y=MIN( p_rect.pos.y , pos.y ); + + + new_rect.size.x = MAX( p_rect.pos.x+p_rect.size.x , pos.x+size.x ); + new_rect.size.y = MAX( p_rect.pos.y+p_rect.size.y , pos.y+size.y ); + + new_rect.size = new_rect.size - new_rect.pos; //make relative again + + return new_rect; + }; + bool has_point(const Point2& p_point) const { + if (p_point.x < pos.x) + return false; + if (p_point.y < pos.y) + return false; + + if (p_point.x >= (pos.x+size.x) ) + return false; + if (p_point.y >= (pos.y+size.y) ) + return false; + + return true; + } + + bool no_area() const { return (size.width<=0 || size.height<=0 ); } + + bool operator==(const Rect2& p_rect) const { return pos==p_rect.pos && size==p_rect.size; } + bool operator!=(const Rect2& p_rect) const { return pos!=p_rect.pos || size!=p_rect.size; } + + Rect2 grow(real_t p_by) const { + + Rect2 g=*this; + g.pos.x-=p_by; + g.pos.y-=p_by; + g.size.width+=p_by*2; + g.size.height+=p_by*2; + return g; + } + + inline Rect2 expand(const Vector2& p_vector) const { + + Rect2 r = *this; + r.expand_to(p_vector); + return r; + } + + inline void expand_to(const Vector2& p_vector) { //in place function for speed + + Vector2 begin=pos; + Vector2 end=pos+size; + + if (p_vector.xend.x) + end.x=p_vector.x; + if (p_vector.y>end.y) + end.y=p_vector.y; + + pos=begin; + size=end-begin; + } + + + operator String() const { return String(pos)+","+String(size); } + + Rect2() {} + Rect2( float p_x, float p_y, float p_width, float p_height) { pos=Point2(p_x,p_y); size=Size2( p_width, p_height ); } + Rect2( const Point2& p_pos, const Size2& p_size ) { pos=p_pos; size=p_size; } +}; + + +/* INTEGER STUFF */ + +struct Point2i { + + union { + int x; + int width; + }; + union { + int y; + int height; + }; + + + _FORCE_INLINE_ int& operator[](int p_idx) { + return p_idx?y:x; + } + _FORCE_INLINE_ const int& operator[](int p_idx) const { + return p_idx?y:x; + } + + Point2i operator+(const Point2i& p_v) const; + void operator+=(const Point2i& p_v); + Point2i operator-(const Point2i& p_v) const; + void operator-=(const Point2i& p_v); + Point2i operator*(const Point2i &p_v1) const; + + Point2i operator*(const int &rvalue) const; + void operator*=(const int &rvalue); + + Point2i operator/(const Point2i &p_v1) const; + + Point2i operator/(const int &rvalue) const; + + void operator/=(const int &rvalue); + + Point2i operator-() const; + bool operator<(const Point2i& p_vec2) const { return (x==p_vec2.x)?(y(const Point2i& p_vec2) const { return (x==p_vec2.x)?(y>p_vec2.y):(x>p_vec2.x); } + + bool operator==(const Point2i& p_vec2) const; + bool operator!=(const Point2i& p_vec2) const; + + float get_aspect() const { return width/(float)height; } + + operator String() const { return String::num(x)+","+String::num(y); } + + operator Vector2() const { return Vector2(x,y); } + inline Point2i(const Vector2& p_vec2) { x=(int)p_vec2.x; y=(int)p_vec2.y; } + inline Point2i(int p_x,int p_y) { x=p_x; y=p_y; } + inline Point2i() { x=0; y=0; } +}; + +typedef Point2i Size2i; + +struct Rect2i { + + Point2i pos; + Size2i size; + + const Point2i& get_pos() const { return pos; } + void set_pos(const Point2i& p_pos) { pos=p_pos; } + const Point2i& get_size() const { return size; } + void set_size(const Point2i& p_size) { size=p_size; } + + int get_area() const { return size.width*size.height; } + + inline bool intersects(const Rect2i& p_rect) const { + if ( pos.x > (p_rect.pos.x + p_rect.size.width) ) + return false; + if ( (pos.x+size.width) < p_rect.pos.x ) + return false; + if ( pos.y > (p_rect.pos.y + p_rect.size.height) ) + return false; + if ( (pos.y+size.height) < p_rect.pos.y ) + return false; + + return true; + } + + inline bool encloses(const Rect2i& p_rect) const { + + return (p_rect.pos.x>=pos.x) && (p_rect.pos.y>=pos.y) && + ((p_rect.pos.x+p_rect.size.x)<(pos.x+size.x)) && + ((p_rect.pos.y+p_rect.size.y)<(pos.y+size.y)); + + } + + inline bool has_no_area() const { + + return (size.x<=0 || size.y<=0); + + } + inline Rect2i clip(const Rect2i& p_rect) const { /// return a clipped rect + + Rect2i new_rect=p_rect; + + if (!intersects( new_rect )) + return Rect2i(); + + new_rect.pos.x = MAX( p_rect.pos.x , pos.x ); + new_rect.pos.y = MAX( p_rect.pos.y , pos.y ); + + Point2 p_rect_end=p_rect.pos+p_rect.size; + Point2 end=pos+size; + + new_rect.size.x=(int)(MIN(p_rect_end.x,end.x) - new_rect.pos.x); + new_rect.size.y=(int)(MIN(p_rect_end.y,end.y) - new_rect.pos.y); + + return new_rect; + } + + inline Rect2i merge(const Rect2i& p_rect) const { ///< return a merged rect + + Rect2i new_rect; + + new_rect.pos.x=MIN( p_rect.pos.x , pos.x ); + new_rect.pos.y=MIN( p_rect.pos.y , pos.y ); + + + new_rect.size.x = MAX( p_rect.pos.x+p_rect.size.x , pos.x+size.x ); + new_rect.size.y = MAX( p_rect.pos.y+p_rect.size.y , pos.y+size.y ); + + new_rect.size = new_rect.size - new_rect.pos; //make relative again + + return new_rect; + }; + bool has_point(const Point2& p_point) { + if (p_point.x < pos.x) + return false; + if (p_point.y < pos.y) + return false; + + if (p_point.x >= (pos.x+size.x) ) + return false; + if (p_point.y >= (pos.y+size.y) ) + return false; + + return true; + } + + bool no_area() { return (size.width<=0 || size.height<=0 ); } + + bool operator==(const Rect2i& p_rect) const { return pos==p_rect.pos && size==p_rect.size; } + bool operator!=(const Rect2i& p_rect) const { return pos!=p_rect.pos || size!=p_rect.size; } + + Rect2i grow(int p_by) const { + + Rect2i g=*this; + g.pos.x-=p_by; + g.pos.y-=p_by; + g.size.width+=p_by*2; + g.size.height+=p_by*2; + return g; + } + + inline void expand_to(const Point2i& p_vector) { + + Point2i begin=pos; + Point2i end=pos+size; + + if (p_vector.xend.x) + end.x=p_vector.x; + if (p_vector.y>end.y) + end.y=p_vector.y; + + pos=begin; + size=end-begin; + } + + + operator String() const { return String(pos)+","+String(size); } + + operator Rect2() const { return Rect2(pos,size); } + Rect2i(const Rect2& p_r2) { pos=p_r2.pos; size=p_r2.size; } + Rect2i() {} + Rect2i( int p_x, int p_y, int p_width, int p_height) { pos=Point2(p_x,p_y); size=Size2( p_width, p_height ); } + Rect2i( const Point2& p_pos, const Size2& p_size ) { pos=p_pos; size=p_size; } +}; + + + +struct Matrix32 { + + + Vector2 elements[3]; + + + _FORCE_INLINE_ float tdotx(const Vector2& v) const { return elements[0][0] * v.x + elements[1][0] * v.y; } + _FORCE_INLINE_ float tdoty(const Vector2& v) const { return elements[0][1] * v.x + elements[1][1] * v.y; } + + const Vector2& operator[](int p_idx) const { return elements[p_idx]; } + Vector2& operator[](int p_idx) { return elements[p_idx]; } + + _FORCE_INLINE_ Vector2 get_axis(int p_axis) const { ERR_FAIL_INDEX_V(p_axis,3,Vector2()); return elements[p_axis]; } + _FORCE_INLINE_ void set_axis(int p_axis,const Vector2& p_vec) { ERR_FAIL_INDEX(p_axis,3); elements[p_axis]=p_vec; } + + void invert(); + Matrix32 inverse() const; + + void affine_invert(); + Matrix32 affine_inverse() const; + + void set_rotation(real_t p_phi); + real_t get_rotation() const; + _FORCE_INLINE_ void set_rotation_and_scale(real_t p_phi,const Size2& p_scale); + void rotate(real_t p_phi); + + void scale(const Vector2& p_scale); + void scale_basis(const Vector2& p_scale); + void translate( real_t p_tx, real_t p_ty); + void translate( const Vector2& p_translation ); + Vector2 get_scale() const; + + _FORCE_INLINE_ const Vector2& get_origin() const { return elements[2]; } + _FORCE_INLINE_ void set_origin(const Vector2& p_origin) { elements[2]=p_origin; } + + Matrix32 scaled(const Vector2& p_scale) const; + Matrix32 basis_scaled(const Vector2& p_scale) const; + Matrix32 translated(const Vector2& p_offset) const; + Matrix32 rotated(float p_phi) const; + + Matrix32 untranslated() const; + + + void orthonormalize(); + Matrix32 orthonormalized() const; + + bool operator==(const Matrix32& p_transform) const; + bool operator!=(const Matrix32& p_transform) const; + + void operator*=(const Matrix32& p_transform); + Matrix32 operator*(const Matrix32& p_transform) const; + + Matrix32 interpolate_with(const Matrix32& p_transform, float p_c) const; + + _FORCE_INLINE_ Vector2 basis_xform(const Vector2& p_vec) const; + _FORCE_INLINE_ Vector2 basis_xform_inv(const Vector2& p_vec) const; + _FORCE_INLINE_ Vector2 xform(const Vector2& p_vec) const; + _FORCE_INLINE_ Vector2 xform_inv(const Vector2& p_vec) const; + _FORCE_INLINE_ Rect2 xform(const Rect2& p_vec) const; + _FORCE_INLINE_ Rect2 xform_inv(const Rect2& p_vec) const; + + + operator String() const; + + + Matrix32(real_t p_rot, const Vector2& p_pos); + Matrix32() { elements[0][0]=1.0; elements[1][1]=1.0; } + +}; + + +Vector2 Matrix32::basis_xform(const Vector2& v) const { + + return Vector2( + tdotx(v), + tdoty(v) + ); +} + +Vector2 Matrix32::basis_xform_inv(const Vector2& v) const{ + + return Vector2( + elements[0].dot(v), + elements[1].dot(v) + ); +} + +Vector2 Matrix32::xform(const Vector2& v) const { + + return Vector2( + tdotx(v), + tdoty(v) + ) + elements[2]; +} +Vector2 Matrix32::xform_inv(const Vector2& p_vec) const { + + Vector2 v = p_vec - elements[2]; + + return Vector2( + elements[0].dot(v), + elements[1].dot(v) + ); + +} +Rect2 Matrix32::xform(const Rect2& p_rect) const { + + Vector2 x=elements[0]*p_rect.size.x; + Vector2 y=elements[1]*p_rect.size.y; + Vector2 pos = xform( p_rect.pos ); + + Rect2 new_rect; + new_rect.pos=pos; + new_rect.expand_to( pos+x ); + new_rect.expand_to( pos+y ); + new_rect.expand_to( pos+x+y ); + return new_rect; +} + +void Matrix32::set_rotation_and_scale(real_t p_rot,const Size2& p_scale) { + + elements[0][0]=Math::cos(p_rot)*p_scale.x; + elements[1][1]=Math::cos(p_rot)*p_scale.y; + elements[0][1]=-Math::sin(p_rot)*p_scale.x; + elements[1][0]=Math::sin(p_rot)*p_scale.y; + +} + +Rect2 Matrix32::xform_inv(const Rect2& p_rect) const { + + Vector2 ends[4]={ + xform_inv( p_rect.pos ), + xform_inv( Vector2(p_rect.pos.x,p_rect.pos.y+p_rect.size.y ) ), + xform_inv( Vector2(p_rect.pos.x+p_rect.size.x,p_rect.pos.y+p_rect.size.y ) ), + xform_inv( Vector2(p_rect.pos.x+p_rect.size.x,p_rect.pos.y ) ) + }; + + Rect2 new_rect; + new_rect.pos=ends[0]; + new_rect.expand_to(ends[1]); + new_rect.expand_to(ends[2]); + new_rect.expand_to(ends[3]); + + return new_rect; +} + + +#endif diff --git a/core/math/math_defs.cpp b/core/math/math_defs.cpp new file mode 100644 index 00000000000..ca43bc7ae36 --- /dev/null +++ b/core/math/math_defs.cpp @@ -0,0 +1,30 @@ +/*************************************************************************/ +/* math_defs.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 "math_defs.h" + diff --git a/core/math/math_defs.h b/core/math/math_defs.h new file mode 100644 index 00000000000..dd0390240a2 --- /dev/null +++ b/core/math/math_defs.h @@ -0,0 +1,60 @@ +/*************************************************************************/ +/* math_defs.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 MATH_DEFS_H +#define MATH_DEFS_H + +#define CMP_EPSILON 0.00001 +#define CMP_EPSILON2 (CMP_EPSILON*CMP_EPSILON) +#define CMP_NORMALIZE_TOLERANCE 0.000001 +#define CMP_POINT_IN_PLANE_EPSILON 0.00001 + +/** + * "Real" is a type that will be translated to either floats or fixed depending + * on the compilation setting + */ + +enum ClockDirection { + + CLOCKWISE, + COUNTERCLOCKWISE +}; + + +#ifdef REAL_T_IS_DOUBLE + +typedef double real_t; + +#else + +typedef float real_t; + +#endif + + +#endif // MATH_DEFS_H diff --git a/core/math/math_funcs.cpp b/core/math/math_funcs.cpp new file mode 100644 index 00000000000..2ba36e5a49b --- /dev/null +++ b/core/math/math_funcs.cpp @@ -0,0 +1,328 @@ +/*************************************************************************/ +/* math_funcs.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 "math_funcs.h" + +#include "core/os/os.h" +#include +#include "float.h" +uint32_t Math::default_seed=1; + + +#define PHI 0x9e3779b9 + +static uint32_t Q[4096], c = 362436; + + +uint32_t Math::rand_from_seed(uint32_t *seed) { + +#if 1 + uint32_t k; + uint32_t s = (*seed); + if (s == 0) + s = 0x12345987; + k = s / 127773; + s = 16807 * (s - k * 127773) - 2836 * k; + if (s < 0) + s += 2147483647; + (*seed) = s; + return (s & Math::RANDOM_MAX); +#else + *seed = *seed * 1103515245 + 12345; + return (*seed % ((unsigned int)RANDOM_MAX + 1)); +#endif +} + +void Math::seed(uint32_t x) { +#if 0 + int i; + + Q[0] = x; + Q[1] = x + PHI; + Q[2] = x + PHI + PHI; + + for (i = 3; i < 4096; i++) + Q[i] = Q[i - 3] ^ Q[i - 2] ^ PHI ^ i; +#else + default_seed=x; +#endif +} + +void Math::randomize() { + + seed(OS::get_singleton()->get_ticks_usec()); /* *OS::get_singleton()->get_time().sec); // windows doesn't have get_time(), returns always 0 */ +} + +uint32_t Math::rand() { + + return rand_from_seed(&default_seed)&0x7FFFFFFF; +} + +double Math::randf() { + + return (double)rand() / (double)RANDOM_MAX; +} + +double Math::sin(double p_x) { + + return ::sin(p_x); + +} + +double Math::cos(double p_x) { + + return ::cos(p_x); + +} + +double Math::tan(double p_x) { + + return ::tan(p_x); + +} +double Math::sinh(double p_x) { + + return ::sinh(p_x); +} + +double Math::cosh(double p_x) { + + return ::cosh(p_x); +} + +double Math::tanh(double p_x) { + + return ::tanh(p_x); +} + + +double Math::deg2rad(double p_y) { + + return p_y*Math_PI/180.0; +} + +double Math::rad2deg(double p_y) { + + return p_y*180.0/Math_PI; +} + +double Math::round(double p_val) { + + if (p_val>0) { + return ::floor(p_val+0.5); + } else { + p_val=-p_val; + return -::floor(p_val+0.5); + } +} +double Math::asin(double p_x) { + + return ::asin(p_x); + +} +double Math::acos(double p_x) { + + return ::acos(p_x); +} + +double Math::atan(double p_x) { + + return ::atan(p_x); +} + +double Math::dectime(double p_value,double p_amount, double p_step) { + + float sgn = p_value < 0 ? -1.0 : 1.0; + float val = absf(p_value); + val-=p_amount*p_step; + if (val<0.0) + val=0.0; + return val*sgn; +} + +double Math::atan2(double p_y, double p_x) { + + return ::atan2(p_y,p_x); + +} +double Math::sqrt(double p_x) { + + return ::sqrt(p_x); +} + +double Math::fmod(double p_x,double p_y) { + + return ::fmod(p_x,p_y); +} + +double Math::fposmod(double p_x,double p_y) { + + if (p_x>=0) { + + return Math::fmod(p_x,p_y); + + } else { + + return p_y-Math::fmod(-p_x,p_y); + } + +} +double Math::floor(double p_x) { + + return ::floor(p_x); +} + +double Math::ceil(double p_x) { + + return ::ceil(p_x); +} + +int Math::decimals(double p_step) { + + int max=4; + int i=0; + while( (p_step - Math::floor(p_step)) != 0.0 && max) { + + p_step*=10.0; + max--; + i++; + } + + return i; + +} + +double Math::ease(double p_x, double p_c) { + + if (p_c>0) { + + return Math::pow(p_x,p_c); + } else if (p_c<0) { + //inout ease + + if (p_x<0.5) { + return Math::pow(p_x*2.0,-p_c)*0.5; + } else { + return (1.0-Math::pow(1.0-(p_x-0.5)*2.0,-p_c))*0.5+0.5; + } + } else + return 0; // no ease (raw) + +} + +double Math::stepify(double p_value,double p_step) { + + if (p_step!=0) { + + p_value=floor( p_value / p_step + 0.5 ) * p_step; + } + return p_value; +} + +bool Math::is_nan(double p_val) { + + return (p_val!=p_val); +} + +bool Math::is_inf(double p_val) { + +#ifdef _MSC_VER + return !_finite(p_val); +#else + return isinf(p_val); +#endif + +} + +uint32_t Math::larger_prime(uint32_t p_val) { + + static const int primes[] = { + 5, + 13, + 23, + 47, + 97, + 193, + 389, + 769, + 1543, + 3079, + 6151, + 12289, + 24593, + 49157, + 98317, + 196613, + 393241, + 786433, + 1572869, + 3145739, + 6291469, + 12582917, + 25165843, + 50331653, + 100663319, + 201326611, + 402653189, + 805306457, + 1610612741, + 0, + }; + + int idx=0; + while (true) { + + ERR_FAIL_COND_V(primes[idx]==0,0); + if (primes[idx]>p_val) + return primes[idx]; + idx++; + } + + return 0; +} + +double Math::random(double from, double to) { + + unsigned int r = Math::rand(); + double ret = (double)r/(double)RANDOM_MAX; + return (ret)*(to-from) + from; +} + +double Math::pow(double x, double y) { + + return ::pow(x,y); +} + +double Math::log(double x) { + + return ::log(x); +} +double Math::exp(double x) { + + return ::exp(x); +} diff --git a/core/math/math_funcs.h b/core/math/math_funcs.h new file mode 100644 index 00000000000..6a60a7f7908 --- /dev/null +++ b/core/math/math_funcs.h @@ -0,0 +1,180 @@ +/*************************************************************************/ +/* math_funcs.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 MATH_FUNCS_H +#define MATH_FUNCS_H + +#include "typedefs.h" +#include "math_defs.h" + +#ifndef NO_MATH_H +#include "math.h" +#endif + +class Math { + + + static uint32_t default_seed; +public: + Math() {}; // useless to instance + + enum { + RANDOM_MAX=2147483647L + }; + + static double sin(double p_x); + static double cos(double p_x); + static double tan(double p_x); + static double sinh(double p_x); + static double cosh(double p_x); + static double tanh(double p_x); + static double asin(double p_x); + static double acos(double p_x); + static double atan(double p_x); + static double atan2(double p_y, double p_x); + static double deg2rad(double p_y); + static double rad2deg(double p_y); + static double sqrt(double p_x); + static double fmod(double p_x,double p_y); + static double fposmod(double p_x,double p_y); + static uint32_t rand_from_seed(uint32_t *seed); + static double floor(double p_x); + static double ceil(double p_x); + static double ease(double p_x, double p_c); + static int decimals(double p_step); + static double stepify(double p_value,double p_step); + static void seed(uint32_t x=0); + static void randomize(); + static uint32_t larger_prime(uint32_t p_val); + static double dectime(double p_value,double p_amount, double p_step); + + + static inline double linear2db(double p_linear) { + + return Math::log( p_linear ) * 8.6858896380650365530225783783321; + } + + static inline double db2linear(double p_linear) { + + return Math::exp( p_linear * 0.11512925464970228420089957273422 ); + } + + static bool is_nan(double p_val); + static bool is_inf(double p_val); + + + + static uint32_t rand(); + static double randf(); + + static double round(double p_val); + + static double random(double from, double to); + + + static _FORCE_INLINE_ real_t abs(real_t g) { + +#ifdef REAL_T_IS_DOUBLE + + return absd(g); +#else + + return absf(g); +#endif + } + + static _FORCE_INLINE_ float absf(float g) { + + union { + float f; + uint32_t i; + } u; + + u.f=g; + u.i&=2147483647u; + return u.f; + } + + static _FORCE_INLINE_ double absd(double g) { + + union { + double d; + uint64_t i; + } u; + u.d=g; + u.i&=(uint64_t)9223372036854775807ll; + return u.d; + } + + //this function should be as fast as possible and rounding mode should not matter + static _FORCE_INLINE_ int fast_ftoi(float a) { + + static int b; + +#if defined(_MSC_VER) + __asm fld a + __asm fistp b +/*#elif defined( __GNUC__ ) && ( defined( __i386__ ) || defined( __x86_64__ ) ) + // use AT&T inline assembly style, document that + // we use memory as output (=m) and input (m) + __asm__ __volatile__ ( + "flds %1 \n\t" + "fistpl %0 \n\t" + : "=m" (b) + : "m" (a));*/ +#else + b=lrintf(a); //assuming everything but msvc has lrint +#endif + return b; + } + + +#if defined(__GNUC__) + + static _FORCE_INLINE_ int64_t dtoll(double p_double) { return (int64_t)p_double; } ///@TODO OPTIMIZE +#else + + static _FORCE_INLINE_ int64_t dtoll(double p_double) { return (int64_t)p_double; } ///@TODO OPTIMIZE +#endif + + static _FORCE_INLINE_ float lerp(float a, float b, float c) { + + return a+(b-a)*c; + } + + static double pow(double x, double y); + static double log(double x); + static double exp(double x); + +}; + + +#define Math_PI 3.14159265358979323846 +#define Math_SQRT12 0.7071067811865475244008443621048490 + +#endif // MATH_FUNCS_H diff --git a/core/math/matrix3.cpp b/core/math/matrix3.cpp new file mode 100644 index 00000000000..ff62e7786b6 --- /dev/null +++ b/core/math/matrix3.cpp @@ -0,0 +1,479 @@ +/*************************************************************************/ +/* matrix3.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 "matrix3.h" +#include "math_funcs.h" +#include "os/copymem.h" + +#define cofac(row1,col1, row2, col2)\ + (elements[row1][col1] * elements[row2][col2] - elements[row1][col2] * elements[row2][col1]) + +void Matrix3::from_z(const Vector3& p_z) { + + if (Math::abs(p_z.z) > Math_SQRT12 ) { + + // choose p in y-z plane + real_t a = p_z[1]*p_z[1] + p_z[2]*p_z[2]; + real_t k = 1.0/Math::sqrt(a); + elements[0]=Vector3(0,-p_z[2]*k,p_z[1]*k); + elements[1]=Vector3(a*k,-p_z[0]*elements[0][2],p_z[0]*elements[0][1]); + } else { + + // choose p in x-y plane + real_t a = p_z.x*p_z.x + p_z.y*p_z.y; + real_t k = 1.0/Math::sqrt(a); + elements[0]=Vector3(-p_z.y*k,p_z.x*k,0); + elements[1]=Vector3(-p_z.z*elements[0].y,p_z.z*elements[0].x,a*k); + } + elements[2]=p_z; +} + +void Matrix3::invert() { + + + real_t co[3]={ + cofac(1, 1, 2, 2), cofac(1, 2, 2, 0), cofac(1, 0, 2, 1) + }; + real_t det = elements[0][0] * co[0]+ + elements[0][1] * co[1]+ + elements[0][2] * co[2]; + + ERR_FAIL_COND( det == 0 ); + real_t s = 1.0/det; + + set( co[0]*s, cofac(0, 2, 2, 1) * s, cofac(0, 1, 1, 2) * s, + co[1]*s, cofac(0, 0, 2, 2) * s, cofac(0, 2, 1, 0) * s, + co[2]*s, cofac(0, 1, 2, 0) * s, cofac(0, 0, 1, 1) * s ); + +} + +void Matrix3::orthonormalize() { + + // Gram-Schmidt Process + + Vector3 x=get_axis(0); + Vector3 y=get_axis(1); + Vector3 z=get_axis(2); + + x.normalize(); + y = (y-x*(x.dot(y))); + y.normalize(); + z = (z-x*(x.dot(z))-y*(y.dot(z))); + z.normalize(); + + set_axis(0,x); + set_axis(1,y); + set_axis(2,z); + +} + +Matrix3 Matrix3::orthonormalized() const { + + Matrix3 c = *this; + c.orthonormalize(); + return c; +} + + +Matrix3 Matrix3::inverse() const { + + Matrix3 inv=*this; + inv.invert(); + return inv; +} + +void Matrix3::transpose() { + + SWAP(elements[0][1],elements[1][0]); + SWAP(elements[0][2],elements[2][0]); + SWAP(elements[1][2],elements[2][1]); +} + +Matrix3 Matrix3::transposed() const { + + Matrix3 tr=*this; + tr.transpose(); + return tr; +} + +void Matrix3::scale(const Vector3& p_scale) { + + elements[0][0]*=p_scale.x; + elements[1][0]*=p_scale.x; + elements[2][0]*=p_scale.x; + elements[0][1]*=p_scale.y; + elements[1][1]*=p_scale.y; + elements[2][1]*=p_scale.y; + elements[0][2]*=p_scale.z; + elements[1][2]*=p_scale.z; + elements[2][2]*=p_scale.z; +} + +Matrix3 Matrix3::scaled( const Vector3& p_scale ) const { + + Matrix3 m = *this; + m.scale(p_scale); + return m; +} + +Vector3 Matrix3::get_scale() const { + + return Vector3( + Vector3(elements[0][0],elements[1][0],elements[2][0]).length(), + Vector3(elements[0][1],elements[1][1],elements[2][1]).length(), + Vector3(elements[0][2],elements[1][2],elements[2][2]).length() + ); + +} +void Matrix3::rotate(const Vector3& p_axis, real_t p_phi) { + + *this = *this * Matrix3(p_axis, p_phi); +} + +Matrix3 Matrix3::rotated(const Vector3& p_axis, real_t p_phi) const { + + return *this * Matrix3(p_axis, p_phi); + +} + +Vector3 Matrix3::get_euler() const { + + // rot = cy*cz -cy*sz sy + // cz*sx*sy+cx*sz cx*cz-sx*sy*sz -cy*sx + // -cx*cz*sy+sx*sz cz*sx+cx*sy*sz cx*cy + + Matrix3 m = *this; + m.orthonormalize(); + + Vector3 euler; + + euler.y = Math::asin(m[0][2]); + if ( euler.y < Math_PI*0.5) { + if ( euler.y > -Math_PI*0.5) { + euler.x = Math::atan2(-m[1][2],m[2][2]); + euler.z = Math::atan2(-m[0][1],m[0][0]); + + } else { + real_t r = Math::atan2(m[1][0],m[1][1]); + euler.z = 0.0; + euler.x = euler.z - r; + + } + } else { + real_t r = Math::atan2(m[0][1],m[1][1]); + euler.z = 0; + euler.x = r - euler.z; + } + + return euler; + + +} + +void Matrix3::set_euler(const Vector3& p_euler) { + + real_t c, s; + + c = Math::cos(p_euler.x); + s = Math::sin(p_euler.x); + Matrix3 xmat(1.0,0.0,0.0,0.0,c,-s,0.0,s,c); + + c = Math::cos(p_euler.y); + s = Math::sin(p_euler.y); + Matrix3 ymat(c,0.0,s,0.0,1.0,0.0,-s,0.0,c); + + c = Math::cos(p_euler.z); + s = Math::sin(p_euler.z); + Matrix3 zmat(c,-s,0.0,s,c,0.0,0.0,0.0,1.0); + + //optimizer will optimize away all this anyway + *this = xmat*(ymat*zmat); +} + +bool Matrix3::operator==(const Matrix3& p_matrix) const { + + for (int i=0;i<3;i++) { + for (int j=0;j<3;j++) { + if (elements[i][j]!=p_matrix.elements[i][j]) + return false; + } + } + + return true; +} +bool Matrix3::operator!=(const Matrix3& p_matrix) const { + + return (!(*this==p_matrix)); +} + +Matrix3::operator String() const { + + String mtx; + for (int i=0;i<3;i++) { + + for (int j=0;j<3;j++) { + + if (i!=0 || j!=0) + mtx+=", "; + + mtx+=rtos( elements[i][j] ); + } + } + + return mtx; +} + +Matrix3::operator Quat() const { + + Matrix3 m=*this; + m.orthonormalize(); + + real_t trace = m.elements[0][0] + m.elements[1][1] + m.elements[2][2]; + real_t temp[4]; + + if (trace > 0.0) + { + real_t s = Math::sqrt(trace + 1.0); + temp[3]=(s * 0.5); + s = 0.5 / s; + + temp[0]=((m.elements[2][1] - m.elements[1][2]) * s); + temp[1]=((m.elements[0][2] - m.elements[2][0]) * s); + temp[2]=((m.elements[1][0] - m.elements[0][1]) * s); + } + else + { + int i = m.elements[0][0] < m.elements[1][1] ? + (m.elements[1][1] < m.elements[2][2] ? 2 : 1) : + (m.elements[0][0] < m.elements[2][2] ? 2 : 0); + int j = (i + 1) % 3; + int k = (i + 2) % 3; + + real_t s = Math::sqrt(m.elements[i][i] - m.elements[j][j] - m.elements[k][k] + 1.0); + temp[i] = s * 0.5; + s = 0.5 / s; + + temp[3] = (m.elements[k][j] - m.elements[j][k]) * s; + temp[j] = (m.elements[j][i] + m.elements[i][j]) * s; + temp[k] = (m.elements[k][i] + m.elements[i][k]) * s; + } + + return Quat(temp[0],temp[1],temp[2],temp[3]); + +} + +static const Matrix3 _ortho_bases[24]={ + Matrix3(1, 0, 0, 0, 1, 0, 0, 0, 1), + Matrix3(0, -1, 0, 1, 0, 0, 0, 0, 1), + Matrix3(-1, 0, 0, 0, -1, 0, 0, 0, 1), + Matrix3(0, 1, 0, -1, 0, 0, 0, 0, 1), + Matrix3(1, 0, 0, 0, 0, -1, 0, 1, 0), + Matrix3(0, 0, 1, 1, 0, 0, 0, 1, 0), + Matrix3(-1, 0, 0, 0, 0, 1, 0, 1, 0), + Matrix3(0, 0, -1, -1, 0, 0, 0, 1, 0), + Matrix3(1, 0, 0, 0, -1, 0, 0, 0, -1), + Matrix3(0, 1, 0, 1, 0, 0, 0, 0, -1), + Matrix3(-1, 0, 0, 0, 1, 0, 0, 0, -1), + Matrix3(0, -1, 0, -1, 0, 0, 0, 0, -1), + Matrix3(1, 0, 0, 0, 0, 1, 0, -1, 0), + Matrix3(0, 0, -1, 1, 0, 0, 0, -1, 0), + Matrix3(-1, 0, 0, 0, 0, -1, 0, -1, 0), + Matrix3(0, 0, 1, -1, 0, 0, 0, -1, 0), + Matrix3(0, 0, 1, 0, 1, 0, -1, 0, 0), + Matrix3(0, -1, 0, 0, 0, 1, -1, 0, 0), + Matrix3(0, 0, -1, 0, -1, 0, -1, 0, 0), + Matrix3(0, 1, 0, 0, 0, -1, -1, 0, 0), + Matrix3(0, 0, 1, 0, -1, 0, 1, 0, 0), + Matrix3(0, 1, 0, 0, 0, 1, 1, 0, 0), + Matrix3(0, 0, -1, 0, 1, 0, 1, 0, 0), + Matrix3(0, -1, 0, 0, 0, -1, 1, 0, 0) +}; + +int Matrix3::get_orthogonal_index() const { + + //could be sped up if i come up with a way + Matrix3 orth=*this; + for(int i=0;i<3;i++) { + for(int j=0;j<3;j++) { + + float v = orth[i][j]; + if (v>0.5) + v=1.0; + else if (v<-0.5) + v=-1.0; + else + v=0; + + orth[i][j]=v; + } + } + + for(int i=0;i<24;i++) { + + if (_ortho_bases[i]==orth) + return i; + + + } + + return 0; +} + +void Matrix3::set_orthogonal_index(int p_index){ + + //there only exist 24 orthogonal bases in r3 + ERR_FAIL_INDEX(p_index,24); + + + *this=_ortho_bases[p_index]; + +} + + +void Matrix3::get_axis_and_angle(Vector3 &r_axis,real_t& r_angle) const { + + + double angle,x,y,z; // variables for result + double epsilon = 0.01; // margin to allow for rounding errors + double epsilon2 = 0.1; // margin to distinguish between 0 and 180 degrees + + if ( (Math::abs(elements[1][0]-elements[0][1])< epsilon) + && (Math::abs(elements[2][0]-elements[0][2])< epsilon) + && (Math::abs(elements[2][1]-elements[1][2])< epsilon)) { + // singularity found + // first check for identity matrix which must have +1 for all terms + // in leading diagonaland zero in other terms + if ((Math::abs(elements[1][0]+elements[0][1]) < epsilon2) + && (Math::abs(elements[2][0]+elements[0][2]) < epsilon2) + && (Math::abs(elements[2][1]+elements[1][2]) < epsilon2) + && (Math::abs(elements[0][0]+elements[1][1]+elements[2][2]-3) < epsilon2)) { + // this singularity is identity matrix so angle = 0 + r_axis=Vector3(0,1,0); + r_angle=0; + return; + } + // otherwise this singularity is angle = 180 + angle = Math_PI; + double xx = (elements[0][0]+1)/2; + double yy = (elements[1][1]+1)/2; + double zz = (elements[2][2]+1)/2; + double xy = (elements[1][0]+elements[0][1])/4; + double xz = (elements[2][0]+elements[0][2])/4; + double yz = (elements[2][1]+elements[1][2])/4; + if ((xx > yy) && (xx > zz)) { // elements[0][0] is the largest diagonal term + if (xx< epsilon) { + x = 0; + y = 0.7071; + z = 0.7071; + } else { + x = Math::sqrt(xx); + y = xy/x; + z = xz/x; + } + } else if (yy > zz) { // elements[1][1] is the largest diagonal term + if (yy< epsilon) { + x = 0.7071; + y = 0; + z = 0.7071; + } else { + y = Math::sqrt(yy); + x = xy/y; + z = yz/y; + } + } else { // elements[2][2] is the largest diagonal term so base result on this + if (zz< epsilon) { + x = 0.7071; + y = 0.7071; + z = 0; + } else { + z = Math::sqrt(zz); + x = xz/z; + y = yz/z; + } + } + r_axis=Vector3(x,y,z); + r_angle=angle; + return; + } + // as we have reached here there are no singularities so we can handle normally + double s = Math::sqrt((elements[1][2] - elements[2][1])*(elements[1][2] - elements[2][1]) + +(elements[2][0] - elements[0][2])*(elements[2][0] - elements[0][2]) + +(elements[0][1] - elements[1][0])*(elements[0][1] - elements[1][0])); // used to normalise + if (Math::abs(s) < 0.001) s=1; + // prevent divide by zero, should not happen if matrix is orthogonal and should be + // caught by singularity test above, but I've left it in just in case + angle = Math::acos(( elements[0][0] + elements[1][1] + elements[2][2] - 1)/2); + x = (elements[1][2] - elements[2][1])/s; + y = (elements[2][0] - elements[0][2])/s; + z = (elements[0][1] - elements[1][0])/s; + + r_axis=Vector3(x,y,z); + r_angle=angle; +} + +Matrix3::Matrix3(const Vector3& p_euler) { + + set_euler( p_euler ); + +} + +Matrix3::Matrix3(const Quat& p_quat) { + + real_t d = p_quat.length_squared(); + real_t s = 2.0 / d; + real_t xs = p_quat.x * s, ys = p_quat.y * s, zs = p_quat.z * s; + real_t wx = p_quat.w * xs, wy = p_quat.w * ys, wz = p_quat.w * zs; + real_t xx = p_quat.x * xs, xy = p_quat.x * ys, xz = p_quat.x * zs; + real_t yy = p_quat.y * ys, yz = p_quat.y * zs, zz = p_quat.z * zs; + set( 1.0 - (yy + zz), xy - wz, xz + wy, + xy + wz, 1.0 - (xx + zz), yz - wx, + xz - wy, yz + wx, 1.0 - (xx + yy)) ; + +} + +Matrix3::Matrix3(const Vector3& p_axis, real_t p_phi) { + + Vector3 axis_sq(p_axis.x*p_axis.x,p_axis.y*p_axis.y,p_axis.z*p_axis.z); + + real_t cosine= Math::cos(p_phi); + real_t sine= Math::sin(p_phi); + + elements[0][0] = axis_sq.x + cosine * ( 1.0 - axis_sq.x ); + elements[0][1] = p_axis.x * p_axis.y * ( 1.0 - cosine ) + p_axis.z * sine; + elements[0][2] = p_axis.z * p_axis.x * ( 1.0 - cosine ) - p_axis.y * sine; + + elements[1][0] = p_axis.x * p_axis.y * ( 1.0 - cosine ) - p_axis.z * sine; + elements[1][1] = axis_sq.y + cosine * ( 1.0 - axis_sq.y ); + elements[1][2] = p_axis.y * p_axis.z * ( 1.0 - cosine ) + p_axis.x * sine; + + elements[2][0] = p_axis.z * p_axis.x * ( 1.0 - cosine ) + p_axis.y * sine; + elements[2][1] = p_axis.y * p_axis.z * ( 1.0 - cosine ) - p_axis.x * sine; + elements[2][2] = axis_sq.z + cosine * ( 1.0 - axis_sq.z ); + +} + diff --git a/core/math/matrix3.h b/core/math/matrix3.h new file mode 100644 index 00000000000..f68703ca232 --- /dev/null +++ b/core/math/matrix3.h @@ -0,0 +1,230 @@ +/*************************************************************************/ +/* matrix3.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 MATRIX3_H +#define MATRIX3_H + +#include "vector3.h" +#include "quat.h" + +/** + @author Juan Linietsky +*/ +class Matrix3 { +public: + + Vector3 elements[3]; + + _FORCE_INLINE_ const Vector3& operator[](int axis) const { + + return elements[axis]; + } + _FORCE_INLINE_ Vector3& operator[](int axis) { + + return elements[axis]; + } + + void invert(); + void transpose(); + + Matrix3 inverse() const; + Matrix3 transposed() const; + + _FORCE_INLINE_ float determinant() const; + + void from_z(const Vector3& p_z); + + _FORCE_INLINE_ Vector3 get_axis(int p_axis) const { + // get actual basis axis (elements is transposed for performance) + return Vector3( elements[0][p_axis], elements[1][p_axis], elements[2][p_axis] ); + } + _FORCE_INLINE_ void set_axis(int p_axis, const Vector3& p_value) { + // get actual basis axis (elements is transposed for performance) + elements[0][p_axis]=p_value.x; + elements[1][p_axis]=p_value.y; + elements[2][p_axis]=p_value.z; + } + + void rotate(const Vector3& p_axis, real_t p_phi); + Matrix3 rotated(const Vector3& p_axis, real_t p_phi) const; + + void scale( const Vector3& p_scale ); + Matrix3 scaled( const Vector3& p_scale ) const; + Vector3 get_scale() const; + + Vector3 get_euler() const; + void set_euler(const Vector3& p_euler); + + // transposed dot products + _FORCE_INLINE_ real_t tdotx(const Vector3& v) const { + return elements[0][0] * v[0] + elements[1][0] * v[1] + elements[2][0] * v[2]; + } + _FORCE_INLINE_ real_t tdoty(const Vector3& v) const { + return elements[0][1] * v[0] + elements[1][1] * v[1] + elements[2][1] * v[2]; + } + _FORCE_INLINE_ real_t tdotz(const Vector3& v) const { + return elements[0][2] * v[0] + elements[1][2] * v[1] + elements[2][2] * v[2]; + } + + bool operator==(const Matrix3& p_matrix) const; + bool operator!=(const Matrix3& p_matrix) const; + + _FORCE_INLINE_ Vector3 xform(const Vector3& p_vector) const; + _FORCE_INLINE_ Vector3 xform_inv(const Vector3& p_vector) const; + _FORCE_INLINE_ void operator*=(const Matrix3& p_matrix); + _FORCE_INLINE_ Matrix3 operator*(const Matrix3& p_matrix) const; + + int get_orthogonal_index() const; + void set_orthogonal_index(int p_index); + + operator String() const; + + void get_axis_and_angle(Vector3 &r_axis,real_t& r_angle) const; + + /* create / set */ + + + _FORCE_INLINE_ void set(real_t xx, real_t xy, real_t xz, real_t yx, real_t yy, real_t yz, real_t zx, real_t zy, real_t zz) { + + elements[0][0]=xx; + elements[0][1]=xy; + elements[0][2]=xz; + elements[1][0]=yx; + elements[1][1]=yy; + elements[1][2]=yz; + elements[2][0]=zx; + elements[2][1]=zy; + elements[2][2]=zz; + } + _FORCE_INLINE_ Vector3 get_column(int i) const { + + return Vector3(elements[0][i],elements[1][i],elements[2][i]); + } + + _FORCE_INLINE_ Vector3 get_row(int i) const { + + return Vector3(elements[i][0],elements[i][1],elements[i][2]); + } + _FORCE_INLINE_ void set_row(int i, const Vector3& p_row) { + elements[i][0]=p_row.x; + elements[i][1]=p_row.y; + elements[i][2]=p_row.z; + } + + _FORCE_INLINE_ void set_zero() { + elements[0].zero(); + elements[1].zero(); + elements[2].zero(); + } + + _FORCE_INLINE_ Matrix3 transpose_xform(const Matrix3& m) const + { + return Matrix3( + elements[0].x * m[0].x + elements[1].x * m[1].x + elements[2].x * m[2].x, + elements[0].x * m[0].y + elements[1].x * m[1].y + elements[2].x * m[2].y, + elements[0].x * m[0].z + elements[1].x * m[1].z + elements[2].x * m[2].z, + elements[0].y * m[0].x + elements[1].y * m[1].x + elements[2].y * m[2].x, + elements[0].y * m[0].y + elements[1].y * m[1].y + elements[2].y * m[2].y, + elements[0].y * m[0].z + elements[1].y * m[1].z + elements[2].y * m[2].z, + elements[0].z * m[0].x + elements[1].z * m[1].x + elements[2].z * m[2].x, + elements[0].z * m[0].y + elements[1].z * m[1].y + elements[2].z * m[2].y, + elements[0].z * m[0].z + elements[1].z * m[1].z + elements[2].z * m[2].z); + } + Matrix3(real_t xx, real_t xy, real_t xz, real_t yx, real_t yy, real_t yz, real_t zx, real_t zy, real_t zz) { + + set(xx, xy, xz, yx, yy, yz, zx, zy, zz); + } + + void orthonormalize(); + Matrix3 orthonormalized() const; + + operator Quat() const; + + Matrix3(const Quat& p_quat); // euler + Matrix3(const Vector3& p_euler); // euler + Matrix3(const Vector3& p_axis, real_t p_phi); + + _FORCE_INLINE_ Matrix3() { + + elements[0][0]=1; + elements[0][1]=0; + elements[0][2]=0; + elements[1][0]=0; + elements[1][1]=1; + elements[1][2]=0; + elements[2][0]=0; + elements[2][1]=0; + elements[2][2]=1; + } + + +}; + +_FORCE_INLINE_ void Matrix3::operator*=(const Matrix3& p_matrix) { + + set( + p_matrix.tdotx(elements[0]), p_matrix.tdoty(elements[0]), p_matrix.tdotz(elements[0]), + p_matrix.tdotx(elements[1]), p_matrix.tdoty(elements[1]), p_matrix.tdotz(elements[1]), + p_matrix.tdotx(elements[2]), p_matrix.tdoty(elements[2]), p_matrix.tdotz(elements[2])); + +} + +_FORCE_INLINE_ Matrix3 Matrix3::operator*(const Matrix3& p_matrix) const { + + return Matrix3( + p_matrix.tdotx(elements[0]), p_matrix.tdoty(elements[0]), p_matrix.tdotz(elements[0]), + p_matrix.tdotx(elements[1]), p_matrix.tdoty(elements[1]), p_matrix.tdotz(elements[1]), + p_matrix.tdotx(elements[2]), p_matrix.tdoty(elements[2]), p_matrix.tdotz(elements[2]) ); + +} + +Vector3 Matrix3::xform(const Vector3& p_vector) const { + + return Vector3( + elements[0].dot(p_vector), + elements[1].dot(p_vector), + elements[2].dot(p_vector) + ); +} + +Vector3 Matrix3::xform_inv(const Vector3& p_vector) const { + + return Vector3( + (elements[0][0]*p_vector.x ) + ( elements[1][0]*p_vector.y ) + ( elements[2][0]*p_vector.z ), + (elements[0][1]*p_vector.x ) + ( elements[1][1]*p_vector.y ) + ( elements[2][1]*p_vector.z ), + (elements[0][2]*p_vector.x ) + ( elements[1][2]*p_vector.y ) + ( elements[2][2]*p_vector.z ) + ); +} + +float Matrix3::determinant() const { + + return elements[0][0]*(elements[1][1]*elements[2][2] - elements[2][1]*elements[1][2]) - + elements[1][0]*(elements[0][1]*elements[2][2] - elements[2][1]*elements[0][2]) + + elements[2][0]*(elements[0][1]*elements[1][2] - elements[1][1]*elements[0][2]); +} +#endif diff --git a/core/math/octree.h b/core/math/octree.h new file mode 100644 index 00000000000..b64ba381c08 --- /dev/null +++ b/core/math/octree.h @@ -0,0 +1,1461 @@ +/*************************************************************************/ +/* octree.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 OCTREE_H +#define OCTREE_H + +#include "vector3.h" +#include "aabb.h" +#include "list.h" +#include "variant.h" +#include "map.h" +#include "print_string.h" + +/** + @author Juan Linietsky +*/ + +typedef uint32_t OctreeElementID; + +#define OCTREE_ELEMENT_INVALID_ID 0 +#define OCTREE_SIZE_LIMIT 1e15 + +template +class Octree { +public: + + typedef void* (*PairCallback)(void*,OctreeElementID, T*,int,OctreeElementID, T*,int); + typedef void (*UnpairCallback)(void*,OctreeElementID, T*,int,OctreeElementID, T*,int,void*); + +private: + enum { + + NEG=0, + POS=1, + }; + + enum { + OCTANT_NX_NY_NZ, + OCTANT_PX_NY_NZ, + OCTANT_NX_PY_NZ, + OCTANT_PX_PY_NZ, + OCTANT_NX_NY_PZ, + OCTANT_PX_NY_PZ, + OCTANT_NX_PY_PZ, + OCTANT_PX_PY_PZ + }; + + + struct PairKey { + + union { + struct { + OctreeElementID A; + OctreeElementID B; + }; + uint64_t key; + }; + + _FORCE_INLINE_ bool operator<(const PairKey& p_pair) const { + + return key pairable_elements; + List elements; + + Octant() { + children_count=0; + parent_index=-1; + last_pass=0; + parent=NULL; + for (int i=0;i<8;i++) + children[i]=NULL; + } + + ~Octant() { + + //for (int i=0;i<8;i++) + // memdelete_notnull(children[i]); + } + }; + + + struct PairData; + + struct Element { + + Octree *octree; + + T *userdata; + int subindex; + bool pairable; + uint32_t pairable_mask; + uint32_t pairable_type; + + uint64_t last_pass; + OctreeElementID _id; + Octant *common_parent; + + AABB aabb; + AABB container_aabb; + + List pair_list; + + struct OctantOwner { + + Octant *octant; + typename List::Element *E; + }; // an element can be in max 8 octants + + List octant_owners; + + + Element() { last_pass=0; _id=0; pairable=false; subindex=0; userdata=0; octree=0; pairable_mask=0; pairable_type=0; common_parent=NULL; } + }; + + + struct PairData { + + int refcount; + bool intersect; + Element *A,*B; + void *ud; + typename List::Element *eA,*eB; + }; + + typedef Map, AL> ElementMap; + typedef Map, AL> PairMap; + ElementMap element_map; + PairMap pair_map; + + PairCallback pair_callback; + UnpairCallback unpair_callback; + void *pair_callback_userdata; + void *unpair_callback_userdata; + + OctreeElementID last_element_id; + uint64_t pass; + + real_t unit_size; + Octant *root; + int octant_count; + int pair_count; + + + + _FORCE_INLINE_ void _pair_check(PairData *p_pair) { + + bool intersect=p_pair->A->aabb.intersects( p_pair->B->aabb ); + + if (intersect!=p_pair->intersect) { + + if (intersect) { + + if (pair_callback) { + p_pair->ud=pair_callback(pair_callback_userdata,p_pair->A->_id, p_pair->A->userdata,p_pair->A->subindex,p_pair->B->_id, p_pair->B->userdata,p_pair->B->subindex); + + } + pair_count++; + } else { + + + if (unpair_callback) { + unpair_callback(pair_callback_userdata,p_pair->A->_id, p_pair->A->userdata,p_pair->A->subindex,p_pair->B->_id, p_pair->B->userdata,p_pair->B->subindex,p_pair->ud); + } + pair_count--; + + } + + p_pair->intersect=intersect; + + } + } + + _FORCE_INLINE_ void _pair_reference(Element* p_A,Element* p_B) { + + if (p_A==p_B || (p_A->userdata==p_B->userdata && p_A->userdata)) + return; + + if ( !(p_A->pairable_type&p_B->pairable_mask) && + !(p_B->pairable_type&p_A->pairable_mask) ) + return; // none can pair with none + + PairKey key(p_A->_id, p_B->_id); + typename PairMap::Element *E=pair_map.find(key); + + if (!E) { + + PairData pdata; + pdata.refcount=1; + pdata.A=p_A; + pdata.B=p_B; + pdata.intersect=false; + E=pair_map.insert(key,pdata); + E->get().eA=p_A->pair_list.push_back(&E->get()); + E->get().eB=p_B->pair_list.push_back(&E->get()); + +// if (pair_callback) +// pair_callback(pair_callback_userdata,p_A->userdata,p_B->userdata); + } else { + + E->get().refcount++; + } + + } + + _FORCE_INLINE_ void _pair_unreference(Element* p_A,Element* p_B) { + + if (p_A==p_B) + return; + + PairKey key(p_A->_id, p_B->_id); + typename PairMap::Element *E=pair_map.find(key); + if (!E) { + return; // no pair + } + + E->get().refcount--; + + + if (E->get().refcount==0) { + // bye pair + + if (E->get().intersect) { + if (unpair_callback) { + unpair_callback(pair_callback_userdata,p_A->_id, p_A->userdata,p_A->subindex,p_B->_id, p_B->userdata,p_B->subindex,E->get().ud); + } + + pair_count--; + } + + if (p_A==E->get().B) { + //may be reaching inverted + SWAP(p_A,p_B); + } + + p_A->pair_list.erase( E->get().eA ); + p_B->pair_list.erase( E->get().eB ); + pair_map.erase(E); + } + + } + + _FORCE_INLINE_ void _element_check_pairs(Element *p_element) { + + typename List::Element *E=p_element->pair_list.front(); + while(E) { + + _pair_check( E->get() ); + E=E->next(); + } + + } + + _FORCE_INLINE_ void _optimize() { + + + while(root && root->children_count<2 && !root->elements.size() && !(use_pairs && root->pairable_elements.size())) { + + + Octant *new_root=NULL; + if (root->children_count==1) { + + for(int i=0;i<8;i++) { + + if (root->children[i]) { + new_root=root->children[i]; + root->children[i]=NULL; + break; + } + } + ERR_FAIL_COND(!new_root); + new_root->parent=NULL; + new_root->parent_index=-1; + } + + memdelete_allocator( root ); + octant_count--; + root=new_root; + + } + } + + + void _insert_element(Element *p_element,Octant *p_octant); + void _ensure_valid_root(const AABB& p_aabb); + bool _remove_element_from_octant(Element *p_element,Octant *p_octant,Octant *p_limit=NULL); + void _remove_element(Element *p_element); + void _pair_element(Element *p_element,Octant *p_octant); + void _unpair_element(Element *p_element,Octant *p_octant); + + + struct _CullConvexData { + + const Plane* planes; + int plane_count; + T** result_array; + int *result_idx; + int result_max; + uint32_t mask; + }; + + void _cull_convex(Octant *p_octant,_CullConvexData *p_cull); + void _cull_AABB(Octant *p_octant,const AABB& p_aabb, T** p_result_array,int *p_result_idx,int p_result_max,int *p_subindex_array,uint32_t p_mask); + void _cull_segment(Octant *p_octant,const Vector3& p_from, const Vector3& p_to,T** p_result_array,int *p_result_idx,int p_result_max,int *p_subindex_array,uint32_t p_mask); + void _cull_point(Octant *p_octant,const Vector3& p_point,T** p_result_array,int *p_result_idx,int p_result_max,int *p_subindex_array,uint32_t p_mask); + + void _remove_tree(Octant *p_octant) { + + if (!p_octant) + return; + + for(int i=0;i<8;i++) { + + if (p_octant->children[i]) + _remove_tree(p_octant->children[i]); + } + + memdelete_allocator(p_octant); + } +public: + + OctreeElementID create(T* p_userdata, const AABB& p_aabb=AABB(), int p_subindex=0, bool p_pairable=false,uint32_t p_pairable_type=0,uint32_t pairable_mask=1); + void move(OctreeElementID p_id, const AABB& p_aabb); + void set_pairable(OctreeElementID p_id,bool p_pairable=false,uint32_t p_pairable_type=0,uint32_t pairable_mask=1); + void erase(OctreeElementID p_id); + + bool is_pairable(OctreeElementID p_id) const; + T *get(OctreeElementID p_id) const; + int get_subindex(OctreeElementID p_id) const; + + int cull_convex(const Vector& p_convex,T** p_result_array,int p_result_max,uint32_t p_mask=0xFFFFFFFF); + int cull_AABB(const AABB& p_aabb,T** p_result_array,int p_result_max,int *p_subindex_array=NULL,uint32_t p_mask=0xFFFFFFFF); + int cull_segment(const Vector3& p_from, const Vector3& p_to,T** p_result_array,int p_result_max,int *p_subindex_array=NULL,uint32_t p_mask=0xFFFFFFFF); + + int cull_point(const Vector3& p_point,T** p_result_array,int p_result_max,int *p_subindex_array=NULL,uint32_t p_mask=0xFFFFFFFF); + + void set_pair_callback( PairCallback p_callback, void *p_userdata ); + void set_unpair_callback( UnpairCallback p_callback, void *p_userdata ); + + int get_octant_count() const { return octant_count; } + int get_pair_count() const { return pair_count; } + Octree(real_t p_unit_size=1.0); + ~Octree() { _remove_tree(root); } +}; + + +/* PRIVATE FUNCTIONS */ + +template +T *Octree::get(OctreeElementID p_id) const { + const typename ElementMap::Element *E = element_map.find(p_id); + ERR_FAIL_COND_V(!E,NULL); + return E->get().userdata; +} + + +template +bool Octree::is_pairable(OctreeElementID p_id) const { + + const typename ElementMap::Element *E = element_map.find(p_id); + ERR_FAIL_COND_V(!E,false); + return E->get().pairable; +} + +template +int Octree::get_subindex(OctreeElementID p_id) const { + + const typename ElementMap::Element *E = element_map.find(p_id); + ERR_FAIL_COND_V(!E,-1); + return E->get().subindex; +} + +#define OCTREE_DIVISOR 4 + +template +void Octree::_insert_element(Element *p_element,Octant *p_octant) { + + float element_size = p_element->aabb.get_longest_axis_size() * 1.01; // avoid precision issues + + if (p_octant->aabb.size.x/OCTREE_DIVISOR < element_size) { + //if (p_octant->aabb.size.x*0.5 < element_size) { + + /* at smallest possible size for the element */ + typename Element::OctantOwner owner; + owner.octant=p_octant; + + if (use_pairs && p_element->pairable) { + + p_octant->pairable_elements.push_back(p_element); + owner.E = p_octant->pairable_elements.back(); + } else { + + p_octant->elements.push_back(p_element); + owner.E = p_octant->elements.back(); + } + + p_element->octant_owners.push_back( owner ); + + if (p_element->common_parent==NULL) { + p_element->common_parent=p_octant; + p_element->container_aabb=p_octant->aabb; + } else { + p_element->container_aabb.merge_with(p_octant->aabb); + } + + + if (use_pairs && p_octant->children_count>0) { + + pass++; //elements below this only get ONE reference added + + for (int i=0;i<8;i++) { + + if (p_octant->children[i]) { + _pair_element(p_element,p_octant->children[i]); + } + } + } + } else { + /* not big enough, send it to subitems */ + int splits=0; + bool candidate=p_element->common_parent==NULL; + + for (int i=0;i<8;i++) { + + if (p_octant->children[i]) { + /* element exists, go straight to it */ + if (p_octant->children[i]->aabb.intersects( p_element->aabb ) ) { + _insert_element( p_element, p_octant->children[i] ); + splits++; + } + } else { + /* check againt AABB where child should be */ + + AABB aabb=p_octant->aabb; + aabb.size*=0.5; + + if (i&1) + aabb.pos.x+=aabb.size.x; + if (i&2) + aabb.pos.y+=aabb.size.y; + if (i&4) + aabb.pos.z+=aabb.size.z; + + if (aabb.intersects( p_element->aabb) ) { + /* if actually intersects, create the child */ + + Octant *child = memnew_allocator( Octant, AL ); + p_octant->children[i]=child; + child->parent=p_octant; + child->parent_index=i; + + child->aabb=aabb; + + p_octant->children_count++; + + _insert_element( p_element, child ); + octant_count++; + splits++; + + } + } + + } + + if (candidate && splits>1) { + + p_element->common_parent=p_octant; + } + + } + + if (use_pairs) { + + typename List::Element *E=p_octant->pairable_elements.front(); + + while(E) { + _pair_reference( p_element,E->get() ); + E=E->next(); + } + + if (p_element->pairable) { + // and always test non-pairable if element is pairable + E=p_octant->elements.front(); + while(E) { + _pair_reference( p_element,E->get() ); + E=E->next(); + } + } + } + + +} + + +template +void Octree::_ensure_valid_root(const AABB& p_aabb) { + + if (!root) { + // octre is empty + + AABB base( Vector3(), Vector3(1.0,1.0,1.0) * unit_size); + + while ( !base.encloses(p_aabb) ) { + + if ( ABS(base.pos.x+base.size.x) <= ABS(base.pos.x) ) { + /* grow towards positive */ + base.size*=2.0; + } else { + base.pos-=base.size; + base.size*=2.0; + } + } + + root = memnew_allocator( Octant, AL ); + + root->parent=NULL; + root->parent_index=-1; + root->aabb=base; + + octant_count++; + + + } else { + + AABB base=root->aabb; + + while( !base.encloses( p_aabb ) ) { + + if (base.size.x > OCTREE_SIZE_LIMIT) { + ERR_EXPLAIN("Octree upper size limit reeached, does the AABB supplied contain NAN?"); + ERR_FAIL(); + } + + Octant * gp = memnew_allocator( Octant, AL ); + octant_count++; + root->parent=gp; + + if ( ABS(base.pos.x+base.size.x) <= ABS(base.pos.x) ) { + /* grow towards positive */ + base.size*=2.0; + gp->aabb=base; + gp->children[0]=root; + root->parent_index=0; + } else { + base.pos-=base.size; + base.size*=2.0; + gp->aabb=base; + gp->children[(1<<0)|(1<<1)|(1<<2)]=root; // add at all-positive + root->parent_index=(1<<0)|(1<<1)|(1<<2); + } + + gp->children_count=1; + root=gp; + } + } +} + +template +bool Octree::_remove_element_from_octant(Element *p_element,Octant *p_octant,Octant *p_limit) { + + bool octant_removed=false; + + while(true) { + + // check all exit conditions + + if (p_octant==p_limit) // reached limit, nothing to erase, exit + return octant_removed; + + bool unpaired=false; + + if (use_pairs && p_octant->last_pass!=pass) { + // check wether we should unpair stuff + // always test pairable + typename List::Element *E=p_octant->pairable_elements.front(); + while(E) { + _pair_unreference( p_element,E->get() ); + E=E->next(); + } + if (p_element->pairable) { + // and always test non-pairable if element is pairable + E=p_octant->elements.front(); + while(E) { + _pair_unreference( p_element,E->get() ); + E=E->next(); + } + } + p_octant->last_pass=pass; + unpaired=true; + } + + bool removed=false; + + Octant *parent=p_octant->parent; + + if (p_octant->children_count==0 && p_octant->elements.empty() && p_octant->pairable_elements.empty()) { + + // erase octant + + if (p_octant==root) { // won't have a parent, just erase + + root=NULL; + } else { + ERR_FAIL_INDEX_V(p_octant->parent_index,8,octant_removed); + + parent->children[ p_octant->parent_index ]=NULL; + parent->children_count--; + } + + memdelete_allocator(p_octant); + octant_count--; + removed=true; + octant_removed=true; + } + + if (!removed && !unpaired) + return octant_removed; // no reason to keep going up anymore! was already visited and was not removed + + p_octant=parent; + + } + + return octant_removed; +} + +template +void Octree::_unpair_element(Element *p_element,Octant *p_octant) { + + + // always test pairable + typename List::Element *E=p_octant->pairable_elements.front(); + while(E) { + if (E->get()->last_pass!=pass) { // only remove ONE reference + _pair_unreference( p_element,E->get() ); + E->get()->last_pass=pass; + } + E=E->next(); + } + + if (p_element->pairable) { + // and always test non-pairable if element is pairable + E=p_octant->elements.front(); + while(E) { + if (E->get()->last_pass!=pass) { // only remove ONE reference + _pair_unreference( p_element,E->get() ); + E->get()->last_pass=pass; + } + E=E->next(); + } + } + + p_octant->last_pass=pass; + + if (p_octant->children_count==0) + return; // small optimization for leafs + + for (int i=0;i<8;i++) { + + if (p_octant->children[i]) + _unpair_element(p_element,p_octant->children[i]); + } +} + +template +void Octree::_pair_element(Element *p_element,Octant *p_octant) { + + // always test pairable + + typename List::Element *E=p_octant->pairable_elements.front(); + + while(E) { + + if (E->get()->last_pass!=pass) { // only get ONE reference + _pair_reference( p_element,E->get() ); + E->get()->last_pass=pass; + } + E=E->next(); + } + + if (p_element->pairable) { + // and always test non-pairable if element is pairable + E=p_octant->elements.front(); + while(E) { + if (E->get()->last_pass!=pass) { // only get ONE reference + _pair_reference( p_element,E->get() ); + E->get()->last_pass=pass; + } + E=E->next(); + } + } + p_octant->last_pass=pass; + + if (p_octant->children_count==0) + return; // small optimization for leafs + + for (int i=0;i<8;i++) { + + if (p_octant->children[i]) + _pair_element(p_element,p_octant->children[i]); + } +} + +template +void Octree::_remove_element(Element *p_element) { + + pass++; // will do a new pass for this + + typename List< typename Element::OctantOwner,AL >::Element *I=p_element->octant_owners.front(); + + + /* FIRST remove going up normally */ + for(;I;I=I->next()) { + + Octant *o=I->get().octant; + + if (!use_pairs) // small speedup + o->elements.erase( I->get().E ); + + _remove_element_from_octant( p_element, o ); + + } + + /* THEN remove going down */ + + I=p_element->octant_owners.front(); + + if (use_pairs) { + + for(;I;I=I->next()) { + + Octant *o=I->get().octant; + + // erase children pairs, they are erased ONCE even if repeated + pass++; + for (int i=0;i<8;i++) { + + if (o->children[i]) + _unpair_element(p_element,o->children[i]); + } + + if (p_element->pairable) + o->pairable_elements.erase( I->get().E ); + else + o->elements.erase( I->get().E ); + + } + } + + p_element->octant_owners.clear(); + + if(use_pairs) { + + int remaining=p_element->pair_list.size(); + //p_element->pair_list.clear(); + ERR_FAIL_COND( remaining ); + } + +} + +template +OctreeElementID Octree::create(T* p_userdata, const AABB& p_aabb, int p_subindex,bool p_pairable,uint32_t p_pairable_type,uint32_t p_pairable_mask) { + + // check for AABB validity +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_V( p_aabb.pos.x > 1e15 || p_aabb.pos.x < -1e15, 0 ); + ERR_FAIL_COND_V( p_aabb.pos.y > 1e15 || p_aabb.pos.y < -1e15, 0 ); + ERR_FAIL_COND_V( p_aabb.pos.z > 1e15 || p_aabb.pos.z < -1e15, 0 ); + ERR_FAIL_COND_V( p_aabb.size.x > 1e15 || p_aabb.size.x < 0.0, 0 ); + ERR_FAIL_COND_V( p_aabb.size.y > 1e15 || p_aabb.size.y < 0.0, 0 ); + ERR_FAIL_COND_V( p_aabb.size.z > 1e15 || p_aabb.size.z < 0.0, 0 ); + ERR_FAIL_COND_V( Math::is_nan(p_aabb.size.x) , 0 ); + ERR_FAIL_COND_V( Math::is_nan(p_aabb.size.y) , 0 ); + ERR_FAIL_COND_V( Math::is_nan(p_aabb.size.z) , 0 ); + + +#endif + typename ElementMap::Element *E = element_map.insert(last_element_id++, + Element()); + Element &e = E->get(); + + e.aabb=p_aabb; + e.userdata=p_userdata; + e.subindex=p_subindex; + e.last_pass=0; + e.octree=this; + e.pairable=p_pairable; + e.pairable_type=p_pairable_type; + e.pairable_mask=p_pairable_mask; + e._id=last_element_id-1; + + if (!e.aabb.has_no_surface()) { + _ensure_valid_root(p_aabb); + _insert_element(&e,root); + if (use_pairs) + _element_check_pairs(&e); + } + + return last_element_id-1; +} + + + +template +void Octree::move(OctreeElementID p_id, const AABB& p_aabb) { + +#ifdef DEBUG_ENABLED + // check for AABB validity + ERR_FAIL_COND( p_aabb.pos.x > 1e15 || p_aabb.pos.x < -1e15 ); + ERR_FAIL_COND( p_aabb.pos.y > 1e15 || p_aabb.pos.y < -1e15 ); + ERR_FAIL_COND( p_aabb.pos.z > 1e15 || p_aabb.pos.z < -1e15 ); + ERR_FAIL_COND( p_aabb.size.x > 1e15 || p_aabb.size.x < 0.0 ); + ERR_FAIL_COND( p_aabb.size.y > 1e15 || p_aabb.size.y < 0.0 ); + ERR_FAIL_COND( p_aabb.size.z > 1e15 || p_aabb.size.z < 0.0 ); + ERR_FAIL_COND( Math::is_nan(p_aabb.size.x) ); + ERR_FAIL_COND( Math::is_nan(p_aabb.size.y) ); + ERR_FAIL_COND( Math::is_nan(p_aabb.size.z) ); +#endif + typename ElementMap::Element *E = element_map.find(p_id); + ERR_FAIL_COND(!E); + Element &e = E->get(); + +#if 0 + + pass++; + if (!e.aabb.has_no_surface()) { + _remove_element(&e); + } + + e.aabb=p_aabb; + + if (!e.aabb.has_no_surface()) { + _ensure_valid_root(p_aabb); + + _insert_element(&e,root); + if (use_pairs) + _element_check_pairs(&e); + + } + + _optimize(); + +#else + + bool old_has_surf=!e.aabb.has_no_surface(); + bool new_has_surf=!p_aabb.has_no_surface(); + + if (old_has_surf!=new_has_surf) { + + + if (old_has_surf) { + _remove_element(&e); // removing + e.common_parent=NULL; + e.aabb=AABB(); + _optimize(); + } else { + _ensure_valid_root(p_aabb); // inserting + e.common_parent=NULL; + e.aabb=p_aabb; + _insert_element(&e,root); + if (use_pairs) + _element_check_pairs(&e); + + } + + return; + } + + if (!old_has_surf) // doing nothing + return; + + // it still is enclosed in the same AABB it was assigned to + if (e.container_aabb.encloses(p_aabb)) { + + e.aabb=p_aabb; + if (use_pairs) + _element_check_pairs(&e); // must check pairs anyway + + + return; + } + + AABB combined=e.aabb; + combined.merge_with(p_aabb); + _ensure_valid_root(combined); + + ERR_FAIL_COND( e.octant_owners.front()==NULL ); + + /* FIND COMMON PARENT */ + + List owners = e.octant_owners; // save the octant owners + Octant *common_parent=e.common_parent; + ERR_FAIL_COND(!common_parent); + + + //src is now the place towards where insertion is going to happen + pass++; + + while(common_parent && !common_parent->aabb.encloses(p_aabb)) + common_parent=common_parent->parent; + + ERR_FAIL_COND(!common_parent); + + //prepare for reinsert + e.octant_owners.clear(); + e.common_parent=NULL; + e.aabb=p_aabb; + + _insert_element(&e,common_parent); // reinsert from this point + + pass++; + + for(typename List::Element *E=owners.front();E;) { + + Octant *o=E->get().octant; + typename List::Element *N=E->next(); + +// if (!use_pairs) +// o->elements.erase( E->get().E ); + + if (use_pairs && e.pairable) + o->pairable_elements.erase( E->get().E ); + else + o->elements.erase( E->get().E ); + + if (_remove_element_from_octant( &e, o, common_parent->parent )) { + + owners.erase(E); + } + + E=N; + } + + + if (use_pairs) { + //unpair child elements in anything that survived + for(typename List::Element *E=owners.front();E;E=E->next()) { + + Octant *o=E->get().octant; + + // erase children pairs, unref ONCE + pass++; + for (int i=0;i<8;i++) { + + if (o->children[i]) + _unpair_element(&e,o->children[i]); + } + + } + + _element_check_pairs(&e); + } + + + _optimize(); +#endif + + +} + +template +void Octree::set_pairable(OctreeElementID p_id,bool p_pairable,uint32_t p_pairable_type,uint32_t p_pairable_mask) { + + typename ElementMap::Element *E = element_map.find(p_id); + ERR_FAIL_COND(!E); + + Element &e = E->get(); + + if (p_pairable == e.pairable && e.pairable_type==p_pairable_type && e.pairable_mask==p_pairable_mask) + return; // no changes, return + + if (!e.aabb.has_no_surface()) { + _remove_element(&e); + } + + e.pairable=p_pairable; + e.pairable_type=p_pairable_type; + e.pairable_mask=p_pairable_mask; + e.common_parent=NULL; + + if (!e.aabb.has_no_surface()) { + _ensure_valid_root(e.aabb); + _insert_element(&e,root); + if (use_pairs) + _element_check_pairs(&e); + + } +} + + +template +void Octree::erase(OctreeElementID p_id) { + + typename ElementMap::Element *E = element_map.find(p_id); + ERR_FAIL_COND(!E); + + Element &e = E->get(); + + if (!e.aabb.has_no_surface()) { + + _remove_element(&e); + } + + element_map.erase(p_id); + _optimize(); +} + +template +void Octree::_cull_convex(Octant *p_octant,_CullConvexData *p_cull) { + + if (*p_cull->result_idx==p_cull->result_max) + return; //pointless + + if (!p_octant->elements.empty()) { + + typename List< Element*,AL >::Element *I; + I=p_octant->elements.front(); + + for(;I;I=I->next()) { + + Element *e=I->get(); + + if (e->last_pass==pass || (use_pairs && !(e->pairable_type&p_cull->mask))) + continue; + e->last_pass=pass; + + if (e->aabb.intersects_convex_shape(p_cull->planes,p_cull->plane_count)) { + + if (*p_cull->result_idxresult_max) { + p_cull->result_array[*p_cull->result_idx] = e->userdata; + (*p_cull->result_idx)++; + } else { + + return; // pointless to continue + } + } + } + } + + if (use_pairs && !p_octant->pairable_elements.empty()) { + + typename List< Element*,AL >::Element *I; + I=p_octant->pairable_elements.front(); + + for(;I;I=I->next()) { + + Element *e=I->get(); + + if (e->last_pass==pass || (use_pairs && !(e->pairable_type&p_cull->mask))) + continue; + e->last_pass=pass; + + if (e->aabb.intersects_convex_shape(p_cull->planes,p_cull->plane_count)) { + + if (*p_cull->result_idxresult_max) { + + p_cull->result_array[*p_cull->result_idx] = e->userdata; + (*p_cull->result_idx)++; + } else { + + return; // pointless to continue + } + } + } + } + + for (int i=0;i<8;i++) { + + if (p_octant->children[i] && p_octant->children[i]->aabb.intersects_convex_shape(p_cull->planes,p_cull->plane_count)) { + _cull_convex(p_octant->children[i],p_cull); + } + } +} + + +template +void Octree::_cull_AABB(Octant *p_octant,const AABB& p_aabb, T** p_result_array,int *p_result_idx,int p_result_max,int *p_subindex_array,uint32_t p_mask) { + + if (*p_result_idx==p_result_max) + return; //pointless + + if (!p_octant->elements.empty()) { + + typename List< Element*,AL >::Element *I; + I=p_octant->elements.front(); + for(;I;I=I->next()) { + + Element *e=I->get(); + + if (e->last_pass==pass || (use_pairs && !(e->pairable_type&p_mask))) + continue; + e->last_pass=pass; + + if (p_aabb.intersects(e->aabb)) { + + if (*p_result_idxuserdata; + if (p_subindex_array) + p_subindex_array[*p_result_idx] = e->subindex; + + (*p_result_idx)++; + } else { + + return; // pointless to continue + } + } + } + } + + if (use_pairs && !p_octant->pairable_elements.empty()) { + + typename List< Element*,AL >::Element *I; + I=p_octant->pairable_elements.front(); + for(;I;I=I->next()) { + + Element *e=I->get(); + + if (e->last_pass==pass || (use_pairs && !(e->pairable_type&p_mask))) + continue; + e->last_pass=pass; + + if (p_aabb.intersects(e->aabb)) { + + if (*p_result_idxuserdata; + if (p_subindex_array) + p_subindex_array[*p_result_idx] = e->subindex; + (*p_result_idx)++; + } else { + + return; // pointless to continue + } + } + } + } + + for (int i=0;i<8;i++) { + + if (p_octant->children[i] && p_octant->children[i]->aabb.intersects(p_aabb)) { + _cull_AABB(p_octant->children[i],p_aabb, p_result_array,p_result_idx,p_result_max,p_subindex_array,p_mask); + } + } + +} + +template +void Octree::_cull_segment(Octant *p_octant,const Vector3& p_from, const Vector3& p_to,T** p_result_array,int *p_result_idx,int p_result_max,int *p_subindex_array,uint32_t p_mask) { + + if (*p_result_idx==p_result_max) + return; //pointless + + if (!p_octant->elements.empty()) { + + typename List< Element*,AL >::Element *I; + I=p_octant->elements.front(); + for(;I;I=I->next()) { + + Element *e=I->get(); + + if (e->last_pass==pass || (use_pairs && !(e->pairable_type&p_mask))) + continue; + e->last_pass=pass; + + if (e->aabb.intersects_segment(p_from,p_to)) { + + if (*p_result_idxuserdata; + if (p_subindex_array) + p_subindex_array[*p_result_idx] = e->subindex; + (*p_result_idx)++; + + } else { + + return; // pointless to continue + } + } + } + } + + if (use_pairs && !p_octant->pairable_elements.empty()) { + + typename List< Element*,AL >::Element *I; + I=p_octant->pairable_elements.front(); + for(;I;I=I->next()) { + + Element *e=I->get(); + + if (e->last_pass==pass || (use_pairs && !(e->pairable_type&p_mask))) + continue; + + e->last_pass=pass; + + if (e->aabb.intersects_segment(p_from,p_to)) { + + if (*p_result_idxuserdata; + if (p_subindex_array) + p_subindex_array[*p_result_idx] = e->subindex; + + (*p_result_idx)++; + + } else { + + return; // pointless to continue + } + } + } + } + + + for (int i=0;i<8;i++) { + + if (p_octant->children[i] && p_octant->children[i]->aabb.intersects_segment(p_from,p_to)) { + _cull_segment(p_octant->children[i],p_from,p_to, p_result_array,p_result_idx,p_result_max,p_subindex_array,p_mask); + } + } +} + + +template +void Octree::_cull_point(Octant *p_octant,const Vector3& p_point,T** p_result_array,int *p_result_idx,int p_result_max,int *p_subindex_array,uint32_t p_mask) { + + if (*p_result_idx==p_result_max) + return; //pointless + + if (!p_octant->elements.empty()) { + + typename List< Element*,AL >::Element *I; + I=p_octant->elements.front(); + for(;I;I=I->next()) { + + Element *e=I->get(); + + if (e->last_pass==pass || (use_pairs && !(e->pairable_type&p_mask))) + continue; + e->last_pass=pass; + + if (e->aabb.has_point(p_point)) { + + if (*p_result_idxuserdata; + if (p_subindex_array) + p_subindex_array[*p_result_idx] = e->subindex; + (*p_result_idx)++; + + } else { + + return; // pointless to continue + } + } + } + } + + if (use_pairs && !p_octant->pairable_elements.empty()) { + + typename List< Element*,AL >::Element *I; + I=p_octant->pairable_elements.front(); + for(;I;I=I->next()) { + + Element *e=I->get(); + + if (e->last_pass==pass || (use_pairs && !(e->pairable_type&p_mask))) + continue; + + e->last_pass=pass; + + if (e->aabb.has_point(p_point)) { + + if (*p_result_idxuserdata; + if (p_subindex_array) + p_subindex_array[*p_result_idx] = e->subindex; + + (*p_result_idx)++; + + } else { + + return; // pointless to continue + } + } + } + } + + + for (int i=0;i<8;i++) { + + //could be optimized.. + if (p_octant->children[i] && p_octant->children[i]->aabb.has_point(p_point)) { + _cull_point(p_octant->children[i],p_point, p_result_array,p_result_idx,p_result_max,p_subindex_array,p_mask); + } + } +} + +template +int Octree::cull_convex(const Vector& p_convex,T** p_result_array,int p_result_max,uint32_t p_mask) { + + if (!root) + return 0; + + int result_count=0; + pass++; + _CullConvexData cdata; + cdata.planes=&p_convex[0]; + cdata.plane_count=p_convex.size(); + cdata.result_array=p_result_array; + cdata.result_max=p_result_max; + cdata.result_idx=&result_count; + cdata.mask=p_mask; + + _cull_convex(root,&cdata); + + return result_count; +} + + + +template +int Octree::cull_AABB(const AABB& p_aabb,T** p_result_array,int p_result_max,int *p_subindex_array,uint32_t p_mask) { + + + if (!root) + return 0; + + int result_count=0; + pass++; + _cull_AABB(root,p_aabb,p_result_array,&result_count,p_result_max,p_subindex_array,p_mask); + + return result_count; +} + + +template +int Octree::cull_segment(const Vector3& p_from, const Vector3& p_to,T** p_result_array,int p_result_max,int *p_subindex_array,uint32_t p_mask) { + + if (!root) + return 0; + + int result_count=0; + pass++; + _cull_segment(root,p_from,p_to,p_result_array,&result_count,p_result_max,p_subindex_array,p_mask); + + return result_count; + +} + +template +int Octree::cull_point(const Vector3& p_point,T** p_result_array,int p_result_max,int *p_subindex_array,uint32_t p_mask) { + + if (!root) + return 0; + + int result_count=0; + pass++; + _cull_point(root,p_point,p_result_array,&result_count,p_result_max,p_subindex_array,p_mask); + + return result_count; + +} + + +template +void Octree::set_pair_callback( PairCallback p_callback, void *p_userdata ) { + + pair_callback=p_callback; + pair_callback_userdata=p_userdata; +} +template +void Octree::set_unpair_callback( UnpairCallback p_callback, void *p_userdata ) { + + unpair_callback=p_callback; + unpair_callback_userdata=p_userdata; + +} + + +template +Octree::Octree(real_t p_unit_size) { + + last_element_id=1; + pass=1; + unit_size=p_unit_size; + root=NULL; + + octant_count=0; + pair_count=0; + + pair_callback=NULL; + unpair_callback=NULL; + pair_callback_userdata=NULL; + unpair_callback_userdata=NULL; + + + + +} + + + + +#endif diff --git a/core/math/plane.cpp b/core/math/plane.cpp new file mode 100644 index 00000000000..88c7be5f63e --- /dev/null +++ b/core/math/plane.cpp @@ -0,0 +1,166 @@ +/*************************************************************************/ +/* plane.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 "plane.h" + +#include "math_funcs.h" + +#define _PLANE_EQ_DOT_EPSILON 0.999 +#define _PLANE_EQ_D_EPSILON 0.0001 + +void Plane::set_normal(const Vector3& p_normal) { + + normal=p_normal; +} + +void Plane::normalize() { + + real_t l = normal.length(); + if (l==0) { + *this=Plane(0,0,0,0); + return; + } + normal/=l; + d/=l; +} + +Plane Plane::normalized() const { + + Plane p = *this; + p.normalize(); + return p; +} + +Vector3 Plane::get_any_point() const { + + return get_normal()*d; +} + +Vector3 Plane::get_any_perpendicular_normal() const { + + static const Vector3 p1 = Vector3(1,0,0); + static const Vector3 p2 = Vector3(0,1,0); + Vector3 p; + + if (ABS(normal.dot(p1)) > 0.99) // if too similar to p1 + p=p2; // use p2 + else + p=p1; // use p1 + + p-=normal * normal.dot(p); + p.normalize(); + + return p; +} + +/* intersections */ + +bool Plane::intersect_3(const Plane &p_plane1, const Plane &p_plane2, Vector3 *r_result) const { + + const Plane &p_plane0=*this; + Vector3 normal0=p_plane0.normal; + Vector3 normal1=p_plane1.normal; + Vector3 normal2=p_plane2.normal; + + real_t denom=vec3_cross(normal0,normal1).dot(normal2); + + if (ABS(denom)<=CMP_EPSILON) + return false; + + if (r_result) { + *r_result = ( (vec3_cross(normal1, normal2) * p_plane0.d) + + (vec3_cross(normal2, normal0) * p_plane1.d) + + (vec3_cross(normal0, normal1) * p_plane2.d) )/denom; + } + + return true; +} + + +bool Plane::intersects_ray(Vector3 p_from, Vector3 p_dir, Vector3* p_intersection) const { + + Vector3 segment=p_dir; + real_t den=normal.dot( segment ); + + //printf("den is %i\n",den); + if (Math::abs(den)<=CMP_EPSILON) { + + return false; + } + + real_t dist=(normal.dot( p_from ) - d) / den; + //printf("dist is %i\n",dist); + + if (dist>CMP_EPSILON) { //this is a ray, before the emiting pos (p_from) doesnt exist + + return false; + } + + dist=-dist; + *p_intersection = p_from + segment * dist; + + return true; +} + +bool Plane::intersects_segment(Vector3 p_begin, Vector3 p_end, Vector3* p_intersection) const { + + Vector3 segment= p_begin - p_end; + real_t den=normal.dot( segment ); + + //printf("den is %i\n",den); + if (Math::abs(den)<=CMP_EPSILON) { + + return false; + } + + real_t dist=(normal.dot( p_begin ) - d) / den; + //printf("dist is %i\n",dist); + + if (dist<-CMP_EPSILON || dist > (1.0 +CMP_EPSILON)) { + + return false; + } + + dist=-dist; + *p_intersection = p_begin + segment * dist; + + return true; +} + +/* misc */ + +bool Plane::is_almost_like(const Plane& p_plane) const { + + return (normal.dot( p_plane.normal ) > _PLANE_EQ_DOT_EPSILON && Math::absd(d-p_plane.d) < _PLANE_EQ_D_EPSILON); +} + + +Plane::operator String() const { + + return normal.operator String() + ", " + rtos(d); +} diff --git a/core/math/plane.h b/core/math/plane.h new file mode 100644 index 00000000000..608ec26926d --- /dev/null +++ b/core/math/plane.h @@ -0,0 +1,146 @@ +/*************************************************************************/ +/* plane.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 PLANE_H +#define PLANE_H + + +#include "vector3.h" + +class Plane { +public: + + Vector3 normal; + real_t d; + + + void set_normal(const Vector3& p_normal); + _FORCE_INLINE_ Vector3 get_normal() const { return normal; }; ///Point is coplanar, CMP_EPSILON for precision + + void normalize(); + Plane normalized() const; + + /* Plane-Point operations */ + + _FORCE_INLINE_ Vector3 center() const { return normal*d; } + Vector3 get_any_point() const; + Vector3 get_any_perpendicular_normal() const; + + _FORCE_INLINE_ bool is_point_over(const Vector3 &p_point) const; ///< Point is over plane + _FORCE_INLINE_ real_t distance_to(const Vector3 &p_point) const; + _FORCE_INLINE_ bool has_point(const Vector3 &p_point,real_t _epsilon=CMP_EPSILON) const; + + /* intersections */ + + bool intersect_3(const Plane &p_plane1, const Plane &p_plane2, Vector3 *r_result=0) const; + bool intersects_ray(Vector3 p_from, Vector3 p_dir, Vector3* p_intersection) const; + bool intersects_segment(Vector3 p_begin, Vector3 p_end, Vector3* p_intersection) const; + + _FORCE_INLINE_ Vector3 project(const Vector3& p_point) const { + + return p_point - normal * distance_to(p_point); + } + + /* misc */ + + Plane operator-() const { return Plane(-normal,-d); } + bool is_almost_like(const Plane& p_plane) const; + + _FORCE_INLINE_ bool operator==(const Plane& p_plane) const; + _FORCE_INLINE_ bool operator!=(const Plane& p_plane) const; + operator String() const; + + _FORCE_INLINE_ Plane() { d=0; } + _FORCE_INLINE_ Plane(real_t p_a, real_t p_b, real_t p_c, real_t p_d) : normal(p_a,p_b,p_c), d(p_d) { }; + + _FORCE_INLINE_ Plane(const Vector3 &p_normal, real_t p_d); + _FORCE_INLINE_ Plane(const Vector3 &p_point, const Vector3& p_normal); + _FORCE_INLINE_ Plane(const Vector3 &p_point1, const Vector3 &p_point2,const Vector3 &p_point3,ClockDirection p_dir = CLOCKWISE); + + +}; + + + +bool Plane::is_point_over(const Vector3 &p_point) const { + + return (normal.dot(p_point) > d); +} + +real_t Plane::distance_to(const Vector3 &p_point) const { + + return (normal.dot(p_point)-d); +} + +bool Plane::has_point(const Vector3 &p_point,real_t _epsilon) const { + + float dist=normal.dot(p_point) - d; + dist=ABS(dist); + return ( dist <= _epsilon); + +} + +Plane::Plane(const Vector3 &p_normal, real_t p_d) { + + normal=p_normal; + d=p_d; +} + +Plane::Plane(const Vector3 &p_point, const Vector3& p_normal) { + + normal=p_normal; + d=p_normal.dot(p_point); +} + +Plane::Plane(const Vector3 &p_point1, const Vector3 &p_point2, const Vector3 &p_point3,ClockDirection p_dir) { + + if (p_dir == CLOCKWISE) + normal=(p_point1-p_point3).cross(p_point1-p_point2); + else + normal=(p_point1-p_point2).cross(p_point1-p_point3); + + + normal.normalize(); + d = normal.dot(p_point1); + + +} + +bool Plane::operator==(const Plane& p_plane) const { + + return normal==p_plane.normal && d == p_plane.d; +} + +bool Plane::operator!=(const Plane& p_plane) const { + + return normal!=p_plane.normal || d != p_plane.d; + +} + +#endif // PLANE_H + diff --git a/core/math/quat.cpp b/core/math/quat.cpp new file mode 100644 index 00000000000..950a4756ad2 --- /dev/null +++ b/core/math/quat.cpp @@ -0,0 +1,267 @@ +/*************************************************************************/ +/* quat.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 "quat.h" +#include "print_string.h" + +void Quat::set_euler(const Vector3& p_euler) { + real_t half_yaw = p_euler.x * 0.5; + real_t half_pitch = p_euler.y * 0.5; + real_t half_roll = p_euler.z * 0.5; + real_t cos_yaw = Math::cos(half_yaw); + real_t sin_yaw = Math::sin(half_yaw); + real_t cos_pitch = Math::cos(half_pitch); + real_t sin_pitch = Math::sin(half_pitch); + real_t cos_roll = Math::cos(half_roll); + real_t sin_roll = Math::sin(half_roll); + set(cos_roll * sin_pitch * cos_yaw+sin_roll * cos_pitch * sin_yaw, + cos_roll * cos_pitch * sin_yaw - sin_roll * sin_pitch * cos_yaw, + sin_roll * cos_pitch * cos_yaw - cos_roll * sin_pitch * sin_yaw, + cos_roll * cos_pitch * cos_yaw+sin_roll * sin_pitch * sin_yaw); +} + +void Quat::operator*=(const Quat& q) { + + set(w * q.x+x * q.w+y * q.z - z * q.y, + w * q.y+y * q.w+z * q.x - x * q.z, + w * q.z+z * q.w+x * q.y - y * q.x, + w * q.w - x * q.x - y * q.y - z * q.z); +} + +Quat Quat::operator*(const Quat& q) const { + + Quat r=*this; + r*=q; + return r; +} + + + + +real_t Quat::length() const { + + return Math::sqrt(length_squared()); +} + +void Quat::normalize() { + *this /= length(); +} + + +Quat Quat::normalized() const { + return *this / length(); +} + +Quat Quat::inverse() const { + return Quat( -x, -y, -z, w ); +} + + +Quat Quat::slerp(const Quat& q, const real_t& t) const { + +#if 0 + + + Quat dst=q; + Quat src=*this; + + src.normalize(); + dst.normalize(); + + real_t cosine = dst.dot(src); + + if (cosine < 0 && true) { + cosine = -cosine; + dst = -dst; + } else { + dst = dst; + } + + if (Math::abs(cosine) < 1 - CMP_EPSILON) { + // Standard case (slerp) + real_t sine = Math::sqrt(1 - cosine*cosine); + real_t angle = Math::atan2(sine, cosine); + real_t inv_sine = 1.0f / sine; + real_t coeff_0 = Math::sin((1.0f - t) * angle) * inv_sine; + real_t coeff_1 = Math::sin(t * angle) * inv_sine; + Quat ret= src * coeff_0 + dst * coeff_1; + + return ret; + } else { + // There are two situations: + // 1. "rkP" and "q" are very close (cosine ~= +1), so we can do a linear + // interpolation safely. + // 2. "rkP" and "q" are almost invedste of each other (cosine ~= -1), there + // are an infinite number of possibilities interpolation. but we haven't + // have method to fix this case, so just use linear interpolation here. + Quat ret = src * (1.0f - t) + dst *t; + // taking the complement requires renormalisation + ret.normalize(); + return ret; + } +#else + + real_t to1[4]; + real_t omega, cosom, sinom, scale0, scale1; + + + // calc cosine + cosom = x * q.x + y * q.y + z * q.z + + w * q.w; + + + // adjust signs (if necessary) + if ( cosom <0.0 ) { + cosom = -cosom; to1[0] = - q.x; + to1[1] = - q.y; + to1[2] = - q.z; + to1[3] = - q.w; + } else { + to1[0] = q.x; + to1[1] = q.y; + to1[2] = q.z; + to1[3] = q.w; + } + + + // calculate coefficients + + if ( (1.0 - cosom) > CMP_EPSILON ) { + // standard case (slerp) + omega = Math::acos(cosom); + sinom = Math::sin(omega); + scale0 = Math::sin((1.0 - t) * omega) / sinom; + scale1 = Math::sin(t * omega) / sinom; + } else { + // "from" and "to" quaternions are very close + // ... so we can do a linear interpolation + scale0 = 1.0 - t; + scale1 = t; + } + // calculate final values + return Quat( + scale0 * x + scale1 * to1[0], + scale0 * y + scale1 * to1[1], + scale0 * z + scale1 * to1[2], + scale0 * w + scale1 * to1[3] + ); +#endif +} + +Quat Quat::slerpni(const Quat& q, const real_t& t) const { + + const Quat &from = *this; + + float dot = from.dot(q); + + if (Math::absf(dot) > 0.9999f) return from; + + float theta = Math::acos(dot), + sinT = 1.0f / Math::sin(theta), + newFactor = Math::sin(t * theta) * sinT, + invFactor = Math::sin((1.0f - t) * theta) * sinT; + + return Quat( invFactor * from.x + newFactor * q.x, + invFactor * from.y + newFactor * q.y, + invFactor * from.z + newFactor * q.z, + invFactor * from.w + newFactor * q.w ); + +#if 0 + real_t to1[4]; + real_t omega, cosom, sinom, scale0, scale1; + + + // calc cosine + cosom = x * q.x + y * q.y + z * q.z + + w * q.w; + + + // adjust signs (if necessary) + if ( cosom <0.0 && false) { + cosom = -cosom; to1[0] = - q.x; + to1[1] = - q.y; + to1[2] = - q.z; + to1[3] = - q.w; + } else { + to1[0] = q.x; + to1[1] = q.y; + to1[2] = q.z; + to1[3] = q.w; + } + + + // calculate coefficients + + if ( (1.0 - cosom) > CMP_EPSILON ) { + // standard case (slerp) + omega = Math::acos(cosom); + sinom = Math::sin(omega); + scale0 = Math::sin((1.0 - t) * omega) / sinom; + scale1 = Math::sin(t * omega) / sinom; + } else { + // "from" and "to" quaternions are very close + // ... so we can do a linear interpolation + scale0 = 1.0 - t; + scale1 = t; + } + // calculate final values + return Quat( + scale0 * x + scale1 * to1[0], + scale0 * y + scale1 * to1[1], + scale0 * z + scale1 * to1[2], + scale0 * w + scale1 * to1[3] + ); +#endif +} + +Quat Quat::cubic_slerp(const Quat& q, const Quat& prep, const Quat& postq,const real_t& t) const { + + //the only way to do slerp :| + float t2 = (1.0-t)*t*2; + Quat sp = this->slerp(q,t); + Quat sq = prep.slerpni(postq,t); + return sp.slerpni(sq,t2); + +} + + +Quat::operator String() const { + + return String::num(x)+","+String::num(y)+","+ String::num(z)+","+ String::num(w); +} + +Quat::Quat(const Vector3& axis, const real_t& angle) { + real_t d = axis.length(); + if (d==0) + set(0,0,0,0); + else { + real_t s = Math::sin(-angle * 0.5) / d; + set(axis.x * s, axis.y * s, axis.z * s, + Math::cos(-angle * 0.5)); + } +} diff --git a/core/math/quat.h b/core/math/quat.h new file mode 100644 index 00000000000..d326073033b --- /dev/null +++ b/core/math/quat.h @@ -0,0 +1,157 @@ +/*************************************************************************/ +/* quat.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 QUAT_H +#define QUAT_H + +#include "math_defs.h" +#include "math_funcs.h" +#include "ustring.h" +#include "vector3.h" + +/** + @author Juan Linietsky +*/ +class Quat{ +public: + + real_t x,y,z,w; + + _FORCE_INLINE_ real_t length_squared() const; + real_t length() const; + void normalize(); + Quat normalized() const; + Quat inverse() const; + _FORCE_INLINE_ real_t dot(const Quat& q) const; + void set_euler(const Vector3& p_euler); + Quat slerp(const Quat& q, const real_t& t) const; + Quat slerpni(const Quat& q, const real_t& t) const; + Quat cubic_slerp(const Quat& q, const Quat& prep, const Quat& postq,const real_t& t) const; + + _FORCE_INLINE_ void get_axis_and_angle(Vector3& r_axis, real_t &r_angle) const { + r_angle = 2 * Math::acos(w); + r_axis.x = -x / Math::sqrt(1-w*w); + r_axis.y = -y / Math::sqrt(1-w*w); + r_axis.z = -z / Math::sqrt(1-w*w); + } + + void operator*=(const Quat& q); + Quat operator*(const Quat& q) const; + + + _FORCE_INLINE_ void operator+=(const Quat& q); + _FORCE_INLINE_ void operator-=(const Quat& q); + _FORCE_INLINE_ void operator*=(const real_t& s); + _FORCE_INLINE_ void operator/=(const real_t& s); + _FORCE_INLINE_ Quat operator+(const Quat& q2) const; + _FORCE_INLINE_ Quat operator-(const Quat& q2) const; + _FORCE_INLINE_ Quat operator-() const; + _FORCE_INLINE_ Quat operator*(const real_t& s) const; + _FORCE_INLINE_ Quat operator/(const real_t& s) const; + + + _FORCE_INLINE_ bool operator==(const Quat& p_quat) const; + _FORCE_INLINE_ bool operator!=(const Quat& p_quat) const; + + operator String() const; + + inline void set( real_t p_x, real_t p_y, real_t p_z, real_t p_w) { + x=p_x; y=p_y; z=p_z; w=p_w; + } + inline Quat(real_t p_x, real_t p_y, real_t p_z, real_t p_w) { + x=p_x; y=p_y; z=p_z; w=p_w; + } + Quat(const Vector3& axis, const real_t& angle); + inline Quat() {x=y=z=0; w=1; } + + +}; + + +real_t Quat::dot(const Quat& q) const { + return x * q.x+y * q.y+z * q.z+w * q.w; +} + +real_t Quat::length_squared() const { + return dot(*this); +} + +void Quat::operator+=(const Quat& q) { + x += q.x; y += q.y; z += q.z; w += q.w; +} + +void Quat::operator-=(const Quat& q) { + x -= q.x; y -= q.y; z -= q.z; w -= q.w; +} + +void Quat::operator*=(const real_t& s) { + x *= s; y *= s; z *= s; w *= s; +} + + +void Quat::operator/=(const real_t& s) { + + *this *= 1.0 / s; +} + +Quat Quat::operator+(const Quat& q2) const { + const Quat& q1 = *this; + return Quat( q1.x+q2.x, q1.y+q2.y, q1.z+q2.z, q1.w+q2.w ); +} + +Quat Quat::operator-(const Quat& q2) const { + const Quat& q1 = *this; + return Quat( q1.x-q2.x, q1.y-q2.y, q1.z-q2.z, q1.w-q2.w); +} + +Quat Quat::operator-() const { + const Quat& q2 = *this; + return Quat( -q2.x, -q2.y, -q2.z, -q2.w); +} + +Quat Quat::operator*(const real_t& s) const { + return Quat(x * s, y * s, z * s, w * s); +} + +Quat Quat::operator/(const real_t& s) const { + return *this * (1.0 / s); +} + + +bool Quat::operator==(const Quat& p_quat) const { + + return x==p_quat.x && y==p_quat.y && z==p_quat.z && w==p_quat.w; +} + +bool Quat::operator!=(const Quat& p_quat) const { + + return x!=p_quat.x || y!=p_quat.y || z!=p_quat.z || w!=p_quat.w; +} + + +#endif diff --git a/core/math/quick_hull.cpp b/core/math/quick_hull.cpp new file mode 100644 index 00000000000..ed30de6915d --- /dev/null +++ b/core/math/quick_hull.cpp @@ -0,0 +1,513 @@ +/*************************************************************************/ +/* quick_hull.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 "quick_hull.h" +#include "map.h" + +uint32_t QuickHull::debug_stop_after=0xFFFFFFFF; + +Error QuickHull::build(const Vector& p_points, Geometry::MeshData &r_mesh) { + + + static const real_t over_tolerance = 0.0001; + + /* CREATE AABB VOLUME */ + + AABB aabb; + for(int i=0;i valid_points; + valid_points.resize(p_points.size()); + Set valid_cache; + + for(int i=0;i max) { + simplex[1]=i; + max=d; + } + + } + } + + //third vertex is one most further away from the line + + + { + float maxd; + Vector3 rel12 = p_points[simplex[0]] - p_points[simplex[1]]; + + for(int i=0;imaxd) { + + maxd=d; + simplex[2]=i; + } + } + } + + //fourth vertex is the one most further away from the plane + + { + float maxd; + Plane p(p_points[simplex[0]],p_points[simplex[1]],p_points[simplex[2]]); + + for(int i=0;imaxd) { + + maxd=d; + simplex[3]=i; + } + } + } + + + //compute center of simplex, this is a point always warranted to be inside + Vector3 center; + + for(int i=0;i<4;i++) { + center+=p_points[simplex[i]]; + } + + center/=4.0; + + //add faces + + List faces; + + for(int i=0;i<4;i++) { + + static const int face_order[4][3]={ + {0,1,2}, + {0,1,3}, + {0,2,3}, + {1,2,3} + }; + + Face f; + for(int j=0;j<3;j++) { + f.vertices[j]=simplex[face_order[i][j]]; + } + + + Plane p(p_points[f.vertices[0]],p_points[f.vertices[1]],p_points[f.vertices[2]]); + + if (p.is_point_over(center)) { + //flip face to clockwise if facing inwards + SWAP( f.vertices[0], f.vertices[1] ); + p=-p; + } + + + f.plane = p; + + faces.push_back(f); + + } + + + /* COMPUTE AVAILABLE VERTICES */ + + for(int i=0;i::Element *E=faces.front();E;E=E->next()) { + + if (E->get().plane.distance_to(p_points[i]) > over_tolerance ) { + + E->get().points_over.push_back(i); + break; + } + } + + + + } + + faces.sort(); // sort them, so the ones with points are in the back + + + /* BUILD HULL */ + + + //poop face (while still remain) + //find further away point + //find lit faces + //determine horizon edges + //build new faces with horizon edges, them assign points side from all lit faces + //remove lit faces + + + uint32_t debug_stop = debug_stop_after; + + while(debug_stop>0 && faces.back()->get().points_over.size()) { + + debug_stop--; + Face& f = faces.back()->get(); + + //find vertex most outside + int next=-1; + real_t next_d=0; + + for(int i=0;i next_d) { + next_d=d; + next=i; + } + } + + ERR_FAIL_COND_V(next==-1,ERR_BUG); + + + + Vector3 v = p_points[f.points_over[next]]; + + //find lit faces and lit edges + List< List::Element* > lit_faces; //lit face is a death sentence + + Map lit_edges; //create this on the flight, should not be that bad for performance and simplifies code a lot + + for(List::Element *E=faces.front();E;E=E->next()) { + + if (E->get().plane.distance_to(v) >0 ) { + + lit_faces.push_back(E); + + for(int i=0;i<3;i++) { + uint32_t a = E->get().vertices[i]; + uint32_t b = E->get().vertices[(i+1)%3]; + Edge e(a,b); + + Map::Element *F=lit_edges.find(e); + if (!F) { + F=lit_edges.insert(e,FaceConnect()); + } + if (e.vertices[0]==a) { + //left + F->get().left=E; + } else { + + F->get().right=E; + } + } + } + } + + + //create new faces from horizon edges + List< List::Element* > new_faces; //new faces + + for(Map::Element *E=lit_edges.front();E;E=E->next()) { + + FaceConnect& fc = E->get(); + if (fc.left && fc.right) { + continue; //edge is uninteresting, not on horizont + } + + //create new face! + + Face face; + face.vertices[0]=f.points_over[next]; + face.vertices[1]=E->key().vertices[0]; + face.vertices[2]=E->key().vertices[1]; + + Plane p(p_points[face.vertices[0]],p_points[face.vertices[1]],p_points[face.vertices[2]]); + + if (p.is_point_over(center)) { + //flip face to clockwise if facing inwards + SWAP( face.vertices[0], face.vertices[1] ); + p = -p; + } + + face.plane = p; + new_faces.push_back( faces.push_back(face) ); + } + + //distribute points into new faces + + for(List< List::Element* >::Element *F=lit_faces.front();F;F=F->next()) { + + Face &lf = F->get()->get(); + + for(int i=0;i::Element* >::Element *E=new_faces.front();E;E=E->next()) { + + Face &f2 = E->get()->get(); + if (f2.plane.distance_to(p)>over_tolerance) { + f2.points_over.push_back(lf.points_over[i]); + break; + } + } + + + } + } + + //erase lit faces + + while(lit_faces.size()) { + + faces.erase(lit_faces.front()->get()); + lit_faces.pop_front(); + } + + //put faces that contain no points on the front + + for (List< List::Element* >::Element *E=new_faces.front();E;E=E->next()) { + + Face &f2 = E->get()->get(); + if (f2.points_over.size()==0) { + faces.move_to_front(E->get()); + } + } + + //whew, done with iteration, go next + + + + } + + /* CREATE MESHDATA */ + + + //make a map of edges again + Map ret_edges; + List ret_faces; + + + for(List::Element *E=faces.front();E;E=E->next()) { + + Geometry::MeshData::Face f; + f.plane = E->get().plane; + + + + for(int i=0;i<3;i++) { + f.indices.push_back(E->get().vertices[i]); + } + + List::Element *F = ret_faces.push_back(f); + + for(int i=0;i<3;i++) { + + uint32_t a = E->get().vertices[i]; + uint32_t b = E->get().vertices[(i+1)%3]; + Edge e(a,b); + + Map::Element *G=ret_edges.find(e); + if (!G) { + G=ret_edges.insert(e,RetFaceConnect()); + } + if (e.vertices[0]==a) { + //left + G->get().left=F; + } else { + + G->get().right=F; + } + } + } + + //fill faces + + for (List::Element *E=ret_faces.front();E;E=E->next()) { + + Geometry::MeshData::Face& f = E->get(); + + for(int i=0;iget().indices[i]; + uint32_t b = E->get().indices[(i+1)%f.indices.size()]; + Edge e(a,b); + + Map::Element *F=ret_edges.find(e); + + ERR_CONTINUE(!F); + + List::Element *O = F->get().left == E ? F->get().right : F->get().left; + ERR_CONTINUE(O==E); + + if (O->get().plane.is_almost_like(f.plane)) { + //merge and delete edge and contiguous face, while repointing edges (uuugh!) + int ois = O->get().indices.size(); + int merged=0; + + + for(int j=0;jget().indices[j]==a) { + //append the rest + for(int k=0;kget().indices[(k+j)%ois]; + int idxn = O->get().indices[(k+j+1)%ois]; + if (idx==b && idxn==a) {//already have b! + break; + } + if (idx!=a) { + f.indices.insert(i+1,idx); + i++; + merged++; + } + Edge e2(idx,idxn); + + Map::Element *F2=ret_edges.find(e2); + + ERR_CONTINUE(!F2); + //change faceconnect, point to this face instead + if (F2->get().left == O) + F2->get().left=E; + else if (F2->get().right == O) + F2->get().right=E; + + } + + break; + } + } + + + ret_edges.erase(F); //remove the edge + ret_faces.erase(O); //remove the face + + + } + + } + + } + + //fill mesh + r_mesh.faces.clear(); + r_mesh.faces.resize(ret_faces.size()); +// print_line("FACECOUNT: "+itos(r_mesh.faces.size())); + + int idx=0; + for (List::Element *E=ret_faces.front();E;E=E->next()) { + r_mesh.faces[idx++]=E->get(); + + + } + r_mesh.edges.resize(ret_edges.size()); + idx=0; + for(Map::Element *E=ret_edges.front();E;E=E->next()) { + + Geometry::MeshData::Edge e; + e.a=E->key().vertices[0]; + e.b=E->key().vertices[1]; + r_mesh.edges[idx++]=e; + } + + r_mesh.vertices=p_points; + + //r_mesh.optimize_vertices(); +/* + print_line("FACES: "+itos(r_mesh.faces.size())); + print_line("EDGES: "+itos(r_mesh.edges.size())); + print_line("VERTICES: "+itos(r_mesh.vertices.size())); +*/ + + return OK; +} diff --git a/core/math/quick_hull.h b/core/math/quick_hull.h new file mode 100644 index 00000000000..d7f056d3668 --- /dev/null +++ b/core/math/quick_hull.h @@ -0,0 +1,95 @@ +/*************************************************************************/ +/* quick_hull.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 QUICK_HULL_H +#define QUICK_HULL_H + +#include "aabb.h" +#include "set.h" +#include "list.h" +#include "geometry.h" + +class QuickHull { + +public: + + + struct Edge { + + union { + uint32_t vertices[2]; + uint64_t id; + }; + + + bool operator<(const Edge& p_edge) const { + return id < p_edge.id; + } + + Edge(int p_vtx_a=0,int p_vtx_b=0) { + + if (p_vtx_a>p_vtx_b) { + SWAP(p_vtx_a,p_vtx_b); + } + + vertices[0]=p_vtx_a; + vertices[1]=p_vtx_b; + } + }; + + struct Face { + + Plane plane; + int vertices[3]; + Vector points_over; + + bool operator<(const Face& p_face) const { + + return points_over.size() < p_face.points_over.size(); + } + + }; +private: + + struct FaceConnect { + List::Element *left,*right; + FaceConnect() { left=NULL; right=NULL; } + }; + struct RetFaceConnect { + List::Element *left,*right; + RetFaceConnect() { left=NULL; right=NULL; } + }; + +public: + + static uint32_t debug_stop_after; + static Error build(const Vector& p_points,Geometry::MeshData& r_mesh); + +}; + +#endif // QUICK_HULL_H diff --git a/core/math/transform.cpp b/core/math/transform.cpp new file mode 100644 index 00000000000..bb874facbda --- /dev/null +++ b/core/math/transform.cpp @@ -0,0 +1,218 @@ +/*************************************************************************/ +/* transform.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 "transform.h" +#include "math_funcs.h" +#include "os/copymem.h" +#include "print_string.h" + + +void Transform::affine_invert() { + + basis.invert(); + origin = basis.xform(-origin); +} + +Transform Transform::affine_inverse() const { + + Transform ret=*this; + ret.affine_invert(); + return ret; + +} + + +void Transform::invert() { + + basis.transpose(); + origin = basis.xform(-origin); +} + +Transform Transform::inverse() const { + + Transform ret=*this; + ret.invert(); + return ret; +} + + +void Transform::rotate(const Vector3& p_axis,real_t p_phi) { + + *this = *this * Transform( Matrix3( p_axis, p_phi ), Vector3() ); +} + +Transform Transform::rotated(const Vector3& p_axis,real_t p_phi) const{ + + return *this * Transform( Matrix3( p_axis, p_phi ), Vector3() ); +} + +void Transform::rotate_basis(const Vector3& p_axis,real_t p_phi) { + + basis.rotate(p_axis,p_phi); +} + +Transform Transform::looking_at( const Vector3& p_target, const Vector3& p_up ) const { + + Transform t = *this; + t.set_look_at(origin,p_target,p_up); + return t; +} + +void Transform::set_look_at( const Vector3& p_eye, const Vector3& p_target, const Vector3& p_up ) { + + // Reference: MESA source code + Vector3 v_x, v_y, v_z; + + /* Make rotation matrix */ + + /* Z vector */ + v_z = p_eye - p_target; + + v_z.normalize(); + + v_y = p_up; + + + v_x=v_y.cross(v_z); + + /* Recompute Y = Z cross X */ + v_y=v_z.cross(v_x); + + v_x.normalize(); + v_y.normalize(); + + basis.set_axis(0,v_x); + basis.set_axis(1,v_y); + basis.set_axis(2,v_z); + origin=p_eye; + +} + +Transform Transform::interpolate_with(const Transform& p_transform, float p_c) const { + + /* not sure if very "efficient" but good enough? */ + + Vector3 src_scale = basis.get_scale(); + Quat src_rot = basis; + Vector3 src_loc = origin; + + Vector3 dst_scale = p_transform.basis.get_scale(); + Quat dst_rot = p_transform.basis; + Vector3 dst_loc = p_transform.origin; + + Transform dst; + dst.basis=src_rot.slerp(dst_rot,p_c); + dst.basis.scale(src_scale.linear_interpolate(dst_scale,p_c)); + dst.origin=src_loc.linear_interpolate(dst_loc,p_c); + + return dst; +} + +void Transform::scale(const Vector3& p_scale) { + + basis.scale(p_scale); + origin*=p_scale; +} + +Transform Transform::scaled(const Vector3& p_scale) const { + + Transform t = *this; + t.scale(p_scale); + return t; +} + +void Transform::scale_basis(const Vector3& p_scale) { + + basis.scale(p_scale); +} + +void Transform::translate( real_t p_tx, real_t p_ty, real_t p_tz) { + translate( Vector3(p_tx,p_ty,p_tz) ); + +} +void Transform::translate( const Vector3& p_translation ) { + + for( int i = 0; i < 3; i++ ) { + origin[i] += basis[i].dot(p_translation); + } +} + +Transform Transform::translated( const Vector3& p_translation ) const { + + Transform t=*this; + t.translate(p_translation); + return t; +} + +void Transform::orthonormalize() { + + basis.orthonormalize(); +} + +Transform Transform::orthonormalized() const { + + Transform _copy = *this; + _copy.orthonormalize(); + return _copy; +} + +bool Transform::operator==(const Transform& p_transform) const { + + return (basis==p_transform.basis && origin==p_transform.origin); +} +bool Transform::operator!=(const Transform& p_transform) const { + + return (basis!=p_transform.basis || origin!=p_transform.origin); +} + +void Transform::operator*=(const Transform& p_transform) { + + origin=xform(p_transform.origin); + basis*=p_transform.basis; +} + +Transform Transform::operator*(const Transform& p_transform) const { + + Transform t=*this; + t*=p_transform; + return t; +} + +Transform::operator String() const { + + return basis.operator String() + " - " + origin.operator String(); +} + + +Transform::Transform(const Matrix3& p_basis, const Vector3& p_origin) { + + basis=p_basis; + origin=p_origin; +} + + diff --git a/core/math/transform.h b/core/math/transform.h new file mode 100644 index 00000000000..b1a0ea1ab8c --- /dev/null +++ b/core/math/transform.h @@ -0,0 +1,268 @@ +/*************************************************************************/ +/* transform.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 TRANSFORM_H +#define TRANSFORM_H + +#include "matrix3.h" +#include "plane.h" +#include "aabb.h" +/** + @author Juan Linietsky +*/ +class Transform { +public: + + Matrix3 basis; + Vector3 origin; + + void invert(); + Transform inverse() const; + + void affine_invert(); + Transform affine_inverse() const; + + Transform rotated(const Vector3& p_axis,real_t p_phi) const; + + void rotate(const Vector3& p_axis,real_t p_phi); + void rotate_basis(const Vector3& p_axis,real_t p_phi); + + void set_look_at( const Vector3& p_eye, const Vector3& p_target, const Vector3& p_up ); + Transform looking_at( const Vector3& p_target, const Vector3& p_up ) const; + + void scale(const Vector3& p_scale); + Transform scaled(const Vector3& p_scale) const; + void scale_basis(const Vector3& p_scale); + void translate( real_t p_tx, real_t p_ty, real_t p_tz ); + void translate( const Vector3& p_translation ); + Transform translated( const Vector3& p_translation ) const; + + const Matrix3& get_basis() const { return basis; } + void set_basis(const Matrix3& p_basis) { basis=p_basis; } + + const Vector3& get_origin() const { return origin; } + void set_origin(const Vector3& p_origin) { origin=p_origin; } + + void orthonormalize(); + Transform orthonormalized() const; + + bool operator==(const Transform& p_transform) const; + bool operator!=(const Transform& p_transform) const; + + _FORCE_INLINE_ Vector3 xform(const Vector3& p_vector) const; + _FORCE_INLINE_ Vector3 xform_inv(const Vector3& p_vector) const; + + _FORCE_INLINE_ Plane xform(const Plane& p_plane) const; + _FORCE_INLINE_ Plane xform_inv(const Plane& p_plane) const; + + _FORCE_INLINE_ AABB xform(const AABB& p_aabb) const; + _FORCE_INLINE_ AABB xform_inv(const AABB& p_aabb) const; + + void operator*=(const Transform& p_transform); + Transform operator*(const Transform& p_transform) const; + + Transform interpolate_with(const Transform& p_transform, float p_c) const; + + _FORCE_INLINE_ Transform inverse_xform(const Transform& t) const { + + Vector3 v = t.origin - origin; + return Transform(basis.transpose_xform(t.basis), + basis.xform(v)); + } + + void set(real_t xx, real_t xy, real_t xz, real_t yx, real_t yy, real_t yz, real_t zx, real_t zy, real_t zz,real_t tx, real_t ty, real_t tz) { + + basis.elements[0][0]=xx; + basis.elements[0][1]=xy; + basis.elements[0][2]=xz; + basis.elements[1][0]=yx; + basis.elements[1][1]=yy; + basis.elements[1][2]=yz; + basis.elements[2][0]=zx; + basis.elements[2][1]=zy; + basis.elements[2][2]=zz; + origin.x=tx; + origin.y=ty; + origin.z=tz; + } + + operator String() const; + + Transform(const Matrix3& p_basis, const Vector3& p_origin=Vector3()); + Transform() {} + +}; + + +_FORCE_INLINE_ Vector3 Transform::xform(const Vector3& p_vector) const { + + return Vector3( + basis[0].dot(p_vector)+origin.x, + basis[1].dot(p_vector)+origin.y, + basis[2].dot(p_vector)+origin.z + ); +} +_FORCE_INLINE_ Vector3 Transform::xform_inv(const Vector3& p_vector) const { + + Vector3 v = p_vector - origin; + + return Vector3( + (basis.elements[0][0]*v.x ) + ( basis.elements[1][0]*v.y ) + ( basis.elements[2][0]*v.z ), + (basis.elements[0][1]*v.x ) + ( basis.elements[1][1]*v.y ) + ( basis.elements[2][1]*v.z ), + (basis.elements[0][2]*v.x ) + ( basis.elements[1][2]*v.y ) + ( basis.elements[2][2]*v.z ) + ); +} + +_FORCE_INLINE_ Plane Transform::xform(const Plane& p_plane) const { + + + Vector3 point=p_plane.normal*p_plane.d; + Vector3 point_dir=point+p_plane.normal; + point=xform(point); + point_dir=xform(point_dir); + + Vector3 normal=point_dir-point; + normal.normalize(); + real_t d=normal.dot(point); + + return Plane(normal,d); + +} +_FORCE_INLINE_ Plane Transform::xform_inv(const Plane& p_plane) const { + + Vector3 point=p_plane.normal*p_plane.d; + Vector3 point_dir=point+p_plane.normal; + xform_inv(point); + xform_inv(point_dir); + + Vector3 normal=point_dir-point; + normal.normalize(); + real_t d=normal.dot(point); + + return Plane(normal,d); + +} + +_FORCE_INLINE_ AABB Transform::xform(const AABB& p_aabb) const { + /* define vertices */ +#if 1 + Vector3 x=basis.get_axis(0)*p_aabb.size.x; + Vector3 y=basis.get_axis(1)*p_aabb.size.y; + Vector3 z=basis.get_axis(2)*p_aabb.size.z; + Vector3 pos = xform( p_aabb.pos ); +//could be even further optimized + AABB new_aabb; + new_aabb.pos=pos; + new_aabb.expand_to( pos+x ); + new_aabb.expand_to( pos+y ); + new_aabb.expand_to( pos+z ); + new_aabb.expand_to( pos+x+y ); + new_aabb.expand_to( pos+x+z ); + new_aabb.expand_to( pos+y+z ); + new_aabb.expand_to( pos+x+y+z ); + return new_aabb; +#else + + + Vector3 vertices[8]={ + Vector3(p_aabb.pos.x+p_aabb.size.x, p_aabb.pos.y+p_aabb.size.y, p_aabb.pos.z+p_aabb.size.z), + Vector3(p_aabb.pos.x+p_aabb.size.x, p_aabb.pos.y+p_aabb.size.y, p_aabb.pos.z), + Vector3(p_aabb.pos.x+p_aabb.size.x, p_aabb.pos.y, p_aabb.pos.z+p_aabb.size.z), + Vector3(p_aabb.pos.x+p_aabb.size.x, p_aabb.pos.y, p_aabb.pos.z), + Vector3(p_aabb.pos.x, p_aabb.pos.y+p_aabb.size.y, p_aabb.pos.z+p_aabb.size.z), + Vector3(p_aabb.pos.x, p_aabb.pos.y+p_aabb.size.y, p_aabb.pos.z), + Vector3(p_aabb.pos.x, p_aabb.pos.y, p_aabb.pos.z+p_aabb.size.z), + Vector3(p_aabb.pos.x, p_aabb.pos.y, p_aabb.pos.z) + }; + + + AABB ret; + + ret.pos=xform(vertices[0]); + + for (int i=1;i<8;i++) { + + ret.expand_to( xform(vertices[i]) ); + } + + return ret; +#endif + +} +_FORCE_INLINE_ AABB Transform::xform_inv(const AABB& p_aabb) const { + + /* define vertices */ + Vector3 vertices[8]={ + Vector3(p_aabb.pos.x+p_aabb.size.x, p_aabb.pos.y+p_aabb.size.y, p_aabb.pos.z+p_aabb.size.z), + Vector3(p_aabb.pos.x+p_aabb.size.x, p_aabb.pos.y+p_aabb.size.y, p_aabb.pos.z), + Vector3(p_aabb.pos.x+p_aabb.size.x, p_aabb.pos.y, p_aabb.pos.z+p_aabb.size.z), + Vector3(p_aabb.pos.x+p_aabb.size.x, p_aabb.pos.y, p_aabb.pos.z), + Vector3(p_aabb.pos.x, p_aabb.pos.y+p_aabb.size.y, p_aabb.pos.z+p_aabb.size.z), + Vector3(p_aabb.pos.x, p_aabb.pos.y+p_aabb.size.y, p_aabb.pos.z), + Vector3(p_aabb.pos.x, p_aabb.pos.y, p_aabb.pos.z+p_aabb.size.z), + Vector3(p_aabb.pos.x, p_aabb.pos.y, p_aabb.pos.z) + }; + + + AABB ret; + + ret.pos=xform_inv(vertices[0]); + + for (int i=1;i<8;i++) { + + ret.expand_to( xform_inv(vertices[i]) ); + } + + return ret; + +} + +#ifdef OPTIMIZED_TRANSFORM_IMPL_OVERRIDE + +#else + +struct OptimizedTransform { + + Transform transform; + + _FORCE_INLINE_ void invert() {transform.invert(); } + _FORCE_INLINE_ void affine_invert() {transform.affine_invert(); } + _FORCE_INLINE_ Vector3 xform(const Vector3& p_vec) const { return transform.xform(p_vec); }; + _FORCE_INLINE_ Vector3 xform_inv(const Vector3& p_vec) const { return transform.xform_inv(p_vec); }; + _FORCE_INLINE_ OptimizedTransform operator*(const OptimizedTransform& p_ot ) const { return OptimizedTransform( transform * p_ot.transform ) ; } + _FORCE_INLINE_ Transform get_transform() const { return transform; } + _FORCE_INLINE_ void set_transform(const Transform& p_transform) { transform=p_transform; } + + OptimizedTransform(const Transform& p_transform) { + transform=p_transform; + } +}; + +#endif + +#endif diff --git a/core/math/triangle_mesh.cpp b/core/math/triangle_mesh.cpp new file mode 100644 index 00000000000..111ceca185a --- /dev/null +++ b/core/math/triangle_mesh.cpp @@ -0,0 +1,568 @@ +/*************************************************************************/ +/* triangle_mesh.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 "triangle_mesh.h" +#include "sort.h" + + + +int TriangleMesh::_create_bvh(BVH*p_bvh,BVH** p_bb,int p_from,int p_size,int p_depth,int&max_depth,int&max_alloc) { + + + if (p_depth>max_depth) { + max_depth=p_depth; + } + + if (p_size==1) { + + + return p_bb[p_from]-p_bvh; + } else if (p_size==0) { + + return -1; + } + + + AABB aabb; + aabb=p_bb[p_from]->aabb; + for(int i=1;iaabb); + } + + int li=aabb.get_longest_axis_index(); + + switch(li) { + + case Vector3::AXIS_X: { + SortArray sort_x; + sort_x.nth_element(0,p_size,p_size/2,&p_bb[p_from]); + //sort_x.sort(&p_bb[p_from],p_size); + } break; + case Vector3::AXIS_Y: { + SortArray sort_y; + sort_y.nth_element(0,p_size,p_size/2,&p_bb[p_from]); + //sort_y.sort(&p_bb[p_from],p_size); + } break; + case Vector3::AXIS_Z: { + SortArray sort_z; + sort_z.nth_element(0,p_size,p_size/2,&p_bb[p_from]); + //sort_z.sort(&p_bb[p_from],p_size); + + } break; + } + + + int left = _create_bvh(p_bvh,p_bb,p_from,p_size/2,p_depth+1,max_depth,max_alloc); + int right = _create_bvh(p_bvh,p_bb,p_from+p_size/2,p_size-p_size/2,p_depth+1,max_depth,max_alloc); + + int index=max_alloc++; + BVH *_new = &p_bvh[index]; + _new->aabb=aabb; + _new->center=aabb.pos+aabb.size*0.5; + _new->face_index=-1; + _new->left=left; + _new->right=right; + + return index; + +} + + +void TriangleMesh::create(const DVector& p_faces) { + + valid=false; + + int fc=p_faces.size(); + ERR_FAIL_COND(!fc || ((fc%3) != 0)); + fc/=3; + triangles.resize(fc); + + bvh.resize(fc*3); //will never be larger than this (todo make better) + DVector::Write bw = bvh.write(); + + { + + //create faces and indices and base bvh + //except for the Set for repeated triangles, everything + //goes in-place. + + DVector::Read r = p_faces.read(); + DVector::Write w = triangles.write(); + Map db; + + for(int i=0;i::Element *E=db.find(vs); + if (E) { + vidx=E->get(); + } else { + vidx=db.size(); + db[vs]=vidx; + } + + f.indices[j]=vidx; + if (j==0) + bw[i].aabb.pos=vs; + else + bw[i].aabb.expand_to(vs); + } + + f.normal=Face3(r[i*3+0],r[i*3+1],r[i*3+2]).get_plane().get_normal(); + + bw[i].left=-1; + bw[i].right=-1; + bw[i].face_index=i; + bw[i].center=bw[i].aabb.pos+bw[i].aabb.size*0.5; + } + + vertices.resize(db.size()); + DVector::Write vw = vertices.write(); + for (Map::Element *E=db.front();E;E=E->next()) { + vw[E->get()]=E->key(); + } + + } + + DVector bwptrs; + bwptrs.resize(fc); + DVector::Write bwp = bwptrs.write(); + for(int i=0;i::Write(); //clearup + bvh.resize(max_alloc); //resize back + + valid=true; + +} + + +Vector3 TriangleMesh::get_area_normal(const AABB& p_aabb) const { + + uint32_t* stack = (uint32_t*)alloca(sizeof(int)*max_depth); + + enum { + TEST_AABB_BIT=0, + VISIT_LEFT_BIT=1, + VISIT_RIGHT_BIT=2, + VISIT_DONE_BIT=3, + VISITED_BIT_SHIFT=29, + NODE_IDX_MASK=(1<::Read trianglesr = triangles.read(); + DVector::Read verticesr=vertices.read(); + DVector::Read bvhr=bvh.read(); + + const Triangle *triangleptr=trianglesr.ptr(); + const Vector3 *vertexptr=verticesr.ptr(); + int pos=bvh.size()-1; + const BVH *bvhptr = bvhr.ptr(); + + stack[0]=pos; + while(true) { + + uint32_t node = stack[level]&NODE_IDX_MASK; + const BVH &b = bvhptr[ node ]; + bool done=false; + + switch(stack[level]>>VISITED_BIT_SHIFT) { + case TEST_AABB_BIT: { + + + bool valid = b.aabb.intersects(p_aabb); + if (!valid) { + + stack[level]=(VISIT_DONE_BIT<=0) { + + const Triangle &s=triangleptr[ b.face_index ]; + n+=s.normal; + n_count++; + + stack[level]=(VISIT_DONE_BIT<0) + n/=n_count; + + return n; + +} + + +bool TriangleMesh::intersect_segment(const Vector3& p_begin,const Vector3& p_end,Vector3 &r_point, Vector3 &r_normal) const { + + + uint32_t* stack = (uint32_t*)alloca(sizeof(int)*max_depth); + + enum { + TEST_AABB_BIT=0, + VISIT_LEFT_BIT=1, + VISIT_RIGHT_BIT=2, + VISIT_DONE_BIT=3, + VISITED_BIT_SHIFT=29, + NODE_IDX_MASK=(1<::Read trianglesr = triangles.read(); + DVector::Read verticesr=vertices.read(); + DVector::Read bvhr=bvh.read(); + + const Triangle *triangleptr=trianglesr.ptr(); + const Vector3 *vertexptr=verticesr.ptr(); + int pos=bvh.size()-1; + const BVH *bvhptr = bvhr.ptr(); + + stack[0]=pos; + while(true) { + + uint32_t node = stack[level]&NODE_IDX_MASK; + const BVH &b = bvhptr[ node ]; + bool done=false; + + switch(stack[level]>>VISITED_BIT_SHIFT) { + case TEST_AABB_BIT: { + + + bool valid = b.aabb.intersects_segment(p_begin,p_end); +// bool valid = b.aabb.intersects(ray_aabb); + + if (!valid) { + + stack[level]=(VISIT_DONE_BIT<=0) { + + const Triangle &s=triangleptr[ b.face_index ]; + Face3 f3(vertexptr[ s.indices[0] ],vertexptr[ s.indices[1] ],vertexptr[ s.indices[2] ]); + + + Vector3 res; + + if (f3.intersects_segment(p_begin,p_end,&res)) { + + + float nd = n.dot(res); + if (nd0) + r_normal=-r_normal; + } + + return inters; +} + + +bool TriangleMesh::intersect_ray(const Vector3& p_begin,const Vector3& p_dir,Vector3 &r_point, Vector3 &r_normal) const { + + + uint32_t* stack = (uint32_t*)alloca(sizeof(int)*max_depth); + + enum { + TEST_AABB_BIT=0, + VISIT_LEFT_BIT=1, + VISIT_RIGHT_BIT=2, + VISIT_DONE_BIT=3, + VISITED_BIT_SHIFT=29, + NODE_IDX_MASK=(1<::Read trianglesr = triangles.read(); + DVector::Read verticesr=vertices.read(); + DVector::Read bvhr=bvh.read(); + + const Triangle *triangleptr=trianglesr.ptr(); + const Vector3 *vertexptr=verticesr.ptr(); + int pos=bvh.size()-1; + const BVH *bvhptr = bvhr.ptr(); + + stack[0]=pos; + while(true) { + + uint32_t node = stack[level]&NODE_IDX_MASK; + const BVH &b = bvhptr[ node ]; + bool done=false; + + switch(stack[level]>>VISITED_BIT_SHIFT) { + case TEST_AABB_BIT: { + + + bool valid = b.aabb.intersects_ray(p_begin,p_dir); + if (!valid) { + + stack[level]=(VISIT_DONE_BIT<=0) { + + const Triangle &s=triangleptr[ b.face_index ]; + Face3 f3(vertexptr[ s.indices[0] ],vertexptr[ s.indices[1] ],vertexptr[ s.indices[2] ]); + + + Vector3 res; + + if (f3.intersects_ray(p_begin,p_dir,&res)) { + + + float nd = n.dot(res); + if (nd0) + r_normal=-r_normal; + } + + return inters; +} + +bool TriangleMesh::is_valid() const { + + return valid; +} + +DVector TriangleMesh::get_faces() const { + + if (!valid) + return DVector(); + + DVector faces; + int ts = triangles.size(); + faces.resize(triangles.size()); + + DVector::Write w=faces.write(); + DVector::Read r = triangles.read(); + DVector::Read rv = vertices.read(); + + for(int i=0;i::Write(); + return faces; +} + +TriangleMesh::TriangleMesh() { + + valid=false; + max_depth=0; +} diff --git a/core/math/triangle_mesh.h b/core/math/triangle_mesh.h new file mode 100644 index 00000000000..ab0cb713b15 --- /dev/null +++ b/core/math/triangle_mesh.h @@ -0,0 +1,99 @@ +/*************************************************************************/ +/* triangle_mesh.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 TRIANGLE_MESH_H +#define TRIANGLE_MESH_H + +#include "reference.h" +#include "face3.h" +class TriangleMesh : public Reference { + + OBJ_TYPE( TriangleMesh, Reference); + + struct Triangle { + + Vector3 normal; + int indices[3]; + }; + + DVector triangles; + DVector vertices; + + struct BVH { + + AABB aabb; + Vector3 center; //used for sorting + int left; + int right; + + int face_index; + }; + + struct BVHCmpX { + + bool operator()(const BVH* p_left, const BVH* p_right) const { + + return p_left->center.x < p_right->center.x; + } + }; + + struct BVHCmpY { + + bool operator()(const BVH* p_left, const BVH* p_right) const { + + return p_left->center.y < p_right->center.y; + } + }; + struct BVHCmpZ { + + bool operator()(const BVH* p_left, const BVH* p_right) const { + + return p_left->center.z < p_right->center.z; + } + }; + + int _create_bvh(BVH*p_bvh,BVH** p_bb,int p_from,int p_size,int p_depth,int&max_depth,int&max_alloc); + + DVector bvh; + int max_depth; + bool valid; + +public: + + bool is_valid() const; + bool intersect_segment(const Vector3& p_begin,const Vector3& p_end,Vector3 &r_point, Vector3 &r_normal) const; + bool intersect_ray(const Vector3& p_begin,const Vector3& p_dir,Vector3 &r_point, Vector3 &r_normal) const; + Vector3 get_area_normal(const AABB& p_aabb) const; + DVector get_faces() const; + + + void create(const DVector& p_faces); + TriangleMesh(); +}; + +#endif // TRIANGLE_MESH_H diff --git a/core/math/triangulate.cpp b/core/math/triangulate.cpp new file mode 100644 index 00000000000..4a870def4bf --- /dev/null +++ b/core/math/triangulate.cpp @@ -0,0 +1,168 @@ +/*************************************************************************/ +/* triangulate.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 "triangulate.h" + +float Triangulate::get_area(const Vector &contour) +{ + + int n = contour.size(); + const Vector2 *c=&contour[0]; + + float A=0.0f; + + for(int p=n-1,q=0; q= 0.0f) && (bCROSScp >= 0.0f) && (cCROSSap >= 0.0f)); +}; + +bool Triangulate::snip(const Vector &p_contour,int u,int v,int w,int n,int *V) +{ + int p; + float Ax, Ay, Bx, By, Cx, Cy, Px, Py; + const Vector2 *contour=&p_contour[0]; + + Ax = contour[V[u]].x; + Ay = contour[V[u]].y; + + Bx = contour[V[v]].x; + By = contour[V[v]].y; + + Cx = contour[V[w]].x; + Cy = contour[V[w]].y; + + if ( CMP_EPSILON > (((Bx-Ax)*(Cy-Ay)) - ((By-Ay)*(Cx-Ax))) ) return false; + + for (p=0;p &contour,Vector &result) +{ + /* allocate and initialize list of Vertices in polygon */ + + int n = contour.size(); + if ( n < 3 ) return false; + + + + int *V = (int*)alloca(sizeof(int)*n); + + /* we want a counter-clockwise polygon in V */ + + if ( 0.0f < get_area(contour) ) + for (int v=0; v2; ) + { + /* if we loop, it is probably a non-simple polygon */ + if (0 >= (count--)) + { + //** Triangulate: ERROR - probable bad polygon! + return false; + } + + /* three consecutive vertices in current polygon, */ + int u = v ; if (nv <= u) u = 0; /* previous */ + v = u+1; if (nv <= v) v = 0; /* new v */ + int w = v+1; if (nv <= w) w = 0; /* next */ + + if ( snip(contour,u,v,w,nv,V) ) + { + int a,b,c,s,t; + + /* true names of the vertices */ + a = V[u]; b = V[v]; c = V[w]; + + /* output Triangle */ + /* + result.push_back( contour[a] ); + result.push_back( contour[b] ); + result.push_back( contour[c] ); +*/ + + result.push_back( a ); + result.push_back( b ); + result.push_back( c ); + + m++; + + /* remove v from remaining polygon */ + for(s=v,t=v+1;t &contour, Vector &result); + + // compute area of a contour/polygon + static float get_area(const Vector< Vector2 > &contour); + + // decide if point Px/Py is inside triangle defined by + // (Ax,Ay) (Bx,By) (Cx,Cy) + static bool is_inside_triangle(float Ax, float Ay, + float Bx, float By, + float Cx, float Cy, + float Px, float Py); + + +private: + static bool snip(const Vector &p_contour,int u,int v,int w,int n,int *V); + +}; + + + +#endif diff --git a/core/math/vector3.cpp b/core/math/vector3.cpp new file mode 100644 index 00000000000..cf6fd9242e6 --- /dev/null +++ b/core/math/vector3.cpp @@ -0,0 +1,201 @@ +/*************************************************************************/ +/* vector3.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 "vector3.h" + + + +void Vector3::rotate(const Vector3& p_axis,float p_phi) { + + Vector3 axis1 = cross(p_axis); + float l = axis1.length(); + if (l==0) + return; + axis1/=l; + Vector3 axis2 = axis1.cross(p_axis).normalized(); + + float _x = axis1.dot(*this); + float _y = axis2.dot(*this); + + float ang = Math::atan2(_x,_y); + + ang+=p_phi; + + *this=((axis1 * Math::cos(ang)) + (axis2 * Math::sin(ang))) * length(); + +} + +Vector3 Vector3::rotated(const Vector3& p_axis,float p_phi) const { + + Vector3 r = *this; + r.rotate(p_axis,p_phi); + return r; +} + +void Vector3::set_axis(int p_axis,real_t p_value) { + ERR_FAIL_INDEX(p_axis,3); + coord[p_axis]=p_value; + +} +real_t Vector3::get_axis(int p_axis) const { + + ERR_FAIL_INDEX_V(p_axis,3,0); + return operator[](p_axis); +} + +int Vector3::min_axis() const { + + return x < y ? (x < z ? 0 : 2) : (y < z ? 1 : 2); +} +int Vector3::max_axis() const { + + return x < y ? (y < z ? 2 : 1) : (x < z ? 2 : 0); +} + + +void Vector3::snap(float p_val) { + + x+=p_val/2.0; + x-=Math::fmod(x,p_val); + y+=p_val/2.0; + y-=Math::fmod(y,p_val); + z+=p_val/2.0; + z-=Math::fmod(z,p_val); + +} +Vector3 Vector3::snapped(float p_val) const { + + Vector3 v=*this; + v.snap(p_val); + return v; +} + + +Vector3 Vector3::cubic_interpolaten(const Vector3& p_b,const Vector3& p_pre_a, const Vector3& p_post_b,float p_t) const { + + Vector3 p0=p_pre_a; + Vector3 p1=*this; + Vector3 p2=p_b; + Vector3 p3=p_post_b; + + { + //normalize + + float ab = p0.distance_to(p1); + float bc = p1.distance_to(p2); + float cd = p2.distance_to(p3); + + if (ab>0) + p0 = p1+(p0-p1)*(bc/ab); + if (cd>0) + p3 = p2+(p3-p2)*(bc/cd); + } + + + float t = p_t; + float t2 = t * t; + float t3 = t2 * t; + + Vector3 out; + out = 0.5f * ( ( p1 * 2.0f) + + ( -p0 + p2 ) * t + + ( 2.0f * p0 - 5.0f * p1 + 4 * p2 - p3 ) * t2 + + ( -p0 + 3.0f * p1 - 3.0f * p2 + p3 ) * t3 ); + return out; + +} + +Vector3 Vector3::cubic_interpolate(const Vector3& p_b,const Vector3& p_pre_a, const Vector3& p_post_b,float p_t) const { + + Vector3 p0=p_pre_a; + Vector3 p1=*this; + Vector3 p2=p_b; + Vector3 p3=p_post_b; + + float t = p_t; + float t2 = t * t; + float t3 = t2 * t; + + Vector3 out; + out = 0.5f * ( ( p1 * 2.0f) + + ( -p0 + p2 ) * t + + ( 2.0f * p0 - 5.0f * p1 + 4 * p2 - p3 ) * t2 + + ( -p0 + 3.0f * p1 - 3.0f * p2 + p3 ) * t3 ); + return out; + +} + +#if 0 +Vector3 Vector3::cubic_interpolate(const Vector3& p_b,const Vector3& p_pre_a, const Vector3& p_post_b,float p_t) const { + + Vector3 p0=p_pre_a; + Vector3 p1=*this; + Vector3 p2=p_b; + Vector3 p3=p_post_b; + + if (true) { + + float ab = p0.distance_to(p1); + float bc = p1.distance_to(p2); + float cd = p2.distance_to(p3); + + //if (ab>bc) { + if (ab>0) + p0 = p1+(p0-p1)*(bc/ab); + //} + + //if (cd>bc) { + if (cd>0) + p3 = p2+(p3-p2)*(bc/cd); + //} + } + + float t = p_t; + float t2 = t * t; + float t3 = t2 * t; + + Vector3 out; + out.x = 0.5f * ( ( 2.0f * p1.x ) + + ( -p0.x + p2.x ) * t + + ( 2.0f * p0.x - 5.0f * p1.x + 4 * p2.x - p3.x ) * t2 + + ( -p0.x + 3.0f * p1.x - 3.0f * p2.x + p3.x ) * t3 ); + out.y = 0.5f * ( ( 2.0f * p1.y ) + + ( -p0.y + p2.y ) * t + + ( 2.0f * p0.y - 5.0f * p1.y + 4 * p2.y - p3.y ) * t2 + + ( -p0.y + 3.0f * p1.y - 3.0f * p2.y + p3.y ) * t3 ); + out.z = 0.5f * ( ( 2.0f * p1.z ) + + ( -p0.z + p2.z ) * t + + ( 2.0f * p0.z - 5.0f * p1.z + 4 * p2.z - p3.z ) * t2 + + ( -p0.z + 3.0f * p1.z - 3.0f * p2.z + p3.z ) * t3 ); + return out; +} +# endif +Vector3::operator String() const { + + return (rtos(x)+", "+rtos(y)+", "+rtos(z)); +} diff --git a/core/math/vector3.h b/core/math/vector3.h new file mode 100644 index 00000000000..959f7cd0a8e --- /dev/null +++ b/core/math/vector3.h @@ -0,0 +1,373 @@ +/*************************************************************************/ +/* vector3.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 VECTOR3_H +#define VECTOR3_H + +#include "typedefs.h" +#include "math_defs.h" +#include "math_funcs.h" +#include "ustring.h" + + +struct Vector3 { + + enum Axis { + AXIS_X, + AXIS_Y, + AXIS_Z, + }; + + union { + +#ifdef USE_QUAD_VECTORS + + struct { + real_t x; + real_t y; + real_t z; + real_t _unused; + }; + real_t coord[4]; +#else + + struct { + real_t x; + real_t y; + real_t z; + }; + + real_t coord[3]; +#endif + }; + + _FORCE_INLINE_ const real_t& operator[](int p_axis) const { + + return coord[p_axis]; + } + + _FORCE_INLINE_ real_t& operator[](int p_axis) { + + return coord[p_axis]; + } + + void set_axis(int p_axis,real_t p_value); + real_t get_axis(int p_axis) const; + + int min_axis() const; + int max_axis() const; + + _FORCE_INLINE_ real_t length() const; + _FORCE_INLINE_ real_t length_squared() const; + + _FORCE_INLINE_ void normalize(); + _FORCE_INLINE_ Vector3 normalized() const; + _FORCE_INLINE_ Vector3 inverse() const; + + _FORCE_INLINE_ void zero(); + + void snap(float p_val); + Vector3 snapped(float p_val) const; + + void rotate(const Vector3& p_axis,float p_phi); + Vector3 rotated(const Vector3& p_axis,float p_phi) const; + + /* Static Methods between 2 vector3s */ + + _FORCE_INLINE_ Vector3 linear_interpolate(const Vector3& p_b,float p_t) const; + Vector3 cubic_interpolate(const Vector3& p_b,const Vector3& p_pre_a, const Vector3& p_post_b,float p_t) const; + Vector3 cubic_interpolaten(const Vector3& p_b,const Vector3& p_pre_a, const Vector3& p_post_b,float p_t) const; + + _FORCE_INLINE_ Vector3 cross(const Vector3& p_b) const; + _FORCE_INLINE_ real_t dot(const Vector3& p_b) const; + + _FORCE_INLINE_ Vector3 abs() const; + + _FORCE_INLINE_ real_t distance_to(const Vector3& p_b) const; + _FORCE_INLINE_ real_t distance_squared_to(const Vector3& p_b) const; + + /* Operators */ + + _FORCE_INLINE_ Vector3& operator+=(const Vector3& p_v); + _FORCE_INLINE_ Vector3 operator+(const Vector3& p_v) const; + _FORCE_INLINE_ Vector3& operator-=(const Vector3& p_v); + _FORCE_INLINE_ Vector3 operator-(const Vector3& p_v) const; + _FORCE_INLINE_ Vector3& operator*=(const Vector3& p_v); + _FORCE_INLINE_ Vector3 operator*(const Vector3& p_v) const; + _FORCE_INLINE_ Vector3& operator/=(const Vector3& p_v); + _FORCE_INLINE_ Vector3 operator/(const Vector3& p_v) const; + + + _FORCE_INLINE_ Vector3& operator*=(real_t p_scalar); + _FORCE_INLINE_ Vector3 operator*(real_t p_scalar) const; + _FORCE_INLINE_ Vector3& operator/=(real_t p_scalar); + _FORCE_INLINE_ Vector3 operator/(real_t p_scalar) const; + + _FORCE_INLINE_ Vector3 operator-() const; + + _FORCE_INLINE_ bool operator==(const Vector3& p_v) const; + _FORCE_INLINE_ bool operator!=(const Vector3& p_v) const; + _FORCE_INLINE_ bool operator<(const Vector3& p_v) const; + _FORCE_INLINE_ bool operator<=(const Vector3& p_v) const; + + operator String() const; + + _FORCE_INLINE_ Vector3() { x=y=z=0; } + _FORCE_INLINE_ Vector3(real_t p_x,real_t p_y,real_t p_z) { x=p_x; y=p_y; z=p_z; } + +}; + +#ifdef VECTOR3_IMPL_OVERRIDE + +#include "vector3_inline.h" + +#else + +Vector3 Vector3::cross(const Vector3& p_b) const { + + Vector3 ret ( + (y * p_b.z) - (z * p_b.y), + (z * p_b.x) - (x * p_b.z), + (x * p_b.y) - (y * p_b.x) + ); + + return ret; +} +real_t Vector3::dot(const Vector3& p_b) const { + + return x*p_b.x + y*p_b.y + z*p_b.z; +} + +Vector3 Vector3::abs() const { + + return Vector3( Math::abs(x), Math::abs(y), Math::abs(z) ); +} + +Vector3 Vector3::linear_interpolate(const Vector3& p_b,float p_t) const { + + return Vector3( + x+(p_t * (p_b.x-x)), + y+(p_t * (p_b.y-y)), + z+(p_t * (p_b.z-z)) + ); + +} + + + +real_t Vector3::distance_to(const Vector3& p_b) const { + return (p_b-*this).length(); +} +real_t Vector3::distance_squared_to(const Vector3& p_b) const { + + return (p_b-*this).length_squared(); +} + +/* Operators */ + +Vector3& Vector3::operator+=(const Vector3& p_v) { + + x+=p_v.x; + y+=p_v.y; + z+=p_v.z; + return *this; +} +Vector3 Vector3::operator+(const Vector3& p_v) const { + + return Vector3(x+p_v.x, y+p_v.y, z+ p_v.z); +} + +Vector3& Vector3::operator-=(const Vector3& p_v) { + + x-=p_v.x; + y-=p_v.y; + z-=p_v.z; + return *this; +} +Vector3 Vector3::operator-(const Vector3& p_v) const { + + return Vector3(x-p_v.x, y-p_v.y, z- p_v.z); +} + + + +Vector3& Vector3::operator*=(const Vector3& p_v) { + + x*=p_v.x; + y*=p_v.y; + z*=p_v.z; + return *this; +} +Vector3 Vector3::operator*(const Vector3& p_v) const { + + return Vector3(x*p_v.x, y*p_v.y, z* p_v.z); +} + +Vector3& Vector3::operator/=(const Vector3& p_v) { + + x/=p_v.x; + y/=p_v.y; + z/=p_v.z; + return *this; +} +Vector3 Vector3::operator/(const Vector3& p_v) const { + + return Vector3(x/p_v.x, y/p_v.y, z/ p_v.z); +} + +Vector3& Vector3::operator*=(real_t p_scalar) { + x*=p_scalar; + y*=p_scalar; + z*=p_scalar; + return *this; + +} + +_FORCE_INLINE_ Vector3 operator*(real_t p_scalar, const Vector3& p_vec) { + return p_vec * p_scalar; +} + +Vector3 Vector3::operator*(real_t p_scalar) const { + + return Vector3( x*p_scalar, y*p_scalar, z*p_scalar); +} + +Vector3& Vector3::operator/=(real_t p_scalar) { + x/=p_scalar; + y/=p_scalar; + z/=p_scalar; + return *this; + +} + +Vector3 Vector3::operator/(real_t p_scalar) const { + + return Vector3( x/p_scalar, y/p_scalar, z/p_scalar); +} + +Vector3 Vector3::operator-() const { + + return Vector3( -x, -y, -z ); +} + + +bool Vector3::operator==(const Vector3& p_v) const { + + return (x==p_v.x && y==p_v.y && z==p_v.z); +} + +bool Vector3::operator!=(const Vector3& p_v) const { + + return (x!=p_v.x || y!=p_v.y || z!=p_v.z); +} + +bool Vector3::operator<(const Vector3& p_v) const { + + if (x==p_v.x) { + if (y==p_v.y) + return z= buffer_size) { + String type; + if (ObjectDB::get_instance(p_id)) + type=ObjectDB::get_instance(p_id)->get_type(); + print_line("failed method: "+type+":"+p_method+" target ID: "+itos(p_id)); + statistics(); + + } + ERR_FAIL_COND_V( (buffer_end+room_needed) >= buffer_size , ERR_OUT_OF_MEMORY ); + Message * msg = memnew_placement( &buffer[ buffer_end ], Message ); + msg->args=args; + msg->instance_ID=p_id; + msg->target=p_method; + msg->type=TYPE_CALL; + buffer_end+=sizeof(Message); + + + if (args>=1) { + + Variant * v = memnew_placement( &buffer[ buffer_end ], Variant ); + buffer_end+=sizeof(Variant); + *v=p_arg1; + } + + if (args>=2) { + + Variant * v = memnew_placement( &buffer[ buffer_end ], Variant ); + buffer_end+=sizeof(Variant); + *v=p_arg2; + } + + if (args>=3) { + + Variant * v = memnew_placement( &buffer[ buffer_end ], Variant ); + buffer_end+=sizeof(Variant); + *v=p_arg3; + + } + + if (args>=4) { + + Variant * v = memnew_placement( &buffer[ buffer_end ], Variant ); + buffer_end+=sizeof(Variant); + *v=p_arg4; + } + + if (args>=5) { + + Variant * v = memnew_placement( &buffer[ buffer_end ], Variant ); + buffer_end+=sizeof(Variant); + *v=p_arg5; + } + + + return OK; +} + +Error MessageQueue::push_set(ObjectID p_id, const StringName& p_prop, const Variant& p_value) { + + _THREAD_SAFE_METHOD_ + + uint8_t room_needed=sizeof(Message)+sizeof(Variant); + + if ((buffer_end+room_needed) >= buffer_size) { + String type; + if (ObjectDB::get_instance(p_id)) + type=ObjectDB::get_instance(p_id)->get_type(); + print_line("failed set: "+type+":"+p_prop+" target ID: "+itos(p_id)); + statistics(); + + } + + ERR_FAIL_COND_V( (buffer_end+room_needed) >= buffer_size , ERR_OUT_OF_MEMORY ); + + Message * msg = memnew_placement( &buffer[ buffer_end ], Message ); + msg->args=1; + msg->instance_ID=p_id; + msg->target=p_prop; + msg->type=TYPE_SET; + + buffer_end+=sizeof(Message); + + Variant * v = memnew_placement( &buffer[ buffer_end ], Variant ); + buffer_end+=sizeof(Variant); + *v=p_value; + + + return OK; +} + +Error MessageQueue::push_notification(ObjectID p_id, int p_notification) { + + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND_V(p_notification<0, ERR_INVALID_PARAMETER ); + + uint8_t room_needed=sizeof(Message); + + if ((buffer_end+room_needed) >= buffer_size) { + String type; + if (ObjectDB::get_instance(p_id)) + type=ObjectDB::get_instance(p_id)->get_type(); + print_line("failed notification: "+itos(p_notification)+" target ID: "+itos(p_id)); + statistics(); + + } + + + + + ERR_FAIL_COND_V( (buffer_end+room_needed) >= buffer_size , ERR_OUT_OF_MEMORY ); + Message * msg = memnew_placement( &buffer[ buffer_end ], Message ); + + msg->type=TYPE_NOTIFICATION; + msg->instance_ID=p_id; + //msg->target; + msg->notification=p_notification; + + buffer_end+=sizeof(Message); + + + return OK; +} + +Error MessageQueue::push_call(Object *p_object, const StringName& p_method, VARIANT_ARG_DECLARE) { + + return push_call(p_object->get_instance_ID(),p_method,VARIANT_ARG_PASS); +} + +Error MessageQueue::push_notification(Object *p_object, int p_notification) { + + return push_notification(p_object->get_instance_ID(),p_notification); +} +Error MessageQueue::push_set(Object *p_object, const StringName& p_prop, const Variant& p_value) { + + return push_set(p_object->get_instance_ID(),p_prop,p_value); +} + + +void MessageQueue::statistics() { + + Map set_count; + Map notify_count; + Map call_count; + int null_count=0; + + uint32_t read_pos=0; + while (read_pos < buffer_end ) { + Message *message = (Message*)&buffer[ read_pos ]; + + Object *target = ObjectDB::get_instance(message->instance_ID); + + if (target!=NULL) { + + + switch(message->type) { + + case TYPE_CALL: { + + if (!call_count.has(message->target)) + call_count[message->target]=0; + + call_count[message->target]++; + + } break; + case TYPE_NOTIFICATION: { + + if (!notify_count.has(message->notification)) + notify_count[message->notification]=0; + + notify_count[message->notification]++; + + } break; + case TYPE_SET: { + + if (!set_count.has(message->target)) + set_count[message->target]=0; + + set_count[message->target]++; + + } break; + + } + + //object was deleted + //WARN_PRINT("Object was deleted while awaiting a callback") + //should it print a warning? + } else { + + null_count++; + } + + + read_pos+=sizeof(Message); + if (message->type!=TYPE_NOTIFICATION) + read_pos+=sizeof(Variant)*message->args; + } + + + print_line("TOTAL BYTES: "+itos(buffer_end)); + print_line("NULL count: "+itos(null_count)); + + for(Map::Element *E=set_count.front();E;E=E->next()) { + + print_line("SET "+E->key()+": "+itos(E->get())); + } + + for(Map::Element *E=call_count.front();E;E=E->next()) { + + print_line("CALL "+E->key()+": "+itos(E->get())); + } + + for(Map::Element *E=notify_count.front();E;E=E->next()) { + + print_line("NOTIFY "+itos(E->key())+": "+itos(E->get())); + } + +} + +bool MessageQueue::print() { +#if 0 + uint32_t read_pos=0; + while (read_pos < buffer_end ) { + Message *message = (Message*)&buffer[ read_pos ]; + + Object *target = ObjectDB::get_instance(message->instance_ID); + String cname; + String cfunc; + + if (target==NULL) { + //object was deleted + //WARN_PRINT("Object was deleted while awaiting a callback") + //should it print a warning? + } else if (message->notification>=0) { + + // messages don't expect a return value + cfunc="notification # "+itos(message->notification); + cname=target->get_type(); + + } else if (!message->target.empty()) { + + cfunc="property: "+message->target; + cname=target->get_type(); + + + } else if (message->target) { + + cfunc=String(message->target)+"()"; + cname=target->get_type(); + } + + + read_pos+=sizeof(Message); + if (message->type!=TYPE_NOTIFICATION) + read_pos+=sizeof(Variant)*message->args; + } +#endif + return false; +} + +int MessageQueue::get_max_buffer_usage() const { + + return buffer_max_used; +} + +void MessageQueue::flush() { + + if (buffer_max_usedinstance_ID); + + if (target!=NULL) { + + switch(message->type) { + case TYPE_CALL: { + + Variant *args= (Variant*)(message+1); + + // messages don't expect a return value + + + target->call( message->target, + (message->args>=1) ? args[0] : Variant(), + (message->args>=2) ? args[1] : Variant(), + (message->args>=3) ? args[2] : Variant(), + (message->args>=4) ? args[3] : Variant(), + (message->args>=5) ? args[4] : Variant() ); + + for(int i=0;iargs;i++) { + args[i].~Variant(); + } + + } break; + case TYPE_NOTIFICATION: { + + // messages don't expect a return value + target->notification(message->notification); + + } break; + case TYPE_SET: { + + Variant *arg= (Variant*)(message+1); + // messages don't expect a return value + target->set(message->target,*arg); + + arg->~Variant(); + } break; + } + + } + message->~Message(); + + read_pos+=sizeof(Message); + if (message->type!=TYPE_NOTIFICATION) + read_pos+=sizeof(Variant)*message->args; + _THREAD_SAFE_UNLOCK_ + + } + + _THREAD_SAFE_LOCK_ + buffer_end=0; // reset buffer + _THREAD_SAFE_UNLOCK_ + +} + +MessageQueue::MessageQueue() { + + ERR_FAIL_COND(singleton!=NULL); + singleton=this; + + buffer_end=0; + buffer_max_used=0; + buffer_size=GLOBAL_DEF( "core/message_queue_size_kb", DEFAULT_QUEUE_SIZE_KB ); + buffer_size*=1024; + buffer = memnew_arr( uint8_t, buffer_size ); +} + + +MessageQueue::~MessageQueue() { + + uint32_t read_pos=0; + + while (read_pos < buffer_end ) { + + Message *message = (Message*)&buffer[ read_pos ]; + Variant *args= (Variant*)(message+1); + int argc = message->args; + if (message->type!=TYPE_NOTIFICATION) { + for (int i=0;i~Message(); + + read_pos+=sizeof(Message); + if (message->type!=TYPE_NOTIFICATION) + read_pos+=sizeof(Variant)*message->args; + } + + singleton=NULL; + memdelete_arr( buffer ); +} diff --git a/core/message_queue.h b/core/message_queue.h new file mode 100644 index 00000000000..691c2898132 --- /dev/null +++ b/core/message_queue.h @@ -0,0 +1,92 @@ +/*************************************************************************/ +/* message_queue.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 MESSAGE_QUEUE_H +#define MESSAGE_QUEUE_H + +#include "object.h" +#include "os/mutex.h" +#include "os/thread_safe.h" +class MessageQueue { + + _THREAD_SAFE_CLASS_ + + enum { + + DEFAULT_QUEUE_SIZE_KB=1024 + }; + + Mutex *mutex; + + enum { + TYPE_CALL, + TYPE_NOTIFICATION, + TYPE_SET + }; + + struct Message { + + ObjectID instance_ID; + StringName target; + int16_t type; + union { + int16_t notification; + int16_t args; + }; + }; + + uint8_t *buffer; + uint32_t buffer_end; + uint32_t buffer_max_used; + uint32_t buffer_size; + + + static MessageQueue *singleton; +public: + + static MessageQueue *get_singleton(); + + Error push_call(ObjectID p_id, const StringName& p_method, VARIANT_ARG_LIST); + Error push_notification(ObjectID p_id, int p_notification); + Error push_set(ObjectID p_id, const StringName& p_prop, const Variant& p_value); + + Error push_call(Object *p_object, const StringName& p_method, VARIANT_ARG_LIST); + Error push_notification(Object *p_object, int p_notification); + Error push_set(Object *p_object, const StringName& p_prop, const Variant& p_value); + + bool print(); + void statistics(); + void flush(); + + int get_max_buffer_usage() const; + + MessageQueue(); + ~MessageQueue(); +}; + +#endif // MESSAGE_QUEUE_H diff --git a/core/method_bind.cpp b/core/method_bind.cpp new file mode 100644 index 00000000000..739745f70f6 --- /dev/null +++ b/core/method_bind.cpp @@ -0,0 +1,127 @@ +/*************************************************************************/ +/* method_bind.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 "object.h" +#include "method_bind.h" + + +#ifdef DEBUG_METHODS_ENABLED +PropertyInfo MethodBind::get_argument_info(int p_argument) const { + + + if (p_argument>=0) { + + String name = (p_argument& p_names) { + + arg_names=p_names; + +} +Vector MethodBind::get_argument_names() const { + + return arg_names; +} + +#endif + + + +void MethodBind::set_default_arguments(const Vector& p_defargs) { + default_arguments=p_defargs; default_argument_count=default_arguments.size(); +} + +#ifdef DEBUG_METHODS_ENABLED +void MethodBind::_generate_argument_types(int p_count) { + + + set_argument_count(p_count); + Variant::Type *argt = memnew_arr(Variant::Type,p_count+1); + argt[0]=_gen_argument_type(-1); + for(int i=0;i + +/** + @author Juan Linietsky +*/ + +#ifdef DEBUG_ENABLED +#define DEBUG_METHODS_ENABLED +#endif + +enum MethodFlags { + + METHOD_FLAG_NORMAL=1, + METHOD_FLAG_EDITOR=2, + METHOD_FLAG_NOSCRIPT=4, + METHOD_FLAG_CONST=8, + METHOD_FLAG_REVERSE=16, // used for events + METHOD_FLAG_VIRTUAL=32, + METHOD_FLAGS_DEFAULT=METHOD_FLAG_NORMAL, +}; + +template +struct VariantCaster { + + static _FORCE_INLINE_ T cast(const Variant& p_variant) { + + return p_variant; + } +}; + +template +struct VariantCaster { + + static _FORCE_INLINE_ T cast(const Variant& p_variant) { + + return p_variant; + } +}; + +template +struct VariantCaster { + + static _FORCE_INLINE_ T cast(const Variant& p_variant) { + + return p_variant; + } +}; + +#define _VC( m_idx )\ + (VariantCaster::cast( (m_idx-1)>=p_arg_count?get_default_argument(m_idx-1):*p_args[m_idx-1] )) + +//SIMPLE_NUMERIC_TYPE is used to avoid a warning on Variant::get_type_for +#define VARIANT_ENUM_CAST( m_enum ) \ +SIMPLE_NUMERIC_TYPE( m_enum );\ +template<> \ +struct VariantCaster {\ +\ + static _FORCE_INLINE_ m_enum cast(const Variant& p_variant) {\ + return (m_enum)p_variant.operator int();\ + }\ +}; + + +#define CHECK_ARG(m_arg)\ + if ((m_arg-1)get_type(),argtype)) {\ + r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;\ + r_error.argument=m_arg-1;\ + r_error.expected=argtype;\ + return Variant();\ + }\ + } + +#define CHECK_NOARG(m_arg)\ + {\ + if (p_arg##m_arg.get_type()!=Variant::NIL) {\ + if (r_argerror) *r_argerror=(m_arg-1);\ + return CALL_ERROR_EXTRA_ARGUMENT;\ + }\ + } + +// some helpers + +VARIANT_ENUM_CAST( Vector3::Axis ); +VARIANT_ENUM_CAST( Image::Format ); +VARIANT_ENUM_CAST( Error ); +VARIANT_ENUM_CAST( wchar_t ); +VARIANT_ENUM_CAST( Margin ); +VARIANT_ENUM_CAST( Orientation ); +VARIANT_ENUM_CAST( HAlign ); + +class MethodBind { + + + int method_id; + uint32_t hint_flags; + StringName name; + Vector default_arguments; + int default_argument_count; + int argument_count; +#ifdef DEBUG_METHODS_ENABLED + Vector arg_names; + Variant::Type *argument_types; + StringName ret_type; +#endif + bool _const; + + +protected: + + void _set_const(bool p_const); +#ifdef DEBUG_METHODS_ENABLED + virtual Variant::Type _gen_argument_type(int p_arg) const=0; + void _generate_argument_types(int p_count); + void set_argument_types(Variant::Type *p_types) { argument_types=p_types; } +#endif + void set_argument_count(int p_count) { argument_count=p_count; } +public: + + Vector get_default_arguments() const { return default_arguments; } + _FORCE_INLINE_ int get_default_argument_count() const { return default_argument_count; } + + _FORCE_INLINE_ Variant has_default_argument(int p_arg) const { + + int idx=argument_count-p_arg-1; + + if (idx<0 || idx>=default_arguments.size()) + return false; + else + return true; + } + + _FORCE_INLINE_ Variant get_default_argument(int p_arg) const { + + int idx=argument_count-p_arg-1; + + if (idx<0 || idx>=default_arguments.size()) + return Variant(); + else + return default_arguments[idx]; + } + +#ifdef DEBUG_METHODS_ENABLED + + _FORCE_INLINE_ void set_return_type(const StringName& p_type) { ret_type=p_type; } + + _FORCE_INLINE_ Variant::Type get_argument_type(int p_argument) const { + + ERR_FAIL_COND_V(p_argument<-1 || p_argument>argument_count,Variant::NIL); + return argument_types[p_argument+1]; + + } + + PropertyInfo get_argument_info(int p_argument) const; + + void set_argument_names(const Vector& p_names); + Vector get_argument_names() const; +#endif + void set_hint_flags(uint32_t p_hint) { hint_flags=p_hint; } + uint32_t get_hint_flags() const { return hint_flags|(is_const()?METHOD_FLAG_CONST:0); } + virtual String get_instance_type() const=0; + + _FORCE_INLINE_ int get_argument_count() const { return argument_count; }; + +#if 0 + _FORCE_INLINE_ Variant call_safe(const Variant** p_args,int p_arg_count, Variant::CallError& r_error) { + + r_error.error=Variant::CallError::CALL_OK; + check_call( p_args, &errorarg ); + if (!err) + return call(p_object, VARIANT_ARG_PASS ); + + VARIANT_ARGPTRS + String errstr; + String methodname = get_instance_type()+"::"+name; + if (err==CALL_ERROR_ARGUMENT_TYPE) { + errstr="Invalid Argument to call: '"+methodname+"'. Cannot convert argument "+itos(errorarg+1)+" from "+Variant::get_type_name(get_argument_type(errorarg))+" to "+Variant::get_type_name(argptr[errorarg]->get_type())+"."; + } + if (err==CALL_ERROR_EXTRA_ARGUMENT) { + errstr="Invalid call. Member function '"+methodname+"' takes "+itos(get_argument_count())+" argument, but argument "+itos(errorarg+1)+" was received."; + } + + ERR_PRINT(errstr.ascii().get_data()); + return Variant(); + } +#endif + virtual Variant call(Object* p_object,const Variant** p_args,int p_arg_count, Variant::CallError& r_error)=0; + StringName get_name() const; + void set_name(const StringName& p_name); + _FORCE_INLINE_ int get_method_id() const { return method_id; } + _FORCE_INLINE_ bool is_const() const { return _const; } + + + void set_default_arguments(const Vector& p_defargs); + + MethodBind(); + virtual ~MethodBind(); +}; + + +template +class MethodBindNative : public MethodBind { +public: + typedef Variant (T::*NativeCall)(const Variant**,int ,Variant::CallError &); +protected: + + NativeCall call_method; +public: + + virtual Variant::Type _gen_argument_type(int p_arg) const { + + return Variant::NIL; + } + + virtual Variant call(Object* p_object,const Variant** p_args,int p_arg_count, Variant::CallError& r_error) { + + T* instance=static_cast(p_object); + return (instance->*call_method)(p_args,p_arg_count,r_error); + } + void set_method_info(const MethodInfo& p_info) { + + + set_argument_count( p_info.arguments.size() ); +#ifdef DEBUG_METHODS_ENABLED + Variant::Type *at = memnew_arr( Variant::Type , p_info.arguments.size()+1 ); + at[0]=p_info.return_val.type; + if (p_info.arguments.size()) { + + Vector names; + names.resize(p_info.arguments.size()); + for(int i=0;i +MethodBind* create_native_method_bind( Variant (T::*p_method)(const Variant**,int ,Variant::CallError &), const MethodInfo& p_info ) { + + MethodBindNative * a = memnew( (MethodBindNative) ); + a->set_method(p_method); + a->set_method_info(p_info); + return a; +} + + +/** This amazing hack is based on the FastDelegates theory */ + +// tale of an amazing hack.. // + +// if you declare an unexisting class.. +class __UnexistingClass; + + +#include "method_bind.inc" + +#endif diff --git a/core/multi_bucket_array.h b/core/multi_bucket_array.h new file mode 100644 index 00000000000..a24af941f8a --- /dev/null +++ b/core/multi_bucket_array.h @@ -0,0 +1,42 @@ +/*************************************************************************/ +/* multi_bucket_array.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 MULTI_BUCKET_ARRAY_H +#define MULTI_BUCKET_ARRAY_H + + +template +class MultiBucketArray { + + + +}; + + + +#endif // MULTI_BUCKET_ARRAY_H diff --git a/core/object.cpp b/core/object.cpp new file mode 100644 index 00000000000..692010b1b70 --- /dev/null +++ b/core/object.cpp @@ -0,0 +1,1660 @@ +/*************************************************************************/ +/* object.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 "object.h" +#include "print_string.h" +#include "object_type_db.h" +#include "script_language.h" +#include "message_queue.h" +#include "core_string_names.h" +#include "translation.h" +Array convert_property_list(const List * p_list) { + + Array va; + for (const List::Element *E=p_list->front();E;E=E->next()) { + + const PropertyInfo &pi = E->get(); + Dictionary d; + d["name"]=pi.name; + d["type"]=pi.type; + d["hint"]=pi.hint; + d["hint_string"]=pi.hint_string; + d["usage"]=pi.usage; + va.push_back(d); + } + + return va; +} + +MethodInfo::MethodInfo() { + + id=0; + flags=METHOD_FLAG_NORMAL; +} + +MethodInfo::MethodInfo(const String& p_name) { + + id=0; + name=p_name; + flags=METHOD_FLAG_NORMAL; +} +MethodInfo::MethodInfo(const String& p_name, const PropertyInfo& p_param1) { + + id=0; + name=p_name; + arguments.push_back( p_param1 ); + flags=METHOD_FLAG_NORMAL; +} +MethodInfo::MethodInfo(const String& p_name, const PropertyInfo& p_param1,const PropertyInfo& p_param2) { + + id=0; + name=p_name; + arguments.push_back( p_param1 ); + arguments.push_back( p_param2 ); + flags=METHOD_FLAG_NORMAL; +} + +MethodInfo::MethodInfo(const String& p_name, const PropertyInfo& p_param1,const PropertyInfo& p_param2,const PropertyInfo& p_param3) { + + id=0; + name=p_name; + arguments.push_back( p_param1 ); + arguments.push_back( p_param2 ); + arguments.push_back( p_param3 ); + flags=METHOD_FLAG_NORMAL; +} + +MethodInfo::MethodInfo(const String& p_name, const PropertyInfo& p_param1,const PropertyInfo& p_param2,const PropertyInfo& p_param3,const PropertyInfo& p_param4) { + + id=0; + name=p_name; + arguments.push_back( p_param1 ); + arguments.push_back( p_param2 ); + arguments.push_back( p_param3 ); + arguments.push_back( p_param4 ); + flags=METHOD_FLAG_NORMAL; +} + +MethodInfo::MethodInfo(const String& p_name, const PropertyInfo& p_param1,const PropertyInfo& p_param2,const PropertyInfo& p_param3,const PropertyInfo& p_param4,const PropertyInfo& p_param5) { + id=0; + name=p_name; + arguments.push_back( p_param1 ); + arguments.push_back( p_param2 ); + arguments.push_back( p_param3 ); + arguments.push_back( p_param4 ); + arguments.push_back( p_param5 ); + flags=METHOD_FLAG_NORMAL; +} + +MethodInfo::MethodInfo(Variant::Type ret) { + + id=0; + flags=METHOD_FLAG_NORMAL; + return_val.type=ret; +} + +MethodInfo::MethodInfo(Variant::Type ret,const String& p_name) { + + id=0; + name=p_name; + flags=METHOD_FLAG_NORMAL; + return_val.type=ret; +} +MethodInfo::MethodInfo(Variant::Type ret,const String& p_name, const PropertyInfo& p_param1) { + + id=0; + name=p_name; + arguments.push_back( p_param1 ); + flags=METHOD_FLAG_NORMAL; + return_val.type=ret; +} +MethodInfo::MethodInfo(Variant::Type ret,const String& p_name, const PropertyInfo& p_param1,const PropertyInfo& p_param2) { + + id=0; + name=p_name; + arguments.push_back( p_param1 ); + arguments.push_back( p_param2 ); + flags=METHOD_FLAG_NORMAL; + return_val.type=ret; +} + +MethodInfo::MethodInfo(Variant::Type ret,const String& p_name, const PropertyInfo& p_param1,const PropertyInfo& p_param2,const PropertyInfo& p_param3) { + + id=0; + name=p_name; + arguments.push_back( p_param1 ); + arguments.push_back( p_param2 ); + arguments.push_back( p_param3 ); + flags=METHOD_FLAG_NORMAL; + return_val.type=ret; +} + +MethodInfo::MethodInfo(Variant::Type ret,const String& p_name, const PropertyInfo& p_param1,const PropertyInfo& p_param2,const PropertyInfo& p_param3,const PropertyInfo& p_param4) { + + id=0; + name=p_name; + arguments.push_back( p_param1 ); + arguments.push_back( p_param2 ); + arguments.push_back( p_param3 ); + arguments.push_back( p_param4 ); + flags=METHOD_FLAG_NORMAL; + return_val.type=ret; +} + +MethodInfo::MethodInfo(Variant::Type ret,const String& p_name, const PropertyInfo& p_param1,const PropertyInfo& p_param2,const PropertyInfo& p_param3,const PropertyInfo& p_param4,const PropertyInfo& p_param5) { + id=0; + name=p_name; + arguments.push_back( p_param1 ); + arguments.push_back( p_param2 ); + arguments.push_back( p_param3 ); + arguments.push_back( p_param4 ); + arguments.push_back( p_param5 ); + flags=METHOD_FLAG_NORMAL; + return_val.type=ret; +} + +Object::Connection::operator Variant() const { + + Dictionary d; + d["source"]=source; + d["signal"]=signal; + d["target"]=target; + d["method"]=method; + d["flags"]=flags; + d["binds"]=binds; + return d; +} + +bool Object::Connection::operator<(const Connection& p_conn) const { + + if (source==p_conn.source) { + + if (signal == p_conn.signal) { + + + if (target == p_conn.target) { + + return method < p_conn.method; + } else { + + return target < p_conn.target; + } + } else + return signal < p_conn.signal; + } else { + return source *p_parents) { + + +} +void Object::_get_valid_parents_static(List *p_parents) { + + +} +#if 0 +//old style set, deprecated + +void Object::set(const String& p_name, const Variant& p_value) { + + _setv(p_name,p_value); + + //if (!_use_builtin_script()) +// return; + + bool success; + ObjectTypeDB::set_property(this,p_name,p_value,success); + if (success) { + return; + } + + if (p_name=="__meta__") { + metadata=p_value; + } else if (p_name=="script/script") { + set_script(p_value); + } else if (script_instance) { + script_instance->set(p_name,p_value); + } + + +} +#endif + +void Object::set(const StringName& p_name, const Variant& p_value, bool *r_valid) { + +#ifdef TOOLS_ENABLED + + _edited=true; +#endif + if (script_instance) { + + if (script_instance->set(p_name,p_value)) { + if (r_valid) + *r_valid=true; + return; + } + + } + + //try built-in setgetter + { + if (ObjectTypeDB::set_property(this,p_name,p_value)) { + if (r_valid) + *r_valid=true; + return; + } + } + + + if (p_name==CoreStringNames::get_singleton()->_script) { + set_script(p_value); + if (r_valid) + *r_valid=true; + return; + + } else if (p_name==CoreStringNames::get_singleton()->_meta) { + //set_meta(p_name,p_value); + metadata=p_value; + if (r_valid) + *r_valid=true; + return; + } else { + //something inside the object... :| + bool success = _setv(p_name,p_value); + if (success) { + if (r_valid) + *r_valid=true; + return; + } + setvar(p_name,p_value,r_valid); + } + +} + +Variant Object::get(const StringName& p_name, bool *r_valid) const{ + + + Variant ret; + + if (script_instance) { + + if (script_instance->get(p_name,ret)) { + if (r_valid) + *r_valid=true; + return ret; + } + + } + + + //try built-in setgetter + { + if (ObjectTypeDB::get_property(const_cast(this),p_name,ret)) { + if (r_valid) + *r_valid=true; + return ret; + } + } + + + if (p_name==CoreStringNames::get_singleton()->_script) { + ret = get_script(); + if (r_valid) + *r_valid=true; + return ret; + + } else if (p_name==CoreStringNames::get_singleton()->_meta) { + ret = metadata; + if (r_valid) + *r_valid=true; + return ret; + } else { + //something inside the object... :| + bool success = _getv(p_name,ret); + if (success) { + if (r_valid) + *r_valid=true; + return ret; + } + //if nothing else, use getvar + return getvar(p_name,r_valid); + } + + +} + + +#if 0 +//old style get, deprecated +Variant Object::get(const String& p_name) const { + + Variant ret=_getv(p_name); + if (ret.get_type()!=Variant::NIL) + return ret; + + bool success; + ObjectTypeDB::get_property(const_cast(this),p_name,ret,success); + if (success) { + return ret; + } + + if (p_name=="__meta__") + return metadata; + else if (p_name=="script/script") + return script; + + if (script_instance) { + return script_instance->get(p_name); + } + + return Variant(); + +} +#endif + +void Object::get_property_list(List *p_list,bool p_reversed) const { + + if (script_instance && p_reversed) { + p_list->push_back( PropertyInfo(Variant::NIL,"Script Variables",PROPERTY_HINT_NONE,String(),PROPERTY_USAGE_CATEGORY)); + script_instance->get_property_list(p_list); + } + + _get_property_listv(p_list,p_reversed); + + if (!_use_builtin_script()) + return; + + if (!is_type("Script")) // can still be set, but this is for userfriendlyness + p_list->push_back( PropertyInfo( Variant::OBJECT, "script/script", PROPERTY_HINT_RESOURCE_TYPE, "Script",PROPERTY_USAGE_DEFAULT|PROPERTY_USAGE_STORE_IF_NONZERO)); + if (!metadata.empty()) + p_list->push_back( PropertyInfo( Variant::DICTIONARY, "__meta__", PROPERTY_HINT_NONE,"",PROPERTY_USAGE_NOEDITOR|PROPERTY_USAGE_STORE_IF_NONZERO)); + if (script_instance && !p_reversed) { + p_list->push_back( PropertyInfo(Variant::NIL,"Script Variables",PROPERTY_HINT_NONE,String(),PROPERTY_USAGE_CATEGORY)); + script_instance->get_property_list(p_list); + } + +} +void Object::get_method_list(List *p_list) const { + + ObjectTypeDB::get_method_list(get_type_name(),p_list); + if (script_instance) { + script_instance->get_method_list(p_list); + } +} + + + +Variant Object::_call_bind(const Variant** p_args, int p_argcount, Variant::CallError& r_error) { + + if (p_argcount<1) { + r_error.error=Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument=0; + return Variant(); + } + + if (p_args[0]->get_type()!=Variant::STRING) { + r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument=0; + r_error.expected=Variant::STRING; + return Variant(); + } + + StringName method = *p_args[0]; + + return call(method,&p_args[1],p_argcount-1,r_error); + + +} + +Variant Object::_call_deferred_bind(const Variant** p_args, int p_argcount, Variant::CallError& r_error) { + + if (p_argcount<1) { + r_error.error=Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument=0; + return Variant(); + } + + if (p_args[0]->get_type()!=Variant::STRING) { + r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument=0; + r_error.expected=Variant::STRING; + return Variant(); + } + + r_error.error=Variant::CallError::CALL_OK; + + StringName signal = *p_args[0]; + + Variant v[VARIANT_ARG_MAX]; + + + for(int i=0;i_free) { +#ifdef DEBUG_ENABLED + if (cast_to()) { + ERR_EXPLAIN("Can't 'free' a reference."); + ERR_FAIL(); + return; + } +#endif + //must be here, must be before everything, + memdelete(this); + return; + } + + //Variant ret; + + Variant::CallError error; + + if (script_instance) { + script_instance->call_multilevel(p_method,p_args,p_argcount); + //_test_call_error(p_method,error); + + } + + MethodBind *method=ObjectTypeDB::get_method(get_type_name(),p_method); + + if (method) { + + method->call(this,p_args,p_argcount,error); + _test_call_error(p_method,error); + } + +} + +void Object::call_multilevel_reversed(const StringName& p_method,const Variant** p_args,int p_argcount) { + + + MethodBind *method=ObjectTypeDB::get_method(get_type_name(),p_method); + + Variant::CallError error; + + if (method) { + + method->call(this,p_args,p_argcount,error); + _test_call_error(p_method,error); + } + + //Variant ret; + + + + if (script_instance) { + script_instance->call_multilevel_reversed(p_method,p_args,p_argcount); + //_test_call_error(p_method,error); + + } + +} + +bool Object::has_method(const StringName& p_method) const { + + if (p_method==CoreStringNames::get_singleton()->_free) { + return true; + } + + + if (script_instance && script_instance->has_method(p_method)) { + return true; + } + + MethodBind *method=ObjectTypeDB::get_method(get_type_name(),p_method); + + if (method) { + return true; + } + + return false; +} + + +Variant Object::getvar(const Variant& p_key, bool *r_valid) const { + + if (r_valid) + *r_valid=false; + return Variant(); +} +void Object::setvar(const Variant& p_key, const Variant& p_value,bool *r_valid) { + + if (r_valid) + *r_valid=false; +} + + +Variant Object::callv(const StringName& p_method,const Array& p_args) { + + if (p_args.size()==0) { + return call(p_method); + } + + Vector args; + args.resize(p_args.size()); + Vector argptrs; + argptrs.resize(p_args.size()); + + for(int i=0;i_free) { +#ifdef DEBUG_ENABLED + if (cast_to()) { + ERR_EXPLAIN("Can't 'free' a reference."); + ERR_FAIL_V(Variant()); + } +#endif + //must be here, must be before everything, + memdelete(this); + return Variant(); + } + + VARIANT_ARGPTRS; + + int argc=0; + for(int i=0;iget_type()==Variant::NIL) + break; + argc++; + } + + Variant::CallError error; + + Variant ret; + + if (script_instance) { + ret = script_instance->call(p_name,argptr,argc,error); + if (_test_call_error(p_name,error)) + return ret; + } + + MethodBind *method=ObjectTypeDB::get_method(get_type_name(),p_name); + + if (method) { + + + Variant ret = method->call(this,argptr,argc,error); + if (_test_call_error(p_name,error)) + return ret; + + return ret; + } else { + + } + + return Variant(); +#else + + VARIANT_ARGPTRS; + + int argc=0; + for(int i=0;iget_type()==Variant::NIL) + break; + argc++; + } + + Variant::CallError error; + + Variant ret = call(p_name,argptr,argc,error); + return ret; + +#endif + +} + +void Object::call_multilevel(const StringName& p_name, VARIANT_ARG_DECLARE) { +#if 0 + if (p_name==CoreStringNames::get_singleton()->_free) { +#ifdef DEBUG_ENABLED + if (cast_to()) { + ERR_EXPLAIN("Can't 'free' a reference."); + ERR_FAIL(); + return; + } +#endif + //must be here, must be before everything, + memdelete(this); + return; + } + + VARIANT_ARGPTRS; + + int argc=0; + for(int i=0;iget_type()==Variant::NIL) + break; + argc++; + } + + Variant::CallError error; + + if (script_instance) { + script_instance->call(p_name,argptr,argc,error); + _test_call_error(p_name,error); + + } + + MethodBind *method=ObjectTypeDB::get_method(get_type_name(),p_name); + + if (method) { + + method->call(this,argptr,argc,error); + _test_call_error(p_name,error); + + } + +#else + + VARIANT_ARGPTRS; + + int argc=0; + for(int i=0;iget_type()==Variant::NIL) + break; + argc++; + } + + //Variant::CallError error; + call_multilevel(p_name,argptr,argc); + +#endif + +} + + + +Variant Object::call(const StringName& p_method,const Variant** p_args,int p_argcount,Variant::CallError &r_error) { + + if (p_method==CoreStringNames::get_singleton()->_free) { + //free must be here, before anything, always ready +#ifdef DEBUG_ENABLED + if (p_argcount!=0) { + r_error.argument=0; + r_error.error=Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; + return Variant(); + } + if (cast_to()) { + r_error.argument=0; + r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD; + ERR_EXPLAIN("Can't 'free' a reference."); + ERR_FAIL_V(Variant()); + } +#endif + //must be here, must be before everything, + memdelete(this); + r_error.error=Variant::CallError::CALL_OK; + return Variant(); + } + + Variant ret; + + if (script_instance) { + ret = script_instance->call(p_method,p_args,p_argcount,r_error); + //force jumptable + switch(r_error.error) { + + case Variant::CallError::CALL_OK: + return ret; + case Variant::CallError::CALL_ERROR_INVALID_METHOD: + break; + case Variant::CallError::CALL_ERROR_INVALID_ARGUMENT: + case Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS: + case Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS: + return ret; + case Variant::CallError::CALL_ERROR_INSTANCE_IS_NULL: {} + + } + } + + MethodBind *method=ObjectTypeDB::get_method(get_type_name(),p_method); + + if (method) { + + ret=method->call(this,p_args,p_argcount,r_error); + } else { + r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD; + } + + return ret; +} + + +void Object::notification(int p_notification,bool p_reversed) { + + + _notificationv(p_notification,p_reversed); + + if (script_instance) { + script_instance->notification(p_notification); + } +} + +void Object::_changed_callback(Object *p_changed,const char *p_prop) { + + +} + +void Object::add_change_receptor( Object *p_receptor ) { + + change_receptors.insert(p_receptor); +} + +void Object::remove_change_receptor( Object *p_receptor ) { + + change_receptors.erase(p_receptor); +} + +void Object::property_list_changed_notify() { + + _change_notify(); +} + +void Object::cancel_delete() { + + _predelete_ok=true; +} + +void Object::set_script(const RefPtr& p_script) { + + if (script==p_script) + return; + + if (script_instance) { + memdelete(script_instance); + script_instance=NULL; + } + + script=p_script; + Ref\n"; + strnew+="\n"; + } else if (lines[i].find("var Module")!=-1) { + strnew+=lines[i]; + strnew+="TOTAL_MEMORY:"+itos(max_memory*1024*1024)+","; + } else { + strnew+=lines[i]+"\n"; + } + } + + CharString cs = strnew.utf8(); + html.resize(cs.size()); + for(int i=9;i& p_data) { + + + String pre = "Module['FS_createDataFile']('/', '"+p_path.replace("res://","")+"',["; + CharString cs = pre.utf8(); + f->store_buffer((const uint8_t*)cs.ptr(),cs.length()); + for(int i=0;i0) + f->store_buffer(&c,1); + + uint8_t str[4]; + uint8_t d = p_data[i]; + if (d<10) { + str[0]='0'+d; + str[1]=0; + f->store_buffer(str,1); + } else if (d<100) { + + str[0]='0'+d/10; + str[1]='0'+d%10; + str[2]=0; + f->store_buffer(str,2); + + } else { + str[0]='0'+d/100; + str[1]='0'+(d/10)%10; + str[2]='0'+d%10; + str[3]=0; + f->store_buffer(str,3); + } + } + String post = "],true,true);\n"; + cs = post.utf8(); + f->store_buffer((const uint8_t*)cs.ptr(),cs.length()); +} + + +Error EditorExportPlatformJavaScript::save_pack_file_js(void *p_userdata,const String& p_path, const Vector& p_data,int p_file,int p_total) { + + JSExportData *ed=(JSExportData*)p_userdata; + + FileAccess *f=(FileAccess *)p_userdata; + store_file_buffer(ed->f,p_path,p_data); + ed->ep->step("File: "+p_path,3+p_file*100/p_total); + return OK; + +} + +Error EditorExportPlatformJavaScript::export_project(const String& p_path,bool p_debug,const String& p_password) { + + + String src_template; + + EditorProgress ep("export","Exporting for javascript",104); + + String template_path = EditorSettings::get_singleton()->get_settings_path()+"/templates/"; + + if (p_debug) { + + src_template=custom_debug_package!=""?custom_debug_package:template_path+"javascript_debug.zip"; + } else { + + src_template=custom_release_package!=""?custom_release_package:template_path+"javascript_release.zip"; + + } + + + FileAccess *src_f=NULL; + zlib_filefunc_def io = zipio_create_io_from_file(&src_f); + + ep.step("Exporting to HTML5",0); + + unzFile pkg = unzOpen2(src_template.utf8().get_data(), &io); + if (!pkg) { + + EditorNode::add_io_error("Could not find template HTML5 to export:\n"+src_template); + return ERR_FILE_NOT_FOUND; + } + + ERR_FAIL_COND_V(!pkg, ERR_CANT_OPEN); + int ret = unzGoToFirstFile(pkg); + + + while(ret==UNZ_OK) { + + //get filename + unz_file_info info; + char fname[16384]; + ret = unzGetCurrentFileInfo(pkg,&info,fname,16384,NULL,0,NULL,0); + + String file=fname; + + Vector data; + data.resize(info.uncompressed_size); + + //read + unzOpenCurrentFile(pkg); + unzReadCurrentFile(pkg,data.ptr(),data.size()); + unzCloseCurrentFile(pkg); + + //write + + if (file=="godot.html") { + + _fix_html(data,p_path.get_file().basename(),1<<(max_memory+5)); + file=p_path.get_file(); + } + if (file=="godot.js") { + + //_fix_godot(data); + file=p_path.get_file().basename()+".js"; + } + + String dst = p_path.get_base_dir().plus_file(file); + FileAccess *f=FileAccess::open(dst,FileAccess::WRITE); + if (!f) { + EditorNode::add_io_error("Could not create file for writing:\n"+dst); + unzClose(pkg); + return ERR_FILE_CANT_WRITE; + } + f->store_buffer(data.ptr(),data.size()); + memdelete(f); + + + ret = unzGoToNextFile(pkg); + } + + + ep.step("Finding Files..",1); + + Vector remaps; + + FileAccess *f=FileAccess::open(p_path.basename()+"_files.js",FileAccess::WRITE); + if (!f) { + EditorNode::add_io_error("Could not create file for writing:\n"+p_path.basename()+"_files.js"); + return ERR_FILE_CANT_WRITE; + } + + f->store_buffer((const uint8_t*)files_pre,strlen(files_pre)); + + if (pack_mode==PACK_SINGLE_FILE) { + + String ftmp = EditorSettings::get_singleton()->get_settings_path()+"/tmp/webpack.pck"; + FileAccess *f2 = FileAccess::open(ftmp,FileAccess::WRITE); + if (!f2) { + memdelete(f); + return ERR_CANT_CREATE; + } + Error err = save_pack(f2,false); + memdelete(f2); + if (err) { + memdelete(f); + return ERR_CANT_CREATE; + } + + Vector data = FileAccess::get_file_as_array(ftmp); + store_file_buffer(f,"data.pck",data); + + + } else { + JSExportData ed; + ed.ep=&ep; + ed.f=f; + + Error err =export_project_files(save_pack_file_js,&ed,false); + if (err) + return err; + } + f->store_buffer((const uint8_t*)files_post,strlen(files_post)); + memdelete(f); + + + return OK; + +} + + +Error EditorExportPlatformJavaScript::run(int p_device) { + + String path = EditorSettings::get_singleton()->get_settings_path()+"/tmp/tmp_export.html"; + Error err = export_project(path,true,""); + if (err) + return err; + + OS::get_singleton()->shell_open(path); + + return OK; +} + + +EditorExportPlatformJavaScript::EditorExportPlatformJavaScript() { + + show_run=false; + Image img( _javascript_logo ); + logo = Ref( memnew( ImageTexture )); + logo->create_from_image(img); + max_memory=3; + pack_mode=PACK_SINGLE_FILE; +} + +bool EditorExportPlatformJavaScript::can_export(String *r_error) const { + + + bool valid=true; + String err; + String exe_path = EditorSettings::get_singleton()->get_settings_path()+"/templates/"; + + if (!FileAccess::exists(exe_path+"javascript_debug.zip") || !FileAccess::exists(exe_path+"javascript_release.zip")) { + valid=false; + err+="No export templates found.\nDownload and install export templates.\n"; + } + + if (custom_debug_package!="" && !FileAccess::exists(custom_debug_package)) { + valid=false; + err+="Custom debug package not found.\n"; + } + + if (custom_release_package!="" && !FileAccess::exists(custom_release_package)) { + valid=false; + err+="Custom release package not found.\n"; + } + + if (r_error) + *r_error=err; + + return valid; +} + + +EditorExportPlatformJavaScript::~EditorExportPlatformJavaScript() { + +} + + +void register_javascript_exporter() { + + + Ref exporter = Ref( memnew(EditorExportPlatformJavaScript) ); + EditorImportExport::get_singleton()->add_export_platform(exporter); + + +} + diff --git a/platform/javascript/export/export.h b/platform/javascript/export/export.h new file mode 100644 index 00000000000..2b575c9149d --- /dev/null +++ b/platform/javascript/export/export.h @@ -0,0 +1,29 @@ +/*************************************************************************/ +/* export.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_javascript_exporter(); diff --git a/platform/javascript/javascript_main.cpp b/platform/javascript/javascript_main.cpp new file mode 100644 index 00000000000..81485bab8ee --- /dev/null +++ b/platform/javascript/javascript_main.cpp @@ -0,0 +1,251 @@ +/*************************************************************************/ +/* javascript_main.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 +#include "os_javascript.h" +#include "main/main.h" +#include "io/resource_loader.h" +#include "os/keyboard.h" +OS_JavaScript *os=NULL; + +static void _gfx_init(void *ud,bool gl2,int w, int h,bool fs) { + + glutInitWindowSize(w, h); + glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); + glutCreateWindow("godot"); + +} + + +static void _glut_skey(bool pressed,int key) { + + InputEvent ev; + ev.type=InputEvent::KEY; + ev.key.pressed=pressed; + switch(key) { + case GLUT_KEY_F1: ev.key.scancode=KEY_F1; break; + case GLUT_KEY_F2: ev.key.scancode=KEY_F2; break; + case GLUT_KEY_F3: ev.key.scancode=KEY_F3; break; + case GLUT_KEY_F4: ev.key.scancode=KEY_F4; break; + case GLUT_KEY_F5: ev.key.scancode=KEY_F5; break; + case GLUT_KEY_F6: ev.key.scancode=KEY_F6; break; + case GLUT_KEY_F7: ev.key.scancode=KEY_F7; break; + case GLUT_KEY_F8: ev.key.scancode=KEY_F8; break; + case GLUT_KEY_F9: ev.key.scancode=KEY_F9; break; + case GLUT_KEY_F10: ev.key.scancode=KEY_F10; break; + case GLUT_KEY_F11: ev.key.scancode=KEY_F11; break; + case GLUT_KEY_F12: ev.key.scancode=KEY_F12; break; + case GLUT_KEY_LEFT: ev.key.scancode=KEY_LEFT; break; + case GLUT_KEY_UP: ev.key.scancode=KEY_UP; break; + case GLUT_KEY_RIGHT: ev.key.scancode=KEY_RIGHT; break; + case GLUT_KEY_DOWN: ev.key.scancode=KEY_DOWN; break; + case GLUT_KEY_PAGE_UP: ev.key.scancode=KEY_PAGEUP; break; + case GLUT_KEY_PAGE_DOWN: ev.key.scancode=KEY_PAGEDOWN; break; + case GLUT_KEY_HOME: ev.key.scancode=KEY_HOME; break; + case GLUT_KEY_END: ev.key.scancode=KEY_END; break; + case GLUT_KEY_INSERT: ev.key.scancode=KEY_INSERT; break; + } + + + uint32_t m = glutGetModifiers(); + ev.key.mod.alt=(m&GLUT_ACTIVE_ALT)!=0; + ev.key.mod.shift=(m&GLUT_ACTIVE_SHIFT)!=0; + ev.key.mod.control=(m&GLUT_ACTIVE_CTRL)!=0; + + os->push_input(ev); +} + +static void _glut_skey_up(int key, int x, int y) { + + _glut_skey(false,key); +} + +static void _glut_skey_down(int key, int x, int y) { + + _glut_skey(true,key); +} + +static void _glut_key(bool pressed,unsigned char key) { + + InputEvent ev; + ev.type=InputEvent::KEY; + ev.key.pressed=pressed; + switch(key) { + case '\n': ev.key.scancode=KEY_RETURN; break; + case 0x1b: ev.key.scancode=KEY_ESCAPE; break; + case 8: ev.key.scancode=KEY_BACKSPACE; break; + case 0x7f: ev.key.scancode=KEY_DELETE; break; + case 0x20: ev.key.scancode=KEY_SPACE; ev.key.unicode=key; break; + default: { + ev.key.unicode=key; + } + } + + + uint32_t m = glutGetModifiers(); + ev.key.mod.alt=(m&GLUT_ACTIVE_ALT)!=0; + ev.key.mod.shift=(m&GLUT_ACTIVE_SHIFT)!=0; + ev.key.mod.control=(m&GLUT_ACTIVE_CTRL)!=0; + + os->push_input(ev); + +} + +static void _glut_key_up(unsigned char key, int x, int y) { + + _glut_key(false,key); +} + +static void _glut_key_down(unsigned char key, int x, int y) { + + _glut_key(true,key); +} + +static uint32_t _mouse_button_mask=0; + +static void _glut_mouse_button(int button, int state, int x, int y) { + + InputEvent ev; + ev.type=InputEvent::MOUSE_BUTTON; + switch(button) { + case GLUT_LEFT_BUTTON: ev.mouse_button.button_index=BUTTON_LEFT; break; + case GLUT_MIDDLE_BUTTON: ev.mouse_button.button_index=BUTTON_MIDDLE; break; + case GLUT_RIGHT_BUTTON: ev.mouse_button.button_index=BUTTON_RIGHT; break; + case 3: ev.mouse_button.button_index=BUTTON_WHEEL_UP; break; + case 4: ev.mouse_button.button_index=BUTTON_WHEEL_DOWN; break; + } + + + ev.mouse_button.pressed=state==GLUT_DOWN; + ev.mouse_button.x=x; + ev.mouse_button.y=y; + ev.mouse_button.global_x=x; + ev.mouse_button.global_y=y; + + if (ev.mouse_button.button_index<4) { + if (ev.mouse_button.pressed) { + _mouse_button_mask|=1<push_input(ev); + +} + + +static int _glut_prev_x=0; +static int _glut_prev_y=0; + +static void _glut_mouse_motion(int x, int y) { + + InputEvent ev; + ev.type=InputEvent::MOUSE_MOTION; + ev.mouse_motion.button_mask=_mouse_button_mask; + ev.mouse_motion.x=x; + ev.mouse_motion.y=y; + ev.mouse_motion.global_x=x; + ev.mouse_motion.global_y=y; + ev.mouse_motion.relative_x=x-_glut_prev_x; + ev.mouse_motion.relative_y=y-_glut_prev_y; + _glut_prev_x=x; + _glut_prev_y=y; + + uint32_t m = glutGetModifiers(); + ev.mouse_motion.mod.alt=(m&GLUT_ACTIVE_ALT)!=0; + ev.mouse_motion.mod.shift=(m&GLUT_ACTIVE_SHIFT)!=0; + ev.mouse_motion.mod.control=(m&GLUT_ACTIVE_CTRL)!=0; + + os->push_input(ev); + +} + +static void _gfx_idle() { + + glutPostRedisplay(); +} + +static void _godot_draw(void) { + + os->main_loop_iterate(); + glutSwapBuffers(); +} + +int main(int argc, char *argv[]) { + /* Initialize the window */ + + printf("let it go!\n"); + glutInit(&argc, argv); + os = new OS_JavaScript(_gfx_init,NULL,NULL,NULL,NULL); +#if 0 + char *args[]={"-test","gui","-v",NULL}; + Error err = Main::setup("apk",3,args); +#else +// char *args[]={"-v",NULL};// +// Error err = Main::setup("",1,args); + Error err = Main::setup("",0,NULL); + +#endif + ResourceLoader::set_abort_on_missing_resources(false); //ease up compatibility + Main::start(); + + glutSpecialUpFunc(_glut_skey_up); + glutSpecialFunc(_glut_skey_down); + glutKeyboardUpFunc(_glut_key_up); + glutKeyboardFunc(_glut_key_down); + glutMouseFunc(_glut_mouse_button); + glutMotionFunc(_glut_mouse_motion); + glutMotionFunc(_glut_mouse_motion); + glutPassiveMotionFunc(_glut_mouse_motion); + + + + /* Set up glut callback functions */ + glutIdleFunc (_gfx_idle); +// glutReshapeFunc(gears_reshape); + glutDisplayFunc(_godot_draw); + //glutSpecialFunc(gears_special); + os->main_loop_begin(); + + glutMainLoop(); + + return 0; +} + + +/* + * + *09] reduz: yes, define TOTAL_MEMORY on Module. for example var Module = { TOTAL_MEMORY: 12345.. }; before the main + * + */ diff --git a/platform/javascript/logo.png b/platform/javascript/logo.png new file mode 100644 index 00000000000..07e0a41292e Binary files /dev/null and b/platform/javascript/logo.png differ diff --git a/platform/javascript/os_javascript.cpp b/platform/javascript/os_javascript.cpp new file mode 100644 index 00000000000..c131a7e84ba --- /dev/null +++ b/platform/javascript/os_javascript.cpp @@ -0,0 +1,593 @@ +/*************************************************************************/ +/* os_javascript.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 "os_javascript.h" +#include "drivers/gles2/rasterizer_gles2.h" +#include "core/io/file_access_buffered_fa.h" +#include "drivers/unix/file_access_unix.h" +#include "drivers/unix/dir_access_unix.h" + +#include "servers/visual/visual_server_raster.h" + +#include "main/main.h" + +#include "core/globals.h" + +int OS_JavaScript::get_video_driver_count() const { + + return 1; +} +const char * OS_JavaScript::get_video_driver_name(int p_driver) const { + + return "GLES2"; +} + +OS::VideoMode OS_JavaScript::get_default_video_mode() const { + + return OS::VideoMode(); +} + +int OS_JavaScript::get_audio_driver_count() const { + + return 1; +} + +const char * OS_JavaScript::get_audio_driver_name(int p_driver) const { + + return "JavaScript"; +} + +void OS_JavaScript::initialize_core() { + + OS_Unix::initialize_core(); + FileAccess::make_default >(FileAccess::ACCESS_RESOURCES); + +} + +void OS_JavaScript::set_opengl_extensions(const char* p_gl_extensions) { + + ERR_FAIL_COND(!p_gl_extensions); + gl_extensions=p_gl_extensions; +} + +void OS_JavaScript::initialize(const VideoMode& p_desired,int p_video_driver,int p_audio_driver) { + + print_line("Init OS"); + + if (gfx_init_func) + gfx_init_func(gfx_init_ud,use_gl2,p_desired.width,p_desired.height,p_desired.fullscreen); + + default_videomode=p_desired; + + print_line("Init Audio"); + + AudioDriverManagerSW::add_driver(&audio_driver_javascript); + + if (true) { + RasterizerGLES2 *rasterizer_gles22=memnew( RasterizerGLES2(false,false,false,false) );; + rasterizer_gles22->set_use_framebuffers(false); //not supported by emscripten + if (gl_extensions) + rasterizer_gles22->set_extensions(gl_extensions); + rasterizer = rasterizer_gles22; + } else { +// rasterizer = memnew( RasterizerGLES1(true, false) ); + } + + print_line("Init VS"); + + visual_server = memnew( VisualServerRaster(rasterizer) ); + visual_server->init(); + visual_server->cursor_set_visible(false, 0); + + AudioDriverManagerSW::get_driver(p_audio_driver)->set_singleton(); + + if (AudioDriverManagerSW::get_driver(p_audio_driver)->init()!=OK) { + + ERR_PRINT("Initializing audio failed."); + } + + print_line("Init SM"); + + sample_manager = memnew( SampleManagerMallocSW ); + audio_server = memnew( AudioServerSW(sample_manager) ); + + print_line("Init Mixer"); + + audio_server->set_mixer_params(AudioMixerSW::INTERPOLATION_LINEAR,false); + audio_server->init(); + + print_line("Init SoundServer"); + + spatial_sound_server = memnew( SpatialSoundServerSW ); + spatial_sound_server->init(); + + print_line("Init SpatialSoundServer"); + + spatial_sound_2d_server = memnew( SpatialSound2DServerSW ); + spatial_sound_2d_server->init(); + + // + print_line("Init Physicsserver"); + + physics_server = memnew( PhysicsServerSW ); + physics_server->init(); + physics_2d_server = memnew( Physics2DServerSW ); + physics_2d_server->init(); + + input = memnew( InputDefault ); + +} + +void OS_JavaScript::set_main_loop( MainLoop * p_main_loop ) { + + main_loop=p_main_loop; + input->set_main_loop(p_main_loop); + +} + +void OS_JavaScript::delete_main_loop() { + + memdelete( main_loop ); +} + +void OS_JavaScript::finalize() { + + memdelete(input); +} + + +void OS_JavaScript::vprint(const char* p_format, va_list p_list, bool p_stderr) { + + if (p_stderr) { + + vfprintf(stderr,p_format,p_list); + fflush(stderr); + } else { + + vprintf(p_format,p_list); + fflush(stdout); + } +} + +void OS_JavaScript::print(const char *p_format, ... ) { + + va_list argp; + va_start(argp, p_format); + vprintf(p_format, argp ); + va_end(argp); + +} + +void OS_JavaScript::alert(const String& p_alert) { + + print("ALERT: %s\n",p_alert.utf8().get_data()); +} + + +void OS_JavaScript::set_mouse_show(bool p_show) { + + //javascript has no mouse... +} + +void OS_JavaScript::set_mouse_grab(bool p_grab) { + + //it really has no mouse...! +} + +bool OS_JavaScript::is_mouse_grab_enabled() const { + + //*sigh* technology has evolved so much since i was a kid.. + return false; +} +Point2 OS_JavaScript::get_mouse_pos() const { + + return Point2(); +} +int OS_JavaScript::get_mouse_button_state() const { + + return 0; +} +void OS_JavaScript::set_window_title(const String& p_title) { + + +} + +//interesting byt not yet +//void set_clipboard(const String& p_text); +//String get_clipboard() const; + +void OS_JavaScript::set_video_mode(const VideoMode& p_video_mode,int p_screen) { + + +} + +OS::VideoMode OS_JavaScript::get_video_mode(int p_screen) const { + + return default_videomode; +} +void OS_JavaScript::get_fullscreen_mode_list(List *p_list,int p_screen) const { + + p_list->push_back(default_videomode); +} + +String OS_JavaScript::get_name() { + + return "HTML5"; +} + +MainLoop *OS_JavaScript::get_main_loop() const { + + return main_loop; +} + +bool OS_JavaScript::can_draw() const { + + return true; //always? +} + +void OS_JavaScript::set_cursor_shape(CursorShape p_shape) { + + //javascript really really really has no mouse.. how amazing.. +} + +void OS_JavaScript::main_loop_begin() { + + if (main_loop) + main_loop->init(); +} +bool OS_JavaScript::main_loop_iterate() { + + if (!main_loop) + return false; + return Main::iteration(); +} + +void OS_JavaScript::main_loop_end() { + + if (main_loop) + main_loop->finish(); + +} + +void OS_JavaScript::main_loop_focusout() { + + if (main_loop) + main_loop->notification(MainLoop::NOTIFICATION_WM_FOCUS_OUT); + //audio_driver_javascript.set_pause(true); + +} + +void OS_JavaScript::main_loop_focusin(){ + + if (main_loop) + main_loop->notification(MainLoop::NOTIFICATION_WM_FOCUS_IN); + //audio_driver_javascript.set_pause(false); + +} + +void OS_JavaScript::push_input(const InputEvent& p_ev) { + + InputEvent ev = p_ev; + ev.ID=last_id++; + input->parse_input_event(p_ev); +} + +void OS_JavaScript::process_touch(int p_what,int p_pointer, const Vector& p_points) { + +// print_line("ev: "+itos(p_what)+" pnt: "+itos(p_pointer)+" pointc: "+itos(p_points.size())); + + switch(p_what) { + case 0: { //gesture begin + + if (touch.size()) { + //end all if exist + InputEvent ev; + ev.type=InputEvent::MOUSE_BUTTON; + ev.ID=last_id++; + ev.mouse_button.button_index=BUTTON_LEFT; + ev.mouse_button.button_mask=BUTTON_MASK_LEFT; + ev.mouse_button.pressed=false; + ev.mouse_button.x=touch[0].pos.x; + ev.mouse_button.y=touch[0].pos.y; + ev.mouse_button.global_x=touch[0].pos.x; + ev.mouse_button.global_y=touch[0].pos.y; + input->parse_input_event(ev); + + + for(int i=0;iparse_input_event(ev); + + } + } + + touch.resize(p_points.size()); + for(int i=0;iparse_input_event(ev); + } + + + //send touch + for(int i=0;iparse_input_event(ev); + } + + } break; + case 1: { //motion + + + if (p_points.size()) { + //send mouse, should look for point 0? + InputEvent ev; + ev.type=InputEvent::MOUSE_MOTION; + ev.ID=last_id++; + ev.mouse_motion.button_mask=BUTTON_MASK_LEFT; + ev.mouse_motion.x=p_points[0].pos.x; + ev.mouse_motion.y=p_points[0].pos.y; + input->set_mouse_pos(Point2(ev.mouse_motion.x,ev.mouse_motion.y)); + ev.mouse_motion.speed_x=input->get_mouse_speed().x; + ev.mouse_motion.speed_y=input->get_mouse_speed().y; + ev.mouse_motion.relative_x=p_points[0].pos.x-last_mouse.x; + ev.mouse_motion.relative_y=p_points[0].pos.y-last_mouse.y; + last_mouse=p_points[0].pos; + input->parse_input_event(ev); + } + + ERR_FAIL_COND(touch.size()!=p_points.size()); + + for(int i=0;iparse_input_event(ev); + touch[i].pos=p_points[idx].pos; + } + + + } break; + case 2: { //release + + + + if (touch.size()) { + //end all if exist + InputEvent ev; + ev.type=InputEvent::MOUSE_BUTTON; + ev.ID=last_id++; + ev.mouse_button.button_index=BUTTON_LEFT; + ev.mouse_button.button_mask=BUTTON_MASK_LEFT; + ev.mouse_button.pressed=false; + ev.mouse_button.x=touch[0].pos.x; + ev.mouse_button.y=touch[0].pos.y; + ev.mouse_button.global_x=touch[0].pos.x; + ev.mouse_button.global_y=touch[0].pos.y; + input->parse_input_event(ev); + + + for(int i=0;iparse_input_event(ev); + + } + touch.clear(); + } + + } break; + case 3: { // add tuchi + + + + + + ERR_FAIL_INDEX(p_pointer,p_points.size()); + + TouchPos tp=p_points[p_pointer]; + touch.push_back(tp); + + InputEvent ev; + ev.type=InputEvent::SCREEN_TOUCH; + ev.ID=last_id++; + ev.screen_touch.index=tp.id; + ev.screen_touch.pressed=true; + ev.screen_touch.x=tp.pos.x; + ev.screen_touch.y=tp.pos.y; + input->parse_input_event(ev); + + } break; + case 4: { + + + for(int i=0;iparse_input_event(ev); + touch.remove(i); + i--; + } + } + + } break; + + } + +} + +void OS_JavaScript::process_accelerometer(const Vector3& p_accelerometer) { + + input->set_accelerometer(p_accelerometer); +} + +bool OS_JavaScript::has_touchscreen_ui_hint() const { + + return true; +} + +void OS_JavaScript::main_loop_request_quit() { + + if (main_loop) + main_loop->notification(MainLoop::NOTIFICATION_WM_QUIT_REQUEST); +} + +void OS_JavaScript::set_display_size(Size2 p_size) { + + default_videomode.width=p_size.x; + default_videomode.height=p_size.y; +} + +void OS_JavaScript::reload_gfx() { + + if (gfx_init_func) + gfx_init_func(gfx_init_ud,use_gl2,default_videomode.width,default_videomode.height,default_videomode.fullscreen); + if (rasterizer) + rasterizer->reload_vram(); +} + +Error OS_JavaScript::shell_open(String p_uri) { + + if (open_uri_func) + return open_uri_func(p_uri)?ERR_CANT_OPEN:OK; + return ERR_UNAVAILABLE; +}; + +String OS_JavaScript::get_resource_dir() const { + + return "/"; //javascript has it's own filesystem for resources inside the APK +} + +String OS_JavaScript::get_locale() const { + + if (get_locale_func) + return get_locale_func(); + return OS_Unix::get_locale(); +} + + +String OS_JavaScript::get_data_dir() const { + + if (get_data_dir_func) + return get_data_dir_func(); + return "/"; + //return Globals::get_singleton()->get_singleton_object("GodotOS")->call("get_data_dir"); +}; + + + + +OS_JavaScript::OS_JavaScript(GFXInitFunc p_gfx_init_func,void*p_gfx_init_ud, OpenURIFunc p_open_uri_func, GetDataDirFunc p_get_data_dir_func,GetLocaleFunc p_get_locale_func) { + + + default_videomode.width=800; + default_videomode.height=600; + default_videomode.fullscreen=true; + default_videomode.resizable=false; + + gfx_init_func=p_gfx_init_func; + gfx_init_ud=p_gfx_init_ud; + main_loop=NULL; + last_id=1; + gl_extensions=NULL; + rasterizer=NULL; + + open_uri_func=p_open_uri_func; + get_data_dir_func=p_get_data_dir_func; + get_locale_func=p_get_locale_func; + + +} + +OS_JavaScript::~OS_JavaScript() { + + +} diff --git a/platform/javascript/os_javascript.h b/platform/javascript/os_javascript.h new file mode 100644 index 00000000000..dfc93d3ff0e --- /dev/null +++ b/platform/javascript/os_javascript.h @@ -0,0 +1,164 @@ +/*************************************************************************/ +/* os_javascript.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 OS_JAVASCRIPT_H +#define OS_JAVASCRIPT_H + +#include "os/input.h" +#include "drivers/unix/os_unix.h" +#include "os/main_loop.h" +#include "servers/physics/physics_server_sw.h" +#include "servers/spatial_sound/spatial_sound_server_sw.h" +#include "servers/spatial_sound_2d/spatial_sound_2d_server_sw.h" +#include "servers/audio/audio_server_sw.h" +#include "servers/physics_2d/physics_2d_server_sw.h" +#include "servers/visual/rasterizer.h" + +#include "audio_driver_javascript.h" + +typedef void (*GFXInitFunc)(void *ud,bool gl2,int w, int h, bool fs); +typedef int (*OpenURIFunc)(const String&); +typedef String (*GetDataDirFunc)(); +typedef String (*GetLocaleFunc)(); + +class OS_JavaScript : public OS_Unix { +public: + + struct TouchPos { + int id; + Point2 pos; + }; + +private: + + Vector touch; + + Point2 last_mouse; + unsigned int last_id; + GFXInitFunc gfx_init_func; + void*gfx_init_ud; + + bool use_gl2; + + Rasterizer *rasterizer; + VisualServer *visual_server; + AudioServerSW *audio_server; + SampleManagerMallocSW *sample_manager; + SpatialSoundServerSW *spatial_sound_server; + SpatialSound2DServerSW *spatial_sound_2d_server; + PhysicsServer *physics_server; + Physics2DServer *physics_2d_server; + AudioDriverJavaScript audio_driver_javascript; + const char* gl_extensions; + + InputDefault *input; + VideoMode default_videomode; + MainLoop * main_loop; + + OpenURIFunc open_uri_func; + GetDataDirFunc get_data_dir_func; + GetLocaleFunc get_locale_func; + +public: + + // functions used by main to initialize/deintialize the OS + virtual int get_video_driver_count() const; + virtual const char * get_video_driver_name(int p_driver) const; + + virtual VideoMode get_default_video_mode() const; + + virtual int get_audio_driver_count() const; + virtual const char * get_audio_driver_name(int p_driver) const; + + virtual void initialize_core(); + virtual void initialize(const VideoMode& p_desired,int p_video_driver,int p_audio_driver); + + virtual void set_main_loop( MainLoop * p_main_loop ); + virtual void delete_main_loop(); + + virtual void finalize(); + + + typedef int64_t ProcessID; + + static OS* get_singleton(); + + virtual void vprint(const char* p_format, va_list p_list, bool p_stderr=false); + virtual void print(const char *p_format, ... ); + virtual void alert(const String& p_alert); + + + virtual void set_mouse_show(bool p_show); + virtual void set_mouse_grab(bool p_grab); + virtual bool is_mouse_grab_enabled() const; + virtual Point2 get_mouse_pos() const; + virtual int get_mouse_button_state() const; + virtual void set_window_title(const String& p_title); + + //virtual void set_clipboard(const String& p_text); + //virtual String get_clipboard() const; + + virtual void set_video_mode(const VideoMode& p_video_mode,int p_screen=0); + virtual VideoMode get_video_mode(int p_screen=0) const; + virtual void get_fullscreen_mode_list(List *p_list,int p_screen=0) const; + + virtual String get_name(); + virtual MainLoop *get_main_loop() const; + + virtual bool can_draw() const; + + virtual void set_cursor_shape(CursorShape p_shape); + + void main_loop_begin(); + bool main_loop_iterate(); + void main_loop_request_quit(); + void main_loop_end(); + void main_loop_focusout(); + void main_loop_focusin(); + + virtual bool has_touchscreen_ui_hint() const; + + void set_opengl_extensions(const char* p_gl_extensions); + void set_display_size(Size2 p_size); + + void reload_gfx(); + + virtual Error shell_open(String p_uri); + virtual String get_data_dir() const; + virtual String get_resource_dir() const; + virtual String get_locale() const; + + void process_accelerometer(const Vector3& p_accelerometer); + void process_touch(int p_what,int p_pointer, const Vector& p_points); + void push_input(const InputEvent& p_ev); + OS_JavaScript(GFXInitFunc p_gfx_init_func,void*p_gfx_init_ud, OpenURIFunc p_open_uri_func, GetDataDirFunc p_get_data_dir_func,GetLocaleFunc p_get_locale_func); + ~OS_JavaScript(); + +}; + +#endif diff --git a/platform/javascript/platform_config.h b/platform/javascript/platform_config.h new file mode 100644 index 00000000000..38fc934ae46 --- /dev/null +++ b/platform/javascript/platform_config.h @@ -0,0 +1,29 @@ +/*************************************************************************/ +/* platform_config.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. */ +/*************************************************************************/ +#include diff --git a/platform/nacl/SCsub b/platform/nacl/SCsub new file mode 100644 index 00000000000..ac01752dbb8 --- /dev/null +++ b/platform/nacl/SCsub @@ -0,0 +1,30 @@ +Import('env') + +nacl_lib = [ + + 'os_nacl.cpp', + 'audio_driver_nacl.cpp', + 'godot_nacl.cpp', + #'pepper_main.cpp', + 'opengl_context.cpp', + 'godot_module.cpp', + 'geturl_handler.cpp', +] + +nacl_posix = [ + + '#drivers/unix/thread_posix.cpp', + '#drivers/unix/mutex_posix.cpp', + '#drivers/unix/semaphore_posix.cpp', +] + +posix_lib = [] +for f in nacl_posix: + posix_lib.append(env.Object(f, CPPFLAGS = env['CPPFLAGS']+['-DUNIX_ENABLED'], OBJSUFFIX = '.posix'+env['OBJSUFFIX'])) + +prog = env.Program('#bin/godot_nacl', nacl_lib + posix_lib) + +if (env['nacl_arch'] == 'i686'): + env.Alias("nacl_32", prog) +if (env['nacl_arch'] == 'x86_64'): + env.Alias("nacl_64", prog) diff --git a/platform/nacl/audio_driver_nacl.cpp b/platform/nacl/audio_driver_nacl.cpp new file mode 100644 index 00000000000..8f1861eee3a --- /dev/null +++ b/platform/nacl/audio_driver_nacl.cpp @@ -0,0 +1,106 @@ +/*************************************************************************/ +/* audio_driver_nacl.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 "audio_driver_nacl.h" + +#include "ppapi/cpp/instance.h" + +extern pp::Instance* godot_instance; + +const char* AudioDriverNacl::get_name() const { + + return "Nacl"; +} + +void AudioDriverNacl::output_callback(void* samples, uint32_t buffer_size, void* data) { + + AudioDriverNacl* ad = (AudioDriverNacl*)data; + int16_t* out = (int16_t*)samples; + + ad->lock(); + ad->audio_server_process(ad->sample_frame_count_, ad->samples_in); + ad->unlock(); + + for (int i=0; isample_count; i++) { + + out[i] = ad->samples_in[i]>>16; + }; + +}; + +Error AudioDriverNacl::init(){ + + int frame_size = 4096; + sample_frame_count_ = pp::AudioConfig::RecommendSampleFrameCount(godot_instance,PP_AUDIOSAMPLERATE_44100, frame_size); + sample_count = sample_frame_count_ * 2; + + audio_ = pp::Audio(godot_instance, + pp::AudioConfig(godot_instance, + PP_AUDIOSAMPLERATE_44100, + sample_frame_count_), + &AudioDriverNacl::output_callback, + this); + + samples_in = memnew_arr(int32_t, sample_frame_count_ * 2); + + return OK; +} +void AudioDriverNacl::start(){ + + audio_.StartPlayback(); +} +int AudioDriverNacl::get_mix_rate() const { + + return 44100; +} +AudioDriverSW::OutputFormat AudioDriverNacl::get_output_format() const{ + + return OUTPUT_STEREO; +} +void AudioDriverNacl::lock(){ + +} +void AudioDriverNacl::unlock() { + + +} +void AudioDriverNacl::finish(){ + + audio_.StopPlayback(); +} + + +AudioDriverNacl::AudioDriverNacl() { +} + +AudioDriverNacl::~AudioDriverNacl() { + + memdelete_arr(samples_in); +} + + diff --git a/platform/nacl/audio_driver_nacl.h b/platform/nacl/audio_driver_nacl.h new file mode 100644 index 00000000000..4a45d0bb4b1 --- /dev/null +++ b/platform/nacl/audio_driver_nacl.h @@ -0,0 +1,63 @@ +/*************************************************************************/ +/* audio_driver_nacl.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 AUDIO_DRIVER_NACL_H +#define AUDIO_DRIVER_NACL_H + +#include "servers/audio/audio_server_sw.h" + +#include "ppapi/cpp/audio.h" + +class AudioDriverNacl : public AudioDriverSW { + + static void output_callback(void* samples, uint32_t buffer_size, void* data); + + int32_t* samples_in; + int sample_frame_count_; + int sample_count; + pp::Audio audio_; + +public: + + virtual const char* get_name() const; + + virtual Error init(); + virtual void start(); + virtual int get_mix_rate() const ; + virtual OutputFormat get_output_format() const; + virtual void lock(); + virtual void unlock(); + virtual void finish(); + + + AudioDriverNacl(); + ~AudioDriverNacl(); +}; + +#endif // AUDIO_DRIVER_NACL_H + diff --git a/platform/nacl/context_gl_nacl.cpp b/platform/nacl/context_gl_nacl.cpp new file mode 100644 index 00000000000..70cf01a16e0 --- /dev/null +++ b/platform/nacl/context_gl_nacl.cpp @@ -0,0 +1,67 @@ +/*************************************************************************/ +/* context_gl_nacl.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 "context_gl_nacl.h" + +#include +#include +#include +#include +#include +#include + +void ContextGLNacl::make_current() { + +}; + +int ContextGLNacl::get_window_width() { + +}; + +int ContextGLNacl::get_window_height() { + +}; + +void ContextGLNacl::swap_buffers() { + +}; + +Error ContextGLNacl::initialize() { + +}; + + +ContextGLNacl::ContextGLNacl() { + + +}; + +ContextGLNacl::~ContextGLNacl() { + +}; + diff --git a/platform/nacl/context_gl_nacl.h b/platform/nacl/context_gl_nacl.h new file mode 100644 index 00000000000..a5cf7a728cf --- /dev/null +++ b/platform/nacl/context_gl_nacl.h @@ -0,0 +1,58 @@ +/*************************************************************************/ +/* context_gl_nacl.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 CONTEXT_GL_NACL_H +#define CONTEXT_GL_NACL_H + +#include "drivers/gl_context/context_gl.h" + +class NPDevice; + +class ContextGLNacl : public ContextGL { + + enum { + COMMAND_BUFFER_SIZE = 1024 * 1024, + }; + + NPDevice* device3d_; + +public: + + virtual void make_current(); + + virtual int get_window_width(); + virtual int get_window_height(); + virtual void swap_buffers(); + + virtual Error initialize(); + + ContextGLNacl(); + ~ContextGLNacl(); +}; + +#endif // CONTEXT_GL_NACL_H diff --git a/platform/nacl/detect.py b/platform/nacl/detect.py new file mode 100644 index 00000000000..f8849cfd259 --- /dev/null +++ b/platform/nacl/detect.py @@ -0,0 +1,71 @@ +import os +import sys + +def is_active(): + return True + +def get_name(): + return "NaCl" + +def can_build(): + + import os + if not os.environ.has_key("NACLPATH"): + return False + return True + +def get_opts(): + + return [ + ('NACLPATH', 'the path to nacl', os.environ.get("NACLPATH", 0)), + ('nacl_arch', 'The architecture for Nacl build (can be i686 or x86_64', 'i686'), + ] + +def get_flags(): + + return [ + ('nedmalloc', 'no'), + ('tools', 'no'), + ] + + + +def configure(env): + + env.Append(CPPPATH=['#platform/nacl']) + + env['OBJSUFFIX'] = ".nacl.${nacl_arch}.o" + env['LIBSUFFIX'] = ".nacl.${nacl_arch}.a" + env['PROGSUFFIX'] = ".${nacl_arch}.nexe" + + env['ENV']['PATH'] = env['ENV']['PATH']+":"+env['NACLPATH']+"/toolchain/linux_x86_newlib/bin" + + env['CC'] = '${nacl_arch}-nacl-gcc' + env['CXX'] = '${nacl_arch}-nacl-g++' + env['AR'] = '${nacl_arch}-nacl-ar' + + env.Append(CCFLAGS=['-fexceptions', '-Wno-long-long', '-pthread', '-DXP_UNIX']) + + env.Append(CPPPATH=env['NACLPATH']) + + if (env["target"]=="release"): + + env.Append(CCFLAGS=['-O2','-ffast-math','-fomit-frame-pointer', '-ffunction-sections', '-fdata-sections', '-fno-default-inline']) + + elif (env["target"]=="debug"): + + env.Append(CCFLAGS=['-g', '-O0', '-Wall','-DDEBUG_ENABLED']) + + + elif (env["target"]=="profile"): + + env.Append(CCFLAGS=['-g','-pg']) + env.Append(LINKFLAGS=['-pg']) + + env.Append(CCFLAGS=['-DNACL_ENABLED', '-DGLES2_ENABLED']) + + env.Append(LIBFLAGS=['m32']) + env.Append(LIBS=env.Split('ppapi ppapi_cpp pthread srpc ppapi_gles22')) + + import methods + env.Append( BUILDERS = { 'GLSL120GLES' : env.Builder(action = methods.build_gles2_headers, suffix = 'glsl.h',src_suffix = '.glsl') } ) diff --git a/platform/nacl/geturl_handler.cpp b/platform/nacl/geturl_handler.cpp new file mode 100644 index 00000000000..4873d691ce8 --- /dev/null +++ b/platform/nacl/geturl_handler.cpp @@ -0,0 +1,150 @@ +/*************************************************************************/ +/* geturl_handler.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 "geturl_handler.h" + +#include "core/os/copymem.h" + +#include +#include +#include "ppapi/c/pp_errors.h" +#include "ppapi/c/ppb_instance.h" +#include "ppapi/cpp/module.h" +#include "ppapi/cpp/var.h" + +void GetURLHandler::Start() { + pp::CompletionCallback cc = + cc_factory_.NewCallback(&GetURLHandler::OnOpen); + url_loader_.Open(url_request_, cc); +} + +void GetURLHandler::OnOpen(int32_t result) { + if (result != PP_OK) { + status = STATUS_ERROR; + return; + } + // Here you would process the headers. A real program would want to at least + // check the HTTP code and potentially cancel the request. + // pp::URLResponseInfo response = loader_.GetResponseInfo(); + + // Start streaming. + ReadBody(); +} + +void GetURLHandler::AppendDataBytes(const char* buffer, int32_t num_bytes) { + if (num_bytes <= 0) + return; + // Make sure we don't get a buffer overrun. + num_bytes = std::min(READ_BUFFER_SIZE, num_bytes); + int ofs = data.size(); + data.resize(ofs + num_bytes); + copymem(&data[ofs], buffer, num_bytes); +} + +void GetURLHandler::OnRead(int32_t result) { + if (result == PP_OK) { + // Streaming the file is complete. + status = STATUS_COMPLETED; + instance_->HandleMessage("package_finished"); + instance_->HandleMessage(0); + printf("completed!\n"); + } else if (result > 0) { + // The URLLoader just filled "result" number of bytes into our buffer. + // Save them and perform another read. + AppendDataBytes(buffer_, result); + ReadBody(); + } else { + // A read error occurred. + status = STATUS_ERROR; + ERR_FAIL_COND(result < 0); + } +} + +void GetURLHandler::ReadBody() { + // Note that you specifically want an "optional" callback here. This will + // allow ReadBody() to return synchronously, ignoring your completion + // callback, if data is available. For fast connections and large files, + // reading as fast as we can will make a large performance difference + // However, in the case of a synchronous return, we need to be sure to run + // the callback we created since the loader won't do anything with it. + pp::CompletionCallback cc = + cc_factory_.NewOptionalCallback(&GetURLHandler::OnRead); + int32_t result = PP_OK; + do { + result = url_loader_.ReadResponseBody(buffer_, sizeof(buffer_), cc); + // Handle streaming data directly. Note that we *don't* want to call + // OnRead here, since in the case of result > 0 it will schedule + // another call to this function. If the network is very fast, we could + // end up with a deeply recursive stack. + if (result > 0) { + AppendDataBytes(buffer_, result); + } + } while (result > 0); + + if (result != PP_OK_COMPLETIONPENDING) { + // Either we reached the end of the stream (result == PP_OK) or there was + // an error. We want OnRead to get called no matter what to handle + // that case, whether the error is synchronous or asynchronous. If the + // result code *is* COMPLETIONPENDING, our callback will be called + // asynchronously. + cc.Run(result); + } +} + +GetURLHandler::Status GetURLHandler::get_status() const { + + return status; +}; + +Vector GetURLHandler::get_data() const { + + return data; +}; + +int GetURLHandler::get_bytes_read() const { + + return data.size(); +}; + +GetURLHandler::GetURLHandler(pp::Instance* instance, + const String& url) + : instance_(instance), + url_(url), + url_request_(instance), + url_loader_(instance), + cc_factory_(this) { + url_request_.SetURL(std::string(url.utf8().get_data())); + url_request_.SetMethod("GET"); + status = STATUS_NONE; + printf("url handler for url %ls!\n", url.c_str()); +} + +GetURLHandler::~GetURLHandler() { +} + + diff --git a/platform/nacl/geturl_handler.h b/platform/nacl/geturl_handler.h new file mode 100644 index 00000000000..44086ae7c1f --- /dev/null +++ b/platform/nacl/geturl_handler.h @@ -0,0 +1,115 @@ +/*************************************************************************/ +/* geturl_handler.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 EXAMPLES_GETURL_GETURL_HANDLER_H_ +#define EXAMPLES_GETURL_GETURL_HANDLER_H_ + +#include "core/ustring.h" +#include "core/vector.h" +#include "ppapi/cpp/completion_callback.h" +#include "ppapi/cpp/url_loader.h" +#include "ppapi/cpp/url_request_info.h" +#include "ppapi/cpp/instance.h" +#include "ppapi/utility/completion_callback_factory.h" + +#define READ_BUFFER_SIZE 32768 + +// GetURLHandler is used to download data from |url|. When download is +// finished or when an error occurs, it posts a message back to the browser +// with the results encoded in the message as a string and self-destroys. +// +// EXAMPLE USAGE: +// GetURLHandler* handler* = GetURLHandler::Create(instance,url); +// handler->Start(); +// +class GetURLHandler { + +public: + + enum Status { + + STATUS_NONE, + STATUS_IN_PROGRESS, + STATUS_COMPLETED, + STATUS_ERROR, + }; + +private: + + Status status; + + // Callback fo the pp::URLLoader::Open(). + // Called by pp::URLLoader when response headers are received or when an + // error occurs (in response to the call of pp::URLLoader::Open()). + // Look at and + // for more information about pp::URLLoader. + void OnOpen(int32_t result); + + // Callback fo the pp::URLLoader::ReadResponseBody(). + // |result| contains the number of bytes read or an error code. + // Appends data from this->buffer_ to this->url_response_body_. + void OnRead(int32_t result); + + // Reads the response body (asynchronously) into this->buffer_. + // OnRead() will be called when bytes are received or when an error occurs. + void ReadBody(); + + // Append data bytes read from the URL onto the internal buffer. Does + // nothing if |num_bytes| is 0. + void AppendDataBytes(const char* buffer, int32_t num_bytes); + + pp::Instance* instance_; // Weak pointer. + String url_; // URL to be downloaded. + pp::URLRequestInfo url_request_; + pp::URLLoader url_loader_; // URLLoader provides an API to download URLs. + char buffer_[READ_BUFFER_SIZE]; // Temporary buffer for reads. + Vector data; // Contains accumulated downloaded data. + pp::CompletionCallbackFactory cc_factory_; + bool complete; + + GetURLHandler(const GetURLHandler&); + void operator=(const GetURLHandler&); + +public: + // Creates instance of GetURLHandler on the heap. + // GetURLHandler objects shall be created only on the heap (they + // self-destroy when all data is in). + // Initiates page (URL) download. + void Start(); + + Status get_status() const; + Vector get_data() const; + + int get_bytes_read() const; + + GetURLHandler(pp::Instance* instance_, const String& url); + ~GetURLHandler(); +}; + +#endif // EXAMPLES_GETURL_GETURL_HANDLER_H_ + diff --git a/platform/nacl/godot_module.cpp b/platform/nacl/godot_module.cpp new file mode 100644 index 00000000000..b5a049d9bfd --- /dev/null +++ b/platform/nacl/godot_module.cpp @@ -0,0 +1,332 @@ +/*************************************************************************/ +/* godot_module.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 "opengl_context.h" + +#include +#include +#include +#include + +#include "ppapi/cpp/instance.h" +#include "ppapi/cpp/module.h" +#include "ppapi/gles2/gl2ext_ppapi.h" + +#include "ppapi/cpp/rect.h" +#include "ppapi/cpp/size.h" +#include "ppapi/cpp/var.h" +#include "geturl_handler.h" + +#include "core/variant.h" +#include "os_nacl.h" + +extern int nacl_main(int argc, const char** argn, const char** argv); +extern void nacl_cleanup(); + +static String pkg_url; + +pp::Instance* godot_instance = NULL; + +struct StateData { + int arg_count; + Array args; + String method; +}; + +extern OSNacl* os_nacl; + +class GodotInstance : public pp::Instance { + + enum State { + STATE_METHOD, + STATE_PARAM_COUNT, + STATE_PARAMS, + STATE_CALL, + }; + + State state; + StateData* sd; + SharedOpenGLContext opengl_context_; + int width; + int height; + + #define MAX_ARGS 64 + uint32_t init_argc; + char* init_argn[MAX_ARGS]; + char* init_argv[MAX_ARGS]; + + bool package_loaded; + GetURLHandler* package_pending; + +public: + explicit GodotInstance(PP_Instance instance) : pp::Instance(instance) { + printf("GodotInstance!\n"); + state = STATE_METHOD; + RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE | PP_INPUTEVENT_CLASS_KEYBOARD | PP_INPUTEVENT_CLASS_WHEEL | PP_INPUTEVENT_CLASS_TOUCH); + sd = NULL; + package_pending = NULL; + package_loaded = false; + godot_instance = this; + } + virtual ~GodotInstance() { + + nacl_cleanup(); + } + + /// Called by the browser to handle the postMessage() call in Javascript. + /// Detects which method is being called from the message contents, and + /// calls the appropriate function. Posts the result back to the browser + /// asynchronously. + /// @param[in] var_message The message posted by the browser. The possible + /// messages are 'fortyTwo' and 'reverseText:Hello World'. Note that + /// the 'reverseText' form contains the string to reverse following a ':' + /// separator. + virtual void HandleMessage(const pp::Var& var_message); + + bool HandleInputEvent(const pp::InputEvent& event); + + bool Init(uint32_t argc, const char* argn[], const char* argv[]) { + + printf("******* init! %i, %p, %p\n", argc, argn, argv); + fflush(stdout); + if (opengl_context_ == NULL) { + opengl_context_.reset(new OpenGLContext(this)); + }; + opengl_context_->InvalidateContext(this); + opengl_context_->ResizeContext(pp::Size(0, 0)); + int current = opengl_context_->MakeContextCurrent(this); + printf("current is %i\n", current); + + os_nacl = new OSNacl; + + pkg_url = ""; + for (uint32_t i=0; iStart(); + }; + return true; + }; + + // Called whenever the in-browser window changes size. + virtual void DidChangeView(const pp::Rect& position, const pp::Rect& clip) { + + if (position.size().width() == width && + position.size().height() == height) + return; // Size didn't change, no need to update anything. + + if (opengl_context_ == NULL) { + opengl_context_.reset(new OpenGLContext(this)); + }; + opengl_context_->InvalidateContext(this); + opengl_context_->ResizeContext(position.size()); + if (!opengl_context_->MakeContextCurrent(this)) + return; + + width = position.size().width(); + height = position.size().height(); + // init gl here? + OS::VideoMode vm; + vm.width = width; + vm.height = height; + vm.resizable = false; + vm.fullscreen = true; + OS::get_singleton()->set_video_mode(vm, 0); + + DrawSelf(); + }; + + // Called to draw the contents of the module's browser area. + void DrawSelf() { + + if (opengl_context_ == NULL) + return; + + opengl_context_->FlushContext(); + }; +}; + +static Variant to_variant(const pp::Var& p_var) { + + if (p_var.is_undefined() || p_var.is_null()) + return Variant(); + if (p_var.is_bool()) + return Variant(p_var.AsBool()); + if (p_var.is_double()) + return Variant(p_var.AsDouble()); + if (p_var.is_int()) + return Variant((int64_t)p_var.AsInt()); + if (p_var.is_string()) + return Variant(String::utf8(p_var.AsString().c_str())); + + return Variant(); +}; + +void GodotInstance::HandleMessage(const pp::Var& var_message) { + + switch (state) { + + case STATE_METHOD: { + + ERR_FAIL_COND(!var_message.is_string()); + sd->method = var_message.AsString().c_str(); + state = STATE_PARAM_COUNT; + } break; + case STATE_PARAM_COUNT: { + + ERR_FAIL_COND(!var_message.is_number()); + sd->arg_count = var_message.AsInt(); + state = sd->arg_count>0?STATE_PARAMS:STATE_CALL; + + } break; + case STATE_PARAMS: { + + Variant p = to_variant(var_message); + sd->args.push_back(p); + if (sd->args.size() >= sd->arg_count) + state = STATE_CALL; + } break; + default: + break; + }; + + if (state == STATE_CALL) { + + // call + state = STATE_METHOD; + + + if (sd->method == "package_finished") { + + GetURLHandler::Status status = package_pending->get_status(); + printf("status is %i, %i, %i\n", status, GetURLHandler::STATUS_ERROR, GetURLHandler::STATUS_COMPLETED); + if (status == GetURLHandler::STATUS_ERROR) { + printf("Error fetching package!\n"); + }; + if (status == GetURLHandler::STATUS_COMPLETED) { + + OSNacl* os = (OSNacl*)OS::get_singleton(); + os->add_package(pkg_url, package_pending->get_data()); + }; + memdelete(package_pending); + package_pending = NULL; + + package_loaded = true; + + opengl_context_->MakeContextCurrent(this); + nacl_main(init_argc, (const char**)init_argn, (const char**)init_argv); + for (uint32_t i=0; imethod == "get_package_status") { + + if (package_loaded) { + // post "loaded" + PostMessage("loaded"); + } else if (package_pending == NULL) { + // post "none" + PostMessage("none"); + } else { + // post package_pending->get_bytes_read(); + PostMessage(package_pending->get_bytes_read()); + }; + }; + }; +} + +bool GodotInstance::HandleInputEvent(const pp::InputEvent& event) { + + OSNacl* os = (OSNacl*)OS::get_singleton(); + os->handle_event(event); + return true; +}; + +class GodotModule : public pp::Module { + public: + GodotModule() : pp::Module() {} + virtual ~GodotModule() { + glTerminatePPAPI(); + } + + /// Create and return a GodotInstance object. + /// @param[in] instance a handle to a plug-in instance. + /// @return a newly created GodotInstance. + /// @note The browser is responsible for calling @a delete when done. + virtual pp::Instance* CreateInstance(PP_Instance instance) { + printf("CreateInstance! %x\n", instance); + return new GodotInstance(instance); + } + + /// Called by the browser when the module is first loaded and ready to run. + /// This is called once per module, not once per instance of the module on + /// the page. + virtual bool Init() { + printf("GodotModule::init!\n"); + return glInitializePPAPI(get_browser_interface()); + } +}; + +namespace pp { +/// Factory function called by the browser when the module is first loaded. +/// The browser keeps a singleton of this module. It calls the +/// CreateInstance() method on the object you return to make instances. There +/// is one instance per tag on the page. This is the main binding +/// point for your NaCl module with the browser. +/// @return new GodotModule. +/// @note The browser is responsible for deleting returned @a Module. +Module* CreateModule() { + printf("CreateModule!\n"); + return new GodotModule(); +} +} // namespace pp diff --git a/platform/nacl/godot_nacl.cpp b/platform/nacl/godot_nacl.cpp new file mode 100644 index 00000000000..753f4124f8d --- /dev/null +++ b/platform/nacl/godot_nacl.cpp @@ -0,0 +1,80 @@ +/*************************************************************************/ +/* godot_nacl.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 "os_nacl.h" +#include "main/main.h" + +#include +#include + +OSNacl* os_nacl = NULL; + +int nacl_main(int argc, const char** argn, const char** argv) { + + // os is created in GodotModule::Init for the nacl module + printf("called with %i args, %p, %p\n", argc, argn, argv); + char* nargv[64]; + int nargc = 1; + nargv[0] = (char*)argv[0]; + for (int i=1; i= |minChromeVersion_|. + * @type {integer} + * @private + */ + this.minChromeVersion_ = minChromeVersion; + + /** + * List of Browser plugin objects. + * @type {Ojbect array} + * @private + */ + this.plugins_ = plugins; + + /** + * Application version string from the Browser. + * @type {integer} + * @private + */ + this.appVersion_ = appVersion; + + /** + * Flag used to indicate if the browser has Native Client and is if the + * browser version is recent enough. + * @type {boolean} + * @private + */ + this.isValidBrowser_ = false; + + /** + * Actual major version of Chrome -- found by querying the browser. + * @type {integer} + * @private + */ + this.chromeVersion_ = null; + + /** + * Browser support status. This allows the user to get a detailed status + * rather than using this.browserSupportMessage. + */ + this.browserSupportStatus_ = + browser_version.BrowserChecker.StatusValues.UNKNOWN; +} + +/** + * The values used for BrowserChecker status to indicate success or + * a specific error. + * @enum {id} + */ +browser_version.BrowserChecker.StatusValues = { + UNKNOWN: 0, + NACL_ENABLED: 1, + UNKNOWN_BROWSER: 2, + CHROME_VERSION_TOO_OLD: 3, + NACL_NOT_ENABLED: 4, + NOT_USING_SERVER: 5 +}; + +/** + * Determines if the plugin with name |name| exists in the browser. + * @param {string} name The name of the plugin. + * @param {Object array} plugins The plugins in this browser. + * @return {bool} |true| if the plugin is found. + */ +browser_version.BrowserChecker.prototype.pluginExists = function(name, + plugins) { + for (var index=0; index < plugins.length; index++) { + var plugin = this.plugins_[index]; + var plugin_name = plugin['name']; + // If the plugin is not found, you can use the Javascript console + // to see the names of the plugins that were found when debugging. + if (plugin_name.indexOf(name) != -1) { + return true; + } + } + return false; +} + +/** + * Returns browserSupportStatus_ which indicates if the browser supports + * Native Client. Values are defined as literals in + * browser_version.BrowserChecker.StatusValues. + * @ return {int} Level of NaCl support. + */ +browser_version.BrowserChecker.prototype.getBrowserSupportStatus = function() { + return this.browserSupportStatus_; +} + +/** + * Returns isValidBrowser (true/false) to indicate if the browser supports + * Native Client. + * @ return {bool} If this browser has NativeClient and correct version. + */ +browser_version.BrowserChecker.prototype.getIsValidBrowser = function() { + return this.isValidBrowser_; +} + +/** + * Checks to see if this browser can support Native Client applications. + * For Chrome browsers, checks to see if the "Native Client" plugin is + * enabled. + */ +browser_version.BrowserChecker.prototype.checkBrowser = function() { + var versionPatt = /Chrome\/(\d+)\.(\d+)\.(\d+)\.(\d+)/; + var result = this.appVersion_.match(versionPatt); + + // |result| stores the Chrome version number. + if (!result) { + this.isValidBrowser_ = false; + this.browserSupportStatus_ = + browser_version.BrowserChecker.StatusValues.UNKNOWN_BROWSER; + } else { + this.chromeVersion_ = result[1]; + // We know we have Chrome, check version and/or plugin named Native Client + if (this.chromeVersion_ >= this.minChromeVersion_) { + var found_nacl = this.pluginExists('Native Client', this.plugins_); + if (found_nacl) { + this.isValidBrowser_ = true; + this.browserSupportStatus_ = + browser_version.BrowserChecker.StatusValues.NACL_ENABLED; + } else { + this.isValidBrowser_ = false; + this.browserSupportStatus_ = + browser_version.BrowserChecker.StatusValues.NACL_NOT_ENABLED; + } + } else { + // We are in a version that is less than |minChromeVersion_| + this.isValidBrowser_ = false; + this.browserSupportStatus_ = + browser_version.BrowserChecker.StatusValues.CHROME_VERSION_TOO_OLD; + } + } + var my_protocol = window.location.protocol; + if (my_protocol.indexOf('file') == 0) { + this.isValidBrowser_ = false; + this.browserSupportStatus_ = + browser_version.BrowserChecker.StatusValues.NOT_USING_SERVER; + } +} + diff --git a/platform/nacl/html/godot_nacl.nmf b/platform/nacl/html/godot_nacl.nmf new file mode 100644 index 00000000000..eca039ec1ee --- /dev/null +++ b/platform/nacl/html/godot_nacl.nmf @@ -0,0 +1,6 @@ +{ + "program": { + "x86-64": {"url": "godot_nacl.x86_64.nexe"}, + "x86-32": {"url": "godot_nacl.i686.nexe"} + } +} diff --git a/platform/nacl/html/icon_128.png b/platform/nacl/html/icon_128.png new file mode 100644 index 00000000000..1793aa7e7a9 Binary files /dev/null and b/platform/nacl/html/icon_128.png differ diff --git a/platform/nacl/html/icon_16.png b/platform/nacl/html/icon_16.png new file mode 100644 index 00000000000..09de19e4184 Binary files /dev/null and b/platform/nacl/html/icon_16.png differ diff --git a/platform/nacl/html/index.html b/platform/nacl/html/index.html new file mode 100644 index 00000000000..e8be6f69b97 --- /dev/null +++ b/platform/nacl/html/index.html @@ -0,0 +1,258 @@ + + + + + Load Progress Example + + + + + + + +

Native Client Load Event Example

+ +

Status

+
NO-STATUS
+ +
+ + + + + + +
+ +

Event Log

+
event log?
+ + + + diff --git a/platform/nacl/html/manifest.json b/platform/nacl/html/manifest.json new file mode 100644 index 00000000000..6e271507a99 --- /dev/null +++ b/platform/nacl/html/manifest.json @@ -0,0 +1,19 @@ +{ + "name": "Capsuleman's Castle Adventure", + "description": "Capsuleman's Castle Adventure", + "version": "1", + "app": { + "launch": { + "local_path": "index.html" + } + }, + "icons": { + "16": "icon_16.png", + "128": "icon_128.png" + }, + "permissions": [ + "unlimitedStorage", + "notifications", + "experimental" + ] +} diff --git a/platform/nacl/logo.png b/platform/nacl/logo.png new file mode 100644 index 00000000000..aac72c01b26 Binary files /dev/null and b/platform/nacl/logo.png differ diff --git a/platform/nacl/nacl_keycodes.h b/platform/nacl/nacl_keycodes.h new file mode 100644 index 00000000000..1cbf379078b --- /dev/null +++ b/platform/nacl/nacl_keycodes.h @@ -0,0 +1,422 @@ +/*************************************************************************/ +/* nacl_keycodes.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. */ +/*************************************************************************/ + * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com. All rights reserved. + * Copyright (C) 2008, 2009 Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, LOSS OF USE, DATA, OR + * PROFITS, OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BASE_KEYBOARD_CODES_POSIX_H_ +#define BASE_KEYBOARD_CODES_POSIX_H_ +#pragma once + +#include "core/os/keyboard.h" + +enum { + VKEY_BACK = 0x08, + VKEY_TAB = 0x09, + VKEY_CLEAR = 0x0C, + VKEY_RETURN = 0x0D, + VKEY_SHIFT = 0x10, + VKEY_CONTROL = 0x11, + VKEY_MENU = 0x12, + VKEY_PAUSE = 0x13, + VKEY_CAPITAL = 0x14, + VKEY_KANA = 0x15, + VKEY_HANGUL = 0x15, + VKEY_JUNJA = 0x17, + VKEY_FINAL = 0x18, + VKEY_HANJA = 0x19, + VKEY_KANJI = 0x19, + VKEY_ESCAPE = 0x1B, + VKEY_CONVERT = 0x1C, + VKEY_NONCONVERT = 0x1D, + VKEY_ACCEPT = 0x1E, + VKEY_MODECHANGE = 0x1F, + VKEY_SPACE = 0x20, + VKEY_PRIOR = 0x21, + VKEY_NEXT = 0x22, + VKEY_END = 0x23, + VKEY_HOME = 0x24, + VKEY_LEFT = 0x25, + VKEY_UP = 0x26, + VKEY_RIGHT = 0x27, + VKEY_DOWN = 0x28, + VKEY_SELECT = 0x29, + VKEY_PRINT = 0x2A, + VKEY_EXECUTE = 0x2B, + VKEY_SNAPSHOT = 0x2C, + VKEY_INSERT = 0x2D, + VKEY_DELETE = 0x2E, + VKEY_HELP = 0x2F, + VKEY_0 = 0x30, + VKEY_1 = 0x31, + VKEY_2 = 0x32, + VKEY_3 = 0x33, + VKEY_4 = 0x34, + VKEY_5 = 0x35, + VKEY_6 = 0x36, + VKEY_7 = 0x37, + VKEY_8 = 0x38, + VKEY_9 = 0x39, + VKEY_A = 0x41, + VKEY_B = 0x42, + VKEY_C = 0x43, + VKEY_D = 0x44, + VKEY_E = 0x45, + VKEY_F = 0x46, + VKEY_G = 0x47, + VKEY_H = 0x48, + VKEY_I = 0x49, + VKEY_J = 0x4A, + VKEY_K = 0x4B, + VKEY_L = 0x4C, + VKEY_M = 0x4D, + VKEY_N = 0x4E, + VKEY_O = 0x4F, + VKEY_P = 0x50, + VKEY_Q = 0x51, + VKEY_R = 0x52, + VKEY_S = 0x53, + VKEY_T = 0x54, + VKEY_U = 0x55, + VKEY_V = 0x56, + VKEY_W = 0x57, + VKEY_X = 0x58, + VKEY_Y = 0x59, + VKEY_Z = 0x5A, + VKEY_LWIN = 0x5B, + VKEY_COMMAND = VKEY_LWIN, // Provide the Mac name for convenience. + VKEY_RWIN = 0x5C, + VKEY_APPS = 0x5D, + VKEY_SLEEP = 0x5F, + VKEY_NUMPAD0 = 0x60, + VKEY_NUMPAD1 = 0x61, + VKEY_NUMPAD2 = 0x62, + VKEY_NUMPAD3 = 0x63, + VKEY_NUMPAD4 = 0x64, + VKEY_NUMPAD5 = 0x65, + VKEY_NUMPAD6 = 0x66, + VKEY_NUMPAD7 = 0x67, + VKEY_NUMPAD8 = 0x68, + VKEY_NUMPAD9 = 0x69, + VKEY_MULTIPLY = 0x6A, + VKEY_ADD = 0x6B, + VKEY_SEPARATOR = 0x6C, + VKEY_SUBTRACT = 0x6D, + VKEY_DECIMAL = 0x6E, + VKEY_DIVIDE = 0x6F, + VKEY_F1 = 0x70, + VKEY_F2 = 0x71, + VKEY_F3 = 0x72, + VKEY_F4 = 0x73, + VKEY_F5 = 0x74, + VKEY_F6 = 0x75, + VKEY_F7 = 0x76, + VKEY_F8 = 0x77, + VKEY_F9 = 0x78, + VKEY_F10 = 0x79, + VKEY_F11 = 0x7A, + VKEY_F12 = 0x7B, + VKEY_F13 = 0x7C, + VKEY_F14 = 0x7D, + VKEY_F15 = 0x7E, + VKEY_F16 = 0x7F, + VKEY_F17 = 0x80, + VKEY_F18 = 0x81, + VKEY_F19 = 0x82, + VKEY_F20 = 0x83, + VKEY_F21 = 0x84, + VKEY_F22 = 0x85, + VKEY_F23 = 0x86, + VKEY_F24 = 0x87, + VKEY_NUMLOCK = 0x90, + VKEY_SCROLL = 0x91, + VKEY_LSHIFT = 0xA0, + VKEY_RSHIFT = 0xA1, + VKEY_LCONTROL = 0xA2, + VKEY_RCONTROL = 0xA3, + VKEY_LMENU = 0xA4, + VKEY_RMENU = 0xA5, + VKEY_BROWSER_BACK = 0xA6, + VKEY_BROWSER_FORWARD = 0xA7, + VKEY_BROWSER_REFRESH = 0xA8, + VKEY_BROWSER_STOP = 0xA9, + VKEY_BROWSER_SEARCH = 0xAA, + VKEY_BROWSER_FAVORITES = 0xAB, + VKEY_BROWSER_HOME = 0xAC, + VKEY_VOLUME_MUTE = 0xAD, + VKEY_VOLUME_DOWN = 0xAE, + VKEY_VOLUME_UP = 0xAF, + VKEY_MEDIA_NEXT_TRACK = 0xB0, + VKEY_MEDIA_PREV_TRACK = 0xB1, + VKEY_MEDIA_STOP = 0xB2, + VKEY_MEDIA_PLAY_PAUSE = 0xB3, + VKEY_MEDIA_LAUNCH_MAIL = 0xB4, + VKEY_MEDIA_LAUNCH_MEDIA_SELECT = 0xB5, + VKEY_MEDIA_LAUNCH_APP1 = 0xB6, + VKEY_MEDIA_LAUNCH_APP2 = 0xB7, + VKEY_OEM_1 = 0xBA, + VKEY_OEM_PLUS = 0xBB, + VKEY_OEM_COMMA = 0xBC, + VKEY_OEM_MINUS = 0xBD, + VKEY_OEM_PERIOD = 0xBE, + VKEY_OEM_2 = 0xBF, + VKEY_OEM_3 = 0xC0, + VKEY_OEM_4 = 0xDB, + VKEY_OEM_5 = 0xDC, + VKEY_OEM_6 = 0xDD, + VKEY_OEM_7 = 0xDE, + VKEY_OEM_8 = 0xDF, + VKEY_OEM_102 = 0xE2, + VKEY_PROCESSKEY = 0xE5, + VKEY_PACKET = 0xE7, + VKEY_ATTN = 0xF6, + VKEY_CRSEL = 0xF7, + VKEY_EXSEL = 0xF8, + VKEY_EREOF = 0xF9, + VKEY_PLAY = 0xFA, + VKEY_ZOOM = 0xFB, + VKEY_NONAME = 0xFC, + VKEY_PA1 = 0xFD, + VKEY_OEM_CLEAR = 0xFE, + VKEY_UNKNOWN = 0 +}; + +static uint32_t godot_key(uint32_t p_key, bool& is_char) { + + is_char = false; + + switch (p_key) { + + case VKEY_BACK: return KEY_BACKSPACE; + case VKEY_TAB: return KEY_TAB; + case VKEY_CLEAR: return KEY_CLEAR; + case VKEY_RETURN: return KEY_RETURN; + case VKEY_SHIFT: return KEY_SHIFT; + case VKEY_CONTROL: return KEY_CONTROL; + case VKEY_MENU: return KEY_MENU; + case VKEY_PAUSE: return KEY_PAUSE; +// case VKEY_CAPITAL: return KEY_CAPITAL; +// case VKEY_KANA: return KEY_KANA; +// case VKEY_HANGUL: return KEY_HANGUL; +// case VKEY_JUNJA: return KEY_JUNJA; +// case VKEY_FINAL: return KEY_FINAL; +// case VKEY_HANJA: return KEY_HANJA; +// case VKEY_KANJI: return KEY_KANJI; + case VKEY_ESCAPE: return KEY_ESCAPE; +// case VKEY_CONVERT: return KEY_CONVERT; +// case VKEY_NONCONVERT: return KEY_NONCONVERT; +// case VKEY_ACCEPT: return KEY_ACCEPT; +// case VKEY_MODECHANGE: return KEY_MODECHANGE; +// case VKEY_PRIOR: return KEY_PRIOR; +// case VKEY_NEXT: return KEY_NEXT; + case VKEY_END: return KEY_END; + case VKEY_HOME: return KEY_HOME; + case VKEY_LEFT: return KEY_LEFT; + case VKEY_UP: return KEY_UP; + case VKEY_RIGHT: return KEY_RIGHT; + case VKEY_DOWN: return KEY_DOWN; +// case VKEY_SELECT: return KEY_SELECT; + case VKEY_PRINT: return KEY_PRINT; +// case VKEY_EXECUTE: return KEY_EXECUTE; +// case VKEY_SNAPSHOT: return KEY_SNAPSHOT; + case VKEY_INSERT: return KEY_INSERT; + case VKEY_DELETE: return KEY_DELETE; + case VKEY_HELP: return KEY_HELP; +// case VKEY_LWIN: return KEY_LWIN; +// case VKEY_RWIN: return KEY_RWIN; +// case VKEY_APPS: return KEY_APPS; +// case VKEY_SLEEP: return KEY_SLEEP; + case VKEY_NUMPAD0: return KEY_KP_0; + case VKEY_NUMPAD1: return KEY_KP_1; + case VKEY_NUMPAD2: return KEY_KP_2; + case VKEY_NUMPAD3: return KEY_KP_3; + case VKEY_NUMPAD4: return KEY_KP_4; + case VKEY_NUMPAD5: return KEY_KP_5; + case VKEY_NUMPAD6: return KEY_KP_6; + case VKEY_NUMPAD7: return KEY_KP_7; + case VKEY_NUMPAD8: return KEY_KP_8; + case VKEY_NUMPAD9: return KEY_KP_9; + case VKEY_MULTIPLY: return KEY_KP_MULTIPLY; + case VKEY_ADD: return KEY_KP_ADD; +// case VKEY_SEPARATOR: return KEY_SEPARATOR; + case VKEY_SUBTRACT: return KEY_KP_SUBSTRACT; + case VKEY_DECIMAL: return KEY_KP_PERIOD; + case VKEY_DIVIDE: return KEY_KP_DIVIDE; + case VKEY_F1: return KEY_F1; + case VKEY_F2: return KEY_F2; + case VKEY_F3: return KEY_F3; + case VKEY_F4: return KEY_F4; + case VKEY_F5: return KEY_F5; + case VKEY_F6: return KEY_F6; + case VKEY_F7: return KEY_F7; + case VKEY_F8: return KEY_F8; + case VKEY_F9: return KEY_F9; + case VKEY_F10: return KEY_F10; + case VKEY_F11: return KEY_F11; + case VKEY_F12: return KEY_F12; + case VKEY_F13: return KEY_F13; + case VKEY_F14: return KEY_F14; + case VKEY_F15: return KEY_F15; + case VKEY_F16: return KEY_F16; + /* + case VKEY_F17: return KEY_F17; + case VKEY_F18: return KEY_F18; + case VKEY_F19: return KEY_F19; + case VKEY_F20: return KEY_F20; + case VKEY_F21: return KEY_F21; + case VKEY_F22: return KEY_F22; + case VKEY_F23: return KEY_F23; + case VKEY_F24: return KEY_F24; + */ + case VKEY_NUMLOCK: return KEY_NUMLOCK; + case VKEY_SCROLL: return KEY_SCROLLLOCK; + case VKEY_LSHIFT: return KEY_SHIFT; + case VKEY_RSHIFT: return KEY_SHIFT; + case VKEY_LCONTROL: return KEY_CONTROL; + case VKEY_RCONTROL: return KEY_CONTROL; + case VKEY_LMENU: return KEY_MENU; + case VKEY_RMENU: return KEY_MENU; + case VKEY_BROWSER_BACK: return KEY_BACK; + case VKEY_BROWSER_FORWARD: return KEY_FORWARD; + case VKEY_BROWSER_REFRESH: return KEY_REFRESH; + case VKEY_BROWSER_STOP: return KEY_STOP; + case VKEY_BROWSER_SEARCH: return KEY_SEARCH; + case VKEY_BROWSER_FAVORITES: return KEY_FAVORITES; + case VKEY_BROWSER_HOME: return KEY_HOMEPAGE; + case VKEY_VOLUME_MUTE: return KEY_VOLUMEMUTE; + case VKEY_VOLUME_DOWN: return KEY_VOLUMEDOWN; + case VKEY_VOLUME_UP: return KEY_VOLUMEUP; + case VKEY_MEDIA_NEXT_TRACK: return KEY_MEDIANEXT; + case VKEY_MEDIA_PREV_TRACK: return KEY_MEDIAPREVIOUS; + case VKEY_MEDIA_STOP: return KEY_MEDIASTOP; + case VKEY_MEDIA_PLAY_PAUSE: return KEY_MEDIAPLAY; + case VKEY_MEDIA_LAUNCH_MAIL: return KEY_LAUNCHMAIL; + case VKEY_MEDIA_LAUNCH_MEDIA_SELECT: return KEY_LAUNCHMEDIA; // FUCKING USELESS KEYS, HOW DO THEY WORK? + case VKEY_MEDIA_LAUNCH_APP1: return KEY_LAUNCH0; + case VKEY_MEDIA_LAUNCH_APP2: return KEY_LAUNCH0; +// case VKEY_OEM_102: return KEY_OEM_102; +// case VKEY_PROCESSKEY: return KEY_PROCESSKEY; +// case VKEY_PACKET: return KEY_PACKET; +// case VKEY_ATTN: return KEY_ATTN; +// case VKEY_CRSEL: return KEY_CRSEL; +// case VKEY_EXSEL: return KEY_EXSEL; +// case VKEY_EREOF: return KEY_EREOF; +// case VKEY_PLAY: return KEY_PLAY; +// case VKEY_ZOOM: return KEY_ZOOM; +// case VKEY_NONAME: return KEY_NONAME; +// case VKEY_PA1: return KEY_PA1; +// case VKEY_OEM_CLEAR: return KEY_OEM_CLEAR; + + default: break; + }; + + is_char = true; + + switch (p_key) { + + case VKEY_SPACE: return KEY_SPACE; + case VKEY_0: return KEY_0; + case VKEY_1: return KEY_1; + case VKEY_2: return KEY_2; + case VKEY_3: return KEY_3; + case VKEY_4: return KEY_4; + case VKEY_5: return KEY_5; + case VKEY_6: return KEY_6; + case VKEY_7: return KEY_7; + case VKEY_8: return KEY_8; + case VKEY_9: return KEY_9; + case VKEY_A: return KEY_A; + case VKEY_B: return KEY_B; + case VKEY_C: return KEY_C; + case VKEY_D: return KEY_D; + case VKEY_E: return KEY_E; + case VKEY_F: return KEY_F; + case VKEY_G: return KEY_G; + case VKEY_H: return KEY_H; + case VKEY_I: return KEY_I; + case VKEY_J: return KEY_J; + case VKEY_K: return KEY_K; + case VKEY_L: return KEY_L; + case VKEY_M: return KEY_M; + case VKEY_N: return KEY_N; + case VKEY_O: return KEY_O; + case VKEY_P: return KEY_P; + case VKEY_Q: return KEY_Q; + case VKEY_R: return KEY_R; + case VKEY_S: return KEY_S; + case VKEY_T: return KEY_T; + case VKEY_U: return KEY_U; + case VKEY_V: return KEY_V; + case VKEY_W: return KEY_W; + case VKEY_X: return KEY_X; + case VKEY_Y: return KEY_Y; + case VKEY_Z: return KEY_Z; + /* + case VKEY_OEM_PLUS: return KEY_PLUS; + case VKEY_OEM_COMMA: return KEY_COMMA; + case VKEY_OEM_MINUS: return KEY_MINUS; + case VKEY_OEM_PERIOD: return KEY_PERIOD; + case VKEY_OEM_1: return KEY_OEM_1; + case VKEY_OEM_2: return KEY_OEM_2; + case VKEY_OEM_3: return KEY_OEM_3; + case VKEY_OEM_4: return KEY_OEM_4; + case VKEY_OEM_5: return KEY_OEM_5; + case VKEY_OEM_6: return KEY_OEM_6; + case VKEY_OEM_7: return KEY_OEM_7; + case VKEY_OEM_8: return KEY_OEM_8; + */ + default: break; + + }; + + return 0; +}; + +#endif // BASE_KEYBOARD_CODES_POSIX_H_ diff --git a/platform/nacl/opengl_context.cpp b/platform/nacl/opengl_context.cpp new file mode 100644 index 00000000000..8dafe959a30 --- /dev/null +++ b/platform/nacl/opengl_context.cpp @@ -0,0 +1,123 @@ +/*************************************************************************/ +/* opengl_context.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 "opengl_context.h" + +#include +#include "ppapi/gles2/gl2ext_ppapi.h" +#include "os_nacl.h" +#include "ppapi/cpp/instance.h" +#include "ppapi/cpp/module.h" +#include "ppapi/cpp/completion_callback.h" +#include "ppapi/utility/completion_callback_factory.h" + + +namespace { +// This is called by the brower when the 3D context has been flushed to the +// browser window. +void FlushCallback(void* data, int32_t result) { + static_cast(data)->set_flush_pending(false); + static_cast(data)->FlushContext(); +} +} // namespace + +OpenGLContext::OpenGLContext(pp::Instance* p_instance) + : pp::Graphics3DClient(p_instance), + flush_pending_(false) { + + instance = p_instance; + pp::Module* module = pp::Module::Get(); + assert(module); + gles2_interface_ = static_cast( + module->GetBrowserInterface(PPB_OPENGLES2_INTERFACE)); + assert(gles2_interface_); +} + +OpenGLContext::~OpenGLContext() { + glSetCurrentContextPPAPI(0); +} + +bool OpenGLContext::MakeContextCurrent(pp::Instance* instance) { + + if (instance == NULL) { + glSetCurrentContextPPAPI(0); + return false; + } + // Lazily create the Pepper context. + if (context_.is_null()) { + int32_t attribs[] = { + PP_GRAPHICS3DATTRIB_ALPHA_SIZE, 8, + PP_GRAPHICS3DATTRIB_DEPTH_SIZE, 24, + PP_GRAPHICS3DATTRIB_STENCIL_SIZE, 8, + PP_GRAPHICS3DATTRIB_SAMPLES, 0, + PP_GRAPHICS3DATTRIB_SAMPLE_BUFFERS, 0, + PP_GRAPHICS3DATTRIB_WIDTH, width, + PP_GRAPHICS3DATTRIB_HEIGHT, height, + PP_GRAPHICS3DATTRIB_NONE + }; + + context_ = pp::Graphics3D(instance, pp::Graphics3D(), attribs); + if (context_.is_null()) { + glSetCurrentContextPPAPI(0); + return false; + } + instance->BindGraphics(context_); + } + glSetCurrentContextPPAPI(context_.pp_resource()); + return true; +} + +void OpenGLContext::ResizeContext(const pp::Size& size) { + + width = size.width(); + height = size.height(); + + if (!context_.is_null()) { + context_.ResizeBuffers(size.width(), size.height()); + } +} + + +void OpenGLContext::InvalidateContext(pp::Instance* instance) { + glSetCurrentContextPPAPI(0); +} + +void OpenGLContext::FlushContext() { + if (flush_pending()) { + // A flush is pending so do nothing; just drop this flush on the floor. + return; + } + set_flush_pending(true); + + OSNacl* os = (OSNacl*)OS::get_singleton(); + MakeContextCurrent(instance); + os->iterate(); + + context_.SwapBuffers(pp::CompletionCallback(&FlushCallback, this)); +} + diff --git a/platform/nacl/opengl_context.h b/platform/nacl/opengl_context.h new file mode 100644 index 00000000000..b0431f29bfd --- /dev/null +++ b/platform/nacl/opengl_context.h @@ -0,0 +1,124 @@ +/*************************************************************************/ +/* opengl_context.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 EXAMPLES_TUMBLER_OPENGL_CONTEXT_H_ +#define EXAMPLES_TUMBLER_OPENGL_CONTEXT_H_ + +/// +/// @file +/// OpenGLContext manages the OpenGL context in the browser that is associated +/// with a @a pp::Instance instance. +/// + +#include + +#include +#include + +#include "ppapi/c/ppb_opengles2.h" +//#include "ppapi/cpp/dev/context_3d_dev.h" +#include "ppapi/cpp/graphics_3d_client.h" +#include "ppapi/cpp/graphics_3d.h" +//#include "ppapi/cpp/dev/surface_3d_dev.h" +#include "ppapi/cpp/instance.h" +#include "ppapi/cpp/size.h" + +// A convenience wrapper for a shared OpenGLContext pointer type. As other +// smart pointer types are needed, add them here. + +#include + +class OpenGLContext; + +typedef std::tr1::shared_ptr SharedOpenGLContext; + +/// OpenGLContext manages an OpenGL rendering context in the browser. +/// +class OpenGLContext : public pp::Graphics3DClient { + public: + explicit OpenGLContext(pp::Instance* instance); + + /// Release all the in-browser resources used by this context, and make this + /// context invalid. + virtual ~OpenGLContext(); + + /// The Graphics3DClient interfcace. + virtual void Graphics3DContextLost() { + assert(!"Unexpectedly lost graphics context"); + } + + /// Make @a this the current 3D context in @a instance. + /// @param instance The instance of the NaCl module that will receive the + /// the current 3D context. + /// @return success. + bool MakeContextCurrent(pp::Instance* instance); + + /// Flush the contents of this context to the browser's 3D device. + void FlushContext(); + + /// Make the underlying 3D device invalid, so that any subsequent rendering + /// commands will have no effect. The next call to MakeContextCurrent() will + /// cause the underlying 3D device to get rebound and start receiving + /// receiving rendering commands again. Use InvalidateContext(), for + /// example, when resizing the context's viewing area. + void InvalidateContext(pp::Instance* instance); + + void ResizeContext(const pp::Size& size); + + /// The OpenGL ES 2.0 interface. + const struct PPB_OpenGLES2_Dev* gles2() const { + return gles2_interface_; + } + + /// The PP_Resource needed to make GLES2 calls through the Pepper interface. + const PP_Resource gl_context() const { + return context_.pp_resource(); + } + + /// Indicate whether a flush is pending. This can only be called from the + /// main thread; it is not thread safe. + bool flush_pending() const { + return flush_pending_; + } + void set_flush_pending(bool flag) { + flush_pending_ = flag; + } + + private: + pp::Graphics3D context_; + bool flush_pending_; + + int width, height; + + pp::Instance* instance; + + const struct PPB_OpenGLES2_Dev* gles2_interface_; +}; + +#endif // EXAMPLES_TUMBLER_OPENGL_CONTEXT_H_ + diff --git a/platform/nacl/os_nacl.cpp b/platform/nacl/os_nacl.cpp new file mode 100644 index 00000000000..d97195c50d8 --- /dev/null +++ b/platform/nacl/os_nacl.cpp @@ -0,0 +1,529 @@ +/*************************************************************************/ +/* os_nacl.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 "os_nacl.h" + +#include "drivers/unix/memory_pool_static_malloc.h" +#include "os/memory_pool_dynamic_static.h" +#include "main/main.h" +#include +#include +#include +#include + +#include "io/file_access_memory.h" +#include "core/io/file_access_pack.h" +#include "scene/io/scene_loader.h" +#include "scene/main/scene_main_loop.h" + +#include "servers/visual/visual_server_raster.h" + +#include "drivers/gles2/rasterizer_gles2.h" +#include "nacl_keycodes.h" + +#include "core/globals.h" +#include "core/input_map.h" + +#include +#include + +#define UNIX_ENABLED +#include "drivers/unix/thread_posix.h" +#include "drivers/unix/semaphore_posix.h" +#include "drivers/unix/mutex_posix.h" + + +int OSNacl::get_video_driver_count() const { + + return 1; +}; +const char * OSNacl::get_video_driver_name(int p_driver) const { + + return "gles2"; +}; + +OS::VideoMode OSNacl::get_default_video_mode() const { + + return OS::VideoMode(800,600,false); +}; + +int OSNacl::get_audio_driver_count() const { + + return 1; +}; + +const char * OSNacl::get_audio_driver_name(int p_driver) const { + + return "nacl_audio"; +}; + +static MemoryPoolStaticMalloc *mempool_static=NULL; +static MemoryPoolDynamicStatic *mempool_dynamic=NULL; + +void OSNacl::initialize_core() { + + ticks_start=0; + ticks_start=get_ticks_usec(); +}; + +void OSNacl::initialize(const VideoMode& p_desired,int p_video_driver,int p_audio_driver) { + + rasterizer = memnew( RasterizerGLES2 ); + visual_server = memnew( VisualServerRaster(rasterizer) ); + visual_server->init(); + visual_server->cursor_set_visible(false, 0); + + audio_driver = memnew(AudioDriverNacl); + audio_driver->set_singleton(); + audio_driver->init(); + + sample_manager = memnew( SampleManagerMallocSW ); + audio_server = memnew( AudioServerSW(sample_manager) ); + audio_server->set_mixer_params(AudioMixerSW::INTERPOLATION_LINEAR,false); + audio_server->init(); + + spatial_sound_server = memnew( SpatialSoundServerSW ); + spatial_sound_server->init(); + + spatial_sound_2d_server = memnew( SpatialSound2DServerSW ); + spatial_sound_2d_server->init(); + + // + physics_server = memnew( PhysicsServerSW ); + physics_server->init(); + + physics_2d_server = memnew( Physics2DServerSW ); + physics_2d_server->init(); + + input = memnew(InputDefault); +}; + +void OSNacl::set_main_loop( MainLoop * p_main_loop ) { + + main_loop = p_main_loop; + input->set_main_loop(p_main_loop); + main_loop->init(); +}; + +void OSNacl::delete_main_loop() { + + if (main_loop) + memdelete(main_loop); +}; + +void OSNacl::finalize() { + + +}; +void OSNacl::finalize_core() { + + if (mempool_dynamic) + memdelete( mempool_dynamic ); + if (mempool_static) + delete mempool_static; + +}; + +void OSNacl::alert(const String& p_alert,const String& p_title) { + + fprintf(stderr,"ERROR: %s\n",p_alert.utf8().get_data()); +}; + +void OSNacl::vprint(const char* p_format, va_list p_list, bool p_strerr) { + + vprintf(p_format,p_list); + fflush(stdout); +} + + +String OSNacl::get_stdin_string(bool p_block) { + + char buff[1024]; + return fgets(buff,1024,stdin); +}; + +void OSNacl::set_mouse_show(bool p_show) { + +}; + +void OSNacl::set_mouse_grab(bool p_grab) { + +}; + +bool OSNacl::is_mouse_grab_enabled() const { + + return false; +}; + +int OSNacl::get_mouse_button_state() const { + + return mouse_mask; +}; + + +Point2 OSNacl::get_mouse_pos() const { + + return Point2(); +}; + +void OSNacl::set_window_title(const String& p_title) { +}; + +void OSNacl::set_video_mode(const VideoMode& p_video_mode, int p_screen) { + + video_mode = p_video_mode; +}; + +OS::VideoMode OSNacl::get_video_mode(int p_screen) const { + + return video_mode; +}; + +void OSNacl::get_fullscreen_mode_list(List *p_list,int p_screen) const { + +}; + +Error OSNacl::execute(const String& p_path, const List& p_arguments,bool p_blocking, OS::ProcessID *r_child_id, String* r_pipe, int *r_exitcode) { + + return ERR_UNAVAILABLE; +}; + +Error OSNacl::kill(const ProcessID& p_pid) { + + return ERR_UNAVAILABLE; +}; + +bool OSNacl::has_environment(const String& p_var) const { + + return getenv(p_var.utf8().get_data())!=NULL; +}; + +String OSNacl::get_environment(const String& p_var) const { + + if (getenv(p_var.utf8().get_data())) + return getenv(p_var.utf8().get_data()); + return ""; +}; + +String OSNacl::get_name() { + + return "NaCl"; +}; + +MainLoop *OSNacl::get_main_loop() const { + + return main_loop; +}; + +OS::Date OSNacl::get_date() const { + + time_t t=time(NULL); + struct tm *lt=localtime(&t); + Date ret; + ret.year=lt->tm_year; + ret.month=(Month)lt->tm_mon; + ret.day=lt->tm_mday; + ret.weekday=(Weekday)lt->tm_wday; + ret.dst=lt->tm_isdst; + + return ret; +}; + +OS::Time OSNacl::get_time() const { + + time_t t=time(NULL); + struct tm *lt=localtime(&t); + Time ret; + ret.hour=lt->tm_hour; + ret.min=lt->tm_min; + ret.sec=lt->tm_sec; + return ret; +}; + +void OSNacl::delay_usec(uint32_t p_usec) const { + + //usleep(p_usec); +}; + +uint64_t OSNacl::get_ticks_usec() const { + + struct timeval tv_now; + gettimeofday(&tv_now,NULL); + + uint64_t longtime = (uint64_t)tv_now.tv_usec + (uint64_t)tv_now.tv_sec*1000000L; + longtime-=ticks_start; + + return longtime; +}; + +bool OSNacl::can_draw() const { + + return minimized != true; +}; + +void OSNacl::queue_event(const InputEvent& p_event) { + + ERR_FAIL_INDEX( event_count, MAX_EVENTS ); + + event_queue[event_count++] = p_event; +}; + +void OSNacl::add_package(String p_name, Vector p_data) { + + FileAccessMemory::register_file(p_name, p_data); + FileAccess::make_default(FileAccess::ACCESS_RESOURCES); + FileAccess::make_default(FileAccess::ACCESS_USERDATA); + FileAccess::make_default(FileAccess::ACCESS_FILESYSTEM); + + if (!PackedData::get_singleton()) + memnew(PackedData); + + printf("adding package %ls, %x\n", p_name.c_str(), PackedData::get_singleton()); + PackedData::get_singleton()->set_disabled(true); + PackedData::get_singleton()->add_pack(p_name); + PackedData::get_singleton()->set_disabled(false); + printf("added\n"); +}; + +void OSNacl::set_cursor_shape(CursorShape p_shape) { + + +}; + +String OSNacl::get_resource_dir() const { + + return "."; +}; + +static int mouse_button(int p_nacl_but) { + + switch (p_nacl_but) { + + case PP_INPUTEVENT_MOUSEBUTTON_LEFT: + return BUTTON_LEFT; + case PP_INPUTEVENT_MOUSEBUTTON_MIDDLE: + return BUTTON_MIDDLE; + case PP_INPUTEVENT_MOUSEBUTTON_RIGHT: + return BUTTON_RIGHT; + }; + + return 0; +}; + +static InputModifierState modifier(uint32_t p_mod) { + + InputModifierState mod_mask; + + mod_mask.shift = p_mod & PP_INPUTEVENT_MODIFIER_SHIFTKEY; + mod_mask.alt = p_mod & PP_INPUTEVENT_MODIFIER_ALTKEY; + mod_mask.control = p_mod & PP_INPUTEVENT_MODIFIER_CONTROLKEY; + mod_mask.meta = p_mod & PP_INPUTEVENT_MODIFIER_METAKEY; + + return mod_mask; +}; + + + +void OSNacl::handle_event(const pp::InputEvent& p_event) { + + int type = p_event.GetType(); + switch (type) { + + case PP_INPUTEVENT_TYPE_MOUSEDOWN: + case PP_INPUTEVENT_TYPE_MOUSEUP: + case PP_INPUTEVENT_TYPE_WHEEL: { + + InputEvent event; + event.ID=++event_id; + event.type = InputEvent::MOUSE_BUTTON; + event.device=0; + + pp::MouseInputEvent mevent(p_event); + if (type == PP_INPUTEVENT_TYPE_WHEEL) { + + pp::WheelInputEvent wevent(p_event);; + float ticks = wevent.GetTicks().y(); + if (ticks == 0) + break; // whut? + + event.mouse_button.pressed = true; + event.mouse_button.button_index = ticks > 0 ? BUTTON_WHEEL_UP : BUTTON_WHEEL_DOWN; + event.mouse_button.doubleclick = false; + + } else { + + event.mouse_button.pressed = (type == PP_INPUTEVENT_TYPE_MOUSEDOWN); + event.mouse_button.button_index = mouse_button(mevent.GetButton()); + event.mouse_button.doubleclick = (mevent.GetClickCount() % 2) == 0; + + mouse_mask &= ~(1<< (event.mouse_button.button_index - 1)); + mouse_mask |= (event.mouse_button.pressed << (event.mouse_button.button_index - 1)); + }; + pp::Point pos = mevent.GetPosition(); + event.mouse_button.button_mask = mouse_mask; + event.mouse_button.global_x = pos.x(); + event.mouse_button.x = pos.x(); + event.mouse_button.global_y = pos.y(); + event.mouse_button.y = pos.y(); + event.mouse_button.pointer_index = 0; + event.mouse_button.mod = modifier(p_event.GetModifiers()); + queue_event(event); + + } break; + + case PP_INPUTEVENT_TYPE_MOUSEMOVE: { + + pp::MouseInputEvent mevent(p_event); + pp::Point pos = mevent.GetPosition(); + + InputEvent event; + event.ID=++event_id; + event.type = InputEvent::MOUSE_MOTION; + event.mouse_motion.pointer_index = 0; + event.mouse_motion.global_x = pos.x(); + event.mouse_motion.global_y = pos.y(); + event.mouse_motion.x = pos.x(); + event.mouse_motion.y = pos.y(); + event.mouse_motion.button_mask = mouse_mask; + event.mouse_motion.mod = modifier(p_event.GetModifiers()); + + event.mouse_motion.relative_x = pos.x() - mouse_last_x; + event.mouse_motion.relative_y = pos.y() - mouse_last_y; + mouse_last_x = pos.x(); + mouse_last_y = pos.y(); + + queue_event(event); + + } break; + + case PP_INPUTEVENT_TYPE_RAWKEYDOWN: + case PP_INPUTEVENT_TYPE_KEYDOWN: + case PP_INPUTEVENT_TYPE_KEYUP: { + + pp::KeyboardInputEvent kevent(p_event); + bool is_char; + uint32_t key = godot_key(kevent.GetKeyCode(), is_char); + if (type != PP_INPUTEVENT_TYPE_KEYUP && is_char) { + + last_scancode = key; + break; + }; + + InputEvent event; + event.ID=++event_id; + event.type = InputEvent::KEY; + event.key.pressed = (type != PP_INPUTEVENT_TYPE_KEYUP); + event.key.scancode = key; + event.key.unicode = key; + + event.key.echo = p_event.GetModifiers() & PP_INPUTEVENT_MODIFIER_ISAUTOREPEAT; + event.key.mod = modifier(p_event.GetModifiers()); + queue_event(event); + } break; + + case PP_INPUTEVENT_TYPE_CHAR: { + + pp::KeyboardInputEvent kevent(p_event); + InputEvent event; + event.ID = ++event_id; + event.type = InputEvent::KEY; + event.key.pressed = true; + event.key.scancode = last_scancode; + event.key.unicode = kevent.GetCharacterText().AsString().c_str()[0]; + event.key.mod = modifier(p_event.GetModifiers()); + event.key.echo = p_event.GetModifiers() & PP_INPUTEVENT_MODIFIER_ISAUTOREPEAT; + queue_event(event); + + } break; + + /* + case NPEventType_Minimize: { + + minimized = p_event->u.minimize.value == 1; + + } break; + + + case NPEventType_Focus: { + + if (p_event->u.focus.value == 1) { + main_loop->notification(MainLoop::NOTIFICATION_WM_FOCUS_IN); + } else { + main_loop->notification(MainLoop::NOTIFICATION_WM_FOCUS_OUT); + }; + } break; + + */ + + default: + ; + }; +}; + +bool OSNacl::iterate() { + + if (!main_loop) { + event_count = 0; + return true; + }; + + for (int i=0; iparse_input_event(event_queue[i]); + }; + + event_count = 0; + + return Main::iteration(); +}; + + +OSNacl::OSNacl() { + + main_loop=NULL; + mempool_dynamic = NULL; + mempool_static = NULL; + mouse_last_x = 0; + mouse_last_y = 0; + event_count = 0; + event_id = 0; + mouse_mask = 0; + video_mode = get_default_video_mode(); + last_scancode = 0; + minimized = false; + + ThreadPosix::make_default(); + SemaphorePosix::make_default(); + MutexPosix::make_default(); + mempool_static = new MemoryPoolStaticMalloc; + mempool_dynamic = memnew( MemoryPoolDynamicStatic ); +}; + +OSNacl::~OSNacl() { + +}; diff --git a/platform/nacl/os_nacl.h b/platform/nacl/os_nacl.h new file mode 100644 index 00000000000..b8173ef61c8 --- /dev/null +++ b/platform/nacl/os_nacl.h @@ -0,0 +1,156 @@ +/*************************************************************************/ +/* os_nacl.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 OS_NACL_H +#define OS_NACL_H + +#include "core/os/os.h" + +#include "servers/visual_server.h" +#include "servers/visual/rasterizer.h" +#include "servers/physics/physics_server_sw.h" +#include "servers/spatial_sound/spatial_sound_server_sw.h" +#include "servers/spatial_sound_2d/spatial_sound_2d_server_sw.h" +#include "servers/audio/audio_server_sw.h" +#include "servers/physics_2d/physics_2d_server_sw.h" +#include "audio_driver_nacl.h" +#include "os/input.h" + + +#include + +class OSNacl : OS { + + uint64_t ticks_start; + +protected: + + enum { + MAX_EVENTS = 64, + }; + + MainLoop *main_loop; + + Rasterizer *rasterizer; + VisualServer *visual_server; + PhysicsServer *physics_server; + SpatialSoundServerSW *spatial_sound_server; + + AudioServerSW *audio_server; + SampleManagerMallocSW *sample_manager; + SpatialSound2DServerSW *spatial_sound_2d_server; + Physics2DServer *physics_2d_server; + AudioDriverNacl* audio_driver; + + + // functions used by main to initialize/deintialize the OS + virtual int get_video_driver_count() const; + virtual const char * get_video_driver_name(int p_driver) const; + + virtual VideoMode get_default_video_mode() const; + + virtual int get_audio_driver_count() const; + virtual const char * get_audio_driver_name(int p_driver) const; + + void vprint(const char* p_format, va_list p_list, bool p_stderr); + + virtual void initialize_core(); + virtual void initialize(const VideoMode& p_desired,int p_video_driver,int p_audio_driver); + + virtual void set_main_loop( MainLoop * p_main_loop ); + virtual void delete_main_loop(); + + virtual void finalize(); + virtual void finalize_core(); + + int mouse_last_x, mouse_last_y; + + InputEvent event_queue[MAX_EVENTS]; + int event_count; + void queue_event(const InputEvent& p_event); + + int event_id; + uint32_t mouse_mask; + + uint32_t last_scancode; + + bool minimized; + + VideoMode video_mode; + + InputDefault *input; + +public: + + void add_package(String p_name, Vector p_data); + + void handle_event(const pp::InputEvent& p_event); + + virtual void alert(const String& p_alert,const String& p_title); + virtual String get_stdin_string(bool p_block); + + virtual void set_mouse_show(bool p_show); + virtual void set_mouse_grab(bool p_grab); + virtual bool is_mouse_grab_enabled() const; + virtual Point2 get_mouse_pos() const; + virtual int get_mouse_button_state() const; + virtual void set_window_title(const String& p_title); + + virtual void set_video_mode(const VideoMode& p_video_mode,int p_screen); + virtual VideoMode get_video_mode(int p_screen) const; + virtual void get_fullscreen_mode_list(List *p_list,int p_screen) const; + + virtual Error execute(const String& p_path, const List& p_arguments,bool p_blocking,ProcessID *r_child_id=NULL,String* r_pipe=NULL,int *r_exitcode=NULL); + virtual Error kill(const ProcessID& p_pid); + + virtual bool has_environment(const String& p_var) const; + virtual String get_environment(const String& p_var) const; + + virtual void set_cursor_shape(CursorShape p_shape); + + virtual String get_name(); + + virtual MainLoop *get_main_loop() const; + + virtual Date get_date() const; + virtual Time get_time() const; + + virtual void delay_usec(uint32_t p_usec) const; + virtual uint64_t get_ticks_usec() const; + + virtual String get_resource_dir() const; + + virtual bool can_draw() const; + + bool iterate(); + + OSNacl(); + ~OSNacl(); +}; + +#endif // OS_NACL_H diff --git a/platform/nacl/pepper_main.cpp b/platform/nacl/pepper_main.cpp new file mode 100644 index 00000000000..6a1f4acbe7e --- /dev/null +++ b/platform/nacl/pepper_main.cpp @@ -0,0 +1,541 @@ +/*************************************************************************/ +/* pepper_main.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. */ +/*************************************************************************/ + IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + ("Apple") in consideration of your agreement to the following terms, and + your use, installation, modification or redistribution of this Apple software + constitutes acceptance of these terms. If you do not agree with these terms, + please do not use, install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive license, + under Apple's copyrights in this original Apple software + (the "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following text and + disclaimers in all such redistributions of the Apple Software. Neither the + name, trademarks, service marks or logos of Apple Computer, Inc. may be used + to endorse or promote products derived from the Apple Software without + specific prior written permission from Apple. Except as expressly stated in + this notice, no other rights or licenses, express or implied, are granted by + Apple herein, including but not limited to any patent rights that may be + infringed by your derivative works or by other works in which the Apple + Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION + AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER + THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR + OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include "os_nacl.h" +#include + +#include +#include +#include +#include + +static NPNetscapeFuncs kBrowserFuncs = { 0 }; +static NPNetscapeFuncs* browser = &kBrowserFuncs; + +static NPDevice* device3d_ = NULL; +static PGLContext pgl_context_; +static NPDeviceContext3D context3d_; +static int width_; +static int height_; + +extern int nacl_main(int argc, char** argn, char** argv); +extern void nacl_cleanup(); + +NPExtensions* extensions = NULL; +static NPP npp_; + +const int32_t kCommandBufferSize = 1024 * 1024; + +// Plugin entry points +extern "C" { + +// Plugin entry points + +// Entrypoints ----------------------------------------------------------------- + +NPError NP_GetEntryPoints(NPPluginFuncs* plugin_funcs) { + plugin_funcs->version = 11; + plugin_funcs->size = sizeof(plugin_funcs); + plugin_funcs->newp = NPP_New; + plugin_funcs->destroy = NPP_Destroy; + plugin_funcs->setwindow = NPP_SetWindow; + plugin_funcs->newstream = NPP_NewStream; + plugin_funcs->destroystream = NPP_DestroyStream; + plugin_funcs->asfile = NPP_StreamAsFile; + plugin_funcs->writeready = NPP_WriteReady; + plugin_funcs->write = (NPP_WriteUPP)NPP_Write; + plugin_funcs->print = NPP_Print; + plugin_funcs->event = NPP_HandleEvent; + plugin_funcs->urlnotify = NPP_URLNotify; + plugin_funcs->getvalue = NPP_GetValue; + plugin_funcs->setvalue = NPP_SetValue; + + return NPERR_NO_ERROR; +} + +NPError NP_Shutdown() { + pglTerminate(); + return NPERR_NO_ERROR; +} + +NPError NP_GetValue(void* instance, NPPVariable variable, void* value); +char* NP_GetMIMEDescription(); + +NPError NP_Initialize(NPNetscapeFuncs* browser_funcs, + NPPluginFuncs* plugin_funcs) { + printf("NPP_Initialize\n"); + memcpy(&kBrowserFuncs, browser_funcs, sizeof(kBrowserFuncs)); + pglInitialize(); + return NP_GetEntryPoints(plugin_funcs); +} + + +} // extern "C" + +void Initialize3D() { + // Initialize a 3D context. + NPDeviceContext3DConfig config; + config.commandBufferSize = kCommandBufferSize; + NPError err = device3d_->initializeContext(npp_, &config, &context3d_); + if (err != NPERR_NO_ERROR) { + printf("Failed to initialize 3D context\n"); + exit(1); + } + + // Create a PGL context. + pgl_context_ = pglCreateContext(npp_, device3d_, &context3d_); + + // Initialize the demo GL state. + //pglMakeCurrent(pgl_context_); + //GLFromCPPInit(); + //pglMakeCurrent(NULL); +} + +NPError NPP_New(NPMIMEType pluginType, + NPP instance, + uint16_t mode, + int16_t argc, char* argn[], char* argv[], + NPSavedData* saved) { + printf("NPP_New\n"); + if (browser->version >= 14) { + + npp_ = instance; + if (!extensions) { + browser->getvalue(npp_, NPNVPepperExtensions, + reinterpret_cast(&extensions)); + // CHECK(extensions); + } + + printf("%s: %i\n", __FUNCTION__, __LINE__); + + device3d_ = extensions->acquireDevice(npp_, NPPepper3DDevice); + if (device3d_ == NULL) { + printf("Failed to acquire 3DDevice\n"); + exit(1); + } + printf("%s: %i\n", __FUNCTION__, __LINE__); + + /* + deviceaudio_ = extensions->acquireDevice(npp_, NPPepperAudioDevice); + if (deviceaudio_ == NULL) { + printf("Failed to acquire AudioDevice\n"); + exit(1); + } + */ + Initialize3D(); + pglMakeCurrent(pgl_context_); + nacl_main(argc, argn, argv); + pglMakeCurrent(NULL); + }; + + return NPERR_NO_ERROR; +} + +NPError NPP_Destroy(NPP instance, NPSavedData** save) { + + nacl_cleanup(); + + return NPERR_NO_ERROR; +} + +void Destroy3D() { + printf("destroy 3d\n"); + // Destroy the PGL context. + pglDestroyContext(pgl_context_); + pgl_context_ = NULL; + + // Destroy the Device3D context. + device3d_->destroyContext(npp_, &context3d_); +} + +static void iteration(void* data) { + + (void)data; + OSNacl* os = (OSNacl*)OS::get_singleton(); + + if (!pglMakeCurrent(pgl_context_) && pglGetError() == PGL_CONTEXT_LOST) { + printf("******* Lost context! :O\n"); + Destroy3D(); + Initialize3D(); + pglMakeCurrent(pgl_context_); + } + + glViewport(0, 0, width_, height_); + + os->iterate(); + + pglSwapBuffers(); + pglMakeCurrent(NULL); + + browser->pluginthreadasynccall(npp_, iteration, NULL); +}; + +NPError NPP_SetWindow(NPP instance, NPWindow* window) { + + width_ = window->width; + height_ = window->height; + + if (!pgl_context_) + Initialize3D(); + + // Schedule the first call to Draw. + OSNacl* os = (OSNacl*)OS::get_singleton(); + OS::VideoMode vm; + vm.width = width_; + vm.height = height_; + vm.resizable = false; + vm.fullscreen = false; + os->set_video_mode(vm); + + browser->pluginthreadasynccall(npp_, iteration, NULL); + + return NPERR_NO_ERROR; +} + +NPError NPP_NewStream(NPP instance, + NPMIMEType type, + NPStream* stream, + NPBool seekable, + uint16_t* stype) { + *stype = NP_ASFILEONLY; + return NPERR_NO_ERROR; +} + +NPError NPP_DestroyStream(NPP instance, NPStream* stream, NPReason reason) { + return NPERR_NO_ERROR; +} + +void NPP_StreamAsFile(NPP instance, NPStream* stream, const char* fname) { +} + +int32_t NPP_Write(NPP instance, + NPStream* stream, + int32_t offset, + int32_t len, + void* buffer) { + return 0; +} + +int32_t NPP_WriteReady(NPP instance, NPStream* stream) { + return 0; +} + +void NPP_Print(NPP instance, NPPrint* platformPrint) { +} + +int16_t NPP_HandleEvent(NPP instance, void* event) { + + OSNacl* os = (OSNacl*)OS::get_singleton(); + os->handle_event(event); + return 1; +} + +void NPP_URLNotify(NPP instance, + const char* url, + NPReason reason, + void* notify_data) { + // PluginObject* obj = static_cast(instance->pdata); +} + +static NPObject* Allocate(NPP npp, NPClass* npclass) { + return new NPObject; +} + +static void Deallocate(NPObject* object) { + delete object; +} + +// Return |true| if |method_name| is a recognized method. +static bool HasMethod(NPObject* obj, NPIdentifier method_name) { + + char *name = NPN_UTF8FromIdentifier(method_name); + bool is_method = false; + if (strcmp((const char *)name, "start_package") == 0) { + is_method = true; + } else if (strcmp((const char*)name, "add_package_chunk") == 0) { + is_method = true; + } else if (strcmp((const char*)name, "end_package") == 0) { + is_method = true; + } else if (strcmp((const char*)name, "start_scene") == 0) { + is_method = true; + } + NPN_MemFree(name); + return is_method; +} + +// I don't know what this is +static bool InvokeDefault(NPObject *obj, const NPVariant *args, + uint32_t argCount, NPVariant *result) { + if (result) { + NULL_TO_NPVARIANT(*result); + } + return true; +} + +static uint8_t* mem = NULL; +static int pkg_size = 0; +static String pkgname; + +static bool variant_is_number(const NPVariant& v) { + + switch (v.type) { + + case NPVariantType_Int32: + case NPVariantType_Double: + return true; + + default: + return false; + } + return false; +}; + +static double variant_as_number(const NPVariant& v) { + + switch (v.type) { + + case NPVariantType_Int32: + return (double)v.value.intValue; + case NPVariantType_Double: + return (double)v.value.doubleValue; + default: + return 0; + } + + return 0; +}; + +// Invoke() is called by the browser to invoke a function object whose name +// is |method_name|. +static bool Invoke(NPObject* obj, + NPIdentifier method_name, + const NPVariant *args, + uint32_t arg_count, + NPVariant *result) { + NULL_TO_NPVARIANT(*result); + char *name = NPN_UTF8FromIdentifier(method_name); + if (name == NULL) + return false; + bool rval = false; + + OSNacl* os = (OSNacl*)OS::get_singleton(); + + if (strcmp(name, "start_package") == 0) { + + printf("arg count is %i\n", arg_count); + for (int i=0; iadd_package(pkgname, mem, pkg_size); + return true; + }; + + + if (strcmp(name, "start_scene") == 0) { +printf("start_scene!\n"); + if (arg_count != 1) { + return false; + }; + + if (args[0].type != NPVariantType_String) return false; +printf("calling with param %s\n", args[0].value.stringValue.UTF8Characters); + + printf("pepper iteration\n"); + if (!pglMakeCurrent(pgl_context_) && pglGetError() == PGL_CONTEXT_LOST) { + printf("******* Lost context! :O\n"); + Destroy3D(); + Initialize3D(); + pglMakeCurrent(pgl_context_); + } + os->start_scene(String::utf8(args[0].value.stringValue.UTF8Characters)); + pglSwapBuffers(); + pglMakeCurrent(NULL); + +printf("returning true\n"); + return true; + }; + + NPN_MemFree(name); + + return rval; +} + + +static NPClass GodotClass = { + NP_CLASS_STRUCT_VERSION, + Allocate, + Deallocate, + NULL, // Invalidate is not implemented + HasMethod, + Invoke, + InvokeDefault, + NULL, // HasProperty is not implemented + NULL, // GetProperty is not implemented + NULL, // SetProperty is not implemented +}; + +static NPObject* npobject = NULL; + +NPError NPP_GetValue(NPP instance, NPPVariable variable, void* value) { + NPError err = NPERR_NO_ERROR; + + switch (variable) { + case NPPVpluginNameString: + *(reinterpret_cast(value)) = "Pepper Test PlugIn"; + break; + case NPPVpluginDescriptionString: + *(reinterpret_cast(value)) = + "Simple Pepper plug-in for manual testing."; + break; + case NPPVpluginNeedsXEmbed: + *(reinterpret_cast(value)) = 1; + break; + case NPPVpluginScriptableNPObject: { + if (npobject == NULL) { + npobject = NPN_CreateObject(instance, &GodotClass); + } else { + NPN_RetainObject(npobject); + }; + void** v = reinterpret_cast(value); + *v = npobject; + } break; + default: + fprintf(stderr, "Unhandled variable to NPP_GetValue\n"); + err = NPERR_GENERIC_ERROR; + break; + } + + return err; +} + +NPError NPP_SetValue(NPP instance, NPNVariable variable, void* value) { + return NPERR_GENERIC_ERROR; +} + +NPError NP_GetValue(void* instance, NPPVariable variable, void* value) { + return NPP_GetValue(reinterpret_cast(instance), variable, value); +} + +char* NP_GetMIMEDescription() { + return const_cast("pepper-application/x-pepper-test-plugin;"); +} diff --git a/platform/nacl/platform_config.h b/platform/nacl/platform_config.h new file mode 100644 index 00000000000..38fc934ae46 --- /dev/null +++ b/platform/nacl/platform_config.h @@ -0,0 +1,29 @@ +/*************************************************************************/ +/* platform_config.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. */ +/*************************************************************************/ +#include diff --git a/platform/osx/SCsub b/platform/osx/SCsub new file mode 100644 index 00000000000..d7839d7d657 --- /dev/null +++ b/platform/osx/SCsub @@ -0,0 +1,11 @@ +Import('env') + +files = [ + 'os_osx.mm', + 'godot_main_osx.mm', + 'audio_driver_osx.cpp', + 'sem_osx.cpp', +# 'context_gl_osx.cpp', + ] + +env.Program('#bin/godot_osx',files) diff --git a/platform/osx/audio_driver_osx.cpp b/platform/osx/audio_driver_osx.cpp new file mode 100644 index 00000000000..8f28e8ff63d --- /dev/null +++ b/platform/osx/audio_driver_osx.cpp @@ -0,0 +1,160 @@ +/*************************************************************************/ +/* audio_driver_osx.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. */ +/*************************************************************************/ +#ifdef OSX_ENABLED + +#include "audio_driver_osx.h" + +Error AudioDriverOSX::init() { + + active = false; + channels = 2; + + AudioStreamBasicDescription strdesc; + strdesc.mFormatID = kAudioFormatLinearPCM; + strdesc.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked; + strdesc.mChannelsPerFrame = channels; + strdesc.mSampleRate = 44100; + strdesc.mFramesPerPacket = 1; + strdesc.mBitsPerChannel = 16; + strdesc.mBytesPerFrame = + strdesc.mBitsPerChannel * strdesc.mChannelsPerFrame / 8; + strdesc.mBytesPerPacket = + strdesc.mBytesPerFrame * strdesc.mFramesPerPacket; + + OSStatus result = noErr; + AURenderCallbackStruct callback; + AudioComponentDescription desc; + AudioComponent comp = NULL; + const AudioUnitElement output_bus = 0; + const AudioUnitElement bus = output_bus; + const AudioUnitScope scope = kAudioUnitScope_Input; + + zeromem(&desc, sizeof(desc)); + desc.componentType = kAudioUnitType_Output; + desc.componentSubType = 0; /* !!! FIXME: ? */ + comp = AudioComponentFindNext(NULL, &desc); + desc.componentManufacturer = kAudioUnitManufacturer_Apple; + + result = AudioComponentInstanceNew(comp, &audio_unit); + ERR_FAIL_COND_V(result != noErr, FAILED); + ERR_FAIL_COND_V(comp == NULL, FAILED); + + result = AudioUnitSetProperty(audio_unit, + kAudioUnitProperty_StreamFormat, + scope, bus, &strdesc, sizeof(strdesc)); + ERR_FAIL_COND_V(result != noErr, FAILED); + + zeromem(&callback, sizeof(AURenderCallbackStruct)); + callback.inputProc = &AudioDriverOSX::output_callback; + callback.inputProcRefCon = this; + result = AudioUnitSetProperty(audio_unit, + kAudioUnitProperty_SetRenderCallback, + scope, bus, &callback, sizeof(callback)); + ERR_FAIL_COND_V(result != noErr, FAILED); + + result = AudioUnitInitialize(audio_unit); + ERR_FAIL_COND_V(result != noErr, FAILED); + + result = AudioOutputUnitStart(audio_unit); + ERR_FAIL_COND_V(result != noErr, FAILED); + + const int samples = 1024; + samples_in = memnew_arr(int32_t, samples); // whatever + buffer_frames = samples / channels; + + return OK; +}; + +OSStatus AudioDriverOSX::output_callback(void *inRefCon, + AudioUnitRenderActionFlags * ioActionFlags, + const AudioTimeStamp * inTimeStamp, + UInt32 inBusNumber, UInt32 inNumberFrames, + AudioBufferList * ioData) { + + + AudioBuffer *abuf; + AudioDriverOSX* ad = (AudioDriverOSX*)inRefCon; + + if (!ad->active) { + for (unsigned int i = 0; i < ioData->mNumberBuffers; i++) { + abuf = &ioData->mBuffers[i]; + zeromem(abuf->mData, abuf->mDataByteSize); + }; + return 0; + }; + + int frames_left; + + for (unsigned int i = 0; i < ioData->mNumberBuffers; i++) { + + abuf = &ioData->mBuffers[i]; + frames_left = inNumberFrames; + int16_t* out = (int16_t*)abuf->mData; + + while (frames_left) { + + int frames = MIN(frames_left, ad->buffer_frames); + ad->lock(); + ad->audio_server_process(frames, ad->samples_in); + ad->unlock(); + + for(int i = 0; i < frames * ad->channels; i++) { + + out[i] = ad->samples_in[i]>>16; + } + + frames_left -= frames; + out += frames * ad->channels; + }; + }; + + return 0; +}; + +void AudioDriverOSX::start() { + active = true; +}; + +int AudioDriverOSX::get_mix_rate() const { + return 44100; +}; + +AudioDriverSW::OutputFormat AudioDriverOSX::get_output_format() const { + return OUTPUT_STEREO; +}; + +void AudioDriverOSX::lock() {}; +void AudioDriverOSX::unlock() {}; + +void AudioDriverOSX::finish() { + + memdelete_arr(samples_in); +}; + +#endif diff --git a/platform/osx/audio_driver_osx.h b/platform/osx/audio_driver_osx.h new file mode 100644 index 00000000000..daa388fb860 --- /dev/null +++ b/platform/osx/audio_driver_osx.h @@ -0,0 +1,71 @@ +/*************************************************************************/ +/* audio_driver_osx.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. */ +/*************************************************************************/ +#ifdef OSX_ENABLED + +#ifndef AUDIO_DRIVER_OSX_H +#define AUDIO_DRIVER_OSX_H + +#include "servers/audio/audio_server_sw.h" + +#include + +class AudioDriverOSX : public AudioDriverSW { + + AudioComponentInstance audio_unit; + bool active; + + int channels; + int32_t* samples_in; + int buffer_frames; + + static OSStatus output_callback(void *inRefCon, + AudioUnitRenderActionFlags * ioActionFlags, + const AudioTimeStamp * inTimeStamp, + UInt32 inBusNumber, UInt32 inNumberFrames, + AudioBufferList * ioData); + + +public: + + const char* get_name() const { + return "AudioUnit"; + }; + + virtual Error init(); + virtual void start(); + virtual int get_mix_rate() const; + virtual OutputFormat get_output_format() const; + virtual void lock(); + virtual void unlock(); + virtual void finish(); +}; + +#endif + +#endif diff --git a/platform/osx/context_gl_osx.cpp b/platform/osx/context_gl_osx.cpp new file mode 100644 index 00000000000..9c2cd16b492 --- /dev/null +++ b/platform/osx/context_gl_osx.cpp @@ -0,0 +1,104 @@ +/*************************************************************************/ +/* context_gl_osx.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 "context_gl_osx.h" + +#ifdef OSX_ENABLED +#if defined(OPENGL_ENABLED) || defined(LEGACYGL_ENABLED) +#include +#include +#include + +#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092 + +void ContextGL_OSX::release_current() { + + aglSetCurrentContext( context ); +} + +void ContextGL_OSX::make_current() { + + aglSetCurrentContext( NULL ); +} +void ContextGL_OSX::swap_buffers() { + + aglSwapBuffers( context ); +} + +Error ContextGL_OSX::initialize() { + + if ( (Ptr) kUnresolvedCFragSymbolAddress == (Ptr) aglChoosePixelFormat ) + return FAILED; + + GLint attributes[] = { AGL_RGBA, + AGL_DOUBLEBUFFER, + AGL_DEPTH_SIZE, 32, + AGL_NO_RECOVERY, + AGL_NONE, + AGL_NONE }; + + AGLPixelFormat format = NULL; + + format = aglChoosePixelFormat( NULL, 0, attributes ); + + if ( !format ) + return FAILED; + + context = aglCreateContext( format, 0 ); + + if ( !context ) + return FAILED; + + aglDestroyPixelFormat( format ); + + aglSetWindowRef( context, window ); + + GLint swapInterval = 1; + aglSetInteger( context, AGL_SWAP_INTERVAL, &swapInterval ); + + aglSetCurrentContext( context ); + + return OK; +} + +ContextGL_OSX::ContextGL_OSX(WindowRef p_window) { + + window = p_window; +} + + +ContextGL_OSX::~ContextGL_OSX() { + + if (context) + aglDestroyContext(context); +} + + +#endif +#endif diff --git a/platform/osx/context_gl_osx.h b/platform/osx/context_gl_osx.h new file mode 100644 index 00000000000..54da42b3316 --- /dev/null +++ b/platform/osx/context_gl_osx.h @@ -0,0 +1,65 @@ +/*************************************************************************/ +/* context_gl_osx.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 CONTEXT_GL_OSX_H +#define CONTEXT_GL_OSX_H + +/** + @author Juan Linietsky +*/ +#ifdef OSX_ENABLED + +#if defined(OPENGL_ENABLED) || defined(LEGACYGL_ENABLED) + +#include "os/os.h" +#include "drivers/gl_context/context_gl.h" +#include +#include + +class ContextGL_OSX : public ContextGL { + + AGLContext context; + WindowRef window; + +public: + + virtual void release_current(); + virtual void make_current(); + virtual void swap_buffers(); + + virtual Error initialize(); + + ContextGL_OSX(WindowRef window); + ~ContextGL_OSX(); + +}; + +#endif + +#endif +#endif diff --git a/platform/osx/detect.py b/platform/osx/detect.py new file mode 100644 index 00000000000..5337416074e --- /dev/null +++ b/platform/osx/detect.py @@ -0,0 +1,107 @@ + +import os +import sys + + +def is_active(): + return True + +def get_name(): + return "OSX" + +def can_build(): + + if (sys.platform != "darwin"): + return False + + return True # osx enabled + +def get_opts(): + + return [ + ('force_64_bits','Force 64 bits binary','no'), + + ] + +def get_flags(): + + return [ + ('opengl', 'no'), + ('legacygl', 'yes'), + ('builtin_zlib', 'no'), + ('freetype','builtin'), #use builtin freetype + ] + + + +def configure(env): + + env.Append(CPPPATH=['#platform/osx']) + + if (env["tools"]=="no"): + #no tools suffix + env['OBJSUFFIX'] = ".nt"+env['OBJSUFFIX'] + env['LIBSUFFIX'] = ".nt"+env['LIBSUFFIX'] + + if (env["target"]=="release"): + + env.Append(CCFLAGS=['-O2','-ffast-math','-fomit-frame-pointer','-ftree-vectorize','-msse2']) + env['OBJSUFFIX'] = "_opt"+env['OBJSUFFIX'] + env['LIBSUFFIX'] = "_opt"+env['LIBSUFFIX'] + + elif (env["target"]=="release_debug"): + + env.Append(CCFLAGS=['-O2','-DDEBUG_ENABLED']) + env['OBJSUFFIX'] = "_optd"+env['OBJSUFFIX'] + env['LIBSUFFIX'] = "_optd"+env['LIBSUFFIX'] + + elif (env["target"]=="debug"): + + env.Append(CCFLAGS=['-g3', '-Wall','-DDEBUG_ENABLED','-DDEBUG_MEMORY_ENABLED']) + + + elif (env["target"]=="profile"): + env.Append(CCFLAGS=['-g','-pg']) + env.Append(LINKFLAGS=['-pg']) + + if (env["freetype"]!="no"): + env.Append(CCFLAGS=['-DFREETYPE_ENABLED']) + env.Append(CPPPATH=['#tools/freetype']) + env.Append(CPPPATH=['#tools/freetype/freetype/include']) + + if (env["force_64_bits"]!="no"): + env.Append(CCFLAGS=['-arch', 'x86_64']) + env.Append(LINKFLAGS=['-arch', 'x86_64']) + env['OBJSUFFIX'] = ".64"+env['OBJSUFFIX'] + env['LIBSUFFIX'] = ".64"+env['LIBSUFFIX'] + else: + env.Append(CCFLAGS=['-arch', 'i386']) + env.Append(LINKFLAGS=['-arch', 'i386']) + +# env.Append(CPPPATH=['#platform/osx/include/freetype2', '#platform/osx/include']) +# env.Append(LIBPATH=['#platform/osx/lib']) + + + #if env['opengl'] == 'yes': + # env.Append(CPPFLAGS=['-DOPENGL_ENABLED','-DGLEW_ENABLED']) + + env.Append(CPPFLAGS=["-DAPPLE_STYLE_KEYS"]) + env.Append(CPPFLAGS=['-DUNIX_ENABLED','-DGLES2_ENABLED','-DGLEW_ENABLED', '-DOSX_ENABLED']) + env.Append(LIBS=['pthread']) + #env.Append(CPPFLAGS=['-F/Developer/SDKs/MacOSX10.4u.sdk/System/Library/Frameworks', '-isysroot', '/Developer/SDKs/MacOSX10.4u.sdk', '-mmacosx-version-min=10.4']) + #env.Append(LINKFLAGS=['-mmacosx-version-min=10.4', '-isysroot', '/Developer/SDKs/MacOSX10.4u.sdk', '-Wl,-syslibroot,/Developer/SDKs/MacOSX10.4u.sdk']) + env.Append(LINKFLAGS=['-framework', 'Cocoa', '-framework', 'OpenGL', '-framework', 'AGL', '-framework', 'AudioUnit','-lz']) + + if (env["CXX"]=="clang++"): + env.Append(CPPFLAGS=['-DTYPED_METHOD_BIND']) + env["CC"]="clang" + env["LD"]="clang++" + + import methods + + env.Append( BUILDERS = { 'GLSL120' : env.Builder(action = methods.build_legacygl_headers, suffix = 'glsl.h',src_suffix = '.glsl') } ) + env.Append( BUILDERS = { 'GLSL' : env.Builder(action = methods.build_glsl_headers, suffix = 'glsl.h',src_suffix = '.glsl') } ) + env.Append( BUILDERS = { 'GLSL120GLES' : env.Builder(action = methods.build_gles2_headers, suffix = 'glsl.h',src_suffix = '.glsl') } ) + #env.Append( BUILDERS = { 'HLSL9' : env.Builder(action = methods.build_hlsl_dx9_headers, suffix = 'hlsl.h',src_suffix = '.hlsl') } ) + + diff --git a/platform/osx/export/export.cpp b/platform/osx/export/export.cpp new file mode 100644 index 00000000000..f55e9017946 --- /dev/null +++ b/platform/osx/export/export.cpp @@ -0,0 +1,504 @@ +#include "version.h" +#include "export.h" +#include "tools/editor/editor_settings.h" +#include "tools/editor/editor_import_export.h" +#include "tools/editor/editor_node.h" +#include "io/zip_io.h" +#include "io/marshalls.h" +#include "io/resource_saver.h" +#include "globals.h" +#include "os/file_access.h" +#include "os/os.h" +#include "platform/osx/logo.h" +#include "string.h" + + +class EditorExportPlatformOSX : public EditorExportPlatform { + + OBJ_TYPE( EditorExportPlatformOSX,EditorExportPlatform ); + + String custom_release_package; + String custom_debug_package; + + + int version_code; + + String app_name; + String info; + String icon; + String identifier; + String short_version; + String version; + String signature; + String copyright; + bool use64; + bool high_resolution; + + Ref logo; + + void _fix_plist(Vector& plist, const String &p_binary); + void _make_icon(const Image& p_icon,Vector& data); + + +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 *p_list) const; + +public: + + virtual String get_name() const { return "Mac OSX"; } + virtual ImageCompression get_image_compression() const { return IMAGE_COMPRESSION_BC; } + virtual Ref get_logo() const { return logo; } + + + virtual bool poll_devices() { return false;} + virtual int get_device_count() const { return 0; }; + virtual String get_device_name(int p_device) const { return String(); } + virtual String get_device_info(int p_device) const { return String(); } + virtual Error run(int p_device); + + virtual bool requieres_password(bool p_debug) const { return false; } + virtual String get_binary_extension() const { return "zip"; } + virtual Error export_project(const String& p_path,bool p_debug,const String& p_password=""); + + virtual bool can_export(String *r_error=NULL) const; + + EditorExportPlatformOSX(); + ~EditorExportPlatformOSX(); +}; + +bool EditorExportPlatformOSX::_set(const StringName& p_name, const Variant& p_value) { + + String n=p_name; + + if (n=="custom_package/debug") + custom_debug_package=p_value; + else if (n=="custom_package/release") + custom_release_package=p_value; + else if (n=="application/name") + app_name=p_value; + else if (n=="application/info") + info=p_value; + else if (n=="application/icon") + icon=p_value; + else if (n=="application/identifier") + identifier=p_value; + else if (n=="application/signature") + signature=p_value; + else if (n=="application/short_version") + short_version=p_value; + else if (n=="application/version") + version=p_value; + else if (n=="application/copyright") + copyright=p_value; + else if (n=="application/64_bits") + use64=p_value; + else if (n=="display/high_res") + high_resolution=p_value; + else + return false; + + return true; +} + +bool EditorExportPlatformOSX::_get(const StringName& p_name,Variant &r_ret) const{ + + String n=p_name; + + if (n=="custom_package/debug") + r_ret=custom_debug_package; + else if (n=="custom_package/release") + r_ret=custom_release_package; + else if (n=="application/name") + r_ret=app_name; + else if (n=="application/info") + r_ret=info; + else if (n=="application/icon") + r_ret=icon; + else if (n=="application/identifier") + r_ret=identifier; + else if (n=="application/signature") + r_ret=signature; + else if (n=="application/short_version") + r_ret=short_version; + else if (n=="application/version") + r_ret=version; + else if (n=="application/copyright") + r_ret=copyright; + else if (n=="application/64_bits") + r_ret=use64; + else if (n=="display/high_res") + r_ret=high_resolution; + else + return false; + + return true; +} +void EditorExportPlatformOSX::_get_property_list( List *p_list) const{ + + p_list->push_back( PropertyInfo( Variant::STRING, "custom_package/debug", PROPERTY_HINT_FILE,"zip")); + p_list->push_back( PropertyInfo( Variant::STRING, "custom_package/release", PROPERTY_HINT_FILE,"zip")); + + p_list->push_back( PropertyInfo( Variant::STRING, "application/name") ); + p_list->push_back( PropertyInfo( Variant::STRING, "application/info") ); + p_list->push_back( PropertyInfo( Variant::STRING, "application/icon",PROPERTY_HINT_FILE,"png") ); + p_list->push_back( PropertyInfo( Variant::STRING, "application/identifier") ); + p_list->push_back( PropertyInfo( Variant::STRING, "application/signature") ); + p_list->push_back( PropertyInfo( Variant::STRING, "application/short_version") ); + p_list->push_back( PropertyInfo( Variant::STRING, "application/version") ); + p_list->push_back( PropertyInfo( Variant::STRING, "application/copyright") ); + p_list->push_back( PropertyInfo( Variant::BOOL, "application/64_bits") ); + p_list->push_back( PropertyInfo( Variant::BOOL, "display/high_res") ); + + + //p_list->push_back( PropertyInfo( Variant::INT, "resources/pack_mode", PROPERTY_HINT_ENUM,"Copy,Single Exec.,Pack (.pck),Bundles (Optical)")); + +} + +void EditorExportPlatformOSX::_make_icon(const Image& p_icon,Vector& icon) { + + + Ref it = memnew( ImageTexture ); + int size=512; + + Vector data; + + data.resize(8); + data[0]='i'; + data[1]='c'; + data[2]='n'; + data[3]='s'; + + const char *name[]={"ic09","ic08","ic07","icp6","icp5","icp4"}; + int index=0; + + while(size>=16) { + + Image copy = p_icon; + copy.convert(Image::FORMAT_RGBA); + copy.resize(size,size); + it->create_from_image(copy); + String path = EditorSettings::get_singleton()->get_settings_path()+"/tmp/icon.png"; + ResourceSaver::save(path,it); + + FileAccess *f = FileAccess::open(path,FileAccess::READ); + ERR_FAIL_COND(!f); + + int ofs = data.size(); + uint32_t len = f->get_len(); + data.resize(data.size()+len+8); + f->get_buffer(&data[ofs+8],len); + memdelete(f); + len+=8; + len=BSWAP32(len); + copymem(&data[ofs],name[index],4); + encode_uint32(len,&data[ofs+4]); + index++; + size/=2; + } + + uint32_t total_len = data.size(); + total_len = BSWAP32(total_len); + encode_uint32(total_len,&data[4]); + + icon=data; +} + + +void EditorExportPlatformOSX::_fix_plist(Vector& plist,const String& p_binary) { + + + String str; + String strnew; + str.parse_utf8((const char*)plist.ptr(),plist.size()); + Vector lines=str.split("\n"); + for(int i=0;i":"")+"\n"; + } else { + strnew+=lines[i]+"\n"; + } + } + + CharString cs = strnew.utf8(); + plist.resize(cs.size()); + for(int i=9;iget_settings_path()+"/templates/osx.zip"; + + if (p_debug) { + + src_pkg=custom_debug_package!=""?custom_debug_package:pkg_path; + } else { + + src_pkg=custom_release_package!=""?custom_release_package:pkg_path; + + } + + + FileAccess *src_f=NULL; + zlib_filefunc_def io = zipio_create_io_from_file(&src_f); + + ep.step("Creating app",0); + + unzFile pkg = unzOpen2(src_pkg.utf8().get_data(), &io); + if (!pkg) { + + EditorNode::add_io_error("Could not find template app to export:\n"+src_pkg); + return ERR_FILE_NOT_FOUND; + } + + ERR_FAIL_COND_V(!pkg, ERR_CANT_OPEN); + int ret = unzGoToFirstFile(pkg); + + zlib_filefunc_def io2=io; + FileAccess *dst_f=NULL; + io2.opaque=&dst_f; + zipFile dpkg=zipOpen2(p_path.utf8().get_data(),APPEND_STATUS_CREATE,NULL,&io2); + + String binary_to_use="godot_osx_"+String(p_debug?"debug":"release")+"."+String(use64?"64":"32"); + + print_line("binary: "+binary_to_use); + String pkg_name; + if (app_name!="") + pkg_name=app_name; + else if (String(Globals::get_singleton()->get("application/name"))!="") + pkg_name=String(Globals::get_singleton()->get("application/name")); + else + pkg_name="Unnamed"; + + + while(ret==UNZ_OK) { + + //get filename + unz_file_info info; + char fname[16384]; + ret = unzGetCurrentFileInfo(pkg,&info,fname,16384,NULL,0,NULL,0); + + String file=fname; + + print_line("READ: "+file); + Vector data; + data.resize(info.uncompressed_size); + + //read + unzOpenCurrentFile(pkg); + unzReadCurrentFile(pkg,data.ptr(),data.size()); + unzCloseCurrentFile(pkg); + + //write + + file = file.replace_first("osx_template.app/",""); + + + if (file=="Contents/Info.plist") { + print_line("parse plist"); + _fix_plist(data,pkg_name); + } + + if (file.begins_with("Contents/MacOS/godot_")) { + if (file!="Contents/MacOS/"+binary_to_use) { + ret = unzGoToNextFile(pkg); + continue; //ignore! + } + file="Contents/MacOS/"+pkg_name; + } + + if (file=="Contents/Resources/icon.icns") { + //see if there is an icon + String iconpath = Globals::get_singleton()->get("application/icon"); + print_line("icon? "+iconpath); + if (iconpath!="") { + Image icon; + icon.load(iconpath); + if (!icon.empty()) { + print_line("loaded?"); + _make_icon(icon,data); + } + } + //bleh? + } + + file=pkg_name+".app/"+file; + + if (data.size()>0) { + print_line("ADDING: "+file+" size: "+itos(data.size())); + + zip_fileinfo fi; + fi.tmz_date.tm_hour=info.tmu_date.tm_hour; + fi.tmz_date.tm_min=info.tmu_date.tm_min; + fi.tmz_date.tm_sec=info.tmu_date.tm_sec; + fi.tmz_date.tm_mon=info.tmu_date.tm_mon; + fi.tmz_date.tm_mday=info.tmu_date.tm_mday; + fi.tmz_date.tm_year=info.tmu_date.tm_year; + fi.dosDate=info.dosDate; + fi.internal_fa=info.internal_fa; + fi.external_fa=info.external_fa; + + int err = zipOpenNewFileInZip(dpkg, + file.utf8().get_data(), + &fi, + NULL, + 0, + NULL, + 0, + NULL, + Z_DEFLATED, + Z_DEFAULT_COMPRESSION); + + print_line("OPEN ERR: "+itos(err)); + err = zipWriteInFileInZip(dpkg,data.ptr(),data.size()); + print_line("WRITE ERR: "+itos(err)); + zipCloseFileInZip(dpkg); + } + + ret = unzGoToNextFile(pkg); + } + + + ep.step("Making PKG",1); + + String pack_path=EditorSettings::get_singleton()->get_settings_path()+"/tmp/data.pck"; + FileAccess *pfs = FileAccess::open(pack_path,FileAccess::WRITE); + Error err = save_pack(pfs); + memdelete(pfs); + + if (err) { + zipClose(dpkg,NULL); + unzClose(pkg); + return err; + + } + + { + //write datapack + + int err = zipOpenNewFileInZip(dpkg, + (pkg_name+".app/Contents/Resources/data.pck").utf8().get_data(), + NULL, + NULL, + 0, + NULL, + 0, + NULL, + Z_DEFLATED, + Z_DEFAULT_COMPRESSION); + + + FileAccess *pf = FileAccess::open(pack_path,FileAccess::READ); + ERR_FAIL_COND_V(!pf,ERR_CANT_OPEN); + const int BSIZE = 16384; + uint8_t buf[BSIZE]; + + while(true) { + + int r = pf->get_buffer(buf,BSIZE); + if (r<=0) + break; + zipWriteInFileInZip(dpkg,buf,r); + + } + zipCloseFileInZip(dpkg); + memdelete(pf); + + } + + zipClose(dpkg,NULL); + unzClose(pkg); + + return OK; +} + + +Error EditorExportPlatformOSX::run(int p_device) { + + return OK; +} + + +EditorExportPlatformOSX::EditorExportPlatformOSX() { + + Image img( _osx_logo ); + logo = Ref( memnew( ImageTexture )); + logo->create_from_image(img); + + info="This Game is Nice"; + identifier="com.godot.macgame"; + signature="godotmacgame"; + short_version="1.0"; + version="1.0"; + use64=false; + high_resolution=false; + +} + +bool EditorExportPlatformOSX::can_export(String *r_error) const { + + + bool valid=true; + String err; + String exe_path = EditorSettings::get_singleton()->get_settings_path()+"/templates/"; + + if (!FileAccess::exists(exe_path+"osx.zip")) { + valid=false; + err+="No export templates found.\nDownload and install export templates.\n"; + } + + if (custom_debug_package!="" && !FileAccess::exists(custom_debug_package)) { + valid=false; + err+="Custom debug package not found.\n"; + } + + if (custom_release_package!="" && !FileAccess::exists(custom_release_package)) { + valid=false; + err+="Custom release package not found.\n"; + } + + if (r_error) + *r_error=err; + + return valid; +} + + +EditorExportPlatformOSX::~EditorExportPlatformOSX() { + +} + + +void register_osx_exporter() { + + + Ref exporter = Ref( memnew(EditorExportPlatformOSX) ); + EditorImportExport::get_singleton()->add_export_platform(exporter); + + +} + diff --git a/platform/osx/export/export.h b/platform/osx/export/export.h new file mode 100644 index 00000000000..b149e482c97 --- /dev/null +++ b/platform/osx/export/export.h @@ -0,0 +1,3 @@ + + +void register_osx_exporter(); diff --git a/platform/osx/godot_main_osx.mm b/platform/osx/godot_main_osx.mm new file mode 100644 index 00000000000..3546c1e71ac --- /dev/null +++ b/platform/osx/godot_main_osx.mm @@ -0,0 +1,86 @@ +/*************************************************************************/ +/* godot_main_osx.mm */ +/*************************************************************************/ +/* 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 "os_osx.h" +#include "main/main.h" + +#include +#include + +//#define main godot_main + +int main(int argc, char** argv) { + + + + if (argc>=1 && argv[0][0]=='/') { + //potentially launched from finder + int len = strlen(argv[0]); + while (len--) { + if (argv[0][len] == '/') break; + } + if (len>=0) { + char *path = (char *)malloc(len+1); + memcpy(path, argv[0], len); + path[len]=0; + + char *pathinfo = (char*)malloc(strlen(path)+strlen("/../Info.plist")+1); + //in real code you would check for errors in malloc here + strcpy(pathinfo, path); + strcat(pathinfo, "/../Info.plist"); + + FILE*f=fopen(pathinfo,"rb"); + if (f) { + //running from app bundle, as Info.plist was found + fclose(f); + chdir(path); + chdir("../Resources"); //data.pck, or just the files are here + + } + + free(path); + free(pathinfo); + } + + + + } + + OS_OSX os; + + + Error err = Main::setup(argv[0],argc-1,&argv[1]); + if (err!=OK) + return 255; + + if (Main::start()) + os.run(); // it is actually the OS that decides how to run + Main::cleanup(); + + return 0; +}; diff --git a/platform/osx/godot_osx.h b/platform/osx/godot_osx.h new file mode 100644 index 00000000000..ebac475089b --- /dev/null +++ b/platform/osx/godot_osx.h @@ -0,0 +1,37 @@ +/*************************************************************************/ +/* godot_osx.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 GODOT_OSX_H +#define GODOT_OSX_H + +#import + +@interface GodotMain : NSObject +@end + +#endif diff --git a/platform/osx/godot_osx.mm b/platform/osx/godot_osx.mm new file mode 100644 index 00000000000..fb3c5808c7e --- /dev/null +++ b/platform/osx/godot_osx.mm @@ -0,0 +1,215 @@ +/*************************************************************************/ +/* godot_osx.mm */ +/*************************************************************************/ +/* 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 /* for MAXPATHLEN */ +#include +#include "godot_osx.h" + +/* For some reaon, Apple removed setAppleMenu from the headers in 10.4, + but the method still is there and works. To avoid warnings, we declare + it ourselves here. */ +@interface NSApplication() +- (void)setAppleMenu:(NSMenu *)menu; +@end + +static int global_argc; +static char **global_argv; +static BOOL gCalledAppMainline = FALSE; + +static NSString *getApplicationName(void) +{ + const NSDictionary *dict; + NSString *appName = 0; + + /* Determine the application name */ + dict = (const NSDictionary *)CFBundleGetInfoDictionary(CFBundleGetMainBundle()); + if (dict) + appName = [dict objectForKey: @"CFBundleName"]; + + if (![appName length]) + appName = [[NSProcessInfo processInfo] processName]; + + return appName; +} + +/* The main class of the application, the application's delegate */ +@implementation GodotMain + +static void setApplicationMenu(void) +{ + /* warning: this code is very odd */ + NSMenu *appleMenu; + NSMenuItem *menuItem; + NSString *title; + NSString *appName; + + appName = getApplicationName(); + appleMenu = [[NSMenu alloc] initWithTitle:@""]; + + /* Add menu items */ + title = [@"About " stringByAppendingString:appName]; + [appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""]; + + [appleMenu addItem:[NSMenuItem separatorItem]]; + + title = [@"Hide " stringByAppendingString:appName]; + [appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"]; + + menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"]; + [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)]; + + [appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""]; + + [appleMenu addItem:[NSMenuItem separatorItem]]; + + title = [@"Quit " stringByAppendingString:appName]; + [appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"]; + + + /* Put menu into the menubar */ + menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""]; + [menuItem setSubmenu:appleMenu]; + [[NSApp mainMenu] addItem:menuItem]; + + /* Tell the application object that this is now the application menu */ + [NSApp setAppleMenu:appleMenu]; + + /* Finally give up our references to the objects */ + [appleMenu release]; + [menuItem release]; +} + +/* Create a window menu */ +static void setupWindowMenu(void) +{ + NSMenu *windowMenu; + NSMenuItem *windowMenuItem; + NSMenuItem *menuItem; + + windowMenu = [[NSMenu alloc] initWithTitle:@"Window"]; + + /* "Minimize" item */ + menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"]; + [windowMenu addItem:menuItem]; + [menuItem release]; + + /* Put menu into the menubar */ + windowMenuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""]; + [windowMenuItem setSubmenu:windowMenu]; + [[NSApp mainMenu] addItem:windowMenuItem]; + + /* Tell the application object that this is now the window menu */ + [NSApp setWindowsMenu:windowMenu]; + + /* Finally give up our references to the objects */ + [windowMenu release]; + [windowMenuItem release]; +} + +/* Replacement for NSApplicationMain */ +static void CustomApplicationMain (int argc, char **argv) +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + GodotMain *main; + + /* Ensure the application object is initialised */ + [NSApplication sharedApplication]; + + /* Set up the menubar */ + [NSApp setMainMenu:[[NSMenu alloc] init]]; + setApplicationMenu(); + setupWindowMenu(); + + main = [[main alloc] init]; + [NSApp setDelegate:main]; + + /* Start the main event loop */ + [NSApp run]; + + [main release]; + [pool release]; +} + +extern int godot_main(int argc, char** argv); + +/* Called when the internal event loop has just started running */ +- (void) applicationDidFinishLaunching: (NSNotification *) note +{ + int status; + + /* Hand off to main application code */ + gCalledAppMainline = TRUE; + + int ret = godot_main(global_argc, global_argv); + + exit(ret); +} +@end + +#ifdef main +# undef main +#endif + + +int main (int argc, char **argv) +{ + /* Copy the arguments into a global variable */ + /* This is passed if we are launched by double-clicking */ + if ( argc >= 2 && strncmp (argv[1], "-psn", 4) == 0 ) { + global_argv = (char **) malloc(sizeof (char *) * 2); + global_argv[0] = argv[0]; + global_argv[1] = NULL; + global_argc = 1; + + // chdir to binary's dir when launched from finder + int len = strlen(global_argv[0]); + + while (len--){ + if (global_argv[0][len] == '/') break; + } + + if (len>=0) { + char *path = (char *)malloc(len+1); + memcpy(path, global_argv[0], len); + path[len]=0; + printf("Path: %s\n", path); + chdir(path); + } + + } else { + int i; + global_argc = argc; + global_argv = (char **) malloc(sizeof (char *) * (argc+1)); + for (i = 0; i <= argc; i++) + global_argv[i] = argv[i]; + } + + CustomApplicationMain (argc, argv); + return 0; +} + diff --git a/platform/osx/logo.png b/platform/osx/logo.png new file mode 100644 index 00000000000..2bcd3aa72e7 Binary files /dev/null and b/platform/osx/logo.png differ diff --git a/platform/osx/os_osx.h b/platform/osx/os_osx.h new file mode 100644 index 00000000000..b9e381d6ec5 --- /dev/null +++ b/platform/osx/os_osx.h @@ -0,0 +1,166 @@ +/*************************************************************************/ +/* os_osx.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 OS_OSX_H +#define OS_OSX_H + + +#include "os/input.h" +#include "drivers/unix/os_unix.h" + +#include "servers/visual_server.h" +#include "servers/visual/visual_server_wrap_mt.h" +#include "servers/visual/rasterizer.h" +#include "servers/physics_server.h" +#include "servers/audio/audio_server_sw.h" +#include "servers/audio/sample_manager_sw.h" +#include "servers/spatial_sound/spatial_sound_server_sw.h" +#include "servers/spatial_sound_2d/spatial_sound_2d_server_sw.h" +#include "drivers/rtaudio/audio_driver_rtaudio.h" +#include "drivers/alsa/audio_driver_alsa.h" +#include "servers/physics_2d/physics_2d_server_sw.h" +#include "platform/osx/audio_driver_osx.h" +#include + +//bitch +#undef CursorShape +/** + @author Juan Linietsky +*/ + +class OS_OSX : public OS_Unix { +public: + bool force_quit; + Rasterizer *rasterizer; + VisualServer *visual_server; + VideoMode current_videomode; + List args; + MainLoop *main_loop; + unsigned int event_id; + + PhysicsServer *physics_server; + Physics2DServer *physics_2d_server; + + IP_Unix *ip_unix; + + AudioDriverOSX audio_driver_osx; + AudioServerSW *audio_server; + SampleManagerMallocSW *sample_manager; + SpatialSoundServerSW *spatial_sound_server; + SpatialSound2DServerSW *spatial_sound_2d_server; + + InputDefault *input; + + /* objc */ + + CGEventSourceRef eventSource; + + void process_events(); + + void* framework; +// pthread_key_t current; + bool mouse_grab; + Point2 mouse_pos; + uint32_t last_id; + + id delegate; + id window_delegate; + id window_object; + id window_view; + id autoreleasePool; + id cursor; + id pixelFormat; + id context; + + CursorShape cursor_shape; + +protected: + + virtual int get_video_driver_count() const; + virtual const char * get_video_driver_name(int p_driver) const; + virtual VideoMode get_default_video_mode() const; + + virtual void initialize_core(); + virtual void initialize(const VideoMode& p_desired,int p_video_driver,int p_audio_driver); + virtual void finalize(); + + virtual void set_main_loop( MainLoop * p_main_loop ); + virtual void delete_main_loop(); + + +public: + + + + + + + static OS_OSX* singleton; + + virtual String get_name(); + + virtual void set_cursor_shape(CursorShape p_shape); + + virtual void set_mouse_show(bool p_show); + virtual void set_mouse_grab(bool p_grab); + virtual bool is_mouse_grab_enabled() const; + virtual Point2 get_mouse_pos() const; + virtual int get_mouse_button_state() const; + virtual void set_window_title(const String& p_title); + + virtual void set_icon(const Image& p_icon); + + virtual MainLoop *get_main_loop() const; + + virtual bool can_draw() const; + + virtual void set_clipboard(const String& p_text); + virtual String get_clipboard() const; + + virtual void release_rendering_thread(); + virtual void make_rendering_thread(); + virtual void swap_buffers(); + + Error shell_open(String p_uri); + void push_input(const InputEvent& p_event); + + virtual void set_video_mode(const VideoMode& p_video_mode,int p_screen=0); + virtual VideoMode get_video_mode(int p_screen=0) const; + virtual void get_fullscreen_mode_list(List *p_list,int p_screen=0) const; + + virtual String get_executable_path() const; + + virtual void move_window_to_foreground(); + + void run(); + + + OS_OSX(); +}; + +#endif diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm new file mode 100644 index 00000000000..86d1dbb4c27 --- /dev/null +++ b/platform/osx/os_osx.mm @@ -0,0 +1,1323 @@ +/*************************************************************************/ +/* os_osx.mm */ +/*************************************************************************/ +/* 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. */ +/*************************************************************************/ +#import +#include +#include +#include +#include + +#include "sem_osx.h" +#include "servers/visual/visual_server_raster.h" +//#include "drivers/opengl/rasterizer_gl.h" +//#include "drivers/gles2/rasterizer_gles2.h" +#include "os_osx.h" +#include +#include +#include "print_string.h" +#include "servers/physics/physics_server_sw.h" +#include "drivers/gles2/rasterizer_instance_gles2.h" +#include "servers/visual/visual_server_wrap_mt.h" +#include "main/main.h" +#include "os/keyboard.h" + +#include +#include +#include +#include +#include +//uses portions of glfw + +//======================================================================== +// GLFW 3.0 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2010 Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +static NSRect convertRectToBacking(NSRect contentRect) { + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 + if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6) + return [OS_OSX::singleton->window_view convertRectToBacking:contentRect]; + else +#endif /*MAC_OS_X_VERSION_MAX_ALLOWED*/ + return contentRect; + +} + +static InputModifierState translateFlags(NSUInteger flags) +{ + InputModifierState mod; + + + mod.shift = (flags & NSShiftKeyMask); + mod.control = (flags & NSControlKeyMask); + mod.alt = (flags & NSAlternateKeyMask); + mod.meta = (flags & NSCommandKeyMask); + + return mod; +} + +static int mouse_x=0; +static int mouse_y=0; +static int prev_mouse_x=0; +static int prev_mouse_y=0; +static int button_mask=0; + + +@interface GodotApplication : NSApplication +@end + +@implementation GodotApplication + +// From http://cocoadev.com/index.pl?GameKeyboardHandlingAlmost +// This works around an AppKit bug, where key up events while holding +// down the command key don't get sent to the key window. +- (void)sendEvent:(NSEvent *)event +{ + if ([event type] == NSKeyUp && ([event modifierFlags] & NSCommandKeyMask)) + [[self keyWindow] sendEvent:event]; + else + [super sendEvent:event]; +} + +@end + +@interface GodotApplicationDelegate : NSObject +@end + +@implementation GodotApplicationDelegate + +- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender +{ +/* _Godotwindow* window; + + for (window = _Godot.windowListHead; window; window = window->next) + _GodotInputWindowCloseRequest(window); +*/ + return NSTerminateCancel; +} + +- (void)applicationDidHide:(NSNotification *)notification +{ + /* _Godotwindow* window; + + for (window = _Godot.windowListHead; window; window = window->next) + _GodotInputWindowVisibility(window, GL_FALSE); + */ +} + +- (void)applicationDidUnhide:(NSNotification *)notification +{ + /* + _Godotwindow* window; + + for (window = _Godot.windowListHead; window; window = window->next) + { + if ([window_object isVisible]) + _GodotInputWindowVisibility(window, GL_TRUE); + } + */ +} + +- (void)applicationDidChangeScreenParameters:(NSNotification *) notification +{ + //_GodotInputMonitorChange(); +} + +@end + +@interface GodotWindowDelegate : NSObject +{ + // _Godotwindow* window; +} + +@end + +@implementation GodotWindowDelegate + + +- (BOOL)windowShouldClose:(id)sender +{ + //_GodotInputWindowCloseRequest(window); + if (OS_OSX::singleton->get_main_loop()) + OS_OSX::singleton->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_QUIT_REQUEST); + return NO; +} + + + + +- (void)windowDidResize:(NSNotification *)notification +{ + [OS_OSX::singleton->context update]; + + const NSRect contentRect = [OS_OSX::singleton->window_view frame]; + const NSRect fbRect = convertRectToBacking(contentRect); + + OS_OSX::singleton->current_videomode.width=fbRect.size.width; + OS_OSX::singleton->current_videomode.height=fbRect.size.height; + + + // _GodotInputFramebufferSize(window, fbRect.size.width, fbRect.size.height); + // _GodotInputWindowSize(window, contentRect.size.width, contentRect.size.height); + //_GodotInputWindowDamage(window); + + //if (window->cursorMode == Godot_CURSOR_DISABLED) + // centerCursor(window); +} + +- (void)windowDidMove:(NSNotification *)notification +{ + // [window->nsgl.context update]; + + // int x, y; + // _GodotPlatformGetWindowPos(window, &x, &y); + // _GodotInputWindowPos(window, x, y); + + //if (window->cursorMode == Godot_CURSOR_DISABLED) + // centerCursor(window); +} + +- (void)windowDidMiniaturize:(NSNotification *)notification +{ + // _GodotInputWindowIconify(window, GL_TRUE); +} + +- (void)windowDidDeminiaturize:(NSNotification *)notification +{ + //if (window->monitor) +// enterFullscreenMode(window); + + // _GodotInputWindowIconify(window, GL_FALSE); +} + +- (void)windowDidBecomeKey:(NSNotification *)notification +{ + // _GodotInputWindowFocus(window, GL_TRUE); + // _GodotPlatformSetCursorMode(window, window->cursorMode); + if (OS_OSX::singleton->get_main_loop()) + OS_OSX::singleton->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_FOCUS_IN); +} + +- (void)windowDidResignKey:(NSNotification *)notification +{ + // _GodotInputWindowFocus(window, GL_FALSE); + // _GodotPlatformSetCursorMode(window, Godot_CURSOR_NORMAL); + if (OS_OSX::singleton->get_main_loop()) + OS_OSX::singleton->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_FOCUS_OUT); +} + +@end + +@interface GodotContentView : NSView +{ + NSTrackingArea* trackingArea; +} + + + +@end + +@implementation GodotContentView + ++ (void)initialize +{ + if (self == [GodotContentView class]) + { + /* if (_glfw.ns.cursor == nil) + { + NSImage* data = [[NSImage alloc] initWithSize:NSMakeSize(1, 1)]; + _glfw.ns.cursor = [[NSCursor alloc] initWithImage:data + hotSpot:NSZeroPoint]; + [data release]; + }*/ + } +} + +- (id)init +{ + self = [super init]; + trackingArea = nil; + [self updateTrackingAreas]; + + return self; +} + +-(void)dealloc +{ + [trackingArea release]; + [super dealloc]; +} + +- (BOOL)isOpaque +{ + return YES; +} + +- (BOOL)canBecomeKeyView +{ + return YES; +} + +- (BOOL)acceptsFirstResponder +{ + return YES; +} + +- (void)cursorUpdate:(NSEvent *)event +{ + // setModeCursor(window, window->cursorMode); +} + +- (void)mouseDown:(NSEvent *)event +{ + + print_line("mouse down:"); + button_mask|=BUTTON_MASK_LEFT; + InputEvent ev; + ev.type=InputEvent::MOUSE_BUTTON; + ev.mouse_button.button_index=BUTTON_LEFT; + ev.mouse_button.pressed=true; + ev.mouse_button.x=mouse_x; + ev.mouse_button.y=mouse_y; + ev.mouse_button.global_x=mouse_x; + ev.mouse_button.global_y=mouse_y; + ev.mouse_button.button_mask=button_mask; + ev.mouse_button.doubleclick = [event clickCount]==2; + ev.mouse_button.mod = translateFlags([event modifierFlags]); + OS_OSX::singleton->push_input(ev); + + + /* _glfwInputMouseClick(window, + GLFW_MOUSE_BUTTON_LEFT, + GLFW_PRESS, + translateFlags([event modifierFlags]));*/ +} + +- (void)mouseDragged:(NSEvent *)event +{ + [self mouseMoved:event]; +} + +- (void)mouseUp:(NSEvent *)event +{ + + button_mask&=~BUTTON_MASK_LEFT; + InputEvent ev; + ev.type=InputEvent::MOUSE_BUTTON; + ev.mouse_button.button_index=BUTTON_LEFT; + ev.mouse_button.pressed=false; + ev.mouse_button.x=mouse_x; + ev.mouse_button.y=mouse_y; + ev.mouse_button.global_x=mouse_x; + ev.mouse_button.global_y=mouse_y; + ev.mouse_button.button_mask=button_mask; + ev.mouse_button.mod = translateFlags([event modifierFlags]); + OS_OSX::singleton->push_input(ev); + + /* _glfwInputMouseClick(window, + GLFW_MOUSE_BUTTON_LEFT, + GLFW_RELEASE, + translateFlags([event modifierFlags]));*/ +} + +- (void)mouseMoved:(NSEvent *)event +{ + + InputEvent ev; + ev.type=InputEvent::MOUSE_MOTION; + ev.mouse_motion.button_mask=button_mask; + prev_mouse_x=mouse_x; + prev_mouse_y=mouse_y; + const NSRect contentRect = [OS_OSX::singleton->window_view frame]; + const NSPoint p = [event locationInWindow]; + mouse_x = p.x; + mouse_y = contentRect.size.height - p.y; + ev.mouse_motion.x=mouse_x; + ev.mouse_motion.y=mouse_y; + ev.mouse_motion.global_x=mouse_x; + ev.mouse_motion.global_y=mouse_y; + ev.mouse_motion.relative_x=mouse_x - prev_mouse_x; + ev.mouse_motion.relative_y=mouse_y - prev_mouse_y; + ev.mouse_motion.mod = translateFlags([event modifierFlags]); + + +// ev.mouse_motion.relative_x=[event deltaX]; +// ev.mouse_motion.relative_y=[event deltaY]; + + OS_OSX::singleton->push_input(ev); + + + /* if (window->cursorMode == GLFW_CURSOR_DISABLED) + _glfwInputCursorMotion(window, [event deltaX], [event deltaY]); + else + { + const NSRect contentRect = [window->ns.view frame]; + const NSPoint p = [event locationInWindow]; + + _glfwInputCursorMotion(window, p.x, contentRect.size.height - p.y); + }*/ +} + +- (void)rightMouseDown:(NSEvent *)event +{ + + button_mask|=BUTTON_MASK_RIGHT; + InputEvent ev; + ev.type=InputEvent::MOUSE_BUTTON; + ev.mouse_button.button_index=BUTTON_RIGHT; + ev.mouse_button.pressed=true; + ev.mouse_button.x=mouse_x; + ev.mouse_button.y=mouse_y; + ev.mouse_button.global_x=mouse_x; + ev.mouse_button.global_y=mouse_y; + ev.mouse_button.button_mask=button_mask; + ev.mouse_button.mod = translateFlags([event modifierFlags]); + OS_OSX::singleton->push_input(ev); + + /* _glfwInputMouseClick(window, + GLFW_MOUSE_BUTTON_RIGHT, + GLFW_PRESS, + translateFlags([event modifierFlags]));*/ +} + +- (void)rightMouseDragged:(NSEvent *)event +{ + [self mouseMoved:event]; +} + +- (void)rightMouseUp:(NSEvent *)event +{ + + button_mask&=~BUTTON_MASK_RIGHT; + InputEvent ev; + ev.type=InputEvent::MOUSE_BUTTON; + ev.mouse_button.button_index=BUTTON_RIGHT; + ev.mouse_button.pressed=false; + ev.mouse_button.x=mouse_x; + ev.mouse_button.y=mouse_y; + ev.mouse_button.global_x=mouse_x; + ev.mouse_button.global_y=mouse_y; + ev.mouse_button.button_mask=button_mask; + ev.mouse_button.mod = translateFlags([event modifierFlags]); + OS_OSX::singleton->push_input(ev); + + /*_glfwInputMouseClick(window, + GLFW_MOUSE_BUTTON_RIGHT, + GLFW_RELEASE, + translateFlags([event modifierFlags]));*/ +} + +- (void)otherMouseDown:(NSEvent *)event +{ + + if ((int) [event buttonNumber]!=2) + return; + + button_mask|=BUTTON_MASK_MIDDLE; + InputEvent ev; + ev.type=InputEvent::MOUSE_BUTTON; + ev.mouse_button.button_index=BUTTON_MIDDLE; + ev.mouse_button.pressed=true; + ev.mouse_button.x=mouse_x; + ev.mouse_button.y=mouse_y; + ev.mouse_button.global_x=mouse_x; + ev.mouse_button.global_y=mouse_y; + ev.mouse_button.button_mask=button_mask; + ev.mouse_button.mod = translateFlags([event modifierFlags]); + OS_OSX::singleton->push_input(ev); + + /*_glfwInputMouseClick(window, + (int) [event buttonNumber], + GLFW_PRESS, + translateFlags([event modifierFlags]));*/ +} + +- (void)otherMouseDragged:(NSEvent *)event +{ + [self mouseMoved:event]; +} + +- (void)otherMouseUp:(NSEvent *)event +{ + + if ((int) [event buttonNumber]!=2) + return; + + button_mask&=~BUTTON_MASK_MIDDLE; + InputEvent ev; + ev.type=InputEvent::MOUSE_BUTTON; + ev.mouse_button.button_index=BUTTON_MIDDLE; + ev.mouse_button.pressed=false; + ev.mouse_button.x=mouse_x; + ev.mouse_button.y=mouse_y; + ev.mouse_button.global_x=mouse_x; + ev.mouse_button.global_y=mouse_y; + ev.mouse_button.button_mask=button_mask; + ev.mouse_button.mod = translateFlags([event modifierFlags]); + OS_OSX::singleton->push_input(ev); + /* _glfwInputMouseClick(window, + (int) [event buttonNumber], + GLFW_RELEASE, + translateFlags([event modifierFlags]));*/ +} + +- (void)mouseExited:(NSEvent *)event +{ + // _glfwInputCursorEnter(window, GL_FALSE); +} + +- (void)mouseEntered:(NSEvent *)event +{ + // _glfwInputCursorEnter(window, GL_TRUE); +} + +- (void)viewDidChangeBackingProperties +{ + /* const NSRect contentRect = [window->ns.view frame]; + const NSRect fbRect = convertRectToBacking(window, contentRect); + + _glfwInputFramebufferSize(window, fbRect.size.width, fbRect.size.height);*/ +} + +- (void)updateTrackingAreas +{ + if (trackingArea != nil) + { + [self removeTrackingArea:trackingArea]; + [trackingArea release]; + } + + NSTrackingAreaOptions options = NSTrackingMouseEnteredAndExited | + NSTrackingActiveInKeyWindow | + NSTrackingCursorUpdate | + NSTrackingInVisibleRect; + + trackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds] + options:options + owner:self + userInfo:nil]; + + [self addTrackingArea:trackingArea]; + [super updateTrackingAreas]; +} + +// Translates a OS X keycode to a Godot keycode +// +static int translateKey(unsigned int key) +{ + // Keyboard symbol translation table + static const unsigned int table[128] = + { + /* 00 */ KEY_A, + /* 01 */ KEY_S, + /* 02 */ KEY_D, + /* 03 */ KEY_F, + /* 04 */ KEY_H, + /* 05 */ KEY_G, + /* 06 */ KEY_Z, + /* 07 */ KEY_X, + /* 08 */ KEY_C, + /* 09 */ KEY_V, + /* 0a */ KEY_UNKNOWN, + /* 0b */ KEY_B, + /* 0c */ KEY_Q, + /* 0d */ KEY_W, + /* 0e */ KEY_E, + /* 0f */ KEY_R, + /* 10 */ KEY_Y, + /* 11 */ KEY_T, + /* 12 */ KEY_1, + /* 13 */ KEY_2, + /* 14 */ KEY_3, + /* 15 */ KEY_4, + /* 16 */ KEY_6, + /* 17 */ KEY_5, + /* 18 */ KEY_EQUAL, + /* 19 */ KEY_9, + /* 1a */ KEY_7, + /* 1b */ KEY_MINUS, + /* 1c */ KEY_8, + /* 1d */ KEY_0, + /* 1e */ KEY_BRACERIGHT, + /* 1f */ KEY_O, + /* 20 */ KEY_U, + /* 21 */ KEY_BRACELEFT, + /* 22 */ KEY_I, + /* 23 */ KEY_P, + /* 24 */ KEY_RETURN, + /* 25 */ KEY_L, + /* 26 */ KEY_J, + /* 27 */ KEY_APOSTROPHE, + /* 28 */ KEY_K, + /* 29 */ KEY_SEMICOLON, + /* 2a */ KEY_BACKSLASH, + /* 2b */ KEY_COMMA, + /* 2c */ KEY_SLASH, + /* 2d */ KEY_N, + /* 2e */ KEY_M, + /* 2f */ KEY_PERIOD, + /* 30 */ KEY_TAB, + /* 31 */ KEY_SPACE, + /* 32 */ KEY_QUOTELEFT, + /* 33 */ KEY_BACKSPACE, + /* 34 */ KEY_UNKNOWN, + /* 35 */ KEY_ESCAPE, + /* 36 */ KEY_META, + /* 37 */ KEY_META, + /* 38 */ KEY_SHIFT, + /* 39 */ KEY_CAPSLOCK, + /* 3a */ KEY_ALT, + /* 3b */ KEY_CONTROL, + /* 3c */ KEY_SHIFT, + /* 3d */ KEY_ALT, + /* 3e */ KEY_CONTROL, + /* 3f */ KEY_UNKNOWN, /* Function */ + /* 40 */ KEY_UNKNOWN, + /* 41 */ KEY_KP_PERIOD, + /* 42 */ KEY_UNKNOWN, + /* 43 */ KEY_KP_MULTIPLY, + /* 44 */ KEY_UNKNOWN, + /* 45 */ KEY_KP_ADD, + /* 46 */ KEY_UNKNOWN, + /* 47 */ KEY_NUMLOCK, /* Really KeypadClear... */ + /* 48 */ KEY_UNKNOWN, /* VolumeUp */ + /* 49 */ KEY_UNKNOWN, /* VolumeDown */ + /* 4a */ KEY_UNKNOWN, /* Mute */ + /* 4b */ KEY_KP_DIVIDE, + /* 4c */ KEY_KP_ENTER, + /* 4d */ KEY_UNKNOWN, + /* 4e */ KEY_KP_SUBSTRACT, + /* 4f */ KEY_UNKNOWN, + /* 50 */ KEY_UNKNOWN, + /* 51 */ KEY_EQUAL, //wtf equal? + /* 52 */ KEY_KP_0, + /* 53 */ KEY_KP_1, + /* 54 */ KEY_KP_2, + /* 55 */ KEY_KP_3, + /* 56 */ KEY_KP_4, + /* 57 */ KEY_KP_5, + /* 58 */ KEY_KP_6, + /* 59 */ KEY_KP_7, + /* 5a */ KEY_UNKNOWN, + /* 5b */ KEY_KP_8, + /* 5c */ KEY_KP_9, + /* 5d */ KEY_UNKNOWN, + /* 5e */ KEY_UNKNOWN, + /* 5f */ KEY_UNKNOWN, + /* 60 */ KEY_F5, + /* 61 */ KEY_F6, + /* 62 */ KEY_F7, + /* 63 */ KEY_F3, + /* 64 */ KEY_F8, + /* 65 */ KEY_F9, + /* 66 */ KEY_UNKNOWN, + /* 67 */ KEY_F11, + /* 68 */ KEY_UNKNOWN, + /* 69 */ KEY_F13, + /* 6a */ KEY_F16, + /* 6b */ KEY_F14, + /* 6c */ KEY_UNKNOWN, + /* 6d */ KEY_F10, + /* 6e */ KEY_UNKNOWN, + /* 6f */ KEY_F12, + /* 70 */ KEY_UNKNOWN, + /* 71 */ KEY_F15, + /* 72 */ KEY_INSERT, /* Really Help... */ + /* 73 */ KEY_HOME, + /* 74 */ KEY_PAGEUP, + /* 75 */ KEY_DELETE, + /* 76 */ KEY_F4, + /* 77 */ KEY_END, + /* 78 */ KEY_F2, + /* 79 */ KEY_PAGEDOWN, + /* 7a */ KEY_F1, + /* 7b */ KEY_LEFT, + /* 7c */ KEY_RIGHT, + /* 7d */ KEY_DOWN, + /* 7e */ KEY_UP, + /* 7f */ KEY_UNKNOWN, + }; + + if (key >= 128) + return KEY_UNKNOWN; + + return table[key]; +} +- (void)keyDown:(NSEvent *)event +{ + InputEvent ev; + ev.type=InputEvent::KEY; + ev.key.pressed=true; + ev.key.mod=translateFlags([event modifierFlags]); + ev.key.scancode = translateKey([event keyCode]); + ev.key.echo = [event isARepeat]; + + NSString* characters = [event characters]; + NSUInteger i, length = [characters length]; + + + if (length>0 && keycode_has_unicode(ev.key.scancode)) { + + + for (i = 0; i < length; i++) { + ev.key.unicode=[characters characterAtIndex:i]; + OS_OSX::singleton->push_input(ev); + ev.key.scancode=0; + } + + } else { + OS_OSX::singleton->push_input(ev); + } +} + +- (void)flagsChanged:(NSEvent *)event +{ + /* int action; + unsigned int newModifierFlags = + [event modifierFlags] & NSDeviceIndependentModifierFlagsMask; + + if (newModifierFlags > window->ns.modifierFlags) + action = GLFW_PRESS; + else + action = GLFW_RELEASE; + + window->ns.modifierFlags = newModifierFlags; + + const int key = translateKey([event keyCode]); + const int mods = translateFlags([event modifierFlags]); + _glfwInputKey(window, key, [event keyCode], action, mods);*/ +} + +- (void)keyUp:(NSEvent *)event +{ + + InputEvent ev; + ev.type=InputEvent::KEY; + ev.key.pressed=false; + ev.key.mod=translateFlags([event modifierFlags]); + ev.key.scancode = translateKey([event keyCode]); + OS_OSX::singleton->push_input(ev); + + + /* const int key = translateKey([event keyCode]); + const int mods = translateFlags([event modifierFlags]); + _glfwInputKey(window, key, [event keyCode], GLFW_RELEASE, mods);*/ +} + +- (void)scrollWheel:(NSEvent *)event +{ + + double deltaX, deltaY; + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 + if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6) + { + deltaX = [event scrollingDeltaX]; + deltaY = [event scrollingDeltaY]; + + if ([event hasPreciseScrollingDeltas]) + { + deltaX *= 0.1; + deltaY *= 0.1; + } + } + else +#endif /*MAC_OS_X_VERSION_MAX_ALLOWED*/ + { + deltaX = [event deltaX]; + deltaY = [event deltaY]; + } + + + if (fabs(deltaY)) { + + InputEvent ev; + ev.type=InputEvent::MOUSE_BUTTON; + ev.mouse_button.button_index=deltaY >0 ? BUTTON_WHEEL_UP : BUTTON_WHEEL_DOWN; + ev.mouse_button.pressed=true; + ev.mouse_button.x=mouse_x; + ev.mouse_button.y=mouse_y; + ev.mouse_button.global_x=mouse_x; + ev.mouse_button.global_y=mouse_y; + ev.mouse_button.button_mask=button_mask; + OS_OSX::singleton->push_input(ev); + ev.mouse_button.pressed=false; + OS_OSX::singleton->push_input(ev); + } + +} + +@end + +@interface GodotWindow : NSWindow {} +@end + +@implementation GodotWindow + +- (BOOL)canBecomeKeyWindow +{ + // Required for NSBorderlessWindowMask windows + return YES; +} + +@end + + +int OS_OSX::get_video_driver_count() const { + + return 1; +} +const char * OS_OSX::get_video_driver_name(int p_driver) const { + + return "GLES2"; +} + +OS::VideoMode OS_OSX::get_default_video_mode() const { + + VideoMode vm; + vm.width=800; + vm.height=600; + vm.fullscreen=false; + vm.resizable=true; + return vm; +} + + +void OS_OSX::initialize_core() { + + OS_Unix::initialize_core(); + SemaphoreOSX::make_default(); + +} + +void OS_OSX::initialize(const VideoMode& p_desired,int p_video_driver,int p_audio_driver) { + + /*** OSX INITIALIZATION ***/ + /*** OSX INITIALIZATION ***/ + /*** OSX INITIALIZATION ***/ + + current_videomode=p_desired; + window_delegate = [[GodotWindowDelegate alloc] init]; + + // Don't use accumulation buffer support; it's not accelerated + // Aux buffers probably aren't accelerated either + + unsigned int styleMask = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | (current_videomode.resizable?NSResizableWindowMask:0); + + + window_object = [[GodotWindow alloc] + initWithContentRect:NSMakeRect(0, 0, current_videomode.width,current_videomode.height) + styleMask:styleMask + backing:NSBackingStoreBuffered + defer:NO]; + + ERR_FAIL_COND( window_object==nil ); + + window_view = [[GodotContentView alloc] init]; + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 + if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6) { + [window_view setWantsBestResolutionOpenGLSurface:YES]; + if (current_videomode.resizable) + [window_object setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary]; + } +#endif /*MAC_OS_X_VERSION_MAX_ALLOWED*/ + +// [window_object setTitle:[NSString stringWithUTF8String:"GodotEnginies"]]; + [window_object setContentView:window_view]; + [window_object setDelegate:window_delegate]; + [window_object setAcceptsMouseMovedEvents:YES]; + [window_object center]; + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 + if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6) + [window_object setRestorable:NO]; +#endif /*MAC_OS_X_VERSION_MAX_ALLOWED*/ + + unsigned int attributeCount = 0; + + // OS X needs non-zero color size, so set resonable values + int colorBits = 24; + + // Fail if a robustness strategy was requested + + +#define ADD_ATTR(x) { attributes[attributeCount++] = x; } +#define ADD_ATTR2(x, y) { ADD_ATTR(x); ADD_ATTR(y); } + + // Arbitrary array size here + NSOpenGLPixelFormatAttribute attributes[40]; + + ADD_ATTR(NSOpenGLPFADoubleBuffer); + ADD_ATTR(NSOpenGLPFAClosestPolicy); + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 + if (false/* use gl3*/) + ADD_ATTR2(NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core); +#endif /*MAC_OS_X_VERSION_MAX_ALLOWED*/ + + ADD_ATTR2(NSOpenGLPFAColorSize, colorBits); + + /* if (fbconfig->alphaBits > 0) + ADD_ATTR2(NSOpenGLPFAAlphaSize, fbconfig->alphaBits);*/ + + ADD_ATTR2(NSOpenGLPFADepthSize, 24); + + ADD_ATTR2(NSOpenGLPFAStencilSize, 8); + + /*if (fbconfig->stereo) + ADD_ATTR(NSOpenGLPFAStereo);*/ + + /* if (fbconfig->samples > 0) + { + ADD_ATTR2(NSOpenGLPFASampleBuffers, 1); + ADD_ATTR2(NSOpenGLPFASamples, fbconfig->samples); + }*/ + + // NOTE: All NSOpenGLPixelFormats on the relevant cards support sRGB + // frambuffer, so there's no need (and no way) to request it + + ADD_ATTR(0); + +#undef ADD_ATTR +#undef ADD_ATTR2 + + pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes]; + ERR_FAIL_COND( pixelFormat == nil); + + + context = [[NSOpenGLContext alloc] initWithFormat:pixelFormat + shareContext:nil]; + + ERR_FAIL_COND(context==nil); + + + [context setView:window_view]; + + [context makeCurrentContext]; + + [NSApp activateIgnoringOtherApps:YES]; + + [window_object makeKeyAndOrderFront:nil]; + + + /*** END OSX INITIALIZATION ***/ + /*** END OSX INITIALIZATION ***/ + /*** END OSX INITIALIZATION ***/ + + bool use_gl2=p_video_driver!=1; + + + + AudioDriverManagerSW::add_driver(&audio_driver_osx); + + + rasterizer = instance_RasterizerGLES2(); + + visual_server = memnew( VisualServerRaster(rasterizer) ); + + if (get_render_thread_mode()!=RENDER_THREAD_UNSAFE) { + + visual_server =memnew(VisualServerWrapMT(visual_server,get_render_thread_mode()==RENDER_SEPARATE_THREAD)); + } + visual_server->init(); + visual_server->cursor_set_visible(false, 0); + + AudioDriverManagerSW::get_driver(p_audio_driver)->set_singleton(); + + if (AudioDriverManagerSW::get_driver(p_audio_driver)->init()!=OK) { + + ERR_PRINT("Initializing audio failed."); + } + + sample_manager = memnew( SampleManagerMallocSW ); + audio_server = memnew( AudioServerSW(sample_manager) ); + + audio_server->set_mixer_params(AudioMixerSW::INTERPOLATION_LINEAR,false); + audio_server->init(); + + spatial_sound_server = memnew( SpatialSoundServerSW ); + spatial_sound_server->init(); + + spatial_sound_2d_server = memnew( SpatialSound2DServerSW ); + spatial_sound_2d_server->init(); + + // + physics_server = memnew( PhysicsServerSW ); + physics_server->init(); + physics_2d_server = memnew( Physics2DServerSW ); + physics_2d_server->init(); + + input = memnew( InputDefault ); + + _ensure_data_dir(); + + +} +void OS_OSX::finalize() { + +} + +void OS_OSX::set_main_loop( MainLoop * p_main_loop ) { + + main_loop=p_main_loop; + input->set_main_loop(p_main_loop); + +} + +void OS_OSX::delete_main_loop() { + + memdelete(main_loop); + main_loop=NULL; +} + + +String OS_OSX::get_name() { + + return "OSX"; +} + +void OS_OSX::set_cursor_shape(CursorShape p_shape) { + + if (cursor_shape==p_shape) + return; + + switch(p_shape) { + case CURSOR_ARROW: [[NSCursor arrowCursor] set]; break; + case CURSOR_IBEAM: [[NSCursor IBeamCursor] set]; break; + case CURSOR_POINTING_HAND: [[NSCursor pointingHandCursor] set]; break; + case CURSOR_CROSS: [[NSCursor crosshairCursor] set]; break; + case CURSOR_WAIT: [[NSCursor arrowCursor] set]; break; + case CURSOR_BUSY: [[NSCursor arrowCursor] set]; break; + case CURSOR_DRAG: [[NSCursor closedHandCursor] set]; break; + case CURSOR_CAN_DROP: [[NSCursor openHandCursor] set]; break; + case CURSOR_FORBIDDEN: [[NSCursor arrowCursor] set]; break; + case CURSOR_VSIZE: [[NSCursor resizeUpDownCursor] set]; break; + case CURSOR_HSIZE: [[NSCursor resizeLeftRightCursor] set]; break; + case CURSOR_BDIAGSIZE: [[NSCursor arrowCursor] set]; break; + case CURSOR_FDIAGSIZE: [[NSCursor arrowCursor] set]; break; + case CURSOR_MOVE: [[NSCursor arrowCursor] set]; break; + case CURSOR_VSPLIT: [[NSCursor resizeUpDownCursor] set]; break; + case CURSOR_HSPLIT: [[NSCursor resizeLeftRightCursor] set]; break; + case CURSOR_HELP: [[NSCursor arrowCursor] set]; break; + default: {}; + } + + cursor_shape=p_shape; +} + +void OS_OSX::set_mouse_show(bool p_show) { + +} +void OS_OSX::set_mouse_grab(bool p_grab) { + +} +bool OS_OSX::is_mouse_grab_enabled() const { + + return mouse_grab; +} +Point2 OS_OSX::get_mouse_pos() const { + + return Vector2(mouse_x,mouse_y); +} +int OS_OSX::get_mouse_button_state() const { + return button_mask; +} +void OS_OSX::set_window_title(const String& p_title) { + + [window_object setTitle:[NSString stringWithUTF8String:p_title.utf8().get_data()]]; + +} + +void OS_OSX::set_icon(const Image& p_icon) { + + Image img=p_icon; + img.convert(Image::FORMAT_RGBA); + NSBitmapImageRep *imgrep= [[[NSBitmapImageRep alloc] initWithBitmapDataPlanes: NULL + pixelsWide: p_icon.get_width() + pixelsHigh: p_icon.get_height() + bitsPerSample: 8 + samplesPerPixel: 4 + hasAlpha: YES + isPlanar: NO + colorSpaceName: NSDeviceRGBColorSpace + bytesPerRow: p_icon.get_width()*4 + bitsPerPixel: 32] autorelease]; + ERR_FAIL_COND(imgrep==nil); + uint8_t *pixels = [imgrep bitmapData]; + + int len = img.get_width()*img.get_height(); + DVector data = img.get_data(); + DVector::Read r = data.read(); + + /* Premultiply the alpha channel */ + for (int i = 0; i *p_list,int p_screen) const { + +} + +void OS_OSX::move_window_to_foreground() { + + [window_object orderFrontRegardless]; +} + +String OS_OSX::get_executable_path() const { + + int ret; + pid_t pid; + char pathbuf[PROC_PIDPATHINFO_MAXSIZE]; + + pid = getpid(); + ret = proc_pidpath (pid, pathbuf, sizeof(pathbuf)); + if ( ret <= 0 ) { + return OS::get_executable_path(); + } else { + String path; + path.parse_utf8(pathbuf); + + return path; + } + +} + + +void OS_OSX::process_events() { + + while (true) { + NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask + untilDate:[NSDate distantPast] + inMode:NSDefaultRunLoopMode + dequeue:YES]; + if (event == nil) + break; + + [NSApp sendEvent:event]; + } + + [autoreleasePool drain]; + autoreleasePool = [[NSAutoreleasePool alloc] init]; +} + + + +void OS_OSX::push_input(const InputEvent& p_event) { + + InputEvent ev=p_event; + ev.ID=last_id++; + //print_line("EV: "+String(ev)); + input->parse_input_event(ev); +} + +void OS_OSX::run() { + + force_quit = false; + + if (!main_loop) + return; + + main_loop->init(); + +// uint64_t last_ticks=get_ticks_usec(); + +// int frames=0; +// uint64_t frame=0; + + while (!force_quit) { + + process_events(); // get rid of pending events +// process_joysticks(); + if (Main::iteration()==true) + break; + }; + + main_loop->finish(); +} + + +OS_OSX* OS_OSX::singleton=NULL; + +OS_OSX::OS_OSX() { + + singleton=this; + autoreleasePool = [[NSAutoreleasePool alloc] init]; + + eventSource = CGEventSourceCreate(kCGEventSourceStateHIDSystemState); + ERR_FAIL_COND(!eventSource); + + CGEventSourceSetLocalEventsSuppressionInterval(eventSource, 0.0); + + + /*if (pthread_key_create(&_Godot.nsgl.current, NULL) != 0) + { + _GodotInputError(Godot_PLATFORM_ERROR, + "NSGL: Failed to create context TLS"); + return GL_FALSE; + }*/ + + framework = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl")); + ERR_FAIL_COND(!framework); + + // Implicitly create shared NSApplication instance + [GodotApplication sharedApplication]; + + // In case we are unbundled, make us a proper UI application + [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; + + #if 0 + // Menu bar setup must go between sharedApplication above and + // finishLaunching below, in order to properly emulate the behavior + // of NSApplicationMain + createMenuBar(); + #endif + + [NSApp finishLaunching]; + + delegate = [[GodotApplicationDelegate alloc] init]; + ERR_FAIL_COND(!delegate); + [NSApp setDelegate:delegate]; + + + last_id=1; + cursor_shape=CURSOR_ARROW; + + +} diff --git a/platform/osx/platform_config.h b/platform/osx/platform_config.h new file mode 100644 index 00000000000..da4265f3cfa --- /dev/null +++ b/platform/osx/platform_config.h @@ -0,0 +1,31 @@ +/*************************************************************************/ +/* platform_config.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. */ +/*************************************************************************/ +#include +#define GLES2_INCLUDE_H "gl_context/glew.h" +#define GLES1_INCLUDE_H "gl_context/glew.h" diff --git a/platform/osx/sem_osx.cpp b/platform/osx/sem_osx.cpp new file mode 100644 index 00000000000..be70bedf84d --- /dev/null +++ b/platform/osx/sem_osx.cpp @@ -0,0 +1,115 @@ +/*************************************************************************/ +/* sem_osx.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 "sem_osx.h" + +#include +#include + +void cgsem_init(cgsem_t *cgsem) +{ + int flags, fd, i; + + pipe(cgsem->pipefd); + + /* Make the pipes FD_CLOEXEC to allow them to close should we call + * execv on restart. */ + for (i = 0; i < 2; i++) { + fd = cgsem->pipefd[i]; + flags = fcntl(fd, F_GETFD, 0); + flags |= FD_CLOEXEC; + fcntl(fd, F_SETFD, flags); + } +} + +void cgsem_post(cgsem_t *cgsem) +{ + const char buf = 1; + + write(cgsem->pipefd[1], &buf, 1); +} + +void cgsem_wait(cgsem_t *cgsem) +{ + char buf; + + read(cgsem->pipefd[0], &buf, 1); +} + +void cgsem_destroy(cgsem_t *cgsem) +{ + close(cgsem->pipefd[1]); + close(cgsem->pipefd[0]); +} + + +#include "os/memory.h" +#include + + +Error SemaphoreOSX::wait() { + + cgsem_wait(&sem); + return OK; +} + +Error SemaphoreOSX::post() { + + cgsem_post(&sem); + + return OK; +} +int SemaphoreOSX::get() const { + + return 0; +} + + +Semaphore *SemaphoreOSX::create_semaphore_osx() { + + return memnew( SemaphoreOSX ); +} + +void SemaphoreOSX::make_default() { + + create_func=create_semaphore_osx; +} + +SemaphoreOSX::SemaphoreOSX() { + + cgsem_init(&sem); +} + + +SemaphoreOSX::~SemaphoreOSX() { + + cgsem_destroy(&sem); +} + + + diff --git a/platform/osx/sem_osx.h b/platform/osx/sem_osx.h new file mode 100644 index 00000000000..65bed7397f6 --- /dev/null +++ b/platform/osx/sem_osx.h @@ -0,0 +1,60 @@ +/*************************************************************************/ +/* sem_osx.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 SEM_OSX_H +#define SEM_OSX_H + +struct cgsem { + int pipefd[2]; +}; + +typedef struct cgsem cgsem_t; + +#include "os/semaphore.h" + +class SemaphoreOSX : public Semaphore { + + mutable cgsem_t sem; + + static Semaphore *create_semaphore_osx(); + +public: + + virtual Error wait(); + virtual Error post(); + virtual int get() const; + + static void make_default(); + SemaphoreOSX(); + + ~SemaphoreOSX(); + +}; + + +#endif diff --git a/platform/server/SCsub b/platform/server/SCsub new file mode 100644 index 00000000000..3dda6b43951 --- /dev/null +++ b/platform/server/SCsub @@ -0,0 +1,8 @@ +Import('env') + + +common_server=[\ + "os_server.cpp",\ +] + +env.Program('#bin/godot_server',['godot_server.cpp']+common_server) diff --git a/platform/server/detect.py b/platform/server/detect.py new file mode 100644 index 00000000000..1ce8c232298 --- /dev/null +++ b/platform/server/detect.py @@ -0,0 +1,91 @@ + +import os +import sys + + +def is_active(): + return True + +def get_name(): + return "Server" + + +def can_build(): + + if (os.name!="posix"): + return False + + return True # enabled + +def get_opts(): + + return [ + ('use_llvm','Use llvm compiler','no'), + ('force_32_bits','Force 32 bits binary','no') + ] + +def get_flags(): + + return [ + ('builtin_zlib', 'no'), + ] + + + +def configure(env): + + env.Append(CPPPATH=['#platform/server']) + if (env["use_llvm"]=="yes"): + env["CC"]="clang" + env["CXX"]="clang++" + env["LD"]="clang++" + + env['OBJSUFFIX'] = ".srv"+env['OBJSUFFIX'] + env['LIBSUFFIX'] = ".srv"+env['LIBSUFFIX'] + + if (env["force_32_bits"]!="no"): + env['OBJSUFFIX'] = ".32"+env['OBJSUFFIX'] + env['LIBSUFFIX'] = ".32"+env['LIBSUFFIX'] + + + if (env["tools"]=="no"): + #no tools suffix + env['OBJSUFFIX'] = ".nt"+env['OBJSUFFIX'] + env['LIBSUFFIX'] = ".nt"+env['LIBSUFFIX'] + + + if (env["target"]=="release"): + + env.Append(CCFLAGS=['-O2','-ffast-math','-fomit-frame-pointer']) + env['OBJSUFFIX'] = "_opt"+env['OBJSUFFIX'] + env['LIBSUFFIX'] = "_opt"+env['LIBSUFFIX'] + + elif (env["target"]=="release_debug"): + + env.Append(CCFLAGS=['-O2','-ffast-math','-DDEBUG_ENABLED']) + env['OBJSUFFIX'] = "_optd"+env['OBJSUFFIX'] + env['LIBSUFFIX'] = "_optd"+env['LIBSUFFIX'] + + + elif (env["target"]=="debug"): + + env.Append(CCFLAGS=['-g2', '-Wall','-DDEBUG_ENABLED','-DDEBUG_MEMORY_ENABLED']) + + elif (env["target"]=="profile"): + + env.Append(CCFLAGS=['-g','-pg']) + env.Append(LINKFLAGS=['-pg']) + + + env.Append(CPPFLAGS=['-DSERVER_ENABLED','-DUNIX_ENABLED']) + env.Append(LIBS=['pthread','z']) #TODO detect linux/BSD! + + if (env["force_32_bits"]=="yes"): + env.Append(CPPFLAGS=['-m32']) + env.Append(LINKFLAGS=['-m32','-L/usr/lib/i386-linux-gnu']) + + if (env["CXX"]=="clang++"): + env.Append(CPPFLAGS=['-DTYPED_METHOD_BIND']) + env["CC"]="clang" + env["LD"]="clang++" + diff --git a/platform/server/godot_server.cpp b/platform/server/godot_server.cpp new file mode 100644 index 00000000000..3f817c7237d --- /dev/null +++ b/platform/server/godot_server.cpp @@ -0,0 +1,45 @@ +/*************************************************************************/ +/* godot_server.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 "main/main.h" +#include "os_server.h" + +int main(int argc, char* argv[]) { + + OS_Server os; + + Error err = Main::setup(argv[0],argc-1,&argv[1]); + if (err!=OK) + return 255; + + if (Main::start()) + os.run(); // it is actually the OS that decides how to run + Main::cleanup(); + + return os.get_exit_code(); +} diff --git a/platform/server/logo.h b/platform/server/logo.h new file mode 100644 index 00000000000..bb383145f5a --- /dev/null +++ b/platform/server/logo.h @@ -0,0 +1,2 @@ + /* AUTOGENERATED FILE, DO NOT EDIT */ + static const unsigned char _server_logo[]={0x89,0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,0x0,0x0,0x20,0x0,0x0,0x0,0x20,0x8,0x6,0x0,0x0,0x0,0x73,0x7a,0x7a,0xf4,0x0,0x0,0x0,0x6,0x62,0x4b,0x47,0x44,0x0,0xff,0x0,0xff,0x0,0xff,0xa0,0xbd,0xa7,0x93,0x0,0x0,0x0,0x9,0x70,0x48,0x59,0x73,0x0,0x0,0xb,0x12,0x0,0x0,0xb,0x12,0x1,0xd2,0xdd,0x7e,0xfc,0x0,0x0,0x0,0x7,0x74,0x49,0x4d,0x45,0x7,0xdd,0x9,0x8,0x10,0x20,0x27,0x86,0x78,0xdd,0x76,0x0,0x0,0x7,0xff,0x49,0x44,0x41,0x54,0x58,0xc3,0xe5,0x97,0x5b,0x6c,0x5c,0xc5,0x19,0xc7,0x7f,0x67,0xe6,0xac,0xf7,0x7e,0xb1,0xbd,0x4e,0x7c,0x49,0xe2,0x24,0xb6,0x43,0x1c,0xc,0xa6,0x20,0x94,0x4,0xda,0x26,0x5,0x51,0x28,0xb4,0x5c,0x2a,0x55,0x50,0xb5,0x85,0xa6,0xea,0x13,0x8,0xa9,0xa5,0x2f,0x54,0xaa,0x8a,0xd4,0xdb,0x4b,0xdb,0x27,0xaa,0x88,0x16,0x1a,0x50,0x8a,0x84,0xd4,0xf6,0xa1,0x2,0x24,0x6e,0x89,0x80,0x8,0x8,0x71,0x2e,0x26,0xc1,0x71,0x12,0x27,0xbe,0x67,0xd7,0xb7,0xf5,0xae,0xd7,0xf6,0xee,0xd9,0x3d,0x7b,0x66,0xa6,0xf,0x6b,0x8c,0x93,0x42,0xc,0x42,0xed,0x4b,0x47,0x1a,0x9d,0xf3,0x30,0x33,0xdf,0xff,0xfb,0x7f,0xff,0x6f,0xe6,0xfb,0xe0,0xff,0x7d,0x58,0x9f,0x77,0xc3,0xbd,0xf7,0xdd,0xdf,0xf4,0xd8,0x63,0x8f,0xee,0xac,0xab,0xab,0xbb,0x49,0x79,0xaa,0xa1,0xe2,0x55,0xd2,0xe5,0x72,0x39,0x35,0x3f,0x3f,0x9f,0x4e,0xa5,0xd2,0xa9,0x43,0xef,0xbe,0x97,0xda,0xbf,0xef,0xe9,0x14,0x60,0xbe,0x30,0x80,0xef,0x3d,0xb8,0x67,0xdd,0xf,0x1f,0xfa,0xc1,0x8e,0x86,0x86,0xe4,0xce,0x40,0x4d,0xe0,0x26,0x21,0xe5,0x8e,0x78,0x3c,0x4a,0xc5,0xad,0x50,0x2a,0x97,0x50,0x9e,0x42,0x6b,0x85,0xa7,0x14,0x46,0x6b,0xb4,0xd6,0xb8,0xae,0xcb,0xc2,0xc2,0x22,0x8b,0x8b,0x8b,0xb8,0xae,0x9b,0xf1,0x94,0x97,0x5a,0x58,0x28,0xa4,0xa,0x85,0xe2,0xd3,0x3f,0xfd,0xc9,0xa3,0xff,0xba,0xdc,0x86,0xfd,0xd1,0xcf,0x9f,0xf6,0x3e,0x15,0xa,0x6,0x2,0x77,0x77,0x77,0x77,0x6f,0xf7,0x7,0xfc,0x3b,0x7d,0xb6,0xbd,0xbd,0xb6,0xb6,0x16,0x30,0x68,0x6d,0xd0,0xc6,0xa0,0x95,0xc2,0x71,0x1c,0x94,0x52,0x68,0xa5,0x51,0x5a,0xa1,0x95,0x42,0x79,0xa,0xa5,0x3c,0x5c,0xd7,0xc5,0xf3,0x3c,0x2a,0x95,0xa,0x42,0xa,0x4a,0x65,0x37,0xd9,0x73,0xa4,0x27,0xd9,0x77,0xfa,0x74,0x77,0x3a,0x95,0x7e,0xe5,0x93,0x9c,0x5c,0x6,0xb0,0x7e,0x5d,0x73,0x2c,0x3f,0x57,0x78,0x61,0x72,0x62,0x8a,0xc6,0xa6,0x35,0x84,0x6a,0x13,0xb8,0x6e,0x19,0xad,0x34,0x58,0x16,0x9e,0xe7,0xe1,0xb3,0xab,0xcb,0x8d,0x31,0x68,0xa3,0xd1,0x4a,0x61,0x8c,0x1,0xc,0x13,0x93,0x53,0x4c,0x4f,0xcf,0x30,0x3a,0x3a,0xc6,0xc9,0x93,0xbd,0x7c,0xd0,0xdb,0xcb,0xc8,0xc8,0x30,0xc1,0x60,0x98,0x58,0x2c,0x4e,0x20,0x10,0xf2,0xad,0x1a,0x82,0x7,0xee,0xff,0xb1,0x9,0x5,0x23,0x18,0xa3,0xb1,0x6d,0x1b,0x7f,0xc0,0x47,0x34,0x16,0x21,0x14,0xc,0x10,0x89,0x86,0xf0,0xf9,0x6c,0x42,0xe1,0x0,0xc1,0x80,0x9f,0xba,0xba,0x3a,0x36,0xb4,0x6e,0xe0,0xf8,0xb1,0x13,0xbc,0xfe,0xc6,0x1b,0xc,0x9c,0x3b,0xc7,0xc0,0xc0,0x59,0xb2,0xd9,0xec,0xf2,0x79,0x42,0x48,0x62,0xd1,0x18,0x9d,0x9d,0xd7,0x21,0xa5,0xec,0x7c,0xe7,0xdd,0x3,0x67,0x3f,0x95,0x1,0x0,0xaf,0xe2,0x22,0x22,0x2,0xa3,0x2d,0x94,0x56,0x14,0xa,0x1e,0x8b,0x5,0x7,0x8c,0xa9,0x7a,0xad,0xf5,0x92,0xc7,0x10,0xa,0x7,0x19,0x1f,0x1b,0xe0,0x95,0x57,0x5f,0xfe,0x44,0xfd,0xb4,0xb4,0xb4,0xd2,0xde,0xb6,0x15,0x21,0x24,0xd3,0xd3,0x13,0xb8,0x95,0xf2,0x5d,0xc0,0x95,0x1,0x54,0xbc,0x45,0x66,0x67,0xb,0xd8,0xb6,0xf,0x29,0x6d,0x22,0x91,0x38,0xb9,0xec,0x3c,0xe1,0x48,0xc,0xcb,0x18,0x8c,0x56,0x60,0x9,0x8c,0x31,0xa8,0x8a,0x62,0x72,0x72,0xea,0x92,0xc3,0xe2,0xf1,0x5a,0xb6,0x75,0x5e,0x83,0x14,0x92,0x91,0xf1,0x21,0x8e,0x1c,0x39,0x44,0x24,0x1a,0x23,0x91,0xa8,0x27,0xe0,0xf,0x9c,0xb8,0xa2,0x6,0x0,0x84,0xc,0x13,0x9,0x47,0x11,0x42,0x60,0xdb,0x92,0x52,0xa9,0x48,0xd1,0x99,0x67,0x2e,0x3f,0x43,0x7c,0xcb,0x97,0xd8,0x75,0xff,0xb7,0xc9,0x8c,0x5c,0xe4,0xec,0xb1,0x51,0xca,0xa5,0x1c,0x0,0x3e,0x9f,0x9f,0xab,0xb6,0x74,0x92,0x48,0x24,0xc8,0x64,0xa6,0x39,0xfc,0xfe,0xa1,0x6a,0x6c,0x2d,0x68,0x6d,0xed,0x60,0xe3,0xc6,0x76,0xa4,0x94,0xcc,0xcc,0x4c,0x25,0x57,0x5,0x30,0x9b,0x49,0x61,0x99,0xb5,0xd8,0x76,0x0,0x69,0xfb,0x10,0x96,0x20,0x1e,0x5f,0x3,0x18,0x4c,0x66,0x92,0x43,0x7b,0x9f,0x42,0xd6,0xaf,0xa5,0x36,0xba,0x16,0xaf,0xe5,0x5a,0x16,0x7,0x7,0x68,0x6b,0x2b,0x30,0x38,0x74,0xe,0xc7,0x71,0x0,0x90,0xd2,0xa6,0xbd,0xed,0x2a,0xea,0xeb,0x1b,0x98,0xcd,0x66,0x18,0x18,0x38,0x4d,0x34,0x1a,0x27,0x18,0xc,0x35,0xac,0xa,0xa0,0xa5,0x65,0x23,0xa0,0x11,0xd2,0x60,0x4b,0x8f,0xe1,0xe1,0x61,0xca,0x65,0x97,0x44,0xa2,0x9e,0x70,0x20,0x84,0x52,0x60,0xb9,0xa3,0x64,0xa7,0xc7,0xf1,0x7,0xfa,0x89,0xe6,0x7,0x39,0x71,0xb6,0x1f,0x80,0x70,0x38,0x4a,0x67,0x67,0x17,0x15,0xd7,0xe5,0xc2,0xe0,0x39,0x46,0xc7,0x86,0x8,0x87,0xa3,0x34,0xae,0x6d,0xa1,0xb9,0x79,0x3d,0xc5,0x52,0xf1,0xf0,0x6a,0x0,0x44,0xa1,0x50,0x24,0x10,0x8,0xe1,0x55,0x34,0x65,0x14,0xcd,0xcd,0x6d,0x48,0x29,0x28,0x14,0xe6,0x11,0xeb,0xb7,0xb0,0xeb,0xfb,0xf,0x30,0xb5,0xe0,0x31,0xfb,0xc1,0xc,0x73,0x73,0x13,0x94,0x2e,0xc,0xd1,0xd8,0xd8,0x4c,0x53,0x53,0xb,0xf3,0xf3,0x39,0x8e,0x1d,0x3b,0xbc,0x44,0xbf,0xc5,0xc6,0x8d,0x1d,0x34,0x35,0xb6,0xe0,0x38,0xe,0x93,0x53,0x69,0xc2,0xa1,0xc8,0xd7,0x80,0xde,0x2b,0x1,0xd0,0xc5,0x62,0x96,0x72,0x39,0x4f,0x30,0x18,0xc1,0x68,0xb,0x63,0x34,0x95,0x8a,0xc0,0xb2,0x6c,0xbc,0xf1,0xb,0x1c,0xfc,0xd5,0x13,0x68,0xe5,0xe1,0x8a,0x20,0xb5,0xeb,0xdb,0xa9,0xa9,0xa9,0xc1,0x71,0xa,0xf4,0xf6,0x1e,0x5d,0xce,0xea,0xd6,0xd6,0x4d,0xd4,0xd7,0x25,0xc9,0xe6,0x66,0x19,0x1e,0xb9,0x40,0x2c,0x9a,0xa0,0xa1,0xa1,0x11,0x9f,0xcf,0x3e,0xb8,0x1a,0x3,0xd2,0xb6,0xc3,0x24,0xe2,0x75,0x28,0x5d,0xc1,0x71,0x16,0x99,0x18,0xbf,0x88,0xb4,0x25,0x16,0x16,0xf1,0x96,0xcd,0x24,0x1a,0x1a,0xc8,0x8f,0x8e,0x52,0x23,0x1c,0x54,0x66,0x8,0x51,0x98,0x21,0x9f,0xcf,0x63,0x59,0x82,0xcd,0x9b,0xdb,0xf1,0xfb,0xfd,0x8c,0x8d,0x8d,0x30,0x35,0x99,0x26,0x12,0x8d,0xd3,0xd4,0xb4,0x8e,0x48,0x38,0x86,0x52,0x1e,0x42,0x88,0x5b,0x80,0x93,0x57,0x2,0xa0,0x8a,0xc5,0x79,0xa4,0x10,0x8,0xe9,0x43,0x4a,0x3f,0x6b,0xd6,0xac,0xc3,0xb2,0x2c,0x7c,0x3e,0x1f,0x4,0x42,0x6c,0xfe,0xea,0x2e,0x8a,0x75,0xad,0xa4,0x3f,0x2c,0x61,0x6a,0x34,0x3a,0xff,0x24,0x6d,0x9b,0xb3,0x60,0x59,0x8c,0x8d,0xd,0x57,0xaf,0x60,0x21,0x68,0xdd,0xd0,0x46,0x6d,0x6d,0x1d,0x45,0xa7,0x48,0xa1,0xb0,0x40,0x24,0x12,0xc3,0xf6,0xf9,0xd4,0xaa,0x22,0x8c,0x44,0x63,0xf8,0xfd,0x12,0x4b,0x68,0xfa,0x4f,0xf7,0x13,0x8,0x4,0xa9,0xab,0x4d,0x12,0x8,0x46,0xd0,0xc5,0x49,0x8e,0x3f,0xbf,0x1f,0xb4,0x46,0x6b,0xf,0x19,0x8,0xe3,0x65,0xd3,0xc,0xe,0x5d,0x58,0xde,0xdf,0xd4,0xd4,0x42,0x22,0x51,0xcb,0xdc,0xdc,0x1c,0x93,0x93,0x13,0x44,0xa3,0xb1,0xe5,0x3b,0xc5,0x96,0x36,0xab,0x8a,0xd0,0x29,0x96,0xd1,0xda,0x2,0x3,0x1d,0x5b,0xba,0x50,0x5e,0x85,0xc5,0xc5,0x79,0xe6,0x17,0x32,0x24,0xb6,0x6d,0x67,0xd7,0xdd,0xdf,0xa0,0xb7,0xa7,0x97,0xf4,0xf1,0x53,0xf8,0xdc,0xc,0x7e,0x5c,0x0,0x92,0xc9,0x6,0x62,0xb1,0x38,0x53,0x53,0x93,0x64,0xb3,0x59,0xa2,0x91,0x28,0xe1,0x70,0x14,0x69,0xdb,0x48,0xdb,0xc6,0x5e,0xfa,0xae,0xca,0x80,0x36,0xd5,0x67,0xd5,0x0,0x8e,0xe3,0x70,0xfe,0xc2,0x19,0x9c,0xe2,0x22,0xd7,0x74,0x5d,0x4f,0xf1,0xec,0x49,0x5e,0x1e,0x3b,0x4f,0xb2,0x7d,0x1b,0xf5,0x5b,0x77,0x53,0x5a,0xd3,0x4e,0xf1,0xc0,0x5f,0x68,0x6e,0x4e,0x91,0xcb,0xe5,0xc8,0x64,0x66,0x10,0x42,0x10,0xe,0x45,0x96,0xd,0x7f,0xe4,0xf9,0x67,0x65,0x0,0xad,0x34,0x5a,0x2b,0xb4,0x31,0x60,0xe0,0x3b,0xb7,0xda,0x68,0x13,0xa1,0x67,0xa0,0x82,0x6d,0x4b,0x28,0x16,0xc9,0x9c,0x3a,0xa,0xa6,0x87,0x80,0xdf,0x47,0x68,0x66,0x8a,0x33,0xe9,0xf4,0x25,0x8f,0xcf,0x4a,0xc3,0xb6,0x5c,0xc1,0x80,0x94,0xd6,0x65,0x8f,0x9f,0x1,0x10,0x97,0x0,0xd0,0x1a,0xa5,0x34,0x46,0x6b,0xdc,0x4a,0x99,0x2d,0xdb,0x1f,0xe3,0xce,0xef,0x3e,0x49,0x3a,0x35,0x8a,0xf6,0x14,0xda,0xa8,0xea,0x1a,0xa3,0xf1,0xb4,0x5,0x42,0x5c,0xea,0x4d,0xd5,0xd0,0xb2,0x61,0x69,0x7f,0xcc,0xc0,0x12,0x80,0x2b,0x33,0x50,0x2d,0x30,0x3c,0x34,0x90,0x4e,0x5f,0x64,0xcf,0x9e,0x3d,0x48,0x29,0x70,0x4a,0xb,0x28,0xad,0x30,0x1f,0x55,0x59,0xc6,0xa0,0xf5,0xa5,0xa2,0xb6,0x2c,0x81,0x6d,0xaf,0xf0,0x7a,0x25,0xfd,0xb6,0x4d,0xa5,0x52,0x9,0x3,0x21,0xa0,0x4,0xe8,0x65,0xd6,0x56,0x1e,0x62,0xb4,0x46,0x69,0x8d,0xe3,0xb8,0x1c,0x7e,0xc6,0xe5,0xbd,0x37,0xf7,0x57,0xaf,0xe8,0xa4,0xc2,0x75,0x4b,0x4b,0xc,0x29,0x94,0xae,0xce,0x95,0x8c,0x4a,0x29,0x90,0xd2,0x46,0xac,0xa0,0x5e,0x2e,0x31,0x12,0xc,0x45,0x48,0xd4,0xd7,0x45,0x81,0x75,0x40,0x1c,0xa8,0x59,0xda,0x6c,0x89,0xff,0x64,0x40,0x61,0x61,0xf8,0xe7,0xeb,0x1e,0xe1,0xdc,0x9f,0xf9,0xdd,0x6f,0x7e,0x4b,0xcf,0xb,0x16,0xdd,0x1d,0xe5,0x6a,0x29,0xa6,0x15,0x4a,0xe9,0x6a,0xa5,0xb4,0xf2,0x16,0x5b,0xe1,0xed,0x32,0xf5,0x42,0xb2,0xb5,0xab,0x9b,0x1b,0x77,0x5c,0xeb,0xbc,0xfd,0xe6,0x4b,0x47,0x81,0x24,0xd0,0xb8,0x4,0x42,0x0,0x66,0x39,0x4,0xc9,0xc6,0x66,0xa2,0x91,0x38,0xb,0x8b,0x79,0x84,0x25,0x78,0xe6,0xb5,0xf5,0xec,0x7d,0xa9,0x82,0x52,0xff,0x20,0x73,0x5a,0x70,0xf4,0xc,0xd8,0xbe,0xa5,0x12,0xec,0xb2,0x10,0x58,0x96,0x55,0xcd,0x77,0xbb,0x1a,0x7f,0xb,0x8b,0xe6,0xd,0x6d,0x6c,0xdf,0x71,0x3,0x5e,0x29,0x8f,0xf1,0xdc,0xe0,0x97,0xbf,0xb2,0xeb,0x9a,0xd1,0xd1,0xbf,0x4d,0x2,0x8b,0x40,0xe,0x50,0x97,0x68,0x20,0x33,0x99,0xd6,0x37,0xff,0xe8,0x67,0x0,0x5c,0x4c,0x8d,0x30,0x35,0x95,0x26,0x37,0x37,0x8b,0x5b,0xb6,0x78,0xeb,0x5c,0x2b,0x96,0x71,0x97,0x6a,0x40,0x30,0x18,0x94,0x56,0xcb,0x1,0x90,0x52,0x56,0xa7,0x25,0x88,0x26,0x92,0xdc,0x7a,0xdb,0xed,0xd4,0xc5,0x7c,0x68,0x77,0x81,0x1a,0x7f,0x70,0xe6,0xc5,0x97,0x5e,0xfc,0xf5,0xa1,0xb7,0xdf,0x3a,0x8,0x8c,0x0,0xc5,0x4f,0x15,0x61,0x47,0xe7,0x9a,0xbd,0xd3,0x53,0x33,0xf,0xd7,0x25,0xb7,0x32,0x3e,0x16,0x63,0x64,0x78,0x14,0xaf,0x54,0x60,0xe6,0x86,0x6f,0xe2,0x8,0x3f,0x35,0x83,0xef,0x13,0x9f,0x1d,0x41,0xb9,0x15,0xe6,0x17,0x16,0xb0,0xac,0x8f,0xd3,0xcf,0xf6,0xd5,0x70,0xf3,0xee,0x3b,0xd8,0xb6,0x75,0x13,0xaa,0x52,0xc2,0xf6,0x85,0x38,0x3f,0x70,0x7e,0xdf,0xbe,0x7d,0x4f,0xef,0x95,0x52,0xe,0x2,0x73,0xab,0x66,0x81,0x2d,0x7c,0xf,0xc7,0x63,0x31,0x2e,0x5e,0x1c,0xa7,0xb1,0x39,0x4e,0xe7,0xb6,0x5d,0xcc,0x66,0x73,0x4c,0x5c,0xec,0xe3,0xe4,0xd1,0x1e,0x46,0x88,0xd3,0xb1,0xa5,0x99,0x5b,0x77,0xef,0xe4,0x54,0xdf,0x0,0xef,0xbc,0xfd,0x16,0x0,0x5b,0xbb,0x6e,0xe4,0xb6,0xdb,0x6e,0x41,0xb9,0xb,0x8,0xcb,0xe0,0x54,0x54,0xcf,0x1f,0xff,0xf0,0xfb,0x5f,0x66,0x32,0x33,0xc7,0x81,0x8c,0x52,0xea,0x33,0x37,0x26,0xfe,0x67,0x9f,0xdb,0x7f,0xdf,0xa6,0xcd,0x9b,0xee,0x9,0xf8,0xfd,0x77,0xcd,0xe5,0xe6,0xa2,0x7d,0x7d,0x1f,0x92,0x4a,0xa7,0x88,0x44,0x22,0xa8,0x8a,0x22,0x9b,0x9b,0xe3,0xd4,0xa9,0x93,0x9c,0x39,0xd3,0xcf,0xd5,0x57,0x5f,0x4f,0xc7,0xb6,0x2e,0x82,0x76,0x85,0x40,0x20,0x40,0x38,0x1c,0xcd,0xf7,0xf4,0x1c,0xf9,0xc5,0x6b,0xaf,0xbd,0xf2,0xf7,0x3b,0xba,0x98,0x7e,0xb5,0xef,0xb,0x76,0x46,0xf,0x3e,0xb4,0x67,0xeb,0x1d,0xb7,0xdf,0x7e,0xaf,0xcf,0xe7,0xfb,0x96,0xe3,0x38,0x37,0xe5,0x72,0x59,0x86,0x86,0x87,0x28,0x97,0xca,0xb4,0x77,0xb4,0x23,0x2c,0x41,0x7f,0x7f,0x3f,0x6b,0xd7,0x36,0x91,0x4a,0xa5,0x9e,0x7d,0xee,0xb9,0xbf,0x3e,0x51,0x5b,0x5b,0x9b,0xca,0xe5,0x72,0xfa,0xbf,0xd1,0x1b,0x5a,0x8f,0x3f,0xfe,0xf3,0xaf,0xd7,0xd7,0x27,0xef,0xf1,0xfb,0xfd,0x77,0x5e,0x77,0x5d,0x77,0x6b,0x3e,0x9f,0xe7,0xe0,0xc1,0x37,0x8f,0x1f,0x38,0xf0,0xfa,0xc3,0x7d,0x7d,0x1f,0x9e,0x0,0xbc,0xff,0x49,0x47,0x2b,0xa5,0xc4,0xb2,0xec,0xc6,0x47,0x1e,0x79,0x74,0xf7,0xe5,0x3a,0xfa,0xbc,0xe3,0xdf,0x37,0xe9,0xbc,0x82,0x8a,0x4b,0xde,0x1,0x0,0x0,0x0,0x0,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82}; diff --git a/platform/server/logo.png b/platform/server/logo.png new file mode 100644 index 00000000000..6b7490097fe Binary files /dev/null and b/platform/server/logo.png differ diff --git a/platform/server/os_server.cpp b/platform/server/os_server.cpp new file mode 100644 index 00000000000..7bc8f61744f --- /dev/null +++ b/platform/server/os_server.cpp @@ -0,0 +1,236 @@ +/*************************************************************************/ +/* os_server.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 "servers/visual/visual_server_raster.h" +#include "servers/visual/rasterizer_dummy.h" +#include "os_server.h" +#include +#include +#include "print_string.h" +#include "servers/physics/physics_server_sw.h" + +#include "main/main.h" + +#include + +int OS_Server::get_video_driver_count() const { + + return 1; +} +const char * OS_Server::get_video_driver_name(int p_driver) const { + + return "Dummy"; +} +OS::VideoMode OS_Server::get_default_video_mode() const { + + return OS::VideoMode(800,600,false); +} + +void OS_Server::initialize(const VideoMode& p_desired,int p_video_driver,int p_audio_driver) { + + args=OS::get_singleton()->get_cmdline_args(); + current_videomode=p_desired; + main_loop=NULL; + + + rasterizer = memnew( RasterizerDummy ); + + visual_server = memnew( VisualServerRaster(rasterizer) ); + + AudioDriverManagerSW::get_driver(p_audio_driver)->set_singleton(); + + if (AudioDriverManagerSW::get_driver(p_audio_driver)->init()!=OK) { + + ERR_PRINT("Initializing audio failed."); + } + + sample_manager = memnew( SampleManagerMallocSW ); + audio_server = memnew( AudioServerSW(sample_manager) ); + audio_server->init(); + spatial_sound_server = memnew( SpatialSoundServerSW ); + spatial_sound_server->init(); + spatial_sound_2d_server = memnew( SpatialSound2DServerSW ); + spatial_sound_2d_server->init(); + + + ERR_FAIL_COND(!visual_server); + + visual_server->init(); + // + physics_server = memnew( PhysicsServerSW ); + physics_server->init(); + physics_2d_server = memnew( Physics2DServerSW ); + physics_2d_server->init(); + + input = memnew( InputDefault ); + + _ensure_data_dir(); + + +} +void OS_Server::finalize() { + + if(main_loop) + memdelete(main_loop); + main_loop=NULL; + + spatial_sound_server->finish(); + memdelete(spatial_sound_server); + spatial_sound_2d_server->finish(); + memdelete(spatial_sound_2d_server); + + //if (debugger_connection_console) { +// memdelete(debugger_connection_console); +//} + + audio_server->finish(); + memdelete(audio_server); + memdelete(sample_manager); + + visual_server->finish(); + memdelete(visual_server); + memdelete(rasterizer); + + physics_server->finish(); + memdelete(physics_server); + + physics_2d_server->finish(); + memdelete(physics_2d_server); + + memdelete(input); + + args.clear(); +} + +void OS_Server::set_mouse_show(bool p_show) { + + +} +void OS_Server::set_mouse_grab(bool p_grab) { + + grab=p_grab; +} +bool OS_Server::is_mouse_grab_enabled() const { + + return grab; +} + +int OS_Server::get_mouse_button_state() const { + + return 0; +} + +Point2 OS_Server::get_mouse_pos() const { + + return Point2(); +} + +void OS_Server::set_window_title(const String& p_title) { + + +} + +void OS_Server::set_video_mode(const VideoMode& p_video_mode,int p_screen) { + + +} +OS::VideoMode OS_Server::get_video_mode(int p_screen) const { + + return current_videomode; +} +void OS_Server::get_fullscreen_mode_list(List *p_list,int p_screen) const { + + +} + + +MainLoop *OS_Server::get_main_loop() const { + + return main_loop; +} + +void OS_Server::delete_main_loop() { + + if (main_loop) + memdelete(main_loop); + main_loop=NULL; +} + +void OS_Server::set_main_loop( MainLoop * p_main_loop ) { + + main_loop=p_main_loop; + input->set_main_loop(p_main_loop); +} + +bool OS_Server::can_draw() const { + + return false; //can never draw +}; + + +String OS_Server::get_name() { + + return "Server"; +} + + + +void OS_Server::move_window_to_foreground() { + +} + +void OS_Server::set_cursor_shape(CursorShape p_shape) { + + +} + +void OS_Server::run() { + + force_quit = false; + + if (!main_loop) + return; + + main_loop->init(); + + while (!force_quit) { + + if (Main::iteration()==true) + break; + }; + + main_loop->finish(); +} + +OS_Server::OS_Server() { + + AudioDriverManagerSW::add_driver(&driver_dummy); + //adriver here + grab=false; + +}; diff --git a/platform/server/os_server.h b/platform/server/os_server.h new file mode 100644 index 00000000000..fcf96253ad8 --- /dev/null +++ b/platform/server/os_server.h @@ -0,0 +1,119 @@ +/*************************************************************************/ +/* os_server.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 OS_SERVER_H +#define OS_SERVER_H + + +#include "os/input.h" +#include "drivers/unix/os_unix.h" +#include "servers/visual_server.h" +#include "servers/visual/rasterizer.h" +#include "servers/audio/audio_driver_dummy.h" +#include "servers/physics_server.h" +#include "servers/audio/audio_server_sw.h" +#include "servers/audio/sample_manager_sw.h" +#include "servers/spatial_sound/spatial_sound_server_sw.h" +#include "servers/spatial_sound_2d/spatial_sound_2d_server_sw.h" +#include "drivers/rtaudio/audio_driver_rtaudio.h" +#include "servers/physics_2d/physics_2d_server_sw.h" + +//bitch +#undef CursorShape +/** + @author Juan Linietsky +*/ + +class OS_Server : public OS_Unix { + + Rasterizer *rasterizer; + VisualServer *visual_server; + VideoMode current_videomode; + List args; + MainLoop *main_loop; + + AudioDriverDummy driver_dummy; + bool grab; + + PhysicsServer *physics_server; + Physics2DServer *physics_2d_server; + + virtual void delete_main_loop(); + IP_Unix *ip_unix; + + AudioServerSW *audio_server; + SampleManagerMallocSW *sample_manager; + SpatialSoundServerSW *spatial_sound_server; + SpatialSound2DServerSW *spatial_sound_2d_server; + + bool force_quit; + + InputDefault *input; + + + +protected: + + virtual int get_video_driver_count() const; + virtual const char * get_video_driver_name(int p_driver) const; + virtual VideoMode get_default_video_mode() const; + + virtual void initialize(const VideoMode& p_desired,int p_video_driver,int p_audio_driver); + virtual void finalize(); + + virtual void set_main_loop( MainLoop * p_main_loop ); + +public: + + virtual String get_name(); + + virtual void set_cursor_shape(CursorShape p_shape); + + virtual void set_mouse_show(bool p_show); + virtual void set_mouse_grab(bool p_grab); + virtual bool is_mouse_grab_enabled() const; + virtual Point2 get_mouse_pos() const; + virtual int get_mouse_button_state() const; + virtual void set_window_title(const String& p_title); + + virtual MainLoop *get_main_loop() const; + + virtual bool can_draw() const; + + virtual void set_video_mode(const VideoMode& p_video_mode,int p_screen=0); + virtual VideoMode get_video_mode(int p_screen=0) const; + virtual void get_fullscreen_mode_list(List *p_list,int p_screen=0) const; + + virtual void move_window_to_foreground(); + + void run(); + + OS_Server(); +}; + +#endif diff --git a/platform/server/platform_config.h b/platform/server/platform_config.h new file mode 100644 index 00000000000..1bb5afb0020 --- /dev/null +++ b/platform/server/platform_config.h @@ -0,0 +1,30 @@ +/*************************************************************************/ +/* platform_config.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. */ +/*************************************************************************/ +#include + diff --git a/platform/windows/SCsub b/platform/windows/SCsub new file mode 100644 index 00000000000..1d454e40c2c --- /dev/null +++ b/platform/windows/SCsub @@ -0,0 +1,13 @@ +Import('env') + + +common_win=[ + "context_gl_win.cpp", + "os_windows.cpp", + "ctxgl_procaddr.cpp", + "key_mapping_win.cpp", + "tcp_server_winsock.cpp", + "stream_peer_winsock.cpp", +] + +env.Program('#bin/godot.exe',['godot_win.cpp']+common_win) diff --git a/platform/windows/context_gl_win.cpp b/platform/windows/context_gl_win.cpp new file mode 100644 index 00000000000..a8f74fde2cb --- /dev/null +++ b/platform/windows/context_gl_win.cpp @@ -0,0 +1,205 @@ +/*************************************************************************/ +/* context_gl_win.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. */ +/*************************************************************************/ +#if defined(OPENGL_ENABLED) || defined(LEGACYGL_ENABLED) || defined(GLES2_ENABLED) + +// +// C++ Implementation: context_gl_x11 +// +// Description: +// +// +// Author: Juan Linietsky , (C) 2008 +// +// Copyright: See COPYING file that comes with this distribution +// +// + +#define WINVER 0x0500 +#include "context_gl_win.h" + +//#include "drivers/opengl/glwrapper.h" +//#include "ctxgl_procaddr.h" + +#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 +#define WGL_CONTEXT_FLAGS_ARB 0x2094 +#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002 + +typedef HGLRC (APIENTRY* PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC, HGLRC, const int*); + + +void ContextGL_Win::release_current() { + + + wglMakeCurrent(hDC,NULL); + +} + + +void ContextGL_Win::make_current() { + + wglMakeCurrent(hDC,hRC); +} + +int ContextGL_Win::get_window_width() { + + return OS::get_singleton()->get_video_mode().width; +} + +int ContextGL_Win::get_window_height() { + + return OS::get_singleton()->get_video_mode().height; +} + +void ContextGL_Win::swap_buffers() { + + SwapBuffers(hDC); +} + +/* +static GLWrapperFuncPtr wrapper_get_proc_address(const char* p_function) { + + print_line(String()+"getting proc of: "+p_function); + GLWrapperFuncPtr func=(GLWrapperFuncPtr)get_gl_proc_address(p_function); + if (!func) { + print_line("Couldn't find function: "+String(p_function)); + print_line("error: "+itos(GetLastError())); + } + return func; + +} +*/ + + +Error ContextGL_Win::initialize() { + + static PIXELFORMATDESCRIPTOR pfd= { + sizeof(PIXELFORMATDESCRIPTOR), // Size Of This Pixel Format Descriptor + 1, + PFD_DRAW_TO_WINDOW | // Format Must Support Window + PFD_SUPPORT_OPENGL | // Format Must Support OpenGL + PFD_DOUBLEBUFFER, + PFD_TYPE_RGBA, + 24, + 0, 0, 0, 0, 0, 0, // Color Bits Ignored + 0,// No Alpha Buffer + 0,// Shift Bit Ignored + 0,// No Accumulation Buffer + 0, 0, 0, 0,// Accumulation Bits Ignored + 24,// 24Bit Z-Buffer (Depth Buffer) + 0,// No Stencil Buffer + 0,// No Auxiliary Buffer + PFD_MAIN_PLANE, // Main Drawing Layer + 0,// Reserved + 0, 0, 0 // Layer Masks Ignored + }; + + if (!(hDC=GetDC(hWnd))) { + MessageBox(NULL,"Can't Create A GL Device Context.","ERROR",MB_OK|MB_ICONEXCLAMATION); + return ERR_CANT_CREATE; // Return FALSE + } + + if (!(pixel_format=ChoosePixelFormat(hDC,&pfd))) // Did Windows Find A Matching Pixel Format? + { + MessageBox(NULL,"Can't Find A Suitable pixel_format.","ERROR",MB_OK|MB_ICONEXCLAMATION); + return ERR_CANT_CREATE; // Return FALSE + } + + if(!SetPixelFormat(hDC,pixel_format,&pfd)) // Are We Able To Set The Pixel Format? + { + MessageBox(NULL,"Can't Set The pixel_format.","ERROR",MB_OK|MB_ICONEXCLAMATION); + return ERR_CANT_CREATE; // Return FALSE + } + + if (!(hRC=wglCreateContext(hDC))) // Are We Able To Get A Rendering Context? + { + MessageBox(NULL,"Can't Create A Temporary GL Rendering Context.","ERROR",MB_OK|MB_ICONEXCLAMATION); + return ERR_CANT_CREATE; // Return FALSE + } + + wglMakeCurrent(hDC,hRC); + + if (opengl_3_context) { + + int attribs[] = { + WGL_CONTEXT_MAJOR_VERSION_ARB, 3,//we want a 3.1 context + WGL_CONTEXT_MINOR_VERSION_ARB, 2, + //and it shall be forward compatible so that we can only use up to date functionality + WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, + 0}; //zero indicates the end of the array + + PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = NULL; //pointer to the method + wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC) wglGetProcAddress("wglCreateContextAttribsARB"); + + if(wglCreateContextAttribsARB == NULL) //OpenGL 3.0 is not supported + { + MessageBox(NULL,"Cannot get Proc Adress for CreateContextAttribs", "ERROR",MB_OK|MB_ICONEXCLAMATION); + wglDeleteContext(hRC); + return ERR_CANT_CREATE; + } + + HGLRC new_hRC; + if (!(new_hRC=wglCreateContextAttribsARB(hDC,0, attribs))) + { + wglDeleteContext(hRC); + MessageBox(NULL,"Can't Create An OpenGL 3.1 Rendering Context.","ERROR",MB_OK|MB_ICONEXCLAMATION); + return ERR_CANT_CREATE; // Return false + } + wglMakeCurrent(hDC,NULL); + wglDeleteContext(hRC); + hRC=new_hRC; + + if (!wglMakeCurrent(hDC,hRC)) // Try To Activate The Rendering Context + { + MessageBox(NULL,"Can't Activate The GL 3.1 Rendering Context.","ERROR",MB_OK|MB_ICONEXCLAMATION); + return ERR_CANT_CREATE; // Return FALSE + } + + printf("Activated GL 3.1 context"); + } + + +// glWrapperInit(wrapper_get_proc_address); + + return OK; +} + +ContextGL_Win::ContextGL_Win(HWND hwnd,bool p_opengl_3_context) { + + opengl_3_context=p_opengl_3_context; + hWnd=hwnd; +} + +ContextGL_Win::~ContextGL_Win() { + + +} + + +#endif diff --git a/platform/windows/context_gl_win.h b/platform/windows/context_gl_win.h new file mode 100644 index 00000000000..6e8d99a5f01 --- /dev/null +++ b/platform/windows/context_gl_win.h @@ -0,0 +1,79 @@ +/*************************************************************************/ +/* context_gl_win.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. */ +/*************************************************************************/ +#if defined(OPENGL_ENABLED) || defined(LEGACYGL_ENABLED) || defined(GLES2_ENABLED) +// +// C++ Interface: context_gl_x11 +// +// Description: +// +// +// Author: Juan Linietsky , (C) 2008 +// +// Copyright: See COPYING file that comes with this distribution +// +// + +#ifndef CONTEXT_GL_WIN_H +#define CONTEXT_GL_WIN_H + + +#include "os/os.h" +#include "drivers/gl_context/context_gl.h" +#include "error_list.h" + +#include + +class ContextGL_Win : public ContextGL { + + HDC hDC; + HGLRC hRC; + unsigned int pixel_format; + HWND hWnd; + bool opengl_3_context; +public: + + + virtual void release_current(); + + virtual void make_current(); + + virtual int get_window_width(); + virtual int get_window_height(); + virtual void swap_buffers(); + + virtual Error initialize(); + + + ContextGL_Win(HWND hwnd,bool p_opengl_3_context); + ~ContextGL_Win(); + +}; + +#endif +#endif diff --git a/platform/windows/ctxgl_procaddr.cpp b/platform/windows/ctxgl_procaddr.cpp new file mode 100644 index 00000000000..9715784c328 --- /dev/null +++ b/platform/windows/ctxgl_procaddr.cpp @@ -0,0 +1,186 @@ +/*************************************************************************/ +/* ctxgl_procaddr.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. */ +/*************************************************************************/ +#ifdef OPENGL_ENABLED +#include "ctxgl_procaddr.h" +#include +#include + +static PROC _gl_procs[]={ + (PROC)glCullFace, + (PROC)glFrontFace, + (PROC)glHint, + (PROC)glLineWidth, + (PROC)glPointSize, + (PROC)glPolygonMode, + (PROC)glScissor, + (PROC)glTexParameterf, + (PROC)glTexParameterfv, + (PROC)glTexParameteri, + (PROC)glTexParameteriv, + (PROC)glTexImage1D, + (PROC)glTexImage2D, + (PROC)glDrawBuffer, + (PROC)glClear, + (PROC)glClearColor, + (PROC)glClearStencil, + (PROC)glClearDepth, + (PROC)glStencilMask, + (PROC)glColorMask, + (PROC)glDepthMask, + (PROC)glDisable, + (PROC)glEnable, + (PROC)glFinish, + (PROC)glFlush, + (PROC)glBlendFunc, + (PROC)glLogicOp, + (PROC)glStencilFunc, + (PROC)glStencilOp, + (PROC)glDepthFunc, + (PROC)glPixelStoref, + (PROC)glPixelStorei, + (PROC)glReadBuffer, + (PROC)glReadPixels, + (PROC)glGetBooleanv, + (PROC)glGetDoublev, + (PROC)glGetError, + (PROC)glGetFloatv, + (PROC)glGetIntegerv, + (PROC)glGetString, + (PROC)glGetTexImage, + (PROC)glGetTexParameterfv, + (PROC)glGetTexParameteriv, + (PROC)glGetTexLevelParameterfv, + (PROC)glGetTexLevelParameteriv, + (PROC)glIsEnabled, + (PROC)glDepthRange, + (PROC)glViewport, + /* not detected in ATI */ + (PROC)glDrawArrays, + (PROC)glDrawElements, + (PROC)glGetPointerv, + (PROC)glPolygonOffset, + (PROC)glCopyTexImage1D, + (PROC)glCopyTexImage2D, + (PROC)glCopyTexSubImage1D, + (PROC)glCopyTexSubImage2D, + (PROC)glTexSubImage1D, + (PROC)glTexSubImage2D, + (PROC)glBindTexture, + (PROC)glDeleteTextures, + (PROC)glGenTextures, + (PROC)glIsTexture, + + 0 +}; + +static const char* _gl_proc_names[]={ + "glCullFace", + "glFrontFace", + "glHint", + "glLineWidth", + "glPointSize", + "glPolygonMode", + "glScissor", + "glTexParameterf", + "glTexParameterfv", + "glTexParameteri", + "glTexParameteriv", + "glTexImage1D", + "glTexImage2D", + "glDrawBuffer", + "glClear", + "glClearColor", + "glClearStencil", + "glClearDepth", + "glStencilMask", + "glColorMask", + "glDepthMask", + "glDisable", + "glEnable", + "glFinish", + "glFlush", + "glBlendFunc", + "glLogicOp", + "glStencilFunc", + "glStencilOp", + "glDepthFunc", + "glPixelStoref", + "glPixelStorei", + "glReadBuffer", + "glReadPixels", + "glGetBooleanv", + "glGetDoublev", + "glGetError", + "glGetFloatv", + "glGetIntegerv", + "glGetString", + "glGetTexImage", + "glGetTexParameterfv", + "glGetTexParameteriv", + "glGetTexLevelParameterfv", + "glGetTexLevelParameteriv", + "glIsEnabled", + "glDepthRange", + "glViewport", + /* not detected in ati */ + "glDrawArrays", + "glDrawElements", + "glGetPointerv", + "glPolygonOffset", + "glCopyTexImage1D", + "glCopyTexImage2D", + "glCopyTexSubImage1D", + "glCopyTexSubImage2D", + "glTexSubImage1D", + "glTexSubImage2D", + "glBindTexture", + "glDeleteTextures", + "glGenTextures", + "glIsTexture", + + 0 +}; + +PROC get_gl_proc_address(const char* p_address) { + + PROC proc = wglGetProcAddress((const CHAR*)p_address); + if (!proc) { + + int i=0; + while(_gl_procs[i]) { + + if (strcmp(p_address,_gl_proc_names[i])==0) { + return _gl_procs[i]; + } + i++; + } + } + return proc; +} +#endif diff --git a/platform/windows/ctxgl_procaddr.h b/platform/windows/ctxgl_procaddr.h new file mode 100644 index 00000000000..d3ab20e82d5 --- /dev/null +++ b/platform/windows/ctxgl_procaddr.h @@ -0,0 +1,38 @@ +/*************************************************************************/ +/* ctxgl_procaddr.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 CTXGL_PROCADDR_H +#define CTXGL_PROCADDR_H + +#ifdef OPENGL_ENABLED +#include + + +PROC get_gl_proc_address(const char* p_address); +#endif +#endif // CTXGL_PROCADDR_H diff --git a/platform/windows/detect.py b/platform/windows/detect.py new file mode 100644 index 00000000000..1774ba17f87 --- /dev/null +++ b/platform/windows/detect.py @@ -0,0 +1,208 @@ + + +import os + +import sys + + +def is_active(): + return True + +def get_name(): + return "Windows" + +def can_build(): + + + if (os.name=="nt"): + #building natively on windows! + if (os.getenv("VSINSTALLDIR")): + return True + else: + print("MSVC Not detected, attempting mingw.") + return True + + + + if (os.name=="posix"): + + if os.system("i586-mingw32msvc-gcc --version") == 0: + return True + + + return False + +def get_opts(): + + mwp="" + mwp64="" + if (os.name!="nt"): + mwp="i586-mingw32msvc-" + mwp64="x86_64-w64-mingw32-" + + return [ + ('force_64_bits','Force 64 bits binary','no'), + ('force_32_bits','Force 32 bits binary','no'), + ('mingw_prefix','Mingw Prefix',mwp), + ('mingw_prefix_64','Mingw Prefix 64 bits',mwp64), + ] + +def get_flags(): + + return [ + ('freetype','builtin'), #use builtin freetype + ] + + + +def configure(env): + + if os.name == "posix": + env['OBJSUFFIX'] = ".win"+env['OBJSUFFIX'] + env['LIBSUFFIX'] = ".win"+env['LIBSUFFIX'] + + env.Append(CPPPATH=['#platform/windows']) + + if (env["tools"]=="no"): + #no tools suffix + env['OBJSUFFIX'] = ".nt"+env['OBJSUFFIX'] + env['LIBSUFFIX'] = ".nt"+env['LIBSUFFIX'] + + + if (os.name=="nt" and os.getenv("VSINSTALLDIR")!=None): + #build using visual studio + env['ENV']['TMP'] = os.environ['TMP'] + env.Append(CPPPATH=['#platform/windows/include']) + env.Append(LIBPATH=['#platform/windows/lib']) + + if (env["freetype"]!="no"): + env.Append(CCFLAGS=['/DFREETYPE_ENABLED']) + env.Append(CPPPATH=['#tools/freetype']) + env.Append(CPPPATH=['#tools/freetype/freetype/include']) + + if (env["target"]=="release"): + + env.Append(CCFLAGS=['/O2']) + env.Append(LINKFLAGS=['/SUBSYSTEM:WINDOWS']) + + elif (env["target"]=="test"): + + env.Append(CCFLAGS=['/O2','/DDEBUG_ENABLED','/DD3D_DEBUG_INFO']) + env.Append(LINKFLAGS=['/SUBSYSTEM:CONSOLE']) + + elif (env["target"]=="debug"): + + env.Append(CCFLAGS=['/Zi','/DDEBUG_ENABLED','/DD3D_DEBUG_INFO','/O1']) + env.Append(LINKFLAGS=['/SUBSYSTEM:CONSOLE']) + + elif (env["target"]=="profile"): + + env.Append(CCFLAGS=['-g','-pg']) + env.Append(LINKFLAGS=['-pg']) + + env.Append(CCFLAGS=['/MT','/Gd','/GR','/nologo']) + env.Append(CXXFLAGS=['/TP']) + env.Append(CPPFLAGS=['/DMSVC', '/GR', ]) + env.Append(CCFLAGS=['/I'+os.getenv("WindowsSdkDir")+"/Include"]) + env.Append(CCFLAGS=['/DWINDOWS_ENABLED']) + env.Append(CCFLAGS=['/DRTAUDIO_ENABLED']) + env.Append(CCFLAGS=['/DWIN32']) + env.Append(CCFLAGS=['/DTYPED_METHOD_BIND']) + + #env.Append(CCFLAGS=['/DLEGACYGL_ENABLED']) + env.Append(CCFLAGS=['/DGLES_ENABLED']) + #env.Append(CCFLAGS=['/DGLES1_ENABLED']) + env.Append(CCFLAGS=['/DGLEW_ENABLED']) + env.Append(LIBS=['winmm','opengl32','dsound','kernel32','ole32','user32','gdi32','wsock32']) + env.Append(LINKFLAGS=['/DEBUG']) + + env.Append(LIBPATH=[os.getenv("WindowsSdkDir")+"/Lib"]) + if (os.getenv("DXSDK_DIR")): + DIRECTX_PATH=os.getenv("DXSDK_DIR") + else: + DIRECTX_PATH="C:/Program Files/Microsoft DirectX SDK (March 2009)" + + if (os.getenv("VCINSTALLDIR")): + VC_PATH=os.getenv("VCINSTALLDIR") + else: + VC_PATH="" + + env.Append(CCFLAGS=["/I"+VC_PATH+"/Include"]) + env.Append(LIBPATH=[VC_PATH+"/Lib"]) + env.Append(CCFLAGS=["/I"+DIRECTX_PATH+"/Include"]) + env.Append(LIBPATH=[DIRECTX_PATH+"/Lib/x86"]) + env['ENV'] = os.environ; + else: + #build using mingw + if (os.name=="nt"): + env['ENV']['TMP'] = os.environ['TMP'] #way to go scons, you can be so stupid sometimes + + mingw_prefix="" + + if (env["force_32_bits"]!="no"): + env['OBJSUFFIX'] = ".32"+env['OBJSUFFIX'] + env['LIBSUFFIX'] = ".32"+env['LIBSUFFIX'] + env.Append(CCFLAGS=['-m32']) + env.Append(LINKFLAGS=['-m32']) + + + + + if (env["force_64_bits"]!="no"): + mingw_prefix=env["mingw_prefix_64"]; + env['OBJSUFFIX'] = ".64"+env['OBJSUFFIX'] + env['LIBSUFFIX'] = ".64"+env['LIBSUFFIX'] + env.Append(LINKFLAGS=['-static']) + else: + mingw_prefix=env["mingw_prefix"]; + + if (env["target"]=="release"): + + env.Append(CCFLAGS=['-O3','-ffast-math','-fomit-frame-pointer','-msse2']) + env['OBJSUFFIX'] = "_opt"+env['OBJSUFFIX'] + env['LIBSUFFIX'] = "_opt"+env['LIBSUFFIX'] + elif (env["target"]=="release_debug"): + + env.Append(CCFLAGS=['-O2','-DDEBUG_ENABLED']) + env['OBJSUFFIX'] = "_optd"+env['OBJSUFFIX'] + env['LIBSUFFIX'] = "_optd"+env['LIBSUFFIX'] + + elif (env["target"]=="debug"): + + env.Append(CCFLAGS=['-g', '-Wall','-DDEBUG_ENABLED']) + elif (env["target"]=="release_tools"): + + env.Append(CCFLAGS=['-O2','-Wall','-DDEBUG_ENABLED']) + + + if (env["freetype"]!="no"): + env.Append(CCFLAGS=['-DFREETYPE_ENABLED']) + env.Append(CPPPATH=['#tools/freetype']) + env.Append(CPPPATH=['#tools/freetype/freetype/include']) + + env["CC"]=mingw_prefix+"gcc" + env['AS']=mingw_prefix+"as" + env['CXX'] = mingw_prefix+"g++" + env['AR'] = mingw_prefix+"ar" + env['RANLIB'] = mingw_prefix+"ranlib" + env['LD'] = mingw_prefix+"g++" + + #env['CC'] = "winegcc" + #env['CXX'] = "wineg++" + + env.Append(CCFLAGS=['-DWINDOWS_ENABLED','-mwindows']) + env.Append(CPPFLAGS=['-DRTAUDIO_ENABLED']) + env.Append(CCFLAGS=['-DGLES2_ENABLED','-DGLES1_ENABLED','-DGLEW_ENABLED']) + env.Append(LIBS=['mingw32','opengl32', 'dsound', 'ole32', 'd3d9','winmm','gdi32','wsock32','kernel32']) + #'d3dx9d' + env.Append(CPPFLAGS=['-DMINGW_ENABLED']) + env.Append(LINKFLAGS=['-g']) + + import methods + env.Append( BUILDERS = { 'GLSL120' : env.Builder(action = methods.build_legacygl_headers, suffix = 'glsl.h',src_suffix = '.glsl') } ) + env.Append( BUILDERS = { 'GLSL' : env.Builder(action = methods.build_glsl_headers, suffix = 'glsl.h',src_suffix = '.glsl') } ) + env.Append( BUILDERS = { 'HLSL9' : env.Builder(action = methods.build_hlsl_dx9_headers, suffix = 'hlsl.h',src_suffix = '.hlsl') } ) + env.Append( BUILDERS = { 'GLSL120GLES' : env.Builder(action = methods.build_gles2_headers, suffix = 'glsl.h',src_suffix = '.glsl') } ) + + + diff --git a/platform/windows/export/export.cpp b/platform/windows/export/export.cpp new file mode 100644 index 00000000000..952f51fdd48 --- /dev/null +++ b/platform/windows/export/export.cpp @@ -0,0 +1,24 @@ +#include "export.h" +#include "platform/windows/logo.h" +#include "tools/editor/editor_import_export.h" + +void register_windows_exporter() { + + Image img(_windows_logo); + Ref logo = memnew( ImageTexture ); + logo->create_from_image(img); + + { + Ref exporter = Ref( memnew(EditorExportPlatformPC) ); + exporter->set_binary_extension("exe"); + exporter->set_release_binary32("windows_32_release.exe"); + exporter->set_debug_binary32("windows_32_debug.exe"); + exporter->set_release_binary64("windows_64_release.exe"); + exporter->set_debug_binary64("windows_64_debug.exe"); + exporter->set_name("Windows Desktop"); + exporter->set_logo(logo); + EditorImportExport::get_singleton()->add_export_platform(exporter); + } + + +} diff --git a/platform/windows/export/export.h b/platform/windows/export/export.h new file mode 100644 index 00000000000..de3dc3fa50e --- /dev/null +++ b/platform/windows/export/export.h @@ -0,0 +1,3 @@ + +void register_windows_exporter(); + diff --git a/platform/windows/godot_win.cpp b/platform/windows/godot_win.cpp new file mode 100644 index 00000000000..2999a9beaea --- /dev/null +++ b/platform/windows/godot_win.cpp @@ -0,0 +1,207 @@ +/*************************************************************************/ +/* godot_win.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 "os_windows.h" +#include "main/main.h" +#include + +PCHAR* + CommandLineToArgvA( + PCHAR CmdLine, + int* _argc + ) + { + PCHAR* argv; + PCHAR _argv; + ULONG len; + ULONG argc; + CHAR a; + ULONG i, j; + + BOOLEAN in_QM; + BOOLEAN in_TEXT; + BOOLEAN in_SPACE; + + len = strlen(CmdLine); + i = ((len+2)/2)*sizeof(PVOID) + sizeof(PVOID); + + argv = (PCHAR*)GlobalAlloc(GMEM_FIXED, + i + (len+2)*sizeof(CHAR)); + + _argv = (PCHAR)(((PUCHAR)argv)+i); + + argc = 0; + argv[argc] = _argv; + in_QM = FALSE; + in_TEXT = FALSE; + in_SPACE = TRUE; + i = 0; + j = 0; + + while( (a = CmdLine[i]) ) { + if(in_QM) { + if(a == '\"') { + in_QM = FALSE; + } else { + _argv[j] = a; + j++; + } + } else { + switch(a) { + case '\"': + in_QM = TRUE; + in_TEXT = TRUE; + if(in_SPACE) { + argv[argc] = _argv+j; + argc++; + } + in_SPACE = FALSE; + break; + case ' ': + case '\t': + case '\n': + case '\r': + if(in_TEXT) { + _argv[j] = '\0'; + j++; + } + in_TEXT = FALSE; + in_SPACE = TRUE; + break; + default: + in_TEXT = TRUE; + if(in_SPACE) { + argv[argc] = _argv+j; + argc++; + } + _argv[j] = a; + j++; + in_SPACE = FALSE; + break; + } + } + i++; + } + _argv[j] = '\0'; + argv[argc] = NULL; + + (*_argc) = argc; + return argv; + } + +int main(int argc, char** argv) { + + OS_Windows os(NULL); + + Main::setup(argv[0], argc - 1, &argv[1]); + if (Main::start()) + os.run(); + Main::cleanup(); + + return os.get_exit_code(); +}; + +HINSTANCE godot_hinstance = NULL; + +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { + + int argc; + char** argv; + + char* arg; + int index; + int result; + + // count the arguments + + argc = 1; + arg = lpCmdLine; + + while (arg[0] != 0) { + + while (arg[0] != 0 && arg[0] == ' ') { + arg++; + } + + if (arg[0] != 0) { + + argc++; + + while (arg[0] != 0 && arg[0] != ' ') { + arg++; + } + + } + + } + + // tokenize the arguments + + argv = (char**)malloc(argc * sizeof(char*)); + + arg = lpCmdLine; + index = 1; + + while (arg[0] != 0) { + + while (arg[0] != 0 && arg[0] == ' ') { + arg++; + } + + if (arg[0] != 0) { + + argv[index] = arg; + index++; + + while (arg[0] != 0 && arg[0] != ' ') { + arg++; + } + + if (arg[0] != 0) { + arg[0] = 0; + arg++; + } + + } + + } + + // put the program name into argv[0] + + char filename[_MAX_PATH]; + + GetModuleFileName(NULL, filename, _MAX_PATH); + argv[0] = filename; + + // call the user specified main function + + result = main(argc, argv); + + free(argv); + return result; +} diff --git a/platform/windows/key_mapping_win.cpp b/platform/windows/key_mapping_win.cpp new file mode 100644 index 00000000000..bdf3b2a92f6 --- /dev/null +++ b/platform/windows/key_mapping_win.cpp @@ -0,0 +1,256 @@ +/*************************************************************************/ +/* key_mapping_win.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. */ +/*************************************************************************/ +#define WINVER 0x0500 +#include "key_mapping_win.h" +#include + +struct _WinTranslatePair { + + unsigned int keysym; + unsigned int keycode; +}; + + +static _WinTranslatePair _vk_to_keycode[]={ + +{ KEY_BACKSPACE, VK_BACK },// (0x08) // backspace +{ KEY_TAB, VK_TAB },//(0x09) + +//VK_CLEAR (0x0C) + +{ KEY_RETURN, VK_RETURN },//(0x0D) + +{ KEY_SHIFT, VK_SHIFT },//(0x10) + +{ KEY_CONTROL, VK_CONTROL },//(0x11) + +{ KEY_MENU, VK_MENU },//(0x12) + +{ KEY_PAUSE, VK_PAUSE },//(0x13) + +{ KEY_CAPSLOCK, VK_CAPITAL },//(0x14) + +{ KEY_ESCAPE, VK_ESCAPE },//(0x1B) + +{ KEY_SPACE, VK_SPACE },//(0x20) + +{ KEY_PAGEUP,VK_PRIOR },//(0x21) + +{ KEY_PAGEDOWN, VK_NEXT },//(0x22) + +{ KEY_END, VK_END },//(0x23) + +{ KEY_HOME, VK_HOME },//(0x24) + +{ KEY_LEFT, VK_LEFT },//(0x25) + +{ KEY_UP, VK_UP },//(0x26) + +{ KEY_RIGHT,VK_RIGHT },//(0x27) + +{ KEY_DOWN, VK_DOWN},// (0x28) + +//VK_SELECT (0x29) + +{ KEY_PRINT, VK_PRINT},// (0x2A) + +//VK_EXECUTE (0x2B) + +{ KEY_PRINT, VK_SNAPSHOT},// (0x2C) + +{ KEY_INSERT, VK_INSERT},// (0x2D) + +{ KEY_DELETE, VK_DELETE},// (0x2E) + +{ KEY_HELP, VK_HELP},// (0x2F) + + +{ KEY_0, (0x30) },////0 key +{ KEY_1, (0x31) },////1 key +{ KEY_2, (0x32) },////2 key +{ KEY_3, (0x33) },////3 key +{ KEY_4, (0x34) },////4 key +{ KEY_5, (0x35) },////5 key +{ KEY_6, (0x36) },////6 key +{ KEY_7, (0x37) },////7 key +{ KEY_8, (0x38) },////8 key +{ KEY_9, (0x39) },////9 key +{ KEY_A, (0x41) },////A key +{ KEY_B, (0x42) },////B key +{ KEY_C, (0x43) },////C key +{ KEY_D, (0x44) },////D key +{ KEY_E, (0x45) },////E key +{ KEY_F, (0x46) },////F key +{ KEY_G, (0x47) },////G key +{ KEY_H, (0x48) },////H key +{ KEY_I, (0x49) },////I key +{ KEY_J, (0x4A) },////J key +{ KEY_K, (0x4B) },////K key +{ KEY_L, (0x4C) },////L key +{ KEY_M, (0x4D) },////M key +{ KEY_N, (0x4E) },////N key +{ KEY_O, (0x4F) },////O key +{ KEY_P, (0x50) },////P key +{ KEY_Q, (0x51) },////Q key +{ KEY_R, (0x52) },////R key +{ KEY_S, (0x53) },////S key +{ KEY_T, (0x54) },////T key +{ KEY_U, (0x55) },////U key +{ KEY_V, (0x56) },////V key +{ KEY_W, (0x57) },////W key +{ KEY_X, (0x58) },////X key +{ KEY_Y, (0x59) },////Y key +{ KEY_Z, (0x5A) },////Z key + +{ KEY_MASK_META, VK_LWIN },//(0x5B) +{ KEY_MASK_META, VK_RWIN },//(0x5C) +//VK_APPS (0x5D) +{ KEY_STANDBY,VK_SLEEP },//(0x5F) +{ KEY_KP_0,VK_NUMPAD0 },//(0x60) +{ KEY_KP_1,VK_NUMPAD1 },//(0x61) +{ KEY_KP_2,VK_NUMPAD2 },//(0x62) +{ KEY_KP_3,VK_NUMPAD3 },//(0x63) +{ KEY_KP_4,VK_NUMPAD4 },//(0x64) +{ KEY_KP_5,VK_NUMPAD5 },//(0x65) +{ KEY_KP_6,VK_NUMPAD6 },//(0x66) +{ KEY_KP_7,VK_NUMPAD7 },//(0x67) +{ KEY_KP_8,VK_NUMPAD8 },//(0x68) +{ KEY_KP_9,VK_NUMPAD9 },//(0x69) +{ KEY_KP_MULTIPLY,VK_MULTIPLY},// (0x6A) +{ KEY_KP_ADD,VK_ADD},// (0x6B) +//VK_SEPARATOR (0x6C) +{ KEY_KP_SUBSTRACT,VK_SUBTRACT},// (0x6D) +{ KEY_KP_PERIOD,VK_DECIMAL},// (0x6E) +{ KEY_KP_DIVIDE,VK_DIVIDE},// (0x6F) +{ KEY_F1,VK_F1},// (0x70) +{ KEY_F2,VK_F2},// (0x71) +{ KEY_F3,VK_F3},// (0x72) +{ KEY_F4,VK_F4},// (0x73) +{ KEY_F5,VK_F5},// (0x74) +{ KEY_F6,VK_F6},// (0x75) +{ KEY_F7,VK_F7},// (0x76) +{ KEY_F8,VK_F8},// (0x77) +{ KEY_F9,VK_F9},// (0x78) +{ KEY_F10,VK_F10},// (0x79) +{ KEY_F11,VK_F11},// (0x7A) +{ KEY_F12,VK_F12},// (0x7B) +{ KEY_F13,VK_F13},// (0x7C) +{ KEY_F14,VK_F14},// (0x7D) +{ KEY_F15,VK_F15},// (0x7E) +{ KEY_F16,VK_F16},// (0x7F) +{ KEY_NUMLOCK,VK_NUMLOCK},// (0x90) +{ KEY_SCROLLLOCK,VK_SCROLL},// (0x91) +{ KEY_SHIFT,VK_LSHIFT},// (0xA0) +{ KEY_SHIFT,VK_RSHIFT},// (0xA1) +{ KEY_CONTROL,VK_LCONTROL},// (0xA2) +{ KEY_CONTROL,VK_RCONTROL},// (0xA3) +{ KEY_MENU,VK_LMENU},// (0xA4) +{ KEY_MENU,VK_RMENU},// (0xA5) + + +{ KEY_BACK,VK_BROWSER_BACK},// (0xA6) + +{ KEY_FORWARD,VK_BROWSER_FORWARD},// (0xA7) + +{ KEY_REFRESH,VK_BROWSER_REFRESH},// (0xA8) + +{ KEY_STOP,VK_BROWSER_STOP},// (0xA9) + +{ KEY_SEARCH,VK_BROWSER_SEARCH},// (0xAA) + +{ KEY_FAVORITES, VK_BROWSER_FAVORITES},// (0xAB) + +{ KEY_HOMEPAGE,VK_BROWSER_HOME},// (0xAC) + +{ KEY_VOLUMEMUTE,VK_VOLUME_MUTE},// (0xAD) + +{ KEY_VOLUMEDOWN,VK_VOLUME_DOWN},// (0xAE) + +{ KEY_VOLUMEUP,VK_VOLUME_UP},// (0xAF) + + +{ KEY_MEDIANEXT,VK_MEDIA_NEXT_TRACK},// (0xB0) + +{ KEY_MEDIAPREVIOUS,VK_MEDIA_PREV_TRACK},// (0xB1) + +{ KEY_MEDIASTOP,VK_MEDIA_STOP},// (0xB2) + +//VK_MEDIA_PLAY_PAUSE (0xB3) + +{ KEY_LAUNCHMAIL, VK_LAUNCH_MAIL},// (0xB4) + +{ KEY_LAUNCHMEDIA,VK_LAUNCH_MEDIA_SELECT},// (0xB5) + +{ KEY_LAUNCH0,VK_LAUNCH_APP1},// (0xB6) + +{ KEY_LAUNCH1,VK_LAUNCH_APP2},// (0xB7) + +{ KEY_SEMICOLON,VK_OEM_1},// (0xBA) + + +{ KEY_EQUAL, VK_OEM_PLUS},// (0xBB) // Windows 2000/XP: For any country/region, the '+' key +{ KEY_COLON,VK_OEM_COMMA},// (0xBC) // Windows 2000/XP: For any country/region, the ',' key +{ KEY_MINUS,VK_OEM_MINUS},// (0xBD) // Windows 2000/XP: For any country/region, the '-' key +{ KEY_PERIOD,VK_OEM_PERIOD},// (0xBE) // Windows 2000/XP: For any country/region, the '.' key +{ KEY_SLASH,VK_OEM_2},// (0xBF) //Windows 2000/XP: For the US standard keyboard, the '/?' key + +{KEY_QUOTELEFT, VK_OEM_3},// (0xC0) +{KEY_BRACELEFT,VK_OEM_4},// (0xDB) +{KEY_BACKSLASH,VK_OEM_5},// (0xDC) +{KEY_BRACERIGHT,VK_OEM_6},// (0xDD) +{KEY_APOSTROPHE, VK_OEM_7},// (0xDE) +/* +{VK_OEM_8 (0xDF) +{VK_OEM_102 (0xE2) // Windows 2000/XP: Either the angle bracket key or the backslash key on the RT 102-key keyboard +*/ +//{ KEY_PLAY, VK_PLAY},// (0xFA) + +{KEY_UNKNOWN, 0} }; + +/* +VK_ZOOM (0xFB) +VK_NONAME (0xFC) +VK_PA1 (0xFD) +VK_OEM_CLEAR (0xFE) +*/ + +unsigned int KeyMappingWindows::get_keysym(unsigned int p_code) { + + for(int i=0;_vk_to_keycode[i].keysym!=KEY_UNKNOWN;i++) { + + if (_vk_to_keycode[i].keycode==p_code) { + //printf("outcode: %x\n",_vk_to_keycode[i].keysym); + + return _vk_to_keycode[i].keysym; + } + } + + + return KEY_UNKNOWN; +} diff --git a/platform/windows/key_mapping_win.h b/platform/windows/key_mapping_win.h new file mode 100644 index 00000000000..3e351675b0f --- /dev/null +++ b/platform/windows/key_mapping_win.h @@ -0,0 +1,51 @@ +/*************************************************************************/ +/* key_mapping_win.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 KEY_MAPPING_WINDOWS_H +#define KEY_MAPPING_WINDOWS_H + + +#include "os/keyboard.h" + +#include + +#include + + +class KeyMappingWindows { + + KeyMappingWindows() {}; +public: + + static unsigned int get_keysym(unsigned int p_code); + +}; + + + +#endif diff --git a/platform/windows/lang_table.h b/platform/windows/lang_table.h new file mode 100644 index 00000000000..83f3fb52fd8 --- /dev/null +++ b/platform/windows/lang_table.h @@ -0,0 +1,190 @@ +/*************************************************************************/ +/* lang_table.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 LANG_TABLE_H +#define LANG_TABLE_H + +//#include + +struct _WinLocale { + const char *locale; + int main_lang; + int sublang; +}; + +static const _WinLocale _win_locales[]={ +{"ar", LANG_ARABIC,SUBLANG_NEUTRAL}, +{"ar_AE", LANG_ARABIC,SUBLANG_ARABIC_UAE}, +{"ar_BH", LANG_ARABIC,SUBLANG_ARABIC_BAHRAIN}, +{"ar_DZ", LANG_ARABIC,SUBLANG_ARABIC_ALGERIA}, +{"ar_EG", LANG_ARABIC,SUBLANG_ARABIC_EGYPT}, +{"ar_IQ", LANG_ARABIC,SUBLANG_ARABIC_IRAQ}, +{"ar_JO", LANG_ARABIC,SUBLANG_ARABIC_JORDAN}, +{"ar_KW", LANG_ARABIC,SUBLANG_ARABIC_KUWAIT}, +{"ar_LB", LANG_ARABIC,SUBLANG_ARABIC_LEBANON}, +{"ar_LY", LANG_ARABIC,SUBLANG_ARABIC_LIBYA}, +{"ar_MA", LANG_ARABIC,SUBLANG_ARABIC_MOROCCO}, +{"ar_OM", LANG_ARABIC,SUBLANG_ARABIC_OMAN}, +{"ar_QA", LANG_ARABIC,SUBLANG_ARABIC_QATAR}, +{"ar_SA", LANG_ARABIC,SUBLANG_ARABIC_SAUDI_ARABIA}, +//no sudan +{"ar_SY", LANG_ARABIC,SUBLANG_ARABIC_SYRIA}, +{"ar_TN", LANG_ARABIC,SUBLANG_ARABIC_TUNISIA}, +{"ar_YE", LANG_ARABIC,SUBLANG_ARABIC_YEMEN}, +{"be", LANG_BELARUSIAN,SUBLANG_NEUTRAL}, +{"be_BY", LANG_BELARUSIAN,SUBLANG_BELARUSIAN_BELARUS}, +{"bg", LANG_BULGARIAN,SUBLANG_NEUTRAL}, +{"bg_BG", LANG_BULGARIAN,SUBLANG_BULGARIAN_BULGARIA}, +{"ca", LANG_CATALAN,SUBLANG_NEUTRAL}, +{"ca_ES", LANG_CATALAN,SUBLANG_CATALAN_CATALAN}, +{"cs", LANG_CZECH,SUBLANG_NEUTRAL}, +{"cs_CZ", LANG_CZECH,SUBLANG_CZECH_CZECH_REPUBLIC}, +{"da", LANG_DANISH,SUBLANG_NEUTRAL}, +{"da_DK", LANG_DANISH,SUBLANG_DANISH_DENMARK}, +{"de", LANG_GERMAN,SUBLANG_NEUTRAL}, +{"de_AT", LANG_GERMAN,SUBLANG_GERMAN_AUSTRIAN}, +{"de_CH", LANG_GERMAN,SUBLANG_GERMAN_SWISS}, +{"de_DE", LANG_GERMAN,SUBLANG_GERMAN}, +{"de_LU", LANG_GERMAN,SUBLANG_GERMAN_LUXEMBOURG}, +{"el", LANG_GREEK,SUBLANG_NEUTRAL}, +{"el_GR", LANG_GREEK,SUBLANG_GREEK_GREECE}, +//{"en_029", LANG_ENGLISH,SUBLANG_ENGLISH_CARIBBEAN}, +{"en", LANG_ENGLISH,SUBLANG_NEUTRAL}, +{"en_AU", LANG_ENGLISH,SUBLANG_ENGLISH_AUS}, +{"en_CA", LANG_ENGLISH,SUBLANG_ENGLISH_CAN}, +{"en_GB", LANG_ENGLISH,SUBLANG_ENGLISH_UK}, +//{"en_IE", LANG_ENGLISH,SUBLANG_ENGLISH_IRELAND}, +{"en_IN", LANG_ENGLISH,SUBLANG_ENGLISH_INDIA}, +//MT +{"en_NZ", LANG_ENGLISH,SUBLANG_ENGLISH_NZ}, +{"en_PH", LANG_ENGLISH,SUBLANG_ENGLISH_PHILIPPINES}, +{"en_SG", LANG_ENGLISH,SUBLANG_ENGLISH_SINGAPORE}, +{"en_US", LANG_ENGLISH,SUBLANG_ENGLISH_US}, +{"en_ZA", LANG_ENGLISH,SUBLANG_ENGLISH_SOUTH_AFRICA}, +{"es", LANG_SPANISH,SUBLANG_NEUTRAL}, +{"es_AR", LANG_SPANISH,SUBLANG_SPANISH_ARGENTINA}, +{"es_BO", LANG_SPANISH,SUBLANG_SPANISH_BOLIVIA}, +{"es_CL", LANG_SPANISH,SUBLANG_SPANISH_CHILE}, +{"es_CO", LANG_SPANISH,SUBLANG_SPANISH_COLOMBIA}, +{"es_CR", LANG_SPANISH,SUBLANG_SPANISH_COSTA_RICA}, +{"es_DO", LANG_SPANISH,SUBLANG_SPANISH_DOMINICAN_REPUBLIC}, +{"es_EC", LANG_SPANISH,SUBLANG_SPANISH_ECUADOR}, +{"es_ES", LANG_SPANISH,SUBLANG_SPANISH}, +{"es_GT", LANG_SPANISH,SUBLANG_SPANISH_GUATEMALA}, +{"es_HN", LANG_SPANISH,SUBLANG_SPANISH_HONDURAS}, +{"es_MX", LANG_SPANISH,SUBLANG_SPANISH_MEXICAN}, +{"es_NI", LANG_SPANISH,SUBLANG_SPANISH_NICARAGUA}, +{"es_PA", LANG_SPANISH,SUBLANG_SPANISH_PANAMA}, +{"es_PE", LANG_SPANISH,SUBLANG_SPANISH_PERU}, +{"es_PR", LANG_SPANISH,SUBLANG_SPANISH_PUERTO_RICO}, +{"es_PY", LANG_SPANISH,SUBLANG_SPANISH_PARAGUAY}, +{"es_SV", LANG_SPANISH,SUBLANG_SPANISH_EL_SALVADOR}, +{"es_US", LANG_SPANISH,SUBLANG_SPANISH_US}, +{"es_UY", LANG_SPANISH,SUBLANG_SPANISH_URUGUAY}, +{"es_VE", LANG_SPANISH,SUBLANG_SPANISH_VENEZUELA}, +{"et", LANG_ESTONIAN,SUBLANG_NEUTRAL}, +{"et_EE", LANG_ESTONIAN,SUBLANG_ESTONIAN_ESTONIA}, +{"fi", LANG_FINNISH,SUBLANG_NEUTRAL}, +{"fi_FI", LANG_FINNISH,SUBLANG_FINNISH_FINLAND}, +{"fr", LANG_FRENCH,SUBLANG_NEUTRAL}, +{"fr_BE", LANG_FRENCH,SUBLANG_FRENCH_BELGIAN}, +{"fr_CA", LANG_FRENCH,SUBLANG_FRENCH_CANADIAN}, +{"fr_CH", LANG_FRENCH,SUBLANG_FRENCH_SWISS}, +{"fr_FR", LANG_FRENCH,SUBLANG_FRENCH}, +{"fr_LU", LANG_FRENCH,SUBLANG_FRENCH_LUXEMBOURG}, +{"ga", LANG_IRISH,SUBLANG_NEUTRAL}, +{"ga_IE", LANG_IRISH,SUBLANG_IRISH_IRELAND}, +{"hi", LANG_HINDI,SUBLANG_NEUTRAL}, +{"hi_IN", LANG_HINDI,SUBLANG_HINDI_INDIA}, +{"hr", LANG_CROATIAN,SUBLANG_NEUTRAL}, +{"hr_HR", LANG_CROATIAN,SUBLANG_CROATIAN_CROATIA}, +{"hu", LANG_HUNGARIAN,SUBLANG_NEUTRAL}, +{"hu_HU", LANG_HUNGARIAN,SUBLANG_HUNGARIAN_HUNGARY}, +{"in", LANG_ARMENIAN,SUBLANG_NEUTRAL}, +{"in_ID", LANG_INDONESIAN,SUBLANG_INDONESIAN_INDONESIA}, +{"is", LANG_ICELANDIC,SUBLANG_NEUTRAL}, +{"is_IS", LANG_ICELANDIC,SUBLANG_ICELANDIC_ICELAND}, +{"it", LANG_ITALIAN,SUBLANG_NEUTRAL}, +{"it_CH", LANG_ITALIAN,SUBLANG_ITALIAN_SWISS}, +{"it_IT", LANG_ITALIAN,SUBLANG_ITALIAN}, +{"iw", LANG_HEBREW,SUBLANG_NEUTRAL}, +{"iw_IL", LANG_HEBREW,SUBLANG_HEBREW_ISRAEL}, +{"ja", LANG_JAPANESE,SUBLANG_NEUTRAL}, +{"ja_JP", LANG_JAPANESE,SUBLANG_JAPANESE_JAPAN}, +{"ko", LANG_KOREAN,SUBLANG_NEUTRAL}, +{"ko_KR", LANG_KOREAN,SUBLANG_KOREAN}, +{"lt", LANG_LITHUANIAN,SUBLANG_NEUTRAL}, +//{"lt_LT", LANG_LITHUANIAN,SUBLANG_LITHUANIAN_LITHUANIA}, +{"lv", LANG_LATVIAN,SUBLANG_NEUTRAL}, +{"lv_LV", LANG_LATVIAN,SUBLANG_LATVIAN_LATVIA}, +{"mk", LANG_MACEDONIAN,SUBLANG_NEUTRAL}, +{"mk_MK", LANG_MACEDONIAN,SUBLANG_MACEDONIAN_MACEDONIA}, +{"ms", LANG_MALAY,SUBLANG_NEUTRAL}, +{"ms_MY", LANG_MALAY,SUBLANG_MALAY_MALAYSIA}, +{"mt", LANG_MALTESE,SUBLANG_NEUTRAL}, +{"mt_MT", LANG_MALTESE,SUBLANG_MALTESE_MALTA}, +{"nl", LANG_DUTCH,SUBLANG_NEUTRAL}, +{"nl_BE", LANG_DUTCH,SUBLANG_DUTCH_BELGIAN}, +{"nl_NL", LANG_DUTCH,SUBLANG_DUTCH}, +{"no", LANG_NORWEGIAN,SUBLANG_NEUTRAL}, +{"no_NO", LANG_NORWEGIAN,SUBLANG_NORWEGIAN_BOKMAL}, +{"no_NO_NY", LANG_NORWEGIAN,SUBLANG_NORWEGIAN_NYNORSK}, +{"pl", LANG_POLISH,SUBLANG_NEUTRAL}, +{"pl_PL", LANG_POLISH,SUBLANG_POLISH_POLAND}, +{"pt", LANG_PORTUGUESE,SUBLANG_NEUTRAL}, +{"pt_BR", LANG_PORTUGUESE,SUBLANG_PORTUGUESE_BRAZILIAN}, +{"pt_PT", LANG_PORTUGUESE,SUBLANG_PORTUGUESE}, +{"ro", LANG_ROMANIAN,SUBLANG_NEUTRAL}, +{"ro_RO", LANG_ROMANIAN,SUBLANG_ROMANIAN_ROMANIA}, +{"ru", LANG_RUSSIAN,SUBLANG_NEUTRAL}, +{"ru_RU", LANG_RUSSIAN,SUBLANG_RUSSIAN_RUSSIA}, +{"sk", LANG_SLOVAK,SUBLANG_NEUTRAL}, +{"sk_SK", LANG_SLOVAK,SUBLANG_SLOVAK_SLOVAKIA}, +{"sl", LANG_SLOVENIAN,SUBLANG_NEUTRAL}, +{"sl_SI", LANG_SLOVENIAN,SUBLANG_SLOVENIAN_SLOVENIA}, +{"sq", LANG_ALBANIAN,SUBLANG_NEUTRAL}, +{"sq_AL", LANG_ALBANIAN,SUBLANG_ALBANIAN_ALBANIA}, +{"sr", LANG_SERBIAN_NEUTRAL,SUBLANG_NEUTRAL}, +{"sv", LANG_SWEDISH,SUBLANG_NEUTRAL}, +{"sv_SE", LANG_SWEDISH,SUBLANG_SWEDISH}, +{"th", LANG_THAI,SUBLANG_NEUTRAL}, +{"th_TH", LANG_THAI,SUBLANG_THAI_THAILAND}, +{"tr", LANG_TURKISH,SUBLANG_NEUTRAL}, +{"tr_TR", LANG_TURKISH,SUBLANG_TURKISH_TURKEY}, +{"uk", LANG_UKRAINIAN,SUBLANG_NEUTRAL}, +{"uk_UA", LANG_UKRAINIAN,SUBLANG_UKRAINIAN_UKRAINE}, +{"vi", LANG_VIETNAMESE,SUBLANG_NEUTRAL}, +{"vi_VN", LANG_VIETNAMESE,SUBLANG_VIETNAMESE_VIETNAM}, +{"zh", LANG_CHINESE,SUBLANG_NEUTRAL}, +{"zh_CN", LANG_CHINESE,SUBLANG_CHINESE_SIMPLIFIED}, +{"zh_HK", LANG_CHINESE,SUBLANG_CHINESE_HONGKONG}, +{"zh_SG", LANG_CHINESE,SUBLANG_CHINESE_SINGAPORE}, +{0, 0,0}, +}; + +#endif // LANG_TABLE_H diff --git a/platform/windows/logo.png b/platform/windows/logo.png new file mode 100644 index 00000000000..a27e14dde82 Binary files /dev/null and b/platform/windows/logo.png differ diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp new file mode 100644 index 00000000000..efeb813a2d4 --- /dev/null +++ b/platform/windows/os_windows.cpp @@ -0,0 +1,1732 @@ +/*************************************************************************/ +/* os_windows.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 "drivers/gles2/rasterizer_gles2.h" +#include "drivers/gles1/rasterizer_gles1.h" +#include "os_windows.h" +#include "drivers/nedmalloc/memory_pool_static_nedmalloc.h" +#include "drivers/unix/memory_pool_static_malloc.h" +#include "os/memory_pool_dynamic_static.h" +#include "drivers/windows/thread_windows.h" +#include "drivers/windows/semaphore_windows.h" +#include "drivers/windows/mutex_windows.h" +#include "main/main.h" +#include "drivers/windows/file_access_windows.h" +#include "drivers/windows/dir_access_windows.h" + + +#include "servers/visual/visual_server_raster.h" +#include "servers/audio/audio_server_sw.h" +#include "servers/visual/visual_server_wrap_mt.h" + +#include "tcp_server_winsock.h" +#include "stream_peer_winsock.h" +#include "os/pc_joystick_map.h" +#include "lang_table.h" +#include "os/memory_pool_dynamic_prealloc.h" +#include "globals.h" +#include "io/marshalls.h" +static const WORD MAX_CONSOLE_LINES = 1500; + +//#define STDOUT_FILE + +extern HINSTANCE godot_hinstance; + +void RedirectIOToConsole() { + + int hConHandle; + + intptr_t lStdHandle; + + CONSOLE_SCREEN_BUFFER_INFO coninfo; + + FILE *fp; + + // allocate a console for this app + + AllocConsole(); + + // set the screen buffer to be big enough to let us scroll text + + GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), + + &coninfo); + + coninfo.dwSize.Y = MAX_CONSOLE_LINES; + + SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), + + coninfo.dwSize); + + // redirect unbuffered STDOUT to the console + + lStdHandle = (intptr_t)GetStdHandle(STD_OUTPUT_HANDLE); + + hConHandle = _open_osfhandle(lStdHandle, _O_TEXT); + + fp = _fdopen( hConHandle, "w" ); + + *stdout = *fp; + + setvbuf( stdout, NULL, _IONBF, 0 ); + + // redirect unbuffered STDIN to the console + + lStdHandle = (intptr_t)GetStdHandle(STD_INPUT_HANDLE); + + hConHandle = _open_osfhandle(lStdHandle, _O_TEXT); + + fp = _fdopen( hConHandle, "r" ); + + *stdin = *fp; + + setvbuf( stdin, NULL, _IONBF, 0 ); + + // redirect unbuffered STDERR to the console + + lStdHandle = (intptr_t)GetStdHandle(STD_ERROR_HANDLE); + + hConHandle = _open_osfhandle(lStdHandle, _O_TEXT); + + fp = _fdopen( hConHandle, "w" ); + + *stderr = *fp; + + setvbuf( stderr, NULL, _IONBF, 0 ); + + // make cout, wcout, cin, wcin, wcerr, cerr, wclog and clog + + // point to console as well +} + +int OS_Windows::get_video_driver_count() const { + + return 2; +} +const char * OS_Windows::get_video_driver_name(int p_driver) const { + + return p_driver==0?"GLES2":"GLES1"; +} + +OS::VideoMode OS_Windows::get_default_video_mode() const { + + return VideoMode(800,600,false); +} + +int OS_Windows::get_audio_driver_count() const { + + return AudioDriverManagerSW::get_driver_count(); +} +const char * OS_Windows::get_audio_driver_name(int p_driver) const { + + AudioDriverSW* driver = AudioDriverManagerSW::get_driver(p_driver); + ERR_FAIL_COND_V( !driver, "" ); + return AudioDriverManagerSW::get_driver(p_driver)->get_name(); +} + +static MemoryPoolStatic *mempool_static=NULL; +static MemoryPoolDynamic *mempool_dynamic=NULL; + +void OS_Windows::initialize_core() { + + + last_button_state=0; + + //RedirectIOToConsole(); + + ThreadWindows::make_default(); + SemaphoreWindows::make_default(); + MutexWindows::make_default(); + + FileAccess::make_default(FileAccess::ACCESS_RESOURCES); + FileAccess::make_default(FileAccess::ACCESS_USERDATA); + FileAccess::make_default(FileAccess::ACCESS_FILESYSTEM); + //FileAccessBufferedFA::make_default(); + DirAccess::make_default(DirAccess::ACCESS_RESOURCES); + DirAccess::make_default(DirAccess::ACCESS_USERDATA); + DirAccess::make_default(DirAccess::ACCESS_FILESYSTEM); + + TCPServerWinsock::make_default(); + StreamPeerWinsock::make_default(); + + mempool_static = new MemoryPoolStaticMalloc; +#if 1 + mempool_dynamic = memnew( MemoryPoolDynamicStatic ); +#else +#define DYNPOOL_SIZE 4*1024*1024 + void * buffer = malloc( DYNPOOL_SIZE ); + mempool_dynamic = memnew( MemoryPoolDynamicPrealloc(buffer,DYNPOOL_SIZE) ); + +#endif + + // We need to know how often the clock is updated + if( !QueryPerformanceFrequency((LARGE_INTEGER *)&ticks_per_second) ) + ticks_per_second = 1000; + // If timeAtGameStart is 0 then we get the time since + // the start of the computer when we call GetGameTime() + ticks_start = 0; + ticks_start = get_ticks_usec(); + + process_map = memnew((Map)); + + IP_Unix::make_default(); + + cursor_shape=CURSOR_ARROW; + + +} + +bool OS_Windows::can_draw() const { + + return !minimized; +}; + + +LRESULT OS_Windows::WndProc(HWND hWnd,UINT uMsg, WPARAM wParam, LPARAM lParam) { + + + switch (uMsg) // Check For Windows Messages + { + case WM_ACTIVATE: // Watch For Window Activate Message + { + minimized = HIWORD(wParam) != 0; + if (!main_loop) { + return 0; + }; + if (LOWORD(wParam) == WA_ACTIVE || LOWORD(wParam) == WA_CLICKACTIVE) { + + main_loop->notification(MainLoop::NOTIFICATION_WM_FOCUS_IN); + alt_mem=false; + control_mem=false; + shift_mem=false; + if (mouse_mode==MOUSE_MODE_CAPTURED) { + RECT clipRect; + GetClientRect(hWnd, &clipRect); + ClientToScreen(hWnd, (POINT*) &clipRect.left); + ClientToScreen(hWnd, (POINT*) &clipRect.right); + ClipCursor(&clipRect); + SetCapture(hWnd); + + } + } else { + main_loop->notification(MainLoop::NOTIFICATION_WM_FOCUS_OUT); + alt_mem=false; + + }; + + return 0; // Return To The Message Loop + } + + case WM_PAINT: + + Main::force_redraw(); + break; + + case WM_SYSCOMMAND: // Intercept System Commands + { + switch (wParam) // Check System Calls + { + case SC_SCREENSAVE: // Screensaver Trying To Start? + case SC_MONITORPOWER: // Monitor Trying To Enter Powersave? + return 0; // Prevent From Happening + case SC_KEYMENU: + if ((lParam>>16)<=0) + return 0; + } + break; // Exit + } + + case WM_CLOSE: // Did We Receive A Close Message? + { + if (main_loop) + main_loop->notification(MainLoop::NOTIFICATION_WM_QUIT_REQUEST); + //force_quit=true; + return 0; // Jump Back + } + case WM_MOUSELEAVE: { + + old_invalid=true; + outside=true; + + } break; + case WM_MOUSEMOVE: { + + if (outside) { + + CursorShape c=cursor_shape; + cursor_shape=CURSOR_MAX; + set_cursor_shape(c); + outside=false; + + //Once-Off notification, must call again.... + TRACKMOUSEEVENT tme; + tme.cbSize=sizeof(TRACKMOUSEEVENT); + tme.dwFlags=TME_LEAVE; + tme.hwndTrack=hWnd; + tme.dwHoverTime=HOVER_DEFAULT; + TrackMouseEvent(&tme); + + } + InputEvent event; + event.type=InputEvent::MOUSE_MOTION; + event.ID=++last_id; + InputEventMouseMotion &mm=event.mouse_motion; + + mm.mod.control=(wParam&MK_CONTROL)!=0; + mm.mod.shift=(wParam&MK_SHIFT)!=0; + mm.mod.alt=alt_mem; + + mm.button_mask|=(wParam&MK_LBUTTON)?(1<<0):0; + mm.button_mask|=(wParam&MK_RBUTTON)?(1<<1):0; + mm.button_mask|=(wParam&MK_MBUTTON)?(1<<2):0; + last_button_state=mm.button_mask; + /*mm.button_mask|=(wParam&MK_XBUTTON1)?(1<<5):0; + mm.button_mask|=(wParam&MK_XBUTTON2)?(1<<6):0;*/ + mm.x=GET_X_LPARAM(lParam); + mm.y=GET_Y_LPARAM(lParam); + + if (mouse_mode==MOUSE_MODE_CAPTURED) { + + Point2i c(video_mode.width/2,video_mode.height/2); + if (Point2i(mm.x,mm.y)==c) { + center=c; + return 0; + } + + Point2i ncenter(mm.x,mm.y); + mm.x = old_x + (mm.x-center.x); + mm.y = old_y + (mm.y-center.y); + center=ncenter; + POINT pos = { (int) c.x, (int) c.y }; + ClientToScreen(hWnd, &pos); + SetCursorPos(pos.x, pos.y); + + } + + input->set_mouse_pos(Point2(mm.x,mm.y)); + mm.speed_x=input->get_mouse_speed().x; + mm.speed_y=input->get_mouse_speed().y; + + if (old_invalid) { + + old_x=mm.x; + old_y=mm.y; + old_invalid=false; + } + + mm.relative_x=mm.x-old_x; + mm.relative_y=mm.y-old_y; + old_x=mm.x; + old_y=mm.y; + if (main_loop) + input->parse_input_event(event); + + + + } break; + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case WM_MBUTTONDOWN: + case WM_MBUTTONUP: + case WM_RBUTTONDOWN: + case WM_RBUTTONUP: + case WM_MOUSEWHEEL: + case WM_LBUTTONDBLCLK: + /*case WM_XBUTTONDOWN: + case WM_XBUTTONUP: */{ + + InputEvent event; + event.type=InputEvent::MOUSE_BUTTON; + event.ID=++last_id; + InputEventMouseButton &mb=event.mouse_button; + + switch (uMsg) { + case WM_LBUTTONDOWN: { + mb.pressed=true; + mb.button_index=1; + } break; + case WM_LBUTTONUP: { + mb.pressed=false; + mb.button_index=1; + } break; + case WM_MBUTTONDOWN: { + mb.pressed=true; + mb.button_index=3; + + } break; + case WM_MBUTTONUP: { + mb.pressed=false; + mb.button_index=3; + } break; + case WM_RBUTTONDOWN: { + mb.pressed=true; + mb.button_index=2; + } break; + case WM_RBUTTONUP: { + mb.pressed=false; + mb.button_index=2; + } break; + case WM_LBUTTONDBLCLK: { + + mb.pressed=true; + mb.button_index=1; + mb.doubleclick = true; + } break; + case WM_MOUSEWHEEL: { + + mb.pressed=true; + int motion = (short)HIWORD(wParam); + if (!motion) + return 0; + + + if (motion>0) + mb.button_index=4; + else + mb.button_index=5; + + + } break; + /* + case WM_XBUTTONDOWN: { + mb.pressed=true; + mb.button_index=(HIWORD(wParam)==XBUTTON1)?6:7; + } break; + case WM_XBUTTONUP: + mb.pressed=true; + mb.button_index=(HIWORD(wParam)==XBUTTON1)?6:7; + } break;*/ + default: { return 0; } + } + + + mb.mod.control=(wParam&MK_CONTROL)!=0; + mb.mod.shift=(wParam&MK_SHIFT)!=0; + mb.mod.alt=alt_mem; + //mb.mod.alt=(wParam&MK_MENU)!=0; + mb.button_mask|=(wParam&MK_LBUTTON)?(1<<0):0; + mb.button_mask|=(wParam&MK_RBUTTON)?(1<<1):0; + mb.button_mask|=(wParam&MK_MBUTTON)?(1<<2):0; + + last_button_state=mb.button_mask; + /* + mb.button_mask|=(wParam&MK_XBUTTON1)?(1<<5):0; + mb.button_mask|=(wParam&MK_XBUTTON2)?(1<<6):0;*/ + mb.x=GET_X_LPARAM(lParam); + mb.y=GET_Y_LPARAM(lParam); + + if (mouse_mode==MOUSE_MODE_CAPTURED) { + + mb.x=old_x; + mb.y=old_y; + } + + mb.global_x=mb.x; + mb.global_y=mb.y; + + + if (uMsg != WM_MOUSEWHEEL) { + if (mb.pressed) { + + if (++pressrc>0) + SetCapture(hWnd); + } else { + + + if (--pressrc<=0) { + ReleaseCapture(); + pressrc=0; + } + + } + } else if (mouse_mode!=MOUSE_MODE_CAPTURED) { + // for reasons unknown to mankind, wheel comes in screen cordinates + RECT rect; + GetWindowRect(hWnd,&rect); + mb.x-=rect.left; + mb.y-=rect.top; + + } + + if (main_loop) { + input->parse_input_event(event); + if (mb.pressed && mb.button_index>3) { + //send release for mouse wheel + mb.pressed=false; + event.ID=++last_id; + input->parse_input_event(event); + + } + } + + + + } break; + + case WM_SIZE: { + video_mode.width=LOWORD(lParam); + video_mode.height=HIWORD(lParam); + //return 0; // Jump Back + } break; + case WM_SYSKEYDOWN: + case WM_SYSKEYUP: + case WM_KEYUP: + case WM_KEYDOWN: { + + + if (wParam==VK_SHIFT) + shift_mem=uMsg==WM_KEYDOWN; + if (wParam==VK_CONTROL) + control_mem=uMsg==WM_KEYDOWN; + if (wParam==VK_MENU) { + alt_mem=(uMsg==WM_KEYDOWN || uMsg==WM_SYSKEYDOWN); + if (lParam&(1<<24)) + gr_mem=alt_mem; + } + + //if (wParam==VK_WIN) TODO wtf is this? + // meta_mem=uMsg==WM_KEYDOWN; + + + } //fallthrough + case WM_CHAR: { + + ERR_BREAK(key_event_pos >= KEY_EVENT_BUFFER_SIZE); + + KeyEvent ke; + ke.mod_state.shift=shift_mem; + ke.mod_state.alt=alt_mem; + ke.mod_state.control=control_mem; + ke.mod_state.meta=meta_mem; + ke.uMsg=uMsg; + + if (ke.uMsg==WM_SYSKEYDOWN) + ke.uMsg=WM_KEYDOWN; + if (ke.uMsg==WM_SYSKEYUP) + ke.uMsg=WM_KEYUP; + + + /*if (ke.uMsg==WM_KEYDOWN && alt_mem && uMsg!=WM_SYSKEYDOWN) { + //altgr hack for intl keyboards, not sure how good it is + //windows is weeeeird + ke.mod_state.alt=false; + ke.mod_state.control=false; + print_line("") + }*/ + + + ke.wParam=wParam; + ke.lParam=lParam; + key_event_buffer[key_event_pos++]=ke; + + } break; + case WM_INPUTLANGCHANGEREQUEST: { + + print_line("input lang change"); + } break; + default: { + + if (user_proc) { + + return CallWindowProcW(user_proc, hWnd, uMsg, wParam, lParam); + }; + }; + } + + return DefWindowProcW(hWnd,uMsg,wParam,lParam); + +} + +LRESULT CALLBACK WndProc(HWND hWnd,UINT uMsg, WPARAM wParam, LPARAM lParam) { + + OS_Windows *os_win = static_cast(OS::get_singleton()); + if (os_win) + return os_win->WndProc(hWnd,uMsg,wParam,lParam); + else + return DefWindowProcW(hWnd,uMsg,wParam,lParam); + +} + +void OS_Windows::probe_joysticks() { + + int device_count = joyGetNumDevs(); + + JOYINFOEX jinfo; + jinfo.dwSize = sizeof(JOYINFOEX); + jinfo.dwFlags = JOY_RETURNALL; + + for (int i=0; i 0) && (joyGetPosEx(JOYSTICKID1 + i, &jinfo) == JOYERR_NOERROR); + + if (!joysticks[i].attached) { + + continue; + }; + + joysticks[i].last_buttons = jinfo.dwButtons; + + joysticks[i].last_axis[0] = jinfo.dwXpos; + joysticks[i].last_axis[1] = jinfo.dwYpos; + joysticks[i].last_axis[2] = jinfo.dwZpos; + joysticks[i].last_axis[3] = jinfo.dwRpos; + joysticks[i].last_axis[4] = jinfo.dwUpos; + joysticks[i].last_axis[5] = jinfo.dwVpos; + }; +}; + +void OS_Windows::process_key_events() { + + for(int i=0;iparse_input_event(event); + + + } break; + } + } + + key_event_pos=0; +} + +void OS_Windows::_post_dpad(DWORD p_dpad, int p_device, bool p_pressed) { + + InputEvent ievent; + ievent.device = p_device; + ievent.type = InputEvent::JOYSTICK_BUTTON; + ievent.joy_button.pressed = p_pressed; + ievent.joy_button.pressure = p_pressed ? 1.0 : 0.0; + + if (p_dpad == 0) { + + ievent.joy_button.button_index = JOY_DPAD_UP; + ievent.ID = ++last_id; + input->parse_input_event(ievent); + + } else if (p_dpad == 4500) { + + ievent.joy_button.button_index = JOY_DPAD_UP; + ievent.ID = ++last_id; + input->parse_input_event(ievent); + + ievent.joy_button.button_index = JOY_DPAD_RIGHT; + ievent.ID = ++last_id; + input->parse_input_event(ievent); + + } else if (p_dpad == 9000) { + + ievent.joy_button.button_index = JOY_DPAD_RIGHT; + ievent.ID = ++last_id; + input->parse_input_event(ievent); + + } else if (p_dpad == 13500) { + + ievent.joy_button.button_index = JOY_DPAD_RIGHT; + ievent.ID = ++last_id; + input->parse_input_event(ievent); + + ievent.joy_button.button_index = JOY_DPAD_DOWN; + ievent.ID = ++last_id; + input->parse_input_event(ievent); + + } else if (p_dpad == 18000) { + + ievent.joy_button.button_index = JOY_DPAD_DOWN; + ievent.ID = ++last_id; + input->parse_input_event(ievent); + + } else if (p_dpad == 22500) { + + ievent.joy_button.button_index = JOY_DPAD_DOWN; + ievent.ID = ++last_id; + input->parse_input_event(ievent); + + ievent.joy_button.button_index = JOY_DPAD_LEFT; + ievent.ID = ++last_id; + input->parse_input_event(ievent); + + } else if (p_dpad == 27000) { + + ievent.joy_button.button_index = JOY_DPAD_LEFT; + ievent.ID = ++last_id; + input->parse_input_event(ievent); + + } else if (p_dpad == 31500) { + + ievent.joy_button.button_index = JOY_DPAD_LEFT; + ievent.ID = ++last_id; + input->parse_input_event(ievent); + + ievent.joy_button.button_index = JOY_DPAD_UP; + ievent.ID = ++last_id; + input->parse_input_event(ievent); + }; +}; + +void OS_Windows::process_joysticks() { + + if (!main_loop) { + return; + }; + + InputEvent ievent; + + JOYINFOEX jinfo; + jinfo.dwSize = sizeof(JOYINFOEX); + jinfo.dwFlags = JOY_RETURNALL; + + for (int i=0; iparse_input_event(ievent);\ + }; + + CHECK_AXIS(0, jinfo.dwXpos); + CHECK_AXIS(1, jinfo.dwYpos); + CHECK_AXIS(2, jinfo.dwZpos); + CHECK_AXIS(3, jinfo.dwRpos); + CHECK_AXIS(4, jinfo.dwUpos); + CHECK_AXIS(5, jinfo.dwVpos); + + if (joysticks[i].last_pov != jinfo.dwPOV) { + + if (joysticks[i].last_pov != JOY_POVCENTERED) + _post_dpad(joysticks[i].last_pov, i, false); + + if (jinfo.dwPOV != JOY_POVCENTERED) + _post_dpad(jinfo.dwPOV, i, true); + + joysticks[i].last_pov = jinfo.dwPOV; + }; + + if (joysticks[i].last_buttons == jinfo.dwButtons) { + continue; + }; + + ievent.type = InputEvent::JOYSTICK_BUTTON; + for (int j=0; j<32; j++) { + + if ( (joysticks[i].last_buttons & (1<parse_input_event(ievent); + }; + }; + + joysticks[i].last_buttons = jinfo.dwButtons; + }; +}; + +void OS_Windows::initialize(const VideoMode& p_desired,int p_video_driver,int p_audio_driver) { + + + + main_loop=NULL; + outside=true; + + WNDCLASSEXW wc; + + video_mode=p_desired; + //printf("**************** desired %s, mode %s\n", p_desired.fullscreen?"true":"false", video_mode.fullscreen?"true":"false"); + RECT WindowRect; + + WindowRect.left=0; + WindowRect.right=video_mode.width; + WindowRect.top=0; + WindowRect.bottom=video_mode.height; + + memset(&wc,0,sizeof(WNDCLASSEXW)); + wc.cbSize=sizeof(WNDCLASSEXW); + wc.style= CS_HREDRAW | CS_VREDRAW | CS_OWNDC | CS_DBLCLKS; + wc.lpfnWndProc = (WNDPROC)::WndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra= 0; + //wc.hInstance = hInstance; + wc.hInstance = godot_hinstance ? godot_hinstance : GetModuleHandle(NULL); + wc.hIcon = LoadIcon(NULL, IDI_WINLOGO); + wc.hCursor = NULL;//LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = NULL; + wc.lpszMenuName = NULL; + wc.lpszClassName = L"Engine"; + + if (!RegisterClassExW(&wc)) { + MessageBox(NULL,"Failed To Register The Window Class.","ERROR",MB_OK|MB_ICONEXCLAMATION); + return; // Return + } + + + if (video_mode.fullscreen) { + + DEVMODE current; + memset(¤t,0,sizeof(current)); + EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, ¤t); + + DEVMODE dmScreenSettings; + memset(&dmScreenSettings,0,sizeof(dmScreenSettings)); + dmScreenSettings.dmSize=sizeof(dmScreenSettings); + dmScreenSettings.dmPelsWidth = video_mode.width; + dmScreenSettings.dmPelsHeight = video_mode.height; + dmScreenSettings.dmBitsPerPel = current.dmBitsPerPel; + dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT; + + LONG err = ChangeDisplaySettings(&dmScreenSettings,CDS_FULLSCREEN); + if (err!=DISP_CHANGE_SUCCESSFUL) { + + video_mode.fullscreen=false; + } + } + + DWORD dwExStyle; + DWORD dwStyle; + + if (video_mode.fullscreen) { + + dwExStyle=WS_EX_APPWINDOW; + dwStyle=WS_POPUP; + + } else { + dwExStyle=WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; + dwStyle=WS_OVERLAPPEDWINDOW; + } + + AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle); + + + char* windowid = getenv("GODOT_WINDOWID"); + if (windowid) { + + // strtoull on mingw + #ifdef MINGW_ENABLED + hWnd = (HWND)strtoull(windowid, NULL, 0); + #else + hWnd = (HWND)_strtoui64(windowid, NULL, 0); + #endif + SetLastError(0); + user_proc = (WNDPROC)GetWindowLongPtr(hWnd, GWLP_WNDPROC); + SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)(WNDPROC)::WndProc); + DWORD le = GetLastError(); + if (user_proc == 0 && le != 0) { + + printf("Error setting WNDPROC: %li\n", le); + }; + LONG_PTR proc = GetWindowLongPtr(hWnd, GWLP_WNDPROC); + + RECT rect; + if (!GetClientRect(hWnd, &rect)) { + MessageBoxW(NULL,L"Window Creation Error.",L"ERROR",MB_OK|MB_ICONEXCLAMATION); + return; // Return FALSE + }; + video_mode.width = rect.right; + video_mode.height = rect.bottom; + video_mode.fullscreen = false; + } else { + + if (!(hWnd=CreateWindowExW(dwExStyle,L"Engine",L"", dwStyle|WS_CLIPSIBLINGS|WS_CLIPCHILDREN, 0, 0,WindowRect.right-WindowRect.left,WindowRect.bottom-WindowRect.top, NULL,NULL, hInstance,NULL))) { + MessageBoxW(NULL,L"Window Creation Error.",L"ERROR",MB_OK|MB_ICONEXCLAMATION); + return; // Return FALSE + } + + + }; + +#if defined(OPENGL_ENABLED) || defined(GLES2_ENABLED) || defined(LEGACYGL_ENABLED) + gl_context = memnew( ContextGL_Win(hWnd,false) ); + gl_context->initialize(); + rasterizer = memnew( RasterizerGLES2 ); +#else + #ifdef DX9_ENABLED + rasterizer = memnew( RasterizerDX9(hWnd) ); + #endif +#endif + + visual_server = memnew( VisualServerRaster(rasterizer) ); + if (get_render_thread_mode()!=RENDER_THREAD_UNSAFE) { + + visual_server =memnew(VisualServerWrapMT(visual_server,get_render_thread_mode()==RENDER_SEPARATE_THREAD)); + } + + // + physics_server = memnew( PhysicsServerSW ); + physics_server->init(); + + physics_2d_server = memnew( Physics2DServerSW ); + physics_2d_server->init(); + + if (!is_no_window_mode_enabled()) { + ShowWindow(hWnd,SW_SHOW); // Show The Window + SetForegroundWindow(hWnd); // Slightly Higher Priority + SetFocus(hWnd); // Sets Keyboard Focus To + } + +/* + DEVMODE dmScreenSettings; // Device Mode + memset(&dmScreenSettings,0,sizeof(dmScreenSettings)); // Makes Sure Memory's Cleared + dmScreenSettings.dmSize=sizeof(dmScreenSettings); // Size Of The Devmode Structure + dmScreenSettings.dmPelsWidth = width; // Selected Screen Width + dmScreenSettings.dmPelsHeight = height; // Selected Screen Height + dmScreenSettings.dmBitsPerPel = bits; // Selected Bits Per Pixel + dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT; + if (ChangeDisplaySettings(&dmScreenSettings,CDS_FULLSCREEN)!=DISP_CHANGE_SUCCESSFUL) + + + + + */ + visual_server->init(); + + input = memnew( InputDefault ); + + AudioDriverManagerSW::get_driver(p_audio_driver)->set_singleton(); + + if (AudioDriverManagerSW::get_driver(p_audio_driver)->init()!=OK) { + + ERR_PRINT("Initializing audio failed."); + } + + sample_manager = memnew( SampleManagerMallocSW ); + audio_server = memnew( AudioServerSW(sample_manager) ); + + audio_server->init(); + + spatial_sound_server = memnew( SpatialSoundServerSW ); + spatial_sound_server->init(); + spatial_sound_2d_server = memnew( SpatialSound2DServerSW ); + spatial_sound_2d_server->init(); + + probe_joysticks(); + + TRACKMOUSEEVENT tme; + tme.cbSize=sizeof(TRACKMOUSEEVENT); + tme.dwFlags=TME_LEAVE; + tme.hwndTrack=hWnd; + tme.dwHoverTime=HOVER_DEFAULT; + TrackMouseEvent(&tme); + + + _ensure_data_dir(); + + +} + +void OS_Windows::set_clipboard(const String& p_text) { + + if (!OpenClipboard(hWnd)) { + ERR_EXPLAIN("Unable to open clipboard."); + ERR_FAIL(); + }; + EmptyClipboard(); + + HGLOBAL mem = GlobalAlloc(GMEM_MOVEABLE, (p_text.length() + 1) * sizeof(CharType)); + if (mem == NULL) { + ERR_EXPLAIN("Unable to allocate memory for clipboard contents."); + ERR_FAIL(); + }; + LPWSTR lptstrCopy = (LPWSTR)GlobalLock(mem); + memcpy(lptstrCopy, p_text.c_str(), (p_text.length() + 1) * sizeof(CharType)); + //memset((lptstrCopy + p_text.length()), 0, sizeof(CharType)); + GlobalUnlock(mem); + + SetClipboardData(CF_UNICODETEXT, mem); + + // set the CF_TEXT version (not needed?) + CharString utf8 = p_text.utf8(); + mem = GlobalAlloc(GMEM_MOVEABLE, utf8.length() + 1); + if (mem == NULL) { + ERR_EXPLAIN("Unable to allocate memory for clipboard contents."); + ERR_FAIL(); + }; + LPTSTR ptr = (LPTSTR)GlobalLock(mem); + memcpy(ptr, utf8.get_data(), utf8.length()); + ptr[utf8.length()] = 0; + GlobalUnlock(mem); + + SetClipboardData(CF_TEXT, mem); + + CloseClipboard(); +}; + +String OS_Windows::get_clipboard() const { + + String ret; + if (!OpenClipboard(hWnd)) { + ERR_EXPLAIN("Unable to open clipboard."); + ERR_FAIL_V(""); + }; + + if (IsClipboardFormatAvailable(CF_UNICODETEXT)) { + + HGLOBAL mem = GetClipboardData(CF_UNICODETEXT); + if (mem != NULL) { + + LPWSTR ptr = (LPWSTR)GlobalLock(mem); + if (ptr != NULL) { + + ret = String((CharType*)ptr); + GlobalUnlock(mem); + }; + }; + + } else if (IsClipboardFormatAvailable(CF_TEXT)) { + + HGLOBAL mem = GetClipboardData(CF_UNICODETEXT); + if (mem != NULL) { + + LPTSTR ptr = (LPTSTR)GlobalLock(mem); + if (ptr != NULL) { + + ret.parse_utf8((const char*)ptr); + GlobalUnlock(mem); + }; + }; + }; + + CloseClipboard(); + + return ret; +}; + + +void OS_Windows::delete_main_loop() { + + if (main_loop) + memdelete(main_loop); + main_loop=NULL; +} + +void OS_Windows::set_main_loop( MainLoop * p_main_loop ) { + + input->set_main_loop(p_main_loop); + main_loop=p_main_loop; +} + +void OS_Windows::finalize() { + + if(main_loop) + memdelete(main_loop); + + main_loop=NULL; + + visual_server->finish(); + memdelete(visual_server); +#ifdef OPENGL_ENABLED + if (gl_context) + memdelete(gl_context); +#endif + if (rasterizer) + memdelete(rasterizer); + + if (user_proc) { + SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)user_proc); + }; + + spatial_sound_server->finish(); + memdelete(spatial_sound_server); + spatial_sound_2d_server->finish(); + memdelete(spatial_sound_2d_server); + + //if (debugger_connection_console) { +// memdelete(debugger_connection_console); +//} + + audio_server->finish(); + memdelete(audio_server); + memdelete(sample_manager); + + memdelete(input); + + physics_server->finish(); + memdelete(physics_server); + + physics_2d_server->finish(); + memdelete(physics_2d_server); + +} +void OS_Windows::finalize_core() { + + memdelete(process_map); + + if (mempool_dynamic) + memdelete( mempool_dynamic ); + if (mempool_static) + delete mempool_static; + + + TCPServerWinsock::cleanup(); + StreamPeerWinsock::cleanup(); +} + +void OS_Windows::vprint(const char* p_format, va_list p_list, bool p_stderr) { + + char buf[16384+1]; + int len = vsnprintf(buf,16384,p_format,p_list); + if (len<=0) + return; + buf[len]=0; + + + int wlen = MultiByteToWideChar(CP_UTF8,0,buf,len,NULL,0); + if (wlen<0) + return; + + wchar_t *wbuf = (wchar_t*)malloc((len+1)*sizeof(wchar_t)); + MultiByteToWideChar(CP_UTF8,0,buf,len,wbuf,wlen); + wbuf[wlen]=0; + + if (p_stderr) + fwprintf(stderr,L"%s",wbuf); + else + wprintf(L"%s",wbuf); + +#ifdef STDOUT_FILE + //vwfprintf(stdo,p_format,p_list); +#endif + free(wbuf); + + fflush(stdout); +}; + +void OS_Windows::alert(const String& p_alert,const String& p_title) { + + if (!is_no_window_mode_enabled()) + MessageBoxW(NULL,p_alert.c_str(),p_title.c_str(),MB_OK|MB_ICONEXCLAMATION); + else + print_line("ALERT: "+p_alert); +} + +void OS_Windows::set_mouse_mode(MouseMode p_mode) { + + if (mouse_mode==p_mode) + return; + ShowCursor(p_mode==MOUSE_MODE_VISIBLE); + mouse_mode=p_mode; + if (p_mode==MOUSE_MODE_CAPTURED) { + RECT clipRect; + GetClientRect(hWnd, &clipRect); + ClientToScreen(hWnd, (POINT*) &clipRect.left); + ClientToScreen(hWnd, (POINT*) &clipRect.right); + ClipCursor(&clipRect); + SetCapture(hWnd); + center=Point2i(video_mode.width/2,video_mode.height/2); + POINT pos = { (int) center.x, (int) center.y }; + ClientToScreen(hWnd, &pos); + SetCursorPos(pos.x, pos.y); + } else { + ReleaseCapture(); + ClipCursor(NULL); + } + +} + +OS_Windows::MouseMode OS_Windows::get_mouse_mode() const{ + + return mouse_mode; +} + + + +Point2 OS_Windows::get_mouse_pos() const { + + return Point2(old_x, old_y); +} + +int OS_Windows::get_mouse_button_state() const { + + return last_button_state; +} + +void OS_Windows::set_window_title(const String& p_title) { + + SetWindowTextW(hWnd,p_title.c_str()); +} + +void OS_Windows::set_video_mode(const VideoMode& p_video_mode,int p_screen) { + + +} +OS::VideoMode OS_Windows::get_video_mode(int p_screen) const { + + return video_mode; +} +void OS_Windows::get_fullscreen_mode_list(List *p_list,int p_screen) const { + + +} + +void OS_Windows::print_error(const char* p_function,const char* p_file,int p_line,const char *p_code,const char*p_rationale,ErrorType p_type) { + + HANDLE hCon=GetStdHandle(STD_OUTPUT_HANDLE); + if (!hCon || hCon==INVALID_HANDLE_VALUE) { + if (p_rationale && p_rationale[0]) { + + print("\E[1;31;40mERROR: %s: \E[1;37;40m%s\n",p_function,p_rationale); + print("\E[0;31;40m At: %s:%i.\E[0;0;37m\n",p_file,p_line); + + } else { + print("\E[1;31;40mERROR: %s: \E[1;37;40m%s\n",p_function,p_code); + print("\E[0;31;40m At: %s:%i.\E[0;0;37m\n",p_file,p_line); + + } + } else { + + CONSOLE_SCREEN_BUFFER_INFO sbi; //original + GetConsoleScreenBufferInfo(hCon,&sbi); + + SetConsoleTextAttribute(hCon,sbi.wAttributes); + + + + uint32_t basecol=0; + switch(p_type) { + case ERR_ERROR: basecol = FOREGROUND_RED; break; + case ERR_WARNING: basecol = FOREGROUND_RED|FOREGROUND_GREEN; break; + case ERR_SCRIPT: basecol = FOREGROUND_GREEN; break; + } + + if (p_rationale && p_rationale[0]) { + + SetConsoleTextAttribute(hCon,basecol|FOREGROUND_INTENSITY); + + + switch(p_type) { + case ERR_ERROR: print("ERROR: "); break; + case ERR_WARNING: print("WARNING: "); break; + case ERR_SCRIPT: print("SCRIPT ERROR: "); break; + } + + SetConsoleTextAttribute(hCon,FOREGROUND_RED|FOREGROUND_BLUE|FOREGROUND_GREEN|FOREGROUND_INTENSITY); + print(" %s\n",p_rationale); + SetConsoleTextAttribute(hCon,basecol); + print("At: "); + SetConsoleTextAttribute(hCon,FOREGROUND_RED|FOREGROUND_BLUE|FOREGROUND_GREEN); + print(" %s:%i\n",p_file,p_line); + + + } else { + SetConsoleTextAttribute(hCon,basecol|FOREGROUND_INTENSITY); + switch(p_type) { + case ERR_ERROR: print("ERROR: %s: ",p_function); break; + case ERR_WARNING: print("WARNING: %s: ",p_function); break; + case ERR_SCRIPT: print("SCRIPT ERROR: %s: ",p_function); break; + } + SetConsoleTextAttribute(hCon,FOREGROUND_RED|FOREGROUND_BLUE|FOREGROUND_GREEN|FOREGROUND_INTENSITY); + print(" %s\n",p_code); + SetConsoleTextAttribute(hCon,basecol); + print("At: "); + SetConsoleTextAttribute(hCon,FOREGROUND_RED|FOREGROUND_BLUE|FOREGROUND_GREEN); + print(" %s:%i\n",p_file,p_line); + } + + SetConsoleTextAttribute(hCon,sbi.wAttributes); + } + +} + + +String OS_Windows::get_name() { + + return "Windows"; +} + +OS::Date OS_Windows::get_date() const { + + SYSTEMTIME systemtime; + GetSystemTime(&systemtime); + Date date; + date.day=systemtime.wDay; + date.month=Month(systemtime.wMonth); + date.weekday=Weekday(systemtime.wDayOfWeek); + date.year=systemtime.wYear; + date.dst=false; + return date; +} +OS::Time OS_Windows::get_time() const { + + SYSTEMTIME systemtime; + GetSystemTime(&systemtime); + + Time time; + time.hour=systemtime.wHour; + time.min=systemtime.wMinute; + time.sec=systemtime.wSecond; + return time; +} + +uint64_t OS_Windows::get_unix_time() const { + + FILETIME ft; + SYSTEMTIME st; + GetSystemTime(&st); + SystemTimeToFileTime(&st, &ft); + + SYSTEMTIME ep; + ep.wYear = 1970; + ep.wMonth = 1; + ep.wDayOfWeek = 4; + ep.wDay = 1; + ep.wHour = 0; + ep.wMinute = 0; + ep.wSecond = 0; + ep.wMilliseconds = 0; + FILETIME fep; + SystemTimeToFileTime(&ep, &fep); + + return (*(uint64_t*)&ft - *(uint64_t*)&fep) / 10000000; +}; + +void OS_Windows::delay_usec(uint32_t p_usec) const { + + if (p_usec < 1000) + Sleep(1); + else + Sleep(p_usec / 1000); + +} +uint64_t OS_Windows::get_ticks_usec() const { + + uint64_t ticks; + uint64_t time; + // This is the number of clock ticks since start + if( !QueryPerformanceCounter((LARGE_INTEGER *)&ticks) ) + ticks = (UINT64)timeGetTime(); + // Divide by frequency to get the time in seconds + time = ticks * 1000000L / ticks_per_second; + // Subtract the time at game start to get + // the time since the game started + time -= ticks_start; + return time; +} + + +void OS_Windows::process_events() { + + MSG msg; + + process_joysticks(); + + while(PeekMessageW(&msg,NULL,0,0,PM_REMOVE)) { + + + TranslateMessage(&msg); + DispatchMessageW(&msg); + + } + + process_key_events(); + +} + +void OS_Windows::set_cursor_shape(CursorShape p_shape) { + + ERR_FAIL_INDEX(p_shape,CURSOR_MAX); + + if (cursor_shape==p_shape) + return; + + static const LPCTSTR win_cursors[CURSOR_MAX]={ + IDC_ARROW, + IDC_IBEAM, + IDC_HAND,//finger + IDC_CROSS, + IDC_WAIT, + IDC_APPSTARTING, + IDC_ARROW, + IDC_ARROW, + IDC_NO, + IDC_SIZENS, + IDC_SIZEWE, + IDC_SIZENESW, + IDC_SIZENWSE, + IDC_SIZEALL, + IDC_SIZENS, + IDC_SIZEWE, + IDC_HELP + }; + + SetCursor(LoadCursor(hInstance,win_cursors[p_shape])); + cursor_shape=p_shape; +} + +Error OS_Windows::execute(const String& p_path, const List& p_arguments,bool p_blocking,ProcessID *r_child_id,String* r_pipe,int *r_exitcode) { + + if (p_blocking && r_pipe) { + + + String argss; + argss="\"\""+p_path+"\""; + + for(int i=0;i::Element* I = p_arguments.front(); + while (I) { + + + cmdline += " \""+I->get() + "\""; + + I = I->next(); + }; + + //cmdline+="\""; + + ProcessInfo pi; + ZeroMemory( &pi.si, sizeof(pi.si) ); + pi.si.cb = sizeof(pi.si); + ZeroMemory( &pi.pi, sizeof(pi.pi) ); + + print_line("running cmdline: "+cmdline); + + int ret = CreateProcess(NULL, (LPSTR)cmdline.utf8().get_data(), NULL, NULL, 0, NORMAL_PRIORITY_CLASS, NULL, NULL, &pi.si, &pi.pi); + ERR_FAIL_COND_V(ret == 0, ERR_CANT_FORK); + + if (p_blocking) { + + DWORD ret = WaitForSingleObject(pi.pi.hProcess, INFINITE); + if (r_exitcode) + *r_exitcode=ret; + + } else { + + ProcessID pid = pi.pi.dwProcessId; + if (r_child_id) { + *r_child_id = pid; + }; + process_map->insert(pid, pi); + }; + return OK; +}; + +Error OS_Windows::kill(const ProcessID& p_pid) { + + HANDLE h; + + if (process_map->has(p_pid)) { + h = (*process_map)[p_pid].pi.hProcess; + process_map->erase(p_pid); + } else { + + ERR_FAIL_COND_V(!process_map->has(p_pid), FAILED); + }; + + int ret = TerminateProcess(h, 0); + + return ret != 0?OK:FAILED; +}; + +Error OS_Windows::set_cwd(const String& p_cwd) { + + if (_wchdir(p_cwd.c_str())!=0) + return ERR_CANT_OPEN; + + return OK; +} + +void OS_Windows::set_icon(const Image& p_icon) { + + + Image icon=p_icon; + if (icon.get_format()!=Image::FORMAT_RGBA) + icon.convert(Image::FORMAT_RGBA); + int w = icon.get_width(); + int h = icon.get_height(); + + /* Create temporary bitmap buffer */ + int icon_len = 40 + h * w * 4; + BYTE *icon_bmp = (BYTE*)alloca(icon_len); + + encode_uint32(40,&icon_bmp[0]); + encode_uint32(w,&icon_bmp[4]); + encode_uint32(h*2,&icon_bmp[8]); + encode_uint16(1,&icon_bmp[12]); + encode_uint16(32,&icon_bmp[14]); + encode_uint32(BI_RGB,&icon_bmp[16]); + encode_uint32(w*h*4,&icon_bmp[20]); + encode_uint32(0,&icon_bmp[24]); + encode_uint32(0,&icon_bmp[28]); + encode_uint32(0,&icon_bmp[32]); + encode_uint32(0,&icon_bmp[36]); + + uint8_t *wr=&icon_bmp[40]; + DVector::Read r= icon.get_data().read(); + + for(int i=0;ilocale) { + + if (wl->main_lang==lang && wl->sublang==SUBLANG_NEUTRAL) + neutral=wl->locale; + + if (lang==wl->main_lang && sublang==wl->sublang) + return wl->locale; + + + wl++; + } + + if (neutral!="") + return neutral; + + return "en"; +} + +void OS_Windows::release_rendering_thread() { + + gl_context->release_current(); + +} + +void OS_Windows::make_rendering_thread() { + + gl_context->make_current(); +} + +void OS_Windows::swap_buffers() { + + gl_context->swap_buffers(); +} + + +void OS_Windows::run() { + + if (!main_loop) + return; + + main_loop->init(); + + uint64_t last_ticks=get_ticks_usec(); + + int frames=0; + uint64_t frame=0; + + while (!force_quit) { + + process_events(); // get rid of pending events + if (Main::iteration()==true) + break; + }; + + main_loop->finish(); + +} + + + +MainLoop *OS_Windows::get_main_loop() const { + + return main_loop; +} + + +String OS_Windows::get_data_dir() const { + + String an = Globals::get_singleton()->get("application/name"); + if (an!="") { + + if (has_environment("APPDATA")) { + + return OS::get_singleton()->get_environment("APPDATA")+"\\"+an; + } + } + + return Globals::get_singleton()->get_resource_path(); + + +} + + +OS_Windows::OS_Windows(HINSTANCE _hInstance) { + + key_event_pos=0; + force_quit=false; + alt_mem=false; + gr_mem=false; + shift_mem=false; + control_mem=false; + meta_mem=false; + minimized = false; + + hInstance=_hInstance; + pressrc=0; + old_invalid=true; + last_id=0; + mouse_mode=MOUSE_MODE_VISIBLE; +#ifdef STDOUT_FILE + stdo=fopen("stdout.txt","wb"); +#endif + user_proc = NULL; + +#ifdef RTAUDIO_ENABLED + AudioDriverManagerSW::add_driver(&driver_rtaudio); +#endif + +} + + +OS_Windows::~OS_Windows() +{ +#ifdef STDOUT_FILE + fclose(stdo); +#endif +} + + diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h new file mode 100644 index 00000000000..5587d89763d --- /dev/null +++ b/platform/windows/os_windows.h @@ -0,0 +1,257 @@ +/*************************************************************************/ +/* os_windows.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 OS_WINDOWS_H +#define OS_WINDOWS_H + +#define WINVER 0x0500 + +#include "os/input.h" +#include "os/os.h" +#include "context_gl_win.h" +#include "servers/visual_server.h" +#include "servers/visual/rasterizer.h" +#include "servers/physics/physics_server_sw.h" + +#include "servers/audio/audio_server_sw.h" +#include "servers/audio/sample_manager_sw.h" +#include "drivers/rtaudio/audio_driver_rtaudio.h" +#include "servers/spatial_sound/spatial_sound_server_sw.h" +#include "servers/spatial_sound_2d/spatial_sound_2d_server_sw.h" +#include "drivers/unix/ip_unix.h" +#include "servers/physics_2d/physics_2d_server_sw.h" + + +#include + +#include "key_mapping_win.h" +#include +#include + +#include +#include +/** + @author Juan Linietsky +*/ +class OS_Windows : public OS { + + enum { + JOYSTICKS_MAX = 8, + JOY_AXIS_COUNT = 6, + MAX_JOY_AXIS = 32768, // I've no idea + KEY_EVENT_BUFFER_SIZE=512 + }; + + FILE *stdo; + + + struct KeyEvent { + + InputModifierState mod_state; + UINT uMsg; + WPARAM wParam; + LPARAM lParam; + + }; + + KeyEvent key_event_buffer[KEY_EVENT_BUFFER_SIZE]; + int key_event_pos; + + + uint64_t ticks_start; + uint64_t ticks_per_second; + + bool minimized; + bool old_invalid; + bool outside; + int old_x,old_y; + Point2i center; + unsigned int last_id; +#if defined(OPENGL_ENABLED) || defined(LEGACYGL_ENABLED) || defined(GLES2_ENABLED) + ContextGL_Win *gl_context; +#endif + VisualServer *visual_server; + Rasterizer *rasterizer; + PhysicsServer *physics_server; + Physics2DServer *physics_2d_server; + int pressrc; + HDC hDC; // Private GDI Device Context + HINSTANCE hInstance; // Holds The Instance Of The Application + HWND hWnd; + + struct Joystick { + + bool attached; + + DWORD last_axis[JOY_AXIS_COUNT]; + DWORD last_buttons; + DWORD last_pov; + + Joystick() { + attached = false; + for (int i=0; i* process_map; + +public: + LRESULT WndProc(HWND hWnd,UINT uMsg, WPARAM wParam, LPARAM lParam); + + + void print_error(const char* p_function,const char* p_file,int p_line,const char *p_code,const char*p_rationale,ErrorType p_type); + + virtual void vprint(const char *p_format, va_list p_list, bool p_stderr=false); + virtual void alert(const String& p_alert,const String& p_title="ALERT!"); + String get_stdin_string(bool p_block); + + void set_mouse_mode(MouseMode p_mode); + MouseMode get_mouse_mode() const; + + virtual Point2 get_mouse_pos() const; + virtual int get_mouse_button_state() const; + virtual void set_window_title(const String& p_title); + + virtual void set_video_mode(const VideoMode& p_video_mode,int p_screen=0); + virtual VideoMode get_video_mode(int p_screen=0) const; + virtual void get_fullscreen_mode_list(List *p_list,int p_screen=0) const; + + virtual MainLoop *get_main_loop() const; + + virtual String get_name(); + + virtual Date get_date() const; + virtual Time get_time() const; + virtual uint64_t get_unix_time() const; + + virtual bool can_draw() const; + virtual Error set_cwd(const String& p_cwd); + + virtual void delay_usec(uint32_t p_usec) const; + virtual uint64_t get_ticks_usec() const; + + virtual Error execute(const String& p_path, const List& p_arguments,bool p_blocking,ProcessID *r_child_id=NULL,String* r_pipe=NULL,int *r_exitcode=NULL); + virtual Error kill(const ProcessID& p_pid); + + virtual bool has_environment(const String& p_var) const; + virtual String get_environment(const String& p_var) const; + + virtual void set_clipboard(const String& p_text); + virtual String get_clipboard() const; + + void set_cursor_shape(CursorShape p_shape); + void set_icon(const Image& p_icon); + + + virtual String get_locale() const; + + virtual void move_window_to_foreground(); + virtual String get_data_dir() const; + + virtual void release_rendering_thread(); + virtual void make_rendering_thread(); + virtual void swap_buffers(); + + void run(); + + virtual bool get_swap_ok_cancel() { return true; } + + OS_Windows(HINSTANCE _hInstance); + ~OS_Windows(); + +}; + +#endif diff --git a/platform/windows/platform_config.h b/platform/windows/platform_config.h new file mode 100644 index 00000000000..7bc3e428335 --- /dev/null +++ b/platform/windows/platform_config.h @@ -0,0 +1,35 @@ +/*************************************************************************/ +/* platform_config.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. */ +/*************************************************************************/ +#include +//#else +//#include +//#endif +#define GLES2_INCLUDE_H "gl_context/glew.h" +#define GLES1_INCLUDE_H "gl_context/glew.h" + diff --git a/platform/windows/stream_peer_winsock.cpp b/platform/windows/stream_peer_winsock.cpp new file mode 100644 index 00000000000..2c3a8db7b1c --- /dev/null +++ b/platform/windows/stream_peer_winsock.cpp @@ -0,0 +1,368 @@ +/*************************************************************************/ +/* stream_peer_winsock.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. */ +/*************************************************************************/ +#ifdef WINDOWS_ENABLED + +#include "stream_peer_winsock.h" + +#include + +int winsock_refcount = 0; + +static void set_addr_in(struct sockaddr_in& their_addr, const IP_Address& p_host, uint16_t p_port) { + + their_addr.sin_family = AF_INET; // host byte order + their_addr.sin_port = htons(p_port); // short, network byte order + their_addr.sin_addr = *((struct in_addr*)&p_host.host); + memset(&(their_addr.sin_zero), '\0', 8); +}; + +StreamPeerTCP* StreamPeerWinsock::_create() { + + return memnew(StreamPeerWinsock); +}; + +void StreamPeerWinsock::make_default() { + + StreamPeerTCP::_create = StreamPeerWinsock::_create; + + if (winsock_refcount == 0) { + WSADATA data; + WSAStartup(MAKEWORD(2,2), &data); + }; + ++winsock_refcount; +}; + +void StreamPeerWinsock::cleanup() { + + --winsock_refcount; + if (winsock_refcount == 0) { + + WSACleanup(); + }; +}; + + +Error StreamPeerWinsock::_block(int p_sockfd, bool p_read, bool p_write) const { + + fd_set read, write; + FD_ZERO(&read); + FD_ZERO(&write); + + if (p_read) + FD_SET(p_sockfd, &read); + if (p_write) + FD_SET(p_sockfd, &write); + + int ret = select(p_sockfd + 1, &read, &write, NULL, NULL); // block forever + return ret < 0 ? FAILED : OK; +}; + +Error StreamPeerWinsock::_poll_connection(bool p_block) const { + + ERR_FAIL_COND_V(status != STATUS_CONNECTING || sockfd == INVALID_SOCKET, FAILED); + + if (p_block) { + + _block(sockfd, false, true); + }; + + struct sockaddr_in their_addr; + set_addr_in(their_addr, peer_host, peer_port); + + if (::connect(sockfd, (struct sockaddr *)&their_addr,sizeof(struct sockaddr)) == SOCKET_ERROR) { + + int err = WSAGetLastError(); + if (err == WSAEISCONN) { + status = STATUS_CONNECTED; + return OK; + }; + + return OK; + } else { + + status = STATUS_CONNECTED; + return OK; + }; + + return OK; +}; + +Error StreamPeerWinsock::write(const uint8_t* p_data,int p_bytes, int &r_sent, bool p_block) { + + if (status == STATUS_NONE || status == STATUS_ERROR) { + + return FAILED; + }; + + if (status != STATUS_CONNECTED) { + + if (_poll_connection(p_block) != OK) { + + return FAILED; + }; + + if (status != STATUS_CONNECTED) { + r_sent = 0; + return OK; + }; + }; + + int data_to_send = p_bytes; + const uint8_t *offset = p_data; + if (sockfd == -1) return FAILED; + errno = 0; + int total_sent = 0; + + while (data_to_send) { + + int sent_amount = send(sockfd, (const char*)offset, data_to_send, 0); + //printf("Sent TCP data of %d bytes, errno %d\n", sent_amount, errno); + + if (sent_amount == -1) { + + if (WSAGetLastError() != WSAEWOULDBLOCK) { + + perror("shit?"); + disconnect(); + ERR_PRINT("Server disconnected!\n"); + return FAILED; + }; + + if (!p_block) { + r_sent = total_sent; + return OK; + }; + + _block(sockfd, false, true); + } else { + + data_to_send -= sent_amount; + offset += sent_amount; + total_sent += sent_amount; + }; + } + + r_sent = total_sent; + + return OK; +}; + +Error StreamPeerWinsock::read(uint8_t* p_buffer, int p_bytes,int &r_received, bool p_block) { + + if (!is_connected()) { + + return FAILED; + }; + + if (status != STATUS_CONNECTED) { + + if (_poll_connection(p_block) != OK) { + + return FAILED; + }; + + if (status != STATUS_CONNECTED) { + r_received = 0; + return OK; + }; + }; + + int to_read = p_bytes; + int total_read = 0; + errno = 0; + + while (to_read) { + + int read = recv(sockfd, (char*)p_buffer + total_read, to_read, 0); + + if (read == -1) { + + if (WSAGetLastError() != WSAEWOULDBLOCK) { + + perror("shit?"); + disconnect(); + ERR_PRINT("Server disconnected!\n"); + return FAILED; + }; + + if (!p_block) { + + r_received = total_read; + return OK; + }; + _block(sockfd, true, false); + } else if (read == 0) { + disconnect(); + return ERR_FILE_EOF; + } else { + + to_read -= read; + total_read += read; + }; + }; + + r_received = total_read; + + return OK; +}; + +Error StreamPeerWinsock::put_data(const uint8_t* p_data,int p_bytes) { + + int total; + return write(p_data, p_bytes, total, true); +}; + +Error StreamPeerWinsock::put_partial_data(const uint8_t* p_data,int p_bytes, int &r_sent) { + + return write(p_data, p_bytes, r_sent, false); +}; + +Error StreamPeerWinsock::get_data(uint8_t* p_buffer, int p_bytes) { + + int total; + return read(p_buffer, p_bytes, total, true); +}; + +Error StreamPeerWinsock::get_partial_data(uint8_t* p_buffer, int p_bytes,int &r_received) { + + return read(p_buffer, p_bytes, r_received, false); +}; + +StreamPeerTCP::Status StreamPeerWinsock::get_status() const { + + if (status == STATUS_CONNECTING) { + _poll_connection(false); + }; + + return status; +}; + + +bool StreamPeerWinsock::is_connected() const { + + if (status == STATUS_NONE || status == STATUS_ERROR) { + + return false; + }; + if (status != STATUS_CONNECTED) { + return true; + }; + + return (sockfd!=INVALID_SOCKET); +}; + +void StreamPeerWinsock::disconnect() { + + if (sockfd != INVALID_SOCKET) + closesocket(sockfd); + sockfd=INVALID_SOCKET; + + status = STATUS_NONE; + + peer_host = IP_Address(); + peer_port = 0; +}; + +void StreamPeerWinsock::set_socket(int p_sockfd, IP_Address p_host, int p_port) { + + sockfd = p_sockfd; + status = STATUS_CONNECTING; + peer_host = p_host; + peer_port = p_port; +}; + +Error StreamPeerWinsock::connect(const IP_Address& p_host, uint16_t p_port) { + + ERR_FAIL_COND_V( p_host.host == 0, ERR_INVALID_PARAMETER); + + if ((sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) { + ERR_PRINT("Socket creation failed!"); + disconnect(); + //perror("socket"); + return FAILED; + }; + + unsigned long par = 1; + if (ioctlsocket(sockfd, FIONBIO, &par)) { + perror("setting non-block mode"); + disconnect(); + return FAILED; + }; + + struct sockaddr_in their_addr; + set_addr_in(their_addr, p_host, p_port); + + if (::connect(sockfd, (struct sockaddr *)&their_addr,sizeof(struct sockaddr)) == SOCKET_ERROR) { + + if (WSAGetLastError() != WSAEWOULDBLOCK) { + ERR_PRINT("Connection to remote host failed!"); + disconnect(); + return FAILED; + }; + status = STATUS_CONNECTING; + } else { + status = STATUS_CONNECTED; + }; + + peer_host = p_host; + peer_port = p_port; + + return OK; +}; + +void StreamPeerWinsock::set_nodelay(bool p_enabled) { + + +} + + +IP_Address StreamPeerWinsock::get_connected_host() const { + + return peer_host; +}; + +uint16_t StreamPeerWinsock::get_connected_port() const { + + return peer_port; +}; + +StreamPeerWinsock::StreamPeerWinsock() { + + sockfd = INVALID_SOCKET; + status = STATUS_NONE; + peer_port = 0; +}; + +StreamPeerWinsock::~StreamPeerWinsock() { + + disconnect(); +}; + + +#endif diff --git a/platform/windows/stream_peer_winsock.h b/platform/windows/stream_peer_winsock.h new file mode 100644 index 00000000000..14dd5f0bb13 --- /dev/null +++ b/platform/windows/stream_peer_winsock.h @@ -0,0 +1,90 @@ +/*************************************************************************/ +/* stream_peer_winsock.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. */ +/*************************************************************************/ +#ifdef WINDOWS_ENABLED + +#ifndef STREAM_PEER_WINSOCK_H +#define STREAM_PEER_WINSOCK_H + +#include "error_list.h" + +#include "core/io/ip_address.h" +#include "core/io/stream_peer_tcp.h" + +class StreamPeerWinsock : public StreamPeerTCP { + +protected: + + mutable Status status; + + int sockfd; + + Error _block(int p_sockfd, bool p_read, bool p_write) const; + + Error _poll_connection(bool p_block) const; + + IP_Address peer_host; + int peer_port; + + Error write(const uint8_t* p_data,int p_bytes, int &r_sent, bool p_block); + Error read(uint8_t* p_buffer, int p_bytes,int &r_received, bool p_block); + + static StreamPeerTCP* _create(); + +public: + + virtual Error connect(const IP_Address& p_host, uint16_t p_port); + + virtual Error put_data(const uint8_t* p_data,int p_bytes); + virtual Error put_partial_data(const uint8_t* p_data,int p_bytes, int &r_sent); + + virtual Error get_data(uint8_t* p_buffer, int p_bytes); + virtual Error get_partial_data(uint8_t* p_buffer, int p_bytes,int &r_received); + + void set_socket(int p_sockfd, IP_Address p_host, int p_port); + + virtual IP_Address get_connected_host() const; + virtual uint16_t get_connected_port() const; + + virtual bool is_connected() const; + virtual Status get_status() const; + virtual void disconnect(); + + static void make_default(); + static void cleanup(); + + virtual void set_nodelay(bool p_enabled); + + + StreamPeerWinsock(); + ~StreamPeerWinsock(); +}; + +#endif // TCP_SOCKET_POSIX_H + +#endif diff --git a/platform/windows/tcp_server_winsock.cpp b/platform/windows/tcp_server_winsock.cpp new file mode 100644 index 00000000000..7b35bcc7ad1 --- /dev/null +++ b/platform/windows/tcp_server_winsock.cpp @@ -0,0 +1,166 @@ +/*************************************************************************/ +/* tcp_server_winsock.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 "tcp_server_winsock.h" + +#include "stream_peer_winsock.h" + +#include + +extern int winsock_refcount; + +TCP_Server* TCPServerWinsock::_create() { + + return memnew(TCPServerWinsock); +}; + +void TCPServerWinsock::make_default() { + + TCP_Server::_create = TCPServerWinsock::_create; + + if (winsock_refcount == 0) { + WSADATA data; + WSAStartup(MAKEWORD(2,2), &data); + }; + ++winsock_refcount; +}; + +void TCPServerWinsock::cleanup() { + + --winsock_refcount; + if (winsock_refcount == 0) { + + WSACleanup(); + }; +}; + + +Error TCPServerWinsock::listen(uint16_t p_port,const List *p_accepted_hosts) { + + int sockfd; + sockfd = socket(AF_INET, SOCK_STREAM, 0); + ERR_FAIL_COND_V(sockfd == INVALID_SOCKET, FAILED); + + unsigned long par = 1; + if (ioctlsocket(sockfd, FIONBIO, &par)) { + perror("setting non-block mode"); + stop(); + return FAILED; + }; + + struct sockaddr_in my_addr; + my_addr.sin_family = AF_INET; // host byte order + my_addr.sin_port = htons(p_port); // short, network byte order + my_addr.sin_addr.s_addr = INADDR_ANY; // automatically fill with my IP TODO: use p_accepted_hosts + memset(my_addr.sin_zero, '\0', sizeof my_addr.sin_zero); + + if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof my_addr) != SOCKET_ERROR) { + + if (::listen(sockfd, SOMAXCONN) == SOCKET_ERROR) { + + closesocket(sockfd); + ERR_FAIL_V(FAILED); + }; + }; + + if (listen_sockfd != INVALID_SOCKET) { + + stop(); + }; + + listen_sockfd = sockfd; + + return OK; +}; + +bool TCPServerWinsock::is_connection_available() const { + + if (listen_sockfd == -1) { + return false; + }; + + timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 0; + + fd_set pfd; + FD_ZERO(&pfd); + FD_SET(listen_sockfd, &pfd); + + int ret = select(listen_sockfd + 1, &pfd, NULL, NULL, &timeout); + ERR_FAIL_COND_V(ret < 0, 0); + + if (ret && (FD_ISSET(listen_sockfd, &pfd))) { + + return true; + }; + + return false; +}; + + +Ref TCPServerWinsock::take_connection() { + + if (!is_connection_available()) { + return NULL; + }; + + struct sockaddr_in their_addr; + int sin_size = sizeof(their_addr); + int fd = accept(listen_sockfd, (struct sockaddr *)&their_addr, &sin_size); + ERR_FAIL_COND_V(fd == INVALID_SOCKET, NULL); + + Ref conn = memnew(StreamPeerWinsock); + IP_Address ip; + ip.host = (uint32_t)their_addr.sin_addr.s_addr; + + conn->set_socket(fd, ip, ntohs(their_addr.sin_port)); + + return conn; +}; + +void TCPServerWinsock::stop() { + + if (listen_sockfd != INVALID_SOCKET) { + closesocket(listen_sockfd); + }; + + listen_sockfd = -1; +}; + + +TCPServerWinsock::TCPServerWinsock() { + + listen_sockfd = INVALID_SOCKET; +}; + +TCPServerWinsock::~TCPServerWinsock() { + + stop(); +}; + diff --git a/platform/windows/tcp_server_winsock.h b/platform/windows/tcp_server_winsock.h new file mode 100644 index 00000000000..2d54b6ce40f --- /dev/null +++ b/platform/windows/tcp_server_winsock.h @@ -0,0 +1,55 @@ +/*************************************************************************/ +/* tcp_server_winsock.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 TCP_SERVER_WINSOCK_H +#define TCP_SERVER_WINSOCK_H + +#include "core/io/tcp_server.h" + +class TCPServerWinsock : public TCP_Server { + + int listen_sockfd; + + static TCP_Server* _create(); + +public: + + virtual Error listen(uint16_t p_port,const List *p_accepted_hosts=NULL); + virtual bool is_connection_available() const; + virtual Ref take_connection(); + + virtual void stop(); //stop listening + + static void make_default(); + static void cleanup(); + + TCPServerWinsock(); + ~TCPServerWinsock(); +}; + +#endif diff --git a/platform/x11/SCsub b/platform/x11/SCsub new file mode 100644 index 00000000000..0644ba52ecc --- /dev/null +++ b/platform/x11/SCsub @@ -0,0 +1,13 @@ +Import('env') + + +common_x11=[\ + "context_gl_x11.cpp",\ + "os_x11.cpp",\ + "key_mapping_x11.cpp",\ +] + +if env["target"]=="release": + env.Program('#bin/godot_rel',['godot_x11.cpp']+common_x11) +else: + env.Program('#bin/godot',['godot_x11.cpp']+common_x11) diff --git a/platform/x11/context_gl_x11.cpp b/platform/x11/context_gl_x11.cpp new file mode 100644 index 00000000000..12708f52e2c --- /dev/null +++ b/platform/x11/context_gl_x11.cpp @@ -0,0 +1,197 @@ +/*************************************************************************/ +/* context_gl_x11.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 "context_gl_x11.h" + +#ifdef X11_ENABLED +#if defined(OPENGL_ENABLED) || defined(LEGACYGL_ENABLED) +#include +#include +#include + +#include + +#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092 + +typedef GLXContext (*GLXCREATECONTEXTATTRIBSARBPROC)(Display*, GLXFBConfig, GLXContext, Bool, const int*); + +struct ContextGL_X11_Private { + + ::GLXContext glx_context; +}; + + +void ContextGL_X11::release_current() { + + glXMakeCurrent(x11_display, None, NULL); +} + +void ContextGL_X11::make_current() { + + glXMakeCurrent(x11_display, x11_window, p->glx_context); +} +void ContextGL_X11::swap_buffers() { + + glXSwapBuffers(x11_display,x11_window); +} +/* +static GLWrapperFuncPtr wrapper_get_proc_address(const char* p_function) { + + //print_line(String()+"getting proc of: "+p_function); + GLWrapperFuncPtr func=(GLWrapperFuncPtr)glXGetProcAddress( (const GLubyte*) p_function); + if (!func) { + print_line("Couldn't find function: "+String(p_function)); + } + + return func; + +}*/ + +Error ContextGL_X11::initialize() { + + + GLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB = NULL; + +// const char *extensions = glXQueryExtensionsString(x11_display, DefaultScreen(x11_display)); + + glXCreateContextAttribsARB = (GLXCREATECONTEXTATTRIBSARBPROC)glXGetProcAddress((const GLubyte*)"glXCreateContextAttribsARB"); + + ERR_FAIL_COND_V( !glXCreateContextAttribsARB, ERR_UNCONFIGURED ); + + + static int visual_attribs[] = { + GLX_RENDER_TYPE, GLX_RGBA_BIT, + GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, + GLX_DOUBLEBUFFER, true, + GLX_RED_SIZE, 1, + GLX_GREEN_SIZE, 1, + GLX_BLUE_SIZE, 1, + GLX_DEPTH_SIZE,0, + None + }; + + int fbcount; + GLXFBConfig *fbc = glXChooseFBConfig(x11_display, DefaultScreen(x11_display), visual_attribs, &fbcount); + ERR_FAIL_COND_V(!fbc,ERR_UNCONFIGURED); + + XVisualInfo *vi = glXGetVisualFromFBConfig(x11_display, fbc[0]); + + XSetWindowAttributes swa; + + swa.colormap = XCreateColormap(x11_display, RootWindow(x11_display, vi->screen), vi->visual, AllocNone); + swa.border_pixel = 0; + swa.event_mask = StructureNotifyMask; + + /* + char* windowid = getenv("GODOT_WINDOWID"); + if (windowid) { + + //freopen("/home/punto/stdout", "w", stdout); + //reopen("/home/punto/stderr", "w", stderr); + x11_window = atol(windowid); + } else { + */ + x11_window = XCreateWindow(x11_display, RootWindow(x11_display, vi->screen), 0, 0, OS::get_singleton()->get_video_mode().width, OS::get_singleton()->get_video_mode().height, 0, vi->depth, InputOutput, vi->visual, CWBorderPixel|CWColormap|CWEventMask, &swa); + ERR_FAIL_COND_V(!x11_window,ERR_UNCONFIGURED); + XMapWindow(x11_display, x11_window); + while(true) { + // wait for mapnotify (window created) + XEvent e; + XNextEvent(x11_display, &e); + if (e.type == MapNotify) + break; + } + //}; + + if (!opengl_3_context) { + //oldstyle context: + p->glx_context = glXCreateContext(x11_display, vi, 0, GL_TRUE); + } else { + static int context_attribs[] = { + GLX_CONTEXT_MAJOR_VERSION_ARB, 3, + GLX_CONTEXT_MINOR_VERSION_ARB, 0, + None + }; + + p->glx_context = glXCreateContextAttribsARB(x11_display, fbc[0], NULL, true, context_attribs); + ERR_FAIL_COND_V(!p->glx_context,ERR_UNCONFIGURED); + } + + glXMakeCurrent(x11_display, x11_window, p->glx_context); + + /* + glWrapperInit(wrapper_get_proc_address); + glFlush(); + + glXSwapBuffers(x11_display,x11_window); +*/ + //glXMakeCurrent(x11_display, None, NULL); + + return OK; +} + +int ContextGL_X11::get_window_width() { + + XWindowAttributes xwa; + XGetWindowAttributes(x11_display,x11_window,&xwa); + + return xwa.width; +} +int ContextGL_X11::get_window_height() { + XWindowAttributes xwa; + XGetWindowAttributes(x11_display,x11_window,&xwa); + + return xwa.height; + +} + + +ContextGL_X11::ContextGL_X11(::Display *p_x11_display,::Window &p_x11_window,const OS::VideoMode& p_default_video_mode,bool p_opengl_3_context) : x11_window(p_x11_window) { + + default_video_mode=p_default_video_mode; + x11_display=p_x11_display; + + opengl_3_context=p_opengl_3_context; + + double_buffer=false; + direct_render=false; + glx_minor=glx_major=0; + p = memnew( ContextGL_X11_Private ); + p->glx_context=0; +} + + +ContextGL_X11::~ContextGL_X11() { + + memdelete( p ); +} + + +#endif +#endif diff --git a/platform/x11/context_gl_x11.h b/platform/x11/context_gl_x11.h new file mode 100644 index 00000000000..20a858bcdd5 --- /dev/null +++ b/platform/x11/context_gl_x11.h @@ -0,0 +1,76 @@ +/*************************************************************************/ +/* context_gl_x11.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 CONTEXT_GL_X11_H +#define CONTEXT_GL_X11_H + +/** + @author Juan Linietsky +*/ +#ifdef X11_ENABLED + +#if defined(OPENGL_ENABLED) || defined(LEGACYGL_ENABLED) + + + +#include "os/os.h" +#include "drivers/gl_context/context_gl.h" +#include + +struct ContextGL_X11_Private; + +class ContextGL_X11 : public ContextGL { + + ContextGL_X11_Private *p; + OS::VideoMode default_video_mode; +// ::Colormap x11_colormap; + ::Display *x11_display; + ::Window& x11_window; + bool double_buffer; + bool direct_render; + int glx_minor,glx_major; + bool opengl_3_context; +public: + + virtual void release_current(); + virtual void make_current(); + virtual void swap_buffers(); + virtual int get_window_width(); + virtual int get_window_height(); + + virtual Error initialize(); + + ContextGL_X11(::Display *p_x11_display,::Window &p_x11_window,const OS::VideoMode& p_default_video_mode,bool p_opengl_3_context); + ~ContextGL_X11(); + +}; + +#endif + +#endif +#endif diff --git a/platform/x11/detect.py b/platform/x11/detect.py new file mode 100644 index 00000000000..38f697ef107 --- /dev/null +++ b/platform/x11/detect.py @@ -0,0 +1,146 @@ + +import os +import sys + + +def is_active(): + return True + +def get_name(): + return "X11" + + +def can_build(): + + if (os.name!="posix"): + return False + + if sys.platform == "darwin": + return False # no x11 on mac for now + + errorval=os.system("pkg-config --version > /dev/null") + + if (errorval): + print("pkg-config not found.. x11 disabled.") + return False + + x11_error=os.system("pkg-config x11 --modversion > /dev/null ") + if (x11_error): + print("X11 not found.. x11 disabled.") + return False + + x11_error=os.system("pkg-config xcursor --modversion > /dev/null ") + if (x11_error): + print("xcursor not found.. x11 disabled.") + return False + + return True # X11 enabled + +def get_opts(): + + return [ + ('use_llvm','Use llvm compiler','no'), + ('use_sanitizer','Use llvm compiler sanitize address','no'), + ('force_32_bits','Force 32 bits binary','no') + ] + +def get_flags(): + + return [ + ('opengl', 'no'), + ('legacygl', 'yes'), + ('builtin_zlib', 'no'), + ] + + + +def configure(env): + + env.Append(CPPPATH=['#platform/x11']) + if (env["use_llvm"]=="yes"): + env["CC"]="clang" + env["CXX"]="clang++" + env["LD"]="clang++" + if (env["use_sanitizer"]=="yes"): + env.Append(CXXFLAGS=['-fsanitize=address','-fno-omit-frame-pointer']) + env.Append(LINKFLAGS=['-fsanitize=address']) + + + + if (env["tools"]=="no"): + #no tools suffix + env['OBJSUFFIX'] = ".nt"+env['OBJSUFFIX'] + env['LIBSUFFIX'] = ".nt"+env['LIBSUFFIX'] + + + if (env["target"]=="release"): + + env.Append(CCFLAGS=['-O2','-ffast-math','-fomit-frame-pointer']) + env['OBJSUFFIX'] = "_opt"+env['OBJSUFFIX'] + env['LIBSUFFIX'] = "_opt"+env['LIBSUFFIX'] + + elif (env["target"]=="release_debug"): + + env.Append(CCFLAGS=['-O2','-ffast-math','-DDEBUG_ENABLED']) + env['OBJSUFFIX'] = "_optd"+env['OBJSUFFIX'] + env['LIBSUFFIX'] = "_optd"+env['LIBSUFFIX'] + + +# env.Append(CCFLAGS=['-Os','-ffast-math','-fomit-frame-pointer']) +#does not seem to have much effect +# env.Append(CCFLAGS=['-fno-default-inline']) +#recommended by wxwidgets +# env.Append(CCFLAGS=['-ffunction-sections','-fdata-sections']) +# env.Append(LINKFLAGS=['-Wl','--gc-sections']) + + elif (env["target"]=="debug"): + + env.Append(CCFLAGS=['-g2', '-Wall','-DDEBUG_ENABLED','-DDEBUG_MEMORY_ENABLED']) +#does not seem to have much effect +# env.Append(CCFLAGS=['-fno-default-inline']) +#recommended by wxwidgets +# env.Append(CCFLAGS=['-ffunction-sections','-fdata-sections']) +# env.Append(LINKFLAGS=['-Wl','--gc-sections']) + + elif (env["target"]=="debug_light"): + + env.Append(CCFLAGS=['-g1', '-Wall','-DDEBUG_ENABLED','-DDEBUG_MEMORY_ENABLED']) + + + elif (env["target"]=="profile"): + + env.Append(CCFLAGS=['-g','-pg']) + env.Append(LINKFLAGS=['-pg']) + + env.ParseConfig('pkg-config x11 --cflags --libs') + env.ParseConfig('pkg-config xcursor --cflags --libs') + + + env.ParseConfig('pkg-config freetype2 --cflags --libs') + env.Append(CCFLAGS=['-DFREETYPE_ENABLED']) + + + if env['opengl'] == 'yes': + env.Append(CPPFLAGS=['-DOPENGL_ENABLED','-DGLEW_ENABLED']) + #env.Append(CPPFLAGS=["-DRTAUDIO_ENABLED"]) + env.Append(CPPFLAGS=["-DALSA_ENABLED"]) + env.Append(CPPFLAGS=['-DX11_ENABLED','-DUNIX_ENABLED','-DGLES2_ENABLED','-DGLES1_ENABLED','-DGLES_OVER_GL']) +# env.Append(CPPFLAGS=['-DX11_ENABLED','-DUNIX_ENABLED','-DGLES2_ENABLED','-DGLES_OVER_GL']) + env.Append(LIBS=['GL', 'GLU', 'pthread','asound','z']) #TODO detect linux/BSD! + #env.Append(CPPFLAGS=['-DMPC_FIXED_POINT']) + if (env["force_32_bits"]=="yes"): + env.Append(CPPFLAGS=['-m32']) + env.Append(LINKFLAGS=['-m32','-L/usr/lib/i386-linux-gnu']) + + if (env["CXX"]=="clang++"): + env.Append(CPPFLAGS=['-DTYPED_METHOD_BIND']) + env["CC"]="clang" + env["LD"]="clang++" + + import methods + + env.Append( BUILDERS = { 'GLSL120' : env.Builder(action = methods.build_legacygl_headers, suffix = 'glsl.h',src_suffix = '.glsl') } ) + env.Append( BUILDERS = { 'GLSL' : env.Builder(action = methods.build_glsl_headers, suffix = 'glsl.h',src_suffix = '.glsl') } ) + env.Append( BUILDERS = { 'GLSL120GLES' : env.Builder(action = methods.build_gles2_headers, suffix = 'glsl.h',src_suffix = '.glsl') } ) + #env.Append( BUILDERS = { 'HLSL9' : env.Builder(action = methods.build_hlsl_dx9_headers, suffix = 'hlsl.h',src_suffix = '.hlsl') } ) + diff --git a/platform/x11/export/export.cpp b/platform/x11/export/export.cpp new file mode 100644 index 00000000000..b17b92bccfe --- /dev/null +++ b/platform/x11/export/export.cpp @@ -0,0 +1,24 @@ +#include "export.h" +#include "platform/x11/logo.h" +#include "tools/editor/editor_import_export.h" +#include "scene/resources/texture.h" + +void register_x11_exporter() { + + Image img(_x11_logo); + Ref logo = memnew( ImageTexture ); + logo->create_from_image(img); + + { + Ref exporter = Ref( memnew(EditorExportPlatformPC) ); + exporter->set_binary_extension("bin"); + exporter->set_release_binary32("linux_x11_32_release"); + exporter->set_debug_binary32("linux_x11_32_debug"); + exporter->set_release_binary64("linux_x11_64_release"); + exporter->set_debug_binary64("linux_x11_64_debug"); + exporter->set_name("Linux X11"); + exporter->set_logo(logo); + EditorImportExport::get_singleton()->add_export_platform(exporter); + } + +} diff --git a/platform/x11/export/export.h b/platform/x11/export/export.h new file mode 100644 index 00000000000..1077709ea14 --- /dev/null +++ b/platform/x11/export/export.h @@ -0,0 +1,4 @@ + + +void register_x11_exporter(); + diff --git a/platform/x11/godot_x11.cpp b/platform/x11/godot_x11.cpp new file mode 100644 index 00000000000..3b50e8e515e --- /dev/null +++ b/platform/x11/godot_x11.cpp @@ -0,0 +1,45 @@ +/*************************************************************************/ +/* godot_x11.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 "main/main.h" +#include "os_x11.h" + +int main(int argc, char* argv[]) { + + OS_X11 os; + + Error err = Main::setup(argv[0],argc-1,&argv[1]); + if (err!=OK) + return 255; + + if (Main::start()) + os.run(); // it is actually the OS that decides how to run + Main::cleanup(); + + return os.get_exit_code(); +} diff --git a/platform/x11/key_mapping_x11.cpp b/platform/x11/key_mapping_x11.cpp new file mode 100644 index 00000000000..2f109355b83 --- /dev/null +++ b/platform/x11/key_mapping_x11.cpp @@ -0,0 +1,1812 @@ +/*************************************************************************/ +/* key_mapping_x11.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 "key_mapping_x11.h" + + +/***** SCAN CODE CONVERSION ******/ + +struct _XTranslatePair { + + KeySym keysym; + unsigned int keycode; +}; + +static _XTranslatePair _xkeysym_to_keycode[]={ + // misc keys + + { XK_Escape, KEY_ESCAPE }, + { XK_Tab, KEY_TAB }, + { XK_ISO_Left_Tab, KEY_BACKTAB }, + { XK_BackSpace, KEY_BACKSPACE }, + { XK_Return, KEY_RETURN }, + { XK_Insert, KEY_INSERT }, + { XK_Delete, KEY_DELETE }, + { XK_Clear, KEY_DELETE }, + { XK_Pause, KEY_PAUSE }, + { XK_Print, KEY_PRINT }, + { XK_Home, KEY_HOME }, + { XK_End, KEY_END }, + { XK_Left, KEY_LEFT }, + { XK_Up, KEY_UP }, + { XK_Right, KEY_RIGHT }, + { XK_Down, KEY_DOWN }, + { XK_Prior, KEY_PAGEUP }, + { XK_Next, KEY_PAGEDOWN }, + { XK_Shift_L, KEY_SHIFT }, + { XK_Shift_R, KEY_SHIFT }, + { XK_Shift_Lock, KEY_SHIFT }, + { XK_Control_L, KEY_CONTROL }, + { XK_Control_R, KEY_CONTROL }, + { XK_Meta_L, KEY_META }, + { XK_Meta_R, KEY_META }, + { XK_Alt_L, KEY_ALT }, + { XK_Alt_R, KEY_ALT }, + { XK_Caps_Lock, KEY_CAPSLOCK }, + { XK_Num_Lock, KEY_NUMLOCK }, + { XK_Scroll_Lock, KEY_SCROLLLOCK }, + { XK_Super_L, KEY_SUPER_L }, + { XK_Super_R, KEY_SUPER_R }, + { XK_Menu, KEY_MENU }, + { XK_Hyper_L, KEY_HYPER_L }, + { XK_Hyper_R, KEY_HYPER_R }, + { XK_Help, KEY_HELP }, + { XK_KP_Space, KEY_SPACE }, + { XK_KP_Tab, KEY_TAB }, + { XK_KP_Enter, KEY_ENTER }, + { XK_Home, KEY_HOME }, + { XK_Left, KEY_LEFT }, + { XK_Up, KEY_UP }, + { XK_Right, KEY_RIGHT }, + { XK_Down, KEY_DOWN }, + { XK_Prior, KEY_PAGEUP }, + { XK_Next, KEY_PAGEDOWN }, + { XK_End, KEY_END }, + { XK_Begin, KEY_CLEAR }, + { XK_Insert, KEY_INSERT }, + { XK_Delete, KEY_DELETE }, +// { XK_KP_Equal, KEY_EQUAL }, +// { XK_KP_Separator, KEY_COMMA }, + { XK_KP_Decimal, KEY_KP_PERIOD }, + { XK_KP_Delete, KEY_KP_PERIOD }, + { XK_KP_Enter, KEY_KP_ENTER }, + { XK_KP_Multiply, KEY_KP_MULTIPLY}, + { XK_KP_Divide, KEY_KP_DIVIDE}, + { XK_KP_Subtract, KEY_KP_SUBSTRACT}, + { XK_KP_Add, KEY_KP_ADD}, + { XK_KP_0, KEY_KP_0}, + { XK_KP_1, KEY_KP_1}, + { XK_KP_2, KEY_KP_2}, + { XK_KP_3, KEY_KP_3}, + { XK_KP_4, KEY_KP_4}, + { XK_KP_5, KEY_KP_5}, + { XK_KP_6, KEY_KP_6}, + { XK_KP_7, KEY_KP_7}, + { XK_KP_8, KEY_KP_8}, + { XK_KP_9, KEY_KP_9}, + // same but with numlock + { XK_KP_Insert, KEY_KP_0}, + { XK_KP_End, KEY_KP_1}, + { XK_KP_Down, KEY_KP_2}, + { XK_KP_Page_Down, KEY_KP_3}, + { XK_KP_Left, KEY_KP_4}, + { XK_KP_Begin, KEY_KP_5}, + { XK_KP_Right, KEY_KP_6}, + { XK_KP_Home, KEY_KP_7}, + { XK_KP_Up, KEY_KP_8}, + { XK_KP_Page_Up, KEY_KP_9}, + { XK_F1, KEY_F1}, + { XK_F2, KEY_F2}, + { XK_F3, KEY_F3}, + { XK_F4, KEY_F4}, + { XK_F5, KEY_F5}, + { XK_F6, KEY_F6}, + { XK_F7, KEY_F7}, + { XK_F8, KEY_F8}, + { XK_F9, KEY_F9}, + { XK_F10, KEY_F10}, + { XK_F11, KEY_F11}, + { XK_F12, KEY_F12}, + { XK_F13, KEY_F13}, + { XK_F14, KEY_F14}, + { XK_F15, KEY_F15}, + { XK_F16, KEY_F16}, + + // media keys + { XF86XK_Back, KEY_BACK }, + { XF86XK_Forward, KEY_FORWARD }, + { XF86XK_Stop, KEY_STOP }, + { XF86XK_Refresh, KEY_REFRESH }, + { XF86XK_Favorites, KEY_FAVORITES }, + { XF86XK_AudioMedia, KEY_LAUNCHMEDIA }, + { XF86XK_OpenURL, KEY_OPENURL }, + { XF86XK_HomePage, KEY_HOMEPAGE }, + { XF86XK_Search, KEY_SEARCH }, + { XF86XK_AudioLowerVolume, KEY_VOLUMEDOWN }, + { XF86XK_AudioMute, KEY_VOLUMEMUTE }, + { XF86XK_AudioRaiseVolume, KEY_VOLUMEUP }, + { XF86XK_AudioPlay, KEY_MEDIAPLAY }, + { XF86XK_AudioStop, KEY_MEDIASTOP }, + { XF86XK_AudioPrev, KEY_MEDIAPREVIOUS }, + { XF86XK_AudioNext, KEY_MEDIANEXT }, + { XF86XK_AudioRecord, KEY_MEDIARECORD }, + + // launch keys + { XF86XK_Mail, KEY_LAUNCHMAIL }, + { XF86XK_MyComputer, KEY_LAUNCH0 }, + { XF86XK_Calculator, KEY_LAUNCH1 }, + { XF86XK_Standby, KEY_STANDBY }, + + { XF86XK_Launch0, KEY_LAUNCH2 }, + { XF86XK_Launch1, KEY_LAUNCH3 }, + { XF86XK_Launch2, KEY_LAUNCH4 }, + { XF86XK_Launch3, KEY_LAUNCH5 }, + { XF86XK_Launch4, KEY_LAUNCH6 }, + { XF86XK_Launch5, KEY_LAUNCH7 }, + { XF86XK_Launch6, KEY_LAUNCH8 }, + { XF86XK_Launch7, KEY_LAUNCH9 }, + { XF86XK_Launch8, KEY_LAUNCHA }, + { XF86XK_Launch9, KEY_LAUNCHB }, + { XF86XK_LaunchA, KEY_LAUNCHC }, + { XF86XK_LaunchB, KEY_LAUNCHD }, + { XF86XK_LaunchC, KEY_LAUNCHE }, + { XF86XK_LaunchD, KEY_LAUNCHF }, + + {0, 0 } +}; + + +unsigned int KeyMappingX11::get_keycode(KeySym p_keysym) { + + // kinda bruteforce.. could optimize. + + if (p_keysym<0x100) // Latin 1, maps 1-1 + return p_keysym; + + // look for special key + for(int idx=0;_xkeysym_to_keycode[idx].keysym!=0;idx++) { + + if (_xkeysym_to_keycode[idx].keysym==p_keysym) + return _xkeysym_to_keycode[idx].keycode; + } + + return 0; +} +KeySym KeyMappingX11::get_keysym(unsigned int p_code) { + + // kinda bruteforce.. could optimize. + + if (p_code<0x100) // Latin 1, maps 1-1 + return p_code; + + // look for special key + for(int idx=0;_xkeysym_to_keycode[idx].keysym!=0;idx++) { + + if (_xkeysym_to_keycode[idx].keycode==p_code) + return _xkeysym_to_keycode[idx].keysym; + } + + return 0; +} + + +/***** UNICODE CONVERSION ******/ + +// Tables taken from FOX toolkit + +struct _XTranslateUnicodePair { + + KeySym keysym; + unsigned int unicode; +}; + +enum { + + _KEYSYM_MAX=759 +}; + +static _XTranslateUnicodePair _xkeysym_to_unicode[_KEYSYM_MAX] = { + { 0x01A1, 0x0104 }, + { 0x01A2, 0x02D8 }, + { 0x01A3, 0x0141 }, + { 0x01A5, 0x013D }, + { 0x01A6, 0x015A }, + { 0x01A9, 0x0160 }, + { 0x01AA, 0x015E }, + { 0x01AB, 0x0164 }, + { 0x01AC, 0x0179 }, + { 0x01AE, 0x017D }, + { 0x01AF, 0x017B }, + { 0x01B1, 0x0105 }, + { 0x01B2, 0x02DB }, + { 0x01B3, 0x0142 }, + { 0x01B5, 0x013E }, + { 0x01B6, 0x015B }, + { 0x01B7, 0x02C7 }, + { 0x01B9, 0x0161 }, + { 0x01BA, 0x015F }, + { 0x01BB, 0x0165 }, + { 0x01BC, 0x017A }, + { 0x01BD, 0x02DD }, + { 0x01BE, 0x017E }, + { 0x01BF, 0x017C }, + { 0x01C0, 0x0154 }, + { 0x01C3, 0x0102 }, + { 0x01C5, 0x0139 }, + { 0x01C6, 0x0106 }, + { 0x01C8, 0x010C }, + { 0x01CA, 0x0118 }, + { 0x01CC, 0x011A }, + { 0x01CF, 0x010E }, + { 0x01D0, 0x0110 }, + { 0x01D1, 0x0143 }, + { 0x01D2, 0x0147 }, + { 0x01D5, 0x0150 }, + { 0x01D8, 0x0158 }, + { 0x01D9, 0x016E }, + { 0x01DB, 0x0170 }, + { 0x01DE, 0x0162 }, + { 0x01E0, 0x0155 }, + { 0x01E3, 0x0103 }, + { 0x01E5, 0x013A }, + { 0x01E6, 0x0107 }, + { 0x01E8, 0x010D }, + { 0x01EA, 0x0119 }, + { 0x01EC, 0x011B }, + { 0x01EF, 0x010F }, + { 0x01F0, 0x0111 }, + { 0x01F1, 0x0144 }, + { 0x01F2, 0x0148 }, + { 0x01F5, 0x0151 }, + { 0x01F8, 0x0159 }, + { 0x01F9, 0x016F }, + { 0x01FB, 0x0171 }, + { 0x01FE, 0x0163 }, + { 0x01FF, 0x02D9 }, + { 0x02A1, 0x0126 }, + { 0x02A6, 0x0124 }, + { 0x02A9, 0x0130 }, + { 0x02AB, 0x011E }, + { 0x02AC, 0x0134 }, + { 0x02B1, 0x0127 }, + { 0x02B6, 0x0125 }, + { 0x02B9, 0x0131 }, + { 0x02BB, 0x011F }, + { 0x02BC, 0x0135 }, + { 0x02C5, 0x010A }, + { 0x02C6, 0x0108 }, + { 0x02D5, 0x0120 }, + { 0x02D8, 0x011C }, + { 0x02DD, 0x016C }, + { 0x02DE, 0x015C }, + { 0x02E5, 0x010B }, + { 0x02E6, 0x0109 }, + { 0x02F5, 0x0121 }, + { 0x02F8, 0x011D }, + { 0x02FD, 0x016D }, + { 0x02FE, 0x015D }, + { 0x03A2, 0x0138 }, + { 0x03A3, 0x0156 }, + { 0x03A5, 0x0128 }, + { 0x03A6, 0x013B }, + { 0x03AA, 0x0112 }, + { 0x03AB, 0x0122 }, + { 0x03AC, 0x0166 }, + { 0x03B3, 0x0157 }, + { 0x03B5, 0x0129 }, + { 0x03B6, 0x013C }, + { 0x03BA, 0x0113 }, + { 0x03BB, 0x0123 }, + { 0x03BC, 0x0167 }, + { 0x03BD, 0x014A }, + { 0x03BF, 0x014B }, + { 0x03C0, 0x0100 }, + { 0x03C7, 0x012E }, + { 0x03CC, 0x0116 }, + { 0x03CF, 0x012A }, + { 0x03D1, 0x0145 }, + { 0x03D2, 0x014C }, + { 0x03D3, 0x0136 }, + { 0x03D9, 0x0172 }, + { 0x03DD, 0x0168 }, + { 0x03DE, 0x016A }, + { 0x03E0, 0x0101 }, + { 0x03E7, 0x012F }, + { 0x03EC, 0x0117 }, + { 0x03EF, 0x012B }, + { 0x03F1, 0x0146 }, + { 0x03F2, 0x014D }, + { 0x03F3, 0x0137 }, + { 0x03F9, 0x0173 }, + { 0x03FD, 0x0169 }, + { 0x03FE, 0x016B }, + { 0x047E, 0x203E }, + { 0x04A1, 0x3002 }, + { 0x04A2, 0x300C }, + { 0x04A3, 0x300D }, + { 0x04A4, 0x3001 }, + { 0x04A5, 0x30FB }, + { 0x04A6, 0x30F2 }, + { 0x04A7, 0x30A1 }, + { 0x04A8, 0x30A3 }, + { 0x04A9, 0x30A5 }, + { 0x04AA, 0x30A7 }, + { 0x04AB, 0x30A9 }, + { 0x04AC, 0x30E3 }, + { 0x04AD, 0x30E5 }, + { 0x04AE, 0x30E7 }, + { 0x04AF, 0x30C3 }, + { 0x04B0, 0x30FC }, + { 0x04B1, 0x30A2 }, + { 0x04B2, 0x30A4 }, + { 0x04B3, 0x30A6 }, + { 0x04B4, 0x30A8 }, + { 0x04B5, 0x30AA }, + { 0x04B6, 0x30AB }, + { 0x04B7, 0x30AD }, + { 0x04B8, 0x30AF }, + { 0x04B9, 0x30B1 }, + { 0x04BA, 0x30B3 }, + { 0x04BB, 0x30B5 }, + { 0x04BC, 0x30B7 }, + { 0x04BD, 0x30B9 }, + { 0x04BE, 0x30BB }, + { 0x04BF, 0x30BD }, + { 0x04C0, 0x30BF }, + { 0x04C1, 0x30C1 }, + { 0x04C2, 0x30C4 }, + { 0x04C3, 0x30C6 }, + { 0x04C4, 0x30C8 }, + { 0x04C5, 0x30CA }, + { 0x04C6, 0x30CB }, + { 0x04C7, 0x30CC }, + { 0x04C8, 0x30CD }, + { 0x04C9, 0x30CE }, + { 0x04CA, 0x30CF }, + { 0x04CB, 0x30D2 }, + { 0x04CC, 0x30D5 }, + { 0x04CD, 0x30D8 }, + { 0x04CE, 0x30DB }, + { 0x04CF, 0x30DE }, + { 0x04D0, 0x30DF }, + { 0x04D1, 0x30E0 }, + { 0x04D2, 0x30E1 }, + { 0x04D3, 0x30E2 }, + { 0x04D4, 0x30E4 }, + { 0x04D5, 0x30E6 }, + { 0x04D6, 0x30E8 }, + { 0x04D7, 0x30E9 }, + { 0x04D8, 0x30EA }, + { 0x04D9, 0x30EB }, + { 0x04DA, 0x30EC }, + { 0x04DB, 0x30ED }, + { 0x04DC, 0x30EF }, + { 0x04DD, 0x30F3 }, + { 0x04DE, 0x309B }, + { 0x04DF, 0x309C }, + { 0x05AC, 0x060C }, + { 0x05BB, 0x061B }, + { 0x05BF, 0x061F }, + { 0x05C1, 0x0621 }, + { 0x05C2, 0x0622 }, + { 0x05C3, 0x0623 }, + { 0x05C4, 0x0624 }, + { 0x05C5, 0x0625 }, + { 0x05C6, 0x0626 }, + { 0x05C7, 0x0627 }, + { 0x05C8, 0x0628 }, + { 0x05C9, 0x0629 }, + { 0x05CA, 0x062A }, + { 0x05CB, 0x062B }, + { 0x05CC, 0x062C }, + { 0x05CD, 0x062D }, + { 0x05CE, 0x062E }, + { 0x05CF, 0x062F }, + { 0x05D0, 0x0630 }, + { 0x05D1, 0x0631 }, + { 0x05D2, 0x0632 }, + { 0x05D3, 0x0633 }, + { 0x05D4, 0x0634 }, + { 0x05D5, 0x0635 }, + { 0x05D6, 0x0636 }, + { 0x05D7, 0x0637 }, + { 0x05D8, 0x0638 }, + { 0x05D9, 0x0639 }, + { 0x05DA, 0x063A }, + { 0x05E0, 0x0640 }, + { 0x05E1, 0x0641 }, + { 0x05E2, 0x0642 }, + { 0x05E3, 0x0643 }, + { 0x05E4, 0x0644 }, + { 0x05E5, 0x0645 }, + { 0x05E6, 0x0646 }, + { 0x05E7, 0x0647 }, + { 0x05E8, 0x0648 }, + { 0x05E9, 0x0649 }, + { 0x05EA, 0x064A }, + { 0x05EB, 0x064B }, + { 0x05EC, 0x064C }, + { 0x05ED, 0x064D }, + { 0x05EE, 0x064E }, + { 0x05EF, 0x064F }, + { 0x05F0, 0x0650 }, + { 0x05F1, 0x0651 }, + { 0x05F2, 0x0652 }, + { 0x06A1, 0x0452 }, + { 0x06A2, 0x0453 }, + { 0x06A3, 0x0451 }, + { 0x06A4, 0x0454 }, + { 0x06A5, 0x0455 }, + { 0x06A6, 0x0456 }, + { 0x06A7, 0x0457 }, + { 0x06A8, 0x0458 }, + { 0x06A9, 0x0459 }, + { 0x06AA, 0x045A }, + { 0x06AB, 0x045B }, + { 0x06AC, 0x045C }, + { 0x06AE, 0x045E }, + { 0x06AF, 0x045F }, + { 0x06B0, 0x2116 }, + { 0x06B1, 0x0402 }, + { 0x06B2, 0x0403 }, + { 0x06B3, 0x0401 }, + { 0x06B4, 0x0404 }, + { 0x06B5, 0x0405 }, + { 0x06B6, 0x0406 }, + { 0x06B7, 0x0407 }, + { 0x06B8, 0x0408 }, + { 0x06B9, 0x0409 }, + { 0x06BA, 0x040A }, + { 0x06BB, 0x040B }, + { 0x06BC, 0x040C }, + { 0x06BE, 0x040E }, + { 0x06BF, 0x040F }, + { 0x06C0, 0x044E }, + { 0x06C1, 0x0430 }, + { 0x06C2, 0x0431 }, + { 0x06C3, 0x0446 }, + { 0x06C4, 0x0434 }, + { 0x06C5, 0x0435 }, + { 0x06C6, 0x0444 }, + { 0x06C7, 0x0433 }, + { 0x06C8, 0x0445 }, + { 0x06C9, 0x0438 }, + { 0x06CA, 0x0439 }, + { 0x06CB, 0x043A }, + { 0x06CC, 0x043B }, + { 0x06CD, 0x043C }, + { 0x06CE, 0x043D }, + { 0x06CF, 0x043E }, + { 0x06D0, 0x043F }, + { 0x06D1, 0x044F }, + { 0x06D2, 0x0440 }, + { 0x06D3, 0x0441 }, + { 0x06D4, 0x0442 }, + { 0x06D5, 0x0443 }, + { 0x06D6, 0x0436 }, + { 0x06D7, 0x0432 }, + { 0x06D8, 0x044C }, + { 0x06D9, 0x044B }, + { 0x06DA, 0x0437 }, + { 0x06DB, 0x0448 }, + { 0x06DC, 0x044D }, + { 0x06DD, 0x0449 }, + { 0x06DE, 0x0447 }, + { 0x06DF, 0x044A }, + { 0x06E0, 0x042E }, + { 0x06E1, 0x0410 }, + { 0x06E2, 0x0411 }, + { 0x06E3, 0x0426 }, + { 0x06E4, 0x0414 }, + { 0x06E5, 0x0415 }, + { 0x06E6, 0x0424 }, + { 0x06E7, 0x0413 }, + { 0x06E8, 0x0425 }, + { 0x06E9, 0x0418 }, + { 0x06EA, 0x0419 }, + { 0x06EB, 0x041A }, + { 0x06EC, 0x041B }, + { 0x06ED, 0x041C }, + { 0x06EE, 0x041D }, + { 0x06EF, 0x041E }, + { 0x06F0, 0x041F }, + { 0x06F1, 0x042F }, + { 0x06F2, 0x0420 }, + { 0x06F3, 0x0421 }, + { 0x06F4, 0x0422 }, + { 0x06F5, 0x0423 }, + { 0x06F6, 0x0416 }, + { 0x06F7, 0x0412 }, + { 0x06F8, 0x042C }, + { 0x06F9, 0x042B }, + { 0x06FA, 0x0417 }, + { 0x06FB, 0x0428 }, + { 0x06FC, 0x042D }, + { 0x06FD, 0x0429 }, + { 0x06FE, 0x0427 }, + { 0x06FF, 0x042A }, + { 0x07A1, 0x0386 }, + { 0x07A2, 0x0388 }, + { 0x07A3, 0x0389 }, + { 0x07A4, 0x038A }, + { 0x07A5, 0x03AA }, + { 0x07A7, 0x038C }, + { 0x07A8, 0x038E }, + { 0x07A9, 0x03AB }, + { 0x07AB, 0x038F }, + { 0x07AE, 0x0385 }, + { 0x07AF, 0x2015 }, + { 0x07B1, 0x03AC }, + { 0x07B2, 0x03AD }, + { 0x07B3, 0x03AE }, + { 0x07B4, 0x03AF }, + { 0x07B5, 0x03CA }, + { 0x07B6, 0x0390 }, + { 0x07B7, 0x03CC }, + { 0x07B8, 0x03CD }, + { 0x07B9, 0x03CB }, + { 0x07BA, 0x03B0 }, + { 0x07BB, 0x03CE }, + { 0x07C1, 0x0391 }, + { 0x07C2, 0x0392 }, + { 0x07C3, 0x0393 }, + { 0x07C4, 0x0394 }, + { 0x07C5, 0x0395 }, + { 0x07C6, 0x0396 }, + { 0x07C7, 0x0397 }, + { 0x07C8, 0x0398 }, + { 0x07C9, 0x0399 }, + { 0x07CA, 0x039A }, + { 0x07CB, 0x039B }, + { 0x07CC, 0x039C }, + { 0x07CD, 0x039D }, + { 0x07CE, 0x039E }, + { 0x07CF, 0x039F }, + { 0x07D0, 0x03A0 }, + { 0x07D1, 0x03A1 }, + { 0x07D2, 0x03A3 }, + { 0x07D4, 0x03A4 }, + { 0x07D5, 0x03A5 }, + { 0x07D6, 0x03A6 }, + { 0x07D7, 0x03A7 }, + { 0x07D8, 0x03A8 }, + { 0x07D9, 0x03A9 }, + { 0x07E1, 0x03B1 }, + { 0x07E2, 0x03B2 }, + { 0x07E3, 0x03B3 }, + { 0x07E4, 0x03B4 }, + { 0x07E5, 0x03B5 }, + { 0x07E6, 0x03B6 }, + { 0x07E7, 0x03B7 }, + { 0x07E8, 0x03B8 }, + { 0x07E9, 0x03B9 }, + { 0x07EA, 0x03BA }, + { 0x07EB, 0x03BB }, + { 0x07EC, 0x03BC }, + { 0x07ED, 0x03BD }, + { 0x07EE, 0x03BE }, + { 0x07EF, 0x03BF }, + { 0x07F0, 0x03C0 }, + { 0x07F1, 0x03C1 }, + { 0x07F2, 0x03C3 }, + { 0x07F3, 0x03C2 }, + { 0x07F4, 0x03C4 }, + { 0x07F5, 0x03C5 }, + { 0x07F6, 0x03C6 }, + { 0x07F7, 0x03C7 }, + { 0x07F8, 0x03C8 }, + { 0x07F9, 0x03C9 }, + { 0x08A1, 0x23B7 }, + { 0x08A2, 0x250C }, + { 0x08A3, 0x2500 }, + { 0x08A4, 0x2320 }, + { 0x08A5, 0x2321 }, + { 0x08A6, 0x2502 }, + { 0x08A7, 0x23A1 }, + { 0x08A8, 0x23A3 }, + { 0x08A9, 0x23A4 }, + { 0x08AA, 0x23A6 }, + { 0x08AB, 0x239B }, + { 0x08AC, 0x239D }, + { 0x08AD, 0x239E }, + { 0x08AE, 0x23A0 }, + { 0x08AF, 0x23A8 }, + { 0x08B0, 0x23AC }, + { 0x08BC, 0x2264 }, + { 0x08BD, 0x2260 }, + { 0x08BE, 0x2265 }, + { 0x08BF, 0x222B }, + { 0x08C0, 0x2234 }, + { 0x08C1, 0x221D }, + { 0x08C2, 0x221E }, + { 0x08C5, 0x2207 }, + { 0x08C8, 0x223C }, + { 0x08C9, 0x2243 }, + { 0x08CD, 0x21D4 }, + { 0x08CE, 0x21D2 }, + { 0x08CF, 0x2261 }, + { 0x08D6, 0x221A }, + { 0x08DA, 0x2282 }, + { 0x08DB, 0x2283 }, + { 0x08DC, 0x2229 }, + { 0x08DD, 0x222A }, + { 0x08DE, 0x2227 }, + { 0x08DF, 0x2228 }, + { 0x08EF, 0x2202 }, + { 0x08F6, 0x0192 }, + { 0x08FB, 0x2190 }, + { 0x08FC, 0x2191 }, + { 0x08FD, 0x2192 }, + { 0x08FE, 0x2193 }, + { 0x09E0, 0x25C6 }, + { 0x09E1, 0x2592 }, + { 0x09E2, 0x2409 }, + { 0x09E3, 0x240C }, + { 0x09E4, 0x240D }, + { 0x09E5, 0x240A }, + { 0x09E8, 0x2424 }, + { 0x09E9, 0x240B }, + { 0x09EA, 0x2518 }, + { 0x09EB, 0x2510 }, + { 0x09EC, 0x250C }, + { 0x09ED, 0x2514 }, + { 0x09EE, 0x253C }, + { 0x09EF, 0x23BA }, + { 0x09F0, 0x23BB }, + { 0x09F1, 0x2500 }, + { 0x09F2, 0x23BC }, + { 0x09F3, 0x23BD }, + { 0x09F4, 0x251C }, + { 0x09F5, 0x2524 }, + { 0x09F6, 0x2534 }, + { 0x09F7, 0x252C }, + { 0x09F8, 0x2502 }, + { 0x0AA1, 0x2003 }, + { 0x0AA2, 0x2002 }, + { 0x0AA3, 0x2004 }, + { 0x0AA4, 0x2005 }, + { 0x0AA5, 0x2007 }, + { 0x0AA6, 0x2008 }, + { 0x0AA7, 0x2009 }, + { 0x0AA8, 0x200A }, + { 0x0AA9, 0x2014 }, + { 0x0AAA, 0x2013 }, + { 0x0AAE, 0x2026 }, + { 0x0AAF, 0x2025 }, + { 0x0AB0, 0x2153 }, + { 0x0AB1, 0x2154 }, + { 0x0AB2, 0x2155 }, + { 0x0AB3, 0x2156 }, + { 0x0AB4, 0x2157 }, + { 0x0AB5, 0x2158 }, + { 0x0AB6, 0x2159 }, + { 0x0AB7, 0x215A }, + { 0x0AB8, 0x2105 }, + { 0x0ABB, 0x2012 }, + { 0x0ABC, 0x2329 }, + { 0x0ABE, 0x232A }, + { 0x0AC3, 0x215B }, + { 0x0AC4, 0x215C }, + { 0x0AC5, 0x215D }, + { 0x0AC6, 0x215E }, + { 0x0AC9, 0x2122 }, + { 0x0ACA, 0x2613 }, + { 0x0ACC, 0x25C1 }, + { 0x0ACD, 0x25B7 }, + { 0x0ACE, 0x25CB }, + { 0x0ACF, 0x25AF }, + { 0x0AD0, 0x2018 }, + { 0x0AD1, 0x2019 }, + { 0x0AD2, 0x201C }, + { 0x0AD3, 0x201D }, + { 0x0AD4, 0x211E }, + { 0x0AD6, 0x2032 }, + { 0x0AD7, 0x2033 }, + { 0x0AD9, 0x271D }, + { 0x0ADB, 0x25AC }, + { 0x0ADC, 0x25C0 }, + { 0x0ADD, 0x25B6 }, + { 0x0ADE, 0x25CF }, + { 0x0ADF, 0x25AE }, + { 0x0AE0, 0x25E6 }, + { 0x0AE1, 0x25AB }, + { 0x0AE2, 0x25AD }, + { 0x0AE3, 0x25B3 }, + { 0x0AE4, 0x25BD }, + { 0x0AE5, 0x2606 }, + { 0x0AE6, 0x2022 }, + { 0x0AE7, 0x25AA }, + { 0x0AE8, 0x25B2 }, + { 0x0AE9, 0x25BC }, + { 0x0AEA, 0x261C }, + { 0x0AEB, 0x261E }, + { 0x0AEC, 0x2663 }, + { 0x0AED, 0x2666 }, + { 0x0AEE, 0x2665 }, + { 0x0AF0, 0x2720 }, + { 0x0AF1, 0x2020 }, + { 0x0AF2, 0x2021 }, + { 0x0AF3, 0x2713 }, + { 0x0AF4, 0x2717 }, + { 0x0AF5, 0x266F }, + { 0x0AF6, 0x266D }, + { 0x0AF7, 0x2642 }, + { 0x0AF8, 0x2640 }, + { 0x0AF9, 0x260E }, + { 0x0AFA, 0x2315 }, + { 0x0AFB, 0x2117 }, + { 0x0AFC, 0x2038 }, + { 0x0AFD, 0x201A }, + { 0x0AFE, 0x201E }, + { 0x0BA3, 0x003C }, + { 0x0BA6, 0x003E }, + { 0x0BA8, 0x2228 }, + { 0x0BA9, 0x2227 }, + { 0x0BC0, 0x00AF }, + { 0x0BC2, 0x22A5 }, + { 0x0BC3, 0x2229 }, + { 0x0BC4, 0x230A }, + { 0x0BC6, 0x005F }, + { 0x0BCA, 0x2218 }, + { 0x0BCC, 0x2395 }, + { 0x0BCE, 0x22A4 }, + { 0x0BCF, 0x25CB }, + { 0x0BD3, 0x2308 }, + { 0x0BD6, 0x222A }, + { 0x0BD8, 0x2283 }, + { 0x0BDA, 0x2282 }, + { 0x0BDC, 0x22A2 }, + { 0x0BFC, 0x22A3 }, + { 0x0CDF, 0x2017 }, + { 0x0CE0, 0x05D0 }, + { 0x0CE1, 0x05D1 }, + { 0x0CE2, 0x05D2 }, + { 0x0CE3, 0x05D3 }, + { 0x0CE4, 0x05D4 }, + { 0x0CE5, 0x05D5 }, + { 0x0CE6, 0x05D6 }, + { 0x0CE7, 0x05D7 }, + { 0x0CE8, 0x05D8 }, + { 0x0CE9, 0x05D9 }, + { 0x0CEA, 0x05DA }, + { 0x0CEB, 0x05DB }, + { 0x0CEC, 0x05DC }, + { 0x0CED, 0x05DD }, + { 0x0CEE, 0x05DE }, + { 0x0CEF, 0x05DF }, + { 0x0CF0, 0x05E0 }, + { 0x0CF1, 0x05E1 }, + { 0x0CF2, 0x05E2 }, + { 0x0CF3, 0x05E3 }, + { 0x0CF4, 0x05E4 }, + { 0x0CF5, 0x05E5 }, + { 0x0CF6, 0x05E6 }, + { 0x0CF7, 0x05E7 }, + { 0x0CF8, 0x05E8 }, + { 0x0CF9, 0x05E9 }, + { 0x0CFA, 0x05EA }, + { 0x0DA1, 0x0E01 }, + { 0x0DA2, 0x0E02 }, + { 0x0DA3, 0x0E03 }, + { 0x0DA4, 0x0E04 }, + { 0x0DA5, 0x0E05 }, + { 0x0DA6, 0x0E06 }, + { 0x0DA7, 0x0E07 }, + { 0x0DA8, 0x0E08 }, + { 0x0DA9, 0x0E09 }, + { 0x0DAA, 0x0E0A }, + { 0x0DAB, 0x0E0B }, + { 0x0DAC, 0x0E0C }, + { 0x0DAD, 0x0E0D }, + { 0x0DAE, 0x0E0E }, + { 0x0DAF, 0x0E0F }, + { 0x0DB0, 0x0E10 }, + { 0x0DB1, 0x0E11 }, + { 0x0DB2, 0x0E12 }, + { 0x0DB3, 0x0E13 }, + { 0x0DB4, 0x0E14 }, + { 0x0DB5, 0x0E15 }, + { 0x0DB6, 0x0E16 }, + { 0x0DB7, 0x0E17 }, + { 0x0DB8, 0x0E18 }, + { 0x0DB9, 0x0E19 }, + { 0x0DBA, 0x0E1A }, + { 0x0DBB, 0x0E1B }, + { 0x0DBC, 0x0E1C }, + { 0x0DBD, 0x0E1D }, + { 0x0DBE, 0x0E1E }, + { 0x0DBF, 0x0E1F }, + { 0x0DC0, 0x0E20 }, + { 0x0DC1, 0x0E21 }, + { 0x0DC2, 0x0E22 }, + { 0x0DC3, 0x0E23 }, + { 0x0DC4, 0x0E24 }, + { 0x0DC5, 0x0E25 }, + { 0x0DC6, 0x0E26 }, + { 0x0DC7, 0x0E27 }, + { 0x0DC8, 0x0E28 }, + { 0x0DC9, 0x0E29 }, + { 0x0DCA, 0x0E2A }, + { 0x0DCB, 0x0E2B }, + { 0x0DCC, 0x0E2C }, + { 0x0DCD, 0x0E2D }, + { 0x0DCE, 0x0E2E }, + { 0x0DCF, 0x0E2F }, + { 0x0DD0, 0x0E30 }, + { 0x0DD1, 0x0E31 }, + { 0x0DD2, 0x0E32 }, + { 0x0DD3, 0x0E33 }, + { 0x0DD4, 0x0E34 }, + { 0x0DD5, 0x0E35 }, + { 0x0DD6, 0x0E36 }, + { 0x0DD7, 0x0E37 }, + { 0x0DD8, 0x0E38 }, + { 0x0DD9, 0x0E39 }, + { 0x0DDA, 0x0E3A }, + { 0x0DDF, 0x0E3F }, + { 0x0DE0, 0x0E40 }, + { 0x0DE1, 0x0E41 }, + { 0x0DE2, 0x0E42 }, + { 0x0DE3, 0x0E43 }, + { 0x0DE4, 0x0E44 }, + { 0x0DE5, 0x0E45 }, + { 0x0DE6, 0x0E46 }, + { 0x0DE7, 0x0E47 }, + { 0x0DE8, 0x0E48 }, + { 0x0DE9, 0x0E49 }, + { 0x0DEA, 0x0E4A }, + { 0x0DEB, 0x0E4B }, + { 0x0DEC, 0x0E4C }, + { 0x0DED, 0x0E4D }, + { 0x0DF0, 0x0E50 }, + { 0x0DF1, 0x0E51 }, + { 0x0DF2, 0x0E52 }, + { 0x0DF3, 0x0E53 }, + { 0x0DF4, 0x0E54 }, + { 0x0DF5, 0x0E55 }, + { 0x0DF6, 0x0E56 }, + { 0x0DF7, 0x0E57 }, + { 0x0DF8, 0x0E58 }, + { 0x0DF9, 0x0E59 }, + { 0x0EA1, 0x3131 }, + { 0x0EA2, 0x3132 }, + { 0x0EA3, 0x3133 }, + { 0x0EA4, 0x3134 }, + { 0x0EA5, 0x3135 }, + { 0x0EA6, 0x3136 }, + { 0x0EA7, 0x3137 }, + { 0x0EA8, 0x3138 }, + { 0x0EA9, 0x3139 }, + { 0x0EAA, 0x313A }, + { 0x0EAB, 0x313B }, + { 0x0EAC, 0x313C }, + { 0x0EAD, 0x313D }, + { 0x0EAE, 0x313E }, + { 0x0EAF, 0x313F }, + { 0x0EB0, 0x3140 }, + { 0x0EB1, 0x3141 }, + { 0x0EB2, 0x3142 }, + { 0x0EB3, 0x3143 }, + { 0x0EB4, 0x3144 }, + { 0x0EB5, 0x3145 }, + { 0x0EB6, 0x3146 }, + { 0x0EB7, 0x3147 }, + { 0x0EB8, 0x3148 }, + { 0x0EB9, 0x3149 }, + { 0x0EBA, 0x314A }, + { 0x0EBB, 0x314B }, + { 0x0EBC, 0x314C }, + { 0x0EBD, 0x314D }, + { 0x0EBE, 0x314E }, + { 0x0EBF, 0x314F }, + { 0x0EC0, 0x3150 }, + { 0x0EC1, 0x3151 }, + { 0x0EC2, 0x3152 }, + { 0x0EC3, 0x3153 }, + { 0x0EC4, 0x3154 }, + { 0x0EC5, 0x3155 }, + { 0x0EC6, 0x3156 }, + { 0x0EC7, 0x3157 }, + { 0x0EC8, 0x3158 }, + { 0x0EC9, 0x3159 }, + { 0x0ECA, 0x315A }, + { 0x0ECB, 0x315B }, + { 0x0ECC, 0x315C }, + { 0x0ECD, 0x315D }, + { 0x0ECE, 0x315E }, + { 0x0ECF, 0x315F }, + { 0x0ED0, 0x3160 }, + { 0x0ED1, 0x3161 }, + { 0x0ED2, 0x3162 }, + { 0x0ED3, 0x3163 }, + { 0x0ED4, 0x11A8 }, + { 0x0ED5, 0x11A9 }, + { 0x0ED6, 0x11AA }, + { 0x0ED7, 0x11AB }, + { 0x0ED8, 0x11AC }, + { 0x0ED9, 0x11AD }, + { 0x0EDA, 0x11AE }, + { 0x0EDB, 0x11AF }, + { 0x0EDC, 0x11B0 }, + { 0x0EDD, 0x11B1 }, + { 0x0EDE, 0x11B2 }, + { 0x0EDF, 0x11B3 }, + { 0x0EE0, 0x11B4 }, + { 0x0EE1, 0x11B5 }, + { 0x0EE2, 0x11B6 }, + { 0x0EE3, 0x11B7 }, + { 0x0EE4, 0x11B8 }, + { 0x0EE5, 0x11B9 }, + { 0x0EE6, 0x11BA }, + { 0x0EE7, 0x11BB }, + { 0x0EE8, 0x11BC }, + { 0x0EE9, 0x11BD }, + { 0x0EEA, 0x11BE }, + { 0x0EEB, 0x11BF }, + { 0x0EEC, 0x11C0 }, + { 0x0EED, 0x11C1 }, + { 0x0EEE, 0x11C2 }, + { 0x0EEF, 0x316D }, + { 0x0EF0, 0x3171 }, + { 0x0EF1, 0x3178 }, + { 0x0EF2, 0x317F }, + { 0x0EF3, 0x3181 }, + { 0x0EF4, 0x3184 }, + { 0x0EF5, 0x3186 }, + { 0x0EF6, 0x318D }, + { 0x0EF7, 0x318E }, + { 0x0EF8, 0x11EB }, + { 0x0EF9, 0x11F0 }, + { 0x0EFA, 0x11F9 }, + { 0x0EFF, 0x20A9 }, + { 0x13A4, 0x20AC }, + { 0x13BC, 0x0152 }, + { 0x13BD, 0x0153 }, + { 0x13BE, 0x0178 }, + { 0x20AC, 0x20AC }, +}; + +unsigned int KeyMappingX11::get_unicode_from_keysym(KeySym p_keysym) { + + /* Latin-1 */ + if (p_keysym>=0x20 && p_keysym<=0x7e) + return p_keysym; + if (p_keysym>=0xa0 && p_keysym<=0xff) + return p_keysym; + // keypad to latin1 is easy + if (p_keysym>=0xffaa && p_keysym<=0xffb9) + return p_keysym-0xff80; + + /* Unicode (may be present)*/ + + if((p_keysym&0xff000000)==0x01000000) + return p_keysym&0x00ffffff; + + int middle,low=0,high=_KEYSYM_MAX-1; + do { + middle=(high+low)/2; + if ( _xkeysym_to_unicode[middle].keysym==p_keysym) + return _xkeysym_to_unicode[middle].unicode; + if ( _xkeysym_to_unicode[middle].keysym<=p_keysym ) + low=middle+1; + else + high=middle-1; + } while (high>=low); + + return 0; + +} + +struct _XTranslateUnicodePairReverse { + + unsigned int unicode; + KeySym keysym; +}; + +enum { + + _UNICODE_MAX=750 +}; + +static _XTranslateUnicodePairReverse _unicode_to_xkeysym[_UNICODE_MAX] = { + { 0x0ABD, 0x002E }, + { 0x0BA3, 0x003C }, + { 0x0BA6, 0x003E }, + { 0x0BC6, 0x005F }, + { 0x0BC0, 0x00AF }, + { 0x03C0, 0x0100 }, + { 0x03E0, 0x0101 }, + { 0x01C3, 0x0102 }, + { 0x01E3, 0x0103 }, + { 0x01A1, 0x0104 }, + { 0x01B1, 0x0105 }, + { 0x01C6, 0x0106 }, + { 0x01E6, 0x0107 }, + { 0x02C6, 0x0108 }, + { 0x02E6, 0x0109 }, + { 0x02C5, 0x010A }, + { 0x02E5, 0x010B }, + { 0x01C8, 0x010C }, + { 0x01E8, 0x010D }, + { 0x01CF, 0x010E }, + { 0x01EF, 0x010F }, + { 0x01D0, 0x0110 }, + { 0x01F0, 0x0111 }, + { 0x03AA, 0x0112 }, + { 0x03BA, 0x0113 }, + { 0x03CC, 0x0116 }, + { 0x03EC, 0x0117 }, + { 0x01CA, 0x0118 }, + { 0x01EA, 0x0119 }, + { 0x01CC, 0x011A }, + { 0x01EC, 0x011B }, + { 0x02D8, 0x011C }, + { 0x02F8, 0x011D }, + { 0x02AB, 0x011E }, + { 0x02BB, 0x011F }, + { 0x02D5, 0x0120 }, + { 0x02F5, 0x0121 }, + { 0x03AB, 0x0122 }, + { 0x03BB, 0x0123 }, + { 0x02A6, 0x0124 }, + { 0x02B6, 0x0125 }, + { 0x02A1, 0x0126 }, + { 0x02B1, 0x0127 }, + { 0x03A5, 0x0128 }, + { 0x03B5, 0x0129 }, + { 0x03CF, 0x012A }, + { 0x03EF, 0x012B }, + { 0x03C7, 0x012E }, + { 0x03E7, 0x012F }, + { 0x02A9, 0x0130 }, + { 0x02B9, 0x0131 }, + { 0x02AC, 0x0134 }, + { 0x02BC, 0x0135 }, + { 0x03D3, 0x0136 }, + { 0x03F3, 0x0137 }, + { 0x03A2, 0x0138 }, + { 0x01C5, 0x0139 }, + { 0x01E5, 0x013A }, + { 0x03A6, 0x013B }, + { 0x03B6, 0x013C }, + { 0x01A5, 0x013D }, + { 0x01B5, 0x013E }, + { 0x01A3, 0x0141 }, + { 0x01B3, 0x0142 }, + { 0x01D1, 0x0143 }, + { 0x01F1, 0x0144 }, + { 0x03D1, 0x0145 }, + { 0x03F1, 0x0146 }, + { 0x01D2, 0x0147 }, + { 0x01F2, 0x0148 }, + { 0x03BD, 0x014A }, + { 0x03BF, 0x014B }, + { 0x03D2, 0x014C }, + { 0x03F2, 0x014D }, + { 0x01D5, 0x0150 }, + { 0x01F5, 0x0151 }, + { 0x13BC, 0x0152 }, + { 0x13BD, 0x0153 }, + { 0x01C0, 0x0154 }, + { 0x01E0, 0x0155 }, + { 0x03A3, 0x0156 }, + { 0x03B3, 0x0157 }, + { 0x01D8, 0x0158 }, + { 0x01F8, 0x0159 }, + { 0x01A6, 0x015A }, + { 0x01B6, 0x015B }, + { 0x02DE, 0x015C }, + { 0x02FE, 0x015D }, + { 0x01AA, 0x015E }, + { 0x01BA, 0x015F }, + { 0x01A9, 0x0160 }, + { 0x01B9, 0x0161 }, + { 0x01DE, 0x0162 }, + { 0x01FE, 0x0163 }, + { 0x01AB, 0x0164 }, + { 0x01BB, 0x0165 }, + { 0x03AC, 0x0166 }, + { 0x03BC, 0x0167 }, + { 0x03DD, 0x0168 }, + { 0x03FD, 0x0169 }, + { 0x03DE, 0x016A }, + { 0x03FE, 0x016B }, + { 0x02DD, 0x016C }, + { 0x02FD, 0x016D }, + { 0x01D9, 0x016E }, + { 0x01F9, 0x016F }, + { 0x01DB, 0x0170 }, + { 0x01FB, 0x0171 }, + { 0x03D9, 0x0172 }, + { 0x03F9, 0x0173 }, + { 0x13BE, 0x0178 }, + { 0x01AC, 0x0179 }, + { 0x01BC, 0x017A }, + { 0x01AF, 0x017B }, + { 0x01BF, 0x017C }, + { 0x01AE, 0x017D }, + { 0x01BE, 0x017E }, + { 0x08F6, 0x0192 }, + { 0x01B7, 0x02C7 }, + { 0x01A2, 0x02D8 }, + { 0x01FF, 0x02D9 }, + { 0x01B2, 0x02DB }, + { 0x01BD, 0x02DD }, + { 0x07AE, 0x0385 }, + { 0x07A1, 0x0386 }, + { 0x07A2, 0x0388 }, + { 0x07A3, 0x0389 }, + { 0x07A4, 0x038A }, + { 0x07A7, 0x038C }, + { 0x07A8, 0x038E }, + { 0x07AB, 0x038F }, + { 0x07B6, 0x0390 }, + { 0x07C1, 0x0391 }, + { 0x07C2, 0x0392 }, + { 0x07C3, 0x0393 }, + { 0x07C4, 0x0394 }, + { 0x07C5, 0x0395 }, + { 0x07C6, 0x0396 }, + { 0x07C7, 0x0397 }, + { 0x07C8, 0x0398 }, + { 0x07C9, 0x0399 }, + { 0x07CA, 0x039A }, + { 0x07CB, 0x039B }, + { 0x07CC, 0x039C }, + { 0x07CD, 0x039D }, + { 0x07CE, 0x039E }, + { 0x07CF, 0x039F }, + { 0x07D0, 0x03A0 }, + { 0x07D1, 0x03A1 }, + { 0x07D2, 0x03A3 }, + { 0x07D4, 0x03A4 }, + { 0x07D5, 0x03A5 }, + { 0x07D6, 0x03A6 }, + { 0x07D7, 0x03A7 }, + { 0x07D8, 0x03A8 }, + { 0x07D9, 0x03A9 }, + { 0x07A5, 0x03AA }, + { 0x07A9, 0x03AB }, + { 0x07B1, 0x03AC }, + { 0x07B2, 0x03AD }, + { 0x07B3, 0x03AE }, + { 0x07B4, 0x03AF }, + { 0x07BA, 0x03B0 }, + { 0x07E1, 0x03B1 }, + { 0x07E2, 0x03B2 }, + { 0x07E3, 0x03B3 }, + { 0x07E4, 0x03B4 }, + { 0x07E5, 0x03B5 }, + { 0x07E6, 0x03B6 }, + { 0x07E7, 0x03B7 }, + { 0x07E8, 0x03B8 }, + { 0x07E9, 0x03B9 }, + { 0x07EA, 0x03BA }, + { 0x07EB, 0x03BB }, + { 0x07EC, 0x03BC }, + { 0x07ED, 0x03BD }, + { 0x07EE, 0x03BE }, + { 0x07EF, 0x03BF }, + { 0x07F0, 0x03C0 }, + { 0x07F1, 0x03C1 }, + { 0x07F3, 0x03C2 }, + { 0x07F2, 0x03C3 }, + { 0x07F4, 0x03C4 }, + { 0x07F5, 0x03C5 }, + { 0x07F6, 0x03C6 }, + { 0x07F7, 0x03C7 }, + { 0x07F8, 0x03C8 }, + { 0x07F9, 0x03C9 }, + { 0x07B5, 0x03CA }, + { 0x07B9, 0x03CB }, + { 0x07B7, 0x03CC }, + { 0x07B8, 0x03CD }, + { 0x07BB, 0x03CE }, + { 0x06B3, 0x0401 }, + { 0x06B1, 0x0402 }, + { 0x06B2, 0x0403 }, + { 0x06B4, 0x0404 }, + { 0x06B5, 0x0405 }, + { 0x06B6, 0x0406 }, + { 0x06B7, 0x0407 }, + { 0x06B8, 0x0408 }, + { 0x06B9, 0x0409 }, + { 0x06BA, 0x040A }, + { 0x06BB, 0x040B }, + { 0x06BC, 0x040C }, + { 0x06BE, 0x040E }, + { 0x06BF, 0x040F }, + { 0x06E1, 0x0410 }, + { 0x06E2, 0x0411 }, + { 0x06F7, 0x0412 }, + { 0x06E7, 0x0413 }, + { 0x06E4, 0x0414 }, + { 0x06E5, 0x0415 }, + { 0x06F6, 0x0416 }, + { 0x06FA, 0x0417 }, + { 0x06E9, 0x0418 }, + { 0x06EA, 0x0419 }, + { 0x06EB, 0x041A }, + { 0x06EC, 0x041B }, + { 0x06ED, 0x041C }, + { 0x06EE, 0x041D }, + { 0x06EF, 0x041E }, + { 0x06F0, 0x041F }, + { 0x06F2, 0x0420 }, + { 0x06F3, 0x0421 }, + { 0x06F4, 0x0422 }, + { 0x06F5, 0x0423 }, + { 0x06E6, 0x0424 }, + { 0x06E8, 0x0425 }, + { 0x06E3, 0x0426 }, + { 0x06FE, 0x0427 }, + { 0x06FB, 0x0428 }, + { 0x06FD, 0x0429 }, + { 0x06FF, 0x042A }, + { 0x06F9, 0x042B }, + { 0x06F8, 0x042C }, + { 0x06FC, 0x042D }, + { 0x06E0, 0x042E }, + { 0x06F1, 0x042F }, + { 0x06C1, 0x0430 }, + { 0x06C2, 0x0431 }, + { 0x06D7, 0x0432 }, + { 0x06C7, 0x0433 }, + { 0x06C4, 0x0434 }, + { 0x06C5, 0x0435 }, + { 0x06D6, 0x0436 }, + { 0x06DA, 0x0437 }, + { 0x06C9, 0x0438 }, + { 0x06CA, 0x0439 }, + { 0x06CB, 0x043A }, + { 0x06CC, 0x043B }, + { 0x06CD, 0x043C }, + { 0x06CE, 0x043D }, + { 0x06CF, 0x043E }, + { 0x06D0, 0x043F }, + { 0x06D2, 0x0440 }, + { 0x06D3, 0x0441 }, + { 0x06D4, 0x0442 }, + { 0x06D5, 0x0443 }, + { 0x06C6, 0x0444 }, + { 0x06C8, 0x0445 }, + { 0x06C3, 0x0446 }, + { 0x06DE, 0x0447 }, + { 0x06DB, 0x0448 }, + { 0x06DD, 0x0449 }, + { 0x06DF, 0x044A }, + { 0x06D9, 0x044B }, + { 0x06D8, 0x044C }, + { 0x06DC, 0x044D }, + { 0x06C0, 0x044E }, + { 0x06D1, 0x044F }, + { 0x06A3, 0x0451 }, + { 0x06A1, 0x0452 }, + { 0x06A2, 0x0453 }, + { 0x06A4, 0x0454 }, + { 0x06A5, 0x0455 }, + { 0x06A6, 0x0456 }, + { 0x06A7, 0x0457 }, + { 0x06A8, 0x0458 }, + { 0x06A9, 0x0459 }, + { 0x06AA, 0x045A }, + { 0x06AB, 0x045B }, + { 0x06AC, 0x045C }, + { 0x06AE, 0x045E }, + { 0x06AF, 0x045F }, + { 0x0CE0, 0x05D0 }, + { 0x0CE1, 0x05D1 }, + { 0x0CE2, 0x05D2 }, + { 0x0CE3, 0x05D3 }, + { 0x0CE4, 0x05D4 }, + { 0x0CE5, 0x05D5 }, + { 0x0CE6, 0x05D6 }, + { 0x0CE7, 0x05D7 }, + { 0x0CE8, 0x05D8 }, + { 0x0CE9, 0x05D9 }, + { 0x0CEA, 0x05DA }, + { 0x0CEB, 0x05DB }, + { 0x0CEC, 0x05DC }, + { 0x0CED, 0x05DD }, + { 0x0CEE, 0x05DE }, + { 0x0CEF, 0x05DF }, + { 0x0CF0, 0x05E0 }, + { 0x0CF1, 0x05E1 }, + { 0x0CF2, 0x05E2 }, + { 0x0CF3, 0x05E3 }, + { 0x0CF4, 0x05E4 }, + { 0x0CF5, 0x05E5 }, + { 0x0CF6, 0x05E6 }, + { 0x0CF7, 0x05E7 }, + { 0x0CF8, 0x05E8 }, + { 0x0CF9, 0x05E9 }, + { 0x0CFA, 0x05EA }, + { 0x05AC, 0x060C }, + { 0x05BB, 0x061B }, + { 0x05BF, 0x061F }, + { 0x05C1, 0x0621 }, + { 0x05C2, 0x0622 }, + { 0x05C3, 0x0623 }, + { 0x05C4, 0x0624 }, + { 0x05C5, 0x0625 }, + { 0x05C6, 0x0626 }, + { 0x05C7, 0x0627 }, + { 0x05C8, 0x0628 }, + { 0x05C9, 0x0629 }, + { 0x05CA, 0x062A }, + { 0x05CB, 0x062B }, + { 0x05CC, 0x062C }, + { 0x05CD, 0x062D }, + { 0x05CE, 0x062E }, + { 0x05CF, 0x062F }, + { 0x05D0, 0x0630 }, + { 0x05D1, 0x0631 }, + { 0x05D2, 0x0632 }, + { 0x05D3, 0x0633 }, + { 0x05D4, 0x0634 }, + { 0x05D5, 0x0635 }, + { 0x05D6, 0x0636 }, + { 0x05D7, 0x0637 }, + { 0x05D8, 0x0638 }, + { 0x05D9, 0x0639 }, + { 0x05DA, 0x063A }, + { 0x05E0, 0x0640 }, + { 0x05E1, 0x0641 }, + { 0x05E2, 0x0642 }, + { 0x05E3, 0x0643 }, + { 0x05E4, 0x0644 }, + { 0x05E5, 0x0645 }, + { 0x05E6, 0x0646 }, + { 0x05E7, 0x0647 }, + { 0x05E8, 0x0648 }, + { 0x05E9, 0x0649 }, + { 0x05EA, 0x064A }, + { 0x05EB, 0x064B }, + { 0x05EC, 0x064C }, + { 0x05ED, 0x064D }, + { 0x05EE, 0x064E }, + { 0x05EF, 0x064F }, + { 0x05F0, 0x0650 }, + { 0x05F1, 0x0651 }, + { 0x05F2, 0x0652 }, + { 0x0DA1, 0x0E01 }, + { 0x0DA2, 0x0E02 }, + { 0x0DA3, 0x0E03 }, + { 0x0DA4, 0x0E04 }, + { 0x0DA5, 0x0E05 }, + { 0x0DA6, 0x0E06 }, + { 0x0DA7, 0x0E07 }, + { 0x0DA8, 0x0E08 }, + { 0x0DA9, 0x0E09 }, + { 0x0DAA, 0x0E0A }, + { 0x0DAB, 0x0E0B }, + { 0x0DAC, 0x0E0C }, + { 0x0DAD, 0x0E0D }, + { 0x0DAE, 0x0E0E }, + { 0x0DAF, 0x0E0F }, + { 0x0DB0, 0x0E10 }, + { 0x0DB1, 0x0E11 }, + { 0x0DB2, 0x0E12 }, + { 0x0DB3, 0x0E13 }, + { 0x0DB4, 0x0E14 }, + { 0x0DB5, 0x0E15 }, + { 0x0DB6, 0x0E16 }, + { 0x0DB7, 0x0E17 }, + { 0x0DB8, 0x0E18 }, + { 0x0DB9, 0x0E19 }, + { 0x0DBA, 0x0E1A }, + { 0x0DBB, 0x0E1B }, + { 0x0DBC, 0x0E1C }, + { 0x0DBD, 0x0E1D }, + { 0x0DBE, 0x0E1E }, + { 0x0DBF, 0x0E1F }, + { 0x0DC0, 0x0E20 }, + { 0x0DC1, 0x0E21 }, + { 0x0DC2, 0x0E22 }, + { 0x0DC3, 0x0E23 }, + { 0x0DC4, 0x0E24 }, + { 0x0DC5, 0x0E25 }, + { 0x0DC6, 0x0E26 }, + { 0x0DC7, 0x0E27 }, + { 0x0DC8, 0x0E28 }, + { 0x0DC9, 0x0E29 }, + { 0x0DCA, 0x0E2A }, + { 0x0DCB, 0x0E2B }, + { 0x0DCC, 0x0E2C }, + { 0x0DCD, 0x0E2D }, + { 0x0DCE, 0x0E2E }, + { 0x0DCF, 0x0E2F }, + { 0x0DD0, 0x0E30 }, + { 0x0DD1, 0x0E31 }, + { 0x0DD2, 0x0E32 }, + { 0x0DD3, 0x0E33 }, + { 0x0DD4, 0x0E34 }, + { 0x0DD5, 0x0E35 }, + { 0x0DD6, 0x0E36 }, + { 0x0DD7, 0x0E37 }, + { 0x0DD8, 0x0E38 }, + { 0x0DD9, 0x0E39 }, + { 0x0DDA, 0x0E3A }, + { 0x0DDF, 0x0E3F }, + { 0x0DE0, 0x0E40 }, + { 0x0DE1, 0x0E41 }, + { 0x0DE2, 0x0E42 }, + { 0x0DE3, 0x0E43 }, + { 0x0DE4, 0x0E44 }, + { 0x0DE5, 0x0E45 }, + { 0x0DE6, 0x0E46 }, + { 0x0DE7, 0x0E47 }, + { 0x0DE8, 0x0E48 }, + { 0x0DE9, 0x0E49 }, + { 0x0DEA, 0x0E4A }, + { 0x0DEB, 0x0E4B }, + { 0x0DEC, 0x0E4C }, + { 0x0DED, 0x0E4D }, + { 0x0DF0, 0x0E50 }, + { 0x0DF1, 0x0E51 }, + { 0x0DF2, 0x0E52 }, + { 0x0DF3, 0x0E53 }, + { 0x0DF4, 0x0E54 }, + { 0x0DF5, 0x0E55 }, + { 0x0DF6, 0x0E56 }, + { 0x0DF7, 0x0E57 }, + { 0x0DF8, 0x0E58 }, + { 0x0DF9, 0x0E59 }, + { 0x0ED4, 0x11A8 }, + { 0x0ED5, 0x11A9 }, + { 0x0ED6, 0x11AA }, + { 0x0ED7, 0x11AB }, + { 0x0ED8, 0x11AC }, + { 0x0ED9, 0x11AD }, + { 0x0EDA, 0x11AE }, + { 0x0EDB, 0x11AF }, + { 0x0EDC, 0x11B0 }, + { 0x0EDD, 0x11B1 }, + { 0x0EDE, 0x11B2 }, + { 0x0EDF, 0x11B3 }, + { 0x0EE0, 0x11B4 }, + { 0x0EE1, 0x11B5 }, + { 0x0EE2, 0x11B6 }, + { 0x0EE3, 0x11B7 }, + { 0x0EE4, 0x11B8 }, + { 0x0EE5, 0x11B9 }, + { 0x0EE6, 0x11BA }, + { 0x0EE7, 0x11BB }, + { 0x0EE8, 0x11BC }, + { 0x0EE9, 0x11BD }, + { 0x0EEA, 0x11BE }, + { 0x0EEB, 0x11BF }, + { 0x0EEC, 0x11C0 }, + { 0x0EED, 0x11C1 }, + { 0x0EEE, 0x11C2 }, + { 0x0EF8, 0x11EB }, + { 0x0EFA, 0x11F9 }, + { 0x0AA2, 0x2002 }, + { 0x0AA1, 0x2003 }, + { 0x0AA3, 0x2004 }, + { 0x0AA4, 0x2005 }, + { 0x0AA5, 0x2007 }, + { 0x0AA6, 0x2008 }, + { 0x0AA7, 0x2009 }, + { 0x0AA8, 0x200A }, + { 0x0ABB, 0x2012 }, + { 0x0AAA, 0x2013 }, + { 0x0AA9, 0x2014 }, + { 0x07AF, 0x2015 }, + { 0x0CDF, 0x2017 }, + { 0x0AD0, 0x2018 }, + { 0x0AD1, 0x2019 }, + { 0x0AFD, 0x201A }, + { 0x0AD2, 0x201C }, + { 0x0AD3, 0x201D }, + { 0x0AFE, 0x201E }, + { 0x0AF1, 0x2020 }, + { 0x0AF2, 0x2021 }, + { 0x0AE6, 0x2022 }, + { 0x0AAE, 0x2026 }, + { 0x0AD6, 0x2032 }, + { 0x0AD7, 0x2033 }, + { 0x0AFC, 0x2038 }, + { 0x047E, 0x203E }, + { 0x20A0, 0x20A0 }, + { 0x20A1, 0x20A1 }, + { 0x20A2, 0x20A2 }, + { 0x20A3, 0x20A3 }, + { 0x20A4, 0x20A4 }, + { 0x20A5, 0x20A5 }, + { 0x20A6, 0x20A6 }, + { 0x20A7, 0x20A7 }, + { 0x20A8, 0x20A8 }, + { 0x0EFF, 0x20A9 }, + { 0x20A9, 0x20A9 }, + { 0x20AA, 0x20AA }, + { 0x20AB, 0x20AB }, + { 0x20AC, 0x20AC }, + { 0x0AB8, 0x2105 }, + { 0x06B0, 0x2116 }, + { 0x0AFB, 0x2117 }, + { 0x0AD4, 0x211E }, + { 0x0AC9, 0x2122 }, + { 0x0AB0, 0x2153 }, + { 0x0AB1, 0x2154 }, + { 0x0AB2, 0x2155 }, + { 0x0AB3, 0x2156 }, + { 0x0AB4, 0x2157 }, + { 0x0AB5, 0x2158 }, + { 0x0AB6, 0x2159 }, + { 0x0AB7, 0x215A }, + { 0x0AC3, 0x215B }, + { 0x0AC4, 0x215C }, + { 0x0AC5, 0x215D }, + { 0x0AC6, 0x215E }, + { 0x08FB, 0x2190 }, + { 0x08FC, 0x2191 }, + { 0x08FD, 0x2192 }, + { 0x08FE, 0x2193 }, + { 0x08CE, 0x21D2 }, + { 0x08CD, 0x21D4 }, + { 0x08EF, 0x2202 }, + { 0x08C5, 0x2207 }, + { 0x0BCA, 0x2218 }, + { 0x08D6, 0x221A }, + { 0x08C1, 0x221D }, + { 0x08C2, 0x221E }, + { 0x08DE, 0x2227 }, + { 0x0BA9, 0x2227 }, + { 0x08DF, 0x2228 }, + { 0x0BA8, 0x2228 }, + { 0x08DC, 0x2229 }, + { 0x0BC3, 0x2229 }, + { 0x08DD, 0x222A }, + { 0x0BD6, 0x222A }, + { 0x08BF, 0x222B }, + { 0x08C0, 0x2234 }, + { 0x08C8, 0x2245 }, + { 0x08BD, 0x2260 }, + { 0x08CF, 0x2261 }, + { 0x08BC, 0x2264 }, + { 0x08BE, 0x2265 }, + { 0x08DA, 0x2282 }, + { 0x0BDA, 0x2282 }, + { 0x08DB, 0x2283 }, + { 0x0BD8, 0x2283 }, + { 0x0BFC, 0x22A2 }, + { 0x0BDC, 0x22A3 }, + { 0x0BC2, 0x22A4 }, + { 0x0BCE, 0x22A5 }, + { 0x0BD3, 0x2308 }, + { 0x0BC4, 0x230A }, + { 0x0AFA, 0x2315 }, + { 0x08A4, 0x2320 }, + { 0x08A5, 0x2321 }, + { 0x0ABC, 0x2329 }, + { 0x0ABE, 0x232A }, + { 0x0BCC, 0x2395 }, + { 0x09E2, 0x2409 }, + { 0x09E5, 0x240A }, + { 0x09E9, 0x240B }, + { 0x09E3, 0x240C }, + { 0x09E4, 0x240D }, + { 0x09DF, 0x2422 }, + { 0x09E8, 0x2424 }, + { 0x09F1, 0x2500 }, + { 0x08A6, 0x2502 }, + { 0x09F8, 0x2502 }, + { 0x09EC, 0x250C }, + { 0x09EB, 0x2510 }, + { 0x09ED, 0x2514 }, + { 0x09EA, 0x2518 }, + { 0x09F4, 0x251C }, + { 0x09F5, 0x2524 }, + { 0x09F7, 0x252C }, + { 0x09F6, 0x2534 }, + { 0x09EE, 0x253C }, + { 0x09E1, 0x2592 }, + { 0x0ADF, 0x25A0 }, + { 0x0ACF, 0x25A1 }, + { 0x0AE7, 0x25AA }, + { 0x0AE1, 0x25AB }, + { 0x0ADB, 0x25AC }, + { 0x0AE2, 0x25AD }, + { 0x0AE8, 0x25B2 }, + { 0x0AE3, 0x25B3 }, + { 0x0ADD, 0x25B6 }, + { 0x0ACD, 0x25B7 }, + { 0x0AE9, 0x25BC }, + { 0x0AE4, 0x25BD }, + { 0x0ADC, 0x25C0 }, + { 0x0ACC, 0x25C1 }, + { 0x09E0, 0x25C6 }, + { 0x0ACE, 0x25CB }, + { 0x0BCF, 0x25CB }, + { 0x0ADE, 0x25CF }, + { 0x0AE0, 0x25E6 }, + { 0x0AE5, 0x2606 }, + { 0x0AF9, 0x260E }, + { 0x0ACA, 0x2613 }, + { 0x0AEA, 0x261C }, + { 0x0AEB, 0x261E }, + { 0x0AF8, 0x2640 }, + { 0x0AF7, 0x2642 }, + { 0x0AEC, 0x2663 }, + { 0x0AEE, 0x2665 }, + { 0x0AED, 0x2666 }, + { 0x0AF6, 0x266D }, + { 0x0AF5, 0x266F }, + { 0x0AF3, 0x2713 }, + { 0x0AF4, 0x2717 }, + { 0x0AD9, 0x271D }, + { 0x0AF0, 0x2720 }, + { 0x04A4, 0x3001 }, + { 0x04A1, 0x3002 }, + { 0x04A2, 0x300C }, + { 0x04A3, 0x300D }, + { 0x04DE, 0x309B }, + { 0x04DF, 0x309C }, + { 0x04A7, 0x30A1 }, + { 0x04B1, 0x30A2 }, + { 0x04A8, 0x30A3 }, + { 0x04B2, 0x30A4 }, + { 0x04A9, 0x30A5 }, + { 0x04B3, 0x30A6 }, + { 0x04AA, 0x30A7 }, + { 0x04B4, 0x30A8 }, + { 0x04AB, 0x30A9 }, + { 0x04B5, 0x30AA }, + { 0x04B6, 0x30AB }, + { 0x04B7, 0x30AD }, + { 0x04B8, 0x30AF }, + { 0x04B9, 0x30B1 }, + { 0x04BA, 0x30B3 }, + { 0x04BB, 0x30B5 }, + { 0x04BC, 0x30B7 }, + { 0x04BD, 0x30B9 }, + { 0x04BE, 0x30BB }, + { 0x04BF, 0x30BD }, + { 0x04C0, 0x30BF }, + { 0x04C1, 0x30C1 }, + { 0x04AF, 0x30C3 }, + { 0x04C2, 0x30C4 }, + { 0x04C3, 0x30C6 }, + { 0x04C4, 0x30C8 }, + { 0x04C5, 0x30CA }, + { 0x04C6, 0x30CB }, + { 0x04C7, 0x30CC }, + { 0x04C8, 0x30CD }, + { 0x04C9, 0x30CE }, + { 0x04CA, 0x30CF }, + { 0x04CB, 0x30D2 }, + { 0x04CC, 0x30D5 }, + { 0x04CD, 0x30D8 }, + { 0x04CE, 0x30DB }, + { 0x04CF, 0x30DE }, + { 0x04D0, 0x30DF }, + { 0x04D1, 0x30E0 }, + { 0x04D2, 0x30E1 }, + { 0x04D3, 0x30E2 }, + { 0x04AC, 0x30E3 }, + { 0x04D4, 0x30E4 }, + { 0x04AD, 0x30E5 }, + { 0x04D5, 0x30E6 }, + { 0x04AE, 0x30E7 }, + { 0x04D6, 0x30E8 }, + { 0x04D7, 0x30E9 }, + { 0x04D8, 0x30EA }, + { 0x04D9, 0x30EB }, + { 0x04DA, 0x30EC }, + { 0x04DB, 0x30ED }, + { 0x04DC, 0x30EF }, + { 0x04A6, 0x30F2 }, + { 0x04DD, 0x30F3 }, + { 0x04A5, 0x30FB }, + { 0x04B0, 0x30FC }, + { 0x0EA1, 0x3131 }, + { 0x0EA2, 0x3132 }, + { 0x0EA3, 0x3133 }, + { 0x0EA4, 0x3134 }, + { 0x0EA5, 0x3135 }, + { 0x0EA6, 0x3136 }, + { 0x0EA7, 0x3137 }, + { 0x0EA8, 0x3138 }, + { 0x0EA9, 0x3139 }, + { 0x0EAA, 0x313A }, + { 0x0EAB, 0x313B }, + { 0x0EAC, 0x313C }, + { 0x0EAD, 0x313D }, + { 0x0EAE, 0x313E }, + { 0x0EAF, 0x313F }, + { 0x0EB0, 0x3140 }, + { 0x0EB1, 0x3141 }, + { 0x0EB2, 0x3142 }, + { 0x0EB3, 0x3143 }, + { 0x0EB4, 0x3144 }, + { 0x0EB5, 0x3145 }, + { 0x0EB6, 0x3146 }, + { 0x0EB7, 0x3147 }, + { 0x0EB8, 0x3148 }, + { 0x0EB9, 0x3149 }, + { 0x0EBA, 0x314A }, + { 0x0EBB, 0x314B }, + { 0x0EBC, 0x314C }, + { 0x0EBD, 0x314D }, + { 0x0EBE, 0x314E }, + { 0x0EBF, 0x314F }, + { 0x0EC0, 0x3150 }, + { 0x0EC1, 0x3151 }, + { 0x0EC2, 0x3152 }, + { 0x0EC3, 0x3153 }, + { 0x0EC4, 0x3154 }, + { 0x0EC5, 0x3155 }, + { 0x0EC6, 0x3156 }, + { 0x0EC7, 0x3157 }, + { 0x0EC8, 0x3158 }, + { 0x0EC9, 0x3159 }, + { 0x0ECA, 0x315A }, + { 0x0ECB, 0x315B }, + { 0x0ECC, 0x315C }, + { 0x0ECD, 0x315D }, + { 0x0ECE, 0x315E }, + { 0x0ECF, 0x315F }, + { 0x0ED0, 0x3160 }, + { 0x0ED1, 0x3161 }, + { 0x0ED2, 0x3162 }, + { 0x0ED3, 0x3163 }, + { 0x0EEF, 0x316D }, + { 0x0EF0, 0x3171 }, + { 0x0EF1, 0x3178 }, + { 0x0EF2, 0x317F }, + { 0x0EF4, 0x3184 }, + { 0x0EF5, 0x3186 }, + { 0x0EF6, 0x318D }, + { 0x0EF7, 0x318E } +}; + +KeySym KeyMappingX11::get_keysym_from_unicode(unsigned int p_unicode) { + + /* Latin 1 */ + + if (p_unicode>=0x20 && p_unicode<=0x7e) + return p_unicode; + + if (p_unicode>=0xa0 && p_unicode<=0xff) + return p_unicode; + + int middle,low=0,high=_UNICODE_MAX-1; + do { + middle=(high+low)/2; + if ( _unicode_to_xkeysym[middle].keysym==p_unicode) + return _unicode_to_xkeysym[middle].keysym; + if ( _unicode_to_xkeysym[middle].keysym<=p_unicode ) + low=middle+1; + else + high=middle-1; + } while (high>=low); + + // if not found, let's hope X understands it as unicode + return p_unicode|0x01000000; +} diff --git a/platform/x11/key_mapping_x11.h b/platform/x11/key_mapping_x11.h new file mode 100644 index 00000000000..97393d92f31 --- /dev/null +++ b/platform/x11/key_mapping_x11.h @@ -0,0 +1,55 @@ +/*************************************************************************/ +/* key_mapping_x11.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 KEY_MAPPING_X11_H +#define KEY_MAPPING_X11_H + +/** + @author Juan Linietsky +*/ +#include +#include +#define XK_MISCELLANY +#define XK_LATIN1 +#define XK_XKB_KEYS +#include + +#include "os/keyboard.h" + +class KeyMappingX11 { + KeyMappingX11() {}; +public: + static unsigned int get_keycode(KeySym p_keysym); + static KeySym get_keysym(unsigned int p_code); + static unsigned int get_unicode_from_keysym(KeySym p_keysym); + static KeySym get_keysym_from_unicode(unsigned int p_unicode); + +}; + + +#endif diff --git a/platform/x11/logo.png b/platform/x11/logo.png new file mode 100644 index 00000000000..c40214d6deb Binary files /dev/null and b/platform/x11/logo.png differ diff --git a/platform/x11/os_x11.cpp b/platform/x11/os_x11.cpp new file mode 100644 index 00000000000..79dd6f12343 --- /dev/null +++ b/platform/x11/os_x11.cpp @@ -0,0 +1,1300 @@ +/*************************************************************************/ +/* os_x11.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 "servers/visual/visual_server_raster.h" +#include "drivers/gles2/rasterizer_gles2.h" +#include "drivers/gles1/rasterizer_gles1.h" +#include "os_x11.h" +#include "key_mapping_x11.h" +#include +#include +#include "print_string.h" +#include "servers/physics/physics_server_sw.h" + +#include "X11/Xutil.h" +#include "main/main.h" + + + +#include +#include +#include +#include +#include + +//stupid linux.h +#ifdef KEY_TAB +#undef KEY_TAB +#endif + + +#include +#include "os/pc_joystick_map.h" + +#undef CursorShape + +int OS_X11::get_video_driver_count() const { + + return 2; +} +const char * OS_X11::get_video_driver_name(int p_driver) const { + + return p_driver==0?"GLES2":"GLES1"; +} +OS::VideoMode OS_X11::get_default_video_mode() const { + + return OS::VideoMode(800,600,false); +} + +void OS_X11::initialize(const VideoMode& p_desired,int p_video_driver,int p_audio_driver) { + + last_button_state=0; + dpad_last[0]=0; + dpad_last[1]=0; + + xmbstring=NULL; + event_id=0; + x11_window=0; + last_click_ms=0; + args=OS::get_singleton()->get_cmdline_args(); + current_videomode=p_desired; + main_loop=NULL; + last_timestamp=0; + last_mouse_pos_valid=false; + last_keyrelease_time=0; + + if (get_render_thread_mode()==RENDER_SEPARATE_THREAD) { + XInitThreads(); + } + + /** XLIB INITIALIZATION **/ + x11_display = XOpenDisplay(NULL); + + char * modifiers = XSetLocaleModifiers ("@im=none"); + ERR_FAIL_COND( modifiers == NULL ); + + xim = XOpenIM (x11_display, NULL, NULL, NULL); + + + if (xim == NULL) { + WARN_PRINT("XOpenIM failed"); + xim_style=NULL; + } else { + ::XIMStyles *xim_styles=NULL; + xim_style=0; + char *imvalret=NULL; + imvalret = XGetIMValues(xim, XNQueryInputStyle, &xim_styles, NULL); + if (imvalret != NULL || xim_styles == NULL) { + fprintf (stderr, "Input method doesn't support any styles\n"); + } + + if (xim_styles) { + xim_style = 0; + for (int i=0;icount_styles;i++) { + + if (xim_styles->supported_styles[i] == + (XIMPreeditNothing | XIMStatusNothing)) { + + xim_style = xim_styles->supported_styles[i]; + break; + } + } + + XFree (xim_styles); + } + } + + /* + char* windowid = getenv("GODOT_WINDOWID"); + if (windowid) { + + //freopen("/home/punto/stdout", "w", stdout); + //reopen("/home/punto/stderr", "w", stderr); + x11_window = atol(windowid); + + XWindowAttributes xwa; + XGetWindowAttributes(x11_display,x11_window,&xwa); + + current_videomode.width = xwa.width; + current_videomode.height = xwa.height; + }; + */ + + // maybe contextgl wants to be in charge of creating the window + //print_line("def videomode "+itos(current_videomode.width)+","+itos(current_videomode.height)); +#if defined(OPENGL_ENABLED) || defined(LEGACYGL_ENABLED) + context_gl = memnew( ContextGL_X11( x11_display, x11_window,current_videomode, false ) ); + context_gl->initialize(); + + if (p_video_driver == 0) { + rasterizer = memnew( RasterizerGLES2 ); + } else { + rasterizer = memnew( RasterizerGLES1 ); + }; + +#endif + visual_server = memnew( VisualServerRaster(rasterizer) ); + + if (get_render_thread_mode()!=RENDER_THREAD_UNSAFE) { + + visual_server =memnew(VisualServerWrapMT(visual_server,get_render_thread_mode()==RENDER_SEPARATE_THREAD)); + } + + AudioDriverManagerSW::get_driver(p_audio_driver)->set_singleton(); + + if (AudioDriverManagerSW::get_driver(p_audio_driver)->init()!=OK) { + + ERR_PRINT("Initializing audio failed."); + } + + sample_manager = memnew( SampleManagerMallocSW ); + audio_server = memnew( AudioServerSW(sample_manager) ); + audio_server->init(); + spatial_sound_server = memnew( SpatialSoundServerSW ); + spatial_sound_server->init(); + spatial_sound_2d_server = memnew( SpatialSound2DServerSW ); + spatial_sound_2d_server->init(); + + + ERR_FAIL_COND(!visual_server); + ERR_FAIL_COND(x11_window==0); + + XSetWindowAttributes new_attr; + + new_attr.event_mask=KeyPressMask | KeyReleaseMask | ButtonPressMask | + ButtonReleaseMask | EnterWindowMask | + LeaveWindowMask | PointerMotionMask | + Button1MotionMask | + Button2MotionMask | Button3MotionMask | + Button4MotionMask | Button5MotionMask | + ButtonMotionMask | KeymapStateMask | + ExposureMask | VisibilityChangeMask | + StructureNotifyMask | + SubstructureNotifyMask | SubstructureRedirectMask | + FocusChangeMask | PropertyChangeMask | + ColormapChangeMask | OwnerGrabButtonMask; + + XChangeWindowAttributes(x11_display, x11_window,CWEventMask,&new_attr); + + wm_delete = XInternAtom(x11_display, "WM_DELETE_WINDOW", true); + XSetWMProtocols(x11_display, x11_window, &wm_delete, 1); + + + if (xim && xim_style) { + + xic = XCreateIC (xim,XNInputStyle, xim_style,XNClientWindow,x11_window,XNFocusWindow, x11_window, (char*)NULL); + } else { + + xic=NULL; + WARN_PRINT("XCreateIC couldn't create xic"); + + } + + XcursorSetTheme(x11_display,"default"); + cursor_size = XcursorGetDefaultSize(x11_display); + cursor_theme = XcursorGetTheme(x11_display); + + if (!cursor_theme) { + print_line("not found theme"); + cursor_theme="default"; + } + + for(int i=0;iinit(); + // + physics_server = memnew( PhysicsServerSW ); + physics_server->init(); + physics_2d_server = memnew( Physics2DServerSW ); + physics_2d_server->init(); + + input = memnew( InputDefault ); + + probe_joystick(); + + _ensure_data_dir(); + + net_wm_icon = XInternAtom(x11_display, "_NET_WM_ICON", False); + + + //printf("got map notify\n"); + +} +void OS_X11::finalize() { + + if(main_loop) + memdelete(main_loop); + main_loop=NULL; + + spatial_sound_server->finish(); + memdelete(spatial_sound_server); + spatial_sound_2d_server->finish(); + memdelete(spatial_sound_2d_server); + + //if (debugger_connection_console) { +// memdelete(debugger_connection_console); +//} + + audio_server->finish(); + memdelete(audio_server); + memdelete(sample_manager); + + visual_server->finish(); + memdelete(visual_server); + memdelete(rasterizer); + + physics_server->finish(); + memdelete(physics_server); + + physics_2d_server->finish(); + memdelete(physics_2d_server); + + memdelete(input); + +#if defined(OPENGL_ENABLED) || defined(LEGACYGL_ENABLED) + memdelete(context_gl); +#endif + + + XCloseDisplay(x11_display); + if (xmbstring) + memfree(xmbstring); + + args.clear(); +} + + +void OS_X11::set_mouse_mode(MouseMode p_mode) { + + print_line("WUTF "+itos(p_mode)+" old "+itos(mouse_mode)); + if (p_mode==mouse_mode) + return; + + if (mouse_mode==MOUSE_MODE_CAPTURED) + XUngrabPointer(x11_display, CurrentTime); + if (mouse_mode!=MOUSE_MODE_VISIBLE && p_mode==MOUSE_MODE_VISIBLE) + XUndefineCursor(x11_display,x11_window); + if (p_mode!=MOUSE_MODE_VISIBLE && mouse_mode==MOUSE_MODE_VISIBLE) { + XDefineCursor(x11_display,x11_window,null_cursor); + } + + mouse_mode=p_mode; + + if (mouse_mode==MOUSE_MODE_CAPTURED) { + if (XGrabPointer(x11_display, x11_window, True, + ButtonPressMask | ButtonReleaseMask | + PointerMotionMask, GrabModeAsync, GrabModeAsync, + x11_window, None, CurrentTime) != + GrabSuccess) { + ERR_PRINT("NO GRAB"); + } + + center.x = current_videomode.width/2; + center.y = current_videomode.height/2; + XWarpPointer(x11_display, None, x11_window, + 0,0,0,0, (int)center.x, (int)center.y); + } + +} + +OS::MouseMode OS_X11::get_mouse_mode() const { + + return mouse_mode; +} + + + +int OS_X11::get_mouse_button_state() const { + + return last_button_state; +} + +Point2 OS_X11::get_mouse_pos() const { + + return last_mouse_pos; +} + +void OS_X11::set_window_title(const String& p_title) { + + XStoreName(x11_display,x11_window,p_title.utf8().get_data()); +} + +void OS_X11::set_video_mode(const VideoMode& p_video_mode,int p_screen) { + + +} +OS::VideoMode OS_X11::get_video_mode(int p_screen) const { + + return current_videomode; +} +void OS_X11::get_fullscreen_mode_list(List *p_list,int p_screen) const { + + +} + + +InputModifierState OS_X11::get_key_modifier_state(unsigned int p_x11_state) { + + InputModifierState state; + + state.shift = (p_x11_state&ShiftMask); + state.control = (p_x11_state&ControlMask); + state.alt = (p_x11_state&Mod1Mask /*|| p_x11_state&Mod5Mask*/); //altgr should not count as alt + state.meta = (p_x11_state&Mod4Mask); + + return state; +} + +unsigned int OS_X11::get_mouse_button_state(unsigned int p_x11_state) { + + unsigned int state=0; + + if (p_x11_state&Button1Mask) { + + state|=1<<0; + } + + if (p_x11_state&Button3Mask) { + + state|=1<<1; + } + + if (p_x11_state&Button2Mask) { + + state|=1<<2; + } + + if (p_x11_state&Button4Mask) { + + state|=1<<3; + } + + if (p_x11_state&Button5Mask) { + + state|=1<<4; + } + + last_button_state=state; + return state; +} + +void OS_X11::handle_key_event(XKeyEvent *p_event) { + + + // X11 functions don't know what const is + XKeyEvent *xkeyevent = p_event; + + // This code was pretty difficult to write. + // The docs stink and every toolkit seems to + // do it in a different way. + + /* Phase 1, obtain a proper keysym */ + + // This was also very difficult to figure out. + // You'd expect you could just use Keysym provided by + // XKeycodeToKeysym to obtain internationalized + // input.. WRONG!! + // you must use XLookupString (???) which not only wastes + // cycles generating an unnecesary string, but also + // still works in half the cases. (won't handle deadkeys) + // For more complex input methods (deadkeys and more advanced) + // you have to use XmbLookupString (??). + // So.. then you have to chosse which of both results + // you want to keep. + // This is a real bizarreness and cpu waster. + + KeySym keysym_keycode=0; // keysym used to find a keycode + KeySym keysym_unicode=0; // keysym used to find unicode + + int nbytes=0; // bytes the string takes + + // XLookupString returns keysyms usable as nice scancodes/ + char str[256+1]; + nbytes=XLookupString(xkeyevent, str, 256, &keysym_keycode, NULL); + + // Meanwhile, XLookupString returns keysyms useful for unicode. + + + if (!xmbstring) { + // keep a temporary buffer for the string + xmbstring=(char*)memalloc(sizeof(char)*8); + xmblen=8; + } + + if (xkeyevent->type == KeyPress && xic) { + + Status status; + do { + + int mnbytes = XmbLookupString (xic, xkeyevent, xmbstring, xmblen - 1, &keysym_unicode, &status); + xmbstring[mnbytes] = '\0'; + + if (status == XBufferOverflow) { + xmblen = mnbytes + 1; + xmbstring = (char*)memrealloc (xmbstring, xmblen); + } + } while (status == XBufferOverflow); + } + + + /* Phase 2, obtain a pigui keycode from the keysym */ + + // KeyMappingX11 just translated the X11 keysym to a PIGUI + // keysym, so it works in all platforms the same. + + unsigned int keycode = KeyMappingX11::get_keycode(keysym_keycode); + + /* Phase 3, obtain an unicode character from the keysym */ + + // KeyMappingX11 also translates keysym to unicode. + // It does a binary search on a table to translate + // most properly. + //print_line("keysym_unicode: "+rtos(keysym_unicode)); + unsigned int unicode = keysym_unicode>0? KeyMappingX11::get_unicode_from_keysym(keysym_unicode):0; + + + /* Phase 4, determine if event must be filtered */ + + // This seems to be a side-effect of using XIM. + // XEventFilter looks like a core X11 funciton, + // but it's actually just used to see if we must + // ignore a deadkey, or events XIM determines + // must not reach the actual gui. + // Guess it was a design problem of the extension + + bool keypress = xkeyevent->type == KeyPress; + + if (xkeyevent->type == KeyPress && xic) { + if (XFilterEvent((XEvent*)xkeyevent, x11_window)) + return; + } + + if (keycode==0 && unicode==0) + return; + + /* Phase 5, determine modifier mask */ + + // No problems here, except I had no way to + // know Mod1 was ALT and Mod4 was META (applekey/winkey) + // just tried Mods until i found them. + + //print_line("mod1: "+itos(xkeyevent->state&Mod1Mask)+" mod 5: "+itos(xkeyevent->state&Mod5Mask)); + + InputModifierState state = get_key_modifier_state(xkeyevent->state); + + /* Phase 6, determine echo character */ + + // Echo characters in X11 are a keyrelease and a keypress + // one after the other with the (almot) same timestamp. + // To detect them, i use XPeekEvent and check that their + // difference in time is below a treshold. + + bool echo=false; + + if (xkeyevent->type == KeyPress) { + + // saved the time of the last keyrelease to see + // if it's the same as this keypress. + if (xkeyevent->time==last_keyrelease_time) + echo=true; + + } else { + + // make sure there are events pending, + // so this call won't block. + if (XPending(x11_display)>0) { + XEvent peek_event; + XPeekEvent(x11_display, &peek_event); + + // I'm using a treshold of 5 msecs, + // since sometimes there seems to be a little + // jitter. I'm still not convinced that all this approach + // is correct, but the xorg developers are + // not very helpful today. + + ::Time tresh=ABS(peek_event.xkey.time-xkeyevent->time); + if (peek_event.type == KeyPress && tresh<5 ) + echo=true; + + // use the time from peek_event so it always works + last_keyrelease_time=peek_event.xkey.time; + } else { + last_keyrelease_time=xkeyevent->time; + } + + // save the time to check for echo when keypress happens + + } + + + /* Phase 7, send event to Window */ + + InputEvent event; + event.ID=++event_id; + event.type = InputEvent::KEY; + event.device=0; + event.key.mod=state; + event.key.pressed=keypress; + + if (keycode>='a' && keycode<='z') + keycode-='a'-'A'; + + event.key.scancode=keycode; + event.key.unicode=unicode; + event.key.echo=echo; + + if (event.key.scancode==KEY_BACKTAB) { + //make it consistent accross platforms. + event.key.scancode=KEY_TAB; + event.key.mod.shift=true; + } + + //printf("key: %x\n",event.key.scancode); + input->parse_input_event( event); + + +} + +void OS_X11::process_xevents() { + + //printf("checking events %i\n", XPending(x11_display)); + + bool do_mouse_warp=false; + + while (XPending(x11_display) > 0) { + XEvent event; + XNextEvent(x11_display, &event); + + switch (event.type) { + case Expose: + Main::force_redraw(); + break; + + case NoExpose: + minimized = true; + break; + + case VisibilityNotify: { + + XVisibilityEvent * visibility = (XVisibilityEvent *)&event; + minimized = (visibility->state == VisibilityFullyObscured); + + } break; + + case FocusIn: + main_loop->notification(MainLoop::NOTIFICATION_WM_FOCUS_IN); + if (mouse_mode==MOUSE_MODE_CAPTURED) { + XGrabPointer(x11_display, x11_window, True, + ButtonPressMask | ButtonReleaseMask | + PointerMotionMask, GrabModeAsync, GrabModeAsync, + x11_window, None, CurrentTime); + } + break; + + case FocusOut: + main_loop->notification(MainLoop::NOTIFICATION_WM_FOCUS_OUT); + if (mouse_mode==MOUSE_MODE_CAPTURED) { + //dear X11, I try, I really try, but you never work, you do whathever you want. + XUngrabPointer(x11_display, CurrentTime); + } + break; + + case ConfigureNotify: + /* call resizeGLScene only if our window-size changed */ + + if ((event.xconfigure.width == current_videomode.width) && + (event.xconfigure.height == current_videomode.height)) + break; + + current_videomode.width=event.xconfigure.width; + current_videomode.height=event.xconfigure.height; + break; + case ButtonPress: + case ButtonRelease: { + + /* exit in case of a mouse button press */ + last_timestamp=event.xbutton.time; + if (mouse_mode==MOUSE_MODE_CAPTURED) { + event.xbutton.x=last_mouse_pos.x; + event.xbutton.y=last_mouse_pos.y; + } + + InputEvent mouse_event; + mouse_event.ID=++event_id; + mouse_event.type = InputEvent::MOUSE_BUTTON; + mouse_event.device=0; + mouse_event.mouse_button.mod = get_key_modifier_state(event.xbutton.state); + mouse_event.mouse_button.button_mask = get_mouse_button_state(event.xbutton.state); + mouse_event.mouse_button.x=event.xbutton.x; + mouse_event.mouse_button.y=event.xbutton.y; + mouse_event.mouse_button.global_x=event.xbutton.x; + mouse_event.mouse_button.global_y=event.xbutton.y; + mouse_event.mouse_button.button_index=event.xbutton.button; + if (mouse_event.mouse_button.button_index==2) + mouse_event.mouse_button.button_index=3; + else if (mouse_event.mouse_button.button_index==3) + mouse_event.mouse_button.button_index=2; + + mouse_event.mouse_button.pressed=(event.type==ButtonPress); + + + if (event.type==ButtonPress && event.xbutton.button==1) { + + uint64_t diff = get_ticks_usec()/1000 - last_click_ms; + + if (diff<400 && Point2(last_click_pos).distance_to(Point2(event.xbutton.x,event.xbutton.y))<5) { + + last_click_ms=0; + last_click_pos = Point2(-100,-100); + mouse_event.mouse_button.doubleclick=true; + mouse_event.ID=++event_id; + + } else { + last_click_ms+=diff; + last_click_pos = Point2(event.xbutton.x,event.xbutton.y); + } + } + + input->parse_input_event( mouse_event); + + + } break; + case MotionNotify: { + + + last_timestamp=event.xmotion.time; + + // Motion is also simple. + // A little hack is in order + // to be able to send relative motion events. + + Point2i pos( event.xmotion.x, event.xmotion.y ); + + if (mouse_mode==MOUSE_MODE_CAPTURED) { +#if 1 + Vector2 c = Point2i(current_videomode.width/2,current_videomode.height/2); + if (pos==Point2i(current_videomode.width/2,current_videomode.height/2)) { + //this sucks, it's a hack, etc and is a little inaccurate, etc. + //but nothing I can do, X11 sucks. + + center=pos; + break; + } + + Point2i ncenter = pos; + pos = last_mouse_pos + ( pos-center ); + center=ncenter; + do_mouse_warp=true; +#else + //Dear X11, thanks for making my life miserable + + center.x = current_videomode.width/2; + center.y = current_videomode.height/2; + pos = last_mouse_pos + ( pos-center ); + if (pos==last_mouse_pos) + break; + XWarpPointer(x11_display, None, x11_window, + 0,0,0,0, (int)center.x, (int)center.y); +#endif + + } + + + if (!last_mouse_pos_valid) { + + last_mouse_pos=pos; + last_mouse_pos_valid=true; + } + + Point2i rel = pos - last_mouse_pos; + + InputEvent motion_event; + motion_event.ID=++event_id; + motion_event.type=InputEvent::MOUSE_MOTION; + motion_event.device=0; + + motion_event.mouse_motion.mod = get_key_modifier_state(event.xmotion.state); + motion_event.mouse_motion.button_mask = get_mouse_button_state(event.xmotion.state); + motion_event.mouse_motion.x=pos.x; + motion_event.mouse_motion.y=pos.y; + input->set_mouse_pos(pos); + motion_event.mouse_motion.global_x=pos.x; + motion_event.mouse_motion.global_y=pos.y; + motion_event.mouse_motion.speed_x=input->get_mouse_speed().x; + motion_event.mouse_motion.speed_y=input->get_mouse_speed().y; + + motion_event.mouse_motion.relative_x=rel.x; + motion_event.mouse_motion.relative_y=rel.y; + + last_mouse_pos=pos; + + input->parse_input_event( motion_event); + + } break; + case KeyPress: + case KeyRelease: { + + last_timestamp=event.xkey.time; + + // key event is a little complex, so + // it will be handled in it's own function. + handle_key_event( (XKeyEvent*)&event ); + } break; + case SelectionRequest: { + + XSelectionRequestEvent *req; + XEvent e, respond; + e = event; + + req=&(e.xselectionrequest); + if (req->target == XA_STRING || req->target == XInternAtom(x11_display, "COMPOUND_TEXT", 0) || + req->target == XInternAtom(x11_display, "UTF8_STRING", 0)) + { + CharString clip = OS::get_clipboard().utf8(); + XChangeProperty (x11_display, + req->requestor, + req->property, + req->target, + 8, + PropModeReplace, + (unsigned char*)clip.get_data(), + clip.length()); + respond.xselection.property=req->property; + } else if (req->target == XInternAtom(x11_display, "TARGETS", 0)) { + + Atom data[2]; + data[0] = XInternAtom(x11_display, "UTF8_STRING", 0); + data[1] = XA_STRING; + XChangeProperty (x11_display, req->requestor, req->property, req->target, + 8, PropModeReplace, (unsigned char *) &data, + sizeof (data)); + respond.xselection.property=req->property; + + } else { + printf ("No String %x\n", + (int)req->target); + respond.xselection.property= None; + } + respond.xselection.type= SelectionNotify; + respond.xselection.display= req->display; + respond.xselection.requestor= req->requestor; + respond.xselection.selection=req->selection; + respond.xselection.target= req->target; + respond.xselection.time = req->time; + XSendEvent (x11_display, req->requestor,0,0,&respond); + XFlush (x11_display); + } break; + + + case ClientMessage: + + if ((unsigned int)event.xclient.data.l[0]==(unsigned int)wm_delete) + main_loop->notification(MainLoop::NOTIFICATION_WM_QUIT_REQUEST); + break; + default: + break; + } + } + + XFlush(x11_display); + + if (do_mouse_warp) { + + XWarpPointer(x11_display, None, x11_window, + 0,0,0,0, (int)current_videomode.width/2, (int)current_videomode.height/2); + + } +} + +MainLoop *OS_X11::get_main_loop() const { + + return main_loop; +} + +void OS_X11::delete_main_loop() { + + if (main_loop) + memdelete(main_loop); + main_loop=NULL; +} + +void OS_X11::set_main_loop( MainLoop * p_main_loop ) { + + main_loop=p_main_loop; + input->set_main_loop(p_main_loop); +} + +bool OS_X11::can_draw() const { + + return !minimized; +}; + +void OS_X11::set_clipboard(const String& p_text) { + + OS::set_clipboard(p_text); + + XSetSelectionOwner(x11_display, XA_PRIMARY, x11_window, CurrentTime); + XSetSelectionOwner(x11_display, XInternAtom(x11_display, "CLIPBOARD", 0), x11_window, CurrentTime); +}; + +static String _get_clipboard(Atom p_source, Window x11_window, ::Display* x11_display, String p_internal_clipboard) { + + String ret; + + Atom type; + Atom selection = XA_PRIMARY; + int format, result; + unsigned long len, bytes_left, dummy; + unsigned char *data; + Window Sown = XGetSelectionOwner (x11_display, p_source); + + if (Sown == x11_window) { + + printf("returning internal clipboard\n"); + return p_internal_clipboard; + }; + + if (Sown != None) { + XConvertSelection (x11_display, p_source, XA_STRING, selection, + x11_window, CurrentTime); + XFlush (x11_display); + while (true) { + XEvent event; + XNextEvent(x11_display, &event); + if (event.type == SelectionNotify && event.xselection.requestor == x11_window) { + break; + }; + }; + + // + // Do not get any data, see how much data is there + // + XGetWindowProperty (x11_display, x11_window, + selection, // Tricky.. + 0, 0, // offset - len + 0, // Delete 0==FALSE + AnyPropertyType, //flag + &type, // return type + &format, // return format + &len, &bytes_left, //that + &data); + // DATA is There + if (bytes_left > 0) + { + result = XGetWindowProperty (x11_display, x11_window, + selection, 0,bytes_left,0, + AnyPropertyType, &type,&format, + &len, &dummy, &data); + if (result == Success) { + ret.parse_utf8((const char*)data); + } else printf ("FAIL\n"); + XFree (data); + } + } + + return ret; + +}; + +String OS_X11::get_clipboard() const { + + String ret; + ret = _get_clipboard(XInternAtom(x11_display, "CLIPBOARD", 0), x11_window, x11_display, OS::get_clipboard()); + + if (ret == "") { + ret = _get_clipboard(XA_PRIMARY, x11_window, x11_display, OS::get_clipboard()); + }; + + return ret; +}; + +String OS_X11::get_name() { + + return "X11"; +} + + +void OS_X11::close_joystick(int p_id) { + + if (p_id == -1) { + for (int i=0; i 0) { + + int ev_count = bytes / sizeof(js_event); + for (int j=0; j0) + val=+1; + + InputEvent ev; + ev.type = InputEvent::JOYSTICK_BUTTON; + ev.ID = ++event_id; + + + if (val!=dpad_last[axis]) { + + int prev_val = dpad_last[axis]; + if (prev_val!=0) { + + ev.joy_button.pressed=false; + ev.joy_button.pressure=0.0; + if (event.number==5) + ev.joy_button.button_index=JOY_DPAD_LEFT+(prev_val+1)/2; + if (event.number==6) + ev.joy_button.button_index=JOY_DPAD_UP+(prev_val+1)/2; + + input->parse_input_event( ev ); + } + } + + if (val!=0) { + + ev.joy_button.pressed=true; + ev.joy_button.pressure=1.0; + if (event.number==5) + ev.joy_button.button_index=JOY_DPAD_LEFT+(val+1)/2; + if (event.number==6) + ev.joy_button.button_index=JOY_DPAD_UP+(val+1)/2; + + input->parse_input_event( ev ); + } + + + dpad_last[axis]=val; + + } + //print_line("ev: "+itos(event.number)+" val: "+ rtos((float)event.value / (float)MAX_JOY_AXIS)); + if (event.number >= JOY_AXIS_MAX) + break; + //ERR_FAIL_COND(event.number >= JOY_AXIS_MAX); + ievent.type = InputEvent::JOYSTICK_MOTION; + ievent.ID = ++event_id; + ievent.joy_motion.axis = _pc_joystick_get_native_axis(event.number); + ievent.joy_motion.axis_value = (float)event.value / (float)MAX_JOY_AXIS; + joysticks[i].last_axis[event.number] = event.value; + input->parse_input_event( ievent ); + }; + break; + + case JS_EVENT_BUTTON: + + + ievent.type = InputEvent::JOYSTICK_BUTTON; + ievent.ID = ++event_id; + ievent.joy_button.button_index = _pc_joystick_get_native_button(event.number); + ievent.joy_button.pressed = event.value; + input->parse_input_event( ievent ); + break; + }; + }; + }; + }; +}; + +void OS_X11::set_cursor_shape(CursorShape p_shape) { + + ERR_FAIL_INDEX(p_shape,CURSOR_MAX); + + if (p_shape==current_cursor) + return; + if (mouse_mode==MOUSE_MODE_VISIBLE) { + if (cursors[p_shape]!=None) + XDefineCursor(x11_display,x11_window,cursors[p_shape]); + else if (cursors[CURSOR_ARROW]!=None) + XDefineCursor(x11_display,x11_window,cursors[CURSOR_ARROW]); + } + + + current_cursor=p_shape; +} + + +void OS_X11::release_rendering_thread() { + + context_gl->release_current(); + +} + +void OS_X11::make_rendering_thread() { + + context_gl->make_current(); +} + +void OS_X11::swap_buffers() { + + context_gl->swap_buffers(); +} + + +void OS_X11::set_icon(const Image& p_icon) { + + //does not work, if anyone knows why, please fix + if (!p_icon.empty()) { + + Image img=p_icon; + img.convert(Image::FORMAT_RGBA); + + + int w = img.get_width(); + int h = img.get_height(); + + Vector pd; + + pd.resize((2+w*h)*sizeof(long)); + + print_line("***** SET ICON ***** "+itos(w)+" "+itos(h)); + + pd[0]=w; + pd[1]=h; + + DVector::Read r = img.get_data().read(); + + uint32_t *wr=(uint32_t*)&pd[2]; + + for(int i=0;iinit(); + +// uint64_t last_ticks=get_ticks_usec(); + +// int frames=0; +// uint64_t frame=0; + + while (!force_quit) { + + process_xevents(); // get rid of pending events + process_joysticks(); + if (Main::iteration()==true) + break; + }; + + main_loop->finish(); +} + +OS_X11::OS_X11() { + +#ifdef RTAUDIO_ENABLED + AudioDriverManagerSW::add_driver(&driver_rtaudio); +#endif + +#ifdef ALSA_ENABLED + AudioDriverManagerSW::add_driver(&driver_alsa); +#endif + + minimized = false; + xim_style=NULL; + mouse_mode=MOUSE_MODE_VISIBLE; + + +}; diff --git a/platform/x11/os_x11.h b/platform/x11/os_x11.h new file mode 100644 index 00000000000..ee50bdea4a8 --- /dev/null +++ b/platform/x11/os_x11.h @@ -0,0 +1,203 @@ +/*************************************************************************/ +/* os_x11.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 OS_X11_H +#define OS_X11_H + + +#include "os/input.h" +#include "drivers/unix/os_unix.h" +#include "context_gl_x11.h" +#include "servers/visual_server.h" +#include "servers/visual/visual_server_wrap_mt.h" +#include "servers/visual/rasterizer.h" +#include "servers/physics_server.h" +#include "servers/audio/audio_server_sw.h" +#include "servers/audio/sample_manager_sw.h" +#include "servers/spatial_sound/spatial_sound_server_sw.h" +#include "servers/spatial_sound_2d/spatial_sound_2d_server_sw.h" +#include "drivers/rtaudio/audio_driver_rtaudio.h" +#include "drivers/alsa/audio_driver_alsa.h" +#include "servers/physics_2d/physics_2d_server_sw.h" + +#include +#include +#include + +//bitch +#undef CursorShape +/** + @author Juan Linietsky +*/ + +class OS_X11 : public OS_Unix { + + Atom wm_delete; +#if defined(OPENGL_ENABLED) || defined(LEGACYGL_ENABLED) + ContextGL_X11 *context_gl; +#endif + Rasterizer *rasterizer; + VisualServer *visual_server; + VideoMode current_videomode; + List args; + Window x11_window; + MainLoop *main_loop; + ::Display* x11_display; + char *xmbstring; + int xmblen; + unsigned long last_timestamp; + ::Time last_keyrelease_time; + ::XIC xic; + ::XIM xim; + ::XIMStyle xim_style; + Point2i last_mouse_pos; + bool last_mouse_pos_valid; + Point2i last_click_pos; + uint64_t last_click_ms; + unsigned int event_id; + uint32_t last_button_state; + + PhysicsServer *physics_server; + unsigned int get_mouse_button_state(unsigned int p_x11_state); + InputModifierState get_key_modifier_state(unsigned int p_x11_state); + Physics2DServer *physics_2d_server; + + MouseMode mouse_mode; + Point2i center; + + void handle_key_event(XKeyEvent *p_event); + void process_xevents(); + virtual void delete_main_loop(); + IP_Unix *ip_unix; + + AudioServerSW *audio_server; + SampleManagerMallocSW *sample_manager; + SpatialSoundServerSW *spatial_sound_server; + SpatialSound2DServerSW *spatial_sound_2d_server; + + bool force_quit; + bool minimized; + int dpad_last[2]; + + + const char *cursor_theme; + int cursor_size; + Cursor cursors[CURSOR_MAX]; + Cursor null_cursor; + CursorShape current_cursor; + + InputDefault *input; + +#ifdef RTAUDIO_ENABLED + AudioDriverRtAudio driver_rtaudio; +#endif + +#ifdef ALSA_ENABLED + AudioDriverALSA driver_alsa; +#endif + + enum { + JOYSTICKS_MAX = 8, + MAX_JOY_AXIS = 32768, // I've no idea + }; + + struct Joystick { + + int fd; + int last_axis[JOY_AXIS_MAX]; + + Joystick() { + fd = -1; + for (int i=0; i *p_list,int p_screen=0) const; + + virtual void move_window_to_foreground(); + + void run(); + + OS_X11(); +}; + +#endif diff --git a/platform/x11/platform_config.h b/platform/x11/platform_config.h new file mode 100644 index 00000000000..d14f3e3f9ab --- /dev/null +++ b/platform/x11/platform_config.h @@ -0,0 +1,32 @@ +/*************************************************************************/ +/* platform_config.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. */ +/*************************************************************************/ +#include +#define GLES2_INCLUDE_H "gl_context/glew.h" +#define GLES1_INCLUDE_H "gl_context/glew.h" + diff --git a/plugins/terrain/plugin.cfg b/plugins/terrain/plugin.cfg new file mode 100644 index 00000000000..d2f29174203 --- /dev/null +++ b/plugins/terrain/plugin.cfg @@ -0,0 +1,16 @@ +[plugin] + +name="Terrain" +description="Simple plugin for generating and editing grid-based terrains. This type of terrains were all the rage in the early 2000's, but lost popularity to hand crafted geometry towards the end of the decade." +author="Juan Linietsky" +version="1.0" +installs=true +script="terrain.gd" +install_files=["terrain.gd","terrain_node.gd","icon_terrain.png"] + + + + + + + diff --git a/plugins/terrain/terrain.gd b/plugins/terrain/terrain.gd new file mode 100644 index 00000000000..b3e3121e7a4 --- /dev/null +++ b/plugins/terrain/terrain.gd @@ -0,0 +1,17 @@ +tool # Always declare as Tool, if it's meant to run in the editor. +extends EditorPlugin + + +func get_name(): + return "Terrain" + + +func _init(): + print("PLUGIN INIT") + + +func _enter_scene(): + add_custom_type("Terrain","Spatial",preload("terrain_node.gd"),preload("terrain.png")) + +func _exit_scene(): + remove_custom_type("Terrain") diff --git a/plugins/terrain/terrain.png b/plugins/terrain/terrain.png new file mode 100644 index 00000000000..7c1c3d70d6c Binary files /dev/null and b/plugins/terrain/terrain.png differ diff --git a/plugins/terrain/terrain_node.gd b/plugins/terrain/terrain_node.gd new file mode 100644 index 00000000000..91cf3fcb2bf --- /dev/null +++ b/plugins/terrain/terrain_node.gd @@ -0,0 +1,3 @@ +extends Spatial + + diff --git a/plugins/time/plugin.cfg b/plugins/time/plugin.cfg new file mode 100644 index 00000000000..5430306a799 --- /dev/null +++ b/plugins/time/plugin.cfg @@ -0,0 +1,14 @@ +[plugin] + +name="The Time" +description="This plugin displays the current local time, with great accuracy, by harvesting the power of quartz crystals inside your computer.\nIt may also serve as simple example on how to write a non-installable editor plugin, or just remind you that it's time to go back home." +author="Juan Linietsky" +version="1.0" +installs=false +script="time.gd" + + + + + + diff --git a/plugins/time/time.gd b/plugins/time/time.gd new file mode 100644 index 00000000000..66b3e9ed043 --- /dev/null +++ b/plugins/time/time.gd @@ -0,0 +1,32 @@ +tool # Always declare as Tool, if it's meant to run in the editor. +extends EditorPlugin + +var timer = null +var label = null + +func _timeout(): + if (label): + var time = OS.get_time() + label.set_text(str(time.hour).pad_zeros(2)+":"+str(time.minute).pad_zeros(2)+":"+str(time.second).pad_zeros(2)) + +func get_name(): + return "The Time" + + +func _init(): + print("PLUGIN INIT") + timer = Timer.new() + add_child(timer) + timer.set_wait_time(0.5) + timer.set_one_shot(false) + timer.connect("timeout",self,"_timeout") + +func _enter_scene(): + label = Label.new() + add_custom_control(CONTAINER_TOOLBAR,label) + timer.start() + +func _exit_scene(): + timer.stop() + label.free() + label=null diff --git a/scene/2d/SCsub b/scene/2d/SCsub new file mode 100644 index 00000000000..055d2f24741 --- /dev/null +++ b/scene/2d/SCsub @@ -0,0 +1,7 @@ +Import('env') + +env.add_source_files(env.scene_sources,"*.cpp") + +Export('env') + + diff --git a/scene/2d/animated_sprite.cpp b/scene/2d/animated_sprite.cpp new file mode 100644 index 00000000000..316dffb3f93 --- /dev/null +++ b/scene/2d/animated_sprite.cpp @@ -0,0 +1,352 @@ +/*************************************************************************/ +/* animated_sprite.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 "animated_sprite.h" + +void AnimatedSprite::edit_set_pivot(const Point2& p_pivot) { + + set_offset(p_pivot); +} + +Point2 AnimatedSprite::edit_get_pivot() const { + + return get_offset(); +} +bool AnimatedSprite::edit_has_pivot() const { + + return true; +} + + +void SpriteFrames::add_frame(const Ref& p_frame,int p_at_pos) { + + if (p_at_pos>=0 && p_at_pos=frames->get_frame_count()) + return; + + Ref texture = frames->get_frame(frame); + if (texture.is_null()) + return; + + //print_line("DECIDED TO DRAW"); + + RID ci = get_canvas_item(); + + /* + texture->draw(ci,Point2()); + break; + */ + + Size2i s; + s = texture->get_size(); + Point2i ofs=offset; + if (centered) + ofs-=s/2; + + Rect2i dst_rect(ofs,s); + + if (hflip) + dst_rect.size.x=-dst_rect.size.x; + if (vflip) + dst_rect.size.y=-dst_rect.size.y; + + texture->draw_rect(ci,dst_rect,false,modulate); +// VisualServer::get_singleton()->canvas_item_add_texture_rect_region(ci,dst_rect,texture->get_rid(),src_rect,modulate); + + } break; + } + +} + +void AnimatedSprite::set_sprite_frames(const Ref &p_frames) { + + if (frames.is_valid()) + frames->disconnect("changed",this,"_res_changed"); + frames=p_frames; + if (frames.is_valid()) + frames->connect("changed",this,"_res_changed"); + + if (!frames.is_valid()) { + frame=0; + + } else { + set_frame(frame); + } + update(); + +} + +Ref AnimatedSprite::get_sprite_frames() const { + + return frames; +} + +void AnimatedSprite::set_frame(int p_frame) { + + if (!frames.is_valid()) { + return; + } + if (p_frame>=frames->get_frame_count()) + p_frame=frames->get_frame_count()-1; + if (p_frame<0) + p_frame=0; + + if (frame==p_frame) + return; + + frame=p_frame; + update(); + _change_notify("frame"); + + +} +int AnimatedSprite::get_frame() const { + + return frame; +} + + +void AnimatedSprite::set_centered(bool p_center) { + + centered=p_center; + update(); + item_rect_changed(); +} + +bool AnimatedSprite::is_centered() const { + + return centered; +} + +void AnimatedSprite::set_offset(const Point2& p_offset) { + + offset=p_offset; + update(); + item_rect_changed(); +} +Point2 AnimatedSprite::get_offset() const { + + return offset; +} + +void AnimatedSprite::set_flip_h(bool p_flip) { + + hflip=p_flip; + update(); +} +bool AnimatedSprite::is_flipped_h() const { + + return hflip; +} + +void AnimatedSprite::set_flip_v(bool p_flip) { + + vflip=p_flip; + update(); +} +bool AnimatedSprite::is_flipped_v() const { + + return vflip; +} + + +void AnimatedSprite::set_modulate(const Color& p_color) { + + modulate=p_color; + update(); +} + +Color AnimatedSprite::get_modulate() const{ + + return modulate; +} + + +Rect2 AnimatedSprite::get_item_rect() const { + + if (!frames.is_valid() || !frames->get_frame_count() || frame<0 || frame>=frames->get_frame_count()) { + return Node2D::get_item_rect(); + } + + Ref t = frames->get_frame(frame); + if (t.is_null()) + return Node2D::get_item_rect(); + Size2i s = t->get_size(); + + Point2i ofs=offset; + if (centered) + ofs-=s/2; + + if (s==Size2(0,0)) + s=Size2(1,1); + + return Rect2(ofs,s); +} + +void AnimatedSprite::_res_changed() { + + set_frame(frame); + update(); +} + +void AnimatedSprite::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("set_sprite_frames","sprite_frames:SpriteFrames"),&AnimatedSprite::set_sprite_frames); + ObjectTypeDB::bind_method(_MD("get_sprite_frames:SpriteFrames"),&AnimatedSprite::get_sprite_frames); + + ObjectTypeDB::bind_method(_MD("set_centered","centered"),&AnimatedSprite::set_centered); + ObjectTypeDB::bind_method(_MD("is_centered"),&AnimatedSprite::is_centered); + + ObjectTypeDB::bind_method(_MD("set_offset","offset"),&AnimatedSprite::set_offset); + ObjectTypeDB::bind_method(_MD("get_offset"),&AnimatedSprite::get_offset); + + ObjectTypeDB::bind_method(_MD("set_flip_h","flip_h"),&AnimatedSprite::set_flip_h); + ObjectTypeDB::bind_method(_MD("is_flipped_h"),&AnimatedSprite::is_flipped_h); + + ObjectTypeDB::bind_method(_MD("set_flip_v","flip_v"),&AnimatedSprite::set_flip_v); + ObjectTypeDB::bind_method(_MD("is_flipped_v"),&AnimatedSprite::is_flipped_v); + + ObjectTypeDB::bind_method(_MD("set_frame","frame"),&AnimatedSprite::set_frame); + ObjectTypeDB::bind_method(_MD("get_frame"),&AnimatedSprite::get_frame); + + ObjectTypeDB::bind_method(_MD("set_modulate","modulate"),&AnimatedSprite::set_modulate); + ObjectTypeDB::bind_method(_MD("get_modulate"),&AnimatedSprite::get_modulate); + + + ObjectTypeDB::bind_method(_MD("_res_changed"),&AnimatedSprite::_res_changed); + + ADD_PROPERTYNZ( PropertyInfo( Variant::OBJECT, "frames",PROPERTY_HINT_RESOURCE_TYPE,"SpriteFrames"), _SCS("set_sprite_frames"),_SCS("get_sprite_frames")); + ADD_PROPERTYNZ( PropertyInfo( Variant::INT, "frame"), _SCS("set_frame"),_SCS("get_frame")); + ADD_PROPERTY( PropertyInfo( Variant::BOOL, "centered"), _SCS("set_centered"),_SCS("is_centered")); + ADD_PROPERTYNZ( PropertyInfo( Variant::VECTOR2, "offset"), _SCS("set_offset"),_SCS("get_offset")); + ADD_PROPERTY( PropertyInfo( Variant::BOOL, "flip_h"), _SCS("set_flip_h"),_SCS("is_flipped_h")); + ADD_PROPERTY( PropertyInfo( Variant::BOOL, "flip_v"), _SCS("set_flip_v"),_SCS("is_flipped_v")); + ADD_PROPERTY( PropertyInfo( Variant::COLOR, "modulate"), _SCS("set_modulate"),_SCS("get_modulate")); + +} + +AnimatedSprite::AnimatedSprite() { + + centered=true; + hflip=false; + vflip=false; + + frame=0; + + + modulate=Color(1,1,1,1); + + +} + + diff --git a/scene/2d/animated_sprite.h b/scene/2d/animated_sprite.h new file mode 100644 index 00000000000..78d738240fd --- /dev/null +++ b/scene/2d/animated_sprite.h @@ -0,0 +1,119 @@ +/*************************************************************************/ +/* animated_sprite.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 ANIMATED_SPRITE_H +#define ANIMATED_SPRITE_H + +#include "scene/2d/node_2d.h" +#include "scene/resources/texture.h" + + +class SpriteFrames : public Resource { + + OBJ_TYPE(SpriteFrames,Resource); + + Vector< Ref > frames; + + Array _get_frames() const; + void _set_frames(const Array& p_frames); +protected: + + static void _bind_methods(); + +public: + + + void add_frame(const Ref& p_frame,int p_at_pos=-1); + int get_frame_count() const; + _FORCE_INLINE_ Ref get_frame(int p_idx) const { ERR_FAIL_INDEX_V(p_idx,frames.size(),Ref()); return frames[p_idx]; } + void set_frame(int p_idx,const Ref& p_frame){ ERR_FAIL_INDEX(p_idx,frames.size()); frames[p_idx]=p_frame; } + void remove_frame(int p_idx); + void clear(); + + SpriteFrames(); + +}; + + + +class AnimatedSprite : public Node2D { + + OBJ_TYPE(AnimatedSprite,Node2D); + + Ref frames; + int frame; + + bool centered; + Point2 offset; + + bool hflip; + bool vflip; + + Color modulate; + + void _res_changed(); +protected: + + static void _bind_methods(); + void _notification(int p_what); + +public: + + + virtual void edit_set_pivot(const Point2& p_pivot); + virtual Point2 edit_get_pivot() const; + virtual bool edit_has_pivot() const; + + void set_sprite_frames(const Ref &p_frames); + Ref get_sprite_frames() const; + + void set_frame(int p_frame); + int get_frame() const; + + void set_centered(bool p_center); + bool is_centered() const; + + void set_offset(const Point2& p_offset); + Point2 get_offset() const; + + void set_flip_h(bool p_flip); + bool is_flipped_h() const; + + void set_flip_v(bool p_flip); + bool is_flipped_v() const; + + void set_modulate(const Color& p_color); + Color get_modulate() const; + + virtual Rect2 get_item_rect() const; + + + AnimatedSprite(); +}; + +#endif // ANIMATED_SPRITE_H diff --git a/scene/2d/area_2d.cpp b/scene/2d/area_2d.cpp new file mode 100644 index 00000000000..70b5a652d5c --- /dev/null +++ b/scene/2d/area_2d.cpp @@ -0,0 +1,326 @@ +/*************************************************************************/ +/* area_2d.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 "area_2d.h" +#include "scene/scene_string_names.h" +#include "servers/physics_2d_server.h" +void Area2D::set_space_override_mode(SpaceOverride p_mode) { + + space_override=p_mode; + Physics2DServer::get_singleton()->area_set_space_override_mode(get_rid(),Physics2DServer::AreaSpaceOverrideMode(p_mode)); + + +} +Area2D::SpaceOverride Area2D::get_space_override_mode() const{ + + return space_override; +} + +void Area2D::set_gravity_is_point(bool p_enabled){ + + gravity_is_point=p_enabled; + Physics2DServer::get_singleton()->area_set_param(get_rid(),Physics2DServer::AREA_PARAM_GRAVITY_IS_POINT,p_enabled); + +} +bool Area2D::is_gravity_a_point() const{ + + return gravity_is_point; +} + +void Area2D::set_gravity_vector(const Vector2& p_vec){ + + gravity_vec=p_vec; + Physics2DServer::get_singleton()->area_set_param(get_rid(),Physics2DServer::AREA_PARAM_GRAVITY_VECTOR,p_vec); + +} +Vector2 Area2D::get_gravity_vector() const{ + + return gravity_vec; +} + +void Area2D::set_gravity(real_t p_gravity){ + + gravity=p_gravity; + Physics2DServer::get_singleton()->area_set_param(get_rid(),Physics2DServer::AREA_PARAM_GRAVITY,p_gravity); +} +real_t Area2D::get_gravity() const{ + + return gravity; +} + +void Area2D::set_density(real_t p_density){ + + density=p_density; + Physics2DServer::get_singleton()->area_set_param(get_rid(),Physics2DServer::AREA_PARAM_DENSITY,p_density); +} +real_t Area2D::get_density() const{ + + return density; +} + +void Area2D::set_priority(real_t p_priority){ + + priority=p_priority; + Physics2DServer::get_singleton()->area_set_param(get_rid(),Physics2DServer::AREA_PARAM_PRIORITY,p_priority); +} +real_t Area2D::get_priority() const{ + + return priority; +} + + +void Area2D::_body_enter_scene(ObjectID p_id) { + + Object *obj = ObjectDB::get_instance(p_id); + Node *node = obj ? obj->cast_to() : NULL; + ERR_FAIL_COND(!node); + + Map::Element *E=body_map.find(p_id); + ERR_FAIL_COND(!E); + ERR_FAIL_COND(E->get().in_scene); + + E->get().in_scene=true; + emit_signal(SceneStringNames::get_singleton()->body_enter,node); + for(int i=0;iget().shapes.size();i++) { + + emit_signal(SceneStringNames::get_singleton()->body_enter_shape,p_id,node,E->get().shapes[i].body_shape,E->get().shapes[i].area_shape); + } + +} + +void Area2D::_body_exit_scene(ObjectID p_id) { + + Object *obj = ObjectDB::get_instance(p_id); + Node *node = obj ? obj->cast_to() : NULL; + ERR_FAIL_COND(!node); + Map::Element *E=body_map.find(p_id); + ERR_FAIL_COND(!E); + ERR_FAIL_COND(!E->get().in_scene); + E->get().in_scene=false; + emit_signal(SceneStringNames::get_singleton()->body_exit,node); + for(int i=0;iget().shapes.size();i++) { + + emit_signal(SceneStringNames::get_singleton()->body_exit_shape,p_id,node,E->get().shapes[i].body_shape,E->get().shapes[i].area_shape); + } + +} + +void Area2D::_body_inout(int p_status,const RID& p_body, int p_instance, int p_body_shape,int p_area_shape) { + + bool body_in = p_status==Physics2DServer::AREA_BODY_ADDED; + ObjectID objid=p_instance; + + Object *obj = ObjectDB::get_instance(objid); + Node *node = obj ? obj->cast_to() : NULL; + + Map::Element *E=body_map.find(objid); + + ERR_FAIL_COND(!body_in && !E); + + if (body_in) { + if (!E) { + + E = body_map.insert(objid,BodyState()); + E->get().rc=0; + E->get().in_scene=node && node->is_inside_scene(); + if (node) { + node->connect(SceneStringNames::get_singleton()->enter_scene,this,SceneStringNames::get_singleton()->_body_enter_scene,make_binds(objid)); + node->connect(SceneStringNames::get_singleton()->exit_scene,this,SceneStringNames::get_singleton()->_body_exit_scene,make_binds(objid)); + if (E->get().in_scene) { + emit_signal(SceneStringNames::get_singleton()->body_enter,node); + } + } + + } + E->get().rc++; + if (node) + E->get().shapes.insert(ShapePair(p_body_shape,p_area_shape)); + + + if (E->get().in_scene) { + emit_signal(SceneStringNames::get_singleton()->body_enter_shape,objid,node,p_body_shape,p_area_shape); + } + + } else { + + E->get().rc--; + + if (node) + E->get().shapes.erase(ShapePair(p_body_shape,p_area_shape)); + + bool eraseit=false; + + if (E->get().rc==0) { + + if (node) { + node->disconnect(SceneStringNames::get_singleton()->enter_scene,this,SceneStringNames::get_singleton()->_body_enter_scene); + node->disconnect(SceneStringNames::get_singleton()->exit_scene,this,SceneStringNames::get_singleton()->_body_exit_scene); + if (E->get().in_scene) + emit_signal(SceneStringNames::get_singleton()->body_exit,obj); + + } + + eraseit=true; + + } + if (node && E->get().in_scene) { + emit_signal(SceneStringNames::get_singleton()->body_exit_shape,objid,obj,p_body_shape,p_area_shape); + } + + if (eraseit) + body_map.erase(E); + + } + +} + + +void Area2D::_clear_monitoring() { + + + Map bmcopy = body_map; + body_map.clear(); + //disconnect all monitored stuff + + for (Map::Element *E=bmcopy.front();E;E=E->next()) { + + Object *obj = ObjectDB::get_instance(E->key()); + Node *node = obj ? obj->cast_to() : NULL; + ERR_CONTINUE(!node); + if (!E->get().in_scene) + continue; + + for(int i=0;iget().shapes.size();i++) { + + emit_signal(SceneStringNames::get_singleton()->body_exit_shape,E->key(),node,E->get().shapes[i].body_shape,E->get().shapes[i].area_shape); + } + + emit_signal(SceneStringNames::get_singleton()->body_exit,obj); + + node->disconnect(SceneStringNames::get_singleton()->enter_scene,this,SceneStringNames::get_singleton()->_body_enter_scene); + node->disconnect(SceneStringNames::get_singleton()->exit_scene,this,SceneStringNames::get_singleton()->_body_exit_scene); + } + +} + +void Area2D::_notification(int p_what) { + + switch(p_what) { + + case NOTIFICATION_EXIT_SCENE: { + + _clear_monitoring(); + } break; + } + +} + + +void Area2D::set_enable_monitoring(bool p_enable) { + + if (p_enable==monitoring) + return; + + monitoring=p_enable; + + if (monitoring) { + + Physics2DServer::get_singleton()->area_set_monitor_callback(get_rid(),this,"_body_inout"); + } else { + Physics2DServer::get_singleton()->area_set_monitor_callback(get_rid(),NULL,StringName()); + _clear_monitoring(); + + } +} + +bool Area2D::is_monitoring_enabled() const { + + return monitoring; +} + + +void Area2D::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("_body_enter_scene","id"),&Area2D::_body_enter_scene); + ObjectTypeDB::bind_method(_MD("_body_exit_scene","id"),&Area2D::_body_exit_scene); + + ObjectTypeDB::bind_method(_MD("set_space_override_mode","enable"),&Area2D::set_space_override_mode); + ObjectTypeDB::bind_method(_MD("get_space_override_mode"),&Area2D::get_space_override_mode); + + ObjectTypeDB::bind_method(_MD("set_gravity_is_point","enable"),&Area2D::set_gravity_is_point); + ObjectTypeDB::bind_method(_MD("is_gravity_a_point"),&Area2D::is_gravity_a_point); + + ObjectTypeDB::bind_method(_MD("set_gravity_vector","vector"),&Area2D::set_gravity_vector); + ObjectTypeDB::bind_method(_MD("get_gravity_vector"),&Area2D::get_gravity_vector); + + ObjectTypeDB::bind_method(_MD("set_gravity","gravity"),&Area2D::set_gravity); + ObjectTypeDB::bind_method(_MD("get_gravity"),&Area2D::get_gravity); + + ObjectTypeDB::bind_method(_MD("set_density","density"),&Area2D::set_density); + ObjectTypeDB::bind_method(_MD("get_density"),&Area2D::get_density); + + ObjectTypeDB::bind_method(_MD("set_priority","priority"),&Area2D::set_priority); + ObjectTypeDB::bind_method(_MD("get_priority"),&Area2D::get_priority); + + ObjectTypeDB::bind_method(_MD("set_enable_monitoring","enable"),&Area2D::set_enable_monitoring); + ObjectTypeDB::bind_method(_MD("is_monitoring_enabled"),&Area2D::is_monitoring_enabled); + + ObjectTypeDB::bind_method(_MD("_body_inout"),&Area2D::_body_inout); + + + ADD_SIGNAL( MethodInfo("body_enter_shape",PropertyInfo(Variant::INT,"body_id"),PropertyInfo(Variant::OBJECT,"body"),PropertyInfo(Variant::INT,"body_shape"),PropertyInfo(Variant::INT,"area_shape"))); + ADD_SIGNAL( MethodInfo("body_exit_shape",PropertyInfo(Variant::INT,"body_id"),PropertyInfo(Variant::OBJECT,"body"),PropertyInfo(Variant::INT,"body_shape"),PropertyInfo(Variant::INT,"area_shape"))); + ADD_SIGNAL( MethodInfo("body_enter",PropertyInfo(Variant::OBJECT,"body"))); + ADD_SIGNAL( MethodInfo("body_exit",PropertyInfo(Variant::OBJECT,"body"))); + + ADD_PROPERTYNZ( PropertyInfo(Variant::INT,"space_override",PROPERTY_HINT_ENUM,"Disabled,Combine,Replace"),_SCS("set_space_override_mode"),_SCS("get_space_override_mode")); + ADD_PROPERTY( PropertyInfo(Variant::BOOL,"gravity_point"),_SCS("set_gravity_is_point"),_SCS("is_gravity_a_point")); + ADD_PROPERTY( PropertyInfo(Variant::VECTOR2,"gravity_vec"),_SCS("set_gravity_vector"),_SCS("get_gravity_vector")); + ADD_PROPERTY( PropertyInfo(Variant::REAL,"gravity",PROPERTY_HINT_RANGE,"-1024,1024,0.01"),_SCS("set_gravity"),_SCS("get_gravity")); + ADD_PROPERTY( PropertyInfo(Variant::REAL,"density",PROPERTY_HINT_RANGE,"0,1024,0.001"),_SCS("set_density"),_SCS("get_density")); + ADD_PROPERTYNZ( PropertyInfo(Variant::INT,"priority",PROPERTY_HINT_RANGE,"0,128,1"),_SCS("set_priority"),_SCS("get_priority")); + ADD_PROPERTY( PropertyInfo(Variant::BOOL,"monitoring"),_SCS("set_enable_monitoring"),_SCS("is_monitoring_enabled")); + +} + +Area2D::Area2D() : CollisionObject2D(Physics2DServer::get_singleton()->area_create(),true) { + + space_override=SPACE_OVERRIDE_DISABLED; + set_gravity(98);; + set_gravity_vector(Vector2(0,1)); + gravity_is_point=false; + density=0.1; + priority=0; + monitoring=false; + +} + +Area2D::~Area2D() { + + +} diff --git a/scene/2d/area_2d.h b/scene/2d/area_2d.h new file mode 100644 index 00000000000..b4c8edf1382 --- /dev/null +++ b/scene/2d/area_2d.h @@ -0,0 +1,122 @@ +/*************************************************************************/ +/* area_2d.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 AREA_2D_H +#define AREA_2D_H + +#include "scene/2d/collision_object_2d.h" +#include "vset.h" + +class Area2D : public CollisionObject2D { + + OBJ_TYPE( Area2D, CollisionObject2D ); +public: + + enum SpaceOverride { + SPACE_OVERRIDE_DISABLED, + SPACE_OVERRIDE_COMBINE, + SPACE_OVERRIDE_REPLACE + }; +private: + + + SpaceOverride space_override; + Vector2 gravity_vec; + real_t gravity; + bool gravity_is_point; + real_t density; + int priority; + bool monitoring; + + void _body_inout(int p_status,const RID& p_body, int p_instance, int p_body_shape,int p_area_shape); + + void _body_enter_scene(ObjectID p_id); + void _body_exit_scene(ObjectID p_id); + + struct ShapePair { + + int body_shape; + int area_shape; + bool operator<(const ShapePair& p_sp) const { + if (body_shape==p_sp.body_shape) + return area_shape < p_sp.area_shape; + else + return body_shape < p_sp.body_shape; + } + + ShapePair() {} + ShapePair(int p_bs, int p_as) { body_shape=p_bs; area_shape=p_as; } + }; + + struct BodyState { + + int rc; + bool in_scene; + VSet shapes; + }; + + Map body_map; + + void _clear_monitoring(); + + +protected: + + void _notification(int p_what); + static void _bind_methods(); +public: + + void set_space_override_mode(SpaceOverride p_mode); + SpaceOverride get_space_override_mode() const; + + void set_gravity_is_point(bool p_enabled); + bool is_gravity_a_point() const; + + void set_gravity_vector(const Vector2& p_vec); + Vector2 get_gravity_vector() const; + + void set_gravity(real_t p_gravity); + real_t get_gravity() const; + + void set_density(real_t p_density); + real_t get_density() const; + + void set_priority(real_t p_priority); + real_t get_priority() const; + + void set_enable_monitoring(bool p_enable); + bool is_monitoring_enabled() const; + + + Area2D(); + ~Area2D(); +}; + +VARIANT_ENUM_CAST(Area2D::SpaceOverride); + +#endif // AREA_2D_H diff --git a/scene/2d/camera_2d.cpp b/scene/2d/camera_2d.cpp new file mode 100644 index 00000000000..1920ce00816 --- /dev/null +++ b/scene/2d/camera_2d.cpp @@ -0,0 +1,484 @@ +/*************************************************************************/ +/* camera_2d.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 "camera_2d.h" +#include "scene/scene_string_names.h" +#include "servers/visual_server.h" + +void Camera2D::_update_scroll() { + + + if (!is_inside_scene()) + return; + + if (get_scene()->is_editor_hint()) { + update(); //will just be drawn + return; + } + + if (current) { + Matrix32 xform = get_camera_transform(); + + RID vp = viewport->get_viewport(); + if (viewport) { + viewport->set_canvas_transform( xform ); + } + get_scene()->call_group(SceneMainLoop::GROUP_CALL_REALTIME,group_name,"_camera_moved",xform); + }; + +} + +void Camera2D::set_zoom(const Vector2 &p_zoom) { + + zoom = p_zoom; + _update_scroll(); +}; + +Vector2 Camera2D::get_zoom() const { + + return zoom; +}; + + +Matrix32 Camera2D::get_camera_transform() { + + if (!get_scene()) + return Matrix32(); + + Size2 screen_size = get_viewport_rect().size; + screen_size=get_viewport_rect().size; + + + Point2 new_camera_pos = get_global_transform().get_origin(); + Point2 ret_camera_pos; + + if (!first) { + + + if (centered) { + + if (h_drag_enabled) { + camera_pos.x = MIN( camera_pos.x, (new_camera_pos.x + screen_size.x * 0.5 * drag_margin[MARGIN_RIGHT])); + camera_pos.x = MAX( camera_pos.x, (new_camera_pos.x - screen_size.x * 0.5 * drag_margin[MARGIN_LEFT])); + } else { + + if (h_ofs<0) { + camera_pos.x = new_camera_pos.x + screen_size.x * 0.5 * drag_margin[MARGIN_RIGHT] * h_ofs; + } else { + camera_pos.x = new_camera_pos.x + screen_size.x * 0.5 * drag_margin[MARGIN_LEFT] * h_ofs; + } + } + + if (v_drag_enabled) { + + camera_pos.y = MIN( camera_pos.y, (new_camera_pos.y + screen_size.y * 0.5 * drag_margin[MARGIN_BOTTOM])); + camera_pos.y = MAX( camera_pos.y, (new_camera_pos.y - screen_size.y * 0.5 * drag_margin[MARGIN_TOP])); + + } else { + + if (v_ofs<0) { + camera_pos.y = new_camera_pos.y + screen_size.y * 0.5 * drag_margin[MARGIN_TOP] * v_ofs; + } else { + camera_pos.y = new_camera_pos.y + screen_size.y * 0.5 * drag_margin[MARGIN_BOTTOM] * v_ofs; + } + } + + } + + + if (smoothing>0.0) { + + float c = smoothing*get_fixed_process_delta_time(); + smoothed_camera_pos = ((new_camera_pos-smoothed_camera_pos)*c)+smoothed_camera_pos; + ret_camera_pos=smoothed_camera_pos; +// camera_pos=camera_pos*(1.0-smoothing)+new_camera_pos*smoothing; + } else { + + ret_camera_pos=smoothed_camera_pos=camera_pos; + + } + + + + } else { + ret_camera_pos=smoothed_camera_pos=camera_pos=new_camera_pos; + first=false; + } + + + Point2 screen_offset = (centered ? (screen_size * 0.5 * zoom) : Point2());; + screen_offset+=offset; + + Rect2 screen_rect(-screen_offset+ret_camera_pos,screen_size); + if (screen_rect.pos.x + screen_rect.size.x > limit[MARGIN_RIGHT]) + screen_rect.pos.x = limit[MARGIN_RIGHT] - screen_rect.size.x; + + if (screen_rect.pos.y + screen_rect.size.y > limit[MARGIN_BOTTOM]) + screen_rect.pos.y = limit[MARGIN_BOTTOM] - screen_rect.size.y; + + + if (screen_rect.pos.x < limit[MARGIN_LEFT]) + screen_rect.pos.x=limit[MARGIN_LEFT]; + + if (screen_rect.pos.y < limit[MARGIN_TOP]) + screen_rect.pos.y =limit[MARGIN_TOP]; + + camera_screen_center=screen_rect.pos+screen_rect.size*0.5; + + Matrix32 xform; + xform.scale_basis(zoom); + xform.set_origin(screen_rect.pos/*.floor()*/); + + +/* + if (0) { + + xform = get_global_transform() * xform; + } else { + + xform.elements[2]+=get_global_transform().get_origin(); + } +*/ + + + return (xform).affine_inverse(); +} + + + +void Camera2D::_notification(int p_what) { + + switch(p_what) { + + case NOTIFICATION_FIXED_PROCESS: { + + _update_scroll(); + + } break; + case NOTIFICATION_TRANSFORM_CHANGED: { + + + if (!is_fixed_processing()) + _update_scroll(); + + } break; + case NOTIFICATION_ENTER_SCENE: { + + viewport = NULL; + Node *n=this; + while(n){ + + viewport = n->cast_to(); + if (viewport) + break; + n=n->get_parent(); + } + + canvas = get_canvas(); + + RID vp = viewport->get_viewport(); + + group_name = "__cameras_"+itos(vp.get_id()); + canvas_group_name ="__cameras_c"+itos(canvas.get_id()); + add_to_group(group_name); + add_to_group(canvas_group_name); + + _update_scroll(); + first=true; + + + } break; + case NOTIFICATION_EXIT_SCENE: { + + if (is_current()) { + if (viewport) { + viewport->set_canvas_transform( Matrix32() ); + } + } + remove_from_group(group_name); + remove_from_group(canvas_group_name); + viewport=NULL; + + } break; + } +} + +void Camera2D::set_offset(const Vector2& p_offset) { + + offset=p_offset; + _update_scroll(); + +} + +Vector2 Camera2D::get_offset() const{ + + return offset; +} + +void Camera2D::set_centered(bool p_centered){ + + centered=p_centered; + _update_scroll(); +} + +bool Camera2D::is_centered() const { + + return centered; +} + + +void Camera2D::_make_current(Object *p_which) { + + if (p_which==this) { + + current=true; + _update_scroll(); + } else { + current=false; + } +} + + +void Camera2D::_set_current(bool p_current) { + + if (p_current) + make_current(); + + current=p_current; +} + +bool Camera2D::is_current() const { + + return current; +} + +void Camera2D::make_current() { + + if (!is_inside_scene()) { + current=true; + } else { + get_scene()->call_group(SceneMainLoop::GROUP_CALL_REALTIME,group_name,"_make_current",this); + } +} + +void Camera2D::set_limit(Margin p_margin,int p_limit) { + + ERR_FAIL_INDEX(p_margin,4); + limit[p_margin]=p_limit; +} + +int Camera2D::get_limit(Margin p_margin) const{ + + ERR_FAIL_INDEX_V(p_margin,4,0); + return limit[p_margin]; + +} + +void Camera2D::set_drag_margin(Margin p_margin,float p_drag_margin) { + + ERR_FAIL_INDEX(p_margin,4); + drag_margin[p_margin]=p_drag_margin; +} + +float Camera2D::get_drag_margin(Margin p_margin) const{ + + ERR_FAIL_INDEX_V(p_margin,4,0); + return drag_margin[p_margin]; + +} + + +Vector2 Camera2D::get_camera_pos() const { + + + return camera_pos; +} + +void Camera2D::force_update_scroll() { + + + _update_scroll(); +} + + +void Camera2D::set_follow_smoothing(float p_speed) { + + smoothing=p_speed; + if (smoothing>0) + set_fixed_process(true); + else + set_fixed_process(false); +} + +float Camera2D::get_follow_smoothing() const{ + + return smoothing; +} + +Point2 Camera2D::get_camera_screen_center() const { + + return camera_screen_center; +} + + +void Camera2D::set_h_drag_enabled(bool p_enabled) { + + h_drag_enabled=p_enabled; +} + +bool Camera2D::is_h_drag_enabled() const{ + + return h_drag_enabled; +} + +void Camera2D::set_v_drag_enabled(bool p_enabled){ + + v_drag_enabled=p_enabled; +} + +bool Camera2D::is_v_drag_enabled() const{ + + return v_drag_enabled; +} + +void Camera2D::set_v_offset(float p_offset) { + + v_ofs=p_offset; +} + +float Camera2D::get_v_offset() const{ + + return v_ofs; +} + +void Camera2D::set_h_offset(float p_offset){ + + h_ofs=p_offset; +} +float Camera2D::get_h_offset() const{ + + return h_ofs; +} + + +void Camera2D::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("set_offset","offset"),&Camera2D::set_offset); + ObjectTypeDB::bind_method(_MD("get_offset"),&Camera2D::get_offset); + + ObjectTypeDB::bind_method(_MD("set_centered","centered"),&Camera2D::set_centered); + ObjectTypeDB::bind_method(_MD("is_centered"),&Camera2D::is_centered); + + ObjectTypeDB::bind_method(_MD("make_current"),&Camera2D::make_current); + ObjectTypeDB::bind_method(_MD("_make_current"),&Camera2D::_make_current); + + ObjectTypeDB::bind_method(_MD("_update_scroll"),&Camera2D::_update_scroll); + + + ObjectTypeDB::bind_method(_MD("_set_current","current"),&Camera2D::_set_current); + ObjectTypeDB::bind_method(_MD("is_current"),&Camera2D::is_current); + + ObjectTypeDB::bind_method(_MD("set_limit","margin","limit"),&Camera2D::set_limit); + ObjectTypeDB::bind_method(_MD("get_limit","margin"),&Camera2D::get_limit); + + ObjectTypeDB::bind_method(_MD("set_v_drag_enabled","enabled"),&Camera2D::set_v_drag_enabled); + ObjectTypeDB::bind_method(_MD("is_v_drag_enabled"),&Camera2D::is_v_drag_enabled); + + ObjectTypeDB::bind_method(_MD("set_h_drag_enabled","enabled"),&Camera2D::set_h_drag_enabled); + ObjectTypeDB::bind_method(_MD("is_h_drag_enabled"),&Camera2D::is_h_drag_enabled); + + ObjectTypeDB::bind_method(_MD("set_v_offset","ofs"),&Camera2D::set_v_offset); + ObjectTypeDB::bind_method(_MD("get_v_offset"),&Camera2D::get_v_offset); + + ObjectTypeDB::bind_method(_MD("set_h_offset","ofs"),&Camera2D::set_h_offset); + ObjectTypeDB::bind_method(_MD("get_h_offset"),&Camera2D::get_h_offset); + + ObjectTypeDB::bind_method(_MD("set_drag_margin","margin","drag_margin"),&Camera2D::set_drag_margin); + ObjectTypeDB::bind_method(_MD("get_drag_margin","margin"),&Camera2D::get_drag_margin); + + ObjectTypeDB::bind_method(_MD("get_camera_pos"),&Camera2D::get_camera_pos); + ObjectTypeDB::bind_method(_MD("get_camera_screen_center"),&Camera2D::get_camera_screen_center); + + ObjectTypeDB::bind_method(_MD("set_zoom"),&Camera2D::set_zoom); + ObjectTypeDB::bind_method(_MD("get_zoom"),&Camera2D::get_zoom); + + + ObjectTypeDB::bind_method(_MD("set_follow_smoothing","follow_smoothing"),&Camera2D::set_follow_smoothing); + ObjectTypeDB::bind_method(_MD("get_follow_smoothing"),&Camera2D::get_follow_smoothing); + + ObjectTypeDB::bind_method(_MD("force_update_scroll"),&Camera2D::force_update_scroll); + + + ADD_PROPERTYNZ( PropertyInfo(Variant::VECTOR2,"offset"),_SCS("set_offset"),_SCS("get_offset")); + ADD_PROPERTY( PropertyInfo(Variant::BOOL,"centered"),_SCS("set_centered"),_SCS("is_centered")); + ADD_PROPERTY( PropertyInfo(Variant::BOOL,"current"),_SCS("_set_current"),_SCS("is_current")); + ADD_PROPERTY( PropertyInfo(Variant::REAL,"smoothing"),_SCS("set_follow_smoothing"),_SCS("get_follow_smoothing") ); + ADD_PROPERTY( PropertyInfo(Variant::VECTOR2,"zoom"),_SCS("set_zoom"),_SCS("get_zoom") ); + + ADD_PROPERTYI( PropertyInfo(Variant::INT,"limit/left"),_SCS("set_limit"),_SCS("get_limit"),MARGIN_LEFT); + ADD_PROPERTYI( PropertyInfo(Variant::INT,"limit/top"),_SCS("set_limit"),_SCS("get_limit"),MARGIN_TOP); + ADD_PROPERTYI( PropertyInfo(Variant::INT,"limit/right"),_SCS("set_limit"),_SCS("get_limit"),MARGIN_RIGHT); + ADD_PROPERTYI( PropertyInfo(Variant::INT,"limit/bottom"),_SCS("set_limit"),_SCS("get_limit"),MARGIN_BOTTOM); + + ADD_PROPERTY( PropertyInfo(Variant::BOOL,"drag_margin/h_enabled"),_SCS("set_h_drag_enabled"),_SCS("is_h_drag_enabled") ); + ADD_PROPERTY( PropertyInfo(Variant::BOOL,"drag_margin/v_enabled"),_SCS("set_v_drag_enabled"),_SCS("is_v_drag_enabled") ); + + ADD_PROPERTYI( PropertyInfo(Variant::REAL,"drag_margin/left",PROPERTY_HINT_RANGE,"0,1,0.01"),_SCS("set_drag_margin"),_SCS("get_drag_margin"),MARGIN_LEFT); + ADD_PROPERTYI( PropertyInfo(Variant::REAL,"drag_margin/top",PROPERTY_HINT_RANGE,"0,1,0.01"),_SCS("set_drag_margin"),_SCS("get_drag_margin"),MARGIN_TOP); + ADD_PROPERTYI( PropertyInfo(Variant::REAL,"drag_margin/right",PROPERTY_HINT_RANGE,"0,1,0.01"),_SCS("set_drag_margin"),_SCS("get_drag_margin"),MARGIN_RIGHT); + ADD_PROPERTYI( PropertyInfo(Variant::REAL,"drag_margin/bottom",PROPERTY_HINT_RANGE,"0,1,0.01"),_SCS("set_drag_margin"),_SCS("get_drag_margin"),MARGIN_BOTTOM); + + + +} + +Camera2D::Camera2D() { + + + + centered=true; + current=false; + limit[MARGIN_LEFT]=-10000000; + limit[MARGIN_TOP]=-10000000; + limit[MARGIN_RIGHT]=10000000; + limit[MARGIN_BOTTOM]=10000000; + drag_margin[MARGIN_LEFT]=0.2; + drag_margin[MARGIN_TOP]=0.2; + drag_margin[MARGIN_RIGHT]=0.2; + drag_margin[MARGIN_BOTTOM]=0.2; + camera_pos=Vector2(); + + smoothing=0.0; + zoom = Vector2(1, 1); + + h_drag_enabled=true; + v_drag_enabled=true; + h_ofs=0; + v_ofs=0; + +} diff --git a/scene/2d/camera_2d.h b/scene/2d/camera_2d.h new file mode 100644 index 00000000000..9d06df2d1b8 --- /dev/null +++ b/scene/2d/camera_2d.h @@ -0,0 +1,118 @@ +/*************************************************************************/ +/* camera_2d.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 CAMERA_2D_H +#define CAMERA_2D_H + +#include "scene/2d/node_2d.h" +#include "scene/main/viewport.h" + + +class Camera2D : public Node2D { + + OBJ_TYPE( Camera2D, Node2D ); + +protected: + Point2 camera_pos; + Point2 smoothed_camera_pos; + bool first; + + Viewport *viewport; + + StringName group_name; + StringName canvas_group_name; + RID canvas; + Vector2 offset; + Vector2 zoom; + bool centered; + bool current; + float smoothing; + int limit[4]; + float drag_margin[4]; + + bool h_drag_enabled; + bool v_drag_enabled; + float h_ofs; + float v_ofs; + + + Point2 camera_screen_center; + void _update_scroll(); + + void _make_current(Object *p_which); + void _set_current(bool p_current); +protected: + + virtual Matrix32 get_camera_transform(); + void _notification(int p_what); + static void _bind_methods(); +public: + + void set_offset(const Vector2& p_offset); + Vector2 get_offset() const; + + void set_centered(bool p_centered); + bool is_centered() const; + + void set_limit(Margin p_margin,int p_limit); + int get_limit(Margin p_margin) const; + + + void set_h_drag_enabled(bool p_enabled); + bool is_h_drag_enabled() const; + + void set_v_drag_enabled(bool p_enabled); + bool is_v_drag_enabled() const; + + void set_drag_margin(Margin p_margin,float p_drag_margin); + float get_drag_margin(Margin p_margin) const; + + void set_v_offset(float p_offset); + float get_v_offset() const; + + void set_h_offset(float p_offset); + float get_h_offset() const; + + void set_follow_smoothing(float p_speed); + float get_follow_smoothing() const; + + void make_current(); + bool is_current() const; + + void set_zoom(const Vector2& p_zoom); + Vector2 get_zoom() const; + + Point2 get_camera_screen_center() const; + + Vector2 get_camera_pos() const; + void force_update_scroll(); + + Camera2D(); +}; + +#endif // CAMERA_2D_H diff --git a/scene/2d/canvas_item.cpp b/scene/2d/canvas_item.cpp new file mode 100644 index 00000000000..8a461c76fcc --- /dev/null +++ b/scene/2d/canvas_item.cpp @@ -0,0 +1,875 @@ +/*************************************************************************/ +/* canvas_item.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 "canvas_item.h" +#include "servers/visual_server.h" +#include "scene/main/viewport.h" +#include "scene/main/canvas_layer.h" +#include "message_queue.h" +#include "scene/scene_string_names.h" +#include "scene/resources/font.h" +#include "scene/resources/texture.h" +#include "scene/resources/style_box.h" + +bool CanvasItem::is_visible() const { + + if (!is_inside_scene()) + return false; + + const CanvasItem *p=this; + + while(p) { + if (p->hidden) + return false; + p=p->get_parent_item(); + } + + + return true; +} + +bool CanvasItem::is_hidden() const { + + /*if (!is_inside_scene()) + return false;*/ + + return hidden; +} + +void CanvasItem::_propagate_visibility_changed(bool p_visible) { + + notification(NOTIFICATION_VISIBILITY_CHANGED); + + if (p_visible) + update(); //todo optimize + else + emit_signal(SceneStringNames::get_singleton()->hide); + _block(); + + for(int i=0;icast_to(); + + if (c && c->hidden!=p_visible) //should the toplevels stop propagation? i think so but.. + c->_propagate_visibility_changed(p_visible); + } + + _unblock(); + +} + +void CanvasItem::show() { + + if (!hidden) + return; + + + hidden=false; + VisualServer::get_singleton()->canvas_item_set_visible(canvas_item,true); + + if (!is_inside_scene()) + return; + + if (is_visible()) { + _propagate_visibility_changed(true); + } +} + + +void CanvasItem::hide() { + + if (hidden) + return; + + bool propagate=is_inside_scene() && is_visible(); + hidden=true; + VisualServer::get_singleton()->canvas_item_set_visible(canvas_item,false); + + if (!is_inside_scene()) + return; + if (propagate) + _propagate_visibility_changed(false); + +} + + +Variant CanvasItem::edit_get_state() const { + + + return Variant(); +} +void CanvasItem::edit_set_state(const Variant& p_state) { + + +} + +void CanvasItem::edit_set_rect(const Rect2& p_edit_rect) { + + //used by editors, implement at will +} + +void CanvasItem::edit_rotate(float p_rot) { + + +} + +Size2 CanvasItem::edit_get_minimum_size() const { + + return Size2(-1,-1); //no limit +} + +void CanvasItem::_update_callback() { + + + + if (!is_inside_scene()) { + pending_update=false; + return; + } + + + VisualServer::get_singleton()->canvas_item_clear(get_canvas_item()); + //todo updating = true - only allow drawing here + if (is_visible()) { //todo optimize this!! + if (first_draw) { + notification(NOTIFICATION_VISIBILITY_CHANGED); + first_draw=false; + } + drawing=true; + notification(NOTIFICATION_DRAW); + emit_signal(SceneStringNames::get_singleton()->draw); + if (get_script_instance()) { + Variant::CallError err; + get_script_instance()->call_multilevel_reversed(SceneStringNames::get_singleton()->_draw,NULL,0); + } + drawing=false; + + } + //todo updating = false + pending_update=false; // don't change to false until finished drawing (avoid recursive update) +} + +Matrix32 CanvasItem::get_global_transform_with_canvas() const { + + const CanvasItem *ci = this; + Matrix32 xform; + const CanvasItem *last_valid=NULL; + + while(ci) { + + last_valid=ci; + xform = ci->get_transform() * xform; + ci=ci->get_parent_item(); + } + + if (last_valid->canvas_layer) + return last_valid->canvas_layer->get_transform() * xform; + else + return xform; +} + +Matrix32 CanvasItem::get_global_transform() const { + + + if (global_invalid) { + + const CanvasItem *pi = get_parent_item(); + if (pi) + global_transform = pi->get_global_transform() * get_transform(); + else + global_transform = get_transform(); + + global_invalid=false; + } + + return global_transform; + +} + + +void CanvasItem::_queue_sort_children() { + + if (pending_children_sort) + return; + + pending_children_sort=true; + MessageQueue::get_singleton()->push_call(this,"_sort_children"); +} + +void CanvasItem::_sort_children() { + + pending_children_sort=false; + + if (!is_inside_scene()) + return; + + for(int i=0;icast_to(); + + if (ci) { + if (ci->toplevel || ci->group!="") + continue; + VisualServer::get_singleton()->canvas_item_raise(n->cast_to()->canvas_item); + } + } +} + +void CanvasItem::_raise_self() { + + if (!is_inside_scene()) + return; + + VisualServer::get_singleton()->canvas_item_raise(canvas_item); +} + + +void CanvasItem::_enter_canvas() { + + if ((!get_parent() || !get_parent()->cast_to()) || toplevel) { + + Node *n = this; + viewport=NULL; + canvas_layer=NULL; + + while(n) { + + if (n->cast_to()) { + + viewport = n->cast_to(); + break; + } + if (!canvas_layer && n->cast_to()) { + + canvas_layer = n->cast_to(); + } + n=n->get_parent(); + } + + RID canvas; + if (canvas_layer) + canvas=canvas_layer->get_world_2d()->get_canvas(); + else + canvas=viewport->find_world_2d()->get_canvas(); + + VisualServer::get_singleton()->canvas_item_set_parent(canvas_item,canvas); + + group = "root_canvas"+itos(canvas.get_id()); + + add_to_group(group); + get_scene()->call_group(SceneMainLoop::GROUP_CALL_UNIQUE,group,"_raise_self"); + + } else { + + CanvasItem *parent = get_parent_item(); + viewport=parent->viewport; + VisualServer::get_singleton()->canvas_item_set_parent(canvas_item,parent->get_canvas_item()); + parent->_queue_sort_children(); + } + + if (!viewport) { + + print_line("no viewport wtf!"); + } + pending_update=false; + update(); + + notification(NOTIFICATION_ENTER_CANVAS); + +} + +void CanvasItem::_exit_canvas() { + + notification(NOTIFICATION_EXIT_CANVAS,true); //reverse the notification + VisualServer::get_singleton()->canvas_item_set_parent(canvas_item,RID()); + viewport=NULL; + canvas_layer=NULL; + group=""; + +} + + +void CanvasItem::_notification(int p_what) { + + + switch(p_what) { + case NOTIFICATION_ENTER_SCENE: { + + first_draw=true; + pending_children_sort=false; + if (get_parent()) { + CanvasItem *ci = get_parent()->cast_to(); + if (ci) + C=ci->children_items.push_back(this); + } + _enter_canvas(); + if (!block_transform_notify && !xform_change.in_list()) { + get_scene()->xform_change_list.add(&xform_change); + } + } break; + case NOTIFICATION_MOVED_IN_PARENT: { + + + if (group!="") { + get_scene()->call_group(SceneMainLoop::GROUP_CALL_UNIQUE,group,"_raise_self"); + } else { + CanvasItem *p = get_parent_item(); + ERR_FAIL_COND(!p); + p->_queue_sort_children(); + } + + + } break; + case NOTIFICATION_EXIT_SCENE: { + if (xform_change.in_list()) + get_scene()->xform_change_list.remove(&xform_change); + _exit_canvas(); + if (C) + get_parent()->cast_to()->children_items.erase(C); + } break; + case NOTIFICATION_DRAW: { + + } break; + case NOTIFICATION_TRANSFORM_CHANGED: { + + + } break; + case NOTIFICATION_VISIBILITY_CHANGED: { + + emit_signal(SceneStringNames::get_singleton()->visibility_changed); + } break; + + } +} + +void CanvasItem::_set_visible_(bool p_visible) { + + if (p_visible) + show(); + else + hide(); +} +bool CanvasItem::_is_visible_() const { + + return !is_hidden(); +} + + +void CanvasItem::update() { + + if (!is_inside_scene()) + return; + if (pending_update) + return; + + pending_update=true; + + MessageQueue::get_singleton()->push_call(this,"_update_callback"); +} + +void CanvasItem::set_opacity(float p_opacity) { + + opacity=p_opacity; + VisualServer::get_singleton()->canvas_item_set_opacity(canvas_item,opacity); + +} +float CanvasItem::get_opacity() const { + + return opacity; +} + + +void CanvasItem::set_as_toplevel(bool p_toplevel) { + + if (toplevel==p_toplevel) + return; + + if (!is_inside_scene()) { + toplevel=p_toplevel; + return; + } + + _exit_canvas(); + toplevel=p_toplevel; + _enter_canvas(); +} + +bool CanvasItem::is_set_as_toplevel() const { + + return toplevel; +} + +CanvasItem *CanvasItem::get_parent_item() const { + + if (toplevel) + return NULL; + + Node *parent = get_parent(); + if (!parent) + return NULL; + + return parent->cast_to(); +} + + +void CanvasItem::set_self_opacity(float p_self_opacity) { + + self_opacity=p_self_opacity; + VisualServer::get_singleton()->canvas_item_set_self_opacity(canvas_item,self_opacity); + +} +float CanvasItem::get_self_opacity() const { + + return self_opacity; +} + +void CanvasItem::set_blend_mode(BlendMode p_blend_mode) { + + ERR_FAIL_INDEX(p_blend_mode,4); + blend_mode=p_blend_mode; + VisualServer::get_singleton()->canvas_item_set_blend_mode(canvas_item,VS::MaterialBlendMode(blend_mode)); + +} + +CanvasItem::BlendMode CanvasItem::get_blend_mode() const { + + return blend_mode; +} + + + +void CanvasItem::item_rect_changed() { + + update(); + emit_signal(SceneStringNames::get_singleton()->item_rect_changed); +} + + +void CanvasItem::draw_line(const Point2& p_from, const Point2& p_to,const Color& p_color,float p_width) { + + if (!drawing) { + ERR_EXPLAIN("Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); + ERR_FAIL(); + } + + VisualServer::get_singleton()->canvas_item_add_line(canvas_item,p_from,p_to,p_color,p_width); +} + +void CanvasItem::draw_rect(const Rect2& p_rect, const Color& p_color) { + + if (!drawing) { + ERR_EXPLAIN("Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); + ERR_FAIL(); + } + + VisualServer::get_singleton()->canvas_item_add_rect(canvas_item,p_rect,p_color); + +} + +void CanvasItem::draw_circle(const Point2& p_pos, float p_radius, const Color& p_color) { + + if (!drawing) { + ERR_EXPLAIN("Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); + ERR_FAIL(); + } + + VisualServer::get_singleton()->canvas_item_add_circle(canvas_item,p_pos,p_radius,p_color); + +} + +void CanvasItem::draw_texture(const Ref& p_texture,const Point2& p_pos) { + + if (!drawing) { + ERR_EXPLAIN("Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); + ERR_FAIL(); + } + + ERR_FAIL_COND(p_texture.is_null()); + + p_texture->draw(canvas_item,p_pos); +} + +void CanvasItem::draw_texture_rect(const Ref& p_texture,const Rect2& p_rect, bool p_tile,const Color& p_modulate) { + + if (!drawing) { + ERR_EXPLAIN("Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); + ERR_FAIL(); + } + + ERR_FAIL_COND(p_texture.is_null()); + p_texture->draw_rect(canvas_item,p_rect,p_tile,p_modulate); + +} +void CanvasItem::draw_texture_rect_region(const Ref& p_texture,const Rect2& p_rect, const Rect2& p_src_rect,const Color& p_modulate) { + + if (!drawing) { + ERR_EXPLAIN("Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); + ERR_FAIL(); + } + ERR_FAIL_COND(p_texture.is_null()); + p_texture->draw_rect_region(canvas_item,p_rect,p_src_rect,p_modulate); +} + +void CanvasItem::draw_style_box(const Ref& p_style_box,const Rect2& p_rect) { + if (!drawing) { + ERR_EXPLAIN("Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); + ERR_FAIL(); + } + + ERR_FAIL_COND(p_style_box.is_null()); + + p_style_box->draw(canvas_item,p_rect); + +} +void CanvasItem::draw_primitive(const Vector& p_points, const Vector& p_colors,const Vector& p_uvs, Ref p_texture,float p_width) { + + if (!drawing) { + ERR_EXPLAIN("Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); + ERR_FAIL(); + } + + RID rid = p_texture.is_valid() ? p_texture->get_rid() : RID(); + + VisualServer::get_singleton()->canvas_item_add_primitive(canvas_item,p_points,p_colors,p_uvs,rid,p_width); +} +void CanvasItem::draw_set_transform(const Point2& p_offset, float p_rot, const Size2& p_scale) { + + if (!drawing) { + ERR_EXPLAIN("Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); + ERR_FAIL(); + } + + Matrix32 xform(p_rot,p_offset); + xform.scale_basis(p_scale); + VisualServer::get_singleton()->canvas_item_set_transform(canvas_item,xform); +} + +void CanvasItem::draw_polygon(const Vector& p_points, const Vector& p_colors,const Vector& p_uvs, Ref p_texture) { + + if (!drawing) { + ERR_EXPLAIN("Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); + ERR_FAIL(); + } + + RID rid = p_texture.is_valid() ? p_texture->get_rid() : RID(); + + VisualServer::get_singleton()->canvas_item_add_polygon(canvas_item,p_points,p_colors,p_uvs,rid); + + +} + +void CanvasItem::draw_colored_polygon(const Vector& p_points, const Color& p_color,const Vector& p_uvs, Ref p_texture) { + + if (!drawing) { + ERR_EXPLAIN("Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); + ERR_FAIL(); + } + + Vector colors; + colors.push_back(p_color); + RID rid = p_texture.is_valid() ? p_texture->get_rid() : RID(); + + VisualServer::get_singleton()->canvas_item_add_polygon(canvas_item,p_points,colors,p_uvs,rid); +} + +void CanvasItem::draw_string(const Ref& p_font,const Point2& p_pos, const String& p_text,const Color& p_modulate,int p_clip_w) { + + if (!drawing) { + ERR_EXPLAIN("Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); + ERR_FAIL(); + } + + ERR_FAIL_COND(p_font.is_null()); + p_font->draw(canvas_item,p_pos,p_text,p_modulate,p_clip_w); + +} + +float CanvasItem::draw_char(const Ref& p_font,const Point2& p_pos, const String& p_char,const String& p_next,const Color& p_modulate) { + + if (!drawing) { + ERR_EXPLAIN("Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); + ERR_FAIL_V(0); + } + + ERR_FAIL_COND_V(p_char.length()!=1,0); + ERR_FAIL_COND_V(p_font.is_null(),0); + + return p_font->draw_char(canvas_item,p_pos,p_char[0],p_next.c_str()[0],p_modulate); + +} + + +void CanvasItem::_notify_transform(CanvasItem *p_node) { + + if (p_node->xform_change.in_list() && p_node->global_invalid) + return; //nothing to do + + p_node->global_invalid=true; + + if (!p_node->xform_change.in_list()) { + if (!p_node->block_transform_notify) { + if (p_node->is_inside_scene()) + get_scene()->xform_change_list.add(&p_node->xform_change); + } + } + + + for(List::Element *E=p_node->children_items.front();E;E=E->next()) { + + CanvasItem* ci=E->get(); + if (ci->toplevel) + continue; + _notify_transform(ci); + } +} + + +Rect2 CanvasItem::get_viewport_rect() const { + + ERR_FAIL_COND_V(!is_inside_scene(),Rect2()); + return viewport->get_visible_rect(); +} + +RID CanvasItem::get_canvas() const { + + ERR_FAIL_COND_V(!is_inside_scene(),RID()); + + if (canvas_layer) + return canvas_layer->get_world_2d()->get_canvas(); + else + return viewport->find_world_2d()->get_canvas(); + + +} + +CanvasItem *CanvasItem::get_toplevel() const { + + CanvasItem *ci=const_cast(this); + while(!ci->toplevel && ci->get_parent() && ci->get_parent()->cast_to()) { + ci=ci->get_parent()->cast_to(); + } + + return ci; +} + +Viewport *CanvasItem::get_viewport() const { + + return viewport; +} + + +Ref CanvasItem::get_world_2d() const { + + ERR_FAIL_COND_V(!is_inside_scene(),Ref()); + + CanvasItem *tl=get_toplevel(); + + if (tl->canvas_layer) { + return tl->canvas_layer->get_world_2d(); + } else if (tl->viewport) { + return tl->viewport->find_world_2d(); + } else { + return Ref(); + } + +} + +RID CanvasItem::get_viewport_rid() const { + + ERR_FAIL_COND_V(!is_inside_scene(),RID()); + return viewport->get_viewport(); +} + +void CanvasItem::set_block_transform_notify(bool p_enable) { + block_transform_notify=p_enable; +} + +bool CanvasItem::is_block_transform_notify_enabled() const { + + return block_transform_notify; +} + +void CanvasItem::set_on_top(bool p_on_top) { + + if (on_top==p_on_top) + return; + on_top=p_on_top; + VisualServer::get_singleton()->canvas_item_set_on_top(canvas_item,on_top); +} + +bool CanvasItem::is_on_top() const { + + return on_top; +} + + +void CanvasItem::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("_sort_children"),&CanvasItem::_sort_children); + ObjectTypeDB::bind_method(_MD("_raise_self"),&CanvasItem::_raise_self); + ObjectTypeDB::bind_method(_MD("_update_callback"),&CanvasItem::_update_callback); + ObjectTypeDB::bind_method(_MD("_set_visible_"),&CanvasItem::_set_visible_); + ObjectTypeDB::bind_method(_MD("_is_visible_"),&CanvasItem::_is_visible_); + + ObjectTypeDB::bind_method(_MD("edit_set_state","state"),&CanvasItem::edit_set_state); + ObjectTypeDB::bind_method(_MD("edit_get"),&CanvasItem::edit_get_state); + ObjectTypeDB::bind_method(_MD("edit_set_rect","rect"),&CanvasItem::edit_set_rect); + ObjectTypeDB::bind_method(_MD("edit_rotate","degrees"),&CanvasItem::edit_rotate); + + ObjectTypeDB::bind_method(_MD("get_item_rect"),&CanvasItem::get_item_rect); + //ObjectTypeDB::bind_method(_MD("get_transform"),&CanvasItem::get_transform); + ObjectTypeDB::bind_method(_MD("get_canvas_item"),&CanvasItem::get_canvas_item); + + ObjectTypeDB::bind_method(_MD("is_visible"),&CanvasItem::is_visible); + ObjectTypeDB::bind_method(_MD("is_hidden"),&CanvasItem::is_hidden); + ObjectTypeDB::bind_method(_MD("show"),&CanvasItem::show); + ObjectTypeDB::bind_method(_MD("hide"),&CanvasItem::hide); + + ObjectTypeDB::bind_method(_MD("update"),&CanvasItem::update); + + ObjectTypeDB::bind_method(_MD("set_as_toplevel","enable"),&CanvasItem::set_as_toplevel); + ObjectTypeDB::bind_method(_MD("is_set_as_toplevel"),&CanvasItem::is_set_as_toplevel); + + ObjectTypeDB::bind_method(_MD("set_blend_mode","blend_mode"),&CanvasItem::set_blend_mode); + ObjectTypeDB::bind_method(_MD("get_blend_mode"),&CanvasItem::get_blend_mode); + + ObjectTypeDB::bind_method(_MD("set_opacity","opacity"),&CanvasItem::set_opacity); + ObjectTypeDB::bind_method(_MD("get_opacity"),&CanvasItem::get_opacity); + ObjectTypeDB::bind_method(_MD("set_self_opacity","self_opacity"),&CanvasItem::set_self_opacity); + ObjectTypeDB::bind_method(_MD("get_self_opacity"),&CanvasItem::get_self_opacity); + + ObjectTypeDB::bind_method(_MD("set_on_top","on_top"),&CanvasItem::set_on_top); + ObjectTypeDB::bind_method(_MD("is_on_top"),&CanvasItem::is_on_top); + + //ObjectTypeDB::bind_method(_MD("get_transform"),&CanvasItem::get_transform); + + ObjectTypeDB::bind_method(_MD("draw_line","from","to","color","width"),&CanvasItem::draw_line,DEFVAL(1.0)); + ObjectTypeDB::bind_method(_MD("draw_rect","rect","color"),&CanvasItem::draw_rect); + ObjectTypeDB::bind_method(_MD("draw_circle","pos","radius","color"),&CanvasItem::draw_circle); + ObjectTypeDB::bind_method(_MD("draw_texture","texture:Texture","pos"),&CanvasItem::draw_texture); + ObjectTypeDB::bind_method(_MD("draw_texture_rect","texture:Texture","rect","tile","modulate"),&CanvasItem::draw_texture_rect,DEFVAL(false),DEFVAL(Color(1,1,1))); + ObjectTypeDB::bind_method(_MD("draw_texture_rect_region","texture:Texture","rect","src_rect","modulate"),&CanvasItem::draw_texture_rect_region,DEFVAL(Color(1,1,1))); + ObjectTypeDB::bind_method(_MD("draw_style_box","style_box:StyleBox","rect"),&CanvasItem::draw_style_box); + ObjectTypeDB::bind_method(_MD("draw_primitive","points","colors","uvs","texture:Texture","width"),&CanvasItem::draw_primitive,DEFVAL(Array()),DEFVAL(Ref()),DEFVAL(1.0)); + ObjectTypeDB::bind_method(_MD("draw_polygon","points","colors","uvs","texture:Texture"),&CanvasItem::draw_primitive,DEFVAL(Array()),DEFVAL(Ref())); + ObjectTypeDB::bind_method(_MD("draw_colored_polygon","points","color","uvs","texture:Texture"),&CanvasItem::draw_primitive,DEFVAL(Array()),DEFVAL(Ref())); + ObjectTypeDB::bind_method(_MD("draw_string","font:Font","pos","text","modulate","clip_w"),&CanvasItem::draw_string,DEFVAL(Color(1,1,1)),DEFVAL(-1)); + ObjectTypeDB::bind_method(_MD("draw_char","font:Font","pos","char","next","modulate"),&CanvasItem::draw_char,DEFVAL(Color(1,1,1))); + + ObjectTypeDB::bind_method(_MD("draw_set_transform","pos","rot","scale"),&CanvasItem::draw_set_transform); + ObjectTypeDB::bind_method(_MD("get_transform"),&CanvasItem::get_transform); + ObjectTypeDB::bind_method(_MD("get_global_transform"),&CanvasItem::get_global_transform); + ObjectTypeDB::bind_method(_MD("get_viewport_transform"),&CanvasItem::get_viewport_transform); + ObjectTypeDB::bind_method(_MD("get_viewport_rect"),&CanvasItem::get_viewport_rect); + ObjectTypeDB::bind_method(_MD("get_canvas"),&CanvasItem::get_canvas); + ObjectTypeDB::bind_method(_MD("get_world_2d"),&CanvasItem::get_world_2d); + ObjectTypeDB::bind_method(_MD("get_viewport"),&CanvasItem::get_viewport); + + BIND_VMETHOD(MethodInfo("_draw")); + + ADD_PROPERTY( PropertyInfo(Variant::BOOL,"visibility/visible"), _SCS("_set_visible_"),_SCS("_is_visible_") ); + ADD_PROPERTY( PropertyInfo(Variant::REAL,"visibility/opacity",PROPERTY_HINT_RANGE, "0,1,0.01"), _SCS("set_opacity"),_SCS("get_opacity") ); + ADD_PROPERTY( PropertyInfo(Variant::REAL,"visibility/self_opacity",PROPERTY_HINT_RANGE, "0,1,0.01"), _SCS("set_self_opacity"),_SCS("get_self_opacity") ); + ADD_PROPERTY( PropertyInfo(Variant::BOOL,"visibility/on_top"), _SCS("set_on_top"),_SCS("is_on_top") ); + + ADD_PROPERTYNZ( PropertyInfo(Variant::INT,"visibility/blend_mode",PROPERTY_HINT_ENUM, "Mix,Add,Sub,Mul"), _SCS("set_blend_mode"),_SCS("get_blend_mode") ); + //exporting these two things doesn't really make much sense i think + //ADD_PROPERTY( PropertyInfo(Variant::BOOL,"transform/toplevel"), _SCS("set_as_toplevel"),_SCS("is_set_as_toplevel") ); + //ADD_PROPERTY(PropertyInfo(Variant::BOOL,"transform/notify"),_SCS("set_transform_notify"),_SCS("is_transform_notify_enabled")); + + ADD_SIGNAL( MethodInfo("draw") ); + ADD_SIGNAL( MethodInfo("visibility_changed") ); + ADD_SIGNAL( MethodInfo("hide") ); + ADD_SIGNAL( MethodInfo("item_rect_changed") ); + + + + BIND_CONSTANT( BLEND_MODE_MIX ); + BIND_CONSTANT( BLEND_MODE_ADD ); + BIND_CONSTANT( BLEND_MODE_SUB ); + BIND_CONSTANT( BLEND_MODE_MUL ); + + + BIND_CONSTANT( NOTIFICATION_DRAW); + BIND_CONSTANT( NOTIFICATION_VISIBILITY_CHANGED ); + BIND_CONSTANT( NOTIFICATION_ENTER_CANVAS ); + BIND_CONSTANT( NOTIFICATION_EXIT_CANVAS ); + BIND_CONSTANT( NOTIFICATION_TRANSFORM_CHANGED ); + + +} + +Matrix32 CanvasItem::get_viewport_transform() const { + + ERR_FAIL_COND_V(!is_inside_scene(),Matrix32()); + + if (canvas_layer) { + + if (viewport) { + return viewport->get_final_transform() * canvas_layer->get_transform(); + } else { + return canvas_layer->get_transform(); + } + + } else if (viewport) { + return viewport->get_final_transform() * viewport->get_canvas_transform(); + } + + return Matrix32(); + +} + + +CanvasItem::CanvasItem() : xform_change(this) { + + + canvas_item=VisualServer::get_singleton()->canvas_item_create(); + hidden=false; + pending_update=false; + opacity=1; + self_opacity=1; + toplevel=false; + pending_children_sort=false; + first_draw=false; + blend_mode=BLEND_MODE_MIX; + drawing=false; + on_top=true; + block_transform_notify=false; + viewport=NULL; + canvas_layer=NULL; + global_invalid=true; + + C=NULL; + +} + +CanvasItem::~CanvasItem() { + + VisualServer::get_singleton()->free(canvas_item); +} diff --git a/scene/2d/canvas_item.h b/scene/2d/canvas_item.h new file mode 100644 index 00000000000..0da4aa30864 --- /dev/null +++ b/scene/2d/canvas_item.h @@ -0,0 +1,209 @@ +/*************************************************************************/ +/* canvas_item.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 CANVAS_ITEM_H +#define CANVAS_ITEM_H + +#include "scene/main/node.h" +#include "scene/resources/texture.h" +#include "scene/main/scene_main_loop.h" + +class CanvasLayer; +class Viewport; +class Font; + +class StyleBox; + +class CanvasItem : public Node { + + OBJ_TYPE( CanvasItem, Node ); +public: + + enum BlendMode { + + BLEND_MODE_MIX, //default + BLEND_MODE_ADD, + BLEND_MODE_SUB, + BLEND_MODE_MUL + }; + +private: + + mutable SelfList xform_change; + + RID canvas_item; + String group; + + Viewport *viewport; + CanvasLayer *canvas_layer; + + float opacity; + float self_opacity; + + List children_items; + List::Element *C; + + BlendMode blend_mode; + + bool first_draw; + bool hidden; + bool pending_update; + bool toplevel; + bool pending_children_sort; + bool drawing; + bool block_transform_notify; + bool on_top; + + mutable Matrix32 global_transform; + mutable bool global_invalid; + + + void _raise_self(); + + void _propagate_visibility_changed(bool p_visible); + + void _set_visible_(bool p_visible); + bool _is_visible_() const; + + void _update_callback(); + + void _enter_canvas(); + void _exit_canvas(); + + void _queue_sort_children(); + void _sort_children(); + + + + void _notify_transform(CanvasItem *p_node); + +protected: + + + + _FORCE_INLINE_ void _notify_transform() { if (!is_inside_scene()) return; _notify_transform(this); if (!block_transform_notify) notification(NOTIFICATION_LOCAL_TRANSFORM_CHANGED); } + + void item_rect_changed(); + + void _notification(int p_what); + static void _bind_methods(); +public: + + + enum { + NOTIFICATION_TRANSFORM_CHANGED=SceneMainLoop::NOTIFICATION_TRANSFORM_CHANGED, //unique + NOTIFICATION_DRAW=30, + NOTIFICATION_VISIBILITY_CHANGED=31, + NOTIFICATION_ENTER_CANVAS=32, + NOTIFICATION_EXIT_CANVAS=33, + NOTIFICATION_LOCAL_TRANSFORM_CHANGED=35, + NOTIFICATION_WORLD_2D_CHANGED=36, + + }; + + /* EDITOR */ + + virtual Variant edit_get_state() const; + virtual void edit_set_state(const Variant& p_state); + virtual void edit_set_rect(const Rect2& p_edit_rect); + virtual void edit_rotate(float p_rot); + virtual Size2 edit_get_minimum_size() const; + + /* VISIBILITY */ + + bool is_visible() const; + bool is_hidden() const; + void show(); + void hide(); + + void update(); + + void set_blend_mode(BlendMode p_blend_mode); + BlendMode get_blend_mode() const; + + void set_opacity(float p_opacity); + float get_opacity() const; + + void set_self_opacity(float p_self_opacity); + float get_self_opacity() const; + + /* DRAWING API */ + + void draw_line(const Point2& p_from, const Point2& p_to,const Color& p_color,float p_width=1.0); + void draw_rect(const Rect2& p_rect, const Color& p_color); + void draw_circle(const Point2& p_pos, float p_radius, const Color& p_color); + void draw_texture(const Ref& p_texture,const Point2& p_pos); + void draw_texture_rect(const Ref& p_texture, const Rect2& p_rect, bool p_tile=false,const Color& p_modulate=Color(1,1,1)); + void draw_texture_rect_region(const Ref& p_texture,const Rect2& p_rect, const Rect2& p_src_rect,const Color& p_modulate=Color(1,1,1)); + void draw_style_box(const Ref& p_style_box,const Rect2& p_rect); + void draw_primitive(const Vector& p_points, const Vector& p_colors,const Vector& p_uvs, Ref p_texture=Ref(),float p_width=1); + void draw_polygon(const Vector& p_points, const Vector& p_colors,const Vector& p_uvs=Vector(), Ref p_texture=Ref()); + void draw_colored_polygon(const Vector& p_points, const Color& p_color,const Vector& p_uvs=Vector(), Ref p_texture=Ref()); + + void draw_string(const Ref& p_font, const Point2& p_pos, const String& p_text,const Color& p_modulate=Color(1,1,1),int p_clip_w=-1); + float draw_char(const Ref& p_font,const Point2& p_pos, const String& p_char,const String& p_next="",const Color& p_modulate=Color(1,1,1)); + + void draw_set_transform(const Point2& p_offset, float p_rot, const Size2& p_scale); + + /* RECT / TRANSFORM */ + + void set_as_toplevel(bool p_toplevel); + bool is_set_as_toplevel() const; + + void set_on_top(bool p_on_top); + bool is_on_top() const; + + CanvasItem *get_parent_item() const; + + virtual Rect2 get_item_rect() const=0; + virtual Matrix32 get_transform() const=0; + + virtual Matrix32 get_global_transform() const; + virtual Matrix32 get_global_transform_with_canvas() const; + + CanvasItem *get_toplevel() const; + _FORCE_INLINE_ RID get_canvas_item() const { return canvas_item; } + + void set_block_transform_notify(bool p_enable); + bool is_block_transform_notify_enabled() const; + + Matrix32 get_viewport_transform() const; + Rect2 get_viewport_rect() const; + RID get_viewport_rid() const; + RID get_canvas() const; + Ref get_world_2d() const; + Viewport *get_viewport() const; + + + CanvasItem(); + ~CanvasItem(); +}; + +VARIANT_ENUM_CAST( CanvasItem::BlendMode ); + +#endif // CANVAS_ITEM_H diff --git a/scene/2d/collision_object_2d.cpp b/scene/2d/collision_object_2d.cpp new file mode 100644 index 00000000000..ab8c4551ee2 --- /dev/null +++ b/scene/2d/collision_object_2d.cpp @@ -0,0 +1,282 @@ +/*************************************************************************/ +/* collision_object_2d.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 "collision_object_2d.h" +#include "servers/physics_2d_server.h" + +void CollisionObject2D::_update_shapes_from_children() { + + shapes.resize(0); + for(int i=0;icall("_add_to_collision_object",this); + } + +// _update_shapes(); +} + +void CollisionObject2D::_notification(int p_what) { + + switch(p_what) { + + case NOTIFICATION_ENTER_SCENE: { + + + RID space = get_world_2d()->get_space(); + if (area) { + Physics2DServer::get_singleton()->area_set_space(rid,space); + } else + Physics2DServer::get_singleton()->body_set_space(rid,space); + + //get space + } + + case NOTIFICATION_TRANSFORM_CHANGED: { + + if (area) + Physics2DServer::get_singleton()->area_set_transform(rid,get_global_transform()); + else + Physics2DServer::get_singleton()->body_set_state(rid,Physics2DServer::BODY_STATE_TRANSFORM,get_global_transform()); + + } break; + case NOTIFICATION_EXIT_SCENE: { + + if (area) { + Physics2DServer::get_singleton()->area_set_space(rid,RID()); + } else + Physics2DServer::get_singleton()->body_set_space(rid,RID()); + + } break; + } +} + +void CollisionObject2D::_update_shapes() { + + if (!rid.is_valid()) + return; + + if (area) + Physics2DServer::get_singleton()->area_clear_shapes(rid); + else + Physics2DServer::get_singleton()->body_clear_shapes(rid); + + for(int i=0;iarea_add_shape(rid,shapes[i].shape->get_rid(),shapes[i].xform); + else { + Physics2DServer::get_singleton()->body_add_shape(rid,shapes[i].shape->get_rid(),shapes[i].xform); + if (shapes[i].trigger) + Physics2DServer::get_singleton()->body_set_shape_as_trigger(rid,i,shapes[i].trigger); + } + } +} + + +bool CollisionObject2D::_set(const StringName& p_name, const Variant& p_value) { + String name=p_name; + + if (name=="shape_count") { + + shapes.resize(p_value); + _update_shapes(); + _change_notify(); + + } else if (name.begins_with("shapes/")) { + + int idx=name.get_slice("/",1).to_int(); + String what=name.get_slice("/",2); + if (what=="shape") + set_shape(idx,RefPtr(p_value)); + else if (what=="transform") + set_shape_transform(idx,p_value); + else if (what=="trigger") + set_shape_as_trigger(idx,p_value); + } else + return false; + + return true; + + +} + +bool CollisionObject2D::_get(const StringName& p_name,Variant &r_ret) const { + + String name=p_name; + + if (name=="shape_count") { + r_ret= shapes.size(); + } else if (name.begins_with("shapes/")) { + + int idx=name.get_slice("/",1).to_int(); + String what=name.get_slice("/",2); + if (what=="shape") + r_ret= get_shape(idx); + else if (what=="transform") + r_ret= get_shape_transform(idx); + else if (what=="trigger") + r_ret= is_shape_set_as_trigger(idx); + } else + return false; + + return true; +} + +void CollisionObject2D::_get_property_list( List *p_list) const { + + p_list->push_back( PropertyInfo(Variant::INT,"shape_count",PROPERTY_HINT_RANGE,"0,256,1",PROPERTY_USAGE_NOEDITOR) ); + + for(int i=0;ipush_back( PropertyInfo(Variant::OBJECT,path+"shape",PROPERTY_HINT_RESOURCE_TYPE,"Shape2D",PROPERTY_USAGE_NOEDITOR) ); + p_list->push_back( PropertyInfo(Variant::TRANSFORM,path+"transform",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_NOEDITOR) ); + p_list->push_back( PropertyInfo(Variant::BOOL,path+"trigger",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_NOEDITOR) ); + } +} + +void CollisionObject2D::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("add_shape","shape:Shape2D","transform"),&CollisionObject2D::add_shape,DEFVAL(Matrix32())); + ObjectTypeDB::bind_method(_MD("get_shape_count"),&CollisionObject2D::get_shape_count); + ObjectTypeDB::bind_method(_MD("set_shape","shape_idx","shape:Shape"),&CollisionObject2D::set_shape); + ObjectTypeDB::bind_method(_MD("set_shape_transform","shape_idx","transform"),&CollisionObject2D::set_shape_transform); + ObjectTypeDB::bind_method(_MD("set_shape_as_trigger","shape_idx","enable"),&CollisionObject2D::set_shape_as_trigger); + ObjectTypeDB::bind_method(_MD("get_shape:Shape2D","shape_idx"),&CollisionObject2D::get_shape); + ObjectTypeDB::bind_method(_MD("get_shape_transform","shape_idx"),&CollisionObject2D::get_shape_transform); + ObjectTypeDB::bind_method(_MD("is_shape_set_as_trigger","shape_idx"),&CollisionObject2D::is_shape_set_as_trigger); + ObjectTypeDB::bind_method(_MD("remove_shape","shape_idx"),&CollisionObject2D::remove_shape); + ObjectTypeDB::bind_method(_MD("clear_shapes"),&CollisionObject2D::clear_shapes); + ObjectTypeDB::bind_method(_MD("get_rid"),&CollisionObject2D::get_rid); + +} + + +void CollisionObject2D::add_shape(const Ref& p_shape, const Matrix32& p_transform) { + + ShapeData sdata; + sdata.shape=p_shape; + sdata.xform=p_transform; + sdata.trigger=false; + shapes.push_back(sdata); + _update_shapes(); + +} +int CollisionObject2D::get_shape_count() const { + + return shapes.size(); + +} +void CollisionObject2D::set_shape(int p_shape_idx, const Ref& p_shape) { + + ERR_FAIL_INDEX(p_shape_idx,shapes.size()); + shapes[p_shape_idx].shape=p_shape; + _update_shapes(); +} + +void CollisionObject2D::set_shape_transform(int p_shape_idx, const Matrix32& p_transform) { + + ERR_FAIL_INDEX(p_shape_idx,shapes.size()); + shapes[p_shape_idx].xform=p_transform; + + _update_shapes(); +} + +Ref CollisionObject2D::get_shape(int p_shape_idx) const { + + ERR_FAIL_INDEX_V(p_shape_idx,shapes.size(),Ref()); + return shapes[p_shape_idx].shape; + +} +Matrix32 CollisionObject2D::get_shape_transform(int p_shape_idx) const { + + ERR_FAIL_INDEX_V(p_shape_idx,shapes.size(),Matrix32()); + return shapes[p_shape_idx].xform; + +} +void CollisionObject2D::remove_shape(int p_shape_idx) { + + ERR_FAIL_INDEX(p_shape_idx,shapes.size()); + shapes.remove(p_shape_idx); + + _update_shapes(); +} + +void CollisionObject2D::set_shape_as_trigger(int p_shape_idx, bool p_trigger) { + + ERR_FAIL_INDEX(p_shape_idx,shapes.size()); + shapes[p_shape_idx].trigger=p_trigger; + if (!area && rid.is_valid()) { + + Physics2DServer::get_singleton()->body_set_shape_as_trigger(rid,p_shape_idx,p_trigger); + + } +} + +bool CollisionObject2D::is_shape_set_as_trigger(int p_shape_idx) const { + + ERR_FAIL_INDEX_V(p_shape_idx,shapes.size(),false); + return shapes[p_shape_idx].trigger; +} + +void CollisionObject2D::clear_shapes() { + + shapes.clear(); + + _update_shapes(); +} + + +CollisionObject2D::CollisionObject2D(RID p_rid, bool p_area) { + + rid=p_rid; + area=p_area; + if (p_area) { + Physics2DServer::get_singleton()->area_attach_object_instance_ID(rid,get_instance_ID()); + } else { + Physics2DServer::get_singleton()->body_attach_object_instance_ID(rid,get_instance_ID()); + } + + +} + + +CollisionObject2D::CollisionObject2D() { + + + //owner= + + +} + +CollisionObject2D::~CollisionObject2D() { + + Physics2DServer::get_singleton()->free(rid); +} diff --git a/scene/2d/collision_object_2d.h b/scene/2d/collision_object_2d.h new file mode 100644 index 00000000000..4a529ce062f --- /dev/null +++ b/scene/2d/collision_object_2d.h @@ -0,0 +1,89 @@ +/*************************************************************************/ +/* collision_object_2d.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 COLLISION_OBJECT_2D_H +#define COLLISION_OBJECT_2D_H + +#include "scene/2d/node_2d.h" +#include "scene/resources/shape_2d.h" + +class CollisionObject2D : public Node2D { + + OBJ_TYPE( CollisionObject2D, Node2D ); + + bool area; + RID rid; + + struct ShapeData { + Matrix32 xform; + Ref shape; + bool trigger; + + ShapeData() { + trigger=false; + } + }; + + + Vector shapes; + + void _update_shapes(); + +friend class CollisionShape2D; +friend class CollisionPolygon2D; + void _update_shapes_from_children(); +protected: + + CollisionObject2D(RID p_rid, bool p_area); + + void _notification(int p_what); + 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 *p_list) const; + static void _bind_methods(); +public: + + + void add_shape(const Ref& p_shape, const Matrix32& p_transform=Matrix32()); + int get_shape_count() const; + void set_shape(int p_shape_idx, const Ref& p_shape); + void set_shape_transform(int p_shape_idx, const Matrix32& p_transform); + Ref get_shape(int p_shape_idx) const; + Matrix32 get_shape_transform(int p_shape_idx) const; + void set_shape_as_trigger(int p_shape_idx, bool p_trigger); + bool is_shape_set_as_trigger(int p_shape_idx) const; + void remove_shape(int p_shape_idx); + void clear_shapes(); + + _FORCE_INLINE_ RID get_rid() const { return rid; } + + CollisionObject2D(); + ~CollisionObject2D(); +}; + +#endif // COLLISION_OBJECT_2D_H diff --git a/scene/2d/collision_polygon_2d.cpp b/scene/2d/collision_polygon_2d.cpp new file mode 100644 index 00000000000..5ab223a1b8b --- /dev/null +++ b/scene/2d/collision_polygon_2d.cpp @@ -0,0 +1,187 @@ +/*************************************************************************/ +/* collision_polygon_2d.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 "collision_polygon_2d.h" +#include "collision_object_2d.h" +#include "scene/resources/concave_polygon_shape_2d.h" +#include "scene/resources/convex_polygon_shape_2d.h" + +void CollisionPolygon2D::_add_to_collision_object(Object *p_obj) { + + CollisionObject2D *co = p_obj->cast_to(); + ERR_FAIL_COND(!co); + + if (polygon.size()==0) + return; + + bool solids=build_mode==BUILD_SOLIDS; + + + if (solids) { + + //here comes the sun, lalalala + //decompose concave into multiple convex polygons and add them + Vector< Vector > decomp = Geometry::decompose_polygon(polygon); + for(int i=0;i convex = memnew( ConvexPolygonShape2D ); + convex->set_points(decomp[i]); + co->add_shape(convex,get_transform()); + + } + + } else { + + Ref concave = memnew( ConcavePolygonShape2D ); + + DVector segments; + segments.resize(polygon.size()*2); + DVector::Write w=segments.write(); + + for(int i=0;i::Write(); + concave->set_segments(segments); + + co->add_shape(concave,get_transform()); + + } + + + //co->add_shape(shape,get_transform()); +} + +void CollisionPolygon2D::_update_parent() { + + Node *parent = get_parent(); + if (!parent) + return; + CollisionObject2D *co = parent->cast_to(); + if (!co) + return; + co->_update_shapes_from_children(); +} + +void CollisionPolygon2D::_notification(int p_what) { + + + switch(p_what) { + case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: { + + if (!is_inside_scene()) + break; + _update_parent(); + + } break; + + case NOTIFICATION_DRAW: { + for(int i=0;i > decomp = Geometry::decompose_polygon(polygon); +#define DEBUG_DECOMPOSE +#ifdef DEBUG_DECOMPOSE + Color c(0.4,0.9,0.1); + for(int i=0;i& p_polygon) { + + polygon=p_polygon; + + for(int i=0;i CollisionPolygon2D::get_polygon() const { + + return polygon; +} + +void CollisionPolygon2D::set_build_mode(BuildMode p_mode) { + + ERR_FAIL_INDEX(p_mode,2); + build_mode=p_mode; +} + +CollisionPolygon2D::BuildMode CollisionPolygon2D::get_build_mode() const{ + + return build_mode; +} + +Rect2 CollisionPolygon2D::get_item_rect() const { + + return aabb; +} + +void CollisionPolygon2D::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("_add_to_collision_object"),&CollisionPolygon2D::_add_to_collision_object); + ObjectTypeDB::bind_method(_MD("set_polygon","polygon"),&CollisionPolygon2D::set_polygon); + ObjectTypeDB::bind_method(_MD("get_polygon"),&CollisionPolygon2D::get_polygon); + + ObjectTypeDB::bind_method(_MD("set_build_mode"),&CollisionPolygon2D::set_build_mode); + ObjectTypeDB::bind_method(_MD("get_build_mode"),&CollisionPolygon2D::get_build_mode); + + ADD_PROPERTY( PropertyInfo(Variant::INT,"build_mode",PROPERTY_HINT_ENUM,"Automatic,Segments,Solids"),_SCS("set_build_mode"),_SCS("get_build_mode")); + ADD_PROPERTY( PropertyInfo(Variant::VECTOR2_ARRAY,"polygon"),_SCS("set_polygon"),_SCS("get_polygon")); +} + +CollisionPolygon2D::CollisionPolygon2D() { + + aabb=Rect2(-10,-10,20,20); + build_mode=BUILD_SOLIDS; + + +} diff --git a/scene/2d/collision_polygon_2d.h b/scene/2d/collision_polygon_2d.h new file mode 100644 index 00000000000..09c2060088f --- /dev/null +++ b/scene/2d/collision_polygon_2d.h @@ -0,0 +1,75 @@ +/*************************************************************************/ +/* collision_polygon_2d.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 COLLISION_POLYGON_2D_H +#define COLLISION_POLYGON_2D_H + +#include "scene/2d/node_2d.h" +#include "scene/resources/shape_2d.h" + + + +class CollisionPolygon2D : public Node2D { + + OBJ_TYPE(CollisionPolygon2D,Node2D); +public: + + enum BuildMode { + BUILD_SOLIDS, + BUILD_SEGMENTS, + }; + +protected: + + + Rect2 aabb; + BuildMode build_mode; + Vector polygon; + + void _add_to_collision_object(Object *p_obj); + void _update_parent(); + +protected: + + void _notification(int p_what); + static void _bind_methods(); +public: + + void set_build_mode(BuildMode p_mode); + BuildMode get_build_mode() const; + + void set_polygon(const Vector& p_polygon); + Vector get_polygon() const; + + virtual Rect2 get_item_rect() const; + CollisionPolygon2D(); +}; + +VARIANT_ENUM_CAST( CollisionPolygon2D::BuildMode ); + +#endif // COLLISION_POLYGON_2D_H diff --git a/scene/2d/collision_shape_2d.cpp b/scene/2d/collision_shape_2d.cpp new file mode 100644 index 00000000000..4ae9b37c21d --- /dev/null +++ b/scene/2d/collision_shape_2d.cpp @@ -0,0 +1,248 @@ +/*************************************************************************/ +/* collision_shape_2d.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 "collision_shape_2d.h" +#include "collision_object_2d.h" +#include "scene/resources/segment_shape_2d.h" +#include "scene/resources/shape_line_2d.h" +#include "scene/resources/circle_shape_2d.h" +#include "scene/resources/rectangle_shape_2d.h" +#include "scene/resources/capsule_shape_2d.h" +#include "scene/resources/convex_polygon_shape_2d.h" +#include "scene/resources/concave_polygon_shape_2d.h" + + +void CollisionShape2D::_add_to_collision_object(Object *p_obj) { + + CollisionObject2D *co = p_obj->cast_to(); + ERR_FAIL_COND(!co); + co->add_shape(shape,get_transform()); + if (trigger) + co->set_shape_as_trigger(co->get_shape_count()-1,true); + +} + +void CollisionShape2D::_shape_changed() { + + update(); + _update_parent(); +} + +void CollisionShape2D::_update_parent() { + + + Node *parent = get_parent(); + if (!parent) + return; + CollisionObject2D *co = parent->cast_to(); + if (!co) + return; + co->_update_shapes_from_children(); +} + +void CollisionShape2D::_notification(int p_what) { + + switch(p_what) { + + case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: { + + if (!is_inside_scene()) + break; + _update_parent(); + + } break; + /* + case NOTIFICATION_TRANSFORM_CHANGED: { + + if (!is_inside_scene()) + break; + _update_parent(); + + } break;*/ + case NOTIFICATION_DRAW: { + + rect=Rect2(); + + Color draw_col=Color(0,0.6,0.7,0.5); + + if (shape->cast_to()) { + + LineShape2D *l = shape->cast_to(); + Vector2 point = l->get_d() * l->get_normal(); + + Vector2 l1[2]={point-l->get_normal().tangent()*100,point+l->get_normal().tangent()*100}; + draw_line(l1[0],l1[1],draw_col,3); + Vector2 l2[2]={point,point+l->get_normal()*30}; + draw_line(l2[0],l2[1],draw_col,3); + rect.pos=l1[0]; + rect.expand_to(l1[1]); + rect.expand_to(l2[0]); + rect.expand_to(l2[1]); + + } else if (shape->cast_to()) { + + SegmentShape2D *s = shape->cast_to(); + draw_line(s->get_a(),s->get_b(),draw_col,3); + rect.pos=s->get_a(); + rect.expand_to(s->get_b()); + + } else if (shape->cast_to()) { + + RayShape2D *s = shape->cast_to(); + + Vector2 tip = Vector2(0,s->get_length()); + draw_line(Vector2(),tip,draw_col,3); + Vector pts; + float tsize=4; + pts.push_back(tip+Vector2(0,tsize)); + pts.push_back(tip+Vector2(0.707*tsize,0)); + pts.push_back(tip+Vector2(-0.707*tsize,0)); + Vector cols; + for(int i=0;i<3;i++) + cols.push_back(draw_col); + + draw_primitive(pts,cols,Vector()); //small arrow + + rect.pos=Vector2(); + rect.expand_to(tip); + rect=rect.grow(0.707*tsize); + + } else if (shape->cast_to()) { + + CircleShape2D *s = shape->cast_to(); + Vector points; + for(int i=0;i<24;i++) { + + points.push_back(Vector2(Math::cos(i*Math_PI*2/24.0),Math::sin(i*Math_PI*2/24.0))*s->get_radius()); + } + + draw_colored_polygon(points,draw_col); + rect.pos=-Point2(s->get_radius(),s->get_radius()); + rect.size=Point2(s->get_radius(),s->get_radius())*2.0; + + } else if (shape->cast_to()) { + + RectangleShape2D *s = shape->cast_to(); + Vector2 he = s->get_extents(); + rect=Rect2(-he,he*2.0); + draw_rect(rect,draw_col);; + + } else if (shape->cast_to()) { + + CapsuleShape2D *s = shape->cast_to(); + + Vector points; + for(int i=0;i<24;i++) { + Vector2 ofs = Vector2(0,(i>6 && i<=18) ? -s->get_height()*0.5 : s->get_height()*0.5); + + points.push_back(Vector2(Math::sin(i*Math_PI*2/24.0),Math::cos(i*Math_PI*2/24.0))*s->get_radius() + ofs); + if (i==6 || i==18) + points.push_back(Vector2(Math::sin(i*Math_PI*2/24.0),Math::cos(i*Math_PI*2/24.0))*s->get_radius() - ofs); + } + + draw_colored_polygon(points,draw_col); + Vector2 he=Point2(s->get_radius(),s->get_radius()+s->get_height()*0.5); + rect.pos=-he; + rect.size=he*2.0; + + } else if (shape->cast_to()) { + + ConvexPolygonShape2D *s = shape->cast_to(); + + Vector points = s->get_points(); + for(int i=0;i& p_shape) { + + if (shape.is_valid()) + shape->disconnect("changed",this,"_shape_changed"); + shape=p_shape; + update(); + _update_parent(); + if (shape.is_valid()) + shape->connect("changed",this,"_shape_changed"); + +} + +Ref CollisionShape2D::get_shape() const { + + return shape; +} + +Rect2 CollisionShape2D::get_item_rect() const { + + return rect; +} + +void CollisionShape2D::set_trigger(bool p_trigger) { + + trigger=p_trigger; + _update_parent(); +} + +bool CollisionShape2D::is_trigger() const{ + + return trigger; +} + +void CollisionShape2D::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("set_shape","shape"),&CollisionShape2D::set_shape); + ObjectTypeDB::bind_method(_MD("get_shape"),&CollisionShape2D::get_shape); + ObjectTypeDB::bind_method(_MD("_shape_changed"),&CollisionShape2D::_shape_changed); + ObjectTypeDB::bind_method(_MD("_add_to_collision_object"),&CollisionShape2D::_add_to_collision_object); + ObjectTypeDB::bind_method(_MD("set_trigger","enable"),&CollisionShape2D::set_trigger); + ObjectTypeDB::bind_method(_MD("is_trigger"),&CollisionShape2D::is_trigger); + + ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT,"shape",PROPERTY_HINT_RESOURCE_TYPE,"Shape2D"),_SCS("set_shape"),_SCS("get_shape")); + ADD_PROPERTY(PropertyInfo(Variant::BOOL,"trigger"),_SCS("set_trigger"),_SCS("is_trigger")); +} + +CollisionShape2D::CollisionShape2D() { + + rect=Rect2(-Point2(10,10),Point2(20,20)); + + trigger=false; +} diff --git a/scene/2d/collision_shape_2d.h b/scene/2d/collision_shape_2d.h new file mode 100644 index 00000000000..2e2023f54d0 --- /dev/null +++ b/scene/2d/collision_shape_2d.h @@ -0,0 +1,61 @@ +/*************************************************************************/ +/* collision_shape_2d.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 COLLISION_SHAPE_2D_H +#define COLLISION_SHAPE_2D_H + +#include "scene/2d/node_2d.h" +#include "scene/resources/shape_2d.h" + +class CollisionShape2D : public Node2D { + + OBJ_TYPE(CollisionShape2D,Node2D); + Ref shape; + Rect2 rect; + bool trigger; + + void _shape_changed(); +protected: + + void _update_parent(); + void _notification(int p_what); + static void _bind_methods(); + + void _add_to_collision_object(Object *p_obj); +public: + + void set_shape(const Ref& p_shape); + Ref get_shape() const; + virtual Rect2 get_item_rect() const; + void set_trigger(bool p_trigger); + bool is_trigger() const; + + CollisionShape2D(); +}; + +#endif // COLLISION_SHAPE_2D_H diff --git a/scene/2d/joints_2d.cpp b/scene/2d/joints_2d.cpp new file mode 100644 index 00000000000..0e673ff7911 --- /dev/null +++ b/scene/2d/joints_2d.cpp @@ -0,0 +1,412 @@ +/*************************************************************************/ +/* joints_2d.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 "joints_2d.h" +#include "servers/physics_2d_server.h" +#include "physics_body_2d.h" + +void Joint2D::_update_joint() { + + if (!is_inside_scene()) + return; + + if (joint.is_valid()) { + Physics2DServer::get_singleton()->free(joint); + } + + joint=RID(); + + + joint = _configure_joint(); + Physics2DServer::get_singleton()->get_singleton()->joint_set_param(joint,Physics2DServer::JOINT_PARAM_BIAS,bias); + + +} + + +void Joint2D::set_node_a(const NodePath& p_node_a) { + + + if (a==p_node_a) + return; + + a=p_node_a; + _update_joint(); +} + +NodePath Joint2D::get_node_a() const{ + + return a; +} + +void Joint2D::set_node_b(const NodePath& p_node_b){ + + if (b==p_node_b) + return; + b=p_node_b; + _update_joint(); + +} +NodePath Joint2D::get_node_b() const{ + + + return b; +} + + +void Joint2D::_notification(int p_what) { + + switch(p_what) { + + case NOTIFICATION_READY: { + _update_joint(); + } break; + case NOTIFICATION_EXIT_SCENE: { + if (joint.is_valid()) { + + Physics2DServer::get_singleton()->free(joint); + joint=RID(); + } + } break; + + } + +} + +void Joint2D::set_bias(real_t p_bias) { + + bias=p_bias; + if (joint.is_valid()) + Physics2DServer::get_singleton()->get_singleton()->joint_set_param(joint,Physics2DServer::JOINT_PARAM_BIAS,bias); +} + +real_t Joint2D::get_bias() const{ + + + return bias; +} + + +void Joint2D::_bind_methods() { + + + ObjectTypeDB::bind_method( _MD("set_node_a","node"), &Joint2D::set_node_a ); + ObjectTypeDB::bind_method( _MD("get_node_a"), &Joint2D::get_node_a ); + + ObjectTypeDB::bind_method( _MD("set_node_b","node"), &Joint2D::set_node_b ); + ObjectTypeDB::bind_method( _MD("get_node_b"), &Joint2D::get_node_b ); + + ObjectTypeDB::bind_method( _MD("set_bias","bias"), &Joint2D::set_bias ); + ObjectTypeDB::bind_method( _MD("get_bias"), &Joint2D::get_bias ); + + ADD_PROPERTY( PropertyInfo( Variant::NODE_PATH, "node_a"), _SCS("set_node_a"),_SCS("get_node_a") ); + ADD_PROPERTY( PropertyInfo( Variant::NODE_PATH, "node_b"), _SCS("set_node_b"),_SCS("get_node_b") ); + ADD_PROPERTY( PropertyInfo( Variant::REAL, "bias/bias",PROPERTY_HINT_RANGE,"0,0.9,0.01"), _SCS("set_bias"),_SCS("get_bias") ); + +} + + + +Joint2D::Joint2D() { + + bias=0; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void PinJoint2D::_notification(int p_what) { + + switch(p_what) { + case NOTIFICATION_DRAW: { + if (is_inside_scene() && get_scene()->is_editor_hint()) { + + draw_line(Point2(-10,0),Point2(+10,0),Color(0.7,0.6,0.0,0.5),3); + draw_line(Point2(0,-10),Point2(0,+10),Color(0.7,0.6,0.0,0.5),3); + } + } break; + } + +} + +RID PinJoint2D::_configure_joint() { + + Node *node_a = has_node( get_node_a() ) ? get_node( get_node_a() ) : (Node*)NULL; + Node *node_b = has_node( get_node_b() ) ? get_node( get_node_b() ) : (Node*)NULL; + + if (!node_a && !node_b) + return RID(); + + PhysicsBody2D *body_a=node_a ? node_a->cast_to() : (PhysicsBody2D*)NULL; + PhysicsBody2D *body_b=node_b ? node_b->cast_to() : (PhysicsBody2D*)NULL; + + if (!body_a && !body_b) + return RID(); + + if (!body_a) { + SWAP(body_a,body_b); + } else if (body_b) { + //add a collision exception between both + Physics2DServer::get_singleton()->body_add_collision_exception(body_a->get_rid(),body_b->get_rid()); + } + + return Physics2DServer::get_singleton()->pin_joint_create(get_global_transform().get_origin(),body_a->get_rid(),body_b?body_b->get_rid():RID()); + +} + + +PinJoint2D::PinJoint2D() { + + +} + + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + + +void GrooveJoint2D::_notification(int p_what) { + + switch(p_what) { + case NOTIFICATION_DRAW: { + if (is_inside_scene() && get_scene()->is_editor_hint()) { + + draw_line(Point2(-10,0),Point2(+10,0),Color(0.7,0.6,0.0,0.5),3); + draw_line(Point2(-10,length),Point2(+10,length),Color(0.7,0.6,0.0,0.5),3); + draw_line(Point2(0,0),Point2(0,length),Color(0.7,0.6,0.0,0.5),3); + draw_line(Point2(-10,initial_offset),Point2(+10,initial_offset),Color(0.8,0.8,0.9,0.5),5); + } + } break; + } +} + +RID GrooveJoint2D::_configure_joint(){ + + + Node *node_a = has_node( get_node_a() ) ? get_node( get_node_a() ) : (Node*)NULL; + Node *node_b = has_node( get_node_b() ) ? get_node( get_node_b() ) : (Node*)NULL; + + if (!node_a || !node_b) + return RID(); + + PhysicsBody2D *body_a=node_a->cast_to(); + PhysicsBody2D *body_b=node_b->cast_to(); + + if (!body_a || !body_b) + return RID(); + + Physics2DServer::get_singleton()->body_add_collision_exception(body_a->get_rid(),body_b->get_rid()); + + Matrix32 gt = get_global_transform(); + Vector2 groove_A1 = gt.get_origin(); + Vector2 groove_A2 = gt.xform( Vector2(0,length) ); + Vector2 anchor_B = gt.xform( Vector2(0,initial_offset) ); + + + return Physics2DServer::get_singleton()->groove_joint_create(groove_A1,groove_A2,anchor_B,body_a->get_rid(),body_b->get_rid()); +} + + +void GrooveJoint2D::set_length(real_t p_length) { + + length=p_length; + update(); +} + +real_t GrooveJoint2D::get_length() const { + + return length; +} + + +void GrooveJoint2D::set_initial_offset(real_t p_initial_offset) { + + initial_offset=p_initial_offset; + update(); +} + +real_t GrooveJoint2D::get_initial_offset() const { + + return initial_offset; +} + + + +void GrooveJoint2D::_bind_methods() { + + + ObjectTypeDB::bind_method(_MD("set_length","length"),&GrooveJoint2D::set_length); + ObjectTypeDB::bind_method(_MD("get_length"),&GrooveJoint2D::get_length); + ObjectTypeDB::bind_method(_MD("set_initial_offset","offset"),&GrooveJoint2D::set_initial_offset); + ObjectTypeDB::bind_method(_MD("get_initial_offset"),&GrooveJoint2D::get_initial_offset); + + ADD_PROPERTY( PropertyInfo( Variant::REAL, "length", PROPERTY_HINT_EXP_RANGE,"1,65535,1"), _SCS("set_length"),_SCS("get_length")); + ADD_PROPERTY( PropertyInfo( Variant::REAL, "initial_offset", PROPERTY_HINT_EXP_RANGE,"1,65535,1"), _SCS("set_initial_offset"),_SCS("get_initial_offset")); +} + +GrooveJoint2D::GrooveJoint2D() { + + length=50; + initial_offset=25; +} + + + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + + + +void DampedSpringJoint2D::_notification(int p_what) { + + switch(p_what) { + case NOTIFICATION_DRAW: { + if (is_inside_scene() && get_scene()->is_editor_hint()) { + + draw_line(Point2(-10,0),Point2(+10,0),Color(0.7,0.6,0.0,0.5),3); + draw_line(Point2(-10,length),Point2(+10,length),Color(0.7,0.6,0.0,0.5),3); + draw_line(Point2(0,0),Point2(0,length),Color(0.7,0.6,0.0,0.5),3); + } + } break; + } +} + +RID DampedSpringJoint2D::_configure_joint(){ + + + Node *node_a = has_node( get_node_a() ) ? get_node( get_node_a() ) : (Node*)NULL; + Node *node_b = has_node( get_node_b() ) ? get_node( get_node_b() ) : (Node*)NULL; + + if (!node_a || !node_b) + return RID(); + + PhysicsBody2D *body_a=node_a->cast_to(); + PhysicsBody2D *body_b=node_b->cast_to(); + + if (!body_a || !body_b) + return RID(); + + Physics2DServer::get_singleton()->body_add_collision_exception(body_a->get_rid(),body_b->get_rid()); + + Matrix32 gt = get_global_transform(); + Vector2 anchor_A = gt.get_origin(); + Vector2 anchor_B = gt.xform( Vector2(0,length) ); + + RID dsj = Physics2DServer::get_singleton()->damped_spring_joint_create(anchor_A,anchor_B,body_a->get_rid(),body_b->get_rid()); + if (rest_length) + Physics2DServer::get_singleton()->damped_string_joint_set_param(dsj,Physics2DServer::DAMPED_STRING_REST_LENGTH,rest_length); + Physics2DServer::get_singleton()->damped_string_joint_set_param(dsj,Physics2DServer::DAMPED_STRING_STIFFNESS,stiffness); + Physics2DServer::get_singleton()->damped_string_joint_set_param(dsj,Physics2DServer::DAMPED_STRING_DAMPING,damping); + + return dsj; +} + + +void DampedSpringJoint2D::set_length(real_t p_length) { + + length=p_length; + update(); +} + +real_t DampedSpringJoint2D::get_length() const { + + return length; +} + +void DampedSpringJoint2D::set_rest_length(real_t p_rest_length) { + + rest_length=p_rest_length; + update(); + if (get_joint().is_valid()) + Physics2DServer::get_singleton()->damped_string_joint_set_param(get_joint(),Physics2DServer::DAMPED_STRING_REST_LENGTH,p_rest_length?p_rest_length:length); + +} + +real_t DampedSpringJoint2D::get_rest_length() const { + + return rest_length; +} + +void DampedSpringJoint2D::set_stiffness(real_t p_stiffness) { + + stiffness=p_stiffness; + update(); + if (get_joint().is_valid()) + Physics2DServer::get_singleton()->damped_string_joint_set_param(get_joint(),Physics2DServer::DAMPED_STRING_STIFFNESS,p_stiffness); +} + +real_t DampedSpringJoint2D::get_stiffness() const { + + return stiffness; +} + +void DampedSpringJoint2D::set_damping(real_t p_damping) { + + damping=p_damping; + update(); + if (get_joint().is_valid()) + Physics2DServer::get_singleton()->damped_string_joint_set_param(get_joint(),Physics2DServer::DAMPED_STRING_DAMPING,p_damping); +} + +real_t DampedSpringJoint2D::get_damping() const { + + return damping; +} + + +void DampedSpringJoint2D::_bind_methods() { + + + ObjectTypeDB::bind_method(_MD("set_length","length"),&DampedSpringJoint2D::set_length); + ObjectTypeDB::bind_method(_MD("get_length"),&DampedSpringJoint2D::get_length); + ObjectTypeDB::bind_method(_MD("set_rest_length","rest_length"),&DampedSpringJoint2D::set_rest_length); + ObjectTypeDB::bind_method(_MD("get_rest_length"),&DampedSpringJoint2D::get_rest_length); + ObjectTypeDB::bind_method(_MD("set_stiffness","stiffness"),&DampedSpringJoint2D::set_stiffness); + ObjectTypeDB::bind_method(_MD("get_stiffness"),&DampedSpringJoint2D::get_stiffness); + ObjectTypeDB::bind_method(_MD("set_damping","damping"),&DampedSpringJoint2D::set_damping); + ObjectTypeDB::bind_method(_MD("get_damping"),&DampedSpringJoint2D::get_damping); + + ADD_PROPERTY( PropertyInfo( Variant::REAL, "length", PROPERTY_HINT_EXP_RANGE,"1,65535,1"), _SCS("set_length"),_SCS("get_length")); + ADD_PROPERTY( PropertyInfo( Variant::REAL, "rest_length", PROPERTY_HINT_EXP_RANGE,"0,65535,1"), _SCS("set_rest_length"),_SCS("get_rest_length")); + ADD_PROPERTY( PropertyInfo( Variant::REAL, "stiffness", PROPERTY_HINT_EXP_RANGE,"0.1,64,0.1"), _SCS("set_stiffness"),_SCS("get_stiffness")); + ADD_PROPERTY( PropertyInfo( Variant::REAL, "damping", PROPERTY_HINT_EXP_RANGE,"0.01,16,0.01"), _SCS("set_damping"),_SCS("get_damping")); + +} + +DampedSpringJoint2D::DampedSpringJoint2D() { + + length=50; + rest_length=0; + stiffness=20; + damping=1; +} diff --git a/scene/2d/joints_2d.h b/scene/2d/joints_2d.h new file mode 100644 index 00000000000..7027e4386a7 --- /dev/null +++ b/scene/2d/joints_2d.h @@ -0,0 +1,141 @@ +/*************************************************************************/ +/* joints_2d.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 JOINTS_2D_H +#define JOINTS_2D_H + + +#include "node_2d.h" + +class Joint2D : public Node2D { + + OBJ_TYPE(Joint2D,Node2D); + + RID joint; + + NodePath a; + NodePath b; + real_t bias; + + +protected: + + void _update_joint(); + + void _notification(int p_what); + virtual RID _configure_joint()=0; + + static void _bind_methods(); +public: + + void set_node_a(const NodePath& p_node_a); + NodePath get_node_a() const; + + void set_node_b(const NodePath& p_node_b); + NodePath get_node_b() const; + + void set_bias(real_t p_bias); + real_t get_bias() const; + + RID get_joint() const { return joint; } + Joint2D(); + +}; + + +class PinJoint2D : public Joint2D { + + OBJ_TYPE(PinJoint2D,Joint2D); + +protected: + + void _notification(int p_what); + virtual RID _configure_joint(); +public: + + + + PinJoint2D(); +}; + +class GrooveJoint2D : public Joint2D { + + OBJ_TYPE(GrooveJoint2D,Joint2D); + + real_t length; + real_t initial_offset; + +protected: + + void _notification(int p_what); + virtual RID _configure_joint(); + static void _bind_methods(); +public: + + void set_length(real_t p_length); + real_t get_length() const; + + void set_initial_offset(real_t p_initial_offset); + real_t get_initial_offset() const; + + GrooveJoint2D(); +}; + +class DampedSpringJoint2D : public Joint2D { + + OBJ_TYPE(DampedSpringJoint2D,Joint2D); + + real_t stiffness; + real_t damping; + real_t rest_length; + real_t length; + +protected: + + void _notification(int p_what); + virtual RID _configure_joint(); + static void _bind_methods(); +public: + + void set_length(real_t p_length); + real_t get_length() const; + + void set_rest_length(real_t p_rest_length); + real_t get_rest_length() const; + + void set_damping(real_t p_damping); + real_t get_damping() const; + + void set_stiffness(real_t p_stiffness); + real_t get_stiffness() const; + + DampedSpringJoint2D(); +}; + + +#endif // JOINTS_2D_H diff --git a/scene/2d/node_2d.cpp b/scene/2d/node_2d.cpp new file mode 100644 index 00000000000..9b2994ef849 --- /dev/null +++ b/scene/2d/node_2d.cpp @@ -0,0 +1,291 @@ +/*************************************************************************/ +/* node_2d.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 "node_2d.h" + +#include "servers/visual_server.h" +#include "scene/gui/control.h" +#include "scene/main/viewport.h" +#include "message_queue.h" + + +void Node2D::edit_set_pivot(const Point2& p_pivot) { + + +} + +Point2 Node2D::edit_get_pivot() const { + + return Point2(); +} +bool Node2D::edit_has_pivot() const { + + return false; +} + +Variant Node2D::edit_get_state() const { + + Array state; + state.push_back(pos); + state.push_back(angle); + state.push_back(scale); + + return state; + +} +void Node2D::edit_set_state(const Variant& p_state) { + + Array state = p_state; + ERR_FAIL_COND( state.size() != 3); + + pos = state[0]; + angle = state[1]; + scale = state[2]; + _update_transform(); + _change_notify("transform/rot"); + _change_notify("transform/scale"); + _change_notify("transform/pos"); + +} + +void Node2D::edit_set_rect(const Rect2& p_edit_rect) { + + Rect2 r = get_item_rect(); + + Vector2 zero_offset; + if (r.size.x!=0) + zero_offset.x = -r.pos.x / r.size.x; + if (r.size.y!=0) + zero_offset.y = -r.pos.y / r.size.y; + + Size2 new_scale(1,1); + + if (r.size.x!=0) + new_scale.x = p_edit_rect.size.x / r.size.x; + if (r.size.y!=0) + new_scale.y = p_edit_rect.size.y / r.size.y; + + Point2 new_pos = p_edit_rect.pos + p_edit_rect.size*zero_offset;//p_edit_rect.pos - r.pos; + + Matrix32 postxf; + postxf.set_rotation_and_scale(angle,scale); + new_pos = postxf.xform(new_pos); + + pos+=new_pos; + scale*=new_scale; + + _update_transform(); + _change_notify("transform/scale"); + _change_notify("transform/pos"); + +} + + +void Node2D::edit_rotate(float p_rot) { + + angle+=p_rot; + _update_transform(); + _change_notify("transform/rot"); +} + + +void Node2D::_update_xform_values() { + + pos=_mat.elements[2]; + angle=_mat.get_rotation(); + scale=_mat.get_scale(); + _xform_dirty=false; +} + +void Node2D::_update_transform() { + + Matrix32 mat(angle,pos); + _mat.set_rotation_and_scale(angle,scale); + _mat.elements[2]=pos; + + VisualServer::get_singleton()->canvas_item_set_transform(get_canvas_item(),_mat); + + if (!is_inside_scene()) + return; + + + _notify_transform(); +} + +void Node2D::set_pos(const Point2& p_pos) { + + if (_xform_dirty) + ((Node2D*)this)->_update_xform_values(); + pos=p_pos; + _update_transform(); + _change_notify("transform/pos"); + + +} + +void Node2D::set_rot(float p_angle) { + + if (_xform_dirty) + ((Node2D*)this)->_update_xform_values(); + angle=p_angle; + _update_transform(); + _change_notify("transform/rot"); +} + +void Node2D::set_scale(const Size2& p_scale) { + + if (_xform_dirty) + ((Node2D*)this)->_update_xform_values(); + scale=p_scale; + _update_transform(); + _change_notify("transform/scale"); + +} + +Point2 Node2D::get_pos() const { + + if (_xform_dirty) + ((Node2D*)this)->_update_xform_values(); + return pos; +} +float Node2D::get_rot() const { + if (_xform_dirty) + ((Node2D*)this)->_update_xform_values(); + + return angle; +} +Size2 Node2D::get_scale() const { + if (_xform_dirty) + ((Node2D*)this)->_update_xform_values(); + + return scale; +} + +void Node2D::_set_rotd(float p_angle) { + + set_rot(Math::deg2rad(p_angle)); +} + +float Node2D::_get_rotd() const { + + return Math::rad2deg(get_rot()); +} + + +void Node2D::_notification(int p_what) { + + switch(p_what) { + + } +} + +Matrix32 Node2D::get_transform() const { + + return _mat; +} + +Rect2 Node2D::get_item_rect() const { + + if (get_script_instance()) { + Variant::CallError err; + Rect2 r = get_script_instance()->call("_get_item_rect",NULL,0,err); + if (err.error==Variant::CallError::CALL_OK) + return r; + } + return Rect2(Point2(-32,-32),Size2(64,64)); +} + +Point2 Node2D::get_global_pos() const { + + return get_global_transform().get_origin(); +} + +void Node2D::set_transform(const Matrix32& p_transform) { + + _mat=p_transform; + _xform_dirty=true; + + VisualServer::get_singleton()->canvas_item_set_transform(get_canvas_item(),_mat); + + if (!is_inside_scene()) + return; + + _notify_transform(); +} + +void Node2D::set_global_transform(const Matrix32& p_transform) { + + CanvasItem *pi = get_parent_item(); + if (pi) + set_transform( pi->get_global_transform().affine_inverse() * p_transform); + else + set_transform(p_transform); + + +} + +void Node2D::_bind_methods() { + + + + ObjectTypeDB::bind_method(_MD("_get_rotd"),&Node2D::_get_rotd); + ObjectTypeDB::bind_method(_MD("_set_rotd"),&Node2D::_set_rotd); + + ObjectTypeDB::bind_method(_MD("set_pos","pos"),&Node2D::set_pos); + ObjectTypeDB::bind_method(_MD("set_rot","rot"),&Node2D::set_rot); + ObjectTypeDB::bind_method(_MD("set_scale","scale"),&Node2D::set_scale); + + ObjectTypeDB::bind_method(_MD("get_pos"),&Node2D::get_pos); + ObjectTypeDB::bind_method(_MD("get_rot"),&Node2D::get_rot); + ObjectTypeDB::bind_method(_MD("get_scale"),&Node2D::get_scale); + + ObjectTypeDB::bind_method(_MD("get_global_pos"),&Node2D::get_global_pos); + + ObjectTypeDB::bind_method(_MD("set_transform","xform"),&Node2D::set_transform); + ObjectTypeDB::bind_method(_MD("set_global_transform","xform"),&Node2D::set_global_transform); + + ObjectTypeDB::bind_method(_MD("edit_set_pivot"),&Node2D::edit_set_pivot); + + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2,"transform/pos"),_SCS("set_pos"),_SCS("get_pos")); + ADD_PROPERTY(PropertyInfo(Variant::REAL,"transform/rot",PROPERTY_HINT_RANGE,"-1440,1440,0.1"),_SCS("_set_rotd"),_SCS("_get_rotd")); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2,"transform/scale"),_SCS("set_scale"),_SCS("get_scale")); + + + +} + + +Node2D::Node2D() { + + + angle=0; + scale=Vector2(1,1); + _xform_dirty=false; + +} + diff --git a/scene/2d/node_2d.h b/scene/2d/node_2d.h new file mode 100644 index 00000000000..8da441dc63a --- /dev/null +++ b/scene/2d/node_2d.h @@ -0,0 +1,89 @@ +/*************************************************************************/ +/* node_2d.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 NODE2D_H +#define NODE2D_H + +#include "scene/2d/canvas_item.h" + +class Node2D : public CanvasItem { + + OBJ_TYPE(Node2D, CanvasItem ); + + Point2 pos; + float angle; + Size2 scale; + + Matrix32 _mat; + + bool _xform_dirty; + + void _update_transform(); + + void _set_rotd(float p_angle); + float _get_rotd() const; + + void _update_xform_values(); + +protected: + + + void _notification(int p_what); + + static void _bind_methods(); +public: + + virtual Variant edit_get_state() const; + virtual void edit_set_state(const Variant& p_state); + virtual void edit_set_rect(const Rect2& p_edit_rect); + virtual void edit_rotate(float p_rot); + virtual void edit_set_pivot(const Point2& p_pivot); + virtual Point2 edit_get_pivot() const; + virtual bool edit_has_pivot() const; + + void set_pos(const Point2& p_pos); + void set_rot(float p_angle); + void set_scale(const Size2& p_scale); + + Point2 get_pos() const; + float get_rot() const; + Size2 get_scale() const; + + Point2 get_global_pos() const; + virtual Rect2 get_item_rect() const; + + void set_transform(const Matrix32& p_transform); + void set_global_transform(const Matrix32& p_transform); + + + Matrix32 get_transform() const; + + Node2D(); +}; + +#endif // NODE2D_H diff --git a/scene/2d/node_2d_singleton.cpp b/scene/2d/node_2d_singleton.cpp new file mode 100644 index 00000000000..58e078101d4 --- /dev/null +++ b/scene/2d/node_2d_singleton.cpp @@ -0,0 +1,30 @@ +/*************************************************************************/ +/* node_2d_singleton.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 "node_2d_singleton.h" + diff --git a/scene/2d/node_2d_singleton.h b/scene/2d/node_2d_singleton.h new file mode 100644 index 00000000000..b1d1e65b8b6 --- /dev/null +++ b/scene/2d/node_2d_singleton.h @@ -0,0 +1,33 @@ +/*************************************************************************/ +/* node_2d_singleton.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 NODE_2D_SINGLETON_H +#define NODE_2D_SINGLETON_H + + +#endif // NODE_2D_SINGLETON_H diff --git a/scene/2d/parallax_background.cpp b/scene/2d/parallax_background.cpp new file mode 100644 index 00000000000..55607bd4eb9 --- /dev/null +++ b/scene/2d/parallax_background.cpp @@ -0,0 +1,200 @@ +/*************************************************************************/ +/* parallax_background.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 "parallax_background.h" +#include "parallax_layer.h" + + + +void ParallaxBackground::_notification(int p_what) { + + switch(p_what) { + + + case NOTIFICATION_ENTER_SCENE: { + + group_name = "__cameras_"+itos(get_viewport().get_id()); + add_to_group(group_name); + + } break; + case NOTIFICATION_EXIT_SCENE: { + + remove_from_group(group_name); + } break; + } + +} + +void ParallaxBackground::_camera_moved(const Matrix32& p_transform) { + + + set_scroll_offset(p_transform.get_origin()); + set_scroll_scale(p_transform.get_scale().dot(Vector2(0.5,0.5))); +} + + +void ParallaxBackground::set_scroll_scale(float p_scale) { + + scale=p_scale; +} + +float ParallaxBackground::get_scroll_scale() const{ + + return scale; +} + + +void ParallaxBackground::set_scroll_offset(const Point2& p_ofs) { + + offset=p_ofs; + + _update_scroll(); +} + +void ParallaxBackground::_update_scroll() { + + if (!is_inside_scene()) + return; + + Vector2 ofs = base_offset+offset*base_scale; + + Size2 vps = get_viewport_size(); + + ofs = -ofs; + if (limit_begin.x < limit_end.x) { + + if (ofs.x < limit_begin.x) + ofs.x=limit_begin.x; + else if (ofs.x+vps.x > limit_end.x) + ofs.x=limit_end.x-vps.x; + } + + + if (limit_begin.y < limit_end.y) { + + if (ofs.y < limit_begin.y) + ofs.y=limit_begin.y; + else if (ofs.y+vps.y > limit_end.y) + ofs.y=limit_end.y-vps.y; + } + ofs = -ofs; + + for(int i=0;icast_to(); + if (!l) + continue; + + l->set_base_offset_and_scale(ofs,scale); + } +} + +Point2 ParallaxBackground::get_scroll_offset() const { + + return offset; +} + +void ParallaxBackground::set_scroll_base_offset(const Point2& p_ofs) { + + base_offset=p_ofs; + _update_scroll(); +} + +Point2 ParallaxBackground::get_scroll_base_offset() const{ + + return base_offset; +} + +void ParallaxBackground::set_scroll_base_scale(const Point2& p_ofs) { + + base_scale=p_ofs; + _update_scroll(); +} + +Point2 ParallaxBackground::get_scroll_base_scale() const{ + + return base_scale; +} + + +void ParallaxBackground::set_limit_begin(const Point2& p_ofs) { + + limit_begin=p_ofs; + _update_scroll(); +} + +Point2 ParallaxBackground::get_limit_begin() const { + + return limit_begin; +} + +void ParallaxBackground::set_limit_end(const Point2& p_ofs) { + + limit_end=p_ofs; + _update_scroll(); + +} + +Point2 ParallaxBackground::get_limit_end() const { + + return limit_end; +} + +void ParallaxBackground::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("_camera_moved"),&ParallaxBackground::_camera_moved); + ObjectTypeDB::bind_method(_MD("set_scroll_offset","ofs"),&ParallaxBackground::set_scroll_offset); + ObjectTypeDB::bind_method(_MD("get_scroll_offset"),&ParallaxBackground::get_scroll_offset); + ObjectTypeDB::bind_method(_MD("set_scroll_base_offset","ofs"),&ParallaxBackground::set_scroll_base_offset); + ObjectTypeDB::bind_method(_MD("get_scroll_base_offset"),&ParallaxBackground::get_scroll_base_offset); + ObjectTypeDB::bind_method(_MD("set_scroll_base_scale","scale"),&ParallaxBackground::set_scroll_base_scale); + ObjectTypeDB::bind_method(_MD("get_scroll_base_scale"),&ParallaxBackground::get_scroll_base_scale); + ObjectTypeDB::bind_method(_MD("set_limit_begin","ofs"),&ParallaxBackground::set_limit_begin); + ObjectTypeDB::bind_method(_MD("get_limit_begin"),&ParallaxBackground::get_limit_begin); + ObjectTypeDB::bind_method(_MD("set_limit_end","ofs"),&ParallaxBackground::set_limit_end); + ObjectTypeDB::bind_method(_MD("get_limit_end"),&ParallaxBackground::get_limit_end); + + + ADD_PROPERTY( PropertyInfo(Variant::VECTOR2,"scroll/offset"),_SCS("set_scroll_offset"),_SCS("get_scroll_offset")); + ADD_PROPERTY( PropertyInfo(Variant::VECTOR2,"scroll/base_offset"),_SCS("set_scroll_base_offset"),_SCS("get_scroll_base_offset")); + ADD_PROPERTY( PropertyInfo(Variant::VECTOR2,"scroll/base_scale"),_SCS("set_scroll_base_scale"),_SCS("get_scroll_base_scale")); + ADD_PROPERTY( PropertyInfo(Variant::VECTOR2,"scroll/limit_begin"),_SCS("set_limit_begin"),_SCS("get_limit_begin")); + ADD_PROPERTY( PropertyInfo(Variant::VECTOR2,"scroll/limit_end"),_SCS("set_limit_end"),_SCS("get_limit_end")); + +} + + + + + +ParallaxBackground::ParallaxBackground() { + + base_scale=Vector2(1,1); + scale=1.0; + set_layer(-1); //behind all by default +} diff --git a/scene/2d/parallax_background.h b/scene/2d/parallax_background.h new file mode 100644 index 00000000000..ed6747f01c8 --- /dev/null +++ b/scene/2d/parallax_background.h @@ -0,0 +1,78 @@ +/*************************************************************************/ +/* parallax_background.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 PARALLAX_BACKGROUND_H +#define PARALLAX_BACKGROUND_H + +#include "scene/main/canvas_layer.h" +#include "scene/2d/node_2d.h" +#include "scene/2d/camera_2d.h" + +class ParallaxBackground : public CanvasLayer { + + OBJ_TYPE( ParallaxBackground, CanvasLayer ); + + Point2 offset; + float scale; + Point2 base_offset; + Point2 base_scale; + String group_name; + Point2 limit_begin; + Point2 limit_end; + + void _update_scroll(); +protected: + + void _camera_moved(const Matrix32& p_transform); + + void _notification(int p_what); + static void _bind_methods(); +public: + + void set_scroll_offset(const Point2& p_ofs); + Point2 get_scroll_offset() const; + + void set_scroll_scale(float p_ofs); + float get_scroll_scale() const; + + void set_scroll_base_offset(const Point2& p_ofs); + Point2 get_scroll_base_offset() const; + + void set_scroll_base_scale(const Point2& p_ofs); + Point2 get_scroll_base_scale() const; + + void set_limit_begin(const Point2& p_ofs); + Point2 get_limit_begin() const; + + void set_limit_end(const Point2& p_ofs); + Point2 get_limit_end() const; + + ParallaxBackground(); +}; + +#endif // PARALLAX_BACKGROUND_H diff --git a/scene/2d/parallax_layer.cpp b/scene/2d/parallax_layer.cpp new file mode 100644 index 00000000000..2cda51dccb7 --- /dev/null +++ b/scene/2d/parallax_layer.cpp @@ -0,0 +1,138 @@ +/*************************************************************************/ +/* parallax_layer.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 "parallax_layer.h" +#include "parallax_background.h" + +void ParallaxLayer::set_motion_scale(const Size2& p_scale) { + + motion_scale=p_scale; +} + +Size2 ParallaxLayer::get_motion_scale() const { + + return motion_scale; + +} + + +void ParallaxLayer::_update_mirroring() { + + if (!get_parent()) + return; + + ParallaxBackground *pb = get_parent()->cast_to(); + if (pb) { + + RID c = pb->get_world_2d()->get_canvas(); + RID ci = get_canvas_item(); + VisualServer::get_singleton()->canvas_set_item_mirroring(c,ci,mirroring); + } + +} + +void ParallaxLayer::set_mirroring(const Size2& p_mirroring) { + + mirroring=p_mirroring; + _update_mirroring(); + +} + +Size2 ParallaxLayer::get_mirroring() const{ + + return mirroring; +} + + + +void ParallaxLayer::_notification(int p_what) { + + switch(p_what) { + + case NOTIFICATION_ENTER_SCENE: { + + orig_offset=get_pos(); + orig_scale=get_scale(); + _update_mirroring(); + } break; + } +} + +void ParallaxLayer::set_base_offset_and_scale(const Point2& p_offset,float p_scale) { + + if (!is_inside_scene()) + return; + if (get_scene()->is_editor_hint()) + return; + Point2 new_ofs = ((orig_offset+p_offset)*motion_scale)*p_scale; + + if (mirroring.x) { + + while( new_ofs.x>=0) { + new_ofs.x -= mirroring.x*p_scale; + } + while(new_ofs.x < -mirroring.x*p_scale) { + new_ofs.x += mirroring.x*p_scale; + } + } + + if (mirroring.y) { + + while( new_ofs.y>=0) { + new_ofs.y -= mirroring.y*p_scale; + } + while(new_ofs.y < -mirroring.y*p_scale) { + new_ofs.y += mirroring.y*p_scale; + } + } + + + set_pos(new_ofs); + set_scale(Vector2(1,1)*p_scale); + + +} + +void ParallaxLayer::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("set_motion_scale","scale"),&ParallaxLayer::set_motion_scale); + ObjectTypeDB::bind_method(_MD("get_motion_scale"),&ParallaxLayer::get_motion_scale); + ObjectTypeDB::bind_method(_MD("set_mirroring","mirror"),&ParallaxLayer::set_mirroring); + ObjectTypeDB::bind_method(_MD("get_mirroring"),&ParallaxLayer::get_mirroring); + + ADD_PROPERTY( PropertyInfo(Variant::VECTOR2,"motion/scale"),_SCS("set_motion_scale"),_SCS("get_motion_scale")); + ADD_PROPERTY( PropertyInfo(Variant::VECTOR2,"motion/mirroring"),_SCS("set_mirroring"),_SCS("get_mirroring")); + +} + + + +ParallaxLayer::ParallaxLayer() +{ + motion_scale=Size2(1,1); +} diff --git a/scene/2d/parallax_layer.h b/scene/2d/parallax_layer.h new file mode 100644 index 00000000000..fccd8509b1b --- /dev/null +++ b/scene/2d/parallax_layer.h @@ -0,0 +1,62 @@ +/*************************************************************************/ +/* parallax_layer.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 PARALLAX_LAYER_H +#define PARALLAX_LAYER_H + +#include "scene/2d/node_2d.h" + +class ParallaxLayer : public Node2D { + + OBJ_TYPE( ParallaxLayer, Node2D ); + + Point2 orig_offset; + Point2 orig_scale; + Size2 motion_scale; + Vector2 mirroring; + void _update_mirroring(); + +protected: + + void _notification(int p_what); + static void _bind_methods(); + +public: + + void set_motion_scale(const Size2& p_scale); + Size2 get_motion_scale() const; + + void set_mirroring(const Size2& p_mirroring); + Size2 get_mirroring() const; + + void set_base_offset_and_scale(const Point2& p_offsetf,float p_scale); + + ParallaxLayer(); +}; + +#endif // PARALLAX_LAYER_H diff --git a/scene/2d/particles_2d.cpp b/scene/2d/particles_2d.cpp new file mode 100644 index 00000000000..c10f60f78b1 --- /dev/null +++ b/scene/2d/particles_2d.cpp @@ -0,0 +1,1045 @@ +/*************************************************************************/ +/* particles_2d.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 "particles_2d.h" + + + +void ParticleAttractor2D::_notification(int p_what) { + + switch(p_what) { + + case NOTIFICATION_ENTER_SCENE: { + + _update_owner(); + + } break; + case NOTIFICATION_DRAW: { + + if (!get_scene()->is_editor_hint()) + return; + + Vector2 pv; + float dr = MIN(disable_radius,radius); + for(int i=0;i<=32;i++) { + Vector2 v(Math::sin(i/32.0*Math_PI*2),Math::cos(i/32.0*Math_PI*2)); + if (i>0) { + draw_line(pv*radius,v*radius,Color(0,0,0.5,0.9)); + if (dr>0) { + draw_line(pv*dr,v*dr,Color(0.5,0,0.0,0.9)); + } + } + pv=v; + } + + } break; + case NOTIFICATION_EXIT_SCENE: { + if (owner) { + _set_owner(NULL); + } + + } break; + } +} + +void ParticleAttractor2D::_owner_exited() { + + ERR_FAIL_COND(!owner); + owner->attractors.erase(this); + owner=NULL; +} + +void ParticleAttractor2D::_update_owner() { + + if (!is_inside_scene() || !has_node(path)) { + _set_owner(NULL); + return; + } + + Node *n = get_node(path); + ERR_FAIL_COND(!n); + Particles2D *pn = n->cast_to(); + if (!pn) { + _set_owner(NULL); + return; + } + + _set_owner(pn); +} + +void ParticleAttractor2D::_set_owner(Particles2D* p_owner) { + + if (owner==p_owner) + return; + + if (owner) { + owner->disconnect("exit_scene",this,"_owner_exited"); + owner->attractors.erase(this); + owner=NULL; + } + owner=p_owner; + + if (owner) { + + owner->connect("exit_scene",this,"_owner_exited",varray(),CONNECT_ONESHOT); + owner->attractors.insert(this); + } +} + +void ParticleAttractor2D::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("set_enabled","enabled"),&ParticleAttractor2D::set_enabled); + ObjectTypeDB::bind_method(_MD("is_enabled"),&ParticleAttractor2D::is_enabled); + + ObjectTypeDB::bind_method(_MD("set_radius","radius"),&ParticleAttractor2D::set_radius); + ObjectTypeDB::bind_method(_MD("get_radius"),&ParticleAttractor2D::get_radius); + + ObjectTypeDB::bind_method(_MD("set_disable_radius","radius"),&ParticleAttractor2D::set_disable_radius); + ObjectTypeDB::bind_method(_MD("get_disable_radius"),&ParticleAttractor2D::get_disable_radius); + + ObjectTypeDB::bind_method(_MD("set_gravity","gravity"),&ParticleAttractor2D::set_gravity); + ObjectTypeDB::bind_method(_MD("get_gravity"),&ParticleAttractor2D::get_gravity); + + ObjectTypeDB::bind_method(_MD("set_absorption","absorption"),&ParticleAttractor2D::set_absorption); + ObjectTypeDB::bind_method(_MD("get_absorption"),&ParticleAttractor2D::get_absorption); + + ObjectTypeDB::bind_method(_MD("set_particles_path","path"),&ParticleAttractor2D::set_particles_path); + ObjectTypeDB::bind_method(_MD("get_particles_path"),&ParticleAttractor2D::get_particles_path); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL,"enabled"),_SCS("set_enabled"),_SCS("is_enabled")); + ADD_PROPERTY(PropertyInfo(Variant::REAL,"radius",PROPERTY_HINT_RANGE,"0.1,16000,0.1"),_SCS("set_radius"),_SCS("get_radius")); + ADD_PROPERTY(PropertyInfo(Variant::REAL,"disable_radius",PROPERTY_HINT_RANGE,"0.1,16000,0.1"),_SCS("set_disable_radius"),_SCS("get_disable_radius")); + ADD_PROPERTY(PropertyInfo(Variant::REAL,"gravity",PROPERTY_HINT_RANGE,"-512,512,0.01"),_SCS("set_gravity"),_SCS("get_gravity")); + ADD_PROPERTY(PropertyInfo(Variant::REAL,"absorption",PROPERTY_HINT_RANGE,"0,512,0.01"),_SCS("set_absorption"),_SCS("get_absorption")); + ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH,"particles_path",PROPERTY_HINT_RESOURCE_TYPE,"Particles2D"),_SCS("set_particles_path"),_SCS("get_particles_path")); + + + +} + + +void ParticleAttractor2D::set_enabled(bool p_enabled) { + + enabled=p_enabled; +} + +bool ParticleAttractor2D::is_enabled() const{ + + return enabled; +} + +void ParticleAttractor2D::set_radius(float p_radius) { + + radius = p_radius; + update(); +} + +float ParticleAttractor2D::get_radius() const { + + return radius; +} + +void ParticleAttractor2D::set_disable_radius(float p_disable_radius) { + + disable_radius = p_disable_radius; + update(); +} +float ParticleAttractor2D::get_disable_radius() const { + + return disable_radius; +} + +void ParticleAttractor2D::set_gravity(float p_gravity) { + + gravity=p_gravity; + +} +float ParticleAttractor2D::get_gravity() const { + + return gravity; +} + +void ParticleAttractor2D::set_absorption(float p_absorption) { + + absorption=p_absorption; + +} +float ParticleAttractor2D::get_absorption() const { + + return absorption; +} + +void ParticleAttractor2D::set_particles_path(NodePath p_path) { + + path=p_path; + _update_owner(); +} +NodePath ParticleAttractor2D::get_particles_path() const { + + return path; +} + + + +ParticleAttractor2D::ParticleAttractor2D() { + + owner=NULL; + radius=50; + disable_radius=0; + gravity=100; + absorption=0; + path=String(".."); + enabled=true; +} + +/****************************************/ + +_FORCE_INLINE_ static float _rand_from_seed(uint32_t *seed) { + + uint32_t k; + uint32_t s = (*seed); + if (s == 0) + s = 0x12345987; + k = s / 127773; + s = 16807 * (s - k * 127773) - 2836 * k; + if (s < 0) + s += 2147483647; + (*seed) = s; + + float v=((float)((*seed) & 0xFFFFF))/(float)0xFFFFF; + v=v*2.0-1.0; + return v; +} + +void Particles2D::_process_particles(float p_delta) { + + if (particles.size()==0 || lifetime==0) + return; + + p_delta*=time_scale; + + float frame_time=p_delta; + + if (emit_timeout > 0) { + time_to_live -= frame_time; + if (time_to_live < 0) { + + emitting = false; + }; + }; + + float next_time = time+frame_time; + + if (next_time > lifetime) + next_time=Math::fmod(next_time,lifetime); + + + Particle *pdata=&particles[0]; + int particle_count=particles.size(); + Matrix32 xform; + if (!local_space) + xform=get_global_transform(); + + active_count=0; + + DVector::Read r; + int emission_point_count=0; + if (emission_points.size()) { + + emission_point_count=emission_points.size(); + r=emission_points.read(); + } + + int attractor_count=0; + AttractorCache *attractor_ptr=NULL; + + if (attractors.size()) { + if (attractors.size()!=attractor_cache.size()) { + attractor_cache.resize(attractors.size()); + } + + int idx=0; + Matrix32 m; + if (local_space) { + m= get_global_transform().affine_inverse(); + } + for (Set::Element *E=attractors.front();E;E=E->next()) { + + attractor_cache[idx].pos=m.xform( E->get()->get_global_pos() ); + attractor_cache[idx].attractor=E->get(); + idx++; + } + + attractor_ptr=attractor_cache.ptr(); + attractor_count=attractor_cache.size(); + } + + for(int i=0;i time || restart_time < next_time ) + restart=true; + + } else if (restart_time > time && restart_time < next_time ) { + restart=true; + } + + if (restart) { + + + if (emitting) { + + p.pos=emissor_offset; + if (emission_point_count) { + + + Vector2 ep = r[Math::rand()%emission_point_count]; + if (!local_space) { + p.pos=xform.xform(p.pos+ep*extents); + } else { + p.pos+=ep*extents; + } + } else { + if (!local_space) { + p.pos=xform.xform(p.pos+Vector2(Math::random(-extents.x,extents.x),Math::random(-extents.y,extents.y))); + } else { + p.pos+=Vector2(Math::random(-extents.x,extents.x),Math::random(-extents.y,extents.y)); + } + } + p.seed=Math::rand() % 12345678; + uint32_t rand_seed=p.seed*(i+1); + + float angle = Math::deg2rad(param[PARAM_DIRECTION]+_rand_from_seed(&rand_seed)*param[PARAM_SPREAD]); + + p.velocity=Vector2( Math::sin(angle), Math::cos(angle) ); + if (!local_space) { + + p.velocity = xform.basis_xform(p.velocity).normalized(); + } + + p.velocity*=param[PARAM_LINEAR_VELOCITY]+param[PARAM_LINEAR_VELOCITY]*_rand_from_seed(&rand_seed)*randomness[PARAM_LINEAR_VELOCITY]; + p.velocity+=initial_velocity; + p.active=true; + p.rot=0; + active_count++; + + + } else { + + p.active=false; + } + + } else { + + if (!p.active) + continue; + + uint32_t rand_seed=p.seed*(i+1); + + Vector2 force; + + //apply gravity + float gravity_dir = Math::deg2rad( param[PARAM_GRAVITY_DIRECTION]+180*randomness[PARAM_GRAVITY_DIRECTION]*_rand_from_seed(&rand_seed)); + force+=Vector2( Math::sin(gravity_dir), Math::cos(gravity_dir) ) * (param[PARAM_GRAVITY_STRENGTH]+param[PARAM_GRAVITY_STRENGTH]*randomness[PARAM_GRAVITY_STRENGTH]*_rand_from_seed(&rand_seed)); + //apply radial + Vector2 rvec = (p.pos - emissor_offset).normalized(); + force+=rvec*(param[PARAM_RADIAL_ACCEL]+param[PARAM_RADIAL_ACCEL]*randomness[PARAM_RADIAL_ACCEL]*_rand_from_seed(&rand_seed)); + //apply orbit + float orbitvel = (param[PARAM_ORBIT_VELOCITY]+param[PARAM_ORBIT_VELOCITY]*randomness[PARAM_ORBIT_VELOCITY]*_rand_from_seed(&rand_seed)); + if (orbitvel!=0) { + Vector2 rel = p.pos - xform.elements[2]; + Matrix32 rot(orbitvel*frame_time,Vector2()); + p.pos = rot.xform(rel) + xform.elements[2]; + + } + + Vector2 tvec=rvec.tangent(); + force+=tvec*(param[PARAM_TANGENTIAL_ACCEL]+param[PARAM_TANGENTIAL_ACCEL]*randomness[PARAM_TANGENTIAL_ACCEL]*_rand_from_seed(&rand_seed)); + + for(int j=0;jenabled || vl==0 || vl > attractor_ptr[j].attractor->radius) + continue; + + + + force+=vec*attractor_ptr[j].attractor->gravity; + float fvl = p.velocity.length(); + if (fvl && attractor_ptr[j].attractor->absorption) { + Vector2 target = vec.normalized(); + p.velocity = p.velocity.normalized().linear_interpolate(target,MIN(frame_time*attractor_ptr[j].attractor->absorption,1))*fvl; + } + + if (attractor_ptr[j].attractor->disable_radius && vl < attractor_ptr[j].attractor->disable_radius) { + p.active=false; + } + } + + p.velocity+=force*frame_time; + + if (param[PARAM_DAMPING]) { + float dmp = param[PARAM_DAMPING]+param[PARAM_DAMPING]*randomness[PARAM_DAMPING]*_rand_from_seed(&rand_seed); + float v = p.velocity.length(); + v -= dmp * frame_time; + if (v<=0) { + p.velocity=Vector2(); + } else { + p.velocity=p.velocity.normalized() * v; + } + + } + + p.pos+=p.velocity*frame_time; + p.rot+=Math::lerp(param[PARAM_SPIN_VELOCITY],param[PARAM_SPIN_VELOCITY]*randomness[PARAM_SPIN_VELOCITY]*_rand_from_seed(&rand_seed),randomness[PARAM_SPIN_VELOCITY])*frame_time; + + active_count++; + + } + + + } + + + + time=Math::fmod( time+frame_time, lifetime ); + if (!emitting && active_count==0) { + set_process(false); + + } + + update(); + + +} + + +void Particles2D::_notification(int p_what) { + + switch(p_what) { + + case NOTIFICATION_PROCESS: { + + _process_particles( get_process_delta_time() ); + } break; + + case NOTIFICATION_ENTER_SCENE: { + + float ppt=preprocess; + while(ppt>0) { + _process_particles(0.1); + ppt-=0.1; + } + } break; + case NOTIFICATION_DRAW: { + + + if (particles.size()==0 || lifetime==0) + return; + + RID ci=get_canvas_item(); + Size2 size(1,1); + Point2 center; + + if (!texture.is_null()) { + size=texture->get_size(); + } + + + float time_pos=(time/lifetime); + + Particle *pdata=&particles[0]; + int particle_count=particles.size(); + Rect2 r(Point2(),size); + RID texrid; + + if (texture.is_valid()) + texrid = texture->get_rid(); + + Matrix32 invxform; + if (!local_space) + invxform=get_global_transform().affine_inverse(); + + int col_count=0; + float last=-1; + ColorPhase cphase[MAX_COLOR_PHASES]; + + for(int i=0;i ptime) + break; + cpos++; + } + + cpos--; + + Color color; + //could be faster.. + if (cpos==-1) + color=Color(1,1,1,1); + else { + if (cpos==col_count-1) + color=cphase[cpos].color; + else { + float diff = (cphase[cpos+1].pos-cphase[cpos].pos); + if (diff>0) + color=cphase[cpos].color.linear_interpolate(cphase[cpos+1].color, (ptime - cphase[cpos].pos) / diff ); + else + color=cphase[cpos+1].color; + } + } + + + { + float huerand=_rand_from_seed(&rand_seed); + float huerot = param[PARAM_HUE_VARIATION] + randomness[PARAM_HUE_VARIATION] * huerand; + + if (Math::abs(huerot) > CMP_EPSILON) { + + float h=color.get_h(); + float s=color.get_s(); + float v=color.get_v(); + float a=color.a; + //float preh=h; + h+=huerot; + h=Math::abs(Math::fposmod(h,1.0)); + //print_line("rand: "+rtos(randomness[PARAM_HUE_VARIATION])+" rand: "+rtos(huerand)); + //print_line(itos(i)+":hue: "+rtos(preh)+" + "+rtos(huerot)+" = "+rtos(h)); + color.set_hsv(h,s,v); + color.a=a; + } + } + + float initial_size = param[PARAM_INITIAL_SIZE]+param[PARAM_INITIAL_SIZE]*_rand_from_seed(&rand_seed)*randomness[PARAM_FINAL_SIZE]; + float final_size = param[PARAM_FINAL_SIZE]+param[PARAM_FINAL_SIZE]*_rand_from_seed(&rand_seed)*randomness[PARAM_FINAL_SIZE]; + + float size_mult=initial_size*(1.0-ptime) + final_size*ptime; + + //Size2 rectsize=size * size_mult; + //rectsize=rectsize.floor(); + + //Rect2 r = Rect2(Vecto,rectsize); + + Matrix32 xform; + + if (p.rot) { + + xform.set_rotation(p.rot); + xform.translate(-size*size_mult/2.0); + xform.elements[2]+=p.pos; + } else { + xform.elements[2]=-size*size_mult/2.0; + xform.elements[2]+=p.pos; + } + + if (!local_space) { + xform = invxform * xform; + } + + + xform.scale_basis(Size2(size_mult,size_mult)); + + + VisualServer::get_singleton()->canvas_item_add_set_transform(ci,xform); + + + if (texrid.is_valid()) { + + VisualServer::get_singleton()->canvas_item_add_texture_rect(ci,r,texrid,false,color); + } else { + VisualServer::get_singleton()->canvas_item_add_rect(ci,r,color); + + } + + } + + + } break; + + } + +} + +static const char* _particlesframe_property_names[Particles2D::PARAM_MAX]={ + "params/direction", + "params/spread", + "params/linear_velocity", + "params/spin_velocity", + "params/orbit_velocity", + "params/gravity_direction", + "params/gravity_strength", + "params/radial_accel", + "params/tangential_accel", + "params/damping", + "params/initial_size", + "params/final_size", + "params/hue_variation" +}; + +static const char* _particlesframe_property_rnames[Particles2D::PARAM_MAX]={ + "randomness/direction", + "randomness/spread", + "randomness/linear_velocity", + "randomness/spin_velocity", + "randomness/orbit_velocity", + "randomness/gravity_direction", + "randomness/gravity_strength", + "randomness/radial_accel", + "randomness/tangential_accel", + "randomness/damping", + "randomness/initial_size", + "randomness/final_size", + "randomness/hue_variation" +}; + +static const char* _particlesframe_property_ranges[Particles2D::PARAM_MAX]={ + "0,360,0.01", + "0,180,0.01", + "-1024,1024,0.01", + "-1024,1024,0.01", + "-1024,1024,0.01", + "0,360,0.01", + "0,1024,0.01", + "-128,128,0.01", + "-128,128,0.01", + "0,1024,0.001", + "0,1024,0.01", + "0,1024,0.01", + "0,1,0.01" +}; + + +void Particles2D::set_emitting(bool p_emitting) { + + if (emitting==p_emitting) + return; + + if (p_emitting) { + + if (active_count==0) + time=0; + set_process(true); + time_to_live = emit_timeout; + }; + emitting=p_emitting; +} + +bool Particles2D::is_emitting() const { + + return emitting; +} + +void Particles2D::set_amount(int p_amount) { + + ERR_FAIL_INDEX(p_amount,1024); + + particles.resize(p_amount); +} +int Particles2D::get_amount() const { + + return particles.size(); +} + +void Particles2D::set_emit_timeout(float p_timeout) { + + emit_timeout = p_timeout; + time_to_live = p_timeout; +}; + +float Particles2D::get_emit_timeout() const { + + return emit_timeout; +}; + +void Particles2D::set_lifetime(float p_lifetime) { + + ERR_FAIL_INDEX(p_lifetime,3600); + + lifetime=p_lifetime; +} +float Particles2D::get_lifetime() const { + + return lifetime; +} + +void Particles2D::set_time_scale(float p_time_scale) { + + time_scale=p_time_scale; +} +float Particles2D::get_time_scale() const { + + return time_scale; +} + +void Particles2D::set_pre_process_time(float p_pre_process_time) { + + preprocess=p_pre_process_time; +} + +float Particles2D::get_pre_process_time() const{ + + return preprocess; +} + + +void Particles2D::set_param(Parameter p_param, float p_value) { + + ERR_FAIL_INDEX(p_param,PARAM_MAX); + param[p_param]=p_value; +} +float Particles2D::get_param(Parameter p_param) const { + + ERR_FAIL_INDEX_V(p_param,PARAM_MAX,0); + return param[p_param]; +} + +void Particles2D::set_randomness(Parameter p_param, float p_value) { + + ERR_FAIL_INDEX(p_param,PARAM_MAX); + randomness[p_param]=p_value; + +} +float Particles2D::get_randomness(Parameter p_param) const { + + ERR_FAIL_INDEX_V(p_param,PARAM_MAX,0); + return randomness[p_param]; + +} + +void Particles2D::set_texture(const Ref& p_texture) { + + texture=p_texture; +} + +Ref Particles2D::get_texture() const { + + return texture; +} + +void Particles2D::set_emissor_offset(const Point2& p_offset) { + + emissor_offset=p_offset; +} + +Point2 Particles2D::get_emissor_offset() const { + + return emissor_offset; +} + + +void Particles2D::set_use_local_space(bool p_use) { + + local_space=p_use; +} + +bool Particles2D::is_using_local_space() const { + + return local_space; +} + + +void Particles2D::set_color_phases(int p_phases) { + + ERR_FAIL_INDEX(p_phases,MAX_COLOR_PHASES+1); + color_phase_count=p_phases; +} + +int Particles2D::get_color_phases() const { + + return color_phase_count; +} + +void Particles2D::set_color_phase_color(int p_phase,const Color& p_color) { + + ERR_FAIL_INDEX(p_phase,MAX_COLOR_PHASES); + color_phases[p_phase].color=p_color; + +} +Color Particles2D::get_color_phase_color(int p_phase) const { + + ERR_FAIL_INDEX_V(p_phase,MAX_COLOR_PHASES,Color()); + return color_phases[p_phase].color; +} + +void Particles2D::set_color_phase_pos(int p_phase,float p_pos) { + ERR_FAIL_INDEX(p_phase,MAX_COLOR_PHASES); + ERR_FAIL_COND(p_pos<0.0 || p_pos>1.0); + color_phases[p_phase].pos=p_pos; + +} +float Particles2D::get_color_phase_pos(int p_phase) const { + + ERR_FAIL_INDEX_V(p_phase,MAX_COLOR_PHASES,0); + return color_phases[p_phase].pos; +} + +void Particles2D::set_emission_half_extents(const Vector2& p_extents) { + + extents=p_extents; +} + +Vector2 Particles2D::get_emission_half_extents() const { + + return extents; +} + +void Particles2D::testee(int a, int b, int c, int d, int e) { + + print_line(itos(a)); + print_line(itos(b)); + print_line(itos(c)); + print_line(itos(d)); + print_line(itos(e)); +} + +void Particles2D::set_initial_velocity(const Vector2& p_velocity) { + + + initial_velocity=p_velocity; +} +Vector2 Particles2D::get_initial_velocity() const{ + + return initial_velocity; +} + + +void Particles2D::pre_process(float p_delta) { + + _process_particles(p_delta); +} + + +void Particles2D::set_explosiveness(float p_value) { + + explosiveness=p_value; +} + +float Particles2D::get_explosiveness() const{ + + return explosiveness; +} + +void Particles2D::set_emission_points(const DVector& p_points) { + + emission_points=p_points; +} + +DVector Particles2D::get_emission_points() const{ + + return emission_points; +} + +void Particles2D::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("set_emitting","active"),&Particles2D::set_emitting); + ObjectTypeDB::bind_method(_MD("is_emitting"),&Particles2D::is_emitting); + + ObjectTypeDB::bind_method(_MD("set_amount","amount"),&Particles2D::set_amount); + ObjectTypeDB::bind_method(_MD("get_amount"),&Particles2D::get_amount); + + ObjectTypeDB::bind_method(_MD("set_lifetime","lifetime"),&Particles2D::set_lifetime); + ObjectTypeDB::bind_method(_MD("get_lifetime"),&Particles2D::get_lifetime); + + ObjectTypeDB::bind_method(_MD("set_time_scale","time_scale"),&Particles2D::set_time_scale); + ObjectTypeDB::bind_method(_MD("get_time_scale"),&Particles2D::get_time_scale); + + ObjectTypeDB::bind_method(_MD("set_pre_process_time","time"),&Particles2D::set_pre_process_time); + ObjectTypeDB::bind_method(_MD("get_pre_process_time"),&Particles2D::get_pre_process_time); + + ObjectTypeDB::bind_method(_MD("set_emit_timeout","value"),&Particles2D::set_emit_timeout); + ObjectTypeDB::bind_method(_MD("get_emit_timeout"),&Particles2D::get_emit_timeout); + + ObjectTypeDB::bind_method(_MD("set_param","param","value"),&Particles2D::set_param); + ObjectTypeDB::bind_method(_MD("get_param","param"),&Particles2D::get_param); + + ObjectTypeDB::bind_method(_MD("set_randomness","param","value"),&Particles2D::set_randomness); + ObjectTypeDB::bind_method(_MD("get_randomness","param"),&Particles2D::get_randomness); + + ObjectTypeDB::bind_method(_MD("set_texture:Texture","texture"),&Particles2D::set_texture); + ObjectTypeDB::bind_method(_MD("get_texture:Texture"),&Particles2D::get_texture); + + ObjectTypeDB::bind_method(_MD("set_emissor_offset","offset"),&Particles2D::set_emissor_offset); + ObjectTypeDB::bind_method(_MD("get_emissor_offset"),&Particles2D::get_emissor_offset); + + ObjectTypeDB::bind_method(_MD("set_emission_half_extents","extents"),&Particles2D::set_emission_half_extents); + ObjectTypeDB::bind_method(_MD("get_emission_half_extents"),&Particles2D::get_emission_half_extents); + + ObjectTypeDB::bind_method(_MD("set_color_phases","phases"),&Particles2D::set_color_phases); + ObjectTypeDB::bind_method(_MD("get_color_phases"),&Particles2D::get_color_phases); + + ObjectTypeDB::bind_method(_MD("set_color_phase_color","phase","color"),&Particles2D::set_color_phase_color); + ObjectTypeDB::bind_method(_MD("get_color_phase_color","phase"),&Particles2D::get_color_phase_color); + + ObjectTypeDB::bind_method(_MD("set_color_phase_pos","phase","pos"),&Particles2D::set_color_phase_pos); + ObjectTypeDB::bind_method(_MD("get_color_phase_pos","phase"),&Particles2D::get_color_phase_pos); + + ObjectTypeDB::bind_method(_MD("pre_process","time"),&Particles2D::pre_process); + + ObjectTypeDB::bind_method(_MD("set_use_local_space","enable"),&Particles2D::set_use_local_space); + ObjectTypeDB::bind_method(_MD("is_using_local_space"),&Particles2D::is_using_local_space); + + ObjectTypeDB::bind_method(_MD("set_initial_velocity","velocity"),&Particles2D::set_initial_velocity); + ObjectTypeDB::bind_method(_MD("get_initial_velocity"),&Particles2D::get_initial_velocity); + + ObjectTypeDB::bind_method(_MD("set_explosiveness","amount"),&Particles2D::set_explosiveness); + ObjectTypeDB::bind_method(_MD("get_explosiveness"),&Particles2D::get_explosiveness); + + ObjectTypeDB::bind_method(_MD("set_emission_points","points"),&Particles2D::set_emission_points); + ObjectTypeDB::bind_method(_MD("get_emission_points"),&Particles2D::get_emission_points); + + ADD_PROPERTY(PropertyInfo(Variant::INT,"config/amount",PROPERTY_HINT_EXP_RANGE,"1,1024"),_SCS("set_amount"),_SCS("get_amount") ); + ADD_PROPERTY(PropertyInfo(Variant::REAL,"config/lifetime",PROPERTY_HINT_EXP_RANGE,"0.1,3600,0.1"),_SCS("set_lifetime"),_SCS("get_lifetime") ); + ADD_PROPERTY(PropertyInfo(Variant::REAL,"config/time_scale",PROPERTY_HINT_EXP_RANGE,"0.01,128,0.01"),_SCS("set_time_scale"),_SCS("get_time_scale") ); + ADD_PROPERTY(PropertyInfo(Variant::REAL,"config/preprocess",PROPERTY_HINT_EXP_RANGE,"0.1,3600,0.1"),_SCS("set_pre_process_time"),_SCS("get_pre_process_time") ); + ADD_PROPERTY(PropertyInfo(Variant::REAL,"config/emit_timeout",PROPERTY_HINT_RANGE,"0,3600,0.1"),_SCS("set_emit_timeout"),_SCS("get_emit_timeout") ); + ADD_PROPERTY(PropertyInfo(Variant::BOOL,"config/emitting"),_SCS("set_emitting"),_SCS("is_emitting") ); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2,"config/offset"),_SCS("set_emissor_offset"),_SCS("get_emissor_offset")); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2,"config/half_extents"),_SCS("set_emission_half_extents"),_SCS("get_emission_half_extents")); + ADD_PROPERTY(PropertyInfo(Variant::BOOL,"config/local_space"),_SCS("set_use_local_space"),_SCS("is_using_local_space")); + ADD_PROPERTY(PropertyInfo(Variant::REAL,"config/explosiveness",PROPERTY_HINT_RANGE,"0,1,0.01"),_SCS("set_explosiveness"),_SCS("get_explosiveness")); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT,"config/texture",PROPERTY_HINT_RESOURCE_TYPE,"Texture"),_SCS("set_texture"),_SCS("get_texture")); + + + for(int i=0;i particles; + int color_phase_count; + struct ColorPhase { + Color color; + float pos; + } color_phases[MAX_COLOR_PHASES]; + + struct AttractorCache { + + Vector2 pos; + ParticleAttractor2D *attractor; + }; + + Vector attractor_cache; + + float explosiveness; + float preprocess; + float lifetime; + bool emitting; + bool local_space; + float emit_timeout; + float time_to_live; + float time_scale; + Point2 emissor_offset; + Vector2 initial_velocity; + Vector2 extents; + DVector emission_points; + + float time; + int active_count; + + Ref texture; + + + void testee(int a, int b, int c, int d, int e); + void _process_particles(float p_delta); +friend class ParticleAttractor2D; + + Set attractors; + +protected: + + void _notification(int p_what); + static void _bind_methods(); + +public: + + void set_emitting(bool p_emitting); + bool is_emitting() const; + + void set_amount(int p_amount); + int get_amount() const; + + void set_lifetime(float p_lifetime); + float get_lifetime() const; + + void set_time_scale(float p_time_scale); + float get_time_scale() const; + + void set_pre_process_time(float p_pre_process_time); + float get_pre_process_time() const; + + void set_emit_timeout(float p_timeout); + float get_emit_timeout() const; + + void set_emission_half_extents(const Vector2& p_extents); + Vector2 get_emission_half_extents() const; + + void set_param(Parameter p_param, float p_value); + float get_param(Parameter p_param) const; + + void set_randomness(Parameter p_randomness, float p_value); + float get_randomness(Parameter p_randomness) const; + + void set_explosiveness(float p_value); + float get_explosiveness() const; + + void set_color_phases(int p_phases); + int get_color_phases() const; + + void set_color_phase_color(int p_phase,const Color& p_color); + Color get_color_phase_color(int p_phase) const; + + void set_color_phase_pos(int p_phase,float p_pos); + float get_color_phase_pos(int p_phase) const; + + void set_texture(const Ref& p_texture); + Ref get_texture() const; + + void set_emissor_offset(const Point2& p_offset); + Point2 get_emissor_offset() const; + + void set_use_local_space(bool p_use); + bool is_using_local_space() const; + + void set_initial_velocity(const Vector2& p_velocity); + Vector2 get_initial_velocity() const; + + void set_emission_points(const DVector& p_points); + DVector get_emission_points() const; + + void pre_process(float p_delta); + + Particles2D(); +}; + +VARIANT_ENUM_CAST( Particles2D::Parameter ); + +#endif // PARTICLES_FRAME_H diff --git a/scene/2d/path_2d.cpp b/scene/2d/path_2d.cpp new file mode 100644 index 00000000000..22d56609eea --- /dev/null +++ b/scene/2d/path_2d.cpp @@ -0,0 +1,92 @@ +/*************************************************************************/ +/* path_2d.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 "path_2d.h" + + +void Path2D::_notification(int p_what) { + + if (p_what==NOTIFICATION_DRAW && curve.is_valid() && is_inside_scene() && get_scene()->is_editor_hint()) { + //draw the curve!! + + for(int i=0;iget_point_count();i++) { + + Vector2 prev_p=curve->get_point_pos(i); + + for(int j=1;j<=8;j++) { + + real_t frac = j/8.0; + Vector2 p = curve->interpolate(i,frac); + draw_line(prev_p,p,Color(0.5,0.6,1.0,0.7),2); + prev_p=p; + } + } + } +} + +void Path2D::_curve_changed() { + + + if (is_inside_scene() && get_scene()->is_editor_hint()) + update(); + +} + + +void Path2D::set_curve(const Ref& p_curve) { + + if (curve.is_valid()) { + curve->disconnect("changed",this,"_curve_changed"); + } + + curve=p_curve; + + if (curve.is_valid()) { + curve->connect("changed",this,"_curve_changed"); + } + +} + +Ref Path2D::get_curve() const{ + + return curve; +} + +void Path2D::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("set_curve","curve:Curve2D"),&Path2D::set_curve); + ObjectTypeDB::bind_method(_MD("get_curve:Curve2D","curve"),&Path2D::get_curve); + ObjectTypeDB::bind_method(_MD("_curve_changed"),&Path2D::_curve_changed); + + ADD_PROPERTY( PropertyInfo( Variant::OBJECT, "curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve2D"), _SCS("set_curve"),_SCS("get_curve")); +} + +Path2D::Path2D() { + + set_curve(Ref( memnew( Curve2D ))); //create one by default +} diff --git a/scene/2d/path_2d.h b/scene/2d/path_2d.h new file mode 100644 index 00000000000..f401f9da4c5 --- /dev/null +++ b/scene/2d/path_2d.h @@ -0,0 +1,57 @@ +/*************************************************************************/ +/* path_2d.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 PATH_2D_H +#define PATH_2D_H + +#include "scene/resources/curve.h" +#include "scene/2d/node_2d.h" + +class Path2D : public Node2D { + + OBJ_TYPE( Path2D, Node2D ); + + Ref curve; + + void _curve_changed(); + + +protected: + + void _notification(int p_what); + static void _bind_methods(); +public: + + void set_curve(const Ref& p_curve); + Ref get_curve() const; + + + Path2D(); +}; + +#endif // PATH_2D_H diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp new file mode 100644 index 00000000000..945e50ff511 --- /dev/null +++ b/scene/2d/physics_body_2d.cpp @@ -0,0 +1,794 @@ +/*************************************************************************/ +/* physics_body_2d.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 "physics_body_2d.h" +#include "scene/scene_string_names.h" + +void PhysicsBody2D::_notification(int p_what) { + +/* + switch(p_what) { + + case NOTIFICATION_TRANSFORM_CHANGED: { + + Physics2DServer::get_singleton()->body_set_state(get_rid(),Physics2DServer::BODY_STATE_TRANSFORM,get_global_transform()); + + } break; + } + */ +} + +PhysicsBody2D::PhysicsBody2D(Physics2DServer::BodyMode p_mode) : CollisionObject2D( Physics2DServer::get_singleton()->body_create(p_mode), false) { + + + +} + +void StaticBody2D::set_constant_linear_velocity(const Vector2& p_vel) { + + constant_linear_velocity=p_vel; + Physics2DServer::get_singleton()->body_set_state(get_rid(),Physics2DServer::BODY_STATE_LINEAR_VELOCITY,constant_linear_velocity); + +} + +void StaticBody2D::set_constant_angular_velocity(real_t p_vel) { + + constant_angular_velocity=p_vel; + Physics2DServer::get_singleton()->body_set_state(get_rid(),Physics2DServer::BODY_STATE_ANGULAR_VELOCITY,constant_angular_velocity); +} + +Vector2 StaticBody2D::get_constant_linear_velocity() const { + + return constant_linear_velocity; +} +real_t StaticBody2D::get_constant_angular_velocity() const { + + return constant_angular_velocity; +} + + +void StaticBody2D::_state_notify(Object *p_object) { + + if (!pre_xform) + return; + + Physics2DDirectBodyState *p2d = (Physics2DDirectBodyState*)p_object; + setting=true; + + Matrix32 new_xform = p2d->get_transform(); + *pre_xform=new_xform; + set_block_transform_notify(true); + set_global_transform(new_xform); + set_block_transform_notify(false); + + setting=false; + + +} + +void StaticBody2D::_update_xform() { + + if (!pre_xform || !pending) + return; + + setting=true; + + + Matrix32 new_xform = get_global_transform(); //obtain the new one + + set_block_transform_notify(true); + Physics2DServer::get_singleton()->body_set_state(get_rid(),Physics2DServer::BODY_STATE_TRANSFORM,*pre_xform); //then simulate motion! + set_global_transform(*pre_xform); //but restore state to previous one in both visual and physics + set_block_transform_notify(false); + + Physics2DServer::get_singleton()->body_static_simulate_motion(get_rid(),new_xform); //then simulate motion! + + setting=false; + pending=false; + +} + +void StaticBody2D::_notification(int p_what) { + + switch(p_what) { + + case NOTIFICATION_ENTER_SCENE: { + + if (pre_xform) + *pre_xform = get_global_transform(); + pending=false; + } break; + case NOTIFICATION_TRANSFORM_CHANGED: { + + if (simulating_motion && !pending && is_inside_scene() && !setting && !get_scene()->is_editor_hint()) { + + + call_deferred(SceneStringNames::get_singleton()->_update_xform); + pending=true; + } + + } break; + } + + +} + +void StaticBody2D::set_simulate_motion(bool p_enable) { + + if (p_enable==simulating_motion) + return; + simulating_motion=p_enable; + + if (p_enable) { + pre_xform = memnew( Matrix32 ); + if (is_inside_scene()) + *pre_xform=get_transform(); +// query = Physics2DServer::get_singleton()->query_create(this,"_state_notify",Variant()); + // Physics2DServer::get_singleton()->query_body_direct_state(query,get_rid()); + Physics2DServer::get_singleton()->body_set_force_integration_callback(get_rid(),this,"_state_notify"); + + } else { + memdelete( pre_xform ); + pre_xform=NULL; + Physics2DServer::get_singleton()->body_set_force_integration_callback(get_rid(),NULL,StringName()); + pending=false; + } +} + +bool StaticBody2D::is_simulating_motion() const { + + return simulating_motion; +} + + +void StaticBody2D::set_friction(real_t p_friction){ + + ERR_FAIL_COND(p_friction<0 || p_friction>1); + + friction=p_friction; + Physics2DServer::get_singleton()->body_set_param(get_rid(),Physics2DServer::BODY_PARAM_FRICTION,friction); + +} +real_t StaticBody2D::get_friction() const{ + + return friction; +} + +void StaticBody2D::set_bounce(real_t p_bounce){ + + ERR_FAIL_COND(p_bounce<0 || p_bounce>1); + + bounce=p_bounce; + Physics2DServer::get_singleton()->body_set_param(get_rid(),Physics2DServer::BODY_PARAM_BOUNCE,bounce); + +} +real_t StaticBody2D::get_bounce() const{ + + return bounce; +} + +void StaticBody2D::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("set_simulate_motion","enabled"),&StaticBody2D::set_simulate_motion); + ObjectTypeDB::bind_method(_MD("is_simulating_motion"),&StaticBody2D::is_simulating_motion); + ObjectTypeDB::bind_method(_MD("_update_xform"),&StaticBody2D::_update_xform); + ObjectTypeDB::bind_method(_MD("_state_notify"),&StaticBody2D::_state_notify); + ObjectTypeDB::bind_method(_MD("set_constant_linear_velocity","vel"),&StaticBody2D::set_constant_linear_velocity); + ObjectTypeDB::bind_method(_MD("set_constant_angular_velocity","vel"),&StaticBody2D::set_constant_angular_velocity); + ObjectTypeDB::bind_method(_MD("get_constant_linear_velocity"),&StaticBody2D::get_constant_linear_velocity); + ObjectTypeDB::bind_method(_MD("get_constant_angular_velocity"),&StaticBody2D::get_constant_angular_velocity); + ObjectTypeDB::bind_method(_MD("set_friction","friction"),&StaticBody2D::set_friction); + ObjectTypeDB::bind_method(_MD("get_friction"),&StaticBody2D::get_friction); + + ObjectTypeDB::bind_method(_MD("set_bounce","bounce"),&StaticBody2D::set_bounce); + ObjectTypeDB::bind_method(_MD("get_bounce"),&StaticBody2D::get_bounce); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL,"simulate_motion"),_SCS("set_simulate_motion"),_SCS("is_simulating_motion")); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2,"constant_linear_velocity"),_SCS("set_constant_linear_velocity"),_SCS("get_constant_linear_velocity")); + ADD_PROPERTY(PropertyInfo(Variant::REAL,"constant_angular_velocity"),_SCS("set_constant_angular_velocity"),_SCS("get_constant_angular_velocity")); + ADD_PROPERTY( PropertyInfo(Variant::REAL,"friction",PROPERTY_HINT_RANGE,"0,1,0.01"),_SCS("set_friction"),_SCS("get_friction")); + ADD_PROPERTY( PropertyInfo(Variant::REAL,"bounce",PROPERTY_HINT_RANGE,"0,1,0.01"),_SCS("set_bounce"),_SCS("get_bounce")); +} + +StaticBody2D::StaticBody2D() : PhysicsBody2D(Physics2DServer::BODY_MODE_STATIC) { + + simulating_motion=false; + pre_xform=NULL; + setting=false; + pending=false; + constant_angular_velocity=0; + bounce=0; + friction=1; + + +} + +StaticBody2D::~StaticBody2D() { + + if (pre_xform) + memdelete(pre_xform); + //if (query.is_valid()) + // Physics2DServer::get_singleton()->free(query); +} + + + + +void RigidBody2D::_body_enter_scene(ObjectID p_id) { + + Object *obj = ObjectDB::get_instance(p_id); + Node *node = obj ? obj->cast_to() : NULL; + ERR_FAIL_COND(!node); + + Map::Element *E=contact_monitor->body_map.find(p_id); + ERR_FAIL_COND(!E); + ERR_FAIL_COND(E->get().in_scene); + + E->get().in_scene=true; + emit_signal(SceneStringNames::get_singleton()->body_enter,node); + + for(int i=0;iget().shapes.size();i++) { + + emit_signal(SceneStringNames::get_singleton()->body_enter_shape,p_id,node,E->get().shapes[i].body_shape,E->get().shapes[i].local_shape); + } + +} + +void RigidBody2D::_body_exit_scene(ObjectID p_id) { + + Object *obj = ObjectDB::get_instance(p_id); + Node *node = obj ? obj->cast_to() : NULL; + ERR_FAIL_COND(!node); + Map::Element *E=contact_monitor->body_map.find(p_id); + ERR_FAIL_COND(!E); + ERR_FAIL_COND(!E->get().in_scene); + E->get().in_scene=false; + emit_signal(SceneStringNames::get_singleton()->body_exit,node); + for(int i=0;iget().shapes.size();i++) { + + emit_signal(SceneStringNames::get_singleton()->body_exit_shape,p_id,node,E->get().shapes[i].body_shape,E->get().shapes[i].local_shape); + } +} + +void RigidBody2D::_body_inout(int p_status, ObjectID p_instance, int p_body_shape,int p_local_shape) { + + bool body_in = p_status==1; + ObjectID objid=p_instance; + + Object *obj = ObjectDB::get_instance(objid); + Node *node = obj ? obj->cast_to() : NULL; + + Map::Element *E=contact_monitor->body_map.find(objid); + + ERR_FAIL_COND(!body_in && !E); + + if (body_in) { + if (!E) { + + E = contact_monitor->body_map.insert(objid,BodyState()); + E->get().rc=0; + E->get().in_scene=node && node->is_inside_scene(); + if (node) { + node->connect(SceneStringNames::get_singleton()->enter_scene,this,SceneStringNames::get_singleton()->_body_enter_scene,make_binds(objid)); + node->connect(SceneStringNames::get_singleton()->exit_scene,this,SceneStringNames::get_singleton()->_body_exit_scene,make_binds(objid)); + if (E->get().in_scene) { + emit_signal(SceneStringNames::get_singleton()->body_enter,node); + } + } + + } + E->get().rc++; + if (node) + E->get().shapes.insert(ShapePair(p_body_shape,p_local_shape)); + + + if (E->get().in_scene) { + emit_signal(SceneStringNames::get_singleton()->body_enter_shape,objid,node,p_body_shape,p_local_shape); + } + + } else { + + E->get().rc--; + + if (node) + E->get().shapes.erase(ShapePair(p_body_shape,p_local_shape)); + + if (E->get().rc==0) { + + if (node) { + node->disconnect(SceneStringNames::get_singleton()->enter_scene,this,SceneStringNames::get_singleton()->_body_enter_scene); + node->disconnect(SceneStringNames::get_singleton()->exit_scene,this,SceneStringNames::get_singleton()->_body_exit_scene); + if (E->get().in_scene) + emit_signal(SceneStringNames::get_singleton()->body_exit,obj); + + } + + contact_monitor->body_map.erase(E); + } + if (node && E->get().in_scene) { + emit_signal(SceneStringNames::get_singleton()->body_exit_shape,objid,obj,p_body_shape,p_local_shape); + } + + } + +} + + +struct _RigidBody2DInOut { + + ObjectID id; + int shape; + int local_shape; +}; + +void RigidBody2D::_direct_state_changed(Object *p_state) { + + //eh.. fuck +#ifdef DEBUG_ENABLED + + state=p_state->cast_to(); +#else + state=(Physics2DDirectBodyState*)p_state; //trust it +#endif + + if (contact_monitor) { + + //untag all + int rc=0; + for( Map::Element *E=contact_monitor->body_map.front();E;E=E->next()) { + + for(int i=0;iget().shapes.size();i++) { + + E->get().shapes[i].tagged=false; + rc++; + } + } + + _RigidBody2DInOut *toadd=(_RigidBody2DInOut*)alloca(state->get_contact_count()*sizeof(_RigidBody2DInOut)); + int toadd_count=0;//state->get_contact_count(); + RigidBody2D_RemoveAction *toremove=(RigidBody2D_RemoveAction*)alloca(rc*sizeof(RigidBody2D_RemoveAction)); + int toremove_count=0; + + //put the ones to add + + for(int i=0;iget_contact_count();i++) { + + ObjectID obj = state->get_contact_collider_id(i); + int local_shape = state->get_contact_local_shape(i); + int shape = state->get_contact_collider_shape(i); + toadd[i].local_shape=local_shape; + toadd[i].id=obj; + toadd[i].shape=shape; + + bool found=false; + + Map::Element *E=contact_monitor->body_map.find(obj); + if (!E) { + toadd_count++; + continue; + } + + ShapePair sp( shape,local_shape ); + int idx = E->get().shapes.find(sp); + if (idx==-1) { + + toadd_count++; + continue; + } + + E->get().shapes[idx].tagged=true; + } + + //put the ones to remove + + for( Map::Element *E=contact_monitor->body_map.front();E;E=E->next()) { + + for(int i=0;iget().shapes.size();i++) { + + if (!E->get().shapes[i].tagged) { + + toremove[toremove_count].body_id=E->key(); + toremove[toremove_count].pair=E->get().shapes[i]; + toremove_count++; + } + } + } + + + //process remotions + + for(int i=0;iget_transform()); + linear_velocity=state->get_linear_velocity(); + angular_velocity=state->get_angular_velocity(); + active=!state->is_sleeping(); + if (get_script_instance()) + get_script_instance()->call("_integrate_forces",state); + set_block_transform_notify(false); // want it back + + state=NULL; +} + +void RigidBody2D::_notification(int p_what) { + + +} + +void RigidBody2D::set_mode(Mode p_mode) { + + mode=p_mode; + switch(p_mode) { + + case MODE_RIGID: { + + Physics2DServer::get_singleton()->body_set_mode(get_rid(),Physics2DServer::BODY_MODE_RIGID); + } break; + case MODE_STATIC: { + + Physics2DServer::get_singleton()->body_set_mode(get_rid(),Physics2DServer::BODY_MODE_STATIC); + + } break; + case MODE_STATIC_ACTIVE: { + + Physics2DServer::get_singleton()->body_set_mode(get_rid(),Physics2DServer::BODY_MODE_STATIC_ACTIVE); + + } break; + case MODE_CHARACTER: { + Physics2DServer::get_singleton()->body_set_mode(get_rid(),Physics2DServer::BODY_MODE_CHARACTER); + + } break; + + } +} + +RigidBody2D::Mode RigidBody2D::get_mode() const{ + + return mode; +} + +void RigidBody2D::set_mass(real_t p_mass){ + + ERR_FAIL_COND(p_mass<=0); + mass=p_mass; + _change_notify("mass"); + _change_notify("weight"); + Physics2DServer::get_singleton()->body_set_param(get_rid(),Physics2DServer::BODY_PARAM_MASS,mass); + +} +real_t RigidBody2D::get_mass() const{ + + return mass; +} + +void RigidBody2D::set_weight(real_t p_weight){ + + set_mass(p_weight/9.8); +} +real_t RigidBody2D::get_weight() const{ + + return mass*9.8; +} + + +void RigidBody2D::set_friction(real_t p_friction){ + + ERR_FAIL_COND(p_friction<0 || p_friction>1); + + friction=p_friction; + Physics2DServer::get_singleton()->body_set_param(get_rid(),Physics2DServer::BODY_PARAM_FRICTION,friction); + +} +real_t RigidBody2D::get_friction() const{ + + return friction; +} + +void RigidBody2D::set_bounce(real_t p_bounce){ + + ERR_FAIL_COND(p_bounce<0 || p_bounce>1); + + bounce=p_bounce; + Physics2DServer::get_singleton()->body_set_param(get_rid(),Physics2DServer::BODY_PARAM_BOUNCE,bounce); + +} +real_t RigidBody2D::get_bounce() const{ + + return bounce; +} + +void RigidBody2D::set_axis_velocity(const Vector2& p_axis) { + + Vector2 v = state? state->get_linear_velocity() : linear_velocity; + Vector2 axis = p_axis.normalized(); + v-=axis*axis.dot(v); + v+=p_axis; + if (state) { + set_linear_velocity(v); + } else { + Physics2DServer::get_singleton()->body_set_axis_velocity(get_rid(),p_axis); + linear_velocity=v; + } +} + +void RigidBody2D::set_linear_velocity(const Vector2& p_velocity){ + + linear_velocity=p_velocity; + if (state) + state->set_linear_velocity(linear_velocity); + else { + + Physics2DServer::get_singleton()->body_set_state(get_rid(),Physics2DServer::BODY_STATE_LINEAR_VELOCITY,linear_velocity); + } + +} + +Vector2 RigidBody2D::get_linear_velocity() const{ + + return linear_velocity; +} + +void RigidBody2D::set_angular_velocity(real_t p_velocity){ + + angular_velocity=p_velocity; + if (state) + state->set_angular_velocity(angular_velocity); + else + Physics2DServer::get_singleton()->body_set_state(get_rid(),Physics2DServer::BODY_STATE_ANGULAR_VELOCITY,angular_velocity); +} +real_t RigidBody2D::get_angular_velocity() const{ + + return angular_velocity; +} + +void RigidBody2D::set_use_custom_integrator(bool p_enable){ + + if (custom_integrator==p_enable) + return; + + custom_integrator=p_enable; + Physics2DServer::get_singleton()->body_set_omit_force_integration(get_rid(),p_enable); + + +} +bool RigidBody2D::is_using_custom_integrator(){ + + return custom_integrator; +} + +void RigidBody2D::set_active(bool p_active) { + + active=p_active; + Physics2DServer::get_singleton()->body_set_state(get_rid(),Physics2DServer::BODY_STATE_SLEEPING,!active); + +} + +void RigidBody2D::set_can_sleep(bool p_active) { + + can_sleep=p_active; + Physics2DServer::get_singleton()->body_set_state(get_rid(),Physics2DServer::BODY_STATE_CAN_SLEEP,p_active); +} + +bool RigidBody2D::is_able_to_sleep() const { + + return can_sleep; +} + +bool RigidBody2D::is_active() const { + + return active; +} + +void RigidBody2D::set_max_contacts_reported(int p_amount) { + + max_contacts_reported=p_amount; + Physics2DServer::get_singleton()->body_set_max_contacts_reported(get_rid(),p_amount); +} + +int RigidBody2D::get_max_contacts_reported() const{ + + return max_contacts_reported; +} + +void RigidBody2D::apply_impulse(const Vector2& p_pos, const Vector2& p_impulse) { + + Physics2DServer::get_singleton()->body_apply_impulse(get_rid(),p_pos,p_impulse); +} + +void RigidBody2D::set_applied_force(const Vector2& p_force) { + + Physics2DServer::get_singleton()->body_set_applied_force(get_rid(), p_force); +}; + +Vector2 RigidBody2D::get_applied_force() const { + + return Physics2DServer::get_singleton()->body_get_applied_force(get_rid()); +}; + +void RigidBody2D::set_use_continuous_collision_detection(bool p_enable) { + + ccd=p_enable; + Physics2DServer::get_singleton()->body_set_enable_continuous_collision_detection(get_rid(),p_enable); +} + +bool RigidBody2D::is_using_continuous_collision_detection() const { + + + return ccd; +} + + +void RigidBody2D::set_contact_monitor(bool p_enabled) { + + if (p_enabled==is_contact_monitor_enabled()) + return; + + if (!p_enabled) { + + for(Map::Element *E=contact_monitor->body_map.front();E;E=E->next()) { + + //clean up mess + } + + memdelete( contact_monitor ); + contact_monitor=NULL; + } else { + + contact_monitor = memnew( ContactMonitor ); + } + +} + +bool RigidBody2D::is_contact_monitor_enabled() const { + + return contact_monitor!=NULL; +} + + + +void RigidBody2D::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("set_mode","mode"),&RigidBody2D::set_mode); + ObjectTypeDB::bind_method(_MD("get_mode"),&RigidBody2D::get_mode); + + ObjectTypeDB::bind_method(_MD("set_mass","mass"),&RigidBody2D::set_mass); + ObjectTypeDB::bind_method(_MD("get_mass"),&RigidBody2D::get_mass); + + ObjectTypeDB::bind_method(_MD("set_weight","weight"),&RigidBody2D::set_weight); + ObjectTypeDB::bind_method(_MD("get_weight"),&RigidBody2D::get_weight); + + ObjectTypeDB::bind_method(_MD("set_friction","friction"),&RigidBody2D::set_friction); + ObjectTypeDB::bind_method(_MD("get_friction"),&RigidBody2D::get_friction); + + ObjectTypeDB::bind_method(_MD("set_bounce","bounce"),&RigidBody2D::set_bounce); + ObjectTypeDB::bind_method(_MD("get_bounce"),&RigidBody2D::get_bounce); + + ObjectTypeDB::bind_method(_MD("set_linear_velocity","linear_velocity"),&RigidBody2D::set_linear_velocity); + ObjectTypeDB::bind_method(_MD("get_linear_velocity"),&RigidBody2D::get_linear_velocity); + + ObjectTypeDB::bind_method(_MD("set_angular_velocity","angular_velocity"),&RigidBody2D::set_angular_velocity); + ObjectTypeDB::bind_method(_MD("get_angular_velocity"),&RigidBody2D::get_angular_velocity); + + ObjectTypeDB::bind_method(_MD("set_max_contacts_reported","amount"),&RigidBody2D::set_max_contacts_reported); + ObjectTypeDB::bind_method(_MD("get_max_contacts_reported"),&RigidBody2D::get_max_contacts_reported); + + ObjectTypeDB::bind_method(_MD("set_use_custom_integrator","enable"),&RigidBody2D::set_use_custom_integrator); + ObjectTypeDB::bind_method(_MD("is_using_custom_integrator"),&RigidBody2D::is_using_custom_integrator); + + ObjectTypeDB::bind_method(_MD("set_contact_monitor","enabled"),&RigidBody2D::set_contact_monitor); + ObjectTypeDB::bind_method(_MD("is_contact_monitor_enabled"),&RigidBody2D::is_contact_monitor_enabled); + + ObjectTypeDB::bind_method(_MD("set_use_continuous_collision_detection","enable"),&RigidBody2D::set_use_continuous_collision_detection); + ObjectTypeDB::bind_method(_MD("is_using_continuous_collision_detection"),&RigidBody2D::is_using_continuous_collision_detection); + + ObjectTypeDB::bind_method(_MD("set_axis_velocity","axis_velocity"),&RigidBody2D::set_axis_velocity); + ObjectTypeDB::bind_method(_MD("apply_impulse","pos","impulse"),&RigidBody2D::apply_impulse); + + ObjectTypeDB::bind_method(_MD("set_applied_force","force"),&RigidBody2D::set_applied_force); + ObjectTypeDB::bind_method(_MD("get_applied_force"),&RigidBody2D::get_applied_force); + + ObjectTypeDB::bind_method(_MD("set_active","active"),&RigidBody2D::set_active); + ObjectTypeDB::bind_method(_MD("is_active"),&RigidBody2D::is_active); + + ObjectTypeDB::bind_method(_MD("set_can_sleep","able_to_sleep"),&RigidBody2D::set_can_sleep); + ObjectTypeDB::bind_method(_MD("is_able_to_sleep"),&RigidBody2D::is_able_to_sleep); + + ObjectTypeDB::bind_method(_MD("_direct_state_changed"),&RigidBody2D::_direct_state_changed); + ObjectTypeDB::bind_method(_MD("_body_enter_scene"),&RigidBody2D::_body_enter_scene); + ObjectTypeDB::bind_method(_MD("_body_exit_scene"),&RigidBody2D::_body_exit_scene); + + BIND_VMETHOD(MethodInfo("_integrate_forces",PropertyInfo(Variant::OBJECT,"state:Physics2DDirectBodyState"))); + + ADD_PROPERTY( PropertyInfo(Variant::INT,"mode",PROPERTY_HINT_ENUM,"Rigid,Static,Character,Static Active"),_SCS("set_mode"),_SCS("get_mode")); + ADD_PROPERTY( PropertyInfo(Variant::REAL,"mass",PROPERTY_HINT_EXP_RANGE,"0.01,65535,0.01"),_SCS("set_mass"),_SCS("get_mass")); + ADD_PROPERTY( PropertyInfo(Variant::REAL,"weight",PROPERTY_HINT_EXP_RANGE,"0.01,65535,0.01",PROPERTY_USAGE_EDITOR),_SCS("set_weight"),_SCS("get_weight")); + ADD_PROPERTY( PropertyInfo(Variant::REAL,"friction",PROPERTY_HINT_RANGE,"0,1,0.01"),_SCS("set_friction"),_SCS("get_friction")); + ADD_PROPERTY( PropertyInfo(Variant::REAL,"bounce",PROPERTY_HINT_RANGE,"0,1,0.01"),_SCS("set_bounce"),_SCS("get_bounce")); + ADD_PROPERTY( PropertyInfo(Variant::BOOL,"custom_integrator"),_SCS("set_use_custom_integrator"),_SCS("is_using_custom_integrator")); + ADD_PROPERTY( PropertyInfo(Variant::BOOL,"continuous_cd"),_SCS("set_use_continuous_collision_detection"),_SCS("is_using_continuous_collision_detection")); + ADD_PROPERTY( PropertyInfo(Variant::INT,"contacts_reported"),_SCS("set_max_contacts_reported"),_SCS("get_max_contacts_reported")); + ADD_PROPERTY( PropertyInfo(Variant::BOOL,"contact_monitor"),_SCS("set_contact_monitor"),_SCS("is_contact_monitor_enabled")); + ADD_PROPERTY( PropertyInfo(Variant::BOOL,"active"),_SCS("set_active"),_SCS("is_active")); + ADD_PROPERTY( PropertyInfo(Variant::BOOL,"can_sleep"),_SCS("set_can_sleep"),_SCS("is_able_to_sleep")); + ADD_PROPERTY( PropertyInfo(Variant::VECTOR2,"velocity/linear"),_SCS("set_linear_velocity"),_SCS("get_linear_velocity")); + ADD_PROPERTY( PropertyInfo(Variant::REAL,"velocity/angular"),_SCS("set_angular_velocity"),_SCS("get_angular_velocity")); + + ADD_SIGNAL( MethodInfo("body_enter_shape",PropertyInfo(Variant::INT,"body_id"),PropertyInfo(Variant::OBJECT,"body"),PropertyInfo(Variant::INT,"body_shape"),PropertyInfo(Variant::INT,"local_shape"))); + ADD_SIGNAL( MethodInfo("body_exit_shape",PropertyInfo(Variant::INT,"body_id"),PropertyInfo(Variant::OBJECT,"body"),PropertyInfo(Variant::INT,"body_shape"),PropertyInfo(Variant::INT,"local_shape"))); + ADD_SIGNAL( MethodInfo("body_enter",PropertyInfo(Variant::OBJECT,"body"))); + ADD_SIGNAL( MethodInfo("body_exit",PropertyInfo(Variant::OBJECT,"body"))); + + BIND_CONSTANT( MODE_STATIC ); + BIND_CONSTANT( MODE_STATIC_ACTIVE ); + BIND_CONSTANT( MODE_RIGID ); + BIND_CONSTANT( MODE_CHARACTER ); +} + +RigidBody2D::RigidBody2D() : PhysicsBody2D(Physics2DServer::BODY_MODE_RIGID) { + + mode=MODE_RIGID; + + bounce=0; + mass=1; + friction=1; + max_contacts_reported=0; + state=NULL; + + angular_velocity=0; + active=true; + ccd=false; + + custom_integrator=false; + contact_monitor=NULL; + can_sleep=true; + + Physics2DServer::get_singleton()->body_set_force_integration_callback(get_rid(),this,"_direct_state_changed"); +} + +RigidBody2D::~RigidBody2D() { + + if (contact_monitor) + memdelete( contact_monitor ); + + + +} + diff --git a/scene/2d/physics_body_2d.h b/scene/2d/physics_body_2d.h new file mode 100644 index 00000000000..9eff59d8a75 --- /dev/null +++ b/scene/2d/physics_body_2d.h @@ -0,0 +1,232 @@ +/*************************************************************************/ +/* physics_body_2d.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 PHYSICS_BODY_2D_H +#define PHYSICS_BODY_2D_H + +#include "scene/2d/collision_object_2d.h" +#include "servers/physics_2d_server.h" +#include "vset.h" + + +class PhysicsBody2D : public CollisionObject2D { + + OBJ_TYPE(PhysicsBody2D,CollisionObject2D); + +protected: + + void _notification(int p_what); + PhysicsBody2D(Physics2DServer::BodyMode p_mode); +public: + + PhysicsBody2D(); + +}; + +class StaticBody2D : public PhysicsBody2D { + + OBJ_TYPE(StaticBody2D,PhysicsBody2D); + + Matrix32 *pre_xform; + //RID query; + bool setting; + bool pending; + bool simulating_motion; + Vector2 constant_linear_velocity; + real_t constant_angular_velocity; + void _update_xform(); + void _state_notify(Object *p_object); + + real_t bounce; + real_t friction; + + +protected: + + void _notification(int p_what); + static void _bind_methods(); + +public: + + void set_friction(real_t p_friction); + real_t get_friction() const; + + void set_bounce(real_t p_bounce); + real_t get_bounce() const; + + void set_simulate_motion(bool p_enable); + bool is_simulating_motion() const; + + void set_constant_linear_velocity(const Vector2& p_vel); + void set_constant_angular_velocity(real_t p_vel); + + Vector2 get_constant_linear_velocity() const; + real_t get_constant_angular_velocity() const; + + StaticBody2D(); + ~StaticBody2D(); + +}; + +class RigidBody2D : public PhysicsBody2D { + + OBJ_TYPE(RigidBody2D,PhysicsBody2D); +public: + + enum Mode { + MODE_RIGID, + MODE_STATIC, + MODE_CHARACTER, + MODE_STATIC_ACTIVE, + }; +private: + + bool can_sleep; + Physics2DDirectBodyState *state; + Mode mode; + + real_t bounce; + real_t mass; + real_t friction; + + Vector2 linear_velocity; + real_t angular_velocity; + bool active; + bool ccd; + + + int max_contacts_reported; + + bool custom_integrator; + + + struct ShapePair { + + int body_shape; + int local_shape; + bool tagged; + bool operator<(const ShapePair& p_sp) const { + if (body_shape==p_sp.body_shape) + return local_shape < p_sp.local_shape; + else + return body_shape < p_sp.body_shape; + } + + ShapePair() {} + ShapePair(int p_bs, int p_ls) { body_shape=p_bs; local_shape=p_ls; } + }; + struct RigidBody2D_RemoveAction { + + + ObjectID body_id; + ShapePair pair; + + }; + struct BodyState { + + int rc; + bool in_scene; + VSet shapes; + }; + + struct ContactMonitor { + + + Map body_map; + + }; + + + ContactMonitor *contact_monitor; + void _body_enter_scene(ObjectID p_id); + void _body_exit_scene(ObjectID p_id); + + + void _body_inout(int p_status, ObjectID p_instance, int p_body_shape,int p_local_shape); + void _direct_state_changed(Object *p_state); + + +protected: + + void _notification(int p_what); + static void _bind_methods(); +public: + + void set_mode(Mode p_mode); + Mode get_mode() const; + + void set_mass(real_t p_mass); + real_t get_mass() const; + + void set_weight(real_t p_weight); + real_t get_weight() const; + + void set_friction(real_t p_friction); + real_t get_friction() const; + + void set_bounce(real_t p_bounce); + real_t get_bounce() const; + + void set_linear_velocity(const Vector2& p_velocity); + Vector2 get_linear_velocity() const; + + void set_axis_velocity(const Vector2& p_axis); + + void set_angular_velocity(real_t p_velocity); + real_t get_angular_velocity() const; + + void set_use_custom_integrator(bool p_enable); + bool is_using_custom_integrator(); + + void set_active(bool p_active); + bool is_active() const; + + void set_can_sleep(bool p_active); + bool is_able_to_sleep() const; + + void set_contact_monitor(bool p_enabled); + bool is_contact_monitor_enabled() const; + + void set_max_contacts_reported(int p_amount); + int get_max_contacts_reported() const; + + void set_use_continuous_collision_detection(bool p_enable); + bool is_using_continuous_collision_detection() const; + + void apply_impulse(const Vector2& p_pos, const Vector2& p_impulse); + + void set_applied_force(const Vector2& p_force); + Vector2 get_applied_force() const; + + RigidBody2D(); + ~RigidBody2D(); + +}; + +VARIANT_ENUM_CAST(RigidBody2D::Mode); +#endif // PHYSICS_BODY_2D_H diff --git a/scene/2d/position_2d.cpp b/scene/2d/position_2d.cpp new file mode 100644 index 00000000000..ca3be9aa8f7 --- /dev/null +++ b/scene/2d/position_2d.cpp @@ -0,0 +1,65 @@ +/*************************************************************************/ +/* position_2d.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 "position_2d.h" +#include "scene/resources/texture.h" + +void Position2D::_draw_cross() { + + draw_line(Point2(-10,0),Point2(+10,0),Color(1,0.5,0.5)); + draw_line(Point2(0,-10),Point2(0,+10),Color(0.5,1,0.5)); + +} + +Rect2 Position2D::get_item_rect() const { + + return Rect2(Point2(-10,-10),Size2(20,20)); +} + +void Position2D::_notification(int p_what) { + + switch(p_what) { + + case NOTIFICATION_ENTER_SCENE: { + + update(); + } break; + case NOTIFICATION_DRAW: { + if (!is_inside_scene()) + break; + if (get_scene()->is_editor_hint()) + _draw_cross(); + + } break; + } + +} + +Position2D::Position2D() +{ +} diff --git a/scene/2d/position_2d.h b/scene/2d/position_2d.h new file mode 100644 index 00000000000..caabf98eae4 --- /dev/null +++ b/scene/2d/position_2d.h @@ -0,0 +1,49 @@ +/*************************************************************************/ +/* position_2d.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 POSITION_2D_H +#define POSITION_2D_H + + +#include "scene/2d/node_2d.h" + +class Position2D : public Node2D { + + OBJ_TYPE(Position2D,Node2D) + + void _draw_cross(); +protected: + + void _notification(int p_what); +public: + + virtual Rect2 get_item_rect() const; + Position2D(); +}; + +#endif // POSITION_2D_H diff --git a/scene/2d/ray_cast_2d.cpp b/scene/2d/ray_cast_2d.cpp new file mode 100644 index 00000000000..a2c57b8ffb1 --- /dev/null +++ b/scene/2d/ray_cast_2d.cpp @@ -0,0 +1,198 @@ +/*************************************************************************/ +/* ray_cast_2d.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 "ray_cast_2d.h" +#include "servers/physics_2d_server.h" + +void RayCast2D::set_cast_to(const Vector2& p_point) { + + cast_to=p_point; + if (is_inside_scene() && get_scene()->is_editor_hint()) + update(); + +} + +Vector2 RayCast2D::get_cast_to() const{ + + return cast_to; +} + +bool RayCast2D::is_colliding() const{ + + return collided; +} +Object *RayCast2D::get_collider() const{ + + if (against==0) + return NULL; + + return ObjectDB::get_instance(against); +} + +int RayCast2D::get_collider_shape() const { + + return against_shape; +} +Vector2 RayCast2D::get_collision_point() const{ + + return collision_point; +} +Vector2 RayCast2D::get_collision_normal() const{ + + return collision_normal; +} + + +void RayCast2D::set_enabled(bool p_enabled) { + + enabled=p_enabled; + if (is_inside_scene() && !get_scene()->is_editor_hint()) + set_fixed_process(p_enabled); + if (!p_enabled) + collided=false; + +} + + +bool RayCast2D::is_enabled() const { + + + return enabled; +} + + +void RayCast2D::_notification(int p_what) { + + switch(p_what) { + + case NOTIFICATION_ENTER_SCENE: { + + if (enabled && !get_scene()->is_editor_hint()) + set_fixed_process(true); + else + set_fixed_process(false); + + } break; + case NOTIFICATION_EXIT_SCENE: { + + if (enabled) + set_fixed_process(false); + + } break; +#ifdef TOOLS_ENABLED + case NOTIFICATION_DRAW: { + + if (!get_scene()->is_editor_hint()) + break; + Matrix32 xf; + xf.rotate(cast_to.atan2()); + xf.translate(Vector2(0,cast_to.length())); + + //Vector2 tip = Vector2(0,s->get_length()); + Color dcol(0.9,0.2,0.2,0.4); + draw_line(Vector2(),cast_to,dcol,3); + Vector pts; + float tsize=4; + pts.push_back(xf.xform(Vector2(0,tsize))); + pts.push_back(xf.xform(Vector2(0.707*tsize,0))); + pts.push_back(xf.xform(Vector2(-0.707*tsize,0))); + Vector cols; + for(int i=0;i<3;i++) + cols.push_back(dcol); + + draw_primitive(pts,cols,Vector()); //small arrow + + } break; +#endif + + case NOTIFICATION_FIXED_PROCESS: { + + if (!enabled) + break; + + + + Ref w2d = get_world_2d(); + ERR_BREAK( w2d.is_null() ); + + Physics2DDirectSpaceState *dss = Physics2DServer::get_singleton()->space_get_direct_state(w2d->get_space()); + ERR_BREAK( !dss ); + + Matrix32 gt = get_global_transform(); + + Vector2 to = cast_to; + if (to==Vector2()) + to=Vector2(0,0.01); + + Physics2DDirectSpaceState::RayResult rr; + + if (dss->intersect_ray(gt.get_origin(),gt.xform(to),rr)) { + + collided=true; + against=rr.collider_id; + collision_point=rr.position; + collision_normal=rr.normal; + against_shape=rr.shape; + } else { + collided=false; + } + + + + } break; + } +} + +void RayCast2D::_bind_methods() { + + + ObjectTypeDB::bind_method(_MD("set_enabled","enabled"),&RayCast2D::set_enabled); + ObjectTypeDB::bind_method(_MD("is_enabled"),&RayCast2D::is_enabled); + + ObjectTypeDB::bind_method(_MD("set_cast_to","local_point"),&RayCast2D::set_cast_to); + ObjectTypeDB::bind_method(_MD("get_cast_to"),&RayCast2D::get_cast_to); + + ObjectTypeDB::bind_method(_MD("is_colliding"),&RayCast2D::is_colliding); + + ObjectTypeDB::bind_method(_MD("get_collider"),&RayCast2D::get_collider); + ObjectTypeDB::bind_method(_MD("get_collider_shape"),&RayCast2D::get_collider_shape); + ObjectTypeDB::bind_method(_MD("get_collision_point"),&RayCast2D::get_collision_point); + ObjectTypeDB::bind_method(_MD("get_collision_normal"),&RayCast2D::get_collision_normal); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL,"enabled"),_SCS("set_enabled"),_SCS("is_enabled")); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2,"cast_to"),_SCS("set_cast_to"),_SCS("get_cast_to")); +} + +RayCast2D::RayCast2D() { + + enabled=false; + against=0; + collided=false; + against_shape=0; + cast_to=Vector2(0,50); +} diff --git a/scene/2d/ray_cast_2d.h b/scene/2d/ray_cast_2d.h new file mode 100644 index 00000000000..62bcb946a67 --- /dev/null +++ b/scene/2d/ray_cast_2d.h @@ -0,0 +1,68 @@ +/*************************************************************************/ +/* ray_cast_2d.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 RAY_CAST_2D_H +#define RAY_CAST_2D_H + +#include "scene/2d/node_2d.h" + +class RayCast2D : public Node2D { + + OBJ_TYPE(RayCast2D,Node2D); + + + bool enabled; + bool collided; + ObjectID against; + int against_shape; + Vector2 collision_point; + Vector2 collision_normal; + + Vector2 cast_to; +protected: + + void _notification(int p_what); + static void _bind_methods(); +public: + + void set_enabled(bool p_enabled); + bool is_enabled() const; + + void set_cast_to(const Vector2& p_point); + Vector2 get_cast_to() const; + + bool is_colliding() const; + Object *get_collider() const; + int get_collider_shape() const; + Vector2 get_collision_point() const; + Vector2 get_collision_normal() const; + + RayCast2D(); +}; + +#endif // RAY_CAST_2D_H diff --git a/scene/2d/remote_transform_2d.cpp b/scene/2d/remote_transform_2d.cpp new file mode 100644 index 00000000000..80d038f6f80 --- /dev/null +++ b/scene/2d/remote_transform_2d.cpp @@ -0,0 +1,122 @@ +/*************************************************************************/ +/* remote_transform_2d.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 "remote_transform_2d.h" +#include "scene/scene_string_names.h" + +void RemoteTransform2D::_update_cache() { + + cache=0; + if (has_node(remote_node)) { + Node *node = get_node(remote_node); + if (!node || this==node || node->is_a_parent_of(this) || this->is_a_parent_of(node)) { + return; + } + + cache=node->get_instance_ID(); + } +} + +void RemoteTransform2D::_update_remote() { + + + if (!is_inside_scene()) + return; + + if (!cache) + return; + + Object *obj = ObjectDB::get_instance(cache); + if (!obj) + return; + + Node2D *n = obj->cast_to(); + if (!n) + return; + + if (!n->is_inside_scene()) + return; + + //todo make faster + n->set_global_transform(get_global_transform()); + +} + +void RemoteTransform2D::_notification(int p_what) { + + switch(p_what) { + + case NOTIFICATION_READY: { + + _update_cache(); + + } break; + case NOTIFICATION_TRANSFORM_CHANGED: { + if (!is_inside_scene()) + break; + + if (cache) { + + _update_remote(); + + } + + } break; + + } +} + + +void RemoteTransform2D::set_remote_node(const NodePath& p_remote_node) { + + remote_node=p_remote_node; + if (is_inside_scene()) + _update_cache(); +} + +NodePath RemoteTransform2D::get_remote_node() const{ + + return remote_node; +} + + +void RemoteTransform2D::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("set_remote_node","path"),&RemoteTransform2D::set_remote_node); + ObjectTypeDB::bind_method(_MD("get_remote_node"),&RemoteTransform2D::get_remote_node); + + ADD_PROPERTY( PropertyInfo(Variant::NODE_PATH,"remote_path"),_SCS("set_remote_node"),_SCS("get_remote_node")); +} + +RemoteTransform2D::RemoteTransform2D() { + + cache=0; + +} + + diff --git a/scene/2d/remote_transform_2d.h b/scene/2d/remote_transform_2d.h new file mode 100644 index 00000000000..9561e722555 --- /dev/null +++ b/scene/2d/remote_transform_2d.h @@ -0,0 +1,52 @@ +/*************************************************************************/ +/* remote_transform_2d.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. */ +/*************************************************************************/ +#include "scene/2d/node_2d.h" + +class RemoteTransform2D : public Node2D { + + OBJ_TYPE(RemoteTransform2D,Node2D); + + NodePath remote_node; + + ObjectID cache; + + void _update_remote(); + void _update_cache(); + void _node_exited_scene(); +protected: + + static void _bind_methods(); + void _notification(int p_what); +public: + + void set_remote_node(const NodePath& p_remote_node); + NodePath get_remote_node() const; + + RemoteTransform2D(); +}; diff --git a/scene/2d/sample_player_2d.cpp b/scene/2d/sample_player_2d.cpp new file mode 100644 index 00000000000..bc1734ec06a --- /dev/null +++ b/scene/2d/sample_player_2d.cpp @@ -0,0 +1,252 @@ +/*************************************************************************/ +/* sample_player_2d.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 "sample_player_2d.h" + +#include "servers/audio_server.h" +#include "servers/audio_server.h" +#include "servers/spatial_sound_server.h" + + +bool SamplePlayer2D::_set(const StringName& p_name, const Variant& p_value) { + + String name=p_name; + + if (name=="play/play") { + if (library.is_valid()) { + + String what=p_value; + if (what=="") + stop_all(); + else + play(what); + + played_back=what; + } + } else + return false; + + return true; +} + +bool SamplePlayer2D::_get(const StringName& p_name,Variant &r_ret) const { + + + String name=p_name; + + if (name=="play/play") { + r_ret=played_back; + } else + return false; + + return true; +} + +void SamplePlayer2D::_get_property_list(List *p_list) const { + + String en=""; + if (library.is_valid()) { + List samples; + Ref ncl=library; + ncl->get_sample_list(&samples); + for (List::Element *E=samples.front();E;E=E->next()) { + + en+=","; + en+=E->get(); + } + } + + p_list->push_back( PropertyInfo( Variant::STRING, "play/play", PROPERTY_HINT_ENUM, en,PROPERTY_USAGE_EDITOR)); +} + +void SamplePlayer2D::_notification(int p_what) { + + + switch(p_what) { + + case NOTIFICATION_ENTER_SCENE: { + + SpatialSound2DServer::get_singleton()->source_set_polyphony(get_source_rid(),polyphony); + + + } break; + } + +} + +void SamplePlayer2D::set_sample_library(const Ref& p_library) { + + library=p_library; +} + +Ref SamplePlayer2D::get_sample_library() const { + + return library; +} + +void SamplePlayer2D::set_polyphony(int p_voice_count) { + + ERR_FAIL_COND(p_voice_count<0 || p_voice_count>64); + polyphony=p_voice_count; + if (get_source_rid().is_valid()) + SpatialSound2DServer::get_singleton()->source_set_polyphony(get_source_rid(),polyphony); + +} + +int SamplePlayer2D::get_polyphony() const { + + return polyphony; +} + +SamplePlayer2D::VoiceID SamplePlayer2D::play(const String& p_sample,int p_voice) { + + if (!get_source_rid().is_valid()) + return INVALID_VOICE; + if (library.is_null()) + return INVALID_VOICE; + if (!library->has_sample(p_sample)) + return INVALID_VOICE; + Ref sample = library->get_sample(p_sample); + float vol_change = library->sample_get_volume_db(p_sample); + float pitch_change = library->sample_get_pitch_scale(p_sample); + + VoiceID vid = SpatialSound2DServer::get_singleton()->source_play_sample(get_source_rid(),sample->get_rid(),sample->get_mix_rate()*pitch_change,p_voice); + if (vol_change) + SpatialSound2DServer::get_singleton()->source_voice_set_volume_scale_db(get_source_rid(),vid,vol_change); + + + if (random_pitch_scale) { + float ps = Math::random(-random_pitch_scale,random_pitch_scale); + if (ps>0) + ps=1.0+ps; + else + ps=1.0/(1.0-ps); + SpatialSound2DServer::get_singleton()->source_voice_set_pitch_scale(get_source_rid(),vid,ps*pitch_change); + + } + + return vid; +} +//voices +void SamplePlayer2D::voice_set_pitch_scale(VoiceID p_voice, float p_pitch_scale) { + + if (!get_source_rid().is_valid()) + return; + + SpatialSound2DServer::get_singleton()->source_voice_set_pitch_scale(get_source_rid(),p_voice,p_pitch_scale); + +} + +void SamplePlayer2D::voice_set_volume_scale_db(VoiceID p_voice, float p_volume_db) { + + if (!get_source_rid().is_valid()) + return; + SpatialSound2DServer::get_singleton()->source_voice_set_volume_scale_db(get_source_rid(),p_voice,p_volume_db); + +} + +bool SamplePlayer2D::is_voice_active(VoiceID p_voice) const { + + if (!get_source_rid().is_valid()) + return false; + return SpatialSound2DServer::get_singleton()->source_is_voice_active(get_source_rid(),p_voice); + +} + +void SamplePlayer2D::stop_voice(VoiceID p_voice) { + + if (!get_source_rid().is_valid()) + return; + SpatialSound2DServer::get_singleton()->source_stop_voice(get_source_rid(),p_voice); + +} + +void SamplePlayer2D::stop_all() { + + if (!get_source_rid().is_valid()) + return; + + for(int i=0;isource_stop_voice(get_source_rid(),i); + } +} + +void SamplePlayer2D::set_random_pitch_scale(float p_scale) { + random_pitch_scale=p_scale; +} + +float SamplePlayer2D::get_random_pitch_scale() const { + + return random_pitch_scale; +} + + +void SamplePlayer2D::_bind_methods() { + + + ObjectTypeDB::bind_method(_MD("set_sample_library","library:SampleLibrary"),&SamplePlayer2D::set_sample_library); + ObjectTypeDB::bind_method(_MD("get_sample_library:SampleLibrary"),&SamplePlayer2D::get_sample_library); + + ObjectTypeDB::bind_method(_MD("set_polyphony","voices"),&SamplePlayer2D::set_polyphony); + ObjectTypeDB::bind_method(_MD("get_polyphony"),&SamplePlayer2D::get_polyphony); + + ObjectTypeDB::bind_method(_MD("play","sample","voice"),&SamplePlayer2D::play,DEFVAL(NEXT_VOICE)); + //voices,DEV + ObjectTypeDB::bind_method(_MD("voice_set_pitch_scale","voice","ratio"),&SamplePlayer2D::voice_set_pitch_scale); + ObjectTypeDB::bind_method(_MD("voice_set_volume_scale_db","voice","db"),&SamplePlayer2D::voice_set_volume_scale_db); + + ObjectTypeDB::bind_method(_MD("is_voice_active","voice"),&SamplePlayer2D::is_voice_active); + ObjectTypeDB::bind_method(_MD("stop_voice","voice"),&SamplePlayer2D::stop_voice); + ObjectTypeDB::bind_method(_MD("stop_all"),&SamplePlayer2D::stop_all); + + ObjectTypeDB::bind_method(_MD("set_random_pitch_scale","val"),&SamplePlayer2D::set_random_pitch_scale); + ObjectTypeDB::bind_method(_MD("get_random_pitch_scale"),&SamplePlayer2D::get_random_pitch_scale); + + BIND_CONSTANT( INVALID_VOICE ); + BIND_CONSTANT( NEXT_VOICE ); + + ADD_PROPERTY( PropertyInfo( Variant::INT, "config/polyphony", PROPERTY_HINT_RANGE, "1,64,1"),_SCS("set_polyphony"),_SCS("get_polyphony")); + ADD_PROPERTY( PropertyInfo( Variant::OBJECT, "config/samples", PROPERTY_HINT_RESOURCE_TYPE,"SampleLibrary"),_SCS("set_sample_library"),_SCS("get_sample_library")); + ADD_PROPERTY( PropertyInfo( Variant::REAL, "config/pitch_random", PROPERTY_HINT_RESOURCE_TYPE,"SampleLibrary"),_SCS("set_random_pitch_scale"),_SCS("get_random_pitch_scale")); + + +} + + +SamplePlayer2D::SamplePlayer2D() { + + polyphony=1; + random_pitch_scale=0; + +} + +SamplePlayer2D::~SamplePlayer2D() { + + +} diff --git a/scene/2d/sample_player_2d.h b/scene/2d/sample_player_2d.h new file mode 100644 index 00000000000..d04abae244b --- /dev/null +++ b/scene/2d/sample_player_2d.h @@ -0,0 +1,92 @@ +/*************************************************************************/ +/* sample_player_2d.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 SAMPLE_PLAYER_2D_H +#define SAMPLE_PLAYER_2D_H + +#include "scene/2d/sound_player_2d.h" +#include "scene/resources/sample_library.h" + +class SamplePlayer2D : public SoundPlayer2D { + + OBJ_TYPE(SamplePlayer2D,SoundPlayer2D); +public: + + enum { + + INVALID_VOICE=SpatialSoundServer::SOURCE_INVALID_VOICE, + NEXT_VOICE=SpatialSoundServer::SOURCE_NEXT_VOICE + }; + + typedef int VoiceID; + + +private: + + Ref library; + int polyphony; + String played_back; + float random_pitch_scale; + +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 *p_list) const; + + void _notification(int p_what); + + static void _bind_methods(); + +public: + + void set_sample_library(const Ref& p_library); + Ref get_sample_library() const; + + void set_polyphony(int p_voice_count); + int get_polyphony() const; + + VoiceID play(const String& p_sample,int p_voice=NEXT_VOICE); + //voices + void voice_set_pitch_scale(VoiceID p_voice, float p_pitch_scale); + void voice_set_volume_scale_db(VoiceID p_voice, float p_volume_db); + + bool is_voice_active(VoiceID p_voice) const; + void stop_voice(VoiceID p_voice); + void stop_all(); + + void set_random_pitch_scale(float p_scale); + float get_random_pitch_scale() const; + + SamplePlayer2D(); + ~SamplePlayer2D(); + + +}; + +#endif // SAMPLE_PLAYER_2D_H diff --git a/scene/2d/screen_button.cpp b/scene/2d/screen_button.cpp new file mode 100644 index 00000000000..f386dc63e8b --- /dev/null +++ b/scene/2d/screen_button.cpp @@ -0,0 +1,372 @@ +/*************************************************************************/ +/* screen_button.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 "screen_button.h" +#include "os/os.h" +#include "input_map.h" +#include "os/input.h" + +void TouchScreenButton::set_texture(const Ref& p_texture) { + + texture=p_texture; + update(); +} + +Ref TouchScreenButton::get_texture() const{ + + return texture; +} + +void TouchScreenButton::set_texture_pressed(const Ref& p_texture_pressed) { + + texture_pressed=p_texture_pressed; + update(); +} + +Ref TouchScreenButton::get_texture_pressed() const{ + + return texture_pressed; +} + +void TouchScreenButton::set_bitmask(const Ref& p_bitmask){ + + bitmask=p_bitmask; +} + +Ref TouchScreenButton::get_bitmask() const{ + + return bitmask; +} + +void TouchScreenButton::_notification(int p_what) { + + switch(p_what) { + + case NOTIFICATION_DRAW: { + + if (!is_inside_scene()) + return; + if (!get_scene()->is_editor_hint() && !OS::get_singleton()->has_touchscreen_ui_hint() && visibility==VISIBILITY_TOUCHSCREEN_ONLY) + return; + + if (finger_pressed!=-1) { + + if (texture_pressed.is_valid()) + draw_texture(texture_pressed,Point2()); + else if (texture.is_valid()) + draw_texture(texture,Point2()); + + } else { + if (texture.is_valid()) + draw_texture(texture,Point2()); + } + + } break; + case NOTIFICATION_ENTER_SCENE: { + + if (!get_scene()->is_editor_hint() && !OS::get_singleton()->has_touchscreen_ui_hint() && visibility==VISIBILITY_TOUCHSCREEN_ONLY) + return; + update(); + set_process_input(true); + if (action.operator String()!="" && InputMap::get_singleton()->has_action(action)) { + action_id=InputMap::get_singleton()->get_action_id(action); + } else { + action_id=-1; + } + } break; + } +} + + +bool TouchScreenButton::is_pressed() const{ + + return finger_pressed!=-1; +} + +void TouchScreenButton::set_action(const String& p_action) { + + action=p_action; + if (action.operator String()!="" && InputMap::get_singleton()->has_action(action)) { + action_id=InputMap::get_singleton()->get_action_id(action); + } else { + action_id=-1; + } + +} + +String TouchScreenButton::get_action() const { + + return action; +} + +void TouchScreenButton::_input(const InputEvent& p_event) { + + if (!get_scene()) + return; + + if (passby_press) { + + if (p_event.type==InputEvent::SCREEN_TOUCH && !p_event.screen_touch.pressed && finger_pressed==p_event.screen_touch.index) { + + emit_signal("released"); + + if (action_id!=-1) { + + Input::get_singleton()->action_release(action); + InputEvent ie; + ie.type=InputEvent::ACTION; + ie.ID=0; + ie.action.action=action_id; + ie.action.pressed=false; + get_scene()->input_event(ie); + } + finger_pressed=-1; + + update(); + + } + + if ((p_event.type==InputEvent::SCREEN_TOUCH && p_event.screen_touch.pressed)|| p_event.type==InputEvent::SCREEN_DRAG) { + + if (finger_pressed==-1 || p_event.screen_touch.index==finger_pressed) { + + Point2 coord = (get_viewport_transform() * get_global_transform()).affine_inverse().xform(Point2(p_event.screen_touch.x,p_event.screen_touch.y)); + + bool touched=false; + if (bitmask.is_valid()) { + + if (Rect2(Point2(),bitmask->get_size()).has_point(coord)) { + + if (bitmask->get_bit(coord)) + touched=true; + } + } else { + + touched=Rect2(Point2(),texture->get_size()).has_point(coord); + } + + + + if (touched) { + + if (finger_pressed==-1) { + finger_pressed=p_event.screen_touch.index; + //emit change stuff + emit_signal("pressed"); + if (action_id!=-1) { + + Input::get_singleton()->action_press(action); + InputEvent ie; + ie.type=InputEvent::ACTION; + ie.ID=0; + ie.action.action=action_id; + ie.action.pressed=true; + get_scene()->input_event(ie); + } + + update(); + } + + } else { + + if (finger_pressed!=-1) { + + emit_signal("released"); + + if (action_id!=-1) { + + Input::get_singleton()->action_release(action); + InputEvent ie; + ie.type=InputEvent::ACTION; + ie.ID=0; + ie.action.action=action_id; + ie.action.pressed=false; + get_scene()->input_event(ie); + } + finger_pressed=-1; + + update(); + } + } + + } + + + } + + } else { + + if (p_event.type==InputEvent::SCREEN_TOUCH) { + + if (p_event.screen_touch.pressed) { + + if (!is_visible()) + return; + + if (finger_pressed!=-1) + return; //already fingering + + Point2 coord = (get_viewport_transform() * get_global_transform()).affine_inverse().xform(Point2(p_event.screen_touch.x,p_event.screen_touch.y)); + + bool touched=false; + if (bitmask.is_valid()) { + + if (Rect2(Point2(),bitmask->get_size()).has_point(coord)) { + + if (bitmask->get_bit(coord)) + touched=true; + } + } else { + + touched=Rect2(Point2(),texture->get_size()).has_point(coord); + } + + + + if (touched) { + + finger_pressed=p_event.screen_touch.index; + //emit change stuff + emit_signal("pressed"); + if (action_id!=-1) { + + Input::get_singleton()->action_press(action); + InputEvent ie; + ie.type=InputEvent::ACTION; + ie.ID=0; + ie.action.action=action_id; + ie.action.pressed=true; + get_scene()->input_event(ie); + } + update(); + + } + } else { + + + if (p_event.screen_touch.index==finger_pressed) { + //untouch + + emit_signal("released"); + + if (action_id!=-1) { + + Input::get_singleton()->action_release(action); + InputEvent ie; + ie.type=InputEvent::ACTION; + ie.ID=0; + ie.action.action=action_id; + ie.action.pressed=false; + get_scene()->input_event(ie); + } + finger_pressed=-1; + update(); + } + } + } + } +} + +Rect2 TouchScreenButton::get_item_rect() const { + + if (texture.is_null()) + return Rect2(0,0,1,1); + //if (texture.is_null()) + // return CanvasItem::get_item_rect(); + + return Rect2(Size2(),texture->get_size()); +} + + +void TouchScreenButton::set_visibility_mode(VisibilityMode p_mode) { + visibility=p_mode; + update(); +} + +TouchScreenButton::VisibilityMode TouchScreenButton::get_visibility_mode() const { + + return visibility; +} + +void TouchScreenButton::set_passby_press(bool p_enable) { + + passby_press=p_enable; +} + +bool TouchScreenButton::is_passby_press_enabled() const{ + + return passby_press; +} + + + +void TouchScreenButton::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("set_texture","texture"),&TouchScreenButton::set_texture); + ObjectTypeDB::bind_method(_MD("get_texture"),&TouchScreenButton::get_texture); + + ObjectTypeDB::bind_method(_MD("set_texture_pressed","texture_pressed"),&TouchScreenButton::set_texture_pressed); + ObjectTypeDB::bind_method(_MD("get_texture_pressed"),&TouchScreenButton::get_texture_pressed); + + ObjectTypeDB::bind_method(_MD("set_bitmask","bitmask"),&TouchScreenButton::set_bitmask); + ObjectTypeDB::bind_method(_MD("get_bitmask"),&TouchScreenButton::get_bitmask); + + ObjectTypeDB::bind_method(_MD("set_action","action"),&TouchScreenButton::set_action); + ObjectTypeDB::bind_method(_MD("get_action"),&TouchScreenButton::get_action); + + ObjectTypeDB::bind_method(_MD("set_visibility_mode","mode"),&TouchScreenButton::set_visibility_mode); + ObjectTypeDB::bind_method(_MD("get_visibility_mode"),&TouchScreenButton::get_visibility_mode); + + ObjectTypeDB::bind_method(_MD("set_passby_press","enabled"),&TouchScreenButton::set_passby_press); + ObjectTypeDB::bind_method(_MD("is_passby_press_enabled"),&TouchScreenButton::is_passby_press_enabled); + + ObjectTypeDB::bind_method(_MD("is_pressed"),&TouchScreenButton::is_pressed); + + ObjectTypeDB::bind_method(_MD("_input"),&TouchScreenButton::_input); + + ADD_PROPERTY( PropertyInfo(Variant::OBJECT,"normal",PROPERTY_HINT_RESOURCE_TYPE,"Texture"),_SCS("set_texture"),_SCS("get_texture")); + ADD_PROPERTY( PropertyInfo(Variant::OBJECT,"pressed",PROPERTY_HINT_RESOURCE_TYPE,"Texture"),_SCS("set_texture_pressed"),_SCS("get_texture_pressed")); + ADD_PROPERTY( PropertyInfo(Variant::OBJECT,"bitmask",PROPERTY_HINT_RESOURCE_TYPE,"BitMap"),_SCS("set_bitmask"),_SCS("get_bitmask")); + ADD_PROPERTY( PropertyInfo(Variant::BOOL,"passby_press"),_SCS("set_passby_press"),_SCS("is_passby_press_enabled")); + ADD_PROPERTY( PropertyInfo(Variant::STRING,"action"),_SCS("set_action"),_SCS("get_action")); + ADD_PROPERTY( PropertyInfo(Variant::INT,"visibility_mode",PROPERTY_HINT_ENUM,"Always,TouchScreen Only"),_SCS("set_visibility_mode"),_SCS("get_visibility_mode")); + + ADD_SIGNAL( MethodInfo("pressed" ) ); + ADD_SIGNAL( MethodInfo("release" ) ); + + + +} + +TouchScreenButton::TouchScreenButton() { + + finger_pressed=-1; + action_id=-1; + passby_press=false; + visibility=VISIBILITY_ALWAYS; +} diff --git a/scene/2d/screen_button.h b/scene/2d/screen_button.h new file mode 100644 index 00000000000..7305fd29fea --- /dev/null +++ b/scene/2d/screen_button.h @@ -0,0 +1,95 @@ +/*************************************************************************/ +/* screen_button.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 SCREEN_BUTTON_H +#define SCREEN_BUTTON_H + +#include "scene/2d/node_2d.h" +#include "scene/resources/texture.h" +#include "scene/resources/bit_mask.h" + +class TouchScreenButton : public Node2D { + + OBJ_TYPE(TouchScreenButton,Node2D); +public: + + enum VisibilityMode { + VISIBILITY_ALWAYS, + VISIBILITY_TOUCHSCREEN_ONLY + }; + +private: + Ref texture; + Ref texture_pressed; + Ref bitmask; + + StringName action; + bool passby_press; + int finger_pressed; + int action_id; + + VisibilityMode visibility; + + void _input(const InputEvent& p_Event); + +protected: + + void _notification(int p_what); + static void _bind_methods(); +public: + + + void set_texture(const Ref& p_texture); + Ref get_texture() const; + + void set_texture_pressed(const Ref& p_texture_pressed); + Ref get_texture_pressed() const; + + void set_bitmask(const Ref& p_bitmask); + Ref get_bitmask() const; + + void set_action(const String& p_action); + String get_action() const; + + void set_passby_press(bool p_enable); + bool is_passby_press_enabled() const; + + void set_visibility_mode(VisibilityMode p_mode); + VisibilityMode get_visibility_mode() const; + + bool is_pressed() const; + + Rect2 get_item_rect() const; + + + TouchScreenButton(); +}; + +VARIANT_ENUM_CAST(TouchScreenButton::VisibilityMode); + +#endif // SCREEN_BUTTON_H diff --git a/scene/2d/sound_player_2d.cpp b/scene/2d/sound_player_2d.cpp new file mode 100644 index 00000000000..2fffef4e51e --- /dev/null +++ b/scene/2d/sound_player_2d.cpp @@ -0,0 +1,123 @@ +/*************************************************************************/ +/* sound_player_2d.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 "sound_player_2d.h" + +#include "servers/audio_server.h" + +#include "servers/spatial_sound_2d_server.h" +#include "scene/resources/surface_tool.h" + + +void SoundPlayer2D::_notification(int p_what) { + + + switch(p_what) { + + case NOTIFICATION_ENTER_SCENE: { + //find the sound space + + source_rid = SpatialSound2DServer::get_singleton()->source_create(get_world_2d()->get_sound_space()); + + for(int i=0;isource_set_transform(source_rid,get_global_transform()); + + + } break; + case NOTIFICATION_TRANSFORM_CHANGED: { + + SpatialSound2DServer::get_singleton()->source_set_transform(source_rid,get_global_transform()); + + } break; + case NOTIFICATION_EXIT_SCENE: { + + if (source_rid.is_valid()) + SpatialSound2DServer::get_singleton()->free(source_rid); + + } break; + } + +} + + +void SoundPlayer2D::set_param( Param p_param, float p_value) { + + ERR_FAIL_INDEX(p_param,PARAM_MAX); + params[p_param]=p_value; + if (source_rid.is_valid()) + SpatialSound2DServer::get_singleton()->source_set_param(source_rid,(SpatialSound2DServer::SourceParam)p_param,p_value); + +} + +float SoundPlayer2D::get_param( Param p_param) const { + + ERR_FAIL_INDEX_V(p_param,PARAM_MAX,0); + return params[p_param]; + +} + + +void SoundPlayer2D::_bind_methods() { + + + ObjectTypeDB::bind_method(_MD("set_param","param","value"),&SoundPlayer2D::set_param); + ObjectTypeDB::bind_method(_MD("get_param","param"),&SoundPlayer2D::get_param); + + BIND_CONSTANT( PARAM_VOLUME_DB ); + BIND_CONSTANT( PARAM_PITCH_SCALE ); + BIND_CONSTANT( PARAM_ATTENUATION_MIN_DISTANCE ); + BIND_CONSTANT( PARAM_ATTENUATION_MAX_DISTANCE ); + BIND_CONSTANT( PARAM_ATTENUATION_DISTANCE_EXP ); + BIND_CONSTANT( PARAM_MAX ); + + ADD_PROPERTYI( PropertyInfo( Variant::REAL, "params/volume_db",PROPERTY_HINT_RANGE, "-80,24,0.01"),_SCS("set_param"),_SCS("get_param"),PARAM_VOLUME_DB); + ADD_PROPERTYI( PropertyInfo( Variant::REAL, "params/pitch_scale",PROPERTY_HINT_RANGE, "0.001,32,0.001"),_SCS("set_param"),_SCS("get_param"),PARAM_PITCH_SCALE); + ADD_PROPERTYI( PropertyInfo( Variant::REAL, "params/attenuation/min_distance",PROPERTY_HINT_EXP_RANGE, "16,16384,1"),_SCS("set_param"),_SCS("get_param"),PARAM_ATTENUATION_MIN_DISTANCE); + ADD_PROPERTYI( PropertyInfo( Variant::REAL, "params/attenuation/max_distance",PROPERTY_HINT_EXP_RANGE, "16,16384,1"),_SCS("set_param"),_SCS("get_param"),PARAM_ATTENUATION_MAX_DISTANCE); + ADD_PROPERTYI( PropertyInfo( Variant::REAL, "params/attenuation/distance_exp",PROPERTY_HINT_EXP_EASING, "attenuation"),_SCS("set_param"),_SCS("get_param"),PARAM_ATTENUATION_DISTANCE_EXP); + +} + + +SoundPlayer2D::SoundPlayer2D() { + + params[PARAM_VOLUME_DB]=0.0; + params[PARAM_PITCH_SCALE]=1.0; + params[PARAM_ATTENUATION_MIN_DISTANCE]=1; + params[PARAM_ATTENUATION_MAX_DISTANCE]=2048; + params[PARAM_ATTENUATION_DISTANCE_EXP]=1.0; //linear (and not really good) + + +} + +SoundPlayer2D::~SoundPlayer2D() { + + +} diff --git a/scene/2d/sound_player_2d.h b/scene/2d/sound_player_2d.h new file mode 100644 index 00000000000..9578c6b323c --- /dev/null +++ b/scene/2d/sound_player_2d.h @@ -0,0 +1,82 @@ +/*************************************************************************/ +/* sound_player_2d.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 SOUND_PLAYER_2D_H +#define SOUND_PLAYER_2D_H + + +#include "scene/2d/node_2d.h" +#include "scene/resources/sample_library.h" +#include "servers/spatial_sound_2d_server.h" +#include "scene/main/viewport.h" + +class SoundPlayer2D : public Node2D { + + OBJ_TYPE(SoundPlayer2D,Node2D); +public: + + + enum Param { + + PARAM_VOLUME_DB=SpatialSound2DServer::SOURCE_PARAM_VOLUME_DB, + PARAM_PITCH_SCALE=SpatialSound2DServer::SOURCE_PARAM_PITCH_SCALE, + PARAM_ATTENUATION_MIN_DISTANCE=SpatialSound2DServer::SOURCE_PARAM_ATTENUATION_MIN_DISTANCE, + PARAM_ATTENUATION_MAX_DISTANCE=SpatialSound2DServer::SOURCE_PARAM_ATTENUATION_MAX_DISTANCE, + PARAM_ATTENUATION_DISTANCE_EXP=SpatialSound2DServer::SOURCE_PARAM_ATTENUATION_DISTANCE_EXP, + PARAM_MAX=SpatialSound2DServer::SOURCE_PARAM_MAX + }; + +private: + + float params[PARAM_MAX]; + RID source_rid; + + +protected: + + _FORCE_INLINE_ RID get_source_rid() const { return source_rid; } + + void _notification(int p_what); + + static void _bind_methods(); + +public: + + void set_param( Param p_param, float p_value); + float get_param( Param p_param) const; + + + SoundPlayer2D(); + ~SoundPlayer2D(); + + +}; + +VARIANT_ENUM_CAST(SoundPlayer2D::Param ); + +#endif // SOUND_PLAYER_2D_H diff --git a/scene/2d/sprite.cpp b/scene/2d/sprite.cpp new file mode 100644 index 00000000000..0265ec4df2f --- /dev/null +++ b/scene/2d/sprite.cpp @@ -0,0 +1,337 @@ +/*************************************************************************/ +/* sprite.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 "sprite.h" +#include "core/core_string_names.h" +#include "scene/scene_string_names.h" +void Sprite::edit_set_pivot(const Point2& p_pivot) { + + set_offset(p_pivot); +} + +Point2 Sprite::edit_get_pivot() const { + + return get_offset(); +} +bool Sprite::edit_has_pivot() const { + + return true; +} + +void Sprite::_notification(int p_what) { + + switch(p_what) { + + case NOTIFICATION_DRAW: { + + if (texture.is_null()) + return; + + + + + RID ci = get_canvas_item(); + + /* + texture->draw(ci,Point2()); + break; + */ + + Size2i s; + Rect2i src_rect; + + if (region) { + + s=region_rect.size; + src_rect=region_rect; + } else { + s = texture->get_size(); + s=s/Size2i(hframes,vframes); + + src_rect.size=s; + src_rect.pos.x+=(frame%hframes)*s.x; + src_rect.pos.y+=(frame/hframes)*s.y; + + } + + Point2i ofs=offset; + if (centered) + ofs-=s/2; + + Rect2i dst_rect(ofs,s); + + if (hflip) + dst_rect.size.x=-dst_rect.size.x; + if (vflip) + dst_rect.size.y=-dst_rect.size.y; + + texture->draw_rect_region(ci,dst_rect,src_rect,modulate); + + } break; + } +} + +void Sprite::set_texture(const Ref& p_texture) { + + if (p_texture==texture) + return; + if (texture.is_valid()) { + texture->disconnect(CoreStringNames::get_singleton()->changed,this,SceneStringNames::get_singleton()->update); + } + texture=p_texture; + if (texture.is_valid()) { + texture->set_flags(texture->get_flags()&(~Texture::FLAG_REPEAT)); //remove repeat from texture, it looks bad in sprites + texture->connect(CoreStringNames::get_singleton()->changed,this,SceneStringNames::get_singleton()->update); + } + update(); + item_rect_changed(); +} + +Ref Sprite::get_texture() const { + + return texture; +} + +void Sprite::set_centered(bool p_center) { + + centered=p_center; + update(); + item_rect_changed(); +} + +bool Sprite::is_centered() const { + + return centered; +} + +void Sprite::set_offset(const Point2& p_offset) { + + offset=p_offset; + update(); + item_rect_changed(); +} +Point2 Sprite::get_offset() const { + + return offset; +} + +void Sprite::set_flip_h(bool p_flip) { + + hflip=p_flip; + update(); +} +bool Sprite::is_flipped_h() const { + + return hflip; +} + +void Sprite::set_flip_v(bool p_flip) { + + vflip=p_flip; + update(); +} +bool Sprite::is_flipped_v() const { + + return vflip; +} + +void Sprite::set_region(bool p_region) { + + if (p_region==region) + return; + + region=p_region; + update(); +} + +bool Sprite::is_region() const{ + + return region; +} + +void Sprite::set_region_rect(const Rect2& p_region_rect) { + + bool changed=region_rect!=p_region_rect; + region_rect=p_region_rect; + if (region && changed) { + update(); + item_rect_changed(); + } +} + +Rect2 Sprite::get_region_rect() const { + + return region_rect; +} + +void Sprite::set_frame(int p_frame) { + + ERR_FAIL_INDEX(p_frame,vframes*hframes); + + if (frame != p_frame) + item_rect_changed(); + + frame=p_frame; +} + +int Sprite::get_frame() const { + + return frame; +} + +void Sprite::set_vframes(int p_amount) { + + ERR_FAIL_COND(p_amount<1); + vframes=p_amount; + update(); + item_rect_changed(); + _change_notify("frame"); +} +int Sprite::get_vframes() const { + + return vframes; +} + +void Sprite::set_hframes(int p_amount) { + + ERR_FAIL_COND(p_amount<1); + hframes=p_amount; + update(); + item_rect_changed(); + _change_notify("frame"); +} +int Sprite::get_hframes() const { + + return hframes; +} + +void Sprite::set_modulate(const Color& p_color) { + + modulate=p_color; + update(); +} + +Color Sprite::get_modulate() const{ + + return modulate; +} + + +Rect2 Sprite::get_item_rect() const { + + if (texture.is_null()) + return Rect2(0,0,1,1); + //if (texture.is_null()) + // return CanvasItem::get_item_rect(); + + Size2i s; + + if (region) { + + s=region_rect.size; + } else { + s = texture->get_size(); + s=s/Point2(hframes,vframes); + } + + Point2i ofs=offset; + if (centered) + ofs-=s/2; + + if (s==Size2(0,0)) + s=Size2(1,1); + + return Rect2(ofs,s); +} + + +void Sprite::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("set_texture","texture:Texture"),&Sprite::set_texture); + ObjectTypeDB::bind_method(_MD("get_texture:Texture"),&Sprite::get_texture); + + ObjectTypeDB::bind_method(_MD("set_centered","centered"),&Sprite::set_centered); + ObjectTypeDB::bind_method(_MD("is_centered"),&Sprite::is_centered); + + ObjectTypeDB::bind_method(_MD("set_offset","offset"),&Sprite::set_offset); + ObjectTypeDB::bind_method(_MD("get_offset"),&Sprite::get_offset); + + ObjectTypeDB::bind_method(_MD("set_flip_h","flip_h"),&Sprite::set_flip_h); + ObjectTypeDB::bind_method(_MD("is_flipped_h"),&Sprite::is_flipped_h); + + ObjectTypeDB::bind_method(_MD("set_flip_v","flip_v"),&Sprite::set_flip_v); + ObjectTypeDB::bind_method(_MD("is_flipped_v"),&Sprite::is_flipped_v); + + ObjectTypeDB::bind_method(_MD("set_region","enabled"),&Sprite::set_region); + ObjectTypeDB::bind_method(_MD("is_region"),&Sprite::is_region); + + ObjectTypeDB::bind_method(_MD("set_region_rect","rect"),&Sprite::set_region_rect); + ObjectTypeDB::bind_method(_MD("get_region_rect"),&Sprite::get_region_rect); + + ObjectTypeDB::bind_method(_MD("set_frame","frame"),&Sprite::set_frame); + ObjectTypeDB::bind_method(_MD("get_frame"),&Sprite::get_frame); + + ObjectTypeDB::bind_method(_MD("set_vframes","vframes"),&Sprite::set_vframes); + ObjectTypeDB::bind_method(_MD("get_vframes"),&Sprite::get_vframes); + + ObjectTypeDB::bind_method(_MD("set_hframes","hframes"),&Sprite::set_hframes); + ObjectTypeDB::bind_method(_MD("get_hframes"),&Sprite::get_hframes); + + ObjectTypeDB::bind_method(_MD("set_modulate","modulate"),&Sprite::set_modulate); + ObjectTypeDB::bind_method(_MD("get_modulate"),&Sprite::get_modulate); + + ADD_PROPERTY( PropertyInfo( Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE,"Texture"), _SCS("set_texture"),_SCS("get_texture")); + ADD_PROPERTY( PropertyInfo( Variant::BOOL, "centered"), _SCS("set_centered"),_SCS("is_centered")); + ADD_PROPERTY( PropertyInfo( Variant::VECTOR2, "offset"), _SCS("set_offset"),_SCS("get_offset")); + ADD_PROPERTY( PropertyInfo( Variant::BOOL, "flip_h"), _SCS("set_flip_h"),_SCS("is_flipped_h")); + ADD_PROPERTY( PropertyInfo( Variant::BOOL, "flip_v"), _SCS("set_flip_v"),_SCS("is_flipped_v")); + ADD_PROPERTY( PropertyInfo( Variant::INT, "vframes"), _SCS("set_vframes"),_SCS("get_vframes")); + ADD_PROPERTY( PropertyInfo( Variant::INT, "hframes"), _SCS("set_hframes"),_SCS("get_hframes")); + ADD_PROPERTY( PropertyInfo( Variant::INT, "frame"), _SCS("set_frame"),_SCS("get_frame")); + ADD_PROPERTY( PropertyInfo( Variant::COLOR, "modulate"), _SCS("set_modulate"),_SCS("get_modulate")); + ADD_PROPERTY( PropertyInfo( Variant::BOOL, "region"), _SCS("set_region"),_SCS("is_region")); + ADD_PROPERTY( PropertyInfo( Variant::RECT2, "region_rect"), _SCS("set_region_rect"),_SCS("get_region_rect")); + +} + +Sprite::Sprite() { + + centered=true; + hflip=false; + vflip=false; + region=false; + + frame=0; + + vframes=1; + hframes=1; + + modulate=Color(1,1,1,1); + + +} diff --git a/scene/2d/sprite.h b/scene/2d/sprite.h new file mode 100644 index 00000000000..42f737fa918 --- /dev/null +++ b/scene/2d/sprite.h @@ -0,0 +1,108 @@ +/*************************************************************************/ +/* sprite.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 SPRITE_H +#define SPRITE_H + +#include "scene/2d/node_2d.h" +#include "scene/resources/texture.h" + + +class Sprite : public Node2D { + + OBJ_TYPE( Sprite, Node2D ); + + Ref texture; + + bool centered; + Point2 offset; + + bool hflip; + bool vflip; + bool region; + Rect2 region_rect; + + int frame; + + int vframes; + int hframes; + + Color modulate; + + +protected: + + void _notification(int p_what); + + static void _bind_methods();; + +public: + + virtual void edit_set_pivot(const Point2& p_pivot); + virtual Point2 edit_get_pivot() const; + virtual bool edit_has_pivot() const; + + void set_texture(const Ref& p_texture); + Ref get_texture() const; + + void set_centered(bool p_center); + bool is_centered() const; + + void set_offset(const Point2& p_offset); + Point2 get_offset() const; + + void set_flip_h(bool p_flip); + bool is_flipped_h() const; + + void set_flip_v(bool p_flip); + bool is_flipped_v() const; + + void set_region(bool p_region); + bool is_region() const; + + void set_region_rect(const Rect2& p_region_rect); + Rect2 get_region_rect() const; + + void set_frame(int p_frame); + int get_frame() const; + + void set_vframes(int p_amount); + int get_vframes() const; + + void set_hframes(int p_amount); + int get_hframes() const; + + void set_modulate(const Color& p_color); + Color get_modulate() const; + + virtual Rect2 get_item_rect() const; + + Sprite(); +}; + +#endif // SPRITE_H diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp new file mode 100644 index 00000000000..26efa99a886 --- /dev/null +++ b/scene/2d/tile_map.cpp @@ -0,0 +1,592 @@ +/*************************************************************************/ +/* tile_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 "tile_map.h" +#include "io/marshalls.h" +#include "servers/physics_2d_server.h" +void TileMap::_notification(int p_what) { + + switch(p_what) { + + case NOTIFICATION_ENTER_SCENE: { + + pending_update=true; + _update_dirty_quadrants(); + RID space = get_world_2d()->get_space(); + _update_quadrant_transform(); + _update_quadrant_space(space); + + + } break; + case NOTIFICATION_EXIT_SCENE: { + + _update_quadrant_space(RID()); + + } break; + case NOTIFICATION_TRANSFORM_CHANGED: { + + //move stuff + _update_quadrant_transform(); + + } break; + } +} + +void TileMap::_update_quadrant_space(const RID& p_space) { + + for (Map::Element *E=quadrant_map.front();E;E=E->next()) { + + Quadrant &q=E->get(); + Physics2DServer::get_singleton()->body_set_space(q.static_body,p_space); + } +} + +void TileMap::_update_quadrant_transform() { + + if (!is_inside_scene()) + return; + + Matrix32 global_transform = get_global_transform(); + + for (Map::Element *E=quadrant_map.front();E;E=E->next()) { + + Quadrant &q=E->get(); + Matrix32 xform; + xform.set_origin( q.pos ); + xform = global_transform * xform; + Physics2DServer::get_singleton()->body_set_state(q.static_body,Physics2DServer::BODY_STATE_TRANSFORM,xform); + } +} + +void TileMap::set_tileset(const Ref& p_tileset) { + + if (tile_set.is_valid()) + tile_set->disconnect("changed",this,"_recreate_quadrants"); + + _clear_quadrants(); + tile_set=p_tileset; + + if (tile_set.is_valid()) + tile_set->connect("changed",this,"_recreate_quadrants"); + else + clear(); + + _recreate_quadrants(); + +} + +Ref TileMap::get_tileset() const { + + return tile_set; +} + +void TileMap::set_cell_size(int p_size) { + + ERR_FAIL_COND(p_size<1); + + _clear_quadrants(); + cell_size=p_size; + _recreate_quadrants(); + + +} +int TileMap::get_cell_size() const { + + return cell_size; +} +void TileMap::set_quadrant_size(int p_size) { + + ERR_FAIL_COND(p_size<1); + + _clear_quadrants(); + quadrant_size=p_size; + _recreate_quadrants(); + +} +int TileMap::get_quadrant_size() const { + + return quadrant_size; +} + +void TileMap::set_center_x(bool p_enable) { + + center_x=p_enable; + _recreate_quadrants(); + +} +bool TileMap::get_center_x() const { + + return center_x; +} +void TileMap::set_center_y(bool p_enable) { + + center_y=p_enable; + _recreate_quadrants(); + +} +bool TileMap::get_center_y() const { + + return center_y; +} + +void TileMap::_update_dirty_quadrants() { + + if (!pending_update) + return; + if (!is_inside_scene()) + return; + if (!tile_set.is_valid()) + return; + + VisualServer *vs = VisualServer::get_singleton(); + Physics2DServer *ps = Physics2DServer::get_singleton(); + + while (dirty_quadrant_list.first()) { + + Quadrant &q = *dirty_quadrant_list.first()->self(); + + vs->canvas_item_clear(q.canvas_item); + ps->body_clear_shapes(q.static_body); + + for(int i=0;i::Element *E=tile_map.find( q.cells[i] ); + Cell &c=E->get(); + //moment of truth + if (!tile_set->has_tile(c.id)) + continue; + Ref tex = tile_set->tile_get_texture(c.id); + Vector2 tile_ofs = tile_set->tile_get_offset(c.id); + + Vector2 offset = Point2( E->key().x, E->key().y )*cell_size - q.pos; + + if (!tex.is_valid()) + continue; + + + Rect2 r = tile_set->tile_get_region(c.id); + Size2 s = tex->get_size(); + + if (r==Rect2()) + s = tex->get_size(); + else { + s = r.size; + r.pos.x+=fp_adjust; + r.pos.y+=fp_adjust; + r.size.x-=fp_adjust*2.0; + r.size.y-=fp_adjust*2.0; + } + + Rect2 rect; + rect.pos=offset.floor(); + rect.size=s; + + rect.size.x+=fp_adjust; + rect.size.y+=fp_adjust; + + if (c.flip_h) + rect.size.x=-rect.size.x; + if (c.flip_v) + rect.size.y=-rect.size.y; + + + if (r==Rect2()) { + + tex->draw_rect(q.canvas_item,rect); + } else { + + tex->draw_rect_region(q.canvas_item,rect,r); + } + + Vector< Ref > shapes = tile_set->tile_get_shapes(c.id); + + + for(int i=0;i shape = shapes[i]; + if (shape.is_valid()) { + + Matrix32 xform; + xform.set_origin(offset.floor()); + if (c.flip_h) { + xform.elements[0]=-xform.elements[0]; + xform.elements[2].x+=s.x; + } + if (c.flip_v) { + xform.elements[1]=-xform.elements[1]; + xform.elements[2].y+=s.y; + } + + ps->body_add_shape(q.static_body,shape->get_rid(),xform); + } + } + } + + dirty_quadrant_list.remove( dirty_quadrant_list.first() ); + } + + + + pending_update=false; + + _recompute_rect_cache(); + +} + +void TileMap::_recompute_rect_cache() { + + +#ifdef DEBUG_ENABLED + + if (!rect_cache_dirty) + return; + + Rect2 r_total; + for (Map::Element *E=quadrant_map.front();E;E=E->next()) { + + + Rect2 r( Point2(E->key().x, E->key().y)*cell_size*quadrant_size, Size2(1,1)*cell_size*quadrant_size ); + if (E==quadrant_map.front()) + r_total=r; + else + r_total=r_total.merge(r); + + } + if (r_total==Rect2()) { + rect_cache=Rect2(-10,-10,20,20); + } else { + rect_cache=r_total; + } + + item_rect_changed(); + + rect_cache_dirty=false; +#endif + + +} + +Map::Element *TileMap::_create_quadrant(const PosKey& p_qk) { + + Matrix32 xform; + xform.set_origin(Point2(p_qk.x,p_qk.y)*quadrant_size*cell_size); + Quadrant q; + q.canvas_item = VisualServer::get_singleton()->canvas_item_create(); + VisualServer::get_singleton()->canvas_item_set_parent( q.canvas_item, get_canvas_item() ); + VisualServer::get_singleton()->canvas_item_set_transform( q.canvas_item, xform ); + q.static_body=Physics2DServer::get_singleton()->body_create(Physics2DServer::BODY_MODE_STATIC); + if (is_inside_scene()) { + xform = get_global_transform() * xform; + RID space = get_world_2d()->get_space(); + Physics2DServer::get_singleton()->body_set_space(q.static_body,space); + } + + Physics2DServer::get_singleton()->body_set_state(q.static_body,Physics2DServer::BODY_STATE_TRANSFORM,xform); + q.pos=Vector2(p_qk.x,p_qk.y)*quadrant_size*cell_size; + + rect_cache_dirty=true; + return quadrant_map.insert(p_qk,q); +} + +void TileMap::_erase_quadrant(Map::Element *Q) { + + Quadrant &q=Q->get(); + Physics2DServer::get_singleton()->free(q.static_body); + VisualServer::get_singleton()->free(q.canvas_item); + if (q.dirty_list.in_list()) + dirty_quadrant_list.remove(&q.dirty_list); + + quadrant_map.erase(Q); + rect_cache_dirty=true; +} + +void TileMap::_make_quadrant_dirty(Map::Element *Q) { + + Quadrant &q=Q->get(); + if (!q.dirty_list.in_list()) + dirty_quadrant_list.add(&q.dirty_list); + + if (pending_update) + return; + pending_update=true; + if (!is_inside_scene()) + return; + call_deferred("_update_dirty_quadrants"); +} + + +void TileMap::set_cell(int p_x,int p_y,int p_tile,bool p_flip_x,bool p_flip_y) { + + PosKey pk(p_x,p_y); + + Map::Element *E=tile_map.find(pk); + if (!E && p_tile==INVALID_CELL) + return; //nothing to do + + PosKey qk(p_x/quadrant_size,p_y/quadrant_size); + if (p_tile==INVALID_CELL) { + //erase existing + tile_map.erase(pk); + Map::Element *Q = quadrant_map.find(qk); + ERR_FAIL_COND(!Q); + Quadrant &q=Q->get(); + q.cells.erase(pk); + if (q.cells.size()==0) + _erase_quadrant(Q); + else + _make_quadrant_dirty(Q); + + return; + } + + Map::Element *Q = quadrant_map.find(qk); + + if (!E) { + E=tile_map.insert(pk,Cell()); + if (!Q) + Q=_create_quadrant(qk); + Quadrant &q=Q->get(); + q.cells.insert(pk); + } else { + ERR_FAIL_COND(!Q); // quadrant should exist... + + if (E->get().id==p_tile && E->get().flip_h==p_flip_x && E->get().flip_v==p_flip_y) + return; //nothing changed + + } + + + Cell &c = E->get(); + + c.id=p_tile; + c.flip_h=p_flip_x; + c.flip_v=p_flip_y; + + _make_quadrant_dirty(Q); + +} + +int TileMap::get_cell(int p_x,int p_y) const { + + PosKey pk(p_x,p_y); + + const Map::Element *E=tile_map.find(pk); + + if (!E) + return INVALID_CELL; + + return E->get().id; + +} +bool TileMap::is_cell_x_flipped(int p_x,int p_y) const { + + PosKey pk(p_x,p_y); + + const Map::Element *E=tile_map.find(pk); + + if (!E) + return false; + + return E->get().flip_h; +} +bool TileMap::is_cell_y_flipped(int p_x,int p_y) const { + + PosKey pk(p_x,p_y); + + const Map::Element *E=tile_map.find(pk); + + if (!E) + return false; + + return E->get().flip_v; +} + + +void TileMap::_recreate_quadrants() { + + _clear_quadrants(); + + for (Map::Element *E=tile_map.front();E;E=E->next()) { + + PosKey qk(E->key().x/quadrant_size,E->key().y/quadrant_size); + + Map::Element *Q=quadrant_map.find(qk); + if (!Q) { + Q=_create_quadrant(qk); + dirty_quadrant_list.add(&Q->get().dirty_list); + } + + Q->get().cells.insert(E->key()); + + } +} + +void TileMap::_clear_quadrants() { + + while (quadrant_map.size()) { + _erase_quadrant( quadrant_map.front() ); + } +} + +void TileMap::clear() { + + _clear_quadrants(); + tile_map.clear(); +} + +void TileMap::_set_tile_data(const DVector& p_data) { + + int c=p_data.size(); + DVector::Read r = p_data.read(); + + + for(int i=0;i4000 || y>4000) +// continue; + set_cell(x,y,v,flip_h,flip_v); + } + +} + +DVector TileMap::_get_tile_data() const { + + DVector data; + data.resize(tile_map.size()*2); + DVector::Write w = data.write(); + + int idx=0; + for(const Map::Element *E=tile_map.front();E;E=E->next()) { + + uint8_t *ptr = (uint8_t*)&w[idx]; + encode_uint16(E->key().x,&ptr[0]); + encode_uint16(E->key().y,&ptr[2]); + uint32_t val = E->get().id; + if (E->get().flip_h) + val|=(1<<29); + if (E->get().flip_v) + val|=(1<<30); + + encode_uint32(val,&ptr[4]); + idx+=2; + } + + + w = DVector::Write(); + + return data; + +} + +Rect2 TileMap::get_item_rect() const { + + const_cast(this)->_update_dirty_quadrants(); + return rect_cache; +} + +void TileMap::_bind_methods() { + + + ObjectTypeDB::bind_method(_MD("set_tileset","tileset:TileSet"),&TileMap::set_tileset); + ObjectTypeDB::bind_method(_MD("get_tileset:TileSet"),&TileMap::get_tileset); + + + ObjectTypeDB::bind_method(_MD("set_cell_size","size"),&TileMap::set_cell_size); + ObjectTypeDB::bind_method(_MD("get_cell_size"),&TileMap::get_cell_size); + + ObjectTypeDB::bind_method(_MD("set_quadrant_size","size"),&TileMap::set_quadrant_size); + ObjectTypeDB::bind_method(_MD("get_quadrant_size"),&TileMap::get_quadrant_size); + + ObjectTypeDB::bind_method(_MD("set_center_x","enable"),&TileMap::set_center_x); + ObjectTypeDB::bind_method(_MD("get_center_x"),&TileMap::get_center_x); + + ObjectTypeDB::bind_method(_MD("set_center_y","enable"),&TileMap::set_center_y); + ObjectTypeDB::bind_method(_MD("get_center_y"),&TileMap::get_center_y); + + + ObjectTypeDB::bind_method(_MD("set_cell","x","y","tile","flip_x","flip_y"),&TileMap::set_cell,DEFVAL(false),DEFVAL(false)); + ObjectTypeDB::bind_method(_MD("get_cell","x","y"),&TileMap::get_cell); + ObjectTypeDB::bind_method(_MD("is_cell_x_flipped","x","y"),&TileMap::is_cell_x_flipped); + ObjectTypeDB::bind_method(_MD("is_cell_y_flipped","x","y"),&TileMap::is_cell_y_flipped); + + ObjectTypeDB::bind_method(_MD("clear"),&TileMap::clear); + + ObjectTypeDB::bind_method(_MD("_clear_quadrants"),&TileMap::_clear_quadrants); + ObjectTypeDB::bind_method(_MD("_recreate_quadrants"),&TileMap::_recreate_quadrants); + ObjectTypeDB::bind_method(_MD("_update_dirty_quadrants"),&TileMap::_update_dirty_quadrants); + + ObjectTypeDB::bind_method(_MD("_set_tile_data"),&TileMap::_set_tile_data); + ObjectTypeDB::bind_method(_MD("_get_tile_data"),&TileMap::_get_tile_data); + + ADD_PROPERTY( PropertyInfo(Variant::INT,"cell_size",PROPERTY_HINT_RANGE,"1,8192,1"),_SCS("set_cell_size"),_SCS("get_cell_size")); + ADD_PROPERTY( PropertyInfo(Variant::INT,"quadrant_size",PROPERTY_HINT_RANGE,"1,128,1"),_SCS("set_quadrant_size"),_SCS("get_quadrant_size")); + ADD_PROPERTY( PropertyInfo(Variant::OBJECT,"tile_set",PROPERTY_HINT_RESOURCE_TYPE,"TileSet"),_SCS("set_tileset"),_SCS("get_tileset")); + ADD_PROPERTY( PropertyInfo(Variant::OBJECT,"tile_data",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_NOEDITOR),_SCS("_set_tile_data"),_SCS("_get_tile_data")); + + BIND_CONSTANT( INVALID_CELL ); +} + +TileMap::TileMap() { + + + + rect_cache_dirty=true; + pending_update=false; + quadrant_size=16; + cell_size=64; + center_x=false; + center_y=false; + + fp_adjust=0.4; + fp_adjust=0.4; +} + +TileMap::~TileMap() { + + clear(); +} diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h new file mode 100644 index 00000000000..a2414382c65 --- /dev/null +++ b/scene/2d/tile_map.h @@ -0,0 +1,154 @@ +/*************************************************************************/ +/* tile_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 TILE_MAP_H +#define TILE_MAP_H + +#include "scene/2d/node_2d.h" +#include "scene/resources/tile_set.h" +#include "self_list.h" +#include "vset.h" + +class TileMap : public Node2D { + + OBJ_TYPE( TileMap, Node2D ); + + + Ref tile_set; + int cell_size; + int quadrant_size; + bool center_x,center_y; + + union PosKey { + + struct { + int16_t x; + int16_t y; + }; + + uint32_t key; + bool operator<(const PosKey& pk) const { return key tile_map; + struct Quadrant { + + Vector2 pos; + RID canvas_item; + RID static_body; + + SelfList dirty_list; + + VSet cells; + + void operator=(const Quadrant& q) { pos=q.pos; canvas_item=q.canvas_item; static_body=q.static_body; cells=q.cells; } + Quadrant(const Quadrant& q) : dirty_list(this) { pos=q.pos; canvas_item=q.canvas_item; static_body=q.static_body; cells=q.cells;} + Quadrant() : dirty_list(this) {} + }; + + Map quadrant_map; + + SelfList::List dirty_quadrant_list; + + bool pending_update; + + Rect2 rect_cache; + bool rect_cache_dirty; + float fp_adjust; + + + Map::Element *_create_quadrant(const PosKey& p_qk); + void _erase_quadrant(Map::Element *Q); + void _make_quadrant_dirty(Map::Element *Q); + void _recreate_quadrants(); + void _clear_quadrants(); + void _update_dirty_quadrants(); + void _update_quadrant_space(const RID& p_space); + void _update_quadrant_transform(); + void _recompute_rect_cache(); + + void _set_tile_data(const DVector& p_data); + DVector _get_tile_data() const; +protected: + + + void _notification(int p_what); + static void _bind_methods(); + +public: + + enum { + INVALID_CELL=-1 + }; + + void set_tileset(const Ref& p_tileset); + Ref get_tileset() const; + + void set_cell_size(int p_size); + int get_cell_size() const; + + void set_quadrant_size(int p_size); + int get_quadrant_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_cell(int p_x,int p_y,int p_tile,bool p_flip_x=false,bool p_flip_y=false); + int get_cell(int p_x,int p_y) const; + bool is_cell_x_flipped(int p_x,int p_y) const; + bool is_cell_y_flipped(int p_x,int p_y) const; + + Rect2 get_item_rect() const; + + void clear(); + + TileMap(); + ~TileMap(); +}; + +#endif // TILE_MAP_H diff --git a/scene/2d/visibility_notifier_2d.cpp b/scene/2d/visibility_notifier_2d.cpp new file mode 100644 index 00000000000..323d02df4b3 --- /dev/null +++ b/scene/2d/visibility_notifier_2d.cpp @@ -0,0 +1,322 @@ +/*************************************************************************/ +/* visibility_notifier_2d.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 "visibility_notifier_2d.h" + +#include "scene/scene_string_names.h" +#include "scene/2d/physics_body_2d.h" +#include "scene/animation/animation_player.h" +#include "scene/scene_string_names.h" + +void VisibilityNotifier2D::_enter_viewport(Viewport* p_viewport) { + + ERR_FAIL_COND(viewports.has(p_viewport)); + viewports.insert(p_viewport); + if (viewports.size()==1) { + emit_signal(SceneStringNames::get_singleton()->enter_screen); + _screen_enter(); + + + } + emit_signal(SceneStringNames::get_singleton()->enter_viewport,p_viewport); + +} + +void VisibilityNotifier2D::_exit_viewport(Viewport* p_viewport){ + + ERR_FAIL_COND(!viewports.has(p_viewport)); + viewports.erase(p_viewport); + + emit_signal(SceneStringNames::get_singleton()->exit_viewport,p_viewport); + if (viewports.size()==0) { + emit_signal(SceneStringNames::get_singleton()->exit_screen); + + _screen_exit(); + } +} + + +void VisibilityNotifier2D::set_rect(const Rect2& p_rect){ + + rect=p_rect; + if (is_inside_scene()) + get_world_2d()->_update_notifier(this,get_global_transform().xform(rect)); + + _change_notify("rect"); +} + +Rect2 VisibilityNotifier2D::get_item_rect() const { + + return rect; +} + +Rect2 VisibilityNotifier2D::get_rect() const{ + + return rect; +} + + +void VisibilityNotifier2D::_notification(int p_what) { + + + switch(p_what) { + case NOTIFICATION_ENTER_SCENE: { + + //get_world_2d()-> + get_world_2d()->_register_notifier(this,get_global_transform().xform(rect)); + } break; + case NOTIFICATION_TRANSFORM_CHANGED: { + + //get_world_2d()-> + get_world_2d()->_update_notifier(this,get_global_transform().xform(rect)); + } break; + case NOTIFICATION_DRAW: { + + if (get_scene()->is_editor_hint()) { + + draw_rect(rect,Color(1,0.5,1,0.2)); + } + } break; + case NOTIFICATION_EXIT_SCENE: { + + get_world_2d()->_remove_notifier(this); + } break; + } +} + +bool VisibilityNotifier2D::is_on_screen() const { + + return viewports.size()>0; +} + +void VisibilityNotifier2D::_bind_methods(){ + + ObjectTypeDB::bind_method(_MD("set_rect","rect"),&VisibilityNotifier2D::set_rect); + ObjectTypeDB::bind_method(_MD("get_rect"),&VisibilityNotifier2D::get_rect); + ObjectTypeDB::bind_method(_MD("is_on_screen"),&VisibilityNotifier2D::is_on_screen); + + ADD_PROPERTY( PropertyInfo(Variant::RECT2,"rect"),_SCS("set_rect"),_SCS("get_rect")); + + ADD_SIGNAL( MethodInfo("enter_viewport",PropertyInfo(Variant::OBJECT,"viewport",PROPERTY_HINT_RESOURCE_TYPE,"Viewport")) ); + ADD_SIGNAL( MethodInfo("exit_viewport",PropertyInfo(Variant::OBJECT,"viewport",PROPERTY_HINT_RESOURCE_TYPE,"Viewport")) ); + ADD_SIGNAL( MethodInfo("enter_screen")); + ADD_SIGNAL( MethodInfo("exit_screen")); +} + + +VisibilityNotifier2D::VisibilityNotifier2D() { + + rect=Rect2(-10,-10,20,20); +} + + + + + +////////////////////////////////////// + + +void VisibilityEnabler2D::_screen_enter() { + + + for(Map::Element *E=nodes.front();E;E=E->next()) { + + _change_node_state(E->key(),true); + } + + visible=true; +} + +void VisibilityEnabler2D::_screen_exit(){ + + for(Map::Element *E=nodes.front();E;E=E->next()) { + + _change_node_state(E->key(),false); + } + + visible=false; +} + +void VisibilityEnabler2D::_find_nodes(Node* p_node) { + + + bool add=false; + Variant meta; + + if (enabler[ENABLER_FREEZE_BODIES]) { + + RigidBody2D *rb2d = p_node->cast_to(); + if (rb2d && ((rb2d->get_mode()==RigidBody2D::MODE_CHARACTER || (rb2d->get_mode()==RigidBody2D::MODE_RIGID && !rb2d->is_able_to_sleep())))) { + + + add=true; + meta=rb2d->get_mode(); + } + } + + if (enabler[ENABLER_PAUSE_ANIMATIONS]) { + + AnimationPlayer *ap = p_node->cast_to(); + if (ap) { + add=true; + } + + } + + if (add) { + + p_node->connect(SceneStringNames::get_singleton()->exit_scene,this,"_node_removed",varray(p_node),CONNECT_ONESHOT); + nodes[p_node]=meta; + _change_node_state(p_node,false); + } + + for(int i=0;iget_child_count();i++) { + Node *c = p_node->get_child(i); + if (c->get_filename()!=String()) + continue; //skip, instance + + _find_nodes(c); + } + +} + +void VisibilityEnabler2D::_notification(int p_what){ + + if (p_what==NOTIFICATION_ENTER_SCENE) { + + if (get_scene()->is_editor_hint()) + return; + + + Node *from = this; + //find where current scene starts + while(from->get_parent() && from->get_filename()==String()) + from=from->get_parent(); + + _find_nodes(from); + + } + + if (p_what==NOTIFICATION_EXIT_SCENE) { + + if (get_scene()->is_editor_hint()) + return; + + + Node *from = this; + //find where current scene starts + + for (Map::Element *E=nodes.front();E;E=E->next()) { + + if (!visible) + _change_node_state(E->key(),true); + E->key()->disconnect(SceneStringNames::get_singleton()->exit_scene,this,"_node_removed"); + } + + nodes.clear(); + + } +} + +void VisibilityEnabler2D::_change_node_state(Node* p_node,bool p_enabled) { + + ERR_FAIL_COND(!nodes.has(p_node)); + + { + RigidBody2D *rb = p_node->cast_to(); + if (rb) { + + if (p_enabled) { + RigidBody2D::Mode mode = RigidBody2D::Mode(nodes[p_node].operator int()); + //rb->set_mode(mode); + rb->set_active(true); + } else { + //rb->set_mode(RigidBody2D::MODE_STATIC); + rb->set_active(false); + } + } + } + + { + AnimationPlayer *ap=p_node->cast_to(); + + if (ap) { + + ap->set_active(p_enabled); + } + } + +} + + +void VisibilityEnabler2D::_node_removed(Node* p_node) { + + if (!visible) + _change_node_state(p_node,true); + //changed to one shot, not needed + //p_node->disconnect(SceneStringNames::get_singleton()->exit_scene,this,"_node_removed"); + nodes.erase(p_node); + +} + +void VisibilityEnabler2D::_bind_methods(){ + + ObjectTypeDB::bind_method(_MD("set_enabler","enabler","enabled"),&VisibilityEnabler2D::set_enabler); + ObjectTypeDB::bind_method(_MD("is_enabler_enabled","enabler"),&VisibilityEnabler2D::is_enabler_enabled); + ObjectTypeDB::bind_method(_MD("_node_removed"),&VisibilityEnabler2D::_node_removed); + + ADD_PROPERTYI( PropertyInfo(Variant::BOOL,"enabler/pause_animations"),_SCS("set_enabler"),_SCS("is_enabler_enabled"), ENABLER_PAUSE_ANIMATIONS ); + ADD_PROPERTYI( PropertyInfo(Variant::BOOL,"enabler/freeze_bodies"),_SCS("set_enabler"),_SCS("is_enabler_enabled"), ENABLER_FREEZE_BODIES); + + BIND_CONSTANT( ENABLER_FREEZE_BODIES ); + BIND_CONSTANT( ENABLER_PAUSE_ANIMATIONS ); + BIND_CONSTANT( ENABLER_MAX); +} + +void VisibilityEnabler2D::set_enabler(Enabler p_enabler,bool p_enable){ + + ERR_FAIL_INDEX(p_enabler,ENABLER_MAX); + enabler[p_enabler]=p_enable; + +} +bool VisibilityEnabler2D::is_enabler_enabled(Enabler p_enabler) const{ + + ERR_FAIL_INDEX_V(p_enabler,ENABLER_MAX,false); + return enabler[p_enabler]; + +} + +VisibilityEnabler2D::VisibilityEnabler2D() { + + for(int i=0;i viewports; + + Rect2 rect; + +protected: +friend class SpatialIndexer2D; + + void _enter_viewport(Viewport* p_viewport); + void _exit_viewport(Viewport* p_viewport); + + + virtual void _screen_enter() {} + virtual void _screen_exit() {} + + void _notification(int p_what); + static void _bind_methods(); +public: + + void set_rect(const Rect2& p_rect); + Rect2 get_rect() const; + + bool is_on_screen() const; + + virtual Rect2 get_item_rect() const; + + VisibilityNotifier2D(); +}; + + +class VisibilityEnabler2D : public VisibilityNotifier2D { + + OBJ_TYPE(VisibilityEnabler2D,VisibilityNotifier2D); +public: + + enum Enabler { + ENABLER_PAUSE_ANIMATIONS, + ENABLER_FREEZE_BODIES, + ENABLER_MAX + }; + +protected: + + virtual void _screen_enter(); + virtual void _screen_exit(); + + bool visible; + + void _find_nodes(Node* p_node); + + Map nodes; + void _node_removed(Node* p_node); + bool enabler[ENABLER_MAX]; + + void _change_node_state(Node* p_node,bool p_enabled); + + void _notification(int p_what); + static void _bind_methods(); + +public: + + void set_enabler(Enabler p_enabler,bool p_enable); + bool is_enabler_enabled(Enabler p_enabler) const; + + VisibilityEnabler2D(); + +}; + +VARIANT_ENUM_CAST(VisibilityEnabler2D::Enabler); + + +#endif // VISIBILITY_NOTIFIER_2D_H diff --git a/scene/3d/SCsub b/scene/3d/SCsub new file mode 100644 index 00000000000..6789851aab2 --- /dev/null +++ b/scene/3d/SCsub @@ -0,0 +1,14 @@ +Import('env') + + +print("V: "+env["disable_3d"]) +if (env["disable_3d"]=="yes"): + + env.scene_sources.append("3d/spatial.cpp") + env.scene_sources.append("3d/skeleton.cpp") +else: + env.add_source_files(env.scene_sources,"*.cpp") + +Export('env') + + diff --git a/scene/3d/area.cpp b/scene/3d/area.cpp new file mode 100644 index 00000000000..964a086cf6d --- /dev/null +++ b/scene/3d/area.cpp @@ -0,0 +1,317 @@ +/*************************************************************************/ +/* area.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 "area.h" +#include "scene/scene_string_names.h" +#include "servers/physics_server.h" +void Area::set_space_override_mode(SpaceOverride p_mode) { + + space_override=p_mode; + PhysicsServer::get_singleton()->area_set_space_override_mode(get_rid(),PhysicsServer::AreaSpaceOverrideMode(p_mode)); + + +} +Area::SpaceOverride Area::get_space_override_mode() const{ + + return space_override; +} + +void Area::set_gravity_is_point(bool p_enabled){ + + gravity_is_point=p_enabled; + PhysicsServer::get_singleton()->area_set_param(get_rid(),PhysicsServer::AREA_PARAM_GRAVITY_IS_POINT,p_enabled); + +} +bool Area::is_gravity_a_point() const{ + + return gravity_is_point; +} + +void Area::set_gravity_vector(const Vector3& p_vec){ + + gravity_vec=p_vec; + PhysicsServer::get_singleton()->area_set_param(get_rid(),PhysicsServer::AREA_PARAM_GRAVITY_VECTOR,p_vec); + +} +Vector3 Area::get_gravity_vector() const{ + + return gravity_vec; +} + +void Area::set_gravity(real_t p_gravity){ + + gravity=p_gravity; + PhysicsServer::get_singleton()->area_set_param(get_rid(),PhysicsServer::AREA_PARAM_GRAVITY,p_gravity); +} +real_t Area::get_gravity() const{ + + return gravity; +} + +void Area::set_density(real_t p_density){ + + density=p_density; + PhysicsServer::get_singleton()->area_set_param(get_rid(),PhysicsServer::AREA_PARAM_DENSITY,p_density); +} +real_t Area::get_density() const{ + + return density; +} + +void Area::set_priority(real_t p_priority){ + + priority=p_priority; + PhysicsServer::get_singleton()->area_set_param(get_rid(),PhysicsServer::AREA_PARAM_PRIORITY,p_priority); +} +real_t Area::get_priority() const{ + + return priority; +} + + +void Area::_body_enter_scene(ObjectID p_id) { + + Object *obj = ObjectDB::get_instance(p_id); + Node *node = obj ? obj->cast_to() : NULL; + ERR_FAIL_COND(!node); + + Map::Element *E=body_map.find(p_id); + ERR_FAIL_COND(!E); + ERR_FAIL_COND(E->get().in_scene); + + E->get().in_scene=true; + emit_signal(SceneStringNames::get_singleton()->body_enter,node); + for(int i=0;iget().shapes.size();i++) { + + emit_signal(SceneStringNames::get_singleton()->body_enter_shape,p_id,node,E->get().shapes[i].body_shape,E->get().shapes[i].area_shape); + } + +} + +void Area::_body_exit_scene(ObjectID p_id) { + + Object *obj = ObjectDB::get_instance(p_id); + Node *node = obj ? obj->cast_to() : NULL; + ERR_FAIL_COND(!node); + Map::Element *E=body_map.find(p_id); + ERR_FAIL_COND(!E); + ERR_FAIL_COND(!E->get().in_scene); + E->get().in_scene=false; + emit_signal(SceneStringNames::get_singleton()->body_exit,node); + for(int i=0;iget().shapes.size();i++) { + + emit_signal(SceneStringNames::get_singleton()->body_exit_shape,p_id,node,E->get().shapes[i].body_shape,E->get().shapes[i].area_shape); + } + +} + +void Area::_body_inout(int p_status,const RID& p_body, int p_instance, int p_body_shape,int p_area_shape) { + + bool body_in = p_status==PhysicsServer::AREA_BODY_ADDED; + ObjectID objid=p_instance; + + Object *obj = ObjectDB::get_instance(objid); + Node *node = obj ? obj->cast_to() : NULL; + + Map::Element *E=body_map.find(objid); + + ERR_FAIL_COND(!body_in && !E); + + if (body_in) { + if (!E) { + + E = body_map.insert(objid,BodyState()); + E->get().rc=0; + E->get().in_scene=node && node->is_inside_scene(); + if (node) { + node->connect(SceneStringNames::get_singleton()->enter_scene,this,SceneStringNames::get_singleton()->_body_enter_scene,make_binds(objid)); + node->connect(SceneStringNames::get_singleton()->exit_scene,this,SceneStringNames::get_singleton()->_body_exit_scene,make_binds(objid)); + if (E->get().in_scene) { + emit_signal(SceneStringNames::get_singleton()->body_enter,node); + } + } + + } + E->get().rc++; + if (node) + E->get().shapes.insert(ShapePair(p_body_shape,p_area_shape)); + + + if (E->get().in_scene) { + emit_signal(SceneStringNames::get_singleton()->body_enter_shape,objid,node,p_body_shape,p_area_shape); + } + + } else { + + E->get().rc--; + + if (node) + E->get().shapes.erase(ShapePair(p_body_shape,p_area_shape)); + + bool eraseit=false; + + if (E->get().rc==0) { + + if (node) { + node->disconnect(SceneStringNames::get_singleton()->enter_scene,this,SceneStringNames::get_singleton()->_body_enter_scene); + node->disconnect(SceneStringNames::get_singleton()->exit_scene,this,SceneStringNames::get_singleton()->_body_exit_scene); + if (E->get().in_scene) + emit_signal(SceneStringNames::get_singleton()->body_exit,obj); + + } + + eraseit=true; + + } + if (node && E->get().in_scene) { + emit_signal(SceneStringNames::get_singleton()->body_exit_shape,objid,obj,p_body_shape,p_area_shape); + } + + if (eraseit) + body_map.erase(E); + + } + +} + + +void Area::_clear_monitoring() { + + Map bmcopy = body_map; + body_map.clear(); + //disconnect all monitored stuff + + for (Map::Element *E=bmcopy.front();E;E=E->next()) { + + Object *obj = ObjectDB::get_instance(E->key()); + Node *node = obj ? obj->cast_to() : NULL; + ERR_CONTINUE(!node); + if (!E->get().in_scene) + continue; + + for(int i=0;iget().shapes.size();i++) { + + emit_signal(SceneStringNames::get_singleton()->body_exit_shape,E->key(),node,E->get().shapes[i].body_shape,E->get().shapes[i].area_shape); + } + + emit_signal(SceneStringNames::get_singleton()->body_exit,obj); + + node->disconnect(SceneStringNames::get_singleton()->enter_scene,this,SceneStringNames::get_singleton()->_body_enter_scene); + node->disconnect(SceneStringNames::get_singleton()->exit_scene,this,SceneStringNames::get_singleton()->_body_exit_scene); + } + +} +void Area::_notification(int p_what) { + + if (p_what==NOTIFICATION_EXIT_SCENE) { + _clear_monitoring(); + } +} + +void Area::set_enable_monitoring(bool p_enable) { + + if (p_enable==monitoring) + return; + + monitoring=p_enable; + + if (monitoring) { + + PhysicsServer::get_singleton()->area_set_monitor_callback(get_rid(),this,"_body_inout"); + } else { + PhysicsServer::get_singleton()->area_set_monitor_callback(get_rid(),NULL,StringName()); + _clear_monitoring(); + } +} + +bool Area::is_monitoring_enabled() const { + + return monitoring; +} + + +void Area::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("_body_enter_scene","id"),&Area::_body_enter_scene); + ObjectTypeDB::bind_method(_MD("_body_exit_scene","id"),&Area::_body_exit_scene); + + ObjectTypeDB::bind_method(_MD("set_space_override_mode","enable"),&Area::set_space_override_mode); + ObjectTypeDB::bind_method(_MD("get_space_override_mode"),&Area::get_space_override_mode); + + ObjectTypeDB::bind_method(_MD("set_gravity_is_point","enable"),&Area::set_gravity_is_point); + ObjectTypeDB::bind_method(_MD("is_gravity_a_point"),&Area::is_gravity_a_point); + + ObjectTypeDB::bind_method(_MD("set_gravity_vector","vector"),&Area::set_gravity_vector); + ObjectTypeDB::bind_method(_MD("get_gravity_vector"),&Area::get_gravity_vector); + + ObjectTypeDB::bind_method(_MD("set_gravity","gravity"),&Area::set_gravity); + ObjectTypeDB::bind_method(_MD("get_gravity"),&Area::get_gravity); + + ObjectTypeDB::bind_method(_MD("set_density","density"),&Area::set_density); + ObjectTypeDB::bind_method(_MD("get_density"),&Area::get_density); + + ObjectTypeDB::bind_method(_MD("set_priority","priority"),&Area::set_priority); + ObjectTypeDB::bind_method(_MD("get_priority"),&Area::get_priority); + + ObjectTypeDB::bind_method(_MD("set_enable_monitoring","enable"),&Area::set_enable_monitoring); + ObjectTypeDB::bind_method(_MD("is_monitoring_enabled"),&Area::is_monitoring_enabled); + + ObjectTypeDB::bind_method(_MD("_body_inout"),&Area::_body_inout); + + + ADD_SIGNAL( MethodInfo("body_enter_shape",PropertyInfo(Variant::INT,"body_id"),PropertyInfo(Variant::OBJECT,"body"),PropertyInfo(Variant::INT,"body_shape"),PropertyInfo(Variant::INT,"area_shape"))); + ADD_SIGNAL( MethodInfo("body_exit_shape",PropertyInfo(Variant::INT,"body_id"),PropertyInfo(Variant::OBJECT,"body"),PropertyInfo(Variant::INT,"body_shape"),PropertyInfo(Variant::INT,"area_shape"))); + ADD_SIGNAL( MethodInfo("body_enter",PropertyInfo(Variant::OBJECT,"body"))); + ADD_SIGNAL( MethodInfo("body_exit",PropertyInfo(Variant::OBJECT,"body"))); + + ADD_PROPERTY( PropertyInfo(Variant::INT,"space_override",PROPERTY_HINT_ENUM,"Disabled,Combine,Replace"),_SCS("set_space_override_mode"),_SCS("get_space_override_mode")); + ADD_PROPERTY( PropertyInfo(Variant::BOOL,"gravity_point"),_SCS("set_gravity_is_point"),_SCS("is_gravity_a_point")); + ADD_PROPERTY( PropertyInfo(Variant::VECTOR3,"gravity_vec"),_SCS("set_gravity_vector"),_SCS("get_gravity_vector")); + ADD_PROPERTY( PropertyInfo(Variant::REAL,"gravity",PROPERTY_HINT_RANGE,"-1024,1024,0.01"),_SCS("set_gravity"),_SCS("get_gravity")); + ADD_PROPERTY( PropertyInfo(Variant::REAL,"density",PROPERTY_HINT_RANGE,"0,1024,0.001"),_SCS("set_density"),_SCS("get_density")); + ADD_PROPERTY( PropertyInfo(Variant::INT,"priority",PROPERTY_HINT_RANGE,"0,128,1"),_SCS("set_priority"),_SCS("get_priority")); + ADD_PROPERTY( PropertyInfo(Variant::BOOL,"monitoring"),_SCS("set_enable_monitoring"),_SCS("is_monitoring_enabled")); + +} + +Area::Area() : CollisionObject(PhysicsServer::get_singleton()->area_create(),true) { + + space_override=SPACE_OVERRIDE_DISABLED; + set_gravity(9.8);; + set_gravity_vector(Vector3(0,-1,0)); + gravity_is_point=false; + density=0.1; + priority=0; + monitoring=false; + +} + +Area::~Area() { + + +} diff --git a/scene/3d/area.h b/scene/3d/area.h new file mode 100644 index 00000000000..79e98f9dabf --- /dev/null +++ b/scene/3d/area.h @@ -0,0 +1,121 @@ +/*************************************************************************/ +/* area.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 AREA_H +#define AREA_H + +#include "scene/3d/collision_object.h" +#include "vset.h" + +class Area : public CollisionObject { + + OBJ_TYPE( Area, CollisionObject ); +public: + + enum SpaceOverride { + SPACE_OVERRIDE_DISABLED, + SPACE_OVERRIDE_COMBINE, + SPACE_OVERRIDE_REPLACE + }; +private: + + + SpaceOverride space_override; + Vector3 gravity_vec; + real_t gravity; + bool gravity_is_point; + real_t density; + int priority; + bool monitoring; + + void _body_inout(int p_status,const RID& p_body, int p_instance, int p_body_shape,int p_area_shape); + + void _body_enter_scene(ObjectID p_id); + void _body_exit_scene(ObjectID p_id); + + struct ShapePair { + + int body_shape; + int area_shape; + bool operator<(const ShapePair& p_sp) const { + if (body_shape==p_sp.body_shape) + return area_shape < p_sp.area_shape; + else + return body_shape < p_sp.body_shape; + } + + ShapePair() {} + ShapePair(int p_bs, int p_as) { body_shape=p_bs; area_shape=p_as; } + }; + + struct BodyState { + + int rc; + bool in_scene; + VSet shapes; + }; + + Map body_map; + void _clear_monitoring(); + + +protected: + + void _notification(int p_what); + static void _bind_methods(); +public: + + void set_space_override_mode(SpaceOverride p_mode); + SpaceOverride get_space_override_mode() const; + + void set_gravity_is_point(bool p_enabled); + bool is_gravity_a_point() const; + + void set_gravity_vector(const Vector3& p_vec); + Vector3 get_gravity_vector() const; + + void set_gravity(real_t p_gravity); + real_t get_gravity() const; + + void set_density(real_t p_density); + real_t get_density() const; + + void set_priority(real_t p_priority); + real_t get_priority() const; + + void set_enable_monitoring(bool p_enable); + bool is_monitoring_enabled() const; + + + Area(); + ~Area(); +}; + +VARIANT_ENUM_CAST(Area::SpaceOverride); + +#endif // AREA__H diff --git a/scene/3d/body_shape.cpp b/scene/3d/body_shape.cpp new file mode 100644 index 00000000000..b291ce7c72c --- /dev/null +++ b/scene/3d/body_shape.cpp @@ -0,0 +1,829 @@ +/*************************************************************************/ +/* body_shape.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 "body_shape.h" +#include "servers/visual_server.h" +#include "scene/resources/sphere_shape.h" +#include "scene/resources/ray_shape.h" +#include "scene/resources/box_shape.h" +#include "scene/resources/capsule_shape.h" +//#include "scene/resources/cylinder_shape.h" +#include "scene/resources/convex_polygon_shape.h" +#include "scene/resources/concave_polygon_shape.h" +#include "scene/resources/height_map_shape.h" +#include "scene/resources/plane_shape.h" +#include "mesh_instance.h" +#include "physics_body.h" +#include "quick_hull.h" + +void CollisionShape::_update_body() { + + + + if (get_parent() && get_parent()->cast_to()) + get_parent()->cast_to()->_update_shapes_from_children(); + +} + +void CollisionShape::make_convex_from_brothers() { + + Node *p = get_parent(); + if (!p) + return; + + for(int i=0;iget_child_count();i++) { + + Node *n = p->get_child(i); + if (n->cast_to()) { + + MeshInstance *mi=n->cast_to(); + Ref m = mi->get_mesh(); + if (m.is_valid()) { + + Ref s = m->create_convex_shape(); + set_shape(s); + } + } + } + +} + + +void CollisionShape::_update_indicator() { + + while (VisualServer::get_singleton()->mesh_get_surface_count(indicator)) + VisualServer::get_singleton()->mesh_remove_surface(indicator,0); + + if (shape.is_null()) + return; + + DVector points; + DVector normals; + + VS::PrimitiveType pt = VS::PRIMITIVE_TRIANGLES; + + if (shape->cast_to()) { + + RayShape *rs = shape->cast_to(); + points.push_back(Vector3()); + points.push_back(Vector3(0,0,rs->get_length())); + pt = VS::PRIMITIVE_LINES; + } else if (shape->cast_to()) { + +// VisualServer *vs=VisualServer::get_singleton(); + SphereShape *shapeptr=shape->cast_to(); + + + Color col(0.4,1.0,1.0,0.5); + + int lats=6; + int lons=12; + float size=shapeptr->get_radius(); + + + for(int i = 1; i <= lats; i++) { + double lat0 = Math_PI * (-0.5 + (double) (i - 1) / lats); + double z0 = Math::sin(lat0); + double zr0 = Math::cos(lat0); + + double lat1 = Math_PI * (-0.5 + (double) i / lats); + double z1 = Math::sin(lat1); + double zr1 = Math::cos(lat1); + + for(int j = lons; j >= 1; j--) { + + double lng0 = 2 * Math_PI * (double) (j - 1) / lons; + double x0 = Math::cos(lng0); + double y0 = Math::sin(lng0); + + double lng1 = 2 * Math_PI * (double) (j) / lons; + double x1 = Math::cos(lng1); + double y1 = Math::sin(lng1); + + Vector3 v4=Vector3(x0 * zr0, z0, y0 *zr0)*size; + Vector3 v3=Vector3(x0 * zr1, z1, y0 *zr1)*size; + Vector3 v2=Vector3(x1 * zr1, z1, y1 *zr1)*size; + Vector3 v1=Vector3(x1 * zr0, z0, y1 *zr0)*size; + + Vector line; + line.push_back(v1); + line.push_back(v2); + line.push_back(v3); + line.push_back(v4); + + + points.push_back(v1); + points.push_back(v2); + points.push_back(v3); + + points.push_back(v1); + points.push_back(v3); + points.push_back(v4); + + normals.push_back(v1.normalized()); + normals.push_back(v2.normalized()); + normals.push_back(v3.normalized()); + + normals.push_back(v1.normalized()); + normals.push_back(v3.normalized()); + normals.push_back(v4.normalized()); + + } + } + } else if (shape->cast_to()) { + + BoxShape *shapeptr=shape->cast_to(); + + 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); + } + } + Vector3 normal; + normal[i%3]=(i>=3?-1:1); + + for(int j=0;j<4;j++) + face_points[j]*=shapeptr->get_extents(); + + points.push_back(face_points[0]); + points.push_back(face_points[1]); + points.push_back(face_points[2]); + + points.push_back(face_points[0]); + points.push_back(face_points[2]); + points.push_back(face_points[3]); + + for(int n=0;n<6;n++) + normals.push_back(normal); + + } + + } else if (shape->cast_to()) { + + ConvexPolygonShape *shapeptr=shape->cast_to(); + + Geometry::MeshData md; + QuickHull::build(Variant(shapeptr->get_points()),md); + + for(int i=0;icast_to()) { + + ConcavePolygonShape *shapeptr=shape->cast_to(); + + points = shapeptr->get_faces(); + for(int i=0;icast_to()) { + + CapsuleShape *shapeptr=shape->cast_to(); + + DVector planes = Geometry::build_capsule_planes(shapeptr->get_radius(), shapeptr->get_height()/2.0, 12, Vector3::AXIS_Z); + Geometry::MeshData md = Geometry::build_convex_mesh(planes); + + for(int i=0;icast_to()) { + + PlaneShape *shapeptr=shape->cast_to(); + + Plane p = shapeptr->get_plane(); + Vector3 n1 = p.get_any_perpendicular_normal(); + Vector3 n2 = p.normal.cross(n1).normalized(); + + Vector3 pface[4]={ + p.normal*p.d+n1*100.0+n2*100.0, + p.normal*p.d+n1*100.0+n2*-100.0, + p.normal*p.d+n1*-100.0+n2*-100.0, + p.normal*p.d+n1*-100.0+n2*100.0, + }; + + points.push_back(pface[0]); + points.push_back(pface[1]); + points.push_back(pface[2]); + + points.push_back(pface[0]); + points.push_back(pface[2]); + points.push_back(pface[3]); + + normals.push_back(p.normal); + normals.push_back(p.normal); + normals.push_back(p.normal); + normals.push_back(p.normal); + normals.push_back(p.normal); + normals.push_back(p.normal); + + } + + if (!points.size()) + return; + RID material = VisualServer::get_singleton()->fixed_material_create(); + VisualServer::get_singleton()->fixed_material_set_param(material,VS::FIXED_MATERIAL_PARAM_DIFFUSE,Color(0,0.6,0.7,0.3)); + VisualServer::get_singleton()->fixed_material_set_param(material,VS::FIXED_MATERIAL_PARAM_EMISSION,0.7); + if (normals.size()==0) + VisualServer::get_singleton()->material_set_flag(material,VS::MATERIAL_FLAG_UNSHADED,true); + VisualServer::get_singleton()->material_set_flag(material,VS::MATERIAL_FLAG_DOUBLE_SIDED,true); + Array d; + d.resize(VS::ARRAY_MAX); + d[VS::ARRAY_VERTEX]=points; + if (normals.size()) + d[VS::ARRAY_NORMAL]=normals; + VisualServer::get_singleton()->mesh_add_surface(indicator,pt,d); + VisualServer::get_singleton()->mesh_surface_set_material(indicator,0,material,true); + +} + + +void CollisionShape::_add_to_collision_object(Object* p_cshape) { + + CollisionObject *co=p_cshape->cast_to(); + ERR_FAIL_COND(!co); + + if (shape.is_valid()) { + + co->add_shape(shape,get_transform()); + if (trigger) + co->set_shape_as_trigger( co->get_shape_count() -1, true ); + } +} + +void CollisionShape::_notification(int p_what) { + + switch(p_what) { + + case NOTIFICATION_ENTER_WORLD: { + indicator_instance = VisualServer::get_singleton()->instance_create2(indicator,get_world()->get_scenario()); + } break; + case NOTIFICATION_TRANSFORM_CHANGED: { + VisualServer::get_singleton()->instance_set_transform(indicator_instance,get_global_transform()); + if (updating_body) { + _update_body(); + } + } break; + case NOTIFICATION_EXIT_WORLD: { + if (indicator_instance.is_valid()) { + VisualServer::get_singleton()->free(indicator_instance); + indicator_instance=RID(); + } + } break; + case NOTIFICATION_UNPARENTED: { + if (updating_body) + _update_body(); + } break; + case NOTIFICATION_PARENTED: { + if (updating_body) + _update_body(); + } break; + } +} + + +void CollisionShape::resource_changed(RES res) { + + update_gizmo(); + + +} + +void CollisionShape::_bind_methods() { + + //not sure if this should do anything + ObjectTypeDB::bind_method(_MD("resource_changed"),&CollisionShape::resource_changed); + ObjectTypeDB::bind_method(_MD("set_shape","shape"),&CollisionShape::set_shape); + ObjectTypeDB::bind_method(_MD("get_shape"),&CollisionShape::get_shape); + ObjectTypeDB::bind_method(_MD("_add_to_collision_object"),&CollisionShape::_add_to_collision_object); + ObjectTypeDB::bind_method(_MD("set_trigger","enable"),&CollisionShape::set_trigger); + ObjectTypeDB::bind_method(_MD("is_trigger"),&CollisionShape::is_trigger); + ObjectTypeDB::bind_method(_MD("make_convex_from_brothers"),&CollisionShape::make_convex_from_brothers); + ObjectTypeDB::set_method_flags("CollisionShape","make_convex_from_brothers",METHOD_FLAGS_DEFAULT|METHOD_FLAG_EDITOR); + + + ADD_PROPERTY( PropertyInfo( Variant::OBJECT, "shape", PROPERTY_HINT_RESOURCE_TYPE, "Shape"), _SCS("set_shape"), _SCS("get_shape")); + ADD_PROPERTY(PropertyInfo(Variant::BOOL,"trigger"),_SCS("set_trigger"),_SCS("is_trigger")); +} + + +void CollisionShape::set_shape(const Ref &p_shape) { + + if (!shape.is_null()) + shape->unregister_owner(this); + shape=p_shape; + if (!shape.is_null()) + shape->register_owner(this); + update_gizmo(); + if (updating_body) + _update_body(); +} + +Ref CollisionShape::get_shape() const { + + return shape; +} + + +void CollisionShape::set_updating_body(bool p_update) { + updating_body=p_update; +} + +bool CollisionShape::is_updating_body() const { + + return updating_body; +} + +void CollisionShape::set_trigger(bool p_trigger) { + + trigger=p_trigger; + if (updating_body) + _update_body(); +} + +bool CollisionShape::is_trigger() const{ + + return trigger; +} + +CollisionShape::CollisionShape() { + + indicator = VisualServer::get_singleton()->mesh_create(); + updating_body=true; + trigger=false; +} + +CollisionShape::~CollisionShape() { + if (!shape.is_null()) + shape->unregister_owner(this); + VisualServer::get_singleton()->free(indicator); +} + +#if 0 +#include "body_volume.h" + +#include "scene/3d/physics_body.h" +#include "geometry.h" + +#define ADD_TRIANGLE( m_a, m_b, m_c, m_color)\ +{\ + Vector points;\ + points.resize(3);\ + points[0]=m_a;\ + points[1]=m_b;\ + points[2]=m_c;\ + Vector colors;\ + colors.resize(3);\ + colors[0]=m_color;\ + colors[1]=m_color;\ + colors[2]=m_color;\ + vs->poly_add_primitive(p_indicator,points,Vector(),colors,Vector());\ +} + + +void CollisionShape::_notification(int p_what) { + + switch (p_what) { + case NOTIFICATION_ENTER_SCENE: { + + + if (get_root_node()->get_editor() && !indicator.is_valid()) { + + indicator=VisualServer::get_singleton()->poly_create(); + RID mat=VisualServer::get_singleton()->fixed_material_create(); + VisualServer::get_singleton()->material_set_flag( mat, VisualServer::MATERIAL_FLAG_UNSHADED, true ); + VisualServer::get_singleton()->material_set_flag( mat, VisualServer::MATERIAL_FLAG_WIREFRAME, true ); + VisualServer::get_singleton()->material_set_flag( mat, VisualServer::MATERIAL_FLAG_DOUBLE_SIDED, true ); + VisualServer::get_singleton()->material_set_line_width( mat, 3 ); + + VisualServer::get_singleton()->poly_set_material(indicator,mat,true); + + update_indicator(indicator); + } + + if (indicator.is_valid()) { + + indicator_instance=VisualServer::get_singleton()->instance_create2(indicator,get_world()->get_scenario()); + VisualServer::get_singleton()->instance_attach_object_instance_ID(indicator_instance,get_instance_ID()); + } + volume_changed(); + } break; + case NOTIFICATION_EXIT_SCENE: { + + if (indicator_instance.is_valid()) { + + VisualServer::get_singleton()->free(indicator_instance); + } + volume_changed(); + } break; + case NOTIFICATION_TRANSFORM_CHANGED: { + + if (indicator_instance.is_valid()) { + + VisualServer::get_singleton()->instance_set_transform(indicator_instance,get_global_transform()); + } + volume_changed(); + } break; + default: {} + } +} + +void CollisionShape::volume_changed() { + + if (indicator.is_valid()) + update_indicator(indicator); + + Object *parent=get_parent(); + if (!parent) + return; + PhysicsBody *physics_body=parent->cast_to(); + + ERR_EXPLAIN("CollisionShape parent is not of type PhysicsBody"); + ERR_FAIL_COND(!physics_body); + + physics_body->recompute_child_volumes(); + +} + +RID CollisionShape::_get_visual_instance_rid() const { + + return indicator_instance; + +} + +void CollisionShape::_bind_methods() { + + ObjectTypeDB::bind_method("_get_visual_instance_rid",&CollisionShape::_get_visual_instance_rid); +} + +CollisionShape::CollisionShape() { + +} + +CollisionShape::~CollisionShape() { + + if (indicator.is_valid()) { + + VisualServer::get_singleton()->free(indicator); + } + +} + +void CollisionShapeSphere::_set(const String& p_name, const Variant& p_value) { + + if (p_name=="radius") { + radius=p_value; + volume_changed(); + } + +} + +Variant CollisionShapeSphere::_get(const String& p_name) const { + + if (p_name=="radius") { + return radius; + } + + return Variant(); +} + +void CollisionShapeSphere::_get_property_list( List *p_list) const { + + p_list->push_back( PropertyInfo(Variant::REAL,"radius",PROPERTY_HINT_RANGE,"0.01,16384,0.01") ); +} + +void CollisionShapeSphere::update_indicator(RID p_indicator) { + + VisualServer *vs=VisualServer::get_singleton(); + + vs->poly_clear(p_indicator); + Color col(0.4,1.0,1.0,0.5); + + int lats=6; + int lons=12; + float size=radius; + + for(int i = 1; i <= lats; i++) { + double lat0 = Math_PI * (-0.5 + (double) (i - 1) / lats); + double z0 = Math::sin(lat0); + double zr0 = Math::cos(lat0); + + double lat1 = Math_PI * (-0.5 + (double) i / lats); + double z1 = Math::sin(lat1); + double zr1 = Math::cos(lat1); + + for(int j = lons; j >= 1; j--) { + + double lng0 = 2 * Math_PI * (double) (j - 1) / lons; + double x0 = Math::cos(lng0); + double y0 = Math::sin(lng0); + + double lng1 = 2 * Math_PI * (double) (j) / lons; + double x1 = Math::cos(lng1); + double y1 = Math::sin(lng1); + + Vector3 v4=Vector3(x0 * zr0, z0, y0 *zr0)*size; + Vector3 v3=Vector3(x0 * zr1, z1, y0 *zr1)*size; + Vector3 v2=Vector3(x1 * zr1, z1, y1 *zr1)*size; + Vector3 v1=Vector3(x1 * zr0, z0, y1 *zr0)*size; + + Vector line; + line.push_back(v1); + line.push_back(v2); + line.push_back(v3); + line.push_back(v4); + + Vector cols; + cols.push_back(col); + cols.push_back(col); + cols.push_back(col); + cols.push_back(col); + + + VisualServer::get_singleton()->poly_add_primitive(p_indicator,line,Vector(),cols,Vector()); + } + } +} + +void CollisionShapeSphere::append_to_volume(Ref p_volume) { + + p_volume->add_sphere_shape(radius,get_transform()); +} + + +CollisionShapeSphere::CollisionShapeSphere() { + + radius=1.0; +} + +/* BOX */ + + +void CollisionShapeBox::_set(const String& p_name, const Variant& p_value) { + + if (p_name=="half_extents") { + half_extents=p_value; + volume_changed(); + } + +} + +Variant CollisionShapeBox::_get(const String& p_name) const { + + if (p_name=="half_extents") { + return half_extents; + } + + return Variant(); +} + +void CollisionShapeBox::_get_property_list( List *p_list) const { + + p_list->push_back( PropertyInfo(Variant::VECTOR3,"half_extents" ) ); +} + + +void CollisionShapeBox::update_indicator(RID p_indicator) { + + VisualServer *vs=VisualServer::get_singleton(); + + vs->poly_clear(p_indicator); + Color col(0.4,1.0,1.0,0.5); + + + 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); + } + } + + for(int j=0;j<4;j++) + face_points[i]*=half_extents; + + ADD_TRIANGLE(face_points[0],face_points[1],face_points[2],col); + ADD_TRIANGLE(face_points[2],face_points[3],face_points[0],col); + + } +} + +void CollisionShapeBox::append_to_volume(Ref p_volume) { + + p_volume->add_box_shape(half_extents,get_transform()); +} + + +CollisionShapeBox::CollisionShapeBox() { + + half_extents=Vector3(1,1,1); +} + +/* CYLINDER */ + + +void CollisionShapeCylinder::_set(const String& p_name, const Variant& p_value) { + + if (p_name=="radius") { + radius=p_value; + volume_changed(); + } + if (p_name=="height") { + height=p_value; + volume_changed(); + } + +} + +Variant CollisionShapeCylinder::_get(const String& p_name) const { + + if (p_name=="radius") { + return radius; + } + if (p_name=="height") { + return height; + } + return Variant(); +} + +void CollisionShapeCylinder::_get_property_list( List *p_list) const { + + p_list->push_back( PropertyInfo(Variant::REAL,"radius",PROPERTY_HINT_RANGE,"0.01,16384,0.01") ); + p_list->push_back( PropertyInfo(Variant::REAL,"height",PROPERTY_HINT_RANGE,"0.01,16384,0.01") ); +} + + +void CollisionShapeCylinder::update_indicator(RID p_indicator) { + + VisualServer *vs=VisualServer::get_singleton(); + + vs->poly_clear(p_indicator); + Color col(0.4,1.0,1.0,0.5); + + DVector planes = Geometry::build_cylinder_planes(radius, height, 12, Vector3::AXIS_Z); + Geometry::MeshData md = Geometry::build_convex_mesh(planes); + + for(int i=0;i p_volume) { + + p_volume->add_cylinder_shape(radius,height*2.0,get_transform()); +} + + +CollisionShapeCylinder::CollisionShapeCylinder() { + + height=1; + radius=1; +} + +/* CAPSULE */ + + +void CollisionShapeCapsule::_set(const String& p_name, const Variant& p_value) { + + if (p_name=="radius") { + radius=p_value; + volume_changed(); + } + + if (p_name=="height") { + height=p_value; + volume_changed(); + } + +} + +Variant CollisionShapeCapsule::_get(const String& p_name) const { + + if (p_name=="radius") { + return radius; + } + if (p_name=="height") { + return height; + } + return Variant(); +} + +void CollisionShapeCapsule::_get_property_list( List *p_list) const { + + p_list->push_back( PropertyInfo(Variant::REAL,"radius",PROPERTY_HINT_RANGE,"0.01,16384,0.01") ); + p_list->push_back( PropertyInfo(Variant::REAL,"height",PROPERTY_HINT_RANGE,"0.01,16384,0.01") ); +} + + +void CollisionShapeCapsule::update_indicator(RID p_indicator) { + + VisualServer *vs=VisualServer::get_singleton(); + + vs->poly_clear(p_indicator); + Color col(0.4,1.0,1.0,0.5); + + DVector planes = Geometry::build_capsule_planes(radius, height, 12, 3, Vector3::AXIS_Z); + Geometry::MeshData md = Geometry::build_convex_mesh(planes); + + for(int i=0;i p_volume) { + + + p_volume->add_capsule_shape(radius,height,get_transform()); +} + + +CollisionShapeCapsule::CollisionShapeCapsule() { + + height=1; + radius=1; +} +#endif diff --git a/scene/3d/body_shape.h b/scene/3d/body_shape.h new file mode 100644 index 00000000000..d1cb229f702 --- /dev/null +++ b/scene/3d/body_shape.h @@ -0,0 +1,80 @@ +/*************************************************************************/ +/* body_shape.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 COLLISION_SHAPE_H +#define COLLISION_SHAPE_H + +#include "scene/3d/spatial.h" +#include "scene/resources/shape.h" + +class CollisionShape : public Spatial { + + OBJ_TYPE( CollisionShape, Spatial ); + OBJ_CATEGORY("3D Physics Nodes"); + + RID _get_visual_instance_rid() const; + + Ref shape; + + void _update_indicator(); + + RID material; + RID indicator; + RID indicator_instance; + + void resource_changed(RES res); + + bool updating_body; + bool trigger; + + void _update_body(); + void _add_to_collision_object(Object* p_cshape); +protected: + + void _notification(int p_what); + static void _bind_methods(); + +public: + + void make_convex_from_brothers(); + + void set_shape(const Ref &p_shape); + Ref get_shape() const; + + void set_updating_body(bool p_update); + bool is_updating_body() const; + + void set_trigger(bool p_trigger); + bool is_trigger() const; + + CollisionShape(); + ~CollisionShape(); +}; + +#endif // BODY_VOLUME_H + diff --git a/scene/3d/bone_attachment.cpp b/scene/3d/bone_attachment.cpp new file mode 100644 index 00000000000..cbc4abb7a98 --- /dev/null +++ b/scene/3d/bone_attachment.cpp @@ -0,0 +1,138 @@ +/*************************************************************************/ +/* bone_attachment.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 "bone_attachment.h" + +bool BoneAttachment::_get(const StringName& p_name,Variant &r_ret) const { + + if (String(p_name)=="bone_name") { + + r_ret=get_bone_name(); + return true; + } + + return false; +} +bool BoneAttachment::_set(const StringName& p_name, const Variant& p_value){ + + if (String(p_name)=="bone_name") { + + set_bone_name(p_value); + return true; + } + + return false; +} +void BoneAttachment::_get_property_list( List* p_list ) const{ + + Skeleton *parent=NULL; + if(get_parent()) + parent=get_parent()->cast_to(); + + if (parent) { + + String names; + for(int i=0;iget_bone_count();i++) { + if(i>0) + names+=","; + names+=parent->get_bone_name(i); + } + + p_list->push_back(PropertyInfo(Variant::STRING,"bone_name",PROPERTY_HINT_ENUM,names)); + } else { + + p_list->push_back(PropertyInfo(Variant::STRING,"bone_name")); + + } + +} + + +void BoneAttachment::_check_bind() { + + if (get_parent() && get_parent()->cast_to()) { + Skeleton *sk = get_parent()->cast_to(); + int idx = sk->find_bone(bone_name); + if (idx!=-1) { + sk->bind_child_node_to_bone(idx,this);; + bound=true; + } + } +} + +void BoneAttachment::_check_unbind() { + + if (bound) { + + if (get_parent() && get_parent()->cast_to()) { + Skeleton *sk = get_parent()->cast_to(); + int idx = sk->find_bone(bone_name); + if (idx!=-1) { + sk->unbind_child_node_from_bone(idx,this);; + } + } + bound=false; + } +} + +void BoneAttachment::set_bone_name(const String& p_name) { + + if (is_inside_scene()) + _check_unbind(); + + bone_name=p_name; + + if (is_inside_scene()) + _check_bind(); +} + +String BoneAttachment::get_bone_name() const{ + + return bone_name; +} + +void BoneAttachment::_notification(int p_what) { + + switch(p_what) { + + case NOTIFICATION_ENTER_SCENE: { + + _check_bind(); + } break; + case NOTIFICATION_EXIT_SCENE: { + + _check_unbind(); + } break; + } +} + +BoneAttachment::BoneAttachment() +{ + bound=false; + +} diff --git a/scene/3d/bone_attachment.h b/scene/3d/bone_attachment.h new file mode 100644 index 00000000000..5043b40fa84 --- /dev/null +++ b/scene/3d/bone_attachment.h @@ -0,0 +1,58 @@ +/*************************************************************************/ +/* bone_attachment.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 BONE_ATTACHMENT_H +#define BONE_ATTACHMENT_H + +#include "scene/3d/skeleton.h" + +class BoneAttachment : public Spatial { + + OBJ_TYPE(BoneAttachment,Spatial); + + bool bound; + String bone_name; + + void _check_bind(); + void _check_unbind(); +protected: + + bool _get(const StringName& p_name,Variant &r_ret) const; + bool _set(const StringName& p_name, const Variant& p_value); + void _get_property_list( List* p_list ) const; + void _notification(int p_what); + +public: + + void set_bone_name(const String& p_name); + String get_bone_name() const; + + BoneAttachment(); +}; + +#endif // BONE_ATTACHMENT_H diff --git a/scene/3d/camera.cpp b/scene/3d/camera.cpp new file mode 100644 index 00000000000..ecdfc8a7f91 --- /dev/null +++ b/scene/3d/camera.cpp @@ -0,0 +1,727 @@ +/*************************************************************************/ +/* camera.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 "camera.h" + +#include "camera_matrix.h" +#include "scene/resources/material.h" +#include "scene/resources/surface_tool.h" + + +void Camera::_update_audio_listener_state() { + + +} + +void Camera::_request_camera_update() { + + _update_camera(); +} + +void Camera::_update_camera_mode() { + + + force_change=true; + switch(mode) { + case PROJECTION_PERSPECTIVE: { + + + set_perspective(fov,near,far); + + } break; + case PROJECTION_ORTHOGONAL: { + set_orthogonal(size,near,far); + } break; + + } + +} + +bool Camera::_set(const StringName& p_name, const Variant& p_value) { + + bool changed_all=false; + if (p_name=="projection") { + + int proj = p_value; + if (proj==PROJECTION_PERSPECTIVE) + mode=PROJECTION_PERSPECTIVE; + if (proj==PROJECTION_ORTHOGONAL) + mode=PROJECTION_ORTHOGONAL; + + changed_all=true; + } else if (p_name=="fov") + fov=p_value; + else if (p_name=="size") + size=p_value; + else if (p_name=="near") + near=p_value; + else if (p_name=="far") + far=p_value; + else if (p_name=="vaspect") + set_use_vertical_aspect(p_value); + else if (p_name=="current") { + if (p_value.operator bool()) { + make_current(); + } else { + clear_current(); + } + } else if (p_name=="visible_layers") { + set_visible_layers(p_value); + } else if (p_name=="environment") { + set_environment(p_value); + } else + return false; + + _update_camera_mode(); + if (changed_all) + _change_notify(); + return true; + +} +bool Camera::_get(const StringName& p_name,Variant &r_ret) const { + + if (p_name=="projection") { + r_ret= mode; + } else if (p_name=="fov") + r_ret= fov; + else if (p_name=="size") + r_ret= size; + else if (p_name=="near") + r_ret= near; + else if (p_name=="far") + r_ret= far; + else if (p_name=="vaspect") + r_ret= vaspect; + else if (p_name=="current") { + + if (is_inside_scene() && get_scene()->is_editor_hint()) { + r_ret=current; + } else { + r_ret=is_current(); + } + } else if (p_name=="visible_layers") { + r_ret=get_visible_layers(); + } else if (p_name=="environment") { + r_ret=get_environment(); + } else + return false; + + return true; +} + +void Camera::_get_property_list( List *p_list) const { + + p_list->push_back( PropertyInfo( Variant::INT, "projection", PROPERTY_HINT_ENUM, "Perspective,Orthogonal") ); + + switch(mode) { + + case PROJECTION_PERSPECTIVE: { + + p_list->push_back( PropertyInfo( Variant::REAL, "fov" , PROPERTY_HINT_RANGE, "1,89,0.1") ); + + } break; + case PROJECTION_ORTHOGONAL: { + + p_list->push_back( PropertyInfo( Variant::REAL, "size" , PROPERTY_HINT_RANGE, "1,16384,0.01" ) ); + } break; + + } + + p_list->push_back( PropertyInfo( Variant::REAL, "near" , PROPERTY_HINT_EXP_RANGE, "0.01,4096.0,0.01") ); + p_list->push_back( PropertyInfo( Variant::REAL, "far" , PROPERTY_HINT_EXP_RANGE, "0.01,4096.0,0.01") ); + p_list->push_back( PropertyInfo( Variant::BOOL, "vaspect") ); + p_list->push_back( PropertyInfo( Variant::BOOL, "current" ) ); + p_list->push_back( PropertyInfo( Variant::INT, "visible_layers",PROPERTY_HINT_ALL_FLAGS ) ); + p_list->push_back( PropertyInfo( Variant::OBJECT, "environment",PROPERTY_HINT_RESOURCE_TYPE,"Environment" ) ); + +} + +void Camera::_update_camera() { + + Transform tr = get_camera_transform(); + VisualServer::get_singleton()->camera_set_transform( camera, tr ); + +// here goes listener stuff +// if (viewport_ptr && is_inside_scene() && is_current()) +// viewport_ptr->_camera_transform_changed_notify(); + + if (is_inside_scene() && is_current()) { + if (viewport_ptr) { + viewport_ptr->_camera_transform_changed_notify(); + } + } + + if (is_current() && get_world().is_valid()) { + get_world()->_update_camera(this); + } + + +} + +void Camera::_notification(int p_what) { + + switch(p_what) { + + case NOTIFICATION_ENTER_WORLD: { + + viewport_ptr=NULL; + + { //find viewport stuff + Node *parent=get_parent(); + + while(parent) { + + Viewport* viewport = parent->cast_to(); + + if (viewport) { + viewport_ptr=viewport; + break; + } + parent=parent->get_parent(); + } + + } + + if (viewport_ptr) + viewport_ptr->cameras.insert(this); + if (current) + make_current(); + + } break; + case NOTIFICATION_TRANSFORM_CHANGED: { + + _request_camera_update(); + } break; + case NOTIFICATION_EXIT_WORLD: { + + if (is_current()) { + clear_current(); + current=true; //keep it true + + } else { + current=false; + } + if (viewport_ptr) + viewport_ptr->cameras.erase(this); + viewport_ptr=NULL; + + } break; + case NOTIFICATION_BECAME_CURRENT: { + if (get_world().is_valid()) { + get_world()->_register_camera(this); + } + } break; + case NOTIFICATION_LOST_CURRENT: { + if (get_world().is_valid()) { + get_world()->_remove_camera(this); + } + } break; + + + } + +} + + +Transform Camera::get_camera_transform() const { + + return get_global_transform(); +} + +void Camera::set_perspective(float p_fovy_degrees, float p_z_near, float p_z_far) { + + if (!force_change && fov==p_fovy_degrees && p_z_near==near && p_z_far==far && mode==PROJECTION_PERSPECTIVE) + return; + + fov=p_fovy_degrees; + near=p_z_near; + far=p_z_far; + mode=PROJECTION_PERSPECTIVE; + + VisualServer::get_singleton()->camera_set_perspective(camera,fov,near,far); + update_gizmo(); + force_change=false; +} +void Camera::set_orthogonal(float p_size, float p_z_near, float p_z_far) { + + if (!force_change && size==p_size && p_z_near==near && p_z_far==far && mode==PROJECTION_ORTHOGONAL) + return; + + size = p_size; + + near=p_z_near; + far=p_z_far; + mode=PROJECTION_ORTHOGONAL; + force_change=false; + + VisualServer::get_singleton()->camera_set_orthogonal(camera,size,near,far); + update_gizmo(); +} + +RID Camera::get_camera() const { + + return camera; +}; + +void Camera::make_current() { + + current=true; + + if (!is_inside_scene()) + return; + + if (viewport_ptr) { + viewport_ptr->_set_camera(this); + } + + //get_scene()->call_group(SceneMainLoop::GROUP_CALL_REALTIME,camera_group,"_camera_make_current",this); +} + +void Camera::clear_current() { + + current=false; + if (!is_inside_scene()) + return; + + if (viewport_ptr) { + if (viewport_ptr->get_camera()==this) + viewport_ptr->_set_camera(NULL); + } + +} + +bool Camera::is_current() const { + + if (is_inside_scene()) { + if (viewport_ptr) + return viewport_ptr->get_camera()==this; + } else + return current; + + return false; +} + + +bool Camera::_can_gizmo_scale() const { + + return false; +} + + +RES Camera::_get_gizmo_geometry() const { + + + Ref surface_tool( memnew( SurfaceTool )); + + Ref mat( memnew( FixedMaterial )); + + mat->set_parameter( FixedMaterial::PARAM_DIFFUSE,Color(1.0,0.5,1.0,0.5) ); + mat->set_line_width(4); + mat->set_flag(Material::FLAG_DOUBLE_SIDED,true); + mat->set_flag(Material::FLAG_UNSHADED,true); + mat->set_hint(Material::HINT_NO_DEPTH_DRAW,true); + + surface_tool->begin(Mesh::PRIMITIVE_LINES); + surface_tool->set_material(mat); + + switch(mode) { + + case PROJECTION_PERSPECTIVE: { + + + + Vector3 side=Vector3( Math::sin(Math::deg2rad(fov)), 0, -Math::cos(Math::deg2rad(fov)) ); + Vector3 nside=side; + nside.x=-nside.x; + Vector3 up=Vector3(0,side.x,0); + + +#define ADD_TRIANGLE( m_a, m_b, m_c)\ +{\ + surface_tool->add_vertex(m_a);\ + surface_tool->add_vertex(m_b);\ + surface_tool->add_vertex(m_b);\ + surface_tool->add_vertex(m_c);\ + surface_tool->add_vertex(m_c);\ + surface_tool->add_vertex(m_a);\ +} + + ADD_TRIANGLE( Vector3(), side+up, side-up ); + ADD_TRIANGLE( Vector3(), nside+up, nside-up ); + ADD_TRIANGLE( Vector3(), side+up, nside+up ); + ADD_TRIANGLE( Vector3(), side-up, nside-up ); + + side.x*=0.25; + nside.x*=0.25; + Vector3 tup( 0, up.y*3/2,side.z); + ADD_TRIANGLE( tup, side+up, nside+up ); + + } break; + case PROJECTION_ORTHOGONAL: { + +#define ADD_QUAD( m_a, m_b, m_c, m_d)\ +{\ + surface_tool->add_vertex(m_a);\ + surface_tool->add_vertex(m_b);\ + surface_tool->add_vertex(m_b);\ + surface_tool->add_vertex(m_c);\ + surface_tool->add_vertex(m_c);\ + surface_tool->add_vertex(m_d);\ + surface_tool->add_vertex(m_d);\ + surface_tool->add_vertex(m_a);\ +} + + float hsize=size*0.5; + Vector3 right(hsize,0,0); + Vector3 up(0,hsize,0); + Vector3 back(0,0,-1.0); + Vector3 front(0,0,0); + + ADD_QUAD( -up-right,-up+right,up+right,up-right); + ADD_QUAD( -up-right+back,-up+right+back,up+right+back,up-right+back); + ADD_QUAD( up+right,up+right+back,up-right+back,up-right); + ADD_QUAD( -up+right,-up+right+back,-up-right+back,-up-right); + + right.x*=0.25; + Vector3 tup( 0, up.y*3/2,back.z ); + ADD_TRIANGLE( tup, right+up+back, -right+up+back ); + + } break; + + } + + return surface_tool->commit(); + +} + +Vector3 Camera::project_ray_normal(const Point2& p_pos) const { + + Vector3 ray = project_local_ray_normal(p_pos); + return get_camera_transform().basis.xform(ray).normalized(); +}; + +Vector3 Camera::project_local_ray_normal(const Point2& p_pos) const { + + if (!is_inside_scene()) { + ERR_EXPLAIN("Camera is not inside scene."); + ERR_FAIL_COND_V(!is_inside_scene(),Vector3()); + } + + Size2 viewport_size = viewport_ptr->get_visible_rect().size; + + Vector3 ray; + + if (mode==PROJECTION_ORTHOGONAL) { + + ray=Vector3(0,0,-1); + } else { + CameraMatrix cm; + cm.set_perspective(fov,viewport_size.get_aspect(),near,far,vaspect); + float screen_w,screen_h; + cm.get_viewport_size(screen_w,screen_h); + ray=Vector3( ((p_pos.x/viewport_size.width)*2.0-1.0)*screen_w, ((1.0-(p_pos.y/viewport_size.height))*2.0-1.0)*screen_h,-near).normalized(); + } + + + return ray; +}; + + +Vector3 Camera::project_ray_origin(const Point2& p_pos) const { + + if (!is_inside_scene()) { + ERR_EXPLAIN("Camera is not inside scene."); + ERR_FAIL_COND_V(!is_inside_scene(),Vector3()); + } + + Size2 viewport_size = viewport_ptr->get_visible_rect().size; + + + ERR_FAIL_COND_V( viewport_size.y == 0, Vector3() ); +// float aspect = viewport_size.x / viewport_size.y; + + if (mode == PROJECTION_PERSPECTIVE) { + + return get_camera_transform().origin; + } else { + + Vector2 pos = p_pos / viewport_size; + float vsize,hsize; + if (vaspect) { + vsize = size/viewport_size.get_aspect(); + hsize = size; + } else { + hsize = size*viewport_size.get_aspect(); + vsize = size; + + } + + Vector3 ray; + ray.x = pos.x * (hsize) - hsize/2; + ray.y = (1.0 - pos.y) * (vsize) - vsize/2; + ray.z = -near; + ray = get_camera_transform().xform(ray); + return ray; + }; +}; + +Point2 Camera::unproject_position(const Vector3& p_pos) const { + + if (!is_inside_scene()) { + ERR_EXPLAIN("Camera is not inside scene."); + ERR_FAIL_COND_V(!is_inside_scene(),Vector2()); + } + + Size2 viewport_size = viewport_ptr->get_visible_rect().size; + + CameraMatrix cm; + + + if (mode==PROJECTION_ORTHOGONAL) + cm.set_orthogonal(size,viewport_size.get_aspect(),near,far,vaspect); + else + cm.set_perspective(fov,viewport_size.get_aspect(),near,far,vaspect); + + Plane p(get_camera_transform().xform_inv(p_pos),1.0); + + p=cm.xform4(p); + p.normal/=p.d; + + + Point2 res; + res.x = (p.normal.x * 0.5 + 0.5) * viewport_size.x; + res.y = (-p.normal.y * 0.5 + 0.5) * viewport_size.y; + + return res; + +} + +Vector3 Camera::project_position(const Point2& p_point) const { + + if (!is_inside_scene()) { + ERR_EXPLAIN("Camera is not inside scene."); + ERR_FAIL_COND_V(!is_inside_scene(),Vector3()); + } + + Size2 viewport_size = viewport_ptr->get_visible_rect().size; + + CameraMatrix cm; + + if (mode==PROJECTION_ORTHOGONAL) + cm.set_orthogonal(size,viewport_size.get_aspect(),near,far,vaspect); + else + cm.set_perspective(fov,viewport_size.get_aspect(),near,far,vaspect); + + Size2 vp_size; + cm.get_viewport_size(vp_size.x,vp_size.y); + + Vector2 point; + point.x = (p_point.x/viewport_size.x) * 2.0 - 1.0; + point.y = (p_point.y/viewport_size.y) * 2.0 - 1.0; + point*=vp_size; + + Vector3 p(point.x,point.y,-near); + + + return get_camera_transform().xform(p); +} + +/* +void Camera::_camera_make_current(Node *p_camera) { + + + if (p_camera==this) { + VisualServer::get_singleton()->viewport_attach_camera(viewport_id,camera); + active=true; + } else { + if (active && p_camera==NULL) { + //detech camera because no one else will claim it + VisualServer::get_singleton()->viewport_attach_camera(viewport_id,RID()); + } + active=false; + } +} +*/ + +void Camera::set_environment(const Ref& p_environment) { + + environment=p_environment; + if (environment.is_valid()) + VS::get_singleton()->camera_set_environment(camera,environment->get_rid()); + else + VS::get_singleton()->camera_set_environment(camera,RID()); +} + +Ref Camera::get_environment() const { + + return environment; +} + + + +void Camera::_bind_methods() { + + ObjectTypeDB::bind_method( _MD("project_ray_normal","screen_point"), &Camera::project_ray_normal); + ObjectTypeDB::bind_method( _MD("project_local_ray_normal","screen_point"), &Camera::project_local_ray_normal); + ObjectTypeDB::bind_method( _MD("project_ray_origin","screen_point"), &Camera::project_ray_origin); + ObjectTypeDB::bind_method( _MD("unproject_position","world_point"), &Camera::unproject_position); + ObjectTypeDB::bind_method( _MD("project_position","screen_point"), &Camera::project_position); + ObjectTypeDB::bind_method( _MD("set_perspective","fov","z_near","z_far"),&Camera::set_perspective ); + ObjectTypeDB::bind_method( _MD("set_orthogonal","size","z_near","z_far"),&Camera::set_orthogonal ); + ObjectTypeDB::bind_method( _MD("make_current"),&Camera::make_current ); + ObjectTypeDB::bind_method( _MD("clear_current"),&Camera::clear_current ); + ObjectTypeDB::bind_method( _MD("is_current"),&Camera::is_current ); + ObjectTypeDB::bind_method( _MD("get_camera_transform"),&Camera::get_camera_transform ); + ObjectTypeDB::bind_method( _MD("get_fov"),&Camera::get_fov ); + ObjectTypeDB::bind_method( _MD("get_size"),&Camera::get_size ); + ObjectTypeDB::bind_method( _MD("get_zfar"),&Camera::get_zfar ); + ObjectTypeDB::bind_method( _MD("get_znear"),&Camera::get_znear ); + ObjectTypeDB::bind_method( _MD("get_projection"),&Camera::get_projection ); + ObjectTypeDB::bind_method( _MD("set_visible_layers","mask"),&Camera::set_visible_layers ); + ObjectTypeDB::bind_method( _MD("get_visible_layers"),&Camera::get_visible_layers ); + ObjectTypeDB::bind_method( _MD("look_at","target","up"),&Camera::look_at ); + ObjectTypeDB::bind_method( _MD("look_at_from_pos","pos","target","up"),&Camera::look_at_from_pos ); + ObjectTypeDB::bind_method(_MD("set_environment","env:Environment"),&Camera::set_environment); + ObjectTypeDB::bind_method(_MD("get_environment:Environment"),&Camera::get_environment); + ObjectTypeDB::bind_method(_MD("set_use_vertical_aspect","enable"),&Camera::set_use_vertical_aspect); + ObjectTypeDB::bind_method(_MD("is_using_vertical_aspect"),&Camera::is_using_vertical_aspect); + //ObjectTypeDB::bind_method( _MD("_camera_make_current"),&Camera::_camera_make_current ); + + BIND_CONSTANT( PROJECTION_PERSPECTIVE ); + BIND_CONSTANT( PROJECTION_ORTHOGONAL ); +} + +float Camera::get_fov() const { + + return fov; +} + +float Camera::get_size() const { + + return size; +} + +float Camera::get_znear() const { + + return near; +} + +float Camera::get_zfar() const { + + return far; +} + + +Camera::Projection Camera::get_projection() const { + + return mode; +} + +void Camera::set_visible_layers(uint32_t p_layers) { + + layers=p_layers; + VisualServer::get_singleton()->camera_set_visible_layers(camera,layers); +} + +uint32_t Camera::get_visible_layers() const{ + + return layers; +} + + +Vector Camera::get_frustum() const { + + ERR_FAIL_COND_V(!is_inside_world(),Vector()); + + Size2 viewport_size = viewport_ptr->get_visible_rect().size; + CameraMatrix cm; + if (mode==PROJECTION_PERSPECTIVE) + cm.set_perspective(fov,viewport_size.get_aspect(),near,far,vaspect); + else + cm.set_orthogonal(size,viewport_size.get_aspect(),near,far,vaspect); + + return cm.get_projection_planes(get_global_transform()); + +} + + +void Camera::set_use_vertical_aspect(bool p_enable) { + + vaspect=p_enable; + VisualServer::get_singleton()->camera_set_use_vertical_aspect(camera,p_enable); +} + + +bool Camera::is_using_vertical_aspect() const{ + + return vaspect; +} + +void Camera::look_at(const Vector3& p_target, const Vector3& p_up_normal) { + + Transform lookat; + lookat.origin=get_global_transform().origin; + lookat=lookat.looking_at(p_target,p_up_normal); + set_global_transform(lookat); +} + +void Camera::look_at_from_pos(const Vector3& p_pos,const Vector3& p_target, const Vector3& p_up_normal) { + + Transform lookat; + lookat.origin=p_pos; + lookat=lookat.looking_at(p_target,p_up_normal); + set_global_transform(lookat); + +} + + +Camera::Camera() { + + camera = VisualServer::get_singleton()->camera_create(); + size=1; + fov=0; + near=0; + far=0; + current=false; + viewport_ptr=NULL; + force_change=false; + mode=PROJECTION_PERSPECTIVE; + set_perspective(60.0,0.1,100.0); + vaspect=false; + layers=0xFFFFFFFF; + //active=false; +} + + +Camera::~Camera() { + + VisualServer::get_singleton()->free(camera); + +} + + diff --git a/scene/3d/camera.h b/scene/3d/camera.h new file mode 100644 index 00000000000..a8599497acc --- /dev/null +++ b/scene/3d/camera.h @@ -0,0 +1,144 @@ +/*************************************************************************/ +/* camera.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 CAMERA_H +#define CAMERA_H + + +#include "scene/3d/spatial.h" +#include "scene/main/viewport.h" +#include "scene/resources/environment.h" +/** + @author Juan Linietsky +*/ +class Camera : public Spatial { + + OBJ_TYPE( Camera, Spatial ); +public: + enum Projection { + + PROJECTION_PERSPECTIVE, + PROJECTION_ORTHOGONAL + }; + +private: + + bool force_change; + bool current; + + Projection mode; + + float fov; + float size; + float near,far; + bool vaspect; + + RID camera; + RID scenario_id; + + uint32_t layers; + + Viewport *viewport_ptr; + Ref environment; + + virtual bool _can_gizmo_scale() const; + virtual RES _get_gizmo_geometry() const; + + + + //void _camera_make_current(Node *p_camera); +friend class Viewport; + void _update_audio_listener_state(); +protected: + + void _update_camera(); + virtual void _request_camera_update(); + void _update_camera_mode(); + + 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 *p_list) const; + void _notification(int p_what); + + static void _bind_methods(); + +public: + + enum { + + NOTIFICATION_BECAME_CURRENT=50, + NOTIFICATION_LOST_CURRENT=51 + }; + + void set_perspective(float p_fovy_degrees, float p_z_near, float p_z_far); + void set_orthogonal(float p_size, float p_z_near, float p_z_far); + + void make_current(); + void clear_current(); + bool is_current() const; + + RID get_camera() const; + + float get_fov() const; + float get_size() const; + float get_zfar() const; + float get_znear() const; + Projection get_projection() const; + + virtual Transform get_camera_transform() const; + + Vector3 project_ray_normal(const Point2& p_point) const; + Vector3 project_ray_origin(const Point2& p_point) const; + Vector3 project_local_ray_normal(const Point2& p_point) const; + Point2 unproject_position(const Vector3& p_pos) const; + Vector3 project_position(const Point2& p_point) const; + + void set_visible_layers(uint32_t p_layers); + uint32_t get_visible_layers() const; + + Vector get_frustum() const; + + void set_environment(const Ref& p_environment); + Ref get_environment() const; + + void set_use_vertical_aspect(bool p_enable); + bool is_using_vertical_aspect() const; + + void look_at(const Vector3& p_target, const Vector3& p_up_normal); + void look_at_from_pos(const Vector3& p_pos,const Vector3& p_target, const Vector3& p_up_normal); + + + Camera(); + ~Camera(); + +}; + + +VARIANT_ENUM_CAST( Camera::Projection ); + +#endif diff --git a/scene/3d/car_body.cpp b/scene/3d/car_body.cpp new file mode 100644 index 00000000000..a21598b07c0 --- /dev/null +++ b/scene/3d/car_body.cpp @@ -0,0 +1,741 @@ +/*************************************************************************/ +/* car_body.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 "car_body.h" + +#define DEG2RADMUL (Math_PI/180.0) +#define RAD2DEGMUL (180.0/Math_PI) + +void CarWheel::_notification(int p_what) { + + + if (p_what==NOTIFICATION_ENTER_SCENE) { + + if (!get_parent()) + return; + CarBody *cb = get_parent()->cast_to(); + if (!cb) + return; + body=cb; + local_xform=get_transform(); + cb->wheels.push_back(this); + } + if (p_what==NOTIFICATION_EXIT_SCENE) { + + if (!get_parent()) + return; + CarBody *cb = get_parent()->cast_to(); + if (!cb) + return; + cb->wheels.erase(this); + body=NULL; + } +} + +void CarWheel::set_side_friction(real_t p_friction) { + + side_friction=p_friction; +} +void CarWheel::set_forward_friction(real_t p_friction) { + + forward_friction=p_friction; +} +void CarWheel::set_travel(real_t p_travel) { + + travel=p_travel; + update_gizmo(); + +} +void CarWheel::set_radius(real_t p_radius) { + + radius=p_radius; + update_gizmo(); + +} +void CarWheel::set_resting_frac(real_t p_frac) { + + resting_frac=p_frac; +} +void CarWheel::set_damping_frac(real_t p_frac) { + + damping_frac=p_frac; +} +void CarWheel::set_num_rays(real_t p_rays) { + + num_rays=p_rays; +} + +real_t CarWheel::get_side_friction() const{ + + return side_friction; +} +real_t CarWheel::get_forward_friction() const{ + + return forward_friction; +} +real_t CarWheel::get_travel() const{ + + return travel; +} +real_t CarWheel::get_radius() const{ + + return radius; +} +real_t CarWheel::get_resting_frac() const{ + + return resting_frac; +} +real_t CarWheel::get_damping_frac() const{ + + return damping_frac; +} + +int CarWheel::get_num_rays() const{ + + return num_rays; +} + + +void CarWheel::update(real_t dt) { + + + if (dt <= 0.0f) + return; + + float origAngVel = angVel; + + if (locked) + { + angVel = 0; + torque = 0; + } + else + { + + float wheelMass = 0.03f * body->mass; + float inertia = 0.5f * (radius * radius) * wheelMass; + + angVel += torque * dt / inertia; + torque = 0; + + // prevent friction from reversing dir - todo do this better + // by limiting the torque + if (((origAngVel > angVelForGrip) && (angVel < angVelForGrip)) || + ((origAngVel < angVelForGrip) && (angVel > angVelForGrip))) + angVel = angVelForGrip; + + angVel += driveTorque * dt / inertia; + driveTorque = 0; + + float maxAngVel = 200; + print_line("angvel: "+rtos(angVel)); + angVel = CLAMP(angVel, -maxAngVel, maxAngVel); + + axisAngle += Math::rad2deg(dt * angVel); + } +} + +bool CarWheel::add_forces(PhysicsDirectBodyState *s) { + + + Vector3 force; + + PhysicsDirectSpaceState *space = s->get_space_state(); + + Transform world = s->get_transform() * local_xform; + + // OpenGl has differnet row/column order for matrixes than XNA has .. + //Vector3 wheelFwd = world.get_basis().get_axis(Vector3::AXIS_Z); + //Vector3 wheelFwd = RotationMatrix(mSteerAngle, worldAxis) * carBody.GetOrientation().GetCol(0); + Vector3 wheelUp = world.get_basis().get_axis(Vector3::AXIS_Y); + Vector3 wheelFwd = Matrix3(wheelUp,Math::deg2rad(steerAngle)).xform( world.get_basis().get_axis(Vector3::AXIS_Z) ); + Vector3 wheelLeft = -wheelUp.cross(wheelFwd).normalized(); + Vector3 worldPos = world.origin; + Vector3 worldAxis = wheelUp; + + // start of ray + float rayLen = 2.0f * radius + travel; + Vector3 wheelRayEnd = worldPos - radius * worldAxis; + Vector3 wheelRayBegin = wheelRayEnd + rayLen * worldAxis; + //wheelRayEnd = -rayLen * worldAxis; + + //Assert(PhysicsSystem.CurrentPhysicsSystem); + + + ///Assert(collSystem); + /// + const int maxNumRays = 32; + + int numRaysUse = MIN(num_rays, maxNumRays); + + // adjust the start position of the ray - divide the wheel into numRays+2 + // rays, but don't use the first/last. + float deltaFwd = (2.0f * radius) / (numRaysUse + 1); + float deltaFwdStart = deltaFwd; + + float fracs[maxNumRays]; + Vector3 segmentEnds[maxNumRays]; + Vector3 groundPositions[maxNumRays]; + Vector3 groundNormals[maxNumRays]; + + + lastOnFloor = false; + int bestIRay = 0; + int iRay; + + + for (iRay = 0; iRay < numRaysUse; ++iRay) + { + fracs[iRay] = 1e20; + // work out the offset relative to the middle ray + float distFwd = (deltaFwdStart + iRay * deltaFwd) - radius; + float zOffset = radius * (1.0f - (float)Math::cos( Math::deg2rad( 90.0f * (distFwd / radius)))); + + segmentEnds[iRay] = wheelRayEnd + distFwd * wheelFwd + zOffset * wheelUp; + + + PhysicsDirectSpaceState::RayResult rr; + + bool collided = space->intersect_ray(wheelRayBegin,segmentEnds[iRay],rr,body->exclude); + + + if (collided){ + lastOnFloor = true; + groundPositions[iRay]=rr.position; + groundNormals[iRay]=rr.normal; + fracs[iRay] = ((wheelRayBegin-rr.position).length() / (wheelRayBegin-wheelRayEnd).length()); + if (fracs[iRay] < fracs[bestIRay]) + bestIRay = iRay; + } + } + + + if (!lastOnFloor) + return false; + + //Assert(bestIRay < numRays); + + // use the best one + Vector3 groundPos = groundPositions[bestIRay]; + float frac = fracs[bestIRay]; + + // const Vector3 groundNormal = (worldPos - segments[bestIRay].GetEnd()).NormaliseSafe(); + // const Vector3 groundNormal = groundNormals[bestIRay]; + + + Vector3 groundNormal = worldAxis; + + if (numRaysUse > 1) + { + for (iRay = 0; iRay < numRaysUse; ++iRay) + { + if (fracs[iRay] <= 1.0f) + { + groundNormal += (1.0f - fracs[iRay]) * (worldPos - segmentEnds[iRay]); + } + } + + groundNormal.normalize(); + + } + else + { + groundNormal = groundNormals[bestIRay]; + } + + + + float spring = (body->mass/body->wheels.size()) * s->get_total_gravity().length() / (resting_frac * travel); + + float displacement = rayLen * (1.0f - frac); + displacement = CLAMP(displacement, 0, travel); + + + + float displacementForceMag = displacement * spring; + + // reduce force when suspension is par to ground + displacementForceMag *= groundNormals[bestIRay].dot(worldAxis); + + // apply damping + float damping = 2.0f * (float)Math::sqrt(spring * body->mass); + damping /= body->wheels.size(); // assume wheels act together + damping *= damping_frac; // a bit bouncy + + float upSpeed = (displacement - lastDisplacement) / s->get_step(); + + float dampingForceMag = upSpeed * damping; + + float totalForceMag = displacementForceMag + dampingForceMag; + + if (totalForceMag < 0.0f) totalForceMag = 0.0f; + + Vector3 extraForce = totalForceMag * worldAxis; + + + force += extraForce; + // side-slip friction and drive force. Work out wheel- and floor-relative coordinate frame + Vector3 groundUp = groundNormal; + Vector3 groundLeft = groundNormal.cross(wheelFwd).normalized(); + + Vector3 groundFwd = groundLeft.cross(groundUp); + + Vector3 wheelPointVel = s->get_linear_velocity() + + (s->get_angular_velocity()).cross(s->get_transform().basis.xform(local_xform.origin));// * mPos); + + Vector3 rimVel = -angVel * wheelLeft.cross(groundPos - worldPos); + wheelPointVel += rimVel; + + // if sitting on another body then adjust for its velocity. + /*if (worldBody != null) + { + Vector3 worldVel = worldBody.Velocity + + Vector3.Cross(worldBody.AngularVelocity, groundPos - worldBody.Position); + + wheelPointVel -= worldVel; + }*/ + + // sideways forces + float noslipVel = 0.2f; + float slipVel = 0.4f; + float slipFactor = 0.7f; + + float smallVel = 3; + float friction = side_friction; + + float sideVel = wheelPointVel.dot(groundLeft); + + if ((sideVel > slipVel) || (sideVel < -slipVel)) + friction *= slipFactor; + else + if ((sideVel > noslipVel) || (sideVel < -noslipVel)) + friction *= 1.0f - (1.0f - slipFactor) * (Math::absf(sideVel) - noslipVel) / (slipVel - noslipVel); + + if (sideVel < 0.0f) + friction *= -1.0f; + + if (Math::absf(sideVel) < smallVel) + friction *= Math::absf(sideVel) / smallVel; + + float sideForce = -friction * totalForceMag; + + extraForce = sideForce * groundLeft; + force += extraForce; + // fwd/back forces + friction = forward_friction; + float fwdVel = wheelPointVel.dot(groundFwd); + + if ((fwdVel > slipVel) || (fwdVel < -slipVel)) + friction *= slipFactor; + else + if ((fwdVel > noslipVel) || (fwdVel < -noslipVel)) + friction *= 1.0f - (1.0f - slipFactor) * (Math::absf(fwdVel) - noslipVel) / (slipVel - noslipVel); + + if (fwdVel < 0.0f) + friction *= -1.0f; + + if (Math::absf(fwdVel) < smallVel) + friction *= Math::absf(fwdVel) / smallVel; + + float fwdForce = -friction * totalForceMag; + + extraForce = fwdForce * groundFwd; + force += extraForce; + + + //if (!force.IsSensible()) + //{ + // TRACE_FILE_IF(ONCE_1) + // TRACE("Bad force in car wheel\n"); + // return true; + //} + + // fwd force also spins the wheel + Vector3 wheelCentreVel = s->get_linear_velocity() + + (s->get_angular_velocity()).cross(s->get_transform().basis.xform(local_xform.origin)); + + angVelForGrip = wheelCentreVel.dot(groundFwd) / radius; + torque += -fwdForce * radius; + + // add force to car +// carBody.AddWorldForce(force, groundPos); + + s->add_force(force,(groundPos-s->get_transform().origin)); + + // add force to the world + /* + if (worldBody != null && !worldBody.Immovable) + { + // todo get the position in the right place... + // also limit the velocity that this force can produce by looking at the + // mass/inertia of the other object + float maxOtherBodyAcc = 500.0f; + float maxOtherBodyForce = maxOtherBodyAcc * worldBody.Mass; + + if (force.LengthSquared() > (maxOtherBodyForce * maxOtherBodyForce)) + force *= maxOtherBodyForce / force.Length(); + + worldBody.AddWorldForce(-force, groundPos); + }*/ + + Transform wheel_xf = local_xform; + wheel_xf.origin += wheelUp * displacement; + wheel_xf.basis = wheel_xf.basis * Matrix3(Vector3(0,1,0),Math::deg2rad(steerAngle)); + //wheel_xf.basis = wheel_xf.basis * Matrix3(wheel_xf.basis[0],-Math::deg2rad(axisAngle)); + + set_transform(wheel_xf); + lastDisplacement=displacement; + return true; + +} + +void CarWheel::set_type_drive(bool p_enable) { + + type_drive=p_enable; +} + +bool CarWheel::is_type_drive() const { + + return type_drive; +} + +void CarWheel::set_type_steer(bool p_enable) { + + type_steer=p_enable; +} + +bool CarWheel::is_type_steer() const { + + return type_steer; +} + + +void CarWheel::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("set_side_friction","friction"),&CarWheel::set_side_friction); + ObjectTypeDB::bind_method(_MD("set_forward_friction","friction"),&CarWheel::set_forward_friction); + ObjectTypeDB::bind_method(_MD("set_travel","distance"),&CarWheel::set_travel); + ObjectTypeDB::bind_method(_MD("set_radius","radius"),&CarWheel::set_radius); + ObjectTypeDB::bind_method(_MD("set_resting_frac","frac"),&CarWheel::set_resting_frac); + ObjectTypeDB::bind_method(_MD("set_damping_frac","frac"),&CarWheel::set_damping_frac); + ObjectTypeDB::bind_method(_MD("set_num_rays","amount"),&CarWheel::set_num_rays); + + ObjectTypeDB::bind_method(_MD("get_side_friction"),&CarWheel::get_side_friction); + ObjectTypeDB::bind_method(_MD("get_forward_friction"),&CarWheel::get_forward_friction); + ObjectTypeDB::bind_method(_MD("get_travel"),&CarWheel::get_travel); + ObjectTypeDB::bind_method(_MD("get_radius"),&CarWheel::get_radius); + ObjectTypeDB::bind_method(_MD("get_resting_frac"),&CarWheel::get_resting_frac); + ObjectTypeDB::bind_method(_MD("get_damping_frac"),&CarWheel::get_damping_frac); + ObjectTypeDB::bind_method(_MD("get_num_rays"),&CarWheel::get_num_rays); + + ObjectTypeDB::bind_method(_MD("set_type_drive","enable"),&CarWheel::set_type_drive); + ObjectTypeDB::bind_method(_MD("is_type_drive"),&CarWheel::is_type_drive); + + ObjectTypeDB::bind_method(_MD("set_type_steer","enable"),&CarWheel::set_type_steer); + ObjectTypeDB::bind_method(_MD("is_type_steer"),&CarWheel::is_type_steer); + + ADD_PROPERTY( PropertyInfo(Variant::BOOL,"type/drive"),_SCS("set_type_drive"),_SCS("is_type_drive")); + ADD_PROPERTY( PropertyInfo(Variant::BOOL,"type/steer"),_SCS("set_type_steer"),_SCS("is_type_steer")); + ADD_PROPERTY( PropertyInfo(Variant::REAL,"config/side_friction",PROPERTY_HINT_RANGE,"0.01,64,0.01"),_SCS("set_side_friction"),_SCS("get_side_friction")); + ADD_PROPERTY( PropertyInfo(Variant::REAL,"config/forward_friction",PROPERTY_HINT_RANGE,"0.01,64,0.01"),_SCS("set_forward_friction"),_SCS("get_forward_friction")); + ADD_PROPERTY( PropertyInfo(Variant::REAL,"config/travel",PROPERTY_HINT_RANGE,"0.01,1024,0.01"),_SCS("set_travel"),_SCS("get_travel")); + ADD_PROPERTY( PropertyInfo(Variant::REAL,"config/radius",PROPERTY_HINT_RANGE,"0.01,1024,0.01"),_SCS("set_radius"),_SCS("get_radius")); + ADD_PROPERTY( PropertyInfo(Variant::REAL,"config/resting_frac",PROPERTY_HINT_RANGE,"0.01,64,0.01"),_SCS("set_resting_frac"),_SCS("get_resting_frac")); + ADD_PROPERTY( PropertyInfo(Variant::REAL,"config/damping_frac",PROPERTY_HINT_RANGE,"0.01,64,0.01"),_SCS("set_damping_frac"),_SCS("get_damping_frac")); + ADD_PROPERTY( PropertyInfo(Variant::REAL,"config/num_rays",PROPERTY_HINT_RANGE,"1,32,1"),_SCS("set_num_rays"),_SCS("get_num_rays")); + +} + +CarWheel::CarWheel() { + + side_friction=4.7; + forward_friction=5.0; + travel=0.2; + radius=0.4; + resting_frac=0.45; + damping_frac=0.3; + num_rays=1; + + angVel = 0.0f; + steerAngle = 0.0f; + torque = 0.0f; + driveTorque = 0.0f; + axisAngle = 0.0f; + upSpeed = 0.0f; + locked = false; + lastDisplacement = 0.0f; + lastOnFloor = false; + angVelForGrip = 0.0f; + angVelForGrip=0; + + type_drive=false; + type_steer=false; + +} + +/// + + +void CarBody::set_max_steer_angle(real_t p_angle) { + + max_steer_angle=p_angle; +} +void CarBody::set_steer_rate(real_t p_rate) { + + steer_rate=p_rate; +} +void CarBody::set_drive_torque(real_t p_torque) { + + drive_torque=p_torque; +} + +real_t CarBody::get_max_steer_angle() const{ + + return max_steer_angle; +} +real_t CarBody::get_steer_rate() const{ + + return steer_rate; +} +real_t CarBody::get_drive_torque() const{ + + return drive_torque; +} + + +void CarBody::set_target_steering(float p_steering) { + + target_steering=p_steering; +} + +void CarBody::set_target_accelerate(float p_accelerate) { + target_accelerate=p_accelerate; +} + +void CarBody::set_hand_brake(float p_amont) { + + hand_brake=p_amont; +} + +real_t CarBody::get_target_steering() const { + + return target_steering; +} +real_t CarBody::get_target_accelerate() const { + + return target_accelerate; +} +real_t CarBody::get_hand_brake() const { + + return hand_brake; +} + + +void CarBody::_direct_state_changed(Object *p_state) { + + PhysicsDirectBodyState *state=p_state->cast_to(); + + float dt = state->get_step(); + AABB aabb; + int drive_total=0; + for(int i=0;ilocal_xform.origin; + } else { + aabb.expand_to(w->local_xform.origin); + } + if (w->type_drive) + drive_total++; + + } + // control inputs + float deltaAccelerate = dt * 4.0f; + + float dAccelerate = target_accelerate - accelerate; + dAccelerate = CLAMP(dAccelerate, -deltaAccelerate, deltaAccelerate); + accelerate += dAccelerate; + + float deltaSteering = dt * steer_rate; + float dSteering = target_steering - steering; + dSteering = CLAMP(dSteering, -deltaSteering, deltaSteering); + steering += dSteering; + + // apply these inputs + float maxTorque = drive_torque; + + float torque_div = drive_total/2; + if (torque_div>0) + maxTorque/=torque_div; + + + float alpha = ABS(max_steer_angle * steering); + float angleSgn = steering > 0.0f ? 1.0f : -1.0f; + + int wheels_on_floor=0; + + for(int i=0;itype_drive) + w->driveTorque+=maxTorque * accelerate; + w->locked = !w->type_steer && (hand_brake > 0.5f); + + if (w->type_steer) { + //steering + + bool inner = (steering > 0 && w->local_xform.origin.x > 0) || (steering < 0 && w->local_xform.origin.x < 0); + + if (inner || alpha==0.0) { + + w->steerAngle = (angleSgn * alpha); + } else { + float dx = aabb.size.z; + float dy = aabb.size.x; + + float beta = Math::atan2(dy, dx + (dy / (float)Math::tan(Math::deg2rad(alpha)))); + beta = Math::rad2deg(beta); + w->steerAngle = (angleSgn * beta); + + } + } + + if (w->add_forces(state)) + wheels_on_floor++; + w->update(dt); + + + } + + print_line("onfloor: "+itos(wheels_on_floor)); + + + set_ignore_transform_notification(true); + set_global_transform(state->get_transform()); + linear_velocity=state->get_linear_velocity(); + angular_velocity=state->get_angular_velocity(); + //active=!state->is_sleeping(); + //if (get_script_instance()) + // get_script_instance()->call("_integrate_forces",state); + set_ignore_transform_notification(false); + + +} + +void CarBody::set_mass(real_t p_mass) { + + mass=p_mass; + PhysicsServer::get_singleton()->body_set_param(get_rid(),PhysicsServer::BODY_PARAM_MASS,mass); +} + +real_t CarBody::get_mass() const{ + + return mass; +} + + +void CarBody::set_friction(real_t p_friction) { + + friction=p_friction; + PhysicsServer::get_singleton()->body_set_param(get_rid(),PhysicsServer::BODY_PARAM_FRICTION,friction); +} + +real_t CarBody::get_friction() const{ + + return friction; +} + + +void CarBody::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("set_max_steer_angle","value"),&CarBody::set_max_steer_angle); + ObjectTypeDB::bind_method(_MD("set_steer_rate","rate"),&CarBody::set_steer_rate); + ObjectTypeDB::bind_method(_MD("set_drive_torque","value"),&CarBody::set_drive_torque); + + ObjectTypeDB::bind_method(_MD("get_max_steer_angle"),&CarBody::get_max_steer_angle); + ObjectTypeDB::bind_method(_MD("get_steer_rate"),&CarBody::get_steer_rate); + ObjectTypeDB::bind_method(_MD("get_drive_torque"),&CarBody::get_drive_torque); + + ObjectTypeDB::bind_method(_MD("set_target_steering","amount"),&CarBody::set_target_steering); + ObjectTypeDB::bind_method(_MD("set_target_accelerate","amount"),&CarBody::set_target_accelerate); + ObjectTypeDB::bind_method(_MD("set_hand_brake","amount"),&CarBody::set_hand_brake); + + ObjectTypeDB::bind_method(_MD("get_target_steering"),&CarBody::get_target_steering); + ObjectTypeDB::bind_method(_MD("get_target_accelerate"),&CarBody::get_target_accelerate); + ObjectTypeDB::bind_method(_MD("get_hand_brake"),&CarBody::get_hand_brake); + + ObjectTypeDB::bind_method(_MD("set_mass","mass"),&CarBody::set_mass); + ObjectTypeDB::bind_method(_MD("get_mass"),&CarBody::get_mass); + + ObjectTypeDB::bind_method(_MD("set_friction","friction"),&CarBody::set_friction); + ObjectTypeDB::bind_method(_MD("get_friction"),&CarBody::get_friction); + + ObjectTypeDB::bind_method(_MD("_direct_state_changed"),&CarBody::_direct_state_changed); + + ADD_PROPERTY( PropertyInfo(Variant::REAL,"body/mass",PROPERTY_HINT_RANGE,"0.01,65536,0.01"),_SCS("set_mass"),_SCS("get_mass")); + ADD_PROPERTY( PropertyInfo(Variant::REAL,"body/friction",PROPERTY_HINT_RANGE,"0.01,1,0.01"),_SCS("set_friction"),_SCS("get_friction")); + + ADD_PROPERTY( PropertyInfo(Variant::REAL,"config/max_steer_angle",PROPERTY_HINT_RANGE,"1,90,1"),_SCS("set_max_steer_angle"),_SCS("get_max_steer_angle")); + ADD_PROPERTY( PropertyInfo(Variant::REAL,"config/drive_torque",PROPERTY_HINT_RANGE,"0.01,64,0.01"),_SCS("set_drive_torque"),_SCS("get_drive_torque")); + ADD_PROPERTY( PropertyInfo(Variant::REAL,"config/steer_rate",PROPERTY_HINT_RANGE,"0.01,64,0.01"),_SCS("set_steer_rate"),_SCS("get_steer_rate")); + + ADD_PROPERTY( PropertyInfo(Variant::REAL,"drive/target_steering",PROPERTY_HINT_RANGE,"-1,1,0.01"),_SCS("set_target_steering"),_SCS("get_target_steering")); + ADD_PROPERTY( PropertyInfo(Variant::REAL,"drive/target_accelerate",PROPERTY_HINT_RANGE,"-1,1,0.01"),_SCS("set_target_accelerate"),_SCS("get_target_accelerate")); + ADD_PROPERTY( PropertyInfo(Variant::REAL,"drive/hand_brake",PROPERTY_HINT_RANGE,"0,1,0.01"),_SCS("set_hand_brake"),_SCS("get_hand_brake")); + +} + +CarBody::CarBody() : PhysicsBody(PhysicsServer::BODY_MODE_RIGID) { + + forward_drive=true; + backward_drive=true; + max_steer_angle=30; + steer_rate=1; + drive_torque=520; + + target_steering=0; + target_accelerate=0; + hand_brake=0; + + steering=0; + accelerate=0; + + mass=1; + friction=1; + + ccd=false; +// can_sleep=true; + + + + + exclude.insert(get_rid()); + PhysicsServer::get_singleton()->body_set_force_integration_callback(get_rid(),this,"_direct_state_changed"); + + +} diff --git a/scene/3d/car_body.h b/scene/3d/car_body.h new file mode 100644 index 00000000000..87eb047bcfe --- /dev/null +++ b/scene/3d/car_body.h @@ -0,0 +1,170 @@ +/*************************************************************************/ +/* car_body.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 CAR_BODY_H +#define CAR_BODY_H + +#include "scene/3d/physics_body.h" + + +class CarBody; + +class CarWheel : public Spatial { + + OBJ_TYPE(CarWheel,Spatial); + +friend class CarBody; + real_t side_friction; + real_t forward_friction; + real_t travel; + real_t radius; + real_t resting_frac; + real_t damping_frac; + int num_rays; + Transform local_xform; + + CarBody *body; + + float angVel; + float steerAngle; + float torque; + float driveTorque; + float axisAngle; + float upSpeed; // speed relative to the car + bool locked; + // last frame stuff + float lastDisplacement; + float angVelForGrip; + bool lastOnFloor; + + bool type_drive; + bool type_steer; + + +protected: + void update(real_t dt); + bool add_forces(PhysicsDirectBodyState *s); + void _notification(int p_what); + static void _bind_methods(); + +public: + + void set_side_friction(real_t p_friction); + void set_forward_friction(real_t p_friction); + void set_travel(real_t p_travel); + void set_radius(real_t p_radius); + void set_resting_frac(real_t p_frac); + void set_damping_frac(real_t p_frac); + void set_num_rays(real_t p_rays); + + real_t get_side_friction() const; + real_t get_forward_friction() const; + real_t get_travel() const; + real_t get_radius() const; + real_t get_resting_frac() const; + real_t get_damping_frac() const; + int get_num_rays() const; + + void set_type_drive(bool p_enable); + bool is_type_drive() const; + + void set_type_steer(bool p_enable); + bool is_type_steer() const; + + CarWheel(); + +}; + + + +class CarBody : public PhysicsBody { + + OBJ_TYPE(CarBody,PhysicsBody); + + real_t mass; + real_t friction; + + Vector3 linear_velocity; + Vector3 angular_velocity; + bool ccd; + + real_t max_steer_angle; + real_t steer_rate; + int wheel_num_rays; + real_t drive_torque; + +// control stuff + real_t target_steering; + real_t target_accelerate; + + bool forward_drive; + bool backward_drive; + + real_t steering; + real_t accelerate; + real_t hand_brake; + Set exclude; + + +friend class CarWheel; + Vector wheels; + + static void _bind_methods(); + + void _direct_state_changed(Object *p_state); +public: + + + void set_mass(real_t p_mass); + real_t get_mass() const; + + void set_friction(real_t p_friction); + real_t get_friction() const; + + void set_max_steer_angle(real_t p_angle); + void set_steer_rate(real_t p_rate); + void set_drive_torque(real_t p_torque); + + real_t get_max_steer_angle() const; + real_t get_steer_rate() const; + real_t get_drive_torque() const; + + + void set_target_steering(float p_steering); + void set_target_accelerate(float p_accelerate); + void set_hand_brake(float p_amont); + + real_t get_target_steering() const; + real_t get_target_accelerate() const; + real_t get_hand_brake() const; + + + CarBody(); +}; + +#endif // CAR_BODY_H diff --git a/scene/3d/character_camera.cpp b/scene/3d/character_camera.cpp new file mode 100644 index 00000000000..e3c071d42fe --- /dev/null +++ b/scene/3d/character_camera.cpp @@ -0,0 +1,718 @@ +/*************************************************************************/ +/* character_camera.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 "character_camera.h" + +#include "physics_body.h" +#if 0 +void CharacterCamera::_set(const String& p_name, const Variant& p_value) { + + if (p_name=="type") + set_camera_type((CameraType)((int)(p_value))); + else if (p_name=="orbit") + set_orbit(p_value); + else if (p_name=="height") + set_height(p_value); + else if (p_name=="inclination") + set_inclination(p_value); + else if (p_name=="max_orbit_x") + set_max_orbit_x(p_value); + else if (p_name=="min_orbit_x") + set_min_orbit_x(p_value); + else if (p_name=="max_distance") + set_max_distance(p_value); + else if (p_name=="min_distance") + set_min_distance(p_value); + else if (p_name=="distance") + set_distance(p_value); + else if (p_name=="clip") + set_clip(p_value); + else if (p_name=="autoturn") + set_autoturn(p_value); + else if (p_name=="autoturn_tolerance") + set_autoturn_tolerance(p_value); + else if (p_name=="autoturn_speed") + set_autoturn_speed(p_value); + +} +Variant CharacterCamera::_get(const String& p_name) const { + + if (p_name=="type") + return get_camera_type(); + else if (p_name=="orbit") + return get_orbit(); + else if (p_name=="height") + return get_height(); + else if (p_name=="inclination") + return get_inclination(); + else if (p_name=="max_orbit_x") + return get_max_orbit_x(); + else if (p_name=="min_orbit_x") + return get_min_orbit_x(); + else if (p_name=="max_distance") + return get_max_distance(); + else if (p_name=="min_distance") + return get_min_distance(); + else if (p_name=="distance") + return get_distance(); + else if (p_name=="clip") + return has_clip(); + else if (p_name=="autoturn") + return has_autoturn(); + else if (p_name=="autoturn_tolerance") + return get_autoturn_tolerance(); + else if (p_name=="autoturn_speed") + return get_autoturn_speed(); + + return Variant(); +} + +void CharacterCamera::_get_property_list( List *p_list) const { + + p_list->push_back( PropertyInfo( Variant::INT, "type", PROPERTY_HINT_ENUM, "Fixed,Follow") ); + p_list->push_back( PropertyInfo( Variant::VECTOR2, "orbit" ) ); + p_list->push_back( PropertyInfo( Variant::REAL, "height", PROPERTY_HINT_RANGE,"-1024,1024,0.01" ) ); + p_list->push_back( PropertyInfo( Variant::REAL, "inclination", PROPERTY_HINT_RANGE,"-90,90,0.01" ) ); ; + p_list->push_back( PropertyInfo( Variant::REAL, "max_orbit_x", PROPERTY_HINT_RANGE,"-90,90,0.01" ) ); + p_list->push_back( PropertyInfo( Variant::REAL, "min_orbit_x", PROPERTY_HINT_RANGE,"-90,90,0.01" ) ); + p_list->push_back( PropertyInfo( Variant::REAL, "min_distance", PROPERTY_HINT_RANGE,"0,100,0.01" ) ); + p_list->push_back( PropertyInfo( Variant::REAL, "max_distance", PROPERTY_HINT_RANGE,"0,100,0.01" ) ); + p_list->push_back( PropertyInfo( Variant::REAL, "distance", PROPERTY_HINT_RANGE,"0.01,1024,0,01") ); + p_list->push_back( PropertyInfo( Variant::BOOL, "clip") ); + p_list->push_back( PropertyInfo( Variant::BOOL, "autoturn") ); + p_list->push_back( PropertyInfo( Variant::REAL, "autoturn_tolerance", PROPERTY_HINT_RANGE,"1,90,0.01") ); + p_list->push_back( PropertyInfo( Variant::REAL, "autoturn_speed", PROPERTY_HINT_RANGE,"1,90,0.01") ); + + +} + +void CharacterCamera::_compute_camera() { + + + // update the transform with the next proposed transform (camera is 1 logic frame delayed) + + /* + float time = get_root_node()->get_frame_time(); + Vector3 oldp = accepted.get_origin(); + Vector3 newp = proposed.get_origin(); + + float frame_dist = time * + if (oldp.distance_to(newp) > + */ + + float time = get_root_node()->get_frame_time(); + + if (true) { + + if (clip_ray[0].clipped && clip_ray[1].clipped && clip_ray[2].clipped) { + //all have been clipped + proposed.origin=clip_ray[1].clip_pos; + + + } else { + + Vector3 rel=proposed.origin-target_pos; + + if (clip_ray[0].clipped && !clip_ray[2].clipped) { + + float distance = target_pos.distance_to(clip_ray[0].clip_pos); + real_t amount = 1.0-(distance/clip_len); + amount = CLAMP(amount,0,1); + + + rel=Matrix3(Vector3(0,1,0)), + rotate_orbit(Vector2(0,autoturn_speed*time*amount)); + } + if (clip_ray[2].clipped && !clip_ray[0].clipped) { + + float distance = target_pos.distance_to(clip_ray[2].clip_pos); + real_t amount = 1.0-(distance/clip_len); + amount = CLAMP(amount,0,1); + + rotate_orbit(Vector2(0,-autoturn_speed*time*amount)); + } + + } + } + + + Transform final; + + static float pos_ratio = 0.9; + static float rot_ratio = 10; + + Vector3 vec1 = accepted.origin; + Vector3 vec2 = proposed.origin; + final.origin = vec2.linear_interpolate(vec1, pos_ratio * time);; + + Quat q1 = accepted.basis; + Quat q2 = proposed.basis; + final.basis = q1.slerp(q2, rot_ratio * time); + + accepted=final; + + _update_camera(); + + // calculate the next proposed transform + + + Vector3 new_pos; + Vector3 character_pos = get_global_transform().origin; + character_pos.y+=height; // height compensate + + if(type==CAMERA_FOLLOW) { + + + + /* calculate some variables */ + + Vector3 rel = follow_pos - character_pos; + + float l = rel.length(); + Vector3 rel_n = (l > 0) ? (rel/l) : Vector3(); +#if 1 + float ang = Math::acos(rel_n.dot( Vector3(0,1,0) )); + + Vector3 tangent = rel_n; + tangent.y=0; // get rid of y + if (tangent.length_squared() < CMP_EPSILON2) + tangent=Vector3(0,0,1); // use Z as tangent if rel is parallel to y + else + tangent.normalize(); + + /* now start applying the rules */ + + //clip distance + if (l > max_distance) + l=max_distance; + if (l < min_distance) + l=min_distance; + + //fix angle + + float ang_min = Math_PI * 0.5 + Math::deg2rad(min_orbit_x); + float ang_max = Math_PI * 0.5 + Math::deg2rad(max_orbit_x); + + if (angang_max) + ang=ang_max; + + /* finally, rebuild the validated camera position */ + + new_pos=Vector3(0,Math::cos(ang),0); + new_pos+=tangent*Math::sin(ang); + new_pos*=l; + new_pos+=character_pos; +#else + if (l > max_distance) + l=max_distance; + if (l < min_distance) + l=min_distance; + + new_pos = character_pos + rel_n * l; + + +#endif + follow_pos=new_pos; + + } else if (type==CAMERA_FIXED) { + + + if (distancemax_distance) + distance=max_distance; + + if (orbit.xmax_orbit_x) + orbit.x=max_orbit_x; + + Matrix3 m; + m.rotate(Vector3(0,1,0),Math::deg2rad(orbit.y)); + m.rotate(Vector3(1,0,0),Math::deg2rad(orbit.x)); + + new_pos = (m.get_axis(2) * distance) + character_pos; + + if (use_lookat_target) { + + Transform t = get_global_transform(); + Vector3 y = t.basis.get_axis(1).normalized(); + Vector3 z = lookat_target - character_pos; + z= (z - y * y.dot(z)).normalized(); + orbit.y = -Math::rad2deg(Math::atan2(z.x,z.z)) + 180; + + /* + Transform t = get_global_transform(); + Vector3 y = t.basis.get_axis(1).normalized(); + Vector3 z = lookat_target - t.origin; + z= (z - y * y.dot(z)).normalized(); + Vector3 x = z.cross(y).normalized(); + Transform t2; + t2.basis.set_axis(0,x); + t2.basis.set_axis(1,y); + t2.basis.set_axis(2,z); + t2.origin=t.origin; + + Vector3 local = t2.xform_inv(camera_pos); + + float ang = Math::atan2(local.x,local.y); + */ + + /* + + Vector3 vec1 = lookat_target - new_pos; + vec1.normalize(); + Vector3 vec2 = character_pos - new_pos; + vec2.normalize(); + + float dot = vec1.dot(vec2); + printf("dot %f\n", dot); + if ( dot < 0.5) { + + rotate_orbit(Vector2(0, 90)); + }; + */ + + + }; + } + + Vector3 target; + if (use_lookat_target) { + + target = lookat_target; + } else { + target = character_pos; + }; + + proposed.set_look_at(new_pos,target,Vector3(0,1,0)); + proposed = proposed * Transform(Matrix3(Vector3(1,0,0),Math::deg2rad(inclination)),Vector3()); //inclination + + + Vector exclude; + exclude.push_back(target_body); + + + + Vector3 rel = new_pos-target; + + for(int i=0;i<3;i++) { + + PhysicsServer::get_singleton()->query_intersection(clip_ray[i].query,get_world().get_space(),exclude); + PhysicsServer::get_singleton()->query_intersection_segment(clip_ray[i].query,target,target+Matrix3(Vector3(0,1,0),Math::deg2rad(autoturn_tolerance*(i-1.0))).xform(rel)); + clip_ray[i].clipped=false; + clip_ray[i].clip_pos=Vector3(); + } + target_pos=target; + clip_len=rel.length(); + + + +} + +void CharacterCamera::set_use_lookat_target(bool p_use, const Vector3 &p_lookat) { + + use_lookat_target = p_use; + lookat_target = p_lookat; +}; + + +void CharacterCamera::_notification(int p_what) { + + switch(p_what) { + + case NOTIFICATION_PROCESS: { + + + _compute_camera(); + } break; + + case NOTIFICATION_ENTER_SCENE: { + + if (type==CAMERA_FOLLOW) { + + set_orbit(orbit); + set_distance(distance); + } + + accepted=get_global_transform(); + proposed=accepted; + + target_body = RID(); + + Node* parent = get_parent(); + while (parent) { + PhysicsBody* p = parent->cast_to(); + if (p) { + target_body = p->get_body(); + break; + }; + parent = parent->get_parent(); + }; + + } break; + + case NOTIFICATION_TRANSFORM_CHANGED: { + + } break; + case NOTIFICATION_EXIT_SCENE: { + + if (type==CAMERA_FOLLOW) { + distance=get_distance(); + orbit=get_orbit(); + + } + } break; + + case NOTIFICATION_BECAME_CURRENT: { + + set_process(true); + } break; + case NOTIFICATION_LOST_CURRENT: { + + set_process(false); + } break; + } + +} + + +void CharacterCamera::set_camera_type(CameraType p_camera_type) { + + + if (p_camera_type==type) + return; + + type=p_camera_type; + + // do conversions +} + +CharacterCamera::CameraType CharacterCamera::get_camera_type() const { + + return type; + +} + +void CharacterCamera::set_orbit(const Vector2& p_orbit) { + + orbit=p_orbit; + + if(type == CAMERA_FOLLOW && is_inside_scene()) { + + Vector3 char_pos = get_global_transform().origin; + char_pos.y+=height; + float d = char_pos.distance_to(follow_pos); + + Matrix3 m; + m.rotate(Vector3(0,1,0),orbit.y); + m.rotate(Vector3(1,0,0),orbit.x); + + follow_pos=char_pos + m.get_axis(2) * d; + + } + +} +void CharacterCamera::set_orbit_x(float p_x) { + + orbit.x=p_x; + if(type == CAMERA_FOLLOW && is_inside_scene()) + set_orbit(Vector2( p_x, get_orbit().y )); +} +void CharacterCamera::set_orbit_y(float p_y) { + + + orbit.y=p_y; + if(type == CAMERA_FOLLOW && is_inside_scene()) + set_orbit(Vector2( get_orbit().x, p_y )); + +} +Vector2 CharacterCamera::get_orbit() const { + + + if (type == CAMERA_FOLLOW && is_inside_scene()) { + + Vector3 char_pos = get_global_transform().origin; + char_pos.y+=height; + Vector3 rel = (follow_pos - char_pos).normalized(); + Vector2 ret_orbit; + ret_orbit.x = Math::acos( Vector3(0,1,0).dot( rel ) ) - Math_PI * 0.5; + ret_orbit.y = Math::atan2(rel.x,rel.z); + return ret_orbit; + } + return orbit; +} + +void CharacterCamera::rotate_orbit(const Vector2& p_relative) { + + if (type == CAMERA_FOLLOW && is_inside_scene()) { + + Matrix3 m; + m.rotate(Vector3(0,1,0),Math::deg2rad(p_relative.y)); + m.rotate(Vector3(1,0,0),Math::deg2rad(p_relative.x)); + + Vector3 char_pos = get_global_transform().origin; + char_pos.y+=height; + Vector3 rel = (follow_pos - char_pos); + rel = m.xform(rel); + follow_pos=char_pos+rel; + + } + + orbit+=p_relative; +} + +void CharacterCamera::set_height(float p_height) { + + + height=p_height; +} + +float CharacterCamera::get_height() const { + + return height; + +} + +void CharacterCamera::set_max_orbit_x(float p_max) { + + max_orbit_x=p_max; +} + +float CharacterCamera::get_max_orbit_x() const { + + return max_orbit_x; +} + +void CharacterCamera::set_min_orbit_x(float p_min) { + + min_orbit_x=p_min; +} + +float CharacterCamera::get_min_orbit_x() const { + + return min_orbit_x; +} + +float CharacterCamera::get_min_distance() const { + + return min_distance; +} +float CharacterCamera::get_max_distance() const { + + return max_distance; +} + +void CharacterCamera::set_min_distance(float p_min) { + + min_distance=p_min; +} + +void CharacterCamera::set_max_distance(float p_max) { + + max_distance = p_max; +} + + +void CharacterCamera::set_distance(float p_distance) { + + if (type == CAMERA_FOLLOW && is_inside_scene()) { + + Vector3 char_pos = get_global_transform().origin; + char_pos.y+=height; + Vector3 rel = (follow_pos - char_pos).normalized(); + rel*=p_distance; + follow_pos=char_pos+rel; + + } + + distance=p_distance; +} + +float CharacterCamera::get_distance() const { + + if (type == CAMERA_FOLLOW && is_inside_scene()) { + + Vector3 char_pos = get_global_transform().origin; + char_pos.y+=height; + return (follow_pos - char_pos).length(); + + } + + return distance; +} + +void CharacterCamera::set_clip(bool p_enabled) { + + + clip=p_enabled; +} + +bool CharacterCamera::has_clip() const { + + return clip; + +} + + +void CharacterCamera::set_autoturn(bool p_enabled) { + + + autoturn=p_enabled; +} + +bool CharacterCamera::has_autoturn() const { + + return autoturn; + +} + +void CharacterCamera::set_autoturn_tolerance(float p_degrees) { + + + autoturn_tolerance=p_degrees; +} +float CharacterCamera::get_autoturn_tolerance() const { + + + return autoturn_tolerance; +} + +void CharacterCamera::set_inclination(float p_degrees) { + + + inclination=p_degrees; +} +float CharacterCamera::get_inclination() const { + + + return inclination; +} + + +void CharacterCamera::set_autoturn_speed(float p_speed) { + + + autoturn_speed=p_speed; +} +float CharacterCamera::get_autoturn_speed() const { + + return autoturn_speed; + +} + + + + + +void CharacterCamera::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("set_camera_type","type"),&CharacterCamera::set_camera_type); + ObjectTypeDB::bind_method(_MD("get_camera_type"),&CharacterCamera::get_camera_type); + ObjectTypeDB::bind_method(_MD("set_orbit","orbit"),&CharacterCamera::set_orbit); + ObjectTypeDB::bind_method(_MD("get_orbit"),&CharacterCamera::get_orbit); + ObjectTypeDB::bind_method(_MD("set_orbit_x","x"),&CharacterCamera::set_orbit_x); + ObjectTypeDB::bind_method(_MD("set_orbit_y","y"),&CharacterCamera::set_orbit_y); + ObjectTypeDB::bind_method(_MD("set_min_orbit_x","x"),&CharacterCamera::set_min_orbit_x); + ObjectTypeDB::bind_method(_MD("get_min_orbit_x"),&CharacterCamera::get_min_orbit_x); + ObjectTypeDB::bind_method(_MD("set_max_orbit_x","x"),&CharacterCamera::set_max_orbit_x); + ObjectTypeDB::bind_method(_MD("get_max_orbit_x"),&CharacterCamera::get_max_orbit_x); + ObjectTypeDB::bind_method(_MD("rotate_orbit"),&CharacterCamera::rotate_orbit); + ObjectTypeDB::bind_method(_MD("set_distance","distance"),&CharacterCamera::set_distance); + ObjectTypeDB::bind_method(_MD("get_distance"),&CharacterCamera::get_distance); + ObjectTypeDB::bind_method(_MD("set_clip","enable"),&CharacterCamera::set_clip); + ObjectTypeDB::bind_method(_MD("has_clip"),&CharacterCamera::has_clip); + ObjectTypeDB::bind_method(_MD("set_autoturn","enable"),&CharacterCamera::set_autoturn); + ObjectTypeDB::bind_method(_MD("has_autoturn"),&CharacterCamera::has_autoturn); + ObjectTypeDB::bind_method(_MD("set_autoturn_tolerance","degrees"),&CharacterCamera::set_autoturn_tolerance); + ObjectTypeDB::bind_method(_MD("get_autoturn_tolerance"),&CharacterCamera::get_autoturn_tolerance); + ObjectTypeDB::bind_method(_MD("set_autoturn_speed","speed"),&CharacterCamera::set_autoturn_speed); + ObjectTypeDB::bind_method(_MD("get_autoturn_speed"),&CharacterCamera::get_autoturn_speed); + ObjectTypeDB::bind_method(_MD("set_use_lookat_target","use","lookat"),&CharacterCamera::set_use_lookat_target, DEFVAL(Vector3())); + + ObjectTypeDB::bind_method(_MD("_ray_collision"),&CharacterCamera::_ray_collision); + + BIND_CONSTANT( CAMERA_FIXED ); + BIND_CONSTANT( CAMERA_FOLLOW ); +} + +void CharacterCamera::_ray_collision(Vector3 p_point, Vector3 p_normal, int p_subindex, ObjectID p_against,int p_idx) { + + + clip_ray[p_idx].clip_pos=p_point; + clip_ray[p_idx].clipped=true; +}; + +Transform CharacterCamera::get_camera_transform() const { + + return accepted; +} + + +CharacterCamera::CharacterCamera() { + + + type=CAMERA_FOLLOW; + height=1; + + orbit=Vector2(0,0); + + distance=3; + min_distance=2; + max_distance=5; + + autoturn=false; + autoturn_tolerance=15; + autoturn_speed=20; + + min_orbit_x=-50; + max_orbit_x=70; + inclination=0; + + clip=false; + use_lookat_target = false; + + for(int i=0;i<3;i++) { + clip_ray[i].query=PhysicsServer::get_singleton()->query_create(this, "_ray_collision", i, true); + clip_ray[i].clipped=false; + } + + +} + +CharacterCamera::~CharacterCamera() { + + for(int i=0;i<3;i++) { + PhysicsServer::get_singleton()->free(clip_ray[i].query); + } + + +} +#endif diff --git a/scene/3d/character_camera.h b/scene/3d/character_camera.h new file mode 100644 index 00000000000..f3bdef54a67 --- /dev/null +++ b/scene/3d/character_camera.h @@ -0,0 +1,167 @@ +/*************************************************************************/ +/* character_camera.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 CHARACTER_CAMERA_H +#define CHARACTER_CAMERA_H + + +#include "scene/3d/camera.h" +#if 0 +class CharacterCamera : public Camera { + + OBJ_TYPE( CharacterCamera, Camera ); +public: + + enum CameraType { + CAMERA_FIXED, + CAMERA_FOLLOW + }; + +private: + + + CameraType type; + + //used for follow + Vector3 follow_pos; + //used for fixed + Vector2 orbit; + float distance; + + float height; + + float min_distance; + float max_distance; + + float max_orbit_x; + float min_orbit_x; + + float inclination; + + bool clip; + bool autoturn; + float autoturn_tolerance; + float autoturn_speed; + + + + struct ClipRay { + RID query; + bool clipped; + Vector3 clip_pos; + }; + + ClipRay clip_ray[3]; + Vector3 target_pos; + float clip_len; + + + Transform accepted; + Vector3 proposed_pos; + + bool use_lookat_target; + Vector3 lookat_target; + + void _compute_camera(); + + RID ray_query; + RID left_turn_query; + RID right_turn_query; + RID target_body; + +protected: + + virtual void _request_camera_update() {} //ignore + + 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 *p_list) const; + void _notification(int p_what); + + static void _bind_methods(); + + void _ray_collision(Vector3 p_point, Vector3 p_normal, int p_subindex, ObjectID p_against,int p_idx); + +public: + + + void set_camera_type(CameraType p_camera_type); + CameraType get_camera_type() const; + + void set_orbit(const Vector2& p_orbit); + void set_orbit_x(float p_x); + void set_orbit_y(float p_y); + Vector2 get_orbit() const; + + void set_height(float p_height); + float get_height() const; + + void set_inclination(float p_degrees); + float get_inclination() const; + + void set_max_orbit_x(float p_max); + float get_max_orbit_x() const; + + void set_min_orbit_x(float p_min); + float get_min_orbit_x() const; + + void rotate_orbit(const Vector2& p_relative); + + void set_distance(float p_distance); + float get_distance() const; + + float get_min_distance() const; + float get_max_distance() const; + void set_min_distance(float p_min); + void set_max_distance(float p_max); + + + void set_clip(bool p_enabled); + bool has_clip() const; + + void set_autoturn(bool p_enabled); + bool has_autoturn() const; + + void set_autoturn_tolerance(float p_degrees); + float get_autoturn_tolerance() const; + + void set_autoturn_speed(float p_speed); + float get_autoturn_speed() const; + + void set_use_lookat_target(bool p_use, const Vector3 &p_lookat = Vector3()); + + virtual Transform get_camera_transform() const; + + CharacterCamera(); + ~CharacterCamera(); +}; + +VARIANT_ENUM_CAST( CharacterCamera::CameraType ); + +#endif +#endif // CHARACTER_CAMERA_H diff --git a/scene/3d/collision_object.cpp b/scene/3d/collision_object.cpp new file mode 100644 index 00000000000..7ad10d32223 --- /dev/null +++ b/scene/3d/collision_object.cpp @@ -0,0 +1,284 @@ +/*************************************************************************/ +/* collision_object.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 "collision_object.h" +#include "servers/physics_server.h" + +void CollisionObject::_update_shapes_from_children() { + + shapes.resize(0); + for(int i=0;icall("_add_to_collision_object",this); + } + +// _update_shapes(); +} + +void CollisionObject::_notification(int p_what) { + + switch(p_what) { + + case NOTIFICATION_ENTER_WORLD: { + + RID space = get_world()->get_space(); + if (area) { + PhysicsServer::get_singleton()->area_set_space(rid,space); + } else + PhysicsServer::get_singleton()->body_set_space(rid,space); + + //get space + } + + case NOTIFICATION_TRANSFORM_CHANGED: { + + if (area) + PhysicsServer::get_singleton()->area_set_transform(rid,get_global_transform()); + else + PhysicsServer::get_singleton()->body_set_state(rid,PhysicsServer::BODY_STATE_TRANSFORM,get_global_transform()); + + } break; + case NOTIFICATION_EXIT_WORLD: { + + if (area) { + PhysicsServer::get_singleton()->area_set_space(rid,RID()); + } else + PhysicsServer::get_singleton()->body_set_space(rid,RID()); + + } break; + } +} + +void CollisionObject::_update_shapes() { + + if (!rid.is_valid()) + return; + + if (area) + PhysicsServer::get_singleton()->area_clear_shapes(rid); + else + PhysicsServer::get_singleton()->body_clear_shapes(rid); + + for(int i=0;iarea_add_shape(rid,shapes[i].shape->get_rid(),shapes[i].xform); + else { + PhysicsServer::get_singleton()->body_add_shape(rid,shapes[i].shape->get_rid(),shapes[i].xform); + if (shapes[i].trigger) + PhysicsServer::get_singleton()->body_set_shape_as_trigger(rid,i,shapes[i].trigger); + } + } +} + + +bool CollisionObject::_set(const StringName& p_name, const Variant& p_value) { + String name=p_name; + + if (name=="shape_count") { + + shapes.resize(p_value); + _update_shapes(); + _change_notify(); + + } else if (name.begins_with("shapes/")) { + + int idx=name.get_slice("/",1).to_int(); + String what=name.get_slice("/",2); + if (what=="shape") + set_shape(idx,RefPtr(p_value)); + else if (what=="transform") + set_shape_transform(idx,p_value); + else if (what=="trigger") + set_shape_as_trigger(idx,p_value); + + + } else + return false; + + return true; + + +} + +bool CollisionObject::_get(const StringName& p_name,Variant &r_ret) const { + + String name=p_name; + + if (name=="shape_count") { + r_ret= shapes.size(); + } else if (name.begins_with("shapes/")) { + + int idx=name.get_slice("/",1).to_int(); + String what=name.get_slice("/",2); + if (what=="shape") + r_ret= get_shape(idx); + else if (what=="transform") + r_ret= get_shape_transform(idx); + else if (what=="trigger") + r_ret= is_shape_set_as_trigger(idx); + + } else + return false; + + return true; +} + +void CollisionObject::_get_property_list( List *p_list) const { + + p_list->push_back( PropertyInfo(Variant::INT,"shape_count",PROPERTY_HINT_RANGE,"0,256,1",PROPERTY_USAGE_NOEDITOR) ); + + for(int i=0;ipush_back( PropertyInfo(Variant::OBJECT,path+"shape",PROPERTY_HINT_RESOURCE_TYPE,"Shape",PROPERTY_USAGE_NOEDITOR) ); + p_list->push_back( PropertyInfo(Variant::TRANSFORM,path+"transform",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_NOEDITOR) ); + p_list->push_back( PropertyInfo(Variant::BOOL,path+"trigger",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_NOEDITOR) ); + + } +} + +void CollisionObject::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("add_shape","shape:Shape","transform"),&CollisionObject::add_shape,DEFVAL(Transform())); + ObjectTypeDB::bind_method(_MD("get_shape_count"),&CollisionObject::get_shape_count); + ObjectTypeDB::bind_method(_MD("set_shape","shape_idx","shape:Shape"),&CollisionObject::set_shape); + ObjectTypeDB::bind_method(_MD("set_shape_transform","shape_idx","transform"),&CollisionObject::set_shape_transform); +// ObjectTypeDB::bind_method(_MD("set_shape_transform","shape_idx","transform"),&CollisionObject::set_shape_transform); + ObjectTypeDB::bind_method(_MD("set_shape_as_trigger","shape_idx","enable"),&CollisionObject::set_shape_as_trigger); + ObjectTypeDB::bind_method(_MD("is_shape_set_as_trigger","shape_idx"),&CollisionObject::is_shape_set_as_trigger); + ObjectTypeDB::bind_method(_MD("get_shape:Shape","shape_idx"),&CollisionObject::get_shape); + ObjectTypeDB::bind_method(_MD("get_shape_transform","shape_idx"),&CollisionObject::get_shape_transform); + ObjectTypeDB::bind_method(_MD("remove_shape","shape_idx"),&CollisionObject::remove_shape); + ObjectTypeDB::bind_method(_MD("clear_shapes"),&CollisionObject::clear_shapes); + ObjectTypeDB::bind_method(_MD("get_rid"),&CollisionObject::get_rid); + +} + + +void CollisionObject::add_shape(const Ref& p_shape, const Transform& p_transform) { + + ShapeData sdata; + sdata.shape=p_shape; + sdata.xform=p_transform; + shapes.push_back(sdata); + _update_shapes(); + +} +int CollisionObject::get_shape_count() const { + + return shapes.size(); + +} +void CollisionObject::set_shape(int p_shape_idx, const Ref& p_shape) { + + ERR_FAIL_INDEX(p_shape_idx,shapes.size()); + shapes[p_shape_idx].shape=p_shape; + _update_shapes(); +} + +void CollisionObject::set_shape_transform(int p_shape_idx, const Transform& p_transform) { + + ERR_FAIL_INDEX(p_shape_idx,shapes.size()); + shapes[p_shape_idx].xform=p_transform; + + _update_shapes(); +} + +Ref CollisionObject::get_shape(int p_shape_idx) const { + + ERR_FAIL_INDEX_V(p_shape_idx,shapes.size(),Ref()); + return shapes[p_shape_idx].shape; + +} +Transform CollisionObject::get_shape_transform(int p_shape_idx) const { + + ERR_FAIL_INDEX_V(p_shape_idx,shapes.size(),Transform()); + return shapes[p_shape_idx].xform; + +} +void CollisionObject::remove_shape(int p_shape_idx) { + + ERR_FAIL_INDEX(p_shape_idx,shapes.size()); + shapes.remove(p_shape_idx); + + _update_shapes(); +} + +void CollisionObject::clear_shapes() { + + shapes.clear(); + + _update_shapes(); +} + +void CollisionObject::set_shape_as_trigger(int p_shape_idx, bool p_trigger) { + + ERR_FAIL_INDEX(p_shape_idx,shapes.size()); + shapes[p_shape_idx].trigger=p_trigger; + if (!area && rid.is_valid()) { + + PhysicsServer::get_singleton()->body_set_shape_as_trigger(rid,p_shape_idx,p_trigger); + + } +} + +bool CollisionObject::is_shape_set_as_trigger(int p_shape_idx) const { + + ERR_FAIL_INDEX_V(p_shape_idx,shapes.size(),false); + return shapes[p_shape_idx].trigger; +} + +CollisionObject::CollisionObject(RID p_rid, bool p_area) { + + rid=p_rid; + area=p_area; + if (p_area) { + PhysicsServer::get_singleton()->area_attach_object_instance_ID(rid,get_instance_ID()); + } else { + PhysicsServer::get_singleton()->body_attach_object_instance_ID(rid,get_instance_ID()); + } +// set_transform_notify(true); + +} + + +CollisionObject::CollisionObject() { + + + //owner= + + //set_transform_notify(true); +} + +CollisionObject::~CollisionObject() { + + PhysicsServer::get_singleton()->free(rid); +} diff --git a/scene/3d/collision_object.h b/scene/3d/collision_object.h new file mode 100644 index 00000000000..54dc6508abb --- /dev/null +++ b/scene/3d/collision_object.h @@ -0,0 +1,90 @@ +/*************************************************************************/ +/* collision_object.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 COLLISION_OBJECT_H +#define COLLISION_OBJECT_H + +#include "scene/3d/spatial.h" +#include "scene/resources/shape.h" + +class CollisionObject : public Spatial { + + OBJ_TYPE( CollisionObject, Spatial ); + + bool area; + RID rid; + + struct ShapeData { + Transform xform; + Ref shape; + bool trigger; + + ShapeData() { + trigger=false; + } + + }; + + + Vector shapes; + + void _update_shapes(); + +friend class CollisionShape; +friend class CollisionPolygon; + void _update_shapes_from_children(); +protected: + + CollisionObject(RID p_rid, bool p_area); + + void _notification(int p_what); + 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 *p_list) const; + static void _bind_methods(); +public: + + + void add_shape(const Ref& p_shape, const Transform& p_transform=Transform()); + int get_shape_count() const; + void set_shape(int p_shape_idx, const Ref& p_shape); + void set_shape_transform(int p_shape_idx, const Transform& p_transform); + Ref get_shape(int p_shape_idx) const; + Transform get_shape_transform(int p_shape_idx) const; + void remove_shape(int p_shape_idx); + void clear_shapes(); + void set_shape_as_trigger(int p_shape_idx, bool p_trigger); + bool is_shape_set_as_trigger(int p_shape_idx) const; + + _FORCE_INLINE_ RID get_rid() const { return rid; } + + CollisionObject(); + ~CollisionObject(); +}; + +#endif // COLLISION_OBJECT__H diff --git a/scene/3d/editable_shape.cpp b/scene/3d/editable_shape.cpp new file mode 100644 index 00000000000..ab3f8320287 --- /dev/null +++ b/scene/3d/editable_shape.cpp @@ -0,0 +1,85 @@ +/*************************************************************************/ +/* editable_shape.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 "editable_shape.h" + + +void EditableShape::_notification(int p_what) { + + + +} + + +void EditableShape::set_bsp_tree(const BSP_Tree& p_bsp) { + + bsp=p_bsp; +} + +void EditableShape::set_shape(const Ref& p_shape) { + + shape=p_shape; +} + + + +EditableShape::EditableShape() +{ +} + + + +///////////////////////// + + +void EditableSphere::set_radius(float p_radius) { + + radius=p_radius; + update_gizmo(); + _change_notify("params/radius"); +} + + +float EditableSphere::get_radius() const{ + + return radius; +} + + +void EditableSphere::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("set_radius","radius"),&EditableSphere::set_radius); + ObjectTypeDB::bind_method(_MD("get_radius"),&EditableSphere::get_radius); + + ADD_PROPERTY( PropertyInfo(Variant::REAL,"params/radius",PROPERTY_HINT_EXP_RANGE,"0.001,16384,0.001"),_SCS("set_radius"),_SCS("get_radius")); +} + +EditableSphere::EditableSphere() { + + radius=1.0; +} diff --git a/scene/3d/editable_shape.h b/scene/3d/editable_shape.h new file mode 100644 index 00000000000..9accea575c7 --- /dev/null +++ b/scene/3d/editable_shape.h @@ -0,0 +1,73 @@ +/*************************************************************************/ +/* editable_shape.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 EDITABLE_SHAPE_H +#define EDITABLE_SHAPE_H + +#include "scene/3d/spatial.h" +#include "scene/resources/shape.h" + +class EditableShape : public Spatial { + + OBJ_TYPE(EditableShape,Spatial); + + //can hold either of those + BSP_Tree bsp; + Ref shape; + + void _update_parent(); +protected: + + void _notification(int p_what); + + void set_bsp_tree(const BSP_Tree& p_bsp); + void set_shape(const Ref& p_shape); +public: + EditableShape(); +}; + + +class EditableSphere : public EditableShape { + + OBJ_TYPE( EditableSphere, EditableShape ); + + + float radius; +protected: + + static void _bind_methods(); +public: + + void set_radius(float p_radius); + float get_radius() const; + + EditableSphere(); +}; + + +#endif // EDITABLE_SHAPE_H diff --git a/scene/3d/follow_camera.cpp b/scene/3d/follow_camera.cpp new file mode 100644 index 00000000000..20a1654b92b --- /dev/null +++ b/scene/3d/follow_camera.cpp @@ -0,0 +1,778 @@ +/*************************************************************************/ +/* follow_camera.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 "follow_camera.h" + + +#include "physics_body.h" +#include "scene/resources/surface_tool.h" + +void FollowCamera::_set_initial_orbit(const Vector2& p_orbit) { + + initial_orbit=p_orbit; + set_orbit(p_orbit); +} + + + +void FollowCamera::_clear_queries() { + + if (!queries_active) + return; +#if 0 + for(int i=0;i<3;i++) + PhysicsServer::get_singleton()->query_clear(clip_ray[i].query); +#endif + queries_active=false; + +} + +void FollowCamera::_compute_camera() { + + // update the transform with the next proposed transform (camera is 1 logic frame delayed) + + /* + float time = get_root_node()->get_frame_time(); + Vector3 oldp = accepted.get_origin(); + Vector3 newp = proposed.get_origin(); + + float frame_dist = time * + if (oldp.distance_to(newp) > + */ + + float time = get_process_delta_time(); + bool noblend=false; + + if (clip) { + + if ((clip_ray[0].clipped==clip_ray[2].clipped || fullclip) && clip_ray[1].clipped) { + //all have been clipped + proposed_pos=clip_ray[1].clip_pos-extraclip*(proposed_pos-target_pos).normalized(); + if (clip_ray[0].clipped) + fullclip=true; + noblend=true; + + + } else { + + + //Vector3 rel=follow_pos-target_pos; + + if (clip_ray[0].clipped && !clip_ray[2].clipped) { + + float distance = target_pos.distance_to(clip_ray[0].clip_pos); + real_t amount = 1.0-(distance/clip_len); + amount = CLAMP(amount,0,1)*autoturn_speed*time; + if (clip_ray[1].clipped) + amount*=2.0; + //rotate_rel=Matrix3(Vector3(0,1,0),amount).xform(rel); + rotate_orbit(Vector2(0,amount)); + + } else if (clip_ray[2].clipped && !clip_ray[0].clipped) { + + float distance = target_pos.distance_to(clip_ray[2].clip_pos); + real_t amount = 1.0-(distance/clip_len); + amount = CLAMP(amount,0,1)*autoturn_speed*time; + if (clip_ray[1].clipped) + amount*=2.0; + rotate_orbit(Vector2(0,-amount)); + } + + fullclip=false; + + } + } + + + Vector3 base_pos = get_global_transform().origin; + Vector3 pull_from = base_pos; + pull_from.y+=height; // height compensate + + + + Vector3 camera_target; + if (use_lookat_target) { + + camera_target = lookat_target; + } else { + camera_target = base_pos; + }; + + Transform proposed; + proposed.set_look_at(proposed_pos,camera_target,up_vector); + proposed = proposed * Transform(Matrix3(Vector3(1,0,0),Math::deg2rad(inclination)),Vector3()); //inclination + + + accepted=proposed; + if (smooth && !noblend) { + + + Vector3 vec1 = accepted.origin; + Vector3 vec2 = final.origin; + final.origin = vec2.linear_interpolate(vec1, MIN(1,smooth_pos_ratio * time));; + + Quat q1 = accepted.basis; + Quat q2 = final.basis; + final.basis = q2.slerp(q1, MIN(1,smooth_rot_ratio * time)); + + } else { + final=accepted; + } + + _update_camera(); + + // calculate the next proposed transform + + + Vector3 new_pos; + + { /*follow code*/ + + + + /* calculate some variables */ + + Vector3 rel = follow_pos - pull_from; + + float l = rel.length(); + Vector3 rel_n = (l > 0) ? (rel/l) : Vector3(); + float ang = Math::acos(rel_n.dot( Vector3(0,1,0) )); + + Vector3 tangent = rel_n; + tangent.y=0; // get rid of y + if (tangent.length_squared() < CMP_EPSILON2) + tangent=Vector3(0,0,1); // use Z as tangent if rel is parallel to y + else + tangent.normalize(); + + /* now start applying the rules */ + + //clip distance + if (l > max_distance) + l=max_distance; + if (l < min_distance) + l=min_distance; + + //fix angle + + float ang_min = Math_PI * 0.5 + Math::deg2rad(min_orbit_x); + float ang_max = Math_PI * 0.5 + Math::deg2rad(max_orbit_x); + + if (angang_max) + ang=ang_max; + + /* finally, rebuild the validated camera position */ + + new_pos=Vector3(0,Math::cos(ang),0); + new_pos+=tangent*Math::sin(ang); + new_pos*=l; + new_pos+=pull_from; + follow_pos=new_pos; + + } + + proposed_pos=new_pos; + + Vector3 rel = new_pos-camera_target; + + + if (clip) { + + Vector exclude; + exclude.push_back(target_body); + + for(int i=0;i<3;i++) { + + clip_ray[i].clipped=false; + clip_ray[i].clip_pos=Vector3(); + clip_ray[i].cast_pos=camera_target; + + Vector3 cast_to = camera_target+Matrix3(Vector3(0,1,0),Math::deg2rad(autoturn_tolerance*(i-1.0))).xform(rel); + + + if (i!=1) { + + Vector3 side = rel.cross(Vector3(0,1,0)).normalized()*(i-1.0); + clip_ray[i].cast_pos+side*target_width+rel.normalized()*target_width; + + Vector3 d = -rel; + d.rotate(Vector3(0,1,0),Math::deg2rad(get_fov())*(i-1.0)); + Plane p(new_pos,new_pos+d,new_pos+Vector3(0,1,0)); //fov clipping plane, build a face and use it as plane, facing doesn't matter + Vector3 intersect; + if (p.intersects_segment(clip_ray[i].cast_pos,cast_to,&intersect)) + cast_to=intersect; + + } else { + + cast_to+=rel.normalized()*extraclip; + } + + // PhysicsServer::get_singleton()->query_intersection(clip_ray[i].query,get_world()->get_space(),exclude); + // PhysicsServer::get_singleton()->query_intersection_segment(clip_ray[i].query,clip_ray[i].cast_pos,cast_to); + + + + + } + + queries_active=true; + } else { + + _clear_queries(); + } + target_pos=camera_target; + clip_len=rel.length(); + +} + +void FollowCamera::set_use_lookat_target(bool p_use, const Vector3 &p_lookat) { + + use_lookat_target = p_use; + lookat_target = p_lookat; +}; + + +void FollowCamera::_notification(int p_what) { + + switch(p_what) { + + case NOTIFICATION_PROCESS: { + + + _compute_camera(); + } break; + + case NOTIFICATION_ENTER_WORLD: { + + set_orbit(orbit); + set_distance(distance); + + accepted=final=get_global_transform(); + proposed_pos=accepted.origin; + + target_body = RID(); +/* + Node* parent = get_parent(); + while (parent) { + PhysicsBody* p = parent->cast_to(); + if (p) { + target_body = p->get_body(); + break; + }; + parent = parent->get_parent(); + }; +*/ + set_process(true); + + } break; + case NOTIFICATION_TRANSFORM_CHANGED: { + + } break; + case NOTIFICATION_EXIT_WORLD: { + + distance=get_distance(); + orbit=get_orbit(); + _clear_queries(); + + } break; + case NOTIFICATION_BECAME_CURRENT: { + + set_process(true); + } break; + case NOTIFICATION_LOST_CURRENT: { + + set_process(false); + _clear_queries(); + + } break; + } + +} + + + +void FollowCamera::set_orbit(const Vector2& p_orbit) { + + orbit=p_orbit; + + if(is_inside_scene()) { + + Vector3 char_pos = get_global_transform().origin; + char_pos.y+=height; + float d = char_pos.distance_to(follow_pos); + + Matrix3 m; + m.rotate(Vector3(0,1,0),orbit.y); + m.rotate(Vector3(1,0,0),orbit.x); + + follow_pos=char_pos + m.get_axis(2) * d; + + } + + update_gizmo(); + +} +void FollowCamera::set_orbit_x(float p_x) { + + orbit.x=p_x; + if(is_inside_scene()) + set_orbit(Vector2( p_x, get_orbit().y )); +} +void FollowCamera::set_orbit_y(float p_y) { + + + orbit.y=p_y; + if(is_inside_scene()) + set_orbit(Vector2( get_orbit().x, p_y )); + +} +Vector2 FollowCamera::get_orbit() const { + + + if (is_inside_scene()) { + + Vector3 char_pos = get_global_transform().origin; + char_pos.y+=height; + Vector3 rel = (follow_pos - char_pos).normalized(); + Vector2 ret_orbit; + ret_orbit.x = Math::acos( Vector3(0,1,0).dot( rel ) ) - Math_PI * 0.5; + ret_orbit.y = Math::atan2(rel.x,rel.z); + return ret_orbit; + } + return orbit; +} + +void FollowCamera::rotate_orbit(const Vector2& p_relative) { + + if (is_inside_scene()) { + + Matrix3 m; + m.rotate(Vector3(0,1,0),Math::deg2rad(p_relative.y)); + m.rotate(Vector3(1,0,0),Math::deg2rad(p_relative.x)); + + Vector3 char_pos = get_global_transform().origin; + char_pos.y+=height; + Vector3 rel = (follow_pos - char_pos); + rel = m.xform(rel); + follow_pos=char_pos+rel; + + } + + orbit+=p_relative; + update_gizmo(); +} + +void FollowCamera::set_height(float p_height) { + + + height=p_height; + update_gizmo(); +} + +float FollowCamera::get_height() const { + + return height; + +} + +void FollowCamera::set_max_orbit_x(float p_max) { + + max_orbit_x=p_max; + update_gizmo(); +} + +float FollowCamera::get_max_orbit_x() const { + + return max_orbit_x; +} + +void FollowCamera::set_min_orbit_x(float p_min) { + + min_orbit_x=p_min; + update_gizmo(); +} + +float FollowCamera::get_min_orbit_x() const { + + return min_orbit_x; +} + +float FollowCamera::get_min_distance() const { + + return min_distance; +} +float FollowCamera::get_max_distance() const { + + return max_distance; +} + +void FollowCamera::set_min_distance(float p_min) { + + min_distance=p_min; + update_gizmo(); +} + +void FollowCamera::set_max_distance(float p_max) { + + max_distance = p_max; + update_gizmo(); +} + + +void FollowCamera::set_distance(float p_distance) { + + if (is_inside_scene()) { + + Vector3 char_pos = get_global_transform().origin; + char_pos.y+=height; + Vector3 rel = (follow_pos - char_pos).normalized(); + rel*=p_distance; + follow_pos=char_pos+rel; + + } + + distance=p_distance; +} + +float FollowCamera::get_distance() const { + + if (is_inside_scene()) { + + Vector3 char_pos = get_global_transform().origin; + char_pos.y+=height; + return (follow_pos - char_pos).length(); + + } + + return distance; +} + +void FollowCamera::set_clip(bool p_enabled) { + + + clip=p_enabled; + + if (!p_enabled) + _clear_queries(); +} + +bool FollowCamera::has_clip() const { + + return clip; + +} + + +void FollowCamera::set_autoturn(bool p_enabled) { + + + autoturn=p_enabled; +} + +bool FollowCamera::has_autoturn() const { + + return autoturn; + +} + +void FollowCamera::set_autoturn_tolerance(float p_degrees) { + + + autoturn_tolerance=p_degrees; +} +float FollowCamera::get_autoturn_tolerance() const { + + + return autoturn_tolerance; +} + +void FollowCamera::set_inclination(float p_degrees) { + + + inclination=p_degrees; +} +float FollowCamera::get_inclination() const { + + + return inclination; +} + + +void FollowCamera::set_autoturn_speed(float p_speed) { + + + autoturn_speed=p_speed; +} + +float FollowCamera::get_autoturn_speed() const { + + return autoturn_speed; + +} + + +RES FollowCamera::_get_gizmo_geometry() const { + + Ref surface_tool( memnew( SurfaceTool )); + + Ref mat( memnew( FixedMaterial )); + + mat->set_parameter( FixedMaterial::PARAM_DIFFUSE,Color(1.0,0.5,1.0,0.3) ); + mat->set_line_width(4); + mat->set_flag(Material::FLAG_DOUBLE_SIDED,true); + mat->set_flag(Material::FLAG_UNSHADED,true); + mat->set_hint(Material::HINT_NO_DEPTH_DRAW,true); + + surface_tool->begin(Mesh::PRIMITIVE_LINES); + surface_tool->set_material(mat); + + + int steps=16; + + Vector3 base_up = Matrix3(Vector3(1,0,0),Math::deg2rad(max_orbit_x)).get_axis(2); + Vector3 base_down = Matrix3(Vector3(1,0,0),Math::deg2rad(min_orbit_x)).get_axis(2); + + Vector3 ofs(0,height,0); + + for(int i=0;iadd_vertex(ofs+up*min_distance); + surface_tool->add_vertex(ofs+up*max_distance); + surface_tool->add_vertex(ofs+up*min_distance); + surface_tool->add_vertex(ofs+up2*min_distance); + surface_tool->add_vertex(ofs+up*max_distance); + surface_tool->add_vertex(ofs+up2*max_distance); + + surface_tool->add_vertex(ofs+down*min_distance); + surface_tool->add_vertex(ofs+down*max_distance); + surface_tool->add_vertex(ofs+down*min_distance); + surface_tool->add_vertex(ofs+down2*min_distance); + surface_tool->add_vertex(ofs+down*max_distance); + surface_tool->add_vertex(ofs+down2*max_distance); + + int substeps = 8; + + for(int j=0;jadd_vertex(ofs+a); + surface_tool->add_vertex(ofs+b); + surface_tool->add_vertex(ofs+am); + surface_tool->add_vertex(ofs+bm); + + } + } + + + return surface_tool->commit(); + + +} + + +void FollowCamera::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("_set_initial_orbit","orbit"),&FollowCamera::_set_initial_orbit); + ObjectTypeDB::bind_method(_MD("set_orbit","orbit"),&FollowCamera::set_orbit); + ObjectTypeDB::bind_method(_MD("get_orbit"),&FollowCamera::get_orbit); + ObjectTypeDB::bind_method(_MD("set_orbit_x","x"),&FollowCamera::set_orbit_x); + ObjectTypeDB::bind_method(_MD("set_orbit_y","y"),&FollowCamera::set_orbit_y); + ObjectTypeDB::bind_method(_MD("set_min_orbit_x","x"),&FollowCamera::set_min_orbit_x); + ObjectTypeDB::bind_method(_MD("get_min_orbit_x"),&FollowCamera::get_min_orbit_x); + ObjectTypeDB::bind_method(_MD("set_max_orbit_x","x"),&FollowCamera::set_max_orbit_x); + ObjectTypeDB::bind_method(_MD("get_max_orbit_x"),&FollowCamera::get_max_orbit_x); + ObjectTypeDB::bind_method(_MD("set_height","height"),&FollowCamera::set_height); + ObjectTypeDB::bind_method(_MD("get_height"),&FollowCamera::get_height); + ObjectTypeDB::bind_method(_MD("set_inclination","inclination"),&FollowCamera::set_inclination); + ObjectTypeDB::bind_method(_MD("get_inclination"),&FollowCamera::get_inclination); + + ObjectTypeDB::bind_method(_MD("rotate_orbit"),&FollowCamera::rotate_orbit); + ObjectTypeDB::bind_method(_MD("set_distance","distance"),&FollowCamera::set_distance); + ObjectTypeDB::bind_method(_MD("get_distance"),&FollowCamera::get_distance); + ObjectTypeDB::bind_method(_MD("set_max_distance","max_distance"),&FollowCamera::set_max_distance); + ObjectTypeDB::bind_method(_MD("get_max_distance"),&FollowCamera::get_max_distance); + ObjectTypeDB::bind_method(_MD("set_min_distance","min_distance"),&FollowCamera::set_min_distance); + ObjectTypeDB::bind_method(_MD("get_min_distance"),&FollowCamera::get_min_distance); + ObjectTypeDB::bind_method(_MD("set_clip","enable"),&FollowCamera::set_clip); + ObjectTypeDB::bind_method(_MD("has_clip"),&FollowCamera::has_clip); + ObjectTypeDB::bind_method(_MD("set_autoturn","enable"),&FollowCamera::set_autoturn); + ObjectTypeDB::bind_method(_MD("has_autoturn"),&FollowCamera::has_autoturn); + ObjectTypeDB::bind_method(_MD("set_autoturn_tolerance","degrees"),&FollowCamera::set_autoturn_tolerance); + ObjectTypeDB::bind_method(_MD("get_autoturn_tolerance"),&FollowCamera::get_autoturn_tolerance); + ObjectTypeDB::bind_method(_MD("set_autoturn_speed","speed"),&FollowCamera::set_autoturn_speed); + ObjectTypeDB::bind_method(_MD("get_autoturn_speed"),&FollowCamera::get_autoturn_speed); + ObjectTypeDB::bind_method(_MD("set_smoothing","enable"),&FollowCamera::set_smoothing); + ObjectTypeDB::bind_method(_MD("has_smoothing"),&FollowCamera::has_smoothing); + ObjectTypeDB::bind_method(_MD("set_rotation_smoothing","amount"),&FollowCamera::set_rotation_smoothing); + ObjectTypeDB::bind_method(_MD("get_rotation_smoothing"),&FollowCamera::get_rotation_smoothing); + ObjectTypeDB::bind_method(_MD("set_translation_smoothing","amount"),&FollowCamera::set_translation_smoothing); + ObjectTypeDB::bind_method(_MD("get_translation_smoothing"),&FollowCamera::get_translation_smoothing); + ObjectTypeDB::bind_method(_MD("set_use_lookat_target","use","lookat"),&FollowCamera::set_use_lookat_target, DEFVAL(Vector3())); + ObjectTypeDB::bind_method(_MD("set_up_vector","vector"),&FollowCamera::set_up_vector); + ObjectTypeDB::bind_method(_MD("get_up_vector"),&FollowCamera::get_up_vector); + + ObjectTypeDB::bind_method(_MD("_ray_collision"),&FollowCamera::_ray_collision); + + ADD_PROPERTY( PropertyInfo( Variant::VECTOR2, "orbit" ), _SCS("_set_initial_orbit"),_SCS("get_orbit") ); + ADD_PROPERTY( PropertyInfo( Variant::REAL, "height", PROPERTY_HINT_RANGE,"-1024,1024,0.01" ), _SCS("set_height"), _SCS("get_height") ); + ADD_PROPERTY( PropertyInfo( Variant::REAL, "inclination", PROPERTY_HINT_RANGE,"-90,90,0.01" ), _SCS("set_inclination"), _SCS("get_inclination") ); + ADD_PROPERTY( PropertyInfo( Variant::REAL, "max_orbit_x", PROPERTY_HINT_RANGE,"-90,90,0.01" ), _SCS("set_max_orbit_x"), _SCS("get_max_orbit_x") ); + ADD_PROPERTY( PropertyInfo( Variant::REAL, "min_orbit_x", PROPERTY_HINT_RANGE,"-90,90,0.01" ), _SCS("set_min_orbit_x"), _SCS("get_min_orbit_x") ); + ADD_PROPERTY( PropertyInfo( Variant::REAL, "min_distance", PROPERTY_HINT_RANGE,"0,100,0.01" ), _SCS("set_min_distance"), _SCS("get_min_distance") ); + ADD_PROPERTY( PropertyInfo( Variant::REAL, "max_distance", PROPERTY_HINT_RANGE,"0,100,0.01" ), _SCS("set_max_distance"), _SCS("get_max_distance") ); + ADD_PROPERTY( PropertyInfo( Variant::REAL, "distance", PROPERTY_HINT_RANGE,"0.01,1024,0,01"), _SCS("set_distance"), _SCS("get_distance") ); + ADD_PROPERTY( PropertyInfo( Variant::BOOL, "clip"), _SCS("set_clip"), _SCS("has_clip") ); + ADD_PROPERTY( PropertyInfo( Variant::BOOL, "autoturn"), _SCS("set_autoturn"), _SCS("has_autoturn") ); + ADD_PROPERTY( PropertyInfo( Variant::REAL, "autoturn_tolerance", PROPERTY_HINT_RANGE,"1,90,0.01") , _SCS("set_autoturn_tolerance"), _SCS("get_autoturn_tolerance") ); + ADD_PROPERTY( PropertyInfo( Variant::REAL, "autoturn_speed", PROPERTY_HINT_RANGE,"1,90,0.01"), _SCS("set_autoturn_speed"), _SCS("get_autoturn_speed") ); + ADD_PROPERTY( PropertyInfo( Variant::BOOL, "smoothing"), _SCS("set_smoothing"), _SCS("has_smoothing") ); + ADD_PROPERTY( PropertyInfo( Variant::REAL, "translation_smooth", PROPERTY_HINT_RANGE,"0.01,128,0.01"), _SCS("set_translation_smoothing"), _SCS("get_translation_smoothing") ); + ADD_PROPERTY( PropertyInfo( Variant::REAL, "rotation_smooth", PROPERTY_HINT_RANGE,"0.01,128,0.01"), _SCS("set_rotation_smoothing"), _SCS("get_rotation_smoothing") ); + + +} + +void FollowCamera::_ray_collision(Vector3 p_point, Vector3 p_normal, int p_subindex, ObjectID p_against,int p_idx) { + + clip_ray[p_idx].clip_pos=p_point; + clip_ray[p_idx].clipped=true; + +}; + +Transform FollowCamera::get_camera_transform() const { + + return final; +} + +void FollowCamera::set_smoothing(bool p_enable) { + + smooth=p_enable; +} + +bool FollowCamera::has_smoothing() const { + + return smooth; +} + +void FollowCamera::set_translation_smoothing(float p_amount) { + + smooth_pos_ratio=p_amount; +} +float FollowCamera::get_translation_smoothing() const { + + return smooth_pos_ratio; +} + +void FollowCamera::set_rotation_smoothing(float p_amount) { + + smooth_rot_ratio=p_amount; + +} + +void FollowCamera::set_up_vector(const Vector3& p_up) { + + up_vector=p_up; +} + +Vector3 FollowCamera::get_up_vector() const{ + + return up_vector; +} + +float FollowCamera::get_rotation_smoothing() const { + + return smooth_pos_ratio; + +} + + +FollowCamera::FollowCamera() { + + + height=1; + + orbit=Vector2(0,0); + up_vector=Vector3(0,1,0); + + distance=3; + min_distance=2; + max_distance=5; + + autoturn=true; + autoturn_tolerance=10; + autoturn_speed=80; + + min_orbit_x=-50; + max_orbit_x=70; + inclination=0; + target_width=0.3; + + clip=true; + use_lookat_target = false; + extraclip=0.3; + fullclip=false; + + smooth=true; + smooth_rot_ratio=10; + smooth_pos_ratio=10; + + + for(int i=0;i<3;i++) { +// clip_ray[i].query=PhysicsServer::get_singleton()->query_create(this, "_ray_collision", i, true); + clip_ray[i].clipped=false; + } + + queries_active=false; + + +} + +FollowCamera::~FollowCamera() { + + for(int i=0;i<3;i++) { + PhysicsServer::get_singleton()->free(clip_ray[i].query); + } + + +} diff --git a/scene/3d/follow_camera.h b/scene/3d/follow_camera.h new file mode 100644 index 00000000000..10912eb6067 --- /dev/null +++ b/scene/3d/follow_camera.h @@ -0,0 +1,180 @@ +/*************************************************************************/ +/* follow_camera.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 FOLLOW_CAMERA_H +#define FOLLOW_CAMERA_H + +#include "scene/3d/camera.h" + +class FollowCamera : public Camera { + + OBJ_TYPE( FollowCamera, Camera ); + +private: + + + //used for follow + Vector3 follow_pos; + //used for fixed + Vector2 initial_orbit; + Vector2 orbit; + float distance; + + float height; + float target_width; + + float min_distance; + float max_distance; + + float max_orbit_x; + float min_orbit_x; + + float inclination; + float extraclip; + bool fullclip; + + bool clip; + bool autoturn; + float autoturn_tolerance; + float autoturn_speed; + + bool smooth; + float smooth_rot_ratio; + float smooth_pos_ratio; + + + + struct ClipRay { + RID query; + bool clipped; + Vector3 cast_pos; + Vector3 clip_pos; + }; + + ClipRay clip_ray[3]; + Vector3 target_pos; + float clip_len; + + Vector3 up_vector; + + + virtual RES _get_gizmo_geometry() const; + + Transform ted; + Vector3 proposed_pos; + Transform accepted; + Transform final; + RID target_body; + + bool use_lookat_target; + Vector3 lookat_target; + + void _compute_camera(); + + bool queries_active; + void _clear_queries(); + + void _set_initial_orbit(const Vector2& p_orbit); + +protected: + + virtual void _request_camera_update() {} //ignore + + void _notification(int p_what); + + static void _bind_methods(); + + void _ray_collision(Vector3 p_point, Vector3 p_normal, int p_subindex, ObjectID p_against,int p_idx); + +public: + + + void set_orbit(const Vector2& p_orbit); + void set_orbit_x(float p_x); + void set_orbit_y(float p_y); + Vector2 get_orbit() const; + + void set_height(float p_height); + float get_height() const; + + void set_inclination(float p_degrees); + float get_inclination() const; + + void set_max_orbit_x(float p_max); + float get_max_orbit_x() const; + + void set_min_orbit_x(float p_min); + float get_min_orbit_x() const; + + void rotate_orbit(const Vector2& p_relative); + + void set_distance(float p_distance); + float get_distance() const; + + float get_min_distance() const; + float get_max_distance() const; + void set_min_distance(float p_min); + void set_max_distance(float p_max); + + /** FINISH THIS AND CLEAN IT UP */ + + void set_clip(bool p_enabled); + bool has_clip() const; + + void set_autoturn(bool p_enabled); + bool has_autoturn() const; + + void set_autoturn_tolerance(float p_degrees); + float get_autoturn_tolerance() const; + + void set_autoturn_speed(float p_speed); + float get_autoturn_speed() const; + + void set_smoothing(bool p_enable); + bool has_smoothing() const; + + void set_translation_smoothing(float p_amount); + float get_translation_smoothing() const; + + void set_rotation_smoothing(float p_amount); + float get_rotation_smoothing() const; + + void set_use_lookat_target(bool p_use, const Vector3 &p_lookat = Vector3()); + + void set_up_vector(const Vector3& p_up); + Vector3 get_up_vector() const; + + virtual Transform get_camera_transform() const; + + FollowCamera(); + ~FollowCamera(); +}; + + + +#endif // FOLLOW_CAMERA_H diff --git a/scene/3d/interpolated_camera.cpp b/scene/3d/interpolated_camera.cpp new file mode 100644 index 00000000000..4d8c9cf7a52 --- /dev/null +++ b/scene/3d/interpolated_camera.cpp @@ -0,0 +1,157 @@ +/*************************************************************************/ +/* interpolated_camera.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 "interpolated_camera.h" + + +void InterpolatedCamera::_notification(int p_what) { + + switch(p_what) { + case NOTIFICATION_ENTER_SCENE: { + + if (get_scene()->is_editor_hint() && enabled) + set_fixed_process(false); + + } break; + case NOTIFICATION_PROCESS: { + + if (!enabled) + break; + if (has_node(target)) { + + Spatial *node = get_node(target)->cast_to(); + if (!node) + break; + + float delta = speed*get_process_delta_time(); + Transform target_xform = node->get_global_transform(); + Transform local_transform = get_transform(); + local_transform = local_transform.interpolate_with(target_xform,delta); + set_global_transform(local_transform); + + if (node->cast_to()) { + Camera *cam = node->cast_to(); + if (cam->get_projection()==get_projection()) { + + float new_near = Math::lerp(get_znear(),cam->get_znear(),delta); + float new_far = Math::lerp(get_zfar(),cam->get_zfar(),delta); + + if (cam->get_projection()==PROJECTION_ORTHOGONAL) { + + float size = Math::lerp(get_size(),cam->get_size(),delta); + set_orthogonal(size,new_near,new_far); + } else { + + float fov = Math::lerp(get_fov(),cam->get_fov(),delta); + set_perspective(fov,new_near,new_far); + } + } + } + + + } + + } break; + } +} + +void InterpolatedCamera::_set_target(const Object *p_target) { + + ERR_FAIL_NULL(p_target); + set_target(p_target->cast_to()); +} + +void InterpolatedCamera::set_target(const Spatial *p_target) { + + ERR_FAIL_NULL(p_target); + target=get_path_to(p_target); +} + + +void InterpolatedCamera::set_target_path(const NodePath& p_path){ + + target=p_path; +} + +NodePath InterpolatedCamera::get_target_path() const{ + + return target; +} + +void InterpolatedCamera::set_interpolation_enabled(bool p_enable) { + + if (enabled==p_enable) + return; + enabled=p_enable; + if (p_enable) { + if (is_inside_scene() && get_scene()->is_editor_hint()) + return; + set_process(true); + } else + set_process(false); +} + +bool InterpolatedCamera::is_interpolation_enabled() const { + + return enabled; +} + +void InterpolatedCamera::set_speed(real_t p_speed) { + + speed=p_speed; +} + +real_t InterpolatedCamera::get_speed() const { + + return speed; +} + + +void InterpolatedCamera::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("set_target_path","target_path"),&InterpolatedCamera::set_target_path); + ObjectTypeDB::bind_method(_MD("get_target_path"),&InterpolatedCamera::get_target_path); + ObjectTypeDB::bind_method(_MD("set_target","target"),&InterpolatedCamera::_set_target); + + ObjectTypeDB::bind_method(_MD("set_speed","speed"),&InterpolatedCamera::set_speed); + ObjectTypeDB::bind_method(_MD("get_speed"),&InterpolatedCamera::get_speed); + + ObjectTypeDB::bind_method(_MD("set_interpolation_enabled","target_path"),&InterpolatedCamera::set_interpolation_enabled); + ObjectTypeDB::bind_method(_MD("is_interpolation_enabled"),&InterpolatedCamera::is_interpolation_enabled); + + ADD_PROPERTY( PropertyInfo(Variant::NODE_PATH,"target"), _SCS("set_target_path"), _SCS("get_target_path") ); + ADD_PROPERTY( PropertyInfo(Variant::REAL,"speed"), _SCS("set_speed"), _SCS("get_speed") ); + ADD_PROPERTY( PropertyInfo(Variant::BOOL,"enabled"), _SCS("set_interpolation_enabled"), _SCS("is_interpolation_enabled") ); +} + +InterpolatedCamera::InterpolatedCamera() { + + enabled=false; + speed=1; + +} diff --git a/scene/3d/interpolated_camera.h b/scene/3d/interpolated_camera.h new file mode 100644 index 00000000000..da0e3d562b3 --- /dev/null +++ b/scene/3d/interpolated_camera.h @@ -0,0 +1,63 @@ +/*************************************************************************/ +/* interpolated_camera.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 INTERPOLATED_CAMERA_H +#define INTERPOLATED_CAMERA_H + +#include "scene/3d/camera.h" + +class InterpolatedCamera : public Camera { + + OBJ_TYPE(InterpolatedCamera,Camera); + + bool enabled; + real_t speed; + NodePath target; +protected: + + void _notification(int p_what); + static void _bind_methods(); + void _set_target(const Object *p_target); + +public: + + void set_target(const Spatial *p_target); + void set_target_path(const NodePath& p_path); + NodePath get_target_path() const; + + void set_speed(real_t p_speed); + real_t get_speed() const; + + void set_interpolation_enabled(bool p_enable); + bool is_interpolation_enabled() const; + + + InterpolatedCamera(); +}; + +#endif // INTERPOLATED_CAMERA_H diff --git a/scene/3d/light.cpp b/scene/3d/light.cpp new file mode 100644 index 00000000000..94c56850ef9 --- /dev/null +++ b/scene/3d/light.cpp @@ -0,0 +1,585 @@ +/*************************************************************************/ +/* light.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 "light.h" + +#include "globals.h" +#include "scene/resources/surface_tool.h" + + +static const char* _light_param_names[VS::LIGHT_PARAM_MAX]={ + "params/spot_attenuation", + "params/spot_angle", + "params/radius", + "params/energy", + "params/attenuation", + "shadow/darkening", + "shadow/z_offset", + "shadow/z_slope_scale" +}; + +void Light::set_parameter(Parameter p_param, float p_value) { + + ERR_FAIL_INDEX(p_param, PARAM_MAX); + vars[p_param]=p_value; + VisualServer::get_singleton()->light_set_param(light,(VisualServer::LightParam)p_param,p_value); + if (p_param==PARAM_RADIUS || p_param==PARAM_SPOT_ANGLE) + update_gizmo(); + _change_notify(_light_param_names[p_param]); +// _change_notify(_param_names[p_param]); +} + +float Light::get_parameter(Parameter p_param) const { + + ERR_FAIL_INDEX_V(p_param, PARAM_MAX, 0); + return vars[p_param]; + +} + +void Light::set_color(LightColor p_color, const Color& p_value) { + + ERR_FAIL_INDEX(p_color, 3); + colors[p_color]=p_value; + VisualServer::get_singleton()->light_set_color(light,(VisualServer::LightColor)p_color,p_value); + //_change_notify(_color_names[p_color]); + +} +Color Light::get_color(LightColor p_color) const { + + ERR_FAIL_INDEX_V(p_color, 3, Color()); + return colors[p_color]; + +} + + +void Light::set_project_shadows(bool p_enabled) { + + shadows=p_enabled; + VisualServer::get_singleton()->light_set_shadow(light, p_enabled); + _change_notify("shadow"); +} +bool Light::has_project_shadows() const { + + return shadows; +} + +void Light::set_projector(const Ref& p_projector) { + + projector=p_projector; + VisualServer::get_singleton()->light_set_projector(light, projector.is_null()?RID():projector->get_rid()); +} + +Ref Light::get_projector() const { + + return projector; +} + + +bool Light::_can_gizmo_scale() const { + + return false; +} + + +static void _make_sphere(int p_lats, int p_lons, float p_radius, Ref p_tool) { + + + p_tool->begin(Mesh::PRIMITIVE_TRIANGLES); + + for(int i = 1; i <= p_lats; i++) { + double lat0 = Math_PI * (-0.5 + (double) (i - 1) / p_lats); + double z0 = Math::sin(lat0); + double zr0 = Math::cos(lat0); + + double lat1 = Math_PI * (-0.5 + (double) i / p_lats); + double z1 = Math::sin(lat1); + double zr1 = Math::cos(lat1); + + for(int j = p_lons; j >= 1; j--) { + + double lng0 = 2 * Math_PI * (double) (j - 1) / p_lons; + double x0 = Math::cos(lng0); + double y0 = Math::sin(lng0); + + double lng1 = 2 * Math_PI * (double) (j) / p_lons; + double x1 = Math::cos(lng1); + double y1 = Math::sin(lng1); + + + Vector3 v[4]={ + Vector3(x1 * zr0, z0, y1 *zr0), + Vector3(x1 * zr1, z1, y1 *zr1), + Vector3(x0 * zr1, z1, y0 *zr1), + Vector3(x0 * zr0, z0, y0 *zr0) + }; + +#define ADD_POINT(m_idx) \ + p_tool->add_normal(v[m_idx]);\ + p_tool->add_vertex(v[m_idx]*p_radius); + + ADD_POINT(0); + ADD_POINT(1); + ADD_POINT(2); + + ADD_POINT(2); + ADD_POINT(3); + ADD_POINT(0); + } + } + +} + +RES Light::_get_gizmo_geometry() const { + + + Ref mat_area( memnew( FixedMaterial )); + + mat_area->set_parameter( FixedMaterial::PARAM_DIFFUSE,Color(0.7,0.6,0.0,0.05) ); + mat_area->set_parameter( FixedMaterial::PARAM_EMISSION,Color(0.7,0.7,0.7) ); + mat_area->set_blend_mode( Material::BLEND_MODE_ADD ); + mat_area->set_flag(Material::FLAG_DOUBLE_SIDED,true); + mat_area->set_hint(Material::HINT_NO_DEPTH_DRAW,true); + + Ref mat_light( memnew( FixedMaterial )); + + mat_light->set_parameter( FixedMaterial::PARAM_DIFFUSE, Color(1.0,1.0,0.8,0.9) ); + mat_light->set_flag(Material::FLAG_UNSHADED,true); + + Ref< Mesh > mesh; + + Ref surftool( memnew( SurfaceTool )); + + switch(type) { + + case VisualServer::LIGHT_DIRECTIONAL: { + + + mat_area->set_parameter( FixedMaterial::PARAM_DIFFUSE,Color(0.9,0.8,0.1,0.8) ); + mat_area->set_blend_mode( Material::BLEND_MODE_MIX); + mat_area->set_flag(Material::FLAG_DOUBLE_SIDED,false); + mat_area->set_flag(Material::FLAG_UNSHADED,true); + + _make_sphere( 5,5,0.6, surftool ); + surftool->set_material(mat_light); + mesh=surftool->commit(mesh); + + // float radius=1; + + surftool->begin(Mesh::PRIMITIVE_TRIANGLES); + + const int arrow_points=5; + Vector3 arrow[arrow_points]={ + Vector3(0,0,2), + Vector3(1,1,2), + Vector3(1,1,-1), + Vector3(2,2,-1), + Vector3(0,0,-3) + }; + + int arrow_sides=4; + + + for(int i = 0; i < arrow_sides ; i++) { + + + Matrix3 ma(Vector3(0,0,1),Math_PI*2*float(i)/arrow_sides); + Matrix3 mb(Vector3(0,0,1),Math_PI*2*float(i+1)/arrow_sides); + + + for(int j=0;jadd_normal(n); + surftool->add_vertex(points[0]); + surftool->add_normal(n); + surftool->add_vertex(points[1]); + surftool->add_normal(n); + surftool->add_vertex(points[2]); + + surftool->add_normal(n); + surftool->add_vertex(points[0]); + surftool->add_normal(n); + surftool->add_vertex(points[2]); + surftool->add_normal(n); + surftool->add_vertex(points[3]); + + + } + + + } + + surftool->set_material(mat_area); + mesh=surftool->commit(mesh); + + + + } break; + case VisualServer::LIGHT_OMNI: { + + + _make_sphere( 20,20,vars[PARAM_RADIUS], surftool ); + surftool->set_material(mat_area); + mesh=surftool->commit(mesh); + _make_sphere(5,5, 0.1, surftool ); + surftool->set_material(mat_light); + mesh=surftool->commit(mesh); + } break; + + case VisualServer::LIGHT_SPOT: { + + _make_sphere( 5,5,0.1, surftool ); + surftool->set_material(mat_light); + mesh=surftool->commit(mesh); + + // make cone + int points=24; + float len=vars[PARAM_RADIUS]; + float size=Math::tan(Math::deg2rad(vars[PARAM_SPOT_ANGLE]))*len; + + surftool->begin(Mesh::PRIMITIVE_TRIANGLES); + + for(int i = 0; i < points; i++) { + + float x0=Math::sin(i * Math_PI * 2 / points); + float y0=Math::cos(i * Math_PI * 2 / points); + float x1=Math::sin((i+1) * Math_PI * 2 / points); + float y1=Math::cos((i+1) * Math_PI * 2 / points); + + Vector3 v1=Vector3(x0*size,y0*size,-len).normalized()*len; + Vector3 v2=Vector3(x1*size,y1*size,-len).normalized()*len; + + Vector3 v3=Vector3(0,0,0); + Vector3 v4=Vector3(0,0,v1.z); + + Vector3 n = Plane(v1,v2,v3).normal; + + + surftool->add_normal(n); + surftool->add_vertex(v1); + surftool->add_normal(n); + surftool->add_vertex(v2); + surftool->add_normal(n); + surftool->add_vertex(v3); + + n=Vector3(0,0,-1); + + surftool->add_normal(n); + surftool->add_vertex(v1); + surftool->add_normal(n); + surftool->add_vertex(v2); + surftool->add_normal(n); + surftool->add_vertex(v4); + + + } + + surftool->set_material(mat_area); + mesh=surftool->commit(mesh); + + + } break; + } + + return mesh; +} + + +AABB Light::get_aabb() const { + + if (type==VisualServer::LIGHT_DIRECTIONAL) { + + return AABB( Vector3(-1,-1,-1), Vector3(2, 2, 2 ) ); + + } else if (type==VisualServer::LIGHT_OMNI) { + + return AABB( Vector3(-1,-1,-1) * vars[PARAM_RADIUS], Vector3(2, 2, 2 ) * vars[PARAM_RADIUS]); + + } else if (type==VisualServer::LIGHT_SPOT) { + + float len=vars[PARAM_RADIUS]; + float size=Math::tan(Math::deg2rad(vars[PARAM_SPOT_ANGLE]))*len; + return AABB( Vector3( -size,-size,-len ), Vector3( size*2, size*2, len ) ); + } + + return AABB(); +} + +DVector Light::get_faces(uint32_t p_usage_flags) const { + + return DVector(); +} + + +void Light::set_operator(Operator p_op) { + ERR_FAIL_INDEX(p_op,2); + op=p_op; + VisualServer::get_singleton()->light_set_operator(light,VS::LightOp(op)); + +} + +Light::Operator Light::get_operator() const { + + return op; +} + +void Light::approximate_opengl_attenuation(float p_constant, float p_linear, float p_quadratic,float p_radius_treshold) { + + //this is horrible and must never be used + + float a = p_quadratic * p_radius_treshold; + float b = p_linear * p_radius_treshold; + float c = p_constant * p_radius_treshold -1; + + float radius=10000; + + if(a == 0) { // solve linear + float d = Math::abs(-c/b); + if(d=0) { + + root = sqrt(root); + + float solution1 = fabs( (-b + root) / denominator); + float solution2 = fabs( (-b - root) / denominator); + + if(solution1 > radius) + solution1 = radius; + + if(solution2 > radius) + solution2 = radius; + + radius = (solution1 > solution2 ? solution1 : solution2); + } + } + } + + float energy=1.0; + + if (p_constant>0) + energy=1.0/p_constant; //energy is this + else + energy=8.0; // some high number.. + + + if (radius==10000) + radius=100; //bug? + + set_parameter(PARAM_RADIUS,radius); + set_parameter(PARAM_ENERGY,energy); + +} + +void Light::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("set_parameter","variable","value"), &Light::set_parameter ); + ObjectTypeDB::bind_method(_MD("get_parameter"), &Light::get_parameter ); + ObjectTypeDB::bind_method(_MD("set_color","color","value"), &Light::set_color ); + ObjectTypeDB::bind_method(_MD("get_color"), &Light::get_color ); + ObjectTypeDB::bind_method(_MD("set_project_shadows","enable"), &Light::set_project_shadows ); + ObjectTypeDB::bind_method(_MD("has_project_shadows"), &Light::has_project_shadows ); + ObjectTypeDB::bind_method(_MD("set_projector","projector:Texture"), &Light::set_projector ); + ObjectTypeDB::bind_method(_MD("get_projector:Texture"), &Light::get_projector ); + ObjectTypeDB::bind_method(_MD("set_operator","operator"), &Light::set_operator ); + ObjectTypeDB::bind_method(_MD("get_operator"), &Light::get_operator ); + + + ADD_PROPERTYI( PropertyInfo( Variant::REAL, "params/energy", PROPERTY_HINT_EXP_RANGE, "0,64,0.01"), _SCS("set_parameter"), _SCS("get_parameter"), PARAM_ENERGY ); + /* + if (type == VisualServer::LIGHT_OMNI || type == VisualServer::LIGHT_SPOT) { + ADD_PROPERTY( PropertyInfo( Variant::REAL, "params/radius", PROPERTY_HINT_RANGE, "0.01,4096,0.01")); + ADD_PROPERTY( PropertyInfo( Variant::REAL, "params/attenuation", PROPERTY_HINT_RANGE, "0,8,0.01")); + } + + if (type == VisualServer::LIGHT_SPOT) { + ADD_PROPERTY( PropertyInfo( Variant::REAL, "params/spot_angle", PROPERTY_HINT_RANGE, "0.01,90.0,0.01")); + ADD_PROPERTY( PropertyInfo( Variant::REAL, "params/spot_attenuation", PROPERTY_HINT_RANGE, "0,8,0.01")); + + }*/ + + ADD_PROPERTYI( PropertyInfo( Variant::COLOR, "colors/ambient"), _SCS("set_color"), _SCS("get_color"),COLOR_AMBIENT); + ADD_PROPERTYI( PropertyInfo( Variant::COLOR, "colors/diffuse"), _SCS("set_color"), _SCS("get_color"),COLOR_DIFFUSE); + ADD_PROPERTYI( PropertyInfo( Variant::COLOR, "colors/specular"), _SCS("set_color"), _SCS("get_color"),COLOR_SPECULAR); + ADD_PROPERTY( PropertyInfo( Variant::BOOL, "shadow/shadow"), _SCS("set_project_shadows"), _SCS("has_project_shadows")); + ADD_PROPERTYI( PropertyInfo( Variant::REAL, "shadow/darkening", PROPERTY_HINT_RANGE, "0,64,0.01"), _SCS("set_parameter"), _SCS("get_parameter"), PARAM_SHADOW_DARKENING ); + ADD_PROPERTYI( PropertyInfo( Variant::REAL, "shadow/z_offset", PROPERTY_HINT_RANGE, "0,128,0.001"), _SCS("set_parameter"), _SCS("get_parameter"), PARAM_SHADOW_Z_OFFSET); + ADD_PROPERTYI( PropertyInfo( Variant::REAL, "shadow/z_slope_scale", PROPERTY_HINT_RANGE, "0,128,0.001"), _SCS("set_parameter"), _SCS("get_parameter"), PARAM_SHADOW_Z_SLOPE_SCALE); + ADD_PROPERTY( PropertyInfo( Variant::OBJECT, "projector",PROPERTY_HINT_RESOURCE_TYPE,"Texture"), _SCS("set_projector"), _SCS("get_projector")); + ADD_PROPERTY( PropertyInfo( Variant::INT, "operator",PROPERTY_HINT_ENUM,"Add,Sub"), _SCS("set_operator"), _SCS("get_operator")); + + + BIND_CONSTANT( PARAM_RADIUS ); + BIND_CONSTANT( PARAM_ENERGY ); + BIND_CONSTANT( PARAM_ATTENUATION ); + BIND_CONSTANT( PARAM_SPOT_ANGLE ); + BIND_CONSTANT( PARAM_SPOT_ATTENUATION ); + BIND_CONSTANT( PARAM_SHADOW_DARKENING ); + BIND_CONSTANT( PARAM_SHADOW_Z_OFFSET ); + + BIND_CONSTANT( COLOR_AMBIENT ); + BIND_CONSTANT( COLOR_DIFFUSE ); + BIND_CONSTANT( COLOR_SPECULAR ); + +} + + +Light::Light(VisualServer::LightType p_type) { + + type=p_type; + light=VisualServer::get_singleton()->light_create(p_type); + + set_parameter(PARAM_SPOT_ATTENUATION,1.0); + set_parameter(PARAM_SPOT_ANGLE,30.0); + set_parameter(PARAM_RADIUS,2.0); + set_parameter(PARAM_ENERGY,1.0); + set_parameter(PARAM_ATTENUATION,1.0); + set_parameter(PARAM_SHADOW_DARKENING,0.0); + set_parameter(PARAM_SHADOW_Z_OFFSET,0.05); + set_parameter(PARAM_SHADOW_Z_SLOPE_SCALE,0); + + set_color( COLOR_AMBIENT, Color(0,0,0)); + set_color( COLOR_DIFFUSE, Color(1,1,1)); + set_color( COLOR_SPECULAR, Color(1,1,1)); + + op=OPERATOR_ADD; + set_project_shadows( false ); + set_base(light); + +} + + +Light::Light() { + + type=VisualServer::LIGHT_DIRECTIONAL; + ERR_PRINT("Light shouldn't be instanced dircetly, use the subtypes."); +} + + +Light::~Light() { + + if (light.is_valid()) + VisualServer::get_singleton()->free(light); +} +///////////////////////////////////////// + + +void DirectionalLight::set_shadow_mode(ShadowMode p_mode) { + + shadow_mode=p_mode; + VS::get_singleton()->light_directional_set_shadow_mode(light,(VS::LightDirectionalShadowMode)p_mode); + +} + +DirectionalLight::ShadowMode DirectionalLight::get_shadow_mode() const{ + + return shadow_mode; +} + +void DirectionalLight::set_shadow_param(ShadowParam p_param, float p_value) { + + ERR_FAIL_INDEX(p_param,3); + shadow_param[p_param]=p_value; + VS::get_singleton()->light_directional_set_shadow_param(light,VS::LightDirectionalShadowParam(p_param),p_value); +} + +float DirectionalLight::get_shadow_param(ShadowParam p_param) const { + ERR_FAIL_INDEX_V(p_param,3,0); + return shadow_param[p_param]; +} + +void DirectionalLight::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("set_shadow_mode","mode"),&DirectionalLight::set_shadow_mode); + ObjectTypeDB::bind_method(_MD("get_shadow_mode"),&DirectionalLight::get_shadow_mode); + ObjectTypeDB::bind_method(_MD("set_shadow_param","param","value"),&DirectionalLight::set_shadow_param); + ObjectTypeDB::bind_method(_MD("get_shadow_param","param"),&DirectionalLight::get_shadow_param); + + ADD_PROPERTY( PropertyInfo(Variant::INT,"shadow/mode",PROPERTY_HINT_ENUM,"Orthogonal,Perspective,PSSM"),_SCS("set_shadow_mode"),_SCS("get_shadow_mode")); + ADD_PROPERTYI( PropertyInfo(Variant::REAL,"shadow/max_distance",PROPERTY_HINT_EXP_RANGE,"0.00,99999,0.01"),_SCS("set_shadow_param"),_SCS("get_shadow_param"), SHADOW_PARAM_MAX_DISTANCE); + ADD_PROPERTYI( PropertyInfo(Variant::REAL,"shadow/split_weight",PROPERTY_HINT_RANGE,"0.01,1.0,0.01"),_SCS("set_shadow_param"),_SCS("get_shadow_param"), SHADOW_PARAM_PSSM_SPLIT_WEIGHT); + ADD_PROPERTYI( PropertyInfo(Variant::REAL,"shadow/zoffset_scale",PROPERTY_HINT_RANGE,"0.01,1024.0,0.01"),_SCS("set_shadow_param"),_SCS("get_shadow_param"), SHADOW_PARAM_PSSM_ZOFFSET_SCALE); + + BIND_CONSTANT( SHADOW_ORTHOGONAL ); + BIND_CONSTANT( SHADOW_PERSPECTIVE ); + BIND_CONSTANT( SHADOW_PARALLEL_SPLIT ); + BIND_CONSTANT( SHADOW_PARAM_MAX_DISTANCE ); + BIND_CONSTANT( SHADOW_PARAM_PSSM_SPLIT_WEIGHT ); + BIND_CONSTANT( SHADOW_PARAM_PSSM_ZOFFSET_SCALE ); + +} + + +DirectionalLight::DirectionalLight() : Light( VisualServer::LIGHT_DIRECTIONAL ) { + + shadow_mode=SHADOW_ORTHOGONAL; + shadow_param[SHADOW_PARAM_MAX_DISTANCE]=0; + shadow_param[SHADOW_PARAM_PSSM_SPLIT_WEIGHT]=0.5; + shadow_param[SHADOW_PARAM_PSSM_ZOFFSET_SCALE]=2.0; + +} + + +void OmniLight::_bind_methods() { + + ADD_PROPERTYI( PropertyInfo( Variant::REAL, "params/radius", PROPERTY_HINT_EXP_RANGE, "0.2,4096,0.01"), _SCS("set_parameter"), _SCS("get_parameter"), PARAM_RADIUS ); + ADD_PROPERTYI( PropertyInfo( Variant::REAL, "params/attenuation", PROPERTY_HINT_EXP_EASING, "attenuation"), _SCS("set_parameter"), _SCS("get_parameter"), PARAM_ATTENUATION ); + +} + +void SpotLight::_bind_methods() { + + ADD_PROPERTYI( PropertyInfo( Variant::REAL, "params/radius", PROPERTY_HINT_EXP_RANGE, "0.2,4096,0.01"), _SCS("set_parameter"), _SCS("get_parameter"), PARAM_RADIUS ); + ADD_PROPERTYI( PropertyInfo( Variant::REAL, "params/attenuation", PROPERTY_HINT_EXP_EASING, "attenuation"), _SCS("set_parameter"), _SCS("get_parameter"), PARAM_ATTENUATION ); + + ADD_PROPERTYI( PropertyInfo( Variant::REAL, "params/spot_angle", PROPERTY_HINT_RANGE, "0.01,89.9,0.01"), _SCS("set_parameter"), _SCS("get_parameter"), PARAM_SPOT_ANGLE ); + ADD_PROPERTYI( PropertyInfo( Variant::REAL, "params/spot_attenuation", PROPERTY_HINT_EXP_EASING, "attenuation"), _SCS("set_parameter"), _SCS("get_parameter"), PARAM_ATTENUATION ); + +} + + diff --git a/scene/3d/light.h b/scene/3d/light.h new file mode 100644 index 00000000000..03bf3363037 --- /dev/null +++ b/scene/3d/light.h @@ -0,0 +1,195 @@ +/*************************************************************************/ +/* light.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 LIGHT_H +#define LIGHT_H + + +#include "scene/3d/visual_instance.h" +#include "scene/resources/texture.h" +#include "servers/visual_server.h" + +/** + @author Juan Linietsky +*/ +class Light : public VisualInstance { + + OBJ_TYPE( Light, VisualInstance ); + OBJ_CATEGORY("3D Light Nodes"); + +public: + + enum Parameter { + PARAM_RADIUS=VisualServer::LIGHT_PARAM_RADIUS, + PARAM_ENERGY=VisualServer::LIGHT_PARAM_ENERGY, + PARAM_ATTENUATION=VisualServer::LIGHT_PARAM_ATTENUATION, + PARAM_SPOT_ANGLE=VisualServer::LIGHT_PARAM_SPOT_ANGLE, + PARAM_SPOT_ATTENUATION=VisualServer::LIGHT_PARAM_ATTENUATION, + PARAM_SHADOW_DARKENING=VisualServer::LIGHT_PARAM_SHADOW_DARKENING, + PARAM_SHADOW_Z_OFFSET=VisualServer::LIGHT_PARAM_SHADOW_Z_OFFSET, + PARAM_SHADOW_Z_SLOPE_SCALE=VisualServer::LIGHT_PARAM_SHADOW_Z_SLOPE_SCALE, + PARAM_MAX=VisualServer::LIGHT_PARAM_MAX + }; + + + enum LightColor { + + COLOR_AMBIENT=VisualServer::LIGHT_COLOR_AMBIENT, + COLOR_DIFFUSE=VisualServer::LIGHT_COLOR_DIFFUSE, + COLOR_SPECULAR=VisualServer::LIGHT_COLOR_SPECULAR + }; + + + enum Operator { + + OPERATOR_ADD, + OPERATOR_SUB + }; +private: + + + Ref projector; + float vars[PARAM_MAX]; + Color colors[3]; + + + VisualServer::LightType type; + bool shadows; + Operator op; + +// bind helpers + +protected: + + RID light; + + virtual bool _can_gizmo_scale() const; + virtual RES _get_gizmo_geometry() const; + + static void _bind_methods(); + + + Light(VisualServer::LightType p_type); +public: + + VS::LightType get_light_type() const { return type; } + + void set_parameter(Parameter p_var, float p_value); + float get_parameter(Parameter p_var) const; + + void set_color(LightColor p_color,const Color& p_value); + Color get_color(LightColor p_color) const; + + void set_project_shadows(bool p_enabled); + bool has_project_shadows() const; + + void set_projector(const Ref& p_projector); + Ref get_projector() const; + + void set_operator(Operator p_op); + Operator get_operator() const; + + virtual AABB get_aabb() const; + virtual DVector get_faces(uint32_t p_usage_flags) const; + + void approximate_opengl_attenuation(float p_constant, float p_linear, float p_quadratic, float p_radius_treshold=0.5); + + Light(); + ~Light(); + +}; + +VARIANT_ENUM_CAST( Light::Parameter ); +VARIANT_ENUM_CAST( Light::LightColor ); +VARIANT_ENUM_CAST( Light::Operator ); + + +class DirectionalLight : public Light { + + OBJ_TYPE( DirectionalLight, Light ); + +public: + + enum ShadowMode { + SHADOW_ORTHOGONAL, + SHADOW_PERSPECTIVE, + SHADOW_PARALLEL_SPLIT + }; + enum ShadowParam { + SHADOW_PARAM_MAX_DISTANCE, + SHADOW_PARAM_PSSM_SPLIT_WEIGHT, + SHADOW_PARAM_PSSM_ZOFFSET_SCALE + }; + +private: + ShadowMode shadow_mode; + float shadow_param[3]; +protected: + static void _bind_methods(); +public: + + void set_shadow_mode(ShadowMode p_mode); + ShadowMode get_shadow_mode() const; + + void set_shadow_max_distance(float p_distance); + float get_shadow_max_distance() const; + void set_shadow_param(ShadowParam p_param, float p_value); + float get_shadow_param(ShadowParam p_param) const; + + DirectionalLight(); +}; + +VARIANT_ENUM_CAST( DirectionalLight::ShadowMode ); +VARIANT_ENUM_CAST( DirectionalLight::ShadowParam ); + + +class OmniLight : public Light { + + OBJ_TYPE( OmniLight, Light ); +protected: + static void _bind_methods(); + +public: + + + OmniLight() : Light( VisualServer::LIGHT_OMNI ) {} +}; + +class SpotLight : public Light { + + OBJ_TYPE( SpotLight, Light ); +protected: + static void _bind_methods(); +public: + + + SpotLight() : Light( VisualServer::LIGHT_SPOT ) {} +}; + + +#endif diff --git a/scene/3d/mesh_instance.cpp b/scene/3d/mesh_instance.cpp new file mode 100644 index 00000000000..6387f5fdbce --- /dev/null +++ b/scene/3d/mesh_instance.cpp @@ -0,0 +1,218 @@ +/*************************************************************************/ +/* mesh_instance.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 "mesh_instance.h" + +#include "skeleton.h" +#include "physics_body.h" + + + +bool MeshInstance::_set(const StringName& p_name, const Variant& p_value) { + + //this is not _too_ bad performance wise, really. it only arrives here if the property was not set anywhere else. + //add to it that it's probably found on first call to _set anyway. + + if (!get_instance().is_valid()) + return false; + + + Map::Element *E = morph_tracks.find(p_name); + if (!E) + return false; + + E->get().value=p_value; + VisualServer::get_singleton()->instance_set_morph_target_weight(get_instance(),E->get().idx,E->get().value); + + return true; +} + +bool MeshInstance::_get(const StringName& p_name,Variant &r_ret) const { + + + if (!get_instance().is_valid()) + return false; + + const Map::Element *E = morph_tracks.find(p_name); + if (!E) + return false; + + r_ret = E->get().value; + + return true; +} + +void MeshInstance::_get_property_list( List *p_list) const { + + List ls; + for(const Map::Element *E=morph_tracks.front();E;E=E->next()) { + + ls.push_back(E->key()); + } + + ls.sort();; + + for(List::Element *E=ls.front();E;E=E->next()) { + p_list->push_back( PropertyInfo(Variant::REAL,E->get(),PROPERTY_HINT_RANGE,"0,1,0.01")); + } +} + + + + +void MeshInstance::set_mesh(const Ref& p_mesh) { + + mesh=p_mesh; + + morph_tracks.clear(); + if (mesh.is_valid()) { + + + for(int i=0;iget_morph_target_count();i++) { + + MorphTrack mt; + mt.idx=i; + mt.value=0; + morph_tracks["morph/"+String(mesh->get_morph_target_name(i))]=mt; + } + set_base(mesh->get_rid()); + } else { + + set_base(RID()); + } + + _change_notify("mesh"); +} +Ref MeshInstance::get_mesh() const { + + return mesh; +} + + +AABB MeshInstance::get_aabb() const { + + if (!mesh.is_null()) + return mesh->get_aabb(); + + return AABB(); +} + +DVector MeshInstance::get_faces(uint32_t p_usage_flags) const { + + if (!(p_usage_flags&(FACES_SOLID|FACES_ENCLOSING))) + return DVector(); + + if (mesh.is_null()) + return DVector(); + + return mesh->get_faces(); +} + + +Node* MeshInstance::create_trimesh_collision_node() { + + if (mesh.is_null()) + return NULL; + + Ref shape = mesh->create_trimesh_shape(); + if (shape.is_null()) + return NULL; + + StaticBody * static_body = memnew( StaticBody ); + static_body->add_shape( shape ); + return static_body; + + return NULL; +} + +void MeshInstance::create_trimesh_collision() { + + + StaticBody* static_body = create_trimesh_collision_node()->cast_to(); + ERR_FAIL_COND(!static_body); + static_body->set_name( String(get_name()) + "_col" ); + + add_child(static_body); + if (get_owner()) + static_body->set_owner( get_owner() ); + +} + +Node* MeshInstance::create_convex_collision_node() { + + if (mesh.is_null()) + return NULL; + + Ref shape = mesh->create_convex_shape(); + if (shape.is_null()) + return NULL; + + StaticBody * static_body = memnew( StaticBody ); + static_body->add_shape( shape ); + return static_body; + + return NULL; +} + +void MeshInstance::create_convex_collision() { + + + StaticBody* static_body = create_convex_collision_node()->cast_to(); + ERR_FAIL_COND(!static_body); + static_body->set_name( String(get_name()) + "_col" ); + + add_child(static_body); + if (get_owner()) + static_body->set_owner( get_owner() ); + +} + +void MeshInstance::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("set_mesh","mesh:Mesh"),&MeshInstance::set_mesh); + ObjectTypeDB::bind_method(_MD("get_mesh:Mesh"),&MeshInstance::get_mesh); + ObjectTypeDB::bind_method(_MD("get_aabb"),&MeshInstance::get_aabb); + ObjectTypeDB::bind_method(_MD("create_trimesh_collision"),&MeshInstance::create_trimesh_collision); + ObjectTypeDB::set_method_flags("MeshInstance","create_trimesh_collision",METHOD_FLAGS_DEFAULT|METHOD_FLAG_EDITOR); + ObjectTypeDB::bind_method(_MD("create_convex_collision"),&MeshInstance::create_convex_collision); + ObjectTypeDB::set_method_flags("MeshInstance","create_convex_collision",METHOD_FLAGS_DEFAULT|METHOD_FLAG_EDITOR); + ADD_PROPERTY( PropertyInfo( Variant::OBJECT, "mesh/mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh" ), _SCS("set_mesh"), _SCS("get_mesh")); + + +} + +MeshInstance::MeshInstance() +{ +} + + +MeshInstance::~MeshInstance() { + +} + + diff --git a/scene/3d/mesh_instance.h b/scene/3d/mesh_instance.h new file mode 100644 index 00000000000..0e071095027 --- /dev/null +++ b/scene/3d/mesh_instance.h @@ -0,0 +1,80 @@ +/*************************************************************************/ +/* mesh_instance.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 MESH_INSTANCE_H +#define MESH_INSTANCE_H + +#include "scene/3d/visual_instance.h" +#include "scene/resources/mesh.h" + +/** + @author Juan Linietsky +*/ +class MeshInstance : public GeometryInstance { + + OBJ_TYPE( MeshInstance, GeometryInstance ); + + Ref mesh; + + struct MorphTrack { + + int idx; + float value; + MorphTrack() { idx=0; value=0; } + }; + + Map morph_tracks; + + +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 *p_list) const; + + + static void _bind_methods(); +public: + + void set_mesh(const Ref& p_mesh); + Ref get_mesh() const; + + Node* create_trimesh_collision_node(); + void create_trimesh_collision(); + + Node* create_convex_collision_node(); + void create_convex_collision(); + + virtual AABB get_aabb() const; + virtual DVector get_faces(uint32_t p_usage_flags) const; + + MeshInstance(); + ~MeshInstance(); +}; + +#endif diff --git a/scene/3d/multimesh_instance.cpp b/scene/3d/multimesh_instance.cpp new file mode 100644 index 00000000000..b4e58aca40f --- /dev/null +++ b/scene/3d/multimesh_instance.cpp @@ -0,0 +1,81 @@ +/*************************************************************************/ +/* multimesh_instance.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 "multimesh_instance.h" + + + + + +void MultiMeshInstance::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("set_multimesh","multimesh"),&MultiMeshInstance::set_multimesh); + ObjectTypeDB::bind_method(_MD("get_multimesh"),&MultiMeshInstance::get_multimesh); + ADD_PROPERTY( PropertyInfo(Variant::OBJECT,"multimesh",PROPERTY_HINT_RESOURCE_TYPE,"MultiMesh"), _SCS("set_multimesh"), _SCS("get_multimesh")); + + +} + +void MultiMeshInstance::set_multimesh(const Ref& p_multimesh) { + + multimesh=p_multimesh; + if (multimesh.is_valid()) + set_base(multimesh->get_rid()); + else + set_base(RID()); + +} + +Ref MultiMeshInstance::get_multimesh() const { + + return multimesh; +} + + + +DVector MultiMeshInstance::get_faces(uint32_t p_usage_flags) const { + + return DVector(); +} + +AABB MultiMeshInstance::get_aabb() const { + + if (multimesh.is_null()) + return AABB(); + else + return multimesh->get_aabb(); +} + +MultiMeshInstance::MultiMeshInstance() { + +} + +MultiMeshInstance::~MultiMeshInstance() { + + +} diff --git a/scene/3d/multimesh_instance.h b/scene/3d/multimesh_instance.h new file mode 100644 index 00000000000..fd50140baed --- /dev/null +++ b/scene/3d/multimesh_instance.h @@ -0,0 +1,64 @@ +/*************************************************************************/ +/* multimesh_instance.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 MULTIMESH_INSTANCE_H +#define MULTIMESH_INSTANCE_H + +#include "scene/3d/visual_instance.h" +#include "scene/resources/multimesh.h" + +/** + @author Juan Linietsky +*/ + +class MultiMeshInstance : public GeometryInstance { + OBJ_TYPE( MultiMeshInstance, GeometryInstance ); + + + Ref multimesh; +protected: + + + static void _bind_methods(); + // bind helpers + +public: + + virtual DVector get_faces(uint32_t p_usage_flags) const; + + void set_multimesh(const Ref& p_multimesh); + Ref get_multimesh() const; + + virtual AABB get_aabb() const; + + MultiMeshInstance(); + ~MultiMeshInstance(); +}; + +#endif // MULTIMESH_INSTANCE_H + diff --git a/scene/3d/optimized_spatial_scene.cpp b/scene/3d/optimized_spatial_scene.cpp new file mode 100644 index 00000000000..12c847f71c0 --- /dev/null +++ b/scene/3d/optimized_spatial_scene.cpp @@ -0,0 +1,33 @@ +/*************************************************************************/ +/* optimized_spatial_scene.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 "optimized_spatial_scene.h" + +OptimizedSpatialScene::OptimizedSpatialScene() +{ +} diff --git a/scene/3d/optimized_spatial_scene.h b/scene/3d/optimized_spatial_scene.h new file mode 100644 index 00000000000..36ae1ec2132 --- /dev/null +++ b/scene/3d/optimized_spatial_scene.h @@ -0,0 +1,41 @@ +/*************************************************************************/ +/* optimized_spatial_scene.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 OPTIMIZED_SPATIAL_SCENE_H +#define OPTIMIZED_SPATIAL_SCENE_H + +#include "scene/3d/spatial.h" + +class OptimizedSpatialScene : public Spatial { + + OBJ_TYPE( OptimizedSpatialScene, Spatial ); +public: + OptimizedSpatialScene(); +}; + +#endif // OPTIMIZED_SPATIAL_SCENE_H diff --git a/scene/3d/particles.cpp b/scene/3d/particles.cpp new file mode 100644 index 00000000000..d74768f0ab8 --- /dev/null +++ b/scene/3d/particles.cpp @@ -0,0 +1,559 @@ +/*************************************************************************/ +/* particles.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 "particles.h" +#include "servers/visual_server.h" +#include "scene/resources/surface_tool.h" + +/* +static const char* _var_names[Particles::VAR_MAX]={ + "vars/lifetime", + "vars/spread", + "vars/gravity", + "vars/linear_vel", + "vars/angular_vel", + "vars/linear_accel", + "vars/radial_accel", + "vars/tan_accel", + "vars/initial_size", + "vars/final_size", + "vars/initial_angle", + "vars/height", + "vars/height_speed_scale", +}; +*/ +static const char* _rand_names[Particles::VAR_MAX]={ + "rand/lifetime", + "rand/spread", + "rand/gravity", + "rand/linear_vel", + "rand/angular_vel", + "rand/linear_accel", + "rand/radial_accel", + "rand/tan_accel", + "rand/damping", + "rand/initial_size", + "rand/final_size", + "rand/initial_angle", + "rand/height", + "rand/height_speed_scale", +}; + +static const Particles::Variable _var_indices[Particles::VAR_MAX]={ + Particles::VAR_LIFETIME, + Particles::VAR_SPREAD, + Particles::VAR_GRAVITY, + Particles::VAR_LINEAR_VELOCITY, + Particles::VAR_ANGULAR_VELOCITY, + Particles::VAR_LINEAR_ACCELERATION, + Particles::VAR_DRAG, + Particles::VAR_TANGENTIAL_ACCELERATION, + Particles::VAR_DAMPING, + Particles::VAR_INITIAL_SIZE, + Particles::VAR_FINAL_SIZE, + Particles::VAR_INITIAL_ANGLE, + Particles::VAR_HEIGHT, + Particles::VAR_HEIGHT_SPEED_SCALE, +}; + + + +AABB Particles::get_aabb() const { + + return AABB( Vector3(-1,-1,-1), Vector3(2, 2, 2 ) ); +} +DVector Particles::get_faces(uint32_t p_usage_flags) const { + + return DVector(); +} + + +void Particles::set_amount(int p_amount) { + + ERR_FAIL_INDEX(p_amount,4096); + amount=p_amount; + VisualServer::get_singleton()->particles_set_amount(particles,p_amount); +} +int Particles::get_amount() const { + + return amount; +} + +void Particles::set_emitting(bool p_emitting) { + + emitting=p_emitting; + VisualServer::get_singleton()->particles_set_emitting(particles,p_emitting); + + setup_timer(); +} +bool Particles::is_emitting() const { + + return emitting; +} + +void Particles::set_visibility_aabb(const AABB& p_aabb) { + + visibility_aabb=p_aabb; + VisualServer::get_singleton()->particles_set_visibility_aabb(particles,p_aabb); + update_gizmo(); + +} +AABB Particles::get_visibility_aabb() const { + + return visibility_aabb; +} + + +void Particles::set_emission_points(const DVector& p_points) { + + using_points = p_points.size(); + VisualServer::get_singleton()->particles_set_emission_points(particles,p_points); +} + +DVector Particles::get_emission_points() const { + + if (!using_points) + return DVector(); + + return VisualServer::get_singleton()->particles_get_emission_points(particles); + +} + +void Particles::set_emission_half_extents(const Vector3& p_half_extents) { + + emission_half_extents=p_half_extents; + VisualServer::get_singleton()->particles_set_emission_half_extents(particles,p_half_extents); + +} + +Vector3 Particles::get_emission_half_extents() const { + + return emission_half_extents; +} + +void Particles::set_emission_base_velocity(const Vector3& p_base_velocity) { + + emission_base_velocity=p_base_velocity; + VisualServer::get_singleton()->particles_set_emission_base_velocity(particles,p_base_velocity); + +} + +Vector3 Particles::get_emission_base_velocity() const { + + return emission_base_velocity; +} + +void Particles::set_gravity_normal(const Vector3& p_normal) { + + gravity_normal=p_normal; + VisualServer::get_singleton()->particles_set_gravity_normal(particles,p_normal); +} + +Vector3 Particles::get_gravity_normal() const { + + return gravity_normal; + +} + +void Particles::set_variable(Variable p_variable,float p_value) { + + ERR_FAIL_INDEX(p_variable,VAR_MAX); + var[p_variable]=p_value; + VisualServer::get_singleton()->particles_set_variable(particles,(VS::ParticleVariable)p_variable,p_value); + if (p_variable==VAR_SPREAD) + update_gizmo(); +} + +float Particles::get_variable(Variable p_variable) const { + + ERR_FAIL_INDEX_V(p_variable,VAR_MAX,-1); + return var[p_variable]; + +} + +void Particles::set_randomness(Variable p_variable,float p_randomness) { + + ERR_FAIL_INDEX(p_variable,VAR_MAX); + var_random[p_variable]=p_randomness; + VisualServer::get_singleton()->particles_set_randomness(particles,(VS::ParticleVariable)p_variable,p_randomness); + +} +float Particles::get_randomness(Variable p_variable) const { + + ERR_FAIL_INDEX_V(p_variable,VAR_MAX,-1); + return var_random[p_variable]; + +} + +void Particles::set_color_phase_pos(int p_phase, float p_pos) { + + ERR_FAIL_INDEX(p_phase,VS::MAX_PARTICLE_COLOR_PHASES); + color_phase[p_phase].pos=p_pos; + VisualServer::get_singleton()->particles_set_color_phase_pos(particles,p_phase,p_pos); + +} +float Particles::get_color_phase_pos(int p_phase) const { + + ERR_FAIL_INDEX_V(p_phase,VS::MAX_PARTICLE_COLOR_PHASES,-1); + return color_phase[p_phase].pos; +} + +void Particles::set_color_phase_color(int p_phase, const Color& p_color) { + + ERR_FAIL_INDEX(p_phase,VS::MAX_PARTICLE_COLOR_PHASES); + color_phase[p_phase].color=p_color; + VisualServer::get_singleton()->particles_set_color_phase_color(particles,p_phase,p_color); + +} +Color Particles::get_color_phase_color(int p_phase) const { + + ERR_FAIL_INDEX_V(p_phase,VS::MAX_PARTICLE_COLOR_PHASES,Color()); + return color_phase[p_phase].color; + +} + +void Particles::set_material(const Ref& p_material) { + + material=p_material; + if(material.is_null()) { + VisualServer::get_singleton()->particles_set_material(particles,RID()); + } else { + VisualServer::get_singleton()->particles_set_material(particles,material->get_rid()); + } + +} + +void Particles::setup_timer() { + + if (emitting && emit_timeout > 0) { + + timer->set_wait_time(emit_timeout); + timer->start(); + timer->set_one_shot(true); + }; +}; + +void Particles::set_emit_timeout(float p_timeout) { + + emit_timeout = p_timeout; + setup_timer(); +}; + +float Particles::get_emit_timeout() const { + + return emit_timeout; +}; + + +Ref Particles::get_material() const { + + return material; +} + +void Particles::set_height_from_velocity(bool p_enable) { + + height_from_velocity=p_enable; + VisualServer::get_singleton()->particles_set_height_from_velocity(particles,height_from_velocity); +} + +bool Particles::has_height_from_velocity() const { + + return height_from_velocity; +} + +void Particles::set_color_phases(int p_phases) { + + color_phase_count=p_phases; + VisualServer::get_singleton()->particles_set_color_phases(particles,p_phases); +} + +int Particles::get_color_phases() const{ + + return color_phase_count; +} + +bool Particles::_can_gizmo_scale() const { + + return false; +} + +void Particles::set_use_local_coordinates(bool p_use) { + + local_coordinates=p_use; + VisualServer::get_singleton()->particles_set_use_local_coordinates(particles,local_coordinates); +} + +bool Particles::is_using_local_coordinates() const{ + + return local_coordinates; +} + + +RES Particles::_get_gizmo_geometry() const { + + Ref surface_tool( memnew( SurfaceTool )); + + Ref mat( memnew( FixedMaterial )); + + mat->set_parameter( FixedMaterial::PARAM_DIFFUSE,Color(0.0,0.6,0.7,0.2) ); + mat->set_parameter( FixedMaterial::PARAM_EMISSION,Color(0.5,0.7,0.8) ); + mat->set_blend_mode( Material::BLEND_MODE_ADD ); + mat->set_flag(Material::FLAG_DOUBLE_SIDED,true); + mat->set_hint(Material::HINT_NO_DEPTH_DRAW,true); + + + surface_tool->begin(Mesh::PRIMITIVE_TRIANGLES); + surface_tool->set_material(mat); + + int sides=16; + int sections=24; + +// float len=1; + float deg=Math::deg2rad(var[VAR_SPREAD]*180); + if (deg==180) + deg=179.5; + + Vector3 to=Vector3(0,0,-1); + + for(int j=0;jadd_normal(p1r.normalized()); + surface_tool->add_vertex(p1r); + surface_tool->add_normal(p1s.normalized()); + surface_tool->add_vertex(p1s); + surface_tool->add_normal(p2s.normalized()); + surface_tool->add_vertex(p2s); + + surface_tool->add_normal(p1r.normalized()); + surface_tool->add_vertex(p1r); + surface_tool->add_normal(p2s.normalized()); + surface_tool->add_vertex(p2s); + surface_tool->add_normal(p2r.normalized()); + surface_tool->add_vertex(p2r); + + if (j==sections-1) { + + surface_tool->add_normal(p2r.normalized()); + surface_tool->add_vertex(p2r); + surface_tool->add_normal(p2s.normalized()); + surface_tool->add_vertex(p2s); + surface_tool->add_normal(Vector3(0,0,1)); + surface_tool->add_vertex(Vector3()); + } + } + } + + + Ref mesh = surface_tool->commit(); + + Ref mat_aabb( memnew( FixedMaterial )); + + mat_aabb->set_parameter( FixedMaterial::PARAM_DIFFUSE,Color(0.8,0.8,0.9,0.7) ); + mat_aabb->set_line_width(3); + mat_aabb->set_flag( Material::FLAG_UNSHADED, true ); + + surface_tool->begin(Mesh::PRIMITIVE_LINES); + surface_tool->set_material(mat_aabb); + + for(int i=0;i<12;i++) { + + Vector3 f,t; + visibility_aabb.get_edge(i,f,t); + surface_tool->add_vertex(f); + surface_tool->add_vertex(t); + } + + return surface_tool->commit(mesh); + +} + + +void Particles::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("set_amount","amount"),&Particles::set_amount); + ObjectTypeDB::bind_method(_MD("get_amount"),&Particles::get_amount); + ObjectTypeDB::bind_method(_MD("set_emitting","enabled"),&Particles::set_emitting); + ObjectTypeDB::bind_method(_MD("is_emitting"),&Particles::is_emitting); + ObjectTypeDB::bind_method(_MD("set_visibility_aabb","aabb"),&Particles::set_visibility_aabb); + ObjectTypeDB::bind_method(_MD("get_visibility_aabb"),&Particles::get_visibility_aabb); + ObjectTypeDB::bind_method(_MD("set_emission_half_extents","half_extents"),&Particles::set_emission_half_extents); + ObjectTypeDB::bind_method(_MD("get_emission_half_extents"),&Particles::get_emission_half_extents); + ObjectTypeDB::bind_method(_MD("set_emission_base_velocity","base_velocity"),&Particles::set_emission_base_velocity); + ObjectTypeDB::bind_method(_MD("get_emission_base_velocity"),&Particles::get_emission_base_velocity); + ObjectTypeDB::bind_method(_MD("set_emission_points","points"),&Particles::set_emission_points); + ObjectTypeDB::bind_method(_MD("get_emission_points"),&Particles::get_emission_points); + ObjectTypeDB::bind_method(_MD("set_gravity_normal","normal"),&Particles::set_gravity_normal); + ObjectTypeDB::bind_method(_MD("get_gravity_normal"),&Particles::get_gravity_normal); + ObjectTypeDB::bind_method(_MD("set_variable","variable","value"),&Particles::set_variable); + ObjectTypeDB::bind_method(_MD("get_variable","variable"),&Particles::get_variable); + ObjectTypeDB::bind_method(_MD("set_randomness","variable","randomness"),&Particles::set_randomness); + ObjectTypeDB::bind_method(_MD("get_randomness"),&Particles::get_randomness); + ObjectTypeDB::bind_method(_MD("set_color_phase_pos","phase","pos"),&Particles::set_color_phase_pos); + ObjectTypeDB::bind_method(_MD("get_color_phase_pos","phase"),&Particles::get_color_phase_pos); + ObjectTypeDB::bind_method(_MD("set_color_phase_color","phase","color"),&Particles::set_color_phase_color); + ObjectTypeDB::bind_method(_MD("get_color_phase_color","phase"),&Particles::get_color_phase_color); + ObjectTypeDB::bind_method(_MD("set_material","material:Material"),&Particles::set_material); + ObjectTypeDB::bind_method(_MD("get_material:Material"),&Particles::get_material); + ObjectTypeDB::bind_method(_MD("set_emit_timeout"),&Particles::set_emit_timeout); + ObjectTypeDB::bind_method(_MD("get_emit_timeout"),&Particles::get_emit_timeout); + ObjectTypeDB::bind_method(_MD("set_height_from_velocity","enable"),&Particles::set_height_from_velocity); + ObjectTypeDB::bind_method(_MD("has_height_from_velocity"),&Particles::has_height_from_velocity); + ObjectTypeDB::bind_method(_MD("set_use_local_coordinates","enable"),&Particles::set_use_local_coordinates); + ObjectTypeDB::bind_method(_MD("is_using_local_coordinates"),&Particles::is_using_local_coordinates); + + ObjectTypeDB::bind_method(_MD("set_color_phases","count"),&Particles::set_color_phases); + ObjectTypeDB::bind_method(_MD("get_color_phases"),&Particles::get_color_phases); + + ADD_PROPERTY( PropertyInfo( Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "Material" ), _SCS("set_material"), _SCS("get_material") ); + + ADD_PROPERTY( PropertyInfo( Variant::INT, "amount", PROPERTY_HINT_RANGE, "1,4096,1" ), _SCS("set_amount"), _SCS("get_amount") ); + ADD_PROPERTY( PropertyInfo( Variant::BOOL, "emitting" ), _SCS("set_emitting"), _SCS("is_emitting") ); + ADD_PROPERTY( PropertyInfo( Variant::_AABB, "visibility" ), _SCS("set_visibility_aabb"), _SCS("get_visibility_aabb") ); + ADD_PROPERTY( PropertyInfo( Variant::VECTOR3, "emission_extents" ), _SCS("set_emission_half_extents"), _SCS("get_emission_half_extents") ); + ADD_PROPERTY( PropertyInfo( Variant::VECTOR3, "emission_base_velocity" ), _SCS("set_emission_base_velocity"), _SCS("get_emission_base_velocity") ); + ADD_PROPERTY( PropertyInfo( Variant::VECTOR3_ARRAY, "emission_points" ), _SCS("set_emission_points"), _SCS("get_emission_points") ); + ADD_PROPERTY( PropertyInfo( Variant::VECTOR3, "gravity_normal" ), _SCS("set_gravity_normal"), _SCS("get_gravity_normal") ); + ADD_PROPERTY( PropertyInfo( Variant::BOOL, "local_coords" ), _SCS("set_use_local_coordinates"), _SCS("is_using_local_coordinates") ); + ADD_PROPERTY( PropertyInfo( Variant::REAL, "emit_timeout",PROPERTY_HINT_RANGE,"0,256,0.01"), _SCS("set_emit_timeout"), _SCS("get_emit_timeout") ); + + + ADD_PROPERTYI( PropertyInfo( Variant::REAL, "vars/lifetime", PROPERTY_HINT_RANGE,"0.1,60,0.01"), _SCS("set_variable"), _SCS("get_variable"), VAR_LIFETIME ); + ADD_PROPERTYI( PropertyInfo( Variant::REAL, "vars/spread", PROPERTY_HINT_RANGE,"0,1,0.01"), _SCS("set_variable"), _SCS("get_variable"), VAR_SPREAD ); + ADD_PROPERTYI( PropertyInfo( Variant::REAL, "vars/gravity", PROPERTY_HINT_RANGE,"-48,48,0.01"), _SCS("set_variable"), _SCS("get_variable"), VAR_GRAVITY ); + ADD_PROPERTYI( PropertyInfo( Variant::REAL, "vars/linear_vel", PROPERTY_HINT_RANGE,"-100,100,0.01"), _SCS("set_variable"), _SCS("get_variable"), VAR_LINEAR_VELOCITY ); + ADD_PROPERTYI( PropertyInfo( Variant::REAL, "vars/angular_vel", PROPERTY_HINT_RANGE,"-100,100,0.01"), _SCS("set_variable"), _SCS("get_variable"), VAR_ANGULAR_VELOCITY ); + ADD_PROPERTYI( PropertyInfo( Variant::REAL, "vars/linear_accel", PROPERTY_HINT_RANGE,"-100,100,0.01"), _SCS("set_variable"), _SCS("get_variable"), VAR_LINEAR_ACCELERATION ); + ADD_PROPERTYI( PropertyInfo( Variant::REAL, "vars/radial_accel", PROPERTY_HINT_RANGE,"-100,100,0.01"), _SCS("set_variable"), _SCS("get_variable"), VAR_DRAG ); + ADD_PROPERTYI( PropertyInfo( Variant::REAL, "vars/tan_accel", PROPERTY_HINT_RANGE,"-100,100,0.01"), _SCS("set_variable"), _SCS("get_variable"), VAR_TANGENTIAL_ACCELERATION ); + ADD_PROPERTYI( PropertyInfo( Variant::REAL, "vars/damping", PROPERTY_HINT_RANGE,"0,128,0.01"), _SCS("set_variable"), _SCS("get_variable"), VAR_DAMPING ); + ADD_PROPERTYI( PropertyInfo( Variant::REAL, "vars/initial_size", PROPERTY_HINT_RANGE,"0,100,0.01"), _SCS("set_variable"), _SCS("get_variable"), VAR_INITIAL_SIZE ); + ADD_PROPERTYI( PropertyInfo( Variant::REAL, "vars/final_size", PROPERTY_HINT_RANGE,"0,100,0.01"), _SCS("set_variable"), _SCS("get_variable"), VAR_FINAL_SIZE ); + ADD_PROPERTYI( PropertyInfo( Variant::REAL, "vars/initial_angle",PROPERTY_HINT_RANGE,"0,1,0.01"), _SCS("set_variable"), _SCS("get_variable"), VAR_INITIAL_ANGLE ); + ADD_PROPERTY( PropertyInfo( Variant::BOOL, "vars/height_from_velocity"), _SCS("set_height_from_velocity"), _SCS("has_height_from_velocity") ); + ADD_PROPERTYI( PropertyInfo( Variant::REAL, "vars/height",PROPERTY_HINT_RANGE,"0,4096,0.01"), _SCS("set_variable"), _SCS("get_variable"), VAR_HEIGHT); + ADD_PROPERTYI( PropertyInfo( Variant::REAL, "vars/height_speed_scale",PROPERTY_HINT_RANGE,"0,4096,0.01"), _SCS("set_variable"), _SCS("get_variable"), VAR_HEIGHT_SPEED_SCALE ); + + for(int i=0;iparticles_create(); + timer = memnew(Timer); + add_child(timer); + emit_timeout = 0; + + set_amount(64); + set_emitting(true); + set_visibility_aabb(AABB( Vector3(-4,-4,-4), Vector3(8,8,8) ) ); + + for (int i=0;i pars; + pars.push_back(false); + timer->connect("timeout", this, "set_emitting", pars); + set_base(particles); + local_coordinates=false; +} + + +Particles::~Particles() { + + VisualServer::get_singleton()->free(particles); +} + diff --git a/scene/3d/particles.h b/scene/3d/particles.h new file mode 100644 index 00000000000..40d569d7b43 --- /dev/null +++ b/scene/3d/particles.h @@ -0,0 +1,165 @@ +/*************************************************************************/ +/* particles.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 VISUALINSTANCEPARTICLES_H +#define VISUALINSTANCEPARTICLES_H + +#include "scene/3d/visual_instance.h" +#include "scene/resources/material.h" +#include "scene/main/timer.h" +#include "rid.h" + +/** + @author Juan Linietsky +*/ + +class Particles : public GeometryInstance { +public: + + enum Variable { + VAR_LIFETIME=VS::PARTICLE_LIFETIME, + VAR_SPREAD=VS::PARTICLE_SPREAD, + VAR_GRAVITY=VS::PARTICLE_GRAVITY, + VAR_LINEAR_VELOCITY=VS::PARTICLE_LINEAR_VELOCITY, + VAR_ANGULAR_VELOCITY=VS::PARTICLE_ANGULAR_VELOCITY, + VAR_LINEAR_ACCELERATION=VS::PARTICLE_LINEAR_ACCELERATION, + VAR_DRAG=VS::PARTICLE_RADIAL_ACCELERATION, + VAR_TANGENTIAL_ACCELERATION=VS::PARTICLE_TANGENTIAL_ACCELERATION, + VAR_DAMPING=VS::PARTICLE_DAMPING, + VAR_INITIAL_SIZE=VS::PARTICLE_INITIAL_SIZE, + VAR_FINAL_SIZE=VS::PARTICLE_FINAL_SIZE, + VAR_INITIAL_ANGLE=VS::PARTICLE_INITIAL_ANGLE, + VAR_HEIGHT=VS::PARTICLE_HEIGHT, + VAR_HEIGHT_SPEED_SCALE=VS::PARTICLE_HEIGHT_SPEED_SCALE, + VAR_MAX=VS::PARTICLE_VAR_MAX + }; + +private: + OBJ_TYPE( Particles, GeometryInstance ); + + RID particles; + + int amount; + bool emitting; + float emit_timeout; + AABB visibility_aabb; + Vector3 gravity_normal; + Vector3 emission_half_extents; + bool using_points; + float var[VAR_MAX]; + float var_random[VAR_MAX]; + bool height_from_velocity; + Vector3 emission_base_velocity; + bool local_coordinates; + + struct ColorPhase { + + Color color; + float pos; + }; + + virtual bool _can_gizmo_scale() const; + virtual RES _get_gizmo_geometry() const; + + int color_phase_count; + + ColorPhase color_phase[4]; + + Ref material; + + Timer* timer; + void setup_timer(); + +protected: + + static void _bind_methods(); + +public: + + + AABB get_aabb() const; + DVector get_faces(uint32_t p_usage_flags) const; + + void set_amount(int p_amount); + int get_amount() const; + + void set_emitting(bool p_emitting); + bool is_emitting() const; + + void set_visibility_aabb(const AABB& p_aabb); + AABB get_visibility_aabb() const; + + void set_emission_half_extents(const Vector3& p_half_extents); + Vector3 get_emission_half_extents() const; + + void set_emission_base_velocity(const Vector3& p_base_velocity); + Vector3 get_emission_base_velocity() const; + + void set_emission_points(const DVector& p_points); + DVector get_emission_points() const; + + void set_gravity_normal(const Vector3& p_normal); + Vector3 get_gravity_normal() const; + + void set_variable(Variable p_variable,float p_value); + float get_variable(Variable p_variable) const; + + void set_randomness(Variable p_variable,float p_randomness); + float get_randomness(Variable p_variable) const; + + void set_color_phases(int p_phases); + int get_color_phases() const; + + void set_color_phase_pos(int p_phase, float p_pos); + float get_color_phase_pos(int p_phase) const; + + void set_color_phase_color(int p_phase, const Color& p_color); + Color get_color_phase_color(int p_phase) const; + + void set_height_from_velocity(bool p_enable); + bool has_height_from_velocity() const; + + void set_material(const Ref& p_material); + Ref get_material() const; + + void set_emit_timeout(float p_timeout); + float get_emit_timeout() const; + + void set_use_local_coordinates(bool p_use); + bool is_using_local_coordinates() const; + + void start_emitting(float p_time); + + + Particles(); + ~Particles(); + +}; + +VARIANT_ENUM_CAST( Particles::Variable ); +#endif diff --git a/scene/3d/path.cpp b/scene/3d/path.cpp new file mode 100644 index 00000000000..bc5cb1c4a2e --- /dev/null +++ b/scene/3d/path.cpp @@ -0,0 +1,388 @@ +/*************************************************************************/ +/* path.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 "path.h" +#include "scene/scene_string_names.h" + +void Path::_notification(int p_what) { +#if 0 + if (p_what==NOTIFICATION_DRAW && curve.is_valid() && is_inside_scene() && get_scene()->is_editor_hint()) { + //draw the curve!! + + for(int i=0;iget_point_count();i++) { + + Vector2 prev_p=curve->get_point_pos(i); + + for(int j=1;j<=8;j++) { + + real_t frac = j/8.0; + Vector2 p = curve->interpolate(i,frac); + draw_line(prev_p,p,Color(0.5,0.6,1.0,0.7),2); + prev_p=p; + } + } + } +#endif +} + +void Path::_curve_changed() { + + + if (is_inside_scene() && get_scene()->is_editor_hint()) + update_gizmo(); +} + + +void Path::set_curve(const Ref& p_curve) { + + if (curve.is_valid()) { + curve->disconnect("changed",this,"_curve_changed"); + } + + curve=p_curve; + + if (curve.is_valid()) { + curve->connect("changed",this,"_curve_changed"); + } + _curve_changed(); + +} + +Ref Path::get_curve() const{ + + return curve; +} + +void Path::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("set_curve","curve:Curve3D"),&Path::set_curve); + ObjectTypeDB::bind_method(_MD("get_curve:Curve3D","curve"),&Path::get_curve); + ObjectTypeDB::bind_method(_MD("_curve_changed"),&Path::_curve_changed); + + ADD_PROPERTY( PropertyInfo( Variant::OBJECT, "curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve3D"), _SCS("set_curve"),_SCS("get_curve")); +} + +Path::Path() { + + set_curve(Ref( memnew( Curve3D ))); //create one by default +} + + +////////////// + + +void PathFollow::_update_transform() { + + + if (!path) + return; + + Ref c =path->get_curve(); + if (!c.is_valid()) + return; + + + float o = offset; + if (loop) + o=Math::fposmod(o,c->get_baked_length()); + + Vector3 pos = c->interpolate_baked(o,cubic); + Transform t=get_transform(); + + + if (rotation_mode!=ROTATION_NONE) { + + Vector3 n = (c->interpolate_baked(o+lookahead,cubic)-pos).normalized(); + + if (rotation_mode==ROTATION_Y) { + + n.y=0; + n.normalize(); + } + + if (n.length()interpolate_baked_tilt(o); + if (tilt!=0) { + + Matrix3 rot(-n,tilt); //remember.. lookat will be znegative.. znegative!! we abide by opengl clan. + up=rot.xform(up); + } + } + + t.set_look_at(pos,pos+n,up); + + } else { + + t.origin=pos; + } + + t.origin+=t.basis.get_axis(0)*h_offset + t.basis.get_axis(1)*v_offset; + set_transform(t); + +} + +void PathFollow::_notification(int p_what) { + + + switch(p_what) { + + case NOTIFICATION_ENTER_SCENE: { + + Node *parent=get_parent(); + if (parent) { + + path=parent->cast_to(); + if (path) { + _update_transform(); + } + } + + } break; + case NOTIFICATION_EXIT_SCENE: { + + + path=NULL; + } break; + } + +} + +void PathFollow::set_cubic_interpolation(bool p_enable) { + + cubic=p_enable; +} + +bool PathFollow::get_cubic_interpolation() const { + + return cubic; +} + + +bool PathFollow::_set(const StringName& p_name, const Variant& p_value) { + + if (p_name==SceneStringNames::get_singleton()->offset) { + set_offset(p_value); + } else if (p_name==SceneStringNames::get_singleton()->unit_offset) { + set_unit_offset(p_value); + } else if (p_name==SceneStringNames::get_singleton()->rotation_mode) { + set_rotation_mode(RotationMode(p_value.operator int())); + } else if (p_name==SceneStringNames::get_singleton()->v_offset) { + set_v_offset(p_value); + } else if (p_name==SceneStringNames::get_singleton()->h_offset) { + set_h_offset(p_value); + } else if (String(p_name)=="cubic_interp") { + set_cubic_interpolation(p_value); + } else if (String(p_name)=="loop") { + set_loop(p_value); + } else if (String(p_name)=="lookahead") { + set_lookahead(p_value); + } else + return false; + + return true; +} + +bool PathFollow::_get(const StringName& p_name,Variant &r_ret) const{ + + if (p_name==SceneStringNames::get_singleton()->offset) { + r_ret=get_offset(); + } else if (p_name==SceneStringNames::get_singleton()->unit_offset) { + r_ret=get_unit_offset(); + } else if (p_name==SceneStringNames::get_singleton()->rotation_mode) { + r_ret=get_rotation_mode(); + } else if (p_name==SceneStringNames::get_singleton()->v_offset) { + r_ret=get_v_offset(); + } else if (p_name==SceneStringNames::get_singleton()->h_offset) { + r_ret=get_h_offset(); + } else if (String(p_name)=="cubic_interp") { + r_ret=cubic; + } else if (String(p_name)=="loop") { + r_ret=loop; + } else if (String(p_name)=="lookahead") { + r_ret=lookahead; + } else + return false; + + return true; + +} +void PathFollow::_get_property_list( List *p_list) const{ + + float max=10000; + if (path && path->get_curve().is_valid()) + max=path->get_curve()->get_baked_length(); + p_list->push_back( PropertyInfo( Variant::REAL, "offset", PROPERTY_HINT_RANGE,"0,"+rtos(max)+",0.01")); + p_list->push_back( PropertyInfo( Variant::REAL, "unit_offset", PROPERTY_HINT_RANGE,"0,1,0.0001",PROPERTY_USAGE_EDITOR)); + p_list->push_back( PropertyInfo( Variant::REAL, "h_offset") ); + p_list->push_back( PropertyInfo( Variant::REAL, "v_offset") ); + p_list->push_back( PropertyInfo( Variant::INT, "rotation_mode", PROPERTY_HINT_ENUM,"None,Y,XY,XYZ")); + p_list->push_back( PropertyInfo( Variant::BOOL, "cubic_interp")); + p_list->push_back( PropertyInfo( Variant::BOOL, "loop")); + p_list->push_back( PropertyInfo( Variant::REAL, "lookahead",PROPERTY_HINT_RANGE,"0.001,1024.0,0.001")); +} + + +void PathFollow::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("set_offset","offset"),&PathFollow::set_offset); + ObjectTypeDB::bind_method(_MD("get_offset"),&PathFollow::get_offset); + + ObjectTypeDB::bind_method(_MD("set_h_offset","h_offset"),&PathFollow::set_h_offset); + ObjectTypeDB::bind_method(_MD("get_h_offset"),&PathFollow::get_h_offset); + + ObjectTypeDB::bind_method(_MD("set_v_offset","v_offset"),&PathFollow::set_v_offset); + ObjectTypeDB::bind_method(_MD("get_v_offset"),&PathFollow::get_v_offset); + + ObjectTypeDB::bind_method(_MD("set_unit_offset","unit_offset"),&PathFollow::set_unit_offset); + ObjectTypeDB::bind_method(_MD("get_unit_offset"),&PathFollow::get_unit_offset); + + ObjectTypeDB::bind_method(_MD("set_rotation_mode","rotation_mode"),&PathFollow::set_rotation_mode); + ObjectTypeDB::bind_method(_MD("get_rotation_mode"),&PathFollow::get_rotation_mode); + + ObjectTypeDB::bind_method(_MD("set_cubic_interpolation","enable"),&PathFollow::set_cubic_interpolation); + ObjectTypeDB::bind_method(_MD("get_cubic_interpolation"),&PathFollow::get_cubic_interpolation); + + ObjectTypeDB::bind_method(_MD("set_loop","loop"),&PathFollow::set_loop); + ObjectTypeDB::bind_method(_MD("has_loop"),&PathFollow::has_loop); + + BIND_CONSTANT( ROTATION_NONE ); + BIND_CONSTANT( ROTATION_Y ); + BIND_CONSTANT( ROTATION_XY ); + BIND_CONSTANT( ROTATION_XYZ ); + +} + +void PathFollow::set_offset(float p_offset) { + + offset=p_offset; + if (path) + _update_transform(); + _change_notify("offset"); + _change_notify("unit_offset"); + +} + +void PathFollow::set_h_offset(float p_h_offset) { + + h_offset=p_h_offset; + if (path) + _update_transform(); + +} + +float PathFollow::get_h_offset() const { + + return h_offset; +} + +void PathFollow::set_v_offset(float p_v_offset) { + + v_offset=p_v_offset; + if (path) + _update_transform(); + +} + +float PathFollow::get_v_offset() const { + + return v_offset; +} + + +float PathFollow::get_offset() const{ + + return offset; +} + +void PathFollow::set_unit_offset(float p_unit_offset) { + + if (path && path->get_curve().is_valid() && path->get_curve()->get_baked_length()) + set_offset(p_unit_offset*path->get_curve()->get_baked_length()); + +} + +float PathFollow::get_unit_offset() const{ + + if (path && path->get_curve().is_valid() && path->get_curve()->get_baked_length()) + return get_offset()/path->get_curve()->get_baked_length(); + else + return 0; +} + +void PathFollow::set_lookahead(float p_lookahead) { + + lookahead=p_lookahead; + +} + +float PathFollow::get_lookahead() const{ + + return lookahead; +} + +void PathFollow::set_rotation_mode(RotationMode p_rotation_mode) { + + rotation_mode=p_rotation_mode; + _update_transform(); +} + +PathFollow::RotationMode PathFollow::get_rotation_mode() const { + + return rotation_mode; +} + +void PathFollow::set_loop(bool p_loop) { + + loop=p_loop; +} + +bool PathFollow::has_loop() const{ + + return loop; +} + + +PathFollow::PathFollow() { + + offset=0; + h_offset=0; + v_offset=0; + path=NULL; + rotation_mode=ROTATION_XYZ; + cubic=true; + loop=true; + lookahead=0.1; +} diff --git a/scene/3d/path.h b/scene/3d/path.h new file mode 100644 index 00000000000..6f907265ba5 --- /dev/null +++ b/scene/3d/path.h @@ -0,0 +1,122 @@ +/*************************************************************************/ +/* path.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 PATH_H +#define PATH_H + +#include "scene/resources/curve.h" +#include "scene/3d/spatial.h" + +class Path : public Spatial { + + OBJ_TYPE( Path, Spatial ); + + Ref curve; + + void _curve_changed(); + + +protected: + + void _notification(int p_what); + static void _bind_methods(); +public: + + void set_curve(const Ref& p_curve); + Ref get_curve() const; + + + Path(); +}; + +class PathFollow : public Spatial { + + OBJ_TYPE(PathFollow,Spatial); +public: + + enum RotationMode { + + ROTATION_NONE, + ROTATION_Y, + ROTATION_XY, + ROTATION_XYZ + }; + +private: + Path *path; + real_t offset; + real_t h_offset; + real_t v_offset; + real_t lookahead; + bool cubic; + bool loop; + RotationMode rotation_mode; + + void _update_transform(); + + +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 *p_list) const; + + void _notification(int p_what); + static void _bind_methods(); +public: + + void set_offset(float p_offset); + float get_offset() const; + + void set_h_offset(float p_h_offset); + float get_h_offset() const; + + void set_v_offset(float p_v_offset); + float get_v_offset() const; + + void set_unit_offset(float p_unit_offset); + float get_unit_offset() const; + + void set_lookahead(float p_lookahead); + float get_lookahead() const; + + void set_loop(bool p_loop); + bool has_loop() const; + + void set_rotation_mode(RotationMode p_rotation_mode); + RotationMode get_rotation_mode() const; + + void set_cubic_interpolation(bool p_enable); + bool get_cubic_interpolation() const; + + PathFollow(); +}; + +VARIANT_ENUM_CAST(PathFollow::RotationMode); + +#endif // PATH_H diff --git a/scene/3d/physics_body.cpp b/scene/3d/physics_body.cpp new file mode 100644 index 00000000000..e42a96cc4ef --- /dev/null +++ b/scene/3d/physics_body.cpp @@ -0,0 +1,741 @@ +/*************************************************************************/ +/* physics_body.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 "physics_body.h" +#include "scene/scene_string_names.h" + +void PhysicsBody::_notification(int p_what) { + +/* + switch(p_what) { + + case NOTIFICATION_TRANSFORM_CHANGED: { + + PhysicsServer::get_singleton()->body_set_state(get_rid(),PhysicsServer::BODY_STATE_TRANSFORM,get_global_transform()); + + } break; + } + */ +} + +PhysicsBody::PhysicsBody(PhysicsServer::BodyMode p_mode) : CollisionObject( PhysicsServer::get_singleton()->body_create(p_mode), false) { + + + +} + +void StaticBody::set_constant_linear_velocity(const Vector3& p_vel) { + + constant_linear_velocity=p_vel; + PhysicsServer::get_singleton()->body_set_state(get_rid(),PhysicsServer::BODY_STATE_LINEAR_VELOCITY,constant_linear_velocity); + +} + +void StaticBody::set_constant_angular_velocity(const Vector3& p_vel) { + + constant_angular_velocity=p_vel; + PhysicsServer::get_singleton()->body_set_state(get_rid(),PhysicsServer::BODY_STATE_ANGULAR_VELOCITY,constant_angular_velocity); +} + +Vector3 StaticBody::get_constant_linear_velocity() const { + + return constant_linear_velocity; +} +Vector3 StaticBody::get_constant_angular_velocity() const { + + return constant_angular_velocity; +} + + +void StaticBody::_state_notify(Object *p_object) { + + if (!pre_xform) + return; + + PhysicsDirectBodyState *p2d = (PhysicsDirectBodyState*)p_object; + setting=true; + + Transform new_xform = p2d->get_transform(); + *pre_xform=new_xform; + set_ignore_transform_notification(true); + set_global_transform(new_xform); + set_ignore_transform_notification(false); + + setting=false; + + +} + +void StaticBody::_update_xform() { + + if (!pre_xform || !pending) + return; + + setting=true; + + + Transform new_xform = get_global_transform(); //obtain the new one + + //set_block_transform_notify(true); + set_ignore_transform_notification(true); + PhysicsServer::get_singleton()->body_set_state(get_rid(),PhysicsServer::BODY_STATE_TRANSFORM,*pre_xform); //then simulate motion! + set_global_transform(*pre_xform); //but restore state to previous one in both visual and physics + set_ignore_transform_notification(false); + + PhysicsServer::get_singleton()->body_static_simulate_motion(get_rid(),new_xform); //then simulate motion! + + setting=false; + pending=false; + +} + +void StaticBody::_notification(int p_what) { + + switch(p_what) { + + case NOTIFICATION_ENTER_SCENE: { + + if (pre_xform) + *pre_xform = get_global_transform(); + pending=false; + } break; + case NOTIFICATION_TRANSFORM_CHANGED: { + + if (simulating_motion && !pending && is_inside_scene() && !setting && !get_scene()->is_editor_hint()) { + + call_deferred(SceneStringNames::get_singleton()->_update_xform); + pending=true; + } + + } break; + } + + +} + +void StaticBody::set_simulate_motion(bool p_enable) { + + if (p_enable==simulating_motion) + return; + simulating_motion=p_enable; + + if (p_enable) { + pre_xform = memnew( Transform ); + if (is_inside_scene()) + *pre_xform=get_transform(); +// query = PhysicsServer::get_singleton()->query_create(this,"_state_notify",Variant()); + // PhysicsServer::get_singleton()->query_body_direct_state(query,get_rid()); + PhysicsServer::get_singleton()->body_set_force_integration_callback(get_rid(),this,"_state_notify"); + + } else { + memdelete( pre_xform ); + pre_xform=NULL; + PhysicsServer::get_singleton()->body_set_force_integration_callback(get_rid(),NULL,StringName()); + pending=false; + } +} + +bool StaticBody::is_simulating_motion() const { + + return simulating_motion; +} + +void StaticBody::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("set_simulate_motion","enabled"),&StaticBody::set_simulate_motion); + ObjectTypeDB::bind_method(_MD("is_simulating_motion"),&StaticBody::is_simulating_motion); + ObjectTypeDB::bind_method(_MD("_update_xform"),&StaticBody::_update_xform); + ObjectTypeDB::bind_method(_MD("_state_notify"),&StaticBody::_state_notify); + ObjectTypeDB::bind_method(_MD("set_constant_linear_velocity","vel"),&StaticBody::set_constant_linear_velocity); + ObjectTypeDB::bind_method(_MD("set_constant_angular_velocity","vel"),&StaticBody::set_constant_angular_velocity); + ObjectTypeDB::bind_method(_MD("get_constant_linear_velocity"),&StaticBody::get_constant_linear_velocity); + ObjectTypeDB::bind_method(_MD("get_constant_angular_velocity"),&StaticBody::get_constant_angular_velocity); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL,"simulate_motion"),_SCS("set_simulate_motion"),_SCS("is_simulating_motion")); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3,"constant_linear_velocity"),_SCS("set_constant_linear_velocity"),_SCS("get_constant_linear_velocity")); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3,"constant_angular_velocity"),_SCS("set_constant_angular_velocity"),_SCS("get_constant_angular_velocity")); +} + +StaticBody::StaticBody() : PhysicsBody(PhysicsServer::BODY_MODE_STATIC) { + + simulating_motion=false; + pre_xform=NULL; + setting=false; + pending=false; + //constant_angular_velocity=0; + +} + +StaticBody::~StaticBody() { + + if (pre_xform) + memdelete(pre_xform); + //if (query.is_valid()) + // PhysicsServer::get_singleton()->free(query); +} + + + + +void RigidBody::_body_enter_scene(ObjectID p_id) { + + Object *obj = ObjectDB::get_instance(p_id); + Node *node = obj ? obj->cast_to() : NULL; + ERR_FAIL_COND(!node); + + Map::Element *E=contact_monitor->body_map.find(p_id); + ERR_FAIL_COND(!E); + ERR_FAIL_COND(E->get().in_scene); + + E->get().in_scene=true; + emit_signal(SceneStringNames::get_singleton()->body_enter,node); + + for(int i=0;iget().shapes.size();i++) { + + emit_signal(SceneStringNames::get_singleton()->body_enter_shape,p_id,node,E->get().shapes[i].body_shape,E->get().shapes[i].local_shape); + } + +} + +void RigidBody::_body_exit_scene(ObjectID p_id) { + + Object *obj = ObjectDB::get_instance(p_id); + Node *node = obj ? obj->cast_to() : NULL; + ERR_FAIL_COND(!node); + Map::Element *E=contact_monitor->body_map.find(p_id); + ERR_FAIL_COND(!E); + ERR_FAIL_COND(!E->get().in_scene); + E->get().in_scene=false; + emit_signal(SceneStringNames::get_singleton()->body_exit,node); + for(int i=0;iget().shapes.size();i++) { + + emit_signal(SceneStringNames::get_singleton()->body_exit_shape,p_id,node,E->get().shapes[i].body_shape,E->get().shapes[i].local_shape); + } +} + +void RigidBody::_body_inout(int p_status, ObjectID p_instance, int p_body_shape,int p_local_shape) { + + bool body_in = p_status==1; + ObjectID objid=p_instance; + + Object *obj = ObjectDB::get_instance(objid); + Node *node = obj ? obj->cast_to() : NULL; + + Map::Element *E=contact_monitor->body_map.find(objid); + + ERR_FAIL_COND(!body_in && !E); + + if (body_in) { + if (!E) { + + E = contact_monitor->body_map.insert(objid,BodyState()); + E->get().rc=0; + E->get().in_scene=node && node->is_inside_scene(); + if (node) { + node->connect(SceneStringNames::get_singleton()->enter_scene,this,SceneStringNames::get_singleton()->_body_enter_scene,make_binds(objid)); + node->connect(SceneStringNames::get_singleton()->exit_scene,this,SceneStringNames::get_singleton()->_body_exit_scene,make_binds(objid)); + if (E->get().in_scene) { + emit_signal(SceneStringNames::get_singleton()->body_enter,node); + } + } + + } + E->get().rc++; + if (node) + E->get().shapes.insert(ShapePair(p_body_shape,p_local_shape)); + + + if (E->get().in_scene) { + emit_signal(SceneStringNames::get_singleton()->body_enter_shape,objid,node,p_body_shape,p_local_shape); + } + + } else { + + E->get().rc--; + + if (node) + E->get().shapes.erase(ShapePair(p_body_shape,p_local_shape)); + + if (E->get().rc==0) { + + if (node) { + node->disconnect(SceneStringNames::get_singleton()->enter_scene,this,SceneStringNames::get_singleton()->_body_enter_scene); + node->disconnect(SceneStringNames::get_singleton()->exit_scene,this,SceneStringNames::get_singleton()->_body_exit_scene); + if (E->get().in_scene) + emit_signal(SceneStringNames::get_singleton()->body_exit,obj); + + } + + contact_monitor->body_map.erase(E); + } + if (node && E->get().in_scene) { + emit_signal(SceneStringNames::get_singleton()->body_exit_shape,objid,obj,p_body_shape,p_local_shape); + } + + } + +} + + +struct _RigidBodyInOut { + + ObjectID id; + int shape; + int local_shape; +}; + +void RigidBody::_direct_state_changed(Object *p_state) { + + //eh.. fuck +#ifdef DEBUG_ENABLED + + state=p_state->cast_to(); +#else + state=(PhysicsDirectBodyState*)p_state; //trust it +#endif + + if (contact_monitor) { + + //untag all + int rc=0; + for( Map::Element *E=contact_monitor->body_map.front();E;E=E->next()) { + + for(int i=0;iget().shapes.size();i++) { + + E->get().shapes[i].tagged=false; + rc++; + } + } + + _RigidBodyInOut *toadd=(_RigidBodyInOut*)alloca(state->get_contact_count()*sizeof(_RigidBodyInOut)); + int toadd_count=0;//state->get_contact_count(); + RigidBody_RemoveAction *toremove=(RigidBody_RemoveAction*)alloca(rc*sizeof(RigidBody_RemoveAction)); + int toremove_count=0; + + //put the ones to add + + for(int i=0;iget_contact_count();i++) { + + ObjectID obj = state->get_contact_collider_id(i); + int local_shape = state->get_contact_local_shape(i); + int shape = state->get_contact_collider_shape(i); + toadd[i].local_shape=local_shape; + toadd[i].id=obj; + toadd[i].shape=shape; + + bool found=false; + + Map::Element *E=contact_monitor->body_map.find(obj); + if (!E) { + toadd_count++; + continue; + } + + ShapePair sp( shape,local_shape ); + int idx = E->get().shapes.find(sp); + if (idx==-1) { + + toadd_count++; + continue; + } + + E->get().shapes[idx].tagged=true; + } + + //put the ones to remove + + for( Map::Element *E=contact_monitor->body_map.front();E;E=E->next()) { + + for(int i=0;iget().shapes.size();i++) { + + if (!E->get().shapes[i].tagged) { + + toremove[toremove_count].body_id=E->key(); + toremove[toremove_count].pair=E->get().shapes[i]; + toremove_count++; + } + } + } + + + //process remotions + + for(int i=0;iget_transform()); + linear_velocity=state->get_linear_velocity(); + angular_velocity=state->get_angular_velocity(); + active=!state->is_sleeping(); + if (get_script_instance()) + get_script_instance()->call("_integrate_forces",state); + set_ignore_transform_notification(false); + + state=NULL; +} + +void RigidBody::_notification(int p_what) { + + +} + +void RigidBody::set_mode(Mode p_mode) { + + mode=p_mode; + switch(p_mode) { + + case MODE_RIGID: { + + PhysicsServer::get_singleton()->body_set_mode(get_rid(),PhysicsServer::BODY_MODE_RIGID); + } break; + case MODE_STATIC: { + + PhysicsServer::get_singleton()->body_set_mode(get_rid(),PhysicsServer::BODY_MODE_STATIC); + + } break; + case MODE_CHARACTER: { + PhysicsServer::get_singleton()->body_set_mode(get_rid(),PhysicsServer::BODY_MODE_CHARACTER); + + } break; + case MODE_STATIC_ACTIVE: { + + PhysicsServer::get_singleton()->body_set_mode(get_rid(),PhysicsServer::BODY_MODE_STATIC_ACTIVE); + } break; + + } +} + +RigidBody::Mode RigidBody::get_mode() const{ + + return mode; +} + +void RigidBody::set_mass(real_t p_mass){ + + ERR_FAIL_COND(p_mass<=0); + mass=p_mass; + _change_notify("mass"); + _change_notify("weight"); + PhysicsServer::get_singleton()->body_set_param(get_rid(),PhysicsServer::BODY_PARAM_MASS,mass); + +} +real_t RigidBody::get_mass() const{ + + return mass; +} + +void RigidBody::set_weight(real_t p_weight){ + + set_mass(p_weight/9.8); +} +real_t RigidBody::get_weight() const{ + + return mass*9.8; +} + + +void RigidBody::set_friction(real_t p_friction){ + + ERR_FAIL_COND(p_friction<0 || p_friction>1); + + friction=p_friction; + PhysicsServer::get_singleton()->body_set_param(get_rid(),PhysicsServer::BODY_PARAM_FRICTION,friction); + +} +real_t RigidBody::get_friction() const{ + + return friction; +} + +void RigidBody::set_bounce(real_t p_bounce){ + + ERR_FAIL_COND(p_bounce<0 || p_bounce>1); + + bounce=p_bounce; + PhysicsServer::get_singleton()->body_set_param(get_rid(),PhysicsServer::BODY_PARAM_BOUNCE,bounce); + +} +real_t RigidBody::get_bounce() const{ + + return bounce; +} + +void RigidBody::set_axis_velocity(const Vector3& p_axis) { + + Vector3 v = state? state->get_linear_velocity() : linear_velocity; + Vector3 axis = p_axis.normalized(); + v-=axis*axis.dot(v); + v+=p_axis; + if (state) { + set_linear_velocity(v); + } else { + PhysicsServer::get_singleton()->body_set_axis_velocity(get_rid(),p_axis); + linear_velocity=v; + } +} + +void RigidBody::set_linear_velocity(const Vector3& p_velocity){ + + linear_velocity=p_velocity; + if (state) + state->set_linear_velocity(linear_velocity); + else + PhysicsServer::get_singleton()->body_set_state(get_rid(),PhysicsServer::BODY_STATE_LINEAR_VELOCITY,linear_velocity); + +} + +Vector3 RigidBody::get_linear_velocity() const{ + + return linear_velocity; +} + +void RigidBody::set_angular_velocity(const Vector3& p_velocity){ + + angular_velocity=p_velocity; + if (state) + state->set_angular_velocity(angular_velocity); + else + PhysicsServer::get_singleton()->body_set_state(get_rid(),PhysicsServer::BODY_STATE_ANGULAR_VELOCITY,angular_velocity); +} +Vector3 RigidBody::get_angular_velocity() const{ + + return angular_velocity; +} + +void RigidBody::set_use_custom_integrator(bool p_enable){ + + if (custom_integrator==p_enable) + return; + + custom_integrator=p_enable; + PhysicsServer::get_singleton()->body_set_omit_force_integration(get_rid(),p_enable); + + +} +bool RigidBody::is_using_custom_integrator(){ + + return custom_integrator; +} + +void RigidBody::set_active(bool p_active) { + + active=p_active; + PhysicsServer::get_singleton()->body_set_state(get_rid(),PhysicsServer::BODY_STATE_SLEEPING,!active); + +} + +void RigidBody::set_can_sleep(bool p_active) { + + can_sleep=p_active; + PhysicsServer::get_singleton()->body_set_state(get_rid(),PhysicsServer::BODY_STATE_CAN_SLEEP,p_active); +} + +bool RigidBody::is_able_to_sleep() const { + + return can_sleep; +} + +bool RigidBody::is_active() const { + + return active; +} + +void RigidBody::set_max_contacts_reported(int p_amount) { + + max_contacts_reported=p_amount; + PhysicsServer::get_singleton()->body_set_max_contacts_reported(get_rid(),p_amount); +} + +int RigidBody::get_max_contacts_reported() const{ + + return max_contacts_reported; +} + +void RigidBody::apply_impulse(const Vector3& p_pos, const Vector3& p_impulse) { + + PhysicsServer::get_singleton()->body_apply_impulse(get_rid(),p_pos,p_impulse); +} + +void RigidBody::set_use_continuous_collision_detection(bool p_enable) { + + ccd=p_enable; + PhysicsServer::get_singleton()->body_set_enable_continuous_collision_detection(get_rid(),p_enable); +} + +bool RigidBody::is_using_continuous_collision_detection() const { + + + return ccd; +} + + +void RigidBody::set_contact_monitor(bool p_enabled) { + + if (p_enabled==is_contact_monitor_enabled()) + return; + + if (!p_enabled) { + + for(Map::Element *E=contact_monitor->body_map.front();E;E=E->next()) { + + //clean up mess + } + + memdelete( contact_monitor ); + contact_monitor=NULL; + } else { + + contact_monitor = memnew( ContactMonitor ); + } + +} + +bool RigidBody::is_contact_monitor_enabled() const { + + return contact_monitor!=NULL; +} + + + +void RigidBody::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("set_mode","mode"),&RigidBody::set_mode); + ObjectTypeDB::bind_method(_MD("get_mode"),&RigidBody::get_mode); + + ObjectTypeDB::bind_method(_MD("set_mass","mass"),&RigidBody::set_mass); + ObjectTypeDB::bind_method(_MD("get_mass"),&RigidBody::get_mass); + + ObjectTypeDB::bind_method(_MD("set_weight","weight"),&RigidBody::set_weight); + ObjectTypeDB::bind_method(_MD("get_weight"),&RigidBody::get_weight); + + ObjectTypeDB::bind_method(_MD("set_friction","friction"),&RigidBody::set_friction); + ObjectTypeDB::bind_method(_MD("get_friction"),&RigidBody::get_friction); + + ObjectTypeDB::bind_method(_MD("set_bounce","bounce"),&RigidBody::set_bounce); + ObjectTypeDB::bind_method(_MD("get_bounce"),&RigidBody::get_bounce); + + ObjectTypeDB::bind_method(_MD("set_linear_velocity","linear_velocity"),&RigidBody::set_linear_velocity); + ObjectTypeDB::bind_method(_MD("get_linear_velocity"),&RigidBody::get_linear_velocity); + + ObjectTypeDB::bind_method(_MD("set_angular_velocity","angular_velocity"),&RigidBody::set_angular_velocity); + ObjectTypeDB::bind_method(_MD("get_angular_velocity"),&RigidBody::get_angular_velocity); + + ObjectTypeDB::bind_method(_MD("set_max_contacts_reported","amount"),&RigidBody::set_max_contacts_reported); + ObjectTypeDB::bind_method(_MD("get_max_contacts_reported"),&RigidBody::get_max_contacts_reported); + + ObjectTypeDB::bind_method(_MD("set_use_custom_integrator","enable"),&RigidBody::set_use_custom_integrator); + ObjectTypeDB::bind_method(_MD("is_using_custom_integrator"),&RigidBody::is_using_custom_integrator); + + ObjectTypeDB::bind_method(_MD("set_contact_monitor","enabled"),&RigidBody::set_contact_monitor); + ObjectTypeDB::bind_method(_MD("is_contact_monitor_enabled"),&RigidBody::is_contact_monitor_enabled); + + ObjectTypeDB::bind_method(_MD("set_use_continuous_collision_detection","enable"),&RigidBody::set_use_continuous_collision_detection); + ObjectTypeDB::bind_method(_MD("is_using_continuous_collision_detection"),&RigidBody::is_using_continuous_collision_detection); + + ObjectTypeDB::bind_method(_MD("set_axis_velocity","axis_velocity"),&RigidBody::set_axis_velocity); + ObjectTypeDB::bind_method(_MD("apply_impulse","pos","impulse"),&RigidBody::apply_impulse); + + ObjectTypeDB::bind_method(_MD("set_active","active"),&RigidBody::set_active); + ObjectTypeDB::bind_method(_MD("is_active"),&RigidBody::is_active); + + ObjectTypeDB::bind_method(_MD("set_can_sleep","able_to_sleep"),&RigidBody::set_can_sleep); + ObjectTypeDB::bind_method(_MD("is_able_to_sleep"),&RigidBody::is_able_to_sleep); + + ObjectTypeDB::bind_method(_MD("_direct_state_changed"),&RigidBody::_direct_state_changed); + ObjectTypeDB::bind_method(_MD("_body_enter_scene"),&RigidBody::_body_enter_scene); + ObjectTypeDB::bind_method(_MD("_body_exit_scene"),&RigidBody::_body_exit_scene); + + BIND_VMETHOD(MethodInfo("_integrate_forces",PropertyInfo(Variant::OBJECT,"state:PhysicsDirectBodyState"))); + + ADD_PROPERTY( PropertyInfo(Variant::INT,"mode",PROPERTY_HINT_ENUM,"Rigid,Static,Character,Static Active"),_SCS("set_mode"),_SCS("get_mode")); + ADD_PROPERTY( PropertyInfo(Variant::REAL,"mass",PROPERTY_HINT_EXP_RANGE,"0.01,65535,0.01"),_SCS("set_mass"),_SCS("get_mass")); + ADD_PROPERTY( PropertyInfo(Variant::REAL,"weight",PROPERTY_HINT_EXP_RANGE,"0.01,65535,0.01",PROPERTY_USAGE_EDITOR),_SCS("set_weight"),_SCS("get_weight")); + ADD_PROPERTY( PropertyInfo(Variant::REAL,"friction",PROPERTY_HINT_RANGE,"0,1,0.01"),_SCS("set_friction"),_SCS("get_friction")); + ADD_PROPERTY( PropertyInfo(Variant::REAL,"bounce",PROPERTY_HINT_RANGE,"0,1,0.01"),_SCS("set_bounce"),_SCS("get_bounce")); + ADD_PROPERTY( PropertyInfo(Variant::BOOL,"custom_integrator"),_SCS("set_use_custom_integrator"),_SCS("is_using_custom_integrator")); + ADD_PROPERTY( PropertyInfo(Variant::BOOL,"continuous_cd"),_SCS("set_use_continuous_collision_detection"),_SCS("is_using_continuous_collision_detection")); + ADD_PROPERTY( PropertyInfo(Variant::INT,"contacts_reported"),_SCS("set_max_contacts_reported"),_SCS("get_max_contacts_reported")); + ADD_PROPERTY( PropertyInfo(Variant::BOOL,"contact_monitor"),_SCS("set_contact_monitor"),_SCS("is_contact_monitor_enabled")); + ADD_PROPERTY( PropertyInfo(Variant::BOOL,"active"),_SCS("set_active"),_SCS("is_active")); + ADD_PROPERTY( PropertyInfo(Variant::BOOL,"can_sleep"),_SCS("set_can_sleep"),_SCS("is_able_to_sleep")); + ADD_PROPERTY( PropertyInfo(Variant::VECTOR3,"velocity/linear"),_SCS("set_linear_velocity"),_SCS("get_linear_velocity")); + ADD_PROPERTY( PropertyInfo(Variant::VECTOR3,"velocity/angular"),_SCS("set_angular_velocity"),_SCS("get_angular_velocity")); + + ADD_SIGNAL( MethodInfo("body_enter_shape",PropertyInfo(Variant::INT,"body_id"),PropertyInfo(Variant::OBJECT,"body"),PropertyInfo(Variant::INT,"body_shape"),PropertyInfo(Variant::INT,"local_shape"))); + ADD_SIGNAL( MethodInfo("body_exit_shape",PropertyInfo(Variant::INT,"body_id"),PropertyInfo(Variant::OBJECT,"body"),PropertyInfo(Variant::INT,"body_shape"),PropertyInfo(Variant::INT,"local_shape"))); + ADD_SIGNAL( MethodInfo("body_enter",PropertyInfo(Variant::OBJECT,"body"))); + ADD_SIGNAL( MethodInfo("body_exit",PropertyInfo(Variant::OBJECT,"body"))); + + BIND_CONSTANT( MODE_STATIC ); + BIND_CONSTANT( MODE_STATIC_ACTIVE ); + BIND_CONSTANT( MODE_RIGID ); + BIND_CONSTANT( MODE_CHARACTER ); +} + +RigidBody::RigidBody() : PhysicsBody(PhysicsServer::BODY_MODE_RIGID) { + + mode=MODE_RIGID; + + bounce=0; + mass=1; + friction=1; + max_contacts_reported=0; + state=NULL; + + //angular_velocity=0; + active=true; + ccd=false; + + custom_integrator=false; + contact_monitor=NULL; + can_sleep=true; + + PhysicsServer::get_singleton()->body_set_force_integration_callback(get_rid(),this,"_direct_state_changed"); +} + +RigidBody::~RigidBody() { + + if (contact_monitor) + memdelete( contact_monitor ); + + + +} + diff --git a/scene/3d/physics_body.h b/scene/3d/physics_body.h new file mode 100644 index 00000000000..0cb24075bda --- /dev/null +++ b/scene/3d/physics_body.h @@ -0,0 +1,219 @@ +/*************************************************************************/ +/* physics_body.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 PHYSICS_BODY__H +#define PHYSICS_BODY__H + +#include "scene/3d/collision_object.h" +#include "servers/physics_server.h" +#include "vset.h" + + +class PhysicsBody : public CollisionObject { + + OBJ_TYPE(PhysicsBody,CollisionObject); + +protected: + + void _notification(int p_what); + PhysicsBody(PhysicsServer::BodyMode p_mode); +public: + + PhysicsBody(); + +}; + +class StaticBody : public PhysicsBody { + + OBJ_TYPE(StaticBody,PhysicsBody); + + Transform *pre_xform; + //RID query; + bool setting; + bool pending; + bool simulating_motion; + Vector3 constant_linear_velocity; + Vector3 constant_angular_velocity; + void _update_xform(); + void _state_notify(Object *p_object); + +protected: + + void _notification(int p_what); + static void _bind_methods(); + +public: + + void set_simulate_motion(bool p_enable); + bool is_simulating_motion() const; + + void set_constant_linear_velocity(const Vector3& p_vel); + void set_constant_angular_velocity(const Vector3& p_vel); + + Vector3 get_constant_linear_velocity() const; + Vector3 get_constant_angular_velocity() const; + + StaticBody(); + ~StaticBody(); + +}; + +class RigidBody : public PhysicsBody { + + OBJ_TYPE(RigidBody,PhysicsBody); +public: + + enum Mode { + MODE_RIGID, + MODE_STATIC, + MODE_CHARACTER, + MODE_STATIC_ACTIVE, + }; +private: + + bool can_sleep; + PhysicsDirectBodyState *state; + Mode mode; + + real_t bounce; + real_t mass; + real_t friction; + + Vector3 linear_velocity; + Vector3 angular_velocity; + bool active; + bool ccd; + + + int max_contacts_reported; + + bool custom_integrator; + + + struct ShapePair { + + int body_shape; + int local_shape; + bool tagged; + bool operator<(const ShapePair& p_sp) const { + if (body_shape==p_sp.body_shape) + return local_shape < p_sp.local_shape; + else + return body_shape < p_sp.body_shape; + } + + ShapePair() {} + ShapePair(int p_bs, int p_ls) { body_shape=p_bs; local_shape=p_ls; } + }; + struct RigidBody_RemoveAction { + + + ObjectID body_id; + ShapePair pair; + + }; + struct BodyState { + + int rc; + bool in_scene; + VSet shapes; + }; + + struct ContactMonitor { + + + Map body_map; + + }; + + + ContactMonitor *contact_monitor; + void _body_enter_scene(ObjectID p_id); + void _body_exit_scene(ObjectID p_id); + + + void _body_inout(int p_status, ObjectID p_instance, int p_body_shape,int p_local_shape); + void _direct_state_changed(Object *p_state); + + +protected: + + void _notification(int p_what); + static void _bind_methods(); +public: + + void set_mode(Mode p_mode); + Mode get_mode() const; + + void set_mass(real_t p_mass); + real_t get_mass() const; + + void set_weight(real_t p_weight); + real_t get_weight() const; + + void set_friction(real_t p_friction); + real_t get_friction() const; + + void set_bounce(real_t p_bounce); + real_t get_bounce() const; + + void set_linear_velocity(const Vector3& p_velocity); + Vector3 get_linear_velocity() const; + + void set_axis_velocity(const Vector3& p_axis); + + void set_angular_velocity(const Vector3&p_velocity); + Vector3 get_angular_velocity() const; + + void set_use_custom_integrator(bool p_enable); + bool is_using_custom_integrator(); + + void set_active(bool p_active); + bool is_active() const; + + void set_can_sleep(bool p_active); + bool is_able_to_sleep() const; + + void set_contact_monitor(bool p_enabled); + bool is_contact_monitor_enabled() const; + + void set_max_contacts_reported(int p_amount); + int get_max_contacts_reported() const; + + void set_use_continuous_collision_detection(bool p_enable); + bool is_using_continuous_collision_detection() const; + + void apply_impulse(const Vector3& p_pos, const Vector3& p_impulse); + + RigidBody(); + ~RigidBody(); + +}; + +VARIANT_ENUM_CAST(RigidBody::Mode); +#endif // PHYSICS_BODY__H diff --git a/scene/3d/physics_joint.cpp b/scene/3d/physics_joint.cpp new file mode 100644 index 00000000000..961198c8d4e --- /dev/null +++ b/scene/3d/physics_joint.cpp @@ -0,0 +1,327 @@ +/*************************************************************************/ +/* physics_joint.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 "physics_joint.h" + +#if 0 + +void PhysicsJoint::_set(const String& p_name, const Variant& p_value) { + + if (p_name=="body_A") + set_body_A(p_value); + else if (p_name=="body_B") + set_body_B(p_value); + else if (p_name=="active") + set_active(p_value); + else if (p_name=="no_collision") + set_disable_collision(p_value); +} +Variant PhysicsJoint::_get(const String& p_name) const { + + if (p_name=="body_A") + return get_body_A(); + else if (p_name=="body_B") + return get_body_B(); + else if (p_name=="active") + return is_active(); + else if (p_name=="no_collision") + return has_disable_collision(); + + return Variant(); +} +void PhysicsJoint::_get_property_list( List *p_list) const { + + + p_list->push_back( PropertyInfo( Variant::NODE_PATH, "body_A" ) ); + p_list->push_back( PropertyInfo( Variant::NODE_PATH, "body_B" ) ); + p_list->push_back( PropertyInfo( Variant::BOOL, "active" ) ); + p_list->push_back( PropertyInfo( Variant::BOOL, "no_collision" ) ); +} +void PhysicsJoint::_notification(int p_what) { + + + switch(p_what) { + + case NOTIFICATION_PARENT_CONFIGURED: { + + _connect(); + if (get_root_node()->get_editor() && !indicator.is_valid()) { + + indicator=VisualServer::get_singleton()->poly_create(); + RID mat=VisualServer::get_singleton()->fixed_material_create(); + VisualServer::get_singleton()->material_set_flag( mat, VisualServer::MATERIAL_FLAG_UNSHADED, true ); + VisualServer::get_singleton()->material_set_flag( mat, VisualServer::MATERIAL_FLAG_ONTOP, true ); + VisualServer::get_singleton()->material_set_flag( mat, VisualServer::MATERIAL_FLAG_WIREFRAME, true ); + VisualServer::get_singleton()->material_set_flag( mat, VisualServer::MATERIAL_FLAG_DOUBLE_SIDED, true ); + VisualServer::get_singleton()->material_set_line_width( mat, 3 ); + + VisualServer::get_singleton()->poly_set_material(indicator,mat,true); + _update_indicator(); + + } + + if (indicator.is_valid()) { + + indicator_instance=VisualServer::get_singleton()->instance_create(indicator,get_world()->get_scenario()); + VisualServer::get_singleton()->instance_attach_object_instance_ID( indicator_instance,get_instance_ID() ); + } + } break; + case NOTIFICATION_TRANSFORM_CHANGED: { + + if (indicator_instance.is_valid()) { + + VisualServer::get_singleton()->instance_set_transform(indicator_instance,get_global_transform()); + } + } break; + case NOTIFICATION_EXIT_SCENE: { + + if (indicator_instance.is_valid()) { + + VisualServer::get_singleton()->free(indicator_instance); + } + _disconnect(); + + } break; + + } +} + + +RID PhysicsJoint::_get_visual_instance_rid() const { + + return indicator_instance; + +} + +void PhysicsJoint::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("_get_visual_instance_rid"),&PhysicsJoint::_get_visual_instance_rid); + ObjectTypeDB::bind_method(_MD("set_body_A","path"),&PhysicsJoint::set_body_A); + ObjectTypeDB::bind_method(_MD("set_body_B"),&PhysicsJoint::set_body_B); + ObjectTypeDB::bind_method(_MD("get_body_A","path"),&PhysicsJoint::get_body_A); + ObjectTypeDB::bind_method(_MD("get_body_B"),&PhysicsJoint::get_body_B); + + ObjectTypeDB::bind_method(_MD("set_active","active"),&PhysicsJoint::set_active); + ObjectTypeDB::bind_method(_MD("is_active"),&PhysicsJoint::is_active); + + ObjectTypeDB::bind_method(_MD("set_disable_collision","disable"),&PhysicsJoint::set_disable_collision); + ObjectTypeDB::bind_method(_MD("has_disable_collision"),&PhysicsJoint::has_disable_collision); + + + ObjectTypeDB::bind_method("reconnect",&PhysicsJoint::reconnect); + + ObjectTypeDB::bind_method(_MD("get_rid"),&PhysicsJoint::get_rid); + +} + +void PhysicsJoint::set_body_A(const NodePath& p_path) { + + _disconnect(); + body_A=p_path; + _connect(); + _change_notify("body_A"); +} +void PhysicsJoint::set_body_B(const NodePath& p_path) { + + _disconnect(); + body_B=p_path; + _connect(); + _change_notify("body_B"); + +} +NodePath PhysicsJoint::get_body_A() const { + + return body_A; +} +NodePath PhysicsJoint::get_body_B() const { + + return body_B; +} + +void PhysicsJoint::set_active(bool p_active) { + + active=p_active; + if (is_inside_scene()) { + PhysicsServer::get_singleton()->joint_set_active(joint,active); + } + _change_notify("active"); +} + +void PhysicsJoint::set_disable_collision(bool p_active) { + + if (no_collision==p_active) + return; + _disconnect(); + no_collision=p_active; + _connect(); + + _change_notify("no_collision"); +} +bool PhysicsJoint::has_disable_collision() const { + + return no_collision; +} + + + +bool PhysicsJoint::is_active() const { + + return active; +} + +void PhysicsJoint::_disconnect() { + + if (!is_inside_scene()) + return; + + if (joint.is_valid()) + PhysicsServer::get_singleton()->free(joint); + + joint=RID(); + + Node *nA = get_node(body_A); + Node *nB = get_node(body_B); + + PhysicsBody *A = nA?nA->cast_to():NULL; + PhysicsBody *B = nA?nB->cast_to():NULL; + + if (!A ||!B) + return; + + if (no_collision) + PhysicsServer::get_singleton()->body_remove_collision_exception(A->get_body(),B->get_body()); + +} +void PhysicsJoint::_connect() { + + if (!is_inside_scene()) + return; + + ERR_FAIL_COND(joint.is_valid()); + + Node *nA = get_node(body_A); + Node *nB = get_node(body_B); + + PhysicsBody *A = nA?nA->cast_to():NULL; + PhysicsBody *B = nA?nB->cast_to():NULL; + + if (!A && !B) + return; + + if (B && !A) + SWAP(B,A); + + joint = create(A,B); + + if (Abody_add_collision_exception(A->get_body(),B->get_body()); + + + +} + +void PhysicsJoint::reconnect() { + + _disconnect(); + _connect(); + +} + + +RID PhysicsJoint::get_rid() { + + return joint; +} + + +PhysicsJoint::PhysicsJoint() { + + active=true; + no_collision=true; +} + + +PhysicsJoint::~PhysicsJoint() { + + if (indicator.is_valid()) { + + VisualServer::get_singleton()->free(indicator); + } + +} + +/* PIN */ + +void PhysicsJointPin::_update_indicator() { + + + VisualServer::get_singleton()->poly_clear(indicator); + + Vector colors; + colors.push_back( Color(0.3,0.9,0.2,0.7) ); + colors.push_back( Color(0.3,0.9,0.2,0.7) ); + + Vector points; + points.resize(2); + points[0]=Vector3(Vector3(-0.2,0,0)); + points[1]=Vector3(Vector3(0.2,0,0)); + VisualServer::get_singleton()->poly_add_primitive(indicator,points,Vector(),colors,Vector()); + + points[0]=Vector3(Vector3(0,-0.2,0)); + points[1]=Vector3(Vector3(0,0.2,0)); + VisualServer::get_singleton()->poly_add_primitive(indicator,points,Vector(),colors,Vector()); + + points[0]=Vector3(Vector3(0,0,-0.2)); + points[1]=Vector3(Vector3(0,0,0.2)); + VisualServer::get_singleton()->poly_add_primitive(indicator,points,Vector(),colors,Vector()); + +} + +RID PhysicsJointPin::create(PhysicsBody*A,PhysicsBody*B) { + + RID body_A = A->get_body(); + RID body_B = B?B->get_body():RID(); + + ERR_FAIL_COND_V( !body_A.is_valid(), RID() ); + + Vector3 pin_pos = get_global_transform().origin; + + if (body_B.is_valid()) + return PhysicsServer::get_singleton()->joint_create_double_pin_global(body_A,pin_pos,body_B,pin_pos); + else + return PhysicsServer::get_singleton()->joint_create_pin(body_A,A->get_global_transform().xform_inv(pin_pos),pin_pos); +} + +PhysicsJointPin::PhysicsJointPin() { + + +} +#endif diff --git a/scene/3d/physics_joint.h b/scene/3d/physics_joint.h new file mode 100644 index 00000000000..4a0c609e692 --- /dev/null +++ b/scene/3d/physics_joint.h @@ -0,0 +1,104 @@ +/*************************************************************************/ +/* physics_joint.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 PHYSICS_JOINT_H +#define PHYSICS_JOINT_H + +#include "scene/3d/spatial.h" +#include "scene/3d/physics_body.h" + +#if 0 +class PhysicsJoint : public Spatial { + + OBJ_TYPE(PhysicsJoint,Spatial); + OBJ_CATEGORY("3D Physics Nodes"); + + NodePath body_A; + NodePath body_B; + bool active; + bool no_collision; + + + RID indicator_instance; + + RID _get_visual_instance_rid() const; +protected: + + RID joint; + RID indicator; + + 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 *p_list) const; + void _notification(int p_what); + static void _bind_methods(); + + virtual RID create(PhysicsBody*A,PhysicsBody*B)=0; + virtual void _update_indicator()=0; + + void _disconnect(); + void _connect(); +public: + + void set_body_A(const NodePath& p_path); + void set_body_B(const NodePath& p_path); + NodePath get_body_A() const; + NodePath get_body_B() const; + + void set_active(bool p_active); + bool is_active() const; + + void set_disable_collision(bool p_active); + bool has_disable_collision() const; + + void reconnect(); + + RID get_rid(); + + PhysicsJoint(); + ~PhysicsJoint(); +}; + + + +class PhysicsJointPin : public PhysicsJoint { + + OBJ_TYPE( PhysicsJointPin, PhysicsJoint ); + +protected: + + virtual void _update_indicator(); + virtual RID create(PhysicsBody*A,PhysicsBody*B); +public: + + + PhysicsJointPin(); +}; + +#endif // PHYSICS_JOINT_H +#endif diff --git a/scene/3d/portal.cpp b/scene/3d/portal.cpp new file mode 100644 index 00000000000..12b9dc4b7a8 --- /dev/null +++ b/scene/3d/portal.cpp @@ -0,0 +1,281 @@ +/*************************************************************************/ +/* portal.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 "portal.h" +#include "servers/visual_server.h" +#include "scene/resources/surface_tool.h" +#include "globals.h" + +bool Portal::_set(const StringName& p_name, const Variant& p_value) { + + if (p_name=="shape") { + DVector src_coords=p_value; + Vector points; + int src_coords_size = src_coords.size(); + ERR_FAIL_COND_V(src_coords_size%2,false); + points.resize(src_coords_size/2); + for (int i=0;i points=get_shape(); + DVector dst_coords; + dst_coords.resize(points.size()*2); + + for (int i=0;i *p_list) const { + + p_list->push_back( PropertyInfo( Variant::REAL_ARRAY, "shape" ) ); + p_list->push_back( PropertyInfo( Variant::BOOL, "enabled" ) ); + p_list->push_back( PropertyInfo( Variant::REAL, "disable_distance",PROPERTY_HINT_RANGE,"0,4096,0.01" ) ); + p_list->push_back( PropertyInfo( Variant::COLOR, "disabled_color") ); + p_list->push_back( PropertyInfo( Variant::REAL, "connect_range",PROPERTY_HINT_RANGE,"0.1,4096,0.01" ) ); +} + + +RES Portal::_get_gizmo_geometry() const { + + Ref surface_tool( memnew( SurfaceTool )); + + Ref mat( memnew( FixedMaterial )); + + mat->set_parameter( FixedMaterial::PARAM_DIFFUSE,Color(1.0,0.8,0.8,0.7) ); + mat->set_line_width(4); + mat->set_flag(Material::FLAG_DOUBLE_SIDED,true); + mat->set_flag(Material::FLAG_UNSHADED,true); + mat->set_hint(Material::HINT_NO_DEPTH_DRAW,true); + + surface_tool->begin(Mesh::PRIMITIVE_LINES); + surface_tool->set_material(mat); + + Vector shape = get_shape(); + + Vector2 center; + for (int i=0;i points; + surface_tool->add_vertex( Vector3( shape[i].x, shape[i].y,0 )); + surface_tool->add_vertex( Vector3( shape[n].x, shape[n].y,0 )); + center+=shape[i]; + + } + + if (shape.size()>0) { + + center/=shape.size(); + Vector points; + surface_tool->add_vertex( Vector3( center.x, center.y,0 )); + surface_tool->add_vertex( Vector3( center.x, center.y,1.0 )); + } + + return surface_tool->commit(); +} + + + +AABB Portal::get_aabb() const { + + return aabb; +} +DVector Portal::get_faces(uint32_t p_usage_flags) const { + + if (!(p_usage_flags&FACES_ENCLOSING)) + return DVector(); + + Vector shape = get_shape(); + if (shape.size()==0) + return DVector(); + + Vector2 center; + for (int i=0;i ret; + center/=shape.size(); + + for (int i=0;i& p_shape) { + + + VisualServer::get_singleton()->portal_set_shape(portal, p_shape); + update_gizmo(); +} + +Vector Portal::get_shape() const { + + return VisualServer::get_singleton()->portal_get_shape(portal); +} + +void Portal::set_connect_range(float p_range) { + + connect_range=p_range; + VisualServer::get_singleton()->portal_set_connect_range(portal,p_range); +} + +float Portal::get_connect_range() const { + + return connect_range; +} + + +void Portal::set_enabled(bool p_enabled) { + + enabled=p_enabled; + VisualServer::get_singleton()->portal_set_enabled(portal,enabled); +} + +bool Portal::is_enabled() const { + + + return enabled; +} + +void Portal::set_disable_distance(float p_distance) { + + disable_distance=p_distance; + VisualServer::get_singleton()->portal_set_disable_distance(portal,disable_distance); + +} +float Portal::get_disable_distance() const { + + + return disable_distance; +} + +void Portal::set_disabled_color(const Color& p_disabled_color) { + + disabled_color=p_disabled_color; + VisualServer::get_singleton()->portal_set_disabled_color(portal,disabled_color); +} + +Color Portal::get_disabled_color() const { + + return disabled_color; +} + +void Portal::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("set_shape","points"),&Portal::set_shape); + ObjectTypeDB::bind_method(_MD("get_shape"),&Portal::get_shape); + + ObjectTypeDB::bind_method(_MD("set_enabled","enable"),&Portal::set_enabled); + ObjectTypeDB::bind_method(_MD("is_enabled"),&Portal::is_enabled); + + ObjectTypeDB::bind_method(_MD("set_disable_distance","distance"),&Portal::set_disable_distance); + ObjectTypeDB::bind_method(_MD("get_disable_distance"),&Portal::get_disable_distance); + + ObjectTypeDB::bind_method(_MD("set_disabled_color","color"),&Portal::set_disabled_color); + ObjectTypeDB::bind_method(_MD("get_disabled_color"),&Portal::get_disabled_color); + + ObjectTypeDB::bind_method(_MD("set_connect_range","range"),&Portal::set_connect_range); + ObjectTypeDB::bind_method(_MD("get_connect_range"),&Portal::get_connect_range); + +} + +Portal::Portal() { + + portal = VisualServer::get_singleton()->portal_create(); + Vector< Point2 > points; + points.push_back( Point2( -1, 1 ) ); + points.push_back( Point2( 1, 1 ) ); + points.push_back( Point2( 1, -1 ) ); + points.push_back( Point2( -1, -1 ) ); + set_shape(points); // default shape + + + set_connect_range(0.8); + set_disable_distance(50); + set_enabled(true); + + set_base(portal); + + +} + + +Portal::~Portal() { + + VisualServer::get_singleton()->free(portal); +} + + diff --git a/scene/3d/portal.h b/scene/3d/portal.h new file mode 100644 index 00000000000..69057244991 --- /dev/null +++ b/scene/3d/portal.h @@ -0,0 +1,93 @@ +/*************************************************************************/ +/* portal.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 PORTAL_H +#define PORTAL_H + +#include "scene/3d/visual_instance.h" +/** + @author Juan Linietsky +*/ + + +/* Portal Logic: + If a portal is placed next (very close to) a similar, opposing portal, they automatically connect, + otherwise, a portal connects to the parent room +*/ + + + +class Portal : public VisualInstance { + + OBJ_TYPE(Portal, VisualInstance); + + RID portal; + + bool enabled; + float disable_distance; + Color disabled_color; + float connect_range; + + AABB aabb; + + virtual RES _get_gizmo_geometry() const; + +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 *p_list) const; + + static void _bind_methods(); + +public: + + virtual AABB get_aabb() const; + virtual DVector get_faces(uint32_t p_usage_flags) const; + + void set_enabled(bool p_enabled); + bool is_enabled() const; + + void set_disable_distance(float p_distance); + float get_disable_distance() const; + + void set_disabled_color(const Color& p_disabled_color); + Color get_disabled_color() const; + + void set_shape(const Vector& p_shape); + Vector get_shape() const; + + void set_connect_range(float p_range); + float get_connect_range() const; + + Portal(); + ~Portal(); + +}; + +#endif diff --git a/scene/3d/position_3d.cpp b/scene/3d/position_3d.cpp new file mode 100644 index 00000000000..19bf1c4e1d3 --- /dev/null +++ b/scene/3d/position_3d.cpp @@ -0,0 +1,66 @@ +/*************************************************************************/ +/* position_3d.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 "position_3d.h" +#include "scene/resources/mesh.h" + +RES Position3D::_get_gizmo_geometry() const { + + + Ref mesh = memnew( Mesh ); + + DVector cursor_points; + DVector cursor_colors; + float cs = 0.25; + cursor_points.push_back(Vector3(+cs,0,0)); + cursor_points.push_back(Vector3(-cs,0,0)); + cursor_points.push_back(Vector3(0,+cs,0)); + cursor_points.push_back(Vector3(0,-cs,0)); + cursor_points.push_back(Vector3(0,0,+cs)); + cursor_points.push_back(Vector3(0,0,-cs)); + cursor_colors.push_back(Color(1,0.5,0.5,1)); + cursor_colors.push_back(Color(1,0.5,0.5,1)); + cursor_colors.push_back(Color(0.5,1,0.5,1)); + cursor_colors.push_back(Color(0.5,1,0.5,1)); + cursor_colors.push_back(Color(0.5,0.5,1,1)); + cursor_colors.push_back(Color(0.5,0.5,1,1)); + + Ref mat = memnew( FixedMaterial ); + mat->set_flag(Material::FLAG_UNSHADED,true); + mat->set_line_width(3); + Array d; + d[Mesh::ARRAY_VERTEX]=cursor_points; + d[Mesh::ARRAY_COLOR]=cursor_colors; + mesh->add_surface(Mesh::PRIMITIVE_LINES,d); + mesh->surface_set_material(0,mat); + return mesh; +} + +Position3D::Position3D() +{ +} diff --git a/scene/3d/position_3d.h b/scene/3d/position_3d.h new file mode 100644 index 00000000000..468b9aafeb8 --- /dev/null +++ b/scene/3d/position_3d.h @@ -0,0 +1,45 @@ +/*************************************************************************/ +/* position_3d.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 POSITION_3D_H +#define POSITION_3D_H + +#include "scene/3d/spatial.h" + +class Position3D : public Spatial { + + OBJ_TYPE(Position3D,Spatial); + + virtual RES _get_gizmo_geometry() const; + +public: + + Position3D(); +}; + +#endif // POSITION_3D_H diff --git a/scene/3d/proximity_group.cpp b/scene/3d/proximity_group.cpp new file mode 100644 index 00000000000..b3378c76986 --- /dev/null +++ b/scene/3d/proximity_group.cpp @@ -0,0 +1,198 @@ +/*************************************************************************/ +/* proximity_group.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 "proximity_group.h" + +#include "math_funcs.h" + +void ProximityGroup::clear_groups() { + + Map::Element *E; + + { + const int size = 16; + StringName remove_list[size]; + E = groups.front(); + int num = 0; + while (E && num < size) { + + if (E->get() != group_version) { + remove_list[num++] = E->key(); + }; + + E = E->next(); + }; + for (int i=0; i::Element* E = groups.find(p_name); + if (!E) { + add_to_group(p_name); + }; + + groups[p_name] = group_version; +}; + +void ProximityGroup::set_group_name(String p_group_name) { + + group_name = p_group_name; +}; + +void ProximityGroup::_notification(int what) { + + switch (what) { + + case NOTIFICATION_EXIT_SCENE: + ++group_version; + clear_groups(); + break; + case NOTIFICATION_TRANSFORM_CHANGED: + update_groups(); + break; + }; +}; + +void ProximityGroup::broadcast(String p_name, Variant p_params) { + + Map::Element *E; + E = groups.front(); + while (E) { + + get_scene()->call_group(SceneMainLoop::GROUP_CALL_DEFAULT, E->key(), "_proximity_group_broadcast", p_name, p_params); + E = E->next(); + }; + +}; + +void ProximityGroup::_proximity_group_broadcast(String p_name, Variant p_params) { + + if (dispatch_mode == MODE_PROXY) { + + get_parent()->call(p_name, p_params); + } else { + + emit_signal("broadcast", p_name, p_params); + }; +}; + + +void ProximityGroup::set_dispatch_mode(int p_mode) { + + dispatch_mode = (DispatchMode)p_mode; +}; + +void ProximityGroup::set_grid_radius(const Vector3& p_radius) { + + grid_radius = p_radius; +}; + +Vector3 ProximityGroup::get_grid_radius() const { + + return grid_radius; +}; + + +void ProximityGroup::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("set_group_name","name"), &ProximityGroup::set_group_name); + ObjectTypeDB::bind_method(_MD("broadcast","name", "parameters"), &ProximityGroup::broadcast); + ObjectTypeDB::bind_method(_MD("set_dispatch_mode","mode"), &ProximityGroup::set_dispatch_mode); + ObjectTypeDB::bind_method(_MD("_proximity_group_broadcast","name","params"), &ProximityGroup::_proximity_group_broadcast); + ObjectTypeDB::bind_method(_MD("set_grid_radius","radius"), &ProximityGroup::set_grid_radius); + ObjectTypeDB::bind_method(_MD("get_grid_radius"), &ProximityGroup::get_grid_radius); + + ADD_PROPERTY( PropertyInfo( Variant::VECTOR3, "grid_radius"), _SCS("set_grid_radius"), _SCS("get_grid_radius")); + + ADD_SIGNAL( MethodInfo("broadcast", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::ARRAY, "parameters")) ); +}; + + +ProximityGroup::ProximityGroup() { + + group_version = 0; + dispatch_mode = MODE_PROXY; + + grid_radius = Vector3(1, 1, 1); + +}; + +ProximityGroup::~ProximityGroup() { + +}; diff --git a/scene/3d/proximity_group.h b/scene/3d/proximity_group.h new file mode 100644 index 00000000000..c8660c17dd4 --- /dev/null +++ b/scene/3d/proximity_group.h @@ -0,0 +1,81 @@ +/*************************************************************************/ +/* proximity_group.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 PROXIMITY_GROUP_H +#define PROXIMITY_GROUP_H + +#include "spatial.h" + +class ProximityGroup : public Spatial { + + OBJ_TYPE( ProximityGroup, Spatial ); + OBJ_CATEGORY("3D"); + +public: + enum DispatchMode { + MODE_PROXY, + MODE_SIGNAL, + }; + +public: + void clear_groups(); + void update_groups(); + + void _notification(int p_what); + + DispatchMode dispatch_mode; + + Map groups; + String group_name; + + float cell_size; + Vector3 grid_radius; + uint32_t group_version; + + void add_groups(int* p_cell, String p_base, int p_depth); + void _new_group(StringName p_name); + + void _proximity_group_broadcast(String p_name, Variant p_params); + + static void _bind_methods(); + +public: + + void set_group_name(String p_group_name); + void broadcast(String p_name, Variant p_params); + void set_dispatch_mode(int p_mode); + + void set_grid_radius(const Vector3& p_radius); + Vector3 get_grid_radius() const; + + ProximityGroup(); + ~ProximityGroup(); +}; + +#endif + diff --git a/scene/3d/quad.cpp b/scene/3d/quad.cpp new file mode 100644 index 00000000000..78dd56141cc --- /dev/null +++ b/scene/3d/quad.cpp @@ -0,0 +1,232 @@ +/*************************************************************************/ +/* quad.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 "quad.h" +#include "servers/visual_server.h" + +void Quad::_update() { + + if (!is_inside_scene()) + return; + + Vector3 normal; + normal[axis]=1.0; + + const int axis_order_1[3]={1,2,0}; + const int axis_order_2[3]={2,0,1}; + const int a1=axis_order_1[axis]; + const int a2=axis_order_2[axis]; + + + + DVector points; + points.resize(4); + DVector::Write pointsw = points.write(); + + Vector2 s2 = size*0.5; + Vector2 o = offset; + if (!centered) + o+=s2; + + pointsw[0][a1]=-s2.x+offset.x; + pointsw[0][a2]=s2.y+offset.y; + + pointsw[1][a1]=s2.x+offset.x; + pointsw[1][a2]=s2.y+offset.y; + + pointsw[2][a1]=s2.x+offset.x; + pointsw[2][a2]=-s2.y+offset.y; + + pointsw[3][a1]=-s2.x+offset.x; + pointsw[3][a2]=-s2.y+offset.y; + + + aabb=AABB(pointsw[0],Vector3()); + for(int i=1;i<4;i++) + aabb.expand_to(pointsw[i]); + + pointsw = DVector::Write(); + + DVector normals; + normals.resize(4); + DVector::Write normalsw = normals.write(); + + for(int i=0;i<4;i++) + normalsw[i]=normal; + + normalsw=DVector::Write(); + + + + DVector uvs; + uvs.resize(4); + DVector::Write uvsw = uvs.write(); + + uvsw[0]=Vector2(0,0); + uvsw[1]=Vector2(1,0); + uvsw[2]=Vector2(1,1); + uvsw[3]=Vector2(0,1); + + uvsw = DVector::Write(); + + DVector indices; + indices.resize(6); + + DVector::Write indicesw = indices.write(); + indicesw[0]=0; + indicesw[1]=1; + indicesw[2]=2; + indicesw[3]=2; + indicesw[4]=3; + indicesw[5]=0; + + indicesw=DVector::Write(); + + Array arr; + arr.resize(VS::ARRAY_MAX); + arr[VS::ARRAY_VERTEX]=points; + arr[VS::ARRAY_NORMAL]=normals; + arr[VS::ARRAY_TEX_UV]=uvs; + arr[VS::ARRAY_INDEX]=indices; + + + if (configured) { + VS::get_singleton()->mesh_remove_surface(mesh,0); + } else { + configured=true; + } + VS::get_singleton()->mesh_add_surface(mesh,VS::PRIMITIVE_TRIANGLES,arr); + + pending_update=false; +} + + +void Quad::set_axis(Vector3::Axis p_axis) { + + axis=p_axis; + _update(); +} + +Vector3::Axis Quad::get_axis() const{ + + return axis; +} + +void Quad::set_size(const Vector2& p_size){ + + size=p_size; + _update(); +} +Vector2 Quad::get_size() const{ + + return size; +} + +void Quad::set_offset(const Vector2& p_offset){ + + offset=p_offset; + _update(); +} +Vector2 Quad::get_offset() const{ + + return offset; +} + +void Quad::set_centered(bool p_enabled){ + + centered=p_enabled; + _update(); +} +bool Quad::is_centered() const{ + + return centered; +} + +void Quad::_notification(int p_what) { + + switch(p_what) { + + case NOTIFICATION_ENTER_SCENE: { + + if (pending_update) + _update(); + + + } break; + case NOTIFICATION_EXIT_SCENE: { + + pending_update=true; + + + } break; + } +} + +DVector Quad::get_faces(uint32_t p_usage_flags) const { + + return DVector(); +} + +AABB Quad::get_aabb() const { + + return aabb; +} + +void Quad::_bind_methods(){ + + ObjectTypeDB::bind_method(_MD("set_axis","axis"),&Quad::set_axis); + ObjectTypeDB::bind_method(_MD("get_axis"),&Quad::get_axis); + + ObjectTypeDB::bind_method(_MD("set_size","size"),&Quad::set_size); + ObjectTypeDB::bind_method(_MD("get_size"),&Quad::get_size); + + ObjectTypeDB::bind_method(_MD("set_centered","centered"),&Quad::set_centered); + ObjectTypeDB::bind_method(_MD("is_centered"),&Quad::is_centered); + + ObjectTypeDB::bind_method(_MD("set_offset","offset"),&Quad::set_offset); + ObjectTypeDB::bind_method(_MD("get_offset"),&Quad::get_offset); + + ADD_PROPERTY( PropertyInfo( Variant::INT, "quad/axis", PROPERTY_HINT_ENUM,"X,Y,Z" ), _SCS("set_axis"), _SCS("get_axis")); + ADD_PROPERTY( PropertyInfo( Variant::VECTOR2, "quad/size" ), _SCS("set_size"), _SCS("get_size")); + ADD_PROPERTY( PropertyInfo( Variant::VECTOR2, "quad/offset" ), _SCS("set_offset"), _SCS("get_offset")); + ADD_PROPERTY( PropertyInfo( Variant::BOOL, "quad/centered" ), _SCS("set_centered"), _SCS("is_centered")); + +} + +Quad::Quad() { + + pending_update=true; + centered=true; + //offset=0; + size=Vector2(1,1); + axis=Vector3::AXIS_Z; + mesh=VisualServer::get_singleton()->mesh_create(); + set_base(mesh); + configured=false; + +} diff --git a/scene/3d/quad.h b/scene/3d/quad.h new file mode 100644 index 00000000000..7d1b4f5dc46 --- /dev/null +++ b/scene/3d/quad.h @@ -0,0 +1,76 @@ +/*************************************************************************/ +/* quad.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 QUAD_H +#define QUAD_H + + +#include "scene/3d/visual_instance.h" +#include "rid.h" + +class Quad : public GeometryInstance { + + OBJ_TYPE(Quad,GeometryInstance); + + Vector3::Axis axis; + bool centered; + Vector2 offset; + Vector2 size; + + AABB aabb; + bool configured; + bool pending_update; + RID mesh; + + void _update(); + +protected: + + void _notification(int p_what); + static void _bind_methods(); +public: + + void set_axis(Vector3::Axis p_axis); + Vector3::Axis get_axis() const; + + void set_size(const Vector2& p_sizze); + Vector2 get_size() const; + + void set_offset(const Vector2& p_offset); + Vector2 get_offset() const; + + void set_centered(bool p_enabled); + bool is_centered() const; + + virtual DVector get_faces(uint32_t p_usage_flags) const; + virtual AABB get_aabb() const; + + Quad(); +}; + +#endif // QUAD_H diff --git a/scene/3d/ray_cast.cpp b/scene/3d/ray_cast.cpp new file mode 100644 index 00000000000..e36e2da14f6 --- /dev/null +++ b/scene/3d/ray_cast.cpp @@ -0,0 +1,190 @@ +/*************************************************************************/ +/* ray_cast.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 "ray_cast.h" + +#include "servers/physics_server.h" +#include "collision_object.h" +void RayCast::set_cast_to(const Vector3& p_point) { + + cast_to=p_point; + if (is_inside_scene() && get_scene()->is_editor_hint()) + update_gizmo(); + +} + +Vector3 RayCast::get_cast_to() const{ + + return cast_to; +} + +bool RayCast::is_colliding() const{ + + return collided; +} +Object *RayCast::get_collider() const{ + + if (against==0) + return NULL; + + return ObjectDB::get_instance(against); +} + +int RayCast::get_collider_shape() const { + + return against_shape; +} +Vector3 RayCast::get_collision_point() const{ + + return collision_point; +} +Vector3 RayCast::get_collision_normal() const{ + + return collision_normal; +} + + +void RayCast::set_enabled(bool p_enabled) { + + enabled=p_enabled; + if (is_inside_scene() && !get_scene()->is_editor_hint()) + set_fixed_process(p_enabled); + if (!p_enabled) + collided=false; + +} + + +bool RayCast::is_enabled() const { + + + return enabled; +} + + +void RayCast::_notification(int p_what) { + + switch(p_what) { + + case NOTIFICATION_ENTER_SCENE: { + + if (enabled && !get_scene()->is_editor_hint()) { + set_fixed_process(true); + Node *p = get_parent(); + while( p && p->cast_to() ) { + + CollisionObject *co = p->cast_to(); + if (co) { + + exception=co->get_rid(); + exceptions.insert(exception); + } + + p=p->get_parent(); + } + } else + set_fixed_process(false); + + + + } break; + case NOTIFICATION_EXIT_SCENE: { + + if (enabled) { + set_fixed_process(false); + } + + exceptions.erase(exception); + + } break; + case NOTIFICATION_FIXED_PROCESS: { + + if (!enabled) + break; + + + + Ref w3d = get_world(); + ERR_BREAK( w3d.is_null() ); + + PhysicsDirectSpaceState *dss = PhysicsServer::get_singleton()->space_get_direct_state(w3d->get_space()); + ERR_BREAK( !dss ); + + Transform gt = get_global_transform(); + + Vector3 to = cast_to; + if (to==Vector3()) + to=Vector3(0,0.01,0); + + PhysicsDirectSpaceState::RayResult rr; + + if (dss->intersect_ray(gt.get_origin(),gt.xform(to),rr,exceptions)) { + + collided=true; + against=rr.collider_id; + collision_point=rr.position; + collision_normal=rr.normal; + against_shape=rr.shape; + } else { + collided=false; + } + + + + } break; + } +} + +void RayCast::_bind_methods() { + + + ObjectTypeDB::bind_method(_MD("set_enabled","enabled"),&RayCast::set_enabled); + ObjectTypeDB::bind_method(_MD("is_enabled"),&RayCast::is_enabled); + + ObjectTypeDB::bind_method(_MD("set_cast_to","local_point"),&RayCast::set_cast_to); + ObjectTypeDB::bind_method(_MD("get_cast_to"),&RayCast::get_cast_to); + + ObjectTypeDB::bind_method(_MD("is_colliding"),&RayCast::is_colliding); + + ObjectTypeDB::bind_method(_MD("get_collider"),&RayCast::get_collider); + ObjectTypeDB::bind_method(_MD("get_collider_shape"),&RayCast::get_collider_shape); + ObjectTypeDB::bind_method(_MD("get_collision_point"),&RayCast::get_collision_point); + ObjectTypeDB::bind_method(_MD("get_collision_normal"),&RayCast::get_collision_normal); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL,"enabled"),_SCS("set_enabled"),_SCS("is_enabled")); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3,"cast_to"),_SCS("set_cast_to"),_SCS("get_cast_to")); +} + +RayCast::RayCast() { + + enabled=false; + against=0; + collided=false; + against_shape=0; + cast_to=Vector3(0,-1,0); +} diff --git a/scene/3d/ray_cast.h b/scene/3d/ray_cast.h new file mode 100644 index 00000000000..96606b1628a --- /dev/null +++ b/scene/3d/ray_cast.h @@ -0,0 +1,72 @@ +/*************************************************************************/ +/* ray_cast.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 RAY_CAST_H +#define RAY_CAST_H + +#include "scene/3d/spatial.h" + +class RayCast : public Spatial { + + OBJ_TYPE(RayCast,Spatial); + + + bool enabled; + bool collided; + ObjectID against; + int against_shape; + Vector3 collision_point; + Vector3 collision_normal; + + Vector3 cast_to; + + RID exception; + Set exceptions; + +protected: + + void _notification(int p_what); + static void _bind_methods(); +public: + + void set_enabled(bool p_enabled); + bool is_enabled() const; + + void set_cast_to(const Vector3& p_point); + Vector3 get_cast_to() const; + + bool is_colliding() const; + Object *get_collider() const; + int get_collider_shape() const; + Vector3 get_collision_point() const; + Vector3 get_collision_normal() const; + + RayCast(); +}; + +#endif // RAY_CAST_H diff --git a/scene/3d/room_instance.cpp b/scene/3d/room_instance.cpp new file mode 100644 index 00000000000..0f390c15af3 --- /dev/null +++ b/scene/3d/room_instance.cpp @@ -0,0 +1,299 @@ +/*************************************************************************/ +/* room_instance.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 "room_instance.h" + +#include "servers/visual_server.h" + +#include "geometry.h" +#include "globals.h" +#include "scene/resources/surface_tool.h" + + +void Room::_notification(int p_what) { + + switch(p_what) { + case NOTIFICATION_ENTER_WORLD: { + // go find parent level + Node *parent_room=get_parent(); + level=0; + + while(parent_room) { + + Room *r = parent_room->cast_to(); + if (r) { + + level=r->level+1; + break; + } + + parent_room=parent_room->get_parent(); + } + + + if (sound_enabled) + SpatialSoundServer::get_singleton()->room_set_space(sound_room,get_world()->get_sound_space()); + + } break; + case NOTIFICATION_TRANSFORM_CHANGED: { + SpatialSoundServer::get_singleton()->room_set_transform(sound_room,get_global_transform()); + } break; + case NOTIFICATION_EXIT_WORLD: { + + if (sound_enabled) + SpatialSoundServer::get_singleton()->room_set_space(sound_room,RID()); + + + } break; + } + +} + + +RES Room::_get_gizmo_geometry() const { + + DVector faces; + if (!room.is_null()) + faces=room->get_geometry_hint(); + + int count=faces.size(); + if (count==0) + return RES(); + + DVector::Read facesr=faces.read(); + + const Face3* facesptr=facesr.ptr(); + + DVector points; + + Ref surface_tool( memnew( SurfaceTool )); + + Ref mat( memnew( FixedMaterial )); + + mat->set_parameter( FixedMaterial::PARAM_DIFFUSE,Color(0.2,0.8,0.9,0.3) ); + mat->set_line_width(4); + mat->set_flag(Material::FLAG_DOUBLE_SIDED,true); + mat->set_flag(Material::FLAG_UNSHADED,true); + mat->set_hint(Material::HINT_NO_DEPTH_DRAW,true); + + surface_tool->begin(Mesh::PRIMITIVE_LINES); + surface_tool->set_material(mat); + + for (int i=0;iadd_vertex(facesptr[i].vertex[0]); + surface_tool->add_vertex(facesptr[i].vertex[1]); + + surface_tool->add_vertex(facesptr[i].vertex[1]); + surface_tool->add_vertex(facesptr[i].vertex[2]); + + surface_tool->add_vertex(facesptr[i].vertex[2]); + surface_tool->add_vertex(facesptr[i].vertex[0]); + + } + + return surface_tool->commit(); +} + + + +AABB Room::get_aabb() const { + + if (room.is_null()) + return AABB(); + + return room->get_bounds().get_aabb(); +} +DVector Room::get_faces(uint32_t p_usage_flags) const { + + return DVector(); + +} + +void Room::set_room( const Ref& p_room ) { + + room=p_room; + update_gizmo(); + + if (room.is_valid()) { + + set_base(room->get_rid()); + } else { + set_base(RID()); + } + + if (!is_inside_scene()) + return; + + + propagate_notification(NOTIFICATION_AREA_CHANGED); + update_gizmo(); + + if (room.is_valid()) + SpatialSoundServer::get_singleton()->room_set_bounds(sound_room,room->get_bounds()); + + +} + +Ref Room::get_room() const { + + return room; +} + +void Room::_parse_node_faces(DVector &all_faces,const Node *p_node) const { + + const VisualInstance *vi=p_node->cast_to(); + + if (vi) { + DVector faces=vi->get_faces(FACES_ENCLOSING); + + if (faces.size()) { + int old_len=all_faces.size(); + all_faces.resize( all_faces.size() + faces.size() ); + int new_len=all_faces.size(); + DVector::Write all_facesw=all_faces.write(); + Face3 * all_facesptr=all_facesw.ptr(); + + DVector::Read facesr=faces.read(); + const Face3 * facesptr=facesr.ptr(); + + Transform tr=vi->get_relative_transform(this); + + for(int i=old_len;iget_child_count();i++) { + + _parse_node_faces(all_faces,p_node->get_child(i)); + } + +} + +void Room::compute_room_from_subtree() { + + + DVector all_faces; + _parse_node_faces(all_faces,this); + + + if (all_faces.size()==0) + return; + float error; + DVector wrapped_faces = Geometry::wrap_geometry(all_faces,&error); + + + if (wrapped_faces.size()==0) + return; + + BSP_Tree tree(wrapped_faces,error); + + Ref room( memnew( RoomBounds ) ); + room->set_bounds(tree); + room->set_geometry_hint(wrapped_faces); + + set_room(room); + +} + + + +void Room::set_simulate_acoustics(bool p_enable) { + + if (sound_enabled==p_enable) + return; + + sound_enabled=p_enable; + if (!is_inside_world()) + return; //nothing to do + + if (sound_enabled) + SpatialSoundServer::get_singleton()->room_set_space(sound_room,get_world()->get_sound_space()); + else + SpatialSoundServer::get_singleton()->room_set_space(sound_room,RID()); + + +} + +void Room::_bounds_changed() { + + update_gizmo(); +} + +bool Room::is_simulating_acoustics() const { + + return sound_enabled; +} + + + +RID Room::get_sound_room() const { + + return RID(); +} + +void Room::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("set_room","room:Room"),&Room::set_room ); + ObjectTypeDB::bind_method(_MD("get_room:Room"),&Room::get_room ); + ObjectTypeDB::bind_method(_MD("compute_room_from_subtree"),&Room::compute_room_from_subtree); + + + + ObjectTypeDB::bind_method(_MD("set_simulate_acoustics","enable"),&Room::set_simulate_acoustics ); + ObjectTypeDB::bind_method(_MD("is_simulating_acoustics"),&Room::is_simulating_acoustics ); + + + + ADD_PROPERTY( PropertyInfo( Variant::OBJECT, "room/room", PROPERTY_HINT_RESOURCE_TYPE, "Area" ), _SCS("set_room"), _SCS("get_room") ); + ADD_PROPERTY( PropertyInfo( Variant::BOOL, "room/simulate_acoustics"), _SCS("set_simulate_acoustics"), _SCS("is_simulating_acoustics") ); +} + + +Room::Room() { + + sound_enabled=false; + sound_room=SpatialSoundServer::get_singleton()->room_create(); + + level=0; + +} + + +Room::~Room() { + + SpatialSoundServer::get_singleton()->free(sound_room); +} + diff --git a/scene/3d/room_instance.h b/scene/3d/room_instance.h new file mode 100644 index 00000000000..1d11630cef7 --- /dev/null +++ b/scene/3d/room_instance.h @@ -0,0 +1,102 @@ +/*************************************************************************/ +/* room_instance.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 ROOM_INSTANCE_H +#define ROOM_INSTANCE_H + +#include "scene/3d/visual_instance.h" +#include "scene/resources/room.h" +#include "servers/spatial_sound_server.h" +/** + @author Juan Linietsky +*/ + + +/* RoomInstance Logic: + a) Instances that belong to the room are drawn only if the room is visible (seen through portal, or player inside) + b) Instances that don't belong to any room are considered to belong to the root room (RID empty) + c) "dynamic" Instances are assigned to the rooms their AABB touch + +*/ + + +class Room : public VisualInstance { + + OBJ_TYPE( Room, VisualInstance ); +public: + + + + +private: + Ref room; + + RID sound_room; + + bool sound_enabled; + + int level; + void _parse_node_faces(DVector &all_faces,const Node *p_node) const; + + + void _bounds_changed(); + virtual RES _get_gizmo_geometry() const; + +protected: + + void _notification(int p_what); + + static void _bind_methods(); + +public: + + enum { + // used to notify portals that the room in which they are has changed. + NOTIFICATION_AREA_CHANGED=60 + }; + + virtual AABB get_aabb() const; + virtual DVector get_faces(uint32_t p_usage_flags) const; + + void set_room( const Ref& p_room ); + Ref get_room() const; + + void set_simulate_acoustics(bool p_enable); + bool is_simulating_acoustics() const; + + void compute_room_from_subtree(); + + RID get_sound_room() const; + + Room(); + ~Room(); + +}; + + +#endif // ROOM_INSTANCE_H diff --git a/scene/3d/scenario_fx.cpp b/scene/3d/scenario_fx.cpp new file mode 100644 index 00000000000..05546ee4db2 --- /dev/null +++ b/scene/3d/scenario_fx.cpp @@ -0,0 +1,74 @@ +/*************************************************************************/ +/* scenario_fx.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 "scenario_fx.h" + + + +void WorldEnvironment::_notification(int p_what) { + + + if (p_what==NOTIFICATION_ENTER_WORLD) { + + get_world()->set_environment(environment); + } else if (p_what==NOTIFICATION_EXIT_WORLD) { + + get_world()->set_environment(Ref()); + } +} + +void WorldEnvironment::set_environment(const Ref& p_environment) { + + environment=p_environment; + if (is_inside_world()) { + get_world()->set_environment(environment); + } + +} + +Ref WorldEnvironment::get_environment() const { + + return environment; +} + + +void WorldEnvironment::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("set_environment","env:Environment"),&WorldEnvironment::set_environment); + ObjectTypeDB::bind_method(_MD("get_environment:Environment"),&WorldEnvironment::get_environment); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT,"environment",PROPERTY_HINT_RESOURCE_TYPE,"Environment"),_SCS("set_environment"),_SCS("get_environment")); + +} + + +WorldEnvironment::WorldEnvironment() { + + +} + + diff --git a/scene/3d/scenario_fx.h b/scene/3d/scenario_fx.h new file mode 100644 index 00000000000..b8b06ea9834 --- /dev/null +++ b/scene/3d/scenario_fx.h @@ -0,0 +1,58 @@ +/*************************************************************************/ +/* scenario_fx.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 SCENARIO_FX_H +#define SCENARIO_FX_H + +#include "scene/3d/spatial.h" + + +/** + @author Juan Linietsky +*/ + + +class WorldEnvironment : public Spatial { + + OBJ_TYPE(WorldEnvironment,Spatial ); + + Ref environment; + +protected: + + void _notification(int p_what); + static void _bind_methods(); +public: + void set_environment(const Ref& p_environment); + Ref get_environment() const; + + WorldEnvironment(); + +}; + +#endif diff --git a/scene/3d/skeleton.cpp b/scene/3d/skeleton.cpp new file mode 100644 index 00000000000..6ec23f96fb4 --- /dev/null +++ b/scene/3d/skeleton.cpp @@ -0,0 +1,536 @@ +/*************************************************************************/ +/* skeleton.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 "skeleton.h" + +#include "message_queue.h" + +#include "scene/resources/surface_tool.h" +#include "core/globals.h" + + +bool Skeleton::_set(const StringName& p_path, const Variant& p_value) { + + String path = p_path; + + if (!path.begins_with("bones/")) + return false; + + int which=path.get_slice("/",1).to_int(); + String what=path.get_slice("/",2); + + if (which==bones.size() && what=="name") { + + add_bone(p_value); + return true; + } + + ERR_FAIL_INDEX_V( which, bones.size(), false ); + + if (what=="parent") + set_bone_parent(which, p_value ); + else if (what=="rest") + set_bone_rest(which, p_value); + else if (what=="enabled") + set_bone_enabled(which, p_value); + else if (what=="pose") + set_bone_pose(which, p_value); + else if (what=="bound_childs") { + Array children=p_value; + + bones[which].nodes_bound.clear(); + + for (int i=0;i::Element *E=bones[which].nodes_bound.front();E;E=E->next()) { + + Object *obj=ObjectDB::get_instance(E->get()); + ERR_CONTINUE(!obj); + Node *node=obj->cast_to(); + ERR_CONTINUE(!node); + NodePath path=get_path_to(node); + children.push_back(path); + + } + + r_ret=children; + } else + return false; + + return true; + +} +void Skeleton::_get_property_list( List* p_list ) const { + + for (int i=0;ipush_back( PropertyInfo( Variant::STRING, prep+"name" ) ); + p_list->push_back( PropertyInfo( Variant::INT, prep+"parent" , PROPERTY_HINT_RANGE,"-1,"+itos(i-1)+",1") ); + p_list->push_back( PropertyInfo( Variant::TRANSFORM, prep+"rest" ) ); + p_list->push_back( PropertyInfo( Variant::BOOL, prep+"enabled" ) ); + p_list->push_back( PropertyInfo( Variant::TRANSFORM, prep+"pose", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR ) ); + p_list->push_back( PropertyInfo( Variant::ARRAY, prep+"bound_childs" ) ); + } +} + +void Skeleton::_notification(int p_what) { + + switch(p_what) { + + case NOTIFICATION_ENTER_WORLD: { + + if (dirty) { + + dirty=false; + _make_dirty(); // property make it dirty + } + + } break; + case NOTIFICATION_EXIT_WORLD: { + + } break; + case NOTIFICATION_UPDATE_SKELETON: { + + + VisualServer *vs=VisualServer::get_singleton(); + Bone *bonesptr=&bones[0]; + int len=bones.size(); + + vs->skeleton_resize( skeleton, len ); // if same size, nothin really happens + + // pose changed, rebuild cache of inverses + if (rest_global_inverse_dirty) { + + // calculate global rests and invert them + for (int i=0;i=0) + b.rest_global_inverse=bonesptr[b.parent].rest_global_inverse * b.rest; + else + b.rest_global_inverse=b.rest; + } + for (int i=0;i=0) { + + b.pose_global=bonesptr[b.parent].pose_global * (b.rest * pose); + } else { + + b.pose_global=b.rest * pose; + } + } else { + + if (b.parent>=0) { + + b.pose_global=bonesptr[b.parent].pose_global * b.rest; + } else { + + b.pose_global=b.rest; + } + } + + vs->skeleton_bone_set_transform( skeleton, i, b.pose_global * b.rest_global_inverse ); + + for(List::Element *E=b.nodes_bound.front();E;E=E->next()) { + + Object *obj=ObjectDB::get_instance(E->get()); + ERR_CONTINUE(!obj); + Spatial *sp = obj->cast_to(); + ERR_CONTINUE(!sp); + sp->set_transform(b.pose_global * b.rest_global_inverse); + } + } + + dirty=false; + } break; + } +} + +Transform Skeleton::get_bone_transform(int p_bone) const { + ERR_FAIL_INDEX_V(p_bone,bones.size(),Transform()); + if (dirty) + const_cast(this)->notification(NOTIFICATION_UPDATE_SKELETON); + return bones[p_bone].pose_global * bones[p_bone].rest_global_inverse; +} + +RID Skeleton::get_skeleton() const { + + return skeleton; +} + +// skeleton creation api +void Skeleton::add_bone(const String& p_name) { + + ERR_FAIL_COND( p_name=="" || p_name.find(":")!=-1 || p_name.find("/")!=-1 ); + + for (int i=0;i=p_bone)); + + bones[p_bone].parent=p_parent; + rest_global_inverse_dirty=true; + _make_dirty(); +} + +int Skeleton::get_bone_parent(int p_bone) const { + + ERR_FAIL_INDEX_V( p_bone, bones.size(), -1 ); + + return bones[p_bone].parent; +} + +void Skeleton::set_bone_rest(int p_bone, const Transform& p_rest) { + + ERR_FAIL_INDEX( p_bone, bones.size() ); + + bones[p_bone].rest=p_rest; + rest_global_inverse_dirty=true; + _make_dirty(); + +} +Transform Skeleton::get_bone_rest(int p_bone) const { + + ERR_FAIL_INDEX_V( p_bone, bones.size(), Transform() ); + + return bones[p_bone].rest; + +} + +void Skeleton::set_bone_enabled(int p_bone, bool p_enabled) { + + ERR_FAIL_INDEX( p_bone, bones.size() ); + + bones[p_bone].enabled=p_enabled; + rest_global_inverse_dirty=true; + _make_dirty(); +} +bool Skeleton::is_bone_enabled(int p_bone) const { + + ERR_FAIL_INDEX_V( p_bone, bones.size(), false ); + return bones[p_bone].enabled; + +} + +void Skeleton::bind_child_node_to_bone(int p_bone,Node *p_node) { + + ERR_FAIL_NULL(p_node); + ERR_FAIL_INDEX( p_bone, bones.size() ); + + uint32_t id=p_node->get_instance_ID(); + + for (List::Element *E=bones[p_bone].nodes_bound.front();E;E=E->next()) { + + if (E->get()==id) + return; // already here + } + + bones[p_bone].nodes_bound.push_back(id); + +} +void Skeleton::unbind_child_node_from_bone(int p_bone,Node *p_node) { + + ERR_FAIL_NULL(p_node); + ERR_FAIL_INDEX( p_bone, bones.size() ); + + uint32_t id=p_node->get_instance_ID(); + bones[p_bone].nodes_bound.erase(id); + +} +void Skeleton::get_bound_child_nodes_to_bone(int p_bone,List *p_bound) const { + + ERR_FAIL_INDEX( p_bone, bones.size() ); + + for (const List::Element *E=bones[p_bone].nodes_bound.front();E;E=E->next()) { + + Object *obj=ObjectDB::get_instance(E->get()); + ERR_CONTINUE(!obj); + p_bound->push_back(obj->cast_to()); + } + +} + +void Skeleton::clear_bones() { + + bones.clear(); + rest_global_inverse_dirty=true; + _make_dirty(); +} + +// posing api + +void Skeleton::set_bone_pose(int p_bone, const Transform& p_pose) { + + ERR_FAIL_INDEX( p_bone, bones.size() ); + ERR_FAIL_COND( !is_inside_scene() ); + + + bones[p_bone].pose=p_pose; + _make_dirty(); +} +Transform Skeleton::get_bone_pose(int p_bone) const { + + ERR_FAIL_INDEX_V( p_bone, bones.size(), Transform() ); + return bones[p_bone].pose; + +} + +void Skeleton::set_bone_custom_pose(int p_bone, const Transform& p_custom_pose) { + + ERR_FAIL_INDEX( p_bone, bones.size() ); +// ERR_FAIL_COND( !is_inside_scene() ); + + + bones[p_bone].custom_pose_enable=(p_custom_pose!=Transform()); + bones[p_bone].custom_pose=p_custom_pose; + + _make_dirty(); +} + +Transform Skeleton::get_bone_custom_pose(int p_bone) const { + + ERR_FAIL_INDEX_V( p_bone, bones.size(), Transform() ); + return bones[p_bone].custom_pose; + +} + + +void Skeleton::_make_dirty() { + + if (dirty) + return; + + if (!is_inside_scene()) { + dirty=true; + return; + } + MessageQueue::get_singleton()->push_notification( this, NOTIFICATION_UPDATE_SKELETON ); + dirty=true; +} + + +RES Skeleton::_get_gizmo_geometry() const { + + if (!GLOBAL_DEF("debug/draw_skeleton", true)) + return RES(); + + if (bones.size()==0) + return RES(); + + Ref surface_tool( memnew( SurfaceTool )); + + Ref mat( memnew( FixedMaterial )); + + mat->set_parameter( FixedMaterial::PARAM_DIFFUSE,Color(0.6,1.0,0.3,0.1) ); + mat->set_line_width(4); + mat->set_flag(Material::FLAG_DOUBLE_SIDED,true); + mat->set_flag(Material::FLAG_UNSHADED,true); + mat->set_flag(Material::FLAG_ONTOP,true); + mat->set_hint(Material::HINT_NO_DEPTH_DRAW,true); + + surface_tool->begin(Mesh::PRIMITIVE_LINES); + surface_tool->set_material(mat); + + + const Bone *bonesptr=&bones[0]; + int len=bones.size(); + + for (int i=0;iadd_vertex(v1); + surface_tool->add_vertex(v2); + + } + + return surface_tool->commit(); + +} + +void Skeleton::localize_rests() { + + for(int i=bones.size()-1;i>=0;i--) { + + if (bones[i].parent>=0) + set_bone_rest(i,bones[bones[i].parent].rest.affine_inverse() * bones[i].rest); + } +} + + + +void Skeleton::_bind_methods() { + + + + + ObjectTypeDB::bind_method(_MD("add_bone","name"),&Skeleton::add_bone); + ObjectTypeDB::bind_method(_MD("find_bone","name"),&Skeleton::find_bone); + ObjectTypeDB::bind_method(_MD("get_bone_name","bone_idx"),&Skeleton::get_bone_name); + + ObjectTypeDB::bind_method(_MD("get_bone_parent","bone_idx"),&Skeleton::get_bone_parent); + ObjectTypeDB::bind_method(_MD("set_bone_parent","bone_idx","parent_idx"),&Skeleton::set_bone_parent); + + ObjectTypeDB::bind_method(_MD("get_bone_count"),&Skeleton::get_bone_count); + + ObjectTypeDB::bind_method(_MD("get_bone_rest","bone_idx"),&Skeleton::get_bone_rest); + ObjectTypeDB::bind_method(_MD("set_bone_rest","bone_idx","rest"),&Skeleton::set_bone_rest); + + ObjectTypeDB::bind_method(_MD("bind_child_node_to_bone","bone_idx","node:Node"),&Skeleton::bind_child_node_to_bone); + ObjectTypeDB::bind_method(_MD("unbind_child_node_from_bone","bone_idx","node:Node"),&Skeleton::unbind_child_node_from_bone); + ObjectTypeDB::bind_method(_MD("get_bound_child_nodes_to_bone","bone_idx"),&Skeleton::_get_bound_child_nodes_to_bone); + + ObjectTypeDB::bind_method(_MD("clear_bones"),&Skeleton::clear_bones); + + ObjectTypeDB::bind_method(_MD("get_bone_pose","bone_idx"),&Skeleton::get_bone_pose); + ObjectTypeDB::bind_method(_MD("set_bone_pose","bone_idx","pose"),&Skeleton::set_bone_pose); + + ObjectTypeDB::bind_method(_MD("get_bone_custom_pose","bone_idx"),&Skeleton::get_bone_custom_pose); + ObjectTypeDB::bind_method(_MD("set_bone_custom_pose","bone_idx","custom_pose"),&Skeleton::set_bone_custom_pose); + + ObjectTypeDB::bind_method(_MD("get_bone_transform","bone_idx"),&Skeleton::get_bone_transform); + + BIND_CONSTANT( NOTIFICATION_UPDATE_SKELETON ); +} + + + +Skeleton::Skeleton() { + + rest_global_inverse_dirty=true; + dirty=false; + skeleton=VisualServer::get_singleton()->skeleton_create(); +} + + +Skeleton::~Skeleton() { + + VisualServer::get_singleton()->free( skeleton ); +} + + diff --git a/scene/3d/skeleton.h b/scene/3d/skeleton.h new file mode 100644 index 00000000000..c95734fbf14 --- /dev/null +++ b/scene/3d/skeleton.h @@ -0,0 +1,144 @@ +/*************************************************************************/ +/* skeleton.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 SKELETON_H +#define SKELETON_H + +#include "scene/3d/spatial.h" +#include "rid.h" + +/** + @author Juan Linietsky +*/ +class Skeleton : public Spatial { + + OBJ_TYPE( Skeleton, Spatial ); + + struct Bone { + + String name; + + bool enabled; + int parent; + + Transform rest; + Transform rest_global_inverse; + + Transform pose; + Transform pose_global; + + bool custom_pose_enable; + Transform custom_pose; + + List nodes_bound; + + Bone() { parent=-1; enabled=true; custom_pose_enable=false; } + }; + + bool rest_global_inverse_dirty; + + Vector bones; + + RID skeleton; + + void _make_dirty(); + bool dirty; + +//bind helpers + Array _get_bound_child_nodes_to_bone(int p_bone) const { + + Array bound; + List childs; + get_bound_child_nodes_to_bone(p_bone,&childs); + + for (int i=0;i* p_list ) const; + void _notification(int p_what); + static void _bind_methods(); + +public: + + enum { + + NOTIFICATION_UPDATE_SKELETON=50 + }; + + + RID get_skeleton() const; + + // skeleton creation api + void add_bone(const String&p_name); + int find_bone(String p_name) const; + String get_bone_name(int p_bone) const; + + void set_bone_parent(int p_bone, int p_parent); + int get_bone_parent(int p_bone) const; + + int get_bone_count() const; + + void set_bone_rest(int p_bone, const Transform& p_rest); + Transform get_bone_rest(int p_bone) const; + Transform get_bone_transform(int p_bone) const; + + void set_bone_enabled(int p_bone, bool p_enabled); + bool is_bone_enabled(int p_bone) const; + + void bind_child_node_to_bone(int p_bone,Node *p_node); + void unbind_child_node_from_bone(int p_bone,Node *p_node); + void get_bound_child_nodes_to_bone(int p_bone,List *p_bound) const; + + void clear_bones(); + + // posing api + + void set_bone_pose(int p_bone, const Transform& p_pose); + Transform get_bone_pose(int p_bone) const; + + void set_bone_custom_pose(int p_bone, const Transform& p_custom_pose); + Transform get_bone_custom_pose(int p_bone) const; + + void localize_rests(); // used for loaders and tools + + Skeleton(); + ~Skeleton(); + +}; + +#endif diff --git a/scene/3d/spatial.cpp b/scene/3d/spatial.cpp new file mode 100644 index 00000000000..de1fd7e8c01 --- /dev/null +++ b/scene/3d/spatial.cpp @@ -0,0 +1,567 @@ +/*************************************************************************/ +/* spatial.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 "spatial.h" + +#include "scene/main/viewport.h" +#include "message_queue.h" +#include "scene/scene_string_names.h" + +/* + + possible algorithms: + + Algorithm 1: (current) + + definition of invalidation: global is invalid + + 1) If a node sets a LOCAL, it produces an invalidation of everything above + a) If above is invalid, don't keep invalidating upwards + 2) If a node sets a GLOBAL, it is converted to LOCAL (and forces validation of everything pending below) + + drawback: setting/reading globals is useful and used very very often, and using affine inverses is slow + +--- + + Algorithm 2: (no longer current) + + definition of invalidation: NONE dirty, LOCAL dirty, GLOBAL dirty + + 1) If a node sets a LOCAL, it must climb the tree and set it as GLOBAL dirty + a) marking GLOBALs as dirty up all the tree must be done always + 2) If a node sets a GLOBAL, it marks local as dirty, and that's all? + + //is clearing the dirty state correct in this case? + + drawback: setting a local down the tree forces many tree walks often + +-- + +future: no idea + + */ + + + +SpatialGizmo::SpatialGizmo() { + +} + +void Spatial::_notify_dirty() { + + if (!data.ignore_notification && !xform_change.in_list()) { + + get_scene()->xform_change_list.add(&xform_change); + } +} + + + +void Spatial::_update_local_transform() const { + + data.local_transform.basis.set_euler(data.rotation); + data.local_transform.basis.scale(data.scale); + + data.dirty&=~DIRTY_LOCAL; +} +void Spatial::_propagate_transform_changed(Spatial *p_origin) { + + if (!is_inside_scene()) { + return; + } + +// if (data.dirty&DIRTY_GLOBAL) +// return; //already dirty + + data.children_lock++; + + for (List::Element *E=data.children.front();E;E=E->next()) { + + if (E->get()->data.toplevel_active) + continue; //don't propagate to a toplevel + E->get()->_propagate_transform_changed(p_origin); + } + + + if (!data.ignore_notification && !xform_change.in_list()) { + + get_scene()->xform_change_list.add(&xform_change); + + } + data.dirty|=DIRTY_GLOBAL; + + data.children_lock--; +} + +void Spatial::_notification(int p_what) { + + switch(p_what) { + case NOTIFICATION_ENTER_SCENE: { + + Node *p = get_parent(); + if (p) + data.parent=p->cast_to(); + + if (data.parent) + data.C=data.parent->data.children.push_back(this); + else + data.C=NULL; + + if (data.toplevel && !get_scene()->is_editor_hint()) { + + if (data.parent) { + data.local_transform = data.parent->get_global_transform() * get_transform(); + data.dirty=DIRTY_VECTORS; //global is always dirty upon entering a scene + } + data.toplevel_active=true; + } + + data.dirty|=DIRTY_GLOBAL; //global is always dirty upon entering a scene + _notify_dirty(); + + notification(NOTIFICATION_ENTER_WORLD); + + } break; + case NOTIFICATION_EXIT_SCENE: { + + notification(NOTIFICATION_EXIT_WORLD,true); + if (xform_change.in_list()) + get_scene()->xform_change_list.remove(&xform_change); + if (data.C) + data.parent->data.children.erase(data.C); + data.parent=NULL; + data.C=NULL; + data.toplevel_active=false; + } break; + case NOTIFICATION_ENTER_WORLD: { + + data.inside_world=true; + data.viewport=NULL; + Node *parent = get_parent(); + while(parent && !data.viewport) { + data.viewport=parent->cast_to(); + parent=parent->get_parent(); + } + + ERR_FAIL_COND(!data.viewport); + + + if (get_script_instance()) { + + Variant::CallError err; + get_script_instance()->call_multilevel(SceneStringNames::get_singleton()->_enter_world,NULL,0); + } +#ifdef TOOLS_ENABLED + if (get_scene()->is_editor_hint()) { + +// get_scene()->call_group(SceneMainLoop::GROUP_CALL_REALTIME,SceneStringNames::get_singleton()->_spatial_editor_group,SceneStringNames::get_singleton()->_request_gizmo,this); + get_scene()->call_group(0,SceneStringNames::get_singleton()->_spatial_editor_group,SceneStringNames::get_singleton()->_request_gizmo,this); + if (!data.gizmo_disabled) { + + if (data.gizmo.is_valid()) + data.gizmo->create(); + } + } +#endif + + } break; + case NOTIFICATION_EXIT_WORLD: { + +#ifdef TOOLS_ENABLED + if (data.gizmo.is_valid()) { + data.gizmo->free(); + } +#endif + + if (get_script_instance()) { + + Variant::CallError err; + get_script_instance()->call_multilevel(SceneStringNames::get_singleton()->_exit_world,NULL,0); + } + + data.viewport=NULL; + data.inside_world=false; + + } break; + + + case NOTIFICATION_TRANSFORM_CHANGED: { + +#ifdef TOOLS_ENABLED + if (data.gizmo.is_valid()) { + data.gizmo->transform(); + } +#endif + } break; + + default: {} + } +} + +void Spatial::set_transform(const Transform& p_transform) { + + data.local_transform=p_transform; + data.dirty|=DIRTY_VECTORS; + _change_notify("transform/translation"); + _change_notify("transform/rotation"); + _change_notify("transform/scale"); + _propagate_transform_changed(this); + +} + +void Spatial::set_global_transform(const Transform& p_transform) { + + Transform xform = + (data.parent && !data.toplevel_active) ? + data.parent->get_global_transform().affine_inverse() * p_transform : + p_transform; + + set_transform(xform); + +} + + +Transform Spatial::get_transform() const { + + if (data.dirty & DIRTY_LOCAL) { + + _update_local_transform(); + } + + return data.local_transform; +} +Transform Spatial::get_global_transform() const { + + ERR_FAIL_COND_V(!is_inside_scene(), Transform()); + + if (data.dirty & DIRTY_GLOBAL) { + + if (data.dirty & DIRTY_LOCAL) { + + _update_local_transform(); + } + + if (data.parent && !data.toplevel_active) { + + data.global_transform=data.parent->get_global_transform() * data.local_transform; + } else { + + data.global_transform=data.local_transform; + } + + data.dirty&=~DIRTY_GLOBAL; + } + + return data.global_transform; +} +#if 0 +void Spatial::add_child_notify(Node *p_child) { +/* + Spatial *s=p_child->cast_to(); + if (!s) + return; + + ERR_FAIL_COND(data.children_lock>0); + + s->data.dirty=DIRTY_GLOBAL; // don't allow global transform to be valid + s->data.parent=this; + data.children.push_back(s); + s->data.C=data.children.back(); +*/ +} + +void Spatial::remove_child_notify(Node *p_child) { +/* + Spatial *s=p_child->cast_to(); + if (!s) + return; + + ERR_FAIL_COND(data.children_lock>0); + + if (s->data.C) + data.children.erase(s->data.C); + s->data.parent=NULL; + s->data.C=NULL; +*/ +} +#endif + +Spatial *Spatial::get_parent_spatial() const { + + return data.parent; + +} + +Transform Spatial::get_relative_transform(const Node *p_parent) const { + + if (p_parent==this) + return Transform(); + + ERR_FAIL_COND_V(!data.parent,Transform()); + + if (p_parent==data.parent) + return get_transform(); + else + return data.parent->get_relative_transform(p_parent) * get_transform(); + +} + +void Spatial::set_translation(const Vector3& p_translation) { + + data.local_transform.origin=p_translation; + _propagate_transform_changed(this); + +} + +void Spatial::set_rotation(const Vector3& p_euler){ + + if (data.dirty&DIRTY_VECTORS) { + data.scale=data.local_transform.basis.get_scale(); + data.dirty&=~DIRTY_VECTORS; + } + + data.rotation=p_euler; + data.dirty|=DIRTY_LOCAL; + _propagate_transform_changed(this); + +} +void Spatial::set_scale(const Vector3& p_scale){ + + if (data.dirty&DIRTY_VECTORS) { + data.rotation=data.local_transform.basis.get_euler(); + data.dirty&=~DIRTY_VECTORS; + } + + data.scale=p_scale; + data.dirty|=DIRTY_LOCAL; + _propagate_transform_changed(this); + +} + +Vector3 Spatial::get_translation() const{ + + return data.local_transform.origin; +} +Vector3 Spatial::get_rotation() const{ + + if (data.dirty&DIRTY_VECTORS) { + data.scale=data.local_transform.basis.get_scale(); + data.rotation=data.local_transform.basis.get_euler(); + data.dirty&=~DIRTY_VECTORS; + } + + return data.rotation; +} +Vector3 Spatial::get_scale() const{ + + if (data.dirty&DIRTY_VECTORS) { + data.scale=data.local_transform.basis.get_scale(); + data.rotation=data.local_transform.basis.get_euler(); + data.dirty&=~DIRTY_VECTORS; + } + + return data.scale; +} + + +void Spatial::update_gizmo() { + +#ifdef TOOLS_ENABLED + if (!is_inside_world()) + return; + if (!data.gizmo.is_valid()) + return; + if (data.gizmo_dirty) + return; + data.gizmo_dirty=true; + MessageQueue::get_singleton()->push_call(this,"_update_gizmo"); +#endif +} + +void Spatial::set_gizmo(const Ref& p_gizmo) { + +#ifdef TOOLS_ENABLED + + if (data.gizmo_disabled) + return; + if (data.gizmo.is_valid() && is_inside_world()) + data.gizmo->free(); + data.gizmo=p_gizmo; + if (data.gizmo.is_valid() && is_inside_world()) { + + data.gizmo->create(); + data.gizmo->redraw(); + data.gizmo->transform(); + } + +#endif +} + +Ref Spatial::get_gizmo() const { + +#ifdef TOOLS_ENABLED + + return data.gizmo; +#else + + return Ref(); +#endif +} + +#ifdef TOOLS_ENABLED + +void Spatial::_update_gizmo() { + + data.gizmo_dirty=false; + if (data.gizmo.is_valid()) + data.gizmo->redraw(); +} + + +void Spatial::set_disable_gizmo(bool p_enabled) { + + data.gizmo_disabled=p_enabled; + if (!p_enabled && data.gizmo.is_valid()) + data.gizmo=Ref(); +} + +#endif + +void Spatial::set_as_toplevel(bool p_enabled) { + + if (data.toplevel==p_enabled) + return; + if (is_inside_scene() && !get_scene()->is_editor_hint()) { + + if (p_enabled) + set_transform(get_global_transform()); + else if (data.parent) + set_transform(data.parent->get_global_transform().affine_inverse() * get_global_transform()); + + data.toplevel=p_enabled; + data.toplevel_active=p_enabled; + + } else { + data.toplevel=p_enabled; + } + +} + +bool Spatial::is_set_as_toplevel() const{ + + return data.toplevel; +} + +void Spatial::_set_rotation_deg(const Vector3& p_deg) { + + set_rotation(p_deg * Math_PI / 180.0); +} + +Vector3 Spatial::_get_rotation_deg() const { + + return get_rotation() * 180.0 / Math_PI; +} + +Ref Spatial::get_world() const { + + ERR_FAIL_COND_V(!is_inside_world(),Ref()); + return data.viewport->find_world(); + +} + + +void Spatial::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("set_transform","local"), &Spatial::set_transform); + ObjectTypeDB::bind_method(_MD("get_transform"), &Spatial::get_transform); + ObjectTypeDB::bind_method(_MD("set_translation","translation"), &Spatial::set_translation); + ObjectTypeDB::bind_method(_MD("get_translation"), &Spatial::get_translation); + ObjectTypeDB::bind_method(_MD("set_rotation","rotation"), &Spatial::set_rotation); + ObjectTypeDB::bind_method(_MD("get_rotation"), &Spatial::get_rotation); + ObjectTypeDB::bind_method(_MD("set_scale","scale"), &Spatial::set_scale); + ObjectTypeDB::bind_method(_MD("get_scale"), &Spatial::get_scale); + ObjectTypeDB::bind_method(_MD("set_global_transform","global"), &Spatial::set_global_transform); + ObjectTypeDB::bind_method(_MD("get_global_transform"), &Spatial::get_global_transform); + ObjectTypeDB::bind_method(_MD("get_parent_spatial"), &Spatial::get_parent_spatial); + ObjectTypeDB::bind_method(_MD("set_ignore_transform_notification","enabled"), &Spatial::set_ignore_transform_notification); + ObjectTypeDB::bind_method(_MD("set_as_toplevel","enable"), &Spatial::set_as_toplevel); + ObjectTypeDB::bind_method(_MD("is_set_as_toplevel"), &Spatial::is_set_as_toplevel); + ObjectTypeDB::bind_method(_MD("_set_rotation_deg","rotation_deg"), &Spatial::_set_rotation_deg); + ObjectTypeDB::bind_method(_MD("_get_rotation_deg"), &Spatial::_get_rotation_deg); + ObjectTypeDB::bind_method(_MD("get_world:World"), &Spatial::get_world); + +#ifdef TOOLS_ENABLED + ObjectTypeDB::bind_method(_MD("_update_gizmo"), &Spatial::_update_gizmo); +#endif + + ObjectTypeDB::bind_method(_MD("update_gizmo"), &Spatial::update_gizmo); + ObjectTypeDB::bind_method(_MD("set_gizmo","gizmo:SpatialGizmo"), &Spatial::set_gizmo); + ObjectTypeDB::bind_method(_MD("get_gizmo:SpatialGizmo"), &Spatial::get_gizmo); + + BIND_CONSTANT( NOTIFICATION_TRANSFORM_CHANGED ); + BIND_CONSTANT( NOTIFICATION_ENTER_WORLD ); + BIND_CONSTANT( NOTIFICATION_EXIT_WORLD ); + + //ADD_PROPERTY( PropertyInfo(Variant::TRANSFORM,"transform/global",PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR ), _SCS("set_global_transform"), _SCS("get_global_transform") ); + ADD_PROPERTYNZ( PropertyInfo(Variant::TRANSFORM,"transform/local",PROPERTY_HINT_NONE,""), _SCS("set_transform"), _SCS("get_transform") ); + ADD_PROPERTY( PropertyInfo(Variant::VECTOR3,"transform/translation",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_EDITOR), _SCS("set_translation"), _SCS("get_translation") ); + ADD_PROPERTY( PropertyInfo(Variant::VECTOR3,"transform/rotation",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_EDITOR), _SCS("_set_rotation_deg"), _SCS("_get_rotation_deg") ); + ADD_PROPERTY( PropertyInfo(Variant::VECTOR3,"transform/rotation_rad",PROPERTY_HINT_NONE,"",0), _SCS("set_rotation"), _SCS("get_rotation") ); + ADD_PROPERTY( PropertyInfo(Variant::VECTOR3,"transform/scale",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_EDITOR), _SCS("set_scale"), _SCS("get_scale") ); + //ADD_PROPERTY( PropertyInfo(Variant::TRANSFORM,"transform/local"), _SCS("set_transform"), _SCS("get_transform") ); + +} + + +Spatial::Spatial() : xform_change(this) +{ + + data.dirty=DIRTY_NONE; + data.children_lock=0; + + data.ignore_notification=false; + data.toplevel=false; + data.toplevel_active=false; + data.scale=Vector3(1,1,1); + data.viewport=NULL; + data.inside_world=false; +#ifdef TOOLS_ENABLED + data.gizmo_disabled=false; + data.gizmo_dirty=false; +#endif + data.parent=NULL; + data.C=NULL; + +} + + +Spatial::~Spatial() { + +} + + diff --git a/scene/3d/spatial.h b/scene/3d/spatial.h new file mode 100644 index 00000000000..e10629f7de1 --- /dev/null +++ b/scene/3d/spatial.h @@ -0,0 +1,166 @@ +/*************************************************************************/ +/* spatial.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 SPATIAL_H +#define SPATIAL_H + +#include "scene/main/node.h" +#include "scene/main/scene_main_loop.h" + +/** + @author Juan Linietsky +*/ + +class SpatialGizmo : public Reference { + + OBJ_TYPE(SpatialGizmo,Reference); + + +public: + + virtual void create()=0; + virtual void transform()=0; + virtual void redraw()=0; + virtual void free()=0; + + SpatialGizmo(); +}; + + +class Spatial : public Node { + + OBJ_TYPE( Spatial, Node ); + OBJ_CATEGORY("3D"); + + enum TransformDirty { + DIRTY_NONE=0, + DIRTY_VECTORS=1, + DIRTY_LOCAL=2, + DIRTY_GLOBAL=4 + }; + + mutable SelfList xform_change; + + struct Data { + + + + mutable Transform global_transform; + mutable Transform local_transform; + mutable Vector3 rotation; + mutable Vector3 scale; + + mutable int dirty; + + Viewport *viewport; + + + bool toplevel_active; + bool toplevel; + bool inside_world; + + int children_lock; + Spatial *parent; + List children; + List::Element *C; + + bool ignore_notification; + +#ifdef TOOLS_ENABLED + Ref gizmo; + bool gizmo_disabled; + bool gizmo_dirty; +#endif + + } data; +#ifdef TOOLS_ENABLED + + void _update_gizmo(); +#endif + void _notify_dirty(); + void _propagate_transform_changed(Spatial *p_origin); + + void _set_rotation_deg(const Vector3& p_deg); + Vector3 _get_rotation_deg() const; + + +protected: + + _FORCE_INLINE_ void set_ignore_transform_notification(bool p_ignore) { data.ignore_notification=p_ignore; } + + _FORCE_INLINE_ void _update_local_transform() const; + + void _notification(int p_what); + static void _bind_methods(); + +public: + + enum { + + NOTIFICATION_TRANSFORM_CHANGED=SceneMainLoop::NOTIFICATION_TRANSFORM_CHANGED, + NOTIFICATION_ENTER_WORLD=41, + NOTIFICATION_EXIT_WORLD=42, + }; + + Spatial *get_parent_spatial() const; + + + Ref get_world() const; + + void set_translation(const Vector3& p_translation); + void set_rotation(const Vector3& p_euler); + void set_scale(const Vector3& p_scale); + + Vector3 get_translation() const; + Vector3 get_rotation() const; + Vector3 get_scale() const; + + void set_transform(const Transform& p_transform); + void set_global_transform(const Transform& p_transform); + + Transform get_transform() const; + Transform get_global_transform() const; + + void set_as_toplevel(bool p_enabled); + bool is_set_as_toplevel() const; + + void set_disable_gizmo(bool p_enabled); + void update_gizmo(); + void set_gizmo(const Ref& p_gizmo); + Ref get_gizmo() const; + + _FORCE_INLINE_ bool is_inside_world() const { return data.inside_world; } + + Transform get_relative_transform(const Node *p_parent) const; + + Spatial(); + ~Spatial(); + +}; + +#endif diff --git a/scene/3d/spatial_indexer.cpp b/scene/3d/spatial_indexer.cpp new file mode 100644 index 00000000000..261c62f51ce --- /dev/null +++ b/scene/3d/spatial_indexer.cpp @@ -0,0 +1,165 @@ +/*************************************************************************/ +/* spatial_indexer.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 "spatial_indexer.h" + +#if 0 + +#include "proximity_area.h" +#include "camera.h" +#include "scene/scene_string_names.h" + +void SpatialIndexer::add_camera(Camera* p_camera) { + + cameras.insert(p_camera); +} + +void SpatialIndexer::remove_camera(Camera* p_camera){ + + for (Set::Element *F=proximity_areas.front();F;F=F->next()) { + + ProximityArea *prox = F->get(); + TK k; + k.against=p_camera; + k.area=prox; + if (camera_pairs.has(k)) { + camera_pairs.erase(k); + prox->area_exit(ProximityArea::TRACK_CAMERAS,p_camera); + } + } + cameras.erase(p_camera); + +} + +void SpatialIndexer::update_camera(Camera* p_camera) { + + + _request_update(); +} + +void SpatialIndexer::_update_pairs() { + + // brute force interseciton code, no broadphase + // will implement broadphase in the future + + for (Set::Element *E=cameras.front();E;E=E->next()) { + + Camera *cam = E->get(); + Vector cplanes = cam->get_frustum(); + + for (Set::Element *F=proximity_areas.front();F;F=F->next()) { + + ProximityArea *prox = F->get(); + + bool inters=false; + + if (prox->get_track_flag(ProximityArea::TRACK_CAMERAS)) { + + AABB aabb = prox->get_global_transform().xform(prox->get_aabb()); + if (aabb.intersects_convex_shape(cplanes.ptr(),cplanes.size())) + inters=true; + } + + TK k; + k.against=cam; + k.area=prox; + + bool has = camera_pairs.has(k); + + if (inters==has) + continue; + + if (inters) { + camera_pairs.insert(k); + prox->area_enter(ProximityArea::TRACK_CAMERAS,cam); + } else { + + camera_pairs.erase(k); + prox->area_exit(ProximityArea::TRACK_CAMERAS,cam); + } + } + + } + + pending_update=false; +} + +void SpatialIndexer::_bind_methods() { + + + ObjectTypeDB::bind_method(_MD("_update_pairs"),&SpatialIndexer::_update_pairs); +} + + +void SpatialIndexer::add_proximity_area(ProximityArea* p_area) { + + proximity_areas.insert(p_area); + +} + +void SpatialIndexer::remove_proximity_area(ProximityArea* p_area) { + + for (Set::Element *E=cameras.front();E;E=E->next()) { + + Camera *cam = E->get(); + TK k; + k.against=cam; + k.area=p_area; + if (camera_pairs.has(k)) { + camera_pairs.erase(k); + p_area->area_exit(ProximityArea::TRACK_CAMERAS,cam); + } + } + proximity_areas.erase(p_area); + +} + +void SpatialIndexer::_request_update() { + + if (pending_update) + return; + pending_update=true; + call_deferred(SceneStringNames::get_singleton()->_update_pairs); + +} + +void SpatialIndexer::update_proximity_area_transform(ProximityArea* p_area) { + + _request_update(); +} + +void SpatialIndexer::update_proximity_area_flags(ProximityArea* p_area) { + + _request_update(); +} + +SpatialIndexer::SpatialIndexer() { + + pending_update=false; +} +#endif diff --git a/scene/3d/spatial_indexer.h b/scene/3d/spatial_indexer.h new file mode 100644 index 00000000000..d3038a1293f --- /dev/null +++ b/scene/3d/spatial_indexer.h @@ -0,0 +1,83 @@ +/*************************************************************************/ +/* spatial_indexer.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 SPATIAL_INDEXER_H +#define SPATIAL_INDEXER_H + +#include "scene/3d/spatial.h" +#if 0 + +class Camera; +class ProximityArea; + +class SpatialIndexer : public Object { + + OBJ_TYPE( SpatialIndexer, Object ); + + template + struct TK { + + T *against; + ProximityArea *area; + bool operator<(const TK& p_k) const { return against==p_k.against ? area < p_k.area : against < p_k.against; } + }; + + + Set cameras; //cameras + Set proximity_areas; + + Set > camera_pairs; + + bool pending_update; + void _update_pairs(); + void _request_update(); + +protected: + + static void _bind_methods(); + +friend class ProximityArea; +friend class Camera; + + void add_proximity_area(ProximityArea* p_area); + void remove_proximity_area(ProximityArea* p_area); + void update_proximity_area_transform(ProximityArea* p_area); + void update_proximity_area_flags(ProximityArea* p_area); + + void add_camera(Camera* p_camera); + void remove_camera(Camera* p_camera); + void update_camera(Camera* p_camera); + +public: + + + SpatialIndexer(); + +}; +#endif +#endif // SPATIAL_INDEXER_H diff --git a/scene/3d/spatial_player.cpp b/scene/3d/spatial_player.cpp new file mode 100644 index 00000000000..08475983567 --- /dev/null +++ b/scene/3d/spatial_player.cpp @@ -0,0 +1,272 @@ +/*************************************************************************/ +/* spatial_player.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 "spatial_player.h" + +#include "servers/audio_server.h" +#include "camera.h" +#include "servers/spatial_sound_server.h" +#include "scene/resources/surface_tool.h" + + +void SpatialPlayer::_notification(int p_what) { + + + switch(p_what) { + + case NOTIFICATION_ENTER_WORLD: { + //find the sound space + + source_rid = SpatialSoundServer::get_singleton()->source_create(get_world()->get_sound_space()); + for(int i=0;isource_set_transform(source_rid,get_global_transform()); + + } break; + case NOTIFICATION_EXIT_WORLD: { + + if (source_rid.is_valid()) + SpatialSoundServer::get_singleton()->free(source_rid); + + } break; + } + +} + + +void SpatialPlayer::set_param( Param p_param, float p_value) { + + ERR_FAIL_INDEX(p_param,PARAM_MAX); + params[p_param]=p_value; + if (p_param==PARAM_EMISSION_CONE_DEGREES) { + update_gizmo(); + } + if (source_rid.is_valid()) + SpatialSoundServer::get_singleton()->source_set_param(source_rid,(SpatialSoundServer::SourceParam)p_param,p_value); + +} + +float SpatialPlayer::get_param( Param p_param) const { + + ERR_FAIL_INDEX_V(p_param,PARAM_MAX,0); + return params[p_param]; + +} + +bool SpatialPlayer::_can_gizmo_scale() const { + + return false; +} + +RES SpatialPlayer::_get_gizmo_geometry() const { + + Ref surface_tool( memnew( SurfaceTool )); + + Ref mat( memnew( FixedMaterial )); + + mat->set_parameter( FixedMaterial::PARAM_DIFFUSE,Color(0.0,0.6,0.7,0.05) ); + mat->set_parameter( FixedMaterial::PARAM_EMISSION,Color(0.5,0.7,0.8) ); + mat->set_blend_mode( Material::BLEND_MODE_ADD ); + mat->set_flag(Material::FLAG_DOUBLE_SIDED,true); + mat->set_hint(Material::HINT_NO_DEPTH_DRAW,true); + + + surface_tool->begin(Mesh::PRIMITIVE_TRIANGLES); + surface_tool->set_material(mat); + + int sides=16; + int sections=24; + +// float len=1; + float deg=Math::deg2rad(params[PARAM_EMISSION_CONE_DEGREES]); + if (deg==180) + deg=179.5; + + Vector3 to=Vector3(0,0,-1); + + for(int j=0;jadd_normal(p1r.normalized()); + surface_tool->add_vertex(p1r); + surface_tool->add_normal(p1s.normalized()); + surface_tool->add_vertex(p1s); + surface_tool->add_normal(p2s.normalized()); + surface_tool->add_vertex(p2s); + + surface_tool->add_normal(p1r.normalized()); + surface_tool->add_vertex(p1r); + surface_tool->add_normal(p2s.normalized()); + surface_tool->add_vertex(p2s); + surface_tool->add_normal(p2r.normalized()); + surface_tool->add_vertex(p2r); + + if (j==sections-1) { + + surface_tool->add_normal(p2r.normalized()); + surface_tool->add_vertex(p2r); + surface_tool->add_normal(p2s.normalized()); + surface_tool->add_vertex(p2s); + surface_tool->add_normal(Vector3(0,0,1)); + surface_tool->add_vertex(Vector3()); + } + } + } + + + Ref mesh = surface_tool->commit(); + + Ref mat_speaker( memnew( FixedMaterial )); + + mat_speaker->set_parameter( FixedMaterial::PARAM_DIFFUSE,Color(0.3,0.3,0.6) ); + mat_speaker->set_parameter( FixedMaterial::PARAM_SPECULAR,Color(0.5,0.5,0.6) ); + //mat_speaker->set_blend_mode( Material::BLEND_MODE_MIX); + //mat_speaker->set_flag(Material::FLAG_DOUBLE_SIDED,false); + //mat_speaker->set_flag(Material::FLAG_UNSHADED,true); + + surface_tool->begin(Mesh::PRIMITIVE_TRIANGLES); + surface_tool->set_material(mat_speaker); + +// float radius=1; + + + const int speaker_points=8; + Vector3 speaker[speaker_points]={ + Vector3(0,0,1)*0.15, + Vector3(1,1,1)*0.15, + Vector3(1,1,0)*0.15, + Vector3(2,2,-1)*0.15, + Vector3(1,1,-1)*0.15, + Vector3(0.8,0.8,-1.2)*0.15, + Vector3(0.5,0.5,-1.4)*0.15, + Vector3(0.0,0.0,-1.6)*0.15 + }; + + int speaker_sides=10; + + + for(int i = 0; i < speaker_sides ; i++) { + + + Matrix3 ma(Vector3(0,0,1),Math_PI*2*float(i)/speaker_sides); + Matrix3 mb(Vector3(0,0,1),Math_PI*2*float(i+1)/speaker_sides); + + + for(int j=0;jadd_normal(n); + surface_tool->add_vertex(points[0]); + surface_tool->add_normal(n); + surface_tool->add_vertex(points[2]); + surface_tool->add_normal(n); + surface_tool->add_vertex(points[1]); + + surface_tool->add_normal(n); + surface_tool->add_vertex(points[0]); + surface_tool->add_normal(n); + surface_tool->add_vertex(points[3]); + surface_tool->add_normal(n); + surface_tool->add_vertex(points[2]); + + + } + + + } + + + return surface_tool->commit(mesh); + +} + + +void SpatialPlayer::_bind_methods() { + + + ObjectTypeDB::bind_method(_MD("set_param","param","value"),&SpatialPlayer::set_param); + ObjectTypeDB::bind_method(_MD("get_param","param"),&SpatialPlayer::get_param); + + BIND_CONSTANT( PARAM_VOLUME_DB ); + BIND_CONSTANT( PARAM_PITCH_SCALE ); + BIND_CONSTANT( PARAM_ATTENUATION_MIN_DISTANCE ); + BIND_CONSTANT( PARAM_ATTENUATION_MAX_DISTANCE ); + BIND_CONSTANT( PARAM_ATTENUATION_DISTANCE_EXP ); + BIND_CONSTANT( PARAM_EMISSION_CONE_DEGREES ); + BIND_CONSTANT( PARAM_EMISSION_CONE_ATTENUATION_DB ); + BIND_CONSTANT( PARAM_MAX ); + + ADD_PROPERTYI( PropertyInfo( Variant::REAL, "params/volume_db",PROPERTY_HINT_RANGE, "-80,24,0.01"),_SCS("set_param"),_SCS("get_param"),PARAM_VOLUME_DB); + ADD_PROPERTYI( PropertyInfo( Variant::REAL, "params/pitch_scale",PROPERTY_HINT_RANGE, "0.001,32,0.001"),_SCS("set_param"),_SCS("get_param"),PARAM_PITCH_SCALE); + ADD_PROPERTYI( PropertyInfo( Variant::REAL, "params/attenuation/min_distance",PROPERTY_HINT_RANGE, "0.01,4096,0.01"),_SCS("set_param"),_SCS("get_param"),PARAM_ATTENUATION_MIN_DISTANCE); + ADD_PROPERTYI( PropertyInfo( Variant::REAL, "params/attenuation/max_distance",PROPERTY_HINT_RANGE, "0.01,4096,0.01"),_SCS("set_param"),_SCS("get_param"),PARAM_ATTENUATION_MAX_DISTANCE); + ADD_PROPERTYI( PropertyInfo( Variant::REAL, "params/attenuation/distance_exp",PROPERTY_HINT_EXP_EASING, "attenuation"),_SCS("set_param"),_SCS("get_param"),PARAM_ATTENUATION_DISTANCE_EXP); + ADD_PROPERTYI( PropertyInfo( Variant::REAL, "params/emission_cone/degrees",PROPERTY_HINT_RANGE, "0,180,0.01"),_SCS("set_param"),_SCS("get_param"),PARAM_EMISSION_CONE_DEGREES); + ADD_PROPERTYI( PropertyInfo( Variant::REAL, "params/emission_cone/attenuation_db",PROPERTY_HINT_RANGE, "-80,24,0.01"),_SCS("set_param"),_SCS("get_param"),PARAM_EMISSION_CONE_ATTENUATION_DB); + +} + + +SpatialPlayer::SpatialPlayer() { + + params[PARAM_VOLUME_DB]=0.0; + params[PARAM_PITCH_SCALE]=1.0; + params[PARAM_ATTENUATION_MIN_DISTANCE]=1; + params[PARAM_ATTENUATION_MAX_DISTANCE]=100; + params[PARAM_ATTENUATION_DISTANCE_EXP]=1.0; //linear (and not really good) + params[PARAM_EMISSION_CONE_DEGREES]=180.0; //cone disabled + params[PARAM_EMISSION_CONE_ATTENUATION_DB]=-6.0; //minus 6 db attenuation + +} + +SpatialPlayer::~SpatialPlayer() { + + +} diff --git a/scene/3d/spatial_player.h b/scene/3d/spatial_player.h new file mode 100644 index 00000000000..e11028d2c93 --- /dev/null +++ b/scene/3d/spatial_player.h @@ -0,0 +1,87 @@ +/*************************************************************************/ +/* spatial_player.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 SPATIAL_PLAYER_H +#define SPATIAL_PLAYER_H + + +#include "scene/3d/spatial.h" +#include "scene/main/node.h" +#include "scene/resources/sample_library.h" +#include "servers/spatial_sound_server.h" +#include "scene/main/viewport.h" + +class SpatialPlayer : public Spatial { + + OBJ_TYPE(SpatialPlayer,Spatial); +public: + + + enum Param { + + PARAM_VOLUME_DB=SpatialSoundServer::SOURCE_PARAM_VOLUME_DB, + PARAM_PITCH_SCALE=SpatialSoundServer::SOURCE_PARAM_PITCH_SCALE, + PARAM_ATTENUATION_MIN_DISTANCE=SpatialSoundServer::SOURCE_PARAM_ATTENUATION_MIN_DISTANCE, + PARAM_ATTENUATION_MAX_DISTANCE=SpatialSoundServer::SOURCE_PARAM_ATTENUATION_MAX_DISTANCE, + PARAM_ATTENUATION_DISTANCE_EXP=SpatialSoundServer::SOURCE_PARAM_ATTENUATION_DISTANCE_EXP, + PARAM_EMISSION_CONE_DEGREES=SpatialSoundServer::SOURCE_PARAM_EMISSION_CONE_DEGREES, + PARAM_EMISSION_CONE_ATTENUATION_DB=SpatialSoundServer::SOURCE_PARAM_EMISSION_CONE_ATTENUATION_DB, + PARAM_MAX=SpatialSoundServer::SOURCE_PARAM_MAX + }; + +private: + + float params[PARAM_MAX]; + RID source_rid; + + virtual bool _can_gizmo_scale() const; + virtual RES _get_gizmo_geometry() const; + + +protected: + + _FORCE_INLINE_ RID get_source_rid() const { return source_rid; } + + void _notification(int p_what); + + static void _bind_methods(); + +public: + + void set_param( Param p_param, float p_value); + float get_param( Param p_param) const; + + + SpatialPlayer(); + ~SpatialPlayer(); + + +}; + +VARIANT_ENUM_CAST( SpatialPlayer::Param ); +#endif // SPATIAL_PLAYER_H diff --git a/scene/3d/spatial_sample_player.cpp b/scene/3d/spatial_sample_player.cpp new file mode 100644 index 00000000000..b4a5d3bc1bb --- /dev/null +++ b/scene/3d/spatial_sample_player.cpp @@ -0,0 +1,229 @@ +/*************************************************************************/ +/* spatial_sample_player.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 "spatial_sample_player.h" + +#include "servers/audio_server.h" +#include "camera.h" +#include "servers/spatial_sound_server.h" +#include "scene/scene_string_names.h" + +bool SpatialSamplePlayer::_set(const StringName& p_name, const Variant& p_value) { + + String name=p_name; + + if (name==SceneStringNames::get_singleton()->play_play) { + if (library.is_valid()) { + + String what=p_value; + if (what=="") + stop_all(); + else + play(what); + + played_back=what; + } + return true; + + } + + return false; +} + +bool SpatialSamplePlayer::_get(const StringName& p_name,Variant &r_ret) const { + + + String name=p_name; + + if (name==SceneStringNames::get_singleton()->play_play) { + r_ret=played_back; + return true; + } + + return false; +} + +void SpatialSamplePlayer::_get_property_list(List *p_list) const { + + String en=""; + if (library.is_valid()) { + List samples; + Ref ncl=library; + ncl->get_sample_list(&samples); + for (List::Element *E=samples.front();E;E=E->next()) { + + en+=","; + en+=E->get(); + } + } + + p_list->push_back( PropertyInfo( Variant::STRING, "play/play", PROPERTY_HINT_ENUM, en,PROPERTY_USAGE_EDITOR)); + +} +void SpatialSamplePlayer::_notification(int p_what) { + + + switch(p_what) { + + case NOTIFICATION_ENTER_WORLD: { + + SpatialSoundServer::get_singleton()->source_set_polyphony(get_source_rid(),polyphony); + + + } break; + } + +} + +void SpatialSamplePlayer::set_sample_library(const Ref& p_library) { + + library=p_library; +} + +Ref SpatialSamplePlayer::get_sample_library() const { + + return library; +} + +void SpatialSamplePlayer::set_polyphony(int p_voice_count) { + + ERR_FAIL_COND(p_voice_count<0 || p_voice_count>64); + polyphony=p_voice_count; + if (get_source_rid().is_valid()) + SpatialSoundServer::get_singleton()->source_set_polyphony(get_source_rid(),polyphony); + +} + +int SpatialSamplePlayer::get_polyphony() const { + + return polyphony; +} + +SpatialSamplePlayer::VoiceID SpatialSamplePlayer::play(const String& p_sample,int p_voice) { + + if (!get_source_rid().is_valid()) + return INVALID_VOICE; + if (library.is_null()) + return INVALID_VOICE; + if (!library->has_sample(p_sample)) + return INVALID_VOICE; + Ref sample = library->get_sample(p_sample); + float vol_change = library->sample_get_volume_db(p_sample); + float pitch_change = library->sample_get_pitch_scale(p_sample); + + VoiceID vid = SpatialSoundServer::get_singleton()->source_play_sample(get_source_rid(),sample->get_rid(),sample->get_mix_rate()*pitch_change,p_voice); + if (vol_change) + SpatialSoundServer::get_singleton()->source_voice_set_volume_scale_db(get_source_rid(),vid,vol_change); + + return vid; + + +} +//voices +void SpatialSamplePlayer::voice_set_pitch_scale(VoiceID p_voice, float p_pitch_scale) { + + if (!get_source_rid().is_valid()) + return; + + SpatialSoundServer::get_singleton()->source_voice_set_pitch_scale(get_source_rid(),p_voice,p_pitch_scale); + +} + +void SpatialSamplePlayer::voice_set_volume_scale_db(VoiceID p_voice, float p_volume_db) { + + if (!get_source_rid().is_valid()) + return; + SpatialSoundServer::get_singleton()->source_voice_set_volume_scale_db(get_source_rid(),p_voice,p_volume_db); + +} + +bool SpatialSamplePlayer::is_voice_active(VoiceID p_voice) const { + + if (!get_source_rid().is_valid()) + return false; + return SpatialSoundServer::get_singleton()->source_is_voice_active(get_source_rid(),p_voice); + +} + +void SpatialSamplePlayer::stop_voice(VoiceID p_voice) { + + if (!get_source_rid().is_valid()) + return; + SpatialSoundServer::get_singleton()->source_stop_voice(get_source_rid(),p_voice); + +} + +void SpatialSamplePlayer::stop_all() { + + if (!get_source_rid().is_valid()) + return; + + for(int i=0;isource_stop_voice(get_source_rid(),i); + } +} + +void SpatialSamplePlayer::_bind_methods() { + + + ObjectTypeDB::bind_method(_MD("set_sample_library","library:SampleLibrary"),&SpatialSamplePlayer::set_sample_library); + ObjectTypeDB::bind_method(_MD("get_sample_library:SampleLibrary"),&SpatialSamplePlayer::get_sample_library); + + ObjectTypeDB::bind_method(_MD("set_polyphony","voices"),&SpatialSamplePlayer::set_polyphony); + ObjectTypeDB::bind_method(_MD("get_polyphony"),&SpatialSamplePlayer::get_polyphony); + + ObjectTypeDB::bind_method(_MD("play","sample","voice"),&SpatialSamplePlayer::play,DEFVAL(NEXT_VOICE)); + //voices,DEV + ObjectTypeDB::bind_method(_MD("voice_set_pitch_scale","voice","ratio"),&SpatialSamplePlayer::voice_set_pitch_scale); + ObjectTypeDB::bind_method(_MD("voice_set_volume_scale_db","voice","db"),&SpatialSamplePlayer::voice_set_volume_scale_db); + + ObjectTypeDB::bind_method(_MD("is_voice_active","voice"),&SpatialSamplePlayer::is_voice_active); + ObjectTypeDB::bind_method(_MD("stop_voice","voice"),&SpatialSamplePlayer::stop_voice); + ObjectTypeDB::bind_method(_MD("stop_all"),&SpatialSamplePlayer::stop_all); + + BIND_CONSTANT( INVALID_VOICE ); + BIND_CONSTANT( NEXT_VOICE ); + + ADD_PROPERTY( PropertyInfo( Variant::INT, "config/polyphony", PROPERTY_HINT_RANGE, "1,64,1"),_SCS("set_polyphony"),_SCS("get_polyphony")); + ADD_PROPERTY( PropertyInfo( Variant::OBJECT, "config/samples", PROPERTY_HINT_RESOURCE_TYPE,"SampleLibrary"),_SCS("set_sample_library"),_SCS("get_sample_library")); + + +} + + +SpatialSamplePlayer::SpatialSamplePlayer() { + + polyphony=1; + +} + +SpatialSamplePlayer::~SpatialSamplePlayer() { + + +} diff --git a/scene/3d/spatial_sample_player.h b/scene/3d/spatial_sample_player.h new file mode 100644 index 00000000000..68a0326c492 --- /dev/null +++ b/scene/3d/spatial_sample_player.h @@ -0,0 +1,89 @@ +/*************************************************************************/ +/* spatial_sample_player.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 SPATIAL_SAMPLE_PLAYER_H +#define SPATIAL_SAMPLE_PLAYER_H + +#include "scene/3d/spatial_player.h" +#include "scene/resources/sample_library.h" +#include "servers/spatial_sound_server.h" + +class SpatialSamplePlayer : public SpatialPlayer { + + OBJ_TYPE(SpatialSamplePlayer,SpatialPlayer); +public: + + enum { + + INVALID_VOICE=SpatialSoundServer::SOURCE_INVALID_VOICE, + NEXT_VOICE=SpatialSoundServer::SOURCE_NEXT_VOICE + }; + + typedef int VoiceID; + + +private: + + Ref library; + int polyphony; + String played_back; +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 *p_list) const; + + void _notification(int p_what); + static void _bind_methods(); + +public: + + void set_sample_library(const Ref& p_library); + Ref get_sample_library() const; + + void set_polyphony(int p_voice_count); + int get_polyphony() const; + + VoiceID play(const String& p_sample,int p_voice=NEXT_VOICE); + //voices + void voice_set_pitch_scale(VoiceID p_voice, float p_pitch_scale); + void voice_set_volume_scale_db(VoiceID p_voice, float p_volume_db); + + bool is_voice_active(VoiceID p_voice) const; + void stop_voice(VoiceID p_voice); + void stop_all(); + + + SpatialSamplePlayer(); + ~SpatialSamplePlayer(); + + +}; + + +#endif // SPATIAL_SAMPLE_PLAYER_H diff --git a/scene/3d/spatial_stream_player.cpp b/scene/3d/spatial_stream_player.cpp new file mode 100644 index 00000000000..25448498cba --- /dev/null +++ b/scene/3d/spatial_stream_player.cpp @@ -0,0 +1,190 @@ +/*************************************************************************/ +/* spatial_stream_player.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 "spatial_stream_player.h" + + + +void SpatialStreamPlayer::_notification(int p_what) { + + switch(p_what) { + + case NOTIFICATION_ENTER_WORLD: { + +// set_idle_process(false); //don't annoy + } break; + case NOTIFICATION_PROCESS: { + +// if (!stream.is_null()) +// stream->update(); + + } break; + case NOTIFICATION_EXIT_WORLD: { + + stop(); //wathever it may be doing, stop + } break; + } +} + + + +void SpatialStreamPlayer::set_stream(const Ref &p_stream) { + + stop(); + + stream=p_stream; + if (!stream.is_null()) { + + stream->set_loop(loops); + } + + +} + +Ref SpatialStreamPlayer::get_stream() const { + + return stream; +} + + +void SpatialStreamPlayer::play() { + + if (!is_inside_scene()) + return; + if (stream.is_null()) + return; + if (stream->is_playing()) + stop(); + stream->play(); + SpatialSoundServer::get_singleton()->source_set_audio_stream(get_source_rid(),stream->get_audio_stream()); + //if (stream->get_update_mode()!=AudioStream::UPDATE_NONE) + // set_idle_process(true); + +} + +void SpatialStreamPlayer::stop() { + + if (!is_inside_scene()) + return; + if (stream.is_null()) + return; + + SpatialSoundServer::get_singleton()->source_set_audio_stream(get_source_rid(),NULL); + stream->stop(); + //set_idle_process(false); +} + +bool SpatialStreamPlayer::is_playing() const { + + if (stream.is_null()) + return false; + + return stream->is_playing(); +} + +void SpatialStreamPlayer::set_loop(bool p_enable) { + + loops=p_enable; + if (stream.is_null()) + return; + stream->set_loop(loops); + +} +bool SpatialStreamPlayer::has_loop() const { + + return loops; +} + + + +String SpatialStreamPlayer::get_stream_name() const { + + if (stream.is_null()) + return ""; + return stream->get_name(); + +} + +int SpatialStreamPlayer::get_loop_count() const { + + if (stream.is_null()) + return 0; + return stream->get_loop_count(); + +} + +float SpatialStreamPlayer::get_pos() const { + + if (stream.is_null()) + return 0; + return stream->get_pos(); + +} +void SpatialStreamPlayer::seek_pos(float p_time) { + + if (stream.is_null()) + return; + return stream->seek_pos(p_time); + +} + +void SpatialStreamPlayer::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("set_stream","stream:Stream"),&SpatialStreamPlayer::set_stream); + ObjectTypeDB::bind_method(_MD("get_stream:Stream"),&SpatialStreamPlayer::get_stream); + + ObjectTypeDB::bind_method(_MD("play"),&SpatialStreamPlayer::play); + ObjectTypeDB::bind_method(_MD("stop"),&SpatialStreamPlayer::stop); + + ObjectTypeDB::bind_method(_MD("is_playing"),&SpatialStreamPlayer::is_playing); + + ObjectTypeDB::bind_method(_MD("set_loop","enabled"),&SpatialStreamPlayer::set_loop); + ObjectTypeDB::bind_method(_MD("has_loop"),&SpatialStreamPlayer::has_loop); + + ObjectTypeDB::bind_method(_MD("get_stream_name"),&SpatialStreamPlayer::get_stream_name); + ObjectTypeDB::bind_method(_MD("get_loop_count"),&SpatialStreamPlayer::get_loop_count); + + ObjectTypeDB::bind_method(_MD("get_pos"),&SpatialStreamPlayer::get_pos); + ObjectTypeDB::bind_method(_MD("seek_pos","time"),&SpatialStreamPlayer::seek_pos); + + ADD_PROPERTY( PropertyInfo(Variant::OBJECT, "stream/stream", PROPERTY_HINT_RESOURCE_TYPE,"AudioStream"), _SCS("set_stream"),_SCS("get_stream") ); + ADD_PROPERTY( PropertyInfo(Variant::BOOL, "stream/loop"), _SCS("set_loop"),_SCS("has_loop") ); + +} + + +SpatialStreamPlayer::SpatialStreamPlayer() { + + loops=false; +} + +SpatialStreamPlayer::~SpatialStreamPlayer() { + +} + + diff --git a/scene/3d/spatial_stream_player.h b/scene/3d/spatial_stream_player.h new file mode 100644 index 00000000000..6b73a8ad1b9 --- /dev/null +++ b/scene/3d/spatial_stream_player.h @@ -0,0 +1,72 @@ +/*************************************************************************/ +/* spatial_stream_player.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 SPATIAL_STREAM_PLAYER_H +#define SPATIAL_STREAM_PLAYER_H + +#include "scene/resources/audio_stream.h" +#include "scene/3d/spatial_player.h" + + +class SpatialStreamPlayer : public SpatialPlayer { + + OBJ_TYPE(SpatialStreamPlayer,SpatialPlayer); + + Ref stream; + bool loops; +protected: + + void _notification(int p_what); + + static void _bind_methods(); +public: + + void set_stream(const Ref &p_stream); + Ref get_stream() const; + + void play(); + void stop(); + bool is_playing() const; + + void set_loop(bool p_enable); + bool has_loop() const; + + + String get_stream_name() const; + int get_loop_count() const; + + + float get_pos() const; + void seek_pos(float p_time); + + + SpatialStreamPlayer(); + ~SpatialStreamPlayer(); +}; + +#endif // SPATIAL_STREAM_PLAYER_H diff --git a/scene/3d/test_cube.cpp b/scene/3d/test_cube.cpp new file mode 100644 index 00000000000..265263a760d --- /dev/null +++ b/scene/3d/test_cube.cpp @@ -0,0 +1,53 @@ +/*************************************************************************/ +/* test_cube.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 "test_cube.h" +#include "servers/visual_server.h" + + + +AABB TestCube::get_aabb() const { + + return AABB( Vector3(-1,-1,-1), Vector3(2, 2, 2 ) ); +} +DVector TestCube::get_faces(uint32_t p_usage_flags) const { + + return DVector(); +} + + +TestCube::TestCube() { + + set_base(VisualServer::get_singleton()->get_test_cube()); +} + + +TestCube::~TestCube() { +} + + diff --git a/scene/3d/test_cube.h b/scene/3d/test_cube.h new file mode 100644 index 00000000000..8a5b566f1f6 --- /dev/null +++ b/scene/3d/test_cube.h @@ -0,0 +1,57 @@ +/*************************************************************************/ +/* test_cube.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 TEST_CUBE_H +#define TEST_CUBE_H + + +#include "scene/3d/visual_instance.h" +#include "rid.h" + + +/** + @author Juan Linietsky +*/ +class TestCube : public GeometryInstance { + + OBJ_TYPE( TestCube, GeometryInstance ); + + RID instance; + + +public: + + virtual AABB get_aabb() const; + virtual DVector get_faces(uint32_t p_usage_flags) const; + + TestCube(); + ~TestCube(); + +}; + +#endif diff --git a/scene/3d/visibility_notifier.cpp b/scene/3d/visibility_notifier.cpp new file mode 100644 index 00000000000..da01827c498 --- /dev/null +++ b/scene/3d/visibility_notifier.cpp @@ -0,0 +1,314 @@ +/*************************************************************************/ +/* visibility_notifier.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 "visibility_notifier.h" + +#include "scene/scene_string_names.h" +#include "scene/3d/physics_body.h" +#include "scene/animation/animation_player.h" +#include "scene/scene_string_names.h" + +void VisibilityNotifier::_enter_camera(Camera* p_camera) { + + ERR_FAIL_COND(cameras.has(p_camera)); + cameras.insert(p_camera); + if (cameras.size()==1) { + emit_signal(SceneStringNames::get_singleton()->enter_screen); + _screen_enter(); + } + emit_signal(SceneStringNames::get_singleton()->enter_camera,p_camera); + +} + +void VisibilityNotifier::_exit_camera(Camera* p_camera){ + + ERR_FAIL_COND(!cameras.has(p_camera)); + cameras.erase(p_camera); + + emit_signal(SceneStringNames::get_singleton()->exit_camera,p_camera); + if (cameras.size()==0) { + emit_signal(SceneStringNames::get_singleton()->exit_screen); + + _screen_exit(); + + } +} + + +void VisibilityNotifier::set_aabb(const AABB& p_aabb){ + + if (aabb==p_aabb) + return; + aabb=p_aabb; + + if (is_inside_world()) { + get_world()->_update_notifier(this,get_global_transform().xform(aabb)); + } + + _change_notify("aabb"); + update_gizmo(); +} + +AABB VisibilityNotifier::get_aabb() const{ + + return aabb; +} + + +void VisibilityNotifier::_notification(int p_what) { + + + switch(p_what) { + case NOTIFICATION_ENTER_WORLD: { + + + get_world()->_register_notifier(this,get_global_transform().xform(aabb)); + } break; + case NOTIFICATION_TRANSFORM_CHANGED: { + + + get_world()->_update_notifier(this,get_global_transform().xform(aabb)); + } break; + case NOTIFICATION_EXIT_WORLD: { + + get_world()->_remove_notifier(this); + } break; + } +} + + +bool VisibilityNotifier::is_on_screen() const { + + return cameras.size()!=0; +} + +void VisibilityNotifier::_bind_methods(){ + + ObjectTypeDB::bind_method(_MD("set_aabb","rect"),&VisibilityNotifier::set_aabb); + ObjectTypeDB::bind_method(_MD("get_aabb"),&VisibilityNotifier::get_aabb); + ObjectTypeDB::bind_method(_MD("is_on_screen"),&VisibilityNotifier::is_on_screen); + + ADD_PROPERTY( PropertyInfo(Variant::_AABB,"aabb"),_SCS("set_aabb"),_SCS("get_aabb")); + + ADD_SIGNAL( MethodInfo("enter_camera",PropertyInfo(Variant::OBJECT,"camera",PROPERTY_HINT_RESOURCE_TYPE,"Camera")) ); + ADD_SIGNAL( MethodInfo("exit_camera",PropertyInfo(Variant::OBJECT,"camera",PROPERTY_HINT_RESOURCE_TYPE,"Camera")) ); + ADD_SIGNAL( MethodInfo("enter_screen")); + ADD_SIGNAL( MethodInfo("exit_screen")); +} + + +VisibilityNotifier::VisibilityNotifier() { + + aabb=AABB(Vector3(-1,-1,-1),Vector3(2,2,2)); + +} + + + + + +////////////////////////////////////// + + +void VisibilityEnabler::_screen_enter() { + + + for(Map::Element *E=nodes.front();E;E=E->next()) { + + _change_node_state(E->key(),true); + } + + visible=true; +} + +void VisibilityEnabler::_screen_exit() { + + for(Map::Element *E=nodes.front();E;E=E->next()) { + + _change_node_state(E->key(),false); + } + + visible=false; +} + +void VisibilityEnabler::_find_nodes(Node* p_node) { + + + bool add=false; + Variant meta; + + if (enabler[ENABLER_FREEZE_BODIES]) { + + RigidBody *rb = p_node->cast_to(); + if (rb && ((rb->get_mode()==RigidBody::MODE_CHARACTER || (rb->get_mode()==RigidBody::MODE_RIGID && !rb->is_able_to_sleep())))) { + + + add=true; + meta=rb->get_mode(); + } + } + + if (enabler[ENABLER_PAUSE_ANIMATIONS]) { + + AnimationPlayer *ap = p_node->cast_to(); + if (ap) { + add=true; + } + } + + if (add) { + + p_node->connect(SceneStringNames::get_singleton()->exit_scene,this,"_node_removed",varray(p_node),CONNECT_ONESHOT); + nodes[p_node]=meta; + _change_node_state(p_node,false); + } + + for(int i=0;iget_child_count();i++) { + Node *c = p_node->get_child(i); + if (c->get_filename()!=String()) + continue; //skip, instance + + _find_nodes(c); + } + +} + +void VisibilityEnabler::_notification(int p_what){ + + if (p_what==NOTIFICATION_ENTER_SCENE) { + + if (get_scene()->is_editor_hint()) + return; + + + Node *from = this; + //find where current scene starts + while(from->get_parent() && from->get_filename()==String()) + from=from->get_parent(); + + _find_nodes(from); + + } + + if (p_what==NOTIFICATION_EXIT_SCENE) { + + if (get_scene()->is_editor_hint()) + return; + + + Node *from = this; + //find where current scene starts + + for (Map::Element *E=nodes.front();E;E=E->next()) { + + if (!visible) + _change_node_state(E->key(),true); + E->key()->disconnect(SceneStringNames::get_singleton()->exit_scene,this,"_node_removed"); + } + + nodes.clear(); + + } +} + +void VisibilityEnabler::_change_node_state(Node* p_node,bool p_enabled) { + + ERR_FAIL_COND(!nodes.has(p_node)); + + { + RigidBody *rb = p_node->cast_to(); + if (rb) { + + if (p_enabled) { + RigidBody::Mode mode = RigidBody::Mode(nodes[p_node].operator int()); + //rb->set_mode(mode); + rb->set_active(true); + } else { + //rb->set_mode(RigidBody::MODE_STATIC); + rb->set_active(false); + } + } + } + + { + AnimationPlayer *ap=p_node->cast_to(); + + if (ap) { + + ap->set_active(p_enabled); + } + } + +} + + +void VisibilityEnabler::_node_removed(Node* p_node) { + + if (!visible) + _change_node_state(p_node,true); + p_node->disconnect(SceneStringNames::get_singleton()->exit_scene,this,"_node_removed"); + nodes.erase(p_node); + +} + +void VisibilityEnabler::_bind_methods(){ + + ObjectTypeDB::bind_method(_MD("set_enabler","enabler","enabled"),&VisibilityEnabler::set_enabler); + ObjectTypeDB::bind_method(_MD("is_enabler_enabled","enabler"),&VisibilityEnabler::is_enabler_enabled); + ObjectTypeDB::bind_method(_MD("_node_removed"),&VisibilityEnabler::_node_removed); + + ADD_PROPERTYI( PropertyInfo(Variant::BOOL,"enabler/pause_animations"),_SCS("set_enabler"),_SCS("is_enabler_enabled"), ENABLER_PAUSE_ANIMATIONS ); + ADD_PROPERTYI( PropertyInfo(Variant::BOOL,"enabler/freeze_bodies"),_SCS("set_enabler"),_SCS("is_enabler_enabled"), ENABLER_FREEZE_BODIES); + + BIND_CONSTANT( ENABLER_FREEZE_BODIES ); + BIND_CONSTANT( ENABLER_PAUSE_ANIMATIONS ); + BIND_CONSTANT( ENABLER_MAX); +} + +void VisibilityEnabler::set_enabler(Enabler p_enabler,bool p_enable){ + + ERR_FAIL_INDEX(p_enabler,ENABLER_MAX); + enabler[p_enabler]=p_enable; + +} +bool VisibilityEnabler::is_enabler_enabled(Enabler p_enabler) const{ + + ERR_FAIL_INDEX_V(p_enabler,ENABLER_MAX,false); + return enabler[p_enabler]; + +} + +VisibilityEnabler::VisibilityEnabler() { + + for(int i=0;i cameras; + + AABB aabb; + +protected: + + virtual void _screen_enter() {} + virtual void _screen_exit() {} + + void _notification(int p_what); + static void _bind_methods(); +friend class SpatialIndexer; + + void _enter_camera(Camera* p_camera); + void _exit_camera(Camera* p_camera); + +public: + + void set_aabb(const AABB& p_aabb); + AABB get_aabb() const; + bool is_on_screen() const; + + VisibilityNotifier(); +}; + + +class VisibilityEnabler : public VisibilityNotifier { + + OBJ_TYPE(VisibilityEnabler,VisibilityNotifier); +public: + + enum Enabler { + ENABLER_PAUSE_ANIMATIONS, + ENABLER_FREEZE_BODIES, + ENABLER_MAX + }; + +protected: + + virtual void _screen_enter(); + virtual void _screen_exit(); + + bool visible; + + void _find_nodes(Node* p_node); + + Map nodes; + void _node_removed(Node* p_node); + bool enabler[ENABLER_MAX]; + + void _change_node_state(Node* p_node,bool p_enabled); + + void _notification(int p_what); + static void _bind_methods(); + +public: + + void set_enabler(Enabler p_enabler,bool p_enable); + bool is_enabler_enabled(Enabler p_enabler) const; + + VisibilityEnabler(); + +}; + +VARIANT_ENUM_CAST(VisibilityEnabler::Enabler); + + +#endif // VISIBILITY_NOTIFIER_H diff --git a/scene/3d/visual_instance.cpp b/scene/3d/visual_instance.cpp new file mode 100644 index 00000000000..9419996187d --- /dev/null +++ b/scene/3d/visual_instance.cpp @@ -0,0 +1,260 @@ +/*************************************************************************/ +/* visual_instance.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 "visual_instance.h" + +#include "servers/visual_server.h" +#include "room_instance.h" +#include "scene/scene_string_names.h" + +#include "skeleton.h" + +AABB VisualInstance::get_transformed_aabb() const { + + return get_global_transform().xform( get_aabb() ); +} + + + +void VisualInstance::_notification(int p_what) { + + switch(p_what) { + + case NOTIFICATION_ENTER_WORLD: { + + // CHECK ROOM + Spatial * parent = get_parent_spatial(); + Room *room=NULL; + + while(parent) { + + room = parent->cast_to(); + if (room) + break; + else + parent=parent->get_parent_spatial(); + } + + + if (room) { + + VisualServer::get_singleton()->instance_set_room(instance,room->get_instance()); + } + // CHECK SKELETON + Skeleton *skeleton=get_parent()?get_parent()->cast_to():NULL; + if (skeleton) + VisualServer::get_singleton()->instance_attach_skeleton( instance, skeleton->get_skeleton() ); + + VisualServer::get_singleton()->instance_set_scenario( instance, get_world()->get_scenario() ); + + } break; + case NOTIFICATION_TRANSFORM_CHANGED: { + + Transform gt = get_global_transform(); + VisualServer::get_singleton()->instance_set_transform(instance,gt); + } break; + case NOTIFICATION_EXIT_WORLD: { + + VisualServer::get_singleton()->instance_set_scenario( instance, RID() ); + VisualServer::get_singleton()->instance_set_room(instance,RID()); + VisualServer::get_singleton()->instance_attach_skeleton( instance, RID() ); + + + } break; + } +} + +RID VisualInstance::get_instance() const { + + return instance; +} + +RID VisualInstance::_get_visual_instance_rid() const { + + return instance; +} + +void VisualInstance::set_layer_mask(uint32_t p_mask) { + + layers=p_mask; + VisualServer::get_singleton()->instance_set_layer_mask(instance,p_mask); +} + +uint32_t VisualInstance::get_layer_mask() const { + + return layers; +} + + +void VisualInstance::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("_get_visual_instance_rid"),&VisualInstance::_get_visual_instance_rid); + ObjectTypeDB::bind_method(_MD("set_base","base"), &VisualInstance::set_base); + ObjectTypeDB::bind_method(_MD("set_layer_mask","mask"), &VisualInstance::set_layer_mask); + ObjectTypeDB::bind_method(_MD("get_layer_mask"), &VisualInstance::get_layer_mask); + + ADD_PROPERTY( PropertyInfo( Variant::INT, "layers",PROPERTY_HINT_ALL_FLAGS), _SCS("set_layer_mask"), _SCS("get_layer_mask")); + + +} + + +void VisualInstance::set_base(const RID& p_base) { + + VisualServer::get_singleton()->instance_set_base(instance,p_base); +} + + +VisualInstance::VisualInstance() +{ + + instance = VisualServer::get_singleton()->instance_create(); + VisualServer::get_singleton()->instance_attach_object_instance_ID( instance, get_instance_ID() ); + layers=1; +} + + +VisualInstance::~VisualInstance() { + + VisualServer::get_singleton()->free(instance); +} + + + + +void GeometryInstance::set_material_override(const Ref& p_material) { + + material_override=p_material; + VS::get_singleton()->instance_geometry_set_material_override(get_instance(),p_material.is_valid() ? p_material->get_rid() : RID()); +} + +Ref GeometryInstance::get_material_override() const{ + + return material_override; +} + + + +void GeometryInstance::set_draw_range_begin(float p_dist){ + + draw_begin=p_dist; + VS::get_singleton()->instance_geometry_set_draw_range(get_instance(),draw_begin,draw_end); +} + +float GeometryInstance::get_draw_range_begin() const{ + + return draw_begin; +} + + +void GeometryInstance::set_draw_range_end(float p_dist) { + + draw_end=p_dist; + VS::get_singleton()->instance_geometry_set_draw_range(get_instance(),draw_begin,draw_end); + +} + +float GeometryInstance::get_draw_range_end() const { + + return draw_end; +} + +void GeometryInstance::set_flag(Flags p_flag,bool p_value) { + + ERR_FAIL_INDEX(p_flag,FLAG_MAX); + if (flags[p_flag]==p_value) + return; + + flags[p_flag]=p_value; + VS::get_singleton()->instance_geometry_set_flag(get_instance(),(VS::InstanceFlags)p_flag,p_value); + if (p_flag==FLAG_VISIBLE) { + _change_notify("geometry/visible"); + emit_signal(SceneStringNames::get_singleton()->visibility_changed); + } + + +} + +bool GeometryInstance::get_flag(Flags p_flag) const{ + + ERR_FAIL_INDEX_V(p_flag,FLAG_MAX,false); + return flags[p_flag]; + +} + + +void GeometryInstance::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("set_material_override","material"), &GeometryInstance::set_material_override); + ObjectTypeDB::bind_method(_MD("get_material_override"), &GeometryInstance::get_material_override); + + ObjectTypeDB::bind_method(_MD("set_flag","flag","value"), &GeometryInstance::set_flag); + ObjectTypeDB::bind_method(_MD("get_flag","flag"), &GeometryInstance::get_flag); + + ObjectTypeDB::bind_method(_MD("set_draw_range_begin","mode"), &GeometryInstance::set_draw_range_begin); + ObjectTypeDB::bind_method(_MD("get_draw_range_begin"), &GeometryInstance::get_draw_range_begin); + + ObjectTypeDB::bind_method(_MD("set_draw_range_end","mode"), &GeometryInstance::set_draw_range_end); + ObjectTypeDB::bind_method(_MD("get_draw_range_end"), &GeometryInstance::get_draw_range_end); + + ADD_PROPERTYI( PropertyInfo( Variant::BOOL, "geometry/visible"), _SCS("set_flag"), _SCS("get_flag"),FLAG_VISIBLE); + ADD_PROPERTY( PropertyInfo( Variant::OBJECT, "geometry/material_override",PROPERTY_HINT_RESOURCE_TYPE,"Material"), _SCS("set_material_override"), _SCS("get_material_override")); + ADD_PROPERTYI( PropertyInfo( Variant::BOOL, "geometry/cast_shadow"), _SCS("set_flag"), _SCS("get_flag"),FLAG_CAST_SHADOW); + ADD_PROPERTYI( PropertyInfo( Variant::BOOL, "geometry/receive_shadows"), _SCS("set_flag"), _SCS("get_flag"),FLAG_RECEIVE_SHADOWS); + ADD_PROPERTY( PropertyInfo( Variant::INT, "geometry/range_begin",PROPERTY_HINT_RANGE,"0,32768,0.01"), _SCS("set_draw_range_begin"), _SCS("get_draw_range_begin")); + ADD_PROPERTY( PropertyInfo( Variant::INT, "geometry/range_end",PROPERTY_HINT_RANGE,"0,32768,0.01"), _SCS("set_draw_range_end"), _SCS("get_draw_range_end")); + ADD_PROPERTYI( PropertyInfo( Variant::BOOL, "geometry/billboard"), _SCS("set_flag"), _SCS("get_flag"),FLAG_BILLBOARD); + ADD_PROPERTYI( PropertyInfo( Variant::BOOL, "geometry/billboard_y"), _SCS("set_flag"), _SCS("get_flag"),FLAG_BILLBOARD_FIX_Y); + ADD_PROPERTYI( PropertyInfo( Variant::BOOL, "geometry/depth_scale"), _SCS("set_flag"), _SCS("get_flag"),FLAG_DEPH_SCALE); + ADD_PROPERTYI( PropertyInfo( Variant::BOOL, "geometry/visible_in_all_rooms"), _SCS("set_flag"), _SCS("get_flag"),FLAG_VISIBLE_IN_ALL_ROOMS); + + ADD_SIGNAL( MethodInfo("visibility_changed")); + + BIND_CONSTANT(FLAG_VISIBLE ); + BIND_CONSTANT(FLAG_CAST_SHADOW ); + BIND_CONSTANT(FLAG_RECEIVE_SHADOWS ); + BIND_CONSTANT(FLAG_BILLBOARD ); + BIND_CONSTANT(FLAG_BILLBOARD_FIX_Y ); + BIND_CONSTANT(FLAG_DEPH_SCALE ); + BIND_CONSTANT(FLAG_VISIBLE_IN_ALL_ROOMS ); + BIND_CONSTANT(FLAG_MAX ); + +} + +GeometryInstance::GeometryInstance() { + draw_begin=0; + draw_end=0; + flags[FLAG_VISIBLE]=true; + flags[FLAG_CAST_SHADOW]=true; + flags[FLAG_RECEIVE_SHADOWS]=true; + flags[FLAG_BILLBOARD]=false; + flags[FLAG_BILLBOARD_FIX_Y]=false; + flags[FLAG_DEPH_SCALE]=false; + flags[FLAG_VISIBLE_IN_ALL_ROOMS]=false; + +} diff --git a/scene/3d/visual_instance.h b/scene/3d/visual_instance.h new file mode 100644 index 00000000000..4e652912c64 --- /dev/null +++ b/scene/3d/visual_instance.h @@ -0,0 +1,126 @@ +/*************************************************************************/ +/* visual_instance.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 VISUAL_INSTANCE_H +#define VISUAL_INSTANCE_H + +#include "scene/3d/spatial.h" +#include "face3.h" +#include "rid.h" +#include "scene/resources/material.h" +/** + @author Juan Linietsky +*/ +class VisualInstance : public Spatial { + + OBJ_TYPE( VisualInstance, Spatial ); + OBJ_CATEGORY("3D Visual Nodes"); + + RID instance; + uint32_t layers; + + + RID _get_visual_instance_rid() const; + +protected: + + + void _notification(int p_what); + static void _bind_methods(); +public: + + enum GetFacesFlags { + FACES_SOLID=1, // solid geometry + FACES_ENCLOSING=2, + FACES_DYNAMIC=4 // dynamic object geometry + + }; + + RID get_instance() const; + virtual AABB get_aabb() const=0; + virtual DVector get_faces(uint32_t p_usage_flags) const=0; + + virtual AABB get_transformed_aabb() const; // helper + + void set_base(const RID& p_base); + + void set_layer_mask(uint32_t p_mask); + uint32_t get_layer_mask() const; + + + VisualInstance(); + ~VisualInstance(); + +}; + +class GeometryInstance : public VisualInstance { + + OBJ_TYPE( GeometryInstance, VisualInstance ); +public: + + enum Flags { + FLAG_VISIBLE=VS::INSTANCE_FLAG_VISIBLE, + FLAG_CAST_SHADOW=VS::INSTANCE_FLAG_CAST_SHADOW, + FLAG_RECEIVE_SHADOWS=VS::INSTANCE_FLAG_RECEIVE_SHADOWS, + FLAG_BILLBOARD=VS::INSTANCE_FLAG_BILLBOARD, + FLAG_BILLBOARD_FIX_Y=VS::INSTANCE_FLAG_BILLBOARD_FIX_Y, + FLAG_DEPH_SCALE=VS::INSTANCE_FLAG_DEPH_SCALE, + FLAG_VISIBLE_IN_ALL_ROOMS=VS::INSTANCE_FLAG_VISIBLE_IN_ALL_ROOMS, + FLAG_MAX=VS::INSTANCE_FLAG_MAX, + }; + + +private: + + bool flags[FLAG_MAX]; + Ref material_override; + float draw_begin; + float draw_end; +protected: + + static void _bind_methods(); +public: + + void set_flag(Flags p_flag,bool p_value); + bool get_flag(Flags p_flag) const; + + void set_draw_range_begin(float p_dist); + float get_draw_range_begin() const; + + void set_draw_range_end(float p_dist); + float get_draw_range_end() const; + + void set_material_override(const Ref& p_material); + Ref get_material_override() const; + + GeometryInstance(); +}; + +VARIANT_ENUM_CAST( GeometryInstance::Flags ); + +#endif diff --git a/scene/SCsub b/scene/SCsub new file mode 100644 index 00000000000..8c4f0499c42 --- /dev/null +++ b/scene/SCsub @@ -0,0 +1,22 @@ +Import('env') + +env.scene_sources=[] +env.add_source_files(env.scene_sources,"*.cpp") + +Export('env') + +SConscript('main/SCsub'); +SConscript('gui/SCsub'); +SConscript('3d/SCsub'); +SConscript('2d/SCsub'); +SConscript('animation/SCsub'); +SConscript('audio/SCsub'); +SConscript('resources/SCsub'); +SConscript('io/SCsub'); + + +lib = env.Library("scene",env.scene_sources) + +env.Prepend(LIBS=[lib]) + + diff --git a/scene/animation/SCsub b/scene/animation/SCsub new file mode 100644 index 00000000000..055d2f24741 --- /dev/null +++ b/scene/animation/SCsub @@ -0,0 +1,7 @@ +Import('env') + +env.add_source_files(env.scene_sources,"*.cpp") + +Export('env') + + diff --git a/scene/animation/animation_cache.cpp b/scene/animation/animation_cache.cpp new file mode 100644 index 00000000000..38cfe0d8a98 --- /dev/null +++ b/scene/animation/animation_cache.cpp @@ -0,0 +1,386 @@ +/*************************************************************************/ +/* animation_cache.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 "animation_cache.h" + + + +void AnimationCache::_node_exit_scene(Node *p_node) { + + //it is one shot, so it disconnects upon arrival + + ERR_FAIL_COND(!connected_nodes.has(p_node)); + + connected_nodes.erase(p_node); + + for(int i=0;iget()->disconnect("exit_scene",this,"_node_exit_scene"); + connected_nodes.erase(connected_nodes.front()); + } + path_cache.clear();; + cache_valid=false; + cache_dirty=true; +} + + +void AnimationCache::_update_cache() { + + cache_valid=false; + + ERR_FAIL_COND(!root); + ERR_FAIL_COND(!root->is_inside_scene()); + ERR_FAIL_COND(animation.is_null()); + + for(int i=0;iget_track_count();i++) { + + + + NodePath np = animation->track_get_path(i); + + Node *node = root->get_node(np); + if (!node) { + + path_cache.push_back(Path()); + ERR_EXPLAIN("Invalid Track Path in Animation: "+np); + ERR_CONTINUE(!node); + } + + + Path path; + + Ref res; + + if (np.get_subname_count()) { + + + if (animation->track_get_type(i)==Animation::TYPE_TRANSFORM) { + + path_cache.push_back(Path()); + ERR_EXPLAIN("Transform tracks can't have a subpath: "+np); + ERR_CONTINUE(animation->track_get_type(i)==Animation::TYPE_TRANSFORM); + + } + + RES res; + + for(int j=0;jget(np.get_subname(j)) : res->get(np.get_subname(j)); + if (res.is_null()) + break; + + } + + if (res.is_null()) { + + path_cache.push_back(Path()); + ERR_EXPLAIN("Invalid Track SubPath in Animation: "+np); + ERR_CONTINUE(res.is_null()); + } + + path.resource=res; + path.object=res.ptr(); + + } else { + + + if (animation->track_get_type(i)==Animation::TYPE_TRANSFORM) { + StringName property = np.get_property(); + String ps = property; + + + Spatial *sp = node->cast_to(); + + if (!sp) { + + path_cache.push_back(Path()); + ERR_EXPLAIN("Transform track not of type Spatial: "+np); + ERR_CONTINUE(!sp); + } + + if (ps!="") { + + Skeleton *sk = node->cast_to(); + if (!sk) { + + path_cache.push_back(Path()); + ERR_EXPLAIN("Property defined in Transform track, but not a Skeleton!: "+np); + ERR_CONTINUE(!sk); + } + + int idx = sk->find_bone(ps); + if (idx==-1) { + + path_cache.push_back(Path()); + ERR_EXPLAIN("Property defined in Transform track, but not a Skeleton Bone!: "+np); + ERR_CONTINUE(idx==-1); + + } + + path.bone_idx=idx; + path.skeleton=sk; + + } + + path.spatial=sp; + + } + + path.node=node; + path.object=node; + + } + + if (animation->track_get_type(i)==Animation::TYPE_VALUE) { + + if (np.get_property().operator String()=="") { + + path_cache.push_back(Path()); + ERR_EXPLAIN("Value Track lacks property: "+np); + ERR_CONTINUE(np.get_property().operator String()==""); + + } + + path.property=np.get_property(); + + } else if (animation->track_get_type(i)==Animation::TYPE_METHOD) { + + if (np.get_property().operator String()!="") { + + path_cache.push_back(Path()); + ERR_EXPLAIN("Method Track has property: "+np); + ERR_CONTINUE(np.get_property().operator String()!=""); + + } + + } + + + path.valid=true; + + path_cache.push_back(path); + + if (!connected_nodes.has(path.node)) { + connected_nodes.insert(path.node); + path.node->connect("exit_scene",this,"_node_exit_scene",Node::make_binds(path.node),CONNECT_ONESHOT); + } + + + + } + + + + cache_dirty=false; + cache_valid=true; +} + +void AnimationCache::set_track_transform(int p_idx,const Transform& p_transform) { + + if (cache_dirty) + _update_cache(); + + ERR_FAIL_COND(!cache_valid); + ERR_FAIL_INDEX(p_idx,path_cache.size()); + Path &p = path_cache[p_idx]; + if (!p.valid) + return; + + ERR_FAIL_COND(!p.node); + ERR_FAIL_COND(!p.spatial); + + if (p.skeleton) { + p.skeleton->set_bone_pose(p.bone_idx,p_transform); + } else { + p.spatial->set_transform(p_transform); + } + +} + +void AnimationCache::set_track_value(int p_idx,const Variant& p_value) { + + if (cache_dirty) + _update_cache(); + + ERR_FAIL_COND(!cache_valid); + ERR_FAIL_INDEX(p_idx,path_cache.size()); + Path &p = path_cache[p_idx]; + if (!p.valid) + return; + + ERR_FAIL_COND(!p.object); + p.object->set(p.property,p_value); +} + + +void AnimationCache::call_track(int p_idx,const StringName& p_method,const Variant** p_args,int p_argcount,Variant::CallError &r_error) { + + if (cache_dirty) + _update_cache(); + + + ERR_FAIL_COND(!cache_valid); + ERR_FAIL_INDEX(p_idx,path_cache.size()); + Path &p = path_cache[p_idx]; + if (!p.valid) + return; + + ERR_FAIL_COND(!p.object); + p.object->call(p_method,p_args,p_argcount,r_error); + +} + + +void AnimationCache::set_all(float p_time, float p_delta) { + + if (cache_dirty) + _update_cache(); + + ERR_FAIL_COND(!cache_valid); + + int tc = animation->get_track_count(); + for(int i=0;itrack_get_type(i)) { + + case Animation::TYPE_TRANSFORM: { + + Vector3 loc,scale; + Quat rot; + animation->transform_track_interpolate(i,p_time,&loc,&rot,&scale); + Transform tr( Matrix3(rot), loc ); + tr.basis.scale(scale); + + set_track_transform(i,tr); + + + } break; + case Animation::TYPE_VALUE: { + + if (animation->value_track_is_continuous(i)) { + Variant v = animation->value_track_interpolate(i,p_time); + set_track_value(i,v); + } else { + + List indices; + animation->value_track_get_key_indices(i,p_time,p_delta,&indices); + + for(List::Element *E=indices.front();E;E=E->next()) { + + Variant v = animation->track_get_key_value(i,E->get()); + set_track_value(i,v); + } + + } + + } break; + case Animation::TYPE_METHOD: { + + List indices; + animation->method_track_get_key_indices(i,p_time,p_delta,&indices); + + for(List::Element *E=indices.front();E;E=E->next()) { + + Vector args = animation->method_track_get_params(i,E->get()); + StringName name = animation->method_track_get_name(i,E->get()); + Variant::CallError err; + + if (!args.size()) { + + call_track(i,name,NULL,0,err); + } else { + + Vector argptrs; + argptrs.resize(args.size()); + for(int j=0;j& p_animation) { + + _clear_cache(); + + if (animation.is_valid()) + animation->disconnect("changed",this,"_animation_changed"); + + animation=p_animation; + + if (animation.is_valid()) + animation->connect("changed",this,"_animation_changed"); +} + +void AnimationCache::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("_node_exit_scene"),&AnimationCache::_node_exit_scene); + ObjectTypeDB::bind_method(_MD("_animation_changed"),&AnimationCache::_animation_changed); +} + +void AnimationCache::set_root(Node* p_root) { + + _clear_cache(); + root=p_root; +} + +AnimationCache::AnimationCache() { + + root=NULL; + cache_dirty=true; + cache_valid=false; +} diff --git a/scene/animation/animation_cache.h b/scene/animation/animation_cache.h new file mode 100644 index 00000000000..72ee8f18609 --- /dev/null +++ b/scene/animation/animation_cache.h @@ -0,0 +1,85 @@ +/*************************************************************************/ +/* animation_cache.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 ANIMATION_CACHE_H +#define ANIMATION_CACHE_H + +#include "scene/resources/animation.h" +#include "scene/3d/skeleton.h" + +class AnimationCache : public Object { + + OBJ_TYPE(AnimationCache,Object); + + struct Path { + + RES resource; + Object *object; + Skeleton *skeleton; // haxor + Node *node; + Spatial *spatial; + + int bone_idx; + StringName property; + bool valid; + Path() { object=NULL; skeleton=NULL; node=NULL; bone_idx=-1; valid=false; spatial=NULL; } + }; + + Set connected_nodes; + Vector path_cache; + + Node *root; + Ref animation; + bool cache_dirty; + bool cache_valid; + + void _node_exit_scene(Node *p_node); + + void _clear_cache(); + void _update_cache(); + void _animation_changed(); + +protected: + + static void _bind_methods(); + +public: + + void set_track_transform(int p_idx,const Transform& p_transform); + void set_track_value(int p_idx,const Variant& p_value); + void call_track(int p_idx,const StringName& p_method,const Variant** p_args,int p_argcount,Variant::CallError &r_error); + + void set_all(float p_time, float p_delta=0); + + void set_animation(const Ref& p_animation); + void set_root(Node* p_root); + + AnimationCache(); +}; + +#endif // ANIMATION_CACHE_H diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp new file mode 100644 index 00000000000..d0a56987623 --- /dev/null +++ b/scene/animation/animation_player.cpp @@ -0,0 +1,1258 @@ +/*************************************************************************/ +/* animation_player.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 "animation_player.h" + +#include "message_queue.h" +#include "scene/scene_string_names.h" + +bool AnimationPlayer::_set(const StringName& p_name, const Variant& p_value) { + + String name=p_name; + + if (name=="playback/speed" || name=="speed") { //bw compatibility + set_speed(p_value); + + } else if (name=="playback/active") { + set_active(p_value); + } else if (name.begins_with("playback/play")) { + + String which=p_value; + + + if (which=="[stop]") + stop(); + else + play(which); + } else if (name.begins_with("anims/")) { + + + String which=name.get_slice("/",1); + + add_animation(which,p_value); + } else if (name.begins_with("next/")) { + + + String which=name.get_slice("/",1); + animation_set_next(which,p_value); + + } else if (name=="blend_times") { + + Array array=p_value; + int len = array.size(); + ERR_FAIL_COND_V(len%3,false); + + + for(int i=0;i::Element *E=blend_times.front();E;E=E->next()) { + + array.set(idx*3+0,E->key().from); + array.set(idx*3+1,E->key().to); + array.set(idx*3+2,E->get()); + idx++; + } + r_ret=array; + } else if (name=="autoplay") { + r_ret=autoplay; + + + } else + return false; + + return true; +} + +void AnimationPlayer::_get_property_list( List *p_list) const { + + + + + List names; + + for( Map::Element *E=animation_set.front();E;E=E->next()) { + + p_list->push_back( PropertyInfo( Variant::OBJECT, "anims/"+String(E->key()), PROPERTY_HINT_RESOURCE_TYPE, "Animation",PROPERTY_USAGE_NOEDITOR) ); + if (E->get().next!=StringName()) + p_list->push_back( PropertyInfo( Variant::STRING, "next/"+String(E->key()), PROPERTY_HINT_NONE, "",PROPERTY_USAGE_NOEDITOR) ); + names.push_back(E->key()); + } + + + { + names.sort(); + names.push_front("[stop]"); + String hint; + for(List::Element *E=names.front();E;E=E->next()) { + + if (E!=names.front()) + hint+=","; + hint+=E->get(); + } + + p_list->push_back( PropertyInfo( Variant::STRING, "playback/play", PROPERTY_HINT_ENUM, hint,PROPERTY_USAGE_EDITOR) ); + p_list->push_back( PropertyInfo( Variant::BOOL, "playback/active", PROPERTY_HINT_NONE,"" ) ); + p_list->push_back( PropertyInfo( Variant::REAL, "playback/speed", PROPERTY_HINT_RANGE, "-64,64,0.01") ); + + } + + + + p_list->push_back( PropertyInfo( Variant::ARRAY, "blend_times", PROPERTY_HINT_NONE, "",PROPERTY_USAGE_NOEDITOR) ); + p_list->push_back( PropertyInfo( Variant::STRING, "autoplay", PROPERTY_HINT_NONE, "",PROPERTY_USAGE_NOEDITOR) ); + +} + + +void AnimationPlayer::_notification(int p_what) { + + switch(p_what) { + + case NOTIFICATION_ENTER_SCENE: { + + if (!processing) { + //make sure that a previous process state was not saved + //only process if "processing" is set + set_fixed_process(false); + set_process(false); + } + //_set_process(false); + clear_caches(); + } break; + case NOTIFICATION_READY: { + + if (!get_scene()->is_editor_hint() && animation_set.has(autoplay)) { + play(autoplay); + } + } break; + case NOTIFICATION_PROCESS: { + if (animation_process_mode==ANIMATION_PROCESS_FIXED) + break; + + if (processing) + _animation_process( get_process_delta_time() ); + } break; + case NOTIFICATION_FIXED_PROCESS: { + + if (animation_process_mode==ANIMATION_PROCESS_IDLE) + break; + + if (processing) + _animation_process( get_fixed_process_delta_time() ); + } break; + case NOTIFICATION_EXIT_SCENE: { + + stop_all(); + clear_caches(); + } break; + } +} + +void AnimationPlayer::_generate_node_caches(AnimationData* p_anim) { + + Node *parent = get_node(root); + + ERR_FAIL_COND(!parent); + + Animation *a=p_anim->animation.operator->(); + + p_anim->node_cache.resize( a->get_track_count() ); + + for (int i=0;iget_track_count();i++) { + + p_anim->node_cache[i]=NULL; + RES resource; + Node *child = parent->get_node_and_resource(a->track_get_path(i),resource); + ERR_CONTINUE(!child); // couldn't find the child node + uint32_t id=resource.is_valid()?resource->get_instance_ID():child->get_instance_ID(); + int bone_idx=-1; + + if (a->track_get_path(i).get_property() && child->cast_to()) { + + bone_idx = child->cast_to()->find_bone( a->track_get_path(i).get_property() ); + if (bone_idx==-1) { + + continue; + } + } + + { + if (!child->is_connected("exit_scene",this,"_node_removed")) + child->connect("exit_scene",this,"_node_removed",make_binds(child),CONNECT_ONESHOT); + } + + TrackNodeCacheKey key; + key.id=id; + key.bone_idx=bone_idx; + + if (node_cache_map.has(key)) { + + p_anim->node_cache[i]=&node_cache_map[key]; + } else { + + + node_cache_map[key]=TrackNodeCache(); + + p_anim->node_cache[i]=&node_cache_map[key]; + p_anim->node_cache[i]->node=child; + p_anim->node_cache[i]->resource=resource; + p_anim->node_cache[i]->node_2d=child->cast_to(); + if (a->track_get_type(i)==Animation::TYPE_TRANSFORM) { + // special cases and caches for transform tracks + + // cache spatial + p_anim->node_cache[i]->spatial=child->cast_to(); + // cache skeleton + p_anim->node_cache[i]->skeleton=child->cast_to(); + if (p_anim->node_cache[i]->skeleton) { + + StringName bone_name=a->track_get_path(i).get_property(); + if (bone_name.operator String()!="") { + + p_anim->node_cache[i]->bone_idx=p_anim->node_cache[i]->skeleton->find_bone(bone_name); + if (p_anim->node_cache[i]->bone_idx<0) { + // broken track (unexisting bone) + p_anim->node_cache[i]->skeleton=NULL; + p_anim->node_cache[i]->spatial=NULL; + printf("bone is %ls\n", String(bone_name).c_str()); + ERR_CONTINUE( p_anim->node_cache[i]->bone_idx<0 ); + } else { + } + } else { + // no property, just use spatialnode + p_anim->node_cache[i]->skeleton=NULL; + } + + } + } + } + + if (a->track_get_type(i)==Animation::TYPE_VALUE) { + + StringName property = a->track_get_path(i).get_property(); + if (!p_anim->node_cache[i]->property_anim.has(property)) { + + TrackNodeCache::PropertyAnim pa; + pa.prop=property; + pa.object=resource.is_valid()?(Object*)resource.ptr():(Object*)child; + pa.special=SP_NONE; + if (false && p_anim->node_cache[i]->node_2d) { + + if (pa.prop==SceneStringNames::get_singleton()->transform_pos) + pa.special=SP_NODE2D_POS; + else if (pa.prop==SceneStringNames::get_singleton()->transform_rot) + pa.special=SP_NODE2D_ROT; + else if (pa.prop==SceneStringNames::get_singleton()->transform_scale) + pa.special=SP_NODE2D_SCALE; + } + p_anim->node_cache[i]->property_anim[property]=pa; + } + } + } +} + + +void AnimationPlayer::_animation_process_animation(AnimationData* p_anim,float p_time, float p_delta,float p_interp, bool p_allow_discrete) { + + if (p_anim->node_cache.size() != p_anim->animation->get_track_count()) { + // animation hasn't been "node-cached" + _generate_node_caches(p_anim); + } + + + ERR_FAIL_COND( p_anim->node_cache.size() != p_anim->animation->get_track_count() ); + + + Animation *a=p_anim->animation.operator->(); + bool can_call = is_inside_scene() && !get_scene()->is_editor_hint(); + + for (int i=0;iget_track_count();i++) { + + TrackNodeCache *nc=p_anim->node_cache[i]; + + if (!nc) // no node cache for this track, skip it + continue; + + if (a->track_get_key_count(i)==0) + continue; // do nothing if track is empty + + switch(a->track_get_type(i)) { + + case Animation::TYPE_TRANSFORM: { + + if (!nc->spatial) + continue; + + + Vector3 loc; + Quat rot; + Vector3 scale; + + + Error err = a->transform_track_interpolate(i,p_time,&loc,&rot,&scale); + ERR_CONTINUE(err!=OK); //used for testing, should be removed + + + if (err!=OK) + continue; + + if (nc->accum_pass!=accum_pass) { + ERR_CONTINUE( cache_update_size >= NODE_CACHE_UPDATE_MAX ); + cache_update[cache_update_size++]=nc; + nc->accum_pass=accum_pass; + nc->loc_accum=loc; + nc->rot_accum=rot; + nc->scale_accum=scale; + + } else { + + nc->loc_accum=nc->loc_accum.linear_interpolate(loc,p_interp); + nc->rot_accum=nc->rot_accum.slerp(rot,p_interp); + nc->scale_accum=nc->scale_accum.linear_interpolate(scale,p_interp); + + } + + } break; + case Animation::TYPE_VALUE: { + + if (!nc->node) + continue; + + //StringName property=a->track_get_path(i).get_property(); + + Map::Element *E=nc->property_anim.find(a->track_get_path(i).get_property()); + ERR_CONTINUE(!E); //should it continue, or create a new one? + + TrackNodeCache::PropertyAnim *pa = &E->get(); + + + if (a->value_track_is_continuous(i) || p_delta==0) { + + + Variant value=a->value_track_interpolate(i,p_time); + if (p_delta==0 && value.get_type()==Variant::STRING) + continue; // doing this with strings is messy, should find another way + if (pa->accum_pass!=accum_pass) { + ERR_CONTINUE( cache_update_prop_size >= NODE_CACHE_UPDATE_MAX ); + cache_update_prop[cache_update_prop_size++]=pa; + pa->value_accum=value; + pa->accum_pass=accum_pass; + } else { + Variant::interpolate(pa->value_accum,value,p_interp,pa->value_accum); + } + + + } else if (p_allow_discrete) { + + List indices; + a->value_track_get_key_indices(i,p_time,p_delta,&indices); + + for(List::Element *F=indices.front();F;F=F->next()) { + + Variant value=a->track_get_key_value(i,F->get()); + switch(pa->special) { + + case SP_NONE: pa->object->set(pa->prop,value); break; //you are not speshul + case SP_NODE2D_POS: static_cast(pa->object)->set_pos(value); break; + case SP_NODE2D_ROT: static_cast(pa->object)->set_rot(Math::deg2rad(value)); break; + case SP_NODE2D_SCALE: static_cast(pa->object)->set_scale(value); break; + } + + } + + } + + } break; + case Animation::TYPE_METHOD: { + + if (!nc->node) + continue; + if (p_delta==0) + continue; + if (!p_allow_discrete) + break; + + List indices; + + a->method_track_get_key_indices(i,p_time,p_delta,&indices); + + for(List::Element *E=indices.front();E;E=E->next()) { + + StringName method=a->method_track_get_name(i,E->get()); + Vector params=a->method_track_get_params(i,E->get()); + + int s=params.size(); + + ERR_CONTINUE( s > VARIANT_ARG_MAX ); + if (can_call) { + MessageQueue::get_singleton()->push_call( + nc->node, + method, + s>=1 ? params[0] : Variant(), + s>=2 ? params[1] : Variant(), + s>=3 ? params[2] : Variant(), + s>=4 ? params[3] : Variant(), + s>=5 ? params[4] : Variant() + ); + } + } + + + } break; + } + } +} + +void AnimationPlayer::_animation_process_data(PlaybackData &cd,float p_delta,float p_blend) { + + float delta=p_delta*speed_scale*cd.speed_scale; + bool backwards=delta<0; + float next_pos=cd.pos+delta; + + float len=cd.from->animation->get_length(); + bool loop=cd.from->animation->has_loop(); + + if (!loop) { + + if (next_pos<0) + next_pos=0; + else if (next_pos>len) + next_pos=len; + + // fix delta + delta=next_pos-cd.pos; + + if (&cd == &playback.current) { + + if (!backwards && cd.pos < len && next_pos==len /*&& playback.blend.empty()*/) { + //playback finished + end_notify=true; + } + + if (backwards && cd.pos > 0 && next_pos==0 /*&& playback.blend.empty()*/) { + //playback finished + end_notify=true; + } + + } + + } else { + + next_pos=Math::fposmod(next_pos,len); + + } + + cd.pos=next_pos; + + _animation_process_animation(cd.from,cd.pos,delta,p_blend,&cd == &playback.current); + + + +} +void AnimationPlayer::_animation_process2(float p_delta) { + + + Playback &c=playback; + + float prev_blend=1.0; + accum_pass++; + + int pop_count=1; + int pop=0; // if >0, then amount of elements to pop from the back + + + for (List::Element *E=c.blend.back();E;E=E->prev(),pop_count++) { + + Blend& b=E->get(); + _animation_process_data(b.data,p_delta,prev_blend); + + prev_blend=1.0-b.blend_left/b.blend_time; + + b.blend_left-=Math::absf(speed_scale*p_delta); + + if (b.blend_left<0) { + + pop=pop_count; + } + } + + while(pop--) { + + c.blend.pop_back(); + } + + + _animation_process_data(c.current,p_delta,prev_blend); + +} + +void AnimationPlayer::_animation_update_transforms() { + + + for (int i=0;iaccum_pass!=accum_pass ); + + if (nc->spatial) { + + Transform t; + t.origin=nc->loc_accum; + t.basis=nc->rot_accum; + t.basis.scale( nc->scale_accum ); + + if (nc->skeleton && nc->bone_idx>=0) { + + nc->skeleton->set_bone_pose( nc->bone_idx, t ); + + } else if (nc->spatial) { + + nc->spatial->set_transform(t); + } + } + + } + + cache_update_size=0; + + for (int i=0;iaccum_pass!=accum_pass ); + +#if 1 + switch(pa->special) { + + + case SP_NONE: pa->object->set(pa->prop,pa->value_accum); break; //you are not speshul + case SP_NODE2D_POS: static_cast(pa->object)->set_pos(pa->value_accum); break; + case SP_NODE2D_ROT: static_cast(pa->object)->set_rot(Math::deg2rad(pa->value_accum)); break; + case SP_NODE2D_SCALE: static_cast(pa->object)->set_scale(pa->value_accum); break; + } +#else + + pa->object->set(pa->prop,pa->value_accum); +#endif + } + + cache_update_prop_size=0; +} + +void AnimationPlayer::_animation_process(float p_delta) { + + + +// bool any_active=false; + + if (playback.current.from) { + + end_notify=false; + _animation_process2(p_delta); + _animation_update_transforms(); + if (end_notify) { + if (queued.size()) { + String old = playback.assigned; + play(queued.front()->get()); + String new_name = playback.assigned; + queued.pop_front(); + emit_signal(SceneStringNames::get_singleton()->animation_changed, old, new_name); + } else { + //stop(); + playing = false; + _set_process(false); + emit_signal(SceneStringNames::get_singleton()->finished); + } + + } + + } else { + _set_process(false); + } + +} + + +Error AnimationPlayer::add_animation(const StringName& p_name, const Ref& p_animation) { + + ERR_EXPLAIN("Invalid animation name: "+String(p_name)); + ERR_FAIL_COND_V( String(p_name).find("/")!=-1 || String(p_name).find(":")!=-1 || String(p_name).find(",")!=-1 || String(p_name).find("[")!=-1, ERR_INVALID_PARAMETER ); + ERR_FAIL_COND_V( p_animation.is_null() , ERR_INVALID_PARAMETER ); + + //print_line("Add anim: "+String(p_name)+" name: "+p_animation->get_name()); + + if (animation_set.has(p_name)) { + + _unref_anim(animation_set[p_name].animation); + animation_set[p_name].animation=p_animation; + clear_caches(); + } else { + + AnimationData ad; + ad.animation=p_animation; + ad.name=p_name; + animation_set[p_name]=ad; + } + + _ref_anim(p_animation); + _change_notify(); + return OK; +} + +void AnimationPlayer::remove_animation(const StringName& p_name) { + + ERR_FAIL_COND(!animation_set.has(p_name) ); + + stop_all(); + _unref_anim(animation_set[p_name].animation); + animation_set.erase(p_name); + + clear_caches(); + _change_notify(); + +} + +void AnimationPlayer::_ref_anim(const Ref& p_anim) { + + if (used_anims.has(p_anim)) + used_anims[p_anim]++; + else { + used_anims[p_anim]=1; + Ref(p_anim)->connect("changed",this,"_animation_changed"); + } + +} + +void AnimationPlayer::_unref_anim(const Ref& p_anim) { + + ERR_FAIL_COND(!used_anims.has(p_anim)); + + + int & n = used_anims[p_anim]; + n--; + if (n==0) { + + Ref(p_anim)->disconnect("changed",this,"_animation_changed"); + used_anims.erase(p_anim); + } + +} + + +void AnimationPlayer::rename_animation(const StringName& p_name,const StringName& p_new_name) { + + + ERR_FAIL_COND(!animation_set.has(p_name) ); + ERR_FAIL_COND( String(p_new_name).find("/")!=-1 || String(p_new_name).find(":")!=-1 ); + ERR_FAIL_COND( animation_set.has(p_new_name) ); + + //print_line("Rename anim: "+String(p_name)+" name: "+String(p_new_name)); + + stop_all(); + AnimationData ad = animation_set[p_name]; + ad.name=p_new_name; + animation_set.erase(p_name); + animation_set[p_new_name]=ad; + + List to_erase; + Map to_insert; + for(Map::Element *E=blend_times.front();E;E=E->next()) { + + BlendKey bk=E->key(); + BlendKey new_bk=bk; + bool erase=false; + if (bk.from==p_name) { + new_bk.from=p_new_name; + erase=true; + } + if (bk.to==p_name) { + new_bk.to=p_new_name; + erase=true; + } + + if (erase) { + to_erase.push_back(bk); + to_insert[new_bk]=E->get(); + } + } + + while(to_erase.size()) { + + blend_times.erase(to_erase.front()->get()); + to_erase.pop_front();; + } + + while(to_insert.size()) { + blend_times[to_insert.front()->key()]=to_insert.front()->get(); + to_insert.erase(to_insert.front()); + } + + if (autoplay==p_name) + autoplay=p_new_name; + + clear_caches(); + _change_notify(); +} + + +bool AnimationPlayer::has_animation(const StringName& p_name) const { + + return animation_set.has(p_name); +} +Ref AnimationPlayer::get_animation(const StringName& p_name) const { + + ERR_FAIL_COND_V( !animation_set.has(p_name), Ref() ); + + const AnimationData& data = animation_set[p_name]; + + return data.animation; + +} +void AnimationPlayer::get_animation_list( List * p_animations) const { + + List anims; + + for( Map::Element *E=animation_set.front();E;E=E->next()) { + + anims.push_back(E->key()); + } + + anims.sort(); + + for(List::Element *E=anims.front();E;E=E->next()) { + + p_animations->push_back(E->get()); + } + +} + +void AnimationPlayer::set_blend_time(const StringName& p_animation1, const StringName& p_animation2, float p_time) { + + ERR_FAIL_COND(p_time<0); + + BlendKey bk; + bk.from=p_animation1; + bk.to=p_animation2; + if (p_time==0) + blend_times.erase(bk); + else + blend_times[bk]=p_time; + +} + + +float AnimationPlayer::get_blend_time( const StringName& p_animation1, const StringName& p_animation2) const { + + BlendKey bk; + bk.from=p_animation1; + bk.to=p_animation2; + + if (blend_times.has(bk)) + return blend_times[bk]; + else + return 0; + + +} + + +void AnimationPlayer::queue(const StringName& p_name) { + + if (!is_playing()) + play(p_name); + else + queued.push_back(p_name); +} + +void AnimationPlayer::clear_queue() { + queued.clear(); +}; + +void AnimationPlayer::play(const StringName& p_name, float p_custom_blend, float p_custom_scale,bool p_from_end) { + + //printf("animation is %ls\n", String(p_name).c_str()); + //ERR_FAIL_COND(!is_inside_scene()); + StringName name=p_name; + + if (String(name)=="") + name=playback.assigned; + + if (!animation_set.has(name)) { + ERR_EXPLAIN("Animation not found: "+name); + ERR_FAIL(); + } + + Playback &c=playback; + + if (c.current.from) { + + float blend_time=0; + // find if it can blend + BlendKey bk; + bk.from=c.current.from->name; + bk.to=name; + + if (p_custom_blend>=0) { + blend_time=p_custom_blend; + } else if (blend_times.has(bk)) { + + blend_time=blend_times[bk]; + } else { + + bk.from="*"; + if (blend_times.has(bk)) { + + blend_time=blend_times[bk]; + } else { + + bk.from=c.current.from->name; + bk.to="*"; + + if (blend_times.has(bk)) { + + blend_time=blend_times[bk]; + } + } + } + + if (p_custom_blend<0 && blend_time==0 && default_blend_time) + blend_time=default_blend_time; + if (blend_time>0) { + + Blend b; + b.data=c.current; + b.blend_time=b.blend_left=blend_time; + c.blend.push_back(b); + } + } + + c.current.pos=p_from_end ? c.current.from->animation->get_length() : 0; + c.current.from=&animation_set[name]; + c.current.speed_scale=p_custom_scale; + c.assigned=p_name; + + queued.clear(); + _set_process(true); // always process when starting an animation + playing = true; + + if (is_inside_scene() && get_scene()->is_editor_hint()) + return; // no next in this case + + + StringName next=animation_get_next(p_name); + if (next!=StringName()) { + queue(next); + } +} + +bool AnimationPlayer::is_playing() const { + + return playing; + /* + if (playback.current.from==NULL) + return false; + + float len=playback.current.from->animation->get_length(); + float pos = playback.current.pos; + bool loop=playback.current.from->animation->has_loop(); + if (!loop && pos >= len) { + return false; + }; + + return true; + */ +} +void AnimationPlayer::set_current_animation(const String& p_anim) { + + if (is_playing()) { + play(p_anim); + } else { + ERR_FAIL_COND(!animation_set.has(p_anim)); + playback.current.pos=0; + playback.current.from=&animation_set[p_anim]; + playback.assigned=p_anim; + } +} + +String AnimationPlayer::get_current_animation() const { + + return (playback.assigned); + +} + +void AnimationPlayer::stop() { + + Playback &c=playback; + c.blend.clear(); + c.current.from=NULL; + _set_process(false); + queued.clear(); + playing = false; +} + +void AnimationPlayer::stop_all() { + + stop(); + + _set_process(false); // always process when starting an animation + +} + +void AnimationPlayer::set_speed(float p_speed) { + + speed_scale=p_speed; + + +} +float AnimationPlayer::get_speed() const { + + return speed_scale; +} + +void AnimationPlayer::seek(float p_time,bool p_update) { + + + if (!playback.current.from) { + if (playback.assigned) + set_current_animation(playback.assigned); + ERR_FAIL_COND(!playback.current.from); + } + + playback.current.pos=p_time; + if (p_update) { + _animation_process(0); + } +} + +void AnimationPlayer::seek_delta(float p_time,float p_delta) { + + if (!playback.current.from) { + if (playback.assigned) + set_current_animation(playback.assigned); + ERR_FAIL_COND(!playback.current.from); + } + + + playback.current.pos=p_time-p_delta; + if (speed_scale!=0.0) + p_delta/=speed_scale; + _animation_process(p_delta); + //playback.current.pos=p_time; + +} + +bool AnimationPlayer::is_valid() const { + + return (playback.current.from); + +} + +float AnimationPlayer::get_current_animation_pos() const { + + ERR_FAIL_COND_V(!playback.current.from,0); + return playback.current.pos; +} + +float AnimationPlayer::get_current_animation_length() const { + + ERR_FAIL_COND_V(!playback.current.from,0); + return playback.current.from->animation->get_length(); +} + +void AnimationPlayer::_animation_changed() { + + clear_caches(); +} + +void AnimationPlayer::_node_removed(Node *p_node) { + + clear_caches(); // nodes contained here ar being removed, clear the caches +} + +void AnimationPlayer::clear_caches() { + + + node_cache_map.clear(); + + for( Map::Element *E=animation_set.front();E;E=E->next()) { + + E->get().node_cache.clear(); + } + + cache_update_size=0; + cache_update_prop_size=0; +} + +void AnimationPlayer::set_active(bool p_active) { + + if (active==p_active) + return; + + active=p_active; + _set_process(processing,true); + + +} + +bool AnimationPlayer::is_active() const { + + return active; +} + +StringName AnimationPlayer::find_animation(const Ref& p_animation) const { + + for( Map::Element *E=animation_set.front();E;E=E->next()) { + + if (E->get().animation==p_animation) + return E->key(); + } + + return ""; +} + +void AnimationPlayer::set_autoplay(const String& p_name) { + + autoplay=p_name; +} + +String AnimationPlayer::get_autoplay() const{ + + return autoplay; +} + +void AnimationPlayer::set_animation_process_mode(AnimationProcessMode p_mode) { + + if (animation_process_mode==p_mode) + return; + + bool pr = processing; + if (pr) + _set_process(false); + animation_process_mode=p_mode; + if (pr) + _set_process(true); + +} + +AnimationPlayer::AnimationProcessMode AnimationPlayer::get_animation_process_mode() const{ + + return animation_process_mode; +} + + +void AnimationPlayer::_set_process(bool p_process,bool p_force) { + + if (processing==p_process && !p_force) + return; + + switch(animation_process_mode) { + + case ANIMATION_PROCESS_FIXED: set_fixed_process(p_process && active); break; + case ANIMATION_PROCESS_IDLE: set_process(p_process && active); break; + } + + processing=p_process; +} + +void AnimationPlayer::animation_set_next(const StringName& p_animation, const StringName& p_next) { + + ERR_FAIL_COND(!animation_set.has(p_animation)); + animation_set[p_animation].next=p_next; + +} + +StringName AnimationPlayer::animation_get_next(const StringName& p_animation) const{ + + if (!animation_set.has(p_animation)) + return StringName(); + return animation_set[p_animation].next; + +} + +void AnimationPlayer::set_default_blend_time(float p_default) { + + default_blend_time=p_default; +} + +float AnimationPlayer::get_default_blend_time() const { + + return default_blend_time; +} + + +void AnimationPlayer::set_root(const NodePath& p_root) { + + root=p_root; + clear_caches(); +} + +NodePath AnimationPlayer::get_root() const { + + return root; +} + + +void AnimationPlayer::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("_node_removed"),&AnimationPlayer::_node_removed); + ObjectTypeDB::bind_method(_MD("_animation_changed"),&AnimationPlayer::_animation_changed); + + ObjectTypeDB::bind_method(_MD("add_animation","name","animation:Animation"),&AnimationPlayer::add_animation); + ObjectTypeDB::bind_method(_MD("remove_animation","name"),&AnimationPlayer::remove_animation); + ObjectTypeDB::bind_method(_MD("rename_animation","name","newname"),&AnimationPlayer::rename_animation); + ObjectTypeDB::bind_method(_MD("has_animation","name"),&AnimationPlayer::has_animation); + ObjectTypeDB::bind_method(_MD("get_animation:Animation","name"),&AnimationPlayer::get_animation); + ObjectTypeDB::bind_method(_MD("get_animation_list"),&AnimationPlayer::_get_animation_list); + + ObjectTypeDB::bind_method(_MD("set_blend_time","anim_from","anim_to","sec"),&AnimationPlayer::set_blend_time); + ObjectTypeDB::bind_method(_MD("get_blend_time","anim_from","anim_to"),&AnimationPlayer::get_blend_time); + + ObjectTypeDB::bind_method(_MD("set_default_blend_time","sec"),&AnimationPlayer::set_default_blend_time); + ObjectTypeDB::bind_method(_MD("get_default_blend_time"),&AnimationPlayer::get_default_blend_time); + + ObjectTypeDB::bind_method(_MD("play","name","custom_blend","custom_speed","from_end"),&AnimationPlayer::play,DEFVAL(""),DEFVAL(-1),DEFVAL(1.0),DEFVAL(false)); + ObjectTypeDB::bind_method(_MD("stop"),&AnimationPlayer::stop); + ObjectTypeDB::bind_method(_MD("stop_all"),&AnimationPlayer::stop_all); + ObjectTypeDB::bind_method(_MD("is_playing"),&AnimationPlayer::is_playing); + ObjectTypeDB::bind_method(_MD("set_current_animation","anim"),&AnimationPlayer::set_current_animation); + ObjectTypeDB::bind_method(_MD("get_current_animation"),&AnimationPlayer::get_current_animation); + ObjectTypeDB::bind_method(_MD("queue","name"),&AnimationPlayer::queue); + ObjectTypeDB::bind_method(_MD("clear_queue"),&AnimationPlayer::clear_queue); + + ObjectTypeDB::bind_method(_MD("set_active","active"),&AnimationPlayer::set_active); + ObjectTypeDB::bind_method(_MD("is_active"),&AnimationPlayer::is_active); + + ObjectTypeDB::bind_method(_MD("set_speed","speed"),&AnimationPlayer::set_speed); + ObjectTypeDB::bind_method(_MD("get_speed"),&AnimationPlayer::get_speed); + + ObjectTypeDB::bind_method(_MD("set_autoplay","name"),&AnimationPlayer::set_autoplay); + ObjectTypeDB::bind_method(_MD("get_autoplay"),&AnimationPlayer::get_autoplay); + + ObjectTypeDB::bind_method(_MD("set_root","path"),&AnimationPlayer::set_root); + ObjectTypeDB::bind_method(_MD("get_root"),&AnimationPlayer::get_root); + + ObjectTypeDB::bind_method(_MD("seek","pos_sec","update"),&AnimationPlayer::seek,DEFVAL(false)); + ObjectTypeDB::bind_method(_MD("get_pos"),&AnimationPlayer::get_current_animation_pos); + + ObjectTypeDB::bind_method(_MD("find_animation","animation:Animation"),&AnimationPlayer::find_animation); + + ObjectTypeDB::bind_method(_MD("clear_caches"),&AnimationPlayer::clear_caches); + + ObjectTypeDB::bind_method(_MD("set_animation_process_mode","mode"),&AnimationPlayer::set_animation_process_mode); + ObjectTypeDB::bind_method(_MD("get_animation_process_mode"),&AnimationPlayer::get_animation_process_mode); + + ObjectTypeDB::bind_method(_MD("get_current_animation_pos"),&AnimationPlayer::get_current_animation_pos); + ObjectTypeDB::bind_method(_MD("get_current_animation_length"),&AnimationPlayer::get_current_animation_length); + + + ADD_PROPERTY( PropertyInfo( Variant::INT, "playback/process_mode", PROPERTY_HINT_ENUM, "Fixed,Idle"), _SCS("set_animation_process_mode"), _SCS("get_animation_process_mode")); + ADD_PROPERTY( PropertyInfo( Variant::REAL, "playback/default_blend_time", PROPERTY_HINT_RANGE, "0,4096,0.01"), _SCS("set_default_blend_time"), _SCS("get_default_blend_time")); + ADD_PROPERTY( PropertyInfo( Variant::NODE_PATH, "root/root"), _SCS("set_root"), _SCS("get_root")); + + ADD_SIGNAL( MethodInfo("finished") ); + ADD_SIGNAL( MethodInfo("animation_changed", PropertyInfo(Variant::STRING,"old_name"), PropertyInfo(Variant::STRING,"new_name")) ); + + BIND_CONSTANT( ANIMATION_PROCESS_FIXED ); + BIND_CONSTANT( ANIMATION_PROCESS_IDLE ); +} + +AnimationPlayer::AnimationPlayer() { + + + accum_pass=1; + cache_update_size=0; + cache_update_prop_size=0; + speed_scale=1; + end_notify=false; + animation_process_mode=ANIMATION_PROCESS_IDLE; + processing=false; + default_blend_time=0; + root=NodePath(".."); + playing = false; + active=true; +} + + +AnimationPlayer::~AnimationPlayer() +{ +} + + diff --git a/scene/animation/animation_player.h b/scene/animation/animation_player.h new file mode 100644 index 00000000000..51c000d4d8f --- /dev/null +++ b/scene/animation/animation_player.h @@ -0,0 +1,300 @@ +/*************************************************************************/ +/* animation_player.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 ANIMATION_PLAYER_H +#define ANIMATION_PLAYER_H + + +#include "scene/resources/animation.h" +#include "scene/3d/spatial.h" +#include "scene/3d/skeleton.h" +#include "scene/main/misc.h" +#include "scene/2d/node_2d.h" +/** + @author Juan Linietsky +*/ + +class AnimationPlayer : public Node { + OBJ_TYPE( AnimationPlayer, Node ); + OBJ_CATEGORY("Animation Nodes"); + +public: + + enum AnimationProcessMode { + ANIMATION_PROCESS_FIXED, + ANIMATION_PROCESS_IDLE, + }; + +private: + + enum { + + NODE_CACHE_UPDATE_MAX=1024, + BLEND_FROM_MAX=3 + }; + + + enum SpecialProperty { + SP_NONE, + SP_NODE2D_POS, + SP_NODE2D_ROT, + SP_NODE2D_SCALE, + }; + + struct TrackNodeCache { + + uint32_t id; + RES resource; + Node *node; + Spatial* spatial; + Node2D* node_2d; + Skeleton *skeleton; + int bone_idx; + // accumulated transforms + + Vector3 loc_accum; + Quat rot_accum; + Vector3 scale_accum; + uint64_t accum_pass; + + struct PropertyAnim { + + SpecialProperty special; //small optimization + StringName prop; + Object *object; + Variant value_accum; + uint64_t accum_pass; + PropertyAnim() { accum_pass=0; object=NULL; } + }; + + Map property_anim; + + + TrackNodeCache() { skeleton=NULL; spatial=NULL; node=NULL; accum_pass=0; bone_idx=-1; node_2d=NULL; } + + }; + + struct TrackNodeCacheKey { + + uint32_t id; + int bone_idx; + + inline bool operator<(const TrackNodeCacheKey& p_right) const { + + if (idp_right.id) + return false; + else + return bone_idx node_cache_map; + + TrackNodeCache* cache_update[NODE_CACHE_UPDATE_MAX]; + int cache_update_size; + TrackNodeCache::PropertyAnim* cache_update_prop[NODE_CACHE_UPDATE_MAX]; + int cache_update_prop_size; + Map,int> used_anims; + + uint64_t accum_pass; + float speed_scale; + float default_blend_time; + + + struct AnimationData { + String name; + StringName next; + Vector node_cache; + Ref animation; + + }; + + Map animation_set; + struct BlendKey { + + StringName from; + StringName to; + bool operator<(const BlendKey& bk) const { return from==bk.from?to blend_times; + + + struct PlaybackData { + + AnimationData* from; + float pos; + float speed_scale; + + PlaybackData() { + + pos=0; + speed_scale=1.0; + from=NULL; + + } + + }; + + struct Blend { + + PlaybackData data; + + float blend_time; + float blend_left; + + Blend() { + + blend_left=0; + blend_time=0; + } + }; + + struct Playback { + + List blend; + PlaybackData current; + StringName assigned; + } playback; + + List queued; + + bool end_notify; + + String autoplay; + AnimationProcessMode animation_process_mode; + bool processing; + bool active; + + NodePath root; + + void _animation_process_animation(AnimationData* p_anim,float p_time, float p_delta,float p_interp, bool p_allow_discrete=true); + + void _generate_node_caches(AnimationData* p_anim); + void _animation_process_data(PlaybackData &cd,float p_delta,float p_blend); + void _animation_process2(float p_delta); + void _animation_update_transforms(); + void _animation_process(float p_delta); + + void _node_removed(Node *p_node); + +// bind helpers + DVector _get_animation_list() const { + + List animations; + get_animation_list(&animations); + DVector ret; + while(animations.size()) { + + ret.push_back( animations.front()->get()); + animations.pop_front(); + } + return ret; + } + + void _animation_changed(); + void _ref_anim(const Ref& p_anim); + void _unref_anim(const Ref& p_anim); + + + void _set_process(bool p_process,bool p_force=false); + + bool playing; + +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 *p_list) const; + void _notification(int p_what); + + static void _bind_methods(); + +public: + + StringName find_animation(const Ref& p_animation) const; + + Error add_animation(const StringName& p_name, const Ref& p_animation); + void remove_animation(const StringName& p_name); + void rename_animation(const StringName& p_name,const StringName& p_new_name); + bool has_animation(const StringName& p_name) const; + Ref get_animation(const StringName& p_name) const; + void get_animation_list( List * p_animations) const; + + void set_blend_time(const StringName& p_animation1, const StringName& p_animation2, float p_time); + float get_blend_time( const StringName& p_animation1, const StringName& p_animation2) const; + + void animation_set_next(const StringName& p_animation, const StringName& p_next); + StringName animation_get_next(const StringName& p_animation) const; + + void set_default_blend_time(float p_default); + float get_default_blend_time() const; + + void play(const StringName& p_name=StringName(),float p_custom_blend=-1,float p_custom_scale=1.0,bool p_from_end=false); + void queue(const StringName& p_name); + void clear_queue(); + void stop(); + bool is_playing() const; + String get_current_animation() const; + void set_current_animation(const String& p_anim); + void stop_all(); + void set_active(bool p_active); + bool is_active() const; + bool is_valid() const; + + void set_speed(float p_speed); + float get_speed() const; + + void set_autoplay(const String& pname); + String get_autoplay() const; + + void set_animation_process_mode(AnimationProcessMode p_mode); + AnimationProcessMode get_animation_process_mode() const; + + void seek(float p_time,bool p_update=false); + void seek_delta(float p_time,float p_delta); + float get_current_animation_pos() const; + float get_current_animation_length() const; + + void set_root(const NodePath& p_root); + NodePath get_root() const; + + void clear_caches(); ///< must be called by hand if an animation was modified after added + + AnimationPlayer(); + ~AnimationPlayer(); + +}; + + +VARIANT_ENUM_CAST( AnimationPlayer::AnimationProcessMode ); + + +#endif diff --git a/scene/animation/animation_tree_player.cpp b/scene/animation/animation_tree_player.cpp new file mode 100644 index 00000000000..bd124746ba8 --- /dev/null +++ b/scene/animation/animation_tree_player.cpp @@ -0,0 +1,1782 @@ +/*************************************************************************/ +/* animation_tree_player.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 "animation_tree_player.h" +#include "animation_player.h" + + +bool AnimationTreePlayer::_set(const StringName& p_name, const Variant& p_value) { + + if (String(p_name)=="base_path") { + set_base_path(p_value); + return true; + } + + if (String(p_name)=="master_player") { + set_master_player(p_value); + return true; + } + + if (String(p_name)!="data") + return false; + + + Dictionary data=p_value; + + Array nodes=data.get_valid("nodes"); + + for(int i=0;i::Element *E=node_map.front();E;E=E->next()) { + + NodeBase *n = node_map[E->key()]; + + Dictionary node; + node["id"]=E->key(); + node["pos"]=n->pos; + + switch(n->type) { + case NODE_OUTPUT: node["type"]= "output"; break; + case NODE_ANIMATION: node["type"]= "animation"; break; + case NODE_ONESHOT: node["type"]= "oneshot"; break; + case NODE_MIX: node["type"]= "mix"; break; + case NODE_BLEND2: node["type"]= "blend2"; break; + case NODE_BLEND3: node["type"]= "blend3"; break; + case NODE_BLEND4: node["type"]= "blend4"; break; + case NODE_TIMESCALE: node["type"]= "timescale"; break; + case NODE_TIMESEEK: node["type"]= "timeseek"; break; + case NODE_TRANSITION: node["type"]= "transition"; break; + default: node["type"]= ""; break; + } + + switch(n->type) { + case NODE_OUTPUT: { + + } break; + case NODE_ANIMATION: { + AnimationNode *an = static_cast(n); + if (master!=NodePath() && an->from!="") { + node["from"]=an->from; + } else { + node["animation"]=an->animation; + } + } break; + case NODE_ONESHOT: { + OneShotNode *osn = static_cast(n); + node["fade_in"]=osn->fade_in; + node["fade_out"]=osn->fade_out; + node["mix"]=osn->mix; + node["autorestart"]=osn->autorestart; + node["autorestart_delay"]=osn->autorestart_delay; + node["autorestart_random_delay"]=osn->autorestart_random_delay; + + Array k; + List keys; + osn->filter.get_key_list(&keys); + k.resize(keys.size()); + int i=0; + for(List::Element *E=keys.front();E;E=E->next()) { + k[i++]=E->get(); + } + node["filter"]=k; + + } break; + case NODE_MIX: { + MixNode *mn = static_cast(n); + node["mix"]=mn->amount; + } break; + case NODE_BLEND2: { + Blend2Node *bn = static_cast(n); + node["blend"]=bn->value; + Array k; + List keys; + bn->filter.get_key_list(&keys); + k.resize(keys.size()); + int i=0; + for(List::Element *E=keys.front();E;E=E->next()) { + k[i++]=E->get(); + } + node["filter"]=k; + + } break; + case NODE_BLEND3: { + Blend3Node *bn = static_cast(n); + node["blend"]=bn->value; + } break; + case NODE_BLEND4: { + Blend4Node *bn = static_cast(n); + node["blend"]=bn->value; + + } break; + case NODE_TIMESCALE: { + TimeScaleNode *tsn = static_cast(n); + node["scale"]=tsn->scale; + } break; + case NODE_TIMESEEK: { + } break; + case NODE_TRANSITION: { + + TransitionNode *tn = static_cast(n); + node["xfade"]=tn->xfade; + Array transitions; + + for(int i=0;iinput_data.size();i++) { + + Dictionary d; + d["auto_advance"]=tn->input_data[i].auto_advance; + transitions.push_back(d); + + } + + node["transitions"]=transitions; + + } break; + default: {}; + } + + nodes.push_back(node); + } + + data["nodes"]=nodes; + //connectiosn + + List connections; + get_connection_list(&connections); + Array connections_arr; + connections_arr.resize(connections.size()*3); + + int idx=0; + for (List::Element *E=connections.front();E;E=E->next()) { + + connections_arr.set(idx+0,E->get().src_node); + connections_arr.set(idx+1,E->get().dst_node); + connections_arr.set(idx+2,E->get().dst_input); + + idx+=3; + } + + data["connections"]=connections_arr; + data["active"]=active; + data["master"]=master; + + r_ret=data; + + return true; + +} + +void AnimationTreePlayer::_get_property_list( List *p_list) const { + + p_list->push_back( PropertyInfo(Variant::NODE_PATH,"base_path" ) ); + p_list->push_back( PropertyInfo(Variant::NODE_PATH,"master_player" ) ); + p_list->push_back( PropertyInfo(Variant::DICTIONARY,"data",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_STORAGE|PROPERTY_USAGE_NETWORK) ); +} + + +void AnimationTreePlayer::_notification(int p_what) { + + switch(p_what) { + + case NOTIFICATION_READY: { + dirty_caches=true; + if (master!=NodePath()) { + _update_sources(); + } + } break; + case NOTIFICATION_PROCESS: { + _process_animation(); + } break; + } + +} + + +float AnimationTreePlayer::_process_node(const StringName& p_node,AnimationNode **r_prev_anim,float p_weight, float p_time, bool p_seek,const HashMap *p_filter, float p_reverse_weight) { + + ERR_FAIL_COND_V(!node_map.has(p_node), 0); + NodeBase *nb=node_map[p_node]; + + //transform to seconds... + + + switch(nb->type) { + + case NODE_OUTPUT: { + + NodeOut *on = static_cast(nb); + return _process_node(on->inputs[0].node,r_prev_anim,p_weight,p_time,p_seek); + + } break; + case NODE_ANIMATION: { + + AnimationNode *an = static_cast(nb); + + float rem = 0; + if (!an->animation.is_null()) { + + // float pos = an->time; +// float delta = p_time; + + // const Animation *a = an->animation.operator->(); + + if (p_seek) { + an->time=p_time; + an->step=0; + } else { + an->time+=p_time; + an->step=p_time; + } + + float anim_size = an->animation->get_length(); + + if (an->animation->has_loop()) { + + if (anim_size) + an->time=Math::fposmod(an->time,anim_size); + + } else if (an->time > anim_size) { + + an->time=anim_size; + } + + an->skip=true; + for (List::Element *E=an->tref.front();E;E=E->next()) { + + if (p_filter && p_filter->has(an->animation->track_get_path(E->get().local_track))) { + + if (p_reverse_weight<0) + E->get().weight=0; + else + E->get().weight=p_reverse_weight; + + } else { + E->get().weight=p_weight; + } + if (E->get().weight>CMP_EPSILON) + an->skip=false; + } + + rem = anim_size - an->time; + + } + + + if (!(*r_prev_anim)) + active_list=an; + else + (*r_prev_anim)->next=an; + + an->next=NULL; + *r_prev_anim=an; + + return rem; + + + } break; + case NODE_ONESHOT: { + + OneShotNode *osn = static_cast(nb); + + if (!osn->active) { + //make it as if this node doesn't exist, pass input 0 by. + return _process_node(osn->inputs[0].node,r_prev_anim,p_weight,p_time,p_seek,p_filter,p_reverse_weight); + } + + if (p_seek) + osn->time=p_time; + if (osn->start) + osn->time=0; + + float blend; + + if (osn->timefade_in) { + + if (osn->fade_in>0) + blend = osn->time/osn->fade_in; + else + blend=0; //wtf + + } else if (!osn->start && osn->remainingfade_out) { + + if (osn->fade_out) + blend=(osn->remaining/osn->fade_out); + else + blend=1.0; + } else + blend=1.0; + + float main_rem; + float os_rem; + + if (!osn->filter.empty()) { + + main_rem = _process_node(osn->inputs[0].node,r_prev_anim,(osn->mix?p_weight:p_weight*(1.0-blend)),p_time,p_seek,&osn->filter,p_weight); + os_rem = _process_node(osn->inputs[1].node,r_prev_anim,p_weight*blend,p_time,p_seek,&osn->filter,-1); + + } else { + + main_rem = _process_node(osn->inputs[0].node,r_prev_anim,(osn->mix?p_weight:p_weight*(1.0-blend)),p_time,p_seek); + os_rem = _process_node(osn->inputs[1].node,r_prev_anim,p_weight*blend,p_time,p_seek); + } + + if (osn->start) { + osn->remaining=os_rem; + osn->start=false; + } + + if (!p_seek) { + osn->time+=p_time; + osn->remaining-=p_time; + if (osn->remaining<0) + osn->active=false; + } + + return MAX(main_rem,osn->remaining); + } break; + case NODE_MIX: { + MixNode *mn = static_cast(nb); + + + float rem = _process_node(mn->inputs[0].node,r_prev_anim,p_weight,p_time,p_seek,p_filter,p_reverse_weight); + _process_node(mn->inputs[1].node,r_prev_anim,p_weight*mn->amount,p_time,p_seek,p_filter,p_reverse_weight); + return rem; + + } break; + case NODE_BLEND2: { + + Blend2Node *bn = static_cast(nb); + + float rem; + if (!bn->filter.empty()) { + + rem = _process_node(bn->inputs[0].node,r_prev_anim,p_weight*(1.0-bn->value),p_time,p_seek,&bn->filter,p_weight); + _process_node(bn->inputs[1].node,r_prev_anim,p_weight*bn->value,p_time,p_seek,&bn->filter,-1); + + } else { + rem = _process_node(bn->inputs[0].node,r_prev_anim,p_weight*(1.0-bn->value),p_time,p_seek,p_filter,p_reverse_weight*(1.0-bn->value)); + _process_node(bn->inputs[1].node,r_prev_anim,p_weight*bn->value,p_time,p_seek,p_filter,p_reverse_weight*bn->value); + } + + return rem; + } break; + case NODE_BLEND3: { + Blend3Node *bn = static_cast(nb); + + float rem; + + if (bn->value==0) { + rem = _process_node(bn->inputs[1].node,r_prev_anim,p_weight,p_time,p_seek,p_filter,p_reverse_weight); + } else if (bn->value>0) { + + rem = _process_node(bn->inputs[1].node,r_prev_anim,p_weight*(1.0-bn->value),p_time,p_seek,p_filter,p_reverse_weight*(1.0-bn->value)); + _process_node(bn->inputs[2].node,r_prev_anim,p_weight*bn->value,p_time,p_seek,p_filter,p_reverse_weight*bn->value); + + } else { + + rem = _process_node(bn->inputs[1].node,r_prev_anim,p_weight*(1.0+bn->value),p_time,p_seek,p_filter,p_reverse_weight*(1.0+bn->value)); + _process_node(bn->inputs[0].node,r_prev_anim,p_weight*-bn->value,p_time,p_seek,p_filter,p_reverse_weight*-bn->value); + } + + return rem; + } break; + case NODE_BLEND4: { + Blend4Node *bn = static_cast(nb); + + float rem = _process_node(bn->inputs[0].node,r_prev_anim,p_weight*(1.0-bn->value.x),p_time,p_seek,p_filter,p_reverse_weight*(1.0-bn->value.x)); + _process_node(bn->inputs[1].node,r_prev_anim,p_weight*bn->value.x,p_time,p_seek,p_filter,p_reverse_weight*bn->value.x); + float rem2 = _process_node(bn->inputs[2].node,r_prev_anim,p_weight*(1.0-bn->value.y),p_time,p_seek,p_filter,p_reverse_weight*(1.0-bn->value.y)); + _process_node(bn->inputs[3].node,r_prev_anim,p_weight*bn->value.y,p_time,p_seek,p_filter,p_reverse_weight*bn->value.y); + + return MAX(rem,rem2); + + } break; + case NODE_TIMESCALE: { + TimeScaleNode *tsn = static_cast(nb); + if (p_seek) + return _process_node(tsn->inputs[0].node,r_prev_anim,p_weight,p_time,true,p_filter,p_reverse_weight); + else + return _process_node(tsn->inputs[0].node,r_prev_anim,p_weight,p_time*tsn->scale,false,p_filter,p_reverse_weight); + + } break; + case NODE_TIMESEEK: { + + TimeSeekNode *tsn = static_cast(nb); + if (tsn->seek_pos>=0) { + + float res = _process_node(tsn->inputs[0].node,r_prev_anim,p_weight,tsn->seek_pos,true,p_filter,p_reverse_weight); + tsn->seek_pos=-1; + return res; + + } else + return _process_node(tsn->inputs[0].node,r_prev_anim,p_weight,p_time,p_seek); + + } break; + case NODE_TRANSITION: { + + TransitionNode *tn = static_cast(nb); + + if (tn->prev<0) { + + float rem = _process_node(tn->inputs[tn->current].node,r_prev_anim,p_weight,p_time,p_seek,p_filter,p_reverse_weight); + if (p_seek) + tn->time=p_time; + else + tn->time+=p_time; + + if (tn->input_data[tn->current].auto_advance && rem < tn->xfade) { + + tn->prev=tn->current; + tn->current++; + if (tn->current>=tn->inputs.size()) + tn->current=0; + tn->prev_xfading=tn->xfade; + tn->prev_time=tn->time; + tn->time=0; + tn->switched=true; + } + + + return rem; + } else { + + + float blend = tn->xfade? (tn->prev_xfading/tn->xfade) : 1; + + float rem; + + if (!p_seek && tn->switched) { //just switched + + rem = _process_node(tn->inputs[tn->current].node,r_prev_anim,p_weight*(1.0-blend),0,true,p_filter,p_reverse_weight*(1.0-blend)); + } else { + + rem = _process_node(tn->inputs[tn->current].node,r_prev_anim,p_weight*(1.0-blend),p_time,p_seek,p_filter,p_reverse_weight*(1.0-blend)); + + } + + tn->switched=false; + + //if (!p_seek) + + + if (p_seek) { + _process_node(tn->inputs[tn->prev].node,r_prev_anim,p_weight*blend,0,false,p_filter,p_reverse_weight*blend); + tn->time=p_time; + } else { + _process_node(tn->inputs[tn->prev].node,r_prev_anim,p_weight*blend,p_time,false,p_filter,p_reverse_weight*blend); + tn->time+=p_time; + tn->prev_xfading-=p_time; + if (tn->prev_xfading<0) { + + tn->prev=-1; + } + + } + + return rem; + } + + + } break; + default: {} + } + + + + return 0; +} + + +void AnimationTreePlayer::_process_animation() { + + if (!active) + return; + + if (last_error!=CONNECT_OK) + return; + + if (dirty_caches) + _recompute_caches(); + + + active_list=NULL; + AnimationNode *prev=NULL; + + if (reset_request) { + _process_node(out_name,&prev, 1.0, 0, true ); + reset_request=false; + } else + _process_node(out_name,&prev, 1.0, get_process_delta_time(), false ); + + if (dirty_caches) { + //some animation changed.. ignore this pass + return; + } + + //update the tracks.. + + + + /* STEP 1 CLEAR TRACKS */ + + for(TrackMap::Element *E=track_map.front();E;E=E->next()) { + + Track &t = E->get(); + + t.loc.zero(); + t.rot=Quat(); + t.scale.x=0; + t.scale.y=0; + t.scale.z=0; + } + + + /* STEP 2 PROCESS ANIMATIONS */ + + AnimationNode *anim_list=active_list; + Quat empty_rot; + + + int total = 0; + while(anim_list) { + + if (!anim_list->animation.is_null() && !anim_list->skip) { + ++total; + //check if animation is meaningful + Animation *a = anim_list->animation.operator->(); + + for(List::Element *E=anim_list->tref.front();E;E=E->next()) { + + + AnimationNode::TrackRef &tr = E->get(); + if (tr.track==NULL || tr.local_track<0 || tr.weight < CMP_EPSILON) + continue; + + float blend=tr.weight; + + switch(a->track_get_type(tr.local_track)) { + case Animation::TYPE_TRANSFORM: { ///< Transform a node or a bone. + + Vector3 loc; + Quat rot; + Vector3 scale; + a->transform_track_interpolate(tr.local_track,anim_list->time,&loc,&rot,&scale); + + tr.track->loc+=loc*blend; + + scale.x-=1.0; + scale.y-=1.0; + scale.z-=1.0; + tr.track->scale+=scale*blend; + + tr.track->rot = tr.track->rot * empty_rot.slerp(rot,blend); + + + } break; + case Animation::TYPE_VALUE: { ///< Set a value in a property, can be interpolated. + + if (a->value_track_is_continuous(tr.local_track)) { + Variant value = a->value_track_interpolate(tr.local_track,anim_list->time); + tr.track->node->set(tr.track->property,value); + } else { + + List indices; + a->value_track_get_key_indices(tr.local_track,anim_list->time,anim_list->step,&indices); + for(List::Element *E=indices.front();E;E=E->next()) { + + Variant value = a->track_get_key_value(tr.local_track,E->get()); + tr.track->node->set(tr.track->property,value); + } + } + } break; + case Animation::TYPE_METHOD: { ///< Call any method on a specific node. + + List indices; + a->method_track_get_key_indices(tr.local_track,anim_list->time,anim_list->step,&indices); + for(List::Element *E=indices.front();E;E=E->next()) { + + StringName method = a->method_track_get_name(tr.local_track,E->get()); + Vector args=a->method_track_get_params(tr.local_track,E->get()); + ERR_CONTINUE(args.size()!=VARIANT_ARG_MAX); + tr.track->node->call(method,args[0],args[1],args[2],args[3],args[4]); + } + } break; + } + + } + } + + anim_list=anim_list->next; + } + + /* STEP 3 APPLY TRACKS */ + + for(TrackMap::Element *E=track_map.front();E;E=E->next()) { + + Track &t = E->get(); + + if (!t.node) + continue; + //if (E->get()->t.type!=Animation::TYPE_TRANSFORM) + // continue; + + Transform xform; + xform.basis=t.rot; + xform.origin=t.loc; + + t.scale.x+=1.0; + t.scale.y+=1.0; + t.scale.z+=1.0; + xform.basis.scale(t.scale); + + if (t.bone_idx>=0) { + if (t.skeleton) + t.skeleton->set_bone_pose(t.bone_idx,xform); + + } else if (t.spatial) { + + t.spatial->set_transform(xform); + } + } + + + +} + + +void AnimationTreePlayer::add_node(NodeType p_type, const StringName& p_node) { + + ERR_FAIL_COND( p_type == NODE_OUTPUT ); + ERR_FAIL_COND( node_map.has(p_node)); + + NodeBase *n=NULL; + + switch(p_type) { + + case NODE_ANIMATION: { + + n = memnew( AnimationNode ); + } break; + case NODE_ONESHOT: { + + n = memnew( OneShotNode ); + + } break; + case NODE_MIX: { + n = memnew( MixNode ); + + } break; + case NODE_BLEND2: { + n = memnew( Blend2Node ); + + } break; + case NODE_BLEND3: { + n = memnew( Blend3Node ); + + } break; + case NODE_BLEND4: { + n = memnew( Blend4Node ); + + } break; + case NODE_TIMESCALE: { + n = memnew( TimeScaleNode ); + + + } break; + case NODE_TIMESEEK: { + n = memnew( TimeSeekNode ); + + } break; + case NODE_TRANSITION: { + n = memnew( TransitionNode ); + + + } break; + default: {} + } + + //n->name+=" "+itos(p_node); + node_map[p_node]=n; +} + + +StringName AnimationTreePlayer::node_get_input_source(const StringName& p_node,int p_input) const { + + ERR_FAIL_COND_V(!node_map.has(p_node),StringName()); + ERR_FAIL_INDEX_V( p_input,node_map[p_node]->inputs.size(),StringName() ); + return node_map[p_node]->inputs[p_input].node; + +} + + +int AnimationTreePlayer::node_get_input_count(const StringName& p_node) const { + + ERR_FAIL_COND_V(!node_map.has(p_node),-1); + return node_map[p_node]->inputs.size(); + +} +#define GET_NODE( m_type, m_cast )\ + ERR_FAIL_COND(!node_map.has(p_node));\ + ERR_EXPLAIN("Invalid parameter for node type.");\ + ERR_FAIL_COND(node_map[p_node]->type!=m_type);\ + m_cast *n = static_cast( node_map[p_node] );\ + + + +void AnimationTreePlayer::animation_node_set_animation(const StringName& p_node,const Ref& p_animation) { + + GET_NODE( NODE_ANIMATION, AnimationNode ); + n->animation=p_animation; + dirty_caches=true; + + +} + +void AnimationTreePlayer::animation_node_set_master_animation(const StringName& p_node,const String& p_master_animation) { + + GET_NODE( NODE_ANIMATION, AnimationNode ); + n->from=p_master_animation; + dirty_caches=true; + if (master!=NodePath()) + _update_sources(); + + +} + +void AnimationTreePlayer::oneshot_node_set_fadein_time(const StringName& p_node,float p_time) { + + GET_NODE( NODE_ONESHOT, OneShotNode ); + n->fade_in=p_time; + +} + + +void AnimationTreePlayer::oneshot_node_set_fadeout_time(const StringName& p_node,float p_time) { + + GET_NODE( NODE_ONESHOT, OneShotNode ); + n->fade_out=p_time; + + +} + +void AnimationTreePlayer::oneshot_node_set_mix_mode(const StringName& p_node,bool p_mix) { + + GET_NODE( NODE_ONESHOT, OneShotNode ); + n->mix=p_mix; +} + + +void AnimationTreePlayer::oneshot_node_set_autorestart(const StringName& p_node,bool p_active) { + + GET_NODE( NODE_ONESHOT, OneShotNode ); + n->autorestart=p_active; + +} + +void AnimationTreePlayer::oneshot_node_set_autorestart_delay(const StringName& p_node,float p_time) { + + GET_NODE( NODE_ONESHOT, OneShotNode ); + n->autorestart_delay=p_time; + +} +void AnimationTreePlayer::oneshot_node_set_autorestart_random_delay(const StringName& p_node,float p_time) { + + GET_NODE( NODE_ONESHOT, OneShotNode ); + n->autorestart_random_delay=p_time; + +} + +void AnimationTreePlayer::oneshot_node_start(const StringName& p_node) { + + GET_NODE( NODE_ONESHOT, OneShotNode ); + n->active=true; + n->start=true; + +} + + +void AnimationTreePlayer::oneshot_node_stop(const StringName& p_node) { + + GET_NODE( NODE_ONESHOT, OneShotNode ); + n->active=false; + +} + + +void AnimationTreePlayer::oneshot_node_set_filter_path(const StringName& p_node,const NodePath& p_filter,bool p_enable) { + + GET_NODE( NODE_ONESHOT, OneShotNode ); + + if (p_enable) + n->filter[p_filter]=true; + else + n->filter.erase(p_filter); + +} + +void AnimationTreePlayer::oneshot_node_set_get_filtered_paths(const StringName& p_node,List *r_paths) const{ + + GET_NODE( NODE_ONESHOT, OneShotNode ); + + n->filter.get_key_list(r_paths); +} + + +void AnimationTreePlayer::mix_node_set_amount(const StringName& p_node,float p_amount) { + + GET_NODE( NODE_MIX, MixNode ); + n->amount=p_amount; + +} + + +void AnimationTreePlayer::blend2_node_set_amount(const StringName& p_node,float p_amount) { + + GET_NODE( NODE_BLEND2, Blend2Node ); + n->value=p_amount; + +} + +void AnimationTreePlayer::blend2_node_set_filter_path(const StringName& p_node,const NodePath& p_filter,bool p_enable) { + + GET_NODE( NODE_BLEND2, Blend2Node ); + + if (p_enable) + n->filter[p_filter]=true; + else + n->filter.erase(p_filter); + +} + +void AnimationTreePlayer::blend2_node_set_get_filtered_paths(const StringName& p_node,List *r_paths) const{ + + GET_NODE( NODE_BLEND2, Blend2Node ); + + n->filter.get_key_list(r_paths); +} + + +void AnimationTreePlayer::blend3_node_set_amount(const StringName& p_node,float p_amount) { + + GET_NODE( NODE_BLEND3, Blend3Node ); + n->value=p_amount; + +} +void AnimationTreePlayer::blend4_node_set_amount(const StringName& p_node,const Vector2& p_amount) { + + GET_NODE( NODE_BLEND4, Blend4Node ); + n->value=p_amount; + +} +void AnimationTreePlayer::timescale_node_set_scale(const StringName& p_node,float p_scale) { + + + GET_NODE( NODE_TIMESCALE, TimeScaleNode ); + n->scale=p_scale; + +} +void AnimationTreePlayer::timeseek_node_seek(const StringName& p_node,float p_pos) { + + +// GET_NODE( NODE_TIMESEEK, TimeSeekNode ); +//hmm +} +void AnimationTreePlayer::transition_node_set_input_count(const StringName& p_node, int p_inputs) { + + + GET_NODE( NODE_TRANSITION, TransitionNode ); + ERR_FAIL_COND(p_inputs<1); + + n->inputs.resize(p_inputs); + n->input_data.resize(p_inputs); + last_error=_cycle_test(out_name); + +} +void AnimationTreePlayer::transition_node_set_input_auto_advance(const StringName& p_node, int p_input,bool p_auto_advance) { + + GET_NODE( NODE_TRANSITION, TransitionNode ); + ERR_FAIL_INDEX(p_input,n->input_data.size()); + + n->input_data[p_input].auto_advance=p_auto_advance; + +} +void AnimationTreePlayer::transition_node_set_xfade_time(const StringName& p_node, float p_time) { + + + GET_NODE( NODE_TRANSITION, TransitionNode ); + n->xfade=p_time; +} + + +void AnimationTreePlayer::transition_node_set_current(const StringName& p_node, int p_current) { + + GET_NODE( NODE_TRANSITION, TransitionNode ); + ERR_FAIL_INDEX(p_current,n->inputs.size()); + + if (n->current==p_current) + return; + + n->prev=n->current; + n->prev_xfading=n->xfade; + n->prev_time=n->time; + n->time=0; + n->current=p_current; + +} + + +void AnimationTreePlayer::node_set_pos(const StringName& p_node, const Vector2& p_pos) { + + ERR_FAIL_COND(!node_map.has(p_node)); + node_map[p_node]->pos=p_pos; + +} + +AnimationTreePlayer::NodeType AnimationTreePlayer::node_get_type(const StringName& p_node) const { + + ERR_FAIL_COND_V(!node_map.has(p_node),NODE_OUTPUT); + return node_map[p_node]->type; + +} +Point2 AnimationTreePlayer::node_get_pos(const StringName& p_node) const { + + ERR_FAIL_COND_V(!node_map.has(p_node),Point2()); + return node_map[p_node]->pos; + + +} + +#define GET_NODE_V( m_type, m_cast, m_ret )\ + ERR_FAIL_COND_V(!node_map.has(p_node),m_ret);\ + ERR_EXPLAIN("Invalid parameter for node type.");\ + ERR_FAIL_COND_V(node_map[p_node]->type!=m_type,m_ret);\ + m_cast *n = static_cast( node_map[p_node] );\ + +Ref AnimationTreePlayer::animation_node_get_animation(const StringName& p_node) const { + + GET_NODE_V(NODE_ANIMATION, AnimationNode, Ref()); + return n->animation; + +} + +String AnimationTreePlayer::animation_node_get_master_animation(const StringName& p_node) const { + + GET_NODE_V(NODE_ANIMATION, AnimationNode, String()); + return n->from; + +} + +float AnimationTreePlayer::oneshot_node_get_fadein_time(const StringName& p_node) const { + + + GET_NODE_V(NODE_ONESHOT, OneShotNode, 0 ); + return n->fade_in; + +} + +float AnimationTreePlayer::oneshot_node_get_fadeout_time(const StringName& p_node) const { + + GET_NODE_V(NODE_ONESHOT, OneShotNode, 0 ); + return n->fade_out; + +} + +bool AnimationTreePlayer::oneshot_node_get_mix_mode(const StringName& p_node) const { + + GET_NODE_V(NODE_ONESHOT, OneShotNode, 0 ); + return n->mix; + +} +bool AnimationTreePlayer::oneshot_node_has_autorestart(const StringName& p_node) const { + + GET_NODE_V(NODE_ONESHOT, OneShotNode, 0 ); + return n->autorestart; + +} +float AnimationTreePlayer::oneshot_node_get_autorestart_delay(const StringName& p_node) const { + + GET_NODE_V(NODE_ONESHOT, OneShotNode, 0 ); + return n->autorestart_delay; + +} +float AnimationTreePlayer::oneshot_node_get_autorestart_random_delay(const StringName& p_node) const { + + GET_NODE_V(NODE_ONESHOT, OneShotNode, 0 ); + return n->autorestart_random_delay; + +} + +bool AnimationTreePlayer::oneshot_node_is_active(const StringName& p_node) const { + + + GET_NODE_V(NODE_ONESHOT, OneShotNode, 0 ); + return n->active; + +} + +bool AnimationTreePlayer::oneshot_node_is_path_filtered(const StringName& p_node,const NodePath& p_path) const { + + GET_NODE_V(NODE_ONESHOT, OneShotNode, 0 ); + return n->filter.has(p_path); +} + + +float AnimationTreePlayer::mix_node_get_amount(const StringName& p_node) const { + + GET_NODE_V(NODE_MIX, MixNode, 0 ); + return n->amount; + +} +float AnimationTreePlayer::blend2_node_get_amount(const StringName& p_node) const { + + GET_NODE_V(NODE_BLEND2, Blend2Node, 0 ); + return n->value; + +} + +bool AnimationTreePlayer::blend2_node_is_path_filtered(const StringName& p_node,const NodePath& p_path) const { + + GET_NODE_V(NODE_BLEND2, Blend2Node, 0 ); + return n->filter.has(p_path); +} + +float AnimationTreePlayer::blend3_node_get_amount(const StringName& p_node) const { + + + GET_NODE_V(NODE_BLEND3, Blend3Node, 0 ); + return n->value; + +} +Vector2 AnimationTreePlayer::blend4_node_get_amount(const StringName& p_node) const { + + GET_NODE_V(NODE_BLEND4, Blend4Node, Vector2() ); + return n->value; + +} + +float AnimationTreePlayer::timescale_node_get_scale(const StringName& p_node) const { + + GET_NODE_V(NODE_TIMESCALE, TimeScaleNode, 0 ); + return n->scale; + +} + +void AnimationTreePlayer::transition_node_delete_input(const StringName& p_node, int p_input) { + + GET_NODE(NODE_TRANSITION, TransitionNode); + ERR_FAIL_INDEX(p_input,n->inputs.size()); + + if (n->inputs.size()<=1) + return; + + + n->inputs.remove(p_input); + n->input_data.remove(p_input); + last_error=_cycle_test(out_name); +} + + +int AnimationTreePlayer::transition_node_get_input_count(const StringName& p_node) const { + + GET_NODE_V(NODE_TRANSITION, TransitionNode, 0 ); + return n->inputs.size(); +} + +bool AnimationTreePlayer::transition_node_has_input_auto_advance(const StringName& p_node, int p_input) const { + + GET_NODE_V(NODE_TRANSITION, TransitionNode, false ); + ERR_FAIL_INDEX_V(p_input,n->inputs.size(),false); + return n->input_data[p_input].auto_advance; + +} +float AnimationTreePlayer::transition_node_get_xfade_time(const StringName& p_node) const { + + GET_NODE_V(NODE_TRANSITION, TransitionNode, 0 ); + return n->xfade; + +} + +int AnimationTreePlayer::transition_node_get_current(const StringName& p_node) const { + + GET_NODE_V(NODE_TRANSITION, TransitionNode, -1 ); + return n->current; + +} + + /*misc */ +void AnimationTreePlayer::get_node_list(List *p_node_list) const { + + for(Map::Element *E=node_map.front();E;E=E->next()) { + + p_node_list->push_back( E->key() ); + } +} + +void AnimationTreePlayer::remove_node(const StringName& p_node) { + + ERR_FAIL_COND( !node_map.has(p_node) ); + ERR_EXPLAIN("Node 0 (output) can't be removed."); + ERR_FAIL_COND( p_node == out_name ); + + for(Map::Element *E=node_map.front();E;E=E->next()) { + + NodeBase *nb = E->get(); + for(int i=0;iinputs.size();i++) { + + if (nb->inputs[i].node==p_node) + nb->inputs[i].node=StringName(); + } + } + + node_map.erase(p_node); + + // compute last error again, just in case + last_error=_cycle_test(out_name); + dirty_caches=true; +} + + +AnimationTreePlayer::ConnectError AnimationTreePlayer::_cycle_test(const StringName& p_at_node) { + + ERR_FAIL_COND_V(!node_map.has(p_at_node), CONNECT_INCOMPLETE); + + NodeBase *nb = node_map[p_at_node]; + if (nb->cycletest) + return CONNECT_CYCLE; + + + nb->cycletest=true; + + for(int i=0;iinputs.size();i++) { + if (nb->inputs[i].node==StringName()) + return CONNECT_INCOMPLETE; + + ConnectError _err = _cycle_test(nb->inputs[i].node); + if (_err) + return _err; + } + + return CONNECT_OK; +} + + +Error AnimationTreePlayer::connect(const StringName& p_src_node,const StringName& p_dst_node, int p_dst_input) { + + ERR_FAIL_COND_V( !node_map.has(p_src_node) , ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V( !node_map.has(p_dst_node) , ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V( p_src_node==p_dst_node , ERR_INVALID_PARAMETER); + +// NodeBase *src = node_map[p_src_node]; + NodeBase *dst = node_map[p_dst_node]; + ERR_FAIL_INDEX_V( p_dst_input, dst->inputs.size(), ERR_INVALID_PARAMETER); + +// int oldval = dst->inputs[p_dst_input].node; + + for(Map::Element *E=node_map.front();E;E=E->next()) { + + NodeBase *nb = E->get(); + for(int i=0;iinputs.size();i++) { + + if (nb->inputs[i].node==p_src_node) + nb->inputs[i].node=StringName(); + } + } + + dst->inputs[p_dst_input].node=p_src_node; + + for(Map::Element *E=node_map.front();E;E=E->next()) { + + NodeBase *nb = E->get(); + nb->cycletest=false; + } + + last_error=_cycle_test(out_name); + if (last_error) { + + if (last_error==CONNECT_INCOMPLETE) + return ERR_UNCONFIGURED; + else if (last_error==CONNECT_CYCLE) + return ERR_CYCLIC_LINK; + } + dirty_caches=true; + return OK; +} + +bool AnimationTreePlayer::is_connected(const StringName& p_src_node,const StringName& p_dst_node, int p_dst_input) const { + + ERR_FAIL_COND_V( !node_map.has(p_src_node) , false); + ERR_FAIL_COND_V( !node_map.has(p_dst_node) , false); + ERR_FAIL_COND_V( p_src_node==p_dst_node , false); + + NodeBase *dst = node_map[p_dst_node]; + + return dst->inputs[p_dst_input].node==p_src_node; + +} + +void AnimationTreePlayer::disconnect(const StringName& p_node, int p_input) { + + ERR_FAIL_COND( !node_map.has(p_node)); + + NodeBase *dst = node_map[p_node]; + ERR_FAIL_INDEX(p_input,dst->inputs.size()); + dst->inputs[p_input].node=StringName(); + last_error=CONNECT_INCOMPLETE; + dirty_caches=true; +} + + +void AnimationTreePlayer::get_connection_list( List *p_connections) const { + + for(Map::Element *E=node_map.front();E;E=E->next()) { + + NodeBase *nb = E->get(); + for(int i=0;iinputs.size();i++) { + + if (nb->inputs[i].node!=StringName()) { + Connection c; + c.src_node=nb->inputs[i].node; + c.dst_node=E->key(); + c.dst_input=i; + p_connections->push_back(c); + } + } + } +} + +AnimationTreePlayer::Track* AnimationTreePlayer::_find_track(const NodePath& p_path) { + + Node *parent=get_node(base_path); + ERR_FAIL_COND_V(!parent,NULL); + + Node *child=parent->get_node(p_path); + if (!child) { + String err = "Animation track references unknown Node: '"+String(p_path)+"'."; + WARN_PRINT(err.ascii().get_data()); + return NULL; + } + + ObjectID id=child->get_instance_ID(); + StringName property; + int bone_idx=-1; + + if (p_path.get_property()) { + + if (child->cast_to()) + bone_idx = child->cast_to()->find_bone( p_path.get_property() ); + if (bone_idx==-1) + property=p_path.get_property(); + } + + TrackKey key; + key.id=id; + key.bone_idx=bone_idx; + key.property=property; + + if (!track_map.has(key)) { + + Track tr; + tr.id=id; + tr.node=child; + tr.skeleton=child->cast_to(); + tr.spatial=child->cast_to(); + tr.bone_idx=bone_idx; + tr.property=property; + + track_map[key]=tr; + } + + return &track_map[key]; + +} + +void AnimationTreePlayer::_recompute_caches() { + + track_map.clear(); + _recompute_caches(out_name); + dirty_caches=false; +} + +void AnimationTreePlayer::_recompute_caches(const StringName& p_node) { + + ERR_FAIL_COND( !node_map.has(p_node) ); + + NodeBase *nb = node_map[p_node]; + + if (nb->type==NODE_ANIMATION) { + + AnimationNode *an = static_cast(nb); + an->tref.clear();; + + if (!an->animation.is_null()) { + + + Ref a = an->animation; + + for(int i=0;ianimation->get_track_count();i++) { + + + Track *tr = _find_track(a->track_get_path(i)); + if (!tr) + continue; + + AnimationNode::TrackRef tref; + tref.local_track=i; + tref.track=tr; + tref.weight=0; + + an->tref.push_back(tref); + + } + } + } + + for(int i=0;iinputs.size();i++) { + + _recompute_caches(nb->inputs[i].node); + } + +} + +void AnimationTreePlayer::recompute_caches() { + + dirty_caches=true; + +} + + + + /* playback */ + +void AnimationTreePlayer::set_active(bool p_active) { + + active=p_active; + set_process(active); +} + +bool AnimationTreePlayer::is_active() const { + + return active; + +} + +AnimationTreePlayer::ConnectError AnimationTreePlayer::get_last_error() const { + + return last_error; +} + +void AnimationTreePlayer::reset() { + + + reset_request=false; +} + + +void AnimationTreePlayer::set_base_path(const NodePath& p_path) { + + base_path=p_path; + recompute_caches(); +} + +NodePath AnimationTreePlayer::get_base_path() const{ + + return base_path; +} + +void AnimationTreePlayer::set_master_player(const NodePath& p_path) { + + if (p_path==master) + return; + + master=p_path; + _update_sources(); + recompute_caches(); +} + +NodePath AnimationTreePlayer::get_master_player() const{ + + return master; +} + +DVector AnimationTreePlayer::_get_node_list() { + + List nl; + get_node_list(&nl); + DVector ret; + ret.resize(nl.size()); + int idx=0; + for(List::Element *E=nl.front();E;E=E->next()) { + ret.set(idx++,E->get()); + } + + return ret; +} + + +void AnimationTreePlayer::_update_sources() { + + if (master==NodePath()) + return; + if (!is_inside_scene()) + return; + + Node *m = get_node(master); + if (!m) { + master=NodePath(); + ERR_FAIL_COND(!m); + } + + AnimationPlayer *ap = m->cast_to(); + + if (!ap) { + + master=NodePath(); + ERR_FAIL_COND(!ap); + } + + for (Map::Element *E=node_map.front();E;E=E->next()) { + + if (E->get()->type==NODE_ANIMATION) { + + AnimationNode *an = static_cast(E->get()); + + if (an->from!="") { + + an->animation = ap->get_animation(an->from); + } + } + } + + +} + +bool AnimationTreePlayer::node_exists(const StringName& p_name) const { + + return (node_map.has(p_name)); +} + +Error AnimationTreePlayer::node_rename(const StringName& p_node,const StringName& p_new_name) { + + if (p_new_name==p_node) + return OK; + ERR_FAIL_COND_V(!node_map.has(p_node),ERR_ALREADY_EXISTS); + ERR_FAIL_COND_V(node_map.has(p_new_name),ERR_ALREADY_EXISTS); + ERR_FAIL_COND_V(p_new_name==StringName(),ERR_INVALID_DATA); + ERR_FAIL_COND_V(p_node==out_name,ERR_INVALID_DATA); + ERR_FAIL_COND_V(p_new_name==out_name,ERR_INVALID_DATA); + + for(Map::Element *E=node_map.front();E;E=E->next()) { + + NodeBase *nb = E->get(); + for(int i=0;iinputs.size();i++) { + + if (nb->inputs[i].node==p_node) { + nb->inputs[i].node=p_new_name; + } + } + } + + node_map[p_new_name]=node_map[p_node]; + node_map.erase(p_node); + + return OK; + +} + + +void AnimationTreePlayer::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("add_node","type","id"),&AnimationTreePlayer::add_node); + + ObjectTypeDB::bind_method(_MD("node_exists","node"),&AnimationTreePlayer::node_exists); + ObjectTypeDB::bind_method(_MD("node_rename","node","new_name"),&AnimationTreePlayer::node_rename); + + ObjectTypeDB::bind_method(_MD("node_get_type","id"),&AnimationTreePlayer::node_get_type); + ObjectTypeDB::bind_method(_MD("node_get_input_count","id"),&AnimationTreePlayer::node_get_input_count); + ObjectTypeDB::bind_method(_MD("node_get_input_sourcre","id","idx"),&AnimationTreePlayer::node_get_input_source); + + ObjectTypeDB::bind_method(_MD("animation_node_set_animation","id","animation:Animation"),&AnimationTreePlayer::animation_node_set_animation); + ObjectTypeDB::bind_method(_MD("animation_node_get_animation:Animation","id"),&AnimationTreePlayer::animation_node_get_animation); + + ObjectTypeDB::bind_method(_MD("animation_node_set_master_animation","id","source"),&AnimationTreePlayer::animation_node_set_master_animation); + ObjectTypeDB::bind_method(_MD("animation_node_get_master_animation","id"),&AnimationTreePlayer::animation_node_get_master_animation); + + ObjectTypeDB::bind_method(_MD("oneshot_node_set_fadein_time","id","time_sec"),&AnimationTreePlayer::oneshot_node_set_fadein_time); + ObjectTypeDB::bind_method(_MD("oneshot_node_get_fadein_time","id"),&AnimationTreePlayer::oneshot_node_get_fadein_time); + + ObjectTypeDB::bind_method(_MD("oneshot_node_set_fadeout_time","id","time_sec"),&AnimationTreePlayer::oneshot_node_set_fadeout_time); + ObjectTypeDB::bind_method(_MD("oneshot_node_get_fadeout_time","id"),&AnimationTreePlayer::oneshot_node_get_fadeout_time); + + + ObjectTypeDB::bind_method(_MD("oneshot_node_set_autorestart","id","enable"),&AnimationTreePlayer::oneshot_node_set_autorestart); + ObjectTypeDB::bind_method(_MD("oneshot_node_set_autorestart_delay","id","delay_sec"),&AnimationTreePlayer::oneshot_node_set_autorestart_delay); + ObjectTypeDB::bind_method(_MD("oneshot_node_set_autorestart_random_delay","id","rand_sec"),&AnimationTreePlayer::oneshot_node_set_autorestart_random_delay); + + + ObjectTypeDB::bind_method(_MD("oneshot_node_has_autorestart","id"),&AnimationTreePlayer::oneshot_node_has_autorestart); + ObjectTypeDB::bind_method(_MD("oneshot_node_get_autorestart_delay","id"),&AnimationTreePlayer::oneshot_node_get_autorestart_delay); + ObjectTypeDB::bind_method(_MD("oneshot_node_get_autorestart_random_delay","id"),&AnimationTreePlayer::oneshot_node_get_autorestart_random_delay); + + ObjectTypeDB::bind_method(_MD("oneshot_node_start","id"),&AnimationTreePlayer::oneshot_node_start); + ObjectTypeDB::bind_method(_MD("oneshot_node_stop","id"),&AnimationTreePlayer::oneshot_node_stop); + ObjectTypeDB::bind_method(_MD("oneshot_node_is_active","id"),&AnimationTreePlayer::oneshot_node_is_active); + ObjectTypeDB::bind_method(_MD("oneshot_node_set_filter_path","id","path","enable"),&AnimationTreePlayer::oneshot_node_set_filter_path); + + ObjectTypeDB::bind_method(_MD("mix_node_set_amount","id","ratio"),&AnimationTreePlayer::mix_node_set_amount); + ObjectTypeDB::bind_method(_MD("mix_node_get_amount","id"),&AnimationTreePlayer::mix_node_get_amount); + + ObjectTypeDB::bind_method(_MD("blend2_node_set_amount","id","blend"),&AnimationTreePlayer::blend2_node_set_amount); + ObjectTypeDB::bind_method(_MD("blend2_node_get_amount","id"),&AnimationTreePlayer::blend2_node_get_amount); + ObjectTypeDB::bind_method(_MD("blend2_node_set_filter_path","id","path","enable"),&AnimationTreePlayer::blend2_node_set_filter_path); + + ObjectTypeDB::bind_method(_MD("blend3_node_set_amount","id","blend"),&AnimationTreePlayer::blend3_node_set_amount); + ObjectTypeDB::bind_method(_MD("blend3_node_get_amount","id"),&AnimationTreePlayer::blend3_node_get_amount); + + ObjectTypeDB::bind_method(_MD("blend4_node_set_amount","id","blend"),&AnimationTreePlayer::blend4_node_set_amount); + ObjectTypeDB::bind_method(_MD("blend4_node_get_amount","id"),&AnimationTreePlayer::blend4_node_get_amount); + + ObjectTypeDB::bind_method(_MD("timescale_node_set_scale","id","scale"),&AnimationTreePlayer::timescale_node_set_scale); + ObjectTypeDB::bind_method(_MD("timescale_node_get_scale","id"),&AnimationTreePlayer::timescale_node_get_scale); + + ObjectTypeDB::bind_method(_MD("timeseek_node_seek","id","pos_sec"),&AnimationTreePlayer::timeseek_node_seek); + + ObjectTypeDB::bind_method(_MD("transition_node_set_input_count","id","count"),&AnimationTreePlayer::transition_node_set_input_count); + ObjectTypeDB::bind_method(_MD("transition_node_get_input_count","id"),&AnimationTreePlayer::transition_node_get_input_count); + ObjectTypeDB::bind_method(_MD("transition_node_delete_input","id","input_idx"),&AnimationTreePlayer::transition_node_delete_input); + + ObjectTypeDB::bind_method(_MD("transition_node_set_input_auto_advance","id","input_idx","enable"),&AnimationTreePlayer::transition_node_set_input_auto_advance); + ObjectTypeDB::bind_method(_MD("transition_node_has_input_auto_advance","id","input_idx"),&AnimationTreePlayer::transition_node_has_input_auto_advance); + + ObjectTypeDB::bind_method(_MD("transition_node_set_xfade_time","id","time_sec"),&AnimationTreePlayer::transition_node_set_xfade_time); + ObjectTypeDB::bind_method(_MD("transition_node_get_xfade_time","id"),&AnimationTreePlayer::transition_node_get_xfade_time); + + ObjectTypeDB::bind_method(_MD("transition_node_set_current","id","input_idx"),&AnimationTreePlayer::transition_node_set_current); + ObjectTypeDB::bind_method(_MD("transition_node_get_current","id"),&AnimationTreePlayer::transition_node_get_current); + + + ObjectTypeDB::bind_method(_MD("node_set_pos","id","screen_pos"),&AnimationTreePlayer::node_set_pos); + ObjectTypeDB::bind_method(_MD("node_get_pos","id"),&AnimationTreePlayer::node_get_pos); + + ObjectTypeDB::bind_method(_MD("remove_node","id"),&AnimationTreePlayer::remove_node); + ObjectTypeDB::bind_method(_MD("connect","id","dst_id","dst_input_idx"),&AnimationTreePlayer::connect); + ObjectTypeDB::bind_method(_MD("is_connected","id","dst_id","dst_input_idx"),&AnimationTreePlayer::is_connected); + ObjectTypeDB::bind_method(_MD("disconnect","id","dst_input_idx"),&AnimationTreePlayer::disconnect); + + ObjectTypeDB::bind_method(_MD("set_active","enabled"),&AnimationTreePlayer::set_active); + ObjectTypeDB::bind_method(_MD("is_active"),&AnimationTreePlayer::is_active); + + ObjectTypeDB::bind_method(_MD("set_base_path","path"),&AnimationTreePlayer::set_base_path); + ObjectTypeDB::bind_method(_MD("get_base_path"),&AnimationTreePlayer::get_base_path); + + ObjectTypeDB::bind_method(_MD("get_node_list"),&AnimationTreePlayer::_get_node_list); + + ObjectTypeDB::bind_method(_MD("reset"),&AnimationTreePlayer::reset); + + ObjectTypeDB::bind_method(_MD("recompute_caches"),&AnimationTreePlayer::recompute_caches); + + BIND_CONSTANT( NODE_OUTPUT ); + BIND_CONSTANT( NODE_ANIMATION ); + BIND_CONSTANT( NODE_ONESHOT ); + BIND_CONSTANT( NODE_MIX ); + BIND_CONSTANT( NODE_BLEND2 ); + BIND_CONSTANT( NODE_BLEND3 ); + BIND_CONSTANT( NODE_BLEND4 ); + BIND_CONSTANT( NODE_TIMESCALE ); + BIND_CONSTANT( NODE_TIMESEEK ); + BIND_CONSTANT( NODE_TRANSITION ); +} + + +AnimationTreePlayer::AnimationTreePlayer() { + + active_list=NULL; + out = memnew( NodeOut ) ; + out_name="out"; + out->pos=Point2(40,40); + node_map.insert( out_name , out); + active=false; + dirty_caches=true; + reset_request=false; + last_error=CONNECT_INCOMPLETE; + base_path=String(".."); +} + + +AnimationTreePlayer::~AnimationTreePlayer() { + + while(node_map.size()) { + memdelete( node_map.front()->get() ); + node_map.erase( node_map.front() ); + } +} + + + diff --git a/scene/animation/animation_tree_player.h b/scene/animation/animation_tree_player.h new file mode 100644 index 00000000000..135e5f414e2 --- /dev/null +++ b/scene/animation/animation_tree_player.h @@ -0,0 +1,420 @@ +/*************************************************************************/ +/* animation_tree_player.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 ANIMATION_TREE_PLAYER_H +#define ANIMATION_TREE_PLAYER_H + + +#include "scene/resources/animation.h" +#include "scene/3d/spatial.h" +#include "scene/3d/skeleton.h" +#include "scene/main/misc.h" + + +class AnimationTreePlayer : public Node { + + OBJ_TYPE( AnimationTreePlayer, Node ); + OBJ_CATEGORY("Animation Nodes"); + +public: + + + enum NodeType { + + NODE_OUTPUT, + NODE_ANIMATION, + NODE_ONESHOT, + NODE_MIX, + NODE_BLEND2, + NODE_BLEND3, + NODE_BLEND4, + NODE_TIMESCALE, + NODE_TIMESEEK, + NODE_TRANSITION, + + NODE_MAX, + }; + + enum ConnectError { + + CONNECT_OK, + CONNECT_INCOMPLETE, + CONNECT_CYCLE + }; + +private: + + enum { + + DISCONNECTED=-1, + }; + + struct TrackKey { + + uint32_t id; + StringName property; + int bone_idx; + + inline bool operator<(const TrackKey& p_right) const { + + if (id==p_right.id) { + if (bone_idx==p_right.bone_idx) { + return property TrackMap; + + TrackMap track_map; + + + struct Input { + + StringName node; + //Input() { node=-1; } + }; + + struct NodeBase { + + bool cycletest; + + NodeType type; + Point2 pos; + + + Vector inputs; + + NodeBase() { cycletest = false; }; + virtual ~NodeBase() { cycletest=false; } + }; + + struct NodeOut : public NodeBase { + + NodeOut() { type=NODE_OUTPUT; inputs.resize(1); } + }; + + struct AnimationNode : public NodeBase { + + Ref animation; + + + struct TrackRef { + int local_track; + Track *track; + float weight; + }; + + uint64_t last_version; + List tref; + AnimationNode *next; + float time; + float step; + String from; + bool skip; + AnimationNode() { type=NODE_ANIMATION; next=NULL; last_version=0; skip=false; } + }; + + + struct OneShotNode : public NodeBase { + + bool active; + bool start; + float fade_in; + float fade_out; + + bool autorestart; + float autorestart_delay; + float autorestart_random_delay; + bool mix; + + + float time; + float remaining; + float autorestart_remaining; + + HashMap filter; + + OneShotNode() { type=NODE_ONESHOT; fade_in=0; fade_out=0; inputs.resize(2); autorestart=false; autorestart_delay=1; autorestart_remaining=0; mix=false; active=false; start=false;} + }; + + struct MixNode : public NodeBase { + + float amount; + MixNode() { type=NODE_MIX; inputs.resize(2); } + }; + + struct Blend2Node : public NodeBase { + + float value; + HashMap filter; + Blend2Node() { type=NODE_BLEND2; value=0; inputs.resize(2); } + }; + + struct Blend3Node : public NodeBase { + + float value; + Blend3Node() { type=NODE_BLEND3; value=0; inputs.resize(3); } + }; + + struct Blend4Node : public NodeBase { + + Point2 value; + Blend4Node() { type=NODE_BLEND4; inputs.resize(4); } + }; + + struct TimeScaleNode : public NodeBase { + + float scale; + TimeScaleNode() { type=NODE_TIMESCALE; scale=1; inputs.resize(1); } + }; + + struct TimeSeekNode : public NodeBase { + + float seek_pos; + + TimeSeekNode() { type=NODE_TIMESEEK; inputs.resize(1); seek_pos=-1; } + }; + + struct TransitionNode : public NodeBase { + + struct InputData { + + bool auto_advance; + InputData() { auto_advance=false; } + }; + + Vector input_data; + + float prev_time; + float prev_xfading; + int prev; + bool switched; + + + float time; + int current; + + float xfade; + + TransitionNode() { type=NODE_TRANSITION; xfade=0; inputs.resize(1); input_data.resize(1); current=0; prev=-1; prev_time=0; prev_xfading=0; switched=false; } + }; + + + void _update_sources(); + + StringName out_name; + NodeOut *out; + + + NodePath base_path; + NodePath master; + + ConnectError last_error; + AnimationNode *active_list; + bool active; + bool dirty_caches; + Map node_map; + + // return time left to finish animation + float _process_node(const StringName& p_node,AnimationNode **r_prev_anim, float p_weight,float p_step, bool p_seek=false,const HashMap *p_filter=NULL, float p_reverse_weight=0); + void _process_animation(); + bool reset_request; + + ConnectError _cycle_test(const StringName &p_at_node); + + Track* _find_track(const NodePath& p_path); + void _recompute_caches(); + void _recompute_caches(const StringName& p_node); + DVector _get_node_list(); + +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 *p_list) const; + void _notification(int p_what); + + static void _bind_methods(); + + +public: + + + void add_node(NodeType p_type, const StringName& p_node); // nodes must be >0 node 0 is built-in (exit) + bool node_exists(const StringName& p_name) const; + + Error node_rename(const StringName& p_node,const StringName& p_new_name); + int node_get_input_count(const StringName& p_node) const; + StringName node_get_input_source(const StringName& p_node,int p_input) const; + + /* ANIMATION NODE */ + void animation_node_set_animation(const StringName& p_node,const Ref& p_animation); + Ref animation_node_get_animation(const StringName& p_node) const; + void animation_node_set_master_animation(const StringName& p_node,const String& p_master_animation); + String animation_node_get_master_animation(const StringName& p_node) const; + + /* ONE SHOT NODE */ + + void oneshot_node_set_fadein_time(const StringName& p_node,float p_time); + void oneshot_node_set_fadeout_time(const StringName& p_node,float p_time); + + float oneshot_node_get_fadein_time(const StringName& p_node) const; + float oneshot_node_get_fadeout_time(const StringName& p_node) const; + + void oneshot_node_set_autorestart(const StringName& p_node,bool p_active); + void oneshot_node_set_autorestart_delay(const StringName& p_node,float p_time); + void oneshot_node_set_autorestart_random_delay(const StringName& p_node,float p_time); + + bool oneshot_node_has_autorestart(const StringName& p_node) const; + float oneshot_node_get_autorestart_delay(const StringName& p_node) const; + float oneshot_node_get_autorestart_random_delay(const StringName& p_node) const; + + void oneshot_node_set_mix_mode(const StringName& p_node,bool p_enabled); + bool oneshot_node_get_mix_mode(const StringName& p_node) const; + + void oneshot_node_start(const StringName& p_node); + void oneshot_node_stop(const StringName& p_node); + bool oneshot_node_is_active(const StringName& p_node) const; + + void oneshot_node_set_filter_path(const StringName& p_node,const NodePath& p_filter,bool p_enable); + void oneshot_node_set_get_filtered_paths(const StringName& p_node,List *r_paths) const; + bool oneshot_node_is_path_filtered(const StringName& p_node,const NodePath& p_path) const; + + + /* MIX/BLEND NODES */ + + void mix_node_set_amount(const StringName& p_node,float p_amount); + float mix_node_get_amount(const StringName& p_node) const; + + void blend2_node_set_amount(const StringName& p_node,float p_amount); + float blend2_node_get_amount(const StringName& p_node) const; + void blend2_node_set_filter_path(const StringName& p_node,const NodePath& p_filter,bool p_enable); + void blend2_node_set_get_filtered_paths(const StringName& p_node,List *r_paths) const; + bool blend2_node_is_path_filtered(const StringName& p_node,const NodePath& p_path) const; + + void blend3_node_set_amount(const StringName& p_node,float p_amount); + float blend3_node_get_amount(const StringName& p_node) const; + + void blend4_node_set_amount(const StringName& p_node,const Point2& p_amount); + Point2 blend4_node_get_amount(const StringName& p_node) const; + + /* TIMESCALE/TIMESEEK NODES */ + + void timescale_node_set_scale(const StringName& p_node,float p_scale); + float timescale_node_get_scale(const StringName& p_node) const; + + void timeseek_node_seek(const StringName& p_node,float p_pos); + + /* TRANSITION NODE */ + + void transition_node_set_input_count(const StringName& p_node, int p_inputs); // used for transition node + int transition_node_get_input_count(const StringName& p_node) const; + void transition_node_delete_input(const StringName& p_node, int p_input); // used for transition node + + void transition_node_set_input_auto_advance(const StringName& p_node, int p_input,bool p_auto_advance); // used for transition node + bool transition_node_has_input_auto_advance(const StringName& p_node, int p_input) const; + + void transition_node_set_xfade_time(const StringName& p_node, float p_time); // used for transition node + float transition_node_get_xfade_time(const StringName& p_node) const; + + void transition_node_set_current(const StringName& p_node, int p_current); + int transition_node_get_current(const StringName& p_node) const; + + + void node_set_pos(const StringName& p_node, const Vector2& p_pos); //for display + + /* GETS */ + Point2 node_get_pos(const StringName& p_node) const; //for display + + NodeType node_get_type(const StringName& p_node) const; + + void get_node_list(List *p_node_list) const; + void remove_node(const StringName& p_node); + + Error connect(const StringName& p_src_node,const StringName& p_dst_node, int p_dst_input); + bool is_connected(const StringName& p_src_node,const StringName& p_dst_node, int p_input) const; + void disconnect(const StringName& p_src_node, int p_input); + + void set_base_path(const NodePath& p_path); + NodePath get_base_path() const; + + void set_master_player(const NodePath& p_path); + NodePath get_master_player() const; + + struct Connection { + + StringName src_node; + StringName dst_node; + int dst_input; + }; + + void get_connection_list( List *p_connections) const; + + /* playback */ + + void set_active(bool p_active); + bool is_active() const; + + void reset(); + + void recompute_caches(); + + ConnectError get_last_error() const; + + AnimationTreePlayer(); + ~AnimationTreePlayer(); + +}; + +VARIANT_ENUM_CAST( AnimationTreePlayer::NodeType ); +#endif // ANIMATION_TREE_PLAYER_H + + diff --git a/scene/animation/transitioner.cpp b/scene/animation/transitioner.cpp new file mode 100644 index 00000000000..990f55f9cb6 --- /dev/null +++ b/scene/animation/transitioner.cpp @@ -0,0 +1,34 @@ +/*************************************************************************/ +/* transitioner.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 "transitioner.h" +/* +Transitioner::Transitioner() +{ +} +*/ diff --git a/scene/animation/transitioner.h b/scene/animation/transitioner.h new file mode 100644 index 00000000000..3cff5e6d49e --- /dev/null +++ b/scene/animation/transitioner.h @@ -0,0 +1,68 @@ +/*************************************************************************/ +/* transitioner.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 TRANSITIONER_H +#define TRANSITIONER_H + +/* +class Transitioner : public Node { + OBJ_TYPE(Transitioner, Node); +public: + enum Interpolation { + INTERPOLATION_NEAREST, + INTERPOLATION_LINEAR, + INTERPOLATION_CUBIC + }; + + enum TransitionType { + TYPE_PROPERTY, + TYPE_METHOD, + TYPE_TRANSITION + + }; + + + void create_transition(const String& p_transition, TransitionType p_type); + void transition_set_target(const NodePath& p_node,const String& p_target); + TransitionType transition_get_type() const; + void transition_add_key(const String& p_transition,float p_time, const Variant& p_key); + int transition_get_key_count(const String& p_transition) const; + Variant transition_get_key_time(const String& p_transition.int p_key_index) const + Variant transition_get_key(const String& p_transition,int p_key_index) const; + + void transition_remove_key(const String& p_transition, int p_key_index); + void transition_clear_keys(const String& p_transition); + void remove_transition(const String& p_transition); + + void transition_ + + + Transitioner(); +}; +*/ +#endif // TRANSITIONER_H diff --git a/scene/audio/SCsub b/scene/audio/SCsub new file mode 100644 index 00000000000..055d2f24741 --- /dev/null +++ b/scene/audio/SCsub @@ -0,0 +1,7 @@ +Import('env') + +env.add_source_files(env.scene_sources,"*.cpp") + +Export('env') + + diff --git a/scene/audio/event_player.cpp b/scene/audio/event_player.cpp new file mode 100644 index 00000000000..07162e42fd5 --- /dev/null +++ b/scene/audio/event_player.cpp @@ -0,0 +1,356 @@ +/*************************************************************************/ +/* event_player.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 "event_player.h" + + +void EventPlayer::_notification(int p_what) { + + switch(p_what) { + + case NOTIFICATION_ENTER_SCENE: { + + //set_idle_process(false); //don't annoy + if (playback.is_valid() && autoplay && !get_scene()->is_editor_hint()) + play(); + } break; + case NOTIFICATION_EXIT_SCENE: { + + stop(); //wathever it may be doing, stop + } break; + } +} + + + +void EventPlayer::set_stream(const Ref &p_stream) { + + stop(); + stream=p_stream; + if (stream.is_valid()) + playback=stream->instance_playback(); + + if (playback.is_valid()) { + + playback->set_loop(loops); + playback->set_paused(paused); + playback->set_volume(volume); + for(int i=0;i<(MIN(MAX_CHANNELS,stream->get_channel_count()));i++) + playback->set_channel_volume(i,channel_volume[i]); + } + + +} + +Ref EventPlayer::get_stream() const { + + return stream; +} + + +void EventPlayer::play() { + + ERR_FAIL_COND(!is_inside_scene()); + if (playback.is_null()) + return; + if (playback->is_playing()) { + AudioServer::get_singleton()->lock(); + stop(); + AudioServer::get_singleton()->unlock(); + } + + AudioServer::get_singleton()->lock(); + playback->play(); + AudioServer::get_singleton()->unlock(); + +} + +void EventPlayer::stop() { + + if (!is_inside_scene()) + return; + if (playback.is_null()) + return; + + AudioServer::get_singleton()->lock(); + playback->stop(); + AudioServer::get_singleton()->unlock(); +} + +bool EventPlayer::is_playing() const { + + if (playback.is_null()) + return false; + + return playback->is_playing(); +} + +void EventPlayer::set_loop(bool p_enable) { + + loops=p_enable; + if (playback.is_null()) + return; + playback->set_loop(loops); + +} +bool EventPlayer::has_loop() const { + + return loops; +} + +void EventPlayer::set_volume(float p_volume) { + + volume=p_volume; + if (playback.is_valid()) + playback->set_volume(volume); +} + +float EventPlayer::get_volume() const { + + return volume; +} + + +void EventPlayer::set_volume_db(float p_db) { + + if (p_db<-79) + set_volume(0); + else + set_volume(Math::db2linear(p_db)); +} + +float EventPlayer::get_volume_db() const { + + if (volume==0) + return -80; + else + return Math::linear2db(volume); +} + +void EventPlayer::set_pitch_scale(float p_pitch_scale) { + + pitch_scale=p_pitch_scale; + if (playback.is_valid()) + playback->set_pitch_scale(pitch_scale); +} + +float EventPlayer::get_pitch_scale() const { + + return pitch_scale; +} + +void EventPlayer::set_tempo_scale(float p_tempo_scale) { + + tempo_scale=p_tempo_scale; + if (playback.is_valid()) + playback->set_tempo_scale(tempo_scale); +} + +float EventPlayer::get_tempo_scale() const { + + return tempo_scale; +} + + +String EventPlayer::get_stream_name() const { + + if (stream.is_null()) + return ""; + return stream->get_name(); + +} + +int EventPlayer::get_loop_count() const { + + if (playback.is_null()) + return 0; + return playback->get_loop_count(); + +} + +float EventPlayer::get_pos() const { + + if (playback.is_null()) + return 0; + return playback->get_pos(); + +} + +float EventPlayer::get_length() const { + + if (stream.is_null()) + return 0; + return stream->get_length(); +} +void EventPlayer::seek_pos(float p_time) { + + if (playback.is_null()) + return; + return playback->seek_pos(p_time); + +} + +void EventPlayer::set_autoplay(bool p_enable) { + + autoplay=p_enable; +} + +bool EventPlayer::has_autoplay() const { + + return autoplay; +} + +void EventPlayer::set_paused(bool p_paused) { + + paused=p_paused; + if (playback.is_valid()) + playback->set_paused(p_paused); +} + +bool EventPlayer::is_paused() const { + + return paused; +} + +void EventPlayer::_set_play(bool p_play) { + + _play=p_play; + if (is_inside_scene()) { + if(_play) + play(); + else + stop(); + } + +} + +bool EventPlayer::_get_play() const{ + + return _play; +} + +void EventPlayer::set_channel_volume(int p_channel,float p_volume) { + + ERR_FAIL_INDEX(p_channel,MAX_CHANNELS); + channel_volume[p_channel]=p_volume; + if (playback.is_valid()) + playback->set_channel_volume(p_channel,p_volume); +} + +float EventPlayer::get_channel_volume(int p_channel) const{ + + ERR_FAIL_INDEX_V(p_channel,MAX_CHANNELS,0); + return channel_volume[p_channel]; + +} + +float EventPlayer::get_channel_last_note_time(int p_channel) const { + + if (playback.is_valid()) + return playback->get_last_note_time(p_channel); + + return 0; +} + +void EventPlayer::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("set_stream","stream:Stream"),&EventPlayer::set_stream); + ObjectTypeDB::bind_method(_MD("get_stream:Stream"),&EventPlayer::get_stream); + + ObjectTypeDB::bind_method(_MD("play"),&EventPlayer::play); + ObjectTypeDB::bind_method(_MD("stop"),&EventPlayer::stop); + + ObjectTypeDB::bind_method(_MD("is_playing"),&EventPlayer::is_playing); + + ObjectTypeDB::bind_method(_MD("set_paused","paused"),&EventPlayer::set_paused); + ObjectTypeDB::bind_method(_MD("is_paused"),&EventPlayer::is_paused); + + ObjectTypeDB::bind_method(_MD("set_loop","enabled"),&EventPlayer::set_loop); + ObjectTypeDB::bind_method(_MD("has_loop"),&EventPlayer::has_loop); + + ObjectTypeDB::bind_method(_MD("set_volume","volume"),&EventPlayer::set_volume); + ObjectTypeDB::bind_method(_MD("get_volume"),&EventPlayer::get_volume); + + ObjectTypeDB::bind_method(_MD("set_pitch_scale","pitch_scale"),&EventPlayer::set_pitch_scale); + ObjectTypeDB::bind_method(_MD("get_pitch_scale"),&EventPlayer::get_pitch_scale); + + ObjectTypeDB::bind_method(_MD("set_tempo_scale","tempo_scale"),&EventPlayer::set_tempo_scale); + ObjectTypeDB::bind_method(_MD("get_tempo_scale"),&EventPlayer::get_tempo_scale); + + ObjectTypeDB::bind_method(_MD("set_volume_db","db"),&EventPlayer::set_volume_db); + ObjectTypeDB::bind_method(_MD("get_volume_db"),&EventPlayer::get_volume_db); + + ObjectTypeDB::bind_method(_MD("get_stream_name"),&EventPlayer::get_stream_name); + ObjectTypeDB::bind_method(_MD("get_loop_count"),&EventPlayer::get_loop_count); + + ObjectTypeDB::bind_method(_MD("get_pos"),&EventPlayer::get_pos); + ObjectTypeDB::bind_method(_MD("seek_pos","time"),&EventPlayer::seek_pos); + + ObjectTypeDB::bind_method(_MD("set_autoplay","enabled"),&EventPlayer::set_autoplay); + ObjectTypeDB::bind_method(_MD("has_autoplay"),&EventPlayer::has_autoplay); + + ObjectTypeDB::bind_method(_MD("set_channel_volume","idx","channel_volume"),&EventPlayer::set_channel_volume); + ObjectTypeDB::bind_method(_MD("get_channel_volume""idx"),&EventPlayer::get_channel_volume); + + ObjectTypeDB::bind_method(_MD("get_length"),&EventPlayer::get_length); + + + ObjectTypeDB::bind_method(_MD("get_channel_last_note_time"),&EventPlayer::get_channel_last_note_time); + + ObjectTypeDB::bind_method(_MD("_set_play","play"),&EventPlayer::_set_play); + ObjectTypeDB::bind_method(_MD("_get_play"),&EventPlayer::_get_play); + + ADD_PROPERTY( PropertyInfo(Variant::OBJECT, "stream/stream", PROPERTY_HINT_RESOURCE_TYPE,"EventStream"), _SCS("set_stream"), _SCS("get_stream") ); + ADD_PROPERTY( PropertyInfo(Variant::BOOL, "stream/play"), _SCS("_set_play"), _SCS("_get_play") ); + ADD_PROPERTY( PropertyInfo(Variant::BOOL, "stream/loop"), _SCS("set_loop"), _SCS("has_loop") ); + ADD_PROPERTY( PropertyInfo(Variant::REAL, "stream/volume_db", PROPERTY_HINT_RANGE,"-80,24,0.01"), _SCS("set_volume_db"), _SCS("get_volume_db") ); + ADD_PROPERTY( PropertyInfo(Variant::REAL, "stream/pitch_scale", PROPERTY_HINT_RANGE,"0.001,16,0.001"), _SCS("set_pitch_scale"), _SCS("get_pitch_scale") ); + ADD_PROPERTY( PropertyInfo(Variant::REAL, "stream/tempo_scale", PROPERTY_HINT_RANGE,"0.001,16,0.001"), _SCS("set_tempo_scale"), _SCS("get_tempo_scale") ); + ADD_PROPERTY( PropertyInfo(Variant::BOOL, "stream/autoplay"), _SCS("set_autoplay"), _SCS("has_autoplay") ); + ADD_PROPERTY( PropertyInfo(Variant::BOOL, "stream/paused"), _SCS("set_paused"), _SCS("is_paused") ); +} + + +EventPlayer::EventPlayer() { + + volume=1; + loops=false; + paused=false; + autoplay=false; + _play=false; + pitch_scale=1.0; + tempo_scale=1.0; + for(int i=0;i playback; + Ref stream; + bool paused; + bool autoplay; + bool loops; + float volume; + + float tempo_scale; + float pitch_scale; + + float channel_volume[MAX_CHANNELS]; + bool _play; + void _set_play(bool p_play); + bool _get_play() const; +protected: + void _notification(int p_what); + + static void _bind_methods(); + +public: + + void set_stream(const Ref &p_stream); + Ref get_stream() const; + + void play(); + void stop(); + bool is_playing() const; + + void set_paused(bool p_paused); + bool is_paused() const; + + void set_loop(bool p_enable); + bool has_loop() const; + + void set_volume(float p_vol); + float get_volume() const; + + void set_volume_db(float p_db); + float get_volume_db() const; + + void set_pitch_scale(float p_scale); + float get_pitch_scale() const; + + void set_tempo_scale(float p_scale); + float get_tempo_scale() const; + + String get_stream_name() const; + + int get_loop_count() const; + + float get_pos() const; + void seek_pos(float p_time); + float get_length() const; + void set_autoplay(bool p_vol); + bool has_autoplay() const; + + void set_channel_volume(int p_channel,float p_volume); + float get_channel_volume(int p_channel) const; + + float get_channel_last_note_time(int p_channel) const; + + EventPlayer(); + ~EventPlayer(); +}; + +#endif // EVENT_PLAYER_H diff --git a/scene/audio/sample_player.cpp b/scene/audio/sample_player.cpp new file mode 100644 index 00000000000..be53b8243ec --- /dev/null +++ b/scene/audio/sample_player.cpp @@ -0,0 +1,707 @@ +/*************************************************************************/ +/* sample_player.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 "sample_player.h" + +#include "servers/audio_server.h" + + +bool SamplePlayer::_set(const StringName& p_name, const Variant& p_value) { + + String name=p_name; + + if (name=="play/play") { + if (library.is_valid()) { + + String what=p_value; + if (what=="") + stop_all(); + else + play(what); + + played_back=what; + } + } else if (name=="config/samples") + set_sample_library(p_value); + else if (name=="config/voices") + set_voice_count(p_value); + else if (name.begins_with("default/")) { + + String what=name.right(7); + + if (what=="volume_db") + set_default_volume_db(p_value); + else if (what=="pitch_scale") + set_default_pitch_scale(p_value); + else if (what=="pan") + _default.pan=p_value; + else if (what=="depth") + _default.depth=p_value; + else if (what=="height") + _default.height=p_value; + else if (what=="filter/type") + _default.filter_type=FilterType(p_value.operator int()); + else if (what=="filter/cutoff") + _default.filter_cutoff=p_value; + else if (what=="filter/resonance") + _default.filter_resonance=p_value; + else if (what=="filter/gain") + _default.filter_gain=p_value; + else if (what=="reverb_room") + _default.reverb_room=ReverbRoomType(p_value.operator int()); + else if (what=="reverb_send") + _default.reverb_send=p_value; + else if (what=="chorus_send") + _default.chorus_send=p_value; + else + return false; + + + } else + return false; + + return true; +} + +bool SamplePlayer::_get(const StringName& p_name,Variant &r_ret) const { + + + String name=p_name; + + if (name=="play/play") { + r_ret=played_back; + } else if (name=="config/voices") { + r_ret= get_voice_count(); + } else if (name=="config/samples") { + + r_ret= get_sample_library(); + } else if (name.begins_with("default/")) { + + String what=name.get_slice("/",1); + + if (what=="volume_db") + r_ret= get_default_volume_db(); + else if (what=="pitch_scale") + r_ret= get_default_pitch_scale(); + else if (what=="pan") + r_ret= _default.pan; + else if (what=="depth") + r_ret= _default.depth; + else if (what=="height") + r_ret= _default.height; + else if (what=="filter/type") + r_ret= _default.filter_type; + else if (what=="filter/cutoff") + r_ret= _default.filter_cutoff; + else if (what=="filter/resonance") + r_ret= _default.filter_resonance; + else if (what=="filter/gain") + r_ret= _default.filter_gain; + else if (what=="reverb_room") + r_ret= _default.reverb_room; + else if (what=="reverb_send") + r_ret= _default.reverb_send; + else if (what=="chorus_send") + r_ret= _default.chorus_send; + else + return false; + + + } else + return false; + + return true; +} + +void SamplePlayer::_get_property_list(List *p_list) const { + + String en=""; + if (library.is_valid()) { + List samples; + Ref ncl=library; + ncl->get_sample_list(&samples); + for (List::Element *E=samples.front();E;E=E->next()) { + + en+=","; + en+=E->get(); + } + } + + p_list->push_back( PropertyInfo( Variant::STRING, "play/play", PROPERTY_HINT_ENUM, en,PROPERTY_USAGE_EDITOR)); + p_list->push_back( PropertyInfo( Variant::INT, "config/voices", PROPERTY_HINT_RANGE, "1,256,1")); + p_list->push_back( PropertyInfo( Variant::OBJECT, "config/samples", PROPERTY_HINT_RESOURCE_TYPE, "SampleLibrary")); + p_list->push_back( PropertyInfo( Variant::REAL, "default/volume_db", PROPERTY_HINT_RANGE, "-80,24,0.01")); + p_list->push_back( PropertyInfo( Variant::REAL, "default/pitch_scale", PROPERTY_HINT_RANGE, "0.01,48,0.01")); + p_list->push_back( PropertyInfo( Variant::REAL, "default/pan", PROPERTY_HINT_RANGE, "-1,1,0.01")); + p_list->push_back( PropertyInfo( Variant::REAL, "default/depth", PROPERTY_HINT_RANGE, "-1,1,0.01")); + p_list->push_back( PropertyInfo( Variant::REAL, "default/height", PROPERTY_HINT_RANGE, "-1,1,0.01")); + p_list->push_back( PropertyInfo( Variant::INT, "default/filter/type", PROPERTY_HINT_ENUM, "Disabled,Lowpass,Bandpass,Highpass,Notch,Peak,BandLimit,LowShelf,HighShelf")); + p_list->push_back( PropertyInfo( Variant::REAL, "default/filter/cutoff", PROPERTY_HINT_RANGE, "20,16384.0,0.01")); + p_list->push_back( PropertyInfo( Variant::REAL, "default/filter/resonance", PROPERTY_HINT_RANGE, "0,4,0.01")); + p_list->push_back( PropertyInfo( Variant::REAL, "default/filter/gain", PROPERTY_HINT_RANGE, "0,2,0.01")); + p_list->push_back( PropertyInfo( Variant::INT, "default/reverb_room", PROPERTY_HINT_ENUM, "Small,Medimum,Large,Hall")); + p_list->push_back( PropertyInfo( Variant::REAL, "default/reverb_send", PROPERTY_HINT_RANGE, "0,1,0.01")); + p_list->push_back( PropertyInfo( Variant::REAL, "default/chorus_send", PROPERTY_HINT_RANGE, "0,1,0.01")); + + +} + + +SamplePlayer::Voice::Voice() { + + voice=AudioServer::get_singleton()->voice_create(); + clear(); +} + + +void SamplePlayer::Voice::clear() { + + check=0; + + mix_rate=44100; + volume=1; + pan=0; + pan_depth=0; + pan_height=0; + filter_type=FILTER_NONE; + filter_cutoff=0; + filter_resonance=0; + chorus_send=0; + reverb_room=REVERB_HALL; + reverb_send=0; + active=false; + +} +SamplePlayer::Voice::~Voice() { + + AudioServer::get_singleton()->free(voice); +} + + +void SamplePlayer::set_voice_count(int p_voice_count) { + + ERR_FAIL_COND( p_voice_count <1 || p_voice_count >0xFFFE ); + + voices.resize(p_voice_count); +} + +int SamplePlayer::get_voice_count() const { + + return voices.size(); +} + +SamplePlayer::VoiceID SamplePlayer::play(const String& p_name,bool unique) { + + if (library.is_null()) + return INVALID_VOICE_ID; + ERR_FAIL_COND_V( !library->has_sample(p_name), INVALID_VOICE_ID ); + + Ref sample = library->get_sample(p_name); + float vol_change = library->sample_get_volume_db(p_name); + float pitch_change = library->sample_get_pitch_scale(p_name); + + last_check++; + last_id = (last_id + 1) % voices.size(); + + Voice&v = voices[last_id]; + v.clear(); + + + v.mix_rate=sample->get_mix_rate()*(_default.pitch_scale*pitch_change); + v.sample_mix_rate=sample->get_mix_rate(); + v.check=last_check; + v.volume=Math::db2linear(_default.volume_db+vol_change); + v.pan=_default.pan; + v.pan_depth=_default.depth; + v.pan_height=_default.height; + v.filter_type=_default.filter_type; + v.filter_cutoff=_default.filter_cutoff; + v.filter_resonance=_default.filter_resonance; + v.filter_gain=_default.filter_gain; + v.chorus_send=_default.chorus_send; + v.reverb_room=_default.reverb_room; + v.reverb_send=_default.reverb_send; + + AudioServer::get_singleton()->voice_play(v.voice,sample->get_rid()); + AudioServer::get_singleton()->voice_set_mix_rate(v.voice,v.mix_rate); + AudioServer::get_singleton()->voice_set_volume(v.voice,v.volume); + AudioServer::get_singleton()->voice_set_pan(v.voice,v.pan,v.pan_depth,v.pan_height); + AudioServer::get_singleton()->voice_set_filter(v.voice,(AudioServer::FilterType)v.filter_type,v.filter_cutoff,v.filter_resonance,v.filter_gain); + AudioServer::get_singleton()->voice_set_chorus(v.voice,v.chorus_send); + AudioServer::get_singleton()->voice_set_reverb(v.voice,(AudioServer::ReverbRoomType)v.reverb_room,v.reverb_send); + + v.active=true; + + if (unique) { + + for(int i=0;ivoice_stop(voices[i].voice); + + voices[i].clear(); + } + + } + + return last_id | (last_check<<16); +} + +void SamplePlayer::stop_all() { + + + for(int i=0;ivoice_stop(voices[i].voice); + voices[i].clear(); + } + +} + +#define _GET_VOICE\ + uint32_t voice=p_voice&0xFFFF;\ + ERR_FAIL_COND(voice > (uint32_t)voices.size());\ + Voice &v=voices[voice];\ + if (v.check!=uint32_t(p_voice>>16))\ + return;\ + ERR_FAIL_COND(!v.active); + +void SamplePlayer::stop(VoiceID p_voice) { + + _GET_VOICE + + AudioServer::get_singleton()->voice_stop(v.voice); + v.active=false; + +} + +void SamplePlayer::set_mix_rate(VoiceID p_voice, int p_mix_rate) { + + _GET_VOICE + + v.mix_rate=p_mix_rate; + AudioServer::get_singleton()->voice_set_mix_rate(v.voice,v.mix_rate); + +} +void SamplePlayer::set_pitch_scale(VoiceID p_voice, float p_pitch_scale) { + + _GET_VOICE + + v.mix_rate=v.sample_mix_rate*p_pitch_scale; + AudioServer::get_singleton()->voice_set_mix_rate(v.voice,v.mix_rate); + +} +void SamplePlayer::set_volume(VoiceID p_voice, float p_volume) { + + + _GET_VOICE + v.volume=p_volume; + AudioServer::get_singleton()->voice_set_volume(v.voice,v.volume); + +} + +void SamplePlayer::set_volume_db(VoiceID p_voice, float p_db) { + + //@TODO handle 0 volume as -80db or something + _GET_VOICE + v.volume=Math::db2linear(p_db); + AudioServer::get_singleton()->voice_set_volume(v.voice,v.volume); + +} + +void SamplePlayer::set_pan(VoiceID p_voice, float p_pan,float p_pan_depth,float p_pan_height) { + + _GET_VOICE + v.pan=p_pan; + v.pan_depth=p_pan_depth; + v.pan_height=p_pan_height; + + AudioServer::get_singleton()->voice_set_pan(v.voice,v.pan,v.pan_depth,v.pan_height); + +} + +void SamplePlayer::set_filter(VoiceID p_voice,FilterType p_filter,float p_cutoff,float p_resonance,float p_gain) { + + _GET_VOICE + v.filter_type=p_filter; + v.filter_cutoff=p_cutoff; + v.filter_resonance=p_resonance; + v.filter_gain=p_gain; + + AudioServer::get_singleton()->voice_set_filter(v.voice,(AudioServer::FilterType)p_filter,p_cutoff,p_resonance); + +} +void SamplePlayer::set_chorus(VoiceID p_voice,float p_send) { + + _GET_VOICE + v.chorus_send=p_send; + + AudioServer::get_singleton()->voice_set_chorus(v.voice,p_send); + +} +void SamplePlayer::set_reverb(VoiceID p_voice,ReverbRoomType p_room,float p_send) { + + _GET_VOICE + v.reverb_room=p_room; + v.reverb_send=p_send; + + AudioServer::get_singleton()->voice_set_reverb(v.voice,(AudioServer::ReverbRoomType)p_room,p_send); + +} + +#define _GET_VOICE_V(m_ret)\ + uint32_t voice=p_voice&0xFFFF;\ + ERR_FAIL_COND_V(voice > (uint32_t)voices.size(),m_ret);\ + const Voice &v=voices[voice];\ + if (v.check!=(p_voice>>16))\ + return m_ret;\ + ERR_FAIL_COND_V(!v.active,m_ret); + + +int SamplePlayer::get_mix_rate(VoiceID p_voice) const { + + _GET_VOICE_V(0); + + return v.mix_rate; +} +float SamplePlayer::get_pitch_scale(VoiceID p_voice) const { + + _GET_VOICE_V(0); + return v.sample_mix_rate/(float)v.mix_rate; +} +float SamplePlayer::get_volume(VoiceID p_voice) const { + + _GET_VOICE_V(0); + return v.volume; +} + + +float SamplePlayer::get_volume_db(VoiceID p_voice) const { + + _GET_VOICE_V(0); + return Math::linear2db(v.volume); +} + +float SamplePlayer::get_pan(VoiceID p_voice) const { + + _GET_VOICE_V(0); + return v.pan; +} +float SamplePlayer::get_pan_depth(VoiceID p_voice) const { + + + _GET_VOICE_V(0); + return v.pan_depth; +} +float SamplePlayer::get_pan_height(VoiceID p_voice) const { + + _GET_VOICE_V(0); + + return v.pan_height; +} +SamplePlayer::FilterType SamplePlayer::get_filter_type(VoiceID p_voice) const { + + _GET_VOICE_V(FILTER_NONE); + + return v.filter_type; +} +float SamplePlayer::get_filter_cutoff(VoiceID p_voice) const { + + _GET_VOICE_V(0); + + return v.filter_cutoff; +} +float SamplePlayer::get_filter_resonance(VoiceID p_voice) const { + + _GET_VOICE_V(0); + + return v.filter_resonance; +} + +float SamplePlayer::get_filter_gain(VoiceID p_voice) const { + + _GET_VOICE_V(0); + + return v.filter_gain; +} +float SamplePlayer::get_chorus(VoiceID p_voice) const { + + _GET_VOICE_V(0); + + return v.chorus_send; +} +float SamplePlayer::get_reverb_room(VoiceID p_voice) const { + + _GET_VOICE_V(0); + + return v.reverb_room; +} + +float SamplePlayer::get_reverb(VoiceID p_voice) const { + + _GET_VOICE_V(0); + + return v.reverb_send; +} + +bool SamplePlayer::is_voice_active(VoiceID p_voice) const { + + _GET_VOICE_V(false); + return v.active && AudioServer::get_singleton()->voice_is_active(v.voice); + +} +bool SamplePlayer::is_active() const { + + for(int i=0;ivoice_is_active(voices[i].voice)) + return true; + + + } + + return false; +} + + + +void SamplePlayer::set_sample_library(const Ref& p_library) { + + library=p_library; +} + +Ref SamplePlayer::get_sample_library() const { + + return library; +} + + + +void SamplePlayer::set_default_pitch_scale(float p_pitch_scale) { + + _default.pitch_scale=p_pitch_scale; +} +void SamplePlayer::set_default_volume(float p_volume) { + + _default.volume_db=Math::linear2db(p_volume); +} +void SamplePlayer::set_default_volume_db(float p_db) { + + _default.volume_db=p_db; +} +void SamplePlayer::set_default_pan(float p_pan,float p_pan_depth,float p_pan_height) { + + _default.pan=p_pan; + _default.depth=p_pan_depth; + _default.height=p_pan_height; + +} +void SamplePlayer::set_default_filter(FilterType p_filter,float p_cutoff,float p_resonance,float p_gain) { + + _default.filter_type=p_filter; + _default.filter_cutoff=p_cutoff; + _default.filter_resonance=p_resonance; + _default.filter_gain=p_gain; +} +void SamplePlayer::set_default_chorus(float p_send) { + + _default.chorus_send=p_send; + +} +void SamplePlayer::set_default_reverb(ReverbRoomType p_room,float p_send) { + + _default.reverb_room=p_room; + _default.reverb_send=p_send; +} + +float SamplePlayer::get_default_volume() const { + + return Math::db2linear(_default.volume_db); +} +float SamplePlayer::get_default_volume_db() const { + + return _default.volume_db; +} +float SamplePlayer::get_default_pitch_scale() const { + + return _default.pitch_scale; +} + + +float SamplePlayer::get_default_pan() const { + + return _default.pan; +} +float SamplePlayer::get_default_pan_depth() const { + + return _default.depth; +} +float SamplePlayer::get_default_pan_height() const { + + return _default.height; +} +SamplePlayer::FilterType SamplePlayer::get_default_filter_type() const { + + return _default.filter_type; +} +float SamplePlayer::get_default_filter_cutoff() const { + + return _default.filter_cutoff; +} +float SamplePlayer::get_default_filter_resonance() const { + + return _default.filter_resonance; +} +float SamplePlayer::get_default_filter_gain() const { + + return _default.filter_gain; +} +float SamplePlayer::get_default_chorus() const { + + return _default.chorus_send; +} +float SamplePlayer::get_default_reverb_room() const { + + return _default.reverb_room; +} +float SamplePlayer::get_default_reverb() const { + + return _default.reverb_send; +} + + +void SamplePlayer::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("set_sample_library","library:SampleLibrary"),&SamplePlayer::set_sample_library ); + ObjectTypeDB::bind_method(_MD("get_sample_library:SampleLibrary"),&SamplePlayer::get_sample_library ); + + ObjectTypeDB::bind_method(_MD("set_voice_count","max_voices"),&SamplePlayer::set_voice_count ); + ObjectTypeDB::bind_method(_MD("get_voice_count"),&SamplePlayer::get_voice_count ); + + ObjectTypeDB::bind_method(_MD("play","name","unique"),&SamplePlayer::play, DEFVAL(false) ); + ObjectTypeDB::bind_method(_MD("stop","voice"),&SamplePlayer::stop ); + ObjectTypeDB::bind_method(_MD("stop_all"),&SamplePlayer::stop_all ); + + ObjectTypeDB::bind_method(_MD("set_mix_rate","voice","hz"),&SamplePlayer::set_mix_rate ); + ObjectTypeDB::bind_method(_MD("set_pitch_scale","voice","ratio"),&SamplePlayer::set_pitch_scale ); + ObjectTypeDB::bind_method(_MD("set_volume","voice","nrg"),&SamplePlayer::set_volume ); + ObjectTypeDB::bind_method(_MD("set_volume_db","voice","nrg"),&SamplePlayer::set_volume_db ); + ObjectTypeDB::bind_method(_MD("set_pan","voice","pan","depth","height"),&SamplePlayer::set_pan,DEFVAL(0),DEFVAL(0) ); + ObjectTypeDB::bind_method(_MD("set_filter","voice","type","cutoff_hz","resonance","gain"),&SamplePlayer::set_filter,DEFVAL(0) ); + ObjectTypeDB::bind_method(_MD("set_chorus","voice","send"),&SamplePlayer::set_chorus ); + ObjectTypeDB::bind_method(_MD("set_reverb","voice","room_type","send"),&SamplePlayer::set_reverb ); + + ObjectTypeDB::bind_method(_MD("get_mix_rate","voice"),&SamplePlayer::get_mix_rate ); + ObjectTypeDB::bind_method(_MD("get_pitch_scale","voice"),&SamplePlayer::get_pitch_scale ); + ObjectTypeDB::bind_method(_MD("get_volume","voice"),&SamplePlayer::get_volume ); + ObjectTypeDB::bind_method(_MD("get_volume_db","voice"),&SamplePlayer::get_volume_db ); + ObjectTypeDB::bind_method(_MD("get_pan","voice"),&SamplePlayer::get_pan ); + ObjectTypeDB::bind_method(_MD("get_pan_depth","voice"),&SamplePlayer::get_pan_depth ); + ObjectTypeDB::bind_method(_MD("get_pan_height","voice"),&SamplePlayer::get_pan_height ); + ObjectTypeDB::bind_method(_MD("get_filter_type","voice"),&SamplePlayer::get_filter_type ); + ObjectTypeDB::bind_method(_MD("get_filter_cutoff","voice"),&SamplePlayer::get_filter_cutoff ); + ObjectTypeDB::bind_method(_MD("get_filter_resonance","voice"),&SamplePlayer::get_filter_resonance ); + ObjectTypeDB::bind_method(_MD("get_filter_gain","voice"),&SamplePlayer::get_filter_gain ); + ObjectTypeDB::bind_method(_MD("get_chorus","voice"),&SamplePlayer::get_chorus ); + ObjectTypeDB::bind_method(_MD("get_reverb_room","voice"),&SamplePlayer::get_reverb_room ); + ObjectTypeDB::bind_method(_MD("get_reverb","voice"),&SamplePlayer::get_reverb ); + + ObjectTypeDB::bind_method(_MD("set_default_pitch_scale","ratio"),&SamplePlayer::set_default_pitch_scale ); + ObjectTypeDB::bind_method(_MD("set_default_volume","nrg"),&SamplePlayer::set_default_volume ); + ObjectTypeDB::bind_method(_MD("set_default_volume_db","db"),&SamplePlayer::set_default_volume ); + ObjectTypeDB::bind_method(_MD("set_default_pan","pan","depth","height"),&SamplePlayer::set_default_pan,DEFVAL(0),DEFVAL(0) ); + ObjectTypeDB::bind_method(_MD("set_default_filter","type","cutoff_hz","resonance","gain"),&SamplePlayer::set_default_filter,DEFVAL(0) ); + ObjectTypeDB::bind_method(_MD("set_default_chorus","send"),&SamplePlayer::set_default_chorus ); + ObjectTypeDB::bind_method(_MD("set_default_reverb","room_type","send"),&SamplePlayer::set_default_reverb ); + + ObjectTypeDB::bind_method(_MD("get_default_pitch_scale"),&SamplePlayer::get_default_pitch_scale ); + ObjectTypeDB::bind_method(_MD("get_default_volume"),&SamplePlayer::get_default_volume ); + ObjectTypeDB::bind_method(_MD("get_default_volume_db"),&SamplePlayer::get_default_volume ); + ObjectTypeDB::bind_method(_MD("get_default_pan"),&SamplePlayer::get_default_pan ); + ObjectTypeDB::bind_method(_MD("get_default_pan_depth"),&SamplePlayer::get_default_pan_depth ); + ObjectTypeDB::bind_method(_MD("get_default_pan_height"),&SamplePlayer::get_default_pan_height ); + ObjectTypeDB::bind_method(_MD("get_default_filter_type"),&SamplePlayer::get_default_filter_type ); + ObjectTypeDB::bind_method(_MD("get_default_filter_cutoff"),&SamplePlayer::get_default_filter_cutoff ); + ObjectTypeDB::bind_method(_MD("get_default_filter_resonance"),&SamplePlayer::get_default_filter_resonance ); + ObjectTypeDB::bind_method(_MD("get_default_filter_gain"),&SamplePlayer::get_default_filter_gain ); + ObjectTypeDB::bind_method(_MD("get_default_chorus"),&SamplePlayer::get_default_chorus ); + ObjectTypeDB::bind_method(_MD("get_default_reverb_room"),&SamplePlayer::get_default_reverb_room ); + ObjectTypeDB::bind_method(_MD("get_default_reverb"),&SamplePlayer::get_default_reverb ); + + ObjectTypeDB::bind_method(_MD("is_active"),&SamplePlayer::is_active ); + ObjectTypeDB::bind_method(_MD("is_voice_active","voice"),&SamplePlayer::is_voice_active ); + + BIND_CONSTANT( FILTER_NONE); + BIND_CONSTANT( FILTER_LOWPASS); + BIND_CONSTANT( FILTER_BANDPASS); + BIND_CONSTANT( FILTER_HIPASS); + BIND_CONSTANT( FILTER_NOTCH); + BIND_CONSTANT( FILTER_PEAK); + BIND_CONSTANT( FILTER_BANDLIMIT); ///< cutoff is LP resonace is HP + BIND_CONSTANT( FILTER_LOW_SHELF); + BIND_CONSTANT( FILTER_HIGH_SHELF); + + BIND_CONSTANT( REVERB_SMALL ); + BIND_CONSTANT( REVERB_MEDIUM ); + BIND_CONSTANT( REVERB_LARGE ); + BIND_CONSTANT( REVERB_HALL ); + +} + + +SamplePlayer::SamplePlayer() { + + voices.resize(1); + + _default.pitch_scale=1; + _default.volume_db=0; + _default.pan=0; + _default.depth=0; + _default.height=0; + _default.filter_type=FILTER_NONE; + _default.filter_cutoff=5000; + _default.filter_resonance=1; + _default.filter_gain=1; + _default.chorus_send=0; + _default.reverb_room=REVERB_LARGE; + _default.reverb_send=0; + last_id=0; + last_check=0; + + +} + +SamplePlayer::~SamplePlayer() { + + +} diff --git a/scene/audio/sample_player.h b/scene/audio/sample_player.h new file mode 100644 index 00000000000..d0ce1825ee6 --- /dev/null +++ b/scene/audio/sample_player.h @@ -0,0 +1,198 @@ +/*************************************************************************/ +/* sample_player.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 SAMPLE_PLAYER_H +#define SAMPLE_PLAYER_H + +#include "scene/main/node.h" +#include "scene/resources/sample_library.h" + +class SamplePlayer : public Node { + + OBJ_TYPE( SamplePlayer, Node ); + OBJ_CATEGORY("Audio Nodes"); +public: + + + enum FilterType { + FILTER_NONE, + FILTER_LOWPASS, + FILTER_BANDPASS, + FILTER_HIPASS, + FILTER_NOTCH, + FILTER_PEAK, + FILTER_BANDLIMIT, ///< cutoff is LP resonace is HP + FILTER_LOW_SHELF, + FILTER_HIGH_SHELF, + }; + + enum ReverbRoomType { + + REVERB_SMALL, + REVERB_MEDIUM, + REVERB_LARGE, + REVERB_HALL + }; + + enum { + + INVALID_VOICE_ID=0xFFFFFFFF + }; + + typedef uint32_t VoiceID; + +private: + + Ref library; + + struct Voice { + + RID voice; + uint32_t check; + bool active; + + int sample_mix_rate; + int mix_rate; + float volume; + float pan; + float pan_depth; + float pan_height; + FilterType filter_type; + float filter_cutoff; + float filter_resonance; + float filter_gain; + float chorus_send; + ReverbRoomType reverb_room; + float reverb_send; + + void clear(); + Voice(); + ~Voice(); + }; + + Vector voices; + + struct Default { + + float reverb_send; + float pitch_scale; + float volume_db; + float pan; + float depth; + float height; + FilterType filter_type; + float filter_cutoff; + float filter_resonance; + float filter_gain; + float chorus_send; + ReverbRoomType reverb_room; + + } _default; + + uint32_t last_id; + uint16_t last_check; + String played_back; +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 *p_list) const; + + static void _bind_methods(); + +public: + + void set_sample_library(const Ref& p_library); + Ref get_sample_library() const; + + void set_voice_count(int p_voice_count); + int get_voice_count() const; + + VoiceID play(const String& p_name,bool unique=false); + void stop(VoiceID p_voice); + void stop_all(); + bool is_voice_active(VoiceID) const; + bool is_active() const; + + void set_mix_rate(VoiceID p_voice, int p_mix_rate); + void set_pitch_scale(VoiceID p_voice, float p_pitch_scale); + void set_volume(VoiceID p_voice, float p_volume); + void set_volume_db(VoiceID p_voice, float p_db); + void set_pan(VoiceID p_voice, float p_pan,float p_pan_depth=0,float p_pan_height=0); + void set_filter(VoiceID p_voice,FilterType p_filter,float p_cutoff,float p_resonance,float p_gain); + void set_chorus(VoiceID p_voice,float p_send); + void set_reverb(VoiceID p_voice,ReverbRoomType p_room,float p_send); + + int get_mix_rate(VoiceID p_voice) const; + float get_pitch_scale(VoiceID p_voice) const; + float get_volume(VoiceID p_voice) const; + float get_volume_db(VoiceID p_voice) const; + + float get_pan(VoiceID p_voice) const; + float get_pan_depth(VoiceID p_voice) const; + float get_pan_height(VoiceID p_voice) const; + FilterType get_filter_type(VoiceID p_voice) const; + float get_filter_cutoff(VoiceID p_voice) const; + float get_filter_resonance(VoiceID p_voice) const; + float get_filter_gain(VoiceID p_voice) const; + float get_chorus(VoiceID p_voice) const; + float get_reverb_room(VoiceID p_voice) const; + float get_reverb(VoiceID p_voice) const; + + + + void set_default_pitch_scale(float p_pitch_scale); + void set_default_volume(float p_volume); + void set_default_volume_db(float p_db); + void set_default_pan(float p_pan,float p_pan_depth=0,float p_pan_height=0); + void set_default_filter(FilterType p_filter,float p_cutoff,float p_resonance,float p_gain); + void set_default_chorus(float p_send); + void set_default_reverb(ReverbRoomType p_room,float p_send); + + float get_default_volume() const; + float get_default_volume_db() const; + float get_default_pitch_scale() const; + float get_default_pan() const; + float get_default_pan_depth() const; + float get_default_pan_height() const; + FilterType get_default_filter_type() const; + float get_default_filter_cutoff() const; + float get_default_filter_resonance() const; + float get_default_filter_gain() const; + float get_default_chorus() const; + float get_default_reverb_room() const; + float get_default_reverb() const; + + SamplePlayer(); + ~SamplePlayer(); +}; + +VARIANT_ENUM_CAST( SamplePlayer::FilterType ); +VARIANT_ENUM_CAST( SamplePlayer::ReverbRoomType ); + +#endif // SAMPLE_PLAYER_H diff --git a/scene/audio/sound_room_params.cpp b/scene/audio/sound_room_params.cpp new file mode 100644 index 00000000000..3e0a64b3f10 --- /dev/null +++ b/scene/audio/sound_room_params.cpp @@ -0,0 +1,180 @@ +/*************************************************************************/ +/* sound_room_params.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 "sound_room_params.h" + +#include "scene/main/viewport.h" + +#ifndef _3D_DISABLED +void SoundRoomParams::_update_sound_room() { + + if (!room.is_valid()) + return; + + for(int i=0;iroom_set_param(room,SpatialSoundServer::RoomParam(i),params[i]); + + } + + SpatialSoundServer::get_singleton()->room_set_reverb(room,SpatialSoundServer::RoomReverb(reverb)); + SpatialSoundServer::get_singleton()->room_set_force_params_to_all_sources(room,force_params_for_all_sources); +} + + +void SoundRoomParams::_notification(int p_what) { + + + switch(p_what) { + + + case NOTIFICATION_ENTER_SCENE: { +#if 0 + Node *n=this; + Room *room_instance=NULL; + while(n) { + + room_instance=n->cast_to(); + if (room_instance) { + + break; + } + if (n->cast_to()) + break; + + n=n->get_parent(); + } + + + if (room_instance) { + room=room_instance->get_sound_room(); + } else { + room=get_scene()->get_default_world()->get_sound_space(); + } + + _update_sound_room(); +#endif + + } break; + case NOTIFICATION_EXIT_SCENE: { + + room=RID(); + + } break; + } +} + + +void SoundRoomParams::set_param(Params p_param, float p_value) { + + ERR_FAIL_INDEX(p_param,PARAM_MAX); + params[p_param]=p_value; + if (room.is_valid()) + SpatialSoundServer::get_singleton()->room_set_param(room,SpatialSoundServer::RoomParam(p_param),p_value); +} + +float SoundRoomParams::get_param(Params p_param) const { + + ERR_FAIL_INDEX_V(p_param,PARAM_MAX,0); + return params[p_param]; +} + + +void SoundRoomParams::set_reverb_mode(Reverb p_mode) { + + ERR_FAIL_INDEX(p_mode,4); + reverb=p_mode; + if (room.is_valid()) + SpatialSoundServer::get_singleton()->room_set_reverb(room,SpatialSoundServer::RoomReverb(p_mode)); +} + +SoundRoomParams::Reverb SoundRoomParams::get_reverb_mode() const { + + return reverb; +} + + +void SoundRoomParams::set_force_params_to_all_sources(bool p_force) { + + force_params_for_all_sources=p_force; + if (room.is_valid()) + SpatialSoundServer::get_singleton()->room_set_force_params_to_all_sources(room,p_force); +} + +bool SoundRoomParams::is_forcing_params_to_all_sources() { + + return force_params_for_all_sources; +} + + +void SoundRoomParams::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("set_param","param","value"),&SoundRoomParams::set_param ); + ObjectTypeDB::bind_method(_MD("get_param","param"),&SoundRoomParams::get_param ); + + ObjectTypeDB::bind_method(_MD("set_reverb_mode","reverb_mode","value"),&SoundRoomParams::set_reverb_mode ); + ObjectTypeDB::bind_method(_MD("get_reverb_mode","reverb_mode"),&SoundRoomParams::get_reverb_mode ); + + ObjectTypeDB::bind_method(_MD("set_force_params_to_all_sources","enabled"),&SoundRoomParams::set_force_params_to_all_sources ); + ObjectTypeDB::bind_method(_MD("is_forcing_params_to_all_sources"),&SoundRoomParams::is_forcing_params_to_all_sources ); + + + ADD_PROPERTY( PropertyInfo( Variant::INT, "reverb/mode", PROPERTY_HINT_ENUM, "Small,Medium,Large,Hall"), _SCS("set_reverb_mode"), _SCS("get_reverb_mode") ); + ADD_PROPERTYI( PropertyInfo( Variant::REAL, "params/speed_of_scale", PROPERTY_HINT_RANGE, "0.01,16,0.01"), _SCS("set_param"), _SCS("get_param"), PARAM_SPEED_OF_SOUND_SCALE); + ADD_PROPERTYI( PropertyInfo( Variant::REAL, "params/doppler_factor",PROPERTY_HINT_RANGE, "0.01,16,0.01"), _SCS("set_param"), _SCS("get_param"), PARAM_DOPPLER_FACTOR ); + ADD_PROPERTYI( PropertyInfo( Variant::REAL, "params/pitch_scale",PROPERTY_HINT_RANGE, "0.01,32,0.01"), _SCS("set_param"), _SCS("get_param"), PARAM_PITCH_SCALE ); + ADD_PROPERTYI( PropertyInfo( Variant::REAL, "params/volume_scale_db",PROPERTY_HINT_RANGE, "-80,24,0.01"), _SCS("set_param"), _SCS("get_param"), PARAM_VOLUME_SCALE_DB ); + ADD_PROPERTYI( PropertyInfo( Variant::REAL, "params/reverb_send",PROPERTY_HINT_RANGE, "0,1,0.01"), _SCS("set_param"), _SCS("get_param"), PARAM_REVERB_SEND ); + ADD_PROPERTYI( PropertyInfo( Variant::REAL, "params/chorus_send",PROPERTY_HINT_RANGE, "0,1,0.01"), _SCS("set_param"), _SCS("get_param"), PARAM_CHORUS_SEND ); + ADD_PROPERTYI( PropertyInfo( Variant::REAL, "params/attenuation_scale",PROPERTY_HINT_RANGE, "0.01,32,0.01"), _SCS("set_param"), _SCS("get_param"), PARAM_ATTENUATION_SCALE ); + ADD_PROPERTYI( PropertyInfo( Variant::REAL, "params/attenuation_hf_cutoff",PROPERTY_HINT_RANGE, "30,16384,1"), _SCS("set_param"), _SCS("get_param"), PARAM_ATTENUATION_HF_CUTOFF ); + ADD_PROPERTYI( PropertyInfo( Variant::REAL, "params/attenuation_hf_floor_db",PROPERTY_HINT_RANGE, "-80,24,0.01"), _SCS("set_param"), _SCS("get_param"), PARAM_ATTENUATION_HF_FLOOR_DB ); + ADD_PROPERTYI( PropertyInfo( Variant::REAL, "params/attenuation_hf_ratio_exp",PROPERTY_HINT_RANGE, "0.01,32,0.01"), _SCS("set_param"), _SCS("get_param"), PARAM_ATTENUATION_HF_RATIO_EXP ); + ADD_PROPERTYI( PropertyInfo( Variant::REAL, "params/attenuation_reverb_scale",PROPERTY_HINT_RANGE, "0.01,32,0.01"), _SCS("set_param"), _SCS("get_param"), PARAM_ATTENUATION_REVERB_SCALE ); + ADD_PROPERTY( PropertyInfo( Variant::BOOL, "force_to_all_sources"),_SCS("set_force_params_to_all_sources"),_SCS("is_forcing_params_to_all_sources") ); + +} + + +SoundRoomParams::SoundRoomParams() { + + reverb=REVERB_HALL; + params[PARAM_SPEED_OF_SOUND_SCALE]=1; + params[PARAM_DOPPLER_FACTOR]=1.0; + params[PARAM_PITCH_SCALE]=1.0; + params[PARAM_VOLUME_SCALE_DB]=0; + params[PARAM_REVERB_SEND]=0; + params[PARAM_CHORUS_SEND]=0; + params[PARAM_ATTENUATION_SCALE]=1.0; + params[PARAM_ATTENUATION_HF_CUTOFF]=5000; + params[PARAM_ATTENUATION_HF_FLOOR_DB]=-24.0; + params[PARAM_ATTENUATION_HF_RATIO_EXP]=1.0; + params[PARAM_ATTENUATION_REVERB_SCALE]=0.0; + force_params_for_all_sources=false; +} +#endif diff --git a/scene/audio/sound_room_params.h b/scene/audio/sound_room_params.h new file mode 100644 index 00000000000..5999046b807 --- /dev/null +++ b/scene/audio/sound_room_params.h @@ -0,0 +1,100 @@ +/*************************************************************************/ +/* sound_room_params.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 SOUND_ROOM_PARAMS_H +#define SOUND_ROOM_PARAMS_H + +#include "scene/main/node.h" +#include "servers/spatial_sound_server.h" + + +#ifndef _3D_DISABLED + +#include "scene/3d/room_instance.h" +class SoundRoomParams : public Node { + + OBJ_TYPE( SoundRoomParams, Node ); +public: + + enum Params { + PARAM_SPEED_OF_SOUND_SCALE=SpatialSoundServer::ROOM_PARAM_SPEED_OF_SOUND_SCALE, + PARAM_DOPPLER_FACTOR=SpatialSoundServer::ROOM_PARAM_DOPPLER_FACTOR, + PARAM_PITCH_SCALE=SpatialSoundServer::ROOM_PARAM_PITCH_SCALE, + PARAM_VOLUME_SCALE_DB=SpatialSoundServer::ROOM_PARAM_VOLUME_SCALE_DB, + PARAM_REVERB_SEND=SpatialSoundServer::ROOM_PARAM_REVERB_SEND, + PARAM_CHORUS_SEND=SpatialSoundServer::ROOM_PARAM_CHORUS_SEND, + PARAM_ATTENUATION_SCALE=SpatialSoundServer::ROOM_PARAM_ATTENUATION_SCALE, + PARAM_ATTENUATION_HF_CUTOFF=SpatialSoundServer::ROOM_PARAM_ATTENUATION_HF_CUTOFF, + PARAM_ATTENUATION_HF_FLOOR_DB=SpatialSoundServer::ROOM_PARAM_ATTENUATION_HF_FLOOR_DB, + PARAM_ATTENUATION_HF_RATIO_EXP=SpatialSoundServer::ROOM_PARAM_ATTENUATION_HF_RATIO_EXP, + PARAM_ATTENUATION_REVERB_SCALE=SpatialSoundServer::ROOM_PARAM_ATTENUATION_REVERB_SCALE, + PARAM_MAX=SpatialSoundServer::ROOM_PARAM_MAX + }; + + enum Reverb { + REVERB_SMALL, + REVERB_MEDIUM, + REVERB_LARGE, + REVERB_HALL + }; +private: + + RID room; + + float params[PARAM_MAX]; + Reverb reverb; + bool force_params_for_all_sources; + void _update_sound_room(); + + +protected: + + void _notification(int p_what); + static void _bind_methods(); + +public: + + + void set_param(Params p_param, float p_value); + float get_param(Params p_param) const; + + void set_reverb_mode(Reverb p_mode); + Reverb get_reverb_mode() const; + + void set_force_params_to_all_sources(bool p_force); + bool is_forcing_params_to_all_sources(); + + SoundRoomParams(); +}; + +VARIANT_ENUM_CAST(SoundRoomParams::Params); +VARIANT_ENUM_CAST(SoundRoomParams::Reverb); + +#endif + +#endif // SOUND_ROOM_PARAMS_H diff --git a/scene/audio/stream_player.cpp b/scene/audio/stream_player.cpp new file mode 100644 index 00000000000..5084c1295ba --- /dev/null +++ b/scene/audio/stream_player.cpp @@ -0,0 +1,290 @@ +/*************************************************************************/ +/* stream_player.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 "stream_player.h" + + +void StreamPlayer::_notification(int p_what) { + + switch(p_what) { + + case NOTIFICATION_ENTER_SCENE: { + + //set_idle_process(false); //don't annoy + if (stream.is_valid() && autoplay && !get_scene()->is_editor_hint()) + play(); + } break; + case NOTIFICATION_EXIT_SCENE: { + + stop(); //wathever it may be doing, stop + } break; + } +} + + + +void StreamPlayer::set_stream(const Ref &p_stream) { + + stop(); + + if (stream_rid.is_valid()) + AudioServer::get_singleton()->free(stream_rid); + stream_rid=RID(); + + stream=p_stream; + if (!stream.is_null()) { + + stream->set_loop(loops); + stream->set_paused(paused); + stream_rid=AudioServer::get_singleton()->audio_stream_create(stream->get_audio_stream()); + } + + +} + +Ref StreamPlayer::get_stream() const { + + return stream; +} + + +void StreamPlayer::play() { + + ERR_FAIL_COND(!is_inside_scene()); + if (stream.is_null()) + return; + if (stream->is_playing()) + stop(); + + stream->play(); + AudioServer::get_singleton()->stream_set_active(stream_rid,true); + AudioServer::get_singleton()->stream_set_volume_scale(stream_rid,volume); +// if (stream->get_update_mode()!=AudioStream::UPDATE_NONE) +// set_idle_process(true); + +} + +void StreamPlayer::stop() { + + if (!is_inside_scene()) + return; + if (stream.is_null()) + return; + + AudioServer::get_singleton()->stream_set_active(stream_rid,false); + stream->stop(); + //set_idle_process(false); +} + +bool StreamPlayer::is_playing() const { + + if (stream.is_null()) + return false; + + return stream->is_playing(); +} + +void StreamPlayer::set_loop(bool p_enable) { + + loops=p_enable; + if (stream.is_null()) + return; + stream->set_loop(loops); + +} +bool StreamPlayer::has_loop() const { + + return loops; +} + +void StreamPlayer::set_volume(float p_vol) { + + volume=p_vol; + if (stream_rid.is_valid()) + AudioServer::get_singleton()->stream_set_volume_scale(stream_rid,volume); +} + +float StreamPlayer::get_volume() const { + + return volume; +} + +void StreamPlayer::set_volume_db(float p_db) { + + if (p_db<-79) + set_volume(0); + else + set_volume(Math::db2linear(p_db)); +} + +float StreamPlayer::get_volume_db() const { + + if (volume==0) + return -80; + else + return Math::linear2db(volume); +} + + +String StreamPlayer::get_stream_name() const { + + if (stream.is_null()) + return ""; + return stream->get_name(); + +} + +int StreamPlayer::get_loop_count() const { + + if (stream.is_null()) + return 0; + return stream->get_loop_count(); + +} + +float StreamPlayer::get_pos() const { + + if (stream.is_null()) + return 0; + return stream->get_pos(); + +} + +float StreamPlayer::get_length() const { + + if (stream.is_null()) + return 0; + return stream->get_length(); +} +void StreamPlayer::seek_pos(float p_time) { + + if (stream.is_null()) + return; + return stream->seek_pos(p_time); + +} + +void StreamPlayer::set_autoplay(bool p_enable) { + + autoplay=p_enable; +} + +bool StreamPlayer::has_autoplay() const { + + return autoplay; +} + +void StreamPlayer::set_paused(bool p_paused) { + + paused=p_paused; + if (stream.is_valid()) + stream->set_paused(p_paused); +} + +bool StreamPlayer::is_paused() const { + + return paused; +} + +void StreamPlayer::_set_play(bool p_play) { + + _play=p_play; + if (is_inside_scene()) { + if(_play) + play(); + else + stop(); + } + +} + +bool StreamPlayer::_get_play() const{ + + return _play; +} + + +void StreamPlayer::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("set_stream","stream:Stream"),&StreamPlayer::set_stream); + ObjectTypeDB::bind_method(_MD("get_stream:Stream"),&StreamPlayer::get_stream); + + ObjectTypeDB::bind_method(_MD("play"),&StreamPlayer::play); + ObjectTypeDB::bind_method(_MD("stop"),&StreamPlayer::stop); + + ObjectTypeDB::bind_method(_MD("is_playing"),&StreamPlayer::is_playing); + + ObjectTypeDB::bind_method(_MD("set_paused","paused"),&StreamPlayer::set_paused); + ObjectTypeDB::bind_method(_MD("is_paused"),&StreamPlayer::is_paused); + + ObjectTypeDB::bind_method(_MD("set_loop","enabled"),&StreamPlayer::set_loop); + ObjectTypeDB::bind_method(_MD("has_loop"),&StreamPlayer::has_loop); + + ObjectTypeDB::bind_method(_MD("set_volume","volume"),&StreamPlayer::set_volume); + ObjectTypeDB::bind_method(_MD("get_volume"),&StreamPlayer::get_volume); + + ObjectTypeDB::bind_method(_MD("set_volume_db","db"),&StreamPlayer::set_volume_db); + ObjectTypeDB::bind_method(_MD("get_volume_db"),&StreamPlayer::get_volume_db); + + ObjectTypeDB::bind_method(_MD("get_stream_name"),&StreamPlayer::get_stream_name); + ObjectTypeDB::bind_method(_MD("get_loop_count"),&StreamPlayer::get_loop_count); + + ObjectTypeDB::bind_method(_MD("get_pos"),&StreamPlayer::get_pos); + ObjectTypeDB::bind_method(_MD("seek_pos","time"),&StreamPlayer::seek_pos); + + ObjectTypeDB::bind_method(_MD("set_autoplay","enabled"),&StreamPlayer::set_autoplay); + ObjectTypeDB::bind_method(_MD("has_autoplay"),&StreamPlayer::has_autoplay); + + ObjectTypeDB::bind_method(_MD("get_length"),&StreamPlayer::get_length); + + ObjectTypeDB::bind_method(_MD("_set_play","play"),&StreamPlayer::_set_play); + ObjectTypeDB::bind_method(_MD("_get_play"),&StreamPlayer::_get_play); + + ADD_PROPERTY( PropertyInfo(Variant::OBJECT, "stream/stream", PROPERTY_HINT_RESOURCE_TYPE,"AudioStream"), _SCS("set_stream"), _SCS("get_stream") ); + ADD_PROPERTY( PropertyInfo(Variant::BOOL, "stream/play"), _SCS("_set_play"), _SCS("_get_play") ); + ADD_PROPERTY( PropertyInfo(Variant::BOOL, "stream/loop"), _SCS("set_loop"), _SCS("has_loop") ); + ADD_PROPERTY( PropertyInfo(Variant::REAL, "stream/volume_db", PROPERTY_HINT_RANGE,"-80,24,0.01"), _SCS("set_volume_db"), _SCS("get_volume_db") ); + ADD_PROPERTY( PropertyInfo(Variant::BOOL, "stream/autoplay"), _SCS("set_autoplay"), _SCS("has_autoplay") ); + ADD_PROPERTY( PropertyInfo(Variant::BOOL, "stream/paused"), _SCS("set_paused"), _SCS("is_paused") ); +} + + +StreamPlayer::StreamPlayer() { + + volume=1; + loops=false; + paused=false; + autoplay=false; + _play=false; +} + +StreamPlayer::~StreamPlayer() { + if (stream_rid.is_valid()) + AudioServer::get_singleton()->free(stream_rid); + +} diff --git a/scene/audio/stream_player.h b/scene/audio/stream_player.h new file mode 100644 index 00000000000..945d615d6c3 --- /dev/null +++ b/scene/audio/stream_player.h @@ -0,0 +1,89 @@ +/*************************************************************************/ +/* stream_player.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 STREAM_PLAYER_H +#define STREAM_PLAYER_H + +#include "scene/resources/audio_stream.h" +#include "scene/main/node.h" + +class StreamPlayer : public Node { + + OBJ_TYPE(StreamPlayer,Node); + + Ref stream; + RID stream_rid; + bool paused; + bool autoplay; + bool loops; + float volume; + + bool _play; + void _set_play(bool p_play); + bool _get_play() const; +protected: + void _notification(int p_what); + + static void _bind_methods(); +public: + + void set_stream(const Ref &p_stream); + Ref get_stream() const; + + void play(); + void stop(); + bool is_playing() const; + + void set_paused(bool p_paused); + bool is_paused() const; + + void set_loop(bool p_enable); + bool has_loop() const; + + void set_volume(float p_vol); + float get_volume() const; + + void set_volume_db(float p_db); + float get_volume_db() const; + + String get_stream_name() const; + + int get_loop_count() const; + + float get_pos() const; + void seek_pos(float p_time); + float get_length() const; + void set_autoplay(bool p_vol); + bool has_autoplay() const; + + + StreamPlayer(); + ~StreamPlayer(); +}; + +#endif // AUDIO_STREAM_PLAYER_H diff --git a/scene/gui/SCsub b/scene/gui/SCsub new file mode 100644 index 00000000000..055d2f24741 --- /dev/null +++ b/scene/gui/SCsub @@ -0,0 +1,7 @@ +Import('env') + +env.add_source_files(env.scene_sources,"*.cpp") + +Export('env') + + diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp new file mode 100644 index 00000000000..2e03871063a --- /dev/null +++ b/scene/gui/base_button.cpp @@ -0,0 +1,375 @@ +/*************************************************************************/ +/* base_button.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 "base_button.h" +#include "os/keyboard.h" +#include "print_string.h" +#include "button_group.h" + + +void BaseButton::_input_event(InputEvent p_event) { + + + if (status.disabled) // no interaction with disabled button + return; + + switch(p_event.type) { + + case InputEvent::MOUSE_BUTTON: { + + const InputEventMouseButton &b=p_event.mouse_button; + + if ( status.disabled || b.button_index!=1 ) + return; + + if (status.pressing_button) + break; + + if (status.click_on_press) { + + if (b.pressed) { + + if (!toggle_mode) { //mouse press attempt + + pressed(); + emit_signal("pressed"); + + } else { + + status.pressed=!status.pressed; + pressed(); + emit_signal("pressed"); + + toggled(status.pressed); + emit_signal("toggled",status.pressed); + + } + + + } + + break; + } + + if (b.pressed) { + + status.press_attempt=true; + status.pressing_inside=true; + + } else { + + + if (status.press_attempt &&status.pressing_inside) { + + if (!toggle_mode) { //mouse press attempt + + pressed(); + emit_signal("pressed"); + + } else { + + status.pressed=!status.pressed; + + pressed(); + emit_signal("pressed"); + + toggled(status.pressed); + emit_signal("toggled",status.pressed); + + } + + } + + status.press_attempt=false; + + } + + update(); + } break; + case InputEvent::MOUSE_MOTION: { + + if (status.press_attempt && status.pressing_button==0) { + bool last_press_inside=status.pressing_inside; + status.pressing_inside=has_point(Point2(p_event.mouse_motion.x,p_event.mouse_motion.y)); + if (last_press_inside!=status.pressing_inside) + update(); + } + } break; + case InputEvent::JOYSTICK_BUTTON: + case InputEvent::KEY: { + + + if (p_event.is_echo()) { + break; + } + + if (status.disabled) { + break; + } + + if (status.press_attempt && status.pressing_button==0) { + break; + } + + if (p_event.is_action("ui_accept")) { + + if (p_event.is_pressed()) { + + status.pressing_button++; + status.press_attempt=true; + status.pressing_inside=true; + + } else if (status.press_attempt) { + + if (status.pressing_button) + status.pressing_button--; + + if (status.pressing_button) + break; + + status.press_attempt=false; + status.pressing_inside=false; + + if (!toggle_mode) { //mouse press attempt + + pressed(); + emit_signal("pressed"); + } else { + + status.pressed=!status.pressed; + + pressed(); + emit_signal("pressed"); + + toggled(status.pressed); + emit_signal("toggled",status.pressed); + } + } + + accept_event(); + update(); + + } + } + + } +} + +void BaseButton::_notification(int p_what) { + + + if (p_what==NOTIFICATION_MOUSE_ENTER) { + + status.hovering=true; + update(); + } + + if (p_what==NOTIFICATION_MOUSE_EXIT) { + status.hovering=false; + update(); + } + if (p_what==NOTIFICATION_FOCUS_EXIT) { + + if (status.pressing_button && status.press_attempt) { + status.press_attempt=false; + status.pressing_button=0; + } + } + + if (p_what==NOTIFICATION_ENTER_SCENE) { + + CanvasItem *ci=this; + while(ci) { + + ButtonGroup *bg = ci->cast_to(); + if (bg) { + + group=bg; + group->_add_button(this); + } + + ci=ci->get_parent_item(); + } + } + + if (p_what==NOTIFICATION_EXIT_SCENE) { + + if (group) + group->_remove_button(this); + } + +} + +void BaseButton::pressed() { + + if (get_script_instance()) + get_script_instance()->call("pressed"); +} + +void BaseButton::toggled(bool p_pressed) { + + if (get_script_instance()) + get_script_instance()->call("toggled",p_pressed); + +} + + +void BaseButton::set_disabled(bool p_disabled) { + + status.disabled = p_disabled; + update(); + _change_notify("disabled"); + if (p_disabled) + set_focus_mode(FOCUS_NONE); + else + set_focus_mode(FOCUS_ALL); +}; + +bool BaseButton::is_disabled() const { + + return status.disabled; +}; + +void BaseButton::set_pressed(bool p_pressed) { + + if (!toggle_mode) + return; + if (status.pressed==p_pressed) + return; + _change_notify("pressed"); + status.pressed=p_pressed; + update(); +} + +bool BaseButton::is_pressing() const{ + + return status.press_attempt; +} + +bool BaseButton::is_pressed() const { + + return toggle_mode?status.pressed:status.press_attempt; +} + + +BaseButton::DrawMode BaseButton::get_draw_mode() const { + + if (status.disabled) { + return DRAW_DISABLED; + }; + + //print_line("press attempt: "+itos(status.press_attempt)+" hover: "+itos(status.hovering)+" pressed: "+itos(status.pressed)); + if (status.press_attempt==false && status.hovering && !status.pressed) { + + + return DRAW_HOVER; + } else { + /* determine if pressed or not */ + + bool pressing; + if (status.press_attempt) { + + pressing=status.pressing_inside; + if (status.pressed) + pressing=!pressing; + } else { + + pressing=status.pressed; + } + + if (pressing) + return DRAW_PRESSED; + else + return DRAW_NORMAL; + } + + return DRAW_NORMAL; +} + +void BaseButton::set_toggle_mode(bool p_on) { + + toggle_mode=p_on; +} + +bool BaseButton::is_toggle_mode() const { + + return toggle_mode; +} + +void BaseButton::set_click_on_press(bool p_click_on_press) { + + status.click_on_press=p_click_on_press; +} + +bool BaseButton::get_click_on_press() const { + + return status.click_on_press; +} + + +void BaseButton::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("_input_event"),&BaseButton::_input_event); + ObjectTypeDB::bind_method(_MD("set_pressed","pressed"),&BaseButton::set_pressed); + ObjectTypeDB::bind_method(_MD("is_pressed"),&BaseButton::is_pressed); + ObjectTypeDB::bind_method(_MD("set_toggle_mode","enabled"),&BaseButton::set_toggle_mode); + ObjectTypeDB::bind_method(_MD("is_toggle_mode"),&BaseButton::is_toggle_mode); + ObjectTypeDB::bind_method(_MD("set_disabled","disabled"),&BaseButton::set_disabled); + ObjectTypeDB::bind_method(_MD("is_disabled"),&BaseButton::is_disabled); + ObjectTypeDB::bind_method(_MD("set_click_on_press","enable"),&BaseButton::set_click_on_press); + ObjectTypeDB::bind_method(_MD("get_click_on_press"),&BaseButton::get_click_on_press); + + ADD_SIGNAL( MethodInfo("pressed" ) ); + ADD_SIGNAL( MethodInfo("toggled", PropertyInfo( Variant::BOOL,"pressed") ) ); + ADD_PROPERTY( PropertyInfo( Variant::BOOL, "disabled"), _SCS("set_disabled"), _SCS("is_disabled")); + ADD_PROPERTY( PropertyInfo( Variant::BOOL, "toggle_mode"), _SCS("set_toggle_mode"), _SCS("is_toggle_mode")); + ADD_PROPERTY( PropertyInfo( Variant::BOOL, "click_on_press"), _SCS("set_click_on_press"), _SCS("get_click_on_press")); + +} + +BaseButton::BaseButton() { + + toggle_mode=false; + status.pressed=false; + status.press_attempt=false; + status.hovering=false; + status.pressing_inside=false; + status.disabled = false; + status.click_on_press=false; + status.pressing_button=0; + set_focus_mode( FOCUS_ALL ); + group=NULL; + + +} + +BaseButton::~BaseButton() +{ +} + + diff --git a/scene/gui/base_button.h b/scene/gui/base_button.h new file mode 100644 index 00000000000..65563ddc03d --- /dev/null +++ b/scene/gui/base_button.h @@ -0,0 +1,102 @@ +/*************************************************************************/ +/* base_button.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 BASE_BUTTON_H +#define BASE_BUTTON_H + +#include "scene/gui/control.h" +/** + @author Juan Linietsky +*/ + + +class ButtonGroup; + +class BaseButton : public Control { + + OBJ_TYPE( BaseButton, Control ); + + bool toggle_mode; + + struct Status { + + bool pressed; + bool hovering; + bool press_attempt; + bool pressing_inside; + + bool disabled; + bool click_on_press; + int pressing_button; + + } status; + + ButtonGroup *group; + + +protected: + + enum DrawMode { + DRAW_NORMAL, + DRAW_PRESSED, + DRAW_HOVER, + DRAW_DISABLED, + }; + + + DrawMode get_draw_mode() const; + + virtual void pressed(); + virtual void toggled(bool p_pressed); + static void _bind_methods(); + virtual void _input_event(InputEvent p_event); + void _notification(int p_what); + +public: + + /* Signals */ + + bool is_pressed() const; ///< return wether button is pressed (toggled in) + bool is_pressing() const; ///< return wether button is pressed (toggled in) + void set_pressed(bool p_pressed); ///only works in toggle mode + void set_toggle_mode(bool p_on); + bool is_toggle_mode() const; + + void set_disabled(bool p_disabled); + bool is_disabled() const; + + void set_click_on_press(bool p_click_on_press); + bool get_click_on_press() const; + + + BaseButton(); + ~BaseButton(); + +}; + +#endif diff --git a/scene/gui/box_container.cpp b/scene/gui/box_container.cpp new file mode 100644 index 00000000000..216c6d71226 --- /dev/null +++ b/scene/gui/box_container.cpp @@ -0,0 +1,290 @@ +/*************************************************************************/ +/* box_container.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 "box_container.h" +#include "margin_container.h" +#include "label.h" + +struct _MinSizeCache { + + int min_size; + bool will_stretch; + int final_size; +}; + +void BoxContainer::_resort() { + + /** First pass, determine minimum size AND amount of stretchable elements */ + + + Size2i new_size=get_size();; + + int sep=get_constant("separation",vertical?"VBoxContainer":"HBoxContainer"); + + bool first=true; + int children_count=0; + int stretch_min=0; + int stretch_avail=0; + float stretch_ratio_total=0; + Map min_size_cache; + + for(int i=0;icast_to(); + if (!c || !c->is_visible()) + continue; + if (c->is_set_as_toplevel()) + continue; + + Size2i size=c->get_combined_minimum_size(); + _MinSizeCache msc; + + if (vertical) { /* VERTICAL */ + stretch_min+=size.height; + msc.min_size=size.height; + msc.will_stretch=c->get_v_size_flags() & SIZE_EXPAND; + + } else { /* HORIZONTAL */ + stretch_min+=size.width; + msc.min_size=size.width; + msc.will_stretch=c->get_h_size_flags() & SIZE_EXPAND; + } + + if (msc.will_stretch) { + stretch_avail+=msc.min_size; + stretch_ratio_total+=c->get_stretch_ratio(); + } + msc.final_size=msc.min_size; + min_size_cache[c]=msc; + children_count++; + } + + if (children_count==0) + return; + + int stretch_max = (vertical? new_size.height : new_size.width ) - (children_count-1) * sep; + int stretch_diff = stretch_max - stretch_min; + if (stretch_diff<0) { + //avoid negative stretch space + stretch_max=stretch_min; + stretch_diff=0; + } + + stretch_avail+=stretch_diff; //available stretch space. + /** Second, pass sucessively to discard elements that can't be stretched, this will run while stretchable + elements exist */ + + + while(stretch_ratio_total>0) { // first of all, dont even be here if no stretchable objects exist + + bool refit_successful=true; //assume refit-test will go well + + for(int i=0;icast_to(); + if (!c || !c->is_visible()) + continue; + if (c->is_set_as_toplevel()) + continue; + + ERR_FAIL_COND(!min_size_cache.has(c)); + _MinSizeCache &msc=min_size_cache[c]; + + if (msc.will_stretch) { //wants to stretch + //let's see if it can really stretch + + int final_pixel_size=stretch_avail * c->get_stretch_ratio() / stretch_ratio_total; + if (final_pixel_sizeget_stretch_ratio(); + refit_successful=false; + stretch_avail-=msc.min_size; + msc.final_size=msc.min_size; + break; + } else { + msc.final_size=final_pixel_size; + } + } + } + + if (refit_successful) //uf refit went well, break + break; + + } + + + /** Final pass, draw and stretch elements **/ + + + int ofs=0; + + first=true; + int idx=0; + + for(int i=0;icast_to(); + if (!c || !c->is_visible()) + continue; + if (c->is_set_as_toplevel()) + continue; + + _MinSizeCache &msc=min_size_cache[c]; + + + if (first) + first=false; + else + ofs+=sep; + + int from=ofs; + int to=ofs+msc.final_size; + + + if (msc.will_stretch && idx==children_count-1) { + //adjust so the last one always fits perfect + //compensating for numerical imprecision + + to=vertical?new_size.height:new_size.width; + + } + + int size=to-from; + + Rect2 rect; + + if (vertical) { + + rect=Rect2(0,from,new_size.width,size); + } else { + + rect=Rect2(from,0,size,new_size.height); + + } + + fit_child_in_rect(c,rect); + + ofs=to; + idx++; + } + +} + +Size2 BoxContainer::get_minimum_size() const { + + + /* Calculate MINIMUM SIZE */ + + Size2i minimum; + int sep=get_constant("separation",vertical?"VBoxContainer":"HBoxContainer"); + + bool first=true; + + for(int i=0;icast_to(); + if (!c) + continue; + if (c->is_set_as_toplevel()) + continue; + + if (c->is_hidden()) { + continue; + } + + Size2i size=c->get_combined_minimum_size(); + + if (vertical) { /* VERTICAL */ + + if ( size.width > minimum.width ) { + minimum.width=size.width; + } + + minimum.height+=size.height+(first?0:sep); + + } else { /* HORIZONTAL */ + + if ( size.height > minimum.height ) { + minimum.height=size.height; + } + + minimum.width+=size.width+(first?0:sep); + + } + + first=false; + } + + return minimum; +} + +void BoxContainer::_notification(int p_what) { + + switch(p_what) { + + case NOTIFICATION_SORT_CHILDREN: { + + _resort(); + } break; + } +} + +void BoxContainer::add_spacer(bool p_begin) { + + Control *c = memnew( Control ); + if (vertical) + c->set_v_size_flags(SIZE_EXPAND_FILL); + else + c->set_h_size_flags(SIZE_EXPAND_FILL); + + add_child(c); + if (p_begin) + move_child(c,0); +} + +BoxContainer::BoxContainer(bool p_vertical) { + + vertical=p_vertical; +// set_ignore_mouse(true); + set_stop_mouse(false); +} + + +MarginContainer* VBoxContainer::add_margin_child(const String& p_label,Control *p_control,bool p_expand) { + + Label *l = memnew( Label ); + l->set_text(p_label); + add_child(l); + MarginContainer *mc = memnew( MarginContainer ); + mc->add_child(p_control); + add_child(mc); + if (p_expand) + mc->set_v_size_flags(SIZE_EXPAND_FILL); + + return mc; +} diff --git a/scene/gui/box_container.h b/scene/gui/box_container.h new file mode 100644 index 00000000000..fb305f0423e --- /dev/null +++ b/scene/gui/box_container.h @@ -0,0 +1,76 @@ +/*************************************************************************/ +/* box_container.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 BOX_CONTAINER_H +#define BOX_CONTAINER_H + +#include "scene/gui/container.h" + +class BoxContainer : public Container { + + OBJ_TYPE(BoxContainer,Container); + + bool vertical; + + void _resort(); +protected: + + void _notification(int p_what); +public: + + void add_spacer(bool p_begin=false); + + virtual Size2 get_minimum_size() const; + + BoxContainer(bool p_vertical=false); +}; + + +class HBoxContainer : public BoxContainer { + + OBJ_TYPE(HBoxContainer,BoxContainer); + +public: + + HBoxContainer() : BoxContainer(false) {} +}; + + +class MarginContainer; +class VBoxContainer : public BoxContainer { + + OBJ_TYPE(VBoxContainer,BoxContainer); + +public: + + MarginContainer* add_margin_child(const String& p_label,Control *p_control,bool p_expand=false); + + VBoxContainer() : BoxContainer(true) {} +}; + +#endif // BOX_CONTAINER_H diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp new file mode 100644 index 00000000000..0532ab22daa --- /dev/null +++ b/scene/gui/button.cpp @@ -0,0 +1,245 @@ +/*************************************************************************/ +/* button.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 "button.h" +#include "servers/visual_server.h" +#include "print_string.h" +#include "translation.h" + + +Size2 Button::get_minimum_size() const { + + Size2 minsize=get_font("font")->get_string_size( text ); + if (clip_text) + minsize.width=0; + + Ref _icon; + if (icon.is_null() && has_icon("icon")) + _icon=Control::get_icon("icon"); + else + _icon=icon; + + if (!_icon.is_null()) { + + minsize.height=MAX( minsize.height, _icon->get_height() ); + minsize.width+=_icon->get_width(); + if (text!="") + minsize.width+=get_constant("hseparation"); + } + + return get_stylebox("normal" )->get_minimum_size() + minsize; + +} + + +void Button::_notification(int p_what) { + + if (p_what==NOTIFICATION_DRAW) { + + RID ci = get_canvas_item(); + Size2 size=get_size(); + Color color; + + //print_line(get_text()+": "+itos(is_flat())+" hover "+itos(get_draw_mode())); + + switch( get_draw_mode() ) { + + case DRAW_NORMAL: { + + if (!flat) + get_stylebox("normal" )->draw( ci, Rect2(Point2(0,0), size) ); + color=get_color("font_color"); + } break; + case DRAW_PRESSED: { + + get_stylebox("pressed" )->draw( ci, Rect2(Point2(0,0), size) ); + if (has_color("font_color_pressed")) + color=get_color("font_color_pressed"); + else + color=get_color("font_color"); + + } break; + case DRAW_HOVER: { + + get_stylebox("hover" )->draw( ci, Rect2(Point2(0,0), size) ); + color=get_color("font_color_hover"); + + } break; + case DRAW_DISABLED: { + + get_stylebox("disabled" )->draw( ci, Rect2(Point2(0,0), size) ); + color=get_color("font_color_disabled"); + + } break; + } + Ref style = get_stylebox("normal" ); + Ref font=get_font("font"); + Ref _icon; + if (icon.is_null() && has_icon("icon")) + _icon=Control::get_icon("icon"); + else + _icon=icon; + + Point2 icon_ofs = (!_icon.is_null())?Point2( _icon->get_width() + get_constant("hseparation"), 0):Point2(); + int text_clip=size.width - style->get_minimum_size().width - icon_ofs.width; + Point2 text_ofs = (size - style->get_minimum_size() - icon_ofs - font->get_string_size( text ) )/2.0; + + switch(align) { + case ALIGN_LEFT: { + text_ofs.x = style->get_margin(MARGIN_LEFT) + icon_ofs.x; + text_ofs.y+=style->get_offset().y; + } break; + case ALIGN_CENTER: { + text_ofs+=icon_ofs; + text_ofs+=style->get_offset(); + } break; + case ALIGN_RIGHT: { + text_ofs.x=size.x - style->get_margin(MARGIN_RIGHT) - font->get_string_size( text ).x; + text_ofs.y+=style->get_offset().y; + } break; + } + + + text_ofs.y+=font->get_ascent(); + font->draw( ci, text_ofs.floor(), text, color,clip_text?text_clip:-1); + if (!_icon.is_null()) { + + _icon->draw(ci,Point2(style->get_offset().x, Math::floor( (size.height-_icon->get_height())/2.0 ) ),is_disabled()?Color(1,1,1,0.4):Color(1,1,1) ); + } + + if (has_focus()) { + + Ref style = get_stylebox("focus"); + style->draw(ci,Rect2(Point2(),size)); + } + + } +} + +void Button::set_text(const String& p_text) { + + if (text==p_text) + return; + text=XL_MESSAGE(p_text); + update(); + _change_notify("text"); + minimum_size_changed(); +} +String Button::get_text() const { + + return text; +} + + +void Button::set_icon(const Ref& p_icon) { + + if (icon==p_icon) + return; + icon=p_icon; + update(); + _change_notify("icon"); + minimum_size_changed(); +} + +Ref Button::get_icon() const { + + return icon; +} + +void Button::set_flat(bool p_flat) { + + flat=p_flat; + update(); + _change_notify("flat"); +} + +bool Button::is_flat() const { + + return flat; +} + +void Button::set_clip_text(bool p_clip_text) { + + clip_text=p_clip_text; + update(); + minimum_size_changed(); +} + +bool Button::get_clip_text() const { + + return clip_text; +} + +void Button::set_text_align(TextAlign p_align) { + + align=p_align; + update(); +} + +Button::TextAlign Button::get_text_align() const { + + return align; +} + + +void Button::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("set_text","text"),&Button::set_text); + ObjectTypeDB::bind_method(_MD("get_text"),&Button::get_text); + ObjectTypeDB::bind_method(_MD("set_button_icon","texture:Texture"),&Button::set_icon); + ObjectTypeDB::bind_method(_MD("get_button_icon:Texture"),&Button::get_icon); + ObjectTypeDB::bind_method(_MD("set_flat","enabled"),&Button::set_flat); + ObjectTypeDB::bind_method(_MD("set_clip_text","enabled"),&Button::set_clip_text); + ObjectTypeDB::bind_method(_MD("get_clip_text"),&Button::get_clip_text); + ObjectTypeDB::bind_method(_MD("set_text_align","align"),&Button::set_text_align); + ObjectTypeDB::bind_method(_MD("get_text_align"),&Button::get_text_align); + ObjectTypeDB::bind_method(_MD("is_flat"),&Button::is_flat); + + ADD_PROPERTY( PropertyInfo( Variant::STRING, "text", PROPERTY_HINT_NONE,"",PROPERTY_USAGE_DEFAULT_INTL ), _SCS("set_text"),_SCS("get_text") ); + ADD_PROPERTY( PropertyInfo( Variant::OBJECT, "icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture" ), _SCS("set_button_icon"),_SCS("get_button_icon") ); + ADD_PROPERTY( PropertyInfo( Variant::BOOL, "flat" ), _SCS("set_flat"),_SCS("is_flat") ); + ADD_PROPERTY( PropertyInfo( Variant::BOOL, "clip_text" ), _SCS("set_clip_text"),_SCS("get_clip_text") ); + ADD_PROPERTY( PropertyInfo( Variant::INT, "align",PROPERTY_HINT_ENUM,"Left,Center,Right" ), _SCS("set_text_align"),_SCS("get_text_align") ); + +} + +Button::Button(const String &p_text) { + + flat=false; + clip_text=false; + set_stop_mouse(true); + set_text(p_text); + align=ALIGN_CENTER; +} + + +Button::~Button() +{ +} + + diff --git a/scene/gui/button.h b/scene/gui/button.h new file mode 100644 index 00000000000..cf79e23579b --- /dev/null +++ b/scene/gui/button.h @@ -0,0 +1,88 @@ +/*************************************************************************/ +/* button.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 BUTTON_H +#define BUTTON_H + +#include "scene/gui/base_button.h" +/** + @author Juan Linietsky +*/ +class Button : public BaseButton { + + OBJ_TYPE( Button, BaseButton ); +public: + + + enum TextAlign { + ALIGN_LEFT, + ALIGN_CENTER, + ALIGN_RIGHT + }; + +private: + + bool flat; + String text; + Ref icon; + bool clip_text; + TextAlign align; + + + +protected: + + virtual Size2 get_minimum_size() const; + void _notification(int p_what); + static void _bind_methods(); +public: +// + void set_text(const String& p_text); + String get_text() const; + + void set_icon(const Ref& p_icon); + Ref get_icon() const; + + void set_flat(bool p_flat); + bool is_flat() const; + + void set_clip_text(bool p_clip_text); + bool get_clip_text() const; + + void set_text_align(TextAlign p_align); + TextAlign get_text_align() const; + + Button(const String& p_text=String()); + ~Button(); + +}; + + +VARIANT_ENUM_CAST(Button::TextAlign); + +#endif diff --git a/scene/gui/button_array.cpp b/scene/gui/button_array.cpp new file mode 100644 index 00000000000..e3a2b7b2905 --- /dev/null +++ b/scene/gui/button_array.cpp @@ -0,0 +1,504 @@ +/*************************************************************************/ +/* button_array.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 "button_array.h" + + +bool ButtonArray::_set(const StringName& p_name, const Variant& p_value) { + + String n=String(p_name); + if (n.begins_with("button/")) { + + String what = n.get_slice("/",1); + if (what=="count") { + int new_size=p_value; + if (new_size>0 && buttons.size()==0) { + selected=0; + } + + if (new_size < buttons.size()) { + if (selected>=new_size) + selected=new_size-1; + } + buttons.resize(new_size); + _change_notify(); + minimum_size_changed(); + } else if (what=="align") { + set_align(Align(p_value.operator int())); + } else if (what=="selected") { + set_selected(p_value); + } else if (what == "min_button_size") { + min_button_size = p_value; + } else { + int idx=what.to_int(); + ERR_FAIL_INDEX_V(idx,buttons.size(),false); + String f = n.get_slice("/",2); + if (f=="text") + buttons[idx].text=p_value; + else if (f=="icon") + buttons[idx].icon=p_value; + else + return false; + + } + + update(); + return true; + } + + return false; + +} + +bool ButtonArray::_get(const StringName& p_name,Variant &r_ret) const { + + String n=String(p_name); + if (n.begins_with("button/")) { + + String what = n.get_slice("/",1); + if (what=="count") { + r_ret=buttons.size(); + } else if (what=="align") { + r_ret=get_align(); + } else if (what=="selected") { + r_ret=get_selected(); + } else if (what == "min_button_size"){ + r_ret = min_button_size; + } else { + int idx=what.to_int(); + ERR_FAIL_INDEX_V(idx,buttons.size(),false); + String f = n.get_slice("/",2); + if (f=="text") + r_ret=buttons[idx].text; + else if (f=="icon") + r_ret=buttons[idx].icon; + else + return false; + + } + + return true; + } + + return false; +} +void ButtonArray::_get_property_list( List *p_list) const { + + p_list->push_back( PropertyInfo( Variant::INT, "button/count",PROPERTY_HINT_RANGE,"0,512,1")); + p_list->push_back( PropertyInfo( Variant::INT, "button/min_button_size",PROPERTY_HINT_RANGE,"0,1024,1")); + p_list->push_back( PropertyInfo( Variant::INT, "button/align",PROPERTY_HINT_ENUM,"Begin,Center,End,Fill,Expand")); + for(int i=0;ipush_back( PropertyInfo( Variant::STRING, base+"text")); + p_list->push_back( PropertyInfo( Variant::OBJECT, base+"icon",PROPERTY_HINT_RESOURCE_TYPE,"Texture")); + } + if (buttons.size()>0) { + p_list->push_back( PropertyInfo( Variant::INT, "button/selected",PROPERTY_HINT_RANGE,"0,"+itos(buttons.size()-1)+",1")); + } + +} + + +Size2 ButtonArray::get_minimum_size() const { + + Ref style_normal = get_stylebox("normal"); + Ref style_selected = get_stylebox("selected"); + Ref font_normal = get_font("font"); + Ref font_selected = get_font("font_selected"); + int icon_sep = get_constant("icon_separator"); + int button_sep = get_constant("button_separator"); + + Size2 minsize; + + for(int i=0;i sb = i==selected ? style_selected : style_normal; + Ref f = i==selected ? font_selected : font_normal; + + Size2 ms; + ms = f->get_string_size(buttons[i].text); + if (buttons[i].icon.is_valid()) { + + Size2 bs = buttons[i].icon->get_size(); + ms.height = MAX(ms.height,bs.height); + ms.width+=bs.width+icon_sep; + } + + ms+=sb->get_minimum_size(); + + buttons[i]._ms_cache=ms[orientation]; + + minsize[orientation]+=ms[orientation]; + if (i>0) + minsize[orientation]+=button_sep; + minsize[!orientation] = MAX(minsize[!orientation],ms[!orientation]); + + } + + return minsize; + + +} + +void ButtonArray::_notification(int p_what) { + + switch(p_what) { + case NOTIFICATION_READY:{ + MethodInfo mi; + mi.name="mouse_sub_enter"; + + add_user_signal(mi); + + }break; + case NOTIFICATION_DRAW: { + + Size2 size=get_size(); + Size2 minsize=get_combined_minimum_size(); + Ref style_normal = get_stylebox("normal"); + Ref style_selected = get_stylebox("selected"); + Ref style_focus = get_stylebox("focus"); + Ref style_hover = get_stylebox("hover"); + Ref font_normal = get_font("font"); + Ref font_selected = get_font("font_selected"); + int icon_sep = get_constant("icon_separator"); + int button_sep = get_constant("button_separator"); + Color color_normal = get_color("font_color"); + Color color_selected = get_color("font_color_selected"); + + int sep=button_sep; + int ofs=0; + int expand=0; + + switch(align) { + case ALIGN_BEGIN: { + + ofs=0; + } break; + case ALIGN_CENTER: { + + ofs=Math::floor((size[orientation] - minsize[orientation])/2); + } break; + case ALIGN_END: { + + ofs=Math::floor((size[orientation] - minsize[orientation])); + } break; + case ALIGN_FILL: { + + if (buttons.size()>1) + sep+=Math::floor((size[orientation]- minsize[orientation])/(buttons.size()-1.0)); + ofs=0; + } break; + case ALIGN_EXPAND_FILL: { + + ofs=0; + expand=size[orientation] - minsize[orientation]; + } break; + + + + } + + int op_size = orientation==VERTICAL ? size.width : size.height; + + + for(int i=0;i0) { + s+=expand/buttons.size(); + } + if(min_button_size != -1 && s < min_button_size){ + s = min_button_size; + } + + Rect2 r; + r.pos[orientation]=ofs; + r.pos[!orientation]=0; + r.size[orientation]=s; + r.size[!orientation]=op_size; + + Ref f; + Color c; + if (i==selected) { + draw_style_box(style_selected,r); + f=font_selected; + c=color_selected; + if (has_focus()) + draw_style_box(style_focus,r); + } else { + if (hover==i) + draw_style_box(style_hover,r); + else + draw_style_box(style_normal,r); + f=font_normal; + c=color_normal; + } + + Size2 ssize = f->get_string_size(buttons[i].text); + if (buttons[i].icon.is_valid()) { + + ssize.x+=buttons[i].icon->get_width(); + } + Point2 text_ofs=((r.size-ssize)/2.0+Point2(0,f->get_ascent())).floor(); + if (buttons[i].icon.is_valid()) { + + draw_texture(buttons[i].icon,r.pos+Point2(text_ofs.x,Math::floor((r.size.height-buttons[i].icon->get_height())/2.0))); + text_ofs.x+=buttons[i].icon->get_width()+icon_sep; + + } + draw_string(f,text_ofs+r.pos,buttons[i].text,c); + buttons[i]._pos_cache=ofs; + buttons[i]._size_cache=s; + + ofs+=s; + ofs+=sep; + } + + } break; + } +} + + +void ButtonArray::_input_event(const InputEvent& p_event) { + + if ( + ( (orientation==HORIZONTAL && p_event.is_action("ui_left") ) || + (orientation==VERTICAL && p_event.is_action("ui_up") ) ) + && p_event.is_pressed() && selected>0) { + set_selected(selected-1); + accept_event(); + emit_signal("button_selected",selected); + return; + + } + + if ( + ( (orientation==HORIZONTAL && p_event.is_action("ui_right") ) || + (orientation==VERTICAL && p_event.is_action("ui_down") ) ) + && p_event.is_pressed() && selected<(buttons.size()-1)) { + set_selected(selected+1); + accept_event(); + emit_signal("button_selected",selected); + return; + + } + + if (p_event.type==InputEvent::MOUSE_BUTTON && p_event.mouse_button.pressed && p_event.mouse_button.button_index==BUTTON_LEFT) { + + int ofs = orientation==HORIZONTAL ? p_event.mouse_button.x: p_event.mouse_button.y; + + for(int i=0;i=buttons[i]._pos_cache && ofs=buttons[i]._pos_cache && ofs& p_icon,const String& p_button) { + + Button button; + button.text=p_button; + button.icon=p_icon; + buttons.push_back(button); + if (selected==-1) + selected=0; + + update(); + +} + +void ButtonArray::set_button_text(int p_button, const String& p_text) { + + ERR_FAIL_INDEX(p_button,buttons.size()); + buttons[p_button].text=p_text; + update(); + minimum_size_changed(); + +} +void ButtonArray::set_button_icon(int p_button, const Ref& p_icon) { + + ERR_FAIL_INDEX(p_button,buttons.size()); + buttons[p_button].icon=p_icon; + update(); + minimum_size_changed(); +} +String ButtonArray::get_button_text(int p_button) const { + + ERR_FAIL_INDEX_V(p_button,buttons.size(),""); + return buttons[p_button].text; +} +Ref ButtonArray::get_button_icon(int p_button) const { + + ERR_FAIL_INDEX_V(p_button,buttons.size(),Ref()); + return buttons[p_button].icon; + +} + +int ButtonArray::get_selected() const { + + return selected; +} + +int ButtonArray::get_hovered() const { + + return hover; +} + +void ButtonArray::set_selected(int p_selected) { + + ERR_FAIL_INDEX(p_selected,buttons.size()); + selected=p_selected; + update(); + +} + +void ButtonArray::erase_button(int p_button) { + + ERR_FAIL_INDEX(p_button,buttons.size()); + buttons.remove(p_button); + if (p_button>=selected) + selected--; + if (selected<0) + selected=0; + if (selected>=buttons.size()) + selected=buttons.size()-1; + + update(); +} + +void ButtonArray::clear(){ + + buttons.clear(); + selected=-1; + update(); +} + +int ButtonArray::get_button_count() const { + + return buttons.size(); +} + +void ButtonArray::get_translatable_strings(List *p_strings) const { + + + for(int i=0;ipush_back(buttons[i].text); +} + + +void ButtonArray::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("add_button","text"),&ButtonArray::add_button); + ObjectTypeDB::bind_method(_MD("add_icon_button","icon","text"),&ButtonArray::add_icon_button,DEFVAL("")); + ObjectTypeDB::bind_method(_MD("set_button_text","button","text"),&ButtonArray::set_button_text); + ObjectTypeDB::bind_method(_MD("set_button_icon","button","icon"),&ButtonArray::set_button_icon); + ObjectTypeDB::bind_method(_MD("get_button_text","button"),&ButtonArray::get_button_text); + ObjectTypeDB::bind_method(_MD("get_button_icon","button"),&ButtonArray::get_button_icon); + ObjectTypeDB::bind_method(_MD("get_button_count"),&ButtonArray::get_button_count); + ObjectTypeDB::bind_method(_MD("get_selected"),&ButtonArray::get_selected); + ObjectTypeDB::bind_method(_MD("get_hovered"),&ButtonArray::get_hovered); + ObjectTypeDB::bind_method(_MD("set_selected","button"),&ButtonArray::set_selected); + ObjectTypeDB::bind_method(_MD("erase_button","button"),&ButtonArray::erase_button); + ObjectTypeDB::bind_method(_MD("clear"),&ButtonArray::clear); + + ObjectTypeDB::bind_method(_MD("_input_event"),&ButtonArray::_input_event); + + BIND_CONSTANT( ALIGN_BEGIN ); + BIND_CONSTANT( ALIGN_CENTER ); + BIND_CONSTANT( ALIGN_END ); + BIND_CONSTANT( ALIGN_FILL ); + BIND_CONSTANT( ALIGN_EXPAND_FILL ); + + ADD_SIGNAL( MethodInfo("button_selected",PropertyInfo(Variant::INT,"button"))); + +} + +ButtonArray::ButtonArray(Orientation p_orientation) { + + orientation=p_orientation; + selected=-1; + set_focus_mode(FOCUS_ALL); + hover=-1; + min_button_size = -1; +} diff --git a/scene/gui/button_array.h b/scene/gui/button_array.h new file mode 100644 index 00000000000..f5360400399 --- /dev/null +++ b/scene/gui/button_array.h @@ -0,0 +1,123 @@ +/*************************************************************************/ +/* button_array.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 BUTTON_ARRAY_H +#define BUTTON_ARRAY_H + +#include "scene/gui/control.h" + +class ButtonArray : public Control { + + OBJ_TYPE(ButtonArray, Control); +public: + enum Align { + ALIGN_BEGIN, + ALIGN_CENTER, + ALIGN_END, + ALIGN_FILL, + ALIGN_EXPAND_FILL + }; +private: + + Orientation orientation; + Align align; + + struct Button { + + String text; + Ref icon; + mutable int _ms_cache; + mutable int _pos_cache; + mutable int _size_cache; + }; + + int selected; + int hover; + double min_button_size; + + Vector