-Add lightmapper
-Fixes to unwrapper (remove degenerates), makes Thekla not crash -Added optional cancel button in EditorProgress -Added function to force processing of events (needed for cancel button)
This commit is contained in:
parent
aa6772d7ab
commit
f3ad14224e
44 changed files with 4585 additions and 1139 deletions
|
@ -168,6 +168,7 @@ opts.Add(BoolVariable('vsproj', "Generate Visual Studio Project.", False))
|
|||
opts.Add(EnumVariable('warnings', "Set the level of warnings emitted during compilation", 'no', ('extra', 'all', 'moderate', 'no')))
|
||||
opts.Add(BoolVariable('progress', "Show a progress indicator during build", True))
|
||||
opts.Add(BoolVariable('dev', "If yes, alias for verbose=yes warnings=all", False))
|
||||
opts.Add(BoolVariable('openmp', "If yes, enable OpenMP", True))
|
||||
|
||||
# Thirdparty libraries
|
||||
opts.Add(BoolVariable('builtin_enet', "Use the builtin enet library", True))
|
||||
|
|
|
@ -442,6 +442,7 @@ public:
|
|||
virtual int get_power_seconds_left();
|
||||
virtual int get_power_percent_left();
|
||||
|
||||
virtual void force_process_input(){};
|
||||
bool has_feature(const String &p_feature);
|
||||
|
||||
/**
|
||||
|
|
|
@ -1849,6 +1849,20 @@ void RasterizerSceneGLES3::_setup_light(RenderList::Element *e, const Transform
|
|||
|
||||
state.scene_shader.set_uniform(SceneShaderGLES3::GI_PROBE2_ENABLED, false);
|
||||
}
|
||||
} else if (!e->instance->lightmap_capture_data.empty()) {
|
||||
|
||||
glUniform4fv(state.scene_shader.get_uniform_location(SceneShaderGLES3::LIGHTMAP_CAPTURES), 12, (const GLfloat *)e->instance->lightmap_capture_data.ptr());
|
||||
state.scene_shader.set_uniform(SceneShaderGLES3::LIGHTMAP_CAPTURE_SKY, false);
|
||||
|
||||
} else if (e->instance->lightmap.is_valid()) {
|
||||
RasterizerStorageGLES3::Texture *lightmap = storage->texture_owner.getornull(e->instance->lightmap);
|
||||
RasterizerStorageGLES3::LightmapCapture *capture = storage->lightmap_capture_data_owner.getornull(e->instance->lightmap_capture->base);
|
||||
|
||||
if (lightmap && capture) {
|
||||
glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 9);
|
||||
glBindTexture(GL_TEXTURE_2D, lightmap->tex_id);
|
||||
state.scene_shader.set_uniform(SceneShaderGLES3::LIGHTMAP_ENERGY, capture->energy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1971,6 +1985,8 @@ void RasterizerSceneGLES3::_render_list(RenderList::Element **p_elements, int p_
|
|||
state.scene_shader.set_conditional(SceneShaderGLES3::SHADOW_MODE_PCF_5, false);
|
||||
state.scene_shader.set_conditional(SceneShaderGLES3::SHADOW_MODE_PCF_13, false);
|
||||
state.scene_shader.set_conditional(SceneShaderGLES3::USE_GI_PROBES, false);
|
||||
state.scene_shader.set_conditional(SceneShaderGLES3::USE_LIGHTMAP_CAPTURE, false);
|
||||
state.scene_shader.set_conditional(SceneShaderGLES3::USE_LIGHTMAP, false);
|
||||
state.scene_shader.set_conditional(SceneShaderGLES3::USE_RADIANCE_MAP, false);
|
||||
state.scene_shader.set_conditional(SceneShaderGLES3::USE_CONTACT_SHADOWS, false);
|
||||
|
||||
|
@ -1978,6 +1994,8 @@ void RasterizerSceneGLES3::_render_list(RenderList::Element **p_elements, int p_
|
|||
} else {
|
||||
|
||||
state.scene_shader.set_conditional(SceneShaderGLES3::USE_GI_PROBES, e->instance->gi_probe_instances.size() > 0);
|
||||
state.scene_shader.set_conditional(SceneShaderGLES3::USE_LIGHTMAP, e->instance->lightmap.is_valid() && e->instance->gi_probe_instances.size() == 0);
|
||||
state.scene_shader.set_conditional(SceneShaderGLES3::USE_LIGHTMAP_CAPTURE, !e->instance->lightmap_capture_data.empty() && !e->instance->lightmap.is_valid() && e->instance->gi_probe_instances.size() == 0);
|
||||
|
||||
state.scene_shader.set_conditional(SceneShaderGLES3::SHADELESS, false);
|
||||
|
||||
|
@ -2148,6 +2166,8 @@ void RasterizerSceneGLES3::_render_list(RenderList::Element **p_elements, int p_
|
|||
state.scene_shader.set_conditional(SceneShaderGLES3::SHADOW_MODE_PCF_5, false);
|
||||
state.scene_shader.set_conditional(SceneShaderGLES3::SHADOW_MODE_PCF_13, false);
|
||||
state.scene_shader.set_conditional(SceneShaderGLES3::USE_GI_PROBES, false);
|
||||
state.scene_shader.set_conditional(SceneShaderGLES3::USE_LIGHTMAP, false);
|
||||
state.scene_shader.set_conditional(SceneShaderGLES3::USE_LIGHTMAP_CAPTURE, false);
|
||||
state.scene_shader.set_conditional(SceneShaderGLES3::USE_CONTACT_SHADOWS, false);
|
||||
state.scene_shader.set_conditional(SceneShaderGLES3::USE_VERTEX_LIGHTING, false);
|
||||
state.scene_shader.set_conditional(SceneShaderGLES3::USE_OPAQUE_PREPASS, false);
|
||||
|
@ -2274,6 +2294,14 @@ void RasterizerSceneGLES3::_add_geometry_with_material(RasterizerStorageGLES3::G
|
|||
e->sort_key |= SORT_KEY_GI_PROBES_FLAG;
|
||||
}
|
||||
|
||||
if (e->instance->lightmap.is_valid()) {
|
||||
e->sort_key |= SORT_KEY_LIGHTMAP_FLAG;
|
||||
}
|
||||
|
||||
if (!e->instance->lightmap_capture_data.empty()) {
|
||||
e->sort_key |= SORT_KEY_LIGHTMAP_CAPTURE_FLAG;
|
||||
}
|
||||
|
||||
e->sort_key |= uint64_t(p_material->render_priority + 128) << RenderList::SORT_KEY_PRIORITY_SHIFT;
|
||||
} else {
|
||||
e->sort_key |= uint64_t(e->instance->depth_layer) << RenderList::SORT_KEY_OPAQUE_DEPTH_LAYER_SHIFT;
|
||||
|
|
|
@ -662,19 +662,21 @@ public:
|
|||
SORT_KEY_OPAQUE_DEPTH_LAYER_SHIFT = 52,
|
||||
SORT_KEY_OPAQUE_DEPTH_LAYER_MASK = 0xF,
|
||||
//64 bits unsupported in MSVC
|
||||
#define SORT_KEY_UNSHADED_FLAG (uint64_t(1) << 51)
|
||||
#define SORT_KEY_NO_DIRECTIONAL_FLAG (uint64_t(1) << 50)
|
||||
#define SORT_KEY_GI_PROBES_FLAG (uint64_t(1) << 49)
|
||||
#define SORT_KEY_VERTEX_LIT_FLAG (uint64_t(1) << 48)
|
||||
SORT_KEY_SHADING_SHIFT = 48,
|
||||
SORT_KEY_SHADING_MASK = 15,
|
||||
//48-32 material index
|
||||
SORT_KEY_MATERIAL_INDEX_SHIFT = 32,
|
||||
//32-12 geometry index
|
||||
SORT_KEY_GEOMETRY_INDEX_SHIFT = 12,
|
||||
//bits 12-8 geometry type
|
||||
SORT_KEY_GEOMETRY_TYPE_SHIFT = 8,
|
||||
//bits 0-7 for flags
|
||||
#define SORT_KEY_UNSHADED_FLAG (uint64_t(1) << 49)
|
||||
#define SORT_KEY_NO_DIRECTIONAL_FLAG (uint64_t(1) << 48)
|
||||
#define SORT_KEY_LIGHTMAP_CAPTURE_FLAG (uint64_t(1) << 47)
|
||||
#define SORT_KEY_LIGHTMAP_FLAG (uint64_t(1) << 46)
|
||||
#define SORT_KEY_GI_PROBES_FLAG (uint64_t(1) << 45)
|
||||
#define SORT_KEY_VERTEX_LIT_FLAG (uint64_t(1) << 44)
|
||||
SORT_KEY_SHADING_SHIFT = 44,
|
||||
SORT_KEY_SHADING_MASK = 63,
|
||||
//44-28 material index
|
||||
SORT_KEY_MATERIAL_INDEX_SHIFT = 28,
|
||||
//28-8 geometry index
|
||||
SORT_KEY_GEOMETRY_INDEX_SHIFT = 8,
|
||||
//bits 5-7 geometry type
|
||||
SORT_KEY_GEOMETRY_TYPE_SHIFT = 5,
|
||||
//bits 0-5 for flags
|
||||
SORT_KEY_OPAQUE_PRE_PASS = 8,
|
||||
SORT_KEY_CULL_DISABLED_FLAG = 4,
|
||||
SORT_KEY_SKELETON_FLAG = 2,
|
||||
|
|
|
@ -5216,6 +5216,104 @@ void RasterizerStorageGLES3::gi_probe_dynamic_data_update(RID p_gi_probe_data, i
|
|||
//glTexImage3D(GL_TEXTURE_3D,p_mipmap,GL_RGBA8,gipd->width>>p_mipmap,gipd->height>>p_mipmap,gipd->depth>>p_mipmap,0,GL_RGBA,GL_UNSIGNED_BYTE,p_data);
|
||||
//glTexImage3D(GL_TEXTURE_3D,p_mipmap,GL_RGBA8,gipd->width>>p_mipmap,gipd->height>>p_mipmap,gipd->depth>>p_mipmap,0,GL_RGBA,GL_UNSIGNED_BYTE,data.ptr());
|
||||
}
|
||||
/////////////////////////////
|
||||
|
||||
RID RasterizerStorageGLES3::lightmap_capture_create() {
|
||||
|
||||
LightmapCapture *capture = memnew(LightmapCapture);
|
||||
return lightmap_capture_data_owner.make_rid(capture);
|
||||
}
|
||||
|
||||
void RasterizerStorageGLES3::lightmap_capture_set_bounds(RID p_capture, const AABB &p_bounds) {
|
||||
|
||||
LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture);
|
||||
ERR_FAIL_COND(!capture);
|
||||
capture->bounds = p_bounds;
|
||||
capture->instance_change_notify();
|
||||
}
|
||||
AABB RasterizerStorageGLES3::lightmap_capture_get_bounds(RID p_capture) const {
|
||||
|
||||
const LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture);
|
||||
ERR_FAIL_COND_V(!capture, AABB());
|
||||
return capture->bounds;
|
||||
}
|
||||
void RasterizerStorageGLES3::lightmap_capture_set_octree(RID p_capture, const PoolVector<uint8_t> &p_octree) {
|
||||
|
||||
LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture);
|
||||
ERR_FAIL_COND(!capture);
|
||||
|
||||
ERR_FAIL_COND(p_octree.size() == 0 || (p_octree.size() % sizeof(LightmapCaptureOctree)) != 0);
|
||||
|
||||
capture->octree.resize(p_octree.size() / sizeof(LightmapCaptureOctree));
|
||||
if (p_octree.size()) {
|
||||
PoolVector<LightmapCaptureOctree>::Write w = capture->octree.write();
|
||||
PoolVector<uint8_t>::Read r = p_octree.read();
|
||||
copymem(w.ptr(), r.ptr(), p_octree.size());
|
||||
}
|
||||
capture->instance_change_notify();
|
||||
}
|
||||
PoolVector<uint8_t> RasterizerStorageGLES3::lightmap_capture_get_octree(RID p_capture) const {
|
||||
|
||||
const LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture);
|
||||
ERR_FAIL_COND_V(!capture, PoolVector<uint8_t>());
|
||||
|
||||
if (capture->octree.size() == 0)
|
||||
return PoolVector<uint8_t>();
|
||||
|
||||
PoolVector<uint8_t> ret;
|
||||
ret.resize(capture->octree.size() * sizeof(LightmapCaptureOctree));
|
||||
{
|
||||
PoolVector<LightmapCaptureOctree>::Read r = capture->octree.read();
|
||||
PoolVector<uint8_t>::Write w = ret.write();
|
||||
copymem(w.ptr(), r.ptr(), ret.size());
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void RasterizerStorageGLES3::lightmap_capture_set_octree_cell_transform(RID p_capture, const Transform &p_xform) {
|
||||
LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture);
|
||||
ERR_FAIL_COND(!capture);
|
||||
capture->cell_xform = p_xform;
|
||||
}
|
||||
|
||||
Transform RasterizerStorageGLES3::lightmap_capture_get_octree_cell_transform(RID p_capture) const {
|
||||
const LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture);
|
||||
ERR_FAIL_COND_V(!capture, Transform());
|
||||
return capture->cell_xform;
|
||||
}
|
||||
|
||||
void RasterizerStorageGLES3::lightmap_capture_set_octree_cell_subdiv(RID p_capture, int p_subdiv) {
|
||||
LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture);
|
||||
ERR_FAIL_COND(!capture);
|
||||
capture->cell_subdiv = p_subdiv;
|
||||
}
|
||||
|
||||
int RasterizerStorageGLES3::lightmap_capture_get_octree_cell_subdiv(RID p_capture) const {
|
||||
const LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture);
|
||||
ERR_FAIL_COND_V(!capture, 0);
|
||||
return capture->cell_subdiv;
|
||||
}
|
||||
|
||||
void RasterizerStorageGLES3::lightmap_capture_set_energy(RID p_capture, float p_energy) {
|
||||
|
||||
LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture);
|
||||
ERR_FAIL_COND(!capture);
|
||||
capture->energy = p_energy;
|
||||
}
|
||||
|
||||
float RasterizerStorageGLES3::lightmap_capture_get_energy(RID p_capture) const {
|
||||
|
||||
const LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture);
|
||||
ERR_FAIL_COND_V(!capture, 0);
|
||||
return capture->energy;
|
||||
}
|
||||
|
||||
const PoolVector<RasterizerStorage::LightmapCaptureOctree> *RasterizerStorageGLES3::lightmap_capture_get_octree_ptr(RID p_capture) const {
|
||||
const LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture);
|
||||
ERR_FAIL_COND_V(!capture, NULL);
|
||||
return &capture->octree;
|
||||
}
|
||||
|
||||
///////
|
||||
|
||||
|
@ -5817,6 +5915,10 @@ void RasterizerStorageGLES3::instance_add_dependency(RID p_base, RasterizerScene
|
|||
inst = gi_probe_owner.getornull(p_base);
|
||||
ERR_FAIL_COND(!inst);
|
||||
} break;
|
||||
case VS::INSTANCE_LIGHTMAP_CAPTURE: {
|
||||
inst = lightmap_capture_data_owner.getornull(p_base);
|
||||
ERR_FAIL_COND(!inst);
|
||||
} break;
|
||||
default: {
|
||||
if (!inst) {
|
||||
ERR_FAIL();
|
||||
|
@ -5860,6 +5962,10 @@ void RasterizerStorageGLES3::instance_remove_dependency(RID p_base, RasterizerSc
|
|||
inst = gi_probe_owner.getornull(p_base);
|
||||
ERR_FAIL_COND(!inst);
|
||||
} break;
|
||||
case VS::INSTANCE_LIGHTMAP_CAPTURE: {
|
||||
inst = lightmap_capture_data_owner.getornull(p_base);
|
||||
ERR_FAIL_COND(!inst);
|
||||
} break;
|
||||
default: {
|
||||
|
||||
if (!inst) {
|
||||
|
@ -6609,6 +6715,10 @@ VS::InstanceType RasterizerStorageGLES3::get_base_type(RID p_rid) const {
|
|||
return VS::INSTANCE_GI_PROBE;
|
||||
}
|
||||
|
||||
if (lightmap_capture_data_owner.owns(p_rid)) {
|
||||
return VS::INSTANCE_LIGHTMAP_CAPTURE;
|
||||
}
|
||||
|
||||
return VS::INSTANCE_NONE;
|
||||
}
|
||||
|
||||
|
@ -6795,6 +6905,13 @@ bool RasterizerStorageGLES3::free(RID p_rid) {
|
|||
glDeleteTextures(1, &gi_probe_data->tex_id);
|
||||
gi_probe_owner.free(p_rid);
|
||||
memdelete(gi_probe_data);
|
||||
} else if (lightmap_capture_data_owner.owns(p_rid)) {
|
||||
|
||||
// delete the texture
|
||||
LightmapCapture *lightmap_capture = lightmap_capture_data_owner.get(p_rid);
|
||||
|
||||
gi_probe_owner.free(p_rid);
|
||||
memdelete(lightmap_capture);
|
||||
|
||||
} else if (canvas_occluder_owner.owns(p_rid)) {
|
||||
|
||||
|
|
|
@ -1069,6 +1069,38 @@ public:
|
|||
virtual RID gi_probe_dynamic_data_create(int p_width, int p_height, int p_depth, GIProbeCompression p_compression);
|
||||
virtual void gi_probe_dynamic_data_update(RID p_gi_probe_data, int p_depth_slice, int p_slice_count, int p_mipmap, const void *p_data);
|
||||
|
||||
/* LIGHTMAP CAPTURE */
|
||||
|
||||
virtual RID lightmap_capture_create();
|
||||
virtual void lightmap_capture_set_bounds(RID p_capture, const AABB &p_bounds);
|
||||
virtual AABB lightmap_capture_get_bounds(RID p_capture) const;
|
||||
virtual void lightmap_capture_set_octree(RID p_capture, const PoolVector<uint8_t> &p_octree);
|
||||
virtual PoolVector<uint8_t> lightmap_capture_get_octree(RID p_capture) const;
|
||||
virtual void lightmap_capture_set_octree_cell_transform(RID p_capture, const Transform &p_xform);
|
||||
virtual Transform lightmap_capture_get_octree_cell_transform(RID p_capture) const;
|
||||
virtual void lightmap_capture_set_octree_cell_subdiv(RID p_capture, int p_subdiv);
|
||||
virtual int lightmap_capture_get_octree_cell_subdiv(RID p_capture) const;
|
||||
|
||||
virtual void lightmap_capture_set_energy(RID p_capture, float p_energy);
|
||||
virtual float lightmap_capture_get_energy(RID p_capture) const;
|
||||
|
||||
virtual const PoolVector<LightmapCaptureOctree> *lightmap_capture_get_octree_ptr(RID p_capture) const;
|
||||
|
||||
struct LightmapCapture : public Instantiable {
|
||||
|
||||
PoolVector<LightmapCaptureOctree> octree;
|
||||
AABB bounds;
|
||||
Transform cell_xform;
|
||||
int cell_subdiv;
|
||||
float energy;
|
||||
LightmapCapture() {
|
||||
energy = 1.0;
|
||||
cell_subdiv = 1;
|
||||
}
|
||||
};
|
||||
|
||||
mutable RID_Owner<LightmapCapture> lightmap_capture_data_owner;
|
||||
|
||||
/* PARTICLES */
|
||||
|
||||
struct Particles : public GeometryOwner {
|
||||
|
|
|
@ -35,7 +35,7 @@ layout(location=3) in vec4 color_attrib;
|
|||
layout(location=4) in vec2 uv_attrib;
|
||||
#endif
|
||||
|
||||
#if defined(ENABLE_UV2_INTERP)
|
||||
#if defined(ENABLE_UV2_INTERP) || defined(USE_LIGHTMAP)
|
||||
layout(location=5) in vec2 uv2_attrib;
|
||||
#endif
|
||||
|
||||
|
@ -223,7 +223,7 @@ out vec4 color_interp;
|
|||
out vec2 uv_interp;
|
||||
#endif
|
||||
|
||||
#if defined(ENABLE_UV2_INTERP)
|
||||
#if defined(ENABLE_UV2_INTERP) || defined (USE_LIGHTMAP)
|
||||
out vec2 uv2_interp;
|
||||
#endif
|
||||
|
||||
|
@ -234,9 +234,6 @@ out vec3 binormal_interp;
|
|||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#if defined(USE_MATERIAL)
|
||||
|
||||
layout(std140) uniform UniformData { //ubo:1
|
||||
|
@ -356,7 +353,7 @@ void main() {
|
|||
uv_interp = uv_attrib;
|
||||
#endif
|
||||
|
||||
#if defined(ENABLE_UV2_INTERP)
|
||||
#if defined(ENABLE_UV2_INTERP) || defined(USE_LIGHTMAP)
|
||||
uv2_interp = uv2_attrib;
|
||||
#endif
|
||||
|
||||
|
@ -549,7 +546,7 @@ in vec4 color_interp;
|
|||
in vec2 uv_interp;
|
||||
#endif
|
||||
|
||||
#if defined(ENABLE_UV2_INTERP)
|
||||
#if defined(ENABLE_UV2_INTERP) || defined(USE_LIGHTMAP)
|
||||
in vec2 uv2_interp;
|
||||
#endif
|
||||
|
||||
|
@ -1357,7 +1354,7 @@ void reflection_process(int idx, vec3 vertex, vec3 normal,vec3 binormal, vec3 ta
|
|||
|
||||
reflection_accum+=reflection;
|
||||
}
|
||||
|
||||
#ifndef USE_LIGHTMAP
|
||||
if (reflections[idx].ambient.a>0.0) { //compute ambient using skybox
|
||||
|
||||
|
||||
|
@ -1403,8 +1400,20 @@ void reflection_process(int idx, vec3 vertex, vec3 normal,vec3 binormal, vec3 ta
|
|||
ambient_accum+=ambient_out;
|
||||
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef USE_LIGHTMAP
|
||||
uniform mediump sampler2D lightmap; //texunit:-9
|
||||
uniform mediump float lightmap_energy;
|
||||
#endif
|
||||
|
||||
#ifdef USE_LIGHTMAP_CAPTURE
|
||||
uniform mediump vec4[12] lightmap_captures;
|
||||
uniform bool lightmap_capture_sky;
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef USE_GI_PROBES
|
||||
|
||||
uniform mediump sampler3D gi_probe1; //texunit:-9
|
||||
|
@ -1632,7 +1641,7 @@ void main() {
|
|||
vec2 uv = uv_interp;
|
||||
#endif
|
||||
|
||||
#if defined(ENABLE_UV2_INTERP)
|
||||
#if defined(ENABLE_UV2_INTERP) || defined (USE_LIGHTMAP)
|
||||
vec2 uv2 = uv2_interp;
|
||||
#endif
|
||||
|
||||
|
@ -1745,7 +1754,7 @@ FRAGMENT_SHADER_CODE
|
|||
//vec3 radiance = textureLod(radiance_cube, r, lod).xyz * ( brdf.x + brdf.y);
|
||||
|
||||
}
|
||||
|
||||
#ifndef USE_LIGHTMAP
|
||||
{
|
||||
|
||||
vec3 ambient_dir=normalize((radiance_inverse_xform * vec4(normal,0.0)).xyz);
|
||||
|
@ -1754,6 +1763,7 @@ FRAGMENT_SHADER_CODE
|
|||
ambient_light=mix(ambient_light_color.rgb,env_ambient,radiance_ambient_contribution);
|
||||
//ambient_light=vec3(0.0,0.0,0.0);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#else
|
||||
|
@ -1938,6 +1948,48 @@ FRAGMENT_SHADER_CODE
|
|||
|
||||
#endif
|
||||
|
||||
#ifdef USE_LIGHTMAP
|
||||
ambient_light = texture(lightmap,uv2).rgb * lightmap_energy;
|
||||
#endif
|
||||
|
||||
#ifdef USE_LIGHTMAP_CAPTURE
|
||||
{
|
||||
vec3 cone_dirs[12] = vec3[] (
|
||||
vec3(0, 0, 1),
|
||||
vec3(0.866025, 0, 0.5),
|
||||
vec3(0.267617, 0.823639, 0.5),
|
||||
vec3(-0.700629, 0.509037, 0.5),
|
||||
vec3(-0.700629, -0.509037, 0.5),
|
||||
vec3(0.267617, -0.823639, 0.5),
|
||||
vec3(0, 0, -1),
|
||||
vec3(0.866025, 0, -0.5),
|
||||
vec3(0.267617, 0.823639, -0.5),
|
||||
vec3(-0.700629, 0.509037, -0.5),
|
||||
vec3(-0.700629, -0.509037, -0.5),
|
||||
vec3(0.267617, -0.823639, -0.5)
|
||||
);
|
||||
|
||||
|
||||
vec3 local_normal = normalize(camera_matrix * vec4(normal,0.0)).xyz;
|
||||
vec4 captured = vec4(0.0);
|
||||
float sum = 0.0;
|
||||
for(int i=0;i<12;i++) {
|
||||
float amount = max(0.0,dot(local_normal,cone_dirs[i])); //not correct, but creates a nice wrap around effect
|
||||
captured += lightmap_captures[i]*amount;
|
||||
sum+=amount;
|
||||
}
|
||||
|
||||
captured/=sum;
|
||||
|
||||
if (lightmap_capture_sky) {
|
||||
ambient_light = mix( ambient_light, captured.rgb, captured.a);
|
||||
} else {
|
||||
ambient_light = captured.rgb;
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_FORWARD_LIGHTING
|
||||
|
||||
|
||||
|
@ -1952,11 +2004,11 @@ FRAGMENT_SHADER_CODE
|
|||
} else {
|
||||
specular_light+=env_reflection_light;
|
||||
}
|
||||
|
||||
#ifndef USE_LIGHTMAP
|
||||
if (ambient_accum.a>0.0) {
|
||||
ambient_light+=ambient_accum.rgb/ambient_accum.a;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef USE_VERTEX_LIGHTING
|
||||
|
|
|
@ -67,6 +67,7 @@
|
|||
#include "editor/plugins/animation_player_editor_plugin.h"
|
||||
#include "editor/plugins/animation_tree_editor_plugin.h"
|
||||
#include "editor/plugins/asset_library_editor_plugin.h"
|
||||
#include "editor/plugins/baked_lightmap_editor_plugin.h"
|
||||
#include "editor/plugins/camera_editor_plugin.h"
|
||||
#include "editor/plugins/canvas_item_editor_plugin.h"
|
||||
#include "editor/plugins/collision_polygon_2d_editor_plugin.h"
|
||||
|
@ -3384,14 +3385,14 @@ void EditorNode::stop_child_process() {
|
|||
_menu_option_confirm(RUN_STOP, false);
|
||||
}
|
||||
|
||||
void EditorNode::progress_add_task(const String &p_task, const String &p_label, int p_steps) {
|
||||
void EditorNode::progress_add_task(const String &p_task, const String &p_label, int p_steps, bool p_can_cancel) {
|
||||
|
||||
singleton->progress_dialog->add_task(p_task, p_label, p_steps);
|
||||
singleton->progress_dialog->add_task(p_task, p_label, p_steps, p_can_cancel);
|
||||
}
|
||||
|
||||
void EditorNode::progress_task_step(const String &p_task, const String &p_state, int p_step, bool p_force_refresh) {
|
||||
bool EditorNode::progress_task_step(const String &p_task, const String &p_state, int p_step, bool p_force_refresh) {
|
||||
|
||||
singleton->progress_dialog->task_step(p_task, p_state, p_step, p_force_refresh);
|
||||
return singleton->progress_dialog->task_step(p_task, p_state, p_step, p_force_refresh);
|
||||
}
|
||||
|
||||
void EditorNode::progress_end_task(const String &p_task) {
|
||||
|
@ -5659,6 +5660,7 @@ EditorNode::EditorNode() {
|
|||
add_editor_plugin(memnew(TextureRegionEditorPlugin(this)));
|
||||
add_editor_plugin(memnew(Particles2DEditorPlugin(this)));
|
||||
add_editor_plugin(memnew(GIProbeEditorPlugin(this)));
|
||||
add_editor_plugin(memnew(BakedLightmapEditorPlugin(this)));
|
||||
add_editor_plugin(memnew(Path2DEditorPlugin(this)));
|
||||
add_editor_plugin(memnew(PathEditorPlugin(this)));
|
||||
add_editor_plugin(memnew(Line2DEditorPlugin(this)));
|
||||
|
|
|
@ -745,8 +745,8 @@ public:
|
|||
|
||||
static void add_io_error(const String &p_error);
|
||||
|
||||
static void progress_add_task(const String &p_task, const String &p_label, int p_steps);
|
||||
static void progress_task_step(const String &p_task, const String &p_state, int p_step = -1, bool p_force_refresh = true);
|
||||
static void progress_add_task(const String &p_task, const String &p_label, int p_steps, bool p_can_cancel = false);
|
||||
static bool progress_task_step(const String &p_task, const String &p_state, int p_step = -1, bool p_force_refresh = true);
|
||||
static void progress_end_task(const String &p_task);
|
||||
|
||||
static void progress_add_task_bg(const String &p_task, const String &p_label, int p_steps);
|
||||
|
@ -807,9 +807,9 @@ public:
|
|||
struct EditorProgress {
|
||||
|
||||
String task;
|
||||
void step(const String &p_state, int p_step = -1, bool p_force_refresh = true) { EditorNode::progress_task_step(task, p_state, p_step, p_force_refresh); }
|
||||
EditorProgress(const String &p_task, const String &p_label, int p_amount) {
|
||||
EditorNode::progress_add_task(p_task, p_label, p_amount);
|
||||
bool step(const String &p_state, int p_step = -1, bool p_force_refresh = true) { return EditorNode::progress_task_step(task, p_state, p_step, p_force_refresh); }
|
||||
EditorProgress(const String &p_task, const String &p_label, int p_amount, bool p_can_cancel = false) {
|
||||
EditorNode::progress_add_task(p_task, p_label, p_amount, p_can_cancel);
|
||||
task = p_task;
|
||||
}
|
||||
~EditorProgress() { EditorNode::progress_end_task(task); }
|
||||
|
|
|
@ -1165,8 +1165,8 @@ void ResourceImporterScene::get_import_options(List<ImportOption> *r_options, in
|
|||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "meshes/compress"), true));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "meshes/ensure_tangents"), true));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "meshes/storage", PROPERTY_HINT_ENUM, "Built-In,Files"), meshes_out ? 1 : 0));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "meshes/light_baking", PROPERTY_HINT_ENUM, "Disabled,Enable,Gen Lightmaps"), 0));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::REAL, "meshes/lightmap_texel_size", PROPERTY_HINT_RANGE, "0.001,100,0.001"), 0.05));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "meshes/light_baking", PROPERTY_HINT_ENUM, "Disabled,Enable,Gen Lightmaps", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 0));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::REAL, "meshes/lightmap_texel_size", PROPERTY_HINT_RANGE, "0.001,100,0.001"), 0.1));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "external_files/store_in_subdir"), false));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/import", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), true));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::REAL, "animation/fps", PROPERTY_HINT_RANGE, "1,120,1"), 15));
|
||||
|
|
95
editor/plugins/baked_lightmap_editor_plugin.cpp
Normal file
95
editor/plugins/baked_lightmap_editor_plugin.cpp
Normal file
|
@ -0,0 +1,95 @@
|
|||
#include "baked_lightmap_editor_plugin.h"
|
||||
|
||||
void BakedLightmapEditorPlugin::_bake() {
|
||||
|
||||
if (lightmap) {
|
||||
BakedLightmap::BakeError err;
|
||||
if (get_tree()->get_edited_scene_root() && get_tree()->get_edited_scene_root() == lightmap) {
|
||||
err = lightmap->bake(lightmap);
|
||||
} else {
|
||||
err = lightmap->bake(lightmap->get_parent());
|
||||
}
|
||||
|
||||
switch (err) {
|
||||
case BakedLightmap::BAKE_ERROR_NO_SAVE_PATH:
|
||||
EditorNode::get_singleton()->show_warning(TTR("Can't determine a save path for lightmap images.\nSave your scene (for images to be saved in the same dir), or pick a save path from the BakedLightmap properties."));
|
||||
break;
|
||||
case BakedLightmap::BAKE_ERROR_NO_MESHES:
|
||||
EditorNode::get_singleton()->show_warning(TTR("No meshes to bake. Make sure they contain an UV2 channel and that the 'Bake Light' flag is on."));
|
||||
break;
|
||||
case BakedLightmap::BAKE_ERROR_CANT_CREATE_IMAGE:
|
||||
EditorNode::get_singleton()->show_warning(TTR("Failed creating lightmap images, make sure path is writable."));
|
||||
break;
|
||||
defaut : {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BakedLightmapEditorPlugin::edit(Object *p_object) {
|
||||
|
||||
BakedLightmap *s = Object::cast_to<BakedLightmap>(p_object);
|
||||
if (!s)
|
||||
return;
|
||||
|
||||
lightmap = s;
|
||||
}
|
||||
|
||||
bool BakedLightmapEditorPlugin::handles(Object *p_object) const {
|
||||
|
||||
return p_object->is_class("BakedLightmap");
|
||||
}
|
||||
|
||||
void BakedLightmapEditorPlugin::make_visible(bool p_visible) {
|
||||
|
||||
if (p_visible) {
|
||||
bake->show();
|
||||
} else {
|
||||
|
||||
bake->hide();
|
||||
}
|
||||
}
|
||||
|
||||
EditorProgress *BakedLightmapEditorPlugin::tmp_progress = NULL;
|
||||
|
||||
void BakedLightmapEditorPlugin::bake_func_begin(int p_steps) {
|
||||
|
||||
ERR_FAIL_COND(tmp_progress != NULL);
|
||||
|
||||
tmp_progress = memnew(EditorProgress("bake_lightmaps", TTR("Bake Lightmaps"), p_steps, true));
|
||||
}
|
||||
|
||||
bool BakedLightmapEditorPlugin::bake_func_step(int p_step, const String &p_description) {
|
||||
|
||||
ERR_FAIL_COND_V(tmp_progress == NULL, false);
|
||||
return tmp_progress->step(p_description, p_step);
|
||||
}
|
||||
|
||||
void BakedLightmapEditorPlugin::bake_func_end() {
|
||||
ERR_FAIL_COND(tmp_progress == NULL);
|
||||
memdelete(tmp_progress);
|
||||
tmp_progress = NULL;
|
||||
}
|
||||
|
||||
void BakedLightmapEditorPlugin::_bind_methods() {
|
||||
|
||||
ClassDB::bind_method("_bake", &BakedLightmapEditorPlugin::_bake);
|
||||
}
|
||||
|
||||
BakedLightmapEditorPlugin::BakedLightmapEditorPlugin(EditorNode *p_node) {
|
||||
|
||||
editor = p_node;
|
||||
bake = memnew(Button);
|
||||
bake->set_icon(editor->get_gui_base()->get_icon("BakedLight", "EditorIcons"));
|
||||
bake->set_text(TTR("Bake Lightmaps"));
|
||||
bake->hide();
|
||||
bake->connect("pressed", this, "_bake");
|
||||
add_control_to_container(CONTAINER_SPATIAL_EDITOR_MENU, bake);
|
||||
lightmap = NULL;
|
||||
|
||||
BakedLightmap::bake_begin_function = bake_func_begin;
|
||||
BakedLightmap::bake_step_function = bake_func_step;
|
||||
BakedLightmap::bake_end_function = bake_func_end;
|
||||
}
|
||||
|
||||
BakedLightmapEditorPlugin::~BakedLightmapEditorPlugin() {
|
||||
}
|
39
editor/plugins/baked_lightmap_editor_plugin.h
Normal file
39
editor/plugins/baked_lightmap_editor_plugin.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
#ifndef BAKED_LIGHTMAP_EDITOR_PLUGIN_H
|
||||
#define BAKED_LIGHTMAP_EDITOR_PLUGIN_H
|
||||
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_plugin.h"
|
||||
#include "scene/3d/baked_lightmap.h"
|
||||
#include "scene/resources/material.h"
|
||||
|
||||
class BakedLightmapEditorPlugin : public EditorPlugin {
|
||||
|
||||
GDCLASS(BakedLightmapEditorPlugin, EditorPlugin);
|
||||
|
||||
BakedLightmap *lightmap;
|
||||
|
||||
Button *bake;
|
||||
EditorNode *editor;
|
||||
|
||||
static EditorProgress *tmp_progress;
|
||||
static void bake_func_begin(int p_steps);
|
||||
static bool bake_func_step(int p_step, const String &p_description);
|
||||
static void bake_func_end();
|
||||
|
||||
void _bake();
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
virtual String get_name() const { return "BakedLightmap"; }
|
||||
bool has_main_screen() const { return false; }
|
||||
virtual void edit(Object *p_object);
|
||||
virtual bool handles(Object *p_object) const;
|
||||
virtual void make_visible(bool p_visible);
|
||||
|
||||
BakedLightmapEditorPlugin(EditorNode *p_node);
|
||||
~BakedLightmapEditorPlugin();
|
||||
};
|
||||
|
||||
#endif // BAKED_LIGHTMAP_EDITOR_PLUGIN_H
|
|
@ -163,7 +163,7 @@ void ProgressDialog::_popup() {
|
|||
popup_centered(ms);
|
||||
}
|
||||
|
||||
void ProgressDialog::add_task(const String &p_task, const String &p_label, int p_steps) {
|
||||
void ProgressDialog::add_task(const String &p_task, const String &p_label, int p_steps, bool p_can_cancel) {
|
||||
|
||||
ERR_FAIL_COND(tasks.has(p_task));
|
||||
Task t;
|
||||
|
@ -180,17 +180,24 @@ void ProgressDialog::add_task(const String &p_task, const String &p_label, int p
|
|||
main->add_child(t.vb);
|
||||
|
||||
tasks[p_task] = t;
|
||||
if (p_can_cancel) {
|
||||
cancel_hb->show();
|
||||
} else {
|
||||
cancel_hb->hide();
|
||||
}
|
||||
cancel_hb->raise();
|
||||
cancelled = false;
|
||||
_popup();
|
||||
}
|
||||
|
||||
void ProgressDialog::task_step(const String &p_task, const String &p_state, int p_step, bool p_force_redraw) {
|
||||
bool ProgressDialog::task_step(const String &p_task, const String &p_state, int p_step, bool p_force_redraw) {
|
||||
|
||||
ERR_FAIL_COND(!tasks.has(p_task));
|
||||
ERR_FAIL_COND_V(!tasks.has(p_task), cancelled);
|
||||
|
||||
if (!p_force_redraw) {
|
||||
uint64_t tus = OS::get_singleton()->get_ticks_usec();
|
||||
if (tus - last_progress_tick < 50000) //50ms
|
||||
return;
|
||||
return cancelled;
|
||||
}
|
||||
|
||||
Task &t = tasks[p_task];
|
||||
|
@ -201,7 +208,11 @@ void ProgressDialog::task_step(const String &p_task, const String &p_state, int
|
|||
|
||||
t.state->set_text(p_state);
|
||||
last_progress_tick = OS::get_singleton()->get_ticks_usec();
|
||||
if (cancel_hb->is_visible()) {
|
||||
OS::get_singleton()->force_process_input();
|
||||
}
|
||||
Main::iteration(); // this will not work on a lot of platforms, so it's only meant for the editor
|
||||
return cancelled;
|
||||
}
|
||||
|
||||
void ProgressDialog::end_task(const String &p_task) {
|
||||
|
@ -218,6 +229,14 @@ void ProgressDialog::end_task(const String &p_task) {
|
|||
_popup();
|
||||
}
|
||||
|
||||
void ProgressDialog::_cancel_pressed() {
|
||||
cancelled = true;
|
||||
}
|
||||
|
||||
void ProgressDialog::_bind_methods() {
|
||||
ClassDB::bind_method("_cancel_pressed", &ProgressDialog::_cancel_pressed);
|
||||
}
|
||||
|
||||
ProgressDialog::ProgressDialog() {
|
||||
|
||||
main = memnew(VBoxContainer);
|
||||
|
@ -226,4 +245,13 @@ ProgressDialog::ProgressDialog() {
|
|||
set_exclusive(true);
|
||||
last_progress_tick = 0;
|
||||
singleton = this;
|
||||
cancel_hb = memnew(HBoxContainer);
|
||||
main->add_child(cancel_hb);
|
||||
cancel_hb->hide();
|
||||
cancel = memnew(Button);
|
||||
cancel_hb->add_spacer();
|
||||
cancel_hb->add_child(cancel);
|
||||
cancel->set_text(TTR("Cancel"));
|
||||
cancel_hb->add_spacer();
|
||||
cancel->connect("pressed", this, "_cancel_pressed");
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#define PROGRESS_DIALOG_H
|
||||
|
||||
#include "scene/gui/box_container.h"
|
||||
#include "scene/gui/button.h"
|
||||
#include "scene/gui/label.h"
|
||||
#include "scene/gui/popup.h"
|
||||
#include "scene/gui/progress_bar.h"
|
||||
|
@ -76,6 +77,8 @@ class ProgressDialog : public Popup {
|
|||
ProgressBar *progress;
|
||||
Label *state;
|
||||
};
|
||||
HBoxContainer *cancel_hb;
|
||||
Button *cancel;
|
||||
|
||||
Map<String, Task> tasks;
|
||||
VBoxContainer *main;
|
||||
|
@ -84,13 +87,17 @@ class ProgressDialog : public Popup {
|
|||
static ProgressDialog *singleton;
|
||||
void _popup();
|
||||
|
||||
void _cancel_pressed();
|
||||
bool cancelled;
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
static ProgressDialog *get_singleton() { return singleton; }
|
||||
void add_task(const String &p_task, const String &p_label, int p_steps);
|
||||
void task_step(const String &p_task, const String &p_state, int p_step = -1, bool p_force_redraw = true);
|
||||
void add_task(const String &p_task, const String &p_label, int p_steps, bool p_can_cancel = false);
|
||||
bool task_step(const String &p_task, const String &p_state, int p_step = -1, bool p_force_redraw = true);
|
||||
void end_task(const String &p_task);
|
||||
|
||||
ProgressDialog();
|
||||
|
|
|
@ -2752,8 +2752,123 @@ GIProbeGizmo::GIProbeGizmo(GIProbe *p_probe) {
|
|||
set_spatial_node(p_probe);
|
||||
}
|
||||
|
||||
////////
|
||||
////////
|
||||
|
||||
///
|
||||
|
||||
String BakedIndirectLightGizmo::get_handle_name(int p_idx) const {
|
||||
|
||||
switch (p_idx) {
|
||||
case 0: return "Extents X";
|
||||
case 1: return "Extents Y";
|
||||
case 2: return "Extents Z";
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
Variant BakedIndirectLightGizmo::get_handle_value(int p_idx) const {
|
||||
|
||||
return baker->get_extents();
|
||||
}
|
||||
void BakedIndirectLightGizmo::set_handle(int p_idx, Camera *p_camera, const Point2 &p_point) {
|
||||
|
||||
Transform gt = baker->get_global_transform();
|
||||
//gt.orthonormalize();
|
||||
Transform gi = gt.affine_inverse();
|
||||
|
||||
Vector3 extents = baker->get_extents();
|
||||
|
||||
Vector3 ray_from = p_camera->project_ray_origin(p_point);
|
||||
Vector3 ray_dir = p_camera->project_ray_normal(p_point);
|
||||
|
||||
Vector3 sg[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 16384) };
|
||||
|
||||
Vector3 axis;
|
||||
axis[p_idx] = 1.0;
|
||||
|
||||
Vector3 ra, rb;
|
||||
Geometry::get_closest_points_between_segments(Vector3(), axis * 16384, sg[0], sg[1], ra, rb);
|
||||
float d = ra[p_idx];
|
||||
if (d < 0.001)
|
||||
d = 0.001;
|
||||
|
||||
extents[p_idx] = d;
|
||||
baker->set_extents(extents);
|
||||
}
|
||||
|
||||
void BakedIndirectLightGizmo::commit_handle(int p_idx, const Variant &p_restore, bool p_cancel) {
|
||||
|
||||
Vector3 restore = p_restore;
|
||||
|
||||
if (p_cancel) {
|
||||
baker->set_extents(restore);
|
||||
return;
|
||||
}
|
||||
|
||||
UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo();
|
||||
ur->create_action(TTR("Change Probe Extents"));
|
||||
ur->add_do_method(baker, "set_extents", baker->get_extents());
|
||||
ur->add_undo_method(baker, "set_extents", restore);
|
||||
ur->commit_action();
|
||||
}
|
||||
|
||||
void BakedIndirectLightGizmo::redraw() {
|
||||
|
||||
Color gizmo_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/baked_indirect_light");
|
||||
Ref<Material> material = create_material("baked_indirect_light_material", gizmo_color);
|
||||
Ref<Material> icon = create_icon_material("baked_indirect_light_icon", SpatialEditor::get_singleton()->get_icon("GizmoGIProbe", "EditorIcons"));
|
||||
Color gizmo_color_internal = gizmo_color;
|
||||
gizmo_color_internal.a = 0.1;
|
||||
Ref<Material> material_internal = create_material("baked_indirect_light_internal_material", gizmo_color_internal);
|
||||
|
||||
clear();
|
||||
|
||||
Vector<Vector3> lines;
|
||||
Vector3 extents = baker->get_extents();
|
||||
|
||||
static const int subdivs[BakedLightmap::SUBDIV_MAX] = { 64, 128, 256, 512 };
|
||||
|
||||
AABB aabb = AABB(-extents, extents * 2);
|
||||
int subdiv = subdivs[baker->get_bake_subdiv()];
|
||||
float cell_size = aabb.get_longest_axis_size() / subdiv;
|
||||
|
||||
for (int i = 0; i < 12; i++) {
|
||||
Vector3 a, b;
|
||||
aabb.get_edge(i, a, b);
|
||||
lines.push_back(a);
|
||||
lines.push_back(b);
|
||||
}
|
||||
|
||||
add_lines(lines, material);
|
||||
add_collision_segments(lines);
|
||||
|
||||
Vector<Vector3> handles;
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
|
||||
Vector3 ax;
|
||||
ax[i] = aabb.position[i] + aabb.size[i];
|
||||
handles.push_back(ax);
|
||||
}
|
||||
|
||||
if (is_selected()) {
|
||||
|
||||
gizmo_color.a = 0.1;
|
||||
Ref<Material> solid_material = create_material("baked_indirect_light_solid_material", gizmo_color);
|
||||
add_solid_box(solid_material, aabb.get_size());
|
||||
}
|
||||
|
||||
add_unscaled_billboard(icon, 0.05);
|
||||
add_handles(handles);
|
||||
}
|
||||
BakedIndirectLightGizmo::BakedIndirectLightGizmo(BakedLightmap *p_baker) {
|
||||
|
||||
baker = p_baker;
|
||||
set_spatial_node(p_baker);
|
||||
}
|
||||
|
||||
////////
|
||||
void NavigationMeshSpatialGizmo::redraw() {
|
||||
|
||||
Ref<Material> edge_material = create_material("navigation_material", EDITOR_GET("editors/3d_gizmos/gizmo_colors/navigation_edge"));
|
||||
|
@ -3409,6 +3524,11 @@ Ref<SpatialEditorGizmo> SpatialEditorGizmos::get_gizmo(Spatial *p_spatial) {
|
|||
Ref<GIProbeGizmo> misg = memnew(GIProbeGizmo(Object::cast_to<GIProbe>(p_spatial)));
|
||||
return misg;
|
||||
}
|
||||
if (Object::cast_to<BakedLightmap>(p_spatial)) {
|
||||
|
||||
Ref<BakedIndirectLightGizmo> misg = memnew(BakedIndirectLightGizmo(Object::cast_to<BakedLightmap>(p_spatial)));
|
||||
return misg;
|
||||
}
|
||||
|
||||
if (Object::cast_to<VehicleWheel>(p_spatial)) {
|
||||
|
||||
|
@ -3495,6 +3615,7 @@ SpatialEditorGizmos::SpatialEditorGizmos() {
|
|||
EDITOR_DEF("editors/3d_gizmos/gizmo_colors/particles", Color(0.8, 0.7, 0.4));
|
||||
EDITOR_DEF("editors/3d_gizmos/gizmo_colors/reflection_probe", Color(0.6, 1, 0.5));
|
||||
EDITOR_DEF("editors/3d_gizmos/gizmo_colors/gi_probe", Color(0.5, 1, 0.6));
|
||||
EDITOR_DEF("editors/3d_gizmos/gizmo_colors/baked_indirect_light", Color(0.5, 0.6, 1));
|
||||
EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1));
|
||||
EDITOR_DEF("editors/3d_gizmos/gizmo_colors/joint", Color(0.5, 0.8, 1));
|
||||
EDITOR_DEF("editors/3d_gizmos/gizmo_colors/navigation_edge", Color(0.5, 1, 1));
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
|
||||
#include "editor/plugins/spatial_editor_plugin.h"
|
||||
#include "scene/3d/audio_stream_player_3d.h"
|
||||
#include "scene/3d/baked_lightmap.h"
|
||||
#include "scene/3d/camera.h"
|
||||
#include "scene/3d/collision_polygon.h"
|
||||
#include "scene/3d/collision_shape.h"
|
||||
|
@ -288,6 +289,22 @@ public:
|
|||
GIProbeGizmo(GIProbe *p_probe = NULL);
|
||||
};
|
||||
|
||||
class BakedIndirectLightGizmo : public EditorSpatialGizmo {
|
||||
|
||||
GDCLASS(BakedIndirectLightGizmo, EditorSpatialGizmo);
|
||||
|
||||
BakedLightmap *baker;
|
||||
|
||||
public:
|
||||
virtual String get_handle_name(int p_idx) const;
|
||||
virtual Variant get_handle_value(int p_idx) const;
|
||||
virtual void set_handle(int p_idx, Camera *p_camera, const Point2 &p_point);
|
||||
virtual void commit_handle(int p_idx, const Variant &p_restore, bool p_cancel = false);
|
||||
|
||||
void redraw();
|
||||
BakedIndirectLightGizmo(BakedLightmap *p_baker = NULL);
|
||||
};
|
||||
|
||||
class CollisionShapeSpatialGizmo : public EditorSpatialGizmo {
|
||||
|
||||
GDCLASS(CollisionShapeSpatialGizmo, EditorSpatialGizmo);
|
||||
|
|
|
@ -42,7 +42,7 @@ bool thekla_mesh_lightmap_unwrap_callback(float p_texel_size, const float *p_ver
|
|||
input_mesh.face_array[i].vertex_index[0] = p_indices[i * 3 + 0];
|
||||
input_mesh.face_array[i].vertex_index[1] = p_indices[i * 3 + 1];
|
||||
input_mesh.face_array[i].vertex_index[2] = p_indices[i * 3 + 2];
|
||||
printf("face %i - %i, %i, %i - mat %i\n", i, input_mesh.face_array[i].vertex_index[0], input_mesh.face_array[i].vertex_index[1], input_mesh.face_array[i].vertex_index[2], p_face_materials[i]);
|
||||
//printf("face %i - %i, %i, %i - mat %i\n", i, input_mesh.face_array[i].vertex_index[0], input_mesh.face_array[i].vertex_index[1], input_mesh.face_array[i].vertex_index[2], p_face_materials[i]);
|
||||
input_mesh.face_array[i].material_index = p_face_materials[i];
|
||||
}
|
||||
input_mesh.vertex_array = new Thekla::Atlas_Input_Vertex[p_vertex_count];
|
||||
|
@ -54,8 +54,8 @@ bool thekla_mesh_lightmap_unwrap_callback(float p_texel_size, const float *p_ver
|
|||
}
|
||||
input_mesh.vertex_array[i].uv[0] = 0;
|
||||
input_mesh.vertex_array[i].uv[1] = 0;
|
||||
printf("vertex %i - %f, %f, %f\n", i, input_mesh.vertex_array[i].position[0], input_mesh.vertex_array[i].position[1], input_mesh.vertex_array[i].position[2]);
|
||||
printf("normal %i - %f, %f, %f\n", i, input_mesh.vertex_array[i].normal[0], input_mesh.vertex_array[i].normal[1], input_mesh.vertex_array[i].normal[2]);
|
||||
//printf("vertex %i - %f, %f, %f\n", i, input_mesh.vertex_array[i].position[0], input_mesh.vertex_array[i].position[1], input_mesh.vertex_array[i].position[2]);
|
||||
//printf("normal %i - %f, %f, %f\n", i, input_mesh.vertex_array[i].normal[0], input_mesh.vertex_array[i].normal[1], input_mesh.vertex_array[i].normal[2]);
|
||||
}
|
||||
input_mesh.face_count = p_index_count / 3;
|
||||
input_mesh.vertex_count = p_vertex_count;
|
||||
|
@ -65,6 +65,7 @@ bool thekla_mesh_lightmap_unwrap_callback(float p_texel_size, const float *p_ver
|
|||
Thekla::atlas_set_default_options(&options);
|
||||
options.packer_options.witness.packing_quality = 1;
|
||||
options.packer_options.witness.texel_area = 1.0 / p_texel_size;
|
||||
options.packer_options.witness.conservative = true;
|
||||
|
||||
//generate
|
||||
Thekla::Atlas_Error err;
|
||||
|
|
|
@ -228,6 +228,8 @@ public:
|
|||
|
||||
virtual Error move_to_trash(const String &p_path);
|
||||
|
||||
void force_process_input();
|
||||
|
||||
OS_OSX();
|
||||
|
||||
private:
|
||||
|
|
|
@ -1971,6 +1971,13 @@ void OS_OSX::push_input(const Ref<InputEvent> &p_event) {
|
|||
input->parse_input_event(ev);
|
||||
}
|
||||
|
||||
void OS_OSX::force_process_input() {
|
||||
|
||||
process_events(); // get rid of pending events
|
||||
joypad_osx->process_joypads();
|
||||
|
||||
}
|
||||
|
||||
void OS_OSX::run() {
|
||||
|
||||
force_quit = false;
|
||||
|
|
|
@ -188,6 +188,9 @@ def configure(env):
|
|||
else:
|
||||
VC_PATH = ""
|
||||
|
||||
if (env["openmp"]):
|
||||
env.Append(CPPFLAGS=['/openmp'])
|
||||
|
||||
env.Append(CCFLAGS=["/I" + p for p in os.getenv("INCLUDE").split(";")])
|
||||
env.Append(LIBPATH=[p for p in os.getenv("LIB").split(";")])
|
||||
|
||||
|
@ -264,6 +267,10 @@ def configure(env):
|
|||
env.Append(CCFLAGS=['-flto'])
|
||||
env.Append(LINKFLAGS=['-flto=' + str(env.GetOption("num_jobs"))])
|
||||
|
||||
if (env["openmp"]):
|
||||
env.Append(CPPFLAGS=['-fopenmp'])
|
||||
env.Append(LIBS=['gomp'])
|
||||
|
||||
## Compile flags
|
||||
|
||||
env.Append(CCFLAGS=['-DWINDOWS_ENABLED', '-mwindows'])
|
||||
|
|
|
@ -2156,6 +2156,10 @@ void OS_Windows::swap_buffers() {
|
|||
gl_context->swap_buffers();
|
||||
}
|
||||
|
||||
void OS_Windows::force_process_input() {
|
||||
process_events(); // get rid of pending events
|
||||
}
|
||||
|
||||
void OS_Windows::run() {
|
||||
|
||||
if (!main_loop)
|
||||
|
|
|
@ -287,6 +287,8 @@ public:
|
|||
void disable_crash_handler();
|
||||
bool is_disable_crash_handler() const;
|
||||
|
||||
void force_process_input();
|
||||
|
||||
virtual Error move_to_trash(const String &p_path);
|
||||
|
||||
OS_Windows(HINSTANCE _hInstance);
|
||||
|
|
|
@ -158,6 +158,7 @@ def configure(env):
|
|||
if not env['builtin_libwebp']:
|
||||
env.ParseConfig('pkg-config libwebp --cflags --libs')
|
||||
|
||||
|
||||
# freetype depends on libpng and zlib, so bundling one of them while keeping others
|
||||
# as shared libraries leads to weird issues
|
||||
if env['builtin_freetype'] or env['builtin_libpng'] or env['builtin_zlib']:
|
||||
|
@ -263,5 +264,10 @@ def configure(env):
|
|||
env.Append(CPPFLAGS=['-m64'])
|
||||
env.Append(LINKFLAGS=['-m64', '-L/usr/lib/i686-linux-gnu'])
|
||||
|
||||
|
||||
if (env["openmp"]):
|
||||
env.Append(CPPFLAGS=['-fopenmp'])
|
||||
env.Append(LIBS=['gomp'])
|
||||
|
||||
if env['use_static_cpp']:
|
||||
env.Append(LINKFLAGS=['-static-libstdc++'])
|
||||
|
|
|
@ -2264,6 +2264,13 @@ void OS_X11::set_icon(const Ref<Image> &p_icon) {
|
|||
XFlush(x11_display);
|
||||
}
|
||||
|
||||
void OS_X11::force_process_input() {
|
||||
process_xevents(); // get rid of pending events
|
||||
#ifdef JOYDEV_ENABLED
|
||||
joypad->process_joypads();
|
||||
#endif
|
||||
}
|
||||
|
||||
void OS_X11::run() {
|
||||
|
||||
force_quit = false;
|
||||
|
|
|
@ -279,6 +279,7 @@ public:
|
|||
|
||||
virtual bool _check_internal_feature_support(const String &p_feature);
|
||||
|
||||
virtual void force_process_input();
|
||||
void run();
|
||||
|
||||
void disable_crash_handler();
|
||||
|
|
717
scene/3d/baked_lightmap.cpp
Normal file
717
scene/3d/baked_lightmap.cpp
Normal file
|
@ -0,0 +1,717 @@
|
|||
#include "baked_lightmap.h"
|
||||
#include "io/resource_saver.h"
|
||||
#include "os/dir_access.h"
|
||||
#include "os/os.h"
|
||||
#include "voxel_light_baker.h"
|
||||
|
||||
void BakedLightmapData::set_bounds(const AABB &p_bounds) {
|
||||
|
||||
bounds = p_bounds;
|
||||
VS::get_singleton()->lightmap_capture_set_bounds(baked_light, p_bounds);
|
||||
}
|
||||
|
||||
AABB BakedLightmapData::get_bounds() const {
|
||||
|
||||
return bounds;
|
||||
}
|
||||
|
||||
void BakedLightmapData::set_octree(const PoolVector<uint8_t> &p_octree) {
|
||||
|
||||
VS::get_singleton()->lightmap_capture_set_octree(baked_light, p_octree);
|
||||
}
|
||||
|
||||
PoolVector<uint8_t> BakedLightmapData::get_octree() const {
|
||||
|
||||
return VS::get_singleton()->lightmap_capture_get_octree(baked_light);
|
||||
}
|
||||
|
||||
void BakedLightmapData::set_cell_space_transform(const Transform &p_xform) {
|
||||
|
||||
cell_space_xform = p_xform;
|
||||
VS::get_singleton()->lightmap_capture_set_octree_cell_transform(baked_light, p_xform);
|
||||
}
|
||||
|
||||
Transform BakedLightmapData::get_cell_space_transform() const {
|
||||
return cell_space_xform;
|
||||
}
|
||||
|
||||
void BakedLightmapData::set_cell_subdiv(int p_cell_subdiv) {
|
||||
cell_subdiv = p_cell_subdiv;
|
||||
VS::get_singleton()->lightmap_capture_set_octree_cell_subdiv(baked_light, p_cell_subdiv);
|
||||
}
|
||||
|
||||
int BakedLightmapData::get_cell_subdiv() const {
|
||||
return cell_subdiv;
|
||||
}
|
||||
|
||||
void BakedLightmapData::set_energy(float p_energy) {
|
||||
|
||||
energy = p_energy;
|
||||
VS::get_singleton()->lightmap_capture_set_energy(baked_light, energy);
|
||||
}
|
||||
|
||||
float BakedLightmapData::get_energy() const {
|
||||
|
||||
return energy;
|
||||
}
|
||||
|
||||
void BakedLightmapData::add_user(const NodePath &p_path, const Ref<Texture> &p_lightmap) {
|
||||
|
||||
ERR_FAIL_COND(p_lightmap.is_null());
|
||||
User user;
|
||||
user.path = p_path;
|
||||
user.lightmap = p_lightmap;
|
||||
users.push_back(user);
|
||||
}
|
||||
|
||||
int BakedLightmapData::get_user_count() const {
|
||||
|
||||
return users.size();
|
||||
}
|
||||
NodePath BakedLightmapData::get_user_path(int p_user) const {
|
||||
|
||||
ERR_FAIL_INDEX_V(p_user, users.size(), NodePath());
|
||||
return users[p_user].path;
|
||||
}
|
||||
Ref<Texture> BakedLightmapData::get_user_lightmap(int p_user) const {
|
||||
|
||||
ERR_FAIL_INDEX_V(p_user, users.size(), Ref<Texture>());
|
||||
return users[p_user].lightmap;
|
||||
}
|
||||
|
||||
void BakedLightmapData::clear_users() {
|
||||
users.clear();
|
||||
}
|
||||
|
||||
void BakedLightmapData::_set_user_data(const Array &p_data) {
|
||||
|
||||
ERR_FAIL_COND(p_data.size() & 1);
|
||||
|
||||
for (int i = 0; i < p_data.size(); i += 2) {
|
||||
add_user(p_data[i], p_data[i + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
Array BakedLightmapData::_get_user_data() const {
|
||||
|
||||
Array ret;
|
||||
for (int i = 0; i < users.size(); i++) {
|
||||
ret.push_back(users[i].path);
|
||||
ret.push_back(users[i].lightmap);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
RID BakedLightmapData::get_rid() const {
|
||||
return baked_light;
|
||||
}
|
||||
void BakedLightmapData::_bind_methods() {
|
||||
|
||||
ClassDB::bind_method(D_METHOD("_set_user_data", "data"), &BakedLightmapData::_set_user_data);
|
||||
ClassDB::bind_method(D_METHOD("_get_user_data"), &BakedLightmapData::_get_user_data);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_bounds", "bounds"), &BakedLightmapData::set_bounds);
|
||||
ClassDB::bind_method(D_METHOD("get_bounds"), &BakedLightmapData::get_bounds);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_cell_space_transform", "xform"), &BakedLightmapData::set_cell_space_transform);
|
||||
ClassDB::bind_method(D_METHOD("get_cell_space_transform"), &BakedLightmapData::get_cell_space_transform);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_cell_subdiv", "cell_subdiv"), &BakedLightmapData::set_cell_subdiv);
|
||||
ClassDB::bind_method(D_METHOD("get_cell_subdiv"), &BakedLightmapData::get_cell_subdiv);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_octree", "octree"), &BakedLightmapData::set_octree);
|
||||
ClassDB::bind_method(D_METHOD("get_octree"), &BakedLightmapData::get_octree);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_energy", "energy"), &BakedLightmapData::set_energy);
|
||||
ClassDB::bind_method(D_METHOD("get_energy"), &BakedLightmapData::get_energy);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("add_user", "path", "lightmap"), &BakedLightmapData::add_user);
|
||||
ClassDB::bind_method(D_METHOD("get_user_count"), &BakedLightmapData::get_user_count);
|
||||
ClassDB::bind_method(D_METHOD("get_user_path", "user_idx"), &BakedLightmapData::get_user_path);
|
||||
ClassDB::bind_method(D_METHOD("get_user_lightmap", "user_idx"), &BakedLightmapData::get_user_lightmap);
|
||||
ClassDB::bind_method(D_METHOD("clear_users"), &BakedLightmapData::clear_users);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::AABB, "bounds", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_bounds", "get_bounds");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::POOL_BYTE_ARRAY, "octree", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_octree", "get_octree");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM, "cell_space_transform", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_cell_space_transform", "get_cell_space_transform");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "cell_subdiv", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_cell_subdiv", "get_cell_subdiv");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::REAL, "energy", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_energy", "get_energy");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "user_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_user_data", "_get_user_data");
|
||||
}
|
||||
|
||||
BakedLightmapData::BakedLightmapData() {
|
||||
|
||||
baked_light = VS::get_singleton()->lightmap_capture_create();
|
||||
energy = 1;
|
||||
cell_subdiv = 1;
|
||||
}
|
||||
|
||||
BakedLightmapData::~BakedLightmapData() {
|
||||
|
||||
VS::get_singleton()->free(baked_light);
|
||||
}
|
||||
|
||||
///////////////////////////
|
||||
|
||||
BakedLightmap::BakeBeginFunc BakedLightmap::bake_begin_function = NULL;
|
||||
BakedLightmap::BakeStepFunc BakedLightmap::bake_step_function = NULL;
|
||||
BakedLightmap::BakeEndFunc BakedLightmap::bake_end_function = NULL;
|
||||
|
||||
void BakedLightmap::set_bake_subdiv(Subdiv p_subdiv) {
|
||||
bake_subdiv = p_subdiv;
|
||||
}
|
||||
|
||||
BakedLightmap::Subdiv BakedLightmap::get_bake_subdiv() const {
|
||||
return bake_subdiv;
|
||||
}
|
||||
|
||||
void BakedLightmap::set_capture_subdiv(Subdiv p_subdiv) {
|
||||
capture_subdiv = p_subdiv;
|
||||
}
|
||||
|
||||
BakedLightmap::Subdiv BakedLightmap::get_capture_subdiv() const {
|
||||
return capture_subdiv;
|
||||
}
|
||||
|
||||
void BakedLightmap::set_extents(const Vector3 &p_extents) {
|
||||
extents = p_extents;
|
||||
}
|
||||
|
||||
Vector3 BakedLightmap::get_extents() const {
|
||||
return extents;
|
||||
}
|
||||
|
||||
void BakedLightmap::_find_meshes_and_lights(Node *p_at_node, List<PlotMesh> &plot_meshes, List<PlotLight> &plot_lights) {
|
||||
|
||||
MeshInstance *mi = Object::cast_to<MeshInstance>(p_at_node);
|
||||
if (mi && mi->get_flag(GeometryInstance::FLAG_USE_BAKED_LIGHT) && mi->is_visible_in_tree()) {
|
||||
Ref<Mesh> mesh = mi->get_mesh();
|
||||
if (mesh.is_valid()) {
|
||||
|
||||
bool all_have_uv2 = true;
|
||||
for (int i = 0; i < mesh->get_surface_count(); i++) {
|
||||
if (!(mesh->surface_get_format(i) & Mesh::ARRAY_FORMAT_TEX_UV2)) {
|
||||
all_have_uv2 = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (all_have_uv2 && mesh->get_lightmap_size_hint() != Size2()) {
|
||||
//READY TO BAKE! size hint could be computed if not found, actually..
|
||||
|
||||
AABB aabb = mesh->get_aabb();
|
||||
|
||||
Transform xf = get_global_transform().affine_inverse() * mi->get_global_transform();
|
||||
|
||||
if (AABB(-extents, extents * 2).intersects(xf.xform(aabb))) {
|
||||
PlotMesh pm;
|
||||
pm.local_xform = xf;
|
||||
pm.mesh = mesh;
|
||||
pm.path = get_path_to(mi);
|
||||
for (int i = 0; i < mesh->get_surface_count(); i++) {
|
||||
pm.instance_materials.push_back(mi->get_surface_material(i));
|
||||
}
|
||||
pm.override_material = mi->get_material_override();
|
||||
plot_meshes.push_back(pm);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Light *light = Object::cast_to<Light>(p_at_node);
|
||||
|
||||
if (light && light->get_bake_mode() != Light::BAKE_DISABLED) {
|
||||
PlotLight pl;
|
||||
Transform xf = get_global_transform().affine_inverse() * light->get_global_transform();
|
||||
|
||||
pl.local_xform = xf;
|
||||
pl.light = light;
|
||||
plot_lights.push_back(pl);
|
||||
}
|
||||
for (int i = 0; i < p_at_node->get_child_count(); i++) {
|
||||
|
||||
Node *child = p_at_node->get_child(i);
|
||||
if (!child->get_owner())
|
||||
continue; //maybe a helper
|
||||
|
||||
_find_meshes_and_lights(child, plot_meshes, plot_lights);
|
||||
}
|
||||
}
|
||||
|
||||
void BakedLightmap::set_hdr(bool p_enable) {
|
||||
hdr = p_enable;
|
||||
}
|
||||
|
||||
bool BakedLightmap::is_hdr() const {
|
||||
return hdr;
|
||||
}
|
||||
|
||||
bool BakedLightmap::_bake_time(void *ud, float p_secs, float p_progress) {
|
||||
|
||||
uint64_t time = OS::get_singleton()->get_ticks_usec();
|
||||
BakeTimeData *btd = (BakeTimeData *)ud;
|
||||
|
||||
if (time - btd->last_step > 1000000) {
|
||||
|
||||
int mins_left = p_secs / 60;
|
||||
int secs_left = Math::fmod(p_secs, 60.0);
|
||||
int percent = p_progress * 100;
|
||||
bool abort = bake_step_function(btd->pass + percent, btd->text + " " + itos(percent) + "% (Time Left: " + itos(mins_left) + ":" + itos(secs_left) + "s)");
|
||||
btd->last_step = time;
|
||||
if (abort)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
BakedLightmap::BakeError BakedLightmap::bake(Node *p_from_node, bool p_create_visual_debug) {
|
||||
|
||||
String save_path;
|
||||
|
||||
if (image_path.begins_with("res://")) {
|
||||
save_path = image_path;
|
||||
} else {
|
||||
if (get_filename() != "") {
|
||||
save_path = get_filename().get_base_dir();
|
||||
} else if (get_owner() && get_owner()->get_filename() != "") {
|
||||
save_path = get_owner()->get_filename().get_base_dir();
|
||||
}
|
||||
|
||||
if (save_path == "") {
|
||||
return BAKE_ERROR_NO_SAVE_PATH;
|
||||
}
|
||||
if (image_path != "") {
|
||||
save_path.plus_file(image_path);
|
||||
}
|
||||
}
|
||||
{
|
||||
//check for valid save path
|
||||
DirAccessRef d = DirAccess::open(save_path);
|
||||
if (!d) {
|
||||
ERR_PRINTS("Invalid Save Path: " + save_path);
|
||||
return BAKE_ERROR_NO_SAVE_PATH;
|
||||
}
|
||||
}
|
||||
|
||||
Ref<BakedLightmapData> new_light_data;
|
||||
new_light_data.instance();
|
||||
|
||||
static const int subdiv_value[SUBDIV_MAX] = { 8, 9, 10, 11, 12, 13 };
|
||||
|
||||
VoxelLightBaker baker;
|
||||
|
||||
baker.begin_bake(subdiv_value[bake_subdiv], AABB(-extents, extents * 2.0));
|
||||
|
||||
List<PlotMesh> mesh_list;
|
||||
List<PlotLight> light_list;
|
||||
|
||||
_find_meshes_and_lights(p_from_node ? p_from_node : get_parent(), mesh_list, light_list);
|
||||
|
||||
if (bake_begin_function) {
|
||||
bake_begin_function(mesh_list.size() + light_list.size() + 1 + mesh_list.size() * 100);
|
||||
}
|
||||
|
||||
int step = 0;
|
||||
|
||||
int pmc = 0;
|
||||
|
||||
for (List<PlotMesh>::Element *E = mesh_list.front(); E; E = E->next()) {
|
||||
|
||||
if (bake_step_function) {
|
||||
bake_step_function(step++, RTR("Plotting Meshes: ") + " (" + itos(pmc + 1) + "/" + itos(mesh_list.size()) + ")");
|
||||
}
|
||||
|
||||
pmc++;
|
||||
baker.plot_mesh(E->get().local_xform, E->get().mesh, E->get().instance_materials, E->get().override_material);
|
||||
}
|
||||
|
||||
pmc = 0;
|
||||
baker.begin_bake_light(VoxelLightBaker::BakeQuality(bake_quality), VoxelLightBaker::BakeMode(bake_mode), propagation, energy);
|
||||
|
||||
for (List<PlotLight>::Element *E = light_list.front(); E; E = E->next()) {
|
||||
|
||||
if (bake_step_function) {
|
||||
bake_step_function(step++, RTR("Plotting Lights:") + " (" + itos(pmc + 1) + "/" + itos(light_list.size()) + ")");
|
||||
}
|
||||
|
||||
pmc++;
|
||||
PlotLight pl = E->get();
|
||||
switch (pl.light->get_light_type()) {
|
||||
case VS::LIGHT_DIRECTIONAL: {
|
||||
baker.plot_light_directional(-pl.local_xform.basis.get_axis(2), pl.light->get_color(), pl.light->get_param(Light::PARAM_ENERGY), pl.light->get_param(Light::PARAM_INDIRECT_ENERGY), pl.light->get_bake_mode() == Light::BAKE_ALL);
|
||||
} break;
|
||||
case VS::LIGHT_OMNI: {
|
||||
baker.plot_light_omni(pl.local_xform.origin, pl.light->get_color(), pl.light->get_param(Light::PARAM_ENERGY), pl.light->get_param(Light::PARAM_INDIRECT_ENERGY), pl.light->get_param(Light::PARAM_RANGE), pl.light->get_param(Light::PARAM_ATTENUATION), pl.light->get_bake_mode() == Light::BAKE_ALL);
|
||||
} break;
|
||||
case VS::LIGHT_SPOT: {
|
||||
baker.plot_light_spot(pl.local_xform.origin, pl.local_xform.basis.get_axis(2), pl.light->get_color(), pl.light->get_param(Light::PARAM_ENERGY), pl.light->get_param(Light::PARAM_INDIRECT_ENERGY), pl.light->get_param(Light::PARAM_RANGE), pl.light->get_param(Light::PARAM_ATTENUATION), pl.light->get_param(Light::PARAM_SPOT_ANGLE), pl.light->get_param(Light::PARAM_SPOT_ATTENUATION), pl.light->get_bake_mode() == Light::BAKE_ALL);
|
||||
|
||||
} break;
|
||||
}
|
||||
}
|
||||
/*if (bake_step_function) {
|
||||
bake_step_function(pmc++, RTR("Finishing Plot"));
|
||||
}*/
|
||||
|
||||
baker.end_bake();
|
||||
|
||||
Set<String> used_mesh_names;
|
||||
|
||||
pmc = 0;
|
||||
for (List<PlotMesh>::Element *E = mesh_list.front(); E; E = E->next()) {
|
||||
|
||||
String mesh_name = E->get().mesh->get_name();
|
||||
if (mesh_name == "" || mesh_name.find(":") != -1 || mesh_name.find("/") != -1) {
|
||||
mesh_name = "LightMap";
|
||||
}
|
||||
|
||||
if (used_mesh_names.has(mesh_name)) {
|
||||
int idx = 2;
|
||||
String base = mesh_name;
|
||||
while (true) {
|
||||
mesh_name = base + itos(idx);
|
||||
if (!used_mesh_names.has(mesh_name))
|
||||
break;
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
used_mesh_names.insert(mesh_name);
|
||||
|
||||
pmc++;
|
||||
VoxelLightBaker::LightMapData lm;
|
||||
|
||||
Error err;
|
||||
if (bake_step_function) {
|
||||
BakeTimeData btd;
|
||||
btd.text = RTR("Lighting Meshes: ") + mesh_name + " (" + itos(pmc) + "/" + itos(mesh_list.size()) + ")";
|
||||
btd.pass = step;
|
||||
btd.last_step = 0;
|
||||
err = baker.make_lightmap(E->get().local_xform, E->get().mesh, lm, _bake_time, &btd);
|
||||
if (err != OK) {
|
||||
bake_end_function();
|
||||
if (err == ERR_SKIP)
|
||||
return BAKE_ERROR_USER_ABORTED;
|
||||
return BAKE_ERROR_CANT_CREATE_IMAGE;
|
||||
}
|
||||
step += 100;
|
||||
} else {
|
||||
|
||||
err = baker.make_lightmap(E->get().local_xform, E->get().mesh, lm);
|
||||
}
|
||||
|
||||
if (err == OK) {
|
||||
|
||||
Ref<Image> image;
|
||||
image.instance();
|
||||
|
||||
uint32_t tex_flags = Texture::FLAGS_DEFAULT;
|
||||
if (hdr) {
|
||||
|
||||
//just save a regular image
|
||||
PoolVector<uint8_t> data;
|
||||
int s = lm.light.size();
|
||||
data.resize(lm.light.size() * 2);
|
||||
{
|
||||
|
||||
PoolVector<uint8_t>::Write w = data.write();
|
||||
PoolVector<float>::Read r = lm.light.read();
|
||||
uint16_t *hfw = (uint16_t *)w.ptr();
|
||||
for (int i = 0; i < s; i++) {
|
||||
hfw[i] = Math::make_half_float(r[i]);
|
||||
}
|
||||
}
|
||||
|
||||
image->create(lm.width, lm.height, false, Image::FORMAT_RGBH, data);
|
||||
|
||||
} else {
|
||||
|
||||
//just save a regular image
|
||||
PoolVector<uint8_t> data;
|
||||
int s = lm.light.size();
|
||||
data.resize(lm.light.size());
|
||||
{
|
||||
|
||||
PoolVector<uint8_t>::Write w = data.write();
|
||||
PoolVector<float>::Read r = lm.light.read();
|
||||
for (int i = 0; i < s; i += 3) {
|
||||
Color c(r[i + 0], r[i + 1], r[i + 2]);
|
||||
c = c.to_srgb();
|
||||
w[i + 0] = CLAMP(c.r * 255, 0, 255);
|
||||
w[i + 1] = CLAMP(c.g * 255, 0, 255);
|
||||
w[i + 2] = CLAMP(c.b * 255, 0, 255);
|
||||
}
|
||||
}
|
||||
|
||||
image->create(lm.width, lm.height, false, Image::FORMAT_RGB8, data);
|
||||
|
||||
//This texture is saved to SRGB for two reasons:
|
||||
// 1) first is so it looks better when doing the LINEAR->SRGB conversion (more accurate)
|
||||
// 2) So it can be used in the GLES2 backend, which does not support linkear workflow
|
||||
tex_flags |= Texture::FLAG_CONVERT_TO_LINEAR;
|
||||
}
|
||||
|
||||
Ref<ImageTexture> tex;
|
||||
String image_path = save_path.plus_file(mesh_name + ".tex");
|
||||
bool set_path = true;
|
||||
if (ResourceCache::has(image_path)) {
|
||||
tex = Ref<Resource>((Resource *)ResourceCache::get(image_path));
|
||||
set_path = false;
|
||||
}
|
||||
|
||||
if (!tex.is_valid()) {
|
||||
tex.instance();
|
||||
}
|
||||
|
||||
tex->create_from_image(image, tex_flags);
|
||||
|
||||
err = ResourceSaver::save(image_path, tex, ResourceSaver::FLAG_CHANGE_PATH);
|
||||
if (err != OK) {
|
||||
if (bake_end_function) {
|
||||
bake_end_function();
|
||||
}
|
||||
ERR_FAIL_COND_V(err != OK, BAKE_ERROR_CANT_CREATE_IMAGE);
|
||||
}
|
||||
|
||||
if (set_path) {
|
||||
tex->set_path(image_path);
|
||||
}
|
||||
new_light_data->add_user(E->get().path, tex);
|
||||
}
|
||||
}
|
||||
|
||||
int csubdiv = subdiv_value[capture_subdiv];
|
||||
AABB bounds = AABB(-extents, extents * 2);
|
||||
new_light_data->set_cell_subdiv(csubdiv);
|
||||
new_light_data->set_bounds(bounds);
|
||||
new_light_data->set_octree(baker.create_capture_octree(csubdiv));
|
||||
{
|
||||
|
||||
Transform to_bounds;
|
||||
to_bounds.basis.scale(Vector3(bounds.get_longest_axis_size(), bounds.get_longest_axis_size(), bounds.get_longest_axis_size()));
|
||||
to_bounds.origin = bounds.position;
|
||||
|
||||
Transform to_grid;
|
||||
to_grid.basis.scale(Vector3(1 << (csubdiv - 1), 1 << (csubdiv - 1), 1 << (csubdiv - 1)));
|
||||
|
||||
Transform to_cell_space = to_grid * to_bounds.affine_inverse();
|
||||
new_light_data->set_cell_space_transform(to_cell_space);
|
||||
}
|
||||
|
||||
if (bake_end_function) {
|
||||
bake_end_function();
|
||||
}
|
||||
|
||||
//create the data for visual server
|
||||
|
||||
if (p_create_visual_debug) {
|
||||
MultiMeshInstance *mmi = memnew(MultiMeshInstance);
|
||||
mmi->set_multimesh(baker.create_debug_multimesh(VoxelLightBaker::DEBUG_LIGHT));
|
||||
add_child(mmi);
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (get_tree()->get_edited_scene_root() == this) {
|
||||
mmi->set_owner(this);
|
||||
} else {
|
||||
mmi->set_owner(get_owner());
|
||||
}
|
||||
#else
|
||||
mmi->set_owner(get_owner());
|
||||
#endif
|
||||
}
|
||||
|
||||
set_light_data(new_light_data);
|
||||
|
||||
return BAKE_ERROR_OK;
|
||||
}
|
||||
|
||||
void BakedLightmap::_notification(int p_what) {
|
||||
if (p_what == NOTIFICATION_READY) {
|
||||
|
||||
if (light_data.is_valid()) {
|
||||
_assign_lightmaps();
|
||||
}
|
||||
request_ready(); //will need ready again if re-enters tree
|
||||
}
|
||||
|
||||
if (p_what == NOTIFICATION_EXIT_TREE) {
|
||||
|
||||
if (light_data.is_valid()) {
|
||||
_clear_lightmaps();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BakedLightmap::_assign_lightmaps() {
|
||||
|
||||
ERR_FAIL_COND(!light_data.is_valid());
|
||||
|
||||
for (int i = 0; i < light_data->get_user_count(); i++) {
|
||||
Node *node = get_node(light_data->get_user_path(i));
|
||||
VisualInstance *vi = Object::cast_to<VisualInstance>(node);
|
||||
ERR_CONTINUE(!vi);
|
||||
Ref<Texture> lightmap = light_data->get_user_lightmap(i);
|
||||
ERR_CONTINUE(!lightmap.is_valid());
|
||||
VS::get_singleton()->instance_set_use_lightmap(vi->get_instance(), get_instance(), lightmap->get_rid());
|
||||
}
|
||||
}
|
||||
|
||||
void BakedLightmap::_clear_lightmaps() {
|
||||
ERR_FAIL_COND(!light_data.is_valid());
|
||||
for (int i = 0; i < light_data->get_user_count(); i++) {
|
||||
Node *node = get_node(light_data->get_user_path(i));
|
||||
VisualInstance *vi = Object::cast_to<VisualInstance>(node);
|
||||
ERR_CONTINUE(!vi);
|
||||
VS::get_singleton()->instance_set_use_lightmap(vi->get_instance(), RID(), RID());
|
||||
}
|
||||
}
|
||||
|
||||
void BakedLightmap::set_light_data(const Ref<BakedLightmapData> &p_data) {
|
||||
|
||||
if (light_data.is_valid()) {
|
||||
if (is_inside_tree()) {
|
||||
_clear_lightmaps();
|
||||
}
|
||||
set_base(RID());
|
||||
}
|
||||
light_data = p_data;
|
||||
|
||||
if (light_data.is_valid()) {
|
||||
set_base(light_data->get_rid());
|
||||
if (is_inside_tree()) {
|
||||
_assign_lightmaps();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ref<BakedLightmapData> BakedLightmap::get_light_data() const {
|
||||
|
||||
return light_data;
|
||||
}
|
||||
|
||||
void BakedLightmap::_debug_bake() {
|
||||
bake(get_parent(), true);
|
||||
}
|
||||
|
||||
void BakedLightmap::set_propagation(float p_propagation) {
|
||||
propagation = p_propagation;
|
||||
}
|
||||
|
||||
float BakedLightmap::get_propagation() const {
|
||||
|
||||
return propagation;
|
||||
}
|
||||
|
||||
void BakedLightmap::set_energy(float p_energy) {
|
||||
energy = p_energy;
|
||||
}
|
||||
|
||||
float BakedLightmap::get_energy() const {
|
||||
|
||||
return energy;
|
||||
}
|
||||
|
||||
void BakedLightmap::set_bake_quality(BakeQuality p_quality) {
|
||||
bake_quality = p_quality;
|
||||
}
|
||||
|
||||
BakedLightmap::BakeQuality BakedLightmap::get_bake_quality() const {
|
||||
return bake_quality;
|
||||
}
|
||||
|
||||
void BakedLightmap::set_bake_mode(BakeMode p_mode) {
|
||||
bake_mode = p_mode;
|
||||
}
|
||||
|
||||
BakedLightmap::BakeMode BakedLightmap::get_bake_mode() const {
|
||||
return bake_mode;
|
||||
}
|
||||
|
||||
void BakedLightmap::set_image_path(const String &p_path) {
|
||||
image_path = p_path;
|
||||
}
|
||||
|
||||
String BakedLightmap::get_image_path() const {
|
||||
return image_path;
|
||||
}
|
||||
|
||||
AABB BakedLightmap::get_aabb() const {
|
||||
return AABB(-extents, extents * 2);
|
||||
}
|
||||
PoolVector<Face3> BakedLightmap::get_faces(uint32_t p_usage_flags) const {
|
||||
return PoolVector<Face3>();
|
||||
}
|
||||
|
||||
void BakedLightmap::_bind_methods() {
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_light_data", "data"), &BakedLightmap::set_light_data);
|
||||
ClassDB::bind_method(D_METHOD("get_light_data"), &BakedLightmap::get_light_data);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_bake_subdiv", "bake_subdiv"), &BakedLightmap::set_bake_subdiv);
|
||||
ClassDB::bind_method(D_METHOD("get_bake_subdiv"), &BakedLightmap::get_bake_subdiv);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_capture_subdiv", "capture_subdiv"), &BakedLightmap::set_capture_subdiv);
|
||||
ClassDB::bind_method(D_METHOD("get_capture_subdiv"), &BakedLightmap::get_capture_subdiv);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_bake_quality", "bake_quality"), &BakedLightmap::set_bake_quality);
|
||||
ClassDB::bind_method(D_METHOD("get_bake_quality"), &BakedLightmap::get_bake_quality);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_bake_mode", "bake_mode"), &BakedLightmap::set_bake_mode);
|
||||
ClassDB::bind_method(D_METHOD("get_bake_mode"), &BakedLightmap::get_bake_mode);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_extents", "extents"), &BakedLightmap::set_extents);
|
||||
ClassDB::bind_method(D_METHOD("get_extents"), &BakedLightmap::get_extents);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_propagation", "propagation"), &BakedLightmap::set_propagation);
|
||||
ClassDB::bind_method(D_METHOD("get_propagation"), &BakedLightmap::get_propagation);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_energy", "energy"), &BakedLightmap::set_energy);
|
||||
ClassDB::bind_method(D_METHOD("get_energy"), &BakedLightmap::get_energy);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_hdr", "hdr"), &BakedLightmap::set_hdr);
|
||||
ClassDB::bind_method(D_METHOD("is_hdr"), &BakedLightmap::is_hdr);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_image_path", "image_path"), &BakedLightmap::set_image_path);
|
||||
ClassDB::bind_method(D_METHOD("get_image_path"), &BakedLightmap::get_image_path);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("bake", "from_node", "create_visual_debug"), &BakedLightmap::bake, DEFVAL(Variant()), DEFVAL(false));
|
||||
ClassDB::bind_method(D_METHOD("debug_bake"), &BakedLightmap::_debug_bake);
|
||||
ClassDB::set_method_flags(get_class_static(), _scs_create("debug_bake"), METHOD_FLAGS_DEFAULT | METHOD_FLAG_EDITOR);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "bake_subdiv", PROPERTY_HINT_ENUM, "128,256,512,1024,2048,4096"), "set_bake_subdiv", "get_bake_subdiv");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "capture_subdiv", PROPERTY_HINT_ENUM, "128,256,512"), "set_capture_subdiv", "get_capture_subdiv");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "bake_quality", PROPERTY_HINT_ENUM, "Low,Medium,High"), "set_bake_quality", "get_bake_quality");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "bake_mode", PROPERTY_HINT_ENUM, "ConeTrace,RayTrace"), "set_bake_mode", "get_bake_mode");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::REAL, "propagation", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_propagation", "get_propagation");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::REAL, "energy", PROPERTY_HINT_RANGE, "0,32,0.01"), "set_energy", "get_energy");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hdr"), "set_hdr", "is_hdr");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::STRING, "image_path", PROPERTY_HINT_DIR), "set_image_path", "get_image_path");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents"), "set_extents", "get_extents");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "light_data", PROPERTY_HINT_RESOURCE_TYPE, "BakedIndirectLightData"), "set_light_data", "get_light_data");
|
||||
|
||||
BIND_ENUM_CONSTANT(SUBDIV_128);
|
||||
BIND_ENUM_CONSTANT(SUBDIV_256);
|
||||
BIND_ENUM_CONSTANT(SUBDIV_512);
|
||||
BIND_ENUM_CONSTANT(SUBDIV_1024);
|
||||
BIND_ENUM_CONSTANT(SUBDIV_2048);
|
||||
BIND_ENUM_CONSTANT(SUBDIV_4096);
|
||||
BIND_ENUM_CONSTANT(SUBDIV_MAX);
|
||||
|
||||
BIND_ENUM_CONSTANT(BAKE_QUALITY_LOW);
|
||||
BIND_ENUM_CONSTANT(BAKE_QUALITY_MEDIUM);
|
||||
BIND_ENUM_CONSTANT(BAKE_QUALITY_HIGH);
|
||||
BIND_ENUM_CONSTANT(BAKE_MODE_CONE_TRACE);
|
||||
BIND_ENUM_CONSTANT(BAKE_MODE_RAY_TRACE);
|
||||
}
|
||||
|
||||
BakedLightmap::BakedLightmap() {
|
||||
|
||||
extents = Vector3(10, 10, 10);
|
||||
bake_subdiv = SUBDIV_256;
|
||||
capture_subdiv = SUBDIV_128;
|
||||
bake_quality = BAKE_QUALITY_MEDIUM;
|
||||
bake_mode = BAKE_MODE_CONE_TRACE;
|
||||
energy = 1;
|
||||
propagation = 1;
|
||||
hdr = false;
|
||||
image_path = ".";
|
||||
}
|
189
scene/3d/baked_lightmap.h
Normal file
189
scene/3d/baked_lightmap.h
Normal file
|
@ -0,0 +1,189 @@
|
|||
#ifndef BAKED_INDIRECT_LIGHT_H
|
||||
#define BAKED_INDIRECT_LIGHT_H
|
||||
|
||||
#include "multimesh_instance.h"
|
||||
#include "scene/3d/light.h"
|
||||
#include "scene/3d/visual_instance.h"
|
||||
|
||||
class BakedLightmapData : public Resource {
|
||||
GDCLASS(BakedLightmapData, Resource);
|
||||
|
||||
RID baked_light;
|
||||
AABB bounds;
|
||||
float energy;
|
||||
int cell_subdiv;
|
||||
Transform cell_space_xform;
|
||||
|
||||
struct User {
|
||||
|
||||
NodePath path;
|
||||
Ref<Texture> lightmap;
|
||||
};
|
||||
|
||||
Vector<User> users;
|
||||
|
||||
void _set_user_data(const Array &p_data);
|
||||
Array _get_user_data() const;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void set_bounds(const AABB &p_bounds);
|
||||
AABB get_bounds() const;
|
||||
|
||||
void set_octree(const PoolVector<uint8_t> &p_octree);
|
||||
PoolVector<uint8_t> get_octree() const;
|
||||
|
||||
void set_cell_space_transform(const Transform &p_xform);
|
||||
Transform get_cell_space_transform() const;
|
||||
|
||||
void set_cell_subdiv(int p_cell_subdiv);
|
||||
int get_cell_subdiv() const;
|
||||
|
||||
void set_energy(float p_energy);
|
||||
float get_energy() const;
|
||||
|
||||
void add_user(const NodePath &p_path, const Ref<Texture> &p_lightmap);
|
||||
int get_user_count() const;
|
||||
NodePath get_user_path(int p_user) const;
|
||||
Ref<Texture> get_user_lightmap(int p_user) const;
|
||||
void clear_users();
|
||||
|
||||
virtual RID get_rid() const;
|
||||
BakedLightmapData();
|
||||
~BakedLightmapData();
|
||||
};
|
||||
|
||||
class BakedLightmap : public VisualInstance {
|
||||
GDCLASS(BakedLightmap, VisualInstance);
|
||||
|
||||
public:
|
||||
enum Subdiv {
|
||||
SUBDIV_128,
|
||||
SUBDIV_256,
|
||||
SUBDIV_512,
|
||||
SUBDIV_1024,
|
||||
SUBDIV_2048,
|
||||
SUBDIV_4096,
|
||||
SUBDIV_MAX
|
||||
|
||||
};
|
||||
|
||||
enum BakeQuality {
|
||||
BAKE_QUALITY_LOW,
|
||||
BAKE_QUALITY_MEDIUM,
|
||||
BAKE_QUALITY_HIGH
|
||||
};
|
||||
|
||||
enum BakeMode {
|
||||
BAKE_MODE_CONE_TRACE,
|
||||
BAKE_MODE_RAY_TRACE,
|
||||
};
|
||||
|
||||
enum BakeError {
|
||||
BAKE_ERROR_OK,
|
||||
BAKE_ERROR_NO_SAVE_PATH,
|
||||
BAKE_ERROR_NO_MESHES,
|
||||
BAKE_ERROR_CANT_CREATE_IMAGE,
|
||||
BAKE_ERROR_USER_ABORTED
|
||||
|
||||
};
|
||||
|
||||
typedef void (*BakeBeginFunc)(int);
|
||||
typedef bool (*BakeStepFunc)(int, const String &);
|
||||
typedef void (*BakeEndFunc)();
|
||||
|
||||
private:
|
||||
Subdiv bake_subdiv;
|
||||
Subdiv capture_subdiv;
|
||||
Vector3 extents;
|
||||
float propagation;
|
||||
float energy;
|
||||
BakeQuality bake_quality;
|
||||
BakeMode bake_mode;
|
||||
bool hdr;
|
||||
String image_path;
|
||||
|
||||
Ref<BakedLightmapData> light_data;
|
||||
|
||||
struct PlotMesh {
|
||||
Ref<Material> override_material;
|
||||
Vector<Ref<Material> > instance_materials;
|
||||
Ref<Mesh> mesh;
|
||||
Transform local_xform;
|
||||
NodePath path;
|
||||
};
|
||||
|
||||
struct PlotLight {
|
||||
Light *light;
|
||||
Transform local_xform;
|
||||
};
|
||||
|
||||
void _find_meshes_and_lights(Node *p_at_node, List<PlotMesh> &plot_meshes, List<PlotLight> &plot_lights);
|
||||
|
||||
void _debug_bake();
|
||||
|
||||
void _assign_lightmaps();
|
||||
void _clear_lightmaps();
|
||||
|
||||
static bool _bake_time(void *ud, float p_secs, float p_progress);
|
||||
|
||||
struct BakeTimeData {
|
||||
String text;
|
||||
int pass;
|
||||
uint64_t last_step;
|
||||
};
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
void _notification(int p_what);
|
||||
|
||||
public:
|
||||
static BakeBeginFunc bake_begin_function;
|
||||
static BakeStepFunc bake_step_function;
|
||||
static BakeEndFunc bake_end_function;
|
||||
|
||||
void set_light_data(const Ref<BakedLightmapData> &p_data);
|
||||
Ref<BakedLightmapData> get_light_data() const;
|
||||
|
||||
void set_bake_subdiv(Subdiv p_subdiv);
|
||||
Subdiv get_bake_subdiv() const;
|
||||
|
||||
void set_capture_subdiv(Subdiv p_subdiv);
|
||||
Subdiv get_capture_subdiv() const;
|
||||
|
||||
void set_extents(const Vector3 &p_extents);
|
||||
Vector3 get_extents() const;
|
||||
|
||||
void set_propagation(float p_propagation);
|
||||
float get_propagation() const;
|
||||
|
||||
void set_energy(float p_energy);
|
||||
float get_energy() const;
|
||||
|
||||
void set_bake_quality(BakeQuality p_quality);
|
||||
BakeQuality get_bake_quality() const;
|
||||
|
||||
void set_bake_mode(BakeMode p_mode);
|
||||
BakeMode get_bake_mode() const;
|
||||
|
||||
void set_hdr(bool p_enable);
|
||||
bool is_hdr() const;
|
||||
|
||||
void set_image_path(const String &p_path);
|
||||
String get_image_path() const;
|
||||
|
||||
AABB get_aabb() const;
|
||||
PoolVector<Face3> get_faces(uint32_t p_usage_flags) const;
|
||||
|
||||
BakeError bake(Node *p_from_node, bool p_create_visual_debug = false);
|
||||
BakedLightmap();
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(BakedLightmap::Subdiv);
|
||||
VARIANT_ENUM_CAST(BakedLightmap::BakeQuality);
|
||||
VARIANT_ENUM_CAST(BakedLightmap::BakeMode);
|
||||
VARIANT_ENUM_CAST(BakedLightmap::BakeError);
|
||||
|
||||
#endif // BAKED_INDIRECT_LIGHT_H
|
File diff suppressed because it is too large
Load diff
|
@ -100,67 +100,6 @@ public:
|
|||
typedef void (*BakeEndFunc)();
|
||||
|
||||
private:
|
||||
//stuff used for bake
|
||||
struct Baker {
|
||||
|
||||
enum {
|
||||
CHILD_EMPTY = 0xFFFFFFFF
|
||||
};
|
||||
struct Cell {
|
||||
|
||||
uint32_t childs[8];
|
||||
float albedo[3]; //albedo in RGB24
|
||||
float emission[3]; //accumulated light in 16:16 fixed point (needs to be integer for moving lights fast)
|
||||
float normal[3];
|
||||
uint32_t used_sides;
|
||||
float alpha; //used for upsampling
|
||||
int level;
|
||||
|
||||
Cell() {
|
||||
for (int i = 0; i < 8; i++) {
|
||||
childs[i] = CHILD_EMPTY;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
emission[i] = 0;
|
||||
albedo[i] = 0;
|
||||
normal[i] = 0;
|
||||
}
|
||||
alpha = 0;
|
||||
used_sides = 0;
|
||||
level = 0;
|
||||
}
|
||||
};
|
||||
|
||||
Vector<Cell> bake_cells;
|
||||
int cell_subdiv;
|
||||
|
||||
struct MaterialCache {
|
||||
//128x128 textures
|
||||
Vector<Color> albedo;
|
||||
Vector<Color> emission;
|
||||
};
|
||||
|
||||
Vector<Color> _get_bake_texture(Ref<Image> p_image, const Color &p_color);
|
||||
Map<Ref<Material>, MaterialCache> material_cache;
|
||||
MaterialCache _get_material_cache(Ref<Material> p_material);
|
||||
int leaf_voxel_count;
|
||||
|
||||
AABB po2_bounds;
|
||||
int axis_cell_size[3];
|
||||
|
||||
struct PlotMesh {
|
||||
Ref<Material> override_material;
|
||||
Vector<Ref<Material> > instance_materials;
|
||||
Ref<Mesh> mesh;
|
||||
Transform local_xform;
|
||||
};
|
||||
|
||||
Transform to_cell_space;
|
||||
|
||||
List<PlotMesh> mesh_list;
|
||||
};
|
||||
|
||||
Ref<GIProbeData> probe_data;
|
||||
|
||||
RID gi_probe;
|
||||
|
@ -175,19 +114,14 @@ private:
|
|||
bool interior;
|
||||
bool compress;
|
||||
|
||||
int color_scan_cell_width;
|
||||
int bake_texture_size;
|
||||
|
||||
Vector<Color> _get_bake_texture(Ref<Image> p_image, const Color &p_color_mul, const Color &p_color_add);
|
||||
Baker::MaterialCache _get_material_cache(Ref<Material> p_material, Baker *p_baker);
|
||||
void _plot_face(int p_idx, int p_level, int p_x, int p_y, int p_z, const Vector3 *p_vtx, const Vector2 *p_uv, const Baker::MaterialCache &p_material, const AABB &p_aabb, Baker *p_baker);
|
||||
void _plot_mesh(const Transform &p_xform, Ref<Mesh> &p_mesh, Baker *p_baker, const Vector<Ref<Material> > &p_materials, const Ref<Material> &p_override_material);
|
||||
void _find_meshes(Node *p_at_node, Baker *p_baker);
|
||||
void _fixup_plot(int p_idx, int p_level, int p_x, int p_y, int p_z, Baker *p_baker);
|
||||
|
||||
void _debug_mesh(int p_idx, int p_level, const AABB &p_aabb, Ref<MultiMesh> &p_multimesh, int &idx, Baker *p_baker);
|
||||
void _create_debug_mesh(Baker *p_baker);
|
||||
struct PlotMesh {
|
||||
Ref<Material> override_material;
|
||||
Vector<Ref<Material> > instance_materials;
|
||||
Ref<Mesh> mesh;
|
||||
Transform local_xform;
|
||||
};
|
||||
|
||||
void _find_meshes(Node *p_at_node, List<PlotMesh> &plot_meshes);
|
||||
void _debug_bake();
|
||||
|
||||
protected:
|
||||
|
|
|
@ -142,6 +142,14 @@ PoolVector<Face3> Light::get_faces(uint32_t p_usage_flags) const {
|
|||
return PoolVector<Face3>();
|
||||
}
|
||||
|
||||
void Light::set_bake_mode(BakeMode p_mode) {
|
||||
bake_mode = p_mode;
|
||||
}
|
||||
|
||||
Light::BakeMode Light::get_bake_mode() const {
|
||||
return bake_mode;
|
||||
}
|
||||
|
||||
void Light::_update_visibility() {
|
||||
|
||||
if (!is_inside_tree())
|
||||
|
@ -219,12 +227,16 @@ void Light::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("set_shadow_color", "shadow_color"), &Light::set_shadow_color);
|
||||
ClassDB::bind_method(D_METHOD("get_shadow_color"), &Light::get_shadow_color);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_bake_mode", "bake_mode"), &Light::set_bake_mode);
|
||||
ClassDB::bind_method(D_METHOD("get_bake_mode"), &Light::get_bake_mode);
|
||||
|
||||
ADD_GROUP("Light", "light_");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "light_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_color", "get_color");
|
||||
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "light_energy", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_param", "get_param", PARAM_ENERGY);
|
||||
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "light_indirect_energy", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_param", "get_param", PARAM_INDIRECT_ENERGY);
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "light_negative"), "set_negative", "is_negative");
|
||||
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "light_specular", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param", "get_param", PARAM_SPECULAR);
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "light_bake_mode", PROPERTY_HINT_ENUM, "Disable,Indirect,All"), "set_bake_mode", "get_bake_mode");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "light_cull_mask", PROPERTY_HINT_LAYERS_3D_RENDER), "set_cull_mask", "get_cull_mask");
|
||||
ADD_GROUP("Shadow", "shadow_");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shadow_enabled"), "set_shadow", "has_shadow");
|
||||
|
@ -252,6 +264,10 @@ void Light::_bind_methods() {
|
|||
BIND_ENUM_CONSTANT(PARAM_SHADOW_BIAS);
|
||||
BIND_ENUM_CONSTANT(PARAM_SHADOW_BIAS_SPLIT_SCALE);
|
||||
BIND_ENUM_CONSTANT(PARAM_MAX);
|
||||
|
||||
BIND_ENUM_CONSTANT(BAKE_DISABLED);
|
||||
BIND_ENUM_CONSTANT(BAKE_INDIRECT);
|
||||
BIND_ENUM_CONSTANT(BAKE_ALL);
|
||||
}
|
||||
|
||||
Light::Light(VisualServer::LightType p_type) {
|
||||
|
@ -267,6 +283,7 @@ Light::Light(VisualServer::LightType p_type) {
|
|||
VS::get_singleton()->instance_set_base(get_instance(), light);
|
||||
|
||||
reverse_cull = false;
|
||||
bake_mode = BAKE_INDIRECT;
|
||||
|
||||
editor_only = false;
|
||||
set_color(Color(1, 1, 1, 1));
|
||||
|
|
|
@ -63,6 +63,12 @@ public:
|
|||
PARAM_MAX = VS::LIGHT_PARAM_MAX
|
||||
};
|
||||
|
||||
enum BakeMode {
|
||||
BAKE_DISABLED,
|
||||
BAKE_INDIRECT,
|
||||
BAKE_ALL
|
||||
};
|
||||
|
||||
private:
|
||||
Color color;
|
||||
float param[PARAM_MAX];
|
||||
|
@ -74,6 +80,7 @@ private:
|
|||
VS::LightType type;
|
||||
bool editor_only;
|
||||
void _update_visibility();
|
||||
BakeMode bake_mode;
|
||||
|
||||
// bind helpers
|
||||
|
||||
|
@ -114,6 +121,9 @@ public:
|
|||
void set_shadow_reverse_cull_face(bool p_enable);
|
||||
bool get_shadow_reverse_cull_face() const;
|
||||
|
||||
void set_bake_mode(BakeMode p_mode);
|
||||
BakeMode get_bake_mode() const;
|
||||
|
||||
virtual AABB get_aabb() const;
|
||||
virtual PoolVector<Face3> get_faces(uint32_t p_usage_flags) const;
|
||||
|
||||
|
@ -122,6 +132,7 @@ public:
|
|||
};
|
||||
|
||||
VARIANT_ENUM_CAST(Light::Param);
|
||||
VARIANT_ENUM_CAST(Light::BakeMode);
|
||||
|
||||
class DirectionalLight : public Light {
|
||||
|
||||
|
|
2373
scene/3d/voxel_light_baker.cpp
Normal file
2373
scene/3d/voxel_light_baker.cpp
Normal file
File diff suppressed because it is too large
Load diff
148
scene/3d/voxel_light_baker.h
Normal file
148
scene/3d/voxel_light_baker.h
Normal file
|
@ -0,0 +1,148 @@
|
|||
#ifndef VOXEL_LIGHT_BAKER_H
|
||||
#define VOXEL_LIGHT_BAKER_H
|
||||
|
||||
#include "scene/3d/mesh_instance.h"
|
||||
#include "scene/resources/multimesh.h"
|
||||
|
||||
class VoxelLightBaker {
|
||||
public:
|
||||
enum DebugMode {
|
||||
DEBUG_ALBEDO,
|
||||
DEBUG_LIGHT
|
||||
};
|
||||
|
||||
enum BakeQuality {
|
||||
BAKE_QUALITY_LOW,
|
||||
BAKE_QUALITY_MEDIUM,
|
||||
BAKE_QUALITY_HIGH
|
||||
};
|
||||
|
||||
enum BakeMode {
|
||||
BAKE_MODE_CONE_TRACE,
|
||||
BAKE_MODE_RAY_TRACE,
|
||||
};
|
||||
|
||||
private:
|
||||
enum {
|
||||
CHILD_EMPTY = 0xFFFFFFFF
|
||||
|
||||
};
|
||||
|
||||
struct Cell {
|
||||
|
||||
uint32_t childs[8];
|
||||
float albedo[3]; //albedo in RGB24
|
||||
float emission[3]; //accumulated light in 16:16 fixed point (needs to be integer for moving lights fast)
|
||||
float normal[3];
|
||||
uint32_t used_sides;
|
||||
float alpha; //used for upsampling
|
||||
int level;
|
||||
|
||||
Cell() {
|
||||
for (int i = 0; i < 8; i++) {
|
||||
childs[i] = CHILD_EMPTY;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
emission[i] = 0;
|
||||
albedo[i] = 0;
|
||||
normal[i] = 0;
|
||||
}
|
||||
alpha = 0;
|
||||
used_sides = 0;
|
||||
level = 0;
|
||||
}
|
||||
};
|
||||
|
||||
Vector<Cell> bake_cells;
|
||||
int cell_subdiv;
|
||||
|
||||
struct Light {
|
||||
int x, y, z;
|
||||
float accum[6][3]; //rgb anisotropic
|
||||
float direct_accum[6][3]; //for direct bake
|
||||
int next_leaf;
|
||||
};
|
||||
|
||||
int first_leaf;
|
||||
|
||||
Vector<Light> bake_light;
|
||||
|
||||
struct MaterialCache {
|
||||
//128x128 textures
|
||||
Vector<Color> albedo;
|
||||
Vector<Color> emission;
|
||||
};
|
||||
|
||||
Map<Ref<Material>, MaterialCache> material_cache;
|
||||
int leaf_voxel_count;
|
||||
bool direct_lights_baked;
|
||||
|
||||
AABB original_bounds;
|
||||
AABB po2_bounds;
|
||||
int axis_cell_size[3];
|
||||
|
||||
Transform to_cell_space;
|
||||
|
||||
int color_scan_cell_width;
|
||||
int bake_texture_size;
|
||||
float cell_size;
|
||||
float propagation;
|
||||
float energy;
|
||||
|
||||
BakeQuality bake_quality;
|
||||
BakeMode bake_mode;
|
||||
|
||||
int max_original_cells;
|
||||
|
||||
void _init_light_plot(int p_idx, int p_level, int p_x, int p_y, int p_z, uint32_t p_parent);
|
||||
|
||||
Vector<Color> _get_bake_texture(Ref<Image> p_image, const Color &p_color_mul, const Color &p_color_add);
|
||||
MaterialCache _get_material_cache(Ref<Material> p_material);
|
||||
void _plot_face(int p_idx, int p_level, int p_x, int p_y, int p_z, const Vector3 *p_vtx, const Vector2 *p_uv, const MaterialCache &p_material, const AABB &p_aabb);
|
||||
void _fixup_plot(int p_idx, int p_level);
|
||||
void _debug_mesh(int p_idx, int p_level, const AABB &p_aabb, Ref<MultiMesh> &p_multimesh, int &idx, DebugMode p_mode);
|
||||
void _check_init_light();
|
||||
|
||||
uint32_t _find_cell_at_pos(const Cell *cells, int x, int y, int z);
|
||||
|
||||
struct LightMap {
|
||||
Vector3 light;
|
||||
Vector3 pos;
|
||||
Vector3 normal;
|
||||
};
|
||||
|
||||
void _plot_triangle(Vector2 *vertices, Vector3 *positions, Vector3 *normals, LightMap *pixels, int width, int height);
|
||||
|
||||
_FORCE_INLINE_ void _sample_baked_octree_filtered_and_anisotropic(const Vector3 &p_posf, const Vector3 &p_direction, float p_level, Vector3 &r_color, float &r_alpha);
|
||||
_FORCE_INLINE_ Vector3 _voxel_cone_trace(const Vector3 &p_pos, const Vector3 &p_normal, float p_aperture);
|
||||
_FORCE_INLINE_ Vector3 _compute_pixel_light_at_pos(const Vector3 &p_pos, const Vector3 &p_normal);
|
||||
_FORCE_INLINE_ Vector3 _compute_ray_trace_at_pos(const Vector3 &p_pos, const Vector3 &p_normal);
|
||||
|
||||
public:
|
||||
void begin_bake(int p_subdiv, const AABB &p_bounds);
|
||||
void plot_mesh(const Transform &p_xform, Ref<Mesh> &p_mesh, const Vector<Ref<Material> > &p_materials, const Ref<Material> &p_override_material);
|
||||
void begin_bake_light(BakeQuality p_quality = BAKE_QUALITY_MEDIUM, BakeMode p_bake_mode = BAKE_MODE_CONE_TRACE, float p_propagation = 0.85, float p_energy = 1);
|
||||
void plot_light_directional(const Vector3 &p_direction, const Color &p_color, float p_energy, float p_indirect_energy, bool p_direct);
|
||||
void plot_light_omni(const Vector3 &p_pos, const Color &p_color, float p_energy, float p_indirect_energy, float p_radius, float p_attenutation, bool p_direct);
|
||||
void plot_light_spot(const Vector3 &p_pos, const Vector3 &p_axis, const Color &p_color, float p_energy, float p_indirect_energy, float p_radius, float p_attenutation, float p_spot_angle, float p_spot_attenuation, bool p_direct);
|
||||
void end_bake();
|
||||
|
||||
struct LightMapData {
|
||||
int width;
|
||||
int height;
|
||||
PoolVector<float> light;
|
||||
};
|
||||
|
||||
Error make_lightmap(const Transform &p_xform, Ref<Mesh> &p_mesh, LightMapData &r_lightmap, bool (*p_bake_time_func)(void *, float, float) = NULL, void *p_bake_time_ud = NULL);
|
||||
|
||||
PoolVector<int> create_gi_probe_data();
|
||||
Ref<MultiMesh> create_debug_multimesh(DebugMode p_mode = DEBUG_ALBEDO);
|
||||
PoolVector<uint8_t> create_capture_octree(int p_subdiv);
|
||||
|
||||
float get_cell_size() const;
|
||||
Transform get_to_cell_space_xform() const;
|
||||
VoxelLightBaker();
|
||||
};
|
||||
|
||||
#endif // VOXEL_LIGHT_BAKER_H
|
|
@ -177,8 +177,8 @@ void Node::_propagate_ready() {
|
|||
}
|
||||
data.blocked--;
|
||||
if (data.ready_first) {
|
||||
notification(NOTIFICATION_READY);
|
||||
data.ready_first = false;
|
||||
notification(NOTIFICATION_READY);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -160,6 +160,7 @@
|
|||
#include "scene/3d/area.h"
|
||||
#include "scene/3d/arvr_nodes.h"
|
||||
#include "scene/3d/audio_stream_player_3d.h"
|
||||
#include "scene/3d/baked_lightmap.h"
|
||||
#include "scene/3d/bone_attachment.h"
|
||||
#include "scene/3d/camera.h"
|
||||
#include "scene/3d/collision_polygon.h"
|
||||
|
@ -375,6 +376,8 @@ void register_scene_types() {
|
|||
ClassDB::register_class<ReflectionProbe>();
|
||||
ClassDB::register_class<GIProbe>();
|
||||
ClassDB::register_class<GIProbeData>();
|
||||
ClassDB::register_class<BakedLightmap>();
|
||||
ClassDB::register_class<BakedLightmapData>();
|
||||
ClassDB::register_class<AnimationTreePlayer>();
|
||||
ClassDB::register_class<Particles>();
|
||||
ClassDB::register_class<Position3D>();
|
||||
|
|
|
@ -645,7 +645,7 @@ void SpatialMaterial::_update_shader() {
|
|||
code += "\tvec2 base_uv = UV;\n";
|
||||
}
|
||||
|
||||
if ((features[FEATURE_DETAIL] && detail_uv == DETAIL_UV_2) || (features[FEATURE_AMBIENT_OCCLUSION] && flags[FLAG_AO_ON_UV2])) {
|
||||
if ((features[FEATURE_DETAIL] && detail_uv == DETAIL_UV_2) || (features[FEATURE_AMBIENT_OCCLUSION] && flags[FLAG_AO_ON_UV2]) || (features[FEATURE_EMISSION] && flags[FLAG_EMISSION_ON_UV2])) {
|
||||
code += "\tvec2 base_uv2 = UV2;\n";
|
||||
}
|
||||
|
||||
|
@ -729,11 +729,20 @@ void SpatialMaterial::_update_shader() {
|
|||
}
|
||||
|
||||
if (features[FEATURE_EMISSION]) {
|
||||
if (flags[FLAG_EMISSION_ON_UV2]) {
|
||||
if (flags[FLAG_UV2_USE_TRIPLANAR]) {
|
||||
code += "\tvec3 emission_tex = triplanar_texture(texture_emission,uv2_power_normal,uv2_triplanar_pos).rgb;\n";
|
||||
} else {
|
||||
code += "\tvec3 emission_tex = texture(texture_emission,base_uv2).rgb;\n";
|
||||
}
|
||||
} else {
|
||||
if (flags[FLAG_UV1_USE_TRIPLANAR]) {
|
||||
code += "\tvec3 emission_tex = triplanar_texture(texture_emission,uv1_power_normal,uv1_triplanar_pos).rgb;\n";
|
||||
} else {
|
||||
code += "\tvec3 emission_tex = texture(texture_emission,base_uv).rgb;\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (emission_op == EMISSION_OP_ADD) {
|
||||
code += "\tEMISSION = (emission.rgb+emission_tex)*emission_energy;\n";
|
||||
} else {
|
||||
|
@ -1892,6 +1901,7 @@ void SpatialMaterial::_bind_methods() {
|
|||
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "emission", PROPERTY_HINT_COLOR_NO_ALPHA), "set_emission", "get_emission");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::REAL, "emission_energy", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_emission_energy", "get_emission_energy");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "emission_operator", PROPERTY_HINT_ENUM, "Add,Multiply"), "set_emission_operator", "get_emission_operator");
|
||||
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "emission_on_uv2"), "set_flag", "get_flag", FLAG_EMISSION_ON_UV2);
|
||||
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "emission_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture", TEXTURE_EMISSION);
|
||||
|
||||
ADD_GROUP("NormalMap", "normal_");
|
||||
|
@ -2034,6 +2044,7 @@ void SpatialMaterial::_bind_methods() {
|
|||
BIND_ENUM_CONSTANT(FLAG_UV1_USE_TRIPLANAR);
|
||||
BIND_ENUM_CONSTANT(FLAG_UV2_USE_TRIPLANAR);
|
||||
BIND_ENUM_CONSTANT(FLAG_AO_ON_UV2);
|
||||
BIND_ENUM_CONSTANT(FLAG_EMISSION_ON_UV2);
|
||||
BIND_ENUM_CONSTANT(FLAG_USE_ALPHA_SCISSOR);
|
||||
BIND_ENUM_CONSTANT(FLAG_TRIPLANAR_USE_WORLD);
|
||||
BIND_ENUM_CONSTANT(FLAG_ALBEDO_TEXTURE_FORCE_SRGB);
|
||||
|
|
|
@ -184,6 +184,7 @@ public:
|
|||
FLAG_UV2_USE_TRIPLANAR,
|
||||
FLAG_TRIPLANAR_USE_WORLD,
|
||||
FLAG_AO_ON_UV2,
|
||||
FLAG_EMISSION_ON_UV2,
|
||||
FLAG_USE_ALPHA_SCISSOR,
|
||||
FLAG_ALBEDO_TEXTURE_FORCE_SRGB,
|
||||
FLAG_MAX
|
||||
|
@ -234,7 +235,7 @@ private:
|
|||
uint64_t blend_mode : 2;
|
||||
uint64_t depth_draw_mode : 2;
|
||||
uint64_t cull_mode : 2;
|
||||
uint64_t flags : 13;
|
||||
uint64_t flags : 14;
|
||||
uint64_t detail_blend_mode : 2;
|
||||
uint64_t diffuse_mode : 3;
|
||||
uint64_t specular_mode : 2;
|
||||
|
|
|
@ -1123,27 +1123,29 @@ Error ArrayMesh::lightmap_unwrap(const Transform &p_base_transform, float p_texe
|
|||
|
||||
PoolVector<int> rindices = arrays[Mesh::ARRAY_INDEX];
|
||||
int ic = rindices.size();
|
||||
int index_ofs = indices.size();
|
||||
|
||||
if (ic == 0) {
|
||||
indices.resize(index_ofs + vc);
|
||||
face_materials.resize((index_ofs + vc) / 3);
|
||||
for (int j = 0; j < vc; j++) {
|
||||
indices[index_ofs + j] = vertex_ofs + j;
|
||||
}
|
||||
|
||||
for (int j = 0; j < vc / 3; j++) {
|
||||
face_materials[(index_ofs / 3) + j] = i;
|
||||
if (Face3(r[j * 3 + 0], r[j * 3 + 1], r[j * 3 + 2]).is_degenerate())
|
||||
continue;
|
||||
|
||||
indices.push_back(vertex_ofs + j * 3 + 0);
|
||||
indices.push_back(vertex_ofs + j * 3 + 1);
|
||||
indices.push_back(vertex_ofs + j * 3 + 2);
|
||||
face_materials.push_back(i);
|
||||
}
|
||||
|
||||
} else {
|
||||
PoolVector<int>::Read ri = rindices.read();
|
||||
indices.resize(index_ofs + ic);
|
||||
face_materials.resize((index_ofs + ic) / 3);
|
||||
for (int j = 0; j < ic; j++) {
|
||||
indices[index_ofs + j] = vertex_ofs + ri[j];
|
||||
}
|
||||
|
||||
for (int j = 0; j < ic / 3; j++) {
|
||||
face_materials[(index_ofs / 3) + j] = i;
|
||||
if (Face3(r[ri[j * 3 + 0]], r[ri[j * 3 + 1]], r[ri[j * 3 + 2]]).is_degenerate())
|
||||
continue;
|
||||
indices.push_back(vertex_ofs + ri[j * 3 + 0]);
|
||||
indices.push_back(vertex_ofs + ri[j * 3 + 1]);
|
||||
indices.push_back(vertex_ofs + ri[j * 3 + 2]);
|
||||
face_materials.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -112,6 +112,10 @@ public:
|
|||
|
||||
SelfList<InstanceBase> dependency_item;
|
||||
|
||||
InstanceBase *lightmap_capture;
|
||||
RID lightmap;
|
||||
Vector<Color> lightmap_capture_data; //in a array (12 values) to avoid wasting space if unused. Alpha is unused, but needed to send to shader
|
||||
|
||||
virtual void base_removed() = 0;
|
||||
virtual void base_changed() = 0;
|
||||
virtual void base_material_changed() = 0;
|
||||
|
@ -126,6 +130,7 @@ public:
|
|||
depth_layer = 0;
|
||||
layer_mask = 1;
|
||||
baked_light = false;
|
||||
lightmap_capture = NULL;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -437,6 +442,32 @@ public:
|
|||
virtual RID gi_probe_dynamic_data_create(int p_width, int p_height, int p_depth, GIProbeCompression p_compression) = 0;
|
||||
virtual void gi_probe_dynamic_data_update(RID p_gi_probe_data, int p_depth_slice, int p_slice_count, int p_mipmap, const void *p_data) = 0;
|
||||
|
||||
/* LIGHTMAP CAPTURE */
|
||||
|
||||
struct LightmapCaptureOctree {
|
||||
|
||||
enum {
|
||||
CHILD_EMPTY = 0xFFFFFFFF
|
||||
};
|
||||
|
||||
uint16_t light[6][3]; //anisotropic light
|
||||
float alpha;
|
||||
uint32_t children[8];
|
||||
};
|
||||
|
||||
virtual RID lightmap_capture_create() = 0;
|
||||
virtual void lightmap_capture_set_bounds(RID p_capture, const AABB &p_bounds) = 0;
|
||||
virtual AABB lightmap_capture_get_bounds(RID p_capture) const = 0;
|
||||
virtual void lightmap_capture_set_octree(RID p_capture, const PoolVector<uint8_t> &p_octree) = 0;
|
||||
virtual PoolVector<uint8_t> lightmap_capture_get_octree(RID p_capture) const = 0;
|
||||
virtual void lightmap_capture_set_octree_cell_transform(RID p_capture, const Transform &p_xform) = 0;
|
||||
virtual Transform lightmap_capture_get_octree_cell_transform(RID p_capture) const = 0;
|
||||
virtual void lightmap_capture_set_octree_cell_subdiv(RID p_capture, int p_subdiv) = 0;
|
||||
virtual int lightmap_capture_get_octree_cell_subdiv(RID p_capture) const = 0;
|
||||
virtual void lightmap_capture_set_energy(RID p_capture, float p_energy) = 0;
|
||||
virtual float lightmap_capture_get_energy(RID p_capture) const = 0;
|
||||
virtual const PoolVector<LightmapCaptureOctree> *lightmap_capture_get_octree_ptr(RID p_capture) const = 0;
|
||||
|
||||
/* PARTICLES */
|
||||
|
||||
virtual RID particles_create() = 0;
|
||||
|
|
|
@ -361,6 +361,24 @@ public:
|
|||
BIND2(gi_probe_set_dynamic_data, RID, const PoolVector<int> &)
|
||||
BIND1RC(PoolVector<int>, gi_probe_get_dynamic_data, RID)
|
||||
|
||||
/* LIGHTMAP CAPTURE */
|
||||
|
||||
BIND0R(RID, lightmap_capture_create)
|
||||
|
||||
BIND2(lightmap_capture_set_bounds, RID, const AABB &)
|
||||
BIND1RC(AABB, lightmap_capture_get_bounds, RID)
|
||||
|
||||
BIND2(lightmap_capture_set_octree, RID, const PoolVector<uint8_t> &)
|
||||
BIND1RC(PoolVector<uint8_t>, lightmap_capture_get_octree, RID)
|
||||
|
||||
BIND2(lightmap_capture_set_octree_cell_transform, RID, const Transform &)
|
||||
BIND1RC(Transform, lightmap_capture_get_octree_cell_transform, RID)
|
||||
BIND2(lightmap_capture_set_octree_cell_subdiv, RID, int)
|
||||
BIND1RC(int, lightmap_capture_get_octree_cell_subdiv, RID)
|
||||
|
||||
BIND2(lightmap_capture_set_energy, RID, float)
|
||||
BIND1RC(float, lightmap_capture_get_energy, RID)
|
||||
|
||||
/* PARTICLES */
|
||||
|
||||
BIND0R(RID, particles_create)
|
||||
|
@ -504,6 +522,7 @@ public:
|
|||
BIND3(instance_set_blend_shape_weight, RID, int, float)
|
||||
BIND3(instance_set_surface_material, RID, int, RID)
|
||||
BIND2(instance_set_visible, RID, bool)
|
||||
BIND3(instance_set_use_lightmap, RID, RID, RID)
|
||||
|
||||
BIND2(instance_set_custom_aabb, RID, AABB)
|
||||
|
||||
|
|
|
@ -132,6 +132,19 @@ void *VisualServerScene::_instance_pair(void *p_self, OctreeElementID, Instance
|
|||
|
||||
geom->reflection_dirty = true;
|
||||
|
||||
return E; //this element should make freeing faster
|
||||
} else if (B->base_type == VS::INSTANCE_LIGHTMAP_CAPTURE && ((1 << A->base_type) & VS::INSTANCE_GEOMETRY_MASK)) {
|
||||
|
||||
InstanceLightmapCaptureData *lightmap_capture = static_cast<InstanceLightmapCaptureData *>(B->base_data);
|
||||
InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(A->base_data);
|
||||
|
||||
InstanceLightmapCaptureData::PairInfo pinfo;
|
||||
pinfo.geometry = A;
|
||||
pinfo.L = geom->lightmap_captures.push_back(B);
|
||||
|
||||
List<InstanceLightmapCaptureData::PairInfo>::Element *E = lightmap_capture->geometries.push_back(pinfo);
|
||||
((VisualServerScene *)p_self)->_instance_queue_update(A, false, false); //need to update capture
|
||||
|
||||
return E; //this element should make freeing faster
|
||||
} else if (B->base_type == VS::INSTANCE_GI_PROBE && ((1 << A->base_type) & VS::INSTANCE_GEOMETRY_MASK)) {
|
||||
|
||||
|
@ -193,6 +206,16 @@ void VisualServerScene::_instance_unpair(void *p_self, OctreeElementID, Instance
|
|||
reflection_probe->geometries.erase(E);
|
||||
|
||||
geom->reflection_dirty = true;
|
||||
} else if (B->base_type == VS::INSTANCE_LIGHTMAP_CAPTURE && ((1 << A->base_type) & VS::INSTANCE_GEOMETRY_MASK)) {
|
||||
|
||||
InstanceLightmapCaptureData *lightmap_capture = static_cast<InstanceLightmapCaptureData *>(B->base_data);
|
||||
InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(A->base_data);
|
||||
|
||||
List<InstanceLightmapCaptureData::PairInfo>::Element *E = reinterpret_cast<List<InstanceLightmapCaptureData::PairInfo>::Element *>(udata);
|
||||
|
||||
geom->lightmap_captures.erase(E->get().L);
|
||||
lightmap_capture->geometries.erase(E);
|
||||
((VisualServerScene *)p_self)->_instance_queue_update(A, false, false); //need to update capture
|
||||
|
||||
} else if (B->base_type == VS::INSTANCE_GI_PROBE && ((1 << A->base_type) & VS::INSTANCE_GEOMETRY_MASK)) {
|
||||
|
||||
|
@ -344,6 +367,14 @@ void VisualServerScene::instance_set_base(RID p_instance, RID p_base) {
|
|||
reflection_probe_render_list.remove(&reflection_probe->update_list);
|
||||
}
|
||||
} break;
|
||||
case VS::INSTANCE_LIGHTMAP_CAPTURE: {
|
||||
|
||||
InstanceLightmapCaptureData *lightmap_capture = static_cast<InstanceLightmapCaptureData *>(instance->base_data);
|
||||
//erase dependencies, since no longer a lightmap
|
||||
while (lightmap_capture->users.front()) {
|
||||
instance_set_use_lightmap(lightmap_capture->users.front()->get()->self, RID(), RID());
|
||||
}
|
||||
} break;
|
||||
case VS::INSTANCE_GI_PROBE: {
|
||||
|
||||
InstanceGIProbeData *gi_probe = static_cast<InstanceGIProbeData *>(instance->base_data);
|
||||
|
@ -355,6 +386,14 @@ void VisualServerScene::instance_set_base(RID p_instance, RID p_base) {
|
|||
VSG::storage->free(gi_probe->dynamic.probe_data);
|
||||
}
|
||||
|
||||
if (instance->lightmap_capture) {
|
||||
Instance *capture = (Instance *)instance->lightmap_capture;
|
||||
InstanceLightmapCaptureData *lightmap_capture = static_cast<InstanceLightmapCaptureData *>(capture->base_data);
|
||||
lightmap_capture->users.erase(instance);
|
||||
instance->lightmap_capture = NULL;
|
||||
instance->lightmap = RID();
|
||||
}
|
||||
|
||||
VSG::scene_render->free(gi_probe->probe_instance);
|
||||
|
||||
} break;
|
||||
|
@ -412,6 +451,12 @@ void VisualServerScene::instance_set_base(RID p_instance, RID p_base) {
|
|||
|
||||
reflection_probe->instance = VSG::scene_render->reflection_probe_instance_create(p_base);
|
||||
} break;
|
||||
case VS::INSTANCE_LIGHTMAP_CAPTURE: {
|
||||
|
||||
InstanceLightmapCaptureData *lightmap_capture = memnew(InstanceLightmapCaptureData);
|
||||
instance->base_data = lightmap_capture;
|
||||
//lightmap_capture->instance = VSG::scene_render->lightmap_capture_instance_create(p_base);
|
||||
} break;
|
||||
case VS::INSTANCE_GI_PROBE: {
|
||||
|
||||
InstanceGIProbeData *gi_probe = memnew(InstanceGIProbeData);
|
||||
|
@ -590,6 +635,12 @@ void VisualServerScene::instance_set_visible(RID p_instance, bool p_visible) {
|
|||
instance->scenario->octree.set_pairable(instance->octree_id, p_visible, 1 << VS::INSTANCE_REFLECTION_PROBE, p_visible ? VS::INSTANCE_GEOMETRY_MASK : 0);
|
||||
}
|
||||
|
||||
} break;
|
||||
case VS::INSTANCE_LIGHTMAP_CAPTURE: {
|
||||
if (instance->octree_id && instance->scenario) {
|
||||
instance->scenario->octree.set_pairable(instance->octree_id, p_visible, 1 << VS::INSTANCE_LIGHTMAP_CAPTURE, p_visible ? VS::INSTANCE_GEOMETRY_MASK : 0);
|
||||
}
|
||||
|
||||
} break;
|
||||
case VS::INSTANCE_GI_PROBE: {
|
||||
if (instance->octree_id && instance->scenario) {
|
||||
|
@ -599,11 +650,35 @@ void VisualServerScene::instance_set_visible(RID p_instance, bool p_visible) {
|
|||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool is_geometry_instance(VisualServer::InstanceType p_type) {
|
||||
return p_type == VS::INSTANCE_MESH || p_type == VS::INSTANCE_MULTIMESH || p_type == VS::INSTANCE_PARTICLES || p_type == VS::INSTANCE_IMMEDIATE;
|
||||
}
|
||||
|
||||
void VisualServerScene::instance_set_use_lightmap(RID p_instance, RID p_lightmap_instance, RID p_lightmap) {
|
||||
|
||||
Instance *instance = instance_owner.get(p_instance);
|
||||
ERR_FAIL_COND(!instance);
|
||||
ERR_FAIL_COND(!is_geometry_instance(instance->base_type));
|
||||
|
||||
if (instance->lightmap_capture) {
|
||||
InstanceLightmapCaptureData *lightmap_capture = static_cast<InstanceLightmapCaptureData *>(((Instance *)instance->lightmap_capture)->base_data);
|
||||
lightmap_capture->users.erase(instance);
|
||||
instance->lightmap = RID();
|
||||
instance->lightmap_capture = NULL;
|
||||
}
|
||||
|
||||
if (p_lightmap_instance.is_valid()) {
|
||||
Instance *lightmap_instance = instance_owner.get(p_lightmap_instance);
|
||||
ERR_FAIL_COND(!lightmap_instance);
|
||||
ERR_FAIL_COND(lightmap_instance->base_type != VS::INSTANCE_LIGHTMAP_CAPTURE);
|
||||
instance->lightmap_capture = lightmap_instance;
|
||||
|
||||
InstanceLightmapCaptureData *lightmap_capture = static_cast<InstanceLightmapCaptureData *>(((Instance *)instance->lightmap_capture)->base_data);
|
||||
lightmap_capture->users.insert(instance);
|
||||
instance->lightmap = p_lightmap;
|
||||
}
|
||||
}
|
||||
|
||||
void VisualServerScene::instance_set_custom_aabb(RID p_instance, AABB p_aabb) {
|
||||
|
||||
Instance *instance = instance_owner.get(p_instance);
|
||||
|
@ -811,6 +886,15 @@ void VisualServerScene::_update_instance(Instance *p_instance) {
|
|||
light->shadow_dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!p_instance->lightmap_capture && geom->lightmap_captures.size()) {
|
||||
//affected by lightmap captures, must update capture info!
|
||||
_update_instance_lightmap_captures(p_instance);
|
||||
} else {
|
||||
if (!p_instance->lightmap_capture_data.empty()) {
|
||||
!p_instance->lightmap_capture_data.resize(0); //not in use, clear capture data
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
p_instance->mirror = p_instance->transform.basis.determinant() < 0.0;
|
||||
|
@ -832,7 +916,7 @@ void VisualServerScene::_update_instance(Instance *p_instance) {
|
|||
uint32_t pairable_mask = 0;
|
||||
bool pairable = false;
|
||||
|
||||
if (p_instance->base_type == VS::INSTANCE_LIGHT || p_instance->base_type == VS::INSTANCE_REFLECTION_PROBE) {
|
||||
if (p_instance->base_type == VS::INSTANCE_LIGHT || p_instance->base_type == VS::INSTANCE_REFLECTION_PROBE || p_instance->base_type == VS::INSTANCE_LIGHTMAP_CAPTURE) {
|
||||
|
||||
pairable_mask = p_instance->visible ? VS::INSTANCE_GEOMETRY_MASK : 0;
|
||||
pairable = true;
|
||||
|
@ -916,6 +1000,11 @@ void VisualServerScene::_update_instance_aabb(Instance *p_instance) {
|
|||
|
||||
new_aabb = VSG::storage->gi_probe_get_bounds(p_instance->base);
|
||||
|
||||
} break;
|
||||
case VisualServer::INSTANCE_LIGHTMAP_CAPTURE: {
|
||||
|
||||
new_aabb = VSG::storage->lightmap_capture_get_bounds(p_instance->base);
|
||||
|
||||
} break;
|
||||
|
||||
default: {}
|
||||
|
@ -928,6 +1017,237 @@ void VisualServerScene::_update_instance_aabb(Instance *p_instance) {
|
|||
p_instance->aabb = new_aabb;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ static void _light_capture_sample_octree(const RasterizerStorage::LightmapCaptureOctree *p_octree, int p_cell_subdiv, const Vector3 &p_pos, const Vector3 &p_dir, float p_level, Vector3 &r_color, float &r_alpha) {
|
||||
|
||||
static const Vector3 aniso_normal[6] = {
|
||||
Vector3(-1, 0, 0),
|
||||
Vector3(1, 0, 0),
|
||||
Vector3(0, -1, 0),
|
||||
Vector3(0, 1, 0),
|
||||
Vector3(0, 0, -1),
|
||||
Vector3(0, 0, 1)
|
||||
};
|
||||
|
||||
int size = 1 << (p_cell_subdiv - 1);
|
||||
|
||||
int clamp_v = size - 1;
|
||||
//first of all, clamp
|
||||
Vector3 pos;
|
||||
pos.x = CLAMP(p_pos.x, 0, clamp_v);
|
||||
pos.y = CLAMP(p_pos.y, 0, clamp_v);
|
||||
pos.z = CLAMP(p_pos.z, 0, clamp_v);
|
||||
|
||||
float level = (p_cell_subdiv - 1) - p_level;
|
||||
|
||||
int target_level;
|
||||
float level_filter;
|
||||
if (level <= 0.0) {
|
||||
level_filter = 0;
|
||||
target_level = 0;
|
||||
} else {
|
||||
target_level = Math::ceil(level);
|
||||
level_filter = target_level - level;
|
||||
}
|
||||
|
||||
Vector3 color[2][8];
|
||||
float alpha[2][8];
|
||||
zeromem(alpha, sizeof(float) * 2 * 8);
|
||||
|
||||
//find cell at given level first
|
||||
|
||||
for (int c = 0; c < 2; c++) {
|
||||
|
||||
int current_level = MAX(0, target_level - c);
|
||||
int level_cell_size = (1 << (p_cell_subdiv - 1)) >> current_level;
|
||||
|
||||
for (int n = 0; n < 8; n++) {
|
||||
|
||||
int x = int(pos.x);
|
||||
int y = int(pos.y);
|
||||
int z = int(pos.z);
|
||||
|
||||
if (n & 1)
|
||||
x += level_cell_size;
|
||||
if (n & 2)
|
||||
y += level_cell_size;
|
||||
if (n & 4)
|
||||
z += level_cell_size;
|
||||
|
||||
int ofs_x = 0;
|
||||
int ofs_y = 0;
|
||||
int ofs_z = 0;
|
||||
|
||||
x = CLAMP(x, 0, clamp_v);
|
||||
y = CLAMP(y, 0, clamp_v);
|
||||
z = CLAMP(z, 0, clamp_v);
|
||||
|
||||
int half = size / 2;
|
||||
uint32_t cell = 0;
|
||||
for (int i = 0; i < current_level; i++) {
|
||||
|
||||
const RasterizerStorage::LightmapCaptureOctree *bc = &p_octree[cell];
|
||||
|
||||
int child = 0;
|
||||
if (x >= ofs_x + half) {
|
||||
child |= 1;
|
||||
ofs_x += half;
|
||||
}
|
||||
if (y >= ofs_y + half) {
|
||||
child |= 2;
|
||||
ofs_y += half;
|
||||
}
|
||||
if (z >= ofs_z + half) {
|
||||
child |= 4;
|
||||
ofs_z += half;
|
||||
}
|
||||
|
||||
cell = bc->children[child];
|
||||
if (cell == RasterizerStorage::LightmapCaptureOctree::CHILD_EMPTY)
|
||||
break;
|
||||
|
||||
half >>= 1;
|
||||
}
|
||||
|
||||
if (cell == RasterizerStorage::LightmapCaptureOctree::CHILD_EMPTY) {
|
||||
alpha[c][n] = 0;
|
||||
} else {
|
||||
alpha[c][n] = p_octree[cell].alpha;
|
||||
|
||||
for (int i = 0; i < 6; i++) {
|
||||
//anisotropic read light
|
||||
float amount = p_dir.dot(aniso_normal[i]);
|
||||
if (amount < 0)
|
||||
amount = 0;
|
||||
color[c][n].x += p_octree[cell].light[i][0] / 1024.0 * amount;
|
||||
color[c][n].y += p_octree[cell].light[i][1] / 1024.0 * amount;
|
||||
color[c][n].z += p_octree[cell].light[i][2] / 1024.0 * amount;
|
||||
}
|
||||
}
|
||||
|
||||
//print_line("\tlev " + itos(c) + " - " + itos(n) + " alpha: " + rtos(cells[test_cell].alpha) + " col: " + color[c][n]);
|
||||
}
|
||||
}
|
||||
|
||||
float target_level_size = size >> target_level;
|
||||
Vector3 pos_fract[2];
|
||||
|
||||
pos_fract[0].x = Math::fmod(pos.x, target_level_size) / target_level_size;
|
||||
pos_fract[0].y = Math::fmod(pos.y, target_level_size) / target_level_size;
|
||||
pos_fract[0].z = Math::fmod(pos.z, target_level_size) / target_level_size;
|
||||
|
||||
target_level_size = size >> MAX(0, target_level - 1);
|
||||
|
||||
pos_fract[1].x = Math::fmod(pos.x, target_level_size) / target_level_size;
|
||||
pos_fract[1].y = Math::fmod(pos.y, target_level_size) / target_level_size;
|
||||
pos_fract[1].z = Math::fmod(pos.z, target_level_size) / target_level_size;
|
||||
|
||||
float alpha_interp[2];
|
||||
Vector3 color_interp[2];
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
|
||||
Vector3 color_x00 = color[i][0].linear_interpolate(color[i][1], pos_fract[i].x);
|
||||
Vector3 color_xy0 = color[i][2].linear_interpolate(color[i][3], pos_fract[i].x);
|
||||
Vector3 blend_z0 = color_x00.linear_interpolate(color_xy0, pos_fract[i].y);
|
||||
|
||||
Vector3 color_x0z = color[i][4].linear_interpolate(color[i][5], pos_fract[i].x);
|
||||
Vector3 color_xyz = color[i][6].linear_interpolate(color[i][7], pos_fract[i].x);
|
||||
Vector3 blend_z1 = color_x0z.linear_interpolate(color_xyz, pos_fract[i].y);
|
||||
|
||||
color_interp[i] = blend_z0.linear_interpolate(blend_z1, pos_fract[i].z);
|
||||
|
||||
float alpha_x00 = Math::lerp(alpha[i][0], alpha[i][1], pos_fract[i].x);
|
||||
float alpha_xy0 = Math::lerp(alpha[i][2], alpha[i][3], pos_fract[i].x);
|
||||
float alpha_z0 = Math::lerp(alpha_x00, alpha_xy0, pos_fract[i].y);
|
||||
|
||||
float alpha_x0z = Math::lerp(alpha[i][4], alpha[i][5], pos_fract[i].x);
|
||||
float alpha_xyz = Math::lerp(alpha[i][6], alpha[i][7], pos_fract[i].x);
|
||||
float alpha_z1 = Math::lerp(alpha_x0z, alpha_xyz, pos_fract[i].y);
|
||||
|
||||
alpha_interp[i] = Math::lerp(alpha_z0, alpha_z1, pos_fract[i].z);
|
||||
}
|
||||
|
||||
r_color = color_interp[0].linear_interpolate(color_interp[1], level_filter);
|
||||
r_alpha = Math::lerp(alpha_interp[0], alpha_interp[1], level_filter);
|
||||
|
||||
// print_line("pos: " + p_posf + " level " + rtos(p_level) + " down to " + itos(target_level) + "." + rtos(level_filter) + " color " + r_color + " alpha " + rtos(r_alpha));
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ static Color _light_capture_voxel_cone_trace(const RasterizerStorage::LightmapCaptureOctree *p_octree, const Vector3 &p_pos, const Vector3 &p_dir, float p_aperture, int p_cell_subdiv) {
|
||||
|
||||
float bias = 0.0; //no need for bias here
|
||||
float max_distance = (Vector3(1, 1, 1) * (1 << (p_cell_subdiv - 1))).length();
|
||||
|
||||
float dist = bias;
|
||||
float alpha = 0.0;
|
||||
Vector3 color;
|
||||
|
||||
Vector3 scolor;
|
||||
float salpha;
|
||||
|
||||
while (dist < max_distance && alpha < 0.95) {
|
||||
float diameter = MAX(1.0, 2.0 * p_aperture * dist);
|
||||
_light_capture_sample_octree(p_octree, p_cell_subdiv, p_pos + dist * p_dir, p_dir, log2(diameter), scolor, salpha);
|
||||
float a = (1.0 - alpha);
|
||||
color += scolor * a;
|
||||
alpha += a * salpha;
|
||||
dist += diameter * 0.5;
|
||||
}
|
||||
|
||||
return Color(color.x, color.y, color.z, alpha);
|
||||
}
|
||||
|
||||
void VisualServerScene::_update_instance_lightmap_captures(Instance *p_instance) {
|
||||
|
||||
InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(p_instance->base_data);
|
||||
|
||||
static const Vector3 cone_traces[12] = {
|
||||
Vector3(0, 0, 1),
|
||||
Vector3(0.866025, 0, 0.5),
|
||||
Vector3(0.267617, 0.823639, 0.5),
|
||||
Vector3(-0.700629, 0.509037, 0.5),
|
||||
Vector3(-0.700629, -0.509037, 0.5),
|
||||
Vector3(0.267617, -0.823639, 0.5),
|
||||
Vector3(0, 0, -1),
|
||||
Vector3(0.866025, 0, -0.5),
|
||||
Vector3(0.267617, 0.823639, -0.5),
|
||||
Vector3(-0.700629, 0.509037, -0.5),
|
||||
Vector3(-0.700629, -0.509037, -0.5),
|
||||
Vector3(0.267617, -0.823639, -0.5)
|
||||
};
|
||||
|
||||
float cone_aperture = 0.577; // tan(angle) 60 degrees
|
||||
|
||||
if (p_instance->lightmap_capture_data.empty()) {
|
||||
p_instance->lightmap_capture_data.resize(12);
|
||||
}
|
||||
|
||||
//print_line("update captures for pos: " + p_instance->transform.origin);
|
||||
|
||||
zeromem(p_instance->lightmap_capture_data.ptrw(), 12 * sizeof(Color));
|
||||
//this could use some sort of blending..
|
||||
for (List<Instance *>::Element *E = geom->lightmap_captures.front(); E; E = E->next()) {
|
||||
const PoolVector<RasterizerStorage::LightmapCaptureOctree> *octree = VSG::storage->lightmap_capture_get_octree_ptr(E->get()->base);
|
||||
//print_line("octree size: " + itos(octree->size()));
|
||||
if (octree->size() == 0)
|
||||
continue;
|
||||
Transform to_cell_xform = VSG::storage->lightmap_capture_get_octree_cell_transform(E->get()->base);
|
||||
int cell_subdiv = VSG::storage->lightmap_capture_get_octree_cell_subdiv(E->get()->base);
|
||||
to_cell_xform = to_cell_xform * E->get()->transform.affine_inverse();
|
||||
|
||||
PoolVector<RasterizerStorage::LightmapCaptureOctree>::Read octree_r = octree->read();
|
||||
|
||||
Vector3 pos = to_cell_xform.xform(p_instance->transform.origin);
|
||||
|
||||
for (int i = 0; i < 12; i++) {
|
||||
|
||||
Vector3 dir = to_cell_xform.basis.xform(cone_traces[i]).normalized();
|
||||
Color capture = _light_capture_voxel_cone_trace(octree_r.ptr(), pos, dir, cone_aperture, cell_subdiv);
|
||||
p_instance->lightmap_capture_data[i] += capture;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VisualServerScene::_light_instance_update_shadow(Instance *p_instance, const Transform p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, RID p_shadow_atlas, Scenario *p_scenario) {
|
||||
|
||||
InstanceLightData *light = static_cast<InstanceLightData *>(p_instance->base_data);
|
||||
|
@ -2188,6 +2508,8 @@ void VisualServerScene::_bake_gi_probe_light(const GIProbeDataHeader *header, co
|
|||
InstanceGIProbeData::LocalData *light = &local_data[idx];
|
||||
|
||||
Vector3 to(light->pos[0] + 0.5, light->pos[1] + 0.5, light->pos[2] + 0.5);
|
||||
to += -light_axis.sign() * 0.47; //make it more likely to receive a ray
|
||||
|
||||
Vector3 norm(
|
||||
(((cells[idx].normal >> 16) & 0xFF) / 255.0) * 2.0 - 1.0,
|
||||
(((cells[idx].normal >> 8) & 0xFF) / 255.0) * 2.0 - 1.0,
|
||||
|
@ -2254,6 +2576,8 @@ void VisualServerScene::_bake_gi_probe_light(const GIProbeDataHeader *header, co
|
|||
InstanceGIProbeData::LocalData *light = &local_data[idx];
|
||||
|
||||
Vector3 to(light->pos[0] + 0.5, light->pos[1] + 0.5, light->pos[2] + 0.5);
|
||||
to += (light_pos - to).sign() * 0.47; //make it more likely to receive a ray
|
||||
|
||||
Vector3 norm(
|
||||
(((cells[idx].normal >> 16) & 0xFF) / 255.0) * 2.0 - 1.0,
|
||||
(((cells[idx].normal >> 8) & 0xFF) / 255.0) * 2.0 - 1.0,
|
||||
|
@ -2927,12 +3251,12 @@ void VisualServerScene::_update_dirty_instance(Instance *p_instance) {
|
|||
}
|
||||
}
|
||||
|
||||
_instance_update_list.remove(&p_instance->update_item);
|
||||
|
||||
_update_instance(p_instance);
|
||||
|
||||
p_instance->update_aabb = false;
|
||||
p_instance->update_materials = false;
|
||||
|
||||
_instance_update_list.remove(&p_instance->update_item);
|
||||
}
|
||||
|
||||
void VisualServerScene::update_dirty_instances() {
|
||||
|
|
|
@ -281,6 +281,8 @@ public:
|
|||
List<Instance *> gi_probes;
|
||||
bool gi_probes_dirty;
|
||||
|
||||
List<Instance *> lightmap_captures;
|
||||
|
||||
InstanceGeometryData() {
|
||||
|
||||
lighting_dirty = false;
|
||||
|
@ -445,6 +447,20 @@ public:
|
|||
|
||||
SelfList<InstanceGIProbeData>::List gi_probe_update_list;
|
||||
|
||||
struct InstanceLightmapCaptureData : public InstanceBaseData {
|
||||
|
||||
struct PairInfo {
|
||||
List<Instance *>::Element *L; //iterator in geometry
|
||||
Instance *geometry;
|
||||
};
|
||||
List<PairInfo> geometries;
|
||||
|
||||
Set<Instance *> users;
|
||||
|
||||
InstanceLightmapCaptureData() {
|
||||
}
|
||||
};
|
||||
|
||||
Instance *instance_cull_result[MAX_INSTANCE_CULL];
|
||||
Instance *instance_shadow_cull_result[MAX_INSTANCE_CULL]; //used for generating shadowmaps
|
||||
Instance *light_cull_result[MAX_LIGHTS_CULLED];
|
||||
|
@ -466,6 +482,7 @@ public:
|
|||
virtual void instance_set_blend_shape_weight(RID p_instance, int p_shape, float p_weight);
|
||||
virtual void instance_set_surface_material(RID p_instance, int p_surface, RID p_material);
|
||||
virtual void instance_set_visible(RID p_instance, bool p_visible);
|
||||
virtual void instance_set_use_lightmap(RID p_instance, RID p_lightmap_instance, RID p_lightmap);
|
||||
|
||||
virtual void instance_set_custom_aabb(RID p_insatnce, AABB aabb);
|
||||
|
||||
|
@ -489,6 +506,7 @@ public:
|
|||
_FORCE_INLINE_ void _update_instance(Instance *p_instance);
|
||||
_FORCE_INLINE_ void _update_instance_aabb(Instance *p_instance);
|
||||
_FORCE_INLINE_ void _update_dirty_instance(Instance *p_instance);
|
||||
_FORCE_INLINE_ void _update_instance_lightmap_captures(Instance *p_instance);
|
||||
|
||||
_FORCE_INLINE_ void _light_instance_update_shadow(Instance *p_instance, const Transform p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, RID p_shadow_atlas, Scenario *p_scenario);
|
||||
|
||||
|
|
|
@ -294,6 +294,22 @@ public:
|
|||
FUNC2(gi_probe_set_dynamic_data, RID, const PoolVector<int> &)
|
||||
FUNC1RC(PoolVector<int>, gi_probe_get_dynamic_data, RID)
|
||||
|
||||
/* LIGHTMAP CAPTURE */
|
||||
|
||||
FUNCRID(lightmap_capture)
|
||||
|
||||
FUNC2(lightmap_capture_set_bounds, RID, const AABB &)
|
||||
FUNC1RC(AABB, lightmap_capture_get_bounds, RID)
|
||||
|
||||
FUNC2(lightmap_capture_set_octree, RID, const PoolVector<uint8_t> &)
|
||||
FUNC1RC(PoolVector<uint8_t>, lightmap_capture_get_octree, RID)
|
||||
FUNC2(lightmap_capture_set_octree_cell_transform, RID, const Transform &)
|
||||
FUNC1RC(Transform, lightmap_capture_get_octree_cell_transform, RID)
|
||||
FUNC2(lightmap_capture_set_octree_cell_subdiv, RID, int)
|
||||
FUNC1RC(int, lightmap_capture_get_octree_cell_subdiv, RID)
|
||||
FUNC2(lightmap_capture_set_energy, RID, float)
|
||||
FUNC1RC(float, lightmap_capture_get_energy, RID)
|
||||
|
||||
/* PARTICLES */
|
||||
|
||||
FUNCRID(particles)
|
||||
|
@ -425,6 +441,8 @@ public:
|
|||
FUNC3(instance_set_blend_shape_weight, RID, int, float)
|
||||
FUNC3(instance_set_surface_material, RID, int, RID)
|
||||
FUNC2(instance_set_visible, RID, bool)
|
||||
FUNC3(instance_set_use_lightmap, RID, RID, RID)
|
||||
|
||||
FUNC2(instance_set_custom_aabb, RID, AABB)
|
||||
|
||||
FUNC2(instance_attach_skeleton, RID, RID)
|
||||
|
|
|
@ -485,6 +485,20 @@ public:
|
|||
virtual void gi_probe_set_compress(RID p_probe, bool p_enable) = 0;
|
||||
virtual bool gi_probe_is_compressed(RID p_probe) const = 0;
|
||||
|
||||
/* LIGHTMAP CAPTURE */
|
||||
|
||||
virtual RID lightmap_capture_create() = 0;
|
||||
virtual void lightmap_capture_set_bounds(RID p_capture, const AABB &p_bounds) = 0;
|
||||
virtual AABB lightmap_capture_get_bounds(RID p_capture) const = 0;
|
||||
virtual void lightmap_capture_set_octree(RID p_capture, const PoolVector<uint8_t> &p_octree) = 0;
|
||||
virtual void lightmap_capture_set_octree_cell_transform(RID p_capture, const Transform &p_xform) = 0;
|
||||
virtual Transform lightmap_capture_get_octree_cell_transform(RID p_capture) const = 0;
|
||||
virtual void lightmap_capture_set_octree_cell_subdiv(RID p_capture, int p_subdiv) = 0;
|
||||
virtual int lightmap_capture_get_octree_cell_subdiv(RID p_capture) const = 0;
|
||||
virtual PoolVector<uint8_t> lightmap_capture_get_octree(RID p_capture) const = 0;
|
||||
virtual void lightmap_capture_set_energy(RID p_capture, float p_energy) = 0;
|
||||
virtual float lightmap_capture_get_energy(RID p_capture) const = 0;
|
||||
|
||||
/* PARTICLES API */
|
||||
|
||||
virtual RID particles_create() = 0;
|
||||
|
@ -735,6 +749,7 @@ public:
|
|||
INSTANCE_LIGHT,
|
||||
INSTANCE_REFLECTION_PROBE,
|
||||
INSTANCE_GI_PROBE,
|
||||
INSTANCE_LIGHTMAP_CAPTURE,
|
||||
INSTANCE_MAX,
|
||||
/*INSTANCE_BAKED_LIGHT_SAMPLER,*/
|
||||
|
||||
|
@ -755,6 +770,8 @@ public:
|
|||
virtual void instance_set_surface_material(RID p_instance, int p_surface, RID p_material) = 0;
|
||||
virtual void instance_set_visible(RID p_instance, bool p_visible) = 0;
|
||||
|
||||
virtual void instance_set_use_lightmap(RID p_instance, RID p_lightmap_instance, RID p_lightmap) = 0;
|
||||
|
||||
virtual void instance_set_custom_aabb(RID p_instance, AABB aabb) = 0;
|
||||
|
||||
virtual void instance_attach_skeleton(RID p_instance, RID p_skeleton) = 0;
|
||||
|
|
Loading…
Reference in a new issue