all light types and shadows are working, pending a lot of clean-up

This commit is contained in:
Juan Linietsky 2016-11-09 23:55:06 -03:00
parent 6b2a27bbe5
commit cacf9ebb7f
38 changed files with 3241 additions and 418 deletions

View file

@ -96,7 +96,10 @@ void* VisualServerScene::_instance_pair(void *p_self, OctreeElementID, Instance
List<InstanceLightData::PairInfo>::Element *E = light->geometries.push_back(pinfo);
light->shadow_sirty=true;
if (geom->can_cast_shadows) {
light->shadow_dirty=true;
}
geom->lighting_dirty=true;
return E; //this element should make freeing faster
@ -180,7 +183,9 @@ void VisualServerScene::_instance_unpair(void *p_self, OctreeElementID, Instance
geom->lighting.erase(E->get().L);
light->geometries.erase(E);
light->shadow_sirty=true;
if (geom->can_cast_shadows) {
light->shadow_dirty=true;
}
geom->lighting_dirty=true;
@ -346,6 +351,12 @@ void VisualServerScene::instance_set_base(RID p_instance, RID p_base){
}
instance->morph_values.clear();
for(int i=0;i<instance->materials.size();i++) {
if (instance->materials[i].is_valid()) {
VSG::storage->material_remove_instance_owner(instance->materials[i],instance);
}
}
instance->materials.clear();
#if 0
@ -667,7 +678,16 @@ void VisualServerScene::instance_set_surface_material(RID p_instance,int p_surfa
ERR_FAIL_INDEX(p_surface,instance->materials.size());
if (instance->materials[p_surface].is_valid()) {
VSG::storage->material_remove_instance_owner(instance->materials[p_surface],instance);
}
instance->materials[p_surface]=p_material;
instance->base_material_changed();
if (instance->materials[p_surface].is_valid()) {
VSG::storage->material_add_instance_owner(instance->materials[p_surface],instance);
}
}
@ -791,12 +811,14 @@ void VisualServerScene::instance_geometry_set_flag(RID p_instance,VS::InstanceFl
} break;
case VS::INSTANCE_FLAG_CAST_SHADOW: {
/*if (p_enabled == true) {
instance->cast_shadows = SHADOW_CASTING_SETTING_ON;
if (p_enabled == true) {
instance->cast_shadows = VS::SHADOW_CASTING_SETTING_ON;
}
else {
instance->cast_shadows = SHADOW_CASTING_SETTING_OFF;
}*/
instance->cast_shadows = VS::SHADOW_CASTING_SETTING_OFF;
}
instance->base_material_changed(); // to actually compute if shadows are visible or not
} break;
case VS::INSTANCE_FLAG_DEPH_SCALE: {
@ -820,8 +842,15 @@ void VisualServerScene::instance_geometry_set_material_override(RID p_instance,
Instance *instance = instance_owner.get( p_instance );
ERR_FAIL_COND( !instance );
if (instance->material_override.is_valid()) {
VSG::storage->material_remove_instance_owner(instance->material_override,instance);
}
instance->material_override=p_material;
instance->base_material_changed();
if (instance->material_override.is_valid()) {
VSG::storage->material_add_instance_owner(instance->material_override,instance);
}
}
@ -843,6 +872,7 @@ void VisualServerScene::_update_instance(Instance *p_instance) {
InstanceLightData *light = static_cast<InstanceLightData*>(p_instance->base_data);
VSG::scene_render->light_instance_set_transform( light->instance, p_instance->transform );
light->shadow_dirty=true;
}
@ -860,11 +890,13 @@ void VisualServerScene::_update_instance(Instance *p_instance) {
if ((1<<p_instance->base_type)&VS::INSTANCE_GEOMETRY_MASK) {
InstanceGeometryData *geom = static_cast<InstanceGeometryData*>(p_instance->base_data);
//make sure lights are updated
//make sure lights are updated if it casts shadow
for (List<Instance*>::Element *E=geom->lighting.front();E;E=E->next()) {
InstanceLightData *light = static_cast<InstanceLightData*>(E->get()->base_data);
light->shadow_sirty=true;
if (geom->can_cast_shadows) {
for (List<Instance*>::Element *E=geom->lighting.front();E;E=E->next()) {
InstanceLightData *light = static_cast<InstanceLightData*>(E->get()->base_data);
light->shadow_dirty=true;
}
}
}
@ -1095,9 +1127,371 @@ void VisualServerScene::_update_instance_aabb(Instance *p_instance) {
void VisualServerScene::render_camera(RID p_camera, RID p_scenario,Size2 p_viewport_size) {
void VisualServerScene::_light_instance_update_shadow(Instance *p_instance,Camera* p_camera,RID p_shadow_atlas,Scenario* p_scenario,Size2 p_viewport_rect) {
InstanceLightData * light = static_cast<InstanceLightData*>(p_instance->base_data);
switch(VSG::storage->light_get_type(p_instance->base)) {
case VS::LIGHT_DIRECTIONAL: {
float max_distance = p_camera->zfar;
float shadow_max = VSG::storage->light_get_param(p_instance->base,VS::LIGHT_PARAM_SHADOW_MAX_DISTANCE);
if (shadow_max>0) {
max_distance=MIN(shadow_max,max_distance);
}
max_distance=MAX(max_distance,p_camera->znear+0.001);
float range = max_distance-p_camera->znear;
int splits=0;
switch(VSG::storage->light_directional_get_shadow_mode(p_instance->base)) {
case VS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL: splits=1; break;
case VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_2_SPLITS: splits=2; break;
case VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_4_SPLITS: splits=4; break;
}
float distances[5];
distances[0]=p_camera->znear;
for(int i=0;i<splits;i++) {
distances[i+1]=p_camera->znear+VSG::storage->light_get_param(p_instance->base,VS::LightParam(VS::LIGHT_PARAM_SHADOW_SPLIT_1_OFFSET+i))*range;
};
distances[splits]=max_distance;
float texture_size=VSG::scene_render->get_directional_light_shadow_size(light->instance);
bool overlap = false;//rasterizer->light_instance_get_pssm_shadow_overlap(p_light->light_info->instance);
for (int i=0;i<splits;i++) {
// setup a camera matrix for that range!
CameraMatrix camera_matrix;
switch(p_camera->type) {
case Camera::ORTHOGONAL: {
camera_matrix.set_orthogonal(
p_camera->size,
p_viewport_rect.width / p_viewport_rect.height,
distances[(i==0 || !overlap )?i:i-1],
distances[i+1],
p_camera->vaspect
);
} break;
case Camera::PERSPECTIVE: {
camera_matrix.set_perspective(
p_camera->fov,
p_viewport_rect.width / (float)p_viewport_rect.height,
distances[(i==0 || !overlap )?i:i-1],
distances[i+1],
p_camera->vaspect
);
} break;
}
//obtain the frustum endpoints
Vector3 endpoints[8]; // frustum plane endpoints
bool res = camera_matrix.get_endpoints(p_camera->transform,endpoints);
ERR_CONTINUE(!res);
// obtain the light frustm ranges (given endpoints)
Vector3 x_vec=p_instance->transform.basis.get_axis( Vector3::AXIS_X ).normalized();
Vector3 y_vec=p_instance->transform.basis.get_axis( Vector3::AXIS_Y ).normalized();
Vector3 z_vec=p_instance->transform.basis.get_axis( Vector3::AXIS_Z ).normalized();
//z_vec points agsint the camera, like in default opengl
float x_min,x_max;
float y_min,y_max;
float z_min,z_max;
float x_min_cam,x_max_cam;
float y_min_cam,y_max_cam;
float z_min_cam,z_max_cam;
//used for culling
for(int j=0;j<8;j++) {
float d_x=x_vec.dot(endpoints[j]);
float d_y=y_vec.dot(endpoints[j]);
float d_z=z_vec.dot(endpoints[j]);
if (j==0 || d_x<x_min)
x_min=d_x;
if (j==0 || d_x>x_max)
x_max=d_x;
if (j==0 || d_y<y_min)
y_min=d_y;
if (j==0 || d_y>y_max)
y_max=d_y;
if (j==0 || d_z<z_min)
z_min=d_z;
if (j==0 || d_z>z_max)
z_max=d_z;
}
{
//camera viewport stuff
//this trick here is what stabilizes the shadow (make potential jaggies to not move)
//at the cost of some wasted resolution. Still the quality increase is very well worth it
Vector3 center;
for(int j=0;j<8;j++) {
center+=endpoints[j];
}
center/=8.0;
//center=x_vec*(x_max-x_min)*0.5 + y_vec*(y_max-y_min)*0.5 + z_vec*(z_max-z_min)*0.5;
float radius=0;
for(int j=0;j<8;j++) {
float d = center.distance_to(endpoints[j]);
if (d>radius)
radius=d;
}
radius *= texture_size/(texture_size-2.0); //add a texel by each side, so stepified texture will always fit
x_max_cam=x_vec.dot(center)+radius;
x_min_cam=x_vec.dot(center)-radius;
y_max_cam=y_vec.dot(center)+radius;
y_min_cam=y_vec.dot(center)-radius;
z_max_cam=z_vec.dot(center)+radius;
z_min_cam=z_vec.dot(center)-radius;
float unit = radius*2.0/texture_size;
x_max_cam=Math::stepify(x_max_cam,unit);
x_min_cam=Math::stepify(x_min_cam,unit);
y_max_cam=Math::stepify(y_max_cam,unit);
y_min_cam=Math::stepify(y_min_cam,unit);
}
//now that we now all ranges, we can proceed to make the light frustum planes, for culling octree
Vector<Plane> light_frustum_planes;
light_frustum_planes.resize(6);
//right/left
light_frustum_planes[0]=Plane( x_vec, x_max );
light_frustum_planes[1]=Plane( -x_vec, -x_min );
//top/bottom
light_frustum_planes[2]=Plane( y_vec, y_max );
light_frustum_planes[3]=Plane( -y_vec, -y_min );
//near/far
light_frustum_planes[4]=Plane( z_vec, z_max+1e6 );
light_frustum_planes[5]=Plane( -z_vec, -z_min ); // z_min is ok, since casters further than far-light plane are not needed
int cull_count = p_scenario->octree.cull_convex(light_frustum_planes,instance_shadow_cull_result,MAX_INSTANCE_CULL,VS::INSTANCE_GEOMETRY_MASK);
// a pre pass will need to be needed to determine the actual z-near to be used
for (int j=0;j<cull_count;j++) {
float min,max;
Instance *instance = instance_shadow_cull_result[j];
if (!instance->visible || !((1<<instance->base_type)&VS::INSTANCE_GEOMETRY_MASK) || !static_cast<InstanceGeometryData*>(instance->base_data)->can_cast_shadows) {
cull_count--;
SWAP(instance_shadow_cull_result[j],instance_shadow_cull_result[cull_count]);
j--;
}
instance->transformed_aabb.project_range_in_plane(Plane(z_vec,0),min,max);
if (max>z_max)
z_max=max;
}
{
CameraMatrix ortho_camera;
real_t half_x = (x_max_cam-x_min_cam) * 0.5;
real_t half_y = (y_max_cam-y_min_cam) * 0.5;
ortho_camera.set_orthogonal( -half_x, half_x,-half_y,half_y, 0, (z_max-z_min_cam) );
Transform ortho_transform;
ortho_transform.basis=p_instance->transform.basis;
ortho_transform.origin=x_vec*(x_min_cam+half_x)+y_vec*(y_min_cam+half_y)+z_vec*z_max;
VSG::scene_render->light_instance_set_shadow_transform(light->instance,ortho_camera,ortho_transform,0,distances[i+1],i);
}
VSG::scene_render->render_shadow(light->instance,p_shadow_atlas,i,(RasterizerScene::InstanceBase**)instance_shadow_cull_result,cull_count);
}
} break;
case VS::LIGHT_OMNI: {
VS::LightOmniShadowMode shadow_mode = VSG::storage->light_omni_get_shadow_mode(p_instance->base);
switch(shadow_mode) {
case VS::LIGHT_OMNI_SHADOW_DUAL_PARABOLOID: {
for(int i=0;i<2;i++) {
//using this one ensures that raster deferred will have it
float radius = VSG::storage->light_get_param( p_instance->base, VS::LIGHT_PARAM_RANGE);
float z =i==0?-1:1;
Vector<Plane> planes;
planes.resize(5);
planes[0]=p_instance->transform.xform(Plane(Vector3(0,0,z),radius));
planes[1]=p_instance->transform.xform(Plane(Vector3(1,0,z).normalized(),radius));
planes[2]=p_instance->transform.xform(Plane(Vector3(-1,0,z).normalized(),radius));
planes[3]=p_instance->transform.xform(Plane(Vector3(0,1,z).normalized(),radius));
planes[4]=p_instance->transform.xform(Plane(Vector3(0,-1,z).normalized(),radius));
int cull_count = p_scenario->octree.cull_convex(planes,instance_shadow_cull_result,MAX_INSTANCE_CULL,VS::INSTANCE_GEOMETRY_MASK);
for (int j=0;j<cull_count;j++) {
Instance *instance = instance_shadow_cull_result[j];
if (!instance->visible || !((1<<instance->base_type)&VS::INSTANCE_GEOMETRY_MASK) || !static_cast<InstanceGeometryData*>(instance->base_data)->can_cast_shadows) {
cull_count--;
SWAP(instance_shadow_cull_result[j],instance_shadow_cull_result[cull_count]);
j--;
}
}
VSG::scene_render->light_instance_set_shadow_transform(light->instance,CameraMatrix(),p_instance->transform,radius,0,i);
VSG::scene_render->render_shadow(light->instance,p_shadow_atlas,i,(RasterizerScene::InstanceBase**)instance_shadow_cull_result,cull_count);
}
} break;
case VS::LIGHT_OMNI_SHADOW_CUBE: {
float radius = VSG::storage->light_get_param( p_instance->base, VS::LIGHT_PARAM_RANGE);
CameraMatrix cm;
cm.set_perspective(90,1,0.01,radius);
for(int i=0;i<6;i++) {
//using this one ensures that raster deferred will have it
static const Vector3 view_normals[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)
};
static const Vector3 view_up[6]={
Vector3( 0,-1, 0),
Vector3( 0,-1, 0),
Vector3( 0, 0,-1),
Vector3( 0, 0,+1),
Vector3( 0,-1, 0),
Vector3( 0,-1, 0)
};
Transform xform = p_instance->transform * Transform().looking_at(view_normals[i],view_up[i]);
Vector<Plane> planes = cm.get_projection_planes(xform);
int cull_count = p_scenario->octree.cull_convex(planes,instance_shadow_cull_result,MAX_INSTANCE_CULL,VS::INSTANCE_GEOMETRY_MASK);
for (int j=0;j<cull_count;j++) {
Instance *instance = instance_shadow_cull_result[j];
if (!instance->visible || !((1<<instance->base_type)&VS::INSTANCE_GEOMETRY_MASK) || !static_cast<InstanceGeometryData*>(instance->base_data)->can_cast_shadows) {
cull_count--;
SWAP(instance_shadow_cull_result[j],instance_shadow_cull_result[cull_count]);
j--;
}
}
VSG::scene_render->light_instance_set_shadow_transform(light->instance,cm,xform,radius,0,i);
VSG::scene_render->render_shadow(light->instance,p_shadow_atlas,i,(RasterizerScene::InstanceBase**)instance_shadow_cull_result,cull_count);
}
//restore the regular DP matrix
VSG::scene_render->light_instance_set_shadow_transform(light->instance,CameraMatrix(),p_instance->transform,radius,0,0);
} break;
}
} break;
case VS::LIGHT_SPOT: {
float radius = VSG::storage->light_get_param( p_instance->base, VS::LIGHT_PARAM_RANGE);
float angle = VSG::storage->light_get_param( p_instance->base, VS::LIGHT_PARAM_SPOT_ANGLE);
CameraMatrix cm;
cm.set_perspective( 90, 1.0, 0.01, radius );
print_line("perspective: "+cm);
Vector<Plane> planes = cm.get_projection_planes(p_instance->transform);
int cull_count = p_scenario->octree.cull_convex(planes,instance_shadow_cull_result,MAX_INSTANCE_CULL,VS::INSTANCE_GEOMETRY_MASK);
for (int j=0;j<cull_count;j++) {
Instance *instance = instance_shadow_cull_result[j];
if (!instance->visible || !((1<<instance->base_type)&VS::INSTANCE_GEOMETRY_MASK) || !static_cast<InstanceGeometryData*>(instance->base_data)->can_cast_shadows) {
cull_count--;
SWAP(instance_shadow_cull_result[j],instance_shadow_cull_result[cull_count]);
j--;
}
}
print_line("MOMONGO");
VSG::scene_render->light_instance_set_shadow_transform(light->instance,cm,p_instance->transform,radius,0,0);
VSG::scene_render->render_shadow(light->instance,p_shadow_atlas,0,(RasterizerScene::InstanceBase**)instance_shadow_cull_result,cull_count);
} break;
}
}
void VisualServerScene::render_camera(RID p_camera, RID p_scenario,Size2 p_viewport_size,RID p_shadow_atlas) {
Camera *camera = camera_owner.getornull(p_camera);
ERR_FAIL_COND(!camera);
@ -1112,8 +1506,10 @@ void VisualServerScene::render_camera(RID p_camera, RID p_scenario,Size2 p_viewp
/* STEP 1 - SETUP CAMERA */
CameraMatrix camera_matrix;
Transform camera_inverse_xform = camera->transform.affine_inverse();
bool ortho=false;
switch(camera->type) {
case Camera::ORTHOGONAL: {
@ -1268,12 +1664,14 @@ void VisualServerScene::render_camera(RID p_camera, RID p_scenario,Size2 p_viewp
//do not add this light if no geometry is affected by it..
light_cull_result[light_cull_count]=ins;
light_instance_cull_result[light_cull_count]=light->instance;
VSG::scene_render->light_instance_mark_visible(light->instance); //mark it visible for shadow allocation later
if (p_shadow_atlas.is_valid() && VSG::storage->light_has_shadow(ins->base)) {
VSG::scene_render->light_instance_mark_visible(light->instance); //mark it visible for shadow allocation later
}
light_cull_count++;
}
// rasterizer->light_instance_set_active_hint(ins->light_info->instance);
}
} else if ((1<<ins->base_type)&VS::INSTANCE_GEOMETRY_MASK && ins->visible && ins->cast_shadows!=VS::SHADOW_CASTING_SETTING_SHADOWS_ONLY) {
@ -1386,6 +1784,10 @@ void VisualServerScene::render_camera(RID p_camera, RID p_scenario,Size2 p_viewp
// directional lights
{
Instance** lights_with_shadow = (Instance**)alloca(sizeof(Instance*)*light_cull_count);
int directional_shadow_count=0;
for (List<Instance*>::Element *E=scenario->directional_lights.front();E;E=E->next()) {
if (light_cull_count+directional_light_count>=MAX_LIGHTS_CULLED) {
@ -1401,42 +1803,142 @@ void VisualServerScene::render_camera(RID p_camera, RID p_scenario,Size2 p_viewp
//check shadow..
/* if (light && light->light_info->enabled && rasterizer->light_has_shadow(light->base_rid)) {
//rasterizer->light_instance_set_active_hint(light->light_info->instance);
_light_instance_update_shadow(light,p_scenario,camera,cull_range);
if (light && VSG::storage->light_has_shadow(E->get()->base)) {
lights_with_shadow[directional_shadow_count++]=E->get();
}
*/
//add to list
directional_light_ptr[directional_light_count++]=light->instance;
}
VSG::scene_render->set_directional_shadow_count(directional_shadow_count);
for(int i=0;i<directional_shadow_count;i++) {
_light_instance_update_shadow(lights_with_shadow[i],camera,p_shadow_atlas,scenario,p_viewport_size);
}
}
#if 0
{ //this should eventually change to
//assign shadows by distance to camera
SortArray<Instance*,_InstanceLightsort> sorter;
sorter.sort(light_cull_result,light_cull_count);
{ //setup shadow maps
//SortArray<Instance*,_InstanceLightsort> sorter;
//sorter.sort(light_cull_result,light_cull_count);
for (int i=0;i<light_cull_count;i++) {
Instance *ins = light_cull_result[i];
if (!rasterizer->light_has_shadow(ins->base_rid) || !shadows_enabled)
if (!p_shadow_atlas.is_valid() || !VSG::storage->light_has_shadow(ins->base))
continue;
/* for far shadows?
if (ins->version == ins->light_info->last_version && rasterizer->light_instance_has_far_shadow(ins->light_info->instance))
continue; // didn't change
*/
InstanceLightData * light = static_cast<InstanceLightData*>(ins->base_data);
float coverage;
{ //compute coverage
Transform cam_xf = camera->transform;
float zn = camera_matrix.get_z_near();
Plane p (cam_xf.origin + cam_xf.basis.get_axis(2) * -zn, -cam_xf.basis.get_axis(2) ); //camera near plane
float vp_w,vp_h; //near plane size in screen coordinates
camera_matrix.get_viewport_size(vp_w,vp_h);
switch(VSG::storage->light_get_type(ins->base)) {
case VS::LIGHT_OMNI: {
float radius = VSG::storage->light_get_param(ins->base,VS::LIGHT_PARAM_RANGE);
//get two points parallel to near plane
Vector3 points[2]={
ins->transform.origin,
ins->transform.origin+cam_xf.basis.get_axis(0)*radius
};
if (!ortho) {
//if using perspetive, map them to near plane
for(int j=0;j<2;j++) {
if (p.distance_to(points[j]) < 0 ) {
points[j].z=-zn; //small hack to keep size constant when hitting the screen
}
p.intersects_segment(cam_xf.origin,points[j],&points[j]); //map to plane
}
}
float screen_diameter = points[0].distance_to(points[1])*2;
coverage = screen_diameter / (vp_w+vp_h);
} break;
case VS::LIGHT_SPOT: {
float radius = VSG::storage->light_get_param(ins->base,VS::LIGHT_PARAM_RANGE);
float angle = VSG::storage->light_get_param(ins->base,VS::LIGHT_PARAM_SPOT_ANGLE);
float w = radius*Math::sin(Math::deg2rad(angle));
float d = radius*Math::cos(Math::deg2rad(angle));
Vector3 base = ins->transform.origin-ins->transform.basis.get_axis(2).normalized()*d;
Vector3 points[2]={
base,
base+cam_xf.basis.get_axis(0)*w
};
if (!ortho) {
//if using perspetive, map them to near plane
for(int j=0;j<2;j++) {
if (p.distance_to(points[j]) < 0 ) {
points[j].z=-zn; //small hack to keep size constant when hitting the screen
}
p.intersects_segment(cam_xf.origin,points[j],&points[j]); //map to plane
}
}
float screen_diameter = points[0].distance_to(points[1])*2;
coverage = screen_diameter / (vp_w+vp_h);
} break;
default: {
ERR_PRINT("Invalid Light Type");
}
}
}
if (light->shadow_dirty) {
light->last_version++;
light->shadow_dirty=false;
}
bool redraw = VSG::scene_render->shadow_atlas_update_light(p_shadow_atlas,light->instance,coverage,light->last_version);
if (redraw) {
//must redraw!
_light_instance_update_shadow(ins,camera,p_shadow_atlas,scenario,p_viewport_size);
}
_light_instance_update_shadow(ins,p_scenario,camera,cull_range);
ins->light_info->last_version=ins->version;
}
}
#endif
/* ENVIRONMENT */
RID environment;
@ -1492,7 +1994,8 @@ void VisualServerScene::render_camera(RID p_camera, RID p_scenario,Size2 p_viewp
VSG::scene_render->render_scene(camera->transform, camera_matrix,ortho,(RasterizerScene::InstanceBase**)instance_cull_result,cull_count,light_instance_cull_result,light_cull_count,directional_light_ptr,directional_light_count,environment);
VSG::scene_render->render_scene(camera->transform, camera_matrix,ortho,(RasterizerScene::InstanceBase**)instance_cull_result,cull_count,light_instance_cull_result,light_cull_count+directional_light_count,environment,p_shadow_atlas);
}
@ -1505,9 +2008,77 @@ void VisualServerScene::_update_dirty_instance(Instance *p_instance) {
_update_instance_aabb(p_instance);
if (p_instance->update_materials) {
if (p_instance->base_type==VS::INSTANCE_MESH) {
p_instance->materials.resize(VSG::storage->mesh_get_surface_count(p_instance->base));
//remove materials no longer used and un-own them
int new_mat_count = VSG::storage->mesh_get_surface_count(p_instance->base);
for(int i=p_instance->materials.size()-1;i>=new_mat_count;i--) {
if (p_instance->materials[i].is_valid()) {
VSG::storage->material_remove_instance_owner(p_instance->materials[i],p_instance);
}
}
p_instance->materials.resize(new_mat_count);
}
if ((1<<p_instance->base_type)&VS::INSTANCE_GEOMETRY_MASK) {
InstanceGeometryData *geom = static_cast<InstanceGeometryData*>(p_instance->base_data);
bool can_cast_shadows=true;
if (p_instance->cast_shadows==VS::SHADOW_CASTING_SETTING_OFF) {
can_cast_shadows=false;
} else if (p_instance->material_override.is_valid()) {
can_cast_shadows=VSG::storage->material_casts_shadows(p_instance->material_override);
} else {
RID mesh;
if (p_instance->base_type==VS::INSTANCE_MESH) {
mesh=p_instance->base;
} else if (p_instance->base_type==VS::INSTANCE_MULTIMESH) {
}
if (mesh.is_valid()) {
bool cast_shadows=false;
for(int i=0;i<p_instance->materials.size();i++) {
RID mat = p_instance->materials[i].is_valid()?p_instance->materials[i]:VSG::storage->mesh_surface_get_material(mesh,i);
if (!mat.is_valid()) {
cast_shadows=true;
break;
}
if (VSG::storage->material_casts_shadows(mat)) {
cast_shadows=true;
break;
}
}
if (!cast_shadows) {
can_cast_shadows=false;
}
}
}
if (can_cast_shadows!=geom->can_cast_shadows) {
//ability to cast shadows change, let lights now
for (List<Instance*>::Element *E=geom->lighting.front();E;E=E->next()) {
InstanceLightData *light = static_cast<InstanceLightData*>(E->get()->base_data);
light->shadow_dirty=true;
}
geom->can_cast_shadows=can_cast_shadows;
}
}
}
_update_instance(p_instance);
@ -1557,6 +2128,7 @@ bool VisualServerScene::free(RID p_rid) {
instance_set_room(p_rid,RID());
instance_set_scenario(p_rid,RID());
instance_set_base(p_rid,RID());
instance_geometry_set_material_override(p_rid,RID());
if (instance->skeleton.is_valid())
instance_attach_skeleton(p_rid,RID());