summaryrefslogtreecommitdiff
path: root/tools/editor
diff options
context:
space:
mode:
authorJuan Linietsky <reduzio@gmail.com>2014-05-14 01:22:15 -0300
committerJuan Linietsky <reduzio@gmail.com>2014-05-14 01:22:15 -0300
commitb324ff7ea584676fcc3292808d7e7ea609982f8e (patch)
treeb80e9aa0b8f2926a398e25ef904f6229cb3e28dd /tools/editor
parent45a509282e912d85c46b40974a2deb926be5be42 (diff)
A bit of everything:
-IMA-ADPCM support for samples, this means that sound effects can be compressed and use 4 timess less RAM. -New 3D import workflow based on Wavefront OBJ. Import single objects as mesh resources instead of full scenes. Many people prefers to work this way. Just like the rest of the imported resources, these are updated in realtime if modified externally. -Mesh resources now support naming surfaces. This helps reimporting to identify which user-created materials must be kept. -Several fixes and improvements to SurfaceTool. -Anti Aliasing added to WorldEnvironment effects (using FXAA) -2D Physics bodies (RigidBody, KinematicBody, etc), Raycasts, Tilemap, etc support collision layers. This makes easy to group which objects collide against which. -2D Trigger shapes can now also trigger collision reporting in other 2D bodies (it used to be in Area2D before) -Viewport render target textures can now be filtered. -Few fixes in GDscript make it easier to work with static functions and class members. -Several and many bugfixes.
Diffstat (limited to 'tools/editor')
-rw-r--r--tools/editor/editor_node.cpp5
-rw-r--r--tools/editor/io_plugins/editor_mesh_import_plugin.cpp555
-rw-r--r--tools/editor/io_plugins/editor_mesh_import_plugin.h29
-rw-r--r--tools/editor/io_plugins/editor_sample_import_plugin.cpp178
-rw-r--r--tools/editor/io_plugins/editor_sample_import_plugin.h1
-rw-r--r--tools/editor/plugins/baked_light_editor_plugin.cpp996
-rw-r--r--tools/editor/plugins/baked_light_editor_plugin.h70
-rw-r--r--tools/editor/plugins/sample_editor_plugin.cpp149
-rw-r--r--tools/editor/plugins/sample_library_editor_plugin.cpp2
-rw-r--r--tools/editor/property_editor.cpp12
-rw-r--r--tools/editor/property_editor.h2
11 files changed, 1901 insertions, 98 deletions
diff --git a/tools/editor/editor_node.cpp b/tools/editor/editor_node.cpp
index fc644e76c5..9821dad872 100644
--- a/tools/editor/editor_node.cpp
+++ b/tools/editor/editor_node.cpp
@@ -85,12 +85,15 @@
#include "plugins/animation_tree_editor_plugin.h"
#include "plugins/tile_set_editor_plugin.h"
#include "plugins/animation_player_editor_plugin.h"
+#include "plugins/baked_light_editor_plugin.h"
// end
#include "tools/editor/io_plugins/editor_texture_import_plugin.h"
#include "tools/editor/io_plugins/editor_scene_import_plugin.h"
#include "tools/editor/io_plugins/editor_font_import_plugin.h"
#include "tools/editor/io_plugins/editor_sample_import_plugin.h"
#include "tools/editor/io_plugins/editor_translation_import_plugin.h"
+#include "tools/editor/io_plugins/editor_mesh_import_plugin.h"
+
EditorNode *EditorNode::singleton=NULL;
@@ -4013,6 +4016,7 @@ EditorNode::EditorNode() {
_scene_import->add_importer(_collada_import);
editor_import_export->add_import_plugin( _scene_import);
editor_import_export->add_import_plugin( Ref<EditorSceneAnimationImportPlugin>( memnew(EditorSceneAnimationImportPlugin(this))));
+ editor_import_export->add_import_plugin( Ref<EditorMeshImportPlugin>( memnew(EditorMeshImportPlugin(this))));
editor_import_export->add_import_plugin( Ref<EditorFontImportPlugin>( memnew(EditorFontImportPlugin(this))));
editor_import_export->add_import_plugin( Ref<EditorSampleImportPlugin>( memnew(EditorSampleImportPlugin(this))));
editor_import_export->add_import_plugin( Ref<EditorTranslationImportPlugin>( memnew(EditorTranslationImportPlugin(this))));
@@ -4051,6 +4055,7 @@ EditorNode::EditorNode() {
add_editor_plugin( memnew( Particles2DEditorPlugin(this) ) );
add_editor_plugin( memnew( Path2DEditorPlugin(this) ) );
add_editor_plugin( memnew( PathEditorPlugin(this) ) );
+ add_editor_plugin( memnew( BakedLightEditorPlugin(this) ) );
for(int i=0;i<EditorPlugins::get_plugin_count();i++)
add_editor_plugin( EditorPlugins::create(i,this) );
diff --git a/tools/editor/io_plugins/editor_mesh_import_plugin.cpp b/tools/editor/io_plugins/editor_mesh_import_plugin.cpp
new file mode 100644
index 0000000000..d76a778433
--- /dev/null
+++ b/tools/editor/io_plugins/editor_mesh_import_plugin.cpp
@@ -0,0 +1,555 @@
+#include "editor_mesh_import_plugin.h"
+
+#include "scene/gui/file_dialog.h"
+#include "tools/editor/editor_dir_dialog.h"
+#include "tools/editor/editor_node.h"
+#include "tools/editor/property_editor.h"
+#include "scene/resources/sample.h"
+#include "io/resource_saver.h"
+#include "os/file_access.h"
+#include "io/marshalls.h"
+#include "scene/resources/surface_tool.h"
+
+class _EditorMeshImportOptions : public Object {
+
+ OBJ_TYPE(_EditorMeshImportOptions,Object);
+public:
+
+
+ bool generate_tangents;
+ bool generate_normals;
+ bool flip_faces;
+ bool smooth_shading;
+ bool weld_vertices;
+ bool import_material;
+ bool import_textures;
+ float weld_tolerance;
+
+
+ bool _set(const StringName& p_name, const Variant& p_value) {
+
+ String n = p_name;
+ if (n=="generate/tangents")
+ generate_tangents=p_value;
+ else if (n=="generate/normals")
+ generate_normals=p_value;
+ else if (n=="import/materials")
+ import_material=p_value;
+ else if (n=="import/textures")
+ import_textures=p_value;
+ else if (n=="force/flip_faces")
+ flip_faces=p_value;
+ else if (n=="force/smooth_shading")
+ smooth_shading=p_value;
+ else if (n=="force/weld_vertices")
+ weld_vertices=p_value;
+ else if (n=="force/weld_tolerance")
+ weld_tolerance=p_value;
+ else
+ return false;
+
+ return true;
+
+ }
+
+ bool _get(const StringName& p_name,Variant &r_ret) const{
+
+ String n = p_name;
+ if (n=="generate/tangents")
+ r_ret=generate_tangents;
+ else if (n=="generate/normals")
+ r_ret=generate_normals;
+ else if (n=="import/materials")
+ r_ret=import_material;
+ else if (n=="import/textures")
+ r_ret=import_textures;
+ else if (n=="force/flip_faces")
+ r_ret=flip_faces;
+ else if (n=="force/smooth_shading")
+ r_ret=smooth_shading;
+ else if (n=="force/weld_vertices")
+ r_ret=weld_vertices;
+ else if (n=="force/weld_tolerance")
+ r_ret=weld_tolerance;
+ else
+ return false;
+
+ return true;
+
+ }
+ void _get_property_list( List<PropertyInfo> *p_list) const{
+
+ p_list->push_back(PropertyInfo(Variant::BOOL,"generate/tangents"));
+ p_list->push_back(PropertyInfo(Variant::BOOL,"generate/normals"));
+ //not for nowp
+ //p_list->push_back(PropertyInfo(Variant::BOOL,"import/materials"));
+ //p_list->push_back(PropertyInfo(Variant::BOOL,"import/textures"));
+ p_list->push_back(PropertyInfo(Variant::BOOL,"force/flip_faces"));
+ p_list->push_back(PropertyInfo(Variant::BOOL,"force/smooth_shading"));
+ p_list->push_back(PropertyInfo(Variant::BOOL,"force/weld_vertices"));
+ p_list->push_back(PropertyInfo(Variant::REAL,"force/weld_tolerance",PROPERTY_HINT_RANGE,"0.00001,16,0.00001"));
+ //p_list->push_back(PropertyInfo(Variant::BOOL,"compress/enable"));
+ //p_list->push_back(PropertyInfo(Variant::INT,"compress/bitrate",PROPERTY_HINT_ENUM,"64,96,128,192"));
+
+
+ }
+
+
+ static void _bind_methods() {
+
+
+ ADD_SIGNAL( MethodInfo("changed"));
+ }
+
+
+ _EditorMeshImportOptions() {
+
+ generate_tangents=true;
+ generate_normals=true;
+ flip_faces=false;
+ smooth_shading=false;
+ weld_vertices=true;
+ weld_tolerance=0.0001;
+ import_material=false;
+ import_textures=false;
+
+ }
+
+
+};
+
+class EditorMeshImportDialog : public ConfirmationDialog {
+
+ OBJ_TYPE(EditorMeshImportDialog,ConfirmationDialog);
+
+ EditorMeshImportPlugin *plugin;
+
+ LineEdit *import_path;
+ LineEdit *save_path;
+ FileDialog *file_select;
+ EditorDirDialog *save_select;
+ ConfirmationDialog *error_dialog;
+ PropertyEditor *option_editor;
+
+ _EditorMeshImportOptions *options;
+
+
+public:
+
+ void _choose_files(const Vector<String>& p_path) {
+
+ String files;
+ for(int i=0;i<p_path.size();i++) {
+
+ if (i>0)
+ files+=",";
+ files+=p_path[i];
+ }
+ /*
+ if (p_path.size()) {
+ String srctex=p_path[0];
+ String ipath = EditorImportDB::get_singleton()->find_source_path(srctex);
+
+ if (ipath!="")
+ save_path->set_text(ipath.get_base_dir());
+ }*/
+ import_path->set_text(files);
+
+ }
+ void _choose_save_dir(const String& p_path) {
+
+ save_path->set_text(p_path);
+ }
+
+ void _browse() {
+
+ file_select->popup_centered_ratio();
+ }
+
+ void _browse_target() {
+
+ save_select->popup_centered_ratio();
+
+ }
+
+
+ void popup_import(const String& p_path) {
+
+ popup_centered(Size2(400,400));
+ if (p_path!="") {
+
+ Ref<ResourceImportMetadata> rimd = ResourceLoader::load_import_metadata(p_path);
+ ERR_FAIL_COND(!rimd.is_valid());
+
+ save_path->set_text(p_path.get_base_dir());
+ List<String> opts;
+ rimd->get_options(&opts);
+ for(List<String>::Element *E=opts.front();E;E=E->next()) {
+
+ options->_set(E->get(),rimd->get_option(E->get()));
+ }
+
+ String src = "";
+ for(int i=0;i<rimd->get_source_count();i++) {
+ if (i>0)
+ src+=",";
+ src+=EditorImportPlugin::expand_source_path(rimd->get_source_path(i));
+ }
+ import_path->set_text(src);
+ }
+ }
+
+
+ void _import() {
+
+ Vector<String> meshes = import_path->get_text().split(",");
+
+ if (meshes.size()==0) {
+ error_dialog->set_text("No meshes to import!");
+ error_dialog->popup_centered(Size2(200,100));
+ }
+
+ for(int i=0;i<meshes.size();i++) {
+
+ Ref<ResourceImportMetadata> imd = memnew( ResourceImportMetadata );
+
+ List<PropertyInfo> pl;
+ options->_get_property_list(&pl);
+ for(List<PropertyInfo>::Element *E=pl.front();E;E=E->next()) {
+
+ Variant v;
+ String opt=E->get().name;
+ options->_get(opt,v);
+ imd->set_option(opt,v);
+
+ }
+
+ imd->add_source(EditorImportPlugin::validate_source_path(meshes[i]));
+
+ String dst = save_path->get_text();
+ if (dst=="") {
+ error_dialog->set_text("Save path is empty!");
+ error_dialog->popup_centered(Size2(200,100));
+ }
+
+ dst = dst.plus_file(meshes[i].get_file().basename()+".msh");
+
+ Error err = plugin->import(dst,imd);
+ }
+
+ hide();
+
+ }
+
+
+ void _notification(int p_what) {
+
+
+ if (p_what==NOTIFICATION_ENTER_SCENE) {
+
+ option_editor->edit(options);
+ }
+ }
+
+ static void _bind_methods() {
+
+
+ ObjectTypeDB::bind_method("_choose_files",&EditorMeshImportDialog::_choose_files);
+ ObjectTypeDB::bind_method("_choose_save_dir",&EditorMeshImportDialog::_choose_save_dir);
+ ObjectTypeDB::bind_method("_import",&EditorMeshImportDialog::_import);
+ ObjectTypeDB::bind_method("_browse",&EditorMeshImportDialog::_browse);
+ ObjectTypeDB::bind_method("_browse_target",&EditorMeshImportDialog::_browse_target);
+ // ADD_SIGNAL( MethodInfo("imported",PropertyInfo(Variant::OBJECT,"scene")) );
+ }
+
+ EditorMeshImportDialog(EditorMeshImportPlugin *p_plugin) {
+
+ plugin=p_plugin;
+
+
+ set_title("Single Mesh Import");
+
+ VBoxContainer *vbc = memnew( VBoxContainer );
+ add_child(vbc);
+ set_child_rect(vbc);
+
+
+ HBoxContainer *hbc = memnew( HBoxContainer );
+ vbc->add_margin_child("Source Mesh(es):",hbc);
+
+ import_path = memnew( LineEdit );
+ import_path->set_h_size_flags(SIZE_EXPAND_FILL);
+ hbc->add_child(import_path);
+
+ Button * import_choose = memnew( Button );
+ import_choose->set_text(" .. ");
+ hbc->add_child(import_choose);
+
+ import_choose->connect("pressed", this,"_browse");
+
+ hbc = memnew( HBoxContainer );
+ vbc->add_margin_child("Target Path:",hbc);
+
+ save_path = memnew( LineEdit );
+ save_path->set_h_size_flags(SIZE_EXPAND_FILL);
+ hbc->add_child(save_path);
+
+ Button * save_choose = memnew( Button );
+ save_choose->set_text(" .. ");
+ hbc->add_child(save_choose);
+
+ save_choose->connect("pressed", this,"_browse_target");
+
+ file_select = memnew(FileDialog);
+ file_select->set_access(FileDialog::ACCESS_FILESYSTEM);
+ add_child(file_select);
+ file_select->set_mode(FileDialog::MODE_OPEN_FILES);
+ file_select->connect("files_selected", this,"_choose_files");
+ file_select->add_filter("*.obj ; Wavefront OBJ");
+ save_select = memnew( EditorDirDialog );
+ add_child(save_select);
+
+ // save_select->set_mode(FileDialog::MODE_OPEN_DIR);
+ save_select->connect("dir_selected", this,"_choose_save_dir");
+
+ get_ok()->connect("pressed", this,"_import");
+ get_ok()->set_text("Import");
+
+
+ error_dialog = memnew ( ConfirmationDialog );
+ add_child(error_dialog);
+ error_dialog->get_ok()->set_text("Accept");
+ // error_dialog->get_cancel()->hide();
+
+ set_hide_on_ok(false);
+ options = memnew( _EditorMeshImportOptions );
+
+ option_editor = memnew( PropertyEditor );
+ option_editor->hide_top_label();
+ vbc->add_margin_child("Options:",option_editor,true);
+ }
+
+ ~EditorMeshImportDialog() {
+ memdelete(options);
+ }
+
+};
+
+
+String EditorMeshImportPlugin::get_name() const {
+
+ return "mesh";
+}
+String EditorMeshImportPlugin::get_visible_name() const{
+
+ return "3D Mesh";
+}
+void EditorMeshImportPlugin::import_dialog(const String& p_from){
+
+ dialog->popup_import(p_from);
+}
+Error EditorMeshImportPlugin::import(const String& p_path, const Ref<ResourceImportMetadata>& p_from){
+
+
+ ERR_FAIL_COND_V(p_from->get_source_count()!=1,ERR_INVALID_PARAMETER);
+
+ Ref<ResourceImportMetadata> from=p_from;
+
+ String src_path=EditorImportPlugin::expand_source_path(from->get_source_path(0));
+ FileAccessRef f = FileAccess::open(src_path,FileAccess::READ);
+ ERR_FAIL_COND_V(!f,ERR_CANT_OPEN);
+
+ Ref<Mesh> mesh;
+ Map<String,Ref<Material> > name_map;
+
+ if (FileAccess::exists(p_path)) {
+ mesh=ResourceLoader::load(p_path,"Mesh");
+ if (mesh.is_valid()) {
+ for(int i=0;i<mesh->get_surface_count();i++) {
+
+ if (!mesh->surface_get_material(i).is_valid())
+ continue;
+ String name;
+ if (mesh->surface_get_name(i)!="")
+ name=mesh->surface_get_name(i);
+ else
+ name="Surface "+itos(i+1);
+
+ name_map[name]=mesh->surface_get_material(i);
+ }
+
+ while(mesh->get_surface_count()) {
+ mesh->surface_remove(0);
+ }
+ }
+ }
+
+ if (!mesh.is_valid())
+ mesh = Ref<Mesh>( memnew( Mesh ) );
+
+
+ bool generate_normals=from->get_option("generate/normals");
+ bool generate_tangents=from->get_option("generate/tangents");
+ bool flip_faces=from->get_option("force/flip_faces");
+ bool force_smooth=from->get_option("force/smooth_shading");
+ bool weld_vertices=from->get_option("force/weld_vertices");
+ float weld_tolerance=from->get_option("force/weld_tolerance");
+ Vector<Vector3> vertices;
+ Vector<Vector3> normals;
+ Vector<Vector2> uvs;
+ String name;
+
+ Ref<SurfaceTool> surf_tool = memnew( SurfaceTool) ;
+ surf_tool->begin(Mesh::PRIMITIVE_TRIANGLES);
+ if (force_smooth)
+ surf_tool->add_smooth_group(true);
+ int has_index_data=false;
+
+ while(true) {
+
+
+ String l = f->get_line().strip_edges();
+
+ if (l.begins_with("v ")) {
+ //vertex
+ Vector<String> v = l.split(" ",false);
+ ERR_FAIL_COND_V(v.size()<4,ERR_INVALID_DATA);
+ Vector3 vtx;
+ vtx.x=v[1].to_float();
+ vtx.y=v[2].to_float();
+ vtx.z=v[3].to_float();
+ vertices.push_back(vtx);
+ } else if (l.begins_with("vt ")) {
+ //uv
+ Vector<String> v = l.split(" ",false);
+ ERR_FAIL_COND_V(v.size()<3,ERR_INVALID_DATA);
+ Vector2 uv;
+ uv.x=v[1].to_float();
+ uv.y=v[2].to_float();
+ uvs.push_back(uv);
+
+ } else if (l.begins_with("vn ")) {
+ //normal
+ Vector<String> v = l.split(" ",false);
+ ERR_FAIL_COND_V(v.size()<4,ERR_INVALID_DATA);
+ Vector3 nrm;
+ nrm.x=v[1].to_float();
+ nrm.y=v[2].to_float();
+ nrm.z=v[3].to_float();
+ normals.push_back(nrm);
+ } if (l.begins_with("f ")) {
+ //vertex
+
+ has_index_data=true;
+ Vector<String> v = l.split(" ",false);
+ ERR_FAIL_COND_V(v.size()<4,ERR_INVALID_DATA);
+
+ //not very fast, could be sped up
+
+
+ Vector<String> face[3];
+ face[0] = v[1].split("/");
+ face[1] = v[2].split("/");
+ ERR_FAIL_COND_V(face[0].size()==0,ERR_PARSE_ERROR);
+ ERR_FAIL_COND_V(face[0].size()!=face[1].size(),ERR_PARSE_ERROR);
+ for(int i=2;i<v.size()-1;i++) {
+
+ face[2] = v[i+1].split("/");
+ ERR_FAIL_COND_V(face[0].size()!=face[2].size(),ERR_PARSE_ERROR);
+ for(int j=0;j<3;j++) {
+
+ int idx=j;
+
+ if (!flip_faces && idx<2) {
+ idx=1^idx;
+ }
+
+
+ if (face[idx].size()==3) {
+ int norm = face[idx][2].to_int()-1;
+ ERR_FAIL_INDEX_V(norm,normals.size(),ERR_PARSE_ERROR);
+ surf_tool->add_normal(normals[norm]);
+ }
+
+ if (face[idx].size()>=2 && face[idx][1]!=String()) {
+
+ int uv = face[idx][1].to_int()-1;
+ ERR_FAIL_INDEX_V(uv,uvs.size(),ERR_PARSE_ERROR);
+ surf_tool->add_uv(uvs[uv]);
+ }
+
+ int vtx = face[idx][0].to_int()-1;
+ print_line("vtx: "+itos(vtx)+"/"+itos(vertices.size()));
+ ERR_FAIL_INDEX_V(vtx,vertices.size(),ERR_PARSE_ERROR);
+
+ Vector3 vertex = vertices[vtx];
+ if (weld_vertices)
+ vertex=vertex.snapped(weld_tolerance);
+ surf_tool->add_vertex(vertex);
+ }
+
+ face[1]=face[2];
+ }
+ } else if (l.begins_with("s ") && !force_smooth) { //smoothing
+ String what = l.substr(2,l.length()).strip_edges();
+ if (what=="off")
+ surf_tool->add_smooth_group(false);
+ else
+ surf_tool->add_smooth_group(true);
+
+ } else if (l.begins_with("o ") || f->eof_reached()) { //new surface or done
+
+ if (has_index_data) {
+ //new object/surface
+ if (generate_normals || force_smooth)
+ surf_tool->generate_normals();
+ if (uvs.size() && (normals.size() || generate_normals))
+ surf_tool->generate_tangents();
+
+ surf_tool->index();
+ mesh = surf_tool->commit(mesh);
+ if (name=="")
+ name="Surface "+itos(mesh->get_surface_count()-1);
+ mesh->surface_set_name(mesh->get_surface_count()-1,name);
+ name="";
+ surf_tool->clear();
+ surf_tool->begin(Mesh::PRIMITIVE_TRIANGLES);
+ if (force_smooth)
+ surf_tool->add_smooth_group(true);
+
+ has_index_data=false;
+
+ if (f->eof_reached())
+ break;
+ }
+
+ if (l.begins_with("o ")) //name
+ name=l.substr(2,l.length()).strip_edges();
+ }
+ }
+
+
+ from->set_source_md5(0,FileAccess::get_md5(src_path));
+ from->set_editor(get_name());
+ mesh->set_import_metadata(from);
+
+ //re-apply materials if exist
+ for(int i=0;i<mesh->get_surface_count();i++) {
+
+ String n = mesh->surface_get_name(i);
+ if (name_map.has(n))
+ mesh->surface_set_material(i,name_map[n]);
+ }
+
+ Error err = ResourceSaver::save(p_path,mesh);
+
+ return err;
+}
+
+
+EditorMeshImportPlugin::EditorMeshImportPlugin(EditorNode* p_editor) {
+
+ dialog = memnew( EditorMeshImportDialog(this));
+ p_editor->get_gui_base()->add_child(dialog);
+}
+
diff --git a/tools/editor/io_plugins/editor_mesh_import_plugin.h b/tools/editor/io_plugins/editor_mesh_import_plugin.h
new file mode 100644
index 0000000000..014954685d
--- /dev/null
+++ b/tools/editor/io_plugins/editor_mesh_import_plugin.h
@@ -0,0 +1,29 @@
+#ifndef EDITOR_MESH_IMPORT_PLUGIN_H
+#define EDITOR_MESH_IMPORT_PLUGIN_H
+
+
+#include "tools/editor/editor_import_export.h"
+#include "scene/resources/font.h"
+
+class EditorNode;
+class EditorMeshImportDialog;
+
+class EditorMeshImportPlugin : public EditorImportPlugin {
+
+ OBJ_TYPE(EditorMeshImportPlugin,EditorImportPlugin);
+
+ EditorMeshImportDialog *dialog;
+
+
+public:
+
+ virtual String get_name() const;
+ virtual String get_visible_name() const;
+ virtual void import_dialog(const String& p_from="");
+ virtual Error import(const String& p_path, const Ref<ResourceImportMetadata>& p_from);
+
+
+ EditorMeshImportPlugin(EditorNode* p_editor);
+};
+
+#endif // EDITOR_MESH_IMPORT_PLUGIN_H
diff --git a/tools/editor/io_plugins/editor_sample_import_plugin.cpp b/tools/editor/io_plugins/editor_sample_import_plugin.cpp
index 55cba432e3..cd02156eef 100644
--- a/tools/editor/io_plugins/editor_sample_import_plugin.cpp
+++ b/tools/editor/io_plugins/editor_sample_import_plugin.cpp
@@ -41,6 +41,12 @@ class _EditorSampleImportOptions : public Object {
OBJ_TYPE(_EditorSampleImportOptions,Object);
public:
+ enum CompressMode {
+ COMPRESS_MODE_DISABLED,
+ COMPRESS_MODE_RAM,
+ COMPRESS_MODE_DISK
+ };
+
enum CompressBitrate {
COMPRESS_64,
COMPRESS_96,
@@ -57,7 +63,7 @@ public:
bool edit_normalize;
bool edit_loop;
- bool compress_enable;
+ CompressMode compress_mode;
CompressBitrate compress_bitrate;
@@ -78,8 +84,8 @@ public:
edit_normalize=p_value;
else if (n=="edit/loop")
edit_loop=p_value;
- else if (n=="compress/enable")
- compress_enable=p_value;
+ else if (n=="compress/mode")
+ compress_mode=CompressMode(int(p_value));
else if (n=="compress/bitrate")
compress_bitrate=CompressBitrate(int(p_value));
else
@@ -106,8 +112,8 @@ public:
r_ret=edit_normalize;
else if (n=="edit/loop")
r_ret=edit_loop;
- else if (n=="compress/enable")
- r_ret=compress_enable;
+ else if (n=="compress/mode")
+ r_ret=compress_mode;
else if (n=="compress/bitrate")
r_ret=compress_bitrate;
else
@@ -125,7 +131,7 @@ public:
p_list->push_back(PropertyInfo(Variant::BOOL,"edit/trim"));
p_list->push_back(PropertyInfo(Variant::BOOL,"edit/normalize"));
p_list->push_back(PropertyInfo(Variant::BOOL,"edit/loop"));
- //p_list->push_back(PropertyInfo(Variant::BOOL,"compress/enable"));
+ p_list->push_back(PropertyInfo(Variant::INT,"compress/mode",PROPERTY_HINT_ENUM,"Disabled,RAM (Ima-ADPCM)"));
//p_list->push_back(PropertyInfo(Variant::INT,"compress/bitrate",PROPERTY_HINT_ENUM,"64,96,128,192"));
@@ -150,7 +156,7 @@ public:
edit_normalize=true;
edit_loop=false;
- compress_enable=false;
+ compress_mode=COMPRESS_MODE_DISABLED;
compress_bitrate=COMPRESS_128;
}
@@ -554,7 +560,11 @@ Error EditorSampleImportPlugin::import(const String& p_path, const Ref<ResourceI
loop_end=len;
}
+ int compression = from->get_option("compress/mode");
bool force_mono = from->get_option("force/mono");
+ if (compression==_EditorSampleImportOptions::COMPRESS_MODE_RAM)
+ force_mono=true;
+
if (force_mono && chans==2) {
Vector<float> new_data;
@@ -575,19 +585,31 @@ Error EditorSampleImportPlugin::import(const String& p_path, const Ref<ResourceI
DVector<uint8_t> dst_data;
- dst_data.resize( data.size() * (is16?2:1));
- {
- DVector<uint8_t>::Write w = dst_data.write();
+ Sample::Format dst_format;
- int ds=data.size();
- for(int i=0;i<ds;i++) {
+ if ( compression == _EditorSampleImportOptions::COMPRESS_MODE_RAM) {
- if (is16) {
- int16_t v = CLAMP(data[i]*32767,-32768,32767);
- encode_uint16(v,&w[i*2]);
- } else {
- int8_t v = CLAMP(data[i]*127,-128,127);
- w[i]=v;
+ dst_format=Sample::FORMAT_IMA_ADPCM;
+
+ _compress_ima_adpcm(data,dst_data);
+
+ } else {
+
+ dst_format=is16?Sample::FORMAT_PCM16:Sample::FORMAT_PCM8;
+ dst_data.resize( data.size() * (is16?2:1));
+ {
+ DVector<uint8_t>::Write w = dst_data.write();
+
+ int ds=data.size();
+ for(int i=0;i<ds;i++) {
+
+ if (is16) {
+ int16_t v = CLAMP(data[i]*32767,-32768,32767);
+ encode_uint16(v,&w[i*2]);
+ } else {
+ int8_t v = CLAMP(data[i]*127,-128,127);
+ w[i]=v;
+ }
}
}
}
@@ -603,7 +625,7 @@ Error EditorSampleImportPlugin::import(const String& p_path, const Ref<ResourceI
target = smp;
}
- target->create(is16?Sample::FORMAT_PCM16:Sample::FORMAT_PCM8,chans==2?true:false,len);
+ target->create(dst_format,chans==2?true:false,len);
target->set_data(dst_data);
target->set_mix_rate(rate);
target->set_loop_format(loop);
@@ -621,6 +643,124 @@ Error EditorSampleImportPlugin::import(const String& p_path, const Ref<ResourceI
}
+void EditorSampleImportPlugin::_compress_ima_adpcm(const Vector<float>& p_data,DVector<uint8_t>& dst_data) {
+
+
+ /*p_sample_data->data = (void*)malloc(len);
+ xm_s8 *dataptr=(xm_s8*)p_sample_data->data;*/
+
+ static const int16_t _ima_adpcm_step_table[89] = {
+ 7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
+ 19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
+ 50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
+ 130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
+ 337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
+ 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
+ 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
+ 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
+ 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
+ };
+
+ static const int8_t _ima_adpcm_index_table[16] = {
+ -1, -1, -1, -1, 2, 4, 6, 8,
+ -1, -1, -1, -1, 2, 4, 6, 8
+ };
+
+
+ int datalen = p_data.size();
+ int datamax=datalen;
+ if (datalen&1)
+ datalen++;
+
+ dst_data.resize(datalen/2+4);
+ DVector<uint8_t>::Write w = dst_data.write();
+
+
+ int i,step_idx=0,prev=0;
+ uint8_t *out = w.ptr();
+ //int16_t xm_prev=0;
+ const float *in=p_data.ptr();
+
+
+ /* initial value is zero */
+ *(out++) =0;
+ *(out++) =0;
+ /* Table index initial value */
+ *(out++) =0;
+ /* unused */
+ *(out++) =0;
+
+ for (i=0;i<datalen;i++) {
+ int step,diff,vpdiff,signed_nibble,p,mask;
+ uint8_t nibble;
+ int16_t xm_sample;
+
+ if (i>=datamax)
+ xm_sample=0;
+ else {
+
+
+ xm_sample=CLAMP(in[i]*32767.0,-32768,32767);
+ if (xm_sample==32767 || xm_sample==-32768)
+ printf("clippy!\n",xm_sample);
+ }
+
+ // xm_sample=xm_sample+xm_prev;
+ // xm_prev=xm_sample;
+
+ diff = (int)xm_sample - prev ;
+
+ nibble=0 ;
+ step = _ima_adpcm_step_table[ step_idx ];
+ vpdiff = step >> 3 ;
+ if (diff < 0) {
+ nibble=8;
+ diff=-diff ;
+ }
+ mask = 4 ;
+ while (mask) {
+
+ if (diff >= step) {
+
+ nibble |= mask;
+ diff -= step;
+ vpdiff += step;
+ }
+
+ step >>= 1 ;
+ mask >>= 1 ;
+ };
+
+ if (nibble&8)
+ prev-=vpdiff ;
+ else
+ prev+=vpdiff ;
+
+ if (prev > 32767) {
+ printf("%i,xms %i, prev %i,diff %i, vpdiff %i, clip up %i\n",i,xm_sample,prev,diff,vpdiff,prev);
+ prev=32767;
+ } else if (prev < -32768) {
+ printf("%i,xms %i, prev %i,diff %i, vpdiff %i, clip down %i\n",i,xm_sample,prev,diff,vpdiff,prev);
+ prev = -32768 ;
+ }
+
+ step_idx += _ima_adpcm_index_table[nibble];
+ if (step_idx< 0)
+ step_idx= 0 ;
+ else if (step_idx> 88)
+ step_idx= 88 ;
+
+
+ if (i&1) {
+ *out|=nibble<<4;
+ out++;
+ } else {
+ *out=nibble;
+ }
+ /*dataptr[i]=prev>>8;*/
+ }
+
+}
EditorSampleImportPlugin::EditorSampleImportPlugin(EditorNode* p_editor) {
diff --git a/tools/editor/io_plugins/editor_sample_import_plugin.h b/tools/editor/io_plugins/editor_sample_import_plugin.h
index a5420671e6..176dece0d5 100644
--- a/tools/editor/io_plugins/editor_sample_import_plugin.h
+++ b/tools/editor/io_plugins/editor_sample_import_plugin.h
@@ -40,6 +40,7 @@ class EditorSampleImportPlugin : public EditorImportPlugin {
OBJ_TYPE(EditorSampleImportPlugin,EditorImportPlugin);
EditorSampleImportDialog *dialog;
+ void _compress_ima_adpcm(const Vector<float>& p_data,DVector<uint8_t>& dst_data);
public:
virtual String get_name() const;
diff --git a/tools/editor/plugins/baked_light_editor_plugin.cpp b/tools/editor/plugins/baked_light_editor_plugin.cpp
new file mode 100644
index 0000000000..b1e42e3369
--- /dev/null
+++ b/tools/editor/plugins/baked_light_editor_plugin.cpp
@@ -0,0 +1,996 @@
+#include "baked_light_editor_plugin.h"
+#include "scene/gui/box_container.h"
+#include "scene/3d/mesh_instance.h"
+#include "scene/3d/light.h"
+
+
+class BakedLightBaker {
+public:
+
+ enum {
+
+ ATTENUATION_CURVE_LEN=256
+ };
+
+ struct Octant {
+ bool leaf;
+ union {
+ struct {
+ float light_accum[3];
+ float surface_area;
+ Octant *next_leaf;
+ float offset[3];
+ };
+ Octant* children[8];
+ };
+ };
+
+ struct Triangle {
+
+ Vector3 vertices[3];
+ Vector2 uv[3];
+ };
+
+
+ struct BVH {
+
+ AABB aabb;
+ Vector3 center;
+ Triangle *leaf;
+ BVH*children[2];
+ };
+
+
+ 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;
+ }
+ };
+
+
+ struct DirLight {
+
+
+ Vector3 pos;
+ Vector3 up;
+ Vector3 left;
+ Vector3 dir;
+ Color diffuse;
+ Color specular;
+ float energy;
+ float length;
+ int rays_thrown;
+
+ };
+
+ AABB octree_aabb;
+ Octant *octree;
+ BVH*bvh;
+ Vector<Triangle> triangles;
+ Transform base_inv;
+ Octant *leaf_list;
+ int octree_depth;
+ int cell_count;
+ uint32_t *ray_stack;
+ BVH **bvh_stack;
+ float cell_size;
+ float plot_size; //multiplied by cell size
+ Vector<DirLight> directional_lights;
+ int max_bounces;
+
+
+
+ void _add_mesh(const Ref<Mesh>& p_mesh,const Ref<Material>& p_mat_override,const Transform& p_xform);
+ void _parse_geometry(Node* p_node);
+ BVH* _parse_bvh(BVH** p_children,int p_size,int p_depth,int& max_depth);
+ void _make_bvh();
+ void _make_octree();
+ void _octree_insert(const AABB& p_aabb,Octant *p_octant,Triangle* p_triangle, int p_depth);
+
+ void _free_octree(Octant *p_octant) {
+
+ if (!p_octant->leaf) {
+
+ for(int i=0;i<8;i++) {
+ if (p_octant->children[i])
+ _free_octree(p_octant->children[i]);
+ }
+ }
+
+ memdelete(p_octant);
+ }
+
+ void _free_bvh(BVH* p_bvh) {
+
+ if (!p_bvh->leaf) {
+ if (p_bvh->children[0])
+ _free_bvh(p_bvh->children[0]);
+ if (p_bvh->children[1])
+ _free_bvh(p_bvh->children[1]);
+ }
+
+ memdelete(p_bvh);
+
+ }
+
+ void _fix_lights();
+
+
+ void _plot_light(const Vector3& p_plot_pos,const AABB& p_plot_aabb, Octant *p_octant, const AABB& p_aabb,const Color& p_light);
+ void _throw_ray(const Vector3& p_from, const Vector3& p_to,const Color& p_light,float *p_att_curve,float p_att_curve_len,int p_bounces);
+
+
+ void throw_rays(int p_amount);
+ float get_normalization() const;
+
+
+ void bake(Node *p_base);
+
+
+ void clear() {
+
+ if (octree)
+ _free_octree(octree);
+ if (bvh)
+ _free_bvh(bvh);
+
+ if (ray_stack)
+ memdelete_arr(ray_stack);
+ if (bvh_stack)
+ memdelete_arr(bvh_stack);
+
+ octree=NULL;
+ bvh=NULL;
+ leaf_list=NULL;
+ cell_count=0;
+ ray_stack=NULL;
+ bvh_stack=NULL;
+ }
+
+ BakedLightBaker() {
+ octree_depth=8;
+ octree=NULL;
+ bvh=NULL;
+ leaf_list=NULL;
+ cell_count=0;
+ ray_stack=NULL;
+ bvh_stack=NULL;
+ plot_size=2;
+ max_bounces=3;
+ }
+
+ ~BakedLightBaker() {
+
+ clear();
+ }
+
+};
+
+
+void BakedLightBaker::_add_mesh(const Ref<Mesh>& p_mesh,const Ref<Material>& p_mat_override,const Transform& p_xform) {
+
+
+ for(int i=0;i<p_mesh->get_surface_count();i++) {
+
+ if (p_mesh->surface_get_primitive_type(i)!=Mesh::PRIMITIVE_TRIANGLES)
+ continue;
+ Ref<Material> mat = p_mat_override.is_valid()?p_mat_override:p_mesh->surface_get_material(i);
+
+ int facecount=0;
+
+
+ if (p_mesh->surface_get_format(i)&Mesh::ARRAY_FORMAT_INDEX) {
+
+ facecount=p_mesh->surface_get_array_index_len(i);
+ } else {
+
+ facecount=p_mesh->surface_get_array_len(i);
+ }
+
+ ERR_CONTINUE((facecount==0 || (facecount%3)!=0));
+
+ facecount/=3;
+
+ int tbase=triangles.size();
+ triangles.resize(facecount+tbase);
+
+
+ Array a = p_mesh->surface_get_arrays(i);
+
+ DVector<Vector3> vertices = a[Mesh::ARRAY_VERTEX];
+ DVector<Vector3>::Read vr=vertices.read();
+
+ if (p_mesh->surface_get_format(i)&Mesh::ARRAY_FORMAT_INDEX) {
+
+ DVector<int> indices = a[Mesh::ARRAY_INDEX];
+ DVector<int>::Read ir = indices.read();
+
+ for(int i=0;i<facecount;i++) {
+ Triangle &t=triangles[tbase+i];
+ t.vertices[0]=p_xform.xform(vr[ ir[i*3+0] ]);
+ t.vertices[1]=p_xform.xform(vr[ ir[i*3+1] ]);
+ t.vertices[2]=p_xform.xform(vr[ ir[i*3+2] ]);
+ }
+
+ } else {
+
+ for(int i=0;i<facecount;i++) {
+ Triangle &t=triangles[tbase+i];
+ t.vertices[0]=p_xform.xform(vr[ i*3+0 ]);
+ t.vertices[1]=p_xform.xform(vr[ i*3+1 ]);
+ t.vertices[2]=p_xform.xform(vr[ i*3+2 ]);
+ }
+ }
+ }
+
+}
+
+
+void BakedLightBaker::_parse_geometry(Node* p_node) {
+
+ if (p_node->cast_to<MeshInstance>()) {
+
+ MeshInstance *meshi=p_node->cast_to<MeshInstance>();
+ Ref<Mesh> mesh=meshi->get_mesh();
+ if (mesh.is_valid()) {
+ _add_mesh(mesh,meshi->get_material_override(),base_inv * meshi->get_global_transform());
+ }
+ }
+
+ if (p_node->cast_to<DirectionalLight>()) {
+
+ DirectionalLight *dl=p_node->cast_to<DirectionalLight>();
+
+ DirLight dirl;
+ dirl.diffuse=dl->get_color(DirectionalLight::COLOR_DIFFUSE);
+ dirl.specular=dl->get_color(DirectionalLight::COLOR_SPECULAR);
+ dirl.energy=dl->get_parameter(DirectionalLight::PARAM_ENERGY);
+ dirl.pos=dl->get_global_transform().origin;
+ dirl.up=dl->get_global_transform().basis.get_axis(1).normalized();
+ dirl.left=dl->get_global_transform().basis.get_axis(0).normalized();
+ dirl.dir=-dl->get_global_transform().basis.get_axis(2).normalized();
+ dirl.rays_thrown=0;
+ directional_lights.push_back(dirl);
+
+ }
+
+ for(int i=0;i<p_node->get_child_count();i++) {
+
+ _parse_geometry(p_node->get_child(i));
+ }
+}
+
+
+void BakedLightBaker::_fix_lights() {
+
+
+ for(int i=0;i<directional_lights.size();i++) {
+
+ DirLight &dl=directional_lights[i];
+ float up_max=-1e10;
+ float dir_max=-1e10;
+ float left_max=-1e10;
+ float up_min=1e10;
+ float dir_min=1e10;
+ float left_min=1e10;
+
+ for(int j=0;j<triangles.size();j++) {
+
+ for(int k=0;k<3;k++) {
+
+ Vector3 v = triangles[j].vertices[j];
+
+ float up_d = dl.up.dot(v);
+ float dir_d = dl.dir.dot(v);
+ float left_d = dl.left.dot(v);
+
+ if (up_d>up_max)
+ up_max=up_d;
+ if (up_d<up_min)
+ up_min=up_d;
+
+ if (left_d>left_max)
+ left_max=left_d;
+ if (left_d<left_min)
+ left_min=left_d;
+
+ if (dir_d>dir_max)
+ dir_max=dir_d;
+ if (dir_d<dir_min)
+ dir_min=dir_d;
+
+ }
+ }
+
+ //make a center point, then the upvector and leftvector
+ dl.pos = dl.left*( left_max+left_min )*0.5 + dl.up*( up_max+up_min )*0.5 + dl.dir*(dir_min-(dir_max-dir_min));
+ dl.left*=(left_max-left_min)*0.5;
+ dl.up*=(up_max-up_min)*0.5;
+ dl.length = (dir_max - dir_min)*10; //arbitrary number to keep it in scale
+
+ }
+}
+
+BakedLightBaker::BVH* BakedLightBaker::_parse_bvh(BVH** p_children, int p_size, int p_depth, int &max_depth) {
+
+ if (p_depth>max_depth) {
+ max_depth=p_depth;
+ }
+
+ if (p_size==1) {
+
+ return p_children[0];
+ } else if (p_size==0) {
+
+ return NULL;
+ }
+
+
+ AABB aabb;
+ aabb=p_children[0]->aabb;
+ for(int i=1;i<p_size;i++) {
+
+ aabb.merge_with(p_children[i]->aabb);
+ }
+
+ int li=aabb.get_longest_axis_index();
+
+ switch(li) {
+
+ case Vector3::AXIS_X: {
+ SortArray<BVH*,BVHCmpX> sort_x;
+ sort_x.nth_element(0,p_size,p_size/2,p_children);
+ //sort_x.sort(&p_bb[p_from],p_size);
+ } break;
+ case Vector3::AXIS_Y: {
+ SortArray<BVH*,BVHCmpY> sort_y;
+ sort_y.nth_element(0,p_size,p_size/2,p_children);
+ //sort_y.sort(&p_bb[p_from],p_size);
+ } break;
+ case Vector3::AXIS_Z: {
+ SortArray<BVH*,BVHCmpZ> sort_z;
+ sort_z.nth_element(0,p_size,p_size/2,p_children);
+ //sort_z.sort(&p_bb[p_from],p_size);
+
+ } break;
+ }
+
+
+ BVH* left = _parse_bvh(p_children,p_size/2,p_depth+1,max_depth);
+ BVH* right = _parse_bvh(&p_children[p_size/2],p_size-p_size/2,p_depth+1,max_depth);
+
+ BVH *_new = memnew(BVH);
+ _new->aabb=aabb;
+ _new->center=aabb.pos+aabb.size*0.5;
+ _new->children[0]=left;
+ _new->children[1]=right;
+ _new->leaf=NULL;
+
+ return _new;
+}
+
+void BakedLightBaker::_make_bvh() {
+
+ Vector<BVH*> bases;
+ bases.resize(triangles.size());
+ int max_depth=0;
+ for(int i=0;i<triangles.size();i++) {
+ bases[i]=memnew( BVH );
+ bases[i]->leaf=&triangles[i];
+ bases[i]->aabb.pos=triangles[i].vertices[0];
+ bases[i]->aabb.expand_to(triangles[i].vertices[1]);
+ bases[i]->aabb.expand_to(triangles[i].vertices[2]);
+ bases[i]->center=bases[i]->aabb.pos+bases[i]->aabb.size*0.5;
+ }
+
+ bvh=_parse_bvh(bases.ptr(),bases.size(),1,max_depth);
+ ray_stack = memnew_arr(uint32_t,max_depth);
+ bvh_stack = memnew_arr(BVH*,max_depth);
+}
+
+void BakedLightBaker::_octree_insert(const AABB& p_aabb,Octant *p_octant,Triangle* p_triangle, int p_depth) {
+
+ if (p_octant->leaf) {
+
+ if (p_aabb.has_point(p_triangle->vertices[0]) && p_aabb.has_point(p_triangle->vertices[1]) &&p_aabb.has_point(p_triangle->vertices[2])) {
+ //face is completely enclosed, add area
+ p_octant->surface_area+=Face3(p_triangle->vertices[0],p_triangle->vertices[1],p_triangle->vertices[2]).get_area();
+ } else {
+ //not completely enclosed, will need to be clipped..
+ Vector<Vector3> poly;
+ poly.push_back(p_triangle->vertices[0]);
+ poly.push_back(p_triangle->vertices[1]);
+ poly.push_back(p_triangle->vertices[2]);
+
+ //clip
+ for(int i=0;i<3;i++) {
+
+ //top plane
+ Plane p(0,0,0,0);
+ p.normal[i]=1.0;
+ p.d=p_aabb.pos[i]+p_aabb.size[i];
+ poly=Geometry::clip_polygon(poly,p);
+
+ //bottom plane
+ p.normal[i]=-1.0;
+ p.d=-p_aabb.pos[i];
+ poly=Geometry::clip_polygon(poly,p);
+ }
+ //calculate area
+ for(int i=2;i<poly.size();i++) {
+ p_octant->surface_area+=Face3(poly[0],poly[i-1],poly[i]).get_area();
+ }
+ }
+
+ } else {
+
+
+ for(int i=0;i<8;i++) {
+
+ AABB aabb=p_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;
+
+ AABB fit_aabb=aabb;
+ //fit_aabb=fit_aabb.grow(bvh->aabb.size.x*0.0001);
+
+ if (!Face3(p_triangle->vertices[0],p_triangle->vertices[1],p_triangle->vertices[2]).intersects_aabb(fit_aabb))
+ continue;
+
+ if (!p_octant->children[i]) {
+ p_octant->children[i]=memnew(Octant);
+ if (p_depth==0) {
+ p_octant->children[i]->leaf=true;
+ p_octant->children[i]->light_accum[0]=0;
+ p_octant->children[i]->light_accum[1]=0;
+ p_octant->children[i]->light_accum[2]=0;
+ p_octant->children[i]->offset[0]=aabb.pos.x+aabb.size.x*0.5;
+ p_octant->children[i]->offset[1]=aabb.pos.y+aabb.size.y*0.5;
+ p_octant->children[i]->offset[2]=aabb.pos.z+aabb.size.z*0.5;
+ p_octant->children[i]->surface_area=0;
+ p_octant->children[i]->next_leaf=leaf_list;
+ leaf_list=p_octant->children[i];
+ cell_count++;
+ } else {
+
+ p_octant->children[i]->leaf=false;
+ for(int j=0;j<8;j++) {
+ p_octant->children[i]->children[j]=0;
+ }
+ }
+ }
+
+ _octree_insert(aabb,p_octant->children[i],p_triangle,p_depth-1);
+ }
+ }
+}
+
+
+void BakedLightBaker::_make_octree() {
+
+ AABB base = bvh->aabb;
+ float lal=base.get_longest_axis_size();
+ //must be square because we want square blocks
+ base.size.x=lal;
+ base.size.y=lal;
+ base.size.z=lal;
+ base.grow_by(lal*0.001); //for precision
+ octree_aabb=base;
+
+ cell_size=base.size.x;
+ for(int i=0;i<=octree_depth;i++)
+ cell_size/=2.0;
+
+ octree = memnew( Octant );
+ octree->leaf=false;
+ for(int i=0;i<8;i++)
+ octree->children[i]=NULL;
+
+ for(int i=0;i<triangles.size();i++) {
+
+ _octree_insert(octree_aabb,octree,&triangles[i],octree_depth-1);
+ }
+
+}
+
+
+void BakedLightBaker::_plot_light(const Vector3& p_plot_pos,const AABB& p_plot_aabb, Octant *p_octant, const AABB& p_aabb,const Color& p_light) {
+
+
+ if (p_octant->leaf) {
+
+ float r=cell_size*plot_size;
+ Vector3 center=p_aabb.pos+p_aabb.size*0.5;
+ float d = p_plot_pos.distance_to(center);
+ if (d>r)
+ return; //oh crap! outside radius
+ float intensity = 1.0 - (d/r)*(d/r); //not gauss but..
+ p_octant->light_accum[0]+=p_light.r*intensity;
+ p_octant->light_accum[1]+=p_light.g*intensity;
+ p_octant->light_accum[2]+=p_light.b*intensity;
+
+ } else {
+
+ for(int i=0;i<8;i++) {
+
+ if (!p_octant->children[i])
+ continue;
+
+ AABB aabb=p_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_plot_aabb))
+ continue;
+
+ _plot_light(p_plot_pos,p_plot_aabb,p_octant->children[i],aabb,p_light);
+
+ }
+
+ }
+}
+
+
+void BakedLightBaker::_throw_ray(const Vector3& p_begin, const Vector3& p_end,const Color& p_light,float *p_att_curve,float p_att_curve_len,int p_bounces) {
+
+
+ uint32_t* stack = ray_stack;
+ BVH **bstack = bvh_stack;
+
+ enum {
+ TEST_AABB_BIT=0,
+ VISIT_LEFT_BIT=1,
+ VISIT_RIGHT_BIT=2,
+ VISIT_DONE_BIT=3,
+
+
+ };
+
+ Vector3 n = (p_end-p_begin).normalized();
+ real_t d=1e10;
+ bool inters=false;
+ Vector3 r_normal;
+ Vector3 r_point;
+
+ //for(int i=0;i<max_depth;i++)
+ // stack[i]=0;
+
+ int level=0;
+ //AABB ray_aabb;
+ //ray_aabb.pos=p_begin;
+ //ray_aabb.expand_to(p_end);
+
+
+ const BVH *bvhptr = bvh;
+
+ bstack[0]=bvh;
+ stack[0]=TEST_AABB_BIT;
+
+
+ while(true) {
+
+ uint32_t mode = stack[level];
+ const BVH &b = *bstack[level];
+ bool done=false;
+
+ switch(mode) {
+ case TEST_AABB_BIT: {
+
+ if (b.leaf) {
+
+
+ Face3 f3(b.leaf->vertices[0],b.leaf->vertices[1],b.leaf->vertices[2]);
+
+
+ Vector3 res;
+
+ if (f3.intersects_segment(p_begin,p_end,&res)) {
+
+
+ float nd = n.dot(res);
+ if (nd<d) {
+
+ d=nd;
+ r_point=res;
+ r_normal=f3.get_plane().get_normal();
+ inters=true;
+ }
+
+ }
+
+ stack[level]=VISIT_DONE_BIT;
+ } else {
+
+
+ bool valid = b.aabb.intersects_segment(p_begin,p_end);
+ // bool valid = b.aabb.intersects(ray_aabb);
+
+ if (!valid) {
+
+ stack[level]=VISIT_DONE_BIT;
+
+ } else {
+
+ stack[level]=VISIT_LEFT_BIT;
+ }
+ }
+
+ } continue;
+ case VISIT_LEFT_BIT: {
+
+ stack[level]=VISIT_RIGHT_BIT;
+ bstack[level+1]=b.children[0];
+ stack[level+1]=TEST_AABB_BIT;
+ level++;
+
+ } continue;
+ case VISIT_RIGHT_BIT: {
+
+ stack[level]=VISIT_DONE_BIT;
+ bstack[level+1]=b.children[1];
+ stack[level+1]=TEST_AABB_BIT;
+ level++;
+ } continue;
+ case VISIT_DONE_BIT: {
+
+ if (level==0) {
+ done=true;
+ break;
+ } else
+ level--;
+
+ } continue;
+ }
+
+
+ if (done)
+ break;
+ }
+
+
+ if (inters) {
+
+ //print_line("collision!");
+ if (n.dot(r_normal)>0)
+ r_normal=-r_normal;
+
+ //ok...
+ Color diffuse_at_point(0.8,0.8,0.8);
+ Color specular_at_point(0.8,0.8,0.8);
+
+ AABB aabb;
+ aabb.pos=r_point;
+ aabb.pos-=Vector3(1,1,1)*cell_size*plot_size;
+ aabb.size=Vector3(2,2,2)*cell_size*plot_size;
+
+ _plot_light(r_point,aabb,octree,octree_aabb,p_light);
+
+ }
+
+}
+
+
+
+
+
+
+float BakedLightBaker::get_normalization() const {
+
+ float nrg=0;
+ for(int i=0;i<directional_lights.size();i++) {
+
+ const DirLight &dl=directional_lights[i];
+ float total_area = dl.left.length()*2*dl.up.length()*2;
+ float cell_area = cell_size*cell_size;;
+ nrg+= dl.energy * (dl.rays_thrown * cell_area / total_area);
+ nrg*=5;
+ }
+
+ return nrg;
+}
+
+void BakedLightBaker::throw_rays(int p_amount) {
+
+
+
+ for(int i=0;i<directional_lights.size();i++) {
+
+ DirLight &dl=directional_lights[i];
+
+ float sr = Math::sqrt(p_amount);
+ float aspect = dl.up.length()/dl.left.length();
+
+
+ for(int j=0;j<p_amount;j++) {
+ Vector3 from = dl.pos;
+ from+=dl.up*(Math::randf()*2.0-1.0);
+ from+=dl.left*(Math::randf()*2.0-1.0);
+ Vector3 to = from+dl.dir*dl.length;
+ Color col=dl.diffuse;
+ col.r*=dl.energy;
+ col.g*=dl.energy;
+ col.b*=dl.energy;
+ dl.rays_thrown++;
+ _throw_ray(from,to,col,NULL,0,max_bounces);
+ }
+
+
+ }
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+void BakedLightBaker::bake(Node* p_node) {
+
+ cell_count=0;
+
+ _parse_geometry(p_node);
+ _fix_lights();
+ _make_bvh();
+ _make_octree();
+
+}
+
+
+
+
+void BakedLightEditor::_node_removed(Node *p_node) {
+
+ if(p_node==node) {
+ node=NULL;
+ p_node->remove_child(preview);
+ preview->set_mesh(Ref<Mesh>());
+ hide();
+ }
+
+}
+
+
+
+void BakedLightEditor::_menu_option(int p_option) {
+
+
+ switch(p_option) {
+
+
+ case MENU_OPTION_BAKE: {
+
+ ERR_FAIL_COND(!node);
+ preview->set_mesh(Ref<Mesh>());
+ baker->base_inv=node->get_global_transform().affine_inverse();
+ baker->bake(node);
+ baker->throw_rays(100000);
+ float norm = baker->get_normalization();
+ float max_lum=0;
+
+ print_line("CELLS: "+itos(baker->cell_count));
+ DVector<Color> colors;
+ DVector<Vector3> vertices;
+ colors.resize(baker->cell_count*36);
+ vertices.resize(baker->cell_count*36);
+
+
+ {
+ DVector<Color>::Write cw=colors.write();
+ DVector<Vector3>::Write vw=vertices.write();
+ BakedLightBaker::Octant *oct = baker->leaf_list;
+ int vert_idx=0;
+
+ while(oct) {
+
+ Color color;
+ color.r=oct->light_accum[0]/norm;
+ color.g=oct->light_accum[1]/norm;
+ color.b=oct->light_accum[2]/norm;
+ float lum = color.get_v();
+ if (lum>max_lum)
+ max_lum=lum;
+
+ 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[j]*=baker->cell_size;
+ face_points[j]+=Vector3(oct->offset[0],oct->offset[1],oct->offset[2]);
+ }
+
+#define ADD_VTX(m_idx) \
+ vw[vert_idx]=face_points[m_idx]; \
+ cw[vert_idx]=color; \
+ vert_idx++;
+
+ //tri 1
+ ADD_VTX(0);
+ ADD_VTX(1);
+ ADD_VTX(2);
+ //tri 2
+ ADD_VTX(2);
+ ADD_VTX(3);
+ ADD_VTX(0);
+
+#undef ADD_VTX
+
+ }
+
+ oct=oct->next_leaf;
+ }
+
+
+ }
+
+ print_line("max lum: "+rtos(max_lum));
+ Array a;
+ a.resize(Mesh::ARRAY_MAX);
+ a[Mesh::ARRAY_VERTEX]=vertices;
+ a[Mesh::ARRAY_COLOR]=colors;
+
+ Ref<FixedMaterial> matcol = memnew( FixedMaterial );
+ matcol->set_fixed_flag(FixedMaterial::FLAG_USE_COLOR_ARRAY,true);
+ matcol->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA,true);
+ matcol->set_flag(FixedMaterial::FLAG_UNSHADED,true);
+ matcol->set_flag(FixedMaterial::FLAG_DOUBLE_SIDED,true);
+ matcol->set_parameter(FixedMaterial::PARAM_DIFFUSE,Color(1,1,1));
+ Ref<Mesh> m = memnew( Mesh );
+ m->add_surface(Mesh::PRIMITIVE_TRIANGLES,a);
+ m->surface_set_material(0,matcol);
+ preview->set_mesh(m);
+
+
+
+
+
+ } break;
+ case MENU_OPTION_CLEAR: {
+
+
+
+ } break;
+
+ }
+}
+
+
+void BakedLightEditor::edit(BakedLight *p_baked_light) {
+
+ if (node==p_baked_light)
+ return;
+ if (node) {
+ node->remove_child(preview);
+ }
+
+ node=p_baked_light;
+
+ if (node)
+ node->add_child(preview);
+
+}
+
+
+
+void BakedLightEditor::_bind_methods() {
+
+ ObjectTypeDB::bind_method("_menu_option",&BakedLightEditor::_menu_option);
+}
+
+BakedLightEditor::BakedLightEditor() {
+
+
+ options = memnew( MenuButton );
+
+ options->set_text("BakedLight");
+ options->get_popup()->add_item("Bake..",MENU_OPTION_BAKE);
+ options->get_popup()->add_item("Clear",MENU_OPTION_CLEAR);
+ options->get_popup()->connect("item_pressed", this,"_menu_option");
+
+
+ err_dialog = memnew( AcceptDialog );
+ add_child(err_dialog);
+ node=NULL;
+ baker = memnew( BakedLightBaker );
+ preview = memnew( MeshInstance );
+}
+
+BakedLightEditor::~BakedLightEditor() {
+
+ memdelete(baker);
+}
+
+void BakedLightEditorPlugin::edit(Object *p_object) {
+
+ baked_light_editor->edit(p_object->cast_to<BakedLight>());
+}
+
+bool BakedLightEditorPlugin::handles(Object *p_object) const {
+
+ return p_object->is_type("BakedLight");
+}
+
+void BakedLightEditorPlugin::make_visible(bool p_visible) {
+
+ if (p_visible) {
+ baked_light_editor->show();
+ baked_light_editor->options->show();
+ } else {
+
+ baked_light_editor->hide();
+ baked_light_editor->options->show();
+ baked_light_editor->edit(NULL);
+ if (baked_light_editor->node) {
+ baked_light_editor->node->remove_child(baked_light_editor->preview);
+ baked_light_editor->node=NULL;
+ }
+ }
+
+}
+
+BakedLightEditorPlugin::BakedLightEditorPlugin(EditorNode *p_node) {
+
+ editor=p_node;
+ baked_light_editor = memnew( BakedLightEditor );
+ editor->get_viewport()->add_child(baked_light_editor);
+ add_custom_control(CONTAINER_SPATIAL_EDITOR_MENU,baked_light_editor->options);
+ baked_light_editor->hide();
+ baked_light_editor->options->hide();
+}
+
+
+BakedLightEditorPlugin::~BakedLightEditorPlugin()
+{
+}
+
+
diff --git a/tools/editor/plugins/baked_light_editor_plugin.h b/tools/editor/plugins/baked_light_editor_plugin.h
new file mode 100644
index 0000000000..698d3f825f
--- /dev/null
+++ b/tools/editor/plugins/baked_light_editor_plugin.h
@@ -0,0 +1,70 @@
+#ifndef BAKED_LIGHT_EDITOR_PLUGIN_H
+#define BAKED_LIGHT_EDITOR_PLUGIN_H
+
+#include "tools/editor/editor_plugin.h"
+#include "tools/editor/editor_node.h"
+#include "scene/3d/baked_light.h"
+#include "scene/gui/spin_box.h"
+
+/**
+ @author Juan Linietsky <reduzio@gmail.com>
+*/
+
+
+class BakedLightBaker;
+class MeshInstance;
+
+class BakedLightEditor : public Control {
+
+ OBJ_TYPE(BakedLightEditor, Control );
+
+
+ MeshInstance *preview;
+ BakedLightBaker *baker;
+ AcceptDialog *err_dialog;
+
+ MenuButton * options;
+ BakedLight *node;
+
+ enum Menu {
+
+ MENU_OPTION_BAKE,
+ MENU_OPTION_CLEAR
+ };
+
+ void _menu_option(int);
+
+friend class BakedLightEditorPlugin;
+protected:
+ void _node_removed(Node *p_node);
+ static void _bind_methods();
+public:
+
+ void edit(BakedLight *p_baked_light);
+ BakedLightEditor();
+ ~BakedLightEditor();
+};
+
+class BakedLightEditorPlugin : public EditorPlugin {
+
+ OBJ_TYPE( BakedLightEditorPlugin, EditorPlugin );
+
+ BakedLightEditor *baked_light_editor;
+ EditorNode *editor;
+
+public:
+
+ virtual String get_name() const { return "BakedLight"; }
+ bool has_main_screen() const { return false; }
+ virtual void edit(Object *p_node);
+ virtual bool handles(Object *p_node) const;
+ virtual void make_visible(bool p_visible);
+
+ BakedLightEditorPlugin(EditorNode *p_node);
+ ~BakedLightEditorPlugin();
+
+};
+
+#endif // MULTIMESH_EDITOR_PLUGIN_H
+
+
diff --git a/tools/editor/plugins/sample_editor_plugin.cpp b/tools/editor/plugins/sample_editor_plugin.cpp
index f55d6640c2..e3fad58a89 100644
--- a/tools/editor/plugins/sample_editor_plugin.cpp
+++ b/tools/editor/plugins/sample_editor_plugin.cpp
@@ -95,93 +95,98 @@ void SampleEditor::generate_preview_texture(const Ref<Sample>& p_sample,Ref<Imag
if (len<1)
return;
- for(int i=0;i<w;i++) {
- // i trust gcc will optimize this loop
- float max[2]={-1e10,-1e10};
- float min[2]={1e10,1e10};
- int c=stereo?2:1;
- int from = i*len/w;
- int to = (i+1)*len/w;
- if (to>=len)
- to=len-1;
-
- if (_16) {
- const int16_t*src =(const int16_t*)sdata;
-
- for(int j=0;j<c;j++) {
-
- for(int k=from;k<=to;k++) {
-
- float v = src[k*c+j]/32768.0;
- if (v>max[j])
- max[j]=v;
- if (v<min[j])
- min[j]=v;
+ if (p_sample->get_format()==Sample::FORMAT_IMA_ADPCM) {
+
+
+ } else {
+ for(int i=0;i<w;i++) {
+ // i trust gcc will optimize this loop
+ float max[2]={-1e10,-1e10};
+ float min[2]={1e10,1e10};
+ int c=stereo?2:1;
+ int from = i*len/w;
+ int to = (i+1)*len/w;
+ if (to>=len)
+ to=len-1;
+
+ if (_16) {
+ const int16_t*src =(const int16_t*)sdata;
+
+ for(int j=0;j<c;j++) {
+
+ for(int k=from;k<=to;k++) {
+
+ float v = src[k*c+j]/32768.0;
+ if (v>max[j])
+ max[j]=v;
+ if (v<min[j])
+ min[j]=v;
+ }
+
}
+ } else {
- }
- } else {
+ const int8_t*src =(const int8_t*)sdata;
- const int8_t*src =(const int8_t*)sdata;
+ for(int j=0;j<c;j++) {
- for(int j=0;j<c;j++) {
+ for(int k=from;k<=to;k++) {
- for(int k=from;k<=to;k++) {
+ float v = src[k*c+j]/128.0;
+ if (v>max[j])
+ max[j]=v;
+ if (v<min[j])
+ min[j]=v;
+ }
- float v = src[k*c+j]/128.0;
- if (v>max[j])
- max[j]=v;
- if (v<min[j])
- min[j]=v;
}
-
}
- }
- if (!stereo) {
- for(int j=0;j<h;j++) {
- float v = (j/(float)h) * 2.0 - 1.0;
- uint8_t* imgofs = &imgw[(j*w+i)*3];
- if (v>min[0] && v<max[0]) {
- imgofs[0]=255;
- imgofs[1]=150;
- imgofs[2]=80;
- } else {
- imgofs[0]=0;
- imgofs[1]=0;
- imgofs[2]=0;
+ if (!stereo) {
+ for(int j=0;j<h;j++) {
+ float v = (j/(float)h) * 2.0 - 1.0;
+ uint8_t* imgofs = &imgw[(j*w+i)*3];
+ if (v>min[0] && v<max[0]) {
+ imgofs[0]=255;
+ imgofs[1]=150;
+ imgofs[2]=80;
+ } else {
+ imgofs[0]=0;
+ imgofs[1]=0;
+ imgofs[2]=0;
+ }
}
- }
- } else {
-
- for(int j=0;j<h;j++) {
-
- int half,ofs;
- float v;
- if (j<(h/2)) {
- half=0;
- ofs=0;
- v = (j/(float)(h/2)) * 2.0 - 1.0;
- } else {
- half=1;
- ofs=h/2;
- v = ((j-(h/2))/(float)(h/2)) * 2.0 - 1.0;
+ } else {
+
+ for(int j=0;j<h;j++) {
+
+ int half,ofs;
+ float v;
+ if (j<(h/2)) {
+ half=0;
+ ofs=0;
+ v = (j/(float)(h/2)) * 2.0 - 1.0;
+ } else {
+ half=1;
+ ofs=h/2;
+ v = ((j-(h/2))/(float)(h/2)) * 2.0 - 1.0;
+ }
+
+ uint8_t* imgofs = &imgw[(j*w+i)*3];
+ if (v>min[half] && v<max[half]) {
+ imgofs[0]=255;
+ imgofs[1]=150;
+ imgofs[2]=80;
+ } else {
+ imgofs[0]=0;
+ imgofs[1]=0;
+ imgofs[2]=0;
+ }
}
- uint8_t* imgofs = &imgw[(j*w+i)*3];
- if (v>min[half] && v<max[half]) {
- imgofs[0]=255;
- imgofs[1]=150;
- imgofs[2]=80;
- } else {
- imgofs[0]=0;
- imgofs[1]=0;
- imgofs[2]=0;
- }
}
}
-
}
imgdata = DVector<uint8_t>::Write();
diff --git a/tools/editor/plugins/sample_library_editor_plugin.cpp b/tools/editor/plugins/sample_library_editor_plugin.cpp
index 86ac1671ae..84143dcd4b 100644
--- a/tools/editor/plugins/sample_library_editor_plugin.cpp
+++ b/tools/editor/plugins/sample_library_editor_plugin.cpp
@@ -262,7 +262,7 @@ void SampleLibraryEditor::_update_library() {
ti->set_editable(2,false);
ti->set_selectable(2,false);
Ref<Sample> s = sample_library->get_sample(E->get());
- ti->set_text(2,String()+/*itos(s->get_length())+" frames ("+String::num(s->get_length()/(float)s->get_mix_rate(),2)+" s), "+*/(s->get_format()==Sample::FORMAT_PCM16?"16 Bits, ":"8 bits, ")+(s->is_stereo()?"Stereo":"Mono"));
+ ti->set_text(2,String()+/*itos(s->get_length())+" frames ("+String::num(s->get_length()/(float)s->get_mix_rate(),2)+" s), "+*/(s->get_format()==Sample::FORMAT_PCM16?"16 Bits, ":(s->get_format()==Sample::FORMAT_PCM8?"8 bits, ":"IMA-ADPCM,"))+(s->is_stereo()?"Stereo":"Mono"));
ti->set_cell_mode(3,TreeItem::CELL_MODE_RANGE);
ti->set_range_config(3,-60,24,0.01);
diff --git a/tools/editor/property_editor.cpp b/tools/editor/property_editor.cpp
index 8c16207887..2087345888 100644
--- a/tools/editor/property_editor.cpp
+++ b/tools/editor/property_editor.cpp
@@ -261,12 +261,12 @@ bool CustomPropertyEditor::edit(Object* p_owner,const String& p_name,Variant::Ty
for(int i=0;i<2;i++) {
Point2 ofs(4,4);
- ofs.y+=16*i;
+ ofs.y+=22*i;
for(int j=0;j<10;j++) {
- CheckButton *c=checks20[i*10+j];
+ Button *c=checks20[i*10+j];
Point2 o=ofs;
- o.x+=j*16;
+ o.x+=j*22;
if (j>=5)
o.x+=4;
c->set_pos(o);
@@ -277,7 +277,7 @@ bool CustomPropertyEditor::edit(Object* p_owner,const String& p_name,Variant::Ty
}
- set_size(checks20[19]->get_pos()+Size2(12,17));
+ set_size(checks20[19]->get_pos()+Size2(20,25));
} else if (hint==PROPERTY_HINT_EXP_EASING) {
@@ -1560,7 +1560,9 @@ CustomPropertyEditor::CustomPropertyEditor() {
}
for(int i=0;i<20;i++) {
- checks20[i]=memnew( CheckButton );
+ checks20[i]=memnew( Button );
+ checks20[i]->set_toggle_mode(true);
+ checks20[i]->set_focus_mode(FOCUS_NONE);
add_child(checks20[i]);
checks20[i]->hide();
checks20[i]->connect("pressed",this,"_action_pressed",make_binds(i));
diff --git a/tools/editor/property_editor.h b/tools/editor/property_editor.h
index 7ee14679c1..f13deab1ae 100644
--- a/tools/editor/property_editor.h
+++ b/tools/editor/property_editor.h
@@ -92,7 +92,7 @@ class CustomPropertyEditor : public Popup {
ColorPicker *color_picker;
TextEdit *text_edit;
bool read_only;
- CheckButton *checks20[20];
+ Button *checks20[20];
Control *easing_draw;