diff options
Diffstat (limited to 'scene/resources')
-rw-r--r-- | scene/resources/SCsub | 1 | ||||
-rw-r--r-- | scene/resources/animation.cpp | 10 | ||||
-rw-r--r-- | scene/resources/animation.h | 4 | ||||
-rw-r--r-- | scene/resources/baked_light.cpp | 94 | ||||
-rw-r--r-- | scene/resources/baked_light.h | 25 | ||||
-rw-r--r-- | scene/resources/image_path_finder.cpp | 427 | ||||
-rw-r--r-- | scene/resources/image_path_finder.h | 84 | ||||
-rw-r--r-- | scene/resources/material.cpp | 28 | ||||
-rw-r--r-- | scene/resources/material.h | 7 | ||||
-rw-r--r-- | scene/resources/mesh.cpp | 27 | ||||
-rw-r--r-- | scene/resources/mesh.h | 1 | ||||
-rw-r--r-- | scene/resources/mikktspace.c | 1890 | ||||
-rw-r--r-- | scene/resources/mikktspace.h | 145 | ||||
-rw-r--r-- | scene/resources/packed_scene.cpp | 46 | ||||
-rw-r--r-- | scene/resources/scene_preloader.cpp | 853 | ||||
-rw-r--r-- | scene/resources/shader_graph.cpp | 4 | ||||
-rw-r--r-- | scene/resources/shape_2d.cpp | 2 | ||||
-rw-r--r-- | scene/resources/surface_tool.cpp | 206 | ||||
-rw-r--r-- | scene/resources/surface_tool.h | 10 | ||||
-rw-r--r-- | scene/resources/texture.cpp | 4 | ||||
-rw-r--r-- | scene/resources/texture.h | 2 |
21 files changed, 2771 insertions, 1099 deletions
diff --git a/scene/resources/SCsub b/scene/resources/SCsub index 87bd33e00e..eaa282ae1a 100644 --- a/scene/resources/SCsub +++ b/scene/resources/SCsub @@ -1,6 +1,7 @@ Import('env') env.add_source_files(env.scene_sources,"*.cpp") +env.add_source_files(env.scene_sources,"*.c") Export('env') diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp index 67f45ced2b..e6359f920b 100644 --- a/scene/resources/animation.cpp +++ b/scene/resources/animation.cpp @@ -1716,7 +1716,7 @@ void Animation::clear() { } -void Animation::_transform_track_optimize(int p_idx,float p_alowed_linear_err,float p_alowed_angular_err) { +void Animation::_transform_track_optimize(int p_idx,float p_alowed_linear_err,float p_alowed_angular_err,float p_max_optimizable_angle) { ERR_FAIL_INDEX(p_idx,tracks.size()); ERR_FAIL_COND(tracks[p_idx]->type!=TYPE_TRANSFORM); @@ -1779,6 +1779,7 @@ void Animation::_transform_track_optimize(int p_idx,float p_alowed_linear_err,fl } else { + Quat r02 = (q0.inverse() * q2).normalized(); Quat r01 = (q0.inverse() * q1).normalized(); @@ -1788,6 +1789,9 @@ void Animation::_transform_track_optimize(int p_idx,float p_alowed_linear_err,fl r02.get_axis_and_angle(v02,a02); r01.get_axis_and_angle(v01,a01); + if (Math::abs(a02)>p_max_optimizable_angle) + continue; + if (v01.dot(v02)<0) { //make sure both rotations go the same way to compare v02=-v02; @@ -1905,7 +1909,7 @@ void Animation::_transform_track_optimize(int p_idx,float p_alowed_linear_err,fl } -void Animation::optimize(float p_allowed_linear_err,float p_allowed_angular_err) { +void Animation::optimize(float p_allowed_linear_err,float p_allowed_angular_err,float p_angle_max) { int total_tt=0; @@ -1913,7 +1917,7 @@ void Animation::optimize(float p_allowed_linear_err,float p_allowed_angular_err) for(int i=0;i<tracks.size();i++) { if (tracks[i]->type==TYPE_TRANSFORM) - _transform_track_optimize(i,p_allowed_linear_err,p_allowed_angular_err); + _transform_track_optimize(i,p_allowed_linear_err,p_allowed_angular_err,p_angle_max); } diff --git a/scene/resources/animation.h b/scene/resources/animation.h index 4c4e2f0275..0c0290295a 100644 --- a/scene/resources/animation.h +++ b/scene/resources/animation.h @@ -204,7 +204,7 @@ private: return idxr; } - void _transform_track_optimize(int p_idx, float p_allowed_err=0.05, float p_alowed_angular_err=0.01); + void _transform_track_optimize(int p_idx, float p_allowed_err=0.05, float p_alowed_angular_err=0.01,float p_max_optimizable_angle=Math_PI*0.125); protected: @@ -271,7 +271,7 @@ public: void clear(); - void optimize(float p_allowed_linear_err=0.05,float p_allowed_angular_err=0.01); + void optimize(float p_allowed_linear_err=0.05,float p_allowed_angular_err=0.01,float p_max_optimizable_angle=Math_PI*0.125); Animation(); ~Animation(); diff --git a/scene/resources/baked_light.cpp b/scene/resources/baked_light.cpp index 647c8df5d4..226edec9ae 100644 --- a/scene/resources/baked_light.cpp +++ b/scene/resources/baked_light.cpp @@ -23,6 +23,27 @@ DVector<uint8_t> BakedLight::get_octree() const { return VS::get_singleton()->baked_light_get_octree(baked_light); } +void BakedLight::set_light(const DVector<uint8_t>& p_light) { + + VS::get_singleton()->baked_light_set_light(baked_light,p_light); +} + +DVector<uint8_t> BakedLight::get_light() const { + + return VS::get_singleton()->baked_light_get_light(baked_light); +} + + +void BakedLight::set_sampler_octree(const DVector<int>& p_sampler_octree) { + + VS::get_singleton()->baked_light_set_sampler_octree(baked_light,p_sampler_octree); +} + +DVector<int> BakedLight::get_sampler_octree() const { + + return VS::get_singleton()->baked_light_get_sampler_octree(baked_light); +} + @@ -199,6 +220,43 @@ float BakedLight::get_normal_damp() const { return normal_damp; } +void BakedLight::set_tint(float p_margin) { + tint=p_margin; +} + +float BakedLight::get_tint() const { + + return tint; +} + +void BakedLight::set_saturation(float p_margin) { + saturation=p_margin; +} + +float BakedLight::get_saturation() const { + + return saturation; +} + +void BakedLight::set_ao_radius(float p_ao_radius) { + ao_radius=p_ao_radius; +} + +float BakedLight::get_ao_radius() const { + return ao_radius; +} + +void BakedLight::set_ao_strength(float p_ao_strength) { + + ao_strength=p_ao_strength; +} + +float BakedLight::get_ao_strength() const { + + return ao_strength; +} + + void BakedLight::set_energy_multiplier(float p_multiplier){ energy_multiply=p_multiplier; @@ -329,6 +387,13 @@ void BakedLight::_bind_methods(){ ObjectTypeDB::bind_method(_MD("set_octree","octree"),&BakedLight::set_octree); ObjectTypeDB::bind_method(_MD("get_octree"),&BakedLight::get_octree); + ObjectTypeDB::bind_method(_MD("set_light","light"),&BakedLight::set_light); + ObjectTypeDB::bind_method(_MD("get_light"),&BakedLight::get_light); + + ObjectTypeDB::bind_method(_MD("set_sampler_octree","sampler_octree"),&BakedLight::set_sampler_octree); + ObjectTypeDB::bind_method(_MD("get_sampler_octree"),&BakedLight::get_sampler_octree); + + ObjectTypeDB::bind_method(_MD("add_lightmap","texture:Texture","gen_size"),&BakedLight::add_lightmap); ObjectTypeDB::bind_method(_MD("erase_lightmap","id"),&BakedLight::erase_lightmap); ObjectTypeDB::bind_method(_MD("clear_lightmaps"),&BakedLight::clear_lightmaps); @@ -357,6 +422,18 @@ void BakedLight::_bind_methods(){ ObjectTypeDB::bind_method(_MD("set_normal_damp","normal_damp"),&BakedLight::set_normal_damp); ObjectTypeDB::bind_method(_MD("get_normal_damp"),&BakedLight::get_normal_damp); + ObjectTypeDB::bind_method(_MD("set_tint","tint"),&BakedLight::set_tint); + ObjectTypeDB::bind_method(_MD("get_tint"),&BakedLight::get_tint); + + ObjectTypeDB::bind_method(_MD("set_saturation","saturation"),&BakedLight::set_saturation); + ObjectTypeDB::bind_method(_MD("get_saturation"),&BakedLight::get_saturation); + + ObjectTypeDB::bind_method(_MD("set_ao_radius","ao_radius"),&BakedLight::set_ao_radius); + ObjectTypeDB::bind_method(_MD("get_ao_radius"),&BakedLight::get_ao_radius); + + ObjectTypeDB::bind_method(_MD("set_ao_strength","ao_strength"),&BakedLight::set_ao_strength); + ObjectTypeDB::bind_method(_MD("get_ao_strength"),&BakedLight::get_ao_strength); + ObjectTypeDB::bind_method(_MD("set_format","format"),&BakedLight::set_format); ObjectTypeDB::bind_method(_MD("get_format"),&BakedLight::get_format); @@ -384,17 +461,24 @@ void BakedLight::_bind_methods(){ ADD_PROPERTY( PropertyInfo(Variant::REAL,"baking/plot_size",PROPERTY_HINT_RANGE,"1.0,16.0,0.01"),_SCS("set_plot_size"),_SCS("get_plot_size")); ADD_PROPERTY( PropertyInfo(Variant::REAL,"baking/energy_mult",PROPERTY_HINT_RANGE,"0.01,4096.0,0.01"),_SCS("set_energy_multiplier"),_SCS("get_energy_multiplier")); ADD_PROPERTY( PropertyInfo(Variant::REAL,"baking/gamma_adjust",PROPERTY_HINT_EXP_EASING),_SCS("set_gamma_adjust"),_SCS("get_gamma_adjust")); + ADD_PROPERTY( PropertyInfo(Variant::REAL,"baking/saturation",PROPERTY_HINT_RANGE,"0,8,0.01"),_SCS("set_saturation"),_SCS("get_saturation")); ADD_PROPERTYI( PropertyInfo(Variant::BOOL,"baking_flags/diffuse"),_SCS("set_bake_flag"),_SCS("get_bake_flag"),BAKE_DIFFUSE); ADD_PROPERTYI( PropertyInfo(Variant::BOOL,"baking_flags/specular"),_SCS("set_bake_flag"),_SCS("get_bake_flag"),BAKE_SPECULAR); ADD_PROPERTYI( PropertyInfo(Variant::BOOL,"baking_flags/translucent"),_SCS("set_bake_flag"),_SCS("get_bake_flag"),BAKE_TRANSLUCENT); ADD_PROPERTYI( PropertyInfo(Variant::BOOL,"baking_flags/conserve_energy"),_SCS("set_bake_flag"),_SCS("get_bake_flag"),BAKE_CONSERVE_ENERGY); + ADD_PROPERTYI( PropertyInfo(Variant::BOOL,"baking_flags/linear_color"),_SCS("set_bake_flag"),_SCS("get_bake_flag"),BAKE_LINEAR_COLOR); ADD_PROPERTY( PropertyInfo(Variant::BOOL,"lightmap/use_only_uv2"),_SCS("set_transfer_lightmaps_only_to_uv2"),_SCS("get_transfer_lightmaps_only_to_uv2")); ADD_PROPERTY( PropertyInfo(Variant::RAW_ARRAY,"octree",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_NOEDITOR),_SCS("set_octree"),_SCS("get_octree")); + ADD_PROPERTY( PropertyInfo(Variant::RAW_ARRAY,"light",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_NOEDITOR),_SCS("set_light"),_SCS("get_light")); + ADD_PROPERTY( PropertyInfo(Variant::INT_ARRAY,"sampler_octree",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_NOEDITOR),_SCS("set_sampler_octree"),_SCS("get_sampler_octree")); ADD_PROPERTY( PropertyInfo(Variant::ARRAY,"lightmaps",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_NOEDITOR),_SCS("_set_lightmap_data"),_SCS("_get_lightmap_data")); ADD_PROPERTY( PropertyInfo(Variant::REAL,"advanced/cell_margin",PROPERTY_HINT_RANGE,"0.01,0.8,0.01"),_SCS("set_cell_extra_margin"),_SCS("get_cell_extra_margin")); ADD_PROPERTY( PropertyInfo(Variant::REAL,"advanced/edge_damp",PROPERTY_HINT_RANGE,"0.0,8.0,0.1"),_SCS("set_edge_damp"),_SCS("get_edge_damp")); ADD_PROPERTY( PropertyInfo(Variant::REAL,"advanced/normal_damp",PROPERTY_HINT_RANGE,"0.0,1.0,0.01"),_SCS("set_normal_damp"),_SCS("get_normal_damp")); + ADD_PROPERTY( PropertyInfo(Variant::REAL,"advanced/light_tint",PROPERTY_HINT_RANGE,"0.0,1.0,0.01"),_SCS("set_tint"),_SCS("get_tint")); + ADD_PROPERTY( PropertyInfo(Variant::REAL,"advanced/ao_radius",PROPERTY_HINT_RANGE,"0.0,16.0,0.01"),_SCS("set_ao_radius"),_SCS("get_ao_radius")); + ADD_PROPERTY( PropertyInfo(Variant::REAL,"advanced/ao_strength",PROPERTY_HINT_RANGE,"0.0,1.0,0.01"),_SCS("set_ao_strength"),_SCS("get_ao_strength")); BIND_CONSTANT( MODE_OCTREE ); BIND_CONSTANT( MODE_LIGHTMAPS ); @@ -415,18 +499,24 @@ BakedLight::BakedLight() { lattice_subdiv=4; plot_size=2.5; bounces=1; - energy_multiply=1.0; - gamma_adjust=1.0; + energy_multiply=2.0; + gamma_adjust=0.7; cell_extra_margin=0.05; edge_damp=0.0; normal_damp=0.0; + saturation=1; + tint=0.0; + ao_radius=2.5; + ao_strength=0.7; format=FORMAT_RGB; transfer_only_uv2=false; + flags[BAKE_DIFFUSE]=true; flags[BAKE_SPECULAR]=false; flags[BAKE_TRANSLUCENT]=true; flags[BAKE_CONSERVE_ENERGY]=false; + flags[BAKE_LINEAR_COLOR]=false; mode=MODE_OCTREE; baked_light=VS::get_singleton()->baked_light_create(); diff --git a/scene/resources/baked_light.h b/scene/resources/baked_light.h index 57ed7d7aee..41e1e5f9e0 100644 --- a/scene/resources/baked_light.h +++ b/scene/resources/baked_light.h @@ -26,6 +26,7 @@ public: BAKE_SPECULAR, BAKE_TRANSLUCENT, BAKE_CONSERVE_ENERGY, + BAKE_LINEAR_COLOR, BAKE_MAX }; @@ -50,6 +51,10 @@ private: float cell_extra_margin; float edge_damp; float normal_damp; + float tint; + float ao_radius; + float ao_strength; + float saturation; int bounces; bool transfer_only_uv2; Format format; @@ -99,6 +104,18 @@ public: void set_normal_damp(float p_margin); float get_normal_damp() const; + void set_tint(float p_margin); + float get_tint() const; + + void set_saturation(float p_saturation); + float get_saturation() const; + + void set_ao_radius(float p_ao_radius); + float get_ao_radius() const; + + void set_ao_strength(float p_ao_strength); + float get_ao_strength() const; + void set_bake_flag(BakeFlags p_flags,bool p_enable); bool get_bake_flag(BakeFlags p_flags) const; @@ -114,6 +131,14 @@ public: void set_octree(const DVector<uint8_t>& p_octree); DVector<uint8_t> get_octree() const; + void set_light(const DVector<uint8_t>& p_light); + DVector<uint8_t> get_light() const; + + void set_sampler_octree(const DVector<int>& p_sampler_octree); + DVector<int> get_sampler_octree() const; + + + void add_lightmap(const Ref<Texture> &p_texture,Size2 p_gen_size=Size2(256,256)); void set_lightmap_gen_size(int p_idx,const Size2& p_size); Size2 get_lightmap_gen_size(int p_idx) const; diff --git a/scene/resources/image_path_finder.cpp b/scene/resources/image_path_finder.cpp deleted file mode 100644 index 1a7758789c..0000000000 --- a/scene/resources/image_path_finder.cpp +++ /dev/null @@ -1,427 +0,0 @@ -/*************************************************************************/ -/* image_path_finder.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_path_finder.h"
-
-
-void ImagePathFinder::_unlock() {
-
- lock=DVector<Cell>::Write();
- cells=NULL;
-
-}
-
-void ImagePathFinder::_lock() {
-
- lock = cell_data.write();
- cells=lock.ptr();
-
-}
-
-
-bool ImagePathFinder::_can_go_straigth(const Point2& p_from, const Point2& p_to) const {
-
- int x1=p_from.x;
- int y1=p_from.y;
- int x2=p_to.x;
- int y2=p_to.y;
-
-#define _TEST_VALID \
- {\
- uint32_t ofs=drawy*width+drawx;\
- if (cells[ofs].solid) {\
- if (!((drawx>0 && cells[ofs-1].visited) ||\
- (drawx<width-1 && cells[ofs+1].visited) ||\
- (drawy>0 && cells[ofs-width].visited) ||\
- (drawy<height-1 && cells[ofs+width].visited))) {\
- return false;\
- }\
- }\
- }\
-
-
- int n, deltax, deltay, sgndeltax, sgndeltay, deltaxabs, deltayabs, x, y, drawx, drawy;
- deltax = x2 - x1;
- deltay = y2 - y1;
- deltaxabs = ABS(deltax);
- deltayabs = ABS(deltay);
- sgndeltax = SGN(deltax);
- sgndeltay = SGN(deltay);
- x = deltayabs >> 1;
- y = deltaxabs >> 1;
- drawx = x1;
- drawy = y1;
- int pc=0;
-
- _TEST_VALID
-
- if(deltaxabs >= deltayabs) {
- for(n = 0; n < deltaxabs; n++) {
- y += deltayabs;
- if(y >= deltaxabs){
- y -= deltaxabs;
- drawy += sgndeltay;
- }
- drawx += sgndeltax;
- _TEST_VALID
- }
- } else {
- for(n = 0; n < deltayabs; n++) {
- x += deltaxabs;
- if(x >= deltayabs) {
- x -= deltayabs;
- drawx += sgndeltax;
- }
- drawy += sgndeltay;
- _TEST_VALID
- }
- }
- return true;
-
-
-}
-
-bool ImagePathFinder::_is_linear_path(const Point2& p_from, const Point2& p_to) {
-
- int x1=p_from.x;
- int y1=p_from.y;
- int x2=p_to.x;
- int y2=p_to.y;
-
-#define _TEST_CELL \
- if (cells[drawy*width+drawx].solid)\
- return false;
-
-
- int n, deltax, deltay, sgndeltax, sgndeltay, deltaxabs, deltayabs, x, y, drawx, drawy;
- deltax = x2 - x1;
- deltay = y2 - y1;
- deltaxabs = ABS(deltax);
- deltayabs = ABS(deltay);
- sgndeltax = SGN(deltax);
- sgndeltay = SGN(deltay);
- x = deltayabs >> 1;
- y = deltaxabs >> 1;
- drawx = x1;
- drawy = y1;
- int pc=0;
-
- _TEST_CELL
-
- if(deltaxabs >= deltayabs) {
- for(n = 0; n < deltaxabs; n++) {
- y += deltayabs;
- if(y >= deltaxabs){
- y -= deltaxabs;
- drawy += sgndeltay;
- }
- drawx += sgndeltax;
- _TEST_CELL
- }
- } else {
- for(n = 0; n < deltayabs; n++) {
- x += deltaxabs;
- if(x >= deltayabs) {
- x -= deltayabs;
- drawx += sgndeltax;
- }
- drawy += sgndeltay;
- _TEST_CELL
- }
- }
- return true;
-}
-
-
-DVector<Point2> ImagePathFinder::find_path(const Point2& p_from, const Point2& p_to,bool p_optimize) {
-
-
- Point2i from=p_from;
- Point2i to=p_to;
-
- ERR_FAIL_COND_V(from.x < 0,DVector<Point2>());
- ERR_FAIL_COND_V(from.y < 0,DVector<Point2>());
- ERR_FAIL_COND_V(from.x >=width,DVector<Point2>());
- ERR_FAIL_COND_V(from.y >=height,DVector<Point2>());
- ERR_FAIL_COND_V(to.x < 0,DVector<Point2>());
- ERR_FAIL_COND_V(to.y < 0,DVector<Point2>());
- ERR_FAIL_COND_V(to.x >=width,DVector<Point2>());
- ERR_FAIL_COND_V(to.y >=height,DVector<Point2>());
-
- if (from==to) {
- DVector<Point2> p;
- p.push_back(from);
- return p;
- }
-
- _lock();
-
-
- if (p_optimize) { //try a line first
-
- if (_is_linear_path(p_from,p_to)) {
- _unlock();
- DVector<Point2> p;
- p.push_back(from);
- p.push_back(to);
- return p;
- }
- }
-
-
- //clear all
- for(int i=0;i<width*height;i++) {
-
- bool s = cells[i].solid;
- cells[i].data=0;
- cells[i].solid=s;
- }
-
-#define CELL_INDEX(m_p) (m_p.y*width+m_p.x)
-#define CELL_COST(m_p) (cells[CELL_INDEX(m_p)].cost+( ABS(m_p.x-to.x)+ABS(m_p.y-to.y))*10)
-
-
- Set<Point2i> pending;
- pending.insert(from);
-
- //helper constants
- static const Point2i neighbour_rel[8]={
- Point2i(-1,-1), //0
- Point2i(-1, 0), //1
- Point2i(-1,+1), //2
- Point2i( 0,-1), //3
- Point2i( 0,+1), //4
- Point2i(+1,-1), //5
- Point2i(+1, 0), //6
- Point2i(+1,+1) }; //7
-
- static const int neighbour_cost[8]={
- 14,
- 10,
- 14,
- 10,
- 10,
- 14,
- 10,
- 14
- };
-
- static const int neighbour_parent[8]={
- 7,
- 6,
- 5,
- 4,
- 3,
- 2,
- 1,
- 0,
- };
-
- while(true) {
-
- if (pending.size() == 0) {
- _unlock();
- return DVector<Point2>(); // points don't connect
- }
- Point2i current;
- int lc=0x7FFFFFFF;
- { //find the one with the least cost
-
- Set<Point2i>::Element *Efound=NULL;
- for (Set<Point2i>::Element *E=pending.front();E;E=E->next()) {
-
- int cc =CELL_COST(E->get());
- if (cc<lc) {
- lc=cc;
- current=E->get();
- Efound=E;
-
- }
-
- }
- pending.erase(Efound);
- }
-
- Cell &c = cells[CELL_INDEX(current)];
-
- //search around other cells
-
-
- int accum_cost = (from==current) ? 0 : cells[CELL_INDEX((current + neighbour_rel[c.parent]))].cost;
-
- bool done=false;
-
- for(int i=0;i<8;i++) {
-
- Point2i neighbour=current+neighbour_rel[i];
- if (neighbour.x<0 || neighbour.y<0 || neighbour.x>=width || neighbour.y>=height)
- continue;
-
- Cell &n = cells[CELL_INDEX(neighbour)];
- if (n.solid)
- continue; //no good
-
- int cost = neighbour_cost[i]+accum_cost;
-
- if (n.visited && n.cost < cost)
- continue;
-
- n.cost=cost;
- n.parent=neighbour_parent[i];
- n.visited=true;
- pending.insert(neighbour);
- if (neighbour==to)
- done=true;
-
- }
-
- if (done)
- break;
- }
-
-
- // go througuh poins twice, first compute amount, then add them
-
- Point2i current=to;
- int pcount=0;
-
- while(true) {
-
- Cell &c = cells[CELL_INDEX(current)];
- c.visited=true;
- pcount++;
- if (current==from)
- break;
- current+=neighbour_rel[ c.parent ];
- }
-
- //now place them in an array
- DVector<Vector2> result;
- result.resize(pcount);
-
- DVector<Vector2>::Write res=result.write();
-
- current=to;
- int pidx=pcount-1;
-
- while(true) {
-
- Cell &c = cells[CELL_INDEX(current)];
- res[pidx]=current;
- pidx--;
- if (current==from)
- break;
- current+=neighbour_rel[ c.parent ];
- }
-
-
- //simplify..
-
-
- if (p_optimize) {
-
- int p=pcount-1;
- while(p>0) {
-
-
- int limit=p;
- while(limit>0) {
-
- limit--;
- if (!_can_go_straigth(res[p],res[limit]))
- break;
- }
-
-
- if (limit<p-1) {
- int diff = p-limit-1;
- pcount-=diff;
- for(int i=limit+1;i<pcount;i++) {
-
- res[i]=res[i+diff];
- }
- }
- p=limit;
- }
- }
-
- res=DVector<Vector2>::Write();
- result.resize(pcount);
- return result;
-}
-
-Size2 ImagePathFinder::get_size() const {
-
- return Size2(width,height);
-}
-bool ImagePathFinder::is_solid(const Point2& p_pos) {
-
-
- Point2i pos = p_pos;
-
- ERR_FAIL_COND_V(pos.x<0,true);
- ERR_FAIL_COND_V(pos.y<0,true);
- ERR_FAIL_COND_V(pos.x>=width,true);
- ERR_FAIL_COND_V(pos.y>=height,true);
-
- return cell_data[pos.y*width+pos.x].solid;
-}
-
-void ImagePathFinder::create_from_image_alpha(const Image& p_image) {
-
- ERR_FAIL_COND(p_image.get_format() != Image::FORMAT_RGBA);
- width = p_image.get_width();
- height = p_image.get_height();
- DVector<uint8_t> data = p_image.get_data();
- cell_data.resize(width * height);
- DVector<uint8_t>::Read read = data.read();
- DVector<Cell>::Write write = cell_data.write();
- for (int i=0; i<width * height; i++) {
- Cell cell;
- cell.data = 0;
- cell.solid = read[i*4+3] < 128;
- write[i] = cell;
- };
-};
-
-
-void ImagePathFinder::_bind_methods() {
-
- ObjectTypeDB::bind_method(_MD("find_path","from","to","optimize"),&ImagePathFinder::find_path,DEFVAL(false));
- ObjectTypeDB::bind_method(_MD("get_size"),&ImagePathFinder::get_size);
- ObjectTypeDB::bind_method(_MD("is_solid","pos"),&ImagePathFinder::is_solid);
- ObjectTypeDB::bind_method(_MD("create_from_image_alpha"),&ImagePathFinder::create_from_image_alpha);
-}
-
-ImagePathFinder::ImagePathFinder()
-{
-
- cells=NULL;
- width=0;
- height=0;
-}
diff --git a/scene/resources/image_path_finder.h b/scene/resources/image_path_finder.h deleted file mode 100644 index e975ea5ed9..0000000000 --- a/scene/resources/image_path_finder.h +++ /dev/null @@ -1,84 +0,0 @@ -/*************************************************************************/ -/* image_path_finder.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_PATH_FINDER_H
-#define IMAGE_PATH_FINDER_H
-
-#include "resource.h"
-
-class ImagePathFinder : public Resource{
-
-
- OBJ_TYPE(ImagePathFinder,Resource);
- union Cell {
-
- struct {
- bool solid:1;
- bool visited:1;
- bool final:1;
- uint8_t parent:3;
- uint32_t cost:26;
- };
-
- uint32_t data;
- };
-
-
-
-
-
- DVector<Cell>::Write lock;
- DVector<Cell> cell_data;
-
- uint32_t width;
- uint32_t height;
- Cell* cells; //when unlocked
-
- void _unlock();
- void _lock();
-
-
- _FORCE_INLINE_ bool _can_go_straigth(const Point2& p_from, const Point2& p_to) const;
- _FORCE_INLINE_ bool _is_linear_path(const Point2& p_from, const Point2& p_to);
-
-protected:
-
- static void _bind_methods();
-public:
-
- DVector<Point2> find_path(const Point2& p_from, const Point2& p_to,bool p_optimize=false);
- Size2 get_size() const;
- bool is_solid(const Point2& p_pos);
- void create_from_image_alpha(const Image& p_image);
-
-
-
- ImagePathFinder();
-};
-
-#endif // IMAGE_PATH_FINDER_H
diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index 2314926b2b..2c278f4fed 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -36,7 +36,8 @@ static const char*_flag_names[Material::FLAG_MAX]={ "invert_faces", "unshaded", "on_top", - "lightmap_on_uv2" + "lightmap_on_uv2", + "colarray_is_srgb" }; @@ -46,7 +47,8 @@ static const Material::Flag _flag_indices[Material::FLAG_MAX]={ Material::FLAG_INVERT_FACES, Material::FLAG_UNSHADED, Material::FLAG_ONTOP, - Material::FLAG_LIGHTMAP_ON_UV2 + Material::FLAG_LIGHTMAP_ON_UV2, + Material::FLAG_COLOR_ARRAY_SRGB, }; @@ -132,6 +134,8 @@ void Material::_bind_methods() { BIND_CONSTANT( FLAG_INVERT_FACES ); BIND_CONSTANT( FLAG_UNSHADED ); BIND_CONSTANT( FLAG_ONTOP ); + BIND_CONSTANT( FLAG_LIGHTMAP_ON_UV2 ); + BIND_CONSTANT( FLAG_COLOR_ARRAY_SRGB ); BIND_CONSTANT( FLAG_MAX ); BIND_CONSTANT( DEPTH_DRAW_ALWAYS ); @@ -156,6 +160,8 @@ Material::Material(const RID& p_material) { flags[FLAG_INVERT_FACES]=false; flags[FLAG_UNSHADED]=false; flags[FLAG_ONTOP]=false; + flags[FLAG_LIGHTMAP_ON_UV2]=true; + flags[FLAG_COLOR_ARRAY_SRGB]=false; depth_draw_mode=DEPTH_DRAW_OPAQUE_ONLY; @@ -316,14 +322,14 @@ Transform FixedMaterial::get_uv_transform() const { void FixedMaterial::set_fixed_flag(FixedFlag p_flag, bool p_value) { - ERR_FAIL_INDEX(p_flag,4); + ERR_FAIL_INDEX(p_flag,5); fixed_flags[p_flag]=p_value; VisualServer::get_singleton()->fixed_material_set_flag(material,(VS::FixedMaterialFlags)p_flag,p_value); } bool FixedMaterial::get_fixed_flag(FixedFlag p_flag) const { - ERR_FAIL_INDEX_V(p_flag,4,false); + ERR_FAIL_INDEX_V(p_flag,5,false); return fixed_flags[p_flag]; } @@ -371,6 +377,7 @@ void FixedMaterial::_bind_methods() { ADD_PROPERTYI( PropertyInfo( Variant::BOOL, "fixed_flags/use_color_array" ), _SCS("set_fixed_flag"), _SCS("get_fixed_flag"), FLAG_USE_COLOR_ARRAY); ADD_PROPERTYI( PropertyInfo( Variant::BOOL, "fixed_flags/use_point_size" ), _SCS("set_fixed_flag"), _SCS("get_fixed_flag"), FLAG_USE_POINT_SIZE); ADD_PROPERTYI( PropertyInfo( Variant::BOOL, "fixed_flags/discard_alpha" ), _SCS("set_fixed_flag"), _SCS("get_fixed_flag"), FLAG_DISCARD_ALPHA); + ADD_PROPERTYI( PropertyInfo( Variant::BOOL, "fixed_flags/use_xy_normalmap" ), _SCS("set_fixed_flag"), _SCS("get_fixed_flag"), FLAG_USE_XY_NORMALMAP); ADD_PROPERTYI( PropertyInfo( Variant::COLOR, "params/diffuse" ), _SCS("set_parameter"), _SCS("get_parameter"), PARAM_DIFFUSE); ADD_PROPERTYI( PropertyInfo( Variant::COLOR, "params/specular", PROPERTY_HINT_COLOR_NO_ALPHA ), _SCS("set_parameter"), _SCS("get_parameter"), PARAM_SPECULAR ); ADD_PROPERTYI( PropertyInfo( Variant::COLOR, "params/emission", PROPERTY_HINT_COLOR_NO_ALPHA ), _SCS("set_parameter"), _SCS("get_parameter"), PARAM_EMISSION ); @@ -426,11 +433,14 @@ FixedMaterial::FixedMaterial() : Material(VS::get_singleton()->fixed_material_cr param[PARAM_SHADE_PARAM]=0.5; param[PARAM_DETAIL]=1.0; - + set_flag(FLAG_COLOR_ARRAY_SRGB,true); fixed_flags[FLAG_USE_ALPHA]=false; fixed_flags[FLAG_USE_COLOR_ARRAY]=false; fixed_flags[FLAG_USE_POINT_SIZE]=false; + fixed_flags[FLAG_USE_XY_NORMALMAP]=false; + fixed_flags[FLAG_DISCARD_ALPHA]=false; + for(int i=0;i<PARAM_MAX;i++) { @@ -540,6 +550,10 @@ void ShaderMaterial::_bind_methods() { ObjectTypeDB::bind_method(_MD("set_shader","shader:Shader"), &ShaderMaterial::set_shader ); ObjectTypeDB::bind_method(_MD("get_shader:Shader"), &ShaderMaterial::get_shader ); + + ObjectTypeDB::bind_method(_MD("set_shader_param","param","value:var"), &ShaderMaterial::set_shader_param); + ObjectTypeDB::bind_method(_MD("get_shader_param:var","param"), &ShaderMaterial::get_shader_param); + ObjectTypeDB::bind_method(_MD("_shader_changed"), &ShaderMaterial::_shader_changed ); } @@ -585,6 +599,8 @@ ParticleSystemMaterial::ParticleSystemMaterial() :Material(VisualServer::get_sin set_depth_draw_mode(DEPTH_DRAW_NEVER); VisualServer::get_singleton()->fixed_material_set_flag(material,VS::FIXED_MATERIAL_FLAG_USE_ALPHA,true); VisualServer::get_singleton()->fixed_material_set_flag(material,VS::FIXED_MATERIAL_FLAG_USE_COLOR_ARRAY,true); + set_flag(FLAG_COLOR_ARRAY_SRGB,true); + } ParticleSystemMaterial::~ParticleSystemMaterial() { @@ -655,6 +671,8 @@ UnshadedMaterial::UnshadedMaterial() :Material(VisualServer::get_singleton()->fi set_flag(FLAG_UNSHADED,true); set_use_alpha(true); + set_flag(FLAG_COLOR_ARRAY_SRGB,true); + } UnshadedMaterial::~UnshadedMaterial() { diff --git a/scene/resources/material.h b/scene/resources/material.h index 23ecb18fac..9c3feede08 100644 --- a/scene/resources/material.h +++ b/scene/resources/material.h @@ -54,6 +54,7 @@ public: FLAG_UNSHADED = VS::MATERIAL_FLAG_UNSHADED, FLAG_ONTOP = VS::MATERIAL_FLAG_ONTOP, FLAG_LIGHTMAP_ON_UV2 = VS::MATERIAL_FLAG_LIGHTMAP_ON_UV2, + FLAG_COLOR_ARRAY_SRGB = VS::MATERIAL_FLAG_COLOR_ARRAY_SRGB, FLAG_MAX = VS::MATERIAL_FLAG_MAX }; @@ -141,7 +142,9 @@ public: FLAG_USE_ALPHA=VS::FIXED_MATERIAL_FLAG_USE_ALPHA, FLAG_USE_COLOR_ARRAY=VS::FIXED_MATERIAL_FLAG_USE_COLOR_ARRAY, FLAG_USE_POINT_SIZE=VS::FIXED_MATERIAL_FLAG_USE_POINT_SIZE, - FLAG_DISCARD_ALPHA=VS::FIXED_MATERIAL_FLAG_DISCARD_ALPHA + FLAG_DISCARD_ALPHA=VS::FIXED_MATERIAL_FLAG_DISCARD_ALPHA, + FLAG_USE_XY_NORMALMAP=VS::FIXED_MATERIAL_FLAG_USE_XY_NORMALMAP, + FLAG_MAX=VS::FIXED_MATERIAL_FLAG_MAX }; enum LightShader { @@ -166,7 +169,7 @@ private: Ref<Texture> texture_param[PARAM_MAX]; TexCoordMode texture_texcoord[PARAM_MAX]; LightShader light_shader; - bool fixed_flags[3]; + bool fixed_flags[FLAG_MAX]; float point_size; diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp index c6e492fcb3..3aeccdc551 100644 --- a/scene/resources/mesh.cpp +++ b/scene/resources/mesh.cpp @@ -29,6 +29,7 @@ #include "mesh.h" #include "scene/resources/concave_polygon_shape.h" #include "scene/resources/convex_polygon_shape.h" +#include "surface_tool.h" static const char*_array_name[]={ "vertex_array", @@ -648,6 +649,30 @@ void Mesh::center_geometry() { } +void Mesh::regen_normalmaps() { + + + Vector< Ref<SurfaceTool> > surfs; + for(int i=0;i<get_surface_count();i++) { + + Ref<SurfaceTool> st = memnew( SurfaceTool ); + st->create_from(Ref<Mesh>(this),i); + surfs.push_back(st); + } + + while (get_surface_count()) { + surface_remove(0); + } + + for(int i=0;i<surfs.size();i++) { + + surfs[i]->generate_tangents(); + surfs[i]->commit(Ref<Mesh>(this)); + } +} + + + Ref<TriangleMesh> Mesh::generate_triangle_mesh() const { if (triangle_mesh.is_valid()) @@ -740,6 +765,8 @@ void Mesh::_bind_methods() { ObjectTypeDB::bind_method(_MD("surface_get_name","surf_idx"),&Mesh::surface_get_name); ObjectTypeDB::bind_method(_MD("center_geometry"),&Mesh::center_geometry); ObjectTypeDB::set_method_flags(get_type_static(),_SCS("center_geometry"),METHOD_FLAGS_DEFAULT|METHOD_FLAG_EDITOR); + ObjectTypeDB::bind_method(_MD("regen_normalmaps"),&Mesh::regen_normalmaps); + ObjectTypeDB::set_method_flags(get_type_static(),_SCS("regen_normalmaps"),METHOD_FLAGS_DEFAULT|METHOD_FLAG_EDITOR); ObjectTypeDB::bind_method(_MD("set_custom_aabb","aabb"),&Mesh::set_custom_aabb); ObjectTypeDB::bind_method(_MD("get_custom_aabb"),&Mesh::get_custom_aabb); diff --git a/scene/resources/mesh.h b/scene/resources/mesh.h index 5243163a4d..d6ab6a1198 100644 --- a/scene/resources/mesh.h +++ b/scene/resources/mesh.h @@ -167,6 +167,7 @@ public: Ref<Shape> create_convex_shape() const; void center_geometry(); + void regen_normalmaps(); DVector<Face3> get_faces() const; Ref<TriangleMesh> generate_triangle_mesh() const; diff --git a/scene/resources/mikktspace.c b/scene/resources/mikktspace.c new file mode 100644 index 0000000000..62aa2da251 --- /dev/null +++ b/scene/resources/mikktspace.c @@ -0,0 +1,1890 @@ +/** \file mikktspace/mikktspace.c + * \ingroup mikktspace + */ +/** + * Copyright (C) 2011 by Morten S. Mikkelsen + * + * 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. + */ + +#include <assert.h> +#include <stdio.h> +#include <math.h> +#include <string.h> +#include <float.h> +#include <stdlib.h> + +#include "mikktspace.h" + +#define TFALSE 0 +#define TTRUE 1 + +#ifndef M_PI +#define M_PI 3.1415926535897932384626433832795 +#endif + +#define INTERNAL_RND_SORT_SEED 39871946 + +// internal structure +typedef struct { + float x, y, z; +} SVec3; + +static tbool veq( const SVec3 v1, const SVec3 v2 ) +{ + return (v1.x == v2.x) && (v1.y == v2.y) && (v1.z == v2.z); +} + +static SVec3 vadd( const SVec3 v1, const SVec3 v2 ) +{ + SVec3 vRes; + + vRes.x = v1.x + v2.x; + vRes.y = v1.y + v2.y; + vRes.z = v1.z + v2.z; + + return vRes; +} + + +static SVec3 vsub( const SVec3 v1, const SVec3 v2 ) +{ + SVec3 vRes; + + vRes.x = v1.x - v2.x; + vRes.y = v1.y - v2.y; + vRes.z = v1.z - v2.z; + + return vRes; +} + +static SVec3 vscale(const float fS, const SVec3 v) +{ + SVec3 vRes; + + vRes.x = fS * v.x; + vRes.y = fS * v.y; + vRes.z = fS * v.z; + + return vRes; +} + +static float LengthSquared( const SVec3 v ) +{ + return v.x*v.x + v.y*v.y + v.z*v.z; +} + +static float Length( const SVec3 v ) +{ + return sqrtf(LengthSquared(v)); +} + +static SVec3 Normalize( const SVec3 v ) +{ + return vscale(1 / Length(v), v); +} + +static float vdot( const SVec3 v1, const SVec3 v2) +{ + return v1.x*v2.x + v1.y*v2.y + v1.z*v2.z; +} + + +static tbool NotZero(const float fX) +{ + // could possibly use FLT_EPSILON instead + return fabsf(fX) > FLT_MIN; +} + +static tbool VNotZero(const SVec3 v) +{ + // might change this to an epsilon based test + return NotZero(v.x) || NotZero(v.y) || NotZero(v.z); +} + + + +typedef struct { + int iNrFaces; + int * pTriMembers; +} SSubGroup; + +typedef struct { + int iNrFaces; + int * pFaceIndices; + int iVertexRepresentitive; + tbool bOrientPreservering; +} SGroup; + +// +#define MARK_DEGENERATE 1 +#define QUAD_ONE_DEGEN_TRI 2 +#define GROUP_WITH_ANY 4 +#define ORIENT_PRESERVING 8 + + + +typedef struct { + int FaceNeighbors[3]; + SGroup * AssignedGroup[3]; + + // normalized first order face derivatives + SVec3 vOs, vOt; + float fMagS, fMagT; // original magnitudes + + // determines if the current and the next triangle are a quad. + int iOrgFaceNumber; + int iFlag, iTSpacesOffs; + unsigned char vert_num[4]; +} STriInfo; + +typedef struct { + SVec3 vOs; + float fMagS; + SVec3 vOt; + float fMagT; + int iCounter; // this is to average back into quads. + tbool bOrient; +} STSpace; + +static int GenerateInitialVerticesIndexList(STriInfo pTriInfos[], int piTriList_out[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn); +static void GenerateSharedVerticesIndexList(int piTriList_in_and_out[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn); +static void InitTriInfo(STriInfo pTriInfos[], const int piTriListIn[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn); +static int Build4RuleGroups(STriInfo pTriInfos[], SGroup pGroups[], int piGroupTrianglesBuffer[], const int piTriListIn[], const int iNrTrianglesIn); +static tbool GenerateTSpaces(STSpace psTspace[], const STriInfo pTriInfos[], const SGroup pGroups[], + const int iNrActiveGroups, const int piTriListIn[], const float fThresCos, + const SMikkTSpaceContext * pContext); + +static int MakeIndex(const int iFace, const int iVert) +{ + assert(iVert>=0 && iVert<4 && iFace>=0); + return (iFace<<2) | (iVert&0x3); +} + +static void IndexToData(int * piFace, int * piVert, const int iIndexIn) +{ + piVert[0] = iIndexIn&0x3; + piFace[0] = iIndexIn>>2; +} + +static STSpace AvgTSpace(const STSpace * pTS0, const STSpace * pTS1) +{ + STSpace ts_res; + + // this if is important. Due to floating point precision + // averaging when ts0==ts1 will cause a slight difference + // which results in tangent space splits later on + if (pTS0->fMagS==pTS1->fMagS && pTS0->fMagT==pTS1->fMagT && + veq(pTS0->vOs,pTS1->vOs) && veq(pTS0->vOt, pTS1->vOt)) + { + ts_res.fMagS = pTS0->fMagS; + ts_res.fMagT = pTS0->fMagT; + ts_res.vOs = pTS0->vOs; + ts_res.vOt = pTS0->vOt; + } + else + { + ts_res.fMagS = 0.5f*(pTS0->fMagS+pTS1->fMagS); + ts_res.fMagT = 0.5f*(pTS0->fMagT+pTS1->fMagT); + ts_res.vOs = vadd(pTS0->vOs,pTS1->vOs); + ts_res.vOt = vadd(pTS0->vOt,pTS1->vOt); + if ( VNotZero(ts_res.vOs) ) ts_res.vOs = Normalize(ts_res.vOs); + if ( VNotZero(ts_res.vOt) ) ts_res.vOt = Normalize(ts_res.vOt); + } + + return ts_res; +} + + + +static SVec3 GetPosition(const SMikkTSpaceContext * pContext, const int index); +static SVec3 GetNormal(const SMikkTSpaceContext * pContext, const int index); +static SVec3 GetTexCoord(const SMikkTSpaceContext * pContext, const int index); + + +// degen triangles +static void DegenPrologue(STriInfo pTriInfos[], int piTriList_out[], const int iNrTrianglesIn, const int iTotTris); +static void DegenEpilogue(STSpace psTspace[], STriInfo pTriInfos[], int piTriListIn[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn, const int iTotTris); + + +tbool genTangSpaceDefault(const SMikkTSpaceContext * pContext) +{ + return genTangSpace(pContext, 180.0f); +} + +tbool genTangSpace(const SMikkTSpaceContext * pContext, const float fAngularThreshold) +{ + // count nr_triangles + int * piTriListIn = NULL, * piGroupTrianglesBuffer = NULL; + STriInfo * pTriInfos = NULL; + SGroup * pGroups = NULL; + STSpace * psTspace = NULL; + int iNrTrianglesIn = 0, f=0, t=0, i=0; + int iNrTSPaces = 0, iTotTris = 0, iDegenTriangles = 0, iNrMaxGroups = 0; + int iNrActiveGroups = 0, index = 0; + const int iNrFaces = pContext->m_pInterface->m_getNumFaces(pContext); + tbool bRes = TFALSE; + const float fThresCos = (float) cos((fAngularThreshold*(float)M_PI)/180.0f); + + // verify all call-backs have been set + if ( pContext->m_pInterface->m_getNumFaces==NULL || + pContext->m_pInterface->m_getNumVerticesOfFace==NULL || + pContext->m_pInterface->m_getPosition==NULL || + pContext->m_pInterface->m_getNormal==NULL || + pContext->m_pInterface->m_getTexCoord==NULL ) + return TFALSE; + + // count triangles on supported faces + for (f=0; f<iNrFaces; f++) + { + const int verts = pContext->m_pInterface->m_getNumVerticesOfFace(pContext, f); + if (verts==3) ++iNrTrianglesIn; + else if (verts==4) iNrTrianglesIn += 2; + } + if (iNrTrianglesIn<=0) return TFALSE; + + // allocate memory for an index list + piTriListIn = (int *) malloc(sizeof(int)*3*iNrTrianglesIn); + pTriInfos = (STriInfo *) malloc(sizeof(STriInfo)*iNrTrianglesIn); + if (piTriListIn==NULL || pTriInfos==NULL) + { + if (piTriListIn!=NULL) free(piTriListIn); + if (pTriInfos!=NULL) free(pTriInfos); + return TFALSE; + } + + // make an initial triangle --> face index list + iNrTSPaces = GenerateInitialVerticesIndexList(pTriInfos, piTriListIn, pContext, iNrTrianglesIn); + + // make a welded index list of identical positions and attributes (pos, norm, texc) + //printf("gen welded index list begin\n"); + GenerateSharedVerticesIndexList(piTriListIn, pContext, iNrTrianglesIn); + //printf("gen welded index list end\n"); + + // Mark all degenerate triangles + iTotTris = iNrTrianglesIn; + iDegenTriangles = 0; + for (t=0; t<iTotTris; t++) + { + const int i0 = piTriListIn[t*3+0]; + const int i1 = piTriListIn[t*3+1]; + const int i2 = piTriListIn[t*3+2]; + const SVec3 p0 = GetPosition(pContext, i0); + const SVec3 p1 = GetPosition(pContext, i1); + const SVec3 p2 = GetPosition(pContext, i2); + if (veq(p0,p1) || veq(p0,p2) || veq(p1,p2)) // degenerate + { + pTriInfos[t].iFlag |= MARK_DEGENERATE; + ++iDegenTriangles; + } + } + iNrTrianglesIn = iTotTris - iDegenTriangles; + + // mark all triangle pairs that belong to a quad with only one + // good triangle. These need special treatment in DegenEpilogue(). + // Additionally, move all good triangles to the start of + // pTriInfos[] and piTriListIn[] without changing order and + // put the degenerate triangles last. + DegenPrologue(pTriInfos, piTriListIn, iNrTrianglesIn, iTotTris); + + + // evaluate triangle level attributes and neighbor list + //printf("gen neighbors list begin\n"); + InitTriInfo(pTriInfos, piTriListIn, pContext, iNrTrianglesIn); + //printf("gen neighbors list end\n"); + + + // based on the 4 rules, identify groups based on connectivity + iNrMaxGroups = iNrTrianglesIn*3; + pGroups = (SGroup *) malloc(sizeof(SGroup)*iNrMaxGroups); + piGroupTrianglesBuffer = (int *) malloc(sizeof(int)*iNrTrianglesIn*3); + if (pGroups==NULL || piGroupTrianglesBuffer==NULL) + { + if (pGroups!=NULL) free(pGroups); + if (piGroupTrianglesBuffer!=NULL) free(piGroupTrianglesBuffer); + free(piTriListIn); + free(pTriInfos); + return TFALSE; + } + //printf("gen 4rule groups begin\n"); + iNrActiveGroups = + Build4RuleGroups(pTriInfos, pGroups, piGroupTrianglesBuffer, piTriListIn, iNrTrianglesIn); + //printf("gen 4rule groups end\n"); + + // + + psTspace = (STSpace *) malloc(sizeof(STSpace)*iNrTSPaces); + if (psTspace==NULL) + { + free(piTriListIn); + free(pTriInfos); + free(pGroups); + free(piGroupTrianglesBuffer); + return TFALSE; + } + memset(psTspace, 0, sizeof(STSpace)*iNrTSPaces); + for (t=0; t<iNrTSPaces; t++) + { + psTspace[t].vOs.x=1.0f; psTspace[t].vOs.y=0.0f; psTspace[t].vOs.z=0.0f; psTspace[t].fMagS = 1.0f; + psTspace[t].vOt.x=0.0f; psTspace[t].vOt.y=1.0f; psTspace[t].vOt.z=0.0f; psTspace[t].fMagT = 1.0f; + } + + // make tspaces, each group is split up into subgroups if necessary + // based on fAngularThreshold. Finally a tangent space is made for + // every resulting subgroup + //printf("gen tspaces begin\n"); + bRes = GenerateTSpaces(psTspace, pTriInfos, pGroups, iNrActiveGroups, piTriListIn, fThresCos, pContext); + //printf("gen tspaces end\n"); + + // clean up + free(pGroups); + free(piGroupTrianglesBuffer); + + if (!bRes) // if an allocation in GenerateTSpaces() failed + { + // clean up and return false + free(pTriInfos); free(piTriListIn); free(psTspace); + return TFALSE; + } + + + // degenerate quads with one good triangle will be fixed by copying a space from + // the good triangle to the coinciding vertex. + // all other degenerate triangles will just copy a space from any good triangle + // with the same welded index in piTriListIn[]. + DegenEpilogue(psTspace, pTriInfos, piTriListIn, pContext, iNrTrianglesIn, iTotTris); + + free(pTriInfos); free(piTriListIn); + + index = 0; + for (f=0; f<iNrFaces; f++) + { + const int verts = pContext->m_pInterface->m_getNumVerticesOfFace(pContext, f); + if (verts!=3 && verts!=4) continue; + + + // I've decided to let degenerate triangles and group-with-anythings + // vary between left/right hand coordinate systems at the vertices. + // All healthy triangles on the other hand are built to always be either or. + + /*// force the coordinate system orientation to be uniform for every face. + // (this is already the case for good triangles but not for + // degenerate ones and those with bGroupWithAnything==true) + bool bOrient = psTspace[index].bOrient; + if (psTspace[index].iCounter == 0) // tspace was not derived from a group + { + // look for a space created in GenerateTSpaces() by iCounter>0 + bool bNotFound = true; + int i=1; + while (i<verts && bNotFound) + { + if (psTspace[index+i].iCounter > 0) bNotFound=false; + else ++i; + } + if (!bNotFound) bOrient = psTspace[index+i].bOrient; + }*/ + + // set data + for (i=0; i<verts; i++) + { + const STSpace * pTSpace = &psTspace[index]; + float tang[] = {pTSpace->vOs.x, pTSpace->vOs.y, pTSpace->vOs.z}; + float bitang[] = {pTSpace->vOt.x, pTSpace->vOt.y, pTSpace->vOt.z}; + if (pContext->m_pInterface->m_setTSpace!=NULL) + pContext->m_pInterface->m_setTSpace(pContext, tang, bitang, pTSpace->fMagS, pTSpace->fMagT, pTSpace->bOrient, f, i); + if (pContext->m_pInterface->m_setTSpaceBasic!=NULL) + pContext->m_pInterface->m_setTSpaceBasic(pContext, tang, pTSpace->bOrient==TTRUE ? 1.0f : (-1.0f), f, i); + + ++index; + } + } + + free(psTspace); + + + return TTRUE; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +typedef struct { + float vert[3]; + int index; +} STmpVert; + +static const int g_iCells = 2048; + +#ifdef _MSC_VER + #define NOINLINE __declspec(noinline) +#else + #define NOINLINE __attribute__ ((noinline)) +#endif + +// it is IMPORTANT that this function is called to evaluate the hash since +// inlining could potentially reorder instructions and generate different +// results for the same effective input value fVal. +static NOINLINE int FindGridCell(const float fMin, const float fMax, const float fVal) +{ + const float fIndex = g_iCells * ((fVal-fMin)/(fMax-fMin)); + const int iIndex = (int)fIndex; + return iIndex < g_iCells ? (iIndex >= 0 ? iIndex : 0) : (g_iCells - 1); +} + +static void MergeVertsFast(int piTriList_in_and_out[], STmpVert pTmpVert[], const SMikkTSpaceContext * pContext, const int iL_in, const int iR_in); +static void MergeVertsSlow(int piTriList_in_and_out[], const SMikkTSpaceContext * pContext, const int pTable[], const int iEntries); +static void GenerateSharedVerticesIndexListSlow(int piTriList_in_and_out[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn); + +static void GenerateSharedVerticesIndexList(int piTriList_in_and_out[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn) +{ + + // Generate bounding box + int * piHashTable=NULL, * piHashCount=NULL, * piHashOffsets=NULL, * piHashCount2=NULL; + STmpVert * pTmpVert = NULL; + int i=0, iChannel=0, k=0, e=0; + int iMaxCount=0; + SVec3 vMin = GetPosition(pContext, 0), vMax = vMin, vDim; + float fMin, fMax; + for (i=1; i<(iNrTrianglesIn*3); i++) + { + const int index = piTriList_in_and_out[i]; + + const SVec3 vP = GetPosition(pContext, index); + if (vMin.x > vP.x) vMin.x = vP.x; + else if (vMax.x < vP.x) vMax.x = vP.x; + if (vMin.y > vP.y) vMin.y = vP.y; + else if (vMax.y < vP.y) vMax.y = vP.y; + if (vMin.z > vP.z) vMin.z = vP.z; + else if (vMax.z < vP.z) vMax.z = vP.z; + } + + vDim = vsub(vMax,vMin); + iChannel = 0; + fMin = vMin.x; fMax=vMax.x; + if (vDim.y>vDim.x && vDim.y>vDim.z) + { + iChannel=1; + fMin = vMin.y, fMax=vMax.y; + } + else if (vDim.z>vDim.x) + { + iChannel=2; + fMin = vMin.z, fMax=vMax.z; + } + + // make allocations + piHashTable = (int *) malloc(sizeof(int)*iNrTrianglesIn*3); + piHashCount = (int *) malloc(sizeof(int)*g_iCells); + piHashOffsets = (int *) malloc(sizeof(int)*g_iCells); + piHashCount2 = (int *) malloc(sizeof(int)*g_iCells); + + if (piHashTable==NULL || piHashCount==NULL || piHashOffsets==NULL || piHashCount2==NULL) + { + if (piHashTable!=NULL) free(piHashTable); + if (piHashCount!=NULL) free(piHashCount); + if (piHashOffsets!=NULL) free(piHashOffsets); + if (piHashCount2!=NULL) free(piHashCount2); + GenerateSharedVerticesIndexListSlow(piTriList_in_and_out, pContext, iNrTrianglesIn); + return; + } + memset(piHashCount, 0, sizeof(int)*g_iCells); + memset(piHashCount2, 0, sizeof(int)*g_iCells); + + // count amount of elements in each cell unit + for (i=0; i<(iNrTrianglesIn*3); i++) + { + const int index = piTriList_in_and_out[i]; + const SVec3 vP = GetPosition(pContext, index); + const float fVal = iChannel==0 ? vP.x : (iChannel==1 ? vP.y : vP.z); + const int iCell = FindGridCell(fMin, fMax, fVal); + ++piHashCount[iCell]; + } + + // evaluate start index of each cell. + piHashOffsets[0]=0; + for (k=1; k<g_iCells; k++) + piHashOffsets[k]=piHashOffsets[k-1]+piHashCount[k-1]; + + // insert vertices + for (i=0; i<(iNrTrianglesIn*3); i++) + { + const int index = piTriList_in_and_out[i]; + const SVec3 vP = GetPosition(pContext, index); + const float fVal = iChannel==0 ? vP.x : (iChannel==1 ? vP.y : vP.z); + const int iCell = FindGridCell(fMin, fMax, fVal); + int * pTable = NULL; + + assert(piHashCount2[iCell]<piHashCount[iCell]); + pTable = &piHashTable[piHashOffsets[iCell]]; + pTable[piHashCount2[iCell]] = i; // vertex i has been inserted. + ++piHashCount2[iCell]; + } + for (k=0; k<g_iCells; k++) + assert(piHashCount2[k] == piHashCount[k]); // verify the count + free(piHashCount2); + + // find maximum amount of entries in any hash entry + iMaxCount = piHashCount[0]; + for (k=1; k<g_iCells; k++) + if (iMaxCount<piHashCount[k]) + iMaxCount=piHashCount[k]; + pTmpVert = (STmpVert *) malloc(sizeof(STmpVert)*iMaxCount); + + + // complete the merge + for (k=0; k<g_iCells; k++) + { + // extract table of cell k and amount of entries in it + int * pTable = &piHashTable[piHashOffsets[k]]; + const int iEntries = piHashCount[k]; + if (iEntries < 2) continue; + + if (pTmpVert!=NULL) + { + for (e=0; e<iEntries; e++) + { + int i = pTable[e]; + const SVec3 vP = GetPosition(pContext, piTriList_in_and_out[i]); + pTmpVert[e].vert[0] = vP.x; pTmpVert[e].vert[1] = vP.y; + pTmpVert[e].vert[2] = vP.z; pTmpVert[e].index = i; + } + MergeVertsFast(piTriList_in_and_out, pTmpVert, pContext, 0, iEntries-1); + } + else + MergeVertsSlow(piTriList_in_and_out, pContext, pTable, iEntries); + } + + if (pTmpVert!=NULL) { free(pTmpVert); } + free(piHashTable); + free(piHashCount); + free(piHashOffsets); +} + +static void MergeVertsFast(int piTriList_in_and_out[], STmpVert pTmpVert[], const SMikkTSpaceContext * pContext, const int iL_in, const int iR_in) +{ + // make bbox + int c=0, l=0, channel=0; + float fvMin[3], fvMax[3]; + float dx=0, dy=0, dz=0, fSep=0; + for (c=0; c<3; c++) + { fvMin[c]=pTmpVert[iL_in].vert[c]; fvMax[c]=fvMin[c]; } + for (l=(iL_in+1); l<=iR_in; l++) + for (c=0; c<3; c++) + if (fvMin[c]>pTmpVert[l].vert[c]) fvMin[c]=pTmpVert[l].vert[c]; + else if (fvMax[c]<pTmpVert[l].vert[c]) fvMax[c]=pTmpVert[l].vert[c]; + + dx = fvMax[0]-fvMin[0]; + dy = fvMax[1]-fvMin[1]; + dz = fvMax[2]-fvMin[2]; + + channel = 0; + if (dy>dx && dy>dz) channel=1; + else if (dz>dx) channel=2; + + fSep = 0.5f*(fvMax[channel]+fvMin[channel]); + + // terminate recursion when the separation/average value + // is no longer strictly between fMin and fMax values. + if (fSep>=fvMax[channel] || fSep<=fvMin[channel]) + { + // complete the weld + for (l=iL_in; l<=iR_in; l++) + { + int i = pTmpVert[l].index; + const int index = piTriList_in_and_out[i]; + const SVec3 vP = GetPosition(pContext, index); + const SVec3 vN = GetNormal(pContext, index); + const SVec3 vT = GetTexCoord(pContext, index); + + tbool bNotFound = TTRUE; + int l2=iL_in, i2rec=-1; + while (l2<l && bNotFound) + { + const int i2 = pTmpVert[l2].index; + const int index2 = piTriList_in_and_out[i2]; + const SVec3 vP2 = GetPosition(pContext, index2); + const SVec3 vN2 = GetNormal(pContext, index2); + const SVec3 vT2 = GetTexCoord(pContext, index2); + i2rec=i2; + + //if (vP==vP2 && vN==vN2 && vT==vT2) + if (vP.x==vP2.x && vP.y==vP2.y && vP.z==vP2.z && + vN.x==vN2.x && vN.y==vN2.y && vN.z==vN2.z && + vT.x==vT2.x && vT.y==vT2.y && vT.z==vT2.z) + bNotFound = TFALSE; + else + ++l2; + } + + // merge if previously found + if (!bNotFound) + piTriList_in_and_out[i] = piTriList_in_and_out[i2rec]; + } + } + else + { + int iL=iL_in, iR=iR_in; + assert((iR_in-iL_in)>0); // at least 2 entries + + // separate (by fSep) all points between iL_in and iR_in in pTmpVert[] + while (iL < iR) + { + tbool bReadyLeftSwap = TFALSE, bReadyRightSwap = TFALSE; + while ((!bReadyLeftSwap) && iL<iR) + { + assert(iL>=iL_in && iL<=iR_in); + bReadyLeftSwap = !(pTmpVert[iL].vert[channel]<fSep); + if (!bReadyLeftSwap) ++iL; + } + while ((!bReadyRightSwap) && iL<iR) + { + assert(iR>=iL_in && iR<=iR_in); + bReadyRightSwap = pTmpVert[iR].vert[channel]<fSep; + if (!bReadyRightSwap) --iR; + } + assert( (iL<iR) || !(bReadyLeftSwap && bReadyRightSwap) ); + + if (bReadyLeftSwap && bReadyRightSwap) + { + const STmpVert sTmp = pTmpVert[iL]; + assert(iL<iR); + pTmpVert[iL] = pTmpVert[iR]; + pTmpVert[iR] = sTmp; + ++iL; --iR; + } + } + + assert(iL==(iR+1) || (iL==iR)); + if (iL==iR) + { + const tbool bReadyRightSwap = pTmpVert[iR].vert[channel]<fSep; + if (bReadyRightSwap) ++iL; + else --iR; + } + + // only need to weld when there is more than 1 instance of the (x,y,z) + if (iL_in < iR) + MergeVertsFast(piTriList_in_and_out, pTmpVert, pContext, iL_in, iR); // weld all left of fSep + if (iL < iR_in) + MergeVertsFast(piTriList_in_and_out, pTmpVert, pContext, iL, iR_in); // weld all right of (or equal to) fSep + } +} + +static void MergeVertsSlow(int piTriList_in_and_out[], const SMikkTSpaceContext * pContext, const int pTable[], const int iEntries) +{ + // this can be optimized further using a tree structure or more hashing. + int e=0; + for (e=0; e<iEntries; e++) + { + int i = pTable[e]; + const int index = piTriList_in_and_out[i]; + const SVec3 vP = GetPosition(pContext, index); + const SVec3 vN = GetNormal(pContext, index); + const SVec3 vT = GetTexCoord(pContext, index); + + tbool bNotFound = TTRUE; + int e2=0, i2rec=-1; + while (e2<e && bNotFound) + { + const int i2 = pTable[e2]; + const int index2 = piTriList_in_and_out[i2]; + const SVec3 vP2 = GetPosition(pContext, index2); + const SVec3 vN2 = GetNormal(pContext, index2); + const SVec3 vT2 = GetTexCoord(pContext, index2); + i2rec = i2; + + if (veq(vP,vP2) && veq(vN,vN2) && veq(vT,vT2)) + bNotFound = TFALSE; + else + ++e2; + } + + // merge if previously found + if (!bNotFound) + piTriList_in_and_out[i] = piTriList_in_and_out[i2rec]; + } +} + +static void GenerateSharedVerticesIndexListSlow(int piTriList_in_and_out[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn) +{ + int iNumUniqueVerts = 0, t=0, i=0; + for (t=0; t<iNrTrianglesIn; t++) + { + for (i=0; i<3; i++) + { + const int offs = t*3 + i; + const int index = piTriList_in_and_out[offs]; + + const SVec3 vP = GetPosition(pContext, index); + const SVec3 vN = GetNormal(pContext, index); + const SVec3 vT = GetTexCoord(pContext, index); + + tbool bFound = TFALSE; + int t2=0, index2rec=-1; + while (!bFound && t2<=t) + { + int j=0; + while (!bFound && j<3) + { + const int index2 = piTriList_in_and_out[t2*3 + j]; + const SVec3 vP2 = GetPosition(pContext, index2); + const SVec3 vN2 = GetNormal(pContext, index2); + const SVec3 vT2 = GetTexCoord(pContext, index2); + + if (veq(vP,vP2) && veq(vN,vN2) && veq(vT,vT2)) + bFound = TTRUE; + else + ++j; + } + if (!bFound) ++t2; + } + + assert(bFound); + // if we found our own + if (index2rec == index) { ++iNumUniqueVerts; } + + piTriList_in_and_out[offs] = index2rec; + } + } +} + +static int GenerateInitialVerticesIndexList(STriInfo pTriInfos[], int piTriList_out[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn) +{ + int iTSpacesOffs = 0, f=0, t=0; + int iDstTriIndex = 0; + for (f=0; f<pContext->m_pInterface->m_getNumFaces(pContext); f++) + { + const int verts = pContext->m_pInterface->m_getNumVerticesOfFace(pContext, f); + if (verts!=3 && verts!=4) continue; + + pTriInfos[iDstTriIndex].iOrgFaceNumber = f; + pTriInfos[iDstTriIndex].iTSpacesOffs = iTSpacesOffs; + + if (verts==3) + { + unsigned char * pVerts = pTriInfos[iDstTriIndex].vert_num; + pVerts[0]=0; pVerts[1]=1; pVerts[2]=2; + piTriList_out[iDstTriIndex*3+0] = MakeIndex(f, 0); + piTriList_out[iDstTriIndex*3+1] = MakeIndex(f, 1); + piTriList_out[iDstTriIndex*3+2] = MakeIndex(f, 2); + ++iDstTriIndex; // next + } + else + { + { + pTriInfos[iDstTriIndex+1].iOrgFaceNumber = f; + pTriInfos[iDstTriIndex+1].iTSpacesOffs = iTSpacesOffs; + } + + { + // need an order independent way to evaluate + // tspace on quads. This is done by splitting + // along the shortest diagonal. + const int i0 = MakeIndex(f, 0); + const int i1 = MakeIndex(f, 1); + const int i2 = MakeIndex(f, 2); + const int i3 = MakeIndex(f, 3); + const SVec3 T0 = GetTexCoord(pContext, i0); + const SVec3 T1 = GetTexCoord(pContext, i1); + const SVec3 T2 = GetTexCoord(pContext, i2); + const SVec3 T3 = GetTexCoord(pContext, i3); + const float distSQ_02 = LengthSquared(vsub(T2,T0)); + const float distSQ_13 = LengthSquared(vsub(T3,T1)); + tbool bQuadDiagIs_02; + if (distSQ_02<distSQ_13) + bQuadDiagIs_02 = TTRUE; + else if (distSQ_13<distSQ_02) + bQuadDiagIs_02 = TFALSE; + else + { + const SVec3 P0 = GetPosition(pContext, i0); + const SVec3 P1 = GetPosition(pContext, i1); + const SVec3 P2 = GetPosition(pContext, i2); + const SVec3 P3 = GetPosition(pContext, i3); + const float distSQ_02 = LengthSquared(vsub(P2,P0)); + const float distSQ_13 = LengthSquared(vsub(P3,P1)); + + bQuadDiagIs_02 = distSQ_13<distSQ_02 ? TFALSE : TTRUE; + } + + if (bQuadDiagIs_02) + { + { + unsigned char * pVerts_A = pTriInfos[iDstTriIndex].vert_num; + pVerts_A[0]=0; pVerts_A[1]=1; pVerts_A[2]=2; + } + piTriList_out[iDstTriIndex*3+0] = i0; + piTriList_out[iDstTriIndex*3+1] = i1; + piTriList_out[iDstTriIndex*3+2] = i2; + ++iDstTriIndex; // next + { + unsigned char * pVerts_B = pTriInfos[iDstTriIndex].vert_num; + pVerts_B[0]=0; pVerts_B[1]=2; pVerts_B[2]=3; + } + piTriList_out[iDstTriIndex*3+0] = i0; + piTriList_out[iDstTriIndex*3+1] = i2; + piTriList_out[iDstTriIndex*3+2] = i3; + ++iDstTriIndex; // next + } + else + { + { + unsigned char * pVerts_A = pTriInfos[iDstTriIndex].vert_num; + pVerts_A[0]=0; pVerts_A[1]=1; pVerts_A[2]=3; + } + piTriList_out[iDstTriIndex*3+0] = i0; + piTriList_out[iDstTriIndex*3+1] = i1; + piTriList_out[iDstTriIndex*3+2] = i3; + ++iDstTriIndex; // next + { + unsigned char * pVerts_B = pTriInfos[iDstTriIndex].vert_num; + pVerts_B[0]=1; pVerts_B[1]=2; pVerts_B[2]=3; + } + piTriList_out[iDstTriIndex*3+0] = i1; + piTriList_out[iDstTriIndex*3+1] = i2; + piTriList_out[iDstTriIndex*3+2] = i3; + ++iDstTriIndex; // next + } + } + } + + iTSpacesOffs += verts; + assert(iDstTriIndex<=iNrTrianglesIn); + } + + for (t=0; t<iNrTrianglesIn; t++) + pTriInfos[t].iFlag = 0; + + // return total amount of tspaces + return iTSpacesOffs; +} + +static SVec3 GetPosition(const SMikkTSpaceContext * pContext, const int index) +{ + int iF, iI; + SVec3 res; float pos[3]; + IndexToData(&iF, &iI, index); + pContext->m_pInterface->m_getPosition(pContext, pos, iF, iI); + res.x=pos[0]; res.y=pos[1]; res.z=pos[2]; + return res; +} + +static SVec3 GetNormal(const SMikkTSpaceContext * pContext, const int index) +{ + int iF, iI; + SVec3 res; float norm[3]; + IndexToData(&iF, &iI, index); + pContext->m_pInterface->m_getNormal(pContext, norm, iF, iI); + res.x=norm[0]; res.y=norm[1]; res.z=norm[2]; + return res; +} + +static SVec3 GetTexCoord(const SMikkTSpaceContext * pContext, const int index) +{ + int iF, iI; + SVec3 res; float texc[2]; + IndexToData(&iF, &iI, index); + pContext->m_pInterface->m_getTexCoord(pContext, texc, iF, iI); + res.x=texc[0]; res.y=texc[1]; res.z=1.0f; + return res; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////// + +typedef union { + struct + { + int i0, i1, f; + }; + int array[3]; +} SEdge; + +static void BuildNeighborsFast(STriInfo pTriInfos[], SEdge * pEdges, const int piTriListIn[], const int iNrTrianglesIn); +static void BuildNeighborsSlow(STriInfo pTriInfos[], const int piTriListIn[], const int iNrTrianglesIn); + +// returns the texture area times 2 +static float CalcTexArea(const SMikkTSpaceContext * pContext, const int indices[]) +{ + const SVec3 t1 = GetTexCoord(pContext, indices[0]); + const SVec3 t2 = GetTexCoord(pContext, indices[1]); + const SVec3 t3 = GetTexCoord(pContext, indices[2]); + + const float t21x = t2.x-t1.x; + const float t21y = t2.y-t1.y; + const float t31x = t3.x-t1.x; + const float t31y = t3.y-t1.y; + + const float fSignedAreaSTx2 = t21x*t31y - t21y*t31x; + + return fSignedAreaSTx2<0 ? (-fSignedAreaSTx2) : fSignedAreaSTx2; +} + +static void InitTriInfo(STriInfo pTriInfos[], const int piTriListIn[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn) +{ + int f=0, i=0, t=0; + // pTriInfos[f].iFlag is cleared in GenerateInitialVerticesIndexList() which is called before this function. + + // generate neighbor info list + for (f=0; f<iNrTrianglesIn; f++) + for (i=0; i<3; i++) + { + pTriInfos[f].FaceNeighbors[i] = -1; + pTriInfos[f].AssignedGroup[i] = NULL; + + pTriInfos[f].vOs.x=0.0f; pTriInfos[f].vOs.y=0.0f; pTriInfos[f].vOs.z=0.0f; + pTriInfos[f].vOt.x=0.0f; pTriInfos[f].vOt.y=0.0f; pTriInfos[f].vOt.z=0.0f; + pTriInfos[f].fMagS = 0; + pTriInfos[f].fMagT = 0; + + // assumed bad + pTriInfos[f].iFlag |= GROUP_WITH_ANY; + } + + // evaluate first order derivatives + for (f=0; f<iNrTrianglesIn; f++) + { + // initial values + const SVec3 v1 = GetPosition(pContext, piTriListIn[f*3+0]); + const SVec3 v2 = GetPosition(pContext, piTriListIn[f*3+1]); + const SVec3 v3 = GetPosition(pContext, piTriListIn[f*3+2]); + const SVec3 t1 = GetTexCoord(pContext, piTriListIn[f*3+0]); + const SVec3 t2 = GetTexCoord(pContext, piTriListIn[f*3+1]); + const SVec3 t3 = GetTexCoord(pContext, piTriListIn[f*3+2]); + + const float t21x = t2.x-t1.x; + const float t21y = t2.y-t1.y; + const float t31x = t3.x-t1.x; + const float t31y = t3.y-t1.y; + const SVec3 d1 = vsub(v2,v1); + const SVec3 d2 = vsub(v3,v1); + + const float fSignedAreaSTx2 = t21x*t31y - t21y*t31x; + //assert(fSignedAreaSTx2!=0); + SVec3 vOs = vsub(vscale(t31y,d1), vscale(t21y,d2)); // eq 18 + SVec3 vOt = vadd(vscale(-t31x,d1), vscale(t21x,d2)); // eq 19 + + pTriInfos[f].iFlag |= (fSignedAreaSTx2>0 ? ORIENT_PRESERVING : 0); + + if ( NotZero(fSignedAreaSTx2) ) + { + const float fAbsArea = fabsf(fSignedAreaSTx2); + const float fLenOs = Length(vOs); + const float fLenOt = Length(vOt); + const float fS = (pTriInfos[f].iFlag&ORIENT_PRESERVING)==0 ? (-1.0f) : 1.0f; + if ( NotZero(fLenOs) ) pTriInfos[f].vOs = vscale(fS/fLenOs, vOs); + if ( NotZero(fLenOt) ) pTriInfos[f].vOt = vscale(fS/fLenOt, vOt); + + // evaluate magnitudes prior to normalization of vOs and vOt + pTriInfos[f].fMagS = fLenOs / fAbsArea; + pTriInfos[f].fMagT = fLenOt / fAbsArea; + + // if this is a good triangle + if ( NotZero(pTriInfos[f].fMagS) && NotZero(pTriInfos[f].fMagT)) + pTriInfos[f].iFlag &= (~GROUP_WITH_ANY); + } + } + + // force otherwise healthy quads to a fixed orientation + while (t<(iNrTrianglesIn-1)) + { + const int iFO_a = pTriInfos[t].iOrgFaceNumber; + const int iFO_b = pTriInfos[t+1].iOrgFaceNumber; + if (iFO_a==iFO_b) // this is a quad + { + const tbool bIsDeg_a = (pTriInfos[t].iFlag&MARK_DEGENERATE)!=0 ? TTRUE : TFALSE; + const tbool bIsDeg_b = (pTriInfos[t+1].iFlag&MARK_DEGENERATE)!=0 ? TTRUE : TFALSE; + + // bad triangles should already have been removed by + // DegenPrologue(), but just in case check bIsDeg_a and bIsDeg_a are false + if ((bIsDeg_a||bIsDeg_b)==TFALSE) + { + const tbool bOrientA = (pTriInfos[t].iFlag&ORIENT_PRESERVING)!=0 ? TTRUE : TFALSE; + const tbool bOrientB = (pTriInfos[t+1].iFlag&ORIENT_PRESERVING)!=0 ? TTRUE : TFALSE; + // if this happens the quad has extremely bad mapping!! + if (bOrientA!=bOrientB) + { + //printf("found quad with bad mapping\n"); + tbool bChooseOrientFirstTri = TFALSE; + if ((pTriInfos[t+1].iFlag&GROUP_WITH_ANY)!=0) bChooseOrientFirstTri = TTRUE; + else if ( CalcTexArea(pContext, &piTriListIn[t*3+0]) >= CalcTexArea(pContext, &piTriListIn[(t+1)*3+0]) ) + bChooseOrientFirstTri = TTRUE; + + // force match + { + const int t0 = bChooseOrientFirstTri ? t : (t+1); + const int t1 = bChooseOrientFirstTri ? (t+1) : t; + pTriInfos[t1].iFlag &= (~ORIENT_PRESERVING); // clear first + pTriInfos[t1].iFlag |= (pTriInfos[t0].iFlag&ORIENT_PRESERVING); // copy bit + } + } + } + t += 2; + } + else + ++t; + } + + // match up edge pairs + { + SEdge * pEdges = (SEdge *) malloc(sizeof(SEdge)*iNrTrianglesIn*3); + if (pEdges==NULL) + BuildNeighborsSlow(pTriInfos, piTriListIn, iNrTrianglesIn); + else + { + BuildNeighborsFast(pTriInfos, pEdges, piTriListIn, iNrTrianglesIn); + + free(pEdges); + } + } +} + +///////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////// + +static tbool AssignRecur(const int piTriListIn[], STriInfo psTriInfos[], const int iMyTriIndex, SGroup * pGroup); +static void AddTriToGroup(SGroup * pGroup, const int iTriIndex); + +static int Build4RuleGroups(STriInfo pTriInfos[], SGroup pGroups[], int piGroupTrianglesBuffer[], const int piTriListIn[], const int iNrTrianglesIn) +{ + const int iNrMaxGroups = iNrTrianglesIn*3; + int iNrActiveGroups = 0; + int iOffset = 0, f=0, i=0; + (void)iNrMaxGroups; /* quiet warnings in non debug mode */ + for (f=0; f<iNrTrianglesIn; f++) + { + for (i=0; i<3; i++) + { + // if not assigned to a group + if ((pTriInfos[f].iFlag&GROUP_WITH_ANY)==0 && pTriInfos[f].AssignedGroup[i]==NULL) + { + tbool bOrPre; + int neigh_indexL, neigh_indexR; + const int vert_index = piTriListIn[f*3+i]; + assert(iNrActiveGroups<iNrMaxGroups); + pTriInfos[f].AssignedGroup[i] = &pGroups[iNrActiveGroups]; + pTriInfos[f].AssignedGroup[i]->iVertexRepresentitive = vert_index; + pTriInfos[f].AssignedGroup[i]->bOrientPreservering = (pTriInfos[f].iFlag&ORIENT_PRESERVING)!=0; + pTriInfos[f].AssignedGroup[i]->iNrFaces = 0; + pTriInfos[f].AssignedGroup[i]->pFaceIndices = &piGroupTrianglesBuffer[iOffset]; + ++iNrActiveGroups; + + AddTriToGroup(pTriInfos[f].AssignedGroup[i], f); + bOrPre = (pTriInfos[f].iFlag&ORIENT_PRESERVING)!=0 ? TTRUE : TFALSE; + neigh_indexL = pTriInfos[f].FaceNeighbors[i]; + neigh_indexR = pTriInfos[f].FaceNeighbors[i>0?(i-1):2]; + if (neigh_indexL>=0) // neighbor + { + const tbool bAnswer = + AssignRecur(piTriListIn, pTriInfos, neigh_indexL, + pTriInfos[f].AssignedGroup[i] ); + + const tbool bOrPre2 = (pTriInfos[neigh_indexL].iFlag&ORIENT_PRESERVING)!=0 ? TTRUE : TFALSE; + const tbool bDiff = bOrPre!=bOrPre2 ? TTRUE : TFALSE; + assert(bAnswer || bDiff); + (void)bAnswer, (void)bDiff; /* quiet warnings in non debug mode */ + } + if (neigh_indexR>=0) // neighbor + { + const tbool bAnswer = + AssignRecur(piTriListIn, pTriInfos, neigh_indexR, + pTriInfos[f].AssignedGroup[i] ); + + const tbool bOrPre2 = (pTriInfos[neigh_indexR].iFlag&ORIENT_PRESERVING)!=0 ? TTRUE : TFALSE; + const tbool bDiff = bOrPre!=bOrPre2 ? TTRUE : TFALSE; + assert(bAnswer || bDiff); + (void)bAnswer, (void)bDiff; /* quiet warnings in non debug mode */ + } + + // update offset + iOffset += pTriInfos[f].AssignedGroup[i]->iNrFaces; + // since the groups are disjoint a triangle can never + // belong to more than 3 groups. Subsequently something + // is completely screwed if this assertion ever hits. + assert(iOffset <= iNrMaxGroups); + } + } + } + + return iNrActiveGroups; +} + +static void AddTriToGroup(SGroup * pGroup, const int iTriIndex) +{ + pGroup->pFaceIndices[pGroup->iNrFaces] = iTriIndex; + ++pGroup->iNrFaces; +} + +static tbool AssignRecur(const int piTriListIn[], STriInfo psTriInfos[], + const int iMyTriIndex, SGroup * pGroup) +{ + STriInfo * pMyTriInfo = &psTriInfos[iMyTriIndex]; + + // track down vertex + const int iVertRep = pGroup->iVertexRepresentitive; + const int * pVerts = &piTriListIn[3*iMyTriIndex+0]; + int i=-1; + if (pVerts[0]==iVertRep) i=0; + else if (pVerts[1]==iVertRep) i=1; + else if (pVerts[2]==iVertRep) i=2; + assert(i>=0 && i<3); + + // early out + if (pMyTriInfo->AssignedGroup[i] == pGroup) return TTRUE; + else if (pMyTriInfo->AssignedGroup[i]!=NULL) return TFALSE; + if ((pMyTriInfo->iFlag&GROUP_WITH_ANY)!=0) + { + // first to group with a group-with-anything triangle + // determines it's orientation. + // This is the only existing order dependency in the code!! + if ( pMyTriInfo->AssignedGroup[0] == NULL && + pMyTriInfo->AssignedGroup[1] == NULL && + pMyTriInfo->AssignedGroup[2] == NULL ) + { + pMyTriInfo->iFlag &= (~ORIENT_PRESERVING); + pMyTriInfo->iFlag |= (pGroup->bOrientPreservering ? ORIENT_PRESERVING : 0); + } + } + { + const tbool bOrient = (pMyTriInfo->iFlag&ORIENT_PRESERVING)!=0 ? TTRUE : TFALSE; + if (bOrient != pGroup->bOrientPreservering) return TFALSE; + } + + AddTriToGroup(pGroup, iMyTriIndex); + pMyTriInfo->AssignedGroup[i] = pGroup; + + { + const int neigh_indexL = pMyTriInfo->FaceNeighbors[i]; + const int neigh_indexR = pMyTriInfo->FaceNeighbors[i>0?(i-1):2]; + if (neigh_indexL>=0) + AssignRecur(piTriListIn, psTriInfos, neigh_indexL, pGroup); + if (neigh_indexR>=0) + AssignRecur(piTriListIn, psTriInfos, neigh_indexR, pGroup); + } + + + + return TTRUE; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////// + +static tbool CompareSubGroups(const SSubGroup * pg1, const SSubGroup * pg2); +static void QuickSort(int* pSortBuffer, int iLeft, int iRight, unsigned int uSeed); +static STSpace EvalTspace(int face_indices[], const int iFaces, const int piTriListIn[], const STriInfo pTriInfos[], const SMikkTSpaceContext * pContext, const int iVertexRepresentitive); + +static tbool GenerateTSpaces(STSpace psTspace[], const STriInfo pTriInfos[], const SGroup pGroups[], + const int iNrActiveGroups, const int piTriListIn[], const float fThresCos, + const SMikkTSpaceContext * pContext) +{ + STSpace * pSubGroupTspace = NULL; + SSubGroup * pUniSubGroups = NULL; + int * pTmpMembers = NULL; + int iMaxNrFaces=0, iUniqueTspaces=0, g=0, i=0; + for (g=0; g<iNrActiveGroups; g++) + if (iMaxNrFaces < pGroups[g].iNrFaces) + iMaxNrFaces = pGroups[g].iNrFaces; + + if (iMaxNrFaces == 0) return TTRUE; + + // make initial allocations + pSubGroupTspace = (STSpace *) malloc(sizeof(STSpace)*iMaxNrFaces); + pUniSubGroups = (SSubGroup *) malloc(sizeof(SSubGroup)*iMaxNrFaces); + pTmpMembers = (int *) malloc(sizeof(int)*iMaxNrFaces); + if (pSubGroupTspace==NULL || pUniSubGroups==NULL || pTmpMembers==NULL) + { + if (pSubGroupTspace!=NULL) free(pSubGroupTspace); + if (pUniSubGroups!=NULL) free(pUniSubGroups); + if (pTmpMembers!=NULL) free(pTmpMembers); + return TFALSE; + } + + + iUniqueTspaces = 0; + for (g=0; g<iNrActiveGroups; g++) + { + const SGroup * pGroup = &pGroups[g]; + int iUniqueSubGroups = 0, s=0; + + for (i=0; i<pGroup->iNrFaces; i++) // triangles + { + const int f = pGroup->pFaceIndices[i]; // triangle number + int index=-1, iVertIndex=-1, iOF_1=-1, iMembers=0, j=0, l=0; + SSubGroup tmp_group; + tbool bFound; + SVec3 n, vOs, vOt; + if (pTriInfos[f].AssignedGroup[0]==pGroup) index=0; + else if (pTriInfos[f].AssignedGroup[1]==pGroup) index=1; + else if (pTriInfos[f].AssignedGroup[2]==pGroup) index=2; + assert(index>=0 && index<3); + + iVertIndex = piTriListIn[f*3+index]; + assert(iVertIndex==pGroup->iVertexRepresentitive); + + // is normalized already + n = GetNormal(pContext, iVertIndex); + + // project + vOs = vsub(pTriInfos[f].vOs, vscale(vdot(n,pTriInfos[f].vOs), n)); + vOt = vsub(pTriInfos[f].vOt, vscale(vdot(n,pTriInfos[f].vOt), n)); + if ( VNotZero(vOs) ) vOs = Normalize(vOs); + if ( VNotZero(vOt) ) vOt = Normalize(vOt); + + // original face number + iOF_1 = pTriInfos[f].iOrgFaceNumber; + + iMembers = 0; + for (j=0; j<pGroup->iNrFaces; j++) + { + const int t = pGroup->pFaceIndices[j]; // triangle number + const int iOF_2 = pTriInfos[t].iOrgFaceNumber; + + // project + SVec3 vOs2 = vsub(pTriInfos[t].vOs, vscale(vdot(n,pTriInfos[t].vOs), n)); + SVec3 vOt2 = vsub(pTriInfos[t].vOt, vscale(vdot(n,pTriInfos[t].vOt), n)); + if ( VNotZero(vOs2) ) vOs2 = Normalize(vOs2); + if ( VNotZero(vOt2) ) vOt2 = Normalize(vOt2); + + { + const tbool bAny = ( (pTriInfos[f].iFlag | pTriInfos[t].iFlag) & GROUP_WITH_ANY )!=0 ? TTRUE : TFALSE; + // make sure triangles which belong to the same quad are joined. + const tbool bSameOrgFace = iOF_1==iOF_2 ? TTRUE : TFALSE; + + const float fCosS = vdot(vOs,vOs2); + const float fCosT = vdot(vOt,vOt2); + + assert(f!=t || bSameOrgFace); // sanity check + if (bAny || bSameOrgFace || (fCosS>fThresCos && fCosT>fThresCos)) + pTmpMembers[iMembers++] = t; + } + } + + // sort pTmpMembers + tmp_group.iNrFaces = iMembers; + tmp_group.pTriMembers = pTmpMembers; + if (iMembers>1) + { + unsigned int uSeed = INTERNAL_RND_SORT_SEED; // could replace with a random seed? + QuickSort(pTmpMembers, 0, iMembers-1, uSeed); + } + + // look for an existing match + bFound = TFALSE; + l=0; + while (l<iUniqueSubGroups && !bFound) + { + bFound = CompareSubGroups(&tmp_group, &pUniSubGroups[l]); + if (!bFound) ++l; + } + + // assign tangent space index + assert(bFound || l==iUniqueSubGroups); + //piTempTangIndices[f*3+index] = iUniqueTspaces+l; + + // if no match was found we allocate a new subgroup + if (!bFound) + { + // insert new subgroup + int * pIndices = (int *) malloc(sizeof(int)*iMembers); + if (pIndices==NULL) + { + // clean up and return false + int s=0; + for (s=0; s<iUniqueSubGroups; s++) + free(pUniSubGroups[s].pTriMembers); + free(pUniSubGroups); + free(pTmpMembers); + free(pSubGroupTspace); + return TFALSE; + } + pUniSubGroups[iUniqueSubGroups].iNrFaces = iMembers; + pUniSubGroups[iUniqueSubGroups].pTriMembers = pIndices; + memcpy(pIndices, tmp_group.pTriMembers, iMembers*sizeof(int)); + pSubGroupTspace[iUniqueSubGroups] = + EvalTspace(tmp_group.pTriMembers, iMembers, piTriListIn, pTriInfos, pContext, pGroup->iVertexRepresentitive); + ++iUniqueSubGroups; + } + + // output tspace + { + const int iOffs = pTriInfos[f].iTSpacesOffs; + const int iVert = pTriInfos[f].vert_num[index]; + STSpace * pTS_out = &psTspace[iOffs+iVert]; + assert(pTS_out->iCounter<2); + assert(((pTriInfos[f].iFlag&ORIENT_PRESERVING)!=0) == pGroup->bOrientPreservering); + if (pTS_out->iCounter==1) + { + *pTS_out = AvgTSpace(pTS_out, &pSubGroupTspace[l]); + pTS_out->iCounter = 2; // update counter + pTS_out->bOrient = pGroup->bOrientPreservering; + } + else + { + assert(pTS_out->iCounter==0); + *pTS_out = pSubGroupTspace[l]; + pTS_out->iCounter = 1; // update counter + pTS_out->bOrient = pGroup->bOrientPreservering; + } + } + } + + // clean up and offset iUniqueTspaces + for (s=0; s<iUniqueSubGroups; s++) + free(pUniSubGroups[s].pTriMembers); + iUniqueTspaces += iUniqueSubGroups; + } + + // clean up + free(pUniSubGroups); + free(pTmpMembers); + free(pSubGroupTspace); + + return TTRUE; +} + +static STSpace EvalTspace(int face_indices[], const int iFaces, const int piTriListIn[], const STriInfo pTriInfos[], + const SMikkTSpaceContext * pContext, const int iVertexRepresentitive) +{ + STSpace res; + float fAngleSum = 0; + int face=0; + res.vOs.x=0.0f; res.vOs.y=0.0f; res.vOs.z=0.0f; + res.vOt.x=0.0f; res.vOt.y=0.0f; res.vOt.z=0.0f; + res.fMagS = 0; res.fMagT = 0; + + for (face=0; face<iFaces; face++) + { + const int f = face_indices[face]; + + // only valid triangles get to add their contribution + if ( (pTriInfos[f].iFlag&GROUP_WITH_ANY)==0 ) + { + SVec3 n, vOs, vOt, p0, p1, p2, v1, v2; + float fCos, fAngle, fMagS, fMagT; + int i=-1, index=-1, i0=-1, i1=-1, i2=-1; + if (piTriListIn[3*f+0]==iVertexRepresentitive) i=0; + else if (piTriListIn[3*f+1]==iVertexRepresentitive) i=1; + else if (piTriListIn[3*f+2]==iVertexRepresentitive) i=2; + assert(i>=0 && i<3); + + // project + index = piTriListIn[3*f+i]; + n = GetNormal(pContext, index); + vOs = vsub(pTriInfos[f].vOs, vscale(vdot(n,pTriInfos[f].vOs), n)); + vOt = vsub(pTriInfos[f].vOt, vscale(vdot(n,pTriInfos[f].vOt), n)); + if ( VNotZero(vOs) ) vOs = Normalize(vOs); + if ( VNotZero(vOt) ) vOt = Normalize(vOt); + + i2 = piTriListIn[3*f + (i<2?(i+1):0)]; + i1 = piTriListIn[3*f + i]; + i0 = piTriListIn[3*f + (i>0?(i-1):2)]; + + p0 = GetPosition(pContext, i0); + p1 = GetPosition(pContext, i1); + p2 = GetPosition(pContext, i2); + v1 = vsub(p0,p1); + v2 = vsub(p2,p1); + + // project + v1 = vsub(v1, vscale(vdot(n,v1),n)); if ( VNotZero(v1) ) v1 = Normalize(v1); + v2 = vsub(v2, vscale(vdot(n,v2),n)); if ( VNotZero(v2) ) v2 = Normalize(v2); + + // weight contribution by the angle + // between the two edge vectors + fCos = vdot(v1,v2); fCos=fCos>1?1:(fCos<(-1) ? (-1) : fCos); + fAngle = (float) acos(fCos); + fMagS = pTriInfos[f].fMagS; + fMagT = pTriInfos[f].fMagT; + + res.vOs=vadd(res.vOs, vscale(fAngle,vOs)); + res.vOt=vadd(res.vOt,vscale(fAngle,vOt)); + res.fMagS+=(fAngle*fMagS); + res.fMagT+=(fAngle*fMagT); + fAngleSum += fAngle; + } + } + + // normalize + if ( VNotZero(res.vOs) ) res.vOs = Normalize(res.vOs); + if ( VNotZero(res.vOt) ) res.vOt = Normalize(res.vOt); + if (fAngleSum>0) + { + res.fMagS /= fAngleSum; + res.fMagT /= fAngleSum; + } + + return res; +} + +static tbool CompareSubGroups(const SSubGroup * pg1, const SSubGroup * pg2) +{ + tbool bStillSame=TTRUE; + int i=0; + if (pg1->iNrFaces!=pg2->iNrFaces) return TFALSE; + while (i<pg1->iNrFaces && bStillSame) + { + bStillSame = pg1->pTriMembers[i]==pg2->pTriMembers[i] ? TTRUE : TFALSE; + if (bStillSame) ++i; + } + return bStillSame; +} + +static void QuickSort(int* pSortBuffer, int iLeft, int iRight, unsigned int uSeed) +{ + int iL, iR, n, index, iMid, iTmp; + + // Random + unsigned int t=uSeed&31; + t=(uSeed<<t)|(uSeed>>(32-t)); + uSeed=uSeed+t+3; + // Random end + + iL=iLeft; iR=iRight; + n = (iR-iL)+1; + assert(n>=0); + index = (int) (uSeed%n); + + iMid=pSortBuffer[index + iL]; + + + do + { + while (pSortBuffer[iL] < iMid) + ++iL; + while (pSortBuffer[iR] > iMid) + --iR; + + if (iL <= iR) + { + iTmp = pSortBuffer[iL]; + pSortBuffer[iL] = pSortBuffer[iR]; + pSortBuffer[iR] = iTmp; + ++iL; --iR; + } + } + while (iL <= iR); + + if (iLeft < iR) + QuickSort(pSortBuffer, iLeft, iR, uSeed); + if (iL < iRight) + QuickSort(pSortBuffer, iL, iRight, uSeed); +} + +///////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////// + +static void QuickSortEdges(SEdge * pSortBuffer, int iLeft, int iRight, const int channel, unsigned int uSeed); +static void GetEdge(int * i0_out, int * i1_out, int * edgenum_out, const int indices[], const int i0_in, const int i1_in); + +static void BuildNeighborsFast(STriInfo pTriInfos[], SEdge * pEdges, const int piTriListIn[], const int iNrTrianglesIn) +{ + // build array of edges + unsigned int uSeed = INTERNAL_RND_SORT_SEED; // could replace with a random seed? + int iEntries=0, iCurStartIndex=-1, f=0, i=0; + for (f=0; f<iNrTrianglesIn; f++) + for (i=0; i<3; i++) + { + const int i0 = piTriListIn[f*3+i]; + const int i1 = piTriListIn[f*3+(i<2?(i+1):0)]; + pEdges[f*3+i].i0 = i0 < i1 ? i0 : i1; // put minimum index in i0 + pEdges[f*3+i].i1 = !(i0 < i1) ? i0 : i1; // put maximum index in i1 + pEdges[f*3+i].f = f; // record face number + } + + // sort over all edges by i0, this is the pricy one. + QuickSortEdges(pEdges, 0, iNrTrianglesIn*3-1, 0, uSeed); // sort channel 0 which is i0 + + // sub sort over i1, should be fast. + // could replace this with a 64 bit int sort over (i0,i1) + // with i0 as msb in the quicksort call above. + iEntries = iNrTrianglesIn*3; + iCurStartIndex = 0; + for (i=1; i<iEntries; i++) + { + if (pEdges[iCurStartIndex].i0 != pEdges[i].i0) + { + const int iL = iCurStartIndex; + const int iR = i-1; + //const int iElems = i-iL; + iCurStartIndex = i; + QuickSortEdges(pEdges, iL, iR, 1, uSeed); // sort channel 1 which is i1 + } + } + + // sub sort over f, which should be fast. + // this step is to remain compliant with BuildNeighborsSlow() when + // more than 2 triangles use the same edge (such as a butterfly topology). + iCurStartIndex = 0; + for (i=1; i<iEntries; i++) + { + if (pEdges[iCurStartIndex].i0 != pEdges[i].i0 || pEdges[iCurStartIndex].i1 != pEdges[i].i1) + { + const int iL = iCurStartIndex; + const int iR = i-1; + //const int iElems = i-iL; + iCurStartIndex = i; + QuickSortEdges(pEdges, iL, iR, 2, uSeed); // sort channel 2 which is f + } + } + + // pair up, adjacent triangles + for (i=0; i<iEntries; i++) + { + const int i0=pEdges[i].i0; + const int i1=pEdges[i].i1; + const int f = pEdges[i].f; + tbool bUnassigned_A; + + int i0_A, i1_A; + int edgenum_A, edgenum_B=0; // 0,1 or 2 + GetEdge(&i0_A, &i1_A, &edgenum_A, &piTriListIn[f*3], i0, i1); // resolve index ordering and edge_num + bUnassigned_A = pTriInfos[f].FaceNeighbors[edgenum_A] == -1 ? TTRUE : TFALSE; + + if (bUnassigned_A) + { + // get true index ordering + int j=i+1, t; + tbool bNotFound = TTRUE; + while (j<iEntries && i0==pEdges[j].i0 && i1==pEdges[j].i1 && bNotFound) + { + tbool bUnassigned_B; + int i0_B, i1_B; + t = pEdges[j].f; + // flip i0_B and i1_B + GetEdge(&i1_B, &i0_B, &edgenum_B, &piTriListIn[t*3], pEdges[j].i0, pEdges[j].i1); // resolve index ordering and edge_num + //assert(!(i0_A==i1_B && i1_A==i0_B)); + bUnassigned_B = pTriInfos[t].FaceNeighbors[edgenum_B]==-1 ? TTRUE : TFALSE; + if (i0_A==i0_B && i1_A==i1_B && bUnassigned_B) + bNotFound = TFALSE; + else + ++j; + } + + if (!bNotFound) + { + int t = pEdges[j].f; + pTriInfos[f].FaceNeighbors[edgenum_A] = t; + //assert(pTriInfos[t].FaceNeighbors[edgenum_B]==-1); + pTriInfos[t].FaceNeighbors[edgenum_B] = f; + } + } + } +} + +static void BuildNeighborsSlow(STriInfo pTriInfos[], const int piTriListIn[], const int iNrTrianglesIn) +{ + int f=0, i=0; + for (f=0; f<iNrTrianglesIn; f++) + { + for (i=0; i<3; i++) + { + // if unassigned + if (pTriInfos[f].FaceNeighbors[i] == -1) + { + const int i0_A = piTriListIn[f*3+i]; + const int i1_A = piTriListIn[f*3+(i<2?(i+1):0)]; + + // search for a neighbor + tbool bFound = TFALSE; + int t=0, j=0; + while (!bFound && t<iNrTrianglesIn) + { + if (t!=f) + { + j=0; + while (!bFound && j<3) + { + // in rev order + const int i1_B = piTriListIn[t*3+j]; + const int i0_B = piTriListIn[t*3+(j<2?(j+1):0)]; + //assert(!(i0_A==i1_B && i1_A==i0_B)); + if (i0_A==i0_B && i1_A==i1_B) + bFound = TTRUE; + else + ++j; + } + } + + if (!bFound) ++t; + } + + // assign neighbors + if (bFound) + { + pTriInfos[f].FaceNeighbors[i] = t; + //assert(pTriInfos[t].FaceNeighbors[j]==-1); + pTriInfos[t].FaceNeighbors[j] = f; + } + } + } + } +} + +static void QuickSortEdges(SEdge * pSortBuffer, int iLeft, int iRight, const int channel, unsigned int uSeed) +{ + unsigned int t; + int iL, iR, n, index, iMid; + + // early out + SEdge sTmp; + const int iElems = iRight-iLeft+1; + if (iElems<2) return; + else if (iElems==2) + { + if (pSortBuffer[iLeft].array[channel] > pSortBuffer[iRight].array[channel]) + { + sTmp = pSortBuffer[iLeft]; + pSortBuffer[iLeft] = pSortBuffer[iRight]; + pSortBuffer[iRight] = sTmp; + } + return; + } + + // Random + t=uSeed&31; + t=(uSeed<<t)|(uSeed>>(32-t)); + uSeed=uSeed+t+3; + // Random end + + iL=iLeft, iR=iRight; + n = (iR-iL)+1; + assert(n>=0); + index = (int) (uSeed%n); + + iMid=pSortBuffer[index + iL].array[channel]; + + do + { + while (pSortBuffer[iL].array[channel] < iMid) + ++iL; + while (pSortBuffer[iR].array[channel] > iMid) + --iR; + + if (iL <= iR) + { + sTmp = pSortBuffer[iL]; + pSortBuffer[iL] = pSortBuffer[iR]; + pSortBuffer[iR] = sTmp; + ++iL; --iR; + } + } + while (iL <= iR); + + if (iLeft < iR) + QuickSortEdges(pSortBuffer, iLeft, iR, channel, uSeed); + if (iL < iRight) + QuickSortEdges(pSortBuffer, iL, iRight, channel, uSeed); +} + +// resolve ordering and edge number +static void GetEdge(int * i0_out, int * i1_out, int * edgenum_out, const int indices[], const int i0_in, const int i1_in) +{ + *edgenum_out = -1; + + // test if first index is on the edge + if (indices[0]==i0_in || indices[0]==i1_in) + { + // test if second index is on the edge + if (indices[1]==i0_in || indices[1]==i1_in) + { + edgenum_out[0]=0; // first edge + i0_out[0]=indices[0]; + i1_out[0]=indices[1]; + } + else + { + edgenum_out[0]=2; // third edge + i0_out[0]=indices[2]; + i1_out[0]=indices[0]; + } + } + else + { + // only second and third index is on the edge + edgenum_out[0]=1; // second edge + i0_out[0]=indices[1]; + i1_out[0]=indices[2]; + } +} + + +///////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////// Degenerate triangles //////////////////////////////////// + +static void DegenPrologue(STriInfo pTriInfos[], int piTriList_out[], const int iNrTrianglesIn, const int iTotTris) +{ + int iNextGoodTriangleSearchIndex=-1; + tbool bStillFindingGoodOnes; + + // locate quads with only one good triangle + int t=0; + while (t<(iTotTris-1)) + { + const int iFO_a = pTriInfos[t].iOrgFaceNumber; + const int iFO_b = pTriInfos[t+1].iOrgFaceNumber; + if (iFO_a==iFO_b) // this is a quad + { + const tbool bIsDeg_a = (pTriInfos[t].iFlag&MARK_DEGENERATE)!=0 ? TTRUE : TFALSE; + const tbool bIsDeg_b = (pTriInfos[t+1].iFlag&MARK_DEGENERATE)!=0 ? TTRUE : TFALSE; + if ((bIsDeg_a^bIsDeg_b)!=0) + { + pTriInfos[t].iFlag |= QUAD_ONE_DEGEN_TRI; + pTriInfos[t+1].iFlag |= QUAD_ONE_DEGEN_TRI; + } + t += 2; + } + else + ++t; + } + + // reorder list so all degen triangles are moved to the back + // without reordering the good triangles + iNextGoodTriangleSearchIndex = 1; + t=0; + bStillFindingGoodOnes = TTRUE; + while (t<iNrTrianglesIn && bStillFindingGoodOnes) + { + const tbool bIsGood = (pTriInfos[t].iFlag&MARK_DEGENERATE)==0 ? TTRUE : TFALSE; + if (bIsGood) + { + if (iNextGoodTriangleSearchIndex < (t+2)) + iNextGoodTriangleSearchIndex = t+2; + } + else + { + int t0, t1; + // search for the first good triangle. + tbool bJustADegenerate = TTRUE; + while (bJustADegenerate && iNextGoodTriangleSearchIndex<iTotTris) + { + const tbool bIsGood = (pTriInfos[iNextGoodTriangleSearchIndex].iFlag&MARK_DEGENERATE)==0 ? TTRUE : TFALSE; + if (bIsGood) bJustADegenerate=TFALSE; + else ++iNextGoodTriangleSearchIndex; + } + + t0 = t; + t1 = iNextGoodTriangleSearchIndex; + ++iNextGoodTriangleSearchIndex; + assert(iNextGoodTriangleSearchIndex > (t+1)); + + // swap triangle t0 and t1 + if (!bJustADegenerate) + { + int i=0; + for (i=0; i<3; i++) + { + const int index = piTriList_out[t0*3+i]; + piTriList_out[t0*3+i] = piTriList_out[t1*3+i]; + piTriList_out[t1*3+i] = index; + } + { + const STriInfo tri_info = pTriInfos[t0]; + pTriInfos[t0] = pTriInfos[t1]; + pTriInfos[t1] = tri_info; + } + } + else + bStillFindingGoodOnes = TFALSE; // this is not supposed to happen + } + + if (bStillFindingGoodOnes) ++t; + } + + assert(bStillFindingGoodOnes); // code will still work. + assert(iNrTrianglesIn == t); +} + +static void DegenEpilogue(STSpace psTspace[], STriInfo pTriInfos[], int piTriListIn[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn, const int iTotTris) +{ + int t=0, i=0; + // deal with degenerate triangles + // punishment for degenerate triangles is O(N^2) + for (t=iNrTrianglesIn; t<iTotTris; t++) + { + // degenerate triangles on a quad with one good triangle are skipped + // here but processed in the next loop + const tbool bSkip = (pTriInfos[t].iFlag&QUAD_ONE_DEGEN_TRI)!=0 ? TTRUE : TFALSE; + + if (!bSkip) + { + for (i=0; i<3; i++) + { + const int index1 = piTriListIn[t*3+i]; + // search through the good triangles + tbool bNotFound = TTRUE; + int j=0; + while (bNotFound && j<(3*iNrTrianglesIn)) + { + const int index2 = piTriListIn[j]; + if (index1==index2) bNotFound=TFALSE; + else ++j; + } + + if (!bNotFound) + { + const int iTri = j/3; + const int iVert = j%3; + const int iSrcVert=pTriInfos[iTri].vert_num[iVert]; + const int iSrcOffs=pTriInfos[iTri].iTSpacesOffs; + const int iDstVert=pTriInfos[t].vert_num[i]; + const int iDstOffs=pTriInfos[t].iTSpacesOffs; + + // copy tspace + psTspace[iDstOffs+iDstVert] = psTspace[iSrcOffs+iSrcVert]; + } + } + } + } + + // deal with degenerate quads with one good triangle + for (t=0; t<iNrTrianglesIn; t++) + { + // this triangle belongs to a quad where the + // other triangle is degenerate + if ( (pTriInfos[t].iFlag&QUAD_ONE_DEGEN_TRI)!=0 ) + { + SVec3 vDstP; + int iOrgF=-1, i=0; + tbool bNotFound; + unsigned char * pV = pTriInfos[t].vert_num; + int iFlag = (1<<pV[0]) | (1<<pV[1]) | (1<<pV[2]); + int iMissingIndex = 0; + if ((iFlag&2)==0) iMissingIndex=1; + else if ((iFlag&4)==0) iMissingIndex=2; + else if ((iFlag&8)==0) iMissingIndex=3; + + iOrgF = pTriInfos[t].iOrgFaceNumber; + vDstP = GetPosition(pContext, MakeIndex(iOrgF, iMissingIndex)); + bNotFound = TTRUE; + i=0; + while (bNotFound && i<3) + { + const int iVert = pV[i]; + const SVec3 vSrcP = GetPosition(pContext, MakeIndex(iOrgF, iVert)); + if (veq(vSrcP, vDstP)==TTRUE) + { + const int iOffs = pTriInfos[t].iTSpacesOffs; + psTspace[iOffs+iMissingIndex] = psTspace[iOffs+iVert]; + bNotFound=TFALSE; + } + else + ++i; + } + assert(!bNotFound); + } + } +} diff --git a/scene/resources/mikktspace.h b/scene/resources/mikktspace.h new file mode 100644 index 0000000000..52c44a713c --- /dev/null +++ b/scene/resources/mikktspace.h @@ -0,0 +1,145 @@ +/** \file mikktspace/mikktspace.h + * \ingroup mikktspace + */ +/** + * Copyright (C) 2011 by Morten S. Mikkelsen + * + * 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. + */ + +#ifndef __MIKKTSPACE_H__ +#define __MIKKTSPACE_H__ + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Author: Morten S. Mikkelsen + * Version: 1.0 + * + * The files mikktspace.h and mikktspace.c are designed to be + * stand-alone files and it is important that they are kept this way. + * Not having dependencies on structures/classes/libraries specific + * to the program, in which they are used, allows them to be copied + * and used as is into any tool, program or plugin. + * The code is designed to consistently generate the same + * tangent spaces, for a given mesh, in any tool in which it is used. + * This is done by performing an internal welding step and subsequently an order-independent evaluation + * of tangent space for meshes consisting of triangles and quads. + * This means faces can be received in any order and the same is true for + * the order of vertices of each face. The generated result will not be affected + * by such reordering. Additionally, whether degenerate (vertices or texture coordinates) + * primitives are present or not will not affect the generated results either. + * Once tangent space calculation is done the vertices of degenerate primitives will simply + * inherit tangent space from neighboring non degenerate primitives. + * The analysis behind this implementation can be found in my master's thesis + * which is available for download --> http://image.diku.dk/projects/media/morten.mikkelsen.08.pdf + * Note that though the tangent spaces at the vertices are generated in an order-independent way, + * by this implementation, the interpolated tangent space is still affected by which diagonal is + * chosen to split each quad. A sensible solution is to have your tools pipeline always + * split quads by the shortest diagonal. This choice is order-independent and works with mirroring. + * If these have the same length then compare the diagonals defined by the texture coordinates. + * XNormal which is a tool for baking normal maps allows you to write your own tangent space plugin + * and also quad triangulator plugin. + */ + + +typedef int tbool; +typedef struct SMikkTSpaceContext SMikkTSpaceContext; + +typedef struct { + // Returns the number of faces (triangles/quads) on the mesh to be processed. + int (*m_getNumFaces)(const SMikkTSpaceContext * pContext); + + // Returns the number of vertices on face number iFace + // iFace is a number in the range {0, 1, ..., getNumFaces()-1} + int (*m_getNumVerticesOfFace)(const SMikkTSpaceContext * pContext, const int iFace); + + // returns the position/normal/texcoord of the referenced face of vertex number iVert. + // iVert is in the range {0,1,2} for triangles and {0,1,2,3} for quads. + void (*m_getPosition)(const SMikkTSpaceContext * pContext, float fvPosOut[], const int iFace, const int iVert); + void (*m_getNormal)(const SMikkTSpaceContext * pContext, float fvNormOut[], const int iFace, const int iVert); + void (*m_getTexCoord)(const SMikkTSpaceContext * pContext, float fvTexcOut[], const int iFace, const int iVert); + + // either (or both) of the two setTSpace callbacks can be set. + // The call-back m_setTSpaceBasic() is sufficient for basic normal mapping. + + // This function is used to return the tangent and fSign to the application. + // fvTangent is a unit length vector. + // For normal maps it is sufficient to use the following simplified version of the bitangent which is generated at pixel/vertex level. + // bitangent = fSign * cross(vN, tangent); + // Note that the results are returned unindexed. It is possible to generate a new index list + // But averaging/overwriting tangent spaces by using an already existing index list WILL produce INCRORRECT results. + // DO NOT! use an already existing index list. + void (*m_setTSpaceBasic)(const SMikkTSpaceContext * pContext, const float fvTangent[], const float fSign, const int iFace, const int iVert); + + // This function is used to return tangent space results to the application. + // fvTangent and fvBiTangent are unit length vectors and fMagS and fMagT are their + // true magnitudes which can be used for relief mapping effects. + // fvBiTangent is the "real" bitangent and thus may not be perpendicular to fvTangent. + // However, both are perpendicular to the vertex normal. + // For normal maps it is sufficient to use the following simplified version of the bitangent which is generated at pixel/vertex level. + // fSign = bIsOrientationPreserving ? 1.0f : (-1.0f); + // bitangent = fSign * cross(vN, tangent); + // Note that the results are returned unindexed. It is possible to generate a new index list + // But averaging/overwriting tangent spaces by using an already existing index list WILL produce INCRORRECT results. + // DO NOT! use an already existing index list. + void (*m_setTSpace)(const SMikkTSpaceContext * pContext, const float fvTangent[], const float fvBiTangent[], const float fMagS, const float fMagT, + const tbool bIsOrientationPreserving, const int iFace, const int iVert); +} SMikkTSpaceInterface; + +struct SMikkTSpaceContext +{ + SMikkTSpaceInterface * m_pInterface; // initialized with callback functions + void * m_pUserData; // pointer to client side mesh data etc. (passed as the first parameter with every interface call) +}; + +// these are both thread safe! +tbool genTangSpaceDefault(const SMikkTSpaceContext * pContext); // Default (recommended) fAngularThreshold is 180 degrees (which means threshold disabled) +tbool genTangSpace(const SMikkTSpaceContext * pContext, const float fAngularThreshold); + + +// To avoid visual errors (distortions/unwanted hard edges in lighting), when using sampled normal maps, the +// normal map sampler must use the exact inverse of the pixel shader transformation. +// The most efficient transformation we can possibly do in the pixel shader is +// achieved by using, directly, the "unnormalized" interpolated tangent, bitangent and vertex normal: vT, vB and vN. +// pixel shader (fast transform out) +// vNout = normalize( vNt.x * vT + vNt.y * vB + vNt.z * vN ); +// where vNt is the tangent space normal. The normal map sampler must likewise use the +// interpolated and "unnormalized" tangent, bitangent and vertex normal to be compliant with the pixel shader. +// sampler does (exact inverse of pixel shader): +// float3 row0 = cross(vB, vN); +// float3 row1 = cross(vN, vT); +// float3 row2 = cross(vT, vB); +// float fSign = dot(vT, row0)<0 ? -1 : 1; +// vNt = normalize( fSign * float3(dot(vNout,row0), dot(vNout,row1), dot(vNout,row2)) ); +// where vNout is the sampled normal in some chosen 3D space. +// +// Should you choose to reconstruct the bitangent in the pixel shader instead +// of the vertex shader, as explained earlier, then be sure to do this in the normal map sampler also. +// Finally, beware of quad triangulations. If the normal map sampler doesn't use the same triangulation of +// quads as your renderer then problems will occur since the interpolated tangent spaces will differ +// eventhough the vertex level tangent spaces match. This can be solved either by triangulating before +// sampling/exporting or by using the order-independent choice of diagonal for splitting quads suggested earlier. +// However, this must be used both by the sampler and your tools/rendering pipeline. + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp index 3bd7314778..75a1b765ee 100644 --- a/scene/resources/packed_scene.cpp +++ b/scene/resources/packed_scene.cpp @@ -29,6 +29,9 @@ #include "packed_scene.h" #include "globals.h" #include "io/resource_loader.h" +#include "scene/3d/spatial.h" +#include "scene/gui/control.h" +#include "scene/2d/node_2d.h" bool PackedScene::can_instance() const { @@ -80,9 +83,28 @@ Node *PackedScene::instance(bool p_gen_edit_state) const { } else { //create anew Object * obj = ObjectTypeDB::instance(snames[ n.type ]); - ERR_FAIL_COND_V(!obj,NULL); + if (!obj || !obj->cast_to<Node>()) { + if (obj) { + memdelete(obj); + obj=NULL; + } + WARN_PRINT(String("Warning node of type "+snames[n.type].operator String()+" does not exist.").ascii().get_data()); + if (n.parent>=0 && n.parent<nc && ret_nodes[n.parent]) { + if (ret_nodes[n.parent]->cast_to<Spatial>()) { + obj = memnew( Spatial ); + } else if (ret_nodes[n.parent]->cast_to<Control>()) { + obj = memnew( Control ); + } else if (ret_nodes[n.parent]->cast_to<Node2D>()) { + obj = memnew( Node2D ); + } + + } + if (!obj) { + obj = memnew( Node ); + } + } + node = obj->cast_to<Node>(); - ERR_FAIL_COND_V(!node,NULL); } @@ -225,22 +247,34 @@ Error PackedScene::_parse_node(Node *p_owner,Node *p_node,int p_parent_idx, Map< p_node->get_property_list(&plist); for (List<PropertyInfo>::Element *E=plist.front();E;E=E->next()) { - if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) + + if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) { continue; + } String name = E->get().name; Variant value = p_node->get( E->get().name ); - if (E->get().usage & PROPERTY_USAGE_STORE_IF_NONZERO && value.is_zero()) + if (E->get().usage & PROPERTY_USAGE_STORE_IF_NONZERO && value.is_zero()) { continue; + } if (nd.instance>=0) { //only save changed properties in instance - if (!instance_state.has(name)) + /* + // this was commented because it would not save properties created from within script + // done with _get_property_list, that are not in the original node. + // if some property is not saved, check again + + if (!instance_state.has(name)) { + print_line("skip not in instance"); continue; - if (instance_state[name]==value) + }*/ + + if (instance_state[name]==value) { continue; + } } diff --git a/scene/resources/scene_preloader.cpp b/scene/resources/scene_preloader.cpp index 59d8cae970..e37a2c4967 100644 --- a/scene/resources/scene_preloader.cpp +++ b/scene/resources/scene_preloader.cpp @@ -26,430 +26,429 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "scene_preloader.h"
-#include "scene/io/scene_loader.h"
-#include "globals.h"
-
-bool ScenePreloader::can_instance() const {
-
- return nodes.size()>0;
-}
-
-Node *ScenePreloader::instance() const {
-
- int nc = nodes.size();
- ERR_FAIL_COND_V(nc==0,NULL);
-
- const StringName*snames=NULL;
- int sname_count=names.size();
- if (sname_count)
- snames=&names[0];
-
- const Variant*props=NULL;
- int prop_count=variants.size();
- if (prop_count)
- props=&variants[0];
-
- Vector<Variant> properties;
-
- const NodeData *nd = &nodes[0];
-
- Node **ret_nodes=(Node**)alloca( sizeof(Node*)*nc );
-
- for(int i=0;i<nc;i++) {
-
- const NodeData &n=nd[i];
-
-
- if (!ObjectTypeDB::is_type_enabled(snames[n.type])) {
- ret_nodes[i]=NULL;
- continue;
- }
-
- Object * obj = ObjectTypeDB::instance(snames[ n.type ]);
- ERR_FAIL_COND_V(!obj,NULL);
- Node *node = obj->cast_to<Node>();
- ERR_FAIL_COND_V(!node,NULL);
-
- int nprop_count=n.properties.size();
- if (nprop_count) {
-
- const NodeData::Property* nprops=&n.properties[0];
-
- for(int j=0;j<nprop_count;j++) {
-
- bool valid;
- node->set(snames[ nprops[j].name ],props[ nprops[j].value ],&valid);
- }
-
-
- }
-
- node->set_name( snames[ n.name ] );
- ret_nodes[i]=node;
- if (i>0) {
- ERR_FAIL_INDEX_V(n.parent,i,NULL);
- ERR_FAIL_COND_V(!ret_nodes[n.parent],NULL);
- ret_nodes[n.parent]->add_child(node);
- }
- }
-
-
- //do connections
-
- int cc = connections.size();
- const ConnectionData *cdata = connections.ptr();
-
- for(int i=0;i<cc;i++) {
-
- const ConnectionData &c=cdata[i];
- ERR_FAIL_INDEX_V( c.from, nc, NULL );
- ERR_FAIL_INDEX_V( c.to, nc, NULL );
-
- Vector<Variant> binds;
- if (c.binds.size()) {
- binds.resize(c.binds.size());
- for(int j=0;j<c.binds.size();j++)
- binds[j]=props[ c.binds[j] ];
- }
-
- if (!ret_nodes[c.from] || !ret_nodes[c.to])
- continue;
- ret_nodes[c.from]->connect( snames[ c.signal], ret_nodes[ c.to ], snames[ c.method], binds,CONNECT_PERSIST );
- }
-
- return ret_nodes[0];
-
-}
-
-
-static int _nm_get_string(const String& p_string, Map<StringName,int> &name_map) {
-
- if (name_map.has(p_string))
- return name_map[p_string];
-
- int idx = name_map.size();
- name_map[p_string]=idx;
- return idx;
-}
-
-static int _vm_get_variant(const Variant& p_variant, HashMap<Variant,int,VariantHasher> &variant_map) {
-
- if (variant_map.has(p_variant))
- return variant_map[p_variant];
-
- int idx = variant_map.size();
- variant_map[p_variant]=idx;
- return idx;
-}
-
-void ScenePreloader::_parse_node(Node *p_owner,Node *p_node,int p_parent_idx, Map<StringName,int> &name_map,HashMap<Variant,int,VariantHasher> &variant_map,Map<Node*,int> &node_map) {
-
- if (p_node!=p_owner && !p_node->get_owner())
- return;
-
- NodeData nd;
- nd.name=_nm_get_string(p_node->get_name(),name_map);
- nd.type=_nm_get_string(p_node->get_type(),name_map);
- nd.parent=p_parent_idx;
-
- List<PropertyInfo> plist;
- p_node->get_property_list(&plist);
- for (List<PropertyInfo>::Element *E=plist.front();E;E=E->next()) {
-
- if (!(E->get().usage & PROPERTY_USAGE_STORAGE))
- continue;
-
- NodeData::Property prop;
- prop.name=_nm_get_string(E->get().name,name_map);
- prop.value=_vm_get_variant( p_node->get( E->get().name ), variant_map);
- nd.properties.push_back(prop);
- }
-
- int idx = nodes.size();
- node_map[p_node]=idx;
- nodes.push_back(nd);
-
- for(int i=0;i<p_node->get_child_count();i++) {
-
- Node *c=p_node->get_child(i);
- _parse_node(p_owner,c,idx,name_map,variant_map,node_map);
- }
-
-
-
-}
-
-void ScenePreloader::_parse_connections(Node *p_node, Map<StringName,int> &name_map,HashMap<Variant,int,VariantHasher> &variant_map,Map<Node*,int> &node_map,bool p_instance) {
-
-
- List<MethodInfo> signals;
- p_node->get_signal_list(&signals);
-
- for(List<MethodInfo>::Element *E=signals.front();E;E=E->next()) {
-
- List<Node::Connection> conns;
- p_node->get_signal_connection_list(E->get().name,&conns);
- for(List<Node::Connection>::Element *F=conns.front();F;F=F->next()) {
-
- const Node::Connection &c = F->get();
- if (!(c.flags&CONNECT_PERSIST))
- continue;
- Node *n=c.target->cast_to<Node>();
- if (!n)
- continue;
-
- if (!node_map.has(n))
- continue;
-
- ConnectionData cd;
- cd.from=node_map[p_node];
- cd.to=node_map[n];
- cd.method=_nm_get_string(c.method,name_map);
- cd.signal=_nm_get_string(c.signal,name_map);
- for(int i=0;i<c.binds.size();i++) {
-
- cd.binds.push_back( _vm_get_variant(c.binds[i],variant_map));
- }
- connections.push_back(cd);
- }
- }
-
-}
-
-
-Error ScenePreloader::load_scene(const String& p_path) {
-
- return ERR_CANT_OPEN;
-#if 0
- if (path==p_path)
- return OK;
-
- String p=Globals::get_singleton()->localize_path(p_path);
- clear();
-
- Node *scene = SceneLoader::load(p);
-
- ERR_FAIL_COND_V(!scene,ERR_CANT_OPEN);
-
- path=p;
-
- Map<StringName,int> name_map;
- HashMap<Variant,int,VariantHasher> variant_map;
- Map<Node*,int> node_map;
-
- _parse_node(scene,scene,-1,name_map,variant_map,node_map);
-
-
- names.resize(name_map.size());
-
- for(Map<StringName,int>::Element *E=name_map.front();E;E=E->next()) {
-
- names[E->get()]=E->key();
- }
-
- variants.resize(variant_map.size());
- const Variant *K=NULL;
- while((K=variant_map.next(K))) {
-
- int idx = variant_map[*K];
- variants[idx]=*K;
- }
-
-
- memdelete(scene); // <- me falto esto :(
- return OK;
-#endif
-}
-
-String ScenePreloader::get_scene_path() const {
-
- return path;
-}
-
-void ScenePreloader::clear() {
-
- names.clear();
- variants.clear();
- nodes.clear();
- connections.clear();
-
-}
-
-void ScenePreloader::_set_bundled_scene(const Dictionary& d) {
-
-
- ERR_FAIL_COND( !d.has("names"));
- ERR_FAIL_COND( !d.has("variants"));
- ERR_FAIL_COND( !d.has("node_count"));
- ERR_FAIL_COND( !d.has("nodes"));
- ERR_FAIL_COND( !d.has("conn_count"));
- ERR_FAIL_COND( !d.has("conns"));
- ERR_FAIL_COND( !d.has("path"));
-
- DVector<String> snames = d["names"];
- if (snames.size()) {
-
- int namecount = snames.size();
- names.resize(namecount);
- DVector<String>::Read r =snames.read();
- for(int i=0;i<names.size();i++)
- names[i]=r[i];
- }
-
- Array svariants = d["variants"];
-
- if (svariants.size()) {
- int varcount=svariants.size();
- variants.resize(varcount);
- for(int i=0;i<varcount;i++) {
-
- variants[i]=svariants[i];
- }
-
- } else {
- variants.clear();
- }
-
- nodes.resize(d["node_count"]);
- int nc=nodes.size();
- if (nc) {
- DVector<int> snodes = d["nodes"];
- DVector<int>::Read r = snodes.read();
- int idx=0;
- for(int i=0;i<nc;i++) {
- NodeData &nd = nodes[i];
- nd.parent=r[idx++];
- nd.type=r[idx++];
- nd.name=r[idx++];
- nd.properties.resize(r[idx++]);
- for(int j=0;j<nd.properties.size();j++) {
-
- nd.properties[j].name=r[idx++];
- nd.properties[j].value=r[idx++];
- }
- }
-
- }
-
- connections.resize(d["conn_count"]);
- int cc=connections.size();
-
- if (cc) {
-
- DVector<int> sconns = d["conns"];
- DVector<int>::Read r = sconns.read();
- int idx=0;
- for(int i=0;i<nc;i++) {
- ConnectionData &cd = connections[nc];
- cd.from=r[idx++];
- cd.to=r[idx++];
- cd.signal=r[idx++];
- cd.method=r[idx++];
- cd.binds.resize(r[idx++]);
- for(int j=0;j<cd.binds.size();j++) {
-
- cd.binds[j]=r[idx++];
- }
- }
-
- }
-
-
-
- path=d["path"];
-
-}
-
-Dictionary ScenePreloader::_get_bundled_scene() const {
-
- DVector<String> rnames;
- rnames.resize(names.size());
-
- if (names.size()) {
-
- DVector<String>::Write r=rnames.write();
-
- for(int i=0;i<names.size();i++)
- r[i]=names[i];
- }
-
- Dictionary d;
- d["names"]=rnames;
- d["variants"]=variants;
-
- Vector<int> rnodes;
- d["node_count"]=nodes.size();
-
- for(int i=0;i<nodes.size();i++) {
-
- const NodeData &nd=nodes[i];
- rnodes.push_back(nd.parent);
- rnodes.push_back(nd.type);
- rnodes.push_back(nd.name);
- rnodes.push_back(nd.properties.size());
- for(int j=0;j<nd.properties.size();j++) {
-
- rnodes.push_back(nd.properties[j].name);
- rnodes.push_back(nd.properties[j].value);
- }
- }
-
- d["nodes"]=rnodes;
-
- Vector<int> rconns;
- d["conn_count"]=connections.size();
-
- for(int i=0;i<connections.size();i++) {
-
- const ConnectionData &cd=connections[i];
- rconns.push_back(cd.from);
- rconns.push_back(cd.to);
- rconns.push_back(cd.signal);
- rconns.push_back(cd.method);
- rconns.push_back(cd.binds.size());
- for(int j=0;j<cd.binds.size();i++)
- rconns.push_back(cd.binds[j]);
-
- }
-
- d["conns"]=rconns;
-
- d["path"]=path;
-
- return d;
-
-
-}
-
-void ScenePreloader::_bind_methods() {
-
- ObjectTypeDB::bind_method(_MD("load_scene","path"),&ScenePreloader::load_scene);
- ObjectTypeDB::bind_method(_MD("get_scene_path"),&ScenePreloader::get_scene_path);
- ObjectTypeDB::bind_method(_MD("instance:Node"),&ScenePreloader::instance);
- ObjectTypeDB::bind_method(_MD("can_instance"),&ScenePreloader::can_instance);
- ObjectTypeDB::bind_method(_MD("_set_bundled_scene"),&ScenePreloader::_set_bundled_scene);
- ObjectTypeDB::bind_method(_MD("_get_bundled_scene"),&ScenePreloader::_get_bundled_scene);
-
- ADD_PROPERTY( PropertyInfo(Variant::DICTIONARY,"_bundled",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_BUNDLE),_SCS("_set_bundled_scene"),_SCS("_get_bundled_scene"));
-#if 0
- List<String> extensions;
- SceneLoader::get_recognized_extensions(&extensions);
- String exthint;
- for (List<String>::Element*E=extensions.front();E;E=E->next()) {
-
- if (exthint!="")
- exthint+=",";
- exthint+="*."+E->get();
- }
-
- exthint+="; Scenes";
-
- ADD_PROPERTY( PropertyInfo(Variant::STRING,"scene",PROPERTY_HINT_FILE,exthint),_SCS("load_scene"),_SCS("get_scene_path"));
-#endif
-}
-
-ScenePreloader::ScenePreloader() {
-
-
-}
+#include "scene_preloader.h" +#include "globals.h" + +bool ScenePreloader::can_instance() const { + + return nodes.size()>0; +} + +Node *ScenePreloader::instance() const { + + int nc = nodes.size(); + ERR_FAIL_COND_V(nc==0,NULL); + + const StringName*snames=NULL; + int sname_count=names.size(); + if (sname_count) + snames=&names[0]; + + const Variant*props=NULL; + int prop_count=variants.size(); + if (prop_count) + props=&variants[0]; + + Vector<Variant> properties; + + const NodeData *nd = &nodes[0]; + + Node **ret_nodes=(Node**)alloca( sizeof(Node*)*nc ); + + for(int i=0;i<nc;i++) { + + const NodeData &n=nd[i]; + + + if (!ObjectTypeDB::is_type_enabled(snames[n.type])) { + ret_nodes[i]=NULL; + continue; + } + + Object * obj = ObjectTypeDB::instance(snames[ n.type ]); + ERR_FAIL_COND_V(!obj,NULL); + Node *node = obj->cast_to<Node>(); + ERR_FAIL_COND_V(!node,NULL); + + int nprop_count=n.properties.size(); + if (nprop_count) { + + const NodeData::Property* nprops=&n.properties[0]; + + for(int j=0;j<nprop_count;j++) { + + bool valid; + node->set(snames[ nprops[j].name ],props[ nprops[j].value ],&valid); + } + + + } + + node->set_name( snames[ n.name ] ); + ret_nodes[i]=node; + if (i>0) { + ERR_FAIL_INDEX_V(n.parent,i,NULL); + ERR_FAIL_COND_V(!ret_nodes[n.parent],NULL); + ret_nodes[n.parent]->add_child(node); + } + } + + + //do connections + + int cc = connections.size(); + const ConnectionData *cdata = connections.ptr(); + + for(int i=0;i<cc;i++) { + + const ConnectionData &c=cdata[i]; + ERR_FAIL_INDEX_V( c.from, nc, NULL ); + ERR_FAIL_INDEX_V( c.to, nc, NULL ); + + Vector<Variant> binds; + if (c.binds.size()) { + binds.resize(c.binds.size()); + for(int j=0;j<c.binds.size();j++) + binds[j]=props[ c.binds[j] ]; + } + + if (!ret_nodes[c.from] || !ret_nodes[c.to]) + continue; + ret_nodes[c.from]->connect( snames[ c.signal], ret_nodes[ c.to ], snames[ c.method], binds,CONNECT_PERSIST ); + } + + return ret_nodes[0]; + +} + + +static int _nm_get_string(const String& p_string, Map<StringName,int> &name_map) { + + if (name_map.has(p_string)) + return name_map[p_string]; + + int idx = name_map.size(); + name_map[p_string]=idx; + return idx; +} + +static int _vm_get_variant(const Variant& p_variant, HashMap<Variant,int,VariantHasher> &variant_map) { + + if (variant_map.has(p_variant)) + return variant_map[p_variant]; + + int idx = variant_map.size(); + variant_map[p_variant]=idx; + return idx; +} + +void ScenePreloader::_parse_node(Node *p_owner,Node *p_node,int p_parent_idx, Map<StringName,int> &name_map,HashMap<Variant,int,VariantHasher> &variant_map,Map<Node*,int> &node_map) { + + if (p_node!=p_owner && !p_node->get_owner()) + return; + + NodeData nd; + nd.name=_nm_get_string(p_node->get_name(),name_map); + nd.type=_nm_get_string(p_node->get_type(),name_map); + nd.parent=p_parent_idx; + + List<PropertyInfo> plist; + p_node->get_property_list(&plist); + for (List<PropertyInfo>::Element *E=plist.front();E;E=E->next()) { + + if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) + continue; + + NodeData::Property prop; + prop.name=_nm_get_string(E->get().name,name_map); + prop.value=_vm_get_variant( p_node->get( E->get().name ), variant_map); + nd.properties.push_back(prop); + } + + int idx = nodes.size(); + node_map[p_node]=idx; + nodes.push_back(nd); + + for(int i=0;i<p_node->get_child_count();i++) { + + Node *c=p_node->get_child(i); + _parse_node(p_owner,c,idx,name_map,variant_map,node_map); + } + + + +} + +void ScenePreloader::_parse_connections(Node *p_node, Map<StringName,int> &name_map,HashMap<Variant,int,VariantHasher> &variant_map,Map<Node*,int> &node_map,bool p_instance) { + + + List<MethodInfo> signals; + p_node->get_signal_list(&signals); + + for(List<MethodInfo>::Element *E=signals.front();E;E=E->next()) { + + List<Node::Connection> conns; + p_node->get_signal_connection_list(E->get().name,&conns); + for(List<Node::Connection>::Element *F=conns.front();F;F=F->next()) { + + const Node::Connection &c = F->get(); + if (!(c.flags&CONNECT_PERSIST)) + continue; + Node *n=c.target->cast_to<Node>(); + if (!n) + continue; + + if (!node_map.has(n)) + continue; + + ConnectionData cd; + cd.from=node_map[p_node]; + cd.to=node_map[n]; + cd.method=_nm_get_string(c.method,name_map); + cd.signal=_nm_get_string(c.signal,name_map); + for(int i=0;i<c.binds.size();i++) { + + cd.binds.push_back( _vm_get_variant(c.binds[i],variant_map)); + } + connections.push_back(cd); + } + } + +} + + +Error ScenePreloader::load_scene(const String& p_path) { + + return ERR_CANT_OPEN; +#if 0 + if (path==p_path) + return OK; + + String p=Globals::get_singleton()->localize_path(p_path); + clear(); + + Node *scene = SceneLoader::load(p); + + ERR_FAIL_COND_V(!scene,ERR_CANT_OPEN); + + path=p; + + Map<StringName,int> name_map; + HashMap<Variant,int,VariantHasher> variant_map; + Map<Node*,int> node_map; + + _parse_node(scene,scene,-1,name_map,variant_map,node_map); + + + names.resize(name_map.size()); + + for(Map<StringName,int>::Element *E=name_map.front();E;E=E->next()) { + + names[E->get()]=E->key(); + } + + variants.resize(variant_map.size()); + const Variant *K=NULL; + while((K=variant_map.next(K))) { + + int idx = variant_map[*K]; + variants[idx]=*K; + } + + + memdelete(scene); // <- me falto esto :( + return OK; +#endif +} + +String ScenePreloader::get_scene_path() const { + + return path; +} + +void ScenePreloader::clear() { + + names.clear(); + variants.clear(); + nodes.clear(); + connections.clear(); + +} + +void ScenePreloader::_set_bundled_scene(const Dictionary& d) { + + + ERR_FAIL_COND( !d.has("names")); + ERR_FAIL_COND( !d.has("variants")); + ERR_FAIL_COND( !d.has("node_count")); + ERR_FAIL_COND( !d.has("nodes")); + ERR_FAIL_COND( !d.has("conn_count")); + ERR_FAIL_COND( !d.has("conns")); + ERR_FAIL_COND( !d.has("path")); + + DVector<String> snames = d["names"]; + if (snames.size()) { + + int namecount = snames.size(); + names.resize(namecount); + DVector<String>::Read r =snames.read(); + for(int i=0;i<names.size();i++) + names[i]=r[i]; + } + + Array svariants = d["variants"]; + + if (svariants.size()) { + int varcount=svariants.size(); + variants.resize(varcount); + for(int i=0;i<varcount;i++) { + + variants[i]=svariants[i]; + } + + } else { + variants.clear(); + } + + nodes.resize(d["node_count"]); + int nc=nodes.size(); + if (nc) { + DVector<int> snodes = d["nodes"]; + DVector<int>::Read r = snodes.read(); + int idx=0; + for(int i=0;i<nc;i++) { + NodeData &nd = nodes[i]; + nd.parent=r[idx++]; + nd.type=r[idx++]; + nd.name=r[idx++]; + nd.properties.resize(r[idx++]); + for(int j=0;j<nd.properties.size();j++) { + + nd.properties[j].name=r[idx++]; + nd.properties[j].value=r[idx++]; + } + } + + } + + connections.resize(d["conn_count"]); + int cc=connections.size(); + + if (cc) { + + DVector<int> sconns = d["conns"]; + DVector<int>::Read r = sconns.read(); + int idx=0; + for(int i=0;i<nc;i++) { + ConnectionData &cd = connections[nc]; + cd.from=r[idx++]; + cd.to=r[idx++]; + cd.signal=r[idx++]; + cd.method=r[idx++]; + cd.binds.resize(r[idx++]); + for(int j=0;j<cd.binds.size();j++) { + + cd.binds[j]=r[idx++]; + } + } + + } + + + + path=d["path"]; + +} + +Dictionary ScenePreloader::_get_bundled_scene() const { + + DVector<String> rnames; + rnames.resize(names.size()); + + if (names.size()) { + + DVector<String>::Write r=rnames.write(); + + for(int i=0;i<names.size();i++) + r[i]=names[i]; + } + + Dictionary d; + d["names"]=rnames; + d["variants"]=variants; + + Vector<int> rnodes; + d["node_count"]=nodes.size(); + + for(int i=0;i<nodes.size();i++) { + + const NodeData &nd=nodes[i]; + rnodes.push_back(nd.parent); + rnodes.push_back(nd.type); + rnodes.push_back(nd.name); + rnodes.push_back(nd.properties.size()); + for(int j=0;j<nd.properties.size();j++) { + + rnodes.push_back(nd.properties[j].name); + rnodes.push_back(nd.properties[j].value); + } + } + + d["nodes"]=rnodes; + + Vector<int> rconns; + d["conn_count"]=connections.size(); + + for(int i=0;i<connections.size();i++) { + + const ConnectionData &cd=connections[i]; + rconns.push_back(cd.from); + rconns.push_back(cd.to); + rconns.push_back(cd.signal); + rconns.push_back(cd.method); + rconns.push_back(cd.binds.size()); + for(int j=0;j<cd.binds.size();i++) + rconns.push_back(cd.binds[j]); + + } + + d["conns"]=rconns; + + d["path"]=path; + + return d; + + +} + +void ScenePreloader::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("load_scene","path"),&ScenePreloader::load_scene); + ObjectTypeDB::bind_method(_MD("get_scene_path"),&ScenePreloader::get_scene_path); + ObjectTypeDB::bind_method(_MD("instance:Node"),&ScenePreloader::instance); + ObjectTypeDB::bind_method(_MD("can_instance"),&ScenePreloader::can_instance); + ObjectTypeDB::bind_method(_MD("_set_bundled_scene"),&ScenePreloader::_set_bundled_scene); + ObjectTypeDB::bind_method(_MD("_get_bundled_scene"),&ScenePreloader::_get_bundled_scene); + + ADD_PROPERTY( PropertyInfo(Variant::DICTIONARY,"_bundled",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_BUNDLE),_SCS("_set_bundled_scene"),_SCS("_get_bundled_scene")); +#if 0 + List<String> extensions; + SceneLoader::get_recognized_extensions(&extensions); + String exthint; + for (List<String>::Element*E=extensions.front();E;E=E->next()) { + + if (exthint!="") + exthint+=","; + exthint+="*."+E->get(); + } + + exthint+="; Scenes"; + + ADD_PROPERTY( PropertyInfo(Variant::STRING,"scene",PROPERTY_HINT_FILE,exthint),_SCS("load_scene"),_SCS("get_scene_path")); +#endif +} + +ScenePreloader::ScenePreloader() { + + +} diff --git a/scene/resources/shader_graph.cpp b/scene/resources/shader_graph.cpp index df5fee81a8..3ed6cebf07 100644 --- a/scene/resources/shader_graph.cpp +++ b/scene/resources/shader_graph.cpp @@ -148,8 +148,8 @@ void ShaderGraph::_bind_methods() { ObjectTypeDB::bind_method(_MD("node_set_pos"),&ShaderGraph::node_set_pos ); ObjectTypeDB::bind_method(_MD("node_get_pos"),&ShaderGraph::node_get_pos ); - ObjectTypeDB::bind_method(_MD("node_get_param"),&ShaderGraph::node_get_type); - ObjectTypeDB::bind_method(_MD("node_get_type"),&ShaderGraph::node_get_param); + ObjectTypeDB::bind_method(_MD("node_get_param"),&ShaderGraph::node_get_param); + ObjectTypeDB::bind_method(_MD("node_get_type"),&ShaderGraph::node_get_type); ObjectTypeDB::bind_method(_MD("connect"),&ShaderGraph::connect ); ObjectTypeDB::bind_method(_MD("disconnect"),&ShaderGraph::disconnect ); diff --git a/scene/resources/shape_2d.cpp b/scene/resources/shape_2d.cpp index ca891920da..738b642d43 100644 --- a/scene/resources/shape_2d.cpp +++ b/scene/resources/shape_2d.cpp @@ -109,7 +109,7 @@ void Shape2D::_bind_methods() { ObjectTypeDB::bind_method(_MD("collide","local_xform","with_shape:Shape2D","shape_xform"),&Shape2D::collide); ObjectTypeDB::bind_method(_MD("collide_with_motion","local_xform","local_motion","with_shape:Shape2D","shape_xform","shape_motion"),&Shape2D::collide_with_motion); ObjectTypeDB::bind_method(_MD("collide_and_get_contacts:var","local_xform","with_shape:Shape2D","shape_xform"),&Shape2D::collide_and_get_contacts); - ObjectTypeDB::bind_method(_MD("collide_with_motion_and_get_contacts:var","local_xform","local_motion","with_shape:Shape2D","shape_xform","shape_motion"),&Shape2D::collide_and_get_contacts); + ObjectTypeDB::bind_method(_MD("collide_with_motion_and_get_contacts:var","local_xform","local_motion","with_shape:Shape2D","shape_xform","shape_motion"),&Shape2D::collide_with_motion_and_get_contacts); ADD_PROPERTY( PropertyInfo(Variant::REAL,"custom_solver_bias",PROPERTY_HINT_RANGE,"0,1,0.001"),_SCS("set_custom_solver_bias"),_SCS("get_custom_solver_bias")); } diff --git a/scene/resources/surface_tool.cpp b/scene/resources/surface_tool.cpp index 2856101674..dd39205932 100644 --- a/scene/resources/surface_tool.cpp +++ b/scene/resources/surface_tool.cpp @@ -105,7 +105,7 @@ void SurfaceTool::add_vertex( const Vector3& p_vertex) { vtx.weights=last_weights; vtx.bones=last_bones; vtx.tangent=last_tangent.normal; - vtx.binormal=last_tangent.normal.cross(last_normal).normalized() * last_tangent.d; + vtx.binormal=last_normal.cross(last_tangent.normal).normalized() * last_tangent.d; vertex_array.push_back(vtx); first=false; format|=Mesh::ARRAY_FORMAT_VERTEX; @@ -299,7 +299,9 @@ Ref<Mesh> SurfaceTool::commit(const Ref<Mesh>& p_existing) { w[idx+0]=v.tangent.x; w[idx+1]=v.tangent.y; w[idx+2]=v.tangent.z; - float d = v.binormal.dot(v.normal.cross(v.tangent)); + + //float d = v.tangent.dot(v.binormal,v.normal); + float d = v.binormal.dot( v.normal.cross(v.tangent)); w[idx+3]=d<0 ? -1 : 1; } @@ -565,6 +567,7 @@ void SurfaceTool::create_from(const Ref<Mesh>& p_existing, int p_surface) { clear(); primitive=p_existing->surface_get_primitive_type(p_surface); _create_list(p_existing,p_surface,&vertex_array,&index_array,format); + material=p_existing->surface_get_material(p_surface); } @@ -611,165 +614,96 @@ void SurfaceTool::append_from(const Ref<Mesh>& p_existing, int p_surface,const T } +//mikktspace callbacks +int SurfaceTool::mikktGetNumFaces(const SMikkTSpaceContext * pContext) { -void SurfaceTool::generate_tangents() { - - ERR_FAIL_COND(!(format&Mesh::ARRAY_FORMAT_TEX_UV)); - ERR_FAIL_COND(!(format&Mesh::ARRAY_FORMAT_NORMAL)); - - - if (index_array.size()) { - - Vector<List<Vertex>::Element*> vtx; - vtx.resize(vertex_array.size()); - int idx=0; - for (List<Vertex>::Element *E=vertex_array.front();E;E=E->next()) { - vtx[idx++]=E; - E->get().binormal=Vector3(); - E->get().tangent=Vector3(); - } - - for (List<int>::Element *E=index_array.front();E;) { - - int i[3]; - i[0]=E->get(); - E=E->next(); - ERR_FAIL_COND(!E); - i[1]=E->get(); - E=E->next(); - ERR_FAIL_COND(!E); - i[2]=E->get(); - E=E->next(); - ERR_FAIL_COND(!E); - - - Vector3 v1 = vtx[ i[0] ]->get().vertex; - Vector3 v2 = vtx[ i[1] ]->get().vertex; - Vector3 v3 = vtx[ i[2] ]->get().vertex; - - Vector2 w1 = vtx[ i[0] ]->get().uv; - Vector2 w2 = vtx[ i[1] ]->get().uv; - Vector2 w3 = vtx[ i[2] ]->get().uv; - - - float x1 = v2.x - v1.x; - float x2 = v3.x - v1.x; - float y1 = v2.y - v1.y; - float y2 = v3.y - v1.y; - float z1 = v2.z - v1.z; - float z2 = v3.z - v1.z; - - float s1 = w2.x - w1.x; - float s2 = w3.x - w1.x; - float t1 = w2.y - w1.y; - float t2 = w3.y - w1.y; - - float r = (s1 * t2 - s2 * t1); - - Vector3 binormal,tangent; - - if (r==0) { - binormal=Vector3(0,0,0); - tangent=Vector3(0,0,0); - } else { - tangent = Vector3((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, - (t2 * z1 - t1 * z2) * r); - binormal = Vector3((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, - (s1 * z2 - s2 * z1) * r); - } - - tangent.normalize(); - binormal.normalize(); - Vector3 normal=Plane( v1, v2, v3 ).normal; - - Vector3 tangentp = tangent - normal * normal.dot( tangent ); - Vector3 binormalp = binormal - normal * (normal.dot(binormal)) - tangent * (tangent.dot(binormal)); - - tangentp.normalize(); - binormalp.normalize(); + Vector<List<Vertex>::Element*> &varr = *((Vector<List<Vertex>::Element*>*)pContext->m_pUserData); + return varr.size()/3; +} +int SurfaceTool::mikktGetNumVerticesOfFace(const SMikkTSpaceContext * pContext, const int iFace){ + return 3; //always 3 +} +void SurfaceTool::mikktGetPosition(const SMikkTSpaceContext * pContext, float fvPosOut[], const int iFace, const int iVert){ - for (int j=0;j<3;j++) { - vtx[ i[j] ]->get().binormal+=binormalp; - vtx[ i[j] ]->get().tangent+=tangentp; + Vector<List<Vertex>::Element*> &varr = *((Vector<List<Vertex>::Element*>*)pContext->m_pUserData); + Vector3 v = varr[iFace*3+iVert]->get().vertex; + fvPosOut[0]=v.x; + fvPosOut[1]=v.y; + fvPosOut[2]=v.z; - } - } - for (List<Vertex>::Element *E=vertex_array.front();E;E=E->next()) { - E->get().binormal.normalize(); - E->get().tangent.normalize(); - } +} +void SurfaceTool::mikktGetNormal(const SMikkTSpaceContext * pContext, float fvNormOut[], const int iFace, const int iVert){ - } else { + Vector<List<Vertex>::Element*> &varr = *((Vector<List<Vertex>::Element*>*)pContext->m_pUserData); + Vector3 v = varr[iFace*3+iVert]->get().normal; + fvNormOut[0]=v.x; + fvNormOut[1]=v.y; + fvNormOut[2]=v.z; - for (List<Vertex>::Element *E=vertex_array.front();E;) { +} +void SurfaceTool::mikktGetTexCoord(const SMikkTSpaceContext * pContext, float fvTexcOut[], const int iFace, const int iVert){ - List< Vertex >::Element *v[3]; - v[0]=E; - v[1]=v[0]->next(); - ERR_FAIL_COND(!v[1]); - v[2]=v[1]->next(); - ERR_FAIL_COND(!v[2]); - E=v[2]->next(); + Vector<List<Vertex>::Element*> &varr = *((Vector<List<Vertex>::Element*>*)pContext->m_pUserData); + Vector2 v = varr[iFace*3+iVert]->get().uv; + fvTexcOut[0]=v.x; + //fvTexcOut[1]=v.y; + fvTexcOut[1]=1.0-v.y; - Vector3 v1 = v[0]->get().vertex; - Vector3 v2 = v[1]->get().vertex; - Vector3 v3 = v[2]->get().vertex; +} +void SurfaceTool::mikktSetTSpaceBasic(const SMikkTSpaceContext * pContext, const float fvTangent[], const float fSign, const int iFace, const int iVert){ - Vector2 w1 = v[0]->get().uv; - Vector2 w2 = v[1]->get().uv; - Vector2 w3 = v[2]->get().uv; + Vector<List<Vertex>::Element*> &varr = *((Vector<List<Vertex>::Element*>*)pContext->m_pUserData); + Vertex &vtx = varr[iFace*3+iVert]->get(); + vtx.tangent = Vector3(fvTangent[0],fvTangent[1],fvTangent[2]); + vtx.binormal = vtx.normal.cross(vtx.tangent) * fSign; +} - float x1 = v2.x - v1.x; - float x2 = v3.x - v1.x; - float y1 = v2.y - v1.y; - float y2 = v3.y - v1.y; - float z1 = v2.z - v1.z; - float z2 = v3.z - v1.z; - float s1 = w2.x - w1.x; - float s2 = w3.x - w1.x; - float t1 = w2.y - w1.y; - float t2 = w3.y - w1.y; +void SurfaceTool::generate_tangents() { - float r = (s1 * t2 - s2 * t1); + ERR_FAIL_COND(!(format&Mesh::ARRAY_FORMAT_TEX_UV)); + ERR_FAIL_COND(!(format&Mesh::ARRAY_FORMAT_NORMAL)); - Vector3 binormal,tangent; + bool indexed = index_array.size()>0; + if (indexed) + deindex(); - if (r==0) { - binormal=Vector3(0,0,0); - tangent=Vector3(0,0,0); - } else { - tangent = Vector3((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, - (t2 * z1 - t1 * z2) * r); - binormal = Vector3((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, - (s1 * z2 - s2 * z1) * r); - } - tangent.normalize(); - binormal.normalize(); - Vector3 normal=Plane( v1, v2, v3 ).normal; + SMikkTSpaceInterface mkif; + mkif.m_getNormal=mikktGetNormal; + mkif.m_getNumFaces=mikktGetNumFaces; + mkif.m_getNumVerticesOfFace=mikktGetNumVerticesOfFace; + mkif.m_getPosition=mikktGetPosition; + mkif.m_getTexCoord=mikktGetTexCoord; + mkif.m_setTSpaceBasic=mikktSetTSpaceBasic; + mkif.m_setTSpace=NULL; - Vector3 tangentp = tangent - normal * normal.dot( tangent ); - Vector3 binormalp = binormal - normal * (normal.dot(binormal)) - tangent * (tangent.dot(binormal)); + SMikkTSpaceContext msc; + msc.m_pInterface=&mkif; - tangentp.normalize(); - binormalp.normalize(); + Vector<List<Vertex>::Element*> vtx; + vtx.resize(vertex_array.size()); + int idx=0; + for (List<Vertex>::Element *E=vertex_array.front();E;E=E->next()) { + vtx[idx++]=E; + E->get().binormal=Vector3(); + E->get().tangent=Vector3(); + } + msc.m_pUserData=&vtx; + bool res = genTangSpaceDefault(&msc); - for (int j=0;j<3;j++) { - v[j]->get().binormal=binormalp; - v[j]->get().tangent=tangentp; + ERR_FAIL_COND(!res); + format|=Mesh::ARRAY_FORMAT_TANGENT; - } - } - } + if (indexed) + index(); - format|=Mesh::ARRAY_FORMAT_TANGENT; } diff --git a/scene/resources/surface_tool.h b/scene/resources/surface_tool.h index fe82d3a4ce..fc5940145b 100644 --- a/scene/resources/surface_tool.h +++ b/scene/resources/surface_tool.h @@ -30,7 +30,7 @@ #define SURFACE_TOOL_H #include "scene/resources/mesh.h" - +#include "mikktspace.h" class SurfaceTool : public Reference { @@ -82,6 +82,14 @@ private: void _create_list(const Ref<Mesh>& p_existing, int p_surface, List<Vertex> *r_vertex, List<int> *r_index,int &lformat); + + //mikktspace callbacks + static int mikktGetNumFaces(const SMikkTSpaceContext * pContext); + static int mikktGetNumVerticesOfFace(const SMikkTSpaceContext * pContext, const int iFace); + static void mikktGetPosition(const SMikkTSpaceContext * pContext, float fvPosOut[], const int iFace, const int iVert); + static void mikktGetNormal(const SMikkTSpaceContext * pContext, float fvNormOut[], const int iFace, const int iVert); + static void mikktGetTexCoord(const SMikkTSpaceContext * pContext, float fvTexcOut[], const int iFace, const int iVert); + static void mikktSetTSpaceBasic(const SMikkTSpaceContext * pContext, const float fvTangent[], const float fSign, const int iFace, const int iVert); protected: static void _bind_methods(); diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp index 5b31ba1f1b..dae055890b 100644 --- a/scene/resources/texture.cpp +++ b/scene/resources/texture.cpp @@ -79,6 +79,8 @@ void Texture::_bind_methods() { BIND_CONSTANT( FLAG_FILTER ); BIND_CONSTANT( FLAG_VIDEO_SURFACE ); BIND_CONSTANT( FLAGS_DEFAULT ); + BIND_CONSTANT( FLAG_ANISOTROPIC_FILTER ); + BIND_CONSTANT( FLAG_CONVERT_TO_LINEAR ); } @@ -179,7 +181,7 @@ void ImageTexture::_get_property_list( List<PropertyInfo> *p_list) const { - p_list->push_back( PropertyInfo( Variant::INT, "flags", PROPERTY_HINT_FLAGS,"Mipmaps,Repeat,Filter") ); + p_list->push_back( PropertyInfo( Variant::INT, "flags", PROPERTY_HINT_FLAGS,"Mipmaps,Repeat,Filter,Anisotropic,sRGB") ); p_list->push_back( PropertyInfo( Variant::IMAGE, "image", img_hint,String::num(lossy_storage_quality)) ); p_list->push_back( PropertyInfo( Variant::VECTOR2, "size",PROPERTY_HINT_NONE, "")); p_list->push_back( PropertyInfo( Variant::INT, "storage", PROPERTY_HINT_ENUM,"Uncompressed,Compress Lossy,Compress Lossless")); diff --git a/scene/resources/texture.h b/scene/resources/texture.h index 86ff246498..4bb2f6d979 100644 --- a/scene/resources/texture.h +++ b/scene/resources/texture.h @@ -52,6 +52,8 @@ public: FLAG_MIPMAPS=VisualServer::TEXTURE_FLAG_MIPMAPS, FLAG_REPEAT=VisualServer::TEXTURE_FLAG_REPEAT, FLAG_FILTER=VisualServer::TEXTURE_FLAG_FILTER, + FLAG_ANISOTROPIC_FILTER=VisualServer::TEXTURE_FLAG_ANISOTROPIC_FILTER, + FLAG_CONVERT_TO_LINEAR=VisualServer::TEXTURE_FLAG_CONVERT_TO_LINEAR, FLAG_VIDEO_SURFACE=VisualServer::TEXTURE_FLAG_VIDEO_SURFACE, FLAGS_DEFAULT=FLAG_MIPMAPS|FLAG_REPEAT|FLAG_FILTER, }; |