diff options
Diffstat (limited to 'tools/editor/plugins/baked_light_baker.cpp')
-rw-r--r-- | tools/editor/plugins/baked_light_baker.cpp | 1765 |
1 files changed, 1765 insertions, 0 deletions
diff --git a/tools/editor/plugins/baked_light_baker.cpp b/tools/editor/plugins/baked_light_baker.cpp new file mode 100644 index 0000000000..ae5746321a --- /dev/null +++ b/tools/editor/plugins/baked_light_baker.cpp @@ -0,0 +1,1765 @@ + +#include "baked_light_baker.h" +#include <stdlib.h> +#include <cmath> +#include "io/marshalls.h" +#include "tools/editor/editor_node.h" + + +BakedLightBaker::MeshTexture* BakedLightBaker::_get_mat_tex(const Ref<Texture>& p_tex) { + + if (!tex_map.has(p_tex)) { + + Ref<ImageTexture> imgtex=p_tex; + if (imgtex.is_null()) + return NULL; + Image image=imgtex->get_data(); + if (image.empty()) + return NULL; + + if (image.get_format()!=Image::FORMAT_RGBA) { + if (image.get_format()>Image::FORMAT_INDEXED_ALPHA) { + Error err = image.decompress(); + if (err) + return NULL; + } + + if (image.get_format()!=Image::FORMAT_RGBA) + image.convert(Image::FORMAT_RGBA); + } + + DVector<uint8_t> dvt=image.get_data(); + DVector<uint8_t>::Read r=dvt.read(); + MeshTexture mt; + mt.tex_w=image.get_width(); + mt.tex_h=image.get_height(); + int len = image.get_width()*image.get_height()*4; + mt.tex.resize(len); + copymem(mt.tex.ptr(),r.ptr(),len); + + textures.push_back(mt); + tex_map[p_tex]=&textures.back()->get(); + } + + return tex_map[p_tex]; +} + + +void BakedLightBaker::_add_mesh(const Ref<Mesh>& p_mesh,const Ref<Material>& p_mat_override,const Transform& p_xform) { + + + for(int i=0;i<p_mesh->get_surface_count();i++) { + + if (p_mesh->surface_get_primitive_type(i)!=Mesh::PRIMITIVE_TRIANGLES) + continue; + Ref<Material> mat = p_mat_override.is_valid()?p_mat_override:p_mesh->surface_get_material(i); + + MeshMaterial *matptr=NULL; + + if (mat.is_valid()) { + + if (!mat_map.has(mat)) { + + MeshMaterial mm; + + Ref<FixedMaterial> fm = mat; + if (fm.is_valid()) { + //fixed route + mm.diffuse.color=fm->get_parameter(FixedMaterial::PARAM_DIFFUSE); + mm.diffuse.tex=_get_mat_tex(fm->get_texture(FixedMaterial::PARAM_DIFFUSE)); + mm.specular.color=fm->get_parameter(FixedMaterial::PARAM_SPECULAR); + mm.specular.tex=_get_mat_tex(fm->get_texture(FixedMaterial::PARAM_SPECULAR)); + } else { + + mm.diffuse.color=Color(1,1,1,1); + mm.diffuse.tex=NULL; + mm.specular.color=Color(0,0,0,1); + mm.specular.tex=NULL; + } + + materials.push_back(mm); + mat_map[mat]=&materials.back()->get(); + + } + + matptr=mat_map[mat]; + + } + + + int facecount=0; + + + if (p_mesh->surface_get_format(i)&Mesh::ARRAY_FORMAT_INDEX) { + + facecount=p_mesh->surface_get_array_index_len(i); + } else { + + facecount=p_mesh->surface_get_array_len(i); + } + + ERR_CONTINUE((facecount==0 || (facecount%3)!=0)); + + facecount/=3; + + int tbase=triangles.size(); + triangles.resize(facecount+tbase); + + + Array a = p_mesh->surface_get_arrays(i); + + DVector<Vector3> vertices = a[Mesh::ARRAY_VERTEX]; + DVector<Vector3>::Read vr=vertices.read(); + DVector<Vector2> uv; + DVector<Vector2>::Read uvr; + DVector<Vector3> normal; + DVector<Vector3>::Read normalr; + bool read_uv=false; + bool read_normal=false; + + if (p_mesh->surface_get_format(i)&Mesh::ARRAY_FORMAT_TEX_UV) { + + uv=a[Mesh::ARRAY_TEX_UV]; + uvr=uv.read(); + read_uv=true; + } + + if (p_mesh->surface_get_format(i)&Mesh::ARRAY_FORMAT_NORMAL) { + + normal=a[Mesh::ARRAY_NORMAL]; + normalr=normal.read(); + read_normal=true; + } + + Matrix3 normal_xform = p_xform.basis.inverse().transposed(); + + + if (p_mesh->surface_get_format(i)&Mesh::ARRAY_FORMAT_INDEX) { + + DVector<int> indices = a[Mesh::ARRAY_INDEX]; + DVector<int>::Read ir = indices.read(); + + for(int i=0;i<facecount;i++) { + Triangle &t=triangles[tbase+i]; + t.vertices[0]=p_xform.xform(vr[ ir[i*3+0] ]); + t.vertices[1]=p_xform.xform(vr[ ir[i*3+1] ]); + t.vertices[2]=p_xform.xform(vr[ ir[i*3+2] ]); + t.material=matptr; + if (read_uv) { + + t.uvs[0]=uvr[ ir[i*3+0] ]; + t.uvs[1]=uvr[ ir[i*3+1] ]; + t.uvs[2]=uvr[ ir[i*3+2] ]; + } + if (read_normal) { + + t.normals[0]=normal_xform.xform(normalr[ ir[i*3+0] ]).normalized(); + t.normals[1]=normal_xform.xform(normalr[ ir[i*3+1] ]).normalized(); + t.normals[2]=normal_xform.xform(normalr[ ir[i*3+2] ]).normalized(); + } + } + + } else { + + for(int i=0;i<facecount;i++) { + Triangle &t=triangles[tbase+i]; + t.vertices[0]=p_xform.xform(vr[ i*3+0 ]); + t.vertices[1]=p_xform.xform(vr[ i*3+1 ]); + t.vertices[2]=p_xform.xform(vr[ i*3+2 ]); + t.material=matptr; + if (read_uv) { + + t.uvs[0]=uvr[ i*3+0 ]; + t.uvs[1]=uvr[ i*3+1 ]; + t.uvs[2]=uvr[ i*3+2 ]; + } + if (read_normal) { + + t.normals[0]=normal_xform.xform(normalr[ i*3+0 ]).normalized(); + t.normals[1]=normal_xform.xform(normalr[ i*3+1 ]).normalized(); + t.normals[2]=normal_xform.xform(normalr[ i*3+2 ]).normalized(); + } + } + } + } + +} + + +void BakedLightBaker::_parse_geometry(Node* p_node) { + + if (p_node->cast_to<MeshInstance>()) { + + MeshInstance *meshi=p_node->cast_to<MeshInstance>(); + Ref<Mesh> mesh=meshi->get_mesh(); + if (mesh.is_valid()) { + _add_mesh(mesh,meshi->get_material_override(),base_inv * meshi->get_global_transform()); + } + } else if (p_node->cast_to<Light>()) { + + Light *dl=p_node->cast_to<Light>(); + + if (dl->get_bake_mode()!=Light::BAKE_MODE_DISABLED) { + + + LightData dirl; + dirl.type=VS::LightType(dl->get_light_type()); + dirl.diffuse=dl->get_color(DirectionalLight::COLOR_DIFFUSE); + dirl.specular=dl->get_color(DirectionalLight::COLOR_SPECULAR); + dirl.energy=dl->get_parameter(DirectionalLight::PARAM_ENERGY); + dirl.pos=dl->get_global_transform().origin; + dirl.up=dl->get_global_transform().basis.get_axis(1).normalized(); + dirl.left=dl->get_global_transform().basis.get_axis(0).normalized(); + dirl.dir=-dl->get_global_transform().basis.get_axis(2).normalized(); + dirl.spot_angle=dl->get_parameter(DirectionalLight::PARAM_SPOT_ANGLE); + dirl.spot_attenuation=dl->get_parameter(DirectionalLight::PARAM_SPOT_ATTENUATION); + dirl.attenuation=dl->get_parameter(DirectionalLight::PARAM_ATTENUATION); + dirl.radius=dl->get_parameter(DirectionalLight::PARAM_RADIUS); + dirl.bake_direct=dl->get_bake_mode()==Light::BAKE_MODE_FULL; + dirl.rays_thrown=0; + lights.push_back(dirl); + } + + } else if (p_node->cast_to<Spatial>()){ + + Spatial *sp = p_node->cast_to<Spatial>(); + + Array arr = p_node->call("_get_baked_light_meshes"); + for(int i=0;i<arr.size();i+=2) { + + Transform xform=arr[i]; + Ref<Mesh> mesh=arr[i+1]; + _add_mesh(mesh,Ref<Material>(),base_inv * (sp->get_global_transform() * xform)); + } + } + + for(int i=0;i<p_node->get_child_count();i++) { + + _parse_geometry(p_node->get_child(i)); + } +} + + +void BakedLightBaker::_fix_lights() { + + + total_light_area=0; + for(int i=0;i<lights.size();i++) { + + LightData &dl=lights[i]; + + switch(dl.type) { + case VS::LIGHT_DIRECTIONAL: { + + float up_max=-1e10; + float dir_max=-1e10; + float left_max=-1e10; + float up_min=1e10; + float dir_min=1e10; + float left_min=1e10; + + for(int j=0;j<triangles.size();j++) { + + for(int k=0;k<3;k++) { + + Vector3 v = triangles[j].vertices[k]; + + float up_d = dl.up.dot(v); + float dir_d = dl.dir.dot(v); + float left_d = dl.left.dot(v); + + if (up_d>up_max) + up_max=up_d; + if (up_d<up_min) + up_min=up_d; + + if (left_d>left_max) + left_max=left_d; + if (left_d<left_min) + left_min=left_d; + + if (dir_d>dir_max) + dir_max=dir_d; + if (dir_d<dir_min) + dir_min=dir_d; + + } + } + + //make a center point, then the upvector and leftvector + dl.pos = dl.left*( left_max+left_min )*0.5 + dl.up*( up_max+up_min )*0.5 + dl.dir*(dir_min-(dir_max-dir_min)); + dl.left*=(left_max-left_min)*0.5; + dl.up*=(up_max-up_min)*0.5; + dl.length = (dir_max - dir_min)*10; //arbitrary number to keep it in scale + dl.area=dl.left.length()*2*dl.up.length()*2; + dl.constant=1.0/dl.area; + } break; + case VS::LIGHT_OMNI: + case VS::LIGHT_SPOT: { + + dl.attenuation_table.resize(ATTENUATION_CURVE_LEN); + for(int j=0;j<ATTENUATION_CURVE_LEN;j++) { + dl.attenuation_table[j]=1.0-Math::pow(j/float(ATTENUATION_CURVE_LEN),dl.attenuation); + float falloff=j*dl.radius/float(ATTENUATION_CURVE_LEN); + if (falloff==0) + falloff=0.000001; + float intensity=4*Math_PI*(falloff*falloff); + //dl.attenuation_table[j]*=falloff*falloff; + dl.attenuation_table[j]*=1.0/(3.0/intensity); + + } + if (dl.type==VS::LIGHT_OMNI) { + + dl.area=4.0*Math_PI*pow(dl.radius,2.0); + dl.constant=1.0/3.5; + } else { + + + float r = Math::tan(Math::deg2rad(dl.spot_angle))*dl.radius; + float c = 1.0-(Math::deg2rad(dl.spot_angle)*0.5+0.5); + dl.constant=1.0/3.5; + dl.constant*=1.0/c; + + dl.area=Math_PI*r*r*c; + } + + } break; + + + } + + total_light_area+=dl.area; + } +} + +BakedLightBaker::BVH* BakedLightBaker::_parse_bvh(BVH** p_children, int p_size, int p_depth, int &max_depth) { + + if (p_depth>max_depth) { + max_depth=p_depth; + } + + if (p_size==1) { + + return p_children[0]; + } else if (p_size==0) { + + return NULL; + } + + + AABB aabb; + aabb=p_children[0]->aabb; + for(int i=1;i<p_size;i++) { + + aabb.merge_with(p_children[i]->aabb); + } + + int li=aabb.get_longest_axis_index(); + + switch(li) { + + case Vector3::AXIS_X: { + SortArray<BVH*,BVHCmpX> sort_x; + sort_x.nth_element(0,p_size,p_size/2,p_children); + //sort_x.sort(&p_bb[p_from],p_size); + } break; + case Vector3::AXIS_Y: { + SortArray<BVH*,BVHCmpY> sort_y; + sort_y.nth_element(0,p_size,p_size/2,p_children); + //sort_y.sort(&p_bb[p_from],p_size); + } break; + case Vector3::AXIS_Z: { + SortArray<BVH*,BVHCmpZ> sort_z; + sort_z.nth_element(0,p_size,p_size/2,p_children); + //sort_z.sort(&p_bb[p_from],p_size); + + } break; + } + + + BVH* left = _parse_bvh(p_children,p_size/2,p_depth+1,max_depth); + BVH* right = _parse_bvh(&p_children[p_size/2],p_size-p_size/2,p_depth+1,max_depth); + + BVH *_new = memnew(BVH); + _new->aabb=aabb; + _new->center=aabb.pos+aabb.size*0.5; + _new->children[0]=left; + _new->children[1]=right; + _new->leaf=NULL; + + return _new; +} + +void BakedLightBaker::_make_bvh() { + + Vector<BVH*> bases; + bases.resize(triangles.size()); + int max_depth=0; + for(int i=0;i<triangles.size();i++) { + bases[i]=memnew( BVH ); + bases[i]->leaf=&triangles[i]; + bases[i]->aabb.pos=triangles[i].vertices[0]; + bases[i]->aabb.expand_to(triangles[i].vertices[1]); + bases[i]->aabb.expand_to(triangles[i].vertices[2]); + triangles[i].aabb=bases[i]->aabb; + bases[i]->center=bases[i]->aabb.pos+bases[i]->aabb.size*0.5; + } + + bvh=_parse_bvh(bases.ptr(),bases.size(),1,max_depth); + ray_stack = memnew_arr(uint32_t,max_depth); + bvh_stack = memnew_arr(BVH*,max_depth); +} + +void BakedLightBaker::_octree_insert(int p_octant,Triangle* p_triangle, int p_depth) { + + + + + uint32_t *stack=octant_stack; + uint32_t *ptr_stack=octantptr_stack; + Octant *octants=octant_pool.ptr(); + + stack[0]=0; + ptr_stack[0]=0; + + int stack_pos=0; + + + while(true) { + + Octant *octant=&octants[ptr_stack[stack_pos]]; + if (stack[stack_pos]<8) { + + int i = stack[stack_pos]; + stack[stack_pos]++; + + + + //fit_aabb=fit_aabb.grow(bvh->aabb.size.x*0.0001); + + int child_idx =octant->children[i]; + bool encloses; + if (!child_idx) { + + AABB aabb=octant->aabb; + aabb.size*=0.5; + if (i&1) + aabb.pos.x+=aabb.size.x; + if (i&2) + aabb.pos.y+=aabb.size.y; + if (i&4) + aabb.pos.z+=aabb.size.z; + + aabb.grow_by(cell_size*octree_extra_margin); + if (!aabb.intersects(p_triangle->aabb)) + continue; + encloses=aabb.grow(cell_size*-octree_extra_margin*2.0).encloses(p_triangle->aabb); + if (!encloses && !Face3(p_triangle->vertices[0],p_triangle->vertices[1],p_triangle->vertices[2]).intersects_aabb2(aabb)) + continue; + } else { + + Octant *child=&octants[child_idx]; + AABB aabb=child->aabb; + aabb.grow_by(cell_size*octree_extra_margin); + if (!aabb.intersects(p_triangle->aabb)) + continue; + encloses=aabb.grow(cell_size*-octree_extra_margin*2.0).encloses(p_triangle->aabb); + if (!encloses && !Face3(p_triangle->vertices[0],p_triangle->vertices[1],p_triangle->vertices[2]).intersects_aabb2(aabb)) + continue; + + } + + if (encloses) + stack[stack_pos]=8; // quick and dirty opt + + if (!child_idx) { + + + if (octant_pool_size==octant_pool.size()) { + octant_pool.resize(octant_pool_size+OCTANT_POOL_CHUNK); + octants=octant_pool.ptr(); + octant=&octants[ptr_stack[stack_pos]]; + } + child_idx=octant_pool_size++; + octant->children[i]=child_idx; + Octant *child=&octants[child_idx]; + + child->aabb=octant->aabb; + child->texture_x=0; + child->texture_y=0; + + child->aabb.size*=0.5; + if (i&1) + child->aabb.pos.x+=child->aabb.size.x; + if (i&2) + child->aabb.pos.y+=child->aabb.size.y; + if (i&4) + child->aabb.pos.z+=child->aabb.size.z; + + + + if (stack_pos==octree_depth-1) { + child->leaf=true; + child->offset[0]=child->aabb.pos.x+child->aabb.size.x*0.5; + child->offset[1]=child->aabb.pos.y+child->aabb.size.y*0.5; + child->offset[2]=child->aabb.pos.z+child->aabb.size.z*0.5; + child->next_leaf=leaf_list; + + for(int ci=0;ci<8;ci++) { + child->normal_accum[ci][0]=0; + child->normal_accum[ci][1]=0; + child->normal_accum[ci][2]=0; + } + + child->bake_neighbour=0; + child->first_neighbour=true; + leaf_list=child_idx; + cell_count++; + + int lz = lights.size(); + child->light = memnew_arr(OctantLight,lz); + + for(int li=0;li<lz;li++) { + for(int ci=0;ci<8;ci++) { + child->light[li].accum[ci][0]=0; + child->light[li].accum[ci][1]=0; + child->light[li].accum[ci][2]=0; + } + } + + child->parent=ptr_stack[stack_pos]; + + } else { + + child->leaf=false; + for(int j=0;j<8;j++) { + child->children[j]=0; + } + } + } + + if (!octants[child_idx].leaf) { + stack_pos++; + stack[stack_pos]=0; + ptr_stack[stack_pos]=child_idx; + } else { + + Octant *child=&octants[child_idx]; + + Vector3 n = Plane(p_triangle->vertices[0],p_triangle->vertices[1],p_triangle->vertices[2]).normal; + + + for(int ci=0;ci<8;ci++) { + + Vector3 pos = child->aabb.pos; + + if (ci&1) + pos.x+=child->aabb.size.x; + if (ci&2) + pos.y+=child->aabb.size.y; + if (ci&4) + pos.z+=child->aabb.size.z; + + + pos.x=floor((pos.x+cell_size*0.5)/cell_size); + pos.y=floor((pos.y+cell_size*0.5)/cell_size); + pos.z=floor((pos.z+cell_size*0.5)/cell_size); + + Map<Vector3,Vector3>::Element *E=endpoint_normal.find(pos); + if (!E) { + endpoint_normal[pos]=n; + } else { + E->get()+=n; + } + + } + + } + + + } else { + stack_pos--; + if (stack_pos<0) + break; + } + } + + +} + + +void BakedLightBaker::_make_octree() { + + + AABB base = bvh->aabb; + float lal=base.get_longest_axis_size(); + //must be square because we want square blocks + base.size.x=lal; + base.size.y=lal; + base.size.z=lal; + base.grow_by(lal*0.001); //for precision + octree_aabb=base; + + cell_size=base.size.x; + for(int i=0;i<octree_depth;i++) + cell_size/=2.0; + octant_stack = memnew_arr(uint32_t,octree_depth*2 ); + octantptr_stack = memnew_arr(uint32_t,octree_depth*2 ); + + octant_pool.resize(OCTANT_POOL_CHUNK); + octant_pool_size=1; + Octant *root=octant_pool.ptr(); + root->leaf=false; + root->aabb=octree_aabb; + root->parent=-1; + for(int i=0;i<8;i++) + root->children[i]=0; + + EditorProgress ep("bake_octree","Parsing "+itos(triangles.size())+" Triangles:",triangles.size()); + + for(int i=0;i<triangles.size();i++) { + + _octree_insert(0,&triangles[i],octree_depth-1); + if ((i%1000)==0) { + + ep.step("Triangle# "+itos(i),i); + } + } + + { + uint32_t oct_idx=leaf_list; + Octant *octants=octant_pool.ptr(); + while(oct_idx) { + + BakedLightBaker::Octant *oct = &octants[oct_idx]; + for(int ci=0;ci<8;ci++) { + + + Vector3 pos = oct->aabb.pos; + + if (ci&1) + pos.x+=oct->aabb.size.x; + if (ci&2) + pos.y+=oct->aabb.size.y; + if (ci&4) + pos.z+=oct->aabb.size.z; + + + pos.x=floor((pos.x+cell_size*0.5)/cell_size); + pos.y=floor((pos.y+cell_size*0.5)/cell_size); + pos.z=floor((pos.z+cell_size*0.5)/cell_size); + + Map<Vector3,Vector3>::Element *E=endpoint_normal.find(pos); + if (!E) { + //? + print_line("lolwut?"); + } else { + Vector3 n = E->get().normalized(); + oct->normal_accum[ci][0]=n.x; + oct->normal_accum[ci][1]=n.y; + oct->normal_accum[ci][2]=n.z; + + } + + } + + oct_idx=oct->next_leaf; + } + } + + +} + + + + + +void BakedLightBaker::_plot_light(int p_light_index, const Vector3& p_plot_pos, const AABB& p_plot_aabb, const Color& p_light, const Plane& p_plane) { + + //stackless version + + uint32_t *stack=octant_stack; + uint32_t *ptr_stack=octantptr_stack; + Octant *octants=octant_pool.ptr(); + + stack[0]=0; + ptr_stack[0]=0; + + int stack_pos=0; + + + while(true) { + + Octant &octant=octants[ptr_stack[stack_pos]]; + + if (octant.leaf) { + + + + //if (p_plane.normal.dot(octant.aabb.get_support(p_plane.normal)) < p_plane.d-CMP_EPSILON) { //octants behind are no go + + + + float r=cell_size*plot_size; + for(int i=0;i<8;i++) { + Vector3 pos=octant.aabb.pos; + if (i&1) + pos.x+=octant.aabb.size.x; + if (i&2) + pos.y+=octant.aabb.size.y; + if (i&4) + pos.z+=octant.aabb.size.z; + + + + float d = p_plot_pos.distance_to(pos); + + if (d<=r) { + + + float intensity = 1.0 - (d/r)*(d/r); //not gauss but.. + float damp = Math::abs(p_plane.normal.dot(Vector3(octant.normal_accum[i][0],octant.normal_accum[i][1],octant.normal_accum[i][2]))); + intensity*=pow(damp,edge_damp); + //intensity*=1.0-Math::abs(p_plane.distance_to(pos))/(plot_size*cell_size); + octant.light[p_light_index].accum[i][0]+=p_light.r*intensity; + octant.light[p_light_index].accum[i][1]+=p_light.g*intensity; + octant.light[p_light_index].accum[i][2]+=p_light.b*intensity; + } + } + + stack_pos--; + } else if (stack[stack_pos]<8) { + + int i = stack[stack_pos]; + stack[stack_pos]++; + + if (!octant.children[i]) { + continue; + } + + Octant &child=octants[octant.children[i]]; + + if (!child.aabb.intersects(p_plot_aabb)) + continue; + + if (child.aabb.encloses(p_plot_aabb)) { + stack[stack_pos]=8; //don't test the rest + } + + stack_pos++; + stack[stack_pos]=0; + ptr_stack[stack_pos]=octant.children[i]; + } else { + stack_pos--; + if (stack_pos<0) + break; + } + } + + +} + + +float BakedLightBaker::_throw_ray(int p_light_index,const Vector3& p_begin, const Vector3& p_end,float p_rest,const Color& p_light,float *p_att_curve,float p_att_pos,int p_att_curve_len,int p_bounces,bool p_first_bounce) { + + + uint32_t* stack = ray_stack; + BVH **bstack = bvh_stack; + + enum { + TEST_AABB_BIT=0, + VISIT_LEFT_BIT=1, + VISIT_RIGHT_BIT=2, + VISIT_DONE_BIT=3, + + + }; + + Vector3 n = (p_end-p_begin); + float len=n.length(); + if (len==0) + return 0; + n/=len; + + + real_t d=1e10; + bool inters=false; + Vector3 r_normal; + Vector3 r_point; + Vector3 end=p_end; + + Triangle *triangle=NULL; + + //for(int i=0;i<max_depth;i++) + // stack[i]=0; + + int level=0; + //AABB ray_aabb; + //ray_aabb.pos=p_begin; + //ray_aabb.expand_to(p_end); + + + const BVH *bvhptr = bvh; + + bstack[0]=bvh; + stack[0]=TEST_AABB_BIT; + + + while(true) { + + uint32_t mode = stack[level]; + const BVH &b = *bstack[level]; + bool done=false; + + switch(mode) { + case TEST_AABB_BIT: { + + if (b.leaf) { + + + Face3 f3(b.leaf->vertices[0],b.leaf->vertices[1],b.leaf->vertices[2]); + + + Vector3 res; + + if (f3.intersects_segment(p_begin,end,&res)) { + + + float nd = n.dot(res); + if (nd<d) { + + d=nd; + r_point=res; + end=res; + len=(p_begin-end).length(); + r_normal=f3.get_plane().get_normal(); + triangle=b.leaf; + inters=true; + } + + } + + stack[level]=VISIT_DONE_BIT; + } else { + + + bool valid = b.aabb.smits_intersect_ray(p_begin,n,0,len); + //bool valid = b.aabb.intersects_segment(p_begin,p_end); + // bool valid = b.aabb.intersects(ray_aabb); + + if (!valid) { + + stack[level]=VISIT_DONE_BIT; + + } else { + + stack[level]=VISIT_LEFT_BIT; + } + } + + } continue; + case VISIT_LEFT_BIT: { + + stack[level]=VISIT_RIGHT_BIT; + bstack[level+1]=b.children[0]; + stack[level+1]=TEST_AABB_BIT; + level++; + + } continue; + case VISIT_RIGHT_BIT: { + + stack[level]=VISIT_DONE_BIT; + bstack[level+1]=b.children[1]; + stack[level+1]=TEST_AABB_BIT; + level++; + } continue; + case VISIT_DONE_BIT: { + + if (level==0) { + done=true; + break; + } else + level--; + + } continue; + } + + + if (done) + break; + } + + + if (inters) { + + + + //should check if there is normals first + Vector2 uv; + if (true) { + + triangle->get_uv_and_normal(r_point,uv,r_normal); + + } else { + + } + + if (n.dot(r_normal)>0) + r_normal=-r_normal; + + + //ok... + Color diffuse_at_point(0.8,0.8,0.8); + Color specular_at_point(0.0,0.0,0.0); + + if (triangle->material) { + + //triangle->get_uv(r_point); + diffuse_at_point=triangle->material->diffuse.get_color(uv); + specular_at_point=triangle->material->specular.get_color(uv); + } + + float dist = p_begin.distance_to(r_point); + + AABB aabb; + aabb.pos=r_point; + aabb.pos-=Vector3(1,1,1)*cell_size*plot_size; + aabb.size=Vector3(2,2,2)*cell_size*plot_size; + + Color res_light=p_light; + float att=1.0; + float dp=(1.0-normal_damp)*n.dot(-r_normal)+normal_damp; + + if (p_att_curve) { + + p_att_pos+=dist; + int cpos = Math::fast_ftoi((p_att_pos/p_att_curve_len)*ATTENUATION_CURVE_LEN); + cpos=CLAMP(cpos,0,ATTENUATION_CURVE_LEN-1); + att=p_att_curve[cpos]; + } + + + res_light.r*=dp; + res_light.g*=dp; + res_light.b*=dp; + + //light is plotted before multiplication with diffuse, this way + //the multiplication can happen with more detail in the shader + + + float ret=1e6; + + if (p_bounces>0) { + + + p_rest-=dist; + if (p_rest<CMP_EPSILON) + return 0; + + if (r_normal==-n) + return 0; //todo change a little + + r_point+=r_normal*0.01; + + + + + diffuse_at_point.r=res_light.r*diffuse_at_point.r; + diffuse_at_point.g=res_light.g*diffuse_at_point.g; + diffuse_at_point.b=res_light.b*diffuse_at_point.b; + + specular_at_point.r=res_light.r*specular_at_point.r; + specular_at_point.g=res_light.g*specular_at_point.g; + specular_at_point.b=res_light.b*specular_at_point.b; + + + + if (use_diffuse && (diffuse_at_point.r>CMP_EPSILON || diffuse_at_point.g>CMP_EPSILON || diffuse_at_point.b>CMP_EPSILON)) { + //diffuse bounce + + Vector3 c1=r_normal.cross(n).normalized(); + Vector3 c2=r_normal.cross(c1).normalized(); + double r1 = double(rand())/RAND_MAX; + double r2 = double(rand())/RAND_MAX; + double r3 = double(rand())/RAND_MAX; +#if 0 + Vector3 next = - ((c1*(r1-0.5)) + (c2*(r2-0.5)) + (r_normal*(r3-0.5))).normalized()*0.5 + r_normal*0.5; + + if (next==Vector3()) + next=r_normal; + Vector3 rn=next.normalized(); + +#else + Vector3 rn = ((c1*(r1-0.5)) + (c2*(r2-0.5)) + (r_normal*r3*0.5)).normalized(); +#endif + + + ret=_throw_ray(p_light_index,r_point,r_point+rn*p_rest,p_rest,diffuse_at_point,p_att_curve,p_att_pos,p_att_curve_len,p_bounces-1); + } + + if (use_specular && (specular_at_point.r>CMP_EPSILON || specular_at_point.g>CMP_EPSILON || specular_at_point.b>CMP_EPSILON)) { + //specular bounce + + //Vector3 c1=r_normal.cross(n).normalized(); + //Vector3 c2=r_normal.cross(c1).normalized(); + + Vector3 rn = n - r_normal *r_normal.dot(n) * 2.0; + + _throw_ray(p_light_index,r_point,r_point+rn*p_rest,p_rest,specular_at_point,p_att_curve,p_att_pos,p_att_curve_len,p_bounces-1); + } + } + + //specular later +// _plot_light_point(r_point,octree,octree_aabb,p_light); + + + Color plot_light=res_light; + plot_light.r*=att; + plot_light.g*=att; + plot_light.b*=att; + + if (!p_first_bounce) { + + + float r = plot_size * cell_size; + if (ret<r) { + //avoid accumulaiton of light on corners + //plot_light=plot_light.linear_interpolate(Color(0,0,0,0),1.0-sd/plot_size*plot_size); + plot_light=Color(0,0,0,0); + } + } + + + if (!p_first_bounce || lights[p_light_index].bake_direct) { + Plane plane(r_point,r_normal); + //print_line(String(plot_light)+String(" ")+rtos(att)); + _plot_light(p_light_index,r_point,aabb,plot_light,plane); + } + + + return dist; + } + + return 0; + +} + + + + +void BakedLightBaker::_make_octree_texture() { + + + BakedLightBaker::Octant *octants=octant_pool.ptr(); + + //find neighbours first, to have a better idea of what amount of space is needed + { + + Vector<OctantHash> octant_hashing; + octant_hashing.resize(octant_pool_size); + Vector<uint32_t> hash_table; + int hash_table_size=Math::larger_prime(16384); + hash_table.resize(hash_table_size); + uint32_t*hashptr = hash_table.ptr(); + OctantHash*octhashptr = octant_hashing.ptr(); + + for(int i=0;i<hash_table_size;i++) + hashptr[i]=0; + + + //step 1 add to hash table + + uint32_t oct_idx=leaf_list; + + + while(oct_idx) { + + BakedLightBaker::Octant *oct = &octants[oct_idx]; + uint64_t base=0; + Vector3 pos = oct->aabb.pos - octree_aabb.pos; //make sure is always positive + base=int((pos.x+cell_size*0.5)/cell_size); + base<<=16; + base|=int((pos.y+cell_size*0.5)/cell_size); + base<<=16; + base|=int((pos.z+cell_size*0.5)/cell_size); + + uint32_t hash = HashMapHahserDefault::hash(base); + uint32_t idx = hash % hash_table_size; + octhashptr[oct_idx].next=hashptr[idx]; + octhashptr[oct_idx].hash=hash; + octhashptr[oct_idx].value=base; + hashptr[idx]=oct_idx; + + oct_idx=oct->next_leaf; + + } + + //step 2 find neighbours + oct_idx=leaf_list; + int neighbours=0; + + + while(oct_idx) { + + BakedLightBaker::Octant *oct = &octants[oct_idx]; + Vector3 pos = oct->aabb.pos - octree_aabb.pos; //make sure is always positive + pos.x+=cell_size; + uint64_t base=0; + base=int((pos.x+cell_size*0.5)/cell_size); + base<<=16; + base|=int((pos.y+cell_size*0.5)/cell_size); + base<<=16; + base|=int((pos.z+cell_size*0.5)/cell_size); + + uint32_t hash = HashMapHahserDefault::hash(base); + uint32_t idx = hash % hash_table_size; + + uint32_t bucket = hashptr[idx]; + + while(bucket) { + + if (octhashptr[bucket].value==base) { + + oct->bake_neighbour=bucket; + octants[bucket].first_neighbour=false; + neighbours++; + break; + } + + bucket = octhashptr[bucket].next; + } + + oct_idx=oct->next_leaf; + + } + + print_line("octant with neighbour: "+itos(neighbours)); + + } + + + //ok let's try to just create a texture + + { + + int otex_w=(1<<lattice_size)*(1<<lattice_size)*2; //make sure lattice fits horizontally + Vector3 lattice_cell_size=octree_aabb.size; + for(int i=0;i<lattice_size;i++) { + + lattice_cell_size*=0.5; + } + + + + while(true) { + + //let's plot the leafs first, given the octree is not so obvious which size it will have + int row=4+4*(1<<lattice_size); + + + uint32_t oct_idx=leaf_list; + + //untag + while(oct_idx) { + + BakedLightBaker::Octant *oct = &octants[oct_idx]; + //0,0 also means unprocessed + oct->texture_x=0; + oct->texture_y=0; + oct_idx=oct->next_leaf; + + } + + oct_idx=leaf_list; + + + print_line("begin at row "+itos(row)); + int longest_line_reused=0; + int col=0; + int processed=0; + + while(oct_idx) { + + BakedLightBaker::Octant *oct = &octants[oct_idx]; + if (oct->first_neighbour && oct->texture_x==0 && oct->texture_y==0) { + //was not processed + uint32_t current_idx=oct_idx; + int reused=0; + + while(current_idx) { + BakedLightBaker::Octant *o = &octants[current_idx]; + if (col+1 >= otex_w) { + col=0; + row+=4; + } + o->texture_x=col; + o->texture_y=row; + processed++; + + if (o->bake_neighbour) { + reused++; + } + col+=o->bake_neighbour ? 1 : 2; //reuse neighbour + current_idx=o->bake_neighbour; + } + + if (reused>longest_line_reused) { + longest_line_reused=reused; + } + } + oct_idx=oct->next_leaf; + } + + print_line("processed "+itos(processed)); + + print_line("longest reused: "+itos(longest_line_reused)); + + col=0; + row+=4; + print_line("end at row "+itos(row)); + + //put octree, no need for recursion, just loop backwards. + int regular_octants=0; + for(int i=octant_pool_size-1;i>=0;i--) { + + BakedLightBaker::Octant *oct = &octants[i]; + if (oct->leaf) //ignore leaf + continue; + if (oct->aabb.size.x>lattice_cell_size.x*1.1) { //bigger than latice, skip + oct->texture_x=0; + oct->texture_y=0; + } else if (oct->aabb.size.x>lattice_cell_size.x*0.8) { + //this is the initial lattice + Vector3 pos = oct->aabb.pos - octree_aabb.pos; //make sure is always positive + int x = int((pos.x+lattice_cell_size.x*0.5)/lattice_cell_size.x); + int y = int((pos.y+lattice_cell_size.y*0.5)/lattice_cell_size.y); + int z = int((pos.z+lattice_cell_size.z*0.5)/lattice_cell_size.z); + //bug net + ERR_FAIL_INDEX(x,(1<<lattice_size)); + ERR_FAIL_INDEX(y,(1<<lattice_size)); + ERR_FAIL_INDEX(z,(1<<lattice_size)); + + /*int ofs = z*(1<<lattice_size)*(1<<lattice_size)+y*(1<<lattice_size)+x; + ofs*=4; + oct->texture_x=ofs%otex_w; + oct->texture_y=(ofs/otex_w)*4+4; + */ + + oct->texture_x=(x+(1<<lattice_size)*z)*2; + oct->texture_y=4+y*4; + //print_line("pos: "+itos(x)+","+itos(y)+","+itos(z)+" - ofs"+itos(oct->texture_x)+","+itos(oct->texture_y)); + + + } else { + //an everyday regular octant + + if (col+2 > otex_w) { + col=0; + row+=4; + } + + oct->texture_x=col; + oct->texture_y=row; + col+=2; + regular_octants++; + + + } + } + print_line("octants end at row "+itos(row)+" totalling"+itos(regular_octants)); + + //ok evaluation. + + if (otex_w<=2048 && row>2048) { //too big upwards, try bigger texture + otex_w*=2; + continue; + } else { + baked_octree_texture_w=otex_w; + baked_octree_texture_h=row+4; + break; + } + + } + + + } + + + baked_octree_texture_h=nearest_power_of_2(baked_octree_texture_h); + print_line("RESULT! "+itos(baked_octree_texture_w)+","+itos(baked_octree_texture_h)); + +} + + + + + + + + +double BakedLightBaker::get_normalization(int p_light_idx) const { + + double nrg=0; + + const LightData &dl=lights[p_light_idx]; + double cell_area = cell_size*cell_size;; + //nrg+= /*dl.energy */ (dl.rays_thrown * cell_area / dl.area); + nrg=dl.rays_thrown * cell_area; + nrg*=(Math_PI*plot_size*plot_size)*0.5; // damping of radial linear gradient kernel + nrg*=dl.constant; + //nrg*=5; + print_line("CS: "+rtos(cell_size)); + + return nrg; +} + +void BakedLightBaker::throw_rays(int p_amount) { + + + + for(int i=0;i<lights.size();i++) { + + LightData &dl=lights[i]; + + + int amount = p_amount * total_light_area / dl.area; + + switch(dl.type) { + + case VS::LIGHT_DIRECTIONAL: { + + + for(int j=0;j<amount;j++) { + Vector3 from = dl.pos; + double r1 = double(rand())/RAND_MAX; + double r2 = double(rand())/RAND_MAX; + from+=dl.up*(r1*2.0-1.0); + from+=dl.left*(r2*2.0-1.0); + Vector3 to = from+dl.dir*dl.length; + Color col=dl.diffuse; + col.r*=dl.energy; + col.g*=dl.energy; + col.b*=dl.energy; + dl.rays_thrown++; + total_rays++; + _throw_ray(i,from,to,dl.length,col,NULL,0,0,max_bounces,true); + } + } break; + case VS::LIGHT_OMNI: { + + + for(int j=0;j<amount;j++) { + Vector3 from = dl.pos; + + double r1 = double(rand())/RAND_MAX; + double r2 = double(rand())/RAND_MAX; + double r3 = double(rand())/RAND_MAX; + +#if 0 + //crap is not uniform.. + Vector3 dir = Vector3(r1*2.0-1.0,r2*2.0-1.0,r3*2.0-1.0).normalized(); + +#else + + double phi = r1*Math_PI*2.0; + double costheta = r2*2.0-1.0; + double u = r3; + + double theta = acos( costheta ); + double r = 1.0 * pow( u,1/3.0 ); + + Vector3 dir( + r * sin( theta) * cos( phi ), + r * sin( theta) * sin( phi ), + r * cos( theta ) + ); + dir.normalize(); + +#endif + Vector3 to = dl.pos+dir*dl.radius; + Color col=dl.diffuse; + col.r*=dl.energy; + col.g*=dl.energy; + col.b*=dl.energy; + + dl.rays_thrown++; + total_rays++; + _throw_ray(i,from,to,dl.radius,col,dl.attenuation_table.ptr(),0,dl.radius,max_bounces,true); +// _throw_ray(i,from,to,dl.radius,col,NULL,0,dl.radius,max_bounces,true); + } + + } break; + case VS::LIGHT_SPOT: { + + for(int j=0;j<amount;j++) { + Vector3 from = dl.pos; + + double r1 = double(rand())/RAND_MAX; + double r2 = double(rand())/RAND_MAX; + double r3 = double(rand())/RAND_MAX; + + float d=Math::tan(Math::deg2rad(dl.spot_angle)); + + float x = sin(r1*Math_PI*2.0)*d; + float y = cos(r1*Math_PI*2.0)*d; + + Vector3 dir = r3*(dl.dir + dl.up*y + dl.left*x) + (1.0-r3)*dl.dir; + dir.normalize(); + + + Vector3 to = dl.pos+dir*dl.radius; + Color col=dl.diffuse; + col.r*=dl.energy; + col.g*=dl.energy; + col.b*=dl.energy; + + dl.rays_thrown++; + total_rays++; + _throw_ray(i,from,to,dl.radius,col,dl.attenuation_table.ptr(),0,dl.radius,max_bounces,true); + // _throw_ray(i,from,to,dl.radius,col,NULL,0,dl.radius,max_bounces,true); + } + + } break; + + } + } +} + + + + + + + + + + + + + +void BakedLightBaker::bake(const Ref<BakedLight> &p_light, Node* p_node) { + + if (baking) + return; + cell_count=0; + + base_inv=p_node->cast_to<Spatial>()->get_global_transform().affine_inverse(); + EditorProgress ep("bake","Light Baker Setup:",5); + baked_light=p_light; + lattice_size=baked_light->get_initial_lattice_subdiv(); + octree_depth=baked_light->get_cell_subdivision(); + plot_size=baked_light->get_plot_size(); + max_bounces=baked_light->get_bounces(); + use_diffuse=baked_light->get_bake_flag(BakedLight::BAKE_DIFFUSE); + use_specular=baked_light->get_bake_flag(BakedLight::BAKE_SPECULAR); + use_translucency=baked_light->get_bake_flag(BakedLight::BAKE_TRANSLUCENT); + + edge_damp=baked_light->get_edge_damp(); + normal_damp=baked_light->get_normal_damp(); + octree_extra_margin=baked_light->get_cell_extra_margin(); + + + + ep.step("Parsing Geometry",0); + _parse_geometry(p_node); + mat_map.clear(); + tex_map.clear(); + print_line("\ttotal triangles: "+itos(triangles.size())); + ep.step("Fixing Lights",1); + _fix_lights(); + ep.step("Making BVH",2); + _make_bvh(); + ep.step("Creating Light Octree",3); + _make_octree(); + ep.step("Creating Octree Texture",4); + _make_octree_texture(); + baking=true; + _start_thread(); + +} + + +void BakedLightBaker::update_octree_image(DVector<uint8_t> &p_image) { + + + int len = baked_octree_texture_w*baked_octree_texture_h*4; + p_image.resize(len); + DVector<uint8_t>::Write w = p_image.write(); + zeromem(w.ptr(),len); + float gamma = baked_light->get_gamma_adjust(); + float mult = baked_light->get_energy_multiplier(); + + for(int i=0;i<len;i+=4) { + w[i+0]=0xFF; + w[i+1]=0; + w[i+2]=0xFF; + w[i+3]=0xFF; + } + + encode_uint32(baked_octree_texture_w,&w[0]); + encode_uint32(baked_octree_texture_h,&w[4]); + encode_uint32(0,&w[8]); + encode_float(1<<lattice_size,&w[12]); + encode_uint32(octree_depth-lattice_size,&w[16]); + + encode_float(octree_aabb.pos.x,&w[32]); + encode_float(octree_aabb.pos.y,&w[36]); + encode_float(octree_aabb.pos.z,&w[40]); + encode_float(octree_aabb.size.x,&w[44]); + encode_float(octree_aabb.size.y,&w[48]); + encode_float(octree_aabb.size.z,&w[52]); + + + BakedLightBaker::Octant *octants=octant_pool.ptr(); + int octant_count=octant_pool_size; + uint8_t *ptr = w.ptr(); + + + int child_offsets[8]={ + 0, + 4, + baked_octree_texture_w*4, + baked_octree_texture_w*4+4, + baked_octree_texture_w*8+0, + baked_octree_texture_w*8+4, + baked_octree_texture_w*8+baked_octree_texture_w*4, + baked_octree_texture_w*8+baked_octree_texture_w*4+4, + }; + + Vector<double> norm_arr; + norm_arr.resize(lights.size()); + + for(int i=0;i<lights.size();i++) { + norm_arr[i] = 1.0/get_normalization(i); + } + + const double *normptr=norm_arr.ptr(); + + int lz=lights.size(); + + for(int i=0;i<octant_count;i++) { + + Octant &oct=octants[i]; + if (oct.texture_x==0 && oct.texture_y==0) + continue; + int ofs = (oct.texture_y * baked_octree_texture_w + oct.texture_x)<<2; + + if (oct.leaf) { + + //write colors + for(int j=0;j<8;j++) { + + //if (!oct.children[j]) + // continue; + uint8_t *iptr=&ptr[ofs+child_offsets[j]]; + float r=0; + float g=0; + float b=0; + + for(int k=0;k<lz;k++) { + r+=oct.light[k].accum[j][0]*normptr[k]; + g+=oct.light[k].accum[j][1]*normptr[k]; + b+=oct.light[k].accum[j][2]*normptr[k]; + } + + r=pow(r*mult,gamma); + g=pow(g*mult,gamma); + b=pow(b*mult,gamma); + + float ic[3]={ + r, + g, + b, + }; + iptr[0]=CLAMP(ic[0]*255.0,0,255); + iptr[1]=CLAMP(ic[1]*255.0,0,255); + iptr[2]=CLAMP(ic[2]*255.0,0,255); + iptr[3]=255; + } + + } else { + + + //write indices + for(int j=0;j<8;j++) { + + if (!oct.children[j]) + continue; + Octant&choct=octants[oct.children[j]]; + uint8_t *iptr=&ptr[ofs+child_offsets[j]]; + + iptr[0]=choct.texture_x>>8; + iptr[1]=choct.texture_x&0xFF; + iptr[2]=choct.texture_y>>8; + iptr[3]=choct.texture_y&0xFF; + + } + } + + } + + +} + + +void BakedLightBaker::_free_bvh(BVH* p_bvh) { + + if (!p_bvh->leaf) { + if (p_bvh->children[0]) + _free_bvh(p_bvh->children[0]); + if (p_bvh->children[1]) + _free_bvh(p_bvh->children[1]); + } + + memdelete(p_bvh); + +} + + +bool BakedLightBaker::is_baking() { + + return baking; +} + +void BakedLightBaker::set_pause(bool p_pause){ + + if (paused==p_pause) + return; + + paused=p_pause; + + if (paused) { + _stop_thread(); + } else { + _start_thread(); + } +} +bool BakedLightBaker::is_paused() { + + return paused; + +} + +void BakedLightBaker::_bake_thread_func(void *arg) { + + BakedLightBaker *ble = (BakedLightBaker*)arg; + + ble->rays_at_snap_time=ble->total_rays; + ble->snap_time=OS::get_singleton()->get_ticks_usec(); + + while(!ble->bake_thread_exit) { + + ble->throw_rays(1000); + uint64_t t=OS::get_singleton()->get_ticks_usec(); + if (t-ble->snap_time>1000000) { + + double time = (t-ble->snap_time)/1000000.0; + + int rays=ble->total_rays-ble->rays_at_snap_time; + ble->rays_sec=int(rays/time); + ble->snap_time=OS::get_singleton()->get_ticks_usec(); + ble->rays_at_snap_time=ble->total_rays; + } + } + +} + +void BakedLightBaker::_start_thread() { + + if (thread!=NULL) + return; + bake_thread_exit=false; + thread=Thread::create(_bake_thread_func,this); + +} + +void BakedLightBaker::_stop_thread() { + + if (thread==NULL) + return; + bake_thread_exit=true; + Thread::wait_to_finish(thread); + thread=NULL; +} + +void BakedLightBaker::clear() { + + + + _stop_thread(); + + if (bvh) + _free_bvh(bvh); + + if (ray_stack) + memdelete_arr(ray_stack); + if (octant_stack) + memdelete_arr(octant_stack); + if (octantptr_stack) + memdelete_arr(octantptr_stack); + if (bvh_stack) + memdelete_arr(bvh_stack); + + for(int i=0;i<octant_pool.size();i++) { + if (octant_pool[i].leaf) { + memdelete_arr( octant_pool[i].light ); + } + } + octant_pool.clear(); + octant_pool_size=0; + bvh=NULL; + leaf_list=0; + cell_count=0; + ray_stack=NULL; + octant_stack=NULL; + octantptr_stack=NULL; + bvh_stack=NULL; + materials.clear(); + materials.clear(); + textures.clear(); + lights.clear(); + triangles.clear();; + endpoint_normal.clear(); + baked_octree_texture_w=0; + baked_octree_texture_h=0; + paused=false; + baking=false; + thread=NULL; + bake_thread_exit=false; + baked_light=Ref<BakedLight>(); + total_rays=0; + +} + +BakedLightBaker::BakedLightBaker() { + octree_depth=9; + lattice_size=4; + octant_pool.clear(); + octant_pool_size=0; + bvh=NULL; + leaf_list=0; + cell_count=0; + ray_stack=NULL; + bvh_stack=NULL; + octant_stack=NULL; + octantptr_stack=NULL; + plot_size=2.5; + max_bounces=2; + materials.clear(); + baked_octree_texture_w=0; + baked_octree_texture_h=0; + paused=false; + baking=false; + thread=NULL; + bake_thread_exit=false; + rays_at_snap_time=0; + snap_time=0; + rays_sec=0; + total_rays=0; + +} + +BakedLightBaker::~BakedLightBaker() { + + clear(); +} |