|
@@ -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());
|