1
0
mirror of https://github.com/godotengine/godot.git synced 2025-11-05 12:10:55 +00:00

Implement global and per instance shader uniforms.

Adds two keywords to shader language for uniforms:
-'global'
-'instance'

This allows them to reference values outside the material.
This commit is contained in:
Juan Linietsky
2020-04-16 23:52:00 -03:00
parent 30ab5c9baa
commit 0e1c66d9fc
49 changed files with 3124 additions and 69 deletions

View File

@@ -968,6 +968,67 @@ void RenderingServerScene::instance_geometry_set_draw_range(RID p_instance, floa
void RenderingServerScene::instance_geometry_set_as_instance_lod(RID p_instance, RID p_as_lod_of_instance) {
}
void RenderingServerScene::instance_geometry_set_shader_parameter(RID p_instance, const StringName &p_parameter, const Variant &p_value) {
Instance *instance = instance_owner.getornull(p_instance);
ERR_FAIL_COND(!instance);
Map<StringName, RasterizerScene::InstanceBase::InstanceShaderParameter>::Element *E = instance->instance_shader_parameters.find(p_parameter);
if (!E) {
RasterizerScene::InstanceBase::InstanceShaderParameter isp;
isp.index = -1;
isp.info = PropertyInfo();
isp.value = p_value;
instance->instance_shader_parameters[p_parameter] = isp;
} else {
E->get().value = p_value;
if (E->get().index >= 0 && instance->instance_allocated_shader_parameters) {
//update directly
RSG::storage->global_variables_instance_update(p_instance, E->get().index, p_value);
}
}
}
Variant RenderingServerScene::instance_geometry_get_shader_parameter(RID p_instance, const StringName &p_parameter) const {
const Instance *instance = const_cast<RenderingServerScene *>(this)->instance_owner.getornull(p_instance);
ERR_FAIL_COND_V(!instance, Variant());
if (instance->instance_shader_parameters.has(p_parameter)) {
return instance->instance_shader_parameters[p_parameter].value;
}
return Variant();
}
Variant RenderingServerScene::instance_geometry_get_shader_parameter_default_value(RID p_instance, const StringName &p_parameter) const {
const Instance *instance = const_cast<RenderingServerScene *>(this)->instance_owner.getornull(p_instance);
ERR_FAIL_COND_V(!instance, Variant());
if (instance->instance_shader_parameters.has(p_parameter)) {
return instance->instance_shader_parameters[p_parameter].default_value;
}
return Variant();
}
void RenderingServerScene::instance_geometry_get_shader_parameter_list(RID p_instance, List<PropertyInfo> *p_parameters) const {
const Instance *instance = const_cast<RenderingServerScene *>(this)->instance_owner.getornull(p_instance);
ERR_FAIL_COND(!instance);
const_cast<RenderingServerScene *>(this)->update_dirty_instances();
Vector<StringName> names;
for (Map<StringName, RasterizerScene::InstanceBase::InstanceShaderParameter>::Element *E = instance->instance_shader_parameters.front(); E; E = E->next()) {
names.push_back(E->key());
}
names.sort_custom<StringName::AlphCompare>();
for (int i = 0; i < names.size(); i++) {
PropertyInfo pinfo = instance->instance_shader_parameters[names[i]].info;
p_parameters->push_back(pinfo);
}
}
void RenderingServerScene::_update_instance(Instance *p_instance) {
p_instance->version++;
@@ -2761,6 +2822,35 @@ void RenderingServerScene::render_probes() {
}
}
void RenderingServerScene::_update_instance_shader_parameters_from_material(Map<StringName, RasterizerScene::InstanceBase::InstanceShaderParameter> &isparams, const Map<StringName, RasterizerScene::InstanceBase::InstanceShaderParameter> &existing_isparams, RID p_material) {
List<RasterizerStorage::InstanceShaderParam> plist;
RSG::storage->material_get_instance_shader_parameters(p_material, &plist);
for (List<RasterizerStorage::InstanceShaderParam>::Element *E = plist.front(); E; E = E->next()) {
StringName name = E->get().info.name;
if (isparams.has(name)) {
if (isparams[name].info.type != E->get().info.type) {
WARN_PRINT("More than one material in instance export the same instance shader uniform '" + E->get().info.name + "', but they do it with different data types. Only the first one (in order) will display correctly.");
}
if (isparams[name].index != E->get().index) {
WARN_PRINT("More than one material in instance export the same instance shader uniform '" + E->get().info.name + "', but they do it with different indices. Only the first one (in order) will display correctly.");
}
continue; //first one found always has priority
}
RasterizerScene::InstanceBase::InstanceShaderParameter isp;
isp.index = E->get().index;
isp.info = E->get().info;
isp.default_value = E->get().default_value;
if (existing_isparams.has(name)) {
isp.value = existing_isparams[name].value;
} else {
isp.value = E->get().default_value;
}
isparams[name] = isp;
}
}
void RenderingServerScene::_update_dirty_instance(Instance *p_instance) {
if (p_instance->update_aabb) {
@@ -2800,12 +2890,18 @@ void RenderingServerScene::_update_dirty_instance(Instance *p_instance) {
bool can_cast_shadows = true;
bool is_animated = false;
Map<StringName, RasterizerScene::InstanceBase::InstanceShaderParameter> isparams;
if (p_instance->cast_shadows == RS::SHADOW_CASTING_SETTING_OFF) {
can_cast_shadows = false;
} else if (p_instance->material_override.is_valid()) {
can_cast_shadows = RSG::storage->material_casts_shadows(p_instance->material_override);
}
if (p_instance->material_override.is_valid()) {
if (!RSG::storage->material_casts_shadows(p_instance->material_override)) {
can_cast_shadows = false;
}
is_animated = RSG::storage->material_is_animated(p_instance->material_override);
_update_instance_shader_parameters_from_material(isparams, p_instance->instance_shader_parameters, p_instance->material_override);
} else {
if (p_instance->base_type == RS::INSTANCE_MESH) {
@@ -2830,6 +2926,8 @@ void RenderingServerScene::_update_dirty_instance(Instance *p_instance) {
is_animated = true;
}
_update_instance_shader_parameters_from_material(isparams, p_instance->instance_shader_parameters, mat);
RSG::storage->material_update_dependency(mat, p_instance);
}
}
@@ -2862,6 +2960,8 @@ void RenderingServerScene::_update_dirty_instance(Instance *p_instance) {
is_animated = true;
}
_update_instance_shader_parameters_from_material(isparams, p_instance->instance_shader_parameters, mat);
RSG::storage->material_update_dependency(mat, p_instance);
}
}
@@ -2876,12 +2976,18 @@ void RenderingServerScene::_update_dirty_instance(Instance *p_instance) {
RID mat = RSG::storage->immediate_get_material(p_instance->base);
can_cast_shadows = !mat.is_valid() || RSG::storage->material_casts_shadows(mat);
if (!(!mat.is_valid() || RSG::storage->material_casts_shadows(mat))) {
can_cast_shadows = false;
}
if (mat.is_valid() && RSG::storage->material_is_animated(mat)) {
is_animated = true;
}
if (mat.is_valid()) {
_update_instance_shader_parameters_from_material(isparams, p_instance->instance_shader_parameters, mat);
}
if (mat.is_valid()) {
RSG::storage->material_update_dependency(mat, p_instance);
}
@@ -2915,6 +3021,8 @@ void RenderingServerScene::_update_dirty_instance(Instance *p_instance) {
is_animated = true;
}
_update_instance_shader_parameters_from_material(isparams, p_instance->instance_shader_parameters, mat);
RSG::storage->material_update_dependency(mat, p_instance);
}
}
@@ -2937,6 +3045,22 @@ void RenderingServerScene::_update_dirty_instance(Instance *p_instance) {
}
geom->material_is_animated = is_animated;
p_instance->instance_shader_parameters = isparams;
if (p_instance->instance_allocated_shader_parameters != (p_instance->instance_shader_parameters.size() > 0)) {
p_instance->instance_allocated_shader_parameters = (p_instance->instance_shader_parameters.size() > 0);
if (p_instance->instance_allocated_shader_parameters) {
p_instance->instance_allocated_shader_parameters_offset = RSG::storage->global_variables_instance_allocate(p_instance->self);
for (Map<StringName, RasterizerScene::InstanceBase::InstanceShaderParameter>::Element *E = p_instance->instance_shader_parameters.front(); E; E = E->next()) {
if (E->get().value.get_type() != Variant::NIL) {
RSG::storage->global_variables_instance_update(p_instance->self, E->get().index, E->get().value);
}
}
} else {
RSG::storage->global_variables_instance_free(p_instance->self);
p_instance->instance_allocated_shader_parameters_offset = -1;
}
}
}
if (p_instance->skeleton.is_valid()) {
@@ -2998,6 +3122,10 @@ bool RenderingServerScene::free(RID p_rid) {
instance_geometry_set_material_override(p_rid, RID());
instance_attach_skeleton(p_rid, RID());
if (instance->instance_allocated_shader_parameters) {
//free the used shader parameters
RSG::storage->global_variables_instance_free(instance->self);
}
update_dirty_instances(); //in case something changed this
instance_owner.free(p_rid);