diff options
author | Juan Linietsky <reduzio@gmail.com> | 2020-03-27 15:21:27 -0300 |
---|---|---|
committer | Juan Linietsky <reduzio@gmail.com> | 2020-03-27 15:21:27 -0300 |
commit | a6f3bc7c696af03e3875f78e098d2476e409d15e (patch) | |
tree | fc1bb58e900436c48c03c52106eb57250442ae35 /servers/rendering | |
parent | 307b1b3a5835ecdb477859785c673a07e248f904 (diff) |
Renaming of servers for coherency.
VisualServer -> RenderingServer
PhysicsServer -> PhysicsServer3D
Physics2DServer -> PhysicsServer2D
NavigationServer -> NavigationServer3D
Navigation2DServer -> NavigationServer2D
Also renamed corresponding files.
Diffstat (limited to 'servers/rendering')
67 files changed, 47600 insertions, 0 deletions
diff --git a/servers/rendering/SCsub b/servers/rendering/SCsub new file mode 100644 index 0000000000..fca18bfea0 --- /dev/null +++ b/servers/rendering/SCsub @@ -0,0 +1,7 @@ +#!/usr/bin/env python + +Import('env') + +env.add_source_files(env.servers_sources, "*.cpp") + +SConscript("rasterizer_rd/SCsub") diff --git a/servers/rendering/rasterizer.cpp b/servers/rendering/rasterizer.cpp new file mode 100644 index 0000000000..a3f93a3f8c --- /dev/null +++ b/servers/rendering/rasterizer.cpp @@ -0,0 +1,77 @@ +/*************************************************************************/ +/* rasterizer.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "rasterizer.h" + +#include "core/os/os.h" +#include "core/print_string.h" + +Rasterizer *(*Rasterizer::_create_func)() = NULL; + +void RasterizerScene::InstanceDependency::instance_notify_changed(bool p_aabb, bool p_dependencies) { + for (Map<InstanceBase *, uint32_t>::Element *E = instances.front(); E; E = E->next()) { + E->key()->dependency_changed(p_aabb, p_dependencies); + } +} +void RasterizerScene::InstanceDependency::instance_notify_deleted(RID p_deleted) { + for (Map<InstanceBase *, uint32_t>::Element *E = instances.front(); E; E = E->next()) { + E->key()->dependency_deleted(p_deleted); + } + for (Map<InstanceBase *, uint32_t>::Element *E = instances.front(); E; E = E->next()) { + E->key()->dependencies.erase(this); + } + + instances.clear(); +} + +RasterizerScene::InstanceDependency::~InstanceDependency() { +#ifdef DEBUG_ENABLED + if (instances.size()) { + WARN_PRINT("Leaked instance dependency: Bug - did not call instance_notify_deleted when freeing."); + for (Map<InstanceBase *, uint32_t>::Element *E = instances.front(); E; E = E->next()) { + E->key()->dependencies.erase(this); + } + } +#endif +} + +Rasterizer *Rasterizer::create() { + + return _create_func(); +} + +RasterizerCanvas *RasterizerCanvas::singleton = NULL; + +RasterizerStorage *RasterizerStorage::base_singleton = NULL; + +RasterizerStorage::RasterizerStorage() { + + base_singleton = this; +} diff --git a/servers/rendering/rasterizer.h b/servers/rendering/rasterizer.h new file mode 100644 index 0000000000..f63d1077bb --- /dev/null +++ b/servers/rendering/rasterizer.h @@ -0,0 +1,1307 @@ +/*************************************************************************/ +/* rasterizer.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef RASTERIZER_H +#define RASTERIZER_H + +#include "core/math/camera_matrix.h" +#include "servers/rendering_server.h" + +#include "core/pair.h" +#include "core/self_list.h" + +class RasterizerScene { + +public: + /* SHADOW ATLAS API */ + + virtual RID shadow_atlas_create() = 0; + virtual void shadow_atlas_set_size(RID p_atlas, int p_size) = 0; + virtual void shadow_atlas_set_quadrant_subdivision(RID p_atlas, int p_quadrant, int p_subdivision) = 0; + virtual bool shadow_atlas_update_light(RID p_atlas, RID p_light_intance, float p_coverage, uint64_t p_light_version) = 0; + + virtual void directional_shadow_atlas_set_size(int p_size) = 0; + virtual int get_directional_light_shadow_size(RID p_light_intance) = 0; + virtual void set_directional_shadow_count(int p_count) = 0; + + /* SKY API */ + + virtual RID sky_create() = 0; + virtual void sky_set_radiance_size(RID p_sky, int p_radiance_size) = 0; + virtual void sky_set_mode(RID p_sky, RS::SkyMode p_samples) = 0; + virtual void sky_set_material(RID p_sky, RID p_material) = 0; + + /* ENVIRONMENT API */ + + virtual RID environment_create() = 0; + + virtual void environment_set_background(RID p_env, RS::EnvironmentBG p_bg) = 0; + virtual void environment_set_sky(RID p_env, RID p_sky) = 0; + virtual void environment_set_sky_custom_fov(RID p_env, float p_scale) = 0; + virtual void environment_set_sky_orientation(RID p_env, const Basis &p_orientation) = 0; + virtual void environment_set_bg_color(RID p_env, const Color &p_color) = 0; + virtual void environment_set_bg_energy(RID p_env, float p_energy) = 0; + virtual void environment_set_canvas_max_layer(RID p_env, int p_max_layer) = 0; + virtual void environment_set_ambient_light(RID p_env, const Color &p_color, RS::EnvironmentAmbientSource p_ambient = RS::ENV_AMBIENT_SOURCE_BG, float p_energy = 1.0, float p_sky_contribution = 0.0, RS::EnvironmentReflectionSource p_reflection_source = RS::ENV_REFLECTION_SOURCE_BG, const Color &p_ao_color = Color()) = 0; +// FIXME: Disabled during Vulkan refactoring, should be ported. +#if 0 + virtual void environment_set_camera_feed_id(RID p_env, int p_camera_feed_id) = 0; +#endif + + virtual void environment_set_glow(RID p_env, bool p_enable, int p_level_flags, float p_intensity, float p_strength, float p_mix, float p_bloom_threshold, RS::EnvironmentGlowBlendMode p_blend_mode, float p_hdr_bleed_threshold, float p_hdr_bleed_scale, float p_hdr_luminance_cap, bool p_bicubic_upscale) = 0; + virtual void environment_set_fog(RID p_env, bool p_enable, float p_begin, float p_end, RID p_gradient_texture) = 0; + + virtual void environment_set_ssr(RID p_env, bool p_enable, int p_max_steps, float p_fade_int, float p_fade_out, float p_depth_tolerance, bool p_roughness) = 0; + virtual void environment_set_ssao(RID p_env, bool p_enable, float p_radius, float p_intensity, float p_bias, float p_light_affect, float p_ao_channel_affect, RS::EnvironmentSSAOBlur p_blur, float p_bilateral_sharpness) = 0; + + virtual void environment_set_ssao_quality(RS::EnvironmentSSAOQuality p_quality, bool p_half_size) = 0; + + virtual void environment_set_tonemap(RID p_env, RS::EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white, bool p_auto_exposure, float p_min_luminance, float p_max_luminance, float p_auto_exp_speed, float p_auto_exp_scale) = 0; + + virtual void environment_set_adjustment(RID p_env, bool p_enable, float p_brightness, float p_contrast, float p_saturation, RID p_ramp) = 0; + + virtual void environment_set_fog(RID p_env, bool p_enable, const Color &p_color, const Color &p_sun_color, float p_sun_amount) = 0; + virtual void environment_set_fog_depth(RID p_env, bool p_enable, float p_depth_begin, float p_depth_end, float p_depth_curve, bool p_transmit, float p_transmit_curve) = 0; + virtual void environment_set_fog_height(RID p_env, bool p_enable, float p_min_height, float p_max_height, float p_height_curve) = 0; + + virtual bool is_environment(RID p_env) const = 0; + virtual RS::EnvironmentBG environment_get_background(RID p_env) const = 0; + virtual int environment_get_canvas_max_layer(RID p_env) const = 0; + + virtual RID camera_effects_create() = 0; + + virtual void camera_effects_set_dof_blur_quality(RS::DOFBlurQuality p_quality, bool p_use_jitter) = 0; + virtual void camera_effects_set_dof_blur_bokeh_shape(RS::DOFBokehShape p_shape) = 0; + + virtual void camera_effects_set_dof_blur(RID p_camera_effects, bool p_far_enable, float p_far_distance, float p_far_transition, bool p_near_enable, float p_near_distance, float p_near_transition, float p_amount) = 0; + virtual void camera_effects_set_custom_exposure(RID p_camera_effects, bool p_enable, float p_exposure) = 0; + + struct InstanceBase; + + struct InstanceDependency { + + void instance_notify_changed(bool p_aabb, bool p_dependencies); + void instance_notify_deleted(RID p_deleted); + + ~InstanceDependency(); + + private: + friend struct InstanceBase; + Map<InstanceBase *, uint32_t> instances; + }; + + struct InstanceBase { + + RS::InstanceType base_type; + RID base; + + RID skeleton; + RID material_override; + + RID instance_data; + + Transform transform; + + int depth_layer; + uint32_t layer_mask; + uint32_t instance_version; + + //RID sampled_light; + + Vector<RID> materials; + Vector<RID> light_instances; + Vector<RID> reflection_probe_instances; + Vector<RID> gi_probe_instances; + + Vector<float> blend_values; + + RS::ShadowCastingSetting cast_shadows; + + //fit in 32 bits + bool mirror : 8; + bool receive_shadows : 8; + bool visible : 8; + bool baked_light : 2; //this flag is only to know if it actually did use baked light + bool dynamic_gi : 2; //this flag is only to know if it actually did use baked light + bool redraw_if_visible : 4; + + float depth; //used for sorting + + 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 + + AABB aabb; + AABB transformed_aabb; + + virtual void dependency_deleted(RID p_dependency) = 0; + virtual void dependency_changed(bool p_aabb, bool p_dependencies) = 0; + + Set<InstanceDependency *> dependencies; + + void instance_increase_version() { + instance_version++; + } + + void update_dependency(InstanceDependency *p_dependency) { + dependencies.insert(p_dependency); + p_dependency->instances[this] = instance_version; + } + + void clean_up_dependencies() { + List<Pair<InstanceDependency *, Map<InstanceBase *, uint32_t>::Element *>> to_clean_up; + for (Set<InstanceDependency *>::Element *E = dependencies.front(); E; E = E->next()) { + InstanceDependency *dep = E->get(); + Map<InstanceBase *, uint32_t>::Element *F = dep->instances.find(this); + ERR_CONTINUE(!F); + if (F->get() != instance_version) { + Pair<InstanceDependency *, Map<InstanceBase *, uint32_t>::Element *> p; + p.first = dep; + p.second = F; + to_clean_up.push_back(p); + } + } + + while (to_clean_up.size()) { + to_clean_up.front()->get().first->instances.erase(to_clean_up.front()->get().second); + to_clean_up.pop_front(); + } + } + + void clear_dependencies() { + for (Set<InstanceDependency *>::Element *E = dependencies.front(); E; E = E->next()) { + InstanceDependency *dep = E->get(); + dep->instances.erase(this); + } + dependencies.clear(); + } + + InstanceBase() : + dependency_item(this) { + + base_type = RS::INSTANCE_NONE; + cast_shadows = RS::SHADOW_CASTING_SETTING_ON; + receive_shadows = true; + visible = true; + depth_layer = 0; + layer_mask = 1; + instance_version = 0; + baked_light = false; + dynamic_gi = false; + redraw_if_visible = false; + lightmap_capture = NULL; + } + + virtual ~InstanceBase() { + clear_dependencies(); + } + }; + + virtual RID light_instance_create(RID p_light) = 0; + virtual void light_instance_set_transform(RID p_light_instance, const Transform &p_transform) = 0; + virtual void light_instance_set_shadow_transform(RID p_light_instance, const CameraMatrix &p_projection, const Transform &p_transform, float p_far, float p_split, int p_pass, float p_bias_scale = 1.0) = 0; + virtual void light_instance_mark_visible(RID p_light_instance) = 0; + virtual bool light_instances_can_render_shadow_cube() const { return true; } + + virtual RID reflection_atlas_create() = 0; + virtual void reflection_atlas_set_size(RID p_ref_atlas, int p_reflection_size, int p_reflection_count) = 0; + + virtual RID reflection_probe_instance_create(RID p_probe) = 0; + virtual void reflection_probe_instance_set_transform(RID p_instance, const Transform &p_transform) = 0; + virtual void reflection_probe_release_atlas_index(RID p_instance) = 0; + virtual bool reflection_probe_instance_needs_redraw(RID p_instance) = 0; + virtual bool reflection_probe_instance_has_reflection(RID p_instance) = 0; + virtual bool reflection_probe_instance_begin_render(RID p_instance, RID p_reflection_atlas) = 0; + virtual bool reflection_probe_instance_postprocess_step(RID p_instance) = 0; + + virtual RID gi_probe_instance_create(RID p_gi_probe) = 0; + virtual void gi_probe_instance_set_transform_to_data(RID p_probe, const Transform &p_xform) = 0; + virtual bool gi_probe_needs_update(RID p_probe) const = 0; + virtual void gi_probe_update(RID p_probe, bool p_update_light_instances, const Vector<RID> &p_light_instances, int p_dynamic_object_count, InstanceBase **p_dynamic_objects) = 0; + + virtual void render_scene(RID p_render_buffers, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID *p_light_cull_result, int p_light_cull_count, RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, RID *p_gi_probe_cull_result, int p_gi_probe_cull_count, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass) = 0; + + virtual void render_shadow(RID p_light, RID p_shadow_atlas, int p_pass, InstanceBase **p_cull_result, int p_cull_count) = 0; + virtual void render_material(const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID p_framebuffer, const Rect2i &p_region) = 0; + + virtual void set_scene_pass(uint64_t p_pass) = 0; + virtual void set_time(double p_time, double p_step) = 0; + virtual void set_debug_draw_mode(RS::ViewportDebugDraw p_debug_draw) = 0; + + virtual RID render_buffers_create() = 0; + virtual void render_buffers_configure(RID p_render_buffers, RID p_render_target, int p_width, int p_height, RS::ViewportMSAA p_msaa) = 0; + + virtual void screen_space_roughness_limiter_set_active(bool p_enable, float p_curve) = 0; + virtual bool screen_space_roughness_limiter_is_active() const = 0; + + virtual bool free(RID p_rid) = 0; + + virtual void update() = 0; + virtual ~RasterizerScene() {} +}; + +class RasterizerStorage { + + Color default_clear_color; + +public: + /* TEXTURE API */ + + virtual RID texture_2d_create(const Ref<Image> &p_image) = 0; + virtual RID texture_2d_layered_create(const Vector<Ref<Image>> &p_layers, RS::TextureLayeredType p_layered_type) = 0; + virtual RID texture_3d_create(const Vector<Ref<Image>> &p_slices) = 0; //all slices, then all the mipmaps, must be coherent + virtual RID texture_proxy_create(RID p_base) = 0; //all slices, then all the mipmaps, must be coherent + + virtual void texture_2d_update_immediate(RID p_texture, const Ref<Image> &p_image, int p_layer = 0) = 0; //mostly used for video and streaming + virtual void texture_2d_update(RID p_texture, const Ref<Image> &p_image, int p_layer = 0) = 0; + virtual void texture_3d_update(RID p_texture, const Ref<Image> &p_image, int p_depth, int p_mipmap) = 0; + virtual void texture_proxy_update(RID p_proxy, RID p_base) = 0; + + //these two APIs can be used together or in combination with the others. + virtual RID texture_2d_placeholder_create() = 0; + virtual RID texture_2d_layered_placeholder_create() = 0; + virtual RID texture_3d_placeholder_create() = 0; + + virtual Ref<Image> texture_2d_get(RID p_texture) const = 0; + virtual Ref<Image> texture_2d_layer_get(RID p_texture, int p_layer) const = 0; + virtual Ref<Image> texture_3d_slice_get(RID p_texture, int p_depth, int p_mipmap) const = 0; + + virtual void texture_replace(RID p_texture, RID p_by_texture) = 0; + virtual void texture_set_size_override(RID p_texture, int p_width, int p_height) = 0; +// FIXME: Disabled during Vulkan refactoring, should be ported. +#if 0 + virtual void texture_bind(RID p_texture, uint32_t p_texture_no) = 0; +#endif + + virtual void texture_set_path(RID p_texture, const String &p_path) = 0; + virtual String texture_get_path(RID p_texture) const = 0; + + virtual void texture_set_detect_3d_callback(RID p_texture, RS::TextureDetectCallback p_callback, void *p_userdata) = 0; + virtual void texture_set_detect_normal_callback(RID p_texture, RS::TextureDetectCallback p_callback, void *p_userdata) = 0; + virtual void texture_set_detect_roughness_callback(RID p_texture, RS::TextureDetectRoughnessCallback p_callback, void *p_userdata) = 0; + + virtual void texture_debug_usage(List<RS::TextureInfo> *r_info) = 0; + + virtual void texture_set_force_redraw_if_visible(RID p_texture, bool p_enable) = 0; + + virtual Size2 texture_size_with_proxy(RID p_proxy) = 0; + + /* SHADER API */ + + virtual RID shader_create() = 0; + + virtual void shader_set_code(RID p_shader, const String &p_code) = 0; + virtual String shader_get_code(RID p_shader) const = 0; + virtual void shader_get_param_list(RID p_shader, List<PropertyInfo> *p_param_list) const = 0; + + virtual void shader_set_default_texture_param(RID p_shader, const StringName &p_name, RID p_texture) = 0; + virtual RID shader_get_default_texture_param(RID p_shader, const StringName &p_name) const = 0; + virtual Variant shader_get_param_default(RID p_material, const StringName &p_param) const = 0; + + /* COMMON MATERIAL API */ + + virtual RID material_create() = 0; + + virtual void material_set_render_priority(RID p_material, int priority) = 0; + virtual void material_set_shader(RID p_shader_material, RID p_shader) = 0; + + virtual void material_set_param(RID p_material, const StringName &p_param, const Variant &p_value) = 0; + virtual Variant material_get_param(RID p_material, const StringName &p_param) const = 0; + + virtual void material_set_next_pass(RID p_material, RID p_next_material) = 0; + + virtual bool material_is_animated(RID p_material) = 0; + virtual bool material_casts_shadows(RID p_material) = 0; + + virtual void material_update_dependency(RID p_material, RasterizerScene::InstanceBase *p_instance) = 0; + + /* MESH API */ + + virtual RID mesh_create() = 0; + + /// Returns stride + virtual void mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface) = 0; + + virtual int mesh_get_blend_shape_count(RID p_mesh) const = 0; + + virtual void mesh_set_blend_shape_mode(RID p_mesh, RS::BlendShapeMode p_mode) = 0; + virtual RS::BlendShapeMode mesh_get_blend_shape_mode(RID p_mesh) const = 0; + + virtual void mesh_surface_update_region(RID p_mesh, int p_surface, int p_offset, const Vector<uint8_t> &p_data) = 0; + + virtual void mesh_surface_set_material(RID p_mesh, int p_surface, RID p_material) = 0; + virtual RID mesh_surface_get_material(RID p_mesh, int p_surface) const = 0; + + virtual RS::SurfaceData mesh_get_surface(RID p_mesh, int p_surface) const = 0; + + virtual int mesh_get_surface_count(RID p_mesh) const = 0; + + virtual void mesh_set_custom_aabb(RID p_mesh, const AABB &p_aabb) = 0; + virtual AABB mesh_get_custom_aabb(RID p_mesh) const = 0; + + virtual AABB mesh_get_aabb(RID p_mesh, RID p_skeleton = RID()) = 0; + + virtual void mesh_clear(RID p_mesh) = 0; + + /* MULTIMESH API */ + + virtual RID multimesh_create() = 0; + + virtual void multimesh_allocate(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors = false, bool p_use_custom_data = false) = 0; + + virtual int multimesh_get_instance_count(RID p_multimesh) const = 0; + + virtual void multimesh_set_mesh(RID p_multimesh, RID p_mesh) = 0; + virtual void multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform &p_transform) = 0; + virtual void multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform) = 0; + virtual void multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) = 0; + virtual void multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) = 0; + + virtual RID multimesh_get_mesh(RID p_multimesh) const = 0; + + virtual Transform multimesh_instance_get_transform(RID p_multimesh, int p_index) const = 0; + virtual Transform2D multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const = 0; + virtual Color multimesh_instance_get_color(RID p_multimesh, int p_index) const = 0; + virtual Color multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const = 0; + + virtual void multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) = 0; + virtual Vector<float> multimesh_get_buffer(RID p_multimesh) const = 0; + + virtual void multimesh_set_visible_instances(RID p_multimesh, int p_visible) = 0; + virtual int multimesh_get_visible_instances(RID p_multimesh) const = 0; + + virtual AABB multimesh_get_aabb(RID p_multimesh) const = 0; + + /* IMMEDIATE API */ + + virtual RID immediate_create() = 0; + virtual void immediate_begin(RID p_immediate, RS::PrimitiveType p_rimitive, RID p_texture = RID()) = 0; + virtual void immediate_vertex(RID p_immediate, const Vector3 &p_vertex) = 0; + virtual void immediate_normal(RID p_immediate, const Vector3 &p_normal) = 0; + virtual void immediate_tangent(RID p_immediate, const Plane &p_tangent) = 0; + virtual void immediate_color(RID p_immediate, const Color &p_color) = 0; + virtual void immediate_uv(RID p_immediate, const Vector2 &tex_uv) = 0; + virtual void immediate_uv2(RID p_immediate, const Vector2 &tex_uv) = 0; + virtual void immediate_end(RID p_immediate) = 0; + virtual void immediate_clear(RID p_immediate) = 0; + virtual void immediate_set_material(RID p_immediate, RID p_material) = 0; + virtual RID immediate_get_material(RID p_immediate) const = 0; + virtual AABB immediate_get_aabb(RID p_immediate) const = 0; + + /* SKELETON API */ + + virtual RID skeleton_create() = 0; + virtual void skeleton_allocate(RID p_skeleton, int p_bones, bool p_2d_skeleton = false) = 0; + virtual int skeleton_get_bone_count(RID p_skeleton) const = 0; + virtual void skeleton_bone_set_transform(RID p_skeleton, int p_bone, const Transform &p_transform) = 0; + virtual Transform skeleton_bone_get_transform(RID p_skeleton, int p_bone) const = 0; + virtual void skeleton_bone_set_transform_2d(RID p_skeleton, int p_bone, const Transform2D &p_transform) = 0; + virtual Transform2D skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const = 0; + virtual void skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform) = 0; + + /* Light API */ + + virtual RID light_create(RS::LightType p_type) = 0; + + RID directional_light_create() { return light_create(RS::LIGHT_DIRECTIONAL); } + RID omni_light_create() { return light_create(RS::LIGHT_OMNI); } + RID spot_light_create() { return light_create(RS::LIGHT_SPOT); } + + virtual void light_set_color(RID p_light, const Color &p_color) = 0; + virtual void light_set_param(RID p_light, RS::LightParam p_param, float p_value) = 0; + virtual void light_set_shadow(RID p_light, bool p_enabled) = 0; + virtual void light_set_shadow_color(RID p_light, const Color &p_color) = 0; + virtual void light_set_projector(RID p_light, RID p_texture) = 0; + virtual void light_set_negative(RID p_light, bool p_enable) = 0; + virtual void light_set_cull_mask(RID p_light, uint32_t p_mask) = 0; + virtual void light_set_reverse_cull_face_mode(RID p_light, bool p_enabled) = 0; + virtual void light_set_use_gi(RID p_light, bool p_enable) = 0; + + virtual void light_omni_set_shadow_mode(RID p_light, RS::LightOmniShadowMode p_mode) = 0; + + virtual void light_directional_set_shadow_mode(RID p_light, RS::LightDirectionalShadowMode p_mode) = 0; + virtual void light_directional_set_blend_splits(RID p_light, bool p_enable) = 0; + virtual bool light_directional_get_blend_splits(RID p_light) const = 0; + virtual void light_directional_set_shadow_depth_range_mode(RID p_light, RS::LightDirectionalShadowDepthRangeMode p_range_mode) = 0; + virtual RS::LightDirectionalShadowDepthRangeMode light_directional_get_shadow_depth_range_mode(RID p_light) const = 0; + + virtual RS::LightDirectionalShadowMode light_directional_get_shadow_mode(RID p_light) = 0; + virtual RS::LightOmniShadowMode light_omni_get_shadow_mode(RID p_light) = 0; + + virtual bool light_has_shadow(RID p_light) const = 0; + + virtual RS::LightType light_get_type(RID p_light) const = 0; + virtual AABB light_get_aabb(RID p_light) const = 0; + virtual float light_get_param(RID p_light, RS::LightParam p_param) = 0; + virtual Color light_get_color(RID p_light) = 0; + virtual bool light_get_use_gi(RID p_light) = 0; + virtual uint64_t light_get_version(RID p_light) const = 0; + + /* PROBE API */ + + virtual RID reflection_probe_create() = 0; + + virtual void reflection_probe_set_update_mode(RID p_probe, RS::ReflectionProbeUpdateMode p_mode) = 0; + virtual void reflection_probe_set_resolution(RID p_probe, int p_resolution) = 0; + virtual void reflection_probe_set_intensity(RID p_probe, float p_intensity) = 0; + virtual void reflection_probe_set_interior_ambient(RID p_probe, const Color &p_ambient) = 0; + virtual void reflection_probe_set_interior_ambient_energy(RID p_probe, float p_energy) = 0; + virtual void reflection_probe_set_interior_ambient_probe_contribution(RID p_probe, float p_contrib) = 0; + virtual void reflection_probe_set_max_distance(RID p_probe, float p_distance) = 0; + virtual void reflection_probe_set_extents(RID p_probe, const Vector3 &p_extents) = 0; + virtual void reflection_probe_set_origin_offset(RID p_probe, const Vector3 &p_offset) = 0; + virtual void reflection_probe_set_as_interior(RID p_probe, bool p_enable) = 0; + virtual void reflection_probe_set_enable_box_projection(RID p_probe, bool p_enable) = 0; + virtual void reflection_probe_set_enable_shadows(RID p_probe, bool p_enable) = 0; + virtual void reflection_probe_set_cull_mask(RID p_probe, uint32_t p_layers) = 0; + + virtual AABB reflection_probe_get_aabb(RID p_probe) const = 0; + virtual RS::ReflectionProbeUpdateMode reflection_probe_get_update_mode(RID p_probe) const = 0; + virtual uint32_t reflection_probe_get_cull_mask(RID p_probe) const = 0; + virtual Vector3 reflection_probe_get_extents(RID p_probe) const = 0; + virtual Vector3 reflection_probe_get_origin_offset(RID p_probe) const = 0; + virtual float reflection_probe_get_origin_max_distance(RID p_probe) const = 0; + virtual bool reflection_probe_renders_shadows(RID p_probe) const = 0; + + virtual void base_update_dependency(RID p_base, RasterizerScene::InstanceBase *p_instance) = 0; + virtual void skeleton_update_dependency(RID p_base, RasterizerScene::InstanceBase *p_instance) = 0; + + /* GI PROBE API */ + + virtual RID gi_probe_create() = 0; + + virtual void gi_probe_allocate(RID p_gi_probe, const Transform &p_to_cell_xform, const AABB &p_aabb, const Vector3i &p_octree_size, const Vector<uint8_t> &p_octree_cells, const Vector<uint8_t> &p_data_cells, const Vector<uint8_t> &p_distance_field, const Vector<int> &p_level_counts) = 0; + + virtual AABB gi_probe_get_bounds(RID p_gi_probe) const = 0; + virtual Vector3i gi_probe_get_octree_size(RID p_gi_probe) const = 0; + virtual Vector<uint8_t> gi_probe_get_octree_cells(RID p_gi_probe) const = 0; + virtual Vector<uint8_t> gi_probe_get_data_cells(RID p_gi_probe) const = 0; + virtual Vector<uint8_t> gi_probe_get_distance_field(RID p_gi_probe) const = 0; + + virtual Vector<int> gi_probe_get_level_counts(RID p_gi_probe) const = 0; + virtual Transform gi_probe_get_to_cell_xform(RID p_gi_probe) const = 0; + + virtual void gi_probe_set_dynamic_range(RID p_gi_probe, float p_range) = 0; + virtual float gi_probe_get_dynamic_range(RID p_gi_probe) const = 0; + + virtual void gi_probe_set_propagation(RID p_gi_probe, float p_range) = 0; + virtual float gi_probe_get_propagation(RID p_gi_probe) const = 0; + + virtual void gi_probe_set_energy(RID p_gi_probe, float p_energy) = 0; + virtual float gi_probe_get_energy(RID p_gi_probe) const = 0; + + virtual void gi_probe_set_ao(RID p_gi_probe, float p_ao) = 0; + virtual float gi_probe_get_ao(RID p_gi_probe) const = 0; + + virtual void gi_probe_set_ao_size(RID p_gi_probe, float p_strength) = 0; + virtual float gi_probe_get_ao_size(RID p_gi_probe) const = 0; + + virtual void gi_probe_set_bias(RID p_gi_probe, float p_bias) = 0; + virtual float gi_probe_get_bias(RID p_gi_probe) const = 0; + + virtual void gi_probe_set_normal_bias(RID p_gi_probe, float p_range) = 0; + virtual float gi_probe_get_normal_bias(RID p_gi_probe) const = 0; + + virtual void gi_probe_set_interior(RID p_gi_probe, bool p_enable) = 0; + virtual bool gi_probe_is_interior(RID p_gi_probe) const = 0; + + virtual void gi_probe_set_use_two_bounces(RID p_gi_probe, bool p_enable) = 0; + virtual bool gi_probe_is_using_two_bounces(RID p_gi_probe) const = 0; + + virtual void gi_probe_set_anisotropy_strength(RID p_gi_probe, float p_strength) = 0; + virtual float gi_probe_get_anisotropy_strength(RID p_gi_probe) const = 0; + + virtual uint32_t gi_probe_get_version(RID p_probe) = 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 Vector<uint8_t> &p_octree) = 0; + virtual Vector<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 Vector<LightmapCaptureOctree> *lightmap_capture_get_octree_ptr(RID p_capture) const = 0; + + /* PARTICLES */ + + virtual RID particles_create() = 0; + + virtual void particles_set_emitting(RID p_particles, bool p_emitting) = 0; + virtual bool particles_get_emitting(RID p_particles) = 0; + + virtual void particles_set_amount(RID p_particles, int p_amount) = 0; + virtual void particles_set_lifetime(RID p_particles, float p_lifetime) = 0; + virtual void particles_set_one_shot(RID p_particles, bool p_one_shot) = 0; + virtual void particles_set_pre_process_time(RID p_particles, float p_time) = 0; + virtual void particles_set_explosiveness_ratio(RID p_particles, float p_ratio) = 0; + virtual void particles_set_randomness_ratio(RID p_particles, float p_ratio) = 0; + virtual void particles_set_custom_aabb(RID p_particles, const AABB &p_aabb) = 0; + virtual void particles_set_speed_scale(RID p_particles, float p_scale) = 0; + virtual void particles_set_use_local_coordinates(RID p_particles, bool p_enable) = 0; + virtual void particles_set_process_material(RID p_particles, RID p_material) = 0; + virtual void particles_set_fixed_fps(RID p_particles, int p_fps) = 0; + virtual void particles_set_fractional_delta(RID p_particles, bool p_enable) = 0; + virtual void particles_restart(RID p_particles) = 0; + + virtual bool particles_is_inactive(RID p_particles) const = 0; + + virtual void particles_set_draw_order(RID p_particles, RS::ParticlesDrawOrder p_order) = 0; + + virtual void particles_set_draw_passes(RID p_particles, int p_count) = 0; + virtual void particles_set_draw_pass_mesh(RID p_particles, int p_pass, RID p_mesh) = 0; + + virtual void particles_request_process(RID p_particles) = 0; + virtual AABB particles_get_current_aabb(RID p_particles) = 0; + virtual AABB particles_get_aabb(RID p_particles) const = 0; + + virtual void particles_set_emission_transform(RID p_particles, const Transform &p_transform) = 0; + + virtual int particles_get_draw_passes(RID p_particles) const = 0; + virtual RID particles_get_draw_pass_mesh(RID p_particles, int p_pass) const = 0; + + /* RENDER TARGET */ + + enum RenderTargetFlags { + RENDER_TARGET_TRANSPARENT, + RENDER_TARGET_DIRECT_TO_SCREEN, + RENDER_TARGET_FLAG_MAX + }; + + virtual RID render_target_create() = 0; + virtual void render_target_set_position(RID p_render_target, int p_x, int p_y) = 0; + virtual void render_target_set_size(RID p_render_target, int p_width, int p_height) = 0; + virtual RID render_target_get_texture(RID p_render_target) = 0; + virtual void render_target_set_external_texture(RID p_render_target, unsigned int p_texture_id) = 0; + virtual void render_target_set_flag(RID p_render_target, RenderTargetFlags p_flag, bool p_value) = 0; + virtual bool render_target_was_used(RID p_render_target) = 0; + virtual void render_target_set_as_unused(RID p_render_target) = 0; + + virtual void render_target_request_clear(RID p_render_target, const Color &p_clear_color) = 0; + virtual bool render_target_is_clear_requested(RID p_render_target) = 0; + virtual Color render_target_get_clear_request_color(RID p_render_target) = 0; + virtual void render_target_disable_clear_request(RID p_render_target) = 0; + virtual void render_target_do_clear_request(RID p_render_target) = 0; + + virtual RS::InstanceType get_base_type(RID p_rid) const = 0; + virtual bool free(RID p_rid) = 0; + + virtual bool has_os_feature(const String &p_feature) const = 0; + + virtual void update_dirty_resources() = 0; + + virtual void set_debug_generate_wireframes(bool p_generate) = 0; + + virtual void render_info_begin_capture() = 0; + virtual void render_info_end_capture() = 0; + virtual int get_captured_render_info(RS::RenderInfo p_info) = 0; + + virtual int get_render_info(RS::RenderInfo p_info) = 0; + virtual String get_video_adapter_name() const = 0; + virtual String get_video_adapter_vendor() const = 0; + + static RasterizerStorage *base_singleton; + + void set_default_clear_color(const Color &p_color) { + default_clear_color = p_color; + } + + Color get_default_clear_color() const { + return default_clear_color; + } +#define TIMESTAMP_BEGIN() \ + { \ + if (RSG::storage->capturing_timestamps) RSG::storage->capture_timestamps_begin(); \ + } + +#define RENDER_TIMESTAMP(m_text) \ + { \ + if (RSG::storage->capturing_timestamps) RSG::storage->capture_timestamp(m_text); \ + } + + bool capturing_timestamps = false; + + virtual void capture_timestamps_begin() = 0; + virtual void capture_timestamp(const String &p_name) = 0; + virtual uint32_t get_captured_timestamps_count() const = 0; + virtual uint64_t get_captured_timestamps_frame() const = 0; + virtual uint64_t get_captured_timestamp_gpu_time(uint32_t p_index) const = 0; + virtual uint64_t get_captured_timestamp_cpu_time(uint32_t p_index) const = 0; + virtual String get_captured_timestamp_name(uint32_t p_index) const = 0; + + RasterizerStorage(); + virtual ~RasterizerStorage() {} +}; + +class RasterizerCanvas { +public: + static RasterizerCanvas *singleton; + + enum CanvasRectFlags { + + CANVAS_RECT_REGION = 1, + CANVAS_RECT_TILE = 2, + CANVAS_RECT_FLIP_H = 4, + CANVAS_RECT_FLIP_V = 8, + CANVAS_RECT_TRANSPOSE = 16, + CANVAS_RECT_CLIP_UV = 32 + }; + + struct Light { + + bool enabled; + Color color; + Transform2D xform; + float height; + float energy; + float scale; + int z_min; + int z_max; + int layer_min; + int layer_max; + int item_mask; + int item_shadow_mask; + RS::CanvasLightMode mode; + RID texture; + Vector2 texture_offset; + RID canvas; + bool use_shadow; + int shadow_buffer_size; + RS::CanvasLightShadowFilter shadow_filter; + Color shadow_color; + float shadow_smooth; + + //void *texture_cache; // implementation dependent + Rect2 rect_cache; + Transform2D xform_cache; + float radius_cache; //used for shadow far plane + //CameraMatrix shadow_matrix_cache; + + Transform2D light_shader_xform; + //Vector2 light_shader_pos; + + Light *shadows_next_ptr; + Light *filter_next_ptr; + Light *next_ptr; + Light *mask_next_ptr; + + RID light_internal; + uint64_t version; + + int32_t render_index_cache; + + Light() { + version = 0; + enabled = true; + color = Color(1, 1, 1); + shadow_color = Color(0, 0, 0, 0); + height = 0; + z_min = -1024; + z_max = 1024; + layer_min = 0; + layer_max = 0; + item_mask = 1; + scale = 1.0; + energy = 1.0; + item_shadow_mask = -1; + mode = RS::CANVAS_LIGHT_MODE_ADD; + // texture_cache = NULL; + next_ptr = NULL; + mask_next_ptr = NULL; + filter_next_ptr = NULL; + use_shadow = false; + shadow_buffer_size = 2048; + shadow_filter = RS::CANVAS_LIGHT_FILTER_NONE; + shadow_smooth = 0.0; + render_index_cache = -1; + } + }; + + typedef uint64_t TextureBindingID; + + virtual TextureBindingID request_texture_binding(RID p_texture, RID p_normalmap, RID p_specular, RS::CanvasItemTextureFilter p_filter, RS::CanvasItemTextureRepeat p_repeat, RID p_multimesh) = 0; + virtual void free_texture_binding(TextureBindingID p_binding) = 0; + + //easier wrap to avoid mistakes + + struct Item; + + struct TextureBinding { + + TextureBindingID binding_id; + + _FORCE_INLINE_ void create(RS::CanvasItemTextureFilter p_item_filter, RS::CanvasItemTextureRepeat p_item_repeat, RID p_texture, RID p_normalmap, RID p_specular, RS::CanvasItemTextureFilter p_filter, RS::CanvasItemTextureRepeat p_repeat, RID p_multimesh) { + if (p_filter == RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT) { + p_filter = p_item_filter; + } + if (p_repeat == RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT) { + p_repeat = p_item_repeat; + } + if (p_texture != RID() || p_normalmap != RID() || p_specular != RID() || p_filter != RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT || p_repeat != RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT || p_multimesh.is_valid()) { + ERR_FAIL_COND(binding_id != 0); + binding_id = singleton->request_texture_binding(p_texture, p_normalmap, p_specular, p_filter, p_repeat, p_multimesh); + } + } + + _FORCE_INLINE_ TextureBinding() { binding_id = 0; } + _FORCE_INLINE_ ~TextureBinding() { + if (binding_id) singleton->free_texture_binding(binding_id); + } + }; + + typedef uint64_t PolygonID; + virtual PolygonID request_polygon(const Vector<int> &p_indices, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), const Vector<int> &p_bones = Vector<int>(), const Vector<float> &p_weights = Vector<float>()) = 0; + virtual void free_polygon(PolygonID p_polygon) = 0; + + //also easier to wrap to avoid mistakes + struct Polygon { + + PolygonID polygon_id; + Rect2 rect_cache; + + _FORCE_INLINE_ void create(const Vector<int> &p_indices, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), const Vector<int> &p_bones = Vector<int>(), const Vector<float> &p_weights = Vector<float>()) { + ERR_FAIL_COND(polygon_id != 0); + { + uint32_t pc = p_points.size(); + const Vector2 *v2 = p_points.ptr(); + rect_cache.position = *v2; + for (uint32_t i = 1; i < pc; i++) { + rect_cache.expand_to(v2[i]); + } + } + polygon_id = singleton->request_polygon(p_indices, p_points, p_colors, p_uvs, p_bones, p_weights); + } + + _FORCE_INLINE_ Polygon() { polygon_id = 0; } + _FORCE_INLINE_ ~Polygon() { + if (polygon_id) singleton->free_polygon(polygon_id); + } + }; + + //item + + struct Item { + + //commands are allocated in blocks of 4k to improve performance + //and cache coherence. + //blocks always grow but never shrink. + + struct CommandBlock { + enum { + MAX_SIZE = 4096 + }; + uint32_t usage; + uint8_t *memory; + }; + + struct Command { + + enum Type { + + TYPE_RECT, + TYPE_NINEPATCH, + TYPE_POLYGON, + TYPE_PRIMITIVE, + TYPE_MESH, + TYPE_MULTIMESH, + TYPE_PARTICLES, + TYPE_TRANSFORM, + TYPE_CLIP_IGNORE, + }; + + Command *next; + Type type; + virtual ~Command() {} + }; + + struct CommandRect : public Command { + + Rect2 rect; + Color modulate; + Rect2 source; + uint8_t flags; + Color specular_shininess; + + TextureBinding texture_binding; + + CommandRect() { + flags = 0; + type = TYPE_RECT; + } + }; + + struct CommandNinePatch : public Command { + + Rect2 rect; + Rect2 source; + float margin[4]; + bool draw_center; + Color color; + RS::NinePatchAxisMode axis_x; + RS::NinePatchAxisMode axis_y; + Color specular_shininess; + TextureBinding texture_binding; + CommandNinePatch() { + draw_center = true; + type = TYPE_NINEPATCH; + } + }; + + struct CommandPolygon : public Command { + + RS::PrimitiveType primitive; + Polygon polygon; + Color specular_shininess; + TextureBinding texture_binding; + CommandPolygon() { + type = TYPE_POLYGON; + } + }; + + struct CommandPrimitive : public Command { + + uint32_t point_count; + Vector2 points[4]; + Vector2 uvs[4]; + Color colors[4]; + Color specular_shininess; + TextureBinding texture_binding; + CommandPrimitive() { + type = TYPE_PRIMITIVE; + } + }; + + struct CommandMesh : public Command { + + RID mesh; + Transform2D transform; + Color modulate; + Color specular_shininess; + TextureBinding texture_binding; + CommandMesh() { type = TYPE_MESH; } + }; + + struct CommandMultiMesh : public Command { + + RID multimesh; + Color specular_shininess; + TextureBinding texture_binding; + CommandMultiMesh() { type = TYPE_MULTIMESH; } + }; + + struct CommandParticles : public Command { + + RID particles; + Color specular_shininess; + TextureBinding texture_binding; + CommandParticles() { type = TYPE_PARTICLES; } + }; + + struct CommandTransform : public Command { + + Transform2D xform; + CommandTransform() { type = TYPE_TRANSFORM; } + }; + + struct CommandClipIgnore : public Command { + + bool ignore; + CommandClipIgnore() { + type = TYPE_CLIP_IGNORE; + ignore = false; + } + }; + + struct ViewportRender { + RenderingServer *owner; + void *udata; + Rect2 rect; + }; + + Transform2D xform; + bool clip; + bool visible; + bool behind; + bool update_when_visible; + //RS::MaterialBlendMode blend_mode; + int light_mask; + int z_final; + + mutable bool custom_rect; + mutable bool rect_dirty; + mutable Rect2 rect; + RID material; + RID skeleton; + + Item *next; + + struct CopyBackBuffer { + Rect2 rect; + Rect2 screen_rect; + bool full; + }; + CopyBackBuffer *copy_back_buffer; + + Color final_modulate; + Transform2D final_transform; + Rect2 final_clip_rect; + Item *final_clip_owner; + Item *material_owner; + ViewportRender *vp_render; + bool distance_field; + bool light_masked; + + Rect2 global_rect_cache; + + const Rect2 &get_rect() const { + if (custom_rect || (!rect_dirty && !update_when_visible)) + return rect; + + //must update rect + + if (commands == NULL) { + + rect = Rect2(); + rect_dirty = false; + return rect; + } + + Transform2D xf; + bool found_xform = false; + bool first = true; + + const Item::Command *c = commands; + + while (c) { + + Rect2 r; + + switch (c->type) { + case Item::Command::TYPE_RECT: { + + const Item::CommandRect *crect = static_cast<const Item::CommandRect *>(c); + r = crect->rect; + + } break; + case Item::Command::TYPE_NINEPATCH: { + + const Item::CommandNinePatch *style = static_cast<const Item::CommandNinePatch *>(c); + r = style->rect; + } break; + + case Item::Command::TYPE_POLYGON: { + + const Item::CommandPolygon *polygon = static_cast<const Item::CommandPolygon *>(c); + r = polygon->polygon.rect_cache; + } break; + case Item::Command::TYPE_PRIMITIVE: { + + const Item::CommandPrimitive *primitive = static_cast<const Item::CommandPrimitive *>(c); + for (uint32_t j = 0; j < primitive->point_count; j++) { + if (j == 0) { + r.position = primitive->points[0]; + } else { + r.expand_to(primitive->points[j]); + } + } + } break; + case Item::Command::TYPE_MESH: { + + const Item::CommandMesh *mesh = static_cast<const Item::CommandMesh *>(c); + AABB aabb = RasterizerStorage::base_singleton->mesh_get_aabb(mesh->mesh, RID()); + + r = Rect2(aabb.position.x, aabb.position.y, aabb.size.x, aabb.size.y); + + } break; + case Item::Command::TYPE_MULTIMESH: { + + const Item::CommandMultiMesh *multimesh = static_cast<const Item::CommandMultiMesh *>(c); + AABB aabb = RasterizerStorage::base_singleton->multimesh_get_aabb(multimesh->multimesh); + + r = Rect2(aabb.position.x, aabb.position.y, aabb.size.x, aabb.size.y); + + } break; + case Item::Command::TYPE_PARTICLES: { + + const Item::CommandParticles *particles_cmd = static_cast<const Item::CommandParticles *>(c); + if (particles_cmd->particles.is_valid()) { + AABB aabb = RasterizerStorage::base_singleton->particles_get_aabb(particles_cmd->particles); + r = Rect2(aabb.position.x, aabb.position.y, aabb.size.x, aabb.size.y); + } + + } break; + case Item::Command::TYPE_TRANSFORM: { + + const Item::CommandTransform *transform = static_cast<const Item::CommandTransform *>(c); + xf = transform->xform; + found_xform = true; + [[fallthrough]]; + } + default: { + c = c->next; + continue; + } + } + + if (found_xform) { + r = xf.xform(r); + found_xform = false; + } + + if (first) { + rect = r; + first = false; + } else { + rect = rect.merge(r); + } + c = c->next; + } + + rect_dirty = false; + return rect; + } + + Command *commands; + Command *last_command; + Vector<CommandBlock> blocks; + uint32_t current_block; + + template <class T> + T *alloc_command() { + T *command; + if (commands == NULL) { + // As the most common use case of canvas items is to + // use only one command, the first is done with it's + // own allocation. The rest of them use blocks. + command = memnew(T); + command->next = NULL; + commands = command; + last_command = command; + } else { + //Subsequent commands go into a block. + + while (true) { + if (unlikely(current_block == (uint32_t)blocks.size())) { + // If we need more blocks, we allocate them + // (they won't be freed until this CanvasItem is + // deleted, though). + CommandBlock cb; + cb.memory = (uint8_t *)memalloc(CommandBlock::MAX_SIZE); + cb.usage = 0; + blocks.push_back(cb); + } + + CommandBlock *c = &blocks.write[current_block]; + size_t space_left = CommandBlock::MAX_SIZE - c->usage; + if (space_left < sizeof(T)) { + current_block++; + continue; + } + + //allocate block and add to the linked list + void *memory = c->memory + c->usage; + command = memnew_placement(memory, T); + command->next = NULL; + last_command->next = command; + last_command = command; + c->usage += sizeof(T); + break; + } + } + + rect_dirty = true; + return command; + } + + struct CustomData { + + virtual ~CustomData() {} + }; + + mutable CustomData *custom_data; //implementation dependent + + void clear() { + Command *c = commands; + while (c) { + Command *n = c->next; + if (c == commands) { + memdelete(commands); + commands = NULL; + } else { + c->~Command(); + } + c = n; + } + { + uint32_t cbc = MIN((current_block + 1), (uint32_t)blocks.size()); + CommandBlock *blockptr = blocks.ptrw(); + for (uint32_t i = 0; i < cbc; i++) { + blockptr[i].usage = 0; + } + } + + last_command = NULL; + commands = NULL; + current_block = 0; + clip = false; + rect_dirty = true; + final_clip_owner = NULL; + material_owner = NULL; + light_masked = false; + } + Item() { + commands = NULL; + last_command = NULL; + current_block = 0; + light_mask = 1; + vp_render = NULL; + next = NULL; + final_clip_owner = NULL; + clip = false; + final_modulate = Color(1, 1, 1, 1); + visible = true; + rect_dirty = true; + custom_rect = false; + behind = false; + material_owner = NULL; + copy_back_buffer = NULL; + distance_field = false; + light_masked = false; + update_when_visible = false; + z_final = 0; + custom_data = NULL; + } + virtual ~Item() { + clear(); + for (int i = 0; i < blocks.size(); i++) { + memfree(blocks[i].memory); + } + if (copy_back_buffer) memdelete(copy_back_buffer); + if (custom_data) { + memdelete(custom_data); + } + } + }; + + virtual void canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, const Transform2D &p_canvas_transform) = 0; + virtual void canvas_debug_viewport_shadows(Light *p_lights_with_shadow) = 0; + + struct LightOccluderInstance { + + bool enabled; + RID canvas; + RID polygon; + RID occluder; + Rect2 aabb_cache; + Transform2D xform; + Transform2D xform_cache; + int light_mask; + RS::CanvasOccluderPolygonCullMode cull_cache; + + LightOccluderInstance *next; + + LightOccluderInstance() { + enabled = true; + next = NULL; + light_mask = 1; + cull_cache = RS::CANVAS_OCCLUDER_POLYGON_CULL_DISABLED; + } + }; + + virtual RID light_create() = 0; + virtual void light_set_texture(RID p_rid, RID p_texture) = 0; + virtual void light_set_use_shadow(RID p_rid, bool p_enable, int p_resolution) = 0; + virtual void light_update_shadow(RID p_rid, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders) = 0; + + virtual RID occluder_polygon_create() = 0; + virtual void occluder_polygon_set_shape_as_lines(RID p_occluder, const Vector<Vector2> &p_lines) = 0; + virtual void occluder_polygon_set_cull_mode(RID p_occluder, RS::CanvasOccluderPolygonCullMode p_mode) = 0; + + virtual void draw_window_margins(int *p_margins, RID *p_margin_textures) = 0; + + virtual bool free(RID p_rid) = 0; + virtual void update() = 0; + + RasterizerCanvas() { singleton = this; } + virtual ~RasterizerCanvas() {} +}; + +class Rasterizer { +protected: + static Rasterizer *(*_create_func)(); + +public: + static Rasterizer *create(); + + virtual RasterizerStorage *get_storage() = 0; + virtual RasterizerCanvas *get_canvas() = 0; + virtual RasterizerScene *get_scene() = 0; + + virtual void set_boot_image(const Ref<Image> &p_image, const Color &p_color, bool p_scale, bool p_use_filter = true) = 0; + + virtual void initialize() = 0; + virtual void begin_frame(double frame_step) = 0; + + struct BlitToScreen { + RID render_target; + Rect2i rect; + //lens distorted parameters for VR should go here + }; + + virtual void prepare_for_blitting_render_targets() = 0; + virtual void blit_render_targets_to_screen(DisplayServer::WindowID p_screen, const BlitToScreen *p_render_targets, int p_amount) = 0; + + virtual void end_frame(bool p_swap_buffers) = 0; + virtual void finalize() = 0; + + virtual bool is_low_end() const = 0; + + virtual ~Rasterizer() {} +}; + +#endif // RASTERIZER_H diff --git a/servers/rendering/rasterizer_rd/SCsub b/servers/rendering/rasterizer_rd/SCsub new file mode 100644 index 0000000000..cc17feeb05 --- /dev/null +++ b/servers/rendering/rasterizer_rd/SCsub @@ -0,0 +1,7 @@ +#!/usr/bin/env python + +Import('env') + +env.add_source_files(env.servers_sources, "*.cpp") + +SConscript("shaders/SCsub") diff --git a/servers/rendering/rasterizer_rd/cubemap_coeffs.h b/servers/rendering/rasterizer_rd/cubemap_coeffs.h new file mode 100644 index 0000000000..1db03ce7c4 --- /dev/null +++ b/servers/rendering/rasterizer_rd/cubemap_coeffs.h @@ -0,0 +1,28 @@ +// Copyright 2016 Activision Publishing, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#ifndef CUBEMAP_COEFFS_H +#define CUBEMAP_COEFFS_H + +const float low_quality_coeffs[7][5][6][4] = { { { { 0.0002037563, 0.0045063655, -0.0016408688, 0.00012037493 }, { -9.1834951e-05, -0.008947532, -8.1524405e-05, -3.9080094e-05 }, { -2.6038267e-05, -6.8409667e-05, 7.2175702e-05, 0.25492775 }, { -9.9426044e-05, 0.0025427756, -0.00074437925, 1.1773191e-05 }, { -3.2668211e-05, 0.0026930659, -4.824934e-05, -0.0006835048 }, { -0.0002864144, -0.0032220854, 0.0021558286, -0.00014573736 } }, { { 0.00030683201, 0.0026819548, -0.00060011756, -0.0067169226 }, { -0.0030993251, 0.0098575575, 0.0022416671, -8.9075401e-05 }, { 0.00052405626, 0.00057860515, 0.00011654518, -0.048018609 }, { 0.00010850967, -0.0088443512, -0.0018168095, 8.6633128e-05 }, { 0.003333989, -0.0050956447, -8.0414612e-05, 0.0049721239 }, { -4.0961436e-05, -8.5486984e-05, 0.0058683066, 2.2978359e-05 } }, { { 0.99999993, 0.99998625, 0.99999847, 0.99997743 }, { 0.99999519, 0.99991138, 0.99999748, 1 }, { 0.99999986, 0.99999983, 0.99999999, 0.96576708 }, { 0.99999999, 0.99995765, 0.99999807, 1 }, { 0.99999444, 0.99998339, 1, 0.99998741 }, { 0.99999996, 0.99999481, 0.99998046, 0.99999999 } }, { { -0.32267524, -0.65409377, -1.4666488, 0.87153305 }, { -1.264365, 0.89880861, -1.2245906, -0.88501403 }, { -0.31118682, -0.086150323, -0.58811532, 1.1317711 }, { -1.2193493, 1.250379, -1.0871569, -0.12694096 }, { -0.4012249, -0.47436307, -0.59661001, 2.7313005 }, { -1.3109856, 0.60929855, 0.55672643, -0.39880018 } }, { { 0.93273157, 0.59530745, 1.1994788, 0.19102276 }, { 1.2272239, 0.23245736, 1.2577607, 2.5491008 }, { 1.1210098, 0.83074953, 1.3049282, -0.001940633 }, { 1.5839111, 0.10520816, 1.150458, 2.3251789 }, { 0.688692, 0.59807498, 1.3374877, 0.095746692 }, { 1.3054173, 0.36604721, 0.065870226, 1.6496907 } } }, { { { 0.10348445, -4.6771514e-07, -0.011513131, 8.8921052e-05 }, { -0.042152043, 0.013143535, 0.00029120107, 0.036661611 }, { -0.04516036, 0.011438473, -0.0099289792, -0.011707897 }, { -0.034779497, 0.0090981166, -5.4202726e-05, 0.038592793 }, { -0.0071967376, -0.0056614418, -0.012278945, 0.0056867462 }, { -0.037678514, 0.011570177, 0.00029044557, 0.038583909 } }, { { 0.048320869, 1.4603673e-05, 0.0092672368, 0.00033289199 }, { 0.0071001761, -0.0090106091, -0.0027305905, -0.00221479 }, { -0.0027204116, 0.00017921587, 0.015296357, -0.00010306185 }, { 0.0079350203, -0.014772431, -1.2410913e-05, -0.0062296897 }, { 0.025087691, 0.00086046427, 0.015034685, -0.00078224706 }, { 0.00074587265, -0.014602074, 0.00027338224, -0.012848552 } }, { { 0.99345662, 1, 0.99989078, 0.99999994 }, { 0.99908598, 0.99987302, 0.99999623, 0.99932528 }, { 0.99897605, 0.99993456, 0.9998337, 0.99993145 }, { 0.99936351, 0.99984949, 1, 0.9992356 }, { 0.99965935, 0.9999836, 0.99981158, 0.99998352 }, { 0.99928963, 0.99982644, 0.99999992, 0.99917276 } }, { { 3.6882765, 0.15963861, 0.55983965, 0.4075649 }, { 2.1169304, 0.56463157, 0.52957047, 2.0117964 }, { 3.1080461, 0.09682931, 0.42125986, 0.089254784 }, { 1.4247315, 0.48411378, -0.17039102, 1.7431674 }, { 4.0339531, 0.14046159, 0.89848909, 0.011661811 }, { 1.9787852, 0.61750145, 0.63514194, 1.9359003 } }, { { 0.030848793, 1.4472743, 1.4356825, 1.4078009 }, { 0.37639678, 1.0793106, 1.1945413, 0.43983395 }, { 0.27451605, 1.5256415, 1.016769, 1.4850575 }, { 0.54580883, 1.1332879, 3.1331784, 0.60772955 }, { 0.11785158, 1.3928946, 0.94998805, 1.0377182 }, { 0.2842108, 1.0026911, 1.9064553, 0.27147854 } } }, { { { -0.096789259, 0.10326967, 0.0011799959, -0.03077328 }, { 0.08342021, 0.033260738, -0.00045864451, -0.021450568 }, { -0.093369441, -0.05807574, -0.033745214, 0.023817208 }, { 0.056747754, 0.031140512, 0.00019362509, -0.023727797 }, { -0.084538386, -0.040545412, -0.0076838784, 0.03424599 }, { 0.074312056, 0.027619787, 0.0015509082, -0.031043528 } }, { { -0.0085160473, -0.012179292, 0.0049910118, 0.020224799 }, { 0.022559343, -0.016273333, -0.0069382139, 0.00058083224 }, { -0.001115062, 0.035002846, -0.0038974773, -0.039378629 }, { 0.0014921617, -0.00058523872, -0.0011606685, 0.02807528 }, { -0.021454809, 0.052957852, -0.0022083677, -0.027956663 }, { -0.016486487, -0.0040233682, 0.00029949558, 0.021924605 } }, { { 0.99526846, 0.99457883, 0.99998685, 0.99932175 }, { 0.99625908, 0.99931422, 0.99997583, 0.99976974 }, { 0.99563091, 0.99769836, 0.99942287, 0.99894047 }, { 0.99838743, 0.99951485, 0.99999931, 0.99932416 }, { 0.99618922, 0.99777329, 0.99996804, 0.99902234 }, { 0.99709875, 0.9996104, 0.99999875, 0.99927754 } }, { { 3.0342011, 4.8022834, 1.3814123, 1.5280754 }, { 2.9043837, 1.7325954, 1.422223, 2.0569263 }, { 3.0358722, 5.3331504, 1.5680146, 1.6079289 }, { 3.2062833, 1.5368069, 1.0484709, 1.5399477 }, { 2.4471653, 4.0916696, 1.5060688, 1.5807009 }, { 2.6932695, 1.5161537, 1.3991175, 1.6301918 } }, { { 0.50787578, 0.17735471, 1.4006765, 1.0878482 }, { 0.69514518, 1.6765187, 1.2224869, 1.3461327 }, { 0.71381288, 0.17509216, 1.2712934, 0.94575821 }, { 1.1817337, 1.796984, 1.8671538, 1.5708691 }, { 0.55621228, 0.38291359, 1.4128781, 0.82625349 }, { 0.72441647, 1.005794, 1.5522327, 1.6032524 } } }, { { { -0.00041301094, -0.095882618, 0.26932618, -0.25137214 }, { 0.13737415, -0.12694293, -0.0090389663, 0.07227623 }, { -0.005236407, -0.0072961249, 0.27776083, -0.19536433 }, { 0.12781899, -0.042881667, -0.095979169, 0.088937396 }, { 0.037496084, -0.090547583, 0.22112334, -0.21930294 }, { 0.13353408, -0.084346121, -0.011365728, 0.043459312 } }, { { -0.05799135, -0.048612281, 0.02422989, 0.015536268 }, { -0.083144241, 0.039381032, 0.018705957, 0.029297922 }, { 0.026364989, -0.041927591, 0.036718516, 0.0050376168 }, { -0.11562256, 0.043521976, -0.014481644, 0.01529188 }, { -0.047859898, -0.057779647, -0.053171395, -0.0063193506 }, { -0.028781196, 0.041145059, -0.00018523142, 0.053524246 } }, { { 0.998317, 0.99420489, 0.96274416, 0.96776581 }, { 0.98702349, 0.99112796, 0.99978417, 0.99695425 }, { 0.99963867, 0.99909401, 0.95994827, 0.9807178 }, { 0.98503489, 0.99813175, 0.99527799, 0.99591983 }, { 0.99815003, 0.99421459, 0.97379529, 0.97563635 }, { 0.99062621, 0.99558667, 0.99993539, 0.99762039 } }, { { 2.3221943, 2.5383575, 4.3177232, 4.2016467 }, { 3.1936529, 3.0443024, 2.548962, 2.7636456 }, { 2.5923827, 2.3497949, 4.2471014, 4.1975975 }, { 3.3748785, 3.2836577, 2.9220414, 2.7175317 }, { 2.3290083, 2.5560991, 4.3572168, 4.4372585 }, { 3.1512055, 3.2863613, 2.4475378, 2.3620003 } }, { { 0.62833231, 0.52378061, 0.55845033, 0.64883444 }, { 0.76905594, 1.1017801, 1.8714048, 1.5664383 }, { 1.5283278, 1.2423369, 0.62247385, 1.0341956 }, { 0.77484548, 1.6866409, 1.0307399, 1.4224643 }, { 0.85627405, 0.72516079, 0.70094339, 0.7547877 }, { 1.202842, 1.7650605, 1.5938526, 0.97031337 } } }, { { { -0.078108035, -0.049518839, 0.26950139, -0.51522828 }, { 0.43015518, -0.045354216, 0.094550359, -0.2395012 }, { -0.079900522, -0.082582235, 0.24464909, -0.5234896 }, { 0.38422945, -0.023833644, 0.07334288, -0.22827313 }, { -0.075370379, -0.05156594, 0.19883182, -0.45064193 }, { 0.46285395, 0.021899343, 0.10155287, -0.25974773 } }, { { 0.068681419, -0.32175988, 0.15143274, -0.0066205388 }, { -0.17060226, 0.31051319, -0.080511981, -0.1593209 }, { 0.08167251, -0.32517768, 0.10937023, -0.06941926 }, { -0.14580685, 0.32474959, -0.081718057, -0.11068378 }, { 0.053961394, -0.29322836, 0.10408839, -0.02243046 }, { -0.030598471, 0.34332821, -0.091528353, -0.16299796 } }, { { 0.99457629, 0.9455255, 0.95101899, 0.85702741 }, { 0.88648824, 0.94948647, 0.99225906, 0.95773484 }, { 0.99345131, 0.94204015, 0.96342357, 0.84919939 }, { 0.9116513, 0.94549969, 0.99395321, 0.96728511 }, { 0.99569447, 0.95465076, 0.97449039, 0.89242295 }, { 0.88590629, 0.93896015, 0.99061071, 0.95182077 } }, { { 3.6380949, 4.1749529, 4.1351439, 4.8389883 }, { 5.256362, 4.2027959, 3.6096892, 3.9848645 }, { 3.5689427, 3.8620869, 4.0023981, 4.8268584 }, { 5.1128497, 4.468934, 3.5851596, 4.047485 }, { 3.7014988, 4.1310058, 4.2446872, 5.3049323 }, { 4.8659881, 4.3133002, 3.4582876, 3.8863853 } }, { { 1.6276316, 0.7747672, 1.0485958, 0.73900224 }, { 0.72010795, 0.65403093, 1.3179681, 0.65610074 }, { 1.5881174, 0.55108527, 1.0509725, 0.72153362 }, { 1.1389053, 1.0905142, 1.6661598, 0.9987548 }, { 1.977914, 0.83001686, 1.0571479, 0.80249183 }, { 0.94107069, 0.80840873, 0.95379751, 0.50386367 } } }, { { { 0.015525428, -0.48038019, -0.021799698, 0.43629156 }, { 0.045681247, -0.55039024, -0.54573329, 0.57817853 }, { -0.045869129, -0.42209953, -0.14040829, 0.37787106 }, { 0.66327604, -0.70070311, -0.55261635, 0.63446196 }, { 0.015397585, -0.43515767, -0.021927897, 0.4203714 }, { 0.85681772, -0.65394729, -0.67557236, 0.60104142 } }, { { -0.31503888, -0.26745648, 0.26817896, 0.26548747 }, { -0.93282124, -0.033621213, 0.68374802, -0.10858524 }, { -0.21723689, -0.17935495, 0.38521982, 0.2578335 }, { -0.39913153, 0.23555359, 0.59589456, -0.19075103 }, { -0.28851798, -0.24142459, 0.28279261, 0.24766617 }, { -0.29435977, -0.25850549, 0.57790878, -0.200546 } }, { { 0.94895177, 0.83528552, 0.96312243, 0.85974768 }, { 0.35743218, 0.8342303, 0.48442112, 0.80865248 }, { 0.97504059, 0.88863029, 0.9120807, 0.88923301 }, { 0.63305523, 0.67344611, 0.58268761, 0.74904744 }, { 0.95735066, 0.86738225, 0.9589304, 0.87289711 }, { 0.42333878, 0.71100482, 0.45784651, 0.77364753 } }, { { 5.3641275, 5.2550422, 5.3103777, 5.2851215 }, { 5.2657045, 6.2095784, 6.9549598, 4.9205516 }, { 5.163385, 5.3141038, 4.9907618, 5.3583852 }, { 6.1257061, 6.1102338, 6.9549598, 5.3129951 }, { 5.3138838, 5.3257842, 5.3133783, 5.2687156 }, { 5.8915091, 6.153324, 6.9549598, 4.9568971 } }, { { 3.1221918, 1.1882615, 2.6991784, 1.1185048 }, { -0.2322432, -0.16590163, 0.088416958, 0.057399579 }, { 3.4395383, 1.5836276, 2.6242352, 1.2873336 }, { -0.23767634, -0.79425452, 0.20477899, 0.40461516 }, { 2.2521751, 1.1933374, 2.3309484, 1.0185309 }, { -0.099258385, -0.2173726, 0.0736866, 0.15470436 } } }, { { { 0.066050217, -0.33053278, -0.13771479, 0.33278465 }, { 0.00084467977, -0.50077778, -0.30083482, 0.6494273 }, { 0.24880159, -0.30354993, -0.15417892, 0.38203296 }, { -0.073325098, -0.4778777, 0.10779844, 0.66683723 }, { 0.15703809, -0.36335455, -0.15657631, 0.35926503 }, { 0.26127617, -0.29524368, -0.14490804, 0.65461301 } }, { { -0.57970022, -0.33939622, 0.72169742, 0.320959 }, { -0.38698206, -0.12730306, 0.65810895, 0.026509232 }, { -0.6199708, -0.34745658, 0.68683659, 0.34547285 }, { -0.3613378, -0.14006845, 0.65917895, 0.038446867 }, { -0.57778101, -0.35057776, 0.57837882, 0.36488991 }, { -0.50051482, -0.019174387, 0.50816239, 0.02682636 } }, { { 0.8121484, 0.88065787, 0.67837119, 0.88670158 }, { 0.92208686, 0.85616327, 0.69021085, -0.75996148 }, { 0.74413303, 0.88720424, 0.71027063, 0.85714604 }, { 0.92954743, 0.86718726, 0.74421946, -0.74421095 }, { 0.80094204, 0.86317363, 0.8006009, 0.85894353 }, { 0.82536033, 0.95522956, 0.8489833, -0.75548802 } }, { { 5.7725061, 5.1565901, 5.6224483, 5.0847054 }, { 5.7717118, 6.4180057, 6.9797014, -0.03290957 }, { 5.7847117, 5.2015529, 5.614561, 5.2019388 }, { 6.2613999, 6.5807982, 6.9797014, -0.032764603 }, { 5.823775, 5.2332343, 5.826694, 5.197143 }, { 6.3463188, 5.8174311, 6.9797014, -0.032766769 } }, { { 2.96787, 1.3557735, 2.0749129, 1.3066609 }, { -0.92782801, 0.0079162579, -0.33479446, 2.699659e-05 }, { 2.1997063, 3.1083252, 2.6810949, 1.8276262 }, { -0.48654719, -0.10954189, -0.32175132, 5.490092e-05 }, { 3.1970446, 1.787085, 3.062849, 1.6274811 }, { -0.78882801, -0.34050184, -0.59962127, 3.6554198e-05 } } } }; + +const float high_quality_coeffs[7][5][3][24][4] = { { { { { -4.8355339e-06, -4.4902569e-05, -9.2632249e-05, -0.00053773136 }, { 0.0040143823, -0.00060900339, -0.0095301923, -0.0053956011 }, { -0.0005923892, -3.6901978e-05, -5.6694857e-06, -0.00017018564 }, { 0.0012441402, 0.02236187, 0.022751769, 0.0062788948 }, { 0.00013810055, -2.2709815e-05, 0.0054849671, -1.6599195e-05 }, { -0.020320408, -0.017066319, -0.017457746, 0.022910628 }, { 0.00024171724, 9.7419073e-05, -0.00047804272, -0.00010093683 }, { 7.6988167e-05, 1.8551597e-05, -5.7692813e-05, -3.332362e-05 }, { -0.00062766208, 2.713742e-05, 0.00026511682, 2.3841873e-05 }, { -0.00043656844, 0.0028645469, 0.0049817085, 0.0080221478 }, { -3.3210444e-05, -8.0852386e-05, -2.2111492e-06, -8.4430827e-05 }, { 0.010967284, 0.018811225, 0.017569463, -0.0046944996 }, { -0.00018391248, -0.00010462174, -0.00017726, -0.00018490133 }, { 0.00012591989, 0.015965386, 0.015964059, -0.0078018431 }, { -0.006125333, -8.2224165e-05, -0.00020500151, -0.00025207244 }, { -0.00016320041, -0.0001279242, 0.00014038799, 8.1359421e-05 }, { -0.00064341098, -0.0011265496, -0.0011634792, -0.00081607159 }, { 0.00089294825, 0.0061923653, 0.0052662392, -0.00058227469 }, { -2.4001308e-05, -1.3534224e-05, -1.4720478e-05, -2.5120827e-05 }, { 0.00029964918, -0.0045658543, -0.0045581938, 0.0017106208 }, { 7.5790173e-05, -1.8265415e-05, 1.5918205e-05, 5.8524021e-05 }, { 0.0011669872, -0.00017571882, -0.00017190275, -0.0023833977 }, { 0.0033487264, -0.0066535821, -0.0066413786, -0.0032332601 }, { -3.6468807e-05, -0.00068145131, -9.8190714e-05, -8.7169435e-05 } }, { { -0.0010440653, -8.9750644e-05, 4.971182e-05, 0.0044618878 }, { 0.0078333883, -0.00090884312, -0.00046920549, -0.002465051 }, { -0.0058778609, 0.0026554895, -0.00031880506, -0.00010649091 }, { -0.0015095448, 0.0094026506, 0.009492703, 0.0024572848 }, { 0.0047331786, 0.00070722401, 0.0028798817, -0.00039779892 }, { -0.0089878107, -0.0095474878, -0.0097187652, 0.008765907 }, { -4.0435321e-05, -0.00061813281, -0.0060490143, 0.0016259965 }, { -0.00014720558, -1.0601876e-05, 0.00014757138, 0.00016227641 }, { -0.010428289, -0.00031812813, -0.0016172213, -0.00012022134 }, { 0.0040517131, 0.0072972763, 0.0060433905, 0.0025041645 }, { 0.00014090924, 0.00027612853, 0.00015961665, 0.0002605418 }, { -0.00020653783, -0.00048482867, -0.00058472338, 0.00026413759 }, { 0.00056712638, 0.00026385353, 0.00035484947, 0.00033212447 }, { -0.00094663094, 0.0029891757, 0.0029887838, -0.0026583585 }, { -0.0017400246, 0.00042350567, 0.00086128207, 0.00039863587 }, { 0.00059604848, 0.00027495434, -0.00059956434, -4.4981673e-05 }, { -0.010211343, -0.0080580409, -0.0085333216, 0.0023258717 }, { 0.00042832593, 0.0056750222, 0.0048059635, -0.0092168281 }, { 3.0214612e-05, 4.540924e-06, 1.7239937e-05, 2.783598e-05 }, { 0.00029393335, -4.5128636e-05, -4.3089017e-05, 0.00030682556 }, { -4.7077735e-05, -1.3596835e-05, -0.0015338149, -7.4957991e-05 }, { -0.00097136844, 0.00018564298, 0.00021815754, 0.0015095577 }, { 0.00043929849, -0.0014691094, -0.0014671742, -0.00029365954 }, { 8.8554045e-05, 0.0062500772, 0.0001495049, 0.00021007601 } }, { { 0.0020307077, 0.0020947445, 0.0017438295, 0.0084822342 }, { -0.0069727503, -0.0010131005, 0.0055261321, -0.0020442588 }, { 0.00031035611, 0.00010839441, 3.7359209e-06, 4.3112837e-05 }, { 9.1207794e-05, 0.0050148169, 0.0051071455, 0.0033679057 }, { -0.00090101737, -0.00053793176, -0.0025829621, 0.0003241927 }, { -0.0019244714, -0.0033690472, -0.0035193497, 0.0027653636 }, { -0.00065476293, -0.00017787403, 0.00040383136, -0.00018123957 }, { -0.00030640434, -0.00018961553, -0.00011036218, -0.00015793049 }, { 0.001110592, -0.00021252645, 0.00015849587, -3.7758317e-05 }, { 0.00077967828, -0.0051765235, -0.0078505592, -0.010796339 }, { -1.2024951e-05, 6.48806e-05, -3.9409005e-05, 7.4639306e-05 }, { -0.00017352424, -0.00037802595, -0.00045639468, 0.00016843169 }, { -4.2866244e-05, -4.3730932e-06, 7.3574276e-05, 5.6076779e-05 }, { 0.00024802387, 0.0018053101, 0.0018042994, -0.0016700716 }, { 0.0082698262, -0.00014605077, 0.0004377682, 8.1585074e-05 }, { -4.494343e-06, 0.00019781519, -0.00058910268, -0.00027360572 }, { 0.0013016934, 0.0021020456, 0.0022718598, -0.0059377824 }, { 0.002185371, -0.0080788056, -0.0071952836, 0.0039688918 }, { 0.00013048617, 0.0001738124, 0.00012978924, 0.00013813358 }, { 0.00032386518, 0.00023046021, 0.00023064714, 0.00033762343 }, { 0.00023643771, 0.00019652953, 0.0013083597, 0.00024739959 }, { -0.0063957036, -0.0055319023, -0.0054742301, -0.0037204932 }, { -0.0005510683, -0.0007715413, -0.00077385934, -0.001009415 }, { 0.00017904616, -0.00096137522, 0.00030252599, -2.2478138e-05 } } }, { { { -0.00038948583, -0.00040817153, -0.00041280315, -0.0010985631 }, { 0.0025695337, 0.00042904308, 0.0054649973, -0.0055079106 }, { 0.00052050672, 2.2618679e-05, 0.00024058975, -0.00012632201 }, { -0.013468886, 0.0079396715, 0.0079402246, 0.026283756 }, { -7.922122e-05, -3.4761763e-06, -0.0041716347, 0.0001478739 }, { 0.023716381, -0.016415262, -0.015296927, -0.021050827 }, { 3.7654391e-05, 0.00012765816, -0.0001337099, 0.00051483398 }, { 0.00015671907, 0.00010686796, 2.1421097e-05, -2.2281569e-05 }, { 3.1779413e-06, 0.00010449913, -0.00018303614, 7.5382489e-05 }, { -0.00020526765, -0.0011333575, -0.0050720108, 0.0051482782 }, { 4.0450357e-05, 1.0808158e-05, -2.3316095e-05, 9.7767333e-06 }, { -0.019107229, 0.010907324, 0.0048969594, 0.017851514 }, { 7.4048796e-05, -7.041835e-06, 8.0226174e-05, 5.1714105e-05 }, { -0.016564627, 0.0023486944, 0.0023601429, 0.016005248 }, { -0.004528284, 3.6291049e-05, 2.4229636e-05, 0.0024853948 }, { 5.6882054e-05, 6.8805135e-05, 0.00013119897, 0.00010339801 }, { 0.00021183341, 0.0008203137, -7.204401e-05, 0.00062599728 }, { -0.00099314707, 0.0030198762, -0.0038989955, 0.00055571214 }, { -7.4247984e-05, -8.3993373e-05, -5.9133252e-05, -7.7411989e-05 }, { 0.0054296732, -0.00057858871, -0.00058417754, -0.005072911 }, { -0.00019259782, -0.00018772532, -4.2959783e-05, -0.0001827295 }, { -0.00029351865, 0.00013736372, 0.00016666048, 0.00020873447 }, { 0.0069341659, 0.0027612928, 0.0027538377, -0.0061770317 }, { 4.2584714e-05, -0.00037063589, -9.0693123e-06, 0.00011845784 } }, { { 0.0028834168, 0.0031807308, 0.0031352582, 0.01064051 }, { 0.0049297987, -4.2149356e-05, -0.0014926841, -0.0002300371 }, { 0.0020396303, -0.00066042794, -6.4359283e-05, 0.00017835163 }, { -0.0025767816, 0.0025148152, 0.0025224779, 0.0043006543 }, { -0.00042084416, -0.00013534305, 0.002453623, -4.0707749e-05 }, { -0.0001803055, -0.0010450606, -0.00084380806, 0.00014843677 }, { -0.0064067107, 0.00011012652, -0.0022552747, -0.00080508294 }, { -0.00017778763, -4.296789e-05, 0.00015343883, 0.00025036711 }, { 0.002825978, -0.00031945362, -0.00031987612, -0.00021117763 }, { 0.00032791249, -0.00049524542, 0.0049368722, -0.0017186408 }, { -0.0001685943, -0.00016766033, -0.0001755097, -0.00017067307 }, { 0.00023939157, -0.00011793706, -6.0620575e-05, -0.0002706595 }, { -2.9718673e-05, 3.5950879e-05, 1.839844e-05, -2.8718148e-05 }, { -0.0017260981, 0.00012145435, 0.0001236679, 0.0018292155 }, { 0.0036086706, 0.0001026898, -2.5518889e-05, -0.00019830236 }, { -0.00031546808, -0.00042107458, -0.00059963868, -0.00061472497 }, { -0.0074719522, 0.0015719596, -0.0033624165, -0.0092664101 }, { -0.0011285776, 0.0018601435, 0.00052060704, -1.5554679e-05 }, { 4.9853171e-05, 7.3650922e-05, 3.4080107e-05, 5.4255445e-05 }, { 0.00015102779, -2.58105e-05, -2.5851018e-05, -4.5185316e-05 }, { 0.0002057452, 0.00019037765, 0.0040052198, 0.00020046579 }, { 0.0027727314, 0.0040749211, 0.0036050794, 0.0034635222 }, { 0.00042503689, 0.00056027382, 0.00056052971, -8.2485044e-05 }, { -5.6309634e-05, 0.0019722025, 6.4267434e-05, -0.00020376412 } }, { { 0.0051607661, 0.0047835358, 0.0047658352, 0.0054281814 }, { -0.0040939561, 0.0012119183, -0.0023408179, -0.00055891234 }, { -0.0031939804, -0.0015954053, -0.00018570689, 0.00028849431 }, { -0.0075625096, 0.0033878734, 0.0033797415, 0.010242674 }, { -0.002293562, 0.00024245282, 0.0019455622, 0.0039550747 }, { 0.0090386754, -0.0086947671, -0.0082684939, -0.0075613346 }, { -0.00085735117, 3.4822634e-05, -0.0024653972, -0.00090964985 }, { -0.00013750587, -0.00010089501, 6.3555498e-05, 0.0002758494 }, { 0.0060496328, -0.00032664426, 0.0005979723, -0.00018819024 }, { 0.00072724184, 0.00082242885, 0.0045668772, -0.0054557456 }, { -9.6167811e-05, 7.9856612e-05, 0.00015672473, 8.0901183e-05 }, { 0.00038859448, -0.00025360755, -0.00017624981, -0.00049125519 }, { -8.8277361e-05, 2.4159527e-05, -0.00016014627, -2.7854246e-05 }, { -0.0037308647, 0.00041434141, 0.0004167221, 0.0037190244 }, { 0.00050696744, -4.6752715e-05, 0.00033183668, -0.0025882828 }, { -0.00015915702, -0.0002325901, -0.00036157415, -0.00016391937 }, { 0.00012320153, 0.0026711886, 0.0018414591, -0.0058215223 }, { -0.0029409983, -0.00015460743, 0.0031951665, 0.0074654329 }, { 9.9084813e-05, 9.1785865e-05, 5.9300007e-05, 0.00010463304 }, { 0.00024773341, -2.5723276e-05, -2.5709769e-05, -0.00015357475 }, { 0.000416633, 0.00028749584, -0.0038632071, 0.00039869488 }, { 0.00018344152, 3.0811778e-05, -0.00010240082, 0.00059301197 }, { 0.0019217461, 0.00034404024, 0.00034318823, -0.0015867375 }, { -0.00011928879, 0.001178769, -5.8655983e-05, -0.00028461439 } } }, { { { 0.99999992, 0.99999992, 0.99999991, 0.99999925 }, { 0.99998864, 0.99999972, 0.99993965, 0.99997027 }, { 0.99999969, 1, 0.99999997, 0.99999998 }, { 0.99990852, 0.99971841, 0.99970961, 0.9996348 }, { 0.99999999, 1, 0.99997626, 0.99999999 }, { 0.99951219, 0.9997196, 0.99973058, 0.99951587 }, { 0.99999997, 0.99999999, 0.99999988, 0.99999986 }, { 0.99999998, 0.99999999, 1, 1 }, { 0.9999998, 0.99999999, 0.99999995, 1 }, { 0.99999988, 0.99999525, 0.99997473, 0.99995457 }, { 1, 1, 1, 1 }, { 0.99975729, 0.99976356, 0.99983365, 0.99982963 }, { 0.99999998, 0.99999999, 0.99999998, 0.99999998 }, { 0.99986279, 0.99986979, 0.99986978, 0.99984147 }, { 0.99997099, 1, 0.99999998, 0.99999688 }, { 0.99999999, 0.99999999, 0.99999998, 0.99999999 }, { 0.99999977, 0.99999903, 0.99999932, 0.99999947 }, { 0.99999911, 0.99997627, 0.99997853, 0.99999968 }, { 1, 1, 1, 1 }, { 0.99998521, 0.99998941, 0.99998944, 0.99998567 }, { 0.99999998, 0.99999998, 1, 0.99999998 }, { 0.99999928, 0.99999998, 0.99999997, 0.99999714 }, { 0.99997035, 0.99997405, 0.99997415, 0.99997569 }, { 1, 0.9999997, 1, 0.99999999 } }, { { 0.00015966941, 0.00014262676, 0.00020165066, 0.00021618914 }, { 2.8140907e-06, -0.00020325872, 0.00017736728, 6.0386679e-05 }, { -0.0003187876, 5.8862288e-05, 6.2281085e-05, 1.7339908e-05 }, { -2.6587911e-05, -0.00011609007, -0.00011725093, -7.6114852e-05 }, { 0.00013665042, 5.2703844e-06, -0.00031293536, 3.8693931e-05 }, { -9.8143069e-05, -0.00012816332, -0.00012926252, -0.00010623032 }, { 0.00032342312, -1.9200091e-06, -0.00010691485, 6.3541059e-05 }, { -8.0643542e-06, 9.7622933e-06, 2.9924822e-05, -1.988333e-05 }, { 0.00025318464, 1.2588649e-05, 1.4665927e-05, 9.3294806e-06 }, { 2.6875391e-06, -2.4928123e-05, 2.251878e-05, 0.00011026808 }, { 1.767638e-05, 1.0309044e-05, 2.4765648e-05, 1.4397941e-05 }, { 6.9000935e-06, 1.0637078e-05, 1.087637e-05, 6.3065784e-06 }, { 5.532953e-05, 1.6231463e-05, 4.9564371e-05, 3.6623041e-05 }, { -1.6958729e-05, -3.1627491e-05, -3.1524511e-05, -2.9954116e-05 }, { 8.9045086e-05, 2.1005026e-05, 1.3016463e-05, 8.7863053e-05 }, { -2.75035e-05, -3.0440427e-05, -3.5356286e-05, 5.9609261e-06 }, { 0.0001586274, 4.0711165e-05, 3.1563135e-05, 0.0001385483 }, { 8.5548316e-06, 7.4531928e-05, -3.7017413e-05, 2.6874037e-05 }, { -1.3750655e-05, -8.2756032e-06, -2.7214983e-07, -1.4830115e-05 }, { -7.0798362e-07, -3.3187173e-07, -3.3266762e-07, -5.7113855e-07 }, { 4.3615512e-05, -4.4076433e-06, 8.9239586e-06, 3.7278531e-05 }, { -7.7366773e-06, 4.610399e-06, 4.3762687e-06, -5.64067e-06 }, { -3.2666125e-06, -1.0773146e-05, -1.0861965e-05, -1.3327232e-06 }, { -9.1178305e-06, 0.00030171207, -1.5395234e-05, -2.0695425e-07 } }, { { 0.00017159464, 0.00014699558, 0.00018752678, 0.0002227926 }, { -4.6524822e-05, -0.00010460271, 0.00034735325, 0.00010082238 }, { -6.8269006e-05, 1.4343751e-05, 7.7283393e-06, 2.5347136e-05 }, { -6.6149546e-05, -7.1168993e-05, -7.0621016e-05, -0.00015246746 }, { 7.12022e-05, 3.8790461e-05, -0.00023994449, 6.6792921e-05 }, { -0.00014735813, -0.00012658353, -0.00012162488, -0.00012106777 }, { 0.00015161388, -1.4439153e-05, -3.7629923e-06, 8.3140788e-06 }, { 4.0175416e-05, 2.5380268e-05, -2.2894421e-06, 4.6374378e-06 }, { 0.00028906023, 1.7695243e-05, 5.3790587e-06, 1.631859e-05 }, { 1.8890685e-05, -1.6898275e-05, 2.1007663e-05, 6.5179363e-05 }, { -3.9142595e-06, 2.5745488e-05, 1.0803197e-05, 2.7099749e-05 }, { 9.4245546e-06, 1.0010075e-05, 9.058324e-06, 9.8703427e-06 }, { -2.3441863e-06, 2.5490323e-05, -1.0097654e-05, 4.0554798e-05 }, { -4.1443921e-05, -1.996316e-05, -2.0000841e-05, -4.7495655e-05 }, { 0.00012591695, 5.6179903e-05, -1.8415869e-05, -3.8697972e-05 }, { 2.6719505e-05, 2.4195362e-06, 2.4287424e-05, 3.4703059e-05 }, { 7.3804931e-05, 4.9784871e-05, 3.1159931e-06, 0.00015857197 }, { -0.00010634331, -1.6427658e-05, -7.4874306e-05, -6.2620255e-05 }, { -4.2561214e-06, -1.6123179e-05, -1.5507273e-05, -1.2909924e-05 }, { -1.2210463e-06, 1.1546399e-06, 1.1413892e-06, -1.3465856e-06 }, { 3.4909884e-05, -1.2677793e-05, 0.00011543701, 2.413091e-05 }, { -2.1953323e-05, -4.6244252e-06, -3.5624435e-06, 4.2293671e-06 }, { -1.1392936e-05, -4.3970369e-06, -4.4264864e-06, -1.208518e-05 }, { -4.4002617e-05, 0.00020912348, -3.9617824e-05, -4.1725112e-05 } } }, { { { -0.32504349, -0.32502096, -0.32501094, -0.32423576 }, { -0.65602876, -0.65622598, -0.65567173, -0.65525128 }, { -1.4666488, -1.4666488, -1.4666488, -1.4666488 }, { 0.87168363, 0.87181364, 0.87181792, 0.8718169 }, { -1.264365, -1.264365, -1.264365, -1.264365 }, { 0.89917968, 0.89916889, 0.89916525, 0.89927374 }, { -1.2245906, -1.2245906, -1.2245906, -1.2245906 }, { -0.8885678, -0.88856217, -0.88856327, -0.88855044 }, { -0.31799095, -0.31916566, -0.31907669, -0.31918911 }, { -0.08987958, -0.090342401, -0.090004674, -0.090222398 }, { -0.59425693, -0.59433999, -0.59429118, -0.59433553 }, { 1.1317575, 1.1317475, 1.1317412, 1.1317494 }, { -1.2193493, -1.2193493, -1.2193493, -1.2193493 }, { 1.2506981, 1.250675, 1.250675, 1.2506569 }, { -1.08782, -1.0877793, -1.0878022, -1.0878025 }, { -0.13925598, -0.13932948, -0.13919658, -0.13913403 }, { -0.40394684, -0.4042314, -0.40436178, -0.40402218 }, { -0.47762966, -0.47745572, -0.47767784, -0.47713093 }, { -0.60177181, -0.60176862, -0.60177347, -0.60177079 }, { 2.7311956, 2.7311911, 2.7311911, 2.731191 }, { -1.3109856, -1.3109856, -1.3109856, -1.3109856 }, { 0.60942644, 0.60941369, 0.6094123, 0.60944198 }, { 0.55675448, 0.55672275, 0.55672303, 0.5567542 }, { -0.40637059, -0.4057945, -0.40635768, -0.40636681 } }, { { -0.0016154222, -0.0015930079, -0.0015828998, -0.00087447165 }, { -0.0011262472, -0.001324462, -0.00094895016, -0.00062188189 }, { 0, 0, 0, 0 }, { 9.7616744e-05, 0.00010718899, 0.00010718606, 0.00012665246 }, { 0, 0, 0, 0 }, { 0.00013476236, 6.982272e-05, 6.8208505e-05, 0.00014604742 }, { 0, 0, 0, 0 }, { -0.0031089951, -0.0031071196, -0.0031207245, -0.0031097054 }, { -0.0027808116, -0.0035049857, -0.0034100135, -0.0035192661 }, { -0.0018291474, -0.0019603285, -0.0018919656, -0.0019656229 }, { -0.0034301741, -0.0034912573, -0.0034474395, -0.0034893985 }, { -6.156701e-06, -9.8568527e-06, -1.2383692e-05, -9.9984205e-06 }, { 0, 0, 0, 0 }, { 0.00011838153, 0.00011008679, 0.00011008878, 0.00010536608 }, { -0.0006246638, -0.00058479459, -0.00061327452, -0.00061085433 }, { -0.0059197749, -0.0059778169, -0.0059586015, -0.0058798299 }, { -0.0013246996, -0.0016061786, -0.0016081246, -0.0014374546 }, { -0.001593227, -0.0014706843, -0.0015974008, -0.001341579 }, { -0.0027930604, -0.0027920013, -0.0027939865, -0.0027928528 }, { -1.8908723e-06, -4.266382e-06, -4.2210172e-06, -5.0155215e-06 }, { 0, 0, 0, 0 }, { 0.00018508026, 0.00019774537, 0.00019744661, 0.00019538593 }, { 2.3243747e-05, 1.7291398e-05, 1.7309712e-05, 2.9261396e-05 }, { -0.0041402471, -0.0037085946, -0.0041294876, -0.0041316136 } }, { { -0.0018899732, -0.0018719182, -0.0018661076, -0.0012234594 }, { -0.0012968123, -0.0012971446, -0.00093522854, -0.00066475268 }, { 0, 0, 0, 0 }, { 9.1054464e-05, 0.00014124217, 0.00014156806, 0.00012014953 }, { 0, 0, 0, 0 }, { 0.00017026995, 0.00010528413, 0.00010537941, 0.00015698848 }, { 0, 0, 0, 0 }, { -0.0025812972, -0.0025835894, -0.0025789321, -0.002554949 }, { -0.0035568863, -0.0042988014, -0.0042155548, -0.004312546 }, { -0.0024184575, -0.0025111277, -0.0024654994, -0.0023980076 }, { -0.0036993386, -0.0037113013, -0.0036987284, -0.0037094875 }, { -5.074861e-06, -1.1367399e-05, -1.4819989e-05, -9.2705899e-06 }, { 0, 0, 0, 0 }, { 0.00012570403, 0.00012150272, 0.00012149179, 0.00010579599 }, { -0.00062162762, -0.00058131015, -0.00060837583, -0.00060795256 }, { -0.00775735, -0.0077198081, -0.0078365948, -0.0077749317 }, { -0.0015325554, -0.0017125784, -0.001703195, -0.0015662859 }, { -0.0018130784, -0.00177106, -0.001858095, -0.0015845058 }, { -0.003668417, -0.0036659688, -0.0036693421, -0.0036680526 }, { -9.5804016e-06, -9.6276607e-06, -9.630607e-06, -1.2159056e-05 }, { 0, 0, 0, 0 }, { 0.00017930618, 0.00020084683, 0.00020150104, 0.00020810787 }, { 2.3869269e-05, 1.1024793e-05, 1.1041937e-05, 1.6467357e-05 }, { -0.004690782, -0.0044656761, -0.0046782065, -0.0046921455 } } }, { { { 0.23047932, 0.23043226, 0.23041471, 0.22922185 }, { 0.14990977, 0.15703656, 0.15110771, 0.15149153 }, { 0.30629171, 0.30426701, 0.30400037, 0.30403889 }, { 0.03476576, 0.036188528, 0.036216719, 0.037322097 }, { 0.31066251, 0.31090363, 0.31041565, 0.31057779 }, { 0.04875259, 0.046468595, 0.046486323, 0.046584523 }, { 0.31745458, 0.31874472, 0.32086369, 0.31880207 }, { 0.64054942, 0.64062862, 0.64051973, 0.64059059 }, { 0.27309038, 0.27480819, 0.27477284, 0.27486762 }, { 0.196647, 0.19687982, 0.19607604, 0.1957915 }, { 0.32867362, 0.32858008, 0.32856702, 0.328555 }, { -0.0026873031, -0.0042393446, -0.0057894907, -0.0041858859 }, { 0.40254624, 0.4024247, 0.4025598, 0.40243731 }, { 0.019362807, 0.018146218, 0.018146051, 0.019656613 }, { 0.29328089, 0.29403937, 0.29435036, 0.29403094 }, { 0.57111506, 0.57118505, 0.57099608, 0.57099266 }, { 0.16966612, 0.16993739, 0.17069399, 0.16991136 }, { 0.14989055, 0.1489484, 0.14995985, 0.15015916 }, { 0.33606014, 0.33606294, 0.33606393, 0.33605429 }, { 0.015421206, 0.015180692, 0.01518037, 0.015431139 }, { 0.33165237, 0.33185282, 0.33162592, 0.33166981 }, { 0.078137018, 0.078153855, 0.078165152, 0.078332343 }, { 0.002896946, 0.0026038621, 0.0026029604, 0.0022081151 }, { 0.41064398, 0.40987685, 0.41065341, 0.41059166 } }, { { -0.0024316111, -0.0024732789, -0.0024922144, -0.0035874346 }, { 0.0013306961, 0.004171802, 0.0027660627, 0.0023671465 }, { 0.0034411091, 0.0020878413, 0.0020874456, 0.0022028237 }, { -0.0032873976, -0.0021351911, -0.0021071363, -0.0028424534 }, { 0.0017995208, 0.0022319618, 0.0039270256, 0.0021249365 }, { -0.0019590835, -0.0012526895, -0.0012347747, -0.0021069943 }, { 0.0012319531, 0.002255621, 0.0030193583, 0.0020970822 }, { 0.0015144077, 0.0015110104, 0.0014803089, 0.0015340007 }, { -0.0036679996, -0.0028160114, -0.0028586497, -0.0027953731 }, { -0.005445786, -0.0052624873, -0.0054843188, -0.0053271749 }, { 0.00067154572, 0.0007530775, 0.00067974516, 0.00074462315 }, { -0.0035626119, -0.0034186877, -0.0038720517, -0.0040088745 }, { 0.003455851, 0.0035040061, 0.0034671486, 0.0035069881 }, { -0.0047789747, -0.0047994804, -0.0047996451, -0.0044008337 }, { 0.0032403482, 0.0033627856, 0.003429619, 0.0031153117 }, { -0.005027022, -0.0049812, -0.0049604573, -0.0050556194 }, { -0.0020728991, -0.0014784158, -0.001216894, -0.0019213729 }, { -0.00013808007, -0.00067270623, -0.00024001574, -0.00030691077 }, { 0.0004367104, 0.00043390709, 0.00043548166, 0.00043425516 }, { -0.00082746467, -0.00088151411, -0.00088152334, -0.0008043643 }, { 0.0030277712, 0.003133577, 0.0028529862, 0.0030362271 }, { -0.0058721937, -0.0059816331, -0.0059799345, -0.0058882832 }, { -0.0057032562, -0.0057401855, -0.0057416619, -0.0062417688 }, { -0.0014357888, -0.0020782049, -0.0014346823, -0.0014513767 } }, { { -0.0027051235, -0.0027087245, -0.0027052303, -0.0033594951 }, { 0.0028036195, 0.0030416572, 0.0014306948, 0.0017897371 }, { 0.0031113166, 0.0026432303, 0.0025937824, 0.0025394463 }, { -0.0036032904, -0.003447065, -0.0034344406, -0.0024163572 }, { 0.0023912799, 0.0025281229, 0.0038665087, 0.0024214034 }, { -0.0023543827, -0.0024294943, -0.0024539784, -0.0027742617 }, { 0.0020903896, 0.0026617586, 0.003395249, 0.0026261065 }, { 0.0019031008, 0.0019405475, 0.0019426085, 0.0019404325 }, { -0.0040413326, -0.0030964835, -0.0031020735, -0.0030826754 }, { -0.0064568993, -0.0062342438, -0.0064704698, -0.0065636744 }, { 0.0010788406, 0.0010092051, 0.0010264121, 0.00099891228 }, { -0.0040759201, -0.0059224283, -0.0066809927, -0.0049099348 }, { 0.0042962009, 0.0041909175, 0.0043195236, 0.0041900138 }, { -0.0062728983, -0.0070256154, -0.007025641, -0.0061758746 }, { 0.0036210401, 0.0039723998, 0.0042232048, 0.0042757707 }, { -0.0058693852, -0.0058583303, -0.0058544016, -0.005887725 }, { -0.0023099876, -0.0021136245, -0.0017298078, -0.0022483337 }, { -0.00017851962, -0.00014956209, 8.5676316e-05, -0.00024971669 }, { 0.0003734781, 0.00037078986, 0.00037364181, 0.00037070594 }, { -0.00030648905, -0.00038230535, -0.00038223043, -0.00028623253 }, { 0.0032871423, 0.0034163052, 0.0028276655, 0.0032991918 }, { -0.0061331695, -0.0063319797, -0.0063340119, -0.0064390374 }, { -0.0062172888, -0.0059787106, -0.0059793294, -0.0060406701 }, { -0.0018276142, -0.0022170788, -0.0018293949, -0.0018222824 } } } }, { { { { 0.13218089, -0.11654637, -0.11622196, -0.044208736 }, { 0.0074579257, 0.0038503609, 0.0013201096, 4.0415784e-05 }, { -0.025474487, -0.01209255, -0.016535858, 0.012704547 }, { -0.0016894103, -0.0081312144, -0.0033264609, 0.0011923269 }, { -0.068044876, 0.018276873, -0.074833897, 0.01308348 }, { 0.02665691, 0.013515118, 0.026440814, -0.0077037816 }, { 0.0023286096, -0.0025782652, 0.0021644694, -0.0042955294 }, { 0.051356261, -0.031058382, -0.085382962, -0.033103269 }, { -0.081609229, 0.0035270199, -0.015722417, 0.048773789 }, { 0.0023928418, -0.001243811, 0.011910492, -0.011621478 }, { -0.028953904, -0.029335777, -0.0057891432, 0.013874136 }, { -0.012473582, 0.001772629, -0.013983442, 0.014846792 }, { -0.016111661, 0.0018902323, 0.025910586, 0.042848276 }, { 0.026200626, 0.024007879, 0.0017667146, -0.016394032 }, { -0.0067006429, -0.0017968936, 0.009028659, 0.0044060413 }, { 0.019280611, 0.0449581, -0.042852227, -0.066012332 }, { -0.014451123, -0.047772741, -0.047475406, 0.098434178 }, { -0.0028954635, 0.010521833, -0.015741597, -0.00091666191 }, { 0.0020291956, -0.057966746, -0.04525094, 0.032711614 }, { 0.020563445, -0.0078684621, -0.015282237, -0.0019830466 }, { -0.019504171, 0.071338511, 0.0033729474, -0.0095772339 }, { 0.013056103, 0.018719519, 0.0096002937, -0.028774366 }, { -0.00038728577, -0.0010662982, -0.0014333502, 0.00059135695 }, { 0.073844752, -0.05666013, -0.1007151, -0.030440738 } }, { { 0.00017766639, -9.2398532e-05, -3.9442682e-05, -3.9559848e-05 }, { -0.0043956477, 0.00044042277, -0.00047491077, 9.4171117e-05 }, { -0.0042095545, -0.00910753, -0.0014295282, 0.0042595844 }, { 0.00070989004, -0.0009623012, 0.00084162653, -0.00015925965 }, { -0.0017587638, 0.0033199811, -0.00025544613, 0.00083644978 }, { 0.0051797987, 0.0015691893, -0.002324397, 0.0050776381 }, { 0.003911779, 0.00072639703, 2.102924e-05, -0.0029529332 }, { 0.0050240476, -0.00041452319, 3.1730448e-06, -0.0072697591 }, { -1.5023048e-05, 0.00032491246, -9.2151952e-05, 0.0035851726 }, { 0.0030984373, 0.0016428856, 0.0032974124, -0.0036034289 }, { -0.00044578206, -0.0035916409, 0.0028146658, 0.0068013321 }, { 0.00025716711, -0.0024772152, 0.0029660992, -0.0008783244 }, { -0.005543602, -0.00046453249, 0.006815884, 0.0069207512 }, { -0.0033541738, -0.0015140333, -0.004071746, -0.0020908789 }, { 0.0027932918, -0.0012517158, -0.0033509184, -0.001271572 }, { 0.0043481525, -0.00088858735, -0.0081538059, 0.00027985077 }, { 7.4017523e-05, -7.0080388e-05, -7.1766386e-05, 0.00020468758 }, { 0.00044507396, 0.010179106, -0.0048087449, 0.0013487105 }, { 0.00082148695, -0.00042640153, -0.0024255173, 0.0044486011 }, { -0.00026383509, -0.0031871528, -0.008203704, -0.00053957093 }, { -0.0002996462, 0.00070789605, 7.9300612e-05, -0.00024002209 }, { 0.0013722116, 0.0049176054, 0.0029283062, -0.000849108 }, { 0.00026545039, 0.0011783443, 0.00072103548, -0.0007355776 }, { 0.002192273, -0.00294318, 1.5452606e-05, -0.0020953993 } }, { { 2.4074136e-05, -2.4931598e-05, -1.0893587e-05, 1.080951e-05 }, { -0.0061635883, -0.0042963493, -0.00177783, -0.00080292808 }, { 0.0047868795, -0.0050472436, 0.0082439123, -0.0090979713 }, { 0.0017221077, 0.0067285193, 0.0031011872, -0.0019932567 }, { 0.0010926271, -0.0012170693, 0.00012875612, 0.00016441623 }, { -0.0048786273, -0.0041225634, -0.005591426, 0.0043469593 }, { -0.0070664098, -0.0012625813, -0.00022220241, -0.0026120468 }, { -0.0026689917, 0.00030860545, 1.9297947e-05, 0.001274799 }, { 0.0026769559, 0.00016106032, 0.00013829246, -0.0017239107 }, { -0.0042495789, 0.0010270326, -0.00078224804, -0.0019210019 }, { 0.0072385804, 0.0086418476, 0.0061428272, -0.0027142827 }, { 0.0019768127, -0.00057957046, 0.0047464783, -0.004599565 }, { 0.0093618867, -0.0010476542, -0.0038681572, -0.0065219521 }, { -0.0076406673, -0.0036729355, -0.0068804827, 0.0077571478 }, { 0.0012706397, -0.00042567505, -0.002521821, 6.0288127e-05 }, { -0.002041411, 0.000430125, 0.0073620925, 0.0021579456 }, { 0.00012145466, 4.1276616e-05, 4.2449608e-05, 9.8351262e-05 }, { 0.0014376278, -0.007439719, 0.0039006971, 0.00051135138 }, { -7.1665367e-05, 0.00023856335, 0.00015274881, -0.0096946274 }, { -0.00076804256, 0.0040182915, 0.012603411, -0.00059669891 }, { -0.00010641981, -0.00052355992, 0.00057481361, 0.00016456343 }, { -0.0027623375, -0.0036761364, -0.010480297, 0.0066006902 }, { 0.00049081404, 0.00077264749, 0.0021355718, -0.00029188425 }, { 0.00028566818, 0.00097678458, 0.00089022281, -0.00013760767 } } }, { { { -0.0098123577, 0.11017117, 0.11245143, -0.01173447 }, { 0.0036188505, -0.0025878518, -0.00043343726, -0.0038813197 }, { 0.013109746, -0.016775181, -0.0011093308, 0.00083465721 }, { -0.0042515898, -0.0028159364, 0.00027829209, -0.002907578 }, { -0.0081027554, -0.0019330574, 0.061872524, -0.037539524 }, { -0.012923735, 0.021011524, 0.002680406, 0.0034369108 }, { 0.0027819214, 0.0028657905, -0.0034177203, -0.0037322329 }, { -0.0036178174, 0.065792163, 0.13263475, 0.0055427994 }, { 0.027832309, -0.083372016, -0.058757582, 0.016164879 }, { -0.0082343898, 0.011782416, 0.011496052, -0.0027847616 }, { 0.0012516658, -0.014686832, -0.025073035, -0.020700577 }, { 0.0055718234, -0.011543219, -0.012867689, -0.0049474286 }, { 0.028869265, -0.035431559, 0.024976635, -0.01063055 }, { -0.0010657662, 0.014977146, 0.027109, 0.01612865 }, { -0.0021697493, 0.0044220507, 0.0055654161, -0.0032373397 }, { -0.018500666, -0.01979267, -0.0068480612, 0.03908391 }, { 0.063306878, 0.01934691, 0.019254616, -0.099824471 }, { 7.0580666e-05, -0.0015082457, -0.0056893693, 0.00022726294 }, { 0.0077067654, -0.014018834, -0.021406454, -0.0076589993 }, { -0.0013072394, 2.6765854e-05, 0.0028400803, 0.0037431063 }, { -0.025369581, -0.064039908, -0.020594137, -0.086807367 }, { -0.033639351, 0.010434758, 0.00082983507, 0.013145885 }, { 0.00029373395, 7.8193614e-05, 0.00048496415, 0.00062972215 }, { -0.0041597628, 0.024283117, -0.030148407, 0.011456515 } }, { { -1.3484857e-05, -3.7204145e-05, -1.5660577e-05, -2.4497955e-05 }, { -0.0068070249, 0.0041035892, 0.0034647689, 0.0035918321 }, { -0.0053613309, 0.0080593503, 0.0028507084, -0.0023104987 }, { 0.0048581064, 0.0039720065, -0.0019058129, 0.0047295789 }, { -0.00030675956, -0.0007787587, -0.00025201217, 0.00020777843 }, { -0.00026433336, -0.0093672701, -0.0053201627, -0.0059632173 }, { -0.0063062815, 0.0011995204, 0.0001870407, 0.0028197877 }, { -0.00053247524, -0.00066138217, -1.4959372e-05, -0.00036023628 }, { 0.00027591427, 0.00011309835, 2.2453632e-05, -0.00075736359 }, { 0.0015654886, 0.0018114616, -0.0004503446, -8.5866048e-05 }, { 0.003501393, 0.0037179893, 0.008328543, 0.013411108 }, { -0.0035136609, -0.0015054003, 0.0011903964, 0.0022551358 }, { -0.0083723767, 0.0061303554, -0.008056962, 0.0035035183 }, { -0.0023715655, -0.0070468331, -0.010219655, -0.0057856465 }, { -0.0011406634, -0.00021204595, -0.001693195, 0.0011051597 }, { 0.0011643412, 0.00037557194, 0.0048567739, -0.00063996433 }, { -3.1728174e-05, -2.9073903e-06, -3.0243209e-06, 2.579239e-05 }, { 0.00053152589, 0.0029635352, 0.0040743289, -0.00051381046 }, { -0.0017253584, 0.00012081524, 0.00012243664, -0.00063598215 }, { 0.0026711847, -0.0020733972, -0.0027860744, 0.0017065643 }, { 5.7762902e-05, 0.00092043577, -0.0035278882, 0.0007846087 }, { 0.0056127705, -0.0051893669, -0.0027072408, -0.0025630045 }, { -0.00059289151, -0.0004168408, -8.8118696e-05, -0.00073538101 }, { 0.0003388606, -0.00094234652, 3.013109e-05, -0.0010532484 } }, { { -2.9013996e-05, 6.1983083e-05, 2.8401438e-05, -3.4901557e-05 }, { 0.0045230474, -0.0021369843, -0.00422706, -0.0018918027 }, { 0.00017586142, 0.005389053, 0.0071352982, -0.0018278685 }, { -0.0012135723, -0.0035970727, 0.00078957165, -0.0017065397 }, { -0.00067051937, -1.9501585e-05, 4.1968766e-05, -0.0010958091 }, { -0.0015277626, -0.0039952533, -0.00049631478, 0.0018042745 }, { 0.0039376754, -0.00097834328, 6.5894634e-06, -0.0044189106 }, { -0.00067623039, 0.0004690807, 1.4532105e-07, 0.0032984829 }, { 0.0020787449, -0.0016586579, -0.00062367064, 0.0021545362 }, { 0.0016427801, 2.6710288e-05, 0.0016011535, -0.00077649869 }, { 0.0039999622, -0.0014968097, -0.0025647576, 0.0022783424 }, { 0.001558454, -0.00083803058, 0.0018955692, 0.0010432376 }, { 0.010555722, -0.010395022, 0.0050354965, -0.0016177699 }, { 0.00011370745, -0.009328355, -0.0063009522, 0.0024377458 }, { -0.00024433189, 0.00052920244, -0.0013213352, -0.0013503982 }, { -0.0057620093, 0.00095391746, -0.0034768563, 0.00093990705 }, { 0.00012108024, 4.1007202e-05, 4.2193381e-05, -0.00011043617 }, { 0.0038593696, -0.00074282979, -0.0093457897, 0.00027311164 }, { 0.0021514797, -7.8742315e-05, -0.0018813077, -0.0017625098 }, { 0.0038491118, 0.00022570776, -0.0061331041, 0.00014956617 }, { -0.00014676603, -0.00025053931, 0.003376287, -0.00014730695 }, { 0.0016439646, 0.0060569792, 0.00063058918, -0.0034810156 }, { 0.00011722835, 0.00032237223, -0.0012556553, -0.0006887808 }, { 0.00060814722, 0.0003708376, -0.00056515636, -0.00016801817 } } }, { { { 0.99117704, 0.98705585, 0.98683693, 0.9989534 }, { 0.99996564, 0.99998924, 0.99999903, 0.99999247 }, { 0.99958951, 0.99978616, 0.99986266, 0.99991895 }, { 0.99998953, 0.99996298, 0.99999443, 0.99999506 }, { 0.99764936, 0.9998311, 0.99527468, 0.99920949 }, { 0.9995611, 0.99968788, 0.99964679, 0.99996442 }, { 0.99999342, 0.99999257, 0.99999182, 0.99998381 }, { 0.99867384, 0.99734987, 0.98748052, 0.99943657 }, { 0.99627571, 0.99651225, 0.99814846, 0.99867903 }, { 0.99996323, 0.99992981, 0.99986298, 0.99992859 }, { 0.99957996, 0.99946171, 0.99966886, 0.99968945 }, { 0.99990668, 0.9999318, 0.99981943, 0.99987754 }, { 0.99945334, 0.99937032, 0.99935219, 0.99902503 }, { 0.99965614, 0.99959957, 0.99963092, 0.99973552 }, { 0.9999752, 0.99998861, 0.99994375, 0.99998505 }, { 0.99964293, 0.99879278, 0.99905795, 0.99705307 }, { 0.99788947, 0.99867085, 0.99868681, 0.99012413 }, { 0.99999581, 0.99994351, 0.99985991, 0.99999955 }, { 0.99996824, 0.99822008, 0.99874627, 0.99943549 }, { 0.9997877, 0.99996904, 0.99987919, 0.99999103 }, { 0.99948785, 0.99539425, 0.99978223, 0.99617908 }, { 0.99934875, 0.99977032, 0.99995357, 0.99949949 }, { 0.99999988, 0.99999943, 0.99999886, 0.99999963 }, { 0.99726107, 0.99809817, 0.99445842, 0.99947091 } }, { { -2.3481737e-05, -6.7307406e-06, -2.8605869e-06, -2.0372001e-06 }, { 6.6885689e-05, 4.5630281e-06, 3.5788218e-05, 1.0842484e-05 }, { -4.9278613e-05, -2.4660601e-05, 3.1625301e-06, 0.00019708279 }, { 1.2439158e-05, 3.0347865e-05, 8.6153947e-06, 1.0887256e-05 }, { -0.00012454598, -6.513709e-05, -3.5853483e-06, -3.4708286e-06 }, { -0.00013746339, 0.00013516333, 8.4535039e-05, 5.693766e-05 }, { -2.3674091e-05, -3.4690053e-06, 5.3812265e-07, -1.7613197e-05 }, { -0.00025790043, 3.0475251e-05, 2.1174795e-06, -0.00023630753 }, { -8.8624748e-06, 7.9175589e-06, -2.4258477e-07, -0.00017288313 }, { 4.0061469e-05, 0.00069846663, -0.00060299476, -0.00015396968 }, { 5.0667108e-06, 2.306363e-05, 0.00028636884, 3.6246633e-05 }, { 0.00032740524, -0.00037985037, -0.00014841039, -0.00012676016 }, { 8.7000758e-05, 0.00018530207, 1.7669124e-05, -0.00023199594 }, { 9.2332094e-05, 0.00013487652, 0.00034587506, -3.8853378e-05 }, { 6.9809868e-05, -0.00015411544, 0.0013505166, 1.4531796e-06 }, { -6.3782301e-05, 4.8545135e-05, -0.00027083794, 4.5129465e-05 }, { 3.0912438e-06, -3.2982361e-06, -3.3551612e-06, -1.7781589e-05 }, { 9.872609e-06, -2.9944213e-05, -4.5592652e-05, 1.5950681e-05 }, { 1.4767773e-05, -2.2486726e-05, -0.00010613341, -0.00015794394 }, { 2.4386215e-05, -1.1610334e-05, -4.4456294e-05, -5.0215596e-06 }, { -4.2741558e-06, 8.7714242e-06, -6.6343322e-05, 6.7010735e-05 }, { 0.00016489767, -3.3636771e-05, 5.1610504e-05, 5.2803593e-06 }, { 1.1649256e-05, 2.1169993e-05, 1.9755999e-05, 1.3389438e-05 }, { -0.00015815197, -0.00014316145, 2.6536218e-06, -4.6846396e-05 } }, { { -3.5109783e-06, -9.8530632e-06, -4.5020804e-06, 6.9233235e-08 }, { 9.9938991e-06, -2.0914089e-06, 3.5717699e-05, 3.2813664e-06 }, { 0.00012938219, 1.111062e-05, 8.0858608e-05, 0.00018147439 }, { 4.8657525e-06, 8.6580257e-06, 3.6742927e-06, 3.5828406e-06 }, { 6.9905696e-05, 2.0985073e-05, 6.8866215e-06, -4.2552499e-05 }, { 0.00012100208, 9.7821801e-05, 0.00013576456, 6.3686234e-05 }, { 1.954525e-06, -1.0727343e-06, 5.2332444e-07, -5.4034988e-06 }, { 0.00013699813, -2.226833e-05, 1.4994043e-06, 1.7110377e-05 }, { 0.0001678261, -0.00013844113, -3.4281745e-05, 5.3854072e-05 }, { -1.3018868e-05, 0.00022176303, 0.00016983401, 0.00038109805 }, { 0.00019016068, 0.00023448876, 2.643329e-05, 4.6842203e-05 }, { -1.2492528e-05, -0.00059486605, 0.00012427061, 8.1876965e-05 }, { 8.400564e-05, -0.00029859163, -4.884214e-05, 0.0002631806 }, { 0.00019907281, 0.00014046808, 0.00015482448, 4.0461099e-05 }, { -0.00024349239, 0.00081298441, 0.00084294728, 7.9617963e-05 }, { -6.0040835e-05, 3.2352918e-07, 0.00024295599, 0.00011067283 }, { -6.0027092e-06, 1.1975092e-06, 1.2248893e-06, -2.1293392e-05 }, { 1.4478736e-05, 6.8326918e-05, -7.8693614e-06, 9.2888155e-06 }, { -1.6982828e-05, 1.2094341e-05, -3.1693808e-05, 0.00028574477 }, { 3.4480942e-05, 2.6556008e-05, 0.00016193956, -1.8966503e-06 }, { -5.7726961e-06, 2.1091148e-05, 5.8963955e-05, -1.0834372e-05 }, { 0.0001214393, 1.4174882e-05, 0.0001371836, 0.00021757165 }, { 1.0140226e-05, 6.1641031e-06, 1.0590727e-05, 1.0893212e-05 }, { -1.7442656e-05, 4.2353331e-05, 7.4324714e-05, -1.9484775e-06 } } }, { { { 3.7217719, 3.6900797, 3.6899881, 3.6670816 }, { 0.067826319, 0.16468028, 0.083129199, 0.1336756 }, { 0.66338737, 0.23883566, 0.093361469, 0.10095622 }, { 0.27185537, 0.20781392, 0.32216624, 0.29876595 }, { 2.0776462, 2.0006156, 2.0243138, 2.080345 }, { 0.57695783, 0.18015147, -0.11440889, 0.14229144 }, { 0.63833683, 0.41431062, 0.44752994, 0.47594414 }, { 1.7890608, 1.962584, 1.9322155, 1.6588331 }, { 3.0538128, 3.108267, 3.1001573, 2.9593433 }, { -0.28383051, -0.27708376, -0.042513902, -0.085181891 }, { 0.3873435, 0.41697884, 0.39625427, 0.33250735 }, { -0.33498881, -0.40206929, -0.028905862, -0.48179632 }, { 1.1875033, 1.3535177, 1.2526197, 1.3337495 }, { 0.42579488, 0.24951727, 0.18976118, 0.20605317 }, { -0.53212666, -0.3861028, -0.75685995, -0.23411882 }, { 1.6910165, 1.686815, 1.5906473, 1.6528217 }, { 4.0570657, 4.0349492, 4.0350332, 4.0498099 }, { -0.017225465, -0.032503897, 0.46003211, 0.21602109 }, { 1.1196901, 1.00885, 0.91675568, 0.99635794 }, { -0.093891275, 0.0809352, -0.13783332, 0.27130678 }, { 1.9925136, 1.9829394, 1.8820721, 1.9542026 }, { 0.84563763, 0.48476746, 0.37907152, 0.70267878 }, { 0.37054708, 0.4228574, 0.6329822, 0.26197064 }, { 1.9618393, 1.8405969, 1.9440918, 1.901629 } }, { { -5.6047186e-06, 6.0454847e-06, 2.8365975e-06, 6.0894367e-06 }, { -0.00069876506, -0.00029642785, -0.00059516082, -0.00025400441 }, { -0.00020850504, -0.00012959593, -0.00032902532, -0.00058117893 }, { -0.00037901964, -0.00038062016, -0.00023777964, -0.00033714679 }, { -5.9894351e-05, -9.820791e-05, -5.9867157e-06, -6.258549e-06 }, { -0.00035424038, -8.7146215e-05, 3.0398362e-05, -0.00061406521 }, { 0.00014971442, 4.5936211e-05, -5.6259869e-06, 0.00013567035 }, { -0.00016180211, 3.1840487e-06, 3.8979157e-07, -0.00017131994 }, { -1.9877193e-05, 2.5768261e-05, 9.0577543e-06, -0.00013927462 }, { -0.0012323564, -0.00042892846, 7.2082106e-05, 0.00010999853 }, { -0.00034618449, -0.00017058897, -0.00016535057, -0.00096982024 }, { -0.00028039653, -7.155747e-05, -0.00075796707, 0.00062756458 }, { 6.6596276e-05, -7.9730809e-05, -8.0686754e-05, -2.9532397e-05 }, { -0.00084106867, -0.00036762453, 0.00012523548, -0.00052789663 }, { 7.6718268e-05, -0.0010042005, -0.00042802983, -0.0011951304 }, { -3.6972258e-05, 2.1447505e-06, -0.00035448623, -1.0620008e-05 }, { 2.8326169e-05, 2.2049468e-05, 2.2640575e-05, 1.7574827e-05 }, { -0.00014318496, -0.0004811524, -0.00049293303, -0.00067646484 }, { -2.7469144e-05, -5.9653763e-06, -1.3998899e-05, -0.00018475323 }, { -0.00017314302, -0.00010954727, -0.00040004932, 3.31106e-05 }, { -3.6093435e-06, -1.6125243e-05, -4.9195648e-05, 1.5586886e-05 }, { 0.0002059631, -0.0004024722, -0.00047984678, -9.8485329e-05 }, { -0.00094100913, -0.00073046048, -0.00052500163, -0.00068196784 }, { -2.2820197e-05, -5.9454557e-05, -6.2505468e-06, -2.6569804e-05 } }, { { 2.6015883e-05, 8.5398335e-06, 3.8473185e-06, 9.1409625e-06 }, { -0.00041459247, -0.0001855224, -0.00030529542, -0.00016322166 }, { -8.8427847e-05, -0.0002302048, -0.00038072959, -0.00076801295 }, { -0.00027717792, -0.00028594346, -0.00017910208, -0.00027291164 }, { 2.8409311e-05, -3.8005817e-05, -4.2266878e-06, -1.4520383e-05 }, { -0.0001088827, -0.00021924377, 3.9307406e-05, -0.00032488556 }, { 0.00027997916, 3.5103699e-05, -5.7448764e-06, 0.00010259251 }, { -4.7807894e-06, -2.9470863e-05, 2.6656233e-07, -0.00014346393 }, { 0.00015527098, -6.8528726e-05, -1.1206714e-05, 2.3422595e-05 }, { -0.0012763247, -0.00051503472, 0.00058055106, -0.00068688488 }, { -6.1232076e-06, -1.7073841e-05, -0.00033533389, -0.00078769935 }, { -0.00044113485, -0.00027577451, -0.0012008622, 0.00013071136 }, { 1.834948e-05, -0.00015615102, -0.00016449385, 3.6685217e-05 }, { -0.00063618257, -0.00032641968, -5.0281118e-05, -0.00041378992 }, { -0.0010181884, -0.0003871932, -0.00050061147, -0.0018967455 }, { -5.7650067e-05, -5.1145774e-06, -0.00017409773, 1.9512036e-05 }, { 1.5838743e-05, 2.503655e-05, 2.5679098e-05, 2.0053218e-05 }, { -0.00018055811, -0.00044345237, -7.9049557e-05, -0.00095669161 }, { -4.98611e-05, -1.1320605e-06, 3.7756645e-06, -8.7299215e-05 }, { -0.00011794063, -0.00015778552, -0.00036514881, 4.7288704e-05 }, { -5.1753817e-06, -1.5040527e-06, -2.836739e-05, -9.4945229e-06 }, { 0.00016873335, -0.00031983601, -0.00052281245, 0.00019034815 }, { -0.0011988594, -0.0010684975, -0.00057577023, -0.0009143845 }, { 5.0336006e-05, -1.356148e-05, 1.5582694e-05, -2.0666272e-05 } } }, { { { 0.012207721, 0.0044164612, 0.0022704542, 0.0042008503 }, { 0.29516302, 0.139976, 0.35038027, 0.13748343 }, { 0.1462123, 0.12114907, 0.28473665, 0.45762717 }, { 0.17976664, 0.19141553, 0.1209483, 0.16393769 }, { 0.044254492, 0.11383095, 0.0062726904, 0.023550537 }, { 0.14785458, 0.10151341, 0.045717467, 0.42243971 }, { -0.24205201, -0.033590842, 0.0032064617, -0.093924041 }, { 0.10866955, 0.016299431, 0.00081631108, 0.15856447 }, { 0.10108337, 0.057931152, 0.024463589, 0.21514346 }, { 0.47967783, 0.75472932, 0.5653649, 0.64752457 }, { 0.30082544, 0.15124922, 0.23567284, 0.47161499 }, { 0.54286166, 0.61049777, 0.61641378, 0.51181399 }, { 0.39328762, 0.25557559, 0.25875912, 0.22436901 }, { 0.45699569, 0.16989563, 0.2429263, 0.3924359 }, { 0.92996797, 1.1024806, 0.78045387, 1.2298879 }, { 0.19029829, -0.022675055, 0.28113642, 0.034941166 }, { 0.013203939, 0.013034069, 0.013414649, 0.011688038 }, { 0.076026927, 0.13838472, 0.29961655, 0.31531564 }, { 0.089182386, 0.010401684, 0.029374547, 0.22995838 }, { 0.052198894, 0.039866726, 0.11570972, -0.013818992 }, { 0.0062380932, 0.01788119, -0.20765047, 0.013339281 }, { 0.12436441, 0.17318651, 0.21554136, 0.18600144 }, { 0.38005287, 0.32135548, 0.28632777, 0.29211902 }, { 0.03798742, 0.0450845, 0.010912505, 0.039060104 } }, { { 0.00077914246, 0.00011130803, 8.1110229e-05, -0.00035312557 }, { 0.00051711901, 0.00029701387, 0.00040733345, 0.00034149723 }, { 0.00063893978, -0.00013702086, 0.00030866699, -0.00020070677 }, { 7.5899443e-05, 9.7456273e-05, -4.5352178e-05, 7.6172703e-06 }, { 0.00066250814, -0.00073033349, 0.00015225542, -0.0010197351 }, { 0.00040931533, -0.00043022747, 0.00093333285, 0.0002579685 }, { -0.00067488578, -0.0003706974, -0.00044487256, -0.00056555959 }, { 0.00075838366, -0.0021903789, -0.0026744174, -0.00047135202 }, { -0.00081050821, -0.0010297809, -0.00099480849, -0.00074914246 }, { 0.00063637392, 5.248783e-05, 0.00044645091, 0.00018028446 }, { 0.00067430392, 0.0004762628, -0.00032736685, 0.00041933609 }, { 6.2324555e-05, -1.6709531e-06, 0.00057418116, -0.0010360999 }, { -0.00038256183, -0.0010104012, -0.00045533693, -1.3888404e-05 }, { 0.00068274628, 0.00068411875, -0.00091273333, 0.00016211145 }, { -0.00039440715, 0.00027665323, -0.00035895503, 0.00013423207 }, { -0.00061939017, 0.00012140102, 0.00024178233, 0.00064755788 }, { -0.00052441128, -0.00050994483, -0.00051126044, 0.00066320373 }, { 0.00085915332, 0.0013567332, -0.00014328466, 0.00056098523 }, { -0.0012682676, 0.0029139719, 0.0019812291, -0.00053863027 }, { 0.0021895869, 0.00062956835, 0.0018161156, 0.00011699452 }, { -0.0010337306, 0.00016880497, -0.0014942346, -0.0034402453 }, { -0.0025336946, -0.00019468865, -0.00018045349, -5.4312149e-05 }, { 0.00021491979, 4.7651714e-05, -0.00044921151, 0.00046742044 }, { 0.0019408125, 0.00044842687, 0.0026003265, -0.00090116109 } }, { { -0.0006591255, 0.00022873584, 0.00026313866, -0.00060151354 }, { 0.00027198127, 0.00034252944, 0.00033246896, 0.00035232159 }, { -0.00034460639, -5.9085725e-05, 7.836454e-05, -0.00018946388 }, { 0.00018790551, 0.0001918358, 9.7031467e-05, 0.00015259869 }, { -0.0023033429, -0.0012945186, -0.00080964072, -0.00030432514 }, { -0.001359781, 0.00055828912, -0.00041912301, 0.00019263336 }, { -0.00042789448, -0.00018313775, -0.00030217124, -0.00028437496 }, { -0.0018340159, 0.00030654336, -0.00010781402, -0.0011985455 }, { -0.002103478, 0.00029492518, -0.00042283946, -0.001472689 }, { 0.00064558079, 0.00049703204, -0.00018932594, -0.00038268301 }, { -0.00097813334, -0.00057838807, 0.00079268109, 0.00039650774 }, { -0.00017335252, 0.00074363734, 0.0008194423, -0.00065923207 }, { -0.00075344545, -0.00026114262, -0.00054658657, -0.0013814943 }, { -0.00028279346, 0.00055730283, 0.00048990213, -0.00022186466 }, { 0.00013438509, -0.0001962818, -0.00036195953, 0.00042669461 }, { -0.00089003585, -0.0011600794, -0.0012554286, -0.0012892408 }, { -0.00067007058, -0.0010597247, -0.0010590421, 0.00044132516 }, { 0.0011626727, 0.001261033, -0.00072912018, 0.00076332442 }, { -0.001204702, -0.00011230019, 0.00036178615, -0.0017559004 }, { 0.00096282849, 0.001025959, 0.0011696947, 0.00046633555 }, { -0.00082328571, -0.00075771669, -0.0011629302, 0.00073458863 }, { -0.0016869269, -0.00035239862, -0.0004024204, -0.0016276971 }, { 0.00029053123, 0.00013409355, -0.00049087974, 0.00061969429 }, { -0.0013198997, -0.0018615784, -0.0025724061, -0.0015563017 } } } }, { { { { -0.072246889, -0.043157285, 0.043289306, 0.095998047 }, { 0.12597079, 0.24289541, -0.10930005, -0.24150539 }, { 0.031889347, -0.036238337, -0.014521983, -0.018963885 }, { -0.044155351, -0.0077170425, -0.043781059, 0.047982339 }, { 0.093995001, -0.0079510758, -0.04688882, -0.11125523 }, { 0.01700754, -0.0034361033, 0.055252382, -0.053119426 }, { -0.0014957087, -0.00063057103, 0.037930463, 0.017656646 }, { -0.017388477, -0.084085888, -0.067726647, 0.061397079 }, { -0.070625168, -0.061293011, -0.077366932, 0.11518646 }, { -0.14771316, -0.12543895, 0.052150789, 0.10530462 }, { -0.03609139, 0.001131616, -0.039549928, 0.03805765 }, { 0.064364205, 0.066758929, 0.045537002, -0.05510954 }, { 0.049051369, 0.098312455, -0.01079726, -0.11202623 }, { 0.033012208, -0.0013996988, -0.0049458824, -0.028981527 }, { 0.008617177, -0.00017670863, -0.0052380282, -0.0023438457 }, { -0.05901498, -0.050754807, -0.00011829844, 0.037297411 }, { -0.056264446, -0.03645315, -0.066412698, 0.019549244 }, { -0.11401603, -0.11856524, 0.12275022, 0.11635143 }, { -0.0011999881, -0.0016334327, -0.0056868938, 0.013393766 }, { 0.054526972, 0.033632235, 0.062591094, -0.0025531074 }, { 0.073041316, 0.073735243, -0.06935254, -0.11214186 }, { 0.034872822, -0.015473423, 0.037359975, -0.026829465 }, { -0.015137592, -0.0064462553, 0.011771178, 0.0025042048 }, { -0.038708904, -0.033968131, -0.044070885, 0.024422773 } }, { { -0.047895007, -0.016535938, 0.04855533, 0.018341613 }, { 0.004310087, 0.01519838, -0.0033290683, -0.013597406 }, { 0.0015859181, 0.016869623, -0.019279963, -0.01426933 }, { -0.0061048976, 0.031131561, 0.018085381, -0.017927117 }, { 0.052590378, 0.0066156852, -0.0025756141, -0.037241705 }, { 0.0083512619, 0.0046235666, 0.024122126, -0.013443654 }, { 0.0010672274, 0.00053123301, -0.0016276029, -0.04221993 }, { -0.0048754166, -0.021474788, -0.0039993317, 0.011831691 }, { -0.054685347, -0.050242732, -0.007606251, 0.043061893 }, { -7.5644942e-05, 0.00086632318, 0.0001960729, 0.0013264286 }, { 0.0042413724, -0.0057181522, 0.0065940983, -0.0078263328 }, { 0.0031260881, -0.0013520907, 0.025073658, -0.010841673 }, { 0.038353769, 0.06620308, -0.0072105562, -0.079188681 }, { 0.003099559, -0.0022927921, 0.021982683, -0.018991144 }, { 0.012285675, 0.0091834074, -0.0041874571, -0.032253924 }, { -0.014563556, 0.009843969, -0.010490279, 0.012979866 }, { -0.005492286, 0.064109426, -0.034795617, -0.020395732 }, { -0.023364141, -0.059336321, 0.080710391, 0.038948527 }, { 0.0028384819, 0.001822471, 0.0012903958, 0.012781079 }, { -0.004510518, -0.0020008272, 0.0017752876, 0.0077607089 }, { 0.032279653, 0.0041906079, -0.034682371, 0.0061335907 }, { -0.0082992317, -0.025250117, -0.017026845, -0.028345042 }, { -0.013132125, -0.026688493, -0.0014827793, -0.003236826 }, { 0.01650781, 0.002313574, -0.012897922, 0.026077933 } }, { { 0.062668058, 0.0081578851, 0.018952049, -0.012267283 }, { 0.0008567722, 0.0033246009, -0.0037620102, -0.0096317368 }, { -0.0083012273, 0.01184624, -0.01209373, 0.020208536 }, { 0.013862003, 0.019166381, 0.013235471, -0.026788736 }, { -0.021904217, -0.051018749, 0.0020330268, 0.006626371 }, { -0.015856131, 0.0028024655, -0.032825412, -0.018920906 }, { 0.0020870233, 0.0011616727, -0.0032704368, -0.027327141 }, { 0.01934969, 0.002427195, 0.049925128, -0.0061414889 }, { 0.013158375, 0.022248445, 0.040266734, -0.017583455 }, { 1.9024812e-05, 0.00071602053, 0.0012622199, 0.0018791611 }, { -0.0011857767, 0.0023417924, 0.026237548, -0.014687892 }, { -0.041419782, 0.024942194, -0.029143101, 0.036590943 }, { -0.015470651, -0.035208671, -0.038530514, 0.037434376 }, { -0.0029356279, 0.0023358079, 0.017641055, 0.0038203652 }, { -0.0030449623, -0.010187444, 0.0066142145, 0.0037433206 }, { 0.0080034603, 0.011463159, -0.0058129532, 0.011831147 }, { -0.0091743137, 0.045949289, 0.022412137, -0.0067531419 }, { 0.00069946656, -0.0068974782, 0.0091806954, 0.0022160793 }, { -0.0027530077, 0.00089797627, 0.0066153093, -0.010355635 }, { -0.019399018, -0.0085762573, 0.0208003, -0.027739023 }, { -0.014354809, -0.011971089, -0.0031124986, 0.044710091 }, { -0.011411144, 0.0073253411, -0.0087561348, -0.014838738 }, { 0.018837992, 0.00231775, -0.013982978, -0.0020044658 }, { 0.0012069362, 0.0012202952, 0.029106153, 0.00062793994 } } }, { { { 0.054154158, -0.11603661, -0.025631275, 0.054671866 }, { -0.2359715, 0.093194255, 0.21874866, -0.08378526 }, { 0.0089903397, 0.0087113885, -0.015445726, 0.011142042 }, { -0.0055372249, -0.0041494086, -0.033355186, -0.010136823 }, { -0.015010227, -0.0077144008, 0.13058394, -0.016779666 }, { -0.015855009, 0.014090685, 0.026549575, 0.025677527 }, { -0.00065423811, -0.0011506403, 0.028628751, 0.0086359197 }, { -0.010571292, 0.035861454, -0.025871285, -0.024827688 }, { 0.00010603924, 0.011433504, -0.052819957, -0.020208661 }, { 0.12243361, -0.14574398, -0.10091072, 0.054524772 }, { -0.014659734, -0.02291001, 0.010102434, -0.0099333349 }, { -0.0079939087, 0.023468399, 0.044548395, 0.04568814 }, { -0.048188816, 0.016469102, 0.084818672, -0.040634065 }, { 0.015089138, 0.025396216, 0.017000121, 0.010820807 }, { -0.0098155552, -0.00080001495, 0.0020122754, -0.00046896909 }, { -0.0018906417, -0.03909342, -0.020339049, -0.024007559 }, { -0.0012744487, -0.027829333, -0.05202457, -0.024366779 }, { 0.10406956, -0.092281421, -0.050420166, 0.10716663 }, { -0.0049603976, -0.0055370076, -0.0016910106, 0.012172389 }, { -0.0026486448, 0.038673757, -0.0016176887, 0.052692494 }, { -0.03722357, 0.055455783, 0.067738953, -0.0087990582 }, { -0.0026491637, 0.017275247, 0.010687117, 0.020312052 }, { -0.0016032469, 0.0090272843, -0.0079027514, -0.0050039898 }, { -0.0073653412, -0.033150577, 0.0082912493, -0.021457881 } }, { { -0.0059001999, 0.033600833, 0.066374213, -0.018058548 }, { -0.0037864945, -0.0064946131, 0.0018627774, 0.0044899139 }, { 0.0048961861, -0.0034770968, -0.0002311598, -0.0053935761 }, { 0.0090090757, 0.012149811, 0.0029969663, 0.0049403543 }, { -0.042874682, -0.0083455851, -0.0064437344, 0.0010579362 }, { 0.011866873, -0.017157526, -0.014724976, 0.0054373752 }, { -0.0006329516, -0.00024834697, 0.0015416168, -0.014246989 }, { 0.031530357, -0.052715858, -0.0063186617, -0.0070200141 }, { -0.0082273844, 0.053856605, 0.0096812384, 0.01684635 }, { -0.00017150577, 0.00097354737, 0.0013944706, 0.00085166684 }, { -0.013604545, 0.0089329355, -0.013809086, 0.0025044469 }, { -0.020284731, 0.0004724419, -0.045697697, -0.01844702 }, { 0.017874081, -0.0040537465, -0.023316716, -0.026344708 }, { 0.0092557469, -0.014456327, -0.0092919835, 0.0091758924 }, { 0.016058873, 0.0019220807, 0.0031692823, 0.0024577167 }, { -0.021184352, 0.021287579, -0.0048442696, 0.0095799112 }, { 0.035229915, -0.054291919, -0.013871324, 0.035585241 }, { 0.001275203, 0.011513119, 0.020184769, -0.0061701639 }, { 0.011353237, 0.0052697685, 0.0047637419, -0.020278005 }, { 0.0068266296, -0.01173749, 0.037482577, -0.0083236299 }, { 0.025699221, -0.03651135, -0.032342446, -0.0059784486 }, { 0.0029540635, -0.0021598269, 0.0028168477, 0.0044577193 }, { 0.0038274002, -0.0050806333, 0.007628551, 0.0027461742 }, { 0.0056567464, 0.006846664, -0.031161558, -0.0040832656 } }, { { 0.025668431, 0.0093723617, 7.4324163e-05, -0.023051436 }, { -0.010148124, 0.0018159908, 0.0072269566, 0.00082671261 }, { 0.0069741056, 0.023493533, 0.028507618, -0.026874125 }, { 0.0083316277, -0.024891629, 0.013623217, 0.0038373532 }, { -0.020992516, 0.070912136, -0.0014634877, -0.015680371 }, { 0.02178962, -0.003772636, -0.024578501, -0.047467019 }, { 0.0028586275, 0.0033445767, 0.0049576063, -0.017365739 }, { 0.0075721122, 0.010652219, -0.024031886, -0.0001146548 }, { 0.016381176, -0.044765924, -0.038036229, -0.014041395 }, { -0.00082564842, 0.00033107944, 0.00073792054, 0.0005712734 }, { 0.0080934887, 0.014534447, -0.0071347609, 0.0085413493 }, { -0.018211778, 0.0064443848, 0.017393403, 0.011490985 }, { -0.071531366, 0.030059694, 0.049103287, 0.0074609412 }, { 0.00770209, -0.017999995, -0.040048679, -0.0029073853 }, { 0.020442166, 0.0019454488, -0.019644905, 0.021793285 }, { 0.035171271, 0.0080192155, -0.023151504, 0.014168348 }, { -0.048901887, -0.0039613606, 0.0021703807, 0.030275152 }, { 0.044666116, -0.029756153, -0.015570779, 0.034470632 }, { -0.0078700362, 0.0037551741, 0.0003070052, -0.0031237403 }, { 0.015288427, -0.01284757, -0.0075319169, 0.026981487 }, { -0.0093872483, 0.013517073, -0.030221944, 0.058356065 }, { 0.0042326205, -0.016381154, 0.021475001, 0.01008732 }, { 0.0034929117, 0.020531314, -0.0085114063, 0.004821913 }, { 0.014314413, 0.01127037, -0.017197896, 0.0046932185 } } }, { { { 0.99591552, 0.99230689, 0.99873374, 0.99387895 }, { 0.96356049, 0.96556546, 0.96964041, 0.96677566 }, { 0.99945097, 0.99930521, 0.99977525, 0.99975808 }, { 0.99900933, 0.99996161, 0.99848418, 0.99879675 }, { 0.99545951, 0.99993863, 0.99032786, 0.9936502 }, { 0.99972964, 0.99989482, 0.99811938, 0.99825798 }, { 0.99999867, 0.99999914, 0.9988702, 0.99980681 }, { 0.99979292, 0.99581299, 0.99736843, 0.99780458 }, { 0.99750292, 0.99805433, 0.99560254, 0.9931383 }, { 0.98142286, 0.98133774, 0.99352772, 0.9929441 }, { 0.99924096, 0.99973689, 0.99916652, 0.99922617 }, { 0.99789446, 0.9974931, 0.99796885, 0.99743448 }, { 0.9976331, 0.99501931, 0.9963379, 0.99287411 }, { 0.99934104, 0.99967648, 0.99984325, 0.99952138 }, { 0.9999147, 0.99999966, 0.99998426, 0.99999714 }, { 0.99825531, 0.99794572, 0.99979313, 0.99901579 }, { 0.99841509, 0.99894779, 0.99643504, 0.99951192 }, { 0.98801309, 0.98864879, 0.99115599, 0.98740957 }, { 0.99998698, 0.99998334, 0.9999824, 0.99983621 }, { 0.99850879, 0.99868574, 0.99803794, 0.99860752 }, { 0.99663402, 0.99573479, 0.99528974, 0.99365325 }, { 0.99938825, 0.99973103, 0.99924472, 0.99943364 }, { 0.99988413, 0.99993848, 0.99989949, 0.99998434 }, { 0.99922338, 0.99887297, 0.998994, 0.9994714 } }, { { -0.0050599833, 0.003362263, 0.0035202243, -0.00056864904 }, { -0.0014675187, -0.0029154981, -0.00077796172, -0.0027392627 }, { -0.0010916411, 0.00078232803, 0.0014339533, -0.0020166729 }, { 0.011183745, 0.008298699, 0.011631254, 0.00030693508 }, { -0.0012964861, -0.00028098882, 0.00098513135, -0.0052243577 }, { 0.0091119501, 0.002780703, 0.011045274, 0.00334383 }, { 4.1103001e-05, 5.5767744e-05, 0.0030605577, 0.0022152241 }, { 0.00085375099, 0.0026952672, 0.0071937971, 0.0056504112 }, { -0.003773118, 0.0047936307, -4.5743022e-05, -0.0038357994 }, { 2.3815581e-05, 0.0002468657, 0.00013492048, -0.00018410816 }, { 0.0070959632, -0.00205589, 0.0056417297, 0.0030702073 }, { 0.010671769, 0.0074346008, 0.0012867659, 0.0075437523 }, { -0.0013037272, -0.0058374269, 0.0025899757, -0.0071565118 }, { 0.0030041304, 0.0018011397, 0.0093160386, 0.0082062863 }, { 0.0053156934, 0.0036543193, 0.0048724246, 0.0035118324 }, { -0.0053866158, 0.0024053442, 0.00052459148, 0.0090970513 }, { 0.011239324, -0.0010327051, -0.00097551594, 0.0044180668 }, { -0.0024379533, -0.0088232426, -0.012355568, -0.0031875953 }, { 0.0026244123, 0.0011858999, 0.0028110843, -0.001005442 }, { 0.0059514328, 0.0018892606, 0.0050231625, 0.0046700575 }, { 0.00050741664, 0.0096547476, -0.00079618251, 0.0024532112 }, { 0.0058717468, -0.0017457656, 0.0080261577, -0.00048009588 }, { 0.0025457914, 0.0016788968, 0.0013982313, 0.00073909928 }, { 0.0075035778, 0.011234409, 0.0079271096, 0.006672353 } }, { { 0.0095152396, 0.0011785006, -0.00081996856, 0.0018904938 }, { -0.0025430397, -0.0010236291, -0.0020168276, -0.0021827861 }, { 0.0036295778, 0.005406882, 0.0040788276, -0.0057729163 }, { -0.00029952998, 0.0024548208, 0.0088548836, 0.0019084209 }, { 0.0034184324, -0.0088925589, 0.00023040452, 0.00017437939 }, { 0.0037804595, 0.012156355, 0.0041276361, 0.012721488 }, { 7.4846461e-05, 0.00010580108, 0.013483417, 0.0024239851 }, { 0.00026411032, -0.00059353627, 0.0093564271, 0.0061507538 }, { 0.0016065383, -0.0027764641, 0.0013620195, 0.0010062065 }, { 9.7127925e-05, 0.00017275393, 1.0814607e-05, -0.00022627793 }, { 0.0048710612, -0.00014794569, 0.0082832436, -0.00072595412 }, { -0.0027392579, 0.0066783951, 0.00087397132, 0.001567366 }, { -0.003378151, 0.0025916338, -0.0025553201, 0.0030152022 }, { 0.0096818399, 0.0012695523, 0.0072489949, 0.016881099 }, { 0.0022796191, 0.0051693266, 0.0023373397, -0.0041448561 }, { -0.0002074582, 0.0035962454, -0.0007460719, 0.0025086317 }, { 0.0035784996, 0.003162753, 0.0022592918, 0.00024595998 }, { -0.0051294944, -0.0041428868, -0.0027597, -0.0039539398 }, { 0.0022410392, 0.00031263884, 0.0016376751, -0.0022787113 }, { 0.0025647038, 0.0074733037, 0.0051722028, 0.0024463612 }, { 0.0011787227, 0.0071159753, 0.0017217143, 0.0062717989 }, { 0.0046836737, 0.0038976423, 0.00062832002, 0.0027638154 }, { 0.0014142926, 0.0024903802, 0.0015757227, 0.0011628587 }, { 0.0016928585, 0.0043828548, 0.001653268, 0.011450696 } } }, { { { 2.8886078, 2.8900127, 2.7925705, 2.7895874 }, { 4.5455217, 4.5284714, 4.7042338, 4.6915273 }, { 0.96672505, 0.99303664, 0.98927606, 1.0351588 }, { 1.2743756, 1.2525364, 0.99649566, 0.94572778 }, { 2.6910679, 2.6922168, 2.8503404, 2.8246076 }, { 1.256075, 1.2325025, 1.5911826, 1.6091223 }, { 1.3601759, 1.3606869, 1.2793533, 1.240925 }, { 2.0291828, 2.0506809, 1.7341658, 1.6555689 }, { 2.6663531, 2.6921882, 3.1290975, 3.11849 }, { 5.3676887, 5.3663279, 5.3848664, 5.3852162 }, { 1.0586431, 1.0865889, 0.8196623, 0.8076665 }, { 1.6967251, 1.7305944, 1.5450413, 1.6347879 }, { 3.0908857, 3.0706775, 3.2974343, 3.3053965 }, { 1.2172073, 1.3839086, 1.5086796, 1.4295506 }, { 0.97676668, 1.0856738, 0.98747912, 1.0385491 }, { 1.5662275, 1.4603538, 1.784278, 1.6575438 }, { 2.1085757, 2.2092885, 2.1410448, 2.1518347 }, { 4.0214776, 4.006424, 3.7686967, 3.7771354 }, { 1.2089239, 1.2116036, 1.1244311, 1.0901017 }, { 1.1827246, 1.1472796, 1.7516784, 1.7833976 }, { 2.2113439, 2.197512, 2.2692963, 2.2787751 }, { 0.98819531, 1.057833, 1.3587301, 1.3890421 }, { 1.208957, 1.2247867, 1.2301205, 1.2325178 }, { 1.0499613, 1.1319197, 1.4067885, 1.3209087 } }, { { -0.002860931, -0.0033581281, -0.0047612075, -0.0030481839 }, { -0.0017370907, -0.0065700936, -0.0011051926, -0.0046915938 }, { -0.0006126207, 0.0010791181, -0.022876686, -0.015937275 }, { -0.010040922, -0.016433531, -0.0044976975, -0.029838315 }, { 0.00056888968, -0.0093450028, -0.00041549218, -0.0069079656 }, { -0.029781683, -0.019722587, 0.019472312, 0.0016798037 }, { -0.0015128736, -0.0012250172, -0.0091568262, -0.0091368119 }, { 0.0010846814, 0.0017189068, 0.012975603, -0.0051530971 }, { -0.026042808, -0.0090684857, -0.0021498742, -0.0032938309 }, { -0.0012792901, -0.0010431731, -0.0021366737, -0.0025526365 }, { -0.03218779, -0.013848893, -0.021872476, -0.029443623 }, { 0.008300061, 0.011951182, -0.011139414, 0.0098292843 }, { -0.0065854884, -0.020955083, -9.3843515e-05, -0.0078425688 }, { -0.054726229, -0.0073673428, -0.019267231, -0.03383648 }, { -0.049769726, 0.0065482059, -0.010189395, -0.0050480393 }, { 0.022565943, -0.020311569, 0.0091512717, -0.015600752 }, { -0.014418429, 0.0060070592, -0.0055296743, -0.003361885 }, { 8.8146509e-05, -0.0082609252, 0.0036746024, 0.0040108321 }, { 0.0010230427, 4.8153189e-06, 0.0052893378, -0.0096303521 }, { 0.0032909351, -0.010982824, 0.003880027, 0.0097699095 }, { -0.006528317, -0.012608887, -0.0057088008, -0.003867806 }, { -0.046599771, -0.024701737, -0.001078321, -0.0041018649 }, { -0.021680777, -0.021120711, 0.0055144734, -0.0031337995 }, { -0.030559213, 0.0089872726, -0.011166202, -0.0077587071 } }, { { -0.0059548858, -0.0040070313, -0.0062572119, -0.0047711065 }, { -0.0031938803, -0.005431389, -0.0026376521, -0.0046119366 }, { 0.0064917253, 0.013030824, -0.027850471, -0.011824849 }, { -0.032644485, -0.025045016, -0.0034396539, -0.039827623 }, { -0.007691681, -0.014095643, -0.0008171964, -0.0051336386 }, { -0.035626586, -0.021424668, 0.00035790929, 0.0099705685 }, { -0.0019006762, -0.0014887089, -0.0050782898, -0.0096835564 }, { -0.00087496879, 0.0052586834, 0.017041675, -0.00046753956 }, { -0.022489507, -0.0084834888, 0.0017184219, -0.0023910992 }, { -0.0010618265, -0.00085888729, -0.0020035777, -0.0024245283 }, { -0.029245834, -0.038977066, -0.013385246, -0.030312138 }, { -0.0028497869, 0.014205986, -0.0125692, 0.0037959624 }, { -0.0086377959, -0.019175965, -0.007684309, -0.005037677 }, { -0.063945685, -0.0060751259, -0.0057457302, -0.019079575 }, { -0.043745147, 0.013651906, -0.034067394, 0.0012111497 }, { 0.0086647574, -0.019171418, 0.020745219, -0.0055629951 }, { -0.024541273, 0.0072112135, -0.0078821942, -0.0085072621 }, { -0.0018227939, -0.0021153099, 0.008577002, 0.0043865151 }, { -0.013984752, -0.012209334, 0.00023638151, -0.0085025952 }, { -0.0099800075, -0.0095390578, 0.0081328135, 0.012673433 }, { -0.0099975551, -0.0028467616, -0.010712056, -0.0045012212 }, { -0.011329139, -0.0084709831, -0.0070232966, 0.0015504012 }, { -0.015334801, -0.0075637633, -0.01107439, -0.0094188163 }, { -0.017505269, -0.00013701888, -0.033955823, -0.034192649 } } }, { { { 0.16413327, 0.084074422, 0.10646123, 0.18806073 }, { 0.039511019, 0.058967072, 0.035166958, 0.052296507 }, { 0.26970995, 0.21576211, 0.2954278, 0.29870678 }, { 0.40442043, 0.38744132, 0.14502571, 0.24076804 }, { 0.22655046, 0.20912486, 0.015295019, 0.16442957 }, { 0.69235319, 0.6080183, 0.36756076, 0.23314717 }, { 0.085565328, 0.075535626, 0.22162979, 0.33140596 }, { 0.16109547, 0.11961895, 0.26619212, 0.25941009 }, { 0.27077686, 0.23481238, 0.063446408, 0.11614487 }, { 0.026116057, 0.027491327, 0.030421883, 0.039965345 }, { 0.33922592, 0.38039792, 0.27167385, 0.31510976 }, { 0.32744968, 0.22567102, 0.23116584, 0.18867836 }, { 0.29783431, 0.28054079, 0.26752139, 0.23889932 }, { 0.61721263, 0.60602797, 0.51283622, 0.47601102 }, { 0.51383952, 0.53111455, 0.44519064, 0.42875877 }, { 0.3485879, 0.35374178, 0.53292055, 0.53995494 }, { 0.4366997, 0.35554257, 0.14878367, 0.22083288 }, { 0.12855375, 0.16718264, 0.17583661, 0.11125895 }, { 0.35898096, 0.37222307, 0.35439108, 0.35956111 }, { 0.16773044, 0.25668894, 0.23246756, 0.1506316 }, { 0.36172813, 0.26938211, 0.20069185, 0.1714591 }, { 0.3998571, 0.23607244, 0.34121623, 0.29126696 }, { 0.31471307, 0.29500525, 0.39451396, 0.40013999 }, { 0.29554399, 0.28083636, 0.47190649, 0.47892938 } }, { { 0.01419653, -0.061214452, -0.032506906, 0.0078227125 }, { -0.015799432, 0.0136148, -0.0090824684, 0.013638505 }, { 0.023848919, 0.022034707, 0.022812846, 0.022790329 }, { -0.0026324255, -0.0053566952, 0.00027470228, 0.050203583 }, { 0.0035659857, -0.02015272, -0.039043616, 0.054511651 }, { 0.0052075445, 0.0051043119, -0.011801097, -0.0074336577 }, { 0.020735195, 0.01811747, 0.00808952, 0.01140964 }, { -0.0073139049, 0.011075347, 0.0057685988, 0.010251582 }, { 0.024813488, -0.01629986, -0.012536791, -0.01110061 }, { -0.014508648, -0.021444084, -0.023836972, -0.014258253 }, { 0.0079687141, -0.00092011446, 0.060249601, 0.033199468 }, { -0.020822483, -0.013924875, -0.005068391, -0.016928794 }, { -0.030059, -0.013887475, -0.045329289, -0.04449219 }, { 0.007264541, 0.0015213919, -0.0066322618, -0.0036449174 }, { 0.0057175046, 0.0012159867, -0.00054271896, 0.0020625484 }, { 0.0027083179, -0.0012554897, -0.0044854592, -0.0045242423 }, { -0.017906563, -0.028301884, -0.010139427, 0.0035851304 }, { -0.020245794, 0.01149232, 0.011320484, -0.013561794 }, { 0.0068048997, 0.011957759, 0.0046962412, -0.0015476541 }, { -0.0022514613, 0.019996868, 0.0051520398, -0.023405604 }, { 0.0055213198, 0.0070384134, 0.024405643, -0.02050399 }, { 0.039987541, 0.021127504, -0.012323503, -0.0041538161 }, { 0.0072321478, 0.0053097351, 0.0039966161, 0.013617175 }, { 0.030470642, 0.0044694115, -0.0024591651, -0.0027274707 } }, { { -0.040500402, -0.039657034, -0.017497359, -0.017857145 }, { -0.0015646885, -0.020957371, -0.0057356498, -0.0060587007 }, { 0.0070388709, -0.013205178, -0.00033412934, 0.02192306 }, { -0.0042317723, 0.020620857, -0.012309167, 0.065948811 }, { -0.016686589, 0.013616667, 0.030139062, -0.019023551 }, { 0.015181564, 0.008673659, -0.0014559576, -0.025916054 }, { 0.031630671, 0.027030197, -0.026982415, 0.025214731 }, { -0.003845127, -0.00062884599, -0.029488655, -0.0051457939 }, { -0.0032476351, 0.0021153707, -0.033110808, -0.033629213 }, { -0.0064637077, -0.010805748, -0.014982403, -0.0084641529 }, { 0.0087766042, 0.017780238, 0.026838871, 0.032580257 }, { 0.0010700985, -0.037414784, -0.0053773565, 0.0040969752 }, { -0.02637392, -0.050236074, -0.048422986, -0.069357813 }, { -0.0089483588, 0.0026259727, 0.0040142797, -0.010752754 }, { -0.0025658872, 0.0071106029, 0.015467367, 0.0012536589 }, { -0.0037247444, -0.0036991733, -0.015429566, -0.016148852 }, { -0.024788221, -0.045938054, -0.028679471, 0.011593494 }, { -0.032699114, -0.036800967, -0.033870575, -0.031842203 }, { 0.018156047, 0.02457546, 0.0209432, 0.015057433 }, { 0.0043152638, 0.025831372, -0.019608349, -0.026614397 }, { -0.0057047815, -0.013831909, 0.027613211, -0.043616864 }, { 0.014124478, -0.010786326, 0.010775415, -0.023241344 }, { 0.018337827, 0.0048735321, 0.018371717, 0.022106807 }, { 0.013619207, 0.022051384, 0.0082720974, -0.0030262071 } } } }, { { { { 0.083322661, 0.079807165, 0.03660117, -0.051657142 }, { -0.099216074, -0.0080141573, 0.10637241, 0.0367403 }, { 0.20813681, -0.0001361621, -0.20762563, -0.085913357 }, { -0.22091149, 0.10003156, -0.16122219, 0.31542901 }, { 0.16226908, 0.02665194, -0.012123307, -0.16559939 }, { -0.14025496, 0.025804505, 0.076174345, 0.20548591 }, { 0.0035713609, -0.0092551928, -0.099937652, 0.0038879391 }, { 0.12405732, -0.0053373497, -0.030865175, -0.060934551 }, { -0.0060175826, -0.026583926, -0.075326797, -0.0063155886 }, { 0.036389362, 0.054175433, 0.06490927, -0.038784258 }, { 0.30604876, -0.030813476, 0.011402956, -0.21074796 }, { -0.31769497, 0.046793931, -0.038212559, 0.21137297 }, { 0.12952945, 0.20720126, 0.08525845, -0.14568109 }, { -0.09735197, -0.17799099, -0.12256082, 0.038889119 }, { 0.002114572, 0.026037779, -0.0036772795, 0.13478173 }, { 0.094577863, 0.0057382415, -0.087017736, -0.059444148 }, { 0.054953104, 0.071323301, 0.097417831, 8.3254475e-05 }, { -0.11005534, 0.027214076, 0.0059378205, 0.02443999 }, { 0.27096654, 0.1864966, 0.034810947, -0.25886676 }, { -0.35626794, 0.037256657, -0.17795321, 0.52988269 }, { 0.14913899, -0.0086988732, -0.028760192, -0.21779266 }, { -0.16010301, -0.17699785, 0.017269826, 0.17878541 }, { -0.0049504093, -0.02387924, -0.04034852, -0.060461173 }, { 0.10405347, 0.0072745723, -0.10244372, -0.072981984 } }, { { 0.019363393, 5.327311e-05, 0.0075925373, 0.0019542034 }, { -0.051707557, 0.06554253, 0.0050626046, -0.0061857803 }, { 0.022891698, 0.014872273, -0.020436928, 0.0069081531 }, { -0.044566611, 0.019854557, 0.023600607, -0.0055387351 }, { 0.02283957, -0.067086756, 0.088865856, -0.033915007 }, { 0.0020254431, -0.16422426, 0.032495902, 0.012460808 }, { -0.017316175, 0.023440087, 0.011459595, 0.0043887872 }, { 0.027714908, -0.06907548, 0.013578806, -0.009848884 }, { 0.0044782488, 0.0079432606, 0.010143137, 0.023589488 }, { 0.014325082, 0.0075465848, -0.0079373813, -0.0056032635 }, { 0.025123579, 0.01904807, -0.0092328848, -0.019002052 }, { -0.02633985, -0.019560519, -0.065544737, 0.0073352606 }, { 0.044308433, -0.0032233834, 0.01324206, -0.00047128106 }, { -0.076577611, -0.021853603, -0.020190543, 0.0026420865 }, { -0.0029799448, -0.0083566545, 0.14896601, 0.0078617095 }, { 0.021033237, -0.08234711, -0.020642328, -0.0089829962 }, { 0.043793881, 0.0096494147, 0.035831274, -0.01294602 }, { -0.014064874, 0.066144489, 0.0143429, 0.015113964 }, { 0.043111732, 0.0029232804, -0.016912145, 0.012142059 }, { 0.0014186333, -0.0078590166, 0.065781153, -0.038375123 }, { 0.02255714, -0.030191796, -0.078373164, -0.0017593196 }, { -0.033878798, 0.016266579, 0.013539653, 0.043519216 }, { 0.019046482, 0.0080403173, -0.0010755939, 0.03305222 }, { 0.023206448, -0.054323067, -0.035173093, -0.010873592 } }, { { 0.014068291, -0.026418786, 0.016375695, 0.0048801469 }, { 0.024404214, 0.0073572002, -0.027247654, 0.00093849398 }, { 0.012741523, -0.012913063, 0.0054881373, -0.021780769 }, { -0.020497215, 0.057437717, 0.0031122704, 0.014713732 }, { 0.012765254, -0.052846334, 0.048042201, 0.0016578534 }, { 0.031245254, -0.0469321, -0.057199738, 0.012436479 }, { -0.0022837759, 0.0068501747, 0.010541107, -0.0005227683 }, { -0.0187059, 0.0025631581, -0.0082184266, 0.0026294483 }, { 0.0053899388, -0.0199458, 0.0023448066, 0.016215236 }, { 0.021117204, 0.010868775, -0.016412681, -0.016399297 }, { -0.0026199223, -0.011436548, 0.0031355049, 0.011933919 }, { 0.017940023, 0.090292392, -0.061029038, 0.016388845 }, { 0.0074493061, -0.045849358, -0.082612855, 0.025851315 }, { 0.061276666, -0.024654813, 0.035447334, -0.025952766 }, { -0.0068267167, -0.02207426, 0.003724368, 0.0070458116 }, { 0.021714649, -0.017552721, -0.037105408, 0.024398534 }, { 0.0092901891, -0.021559075, 0.009034776, -0.016574279 }, { -0.017218595, -0.041930302, 0.003369899, 0.017959363 }, { -0.0022510875, 0.028106616, -0.042936548, -0.041948028 }, { -0.017145551, -0.032331654, 0.021486923, -0.020295391 }, { -0.023196465, -0.088353584, 0.010086154, 0.018689553 }, { -0.024508386, -0.00058959302, -0.02867958, 0.019018994 }, { 0.0088748911, 0.012528454, -0.016636351, 0.0078166115 }, { 0.00066772723, 0.001693912, 0.032066885, 0.016951148 } } }, { { { 0.015200105, 0.071414961, -0.020616434, 0.0063982643 }, { -0.084578144, -0.12318522, -0.035470756, 0.057833574 }, { 0.19487946, 0.44043059, 0.10981527, -0.31907303 }, { -0.17774238, -0.30460726, -0.53133003, 0.31186606 }, { -0.1172677, 0.3183613, 0.10375266, -0.066515168 }, { 0.054176263, -0.12382077, -0.033807438, 0.039809238 }, { -5.3634009e-05, 0.004084452, 0.005103199, -0.060697866 }, { 0.06093199, 0.060355274, 0.049176467, -0.060579228 }, { 0.054611799, 9.0520863e-05, -0.048891261, -0.047609349 }, { -0.036428706, 0.06336736, 0.0020843807, 0.033254378 }, { 0.26975732, 0.51328693, 0.29976157, 0.049031141 }, { -0.28383516, -0.48219276, -0.27898799, -0.033028759 }, { -0.078976834, 0.14077934, 0.098587186, 0.051336328 }, { 0.076281206, -0.074223398, -0.053178835, -0.099578331 }, { -0.056377095, -0.00066113896, -0.11597726, 0.058805777 }, { -0.0027130032, 0.12007881, 0.0081935835, -0.10415807 }, { -0.019349408, 0.06206561, -0.0079099126, 0.079363093 }, { -0.059959607, -0.0591041, -0.047505451, -0.0031496967 }, { -0.11419194, 0.20904287, 0.53960104, 0.10467592 }, { -0.21312862, -0.34770872, -0.54593093, 0.23230512 }, { -0.073229448, 0.12913, 0.27728133, -0.050627706 }, { 0.082312471, -0.24529296, -0.12381516, 0.05577292 }, { 0.03015389, -0.0015805638, 0.024306632, -0.080697961 }, { 0.061367564, 0.056058289, 0.041197211, -0.015551356 } }, { { -0.029269776, -0.030251548, 0.01352869, 0.0084860712 }, { 0.053983187, 0.047657625, -0.026379004, 0.022474039 }, { 0.011898439, 0.045120742, -0.024430477, -0.081318878 }, { -0.0012641508, -0.018495044, -0.030127865, -0.0088483264 }, { 0.040728292, 0.010691761, -0.023566342, 0.028045232 }, { 0.014593998, 0.0047006468, -0.049032498, -0.011446808 }, { 0.00045433705, -0.0030610749, -0.010359449, -0.0026455857 }, { -0.0026794352, -0.032142744, 0.010153936, -0.0034586152 }, { 0.0097198782, 0.0051005644, 0.03482872, -0.0043676475 }, { -0.0012381415, -0.025746274, -0.0081178021, 0.0041481596 }, { -0.01598781, 0.0048815642, 0.06313106, -0.0062291669 }, { 0.072970618, -0.041153529, -0.007457013, 0.059776924 }, { 0.0024768493, 0.0093018711, 0.024827984, 0.043842172 }, { -0.012927661, -0.023256709, -0.0035951539, -0.069710027 }, { 0.0064149713, 0.0019783425, 0.010135188, 0.019449636 }, { -0.0071551675, 0.015761815, 0.0086309278, 0.038854386 }, { 0.020978109, -0.0056696814, 0.0025526797, -0.017352926 }, { -0.010711116, -0.0097050903, 0.0022304504, -0.0039308489 }, { 0.036904234, 0.025927127, 0.028330671, 0.051193417 }, { -0.00076391153, -0.077528792, -0.029763477, 0.0033945843 }, { -0.01775202, 0.034507636, 0.065392848, -0.017840909 }, { -0.019567742, -0.019880035, 0.055214211, -0.02206159 }, { 0.01110111, 0.0022938832, -0.011417507, 0.017692635 }, { 0.050208493, -0.028178909, 0.0065276591, -0.0056267473 } }, { { 0.0065622702, -0.0012303136, -0.0081183663, 0.00079383048 }, { 0.030775912, 0.052260356, -0.019758331, -0.020044147 }, { 0.019016537, -0.043070451, 0.035298744, -0.040592775 }, { 0.010468089, 0.00057085185, 0.0081761984, 0.0033382478 }, { 0.047189462, -0.052695409, 0.021849623, 0.033585939 }, { 0.0012065616, -0.050287476, -0.065085924, -0.039012886 }, { -0.012294892, 0.006839242, 0.0051165438, -2.0711078e-05 }, { -0.03292822, 0.015299577, 0.0029119931, 0.0073040242 }, { -0.0086784873, 0.0085910164, -0.0059378411, -0.010259049 }, { -0.014191355, -0.011172486, -0.01299927, 0.015386671 }, { 0.040453224, -0.041489173, 0.015047889, 0.064340197 }, { -0.020000046, 0.058477092, -0.0018150465, 0.048536972 }, { -0.006105982, 0.03437044, 0.0087640339, 0.032868283 }, { -0.027120362, 0.016579996, -0.01708524, 0.011178424 }, { 0.030535528, 0.0058718219, -0.031240404, 0.024241052 }, { 0.003729958, -0.055735848, -0.0055392842, 0.03447519 }, { -0.04084502, -0.01227488, 0.0062970198, -0.021996031 }, { 0.053671675, -0.067787009, 0.0053426012, -0.0080796738 }, { -0.021911856, 0.038395527, -0.07713235, 0.024805484 }, { -0.0034319194, 0.0052741327, 0.026402991, 0.0012916612 }, { -0.033119652, -0.0046506889, 0.045613946, -0.050230593 }, { -0.0054612035, -0.033482221, 0.084267507, -0.0224334 }, { -0.0063348693, -0.0074524817, -0.0029629355, 0.035493958 }, { -0.0073519185, 0.045139911, 0.0022901735, -0.041385515 } } }, { { { 0.99640669, 0.99424882, 0.99911727, 0.99864438 }, { 0.99146493, 0.99235134, 0.99369348, 0.99764995 }, { 0.95848895, 0.89778665, 0.9720248, 0.943828 }, { 0.95896077, 0.9472107, 0.83168251, 0.89623886 }, { 0.97975356, 0.94759472, 0.99452924, 0.98394744 }, { 0.98863213, 0.99196902, 0.99652121, 0.97785007 }, { 0.99999362, 0.99994883, 0.99498061, 0.99814861 }, { 0.99040248, 0.99816269, 0.99831309, 0.99630173 }, { 0.99848953, 0.99964658, 0.9959596, 0.99884607 }, { 0.9986735, 0.99651874, 0.99788899, 0.99869411 }, { 0.91299789, 0.85766372, 0.953946, 0.97631002 }, { 0.90471405, 0.87481454, 0.959534, 0.97684726 }, { 0.9884254, 0.96811612, 0.9914694, 0.98799879 }, { 0.99232241, 0.98122887, 0.99103524, 0.99426948 }, { 0.99840731, 0.99966074, 0.99324506, 0.98912879 }, { 0.99551377, 0.99274778, 0.99617307, 0.9927827 }, { 0.99830144, 0.99552039, 0.99521214, 0.99684577 }, { 0.99211525, 0.9978808, 0.99885333, 0.99969634 }, { 0.95579147, 0.95995838, 0.84120087, 0.96022443 }, { 0.90975235, 0.9368621, 0.81871367, 0.8156339 }, { 0.98610091, 0.99158952, 0.96035822, 0.97468107 }, { 0.98366238, 0.9531543, 0.99215501, 0.98230604 }, { 0.99953301, 0.9997136, 0.99888998, 0.99490315 }, { 0.99267663, 0.998401, 0.99388534, 0.99721201 } }, { { -0.0021537732, 0.010607958, -0.0066166595, -0.0027390442 }, { -0.0069401807, 0.0053215201, 0.0062121114, 0.013403291 }, { -0.0035740125, -0.021839368, 0.00042431197, -0.029478899 }, { -0.007886159, -0.0087705321, -0.010570968, 0.0040635318 }, { -0.0021772698, 0.00025306776, -0.0092725896, -0.0075657706 }, { -0.010438319, -0.0072866821, 0.009272756, 0.0043932916 }, { -0.00058203184, 0.0081284104, 0.027749999, 0.0035426599 }, { -0.003604276, -0.012244348, 0.0072177908, 0.0026686264 }, { 0.011192179, 0.0069527119, 0.017278396, -0.0053058312 }, { -0.020276487, -0.0063228657, 0.013968347, -0.0021534789 }, { -0.0037534313, 0.00061399133, -0.02126817, 0.0085256452 }, { 0.015620795, -0.022637876, 0.00069280338, 0.0054369037 }, { 0.0095244184, -0.0026896982, -0.0057963534, 0.0067237437 }, { -0.0085689961, -0.004816024, -0.00088793436, -0.0034021999 }, { 0.015428153, 0.019777562, -0.011217833, 0.0095744159 }, { -0.003802304, 0.0022643577, 0.0054254827, 0.025560756 }, { -0.0053298651, 0.021621993, -0.01864184, 0.019120967 }, { 0.015380344, -0.0027384467, 0.0010235928, 0.0062792725 }, { -0.001166873, -0.0049586656, -0.014850883, 0.00057841904 }, { 0.0032865456, -0.033386196, 0.0032068954, 0.02854738 }, { 0.010308266, -0.000233004, -0.020287643, 0.0044441043 }, { -0.0040523345, 0.0050367711, 0.01627907, -0.010032412 }, { 0.0073463987, 0.00073274858, 0.002814661, 0.030221018 }, { 0.0057509063, -0.011441338, 0.01894259, 0.0077856453 } }, { { -0.0053054924, 0.0037677068, 0.0066263851, 0.0011220287 }, { -0.02212139, 0.013769097, -0.0013834097, 0.014152363 }, { -0.0008493126, 0.021473024, -0.0039313241, -0.017764981 }, { -0.00081897848, -0.0074161164, 0.0038179092, -0.0035760615 }, { 0.014045643, 0.015317904, 0.0045966739, 0.0075917156 }, { 0.0035574126, -0.00017773424, -0.0010937491, -0.0017762282 }, { 0.0072018344, 0.012586227, 0.0138702, -0.0085424173 }, { -0.0055783456, -0.019909385, 0.01190919, -0.0065821489 }, { 1.7402026e-05, 0.0094513341, 0.015333305, -0.0072158969 }, { -0.0063049905, 0.0021776758, 0.014376378, 0.0072426401 }, { -0.0078049673, 0.028764242, -0.0024169449, 0.0077604105 }, { 0.00047536469, 0.029806623, 0.0017798261, 0.00087410198 }, { -0.0030498401, 0.0044874501, 0.0020382571, -0.0011101062 }, { -0.0057084397, -0.0013428994, -0.001024136, 0.0066188614 }, { 0.039201052, 0.015120258, -0.0082642793, 0.0051985023 }, { -0.0091203243, 0.020790215, 0.0025270937, 0.020092044 }, { -0.0029830063, 0.006602841, -0.00833601, 0.044852353 }, { 0.025206353, -0.0038915173, 0.00045914851, 0.0037840538 }, { 0.0014814254, -0.011573911, 0.046232337, -0.015228958 }, { -0.0071984443, 0.0090004063, 0.022942838, 0.016019787 }, { 0.0050929336, 0.0060892107, -0.0061771339, 0.0047850766 }, { -0.011634853, 0.0010276548, 0.022396644, -0.0021248711 }, { -0.012943002, 0.0016430074, 0.02034928, 0.024289705 }, { 0.0051047037, 0.010052556, 0.0020923265, -0.019043181 } } }, { { { 2.1627647, 2.1788232, 1.9290264, 1.8457806 }, { 2.526488, 2.3020441, 2.538915, 2.03484 }, { 3.9987521, 4.3952121, 3.906821, 4.1693278 }, { 4.0400466, 4.1069844, 5.2512999, 5.4283264 }, { 3.0141968, 3.3306035, 3.2224806, 3.2473051 }, { 2.9840674, 3.1294685, 3.2964833, 3.2929246 }, { 1.8346741, 1.8637353, 2.3037966, 2.0860888 }, { 2.691236, 2.6068079, 1.9349032, 2.1632935 }, { 1.9231956, 1.7251627, 2.1609654, 2.1155629 }, { 2.165771, 2.1908952, 1.777038, 2.0223741 }, { 4.5166991, 4.8674508, 3.918546, 3.378087 }, { 4.4502295, 4.5429338, 3.9552598, 3.3580272 }, { 3.0973598, 3.3953852, 2.2704362, 2.6488177 }, { 3.2110537, 3.3104376, 2.515002, 2.3267785 }, { 1.8303675, 1.7094345, 3.1787979, 2.5960104 }, { 2.4391795, 2.8730077, 2.3730261, 2.1545299 }, { 2.2130903, 2.1899209, 2.4997355, 1.9058674 }, { 2.6472893, 2.5455636, 2.1164596, 1.8341163 }, { 3.9428283, 4.0433678, 4.5430063, 4.2482776 }, { 4.1941673, 4.28852, 4.64044, 4.6644567 }, { 3.0873642, 2.649364, 3.6026133, 3.2426354 }, { 3.2415154, 3.5406745, 3.2976852, 3.3100246 }, { 1.8400289, 1.8404692, 1.889289, 2.0125184 }, { 2.7063995, 2.7229173, 2.6289878, 2.4313709 } }, { { -0.015335928, -0.043382119, -0.0054163805, -0.028249934 }, { -0.017200109, 0.0027582413, -0.079612821, -0.0013966663 }, { -0.027233584, -0.018783395, -0.01183278, -0.020918937 }, { -0.0036358348, -0.015712206, -0.0089146421, -0.0057117233 }, { 0.020392865, 0.017743746, -0.068597326, -0.030425581 }, { -0.041123673, -0.020767538, -0.0087941887, -0.0065248183 }, { -0.0055478408, -0.00082196865, 0.0088521402, -0.045916836 }, { -0.010506485, 0.0078523247, -0.030002306, -0.0015085765 }, { 0.01894068, -0.012424968, -0.034837214, -0.045009941 }, { -0.045299587, 0.02630478, -0.017175711, -0.043601235 }, { -0.046003661, -0.020588165, 0.034398873, -0.054653787 }, { -0.0042534368, 0.01325834, -0.0036369576, -0.079162988 }, { -0.028728556, 0.0051289128, 0.012104313, 0.010686997 }, { -0.066337767, 0.00059928728, -0.080303668, 0.011318772 }, { -0.031879871, 0.0011317962, -0.050259029, 0.0031596552 }, { -0.090121238, -0.011196084, -0.072456123, -0.00079731072 }, { -0.024243475, 0.021401076, -0.018209385, -0.0083196072 }, { -0.079888701, 0.0032806631, -0.12762259, -0.04652308 }, { 0.031806075, -0.034165157, -0.015255921, -0.049164663 }, { -0.0012051123, 0.030788487, 0.022291919, 0.0025694519 }, { 0.035836509, 0.0055365388, 0.026704836, 0.0001547235 }, { -0.012129747, -0.0094322145, -0.040637935, -0.12125388 }, { -0.027044986, 0.04531553, -0.033484589, -0.0059927923 }, { 0.0067188802, -0.051166351, -0.048822794, -0.025926988 } }, { { 0.022049053, 0.021265778, -0.040370641, -0.036232952 }, { -0.0058098424, -0.0042264198, -0.077428509, -0.04241654 }, { -0.0026825379, -0.029453318, -0.016181275, -0.028320229 }, { -0.012541692, -0.01345735, 0.00037814888, -0.0046052489 }, { -0.026527394, 0.020033638, -0.025683861, -0.084207169 }, { -0.0010459945, -0.036745215, -0.039772051, 0.024810839 }, { 0.012134618, 0.0068515798, -0.035286972, 0.043129595 }, { -0.077093357, -0.026872688, 0.032800133, -0.090326706 }, { 0.13930909, 0.0081274014, -0.08349188, -0.012200005 }, { -0.091693797, -0.012567011, -0.069736822, -0.0061444184 }, { -0.053061301, 0.003642159, 0.0052515175, -0.036957472 }, { 0.0043493933, -0.013069332, -0.014708126, -0.032765039 }, { -0.016116105, -0.022907609, -0.043503106, -0.013266465 }, { -0.072759977, -0.077354585, 0.0043827591, -0.013821612 }, { -0.032399073, -0.045305037, -0.021840791, 0.073996542 }, { -0.057239255, -0.056581235, -0.038880927, 0.044102943 }, { -0.026951489, -0.088667645, -0.013659704, 0.033527579 }, { 0.034815442, -0.028634059, -0.036666529, 0.011546036 }, { 0.026688447, -0.0081892129, -0.031138092, -0.041739155 }, { 0.0015665701, -0.012701682, 0.0013533943, -0.002849785 }, { 0.032994636, 0.008802974, 0.019032649, 0.0039042621 }, { -0.044544917, 0.0093201326, -0.017968915, 0.01936344 }, { -0.034794535, 0.043032983, -0.051072531, -0.040148303 }, { -0.0030398597, -0.027112065, -0.064007483, -0.01798277 } } }, { { { 0.22040906, 0.24911942, 0.41660708, 0.23632869 }, { 0.25894466, 0.1416669, 0.41902981, 0.35717608 }, { 0.26918091, 0.14566759, 0.2147652, 0.15769391 }, { 0.22500921, 0.12113361, 0.11151768, 0.12348609 }, { 0.25699055, 0.056819107, 0.3859882, 0.4585378 }, { 0.7304995, 0.20719358, 0.44455636, 0.42226989 }, { 0.43602897, 0.51049581, 0.41978824, 0.62521039 }, { 0.42004119, 0.52912054, 0.33314238, 0.38257921 }, { 0.55092562, 0.43085653, 0.31149977, 0.34391138 }, { 0.40391149, 0.48820255, 0.13569806, 0.36060266 }, { 0.13647907, 0.12061002, 0.20668806, 0.30221394 }, { 0.15583476, 0.13133696, 0.22775202, 0.35653823 }, { 0.56336195, 0.25684627, 0.11118383, 0.23109245 }, { 0.45430401, 0.42843367, 0.25496534, 0.097473509 }, { 0.3420223, 0.39418925, 0.26458947, 0.30588082 }, { 0.51345558, 0.3612731, 0.41151773, 0.25269512 }, { 0.29195176, 0.42659964, 0.47971993, 0.32714756 }, { 0.49222777, 0.28477645, 0.74993827, 0.43781271 }, { 0.098434481, 0.31164923, 0.14486345, 0.11466693 }, { 0.070833248, 0.20569754, 0.10233576, 0.047352701 }, { 0.51050902, 0.15597643, 0.1417112, 0.35581415 }, { 0.48261165, 0.14592221, 0.62554576, 0.5209765 }, { 0.33562628, 0.39920067, 0.28183433, 0.297464 }, { 0.366851, 0.59278666, 0.59095922, 0.48385165 } }, { { 0.13792051, 0.072076744, 0.094800532, 0.026318377 }, { 0.13607414, -0.061382542, 0.061800151, -0.020060553 }, { 0.028096406, 0.069282616, 0.010195109, -0.010461141 }, { 0.018651237, 0.02642439, 0.0077552848, -0.051151646 }, { 0.098299803, -0.0085081153, -0.011764584, 0.087405711 }, { 0.064082346, -0.04626424, -0.071480607, 0.064447268 }, { 0.022766233, 0.0167542, -0.021285286, -0.071637286 }, { -0.0202445, 0.011692601, 0.048325551, 0.0097755172 }, { -0.027775183, 0.016463115, 0.060050391, -0.034226107 }, { 0.019412547, 0.059977501, -0.0041737169, 0.031539317 }, { 0.013192979, 0.036015595, -0.049943198, 0.014112312 }, { -0.013272349, 0.035821037, -0.060503687, 0.095316821 }, { 0.038338785, -0.059038809, -0.044954172, -0.00051347307 }, { -0.039594082, 0.018205882, 0.13413799, 0.012292954 }, { 0.015177594, -0.0082493854, 0.00029420179, 0.010356248 }, { 0.100271, -0.13623174, 0.1121235, 0.068902399 }, { 0.025189636, 0.0014918434, 0.0088847718, -0.053714493 }, { 0.06487698, -0.097217547, -0.069537353, 0.032490984 }, { -0.030729608, 0.048956315, 0.016036034, 0.022485239 }, { 0.049839618, 0.01148525, -0.021032427, -0.019665817 }, { -0.0037762817, -0.030422275, -0.062343207, 0.057994884 }, { 0.014035184, -0.021387762, -0.080846143, -0.020681511 }, { -0.03594567, 0.026862531, 0.078975557, -0.034056659 }, { -0.014490672, 0.026128902, 0.045617611, 0.090192953 } }, { { 0.011904288, -0.014624471, 0.042023114, 0.019592867 }, { 0.032705848, 0.00038558691, 0.031901745, 0.027208951 }, { -0.044369719, -0.039761364, -0.013366816, -0.019308126 }, { -0.019051023, -0.00015767269, -0.082968285, -0.035266053 }, { -0.004775162, 0.010889271, 0.0089521094, 0.027037104 }, { 0.005616143, -0.00099668486, 0.0068716426, -0.12649184 }, { 0.018531199, 0.023881776, -0.053798787, -0.041912909 }, { -0.0036187094, 0.11590788, 0.025140733, 0.022280209 }, { -0.02994342, -0.026293799, -0.017204658, 0.044901944 }, { 0.079892089, 0.10816526, 0.14667807, 0.027301352 }, { -0.045296738, -0.066748968, -0.0099354431, -0.070369692 }, { -0.08357374, -0.043311901, 0.013163375, -0.0881777 }, { -0.065923811, -0.10382274, 0.090440302, -0.013617198 }, { -0.092578587, -0.010178017, -0.01416593, 0.0432333 }, { 0.055172515, 0.10021805, -0.0062782668, -0.11791805 }, { -0.039684132, -0.08934283, 0.020686084, -0.0013788117 }, { 0.064624676, 0.051773746, 0.0045383964, -0.037696971 }, { -0.066296373, 0.020570689, -0.017742721, -0.022651449 }, { -0.0061572447, -0.094510525, -0.094775804, -0.038022514 }, { 0.0055683313, 0.039513342, -0.096815654, -0.0065483011 }, { -0.03311602, -0.018395457, 0.0028464434, -0.088048272 }, { -0.073106109, -0.055187863, -0.093209932, -0.10155137 }, { 0.042841842, -0.005778703, 0.074069607, -0.025841052 }, { -0.018569637, 0.063144303, 0.02291584, 0.005525742 } } } }, { { { { -0.20809663, -0.18346453, -0.072140694, -0.0078104407 }, { -0.19490097, 0.25712922, 0.37640771, 0.11563399 }, { 0.26894915, -0.33477877, -0.093739129, -0.55078405 }, { -0.65794103, 0.09211629, -0.19166986, 0.5574327 }, { 0.45579532, 0.23202083, 0.19626303, -0.64130523 }, { -0.018763975, -0.24981569, -0.32514026, -0.11121342 }, { 0.22376238, 0.09515938, 0.071728264, -0.02790747 }, { -0.3053338, 0.34023365, 0.099862481, 0.26163964 }, { -0.21722968, -0.094881958, -0.086364431, -0.0081863581 }, { -0.16090709, 0.23527698, 0.28947119, 0.11309742 }, { 0.26447184, -0.33536416, -0.096418234, -0.26201294 }, { -0.56343769, -0.041662822, -0.24873841, 0.67122901 }, { 0.35362642, 0.2577592, 0.2009013, -0.74233681 }, { -0.047956299, -0.54973418, -0.4958485, -0.12453303 }, { 0.06917425, 0.080509853, 0.0090863722, -0.023518805 }, { -0.27000602, 0.083167162, 0.12715558, 0.12397839 }, { -0.11376964, -0.079199259, 0.019676685, -0.0094352472 }, { -0.19185851, 0.22193112, 0.28110877, -0.06422845 }, { 0.084091992, -0.16151548, 0.091400556, -0.28257376 }, { -0.53821376, 0.21718328, -0.2234907, 0.52302804 }, { 0.71322306, 0.042728493, 0.13229522, -0.61892094 }, { 0.15270046, -0.26304886, -0.33110633, -0.052728951 }, { 0.072398971, 0.25829764, 0.25881687, -0.020942042 }, { -0.26788161, 0.055822039, 0.33817103, 0.42061402 } }, { { 0.088248648, 0.091306255, 0.020476927, 0.0030144802 }, { 0.0087376707, 0.043816157, 0.0022807168, 0.016745414 }, { -0.13412414, 0.12686539, 0.060531476, 0.044582027 }, { 0.019204757, -0.0070891897, 0.091194602, 0.065258927 }, { -0.10429513, -0.027665602, -0.064350626, 0.0053147478 }, { 0.069218141, -0.035018324, -0.088257571, 0.019279642 }, { -0.073137338, 0.040764456, -0.022352804, 0.031743288 }, { 0.040325697, -0.12840825, -0.009582113, 0.034509657 }, { 0.081971224, -0.0035223125, -0.051728499, 0.0038899717 }, { 0.050968435, 0.022254651, 0.18781134, -0.032392139 }, { 0.024342518, 0.13929014, -0.019175435, -0.0011608234 }, { -0.0021942487, -0.01251222, 0.024263454, -0.063179344 }, { -0.13071776, -0.059221747, -0.034153238, 0.036561209 }, { 0.054124093, 0.070495803, 0.081441614, 0.051900357 }, { 0.027480327, 0.028940343, -0.01469313, 0.032388411 }, { -0.039696828, -0.0069393798, -0.011361641, 0.035031025 }, { -0.039730763, 0.0085971581, -0.0077461932, -0.040735188 }, { 0.10893368, 0.00014757217, 0.025489178, -0.11388774 }, { -0.0013816669, 0.0031148929, 0.10281666, -0.019860642 }, { -0.065093128, -0.11495815, 0.041783056, -0.091373461 }, { -0.044985581, 0.0012713031, -0.16078032, 0.17303747 }, { -0.038132358, -0.02995975, -0.037612782, 0.012575173 }, { 0.0042976619, 0.027014275, 0.017518808, 0.030405184 }, { -0.0015298607, 0.029297664, -0.1034349, 0.023450502 } }, { { 0.028785558, -0.028708377, -0.010459636, 2.8360915e-05 }, { 0.091634877, 0.021214811, 0.12282079, 0.080617943 }, { -0.29287977, 0.045481846, 0.014712563, 0.057317576 }, { -0.10728772, 0.03268482, 0.015167285, -0.011256231 }, { 0.09337321, 0.037150859, 0.052549202, -0.042671474 }, { -0.0041288689, -0.024299997, -0.11357403, -0.022045772 }, { -0.041469935, -0.0071353646, -0.0086607538, 0.008536762 }, { 0.033629272, -0.0070042955, -0.037864853, -0.0055907778 }, { 0.016404597, -0.0055321059, -0.020989839, -0.013771265 }, { 0.042552435, 0.04428518, 0.0030587466, 0.044894182 }, { -0.027600219, 0.026831779, 0.051120849, -0.032184808 }, { 0.13870554, 0.15273282, 0.049260112, 0.043371121 }, { -0.018453269, -0.18061413, 0.24805649, -0.031741165 }, { -0.085137374, 0.025935867, 0.015978067, 0.067726486 }, { 0.072393868, 0.0050430488, 0.0016664585, 0.0072097064 }, { 0.033840162, 0.082225764, -0.079387016, 0.033165625 }, { 0.033170766, 0.0012231618, -0.066984982, 0.051671704 }, { 0.017894231, -0.012267532, 0.045536123, -0.07327109 }, { 0.0073109731, -0.063797898, -0.13446413, 0.1408986 }, { -0.045702456, -0.1647051, -0.14336468, 0.054543693 }, { 0.0042448876, -0.13234456, 0.092181719, -0.10440841 }, { -0.060020212, -0.011098469, -0.030257182, -0.030922037 }, { -0.018118661, 0.00067983745, -0.0061776598, -0.031721273 }, { -0.019885189, 0.094157888, 0.014017961, -0.051373389 } } }, { { { 0.12415319, -0.13611564, -0.029441661, -0.14143497 }, { -0.26074418, 0.011913326, -0.033328425, 0.43248793 }, { 0.19336432, 0.37269586, 0.36803538, -0.51720719 }, { -0.15185913, -0.47431781, -0.6593667, 0.23163184 }, { 0.18276216, 0.19248743, 0.65453332, 0.54748087 }, { 0.17751443, -0.0020337696, 0.08506463, -0.40147769 }, { -0.11370932, 0.11523476, -0.010573025, 0.082295392 }, { -0.13666335, -0.32747478, -0.16897386, 0.15359006 }, { 0.11716326, -0.12259922, 0.0033396256, -0.13240653 }, { -0.27776876, -0.10222241, -0.039920479, 0.35499708 }, { 0.090003723, 0.3313923, 0.1871549, 0.003163675 }, { -0.51626118, -0.76341562, -0.56326874, 0.20153559 }, { -0.34172723, 0.26975563, 0.67520079, -0.1252004 }, { 0.45758078, -0.19142179, 0.064180031, -0.48748431 }, { -0.12800789, 0.1399912, 0.0077954775, 0.14379741 }, { -0.13042104, -0.45670817, -0.18831095, 0.0032738639 }, { 0.12446807, -0.11504524, -0.027331682, 0.03861758 }, { -0.31337986, -0.11842668, 0.033415325, 0.45344231 }, { 0.11463107, 0.077427841, 0.060880794, -0.069619455 }, { -0.37772106, -0.59628905, -0.65426572, 0.065297039 }, { 0.29532991, 0.75920243, 0.53294265, -0.15002562 }, { 0.3618333, 0.10488387, 0.36007528, -0.30963565 }, { -0.13738196, 0.20795596, 0.029274703, 0.18017599 }, { -0.10290023, -0.48517535, -0.33278584, 0.56477854 } }, { { -0.0047891472, 0.024629901, 0.015256654, -0.0084462001 }, { 0.056227746, -0.048057782, -0.15671312, 0.06418471 }, { -0.070093217, -0.018057199, 0.062026545, -0.051053726 }, { -0.0091221476, 0.0020547295, -0.087729813, -0.10164738 }, { 0.098917091, -0.066835916, 0.083151519, 0.006342544 }, { 0.0013540606, 0.038719082, 0.036333261, -0.053178668 }, { 0.0083787438, 0.0028359378, 0.0089872852, 0.031308249 }, { 0.014379686, -0.079563474, -0.079160006, -0.016352226 }, { 0.0091376645, -0.016678006, -0.044636785, -0.0011035265 }, { 0.0099146109, 0.027589302, -0.09494437, 0.07451767 }, { 0.017453983, 0.080674871, 0.06341808, 0.048820473 }, { 0.02794057, 0.058230195, -0.010793601, 0.091813872 }, { -0.049633232, -0.1142016, 0.036984283, 0.0034294865 }, { 0.047712957, 0.10161366, 0.13774722, 0.039503136 }, { 0.014194782, -0.014555183, -0.00053182909, 0.0019143477 }, { 0.0014900262, 0.0056176356, -0.034517871, -0.0010707988 }, { 0.013287784, -0.0073967933, -0.019271341, 0.016354896 }, { -0.10345626, 0.023536634, 0.027943639, -0.015686972 }, { -0.025193395, -0.10224801, 0.078686884, -0.048574399 }, { 0.15797878, -0.0012322757, -0.036096649, -0.23983963 }, { -0.10455507, -0.056368102, -0.06570944, 0.29104616 }, { 0.05155239, -0.040940824, -0.038367594, 0.058174485 }, { 0.010471732, -0.066952904, -0.047763843, -0.021124742 }, { -0.033555686, 0.0049111983, -0.026592789, 0.014438586 } }, { { -0.0048440946, 0.025915095, -0.018325403, 0.022133613 }, { 0.059240081, -0.031272176, -0.12967647, -0.17957913 }, { 0.0574837, 0.067005152, 0.024644254, 0.10786296 }, { 0.067084865, 0.008513386, 0.04077659, 0.10587924 }, { 0.026332643, 0.1072618, -0.098375042, -0.001724609 }, { -0.021386362, -0.0020174921, 0.16800158, 0.081359882 }, { -0.018204146, -0.026432136, -0.0068153455, -0.029997667 }, { -0.043221501, -0.016869967, -0.067406967, -0.024965804 }, { -0.0033879999, 0.031310818, -0.010853802, 0.00088944004 }, { -0.068991006, 0.087874253, -0.15737392, -0.088870044 }, { 0.061763806, -0.00072874343, -0.009915009, -0.0178225 }, { -0.07340717, 0.080339271, -0.0027124572, -0.13078641 }, { -0.023682834, 0.16512313, -0.15784472, 0.047978827 }, { 0.0063250439, -0.09953777, 0.094180888, 0.010565041 }, { 0.010047311, -0.042999009, -0.012483998, -0.016966759 }, { -0.048612679, 0.051708319, 0.015059148, 0.0036776472 }, { -0.011737015, -0.0027276603, 0.026535075, -0.065453876 }, { 0.056388137, 0.061461073, -0.12726984, -0.025578248 }, { 0.0016833003, 0.10878558, 0.13254828, -0.017098914 }, { -0.031606282, -0.072245098, 0.12724789, -0.21852899 }, { -0.062502612, -0.073402771, -0.049624729, 0.069066032 }, { -0.075837195, -0.10297347, -0.07249237, -0.11538062 }, { -0.015644005, 0.039474396, 0.074415075, -0.038881161 }, { -0.040175911, 0.034030267, 0.03947059, 0.014167463 } } }, { { { 0.97019677, 0.97355703, 0.99695983, 0.98991674 }, { 0.94552952, 0.96630359, 0.92585444, 0.89419404 }, { 0.9435447, 0.86545998, 0.92507456, 0.65508294 }, { 0.73759908, 0.87552111, 0.72697883, 0.79725496 }, { 0.87111918, 0.95347518, 0.73011435, 0.53758004 }, { 0.9839393, 0.96829127, 0.94183216, 0.90909143 }, { 0.96798791, 0.98876976, 0.99736817, 0.99621717 }, { 0.9423876, 0.88147679, 0.98054848, 0.95286662 }, { 0.96906348, 0.98791034, 0.99625801, 0.99116169 }, { 0.94707625, 0.9665378, 0.9563539, 0.9280011 }, { 0.96018435, 0.88187869, 0.97758711, 0.96505917 }, { 0.64499021, 0.64456248, 0.78794513, 0.71332673 }, { 0.87073007, 0.92778882, 0.70974824, 0.65822558 }, { 0.88787388, 0.81311133, 0.86603417, 0.86420517 }, { 0.98935782, 0.98687417, 0.99992833, 0.98932764 }, { 0.95398485, 0.88572054, 0.97384313, 0.99227952 }, { 0.98567955, 0.99019799, 0.99943274, 0.99920952 }, { 0.93004482, 0.96784384, 0.95909399, 0.88896838 }, { 0.98984254, 0.98382807, 0.99395144, 0.95671584 }, { 0.75342733, 0.77283296, 0.72248756, 0.84981055 }, { 0.63568318, 0.6494505, 0.83574524, 0.77099234 }, { 0.91965169, 0.95906448, 0.87218942, 0.94939213 }, { 0.98786871, 0.94341754, 0.96548269, 0.98341143 }, { 0.95794101, 0.87263324, 0.8802806, 0.71000638 } }, { { -0.0064390277, 0.051629953, -0.011423447, 0.032337826 }, { 0.055030538, 0.061305324, -0.016012659, 0.083766345 }, { 0.052467122, 0.018425134, -0.00054737782, 0.048038459 }, { 0.076436505, 0.016815709, -0.024174832, -0.00829119 }, { 0.057903371, 0.068822104, -0.0064003131, 0.00010695928 }, { 0.067104151, 0.067284611, 0.0074295447, 0.024215238 }, { 0.073380541, 0.01486405, 0.01523157, 0.012966612 }, { -0.0002536971, 0.010628632, 0.00045031869, 0.041891438 }, { 0.055922922, 0.0090823157, 0.011101162, 0.033807592 }, { -0.040264953, 0.022318628, -0.013682045, -0.016112502 }, { -0.034286564, 4.7089727e-05, -0.013030079, -0.012231424 }, { 0.027756308, 0.084041595, 0.018308393, 0.11564334 }, { 0.0026690817, 0.058149333, -0.013682964, 0.052975934 }, { -0.03852481, 0.063493354, 0.059460027, 0.047740976 }, { 0.026410264, -0.0073902435, 0.022353771, 0.012987341 }, { 0.035217135, -0.0023455309, -0.0055505614, 0.010102857 }, { 0.00075590283, 0.038624793, -0.0040614962, 0.070039437 }, { -0.02318411, 0.04527054, 0.013119286, 0.025335215 }, { 0.021268391, 0.044855911, 0.012622905, 0.04827088 }, { -0.0046678346, -0.01934799, 0.018393432, 0.09750434 }, { 0.12480373, 0.059151139, 0.055196092, 0.26701338 }, { -0.0096669036, 0.065624767, 0.016918517, 0.028425135 }, { 0.026488514, -0.0037618693, 0.0077028717, 0.041713399 }, { 0.018628451, 0.033145064, 0.029067918, -0.000924258 } }, { { -0.043525781, 0.028119778, -0.011653105, -0.020930158 }, { -0.028099186, 0.017594088, -0.099226445, 0.10408808 }, { 0.11750066, -0.0010629746, 0.018381448, 0.096538552 }, { 0.0010069446, 0.013799541, 0.1325137, 0.020820734 }, { -0.053571928, -0.0066793785, 0.14596488, -0.03272949 }, { 0.028507895, 0.015474376, -0.025411653, 0.037264272 }, { 0.033698911, 0.018088387, 0.0038898537, 0.03163178 }, { 0.0057766828, 0.015879322, 0.012557033, 0.071771631 }, { -0.0044521866, 0.0083963511, -0.0020426175, 0.023784146 }, { -0.011508765, 0.0075020051, 0.0018808294, 0.040843424 }, { 0.0085150894, 0.0056891711, 0.010134672, 0.046224768 }, { 0.040825446, 0.10099754, 0.021853299, 0.024507528 }, { -0.0055958303, -0.0060958, 0.1115321, -0.021701014 }, { 0.010487817, -0.010033143, -0.031203025, 0.054265436 }, { 0.0040500672, 0.0053935875, 0.018233022, 0.018797311 }, { 0.064057639, 0.014318185, 0.0199119, 0.014366235 }, { 0.02411682, 0.045454692, 0.0030084434, 0.019464939 }, { 0.012500289, 0.027734846, 0.0025097372, 0.047343669 }, { 0.037625829, -0.00064472688, 0.0557556, 0.04785655 }, { 0.0020433437, 0.019929208, 0.087936103, -0.036738471 }, { 0.020811556, 0.0915387, 0.055445303, -0.065132763 }, { 0.03911814, 0.043721622, 0.0074336204, -0.031370424 }, { 0.014072509, -0.014795458, 0.010517063, 0.022409628 }, { -0.0054107234, 0.055313602, 0.053556404, 0.048574319 } } }, { { { 3.4224197, 3.3162336, 3.1136621, 3.3189801 }, { 4.0715355, 3.5614196, 4.1797877, 4.0959601 }, { 4.3979407, 4.1858272, 4.3116447, 4.5467451 }, { 4.4920032, 4.0716439, 4.6107962, 4.5268016 }, { 5.6570832, 4.9036495, 4.7373547, 4.7259419 }, { 3.3277827, 3.6015237, 4.226646, 3.7939772 }, { 3.4893058, 3.3260638, 3.0626103, 3.1798705 }, { 3.6423735, 4.1092281, 3.3264203, 3.7325301 }, { 3.4756581, 3.2550256, 3.224671, 3.4093307 }, { 3.8511362, 3.4821381, 4.3232597, 3.7357164 }, { 3.6688024, 4.0797971, 3.4140927, 3.6881261 }, { 4.5298469, 4.7472506, 4.4046473, 4.7279944 }, { 4.1614448, 4.1242955, 4.6741969, 5.0037875 }, { 4.3148703, 4.3815566, 4.1976536, 3.9032858 }, { 3.2640506, 3.3214728, 2.9463564, 3.3562068 }, { 3.6729325, 3.9218642, 3.4550701, 3.4833871 }, { 3.435975, 3.3079446, 3.3432341, 3.3632985 }, { 3.8404619, 3.4716915, 3.858149, 3.8677391 }, { 3.3181827, 3.8403872, 4.0363918, 3.9604287 }, { 5.0916792, 5.2773748, 4.5404255, 4.377031 }, { 4.6514614, 4.7569957, 4.1233238, 4.4022582 }, { 3.6884833, 3.6283543, 4.1874612, 4.2963913 }, { 3.456705, 3.6250566, 3.5292789, 3.1420033 }, { 3.5986317, 4.0596074, 4.0696874, 4.5327067 } }, { { -0.12592901, -0.14780788, -0.11051274, -0.18767653 }, { -0.020435093, 0.0055221209, -0.021183195, -0.15159792 }, { 0.022498629, -0.025100789, -0.30939177, 0.016420202 }, { 0.21296442, -0.042976575, 0.082118132, 0.14574735 }, { -0.13608022, 0.16141834, -0.015091164, 0.044951541 }, { -0.08235774, -0.10333151, 0.089785432, -0.036620639 }, { -0.17664465, -0.015842477, -0.083075331, -0.15660828 }, { -0.11292423, -0.072894494, -0.068901923, -0.2283674 }, { -0.19063437, -0.071954393, 0.091375283, -0.26993547 }, { 0.042798331, -0.06495575, 0.050221766, 0.024602586 }, { -0.026228614, 0.0049810367, 0.046584088, -0.13067577 }, { 0.072779737, -0.023369437, -0.030275791, 0.19591126 }, { -0.018649072, 0.029208952, 0.012033439, 0.00094798196 }, { -0.094599446, 0.0070746366, -0.0007115864, -0.040175552 }, { -0.027599009, -0.068747365, 0.19480498, -0.19423733 }, { -0.076671551, 0.0075475135, 0.019853903, -0.012984601 }, { 0.064371855, -0.24044027, -0.043765356, 0.0016424127 }, { -0.076744435, 0.035881398, 0.12967612, 0.081825243 }, { -0.15224256, 0.032665115, -0.027927205, 0.076091133 }, { -0.0057973613, -0.14914213, -0.047678749, -0.037214457 }, { 0.10060085, -0.099197666, -0.22704457, -0.0020812401 }, { -0.070664558, -0.13179176, -0.014217065, -0.030410253 }, { -0.12286487, -0.046623366, -0.10695394, -0.0081383175 }, { -0.14561788, 0.02765909, 0.10439783, 0.033139041 } }, { { 0.0063171031, -0.0047223477, -0.056312039, -0.065065766 }, { -0.0059575982, -0.062348475, 0.069540315, -0.090331962 }, { 0.10218203, 0.050383376, -0.0089914697, -0.037837343 }, { -0.0037657879, 0.18278082, 0.079014627, -0.052587294 }, { -0.33929282, 0.018522098, 0.0078923893, 0.042545349 }, { 0.027294929, -0.086490439, -0.0057363347, -0.035932082 }, { -0.061716003, -0.14470599, 0.033117786, -0.08112808 }, { 0.16414856, 0.082471596, -0.058497326, 0.050552718 }, { -0.07627083, -0.0064181717, -0.031179581, -0.075705068 }, { -0.057808009, -0.00074561624, -0.23990956, 0.018671772 }, { 0.1677602, 0.10757253, 0.028015134, -0.23923178 }, { 0.078827365, 0.068682485, 0.056277532, -0.069749241 }, { 0.079502977, 0.05526585, 0.0089767144, -0.15319341 }, { -0.038594242, -0.055488998, -0.043132461, 0.054313031 }, { 0.12890592, -0.082639555, 0.22520491, -0.026781096 }, { -0.071292391, 0.064592881, -0.050368563, -0.072488866 }, { 0.092998671, 0.12152394, 0.033318795, -0.039691417 }, { -0.0049706273, -0.0014175115, -0.11634604, 0.15219284 }, { -0.012414906, 0.035583927, -0.072463074, -0.058394705 }, { -0.071558898, -0.00093653835, 0.013149622, 0.01495775 }, { -0.057103279, 0.013702583, -0.020242751, 0.04649072 }, { -0.083398977, -0.20123674, 0.062758815, -0.043671819 }, { 0.084479675, 0.17868517, -0.021185269, 0.15711776 }, { 0.11862504, 0.079985297, 0.063556911, 0.14639069 } } }, { { { 0.48018566, 0.17712962, 0.45065949, 0.76214707 }, { 0.37788335, 0.385421, 0.24766167, 0.3647243 }, { 0.45095873, 0.2634498, 0.37824131, 0.10713483 }, { 0.18808611, 0.27852978, 0.23671202, 0.23174978 }, { 0.39404781, -0.7399413, 0.28511918, 0.026007027 }, { 0.46587668, 0.46802177, 0.36697974, 0.23706778 }, { 0.48925391, 0.42086488, 0.49570155, 0.45137287 }, { 0.30655255, 0.35196398, 0.23019387, 0.50586011 }, { 0.45798975, 0.34137244, 0.33289763, 0.54218519 }, { 0.42271216, 0.38700914, 0.48791862, 0.15025833 }, { 0.7282781, 0.37956244, 0.25156645, 0.51632504 }, { 0.084933462, 0.15576738, 0.16469359, 0.29684651 }, { 0.34570877, 0.34912791, 0.26663435, 0.11188061 }, { 0.48552914, 0.19012867, 0.12677402, 0.1234341 }, { 0.2190939, 0.41431469, 0.64823269, 0.51846746 }, { 0.49289149, 0.29829354, 0.29090992, 0.36465152 }, { 0.50568056, 0.64150077, 0.40217634, 0.53523743 }, { 0.24945735, 0.47058801, 0.29099852, 0.25452114 }, { 0.49039753, 0.26327736, 0.39431507, 0.50632023 }, { 0.19678915, 0.031547614, 0.22295107, 0.26300048 }, { 0.12409997, 0.11506147, 0.19327618, 0.2174585 }, { 0.15319333, 0.39177705, 0.38498586, 0.25972804 }, { 0.69027161, 0.37279682, 0.31143504, 0.23440833 }, { 0.39682066, 0.3156927, 0.36369313, 0.14308402 } }, { { 0.15030994, 0.15410005, 0.0072554408, -0.22242826 }, { -0.032421729, 0.22531436, 0.22185899, -0.022703209 }, { 0.070341052, 0.30237173, 0.047916387, 0.03629681 }, { -0.024283222, 0.075614195, 0.013940033, -0.016841468 }, { 0.077729482, 0.19455394, -0.02162282, -0.018761003 }, { -0.22986895, 0.18914992, 0.14483608, 0.11173921 }, { 0.14132894, -0.0081864768, -0.11405791, 0.031777789 }, { 0.38775389, 0.0085565642, -0.057167843, 0.09784167 }, { 0.079102739, 0.030530894, 0.041954967, 0.02957611 }, { 0.076915126, 0.18656729, 0.044218872, 0.22478833 }, { 0.017173879, 0.11961351, -0.085099523, 0.22720323 }, { 0.030466202, 0.095221887, -0.042982583, -0.069264747 }, { 0.041170442, -0.090598444, -0.021082598, -0.028016784 }, { -0.082581617, -0.023712106, 0.32427665, 0.1010696 }, { 0.19197752, 0.10900527, -0.0053794951, 0.068553764 }, { 0.18674269, 0.028895321, -0.053421028, 0.063918058 }, { 0.044090722, -0.054247791, 0.05585954, -0.13406746 }, { 0.08358642, -0.032301886, 0.010371619, 0.099505528 }, { 0.16467816, 0.044994571, -0.0045949279, 0.0626774 }, { 0.12942209, 0.092097891, 0.019866495, 0.10340014 }, { 0.037094903, 0.13829877, 0.15116473, -0.048632499 }, { 0.10749044, 0.14329542, -0.061272024, -0.1536028 }, { 0.097716907, 0.044246181, 0.056664419, 0.15804873 }, { 0.031819999, 0.10132976, 0.079198524, 0.017871462 } }, { { 0.056219172, 0.08683492, -0.061488015, 0.065746152 }, { 0.088983664, 0.19773741, -0.096766599, 0.16352101 }, { -0.0097043787, -0.040925999, 0.097458334, 0.032319634 }, { -0.024873518, 0.057873123, -0.0059256291, -0.057498398 }, { -0.13355098, 0.39190863, 0.017449142, -0.0076009344 }, { 0.10319658, 0.22069551, -0.098795717, 0.10603434 }, { 0.090765308, 0.13803326, -0.070647945, 0.14557561 }, { -0.068457348, 0.058955208, -0.050501105, 0.02914144 }, { 0.10363866, 0.060231993, 0.027681685, 0.079659088 }, { 0.01269983, 0.11977996, -0.049648315, 0.089882363 }, { -0.072877286, 0.019348792, 0.13977764, 0.055396044 }, { 0.028834456, -0.1084196, -0.0043985215, -0.072640844 }, { -0.040232522, 0.051835989, -0.02198193, 0.016421295 }, { -0.087848469, -0.04621504, 0.099259188, -0.0025909067 }, { 0.3000131, 0.10526775, 0.016890366, 0.12892588 }, { -0.021028821, -0.024429075, 0.088067677, -0.084594075 }, { 0.086861805, -0.045902006, 0.0058222123, -0.0075466204 }, { 0.14411905, 0.036488937, 0.05091815, 0.16385101 }, { 0.1576814, 0.043890956, -0.064244298, -0.087234754 }, { -0.071100004, 0.16782304, -0.10860149, -0.1601076 }, { 0.032634641, -0.0025068263, -0.093802703, -0.076176546 }, { 0.1121451, 0.15584236, 0.070074778, 0.083736091 }, { 0.16981897, -0.078106227, 0.12480295, -0.0056807652 }, { -0.20300117, -0.017467249, 0.035504155, 0.056546123 } } } }, { { { { 0.014994926, 0.3118252, 0.12179235, -0.2013765 }, { -0.2622824, 0.28086607, 0.018805882, 0.72058929 }, { -0.0081002049, -0.28176506, -0.592214, -0.15032918 }, { 0.18913426, -0.24000825, 0.0020279072, -0.54749128 }, { 0.010237954, 0.76905205, 0.80173664, -0.016024595 }, { -0.53448318, 0.31204229, -0.16183732, 0.76857439 }, { -0.57639279, -0.63719194, -0.71354849, 0.56346054 }, { 0.49443258, 0.15067585, 0.31864726, -0.30570933 }, { -0.20756322, 0.2544828, -0.005298245, 0.0073796841 }, { -0.61822672, 0.21508574, 0.6362534, 0.30433278 }, { -0.0050327191, -0.278054, -0.3460806, 0.29967778 }, { 0.33983098, -0.11715664, -0.21761592, -0.068273894 }, { 0.5550354, 0.44369709, 0.64019993, -0.026032291 }, { -0.72587268, -0.33528197, -0.33592445, 0.53027141 }, { -0.47623191, -0.61767624, -0.61525655, 0.37823554 }, { 0.82869964, 0.219401, -0.018181789, -0.56937955 }, { -0.051792934, 0.3461701, 0.20915925, 0.078166496 }, { -0.26705611, 0.14439061, 0.0055054648, 0.463243 }, { -0.0019649711, -0.34119962, -0.29306531, -0.040223173 }, { 0.29285811, -0.32824753, -0.24768208, -0.29676955 }, { 0.87604898, 0.25374435, 0.2341931, -0.77851996 }, { -0.80404697, 0.011122158, 0.18899178, 0.55592668 }, { -0.78397618, -0.53690406, -0.59931185, 0.62348293 }, { 0.54613799, 0.080819658, 0.12590931, -0.60614071 } }, { { -0.12307869, -0.20242175, 0.21530167, -0.15608553 }, { 0.00052208688, 0.09998365, -0.067550225, -0.14009319 }, { 0.12621699, -0.089024022, 0.022656689, 0.18947331 }, { 0.34838897, -0.04936051, 0.25527451, -0.18942819 }, { 0.013210249, -0.043957685, -0.19088103, -0.034189573 }, { -0.0027790938, -0.026595097, 0.087083287, -0.12513839 }, { -0.038231564, 0.013328425, -0.0091503894, -0.005743873 }, { 0.17205702, -0.14956835, -0.0088915291, 0.18720588 }, { -0.049670195, 0.39532325, 0.080260299, 0.01811245 }, { 0.043555003, -0.30289197, -0.50878196, 0.27306166 }, { 0.02555972, -0.0068359476, 0.061097702, -0.43822038 }, { -0.10926471, 0.1870906, 0.12419548, 0.1245213 }, { -0.012443149, 0.040036941, 0.18601483, 0.02310445 }, { -0.10442982, 0.057455632, 0.13475314, -0.0019859122 }, { -0.068181593, -0.0033655904, 0.01922998, -0.020393828 }, { -0.10660626, 0.0020812455, 0.081209707, 0.077131932 }, { 0.088733212, -0.10430986, 0.45554817, -0.17113078 }, { 0.0046831409, 0.13247549, -0.1077727, 0.15382275 }, { 0.022346595, 0.022924261, -0.35016323, 0.2437608 }, { 0.029795657, 0.23046877, -0.020493651, -0.33214749 }, { -0.016101582, 0.042296203, 0.046779444, 0.037412394 }, { -0.02214903, -0.025218605, 0.14797485, -0.051723623 }, { 0.021321783, 0.010405115, 0.0075476201, 0.0082410917 }, { 0.040559796, 0.027927916, -0.012812736, -0.0096642379 } }, { { -0.055647079, 0.017595207, 0.34495838, -0.03055759 }, { -0.058415094, 0.027416036, 0.18568916, 0.13044498 }, { 0.01482217, -0.17300703, 0.027540135, -0.2744944 }, { 0.25558424, -0.15324455, -0.29751197, -0.11422984 }, { -0.068936732, -0.11425403, 0.094767025, -0.0020892558 }, { 0.040887892, 0.031622148, -0.095292456, -0.02460001 }, { -0.0026237665, 0.017734103, 0.01213911, 0.0056586962 }, { -0.052138375, 0.052245567, 0.04608449, -0.043004468 }, { -0.17693366, 0.0021023738, 0.13167397, -0.14062006 }, { -0.20900333, 0.0057695127, 0.13057243, 0.046715668 }, { -0.020569928, -0.08439655, -0.09683347, 0.038139385 }, { 0.18196242, 0.44461908, -0.11388512, -0.12413082 }, { 0.072801844, -0.0017236427, -0.0026756083, 0.049805114 }, { -0.092195952, -0.0076195172, -0.22763849, -0.11320887 }, { 0.016234922, 0.007258942, 0.078535592, -0.084829275 }, { -0.15320003, 0.057490618, -0.16065455, -0.17063675 }, { -0.012856124, 0.024818957, 0.097529739, 0.11569844 }, { -0.11141243, 0.26677735, 0.1319403, -0.15699502 }, { -0.021128161, -0.12370585, 0.056198856, -0.1836225 }, { -0.01871806, 0.025525037, 0.063822152, 0.066517944 }, { -0.013759301, 0.11401068, -0.04701374, -0.021321516 }, { 0.032714649, -3.161284e-06, 0.026930697, 0.00019593482 }, { 0.10575127, 0.016956425, 0.016873291, 0.0049304377 }, { -0.11938883, 0.31242334, 0.29347156, -0.19514533 } } }, { { { -0.17374661, -0.028781395, -0.25993234, 0.27242277 }, { -0.13675759, -0.62291002, -0.80742781, 0.54260546 }, { 0.16876581, -0.052588487, 0.22415557, -0.59669887 }, { 0.1769234, 0.64210979, 0.81157479, -0.2718564 }, { -0.99873125, -0.013258174, 0.58939675, 0.99930085 }, { -0.30883355, -0.71116337, -0.76218623, 0.096388818 }, { 0.65749012, -0.54533843, -0.57508599, -0.70359398 }, { -0.27406769, 0.61006308, 0.1873512, 0.2563151 }, { -0.78453523, -0.13585943, -0.048534939, 0.02085237 }, { 0.40938527, -0.76981396, -0.42506866, 0.22362984 }, { 0.29003079, -0.20624421, 0.1151133, -0.50558933 }, { 0.0070051806, 0.20763719, 0.59485798, -0.61562639 }, { -0.4371111, 0.48314196, 0.72981069, 0.99889301 }, { 0.58257878, -0.8603979, -0.94188892, -0.83140889 }, { 0.71858167, -0.49534538, -0.63421799, -0.84488463 }, { 0.016158248, 0.65330502, 0.82883727, -0.127372 }, { -0.50292264, -0.14848746, -0.20836533, 0.2471481 }, { -0.15815031, -0.63472031, -0.79826416, 0.15325573 }, { -0.010424343, -0.022843894, 0.099730136, -0.26040744 }, { 0.15069433, 0.31188588, 0.63836617, -0.25234477 }, { -0.36946506, 0.92093529, 0.96548808, 0.62354203 }, { -0.57070465, -0.99847512, -0.47855156, -0.079970605 }, { 0.077467525, -0.71134336, -0.67172579, -0.66364974 }, { -0.27299386, 0.89512951, 0.61598356, 0.49577277 } }, { { 0.070458859, -0.28774455, 0.21287043, -0.094689772 }, { 0.0029548085, -0.31404605, -0.039280892, -0.3652277 }, { -0.033729607, 0.041215792, 0.065844258, -0.21509418 }, { 0.39270582, 0.067526811, 0.15655351, 0.053346856 }, { 0.052704394, -0.087801294, 0.18655104, 0.056114808 }, { -0.074582751, -0.055177669, -0.22165519, 0.13272162 }, { -0.027850171, 0.0029849066, -0.0062314784, -0.010484316 }, { 0.20753796, -0.0087111988, -0.13875075, -0.06137521 }, { 0.089744421, 0.07271039, 0.099417029, -0.22157272 }, { -0.013209094, 0.048633419, -0.26528065, -0.15253703 }, { 0.052922007, 0.24859103, 0.14406684, 0.13857649 }, { 0.00096142813, 0.32643367, 0.17939549, -0.39761314 }, { 0.013505803, -0.036986517, -0.12729111, 0.15459921 }, { -0.00049722057, -0.047063275, -0.0018666598, 0.1067114 }, { -0.074221027, -0.00927958, -0.029535811, -0.024240068 }, { -0.12387933, 0.06626829, 0.16422781, 0.077740779 }, { 0.14560404, -0.082132455, 0.027268021, 0.18857832 }, { 0.10470732, -0.29519533, -0.23666419, 0.10917064 }, { 0.042550279, 0.02436036, -0.31865644, -0.024987356 }, { -0.030434576, 0.082115299, 0.17770796, 0.020944092 }, { -0.17365377, 0.13807361, 0.12476029, 0.072738061 }, { -0.11503962, -0.04022554, 0.028018434, -0.070211356 }, { -0.043677907, 0.0053361863, 0.0039019898, 0.0027489647 }, { 0.27060899, -0.0016552279, 0.14166067, -0.25461265 } }, { { 0.014703402, 0.094752279, -0.32162049, 0.082335322 }, { -0.31539882, 0.44394592, 0.44316202, -0.031456167 }, { -0.024148679, 0.082370612, -0.0031744796, 0.098610537 }, { 0.46130367, -0.19989896, -0.56118891, 0.11979937 }, { 0.11784636, 0.079971516, -0.16977121, 0.014922099 }, { 0.018367216, -0.076519762, 0.13801492, 0.039682415 }, { -0.0027614728, 0.0010389006, -0.023126227, 0.0027068473 }, { 0.22249856, -0.071302328, 0.23721977, 0.10734273 }, { 0.41478408, -0.36611101, 0.18031261, -0.11176768 }, { 0.15800457, 0.23829725, -0.0016193556, 0.2112867 }, { -0.14793833, -0.15378785, 0.0082778301, 0.27105519 }, { -0.064743588, 0.44794816, -0.12599819, 0.4310022 }, { 0.092725214, 0.033947737, 0.19969884, 0.0072363359 }, { -0.074190657, 0.005985921, 0.300818, -0.090919095 }, { 0.024238118, -0.010955859, -0.068086841, -0.021137349 }, { 0.12196721, -0.19977338, -0.64428422, -0.30808722 }, { 0.46567096, -0.042072501, -0.1778338, 0.34294059 }, { -0.32528695, 0.25699981, 0.49346557, -0.20743316 }, { 0.10422458, 0.049488574, 0.49098274, -0.34871439 }, { 0.16431875, -0.050748897, -0.18464312, -0.61695364 }, { -0.1753479, 0.033238479, -0.046267845, -0.012339883 }, { -0.16098841, 0.080519992, -0.11793031, 0.036790025 }, { 0.017193144, -0.0029212372, -0.0044153187, -0.0057094316 }, { 0.23481771, -0.1556448, -0.18775429, -0.013697353 } } }, { { { 0.98467622, 0.94970347, 0.95791534, 0.9408684 }, { 0.95525144, 0.73013516, 0.58966657, 0.43166004 }, { 0.98562289, 0.95804118, 0.77397471, 0.78825859 }, { 0.96588112, 0.72807352, 0.58424502, 0.79142113 }, { -0.049305848, 0.63904864, 0.099145551, -0.03377918 }, { 0.78673348, 0.62998117, 0.62680207, 0.63245759 }, { 0.48526085, 0.544603, 0.40015579, 0.43297544 }, { 0.82487776, 0.77789448, 0.92917353, 0.91697567 }, { 0.58431326, 0.95748667, 0.99880743, 0.99975533 }, { 0.67096902, 0.60093643, 0.64381538, 0.92594344 }, { 0.95700408, 0.93816272, 0.93111608, 0.80905665 }, { 0.94046044, 0.97116483, 0.77381347, 0.78507504 }, { 0.7077214, 0.7547892, 0.23983411, -0.039180128 }, { 0.3656649, 0.38379871, -0.00015338393, 0.16604667 }, { 0.50679735, 0.6108265, 0.46821675, 0.37829596 }, { 0.55946029, 0.72460731, 0.55919425, 0.81214734 }, { 0.86277825, 0.92634645, 0.95542467, 0.96581976 }, { 0.95061533, 0.75913205, 0.60228234, 0.87287949 }, { 0.99994373, 0.93971324, 0.95087677, 0.96466059 }, { 0.9442062, 0.89161694, 0.72879505, 0.92100486 }, { 0.30989313, 0.29579046, 0.11395771, 0.071428407 }, { 0.16674735, -0.054071458, 0.85747916, 0.82737551 }, { 0.61593841, 0.45356879, 0.43544204, 0.41332561 }, { 0.79196443, 0.43841915, 0.77763172, 0.62193473 } }, { { 0.028699614, 0.071974788, -0.028868668, 0.030119772 }, { -0.16988515, -0.35713152, 0.36877151, 0.37172103 }, { 0.024472009, 0.10373643, 0.052160621, -0.12998364 }, { 0.051999909, -0.1688679, 0.05813266, -0.11063347 }, { 0.026373007, 0.067310776, 0.34433164, 0.0017481699 }, { -0.017659611, -0.10215276, -0.23736187, 0.12678732 }, { -0.0019097928, 0.02067204, -0.030447136, -0.0093192388 }, { 0.10615435, 0.11124023, 0.04473958, 0.14369936 }, { 0.14791062, -0.034502091, 0.041456555, 0.06737059 }, { 0.22389399, 0.2668048, 0.25742349, 0.03724758 }, { 0.0046009946, 0.066632032, 0.097957775, 0.22969631 }, { 0.043253167, -0.013638494, 0.071328387, -0.19249903 }, { -0.023561087, 0.011490741, 0.19824644, -0.04133258 }, { -0.057507532, -0.039265903, 0.060469313, 0.37300659 }, { 0.027051207, -0.0086784396, -0.0055877341, -0.0315352 }, { 0.15724931, 0.0099485187, 0.22462997, 0.14112999 }, { 0.13909905, 0.026199511, -0.12430815, -0.076900423 }, { -0.022327596, -0.1975812, 0.49862652, -0.096026553 }, { 0.076782007, 0.041598482, 0.0033451155, 0.039947963 }, { 0.005353589, 0.070993946, 0.0068174778, -0.17805261 }, { -0.059912765, -0.17027417, -0.060069718, 0.1561139 }, { 0.017122435, 0.048532637, -0.05315926, 0.066962855 }, { 0.058014377, 0.021874362, 0.017248667, -0.0069413843 }, { 0.099274028, 0.040622241, 0.040435904, 0.14191123 } }, { { -0.13453832, 0.071519908, -0.1597656, -0.030758273 }, { -0.13511715, 0.32373425, 0.35851035, -0.18685481 }, { 0.021440457, 0.034442875, 0.14324368, 0.15754565 }, { -0.061440371, 0.16837735, 0.47887644, -0.036265812 }, { 0.55060811, 0.14095672, 0.13077418, 0.25515565 }, { -0.084599968, -0.084002143, 0.1542308, 0.044223437 }, { 0.0017727822, 0.025149715, -0.025479364, -0.0023658361 }, { 0.1619123, 0.069159159, -0.016343512, 0.026108175 }, { 0.3296525, 0.029456656, 0.039715069, 0.015958704 }, { -0.093419591, 0.37051381, -0.063182977, -0.017764112 }, { 0.11962535, 0.062511772, -0.070445145, 0.27768911 }, { 0.07458833, -0.16218828, 0.064111239, 0.43889373 }, { -0.0326486, -0.03666828, -0.17597139, 0.34213144 }, { 0.061334301, -0.0099525239, 0.21497301, 0.0074569296 }, { -0.016749445, 0.00054557189, 0.040331287, 0.066200794 }, { 0.20620866, 0.25268529, 0.46594276, 0.059651923 }, { 0.15170896, 0.041438057, 0.021708506, -0.15049245 }, { -0.14317538, 0.13548996, 0.37297491, 0.13718874 }, { 0.053339004, 0.015014013, -0.10418356, -0.13598877 }, { -0.02227412, 0.045548464, 0.21534467, -0.23828118 }, { -0.055326885, 0.11851609, 0.28938409, 0.041373996 }, { -0.1219532, 0.57338554, -0.094571555, 0.025008596 }, { 0.070380772, 0.016993506, 0.018073937, -0.015404818 }, { 0.17033841, 0.12449473, 0.10847869, -0.11141982 } } }, { { { 4.409738, 4.5071479, 5.4761817, 5.3214091 }, { 5.3741435, 4.6270256, 5.4786338, 5.323679 }, { 4.305776, 4.4890731, 4.6894257, 4.6068436 }, { 5.4930574, 4.9116386, 5.4097636, 4.9225404 }, { 5.1861828, 5.5144226, 5.1307797, 5.0804212 }, { 6.1194597, 6.0655136, 5.7369562, 6.1076578 }, { 6.9549598, 6.9281578, 6.9549598, 6.9549598 }, { 4.5030565, 4.5849566, 4.4830953, 4.4904323 }, { 5.3629211, 5.5524848, 4.5719135, 4.9103175 }, { 4.8906163, 5.3972226, 4.8806206, 5.1834202 }, { 4.5047396, 4.5984947, 4.7039612, 4.3422371 }, { 4.5956963, 5.6294962, 4.46025, 4.4827131 }, { 5.8454206, 6.000743, 5.4594428, 4.9952614 }, { 6.09642, 6.3979283, 4.9784963, 5.6878449 }, { 6.9549598, 6.9752898, 6.9549598, 6.9549598 }, { 6.2053562, 4.9984547, 5.3887395, 4.6221036 }, { 4.5265196, 4.3684629, 5.5819288, 5.4957366 }, { 5.2220057, 4.6118907, 5.5046208, 4.9190037 }, { 4.3408178, 4.4980303, 5.4937404, 5.6154153 }, { 4.4802186, 4.4666194, 4.8546878, 5.1764252 }, { 5.7384024, 5.9048089, 5.4636107, 5.0807017 }, { 5.1013817, 5.2237041, 6.0338955, 5.8869417 }, { 6.9414339, 6.9549598, 6.9549598, 6.9549598 }, { 4.3368412, 4.9692663, 4.7090567, 4.9023075 } }, { { 0.0093525884, -0.33796029, -0.4366682, -0.18161326 }, { -0.34446047, 0.10854359, -0.61563912, -0.16514117 }, { 0.055849315, 0.093045585, 0.36722184, 0.085665647 }, { -0.21881508, -0.036846235, -0.25226403, -0.012790033 }, { -0.14697546, -0.026656628, 0.2559775, 0.026279081 }, { 0.073189287, -0.074472165, -0.15439557, 0.020907645 }, { 0, -0.015078298, 0, 0 }, { 0.027540893, -0.30876053, -0.15680794, -0.18470107 }, { -0.072547269, -0.019227086, -0.26735769, -0.1362069 }, { 0.36907279, -0.28005156, 0.01966203, -0.10277819 }, { -0.26755862, 0.066747173, 0.60834173, -0.23356165 }, { -0.12357338, -0.41742338, 0.081840746, -0.14596222 }, { -0.068599762, -0.004402392, -0.17192993, -0.15797464 }, { -0.072923207, -0.02555551, -0.21075071, 0.047272919 }, { 0, 0.0115085, 0, 0 }, { 0.32527558, 0.066048741, -0.28639187, 0.45171914 }, { -0.158086, -0.049098981, -0.17226122, -0.50289857 }, { -0.39456648, 0.031970902, -0.74883626, 0.20536003 }, { 0.22864705, -0.0095988927, -0.1155595, -0.06240073 }, { 0.12336497, -0.34128076, 0.34341316, 0.083678547 }, { -0.032718317, 0.076359349, -0.30099369, -0.016865529 }, { -0.23491753, -0.17228011, -0.044893186, -0.057411459 }, { -0.0077848677, 0, 0, 0 }, { -0.18713605, -0.11612415, 0.30907006, 0.064707406 } }, { { -0.20768494, -0.15642062, -0.079474216, -0.020948121 }, { -0.18767308, -0.013722599, 0.15827086, -0.27421942 }, { -0.11484158, -0.29325715, 0.24426149, 0.34598577 }, { -0.095599056, 0.16784413, 0.23369965, 0.15036114 }, { 0.058496274, -0.064565923, -0.076598803, -0.11988702 }, { -0.03406356, -0.010863931, -0.036116475, 0.0077051595 }, { 0, -0.015078298, 0, 0 }, { -0.21271534, 0.31678528, 0.084310434, -0.039787477 }, { 0.057420352, -0.60894321, -0.14275706, -0.29178151 }, { -0.21477227, 0.091254596, -0.053659362, -0.13299553 }, { -0.24972574, 0.22261101, -0.59415755, -0.13299464 }, { -0.406027, 0.15018847, 0.33281927, 0.28006105 }, { -0.033198856, 0.013081228, 0.0098634494, -0.18858267 }, { -0.16914457, -0.014917022, -0.15618156, 0.038961385 }, { 0, 0.0115085, 0, 0 }, { 0.047340338, -0.052961301, 0.30193278, 0.38564757 }, { -0.2009302, -0.15247105, -0.32333852, 0.22878398 }, { -0.22934017, 0.022888443, 0.30911154, -0.12420416 }, { 0.21191356, -0.33281926, -0.13523708, -0.038546557 }, { 0.28507859, -0.012777666, 0.16285544, -0.12612215 }, { -0.057034227, 0.01719448, -0.037892291, -0.13064036 }, { -0.075888865, 0.041589292, 0.0089100653, -0.10775402 }, { 0.0075560462, 0, 0, 0 }, { -0.18120766, 0.16485298, 0.58949587, 0.072313493 } } }, { { { 0.60381773, 0.64633179, 0.92301353, 0.23720177 }, { 1.1128727, 0.42172315, 1.6605811, 0.22066721 }, { 0.55829912, 0.7107351, 0.47437673, 0.53646626 }, { 0.75684406, 0.65607146, 1.5264507, 0.12817954 }, { -0.25070514, 0.30263175, -0.21070678, -0.2264813 }, { -0.24745858, -0.26801252, 0.2750925, 0.055035565 }, { -0.018769156, -0.066023008, 0.10111114, 0.0089232736 }, { 0.41152465, 0.52508091, 0.4161358, 0.39058287 }, { 0.90919582, 1.2448772, 0.61547497, 0.51303689 }, { 0.2973136, 1.2348603, 0.24154398, 0.76087607 }, { 0.23369317, 0.68368068, 0.81024353, 0.35451079 }, { 0.69272073, 0.47014545, 0.61401877, 0.43768641 }, { -0.44449894, -0.10123077, -0.19173956, -0.15811184 }, { -0.089717, -0.068601549, -0.16704813, -0.29761406 }, { 0.0055968308, -0.089855929, -0.087150641, 0.2244144 }, { 0.38902787, 0.62620686, 1.3314901, 0.26038797 }, { 0.16776511, 0.32722251, 0.71914611, 0.53556119 }, { 0.63106992, 0.46256454, 1.785895, 0.17339911 }, { 0.72516261, 0.44941094, 0.81174974, 0.61247129 }, { 0.56877815, 0.20989179, 0.7607991, 0.017998645 }, { 0.016372087, 0.26062407, -0.32771461, -0.075930098 }, { -0.11957223, -0.22579003, -0.42587945, -0.0015549589 }, { 0.0049992009, 0.053511694, 0.00053268274, 0.022778575 }, { 0.19356675, 0.5564623, 0.74981777, 0.28733119 } }, { { 0.017029304, 0.22690356, 0.25927682, -0.048136042 }, { 0.52936856, -0.26082526, 0.12568074, -0.046727529 }, { 0.08949554, -0.019090555, 0.31477592, -0.067513409 }, { 0.056302335, -0.011819435, -0.063621104, 0.27092306 }, { 0.053971592, -0.17913246, -0.14991651, -0.044263405 }, { 0.29037749, -0.040498369, -0.33600753, 0.16250066 }, { -0.067102844, -0.17843768, 0.033172168, 0.13638573 }, { 0.057127881, -0.044468822, 0.33005778, 0.34775491 }, { -0.14300931, 0.022121077, -0.045281831, -0.065216583 }, { 0.084931489, 0.06688461, 0.15758114, -0.091330485 }, { -0.014274888, 0.29139103, 0.089163749, -0.18005467 }, { -0.2191522, -0.1333803, -0.31948964, -0.28536602 }, { 0.20298891, -0.0031882515, -0.15749696, -0.014977715 }, { -0.14016857, -0.17278064, 0.01369474, 0.10971499 }, { 0.018219806, 0.080447764, 0.0056022696, -0.043028475 }, { -0.076556403, -0.13038184, -0.23788273, 0.5849635 }, { 0.1038427, 0.18199702, 0.35294355, -0.0023601311 }, { 0.22294845, -0.37427713, 0.2907529, 0.26234219 }, { 0.40809306, 0.12982813, 0.42857338, 0.14064303 }, { 0.4265028, 0.18710053, 0.15310514, 0.067551813 }, { -0.18986488, -0.029676062, -0.087045959, -0.14788626 }, { -0.07865478, 0.011558295, -0.018262356, 0.38992629 }, { 0.22297641, 0.072192947, 0.064119712, 0.12862555 }, { -0.069262467, -0.14990585, 0.31342655, -0.15002022 } }, { { 0.25288162, -0.096551539, 0.051695506, 0.20925392 }, { 0.23093904, 0.096712594, 0.19826434, 0.32530694 }, { 0.14114785, 0.071010138, -0.17642029, 0.092260082 }, { 0.39001648, -0.17666595, 0.088397252, 0.1462816 }, { 0.12484597, 0.066920676, -0.16116194, 0.21758387 }, { 0.15625272, -0.00043631439, -0.07868976, -0.19261141 }, { -0.0142415, 0.06356153, 0.026276923, -0.024546668 }, { 0.097089221, 0.085426402, 0.11936115, 0.012042542 }, { 0.52509109, -0.22465399, -0.11490612, 0.023562122 }, { -0.12418278, 0.11985465, 0.087804943, 0.25283464 }, { 0.10716753, -0.036426901, 0.2469409, -0.095816257 }, { -0.095364501, 0.14001518, -0.068636804, -0.082487255 }, { 0.074490355, 0.25323233, 0.17863748, 0.12482145 }, { -0.019616587, -0.0053326518, 0.047558858, 0.066104462 }, { 0.12647102, 0.25712368, 0.12306783, -0.050252261 }, { -0.13375041, 0.17825067, 0.026649645, -0.33338076 }, { 0.16384463, -0.022241979, 0.17817325, 0.6808721 }, { 0.42075944, -0.024292721, -0.11323318, 0.45027063 }, { -0.023953485, 0.25719992, 0.28680108, 0.33600529 }, { 0.013445546, 0.22504275, 0.17408162, 0.52860686 }, { -0.098839039, -0.27017244, 0.10293505, -0.012472685 }, { 0.074267375, -0.0056418849, 0.17632358, 0.21754089 }, { 0.1491061, 0.017927571, -0.0217757, -0.0039381966 }, { 0.067239102, -0.74624136, 0.12992555, -0.058866581 } } } }, { { { { 0.1270204, 0.7650174, 0.55252173, 0.05956498 }, { -0.36870832, 0.31227245, 0.52167466, 0.4282174 }, { -0.036761861, -0.5477415, -0.76091563, -0.37583127 }, { 0.17129434, -0.14281209, -0.40463148, -0.56367877 }, { 0.07429238, 0.45420144, 0.41919765, 0.019225986 }, { -0.44125436, -0.05567539, 0.080551064, 0.54444995 }, { -0.36600455, -0.55359309, -0.3290331, 0.33946169 }, { 0.65253747, 0.015186649, 0.0665303, -0.64649501 }, { 0.05392469, 0.54355001, 0.7539307, -0.41089455 }, { -0.29264863, 0.49684721, 0.39184208, 0.47737193 }, { 0.10885354, -0.80803227, -0.7443769, -0.3736688 }, { 0.1939378, -0.079590275, -0.42241709, -0.75536039 }, { 0.44776697, 0.44884546, 0.427965, 0.3297221 }, { -0.34595785, 0.27723463, 0.12245317, 0.43884357 }, { 0.18467758, -0.55582608, -0.99421464, -0.0096027817 }, { 0.6672057, -0.038103784, -0.048616141, -0.68508055 }, { -0.016615937, 0.62001729, 0.50530563, -0.22211425 }, { -0.16823123, 0.31934529, 0.47092187, 0.4884373 }, { 0.03194189, -0.5624624, -0.44688229, 0.223814 }, { 0.17828041, -0.080017082, -0.44239439, -0.46726625 }, { 0.19895649, 0.82568772, 0.47859751, 0.064443297 }, { -0.47464217, 0.011895223, 0.01123465, -0.010697203 }, { -0.17670677, -0.66931423, -0.5814681, -0.01325001 }, { 0.65193874, -0.010713062, -0.007915928, -0.65520853 } }, { { -0.01027431, -0.0019056004, 0.0020213958, 0.0064495753 }, { 0.0058416688, 0.0051314639, 0.021497114, 0.005870592 }, { -0.00035518612, -0.00087553938, -0.0029318969, 0.0087577986 }, { -0.0048770476, -0.015949665, -0.034816051, -0.006104917 }, { 0.0015371362, -0.0012591621, 0.01241148, 0.00096621463 }, { 0.0032416133, 0.021025709, 0.0036344622, 0.0015436078 }, { -0.0093946276, 0.0046564763, 0.028177476, -0.01022744 }, { 0.00014675555, 0.030031482, -0.0092302407, -0.001999398 }, { -0.049980321, 0.024752279, 0.016684689, -0.0045230976 }, { 0.0067493834, 0.014071508, 0.0079316435, 0.034593704 }, { 0.01971715, -0.0037227013, -0.013430278, -0.024257585 }, { -0.004342319, 0.024001878, -0.013356442, -0.022792018 }, { -0.0051709665, -0.017029547, 0.040567567, 0.0052520812 }, { 0.0090399102, 0.0079604733, 0.00018765016, -0.0092868977 }, { -0.020304032, 0.0056590257, -0.0045373063, -0.018653318 }, { -9.9636934e-05, 0.002001886, 0.0046843544, 0.0055608043 }, { 0.0018025744, -0.0025962216, 0.0068285574, -0.014851062 }, { 0.00041645221, 0.0054738242, 0.0076769026, -0.013419208 }, { 0.0038347099, -0.0042555066, -0.0066470075, 0.0039146778 }, { -0.009084153, 0.024461537, 0.0034578066, -0.0054827001 }, { 0.0033463477, 0.0045594748, 0.00037604935, -0.01571513 }, { -0.012589588, 0.029678359, -0.019924871, -0.004708459 }, { -0.0002642682, -0.0051057336, -0.0042867302, -0.00041141781 }, { -0.00086487068, -0.0025170841, 0.0030062196, -0.0030385417 } }, { { -0.01027431, -0.0019056004, 0.0020213958, 0.0064495753 }, { 0.0058416688, 0.0051314639, 0.021497114, 0.005870592 }, { -0.00035518612, -0.00087553938, -0.0029318969, 0.0087577986 }, { -0.0048770476, -0.015949665, -0.034816051, -0.006104917 }, { 0.0015371362, -0.0012591621, 0.01241148, 0.00096621463 }, { 0.0032416133, 0.021025709, 0.0036344622, 0.0015436078 }, { -0.0093946276, 0.0046564763, 0.028177476, -0.01022744 }, { 0.00014675555, 0.030031482, -0.0092302407, -0.001999398 }, { -0.049980321, 0.024752279, 0.016684689, -0.0045230976 }, { 0.0067493834, 0.014071508, 0.0079316435, 0.034593704 }, { 0.01971715, -0.0037227013, -0.013430278, -0.024257585 }, { -0.004342319, 0.024001878, -0.013356442, -0.022792018 }, { -0.0051709665, -0.017029547, 0.040567567, 0.0052520812 }, { 0.0090399102, 0.0079604733, 0.00018765016, -0.0092868977 }, { -0.020304032, 0.0056590257, -0.0045373063, -0.018653318 }, { -9.9636934e-05, 0.002001886, 0.0046843544, 0.0055608043 }, { 0.0018025744, -0.0025962216, 0.0068285574, -0.014851062 }, { 0.00041645221, 0.0054738242, 0.0076769026, -0.013419208 }, { 0.0038347099, -0.0042555066, -0.0066470075, 0.0039146778 }, { -0.009084153, 0.024461537, 0.0034578066, -0.0054827001 }, { 0.0033463477, 0.0045594748, 0.00037604935, -0.01571513 }, { -0.012589588, 0.029678359, -0.019924871, -0.004708459 }, { -0.0002642682, -0.0051057336, -0.0042867302, -0.00041141781 }, { -0.00086487068, -0.0025170841, 0.0030062196, -0.0030385417 } } }, { { { -0.68772793, 0.19029367, -0.17427646, 0.60300616 }, { -0.29980532, -0.22397537, -0.4071009, 0.36277983 }, { 0.75628069, -0.13426242, 0.13645381, -0.74653491 }, { 0.14891408, -0.13497977, 0.36807879, -0.39814386 }, { -0.20608987, -0.076497863, -0.19510375, 0.34604256 }, { -0.02421123, -0.4588774, -0.64965351, 0.083039161 }, { 0.51918764, -0.30614677, -0.25791921, -0.40837612 }, { 0.028860181, 0.63152733, 0.5876224, -0.033139773 }, { -0.63418144, 0.046874151, 0.24431924, 0.71662556 }, { -0.29088451, -0.21455586, -0.73980807, 0.65038559 }, { 0.78663226, 0.00020858525, 0.40361403, -0.75720144 }, { 0.1998276, 0.54590973, 0.1773378, -0.35464319 }, { -0.40236144, 0.31362578, -0.34406026, 0.38120073 }, { -0.27845549, -0.46862161, -0.47141499, 0.095899189 }, { 0.6004921, 0.28051621, -0.011378178, -0.98141078 }, { 0.032724674, 0.66798127, 0.66430425, -0.05209965 }, { -0.59603974, -0.083198329, 0.34616224, 0.42082916 }, { -0.14262632, -0.21418442, -0.37504914, 0.32676687 }, { 0.58204273, 0.0067537174, -0.35923481, -0.40792038 }, { 0.15607366, 0.17215007, 0.34414936, -0.33566945 }, { -0.44862333, 0.004919013, 0.0076768115, 0.41897935 }, { -0.022062848, -0.39695079, -0.0062786656, 0.042925103 }, { 0.65953535, -0.15521993, 0.011867978, -0.57721165 }, { 0.031305912, 0.65627006, 0.66779002, -0.029815636 } }, { { 0.011457792, -0.011774949, -0.012205337, 0.0048139052 }, { -0.024024566, 0.018313023, -0.023210623, -0.0046351547 }, { 0.0039133571, 0.0046801024, -0.020590099, -0.0018568631 }, { -0.015369931, -0.0092621276, -0.026149742, 0.0010335971 }, { 0.032555144, -0.01336897, -0.022733265, -0.027997469 }, { -0.028161537, -0.00073877629, -0.023989631, 0.0055660453 }, { -0.012966193, 0.003944376, 0.025685982, -0.0017458044 }, { 0.00015626641, -0.009524206, 0.0083025026, -0.00049753811 }, { -0.02358661, 0.006370149, 0.00087066462, -0.00054248544 }, { -0.0024571244, -0.023218369, -0.010895303, -0.0095647684 }, { 0.0069970393, -0.00093403301, -0.0081922371, -0.00026359768 }, { 0.0065921354, 0.028846533, -0.045676337, 0.006070217 }, { 0.0045248423, -0.0084676847, 0.028756195, 0.020612871 }, { 0.0037691244, -0.0069385161, -0.00029501448, -0.0017839033 }, { -0.0048675353, -0.011930456, 0.0044251285, -0.00016323616 }, { -0.0012291164, -0.0019575288, 0.0078250029, -0.0011151155 }, { 0.00503333, -0.0094538968, 0.0092375183, 0.018207648 }, { 0.0080615812, -0.0073583459, -0.0166794, 0.016416158 }, { 0.002192959, -0.01153759, -0.0048668362, -0.0071123281 }, { -0.010116143, -0.010224552, 0.010897731, 0.00093792816 }, { 0.017199359, -0.0087516179, 0.0021169251, -0.020946959 }, { -0.01570063, 0.020087246, 0.014492818, -0.016014018 }, { 0.0023484072, 0.0015070243, -0.00045616273, -0.001211882 }, { 0.0018090492, -0.0012261901, 0.0012809284, 0.00096488905 } }, { { 0.011457792, -0.011774949, -0.012205337, 0.0048139052 }, { -0.024024566, 0.018313023, -0.023210623, -0.0046351547 }, { 0.0039133571, 0.0046801024, -0.020590099, -0.0018568631 }, { -0.015369931, -0.0092621276, -0.026149742, 0.0010335971 }, { 0.032555144, -0.01336897, -0.022733265, -0.027997469 }, { -0.028161537, -0.00073877629, -0.023989631, 0.0055660453 }, { -0.012966193, 0.003944376, 0.025685982, -0.0017458044 }, { 0.00015626641, -0.009524206, 0.0083025026, -0.00049753811 }, { -0.02358661, 0.006370149, 0.00087066462, -0.00054248544 }, { -0.0024571244, -0.023218369, -0.010895303, -0.0095647684 }, { 0.0069970393, -0.00093403301, -0.0081922371, -0.00026359768 }, { 0.0065921354, 0.028846533, -0.045676337, 0.006070217 }, { 0.0045248423, -0.0084676847, 0.028756195, 0.020612871 }, { 0.0037691244, -0.0069385161, -0.00029501448, -0.0017839033 }, { -0.0048675353, -0.011930456, 0.0044251285, -0.00016323616 }, { -0.0012291164, -0.0019575288, 0.0078250029, -0.0011151155 }, { 0.00503333, -0.0094538968, 0.0092375183, 0.018207648 }, { 0.0080615812, -0.0073583459, -0.0166794, 0.016416158 }, { 0.002192959, -0.01153759, -0.0048668362, -0.0071123281 }, { -0.010116143, -0.010224552, 0.010897731, 0.00093792816 }, { 0.017199359, -0.0087516179, 0.0021169251, -0.020946959 }, { -0.01570063, 0.020087246, 0.014492818, -0.016014018 }, { 0.0023484072, 0.0015070243, -0.00045616273, -0.001211882 }, { 0.0018090492, -0.0012261901, 0.0012809284, 0.00096488905 } } }, { { { 0.71476997, 0.61525336, 0.81507512, 0.79550964 }, { 0.87986984, 0.9232123, 0.74974956, 0.82765975 }, { 0.65321366, 0.82580437, 0.63434042, 0.54903231 }, { 0.97390084, 0.98050251, 0.83713283, 0.72370416 }, { 0.97570877, 0.88760866, 0.88668363, 0.9380218 }, { 0.89705541, 0.88675351, 0.75595095, 0.83467284 }, { 0.77232433, 0.77447327, 0.9084134, 0.84734569 }, { -0.75720667, -0.77520488, -0.80639546, -0.76219811 }, { 0.77130152, 0.83806694, 0.60983327, 0.56357207 }, { 0.91090229, 0.84089752, 0.54694041, 0.59085922 }, { 0.60775044, 0.58913818, 0.53197627, 0.53574024 }, { 0.96044628, 0.83405513, 0.88888419, 0.55105253 }, { 0.79850486, 0.83676557, 0.83574428, 0.86369517 }, { 0.89597751, 0.83876978, 0.87336884, 0.8934314 }, { 0.77801249, 0.78253947, 0.10680725, 0.19167855 }, { -0.74415432, -0.74320194, -0.74587957, -0.72660186 }, { 0.802783, 0.78016447, 0.79046691, 0.87952719 }, { 0.97537479, 0.92311625, 0.79848027, 0.80910594 }, { 0.8125306, 0.82679528, 0.81929639, 0.88516002 }, { 0.97152309, 0.98181547, 0.82815966, 0.81791703 }, { 0.87129411, 0.56410602, 0.87800085, 0.905706 }, { 0.87990229, 0.91776281, 0.99991718, 0.99902102 }, { 0.73060786, 0.72658464, 0.81348263, 0.81648708 }, { -0.75762512, -0.75445002, -0.74430762, -0.75485946 } }, { { 0.018332644, 0.0084005452, -0.0018937689, -0.0035491975 }, { 0.0016556654, 0.0049261013, -0.021796869, 0.0025973591 }, { -0.0019671758, 0.00051947074, 0.0071261223, 0.0056689139 }, { 0.00041901024, -0.0023903288, -0.0035639711, -0.0036673013 }, { 0.009963464, 0.00099195429, -0.0042516892, 0.0092605531 }, { 0.0034813664, 0.0028575465, -0.016343415, -0.0014475905 }, { 0.0053571039, 0.0051116063, 0.016171091, -0.00052744238 }, { 0.00013272575, -0.0095491849, 0.0070156475, 0.0017057538 }, { 0.028067438, -0.0086835729, -0.0087852674, 0.0035321054 }, { 0.0025007808, -0.0075654884, -0.012551417, -0.0068823899 }, { -0.00017607308, 0.002636122, -0.011272055, -0.010314896 }, { 0.010646599, 0.00042804331, 0.013900837, -0.01279076 }, { 0.0059898286, 0.012331371, -0.0073125296, 0.016248603 }, { 0.031579315, -0.0057840222, -0.00018304192, 0.005171422 }, { 0.010928513, 0.0092660887, 0.030404621, 0.0053167707 }, { -0.00014899672, -0.0035246494, 0.0075862845, -0.005861723 }, { 0.0067791918, 0.0021224495, -0.0071755505, -0.010370936 }, { 0.0015352958, -0.0025785166, -0.0092688001, 0.003966373 }, { 0.0036915074, -0.002306452, -0.005736452, -0.0033594125 }, { 0.0065128512, 0.006188005, 0.00088322638, -0.0016227066 }, { 0.0092720771, -0.0046684631, -7.3769604e-05, 0.013807013 }, { -0.0031421984, 0.010622679, 0.00041591214, 0.0032786075 }, { -0.0021421613, -0.0041675589, -0.0029529994, -0.00085350449 }, { -0.00069204344, -0.0010785124, 0.00097549628, 0.0025280456 } }, { { 0.018332644, 0.0084005452, -0.0018937689, -0.0035491975 }, { 0.0016556654, 0.0049261013, -0.021796869, 0.0025973591 }, { -0.0019671758, 0.00051947074, 0.0071261223, 0.0056689139 }, { 0.00041901024, -0.0023903288, -0.0035639711, -0.0036673013 }, { 0.009963464, 0.00099195429, -0.0042516892, 0.0092605531 }, { 0.0034813664, 0.0028575465, -0.016343415, -0.0014475905 }, { 0.0053571039, 0.0051116063, 0.016171091, -0.00052744238 }, { 0.00013272575, -0.0095491849, 0.0070156475, 0.0017057538 }, { 0.028067438, -0.0086835729, -0.0087852674, 0.0035321054 }, { 0.0025007808, -0.0075654884, -0.012551417, -0.0068823899 }, { -0.00017607308, 0.002636122, -0.011272055, -0.010314896 }, { 0.010646599, 0.00042804331, 0.013900837, -0.01279076 }, { 0.0059898286, 0.012331371, -0.0073125296, 0.016248603 }, { 0.031579315, -0.0057840222, -0.00018304192, 0.005171422 }, { 0.010928513, 0.0092660887, 0.030404621, 0.0053167707 }, { -0.00014899672, -0.0035246494, 0.0075862845, -0.005861723 }, { 0.0067791918, 0.0021224495, -0.0071755505, -0.010370936 }, { 0.0015352958, -0.0025785166, -0.0092688001, 0.003966373 }, { 0.0036915074, -0.002306452, -0.005736452, -0.0033594125 }, { 0.0065128512, 0.006188005, 0.00088322638, -0.0016227066 }, { 0.0092720771, -0.0046684631, -7.3769604e-05, 0.013807013 }, { -0.0031421984, 0.010622679, 0.00041591214, 0.0032786075 }, { -0.0021421613, -0.0041675589, -0.0029529994, -0.00085350449 }, { -0.00069204344, -0.0010785124, 0.00097549628, 0.0025280456 } } }, { { { 5.3792285, 5.1960477, 5.5112916, 5.6615254 }, { 5.0489877, 5.2428834, 5.1752035, 5.1109826 }, { 5.5205204, 5.7511938, 5.0202917, 4.9168865 }, { 4.9522523, 4.8880256, 5.1015936, 5.2858816 }, { 5.7256502, 5.7919759, 5.645241, 5.6035708 }, { 6.4076931, 6.4822111, 6.2642633, 6.3925959 }, { 6.9797014, 6.981436, 7.0028674, 6.9976464 }, { -0.03290957, -0.03290957, -0.03290957, -0.03290957 }, { 5.4977854, 5.7684965, 5.3463095, 4.8810492 }, { 4.9869047, 5.4896416, 4.9647805, 4.884877 }, { 5.3141219, 5.3357788, 4.7695434, 4.8709631 }, { 5.2056063, 5.407802, 5.2123857, 4.9428208 }, { 6.2188218, 6.17756, 6.2751008, 6.3672109 }, { 6.9105856, 6.7986798, 6.5712335, 6.5907061 }, { 6.9797014, 6.9797014, 5.6859993, 5.5642483 }, { -0.032764603, -0.032764603, -0.032764603, -0.032764603 }, { 5.7724142, 6.0929556, 5.99581, 5.9265164 }, { 4.9363192, 4.9823732, 5.1732995, 5.2475265 }, { 5.8365191, 5.9972902, 5.9778441, 5.9270668 }, { 4.8706768, 5.0194503, 5.155585, 5.2188041 }, { 6.1569904, 6.0563989, 6.0989699, 6.2139837 }, { 5.8727399, 5.8948086, 5.5734095, 5.5536103 }, { 6.9797014, 6.9797014, 6.9797014, 6.9797014 }, { -0.032766769, -0.032766769, -0.032766769, -0.032766769 } }, { { 0.0011802354, -0.006546101, -0.02103972, 0.0008654047 }, { -0.015460534, 0.017874544, 0.0029121134, 0.023511773 }, { -0.040909245, 0.011927691, 0.011991588, 0.01677931 }, { -0.015633544, -0.0042321141, 0.026623034, 0.0080414514 }, { 0.012614382, 0.0065080145, 0.035716738, -0.0080665814 }, { -0.0057849744, -0.017478461, -0.031219642, 0.00016446523 }, { 0, 0.00032235028, 0, 0 }, { 0, 0, 0, 0 }, { -0.068586697, -0.024228236, -0.012857221, -0.039493706 }, { -0.018078201, -0.015140979, 0.00072119173, -0.051249859 }, { -0.054228277, 0.0097895101, 0.0019832646, -0.011715411 }, { -0.042326208, -0.010160072, 0.037088052, -0.031848667 }, { 0.00067130897, -0.013966717, -0.017268559, -0.0074614576 }, { 0.070515961, 0.012848107, -0.0008396517, 0.0049006506 }, { 0, 0, -0.063014256, -0.0085124986 }, { 0, 0, 0, 0 }, { -0.040302299, 0.0048936307, 0.0064406394, 0.0034044871 }, { -0.010453589, 0.0035820836, -0.017384391, -0.038199947 }, { -0.044968611, -0.0088322127, 0.020303819, 0.0058131005 }, { -0.0056838535, 0.010211409, -0.010999927, -0.027621859 }, { 0.0064753811, -0.0059341242, -0.014902755, 0.0082868118 }, { -0.0013222735, 0.0028492181, -0.023523273, -0.02576271 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 } }, { { 0.0011802354, -0.006546101, -0.02103972, 0.00086540469 }, { -0.015460534, 0.017874544, 0.0029121134, 0.023511773 }, { -0.040909245, 0.011927691, 0.011991588, 0.01677931 }, { -0.015633544, -0.0042321141, 0.026623034, 0.0080414514 }, { 0.012614382, 0.0065080145, 0.035716738, -0.0080665814 }, { -0.0057849744, -0.017478461, -0.031219642, 0.00016446523 }, { 0, 0.00032235028, 0, 0 }, { 0, 0, 0, 0 }, { -0.068586697, -0.024228236, -0.012857221, -0.039493706 }, { -0.018078201, -0.015140979, 0.00072119173, -0.051249859 }, { -0.054228277, 0.0097895101, 0.0019832646, -0.011715411 }, { -0.042326208, -0.010160072, 0.037088052, -0.031848667 }, { 0.00067130897, -0.013966717, -0.017268559, -0.0074614576 }, { 0.070515961, 0.012848107, -0.0008396517, 0.0049006506 }, { 0, 0, -0.063014256, -0.0085124986 }, { 0, 0, 0, 0 }, { -0.040302299, 0.0048936307, 0.0064406394, 0.0034044871 }, { -0.010453589, 0.0035820836, -0.017384391, -0.038199947 }, { -0.044968611, -0.0088322127, 0.020303819, 0.0058131005 }, { -0.0056838535, 0.010211409, -0.010999927, -0.027621859 }, { 0.0064753811, -0.0059341242, -0.014902755, 0.0082868118 }, { -0.0013222735, 0.0028492181, -0.023523273, -0.02576271 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 } } }, { { { 0.72189984, 0.22069996, 0.71952927, 0.77725949 }, { 0.4054405, 0.20582059, 0.2747016, 0.37612563 }, { 0.58887422, 0.27441131, 0.19468101, 0.21480554 }, { 0.46814145, 0.34317, 0.46068212, 0.13962064 }, { -0.18134132, -0.26668789, -0.60984999, -0.67879259 }, { -0.47870351, -0.34453227, 0.32494779, 0.10292971 }, { 0.087252967, 0.066950358, 0.31813819, 0.071094818 }, { -0.0031436256, 0.038245091, -0.0076651913, -0.015389479 }, { 1.2668531, 1.2894974, 0.40584018, 0.51755806 }, { 1.3207257, 1.3403747, 0.54924634, 0.40282713 }, { 0.78581828, 0.56379328, 0.27901993, 0.56429306 }, { 0.8748226, 1.0271253, 1.0085726, 0.3888545 }, { -0.22577636, -0.32895071, -0.2846317, -0.11679531 }, { 0.26477285, 0.3179447, -0.063393238, 0.024059773 }, { -0.15463395, -0.22721468, -0.20680404, -0.15700788 }, { 0.012107106, -0.0061245949, -0.024224367, 0.005040693 }, { 0.97943693, 0.64840429, 0.45106998, 0.40771935 }, { 0.49907853, 0.1562184, 0.34338458, 0.39710628 }, { 0.95047709, 0.53336107, 0.38318275, 0.44919148 }, { 0.41892697, 0.069965886, 0.45831656, 0.38821529 }, { -0.20216736, -0.43209441, -0.57684857, -0.40189427 }, { -0.63992377, -0.40683032, -0.59207903, -0.57251716 }, { -0.047117438, -0.1880015, -0.12265155, 0.00059988607 }, { -0.011836442, -0.010049497, -0.0026152072, 0.016137736 } }, { { 0.092068993, 0.0045466749, 0.0054574031, 0.02582156 }, { 0.022115456, -0.015664041, -0.022004653, 0.041431654 }, { 0.029951298, -0.0004408542, 0.0087496069, 0.017850027 }, { 0.029086373, 0.022116039, 0.044010315, 0.001644876 }, { 0.016256387, 0.0083249367, 0.019570849, -0.0021276222 }, { 0.0079070076, -0.024696939, 0.044311101, 0.023671132 }, { -0.0081796119, -0.0024995551, 0.033501743, -0.031958988 }, { 0.0065005403, -0.076642001, 0.015736477, 0.030966939 }, { 0.029110717, 0.039154477, -0.074376619, 0.025532063 }, { -0.10980761, 0.0038346834, 0.014449171, -0.030702653 }, { -0.00068350423, -0.037251569, -0.008409224, -0.026322878 }, { 0.035406012, 0.064176275, 0.031437854, -0.0344642 }, { 0.037145809, -0.024909212, 0.041030386, 0.035216105 }, { -0.093276646, -0.013904083, -0.019536023, -0.023834405 }, { 0.042751846, -0.03620164, 0.081115921, 0.018379967 }, { -0.023909625, 0.012833691, 0.048086442, -0.0097340268 }, { 0.039552712, -0.00026806514, 0.011646753, 0.0065939486 }, { 0.058985248, 0.020165701, 0.0076721521, 0.033274221 }, { 0.052889871, 0.0042520093, 0.016490396, 0.009287973 }, { 0.044305975, -0.0016263469, 0.041390177, 0.033541355 }, { 0.014595133, -0.004801042, -0.0049517302, 0.015714264 }, { 0.00075086205, 0.0080838736, -0.037611057, -0.030488441 }, { 0.0019178075, -0.0082517768, -0.002525773, 0.0043993022 }, { 0.023774971, 0.020335611, 0.0056643868, -0.032100338 } }, { { 0.092068993, 0.0045466749, 0.0054574031, 0.02582156 }, { 0.022115456, -0.015664041, -0.022004653, 0.041431654 }, { 0.029951298, -0.0004408542, 0.0087496069, 0.017850027 }, { 0.029086373, 0.022116039, 0.044010315, 0.001644876 }, { 0.016256387, 0.0083249367, 0.019570849, -0.0021276222 }, { 0.0079070076, -0.024696939, 0.044311101, 0.023671132 }, { -0.0081796119, -0.0024995551, 0.033501743, -0.031958988 }, { 0.0065005403, -0.076642001, 0.015736477, 0.030966939 }, { 0.029110717, 0.039154477, -0.074376619, 0.025532063 }, { -0.10980761, 0.0038346834, 0.014449171, -0.030702653 }, { -0.00068350423, -0.037251569, -0.008409224, -0.026322878 }, { 0.035406012, 0.064176275, 0.031437854, -0.0344642 }, { 0.037145809, -0.024909212, 0.041030386, 0.035216105 }, { -0.093276646, -0.013904083, -0.019536023, -0.023834405 }, { 0.042751846, -0.03620164, 0.081115921, 0.018379967 }, { -0.023909625, 0.012833691, 0.048086442, -0.0097340268 }, { 0.039552712, -0.00026806514, 0.011646753, 0.0065939486 }, { 0.058985248, 0.020165701, 0.0076721521, 0.033274221 }, { 0.052889871, 0.0042520093, 0.016490396, 0.009287973 }, { 0.044305975, -0.0016263469, 0.041390177, 0.033541355 }, { 0.014595133, -0.004801042, -0.0049517303, 0.015714264 }, { 0.00075086205, 0.0080838736, -0.037611057, -0.030488441 }, { 0.0019178075, -0.0082517768, -0.002525773, 0.0043993022 }, { 0.023774971, 0.020335611, 0.0056643868, -0.032100338 } } } } }; + +#endif diff --git a/servers/rendering/rasterizer_rd/light_cluster_builder.cpp b/servers/rendering/rasterizer_rd/light_cluster_builder.cpp new file mode 100644 index 0000000000..943ef1c7fa --- /dev/null +++ b/servers/rendering/rasterizer_rd/light_cluster_builder.cpp @@ -0,0 +1,255 @@ +/*************************************************************************/ +/* light_cluster_builder.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "light_cluster_builder.h" + +void LightClusterBuilder::begin(const Transform &p_view_transform, const CameraMatrix &p_cam_projection) { + view_xform = p_view_transform; + projection = p_cam_projection; + z_near = -projection.get_z_near(); + z_far = -projection.get_z_far(); + + //reset counts + light_count = 0; + refprobe_count = 0; + item_count = 0; + sort_id_count = 0; +} + +void LightClusterBuilder::bake_cluster() { + + float slice_depth = (z_near - z_far) / depth; + + uint8_t *cluster_dataw = cluster_data.ptrw(); + Cell *cluster_data_ptr = (Cell *)cluster_dataw; + //clear the cluster + zeromem(cluster_data_ptr, (width * height * depth * sizeof(Cell))); + + /* Step 1, create cell positions and count them */ + + for (uint32_t i = 0; i < item_count; i++) { + + const Item &item = items[i]; + + int from_slice = Math::floor((z_near - (item.aabb.position.z + item.aabb.size.z)) / slice_depth); + int to_slice = Math::floor((z_near - item.aabb.position.z) / slice_depth); + + if (from_slice >= (int)depth || to_slice < 0) { + continue; //sorry no go + } + + from_slice = MAX(0, from_slice); + to_slice = MIN((int)depth - 1, to_slice); + + for (int j = from_slice; j <= to_slice; j++) { + + Vector3 min = item.aabb.position; + Vector3 max = item.aabb.position + item.aabb.size; + + float limit_near = MIN((z_near - slice_depth * j), max.z); + float limit_far = MAX((z_near - slice_depth * (j + 1)), min.z); + + max.z = limit_near; + min.z = limit_near; + + Vector3 proj_min = projection.xform(min); + Vector3 proj_max = projection.xform(max); + + int near_from_x = int(Math::floor((proj_min.x * 0.5 + 0.5) * width)); + int near_from_y = int(Math::floor((-proj_max.y * 0.5 + 0.5) * height)); + int near_to_x = int(Math::floor((proj_max.x * 0.5 + 0.5) * width)); + int near_to_y = int(Math::floor((-proj_min.y * 0.5 + 0.5) * height)); + + max.z = limit_far; + min.z = limit_far; + + proj_min = projection.xform(min); + proj_max = projection.xform(max); + + int far_from_x = int(Math::floor((proj_min.x * 0.5 + 0.5) * width)); + int far_from_y = int(Math::floor((-proj_max.y * 0.5 + 0.5) * height)); + int far_to_x = int(Math::floor((proj_max.x * 0.5 + 0.5) * width)); + int far_to_y = int(Math::floor((-proj_min.y * 0.5 + 0.5) * height)); + + //print_line(itos(j) + " near - " + Vector2i(near_from_x, near_from_y) + " -> " + Vector2i(near_to_x, near_to_y)); + //print_line(itos(j) + " far - " + Vector2i(far_from_x, far_from_y) + " -> " + Vector2i(far_to_x, far_to_y)); + + int from_x = MIN(near_from_x, far_from_x); + int from_y = MIN(near_from_y, far_from_y); + int to_x = MAX(near_to_x, far_to_x); + int to_y = MAX(near_to_y, far_to_y); + + if (from_x >= (int)width || to_x < 0 || from_y >= (int)height || to_y < 0) { + continue; + } + + int sx = MAX(0, from_x); + int sy = MAX(0, from_y); + int dx = MIN((int)width - 1, to_x); + int dy = MIN((int)height - 1, to_y); + + //print_line(itos(j) + " - " + Vector2i(sx, sy) + " -> " + Vector2i(dx, dy)); + + for (int x = sx; x <= dx; x++) { + for (int y = sy; y <= dy; y++) { + uint32_t offset = j * (width * height) + y * width + x; + + if (unlikely(sort_id_count == sort_id_max)) { + sort_id_max = nearest_power_of_2_templated(sort_id_max + 1); + sort_ids = (SortID *)memrealloc(sort_ids, sizeof(SortID) * sort_id_max); + if (ids.size()) { + + ids.resize(sort_id_max); + RD::get_singleton()->free(items_buffer); + items_buffer = RD::get_singleton()->storage_buffer_create(sizeof(uint32_t) * sort_id_max); + } + } + + sort_ids[sort_id_count].cell_index = offset; + sort_ids[sort_id_count].item_index = item.index; + sort_ids[sort_id_count].item_type = item.type; + + sort_id_count++; + + //for now, only count + cluster_data_ptr[offset].item_pointers[item.type]++; + //print_line("at offset " + itos(offset) + " value: " + itos(cluster_data_ptr[offset].item_pointers[item.type])); + } + } + } + } + + /* Step 2, Assign pointers (and reset counters) */ + + uint32_t offset = 0; + for (uint32_t i = 0; i < (width * height * depth); i++) { + for (int j = 0; j < ITEM_TYPE_MAX; j++) { + uint32_t count = cluster_data_ptr[i].item_pointers[j]; //save count + cluster_data_ptr[i].item_pointers[j] = offset; //replace count by pointer + offset += count; //increase offset by count; + } + } + + //print_line("offset: " + itos(offset)); + /* Step 3, Place item lists */ + + uint32_t *ids_ptr = ids.ptrw(); + + for (uint32_t i = 0; i < sort_id_count; i++) { + const SortID &id = sort_ids[i]; + Cell &cell = cluster_data_ptr[id.cell_index]; + uint32_t pointer = cell.item_pointers[id.item_type] & POINTER_MASK; + uint32_t counter = cell.item_pointers[id.item_type] >> COUNTER_SHIFT; + ids_ptr[pointer + counter] = id.item_index; + + cell.item_pointers[id.item_type] = pointer | ((counter + 1) << COUNTER_SHIFT); + } + + RD::get_singleton()->texture_update(cluster_texture, 0, cluster_data, true); + RD::get_singleton()->buffer_update(items_buffer, 0, offset * sizeof(uint32_t), ids_ptr, true); +} + +void LightClusterBuilder::setup(uint32_t p_width, uint32_t p_height, uint32_t p_depth) { + + if (width == p_width && height == p_height && depth == p_depth) { + return; + } + if (cluster_texture.is_valid()) { + RD::get_singleton()->free(cluster_texture); + } + + width = p_width; + height = p_height; + depth = p_depth; + + cluster_data.resize(width * height * depth * sizeof(Cell)); + + { + RD::TextureFormat tf; + tf.format = RD::DATA_FORMAT_R32G32B32A32_UINT; + tf.type = RD::TEXTURE_TYPE_3D; + tf.width = width; + tf.height = height; + tf.depth = depth; + tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT; + + cluster_texture = RD::get_singleton()->texture_create(tf, RD::TextureView()); + } +} + +RID LightClusterBuilder::get_cluster_texture() const { + return cluster_texture; +} +RID LightClusterBuilder::get_cluster_indices_buffer() const { + return items_buffer; +} + +LightClusterBuilder::LightClusterBuilder() { + //initialize accumulators to something + lights = (LightData *)memalloc(sizeof(LightData) * 1024); + light_max = 1024; + + refprobes = (OrientedBoxData *)memalloc(sizeof(OrientedBoxData) * 1024); + refprobe_max = 1024; + + decals = (OrientedBoxData *)memalloc(sizeof(OrientedBoxData) * 1024); + decal_max = 1024; + + items = (Item *)memalloc(sizeof(Item) * 1024); + item_max = 1024; + + sort_ids = (SortID *)memalloc(sizeof(SortID) * 1024); + ids.resize(2014); + items_buffer = RD::get_singleton()->storage_buffer_create(sizeof(uint32_t) * 1024); + item_max = 1024; +} +LightClusterBuilder::~LightClusterBuilder() { + + if (cluster_data.size()) { + RD::get_singleton()->free(cluster_texture); + } + + if (lights) { + memfree(lights); + } + if (refprobes) { + memfree(refprobes); + } + if (decals) { + memfree(decals); + } + if (items) { + memfree(items); + } + if (sort_ids) { + memfree(sort_ids); + RD::get_singleton()->free(items_buffer); + } +} diff --git a/servers/rendering/rasterizer_rd/light_cluster_builder.h b/servers/rendering/rasterizer_rd/light_cluster_builder.h new file mode 100644 index 0000000000..3411ed07a0 --- /dev/null +++ b/servers/rendering/rasterizer_rd/light_cluster_builder.h @@ -0,0 +1,291 @@ +/*************************************************************************/ +/* light_cluster_builder.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef LIGHT_CLUSTER_BUILDER_H +#define LIGHT_CLUSTER_BUILDER_H + +#include "servers/rendering/rasterizer_rd/rasterizer_storage_rd.h" + +class LightClusterBuilder { +public: + enum LightType { + LIGHT_TYPE_OMNI, + LIGHT_TYPE_SPOT + }; + + enum ItemType { + ITEM_TYPE_OMNI_LIGHT, + ITEM_TYPE_SPOT_LIGHT, + ITEM_TYPE_REFLECTION_PROBE, + ITEM_TYPE_DECAL, + ITEM_TYPE_MAX //should always be 4 + }; + + enum { + COUNTER_SHIFT = 20, //one million total ids + POINTER_MASK = (1 << COUNTER_SHIFT) - 1, + COUNTER_MASK = 0xfff // 4096 items per cell + }; + +private: + struct LightData { + float position[3]; + uint32_t type; + float radius; + float spot_aperture; + uint32_t pad[2]; + }; + + uint32_t light_count = 0; + uint32_t light_max = 0; + LightData *lights = nullptr; + + struct OrientedBoxData { + float position[3]; + uint32_t pad; + float x_axis[3]; + uint32_t pad2; + float y_axis[3]; + uint32_t pad3; + float z_axis[3]; + uint32_t pad4; + }; + + uint32_t refprobe_count = 0; + uint32_t refprobe_max = 0; + OrientedBoxData *refprobes = nullptr; + + uint32_t decal_count = 0; + uint32_t decal_max = 0; + OrientedBoxData *decals = nullptr; + + struct Item { + AABB aabb; + ItemType type; + uint32_t index; + }; + + Item *items = nullptr; + uint32_t item_count = 0; + uint32_t item_max = 0; + + uint32_t width = 0; + uint32_t height = 0; + uint32_t depth = 0; + + struct Cell { + uint32_t item_pointers[ITEM_TYPE_MAX]; + }; + + Vector<uint8_t> cluster_data; + RID cluster_texture; + + struct SortID { + uint32_t cell_index; + uint32_t item_index; + ItemType item_type; + }; + + SortID *sort_ids = nullptr; + Vector<uint32_t> ids; + uint32_t sort_id_count = 0; + uint32_t sort_id_max = 0; + RID items_buffer; + + Transform view_xform; + CameraMatrix projection; + float z_far = 0; + float z_near = 0; + + _FORCE_INLINE_ void _add_item(const AABB &p_aabb, ItemType p_type, uint32_t p_index) { + if (unlikely(item_count == item_max)) { + item_max = nearest_power_of_2_templated(item_max + 1); + items = (Item *)memrealloc(items, sizeof(Item) * item_max); + } + + Item &item = items[item_count]; + item.aabb = p_aabb; + item.index = p_index; + item.type = p_type; + item_count++; + } + +public: + void begin(const Transform &p_view_transform, const CameraMatrix &p_cam_projection); + + _FORCE_INLINE_ void add_light(LightType p_type, const Transform &p_transform, float p_radius, float p_spot_aperture) { + if (unlikely(light_count == light_max)) { + light_max = nearest_power_of_2_templated(light_max + 1); + lights = (LightData *)memrealloc(lights, sizeof(LightData) * light_max); + } + + LightData &ld = lights[light_count]; + ld.type = p_type; + ld.position[0] = p_transform.origin.x; + ld.position[1] = p_transform.origin.y; + ld.position[2] = p_transform.origin.z; + ld.radius = p_radius; + ld.spot_aperture = p_spot_aperture; + + Transform xform = view_xform * p_transform; + + ld.radius *= xform.basis.get_uniform_scale(); + + AABB aabb; + + switch (p_type) { + case LIGHT_TYPE_OMNI: { + aabb.position = xform.origin; + aabb.size = Vector3(ld.radius, ld.radius, ld.radius); + aabb.position -= aabb.size; + aabb.size *= 2.0; + + _add_item(aabb, ITEM_TYPE_OMNI_LIGHT, light_count); + } break; + case LIGHT_TYPE_SPOT: { + Vector3 v(0, 0, -1); + v.rotated(Vector3(0, 1, 0), Math::deg2rad(ld.spot_aperture)); //rotate in x-z + v.normalize(); + v *= ld.radius; + v.y = v.x; + + aabb.position = xform.origin; + aabb.expand_to(xform.xform(v)); + aabb.expand_to(xform.xform(Vector3(-v.x, v.y, v.z))); + aabb.expand_to(xform.xform(Vector3(-v.x, -v.y, v.z))); + aabb.expand_to(xform.xform(Vector3(v.x, -v.y, v.z))); + _add_item(aabb, ITEM_TYPE_SPOT_LIGHT, light_count); + } break; + } + + light_count++; + } + + _FORCE_INLINE_ void add_reflection_probe(const Transform &p_transform, const Vector3 &p_half_extents) { + + if (unlikely(refprobe_count == refprobe_max)) { + refprobe_max = nearest_power_of_2_templated(refprobe_max + 1); + refprobes = (OrientedBoxData *)memrealloc(refprobes, sizeof(OrientedBoxData) * refprobe_max); + } + + OrientedBoxData &rp = refprobes[refprobe_count]; + Vector3 origin = p_transform.origin; + rp.position[0] = origin.x; + rp.position[1] = origin.y; + rp.position[2] = origin.z; + + Vector3 x_axis = p_transform.basis.get_axis(0) * p_half_extents.x; + rp.x_axis[0] = x_axis.x; + rp.x_axis[1] = x_axis.y; + rp.x_axis[2] = x_axis.z; + + Vector3 y_axis = p_transform.basis.get_axis(1) * p_half_extents.y; + rp.y_axis[0] = y_axis.x; + rp.y_axis[1] = y_axis.y; + rp.y_axis[2] = y_axis.z; + + Vector3 z_axis = p_transform.basis.get_axis(2) * p_half_extents.z; + rp.z_axis[0] = z_axis.x; + rp.z_axis[1] = z_axis.y; + rp.z_axis[2] = z_axis.z; + + AABB aabb; + + aabb.position = origin + x_axis + y_axis + z_axis; + aabb.expand_to(origin + x_axis + y_axis - z_axis); + aabb.expand_to(origin + x_axis - y_axis + z_axis); + aabb.expand_to(origin + x_axis - y_axis - z_axis); + aabb.expand_to(origin - x_axis + y_axis + z_axis); + aabb.expand_to(origin - x_axis + y_axis - z_axis); + aabb.expand_to(origin - x_axis - y_axis + z_axis); + aabb.expand_to(origin - x_axis - y_axis - z_axis); + + _add_item(aabb, ITEM_TYPE_REFLECTION_PROBE, refprobe_count); + + refprobe_count++; + } + + _FORCE_INLINE_ void add_decal(const Transform &p_transform, const Vector2 &p_half_extents, float p_depth) { + + if (unlikely(decal_count == decal_max)) { + decal_max = nearest_power_of_2_templated(decal_max + 1); + decals = (OrientedBoxData *)memrealloc(decals, sizeof(OrientedBoxData) * decal_max); + } + + OrientedBoxData &dc = decals[decal_count]; + + Vector3 z_axis = -p_transform.basis.get_axis(2) * p_depth * 0.5; + dc.z_axis[0] = z_axis.x; + dc.z_axis[1] = z_axis.y; + dc.z_axis[2] = z_axis.z; + + Vector3 origin = p_transform.origin - z_axis; + dc.position[0] = origin.x; + dc.position[1] = origin.y; + dc.position[2] = origin.z; + + Vector3 x_axis = p_transform.basis.get_axis(0) * p_half_extents.x; + dc.x_axis[0] = x_axis.x; + dc.x_axis[1] = x_axis.y; + dc.x_axis[2] = x_axis.z; + + Vector3 y_axis = p_transform.basis.get_axis(1) * p_half_extents.y; + dc.y_axis[0] = y_axis.x; + dc.y_axis[1] = y_axis.y; + dc.y_axis[2] = y_axis.z; + + AABB aabb; + + aabb.position = origin + x_axis + y_axis + z_axis; + aabb.expand_to(origin + x_axis + y_axis - z_axis); + aabb.expand_to(origin + x_axis - y_axis + z_axis); + aabb.expand_to(origin + x_axis - y_axis - z_axis); + aabb.expand_to(origin - x_axis + y_axis + z_axis); + aabb.expand_to(origin - x_axis + y_axis - z_axis); + aabb.expand_to(origin - x_axis - y_axis + z_axis); + aabb.expand_to(origin - x_axis - y_axis - z_axis); + + _add_item(aabb, ITEM_TYPE_DECAL, decal_count); + + decal_count++; + } + + void bake_cluster(); + + void setup(uint32_t p_width, uint32_t p_height, uint32_t p_depth); + + RID get_cluster_texture() const; + RID get_cluster_indices_buffer() const; + + LightClusterBuilder(); + ~LightClusterBuilder(); +}; + +#endif // LIGHT_CLUSTER_BUILDER_H diff --git a/servers/rendering/rasterizer_rd/rasterizer_canvas_rd.cpp b/servers/rendering/rasterizer_rd/rasterizer_canvas_rd.cpp new file mode 100644 index 0000000000..8d52ffd7b9 --- /dev/null +++ b/servers/rendering/rasterizer_rd/rasterizer_canvas_rd.cpp @@ -0,0 +1,2558 @@ +/*************************************************************************/ +/* rasterizer_canvas_rd.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "rasterizer_canvas_rd.h" +#include "core/math/math_funcs.h" +#include "core/project_settings.h" +#include "rasterizer_rd.h" + +void RasterizerCanvasRD::_update_transform_2d_to_mat4(const Transform2D &p_transform, float *p_mat4) { + + p_mat4[0] = p_transform.elements[0][0]; + p_mat4[1] = p_transform.elements[0][1]; + p_mat4[2] = 0; + p_mat4[3] = 0; + p_mat4[4] = p_transform.elements[1][0]; + p_mat4[5] = p_transform.elements[1][1]; + p_mat4[6] = 0; + p_mat4[7] = 0; + p_mat4[8] = 0; + p_mat4[9] = 0; + p_mat4[10] = 1; + p_mat4[11] = 0; + p_mat4[12] = p_transform.elements[2][0]; + p_mat4[13] = p_transform.elements[2][1]; + p_mat4[14] = 0; + p_mat4[15] = 1; +} + +void RasterizerCanvasRD::_update_transform_2d_to_mat2x4(const Transform2D &p_transform, float *p_mat2x4) { + + p_mat2x4[0] = p_transform.elements[0][0]; + p_mat2x4[1] = p_transform.elements[1][0]; + p_mat2x4[2] = 0; + p_mat2x4[3] = p_transform.elements[2][0]; + + p_mat2x4[4] = p_transform.elements[0][1]; + p_mat2x4[5] = p_transform.elements[1][1]; + p_mat2x4[6] = 0; + p_mat2x4[7] = p_transform.elements[2][1]; +} + +void RasterizerCanvasRD::_update_transform_2d_to_mat2x3(const Transform2D &p_transform, float *p_mat2x3) { + + p_mat2x3[0] = p_transform.elements[0][0]; + p_mat2x3[1] = p_transform.elements[0][1]; + p_mat2x3[2] = p_transform.elements[1][0]; + p_mat2x3[3] = p_transform.elements[1][1]; + p_mat2x3[4] = p_transform.elements[2][0]; + p_mat2x3[5] = p_transform.elements[2][1]; +} + +void RasterizerCanvasRD::_update_transform_to_mat4(const Transform &p_transform, float *p_mat4) { + + p_mat4[0] = p_transform.basis.elements[0][0]; + p_mat4[1] = p_transform.basis.elements[1][0]; + p_mat4[2] = p_transform.basis.elements[2][0]; + p_mat4[3] = 0; + p_mat4[4] = p_transform.basis.elements[0][1]; + p_mat4[5] = p_transform.basis.elements[1][1]; + p_mat4[6] = p_transform.basis.elements[2][1]; + p_mat4[7] = 0; + p_mat4[8] = p_transform.basis.elements[0][2]; + p_mat4[9] = p_transform.basis.elements[1][2]; + p_mat4[10] = p_transform.basis.elements[2][2]; + p_mat4[11] = 0; + p_mat4[12] = p_transform.origin.x; + p_mat4[13] = p_transform.origin.y; + p_mat4[14] = p_transform.origin.z; + p_mat4[15] = 1; +} + +void RasterizerCanvasRD::_update_specular_shininess(const Color &p_transform, uint32_t *r_ss) { + + *r_ss = uint32_t(CLAMP(p_transform.a * 255.0, 0, 255)) << 24; + *r_ss |= uint32_t(CLAMP(p_transform.b * 255.0, 0, 255)) << 16; + *r_ss |= uint32_t(CLAMP(p_transform.g * 255.0, 0, 255)) << 8; + *r_ss |= uint32_t(CLAMP(p_transform.r * 255.0, 0, 255)); +} + +RID RasterizerCanvasRD::_create_texture_binding(RID p_texture, RID p_normalmap, RID p_specular, RenderingServer::CanvasItemTextureFilter p_filter, RenderingServer::CanvasItemTextureRepeat p_repeat, RID p_multimesh) { + + Vector<RD::Uniform> uniform_set; + + { // COLOR TEXTURE + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_TEXTURE; + u.binding = 1; + RID texture = storage->texture_get_rd_texture(p_texture); + if (!texture.is_valid()) { + //use default white texture + texture = storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_WHITE); + } + u.ids.push_back(texture); + uniform_set.push_back(u); + } + + { // NORMAL TEXTURE + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_TEXTURE; + u.binding = 2; + RID texture = storage->texture_get_rd_texture(p_normalmap); + if (!texture.is_valid()) { + //use default normal texture + texture = storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_NORMAL); + } + u.ids.push_back(texture); + uniform_set.push_back(u); + } + + { // SPECULAR TEXTURE + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_TEXTURE; + u.binding = 3; + RID texture = storage->texture_get_rd_texture(p_specular); + if (!texture.is_valid()) { + //use default white texture + texture = storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_WHITE); + } + u.ids.push_back(texture); + uniform_set.push_back(u); + } + + { // SAMPLER + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_SAMPLER; + u.binding = 4; + RID sampler = storage->sampler_rd_get_default(p_filter, p_repeat); + ERR_FAIL_COND_V(sampler.is_null(), RID()); + u.ids.push_back(sampler); + uniform_set.push_back(u); + } + + { // MULTIMESH TEXTURE BUFFER + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_TEXTURE_BUFFER; + u.binding = 5; + u.ids.push_back(storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_MULTIMESH_BUFFER)); + uniform_set.push_back(u); + } + + return RD::get_singleton()->uniform_set_create(uniform_set, shader.default_version_rd_shader, 0); +} + +RasterizerCanvas::TextureBindingID RasterizerCanvasRD::request_texture_binding(RID p_texture, RID p_normalmap, RID p_specular, RenderingServer::CanvasItemTextureFilter p_filter, RenderingServer::CanvasItemTextureRepeat p_repeat, RID p_multimesh) { + + if (p_filter == RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT) { + p_filter = default_samplers.default_filter; + } + + if (p_repeat == RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT) { + p_repeat = default_samplers.default_repeat; + } + + TextureBindingKey key; + key.texture = p_texture; + key.normalmap = p_normalmap; + key.specular = p_specular; + key.multimesh = p_multimesh; + key.texture_filter = p_filter; + key.texture_repeat = p_repeat; + + TextureBinding *binding; + TextureBindingID id; + { + TextureBindingID *idptr = bindings.texture_key_bindings.getptr(key); + + if (!idptr) { + id = bindings.id_generator++; + bindings.texture_key_bindings[key] = id; + binding = memnew(TextureBinding); + binding->key = key; + binding->id = id; + + bindings.texture_bindings[id] = binding; + + } else { + id = *idptr; + binding = bindings.texture_bindings[id]; + } + } + + binding->reference_count++; + + if (binding->to_dispose.in_list()) { + //was queued for disposal previously, but ended up reused. + bindings.to_dispose_list.remove(&binding->to_dispose); + } + + if (binding->uniform_set.is_null() || !RD::get_singleton()->uniform_set_is_valid(binding->uniform_set)) { + binding->uniform_set = _create_texture_binding(p_texture, p_normalmap, p_specular, p_filter, p_repeat, p_multimesh); + } + + return id; +} + +void RasterizerCanvasRD::free_texture_binding(TextureBindingID p_binding) { + + TextureBinding **binding_ptr = bindings.texture_bindings.getptr(p_binding); + ERR_FAIL_COND(!binding_ptr); + TextureBinding *binding = *binding_ptr; + ERR_FAIL_COND(binding->reference_count == 0); + binding->reference_count--; + if (binding->reference_count == 0) { + bindings.to_dispose_list.add(&binding->to_dispose); + } +} + +void RasterizerCanvasRD::_dispose_bindings() { + + while (bindings.to_dispose_list.first()) { + TextureBinding *binding = bindings.to_dispose_list.first()->self(); + if (binding->uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(binding->uniform_set)) { + RD::get_singleton()->free(binding->uniform_set); + } + + bindings.texture_key_bindings.erase(binding->key); + bindings.texture_bindings.erase(binding->id); + bindings.to_dispose_list.remove(&binding->to_dispose); + memdelete(binding); + } +} + +RasterizerCanvas::PolygonID RasterizerCanvasRD::request_polygon(const Vector<int> &p_indices, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs, const Vector<int> &p_bones, const Vector<float> &p_weights) { + + // Care must be taken to generate array formats + // in ways where they could be reused, so we will + // put single-occuring elements first, and repeated + // elements later. This way the generated formats are + // the same no matter the length of the arrays. + // This dramatically reduces the amount of pipeline objects + // that need to be created for these formats. + + uint32_t vertex_count = p_points.size(); + uint32_t stride = 2; //vertices always repeat + if ((uint32_t)p_colors.size() == vertex_count || p_colors.size() == 1) { + stride += 4; + } + if ((uint32_t)p_uvs.size() == vertex_count) { + stride += 2; + } + if ((uint32_t)p_bones.size() == vertex_count * 4 && (uint32_t)p_weights.size() == vertex_count * 4) { + stride += 4; + } + + uint32_t buffer_size = stride * p_points.size(); + + Vector<uint8_t> polygon_buffer; + polygon_buffer.resize(buffer_size * sizeof(float)); + Vector<RD::VertexDescription> descriptions; + descriptions.resize(4); + Vector<RID> buffers; + buffers.resize(4); + + { + const uint8_t *r = polygon_buffer.ptr(); + float *fptr = (float *)r; + uint32_t *uptr = (uint32_t *)r; + uint32_t base_offset = 0; + { //vertices + RD::VertexDescription vd; + vd.format = RD::DATA_FORMAT_R32G32_SFLOAT; + vd.offset = base_offset * sizeof(float); + vd.location = RS::ARRAY_VERTEX; + vd.stride = stride * sizeof(float); + + descriptions.write[0] = vd; + + const Vector2 *points_ptr = p_points.ptr(); + + for (uint32_t i = 0; i < vertex_count; i++) { + fptr[base_offset + i * stride + 0] = points_ptr[i].x; + fptr[base_offset + i * stride + 1] = points_ptr[i].y; + } + + base_offset += 2; + } + + //colors + if ((uint32_t)p_colors.size() == vertex_count || p_colors.size() == 1) { + RD::VertexDescription vd; + vd.format = RD::DATA_FORMAT_R32G32B32A32_SFLOAT; + vd.offset = base_offset * sizeof(float); + vd.location = RS::ARRAY_COLOR; + vd.stride = stride * sizeof(float); + + descriptions.write[1] = vd; + + if (p_colors.size() == 1) { + Color color = p_colors[0]; + for (uint32_t i = 0; i < vertex_count; i++) { + fptr[base_offset + i * stride + 0] = color.r; + fptr[base_offset + i * stride + 1] = color.g; + fptr[base_offset + i * stride + 2] = color.b; + fptr[base_offset + i * stride + 3] = color.a; + } + } else { + const Color *color_ptr = p_colors.ptr(); + + for (uint32_t i = 0; i < vertex_count; i++) { + fptr[base_offset + i * stride + 0] = color_ptr[i].r; + fptr[base_offset + i * stride + 1] = color_ptr[i].g; + fptr[base_offset + i * stride + 2] = color_ptr[i].b; + fptr[base_offset + i * stride + 3] = color_ptr[i].a; + } + } + base_offset += 4; + } else { + RD::VertexDescription vd; + vd.format = RD::DATA_FORMAT_R32G32B32A32_SFLOAT; + vd.offset = 0; + vd.location = RS::ARRAY_COLOR; + vd.stride = 0; + + descriptions.write[1] = vd; + buffers.write[1] = storage->mesh_get_default_rd_buffer(RasterizerStorageRD::DEFAULT_RD_BUFFER_COLOR); + } + + //uvs + if ((uint32_t)p_uvs.size() == vertex_count) { + RD::VertexDescription vd; + vd.format = RD::DATA_FORMAT_R32G32_SFLOAT; + vd.offset = base_offset * sizeof(float); + vd.location = RS::ARRAY_TEX_UV; + vd.stride = stride * sizeof(float); + + descriptions.write[2] = vd; + + const Vector2 *uv_ptr = p_uvs.ptr(); + + for (uint32_t i = 0; i < vertex_count; i++) { + fptr[base_offset + i * stride + 0] = uv_ptr[i].x; + fptr[base_offset + i * stride + 1] = uv_ptr[i].y; + } + base_offset += 2; + } else { + RD::VertexDescription vd; + vd.format = RD::DATA_FORMAT_R32G32_SFLOAT; + vd.offset = 0; + vd.location = RS::ARRAY_TEX_UV; + vd.stride = 0; + + descriptions.write[2] = vd; + buffers.write[2] = storage->mesh_get_default_rd_buffer(RasterizerStorageRD::DEFAULT_RD_BUFFER_TEX_UV); + } + + //bones + if ((uint32_t)p_indices.size() == vertex_count * 4 && (uint32_t)p_weights.size() == vertex_count * 4) { + RD::VertexDescription vd; + vd.format = RD::DATA_FORMAT_R32G32B32A32_UINT; + vd.offset = base_offset * sizeof(float); + vd.location = RS::ARRAY_BONES; + vd.stride = stride * sizeof(float); + + descriptions.write[3] = vd; + + const int *bone_ptr = p_bones.ptr(); + const float *weight_ptr = p_weights.ptr(); + + for (uint32_t i = 0; i < vertex_count; i++) { + + uint16_t *bone16w = (uint16_t *)&uptr[base_offset + i * stride]; + uint16_t *weight16w = (uint16_t *)&uptr[base_offset + i * stride + 2]; + + bone16w[0] = bone_ptr[i * 4 + 0]; + bone16w[1] = bone_ptr[i * 4 + 1]; + bone16w[2] = bone_ptr[i * 4 + 2]; + bone16w[3] = bone_ptr[i * 4 + 3]; + + weight16w[0] = CLAMP(weight_ptr[i * 4 + 0] * 65535, 0, 65535); + weight16w[1] = CLAMP(weight_ptr[i * 4 + 1] * 65535, 0, 65535); + weight16w[2] = CLAMP(weight_ptr[i * 4 + 2] * 65535, 0, 65535); + weight16w[3] = CLAMP(weight_ptr[i * 4 + 3] * 65535, 0, 65535); + } + + base_offset += 4; + } else { + RD::VertexDescription vd; + vd.format = RD::DATA_FORMAT_R32G32B32A32_UINT; + vd.offset = 0; + vd.location = RS::ARRAY_BONES; + vd.stride = 0; + + descriptions.write[3] = vd; + buffers.write[3] = storage->mesh_get_default_rd_buffer(RasterizerStorageRD::DEFAULT_RD_BUFFER_BONES); + } + + //check that everything is as it should be + ERR_FAIL_COND_V(base_offset != stride, 0); //bug + } + + RD::VertexFormatID vertex_id = RD::get_singleton()->vertex_format_create(descriptions); + ERR_FAIL_COND_V(vertex_id == RD::INVALID_ID, 0); + + PolygonBuffers pb; + pb.vertex_buffer = RD::get_singleton()->vertex_buffer_create(polygon_buffer.size(), polygon_buffer); + for (int i = 0; i < descriptions.size(); i++) { + if (buffers[i] == RID()) { //if put in vertex, use as vertex + buffers.write[i] = pb.vertex_buffer; + } + } + + pb.vertex_array = RD::get_singleton()->vertex_array_create(p_points.size(), vertex_id, buffers); + + if (p_indices.size()) { + //create indices, as indices were requested + Vector<uint8_t> index_buffer; + index_buffer.resize(p_indices.size() * sizeof(int32_t)); + { + uint8_t *w = index_buffer.ptrw(); + copymem(w, p_indices.ptr(), sizeof(int32_t) * p_indices.size()); + } + pb.index_buffer = RD::get_singleton()->index_buffer_create(p_indices.size(), RD::INDEX_BUFFER_FORMAT_UINT32, index_buffer); + pb.indices = RD::get_singleton()->index_array_create(pb.index_buffer, 0, p_indices.size()); + } + + pb.vertex_format_id = vertex_id; + + PolygonID id = polygon_buffers.last_id++; + + polygon_buffers.polygons[id] = pb; + + return id; +} + +void RasterizerCanvasRD::free_polygon(PolygonID p_polygon) { + + PolygonBuffers *pb_ptr = polygon_buffers.polygons.getptr(p_polygon); + ERR_FAIL_COND(!pb_ptr); + + PolygonBuffers &pb = *pb_ptr; + + if (pb.indices.is_valid()) { + RD::get_singleton()->free(pb.indices); + } + if (pb.index_buffer.is_valid()) { + RD::get_singleton()->free(pb.index_buffer); + } + + RD::get_singleton()->free(pb.vertex_array); + RD::get_singleton()->free(pb.vertex_buffer); + + polygon_buffers.polygons.erase(p_polygon); +} + +Size2i RasterizerCanvasRD::_bind_texture_binding(TextureBindingID p_binding, RD::DrawListID p_draw_list, uint32_t &flags) { + + TextureBinding **texture_binding_ptr = bindings.texture_bindings.getptr(p_binding); + ERR_FAIL_COND_V(!texture_binding_ptr, Size2i()); + TextureBinding *texture_binding = *texture_binding_ptr; + + if (texture_binding->key.normalmap.is_valid()) { + flags |= FLAGS_DEFAULT_NORMAL_MAP_USED; + } + if (texture_binding->key.specular.is_valid()) { + flags |= FLAGS_DEFAULT_SPECULAR_MAP_USED; + } + + if (!RD::get_singleton()->uniform_set_is_valid(texture_binding->uniform_set)) { + //texture may have changed (erased or replaced, see if we can fix) + texture_binding->uniform_set = _create_texture_binding(texture_binding->key.texture, texture_binding->key.normalmap, texture_binding->key.specular, texture_binding->key.texture_filter, texture_binding->key.texture_repeat, texture_binding->key.multimesh); + ERR_FAIL_COND_V(!texture_binding->uniform_set.is_valid(), Size2i(1, 1)); + } + + RD::get_singleton()->draw_list_bind_uniform_set(p_draw_list, texture_binding->uniform_set, 0); + if (texture_binding->key.texture.is_valid()) { + return storage->texture_2d_get_size(texture_binding->key.texture); + } else { + return Size2i(1, 1); + } +} + +//////////////////// +void RasterizerCanvasRD::_render_item(RD::DrawListID p_draw_list, const Item *p_item, RD::FramebufferFormatID p_framebuffer_format, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, Light *p_lights, PipelineVariants *p_pipeline_variants) { + + //create an empty push constant + + PushConstant push_constant; + Transform2D base_transform = p_canvas_transform_inverse * p_item->final_transform; + _update_transform_2d_to_mat2x3(base_transform, push_constant.world); + + Color base_color = p_item->final_modulate; + + for (int i = 0; i < 4; i++) { + push_constant.modulation[i] = 0; + push_constant.ninepatch_margins[i] = 0; + push_constant.src_rect[i] = 0; + push_constant.dst_rect[i] = 0; + } + push_constant.flags = 0; + push_constant.color_texture_pixel_size[0] = 0; + push_constant.color_texture_pixel_size[1] = 0; + + push_constant.pad[0] = 0; + push_constant.pad[1] = 0; + + push_constant.lights[0] = 0; + push_constant.lights[1] = 0; + push_constant.lights[2] = 0; + push_constant.lights[3] = 0; + + uint32_t base_flags = 0; + + bool light_uniform_set_dirty = false; + + if (!p_item->custom_data) { + p_item->custom_data = memnew(ItemStateData); + light_uniform_set_dirty = true; + } + + ItemStateData *state_data = (ItemStateData *)p_item->custom_data; + + Light *light_cache[DEFAULT_MAX_LIGHTS_PER_ITEM]; + uint16_t light_count = 0; + PipelineLightMode light_mode; + + { + + Light *light = p_lights; + + while (light) { + + if (light->render_index_cache >= 0 && p_item->light_mask & light->item_mask && p_item->z_final >= light->z_min && p_item->z_final <= light->z_max && p_item->global_rect_cache.intersects_transformed(light->xform_cache, light->rect_cache)) { + + uint32_t light_index = light->render_index_cache; + push_constant.lights[light_count >> 2] |= light_index << ((light_count & 3) * 8); + + if (!light_uniform_set_dirty && (state_data->light_cache[light_count].light != light || state_data->light_cache[light_count].light_version != light->version)) { + light_uniform_set_dirty = true; + } + + light_cache[light_count] = light; + + light_count++; + if (light->mode == RS::CANVAS_LIGHT_MODE_MASK) { + base_flags |= FLAGS_USING_LIGHT_MASK; + } + if (light_count == state.max_lights_per_item) { + break; + } + } + light = light->next_ptr; + } + + if (light_count != state_data->light_cache_count) { + light_uniform_set_dirty = true; + } + base_flags |= light_count << FLAGS_LIGHT_COUNT_SHIFT; + } + + { + + RID &canvas_item_state = light_count ? state_data->state_uniform_set_with_light : state_data->state_uniform_set; + + bool invalid_uniform = canvas_item_state.is_valid() && !RD::get_singleton()->uniform_set_is_valid(canvas_item_state); + + if (canvas_item_state.is_null() || invalid_uniform || (light_count > 0 && light_uniform_set_dirty)) { + //re create canvas state + Vector<RD::Uniform> uniforms; + + if (state_data->state_uniform_set_with_light.is_valid() && !invalid_uniform) { + RD::get_singleton()->free(canvas_item_state); + } + + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; + u.binding = 0; + u.ids.push_back(state.canvas_state_buffer); + uniforms.push_back(u); + } + + if (false && p_item->skeleton.is_valid()) { + //bind skeleton stuff + } else { + //bind default + + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_TEXTURE_BUFFER; + u.binding = 1; + u.ids.push_back(shader.default_skeleton_texture_buffer); + uniforms.push_back(u); + } + + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; + u.binding = 2; + u.ids.push_back(shader.default_skeleton_uniform_buffer); + uniforms.push_back(u); + } + } + + //validate and update lighs if they are being used + + if (light_count > 0) { + //recreate uniform set + + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; + u.binding = 3; + u.ids.push_back(state.lights_uniform_buffer); + uniforms.push_back(u); + } + + { + + RD::Uniform u_lights; + u_lights.type = RD::UNIFORM_TYPE_TEXTURE; + u_lights.binding = 4; + + RD::Uniform u_shadows; + u_shadows.type = RD::UNIFORM_TYPE_TEXTURE; + u_shadows.binding = 5; + + //lights + for (uint32_t i = 0; i < state.max_lights_per_item; i++) { + if (i < light_count) { + + CanvasLight *cl = canvas_light_owner.getornull(light_cache[i]->light_internal); + ERR_CONTINUE(!cl); + + RID rd_texture; + + if (cl->texture.is_valid()) { + rd_texture = storage->texture_get_rd_texture(cl->texture); + } + if (rd_texture.is_valid()) { + u_lights.ids.push_back(rd_texture); + } else { + u_lights.ids.push_back(storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_WHITE)); + } + if (cl->shadow.texture.is_valid()) { + u_shadows.ids.push_back(cl->shadow.texture); + } else { + u_shadows.ids.push_back(storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_BLACK)); + } + } else { + u_lights.ids.push_back(storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_WHITE)); + u_shadows.ids.push_back(storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_BLACK)); + } + } + + uniforms.push_back(u_lights); + uniforms.push_back(u_shadows); + } + + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_SAMPLER; + u.binding = 6; + u.ids.push_back(state.shadow_sampler); + uniforms.push_back(u); + } + + canvas_item_state = RD::get_singleton()->uniform_set_create(uniforms, shader.default_version_rd_shader_light, 2); + } else { + canvas_item_state = RD::get_singleton()->uniform_set_create(uniforms, shader.default_version_rd_shader, 2); + } + } + + RD::get_singleton()->draw_list_bind_uniform_set(p_draw_list, canvas_item_state, 2); + } + + light_mode = light_count > 0 ? PIPELINE_LIGHT_MODE_ENABLED : PIPELINE_LIGHT_MODE_DISABLED; + + PipelineVariants *pipeline_variants = p_pipeline_variants; + + bool reclip = false; + + const Item::Command *c = p_item->commands; + while (c) { + push_constant.flags = base_flags; //reset on each command for sanity + push_constant.specular_shininess = 0xFFFFFFFF; + + switch (c->type) { + case Item::Command::TYPE_RECT: { + + const Item::CommandRect *rect = static_cast<const Item::CommandRect *>(c); + + //bind pipeline + { + RID pipeline = pipeline_variants->variants[light_mode][PIPELINE_VARIANT_QUAD].get_render_pipeline(RD::INVALID_ID, p_framebuffer_format); + RD::get_singleton()->draw_list_bind_render_pipeline(p_draw_list, pipeline); + } + + //bind textures + + Size2 texpixel_size; + { + texpixel_size = _bind_texture_binding(rect->texture_binding.binding_id, p_draw_list, push_constant.flags); + texpixel_size.x = 1.0 / texpixel_size.x; + texpixel_size.y = 1.0 / texpixel_size.y; + } + + if (rect->specular_shininess.a < 0.999) { + push_constant.flags |= FLAGS_DEFAULT_SPECULAR_MAP_USED; + } + + _update_specular_shininess(rect->specular_shininess, &push_constant.specular_shininess); + + Rect2 src_rect; + Rect2 dst_rect; + + if (texpixel_size != Vector2()) { + push_constant.color_texture_pixel_size[0] = texpixel_size.x; + push_constant.color_texture_pixel_size[1] = texpixel_size.y; + + src_rect = (rect->flags & CANVAS_RECT_REGION) ? Rect2(rect->source.position * texpixel_size, rect->source.size * texpixel_size) : Rect2(0, 0, 1, 1); + dst_rect = Rect2(rect->rect.position, rect->rect.size); + + if (dst_rect.size.width < 0) { + dst_rect.position.x += dst_rect.size.width; + dst_rect.size.width *= -1; + } + if (dst_rect.size.height < 0) { + dst_rect.position.y += dst_rect.size.height; + dst_rect.size.height *= -1; + } + + if (rect->flags & CANVAS_RECT_FLIP_H) { + src_rect.size.x *= -1; + } + + if (rect->flags & CANVAS_RECT_FLIP_V) { + src_rect.size.y *= -1; + } + + if (rect->flags & CANVAS_RECT_TRANSPOSE) { + dst_rect.size.x *= -1; // Encoding in the dst_rect.z uniform + } + + if (rect->flags & CANVAS_RECT_CLIP_UV) { + push_constant.flags |= FLAGS_CLIP_RECT_UV; + } + + } else { + dst_rect = Rect2(rect->rect.position, rect->rect.size); + + if (dst_rect.size.width < 0) { + dst_rect.position.x += dst_rect.size.width; + dst_rect.size.width *= -1; + } + if (dst_rect.size.height < 0) { + dst_rect.position.y += dst_rect.size.height; + dst_rect.size.height *= -1; + } + + src_rect = Rect2(0, 0, 1, 1); + texpixel_size = Vector2(1, 1); + } + + push_constant.modulation[0] = rect->modulate.r * base_color.r; + push_constant.modulation[1] = rect->modulate.g * base_color.g; + push_constant.modulation[2] = rect->modulate.b * base_color.b; + push_constant.modulation[3] = rect->modulate.a * base_color.a; + + push_constant.src_rect[0] = src_rect.position.x; + push_constant.src_rect[1] = src_rect.position.y; + push_constant.src_rect[2] = src_rect.size.width; + push_constant.src_rect[3] = src_rect.size.height; + + push_constant.dst_rect[0] = dst_rect.position.x; + push_constant.dst_rect[1] = dst_rect.position.y; + push_constant.dst_rect[2] = dst_rect.size.width; + push_constant.dst_rect[3] = dst_rect.size.height; + + push_constant.color_texture_pixel_size[0] = texpixel_size.x; + push_constant.color_texture_pixel_size[1] = texpixel_size.y; + + RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(PushConstant)); + RD::get_singleton()->draw_list_bind_index_array(p_draw_list, shader.quad_index_array); + RD::get_singleton()->draw_list_draw(p_draw_list, true); + + } break; + + case Item::Command::TYPE_NINEPATCH: { + + const Item::CommandNinePatch *np = static_cast<const Item::CommandNinePatch *>(c); + + //bind pipeline + { + RID pipeline = pipeline_variants->variants[light_mode][PIPELINE_VARIANT_NINEPATCH].get_render_pipeline(RD::INVALID_ID, p_framebuffer_format); + RD::get_singleton()->draw_list_bind_render_pipeline(p_draw_list, pipeline); + } + + //bind textures + + Size2 texpixel_size; + { + texpixel_size = _bind_texture_binding(np->texture_binding.binding_id, p_draw_list, push_constant.flags); + texpixel_size.x = 1.0 / texpixel_size.x; + texpixel_size.y = 1.0 / texpixel_size.y; + } + + if (np->specular_shininess.a < 0.999) { + push_constant.flags |= FLAGS_DEFAULT_SPECULAR_MAP_USED; + } + + _update_specular_shininess(np->specular_shininess, &push_constant.specular_shininess); + + Rect2 src_rect; + Rect2 dst_rect(np->rect.position.x, np->rect.position.y, np->rect.size.x, np->rect.size.y); + + if (texpixel_size == Size2()) { + + texpixel_size = Size2(1, 1); + src_rect = Rect2(0, 0, 1, 1); + + } else { + + if (np->source != Rect2()) { + src_rect = Rect2(np->source.position.x * texpixel_size.width, np->source.position.y * texpixel_size.height, np->source.size.x * texpixel_size.width, np->source.size.y * texpixel_size.height); + texpixel_size = Size2(1.0 / np->source.size.width, 1.0 / np->source.size.height); + } else { + src_rect = Rect2(0, 0, 1, 1); + } + } + + push_constant.modulation[0] = np->color.r * base_color.r; + push_constant.modulation[1] = np->color.g * base_color.g; + push_constant.modulation[2] = np->color.b * base_color.b; + push_constant.modulation[3] = np->color.a * base_color.a; + + push_constant.src_rect[0] = src_rect.position.x; + push_constant.src_rect[1] = src_rect.position.y; + push_constant.src_rect[2] = src_rect.size.width; + push_constant.src_rect[3] = src_rect.size.height; + + push_constant.dst_rect[0] = dst_rect.position.x; + push_constant.dst_rect[1] = dst_rect.position.y; + push_constant.dst_rect[2] = dst_rect.size.width; + push_constant.dst_rect[3] = dst_rect.size.height; + + push_constant.color_texture_pixel_size[0] = texpixel_size.x; + push_constant.color_texture_pixel_size[1] = texpixel_size.y; + + push_constant.flags |= int(np->axis_x) << FLAGS_NINEPATCH_H_MODE_SHIFT; + push_constant.flags |= int(np->axis_y) << FLAGS_NINEPATCH_V_MODE_SHIFT; + + if (np->draw_center) { + push_constant.flags |= FLAGS_NINEPACH_DRAW_CENTER; + } + + push_constant.ninepatch_margins[0] = np->margin[MARGIN_LEFT]; + push_constant.ninepatch_margins[1] = np->margin[MARGIN_TOP]; + push_constant.ninepatch_margins[2] = np->margin[MARGIN_RIGHT]; + push_constant.ninepatch_margins[3] = np->margin[MARGIN_BOTTOM]; + + RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(PushConstant)); + RD::get_singleton()->draw_list_bind_index_array(p_draw_list, shader.quad_index_array); + RD::get_singleton()->draw_list_draw(p_draw_list, true); + + } break; + case Item::Command::TYPE_POLYGON: { + + const Item::CommandPolygon *polygon = static_cast<const Item::CommandPolygon *>(c); + + PolygonBuffers *pb = polygon_buffers.polygons.getptr(polygon->polygon.polygon_id); + ERR_CONTINUE(!pb); + //bind pipeline + { + static const PipelineVariant variant[RS::PRIMITIVE_MAX] = { PIPELINE_VARIANT_ATTRIBUTE_POINTS, PIPELINE_VARIANT_ATTRIBUTE_LINES, PIPELINE_VARIANT_ATTRIBUTE_LINES_STRIP, PIPELINE_VARIANT_ATTRIBUTE_TRIANGLES, PIPELINE_VARIANT_ATTRIBUTE_TRIANGLE_STRIP }; + ERR_CONTINUE(polygon->primitive < 0 || polygon->primitive >= RS::PRIMITIVE_MAX); + RID pipeline = pipeline_variants->variants[light_mode][variant[polygon->primitive]].get_render_pipeline(pb->vertex_format_id, p_framebuffer_format); + RD::get_singleton()->draw_list_bind_render_pipeline(p_draw_list, pipeline); + } + + if (polygon->primitive == RS::PRIMITIVE_LINES) { + //not supported in most hardware, so pointless + //RD::get_singleton()->draw_list_set_line_width(p_draw_list, polygon->line_width); + } + + //bind textures + + Size2 texpixel_size; + { + texpixel_size = _bind_texture_binding(polygon->texture_binding.binding_id, p_draw_list, push_constant.flags); + texpixel_size.x = 1.0 / texpixel_size.x; + texpixel_size.y = 1.0 / texpixel_size.y; + } + + if (polygon->specular_shininess.a < 0.999) { + push_constant.flags |= FLAGS_DEFAULT_SPECULAR_MAP_USED; + } + + _update_specular_shininess(polygon->specular_shininess, &push_constant.specular_shininess); + + push_constant.modulation[0] = base_color.r; + push_constant.modulation[1] = base_color.g; + push_constant.modulation[2] = base_color.b; + push_constant.modulation[3] = base_color.a; + + for (int j = 0; j < 4; j++) { + push_constant.src_rect[j] = 0; + push_constant.dst_rect[j] = 0; + push_constant.ninepatch_margins[j] = 0; + } + + push_constant.color_texture_pixel_size[0] = texpixel_size.x; + push_constant.color_texture_pixel_size[1] = texpixel_size.y; + + RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(PushConstant)); + RD::get_singleton()->draw_list_bind_vertex_array(p_draw_list, pb->vertex_array); + if (pb->indices.is_valid()) { + RD::get_singleton()->draw_list_bind_index_array(p_draw_list, pb->indices); + } + RD::get_singleton()->draw_list_draw(p_draw_list, pb->indices.is_valid()); + + } break; + case Item::Command::TYPE_PRIMITIVE: { + + const Item::CommandPrimitive *primitive = static_cast<const Item::CommandPrimitive *>(c); + + //bind pipeline + { + static const PipelineVariant variant[4] = { PIPELINE_VARIANT_PRIMITIVE_POINTS, PIPELINE_VARIANT_PRIMITIVE_LINES, PIPELINE_VARIANT_PRIMITIVE_TRIANGLES, PIPELINE_VARIANT_PRIMITIVE_TRIANGLES }; + ERR_CONTINUE(primitive->point_count == 0 || primitive->point_count > 4); + RID pipeline = pipeline_variants->variants[light_mode][variant[primitive->point_count - 1]].get_render_pipeline(RD::INVALID_ID, p_framebuffer_format); + RD::get_singleton()->draw_list_bind_render_pipeline(p_draw_list, pipeline); + } + + //bind textures + + { + _bind_texture_binding(primitive->texture_binding.binding_id, p_draw_list, push_constant.flags); + } + + if (primitive->specular_shininess.a < 0.999) { + push_constant.flags |= FLAGS_DEFAULT_SPECULAR_MAP_USED; + } + + _update_specular_shininess(primitive->specular_shininess, &push_constant.specular_shininess); + + RD::get_singleton()->draw_list_bind_index_array(p_draw_list, primitive_arrays.index_array[MIN(3, primitive->point_count) - 1]); + + for (uint32_t j = 0; j < MIN(3, primitive->point_count); j++) { + push_constant.points[j * 2 + 0] = primitive->points[j].x; + push_constant.points[j * 2 + 1] = primitive->points[j].y; + push_constant.uvs[j * 2 + 0] = primitive->uvs[j].x; + push_constant.uvs[j * 2 + 1] = primitive->uvs[j].y; + Color col = primitive->colors[j] * base_color; + push_constant.colors[j * 2 + 0] = (uint32_t(Math::make_half_float(col.g)) << 16) | Math::make_half_float(col.r); + push_constant.colors[j * 2 + 1] = (uint32_t(Math::make_half_float(col.a)) << 16) | Math::make_half_float(col.b); + } + RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(PushConstant)); + RD::get_singleton()->draw_list_draw(p_draw_list, true); + + if (primitive->point_count == 4) { + for (uint32_t j = 1; j < 3; j++) { + //second half of triangle + push_constant.points[j * 2 + 0] = primitive->points[j + 1].x; + push_constant.points[j * 2 + 1] = primitive->points[j + 1].y; + push_constant.uvs[j * 2 + 0] = primitive->uvs[j + 1].x; + push_constant.uvs[j * 2 + 1] = primitive->uvs[j + 1].y; + Color col = primitive->colors[j + 1] * base_color; + push_constant.colors[j * 2 + 0] = (uint32_t(Math::make_half_float(col.g)) << 16) | Math::make_half_float(col.r); + push_constant.colors[j * 2 + 1] = (uint32_t(Math::make_half_float(col.a)) << 16) | Math::make_half_float(col.b); + } + + RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(PushConstant)); + RD::get_singleton()->draw_list_draw(p_draw_list, true); + } + + } break; + case Item::Command::TYPE_MESH: + case Item::Command::TYPE_MULTIMESH: + case Item::Command::TYPE_PARTICLES: { + ERR_PRINT("FIXME: Mesh, MultiMesh and Particles render commands are unimplemented currently, they need to be ported to the 4.0 rendering architecture."); +#ifndef _MSC_VER +#warning Item::Command types for Mesh, MultiMesh and Particles need to be implemented. +#endif + // See #if 0'ed code below to port from GLES3. + } break; + +#if 0 + case Item::Command::TYPE_MESH: { + + Item::CommandMesh *mesh = static_cast<Item::CommandMesh *>(c); + _set_texture_rect_mode(false); + + RasterizerStorageGLES3::Texture *texture = _bind_canvas_texture(mesh->texture, mesh->normal_map); + + if (texture) { + Size2 texpixel_size(1.0 / texture->width, 1.0 / texture->height); + state.canvas_shader.set_uniform(CanvasShaderGLES3::COLOR_TEXPIXEL_SIZE, texpixel_size); + } + + state.canvas_shader.set_uniform(CanvasShaderGLES3::MODELVIEW_MATRIX, state.final_transform * mesh->transform); + + RasterizerStorageGLES3::Mesh *mesh_data = storage->mesh_owner.getornull(mesh->mesh); + if (mesh_data) { + + for (int j = 0; j < mesh_data->surfaces.size(); j++) { + RasterizerStorageGLES3::Surface *s = mesh_data->surfaces[j]; + // materials are ignored in 2D meshes, could be added but many things (ie, lighting mode, reading from screen, etc) would break as they are not meant be set up at this point of drawing + glBindVertexArray(s->array_id); + + glVertexAttrib4f(RS::ARRAY_COLOR, mesh->modulate.r, mesh->modulate.g, mesh->modulate.b, mesh->modulate.a); + + if (s->index_array_len) { + glDrawElements(gl_primitive[s->primitive], s->index_array_len, (s->array_len >= (1 << 16)) ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT, 0); + } else { + glDrawArrays(gl_primitive[s->primitive], 0, s->array_len); + } + + glBindVertexArray(0); + } + } + state.canvas_shader.set_uniform(CanvasShaderGLES3::MODELVIEW_MATRIX, state.final_transform); + + } break; + case Item::Command::TYPE_MULTIMESH: { + + Item::CommandMultiMesh *mmesh = static_cast<Item::CommandMultiMesh *>(c); + + RasterizerStorageGLES3::MultiMesh *multi_mesh = storage->multimesh_owner.getornull(mmesh->multimesh); + + if (!multi_mesh) + break; + + RasterizerStorageGLES3::Mesh *mesh_data = storage->mesh_owner.getornull(multi_mesh->mesh); + + if (!mesh_data) + break; + + RasterizerStorageGLES3::Texture *texture = _bind_canvas_texture(mmesh->texture, mmesh->normal_map); + + state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_INSTANCE_CUSTOM, multi_mesh->custom_data_format != RS::MULTIMESH_CUSTOM_DATA_NONE); + state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_INSTANCING, true); + //reset shader and force rebind + state.using_texture_rect = true; + _set_texture_rect_mode(false); + + if (texture) { + Size2 texpixel_size(1.0 / texture->width, 1.0 / texture->height); + state.canvas_shader.set_uniform(CanvasShaderGLES3::COLOR_TEXPIXEL_SIZE, texpixel_size); + } + + int amount = MIN(multi_mesh->size, multi_mesh->visible_instances); + + if (amount == -1) { + amount = multi_mesh->size; + } + + for (int j = 0; j < mesh_data->surfaces.size(); j++) { + RasterizerStorageGLES3::Surface *s = mesh_data->surfaces[j]; + // materials are ignored in 2D meshes, could be added but many things (ie, lighting mode, reading from screen, etc) would break as they are not meant be set up at this point of drawing + glBindVertexArray(s->instancing_array_id); + + glBindBuffer(GL_ARRAY_BUFFER, multi_mesh->buffer); //modify the buffer + + int stride = (multi_mesh->xform_floats + multi_mesh->color_floats + multi_mesh->custom_data_floats) * 4; + glEnableVertexAttribArray(8); + glVertexAttribPointer(8, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(0)); + glVertexAttribDivisor(8, 1); + glEnableVertexAttribArray(9); + glVertexAttribPointer(9, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(4 * 4)); + glVertexAttribDivisor(9, 1); + + int color_ofs; + + if (multi_mesh->transform_format == RS::MULTIMESH_TRANSFORM_3D) { + glEnableVertexAttribArray(10); + glVertexAttribPointer(10, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(8 * 4)); + glVertexAttribDivisor(10, 1); + color_ofs = 12 * 4; + } else { + glDisableVertexAttribArray(10); + glVertexAttrib4f(10, 0, 0, 1, 0); + color_ofs = 8 * 4; + } + + int custom_data_ofs = color_ofs; + + switch (multi_mesh->color_format) { + + case RS::MULTIMESH_COLOR_NONE: { + glDisableVertexAttribArray(11); + glVertexAttrib4f(11, 1, 1, 1, 1); + } break; + case RS::MULTIMESH_COLOR_8BIT: { + glEnableVertexAttribArray(11); + glVertexAttribPointer(11, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride, CAST_INT_TO_UCHAR_PTR(color_ofs)); + glVertexAttribDivisor(11, 1); + custom_data_ofs += 4; + + } break; + case RS::MULTIMESH_COLOR_FLOAT: { + glEnableVertexAttribArray(11); + glVertexAttribPointer(11, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(color_ofs)); + glVertexAttribDivisor(11, 1); + custom_data_ofs += 4 * 4; + } break; + } + + switch (multi_mesh->custom_data_format) { + + case RS::MULTIMESH_CUSTOM_DATA_NONE: { + glDisableVertexAttribArray(12); + glVertexAttrib4f(12, 1, 1, 1, 1); + } break; + case RS::MULTIMESH_CUSTOM_DATA_8BIT: { + glEnableVertexAttribArray(12); + glVertexAttribPointer(12, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride, CAST_INT_TO_UCHAR_PTR(custom_data_ofs)); + glVertexAttribDivisor(12, 1); + + } break; + case RS::MULTIMESH_CUSTOM_DATA_FLOAT: { + glEnableVertexAttribArray(12); + glVertexAttribPointer(12, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(custom_data_ofs)); + glVertexAttribDivisor(12, 1); + } break; + } + + if (s->index_array_len) { + glDrawElementsInstanced(gl_primitive[s->primitive], s->index_array_len, (s->array_len >= (1 << 16)) ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT, 0, amount); + } else { + glDrawArraysInstanced(gl_primitive[s->primitive], 0, s->array_len, amount); + } + + glBindVertexArray(0); + } + + state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_INSTANCE_CUSTOM, false); + state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_INSTANCING, false); + state.using_texture_rect = true; + _set_texture_rect_mode(false); + + } break; + case Item::Command::TYPE_PARTICLES: { + + Item::CommandParticles *particles_cmd = static_cast<Item::CommandParticles *>(c); + + RasterizerStorageGLES3::Particles *particles = storage->particles_owner.getornull(particles_cmd->particles); + if (!particles) + break; + + if (particles->inactive && !particles->emitting) + break; + + glVertexAttrib4f(RS::ARRAY_COLOR, 1, 1, 1, 1); //not used, so keep white + + RenderingServerRaster::redraw_request(); + + storage->particles_request_process(particles_cmd->particles); + //enable instancing + + state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_INSTANCE_CUSTOM, true); + state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_PARTICLES, true); + state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_INSTANCING, true); + //reset shader and force rebind + state.using_texture_rect = true; + _set_texture_rect_mode(false); + + RasterizerStorageGLES3::Texture *texture = _bind_canvas_texture(particles_cmd->texture, particles_cmd->normal_map); + + if (texture) { + Size2 texpixel_size(1.0 / texture->width, 1.0 / texture->height); + state.canvas_shader.set_uniform(CanvasShaderGLES3::COLOR_TEXPIXEL_SIZE, texpixel_size); + } else { + state.canvas_shader.set_uniform(CanvasShaderGLES3::COLOR_TEXPIXEL_SIZE, Vector2(1.0, 1.0)); + } + + if (!particles->use_local_coords) { + + Transform2D inv_xf; + inv_xf.set_axis(0, Vector2(particles->emission_transform.basis.get_axis(0).x, particles->emission_transform.basis.get_axis(0).y)); + inv_xf.set_axis(1, Vector2(particles->emission_transform.basis.get_axis(1).x, particles->emission_transform.basis.get_axis(1).y)); + inv_xf.set_origin(Vector2(particles->emission_transform.get_origin().x, particles->emission_transform.get_origin().y)); + inv_xf.affine_invert(); + + state.canvas_shader.set_uniform(CanvasShaderGLES3::MODELVIEW_MATRIX, state.final_transform * inv_xf); + } + + glBindVertexArray(data.particle_quad_array); //use particle quad array + glBindBuffer(GL_ARRAY_BUFFER, particles->particle_buffers[0]); //bind particle buffer + + int stride = sizeof(float) * 4 * 6; + + int amount = particles->amount; + + if (particles->draw_order != RS::PARTICLES_DRAW_ORDER_LIFETIME) { + + glEnableVertexAttribArray(8); //xform x + glVertexAttribPointer(8, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(sizeof(float) * 4 * 3)); + glVertexAttribDivisor(8, 1); + glEnableVertexAttribArray(9); //xform y + glVertexAttribPointer(9, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(sizeof(float) * 4 * 4)); + glVertexAttribDivisor(9, 1); + glEnableVertexAttribArray(10); //xform z + glVertexAttribPointer(10, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(sizeof(float) * 4 * 5)); + glVertexAttribDivisor(10, 1); + glEnableVertexAttribArray(11); //color + glVertexAttribPointer(11, 4, GL_FLOAT, GL_FALSE, stride, NULL); + glVertexAttribDivisor(11, 1); + glEnableVertexAttribArray(12); //custom + glVertexAttribPointer(12, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(sizeof(float) * 4 * 2)); + glVertexAttribDivisor(12, 1); + + glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, amount); + } else { + //split + int split = int(Math::ceil(particles->phase * particles->amount)); + + if (amount - split > 0) { + glEnableVertexAttribArray(8); //xform x + glVertexAttribPointer(8, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(stride * split + sizeof(float) * 4 * 3)); + glVertexAttribDivisor(8, 1); + glEnableVertexAttribArray(9); //xform y + glVertexAttribPointer(9, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(stride * split + sizeof(float) * 4 * 4)); + glVertexAttribDivisor(9, 1); + glEnableVertexAttribArray(10); //xform z + glVertexAttribPointer(10, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(stride * split + sizeof(float) * 4 * 5)); + glVertexAttribDivisor(10, 1); + glEnableVertexAttribArray(11); //color + glVertexAttribPointer(11, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(stride * split + 0)); + glVertexAttribDivisor(11, 1); + glEnableVertexAttribArray(12); //custom + glVertexAttribPointer(12, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(stride * split + sizeof(float) * 4 * 2)); + glVertexAttribDivisor(12, 1); + + glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, amount - split); + } + + if (split > 0) { + glEnableVertexAttribArray(8); //xform x + glVertexAttribPointer(8, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(sizeof(float) * 4 * 3)); + glVertexAttribDivisor(8, 1); + glEnableVertexAttribArray(9); //xform y + glVertexAttribPointer(9, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(sizeof(float) * 4 * 4)); + glVertexAttribDivisor(9, 1); + glEnableVertexAttribArray(10); //xform z + glVertexAttribPointer(10, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(sizeof(float) * 4 * 5)); + glVertexAttribDivisor(10, 1); + glEnableVertexAttribArray(11); //color + glVertexAttribPointer(11, 4, GL_FLOAT, GL_FALSE, stride, NULL); + glVertexAttribDivisor(11, 1); + glEnableVertexAttribArray(12); //custom + glVertexAttribPointer(12, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(sizeof(float) * 4 * 2)); + glVertexAttribDivisor(12, 1); + + glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, split); + } + } + + glBindVertexArray(0); + + state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_INSTANCE_CUSTOM, false); + state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_PARTICLES, false); + state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_INSTANCING, false); + state.using_texture_rect = true; + _set_texture_rect_mode(false); + + } break; +#endif + case Item::Command::TYPE_TRANSFORM: { + + const Item::CommandTransform *transform = static_cast<const Item::CommandTransform *>(c); + _update_transform_2d_to_mat2x3(base_transform * transform->xform, push_constant.world); + + } break; + case Item::Command::TYPE_CLIP_IGNORE: { + + const Item::CommandClipIgnore *ci = static_cast<const Item::CommandClipIgnore *>(c); + if (current_clip) { + + if (ci->ignore != reclip) { + + if (ci->ignore) { + RD::get_singleton()->draw_list_disable_scissor(p_draw_list); + reclip = true; + } else { + + RD::get_singleton()->draw_list_enable_scissor(p_draw_list, current_clip->final_clip_rect); + reclip = false; + } + } + } + + } break; + } + + c = c->next; + } + + if (current_clip && reclip) { + //will make it re-enable clipping if needed afterwards + current_clip = NULL; + } +} + +void RasterizerCanvasRD::_render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, RID p_screen_uniform_set) { + + Item *current_clip = NULL; + + Transform2D canvas_transform_inverse = p_canvas_transform_inverse; + + RID framebuffer = storage->render_target_get_rd_framebuffer(p_to_render_target); + + Vector<Color> clear_colors; + bool clear = false; + if (storage->render_target_is_clear_requested(p_to_render_target)) { + clear = true; + clear_colors.push_back(storage->render_target_get_clear_request_color(p_to_render_target)); + storage->render_target_disable_clear_request(p_to_render_target); + } +#ifndef _MSC_VER +#warning TODO obtain from framebuffer format eventually when this is implemented +#endif + + RD::FramebufferFormatID fb_format = RD::get_singleton()->framebuffer_get_format(framebuffer); + + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, clear ? RD::INITIAL_ACTION_CLEAR : RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD, clear_colors); + + if (p_screen_uniform_set.is_valid()) { + RD::get_singleton()->draw_list_bind_uniform_set(draw_list, p_screen_uniform_set, 3); + } + RID prev_material; + + PipelineVariants *pipeline_variants = &shader.pipeline_variants; + + for (int i = 0; i < p_item_count; i++) { + + Item *ci = items[i]; + + if (current_clip != ci->final_clip_owner) { + + current_clip = ci->final_clip_owner; + + //setup clip + if (current_clip) { + + RD::get_singleton()->draw_list_enable_scissor(draw_list, current_clip->final_clip_rect); + + } else { + + RD::get_singleton()->draw_list_disable_scissor(draw_list); + } + } + + if (ci->material != prev_material) { + + MaterialData *material_data = NULL; + if (ci->material.is_valid()) { + material_data = (MaterialData *)storage->material_get_data(ci->material, RasterizerStorageRD::SHADER_TYPE_2D); + } + + if (material_data) { + + if (material_data->shader_data->version.is_valid() && material_data->shader_data->valid) { + pipeline_variants = &material_data->shader_data->pipeline_variants; + if (material_data->uniform_set.is_valid()) { + RD::get_singleton()->draw_list_bind_uniform_set(draw_list, material_data->uniform_set, 1); + } + } else { + pipeline_variants = &shader.pipeline_variants; + } + } else { + pipeline_variants = &shader.pipeline_variants; + } + } + + _render_item(draw_list, ci, fb_format, canvas_transform_inverse, current_clip, p_lights, pipeline_variants); + + prev_material = ci->material; + } + + RD::get_singleton()->draw_list_end(); +} + +void RasterizerCanvasRD::canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, const Transform2D &p_canvas_transform) { + + int item_count = 0; + + //setup canvas state uniforms if needed + + Transform2D canvas_transform_inverse = p_canvas_transform.affine_inverse(); + + { + //update canvas state uniform buffer + State::Buffer state_buffer; + + Size2i ssize = storage->render_target_get_size(p_to_render_target); + + Transform screen_transform; + screen_transform.translate(-(ssize.width / 2.0f), -(ssize.height / 2.0f), 0.0f); + screen_transform.scale(Vector3(2.0f / ssize.width, 2.0f / ssize.height, 1.0f)); + _update_transform_to_mat4(screen_transform, state_buffer.screen_transform); + _update_transform_2d_to_mat4(p_canvas_transform, state_buffer.canvas_transform); + + Transform2D normal_transform = p_canvas_transform; + normal_transform.elements[0].normalize(); + normal_transform.elements[1].normalize(); + normal_transform.elements[2] = Vector2(); + _update_transform_2d_to_mat4(normal_transform, state_buffer.canvas_normal_transform); + + state_buffer.canvas_modulate[0] = p_modulate.r; + state_buffer.canvas_modulate[1] = p_modulate.g; + state_buffer.canvas_modulate[2] = p_modulate.b; + state_buffer.canvas_modulate[3] = p_modulate.a; + + Size2 render_target_size = storage->render_target_get_size(p_to_render_target); + state_buffer.screen_pixel_size[0] = 1.0 / render_target_size.x; + state_buffer.screen_pixel_size[1] = 1.0 / render_target_size.y; + + state_buffer.time = state.time; + RD::get_singleton()->buffer_update(state.canvas_state_buffer, 0, sizeof(State::Buffer), &state_buffer, true); + } + + //setup lights if exist + + { + + Light *l = p_light_list; + uint32_t index = 0; + + while (l) { + + if (index == state.max_lights_per_render) { + l->render_index_cache = -1; + l = l->next_ptr; + continue; + } + + CanvasLight *clight = canvas_light_owner.getornull(l->light_internal); + if (!clight) { //unused or invalid texture + l->render_index_cache = -1; + l = l->next_ptr; + ERR_CONTINUE(!clight); + } + Transform2D to_light_xform = (p_canvas_transform * l->light_shader_xform).affine_inverse(); + + Vector2 canvas_light_pos = p_canvas_transform.xform(l->xform.get_origin()); //convert light position to canvas coordinates, as all computation is done in canvas coords to avoid precision loss + state.light_uniforms[index].position[0] = canvas_light_pos.x; + state.light_uniforms[index].position[1] = canvas_light_pos.y; + + _update_transform_2d_to_mat2x4(to_light_xform, state.light_uniforms[index].matrix); + _update_transform_2d_to_mat2x4(l->xform_cache.affine_inverse(), state.light_uniforms[index].shadow_matrix); + + state.light_uniforms[index].height = l->height * (p_canvas_transform.elements[0].length() + p_canvas_transform.elements[1].length()) * 0.5; //approximate height conversion to the canvas size, since all calculations are done in canvas coords to avoid precision loss + for (int i = 0; i < 4; i++) { + state.light_uniforms[index].shadow_color[i] = l->shadow_color[i]; + state.light_uniforms[index].color[i] = l->color[i]; + } + + state.light_uniforms[index].color[3] = l->energy; //use alpha for energy, so base color can go separate + + if (clight->shadow.texture.is_valid()) { + state.light_uniforms[index].shadow_pixel_size = (1.0 / clight->shadow.size) * (1.0 + l->shadow_smooth); + } else { + state.light_uniforms[index].shadow_pixel_size = 1.0; + } + + state.light_uniforms[index].flags |= l->mode << LIGHT_FLAGS_BLEND_SHIFT; + state.light_uniforms[index].flags |= l->shadow_filter << LIGHT_FLAGS_FILTER_SHIFT; + if (clight->shadow.texture.is_valid()) { + state.light_uniforms[index].flags |= LIGHT_FLAGS_HAS_SHADOW; + } + + l->render_index_cache = index; + + index++; + l = l->next_ptr; + } + + if (index > 0) { + RD::get_singleton()->buffer_update(state.lights_uniform_buffer, 0, sizeof(LightUniform) * index, &state.light_uniforms[0], true); + } + } + + //fill the list until rendering is possible. + bool material_screen_texture_found = false; + Item *ci = p_item_list; + Rect2 back_buffer_rect; + bool backbuffer_copy = false; + RID screen_uniform_set; + + while (ci) { + + if (ci->copy_back_buffer) { + backbuffer_copy = true; + + if (ci->copy_back_buffer->full) { + back_buffer_rect = Rect2(); + } else { + back_buffer_rect = ci->copy_back_buffer->rect; + } + } + + if (ci->material.is_valid()) { + MaterialData *md = (MaterialData *)storage->material_get_data(ci->material, RasterizerStorageRD::SHADER_TYPE_2D); + if (md && md->shader_data->valid) { + + if (md->shader_data->uses_screen_texture) { + if (!material_screen_texture_found) { + backbuffer_copy = true; + back_buffer_rect = Rect2(); + } + if (screen_uniform_set.is_null()) { + RID backbuffer_shader = shader.canvas_shader.version_get_shader(md->shader_data->version, 0); //any version is fine + screen_uniform_set = storage->render_target_get_back_buffer_uniform_set(p_to_render_target, backbuffer_shader); + } + } + + if (md->last_frame != RasterizerRD::get_frame_number()) { + md->last_frame = RasterizerRD::get_frame_number(); + if (!RD::get_singleton()->uniform_set_is_valid(md->uniform_set)) { + // uniform set may be gone because a dependency was erased. In this case, it will happen + // if a texture is deleted, so just re-create it. + storage->material_force_update_textures(ci->material, RasterizerStorageRD::SHADER_TYPE_2D); + } + } + } + } + + if (backbuffer_copy) { + //render anything pending, including clearing if no items + _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, screen_uniform_set); + item_count = 0; + + storage->render_target_copy_to_back_buffer(p_to_render_target, back_buffer_rect); + + backbuffer_copy = false; + material_screen_texture_found = true; //after a backbuffer copy, screen texture makes no further copies + } + + items[item_count++] = ci; + + if (!ci->next || item_count == MAX_RENDER_ITEMS - 1) { + _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, screen_uniform_set); + //then reset + item_count = 0; + } + + ci = ci->next; + } +} + +RID RasterizerCanvasRD::light_create() { + + CanvasLight canvas_light; + canvas_light.shadow.size = 0; + return canvas_light_owner.make_rid(canvas_light); +} + +void RasterizerCanvasRD::light_set_texture(RID p_rid, RID p_texture) { + CanvasLight *cl = canvas_light_owner.getornull(p_rid); + ERR_FAIL_COND(!cl); + if (cl->texture == p_texture) { + return; + } + + cl->texture = p_texture; +} +void RasterizerCanvasRD::light_set_use_shadow(RID p_rid, bool p_enable, int p_resolution) { + CanvasLight *cl = canvas_light_owner.getornull(p_rid); + ERR_FAIL_COND(!cl); + ERR_FAIL_COND(p_resolution < 64); + if (cl->shadow.texture.is_valid() == p_enable && p_resolution == cl->shadow.size) { + return; + } + + if (cl->shadow.texture.is_valid()) { + + RD::get_singleton()->free(cl->shadow.fb); + RD::get_singleton()->free(cl->shadow.depth); + RD::get_singleton()->free(cl->shadow.texture); + cl->shadow.fb = RID(); + cl->shadow.texture = RID(); + cl->shadow.depth = RID(); + } + + if (p_enable) { + + Vector<RID> fb_textures; + + { //texture + RD::TextureFormat tf; + tf.type = RD::TEXTURE_TYPE_2D; + tf.width = p_resolution; + tf.height = 1; + tf.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT; + tf.format = RD::DATA_FORMAT_R32_SFLOAT; + + cl->shadow.texture = RD::get_singleton()->texture_create(tf, RD::TextureView()); + fb_textures.push_back(cl->shadow.texture); + } + { + RD::TextureFormat tf; + tf.type = RD::TEXTURE_TYPE_2D; + tf.width = p_resolution; + tf.height = 1; + tf.usage_bits = RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + tf.format = RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_X8_D24_UNORM_PACK32, RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) ? RD::DATA_FORMAT_X8_D24_UNORM_PACK32 : RD::DATA_FORMAT_D32_SFLOAT; + //chunks to write + cl->shadow.depth = RD::get_singleton()->texture_create(tf, RD::TextureView()); + fb_textures.push_back(cl->shadow.depth); + } + + cl->shadow.fb = RD::get_singleton()->framebuffer_create(fb_textures); + } + + cl->shadow.size = p_resolution; +} + +void RasterizerCanvasRD::light_update_shadow(RID p_rid, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders) { + + CanvasLight *cl = canvas_light_owner.getornull(p_rid); + ERR_FAIL_COND(cl->shadow.texture.is_null()); + + for (int i = 0; i < 4; i++) { + + //make sure it remains orthogonal, makes easy to read angle later + + //light.basis.scale(Vector3(to_light.elements[0].length(),to_light.elements[1].length(),1)); + + Vector<Color> cc; + cc.push_back(Color(p_far, p_far, p_far, 1.0)); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(cl->shadow.fb, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, cc, 1.0, 0, Rect2i((cl->shadow.size / 4) * i, 0, (cl->shadow.size / 4), 1)); + + CameraMatrix projection; + { + real_t fov = 90; + real_t nearp = p_near; + real_t farp = p_far; + real_t aspect = 1.0; + + real_t ymax = nearp * Math::tan(Math::deg2rad(fov * 0.5)); + real_t ymin = -ymax; + real_t xmin = ymin * aspect; + real_t xmax = ymax * aspect; + + projection.set_frustum(xmin, xmax, ymin, ymax, nearp, farp); + } + + Vector3 cam_target = Basis(Vector3(0, 0, Math_PI * 2 * ((i + 3) / 4.0))).xform(Vector3(0, 1, 0)); + projection = projection * CameraMatrix(Transform().looking_at(cam_target, Vector3(0, 0, -1)).affine_inverse()); + + ShadowRenderPushConstant push_constant; + for (int y = 0; y < 4; y++) { + for (int x = 0; x < 4; x++) { + push_constant.projection[y * 4 + x] = projection.matrix[y][x]; + } + } + static const Vector2 directions[4] = { Vector2(1, 0), Vector2(0, 1), Vector2(-1, 0), Vector2(0, -1) }; + push_constant.direction[0] = directions[i].x; + push_constant.direction[1] = directions[i].y; + push_constant.pad[0] = 0; + push_constant.pad[1] = 0; + + /*if (i == 0) + *p_xform_cache = projection;*/ + + LightOccluderInstance *instance = p_occluders; + + while (instance) { + + OccluderPolygon *co = occluder_polygon_owner.getornull(instance->occluder); + + if (!co || co->index_array.is_null() || !(p_light_mask & instance->light_mask)) { + + instance = instance->next; + continue; + } + + _update_transform_2d_to_mat2x4(p_light_xform * instance->xform_cache, push_constant.modelview); + + RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, shadow_render.render_pipelines[co->cull_mode]); + RD::get_singleton()->draw_list_bind_vertex_array(draw_list, co->vertex_array); + RD::get_singleton()->draw_list_bind_index_array(draw_list, co->index_array); + RD::get_singleton()->draw_list_set_push_constant(draw_list, &push_constant, sizeof(ShadowRenderPushConstant)); + + RD::get_singleton()->draw_list_draw(draw_list, true); + + instance = instance->next; + } + + RD::get_singleton()->draw_list_end(); + } +} + +RID RasterizerCanvasRD::occluder_polygon_create() { + + OccluderPolygon occluder; + occluder.point_count = 0; + occluder.cull_mode = RS::CANVAS_OCCLUDER_POLYGON_CULL_DISABLED; + return occluder_polygon_owner.make_rid(occluder); +} + +void RasterizerCanvasRD::occluder_polygon_set_shape_as_lines(RID p_occluder, const Vector<Vector2> &p_lines) { + + OccluderPolygon *oc = occluder_polygon_owner.getornull(p_occluder); + ERR_FAIL_COND(!oc); + + if (oc->point_count != p_lines.size() && oc->vertex_array.is_valid()) { + + RD::get_singleton()->free(oc->vertex_array); + RD::get_singleton()->free(oc->vertex_buffer); + RD::get_singleton()->free(oc->index_array); + RD::get_singleton()->free(oc->index_buffer); + + oc->vertex_array = RID(); + oc->vertex_buffer = RID(); + oc->index_array = RID(); + oc->index_buffer = RID(); + } + + if (p_lines.size()) { + + Vector<uint8_t> geometry; + Vector<uint8_t> indices; + int lc = p_lines.size(); + + geometry.resize(lc * 6 * sizeof(float)); + indices.resize(lc * 3 * sizeof(uint16_t)); + + { + uint8_t *vw = geometry.ptrw(); + float *vwptr = (float *)vw; + uint8_t *iw = indices.ptrw(); + uint16_t *iwptr = (uint16_t *)iw; + + const Vector2 *lr = p_lines.ptr(); + + const int POLY_HEIGHT = 16384; + + for (int i = 0; i < lc / 2; i++) { + + vwptr[i * 12 + 0] = lr[i * 2 + 0].x; + vwptr[i * 12 + 1] = lr[i * 2 + 0].y; + vwptr[i * 12 + 2] = POLY_HEIGHT; + + vwptr[i * 12 + 3] = lr[i * 2 + 1].x; + vwptr[i * 12 + 4] = lr[i * 2 + 1].y; + vwptr[i * 12 + 5] = POLY_HEIGHT; + + vwptr[i * 12 + 6] = lr[i * 2 + 1].x; + vwptr[i * 12 + 7] = lr[i * 2 + 1].y; + vwptr[i * 12 + 8] = -POLY_HEIGHT; + + vwptr[i * 12 + 9] = lr[i * 2 + 0].x; + vwptr[i * 12 + 10] = lr[i * 2 + 0].y; + vwptr[i * 12 + 11] = -POLY_HEIGHT; + + iwptr[i * 6 + 0] = i * 4 + 0; + iwptr[i * 6 + 1] = i * 4 + 1; + iwptr[i * 6 + 2] = i * 4 + 2; + + iwptr[i * 6 + 3] = i * 4 + 2; + iwptr[i * 6 + 4] = i * 4 + 3; + iwptr[i * 6 + 5] = i * 4 + 0; + } + } + + //if same buffer len is being set, just use BufferSubData to avoid a pipeline flush + + if (oc->vertex_array.is_null()) { + //create from scratch + //vertices + oc->vertex_buffer = RD::get_singleton()->vertex_buffer_create(lc * 6 * sizeof(real_t), geometry); + + Vector<RID> buffer; + buffer.push_back(oc->vertex_buffer); + oc->vertex_array = RD::get_singleton()->vertex_array_create(4 * lc / 2, shadow_render.vertex_format, buffer); + //indices + + oc->index_buffer = RD::get_singleton()->index_buffer_create(3 * lc, RD::INDEX_BUFFER_FORMAT_UINT16, indices); + oc->index_array = RD::get_singleton()->index_array_create(oc->index_buffer, 0, 3 * lc); + + } else { + //update existing + const uint8_t *vr = geometry.ptr(); + RD::get_singleton()->buffer_update(oc->vertex_buffer, 0, geometry.size(), vr); + const uint8_t *ir = indices.ptr(); + RD::get_singleton()->buffer_update(oc->index_buffer, 0, indices.size(), ir); + } + } +} +void RasterizerCanvasRD::occluder_polygon_set_cull_mode(RID p_occluder, RS::CanvasOccluderPolygonCullMode p_mode) { + OccluderPolygon *oc = occluder_polygon_owner.getornull(p_occluder); + ERR_FAIL_COND(!oc); + oc->cull_mode = p_mode; +} + +void RasterizerCanvasRD::ShaderData::set_code(const String &p_code) { + //compile + + code = p_code; + valid = false; + ubo_size = 0; + uniforms.clear(); + uses_screen_texture = false; + uses_material_samplers = false; + + if (code == String()) { + return; //just invalid, but no error + } + + ShaderCompilerRD::GeneratedCode gen_code; + + int light_mode = LIGHT_MODE_NORMAL; + int blend_mode = BLEND_MODE_MIX; + uses_screen_texture = false; + + ShaderCompilerRD::IdentifierActions actions; + + actions.render_mode_values["blend_add"] = Pair<int *, int>(&blend_mode, BLEND_MODE_ADD); + actions.render_mode_values["blend_mix"] = Pair<int *, int>(&blend_mode, BLEND_MODE_MIX); + actions.render_mode_values["blend_sub"] = Pair<int *, int>(&blend_mode, BLEND_MODE_SUB); + actions.render_mode_values["blend_mul"] = Pair<int *, int>(&blend_mode, BLEND_MODE_MUL); + actions.render_mode_values["blend_premul_alpha"] = Pair<int *, int>(&blend_mode, BLEND_MODE_PMALPHA); + actions.render_mode_values["blend_disabled"] = Pair<int *, int>(&blend_mode, BLEND_MODE_DISABLED); + + actions.render_mode_values["unshaded"] = Pair<int *, int>(&light_mode, LIGHT_MODE_UNSHADED); + actions.render_mode_values["light_only"] = Pair<int *, int>(&light_mode, LIGHT_MODE_LIGHT_ONLY); + + actions.usage_flag_pointers["SCREEN_TEXTURE"] = &uses_screen_texture; + + actions.uniforms = &uniforms; + + RasterizerCanvasRD *canvas_singleton = (RasterizerCanvasRD *)RasterizerCanvas::singleton; + + Error err = canvas_singleton->shader.compiler.compile(RS::SHADER_CANVAS_ITEM, code, &actions, path, gen_code); + + ERR_FAIL_COND(err != OK); + + if (version.is_null()) { + version = canvas_singleton->shader.canvas_shader.version_create(); + } + + if (gen_code.texture_uniforms.size() || uses_screen_texture) { //requires the samplers + gen_code.defines.push_back("\n#define USE_MATERIAL_SAMPLERS\n"); + uses_material_samplers = true; + } +#if 0 + print_line("**compiling shader:"); + print_line("**defines:\n"); + for (int i = 0; i < gen_code.defines.size(); i++) { + print_line(gen_code.defines[i]); + } + print_line("\n**uniforms:\n" + gen_code.uniforms); + print_line("\n**vertex_globals:\n" + gen_code.vertex_global); + print_line("\n**vertex_code:\n" + gen_code.vertex); + print_line("\n**fragment_globals:\n" + gen_code.fragment_global); + print_line("\n**fragment_code:\n" + gen_code.fragment); + print_line("\n**light_code:\n" + gen_code.light); +#endif + canvas_singleton->shader.canvas_shader.version_set_code(version, gen_code.uniforms, gen_code.vertex_global, gen_code.vertex, gen_code.fragment_global, gen_code.light, gen_code.fragment, gen_code.defines); + ERR_FAIL_COND(!canvas_singleton->shader.canvas_shader.version_is_valid(version)); + + ubo_size = gen_code.uniform_total_size; + ubo_offsets = gen_code.uniform_offsets; + texture_uniforms = gen_code.texture_uniforms; + + //update them pipelines + + RD::PipelineColorBlendState::Attachment attachment; + + switch (blend_mode) { + case BLEND_MODE_DISABLED: { + + // nothing to do here, disabled by default + + } break; + case BLEND_MODE_MIX: { + + attachment.enable_blend = true; + attachment.alpha_blend_op = RD::BLEND_OP_ADD; + attachment.color_blend_op = RD::BLEND_OP_ADD; + attachment.src_color_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA; + attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_ONE; + attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + + } break; + case BLEND_MODE_ADD: { + + attachment.enable_blend = true; + attachment.alpha_blend_op = RD::BLEND_OP_ADD; + attachment.color_blend_op = RD::BLEND_OP_ADD; + attachment.src_color_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA; + attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ONE; + attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA; + attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ONE; + + } break; + case BLEND_MODE_SUB: { + + attachment.enable_blend = true; + attachment.alpha_blend_op = RD::BLEND_OP_SUBTRACT; + attachment.color_blend_op = RD::BLEND_OP_SUBTRACT; + attachment.src_color_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA; + attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ONE; + attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA; + attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ONE; + + } break; + case BLEND_MODE_MUL: { + attachment.enable_blend = true; + attachment.alpha_blend_op = RD::BLEND_OP_ADD; + attachment.color_blend_op = RD::BLEND_OP_ADD; + attachment.src_color_blend_factor = RD::BLEND_FACTOR_DST_COLOR; + attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ZERO; + attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_DST_ALPHA; + attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ZERO; + + } break; + case BLEND_MODE_PMALPHA: { + attachment.enable_blend = true; + attachment.alpha_blend_op = RD::BLEND_OP_ADD; + attachment.color_blend_op = RD::BLEND_OP_ADD; + attachment.src_color_blend_factor = RD::BLEND_FACTOR_ONE; + attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_ONE; + attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + + } break; + } + + RD::PipelineColorBlendState blend_state; + blend_state.attachments.push_back(attachment); + + //update pipelines + + for (int i = 0; i < PIPELINE_LIGHT_MODE_MAX; i++) { + for (int j = 0; j < PIPELINE_VARIANT_MAX; j++) { + RD::RenderPrimitive primitive[PIPELINE_VARIANT_MAX] = { + RD::RENDER_PRIMITIVE_TRIANGLES, + RD::RENDER_PRIMITIVE_TRIANGLES, + RD::RENDER_PRIMITIVE_TRIANGLES, + RD::RENDER_PRIMITIVE_LINES, + RD::RENDER_PRIMITIVE_POINTS, + RD::RENDER_PRIMITIVE_TRIANGLES, + RD::RENDER_PRIMITIVE_TRIANGLE_STRIPS, + RD::RENDER_PRIMITIVE_LINES, + RD::RENDER_PRIMITIVE_LINESTRIPS, + RD::RENDER_PRIMITIVE_POINTS, + }; + + ShaderVariant shader_variants[PIPELINE_LIGHT_MODE_MAX][PIPELINE_VARIANT_MAX] = { + { //non lit + SHADER_VARIANT_QUAD, + SHADER_VARIANT_NINEPATCH, + SHADER_VARIANT_PRIMITIVE, + SHADER_VARIANT_PRIMITIVE, + SHADER_VARIANT_PRIMITIVE_POINTS, + SHADER_VARIANT_ATTRIBUTES, + SHADER_VARIANT_ATTRIBUTES, + SHADER_VARIANT_ATTRIBUTES, + SHADER_VARIANT_ATTRIBUTES, + SHADER_VARIANT_ATTRIBUTES_POINTS }, + { //lit + SHADER_VARIANT_QUAD_LIGHT, + SHADER_VARIANT_NINEPATCH_LIGHT, + SHADER_VARIANT_PRIMITIVE_LIGHT, + SHADER_VARIANT_PRIMITIVE_LIGHT, + SHADER_VARIANT_PRIMITIVE_POINTS_LIGHT, + SHADER_VARIANT_ATTRIBUTES_LIGHT, + SHADER_VARIANT_ATTRIBUTES_LIGHT, + SHADER_VARIANT_ATTRIBUTES_LIGHT, + SHADER_VARIANT_ATTRIBUTES_LIGHT, + SHADER_VARIANT_ATTRIBUTES_POINTS_LIGHT }, + }; + + RID shader_variant = canvas_singleton->shader.canvas_shader.version_get_shader(version, shader_variants[i][j]); + pipeline_variants.variants[i][j].setup(shader_variant, primitive[j], RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), blend_state, 0); + } + } + + valid = true; +} + +void RasterizerCanvasRD::ShaderData::set_default_texture_param(const StringName &p_name, RID p_texture) { + if (!p_texture.is_valid()) { + default_texture_params.erase(p_name); + } else { + default_texture_params[p_name] = p_texture; + } +} +void RasterizerCanvasRD::ShaderData::get_param_list(List<PropertyInfo> *p_param_list) const { + + Map<int, StringName> order; + + for (Map<StringName, ShaderLanguage::ShaderNode::Uniform>::Element *E = uniforms.front(); E; E = E->next()) { + + if (E->get().texture_order >= 0) { + order[E->get().texture_order + 100000] = E->key(); + } else { + order[E->get().order] = E->key(); + } + } + + for (Map<int, StringName>::Element *E = order.front(); E; E = E->next()) { + + PropertyInfo pi = ShaderLanguage::uniform_to_property_info(uniforms[E->get()]); + pi.name = E->get(); + p_param_list->push_back(pi); + } +} + +bool RasterizerCanvasRD::ShaderData::is_param_texture(const StringName &p_param) const { + if (!uniforms.has(p_param)) { + return false; + } + + return uniforms[p_param].texture_order >= 0; +} + +bool RasterizerCanvasRD::ShaderData::is_animated() const { + return false; +} +bool RasterizerCanvasRD::ShaderData::casts_shadows() const { + return false; +} +Variant RasterizerCanvasRD::ShaderData::get_default_parameter(const StringName &p_parameter) const { + if (uniforms.has(p_parameter)) { + ShaderLanguage::ShaderNode::Uniform uniform = uniforms[p_parameter]; + Vector<ShaderLanguage::ConstantNode::Value> default_value = uniform.default_value; + return ShaderLanguage::constant_value_to_variant(default_value, uniform.type, uniform.hint); + } + return Variant(); +} + +RasterizerCanvasRD::ShaderData::ShaderData() { + valid = false; + uses_screen_texture = false; + uses_material_samplers = false; +} + +RasterizerCanvasRD::ShaderData::~ShaderData() { + RasterizerCanvasRD *canvas_singleton = (RasterizerCanvasRD *)RasterizerCanvas::singleton; + ERR_FAIL_COND(!canvas_singleton); + //pipeline variants will clear themselves if shader is gone + if (version.is_valid()) { + canvas_singleton->shader.canvas_shader.version_free(version); + } +} + +RasterizerStorageRD::ShaderData *RasterizerCanvasRD::_create_shader_func() { + ShaderData *shader_data = memnew(ShaderData); + return shader_data; +} +void RasterizerCanvasRD::MaterialData::update_parameters(const Map<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty) { + + RasterizerCanvasRD *canvas_singleton = (RasterizerCanvasRD *)RasterizerCanvas::singleton; + + if ((uint32_t)ubo_data.size() != shader_data->ubo_size) { + p_uniform_dirty = true; + if (uniform_buffer.is_valid()) { + RD::get_singleton()->free(uniform_buffer); + uniform_buffer = RID(); + } + + ubo_data.resize(shader_data->ubo_size); + if (ubo_data.size()) { + uniform_buffer = RD::get_singleton()->uniform_buffer_create(ubo_data.size()); + memset(ubo_data.ptrw(), 0, ubo_data.size()); //clear + } + + //clear previous uniform set + if (uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(uniform_set)) { + RD::get_singleton()->free(uniform_set); + uniform_set = RID(); + } + } + + //check whether buffer changed + if (p_uniform_dirty && ubo_data.size()) { + + update_uniform_buffer(shader_data->uniforms, shader_data->ubo_offsets.ptr(), p_parameters, ubo_data.ptrw(), ubo_data.size(), false); + RD::get_singleton()->buffer_update(uniform_buffer, 0, ubo_data.size(), ubo_data.ptrw()); + } + + uint32_t tex_uniform_count = shader_data->texture_uniforms.size(); + + if ((uint32_t)texture_cache.size() != tex_uniform_count) { + texture_cache.resize(tex_uniform_count); + p_textures_dirty = true; + + //clear previous uniform set + if (uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(uniform_set)) { + RD::get_singleton()->free(uniform_set); + uniform_set = RID(); + } + } + + if (p_textures_dirty && tex_uniform_count) { + + update_textures(p_parameters, shader_data->default_texture_params, shader_data->texture_uniforms, texture_cache.ptrw(), false); + } + + if (shader_data->ubo_size == 0 && !shader_data->uses_material_samplers) { + // This material does not require an uniform set, so don't create it. + return; + } + + if (!p_textures_dirty && uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(uniform_set)) { + //no reason to update uniform set, only UBO (or nothing) was needed to update + return; + } + + Vector<RD::Uniform> uniforms; + + { + if (shader_data->uses_material_samplers) { + //needs samplers for the material (uses custom textures) create them + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_SAMPLER; + u.binding = 0; + u.ids.resize(12); + RID *ids_ptr = u.ids.ptrw(); + ids_ptr[0] = canvas_singleton->storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + ids_ptr[1] = canvas_singleton->storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + ids_ptr[2] = canvas_singleton->storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + ids_ptr[3] = canvas_singleton->storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + ids_ptr[4] = canvas_singleton->storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + ids_ptr[5] = canvas_singleton->storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + ids_ptr[6] = canvas_singleton->storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED); + ids_ptr[7] = canvas_singleton->storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED); + ids_ptr[8] = canvas_singleton->storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED); + ids_ptr[9] = canvas_singleton->storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED); + ids_ptr[10] = canvas_singleton->storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED); + ids_ptr[11] = canvas_singleton->storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED); + uniforms.push_back(u); + } + + if (shader_data->ubo_size) { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; + u.binding = 1; + u.ids.push_back(uniform_buffer); + uniforms.push_back(u); + } + + const RID *textures = texture_cache.ptrw(); + for (uint32_t i = 0; i < tex_uniform_count; i++) { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_TEXTURE; + u.binding = 2 + i; + u.ids.push_back(textures[i]); + uniforms.push_back(u); + } + } + + uniform_set = RD::get_singleton()->uniform_set_create(uniforms, canvas_singleton->shader.canvas_shader.version_get_shader(shader_data->version, 0), 1); +} +RasterizerCanvasRD::MaterialData::~MaterialData() { + if (uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(uniform_set)) { + RD::get_singleton()->free(uniform_set); + } + + if (uniform_buffer.is_valid()) { + RD::get_singleton()->free(uniform_buffer); + } +} + +RasterizerStorageRD::MaterialData *RasterizerCanvasRD::_create_material_func(ShaderData *p_shader) { + MaterialData *material_data = memnew(MaterialData); + material_data->shader_data = p_shader; + material_data->last_frame = false; + //update will happen later anyway so do nothing. + return material_data; +} + +void RasterizerCanvasRD::set_time(double p_time) { + state.time = p_time; +} + +void RasterizerCanvasRD::update() { + _dispose_bindings(); +} + +RasterizerCanvasRD::RasterizerCanvasRD(RasterizerStorageRD *p_storage) { + storage = p_storage; + + { //create default samplers + + default_samplers.default_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR; + default_samplers.default_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED; + } + + { //shader variants + + uint32_t textures_per_stage = RD::get_singleton()->limit_get(RD::LIMIT_MAX_TEXTURES_PER_SHADER_STAGE); + + String global_defines; + if (textures_per_stage <= 16) { + //ARM pretty much, and very old Intel GPUs under Linux + state.max_lights_per_item = 4; //sad + global_defines += "#define MAX_LIGHT_TEXTURES 4\n"; + } else if (textures_per_stage <= 32) { + //Apple (Metal) + state.max_lights_per_item = 8; //sad + global_defines += "#define MAX_LIGHT_TEXTURES 8\n"; + } else { + //Anything else (16 lights per item) + state.max_lights_per_item = DEFAULT_MAX_LIGHTS_PER_ITEM; + global_defines += "#define MAX_LIGHT_TEXTURES " + itos(DEFAULT_MAX_LIGHTS_PER_ITEM) + "\n"; + } + + uint32_t uniform_max_size = RD::get_singleton()->limit_get(RD::LIMIT_MAX_UNIFORM_BUFFER_SIZE); + if (uniform_max_size < 65536) { + //Yes, you guessed right, ARM again + state.max_lights_per_render = 64; + global_defines += "#define MAX_LIGHTS 64\n"; + } else { + state.max_lights_per_render = DEFAULT_MAX_LIGHTS_PER_RENDER; + global_defines += "#define MAX_LIGHTS " + itos(DEFAULT_MAX_LIGHTS_PER_RENDER) + "\n"; + } + + state.light_uniforms = memnew_arr(LightUniform, state.max_lights_per_render); + Vector<String> variants; + //non light variants + variants.push_back(""); //none by default is first variant + variants.push_back("#define USE_NINEPATCH\n"); //ninepatch is the second variant + variants.push_back("#define USE_PRIMITIVE\n"); //primitive is the third + variants.push_back("#define USE_PRIMITIVE\n#define USE_POINT_SIZE\n"); //points need point size + variants.push_back("#define USE_ATTRIBUTES\n"); // attributes for vertex arrays + variants.push_back("#define USE_ATTRIBUTES\n#define USE_POINT_SIZE\n"); //attributes with point size + //light variants + variants.push_back("#define USE_LIGHTING\n"); //none by default is first variant + variants.push_back("#define USE_LIGHTING\n#define USE_NINEPATCH\n"); //ninepatch is the second variant + variants.push_back("#define USE_LIGHTING\n#define USE_PRIMITIVE\n"); //primitive is the third + variants.push_back("#define USE_LIGHTING\n#define USE_PRIMITIVE\n#define USE_POINT_SIZE\n"); //points need point size + variants.push_back("#define USE_LIGHTING\n#define USE_ATTRIBUTES\n"); // attributes for vertex arrays + variants.push_back("#define USE_LIGHTING\n#define USE_ATTRIBUTES\n#define USE_POINT_SIZE\n"); //attributes with point size + + shader.canvas_shader.initialize(variants, global_defines); + + shader.default_version = shader.canvas_shader.version_create(); + shader.default_version_rd_shader = shader.canvas_shader.version_get_shader(shader.default_version, SHADER_VARIANT_QUAD); + shader.default_version_rd_shader_light = shader.canvas_shader.version_get_shader(shader.default_version, SHADER_VARIANT_QUAD_LIGHT); + + for (int i = 0; i < PIPELINE_LIGHT_MODE_MAX; i++) { + for (int j = 0; j < PIPELINE_VARIANT_MAX; j++) { + RD::RenderPrimitive primitive[PIPELINE_VARIANT_MAX] = { + RD::RENDER_PRIMITIVE_TRIANGLES, + RD::RENDER_PRIMITIVE_TRIANGLES, + RD::RENDER_PRIMITIVE_TRIANGLES, + RD::RENDER_PRIMITIVE_LINES, + RD::RENDER_PRIMITIVE_POINTS, + RD::RENDER_PRIMITIVE_TRIANGLES, + RD::RENDER_PRIMITIVE_TRIANGLE_STRIPS, + RD::RENDER_PRIMITIVE_LINES, + RD::RENDER_PRIMITIVE_LINESTRIPS, + RD::RENDER_PRIMITIVE_POINTS, + }; + + ShaderVariant shader_variants[PIPELINE_LIGHT_MODE_MAX][PIPELINE_VARIANT_MAX] = { + { //non lit + SHADER_VARIANT_QUAD, + SHADER_VARIANT_NINEPATCH, + SHADER_VARIANT_PRIMITIVE, + SHADER_VARIANT_PRIMITIVE, + SHADER_VARIANT_PRIMITIVE_POINTS, + SHADER_VARIANT_ATTRIBUTES, + SHADER_VARIANT_ATTRIBUTES, + SHADER_VARIANT_ATTRIBUTES, + SHADER_VARIANT_ATTRIBUTES, + SHADER_VARIANT_ATTRIBUTES_POINTS }, + { //lit + SHADER_VARIANT_QUAD_LIGHT, + SHADER_VARIANT_NINEPATCH_LIGHT, + SHADER_VARIANT_PRIMITIVE_LIGHT, + SHADER_VARIANT_PRIMITIVE_LIGHT, + SHADER_VARIANT_PRIMITIVE_POINTS_LIGHT, + SHADER_VARIANT_ATTRIBUTES_LIGHT, + SHADER_VARIANT_ATTRIBUTES_LIGHT, + SHADER_VARIANT_ATTRIBUTES_LIGHT, + SHADER_VARIANT_ATTRIBUTES_LIGHT, + SHADER_VARIANT_ATTRIBUTES_POINTS_LIGHT }, + }; + + RID shader_variant = shader.canvas_shader.version_get_shader(shader.default_version, shader_variants[i][j]); + shader.pipeline_variants.variants[i][j].setup(shader_variant, primitive[j], RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_blend(), 0); + } + } + } + + { + //shader compiler + ShaderCompilerRD::DefaultIdentifierActions actions; + + actions.renames["VERTEX"] = "vertex"; + actions.renames["LIGHT_VERTEX"] = "light_vertex"; + actions.renames["SHADOW_VERTEX"] = "shadow_vertex"; + actions.renames["UV"] = "uv"; + actions.renames["POINT_SIZE"] = "gl_PointSize"; + + actions.renames["WORLD_MATRIX"] = "world_matrix"; + actions.renames["CANVAS_MATRIX"] = "canvas_data.canvas_transform"; + actions.renames["SCREEN_MATRIX"] = "canvas_data.screen_transform"; + actions.renames["TIME"] = "canvas_data.time"; + actions.renames["AT_LIGHT_PASS"] = "false"; + actions.renames["INSTANCE_CUSTOM"] = "instance_custom"; + + actions.renames["COLOR"] = "color"; + actions.renames["NORMAL"] = "normal"; + actions.renames["NORMALMAP"] = "normal_map"; + actions.renames["NORMALMAP_DEPTH"] = "normal_depth"; + actions.renames["TEXTURE"] = "color_texture"; + actions.renames["TEXTURE_PIXEL_SIZE"] = "draw_data.color_texture_pixel_size"; + actions.renames["NORMAL_TEXTURE"] = "normal_texture"; + actions.renames["SPECULAR_SHININESS_TEXTURE"] = "specular_texture"; + actions.renames["SPECULAR_SHININESS"] = "specular_shininess"; + actions.renames["SCREEN_UV"] = "screen_uv"; + actions.renames["SCREEN_TEXTURE"] = "screen_texture"; + actions.renames["SCREEN_PIXEL_SIZE"] = "canvas_data.screen_pixel_size"; + actions.renames["FRAGCOORD"] = "gl_FragCoord"; + actions.renames["POINT_COORD"] = "gl_PointCoord"; + + actions.renames["LIGHT_POSITION"] = "light_pos"; + actions.renames["LIGHT_COLOR"] = "light_color"; + actions.renames["LIGHT_ENERGY"] = "light_energy"; + actions.renames["LIGHT"] = "light"; + actions.renames["SHADOW_MODULATE"] = "shadow_modulate"; + + actions.usage_defines["COLOR"] = "#define COLOR_USED\n"; + actions.usage_defines["SCREEN_TEXTURE"] = "#define SCREEN_TEXTURE_USED\n"; + actions.usage_defines["SCREEN_UV"] = "#define SCREEN_UV_USED\n"; + actions.usage_defines["SCREEN_PIXEL_SIZE"] = "@SCREEN_UV"; + actions.usage_defines["NORMAL"] = "#define NORMAL_USED\n"; + actions.usage_defines["NORMALMAP"] = "#define NORMALMAP_USED\n"; + actions.usage_defines["LIGHT"] = "#define LIGHT_SHADER_CODE_USED\n"; + + actions.render_mode_defines["skip_vertex_transform"] = "#define SKIP_TRANSFORM_USED\n"; + + actions.custom_samplers["TEXTURE"] = "texture_sampler"; + actions.custom_samplers["NORMAL_TEXTURE"] = "texture_sampler"; + actions.custom_samplers["SPECULAR_SHININESS_TEXTURE"] = "texture_sampler"; + actions.custom_samplers["SCREEN_TEXTURE"] = "material_samplers[3]"; //mipmap and filter for screen texture + actions.sampler_array_name = "material_samplers"; + actions.base_texture_binding_index = 2; + actions.texture_layout_set = 1; + actions.base_uniform_string = "material."; + actions.default_filter = ShaderLanguage::FILTER_LINEAR; + actions.default_repeat = ShaderLanguage::REPEAT_DISABLE; + actions.base_varying_index = 4; + + shader.compiler.initialize(actions); + } + + { //shadow rendering + Vector<String> versions; + versions.push_back(String()); //no versions + shadow_render.shader.initialize(versions); + + { + Vector<RD::AttachmentFormat> attachments; + + RD::AttachmentFormat af_color; + af_color.format = RD::DATA_FORMAT_R32_SFLOAT; + af_color.usage_flags = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; + + attachments.push_back(af_color); + + RD::AttachmentFormat af_depth; + af_depth.format = RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_D32_SFLOAT, RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) ? RD::DATA_FORMAT_D32_SFLOAT : RD::DATA_FORMAT_X8_D24_UNORM_PACK32; + af_depth.usage_flags = RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + + attachments.push_back(af_depth); + + shadow_render.framebuffer_format = RD::get_singleton()->framebuffer_format_create(attachments); + } + + //pipelines + Vector<RD::VertexDescription> vf; + RD::VertexDescription vd; + vd.format = RD::DATA_FORMAT_R32G32B32_SFLOAT; + vd.location = 0; + vd.offset = 0; + vd.stride = sizeof(float) * 3; + vf.push_back(vd); + shadow_render.vertex_format = RD::get_singleton()->vertex_format_create(vf); + + shadow_render.shader_version = shadow_render.shader.version_create(); + + for (int i = 0; i < 3; i++) { + RD::PipelineRasterizationState rs; + rs.cull_mode = i == 0 ? RD::POLYGON_CULL_DISABLED : (i == 1 ? RD::POLYGON_CULL_FRONT : RD::POLYGON_CULL_BACK); + RD::PipelineDepthStencilState ds; + ds.enable_depth_write = true; + ds.enable_depth_test = true; + ds.depth_compare_operator = RD::COMPARE_OP_LESS; + shadow_render.render_pipelines[i] = RD::get_singleton()->render_pipeline_create(shadow_render.shader.version_get_shader(shadow_render.shader_version, 0), shadow_render.framebuffer_format, shadow_render.vertex_format, RD::RENDER_PRIMITIVE_TRIANGLES, rs, RD::PipelineMultisampleState(), ds, RD::PipelineColorBlendState::create_disabled(), 0); + } + } + + { //bindings + bindings.id_generator = 0; + //generate for 0 + bindings.default_empty = request_texture_binding(RID(), RID(), RID(), RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT, RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT, RID()); + + { //state allocate + state.canvas_state_buffer = RD::get_singleton()->uniform_buffer_create(sizeof(State::Buffer)); + state.lights_uniform_buffer = RD::get_singleton()->uniform_buffer_create(sizeof(LightUniform) * state.max_lights_per_render); + + RD::SamplerState shadow_sampler_state; + shadow_sampler_state.mag_filter = RD::SAMPLER_FILTER_LINEAR; + shadow_sampler_state.min_filter = RD::SAMPLER_FILTER_LINEAR; + shadow_sampler_state.repeat_u = RD::SAMPLER_REPEAT_MODE_REPEAT; //shadow wrap around + shadow_sampler_state.compare_op = RD::COMPARE_OP_GREATER; + state.shadow_sampler = RD::get_singleton()->sampler_create(shadow_sampler_state); + } + } + + { + //polygon buffers + polygon_buffers.last_id = 1; + } + + { // default index buffer + + Vector<uint8_t> pv; + pv.resize(6 * 4); + { + uint8_t *w = pv.ptrw(); + int *p32 = (int *)w; + p32[0] = 0; + p32[1] = 1; + p32[2] = 2; + p32[3] = 0; + p32[4] = 2; + p32[5] = 3; + } + shader.quad_index_buffer = RD::get_singleton()->index_buffer_create(6, RenderingDevice::INDEX_BUFFER_FORMAT_UINT32, pv); + shader.quad_index_array = RD::get_singleton()->index_array_create(shader.quad_index_buffer, 0, 6); + } + + { //primitive + primitive_arrays.index_array[0] = shader.quad_index_array = RD::get_singleton()->index_array_create(shader.quad_index_buffer, 0, 1); + primitive_arrays.index_array[1] = shader.quad_index_array = RD::get_singleton()->index_array_create(shader.quad_index_buffer, 0, 2); + primitive_arrays.index_array[2] = shader.quad_index_array = RD::get_singleton()->index_array_create(shader.quad_index_buffer, 0, 3); + primitive_arrays.index_array[3] = shader.quad_index_array = RD::get_singleton()->index_array_create(shader.quad_index_buffer, 0, 6); + } + + { //default skeleton buffer + + shader.default_skeleton_uniform_buffer = RD::get_singleton()->uniform_buffer_create(sizeof(SkeletonUniform)); + SkeletonUniform su; + _update_transform_2d_to_mat4(Transform2D(), su.skeleton_inverse); + _update_transform_2d_to_mat4(Transform2D(), su.skeleton_transform); + RD::get_singleton()->buffer_update(shader.default_skeleton_uniform_buffer, 0, sizeof(SkeletonUniform), &su); + + shader.default_skeleton_texture_buffer = RD::get_singleton()->texture_buffer_create(32, RD::DATA_FORMAT_R32G32B32A32_SFLOAT); + } + + //create functions for shader and material + storage->shader_set_data_request_function(RasterizerStorageRD::SHADER_TYPE_2D, _create_shader_funcs); + storage->material_set_data_request_function(RasterizerStorageRD::SHADER_TYPE_2D, _create_material_funcs); + + state.time = 0; + + static_assert(sizeof(PushConstant) == 128); +} + +bool RasterizerCanvasRD::free(RID p_rid) { + + if (canvas_light_owner.owns(p_rid)) { + CanvasLight *cl = canvas_light_owner.getornull(p_rid); + ERR_FAIL_COND_V(!cl, false); + light_set_use_shadow(p_rid, false, 64); + canvas_light_owner.free(p_rid); + } else if (occluder_polygon_owner.owns(p_rid)) { + occluder_polygon_set_shape_as_lines(p_rid, Vector<Vector2>()); + occluder_polygon_owner.free(p_rid); + } else { + return false; + } + + return true; +} + +RasterizerCanvasRD::~RasterizerCanvasRD() { + + //canvas state + + { + if (state.canvas_state_buffer.is_valid()) { + RD::get_singleton()->free(state.canvas_state_buffer); + } + + memdelete_arr(state.light_uniforms); + RD::get_singleton()->free(state.lights_uniform_buffer); + RD::get_singleton()->free(shader.default_skeleton_uniform_buffer); + RD::get_singleton()->free(shader.default_skeleton_texture_buffer); + } + + //shadow rendering + { + + shadow_render.shader.version_free(shadow_render.shader_version); + //this will also automatically clear all pipelines + RD::get_singleton()->free(state.shadow_sampler); + } + //bindings + { + + free_texture_binding(bindings.default_empty); + + //dispose pending + _dispose_bindings(); + //anything remains? + if (bindings.texture_bindings.size()) { + ERR_PRINT("Some texture bindings were not properly freed (leaked canvasitems?"); + const TextureBindingID *key = NULL; + while ((key = bindings.texture_bindings.next(key))) { + TextureBinding *tb = bindings.texture_bindings[*key]; + tb->reference_count = 1; + free_texture_binding(*key); + } + //dispose pending + _dispose_bindings(); + } + } + + //shaders + + shader.canvas_shader.version_free(shader.default_version); + + //buffers + { + RD::get_singleton()->free(shader.quad_index_array); + RD::get_singleton()->free(shader.quad_index_buffer); + //primitives are erase by dependency + } + + //pipelines don't need freeing, they are all gone after shaders are gone +} diff --git a/servers/rendering/rasterizer_rd/rasterizer_canvas_rd.h b/servers/rendering/rasterizer_rd/rasterizer_canvas_rd.h new file mode 100644 index 0000000000..0c151975bc --- /dev/null +++ b/servers/rendering/rasterizer_rd/rasterizer_canvas_rd.h @@ -0,0 +1,501 @@ +/*************************************************************************/ +/* rasterizer_canvas_rd.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef RASTERIZER_CANVAS_RD_H +#define RASTERIZER_CANVAS_RD_H + +#include "servers/rendering/rasterizer.h" +#include "servers/rendering/rasterizer_rd/rasterizer_storage_rd.h" +#include "servers/rendering/rasterizer_rd/render_pipeline_vertex_format_cache_rd.h" +#include "servers/rendering/rasterizer_rd/shader_compiler_rd.h" +#include "servers/rendering/rasterizer_rd/shaders/canvas.glsl.gen.h" +#include "servers/rendering/rasterizer_rd/shaders/canvas_occlusion.glsl.gen.h" +#include "servers/rendering/rendering_device.h" + +class RasterizerCanvasRD : public RasterizerCanvas { + + RasterizerStorageRD *storage; + + enum ShaderVariant { + SHADER_VARIANT_QUAD, + SHADER_VARIANT_NINEPATCH, + SHADER_VARIANT_PRIMITIVE, + SHADER_VARIANT_PRIMITIVE_POINTS, + SHADER_VARIANT_ATTRIBUTES, + SHADER_VARIANT_ATTRIBUTES_POINTS, + SHADER_VARIANT_QUAD_LIGHT, + SHADER_VARIANT_NINEPATCH_LIGHT, + SHADER_VARIANT_PRIMITIVE_LIGHT, + SHADER_VARIANT_PRIMITIVE_POINTS_LIGHT, + SHADER_VARIANT_ATTRIBUTES_LIGHT, + SHADER_VARIANT_ATTRIBUTES_POINTS_LIGHT, + SHADER_VARIANT_MAX + }; + + enum { + FLAGS_INSTANCING_STRIDE_MASK = 0xF, + FLAGS_INSTANCING_ENABLED = (1 << 4), + FLAGS_INSTANCING_HAS_COLORS = (1 << 5), + FLAGS_INSTANCING_COLOR_8BIT = (1 << 6), + FLAGS_INSTANCING_HAS_CUSTOM_DATA = (1 << 7), + FLAGS_INSTANCING_CUSTOM_DATA_8_BIT = (1 << 8), + + FLAGS_CLIP_RECT_UV = (1 << 9), + FLAGS_TRANSPOSE_RECT = (1 << 10), + FLAGS_USING_LIGHT_MASK = (1 << 11), + + FLAGS_NINEPACH_DRAW_CENTER = (1 << 12), + FLAGS_USING_PARTICLES = (1 << 13), + FLAGS_USE_PIXEL_SNAP = (1 << 14), + + FLAGS_USE_SKELETON = (1 << 15), + FLAGS_NINEPATCH_H_MODE_SHIFT = 16, + FLAGS_NINEPATCH_V_MODE_SHIFT = 18, + FLAGS_LIGHT_COUNT_SHIFT = 20, + + FLAGS_DEFAULT_NORMAL_MAP_USED = (1 << 26), + FLAGS_DEFAULT_SPECULAR_MAP_USED = (1 << 27) + + }; + + enum { + LIGHT_FLAGS_TEXTURE_MASK = 0xFFFF, + LIGHT_FLAGS_BLEND_SHIFT = 16, + LIGHT_FLAGS_BLEND_MASK = (3 << 16), + LIGHT_FLAGS_BLEND_MODE_ADD = (0 << 16), + LIGHT_FLAGS_BLEND_MODE_SUB = (1 << 16), + LIGHT_FLAGS_BLEND_MODE_MIX = (2 << 16), + LIGHT_FLAGS_BLEND_MODE_MASK = (3 << 16), + LIGHT_FLAGS_HAS_SHADOW = (1 << 20), + LIGHT_FLAGS_FILTER_SHIFT = 22 + + }; + + enum { + MAX_RENDER_ITEMS = 256 * 1024, + MAX_LIGHT_TEXTURES = 1024, + DEFAULT_MAX_LIGHTS_PER_ITEM = 16, + DEFAULT_MAX_LIGHTS_PER_RENDER = 256 + }; + + /****************/ + /**** SHADER ****/ + /****************/ + + enum PipelineVariant { + PIPELINE_VARIANT_QUAD, + PIPELINE_VARIANT_NINEPATCH, + PIPELINE_VARIANT_PRIMITIVE_TRIANGLES, + PIPELINE_VARIANT_PRIMITIVE_LINES, + PIPELINE_VARIANT_PRIMITIVE_POINTS, + PIPELINE_VARIANT_ATTRIBUTE_TRIANGLES, + PIPELINE_VARIANT_ATTRIBUTE_TRIANGLE_STRIP, + PIPELINE_VARIANT_ATTRIBUTE_LINES, + PIPELINE_VARIANT_ATTRIBUTE_LINES_STRIP, + PIPELINE_VARIANT_ATTRIBUTE_POINTS, + PIPELINE_VARIANT_MAX + }; + enum PipelineLightMode { + PIPELINE_LIGHT_MODE_DISABLED, + PIPELINE_LIGHT_MODE_ENABLED, + PIPELINE_LIGHT_MODE_MAX + }; + + struct PipelineVariants { + RenderPipelineVertexFormatCacheRD variants[PIPELINE_LIGHT_MODE_MAX][PIPELINE_VARIANT_MAX]; + }; + + struct { + CanvasShaderRD canvas_shader; + RID default_version; + RID default_version_rd_shader; + RID default_version_rd_shader_light; + RID quad_index_buffer; + RID quad_index_array; + PipelineVariants pipeline_variants; + + // default_skeleton uniform set + RID default_skeleton_uniform_buffer; + RID default_skeleton_texture_buffer; + + ShaderCompilerRD compiler; + } shader; + + struct ShaderData : public RasterizerStorageRD::ShaderData { + + enum BlendMode { //used internally + BLEND_MODE_MIX, + BLEND_MODE_ADD, + BLEND_MODE_SUB, + BLEND_MODE_MUL, + BLEND_MODE_PMALPHA, + BLEND_MODE_DISABLED, + }; + + enum LightMode { + LIGHT_MODE_NORMAL, + LIGHT_MODE_UNSHADED, + LIGHT_MODE_LIGHT_ONLY + }; + + bool valid; + RID version; + PipelineVariants pipeline_variants; + String path; + + Map<StringName, ShaderLanguage::ShaderNode::Uniform> uniforms; + Vector<ShaderCompilerRD::GeneratedCode::Texture> texture_uniforms; + + Vector<uint32_t> ubo_offsets; + uint32_t ubo_size; + + String code; + Map<StringName, RID> default_texture_params; + + bool uses_screen_texture; + bool uses_material_samplers; + + virtual void set_code(const String &p_Code); + virtual void set_default_texture_param(const StringName &p_name, RID p_texture); + virtual void get_param_list(List<PropertyInfo> *p_param_list) const; + virtual bool is_param_texture(const StringName &p_param) const; + virtual bool is_animated() const; + virtual bool casts_shadows() const; + virtual Variant get_default_parameter(const StringName &p_parameter) const; + ShaderData(); + virtual ~ShaderData(); + }; + + RasterizerStorageRD::ShaderData *_create_shader_func(); + static RasterizerStorageRD::ShaderData *_create_shader_funcs() { + return static_cast<RasterizerCanvasRD *>(singleton)->_create_shader_func(); + } + + struct MaterialData : public RasterizerStorageRD::MaterialData { + uint64_t last_frame; + ShaderData *shader_data; + RID uniform_buffer; + RID uniform_set; + Vector<RID> texture_cache; + Vector<uint8_t> ubo_data; + + virtual void set_render_priority(int p_priority) {} + virtual void set_next_pass(RID p_pass) {} + virtual void update_parameters(const Map<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty); + virtual ~MaterialData(); + }; + + RasterizerStorageRD::MaterialData *_create_material_func(ShaderData *p_shader); + static RasterizerStorageRD::MaterialData *_create_material_funcs(RasterizerStorageRD::ShaderData *p_shader) { + return static_cast<RasterizerCanvasRD *>(singleton)->_create_material_func(static_cast<ShaderData *>(p_shader)); + } + + /**************************/ + /**** TEXTURE BINDINGS ****/ + /**************************/ + + // bindings used to render commands, + // cached for performance. + + struct TextureBindingKey { + RID texture; + RID normalmap; + RID specular; + RID multimesh; + RS::CanvasItemTextureFilter texture_filter; + RS::CanvasItemTextureRepeat texture_repeat; + bool operator==(const TextureBindingKey &p_key) const { + return texture == p_key.texture && normalmap == p_key.normalmap && specular == p_key.specular && multimesh == p_key.specular && texture_filter == p_key.texture_filter && texture_repeat == p_key.texture_repeat; + } + }; + + struct TextureBindingKeyHasher { + static _FORCE_INLINE_ uint32_t hash(const TextureBindingKey &p_key) { + uint32_t hash = hash_djb2_one_64(p_key.texture.get_id()); + hash = hash_djb2_one_64(p_key.normalmap.get_id(), hash); + hash = hash_djb2_one_64(p_key.specular.get_id(), hash); + hash = hash_djb2_one_64(p_key.multimesh.get_id(), hash); + hash = hash_djb2_one_32(uint32_t(p_key.texture_filter) << 16 | uint32_t(p_key.texture_repeat), hash); + return hash; + } + }; + + struct TextureBinding { + TextureBindingID id; + TextureBindingKey key; + SelfList<TextureBinding> to_dispose; + uint32_t reference_count; + RID uniform_set; + TextureBinding() : + to_dispose(this) { + reference_count = 0; + } + }; + + struct { + SelfList<TextureBinding>::List to_dispose_list; + + TextureBindingID id_generator; + HashMap<TextureBindingKey, TextureBindingID, TextureBindingKeyHasher> texture_key_bindings; + HashMap<TextureBindingID, TextureBinding *> texture_bindings; + + TextureBindingID default_empty; + } bindings; + + RID _create_texture_binding(RID p_texture, RID p_normalmap, RID p_specular, RenderingServer::CanvasItemTextureFilter p_filter, RenderingServer::CanvasItemTextureRepeat p_repeat, RID p_multimesh); + void _dispose_bindings(); + + struct { + RS::CanvasItemTextureFilter default_filter; + RS::CanvasItemTextureRepeat default_repeat; + } default_samplers; + + /******************/ + /**** POLYGONS ****/ + /******************/ + + struct PolygonBuffers { + RD::VertexFormatID vertex_format_id; + RID vertex_buffer; + RID vertex_array; + RID index_buffer; + RID indices; + }; + + struct { + HashMap<PolygonID, PolygonBuffers> polygons; + PolygonID last_id; + } polygon_buffers; + + /********************/ + /**** PRIMITIVES ****/ + /********************/ + + struct { + RID index_array[4]; + } primitive_arrays; + + /*******************/ + /**** MATERIALS ****/ + /*******************/ + + /******************/ + /**** LIGHTING ****/ + /******************/ + + struct CanvasLight { + + RID texture; + struct { + int size; + RID texture; + RID depth; + RID fb; + } shadow; + }; + + RID_Owner<CanvasLight> canvas_light_owner; + + struct ShadowRenderPushConstant { + float projection[16]; + float modelview[8]; + float direction[2]; + float pad[2]; + }; + + struct OccluderPolygon { + + RS::CanvasOccluderPolygonCullMode cull_mode; + int point_count; + RID vertex_buffer; + RID vertex_array; + RID index_buffer; + RID index_array; + }; + + struct LightUniform { + float matrix[8]; //light to texture coordinate matrix + float shadow_matrix[8]; //light to shadow coordinate matrix + float color[4]; + float shadow_color[4]; + float position[2]; + uint32_t flags; //index to light texture + float height; + float shadow_pixel_size; + float pad[3]; + }; + + RID_Owner<OccluderPolygon> occluder_polygon_owner; + + struct { + CanvasOcclusionShaderRD shader; + RID shader_version; + RID render_pipelines[3]; + RD::VertexFormatID vertex_format; + RD::FramebufferFormatID framebuffer_format; + } shadow_render; + + /***************/ + /**** STATE ****/ + /***************/ + + //state that does not vary across rendering all items + + struct ItemStateData : public Item::CustomData { + + struct LightCache { + uint64_t light_version; + Light *light; + }; + + LightCache light_cache[DEFAULT_MAX_LIGHTS_PER_ITEM]; + uint32_t light_cache_count; + RID state_uniform_set_with_light; + RID state_uniform_set; + ItemStateData() { + + for (int i = 0; i < DEFAULT_MAX_LIGHTS_PER_ITEM; i++) { + light_cache[i].light_version = 0; + light_cache[i].light = NULL; + } + light_cache_count = 0xFFFFFFFF; + } + + ~ItemStateData() { + if (state_uniform_set_with_light.is_valid() && RD::get_singleton()->uniform_set_is_valid(state_uniform_set_with_light)) { + RD::get_singleton()->free(state_uniform_set_with_light); + } + if (state_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(state_uniform_set)) { + RD::get_singleton()->free(state_uniform_set); + } + } + }; + + struct State { + + //state buffer + struct Buffer { + float canvas_transform[16]; + float screen_transform[16]; + float canvas_normal_transform[16]; + float canvas_modulate[4]; + float screen_pixel_size[2]; + float time; + float pad; + + //uint32_t light_count; + //uint32_t pad[3]; + }; + + LightUniform *light_uniforms; + + RID lights_uniform_buffer; + RID canvas_state_buffer; + RID shadow_sampler; + + uint32_t max_lights_per_render; + uint32_t max_lights_per_item; + + double time; + } state; + + struct PushConstant { + float world[6]; + uint32_t flags; + uint32_t specular_shininess; + union { + //rect + struct { + float modulation[4]; + float ninepatch_margins[4]; + float dst_rect[4]; + float src_rect[4]; + float pad[2]; + }; + //primitive + struct { + float points[6]; // vec2 points[3] + float uvs[6]; // vec2 points[3] + uint32_t colors[6]; // colors encoded as half + }; + }; + float color_texture_pixel_size[2]; + uint32_t lights[4]; + }; + + struct SkeletonUniform { + float skeleton_transform[16]; + float skeleton_inverse[16]; + }; + + Item *items[MAX_RENDER_ITEMS]; + + Size2i _bind_texture_binding(TextureBindingID p_binding, RenderingDevice::DrawListID p_draw_list, uint32_t &flags); + void _render_item(RenderingDevice::DrawListID p_draw_list, const Item *p_item, RenderingDevice::FramebufferFormatID p_framebuffer_format, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, Light *p_lights, PipelineVariants *p_pipeline_variants); + void _render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, RID p_screen_uniform_set); + + _FORCE_INLINE_ void _update_transform_2d_to_mat2x4(const Transform2D &p_transform, float *p_mat2x4); + _FORCE_INLINE_ void _update_transform_2d_to_mat2x3(const Transform2D &p_transform, float *p_mat2x3); + + _FORCE_INLINE_ void _update_transform_2d_to_mat4(const Transform2D &p_transform, float *p_mat4); + _FORCE_INLINE_ void _update_transform_to_mat4(const Transform &p_transform, float *p_mat4); + + _FORCE_INLINE_ void _update_specular_shininess(const Color &p_transform, uint32_t *r_ss); + +public: + TextureBindingID request_texture_binding(RID p_texture, RID p_normalmap, RID p_specular, RS::CanvasItemTextureFilter p_filter, RS::CanvasItemTextureRepeat p_repeat, RID p_multimesh); + void free_texture_binding(TextureBindingID p_binding); + + PolygonID request_polygon(const Vector<int> &p_indices, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), const Vector<int> &p_bones = Vector<int>(), const Vector<float> &p_weights = Vector<float>()); + void free_polygon(PolygonID p_polygon); + + RID light_create(); + void light_set_texture(RID p_rid, RID p_texture); + void light_set_use_shadow(RID p_rid, bool p_enable, int p_resolution); + void light_update_shadow(RID p_rid, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders); + + RID occluder_polygon_create(); + void occluder_polygon_set_shape_as_lines(RID p_occluder, const Vector<Vector2> &p_lines); + void occluder_polygon_set_cull_mode(RID p_occluder, RS::CanvasOccluderPolygonCullMode p_mode); + + void canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, const Transform2D &p_canvas_transform); + + void canvas_debug_viewport_shadows(Light *p_lights_with_shadow){}; + + void draw_window_margins(int *p_margins, RID *p_margin_textures) {} + + void set_time(double p_time); + void update(); + bool free(RID p_rid); + RasterizerCanvasRD(RasterizerStorageRD *p_storage); + ~RasterizerCanvasRD(); +}; + +#endif // RASTERIZER_CANVAS_RD_H diff --git a/servers/rendering/rasterizer_rd/rasterizer_effects_rd.cpp b/servers/rendering/rasterizer_rd/rasterizer_effects_rd.cpp new file mode 100644 index 0000000000..cc94815152 --- /dev/null +++ b/servers/rendering/rasterizer_rd/rasterizer_effects_rd.cpp @@ -0,0 +1,1092 @@ +/*************************************************************************/ +/* rasterizer_effects_rd.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "rasterizer_effects_rd.h" +#include "core/os/os.h" +#include "core/project_settings.h" +#include "cubemap_coeffs.h" + +static _FORCE_INLINE_ void store_transform_3x3(const Basis &p_basis, float *p_array) { + p_array[0] = p_basis.elements[0][0]; + p_array[1] = p_basis.elements[1][0]; + p_array[2] = p_basis.elements[2][0]; + p_array[3] = 0; + p_array[4] = p_basis.elements[0][1]; + p_array[5] = p_basis.elements[1][1]; + p_array[6] = p_basis.elements[2][1]; + p_array[7] = 0; + p_array[8] = p_basis.elements[0][2]; + p_array[9] = p_basis.elements[1][2]; + p_array[10] = p_basis.elements[2][2]; + p_array[11] = 0; +} + +RID RasterizerEffectsRD::_get_uniform_set_from_image(RID p_image) { + + if (image_to_uniform_set_cache.has(p_image)) { + RID uniform_set = image_to_uniform_set_cache[p_image]; + if (RD::get_singleton()->uniform_set_is_valid(uniform_set)) { + return uniform_set; + } + } + Vector<RD::Uniform> uniforms; + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 0; + u.ids.push_back(p_image); + uniforms.push_back(u); + //any thing with the same configuration (one texture in binding 0 for set 0), is good + RID uniform_set = RD::get_singleton()->uniform_set_create(uniforms, luminance_reduce.shader.version_get_shader(luminance_reduce.shader_version, 0), 1); + + image_to_uniform_set_cache[p_image] = uniform_set; + + return uniform_set; +} + +RID RasterizerEffectsRD::_get_uniform_set_from_texture(RID p_texture, bool p_use_mipmaps) { + + if (texture_to_uniform_set_cache.has(p_texture)) { + RID uniform_set = texture_to_uniform_set_cache[p_texture]; + if (RD::get_singleton()->uniform_set_is_valid(uniform_set)) { + return uniform_set; + } + } + + Vector<RD::Uniform> uniforms; + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE; + u.binding = 0; + u.ids.push_back(p_use_mipmaps ? default_mipmap_sampler : default_sampler); + u.ids.push_back(p_texture); + uniforms.push_back(u); + //any thing with the same configuration (one texture in binding 0 for set 0), is good + RID uniform_set = RD::get_singleton()->uniform_set_create(uniforms, blur.shader.version_get_shader(blur.shader_version, 0), 0); + + texture_to_uniform_set_cache[p_texture] = uniform_set; + + return uniform_set; +} + +RID RasterizerEffectsRD::_get_compute_uniform_set_from_texture(RID p_texture, bool p_use_mipmaps) { + + if (texture_to_compute_uniform_set_cache.has(p_texture)) { + RID uniform_set = texture_to_compute_uniform_set_cache[p_texture]; + if (RD::get_singleton()->uniform_set_is_valid(uniform_set)) { + return uniform_set; + } + } + + Vector<RD::Uniform> uniforms; + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE; + u.binding = 0; + u.ids.push_back(p_use_mipmaps ? default_mipmap_sampler : default_sampler); + u.ids.push_back(p_texture); + uniforms.push_back(u); + //any thing with the same configuration (one texture in binding 0 for set 0), is good + RID uniform_set = RD::get_singleton()->uniform_set_create(uniforms, luminance_reduce.shader.version_get_shader(luminance_reduce.shader_version, 0), 0); + + texture_to_compute_uniform_set_cache[p_texture] = uniform_set; + + return uniform_set; +} + +void RasterizerEffectsRD::copy_to_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2 &p_rect, bool p_flip_y, bool p_force_luminance) { + + zeromem(&blur.push_constant, sizeof(BlurPushConstant)); + if (p_flip_y) { + blur.push_constant.flags |= BLUR_FLAG_FLIP_Y; + } + if (p_force_luminance) { + blur.push_constant.flags |= BLUR_COPY_FORCE_LUMINANCE; + } + + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD, Vector<Color>(), 1.0, 0, p_rect); + RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blur.pipelines[BLUR_MODE_SIMPLY_COPY].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer))); + RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_source_rd_texture), 0); + RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array); + RD::get_singleton()->draw_list_set_push_constant(draw_list, &blur.push_constant, sizeof(BlurPushConstant)); + RD::get_singleton()->draw_list_draw(draw_list, true); + RD::get_singleton()->draw_list_end(); +} + +void RasterizerEffectsRD::region_copy(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2 &p_region) { + + zeromem(&blur.push_constant, sizeof(BlurPushConstant)); + + if (p_region != Rect2()) { + blur.push_constant.flags = BLUR_FLAG_USE_BLUR_SECTION; + blur.push_constant.section[0] = p_region.position.x; + blur.push_constant.section[1] = p_region.position.y; + blur.push_constant.section[2] = p_region.size.width; + blur.push_constant.section[3] = p_region.size.height; + } + + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); + RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blur.pipelines[BLUR_MODE_SIMPLY_COPY].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer))); + RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_source_rd_texture), 0); + RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array); + RD::get_singleton()->draw_list_set_push_constant(draw_list, &blur.push_constant, sizeof(BlurPushConstant)); + RD::get_singleton()->draw_list_draw(draw_list, true); + RD::get_singleton()->draw_list_end(); +} + +void RasterizerEffectsRD::gaussian_blur(RID p_source_rd_texture, RID p_framebuffer_half, RID p_rd_texture_half, RID p_dest_framebuffer, const Vector2 &p_pixel_size, const Rect2 &p_region) { + + zeromem(&blur.push_constant, sizeof(BlurPushConstant)); + + uint32_t base_flags = 0; + if (p_region != Rect2()) { + base_flags = BLUR_FLAG_USE_BLUR_SECTION; + blur.push_constant.section[0] = p_region.position.x; + blur.push_constant.section[1] = p_region.position.y; + blur.push_constant.section[2] = p_region.size.width; + blur.push_constant.section[3] = p_region.size.height; + } + + blur.push_constant.pixel_size[0] = p_pixel_size.x; + blur.push_constant.pixel_size[1] = p_pixel_size.y; + + //HORIZONTAL + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer_half, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); + RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blur.pipelines[BLUR_MODE_GAUSSIAN_BLUR].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_framebuffer_half))); + RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_source_rd_texture), 0); + RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array); + + blur.push_constant.flags = base_flags | BLUR_FLAG_HORIZONTAL; + RD::get_singleton()->draw_list_set_push_constant(draw_list, &blur.push_constant, sizeof(BlurPushConstant)); + + RD::get_singleton()->draw_list_draw(draw_list, true); + RD::get_singleton()->draw_list_end(); + + //VERTICAL + draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); + RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blur.pipelines[BLUR_MODE_GAUSSIAN_BLUR].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer))); + RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_rd_texture_half), 0); + RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array); + + blur.push_constant.flags = base_flags; + RD::get_singleton()->draw_list_set_push_constant(draw_list, &blur.push_constant, sizeof(BlurPushConstant)); + + RD::get_singleton()->draw_list_draw(draw_list, true); + RD::get_singleton()->draw_list_end(); +} + +void RasterizerEffectsRD::gaussian_glow(RID p_source_rd_texture, RID p_framebuffer_half, RID p_rd_texture_half, RID p_dest_framebuffer, const Vector2 &p_pixel_size, float p_strength, bool p_first_pass, float p_luminance_cap, float p_exposure, float p_bloom, float p_hdr_bleed_treshold, float p_hdr_bleed_scale, RID p_auto_exposure, float p_auto_exposure_grey) { + + zeromem(&blur.push_constant, sizeof(BlurPushConstant)); + + BlurMode blur_mode = p_first_pass && p_auto_exposure.is_valid() ? BLUR_MODE_GAUSSIAN_GLOW_AUTO_EXPOSURE : BLUR_MODE_GAUSSIAN_GLOW; + uint32_t base_flags = 0; + + blur.push_constant.pixel_size[0] = p_pixel_size.x; + blur.push_constant.pixel_size[1] = p_pixel_size.y; + + blur.push_constant.glow_strength = p_strength; + blur.push_constant.glow_bloom = p_bloom; + blur.push_constant.glow_hdr_threshold = p_hdr_bleed_treshold; + blur.push_constant.glow_hdr_scale = p_hdr_bleed_scale; + blur.push_constant.glow_exposure = p_exposure; + blur.push_constant.glow_white = 0; //actually unused + blur.push_constant.glow_luminance_cap = p_luminance_cap; + blur.push_constant.glow_auto_exposure_grey = p_auto_exposure_grey; //unused also + + //HORIZONTAL + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer_half, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); + RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blur.pipelines[blur_mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_framebuffer_half))); + RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_source_rd_texture), 0); + if (p_auto_exposure.is_valid() && p_first_pass) { + RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_auto_exposure), 1); + } + RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array); + + blur.push_constant.flags = base_flags | BLUR_FLAG_HORIZONTAL | (p_first_pass ? BLUR_FLAG_GLOW_FIRST_PASS : 0); + RD::get_singleton()->draw_list_set_push_constant(draw_list, &blur.push_constant, sizeof(BlurPushConstant)); + + RD::get_singleton()->draw_list_draw(draw_list, true); + RD::get_singleton()->draw_list_end(); + + blur_mode = BLUR_MODE_GAUSSIAN_GLOW; + + //VERTICAL + draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); + RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blur.pipelines[blur_mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer))); + RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_rd_texture_half), 0); + RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array); + + blur.push_constant.flags = base_flags; + RD::get_singleton()->draw_list_set_push_constant(draw_list, &blur.push_constant, sizeof(BlurPushConstant)); + + RD::get_singleton()->draw_list_draw(draw_list, true); + RD::get_singleton()->draw_list_end(); +} + +void RasterizerEffectsRD::make_mipmap(RID p_source_rd_texture, RID p_dest_framebuffer, const Vector2 &p_pixel_size) { + + zeromem(&blur.push_constant, sizeof(BlurPushConstant)); + + blur.push_constant.pixel_size[0] = p_pixel_size.x; + blur.push_constant.pixel_size[1] = p_pixel_size.y; + + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); + RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blur.pipelines[BLUR_MODE_MIPMAP].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer))); + RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_source_rd_texture), 0); + RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array); + RD::get_singleton()->draw_list_set_push_constant(draw_list, &blur.push_constant, sizeof(BlurPushConstant)); + RD::get_singleton()->draw_list_draw(draw_list, true); + RD::get_singleton()->draw_list_end(); +} + +void RasterizerEffectsRD::copy_cubemap_to_dp(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2 &p_rect, float p_z_near, float p_z_far, float p_bias, bool p_dp_flip) { + + CopyToDPPushConstant push_constant; + push_constant.bias = p_bias; + push_constant.z_far = p_z_far; + push_constant.z_near = p_z_near; + push_constant.z_flip = p_dp_flip; + + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD, Vector<Color>(), 1.0, 0, p_rect); + RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, copy.pipelines[COPY_MODE_CUBE_TO_DP].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer))); + RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_source_rd_texture), 0); + RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array); + RD::get_singleton()->draw_list_set_push_constant(draw_list, &push_constant, sizeof(CopyToDPPushConstant)); + RD::get_singleton()->draw_list_draw(draw_list, true); + RD::get_singleton()->draw_list_end(); +} + +void RasterizerEffectsRD::tonemapper(RID p_source_color, RID p_dst_framebuffer, const TonemapSettings &p_settings) { + + zeromem(&tonemap.push_constant, sizeof(TonemapPushConstant)); + + tonemap.push_constant.use_bcs = p_settings.use_bcs; + tonemap.push_constant.bcs[0] = p_settings.brightness; + tonemap.push_constant.bcs[1] = p_settings.contrast; + tonemap.push_constant.bcs[2] = p_settings.saturation; + + tonemap.push_constant.use_glow = p_settings.use_glow; + tonemap.push_constant.glow_intensity = p_settings.glow_intensity; + tonemap.push_constant.glow_level_flags = p_settings.glow_level_flags; + tonemap.push_constant.glow_texture_size[0] = p_settings.glow_texture_size.x; + tonemap.push_constant.glow_texture_size[1] = p_settings.glow_texture_size.y; + tonemap.push_constant.glow_mode = p_settings.glow_mode; + + TonemapMode mode = p_settings.glow_use_bicubic_upscale ? TONEMAP_MODE_BICUBIC_GLOW_FILTER : TONEMAP_MODE_NORMAL; + + tonemap.push_constant.tonemapper = p_settings.tonemap_mode; + tonemap.push_constant.use_auto_exposure = p_settings.use_auto_exposure; + tonemap.push_constant.exposure = p_settings.exposure; + tonemap.push_constant.white = p_settings.white; + tonemap.push_constant.auto_exposure_grey = p_settings.auto_exposure_grey; + + tonemap.push_constant.use_color_correction = p_settings.use_color_correction; + + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dst_framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); + RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, tonemap.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dst_framebuffer))); + RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_source_color), 0); + RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_settings.exposure_texture), 1); + RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_settings.glow_texture, true), 2); + RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_settings.color_correction_texture), 3); + RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array); + + RD::get_singleton()->draw_list_set_push_constant(draw_list, &tonemap.push_constant, sizeof(TonemapPushConstant)); + RD::get_singleton()->draw_list_draw(draw_list, true); + RD::get_singleton()->draw_list_end(); +} + +void RasterizerEffectsRD::luminance_reduction(RID p_source_texture, const Size2i p_source_size, const Vector<RID> p_reduce, RID p_prev_luminance, float p_min_luminance, float p_max_luminance, float p_adjust, bool p_set) { + + luminance_reduce.push_constant.source_size[0] = p_source_size.x; + luminance_reduce.push_constant.source_size[1] = p_source_size.y; + luminance_reduce.push_constant.max_luminance = p_max_luminance; + luminance_reduce.push_constant.min_luminance = p_min_luminance; + luminance_reduce.push_constant.exposure_adjust = p_adjust; + + RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); + + for (int i = 0; i < p_reduce.size(); i++) { + + if (i == 0) { + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, luminance_reduce.pipelines[LUMINANCE_REDUCE_READ]); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_source_texture), 0); + } else { + + RD::get_singleton()->compute_list_add_barrier(compute_list); //needs barrier, wait until previous is done + + if (i == p_reduce.size() - 1 && !p_set) { + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, luminance_reduce.pipelines[LUMINANCE_REDUCE_WRITE]); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_prev_luminance), 2); + } else { + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, luminance_reduce.pipelines[LUMINANCE_REDUCE]); + } + + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_reduce[i - 1]), 0); + } + + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_reduce[i]), 1); + + RD::get_singleton()->compute_list_set_push_constant(compute_list, &luminance_reduce.push_constant, sizeof(LuminanceReducePushConstant)); + + int32_t x_groups = (luminance_reduce.push_constant.source_size[0] - 1) / 8 + 1; + int32_t y_groups = (luminance_reduce.push_constant.source_size[1] - 1) / 8 + 1; + + RD::get_singleton()->compute_list_dispatch(compute_list, x_groups, y_groups, 1); + + luminance_reduce.push_constant.source_size[0] = MAX(luminance_reduce.push_constant.source_size[0] / 8, 1); + luminance_reduce.push_constant.source_size[1] = MAX(luminance_reduce.push_constant.source_size[1] / 8, 1); + } + + RD::get_singleton()->compute_list_end(); +} + +void RasterizerEffectsRD::bokeh_dof(RID p_base_texture, RID p_depth_texture, const Size2i &p_base_texture_size, RID p_secondary_texture, RID p_halfsize_texture1, RID p_halfsize_texture2, bool p_dof_far, float p_dof_far_begin, float p_dof_far_size, bool p_dof_near, float p_dof_near_begin, float p_dof_near_size, float p_bokeh_size, RenderingServer::DOFBokehShape p_bokeh_shape, RS::DOFBlurQuality p_quality, bool p_use_jitter, float p_cam_znear, float p_cam_zfar, bool p_cam_orthogonal) { + + bokeh.push_constant.blur_far_active = p_dof_far; + bokeh.push_constant.blur_far_begin = p_dof_far_begin; + bokeh.push_constant.blur_far_end = p_dof_far_begin + p_dof_far_size; + + bokeh.push_constant.blur_near_active = p_dof_near; + bokeh.push_constant.blur_near_begin = p_dof_near_begin; + bokeh.push_constant.blur_near_end = MAX(0, p_dof_near_begin - p_dof_near_size); + bokeh.push_constant.use_jitter = p_use_jitter; + bokeh.push_constant.jitter_seed = Math::randf() * 1000.0; + + bokeh.push_constant.z_near = p_cam_znear; + bokeh.push_constant.z_far = p_cam_zfar; + bokeh.push_constant.orthogonal = p_cam_orthogonal; + bokeh.push_constant.blur_size = p_bokeh_size; + + bokeh.push_constant.second_pass = false; + bokeh.push_constant.half_size = false; + + bokeh.push_constant.blur_scale = 0.5; + + RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); + + /* FIRST PASS */ + // The alpha channel of the source color texture is filled with the expected circle size + // If used for DOF far, the size is positive, if used for near, its negative. + + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, bokeh.pipelines[BOKEH_GEN_BLUR_SIZE]); + + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_base_texture), 0); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_depth_texture), 1); + + int32_t x_groups = (p_base_texture_size.x - 1) / 8 + 1; + int32_t y_groups = (p_base_texture_size.y - 1) / 8 + 1; + bokeh.push_constant.size[0] = p_base_texture_size.x; + bokeh.push_constant.size[1] = p_base_texture_size.y; + + RD::get_singleton()->compute_list_set_push_constant(compute_list, &bokeh.push_constant, sizeof(BokehPushConstant)); + + RD::get_singleton()->compute_list_dispatch(compute_list, x_groups, y_groups, 1); + RD::get_singleton()->compute_list_add_barrier(compute_list); + + if (p_bokeh_shape == RS::DOF_BOKEH_BOX || p_bokeh_shape == RS::DOF_BOKEH_HEXAGON) { + + //second pass + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, bokeh.pipelines[p_bokeh_shape == RS::DOF_BOKEH_BOX ? BOKEH_GEN_BOKEH_BOX : BOKEH_GEN_BOKEH_HEXAGONAL]); + + static const int quality_samples[4] = { 6, 12, 12, 24 }; + + bokeh.push_constant.steps = quality_samples[p_quality]; + + if (p_quality == RS::DOF_BLUR_QUALITY_VERY_LOW || p_quality == RS::DOF_BLUR_QUALITY_LOW) { + //box and hexagon are more or less the same, and they can work in either half (very low and low quality) or full (medium and high quality_ sizes) + + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_halfsize_texture1), 0); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_base_texture), 1); + + x_groups = ((p_base_texture_size.x >> 1) - 1) / 8 + 1; + y_groups = ((p_base_texture_size.y >> 1) - 1) / 8 + 1; + bokeh.push_constant.size[0] = p_base_texture_size.x >> 1; + bokeh.push_constant.size[1] = p_base_texture_size.y >> 1; + bokeh.push_constant.half_size = true; + bokeh.push_constant.blur_size *= 0.5; + + } else { + //medium and high quality use full size + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_secondary_texture), 0); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_base_texture), 1); + } + + RD::get_singleton()->compute_list_set_push_constant(compute_list, &bokeh.push_constant, sizeof(BokehPushConstant)); + + RD::get_singleton()->compute_list_dispatch(compute_list, x_groups, y_groups, 1); + RD::get_singleton()->compute_list_add_barrier(compute_list); + + //third pass + bokeh.push_constant.second_pass = true; + + if (p_quality == RS::DOF_BLUR_QUALITY_VERY_LOW || p_quality == RS::DOF_BLUR_QUALITY_LOW) { + + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_halfsize_texture2), 0); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_halfsize_texture1), 1); + } else { + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_base_texture), 0); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_secondary_texture), 1); + } + + RD::get_singleton()->compute_list_set_push_constant(compute_list, &bokeh.push_constant, sizeof(BokehPushConstant)); + + RD::get_singleton()->compute_list_dispatch(compute_list, x_groups, y_groups, 1); + RD::get_singleton()->compute_list_add_barrier(compute_list); + + if (p_quality == RS::DOF_BLUR_QUALITY_VERY_LOW || p_quality == RS::DOF_BLUR_QUALITY_LOW) { + //forth pass, upscale for low quality + + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, bokeh.pipelines[BOKEH_COMPOSITE]); + + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_base_texture), 0); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_halfsize_texture2), 1); + + x_groups = (p_base_texture_size.x - 1) / 8 + 1; + y_groups = (p_base_texture_size.y - 1) / 8 + 1; + bokeh.push_constant.size[0] = p_base_texture_size.x; + bokeh.push_constant.size[1] = p_base_texture_size.y; + bokeh.push_constant.half_size = false; + bokeh.push_constant.second_pass = false; + + RD::get_singleton()->compute_list_set_push_constant(compute_list, &bokeh.push_constant, sizeof(BokehPushConstant)); + + RD::get_singleton()->compute_list_dispatch(compute_list, x_groups, y_groups, 1); + } + } else { + //circle + + //second pass + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, bokeh.pipelines[BOKEH_GEN_BOKEH_CIRCULAR]); + + static const float quality_scale[4] = { 8.0, 4.0, 1.0, 0.5 }; + + bokeh.push_constant.steps = 0; + bokeh.push_constant.blur_scale = quality_scale[p_quality]; + + //circle always runs in half size, otherwise too expensive + + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_halfsize_texture1), 0); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_base_texture), 1); + + x_groups = ((p_base_texture_size.x >> 1) - 1) / 8 + 1; + y_groups = ((p_base_texture_size.y >> 1) - 1) / 8 + 1; + bokeh.push_constant.size[0] = p_base_texture_size.x >> 1; + bokeh.push_constant.size[1] = p_base_texture_size.y >> 1; + bokeh.push_constant.half_size = true; + + RD::get_singleton()->compute_list_set_push_constant(compute_list, &bokeh.push_constant, sizeof(BokehPushConstant)); + + RD::get_singleton()->compute_list_dispatch(compute_list, x_groups, y_groups, 1); + RD::get_singleton()->compute_list_add_barrier(compute_list); + + //circle is just one pass, then upscale + + // upscale + + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, bokeh.pipelines[BOKEH_COMPOSITE]); + + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_base_texture), 0); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_halfsize_texture1), 1); + + x_groups = (p_base_texture_size.x - 1) / 8 + 1; + y_groups = (p_base_texture_size.y - 1) / 8 + 1; + bokeh.push_constant.size[0] = p_base_texture_size.x; + bokeh.push_constant.size[1] = p_base_texture_size.y; + bokeh.push_constant.half_size = false; + bokeh.push_constant.second_pass = false; + + RD::get_singleton()->compute_list_set_push_constant(compute_list, &bokeh.push_constant, sizeof(BokehPushConstant)); + + RD::get_singleton()->compute_list_dispatch(compute_list, x_groups, y_groups, 1); + } + + RD::get_singleton()->compute_list_end(); +} + +void RasterizerEffectsRD::generate_ssao(RID p_depth_buffer, RID p_normal_buffer, const Size2i &p_depth_buffer_size, RID p_depth_mipmaps_texture, const Vector<RID> &depth_mipmaps, RID p_ao1, bool p_half_size, RID p_ao2, RID p_upscale_buffer, float p_intensity, float p_radius, float p_bias, const CameraMatrix &p_projection, RS::EnvironmentSSAOQuality p_quality, RS::EnvironmentSSAOBlur p_blur, float p_edge_sharpness) { + + //minify first + ssao.minify_push_constant.orthogonal = p_projection.is_orthogonal(); + ssao.minify_push_constant.z_near = p_projection.get_z_near(); + ssao.minify_push_constant.z_far = p_projection.get_z_far(); + ssao.minify_push_constant.pixel_size[0] = 1.0 / p_depth_buffer_size.x; + ssao.minify_push_constant.pixel_size[1] = 1.0 / p_depth_buffer_size.y; + ssao.minify_push_constant.source_size[0] = p_depth_buffer_size.x; + ssao.minify_push_constant.source_size[1] = p_depth_buffer_size.y; + + RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); + + /* FIRST PASS */ + // Minify the depth buffer. + + for (int i = 0; i < depth_mipmaps.size(); i++) { + + if (i == 0) { + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, ssao.pipelines[SSAO_MINIFY_FIRST]); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_depth_buffer), 0); + } else { + if (i == 1) { + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, ssao.pipelines[SSAO_MINIFY_MIPMAP]); + } + + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(depth_mipmaps[i - 1]), 0); + } + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(depth_mipmaps[i]), 1); + + RD::get_singleton()->compute_list_set_push_constant(compute_list, &ssao.minify_push_constant, sizeof(SSAOMinifyPushConstant)); + // shrink after set + ssao.minify_push_constant.source_size[0] = MAX(1, ssao.minify_push_constant.source_size[0] >> 1); + ssao.minify_push_constant.source_size[1] = MAX(1, ssao.minify_push_constant.source_size[1] >> 1); + + int x_groups = (ssao.minify_push_constant.source_size[0] - 1) / 8 + 1; + int y_groups = (ssao.minify_push_constant.source_size[1] - 1) / 8 + 1; + + RD::get_singleton()->compute_list_dispatch(compute_list, x_groups, y_groups, 1); + RD::get_singleton()->compute_list_add_barrier(compute_list); + } + + /* SECOND PASS */ + // Gather samples + + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, ssao.pipelines[(SSAO_GATHER_LOW + p_quality) + (p_half_size ? 4 : 0)]); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_depth_mipmaps_texture), 0); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_ao1), 1); + if (!p_half_size) { + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_depth_buffer), 2); + } + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_normal_buffer), 3); + + ssao.gather_push_constant.screen_size[0] = p_depth_buffer_size.x; + ssao.gather_push_constant.screen_size[1] = p_depth_buffer_size.y; + if (p_half_size) { + ssao.gather_push_constant.screen_size[0] >>= 1; + ssao.gather_push_constant.screen_size[1] >>= 1; + } + ssao.gather_push_constant.z_far = p_projection.get_z_far(); + ssao.gather_push_constant.z_near = p_projection.get_z_near(); + ssao.gather_push_constant.orthogonal = p_projection.is_orthogonal(); + + ssao.gather_push_constant.proj_info[0] = -2.0f / (ssao.gather_push_constant.screen_size[0] * p_projection.matrix[0][0]); + ssao.gather_push_constant.proj_info[1] = -2.0f / (ssao.gather_push_constant.screen_size[1] * p_projection.matrix[1][1]); + ssao.gather_push_constant.proj_info[2] = (1.0f - p_projection.matrix[0][2]) / p_projection.matrix[0][0]; + ssao.gather_push_constant.proj_info[3] = (1.0f + p_projection.matrix[1][2]) / p_projection.matrix[1][1]; + //ssao.gather_push_constant.proj_info[2] = (1.0f - p_projection.matrix[0][2]) / p_projection.matrix[0][0]; + //ssao.gather_push_constant.proj_info[3] = -(1.0f + p_projection.matrix[1][2]) / p_projection.matrix[1][1]; + + ssao.gather_push_constant.radius = p_radius; + + ssao.gather_push_constant.proj_scale = float(p_projection.get_pixels_per_meter(ssao.gather_push_constant.screen_size[0])); + ssao.gather_push_constant.bias = p_bias; + ssao.gather_push_constant.intensity_div_r6 = p_intensity / pow(p_radius, 6.0f); + + ssao.gather_push_constant.pixel_size[0] = 1.0 / p_depth_buffer_size.x; + ssao.gather_push_constant.pixel_size[1] = 1.0 / p_depth_buffer_size.y; + + RD::get_singleton()->compute_list_set_push_constant(compute_list, &ssao.gather_push_constant, sizeof(SSAOGatherPushConstant)); + + int x_groups = (ssao.gather_push_constant.screen_size[0] - 1) / 8 + 1; + int y_groups = (ssao.gather_push_constant.screen_size[1] - 1) / 8 + 1; + + RD::get_singleton()->compute_list_dispatch(compute_list, x_groups, y_groups, 1); + RD::get_singleton()->compute_list_add_barrier(compute_list); + + /* THIRD PASS */ + // Blur horizontal + + ssao.blur_push_constant.edge_sharpness = p_edge_sharpness; + ssao.blur_push_constant.filter_scale = p_blur; + ssao.blur_push_constant.screen_size[0] = ssao.gather_push_constant.screen_size[0]; + ssao.blur_push_constant.screen_size[1] = ssao.gather_push_constant.screen_size[1]; + ssao.blur_push_constant.z_far = p_projection.get_z_far(); + ssao.blur_push_constant.z_near = p_projection.get_z_near(); + ssao.blur_push_constant.orthogonal = p_projection.is_orthogonal(); + ssao.blur_push_constant.axis[0] = 1; + ssao.blur_push_constant.axis[1] = 0; + + if (p_blur != RS::ENV_SSAO_BLUR_DISABLED) { + + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, ssao.pipelines[p_half_size ? SSAO_BLUR_PASS_HALF : SSAO_BLUR_PASS]); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_ao1), 0); + if (p_half_size) { + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_depth_mipmaps_texture), 1); + } else { + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_depth_buffer), 1); + } + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_ao2), 3); + + RD::get_singleton()->compute_list_set_push_constant(compute_list, &ssao.blur_push_constant, sizeof(SSAOBlurPushConstant)); + + RD::get_singleton()->compute_list_dispatch(compute_list, x_groups, y_groups, 1); + RD::get_singleton()->compute_list_add_barrier(compute_list); + + /* THIRD PASS */ + // Blur vertical + + ssao.blur_push_constant.axis[0] = 0; + ssao.blur_push_constant.axis[1] = 1; + + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_ao2), 0); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_ao1), 3); + + RD::get_singleton()->compute_list_set_push_constant(compute_list, &ssao.blur_push_constant, sizeof(SSAOBlurPushConstant)); + + RD::get_singleton()->compute_list_dispatch(compute_list, x_groups, y_groups, 1); + } + if (p_half_size) { //must upscale + + /* FOURTH PASS */ + // upscale if half size + //back to full size + ssao.blur_push_constant.screen_size[0] = p_depth_buffer_size.x; + ssao.blur_push_constant.screen_size[1] = p_depth_buffer_size.y; + + RD::get_singleton()->compute_list_add_barrier(compute_list); + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, ssao.pipelines[SSAO_BLUR_UPSCALE]); + + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_ao1), 0); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_upscale_buffer), 3); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_depth_buffer), 1); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_depth_mipmaps_texture), 2); + + RD::get_singleton()->compute_list_set_push_constant(compute_list, &ssao.blur_push_constant, sizeof(SSAOBlurPushConstant)); //not used but set anyway + + x_groups = (p_depth_buffer_size.x - 1) / 8 + 1; + y_groups = (p_depth_buffer_size.y - 1) / 8 + 1; + + RD::get_singleton()->compute_list_dispatch(compute_list, x_groups, y_groups, 1); + } + + RD::get_singleton()->compute_list_end(); +} + +void RasterizerEffectsRD::roughness_limit(RID p_source_normal, RID p_roughness, const Size2i &p_size, float p_curve) { + + roughness_limiter.push_constant.screen_size[0] = p_size.x; + roughness_limiter.push_constant.screen_size[1] = p_size.y; + roughness_limiter.push_constant.curve = p_curve; + + RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, roughness_limiter.pipeline); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_source_normal), 0); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_roughness), 1); + + int x_groups = (p_size.x - 1) / 8 + 1; + int y_groups = (p_size.y - 1) / 8 + 1; + + RD::get_singleton()->compute_list_set_push_constant(compute_list, &roughness_limiter.push_constant, sizeof(RoughnessLimiterPushConstant)); //not used but set anyway + + RD::get_singleton()->compute_list_dispatch(compute_list, x_groups, y_groups, 1); + + RD::get_singleton()->compute_list_end(); +} + +void RasterizerEffectsRD::cubemap_roughness(RID p_source_rd_texture, RID p_dest_framebuffer, uint32_t p_face_id, uint32_t p_sample_count, float p_roughness, float p_size) { + + zeromem(&roughness.push_constant, sizeof(CubemapRoughnessPushConstant)); + + roughness.push_constant.face_id = p_face_id > 9 ? 0 : p_face_id; + roughness.push_constant.roughness = p_roughness; + roughness.push_constant.sample_count = p_sample_count; + roughness.push_constant.use_direct_write = p_roughness == 0.0; + roughness.push_constant.face_size = p_size; + + RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, roughness.pipeline); + + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_source_rd_texture), 0); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_dest_framebuffer), 1); + + RD::get_singleton()->compute_list_set_push_constant(compute_list, &roughness.push_constant, sizeof(CubemapRoughnessPushConstant)); + + int x_groups = (p_size - 1) / 8 + 1; + int y_groups = (p_size - 1) / 8 + 1; + + RD::get_singleton()->compute_list_dispatch(compute_list, x_groups, y_groups, p_face_id > 9 ? 6 : 1); + + RD::get_singleton()->compute_list_end(); +} + +void RasterizerEffectsRD::cubemap_downsample(RID p_source_cubemap, RID p_dest_cubemap, const Size2i &p_size) { + + cubemap_downsampler.push_constant.face_size = p_size.x; + + RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, cubemap_downsampler.pipeline); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_source_cubemap), 0); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_dest_cubemap), 1); + + int x_groups = (p_size.x - 1) / 8 + 1; + int y_groups = (p_size.y - 1) / 8 + 1; + + RD::get_singleton()->compute_list_set_push_constant(compute_list, &cubemap_downsampler.push_constant, sizeof(CubemapDownsamplerPushConstant)); + + RD::get_singleton()->compute_list_dispatch(compute_list, x_groups, y_groups, 6); // one z_group for each face + + RD::get_singleton()->compute_list_end(); +} + +void RasterizerEffectsRD::cubemap_filter(RID p_source_cubemap, Vector<RID> p_dest_cubemap, bool p_use_array) { + + Vector<RD::Uniform> uniforms; + for (int i = 0; i < p_dest_cubemap.size(); i++) { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = i; + u.ids.push_back(p_dest_cubemap[i]); + uniforms.push_back(u); + } + if (RD::get_singleton()->uniform_set_is_valid(filter.image_uniform_set)) { + RD::get_singleton()->free(filter.image_uniform_set); + } + filter.image_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, filter.shader.version_get_shader(filter.shader_version, 0), 2); + + int pipeline = p_use_array ? FILTER_MODE_HIGH_QUALITY_ARRAY : FILTER_MODE_HIGH_QUALITY; + pipeline = filter.use_high_quality ? pipeline : pipeline + 1; + RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, filter.pipelines[pipeline]); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_source_cubemap, true), 0); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, filter.uniform_set, 1); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, filter.image_uniform_set, 2); + + int x_groups = p_use_array ? 1792 : 342; // (128 * 128 * 7) / 64 : (128*128 + 64*64 + 32*32 + 16*16 + 8*8 + 4*4 + 2*2) / 64 + + RD::get_singleton()->compute_list_dispatch(compute_list, x_groups, 6, 1); // one y_group for each face + + RD::get_singleton()->compute_list_end(); +} + +void RasterizerEffectsRD::render_sky(RD::DrawListID p_list, float p_time, RID p_fb, RID p_samplers, RID p_lights, RenderPipelineVertexFormatCacheRD *p_pipeline, RID p_uniform_set, RID p_texture_set, const CameraMatrix &p_camera, const Basis &p_orientation, float p_multiplier, const Vector3 &p_position) { + + SkyPushConstant sky_push_constant; + + zeromem(&sky_push_constant, sizeof(SkyPushConstant)); + + sky_push_constant.proj[0] = p_camera.matrix[2][0]; + sky_push_constant.proj[1] = p_camera.matrix[0][0]; + sky_push_constant.proj[2] = p_camera.matrix[2][1]; + sky_push_constant.proj[3] = p_camera.matrix[1][1]; + sky_push_constant.position[0] = p_position.x; + sky_push_constant.position[1] = p_position.y; + sky_push_constant.position[2] = p_position.z; + sky_push_constant.multiplier = p_multiplier; + sky_push_constant.time = p_time; + store_transform_3x3(p_orientation, sky_push_constant.orientation); + + RenderingDevice::FramebufferFormatID fb_format = RD::get_singleton()->framebuffer_get_format(p_fb); + + RD::DrawListID draw_list = p_list; + + RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, p_pipeline->get_render_pipeline(RD::INVALID_ID, fb_format)); + + RD::get_singleton()->draw_list_bind_uniform_set(draw_list, p_samplers, 0); + RD::get_singleton()->draw_list_bind_uniform_set(draw_list, p_uniform_set, 1); + RD::get_singleton()->draw_list_bind_uniform_set(draw_list, p_texture_set, 2); + RD::get_singleton()->draw_list_bind_uniform_set(draw_list, p_lights, 3); + + RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array); + + RD::get_singleton()->draw_list_set_push_constant(draw_list, &sky_push_constant, sizeof(SkyPushConstant)); + + RD::get_singleton()->draw_list_draw(draw_list, true); +} + +RasterizerEffectsRD::RasterizerEffectsRD() { + + { + // Initialize blur + Vector<String> blur_modes; + blur_modes.push_back("\n#define MODE_GAUSSIAN_BLUR\n"); + blur_modes.push_back("\n#define MODE_GAUSSIAN_GLOW\n"); + blur_modes.push_back("\n#define MODE_GAUSSIAN_GLOW\n#define GLOW_USE_AUTO_EXPOSURE\n"); + blur_modes.push_back("\n#define MODE_DOF_NEAR_BLUR\n#define DOF_QUALITY_LOW\n"); + blur_modes.push_back("\n#define MODE_DOF_NEAR_BLUR\n#define DOF_QUALITY_MEDIUM\n"); + blur_modes.push_back("\n#define MODE_DOF_NEAR_BLUR\n#define DOF_QUALITY_HIGH\n"); + blur_modes.push_back("\n#define MODE_DOF_NEAR_BLUR\n#define DOF_QUALITY_LOW\n#define DOF_NEAR_BLUR_MERGE\n"); + blur_modes.push_back("\n#define MODE_DOF_NEAR_BLUR\n#define DOF_QUALITY_MEDIUM\n#define DOF_NEAR_BLUR_MERGE\n"); + blur_modes.push_back("\n#define MODE_DOF_NEAR_BLUR\n#define DOF_QUALITY_HIGH\n#define DOF_NEAR_BLUR_MERGE\n"); + blur_modes.push_back("\n#define MODE_DOF_FAR_BLUR\n#define DOF_QUALITY_LOW\n"); + blur_modes.push_back("\n#define MODE_DOF_FAR_BLUR\n#define DOF_QUALITY_MEDIUM\n"); + blur_modes.push_back("\n#define MODE_DOF_FAR_BLUR\n#define DOF_QUALITY_HIGH\n"); + blur_modes.push_back("\n#define MODE_SSAO_MERGE\n"); + blur_modes.push_back("\n#define MODE_SIMPLE_COPY\n"); + blur_modes.push_back("\n#define MODE_MIPMAP\n"); + + blur.shader.initialize(blur_modes); + zeromem(&blur.push_constant, sizeof(BlurPushConstant)); + blur.shader_version = blur.shader.version_create(); + + for (int i = 0; i < BLUR_MODE_MAX; i++) { + blur.pipelines[i].setup(blur.shader.version_get_shader(blur.shader_version, i), RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_disabled(), 0); + } + } + + { + // Initialize roughness + Vector<String> cubemap_roughness_modes; + cubemap_roughness_modes.push_back(""); + roughness.shader.initialize(cubemap_roughness_modes); + + roughness.shader_version = roughness.shader.version_create(); + + roughness.pipeline = RD::get_singleton()->compute_pipeline_create(roughness.shader.version_get_shader(roughness.shader_version, 0)); + } + + { + // Initialize tonemapper + Vector<String> tonemap_modes; + tonemap_modes.push_back("\n"); + tonemap_modes.push_back("\n#define USE_GLOW_FILTER_BICUBIC\n"); + + tonemap.shader.initialize(tonemap_modes); + + tonemap.shader_version = tonemap.shader.version_create(); + + for (int i = 0; i < TONEMAP_MODE_MAX; i++) { + tonemap.pipelines[i].setup(tonemap.shader.version_get_shader(tonemap.shader_version, i), RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_disabled(), 0); + } + } + + { + // Initialize luminance_reduce + Vector<String> luminance_reduce_modes; + luminance_reduce_modes.push_back("\n#define READ_TEXTURE\n"); + luminance_reduce_modes.push_back("\n"); + luminance_reduce_modes.push_back("\n#define WRITE_LUMINANCE\n"); + + luminance_reduce.shader.initialize(luminance_reduce_modes); + + luminance_reduce.shader_version = luminance_reduce.shader.version_create(); + + for (int i = 0; i < LUMINANCE_REDUCE_MAX; i++) { + luminance_reduce.pipelines[i] = RD::get_singleton()->compute_pipeline_create(luminance_reduce.shader.version_get_shader(luminance_reduce.shader_version, i)); + } + } + + { + // Initialize copier + Vector<String> copy_modes; + copy_modes.push_back("\n#define MODE_CUBE_TO_DP\n"); + + copy.shader.initialize(copy_modes); + + copy.shader_version = copy.shader.version_create(); + + for (int i = 0; i < COPY_MODE_MAX; i++) { + copy.pipelines[i].setup(copy.shader.version_get_shader(copy.shader_version, i), RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_disabled(), 0); + } + } + + { + // Initialize bokeh + Vector<String> bokeh_modes; + bokeh_modes.push_back("\n#define MODE_GEN_BLUR_SIZE\n"); + bokeh_modes.push_back("\n#define MODE_BOKEH_BOX\n"); + bokeh_modes.push_back("\n#define MODE_BOKEH_HEXAGONAL\n"); + bokeh_modes.push_back("\n#define MODE_BOKEH_CIRCULAR\n"); + bokeh_modes.push_back("\n#define MODE_COMPOSITE_BOKEH\n"); + + bokeh.shader.initialize(bokeh_modes); + + bokeh.shader_version = bokeh.shader.version_create(); + + for (int i = 0; i < BOKEH_MAX; i++) { + bokeh.pipelines[i] = RD::get_singleton()->compute_pipeline_create(bokeh.shader.version_get_shader(bokeh.shader_version, i)); + } + } + + { + // Initialize ssao + uint32_t pipeline = 0; + { + Vector<String> ssao_modes; + ssao_modes.push_back("\n#define MINIFY_START\n"); + ssao_modes.push_back("\n"); + + ssao.minify_shader.initialize(ssao_modes); + + ssao.minify_shader_version = ssao.minify_shader.version_create(); + + for (int i = 0; i <= SSAO_MINIFY_MIPMAP; i++) { + ssao.pipelines[pipeline] = RD::get_singleton()->compute_pipeline_create(ssao.minify_shader.version_get_shader(ssao.minify_shader_version, i)); + pipeline++; + } + } + { + Vector<String> ssao_modes; + ssao_modes.push_back("\n#define SSAO_QUALITY_LOW\n"); + ssao_modes.push_back("\n"); + ssao_modes.push_back("\n#define SSAO_QUALITY_HIGH\n"); + ssao_modes.push_back("\n#define SSAO_QUALITY_ULTRA\n"); + ssao_modes.push_back("\n#define SSAO_QUALITY_LOW\n#define USE_HALF_SIZE\n"); + ssao_modes.push_back("\n#define USE_HALF_SIZE\n"); + ssao_modes.push_back("\n#define SSAO_QUALITY_HIGH\n#define USE_HALF_SIZE\n"); + ssao_modes.push_back("\n#define SSAO_QUALITY_ULTRA\n#define USE_HALF_SIZE\n"); + + ssao.gather_shader.initialize(ssao_modes); + + ssao.gather_shader_version = ssao.gather_shader.version_create(); + + for (int i = SSAO_GATHER_LOW; i <= SSAO_GATHER_ULTRA_HALF; i++) { + ssao.pipelines[pipeline] = RD::get_singleton()->compute_pipeline_create(ssao.gather_shader.version_get_shader(ssao.gather_shader_version, i - SSAO_GATHER_LOW)); + pipeline++; + } + } + { + Vector<String> ssao_modes; + ssao_modes.push_back("\n#define MODE_FULL_SIZE\n"); + ssao_modes.push_back("\n"); + ssao_modes.push_back("\n#define MODE_UPSCALE\n"); + + ssao.blur_shader.initialize(ssao_modes); + + ssao.blur_shader_version = ssao.blur_shader.version_create(); + + for (int i = SSAO_BLUR_PASS; i <= SSAO_BLUR_UPSCALE; i++) { + ssao.pipelines[pipeline] = RD::get_singleton()->compute_pipeline_create(ssao.blur_shader.version_get_shader(ssao.blur_shader_version, i - SSAO_BLUR_PASS)); + pipeline++; + } + } + + ERR_FAIL_COND(pipeline != SSAO_MAX); + } + + { + // Initialize roughness limiter + Vector<String> shader_modes; + shader_modes.push_back(""); + + roughness_limiter.shader.initialize(shader_modes); + + roughness_limiter.shader_version = roughness_limiter.shader.version_create(); + + roughness_limiter.pipeline = RD::get_singleton()->compute_pipeline_create(roughness_limiter.shader.version_get_shader(roughness_limiter.shader_version, 0)); + } + + { + //Initialize cubemap downsampler + Vector<String> cubemap_downsampler_modes; + cubemap_downsampler_modes.push_back(""); + cubemap_downsampler.shader.initialize(cubemap_downsampler_modes); + + cubemap_downsampler.shader_version = cubemap_downsampler.shader.version_create(); + + cubemap_downsampler.pipeline = RD::get_singleton()->compute_pipeline_create(cubemap_downsampler.shader.version_get_shader(cubemap_downsampler.shader_version, 0)); + } + + { + // Initialize cubemap filter + filter.use_high_quality = GLOBAL_GET("rendering/quality/reflections/fast_filter_high_quality"); + + Vector<String> cubemap_filter_modes; + cubemap_filter_modes.push_back("\n#define USE_HIGH_QUALITY\n"); + cubemap_filter_modes.push_back("\n#define USE_LOW_QUALITY\n"); + cubemap_filter_modes.push_back("\n#define USE_HIGH_QUALITY\n#define USE_TEXTURE_ARRAY\n"); + cubemap_filter_modes.push_back("\n#define USE_LOW_QUALITY\n#define USE_TEXTURE_ARRAY\n"); + filter.shader.initialize(cubemap_filter_modes); + filter.shader_version = filter.shader.version_create(); + + for (int i = 0; i < FILTER_MODE_MAX; i++) { + filter.pipelines[i] = RD::get_singleton()->compute_pipeline_create(filter.shader.version_get_shader(filter.shader_version, i)); + } + + if (filter.use_high_quality) { + filter.coefficient_buffer = RD::get_singleton()->storage_buffer_create(sizeof(high_quality_coeffs)); + RD::get_singleton()->buffer_update(filter.coefficient_buffer, 0, sizeof(high_quality_coeffs), &high_quality_coeffs[0], false); + } else { + filter.coefficient_buffer = RD::get_singleton()->storage_buffer_create(sizeof(low_quality_coeffs)); + RD::get_singleton()->buffer_update(filter.coefficient_buffer, 0, sizeof(low_quality_coeffs), &low_quality_coeffs[0], false); + } + + Vector<RD::Uniform> uniforms; + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER; + u.binding = 0; + u.ids.push_back(filter.coefficient_buffer); + uniforms.push_back(u); + } + filter.uniform_set = RD::get_singleton()->uniform_set_create(uniforms, filter.shader.version_get_shader(filter.shader_version, filter.use_high_quality ? 0 : 1), 1); + } + + RD::SamplerState sampler; + sampler.mag_filter = RD::SAMPLER_FILTER_LINEAR; + sampler.min_filter = RD::SAMPLER_FILTER_LINEAR; + sampler.max_lod = 0; + + default_sampler = RD::get_singleton()->sampler_create(sampler); + + sampler.min_filter = RD::SAMPLER_FILTER_LINEAR; + sampler.mip_filter = RD::SAMPLER_FILTER_LINEAR; + sampler.max_lod = 1e20; + + default_mipmap_sampler = RD::get_singleton()->sampler_create(sampler); + + { //create index array for copy shaders + Vector<uint8_t> pv; + pv.resize(6 * 4); + { + uint8_t *w = pv.ptrw(); + int *p32 = (int *)w; + p32[0] = 0; + p32[1] = 1; + p32[2] = 2; + p32[3] = 0; + p32[4] = 2; + p32[5] = 3; + } + index_buffer = RD::get_singleton()->index_buffer_create(6, RenderingDevice::INDEX_BUFFER_FORMAT_UINT32, pv); + index_array = RD::get_singleton()->index_array_create(index_buffer, 0, 6); + } +} + +RasterizerEffectsRD::~RasterizerEffectsRD() { + if (RD::get_singleton()->uniform_set_is_valid(filter.image_uniform_set)) { + RD::get_singleton()->free(filter.image_uniform_set); + } + + if (RD::get_singleton()->uniform_set_is_valid(filter.uniform_set)) { + RD::get_singleton()->free(filter.uniform_set); + } + + RD::get_singleton()->free(default_sampler); + RD::get_singleton()->free(default_mipmap_sampler); + RD::get_singleton()->free(index_buffer); //array gets freed as dependency + RD::get_singleton()->free(filter.coefficient_buffer); + blur.shader.version_free(blur.shader_version); + roughness.shader.version_free(roughness.shader_version); + tonemap.shader.version_free(tonemap.shader_version); + luminance_reduce.shader.version_free(luminance_reduce.shader_version); + copy.shader.version_free(copy.shader_version); + bokeh.shader.version_free(bokeh.shader_version); + ssao.minify_shader.version_free(ssao.minify_shader_version); + ssao.gather_shader.version_free(ssao.gather_shader_version); + ssao.blur_shader.version_free(ssao.blur_shader_version); + roughness_limiter.shader.version_free(roughness_limiter.shader_version); + cubemap_downsampler.shader.version_free(cubemap_downsampler.shader_version); + filter.shader.version_free(filter.shader_version); +} diff --git a/servers/rendering/rasterizer_rd/rasterizer_effects_rd.h b/servers/rendering/rasterizer_rd/rasterizer_effects_rd.h new file mode 100644 index 0000000000..69da5dc6d4 --- /dev/null +++ b/servers/rendering/rasterizer_rd/rasterizer_effects_rd.h @@ -0,0 +1,457 @@ +/*************************************************************************/ +/* rasterizer_effects_rd.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef RASTERIZER_EFFECTS_RD_H +#define RASTERIZER_EFFECTS_RD_H + +#include "core/math/camera_matrix.h" +#include "servers/rendering/rasterizer_rd/render_pipeline_vertex_format_cache_rd.h" +#include "servers/rendering/rasterizer_rd/shaders/blur.glsl.gen.h" +#include "servers/rendering/rasterizer_rd/shaders/bokeh_dof.glsl.gen.h" +#include "servers/rendering/rasterizer_rd/shaders/copy.glsl.gen.h" +#include "servers/rendering/rasterizer_rd/shaders/cubemap_downsampler.glsl.gen.h" +#include "servers/rendering/rasterizer_rd/shaders/cubemap_filter.glsl.gen.h" +#include "servers/rendering/rasterizer_rd/shaders/cubemap_roughness.glsl.gen.h" +#include "servers/rendering/rasterizer_rd/shaders/luminance_reduce.glsl.gen.h" +#include "servers/rendering/rasterizer_rd/shaders/roughness_limiter.glsl.gen.h" +#include "servers/rendering/rasterizer_rd/shaders/ssao.glsl.gen.h" +#include "servers/rendering/rasterizer_rd/shaders/ssao_blur.glsl.gen.h" +#include "servers/rendering/rasterizer_rd/shaders/ssao_minify.glsl.gen.h" +#include "servers/rendering/rasterizer_rd/shaders/tonemap.glsl.gen.h" + +#include "servers/rendering_server.h" + +class RasterizerEffectsRD { + + enum BlurMode { + BLUR_MODE_GAUSSIAN_BLUR, + BLUR_MODE_GAUSSIAN_GLOW, + BLUR_MODE_GAUSSIAN_GLOW_AUTO_EXPOSURE, + BLUR_MODE_DOF_NEAR_LOW, + BLUR_MODE_DOF_NEAR_MEDIUM, + BLUR_MODE_DOF_NEAR_HIGH, + BLUR_MODE_DOF_NEAR_MERGE_LOW, + BLUR_MODE_DOF_NEAR_MERGE_MEDIUM, + BLUR_MODE_DOF_NEAR_MERGE_HIGH, + BLUR_MODE_DOF_FAR_LOW, + BLUR_MODE_DOF_FAR_MEDIUM, + BLUR_MODE_DOF_FAR_HIGH, + BLUR_MODE_SSAO_MERGE, + BLUR_MODE_SIMPLY_COPY, + BLUR_MODE_MIPMAP, + BLUR_MODE_MAX, + + }; + + enum { + BLUR_FLAG_HORIZONTAL = (1 << 0), + BLUR_FLAG_USE_BLUR_SECTION = (1 << 1), + BLUR_FLAG_USE_ORTHOGONAL_PROJECTION = (1 << 2), + BLUR_FLAG_DOF_NEAR_FIRST_TAP = (1 << 3), + BLUR_FLAG_GLOW_FIRST_PASS = (1 << 4), + BLUR_FLAG_FLIP_Y = (1 << 5), + BLUR_COPY_FORCE_LUMINANCE = (1 << 6) + }; + + struct BlurPushConstant { + float section[4]; + float pixel_size[2]; + uint32_t flags; + uint32_t pad; + //glow + float glow_strength; + float glow_bloom; + float glow_hdr_threshold; + float glow_hdr_scale; + float glow_exposure; + float glow_white; + float glow_luminance_cap; + float glow_auto_exposure_grey; + //dof + float dof_begin; + float dof_end; + float dof_radius; + float dof_pad; + + float dof_dir[2]; + float camera_z_far; + float camera_z_near; + + float ssao_color[4]; + }; + + struct Blur { + BlurPushConstant push_constant; + BlurShaderRD shader; + RID shader_version; + RenderPipelineVertexFormatCacheRD pipelines[BLUR_MODE_MAX]; + + } blur; + + struct CubemapRoughnessPushConstant { + uint32_t face_id; + uint32_t sample_count; + float roughness; + uint32_t use_direct_write; + float face_size; + float pad[3]; + }; + + struct CubemapRoughness { + + CubemapRoughnessPushConstant push_constant; + CubemapRoughnessShaderRD shader; + RID shader_version; + RID pipeline; + } roughness; + + enum TonemapMode { + TONEMAP_MODE_NORMAL, + TONEMAP_MODE_BICUBIC_GLOW_FILTER, + TONEMAP_MODE_MAX + }; + + struct TonemapPushConstant { + float bcs[3]; + uint32_t use_bcs; + + uint32_t use_glow; + uint32_t use_auto_exposure; + uint32_t use_color_correction; + uint32_t tonemapper; + + uint32_t glow_texture_size[2]; + + float glow_intensity; + uint32_t glow_level_flags; + uint32_t glow_mode; + + float exposure; + float white; + float auto_exposure_grey; + }; + + struct Tonemap { + + TonemapPushConstant push_constant; + TonemapShaderRD shader; + RID shader_version; + RenderPipelineVertexFormatCacheRD pipelines[TONEMAP_MODE_MAX]; + } tonemap; + + enum LuminanceReduceMode { + LUMINANCE_REDUCE_READ, + LUMINANCE_REDUCE, + LUMINANCE_REDUCE_WRITE, + LUMINANCE_REDUCE_MAX + }; + + struct LuminanceReducePushConstant { + int32_t source_size[2]; + float max_luminance; + float min_luminance; + float exposure_adjust; + float pad[3]; + }; + + struct LuminanceReduce { + + LuminanceReducePushConstant push_constant; + LuminanceReduceShaderRD shader; + RID shader_version; + RID pipelines[LUMINANCE_REDUCE_MAX]; + } luminance_reduce; + + struct CopyToDPPushConstant { + float bias; + float z_far; + float z_near; + uint32_t z_flip; + }; + + enum CopyMode { + COPY_MODE_CUBE_TO_DP, + COPY_MODE_MAX + }; + + struct Copy { + + CopyShaderRD shader; + RID shader_version; + RenderPipelineVertexFormatCacheRD pipelines[COPY_MODE_MAX]; + } copy; + + struct BokehPushConstant { + uint32_t size[2]; + float z_far; + float z_near; + + uint32_t orthogonal; + float blur_size; + float blur_scale; + uint32_t steps; + + uint32_t blur_near_active; + float blur_near_begin; + float blur_near_end; + uint32_t blur_far_active; + + float blur_far_begin; + float blur_far_end; + uint32_t second_pass; + uint32_t half_size; + + uint32_t use_jitter; + float jitter_seed; + uint32_t pad[2]; + }; + + enum BokehMode { + BOKEH_GEN_BLUR_SIZE, + BOKEH_GEN_BOKEH_BOX, + BOKEH_GEN_BOKEH_HEXAGONAL, + BOKEH_GEN_BOKEH_CIRCULAR, + BOKEH_COMPOSITE, + BOKEH_MAX + }; + + struct Bokeh { + + BokehPushConstant push_constant; + BokehDofShaderRD shader; + RID shader_version; + RID pipelines[BOKEH_MAX]; + } bokeh; + + enum SSAOMode { + SSAO_MINIFY_FIRST, + SSAO_MINIFY_MIPMAP, + SSAO_GATHER_LOW, + SSAO_GATHER_MEDIUM, + SSAO_GATHER_HIGH, + SSAO_GATHER_ULTRA, + SSAO_GATHER_LOW_HALF, + SSAO_GATHER_MEDIUM_HALF, + SSAO_GATHER_HIGH_HALF, + SSAO_GATHER_ULTRA_HALF, + SSAO_BLUR_PASS, + SSAO_BLUR_PASS_HALF, + SSAO_BLUR_UPSCALE, + SSAO_MAX + }; + + struct SSAOMinifyPushConstant { + float pixel_size[2]; + float z_far; + float z_near; + int32_t source_size[2]; + uint32_t orthogonal; + uint32_t pad; + }; + + struct SSAOGatherPushConstant { + int32_t screen_size[2]; + float z_far; + float z_near; + + uint32_t orthogonal; + float intensity_div_r6; + float radius; + float bias; + + float proj_info[4]; + float pixel_size[2]; + float proj_scale; + uint32_t pad; + }; + + struct SSAOBlurPushConstant { + float edge_sharpness; + int32_t filter_scale; + float z_far; + float z_near; + uint32_t orthogonal; + uint32_t pad[3]; + int32_t axis[2]; + int32_t screen_size[2]; + }; + + struct SSAO { + + SSAOMinifyPushConstant minify_push_constant; + SsaoMinifyShaderRD minify_shader; + RID minify_shader_version; + + SSAOGatherPushConstant gather_push_constant; + SsaoShaderRD gather_shader; + RID gather_shader_version; + + SSAOBlurPushConstant blur_push_constant; + SsaoBlurShaderRD blur_shader; + RID blur_shader_version; + + RID pipelines[SSAO_MAX]; + } ssao; + + struct RoughnessLimiterPushConstant { + int32_t screen_size[2]; + float curve; + uint32_t pad; + }; + + struct RoughnessLimiter { + + RoughnessLimiterPushConstant push_constant; + RoughnessLimiterShaderRD shader; + RID shader_version; + RID pipeline; + + } roughness_limiter; + + struct CubemapDownsamplerPushConstant { + uint32_t face_size; + float pad[3]; + }; + + struct CubemapDownsampler { + + CubemapDownsamplerPushConstant push_constant; + CubemapDownsamplerShaderRD shader; + RID shader_version; + RID pipeline; + + } cubemap_downsampler; + + enum CubemapFilterMode { + FILTER_MODE_HIGH_QUALITY, + FILTER_MODE_LOW_QUALITY, + FILTER_MODE_HIGH_QUALITY_ARRAY, + FILTER_MODE_LOW_QUALITY_ARRAY, + FILTER_MODE_MAX, + }; + + struct CubemapFilter { + + CubemapFilterShaderRD shader; + RID shader_version; + RID pipelines[FILTER_MODE_MAX]; + RID uniform_set; + RID image_uniform_set; + RID coefficient_buffer; + bool use_high_quality; + + } filter; + + struct SkyPushConstant { + float orientation[12]; + float proj[4]; + float position[3]; + float multiplier; + float time; + float pad[3]; + }; + + RID default_sampler; + RID default_mipmap_sampler; + RID index_buffer; + RID index_array; + + Map<RID, RID> texture_to_uniform_set_cache; + + Map<RID, RID> image_to_uniform_set_cache; + Map<RID, RID> texture_to_compute_uniform_set_cache; + + RID _get_uniform_set_from_image(RID p_texture); + RID _get_uniform_set_from_texture(RID p_texture, bool p_use_mipmaps = false); + RID _get_compute_uniform_set_from_texture(RID p_texture, bool p_use_mipmaps = false); + +public: + //TODO must re-do most of the shaders in compute + + void region_copy(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2 &p_region); + void copy_to_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2 &p_rect, bool p_flip_y = false, bool p_force_luminance = false); + void gaussian_blur(RID p_source_rd_texture, RID p_framebuffer_half, RID p_rd_texture_half, RID p_dest_framebuffer, const Vector2 &p_pixel_size, const Rect2 &p_region); + void gaussian_glow(RID p_source_rd_texture, RID p_framebuffer_half, RID p_rd_texture_half, RID p_dest_framebuffer, const Vector2 &p_pixel_size, float p_strength = 1.0, bool p_first_pass = false, float p_luminance_cap = 16.0, float p_exposure = 1.0, float p_bloom = 0.0, float p_hdr_bleed_treshold = 1.0, float p_hdr_bleed_scale = 1.0, RID p_auto_exposure = RID(), float p_auto_exposure_grey = 1.0); + + void cubemap_roughness(RID p_source_rd_texture, RID p_dest_framebuffer, uint32_t p_face_id, uint32_t p_sample_count, float p_roughness, float p_size); + void make_mipmap(RID p_source_rd_texture, RID p_framebuffer_half, const Vector2 &p_pixel_size); + void copy_cubemap_to_dp(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2 &p_rect, float p_z_near, float p_z_far, float p_bias, bool p_dp_flip); + void luminance_reduction(RID p_source_texture, const Size2i p_source_size, const Vector<RID> p_reduce, RID p_prev_luminance, float p_min_luminance, float p_max_luminance, float p_adjust, bool p_set = false); + void bokeh_dof(RID p_base_texture, RID p_depth_texture, const Size2i &p_base_texture_size, RID p_secondary_texture, RID p_bokeh_texture1, RID p_bokeh_texture2, bool p_dof_far, float p_dof_far_begin, float p_dof_far_size, bool p_dof_near, float p_dof_near_begin, float p_dof_near_size, float p_bokeh_size, RS::DOFBokehShape p_bokeh_shape, RS::DOFBlurQuality p_quality, bool p_use_jitter, float p_cam_znear, float p_cam_zfar, bool p_cam_orthogonal); + + struct TonemapSettings { + + bool use_glow = false; + enum GlowMode { + GLOW_MODE_ADD, + GLOW_MODE_SCREEN, + GLOW_MODE_SOFTLIGHT, + GLOW_MODE_REPLACE, + GLOW_MODE_MIX + }; + + GlowMode glow_mode = GLOW_MODE_ADD; + float glow_intensity = 1.0; + uint32_t glow_level_flags = 0; + Vector2i glow_texture_size; + bool glow_use_bicubic_upscale = false; + RID glow_texture; + + RS::EnvironmentToneMapper tonemap_mode = RS::ENV_TONE_MAPPER_LINEAR; + float exposure = 1.0; + float white = 1.0; + + bool use_auto_exposure = false; + float auto_exposure_grey = 0.5; + RID exposure_texture; + + bool use_bcs = false; + float brightness = 1.0; + float contrast = 1.0; + float saturation = 1.0; + + bool use_color_correction = false; + RID color_correction_texture; + }; + + void tonemapper(RID p_source_color, RID p_dst_framebuffer, const TonemapSettings &p_settings); + + void generate_ssao(RID p_depth_buffer, RID p_normal_buffer, const Size2i &p_depth_buffer_size, RID p_depth_mipmaps_texture, const Vector<RID> &depth_mipmaps, RID p_ao1, bool p_half_size, RID p_ao2, RID p_upscale_buffer, float p_intensity, float p_radius, float p_bias, const CameraMatrix &p_projection, RS::EnvironmentSSAOQuality p_quality, RS::EnvironmentSSAOBlur p_blur, float p_edge_sharpness); + + void roughness_limit(RID p_source_normal, RID p_roughness, const Size2i &p_size, float p_curve); + void cubemap_downsample(RID p_source_cubemap, RID p_dest_cubemap, const Size2i &p_size); + void cubemap_filter(RID p_source_cubemap, Vector<RID> p_dest_cubemap, bool p_use_array); + void render_sky(RD::DrawListID p_list, float p_time, RID p_fb, RID p_samplers, RID p_lights, RenderPipelineVertexFormatCacheRD *p_pipeline, RID p_uniform_set, RID p_texture_set, const CameraMatrix &p_camera, const Basis &p_orientation, float p_multiplier, const Vector3 &p_position); + + RasterizerEffectsRD(); + ~RasterizerEffectsRD(); +}; + +#endif // !RASTERIZER_EFFECTS_RD_H diff --git a/servers/rendering/rasterizer_rd/rasterizer_rd.cpp b/servers/rendering/rasterizer_rd/rasterizer_rd.cpp new file mode 100644 index 0000000000..9c54f0caae --- /dev/null +++ b/servers/rendering/rasterizer_rd/rasterizer_rd.cpp @@ -0,0 +1,177 @@ +/*************************************************************************/ +/* rasterizer_rd.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "rasterizer_rd.h" + +void RasterizerRD::prepare_for_blitting_render_targets() { + RD::get_singleton()->prepare_screen_for_drawing(); +} + +void RasterizerRD::blit_render_targets_to_screen(DisplayServer::WindowID p_screen, const BlitToScreen *p_render_targets, int p_amount) { + + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin_for_screen(p_screen); + + for (int i = 0; i < p_amount; i++) { + RID texture = storage->render_target_get_texture(p_render_targets[i].render_target); + ERR_CONTINUE(texture.is_null()); + RID rd_texture = storage->texture_get_rd_texture(texture); + ERR_CONTINUE(rd_texture.is_null()); + if (!render_target_descriptors.has(rd_texture) || !RD::get_singleton()->uniform_set_is_valid(render_target_descriptors[rd_texture])) { + Vector<RD::Uniform> uniforms; + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE; + u.binding = 0; + u.ids.push_back(copy_viewports_sampler); + u.ids.push_back(rd_texture); + uniforms.push_back(u); + RID uniform_set = RD::get_singleton()->uniform_set_create(uniforms, copy_viewports_rd_shader, 0); + + render_target_descriptors[rd_texture] = uniform_set; + } + + Size2 screen_size(RD::get_singleton()->screen_get_width(p_screen), RD::get_singleton()->screen_get_height(p_screen)); + + RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, copy_viewports_rd_pipeline); + RD::get_singleton()->draw_list_bind_index_array(draw_list, copy_viewports_rd_array); + RD::get_singleton()->draw_list_bind_uniform_set(draw_list, render_target_descriptors[rd_texture], 0); + + float push_constant[4] = { + p_render_targets[i].rect.position.x / screen_size.width, + p_render_targets[i].rect.position.y / screen_size.height, + p_render_targets[i].rect.size.width / screen_size.width, + p_render_targets[i].rect.size.height / screen_size.height, + }; + RD::get_singleton()->draw_list_set_push_constant(draw_list, push_constant, 4 * sizeof(float)); + RD::get_singleton()->draw_list_draw(draw_list, true); + } + + RD::get_singleton()->draw_list_end(); +} + +void RasterizerRD::begin_frame(double frame_step) { + frame++; + time += frame_step; + canvas->set_time(time); + scene->set_time(time, frame_step); +} + +void RasterizerRD::end_frame(bool p_swap_buffers) { + +#ifndef _MSC_VER +#warning TODO: likely passa bool to swap buffers to avoid display? +#endif + RD::get_singleton()->swap_buffers(); //probably should pass some bool to avoid display? +} + +void RasterizerRD::initialize() { + + { //create framebuffer copy shader + RenderingDevice::ShaderStageData vert; + vert.shader_stage = RenderingDevice::SHADER_STAGE_VERTEX; + vert.spir_v = RenderingDevice::get_singleton()->shader_compile_from_source(RenderingDevice::SHADER_STAGE_VERTEX, + "#version 450\n" + "layout(push_constant, binding = 0, std140) uniform Pos { vec4 dst_rect; } pos;\n" + "layout(location =0) out vec2 uv;\n" + "void main() { \n" + " vec2 base_arr[4] = vec2[](vec2(0.0,0.0),vec2(0.0,1.0),vec2(1.0,1.0),vec2(1.0,0.0));\n" + " uv = base_arr[gl_VertexIndex];\n" + " vec2 vtx = pos.dst_rect.xy+uv*pos.dst_rect.zw;\n" + " gl_Position = vec4(vtx * 2.0 - 1.0,0.0,1.0);\n" + "}\n"); + + RenderingDevice::ShaderStageData frag; + frag.shader_stage = RenderingDevice::SHADER_STAGE_FRAGMENT; + frag.spir_v = RenderingDevice::get_singleton()->shader_compile_from_source(RenderingDevice::SHADER_STAGE_FRAGMENT, + "#version 450\n" + "layout (location = 0) in vec2 uv;\n" + "layout (location = 0) out vec4 color;\n" + "layout (binding = 0) uniform sampler2D src_rt;\n" + "void main() { color=texture(src_rt,uv); }\n"); + + Vector<RenderingDevice::ShaderStageData> source; + source.push_back(vert); + source.push_back(frag); + String error; + copy_viewports_rd_shader = RD::get_singleton()->shader_create(source); + if (!copy_viewports_rd_shader.is_valid()) { + print_line("Failed compilation: " + error); + } + } + + { //create index array for copy shader + Vector<uint8_t> pv; + pv.resize(6 * 4); + { + uint8_t *w = pv.ptrw(); + int *p32 = (int *)w; + p32[0] = 0; + p32[1] = 1; + p32[2] = 2; + p32[3] = 0; + p32[4] = 2; + p32[5] = 3; + } + copy_viewports_rd_index_buffer = RD::get_singleton()->index_buffer_create(6, RenderingDevice::INDEX_BUFFER_FORMAT_UINT32, pv); + copy_viewports_rd_array = RD::get_singleton()->index_array_create(copy_viewports_rd_index_buffer, 0, 6); + } + + { //pipeline + copy_viewports_rd_pipeline = RD::get_singleton()->render_pipeline_create(copy_viewports_rd_shader, RD::get_singleton()->screen_get_framebuffer_format(), RD::INVALID_ID, RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RenderingDevice::PipelineColorBlendState::create_disabled(), 0); + } + { // sampler + copy_viewports_sampler = RD::get_singleton()->sampler_create(RD::SamplerState()); + } +} + +ThreadWorkPool RasterizerRD::thread_work_pool; +uint32_t RasterizerRD::frame = 1; + +void RasterizerRD::finalize() { + + thread_work_pool.finish(); + + memdelete(scene); + memdelete(canvas); + memdelete(storage); + + //only need to erase these, the rest are erased by cascade + RD::get_singleton()->free(copy_viewports_rd_index_buffer); + RD::get_singleton()->free(copy_viewports_rd_shader); + RD::get_singleton()->free(copy_viewports_sampler); +} + +RasterizerRD::RasterizerRD() { + thread_work_pool.init(); + time = 0; + + storage = memnew(RasterizerStorageRD); + canvas = memnew(RasterizerCanvasRD(storage)); + scene = memnew(RasterizerSceneHighEndRD(storage)); +} diff --git a/servers/rendering/rasterizer_rd/rasterizer_rd.h b/servers/rendering/rasterizer_rd/rasterizer_rd.h new file mode 100644 index 0000000000..756b9499ca --- /dev/null +++ b/servers/rendering/rasterizer_rd/rasterizer_rd.h @@ -0,0 +1,95 @@ +/*************************************************************************/ +/* rasterizer_rd.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef RASTERIZER_RD_H +#define RASTERIZER_RD_H + +#include "core/os/os.h" +#include "core/thread_work_pool.h" +#include "servers/rendering/rasterizer.h" +#include "servers/rendering/rasterizer_rd/rasterizer_canvas_rd.h" +#include "servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.h" +#include "servers/rendering/rasterizer_rd/rasterizer_storage_rd.h" + +class RasterizerRD : public Rasterizer { +protected: + RasterizerCanvasRD *canvas; + RasterizerStorageRD *storage; + RasterizerSceneHighEndRD *scene; + + RID copy_viewports_rd_shader; + RID copy_viewports_rd_pipeline; + RID copy_viewports_rd_index_buffer; + RID copy_viewports_rd_array; + RID copy_viewports_sampler; + + Map<RID, RID> render_target_descriptors; + + double time; + + static uint32_t frame; + +public: + RasterizerStorage *get_storage() { return storage; } + RasterizerCanvas *get_canvas() { return canvas; } + RasterizerScene *get_scene() { return scene; } + + void set_boot_image(const Ref<Image> &p_image, const Color &p_color, bool p_scale, bool p_use_filter) {} + + void initialize(); + void begin_frame(double frame_step); + void prepare_for_blitting_render_targets(); + void blit_render_targets_to_screen(DisplayServer::WindowID p_screen, const BlitToScreen *p_render_targets, int p_amount); + + void end_frame(bool p_swap_buffers); + void finalize(); + + static _ALWAYS_INLINE_ uint64_t get_frame_number() { return frame; } + + static Error is_viable() { + return OK; + } + + static Rasterizer *_create_current() { + return memnew(RasterizerRD); + } + + static void make_current() { + _create_func = _create_current; + } + + virtual bool is_low_end() const { return false; } + + static ThreadWorkPool thread_work_pool; + + RasterizerRD(); + ~RasterizerRD() {} +}; +#endif // RASTERIZER_RD_H diff --git a/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.cpp b/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.cpp new file mode 100644 index 0000000000..bf3cd045f1 --- /dev/null +++ b/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.cpp @@ -0,0 +1,2729 @@ +/*************************************************************************/ +/* rasterizer_scene_high_end_rd.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "rasterizer_scene_high_end_rd.h" +#include "core/project_settings.h" +#include "servers/rendering/rendering_device.h" +#include "servers/rendering/rendering_server_raster.h" + +static _FORCE_INLINE_ void store_transform(const Transform &p_mtx, float *p_array) { + p_array[0] = p_mtx.basis.elements[0][0]; + p_array[1] = p_mtx.basis.elements[1][0]; + p_array[2] = p_mtx.basis.elements[2][0]; + p_array[3] = 0; + p_array[4] = p_mtx.basis.elements[0][1]; + p_array[5] = p_mtx.basis.elements[1][1]; + p_array[6] = p_mtx.basis.elements[2][1]; + p_array[7] = 0; + p_array[8] = p_mtx.basis.elements[0][2]; + p_array[9] = p_mtx.basis.elements[1][2]; + p_array[10] = p_mtx.basis.elements[2][2]; + p_array[11] = 0; + p_array[12] = p_mtx.origin.x; + p_array[13] = p_mtx.origin.y; + p_array[14] = p_mtx.origin.z; + p_array[15] = 1; +} + +static _FORCE_INLINE_ void store_transform_3x3(const Transform &p_mtx, float *p_array) { + p_array[0] = p_mtx.basis.elements[0][0]; + p_array[1] = p_mtx.basis.elements[1][0]; + p_array[2] = p_mtx.basis.elements[2][0]; + p_array[3] = 0; + p_array[4] = p_mtx.basis.elements[0][1]; + p_array[5] = p_mtx.basis.elements[1][1]; + p_array[6] = p_mtx.basis.elements[2][1]; + p_array[7] = 0; + p_array[8] = p_mtx.basis.elements[0][2]; + p_array[9] = p_mtx.basis.elements[1][2]; + p_array[10] = p_mtx.basis.elements[2][2]; + p_array[11] = 0; +} + +static _FORCE_INLINE_ void store_camera(const CameraMatrix &p_mtx, float *p_array) { + + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + + p_array[i * 4 + j] = p_mtx.matrix[i][j]; + } + } +} + +/* SCENE SHADER */ +void RasterizerSceneHighEndRD::ShaderData::set_code(const String &p_code) { + //compile + + code = p_code; + valid = false; + ubo_size = 0; + uniforms.clear(); + uses_screen_texture = false; + + if (code == String()) { + return; //just invalid, but no error + } + + ShaderCompilerRD::GeneratedCode gen_code; + + int blend_mode = BLEND_MODE_MIX; + int depth_testi = DEPTH_TEST_ENABLED; + int cull = CULL_BACK; + + uses_point_size = false; + uses_alpha = false; + uses_blend_alpha = false; + uses_depth_pre_pass = false; + uses_discard = false; + uses_roughness = false; + uses_normal = false; + bool wireframe = false; + + unshaded = false; + uses_vertex = false; + uses_sss = false; + uses_screen_texture = false; + uses_depth_texture = false; + uses_normal_texture = false; + uses_time = false; + writes_modelview_or_projection = false; + uses_world_coordinates = false; + + int depth_drawi = DEPTH_DRAW_OPAQUE; + + ShaderCompilerRD::IdentifierActions actions; + + actions.render_mode_values["blend_add"] = Pair<int *, int>(&blend_mode, BLEND_MODE_ADD); + actions.render_mode_values["blend_mix"] = Pair<int *, int>(&blend_mode, BLEND_MODE_MIX); + actions.render_mode_values["blend_sub"] = Pair<int *, int>(&blend_mode, BLEND_MODE_SUB); + actions.render_mode_values["blend_mul"] = Pair<int *, int>(&blend_mode, BLEND_MODE_MUL); + + actions.render_mode_values["depth_draw_never"] = Pair<int *, int>(&depth_drawi, DEPTH_DRAW_DISABLED); + actions.render_mode_values["depth_draw_opaque"] = Pair<int *, int>(&depth_drawi, DEPTH_DRAW_OPAQUE); + actions.render_mode_values["depth_draw_always"] = Pair<int *, int>(&depth_drawi, DEPTH_DRAW_ALWAYS); + + actions.render_mode_values["depth_test_disabled"] = Pair<int *, int>(&depth_testi, DEPTH_TEST_DISABLED); + + actions.render_mode_values["cull_disabled"] = Pair<int *, int>(&cull, CULL_DISABLED); + actions.render_mode_values["cull_front"] = Pair<int *, int>(&cull, CULL_FRONT); + actions.render_mode_values["cull_back"] = Pair<int *, int>(&cull, CULL_BACK); + + actions.render_mode_flags["unshaded"] = &unshaded; + actions.render_mode_flags["wireframe"] = &wireframe; + + actions.usage_flag_pointers["ALPHA"] = &uses_alpha; + actions.render_mode_flags["depth_prepass_alpha"] = &uses_depth_pre_pass; + + actions.usage_flag_pointers["SSS_STRENGTH"] = &uses_sss; + + actions.usage_flag_pointers["SCREEN_TEXTURE"] = &uses_screen_texture; + actions.usage_flag_pointers["DEPTH_TEXTURE"] = &uses_depth_texture; + actions.usage_flag_pointers["NORMAL_TEXTURE"] = &uses_normal_texture; + actions.usage_flag_pointers["DISCARD"] = &uses_discard; + actions.usage_flag_pointers["TIME"] = &uses_time; + actions.usage_flag_pointers["ROUGHNESS"] = &uses_roughness; + actions.usage_flag_pointers["NORMAL"] = &uses_normal; + actions.usage_flag_pointers["NORMALMAP"] = &uses_normal; + + actions.usage_flag_pointers["POINT_SIZE"] = &uses_point_size; + actions.usage_flag_pointers["POINT_COORD"] = &uses_point_size; + + actions.write_flag_pointers["MODELVIEW_MATRIX"] = &writes_modelview_or_projection; + actions.write_flag_pointers["PROJECTION_MATRIX"] = &writes_modelview_or_projection; + actions.write_flag_pointers["VERTEX"] = &uses_vertex; + + actions.uniforms = &uniforms; + + RasterizerSceneHighEndRD *scene_singleton = (RasterizerSceneHighEndRD *)RasterizerSceneHighEndRD::singleton; + + Error err = scene_singleton->shader.compiler.compile(RS::SHADER_SPATIAL, code, &actions, path, gen_code); + + ERR_FAIL_COND(err != OK); + + if (version.is_null()) { + version = scene_singleton->shader.scene_shader.version_create(); + } + + depth_draw = DepthDraw(depth_drawi); + depth_test = DepthTest(depth_testi); + +#if 0 + print_line("**compiling shader:"); + print_line("**defines:\n"); + for (int i = 0; i < gen_code.defines.size(); i++) { + print_line(gen_code.defines[i]); + } + print_line("\n**uniforms:\n" + gen_code.uniforms); + print_line("\n**vertex_globals:\n" + gen_code.vertex_global); + print_line("\n**vertex_code:\n" + gen_code.vertex); + print_line("\n**fragment_globals:\n" + gen_code.fragment_global); + print_line("\n**fragment_code:\n" + gen_code.fragment); + print_line("\n**light_code:\n" + gen_code.light); +#endif + scene_singleton->shader.scene_shader.version_set_code(version, gen_code.uniforms, gen_code.vertex_global, gen_code.vertex, gen_code.fragment_global, gen_code.light, gen_code.fragment, gen_code.defines); + ERR_FAIL_COND(!scene_singleton->shader.scene_shader.version_is_valid(version)); + + ubo_size = gen_code.uniform_total_size; + ubo_offsets = gen_code.uniform_offsets; + texture_uniforms = gen_code.texture_uniforms; + + //blend modes + + RD::PipelineColorBlendState::Attachment blend_attachment; + + switch (blend_mode) { + case BLEND_MODE_MIX: { + + blend_attachment.enable_blend = true; + blend_attachment.alpha_blend_op = RD::BLEND_OP_ADD; + blend_attachment.color_blend_op = RD::BLEND_OP_ADD; + blend_attachment.src_color_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA; + blend_attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + blend_attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_ONE; + blend_attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + + } break; + case BLEND_MODE_ADD: { + + blend_attachment.enable_blend = true; + blend_attachment.alpha_blend_op = RD::BLEND_OP_ADD; + blend_attachment.color_blend_op = RD::BLEND_OP_ADD; + blend_attachment.src_color_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA; + blend_attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ONE; + blend_attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA; + blend_attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ONE; + uses_blend_alpha = true; //force alpha used because of blend + + } break; + case BLEND_MODE_SUB: { + + blend_attachment.enable_blend = true; + blend_attachment.alpha_blend_op = RD::BLEND_OP_SUBTRACT; + blend_attachment.color_blend_op = RD::BLEND_OP_SUBTRACT; + blend_attachment.src_color_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA; + blend_attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ONE; + blend_attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA; + blend_attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ONE; + uses_blend_alpha = true; //force alpha used because of blend + + } break; + case BLEND_MODE_MUL: { + blend_attachment.enable_blend = true; + blend_attachment.alpha_blend_op = RD::BLEND_OP_ADD; + blend_attachment.color_blend_op = RD::BLEND_OP_ADD; + blend_attachment.src_color_blend_factor = RD::BLEND_FACTOR_DST_COLOR; + blend_attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ZERO; + blend_attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_DST_ALPHA; + blend_attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ZERO; + uses_blend_alpha = true; //force alpha used because of blend + } break; + } + + RD::PipelineColorBlendState blend_state_blend; + blend_state_blend.attachments.push_back(blend_attachment); + RD::PipelineColorBlendState blend_state_opaque = RD::PipelineColorBlendState::create_disabled(1); + RD::PipelineColorBlendState blend_state_opaque_specular = RD::PipelineColorBlendState::create_disabled(2); + RD::PipelineColorBlendState blend_state_depth_normal = RD::PipelineColorBlendState::create_disabled(1); + RD::PipelineColorBlendState blend_state_depth_normal_roughness = RD::PipelineColorBlendState::create_disabled(2); + + //update pipelines + + RD::PipelineDepthStencilState depth_stencil_state; + + if (depth_test != DEPTH_TEST_DISABLED) { + depth_stencil_state.enable_depth_test = true; + depth_stencil_state.depth_compare_operator = RD::COMPARE_OP_LESS_OR_EQUAL; + depth_stencil_state.enable_depth_write = depth_draw != DEPTH_DRAW_DISABLED ? true : false; + } + + for (int i = 0; i < CULL_VARIANT_MAX; i++) { + + RD::PolygonCullMode cull_mode_rd_table[CULL_VARIANT_MAX][3] = { + { RD::POLYGON_CULL_DISABLED, RD::POLYGON_CULL_FRONT, RD::POLYGON_CULL_BACK }, + { RD::POLYGON_CULL_DISABLED, RD::POLYGON_CULL_BACK, RD::POLYGON_CULL_FRONT }, + { RD::POLYGON_CULL_DISABLED, RD::POLYGON_CULL_DISABLED, RD::POLYGON_CULL_DISABLED } + }; + + RD::PolygonCullMode cull_mode_rd = cull_mode_rd_table[i][cull]; + + for (int j = 0; j < RS::PRIMITIVE_MAX; j++) { + + RD::RenderPrimitive primitive_rd_table[RS::PRIMITIVE_MAX] = { + RD::RENDER_PRIMITIVE_POINTS, + RD::RENDER_PRIMITIVE_LINES, + RD::RENDER_PRIMITIVE_LINESTRIPS, + RD::RENDER_PRIMITIVE_TRIANGLES, + RD::RENDER_PRIMITIVE_TRIANGLE_STRIPS, + }; + + RD::RenderPrimitive primitive_rd = uses_point_size ? RD::RENDER_PRIMITIVE_POINTS : primitive_rd_table[j]; + + for (int k = 0; k < SHADER_VERSION_MAX; k++) { + + RD::PipelineRasterizationState raster_state; + raster_state.cull_mode = cull_mode_rd; + raster_state.wireframe = wireframe; + + RD::PipelineColorBlendState blend_state; + RD::PipelineDepthStencilState depth_stencil = depth_stencil_state; + + if (uses_alpha || uses_blend_alpha) { + if (k == SHADER_VERSION_COLOR_PASS || k == SHADER_VERSION_VCT_COLOR_PASS || k == SHADER_VERSION_LIGHTMAP_COLOR_PASS) { + blend_state = blend_state_blend; + if (depth_draw == DEPTH_DRAW_OPAQUE) { + depth_stencil.enable_depth_write = false; //alpha does not draw depth + } + } else if (uses_depth_pre_pass && (k == SHADER_VERSION_DEPTH_PASS || k == SHADER_VERSION_DEPTH_PASS_DP || k == SHADER_VERSION_DEPTH_PASS_WITH_NORMAL || k == SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS || k == SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL)) { + if (k == SHADER_VERSION_DEPTH_PASS || k == SHADER_VERSION_DEPTH_PASS_DP) { + //none, blend state contains nothing + } else if (k == SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL) { + blend_state = RD::PipelineColorBlendState::create_disabled(5); //writes to normal and roughness in opaque way + } else { + blend_state = blend_state_opaque; //writes to normal and roughness in opaque way + } + } else { + pipelines[i][j][k].clear(); + continue; // do not use this version (will error if using it is attempted) + } + } else { + + if (k == SHADER_VERSION_COLOR_PASS || k == SHADER_VERSION_VCT_COLOR_PASS || k == SHADER_VERSION_LIGHTMAP_COLOR_PASS) { + blend_state = blend_state_opaque; + } else if (k == SHADER_VERSION_DEPTH_PASS || k == SHADER_VERSION_DEPTH_PASS_DP) { + //none, leave empty + } else if (k == SHADER_VERSION_DEPTH_PASS_WITH_NORMAL) { + blend_state = blend_state_depth_normal; + } else if (k == SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS) { + blend_state = blend_state_depth_normal; + } else if (k == SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL) { + blend_state = RD::PipelineColorBlendState::create_disabled(5); //writes to normal and roughness in opaque way + + } else { + //specular write + blend_state = blend_state_opaque_specular; + } + } + + RID shader_variant = scene_singleton->shader.scene_shader.version_get_shader(version, k); + pipelines[i][j][k].setup(shader_variant, primitive_rd, raster_state, RD::PipelineMultisampleState(), depth_stencil, blend_state, 0); + } + } + } + + valid = true; +} + +void RasterizerSceneHighEndRD::ShaderData::set_default_texture_param(const StringName &p_name, RID p_texture) { + if (!p_texture.is_valid()) { + default_texture_params.erase(p_name); + } else { + default_texture_params[p_name] = p_texture; + } +} + +void RasterizerSceneHighEndRD::ShaderData::get_param_list(List<PropertyInfo> *p_param_list) const { + + Map<int, StringName> order; + + for (Map<StringName, ShaderLanguage::ShaderNode::Uniform>::Element *E = uniforms.front(); E; E = E->next()) { + + if (E->get().texture_order >= 0) { + order[E->get().texture_order + 100000] = E->key(); + } else { + order[E->get().order] = E->key(); + } + } + + for (Map<int, StringName>::Element *E = order.front(); E; E = E->next()) { + + PropertyInfo pi = ShaderLanguage::uniform_to_property_info(uniforms[E->get()]); + pi.name = E->get(); + p_param_list->push_back(pi); + } +} + +bool RasterizerSceneHighEndRD::ShaderData::is_param_texture(const StringName &p_param) const { + if (!uniforms.has(p_param)) { + return false; + } + + return uniforms[p_param].texture_order >= 0; +} + +bool RasterizerSceneHighEndRD::ShaderData::is_animated() const { + return false; +} + +bool RasterizerSceneHighEndRD::ShaderData::casts_shadows() const { + return false; +} +Variant RasterizerSceneHighEndRD::ShaderData::get_default_parameter(const StringName &p_parameter) const { + if (uniforms.has(p_parameter)) { + ShaderLanguage::ShaderNode::Uniform uniform = uniforms[p_parameter]; + Vector<ShaderLanguage::ConstantNode::Value> default_value = uniform.default_value; + return ShaderLanguage::constant_value_to_variant(default_value, uniform.type, uniform.hint); + } + return Variant(); +} + +RasterizerSceneHighEndRD::ShaderData::ShaderData() { + valid = false; + uses_screen_texture = false; +} + +RasterizerSceneHighEndRD::ShaderData::~ShaderData() { + RasterizerSceneHighEndRD *scene_singleton = (RasterizerSceneHighEndRD *)RasterizerSceneHighEndRD::singleton; + ERR_FAIL_COND(!scene_singleton); + //pipeline variants will clear themselves if shader is gone + if (version.is_valid()) { + scene_singleton->shader.scene_shader.version_free(version); + } +} + +RasterizerStorageRD::ShaderData *RasterizerSceneHighEndRD::_create_shader_func() { + ShaderData *shader_data = memnew(ShaderData); + return shader_data; +} + +void RasterizerSceneHighEndRD::MaterialData::set_render_priority(int p_priority) { + priority = p_priority - RS::MATERIAL_RENDER_PRIORITY_MIN; //8 bits +} + +void RasterizerSceneHighEndRD::MaterialData::set_next_pass(RID p_pass) { + next_pass = p_pass; +} + +void RasterizerSceneHighEndRD::MaterialData::update_parameters(const Map<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty) { + + RasterizerSceneHighEndRD *scene_singleton = (RasterizerSceneHighEndRD *)RasterizerSceneHighEndRD::singleton; + + if ((uint32_t)ubo_data.size() != shader_data->ubo_size) { + p_uniform_dirty = true; + if (uniform_buffer.is_valid()) { + RD::get_singleton()->free(uniform_buffer); + uniform_buffer = RID(); + } + + ubo_data.resize(shader_data->ubo_size); + if (ubo_data.size()) { + uniform_buffer = RD::get_singleton()->uniform_buffer_create(ubo_data.size()); + memset(ubo_data.ptrw(), 0, ubo_data.size()); //clear + } + + //clear previous uniform set + if (uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(uniform_set)) { + RD::get_singleton()->free(uniform_set); + uniform_set = RID(); + } + } + + //check whether buffer changed + if (p_uniform_dirty && ubo_data.size()) { + + update_uniform_buffer(shader_data->uniforms, shader_data->ubo_offsets.ptr(), p_parameters, ubo_data.ptrw(), ubo_data.size(), false); + RD::get_singleton()->buffer_update(uniform_buffer, 0, ubo_data.size(), ubo_data.ptrw()); + } + + uint32_t tex_uniform_count = shader_data->texture_uniforms.size(); + + if ((uint32_t)texture_cache.size() != tex_uniform_count) { + texture_cache.resize(tex_uniform_count); + p_textures_dirty = true; + + //clear previous uniform set + if (uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(uniform_set)) { + RD::get_singleton()->free(uniform_set); + uniform_set = RID(); + } + } + + if (p_textures_dirty && tex_uniform_count) { + + update_textures(p_parameters, shader_data->default_texture_params, shader_data->texture_uniforms, texture_cache.ptrw(), true); + } + + if (shader_data->ubo_size == 0 && shader_data->texture_uniforms.size() == 0) { + // This material does not require an uniform set, so don't create it. + return; + } + + if (!p_textures_dirty && uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(uniform_set)) { + //no reason to update uniform set, only UBO (or nothing) was needed to update + return; + } + + Vector<RD::Uniform> uniforms; + + { + + if (shader_data->ubo_size) { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; + u.binding = 0; + u.ids.push_back(uniform_buffer); + uniforms.push_back(u); + } + + const RID *textures = texture_cache.ptrw(); + for (uint32_t i = 0; i < tex_uniform_count; i++) { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_TEXTURE; + u.binding = 1 + i; + u.ids.push_back(textures[i]); + uniforms.push_back(u); + } + } + + uniform_set = RD::get_singleton()->uniform_set_create(uniforms, scene_singleton->shader.scene_shader.version_get_shader(shader_data->version, 0), MATERIAL_UNIFORM_SET); +} + +RasterizerSceneHighEndRD::MaterialData::~MaterialData() { + if (uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(uniform_set)) { + RD::get_singleton()->free(uniform_set); + } + + if (uniform_buffer.is_valid()) { + RD::get_singleton()->free(uniform_buffer); + } +} + +RasterizerStorageRD::MaterialData *RasterizerSceneHighEndRD::_create_material_func(ShaderData *p_shader) { + MaterialData *material_data = memnew(MaterialData); + material_data->shader_data = p_shader; + material_data->last_frame = false; + //update will happen later anyway so do nothing. + return material_data; +} + +RasterizerSceneHighEndRD::RenderBufferDataHighEnd::~RenderBufferDataHighEnd() { + clear(); +} + +void RasterizerSceneHighEndRD::RenderBufferDataHighEnd::ensure_specular() { + + if (!specular.is_valid()) { + RD::TextureFormat tf; + tf.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT; + tf.width = width; + tf.height = height; + tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; + + specular = RD::get_singleton()->texture_create(tf, RD::TextureView()); + + Vector<RID> fb; + fb.push_back(color); + fb.push_back(specular); + fb.push_back(depth); + + color_specular_fb = RD::get_singleton()->framebuffer_create(fb); + } +} + +void RasterizerSceneHighEndRD::RenderBufferDataHighEnd::clear() { + + if (specular.is_valid()) { + RD::get_singleton()->free(specular); + specular = RID(); + } + + color_specular_fb = RID(); + color_fb = RID(); + + if (normal_buffer.is_valid()) { + RD::get_singleton()->free(normal_buffer); + normal_buffer = RID(); + depth_normal_fb = RID(); + } + + if (roughness_buffer.is_valid()) { + RD::get_singleton()->free(roughness_buffer); + roughness_buffer = RID(); + depth_normal_roughness_fb = RID(); + } +} + +void RasterizerSceneHighEndRD::RenderBufferDataHighEnd::configure(RID p_color_buffer, RID p_depth_buffer, int p_width, int p_height, RS::ViewportMSAA p_msaa) { + clear(); + + width = p_width; + height = p_height; + + color = p_color_buffer; + depth = p_depth_buffer; + + { + Vector<RID> fb; + fb.push_back(p_color_buffer); + fb.push_back(depth); + + color_fb = RD::get_singleton()->framebuffer_create(fb); + } + { + Vector<RID> fb; + fb.push_back(depth); + + depth_fb = RD::get_singleton()->framebuffer_create(fb); + } +} + +void RasterizerSceneHighEndRD::_allocate_normal_texture(RenderBufferDataHighEnd *rb) { + if (rb->normal_buffer.is_valid()) { + return; + } + + RD::TextureFormat tf; + tf.format = RD::DATA_FORMAT_A2B10G10R10_UNORM_PACK32; + tf.width = rb->width; + tf.height = rb->height; + tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; + + rb->normal_buffer = RD::get_singleton()->texture_create(tf, RD::TextureView()); + Vector<RID> fb; + fb.push_back(rb->depth); + fb.push_back(rb->normal_buffer); + rb->depth_normal_fb = RD::get_singleton()->framebuffer_create(fb); + + _render_buffers_clear_uniform_set(rb); +} + +void RasterizerSceneHighEndRD::_allocate_roughness_texture(RenderBufferDataHighEnd *rb) { + + if (rb->roughness_buffer.is_valid()) { + return; + } + + ERR_FAIL_COND(rb->normal_buffer.is_null()); + + RD::TextureFormat tf; + tf.format = RD::DATA_FORMAT_R8_UNORM; + tf.width = rb->width; + tf.height = rb->height; + tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; + + rb->roughness_buffer = RD::get_singleton()->texture_create(tf, RD::TextureView()); + Vector<RID> fb; + fb.push_back(rb->depth); + fb.push_back(rb->normal_buffer); + fb.push_back(rb->roughness_buffer); + rb->depth_normal_roughness_fb = RD::get_singleton()->framebuffer_create(fb); + + _render_buffers_clear_uniform_set(rb); +} + +RasterizerSceneRD::RenderBufferData *RasterizerSceneHighEndRD::_create_render_buffer_data() { + return memnew(RenderBufferDataHighEnd); +} + +bool RasterizerSceneHighEndRD::free(RID p_rid) { + if (RasterizerSceneRD::free(p_rid)) { + return true; + } + return false; +} + +void RasterizerSceneHighEndRD::_fill_instances(RenderList::Element **p_elements, int p_element_count, bool p_for_depth) { + + for (int i = 0; i < p_element_count; i++) { + + const RenderList::Element *e = p_elements[i]; + InstanceData &id = scene_state.instances[i]; + store_transform(e->instance->transform, id.transform); + store_transform(Transform(e->instance->transform.basis.inverse().transposed()), id.normal_transform); + id.flags = 0; + id.mask = e->instance->layer_mask; + + if (e->instance->base_type == RS::INSTANCE_MULTIMESH) { + id.flags |= INSTANCE_DATA_FLAG_MULTIMESH; + uint32_t stride; + if (storage->multimesh_get_transform_format(e->instance->base) == RS::MULTIMESH_TRANSFORM_2D) { + id.flags |= INSTANCE_DATA_FLAG_MULTIMESH_FORMAT_2D; + stride = 2; + } else { + stride = 3; + } + if (storage->multimesh_uses_colors(e->instance->base)) { + id.flags |= INSTANCE_DATA_FLAG_MULTIMESH_HAS_COLOR; + stride += 1; + } + if (storage->multimesh_uses_custom_data(e->instance->base)) { + id.flags |= INSTANCE_DATA_FLAG_MULTIMESH_HAS_CUSTOM_DATA; + stride += 1; + } + + id.flags |= (stride << INSTANCE_DATA_FLAGS_MULTIMESH_STRIDE_SHIFT); + } else if (e->instance->base_type == RS::INSTANCE_MESH) { + if (e->instance->skeleton.is_valid()) { + id.flags |= INSTANCE_DATA_FLAG_SKELETON; + } + } + + if (p_for_depth) { + id.gi_offset = 0xFFFFFFFF; + continue; + } + + if (!e->instance->gi_probe_instances.empty()) { + uint32_t written = 0; + for (int j = 0; j < e->instance->gi_probe_instances.size(); j++) { + RID probe = e->instance->gi_probe_instances[j]; + int slot = gi_probe_instance_get_slot(probe); + if (slot < 0) { + continue; //unallocated, dont render + } + + if (render_pass != gi_probe_instance_get_render_pass(probe)) { + continue; //not rendered in this frame + } + + uint32_t index = gi_probe_instance_get_render_index(probe); + + if (written == 0) { + id.gi_offset = index; + written = 1; + } else { + id.gi_offset = index << 16; + written = 2; + break; + } + } + if (written == 0) { + id.gi_offset = 0xFFFFFFFF; + } else if (written == 1) { + id.gi_offset |= 0xFFFF0000; + } + } else { + id.gi_offset = 0xFFFFFFFF; + } + } + + RD::get_singleton()->buffer_update(scene_state.instance_buffer, 0, sizeof(InstanceData) * p_element_count, scene_state.instances, true); +} + +/// RENDERING /// + +void RasterizerSceneHighEndRD::_render_list(RenderingDevice::DrawListID p_draw_list, RenderingDevice::FramebufferFormatID p_framebuffer_Format, RenderList::Element **p_elements, int p_element_count, bool p_reverse_cull, PassMode p_pass_mode, bool p_no_gi, RID p_radiance_uniform_set, RID p_render_buffers_uniform_set) { + + RD::DrawListID draw_list = p_draw_list; + RD::FramebufferFormatID framebuffer_format = p_framebuffer_Format; + + //global scope bindings + RD::get_singleton()->draw_list_bind_uniform_set(draw_list, render_base_uniform_set, SCENE_UNIFORM_SET); + if (p_radiance_uniform_set.is_valid()) { + RD::get_singleton()->draw_list_bind_uniform_set(draw_list, p_radiance_uniform_set, RADIANCE_UNIFORM_SET); + } else { + RD::get_singleton()->draw_list_bind_uniform_set(draw_list, default_radiance_uniform_set, RADIANCE_UNIFORM_SET); + } + RD::get_singleton()->draw_list_bind_uniform_set(draw_list, view_dependant_uniform_set, VIEW_DEPENDANT_UNIFORM_SET); + if (p_render_buffers_uniform_set.is_valid()) { + RD::get_singleton()->draw_list_bind_uniform_set(draw_list, p_render_buffers_uniform_set, RENDER_BUFFERS_UNIFORM_SET); + } else { + RD::get_singleton()->draw_list_bind_uniform_set(draw_list, default_render_buffers_uniform_set, RENDER_BUFFERS_UNIFORM_SET); + } + RD::get_singleton()->draw_list_bind_uniform_set(draw_list, default_vec4_xform_uniform_set, TRANSFORMS_UNIFORM_SET); + + MaterialData *prev_material = nullptr; + + RID prev_vertex_array_rd; + RID prev_index_array_rd; + RID prev_pipeline_rd; + RID prev_xforms_uniform_set; + + PushConstant push_constant; + zeromem(&push_constant, sizeof(PushConstant)); + + for (int i = 0; i < p_element_count; i++) { + + const RenderList::Element *e = p_elements[i]; + + MaterialData *material = e->material; + ShaderData *shader = material->shader_data; + RID xforms_uniform_set; + + //find cull variant + ShaderData::CullVariant cull_variant; + + if ((p_pass_mode == PASS_MODE_SHADOW || p_pass_mode == PASS_MODE_SHADOW_DP) && e->instance->cast_shadows == RS::SHADOW_CASTING_SETTING_DOUBLE_SIDED) { + cull_variant = ShaderData::CULL_VARIANT_DOUBLE_SIDED; + } else { + bool mirror = e->instance->mirror; + if (p_reverse_cull) { + mirror = !mirror; + } + cull_variant = mirror ? ShaderData::CULL_VARIANT_REVERSED : ShaderData::CULL_VARIANT_NORMAL; + } + + //find primitive and vertex format + RS::PrimitiveType primitive; + + switch (e->instance->base_type) { + case RS::INSTANCE_MESH: { + primitive = storage->mesh_surface_get_primitive(e->instance->base, e->surface_index); + if (e->instance->skeleton.is_valid()) { + xforms_uniform_set = storage->skeleton_get_3d_uniform_set(e->instance->skeleton, default_shader_rd, TRANSFORMS_UNIFORM_SET); + } + } break; + case RS::INSTANCE_MULTIMESH: { + RID mesh = storage->multimesh_get_mesh(e->instance->base); + ERR_CONTINUE(!mesh.is_valid()); //should be a bug + primitive = storage->mesh_surface_get_primitive(mesh, e->surface_index); + + xforms_uniform_set = storage->multimesh_get_3d_uniform_set(e->instance->base, default_shader_rd, TRANSFORMS_UNIFORM_SET); + + } break; + case RS::INSTANCE_IMMEDIATE: { + ERR_CONTINUE(true); //should be a bug + } break; + case RS::INSTANCE_PARTICLES: { + ERR_CONTINUE(true); //should be a bug + } break; + default: { + ERR_CONTINUE(true); //should be a bug + } + } + + ShaderVersion shader_version = SHADER_VERSION_MAX; // Assigned to silence wrong -Wmaybe-initialized. + + switch (p_pass_mode) { + case PASS_MODE_COLOR: + case PASS_MODE_COLOR_TRANSPARENT: { + if (e->uses_lightmap) { + shader_version = SHADER_VERSION_LIGHTMAP_COLOR_PASS; + } else if (e->uses_vct) { + shader_version = SHADER_VERSION_VCT_COLOR_PASS; + } else { + shader_version = SHADER_VERSION_COLOR_PASS; + } + } break; + case PASS_MODE_COLOR_SPECULAR: { + if (e->uses_lightmap) { + shader_version = SHADER_VERSION_LIGHTMAP_COLOR_PASS_WITH_SEPARATE_SPECULAR; + } else if (e->uses_vct) { + shader_version = SHADER_VERSION_VCT_COLOR_PASS_WITH_SEPARATE_SPECULAR; + } else { + shader_version = SHADER_VERSION_COLOR_PASS_WITH_SEPARATE_SPECULAR; + } + } break; + case PASS_MODE_SHADOW: + case PASS_MODE_DEPTH: { + shader_version = SHADER_VERSION_DEPTH_PASS; + } break; + case PASS_MODE_SHADOW_DP: { + shader_version = SHADER_VERSION_DEPTH_PASS_DP; + } break; + case PASS_MODE_DEPTH_NORMAL: { + shader_version = SHADER_VERSION_DEPTH_PASS_WITH_NORMAL; + } break; + case PASS_MODE_DEPTH_NORMAL_ROUGHNESS: { + shader_version = SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS; + } break; + case PASS_MODE_DEPTH_MATERIAL: { + shader_version = SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL; + } break; + } + + RenderPipelineVertexFormatCacheRD *pipeline = nullptr; + + pipeline = &shader->pipelines[cull_variant][primitive][shader_version]; + + RD::VertexFormatID vertex_format = -1; + RID vertex_array_rd; + RID index_array_rd; + + switch (e->instance->base_type) { + case RS::INSTANCE_MESH: { + storage->mesh_surface_get_arrays_and_format(e->instance->base, e->surface_index, pipeline->get_vertex_input_mask(), vertex_array_rd, index_array_rd, vertex_format); + } break; + case RS::INSTANCE_MULTIMESH: { + RID mesh = storage->multimesh_get_mesh(e->instance->base); + ERR_CONTINUE(!mesh.is_valid()); //should be a bug + storage->mesh_surface_get_arrays_and_format(mesh, e->surface_index, pipeline->get_vertex_input_mask(), vertex_array_rd, index_array_rd, vertex_format); + } break; + case RS::INSTANCE_IMMEDIATE: { + ERR_CONTINUE(true); //should be a bug + } break; + case RS::INSTANCE_PARTICLES: { + ERR_CONTINUE(true); //should be a bug + } break; + default: { + ERR_CONTINUE(true); //should be a bug + } + } + + if (prev_vertex_array_rd != vertex_array_rd) { + RD::get_singleton()->draw_list_bind_vertex_array(draw_list, vertex_array_rd); + prev_vertex_array_rd = vertex_array_rd; + } + + if (prev_index_array_rd != index_array_rd) { + if (index_array_rd.is_valid()) { + RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array_rd); + } + prev_index_array_rd = index_array_rd; + } + + RID pipeline_rd = pipeline->get_render_pipeline(vertex_format, framebuffer_format); + + if (pipeline_rd != prev_pipeline_rd) { + // checking with prev shader does not make so much sense, as + // the pipeline may still be different. + RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, pipeline_rd); + prev_pipeline_rd = pipeline_rd; + } + + if (xforms_uniform_set.is_valid() && prev_xforms_uniform_set != xforms_uniform_set) { + RD::get_singleton()->draw_list_bind_uniform_set(draw_list, xforms_uniform_set, TRANSFORMS_UNIFORM_SET); + prev_xforms_uniform_set = xforms_uniform_set; + } + + if (material != prev_material) { + //update uniform set + if (material->uniform_set.is_valid()) { + RD::get_singleton()->draw_list_bind_uniform_set(draw_list, material->uniform_set, MATERIAL_UNIFORM_SET); + } + + prev_material = material; + } + + push_constant.index = i; + RD::get_singleton()->draw_list_set_push_constant(draw_list, &push_constant, sizeof(PushConstant)); + + switch (e->instance->base_type) { + case RS::INSTANCE_MESH: { + RD::get_singleton()->draw_list_draw(draw_list, index_array_rd.is_valid()); + } break; + case RS::INSTANCE_MULTIMESH: { + uint32_t instances = storage->multimesh_get_instances_to_draw(e->instance->base); + RD::get_singleton()->draw_list_draw(draw_list, index_array_rd.is_valid(), instances); + } break; + case RS::INSTANCE_IMMEDIATE: { + + } break; + case RS::INSTANCE_PARTICLES: { + + } break; + default: { + ERR_CONTINUE(true); //should be a bug + } + } + } +} + +void RasterizerSceneHighEndRD::_setup_environment(RID p_environment, const CameraMatrix &p_cam_projection, const Transform &p_cam_transform, RID p_reflection_probe, bool p_no_fog, const Size2 &p_screen_pixel_size, RID p_shadow_atlas, bool p_flip_y, const Color &p_default_bg_color, float p_znear, float p_zfar, bool p_opaque_render_buffers) { + + //CameraMatrix projection = p_cam_projection; + //projection.flip_y(); // Vulkan and modern APIs use Y-Down + CameraMatrix correction; + correction.set_depth_correction(p_flip_y); + CameraMatrix projection = correction * p_cam_projection; + + //store camera into ubo + store_camera(projection, scene_state.ubo.projection_matrix); + store_camera(projection.inverse(), scene_state.ubo.inv_projection_matrix); + store_transform(p_cam_transform, scene_state.ubo.camera_matrix); + store_transform(p_cam_transform.affine_inverse(), scene_state.ubo.inv_camera_matrix); + + scene_state.ubo.z_far = p_zfar; + scene_state.ubo.z_near = p_znear; + + scene_state.ubo.screen_pixel_size[0] = p_screen_pixel_size.x; + scene_state.ubo.screen_pixel_size[1] = p_screen_pixel_size.y; + + if (p_shadow_atlas.is_valid()) { + Vector2 sas = shadow_atlas_get_size(p_shadow_atlas); + scene_state.ubo.shadow_atlas_pixel_size[0] = 1.0 / sas.x; + scene_state.ubo.shadow_atlas_pixel_size[1] = 1.0 / sas.y; + } + { + Vector2 dss = directional_shadow_get_size(); + scene_state.ubo.directional_shadow_pixel_size[0] = 1.0 / dss.x; + scene_state.ubo.directional_shadow_pixel_size[1] = 1.0 / dss.y; + } + //time global variables + scene_state.ubo.time = time; + + if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_UNSHADED) { + + scene_state.ubo.use_ambient_light = true; + scene_state.ubo.ambient_light_color_energy[0] = 1; + scene_state.ubo.ambient_light_color_energy[1] = 1; + scene_state.ubo.ambient_light_color_energy[2] = 1; + scene_state.ubo.ambient_light_color_energy[3] = 1.0; + scene_state.ubo.use_ambient_cubemap = false; + scene_state.ubo.use_reflection_cubemap = false; + scene_state.ubo.ssao_enabled = false; + + } else if (is_environment(p_environment)) { + + RS::EnvironmentBG env_bg = environment_get_background(p_environment); + RS::EnvironmentAmbientSource ambient_src = environment_get_ambient_light_ambient_source(p_environment); + + float bg_energy = environment_get_bg_energy(p_environment); + scene_state.ubo.ambient_light_color_energy[3] = bg_energy; + + scene_state.ubo.ambient_color_sky_mix = environment_get_ambient_sky_contribution(p_environment); + + //ambient + if (ambient_src == RS::ENV_AMBIENT_SOURCE_BG && (env_bg == RS::ENV_BG_CLEAR_COLOR || env_bg == RS::ENV_BG_COLOR)) { + + Color color = env_bg == RS::ENV_BG_CLEAR_COLOR ? p_default_bg_color : environment_get_bg_color(p_environment); + color = color.to_linear(); + + scene_state.ubo.ambient_light_color_energy[0] = color.r * bg_energy; + scene_state.ubo.ambient_light_color_energy[1] = color.g * bg_energy; + scene_state.ubo.ambient_light_color_energy[2] = color.b * bg_energy; + scene_state.ubo.use_ambient_light = true; + scene_state.ubo.use_ambient_cubemap = false; + } else { + + float energy = environment_get_ambient_light_ambient_energy(p_environment); + Color color = environment_get_ambient_light_color(p_environment); + color = color.to_linear(); + scene_state.ubo.ambient_light_color_energy[0] = color.r * energy; + scene_state.ubo.ambient_light_color_energy[1] = color.g * energy; + scene_state.ubo.ambient_light_color_energy[2] = color.b * energy; + + Basis sky_transform = environment_get_sky_orientation(p_environment); + sky_transform = sky_transform.inverse() * p_cam_transform.basis; + store_transform_3x3(sky_transform, scene_state.ubo.radiance_inverse_xform); + + scene_state.ubo.use_ambient_cubemap = (ambient_src == RS::ENV_AMBIENT_SOURCE_BG && env_bg == RS::ENV_BG_SKY) || ambient_src == RS::ENV_AMBIENT_SOURCE_SKY; + scene_state.ubo.use_ambient_light = scene_state.ubo.use_ambient_cubemap || ambient_src == RS::ENV_AMBIENT_SOURCE_COLOR; + } + + //specular + RS::EnvironmentReflectionSource ref_src = environment_get_reflection_source(p_environment); + if ((ref_src == RS::ENV_REFLECTION_SOURCE_BG && env_bg == RS::ENV_BG_SKY) || ref_src == RS::ENV_REFLECTION_SOURCE_SKY) { + scene_state.ubo.use_reflection_cubemap = true; + } else { + scene_state.ubo.use_reflection_cubemap = false; + } + + scene_state.ubo.ssao_enabled = p_opaque_render_buffers && environment_is_ssao_enabled(p_environment); + scene_state.ubo.ssao_ao_affect = environment_get_ssao_ao_affect(p_environment); + scene_state.ubo.ssao_light_affect = environment_get_ssao_light_affect(p_environment); + + Color ao_color = environment_get_ao_color(p_environment); + scene_state.ubo.ao_color[0] = ao_color.r; + scene_state.ubo.ao_color[1] = ao_color.g; + scene_state.ubo.ao_color[2] = ao_color.b; + scene_state.ubo.ao_color[3] = ao_color.a; + + } else { + + if (p_reflection_probe.is_valid() && storage->reflection_probe_is_interior(reflection_probe_instance_get_probe(p_reflection_probe))) { + scene_state.ubo.use_ambient_light = false; + } else { + scene_state.ubo.use_ambient_light = true; + Color clear_color = p_default_bg_color; + clear_color = clear_color.to_linear(); + scene_state.ubo.ambient_light_color_energy[0] = clear_color.r; + scene_state.ubo.ambient_light_color_energy[1] = clear_color.g; + scene_state.ubo.ambient_light_color_energy[2] = clear_color.b; + scene_state.ubo.ambient_light_color_energy[3] = 1.0; + } + + scene_state.ubo.use_ambient_cubemap = false; + scene_state.ubo.use_reflection_cubemap = false; + } + + scene_state.ubo.roughness_limiter_enabled = p_opaque_render_buffers && screen_space_roughness_limiter_is_active(); + + RD::get_singleton()->buffer_update(scene_state.uniform_buffer, 0, sizeof(SceneState::UBO), &scene_state.ubo, true); +} + +void RasterizerSceneHighEndRD::_add_geometry(InstanceBase *p_instance, uint32_t p_surface, RID p_material, PassMode p_pass_mode, uint32_t p_geometry_index) { + + RID m_src; + + m_src = p_instance->material_override.is_valid() ? p_instance->material_override : p_material; + + if (unlikely(get_debug_draw_mode() != RS::VIEWPORT_DEBUG_DRAW_DISABLED)) { + if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_OVERDRAW) { + m_src = overdraw_material; + } else if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME) { + m_src = wireframe_material; + } else if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_LIGHTING) { + m_src = default_material; + } + } + + MaterialData *material = NULL; + + if (m_src.is_valid()) { + material = (MaterialData *)storage->material_get_data(m_src, RasterizerStorageRD::SHADER_TYPE_3D); + if (!material || !material->shader_data->valid) { + material = NULL; + } + } + + if (!material) { + material = (MaterialData *)storage->material_get_data(default_material, RasterizerStorageRD::SHADER_TYPE_3D); + m_src = default_material; + } + + ERR_FAIL_COND(!material); + + _add_geometry_with_material(p_instance, p_surface, material, m_src, p_pass_mode, p_geometry_index); + + while (material->next_pass.is_valid()) { + + material = (MaterialData *)storage->material_get_data(material->next_pass, RasterizerStorageRD::SHADER_TYPE_3D); + if (!material || !material->shader_data->valid) + break; + _add_geometry_with_material(p_instance, p_surface, material, material->next_pass, p_pass_mode, p_geometry_index); + } +} + +void RasterizerSceneHighEndRD::_add_geometry_with_material(InstanceBase *p_instance, uint32_t p_surface, MaterialData *p_material, RID p_material_rid, PassMode p_pass_mode, uint32_t p_geometry_index) { + + bool has_read_screen_alpha = p_material->shader_data->uses_screen_texture || p_material->shader_data->uses_depth_texture || p_material->shader_data->uses_normal_texture; + bool has_base_alpha = (p_material->shader_data->uses_alpha || has_read_screen_alpha); + bool has_blend_alpha = p_material->shader_data->uses_blend_alpha; + bool has_alpha = has_base_alpha || has_blend_alpha; + + if (p_material->shader_data->uses_sss) { + scene_state.used_sss = true; + } + + if (p_material->shader_data->uses_screen_texture) { + scene_state.used_screen_texture = true; + } + + if (p_material->shader_data->uses_depth_texture) { + scene_state.used_depth_texture = true; + } + + if (p_material->shader_data->uses_normal_texture) { + scene_state.used_normal_texture = true; + } + + if (p_pass_mode != PASS_MODE_COLOR && p_pass_mode != PASS_MODE_COLOR_SPECULAR) { + + if (has_blend_alpha || has_read_screen_alpha || (has_base_alpha && !p_material->shader_data->uses_depth_pre_pass) || p_material->shader_data->depth_draw == ShaderData::DEPTH_DRAW_DISABLED || p_material->shader_data->depth_test == ShaderData::DEPTH_TEST_DISABLED || p_instance->cast_shadows == RS::SHADOW_CASTING_SETTING_OFF) { + //conditions in which no depth pass should be processed + return; + } + + if (p_pass_mode != PASS_MODE_DEPTH_MATERIAL && !p_material->shader_data->writes_modelview_or_projection && !p_material->shader_data->uses_vertex && !p_material->shader_data->uses_discard && !p_material->shader_data->uses_depth_pre_pass) { + //shader does not use discard and does not write a vertex position, use generic material + if (p_pass_mode == PASS_MODE_SHADOW || p_pass_mode == PASS_MODE_DEPTH) { + p_material = (MaterialData *)storage->material_get_data(default_material, RasterizerStorageRD::SHADER_TYPE_3D); + } else if (p_pass_mode == PASS_MODE_DEPTH_NORMAL && !p_material->shader_data->uses_normal) { + p_material = (MaterialData *)storage->material_get_data(default_material, RasterizerStorageRD::SHADER_TYPE_3D); + } else if (p_pass_mode == PASS_MODE_DEPTH_NORMAL_ROUGHNESS && !p_material->shader_data->uses_normal && !p_material->shader_data->uses_roughness) { + p_material = (MaterialData *)storage->material_get_data(default_material, RasterizerStorageRD::SHADER_TYPE_3D); + } + } + + has_alpha = false; + } + + RenderList::Element *e = (has_alpha || p_material->shader_data->depth_test == ShaderData::DEPTH_TEST_DISABLED) ? render_list.add_alpha_element() : render_list.add_element(); + + if (!e) + return; + + e->instance = p_instance; + e->material = p_material; + e->surface_index = p_surface; + e->sort_key = 0; + + if (e->material->last_pass != render_pass) { + if (!RD::get_singleton()->uniform_set_is_valid(e->material->uniform_set)) { + //uniform set no longer valid, probably a texture changed + storage->material_force_update_textures(p_material_rid, RasterizerStorageRD::SHADER_TYPE_3D); + } + e->material->last_pass = render_pass; + e->material->index = scene_state.current_material_index++; + if (e->material->shader_data->last_pass != render_pass) { + e->material->shader_data->last_pass = scene_state.current_material_index++; + e->material->shader_data->index = scene_state.current_shader_index++; + } + } + e->geometry_index = p_geometry_index; + e->material_index = e->material->index; + e->uses_instancing = e->instance->base_type == RS::INSTANCE_MULTIMESH; + e->uses_lightmap = e->instance->lightmap.is_valid(); + e->uses_vct = e->instance->gi_probe_instances.size(); + e->shader_index = e->shader_index; + e->depth_layer = e->instance->depth_layer; + e->priority = p_material->priority; + + if (p_material->shader_data->uses_time) { + RenderingServerRaster::redraw_request(); + } +} + +void RasterizerSceneHighEndRD::_fill_render_list(InstanceBase **p_cull_result, int p_cull_count, PassMode p_pass_mode, bool p_no_gi) { + + scene_state.current_shader_index = 0; + scene_state.current_material_index = 0; + scene_state.used_sss = false; + scene_state.used_screen_texture = false; + scene_state.used_normal_texture = false; + scene_state.used_depth_texture = false; + + uint32_t geometry_index = 0; + + //fill list + + for (int i = 0; i < p_cull_count; i++) { + + InstanceBase *inst = p_cull_result[i]; + + //add geometry for drawing + switch (inst->base_type) { + + case RS::INSTANCE_MESH: { + + const RID *materials = NULL; + uint32_t surface_count; + + materials = storage->mesh_get_surface_count_and_materials(inst->base, surface_count); + if (!materials) { + continue; //nothing to do + } + + const RID *inst_materials = inst->materials.ptr(); + + for (uint32_t j = 0; j < surface_count; j++) { + + RID material = inst_materials[j].is_valid() ? inst_materials[j] : materials[j]; + + uint32_t surface_index = storage->mesh_surface_get_render_pass_index(inst->base, j, render_pass, &geometry_index); + _add_geometry(inst, j, material, p_pass_mode, surface_index); + } + + //mesh->last_pass=frame; + + } break; + + case RS::INSTANCE_MULTIMESH: { + + if (storage->multimesh_get_instances_to_draw(inst->base) == 0) { + //not visible, 0 instances + continue; + } + + RID mesh = storage->multimesh_get_mesh(inst->base); + if (!mesh.is_valid()) { + continue; + } + + const RID *materials = NULL; + uint32_t surface_count; + + materials = storage->mesh_get_surface_count_and_materials(mesh, surface_count); + if (!materials) { + continue; //nothing to do + } + + for (uint32_t j = 0; j < surface_count; j++) { + + uint32_t surface_index = storage->mesh_surface_get_multimesh_render_pass_index(mesh, j, render_pass, &geometry_index); + _add_geometry(inst, j, materials[j], p_pass_mode, surface_index); + } + + } break; +#if 0 + case RS::INSTANCE_IMMEDIATE: { + + RasterizerStorageGLES3::Immediate *immediate = storage->immediate_owner.getornull(inst->base); + ERR_CONTINUE(!immediate); + + _add_geometry(immediate, inst, NULL, -1, p_depth_pass, p_shadow_pass); + + } break; + case RS::INSTANCE_PARTICLES: { + + RasterizerStorageGLES3::Particles *particles = storage->particles_owner.getornull(inst->base); + ERR_CONTINUE(!particles); + + for (int j = 0; j < particles->draw_passes.size(); j++) { + + RID pmesh = particles->draw_passes[j]; + if (!pmesh.is_valid()) + continue; + RasterizerStorageGLES3::Mesh *mesh = storage->mesh_owner.getornull(pmesh); + if (!mesh) + continue; //mesh not assigned + + int ssize = mesh->surfaces.size(); + + for (int k = 0; k < ssize; k++) { + + RasterizerStorageGLES3::Surface *s = mesh->surfaces[k]; + _add_geometry(s, inst, particles, -1, p_depth_pass, p_shadow_pass); + } + } + + } break; +#endif + default: { + } + } + } +} + +void RasterizerSceneHighEndRD::_setup_reflections(RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, const Transform &p_camera_inverse_transform, RID p_environment) { + + for (int i = 0; i < p_reflection_probe_cull_count; i++) { + + RID rpi = p_reflection_probe_cull_result[i]; + + if (i >= (int)scene_state.max_reflections) { + reflection_probe_instance_set_render_index(rpi, 0); //invalid, but something needs to be set + continue; + } + + reflection_probe_instance_set_render_index(rpi, i); + + RID base_probe = reflection_probe_instance_get_probe(rpi); + + ReflectionData &reflection_ubo = scene_state.reflections[i]; + + Vector3 extents = storage->reflection_probe_get_extents(base_probe); + + reflection_ubo.box_extents[0] = extents.x; + reflection_ubo.box_extents[1] = extents.y; + reflection_ubo.box_extents[2] = extents.z; + reflection_ubo.index = reflection_probe_instance_get_atlas_index(rpi); + + Vector3 origin_offset = storage->reflection_probe_get_origin_offset(base_probe); + + reflection_ubo.box_offset[0] = origin_offset.x; + reflection_ubo.box_offset[1] = origin_offset.y; + reflection_ubo.box_offset[2] = origin_offset.z; + reflection_ubo.mask = storage->reflection_probe_get_cull_mask(base_probe); + + float intensity = storage->reflection_probe_get_intensity(base_probe); + bool interior = storage->reflection_probe_is_interior(base_probe); + bool box_projection = storage->reflection_probe_is_box_projection(base_probe); + + reflection_ubo.params[0] = intensity; + reflection_ubo.params[1] = 0; + reflection_ubo.params[2] = interior ? 1.0 : 0.0; + reflection_ubo.params[3] = box_projection ? 1.0 : 0.0; + + if (interior) { + Color ambient_linear = storage->reflection_probe_get_interior_ambient(base_probe).to_linear(); + float interior_ambient_energy = storage->reflection_probe_get_interior_ambient_energy(base_probe); + float interior_ambient_probe_contrib = storage->reflection_probe_get_interior_ambient_probe_contribution(base_probe); + reflection_ubo.ambient[0] = ambient_linear.r * interior_ambient_energy; + reflection_ubo.ambient[1] = ambient_linear.g * interior_ambient_energy; + reflection_ubo.ambient[2] = ambient_linear.b * interior_ambient_energy; + reflection_ubo.ambient[3] = interior_ambient_probe_contrib; + } else { + Color ambient_linear = storage->reflection_probe_get_interior_ambient(base_probe).to_linear(); + if (is_environment(p_environment)) { + Color env_ambient_color = environment_get_ambient_light_color(p_environment).to_linear(); + float env_ambient_energy = environment_get_ambient_light_ambient_energy(p_environment); + ambient_linear = env_ambient_color; + ambient_linear.r *= env_ambient_energy; + ambient_linear.g *= env_ambient_energy; + ambient_linear.b *= env_ambient_energy; + } + + reflection_ubo.ambient[0] = ambient_linear.r; + reflection_ubo.ambient[1] = ambient_linear.g; + reflection_ubo.ambient[2] = ambient_linear.b; + reflection_ubo.ambient[3] = 0; //not used in exterior mode, since it just blends with regular ambient light + } + + Transform transform = reflection_probe_instance_get_transform(rpi); + Transform proj = (p_camera_inverse_transform * transform).inverse(); + store_transform(proj, reflection_ubo.local_matrix); + + cluster_builder.add_reflection_probe(transform, extents); + + reflection_probe_instance_set_render_pass(rpi, render_pass); + } + + if (p_reflection_probe_cull_count) { + RD::get_singleton()->buffer_update(scene_state.reflection_buffer, 0, MIN(scene_state.max_reflections, (unsigned int)p_reflection_probe_cull_count) * sizeof(ReflectionData), scene_state.reflections, true); + } +} + +void RasterizerSceneHighEndRD::_setup_gi_probes(RID *p_gi_probe_probe_cull_result, int p_gi_probe_probe_cull_count, const Transform &p_camera_transform) { + + int index = 0; + + for (int i = 0; i < p_gi_probe_probe_cull_count; i++) { + + RID rpi = p_gi_probe_probe_cull_result[i]; + + if (index >= (int)scene_state.max_gi_probes) { + continue; + } + + int slot = gi_probe_instance_get_slot(rpi); + if (slot < 0) { + continue; //not usable + } + + RID base_probe = gi_probe_instance_get_base_probe(rpi); + + GIProbeData &gi_probe_ubo = scene_state.gi_probes[index]; + + Transform to_cell = gi_probe_instance_get_transform_to_cell(rpi) * p_camera_transform; + + store_transform(to_cell, gi_probe_ubo.xform); + + Vector3 bounds = storage->gi_probe_get_octree_size(base_probe); + + gi_probe_ubo.bounds[0] = bounds.x; + gi_probe_ubo.bounds[1] = bounds.y; + gi_probe_ubo.bounds[2] = bounds.z; + + gi_probe_ubo.dynamic_range = storage->gi_probe_get_dynamic_range(base_probe) * storage->gi_probe_get_energy(base_probe); + gi_probe_ubo.bias = storage->gi_probe_get_bias(base_probe); + gi_probe_ubo.normal_bias = storage->gi_probe_get_normal_bias(base_probe); + gi_probe_ubo.blend_ambient = !storage->gi_probe_is_interior(base_probe); + gi_probe_ubo.texture_slot = gi_probe_instance_get_slot(rpi); + gi_probe_ubo.anisotropy_strength = storage->gi_probe_get_anisotropy_strength(base_probe); + gi_probe_ubo.ao = storage->gi_probe_get_ao(base_probe); + gi_probe_ubo.ao_size = Math::pow(storage->gi_probe_get_ao_size(base_probe), 4.0f); + + if (gi_probe_is_anisotropic()) { + gi_probe_ubo.texture_slot *= 3; + } + + gi_probe_instance_set_render_index(rpi, index); + gi_probe_instance_set_render_pass(rpi, render_pass); + + index++; + } + + if (index) { + RD::get_singleton()->buffer_update(scene_state.gi_probe_buffer, 0, index * sizeof(GIProbeData), scene_state.gi_probes, true); + } +} + +void RasterizerSceneHighEndRD::_setup_lights(RID *p_light_cull_result, int p_light_cull_count, const Transform &p_camera_inverse_transform, RID p_shadow_atlas, bool p_using_shadows) { + + uint32_t light_count = 0; + scene_state.ubo.directional_light_count = 0; + sky_scene_state.directional_light_count = 0; + + for (int i = 0; i < p_light_cull_count; i++) { + + RID li = p_light_cull_result[i]; + RID base = light_instance_get_base_light(li); + + ERR_CONTINUE(base.is_null()); + + RS::LightType type = storage->light_get_type(base); + switch (type) { + + case RS::LIGHT_DIRECTIONAL: { + + if (scene_state.ubo.directional_light_count >= scene_state.max_directional_lights) { + continue; + } + + DirectionalLightData &light_data = scene_state.directional_lights[scene_state.ubo.directional_light_count]; + + Transform light_transform = light_instance_get_base_transform(li); + + Vector3 direction = p_camera_inverse_transform.basis.xform(light_transform.basis.xform(Vector3(0, 0, 1))).normalized(); + + light_data.direction[0] = direction.x; + light_data.direction[1] = direction.y; + light_data.direction[2] = direction.z; + + float sign = storage->light_is_negative(base) ? -1 : 1; + + light_data.energy = sign * storage->light_get_param(base, RS::LIGHT_PARAM_ENERGY) * Math_PI; + + Color linear_col = storage->light_get_color(base).to_linear(); + light_data.color[0] = linear_col.r; + light_data.color[1] = linear_col.g; + light_data.color[2] = linear_col.b; + + light_data.specular = storage->light_get_param(base, RS::LIGHT_PARAM_SPECULAR); + light_data.mask = storage->light_get_cull_mask(base); + + Color shadow_col = storage->light_get_shadow_color(base).to_linear(); + + light_data.shadow_color[0] = shadow_col.r; + light_data.shadow_color[1] = shadow_col.g; + light_data.shadow_color[2] = shadow_col.b; + + light_data.shadow_enabled = p_using_shadows && storage->light_has_shadow(base); + + if (light_data.shadow_enabled) { + + RS::LightDirectionalShadowMode smode = storage->light_directional_get_shadow_mode(base); + + int limit = smode == RS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL ? 0 : (smode == RS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_2_SPLITS ? 1 : 3); + light_data.blend_splits = storage->light_directional_get_blend_splits(base); + for (int j = 0; j < 4; j++) { + Rect2 atlas_rect = light_instance_get_directional_shadow_atlas_rect(li, j); + CameraMatrix matrix = light_instance_get_shadow_camera(li, j); + float split = light_instance_get_directional_shadow_split(li, MIN(limit, j)); + + CameraMatrix bias; + bias.set_light_bias(); + CameraMatrix rectm; + rectm.set_light_atlas_rect(atlas_rect); + + Transform modelview = (p_camera_inverse_transform * light_instance_get_shadow_transform(li, j)).inverse(); + + CameraMatrix shadow_mtx = rectm * bias * matrix * modelview; + light_data.shadow_split_offsets[j] = split; + store_camera(shadow_mtx, light_data.shadow_matrices[j]); + } + + float fade_start = storage->light_get_param(base, RS::LIGHT_PARAM_SHADOW_FADE_START); + light_data.fade_from = -light_data.shadow_split_offsets[3] * MIN(fade_start, 0.999); //using 1.0 would break smoothstep + light_data.fade_to = -light_data.shadow_split_offsets[3]; + } + + // Copy to SkyDirectionalLightData + if (sky_scene_state.directional_light_count < sky_scene_state.max_directional_lights) { + + SkyDirectionalLightData &sky_light_data = sky_scene_state.directional_lights[sky_scene_state.directional_light_count]; + + Vector3 world_direction = light_transform.basis.xform(Vector3(0, 0, 1)).normalized(); + + sky_light_data.direction[0] = world_direction.x; + sky_light_data.direction[1] = world_direction.y; + sky_light_data.direction[2] = -world_direction.z; + + sky_light_data.energy = light_data.energy / Math_PI; + + sky_light_data.color[0] = light_data.color[0]; + sky_light_data.color[1] = light_data.color[1]; + sky_light_data.color[2] = light_data.color[2]; + + sky_light_data.enabled = true; + sky_scene_state.directional_light_count++; + } + + scene_state.ubo.directional_light_count++; + } break; + case RS::LIGHT_SPOT: + case RS::LIGHT_OMNI: { + + if (light_count >= scene_state.max_lights) { + continue; + } + + Transform light_transform = light_instance_get_base_transform(li); + + LightData &light_data = scene_state.lights[light_count]; + + float sign = storage->light_is_negative(base) ? -1 : 1; + Color linear_col = storage->light_get_color(base).to_linear(); + + light_data.attenuation_energy[0] = Math::make_half_float(storage->light_get_param(base, RS::LIGHT_PARAM_ATTENUATION)); + light_data.attenuation_energy[1] = Math::make_half_float(sign * storage->light_get_param(base, RS::LIGHT_PARAM_ENERGY) * Math_PI); + + light_data.color_specular[0] = MIN(uint32_t(linear_col.r * 255), 255); + light_data.color_specular[1] = MIN(uint32_t(linear_col.g * 255), 255); + light_data.color_specular[2] = MIN(uint32_t(linear_col.b * 255), 255); + light_data.color_specular[3] = MIN(uint32_t(storage->light_get_param(base, RS::LIGHT_PARAM_SPECULAR) * 255), 255); + + float radius = MAX(0.001, storage->light_get_param(base, RS::LIGHT_PARAM_RANGE)); + light_data.inv_radius = 1.0 / radius; + + Vector3 pos = p_camera_inverse_transform.xform(light_transform.origin); + + light_data.position[0] = pos.x; + light_data.position[1] = pos.y; + light_data.position[2] = pos.z; + + Vector3 direction = p_camera_inverse_transform.basis.xform(light_transform.basis.xform(Vector3(0, 0, -1))).normalized(); + + light_data.direction[0] = direction.x; + light_data.direction[1] = direction.y; + light_data.direction[2] = direction.z; + + light_data.cone_attenuation_angle[0] = Math::make_half_float(storage->light_get_param(base, RS::LIGHT_PARAM_SPOT_ATTENUATION)); + float spot_angle = storage->light_get_param(base, RS::LIGHT_PARAM_SPOT_ANGLE); + light_data.cone_attenuation_angle[1] = Math::make_half_float(Math::cos(Math::deg2rad(spot_angle))); + + light_data.mask = storage->light_get_cull_mask(base); + + Color shadow_color = storage->light_get_shadow_color(base); + + bool has_shadow = p_using_shadows && storage->light_has_shadow(base); + light_data.shadow_color_enabled[0] = MIN(uint32_t(shadow_color.r * 255), 255); + light_data.shadow_color_enabled[1] = MIN(uint32_t(shadow_color.g * 255), 255); + light_data.shadow_color_enabled[2] = MIN(uint32_t(shadow_color.b * 255), 255); + light_data.shadow_color_enabled[3] = has_shadow ? 255 : 0; + + light_data.atlas_rect[0] = 0; + light_data.atlas_rect[1] = 0; + light_data.atlas_rect[2] = 0; + light_data.atlas_rect[3] = 0; + + if (p_using_shadows && p_shadow_atlas.is_valid() && shadow_atlas_owns_light_instance(p_shadow_atlas, li)) { + // fill in the shadow information + + Rect2 rect = light_instance_get_shadow_atlas_rect(li, p_shadow_atlas); + + if (type == RS::LIGHT_OMNI) { + + light_data.atlas_rect[0] = rect.position.x; + light_data.atlas_rect[1] = rect.position.y; + light_data.atlas_rect[2] = rect.size.width; + light_data.atlas_rect[3] = rect.size.height * 0.5; + + Transform proj = (p_camera_inverse_transform * light_transform).inverse(); + + store_transform(proj, light_data.shadow_matrix); + } else if (type == RS::LIGHT_SPOT) { + + Transform modelview = (p_camera_inverse_transform * light_transform).inverse(); + CameraMatrix bias; + bias.set_light_bias(); + CameraMatrix rectm; + rectm.set_light_atlas_rect(rect); + + CameraMatrix shadow_mtx = rectm * bias * light_instance_get_shadow_camera(li, 0) * modelview; + store_camera(shadow_mtx, light_data.shadow_matrix); + } + } + + light_instance_set_index(li, light_count); + + cluster_builder.add_light(type == RS::LIGHT_SPOT ? LightClusterBuilder::LIGHT_TYPE_SPOT : LightClusterBuilder::LIGHT_TYPE_OMNI, light_transform, radius, spot_angle); + + light_count++; + } break; + } + + light_instance_set_render_pass(li, render_pass); + + //update UBO for forward rendering, blit to texture for clustered + } + + if (light_count) { + RD::get_singleton()->buffer_update(scene_state.light_buffer, 0, sizeof(LightData) * light_count, scene_state.lights, true); + } + + if (scene_state.ubo.directional_light_count) { + RD::get_singleton()->buffer_update(scene_state.directional_light_buffer, 0, sizeof(DirectionalLightData) * scene_state.ubo.directional_light_count, scene_state.directional_lights, true); + } +} + +void RasterizerSceneHighEndRD::_render_scene(RID p_render_buffer, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID *p_light_cull_result, int p_light_cull_count, RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, RID *p_gi_probe_cull_result, int p_gi_probe_cull_count, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, const Color &p_default_bg_color) { + + RenderBufferDataHighEnd *render_buffer = NULL; + if (p_render_buffer.is_valid()) { + render_buffer = (RenderBufferDataHighEnd *)render_buffers_get_data(p_render_buffer); + } + + //first of all, make a new render pass + render_pass++; + + //fill up ubo +#if 0 + storage->info.render.object_count += p_cull_count; + + Environment *env = environment_owner.getornull(p_environment); + ShadowAtlas *shadow_atlas = shadow_atlas_owner.getornull(p_shadow_atlas); + ReflectionAtlas *reflection_atlas = reflection_atlas_owner.getornull(p_reflection_atlas); + + if (shadow_atlas && shadow_atlas->size) { + glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 5); + glBindTexture(GL_TEXTURE_2D, shadow_atlas->depth); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LESS); + scene_state.ubo.shadow_atlas_pixel_size[0] = 1.0 / shadow_atlas->size; + scene_state.ubo.shadow_atlas_pixel_size[1] = 1.0 / shadow_atlas->size; + } + + if (reflection_atlas && reflection_atlas->size) { + glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 3); + glBindTexture(GL_TEXTURE_2D, reflection_atlas->color); + } +#endif + + RENDER_TIMESTAMP("Setup 3D Scene"); + + if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_UNSHADED) { + p_light_cull_count = 0; + p_reflection_probe_cull_count = 0; + p_gi_probe_cull_count = 0; + } + + bool using_shadows = true; + + if (p_reflection_probe.is_valid()) { + scene_state.ubo.reflection_multiplier = 0.0; + if (!storage->reflection_probe_renders_shadows(reflection_probe_instance_get_probe(p_reflection_probe))) { + using_shadows = false; + } + } else { + scene_state.ubo.reflection_multiplier = 1.0; + } + + //scene_state.ubo.subsurface_scatter_width = subsurface_scatter_size; + + scene_state.ubo.shadow_z_offset = 0; + scene_state.ubo.shadow_z_slope_scale = 0; + + Vector2 vp_he = p_cam_projection.get_viewport_half_extents(); + scene_state.ubo.viewport_size[0] = vp_he.x; + scene_state.ubo.viewport_size[1] = vp_he.y; + + Size2 screen_pixel_size; + Size2i screen_size; + RID opaque_framebuffer; + RID depth_framebuffer; + RID alpha_framebuffer; + + PassMode depth_pass_mode = PASS_MODE_DEPTH; + Vector<Color> depth_pass_clear; + + if (render_buffer) { + screen_pixel_size.width = 1.0 / render_buffer->width; + screen_pixel_size.height = 1.0 / render_buffer->height; + screen_size.x = render_buffer->width; + screen_size.y = render_buffer->height; + + opaque_framebuffer = render_buffer->color_fb; + + if (p_environment.is_valid() && environment_is_ssr_enabled(p_environment)) { + depth_pass_mode = PASS_MODE_DEPTH_NORMAL_ROUGHNESS; + } else if (screen_space_roughness_limiter_is_active()) { + depth_pass_mode = PASS_MODE_DEPTH_NORMAL; + //we need to allocate both these, if not allocated + _allocate_normal_texture(render_buffer); + _allocate_roughness_texture(render_buffer); + } else if (p_environment.is_valid() && (environment_is_ssao_enabled(p_environment) || get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_NORMAL_BUFFER)) { + depth_pass_mode = PASS_MODE_DEPTH_NORMAL; + } + + switch (depth_pass_mode) { + case PASS_MODE_DEPTH: { + depth_framebuffer = render_buffer->depth_fb; + } break; + case PASS_MODE_DEPTH_NORMAL: { + _allocate_normal_texture(render_buffer); + depth_framebuffer = render_buffer->depth_normal_fb; + depth_pass_clear.push_back(Color(0.5, 0.5, 0.5, 0)); + } break; + case PASS_MODE_DEPTH_NORMAL_ROUGHNESS: { + _allocate_normal_texture(render_buffer); + _allocate_roughness_texture(render_buffer); + depth_framebuffer = render_buffer->depth_normal_roughness_fb; + depth_pass_clear.push_back(Color(0.5, 0.5, 0.5, 0)); + depth_pass_clear.push_back(Color()); + } break; + default: { + }; + } + + alpha_framebuffer = opaque_framebuffer; + + } else if (p_reflection_probe.is_valid()) { + uint32_t resolution = reflection_probe_instance_get_resolution(p_reflection_probe); + screen_pixel_size.width = 1.0 / resolution; + screen_pixel_size.height = 1.0 / resolution; + screen_size.x = resolution; + screen_size.y = resolution; + + opaque_framebuffer = reflection_probe_instance_get_framebuffer(p_reflection_probe, p_reflection_probe_pass); + depth_framebuffer = reflection_probe_instance_get_depth_framebuffer(p_reflection_probe, p_reflection_probe_pass); + alpha_framebuffer = opaque_framebuffer; + + if (storage->reflection_probe_is_interior(reflection_probe_instance_get_probe(p_reflection_probe))) { + p_environment = RID(); //no environment on interiors + } + + } else { + ERR_FAIL(); //bug? + } + + cluster_builder.begin(p_cam_transform.affine_inverse(), p_cam_projection); //prepare cluster + + _setup_lights(p_light_cull_result, p_light_cull_count, p_cam_transform.affine_inverse(), p_shadow_atlas, using_shadows); + _setup_reflections(p_reflection_probe_cull_result, p_reflection_probe_cull_count, p_cam_transform.affine_inverse(), p_environment); + _setup_gi_probes(p_gi_probe_cull_result, p_gi_probe_cull_count, p_cam_transform); + _setup_environment(p_environment, p_cam_projection, p_cam_transform, p_reflection_probe, p_reflection_probe.is_valid(), screen_pixel_size, p_shadow_atlas, !p_reflection_probe.is_valid(), p_default_bg_color, p_cam_projection.get_z_near(), p_cam_projection.get_z_far(), false); + + cluster_builder.bake_cluster(); //bake to cluster + + _update_render_base_uniform_set(); //may have changed due to the above (light buffer enlarged, as an example) + + render_list.clear(); + _fill_render_list(p_cull_result, p_cull_count, PASS_MODE_COLOR, render_buffer == nullptr); + + RID radiance_uniform_set; + bool draw_sky = false; + + Color clear_color; + bool keep_color = false; + + if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_OVERDRAW) { + clear_color = Color(0, 0, 0, 1); //in overdraw mode, BG should always be black + } else if (is_environment(p_environment)) { + RS::EnvironmentBG bg_mode = environment_get_background(p_environment); + float bg_energy = environment_get_bg_energy(p_environment); + switch (bg_mode) { + case RS::ENV_BG_CLEAR_COLOR: { + clear_color = p_default_bg_color; + clear_color.r *= bg_energy; + clear_color.g *= bg_energy; + clear_color.b *= bg_energy; + } break; + case RS::ENV_BG_COLOR: { + clear_color = environment_get_bg_color(p_environment); + clear_color.r *= bg_energy; + clear_color.g *= bg_energy; + clear_color.b *= bg_energy; + } break; + case RS::ENV_BG_SKY: { + RID sky = environment_get_sky(p_environment); + if (sky.is_valid()) { + + RENDER_TIMESTAMP("Setup Sky"); + CameraMatrix projection = p_cam_projection; + if (p_reflection_probe.is_valid()) { + CameraMatrix correction; + correction.set_depth_correction(true); + projection = correction * p_cam_projection; + } + + _setup_sky(p_environment, p_cam_transform.origin, screen_size); + _update_sky(p_environment, projection, p_cam_transform); + radiance_uniform_set = sky_get_radiance_uniform_set_rd(sky, default_shader_rd, RADIANCE_UNIFORM_SET); + + draw_sky = true; + } + } break; + case RS::ENV_BG_CANVAS: { + keep_color = true; + } break; + case RS::ENV_BG_KEEP: { + keep_color = true; + } break; + case RS::ENV_BG_CAMERA_FEED: { + + } break; + default: { + } + } + } else { + + clear_color = p_default_bg_color; + } + + _setup_view_dependant_uniform_set(p_shadow_atlas, p_reflection_atlas); + + render_list.sort_by_key(false); + + _fill_instances(render_list.elements, render_list.element_count, false); + + bool can_continue = true; //unless the middle buffers are needed + bool debug_giprobes = get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_GI_PROBE_ALBEDO || get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_GI_PROBE_LIGHTING || get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_GI_PROBE_EMISSION; + bool using_separate_specular = false; + + bool depth_pre_pass = depth_framebuffer.is_valid(); + RID render_buffers_uniform_set; + + if (depth_pre_pass) { //depth pre pass + RENDER_TIMESTAMP("Render Depth Pre-Pass"); + + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(depth_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_CONTINUE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_CONTINUE, depth_pass_clear); + _render_list(draw_list, RD::get_singleton()->framebuffer_get_format(depth_framebuffer), render_list.elements, render_list.element_count, false, depth_pass_mode, render_buffer == nullptr, radiance_uniform_set, RID()); + RD::get_singleton()->draw_list_end(); + } + + if (p_render_buffer.is_valid() && p_environment.is_valid() && environment_is_ssao_enabled(p_environment)) { + _process_ssao(p_render_buffer, p_environment, render_buffer->normal_buffer, p_cam_projection); + } + + if (p_render_buffer.is_valid() && screen_space_roughness_limiter_is_active()) { + storage->get_effects()->roughness_limit(render_buffer->normal_buffer, render_buffer->roughness_buffer, Size2(render_buffer->width, render_buffer->height), screen_space_roughness_limiter_get_curve()); + } + + if (p_render_buffer.is_valid()) { + //update the render buffers uniform set in case it changed + _update_render_buffers_uniform_set(p_render_buffer); + render_buffers_uniform_set = render_buffer->uniform_set; + } + + _setup_environment(p_environment, p_cam_projection, p_cam_transform, p_reflection_probe, p_reflection_probe.is_valid(), screen_pixel_size, p_shadow_atlas, !p_reflection_probe.is_valid(), p_default_bg_color, p_cam_projection.get_z_near(), p_cam_projection.get_z_far(), p_render_buffer.is_valid()); + + RENDER_TIMESTAMP("Render Opaque Pass"); + + { + bool will_continue = (can_continue || draw_sky || debug_giprobes); + //regular forward for now + Vector<Color> c; + c.push_back(clear_color.to_linear()); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(opaque_framebuffer, keep_color ? RD::INITIAL_ACTION_KEEP : RD::INITIAL_ACTION_CLEAR, will_continue ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, depth_pre_pass ? RD::INITIAL_ACTION_CONTINUE : RD::INITIAL_ACTION_CLEAR, will_continue ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, c, 1.0, 0); + _render_list(draw_list, RD::get_singleton()->framebuffer_get_format(opaque_framebuffer), render_list.elements, render_list.element_count, false, PASS_MODE_COLOR, render_buffer == nullptr, radiance_uniform_set, render_buffers_uniform_set); + RD::get_singleton()->draw_list_end(); + } + + if (debug_giprobes) { + //debug giprobes + bool will_continue = (can_continue || draw_sky); + CameraMatrix dc; + dc.set_depth_correction(true); + CameraMatrix cm = (dc * p_cam_projection) * CameraMatrix(p_cam_transform.affine_inverse()); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(opaque_framebuffer, RD::INITIAL_ACTION_CONTINUE, will_continue ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CONTINUE, will_continue ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ); + for (int i = 0; i < p_gi_probe_cull_count; i++) { + _debug_giprobe(p_gi_probe_cull_result[i], draw_list, opaque_framebuffer, cm, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_GI_PROBE_LIGHTING, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_GI_PROBE_EMISSION, 1.0); + } + RD::get_singleton()->draw_list_end(); + } + + if (draw_sky) { + RENDER_TIMESTAMP("Render Sky"); + + CameraMatrix projection = p_cam_projection; + if (p_reflection_probe.is_valid()) { + CameraMatrix correction; + correction.set_depth_correction(true); + projection = correction * p_cam_projection; + } + + _draw_sky(can_continue, opaque_framebuffer, p_environment, projection, p_cam_transform); + + if (using_separate_specular && !can_continue) { + //can't continue, so close the buffers + //RD::get_singleton()->draw_list_begin(render_buffer->color_specular_fb, RD::INITIAL_ACTION_CONTINUE, RD::FINAL_ACTION_READ_COLOR_AND_DEPTH, c); + //RD::get_singleton()->draw_list_end(); + } + } + + RENDER_TIMESTAMP("Render Transparent Pass"); + + _setup_environment(p_environment, p_cam_projection, p_cam_transform, p_reflection_probe, p_reflection_probe.is_valid(), screen_pixel_size, p_shadow_atlas, !p_reflection_probe.is_valid(), p_default_bg_color, p_cam_projection.get_z_near(), p_cam_projection.get_z_far(), false); + + render_list.sort_by_reverse_depth_and_priority(true); + + _fill_instances(&render_list.elements[render_list.max_elements - render_list.alpha_element_count], render_list.alpha_element_count, false); + + { + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(alpha_framebuffer, can_continue ? RD::INITIAL_ACTION_CONTINUE : RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, can_continue ? RD::INITIAL_ACTION_CONTINUE : RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ); + _render_list(draw_list, RD::get_singleton()->framebuffer_get_format(alpha_framebuffer), &render_list.elements[render_list.max_elements - render_list.alpha_element_count], render_list.alpha_element_count, false, PASS_MODE_COLOR, render_buffer == nullptr, radiance_uniform_set, render_buffers_uniform_set); + RD::get_singleton()->draw_list_end(); + } + + //_render_list +#if 0 + if (state.directional_light_count == 0) { + directional_light = NULL; + _render_list(&render_list.elements[render_list.max_elements - render_list.alpha_element_count], render_list.alpha_element_count, p_cam_transform, p_cam_projection, env_radiance_tex, false, true, false, false, shadow_atlas != NULL); + } else { + for (int i = 0; i < state.directional_light_count; i++) { + directional_light = directional_lights[i]; + _setup_directional_light(i, p_cam_transform.affine_inverse(), shadow_atlas != NULL && shadow_atlas->size > 0); + _render_list(&render_list.elements[render_list.max_elements - render_list.alpha_element_count], render_list.alpha_element_count, p_cam_transform, p_cam_projection, env_radiance_tex, false, true, false, i > 0, shadow_atlas != NULL); + } + } +#endif + +#if 0 + _post_process(env, p_cam_projection); + // Needed only for debugging + /* if (shadow_atlas && storage->frame.current_rt) { + + //_copy_texture_to_front_buffer(shadow_atlas->depth); + storage->canvas->canvas_begin(); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, shadow_atlas->depth); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE); + storage->canvas->draw_generic_textured_rect(Rect2(0, 0, storage->frame.current_rt->width / 2, storage->frame.current_rt->height / 2), Rect2(0, 0, 1, 1)); + } + + if (storage->frame.current_rt) { + + //_copy_texture_to_front_buffer(shadow_atlas->depth); + storage->canvas->canvas_begin(); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, exposure_shrink[4].color); + //glBindTexture(GL_TEXTURE_2D,storage->frame.current_rt->exposure.color); + storage->canvas->draw_generic_textured_rect(Rect2(0, 0, storage->frame.current_rt->width / 16, storage->frame.current_rt->height / 16), Rect2(0, 0, 1, 1)); + } + + if (reflection_atlas && storage->frame.current_rt) { + + //_copy_texture_to_front_buffer(shadow_atlas->depth); + storage->canvas->canvas_begin(); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, reflection_atlas->color); + storage->canvas->draw_generic_textured_rect(Rect2(0, 0, storage->frame.current_rt->width / 2, storage->frame.current_rt->height / 2), Rect2(0, 0, 1, 1)); + } + + if (directional_shadow.fbo) { + + //_copy_texture_to_front_buffer(shadow_atlas->depth); + storage->canvas->canvas_begin(); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, directional_shadow.depth); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE); + storage->canvas->draw_generic_textured_rect(Rect2(0, 0, storage->frame.current_rt->width / 2, storage->frame.current_rt->height / 2), Rect2(0, 0, 1, 1)); + } + + if ( env_radiance_tex) { + + //_copy_texture_to_front_buffer(shadow_atlas->depth); + storage->canvas->canvas_begin(); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, env_radiance_tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + storage->canvas->draw_generic_textured_rect(Rect2(0, 0, storage->frame.current_rt->width / 2, storage->frame.current_rt->height / 2), Rect2(0, 0, 1, 1)); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + }*/ + //disable all stuff +#endif +} +void RasterizerSceneHighEndRD::_render_shadow(RID p_framebuffer, InstanceBase **p_cull_result, int p_cull_count, const CameraMatrix &p_projection, const Transform &p_transform, float p_zfar, float p_bias, float p_normal_bias, bool p_use_dp, bool p_use_dp_flip) { + + RENDER_TIMESTAMP("Setup Rendering Shadow"); + + _update_render_base_uniform_set(); + + render_pass++; + + scene_state.ubo.shadow_z_offset = p_bias; + scene_state.ubo.shadow_z_slope_scale = p_normal_bias; + scene_state.ubo.dual_paraboloid_side = p_use_dp_flip ? -1 : 1; + + _setup_environment(RID(), p_projection, p_transform, RID(), true, Vector2(1, 1), RID(), true, Color(), 0, p_zfar); + + render_list.clear(); + + PassMode pass_mode = p_use_dp ? PASS_MODE_SHADOW_DP : PASS_MODE_SHADOW; + + _fill_render_list(p_cull_result, p_cull_count, pass_mode, true); + + _setup_view_dependant_uniform_set(RID(), RID()); + + RENDER_TIMESTAMP("Render Shadow"); + + render_list.sort_by_key(false); + + _fill_instances(render_list.elements, render_list.element_count, true); + + { + //regular forward for now + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ); + _render_list(draw_list, RD::get_singleton()->framebuffer_get_format(p_framebuffer), render_list.elements, render_list.element_count, p_use_dp_flip, pass_mode, true, RID(), RID()); + RD::get_singleton()->draw_list_end(); + } +} + +void RasterizerSceneHighEndRD::_render_material(const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID p_framebuffer, const Rect2i &p_region) { + RENDER_TIMESTAMP("Setup Rendering Shadow"); + + _update_render_base_uniform_set(); + + render_pass++; + + scene_state.ubo.shadow_z_offset = 0; + scene_state.ubo.shadow_z_slope_scale = 0; + scene_state.ubo.dual_paraboloid_side = 0; + + _setup_environment(RID(), p_cam_projection, p_cam_transform, RID(), true, Vector2(1, 1), RID(), false, Color(), 0, 0); + + render_list.clear(); + + PassMode pass_mode = PASS_MODE_DEPTH_MATERIAL; + _fill_render_list(p_cull_result, p_cull_count, pass_mode, true); + + _setup_view_dependant_uniform_set(RID(), RID()); + + RENDER_TIMESTAMP("Render Material"); + + render_list.sort_by_key(false); + + _fill_instances(render_list.elements, render_list.element_count, true); + + { + //regular forward for now + Vector<Color> clear; + clear.push_back(Color(0, 0, 0, 0)); + clear.push_back(Color(0, 0, 0, 0)); + clear.push_back(Color(0, 0, 0, 0)); + clear.push_back(Color(0, 0, 0, 0)); + clear.push_back(Color(0, 0, 0, 0)); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, clear, 1.0, 0, p_region); + _render_list(draw_list, RD::get_singleton()->framebuffer_get_format(p_framebuffer), render_list.elements, render_list.element_count, true, pass_mode, true, RID(), RID()); + RD::get_singleton()->draw_list_end(); + } +} + +void RasterizerSceneHighEndRD::_base_uniforms_changed() { + + if (!render_base_uniform_set.is_null() && RD::get_singleton()->uniform_set_is_valid(render_base_uniform_set)) { + RD::get_singleton()->free(render_base_uniform_set); + } + render_base_uniform_set = RID(); +} + +void RasterizerSceneHighEndRD::_update_render_base_uniform_set() { + + if (render_base_uniform_set.is_null() || !RD::get_singleton()->uniform_set_is_valid(render_base_uniform_set)) { + + if (render_base_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(render_base_uniform_set)) { + RD::get_singleton()->free(render_base_uniform_set); + } + + Vector<RD::Uniform> uniforms; + + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_SAMPLER; + u.binding = 1; + u.ids.resize(12); + RID *ids_ptr = u.ids.ptrw(); + ids_ptr[0] = storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + ids_ptr[1] = storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + ids_ptr[2] = storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + ids_ptr[3] = storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + ids_ptr[4] = storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + ids_ptr[5] = storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + ids_ptr[6] = storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED); + ids_ptr[7] = storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED); + ids_ptr[8] = storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED); + ids_ptr[9] = storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED); + ids_ptr[10] = storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED); + ids_ptr[11] = storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED); + uniforms.push_back(u); + } + + { + RD::Uniform u; + u.binding = 2; + u.type = RD::UNIFORM_TYPE_SAMPLER; + u.ids.push_back(shadow_sampler); + uniforms.push_back(u); + } + + { + RD::Uniform u; + u.binding = 3; + u.type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; + u.ids.push_back(scene_state.uniform_buffer); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.binding = 4; + u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER; + u.ids.push_back(scene_state.instance_buffer); + uniforms.push_back(u); + } + + { + RD::Uniform u; + u.binding = 5; + u.type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; + u.ids.push_back(scene_state.light_buffer); + uniforms.push_back(u); + } + + { + RD::Uniform u; + u.binding = 6; + u.type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; + u.ids.push_back(scene_state.reflection_buffer); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.binding = 7; + u.type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; + u.ids.push_back(scene_state.directional_light_buffer); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.binding = 8; + u.type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; + u.ids.push_back(scene_state.gi_probe_buffer); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.binding = 9; + u.type = RD::UNIFORM_TYPE_TEXTURE; + int slot_count = gi_probe_get_slots().size(); + if (gi_probe_is_anisotropic()) { + u.ids.resize(slot_count * 3); + } else { + u.ids.resize(slot_count); + } + + for (int i = 0; i < slot_count; i++) { + + RID probe = gi_probe_get_slots()[i]; + + if (gi_probe_is_anisotropic()) { + if (probe.is_null()) { + RID empty_tex = storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE); + u.ids.write[i * 3 + 0] = empty_tex; + u.ids.write[i * 3 + 1] = empty_tex; + u.ids.write[i * 3 + 2] = empty_tex; + } else { + u.ids.write[i * 3 + 0] = gi_probe_instance_get_texture(probe); + u.ids.write[i * 3 + 1] = gi_probe_instance_get_aniso_texture(probe, 0); + u.ids.write[i * 3 + 2] = gi_probe_instance_get_aniso_texture(probe, 1); + } + } else { + if (probe.is_null()) { + u.ids.write[i] = storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE); + } else { + u.ids.write[i] = gi_probe_instance_get_texture(probe); + } + } + } + + uniforms.push_back(u); + } + + { + RD::Uniform u; + u.binding = 10; + u.type = RD::UNIFORM_TYPE_TEXTURE; + u.ids.push_back(cluster_builder.get_cluster_texture()); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.binding = 11; + u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER; + u.ids.push_back(cluster_builder.get_cluster_indices_buffer()); + uniforms.push_back(u); + } + + { + RD::Uniform u; + u.binding = 12; + u.type = RD::UNIFORM_TYPE_TEXTURE; + if (directional_shadow_get_texture().is_valid()) { + u.ids.push_back(directional_shadow_get_texture()); + } else { + u.ids.push_back(storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_WHITE)); + } + uniforms.push_back(u); + } + + render_base_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, default_shader_rd, SCENE_UNIFORM_SET); + } +} + +void RasterizerSceneHighEndRD::_setup_view_dependant_uniform_set(RID p_shadow_atlas, RID p_reflection_atlas) { + + if (view_dependant_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(view_dependant_uniform_set)) { + RD::get_singleton()->free(view_dependant_uniform_set); + } + + //default render buffer and scene state uniform set + + Vector<RD::Uniform> uniforms; + + { + + RID ref_texture = p_reflection_atlas.is_valid() ? reflection_atlas_get_texture(p_reflection_atlas) : RID(); + RD::Uniform u; + u.binding = 0; + u.type = RD::UNIFORM_TYPE_TEXTURE; + if (ref_texture.is_valid()) { + u.ids.push_back(ref_texture); + } else { + u.ids.push_back(storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_CUBEMAP_ARRAY_BLACK)); + } + uniforms.push_back(u); + } + + { + RD::Uniform u; + u.binding = 1; + u.type = RD::UNIFORM_TYPE_TEXTURE; + RID texture; + if (p_shadow_atlas.is_valid()) { + texture = shadow_atlas_get_texture(p_shadow_atlas); + } + if (!texture.is_valid()) { + texture = storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_WHITE); + } + u.ids.push_back(texture); + uniforms.push_back(u); + } + + view_dependant_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, default_shader_rd, VIEW_DEPENDANT_UNIFORM_SET); +} + +void RasterizerSceneHighEndRD::_render_buffers_clear_uniform_set(RenderBufferDataHighEnd *rb) { + + if (!rb->uniform_set.is_null() && RD::get_singleton()->uniform_set_is_valid(rb->uniform_set)) { + RD::get_singleton()->free(rb->uniform_set); + } + rb->uniform_set = RID(); +} + +void RasterizerSceneHighEndRD::_render_buffers_uniform_set_changed(RID p_render_buffers) { + + RenderBufferDataHighEnd *rb = (RenderBufferDataHighEnd *)render_buffers_get_data(p_render_buffers); + + _render_buffers_clear_uniform_set(rb); +} + +RID RasterizerSceneHighEndRD::_render_buffers_get_roughness_texture(RID p_render_buffers) { + RenderBufferDataHighEnd *rb = (RenderBufferDataHighEnd *)render_buffers_get_data(p_render_buffers); + + return rb->roughness_buffer; +} + +RID RasterizerSceneHighEndRD::_render_buffers_get_normal_texture(RID p_render_buffers) { + RenderBufferDataHighEnd *rb = (RenderBufferDataHighEnd *)render_buffers_get_data(p_render_buffers); + + return rb->normal_buffer; +} + +void RasterizerSceneHighEndRD::_update_render_buffers_uniform_set(RID p_render_buffers) { + + RenderBufferDataHighEnd *rb = (RenderBufferDataHighEnd *)render_buffers_get_data(p_render_buffers); + + if (rb->uniform_set.is_null() || !RD::get_singleton()->uniform_set_is_valid(rb->uniform_set)) { + + Vector<RD::Uniform> uniforms; + { + RD::Uniform u; + u.binding = 0; + u.type = RD::UNIFORM_TYPE_TEXTURE; + RID texture = false && rb->depth.is_valid() ? rb->depth : storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_WHITE); + u.ids.push_back(texture); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.binding = 1; + u.type = RD::UNIFORM_TYPE_TEXTURE; + RID bbt = render_buffers_get_back_buffer_texture(p_render_buffers); + RID texture = bbt.is_valid() ? bbt : storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_BLACK); + u.ids.push_back(texture); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.binding = 2; + u.type = RD::UNIFORM_TYPE_TEXTURE; + RID texture = rb->normal_buffer.is_valid() ? rb->normal_buffer : storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_NORMAL); + u.ids.push_back(texture); + uniforms.push_back(u); + } + + { + RD::Uniform u; + u.binding = 3; + u.type = RD::UNIFORM_TYPE_TEXTURE; + RID texture = rb->roughness_buffer.is_valid() ? rb->roughness_buffer : storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_BLACK); + u.ids.push_back(texture); + uniforms.push_back(u); + } + + { + RD::Uniform u; + u.binding = 4; + u.type = RD::UNIFORM_TYPE_TEXTURE; + RID aot = render_buffers_get_ao_texture(p_render_buffers); + RID texture = aot.is_valid() ? aot : storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_BLACK); + u.ids.push_back(texture); + uniforms.push_back(u); + } + + rb->uniform_set = RD::get_singleton()->uniform_set_create(uniforms, default_shader_rd, RENDER_BUFFERS_UNIFORM_SET); + } +} + +RasterizerSceneHighEndRD *RasterizerSceneHighEndRD::singleton = NULL; + +void RasterizerSceneHighEndRD::set_time(double p_time, double p_step) { + time = p_time; + RasterizerSceneRD::set_time(p_time, p_step); +} + +RasterizerSceneHighEndRD::RasterizerSceneHighEndRD(RasterizerStorageRD *p_storage) : + RasterizerSceneRD(p_storage) { + singleton = this; + storage = p_storage; + + /* SCENE SHADER */ + + { + String defines; + defines += "\n#define MAX_ROUGHNESS_LOD " + itos(get_roughness_layers() - 1) + ".0\n"; + if (is_using_radiance_cubemap_array()) { + defines += "\n#define USE_RADIANCE_CUBEMAP_ARRAY \n"; + } + + uint32_t uniform_max_size = RD::get_singleton()->limit_get(RD::LIMIT_MAX_UNIFORM_BUFFER_SIZE); + + { //reflections + uint32_t reflection_buffer_size; + if (uniform_max_size < 65536) { + //Yes, you guessed right, ARM again + reflection_buffer_size = uniform_max_size; + } else { + reflection_buffer_size = 65536; + } + + scene_state.max_reflections = reflection_buffer_size / sizeof(ReflectionData); + scene_state.reflections = memnew_arr(ReflectionData, scene_state.max_reflections); + scene_state.reflection_buffer = RD::get_singleton()->uniform_buffer_create(reflection_buffer_size); + defines += "\n#define MAX_REFLECTION_DATA_STRUCTS " + itos(scene_state.max_reflections) + "\n"; + } + + { //lights + scene_state.max_lights = MIN(65536, uniform_max_size) / sizeof(LightData); + uint32_t light_buffer_size = scene_state.max_lights * sizeof(LightData); + scene_state.lights = memnew_arr(LightData, scene_state.max_lights); + scene_state.light_buffer = RD::get_singleton()->uniform_buffer_create(light_buffer_size); + defines += "\n#define MAX_LIGHT_DATA_STRUCTS " + itos(scene_state.max_lights) + "\n"; + + scene_state.max_directional_lights = 8; + uint32_t directional_light_buffer_size = scene_state.max_directional_lights * sizeof(DirectionalLightData); + scene_state.directional_lights = memnew_arr(DirectionalLightData, scene_state.max_directional_lights); + scene_state.directional_light_buffer = RD::get_singleton()->uniform_buffer_create(directional_light_buffer_size); + defines += "\n#define MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS " + itos(scene_state.max_directional_lights) + "\n"; + } + + { //giprobes + int slot_count = gi_probe_get_slots().size(); + if (gi_probe_is_anisotropic()) { + slot_count *= 3; + defines += "\n#define GI_PROBE_USE_ANISOTROPY\n"; + } + + if (gi_probe_get_quality() == GIPROBE_QUALITY_ULTRA_LOW) { + defines += "\n#define GI_PROBE_LOW_QUALITY\n"; + } else if (gi_probe_get_quality() == GIPROBE_QUALITY_HIGH) { + defines += "\n#define GI_PROBE_HIGH_QUALITY\n"; + } + + defines += "\n#define MAX_GI_PROBE_TEXTURES " + itos(slot_count) + "\n"; + + uint32_t giprobe_buffer_size; + if (uniform_max_size < 65536) { + //Yes, you guessed right, ARM again + giprobe_buffer_size = uniform_max_size; + } else { + giprobe_buffer_size = 65536; + } + + giprobe_buffer_size = MIN(sizeof(GIProbeData) * gi_probe_get_slots().size(), giprobe_buffer_size); + scene_state.max_gi_probes = giprobe_buffer_size / sizeof(GIProbeData); + scene_state.gi_probes = memnew_arr(GIProbeData, scene_state.max_gi_probes); + scene_state.gi_probe_buffer = RD::get_singleton()->uniform_buffer_create(sizeof(GIProbeData) * scene_state.max_gi_probes); + defines += "\n#define MAX_GI_PROBES " + itos(scene_state.max_gi_probes) + "\n"; + } + + Vector<String> shader_versions; + shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n"); + shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_DUAL_PARABOLOID\n"); + shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_NORMAL\n"); + shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_NORMAL\n#define MODE_RENDER_ROUGHNESS\n"); + shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_MATERIAL\n"); + shader_versions.push_back(""); + shader_versions.push_back("\n#define MODE_MULTIPLE_RENDER_TARGETS\n"); + shader_versions.push_back("\n#define USE_VOXEL_CONE_TRACING\n"); + shader_versions.push_back("\n#define MODE_MULTIPLE_RENDER_TARGETS\n#define USE_VOXEL_CONE_TRACING\n"); + shader_versions.push_back("\n#define USE_LIGHTMAP\n"); + shader_versions.push_back("\n#define MODE_MULTIPLE_RENDER_TARGETS\n#define USE_LIGHTMAP\n"); + shader.scene_shader.initialize(shader_versions, defines); + } + + storage->shader_set_data_request_function(RasterizerStorageRD::SHADER_TYPE_3D, _create_shader_funcs); + storage->material_set_data_request_function(RasterizerStorageRD::SHADER_TYPE_3D, _create_material_funcs); + + { + //shader compiler + ShaderCompilerRD::DefaultIdentifierActions actions; + + actions.renames["WORLD_MATRIX"] = "world_matrix"; + actions.renames["WORLD_NORMAL_MATRIX"] = "world_normal_matrix"; + actions.renames["INV_CAMERA_MATRIX"] = "scene_data.inv_camera_matrix"; + actions.renames["CAMERA_MATRIX"] = "scene_data.camera_matrix"; + actions.renames["PROJECTION_MATRIX"] = "projection_matrix"; + actions.renames["INV_PROJECTION_MATRIX"] = "scene_data.inv_projection_matrix"; + actions.renames["MODELVIEW_MATRIX"] = "modelview"; + actions.renames["MODELVIEW_NORMAL_MATRIX"] = "modelview_normal"; + + actions.renames["VERTEX"] = "vertex"; + actions.renames["NORMAL"] = "normal"; + actions.renames["TANGENT"] = "tangent"; + actions.renames["BINORMAL"] = "binormal"; + actions.renames["POSITION"] = "position"; + actions.renames["UV"] = "uv_interp"; + actions.renames["UV2"] = "uv2_interp"; + actions.renames["COLOR"] = "color_interp"; + actions.renames["POINT_SIZE"] = "gl_PointSize"; + actions.renames["INSTANCE_ID"] = "gl_InstanceIndex"; + + //builtins + + actions.renames["TIME"] = "scene_data.time"; + actions.renames["VIEWPORT_SIZE"] = "scene_data.viewport_size"; + + actions.renames["FRAGCOORD"] = "gl_FragCoord"; + actions.renames["FRONT_FACING"] = "gl_FrontFacing"; + actions.renames["NORMALMAP"] = "normalmap"; + actions.renames["NORMALMAP_DEPTH"] = "normaldepth"; + actions.renames["ALBEDO"] = "albedo"; + actions.renames["ALPHA"] = "alpha"; + actions.renames["METALLIC"] = "metallic"; + actions.renames["SPECULAR"] = "specular"; + actions.renames["ROUGHNESS"] = "roughness"; + actions.renames["RIM"] = "rim"; + actions.renames["RIM_TINT"] = "rim_tint"; + actions.renames["CLEARCOAT"] = "clearcoat"; + actions.renames["CLEARCOAT_GLOSS"] = "clearcoat_gloss"; + actions.renames["ANISOTROPY"] = "anisotropy"; + actions.renames["ANISOTROPY_FLOW"] = "anisotropy_flow"; + actions.renames["SSS_STRENGTH"] = "sss_strength"; + actions.renames["TRANSMISSION"] = "transmission"; + actions.renames["AO"] = "ao"; + actions.renames["AO_LIGHT_AFFECT"] = "ao_light_affect"; + actions.renames["EMISSION"] = "emission"; + actions.renames["POINT_COORD"] = "gl_PointCoord"; + actions.renames["INSTANCE_CUSTOM"] = "instance_custom"; + actions.renames["SCREEN_UV"] = "screen_uv"; + actions.renames["SCREEN_TEXTURE"] = "color_buffer"; + actions.renames["DEPTH_TEXTURE"] = "depth_buffer"; + actions.renames["NORMAL_TEXTURE"] = "normal_buffer"; + actions.renames["DEPTH"] = "gl_FragDepth"; + actions.renames["OUTPUT_IS_SRGB"] = "true"; + + //for light + actions.renames["VIEW"] = "view"; + actions.renames["LIGHT_COLOR"] = "light_color"; + actions.renames["LIGHT"] = "light"; + actions.renames["ATTENUATION"] = "attenuation"; + actions.renames["DIFFUSE_LIGHT"] = "diffuse_light"; + actions.renames["SPECULAR_LIGHT"] = "specular_light"; + + actions.usage_defines["TANGENT"] = "#define TANGENT_USED\n"; + actions.usage_defines["BINORMAL"] = "@TANGENT"; + actions.usage_defines["RIM"] = "#define LIGHT_RIM_USED\n"; + actions.usage_defines["RIM_TINT"] = "@RIM"; + actions.usage_defines["CLEARCOAT"] = "#define LIGHT_CLEARCOAT_USED\n"; + actions.usage_defines["CLEARCOAT_GLOSS"] = "@CLEARCOAT"; + actions.usage_defines["ANISOTROPY"] = "#define LIGHT_ANISOTROPY_USED\n"; + actions.usage_defines["ANISOTROPY_FLOW"] = "@ANISOTROPY"; + actions.usage_defines["AO"] = "#define AO_USED\n"; + actions.usage_defines["AO_LIGHT_AFFECT"] = "#define AO_USED\n"; + actions.usage_defines["UV"] = "#define UV_USED\n"; + actions.usage_defines["UV2"] = "#define UV2_USED\n"; + actions.usage_defines["NORMALMAP"] = "#define NORMALMAP_USED\n"; + actions.usage_defines["NORMALMAP_DEPTH"] = "@NORMALMAP"; + actions.usage_defines["COLOR"] = "#define COLOR_USED\n"; + actions.usage_defines["INSTANCE_CUSTOM"] = "#define ENABLE_INSTANCE_CUSTOM\n"; + actions.usage_defines["POSITION"] = "#define OVERRIDE_POSITION\n"; + + actions.usage_defines["SSS_STRENGTH"] = "#define ENABLE_SSS\n"; + actions.usage_defines["TRANSMISSION"] = "#define LIGHT_TRANSMISSION_USED\n"; + actions.usage_defines["SCREEN_TEXTURE"] = "#define SCREEN_TEXTURE_USED\n"; + actions.usage_defines["SCREEN_UV"] = "#define SCREEN_UV_USED\n"; + + actions.usage_defines["DIFFUSE_LIGHT"] = "#define USE_LIGHT_SHADER_CODE\n"; + actions.usage_defines["SPECULAR_LIGHT"] = "#define USE_LIGHT_SHADER_CODE\n"; + + actions.render_mode_defines["skip_vertex_transform"] = "#define SKIP_TRANSFORM_USED\n"; + actions.render_mode_defines["world_vertex_coords"] = "#define VERTEX_WORLD_COORDS_USED\n"; + actions.render_mode_defines["ensure_correct_normals"] = "#define ENSURE_CORRECT_NORMALS\n"; + actions.render_mode_defines["cull_front"] = "#define DO_SIDE_CHECK\n"; + actions.render_mode_defines["cull_disabled"] = "#define DO_SIDE_CHECK\n"; + + bool force_lambert = GLOBAL_GET("rendering/quality/shading/force_lambert_over_burley"); + + if (!force_lambert) { + actions.render_mode_defines["diffuse_burley"] = "#define DIFFUSE_BURLEY\n"; + } + + actions.render_mode_defines["diffuse_oren_nayar"] = "#define DIFFUSE_OREN_NAYAR\n"; + actions.render_mode_defines["diffuse_lambert_wrap"] = "#define DIFFUSE_LAMBERT_WRAP\n"; + actions.render_mode_defines["diffuse_toon"] = "#define DIFFUSE_TOON\n"; + + bool force_blinn = GLOBAL_GET("rendering/quality/shading/force_blinn_over_ggx"); + + if (!force_blinn) { + actions.render_mode_defines["specular_schlick_ggx"] = "#define SPECULAR_SCHLICK_GGX\n"; + } else { + actions.render_mode_defines["specular_schlick_ggx"] = "#define SPECULAR_BLINN\n"; + } + + actions.render_mode_defines["specular_blinn"] = "#define SPECULAR_BLINN\n"; + actions.render_mode_defines["specular_phong"] = "#define SPECULAR_PHONG\n"; + actions.render_mode_defines["specular_toon"] = "#define SPECULAR_TOON\n"; + actions.render_mode_defines["specular_disabled"] = "#define SPECULAR_DISABLED\n"; + actions.render_mode_defines["shadows_disabled"] = "#define SHADOWS_DISABLED\n"; + actions.render_mode_defines["ambient_light_disabled"] = "#define AMBIENT_LIGHT_DISABLED\n"; + actions.render_mode_defines["shadow_to_opacity"] = "#define USE_SHADOW_TO_OPACITY\n"; + actions.render_mode_defines["unshaded"] = "#define MODE_UNSHADED\n"; + + actions.sampler_array_name = "material_samplers"; + actions.base_texture_binding_index = 1; + actions.texture_layout_set = MATERIAL_UNIFORM_SET; + actions.base_uniform_string = "material."; + actions.base_varying_index = 10; + + actions.default_filter = ShaderLanguage::FILTER_LINEAR_MIPMAP; + actions.default_repeat = ShaderLanguage::REPEAT_ENABLE; + + shader.compiler.initialize(actions); + } + + //render list + render_list.max_elements = GLOBAL_DEF_RST("rendering/limits/rendering/max_renderable_elements", (int)128000); + render_list.init(); + render_pass = 0; + + { + + scene_state.max_instances = render_list.max_elements; + scene_state.instances = memnew_arr(InstanceData, scene_state.max_instances); + scene_state.instance_buffer = RD::get_singleton()->storage_buffer_create(sizeof(InstanceData) * scene_state.max_instances); + } + + scene_state.uniform_buffer = RD::get_singleton()->uniform_buffer_create(sizeof(SceneState::UBO)); + + { + //default material and shader + default_shader = storage->shader_create(); + storage->shader_set_code(default_shader, "shader_type spatial; void vertex() { ROUGHNESS = 0.8; } void fragment() { ALBEDO=vec3(0.6); ROUGHNESS=0.8; METALLIC=0.2; } \n"); + default_material = storage->material_create(); + storage->material_set_shader(default_material, default_shader); + + MaterialData *md = (MaterialData *)storage->material_get_data(default_material, RasterizerStorageRD::SHADER_TYPE_3D); + default_shader_rd = shader.scene_shader.version_get_shader(md->shader_data->version, SHADER_VERSION_COLOR_PASS); + } + + { + + overdraw_material_shader = storage->shader_create(); + storage->shader_set_code(overdraw_material_shader, "shader_type spatial;\nrender_mode blend_add,unshaded;\n void fragment() { ALBEDO=vec3(0.4,0.8,0.8); ALPHA=0.2; }"); + overdraw_material = storage->material_create(); + storage->material_set_shader(overdraw_material, overdraw_material_shader); + + wireframe_material_shader = storage->shader_create(); + storage->shader_set_code(wireframe_material_shader, "shader_type spatial;\nrender_mode wireframe,unshaded;\n void fragment() { ALBEDO=vec3(0.0,0.0,0.0); }"); + wireframe_material = storage->material_create(); + storage->material_set_shader(wireframe_material, wireframe_material_shader); + } + + { + default_vec4_xform_buffer = RD::get_singleton()->storage_buffer_create(256); + Vector<RD::Uniform> uniforms; + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER; + u.ids.push_back(default_vec4_xform_buffer); + u.binding = 0; + uniforms.push_back(u); + + default_vec4_xform_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, default_shader_rd, TRANSFORMS_UNIFORM_SET); + } + { + + RD::SamplerState sampler; + sampler.mag_filter = RD::SAMPLER_FILTER_LINEAR; + sampler.min_filter = RD::SAMPLER_FILTER_LINEAR; + sampler.enable_compare = true; + sampler.compare_op = RD::COMPARE_OP_LESS; + shadow_sampler = RD::get_singleton()->sampler_create(sampler); + } + + { + Vector<RD::Uniform> uniforms; + + RD::Uniform u; + u.binding = 0; + u.type = RD::UNIFORM_TYPE_TEXTURE; + RID texture = storage->texture_rd_get_default(is_using_radiance_cubemap_array() ? RasterizerStorageRD::DEFAULT_RD_TEXTURE_CUBEMAP_ARRAY_BLACK : RasterizerStorageRD::DEFAULT_RD_TEXTURE_CUBEMAP_BLACK); + u.ids.push_back(texture); + uniforms.push_back(u); + + default_radiance_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, default_shader_rd, RADIANCE_UNIFORM_SET); + } + + { //render buffers + Vector<RD::Uniform> uniforms; + for (int i = 0; i < 5; i++) { + RD::Uniform u; + u.binding = i; + u.type = RD::UNIFORM_TYPE_TEXTURE; + RID texture = storage->texture_rd_get_default(i == 0 ? RasterizerStorageRD::DEFAULT_RD_TEXTURE_WHITE : (i == 2 ? RasterizerStorageRD::DEFAULT_RD_TEXTURE_NORMAL : RasterizerStorageRD::DEFAULT_RD_TEXTURE_BLACK)); + u.ids.push_back(texture); + uniforms.push_back(u); + } + + default_render_buffers_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, default_shader_rd, RENDER_BUFFERS_UNIFORM_SET); + } + + cluster_builder.setup(16, 8, 24); +} + +RasterizerSceneHighEndRD::~RasterizerSceneHighEndRD() { + directional_shadow_atlas_set_size(0); + + //clear base uniform set if still valid + if (view_dependant_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(view_dependant_uniform_set)) { + RD::get_singleton()->free(view_dependant_uniform_set); + } + + RD::get_singleton()->free(default_render_buffers_uniform_set); + RD::get_singleton()->free(default_radiance_uniform_set); + RD::get_singleton()->free(default_vec4_xform_buffer); + RD::get_singleton()->free(shadow_sampler); + + storage->free(wireframe_material_shader); + storage->free(overdraw_material_shader); + storage->free(default_shader); + + storage->free(wireframe_material); + storage->free(overdraw_material); + storage->free(default_material); + + { + RD::get_singleton()->free(scene_state.uniform_buffer); + RD::get_singleton()->free(scene_state.instance_buffer); + RD::get_singleton()->free(scene_state.gi_probe_buffer); + RD::get_singleton()->free(scene_state.directional_light_buffer); + RD::get_singleton()->free(scene_state.light_buffer); + RD::get_singleton()->free(scene_state.reflection_buffer); + memdelete_arr(scene_state.instances); + memdelete_arr(scene_state.gi_probes); + memdelete_arr(scene_state.directional_lights); + memdelete_arr(scene_state.lights); + memdelete_arr(scene_state.reflections); + } +} diff --git a/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.h b/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.h new file mode 100644 index 0000000000..4c3422cedb --- /dev/null +++ b/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.h @@ -0,0 +1,585 @@ +/*************************************************************************/ +/* rasterizer_scene_high_end_rd.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef RASTERIZER_SCENE_HIGHEND_RD_H +#define RASTERIZER_SCENE_HIGHEND_RD_H + +#include "servers/rendering/rasterizer_rd/light_cluster_builder.h" +#include "servers/rendering/rasterizer_rd/rasterizer_scene_rd.h" +#include "servers/rendering/rasterizer_rd/rasterizer_storage_rd.h" +#include "servers/rendering/rasterizer_rd/render_pipeline_vertex_format_cache_rd.h" +#include "servers/rendering/rasterizer_rd/shaders/scene_high_end.glsl.gen.h" + +class RasterizerSceneHighEndRD : public RasterizerSceneRD { + + enum { + SCENE_UNIFORM_SET = 0, + RADIANCE_UNIFORM_SET = 1, + VIEW_DEPENDANT_UNIFORM_SET = 2, + RENDER_BUFFERS_UNIFORM_SET = 3, + TRANSFORMS_UNIFORM_SET = 4, + MATERIAL_UNIFORM_SET = 5 + }; + + /* Scene Shader */ + + enum ShaderVersion { + SHADER_VERSION_DEPTH_PASS, + SHADER_VERSION_DEPTH_PASS_DP, + SHADER_VERSION_DEPTH_PASS_WITH_NORMAL, + SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS, + SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL, + SHADER_VERSION_COLOR_PASS, + SHADER_VERSION_COLOR_PASS_WITH_SEPARATE_SPECULAR, + SHADER_VERSION_VCT_COLOR_PASS, + SHADER_VERSION_VCT_COLOR_PASS_WITH_SEPARATE_SPECULAR, + SHADER_VERSION_LIGHTMAP_COLOR_PASS, + SHADER_VERSION_LIGHTMAP_COLOR_PASS_WITH_SEPARATE_SPECULAR, + SHADER_VERSION_MAX + }; + + struct { + SceneHighEndShaderRD scene_shader; + ShaderCompilerRD compiler; + } shader; + + RasterizerStorageRD *storage; + + /* Material */ + + struct ShaderData : public RasterizerStorageRD::ShaderData { + + enum BlendMode { //used internally + BLEND_MODE_MIX, + BLEND_MODE_ADD, + BLEND_MODE_SUB, + BLEND_MODE_MUL, + }; + + enum DepthDraw { + DEPTH_DRAW_DISABLED, + DEPTH_DRAW_OPAQUE, + DEPTH_DRAW_ALWAYS + }; + + enum DepthTest { + DEPTH_TEST_DISABLED, + DEPTH_TEST_ENABLED + }; + + enum Cull { + CULL_DISABLED, + CULL_FRONT, + CULL_BACK + }; + + enum CullVariant { + CULL_VARIANT_NORMAL, + CULL_VARIANT_REVERSED, + CULL_VARIANT_DOUBLE_SIDED, + CULL_VARIANT_MAX + + }; + + bool valid; + RID version; + uint32_t vertex_input_mask; + RenderPipelineVertexFormatCacheRD pipelines[CULL_VARIANT_MAX][RS::PRIMITIVE_MAX][SHADER_VERSION_MAX]; + + String path; + + Map<StringName, ShaderLanguage::ShaderNode::Uniform> uniforms; + Vector<ShaderCompilerRD::GeneratedCode::Texture> texture_uniforms; + + Vector<uint32_t> ubo_offsets; + uint32_t ubo_size; + + String code; + Map<StringName, RID> default_texture_params; + + DepthDraw depth_draw; + DepthTest depth_test; + + bool uses_point_size; + bool uses_alpha; + bool uses_blend_alpha; + bool uses_depth_pre_pass; + bool uses_discard; + bool uses_roughness; + bool uses_normal; + + bool unshaded; + bool uses_vertex; + bool uses_sss; + bool uses_screen_texture; + bool uses_depth_texture; + bool uses_normal_texture; + bool uses_time; + bool writes_modelview_or_projection; + bool uses_world_coordinates; + + uint64_t last_pass = 0; + uint32_t index = 0; + + virtual void set_code(const String &p_Code); + virtual void set_default_texture_param(const StringName &p_name, RID p_texture); + virtual void get_param_list(List<PropertyInfo> *p_param_list) const; + virtual bool is_param_texture(const StringName &p_param) const; + virtual bool is_animated() const; + virtual bool casts_shadows() const; + virtual Variant get_default_parameter(const StringName &p_parameter) const; + ShaderData(); + virtual ~ShaderData(); + }; + + RasterizerStorageRD::ShaderData *_create_shader_func(); + static RasterizerStorageRD::ShaderData *_create_shader_funcs() { + return static_cast<RasterizerSceneHighEndRD *>(singleton)->_create_shader_func(); + } + + struct MaterialData : public RasterizerStorageRD::MaterialData { + uint64_t last_frame; + ShaderData *shader_data; + RID uniform_buffer; + RID uniform_set; + Vector<RID> texture_cache; + Vector<uint8_t> ubo_data; + uint64_t last_pass = 0; + uint32_t index = 0; + RID next_pass; + uint8_t priority; + virtual void set_render_priority(int p_priority); + virtual void set_next_pass(RID p_pass); + virtual void update_parameters(const Map<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty); + virtual ~MaterialData(); + }; + + RasterizerStorageRD::MaterialData *_create_material_func(ShaderData *p_shader); + static RasterizerStorageRD::MaterialData *_create_material_funcs(RasterizerStorageRD::ShaderData *p_shader) { + return static_cast<RasterizerSceneHighEndRD *>(singleton)->_create_material_func(static_cast<ShaderData *>(p_shader)); + } + + /* Push Constant */ + + struct PushConstant { + uint32_t index; + uint32_t pad[3]; + }; + + /* Framebuffer */ + + struct RenderBufferDataHighEnd : public RenderBufferData { + //for rendering, may be MSAAd + RID color; + RID depth; + RID specular; + RID normal_buffer; + RID roughness_buffer; + RID depth_fb; + RID depth_normal_fb; + RID depth_normal_roughness_fb; + RID color_fb; + RID color_specular_fb; + int width, height; + + void ensure_specular(); + void clear(); + virtual void configure(RID p_color_buffer, RID p_depth_buffer, int p_width, int p_height, RS::ViewportMSAA p_msaa); + + RID uniform_set; + + ~RenderBufferDataHighEnd(); + }; + + virtual RenderBufferData *_create_render_buffer_data(); + void _allocate_normal_texture(RenderBufferDataHighEnd *rb); + void _allocate_roughness_texture(RenderBufferDataHighEnd *rb); + + RID shadow_sampler; + RID render_base_uniform_set; + RID view_dependant_uniform_set; + + virtual void _base_uniforms_changed(); + void _render_buffers_clear_uniform_set(RenderBufferDataHighEnd *rb); + virtual void _render_buffers_uniform_set_changed(RID p_render_buffers); + virtual RID _render_buffers_get_roughness_texture(RID p_render_buffers); + virtual RID _render_buffers_get_normal_texture(RID p_render_buffers); + + void _update_render_base_uniform_set(); + void _setup_view_dependant_uniform_set(RID p_shadow_atlas, RID p_reflection_atlas); + void _update_render_buffers_uniform_set(RID p_render_buffers); + + /* Scene State UBO */ + + struct ReflectionData { //should always be 128 bytes + float box_extents[3]; + float index; + float box_offset[3]; + uint32_t mask; + float params[4]; // intensity, 0, interior , boxproject + float ambient[4]; // ambient color, energy + float local_matrix[16]; // up to here for spot and omni, rest is for directional + }; + + struct LightData { + float position[3]; + float inv_radius; + float direction[3]; + uint16_t attenuation_energy[2]; //16 bits attenuation, then energy + uint8_t color_specular[4]; //rgb color, a specular (8 bit unorm) + uint16_t cone_attenuation_angle[2]; // attenuation and angle, (16bit float) + uint32_t mask; + uint8_t shadow_color_enabled[4]; //shadow rgb color, a>0.5 enabled (8bit unorm) + float atlas_rect[4]; // in omni, used for atlas uv, in spot, used for projector uv + float shadow_matrix[16]; + }; + + struct DirectionalLightData { + + float direction[3]; + float energy; + float color[3]; + float specular; + float shadow_color[3]; + uint32_t mask; + uint32_t blend_splits; + uint32_t shadow_enabled; + float fade_from; + float fade_to; + float shadow_split_offsets[4]; + float shadow_matrices[4][16]; + }; + + struct GIProbeData { + float xform[16]; + float bounds[3]; + float dynamic_range; + + float bias; + float normal_bias; + uint32_t blend_ambient; + uint32_t texture_slot; + + float anisotropy_strength; + float ao; + float ao_size; + uint32_t pad[1]; + }; + + enum { + INSTANCE_DATA_FLAG_MULTIMESH = 1 << 12, + INSTANCE_DATA_FLAG_MULTIMESH_FORMAT_2D = 1 << 13, + INSTANCE_DATA_FLAG_MULTIMESH_HAS_COLOR = 1 << 14, + INSTANCE_DATA_FLAG_MULTIMESH_HAS_CUSTOM_DATA = 1 << 15, + INSTANCE_DATA_FLAGS_MULTIMESH_STRIDE_SHIFT = 16, + INSTANCE_DATA_FLAGS_MULTIMESH_STRIDE_MASK = 0x7, + INSTANCE_DATA_FLAG_SKELETON = 1 << 19, + }; + + struct InstanceData { + float transform[16]; + float normal_transform[16]; + uint32_t flags; + uint32_t instance_ofs; //instance_offset in instancing/skeleton buffer + uint32_t gi_offset; //GI information when using lightmapping (VCT or lightmap) + uint32_t mask; + }; + + struct SceneState { + struct UBO { + float projection_matrix[16]; + float inv_projection_matrix[16]; + + float camera_matrix[16]; + float inv_camera_matrix[16]; + + float viewport_size[2]; + float screen_pixel_size[2]; + + float shadow_z_offset; + float shadow_z_slope_scale; + + float time; + float reflection_multiplier; + + float ambient_light_color_energy[4]; + + float ambient_color_sky_mix; + uint32_t use_ambient_light; + uint32_t use_ambient_cubemap; + uint32_t use_reflection_cubemap; + + float radiance_inverse_xform[12]; + + float shadow_atlas_pixel_size[2]; + float directional_shadow_pixel_size[2]; + + uint32_t directional_light_count; + float dual_paraboloid_side; + float z_far; + float z_near; + + uint32_t ssao_enabled; + float ssao_light_affect; + float ssao_ao_affect; + uint32_t roughness_limiter_enabled; + + float ao_color[4]; + }; + + UBO ubo; + + RID uniform_buffer; + + ReflectionData *reflections; + uint32_t max_reflections; + RID reflection_buffer; + uint32_t max_reflection_probes_per_instance; + + GIProbeData *gi_probes; + uint32_t max_gi_probes; + RID gi_probe_buffer; + uint32_t max_gi_probe_probes_per_instance; + + LightData *lights; + uint32_t max_lights; + RID light_buffer; + + DirectionalLightData *directional_lights; + uint32_t max_directional_lights; + RID directional_light_buffer; + + RID instance_buffer; + InstanceData *instances; + uint32_t max_instances; + + bool used_screen_texture = false; + bool used_normal_texture = false; + bool used_depth_texture = false; + bool used_sss = false; + uint32_t current_shader_index = 0; + uint32_t current_material_index = 0; + } scene_state; + + /* Render List */ + + struct RenderList { + + int max_elements; + + struct Element { + RasterizerScene::InstanceBase *instance; + MaterialData *material; + union { + struct { + //from least significant to most significant in sort, TODO: should be endian swapped on big endian + uint64_t geometry_index : 20; + uint64_t material_index : 15; + uint64_t shader_index : 12; + uint64_t uses_instancing : 1; + uint64_t uses_vct : 1; + uint64_t uses_lightmap : 1; + uint64_t depth_layer : 4; + uint64_t priority : 8; + }; + + uint64_t sort_key; + }; + uint32_t surface_index; + }; + + Element *base_elements; + Element **elements; + + int element_count; + int alpha_element_count; + + void clear() { + + element_count = 0; + alpha_element_count = 0; + } + + //should eventually be replaced by radix + + struct SortByKey { + + _FORCE_INLINE_ bool operator()(const Element *A, const Element *B) const { + return A->sort_key < B->sort_key; + } + }; + + void sort_by_key(bool p_alpha) { + + SortArray<Element *, SortByKey> sorter; + if (p_alpha) { + sorter.sort(&elements[max_elements - alpha_element_count], alpha_element_count); + } else { + sorter.sort(elements, element_count); + } + } + + struct SortByDepth { + + _FORCE_INLINE_ bool operator()(const Element *A, const Element *B) const { + return A->instance->depth < B->instance->depth; + } + }; + + void sort_by_depth(bool p_alpha) { //used for shadows + + SortArray<Element *, SortByDepth> sorter; + if (p_alpha) { + sorter.sort(&elements[max_elements - alpha_element_count], alpha_element_count); + } else { + sorter.sort(elements, element_count); + } + } + + struct SortByReverseDepthAndPriority { + + _FORCE_INLINE_ bool operator()(const Element *A, const Element *B) const { + uint32_t layer_A = uint32_t(A->priority); + uint32_t layer_B = uint32_t(B->priority); + if (layer_A == layer_B) { + return A->instance->depth > B->instance->depth; + } else { + return layer_A < layer_B; + } + } + }; + + void sort_by_reverse_depth_and_priority(bool p_alpha) { //used for alpha + + SortArray<Element *, SortByReverseDepthAndPriority> sorter; + if (p_alpha) { + sorter.sort(&elements[max_elements - alpha_element_count], alpha_element_count); + } else { + sorter.sort(elements, element_count); + } + } + + _FORCE_INLINE_ Element *add_element() { + + if (element_count + alpha_element_count >= max_elements) + return NULL; + elements[element_count] = &base_elements[element_count]; + return elements[element_count++]; + } + + _FORCE_INLINE_ Element *add_alpha_element() { + + if (element_count + alpha_element_count >= max_elements) + return NULL; + int idx = max_elements - alpha_element_count - 1; + elements[idx] = &base_elements[idx]; + alpha_element_count++; + return elements[idx]; + } + + void init() { + + element_count = 0; + alpha_element_count = 0; + elements = memnew_arr(Element *, max_elements); + base_elements = memnew_arr(Element, max_elements); + for (int i = 0; i < max_elements; i++) + elements[i] = &base_elements[i]; // assign elements + } + + RenderList() { + + max_elements = 0; + } + + ~RenderList() { + memdelete_arr(elements); + memdelete_arr(base_elements); + } + }; + + RenderList render_list; + + static RasterizerSceneHighEndRD *singleton; + uint64_t render_pass; + double time; + RID default_shader; + RID default_material; + RID overdraw_material_shader; + RID overdraw_material; + RID wireframe_material_shader; + RID wireframe_material; + RID default_shader_rd; + RID default_radiance_uniform_set; + RID default_render_buffers_uniform_set; + + RID default_vec4_xform_buffer; + RID default_vec4_xform_uniform_set; + + LightClusterBuilder cluster_builder; + + enum PassMode { + PASS_MODE_COLOR, + PASS_MODE_COLOR_SPECULAR, + PASS_MODE_COLOR_TRANSPARENT, + PASS_MODE_SHADOW, + PASS_MODE_SHADOW_DP, + PASS_MODE_DEPTH, + PASS_MODE_DEPTH_NORMAL, + PASS_MODE_DEPTH_NORMAL_ROUGHNESS, + PASS_MODE_DEPTH_MATERIAL, + }; + + void _setup_environment(RID p_environment, const CameraMatrix &p_cam_projection, const Transform &p_cam_transform, RID p_reflection_probe, bool p_no_fog, const Size2 &p_screen_pixel_size, RID p_shadow_atlas, bool p_flip_y, const Color &p_default_bg_color, float p_znear, float p_zfar, bool p_opaque_render_buffers = false); + void _setup_lights(RID *p_light_cull_result, int p_light_cull_count, const Transform &p_camera_inverse_transform, RID p_shadow_atlas, bool p_using_shadows); + void _setup_reflections(RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, const Transform &p_camera_inverse_transform, RID p_environment); + void _setup_gi_probes(RID *p_gi_probe_probe_cull_result, int p_gi_probe_probe_cull_count, const Transform &p_camera_transform); + + void _fill_instances(RenderList::Element **p_elements, int p_element_count, bool p_for_depth); + void _render_list(RenderingDevice::DrawListID p_draw_list, RenderingDevice::FramebufferFormatID p_framebuffer_Format, RenderList::Element **p_elements, int p_element_count, bool p_reverse_cull, PassMode p_pass_mode, bool p_no_gi, RID p_radiance_uniform_set, RID p_render_buffers_uniform_set); + _FORCE_INLINE_ void _add_geometry(InstanceBase *p_instance, uint32_t p_surface, RID p_material, PassMode p_pass_mode, uint32_t p_geometry_index); + _FORCE_INLINE_ void _add_geometry_with_material(InstanceBase *p_instance, uint32_t p_surface, MaterialData *p_material, RID p_material_rid, PassMode p_pass_mode, uint32_t p_geometry_index); + + void _fill_render_list(InstanceBase **p_cull_result, int p_cull_count, PassMode p_pass_mode, bool p_no_gi); + +protected: + virtual void _render_scene(RID p_render_buffer, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID *p_light_cull_result, int p_light_cull_count, RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, RID *p_gi_probe_cull_result, int p_gi_probe_cull_count, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, const Color &p_default_bg_color); + virtual void _render_shadow(RID p_framebuffer, InstanceBase **p_cull_result, int p_cull_count, const CameraMatrix &p_projection, const Transform &p_transform, float p_zfar, float p_bias, float p_normal_bias, bool p_use_dp, bool p_use_dp_flip); + virtual void _render_material(const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID p_framebuffer, const Rect2i &p_region); + +public: + virtual void set_time(double p_time, double p_step); + + virtual bool free(RID p_rid); + + RasterizerSceneHighEndRD(RasterizerStorageRD *p_storage); + ~RasterizerSceneHighEndRD(); +}; +#endif // RASTERIZER_SCENE_HIGHEND_RD_H diff --git a/servers/rendering/rasterizer_rd/rasterizer_scene_rd.cpp b/servers/rendering/rasterizer_rd/rasterizer_scene_rd.cpp new file mode 100644 index 0000000000..842f7046c9 --- /dev/null +++ b/servers/rendering/rasterizer_rd/rasterizer_scene_rd.cpp @@ -0,0 +1,4039 @@ +/*************************************************************************/ +/* rasterizer_scene_rd.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "rasterizer_scene_rd.h" + +#include "core/os/os.h" +#include "core/project_settings.h" +#include "servers/rendering/rendering_server_raster.h" + +uint64_t RasterizerSceneRD::auto_exposure_counter = 2; + +void RasterizerSceneRD::_clear_reflection_data(ReflectionData &rd) { + + rd.layers.clear(); + rd.radiance_base_cubemap = RID(); + if (rd.downsampled_radiance_cubemap.is_valid()) { + RD::get_singleton()->free(rd.downsampled_radiance_cubemap); + } + rd.downsampled_radiance_cubemap = RID(); + rd.downsampled_layer.mipmaps.clear(); + rd.coefficient_buffer = RID(); +} + +void RasterizerSceneRD::_update_reflection_data(ReflectionData &rd, int p_size, int p_mipmaps, bool p_use_array, RID p_base_cube, int p_base_layer, bool p_low_quality) { + //recreate radiance and all data + + int mipmaps = p_mipmaps; + uint32_t w = p_size, h = p_size; + + if (p_use_array) { + int layers = p_low_quality ? 8 : roughness_layers; + + for (int i = 0; i < layers; i++) { + ReflectionData::Layer layer; + uint32_t mmw = w; + uint32_t mmh = h; + layer.mipmaps.resize(mipmaps); + layer.views.resize(mipmaps); + for (int j = 0; j < mipmaps; j++) { + ReflectionData::Layer::Mipmap &mm = layer.mipmaps.write[j]; + mm.size.width = mmw; + mm.size.height = mmh; + for (int k = 0; k < 6; k++) { + mm.views[k] = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), p_base_cube, p_base_layer + i * 6 + k, j); + Vector<RID> fbtex; + fbtex.push_back(mm.views[k]); + mm.framebuffers[k] = RD::get_singleton()->framebuffer_create(fbtex); + } + + layer.views.write[j] = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), p_base_cube, p_base_layer + i * 6, j, RD::TEXTURE_SLICE_CUBEMAP); + + mmw = MAX(1, mmw >> 1); + mmh = MAX(1, mmh >> 1); + } + + rd.layers.push_back(layer); + } + + } else { + mipmaps = p_low_quality ? 8 : mipmaps; + //regular cubemap, lower quality (aliasing, less memory) + ReflectionData::Layer layer; + uint32_t mmw = w; + uint32_t mmh = h; + layer.mipmaps.resize(mipmaps); + layer.views.resize(mipmaps); + for (int j = 0; j < mipmaps; j++) { + ReflectionData::Layer::Mipmap &mm = layer.mipmaps.write[j]; + mm.size.width = mmw; + mm.size.height = mmh; + for (int k = 0; k < 6; k++) { + mm.views[k] = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), p_base_cube, p_base_layer + k, j); + Vector<RID> fbtex; + fbtex.push_back(mm.views[k]); + mm.framebuffers[k] = RD::get_singleton()->framebuffer_create(fbtex); + } + + layer.views.write[j] = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), p_base_cube, p_base_layer, j, RD::TEXTURE_SLICE_CUBEMAP); + + mmw = MAX(1, mmw >> 1); + mmh = MAX(1, mmh >> 1); + } + + rd.layers.push_back(layer); + } + + rd.radiance_base_cubemap = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), p_base_cube, p_base_layer, 0, RD::TEXTURE_SLICE_CUBEMAP); + + RD::TextureFormat tf; + tf.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT; + tf.width = 64; // Always 64x64 + tf.height = 64; + tf.type = RD::TEXTURE_TYPE_CUBE; + tf.array_layers = 6; + tf.mipmaps = 7; + tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; + + rd.downsampled_radiance_cubemap = RD::get_singleton()->texture_create(tf, RD::TextureView()); + { + uint32_t mmw = 64; + uint32_t mmh = 64; + rd.downsampled_layer.mipmaps.resize(7); + for (int j = 0; j < rd.downsampled_layer.mipmaps.size(); j++) { + ReflectionData::DownsampleLayer::Mipmap &mm = rd.downsampled_layer.mipmaps.write[j]; + mm.size.width = mmw; + mm.size.height = mmh; + mm.view = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), rd.downsampled_radiance_cubemap, 0, j, RD::TEXTURE_SLICE_CUBEMAP); + + mmw = MAX(1, mmw >> 1); + mmh = MAX(1, mmh >> 1); + } + } +} + +void RasterizerSceneRD::_create_reflection_fast_filter(ReflectionData &rd, bool p_use_arrays) { + + storage->get_effects()->cubemap_downsample(rd.radiance_base_cubemap, rd.downsampled_layer.mipmaps[0].view, rd.downsampled_layer.mipmaps[0].size); + + for (int i = 1; i < rd.downsampled_layer.mipmaps.size(); i++) { + storage->get_effects()->cubemap_downsample(rd.downsampled_layer.mipmaps[i - 1].view, rd.downsampled_layer.mipmaps[i].view, rd.downsampled_layer.mipmaps[i].size); + } + + Vector<RID> views; + if (p_use_arrays) { + for (int i = 1; i < rd.layers.size(); i++) { + views.push_back(rd.layers[i].views[0]); + } + } else { + for (int i = 1; i < rd.layers[0].views.size(); i++) { + views.push_back(rd.layers[0].views[i]); + } + } + + storage->get_effects()->cubemap_filter(rd.downsampled_radiance_cubemap, views, p_use_arrays); +} + +void RasterizerSceneRD::_create_reflection_importance_sample(ReflectionData &rd, bool p_use_arrays, int p_cube_side, int p_base_layer) { + + if (p_use_arrays) { + + //render directly to the layers + storage->get_effects()->cubemap_roughness(rd.radiance_base_cubemap, rd.layers[p_base_layer].views[0], p_cube_side, sky_ggx_samples_quality, float(p_base_layer) / (rd.layers.size() - 1.0), rd.layers[p_base_layer].mipmaps[0].size.x); + } else { + + storage->get_effects()->cubemap_roughness(rd.layers[0].views[p_base_layer - 1], rd.layers[0].views[p_base_layer], p_cube_side, sky_ggx_samples_quality, float(p_base_layer) / (rd.layers[0].mipmaps.size() - 1.0), rd.layers[0].mipmaps[p_base_layer].size.x); + } +} + +void RasterizerSceneRD::_update_reflection_mipmaps(ReflectionData &rd) { + + if (sky_use_cubemap_array) { + + for (int i = 0; i < rd.layers.size(); i++) { + for (int j = 0; j < rd.layers[i].mipmaps.size() - 1; j++) { + for (int k = 0; k < 6; k++) { + RID view = rd.layers[i].mipmaps[j].views[k]; + RID fb = rd.layers[i].mipmaps[j + 1].framebuffers[k]; + Vector2 size = rd.layers[i].mipmaps[j].size; + size = Vector2(1.0 / size.x, 1.0 / size.y); + storage->get_effects()->make_mipmap(view, fb, size); + } + } + } + } +} + +RID RasterizerSceneRD::sky_create() { + return sky_owner.make_rid(Sky()); +} + +void RasterizerSceneRD::_sky_invalidate(Sky *p_sky) { + if (!p_sky->dirty) { + p_sky->dirty = true; + p_sky->dirty_list = dirty_sky_list; + dirty_sky_list = p_sky; + } +} + +void RasterizerSceneRD::sky_set_radiance_size(RID p_sky, int p_radiance_size) { + Sky *sky = sky_owner.getornull(p_sky); + ERR_FAIL_COND(!sky); + ERR_FAIL_COND(p_radiance_size < 32 || p_radiance_size > 2048); + if (sky->radiance_size == p_radiance_size) { + return; + } + sky->radiance_size = p_radiance_size; + + if (sky->mode == RS::SKY_MODE_REALTIME && sky->radiance_size != 256) { + WARN_PRINT("Realtime Skies can only use a radiance size of 256. Radiance size will be set to 256 internally."); + sky->radiance_size = 256; + } + + _sky_invalidate(sky); + if (sky->radiance.is_valid()) { + RD::get_singleton()->free(sky->radiance); + sky->radiance = RID(); + } + _clear_reflection_data(sky->reflection); +} + +void RasterizerSceneRD::sky_set_mode(RID p_sky, RS::SkyMode p_mode) { + Sky *sky = sky_owner.getornull(p_sky); + ERR_FAIL_COND(!sky); + + if (sky->mode == p_mode) { + return; + } + + sky->mode = p_mode; + + if (sky->mode == RS::SKY_MODE_REALTIME && sky->radiance_size != 256) { + WARN_PRINT("Realtime Skies can only use a radiance size of 256. Radiance size will be set to 256 internally."); + sky_set_radiance_size(p_sky, 256); + } + + _sky_invalidate(sky); + if (sky->radiance.is_valid()) { + RD::get_singleton()->free(sky->radiance); + sky->radiance = RID(); + } + _clear_reflection_data(sky->reflection); +} + +void RasterizerSceneRD::sky_set_material(RID p_sky, RID p_material) { + Sky *sky = sky_owner.getornull(p_sky); + ERR_FAIL_COND(!sky); + sky->material = p_material; +} +void RasterizerSceneRD::_update_dirty_skys() { + + Sky *sky = dirty_sky_list; + + while (sky) { + + bool texture_set_dirty = false; + //update sky configuration if texture is missing + + if (sky->radiance.is_null()) { + int mipmaps = Image::get_image_required_mipmaps(sky->radiance_size, sky->radiance_size, Image::FORMAT_RGBAH) + 1; + + uint32_t w = sky->radiance_size, h = sky->radiance_size; + int layers = roughness_layers; + if (sky->mode == RS::SKY_MODE_REALTIME) { + layers = 8; + if (roughness_layers != 8) { + WARN_PRINT("When using REALTIME skies, roughness_layers should be set to 8 in the project settings for best quality reflections"); + } + } + + if (sky_use_cubemap_array) { + //array (higher quality, 6 times more memory) + RD::TextureFormat tf; + tf.array_layers = layers * 6; + tf.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT; + tf.type = RD::TEXTURE_TYPE_CUBE_ARRAY; + tf.mipmaps = mipmaps; + tf.width = w; + tf.height = h; + tf.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT; + + sky->radiance = RD::get_singleton()->texture_create(tf, RD::TextureView()); + + _update_reflection_data(sky->reflection, sky->radiance_size, mipmaps, true, sky->radiance, 0, sky->mode == RS::SKY_MODE_REALTIME); + + } else { + //regular cubemap, lower quality (aliasing, less memory) + RD::TextureFormat tf; + tf.array_layers = 6; + tf.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT; + tf.type = RD::TEXTURE_TYPE_CUBE; + tf.mipmaps = MIN(mipmaps, layers); + tf.width = w; + tf.height = h; + tf.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT; + + sky->radiance = RD::get_singleton()->texture_create(tf, RD::TextureView()); + + _update_reflection_data(sky->reflection, sky->radiance_size, MIN(mipmaps, layers), false, sky->radiance, 0, sky->mode == RS::SKY_MODE_REALTIME); + } + texture_set_dirty = true; + } + + // Create subpass buffers if they havent been created already + if (sky->half_res_pass.is_null() && !RD::get_singleton()->texture_is_valid(sky->half_res_pass) && sky->screen_size.x >= 4 && sky->screen_size.y >= 4) { + RD::TextureFormat tformat; + tformat.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT; + tformat.width = sky->screen_size.x / 2; + tformat.height = sky->screen_size.y / 2; + tformat.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; + tformat.type = RD::TEXTURE_TYPE_2D; + + sky->half_res_pass = RD::get_singleton()->texture_create(tformat, RD::TextureView()); + Vector<RID> texs; + texs.push_back(sky->half_res_pass); + sky->half_res_framebuffer = RD::get_singleton()->framebuffer_create(texs); + texture_set_dirty = true; + } + + if (sky->quarter_res_pass.is_null() && !RD::get_singleton()->texture_is_valid(sky->quarter_res_pass) && sky->screen_size.x >= 4 && sky->screen_size.y >= 4) { + RD::TextureFormat tformat; + tformat.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT; + tformat.width = sky->screen_size.x / 4; + tformat.height = sky->screen_size.y / 4; + tformat.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; + tformat.type = RD::TEXTURE_TYPE_2D; + + sky->quarter_res_pass = RD::get_singleton()->texture_create(tformat, RD::TextureView()); + Vector<RID> texs; + texs.push_back(sky->quarter_res_pass); + sky->quarter_res_framebuffer = RD::get_singleton()->framebuffer_create(texs); + texture_set_dirty = true; + } + + if (texture_set_dirty) { + for (int i = 0; i < SKY_TEXTURE_SET_MAX; i++) { + if (sky->texture_uniform_sets[i].is_valid() && RD::get_singleton()->uniform_set_is_valid(sky->texture_uniform_sets[i])) { + RD::get_singleton()->free(sky->texture_uniform_sets[i]); + sky->texture_uniform_sets[i] = RID(); + } + } + } + + sky->reflection.dirty = true; + + Sky *next = sky->dirty_list; + sky->dirty_list = nullptr; + sky->dirty = false; + sky = next; + } + + dirty_sky_list = nullptr; +} + +RID RasterizerSceneRD::sky_get_radiance_texture_rd(RID p_sky) const { + Sky *sky = sky_owner.getornull(p_sky); + ERR_FAIL_COND_V(!sky, RID()); + + return sky->radiance; +} + +RID RasterizerSceneRD::sky_get_radiance_uniform_set_rd(RID p_sky, RID p_shader, int p_set) const { + Sky *sky = sky_owner.getornull(p_sky); + ERR_FAIL_COND_V(!sky, RID()); + + if (sky->uniform_set.is_null() || !RD::get_singleton()->uniform_set_is_valid(sky->uniform_set)) { + + sky->uniform_set = RID(); + if (sky->radiance.is_valid()) { + Vector<RD::Uniform> uniforms; + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_TEXTURE; + u.binding = 0; + u.ids.push_back(sky->radiance); + uniforms.push_back(u); + } + + sky->uniform_set = RD::get_singleton()->uniform_set_create(uniforms, p_shader, p_set); + } + } + + return sky->uniform_set; +} + +RID RasterizerSceneRD::_get_sky_textures(Sky *p_sky, SkyTextureSetVersion p_version) { + + if (p_sky->texture_uniform_sets[p_version].is_valid() && RD::get_singleton()->uniform_set_is_valid(p_sky->texture_uniform_sets[p_version])) { + return p_sky->texture_uniform_sets[p_version]; + } + Vector<RD::Uniform> uniforms; + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_TEXTURE; + u.binding = 0; + if (p_sky->radiance.is_valid() && p_version <= SKY_TEXTURE_SET_QUARTER_RES) { + u.ids.push_back(p_sky->radiance); + } else { + u.ids.push_back(storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_CUBEMAP_BLACK)); + } + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_TEXTURE; + u.binding = 1; // half res + if (p_sky->half_res_pass.is_valid() && p_version != SKY_TEXTURE_SET_HALF_RES && p_version != SKY_TEXTURE_SET_CUBEMAP_HALF_RES) { + if (p_version >= SKY_TEXTURE_SET_CUBEMAP) { + u.ids.push_back(p_sky->reflection.layers[0].views[1]); + } else { + u.ids.push_back(p_sky->half_res_pass); + } + } else { + if (p_version < SKY_TEXTURE_SET_CUBEMAP) { + u.ids.push_back(storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_WHITE)); + } else { + u.ids.push_back(storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_CUBEMAP_BLACK)); + } + } + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_TEXTURE; + u.binding = 2; // quarter res + if (p_sky->quarter_res_pass.is_valid() && p_version != SKY_TEXTURE_SET_QUARTER_RES && p_version != SKY_TEXTURE_SET_CUBEMAP_QUARTER_RES) { + if (p_version >= SKY_TEXTURE_SET_CUBEMAP) { + u.ids.push_back(p_sky->reflection.layers[0].views[2]); + } else { + u.ids.push_back(p_sky->quarter_res_pass); + } + } else { + if (p_version < SKY_TEXTURE_SET_CUBEMAP) { + u.ids.push_back(storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_WHITE)); + } else { + u.ids.push_back(storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_CUBEMAP_BLACK)); + } + } + uniforms.push_back(u); + } + + p_sky->texture_uniform_sets[p_version] = RD::get_singleton()->uniform_set_create(uniforms, sky_shader.default_shader_rd, SKY_SET_TEXTURES); + return p_sky->texture_uniform_sets[p_version]; +} + +RID RasterizerSceneRD::sky_get_material(RID p_sky) const { + Sky *sky = sky_owner.getornull(p_sky); + ERR_FAIL_COND_V(!sky, RID()); + + return sky->material; +} + +void RasterizerSceneRD::_draw_sky(bool p_can_continue, RID p_fb, RID p_environment, const CameraMatrix &p_projection, const Transform &p_transform) { + + ERR_FAIL_COND(!is_environment(p_environment)); + + Sky *sky = sky_owner.getornull(environment_get_sky(p_environment)); + ERR_FAIL_COND(!sky); + + RID sky_material = sky_get_material(environment_get_sky(p_environment)); + + SkyMaterialData *material = NULL; + + if (sky_material.is_valid()) { + material = (SkyMaterialData *)storage->material_get_data(sky_material, RasterizerStorageRD::SHADER_TYPE_SKY); + if (!material || !material->shader_data->valid) { + material = NULL; + } + } + + if (!material) { + sky_material = sky_shader.default_material; + material = (SkyMaterialData *)storage->material_get_data(sky_material, RasterizerStorageRD::SHADER_TYPE_SKY); + } + + ERR_FAIL_COND(!material); + + SkyShaderData *shader_data = material->shader_data; + + ERR_FAIL_COND(!shader_data); + + Basis sky_transform = environment_get_sky_orientation(p_environment); + sky_transform.invert(); + + float multiplier = environment_get_bg_energy(p_environment); + float custom_fov = environment_get_sky_custom_fov(p_environment); + // Camera + CameraMatrix camera; + + if (custom_fov) { + + float near_plane = p_projection.get_z_near(); + float far_plane = p_projection.get_z_far(); + float aspect = p_projection.get_aspect(); + + camera.set_perspective(custom_fov, aspect, near_plane, far_plane); + + } else { + camera = p_projection; + } + + sky_transform = p_transform.basis * sky_transform; + + if (shader_data->uses_quarter_res) { + RenderPipelineVertexFormatCacheRD *pipeline = &shader_data->pipelines[SKY_VERSION_QUARTER_RES]; + + RID texture_uniform_set = _get_sky_textures(sky, SKY_TEXTURE_SET_QUARTER_RES); + + Vector<Color> clear_colors; + clear_colors.push_back(Color(0.0, 0.0, 0.0)); + + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(sky->quarter_res_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, clear_colors); + storage->get_effects()->render_sky(draw_list, time, sky->quarter_res_framebuffer, sky_scene_state.sampler_uniform_set, sky_scene_state.light_uniform_set, pipeline, material->uniform_set, texture_uniform_set, camera, sky_transform, multiplier, p_transform.origin); + RD::get_singleton()->draw_list_end(); + } + + if (shader_data->uses_half_res) { + RenderPipelineVertexFormatCacheRD *pipeline = &shader_data->pipelines[SKY_VERSION_HALF_RES]; + + RID texture_uniform_set = _get_sky_textures(sky, SKY_TEXTURE_SET_HALF_RES); + + Vector<Color> clear_colors; + clear_colors.push_back(Color(0.0, 0.0, 0.0)); + + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(sky->half_res_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, clear_colors); + storage->get_effects()->render_sky(draw_list, time, sky->half_res_framebuffer, sky_scene_state.sampler_uniform_set, sky_scene_state.light_uniform_set, pipeline, material->uniform_set, texture_uniform_set, camera, sky_transform, multiplier, p_transform.origin); + RD::get_singleton()->draw_list_end(); + } + + RenderPipelineVertexFormatCacheRD *pipeline = &shader_data->pipelines[SKY_VERSION_BACKGROUND]; + + RID texture_uniform_set = _get_sky_textures(sky, SKY_TEXTURE_SET_BACKGROUND); + + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_fb, RD::INITIAL_ACTION_CONTINUE, p_can_continue ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CONTINUE, p_can_continue ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ); + storage->get_effects()->render_sky(draw_list, time, p_fb, sky_scene_state.sampler_uniform_set, sky_scene_state.light_uniform_set, pipeline, material->uniform_set, texture_uniform_set, camera, sky_transform, multiplier, p_transform.origin); + RD::get_singleton()->draw_list_end(); +} + +void RasterizerSceneRD::_setup_sky(RID p_environment, const Vector3 &p_position, const Size2i p_screen_size) { + + ERR_FAIL_COND(!is_environment(p_environment)); + + Sky *sky = sky_owner.getornull(environment_get_sky(p_environment)); + ERR_FAIL_COND(!sky); + + RID sky_material = sky_get_material(environment_get_sky(p_environment)); + + SkyMaterialData *material = NULL; + + if (sky_material.is_valid()) { + material = (SkyMaterialData *)storage->material_get_data(sky_material, RasterizerStorageRD::SHADER_TYPE_SKY); + if (!material || !material->shader_data->valid) { + material = NULL; + } + } + + if (!material) { + sky_material = sky_shader.default_material; + material = (SkyMaterialData *)storage->material_get_data(sky_material, RasterizerStorageRD::SHADER_TYPE_SKY); + } + + ERR_FAIL_COND(!material); + + SkyShaderData *shader_data = material->shader_data; + + ERR_FAIL_COND(!shader_data); + + // Invalidate supbass buffers if screen size changes + if (sky->screen_size != p_screen_size) { + sky->screen_size = p_screen_size; + sky->screen_size.x = sky->screen_size.x < 4 ? 4 : sky->screen_size.x; + sky->screen_size.y = sky->screen_size.y < 4 ? 4 : sky->screen_size.y; + if (shader_data->uses_half_res) { + if (sky->half_res_pass.is_valid()) { + RD::get_singleton()->free(sky->half_res_pass); + sky->half_res_pass = RID(); + } + _sky_invalidate(sky); + } + if (shader_data->uses_quarter_res) { + if (sky->quarter_res_pass.is_valid()) { + RD::get_singleton()->free(sky->quarter_res_pass); + sky->quarter_res_pass = RID(); + } + _sky_invalidate(sky); + } + } + + // Create new subpass buffers if necessary + if ((shader_data->uses_half_res && sky->half_res_pass.is_null()) || + (shader_data->uses_quarter_res && sky->quarter_res_pass.is_null()) || + sky->radiance.is_null()) { + _sky_invalidate(sky); + _update_dirty_skys(); + } + + if (shader_data->uses_time && time - sky->prev_time > 0.00001) { + + sky->prev_time = time; + sky->reflection.dirty = true; + RenderingServerRaster::redraw_request(); + } + + if (material != sky->prev_material) { + + sky->prev_material = material; + sky->reflection.dirty = true; + } + + if (material->uniform_set_updated) { + + material->uniform_set_updated = false; + sky->reflection.dirty = true; + } + + if (!p_position.is_equal_approx(sky->prev_position) && shader_data->uses_position) { + + sky->prev_position = p_position; + sky->reflection.dirty = true; + } + + if (shader_data->uses_light || sky_scene_state.light_uniform_set.is_null()) { + // Check whether the directional_light_buffer changes + bool light_data_dirty = false; + + if (sky_scene_state.directional_light_count != sky_scene_state.last_frame_directional_light_count) { + light_data_dirty = true; + for (uint32_t i = sky_scene_state.directional_light_count; i < sky_scene_state.max_directional_lights; i++) { + sky_scene_state.directional_lights[i].enabled = false; + } + } + if (!light_data_dirty) { + for (uint32_t i = 0; i < sky_scene_state.directional_light_count; i++) { + if (sky_scene_state.directional_lights[i].direction[0] != sky_scene_state.last_frame_directional_lights[i].direction[0] || + sky_scene_state.directional_lights[i].direction[1] != sky_scene_state.last_frame_directional_lights[i].direction[1] || + sky_scene_state.directional_lights[i].direction[2] != sky_scene_state.last_frame_directional_lights[i].direction[2] || + sky_scene_state.directional_lights[i].energy != sky_scene_state.last_frame_directional_lights[i].energy || + sky_scene_state.directional_lights[i].color[0] != sky_scene_state.last_frame_directional_lights[i].color[0] || + sky_scene_state.directional_lights[i].color[1] != sky_scene_state.last_frame_directional_lights[i].color[1] || + sky_scene_state.directional_lights[i].color[2] != sky_scene_state.last_frame_directional_lights[i].color[2] || + sky_scene_state.directional_lights[i].enabled != sky_scene_state.last_frame_directional_lights[i].enabled) { + light_data_dirty = true; + break; + } + } + } + + if (light_data_dirty || sky_scene_state.light_uniform_set.is_null()) { + + RD::get_singleton()->buffer_update(sky_scene_state.directional_light_buffer, 0, sizeof(SkyDirectionalLightData) * sky_scene_state.max_directional_lights, sky_scene_state.directional_lights, true); + + if (sky_scene_state.light_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(sky_scene_state.light_uniform_set)) { + RD::get_singleton()->free(sky_scene_state.light_uniform_set); + } + + Vector<RD::Uniform> uniforms; + { + RD::Uniform u; + u.binding = 0; + u.type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; + u.ids.push_back(sky_scene_state.directional_light_buffer); + uniforms.push_back(u); + } + + sky_scene_state.light_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, sky_shader.default_shader_rd, SKY_SET_LIGHTS); + + RasterizerSceneRD::SkyDirectionalLightData *temp = sky_scene_state.last_frame_directional_lights; + sky_scene_state.last_frame_directional_lights = sky_scene_state.directional_lights; + sky_scene_state.directional_lights = temp; + sky_scene_state.last_frame_directional_light_count = sky_scene_state.directional_light_count; + sky->reflection.dirty = true; + } + } +} + +void RasterizerSceneRD::_update_sky(RID p_environment, const CameraMatrix &p_projection, const Transform &p_transform) { + + ERR_FAIL_COND(!is_environment(p_environment)); + + Sky *sky = sky_owner.getornull(environment_get_sky(p_environment)); + ERR_FAIL_COND(!sky); + + RID sky_material = sky_get_material(environment_get_sky(p_environment)); + + SkyMaterialData *material = NULL; + + if (sky_material.is_valid()) { + material = (SkyMaterialData *)storage->material_get_data(sky_material, RasterizerStorageRD::SHADER_TYPE_SKY); + if (!material || !material->shader_data->valid) { + material = NULL; + } + } + + if (!material) { + sky_material = sky_shader.default_material; + material = (SkyMaterialData *)storage->material_get_data(sky_material, RasterizerStorageRD::SHADER_TYPE_SKY); + } + + ERR_FAIL_COND(!material); + + SkyShaderData *shader_data = material->shader_data; + + ERR_FAIL_COND(!shader_data); + + float multiplier = environment_get_bg_energy(p_environment); + + // Update radiance cubemap + if (sky->reflection.dirty) { + + 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) + }; + + CameraMatrix cm; + cm.set_perspective(90, 1, 0.01, 10.0); + CameraMatrix correction; + correction.set_depth_correction(true); + cm = correction * cm; + + if (shader_data->uses_quarter_res) { + RenderPipelineVertexFormatCacheRD *pipeline = &shader_data->pipelines[SKY_VERSION_CUBEMAP_QUARTER_RES]; + + Vector<Color> clear_colors; + clear_colors.push_back(Color(0.0, 0.0, 0.0)); + RD::DrawListID cubemap_draw_list; + + for (int i = 0; i < 6; i++) { + Transform local_view; + local_view.set_look_at(Vector3(0, 0, 0), view_normals[i], view_up[i]); + RID texture_uniform_set = _get_sky_textures(sky, SKY_TEXTURE_SET_CUBEMAP_QUARTER_RES); + + cubemap_draw_list = RD::get_singleton()->draw_list_begin(sky->reflection.layers[0].mipmaps[2].framebuffers[i], RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); + storage->get_effects()->render_sky(cubemap_draw_list, time, sky->reflection.layers[0].mipmaps[2].framebuffers[i], sky_scene_state.sampler_uniform_set, sky_scene_state.light_uniform_set, pipeline, material->uniform_set, texture_uniform_set, cm, local_view.basis, multiplier, p_transform.origin); + RD::get_singleton()->draw_list_end(); + } + } + + if (shader_data->uses_half_res) { + RenderPipelineVertexFormatCacheRD *pipeline = &shader_data->pipelines[SKY_VERSION_CUBEMAP_HALF_RES]; + + Vector<Color> clear_colors; + clear_colors.push_back(Color(0.0, 0.0, 0.0)); + RD::DrawListID cubemap_draw_list; + + for (int i = 0; i < 6; i++) { + Transform local_view; + local_view.set_look_at(Vector3(0, 0, 0), view_normals[i], view_up[i]); + RID texture_uniform_set = _get_sky_textures(sky, SKY_TEXTURE_SET_CUBEMAP_HALF_RES); + + cubemap_draw_list = RD::get_singleton()->draw_list_begin(sky->reflection.layers[0].mipmaps[1].framebuffers[i], RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); + storage->get_effects()->render_sky(cubemap_draw_list, time, sky->reflection.layers[0].mipmaps[1].framebuffers[i], sky_scene_state.sampler_uniform_set, sky_scene_state.light_uniform_set, pipeline, material->uniform_set, texture_uniform_set, cm, local_view.basis, multiplier, p_transform.origin); + RD::get_singleton()->draw_list_end(); + } + } + + RD::DrawListID cubemap_draw_list; + RenderPipelineVertexFormatCacheRD *pipeline = &shader_data->pipelines[SKY_VERSION_CUBEMAP]; + + for (int i = 0; i < 6; i++) { + Transform local_view; + local_view.set_look_at(Vector3(0, 0, 0), view_normals[i], view_up[i]); + RID texture_uniform_set = _get_sky_textures(sky, SKY_TEXTURE_SET_CUBEMAP); + + cubemap_draw_list = RD::get_singleton()->draw_list_begin(sky->reflection.layers[0].mipmaps[0].framebuffers[i], RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); + storage->get_effects()->render_sky(cubemap_draw_list, time, sky->reflection.layers[0].mipmaps[0].framebuffers[i], sky_scene_state.sampler_uniform_set, sky_scene_state.light_uniform_set, pipeline, material->uniform_set, texture_uniform_set, cm, local_view.basis, multiplier, p_transform.origin); + RD::get_singleton()->draw_list_end(); + } + if (sky_use_cubemap_array) { + if (sky->mode == RS::SKY_MODE_QUALITY) { + for (int i = 1; i < sky->reflection.layers.size(); i++) { + _create_reflection_importance_sample(sky->reflection, sky_use_cubemap_array, 10, i); + } + } else { + _create_reflection_fast_filter(sky->reflection, sky_use_cubemap_array); + } + + _update_reflection_mipmaps(sky->reflection); + } else { + if (sky->mode == RS::SKY_MODE_QUALITY) { + for (int i = 1; i < sky->reflection.layers[0].mipmaps.size(); i++) { + _create_reflection_importance_sample(sky->reflection, sky_use_cubemap_array, 10, i); + } + } else { + _create_reflection_fast_filter(sky->reflection, sky_use_cubemap_array); + } + } + + sky->reflection.dirty = false; + } +} + +/* SKY SHADER */ + +void RasterizerSceneRD::SkyShaderData::set_code(const String &p_code) { + //compile + + code = p_code; + valid = false; + ubo_size = 0; + uniforms.clear(); + + if (code == String()) { + return; //just invalid, but no error + } + + ShaderCompilerRD::GeneratedCode gen_code; + ShaderCompilerRD::IdentifierActions actions; + + uses_time = false; + uses_half_res = false; + uses_quarter_res = false; + uses_position = false; + uses_light = false; + + actions.render_mode_flags["use_half_res_pass"] = &uses_half_res; + actions.render_mode_flags["use_quarter_res_pass"] = &uses_quarter_res; + + actions.usage_flag_pointers["TIME"] = &uses_time; + actions.usage_flag_pointers["POSITION"] = &uses_position; + actions.usage_flag_pointers["LIGHT0_ENABLED"] = &uses_light; + actions.usage_flag_pointers["LIGHT0_ENERGY"] = &uses_light; + actions.usage_flag_pointers["LIGHT0_DIRECTION"] = &uses_light; + actions.usage_flag_pointers["LIGHT0_COLOR"] = &uses_light; + actions.usage_flag_pointers["LIGHT1_ENABLED"] = &uses_light; + actions.usage_flag_pointers["LIGHT1_ENERGY"] = &uses_light; + actions.usage_flag_pointers["LIGHT1_DIRECTION"] = &uses_light; + actions.usage_flag_pointers["LIGHT1_COLOR"] = &uses_light; + actions.usage_flag_pointers["LIGHT2_ENABLED"] = &uses_light; + actions.usage_flag_pointers["LIGHT2_ENERGY"] = &uses_light; + actions.usage_flag_pointers["LIGHT2_DIRECTION"] = &uses_light; + actions.usage_flag_pointers["LIGHT2_COLOR"] = &uses_light; + actions.usage_flag_pointers["LIGHT3_ENABLED"] = &uses_light; + actions.usage_flag_pointers["LIGHT3_ENERGY"] = &uses_light; + actions.usage_flag_pointers["LIGHT3_DIRECTION"] = &uses_light; + actions.usage_flag_pointers["LIGHT3_COLOR"] = &uses_light; + + actions.uniforms = &uniforms; + + RasterizerSceneRD *scene_singleton = (RasterizerSceneRD *)RasterizerSceneRD::singleton; + + Error err = scene_singleton->sky_shader.compiler.compile(RS::SHADER_SKY, code, &actions, path, gen_code); + + ERR_FAIL_COND(err != OK); + + if (version.is_null()) { + version = scene_singleton->sky_shader.shader.version_create(); + } + +#if 0 + print_line("**compiling shader:"); + print_line("**defines:\n"); + for (int i = 0; i < gen_code.defines.size(); i++) { + print_line(gen_code.defines[i]); + } + print_line("\n**uniforms:\n" + gen_code.uniforms); + // print_line("\n**vertex_globals:\n" + gen_code.vertex_global); + // print_line("\n**vertex_code:\n" + gen_code.vertex); + print_line("\n**fragment_globals:\n" + gen_code.fragment_global); + print_line("\n**fragment_code:\n" + gen_code.fragment); + print_line("\n**light_code:\n" + gen_code.light); +#endif + + scene_singleton->sky_shader.shader.version_set_code(version, gen_code.uniforms, gen_code.vertex_global, gen_code.vertex, gen_code.fragment_global, gen_code.light, gen_code.fragment, gen_code.defines); + ERR_FAIL_COND(!scene_singleton->sky_shader.shader.version_is_valid(version)); + + ubo_size = gen_code.uniform_total_size; + ubo_offsets = gen_code.uniform_offsets; + texture_uniforms = gen_code.texture_uniforms; + + //update pipelines + + for (int i = 0; i < SKY_VERSION_MAX; i++) { + + RD::PipelineDepthStencilState depth_stencil_state; + depth_stencil_state.enable_depth_test = true; + depth_stencil_state.depth_compare_operator = RD::COMPARE_OP_LESS_OR_EQUAL; + + RID shader_variant = scene_singleton->sky_shader.shader.version_get_shader(version, i); + pipelines[i].setup(shader_variant, RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), depth_stencil_state, RD::PipelineColorBlendState::create_disabled(), 0); + } + + valid = true; +} + +void RasterizerSceneRD::SkyShaderData::set_default_texture_param(const StringName &p_name, RID p_texture) { + if (!p_texture.is_valid()) { + default_texture_params.erase(p_name); + } else { + default_texture_params[p_name] = p_texture; + } +} + +void RasterizerSceneRD::SkyShaderData::get_param_list(List<PropertyInfo> *p_param_list) const { + + Map<int, StringName> order; + + for (Map<StringName, ShaderLanguage::ShaderNode::Uniform>::Element *E = uniforms.front(); E; E = E->next()) { + + if (E->get().texture_order >= 0) { + order[E->get().texture_order + 100000] = E->key(); + } else { + order[E->get().order] = E->key(); + } + } + + for (Map<int, StringName>::Element *E = order.front(); E; E = E->next()) { + + PropertyInfo pi = ShaderLanguage::uniform_to_property_info(uniforms[E->get()]); + pi.name = E->get(); + p_param_list->push_back(pi); + } +} + +bool RasterizerSceneRD::SkyShaderData::is_param_texture(const StringName &p_param) const { + if (!uniforms.has(p_param)) { + return false; + } + + return uniforms[p_param].texture_order >= 0; +} + +bool RasterizerSceneRD::SkyShaderData::is_animated() const { + return false; +} + +bool RasterizerSceneRD::SkyShaderData::casts_shadows() const { + return false; +} + +Variant RasterizerSceneRD::SkyShaderData::get_default_parameter(const StringName &p_parameter) const { + if (uniforms.has(p_parameter)) { + ShaderLanguage::ShaderNode::Uniform uniform = uniforms[p_parameter]; + Vector<ShaderLanguage::ConstantNode::Value> default_value = uniform.default_value; + return ShaderLanguage::constant_value_to_variant(default_value, uniform.type, uniform.hint); + } + return Variant(); +} + +RasterizerSceneRD::SkyShaderData::SkyShaderData() { + valid = false; +} + +RasterizerSceneRD::SkyShaderData::~SkyShaderData() { + RasterizerSceneRD *scene_singleton = (RasterizerSceneRD *)RasterizerSceneRD::singleton; + ERR_FAIL_COND(!scene_singleton); + //pipeline variants will clear themselves if shader is gone + if (version.is_valid()) { + scene_singleton->sky_shader.shader.version_free(version); + } +} + +RasterizerStorageRD::ShaderData *RasterizerSceneRD::_create_sky_shader_func() { + SkyShaderData *shader_data = memnew(SkyShaderData); + return shader_data; +} + +void RasterizerSceneRD::SkyMaterialData::update_parameters(const Map<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty) { + + RasterizerSceneRD *scene_singleton = (RasterizerSceneRD *)RasterizerSceneRD::singleton; + + uniform_set_updated = true; + + if ((uint32_t)ubo_data.size() != shader_data->ubo_size) { + p_uniform_dirty = true; + if (uniform_buffer.is_valid()) { + RD::get_singleton()->free(uniform_buffer); + uniform_buffer = RID(); + } + + ubo_data.resize(shader_data->ubo_size); + if (ubo_data.size()) { + uniform_buffer = RD::get_singleton()->uniform_buffer_create(ubo_data.size()); + memset(ubo_data.ptrw(), 0, ubo_data.size()); //clear + } + + //clear previous uniform set + if (uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(uniform_set)) { + RD::get_singleton()->free(uniform_set); + uniform_set = RID(); + } + } + + //check whether buffer changed + if (p_uniform_dirty && ubo_data.size()) { + + update_uniform_buffer(shader_data->uniforms, shader_data->ubo_offsets.ptr(), p_parameters, ubo_data.ptrw(), ubo_data.size(), false); + RD::get_singleton()->buffer_update(uniform_buffer, 0, ubo_data.size(), ubo_data.ptrw()); + } + + uint32_t tex_uniform_count = shader_data->texture_uniforms.size(); + + if ((uint32_t)texture_cache.size() != tex_uniform_count) { + texture_cache.resize(tex_uniform_count); + p_textures_dirty = true; + + //clear previous uniform set + if (uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(uniform_set)) { + RD::get_singleton()->free(uniform_set); + uniform_set = RID(); + } + } + + if (p_textures_dirty && tex_uniform_count) { + + update_textures(p_parameters, shader_data->default_texture_params, shader_data->texture_uniforms, texture_cache.ptrw(), true); + } + + if (shader_data->ubo_size == 0 && shader_data->texture_uniforms.size() == 0) { + // This material does not require an uniform set, so don't create it. + return; + } + + if (!p_textures_dirty && uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(uniform_set)) { + //no reason to update uniform set, only UBO (or nothing) was needed to update + return; + } + + Vector<RD::Uniform> uniforms; + + { + + if (shader_data->ubo_size) { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; + u.binding = 0; + u.ids.push_back(uniform_buffer); + uniforms.push_back(u); + } + + const RID *textures = texture_cache.ptrw(); + for (uint32_t i = 0; i < tex_uniform_count; i++) { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_TEXTURE; + u.binding = 1 + i; + u.ids.push_back(textures[i]); + uniforms.push_back(u); + } + } + + uniform_set = RD::get_singleton()->uniform_set_create(uniforms, scene_singleton->sky_shader.shader.version_get_shader(shader_data->version, 0), SKY_SET_MATERIAL); +} + +RasterizerSceneRD::SkyMaterialData::~SkyMaterialData() { + if (uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(uniform_set)) { + RD::get_singleton()->free(uniform_set); + } + + if (uniform_buffer.is_valid()) { + RD::get_singleton()->free(uniform_buffer); + } +} + +RasterizerStorageRD::MaterialData *RasterizerSceneRD::_create_sky_material_func(SkyShaderData *p_shader) { + SkyMaterialData *material_data = memnew(SkyMaterialData); + material_data->shader_data = p_shader; + material_data->last_frame = false; + //update will happen later anyway so do nothing. + return material_data; +} + +RID RasterizerSceneRD::environment_create() { + + return environment_owner.make_rid(Environent()); +} + +void RasterizerSceneRD::environment_set_background(RID p_env, RS::EnvironmentBG p_bg) { + Environent *env = environment_owner.getornull(p_env); + ERR_FAIL_COND(!env); + env->background = p_bg; +} +void RasterizerSceneRD::environment_set_sky(RID p_env, RID p_sky) { + Environent *env = environment_owner.getornull(p_env); + ERR_FAIL_COND(!env); + env->sky = p_sky; +} +void RasterizerSceneRD::environment_set_sky_custom_fov(RID p_env, float p_scale) { + Environent *env = environment_owner.getornull(p_env); + ERR_FAIL_COND(!env); + env->sky_custom_fov = p_scale; +} +void RasterizerSceneRD::environment_set_sky_orientation(RID p_env, const Basis &p_orientation) { + Environent *env = environment_owner.getornull(p_env); + ERR_FAIL_COND(!env); + env->sky_orientation = p_orientation; +} +void RasterizerSceneRD::environment_set_bg_color(RID p_env, const Color &p_color) { + Environent *env = environment_owner.getornull(p_env); + ERR_FAIL_COND(!env); + env->bg_color = p_color; +} +void RasterizerSceneRD::environment_set_bg_energy(RID p_env, float p_energy) { + Environent *env = environment_owner.getornull(p_env); + ERR_FAIL_COND(!env); + env->bg_energy = p_energy; +} +void RasterizerSceneRD::environment_set_canvas_max_layer(RID p_env, int p_max_layer) { + Environent *env = environment_owner.getornull(p_env); + ERR_FAIL_COND(!env); + env->canvas_max_layer = p_max_layer; +} +void RasterizerSceneRD::environment_set_ambient_light(RID p_env, const Color &p_color, RS::EnvironmentAmbientSource p_ambient, float p_energy, float p_sky_contribution, RS::EnvironmentReflectionSource p_reflection_source, const Color &p_ao_color) { + Environent *env = environment_owner.getornull(p_env); + ERR_FAIL_COND(!env); + env->ambient_light = p_color; + env->ambient_source = p_ambient; + env->ambient_light_energy = p_energy; + env->ambient_sky_contribution = p_sky_contribution; + env->reflection_source = p_reflection_source; + env->ao_color = p_ao_color; +} + +RS::EnvironmentBG RasterizerSceneRD::environment_get_background(RID p_env) const { + Environent *env = environment_owner.getornull(p_env); + ERR_FAIL_COND_V(!env, RS::ENV_BG_MAX); + return env->background; +} +RID RasterizerSceneRD::environment_get_sky(RID p_env) const { + Environent *env = environment_owner.getornull(p_env); + ERR_FAIL_COND_V(!env, RID()); + return env->sky; +} +float RasterizerSceneRD::environment_get_sky_custom_fov(RID p_env) const { + Environent *env = environment_owner.getornull(p_env); + ERR_FAIL_COND_V(!env, 0); + return env->sky_custom_fov; +} +Basis RasterizerSceneRD::environment_get_sky_orientation(RID p_env) const { + Environent *env = environment_owner.getornull(p_env); + ERR_FAIL_COND_V(!env, Basis()); + return env->sky_orientation; +} +Color RasterizerSceneRD::environment_get_bg_color(RID p_env) const { + Environent *env = environment_owner.getornull(p_env); + ERR_FAIL_COND_V(!env, Color()); + return env->bg_color; +} +float RasterizerSceneRD::environment_get_bg_energy(RID p_env) const { + Environent *env = environment_owner.getornull(p_env); + ERR_FAIL_COND_V(!env, 0); + return env->bg_energy; +} +int RasterizerSceneRD::environment_get_canvas_max_layer(RID p_env) const { + Environent *env = environment_owner.getornull(p_env); + ERR_FAIL_COND_V(!env, 0); + return env->canvas_max_layer; +} +Color RasterizerSceneRD::environment_get_ambient_light_color(RID p_env) const { + Environent *env = environment_owner.getornull(p_env); + ERR_FAIL_COND_V(!env, Color()); + return env->ambient_light; +} +RS::EnvironmentAmbientSource RasterizerSceneRD::environment_get_ambient_light_ambient_source(RID p_env) const { + Environent *env = environment_owner.getornull(p_env); + ERR_FAIL_COND_V(!env, RS::ENV_AMBIENT_SOURCE_BG); + return env->ambient_source; +} +float RasterizerSceneRD::environment_get_ambient_light_ambient_energy(RID p_env) const { + Environent *env = environment_owner.getornull(p_env); + ERR_FAIL_COND_V(!env, 0); + return env->ambient_light_energy; +} +float RasterizerSceneRD::environment_get_ambient_sky_contribution(RID p_env) const { + Environent *env = environment_owner.getornull(p_env); + ERR_FAIL_COND_V(!env, 0); + return env->ambient_sky_contribution; +} +RS::EnvironmentReflectionSource RasterizerSceneRD::environment_get_reflection_source(RID p_env) const { + Environent *env = environment_owner.getornull(p_env); + ERR_FAIL_COND_V(!env, RS::ENV_REFLECTION_SOURCE_DISABLED); + return env->reflection_source; +} + +Color RasterizerSceneRD::environment_get_ao_color(RID p_env) const { + Environent *env = environment_owner.getornull(p_env); + ERR_FAIL_COND_V(!env, Color()); + return env->ao_color; +} + +void RasterizerSceneRD::environment_set_tonemap(RID p_env, RS::EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white, bool p_auto_exposure, float p_min_luminance, float p_max_luminance, float p_auto_exp_speed, float p_auto_exp_scale) { + Environent *env = environment_owner.getornull(p_env); + ERR_FAIL_COND(!env); + env->exposure = p_exposure; + env->tone_mapper = p_tone_mapper; + if (!env->auto_exposure && p_auto_exposure) { + env->auto_exposure_version = ++auto_exposure_counter; + } + env->auto_exposure = p_auto_exposure; + env->white = p_white; + env->min_luminance = p_min_luminance; + env->max_luminance = p_max_luminance; + env->auto_exp_speed = p_auto_exp_speed; + env->auto_exp_scale = p_auto_exp_scale; +} + +void RasterizerSceneRD::environment_set_glow(RID p_env, bool p_enable, int p_level_flags, float p_intensity, float p_strength, float p_mix, float p_bloom_threshold, RS::EnvironmentGlowBlendMode p_blend_mode, float p_hdr_bleed_threshold, float p_hdr_bleed_scale, float p_hdr_luminance_cap, bool p_bicubic_upscale) { + + Environent *env = environment_owner.getornull(p_env); + ERR_FAIL_COND(!env); + env->glow_enabled = p_enable; + env->glow_levels = p_level_flags; + env->glow_intensity = p_intensity; + env->glow_strength = p_strength; + env->glow_mix = p_mix; + env->glow_bloom = p_bloom_threshold; + env->glow_blend_mode = p_blend_mode; + env->glow_hdr_bleed_threshold = p_hdr_bleed_threshold; + env->glow_hdr_bleed_scale = p_hdr_bleed_scale; + env->glow_hdr_luminance_cap = p_hdr_luminance_cap; + env->glow_bicubic_upscale = p_bicubic_upscale; +} + +void RasterizerSceneRD::environment_set_ssao(RID p_env, bool p_enable, float p_radius, float p_intensity, float p_bias, float p_light_affect, float p_ao_channel_affect, RS::EnvironmentSSAOBlur p_blur, float p_bilateral_sharpness) { + + Environent *env = environment_owner.getornull(p_env); + ERR_FAIL_COND(!env); + + env->ssao_enabled = p_enable; + env->ssao_radius = p_radius; + env->ssao_intensity = p_intensity; + env->ssao_bias = p_bias; + env->ssao_direct_light_affect = p_light_affect; + env->ssao_ao_channel_affect = p_ao_channel_affect; + env->ssao_blur = p_blur; +} + +void RasterizerSceneRD::environment_set_ssao_quality(RS::EnvironmentSSAOQuality p_quality, bool p_half_size) { + ssao_quality = p_quality; + ssao_half_size = p_half_size; +} + +bool RasterizerSceneRD::environment_is_ssao_enabled(RID p_env) const { + + Environent *env = environment_owner.getornull(p_env); + ERR_FAIL_COND_V(!env, false); + return env->ssao_enabled; +} + +float RasterizerSceneRD::environment_get_ssao_ao_affect(RID p_env) const { + Environent *env = environment_owner.getornull(p_env); + ERR_FAIL_COND_V(!env, false); + return env->ssao_ao_channel_affect; +} +float RasterizerSceneRD::environment_get_ssao_light_affect(RID p_env) const { + Environent *env = environment_owner.getornull(p_env); + ERR_FAIL_COND_V(!env, false); + return env->ssao_direct_light_affect; +} + +bool RasterizerSceneRD::environment_is_ssr_enabled(RID p_env) const { + + Environent *env = environment_owner.getornull(p_env); + ERR_FAIL_COND_V(!env, false); + return false; +} + +bool RasterizerSceneRD::is_environment(RID p_env) const { + return environment_owner.owns(p_env); +} + +//////////////////////////////////////////////////////////// + +RID RasterizerSceneRD::reflection_atlas_create() { + + ReflectionAtlas ra; + ra.count = GLOBAL_GET("rendering/quality/reflection_atlas/reflection_count"); + ra.size = GLOBAL_GET("rendering/quality/reflection_atlas/reflection_size"); + + return reflection_atlas_owner.make_rid(ra); +} + +void RasterizerSceneRD::reflection_atlas_set_size(RID p_ref_atlas, int p_reflection_size, int p_reflection_count) { + + ReflectionAtlas *ra = reflection_atlas_owner.getornull(p_ref_atlas); + ERR_FAIL_COND(!ra); + + if (ra->size == p_reflection_size && ra->count == p_reflection_count) { + return; //no changes + } + + ra->size = p_reflection_size; + ra->count = p_reflection_count; + + if (ra->reflection.is_valid()) { + //clear and invalidate everything + RD::get_singleton()->free(ra->reflection); + ra->reflection = RID(); + RD::get_singleton()->free(ra->depth_buffer); + ra->depth_buffer = RID(); + + for (int i = 0; i < ra->reflections.size(); i++) { + _clear_reflection_data(ra->reflections.write[i].data); + if (ra->reflections[i].owner.is_null()) { + continue; + } + reflection_probe_release_atlas_index(ra->reflections[i].owner); + //rp->atlasindex clear + } + + ra->reflections.clear(); + } +} + +//////////////////////// +RID RasterizerSceneRD::reflection_probe_instance_create(RID p_probe) { + ReflectionProbeInstance rpi; + rpi.probe = p_probe; + return reflection_probe_instance_owner.make_rid(rpi); +} + +void RasterizerSceneRD::reflection_probe_instance_set_transform(RID p_instance, const Transform &p_transform) { + ReflectionProbeInstance *rpi = reflection_probe_instance_owner.getornull(p_instance); + ERR_FAIL_COND(!rpi); + + rpi->transform = p_transform; + rpi->dirty = true; +} + +void RasterizerSceneRD::reflection_probe_release_atlas_index(RID p_instance) { + + ReflectionProbeInstance *rpi = reflection_probe_instance_owner.getornull(p_instance); + ERR_FAIL_COND(!rpi); + + if (rpi->atlas.is_null()) { + return; //nothing to release + } + ReflectionAtlas *atlas = reflection_atlas_owner.getornull(rpi->atlas); + ERR_FAIL_COND(!atlas); + ERR_FAIL_INDEX(rpi->atlas_index, atlas->reflections.size()); + atlas->reflections.write[rpi->atlas_index].owner = RID(); + rpi->atlas_index = -1; + rpi->atlas = RID(); +} + +bool RasterizerSceneRD::reflection_probe_instance_needs_redraw(RID p_instance) { + + ReflectionProbeInstance *rpi = reflection_probe_instance_owner.getornull(p_instance); + ERR_FAIL_COND_V(!rpi, false); + + if (rpi->rendering) { + return false; + } + + if (rpi->dirty) { + return true; + } + + if (storage->reflection_probe_get_update_mode(rpi->probe) == RS::REFLECTION_PROBE_UPDATE_ALWAYS) { + return true; + } + + return rpi->atlas_index == -1; +} + +bool RasterizerSceneRD::reflection_probe_instance_has_reflection(RID p_instance) { + + ReflectionProbeInstance *rpi = reflection_probe_instance_owner.getornull(p_instance); + ERR_FAIL_COND_V(!rpi, false); + + return rpi->atlas.is_valid(); +} + +bool RasterizerSceneRD::reflection_probe_instance_begin_render(RID p_instance, RID p_reflection_atlas) { + + ReflectionAtlas *atlas = reflection_atlas_owner.getornull(p_reflection_atlas); + + ERR_FAIL_COND_V(!atlas, false); + + ReflectionProbeInstance *rpi = reflection_probe_instance_owner.getornull(p_instance); + ERR_FAIL_COND_V(!rpi, false); + + if (storage->reflection_probe_get_update_mode(rpi->probe) == RS::REFLECTION_PROBE_UPDATE_ALWAYS && atlas->reflection.is_valid() && atlas->size != 256) { + WARN_PRINT("ReflectionProbes set to UPDATE_ALWAYS must have an atlas size of 256. Please update the atlas size in the ProjectSettings."); + reflection_atlas_set_size(p_reflection_atlas, 256, atlas->count); + } + + if (storage->reflection_probe_get_update_mode(rpi->probe) == RS::REFLECTION_PROBE_UPDATE_ALWAYS && atlas->reflection.is_valid() && atlas->reflections[0].data.layers[0].mipmaps.size() != 8) { + // Invalidate reflection atlas, need to regenerate + RD::get_singleton()->free(atlas->reflection); + atlas->reflection = RID(); + + for (int i = 0; i < atlas->reflections.size(); i++) { + if (atlas->reflections[i].owner.is_null()) { + continue; + } + reflection_probe_release_atlas_index(atlas->reflections[i].owner); + } + + atlas->reflections.clear(); + } + + if (atlas->reflection.is_null()) { + int mipmaps = MIN(roughness_layers, Image::get_image_required_mipmaps(atlas->size, atlas->size, Image::FORMAT_RGBAH) + 1); + mipmaps = storage->reflection_probe_get_update_mode(rpi->probe) == RS::REFLECTION_PROBE_UPDATE_ALWAYS ? 8 : mipmaps; // always use 8 mipmaps with real time filtering + { + //reflection atlas was unused, create: + RD::TextureFormat tf; + tf.array_layers = 6 * atlas->count; + tf.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT; + tf.type = RD::TEXTURE_TYPE_CUBE_ARRAY; + tf.mipmaps = mipmaps; + tf.width = atlas->size; + tf.height = atlas->size; + tf.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT; + + atlas->reflection = RD::get_singleton()->texture_create(tf, RD::TextureView()); + } + { + + RD::TextureFormat tf; + tf.format = RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_D32_SFLOAT, RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) ? RD::DATA_FORMAT_D32_SFLOAT : RD::DATA_FORMAT_X8_D24_UNORM_PACK32; + tf.width = atlas->size; + tf.height = atlas->size; + tf.usage_bits = RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT; + atlas->depth_buffer = RD::get_singleton()->texture_create(tf, RD::TextureView()); + } + atlas->reflections.resize(atlas->count); + for (int i = 0; i < atlas->count; i++) { + _update_reflection_data(atlas->reflections.write[i].data, atlas->size, mipmaps, false, atlas->reflection, i * 6, storage->reflection_probe_get_update_mode(rpi->probe) == RS::REFLECTION_PROBE_UPDATE_ALWAYS); + for (int j = 0; j < 6; j++) { + Vector<RID> fb; + fb.push_back(atlas->reflections.write[i].data.layers[0].mipmaps[0].views[j]); + fb.push_back(atlas->depth_buffer); + atlas->reflections.write[i].fbs[j] = RD::get_singleton()->framebuffer_create(fb); + } + } + + Vector<RID> fb; + fb.push_back(atlas->depth_buffer); + atlas->depth_fb = RD::get_singleton()->framebuffer_create(fb); + } + + if (rpi->atlas_index == -1) { + for (int i = 0; i < atlas->reflections.size(); i++) { + if (atlas->reflections[i].owner.is_null()) { + rpi->atlas_index = i; + break; + } + } + //find the one used last + if (rpi->atlas_index == -1) { + //everything is in use, find the one least used via LRU + uint64_t pass_min = 0; + + for (int i = 0; i < atlas->reflections.size(); i++) { + ReflectionProbeInstance *rpi2 = reflection_probe_instance_owner.getornull(atlas->reflections[i].owner); + if (rpi2->last_pass < pass_min) { + pass_min = rpi2->last_pass; + rpi->atlas_index = i; + } + } + } + } + + rpi->atlas = p_reflection_atlas; + rpi->rendering = true; + rpi->dirty = false; + rpi->processing_layer = 1; + rpi->processing_side = 0; + + return true; +} + +bool RasterizerSceneRD::reflection_probe_instance_postprocess_step(RID p_instance) { + + ReflectionProbeInstance *rpi = reflection_probe_instance_owner.getornull(p_instance); + ERR_FAIL_COND_V(!rpi, false); + ERR_FAIL_COND_V(!rpi->rendering, false); + ERR_FAIL_COND_V(rpi->atlas.is_null(), false); + + ReflectionAtlas *atlas = reflection_atlas_owner.getornull(rpi->atlas); + if (!atlas || rpi->atlas_index == -1) { + //does not belong to an atlas anymore, cancel (was removed from atlas or atlas changed while rendering) + rpi->rendering = false; + return false; + } + + if (storage->reflection_probe_get_update_mode(rpi->probe) == RS::REFLECTION_PROBE_UPDATE_ALWAYS) { + // Using real time reflections, all roughness is done in one step + _create_reflection_fast_filter(atlas->reflections.write[rpi->atlas_index].data, false); + rpi->rendering = false; + rpi->processing_side = 0; + rpi->processing_layer = 1; + return true; + } + + if (rpi->processing_layer > 1) { + _create_reflection_importance_sample(atlas->reflections.write[rpi->atlas_index].data, false, 10, rpi->processing_layer); + rpi->processing_layer++; + if (rpi->processing_layer == atlas->reflections[rpi->atlas_index].data.layers[0].mipmaps.size()) { + rpi->rendering = false; + rpi->processing_side = 0; + rpi->processing_layer = 1; + return true; + } + return false; + + } else { + _create_reflection_importance_sample(atlas->reflections.write[rpi->atlas_index].data, false, rpi->processing_side, rpi->processing_layer); + } + + rpi->processing_side++; + if (rpi->processing_side == 6) { + rpi->processing_side = 0; + rpi->processing_layer++; + } + + return false; +} + +uint32_t RasterizerSceneRD::reflection_probe_instance_get_resolution(RID p_instance) { + ReflectionProbeInstance *rpi = reflection_probe_instance_owner.getornull(p_instance); + ERR_FAIL_COND_V(!rpi, 0); + + ReflectionAtlas *atlas = reflection_atlas_owner.getornull(rpi->atlas); + ERR_FAIL_COND_V(!atlas, 0); + return atlas->size; +} + +RID RasterizerSceneRD::reflection_probe_instance_get_framebuffer(RID p_instance, int p_index) { + ReflectionProbeInstance *rpi = reflection_probe_instance_owner.getornull(p_instance); + ERR_FAIL_COND_V(!rpi, RID()); + ERR_FAIL_INDEX_V(p_index, 6, RID()); + + ReflectionAtlas *atlas = reflection_atlas_owner.getornull(rpi->atlas); + ERR_FAIL_COND_V(!atlas, RID()); + return atlas->reflections[rpi->atlas_index].fbs[p_index]; +} + +RID RasterizerSceneRD::reflection_probe_instance_get_depth_framebuffer(RID p_instance, int p_index) { + ReflectionProbeInstance *rpi = reflection_probe_instance_owner.getornull(p_instance); + ERR_FAIL_COND_V(!rpi, RID()); + ERR_FAIL_INDEX_V(p_index, 6, RID()); + + ReflectionAtlas *atlas = reflection_atlas_owner.getornull(rpi->atlas); + ERR_FAIL_COND_V(!atlas, RID()); + return atlas->depth_fb; +} + +/////////////////////////////////////////////////////////// + +RID RasterizerSceneRD::shadow_atlas_create() { + + return shadow_atlas_owner.make_rid(ShadowAtlas()); +} + +void RasterizerSceneRD::shadow_atlas_set_size(RID p_atlas, int p_size) { + + ShadowAtlas *shadow_atlas = shadow_atlas_owner.getornull(p_atlas); + ERR_FAIL_COND(!shadow_atlas); + ERR_FAIL_COND(p_size < 0); + p_size = next_power_of_2(p_size); + + if (p_size == shadow_atlas->size) + return; + + // erasing atlas + if (shadow_atlas->depth.is_valid()) { + RD::get_singleton()->free(shadow_atlas->depth); + shadow_atlas->depth = RID(); + shadow_atlas->fb = RID(); + } + for (int i = 0; i < 4; i++) { + //clear subdivisions + shadow_atlas->quadrants[i].shadows.resize(0); + shadow_atlas->quadrants[i].shadows.resize(1 << shadow_atlas->quadrants[i].subdivision); + } + + //erase shadow atlas reference from lights + for (Map<RID, uint32_t>::Element *E = shadow_atlas->shadow_owners.front(); E; E = E->next()) { + LightInstance *li = light_instance_owner.getornull(E->key()); + ERR_CONTINUE(!li); + li->shadow_atlases.erase(p_atlas); + } + + //clear owners + shadow_atlas->shadow_owners.clear(); + + shadow_atlas->size = p_size; + + if (shadow_atlas->size) { + + RD::TextureFormat tf; + tf.format = RD::DATA_FORMAT_R32_SFLOAT; + tf.width = shadow_atlas->size; + tf.height = shadow_atlas->size; + tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; + + shadow_atlas->depth = RD::get_singleton()->texture_create(tf, RD::TextureView()); + + Vector<RID> fb; + fb.push_back(shadow_atlas->depth); + shadow_atlas->fb = RD::get_singleton()->framebuffer_create(fb); + } +} + +void RasterizerSceneRD::shadow_atlas_set_quadrant_subdivision(RID p_atlas, int p_quadrant, int p_subdivision) { + + ShadowAtlas *shadow_atlas = shadow_atlas_owner.getornull(p_atlas); + ERR_FAIL_COND(!shadow_atlas); + ERR_FAIL_INDEX(p_quadrant, 4); + ERR_FAIL_INDEX(p_subdivision, 16384); + + uint32_t subdiv = next_power_of_2(p_subdivision); + if (subdiv & 0xaaaaaaaa) { //sqrt(subdiv) must be integer + subdiv <<= 1; + } + + subdiv = int(Math::sqrt((float)subdiv)); + + //obtain the number that will be x*x + + if (shadow_atlas->quadrants[p_quadrant].subdivision == subdiv) + return; + + //erase all data from quadrant + for (int i = 0; i < shadow_atlas->quadrants[p_quadrant].shadows.size(); i++) { + + if (shadow_atlas->quadrants[p_quadrant].shadows[i].owner.is_valid()) { + shadow_atlas->shadow_owners.erase(shadow_atlas->quadrants[p_quadrant].shadows[i].owner); + LightInstance *li = light_instance_owner.getornull(shadow_atlas->quadrants[p_quadrant].shadows[i].owner); + ERR_CONTINUE(!li); + li->shadow_atlases.erase(p_atlas); + } + } + + shadow_atlas->quadrants[p_quadrant].shadows.resize(0); + shadow_atlas->quadrants[p_quadrant].shadows.resize(subdiv * subdiv); + shadow_atlas->quadrants[p_quadrant].subdivision = subdiv; + + //cache the smallest subdiv (for faster allocation in light update) + + shadow_atlas->smallest_subdiv = 1 << 30; + + for (int i = 0; i < 4; i++) { + if (shadow_atlas->quadrants[i].subdivision) { + shadow_atlas->smallest_subdiv = MIN(shadow_atlas->smallest_subdiv, shadow_atlas->quadrants[i].subdivision); + } + } + + if (shadow_atlas->smallest_subdiv == 1 << 30) { + shadow_atlas->smallest_subdiv = 0; + } + + //resort the size orders, simple bublesort for 4 elements.. + + int swaps = 0; + do { + swaps = 0; + + for (int i = 0; i < 3; i++) { + if (shadow_atlas->quadrants[shadow_atlas->size_order[i]].subdivision < shadow_atlas->quadrants[shadow_atlas->size_order[i + 1]].subdivision) { + SWAP(shadow_atlas->size_order[i], shadow_atlas->size_order[i + 1]); + swaps++; + } + } + } while (swaps > 0); +} + +bool RasterizerSceneRD::_shadow_atlas_find_shadow(ShadowAtlas *shadow_atlas, int *p_in_quadrants, int p_quadrant_count, int p_current_subdiv, uint64_t p_tick, int &r_quadrant, int &r_shadow) { + + for (int i = p_quadrant_count - 1; i >= 0; i--) { + + int qidx = p_in_quadrants[i]; + + if (shadow_atlas->quadrants[qidx].subdivision == (uint32_t)p_current_subdiv) { + return false; + } + + //look for an empty space + int sc = shadow_atlas->quadrants[qidx].shadows.size(); + ShadowAtlas::Quadrant::Shadow *sarr = shadow_atlas->quadrants[qidx].shadows.ptrw(); + + int found_free_idx = -1; //found a free one + int found_used_idx = -1; //found existing one, must steal it + uint64_t min_pass = 0; // pass of the existing one, try to use the least recently used one (LRU fashion) + + for (int j = 0; j < sc; j++) { + if (!sarr[j].owner.is_valid()) { + found_free_idx = j; + break; + } + + LightInstance *sli = light_instance_owner.getornull(sarr[j].owner); + ERR_CONTINUE(!sli); + + if (sli->last_scene_pass != scene_pass) { + + //was just allocated, don't kill it so soon, wait a bit.. + if (p_tick - sarr[j].alloc_tick < shadow_atlas_realloc_tolerance_msec) + continue; + + if (found_used_idx == -1 || sli->last_scene_pass < min_pass) { + found_used_idx = j; + min_pass = sli->last_scene_pass; + } + } + } + + if (found_free_idx == -1 && found_used_idx == -1) + continue; //nothing found + + if (found_free_idx == -1 && found_used_idx != -1) { + found_free_idx = found_used_idx; + } + + r_quadrant = qidx; + r_shadow = found_free_idx; + + return true; + } + + return false; +} + +bool RasterizerSceneRD::shadow_atlas_update_light(RID p_atlas, RID p_light_intance, float p_coverage, uint64_t p_light_version) { + + ShadowAtlas *shadow_atlas = shadow_atlas_owner.getornull(p_atlas); + ERR_FAIL_COND_V(!shadow_atlas, false); + + LightInstance *li = light_instance_owner.getornull(p_light_intance); + ERR_FAIL_COND_V(!li, false); + + if (shadow_atlas->size == 0 || shadow_atlas->smallest_subdiv == 0) { + return false; + } + + uint32_t quad_size = shadow_atlas->size >> 1; + int desired_fit = MIN(quad_size / shadow_atlas->smallest_subdiv, next_power_of_2(quad_size * p_coverage)); + + int valid_quadrants[4]; + int valid_quadrant_count = 0; + int best_size = -1; //best size found + int best_subdiv = -1; //subdiv for the best size + + //find the quadrants this fits into, and the best possible size it can fit into + for (int i = 0; i < 4; i++) { + int q = shadow_atlas->size_order[i]; + int sd = shadow_atlas->quadrants[q].subdivision; + if (sd == 0) + continue; //unused + + int max_fit = quad_size / sd; + + if (best_size != -1 && max_fit > best_size) + break; //too large + + valid_quadrants[valid_quadrant_count++] = q; + best_subdiv = sd; + + if (max_fit >= desired_fit) { + best_size = max_fit; + } + } + + ERR_FAIL_COND_V(valid_quadrant_count == 0, false); + + uint64_t tick = OS::get_singleton()->get_ticks_msec(); + + //see if it already exists + + if (shadow_atlas->shadow_owners.has(p_light_intance)) { + //it does! + uint32_t key = shadow_atlas->shadow_owners[p_light_intance]; + uint32_t q = (key >> ShadowAtlas::QUADRANT_SHIFT) & 0x3; + uint32_t s = key & ShadowAtlas::SHADOW_INDEX_MASK; + + bool should_realloc = shadow_atlas->quadrants[q].subdivision != (uint32_t)best_subdiv && (shadow_atlas->quadrants[q].shadows[s].alloc_tick - tick > shadow_atlas_realloc_tolerance_msec); + bool should_redraw = shadow_atlas->quadrants[q].shadows[s].version != p_light_version; + + if (!should_realloc) { + shadow_atlas->quadrants[q].shadows.write[s].version = p_light_version; + //already existing, see if it should redraw or it's just OK + return should_redraw; + } + + int new_quadrant, new_shadow; + + //find a better place + if (_shadow_atlas_find_shadow(shadow_atlas, valid_quadrants, valid_quadrant_count, shadow_atlas->quadrants[q].subdivision, tick, new_quadrant, new_shadow)) { + //found a better place! + ShadowAtlas::Quadrant::Shadow *sh = &shadow_atlas->quadrants[new_quadrant].shadows.write[new_shadow]; + if (sh->owner.is_valid()) { + //is taken, but is invalid, erasing it + shadow_atlas->shadow_owners.erase(sh->owner); + LightInstance *sli = light_instance_owner.getornull(sh->owner); + sli->shadow_atlases.erase(p_atlas); + } + + //erase previous + shadow_atlas->quadrants[q].shadows.write[s].version = 0; + shadow_atlas->quadrants[q].shadows.write[s].owner = RID(); + + sh->owner = p_light_intance; + sh->alloc_tick = tick; + sh->version = p_light_version; + li->shadow_atlases.insert(p_atlas); + + //make new key + key = new_quadrant << ShadowAtlas::QUADRANT_SHIFT; + key |= new_shadow; + //update it in map + shadow_atlas->shadow_owners[p_light_intance] = key; + //make it dirty, as it should redraw anyway + return true; + } + + //no better place for this shadow found, keep current + + //already existing, see if it should redraw or it's just OK + + shadow_atlas->quadrants[q].shadows.write[s].version = p_light_version; + + return should_redraw; + } + + int new_quadrant, new_shadow; + + //find a better place + if (_shadow_atlas_find_shadow(shadow_atlas, valid_quadrants, valid_quadrant_count, -1, tick, new_quadrant, new_shadow)) { + //found a better place! + ShadowAtlas::Quadrant::Shadow *sh = &shadow_atlas->quadrants[new_quadrant].shadows.write[new_shadow]; + if (sh->owner.is_valid()) { + //is taken, but is invalid, erasing it + shadow_atlas->shadow_owners.erase(sh->owner); + LightInstance *sli = light_instance_owner.getornull(sh->owner); + sli->shadow_atlases.erase(p_atlas); + } + + sh->owner = p_light_intance; + sh->alloc_tick = tick; + sh->version = p_light_version; + li->shadow_atlases.insert(p_atlas); + + //make new key + uint32_t key = new_quadrant << ShadowAtlas::QUADRANT_SHIFT; + key |= new_shadow; + //update it in map + shadow_atlas->shadow_owners[p_light_intance] = key; + //make it dirty, as it should redraw anyway + + return true; + } + + //no place to allocate this light, apologies + + return false; +} + +void RasterizerSceneRD::directional_shadow_atlas_set_size(int p_size) { + + p_size = nearest_power_of_2_templated(p_size); + + if (directional_shadow.size == p_size) { + return; + } + + directional_shadow.size = p_size; + + if (directional_shadow.depth.is_valid()) { + RD::get_singleton()->free(directional_shadow.depth); + directional_shadow.depth = RID(); + directional_shadow.fb = RID(); + } + + if (p_size > 0) { + + RD::TextureFormat tf; + tf.format = RD::DATA_FORMAT_R32_SFLOAT; + tf.width = p_size; + tf.height = p_size; + tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; + + directional_shadow.depth = RD::get_singleton()->texture_create(tf, RD::TextureView()); + Vector<RID> fb; + fb.push_back(directional_shadow.depth); + directional_shadow.fb = RD::get_singleton()->framebuffer_create(fb); + } + + _base_uniforms_changed(); +} + +void RasterizerSceneRD::set_directional_shadow_count(int p_count) { + + directional_shadow.light_count = p_count; + directional_shadow.current_light = 0; +} + +static Rect2i _get_directional_shadow_rect(int p_size, int p_shadow_count, int p_shadow_index) { + + int split_h = 1; + int split_v = 1; + + while (split_h * split_v < p_shadow_count) { + if (split_h == split_v) { + split_h <<= 1; + } else { + split_v <<= 1; + } + } + + Rect2i rect(0, 0, p_size, p_size); + rect.size.width /= split_h; + rect.size.height /= split_v; + + rect.position.x = rect.size.width * (p_shadow_index % split_h); + rect.position.y = rect.size.height * (p_shadow_index / split_h); + + return rect; +} + +int RasterizerSceneRD::get_directional_light_shadow_size(RID p_light_intance) { + + ERR_FAIL_COND_V(directional_shadow.light_count == 0, 0); + + Rect2i r = _get_directional_shadow_rect(directional_shadow.size, directional_shadow.light_count, 0); + + LightInstance *light_instance = light_instance_owner.getornull(p_light_intance); + ERR_FAIL_COND_V(!light_instance, 0); + + switch (storage->light_directional_get_shadow_mode(light_instance->light)) { + case RS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL: + break; //none + case RS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_2_SPLITS: r.size.height /= 2; break; + case RS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_4_SPLITS: r.size /= 2; break; + } + + return MAX(r.size.width, r.size.height); +} + +////////////////////////////////////////////////// + +RID RasterizerSceneRD::camera_effects_create() { + + return camera_effects_owner.make_rid(CameraEffects()); +} + +void RasterizerSceneRD::camera_effects_set_dof_blur_quality(RS::DOFBlurQuality p_quality, bool p_use_jitter) { + + dof_blur_quality = p_quality; + dof_blur_use_jitter = p_use_jitter; +} + +void RasterizerSceneRD::camera_effects_set_dof_blur_bokeh_shape(RS::DOFBokehShape p_shape) { + + dof_blur_bokeh_shape = p_shape; +} + +void RasterizerSceneRD::camera_effects_set_dof_blur(RID p_camera_effects, bool p_far_enable, float p_far_distance, float p_far_transition, bool p_near_enable, float p_near_distance, float p_near_transition, float p_amount) { + CameraEffects *camfx = camera_effects_owner.getornull(p_camera_effects); + ERR_FAIL_COND(!camfx); + + camfx->dof_blur_far_enabled = p_far_enable; + camfx->dof_blur_far_distance = p_far_distance; + camfx->dof_blur_far_transition = p_far_transition; + + camfx->dof_blur_near_enabled = p_near_enable; + camfx->dof_blur_near_distance = p_near_distance; + camfx->dof_blur_near_transition = p_near_transition; + + camfx->dof_blur_amount = p_amount; +} + +void RasterizerSceneRD::camera_effects_set_custom_exposure(RID p_camera_effects, bool p_enable, float p_exposure) { + + CameraEffects *camfx = camera_effects_owner.getornull(p_camera_effects); + ERR_FAIL_COND(!camfx); + + camfx->override_exposure_enabled = p_enable; + camfx->override_exposure = p_exposure; +} + +RID RasterizerSceneRD::light_instance_create(RID p_light) { + + RID li = light_instance_owner.make_rid(LightInstance()); + + LightInstance *light_instance = light_instance_owner.getornull(li); + + light_instance->self = li; + light_instance->light = p_light; + light_instance->light_type = storage->light_get_type(p_light); + + return li; +} + +void RasterizerSceneRD::light_instance_set_transform(RID p_light_instance, const Transform &p_transform) { + + LightInstance *light_instance = light_instance_owner.getornull(p_light_instance); + ERR_FAIL_COND(!light_instance); + + light_instance->transform = p_transform; +} + +void RasterizerSceneRD::light_instance_set_shadow_transform(RID p_light_instance, const CameraMatrix &p_projection, const Transform &p_transform, float p_far, float p_split, int p_pass, float p_bias_scale) { + + LightInstance *light_instance = light_instance_owner.getornull(p_light_instance); + ERR_FAIL_COND(!light_instance); + + if (storage->light_get_type(light_instance->light) != RS::LIGHT_DIRECTIONAL) { + p_pass = 0; + } + + ERR_FAIL_INDEX(p_pass, 4); + + light_instance->shadow_transform[p_pass].camera = p_projection; + light_instance->shadow_transform[p_pass].transform = p_transform; + light_instance->shadow_transform[p_pass].farplane = p_far; + light_instance->shadow_transform[p_pass].split = p_split; + light_instance->shadow_transform[p_pass].bias_scale = p_bias_scale; +} + +void RasterizerSceneRD::light_instance_mark_visible(RID p_light_instance) { + + LightInstance *light_instance = light_instance_owner.getornull(p_light_instance); + ERR_FAIL_COND(!light_instance); + + light_instance->last_scene_pass = scene_pass; +} + +RasterizerSceneRD::ShadowCubemap *RasterizerSceneRD::_get_shadow_cubemap(int p_size) { + + if (!shadow_cubemaps.has(p_size)) { + + ShadowCubemap sc; + { + RD::TextureFormat tf; + tf.format = RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_D32_SFLOAT, RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) ? RD::DATA_FORMAT_D32_SFLOAT : RD::DATA_FORMAT_X8_D24_UNORM_PACK32; + tf.width = p_size; + tf.height = p_size; + tf.type = RD::TEXTURE_TYPE_CUBE; + tf.array_layers = 6; + tf.usage_bits = RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT; + sc.cubemap = RD::get_singleton()->texture_create(tf, RD::TextureView()); + } + + for (int i = 0; i < 6; i++) { + RID side_texture = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), sc.cubemap, i, 0); + Vector<RID> fbtex; + fbtex.push_back(side_texture); + sc.side_fb[i] = RD::get_singleton()->framebuffer_create(fbtex); + } + + shadow_cubemaps[p_size] = sc; + } + + return &shadow_cubemaps[p_size]; +} + +RasterizerSceneRD::ShadowMap *RasterizerSceneRD::_get_shadow_map(const Size2i &p_size) { + + if (!shadow_maps.has(p_size)) { + + ShadowMap sm; + { + RD::TextureFormat tf; + tf.format = RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_D32_SFLOAT, RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) ? RD::DATA_FORMAT_D32_SFLOAT : RD::DATA_FORMAT_X8_D24_UNORM_PACK32; + tf.width = p_size.width; + tf.height = p_size.height; + tf.usage_bits = RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT; + + sm.depth = RD::get_singleton()->texture_create(tf, RD::TextureView()); + } + + Vector<RID> fbtex; + fbtex.push_back(sm.depth); + sm.fb = RD::get_singleton()->framebuffer_create(fbtex); + + shadow_maps[p_size] = sm; + } + + return &shadow_maps[p_size]; +} +///////////////////////////////// + +RID RasterizerSceneRD::gi_probe_instance_create(RID p_base) { + //find a free slot + int index = -1; + for (int i = 0; i < gi_probe_slots.size(); i++) { + if (gi_probe_slots[i] == RID()) { + index = i; + break; + } + } + + ERR_FAIL_COND_V(index == -1, RID()); + + GIProbeInstance gi_probe; + gi_probe.slot = index; + gi_probe.probe = p_base; + RID rid = gi_probe_instance_owner.make_rid(gi_probe); + gi_probe_slots.write[index] = rid; + + return rid; +} + +void RasterizerSceneRD::gi_probe_instance_set_transform_to_data(RID p_probe, const Transform &p_xform) { + + GIProbeInstance *gi_probe = gi_probe_instance_owner.getornull(p_probe); + ERR_FAIL_COND(!gi_probe); + + gi_probe->transform = p_xform; +} + +bool RasterizerSceneRD::gi_probe_needs_update(RID p_probe) const { + GIProbeInstance *gi_probe = gi_probe_instance_owner.getornull(p_probe); + ERR_FAIL_COND_V(!gi_probe, false); + + //return true; + return gi_probe->last_probe_version != storage->gi_probe_get_version(gi_probe->probe); +} + +void RasterizerSceneRD::gi_probe_update(RID p_probe, bool p_update_light_instances, const Vector<RID> &p_light_instances, int p_dynamic_object_count, InstanceBase **p_dynamic_objects) { + + GIProbeInstance *gi_probe = gi_probe_instance_owner.getornull(p_probe); + ERR_FAIL_COND(!gi_probe); + + uint32_t data_version = storage->gi_probe_get_data_version(gi_probe->probe); + + // (RE)CREATE IF NEEDED + + if (gi_probe->last_probe_data_version != data_version) { + //need to re-create everything + if (gi_probe->texture.is_valid()) { + RD::get_singleton()->free(gi_probe->texture); + if (gi_probe_use_anisotropy) { + RD::get_singleton()->free(gi_probe->anisotropy_r16[0]); + RD::get_singleton()->free(gi_probe->anisotropy_r16[1]); + } + RD::get_singleton()->free(gi_probe->write_buffer); + gi_probe->mipmaps.clear(); + } + + for (int i = 0; i < gi_probe->dynamic_maps.size(); i++) { + RD::get_singleton()->free(gi_probe->dynamic_maps[i].texture); + RD::get_singleton()->free(gi_probe->dynamic_maps[i].depth); + } + + gi_probe->dynamic_maps.clear(); + + Vector3i octree_size = storage->gi_probe_get_octree_size(gi_probe->probe); + + if (octree_size != Vector3i()) { + //can create a 3D texture + Vector<int> levels = storage->gi_probe_get_level_counts(gi_probe->probe); + + RD::TextureFormat tf; + tf.format = RD::DATA_FORMAT_R8G8B8A8_UNORM; + tf.width = octree_size.x; + tf.height = octree_size.y; + tf.depth = octree_size.z; + tf.type = RD::TEXTURE_TYPE_3D; + tf.mipmaps = levels.size(); + + tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT; + + gi_probe->texture = RD::get_singleton()->texture_create(tf, RD::TextureView()); + + RD::get_singleton()->texture_clear(gi_probe->texture, Color(0, 0, 0, 0), 0, levels.size(), 0, 1, false); + + if (gi_probe_use_anisotropy) { + tf.format = RD::DATA_FORMAT_R16_UINT; + tf.shareable_formats.push_back(RD::DATA_FORMAT_R16_UINT); + tf.shareable_formats.push_back(RD::DATA_FORMAT_R5G6B5_UNORM_PACK16); + + //need to create R16 first, else driver does not like the storage bit for compute.. + gi_probe->anisotropy_r16[0] = RD::get_singleton()->texture_create(tf, RD::TextureView()); + gi_probe->anisotropy_r16[1] = RD::get_singleton()->texture_create(tf, RD::TextureView()); + + RD::TextureView tv; + tv.format_override = RD::DATA_FORMAT_R5G6B5_UNORM_PACK16; + gi_probe->anisotropy[0] = RD::get_singleton()->texture_create_shared(tv, gi_probe->anisotropy_r16[0]); + gi_probe->anisotropy[1] = RD::get_singleton()->texture_create_shared(tv, gi_probe->anisotropy_r16[1]); + + RD::get_singleton()->texture_clear(gi_probe->anisotropy[0], Color(0, 0, 0, 0), 0, levels.size(), 0, 1, false); + RD::get_singleton()->texture_clear(gi_probe->anisotropy[1], Color(0, 0, 0, 0), 0, levels.size(), 0, 1, false); + } + + { + int total_elements = 0; + for (int i = 0; i < levels.size(); i++) { + total_elements += levels[i]; + } + + if (gi_probe_use_anisotropy) { + total_elements *= 6; + } + + gi_probe->write_buffer = RD::get_singleton()->storage_buffer_create(total_elements * 16); + } + + for (int i = 0; i < levels.size(); i++) { + GIProbeInstance::Mipmap mipmap; + mipmap.texture = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), gi_probe->texture, 0, i, RD::TEXTURE_SLICE_3D); + if (gi_probe_use_anisotropy) { + RD::TextureView tv; + tv.format_override = RD::DATA_FORMAT_R16_UINT; + mipmap.anisotropy[0] = RD::get_singleton()->texture_create_shared_from_slice(tv, gi_probe->anisotropy[0], 0, i, RD::TEXTURE_SLICE_3D); + mipmap.anisotropy[1] = RD::get_singleton()->texture_create_shared_from_slice(tv, gi_probe->anisotropy[1], 0, i, RD::TEXTURE_SLICE_3D); + } + + mipmap.level = levels.size() - i - 1; + mipmap.cell_offset = 0; + for (uint32_t j = 0; j < mipmap.level; j++) { + mipmap.cell_offset += levels[j]; + } + mipmap.cell_count = levels[mipmap.level]; + + Vector<RD::Uniform> uniforms; + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER; + u.binding = 1; + u.ids.push_back(storage->gi_probe_get_octree_buffer(gi_probe->probe)); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER; + u.binding = 2; + u.ids.push_back(storage->gi_probe_get_data_buffer(gi_probe->probe)); + uniforms.push_back(u); + } + + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER; + u.binding = 4; + u.ids.push_back(gi_probe->write_buffer); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_TEXTURE; + u.binding = 9; + u.ids.push_back(storage->gi_probe_get_sdf_texture(gi_probe->probe)); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_SAMPLER; + u.binding = 10; + u.ids.push_back(storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED)); + uniforms.push_back(u); + } + + { + Vector<RD::Uniform> copy_uniforms = uniforms; + if (i == 0) { + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; + u.binding = 3; + u.ids.push_back(gi_probe_lights_uniform); + copy_uniforms.push_back(u); + } + + mipmap.uniform_set = RD::get_singleton()->uniform_set_create(copy_uniforms, giprobe_lighting_shader_version_shaders[GI_PROBE_SHADER_VERSION_COMPUTE_LIGHT], 0); + + copy_uniforms = uniforms; //restore + + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_TEXTURE; + u.binding = 5; + u.ids.push_back(gi_probe->texture); + copy_uniforms.push_back(u); + } + + if (gi_probe_use_anisotropy) { + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_TEXTURE; + u.binding = 7; + u.ids.push_back(gi_probe->anisotropy[0]); + copy_uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_TEXTURE; + u.binding = 8; + u.ids.push_back(gi_probe->anisotropy[1]); + copy_uniforms.push_back(u); + } + } + + mipmap.second_bounce_uniform_set = RD::get_singleton()->uniform_set_create(copy_uniforms, giprobe_lighting_shader_version_shaders[GI_PROBE_SHADER_VERSION_COMPUTE_SECOND_BOUNCE], 0); + } else { + mipmap.uniform_set = RD::get_singleton()->uniform_set_create(copy_uniforms, giprobe_lighting_shader_version_shaders[GI_PROBE_SHADER_VERSION_COMPUTE_MIPMAP], 0); + } + } + + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 5; + u.ids.push_back(mipmap.texture); + uniforms.push_back(u); + } + + if (gi_probe_use_anisotropy) { + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 6; + u.ids.push_back(mipmap.anisotropy[0]); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 7; + u.ids.push_back(mipmap.anisotropy[1]); + uniforms.push_back(u); + } + } + + mipmap.write_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, giprobe_lighting_shader_version_shaders[GI_PROBE_SHADER_VERSION_WRITE_TEXTURE], 0); + + gi_probe->mipmaps.push_back(mipmap); + } + + { + uint32_t dynamic_map_size = MAX(MAX(octree_size.x, octree_size.y), octree_size.z); + uint32_t oversample = nearest_power_of_2_templated(4); + int mipmap_index = 0; + + while (mipmap_index < gi_probe->mipmaps.size()) { + GIProbeInstance::DynamicMap dmap; + + if (oversample > 0) { + dmap.size = dynamic_map_size * (1 << oversample); + dmap.mipmap = -1; + oversample--; + } else { + dmap.size = dynamic_map_size >> mipmap_index; + dmap.mipmap = mipmap_index; + mipmap_index++; + } + + RD::TextureFormat dtf; + dtf.width = dmap.size; + dtf.height = dmap.size; + dtf.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT; + dtf.usage_bits = RD::TEXTURE_USAGE_STORAGE_BIT; + + if (gi_probe->dynamic_maps.size() == 0) { + dtf.usage_bits |= RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; + } + dmap.texture = RD::get_singleton()->texture_create(dtf, RD::TextureView()); + + if (gi_probe->dynamic_maps.size() == 0) { + //render depth for first one + dtf.format = RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_D32_SFLOAT, RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) ? RD::DATA_FORMAT_D32_SFLOAT : RD::DATA_FORMAT_X8_D24_UNORM_PACK32; + dtf.usage_bits = RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + dmap.fb_depth = RD::get_singleton()->texture_create(dtf, RD::TextureView()); + } + + //just use depth as-is + dtf.format = RD::DATA_FORMAT_R32_SFLOAT; + dtf.usage_bits = RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; + + dmap.depth = RD::get_singleton()->texture_create(dtf, RD::TextureView()); + + if (gi_probe->dynamic_maps.size() == 0) { + + dtf.format = RD::DATA_FORMAT_R8G8B8A8_UNORM; + dtf.usage_bits = RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; + dmap.albedo = RD::get_singleton()->texture_create(dtf, RD::TextureView()); + dmap.normal = RD::get_singleton()->texture_create(dtf, RD::TextureView()); + dmap.orm = RD::get_singleton()->texture_create(dtf, RD::TextureView()); + + Vector<RID> fb; + fb.push_back(dmap.albedo); + fb.push_back(dmap.normal); + fb.push_back(dmap.orm); + fb.push_back(dmap.texture); //emission + fb.push_back(dmap.depth); + fb.push_back(dmap.fb_depth); + + dmap.fb = RD::get_singleton()->framebuffer_create(fb); + + { + Vector<RD::Uniform> uniforms; + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; + u.binding = 3; + u.ids.push_back(gi_probe_lights_uniform); + uniforms.push_back(u); + } + + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 5; + u.ids.push_back(dmap.albedo); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 6; + u.ids.push_back(dmap.normal); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 7; + u.ids.push_back(dmap.orm); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_TEXTURE; + u.binding = 8; + u.ids.push_back(dmap.fb_depth); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_TEXTURE; + u.binding = 9; + u.ids.push_back(storage->gi_probe_get_sdf_texture(gi_probe->probe)); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_SAMPLER; + u.binding = 10; + u.ids.push_back(storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED)); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 11; + u.ids.push_back(dmap.texture); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 12; + u.ids.push_back(dmap.depth); + uniforms.push_back(u); + } + + dmap.uniform_set = RD::get_singleton()->uniform_set_create(uniforms, giprobe_lighting_shader_version_shaders[GI_PROBE_SHADER_VERSION_DYNAMIC_OBJECT_LIGHTING], 0); + } + } else { + bool plot = dmap.mipmap >= 0; + bool write = dmap.mipmap < (gi_probe->mipmaps.size() - 1); + + Vector<RD::Uniform> uniforms; + + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 5; + u.ids.push_back(gi_probe->dynamic_maps[gi_probe->dynamic_maps.size() - 1].texture); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 6; + u.ids.push_back(gi_probe->dynamic_maps[gi_probe->dynamic_maps.size() - 1].depth); + uniforms.push_back(u); + } + + if (write) { + + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 7; + u.ids.push_back(dmap.texture); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 8; + u.ids.push_back(dmap.depth); + uniforms.push_back(u); + } + } + + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_TEXTURE; + u.binding = 9; + u.ids.push_back(storage->gi_probe_get_sdf_texture(gi_probe->probe)); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_SAMPLER; + u.binding = 10; + u.ids.push_back(storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED)); + uniforms.push_back(u); + } + + if (plot) { + + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 11; + u.ids.push_back(gi_probe->mipmaps[dmap.mipmap].texture); + uniforms.push_back(u); + } + if (gi_probe_is_anisotropic()) { + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 12; + u.ids.push_back(gi_probe->mipmaps[dmap.mipmap].anisotropy[0]); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 13; + u.ids.push_back(gi_probe->mipmaps[dmap.mipmap].anisotropy[1]); + uniforms.push_back(u); + } + } + } + + dmap.uniform_set = RD::get_singleton()->uniform_set_create(uniforms, giprobe_lighting_shader_version_shaders[(write && plot) ? GI_PROBE_SHADER_VERSION_DYNAMIC_SHRINK_WRITE_PLOT : write ? GI_PROBE_SHADER_VERSION_DYNAMIC_SHRINK_WRITE : GI_PROBE_SHADER_VERSION_DYNAMIC_SHRINK_PLOT], 0); + } + + gi_probe->dynamic_maps.push_back(dmap); + } + } + } + + gi_probe->last_probe_data_version = data_version; + p_update_light_instances = true; //just in case + + _base_uniforms_changed(); + } + + // UDPDATE TIME + + if (gi_probe->has_dynamic_object_data) { + //if it has dynamic object data, it needs to be cleared + RD::get_singleton()->texture_clear(gi_probe->texture, Color(0, 0, 0, 0), 0, gi_probe->mipmaps.size(), 0, 1, true); + if (gi_probe_is_anisotropic()) { + RD::get_singleton()->texture_clear(gi_probe->anisotropy[0], Color(0, 0, 0, 0), 0, gi_probe->mipmaps.size(), 0, 1, true); + RD::get_singleton()->texture_clear(gi_probe->anisotropy[1], Color(0, 0, 0, 0), 0, gi_probe->mipmaps.size(), 0, 1, true); + } + } + + uint32_t light_count = 0; + + if (p_update_light_instances || p_dynamic_object_count > 0) { + + light_count = MIN(gi_probe_max_lights, (uint32_t)p_light_instances.size()); + + { + Transform to_cell = storage->gi_probe_get_to_cell_xform(gi_probe->probe); + Transform to_probe_xform = (gi_probe->transform * to_cell.affine_inverse()).affine_inverse(); + //update lights + + for (uint32_t i = 0; i < light_count; i++) { + GIProbeLight &l = gi_probe_lights[i]; + RID light_instance = p_light_instances[i]; + RID light = light_instance_get_base_light(light_instance); + + l.type = storage->light_get_type(light); + l.attenuation = storage->light_get_param(light, RS::LIGHT_PARAM_ATTENUATION); + l.energy = storage->light_get_param(light, RS::LIGHT_PARAM_ENERGY) * storage->light_get_param(light, RS::LIGHT_PARAM_INDIRECT_ENERGY); + l.radius = to_cell.basis.xform(Vector3(storage->light_get_param(light, RS::LIGHT_PARAM_RANGE), 0, 0)).length(); + Color color = storage->light_get_color(light).to_linear(); + l.color[0] = color.r; + l.color[1] = color.g; + l.color[2] = color.b; + + l.spot_angle_radians = Math::deg2rad(storage->light_get_param(light, RS::LIGHT_PARAM_SPOT_ANGLE)); + l.spot_attenuation = storage->light_get_param(light, RS::LIGHT_PARAM_SPOT_ATTENUATION); + + Transform xform = light_instance_get_base_transform(light_instance); + + Vector3 pos = to_probe_xform.xform(xform.origin); + Vector3 dir = to_probe_xform.basis.xform(-xform.basis.get_axis(2)).normalized(); + + l.position[0] = pos.x; + l.position[1] = pos.y; + l.position[2] = pos.z; + + l.direction[0] = dir.x; + l.direction[1] = dir.y; + l.direction[2] = dir.z; + + l.has_shadow = storage->light_has_shadow(light); + } + + RD::get_singleton()->buffer_update(gi_probe_lights_uniform, 0, sizeof(GIProbeLight) * light_count, gi_probe_lights, true); + } + } + + if (gi_probe->has_dynamic_object_data || p_update_light_instances || p_dynamic_object_count) { + // PROCESS MIPMAPS + if (gi_probe->mipmaps.size()) { + //can update mipmaps + + Vector3i probe_size = storage->gi_probe_get_octree_size(gi_probe->probe); + + GIProbePushConstant push_constant; + + push_constant.limits[0] = probe_size.x; + push_constant.limits[1] = probe_size.y; + push_constant.limits[2] = probe_size.z; + push_constant.stack_size = gi_probe->mipmaps.size(); + push_constant.emission_scale = 1.0; + push_constant.propagation = storage->gi_probe_get_propagation(gi_probe->probe); + push_constant.dynamic_range = storage->gi_probe_get_dynamic_range(gi_probe->probe); + push_constant.light_count = light_count; + push_constant.aniso_strength = storage->gi_probe_get_anisotropy_strength(gi_probe->probe); + + /* print_line("probe update to version " + itos(gi_probe->last_probe_version)); + print_line("propagation " + rtos(push_constant.propagation)); + print_line("dynrange " + rtos(push_constant.dynamic_range)); + */ + RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); + + int passes; + if (p_update_light_instances) { + passes = storage->gi_probe_is_using_two_bounces(gi_probe->probe) ? 2 : 1; + } else { + passes = 1; //only re-blitting is necessary + } + int wg_size = 64; + int wg_limit_x = RD::get_singleton()->limit_get(RD::LIMIT_MAX_COMPUTE_WORKGROUP_COUNT_X); + + for (int pass = 0; pass < passes; pass++) { + + if (p_update_light_instances) { + + for (int i = 0; i < gi_probe->mipmaps.size(); i++) { + if (i == 0) { + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, giprobe_lighting_shader_version_pipelines[pass == 0 ? GI_PROBE_SHADER_VERSION_COMPUTE_LIGHT : GI_PROBE_SHADER_VERSION_COMPUTE_SECOND_BOUNCE]); + } else if (i == 1) { + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, giprobe_lighting_shader_version_pipelines[GI_PROBE_SHADER_VERSION_COMPUTE_MIPMAP]); + } + + if (pass == 1 || i > 0) { + RD::get_singleton()->compute_list_add_barrier(compute_list); //wait til previous step is done + } + if (pass == 0 || i > 0) { + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, gi_probe->mipmaps[i].uniform_set, 0); + } else { + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, gi_probe->mipmaps[i].second_bounce_uniform_set, 0); + } + + push_constant.cell_offset = gi_probe->mipmaps[i].cell_offset; + push_constant.cell_count = gi_probe->mipmaps[i].cell_count; + + int wg_todo = (gi_probe->mipmaps[i].cell_count - 1) / wg_size + 1; + while (wg_todo) { + int wg_count = MIN(wg_todo, wg_limit_x); + RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(GIProbePushConstant)); + RD::get_singleton()->compute_list_dispatch(compute_list, wg_count, 1, 1); + wg_todo -= wg_count; + push_constant.cell_offset += wg_count * wg_size; + } + } + + RD::get_singleton()->compute_list_add_barrier(compute_list); //wait til previous step is done + } + + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, giprobe_lighting_shader_version_pipelines[GI_PROBE_SHADER_VERSION_WRITE_TEXTURE]); + + for (int i = 0; i < gi_probe->mipmaps.size(); i++) { + + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, gi_probe->mipmaps[i].write_uniform_set, 0); + + push_constant.cell_offset = gi_probe->mipmaps[i].cell_offset; + push_constant.cell_count = gi_probe->mipmaps[i].cell_count; + + int wg_todo = (gi_probe->mipmaps[i].cell_count - 1) / wg_size + 1; + while (wg_todo) { + int wg_count = MIN(wg_todo, wg_limit_x); + RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(GIProbePushConstant)); + RD::get_singleton()->compute_list_dispatch(compute_list, wg_count, 1, 1); + wg_todo -= wg_count; + push_constant.cell_offset += wg_count * wg_size; + } + } + } + + RD::get_singleton()->compute_list_end(); + } + } + + gi_probe->has_dynamic_object_data = false; //clear until dynamic object data is used again + + if (p_dynamic_object_count && gi_probe->dynamic_maps.size()) { + + Vector3i octree_size = storage->gi_probe_get_octree_size(gi_probe->probe); + int multiplier = gi_probe->dynamic_maps[0].size / MAX(MAX(octree_size.x, octree_size.y), octree_size.z); + + Transform oversample_scale; + oversample_scale.basis.scale(Vector3(multiplier, multiplier, multiplier)); + + Transform to_cell = oversample_scale * storage->gi_probe_get_to_cell_xform(gi_probe->probe); + Transform to_world_xform = gi_probe->transform * to_cell.affine_inverse(); + Transform to_probe_xform = to_world_xform.affine_inverse(); + + AABB probe_aabb(Vector3(), octree_size); + + //this could probably be better parallelized in compute.. + for (int i = 0; i < p_dynamic_object_count; i++) { + + InstanceBase *instance = p_dynamic_objects[i]; + //not used, so clear + instance->depth_layer = 0; + instance->depth = 0; + + //transform aabb to giprobe + AABB aabb = (to_probe_xform * instance->transform).xform(instance->aabb); + + //this needs to wrap to grid resolution to avoid jitter + //also extend margin a bit just in case + Vector3i begin = aabb.position - Vector3i(1, 1, 1); + Vector3i end = aabb.position + aabb.size + Vector3i(1, 1, 1); + + for (int j = 0; j < 3; j++) { + if ((end[j] - begin[j]) & 1) { + end[j]++; //for half extents split, it needs to be even + } + begin[j] = MAX(begin[j], 0); + end[j] = MIN(end[j], octree_size[j] * multiplier); + } + + //aabb = aabb.intersection(probe_aabb); //intersect + aabb.position = begin; + aabb.size = end - begin; + + //print_line("aabb: " + aabb); + + for (int j = 0; j < 6; j++) { + + //if (j != 0 && j != 3) { + // continue; + //} + static const Vector3 render_z[6] = { + Vector3(1, 0, 0), + Vector3(0, 1, 0), + Vector3(0, 0, 1), + Vector3(-1, 0, 0), + Vector3(0, -1, 0), + Vector3(0, 0, -1), + }; + static const Vector3 render_up[6] = { + Vector3(0, 1, 0), + Vector3(0, 0, 1), + Vector3(0, 1, 0), + Vector3(0, 1, 0), + Vector3(0, 0, 1), + Vector3(0, 1, 0), + }; + + Vector3 render_dir = render_z[j]; + Vector3 up_dir = render_up[j]; + + Vector3 center = aabb.position + aabb.size * 0.5; + Transform xform; + xform.set_look_at(center - aabb.size * 0.5 * render_dir, center, up_dir); + + Vector3 x_dir = xform.basis.get_axis(0).abs(); + int x_axis = int(Vector3(0, 1, 2).dot(x_dir)); + Vector3 y_dir = xform.basis.get_axis(1).abs(); + int y_axis = int(Vector3(0, 1, 2).dot(y_dir)); + Vector3 z_dir = -xform.basis.get_axis(2); + int z_axis = int(Vector3(0, 1, 2).dot(z_dir.abs())); + + Rect2i rect(aabb.position[x_axis], aabb.position[y_axis], aabb.size[x_axis], aabb.size[y_axis]); + bool x_flip = bool(Vector3(1, 1, 1).dot(xform.basis.get_axis(0)) < 0); + bool y_flip = bool(Vector3(1, 1, 1).dot(xform.basis.get_axis(1)) < 0); + bool z_flip = bool(Vector3(1, 1, 1).dot(xform.basis.get_axis(2)) > 0); + + CameraMatrix cm; + cm.set_orthogonal(-rect.size.width / 2, rect.size.width / 2, -rect.size.height / 2, rect.size.height / 2, 0.0001, aabb.size[z_axis]); + + _render_material(to_world_xform * xform, cm, true, &instance, 1, gi_probe->dynamic_maps[0].fb, Rect2i(Vector2i(), rect.size)); + + GIProbeDynamicPushConstant push_constant; + zeromem(&push_constant, sizeof(GIProbeDynamicPushConstant)); + push_constant.limits[0] = octree_size.x; + push_constant.limits[1] = octree_size.y; + push_constant.limits[2] = octree_size.z; + push_constant.light_count = p_light_instances.size(); + push_constant.x_dir[0] = x_dir[0]; + push_constant.x_dir[1] = x_dir[1]; + push_constant.x_dir[2] = x_dir[2]; + push_constant.y_dir[0] = y_dir[0]; + push_constant.y_dir[1] = y_dir[1]; + push_constant.y_dir[2] = y_dir[2]; + push_constant.z_dir[0] = z_dir[0]; + push_constant.z_dir[1] = z_dir[1]; + push_constant.z_dir[2] = z_dir[2]; + push_constant.z_base = xform.origin[z_axis]; + push_constant.z_sign = (z_flip ? -1.0 : 1.0); + push_constant.pos_multiplier = float(1.0) / multiplier; + push_constant.dynamic_range = storage->gi_probe_get_dynamic_range(gi_probe->probe); + push_constant.flip_x = x_flip; + push_constant.flip_y = y_flip; + push_constant.rect_pos[0] = rect.position[0]; + push_constant.rect_pos[1] = rect.position[1]; + push_constant.rect_size[0] = rect.size[0]; + push_constant.rect_size[1] = rect.size[1]; + push_constant.prev_rect_ofs[0] = 0; + push_constant.prev_rect_ofs[1] = 0; + push_constant.prev_rect_size[0] = 0; + push_constant.prev_rect_size[1] = 0; + push_constant.on_mipmap = false; + push_constant.propagation = storage->gi_probe_get_propagation(gi_probe->probe); + push_constant.pad[0] = 0; + push_constant.pad[1] = 0; + push_constant.pad[2] = 0; + + //process lighting + RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, giprobe_lighting_shader_version_pipelines[GI_PROBE_SHADER_VERSION_DYNAMIC_OBJECT_LIGHTING]); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, gi_probe->dynamic_maps[0].uniform_set, 0); + RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(GIProbeDynamicPushConstant)); + RD::get_singleton()->compute_list_dispatch(compute_list, (rect.size.x - 1) / 8 + 1, (rect.size.y - 1) / 8 + 1, 1); + //print_line("rect: " + itos(i) + ": " + rect); + + for (int k = 1; k < gi_probe->dynamic_maps.size(); k++) { + + // enlarge the rect if needed so all pixels fit when downscaled, + // this ensures downsampling is smooth and optimal because no pixels are left behind + + //x + if (rect.position.x & 1) { + rect.size.x++; + push_constant.prev_rect_ofs[0] = 1; //this is used to ensure reading is also optimal + } else { + push_constant.prev_rect_ofs[0] = 0; + } + if (rect.size.x & 1) { + rect.size.x++; + } + + rect.position.x >>= 1; + rect.size.x = MAX(1, rect.size.x >> 1); + + //y + if (rect.position.y & 1) { + rect.size.y++; + push_constant.prev_rect_ofs[1] = 1; + } else { + push_constant.prev_rect_ofs[1] = 0; + } + if (rect.size.y & 1) { + rect.size.y++; + } + + rect.position.y >>= 1; + rect.size.y = MAX(1, rect.size.y >> 1); + + //shrink limits to ensure plot does not go outside map + if (gi_probe->dynamic_maps[k].mipmap > 0) { + for (int l = 0; l < 3; l++) { + push_constant.limits[l] = MAX(1, push_constant.limits[l] >> 1); + } + } + + //print_line("rect: " + itos(i) + ": " + rect); + push_constant.rect_pos[0] = rect.position[0]; + push_constant.rect_pos[1] = rect.position[1]; + push_constant.prev_rect_size[0] = push_constant.rect_size[0]; + push_constant.prev_rect_size[1] = push_constant.rect_size[1]; + push_constant.rect_size[0] = rect.size[0]; + push_constant.rect_size[1] = rect.size[1]; + push_constant.on_mipmap = gi_probe->dynamic_maps[k].mipmap > 0; + + RD::get_singleton()->compute_list_add_barrier(compute_list); + + if (gi_probe->dynamic_maps[k].mipmap < 0) { + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, giprobe_lighting_shader_version_pipelines[GI_PROBE_SHADER_VERSION_DYNAMIC_SHRINK_WRITE]); + } else if (k < gi_probe->dynamic_maps.size() - 1) { + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, giprobe_lighting_shader_version_pipelines[GI_PROBE_SHADER_VERSION_DYNAMIC_SHRINK_WRITE_PLOT]); + } else { + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, giprobe_lighting_shader_version_pipelines[GI_PROBE_SHADER_VERSION_DYNAMIC_SHRINK_PLOT]); + } + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, gi_probe->dynamic_maps[k].uniform_set, 0); + RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(GIProbeDynamicPushConstant)); + RD::get_singleton()->compute_list_dispatch(compute_list, (rect.size.x - 1) / 8 + 1, (rect.size.y - 1) / 8 + 1, 1); + } + + RD::get_singleton()->compute_list_end(); + } + } + + gi_probe->has_dynamic_object_data = true; //clear until dynamic object data is used again + } + + gi_probe->last_probe_version = storage->gi_probe_get_version(gi_probe->probe); +} + +void RasterizerSceneRD::_debug_giprobe(RID p_gi_probe, RD::DrawListID p_draw_list, RID p_framebuffer, const CameraMatrix &p_camera_with_transform, bool p_lighting, bool p_emission, float p_alpha) { + GIProbeInstance *gi_probe = gi_probe_instance_owner.getornull(p_gi_probe); + ERR_FAIL_COND(!gi_probe); + + if (gi_probe->mipmaps.size() == 0) { + return; + } + + CameraMatrix transform = (p_camera_with_transform * CameraMatrix(gi_probe->transform)) * CameraMatrix(storage->gi_probe_get_to_cell_xform(gi_probe->probe).affine_inverse()); + + int level = 0; + Vector3i octree_size = storage->gi_probe_get_octree_size(gi_probe->probe); + + GIProbeDebugPushConstant push_constant; + push_constant.alpha = p_alpha; + push_constant.dynamic_range = storage->gi_probe_get_dynamic_range(gi_probe->probe); + push_constant.cell_offset = gi_probe->mipmaps[level].cell_offset; + push_constant.level = level; + + push_constant.bounds[0] = octree_size.x >> level; + push_constant.bounds[1] = octree_size.y >> level; + push_constant.bounds[2] = octree_size.z >> level; + push_constant.pad = 0; + + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + + push_constant.projection[i * 4 + j] = transform.matrix[i][j]; + } + } + + if (giprobe_debug_uniform_set.is_valid()) { + RD::get_singleton()->free(giprobe_debug_uniform_set); + } + Vector<RD::Uniform> uniforms; + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER; + u.binding = 1; + u.ids.push_back(storage->gi_probe_get_data_buffer(gi_probe->probe)); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_TEXTURE; + u.binding = 2; + u.ids.push_back(gi_probe->texture); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_SAMPLER; + u.binding = 3; + u.ids.push_back(storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED)); + uniforms.push_back(u); + } + + if (gi_probe_use_anisotropy) { + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_TEXTURE; + u.binding = 4; + u.ids.push_back(gi_probe->anisotropy[0]); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_TEXTURE; + u.binding = 5; + u.ids.push_back(gi_probe->anisotropy[1]); + uniforms.push_back(u); + } + } + + int cell_count; + if (!p_emission && p_lighting && gi_probe->has_dynamic_object_data) { + cell_count = push_constant.bounds[0] * push_constant.bounds[1] * push_constant.bounds[2]; + } else { + cell_count = gi_probe->mipmaps[level].cell_count; + } + + giprobe_debug_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, giprobe_debug_shader_version_shaders[0], 0); + RD::get_singleton()->draw_list_bind_render_pipeline(p_draw_list, giprobe_debug_shader_version_pipelines[p_emission ? GI_PROBE_DEBUG_EMISSION : p_lighting ? (gi_probe->has_dynamic_object_data ? GI_PROBE_DEBUG_LIGHT_FULL : GI_PROBE_DEBUG_LIGHT) : GI_PROBE_DEBUG_COLOR].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_framebuffer))); + RD::get_singleton()->draw_list_bind_uniform_set(p_draw_list, giprobe_debug_uniform_set, 0); + RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(GIProbeDebugPushConstant)); + RD::get_singleton()->draw_list_draw(p_draw_list, false, cell_count, 36); +} + +const Vector<RID> &RasterizerSceneRD::gi_probe_get_slots() const { + + return gi_probe_slots; +} + +RasterizerSceneRD::GIProbeQuality RasterizerSceneRD::gi_probe_get_quality() const { + return gi_probe_quality; +} + +//////////////////////////////// +RID RasterizerSceneRD::render_buffers_create() { + RenderBuffers rb; + rb.data = _create_render_buffer_data(); + return render_buffers_owner.make_rid(rb); +} + +void RasterizerSceneRD::_allocate_blur_textures(RenderBuffers *rb) { + ERR_FAIL_COND(!rb->blur[0].texture.is_null()); + + uint32_t mipmaps_required = Image::get_image_required_mipmaps(rb->width, rb->height, Image::FORMAT_RGBAH); + + RD::TextureFormat tf; + tf.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT; + tf.width = rb->width; + tf.height = rb->height; + tf.type = RD::TEXTURE_TYPE_2D; + tf.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT; + tf.mipmaps = mipmaps_required; + + rb->blur[0].texture = RD::get_singleton()->texture_create(tf, RD::TextureView()); + //the second one is smaller (only used for separatable part of blur) + tf.width >>= 1; + tf.height >>= 1; + tf.mipmaps--; + rb->blur[1].texture = RD::get_singleton()->texture_create(tf, RD::TextureView()); + + int base_width = rb->width; + int base_height = rb->height; + + for (uint32_t i = 0; i < mipmaps_required; i++) { + + RenderBuffers::Blur::Mipmap mm; + mm.texture = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), rb->blur[0].texture, 0, i); + { + Vector<RID> fbs; + fbs.push_back(mm.texture); + mm.framebuffer = RD::get_singleton()->framebuffer_create(fbs); + } + + mm.width = base_width; + mm.height = base_height; + + rb->blur[0].mipmaps.push_back(mm); + + if (i > 0) { + + mm.texture = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), rb->blur[1].texture, 0, i - 1); + { + Vector<RID> fbs; + fbs.push_back(mm.texture); + mm.framebuffer = RD::get_singleton()->framebuffer_create(fbs); + } + + rb->blur[1].mipmaps.push_back(mm); + } + + base_width = MAX(1, base_width >> 1); + base_height = MAX(1, base_height >> 1); + } +} + +void RasterizerSceneRD::_allocate_luminance_textures(RenderBuffers *rb) { + ERR_FAIL_COND(!rb->luminance.current.is_null()); + + int w = rb->width; + int h = rb->height; + + while (true) { + w = MAX(w / 8, 1); + h = MAX(h / 8, 1); + + RD::TextureFormat tf; + tf.format = RD::DATA_FORMAT_R32_SFLOAT; + tf.width = w; + tf.height = h; + tf.usage_bits = RD::TEXTURE_USAGE_STORAGE_BIT; + + bool final = w == 1 && h == 1; + + if (final) { + tf.usage_bits |= RD::TEXTURE_USAGE_SAMPLING_BIT; + } + + RID texture = RD::get_singleton()->texture_create(tf, RD::TextureView()); + + rb->luminance.reduce.push_back(texture); + + if (final) { + rb->luminance.current = RD::get_singleton()->texture_create(tf, RD::TextureView()); + break; + } + } +} + +void RasterizerSceneRD::_free_render_buffer_data(RenderBuffers *rb) { + + if (rb->texture.is_valid()) { + RD::get_singleton()->free(rb->texture); + rb->texture = RID(); + } + + if (rb->depth_texture.is_valid()) { + RD::get_singleton()->free(rb->depth_texture); + rb->depth_texture = RID(); + } + + for (int i = 0; i < 2; i++) { + if (rb->blur[i].texture.is_valid()) { + RD::get_singleton()->free(rb->blur[i].texture); + rb->blur[i].texture = RID(); + rb->blur[i].mipmaps.clear(); + } + } + + for (int i = 0; i < rb->luminance.reduce.size(); i++) { + RD::get_singleton()->free(rb->luminance.reduce[i]); + } + + for (int i = 0; i < rb->luminance.reduce.size(); i++) { + RD::get_singleton()->free(rb->luminance.reduce[i]); + } + rb->luminance.reduce.clear(); + + if (rb->luminance.current.is_valid()) { + RD::get_singleton()->free(rb->luminance.current); + rb->luminance.current = RID(); + } + + if (rb->ssao.ao[0].is_valid()) { + RD::get_singleton()->free(rb->ssao.depth); + RD::get_singleton()->free(rb->ssao.ao[0]); + if (rb->ssao.ao[1].is_valid()) { + RD::get_singleton()->free(rb->ssao.ao[1]); + } + if (rb->ssao.ao_full.is_valid()) { + RD::get_singleton()->free(rb->ssao.ao_full); + } + + rb->ssao.depth = RID(); + rb->ssao.ao[0] = RID(); + rb->ssao.ao[1] = RID(); + rb->ssao.ao_full = RID(); + rb->ssao.depth_slices.clear(); + } +} + +void RasterizerSceneRD::_process_ssao(RID p_render_buffers, RID p_environment, RID p_normal_buffer, const CameraMatrix &p_projection) { + + RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers); + ERR_FAIL_COND(!rb); + + Environent *env = environment_owner.getornull(p_environment); + ERR_FAIL_COND(!env); + + if (rb->ssao.ao[0].is_valid() && rb->ssao.ao_full.is_valid() != ssao_half_size) { + RD::get_singleton()->free(rb->ssao.depth); + RD::get_singleton()->free(rb->ssao.ao[0]); + if (rb->ssao.ao[1].is_valid()) { + RD::get_singleton()->free(rb->ssao.ao[1]); + } + if (rb->ssao.ao_full.is_valid()) { + RD::get_singleton()->free(rb->ssao.ao_full); + } + + rb->ssao.depth = RID(); + rb->ssao.ao[0] = RID(); + rb->ssao.ao[1] = RID(); + rb->ssao.ao_full = RID(); + rb->ssao.depth_slices.clear(); + } + + if (!rb->ssao.ao[0].is_valid()) { + //allocate depth slices + + { + RD::TextureFormat tf; + tf.format = RD::DATA_FORMAT_R32_SFLOAT; + tf.width = rb->width / 2; + tf.height = rb->height / 2; + tf.mipmaps = Image::get_image_required_mipmaps(tf.width, tf.height, Image::FORMAT_RF) + 1; + tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT; + rb->ssao.depth = RD::get_singleton()->texture_create(tf, RD::TextureView()); + for (uint32_t i = 0; i < tf.mipmaps; i++) { + RID slice = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), rb->ssao.depth, 0, i); + rb->ssao.depth_slices.push_back(slice); + } + } + + { + RD::TextureFormat tf; + tf.format = RD::DATA_FORMAT_R8_UNORM; + tf.width = ssao_half_size ? rb->width / 2 : rb->width; + tf.height = ssao_half_size ? rb->height / 2 : rb->height; + tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT; + rb->ssao.ao[0] = RD::get_singleton()->texture_create(tf, RD::TextureView()); + rb->ssao.ao[1] = RD::get_singleton()->texture_create(tf, RD::TextureView()); + } + + if (ssao_half_size) { + //upsample texture + RD::TextureFormat tf; + tf.format = RD::DATA_FORMAT_R8_UNORM; + tf.width = rb->width; + tf.height = rb->height; + tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT; + rb->ssao.ao_full = RD::get_singleton()->texture_create(tf, RD::TextureView()); + } + + _render_buffers_uniform_set_changed(p_render_buffers); + } + + storage->get_effects()->generate_ssao(rb->depth_texture, p_normal_buffer, Size2i(rb->width, rb->height), rb->ssao.depth, rb->ssao.depth_slices, rb->ssao.ao[0], rb->ssao.ao_full.is_valid(), rb->ssao.ao[1], rb->ssao.ao_full, env->ssao_intensity, env->ssao_radius, env->ssao_bias, p_projection, ssao_quality, env->ssao_blur, env->ssao_blur_edge_sharpness); +} + +void RasterizerSceneRD::_render_buffers_post_process_and_tonemap(RID p_render_buffers, RID p_environment, RID p_camera_effects, const CameraMatrix &p_projection) { + + RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers); + ERR_FAIL_COND(!rb); + + Environent *env = environment_owner.getornull(p_environment); + //glow (if enabled) + CameraEffects *camfx = camera_effects_owner.getornull(p_camera_effects); + + bool can_use_effects = rb->width >= 8 && rb->height >= 8; + + if (can_use_effects && camfx && (camfx->dof_blur_near_enabled || camfx->dof_blur_far_enabled) && camfx->dof_blur_amount > 0.0) { + + if (rb->blur[0].texture.is_null()) { + _allocate_blur_textures(rb); + _render_buffers_uniform_set_changed(p_render_buffers); + } + + float bokeh_size = camfx->dof_blur_amount * 64.0; + storage->get_effects()->bokeh_dof(rb->texture, rb->depth_texture, Size2i(rb->width, rb->height), rb->blur[0].mipmaps[0].texture, rb->blur[1].mipmaps[0].texture, rb->blur[0].mipmaps[1].texture, camfx->dof_blur_far_enabled, camfx->dof_blur_far_distance, camfx->dof_blur_far_transition, camfx->dof_blur_near_enabled, camfx->dof_blur_near_distance, camfx->dof_blur_near_transition, bokeh_size, dof_blur_bokeh_shape, dof_blur_quality, dof_blur_use_jitter, p_projection.get_z_near(), p_projection.get_z_far(), p_projection.is_orthogonal()); + } + + if (can_use_effects && env && env->auto_exposure) { + + if (rb->luminance.current.is_null()) { + _allocate_luminance_textures(rb); + _render_buffers_uniform_set_changed(p_render_buffers); + } + + bool set_immediate = env->auto_exposure_version != rb->auto_exposure_version; + rb->auto_exposure_version = env->auto_exposure_version; + + double step = env->auto_exp_speed * time_step; + storage->get_effects()->luminance_reduction(rb->texture, Size2i(rb->width, rb->height), rb->luminance.reduce, rb->luminance.current, env->min_luminance, env->max_luminance, step, set_immediate); + + //swap final reduce with prev luminance + SWAP(rb->luminance.current, rb->luminance.reduce.write[rb->luminance.reduce.size() - 1]); + RenderingServerRaster::redraw_request(); //redraw all the time if auto exposure rendering is on + } + + int max_glow_level = -1; + int glow_mask = 0; + + if (can_use_effects && env && env->glow_enabled) { + + /* see that blur textures are allocated */ + + if (rb->blur[0].texture.is_null()) { + _allocate_blur_textures(rb); + _render_buffers_uniform_set_changed(p_render_buffers); + } + + for (int i = 0; i < RS::MAX_GLOW_LEVELS; i++) { + if (env->glow_levels & (1 << i)) { + + if (i >= rb->blur[1].mipmaps.size()) { + max_glow_level = rb->blur[1].mipmaps.size() - 1; + glow_mask |= 1 << max_glow_level; + + } else { + max_glow_level = i; + glow_mask |= (1 << i); + } + } + } + + for (int i = 0; i < (max_glow_level + 1); i++) { + + int vp_w = rb->blur[1].mipmaps[i].width; + int vp_h = rb->blur[1].mipmaps[i].height; + + if (i == 0) { + RID luminance_texture; + if (env->auto_exposure && rb->luminance.current.is_valid()) { + luminance_texture = rb->luminance.current; + } + storage->get_effects()->gaussian_glow(rb->texture, rb->blur[0].mipmaps[i + 1].framebuffer, rb->blur[0].mipmaps[i + 1].texture, rb->blur[1].mipmaps[i].framebuffer, Vector2(1.0 / vp_w, 1.0 / vp_h), env->glow_strength, true, env->glow_hdr_luminance_cap, env->exposure, env->glow_bloom, env->glow_hdr_bleed_threshold, env->glow_hdr_bleed_scale, luminance_texture, env->auto_exp_scale); + } else { + storage->get_effects()->gaussian_glow(rb->blur[1].mipmaps[i - 1].texture, rb->blur[0].mipmaps[i + 1].framebuffer, rb->blur[0].mipmaps[i + 1].texture, rb->blur[1].mipmaps[i].framebuffer, Vector2(1.0 / vp_w, 1.0 / vp_h), env->glow_strength); + } + } + } + + { + //tonemap + RasterizerEffectsRD::TonemapSettings tonemap; + + tonemap.color_correction_texture = storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE); + + if (can_use_effects && env && env->auto_exposure && rb->luminance.current.is_valid()) { + tonemap.use_auto_exposure = true; + tonemap.exposure_texture = rb->luminance.current; + tonemap.auto_exposure_grey = env->auto_exp_scale; + } else { + + tonemap.exposure_texture = storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_WHITE); + } + + if (can_use_effects && env && env->glow_enabled) { + tonemap.use_glow = true; + tonemap.glow_mode = RasterizerEffectsRD::TonemapSettings::GlowMode(env->glow_blend_mode); + tonemap.glow_intensity = env->glow_blend_mode == RS::ENV_GLOW_BLEND_MODE_MIX ? env->glow_mix : env->glow_intensity; + tonemap.glow_level_flags = glow_mask; + tonemap.glow_texture_size.x = rb->blur[1].mipmaps[0].width; + tonemap.glow_texture_size.y = rb->blur[1].mipmaps[0].height; + tonemap.glow_use_bicubic_upscale = env->glow_bicubic_upscale; + tonemap.glow_texture = rb->blur[1].texture; + } else { + tonemap.glow_texture = storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_BLACK); + } + + if (env) { + tonemap.tonemap_mode = env->tone_mapper; + tonemap.white = env->white; + tonemap.exposure = env->exposure; + } + + storage->get_effects()->tonemapper(rb->texture, storage->render_target_get_rd_framebuffer(rb->render_target), tonemap); + } + + storage->render_target_disable_clear_request(rb->render_target); +} + +void RasterizerSceneRD::_render_buffers_debug_draw(RID p_render_buffers, RID p_shadow_atlas) { + RasterizerEffectsRD *effects = storage->get_effects(); + + RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers); + ERR_FAIL_COND(!rb); + + if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_SHADOW_ATLAS) { + if (p_shadow_atlas.is_valid()) { + RID shadow_atlas_texture = shadow_atlas_get_texture(p_shadow_atlas); + Size2 rtsize = storage->render_target_get_size(rb->render_target); + + effects->copy_to_rect(shadow_atlas_texture, storage->render_target_get_rd_framebuffer(rb->render_target), Rect2(Vector2(), rtsize / 2), false, true); + } + } + + if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_DIRECTIONAL_SHADOW_ATLAS) { + if (directional_shadow_get_texture().is_valid()) { + RID shadow_atlas_texture = directional_shadow_get_texture(); + Size2 rtsize = storage->render_target_get_size(rb->render_target); + + effects->copy_to_rect(shadow_atlas_texture, storage->render_target_get_rd_framebuffer(rb->render_target), Rect2(Vector2(), rtsize / 2), false, true); + } + } + + if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_SCENE_LUMINANCE) { + if (rb->luminance.current.is_valid()) { + Size2 rtsize = storage->render_target_get_size(rb->render_target); + + effects->copy_to_rect(rb->luminance.current, storage->render_target_get_rd_framebuffer(rb->render_target), Rect2(Vector2(), rtsize / 8), false, true); + } + } + + if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_SSAO && rb->ssao.ao[0].is_valid()) { + Size2 rtsize = storage->render_target_get_size(rb->render_target); + RID ao_buf = rb->ssao.ao_full.is_valid() ? rb->ssao.ao_full : rb->ssao.ao[0]; + effects->copy_to_rect(ao_buf, storage->render_target_get_rd_framebuffer(rb->render_target), Rect2(Vector2(), rtsize), false, true); + } + + if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_ROUGHNESS_LIMITER && _render_buffers_get_roughness_texture(p_render_buffers).is_valid()) { + Size2 rtsize = storage->render_target_get_size(rb->render_target); + effects->copy_to_rect(_render_buffers_get_roughness_texture(p_render_buffers), storage->render_target_get_rd_framebuffer(rb->render_target), Rect2(Vector2(), rtsize), false, true); + } + + if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_NORMAL_BUFFER && _render_buffers_get_normal_texture(p_render_buffers).is_valid()) { + Size2 rtsize = storage->render_target_get_size(rb->render_target); + effects->copy_to_rect(_render_buffers_get_normal_texture(p_render_buffers), storage->render_target_get_rd_framebuffer(rb->render_target), Rect2(Vector2(), rtsize)); + } +} + +RID RasterizerSceneRD::render_buffers_get_back_buffer_texture(RID p_render_buffers) { + + RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers); + ERR_FAIL_COND_V(!rb, RID()); + if (!rb->blur[0].texture.is_valid()) { + return RID(); //not valid at the moment + } + return rb->blur[0].texture; +} + +RID RasterizerSceneRD::render_buffers_get_ao_texture(RID p_render_buffers) { + RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers); + ERR_FAIL_COND_V(!rb, RID()); + + return rb->ssao.ao_full.is_valid() ? rb->ssao.ao_full : rb->ssao.ao[0]; +} + +void RasterizerSceneRD::render_buffers_configure(RID p_render_buffers, RID p_render_target, int p_width, int p_height, RS::ViewportMSAA p_msaa) { + + RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers); + rb->width = p_width; + rb->height = p_height; + rb->render_target = p_render_target; + rb->msaa = p_msaa; + _free_render_buffer_data(rb); + + { + RD::TextureFormat tf; + tf.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT; + tf.width = rb->width; + tf.height = rb->height; + tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; + + rb->texture = RD::get_singleton()->texture_create(tf, RD::TextureView()); + } + + { + RD::TextureFormat tf; + tf.format = RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_D24_UNORM_S8_UINT, RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) ? RD::DATA_FORMAT_D24_UNORM_S8_UINT : RD::DATA_FORMAT_D32_SFLOAT_S8_UINT; + tf.width = p_width; + tf.height = p_height; + tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + + rb->depth_texture = RD::get_singleton()->texture_create(tf, RD::TextureView()); + } + + rb->data->configure(rb->texture, rb->depth_texture, p_width, p_height, p_msaa); + _render_buffers_uniform_set_changed(p_render_buffers); +} + +int RasterizerSceneRD::get_roughness_layers() const { + return roughness_layers; +} + +bool RasterizerSceneRD::is_using_radiance_cubemap_array() const { + return sky_use_cubemap_array; +} + +RasterizerSceneRD::RenderBufferData *RasterizerSceneRD::render_buffers_get_data(RID p_render_buffers) { + RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers); + ERR_FAIL_COND_V(!rb, NULL); + return rb->data; +} + +void RasterizerSceneRD::render_scene(RID p_render_buffers, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID *p_light_cull_result, int p_light_cull_count, RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, RID *p_gi_probe_cull_result, int p_gi_probe_cull_count, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass) { + + Color clear_color; + if (p_render_buffers.is_valid()) { + RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers); + ERR_FAIL_COND(!rb); + clear_color = storage->render_target_get_clear_request_color(rb->render_target); + } else { + clear_color = storage->get_default_clear_color(); + } + + _render_scene(p_render_buffers, p_cam_transform, p_cam_projection, p_cam_ortogonal, p_cull_result, p_cull_count, p_light_cull_result, p_light_cull_count, p_reflection_probe_cull_result, p_reflection_probe_cull_count, p_gi_probe_cull_result, p_gi_probe_cull_count, p_environment, p_camera_effects, p_shadow_atlas, p_reflection_atlas, p_reflection_probe, p_reflection_probe_pass, clear_color); + + if (p_render_buffers.is_valid()) { + RENDER_TIMESTAMP("Tonemap"); + + _render_buffers_post_process_and_tonemap(p_render_buffers, p_environment, p_camera_effects, p_cam_projection); + _render_buffers_debug_draw(p_render_buffers, p_shadow_atlas); + } +} + +void RasterizerSceneRD::render_shadow(RID p_light, RID p_shadow_atlas, int p_pass, InstanceBase **p_cull_result, int p_cull_count) { + + LightInstance *light_instance = light_instance_owner.getornull(p_light); + ERR_FAIL_COND(!light_instance); + + Rect2i atlas_rect; + RID atlas_fb; + + bool using_dual_paraboloid = false; + bool using_dual_paraboloid_flip = false; + float zfar = 0; + RID render_fb; + RID render_texture; + float bias = 0; + float normal_bias = 0; + + bool render_cubemap = false; + bool finalize_cubemap = false; + + CameraMatrix light_projection; + Transform light_transform; + + if (storage->light_get_type(light_instance->light) == RS::LIGHT_DIRECTIONAL) { + //set pssm stuff + if (light_instance->last_scene_shadow_pass != scene_pass) { + light_instance->directional_rect = _get_directional_shadow_rect(directional_shadow.size, directional_shadow.light_count, directional_shadow.current_light); + directional_shadow.current_light++; + light_instance->last_scene_shadow_pass = scene_pass; + } + + light_projection = light_instance->shadow_transform[p_pass].camera; + light_transform = light_instance->shadow_transform[p_pass].transform; + + atlas_rect.position.x = light_instance->directional_rect.position.x; + atlas_rect.position.y = light_instance->directional_rect.position.y; + atlas_rect.size.width = light_instance->directional_rect.size.x; + atlas_rect.size.height = light_instance->directional_rect.size.y; + + if (storage->light_directional_get_shadow_mode(light_instance->light) == RS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_4_SPLITS) { + + atlas_rect.size.width /= 2; + atlas_rect.size.height /= 2; + + if (p_pass == 1) { + atlas_rect.position.x += atlas_rect.size.width; + } else if (p_pass == 2) { + atlas_rect.position.y += atlas_rect.size.height; + } else if (p_pass == 3) { + atlas_rect.position.x += atlas_rect.size.width; + atlas_rect.position.y += atlas_rect.size.height; + } + + } else if (storage->light_directional_get_shadow_mode(light_instance->light) == RS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_2_SPLITS) { + + atlas_rect.size.height /= 2; + + if (p_pass == 0) { + + } else { + atlas_rect.position.y += atlas_rect.size.height; + } + } + + light_instance->shadow_transform[p_pass].atlas_rect = atlas_rect; + + light_instance->shadow_transform[p_pass].atlas_rect.position /= directional_shadow.size; + light_instance->shadow_transform[p_pass].atlas_rect.size /= directional_shadow.size; + + float bias_mult = Math::lerp(1.0f, light_instance->shadow_transform[p_pass].bias_scale, storage->light_get_param(light_instance->light, RS::LIGHT_PARAM_SHADOW_BIAS_SPLIT_SCALE)); + zfar = storage->light_get_param(light_instance->light, RS::LIGHT_PARAM_RANGE); + bias = storage->light_get_param(light_instance->light, RS::LIGHT_PARAM_SHADOW_BIAS) * bias_mult; + normal_bias = storage->light_get_param(light_instance->light, RS::LIGHT_PARAM_SHADOW_NORMAL_BIAS) * bias_mult; + + ShadowMap *shadow_map = _get_shadow_map(atlas_rect.size); + render_fb = shadow_map->fb; + render_texture = shadow_map->depth; + atlas_fb = directional_shadow.fb; + + } else { + //set from shadow atlas + + ShadowAtlas *shadow_atlas = shadow_atlas_owner.getornull(p_shadow_atlas); + ERR_FAIL_COND(!shadow_atlas); + ERR_FAIL_COND(!shadow_atlas->shadow_owners.has(p_light)); + + uint32_t key = shadow_atlas->shadow_owners[p_light]; + + uint32_t quadrant = (key >> ShadowAtlas::QUADRANT_SHIFT) & 0x3; + uint32_t shadow = key & ShadowAtlas::SHADOW_INDEX_MASK; + + ERR_FAIL_INDEX((int)shadow, shadow_atlas->quadrants[quadrant].shadows.size()); + + uint32_t quadrant_size = shadow_atlas->size >> 1; + + atlas_rect.position.x = (quadrant & 1) * quadrant_size; + atlas_rect.position.y = (quadrant >> 1) * quadrant_size; + + uint32_t shadow_size = (quadrant_size / shadow_atlas->quadrants[quadrant].subdivision); + atlas_rect.position.x += (shadow % shadow_atlas->quadrants[quadrant].subdivision) * shadow_size; + atlas_rect.position.y += (shadow / shadow_atlas->quadrants[quadrant].subdivision) * shadow_size; + + atlas_rect.size.width = shadow_size; + atlas_rect.size.height = shadow_size; + atlas_fb = shadow_atlas->fb; + + zfar = storage->light_get_param(light_instance->light, RS::LIGHT_PARAM_RANGE); + bias = storage->light_get_param(light_instance->light, RS::LIGHT_PARAM_SHADOW_BIAS); + normal_bias = storage->light_get_param(light_instance->light, RS::LIGHT_PARAM_SHADOW_NORMAL_BIAS); + + if (storage->light_get_type(light_instance->light) == RS::LIGHT_OMNI) { + + if (storage->light_omni_get_shadow_mode(light_instance->light) == RS::LIGHT_OMNI_SHADOW_CUBE) { + + ShadowCubemap *cubemap = _get_shadow_cubemap(shadow_size / 2); + + render_fb = cubemap->side_fb[p_pass]; + render_texture = cubemap->cubemap; + + light_projection = light_instance->shadow_transform[0].camera; + light_transform = light_instance->shadow_transform[0].transform; + render_cubemap = true; + finalize_cubemap = p_pass == 5; + + } else { + + light_projection = light_instance->shadow_transform[0].camera; + light_transform = light_instance->shadow_transform[0].transform; + + atlas_rect.size.height /= 2; + atlas_rect.position.y += p_pass * atlas_rect.size.height; + + using_dual_paraboloid = true; + using_dual_paraboloid_flip = p_pass == 1; + + ShadowMap *shadow_map = _get_shadow_map(atlas_rect.size); + render_fb = shadow_map->fb; + render_texture = shadow_map->depth; + } + + } else if (storage->light_get_type(light_instance->light) == RS::LIGHT_SPOT) { + + light_projection = light_instance->shadow_transform[0].camera; + light_transform = light_instance->shadow_transform[0].transform; + + ShadowMap *shadow_map = _get_shadow_map(atlas_rect.size); + render_fb = shadow_map->fb; + render_texture = shadow_map->depth; + } + } + + if (render_cubemap) { + //rendering to cubemap + _render_shadow(render_fb, p_cull_result, p_cull_count, light_projection, light_transform, zfar, 0, 0, false, false); + if (finalize_cubemap) { + //reblit + atlas_rect.size.height /= 2; + storage->get_effects()->copy_cubemap_to_dp(render_texture, atlas_fb, atlas_rect, light_projection.get_z_near(), light_projection.get_z_far(), bias, false); + atlas_rect.position.y += atlas_rect.size.height; + storage->get_effects()->copy_cubemap_to_dp(render_texture, atlas_fb, atlas_rect, light_projection.get_z_near(), light_projection.get_z_far(), bias, true); + } + } else { + //render shadow + + _render_shadow(render_fb, p_cull_result, p_cull_count, light_projection, light_transform, zfar, bias, normal_bias, using_dual_paraboloid, using_dual_paraboloid_flip); + + //copy to atlas + storage->get_effects()->copy_to_rect(render_texture, atlas_fb, atlas_rect, true); + + //does not work from depth to color + //RD::get_singleton()->texture_copy(render_texture, atlas_texture, Vector3(0, 0, 0), Vector3(atlas_rect.position.x, atlas_rect.position.y, 0), Vector3(atlas_rect.size.x, atlas_rect.size.y, 1), 0, 0, 0, 0, true); + } +} + +void RasterizerSceneRD::render_material(const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID p_framebuffer, const Rect2i &p_region) { + + _render_material(p_cam_transform, p_cam_projection, p_cam_ortogonal, p_cull_result, p_cull_count, p_framebuffer, p_region); +} + +bool RasterizerSceneRD::free(RID p_rid) { + + if (render_buffers_owner.owns(p_rid)) { + RenderBuffers *rb = render_buffers_owner.getornull(p_rid); + _free_render_buffer_data(rb); + memdelete(rb->data); + render_buffers_owner.free(p_rid); + } else if (environment_owner.owns(p_rid)) { + //not much to delete, just free it + environment_owner.free(p_rid); + } else if (camera_effects_owner.owns(p_rid)) { + //not much to delete, just free it + camera_effects_owner.free(p_rid); + } else if (reflection_atlas_owner.owns(p_rid)) { + reflection_atlas_set_size(p_rid, 0, 0); + reflection_atlas_owner.free(p_rid); + } else if (reflection_probe_instance_owner.owns(p_rid)) { + //not much to delete, just free it + //ReflectionProbeInstance *rpi = reflection_probe_instance_owner.getornull(p_rid); + reflection_probe_release_atlas_index(p_rid); + reflection_probe_instance_owner.free(p_rid); + } else if (gi_probe_instance_owner.owns(p_rid)) { + GIProbeInstance *gi_probe = gi_probe_instance_owner.getornull(p_rid); + if (gi_probe->texture.is_valid()) { + RD::get_singleton()->free(gi_probe->texture); + RD::get_singleton()->free(gi_probe->write_buffer); + } + if (gi_probe->anisotropy[0].is_valid()) { + RD::get_singleton()->free(gi_probe->anisotropy[0]); + RD::get_singleton()->free(gi_probe->anisotropy[1]); + } + + for (int i = 0; i < gi_probe->dynamic_maps.size(); i++) { + RD::get_singleton()->free(gi_probe->dynamic_maps[i].texture); + RD::get_singleton()->free(gi_probe->dynamic_maps[i].depth); + } + + gi_probe_slots.write[gi_probe->slot] = RID(); + + gi_probe_instance_owner.free(p_rid); + } else if (sky_owner.owns(p_rid)) { + _update_dirty_skys(); + Sky *sky = sky_owner.getornull(p_rid); + + if (sky->radiance.is_valid()) { + RD::get_singleton()->free(sky->radiance); + sky->radiance = RID(); + } + _clear_reflection_data(sky->reflection); + + if (sky->uniform_buffer.is_valid()) { + RD::get_singleton()->free(sky->uniform_buffer); + sky->uniform_buffer = RID(); + } + + if (sky->half_res_pass.is_valid()) { + RD::get_singleton()->free(sky->half_res_pass); + sky->half_res_pass = RID(); + } + + if (sky->quarter_res_pass.is_valid()) { + RD::get_singleton()->free(sky->quarter_res_pass); + sky->quarter_res_pass = RID(); + } + + if (sky->material.is_valid()) { + storage->free(sky->material); + } + + sky_owner.free(p_rid); + } else if (light_instance_owner.owns(p_rid)) { + + LightInstance *light_instance = light_instance_owner.getornull(p_rid); + + //remove from shadow atlases.. + for (Set<RID>::Element *E = light_instance->shadow_atlases.front(); E; E = E->next()) { + ShadowAtlas *shadow_atlas = shadow_atlas_owner.getornull(E->get()); + ERR_CONTINUE(!shadow_atlas->shadow_owners.has(p_rid)); + uint32_t key = shadow_atlas->shadow_owners[p_rid]; + uint32_t q = (key >> ShadowAtlas::QUADRANT_SHIFT) & 0x3; + uint32_t s = key & ShadowAtlas::SHADOW_INDEX_MASK; + + shadow_atlas->quadrants[q].shadows.write[s].owner = RID(); + shadow_atlas->shadow_owners.erase(p_rid); + } + + light_instance_owner.free(p_rid); + + } else if (shadow_atlas_owner.owns(p_rid)) { + + shadow_atlas_set_size(p_rid, 0); + shadow_atlas_owner.free(p_rid); + + } else { + return false; + } + + return true; +} + +void RasterizerSceneRD::set_debug_draw_mode(RS::ViewportDebugDraw p_debug_draw) { + debug_draw = p_debug_draw; +} + +void RasterizerSceneRD::update() { + _update_dirty_skys(); +} + +void RasterizerSceneRD::set_time(double p_time, double p_step) { + time = p_time; + time_step = p_step; +} + +void RasterizerSceneRD::screen_space_roughness_limiter_set_active(bool p_enable, float p_curve) { + screen_space_roughness_limiter = p_enable; + screen_space_roughness_limiter_curve = p_curve; +} + +bool RasterizerSceneRD::screen_space_roughness_limiter_is_active() const { + return screen_space_roughness_limiter; +} + +float RasterizerSceneRD::screen_space_roughness_limiter_get_curve() const { + return screen_space_roughness_limiter_curve; +} + +RasterizerSceneRD *RasterizerSceneRD::singleton = NULL; + +RasterizerSceneRD::RasterizerSceneRD(RasterizerStorageRD *p_storage) { + storage = p_storage; + singleton = this; + + roughness_layers = GLOBAL_GET("rendering/quality/reflections/roughness_layers"); + sky_ggx_samples_quality = GLOBAL_GET("rendering/quality/reflections/ggx_samples"); + sky_use_cubemap_array = GLOBAL_GET("rendering/quality/reflections/texture_array_reflections"); + // sky_use_cubemap_array = false; + + uint32_t textures_per_stage = RD::get_singleton()->limit_get(RD::LIMIT_MAX_TEXTURES_PER_SHADER_STAGE); + + { + + //kinda complicated to compute the amount of slots, we try to use as many as we can + + gi_probe_max_lights = 32; + + gi_probe_lights = memnew_arr(GIProbeLight, gi_probe_max_lights); + gi_probe_lights_uniform = RD::get_singleton()->uniform_buffer_create(gi_probe_max_lights * sizeof(GIProbeLight)); + + gi_probe_use_anisotropy = GLOBAL_GET("rendering/quality/gi_probes/anisotropic"); + gi_probe_quality = GIProbeQuality(CLAMP(int(GLOBAL_GET("rendering/quality/gi_probes/quality")), 0, 2)); + + if (textures_per_stage <= 16) { + gi_probe_slots.resize(2); //thats all you can get + gi_probe_use_anisotropy = false; + } else if (textures_per_stage <= 31) { + gi_probe_slots.resize(4); //thats all you can get, iOS + gi_probe_use_anisotropy = false; + } else if (textures_per_stage <= 128) { + gi_probe_slots.resize(32); //old intel + gi_probe_use_anisotropy = false; + } else if (textures_per_stage <= 256) { + gi_probe_slots.resize(64); //old intel too + gi_probe_use_anisotropy = false; + } else { + if (gi_probe_use_anisotropy) { + gi_probe_slots.resize(1024 / 3); //needs 3 textures + } else { + gi_probe_slots.resize(1024); //modern intel, nvidia, 8192 or greater + } + } + + String defines = "\n#define MAX_LIGHTS " + itos(gi_probe_max_lights) + "\n"; + if (gi_probe_use_anisotropy) { + defines += "\n#define MODE_ANISOTROPIC\n"; + } + + Vector<String> versions; + versions.push_back("\n#define MODE_COMPUTE_LIGHT\n"); + versions.push_back("\n#define MODE_SECOND_BOUNCE\n"); + versions.push_back("\n#define MODE_UPDATE_MIPMAPS\n"); + versions.push_back("\n#define MODE_WRITE_TEXTURE\n"); + versions.push_back("\n#define MODE_DYNAMIC\n#define MODE_DYNAMIC_LIGHTING\n"); + versions.push_back("\n#define MODE_DYNAMIC\n#define MODE_DYNAMIC_SHRINK\n#define MODE_DYNAMIC_SHRINK_WRITE\n"); + versions.push_back("\n#define MODE_DYNAMIC\n#define MODE_DYNAMIC_SHRINK\n#define MODE_DYNAMIC_SHRINK_PLOT\n"); + versions.push_back("\n#define MODE_DYNAMIC\n#define MODE_DYNAMIC_SHRINK\n#define MODE_DYNAMIC_SHRINK_PLOT\n#define MODE_DYNAMIC_SHRINK_WRITE\n"); + + giprobe_shader.initialize(versions, defines); + giprobe_lighting_shader_version = giprobe_shader.version_create(); + for (int i = 0; i < GI_PROBE_SHADER_VERSION_MAX; i++) { + giprobe_lighting_shader_version_shaders[i] = giprobe_shader.version_get_shader(giprobe_lighting_shader_version, i); + giprobe_lighting_shader_version_pipelines[i] = RD::get_singleton()->compute_pipeline_create(giprobe_lighting_shader_version_shaders[i]); + } + } + + { + + String defines; + if (gi_probe_use_anisotropy) { + defines += "\n#define USE_ANISOTROPY\n"; + } + Vector<String> versions; + versions.push_back("\n#define MODE_DEBUG_COLOR\n"); + versions.push_back("\n#define MODE_DEBUG_LIGHT\n"); + versions.push_back("\n#define MODE_DEBUG_EMISSION\n"); + versions.push_back("\n#define MODE_DEBUG_LIGHT\n#define MODE_DEBUG_LIGHT_FULL\n"); + + giprobe_debug_shader.initialize(versions, defines); + giprobe_debug_shader_version = giprobe_debug_shader.version_create(); + for (int i = 0; i < GI_PROBE_DEBUG_MAX; i++) { + giprobe_debug_shader_version_shaders[i] = giprobe_debug_shader.version_get_shader(giprobe_debug_shader_version, i); + + RD::PipelineRasterizationState rs; + rs.cull_mode = RD::POLYGON_CULL_FRONT; + RD::PipelineDepthStencilState ds; + ds.enable_depth_test = true; + ds.enable_depth_write = true; + ds.depth_compare_operator = RD::COMPARE_OP_LESS_OR_EQUAL; + + giprobe_debug_shader_version_pipelines[i].setup(giprobe_debug_shader_version_shaders[i], RD::RENDER_PRIMITIVE_TRIANGLES, rs, RD::PipelineMultisampleState(), ds, RD::PipelineColorBlendState::create_disabled(), 0); + } + } + + /* SKY SHADER */ + + { + // Start with the directional lights for the sky + sky_scene_state.max_directional_lights = 4; + uint32_t directional_light_buffer_size = sky_scene_state.max_directional_lights * sizeof(SkyDirectionalLightData); + sky_scene_state.directional_lights = memnew_arr(SkyDirectionalLightData, sky_scene_state.max_directional_lights); + sky_scene_state.last_frame_directional_lights = memnew_arr(SkyDirectionalLightData, sky_scene_state.max_directional_lights); + sky_scene_state.last_frame_directional_light_count = sky_scene_state.max_directional_lights + 1; + sky_scene_state.directional_light_buffer = RD::get_singleton()->uniform_buffer_create(directional_light_buffer_size); + + String defines = "\n#define MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS " + itos(sky_scene_state.max_directional_lights) + "\n"; + + // Initialize sky + Vector<String> sky_modes; + sky_modes.push_back(""); // Full size + sky_modes.push_back("\n#define USE_HALF_RES_PASS\n"); // Half Res + sky_modes.push_back("\n#define USE_QUARTER_RES_PASS\n"); // Quarter res + sky_modes.push_back("\n#define USE_CUBEMAP_PASS\n"); // Cubemap + sky_modes.push_back("\n#define USE_CUBEMAP_PASS\n#define USE_HALF_RES_PASS\n"); // Half Res Cubemap + sky_modes.push_back("\n#define USE_CUBEMAP_PASS\n#define USE_QUARTER_RES_PASS\n"); // Quarter res Cubemap + sky_shader.shader.initialize(sky_modes, defines); + } + + // register our shader funds + storage->shader_set_data_request_function(RasterizerStorageRD::SHADER_TYPE_SKY, _create_sky_shader_funcs); + storage->material_set_data_request_function(RasterizerStorageRD::SHADER_TYPE_SKY, _create_sky_material_funcs); + + { + ShaderCompilerRD::DefaultIdentifierActions actions; + + actions.renames["COLOR"] = "color"; + actions.renames["ALPHA"] = "alpha"; + actions.renames["EYEDIR"] = "cube_normal"; + actions.renames["POSITION"] = "params.position_multiplier.xyz"; + actions.renames["SKY_COORDS"] = "panorama_coords"; + actions.renames["SCREEN_UV"] = "uv"; + actions.renames["TIME"] = "params.time"; + actions.renames["HALF_RES_COLOR"] = "half_res_color"; + actions.renames["QUARTER_RES_COLOR"] = "quarter_res_color"; + actions.renames["RADIANCE"] = "radiance"; + actions.renames["LIGHT0_ENABLED"] = "directional_lights.data[0].enabled"; + actions.renames["LIGHT0_DIRECTION"] = "directional_lights.data[0].direction"; + actions.renames["LIGHT0_ENERGY"] = "directional_lights.data[0].energy"; + actions.renames["LIGHT0_COLOR"] = "directional_lights.data[0].color"; + actions.renames["LIGHT1_ENABLED"] = "directional_lights.data[1].enabled"; + actions.renames["LIGHT1_DIRECTION"] = "directional_lights.data[1].direction"; + actions.renames["LIGHT1_ENERGY"] = "directional_lights.data[1].energy"; + actions.renames["LIGHT1_COLOR"] = "directional_lights.data[1].color"; + actions.renames["LIGHT2_ENABLED"] = "directional_lights.data[2].enabled"; + actions.renames["LIGHT2_DIRECTION"] = "directional_lights.data[2].direction"; + actions.renames["LIGHT2_ENERGY"] = "directional_lights.data[2].energy"; + actions.renames["LIGHT2_COLOR"] = "directional_lights.data[2].color"; + actions.renames["LIGHT3_ENABLED"] = "directional_lights.data[3].enabled"; + actions.renames["LIGHT3_DIRECTION"] = "directional_lights.data[3].direction"; + actions.renames["LIGHT3_ENERGY"] = "directional_lights.data[3].energy"; + actions.renames["LIGHT3_COLOR"] = "directional_lights.data[3].color"; + actions.renames["AT_CUBEMAP_PASS"] = "AT_CUBEMAP_PASS"; + actions.renames["AT_HALF_RES_PASS"] = "AT_HALF_RES_PASS"; + actions.renames["AT_QUARTER_RES_PASS"] = "AT_QUARTER_RES_PASS"; + actions.custom_samplers["RADIANCE"] = "material_samplers[3]"; + actions.usage_defines["HALF_RES_COLOR"] = "\n#define USES_HALF_RES_COLOR\n"; + actions.usage_defines["QUARTER_RES_COLOR"] = "\n#define USES_QUARTER_RES_COLOR\n"; + + actions.sampler_array_name = "material_samplers"; + actions.base_texture_binding_index = 1; + actions.texture_layout_set = 1; + actions.base_uniform_string = "material."; + actions.base_varying_index = 10; + + actions.default_filter = ShaderLanguage::FILTER_LINEAR_MIPMAP; + actions.default_repeat = ShaderLanguage::REPEAT_ENABLE; + + sky_shader.compiler.initialize(actions); + } + + { + // default material and shader for sky shader + sky_shader.default_shader = storage->shader_create(); + storage->shader_set_code(sky_shader.default_shader, "shader_type sky; void fragment() { COLOR = mix(vec3(0.3), vec3(0.2, 0.4, 0.9), smoothstep(0.0, 0.05, EYEDIR.y)); } \n"); + sky_shader.default_material = storage->material_create(); + storage->material_set_shader(sky_shader.default_material, sky_shader.default_shader); + + SkyMaterialData *md = (SkyMaterialData *)storage->material_get_data(sky_shader.default_material, RasterizerStorageRD::SHADER_TYPE_SKY); + sky_shader.default_shader_rd = sky_shader.shader.version_get_shader(md->shader_data->version, SKY_VERSION_BACKGROUND); + + Vector<RD::Uniform> uniforms; + + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_SAMPLER; + u.binding = 0; + u.ids.resize(12); + RID *ids_ptr = u.ids.ptrw(); + ids_ptr[0] = storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + ids_ptr[1] = storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + ids_ptr[2] = storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + ids_ptr[3] = storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + ids_ptr[4] = storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + ids_ptr[5] = storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + ids_ptr[6] = storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED); + ids_ptr[7] = storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED); + ids_ptr[8] = storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED); + ids_ptr[9] = storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED); + ids_ptr[10] = storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED); + ids_ptr[11] = storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED); + uniforms.push_back(u); + } + + sky_scene_state.sampler_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, sky_shader.default_shader_rd, SKY_SET_SAMPLERS); + } + + camera_effects_set_dof_blur_bokeh_shape(RS::DOFBokehShape(int(GLOBAL_GET("rendering/quality/filters/depth_of_field_bokeh_shape")))); + camera_effects_set_dof_blur_quality(RS::DOFBlurQuality(int(GLOBAL_GET("rendering/quality/filters/depth_of_field_bokeh_quality"))), GLOBAL_GET("rendering/quality/filters/depth_of_field_use_jitter")); + environment_set_ssao_quality(RS::EnvironmentSSAOQuality(int(GLOBAL_GET("rendering/quality/ssao/quality"))), GLOBAL_GET("rendering/quality/ssao/half_size")); + screen_space_roughness_limiter = GLOBAL_GET("rendering/quality/filters/screen_space_roughness_limiter"); + screen_space_roughness_limiter_curve = GLOBAL_GET("rendering/quality/filters/screen_space_roughness_limiter_curve"); +} + +RasterizerSceneRD::~RasterizerSceneRD() { + for (Map<Vector2i, ShadowMap>::Element *E = shadow_maps.front(); E; E = E->next()) { + RD::get_singleton()->free(E->get().depth); + } + for (Map<int, ShadowCubemap>::Element *E = shadow_cubemaps.front(); E; E = E->next()) { + RD::get_singleton()->free(E->get().cubemap); + } + + if (sky_scene_state.sampler_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(sky_scene_state.sampler_uniform_set)) { + RD::get_singleton()->free(sky_scene_state.sampler_uniform_set); + } + if (sky_scene_state.light_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(sky_scene_state.light_uniform_set)) { + RD::get_singleton()->free(sky_scene_state.light_uniform_set); + } + + RD::get_singleton()->free(gi_probe_lights_uniform); + giprobe_debug_shader.version_free(giprobe_debug_shader_version); + giprobe_shader.version_free(giprobe_lighting_shader_version); + memdelete_arr(gi_probe_lights); + SkyMaterialData *md = (SkyMaterialData *)storage->material_get_data(sky_shader.default_material, RasterizerStorageRD::SHADER_TYPE_SKY); + sky_shader.shader.version_free(md->shader_data->version); + RD::get_singleton()->free(sky_scene_state.directional_light_buffer); + memdelete_arr(sky_scene_state.directional_lights); + memdelete_arr(sky_scene_state.last_frame_directional_lights); + storage->free(sky_shader.default_shader); + storage->free(sky_shader.default_material); +} diff --git a/servers/rendering/rasterizer_rd/rasterizer_scene_rd.h b/servers/rendering/rasterizer_rd/rasterizer_scene_rd.h new file mode 100644 index 0000000000..0a1cc8ebd7 --- /dev/null +++ b/servers/rendering/rasterizer_rd/rasterizer_scene_rd.h @@ -0,0 +1,1109 @@ +/*************************************************************************/ +/* rasterizer_scene_rd.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef RASTERIZER_SCENE_RD_H +#define RASTERIZER_SCENE_RD_H + +#include "core/rid_owner.h" +#include "servers/rendering/rasterizer.h" +#include "servers/rendering/rasterizer_rd/rasterizer_storage_rd.h" +#include "servers/rendering/rasterizer_rd/shaders/giprobe.glsl.gen.h" +#include "servers/rendering/rasterizer_rd/shaders/giprobe_debug.glsl.gen.h" +#include "servers/rendering/rasterizer_rd/shaders/sky.glsl.gen.h" +#include "servers/rendering/rendering_device.h" + +class RasterizerSceneRD : public RasterizerScene { +public: + enum GIProbeQuality { + GIPROBE_QUALITY_ULTRA_LOW, + GIPROBE_QUALITY_MEDIUM, + GIPROBE_QUALITY_HIGH, + }; + +protected: + double time; + + // Skys need less info from Directional Lights than the normal shaders + struct SkyDirectionalLightData { + + float direction[3]; + float energy; + float color[3]; + uint32_t enabled; + }; + + struct SkySceneState { + + SkyDirectionalLightData *directional_lights; + SkyDirectionalLightData *last_frame_directional_lights; + uint32_t max_directional_lights; + uint32_t directional_light_count; + uint32_t last_frame_directional_light_count; + RID directional_light_buffer; + RID sampler_uniform_set; + RID light_uniform_set; + } sky_scene_state; + + struct RenderBufferData { + + virtual void configure(RID p_color_buffer, RID p_depth_buffer, int p_width, int p_height, RS::ViewportMSAA p_msaa) = 0; + virtual ~RenderBufferData() {} + }; + virtual RenderBufferData *_create_render_buffer_data() = 0; + + virtual void _render_scene(RID p_render_buffer, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID *p_light_cull_result, int p_light_cull_count, RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, RID *p_gi_probe_cull_result, int p_gi_probe_cull_count, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, const Color &p_default_color) = 0; + virtual void _render_shadow(RID p_framebuffer, InstanceBase **p_cull_result, int p_cull_count, const CameraMatrix &p_projection, const Transform &p_transform, float p_zfar, float p_bias, float p_normal_bias, bool p_use_dp, bool use_dp_flip) = 0; + virtual void _render_material(const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID p_framebuffer, const Rect2i &p_region) = 0; + + virtual void _debug_giprobe(RID p_gi_probe, RenderingDevice::DrawListID p_draw_list, RID p_framebuffer, const CameraMatrix &p_camera_with_transform, bool p_lighting, bool p_emission, float p_alpha); + + RenderBufferData *render_buffers_get_data(RID p_render_buffers); + + virtual void _base_uniforms_changed() = 0; + virtual void _render_buffers_uniform_set_changed(RID p_render_buffers) = 0; + virtual RID _render_buffers_get_roughness_texture(RID p_render_buffers) = 0; + virtual RID _render_buffers_get_normal_texture(RID p_render_buffers) = 0; + + void _process_ssao(RID p_render_buffers, RID p_environment, RID p_normal_buffer, const CameraMatrix &p_projection); + + void _setup_sky(RID p_environment, const Vector3 &p_position, const Size2i p_screen_size); + void _update_sky(RID p_environment, const CameraMatrix &p_projection, const Transform &p_transform); + void _draw_sky(bool p_can_continue, RID p_fb, RID p_environment, const CameraMatrix &p_projection, const Transform &p_transform); + +private: + RS::ViewportDebugDraw debug_draw = RS::VIEWPORT_DEBUG_DRAW_DISABLED; + double time_step = 0; + static RasterizerSceneRD *singleton; + + int roughness_layers; + + RasterizerStorageRD *storage; + + struct ReflectionData { + + struct Layer { + struct Mipmap { + RID framebuffers[6]; + RID views[6]; + Size2i size; + }; + Vector<Mipmap> mipmaps; //per-face view + Vector<RID> views; // per-cubemap view + }; + + struct DownsampleLayer { + struct Mipmap { + RID view; + Size2i size; + }; + Vector<Mipmap> mipmaps; + }; + + RID radiance_base_cubemap; //cubemap for first layer, first cubemap + RID downsampled_radiance_cubemap; + DownsampleLayer downsampled_layer; + RID coefficient_buffer; + + bool dirty = true; + + Vector<Layer> layers; + }; + + void _clear_reflection_data(ReflectionData &rd); + void _update_reflection_data(ReflectionData &rd, int p_size, int p_mipmaps, bool p_use_array, RID p_base_cube, int p_base_layer, bool p_low_quality); + void _create_reflection_fast_filter(ReflectionData &rd, bool p_use_arrays); + void _create_reflection_importance_sample(ReflectionData &rd, bool p_use_arrays, int p_cube_side, int p_base_layer); + void _update_reflection_mipmaps(ReflectionData &rd); + + /* Sky shader */ + + enum SkyVersion { + SKY_VERSION_BACKGROUND, + SKY_VERSION_HALF_RES, + SKY_VERSION_QUARTER_RES, + SKY_VERSION_CUBEMAP, + SKY_VERSION_CUBEMAP_HALF_RES, + SKY_VERSION_CUBEMAP_QUARTER_RES, + SKY_VERSION_MAX + }; + + struct SkyShader { + SkyShaderRD shader; + ShaderCompilerRD compiler; + + RID default_shader; + RID default_material; + RID default_shader_rd; + } sky_shader; + + struct SkyShaderData : public RasterizerStorageRD::ShaderData { + bool valid; + RID version; + + RenderPipelineVertexFormatCacheRD pipelines[SKY_VERSION_MAX]; + Map<StringName, ShaderLanguage::ShaderNode::Uniform> uniforms; + Vector<ShaderCompilerRD::GeneratedCode::Texture> texture_uniforms; + + Vector<uint32_t> ubo_offsets; + uint32_t ubo_size; + + String path; + String code; + Map<StringName, RID> default_texture_params; + + bool uses_time; + bool uses_position; + bool uses_half_res; + bool uses_quarter_res; + bool uses_light; + + virtual void set_code(const String &p_Code); + virtual void set_default_texture_param(const StringName &p_name, RID p_texture); + virtual void get_param_list(List<PropertyInfo> *p_param_list) const; + virtual bool is_param_texture(const StringName &p_param) const; + virtual bool is_animated() const; + virtual bool casts_shadows() const; + virtual Variant get_default_parameter(const StringName &p_parameter) const; + SkyShaderData(); + virtual ~SkyShaderData(); + }; + + RasterizerStorageRD::ShaderData *_create_sky_shader_func(); + static RasterizerStorageRD::ShaderData *_create_sky_shader_funcs() { + return static_cast<RasterizerSceneRD *>(singleton)->_create_sky_shader_func(); + }; + + struct SkyMaterialData : public RasterizerStorageRD::MaterialData { + uint64_t last_frame; + SkyShaderData *shader_data; + RID uniform_buffer; + RID uniform_set; + Vector<RID> texture_cache; + Vector<uint8_t> ubo_data; + bool uniform_set_updated; + + virtual void set_render_priority(int p_priority) {} + virtual void set_next_pass(RID p_pass) {} + virtual void update_parameters(const Map<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty); + virtual ~SkyMaterialData(); + }; + + RasterizerStorageRD::MaterialData *_create_sky_material_func(SkyShaderData *p_shader); + static RasterizerStorageRD::MaterialData *_create_sky_material_funcs(RasterizerStorageRD::ShaderData *p_shader) { + return static_cast<RasterizerSceneRD *>(singleton)->_create_sky_material_func(static_cast<SkyShaderData *>(p_shader)); + }; + + enum SkyTextureSetVersion { + SKY_TEXTURE_SET_BACKGROUND, + SKY_TEXTURE_SET_HALF_RES, + SKY_TEXTURE_SET_QUARTER_RES, + SKY_TEXTURE_SET_CUBEMAP, + SKY_TEXTURE_SET_CUBEMAP_HALF_RES, + SKY_TEXTURE_SET_CUBEMAP_QUARTER_RES, + SKY_TEXTURE_SET_MAX + }; + + enum SkySet { + SKY_SET_SAMPLERS, + SKY_SET_MATERIAL, + SKY_SET_TEXTURES, + SKY_SET_LIGHTS, + SKY_SET_MAX + }; + + /* SKY */ + struct Sky { + RID radiance; + RID half_res_pass; + RID half_res_framebuffer; + RID quarter_res_pass; + RID quarter_res_framebuffer; + Size2i screen_size; + + RID texture_uniform_sets[SKY_TEXTURE_SET_MAX]; + RID uniform_set; + + RID material; + RID uniform_buffer; + + int radiance_size = 256; + + RS::SkyMode mode = RS::SKY_MODE_QUALITY; + + ReflectionData reflection; + bool dirty = false; + Sky *dirty_list = nullptr; + + //State to track when radiance cubemap needs updating + SkyMaterialData *prev_material; + Vector3 prev_position; + float prev_time; + }; + + Sky *dirty_sky_list = nullptr; + + void _sky_invalidate(Sky *p_sky); + void _update_dirty_skys(); + RID _get_sky_textures(Sky *p_sky, SkyTextureSetVersion p_version); + + uint32_t sky_ggx_samples_quality; + bool sky_use_cubemap_array; + + mutable RID_Owner<Sky> sky_owner; + + /* REFLECTION ATLAS */ + + struct ReflectionAtlas { + + int count = 0; + int size = 0; + + RID reflection; + RID depth_buffer; + RID depth_fb; + + struct Reflection { + RID owner; + ReflectionData data; + RID fbs[6]; + }; + + Vector<Reflection> reflections; + }; + + RID_Owner<ReflectionAtlas> reflection_atlas_owner; + + /* REFLECTION PROBE INSTANCE */ + + struct ReflectionProbeInstance { + + RID probe; + int atlas_index = -1; + RID atlas; + + bool dirty = true; + bool rendering = false; + int processing_layer = 1; + int processing_side = 0; + + uint32_t render_step = 0; + uint64_t last_pass = 0; + uint32_t render_index = 0; + + Transform transform; + }; + + mutable RID_Owner<ReflectionProbeInstance> reflection_probe_instance_owner; + + /* GIPROBE INSTANCE */ + + struct GIProbeLight { + + uint32_t type; + float energy; + float radius; + float attenuation; + + float color[3]; + float spot_angle_radians; + + float position[3]; + float spot_attenuation; + + float direction[3]; + uint32_t has_shadow; + }; + + struct GIProbePushConstant { + + int32_t limits[3]; + uint32_t stack_size; + + float emission_scale; + float propagation; + float dynamic_range; + uint32_t light_count; + + uint32_t cell_offset; + uint32_t cell_count; + float aniso_strength; + uint32_t pad; + }; + + struct GIProbeDynamicPushConstant { + + int32_t limits[3]; + uint32_t light_count; + int32_t x_dir[3]; + float z_base; + int32_t y_dir[3]; + float z_sign; + int32_t z_dir[3]; + float pos_multiplier; + uint32_t rect_pos[2]; + uint32_t rect_size[2]; + uint32_t prev_rect_ofs[2]; + uint32_t prev_rect_size[2]; + uint32_t flip_x; + uint32_t flip_y; + float dynamic_range; + uint32_t on_mipmap; + float propagation; + float pad[3]; + }; + + struct GIProbeInstance { + + RID probe; + RID texture; + RID anisotropy[2]; //only if anisotropy is used + RID anisotropy_r16[2]; //only if anisotropy is used + RID write_buffer; + + struct Mipmap { + RID texture; + RID anisotropy[2]; //only if anisotropy is used + RID uniform_set; + RID second_bounce_uniform_set; + RID write_uniform_set; + uint32_t level; + uint32_t cell_offset; + uint32_t cell_count; + }; + Vector<Mipmap> mipmaps; + + struct DynamicMap { + RID texture; //color normally, or emission on first pass + RID fb_depth; //actual depth buffer for the first pass, float depth for later passes + RID depth; //actual depth buffer for the first pass, float depth for later passes + RID normal; //normal buffer for the first pass + RID albedo; //emission buffer for the first pass + RID orm; //orm buffer for the first pass + RID fb; //used for rendering, only valid on first map + RID uniform_set; + uint32_t size; + int mipmap; // mipmap to write to, -1 if no mipmap assigned + }; + + Vector<DynamicMap> dynamic_maps; + + int slot = -1; + uint32_t last_probe_version = 0; + uint32_t last_probe_data_version = 0; + + uint64_t last_pass = 0; + uint32_t render_index = 0; + + bool has_dynamic_object_data = false; + + Transform transform; + }; + + GIProbeLight *gi_probe_lights; + uint32_t gi_probe_max_lights; + RID gi_probe_lights_uniform; + + bool gi_probe_use_anisotropy = false; + GIProbeQuality gi_probe_quality = GIPROBE_QUALITY_MEDIUM; + + Vector<RID> gi_probe_slots; + + enum { + GI_PROBE_SHADER_VERSION_COMPUTE_LIGHT, + GI_PROBE_SHADER_VERSION_COMPUTE_SECOND_BOUNCE, + GI_PROBE_SHADER_VERSION_COMPUTE_MIPMAP, + GI_PROBE_SHADER_VERSION_WRITE_TEXTURE, + GI_PROBE_SHADER_VERSION_DYNAMIC_OBJECT_LIGHTING, + GI_PROBE_SHADER_VERSION_DYNAMIC_SHRINK_WRITE, + GI_PROBE_SHADER_VERSION_DYNAMIC_SHRINK_PLOT, + GI_PROBE_SHADER_VERSION_DYNAMIC_SHRINK_WRITE_PLOT, + GI_PROBE_SHADER_VERSION_MAX + }; + GiprobeShaderRD giprobe_shader; + RID giprobe_lighting_shader_version; + RID giprobe_lighting_shader_version_shaders[GI_PROBE_SHADER_VERSION_MAX]; + RID giprobe_lighting_shader_version_pipelines[GI_PROBE_SHADER_VERSION_MAX]; + + mutable RID_Owner<GIProbeInstance> gi_probe_instance_owner; + + enum { + GI_PROBE_DEBUG_COLOR, + GI_PROBE_DEBUG_LIGHT, + GI_PROBE_DEBUG_EMISSION, + GI_PROBE_DEBUG_LIGHT_FULL, + GI_PROBE_DEBUG_MAX + }; + + struct GIProbeDebugPushConstant { + float projection[16]; + uint32_t cell_offset; + float dynamic_range; + float alpha; + uint32_t level; + int32_t bounds[3]; + uint32_t pad; + }; + + GiprobeDebugShaderRD giprobe_debug_shader; + RID giprobe_debug_shader_version; + RID giprobe_debug_shader_version_shaders[GI_PROBE_DEBUG_MAX]; + RenderPipelineVertexFormatCacheRD giprobe_debug_shader_version_pipelines[GI_PROBE_DEBUG_MAX]; + RID giprobe_debug_uniform_set; + + /* SHADOW ATLAS */ + + struct ShadowAtlas { + + enum { + QUADRANT_SHIFT = 27, + SHADOW_INDEX_MASK = (1 << QUADRANT_SHIFT) - 1, + SHADOW_INVALID = 0xFFFFFFFF + }; + + struct Quadrant { + + uint32_t subdivision; + + struct Shadow { + RID owner; + uint64_t version; + uint64_t alloc_tick; + + Shadow() { + version = 0; + alloc_tick = 0; + } + }; + + Vector<Shadow> shadows; + + Quadrant() { + subdivision = 0; //not in use + } + + } quadrants[4]; + + int size_order[4] = { 0, 1, 2, 3 }; + uint32_t smallest_subdiv = 0; + + int size = 0; + + RID depth; + RID fb; //for copying + + Map<RID, uint32_t> shadow_owners; + }; + + RID_Owner<ShadowAtlas> shadow_atlas_owner; + + bool _shadow_atlas_find_shadow(ShadowAtlas *shadow_atlas, int *p_in_quadrants, int p_quadrant_count, int p_current_subdiv, uint64_t p_tick, int &r_quadrant, int &r_shadow); + + /* DIRECTIONAL SHADOW */ + + struct DirectionalShadow { + RID depth; + RID fb; //for copying + + int light_count = 0; + int size = 0; + int current_light = 0; + } directional_shadow; + + /* SHADOW CUBEMAPS */ + + struct ShadowCubemap { + + RID cubemap; + RID side_fb[6]; + }; + + Map<int, ShadowCubemap> shadow_cubemaps; + ShadowCubemap *_get_shadow_cubemap(int p_size); + + struct ShadowMap { + RID depth; + RID fb; + }; + + Map<Vector2i, ShadowMap> shadow_maps; + ShadowMap *_get_shadow_map(const Size2i &p_size); + + void _create_shadow_cubemaps(); + + /* LIGHT INSTANCE */ + + struct LightInstance { + + struct ShadowTransform { + + CameraMatrix camera; + Transform transform; + float farplane; + float split; + float bias_scale; + Rect2 atlas_rect; + }; + + RS::LightType light_type; + + ShadowTransform shadow_transform[4]; + + RID self; + RID light; + Transform transform; + + Vector3 light_vector; + Vector3 spot_vector; + float linear_att; + + uint64_t shadow_pass = 0; + uint64_t last_scene_pass = 0; + uint64_t last_scene_shadow_pass = 0; + uint64_t last_pass = 0; + uint32_t light_index = 0; + uint32_t light_directional_index = 0; + + uint32_t current_shadow_atlas_key; + + Vector2 dp; + + Rect2 directional_rect; + + Set<RID> shadow_atlases; //shadow atlases where this light is registered + + LightInstance() {} + }; + + mutable RID_Owner<LightInstance> light_instance_owner; + + /* ENVIRONMENT */ + + struct Environent { + + // BG + RS::EnvironmentBG background = RS::ENV_BG_CLEAR_COLOR; + RID sky; + float sky_custom_fov = 0.0; + Basis sky_orientation; + Color bg_color; + float bg_energy = 1.0; + int canvas_max_layer = 0; + RS::EnvironmentAmbientSource ambient_source = RS::ENV_AMBIENT_SOURCE_BG; + Color ambient_light; + float ambient_light_energy = 1.0; + float ambient_sky_contribution = 1.0; + RS::EnvironmentReflectionSource reflection_source = RS::ENV_REFLECTION_SOURCE_BG; + Color ao_color; + + /// Tonemap + + RS::EnvironmentToneMapper tone_mapper; + float exposure = 1.0; + float white = 1.0; + bool auto_exposure = false; + float min_luminance = 0.2; + float max_luminance = 8.0; + float auto_exp_speed = 0.2; + float auto_exp_scale = 0.5; + uint64_t auto_exposure_version = 0; + + /// Glow + + bool glow_enabled = false; + int glow_levels = (1 << 2) | (1 << 4); + float glow_intensity = 0.8; + float glow_strength = 1.0; + float glow_bloom = 0.0; + float glow_mix = 0.01; + RS::EnvironmentGlowBlendMode glow_blend_mode = RS::ENV_GLOW_BLEND_MODE_SOFTLIGHT; + float glow_hdr_bleed_threshold = 1.0; + float glow_hdr_luminance_cap = 12.0; + float glow_hdr_bleed_scale = 2.0; + bool glow_bicubic_upscale = false; + + /// SSAO + + bool ssao_enabled = false; + float ssao_radius = 1; + float ssao_intensity = 1; + float ssao_bias = 0.01; + float ssao_direct_light_affect = 0.0; + float ssao_ao_channel_affect = 0.0; + float ssao_blur_edge_sharpness = 4.0; + RS::EnvironmentSSAOBlur ssao_blur = RS::ENV_SSAO_BLUR_3x3; + }; + + RS::EnvironmentSSAOQuality ssao_quality = RS::ENV_SSAO_QUALITY_MEDIUM; + bool ssao_half_size = false; + + static uint64_t auto_exposure_counter; + + mutable RID_Owner<Environent> environment_owner; + + /* CAMERA EFFECTS */ + + struct CameraEffects { + + bool dof_blur_far_enabled = false; + float dof_blur_far_distance = 10; + float dof_blur_far_transition = 5; + + bool dof_blur_near_enabled = false; + float dof_blur_near_distance = 2; + float dof_blur_near_transition = 1; + + float dof_blur_amount = 0.1; + + bool override_exposure_enabled = false; + float override_exposure = 1; + }; + + RS::DOFBlurQuality dof_blur_quality = RS::DOF_BLUR_QUALITY_MEDIUM; + RS::DOFBokehShape dof_blur_bokeh_shape = RS::DOF_BOKEH_HEXAGON; + bool dof_blur_use_jitter = false; + + mutable RID_Owner<CameraEffects> camera_effects_owner; + + /* RENDER BUFFERS */ + + struct RenderBuffers { + + RenderBufferData *data = nullptr; + int width = 0, height = 0; + RS::ViewportMSAA msaa = RS::VIEWPORT_MSAA_DISABLED; + RID render_target; + + uint64_t auto_exposure_version = 1; + + RID texture; //main texture for rendering to, must be filled after done rendering + RID depth_texture; //main depth texture + + //built-in textures used for ping pong image processing and blurring + struct Blur { + RID texture; + + struct Mipmap { + RID texture; + RID framebuffer; + int width; + int height; + }; + + Vector<Mipmap> mipmaps; + }; + + Blur blur[2]; //the second one starts from the first mipmap + + struct Luminance { + + Vector<RID> reduce; + RID current; + } luminance; + + struct SSAO { + RID depth; + Vector<RID> depth_slices; + RID ao[2]; + RID ao_full; //when using half-size + } ssao; + }; + + bool screen_space_roughness_limiter = false; + float screen_space_roughness_limiter_curve = 1.0; + + mutable RID_Owner<RenderBuffers> render_buffers_owner; + + void _free_render_buffer_data(RenderBuffers *rb); + void _allocate_blur_textures(RenderBuffers *rb); + void _allocate_luminance_textures(RenderBuffers *rb); + + void _render_buffers_debug_draw(RID p_render_buffers, RID p_shadow_atlas); + void _render_buffers_post_process_and_tonemap(RID p_render_buffers, RID p_environment, RID p_camera_effects, const CameraMatrix &p_projection); + + uint64_t scene_pass = 0; + uint64_t shadow_atlas_realloc_tolerance_msec = 500; + +public: + /* SHADOW ATLAS API */ + + RID shadow_atlas_create(); + void shadow_atlas_set_size(RID p_atlas, int p_size); + void shadow_atlas_set_quadrant_subdivision(RID p_atlas, int p_quadrant, int p_subdivision); + bool shadow_atlas_update_light(RID p_atlas, RID p_light_intance, float p_coverage, uint64_t p_light_version); + _FORCE_INLINE_ bool shadow_atlas_owns_light_instance(RID p_atlas, RID p_light_intance) { + ShadowAtlas *atlas = shadow_atlas_owner.getornull(p_atlas); + ERR_FAIL_COND_V(!atlas, false); + return atlas->shadow_owners.has(p_light_intance); + } + + _FORCE_INLINE_ RID shadow_atlas_get_texture(RID p_atlas) { + ShadowAtlas *atlas = shadow_atlas_owner.getornull(p_atlas); + ERR_FAIL_COND_V(!atlas, RID()); + return atlas->depth; + } + + _FORCE_INLINE_ Size2i shadow_atlas_get_size(RID p_atlas) { + ShadowAtlas *atlas = shadow_atlas_owner.getornull(p_atlas); + ERR_FAIL_COND_V(!atlas, Size2i()); + return Size2(atlas->size, atlas->size); + } + + void directional_shadow_atlas_set_size(int p_size); + int get_directional_light_shadow_size(RID p_light_intance); + void set_directional_shadow_count(int p_count); + + _FORCE_INLINE_ RID directional_shadow_get_texture() { + return directional_shadow.depth; + } + + _FORCE_INLINE_ Size2i directional_shadow_get_size() { + return Size2i(directional_shadow.size, directional_shadow.size); + } + + /* SKY API */ + + RID sky_create(); + void sky_set_radiance_size(RID p_sky, int p_radiance_size); + void sky_set_mode(RID p_sky, RS::SkyMode p_mode); + void sky_set_material(RID p_sky, RID p_material); + + RID sky_get_radiance_texture_rd(RID p_sky) const; + RID sky_get_radiance_uniform_set_rd(RID p_sky, RID p_shader, int p_set) const; + RID sky_get_material(RID p_sky) const; + + /* ENVIRONMENT API */ + + RID environment_create(); + + void environment_set_background(RID p_env, RS::EnvironmentBG p_bg); + void environment_set_sky(RID p_env, RID p_sky); + void environment_set_sky_custom_fov(RID p_env, float p_scale); + void environment_set_sky_orientation(RID p_env, const Basis &p_orientation); + void environment_set_bg_color(RID p_env, const Color &p_color); + void environment_set_bg_energy(RID p_env, float p_energy); + void environment_set_canvas_max_layer(RID p_env, int p_max_layer); + void environment_set_ambient_light(RID p_env, const Color &p_color, RS::EnvironmentAmbientSource p_ambient = RS::ENV_AMBIENT_SOURCE_BG, float p_energy = 1.0, float p_sky_contribution = 0.0, RS::EnvironmentReflectionSource p_reflection_source = RS::ENV_REFLECTION_SOURCE_BG, const Color &p_ao_color = Color()); + + RS::EnvironmentBG environment_get_background(RID p_env) const; + RID environment_get_sky(RID p_env) const; + float environment_get_sky_custom_fov(RID p_env) const; + Basis environment_get_sky_orientation(RID p_env) const; + Color environment_get_bg_color(RID p_env) const; + float environment_get_bg_energy(RID p_env) const; + int environment_get_canvas_max_layer(RID p_env) const; + Color environment_get_ambient_light_color(RID p_env) const; + RS::EnvironmentAmbientSource environment_get_ambient_light_ambient_source(RID p_env) const; + float environment_get_ambient_light_ambient_energy(RID p_env) const; + float environment_get_ambient_sky_contribution(RID p_env) const; + RS::EnvironmentReflectionSource environment_get_reflection_source(RID p_env) const; + Color environment_get_ao_color(RID p_env) const; + + bool is_environment(RID p_env) const; + + void environment_set_glow(RID p_env, bool p_enable, int p_level_flags, float p_intensity, float p_strength, float p_mix, float p_bloom_threshold, RS::EnvironmentGlowBlendMode p_blend_mode, float p_hdr_bleed_threshold, float p_hdr_bleed_scale, float p_hdr_luminance_cap, bool p_bicubic_upscale); + + void environment_set_fog(RID p_env, bool p_enable, float p_begin, float p_end, RID p_gradient_texture) {} + + void environment_set_ssr(RID p_env, bool p_enable, int p_max_steps, float p_fade_int, float p_fade_out, float p_depth_tolerance, bool p_roughness) {} + void environment_set_ssao(RID p_env, bool p_enable, float p_radius, float p_intensity, float p_bias, float p_light_affect, float p_ao_channel_affect, RS::EnvironmentSSAOBlur p_blur, float p_bilateral_sharpness); + void environment_set_ssao_quality(RS::EnvironmentSSAOQuality p_quality, bool p_half_size); + bool environment_is_ssao_enabled(RID p_env) const; + float environment_get_ssao_ao_affect(RID p_env) const; + float environment_get_ssao_light_affect(RID p_env) const; + bool environment_is_ssr_enabled(RID p_env) const; + + void environment_set_tonemap(RID p_env, RS::EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white, bool p_auto_exposure, float p_min_luminance, float p_max_luminance, float p_auto_exp_speed, float p_auto_exp_scale); + void environment_set_adjustment(RID p_env, bool p_enable, float p_brightness, float p_contrast, float p_saturation, RID p_ramp) {} + + void environment_set_fog(RID p_env, bool p_enable, const Color &p_color, const Color &p_sun_color, float p_sun_amount) {} + void environment_set_fog_depth(RID p_env, bool p_enable, float p_depth_begin, float p_depth_end, float p_depth_curve, bool p_transmit, float p_transmit_curve) {} + void environment_set_fog_height(RID p_env, bool p_enable, float p_min_height, float p_max_height, float p_height_curve) {} + + virtual RID camera_effects_create(); + + virtual void camera_effects_set_dof_blur_quality(RS::DOFBlurQuality p_quality, bool p_use_jitter); + virtual void camera_effects_set_dof_blur_bokeh_shape(RS::DOFBokehShape p_shape); + + virtual void camera_effects_set_dof_blur(RID p_camera_effects, bool p_far_enable, float p_far_distance, float p_far_transition, bool p_near_enable, float p_near_distance, float p_near_transition, float p_amount); + virtual void camera_effects_set_custom_exposure(RID p_camera_effects, bool p_enable, float p_exposure); + + RID light_instance_create(RID p_light); + void light_instance_set_transform(RID p_light_instance, const Transform &p_transform); + void light_instance_set_shadow_transform(RID p_light_instance, const CameraMatrix &p_projection, const Transform &p_transform, float p_far, float p_split, int p_pass, float p_bias_scale = 1.0); + void light_instance_mark_visible(RID p_light_instance); + + _FORCE_INLINE_ RID light_instance_get_base_light(RID p_light_instance) { + LightInstance *li = light_instance_owner.getornull(p_light_instance); + return li->light; + } + + _FORCE_INLINE_ Transform light_instance_get_base_transform(RID p_light_instance) { + LightInstance *li = light_instance_owner.getornull(p_light_instance); + return li->transform; + } + + _FORCE_INLINE_ Rect2 light_instance_get_shadow_atlas_rect(RID p_light_instance, RID p_shadow_atlas) { + + ShadowAtlas *shadow_atlas = shadow_atlas_owner.getornull(p_shadow_atlas); + LightInstance *li = light_instance_owner.getornull(p_light_instance); + uint32_t key = shadow_atlas->shadow_owners[li->self]; + + uint32_t quadrant = (key >> ShadowAtlas::QUADRANT_SHIFT) & 0x3; + uint32_t shadow = key & ShadowAtlas::SHADOW_INDEX_MASK; + + ERR_FAIL_COND_V(shadow >= (uint32_t)shadow_atlas->quadrants[quadrant].shadows.size(), Rect2()); + + uint32_t atlas_size = shadow_atlas->size; + uint32_t quadrant_size = atlas_size >> 1; + + uint32_t x = (quadrant & 1) * quadrant_size; + uint32_t y = (quadrant >> 1) * quadrant_size; + + uint32_t shadow_size = (quadrant_size / shadow_atlas->quadrants[quadrant].subdivision); + x += (shadow % shadow_atlas->quadrants[quadrant].subdivision) * shadow_size; + y += (shadow / shadow_atlas->quadrants[quadrant].subdivision) * shadow_size; + + uint32_t width = shadow_size; + uint32_t height = shadow_size; + + return Rect2(x / float(shadow_atlas->size), y / float(shadow_atlas->size), width / float(shadow_atlas->size), height / float(shadow_atlas->size)); + } + + _FORCE_INLINE_ CameraMatrix light_instance_get_shadow_camera(RID p_light_instance, int p_index) { + + LightInstance *li = light_instance_owner.getornull(p_light_instance); + return li->shadow_transform[p_index].camera; + } + + _FORCE_INLINE_ Transform light_instance_get_shadow_transform(RID p_light_instance, int p_index) { + + LightInstance *li = light_instance_owner.getornull(p_light_instance); + return li->shadow_transform[p_index].transform; + } + + _FORCE_INLINE_ Rect2 light_instance_get_directional_shadow_atlas_rect(RID p_light_instance, int p_index) { + + LightInstance *li = light_instance_owner.getornull(p_light_instance); + return li->shadow_transform[p_index].atlas_rect; + } + + _FORCE_INLINE_ float light_instance_get_directional_shadow_split(RID p_light_instance, int p_index) { + + LightInstance *li = light_instance_owner.getornull(p_light_instance); + return li->shadow_transform[p_index].split; + } + + _FORCE_INLINE_ void light_instance_set_render_pass(RID p_light_instance, uint64_t p_pass) { + LightInstance *li = light_instance_owner.getornull(p_light_instance); + li->last_pass = p_pass; + } + + _FORCE_INLINE_ uint64_t light_instance_get_render_pass(RID p_light_instance) { + LightInstance *li = light_instance_owner.getornull(p_light_instance); + return li->last_pass; + } + + _FORCE_INLINE_ void light_instance_set_index(RID p_light_instance, uint32_t p_index) { + LightInstance *li = light_instance_owner.getornull(p_light_instance); + li->light_index = p_index; + } + + _FORCE_INLINE_ uint32_t light_instance_get_index(RID p_light_instance) { + LightInstance *li = light_instance_owner.getornull(p_light_instance); + return li->light_index; + } + + _FORCE_INLINE_ RS::LightType light_instance_get_type(RID p_light_instance) { + LightInstance *li = light_instance_owner.getornull(p_light_instance); + return li->light_type; + } + + virtual RID reflection_atlas_create(); + virtual void reflection_atlas_set_size(RID p_ref_atlas, int p_reflection_size, int p_reflection_count); + _FORCE_INLINE_ RID reflection_atlas_get_texture(RID p_ref_atlas) { + ReflectionAtlas *atlas = reflection_atlas_owner.getornull(p_ref_atlas); + ERR_FAIL_COND_V(!atlas, RID()); + return atlas->reflection; + } + + virtual RID reflection_probe_instance_create(RID p_probe); + virtual void reflection_probe_instance_set_transform(RID p_instance, const Transform &p_transform); + virtual void reflection_probe_release_atlas_index(RID p_instance); + virtual bool reflection_probe_instance_needs_redraw(RID p_instance); + virtual bool reflection_probe_instance_has_reflection(RID p_instance); + virtual bool reflection_probe_instance_begin_render(RID p_instance, RID p_reflection_atlas); + virtual bool reflection_probe_instance_postprocess_step(RID p_instance); + + uint32_t reflection_probe_instance_get_resolution(RID p_instance); + RID reflection_probe_instance_get_framebuffer(RID p_instance, int p_index); + RID reflection_probe_instance_get_depth_framebuffer(RID p_instance, int p_index); + + _FORCE_INLINE_ RID reflection_probe_instance_get_probe(RID p_instance) { + ReflectionProbeInstance *rpi = reflection_probe_instance_owner.getornull(p_instance); + ERR_FAIL_COND_V(!rpi, RID()); + + return rpi->probe; + } + + _FORCE_INLINE_ void reflection_probe_instance_set_render_index(RID p_instance, uint32_t p_render_index) { + ReflectionProbeInstance *rpi = reflection_probe_instance_owner.getornull(p_instance); + ERR_FAIL_COND(!rpi); + rpi->render_index = p_render_index; + } + + _FORCE_INLINE_ uint32_t reflection_probe_instance_get_render_index(RID p_instance) { + ReflectionProbeInstance *rpi = reflection_probe_instance_owner.getornull(p_instance); + ERR_FAIL_COND_V(!rpi, 0); + + return rpi->render_index; + } + + _FORCE_INLINE_ void reflection_probe_instance_set_render_pass(RID p_instance, uint32_t p_render_pass) { + ReflectionProbeInstance *rpi = reflection_probe_instance_owner.getornull(p_instance); + ERR_FAIL_COND(!rpi); + rpi->last_pass = p_render_pass; + } + + _FORCE_INLINE_ uint32_t reflection_probe_instance_get_render_pass(RID p_instance) { + ReflectionProbeInstance *rpi = reflection_probe_instance_owner.getornull(p_instance); + ERR_FAIL_COND_V(!rpi, 0); + + return rpi->last_pass; + } + + _FORCE_INLINE_ Transform reflection_probe_instance_get_transform(RID p_instance) { + ReflectionProbeInstance *rpi = reflection_probe_instance_owner.getornull(p_instance); + ERR_FAIL_COND_V(!rpi, Transform()); + + return rpi->transform; + } + + _FORCE_INLINE_ int reflection_probe_instance_get_atlas_index(RID p_instance) { + ReflectionProbeInstance *rpi = reflection_probe_instance_owner.getornull(p_instance); + ERR_FAIL_COND_V(!rpi, -1); + + return rpi->atlas_index; + } + + RID gi_probe_instance_create(RID p_base); + void gi_probe_instance_set_transform_to_data(RID p_probe, const Transform &p_xform); + bool gi_probe_needs_update(RID p_probe) const; + void gi_probe_update(RID p_probe, bool p_update_light_instances, const Vector<RID> &p_light_instances, int p_dynamic_object_count, InstanceBase **p_dynamic_objects); + + _FORCE_INLINE_ uint32_t gi_probe_instance_get_slot(RID p_probe) { + GIProbeInstance *gi_probe = gi_probe_instance_owner.getornull(p_probe); + return gi_probe->slot; + } + _FORCE_INLINE_ RID gi_probe_instance_get_base_probe(RID p_probe) { + GIProbeInstance *gi_probe = gi_probe_instance_owner.getornull(p_probe); + return gi_probe->probe; + } + _FORCE_INLINE_ Transform gi_probe_instance_get_transform_to_cell(RID p_probe) { + GIProbeInstance *gi_probe = gi_probe_instance_owner.getornull(p_probe); + return storage->gi_probe_get_to_cell_xform(gi_probe->probe) * gi_probe->transform.affine_inverse(); + } + + _FORCE_INLINE_ RID gi_probe_instance_get_texture(RID p_probe) { + GIProbeInstance *gi_probe = gi_probe_instance_owner.getornull(p_probe); + return gi_probe->texture; + } + _FORCE_INLINE_ RID gi_probe_instance_get_aniso_texture(RID p_probe, int p_index) { + GIProbeInstance *gi_probe = gi_probe_instance_owner.getornull(p_probe); + return gi_probe->anisotropy[p_index]; + } + + _FORCE_INLINE_ void gi_probe_instance_set_render_index(RID p_instance, uint32_t p_render_index) { + GIProbeInstance *gi_probe = gi_probe_instance_owner.getornull(p_instance); + ERR_FAIL_COND(!gi_probe); + gi_probe->render_index = p_render_index; + } + + _FORCE_INLINE_ uint32_t gi_probe_instance_get_render_index(RID p_instance) { + GIProbeInstance *gi_probe = gi_probe_instance_owner.getornull(p_instance); + ERR_FAIL_COND_V(!gi_probe, 0); + + return gi_probe->render_index; + } + + _FORCE_INLINE_ void gi_probe_instance_set_render_pass(RID p_instance, uint32_t p_render_pass) { + GIProbeInstance *g_probe = gi_probe_instance_owner.getornull(p_instance); + ERR_FAIL_COND(!g_probe); + g_probe->last_pass = p_render_pass; + } + + _FORCE_INLINE_ uint32_t gi_probe_instance_get_render_pass(RID p_instance) { + GIProbeInstance *g_probe = gi_probe_instance_owner.getornull(p_instance); + ERR_FAIL_COND_V(!g_probe, 0); + + return g_probe->last_pass; + } + + const Vector<RID> &gi_probe_get_slots() const; + _FORCE_INLINE_ bool gi_probe_is_anisotropic() const { + return gi_probe_use_anisotropy; + } + GIProbeQuality gi_probe_get_quality() const; + + RID render_buffers_create(); + void render_buffers_configure(RID p_render_buffers, RID p_render_target, int p_width, int p_height, RS::ViewportMSAA p_msaa); + + RID render_buffers_get_ao_texture(RID p_render_buffers); + RID render_buffers_get_back_buffer_texture(RID p_render_buffers); + + void render_scene(RID p_render_buffers, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID *p_light_cull_result, int p_light_cull_count, RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, RID *p_gi_probe_cull_result, int p_gi_probe_cull_count, RID p_environment, RID p_shadow_atlas, RID p_camera_effects, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass); + + void render_shadow(RID p_light, RID p_shadow_atlas, int p_pass, InstanceBase **p_cull_result, int p_cull_count); + + void render_material(const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID p_framebuffer, const Rect2i &p_region); + + virtual void set_scene_pass(uint64_t p_pass) { scene_pass = p_pass; } + _FORCE_INLINE_ uint64_t get_scene_pass() { return scene_pass; } + + virtual void screen_space_roughness_limiter_set_active(bool p_enable, float p_curve); + virtual bool screen_space_roughness_limiter_is_active() const; + virtual float screen_space_roughness_limiter_get_curve() const; + + int get_roughness_layers() const; + bool is_using_radiance_cubemap_array() const; + + virtual bool free(RID p_rid); + + virtual void update(); + + virtual void set_debug_draw_mode(RS::ViewportDebugDraw p_debug_draw); + _FORCE_INLINE_ RS::ViewportDebugDraw get_debug_draw_mode() const { return debug_draw; } + + virtual void set_time(double p_time, double p_step); + + RasterizerSceneRD(RasterizerStorageRD *p_storage); + ~RasterizerSceneRD(); +}; + +#endif // RASTERIZER_SCENE_RD_H diff --git a/servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp b/servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp new file mode 100644 index 0000000000..83af15602c --- /dev/null +++ b/servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp @@ -0,0 +1,4822 @@ +/*************************************************************************/ +/* rasterizer_storage_rd.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "rasterizer_storage_rd.h" +#include "core/engine.h" +#include "core/project_settings.h" +#include "servers/rendering/shader_language.h" + +Ref<Image> RasterizerStorageRD::_validate_texture_format(const Ref<Image> &p_image, TextureToRDFormat &r_format) { + + Ref<Image> image = p_image->duplicate(); + + switch (p_image->get_format()) { + case Image::FORMAT_L8: { + r_format.format = RD::DATA_FORMAT_R8_UNORM; + r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; + r_format.swizzle_g = RD::TEXTURE_SWIZZLE_R; + r_format.swizzle_b = RD::TEXTURE_SWIZZLE_R; + r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE; + } break; //luminance + case Image::FORMAT_LA8: { + r_format.format = RD::DATA_FORMAT_R8G8_UNORM; + r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; + r_format.swizzle_g = RD::TEXTURE_SWIZZLE_R; + r_format.swizzle_b = RD::TEXTURE_SWIZZLE_R; + r_format.swizzle_a = RD::TEXTURE_SWIZZLE_G; + } break; //luminance-alpha + case Image::FORMAT_R8: { + r_format.format = RD::DATA_FORMAT_R8_UNORM; + r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; + r_format.swizzle_g = RD::TEXTURE_SWIZZLE_ZERO; + r_format.swizzle_b = RD::TEXTURE_SWIZZLE_ZERO; + r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE; + } break; + case Image::FORMAT_RG8: { + r_format.format = RD::DATA_FORMAT_R8G8_UNORM; + r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; + r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G; + r_format.swizzle_b = RD::TEXTURE_SWIZZLE_ZERO; + r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE; + } break; + case Image::FORMAT_RGB8: { + //this format is not mandatory for specification, check if supported first + if (false && RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_R8G8B8_UNORM, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT) && RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_R8G8B8_SRGB, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT)) { + r_format.format = RD::DATA_FORMAT_R8G8B8_UNORM; + r_format.format_srgb = RD::DATA_FORMAT_R8G8B8_SRGB; + } else { + //not supported, reconvert + r_format.format = RD::DATA_FORMAT_R8G8B8A8_UNORM; + r_format.format_srgb = RD::DATA_FORMAT_R8G8B8A8_SRGB; + image->convert(Image::FORMAT_RGBA8); + } + r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; + r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G; + r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B; + r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE; + + } break; + case Image::FORMAT_RGBA8: { + r_format.format = RD::DATA_FORMAT_R8G8B8A8_UNORM; + r_format.format_srgb = RD::DATA_FORMAT_R8G8B8A8_SRGB; + r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; + r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G; + r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B; + r_format.swizzle_a = RD::TEXTURE_SWIZZLE_A; + } break; + case Image::FORMAT_RGBA4444: { + r_format.format = RD::DATA_FORMAT_B4G4R4A4_UNORM_PACK16; + r_format.swizzle_r = RD::TEXTURE_SWIZZLE_B; //needs swizzle + r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G; + r_format.swizzle_b = RD::TEXTURE_SWIZZLE_R; + r_format.swizzle_a = RD::TEXTURE_SWIZZLE_A; + } break; + case Image::FORMAT_RGB565: { + r_format.format = RD::DATA_FORMAT_B5G6R5_UNORM_PACK16; + r_format.swizzle_r = RD::TEXTURE_SWIZZLE_B; + r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G; + r_format.swizzle_b = RD::TEXTURE_SWIZZLE_R; + r_format.swizzle_a = RD::TEXTURE_SWIZZLE_A; + } break; + case Image::FORMAT_RF: { + r_format.format = RD::DATA_FORMAT_R32_SFLOAT; + r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; + r_format.swizzle_g = RD::TEXTURE_SWIZZLE_ZERO; + r_format.swizzle_b = RD::TEXTURE_SWIZZLE_ZERO; + r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE; + } break; //float + case Image::FORMAT_RGF: { + r_format.format = RD::DATA_FORMAT_R32G32_SFLOAT; + r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; + r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G; + r_format.swizzle_b = RD::TEXTURE_SWIZZLE_ZERO; + r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE; + } break; + case Image::FORMAT_RGBF: { + //this format is not mandatory for specification, check if supported first + if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_R32G32B32_SFLOAT, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT)) { + r_format.format = RD::DATA_FORMAT_R32G32B32_SFLOAT; + } else { + //not supported, reconvert + r_format.format = RD::DATA_FORMAT_R32G32B32A32_SFLOAT; + image->convert(Image::FORMAT_RGBAF); + } + + r_format.format = RD::DATA_FORMAT_R32G32B32A32_SFLOAT; + r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; + r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G; + r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B; + r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE; + } break; + case Image::FORMAT_RGBAF: { + r_format.format = RD::DATA_FORMAT_R32G32B32A32_SFLOAT; + r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; + r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G; + r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B; + r_format.swizzle_a = RD::TEXTURE_SWIZZLE_A; + + } break; + case Image::FORMAT_RH: { + r_format.format = RD::DATA_FORMAT_R16_SFLOAT; + r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; + r_format.swizzle_g = RD::TEXTURE_SWIZZLE_ZERO; + r_format.swizzle_b = RD::TEXTURE_SWIZZLE_ZERO; + r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE; + + } break; //half float + case Image::FORMAT_RGH: { + r_format.format = RD::DATA_FORMAT_R16G16_SFLOAT; + r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; + r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G; + r_format.swizzle_b = RD::TEXTURE_SWIZZLE_ZERO; + r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE; + + } break; + case Image::FORMAT_RGBH: { + //this format is not mandatory for specification, check if supported first + if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_R16G16B16_SFLOAT, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT)) { + r_format.format = RD::DATA_FORMAT_R16G16B16_SFLOAT; + } else { + //not supported, reconvert + r_format.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT; + image->convert(Image::FORMAT_RGBAH); + } + + r_format.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT; + r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; + r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G; + r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B; + r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE; + } break; + case Image::FORMAT_RGBAH: { + r_format.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT; + r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; + r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G; + r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B; + r_format.swizzle_a = RD::TEXTURE_SWIZZLE_A; + + } break; + case Image::FORMAT_RGBE9995: { + r_format.format = RD::DATA_FORMAT_E5B9G9R9_UFLOAT_PACK32; +#ifndef _MSC_VER +#warning TODO need to make a function in Image to swap bits for this +#endif + r_format.swizzle_r = RD::TEXTURE_SWIZZLE_IDENTITY; + r_format.swizzle_g = RD::TEXTURE_SWIZZLE_IDENTITY; + r_format.swizzle_b = RD::TEXTURE_SWIZZLE_IDENTITY; + r_format.swizzle_a = RD::TEXTURE_SWIZZLE_IDENTITY; + } break; + case Image::FORMAT_DXT1: { + if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_BC1_RGB_UNORM_BLOCK, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT)) { + r_format.format = RD::DATA_FORMAT_BC1_RGB_UNORM_BLOCK; + r_format.format_srgb = RD::DATA_FORMAT_BC1_RGB_SRGB_BLOCK; + } else { + //not supported, reconvert + r_format.format = RD::DATA_FORMAT_R8G8B8A8_UNORM; + r_format.format_srgb = RD::DATA_FORMAT_R8G8B8A8_SRGB; + image->decompress(); + image->convert(Image::FORMAT_RGBA8); + } + r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; + r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G; + r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B; + r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE; + + } break; //s3tc bc1 + case Image::FORMAT_DXT3: { + if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_BC2_UNORM_BLOCK, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT)) { + r_format.format = RD::DATA_FORMAT_BC2_UNORM_BLOCK; + r_format.format_srgb = RD::DATA_FORMAT_BC2_SRGB_BLOCK; + } else { + //not supported, reconvert + r_format.format = RD::DATA_FORMAT_R8G8B8A8_UNORM; + r_format.format_srgb = RD::DATA_FORMAT_R8G8B8A8_SRGB; + image->decompress(); + image->convert(Image::FORMAT_RGBA8); + } + r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; + r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G; + r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B; + r_format.swizzle_a = RD::TEXTURE_SWIZZLE_A; + + } break; //bc2 + case Image::FORMAT_DXT5: { + if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_BC3_UNORM_BLOCK, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT)) { + r_format.format = RD::DATA_FORMAT_BC3_UNORM_BLOCK; + r_format.format_srgb = RD::DATA_FORMAT_BC3_SRGB_BLOCK; + } else { + //not supported, reconvert + r_format.format = RD::DATA_FORMAT_R8G8B8A8_UNORM; + r_format.format_srgb = RD::DATA_FORMAT_R8G8B8A8_SRGB; + image->decompress(); + image->convert(Image::FORMAT_RGBA8); + } + r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; + r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G; + r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B; + r_format.swizzle_a = RD::TEXTURE_SWIZZLE_A; + } break; //bc3 + case Image::FORMAT_RGTC_R: { + if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_BC4_UNORM_BLOCK, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT)) { + r_format.format = RD::DATA_FORMAT_BC4_UNORM_BLOCK; + } else { + //not supported, reconvert + r_format.format = RD::DATA_FORMAT_R8_UNORM; + image->decompress(); + image->convert(Image::FORMAT_R8); + } + r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; + r_format.swizzle_g = RD::TEXTURE_SWIZZLE_ZERO; + r_format.swizzle_b = RD::TEXTURE_SWIZZLE_ZERO; + r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE; + + } break; + case Image::FORMAT_RGTC_RG: { + if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_BC5_UNORM_BLOCK, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT)) { + r_format.format = RD::DATA_FORMAT_BC5_UNORM_BLOCK; + } else { + //not supported, reconvert + r_format.format = RD::DATA_FORMAT_R8G8_UNORM; + image->decompress(); + image->convert(Image::FORMAT_RG8); + } + r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; + r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G; + r_format.swizzle_b = RD::TEXTURE_SWIZZLE_ZERO; + r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE; + + } break; + case Image::FORMAT_BPTC_RGBA: { + if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_BC7_UNORM_BLOCK, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT)) { + r_format.format = RD::DATA_FORMAT_BC7_UNORM_BLOCK; + r_format.format_srgb = RD::DATA_FORMAT_BC7_SRGB_BLOCK; + } else { + //not supported, reconvert + r_format.format = RD::DATA_FORMAT_R8G8B8A8_UNORM; + r_format.format_srgb = RD::DATA_FORMAT_R8G8B8A8_SRGB; + image->decompress(); + image->convert(Image::FORMAT_RGBA8); + } + r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; + r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G; + r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B; + r_format.swizzle_a = RD::TEXTURE_SWIZZLE_A; + + } break; //btpc bc7 + case Image::FORMAT_BPTC_RGBF: { + if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_BC6H_SFLOAT_BLOCK, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT)) { + r_format.format = RD::DATA_FORMAT_BC6H_SFLOAT_BLOCK; + } else { + //not supported, reconvert + r_format.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT; + image->decompress(); + image->convert(Image::FORMAT_RGBAH); + } + r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; + r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G; + r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B; + r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE; + } break; //float bc6h + case Image::FORMAT_BPTC_RGBFU: { + if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_BC6H_UFLOAT_BLOCK, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT)) { + r_format.format = RD::DATA_FORMAT_BC6H_UFLOAT_BLOCK; + } else { + //not supported, reconvert + r_format.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT; + image->decompress(); + image->convert(Image::FORMAT_RGBAH); + } + r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; + r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G; + r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B; + r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE; + } break; //unsigned float bc6hu + case Image::FORMAT_PVRTC2: { + //this is not properly supported by MoltekVK it seems, so best to use ETC2 + if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT)) { + r_format.format = RD::DATA_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG; + r_format.format_srgb = RD::DATA_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG; + } else { + //not supported, reconvert + r_format.format = RD::DATA_FORMAT_R8G8B8A8_UNORM; + r_format.format_srgb = RD::DATA_FORMAT_R8G8B8A8_SRGB; + image->decompress(); + image->convert(Image::FORMAT_RGBA8); + } + r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; + r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G; + r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B; + r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE; + + } break; //pvrtc + case Image::FORMAT_PVRTC2A: { + //this is not properly supported by MoltekVK it seems, so best to use ETC2 + if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT)) { + r_format.format = RD::DATA_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG; + r_format.format_srgb = RD::DATA_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG; + } else { + //not supported, reconvert + r_format.format = RD::DATA_FORMAT_R8G8B8A8_UNORM; + r_format.format_srgb = RD::DATA_FORMAT_R8G8B8A8_SRGB; + image->decompress(); + image->convert(Image::FORMAT_RGBA8); + } + r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; + r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G; + r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B; + r_format.swizzle_a = RD::TEXTURE_SWIZZLE_A; + } break; + case Image::FORMAT_PVRTC4: { + //this is not properly supported by MoltekVK it seems, so best to use ETC2 + if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT)) { + r_format.format = RD::DATA_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG; + r_format.format_srgb = RD::DATA_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG; + } else { + //not supported, reconvert + r_format.format = RD::DATA_FORMAT_R8G8B8A8_UNORM; + r_format.format_srgb = RD::DATA_FORMAT_R8G8B8A8_SRGB; + image->decompress(); + image->convert(Image::FORMAT_RGBA8); + } + r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; + r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G; + r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B; + r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE; + } break; + case Image::FORMAT_PVRTC4A: { + //this is not properly supported by MoltekVK it seems, so best to use ETC2 + if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT)) { + r_format.format = RD::DATA_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG; + r_format.format_srgb = RD::DATA_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG; + } else { + //not supported, reconvert + r_format.format = RD::DATA_FORMAT_R8G8B8A8_UNORM; + r_format.format_srgb = RD::DATA_FORMAT_R8G8B8A8_SRGB; + image->decompress(); + image->convert(Image::FORMAT_RGBA8); + } + r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; + r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G; + r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B; + r_format.swizzle_a = RD::TEXTURE_SWIZZLE_A; + } break; + case Image::FORMAT_ETC2_R11: { + if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_EAC_R11_UNORM_BLOCK, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT)) { + r_format.format = RD::DATA_FORMAT_EAC_R11_UNORM_BLOCK; + } else { + //not supported, reconvert + r_format.format = RD::DATA_FORMAT_R8_UNORM; + image->decompress(); + image->convert(Image::FORMAT_R8); + } + r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; + r_format.swizzle_g = RD::TEXTURE_SWIZZLE_ZERO; + r_format.swizzle_b = RD::TEXTURE_SWIZZLE_ZERO; + r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE; + + } break; //etc2 + case Image::FORMAT_ETC2_R11S: { + + if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_EAC_R11_SNORM_BLOCK, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT)) { + r_format.format = RD::DATA_FORMAT_EAC_R11_SNORM_BLOCK; + } else { + //not supported, reconvert + r_format.format = RD::DATA_FORMAT_R8_SNORM; + image->decompress(); + image->convert(Image::FORMAT_R8); + } + r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; + r_format.swizzle_g = RD::TEXTURE_SWIZZLE_ZERO; + r_format.swizzle_b = RD::TEXTURE_SWIZZLE_ZERO; + r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE; + } break; //signed: {} break; NOT srgb. + case Image::FORMAT_ETC2_RG11: { + if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_EAC_R11G11_UNORM_BLOCK, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT)) { + r_format.format = RD::DATA_FORMAT_EAC_R11G11_UNORM_BLOCK; + } else { + //not supported, reconvert + r_format.format = RD::DATA_FORMAT_R8G8_UNORM; + image->decompress(); + image->convert(Image::FORMAT_RG8); + } + r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; + r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G; + r_format.swizzle_b = RD::TEXTURE_SWIZZLE_ZERO; + r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE; + } break; + case Image::FORMAT_ETC2_RG11S: { + if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_EAC_R11G11_SNORM_BLOCK, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT)) { + r_format.format = RD::DATA_FORMAT_EAC_R11G11_SNORM_BLOCK; + } else { + //not supported, reconvert + r_format.format = RD::DATA_FORMAT_R8G8_SNORM; + image->decompress(); + image->convert(Image::FORMAT_RG8); + } + r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; + r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G; + r_format.swizzle_b = RD::TEXTURE_SWIZZLE_ZERO; + r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE; + } break; + case Image::FORMAT_ETC: + case Image::FORMAT_ETC2_RGB8: { + //ETC2 is backwards compatible with ETC1, and all modern platforms support it + if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_ETC2_R8G8B8_UNORM_BLOCK, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT)) { + r_format.format = RD::DATA_FORMAT_ETC2_R8G8B8_UNORM_BLOCK; + r_format.format_srgb = RD::DATA_FORMAT_ETC2_R8G8B8_SRGB_BLOCK; + } else { + //not supported, reconvert + r_format.format = RD::DATA_FORMAT_R8G8B8A8_UNORM; + r_format.format_srgb = RD::DATA_FORMAT_R8G8B8A8_SRGB; + image->decompress(); + image->convert(Image::FORMAT_RGBA8); + } + r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; + r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G; + r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B; + r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE; + + } break; + case Image::FORMAT_ETC2_RGBA8: { + if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT)) { + r_format.format = RD::DATA_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK; + r_format.format_srgb = RD::DATA_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK; + } else { + //not supported, reconvert + r_format.format = RD::DATA_FORMAT_R8G8B8A8_UNORM; + r_format.format_srgb = RD::DATA_FORMAT_R8G8B8A8_SRGB; + image->decompress(); + image->convert(Image::FORMAT_RGBA8); + } + r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; + r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G; + r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B; + r_format.swizzle_a = RD::TEXTURE_SWIZZLE_A; + } break; + case Image::FORMAT_ETC2_RGB8A1: { + + if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT)) { + r_format.format = RD::DATA_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK; + r_format.format_srgb = RD::DATA_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK; + } else { + //not supported, reconvert + r_format.format = RD::DATA_FORMAT_R8G8B8A8_UNORM; + r_format.format_srgb = RD::DATA_FORMAT_R8G8B8A8_SRGB; + image->decompress(); + image->convert(Image::FORMAT_RGBA8); + } + r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; + r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G; + r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B; + r_format.swizzle_a = RD::TEXTURE_SWIZZLE_A; + } break; + case Image::FORMAT_ETC2_RA_AS_RG: { + + if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT)) { + r_format.format = RD::DATA_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK; + r_format.format_srgb = RD::DATA_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK; + } else { + //not supported, reconvert + r_format.format = RD::DATA_FORMAT_R8G8B8A8_UNORM; + r_format.format_srgb = RD::DATA_FORMAT_R8G8B8A8_SRGB; + image->decompress(); + image->convert(Image::FORMAT_RGBA8); + } + r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; + r_format.swizzle_g = RD::TEXTURE_SWIZZLE_A; + r_format.swizzle_b = RD::TEXTURE_SWIZZLE_ZERO; + r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE; + } break; + case Image::FORMAT_DXT5_RA_AS_RG: { + if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_BC3_UNORM_BLOCK, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT)) { + r_format.format = RD::DATA_FORMAT_BC3_UNORM_BLOCK; + r_format.format_srgb = RD::DATA_FORMAT_BC3_SRGB_BLOCK; + } else { + //not supported, reconvert + r_format.format = RD::DATA_FORMAT_R8G8B8A8_UNORM; + r_format.format_srgb = RD::DATA_FORMAT_R8G8B8A8_SRGB; + image->decompress(); + image->convert(Image::FORMAT_RGBA8); + } + r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; + r_format.swizzle_g = RD::TEXTURE_SWIZZLE_A; + r_format.swizzle_b = RD::TEXTURE_SWIZZLE_ZERO; + r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE; + } break; + + default: { + } + } + + return image; +} + +RID RasterizerStorageRD::texture_2d_create(const Ref<Image> &p_image) { + ERR_FAIL_COND_V(p_image.is_null(), RID()); + ERR_FAIL_COND_V(p_image->empty(), RID()); + + TextureToRDFormat ret_format; + Ref<Image> image = _validate_texture_format(p_image, ret_format); + + Texture texture; + + texture.type = Texture::TYPE_2D; + + texture.width = p_image->get_width(); + texture.height = p_image->get_height(); + texture.layers = 1; + texture.mipmaps = p_image->get_mipmap_count() + 1; + texture.depth = 1; + texture.format = p_image->get_format(); + texture.validated_format = image->get_format(); + + texture.rd_type = RD::TEXTURE_TYPE_2D; + texture.rd_format = ret_format.format; + texture.rd_format_srgb = ret_format.format_srgb; + + RD::TextureFormat rd_format; + RD::TextureView rd_view; + { //attempt register + rd_format.format = texture.rd_format; + rd_format.width = texture.width; + rd_format.height = texture.height; + rd_format.depth = 1; + rd_format.array_layers = 1; + rd_format.mipmaps = texture.mipmaps; + rd_format.type = texture.rd_type; + rd_format.samples = RD::TEXTURE_SAMPLES_1; + rd_format.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT; + if (texture.rd_format_srgb != RD::DATA_FORMAT_MAX) { + rd_format.shareable_formats.push_back(texture.rd_format); + rd_format.shareable_formats.push_back(texture.rd_format_srgb); + } + } + { + rd_view.swizzle_r = ret_format.swizzle_r; + rd_view.swizzle_g = ret_format.swizzle_g; + rd_view.swizzle_b = ret_format.swizzle_b; + rd_view.swizzle_a = ret_format.swizzle_a; + } + Vector<uint8_t> data = image->get_data(); //use image data + Vector<Vector<uint8_t>> data_slices; + data_slices.push_back(data); + texture.rd_texture = RD::get_singleton()->texture_create(rd_format, rd_view, data_slices); + ERR_FAIL_COND_V(texture.rd_texture.is_null(), RID()); + if (texture.rd_format_srgb != RD::DATA_FORMAT_MAX) { + rd_view.format_override = texture.rd_format_srgb; + texture.rd_texture_srgb = RD::get_singleton()->texture_create_shared(rd_view, texture.rd_texture); + if (texture.rd_texture_srgb.is_null()) { + RD::get_singleton()->free(texture.rd_texture); + ERR_FAIL_COND_V(texture.rd_texture_srgb.is_null(), RID()); + } + } + + //used for 2D, overridable + texture.width_2d = texture.width; + texture.height_2d = texture.height; + texture.is_render_target = false; + texture.rd_view = rd_view; + texture.is_proxy = false; + + return texture_owner.make_rid(texture); +} + +RID RasterizerStorageRD::texture_2d_layered_create(const Vector<Ref<Image>> &p_layers, RS::TextureLayeredType p_layered_type) { + + return RID(); +} +RID RasterizerStorageRD::texture_3d_create(const Vector<Ref<Image>> &p_slices) { + + return RID(); +} + +RID RasterizerStorageRD::texture_proxy_create(RID p_base) { + Texture *tex = texture_owner.getornull(p_base); + ERR_FAIL_COND_V(!tex, RID()); + Texture proxy_tex = *tex; + + proxy_tex.rd_view.format_override = tex->rd_format; + proxy_tex.rd_texture = RD::get_singleton()->texture_create_shared(proxy_tex.rd_view, tex->rd_texture); + if (proxy_tex.rd_texture_srgb.is_valid()) { + proxy_tex.rd_view.format_override = tex->rd_format_srgb; + proxy_tex.rd_texture_srgb = RD::get_singleton()->texture_create_shared(proxy_tex.rd_view, tex->rd_texture); + } + proxy_tex.proxy_to = p_base; + proxy_tex.is_render_target = false; + proxy_tex.is_proxy = true; + proxy_tex.proxies.clear(); + + RID rid = texture_owner.make_rid(proxy_tex); + + tex->proxies.push_back(rid); + + return rid; +} + +void RasterizerStorageRD::_texture_2d_update(RID p_texture, const Ref<Image> &p_image, int p_layer, bool p_immediate) { + + ERR_FAIL_COND(p_image.is_null() || p_image->empty()); + + Texture *tex = texture_owner.getornull(p_texture); + ERR_FAIL_COND(!tex); + ERR_FAIL_COND(tex->is_render_target); + ERR_FAIL_COND(p_image->get_width() != tex->width || p_image->get_height() != tex->height); + ERR_FAIL_COND(p_image->get_format() != tex->format); + + if (tex->type == Texture::TYPE_LAYERED) { + ERR_FAIL_INDEX(p_layer, tex->layers); + } + +#ifdef TOOLS_ENABLED + tex->image_cache_2d.unref(); +#endif + TextureToRDFormat f; + Ref<Image> validated = _validate_texture_format(p_image, f); + + RD::get_singleton()->texture_update(tex->rd_texture, p_layer, validated->get_data(), !p_immediate); +} + +void RasterizerStorageRD::texture_2d_update_immediate(RID p_texture, const Ref<Image> &p_image, int p_layer) { + _texture_2d_update(p_texture, p_image, p_layer, true); +} +void RasterizerStorageRD::texture_2d_update(RID p_texture, const Ref<Image> &p_image, int p_layer) { + _texture_2d_update(p_texture, p_image, p_layer, false); +} +void RasterizerStorageRD::texture_3d_update(RID p_texture, const Ref<Image> &p_image, int p_depth, int p_mipmap) { +} + +void RasterizerStorageRD::texture_proxy_update(RID p_texture, RID p_proxy_to) { + + Texture *tex = texture_owner.getornull(p_texture); + ERR_FAIL_COND(!tex); + ERR_FAIL_COND(!tex->is_proxy); + Texture *proxy_to = texture_owner.getornull(p_proxy_to); + ERR_FAIL_COND(!proxy_to); + ERR_FAIL_COND(proxy_to->is_proxy); + + if (tex->proxy_to.is_valid()) { + //unlink proxy + if (RD::get_singleton()->texture_is_valid(tex->rd_texture)) { + RD::get_singleton()->free(tex->rd_texture); + tex->rd_texture = RID(); + } + if (RD::get_singleton()->texture_is_valid(tex->rd_texture_srgb)) { + RD::get_singleton()->free(tex->rd_texture_srgb); + tex->rd_texture_srgb = RID(); + } + Texture *prev_tex = texture_owner.getornull(tex->proxy_to); + ERR_FAIL_COND(!prev_tex); + prev_tex->proxies.erase(p_texture); + } + + *tex = *proxy_to; + + tex->proxy_to = p_proxy_to; + tex->is_render_target = false; + tex->is_proxy = true; + tex->proxies.clear(); + proxy_to->proxies.push_back(p_texture); + + tex->rd_view.format_override = tex->rd_format; + tex->rd_texture = RD::get_singleton()->texture_create_shared(tex->rd_view, proxy_to->rd_texture); + if (tex->rd_texture_srgb.is_valid()) { + tex->rd_view.format_override = tex->rd_format_srgb; + tex->rd_texture_srgb = RD::get_singleton()->texture_create_shared(tex->rd_view, proxy_to->rd_texture); + } +} + +//these two APIs can be used together or in combination with the others. +RID RasterizerStorageRD::texture_2d_placeholder_create() { + + //this could be better optimized to reuse an existing image , done this way + //for now to get it working + Ref<Image> image; + image.instance(); + image->create(4, 4, false, Image::FORMAT_RGBA8); + + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + image->set_pixel(i, j, Color(1, 0, 1, 1)); + } + } + + return texture_2d_create(image); +} +RID RasterizerStorageRD::texture_2d_layered_placeholder_create() { + + return RID(); +} +RID RasterizerStorageRD::texture_3d_placeholder_create() { + + return RID(); +} + +Ref<Image> RasterizerStorageRD::texture_2d_get(RID p_texture) const { + + Texture *tex = texture_owner.getornull(p_texture); + ERR_FAIL_COND_V(!tex, Ref<Image>()); + +#ifdef TOOLS_ENABLED + if (tex->image_cache_2d.is_valid()) { + return tex->image_cache_2d; + } +#endif + Vector<uint8_t> data = RD::get_singleton()->texture_get_data(tex->rd_texture, 0); + ERR_FAIL_COND_V(data.size() == 0, Ref<Image>()); + Ref<Image> image; + image.instance(); + image->create(tex->width, tex->height, tex->mipmaps > 1, tex->validated_format, data); + ERR_FAIL_COND_V(image->empty(), Ref<Image>()); + if (tex->format != tex->validated_format) { + image->convert(tex->format); + } + +#ifdef TOOLS_ENABLED + if (Engine::get_singleton()->is_editor_hint()) { + tex->image_cache_2d = image; + } +#endif + + return image; +} +Ref<Image> RasterizerStorageRD::texture_2d_layer_get(RID p_texture, int p_layer) const { + + return Ref<Image>(); +} +Ref<Image> RasterizerStorageRD::texture_3d_slice_get(RID p_texture, int p_depth, int p_mipmap) const { + + return Ref<Image>(); +} + +void RasterizerStorageRD::texture_replace(RID p_texture, RID p_by_texture) { + + Texture *tex = texture_owner.getornull(p_texture); + ERR_FAIL_COND(!tex); + ERR_FAIL_COND(tex->proxy_to.is_valid()); //cant replace proxy + Texture *by_tex = texture_owner.getornull(p_by_texture); + ERR_FAIL_COND(!by_tex); + ERR_FAIL_COND(by_tex->proxy_to.is_valid()); //cant replace proxy + + if (tex == by_tex) { + return; + } + + if (tex->rd_texture_srgb.is_valid()) { + RD::get_singleton()->free(tex->rd_texture_srgb); + } + RD::get_singleton()->free(tex->rd_texture); + + Vector<RID> proxies_to_update = tex->proxies; + Vector<RID> proxies_to_redirect = by_tex->proxies; + + *tex = *by_tex; + + tex->proxies = proxies_to_update; //restore proxies, so they can be updated + + for (int i = 0; i < proxies_to_update.size(); i++) { + texture_proxy_update(proxies_to_update[i], p_texture); + } + for (int i = 0; i < proxies_to_redirect.size(); i++) { + texture_proxy_update(proxies_to_redirect[i], p_texture); + } + //delete last, so proxies can be updated + texture_owner.free(p_by_texture); +} +void RasterizerStorageRD::texture_set_size_override(RID p_texture, int p_width, int p_height) { + Texture *tex = texture_owner.getornull(p_texture); + ERR_FAIL_COND(!tex); + ERR_FAIL_COND(tex->type != Texture::TYPE_2D); + tex->width_2d = p_width; + tex->height_2d = p_height; +} + +void RasterizerStorageRD::texture_set_path(RID p_texture, const String &p_path) { + Texture *tex = texture_owner.getornull(p_texture); + ERR_FAIL_COND(!tex); + tex->path = p_path; +} +String RasterizerStorageRD::texture_get_path(RID p_texture) const { + return String(); +} + +void RasterizerStorageRD::texture_set_detect_3d_callback(RID p_texture, RS::TextureDetectCallback p_callback, void *p_userdata) { + Texture *tex = texture_owner.getornull(p_texture); + ERR_FAIL_COND(!tex); + tex->detect_3d_callback_ud = p_userdata; + tex->detect_3d_callback = p_callback; +} +void RasterizerStorageRD::texture_set_detect_normal_callback(RID p_texture, RS::TextureDetectCallback p_callback, void *p_userdata) { + Texture *tex = texture_owner.getornull(p_texture); + ERR_FAIL_COND(!tex); + tex->detect_normal_callback_ud = p_userdata; + tex->detect_normal_callback = p_callback; +} +void RasterizerStorageRD::texture_set_detect_roughness_callback(RID p_texture, RS::TextureDetectRoughnessCallback p_callback, void *p_userdata) { + Texture *tex = texture_owner.getornull(p_texture); + ERR_FAIL_COND(!tex); + tex->detect_roughness_callback_ud = p_userdata; + tex->detect_roughness_callback = p_callback; +} +void RasterizerStorageRD::texture_debug_usage(List<RS::TextureInfo> *r_info) { +} + +void RasterizerStorageRD::texture_set_proxy(RID p_proxy, RID p_base) { +} +void RasterizerStorageRD::texture_set_force_redraw_if_visible(RID p_texture, bool p_enable) { +} + +Size2 RasterizerStorageRD::texture_size_with_proxy(RID p_proxy) { + return texture_2d_get_size(p_proxy); +} + +/* SHADER API */ + +RID RasterizerStorageRD::shader_create() { + + Shader shader; + shader.data = NULL; + shader.type = SHADER_TYPE_MAX; + + return shader_owner.make_rid(shader); +} + +void RasterizerStorageRD::shader_set_code(RID p_shader, const String &p_code) { + Shader *shader = shader_owner.getornull(p_shader); + ERR_FAIL_COND(!shader); + + shader->code = p_code; + String mode_string = ShaderLanguage::get_shader_type(p_code); + + ShaderType new_type; + if (mode_string == "canvas_item") + new_type = SHADER_TYPE_2D; + else if (mode_string == "particles") + new_type = SHADER_TYPE_PARTICLES; + else if (mode_string == "spatial") + new_type = SHADER_TYPE_3D; + else if (mode_string == "sky") + new_type = SHADER_TYPE_SKY; + else + new_type = SHADER_TYPE_MAX; + + if (new_type != shader->type) { + if (shader->data) { + memdelete(shader->data); + shader->data = NULL; + } + + for (Set<Material *>::Element *E = shader->owners.front(); E; E = E->next()) { + + Material *material = E->get(); + material->shader_type = new_type; + if (material->data) { + memdelete(material->data); + material->data = NULL; + } + } + + shader->type = new_type; + + if (new_type < SHADER_TYPE_MAX && shader_data_request_func[new_type]) { + shader->data = shader_data_request_func[new_type](); + } else { + shader->type = SHADER_TYPE_MAX; //invalid + } + + for (Set<Material *>::Element *E = shader->owners.front(); E; E = E->next()) { + Material *material = E->get(); + if (shader->data) { + material->data = material_data_request_func[new_type](shader->data); + material->data->set_next_pass(material->next_pass); + material->data->set_render_priority(material->priority); + } + material->shader_type = new_type; + } + } + + if (shader->data) { + shader->data->set_code(p_code); + } + + for (Set<Material *>::Element *E = shader->owners.front(); E; E = E->next()) { + Material *material = E->get(); + material->instance_dependency.instance_notify_changed(false, true); + _material_queue_update(material, true, true); + } +} + +String RasterizerStorageRD::shader_get_code(RID p_shader) const { + Shader *shader = shader_owner.getornull(p_shader); + ERR_FAIL_COND_V(!shader, String()); + return shader->code; +} +void RasterizerStorageRD::shader_get_param_list(RID p_shader, List<PropertyInfo> *p_param_list) const { + + Shader *shader = shader_owner.getornull(p_shader); + ERR_FAIL_COND(!shader); + if (shader->data) { + return shader->data->get_param_list(p_param_list); + } +} + +void RasterizerStorageRD::shader_set_default_texture_param(RID p_shader, const StringName &p_name, RID p_texture) { + + Shader *shader = shader_owner.getornull(p_shader); + ERR_FAIL_COND(!shader); + + if (p_texture.is_valid() && texture_owner.owns(p_texture)) { + shader->default_texture_parameter[p_name] = p_texture; + } else { + shader->default_texture_parameter.erase(p_name); + } + + for (Set<Material *>::Element *E = shader->owners.front(); E; E = E->next()) { + Material *material = E->get(); + _material_queue_update(material, false, true); + } +} + +RID RasterizerStorageRD::shader_get_default_texture_param(RID p_shader, const StringName &p_name) const { + Shader *shader = shader_owner.getornull(p_shader); + ERR_FAIL_COND_V(!shader, RID()); + if (shader->default_texture_parameter.has(p_name)) { + return shader->default_texture_parameter[p_name]; + } + + return RID(); +} +Variant RasterizerStorageRD::shader_get_param_default(RID p_shader, const StringName &p_param) const { + Shader *shader = shader_owner.getornull(p_shader); + ERR_FAIL_COND_V(!shader, Variant()); + if (shader->data) { + return shader->data->get_default_parameter(p_param); + } + return Variant(); +} +void RasterizerStorageRD::shader_set_data_request_function(ShaderType p_shader_type, ShaderDataRequestFunction p_function) { + ERR_FAIL_INDEX(p_shader_type, SHADER_TYPE_MAX); + shader_data_request_func[p_shader_type] = p_function; +} + +/* COMMON MATERIAL API */ + +RID RasterizerStorageRD::material_create() { + + Material material; + material.data = NULL; + material.shader = NULL; + material.shader_type = SHADER_TYPE_MAX; + material.update_next = NULL; + material.update_requested = false; + material.uniform_dirty = false; + material.texture_dirty = false; + material.priority = 0; + RID id = material_owner.make_rid(material); + { + Material *material_ptr = material_owner.getornull(id); + material_ptr->self = id; + } + return id; +} + +void RasterizerStorageRD::_material_queue_update(Material *material, bool p_uniform, bool p_texture) { + if (material->update_requested) { + return; + } + + material->update_next = material_update_list; + material_update_list = material; + material->update_requested = true; + material->uniform_dirty = p_uniform; + material->texture_dirty = p_texture; +} + +void RasterizerStorageRD::material_set_shader(RID p_material, RID p_shader) { + + Material *material = material_owner.getornull(p_material); + ERR_FAIL_COND(!material); + + if (material->data) { + memdelete(material->data); + material->data = NULL; + } + + if (material->shader) { + material->shader->owners.erase(material); + material->shader = NULL; + material->shader_type = SHADER_TYPE_MAX; + } + + if (p_shader.is_null()) { + material->instance_dependency.instance_notify_changed(false, true); + return; + } + + Shader *shader = shader_owner.getornull(p_shader); + ERR_FAIL_COND(!shader); + material->shader = shader; + material->shader_type = shader->type; + shader->owners.insert(material); + + if (shader->type == SHADER_TYPE_MAX) { + return; + } + + ERR_FAIL_COND(shader->data == NULL); + + material->data = material_data_request_func[shader->type](shader->data); + material->data->set_next_pass(material->next_pass); + material->data->set_render_priority(material->priority); + //updating happens later + material->instance_dependency.instance_notify_changed(false, true); + _material_queue_update(material, true, true); +} + +void RasterizerStorageRD::material_set_param(RID p_material, const StringName &p_param, const Variant &p_value) { + + Material *material = material_owner.getornull(p_material); + ERR_FAIL_COND(!material); + + if (p_value.get_type() == Variant::NIL) { + material->params.erase(p_param); + } else { + material->params[p_param] = p_value; + } + + if (material->shader && material->shader->data) { //shader is valid + bool is_texture = material->shader->data->is_param_texture(p_param); + _material_queue_update(material, !is_texture, is_texture); + } else { + _material_queue_update(material, true, true); + } +} + +Variant RasterizerStorageRD::material_get_param(RID p_material, const StringName &p_param) const { + Material *material = material_owner.getornull(p_material); + ERR_FAIL_COND_V(!material, Variant()); + if (material->params.has(p_param)) { + return material->params[p_param]; + } else { + return Variant(); + } +} + +void RasterizerStorageRD::material_set_next_pass(RID p_material, RID p_next_material) { + Material *material = material_owner.getornull(p_material); + ERR_FAIL_COND(!material); + + if (material->next_pass == p_next_material) { + return; + } + + material->next_pass = p_next_material; + if (material->data) { + material->data->set_next_pass(p_next_material); + } + + material->instance_dependency.instance_notify_changed(false, true); +} +void RasterizerStorageRD::material_set_render_priority(RID p_material, int priority) { + Material *material = material_owner.getornull(p_material); + ERR_FAIL_COND(!material); + material->priority = priority; + if (material->data) { + material->data->set_render_priority(priority); + } +} + +bool RasterizerStorageRD::material_is_animated(RID p_material) { + Material *material = material_owner.getornull(p_material); + ERR_FAIL_COND_V(!material, false); + if (material->shader && material->shader->data) { + if (material->shader->data->is_animated()) { + return true; + } else if (material->next_pass.is_valid()) { + return material_is_animated(material->next_pass); + } + } + return false; //by default nothing is animated +} +bool RasterizerStorageRD::material_casts_shadows(RID p_material) { + Material *material = material_owner.getornull(p_material); + ERR_FAIL_COND_V(!material, true); + if (material->shader && material->shader->data) { + if (material->shader->data->casts_shadows()) { + return true; + } else if (material->next_pass.is_valid()) { + return material_casts_shadows(material->next_pass); + } + } + return true; //by default everything casts shadows +} + +void RasterizerStorageRD::material_update_dependency(RID p_material, RasterizerScene::InstanceBase *p_instance) { + Material *material = material_owner.getornull(p_material); + ERR_FAIL_COND(!material); + p_instance->update_dependency(&material->instance_dependency); + if (material->next_pass.is_valid()) { + material_update_dependency(material->next_pass, p_instance); + } +} + +void RasterizerStorageRD::material_set_data_request_function(ShaderType p_shader_type, MaterialDataRequestFunction p_function) { + ERR_FAIL_INDEX(p_shader_type, SHADER_TYPE_MAX); + material_data_request_func[p_shader_type] = p_function; +} + +_FORCE_INLINE_ static void _fill_std140_variant_ubo_value(ShaderLanguage::DataType type, const Variant &value, uint8_t *data, bool p_linear_color) { + switch (type) { + case ShaderLanguage::TYPE_BOOL: { + + bool v = value; + + uint32_t *gui = (uint32_t *)data; + *gui = v ? 1 : 0; + } break; + case ShaderLanguage::TYPE_BVEC2: { + + int v = value; + uint32_t *gui = (uint32_t *)data; + gui[0] = v & 1 ? 1 : 0; + gui[1] = v & 2 ? 1 : 0; + + } break; + case ShaderLanguage::TYPE_BVEC3: { + + int v = value; + uint32_t *gui = (uint32_t *)data; + gui[0] = (v & 1) ? 1 : 0; + gui[1] = (v & 2) ? 1 : 0; + gui[2] = (v & 4) ? 1 : 0; + + } break; + case ShaderLanguage::TYPE_BVEC4: { + + int v = value; + uint32_t *gui = (uint32_t *)data; + gui[0] = (v & 1) ? 1 : 0; + gui[1] = (v & 2) ? 1 : 0; + gui[2] = (v & 4) ? 1 : 0; + gui[3] = (v & 8) ? 1 : 0; + + } break; + case ShaderLanguage::TYPE_INT: { + + int v = value; + int32_t *gui = (int32_t *)data; + gui[0] = v; + + } break; + case ShaderLanguage::TYPE_IVEC2: { + + Vector<int> iv = value; + int s = iv.size(); + int32_t *gui = (int32_t *)data; + + const int *r = iv.ptr(); + + for (int i = 0; i < 2; i++) { + if (i < s) + gui[i] = r[i]; + else + gui[i] = 0; + } + + } break; + case ShaderLanguage::TYPE_IVEC3: { + + Vector<int> iv = value; + int s = iv.size(); + int32_t *gui = (int32_t *)data; + + const int *r = iv.ptr(); + + for (int i = 0; i < 3; i++) { + if (i < s) + gui[i] = r[i]; + else + gui[i] = 0; + } + } break; + case ShaderLanguage::TYPE_IVEC4: { + + Vector<int> iv = value; + int s = iv.size(); + int32_t *gui = (int32_t *)data; + + const int *r = iv.ptr(); + + for (int i = 0; i < 4; i++) { + if (i < s) + gui[i] = r[i]; + else + gui[i] = 0; + } + } break; + case ShaderLanguage::TYPE_UINT: { + + int v = value; + uint32_t *gui = (uint32_t *)data; + gui[0] = v; + + } break; + case ShaderLanguage::TYPE_UVEC2: { + + Vector<int> iv = value; + int s = iv.size(); + uint32_t *gui = (uint32_t *)data; + + const int *r = iv.ptr(); + + for (int i = 0; i < 2; i++) { + if (i < s) + gui[i] = r[i]; + else + gui[i] = 0; + } + } break; + case ShaderLanguage::TYPE_UVEC3: { + Vector<int> iv = value; + int s = iv.size(); + uint32_t *gui = (uint32_t *)data; + + const int *r = iv.ptr(); + + for (int i = 0; i < 3; i++) { + if (i < s) + gui[i] = r[i]; + else + gui[i] = 0; + } + + } break; + case ShaderLanguage::TYPE_UVEC4: { + Vector<int> iv = value; + int s = iv.size(); + uint32_t *gui = (uint32_t *)data; + + const int *r = iv.ptr(); + + for (int i = 0; i < 4; i++) { + if (i < s) + gui[i] = r[i]; + else + gui[i] = 0; + } + } break; + case ShaderLanguage::TYPE_FLOAT: { + float v = value; + float *gui = (float *)data; + gui[0] = v; + + } break; + case ShaderLanguage::TYPE_VEC2: { + Vector2 v = value; + float *gui = (float *)data; + gui[0] = v.x; + gui[1] = v.y; + + } break; + case ShaderLanguage::TYPE_VEC3: { + Vector3 v = value; + float *gui = (float *)data; + gui[0] = v.x; + gui[1] = v.y; + gui[2] = v.z; + + } break; + case ShaderLanguage::TYPE_VEC4: { + + float *gui = (float *)data; + + if (value.get_type() == Variant::COLOR) { + Color v = value; + + if (p_linear_color) { + v = v.to_linear(); + } + + gui[0] = v.r; + gui[1] = v.g; + gui[2] = v.b; + gui[3] = v.a; + } else if (value.get_type() == Variant::RECT2) { + Rect2 v = value; + + gui[0] = v.position.x; + gui[1] = v.position.y; + gui[2] = v.size.x; + gui[3] = v.size.y; + } else if (value.get_type() == Variant::QUAT) { + Quat v = value; + + gui[0] = v.x; + gui[1] = v.y; + gui[2] = v.z; + gui[3] = v.w; + } else { + Plane v = value; + + gui[0] = v.normal.x; + gui[1] = v.normal.y; + gui[2] = v.normal.z; + gui[3] = v.d; + } + } break; + case ShaderLanguage::TYPE_MAT2: { + Transform2D v = value; + float *gui = (float *)data; + + //in std140 members of mat2 are treated as vec4s + gui[0] = v.elements[0][0]; + gui[1] = v.elements[0][1]; + gui[2] = 0; + gui[3] = 0; + gui[4] = v.elements[1][0]; + gui[5] = v.elements[1][1]; + gui[6] = 0; + gui[7] = 0; + } break; + case ShaderLanguage::TYPE_MAT3: { + + Basis v = value; + float *gui = (float *)data; + + gui[0] = v.elements[0][0]; + gui[1] = v.elements[1][0]; + gui[2] = v.elements[2][0]; + gui[3] = 0; + gui[4] = v.elements[0][1]; + gui[5] = v.elements[1][1]; + gui[6] = v.elements[2][1]; + gui[7] = 0; + gui[8] = v.elements[0][2]; + gui[9] = v.elements[1][2]; + gui[10] = v.elements[2][2]; + gui[11] = 0; + } break; + case ShaderLanguage::TYPE_MAT4: { + + Transform v = value; + float *gui = (float *)data; + + gui[0] = v.basis.elements[0][0]; + gui[1] = v.basis.elements[1][0]; + gui[2] = v.basis.elements[2][0]; + gui[3] = 0; + gui[4] = v.basis.elements[0][1]; + gui[5] = v.basis.elements[1][1]; + gui[6] = v.basis.elements[2][1]; + gui[7] = 0; + gui[8] = v.basis.elements[0][2]; + gui[9] = v.basis.elements[1][2]; + gui[10] = v.basis.elements[2][2]; + gui[11] = 0; + gui[12] = v.origin.x; + gui[13] = v.origin.y; + gui[14] = v.origin.z; + gui[15] = 1; + } break; + default: { + } + } +} + +_FORCE_INLINE_ static void _fill_std140_ubo_value(ShaderLanguage::DataType type, const Vector<ShaderLanguage::ConstantNode::Value> &value, uint8_t *data) { + + switch (type) { + case ShaderLanguage::TYPE_BOOL: { + + uint32_t *gui = (uint32_t *)data; + *gui = value[0].boolean ? 1 : 0; + } break; + case ShaderLanguage::TYPE_BVEC2: { + + uint32_t *gui = (uint32_t *)data; + gui[0] = value[0].boolean ? 1 : 0; + gui[1] = value[1].boolean ? 1 : 0; + + } break; + case ShaderLanguage::TYPE_BVEC3: { + + uint32_t *gui = (uint32_t *)data; + gui[0] = value[0].boolean ? 1 : 0; + gui[1] = value[1].boolean ? 1 : 0; + gui[2] = value[2].boolean ? 1 : 0; + + } break; + case ShaderLanguage::TYPE_BVEC4: { + + uint32_t *gui = (uint32_t *)data; + gui[0] = value[0].boolean ? 1 : 0; + gui[1] = value[1].boolean ? 1 : 0; + gui[2] = value[2].boolean ? 1 : 0; + gui[3] = value[3].boolean ? 1 : 0; + + } break; + case ShaderLanguage::TYPE_INT: { + + int32_t *gui = (int32_t *)data; + gui[0] = value[0].sint; + + } break; + case ShaderLanguage::TYPE_IVEC2: { + + int32_t *gui = (int32_t *)data; + + for (int i = 0; i < 2; i++) { + gui[i] = value[i].sint; + } + + } break; + case ShaderLanguage::TYPE_IVEC3: { + + int32_t *gui = (int32_t *)data; + + for (int i = 0; i < 3; i++) { + gui[i] = value[i].sint; + } + + } break; + case ShaderLanguage::TYPE_IVEC4: { + + int32_t *gui = (int32_t *)data; + + for (int i = 0; i < 4; i++) { + gui[i] = value[i].sint; + } + + } break; + case ShaderLanguage::TYPE_UINT: { + + uint32_t *gui = (uint32_t *)data; + gui[0] = value[0].uint; + + } break; + case ShaderLanguage::TYPE_UVEC2: { + + int32_t *gui = (int32_t *)data; + + for (int i = 0; i < 2; i++) { + gui[i] = value[i].uint; + } + } break; + case ShaderLanguage::TYPE_UVEC3: { + int32_t *gui = (int32_t *)data; + + for (int i = 0; i < 3; i++) { + gui[i] = value[i].uint; + } + + } break; + case ShaderLanguage::TYPE_UVEC4: { + int32_t *gui = (int32_t *)data; + + for (int i = 0; i < 4; i++) { + gui[i] = value[i].uint; + } + } break; + case ShaderLanguage::TYPE_FLOAT: { + + float *gui = (float *)data; + gui[0] = value[0].real; + + } break; + case ShaderLanguage::TYPE_VEC2: { + + float *gui = (float *)data; + + for (int i = 0; i < 2; i++) { + gui[i] = value[i].real; + } + + } break; + case ShaderLanguage::TYPE_VEC3: { + + float *gui = (float *)data; + + for (int i = 0; i < 3; i++) { + gui[i] = value[i].real; + } + + } break; + case ShaderLanguage::TYPE_VEC4: { + + float *gui = (float *)data; + + for (int i = 0; i < 4; i++) { + gui[i] = value[i].real; + } + } break; + case ShaderLanguage::TYPE_MAT2: { + float *gui = (float *)data; + + //in std140 members of mat2 are treated as vec4s + gui[0] = value[0].real; + gui[1] = value[1].real; + gui[2] = 0; + gui[3] = 0; + gui[4] = value[2].real; + gui[5] = value[3].real; + gui[6] = 0; + gui[7] = 0; + } break; + case ShaderLanguage::TYPE_MAT3: { + + float *gui = (float *)data; + + gui[0] = value[0].real; + gui[1] = value[1].real; + gui[2] = value[2].real; + gui[3] = 0; + gui[4] = value[3].real; + gui[5] = value[4].real; + gui[6] = value[5].real; + gui[7] = 0; + gui[8] = value[6].real; + gui[9] = value[7].real; + gui[10] = value[8].real; + gui[11] = 0; + } break; + case ShaderLanguage::TYPE_MAT4: { + + float *gui = (float *)data; + + for (int i = 0; i < 16; i++) { + gui[i] = value[i].real; + } + } break; + default: { + } + } +} + +_FORCE_INLINE_ static void _fill_std140_ubo_empty(ShaderLanguage::DataType type, uint8_t *data) { + + switch (type) { + + case ShaderLanguage::TYPE_BOOL: + case ShaderLanguage::TYPE_INT: + case ShaderLanguage::TYPE_UINT: + case ShaderLanguage::TYPE_FLOAT: { + zeromem(data, 4); + } break; + case ShaderLanguage::TYPE_BVEC2: + case ShaderLanguage::TYPE_IVEC2: + case ShaderLanguage::TYPE_UVEC2: + case ShaderLanguage::TYPE_VEC2: { + zeromem(data, 8); + } break; + case ShaderLanguage::TYPE_BVEC3: + case ShaderLanguage::TYPE_IVEC3: + case ShaderLanguage::TYPE_UVEC3: + case ShaderLanguage::TYPE_VEC3: + case ShaderLanguage::TYPE_BVEC4: + case ShaderLanguage::TYPE_IVEC4: + case ShaderLanguage::TYPE_UVEC4: + case ShaderLanguage::TYPE_VEC4: { + + zeromem(data, 16); + } break; + case ShaderLanguage::TYPE_MAT2: { + + zeromem(data, 32); + } break; + case ShaderLanguage::TYPE_MAT3: { + + zeromem(data, 48); + } break; + case ShaderLanguage::TYPE_MAT4: { + zeromem(data, 64); + } break; + + default: { + } + } +} + +void RasterizerStorageRD::MaterialData::update_uniform_buffer(const Map<StringName, ShaderLanguage::ShaderNode::Uniform> &p_uniforms, const uint32_t *p_uniform_offsets, const Map<StringName, Variant> &p_parameters, uint8_t *p_buffer, uint32_t p_buffer_size, bool p_use_linear_color) { + + for (Map<StringName, ShaderLanguage::ShaderNode::Uniform>::Element *E = p_uniforms.front(); E; E = E->next()) { + + if (E->get().order < 0) + continue; // texture, does not go here + + //regular uniform + uint32_t offset = p_uniform_offsets[E->get().order]; +#ifdef DEBUG_ENABLED + uint32_t size = ShaderLanguage::get_type_size(E->get().type); + ERR_CONTINUE(offset + size > p_buffer_size); +#endif + uint8_t *data = &p_buffer[offset]; + const Map<StringName, Variant>::Element *V = p_parameters.find(E->key()); + + if (V) { + //user provided + _fill_std140_variant_ubo_value(E->get().type, V->get(), data, p_use_linear_color); + + } else if (E->get().default_value.size()) { + //default value + _fill_std140_ubo_value(E->get().type, E->get().default_value, data); + //value=E->get().default_value; + } else { + //zero because it was not provided + if (E->get().type == ShaderLanguage::TYPE_VEC4 && E->get().hint == ShaderLanguage::ShaderNode::Uniform::HINT_COLOR) { + //colors must be set as black, with alpha as 1.0 + _fill_std140_variant_ubo_value(E->get().type, Color(0, 0, 0, 1), data, p_use_linear_color); + } else { + //else just zero it out + _fill_std140_ubo_empty(E->get().type, data); + } + } + } +} + +void RasterizerStorageRD::MaterialData::update_textures(const Map<StringName, Variant> &p_parameters, const Map<StringName, RID> &p_default_textures, const Vector<ShaderCompilerRD::GeneratedCode::Texture> &p_texture_uniforms, RID *p_textures, bool p_use_linear_color) { + + RasterizerStorageRD *singleton = (RasterizerStorageRD *)RasterizerStorage::base_singleton; +#ifdef TOOLS_ENABLED + Texture *roughness_detect_texture = nullptr; + RS::TextureDetectRoughnessChannel roughness_channel; + Texture *normal_detect_texture = nullptr; +#endif + + for (int i = 0; i < p_texture_uniforms.size(); i++) { + + const StringName &uniform_name = p_texture_uniforms[i].name; + + RID texture; + + const Map<StringName, Variant>::Element *V = p_parameters.find(uniform_name); + if (V) { + texture = V->get(); + } + + if (!texture.is_valid()) { + const Map<StringName, RID>::Element *W = p_default_textures.find(uniform_name); + if (W) { + + texture = W->get(); + } + } + + RID rd_texture; + + if (texture.is_null()) { + //check default usage + switch (p_texture_uniforms[i].hint) { + case ShaderLanguage::ShaderNode::Uniform::HINT_BLACK: + case ShaderLanguage::ShaderNode::Uniform::HINT_BLACK_ALBEDO: { + rd_texture = singleton->texture_rd_get_default(DEFAULT_RD_TEXTURE_BLACK); + } break; + case ShaderLanguage::ShaderNode::Uniform::HINT_NONE: { + rd_texture = singleton->texture_rd_get_default(DEFAULT_RD_TEXTURE_NORMAL); + } break; + case ShaderLanguage::ShaderNode::Uniform::HINT_ANISO: { + rd_texture = singleton->texture_rd_get_default(DEFAULT_RD_TEXTURE_ANISO); + } break; + default: { + rd_texture = singleton->texture_rd_get_default(DEFAULT_RD_TEXTURE_WHITE); + } break; + } + } else { + bool srgb = p_use_linear_color && (p_texture_uniforms[i].hint == ShaderLanguage::ShaderNode::Uniform::HINT_ALBEDO || p_texture_uniforms[i].hint == ShaderLanguage::ShaderNode::Uniform::HINT_BLACK_ALBEDO); + + Texture *tex = singleton->texture_owner.getornull(texture); + + if (tex) { + + rd_texture = (srgb && tex->rd_texture_srgb.is_valid()) ? tex->rd_texture_srgb : tex->rd_texture; +#ifdef TOOLS_ENABLED + if (tex->detect_3d_callback && p_use_linear_color) { + tex->detect_3d_callback(tex->detect_3d_callback_ud); + } + if (tex->detect_normal_callback && (p_texture_uniforms[i].hint == ShaderLanguage::ShaderNode::Uniform::HINT_NORMAL || p_texture_uniforms[i].hint == ShaderLanguage::ShaderNode::Uniform::HINT_ROUGHNESS_NORMAL)) { + if (p_texture_uniforms[i].hint == ShaderLanguage::ShaderNode::Uniform::HINT_ROUGHNESS_NORMAL) { + normal_detect_texture = tex; + } + tex->detect_normal_callback(tex->detect_normal_callback_ud); + } + if (tex->detect_roughness_callback && (p_texture_uniforms[i].hint >= ShaderLanguage::ShaderNode::Uniform::HINT_ROUGHNESS_R || p_texture_uniforms[i].hint <= ShaderLanguage::ShaderNode::Uniform::HINT_ROUGHNESS_GRAY)) { + //find the normal texture + roughness_detect_texture = tex; + roughness_channel = RS::TextureDetectRoughnessChannel(p_texture_uniforms[i].hint - ShaderLanguage::ShaderNode::Uniform::HINT_ROUGHNESS_R); + } + +#endif + } + + if (rd_texture.is_null()) { + //wtf + rd_texture = singleton->texture_rd_get_default(DEFAULT_RD_TEXTURE_WHITE); + } + } + + p_textures[i] = rd_texture; + } +#ifdef TOOLS_ENABLED + if (roughness_detect_texture && normal_detect_texture && normal_detect_texture->path != String()) { + roughness_detect_texture->detect_roughness_callback(roughness_detect_texture->detect_roughness_callback_ud, normal_detect_texture->path, roughness_channel); + } +#endif +} + +void RasterizerStorageRD::material_force_update_textures(RID p_material, ShaderType p_shader_type) { + Material *material = material_owner.getornull(p_material); + if (material->shader_type != p_shader_type) { + return; + } + if (material->data) { + material->data->update_parameters(material->params, false, true); + } +} + +void RasterizerStorageRD::_update_queued_materials() { + Material *material = material_update_list; + while (material) { + Material *next = material->update_next; + + if (material->data) { + material->data->update_parameters(material->params, material->uniform_dirty, material->texture_dirty); + } + material->update_requested = false; + material->texture_dirty = false; + material->uniform_dirty = false; + material->update_next = NULL; + material = next; + } + material_update_list = NULL; +} +/* MESH API */ + +RID RasterizerStorageRD::mesh_create() { + + return mesh_owner.make_rid(Mesh()); +} + +/// Returns stride +void RasterizerStorageRD::mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface) { + + Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND(!mesh); + + //ensure blend shape consistency + ERR_FAIL_COND(mesh->blend_shape_count && p_surface.blend_shapes.size() != (int)mesh->blend_shape_count); + ERR_FAIL_COND(mesh->blend_shape_count && p_surface.bone_aabbs.size() != mesh->bone_aabbs.size()); + +#ifdef DEBUG_ENABLED + //do a validation, to catch errors first + { + + uint32_t stride = 0; + + for (int i = 0; i < RS::ARRAY_WEIGHTS; i++) { + + if ((p_surface.format & (1 << i))) { + + switch (i) { + + case RS::ARRAY_VERTEX: { + + if (p_surface.format & RS::ARRAY_FLAG_USE_2D_VERTICES) { + stride += sizeof(float) * 2; + } else { + stride += sizeof(float) * 3; + } + + } break; + case RS::ARRAY_NORMAL: { + + if (p_surface.format & RS::ARRAY_COMPRESS_NORMAL) { + stride += sizeof(int8_t) * 4; + } else { + stride += sizeof(float) * 4; + } + + } break; + case RS::ARRAY_TANGENT: { + + if (p_surface.format & RS::ARRAY_COMPRESS_TANGENT) { + stride += sizeof(int8_t) * 4; + } else { + stride += sizeof(float) * 4; + } + + } break; + case RS::ARRAY_COLOR: { + + if (p_surface.format & RS::ARRAY_COMPRESS_COLOR) { + stride += sizeof(int8_t) * 4; + } else { + stride += sizeof(float) * 4; + } + + } break; + case RS::ARRAY_TEX_UV: { + + if (p_surface.format & RS::ARRAY_COMPRESS_TEX_UV) { + stride += sizeof(int16_t) * 2; + } else { + stride += sizeof(float) * 2; + } + + } break; + case RS::ARRAY_TEX_UV2: { + + if (p_surface.format & RS::ARRAY_COMPRESS_TEX_UV2) { + stride += sizeof(int16_t) * 2; + } else { + stride += sizeof(float) * 2; + } + + } break; + case RS::ARRAY_BONES: { + //assumed weights too + + //unique format, internally 16 bits, exposed as single array for 32 + + stride += sizeof(int32_t) * 4; + + } break; + } + } + } + + int expected_size = stride * p_surface.vertex_count; + ERR_FAIL_COND_MSG(expected_size != p_surface.vertex_data.size(), "Size of data provided (" + itos(p_surface.vertex_data.size()) + ") does not match expected (" + itos(expected_size) + ")"); + } + +#endif + + Mesh::Surface *s = memnew(Mesh::Surface); + + s->format = p_surface.format; + s->primitive = p_surface.primitive; + + s->vertex_buffer = RD::get_singleton()->vertex_buffer_create(p_surface.vertex_data.size(), p_surface.vertex_data); + s->vertex_count = p_surface.vertex_count; + + if (p_surface.index_count) { + bool is_index_16 = p_surface.vertex_count <= 65536; + + s->index_buffer = RD::get_singleton()->index_buffer_create(p_surface.index_count, is_index_16 ? RD::INDEX_BUFFER_FORMAT_UINT16 : RD::INDEX_BUFFER_FORMAT_UINT32, p_surface.index_data, false); + s->index_count = p_surface.index_count; + s->index_array = RD::get_singleton()->index_array_create(s->index_buffer, 0, s->index_count); + if (p_surface.lods.size()) { + s->lods = memnew_arr(Mesh::Surface::LOD, p_surface.lods.size()); + s->lod_count = p_surface.lods.size(); + + for (int i = 0; i < p_surface.lods.size(); i++) { + + uint32_t indices = p_surface.lods[i].index_data.size() / (is_index_16 ? 2 : 4); + s->lods[i].index_buffer = RD::get_singleton()->index_buffer_create(indices, is_index_16 ? RD::INDEX_BUFFER_FORMAT_UINT16 : RD::INDEX_BUFFER_FORMAT_UINT32, p_surface.lods[i].index_data); + s->lods[i].index_array = RD::get_singleton()->index_array_create(s->lods[i].index_buffer, 0, indices); + s->lods[i].edge_length = p_surface.lods[i].edge_length; + } + } + } + + s->aabb = p_surface.aabb; + s->bone_aabbs = p_surface.bone_aabbs; //only really useful for returning them. + + for (int i = 0; i < p_surface.blend_shapes.size(); i++) { + + if (p_surface.blend_shapes[i].size() != p_surface.vertex_data.size()) { + memdelete(s); + ERR_FAIL_COND(p_surface.blend_shapes[i].size() != p_surface.vertex_data.size()); + } + RID vertex_buffer = RD::get_singleton()->vertex_buffer_create(p_surface.blend_shapes[i].size(), p_surface.blend_shapes[i]); + s->blend_shapes.push_back(vertex_buffer); + } + + mesh->blend_shape_count = p_surface.blend_shapes.size(); + + if (mesh->surface_count == 0) { + mesh->bone_aabbs = p_surface.bone_aabbs; + mesh->aabb = p_surface.aabb; + } else { + for (int i = 0; i < p_surface.bone_aabbs.size(); i++) { + mesh->bone_aabbs.write[i].merge_with(p_surface.bone_aabbs[i]); + } + mesh->aabb.merge_with(p_surface.aabb); + } + + s->material = p_surface.material; + + mesh->surfaces = (Mesh::Surface **)memrealloc(mesh->surfaces, sizeof(Mesh::Surface *) * (mesh->surface_count + 1)); + mesh->surfaces[mesh->surface_count] = s; + mesh->surface_count++; + + mesh->instance_dependency.instance_notify_changed(true, true); + + mesh->material_cache.clear(); +} + +int RasterizerStorageRD::mesh_get_blend_shape_count(RID p_mesh) const { + const Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND_V(!mesh, -1); + return mesh->blend_shape_count; +} + +void RasterizerStorageRD::mesh_set_blend_shape_mode(RID p_mesh, RS::BlendShapeMode p_mode) { + Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND(!mesh); + ERR_FAIL_INDEX((int)p_mode, 2); + + mesh->blend_shape_mode = p_mode; +} +RS::BlendShapeMode RasterizerStorageRD::mesh_get_blend_shape_mode(RID p_mesh) const { + Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND_V(!mesh, RS::BLEND_SHAPE_MODE_NORMALIZED); + return mesh->blend_shape_mode; +} + +void RasterizerStorageRD::mesh_surface_update_region(RID p_mesh, int p_surface, int p_offset, const Vector<uint8_t> &p_data) { + Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND(!mesh); + ERR_FAIL_UNSIGNED_INDEX((uint32_t)p_surface, mesh->surface_count); + ERR_FAIL_COND(p_data.size() == 0); + uint64_t data_size = p_data.size(); + const uint8_t *r = p_data.ptr(); + + RD::get_singleton()->buffer_update(mesh->surfaces[p_surface]->vertex_buffer, p_offset, data_size, r); +} + +void RasterizerStorageRD::mesh_surface_set_material(RID p_mesh, int p_surface, RID p_material) { + Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND(!mesh); + ERR_FAIL_UNSIGNED_INDEX((uint32_t)p_surface, mesh->surface_count); + mesh->surfaces[p_surface]->material = p_material; + + mesh->instance_dependency.instance_notify_changed(false, true); + mesh->material_cache.clear(); +} +RID RasterizerStorageRD::mesh_surface_get_material(RID p_mesh, int p_surface) const { + Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND_V(!mesh, RID()); + ERR_FAIL_UNSIGNED_INDEX_V((uint32_t)p_surface, mesh->surface_count, RID()); + + return mesh->surfaces[p_surface]->material; +} + +RS::SurfaceData RasterizerStorageRD::mesh_get_surface(RID p_mesh, int p_surface) const { + + Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND_V(!mesh, RS::SurfaceData()); + ERR_FAIL_UNSIGNED_INDEX_V((uint32_t)p_surface, mesh->surface_count, RS::SurfaceData()); + + Mesh::Surface &s = *mesh->surfaces[p_surface]; + + RS::SurfaceData sd; + sd.format = s.format; + sd.vertex_data = RD::get_singleton()->buffer_get_data(s.vertex_buffer); + sd.vertex_count = s.vertex_count; + sd.index_count = s.index_count; + sd.primitive = s.primitive; + + if (sd.index_count) { + sd.index_data = RD::get_singleton()->buffer_get_data(s.index_buffer); + } + sd.aabb = s.aabb; + for (uint32_t i = 0; i < s.lod_count; i++) { + RS::SurfaceData::LOD lod; + lod.edge_length = s.lods[i].edge_length; + lod.index_data = RD::get_singleton()->buffer_get_data(s.lods[i].index_buffer); + sd.lods.push_back(lod); + } + + sd.bone_aabbs = s.bone_aabbs; + + for (int i = 0; i < s.blend_shapes.size(); i++) { + Vector<uint8_t> bs = RD::get_singleton()->buffer_get_data(s.blend_shapes[i]); + sd.blend_shapes.push_back(bs); + } + + return sd; +} + +int RasterizerStorageRD::mesh_get_surface_count(RID p_mesh) const { + Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND_V(!mesh, 0); + return mesh->surface_count; +} + +void RasterizerStorageRD::mesh_set_custom_aabb(RID p_mesh, const AABB &p_aabb) { + Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND(!mesh); + mesh->custom_aabb = p_aabb; +} +AABB RasterizerStorageRD::mesh_get_custom_aabb(RID p_mesh) const { + Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND_V(!mesh, AABB()); + return mesh->custom_aabb; +} + +AABB RasterizerStorageRD::mesh_get_aabb(RID p_mesh, RID p_skeleton) { + Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND_V(!mesh, AABB()); + + if (mesh->custom_aabb != AABB()) { + return mesh->custom_aabb; + } + + Skeleton *skeleton = skeleton_owner.getornull(p_skeleton); + + if (!skeleton || skeleton->size == 0) { + return mesh->aabb; + } + + AABB aabb; + + for (uint32_t i = 0; i < mesh->surface_count; i++) { + + AABB laabb; + if ((mesh->surfaces[i]->format & RS::ARRAY_FORMAT_BONES) && mesh->surfaces[i]->bone_aabbs.size()) { + + int bs = mesh->surfaces[i]->bone_aabbs.size(); + const AABB *skbones = mesh->surfaces[i]->bone_aabbs.ptr(); + + int sbs = skeleton->size; + ERR_CONTINUE(bs > sbs); + const float *baseptr = skeleton->data.ptr(); + + bool first = true; + + if (skeleton->use_2d) { + for (int j = 0; j < bs; j++) { + + if (skbones[0].size == Vector3()) + continue; //bone is unused + + const float *dataptr = baseptr + j * 8; + + Transform mtx; + + mtx.basis.elements[0].x = dataptr[0]; + mtx.basis.elements[1].x = dataptr[1]; + mtx.origin.x = dataptr[3]; + + mtx.basis.elements[0].y = dataptr[4]; + mtx.basis.elements[1].y = dataptr[5]; + mtx.origin.y = dataptr[7]; + + AABB baabb = mtx.xform(skbones[j]); + + if (first) { + laabb = baabb; + first = false; + } else { + laabb.merge_with(baabb); + } + } + } else { + for (int j = 0; j < bs; j++) { + + if (skbones[0].size == Vector3()) + continue; //bone is unused + + const float *dataptr = baseptr + j * 12; + + Transform mtx; + + mtx.basis.elements[0][0] = dataptr[0]; + mtx.basis.elements[0][1] = dataptr[1]; + mtx.basis.elements[0][2] = dataptr[2]; + mtx.origin.x = dataptr[3]; + mtx.basis.elements[1][0] = dataptr[4]; + mtx.basis.elements[1][1] = dataptr[5]; + mtx.basis.elements[1][2] = dataptr[6]; + mtx.origin.y = dataptr[7]; + mtx.basis.elements[2][0] = dataptr[8]; + mtx.basis.elements[2][1] = dataptr[9]; + mtx.basis.elements[2][2] = dataptr[10]; + mtx.origin.z = dataptr[11]; + + AABB baabb = mtx.xform(skbones[j]); + if (first) { + laabb = baabb; + first = false; + } else { + laabb.merge_with(baabb); + } + } + } + + if (laabb.size == Vector3()) { + laabb = mesh->surfaces[i]->aabb; + } + } else { + + laabb = mesh->surfaces[i]->aabb; + } + + if (i == 0) { + aabb = laabb; + } else { + aabb.merge_with(laabb); + } + } + + return aabb; +} + +void RasterizerStorageRD::mesh_clear(RID p_mesh) { + + Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND(!mesh); + for (uint32_t i = 0; i < mesh->surface_count; i++) { + Mesh::Surface &s = *mesh->surfaces[i]; + RD::get_singleton()->free(s.vertex_buffer); //clears arrays as dependency automatically, including all versions + if (s.versions) { + memfree(s.versions); //reallocs, so free with memfree. + } + + if (s.index_buffer.is_valid()) { + RD::get_singleton()->free(s.index_buffer); + } + + if (s.lod_count) { + for (uint32_t j = 0; j < s.lod_count; j++) { + RD::get_singleton()->free(s.lods[j].index_buffer); + } + memdelete_arr(s.lods); + } + + for (int32_t j = 0; j < s.blend_shapes.size(); j++) { + RD::get_singleton()->free(s.blend_shapes[j]); + } + + if (s.blend_shape_base_buffer.is_valid()) { + RD::get_singleton()->free(s.blend_shape_base_buffer); + } + + memdelete(mesh->surfaces[i]); + } + if (mesh->surfaces) { + memfree(mesh->surfaces); + } + + mesh->surfaces = nullptr; + mesh->surface_count = 0; + mesh->material_cache.clear(); + mesh->instance_dependency.instance_notify_changed(true, true); +} + +void RasterizerStorageRD::_mesh_surface_generate_version_for_input_mask(Mesh::Surface *s, uint32_t p_input_mask) { + uint32_t version = s->version_count; + s->version_count++; + s->versions = (Mesh::Surface::Version *)memrealloc(s->versions, sizeof(Mesh::Surface::Version) * s->version_count); + + Mesh::Surface::Version &v = s->versions[version]; + + Vector<RD::VertexDescription> attributes; + Vector<RID> buffers; + + uint32_t stride = 0; + + for (int i = 0; i < RS::ARRAY_WEIGHTS; i++) { + + RD::VertexDescription vd; + RID buffer; + vd.location = i; + + if (!(s->format & (1 << i))) { + // Not supplied by surface, use default value + buffer = mesh_default_rd_buffers[i]; + switch (i) { + + case RS::ARRAY_VERTEX: { + + vd.format = RD::DATA_FORMAT_R32G32B32_SFLOAT; + + } break; + case RS::ARRAY_NORMAL: { + vd.format = RD::DATA_FORMAT_R32G32B32_SFLOAT; + } break; + case RS::ARRAY_TANGENT: { + + vd.format = RD::DATA_FORMAT_R32G32B32A32_SFLOAT; + } break; + case RS::ARRAY_COLOR: { + + vd.format = RD::DATA_FORMAT_R32G32B32A32_SFLOAT; + + } break; + case RS::ARRAY_TEX_UV: { + + vd.format = RD::DATA_FORMAT_R32G32_SFLOAT; + + } break; + case RS::ARRAY_TEX_UV2: { + + vd.format = RD::DATA_FORMAT_R32G32_SFLOAT; + } break; + case RS::ARRAY_BONES: { + + //assumed weights too + vd.format = RD::DATA_FORMAT_R32G32B32A32_UINT; + } break; + } + } else { + //Supplied, use it + + vd.offset = stride; + vd.stride = 1; //mark that it needs a stride set + buffer = s->vertex_buffer; + + switch (i) { + + case RS::ARRAY_VERTEX: { + + if (s->format & RS::ARRAY_FLAG_USE_2D_VERTICES) { + vd.format = RD::DATA_FORMAT_R32G32_SFLOAT; + stride += sizeof(float) * 2; + } else { + vd.format = RD::DATA_FORMAT_R32G32B32_SFLOAT; + stride += sizeof(float) * 3; + } + + } break; + case RS::ARRAY_NORMAL: { + + if (s->format & RS::ARRAY_COMPRESS_NORMAL) { + vd.format = RD::DATA_FORMAT_R8G8B8A8_SNORM; + stride += sizeof(int8_t) * 4; + } else { + vd.format = RD::DATA_FORMAT_R32G32B32A32_SFLOAT; + stride += sizeof(float) * 4; + } + + } break; + case RS::ARRAY_TANGENT: { + + if (s->format & RS::ARRAY_COMPRESS_TANGENT) { + vd.format = RD::DATA_FORMAT_R8G8B8A8_SNORM; + stride += sizeof(int8_t) * 4; + } else { + vd.format = RD::DATA_FORMAT_R32G32B32A32_SFLOAT; + stride += sizeof(float) * 4; + } + + } break; + case RS::ARRAY_COLOR: { + + if (s->format & RS::ARRAY_COMPRESS_COLOR) { + vd.format = RD::DATA_FORMAT_R8G8B8A8_UNORM; + stride += sizeof(int8_t) * 4; + } else { + vd.format = RD::DATA_FORMAT_R32G32B32A32_SFLOAT; + stride += sizeof(float) * 4; + } + + } break; + case RS::ARRAY_TEX_UV: { + + if (s->format & RS::ARRAY_COMPRESS_TEX_UV) { + vd.format = RD::DATA_FORMAT_R16G16_SFLOAT; + stride += sizeof(int16_t) * 2; + } else { + vd.format = RD::DATA_FORMAT_R32G32_SFLOAT; + stride += sizeof(float) * 2; + } + + } break; + case RS::ARRAY_TEX_UV2: { + + if (s->format & RS::ARRAY_COMPRESS_TEX_UV2) { + vd.format = RD::DATA_FORMAT_R16G16_SFLOAT; + stride += sizeof(int16_t) * 2; + } else { + vd.format = RD::DATA_FORMAT_R32G32_SFLOAT; + stride += sizeof(float) * 2; + } + + } break; + case RS::ARRAY_BONES: { + //assumed weights too + + //unique format, internally 16 bits, exposed as single array for 32 + + vd.format = RD::DATA_FORMAT_R32G32B32A32_UINT; + stride += sizeof(int32_t) * 4; + + } break; + } + } + + if (!(p_input_mask & (1 << i))) { + continue; // Shader does not need this, skip it + } + + attributes.push_back(vd); + buffers.push_back(buffer); + } + + //update final stride + for (int i = 0; i < attributes.size(); i++) { + if (attributes[i].stride == 1) { + attributes.write[i].stride = stride; + } + } + + v.input_mask = p_input_mask; + v.vertex_format = RD::get_singleton()->vertex_format_create(attributes); + v.vertex_array = RD::get_singleton()->vertex_array_create(s->vertex_count, v.vertex_format, buffers); +} + +////////////////// MULTIMESH + +RID RasterizerStorageRD::multimesh_create() { + + return multimesh_owner.make_rid(MultiMesh()); +} + +void RasterizerStorageRD::multimesh_allocate(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors, bool p_use_custom_data) { + + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND(!multimesh); + + if (multimesh->instances == p_instances && multimesh->xform_format == p_transform_format && multimesh->uses_colors == p_use_colors && multimesh->uses_custom_data == p_use_custom_data) { + return; + } + + if (multimesh->buffer.is_valid()) { + RD::get_singleton()->free(multimesh->buffer); + multimesh->buffer = RID(); + multimesh->uniform_set_3d = RID(); //cleared by dependency + } + + if (multimesh->data_cache_dirty_regions) { + memdelete_arr(multimesh->data_cache_dirty_regions); + multimesh->data_cache_dirty_regions = nullptr; + multimesh->data_cache_used_dirty_regions = 0; + } + + multimesh->instances = p_instances; + multimesh->xform_format = p_transform_format; + multimesh->uses_colors = p_use_colors; + multimesh->color_offset_cache = p_transform_format == RS::MULTIMESH_TRANSFORM_2D ? 8 : 12; + multimesh->uses_custom_data = p_use_custom_data; + multimesh->custom_data_offset_cache = multimesh->color_offset_cache + (p_use_colors ? 4 : 0); + multimesh->stride_cache = multimesh->custom_data_offset_cache + (p_use_custom_data ? 4 : 0); + multimesh->buffer_set = false; + + //print_line("allocate, elements: " + itos(p_instances) + " 2D: " + itos(p_transform_format == RS::MULTIMESH_TRANSFORM_2D) + " colors " + itos(multimesh->uses_colors) + " data " + itos(multimesh->uses_custom_data) + " stride " + itos(multimesh->stride_cache) + " total size " + itos(multimesh->stride_cache * multimesh->instances)); + multimesh->data_cache = Vector<float>(); + multimesh->aabb = AABB(); + multimesh->aabb_dirty = false; + multimesh->visible_instances = MIN(multimesh->visible_instances, multimesh->instances); + + if (multimesh->instances) { + + multimesh->buffer = RD::get_singleton()->storage_buffer_create(multimesh->instances * multimesh->stride_cache * 4); + } +} + +int RasterizerStorageRD::multimesh_get_instance_count(RID p_multimesh) const { + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND_V(!multimesh, 0); + return multimesh->instances; +} + +void RasterizerStorageRD::multimesh_set_mesh(RID p_multimesh, RID p_mesh) { + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND(!multimesh); + if (multimesh->mesh == p_mesh) { + return; + } + multimesh->mesh = p_mesh; + + if (multimesh->instances == 0) { + return; + } + + if (multimesh->data_cache.size()) { + //we have a data cache, just mark it dirt + _multimesh_mark_all_dirty(multimesh, false, true); + } else if (multimesh->instances) { + //need to re-create AABB unfortunately, calling this has a penalty + if (multimesh->buffer_set) { + Vector<uint8_t> buffer = RD::get_singleton()->buffer_get_data(multimesh->buffer); + const uint8_t *r = buffer.ptr(); + const float *data = (const float *)r; + _multimesh_re_create_aabb(multimesh, data, multimesh->instances); + } + } + + multimesh->instance_dependency.instance_notify_changed(true, true); +} + +#define MULTIMESH_DIRTY_REGION_SIZE 512 + +void RasterizerStorageRD::_multimesh_make_local(MultiMesh *multimesh) const { + if (multimesh->data_cache.size() > 0) { + return; //already local + } + ERR_FAIL_COND(multimesh->data_cache.size() > 0); + // this means that the user wants to load/save individual elements, + // for this, the data must reside on CPU, so just copy it there. + multimesh->data_cache.resize(multimesh->instances * multimesh->stride_cache); + { + float *w = multimesh->data_cache.ptrw(); + + if (multimesh->buffer_set) { + Vector<uint8_t> buffer = RD::get_singleton()->buffer_get_data(multimesh->buffer); + { + + const uint8_t *r = buffer.ptr(); + copymem(w, r, buffer.size()); + } + } else { + zeromem(w, multimesh->instances * multimesh->stride_cache * sizeof(float)); + } + } + uint32_t data_cache_dirty_region_count = (multimesh->instances - 1) / MULTIMESH_DIRTY_REGION_SIZE + 1; + multimesh->data_cache_dirty_regions = memnew_arr(bool, data_cache_dirty_region_count); + for (uint32_t i = 0; i < data_cache_dirty_region_count; i++) { + multimesh->data_cache_dirty_regions[i] = 0; + } + multimesh->data_cache_used_dirty_regions = 0; +} + +void RasterizerStorageRD::_multimesh_mark_dirty(MultiMesh *multimesh, int p_index, bool p_aabb) { + + uint32_t region_index = p_index / MULTIMESH_DIRTY_REGION_SIZE; +#ifdef DEBUG_ENABLED + uint32_t data_cache_dirty_region_count = (multimesh->instances - 1) / MULTIMESH_DIRTY_REGION_SIZE + 1; + ERR_FAIL_UNSIGNED_INDEX(region_index, data_cache_dirty_region_count); //bug +#endif + if (!multimesh->data_cache_dirty_regions[region_index]) { + multimesh->data_cache_dirty_regions[region_index] = true; + multimesh->data_cache_used_dirty_regions++; + } + + if (p_aabb) { + multimesh->aabb_dirty = true; + } + + if (!multimesh->dirty) { + multimesh->dirty_list = multimesh_dirty_list; + multimesh_dirty_list = multimesh; + multimesh->dirty = true; + } +} + +void RasterizerStorageRD::_multimesh_mark_all_dirty(MultiMesh *multimesh, bool p_data, bool p_aabb) { + if (p_data) { + uint32_t data_cache_dirty_region_count = (multimesh->instances - 1) / MULTIMESH_DIRTY_REGION_SIZE + 1; + + for (uint32_t i = 0; i < data_cache_dirty_region_count; i++) { + if (!multimesh->data_cache_dirty_regions[i]) { + multimesh->data_cache_dirty_regions[i] = true; + multimesh->data_cache_used_dirty_regions++; + } + } + } + + if (p_aabb) { + multimesh->aabb_dirty = true; + } + + if (!multimesh->dirty) { + multimesh->dirty_list = multimesh_dirty_list; + multimesh_dirty_list = multimesh; + multimesh->dirty = true; + } +} + +void RasterizerStorageRD::_multimesh_re_create_aabb(MultiMesh *multimesh, const float *p_data, int p_instances) { + + ERR_FAIL_COND(multimesh->mesh.is_null()); + AABB aabb; + AABB mesh_aabb = mesh_get_aabb(multimesh->mesh); + for (int i = 0; i < p_instances; i++) { + const float *data = p_data + multimesh->stride_cache * i; + Transform t; + + if (multimesh->xform_format == RS::MULTIMESH_TRANSFORM_3D) { + + t.basis.elements[0][0] = data[0]; + t.basis.elements[0][1] = data[1]; + t.basis.elements[0][2] = data[2]; + t.origin.x = data[3]; + t.basis.elements[1][0] = data[4]; + t.basis.elements[1][1] = data[5]; + t.basis.elements[1][2] = data[6]; + t.origin.y = data[7]; + t.basis.elements[2][0] = data[8]; + t.basis.elements[2][1] = data[9]; + t.basis.elements[2][2] = data[10]; + t.origin.z = data[11]; + + } else { + + t.basis.elements[0].x = data[0]; + t.basis.elements[1].x = data[1]; + t.origin.x = data[3]; + + t.basis.elements[0].y = data[4]; + t.basis.elements[1].y = data[5]; + t.origin.y = data[7]; + } + + if (i == 0) { + aabb = t.xform(mesh_aabb); + } else { + aabb.merge_with(t.xform(mesh_aabb)); + } + } + + multimesh->aabb = aabb; +} + +void RasterizerStorageRD::multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform &p_transform) { + + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND(!multimesh); + ERR_FAIL_INDEX(p_index, multimesh->instances); + ERR_FAIL_COND(multimesh->xform_format != RS::MULTIMESH_TRANSFORM_3D); + + _multimesh_make_local(multimesh); + + { + float *w = multimesh->data_cache.ptrw(); + + float *dataptr = w + p_index * multimesh->stride_cache; + + dataptr[0] = p_transform.basis.elements[0][0]; + dataptr[1] = p_transform.basis.elements[0][1]; + dataptr[2] = p_transform.basis.elements[0][2]; + dataptr[3] = p_transform.origin.x; + dataptr[4] = p_transform.basis.elements[1][0]; + dataptr[5] = p_transform.basis.elements[1][1]; + dataptr[6] = p_transform.basis.elements[1][2]; + dataptr[7] = p_transform.origin.y; + dataptr[8] = p_transform.basis.elements[2][0]; + dataptr[9] = p_transform.basis.elements[2][1]; + dataptr[10] = p_transform.basis.elements[2][2]; + dataptr[11] = p_transform.origin.z; + } + + _multimesh_mark_dirty(multimesh, p_index, true); +} + +void RasterizerStorageRD::multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform) { + + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND(!multimesh); + ERR_FAIL_INDEX(p_index, multimesh->instances); + ERR_FAIL_COND(multimesh->xform_format != RS::MULTIMESH_TRANSFORM_2D); + + _multimesh_make_local(multimesh); + + { + float *w = multimesh->data_cache.ptrw(); + + float *dataptr = w + p_index * multimesh->stride_cache; + + dataptr[0] = p_transform.elements[0][0]; + dataptr[1] = p_transform.elements[1][0]; + dataptr[2] = 0; + dataptr[3] = p_transform.elements[2][0]; + dataptr[4] = p_transform.elements[0][1]; + dataptr[5] = p_transform.elements[1][1]; + dataptr[6] = 0; + dataptr[7] = p_transform.elements[2][1]; + } + + _multimesh_mark_dirty(multimesh, p_index, true); +} +void RasterizerStorageRD::multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) { + + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND(!multimesh); + ERR_FAIL_INDEX(p_index, multimesh->instances); + ERR_FAIL_COND(!multimesh->uses_colors); + + _multimesh_make_local(multimesh); + + { + float *w = multimesh->data_cache.ptrw(); + + float *dataptr = w + p_index * multimesh->stride_cache + multimesh->color_offset_cache; + + dataptr[0] = p_color.r; + dataptr[1] = p_color.g; + dataptr[2] = p_color.b; + dataptr[3] = p_color.a; + } + + _multimesh_mark_dirty(multimesh, p_index, false); +} +void RasterizerStorageRD::multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) { + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND(!multimesh); + ERR_FAIL_INDEX(p_index, multimesh->instances); + ERR_FAIL_COND(!multimesh->uses_custom_data); + + _multimesh_make_local(multimesh); + + { + float *w = multimesh->data_cache.ptrw(); + + float *dataptr = w + p_index * multimesh->stride_cache + multimesh->custom_data_offset_cache; + + dataptr[0] = p_color.r; + dataptr[1] = p_color.g; + dataptr[2] = p_color.b; + dataptr[3] = p_color.a; + } + + _multimesh_mark_dirty(multimesh, p_index, false); +} + +RID RasterizerStorageRD::multimesh_get_mesh(RID p_multimesh) const { + + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND_V(!multimesh, RID()); + + return multimesh->mesh; +} + +Transform RasterizerStorageRD::multimesh_instance_get_transform(RID p_multimesh, int p_index) const { + + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND_V(!multimesh, Transform()); + ERR_FAIL_INDEX_V(p_index, multimesh->instances, Transform()); + ERR_FAIL_COND_V(multimesh->xform_format != RS::MULTIMESH_TRANSFORM_3D, Transform()); + + _multimesh_make_local(multimesh); + + Transform t; + { + const float *r = multimesh->data_cache.ptr(); + + const float *dataptr = r + p_index * multimesh->stride_cache; + + t.basis.elements[0][0] = dataptr[0]; + t.basis.elements[0][1] = dataptr[1]; + t.basis.elements[0][2] = dataptr[2]; + t.origin.x = dataptr[3]; + t.basis.elements[1][0] = dataptr[4]; + t.basis.elements[1][1] = dataptr[5]; + t.basis.elements[1][2] = dataptr[6]; + t.origin.y = dataptr[7]; + t.basis.elements[2][0] = dataptr[8]; + t.basis.elements[2][1] = dataptr[9]; + t.basis.elements[2][2] = dataptr[10]; + t.origin.z = dataptr[11]; + } + + return t; +} +Transform2D RasterizerStorageRD::multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const { + + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND_V(!multimesh, Transform2D()); + ERR_FAIL_INDEX_V(p_index, multimesh->instances, Transform2D()); + ERR_FAIL_COND_V(multimesh->xform_format != RS::MULTIMESH_TRANSFORM_2D, Transform2D()); + + _multimesh_make_local(multimesh); + + Transform2D t; + { + const float *r = multimesh->data_cache.ptr(); + + const float *dataptr = r + p_index * multimesh->stride_cache; + + t.elements[0][0] = dataptr[0]; + t.elements[1][0] = dataptr[1]; + t.elements[2][0] = dataptr[3]; + t.elements[0][1] = dataptr[4]; + t.elements[1][1] = dataptr[5]; + t.elements[2][1] = dataptr[7]; + } + + return t; +} +Color RasterizerStorageRD::multimesh_instance_get_color(RID p_multimesh, int p_index) const { + + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND_V(!multimesh, Color()); + ERR_FAIL_INDEX_V(p_index, multimesh->instances, Color()); + ERR_FAIL_COND_V(!multimesh->uses_colors, Color()); + + _multimesh_make_local(multimesh); + + Color c; + { + const float *r = multimesh->data_cache.ptr(); + + const float *dataptr = r + p_index * multimesh->stride_cache + multimesh->color_offset_cache; + + c.r = dataptr[0]; + c.g = dataptr[1]; + c.b = dataptr[2]; + c.a = dataptr[3]; + } + + return c; +} +Color RasterizerStorageRD::multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const { + + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND_V(!multimesh, Color()); + ERR_FAIL_INDEX_V(p_index, multimesh->instances, Color()); + ERR_FAIL_COND_V(!multimesh->uses_custom_data, Color()); + + _multimesh_make_local(multimesh); + + Color c; + { + const float *r = multimesh->data_cache.ptr(); + + const float *dataptr = r + p_index * multimesh->stride_cache + multimesh->custom_data_offset_cache; + + c.r = dataptr[0]; + c.g = dataptr[1]; + c.b = dataptr[2]; + c.a = dataptr[3]; + } + + return c; +} + +void RasterizerStorageRD::multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) { + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND(!multimesh); + ERR_FAIL_COND(p_buffer.size() != (multimesh->instances * (int)multimesh->stride_cache)); + + { + const float *r = p_buffer.ptr(); + RD::get_singleton()->buffer_update(multimesh->buffer, 0, p_buffer.size() * sizeof(float), r, false); + multimesh->buffer_set = true; + } + + if (multimesh->data_cache.size()) { + //if we have a data cache, just update it + multimesh->data_cache = p_buffer; + { + //clear dirty since nothing will be dirty anymore + uint32_t data_cache_dirty_region_count = (multimesh->instances - 1) / MULTIMESH_DIRTY_REGION_SIZE + 1; + for (uint32_t i = 0; i < data_cache_dirty_region_count; i++) { + multimesh->data_cache_dirty_regions[i] = false; + } + multimesh->data_cache_used_dirty_regions = 0; + } + + _multimesh_mark_all_dirty(multimesh, false, true); //update AABB + } else if (multimesh->mesh.is_valid()) { + //if we have a mesh set, we need to re-generate the AABB from the new data + const float *data = p_buffer.ptr(); + + _multimesh_re_create_aabb(multimesh, data, multimesh->instances); + multimesh->instance_dependency.instance_notify_changed(true, false); + } +} + +Vector<float> RasterizerStorageRD::multimesh_get_buffer(RID p_multimesh) const { + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND_V(!multimesh, Vector<float>()); + if (multimesh->buffer.is_null()) { + return Vector<float>(); + } else if (multimesh->data_cache.size()) { + return multimesh->data_cache; + } else { + //get from memory + + Vector<uint8_t> buffer = RD::get_singleton()->buffer_get_data(multimesh->buffer); + Vector<float> ret; + ret.resize(multimesh->instances); + { + float *w = multimesh->data_cache.ptrw(); + const uint8_t *r = buffer.ptr(); + copymem(w, r, buffer.size()); + } + + return ret; + } +} + +void RasterizerStorageRD::multimesh_set_visible_instances(RID p_multimesh, int p_visible) { + + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND(!multimesh); + ERR_FAIL_COND(p_visible < -1 || p_visible > multimesh->instances); + if (multimesh->visible_instances == p_visible) { + return; + } + + if (multimesh->data_cache.size()) { + //there is a data cache.. + _multimesh_mark_all_dirty(multimesh, false, true); + } + + multimesh->visible_instances = p_visible; +} +int RasterizerStorageRD::multimesh_get_visible_instances(RID p_multimesh) const { + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND_V(!multimesh, 0); + return multimesh->visible_instances; +} + +AABB RasterizerStorageRD::multimesh_get_aabb(RID p_multimesh) const { + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND_V(!multimesh, AABB()); + if (multimesh->aabb_dirty) { + const_cast<RasterizerStorageRD *>(this)->_update_dirty_multimeshes(); + } + return multimesh->aabb; +} + +void RasterizerStorageRD::_update_dirty_multimeshes() { + + while (multimesh_dirty_list) { + + MultiMesh *multimesh = multimesh_dirty_list; + + if (multimesh->data_cache.size()) { //may have been cleared, so only process if it exists + const float *data = multimesh->data_cache.ptr(); + + uint32_t visible_instances = multimesh->visible_instances >= 0 ? multimesh->visible_instances : multimesh->instances; + + if (multimesh->data_cache_used_dirty_regions) { + + uint32_t data_cache_dirty_region_count = (multimesh->instances - 1) / MULTIMESH_DIRTY_REGION_SIZE + 1; + uint32_t visible_region_count = (visible_instances - 1) / MULTIMESH_DIRTY_REGION_SIZE + 1; + + uint32_t region_size = multimesh->stride_cache * MULTIMESH_DIRTY_REGION_SIZE * sizeof(float); + + if (multimesh->data_cache_used_dirty_regions > 32 || multimesh->data_cache_used_dirty_regions > visible_region_count / 2) { + //if there too many dirty regions, or represent the majority of regions, just copy all, else transfer cost piles up too much + RD::get_singleton()->buffer_update(multimesh->buffer, 0, MIN(visible_region_count * region_size, multimesh->instances * multimesh->stride_cache * sizeof(float)), data, false); + } else { + //not that many regions? update them all + for (uint32_t i = 0; i < visible_region_count; i++) { + if (multimesh->data_cache_dirty_regions[i]) { + uint64_t offset = i * region_size; + uint64_t size = multimesh->stride_cache * multimesh->instances * sizeof(float); + RD::get_singleton()->buffer_update(multimesh->buffer, offset, MIN(region_size, size - offset), &data[i * region_size], false); + } + } + } + + for (uint32_t i = 0; i < data_cache_dirty_region_count; i++) { + multimesh->data_cache_dirty_regions[i] = false; + } + + multimesh->data_cache_used_dirty_regions = 0; + } + + if (multimesh->aabb_dirty) { + //aabb is dirty.. + _multimesh_re_create_aabb(multimesh, data, visible_instances); + multimesh->aabb_dirty = false; + multimesh->instance_dependency.instance_notify_changed(true, false); + } + } + + multimesh_dirty_list = multimesh->dirty_list; + + multimesh->dirty_list = nullptr; + multimesh->dirty = false; + } + + multimesh_dirty_list = nullptr; +} + +/* SKELETON */ + +/* SKELETON API */ + +RID RasterizerStorageRD::skeleton_create() { + + return skeleton_owner.make_rid(Skeleton()); +} + +void RasterizerStorageRD::_skeleton_make_dirty(Skeleton *skeleton) { + + if (!skeleton->dirty) { + skeleton->dirty = true; + skeleton->dirty_list = skeleton_dirty_list; + skeleton_dirty_list = skeleton; + } +} + +void RasterizerStorageRD::skeleton_allocate(RID p_skeleton, int p_bones, bool p_2d_skeleton) { + + Skeleton *skeleton = skeleton_owner.getornull(p_skeleton); + ERR_FAIL_COND(!skeleton); + ERR_FAIL_COND(p_bones < 0); + + if (skeleton->size == p_bones && skeleton->use_2d == p_2d_skeleton) + return; + + skeleton->size = p_bones; + skeleton->use_2d = p_2d_skeleton; + skeleton->uniform_set_3d = RID(); + + if (skeleton->buffer.is_valid()) { + RD::get_singleton()->free(skeleton->buffer); + skeleton->buffer = RID(); + skeleton->data.resize(0); + } + + if (skeleton->size) { + + skeleton->data.resize(skeleton->size * (skeleton->use_2d ? 8 : 12)); + skeleton->buffer = RD::get_singleton()->storage_buffer_create(skeleton->data.size() * sizeof(float)); + zeromem(skeleton->data.ptrw(), skeleton->data.size() * sizeof(float)); + + _skeleton_make_dirty(skeleton); + } +} +int RasterizerStorageRD::skeleton_get_bone_count(RID p_skeleton) const { + + Skeleton *skeleton = skeleton_owner.getornull(p_skeleton); + ERR_FAIL_COND_V(!skeleton, 0); + + return skeleton->size; +} + +void RasterizerStorageRD::skeleton_bone_set_transform(RID p_skeleton, int p_bone, const Transform &p_transform) { + + Skeleton *skeleton = skeleton_owner.getornull(p_skeleton); + + ERR_FAIL_COND(!skeleton); + ERR_FAIL_INDEX(p_bone, skeleton->size); + ERR_FAIL_COND(skeleton->use_2d); + + float *dataptr = skeleton->data.ptrw() + p_bone * 12; + + dataptr[0] = p_transform.basis.elements[0][0]; + dataptr[1] = p_transform.basis.elements[0][1]; + dataptr[2] = p_transform.basis.elements[0][2]; + dataptr[3] = p_transform.origin.x; + dataptr[4] = p_transform.basis.elements[1][0]; + dataptr[5] = p_transform.basis.elements[1][1]; + dataptr[6] = p_transform.basis.elements[1][2]; + dataptr[7] = p_transform.origin.y; + dataptr[8] = p_transform.basis.elements[2][0]; + dataptr[9] = p_transform.basis.elements[2][1]; + dataptr[10] = p_transform.basis.elements[2][2]; + dataptr[11] = p_transform.origin.z; + + _skeleton_make_dirty(skeleton); +} + +Transform RasterizerStorageRD::skeleton_bone_get_transform(RID p_skeleton, int p_bone) const { + + Skeleton *skeleton = skeleton_owner.getornull(p_skeleton); + + ERR_FAIL_COND_V(!skeleton, Transform()); + ERR_FAIL_INDEX_V(p_bone, skeleton->size, Transform()); + ERR_FAIL_COND_V(skeleton->use_2d, Transform()); + + const float *dataptr = skeleton->data.ptr() + p_bone * 12; + + Transform t; + + t.basis.elements[0][0] = dataptr[0]; + t.basis.elements[0][1] = dataptr[1]; + t.basis.elements[0][2] = dataptr[2]; + t.origin.x = dataptr[3]; + t.basis.elements[1][0] = dataptr[4]; + t.basis.elements[1][1] = dataptr[5]; + t.basis.elements[1][2] = dataptr[6]; + t.origin.y = dataptr[7]; + t.basis.elements[2][0] = dataptr[8]; + t.basis.elements[2][1] = dataptr[9]; + t.basis.elements[2][2] = dataptr[10]; + t.origin.z = dataptr[11]; + + return t; +} +void RasterizerStorageRD::skeleton_bone_set_transform_2d(RID p_skeleton, int p_bone, const Transform2D &p_transform) { + + Skeleton *skeleton = skeleton_owner.getornull(p_skeleton); + + ERR_FAIL_COND(!skeleton); + ERR_FAIL_INDEX(p_bone, skeleton->size); + ERR_FAIL_COND(!skeleton->use_2d); + + float *dataptr = skeleton->data.ptrw() + p_bone * 8; + + dataptr[0] = p_transform.elements[0][0]; + dataptr[1] = p_transform.elements[1][0]; + dataptr[2] = 0; + dataptr[3] = p_transform.elements[2][0]; + dataptr[4] = p_transform.elements[0][1]; + dataptr[5] = p_transform.elements[1][1]; + dataptr[6] = 0; + dataptr[7] = p_transform.elements[2][1]; + + _skeleton_make_dirty(skeleton); +} +Transform2D RasterizerStorageRD::skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const { + + Skeleton *skeleton = skeleton_owner.getornull(p_skeleton); + + ERR_FAIL_COND_V(!skeleton, Transform2D()); + ERR_FAIL_INDEX_V(p_bone, skeleton->size, Transform2D()); + ERR_FAIL_COND_V(!skeleton->use_2d, Transform2D()); + + const float *dataptr = skeleton->data.ptr() + p_bone * 8; + + Transform2D t; + t.elements[0][0] = dataptr[0]; + t.elements[1][0] = dataptr[1]; + t.elements[2][0] = dataptr[3]; + t.elements[0][1] = dataptr[4]; + t.elements[1][1] = dataptr[5]; + t.elements[2][1] = dataptr[7]; + + return t; +} + +void RasterizerStorageRD::skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform) { + + Skeleton *skeleton = skeleton_owner.getornull(p_skeleton); + + ERR_FAIL_COND(!skeleton->use_2d); + + skeleton->base_transform_2d = p_base_transform; +} + +void RasterizerStorageRD::_update_dirty_skeletons() { + + while (skeleton_dirty_list) { + + Skeleton *skeleton = skeleton_dirty_list; + + if (skeleton->size) { + + RD::get_singleton()->buffer_update(skeleton->buffer, 0, skeleton->data.size() * sizeof(float), skeleton->data.ptr(), false); + } + + skeleton_dirty_list = skeleton->dirty_list; + + skeleton->instance_dependency.instance_notify_changed(true, false); + + skeleton->dirty = false; + skeleton->dirty_list = nullptr; + } + + skeleton_dirty_list = nullptr; +} + +/* LIGHT */ + +RID RasterizerStorageRD::light_create(RS::LightType p_type) { + + Light light; + light.type = p_type; + + light.param[RS::LIGHT_PARAM_ENERGY] = 1.0; + light.param[RS::LIGHT_PARAM_INDIRECT_ENERGY] = 1.0; + light.param[RS::LIGHT_PARAM_SPECULAR] = 0.5; + light.param[RS::LIGHT_PARAM_RANGE] = 1.0; + light.param[RS::LIGHT_PARAM_SPOT_ANGLE] = 45; + light.param[RS::LIGHT_PARAM_CONTACT_SHADOW_SIZE] = 45; + light.param[RS::LIGHT_PARAM_SHADOW_MAX_DISTANCE] = 0; + light.param[RS::LIGHT_PARAM_SHADOW_SPLIT_1_OFFSET] = 0.1; + light.param[RS::LIGHT_PARAM_SHADOW_SPLIT_2_OFFSET] = 0.3; + light.param[RS::LIGHT_PARAM_SHADOW_SPLIT_3_OFFSET] = 0.6; + light.param[RS::LIGHT_PARAM_SHADOW_FADE_START] = 0.8; + light.param[RS::LIGHT_PARAM_SHADOW_NORMAL_BIAS] = 0.1; + light.param[RS::LIGHT_PARAM_SHADOW_BIAS_SPLIT_SCALE] = 0.1; + + return light_owner.make_rid(light); +} + +void RasterizerStorageRD::light_set_color(RID p_light, const Color &p_color) { + + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND(!light); + + light->color = p_color; +} +void RasterizerStorageRD::light_set_param(RID p_light, RS::LightParam p_param, float p_value) { + + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND(!light); + ERR_FAIL_INDEX(p_param, RS::LIGHT_PARAM_MAX); + + switch (p_param) { + case RS::LIGHT_PARAM_RANGE: + case RS::LIGHT_PARAM_SPOT_ANGLE: + case RS::LIGHT_PARAM_SHADOW_MAX_DISTANCE: + case RS::LIGHT_PARAM_SHADOW_SPLIT_1_OFFSET: + case RS::LIGHT_PARAM_SHADOW_SPLIT_2_OFFSET: + case RS::LIGHT_PARAM_SHADOW_SPLIT_3_OFFSET: + case RS::LIGHT_PARAM_SHADOW_NORMAL_BIAS: + case RS::LIGHT_PARAM_SHADOW_BIAS: { + + light->version++; + light->instance_dependency.instance_notify_changed(true, false); + } break; + default: { + } + } + + light->param[p_param] = p_value; +} +void RasterizerStorageRD::light_set_shadow(RID p_light, bool p_enabled) { + + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND(!light); + light->shadow = p_enabled; + + light->version++; + light->instance_dependency.instance_notify_changed(true, false); +} + +void RasterizerStorageRD::light_set_shadow_color(RID p_light, const Color &p_color) { + + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND(!light); + light->shadow_color = p_color; +} + +void RasterizerStorageRD::light_set_projector(RID p_light, RID p_texture) { + + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND(!light); + + light->projector = p_texture; +} + +void RasterizerStorageRD::light_set_negative(RID p_light, bool p_enable) { + + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND(!light); + + light->negative = p_enable; +} +void RasterizerStorageRD::light_set_cull_mask(RID p_light, uint32_t p_mask) { + + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND(!light); + + light->cull_mask = p_mask; + + light->version++; + light->instance_dependency.instance_notify_changed(true, false); +} + +void RasterizerStorageRD::light_set_reverse_cull_face_mode(RID p_light, bool p_enabled) { + + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND(!light); + + light->reverse_cull = p_enabled; + + light->version++; + light->instance_dependency.instance_notify_changed(true, false); +} + +void RasterizerStorageRD::light_set_use_gi(RID p_light, bool p_enabled) { + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND(!light); + + light->use_gi = p_enabled; + + light->version++; + light->instance_dependency.instance_notify_changed(true, false); +} +void RasterizerStorageRD::light_omni_set_shadow_mode(RID p_light, RS::LightOmniShadowMode p_mode) { + + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND(!light); + + light->omni_shadow_mode = p_mode; + + light->version++; + light->instance_dependency.instance_notify_changed(true, false); +} + +RS::LightOmniShadowMode RasterizerStorageRD::light_omni_get_shadow_mode(RID p_light) { + + const Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND_V(!light, RS::LIGHT_OMNI_SHADOW_CUBE); + + return light->omni_shadow_mode; +} + +void RasterizerStorageRD::light_directional_set_shadow_mode(RID p_light, RS::LightDirectionalShadowMode p_mode) { + + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND(!light); + + light->directional_shadow_mode = p_mode; + light->version++; + light->instance_dependency.instance_notify_changed(true, false); +} + +void RasterizerStorageRD::light_directional_set_blend_splits(RID p_light, bool p_enable) { + + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND(!light); + + light->directional_blend_splits = p_enable; + light->version++; + light->instance_dependency.instance_notify_changed(true, false); +} + +bool RasterizerStorageRD::light_directional_get_blend_splits(RID p_light) const { + + const Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND_V(!light, false); + + return light->directional_blend_splits; +} + +RS::LightDirectionalShadowMode RasterizerStorageRD::light_directional_get_shadow_mode(RID p_light) { + + const Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND_V(!light, RS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL); + + return light->directional_shadow_mode; +} + +void RasterizerStorageRD::light_directional_set_shadow_depth_range_mode(RID p_light, RS::LightDirectionalShadowDepthRangeMode p_range_mode) { + + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND(!light); + + light->directional_range_mode = p_range_mode; +} + +RS::LightDirectionalShadowDepthRangeMode RasterizerStorageRD::light_directional_get_shadow_depth_range_mode(RID p_light) const { + + const Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND_V(!light, RS::LIGHT_DIRECTIONAL_SHADOW_DEPTH_RANGE_STABLE); + + return light->directional_range_mode; +} + +bool RasterizerStorageRD::light_get_use_gi(RID p_light) { + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND_V(!light, false); + + return light->use_gi; +} + +uint64_t RasterizerStorageRD::light_get_version(RID p_light) const { + + const Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND_V(!light, 0); + + return light->version; +} + +AABB RasterizerStorageRD::light_get_aabb(RID p_light) const { + + const Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND_V(!light, AABB()); + + switch (light->type) { + + case RS::LIGHT_SPOT: { + + float len = light->param[RS::LIGHT_PARAM_RANGE]; + float size = Math::tan(Math::deg2rad(light->param[RS::LIGHT_PARAM_SPOT_ANGLE])) * len; + return AABB(Vector3(-size, -size, -len), Vector3(size * 2, size * 2, len)); + }; + case RS::LIGHT_OMNI: { + + float r = light->param[RS::LIGHT_PARAM_RANGE]; + return AABB(-Vector3(r, r, r), Vector3(r, r, r) * 2); + }; + case RS::LIGHT_DIRECTIONAL: { + + return AABB(); + }; + } + + ERR_FAIL_V(AABB()); +} + +/* REFLECTION PROBE */ + +RID RasterizerStorageRD::reflection_probe_create() { + + return reflection_probe_owner.make_rid(ReflectionProbe()); +} + +void RasterizerStorageRD::reflection_probe_set_update_mode(RID p_probe, RS::ReflectionProbeUpdateMode p_mode) { + + ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND(!reflection_probe); + + reflection_probe->update_mode = p_mode; + reflection_probe->instance_dependency.instance_notify_changed(true, false); +} + +void RasterizerStorageRD::reflection_probe_set_intensity(RID p_probe, float p_intensity) { + + ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND(!reflection_probe); + + reflection_probe->intensity = p_intensity; +} + +void RasterizerStorageRD::reflection_probe_set_interior_ambient(RID p_probe, const Color &p_ambient) { + + ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND(!reflection_probe); + + reflection_probe->interior_ambient = p_ambient; +} + +void RasterizerStorageRD::reflection_probe_set_interior_ambient_energy(RID p_probe, float p_energy) { + + ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND(!reflection_probe); + + reflection_probe->interior_ambient_energy = p_energy; +} + +void RasterizerStorageRD::reflection_probe_set_interior_ambient_probe_contribution(RID p_probe, float p_contrib) { + + ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND(!reflection_probe); + + reflection_probe->interior_ambient_probe_contrib = p_contrib; +} + +void RasterizerStorageRD::reflection_probe_set_max_distance(RID p_probe, float p_distance) { + + ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND(!reflection_probe); + + reflection_probe->max_distance = p_distance; + + reflection_probe->instance_dependency.instance_notify_changed(true, false); +} +void RasterizerStorageRD::reflection_probe_set_extents(RID p_probe, const Vector3 &p_extents) { + + ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND(!reflection_probe); + + reflection_probe->extents = p_extents; + reflection_probe->instance_dependency.instance_notify_changed(true, false); +} +void RasterizerStorageRD::reflection_probe_set_origin_offset(RID p_probe, const Vector3 &p_offset) { + + ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND(!reflection_probe); + + reflection_probe->origin_offset = p_offset; + reflection_probe->instance_dependency.instance_notify_changed(true, false); +} + +void RasterizerStorageRD::reflection_probe_set_as_interior(RID p_probe, bool p_enable) { + + ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND(!reflection_probe); + + reflection_probe->interior = p_enable; + reflection_probe->instance_dependency.instance_notify_changed(true, false); +} +void RasterizerStorageRD::reflection_probe_set_enable_box_projection(RID p_probe, bool p_enable) { + + ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND(!reflection_probe); + + reflection_probe->box_projection = p_enable; +} + +void RasterizerStorageRD::reflection_probe_set_enable_shadows(RID p_probe, bool p_enable) { + + ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND(!reflection_probe); + + reflection_probe->enable_shadows = p_enable; + reflection_probe->instance_dependency.instance_notify_changed(true, false); +} +void RasterizerStorageRD::reflection_probe_set_cull_mask(RID p_probe, uint32_t p_layers) { + + ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND(!reflection_probe); + + reflection_probe->cull_mask = p_layers; + reflection_probe->instance_dependency.instance_notify_changed(true, false); +} + +void RasterizerStorageRD::reflection_probe_set_resolution(RID p_probe, int p_resolution) { + + ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND(!reflection_probe); + ERR_FAIL_COND(p_resolution < 32); + + reflection_probe->resolution = p_resolution; +} + +AABB RasterizerStorageRD::reflection_probe_get_aabb(RID p_probe) const { + const ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND_V(!reflection_probe, AABB()); + + AABB aabb; + aabb.position = -reflection_probe->extents; + aabb.size = reflection_probe->extents * 2.0; + + return aabb; +} +RS::ReflectionProbeUpdateMode RasterizerStorageRD::reflection_probe_get_update_mode(RID p_probe) const { + + const ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND_V(!reflection_probe, RS::REFLECTION_PROBE_UPDATE_ALWAYS); + + return reflection_probe->update_mode; +} + +uint32_t RasterizerStorageRD::reflection_probe_get_cull_mask(RID p_probe) const { + + const ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND_V(!reflection_probe, 0); + + return reflection_probe->cull_mask; +} + +Vector3 RasterizerStorageRD::reflection_probe_get_extents(RID p_probe) const { + + const ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND_V(!reflection_probe, Vector3()); + + return reflection_probe->extents; +} +Vector3 RasterizerStorageRD::reflection_probe_get_origin_offset(RID p_probe) const { + + const ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND_V(!reflection_probe, Vector3()); + + return reflection_probe->origin_offset; +} + +bool RasterizerStorageRD::reflection_probe_renders_shadows(RID p_probe) const { + + const ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND_V(!reflection_probe, false); + + return reflection_probe->enable_shadows; +} + +float RasterizerStorageRD::reflection_probe_get_origin_max_distance(RID p_probe) const { + + const ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND_V(!reflection_probe, 0); + + return reflection_probe->max_distance; +} + +int RasterizerStorageRD::reflection_probe_get_resolution(RID p_probe) const { + + const ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND_V(!reflection_probe, 0); + + return reflection_probe->resolution; +} + +float RasterizerStorageRD::reflection_probe_get_intensity(RID p_probe) const { + + const ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND_V(!reflection_probe, 0); + + return reflection_probe->intensity; +} +bool RasterizerStorageRD::reflection_probe_is_interior(RID p_probe) const { + + const ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND_V(!reflection_probe, false); + + return reflection_probe->interior; +} +bool RasterizerStorageRD::reflection_probe_is_box_projection(RID p_probe) const { + + const ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND_V(!reflection_probe, false); + + return reflection_probe->box_projection; +} + +Color RasterizerStorageRD::reflection_probe_get_interior_ambient(RID p_probe) const { + + const ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND_V(!reflection_probe, Color()); + + return reflection_probe->interior_ambient; +} +float RasterizerStorageRD::reflection_probe_get_interior_ambient_energy(RID p_probe) const { + + const ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND_V(!reflection_probe, 0); + + return reflection_probe->interior_ambient_energy; +} +float RasterizerStorageRD::reflection_probe_get_interior_ambient_probe_contribution(RID p_probe) const { + + const ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND_V(!reflection_probe, 0); + + return reflection_probe->interior_ambient_probe_contrib; +} + +RID RasterizerStorageRD::gi_probe_create() { + + return gi_probe_owner.make_rid(GIProbe()); +} + +void RasterizerStorageRD::gi_probe_allocate(RID p_gi_probe, const Transform &p_to_cell_xform, const AABB &p_aabb, const Vector3i &p_octree_size, const Vector<uint8_t> &p_octree_cells, const Vector<uint8_t> &p_data_cells, const Vector<uint8_t> &p_distance_field, const Vector<int> &p_level_counts) { + GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe); + ERR_FAIL_COND(!gi_probe); + + if (gi_probe->octree_buffer.is_valid()) { + RD::get_singleton()->free(gi_probe->octree_buffer); + RD::get_singleton()->free(gi_probe->data_buffer); + if (gi_probe->sdf_texture.is_valid()) { + RD::get_singleton()->free(gi_probe->sdf_texture); + } + + gi_probe->sdf_texture = RID(); + gi_probe->octree_buffer = RID(); + gi_probe->data_buffer = RID(); + gi_probe->octree_buffer_size = 0; + gi_probe->data_buffer_size = 0; + gi_probe->cell_count = 0; + } + + gi_probe->to_cell_xform = p_to_cell_xform; + gi_probe->bounds = p_aabb; + gi_probe->octree_size = p_octree_size; + gi_probe->level_counts = p_level_counts; + + if (p_octree_cells.size()) { + ERR_FAIL_COND(p_octree_cells.size() % 32 != 0); //cells size must be a multiple of 32 + + uint32_t cell_count = p_octree_cells.size() / 32; + + ERR_FAIL_COND(p_data_cells.size() != (int)cell_count * 16); //see that data size matches + + gi_probe->cell_count = cell_count; + gi_probe->octree_buffer = RD::get_singleton()->storage_buffer_create(p_octree_cells.size(), p_octree_cells); + gi_probe->octree_buffer_size = p_octree_cells.size(); + gi_probe->data_buffer = RD::get_singleton()->storage_buffer_create(p_data_cells.size(), p_data_cells); + gi_probe->data_buffer_size = p_data_cells.size(); + + if (p_distance_field.size()) { + RD::TextureFormat tf; + tf.format = RD::DATA_FORMAT_R8_UNORM; + tf.width = gi_probe->octree_size.x; + tf.height = gi_probe->octree_size.y; + tf.depth = gi_probe->octree_size.z; + tf.type = RD::TEXTURE_TYPE_3D; + tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT; + Vector<Vector<uint8_t>> s; + s.push_back(p_distance_field); + gi_probe->sdf_texture = RD::get_singleton()->texture_create(tf, RD::TextureView(), s); + } +#if 0 + { + RD::TextureFormat tf; + tf.format = RD::DATA_FORMAT_R8_UNORM; + tf.width = gi_probe->octree_size.x; + tf.height = gi_probe->octree_size.y; + tf.depth = gi_probe->octree_size.z; + tf.type = RD::TEXTURE_TYPE_3D; + tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT; + tf.shareable_formats.push_back(RD::DATA_FORMAT_R8_UNORM); + tf.shareable_formats.push_back(RD::DATA_FORMAT_R8_UINT); + gi_probe->sdf_texture = RD::get_singleton()->texture_create(tf, RD::TextureView()); + } + RID shared_tex; + { + + RD::TextureView tv; + tv.format_override = RD::DATA_FORMAT_R8_UINT; + shared_tex = RD::get_singleton()->texture_create_shared(tv, gi_probe->sdf_texture); + } + //update SDF texture + Vector<RD::Uniform> uniforms; + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER; + u.binding = 1; + u.ids.push_back(gi_probe->octree_buffer); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER; + u.binding = 2; + u.ids.push_back(gi_probe->data_buffer); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 3; + u.ids.push_back(shared_tex); + uniforms.push_back(u); + } + + RID uniform_set = RD::get_singleton()->uniform_set_create(uniforms, giprobe_sdf_shader_version_shader, 0); + + { + uint32_t push_constant[4] = { 0, 0, 0, 0 }; + + for (int i = 0; i < gi_probe->level_counts.size() - 1; i++) { + push_constant[0] += gi_probe->level_counts[i]; + } + push_constant[1] = push_constant[0] + gi_probe->level_counts[gi_probe->level_counts.size() - 1]; + + print_line("offset: " + itos(push_constant[0])); + print_line("size: " + itos(push_constant[1])); + //create SDF + RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, giprobe_sdf_shader_pipeline); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set, 0); + RD::get_singleton()->compute_list_set_push_constant(compute_list, push_constant, sizeof(uint32_t) * 4); + RD::get_singleton()->compute_list_dispatch(compute_list, gi_probe->octree_size.x / 4, gi_probe->octree_size.y / 4, gi_probe->octree_size.z / 4); + RD::get_singleton()->compute_list_end(); + } + + RD::get_singleton()->free(uniform_set); + RD::get_singleton()->free(shared_tex); + } +#endif + } + + gi_probe->version++; + gi_probe->data_version++; + + gi_probe->instance_dependency.instance_notify_changed(true, false); +} + +AABB RasterizerStorageRD::gi_probe_get_bounds(RID p_gi_probe) const { + GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe); + ERR_FAIL_COND_V(!gi_probe, AABB()); + + return gi_probe->bounds; +} + +Vector3i RasterizerStorageRD::gi_probe_get_octree_size(RID p_gi_probe) const { + GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe); + ERR_FAIL_COND_V(!gi_probe, Vector3i()); + return gi_probe->octree_size; +} +Vector<uint8_t> RasterizerStorageRD::gi_probe_get_octree_cells(RID p_gi_probe) const { + GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe); + ERR_FAIL_COND_V(!gi_probe, Vector<uint8_t>()); + + if (gi_probe->octree_buffer.is_valid()) { + return RD::get_singleton()->buffer_get_data(gi_probe->octree_buffer); + } + return Vector<uint8_t>(); +} +Vector<uint8_t> RasterizerStorageRD::gi_probe_get_data_cells(RID p_gi_probe) const { + GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe); + ERR_FAIL_COND_V(!gi_probe, Vector<uint8_t>()); + + if (gi_probe->data_buffer.is_valid()) { + return RD::get_singleton()->buffer_get_data(gi_probe->data_buffer); + } + return Vector<uint8_t>(); +} +Vector<uint8_t> RasterizerStorageRD::gi_probe_get_distance_field(RID p_gi_probe) const { + GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe); + ERR_FAIL_COND_V(!gi_probe, Vector<uint8_t>()); + + if (gi_probe->data_buffer.is_valid()) { + return RD::get_singleton()->texture_get_data(gi_probe->sdf_texture, 0); + } + return Vector<uint8_t>(); +} +Vector<int> RasterizerStorageRD::gi_probe_get_level_counts(RID p_gi_probe) const { + GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe); + ERR_FAIL_COND_V(!gi_probe, Vector<int>()); + + return gi_probe->level_counts; +} +Transform RasterizerStorageRD::gi_probe_get_to_cell_xform(RID p_gi_probe) const { + GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe); + ERR_FAIL_COND_V(!gi_probe, Transform()); + + return gi_probe->to_cell_xform; +} + +void RasterizerStorageRD::gi_probe_set_dynamic_range(RID p_gi_probe, float p_range) { + GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe); + ERR_FAIL_COND(!gi_probe); + + gi_probe->dynamic_range = p_range; + gi_probe->version++; +} +float RasterizerStorageRD::gi_probe_get_dynamic_range(RID p_gi_probe) const { + GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe); + ERR_FAIL_COND_V(!gi_probe, 0); + + return gi_probe->dynamic_range; +} + +void RasterizerStorageRD::gi_probe_set_propagation(RID p_gi_probe, float p_range) { + GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe); + ERR_FAIL_COND(!gi_probe); + + gi_probe->propagation = p_range; + gi_probe->version++; +} +float RasterizerStorageRD::gi_probe_get_propagation(RID p_gi_probe) const { + GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe); + ERR_FAIL_COND_V(!gi_probe, 0); + return gi_probe->propagation; +} + +void RasterizerStorageRD::gi_probe_set_energy(RID p_gi_probe, float p_energy) { + GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe); + ERR_FAIL_COND(!gi_probe); + + gi_probe->energy = p_energy; +} +float RasterizerStorageRD::gi_probe_get_energy(RID p_gi_probe) const { + GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe); + ERR_FAIL_COND_V(!gi_probe, 0); + return gi_probe->energy; +} + +void RasterizerStorageRD::gi_probe_set_ao(RID p_gi_probe, float p_ao) { + GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe); + ERR_FAIL_COND(!gi_probe); + + gi_probe->ao = p_ao; +} +float RasterizerStorageRD::gi_probe_get_ao(RID p_gi_probe) const { + GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe); + ERR_FAIL_COND_V(!gi_probe, 0); + return gi_probe->ao; +} + +void RasterizerStorageRD::gi_probe_set_ao_size(RID p_gi_probe, float p_strength) { + + GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe); + ERR_FAIL_COND(!gi_probe); + + gi_probe->ao_size = p_strength; +} + +float RasterizerStorageRD::gi_probe_get_ao_size(RID p_gi_probe) const { + GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe); + ERR_FAIL_COND_V(!gi_probe, 0); + return gi_probe->ao_size; +} + +void RasterizerStorageRD::gi_probe_set_bias(RID p_gi_probe, float p_bias) { + GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe); + ERR_FAIL_COND(!gi_probe); + + gi_probe->bias = p_bias; +} +float RasterizerStorageRD::gi_probe_get_bias(RID p_gi_probe) const { + GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe); + ERR_FAIL_COND_V(!gi_probe, 0); + return gi_probe->bias; +} + +void RasterizerStorageRD::gi_probe_set_normal_bias(RID p_gi_probe, float p_normal_bias) { + GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe); + ERR_FAIL_COND(!gi_probe); + + gi_probe->normal_bias = p_normal_bias; +} +float RasterizerStorageRD::gi_probe_get_normal_bias(RID p_gi_probe) const { + GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe); + ERR_FAIL_COND_V(!gi_probe, 0); + return gi_probe->normal_bias; +} + +void RasterizerStorageRD::gi_probe_set_anisotropy_strength(RID p_gi_probe, float p_strength) { + + GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe); + ERR_FAIL_COND(!gi_probe); + + gi_probe->anisotropy_strength = p_strength; +} + +float RasterizerStorageRD::gi_probe_get_anisotropy_strength(RID p_gi_probe) const { + GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe); + ERR_FAIL_COND_V(!gi_probe, 0); + return gi_probe->anisotropy_strength; +} + +void RasterizerStorageRD::gi_probe_set_interior(RID p_gi_probe, bool p_enable) { + GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe); + ERR_FAIL_COND(!gi_probe); + + gi_probe->interior = p_enable; +} + +void RasterizerStorageRD::gi_probe_set_use_two_bounces(RID p_gi_probe, bool p_enable) { + GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe); + ERR_FAIL_COND(!gi_probe); + + gi_probe->use_two_bounces = p_enable; + gi_probe->version++; +} + +bool RasterizerStorageRD::gi_probe_is_using_two_bounces(RID p_gi_probe) const { + GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe); + ERR_FAIL_COND_V(!gi_probe, false); + return gi_probe->use_two_bounces; +} + +bool RasterizerStorageRD::gi_probe_is_interior(RID p_gi_probe) const { + GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe); + ERR_FAIL_COND_V(!gi_probe, 0); + return gi_probe->interior; +} + +uint32_t RasterizerStorageRD::gi_probe_get_version(RID p_gi_probe) { + GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe); + ERR_FAIL_COND_V(!gi_probe, 0); + return gi_probe->version; +} + +uint32_t RasterizerStorageRD::gi_probe_get_data_version(RID p_gi_probe) { + GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe); + ERR_FAIL_COND_V(!gi_probe, 0); + return gi_probe->data_version; +} + +RID RasterizerStorageRD::gi_probe_get_octree_buffer(RID p_gi_probe) const { + + GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe); + ERR_FAIL_COND_V(!gi_probe, RID()); + return gi_probe->octree_buffer; +} +RID RasterizerStorageRD::gi_probe_get_data_buffer(RID p_gi_probe) const { + + GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe); + ERR_FAIL_COND_V(!gi_probe, RID()); + return gi_probe->data_buffer; +} + +RID RasterizerStorageRD::gi_probe_get_sdf_texture(RID p_gi_probe) { + GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe); + ERR_FAIL_COND_V(!gi_probe, RID()); + + return gi_probe->sdf_texture; +} + +/* RENDER TARGET API */ + +void RasterizerStorageRD::_clear_render_target(RenderTarget *rt) { + + //free in reverse dependency order + if (rt->framebuffer.is_valid()) { + RD::get_singleton()->free(rt->framebuffer); + } + + if (rt->color.is_valid()) { + RD::get_singleton()->free(rt->color); + } + + if (rt->backbuffer.is_valid()) { + RD::get_singleton()->free(rt->backbuffer); + rt->backbuffer = RID(); + rt->backbuffer_fb = RID(); + for (int i = 0; i < rt->backbuffer_mipmaps.size(); i++) { + //just erase copies, since the rest are erased by dependency + RD::get_singleton()->free(rt->backbuffer_mipmaps[i].mipmap_copy); + } + rt->backbuffer_mipmaps.clear(); + if (rt->backbuffer_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(rt->backbuffer_uniform_set)) { + RD::get_singleton()->free(rt->backbuffer_uniform_set); + } + rt->backbuffer_uniform_set = RID(); + } + + rt->framebuffer = RID(); + rt->color = RID(); +} + +void RasterizerStorageRD::_update_render_target(RenderTarget *rt) { + + if (rt->texture.is_null()) { + //create a placeholder until updated + rt->texture = texture_2d_placeholder_create(); + Texture *tex = texture_owner.getornull(rt->texture); + tex->is_render_target = true; + } + + _clear_render_target(rt); + + if (rt->size.width == 0 || rt->size.height == 0) { + return; + } + //until we implement support for HDR monitors (and render target is attached to screen), this is enough. + rt->color_format = RD::DATA_FORMAT_R8G8B8A8_UNORM; + rt->color_format_srgb = RD::DATA_FORMAT_R8G8B8A8_SRGB; + rt->image_format = rt->flags[RENDER_TARGET_TRANSPARENT] ? Image::FORMAT_RGBA8 : Image::FORMAT_RGB8; + + RD::TextureFormat rd_format; + RD::TextureView rd_view; + { //attempt register + rd_format.format = rt->color_format; + rd_format.width = rt->size.width; + rd_format.height = rt->size.height; + rd_format.depth = 1; + rd_format.array_layers = 1; + rd_format.mipmaps = 1; + rd_format.type = RD::TEXTURE_TYPE_2D; + rd_format.samples = RD::TEXTURE_SAMPLES_1; + rd_format.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT; + rd_format.shareable_formats.push_back(rt->color_format); + rd_format.shareable_formats.push_back(rt->color_format_srgb); + } + + rt->color = RD::get_singleton()->texture_create(rd_format, rd_view); + ERR_FAIL_COND(rt->color.is_null()); + + Vector<RID> fb_textures; + fb_textures.push_back(rt->color); + rt->framebuffer = RD::get_singleton()->framebuffer_create(fb_textures); + if (rt->framebuffer.is_null()) { + _clear_render_target(rt); + ERR_FAIL_COND(rt->framebuffer.is_null()); + } + + { //update texture + + Texture *tex = texture_owner.getornull(rt->texture); + + //free existing textures + if (RD::get_singleton()->texture_is_valid(tex->rd_texture)) { + RD::get_singleton()->free(tex->rd_texture); + } + if (RD::get_singleton()->texture_is_valid(tex->rd_texture_srgb)) { + RD::get_singleton()->free(tex->rd_texture_srgb); + } + + tex->rd_texture = RID(); + tex->rd_texture_srgb = RID(); + + //create shared textures to the color buffer, + //so transparent can be supported + RD::TextureView view; + view.format_override = rt->color_format; + if (!rt->flags[RENDER_TARGET_TRANSPARENT]) { + view.swizzle_a = RD::TEXTURE_SWIZZLE_ONE; + } + tex->rd_texture = RD::get_singleton()->texture_create_shared(view, rt->color); + if (rt->color_format_srgb != RD::DATA_FORMAT_MAX) { + view.format_override = rt->color_format_srgb; + tex->rd_texture_srgb = RD::get_singleton()->texture_create_shared(view, rt->color); + } + tex->rd_view = view; + tex->width = rt->size.width; + tex->height = rt->size.height; + tex->width_2d = rt->size.width; + tex->height_2d = rt->size.height; + tex->rd_format = rt->color_format; + tex->rd_format_srgb = rt->color_format_srgb; + tex->format = rt->image_format; + + Vector<RID> proxies = tex->proxies; //make a copy, since update may change it + for (int i = 0; i < proxies.size(); i++) { + texture_proxy_update(proxies[i], rt->texture); + } + } +} + +void RasterizerStorageRD::_create_render_target_backbuffer(RenderTarget *rt) { + ERR_FAIL_COND(rt->backbuffer.is_valid()); + + uint32_t mipmaps_required = Image::get_image_required_mipmaps(rt->size.width, rt->size.height, Image::FORMAT_RGBA8); + RD::TextureFormat tf; + tf.format = rt->color_format; + tf.width = rt->size.width; + tf.height = rt->size.height; + tf.type = RD::TEXTURE_TYPE_2D; + tf.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT; + tf.mipmaps = mipmaps_required; + + rt->backbuffer = RD::get_singleton()->texture_create(tf, RD::TextureView()); + + { + Vector<RID> backbuffer_att; + RID backbuffer_fb_tex = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), rt->backbuffer, 0, 0); + backbuffer_att.push_back(backbuffer_fb_tex); + rt->backbuffer_fb = RD::get_singleton()->framebuffer_create(backbuffer_att); + } + + //create mipmaps + for (uint32_t i = 1; i < mipmaps_required; i++) { + + RenderTarget::BackbufferMipmap mm; + { + mm.mipmap = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), rt->backbuffer, 0, i); + Vector<RID> mm_fb_at; + mm_fb_at.push_back(mm.mipmap); + mm.mipmap_fb = RD::get_singleton()->framebuffer_create(mm_fb_at); + } + + { + Size2 mm_size = Image::get_image_mipmap_size(tf.width, tf.height, Image::FORMAT_RGBA8, i); + + RD::TextureFormat mmtf = tf; + mmtf.width = mm_size.width; + mmtf.height = mm_size.height; + mmtf.mipmaps = 1; + + mm.mipmap_copy = RD::get_singleton()->texture_create(mmtf, RD::TextureView()); + Vector<RID> mm_fb_at; + mm_fb_at.push_back(mm.mipmap_copy); + mm.mipmap_copy_fb = RD::get_singleton()->framebuffer_create(mm_fb_at); + } + + rt->backbuffer_mipmaps.push_back(mm); + } +} + +RID RasterizerStorageRD::render_target_create() { + RenderTarget render_target; + + render_target.was_used = false; + render_target.clear_requested = false; + + for (int i = 0; i < RENDER_TARGET_FLAG_MAX; i++) { + render_target.flags[i] = false; + } + _update_render_target(&render_target); + return render_target_owner.make_rid(render_target); +} + +void RasterizerStorageRD::render_target_set_position(RID p_render_target, int p_x, int p_y) { + //unused for this render target +} + +void RasterizerStorageRD::render_target_set_size(RID p_render_target, int p_width, int p_height) { + RenderTarget *rt = render_target_owner.getornull(p_render_target); + ERR_FAIL_COND(!rt); + rt->size.x = p_width; + rt->size.y = p_height; + _update_render_target(rt); +} + +RID RasterizerStorageRD::render_target_get_texture(RID p_render_target) { + RenderTarget *rt = render_target_owner.getornull(p_render_target); + ERR_FAIL_COND_V(!rt, RID()); + + return rt->texture; +} + +void RasterizerStorageRD::render_target_set_external_texture(RID p_render_target, unsigned int p_texture_id) { +} + +void RasterizerStorageRD::render_target_set_flag(RID p_render_target, RenderTargetFlags p_flag, bool p_value) { + RenderTarget *rt = render_target_owner.getornull(p_render_target); + ERR_FAIL_COND(!rt); + rt->flags[p_flag] = p_value; + _update_render_target(rt); +} + +bool RasterizerStorageRD::render_target_was_used(RID p_render_target) { + + RenderTarget *rt = render_target_owner.getornull(p_render_target); + ERR_FAIL_COND_V(!rt, false); + return rt->was_used; +} + +void RasterizerStorageRD::render_target_set_as_unused(RID p_render_target) { + + RenderTarget *rt = render_target_owner.getornull(p_render_target); + ERR_FAIL_COND(!rt); + rt->was_used = false; +} + +Size2 RasterizerStorageRD::render_target_get_size(RID p_render_target) { + RenderTarget *rt = render_target_owner.getornull(p_render_target); + ERR_FAIL_COND_V(!rt, Size2()); + + return rt->size; +} + +RID RasterizerStorageRD::render_target_get_rd_framebuffer(RID p_render_target) { + RenderTarget *rt = render_target_owner.getornull(p_render_target); + ERR_FAIL_COND_V(!rt, RID()); + + return rt->framebuffer; +} + +void RasterizerStorageRD::render_target_request_clear(RID p_render_target, const Color &p_clear_color) { + RenderTarget *rt = render_target_owner.getornull(p_render_target); + ERR_FAIL_COND(!rt); + rt->clear_requested = true; + rt->clear_color = p_clear_color; +} + +bool RasterizerStorageRD::render_target_is_clear_requested(RID p_render_target) { + RenderTarget *rt = render_target_owner.getornull(p_render_target); + ERR_FAIL_COND_V(!rt, false); + return rt->clear_requested; +} + +Color RasterizerStorageRD::render_target_get_clear_request_color(RID p_render_target) { + + RenderTarget *rt = render_target_owner.getornull(p_render_target); + ERR_FAIL_COND_V(!rt, Color()); + return rt->clear_color; +} + +void RasterizerStorageRD::render_target_disable_clear_request(RID p_render_target) { + + RenderTarget *rt = render_target_owner.getornull(p_render_target); + ERR_FAIL_COND(!rt); + rt->clear_requested = false; +} + +void RasterizerStorageRD::render_target_do_clear_request(RID p_render_target) { + + RenderTarget *rt = render_target_owner.getornull(p_render_target); + ERR_FAIL_COND(!rt); + if (!rt->clear_requested) { + return; + } + Vector<Color> clear_colors; + clear_colors.push_back(rt->clear_color); + RD::get_singleton()->draw_list_begin(rt->framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD, clear_colors); + RD::get_singleton()->draw_list_end(); + rt->clear_requested = false; +} + +void RasterizerStorageRD::render_target_copy_to_back_buffer(RID p_render_target, const Rect2i &p_region) { + RenderTarget *rt = render_target_owner.getornull(p_render_target); + ERR_FAIL_COND(!rt); + if (!rt->backbuffer.is_valid()) { + _create_render_target_backbuffer(rt); + } + + Rect2i region = p_region; + Rect2 blur_region; + if (region == Rect2i()) { + region.size = rt->size; + } else { + blur_region = region; + blur_region.position /= rt->size; + blur_region.size /= rt->size; + } + + //single texture copy for backbuffer + RD::get_singleton()->texture_copy(rt->color, rt->backbuffer, Vector3(region.position.x, region.position.y, 0), Vector3(region.position.x, region.position.y, 0), Vector3(region.size.x, region.size.y, 1), 0, 0, 0, 0, true); + //effects.copy(rt->color, rt->backbuffer_fb, blur_region); + + //then mipmap blur + RID prev_texture = rt->color; //use color, not backbuffer, as bb has mipmaps. + Vector2 pixel_size = Vector2(1.0 / rt->size.width, 1.0 / rt->size.height); + + for (int i = 0; i < rt->backbuffer_mipmaps.size(); i++) { + pixel_size *= 2.0; //go halfway + const RenderTarget::BackbufferMipmap &mm = rt->backbuffer_mipmaps[i]; + effects.gaussian_blur(prev_texture, mm.mipmap_copy_fb, mm.mipmap_copy, mm.mipmap_fb, pixel_size, blur_region); + prev_texture = mm.mipmap; + } +} + +RID RasterizerStorageRD::render_target_get_back_buffer_uniform_set(RID p_render_target, RID p_base_shader) { + RenderTarget *rt = render_target_owner.getornull(p_render_target); + ERR_FAIL_COND_V(!rt, RID()); + + if (!rt->backbuffer.is_valid()) { + _create_render_target_backbuffer(rt); + } + + if (rt->backbuffer_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(rt->backbuffer_uniform_set)) { + return rt->backbuffer_uniform_set; //if still valid, return/reuse it. + } + + //create otherwise + Vector<RD::Uniform> uniforms; + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_TEXTURE; + u.binding = 0; + u.ids.push_back(rt->backbuffer); + uniforms.push_back(u); + + rt->backbuffer_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, p_base_shader, 3); + ERR_FAIL_COND_V(!rt->backbuffer_uniform_set.is_valid(), RID()); + + return rt->backbuffer_uniform_set; +} + +void RasterizerStorageRD::base_update_dependency(RID p_base, RasterizerScene::InstanceBase *p_instance) { + if (mesh_owner.owns(p_base)) { + Mesh *mesh = mesh_owner.getornull(p_base); + p_instance->update_dependency(&mesh->instance_dependency); + } else if (multimesh_owner.owns(p_base)) { + + MultiMesh *multimesh = multimesh_owner.getornull(p_base); + p_instance->update_dependency(&multimesh->instance_dependency); + if (multimesh->mesh.is_valid()) { + base_update_dependency(multimesh->mesh, p_instance); + } + } else if (reflection_probe_owner.owns(p_base)) { + ReflectionProbe *rp = reflection_probe_owner.getornull(p_base); + p_instance->update_dependency(&rp->instance_dependency); + } else if (gi_probe_owner.owns(p_base)) { + GIProbe *gip = gi_probe_owner.getornull(p_base); + p_instance->update_dependency(&gip->instance_dependency); + } else if (light_owner.owns(p_base)) { + Light *l = light_owner.getornull(p_base); + p_instance->update_dependency(&l->instance_dependency); + } +} + +void RasterizerStorageRD::skeleton_update_dependency(RID p_skeleton, RasterizerScene::InstanceBase *p_instance) { + + Skeleton *skeleton = skeleton_owner.getornull(p_skeleton); + ERR_FAIL_COND(!skeleton); + + p_instance->update_dependency(&skeleton->instance_dependency); +} + +RS::InstanceType RasterizerStorageRD::get_base_type(RID p_rid) const { + + if (mesh_owner.owns(p_rid)) { + return RS::INSTANCE_MESH; + } + if (multimesh_owner.owns(p_rid)) { + return RS::INSTANCE_MULTIMESH; + } + if (reflection_probe_owner.owns(p_rid)) { + return RS::INSTANCE_REFLECTION_PROBE; + } + if (gi_probe_owner.owns(p_rid)) { + return RS::INSTANCE_GI_PROBE; + } + if (light_owner.owns(p_rid)) { + return RS::INSTANCE_LIGHT; + } + + return RS::INSTANCE_NONE; +} +void RasterizerStorageRD::update_dirty_resources() { + _update_queued_materials(); + _update_dirty_multimeshes(); + _update_dirty_skeletons(); +} + +bool RasterizerStorageRD::has_os_feature(const String &p_feature) const { + + if (p_feature == "rgtc" && RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_BC5_UNORM_BLOCK, RD::TEXTURE_USAGE_SAMPLING_BIT)) { + return true; + } + + if (p_feature == "s3tc" && RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_BC1_RGB_UNORM_BLOCK, RD::TEXTURE_USAGE_SAMPLING_BIT)) { + return true; + } + + if (p_feature == "bptc" && RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_BC7_UNORM_BLOCK, RD::TEXTURE_USAGE_SAMPLING_BIT)) { + return true; + } + + if ((p_feature == "etc" || p_feature == "etc2") && RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_ETC2_R8G8B8_UNORM_BLOCK, RD::TEXTURE_USAGE_SAMPLING_BIT)) { + return true; + } + + if (p_feature == "pvrtc" && RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG, RD::TEXTURE_USAGE_SAMPLING_BIT)) { + return true; + } + + return false; +} +bool RasterizerStorageRD::free(RID p_rid) { + + if (texture_owner.owns(p_rid)) { + Texture *t = texture_owner.getornull(p_rid); + + ERR_FAIL_COND_V(t->is_render_target, false); + + if (RD::get_singleton()->texture_is_valid(t->rd_texture_srgb)) { + //erase this first, as it's a dependency of the one below + RD::get_singleton()->free(t->rd_texture_srgb); + } + if (RD::get_singleton()->texture_is_valid(t->rd_texture)) { + RD::get_singleton()->free(t->rd_texture); + } + + if (t->is_proxy && t->proxy_to.is_valid()) { + Texture *proxy_to = texture_owner.getornull(t->proxy_to); + if (proxy_to) { + proxy_to->proxies.erase(p_rid); + } + } + + for (int i = 0; i < t->proxies.size(); i++) { + Texture *p = texture_owner.getornull(t->proxies[i]); + ERR_CONTINUE(!p); + p->proxy_to = RID(); + p->rd_texture = RID(); + p->rd_texture_srgb = RID(); + } + texture_owner.free(p_rid); + + } else if (shader_owner.owns(p_rid)) { + Shader *shader = shader_owner.getornull(p_rid); + //make material unreference this + while (shader->owners.size()) { + material_set_shader(shader->owners.front()->get()->self, RID()); + } + //clear data if exists + if (shader->data) { + memdelete(shader->data); + } + shader_owner.free(p_rid); + + } else if (material_owner.owns(p_rid)) { + Material *material = material_owner.getornull(p_rid); + if (material->update_requested) { + _update_queued_materials(); + } + material_set_shader(p_rid, RID()); //clean up shader + material->instance_dependency.instance_notify_deleted(p_rid); + material_owner.free(p_rid); + } else if (mesh_owner.owns(p_rid)) { + mesh_clear(p_rid); + Mesh *mesh = mesh_owner.getornull(p_rid); + mesh->instance_dependency.instance_notify_deleted(p_rid); + mesh_owner.free(p_rid); + } else if (multimesh_owner.owns(p_rid)) { + _update_dirty_multimeshes(); + multimesh_allocate(p_rid, 0, RS::MULTIMESH_TRANSFORM_2D); + MultiMesh *multimesh = multimesh_owner.getornull(p_rid); + multimesh->instance_dependency.instance_notify_deleted(p_rid); + multimesh_owner.free(p_rid); + } else if (skeleton_owner.owns(p_rid)) { + _update_dirty_skeletons(); + skeleton_allocate(p_rid, 0); + Skeleton *skeleton = skeleton_owner.getornull(p_rid); + skeleton->instance_dependency.instance_notify_deleted(p_rid); + skeleton_owner.free(p_rid); + } else if (reflection_probe_owner.owns(p_rid)) { + ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_rid); + reflection_probe->instance_dependency.instance_notify_deleted(p_rid); + reflection_probe_owner.free(p_rid); + } else if (gi_probe_owner.owns(p_rid)) { + gi_probe_allocate(p_rid, Transform(), AABB(), Vector3i(), Vector<uint8_t>(), Vector<uint8_t>(), Vector<uint8_t>(), Vector<int>()); //deallocate + GIProbe *gi_probe = gi_probe_owner.getornull(p_rid); + gi_probe->instance_dependency.instance_notify_deleted(p_rid); + gi_probe_owner.free(p_rid); + + } else if (light_owner.owns(p_rid)) { + + // delete the texture + Light *light = light_owner.getornull(p_rid); + light->instance_dependency.instance_notify_deleted(p_rid); + light_owner.free(p_rid); + + } else if (render_target_owner.owns(p_rid)) { + RenderTarget *rt = render_target_owner.getornull(p_rid); + + _clear_render_target(rt); + + if (rt->texture.is_valid()) { + Texture *tex = texture_owner.getornull(rt->texture); + tex->is_render_target = false; + free(rt->texture); + } + + render_target_owner.free(p_rid); + } else { + return false; + } + + return true; +} + +RasterizerEffectsRD *RasterizerStorageRD::get_effects() { + return &effects; +} + +void RasterizerStorageRD::capture_timestamps_begin() { + RD::get_singleton()->capture_timestamp("Frame Begin", false); +} + +void RasterizerStorageRD::capture_timestamp(const String &p_name) { + RD::get_singleton()->capture_timestamp(p_name, true); +} + +uint32_t RasterizerStorageRD::get_captured_timestamps_count() const { + return RD::get_singleton()->get_captured_timestamps_count(); +} +uint64_t RasterizerStorageRD::get_captured_timestamps_frame() const { + return RD::get_singleton()->get_captured_timestamps_frame(); +} + +uint64_t RasterizerStorageRD::get_captured_timestamp_gpu_time(uint32_t p_index) const { + return RD::get_singleton()->get_captured_timestamp_gpu_time(p_index); +} +uint64_t RasterizerStorageRD::get_captured_timestamp_cpu_time(uint32_t p_index) const { + return RD::get_singleton()->get_captured_timestamp_cpu_time(p_index); +} +String RasterizerStorageRD::get_captured_timestamp_name(uint32_t p_index) const { + return RD::get_singleton()->get_captured_timestamp_name(p_index); +} + +RasterizerStorageRD::RasterizerStorageRD() { + + for (int i = 0; i < SHADER_TYPE_MAX; i++) { + shader_data_request_func[i] = NULL; + } + + material_update_list = NULL; + { //create default textures + + RD::TextureFormat tformat; + tformat.format = RD::DATA_FORMAT_R8G8B8A8_UNORM; + tformat.width = 4; + tformat.height = 4; + tformat.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT; + tformat.type = RD::TEXTURE_TYPE_2D; + + Vector<uint8_t> pv; + pv.resize(16 * 4); + for (int i = 0; i < 16; i++) { + pv.set(i * 4 + 0, 255); + pv.set(i * 4 + 1, 255); + pv.set(i * 4 + 2, 255); + pv.set(i * 4 + 3, 255); + } + + { + Vector<Vector<uint8_t>> vpv; + vpv.push_back(pv); + default_rd_textures[DEFAULT_RD_TEXTURE_WHITE] = RD::get_singleton()->texture_create(tformat, RD::TextureView(), vpv); + } + + for (int i = 0; i < 16; i++) { + pv.set(i * 4 + 0, 0); + pv.set(i * 4 + 1, 0); + pv.set(i * 4 + 2, 0); + pv.set(i * 4 + 3, 255); + } + + { + Vector<Vector<uint8_t>> vpv; + vpv.push_back(pv); + default_rd_textures[DEFAULT_RD_TEXTURE_BLACK] = RD::get_singleton()->texture_create(tformat, RD::TextureView(), vpv); + } + + for (int i = 0; i < 16; i++) { + pv.set(i * 4 + 0, 128); + pv.set(i * 4 + 1, 128); + pv.set(i * 4 + 2, 255); + pv.set(i * 4 + 3, 255); + } + + { + Vector<Vector<uint8_t>> vpv; + vpv.push_back(pv); + default_rd_textures[DEFAULT_RD_TEXTURE_NORMAL] = RD::get_singleton()->texture_create(tformat, RD::TextureView(), vpv); + } + + for (int i = 0; i < 16; i++) { + pv.set(i * 4 + 0, 255); + pv.set(i * 4 + 1, 128); + pv.set(i * 4 + 2, 255); + pv.set(i * 4 + 3, 255); + } + + { + Vector<Vector<uint8_t>> vpv; + vpv.push_back(pv); + default_rd_textures[DEFAULT_RD_TEXTURE_ANISO] = RD::get_singleton()->texture_create(tformat, RD::TextureView(), vpv); + } + + for (int i = 0; i < 16; i++) { + pv.set(i * 4 + 0, 0); + pv.set(i * 4 + 1, 0); + pv.set(i * 4 + 2, 0); + pv.set(i * 4 + 3, 0); + } + + default_rd_textures[DEFAULT_RD_TEXTURE_MULTIMESH_BUFFER] = RD::get_singleton()->texture_buffer_create(16, RD::DATA_FORMAT_R8G8B8A8_UNORM, pv); + } + + { //create default cubemap + + RD::TextureFormat tformat; + tformat.format = RD::DATA_FORMAT_R8G8B8A8_UNORM; + tformat.width = 4; + tformat.height = 4; + tformat.array_layers = 6; + tformat.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT; + tformat.type = RD::TEXTURE_TYPE_CUBE_ARRAY; + + Vector<uint8_t> pv; + pv.resize(16 * 4); + for (int i = 0; i < 16; i++) { + pv.set(i * 4 + 0, 0); + pv.set(i * 4 + 1, 0); + pv.set(i * 4 + 2, 0); + pv.set(i * 4 + 3, 0); + } + + { + Vector<Vector<uint8_t>> vpv; + for (int i = 0; i < 6; i++) { + vpv.push_back(pv); + } + default_rd_textures[DEFAULT_RD_TEXTURE_CUBEMAP_ARRAY_BLACK] = RD::get_singleton()->texture_create(tformat, RD::TextureView(), vpv); + } + } + + { //create default cubemap array + + RD::TextureFormat tformat; + tformat.format = RD::DATA_FORMAT_R8G8B8A8_UNORM; + tformat.width = 4; + tformat.height = 4; + tformat.array_layers = 6; + tformat.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT; + tformat.type = RD::TEXTURE_TYPE_CUBE; + + Vector<uint8_t> pv; + pv.resize(16 * 4); + for (int i = 0; i < 16; i++) { + pv.set(i * 4 + 0, 0); + pv.set(i * 4 + 1, 0); + pv.set(i * 4 + 2, 0); + pv.set(i * 4 + 3, 0); + } + + { + Vector<Vector<uint8_t>> vpv; + for (int i = 0; i < 6; i++) { + vpv.push_back(pv); + } + default_rd_textures[DEFAULT_RD_TEXTURE_CUBEMAP_BLACK] = RD::get_singleton()->texture_create(tformat, RD::TextureView(), vpv); + } + } + + { //create default 3D + + RD::TextureFormat tformat; + tformat.format = RD::DATA_FORMAT_R8G8B8A8_UNORM; + tformat.width = 4; + tformat.height = 4; + tformat.depth = 4; + tformat.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT; + tformat.type = RD::TEXTURE_TYPE_3D; + + Vector<uint8_t> pv; + pv.resize(64 * 4); + for (int i = 0; i < 64; i++) { + pv.set(i * 4 + 0, 0); + pv.set(i * 4 + 1, 0); + pv.set(i * 4 + 2, 0); + pv.set(i * 4 + 3, 0); + } + + { + Vector<Vector<uint8_t>> vpv; + vpv.push_back(pv); + default_rd_textures[DEFAULT_RD_TEXTURE_3D_WHITE] = RD::get_singleton()->texture_create(tformat, RD::TextureView(), vpv); + } + } + + //default samplers + for (int i = 1; i < RS::CANVAS_ITEM_TEXTURE_FILTER_MAX; i++) { + for (int j = 1; j < RS::CANVAS_ITEM_TEXTURE_REPEAT_MAX; j++) { + RD::SamplerState sampler_state; + switch (i) { + case RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST: { + sampler_state.mag_filter = RD::SAMPLER_FILTER_NEAREST; + sampler_state.min_filter = RD::SAMPLER_FILTER_NEAREST; + sampler_state.max_lod = 0; + } break; + case RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR: { + + sampler_state.mag_filter = RD::SAMPLER_FILTER_LINEAR; + sampler_state.min_filter = RD::SAMPLER_FILTER_LINEAR; + sampler_state.max_lod = 0; + } break; + case RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS: { + sampler_state.mag_filter = RD::SAMPLER_FILTER_NEAREST; + sampler_state.min_filter = RD::SAMPLER_FILTER_LINEAR; + sampler_state.mip_filter = RD::SAMPLER_FILTER_LINEAR; + } break; + case RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS: { + sampler_state.mag_filter = RD::SAMPLER_FILTER_LINEAR; + sampler_state.min_filter = RD::SAMPLER_FILTER_LINEAR; + sampler_state.mip_filter = RD::SAMPLER_FILTER_LINEAR; + + } break; + case RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC: { + sampler_state.mag_filter = RD::SAMPLER_FILTER_NEAREST; + sampler_state.min_filter = RD::SAMPLER_FILTER_LINEAR; + sampler_state.mip_filter = RD::SAMPLER_FILTER_LINEAR; + sampler_state.use_anisotropy = true; + sampler_state.anisotropy_max = GLOBAL_GET("rendering/quality/filters/max_anisotropy"); + } break; + case RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC: { + sampler_state.mag_filter = RD::SAMPLER_FILTER_LINEAR; + sampler_state.min_filter = RD::SAMPLER_FILTER_LINEAR; + sampler_state.mip_filter = RD::SAMPLER_FILTER_LINEAR; + sampler_state.use_anisotropy = true; + sampler_state.anisotropy_max = GLOBAL_GET("rendering/quality/filters/max_anisotropy"); + + } break; + default: { + } + } + switch (j) { + case RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED: { + + sampler_state.repeat_u = RD::SAMPLER_REPEAT_MODE_CLAMP_TO_EDGE; + sampler_state.repeat_v = RD::SAMPLER_REPEAT_MODE_CLAMP_TO_EDGE; + + } break; + case RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED: { + sampler_state.repeat_u = RD::SAMPLER_REPEAT_MODE_REPEAT; + sampler_state.repeat_v = RD::SAMPLER_REPEAT_MODE_REPEAT; + } break; + case RS::CANVAS_ITEM_TEXTURE_REPEAT_MIRROR: { + sampler_state.repeat_u = RD::SAMPLER_REPEAT_MODE_MIRRORED_REPEAT; + sampler_state.repeat_v = RD::SAMPLER_REPEAT_MODE_MIRRORED_REPEAT; + } break; + default: { + } + } + + default_rd_samplers[i][j] = RD::get_singleton()->sampler_create(sampler_state); + } + } + + //default rd buffers + { + + { //vertex + + Vector<uint8_t> buffer; + buffer.resize(sizeof(float) * 3); + { + uint8_t *w = buffer.ptrw(); + float *fptr = (float *)w; + fptr[0] = 0.0; + fptr[1] = 0.0; + fptr[2] = 0.0; + } + mesh_default_rd_buffers[DEFAULT_RD_BUFFER_VERTEX] = RD::get_singleton()->vertex_buffer_create(buffer.size(), buffer); +} + +{ //normal + Vector<uint8_t> buffer; + buffer.resize(sizeof(float) * 3); + { + uint8_t *w = buffer.ptrw(); + float *fptr = (float *)w; + fptr[0] = 1.0; + fptr[1] = 0.0; + fptr[2] = 0.0; + } + mesh_default_rd_buffers[DEFAULT_RD_BUFFER_NORMAL] = RD::get_singleton()->vertex_buffer_create(buffer.size(), buffer); +} + +{ //tangent + Vector<uint8_t> buffer; + buffer.resize(sizeof(float) * 4); + { + uint8_t *w = buffer.ptrw(); + float *fptr = (float *)w; + fptr[0] = 1.0; + fptr[1] = 0.0; + fptr[2] = 0.0; + fptr[3] = 0.0; + } + mesh_default_rd_buffers[DEFAULT_RD_BUFFER_TANGENT] = RD::get_singleton()->vertex_buffer_create(buffer.size(), buffer); +} + +{ //color + Vector<uint8_t> buffer; + buffer.resize(sizeof(float) * 4); + { + uint8_t *w = buffer.ptrw(); + float *fptr = (float *)w; + fptr[0] = 1.0; + fptr[1] = 1.0; + fptr[2] = 1.0; + fptr[3] = 1.0; + } + mesh_default_rd_buffers[DEFAULT_RD_BUFFER_COLOR] = RD::get_singleton()->vertex_buffer_create(buffer.size(), buffer); +} + +{ //tex uv 1 + Vector<uint8_t> buffer; + buffer.resize(sizeof(float) * 2); + { + uint8_t *w = buffer.ptrw(); + float *fptr = (float *)w; + fptr[0] = 0.0; + fptr[1] = 0.0; + } + mesh_default_rd_buffers[DEFAULT_RD_BUFFER_TEX_UV] = RD::get_singleton()->vertex_buffer_create(buffer.size(), buffer); +} +{ //tex uv 2 + Vector<uint8_t> buffer; + buffer.resize(sizeof(float) * 2); + { + uint8_t *w = buffer.ptrw(); + float *fptr = (float *)w; + fptr[0] = 0.0; + fptr[1] = 0.0; + } + mesh_default_rd_buffers[DEFAULT_RD_BUFFER_TEX_UV2] = RD::get_singleton()->vertex_buffer_create(buffer.size(), buffer); +} + +{ //bones + Vector<uint8_t> buffer; + buffer.resize(sizeof(uint32_t) * 4); + { + uint8_t *w = buffer.ptrw(); + uint32_t *fptr = (uint32_t *)w; + fptr[0] = 0; + fptr[1] = 0; + fptr[2] = 0; + fptr[3] = 0; + } + mesh_default_rd_buffers[DEFAULT_RD_BUFFER_BONES] = RD::get_singleton()->vertex_buffer_create(buffer.size(), buffer); +} + +{ //weights + Vector<uint8_t> buffer; + buffer.resize(sizeof(float) * 4); + { + uint8_t *w = buffer.ptrw(); + float *fptr = (float *)w; + fptr[0] = 0.0; + fptr[1] = 0.0; + fptr[2] = 0.0; + fptr[3] = 0.0; + } + mesh_default_rd_buffers[DEFAULT_RD_BUFFER_WEIGHTS] = RD::get_singleton()->vertex_buffer_create(buffer.size(), buffer); +} +} + +{ + Vector<String> sdf_versions; + sdf_versions.push_back(""); //one only + giprobe_sdf_shader.initialize(sdf_versions); + giprobe_sdf_shader_version = giprobe_sdf_shader.version_create(); + giprobe_sdf_shader.version_set_compute_code(giprobe_sdf_shader_version, "", "", "", Vector<String>()); + giprobe_sdf_shader_version_shader = giprobe_sdf_shader.version_get_shader(giprobe_sdf_shader_version, 0); + giprobe_sdf_shader_pipeline = RD::get_singleton()->compute_pipeline_create(giprobe_sdf_shader_version_shader); +} +} + +RasterizerStorageRD::~RasterizerStorageRD() { + + //def textures + for (int i = 0; i < DEFAULT_RD_TEXTURE_MAX; i++) { + RD::get_singleton()->free(default_rd_textures[i]); + } + + //def samplers + for (int i = 1; i < RS::CANVAS_ITEM_TEXTURE_FILTER_MAX; i++) { + for (int j = 1; j < RS::CANVAS_ITEM_TEXTURE_REPEAT_MAX; j++) { + RD::get_singleton()->free(default_rd_samplers[i][j]); + } + } + + //def buffers + for (int i = 0; i < DEFAULT_RD_BUFFER_MAX; i++) { + RD::get_singleton()->free(mesh_default_rd_buffers[i]); + } + giprobe_sdf_shader.version_free(giprobe_sdf_shader_version); +} diff --git a/servers/rendering/rasterizer_rd/rasterizer_storage_rd.h b/servers/rendering/rasterizer_rd/rasterizer_storage_rd.h new file mode 100644 index 0000000000..e69be644d7 --- /dev/null +++ b/servers/rendering/rasterizer_rd/rasterizer_storage_rd.h @@ -0,0 +1,1135 @@ +/*************************************************************************/ +/* rasterizer_storage_rd.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef RASTERIZER_STORAGE_RD_H +#define RASTERIZER_STORAGE_RD_H + +#include "core/rid_owner.h" +#include "servers/rendering/rasterizer.h" +#include "servers/rendering/rasterizer_rd/rasterizer_effects_rd.h" +#include "servers/rendering/rasterizer_rd/shader_compiler_rd.h" +#include "servers/rendering/rasterizer_rd/shaders/giprobe_sdf.glsl.gen.h" +#include "servers/rendering/rendering_device.h" + +class RasterizerStorageRD : public RasterizerStorage { +public: + enum ShaderType { + SHADER_TYPE_2D, + SHADER_TYPE_3D, + SHADER_TYPE_PARTICLES, + SHADER_TYPE_SKY, + SHADER_TYPE_MAX + }; + + struct ShaderData { + virtual void set_code(const String &p_Code) = 0; + virtual void set_default_texture_param(const StringName &p_name, RID p_texture) = 0; + virtual void get_param_list(List<PropertyInfo> *p_param_list) const = 0; + virtual bool is_param_texture(const StringName &p_param) const = 0; + virtual bool is_animated() const = 0; + virtual bool casts_shadows() const = 0; + virtual Variant get_default_parameter(const StringName &p_parameter) const = 0; + virtual ~ShaderData() {} + }; + + typedef ShaderData *(*ShaderDataRequestFunction)(); + + struct MaterialData { + + void update_uniform_buffer(const Map<StringName, ShaderLanguage::ShaderNode::Uniform> &p_uniforms, const uint32_t *p_uniform_offsets, const Map<StringName, Variant> &p_parameters, uint8_t *p_buffer, uint32_t p_buffer_size, bool p_use_linear_color); + void update_textures(const Map<StringName, Variant> &p_parameters, const Map<StringName, RID> &p_default_textures, const Vector<ShaderCompilerRD::GeneratedCode::Texture> &p_texture_uniforms, RID *p_textures, bool p_use_linear_color); + + virtual void set_render_priority(int p_priority) = 0; + virtual void set_next_pass(RID p_pass) = 0; + virtual void update_parameters(const Map<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty) = 0; + virtual ~MaterialData() {} + }; + typedef MaterialData *(*MaterialDataRequestFunction)(ShaderData *); + + enum DefaultRDTexture { + DEFAULT_RD_TEXTURE_WHITE, + DEFAULT_RD_TEXTURE_BLACK, + DEFAULT_RD_TEXTURE_NORMAL, + DEFAULT_RD_TEXTURE_ANISO, + DEFAULT_RD_TEXTURE_MULTIMESH_BUFFER, + DEFAULT_RD_TEXTURE_CUBEMAP_BLACK, + DEFAULT_RD_TEXTURE_CUBEMAP_ARRAY_BLACK, + DEFAULT_RD_TEXTURE_3D_WHITE, + DEFAULT_RD_TEXTURE_MAX + }; + + enum DefaultRDBuffer { + DEFAULT_RD_BUFFER_VERTEX, + DEFAULT_RD_BUFFER_NORMAL, + DEFAULT_RD_BUFFER_TANGENT, + DEFAULT_RD_BUFFER_COLOR, + DEFAULT_RD_BUFFER_TEX_UV, + DEFAULT_RD_BUFFER_TEX_UV2, + DEFAULT_RD_BUFFER_BONES, + DEFAULT_RD_BUFFER_WEIGHTS, + DEFAULT_RD_BUFFER_MAX, + }; + +private: + /* TEXTURE API */ + struct Texture { + + enum Type { + TYPE_2D, + TYPE_LAYERED, + TYPE_3D + }; + + Type type; + + RenderingDevice::TextureType rd_type; + RID rd_texture; + RID rd_texture_srgb; + RenderingDevice::DataFormat rd_format; + RenderingDevice::DataFormat rd_format_srgb; + + RD::TextureView rd_view; + + Image::Format format; + Image::Format validated_format; + + int width; + int height; + int depth; + int layers; + int mipmaps; + + int height_2d; + int width_2d; + + bool is_render_target; + bool is_proxy; + + Ref<Image> image_cache_2d; + String path; + + RID proxy_to; + Vector<RID> proxies; + + RS::TextureDetectCallback detect_3d_callback = nullptr; + void *detect_3d_callback_ud = nullptr; + + RS::TextureDetectCallback detect_normal_callback = nullptr; + void *detect_normal_callback_ud = nullptr; + + RS::TextureDetectRoughnessCallback detect_roughness_callback = nullptr; + void *detect_roughness_callback_ud = nullptr; + }; + + struct TextureToRDFormat { + RD::DataFormat format; + RD::DataFormat format_srgb; + RD::TextureSwizzle swizzle_r; + RD::TextureSwizzle swizzle_g; + RD::TextureSwizzle swizzle_b; + RD::TextureSwizzle swizzle_a; + TextureToRDFormat() { + format = RD::DATA_FORMAT_MAX; + format_srgb = RD::DATA_FORMAT_MAX; + swizzle_r = RD::TEXTURE_SWIZZLE_R; + swizzle_g = RD::TEXTURE_SWIZZLE_G; + swizzle_b = RD::TEXTURE_SWIZZLE_B; + swizzle_a = RD::TEXTURE_SWIZZLE_A; + } + }; + + //textures can be created from threads, so this RID_Owner is thread safe + mutable RID_Owner<Texture, true> texture_owner; + + Ref<Image> _validate_texture_format(const Ref<Image> &p_image, TextureToRDFormat &r_format); + + RID default_rd_textures[DEFAULT_RD_TEXTURE_MAX]; + RID default_rd_samplers[RS::CANVAS_ITEM_TEXTURE_FILTER_MAX][RS::CANVAS_ITEM_TEXTURE_REPEAT_MAX]; + + /* SHADER */ + + struct Material; + + struct Shader { + ShaderData *data; + String code; + ShaderType type; + Map<StringName, RID> default_texture_parameter; + Set<Material *> owners; + }; + + ShaderDataRequestFunction shader_data_request_func[SHADER_TYPE_MAX]; + mutable RID_Owner<Shader> shader_owner; + + /* Material */ + + struct Material { + RID self; + MaterialData *data; + Shader *shader; + //shortcut to shader data and type + ShaderType shader_type; + bool update_requested; + bool uniform_dirty; + bool texture_dirty; + Material *update_next; + Map<StringName, Variant> params; + int32_t priority; + RID next_pass; + RasterizerScene::InstanceDependency instance_dependency; + }; + + MaterialDataRequestFunction material_data_request_func[SHADER_TYPE_MAX]; + mutable RID_Owner<Material> material_owner; + + Material *material_update_list; + void _material_queue_update(Material *material, bool p_uniform, bool p_texture); + void _update_queued_materials(); + + /* Mesh */ + + struct Mesh { + + struct Surface { + RS::PrimitiveType primitive; + uint32_t format = 0; + + RID vertex_buffer; + uint32_t vertex_count = 0; + + // A different pipeline needs to be allocated + // depending on the inputs available in the + // material. + // There are never that many geometry/material + // combinations, so a simple array is the most + // cache-efficient structure. + + struct Version { + uint32_t input_mask; + RD::VertexFormatID vertex_format; + RID vertex_array; + }; + + SpinLock version_lock; //needed to access versions + Version *versions = nullptr; //allocated on demand + uint32_t version_count = 0; + + RID index_buffer; + RID index_array; + uint32_t index_count = 0; + + struct LOD { + float edge_length; + RID index_buffer; + RID index_array; + }; + + LOD *lods = nullptr; + uint32_t lod_count = 0; + + AABB aabb; + + Vector<AABB> bone_aabbs; + + Vector<RID> blend_shapes; + RID blend_shape_base_buffer; //source buffer goes here when using blend shapes, and main one is uncompressed + + RID material; + + uint32_t render_index = 0; + uint64_t render_pass = 0; + + uint32_t multimesh_render_index = 0; + uint64_t multimesh_render_pass = 0; + }; + + uint32_t blend_shape_count = 0; + RS::BlendShapeMode blend_shape_mode = RS::BLEND_SHAPE_MODE_NORMALIZED; + + Surface **surfaces = nullptr; + uint32_t surface_count = 0; + + Vector<AABB> bone_aabbs; + + AABB aabb; + AABB custom_aabb; + + Vector<RID> material_cache; + + RasterizerScene::InstanceDependency instance_dependency; + }; + + mutable RID_Owner<Mesh> mesh_owner; + + void _mesh_surface_generate_version_for_input_mask(Mesh::Surface *s, uint32_t p_input_mask); + + RID mesh_default_rd_buffers[DEFAULT_RD_BUFFER_MAX]; + + /* MultiMesh */ + struct MultiMesh { + RID mesh; + int instances = 0; + RS::MultimeshTransformFormat xform_format = RS::MULTIMESH_TRANSFORM_3D; + bool uses_colors = false; + bool uses_custom_data = false; + int visible_instances = -1; + AABB aabb; + bool aabb_dirty = false; + bool buffer_set = false; + uint32_t stride_cache = 0; + uint32_t color_offset_cache = 0; + uint32_t custom_data_offset_cache = 0; + + Vector<float> data_cache; //used if individual setting is used + bool *data_cache_dirty_regions = nullptr; + uint32_t data_cache_used_dirty_regions = 0; + + RID buffer; //storage buffer + RID uniform_set_3d; + + bool dirty = false; + MultiMesh *dirty_list = nullptr; + + RasterizerScene::InstanceDependency instance_dependency; + }; + + mutable RID_Owner<MultiMesh> multimesh_owner; + + MultiMesh *multimesh_dirty_list = nullptr; + + _FORCE_INLINE_ void _multimesh_make_local(MultiMesh *multimesh) const; + _FORCE_INLINE_ void _multimesh_mark_dirty(MultiMesh *multimesh, int p_index, bool p_aabb); + _FORCE_INLINE_ void _multimesh_mark_all_dirty(MultiMesh *multimesh, bool p_data, bool p_aabb); + _FORCE_INLINE_ void _multimesh_re_create_aabb(MultiMesh *multimesh, const float *p_data, int p_instances); + void _update_dirty_multimeshes(); + + /* Skeleton */ + + struct Skeleton { + bool use_2d = false; + int size = 0; + Vector<float> data; + RID buffer; + + bool dirty = false; + Skeleton *dirty_list = nullptr; + Transform2D base_transform_2d; + + RID uniform_set_3d; + + RasterizerScene::InstanceDependency instance_dependency; + }; + + mutable RID_Owner<Skeleton> skeleton_owner; + + _FORCE_INLINE_ void _skeleton_make_dirty(Skeleton *skeleton); + + Skeleton *skeleton_dirty_list = nullptr; + + void _update_dirty_skeletons(); + + /* LIGHT */ + + struct Light { + + RS::LightType type; + float param[RS::LIGHT_PARAM_MAX]; + Color color = Color(1, 1, 1, 1); + Color shadow_color; + RID projector; + bool shadow = false; + bool negative = false; + bool reverse_cull = false; + bool use_gi = true; + uint32_t cull_mask = 0xFFFFFFFF; + RS::LightOmniShadowMode omni_shadow_mode = RS::LIGHT_OMNI_SHADOW_DUAL_PARABOLOID; + RS::LightDirectionalShadowMode directional_shadow_mode = RS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL; + RS::LightDirectionalShadowDepthRangeMode directional_range_mode = RS::LIGHT_DIRECTIONAL_SHADOW_DEPTH_RANGE_STABLE; + bool directional_blend_splits = false; + uint64_t version = 0; + + RasterizerScene::InstanceDependency instance_dependency; + }; + + mutable RID_Owner<Light> light_owner; + + /* REFLECTION PROBE */ + + struct ReflectionProbe { + + RS::ReflectionProbeUpdateMode update_mode = RS::REFLECTION_PROBE_UPDATE_ONCE; + int resolution = 256; + float intensity = 1.0; + Color interior_ambient; + float interior_ambient_energy = 1.0; + float interior_ambient_probe_contrib = 0.0; + float max_distance = 0; + Vector3 extents = Vector3(1, 1, 1); + Vector3 origin_offset; + bool interior = false; + bool box_projection = false; + bool enable_shadows = false; + uint32_t cull_mask = (1 << 20) - 1; + + RasterizerScene::InstanceDependency instance_dependency; + }; + + mutable RID_Owner<ReflectionProbe> reflection_probe_owner; + + /* GI PROBE */ + + struct GIProbe { + + RID octree_buffer; + RID data_buffer; + RID sdf_texture; + + uint32_t octree_buffer_size = 0; + uint32_t data_buffer_size = 0; + + Vector<int> level_counts; + + int cell_count = 0; + + Transform to_cell_xform; + AABB bounds; + Vector3i octree_size; + + float dynamic_range = 4.0; + float energy = 1.0; + float ao = 0.0; + float ao_size = 0.5; + float bias = 1.4; + float normal_bias = 0.0; + float propagation = 0.7; + bool interior = false; + bool use_two_bounces = false; + + float anisotropy_strength = 0.5; + + uint32_t version = 1; + uint32_t data_version = 1; + + RasterizerScene::InstanceDependency instance_dependency; + }; + + GiprobeSdfShaderRD giprobe_sdf_shader; + RID giprobe_sdf_shader_version; + RID giprobe_sdf_shader_version_shader; + RID giprobe_sdf_shader_pipeline; + + mutable RID_Owner<GIProbe> gi_probe_owner; + + /* RENDER TARGET */ + + struct RenderTarget { + + Size2i size; + RID framebuffer; + RID color; + + //used for retrieving from CPU + RD::DataFormat color_format; + RD::DataFormat color_format_srgb; + Image::Format image_format; + + bool flags[RENDER_TARGET_FLAG_MAX]; + + RID backbuffer; //used for effects + RID backbuffer_fb; + + struct BackbufferMipmap { + RID mipmap; + RID mipmap_fb; + RID mipmap_copy; + RID mipmap_copy_fb; + }; + + Vector<BackbufferMipmap> backbuffer_mipmaps; + RID backbuffer_uniform_set; + + //texture generated for this owner (nor RD). + RID texture; + bool was_used; + + //clear request + bool clear_requested; + Color clear_color; + }; + + RID_Owner<RenderTarget> render_target_owner; + + void _clear_render_target(RenderTarget *rt); + void _update_render_target(RenderTarget *rt); + void _create_render_target_backbuffer(RenderTarget *rt); + + /* EFFECTS */ + + RasterizerEffectsRD effects; + +public: + /* TEXTURE API */ + + virtual RID texture_2d_create(const Ref<Image> &p_image); + virtual RID texture_2d_layered_create(const Vector<Ref<Image>> &p_layers, RS::TextureLayeredType p_layered_type); + virtual RID texture_3d_create(const Vector<Ref<Image>> &p_slices); //all slices, then all the mipmaps, must be coherent + virtual RID texture_proxy_create(RID p_base); + + virtual void _texture_2d_update(RID p_texture, const Ref<Image> &p_image, int p_layer, bool p_immediate); + + virtual void texture_2d_update_immediate(RID p_texture, const Ref<Image> &p_image, int p_layer = 0); //mostly used for video and streaming + virtual void texture_2d_update(RID p_texture, const Ref<Image> &p_image, int p_layer = 0); + virtual void texture_3d_update(RID p_texture, const Ref<Image> &p_image, int p_depth, int p_mipmap); + virtual void texture_proxy_update(RID p_texture, RID p_proxy_to); + + //these two APIs can be used together or in combination with the others. + virtual RID texture_2d_placeholder_create(); + virtual RID texture_2d_layered_placeholder_create(); + virtual RID texture_3d_placeholder_create(); + + virtual Ref<Image> texture_2d_get(RID p_texture) const; + virtual Ref<Image> texture_2d_layer_get(RID p_texture, int p_layer) const; + virtual Ref<Image> texture_3d_slice_get(RID p_texture, int p_depth, int p_mipmap) const; + + virtual void texture_replace(RID p_texture, RID p_by_texture); + virtual void texture_set_size_override(RID p_texture, int p_width, int p_height); + + virtual void texture_set_path(RID p_texture, const String &p_path); + virtual String texture_get_path(RID p_texture) const; + + virtual void texture_set_detect_3d_callback(RID p_texture, RS::TextureDetectCallback p_callback, void *p_userdata); + virtual void texture_set_detect_normal_callback(RID p_texture, RS::TextureDetectCallback p_callback, void *p_userdata); + virtual void texture_set_detect_roughness_callback(RID p_texture, RS::TextureDetectRoughnessCallback p_callback, void *p_userdata); + + virtual void texture_debug_usage(List<RS::TextureInfo> *r_info); + + virtual void texture_set_proxy(RID p_proxy, RID p_base); + virtual void texture_set_force_redraw_if_visible(RID p_texture, bool p_enable); + + virtual Size2 texture_size_with_proxy(RID p_proxy); + + //internal usage + + _FORCE_INLINE_ RID texture_get_rd_texture(RID p_texture, bool p_srgb = false) { + if (p_texture.is_null()) { + return RID(); + } + Texture *tex = texture_owner.getornull(p_texture); + + if (!tex) { + return RID(); + } + return (p_srgb && tex->rd_texture_srgb.is_valid()) ? tex->rd_texture_srgb : tex->rd_texture; + } + + _FORCE_INLINE_ Size2i texture_2d_get_size(RID p_texture) { + if (p_texture.is_null()) { + return Size2i(); + } + Texture *tex = texture_owner.getornull(p_texture); + + if (!tex) { + return Size2i(); + } + return Size2i(tex->width_2d, tex->height_2d); + } + + _FORCE_INLINE_ RID texture_rd_get_default(DefaultRDTexture p_texture) { + return default_rd_textures[p_texture]; + } + _FORCE_INLINE_ RID sampler_rd_get_default(RS::CanvasItemTextureFilter p_filter, RS::CanvasItemTextureRepeat p_repeat) { + return default_rd_samplers[p_filter][p_repeat]; + } + + /* SHADER API */ + + RID shader_create(); + + void shader_set_code(RID p_shader, const String &p_code); + String shader_get_code(RID p_shader) const; + void shader_get_param_list(RID p_shader, List<PropertyInfo> *p_param_list) const; + + void shader_set_default_texture_param(RID p_shader, const StringName &p_name, RID p_texture); + RID shader_get_default_texture_param(RID p_shader, const StringName &p_name) const; + Variant shader_get_param_default(RID p_shader, const StringName &p_param) const; + void shader_set_data_request_function(ShaderType p_shader_type, ShaderDataRequestFunction p_function); + + /* COMMON MATERIAL API */ + + RID material_create(); + + void material_set_shader(RID p_material, RID p_shader); + + void material_set_param(RID p_material, const StringName &p_param, const Variant &p_value); + Variant material_get_param(RID p_material, const StringName &p_param) const; + + void material_set_next_pass(RID p_material, RID p_next_material); + void material_set_render_priority(RID p_material, int priority); + + bool material_is_animated(RID p_material); + bool material_casts_shadows(RID p_material); + + void material_update_dependency(RID p_material, RasterizerScene::InstanceBase *p_instance); + void material_force_update_textures(RID p_material, ShaderType p_shader_type); + + void material_set_data_request_function(ShaderType p_shader_type, MaterialDataRequestFunction p_function); + + _FORCE_INLINE_ MaterialData *material_get_data(RID p_material, ShaderType p_shader_type) { + Material *material = material_owner.getornull(p_material); + if (!material || material->shader_type != p_shader_type) { + return NULL; + } else { + return material->data; + } + } + + /* MESH API */ + + virtual RID mesh_create(); + + /// Return stride + virtual void mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface); + + virtual int mesh_get_blend_shape_count(RID p_mesh) const; + + virtual void mesh_set_blend_shape_mode(RID p_mesh, RS::BlendShapeMode p_mode); + virtual RS::BlendShapeMode mesh_get_blend_shape_mode(RID p_mesh) const; + + virtual void mesh_surface_update_region(RID p_mesh, int p_surface, int p_offset, const Vector<uint8_t> &p_data); + + virtual void mesh_surface_set_material(RID p_mesh, int p_surface, RID p_material); + virtual RID mesh_surface_get_material(RID p_mesh, int p_surface) const; + + virtual RS::SurfaceData mesh_get_surface(RID p_mesh, int p_surface) const; + + virtual int mesh_get_surface_count(RID p_mesh) const; + + virtual void mesh_set_custom_aabb(RID p_mesh, const AABB &p_aabb); + virtual AABB mesh_get_custom_aabb(RID p_mesh) const; + + virtual AABB mesh_get_aabb(RID p_mesh, RID p_skeleton = RID()); + + virtual void mesh_clear(RID p_mesh); + + _FORCE_INLINE_ const RID *mesh_get_surface_count_and_materials(RID p_mesh, uint32_t &r_surface_count) { + Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND_V(!mesh, NULL); + r_surface_count = mesh->surface_count; + if (r_surface_count == 0) { + return NULL; + } + if (mesh->material_cache.empty()) { + mesh->material_cache.resize(mesh->surface_count); + for (uint32_t i = 0; i < r_surface_count; i++) { + mesh->material_cache.write[i] = mesh->surfaces[i]->material; + } + } + + return mesh->material_cache.ptr(); + } + + _FORCE_INLINE_ RS::PrimitiveType mesh_surface_get_primitive(RID p_mesh, uint32_t p_surface_index) { + Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND_V(!mesh, RS::PRIMITIVE_MAX); + ERR_FAIL_UNSIGNED_INDEX_V(p_surface_index, mesh->surface_count, RS::PRIMITIVE_MAX); + + return mesh->surfaces[p_surface_index]->primitive; + } + + _FORCE_INLINE_ void mesh_surface_get_arrays_and_format(RID p_mesh, uint32_t p_surface_index, uint32_t p_input_mask, RID &r_vertex_array_rd, RID &r_index_array_rd, RD::VertexFormatID &r_vertex_format) { + Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND(!mesh); + ERR_FAIL_UNSIGNED_INDEX(p_surface_index, mesh->surface_count); + + Mesh::Surface *s = mesh->surfaces[p_surface_index]; + + r_index_array_rd = s->index_array; + + s->version_lock.lock(); + + //there will never be more than, at much, 3 or 4 versions, so iterating is the fastest way + + for (uint32_t i = 0; i < s->version_count; i++) { + if (s->versions[i].input_mask != p_input_mask) { + continue; + } + //we have this version, hooray + r_vertex_format = s->versions[i].vertex_format; + r_vertex_array_rd = s->versions[i].vertex_array; + s->version_lock.unlock(); + return; + } + + uint32_t version = s->version_count; //gets added at the end + + _mesh_surface_generate_version_for_input_mask(s, p_input_mask); + + r_vertex_format = s->versions[version].vertex_format; + r_vertex_array_rd = s->versions[version].vertex_array; + + s->version_lock.unlock(); + } + + _FORCE_INLINE_ RID mesh_get_default_rd_buffer(DefaultRDBuffer p_buffer) { + ERR_FAIL_INDEX_V(p_buffer, DEFAULT_RD_BUFFER_MAX, RID()); + return mesh_default_rd_buffers[p_buffer]; + } + + _FORCE_INLINE_ uint32_t mesh_surface_get_render_pass_index(RID p_mesh, uint32_t p_surface_index, uint64_t p_render_pass, uint32_t *r_index) { + Mesh *mesh = mesh_owner.getornull(p_mesh); + Mesh::Surface *s = mesh->surfaces[p_surface_index]; + + if (s->render_pass != p_render_pass) { + (*r_index)++; + s->render_pass = p_render_pass; + s->render_index = *r_index; + } + + return s->render_index; + } + + _FORCE_INLINE_ uint32_t mesh_surface_get_multimesh_render_pass_index(RID p_mesh, uint32_t p_surface_index, uint64_t p_render_pass, uint32_t *r_index) { + Mesh *mesh = mesh_owner.getornull(p_mesh); + Mesh::Surface *s = mesh->surfaces[p_surface_index]; + + if (s->multimesh_render_pass != p_render_pass) { + (*r_index)++; + s->multimesh_render_pass = p_render_pass; + s->multimesh_render_index = *r_index; + } + + return s->multimesh_render_index; + } + + /* MULTIMESH API */ + + RID multimesh_create(); + + void multimesh_allocate(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors = false, bool p_use_custom_data = false); + int multimesh_get_instance_count(RID p_multimesh) const; + + void multimesh_set_mesh(RID p_multimesh, RID p_mesh); + void multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform &p_transform); + void multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform); + void multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color); + void multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color); + + RID multimesh_get_mesh(RID p_multimesh) const; + + Transform multimesh_instance_get_transform(RID p_multimesh, int p_index) const; + Transform2D multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const; + Color multimesh_instance_get_color(RID p_multimesh, int p_index) const; + Color multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const; + + void multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer); + Vector<float> multimesh_get_buffer(RID p_multimesh) const; + + void multimesh_set_visible_instances(RID p_multimesh, int p_visible); + int multimesh_get_visible_instances(RID p_multimesh) const; + + AABB multimesh_get_aabb(RID p_multimesh) const; + + _FORCE_INLINE_ RS::MultimeshTransformFormat multimesh_get_transform_format(RID p_multimesh) const { + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + return multimesh->xform_format; + } + + _FORCE_INLINE_ bool multimesh_uses_colors(RID p_multimesh) const { + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + return multimesh->uses_colors; + } + + _FORCE_INLINE_ bool multimesh_uses_custom_data(RID p_multimesh) const { + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + return multimesh->uses_custom_data; + } + + _FORCE_INLINE_ uint32_t multimesh_get_instances_to_draw(RID p_multimesh) const { + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + if (multimesh->visible_instances >= 0) { + return multimesh->visible_instances; + } + return multimesh->instances; + } + + _FORCE_INLINE_ RID multimesh_get_3d_uniform_set(RID p_multimesh, RID p_shader, uint32_t p_set) const { + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + if (!multimesh->uniform_set_3d.is_valid()) { + Vector<RD::Uniform> uniforms; + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER; + u.binding = 0; + u.ids.push_back(multimesh->buffer); + uniforms.push_back(u); + multimesh->uniform_set_3d = RD::get_singleton()->uniform_set_create(uniforms, p_shader, p_set); + } + + return multimesh->uniform_set_3d; + } + + /* IMMEDIATE API */ + + RID immediate_create() { return RID(); } + void immediate_begin(RID p_immediate, RS::PrimitiveType p_rimitive, RID p_texture = RID()) {} + void immediate_vertex(RID p_immediate, const Vector3 &p_vertex) {} + void immediate_normal(RID p_immediate, const Vector3 &p_normal) {} + void immediate_tangent(RID p_immediate, const Plane &p_tangent) {} + void immediate_color(RID p_immediate, const Color &p_color) {} + void immediate_uv(RID p_immediate, const Vector2 &tex_uv) {} + void immediate_uv2(RID p_immediate, const Vector2 &tex_uv) {} + void immediate_end(RID p_immediate) {} + void immediate_clear(RID p_immediate) {} + void immediate_set_material(RID p_immediate, RID p_material) {} + RID immediate_get_material(RID p_immediate) const { return RID(); } + AABB immediate_get_aabb(RID p_immediate) const { return AABB(); } + + /* SKELETON API */ + + RID skeleton_create(); + void skeleton_allocate(RID p_skeleton, int p_bones, bool p_2d_skeleton = false); + void skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform); + void skeleton_set_world_transform(RID p_skeleton, bool p_enable, const Transform &p_world_transform); + int skeleton_get_bone_count(RID p_skeleton) const; + void skeleton_bone_set_transform(RID p_skeleton, int p_bone, const Transform &p_transform); + Transform skeleton_bone_get_transform(RID p_skeleton, int p_bone) const; + void skeleton_bone_set_transform_2d(RID p_skeleton, int p_bone, const Transform2D &p_transform); + Transform2D skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const; + + _FORCE_INLINE_ RID skeleton_get_3d_uniform_set(RID p_skeleton, RID p_shader, uint32_t p_set) const { + Skeleton *skeleton = skeleton_owner.getornull(p_skeleton); + ERR_FAIL_COND_V(!skeleton, RID()); + ERR_FAIL_COND_V(skeleton->size == 0, RID()); + if (skeleton->use_2d) { + return RID(); + } + if (!skeleton->uniform_set_3d.is_valid()) { + Vector<RD::Uniform> uniforms; + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER; + u.binding = 0; + u.ids.push_back(skeleton->buffer); + uniforms.push_back(u); + skeleton->uniform_set_3d = RD::get_singleton()->uniform_set_create(uniforms, p_shader, p_set); + } + + return skeleton->uniform_set_3d; + } + /* Light API */ + + RID light_create(RS::LightType p_type); + + RID directional_light_create() { return light_create(RS::LIGHT_DIRECTIONAL); } + RID omni_light_create() { return light_create(RS::LIGHT_OMNI); } + RID spot_light_create() { return light_create(RS::LIGHT_SPOT); } + + void light_set_color(RID p_light, const Color &p_color); + void light_set_param(RID p_light, RS::LightParam p_param, float p_value); + void light_set_shadow(RID p_light, bool p_enabled); + void light_set_shadow_color(RID p_light, const Color &p_color); + void light_set_projector(RID p_light, RID p_texture); + void light_set_negative(RID p_light, bool p_enable); + void light_set_cull_mask(RID p_light, uint32_t p_mask); + void light_set_reverse_cull_face_mode(RID p_light, bool p_enabled); + void light_set_use_gi(RID p_light, bool p_enabled); + + void light_omni_set_shadow_mode(RID p_light, RS::LightOmniShadowMode p_mode); + + void light_directional_set_shadow_mode(RID p_light, RS::LightDirectionalShadowMode p_mode); + void light_directional_set_blend_splits(RID p_light, bool p_enable); + bool light_directional_get_blend_splits(RID p_light) const; + void light_directional_set_shadow_depth_range_mode(RID p_light, RS::LightDirectionalShadowDepthRangeMode p_range_mode); + RS::LightDirectionalShadowDepthRangeMode light_directional_get_shadow_depth_range_mode(RID p_light) const; + + RS::LightDirectionalShadowMode light_directional_get_shadow_mode(RID p_light); + RS::LightOmniShadowMode light_omni_get_shadow_mode(RID p_light); + + _FORCE_INLINE_ RS::LightType light_get_type(RID p_light) const { + const Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND_V(!light, RS::LIGHT_DIRECTIONAL); + + return light->type; + } + AABB light_get_aabb(RID p_light) const; + + _FORCE_INLINE_ float light_get_param(RID p_light, RS::LightParam p_param) { + + const Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND_V(!light, 0); + + return light->param[p_param]; + } + + _FORCE_INLINE_ Color light_get_color(RID p_light) { + + const Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND_V(!light, Color()); + + return light->color; + } + + _FORCE_INLINE_ Color light_get_shadow_color(RID p_light) { + + const Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND_V(!light, Color()); + + return light->shadow_color; + } + + _FORCE_INLINE_ uint32_t light_get_cull_mask(RID p_light) { + + const Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND_V(!light, 0); + + return light->cull_mask; + } + + _FORCE_INLINE_ bool light_has_shadow(RID p_light) const { + + const Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND_V(!light, RS::LIGHT_DIRECTIONAL); + + return light->shadow; + } + + _FORCE_INLINE_ bool light_is_negative(RID p_light) const { + + const Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND_V(!light, RS::LIGHT_DIRECTIONAL); + + return light->negative; + } + + bool light_get_use_gi(RID p_light); + uint64_t light_get_version(RID p_light) const; + + /* PROBE API */ + + RID reflection_probe_create(); + + void reflection_probe_set_update_mode(RID p_probe, RS::ReflectionProbeUpdateMode p_mode); + void reflection_probe_set_intensity(RID p_probe, float p_intensity); + void reflection_probe_set_interior_ambient(RID p_probe, const Color &p_ambient); + void reflection_probe_set_interior_ambient_energy(RID p_probe, float p_energy); + void reflection_probe_set_interior_ambient_probe_contribution(RID p_probe, float p_contrib); + void reflection_probe_set_max_distance(RID p_probe, float p_distance); + void reflection_probe_set_extents(RID p_probe, const Vector3 &p_extents); + void reflection_probe_set_origin_offset(RID p_probe, const Vector3 &p_offset); + void reflection_probe_set_as_interior(RID p_probe, bool p_enable); + void reflection_probe_set_enable_box_projection(RID p_probe, bool p_enable); + void reflection_probe_set_enable_shadows(RID p_probe, bool p_enable); + void reflection_probe_set_cull_mask(RID p_probe, uint32_t p_layers); + void reflection_probe_set_resolution(RID p_probe, int p_resolution); + + AABB reflection_probe_get_aabb(RID p_probe) const; + RS::ReflectionProbeUpdateMode reflection_probe_get_update_mode(RID p_probe) const; + uint32_t reflection_probe_get_cull_mask(RID p_probe) const; + Vector3 reflection_probe_get_extents(RID p_probe) const; + Vector3 reflection_probe_get_origin_offset(RID p_probe) const; + float reflection_probe_get_origin_max_distance(RID p_probe) const; + int reflection_probe_get_resolution(RID p_probe) const; + bool reflection_probe_renders_shadows(RID p_probe) const; + + float reflection_probe_get_intensity(RID p_probe) const; + bool reflection_probe_is_interior(RID p_probe) const; + bool reflection_probe_is_box_projection(RID p_probe) const; + Color reflection_probe_get_interior_ambient(RID p_probe) const; + float reflection_probe_get_interior_ambient_energy(RID p_probe) const; + float reflection_probe_get_interior_ambient_probe_contribution(RID p_probe) const; + + void base_update_dependency(RID p_base, RasterizerScene::InstanceBase *p_instance); + void skeleton_update_dependency(RID p_skeleton, RasterizerScene::InstanceBase *p_instance); + + /* GI PROBE API */ + + RID gi_probe_create(); + + void gi_probe_allocate(RID p_gi_probe, const Transform &p_to_cell_xform, const AABB &p_aabb, const Vector3i &p_octree_size, const Vector<uint8_t> &p_octree_cells, const Vector<uint8_t> &p_data_cells, const Vector<uint8_t> &p_distance_field, const Vector<int> &p_level_counts); + + AABB gi_probe_get_bounds(RID p_gi_probe) const; + Vector3i gi_probe_get_octree_size(RID p_gi_probe) const; + Vector<uint8_t> gi_probe_get_octree_cells(RID p_gi_probe) const; + Vector<uint8_t> gi_probe_get_data_cells(RID p_gi_probe) const; + Vector<uint8_t> gi_probe_get_distance_field(RID p_gi_probe) const; + + Vector<int> gi_probe_get_level_counts(RID p_gi_probe) const; + Transform gi_probe_get_to_cell_xform(RID p_gi_probe) const; + + void gi_probe_set_dynamic_range(RID p_gi_probe, float p_range); + float gi_probe_get_dynamic_range(RID p_gi_probe) const; + + void gi_probe_set_propagation(RID p_gi_probe, float p_range); + float gi_probe_get_propagation(RID p_gi_probe) const; + + void gi_probe_set_energy(RID p_gi_probe, float p_energy); + float gi_probe_get_energy(RID p_gi_probe) const; + + void gi_probe_set_ao(RID p_gi_probe, float p_ao); + float gi_probe_get_ao(RID p_gi_probe) const; + + void gi_probe_set_ao_size(RID p_gi_probe, float p_strength); + float gi_probe_get_ao_size(RID p_gi_probe) const; + + void gi_probe_set_bias(RID p_gi_probe, float p_bias); + float gi_probe_get_bias(RID p_gi_probe) const; + + void gi_probe_set_normal_bias(RID p_gi_probe, float p_range); + float gi_probe_get_normal_bias(RID p_gi_probe) const; + + void gi_probe_set_interior(RID p_gi_probe, bool p_enable); + bool gi_probe_is_interior(RID p_gi_probe) const; + + void gi_probe_set_use_two_bounces(RID p_gi_probe, bool p_enable); + bool gi_probe_is_using_two_bounces(RID p_gi_probe) const; + + void gi_probe_set_anisotropy_strength(RID p_gi_probe, float p_strength); + float gi_probe_get_anisotropy_strength(RID p_gi_probe) const; + + uint32_t gi_probe_get_version(RID p_probe); + uint32_t gi_probe_get_data_version(RID p_probe); + + RID gi_probe_get_octree_buffer(RID p_gi_probe) const; + RID gi_probe_get_data_buffer(RID p_gi_probe) const; + + RID gi_probe_get_sdf_texture(RID p_gi_probe); + + /* LIGHTMAP CAPTURE */ + + void lightmap_capture_set_bounds(RID p_capture, const AABB &p_bounds) {} + AABB lightmap_capture_get_bounds(RID p_capture) const { return AABB(); } + void lightmap_capture_set_octree(RID p_capture, const Vector<uint8_t> &p_octree) {} + RID lightmap_capture_create() { + return RID(); + } + Vector<uint8_t> lightmap_capture_get_octree(RID p_capture) const { + return Vector<uint8_t>(); + } + void lightmap_capture_set_octree_cell_transform(RID p_capture, const Transform &p_xform) {} + Transform lightmap_capture_get_octree_cell_transform(RID p_capture) const { return Transform(); } + void lightmap_capture_set_octree_cell_subdiv(RID p_capture, int p_subdiv) {} + int lightmap_capture_get_octree_cell_subdiv(RID p_capture) const { return 0; } + void lightmap_capture_set_energy(RID p_capture, float p_energy) {} + float lightmap_capture_get_energy(RID p_capture) const { return 0.0; } + const Vector<LightmapCaptureOctree> *lightmap_capture_get_octree_ptr(RID p_capture) const { + return NULL; + } + + /* PARTICLES */ + + RID particles_create() { return RID(); } + + void particles_set_emitting(RID p_particles, bool p_emitting) {} + void particles_set_amount(RID p_particles, int p_amount) {} + void particles_set_lifetime(RID p_particles, float p_lifetime) {} + void particles_set_one_shot(RID p_particles, bool p_one_shot) {} + void particles_set_pre_process_time(RID p_particles, float p_time) {} + void particles_set_explosiveness_ratio(RID p_particles, float p_ratio) {} + void particles_set_randomness_ratio(RID p_particles, float p_ratio) {} + void particles_set_custom_aabb(RID p_particles, const AABB &p_aabb) {} + void particles_set_speed_scale(RID p_particles, float p_scale) {} + void particles_set_use_local_coordinates(RID p_particles, bool p_enable) {} + void particles_set_process_material(RID p_particles, RID p_material) {} + void particles_set_fixed_fps(RID p_particles, int p_fps) {} + void particles_set_fractional_delta(RID p_particles, bool p_enable) {} + void particles_restart(RID p_particles) {} + + void particles_set_draw_order(RID p_particles, RS::ParticlesDrawOrder p_order) {} + + void particles_set_draw_passes(RID p_particles, int p_count) {} + void particles_set_draw_pass_mesh(RID p_particles, int p_pass, RID p_mesh) {} + + void particles_request_process(RID p_particles) {} + AABB particles_get_current_aabb(RID p_particles) { return AABB(); } + AABB particles_get_aabb(RID p_particles) const { return AABB(); } + + void particles_set_emission_transform(RID p_particles, const Transform &p_transform) {} + + bool particles_get_emitting(RID p_particles) { return false; } + int particles_get_draw_passes(RID p_particles) const { return 0; } + RID particles_get_draw_pass_mesh(RID p_particles, int p_pass) const { return RID(); } + + virtual bool particles_is_inactive(RID p_particles) const { return false; } + + /* RENDER TARGET API */ + + RID render_target_create(); + void render_target_set_position(RID p_render_target, int p_x, int p_y); + void render_target_set_size(RID p_render_target, int p_width, int p_height); + RID render_target_get_texture(RID p_render_target); + void render_target_set_external_texture(RID p_render_target, unsigned int p_texture_id); + void render_target_set_flag(RID p_render_target, RenderTargetFlags p_flag, bool p_value); + bool render_target_was_used(RID p_render_target); + void render_target_set_as_unused(RID p_render_target); + void render_target_copy_to_back_buffer(RID p_render_target, const Rect2i &p_region); + RID render_target_get_back_buffer_uniform_set(RID p_render_target, RID p_base_shader); + + virtual void render_target_request_clear(RID p_render_target, const Color &p_clear_color); + virtual bool render_target_is_clear_requested(RID p_render_target); + virtual Color render_target_get_clear_request_color(RID p_render_target); + virtual void render_target_disable_clear_request(RID p_render_target); + virtual void render_target_do_clear_request(RID p_render_target); + + Size2 render_target_get_size(RID p_render_target); + RID render_target_get_rd_framebuffer(RID p_render_target); + + RS::InstanceType get_base_type(RID p_rid) const; + + bool free(RID p_rid); + + bool has_os_feature(const String &p_feature) const; + + void update_dirty_resources(); + + void set_debug_generate_wireframes(bool p_generate) {} + + void render_info_begin_capture() {} + void render_info_end_capture() {} + int get_captured_render_info(RS::RenderInfo p_info) { return 0; } + + int get_render_info(RS::RenderInfo p_info) { return 0; } + String get_video_adapter_name() const { return String(); } + String get_video_adapter_vendor() const { return String(); } + + virtual void capture_timestamps_begin(); + virtual void capture_timestamp(const String &p_name); + virtual uint32_t get_captured_timestamps_count() const; + virtual uint64_t get_captured_timestamps_frame() const; + virtual uint64_t get_captured_timestamp_gpu_time(uint32_t p_index) const; + virtual uint64_t get_captured_timestamp_cpu_time(uint32_t p_index) const; + virtual String get_captured_timestamp_name(uint32_t p_index) const; + + static RasterizerStorage *base_singleton; + + RasterizerEffectsRD *get_effects(); + + RasterizerStorageRD(); + ~RasterizerStorageRD(); +}; + +#endif // RASTERIZER_STORAGE_RD_H diff --git a/servers/rendering/rasterizer_rd/render_pipeline_vertex_format_cache_rd.cpp b/servers/rendering/rasterizer_rd/render_pipeline_vertex_format_cache_rd.cpp new file mode 100644 index 0000000000..4ee020aa69 --- /dev/null +++ b/servers/rendering/rasterizer_rd/render_pipeline_vertex_format_cache_rd.cpp @@ -0,0 +1,97 @@ +/*************************************************************************/ +/* render_pipeline_vertex_format_cache_rd.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "render_pipeline_vertex_format_cache_rd.h" +#include "core/os/memory.h" + +RID RenderPipelineVertexFormatCacheRD::_generate_version(RD::VertexFormatID p_vertex_format_id, RD::FramebufferFormatID p_framebuffer_format_id) { + + RD::PipelineMultisampleState multisample_state_version = multisample_state; + multisample_state_version.sample_count = RD::get_singleton()->framebuffer_format_get_texture_samples(p_framebuffer_format_id); + + RID pipeline = RD::get_singleton()->render_pipeline_create(shader, p_framebuffer_format_id, p_vertex_format_id, render_primitive, rasterization_state, multisample_state_version, depth_stencil_state, blend_state, dynamic_state_flags); + ERR_FAIL_COND_V(pipeline.is_null(), RID()); + versions = (Version *)memrealloc(versions, sizeof(Version) * (version_count + 1)); + versions[version_count].framebuffer_id = p_framebuffer_format_id; + versions[version_count].vertex_id = p_vertex_format_id; + versions[version_count].pipeline = pipeline; + version_count++; + return pipeline; +} + +void RenderPipelineVertexFormatCacheRD::_clear() { + + if (versions) { + for (uint32_t i = 0; i < version_count; i++) { + //shader may be gone, so this may not be valid + if (RD::get_singleton()->render_pipeline_is_valid(versions[i].pipeline)) { + RD::get_singleton()->free(versions[i].pipeline); + } + } + version_count = 0; + memfree(versions); + versions = NULL; + } +} + +void RenderPipelineVertexFormatCacheRD::setup(RID p_shader, RD::RenderPrimitive p_primitive, const RD::PipelineRasterizationState &p_rasterization_state, RD::PipelineMultisampleState p_multisample, const RD::PipelineDepthStencilState &p_depth_stencil_state, const RD::PipelineColorBlendState &p_blend_state, int p_dynamic_state_flags) { + ERR_FAIL_COND(p_shader.is_null()); + _clear(); + shader = p_shader; + input_mask = RD::get_singleton()->shader_get_vertex_input_attribute_mask(p_shader); + render_primitive = p_primitive; + rasterization_state = p_rasterization_state; + multisample_state = p_multisample; + depth_stencil_state = p_depth_stencil_state; + blend_state = p_blend_state; + dynamic_state_flags = p_dynamic_state_flags; +} + +void RenderPipelineVertexFormatCacheRD::update_shader(RID p_shader) { + ERR_FAIL_COND(p_shader.is_null()); + _clear(); + setup(p_shader, render_primitive, rasterization_state, multisample_state, depth_stencil_state, blend_state, dynamic_state_flags); +} + +void RenderPipelineVertexFormatCacheRD::clear() { + _clear(); + shader = RID(); //clear shader + input_mask = 0; +} + +RenderPipelineVertexFormatCacheRD::RenderPipelineVertexFormatCacheRD() { + version_count = 0; + versions = NULL; + input_mask = 0; +} + +RenderPipelineVertexFormatCacheRD::~RenderPipelineVertexFormatCacheRD() { + _clear(); +} diff --git a/servers/rendering/rasterizer_rd/render_pipeline_vertex_format_cache_rd.h b/servers/rendering/rasterizer_rd/render_pipeline_vertex_format_cache_rd.h new file mode 100644 index 0000000000..ecb1b42b06 --- /dev/null +++ b/servers/rendering/rasterizer_rd/render_pipeline_vertex_format_cache_rd.h @@ -0,0 +1,96 @@ +/*************************************************************************/ +/* render_pipeline_vertex_format_cache_rd.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef RENDER_PIPELINE_CACHE_RD_H +#define RENDER_PIPELINE_CACHE_RD_H + +#include "core/spin_lock.h" +#include "servers/rendering/rendering_device.h" + +class RenderPipelineVertexFormatCacheRD { + + SpinLock spin_lock; + + RID shader; + uint32_t input_mask; + + RD::RenderPrimitive render_primitive; + RD::PipelineRasterizationState rasterization_state; + RD::PipelineMultisampleState multisample_state; + RD::PipelineDepthStencilState depth_stencil_state; + RD::PipelineColorBlendState blend_state; + int dynamic_state_flags; + + struct Version { + RD::VertexFormatID vertex_id; + RD::FramebufferFormatID framebuffer_id; + RID pipeline; + }; + + Version *versions; + uint32_t version_count; + + RID _generate_version(RD::VertexFormatID p_vertex_format_id, RD::FramebufferFormatID p_framebuffer_format_id); + + void _clear(); + +public: + void setup(RID p_shader, RD::RenderPrimitive p_primitive, const RD::PipelineRasterizationState &p_rasterization_state, RD::PipelineMultisampleState p_multisample, const RD::PipelineDepthStencilState &p_depth_stencil_state, const RD::PipelineColorBlendState &p_blend_state, int p_dynamic_state_flags = 0); + void update_shader(RID p_shader); + + _FORCE_INLINE_ RID get_render_pipeline(RD::VertexFormatID p_vertex_format_id, RD::FramebufferFormatID p_framebuffer_format_id) { +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_V_MSG(shader.is_null(), RID(), + "Attempted to use an unused shader variant (shader is null),"); +#endif + + spin_lock.lock(); + RID result; + for (uint32_t i = 0; i < version_count; i++) { + if (versions[i].vertex_id == p_vertex_format_id && versions[i].framebuffer_id == p_framebuffer_format_id) { + result = versions[i].pipeline; + spin_lock.unlock(); + return result; + } + } + result = _generate_version(p_vertex_format_id, p_framebuffer_format_id); + spin_lock.unlock(); + return result; + } + + _FORCE_INLINE_ uint32_t get_vertex_input_mask() const { + return input_mask; + } + void clear(); + RenderPipelineVertexFormatCacheRD(); + ~RenderPipelineVertexFormatCacheRD(); +}; + +#endif // RENDER_PIPELINE_CACHE_RD_H diff --git a/servers/rendering/rasterizer_rd/shader_compiler_rd.cpp b/servers/rendering/rasterizer_rd/shader_compiler_rd.cpp new file mode 100644 index 0000000000..b3a4b0ede8 --- /dev/null +++ b/servers/rendering/rasterizer_rd/shader_compiler_rd.cpp @@ -0,0 +1,1243 @@ +/*************************************************************************/ +/* shader_compiler_rd.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "shader_compiler_rd.h" + +#include "core/os/os.h" +#include "core/project_settings.h" + +#define SL ShaderLanguage + +static String _mktab(int p_level) { + + String tb; + for (int i = 0; i < p_level; i++) { + tb += "\t"; + } + + return tb; +} + +static String _typestr(SL::DataType p_type) { + + String type = ShaderLanguage::get_datatype_name(p_type); + if (ShaderLanguage::is_sampler_type(p_type)) { + type = type.replace("sampler", "texture"); //we use textures instead of samplers + } + return type; +} + +static int _get_datatype_size(SL::DataType p_type) { + + switch (p_type) { + + case SL::TYPE_VOID: return 0; + case SL::TYPE_BOOL: return 4; + case SL::TYPE_BVEC2: return 8; + case SL::TYPE_BVEC3: return 12; + case SL::TYPE_BVEC4: return 16; + case SL::TYPE_INT: return 4; + case SL::TYPE_IVEC2: return 8; + case SL::TYPE_IVEC3: return 12; + case SL::TYPE_IVEC4: return 16; + case SL::TYPE_UINT: return 4; + case SL::TYPE_UVEC2: return 8; + case SL::TYPE_UVEC3: return 12; + case SL::TYPE_UVEC4: return 16; + case SL::TYPE_FLOAT: return 4; + case SL::TYPE_VEC2: return 8; + case SL::TYPE_VEC3: return 12; + case SL::TYPE_VEC4: return 16; + case SL::TYPE_MAT2: + return 32; //4 * 4 + 4 * 4 + case SL::TYPE_MAT3: + return 48; // 4 * 4 + 4 * 4 + 4 * 4 + case SL::TYPE_MAT4: return 64; + case SL::TYPE_SAMPLER2D: return 16; + case SL::TYPE_ISAMPLER2D: return 16; + case SL::TYPE_USAMPLER2D: return 16; + case SL::TYPE_SAMPLER2DARRAY: return 16; + case SL::TYPE_ISAMPLER2DARRAY: return 16; + case SL::TYPE_USAMPLER2DARRAY: return 16; + case SL::TYPE_SAMPLER3D: return 16; + case SL::TYPE_ISAMPLER3D: return 16; + case SL::TYPE_USAMPLER3D: return 16; + case SL::TYPE_SAMPLERCUBE: return 16; + case SL::TYPE_STRUCT: return 0; + } + + ERR_FAIL_V(0); +} + +static int _get_datatype_alignment(SL::DataType p_type) { + + switch (p_type) { + + case SL::TYPE_VOID: return 0; + case SL::TYPE_BOOL: return 4; + case SL::TYPE_BVEC2: return 8; + case SL::TYPE_BVEC3: return 16; + case SL::TYPE_BVEC4: return 16; + case SL::TYPE_INT: return 4; + case SL::TYPE_IVEC2: return 8; + case SL::TYPE_IVEC3: return 16; + case SL::TYPE_IVEC4: return 16; + case SL::TYPE_UINT: return 4; + case SL::TYPE_UVEC2: return 8; + case SL::TYPE_UVEC3: return 16; + case SL::TYPE_UVEC4: return 16; + case SL::TYPE_FLOAT: return 4; + case SL::TYPE_VEC2: return 8; + case SL::TYPE_VEC3: return 16; + case SL::TYPE_VEC4: return 16; + case SL::TYPE_MAT2: return 16; + case SL::TYPE_MAT3: return 16; + case SL::TYPE_MAT4: return 16; + case SL::TYPE_SAMPLER2D: return 16; + case SL::TYPE_ISAMPLER2D: return 16; + case SL::TYPE_USAMPLER2D: return 16; + case SL::TYPE_SAMPLER2DARRAY: return 16; + case SL::TYPE_ISAMPLER2DARRAY: return 16; + case SL::TYPE_USAMPLER2DARRAY: return 16; + case SL::TYPE_SAMPLER3D: return 16; + case SL::TYPE_ISAMPLER3D: return 16; + case SL::TYPE_USAMPLER3D: return 16; + case SL::TYPE_SAMPLERCUBE: return 16; + case SL::TYPE_STRUCT: return 0; + } + + ERR_FAIL_V(0); +} +static String _interpstr(SL::DataInterpolation p_interp) { + + switch (p_interp) { + case SL::INTERPOLATION_FLAT: return "flat "; + case SL::INTERPOLATION_SMOOTH: return ""; + } + return ""; +} + +static String _prestr(SL::DataPrecision p_pres) { + + switch (p_pres) { + case SL::PRECISION_LOWP: return "lowp "; + case SL::PRECISION_MEDIUMP: return "mediump "; + case SL::PRECISION_HIGHP: return "highp "; + case SL::PRECISION_DEFAULT: return ""; + } + return ""; +} + +static String _qualstr(SL::ArgumentQualifier p_qual) { + + switch (p_qual) { + case SL::ARGUMENT_QUALIFIER_IN: return ""; + case SL::ARGUMENT_QUALIFIER_OUT: return "out "; + case SL::ARGUMENT_QUALIFIER_INOUT: return "inout "; + } + return ""; +} + +static String _opstr(SL::Operator p_op) { + + return SL::get_operator_text(p_op); +} + +static String _mkid(const String &p_id) { + + String id = "m_" + p_id.replace("__", "_dus_"); + return id.replace("__", "_dus_"); //doubleunderscore is reserved in glsl +} + +static String f2sp0(float p_float) { + + String num = rtoss(p_float); + if (num.find(".") == -1 && num.find("e") == -1) { + num += ".0"; + } + return num; +} + +static String get_constant_text(SL::DataType p_type, const Vector<SL::ConstantNode::Value> &p_values) { + + switch (p_type) { + case SL::TYPE_BOOL: return p_values[0].boolean ? "true" : "false"; + case SL::TYPE_BVEC2: + case SL::TYPE_BVEC3: + case SL::TYPE_BVEC4: { + + String text = "bvec" + itos(p_type - SL::TYPE_BOOL + 1) + "("; + for (int i = 0; i < p_values.size(); i++) { + if (i > 0) + text += ","; + + text += p_values[i].boolean ? "true" : "false"; + } + text += ")"; + return text; + } + + case SL::TYPE_INT: return itos(p_values[0].sint); + case SL::TYPE_IVEC2: + case SL::TYPE_IVEC3: + case SL::TYPE_IVEC4: { + + String text = "ivec" + itos(p_type - SL::TYPE_INT + 1) + "("; + for (int i = 0; i < p_values.size(); i++) { + if (i > 0) + text += ","; + + text += itos(p_values[i].sint); + } + text += ")"; + return text; + + } break; + case SL::TYPE_UINT: return itos(p_values[0].uint) + "u"; + case SL::TYPE_UVEC2: + case SL::TYPE_UVEC3: + case SL::TYPE_UVEC4: { + + String text = "uvec" + itos(p_type - SL::TYPE_UINT + 1) + "("; + for (int i = 0; i < p_values.size(); i++) { + if (i > 0) + text += ","; + + text += itos(p_values[i].uint) + "u"; + } + text += ")"; + return text; + } break; + case SL::TYPE_FLOAT: return f2sp0(p_values[0].real); + case SL::TYPE_VEC2: + case SL::TYPE_VEC3: + case SL::TYPE_VEC4: { + + String text = "vec" + itos(p_type - SL::TYPE_FLOAT + 1) + "("; + for (int i = 0; i < p_values.size(); i++) { + if (i > 0) + text += ","; + + text += f2sp0(p_values[i].real); + } + text += ")"; + return text; + + } break; + case SL::TYPE_MAT2: + case SL::TYPE_MAT3: + case SL::TYPE_MAT4: { + + String text = "mat" + itos(p_type - SL::TYPE_MAT2 + 2) + "("; + for (int i = 0; i < p_values.size(); i++) { + if (i > 0) + text += ","; + + text += f2sp0(p_values[i].real); + } + text += ")"; + return text; + + } break; + default: ERR_FAIL_V(String()); + } +} + +String ShaderCompilerRD::_get_sampler_name(ShaderLanguage::TextureFilter p_filter, ShaderLanguage::TextureRepeat p_repeat) { + if (p_filter == ShaderLanguage::FILTER_DEFAULT) { + ERR_FAIL_COND_V(actions.default_filter == ShaderLanguage::FILTER_DEFAULT, String()); + p_filter = actions.default_filter; + } + if (p_repeat == ShaderLanguage::REPEAT_DEFAULT) { + ERR_FAIL_COND_V(actions.default_repeat == ShaderLanguage::REPEAT_DEFAULT, String()); + p_repeat = actions.default_repeat; + } + return actions.sampler_array_name + "[" + itos(p_filter + (p_repeat == ShaderLanguage::REPEAT_ENABLE ? ShaderLanguage::FILTER_DEFAULT : 0)) + "]"; +} + +void ShaderCompilerRD::_dump_function_deps(const SL::ShaderNode *p_node, const StringName &p_for_func, const Map<StringName, String> &p_func_code, String &r_to_add, Set<StringName> &added) { + + int fidx = -1; + + for (int i = 0; i < p_node->functions.size(); i++) { + if (p_node->functions[i].name == p_for_func) { + fidx = i; + break; + } + } + + ERR_FAIL_COND(fidx == -1); + + for (Set<StringName>::Element *E = p_node->functions[fidx].uses_function.front(); E; E = E->next()) { + + if (added.has(E->get())) { + continue; //was added already + } + + _dump_function_deps(p_node, E->get(), p_func_code, r_to_add, added); + + SL::FunctionNode *fnode = NULL; + + for (int i = 0; i < p_node->functions.size(); i++) { + if (p_node->functions[i].name == E->get()) { + fnode = p_node->functions[i].function; + break; + } + } + + ERR_FAIL_COND(!fnode); + + r_to_add += "\n"; + + String header; + if (fnode->return_type == SL::TYPE_STRUCT) { + header = _mkid(fnode->return_struct_name) + " " + _mkid(fnode->name) + "("; + } else { + header = _typestr(fnode->return_type) + " " + _mkid(fnode->name) + "("; + } + for (int i = 0; i < fnode->arguments.size(); i++) { + + if (i > 0) + header += ", "; + if (fnode->arguments[i].type == SL::TYPE_STRUCT) { + header += _qualstr(fnode->arguments[i].qualifier) + _mkid(fnode->arguments[i].type_str) + " " + _mkid(fnode->arguments[i].name); + } else { + header += _qualstr(fnode->arguments[i].qualifier) + _prestr(fnode->arguments[i].precision) + _typestr(fnode->arguments[i].type) + " " + _mkid(fnode->arguments[i].name); + } + } + + header += ")\n"; + r_to_add += header; + r_to_add += p_func_code[E->get()]; + + added.insert(E->get()); + } +} + +String ShaderCompilerRD::_dump_node_code(const SL::Node *p_node, int p_level, GeneratedCode &r_gen_code, IdentifierActions &p_actions, const DefaultIdentifierActions &p_default_actions, bool p_assigning) { + + String code; + + switch (p_node->type) { + + case SL::Node::TYPE_SHADER: { + + SL::ShaderNode *pnode = (SL::ShaderNode *)p_node; + + for (int i = 0; i < pnode->render_modes.size(); i++) { + + if (p_default_actions.render_mode_defines.has(pnode->render_modes[i]) && !used_rmode_defines.has(pnode->render_modes[i])) { + + r_gen_code.defines.push_back(p_default_actions.render_mode_defines[pnode->render_modes[i]]); + used_rmode_defines.insert(pnode->render_modes[i]); + } + + if (p_actions.render_mode_flags.has(pnode->render_modes[i])) { + *p_actions.render_mode_flags[pnode->render_modes[i]] = true; + } + + if (p_actions.render_mode_values.has(pnode->render_modes[i])) { + Pair<int *, int> &p = p_actions.render_mode_values[pnode->render_modes[i]]; + *p.first = p.second; + } + } + + // structs + + for (int i = 0; i < pnode->vstructs.size(); i++) { + + SL::StructNode *st = pnode->vstructs[i].shader_struct; + String struct_code; + + struct_code += "struct "; + struct_code += _mkid(pnode->vstructs[i].name); + struct_code += " "; + struct_code += "{\n"; + for (int j = 0; j < st->members.size(); j++) { + SL::MemberNode *m = st->members[j]; + if (m->datatype == SL::TYPE_STRUCT) { + struct_code += _mkid(m->struct_name); + } else { + struct_code += _prestr(m->precision); + struct_code += _typestr(m->datatype); + } + struct_code += " "; + struct_code += m->name; + if (m->array_size > 0) { + struct_code += "["; + struct_code += itos(m->array_size); + struct_code += "]"; + } + struct_code += ";\n"; + } + struct_code += "}"; + struct_code += ";\n"; + + r_gen_code.vertex_global += struct_code; + r_gen_code.fragment_global += struct_code; + } + + int max_texture_uniforms = 0; + int max_uniforms = 0; + + for (Map<StringName, SL::ShaderNode::Uniform>::Element *E = pnode->uniforms.front(); E; E = E->next()) { + if (SL::is_sampler_type(E->get().type)) + max_texture_uniforms++; + else + max_uniforms++; + } + + r_gen_code.texture_uniforms.resize(max_texture_uniforms); + + Vector<int> uniform_sizes; + Vector<int> uniform_alignments; + Vector<StringName> uniform_defines; + uniform_sizes.resize(max_uniforms); + uniform_alignments.resize(max_uniforms); + uniform_defines.resize(max_uniforms); + bool uses_uniforms = false; + + for (Map<StringName, SL::ShaderNode::Uniform>::Element *E = pnode->uniforms.front(); E; E = E->next()) { + + String ucode; + + if (SL::is_sampler_type(E->get().type)) { + ucode = "layout(set = " + itos(actions.texture_layout_set) + ", binding = " + itos(actions.base_texture_binding_index + E->get().texture_order) + ") uniform "; + } + + ucode += _prestr(E->get().precision); + ucode += _typestr(E->get().type); + ucode += " " + _mkid(E->key()); + ucode += ";\n"; + if (SL::is_sampler_type(E->get().type)) { + r_gen_code.vertex_global += ucode; + r_gen_code.fragment_global += ucode; + + GeneratedCode::Texture texture; + texture.name = E->key(); + texture.hint = E->get().hint; + texture.type = E->get().type; + texture.filter = E->get().filter; + texture.repeat = E->get().repeat; + + r_gen_code.texture_uniforms.write[E->get().texture_order] = texture; + } else { + if (!uses_uniforms) { + + r_gen_code.defines.push_back(String("#define USE_MATERIAL_UNIFORMS\n")); + uses_uniforms = true; + } + uniform_defines.write[E->get().order] = ucode; + uniform_sizes.write[E->get().order] = _get_datatype_size(E->get().type); + uniform_alignments.write[E->get().order] = _get_datatype_alignment(E->get().type); + } + + p_actions.uniforms->insert(E->key(), E->get()); + } + + for (int i = 0; i < max_uniforms; i++) { + r_gen_code.uniforms += uniform_defines[i]; + } + +#if 1 + // add up + int offset = 0; + for (int i = 0; i < uniform_sizes.size(); i++) { + + int align = offset % uniform_alignments[i]; + + if (align != 0) { + offset += uniform_alignments[i] - align; + } + + r_gen_code.uniform_offsets.push_back(offset); + + offset += uniform_sizes[i]; + } + + r_gen_code.uniform_total_size = offset; + + if (r_gen_code.uniform_total_size % 16 != 0) { //UBO sizes must be multiples of 16 + r_gen_code.uniform_total_size += 16 - (r_gen_code.uniform_total_size % 16); + } +#else + // add up + for (int i = 0; i < uniform_sizes.size(); i++) { + + if (i > 0) { + + int align = uniform_sizes[i - 1] % uniform_alignments[i]; + if (align != 0) { + uniform_sizes[i - 1] += uniform_alignments[i] - align; + } + + uniform_sizes[i] = uniform_sizes[i] + uniform_sizes[i - 1]; + } + } + //offset + r_gen_code.uniform_offsets.resize(uniform_sizes.size()); + for (int i = 0; i < uniform_sizes.size(); i++) { + + if (i > 0) + r_gen_code.uniform_offsets[i] = uniform_sizes[i - 1]; + else + r_gen_code.uniform_offsets[i] = 0; + } + /* + for(Map<StringName,SL::ShaderNode::Uniform>::Element *E=pnode->uniforms.front();E;E=E->next()) { + + if (SL::is_sampler_type(E->get().type)) { + continue; + } + + } + +*/ + if (uniform_sizes.size()) { + r_gen_code.uniform_total_size = uniform_sizes[uniform_sizes.size() - 1]; + } else { + r_gen_code.uniform_total_size = 0; + } +#endif + + uint32_t index = p_default_actions.base_varying_index; + + for (Map<StringName, SL::ShaderNode::Varying>::Element *E = pnode->varyings.front(); E; E = E->next()) { + + String vcode; + String interp_mode = _interpstr(E->get().interpolation); + vcode += _prestr(E->get().precision); + vcode += _typestr(E->get().type); + vcode += " " + _mkid(E->key()); + if (E->get().array_size > 0) { + vcode += "["; + vcode += itos(E->get().array_size); + vcode += "]"; + } + vcode += ";\n"; + r_gen_code.vertex_global += "layout(location=" + itos(index) + ") " + interp_mode + "out " + vcode; + r_gen_code.fragment_global += "layout(location=" + itos(index) + ") " + interp_mode + "in " + vcode; + index++; + } + + for (Map<StringName, SL::ShaderNode::Constant>::Element *E = pnode->constants.front(); E; E = E->next()) { + String gcode; + gcode += "const "; + gcode += _prestr(E->get().precision); + if (E->get().type == SL::TYPE_STRUCT) { + gcode += _mkid(E->get().type_str); + } else { + gcode += _typestr(E->get().type); + } + gcode += " " + _mkid(E->key()); + gcode += "="; + gcode += _dump_node_code(E->get().initializer, p_level, r_gen_code, p_actions, p_default_actions, p_assigning); + gcode += ";\n"; + r_gen_code.vertex_global += gcode; + r_gen_code.fragment_global += gcode; + } + + Map<StringName, String> function_code; + + //code for functions + for (int i = 0; i < pnode->functions.size(); i++) { + SL::FunctionNode *fnode = pnode->functions[i].function; + function = fnode; + current_func_name = fnode->name; + function_code[fnode->name] = _dump_node_code(fnode->body, p_level + 1, r_gen_code, p_actions, p_default_actions, p_assigning); + function = NULL; + } + + //place functions in actual code + + Set<StringName> added_vtx; + Set<StringName> added_fragment; //share for light + + for (int i = 0; i < pnode->functions.size(); i++) { + + SL::FunctionNode *fnode = pnode->functions[i].function; + + function = fnode; + + current_func_name = fnode->name; + + if (fnode->name == vertex_name) { + + _dump_function_deps(pnode, fnode->name, function_code, r_gen_code.vertex_global, added_vtx); + r_gen_code.vertex = function_code[vertex_name]; + } + + if (fnode->name == fragment_name) { + + _dump_function_deps(pnode, fnode->name, function_code, r_gen_code.fragment_global, added_fragment); + r_gen_code.fragment = function_code[fragment_name]; + } + + if (fnode->name == light_name) { + + _dump_function_deps(pnode, fnode->name, function_code, r_gen_code.fragment_global, added_fragment); + r_gen_code.light = function_code[light_name]; + } + function = NULL; + } + + //code+=dump_node_code(pnode->body,p_level); + } break; + case SL::Node::TYPE_STRUCT: { + + } break; + case SL::Node::TYPE_FUNCTION: { + + } break; + case SL::Node::TYPE_BLOCK: { + SL::BlockNode *bnode = (SL::BlockNode *)p_node; + + //variables + if (!bnode->single_statement) { + code += _mktab(p_level - 1) + "{\n"; + } + + for (int i = 0; i < bnode->statements.size(); i++) { + + String scode = _dump_node_code(bnode->statements[i], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); + + if (bnode->statements[i]->type == SL::Node::TYPE_CONTROL_FLOW || bnode->single_statement) { + code += scode; //use directly + } else { + code += _mktab(p_level) + scode + ";\n"; + } + } + if (!bnode->single_statement) { + code += _mktab(p_level - 1) + "}\n"; + } + + } break; + case SL::Node::TYPE_VARIABLE_DECLARATION: { + SL::VariableDeclarationNode *vdnode = (SL::VariableDeclarationNode *)p_node; + + String declaration; + if (vdnode->is_const) { + declaration += "const "; + } + if (vdnode->datatype == SL::TYPE_STRUCT) { + declaration += _mkid(vdnode->struct_name); + } else { + declaration += _prestr(vdnode->precision) + _typestr(vdnode->datatype); + } + for (int i = 0; i < vdnode->declarations.size(); i++) { + if (i > 0) { + declaration += ","; + } else { + declaration += " "; + } + declaration += _mkid(vdnode->declarations[i].name); + if (vdnode->declarations[i].initializer) { + declaration += "="; + declaration += _dump_node_code(vdnode->declarations[i].initializer, p_level, r_gen_code, p_actions, p_default_actions, p_assigning); + } + } + + code += declaration; + } break; + case SL::Node::TYPE_VARIABLE: { + SL::VariableNode *vnode = (SL::VariableNode *)p_node; + + if (p_assigning && p_actions.write_flag_pointers.has(vnode->name)) { + *p_actions.write_flag_pointers[vnode->name] = true; + } + + if (p_default_actions.usage_defines.has(vnode->name) && !used_name_defines.has(vnode->name)) { + String define = p_default_actions.usage_defines[vnode->name]; + if (define.begins_with("@")) { + define = p_default_actions.usage_defines[define.substr(1, define.length())]; + } + r_gen_code.defines.push_back(define); + used_name_defines.insert(vnode->name); + } + + if (p_actions.usage_flag_pointers.has(vnode->name) && !used_flag_pointers.has(vnode->name)) { + *p_actions.usage_flag_pointers[vnode->name] = true; + used_flag_pointers.insert(vnode->name); + } + + if (p_default_actions.renames.has(vnode->name)) + code = p_default_actions.renames[vnode->name]; + else { + code = _mkid(vnode->name); + if (actions.base_uniform_string != String() && shader->uniforms.has(vnode->name) && shader->uniforms[vnode->name].texture_order < 0) { + code = actions.base_uniform_string + code; + } + } + + if (vnode->name == time_name) { + if (current_func_name == vertex_name) { + r_gen_code.uses_vertex_time = true; + } + if (current_func_name == fragment_name || current_func_name == light_name) { + r_gen_code.uses_fragment_time = true; + } + } + + } break; + case SL::Node::TYPE_ARRAY_CONSTRUCT: { + SL::ArrayConstructNode *acnode = (SL::ArrayConstructNode *)p_node; + int sz = acnode->initializer.size(); + if (acnode->datatype == SL::TYPE_STRUCT) { + code += _mkid(acnode->struct_name); + } else { + code += _typestr(acnode->datatype); + } + code += "["; + code += itos(acnode->initializer.size()); + code += "]"; + code += "("; + for (int i = 0; i < sz; i++) { + code += _dump_node_code(acnode->initializer[i], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); + if (i != sz - 1) { + code += ", "; + } + } + code += ")"; + } break; + case SL::Node::TYPE_ARRAY_DECLARATION: { + + SL::ArrayDeclarationNode *adnode = (SL::ArrayDeclarationNode *)p_node; + String declaration; + if (adnode->is_const) { + declaration += "const "; + } + if (adnode->datatype == SL::TYPE_STRUCT) { + declaration += _mkid(adnode->struct_name); + } else { + declaration = _prestr(adnode->precision) + _typestr(adnode->datatype); + } + for (int i = 0; i < adnode->declarations.size(); i++) { + if (i > 0) { + declaration += ","; + } else { + declaration += " "; + } + declaration += _mkid(adnode->declarations[i].name); + declaration += "["; + declaration += itos(adnode->declarations[i].size); + declaration += "]"; + int sz = adnode->declarations[i].initializer.size(); + if (sz > 0) { + declaration += "="; + if (adnode->datatype == SL::TYPE_STRUCT) { + declaration += _mkid(adnode->struct_name); + } else { + declaration += _typestr(adnode->datatype); + } + declaration += "["; + declaration += itos(sz); + declaration += "]"; + declaration += "("; + for (int j = 0; j < sz; j++) { + declaration += _dump_node_code(adnode->declarations[i].initializer[j], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); + if (j != sz - 1) { + declaration += ", "; + } + } + declaration += ")"; + } + } + + code += declaration; + } break; + case SL::Node::TYPE_ARRAY: { + SL::ArrayNode *anode = (SL::ArrayNode *)p_node; + + if (p_assigning && p_actions.write_flag_pointers.has(anode->name)) { + *p_actions.write_flag_pointers[anode->name] = true; + } + + if (p_default_actions.usage_defines.has(anode->name) && !used_name_defines.has(anode->name)) { + String define = p_default_actions.usage_defines[anode->name]; + if (define.begins_with("@")) { + define = p_default_actions.usage_defines[define.substr(1, define.length())]; + } + r_gen_code.defines.push_back(define); + used_name_defines.insert(anode->name); + } + + if (p_actions.usage_flag_pointers.has(anode->name) && !used_flag_pointers.has(anode->name)) { + *p_actions.usage_flag_pointers[anode->name] = true; + used_flag_pointers.insert(anode->name); + } + + if (p_default_actions.renames.has(anode->name)) + code = p_default_actions.renames[anode->name]; + else + code = _mkid(anode->name); + + if (anode->call_expression != NULL) { + code += "."; + code += _dump_node_code(anode->call_expression, p_level, r_gen_code, p_actions, p_default_actions, p_assigning); + } + + if (anode->index_expression != NULL) { + code += "["; + code += _dump_node_code(anode->index_expression, p_level, r_gen_code, p_actions, p_default_actions, p_assigning); + code += "]"; + } + + if (anode->name == time_name) { + if (current_func_name == vertex_name) { + r_gen_code.uses_vertex_time = true; + } + if (current_func_name == fragment_name || current_func_name == light_name) { + r_gen_code.uses_fragment_time = true; + } + } + + } break; + case SL::Node::TYPE_CONSTANT: { + SL::ConstantNode *cnode = (SL::ConstantNode *)p_node; + return get_constant_text(cnode->datatype, cnode->values); + + } break; + case SL::Node::TYPE_OPERATOR: { + SL::OperatorNode *onode = (SL::OperatorNode *)p_node; + + switch (onode->op) { + + case SL::OP_ASSIGN: + case SL::OP_ASSIGN_ADD: + case SL::OP_ASSIGN_SUB: + case SL::OP_ASSIGN_MUL: + case SL::OP_ASSIGN_DIV: + case SL::OP_ASSIGN_SHIFT_LEFT: + case SL::OP_ASSIGN_SHIFT_RIGHT: + case SL::OP_ASSIGN_MOD: + case SL::OP_ASSIGN_BIT_AND: + case SL::OP_ASSIGN_BIT_OR: + case SL::OP_ASSIGN_BIT_XOR: + code = _dump_node_code(onode->arguments[0], p_level, r_gen_code, p_actions, p_default_actions, true) + _opstr(onode->op) + _dump_node_code(onode->arguments[1], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); + break; + case SL::OP_BIT_INVERT: + case SL::OP_NEGATE: + case SL::OP_NOT: + case SL::OP_DECREMENT: + case SL::OP_INCREMENT: + code = _opstr(onode->op) + _dump_node_code(onode->arguments[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); + break; + case SL::OP_POST_DECREMENT: + case SL::OP_POST_INCREMENT: + code = _dump_node_code(onode->arguments[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning) + _opstr(onode->op); + break; + case SL::OP_CALL: + case SL::OP_STRUCT: + case SL::OP_CONSTRUCT: { + + ERR_FAIL_COND_V(onode->arguments[0]->type != SL::Node::TYPE_VARIABLE, String()); + + SL::VariableNode *vnode = (SL::VariableNode *)onode->arguments[0]; + + bool is_texture_func = false; + if (onode->op == SL::OP_STRUCT) { + code += _mkid(vnode->name); + } else if (onode->op == SL::OP_CONSTRUCT) { + code += String(vnode->name); + } else { + + if (internal_functions.has(vnode->name)) { + code += vnode->name; + is_texture_func = texture_functions.has(vnode->name); + } else if (p_default_actions.renames.has(vnode->name)) { + code += p_default_actions.renames[vnode->name]; + } else { + code += _mkid(vnode->name); + } + } + + code += "("; + + for (int i = 1; i < onode->arguments.size(); i++) { + if (i > 1) + code += ", "; + String node_code = _dump_node_code(onode->arguments[i], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); + if (is_texture_func && i == 1 && onode->arguments[i]->type == SL::Node::TYPE_VARIABLE) { + + //need to map from texture to sampler in order to sample + const SL::VariableNode *varnode = static_cast<const SL::VariableNode *>(onode->arguments[i]); + + StringName texture_uniform = varnode->name; + + String sampler_name; + + if (actions.custom_samplers.has(texture_uniform)) { + sampler_name = actions.custom_samplers[texture_uniform]; + } else { + if (shader->uniforms.has(texture_uniform)) { + sampler_name = _get_sampler_name(shader->uniforms[texture_uniform].filter, shader->uniforms[texture_uniform].repeat); + } else { + bool found = false; + + for (int j = 0; j < function->arguments.size(); j++) { + if (function->arguments[j].name == texture_uniform) { + if (function->arguments[j].tex_builtin_check) { + ERR_CONTINUE(!actions.custom_samplers.has(function->arguments[j].tex_builtin)); + sampler_name = actions.custom_samplers[function->arguments[j].tex_builtin]; + found = true; + break; + } + if (function->arguments[j].tex_argument_check) { + sampler_name = _get_sampler_name(function->arguments[j].tex_argument_filter, function->arguments[j].tex_argument_repeat); + found = true; + break; + } + } + } + if (!found) { + //function was most likely unused, so use anything (compiler will remove it anyway) + sampler_name = _get_sampler_name(ShaderLanguage::FILTER_DEFAULT, ShaderLanguage::REPEAT_DEFAULT); + } + } + } + + code += ShaderLanguage::get_datatype_name(onode->arguments[i]->get_datatype()) + "(" + node_code + ", " + sampler_name + ")"; + } else { + code += node_code; + } + } + code += ")"; + } break; + case SL::OP_INDEX: { + + code += _dump_node_code(onode->arguments[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); + code += "["; + code += _dump_node_code(onode->arguments[1], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); + code += "]"; + + } break; + case SL::OP_SELECT_IF: { + + code += "("; + code += _dump_node_code(onode->arguments[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); + code += "?"; + code += _dump_node_code(onode->arguments[1], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); + code += ":"; + code += _dump_node_code(onode->arguments[2], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); + code += ")"; + + } break; + + default: { + + code = "(" + _dump_node_code(onode->arguments[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning) + _opstr(onode->op) + _dump_node_code(onode->arguments[1], p_level, r_gen_code, p_actions, p_default_actions, p_assigning) + ")"; + break; + } + } + + } break; + case SL::Node::TYPE_CONTROL_FLOW: { + SL::ControlFlowNode *cfnode = (SL::ControlFlowNode *)p_node; + if (cfnode->flow_op == SL::FLOW_OP_IF) { + + code += _mktab(p_level) + "if (" + _dump_node_code(cfnode->expressions[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning) + ")\n"; + code += _dump_node_code(cfnode->blocks[0], p_level + 1, r_gen_code, p_actions, p_default_actions, p_assigning); + if (cfnode->blocks.size() == 2) { + + code += _mktab(p_level) + "else\n"; + code += _dump_node_code(cfnode->blocks[1], p_level + 1, r_gen_code, p_actions, p_default_actions, p_assigning); + } + } else if (cfnode->flow_op == SL::FLOW_OP_SWITCH) { + + code += _mktab(p_level) + "switch (" + _dump_node_code(cfnode->expressions[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning) + ")\n"; + code += _dump_node_code(cfnode->blocks[0], p_level + 1, r_gen_code, p_actions, p_default_actions, p_assigning); + } else if (cfnode->flow_op == SL::FLOW_OP_CASE) { + + code += _mktab(p_level) + "case " + _dump_node_code(cfnode->expressions[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning) + ":\n"; + code += _dump_node_code(cfnode->blocks[0], p_level + 1, r_gen_code, p_actions, p_default_actions, p_assigning); + } else if (cfnode->flow_op == SL::FLOW_OP_DEFAULT) { + + code += _mktab(p_level) + "default:\n"; + code += _dump_node_code(cfnode->blocks[0], p_level + 1, r_gen_code, p_actions, p_default_actions, p_assigning); + } else if (cfnode->flow_op == SL::FLOW_OP_DO) { + + code += _mktab(p_level) + "do"; + code += _dump_node_code(cfnode->blocks[0], p_level + 1, r_gen_code, p_actions, p_default_actions, p_assigning); + code += _mktab(p_level) + "while (" + _dump_node_code(cfnode->expressions[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning) + ");"; + } else if (cfnode->flow_op == SL::FLOW_OP_WHILE) { + + code += _mktab(p_level) + "while (" + _dump_node_code(cfnode->expressions[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning) + ")\n"; + code += _dump_node_code(cfnode->blocks[0], p_level + 1, r_gen_code, p_actions, p_default_actions, p_assigning); + } else if (cfnode->flow_op == SL::FLOW_OP_FOR) { + + String left = _dump_node_code(cfnode->blocks[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); + String middle = _dump_node_code(cfnode->expressions[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); + String right = _dump_node_code(cfnode->expressions[1], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); + code += _mktab(p_level) + "for (" + left + ";" + middle + ";" + right + ")\n"; + code += _dump_node_code(cfnode->blocks[1], p_level + 1, r_gen_code, p_actions, p_default_actions, p_assigning); + + } else if (cfnode->flow_op == SL::FLOW_OP_RETURN) { + + if (cfnode->expressions.size()) { + code = "return " + _dump_node_code(cfnode->expressions[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning) + ";"; + } else { + code = "return;"; + } + } else if (cfnode->flow_op == SL::FLOW_OP_DISCARD) { + + if (p_actions.usage_flag_pointers.has("DISCARD") && !used_flag_pointers.has("DISCARD")) { + *p_actions.usage_flag_pointers["DISCARD"] = true; + used_flag_pointers.insert("DISCARD"); + } + + code = "discard;"; + } else if (cfnode->flow_op == SL::FLOW_OP_CONTINUE) { + + code = "continue;"; + } else if (cfnode->flow_op == SL::FLOW_OP_BREAK) { + + code = "break;"; + } + + } break; + case SL::Node::TYPE_MEMBER: { + SL::MemberNode *mnode = (SL::MemberNode *)p_node; + code = _dump_node_code(mnode->owner, p_level, r_gen_code, p_actions, p_default_actions, p_assigning) + "." + mnode->name; + if (mnode->index_expression != NULL) { + code += "["; + code += _dump_node_code(mnode->index_expression, p_level, r_gen_code, p_actions, p_default_actions, p_assigning); + code += "]"; + } + + } break; + } + + return code; +} + +Error ShaderCompilerRD::compile(RS::ShaderMode p_mode, const String &p_code, IdentifierActions *p_actions, const String &p_path, GeneratedCode &r_gen_code) { + + Error err = parser.compile(p_code, ShaderTypes::get_singleton()->get_functions(p_mode), ShaderTypes::get_singleton()->get_modes(p_mode), ShaderTypes::get_singleton()->get_types()); + + if (err != OK) { + + Vector<String> shader = p_code.split("\n"); + for (int i = 0; i < shader.size(); i++) { + print_line(itos(i + 1) + " " + shader[i]); + } + + _err_print_error(NULL, p_path.utf8().get_data(), parser.get_error_line(), parser.get_error_text().utf8().get_data(), ERR_HANDLER_SHADER); + return err; + } + + r_gen_code.defines.clear(); + r_gen_code.vertex = String(); + r_gen_code.vertex_global = String(); + r_gen_code.fragment = String(); + r_gen_code.fragment_global = String(); + r_gen_code.light = String(); + r_gen_code.uses_fragment_time = false; + r_gen_code.uses_vertex_time = false; + + used_name_defines.clear(); + used_rmode_defines.clear(); + used_flag_pointers.clear(); + + shader = parser.get_shader(); + function = NULL; + _dump_node_code(shader, 1, r_gen_code, *p_actions, actions, false); + + return OK; +} + +void ShaderCompilerRD::initialize(DefaultIdentifierActions p_actions) { + actions = p_actions; + + vertex_name = "vertex"; + fragment_name = "fragment"; + light_name = "light"; + time_name = "TIME"; + + List<String> func_list; + + ShaderLanguage::get_builtin_funcs(&func_list); + + for (List<String>::Element *E = func_list.front(); E; E = E->next()) { + internal_functions.insert(E->get()); + } + texture_functions.insert("texture"); + texture_functions.insert("textureProj"); + texture_functions.insert("textureLod"); + texture_functions.insert("textureProjLod"); + texture_functions.insert("textureGrad"); +} + +ShaderCompilerRD::ShaderCompilerRD() { +#if 0 + + /** SPATIAL SHADER **/ + + actions[RS::SHADER_SPATIAL].renames["WORLD_MATRIX"] = "world_transform"; + actions[RS::SHADER_SPATIAL].renames["INV_CAMERA_MATRIX"] = "camera_inverse_matrix"; + actions[RS::SHADER_SPATIAL].renames["CAMERA_MATRIX"] = "camera_matrix"; + actions[RS::SHADER_SPATIAL].renames["PROJECTION_MATRIX"] = "projection_matrix"; + actions[RS::SHADER_SPATIAL].renames["INV_PROJECTION_MATRIX"] = "inv_projection_matrix"; + actions[RS::SHADER_SPATIAL].renames["MODELVIEW_MATRIX"] = "modelview"; + + actions[RS::SHADER_SPATIAL].renames["VERTEX"] = "vertex.xyz"; + actions[RS::SHADER_SPATIAL].renames["NORMAL"] = "normal"; + actions[RS::SHADER_SPATIAL].renames["TANGENT"] = "tangent"; + actions[RS::SHADER_SPATIAL].renames["BINORMAL"] = "binormal"; + actions[RS::SHADER_SPATIAL].renames["POSITION"] = "position"; + actions[RS::SHADER_SPATIAL].renames["UV"] = "uv_interp"; + actions[RS::SHADER_SPATIAL].renames["UV2"] = "uv2_interp"; + actions[RS::SHADER_SPATIAL].renames["COLOR"] = "color_interp"; + actions[RS::SHADER_SPATIAL].renames["POINT_SIZE"] = "gl_PointSize"; + actions[RS::SHADER_SPATIAL].renames["INSTANCE_ID"] = "gl_InstanceID"; + + //builtins + + actions[RS::SHADER_SPATIAL].renames["TIME"] = "time"; + actions[RS::SHADER_SPATIAL].renames["VIEWPORT_SIZE"] = "viewport_size"; + + actions[RS::SHADER_SPATIAL].renames["FRAGCOORD"] = "gl_FragCoord"; + actions[RS::SHADER_SPATIAL].renames["FRONT_FACING"] = "gl_FrontFacing"; + actions[RS::SHADER_SPATIAL].renames["NORMALMAP"] = "normalmap"; + actions[RS::SHADER_SPATIAL].renames["NORMALMAP_DEPTH"] = "normaldepth"; + actions[RS::SHADER_SPATIAL].renames["ALBEDO"] = "albedo"; + actions[RS::SHADER_SPATIAL].renames["ALPHA"] = "alpha"; + actions[RS::SHADER_SPATIAL].renames["METALLIC"] = "metallic"; + actions[RS::SHADER_SPATIAL].renames["SPECULAR"] = "specular"; + actions[RS::SHADER_SPATIAL].renames["ROUGHNESS"] = "roughness"; + actions[RS::SHADER_SPATIAL].renames["RIM"] = "rim"; + actions[RS::SHADER_SPATIAL].renames["RIM_TINT"] = "rim_tint"; + actions[RS::SHADER_SPATIAL].renames["CLEARCOAT"] = "clearcoat"; + actions[RS::SHADER_SPATIAL].renames["CLEARCOAT_GLOSS"] = "clearcoat_gloss"; + actions[RS::SHADER_SPATIAL].renames["ANISOTROPY"] = "anisotropy"; + actions[RS::SHADER_SPATIAL].renames["ANISOTROPY_FLOW"] = "anisotropy_flow"; + actions[RS::SHADER_SPATIAL].renames["SSS_STRENGTH"] = "sss_strength"; + actions[RS::SHADER_SPATIAL].renames["TRANSMISSION"] = "transmission"; + actions[RS::SHADER_SPATIAL].renames["AO"] = "ao"; + actions[RS::SHADER_SPATIAL].renames["AO_LIGHT_AFFECT"] = "ao_light_affect"; + actions[RS::SHADER_SPATIAL].renames["EMISSION"] = "emission"; + actions[RS::SHADER_SPATIAL].renames["POINT_COORD"] = "gl_PointCoord"; + actions[RS::SHADER_SPATIAL].renames["INSTANCE_CUSTOM"] = "instance_custom"; + actions[RS::SHADER_SPATIAL].renames["SCREEN_UV"] = "screen_uv"; + actions[RS::SHADER_SPATIAL].renames["SCREEN_TEXTURE"] = "screen_texture"; + actions[RS::SHADER_SPATIAL].renames["DEPTH_TEXTURE"] = "depth_buffer"; + actions[RS::SHADER_SPATIAL].renames["DEPTH"] = "gl_FragDepth"; + actions[RS::SHADER_SPATIAL].renames["ALPHA_SCISSOR"] = "alpha_scissor"; + actions[RS::SHADER_SPATIAL].renames["OUTPUT_IS_SRGB"] = "SHADER_IS_SRGB"; + + //for light + actions[RS::SHADER_SPATIAL].renames["VIEW"] = "view"; + actions[RS::SHADER_SPATIAL].renames["LIGHT_COLOR"] = "light_color"; + actions[RS::SHADER_SPATIAL].renames["LIGHT"] = "light"; + actions[RS::SHADER_SPATIAL].renames["ATTENUATION"] = "attenuation"; + actions[RS::SHADER_SPATIAL].renames["DIFFUSE_LIGHT"] = "diffuse_light"; + actions[RS::SHADER_SPATIAL].renames["SPECULAR_LIGHT"] = "specular_light"; + + actions[RS::SHADER_SPATIAL].usage_defines["TANGENT"] = "#define ENABLE_TANGENT_INTERP\n"; + actions[RS::SHADER_SPATIAL].usage_defines["BINORMAL"] = "@TANGENT"; + actions[RS::SHADER_SPATIAL].usage_defines["RIM"] = "#define LIGHT_USE_RIM\n"; + actions[RS::SHADER_SPATIAL].usage_defines["RIM_TINT"] = "@RIM"; + actions[RS::SHADER_SPATIAL].usage_defines["CLEARCOAT"] = "#define LIGHT_USE_CLEARCOAT\n"; + actions[RS::SHADER_SPATIAL].usage_defines["CLEARCOAT_GLOSS"] = "@CLEARCOAT"; + actions[RS::SHADER_SPATIAL].usage_defines["ANISOTROPY"] = "#define LIGHT_USE_ANISOTROPY\n"; + actions[RS::SHADER_SPATIAL].usage_defines["ANISOTROPY_FLOW"] = "@ANISOTROPY"; + actions[RS::SHADER_SPATIAL].usage_defines["AO"] = "#define ENABLE_AO\n"; + actions[RS::SHADER_SPATIAL].usage_defines["AO_LIGHT_AFFECT"] = "#define ENABLE_AO\n"; + actions[RS::SHADER_SPATIAL].usage_defines["UV"] = "#define ENABLE_UV_INTERP\n"; + actions[RS::SHADER_SPATIAL].usage_defines["UV2"] = "#define ENABLE_UV2_INTERP\n"; + actions[RS::SHADER_SPATIAL].usage_defines["NORMALMAP"] = "#define ENABLE_NORMALMAP\n"; + actions[RS::SHADER_SPATIAL].usage_defines["NORMALMAP_DEPTH"] = "@NORMALMAP"; + actions[RS::SHADER_SPATIAL].usage_defines["COLOR"] = "#define ENABLE_COLOR_INTERP\n"; + actions[RS::SHADER_SPATIAL].usage_defines["INSTANCE_CUSTOM"] = "#define ENABLE_INSTANCE_CUSTOM\n"; + actions[RS::SHADER_SPATIAL].usage_defines["ALPHA_SCISSOR"] = "#define ALPHA_SCISSOR_USED\n"; + actions[RS::SHADER_SPATIAL].usage_defines["POSITION"] = "#define OVERRIDE_POSITION\n"; + + actions[RS::SHADER_SPATIAL].usage_defines["SSS_STRENGTH"] = "#define ENABLE_SSS\n"; + actions[RS::SHADER_SPATIAL].usage_defines["TRANSMISSION"] = "#define TRANSMISSION_USED\n"; + actions[RS::SHADER_SPATIAL].usage_defines["SCREEN_TEXTURE"] = "#define SCREEN_TEXTURE_USED\n"; + actions[RS::SHADER_SPATIAL].usage_defines["SCREEN_UV"] = "#define SCREEN_UV_USED\n"; + + actions[RS::SHADER_SPATIAL].usage_defines["DIFFUSE_LIGHT"] = "#define USE_LIGHT_SHADER_CODE\n"; + actions[RS::SHADER_SPATIAL].usage_defines["SPECULAR_LIGHT"] = "#define USE_LIGHT_SHADER_CODE\n"; + + actions[RS::SHADER_SPATIAL].render_mode_defines["skip_vertex_transform"] = "#define SKIP_TRANSFORM_USED\n"; + actions[RS::SHADER_SPATIAL].render_mode_defines["world_vertex_coords"] = "#define VERTEX_WORLD_COORDS_USED\n"; + actions[RS::SHADER_SPATIAL].render_mode_defines["ensure_correct_normals"] = "#define ENSURE_CORRECT_NORMALS\n"; + actions[RS::SHADER_SPATIAL].render_mode_defines["cull_front"] = "#define DO_SIDE_CHECK\n"; + actions[RS::SHADER_SPATIAL].render_mode_defines["cull_disabled"] = "#define DO_SIDE_CHECK\n"; + + bool force_lambert = GLOBAL_GET("rendering/quality/shading/force_lambert_over_burley"); + + if (!force_lambert) { + actions[RS::SHADER_SPATIAL].render_mode_defines["diffuse_burley"] = "#define DIFFUSE_BURLEY\n"; + } + + actions[RS::SHADER_SPATIAL].render_mode_defines["diffuse_oren_nayar"] = "#define DIFFUSE_OREN_NAYAR\n"; + actions[RS::SHADER_SPATIAL].render_mode_defines["diffuse_lambert_wrap"] = "#define DIFFUSE_LAMBERT_WRAP\n"; + actions[RS::SHADER_SPATIAL].render_mode_defines["diffuse_toon"] = "#define DIFFUSE_TOON\n"; + + bool force_blinn = GLOBAL_GET("rendering/quality/shading/force_blinn_over_ggx"); + + if (!force_blinn) { + actions[RS::SHADER_SPATIAL].render_mode_defines["specular_schlick_ggx"] = "#define SPECULAR_SCHLICK_GGX\n"; + } else { + actions[RS::SHADER_SPATIAL].render_mode_defines["specular_schlick_ggx"] = "#define SPECULAR_BLINN\n"; + } + + actions[RS::SHADER_SPATIAL].render_mode_defines["specular_blinn"] = "#define SPECULAR_BLINN\n"; + actions[RS::SHADER_SPATIAL].render_mode_defines["specular_phong"] = "#define SPECULAR_PHONG\n"; + actions[RS::SHADER_SPATIAL].render_mode_defines["specular_toon"] = "#define SPECULAR_TOON\n"; + actions[RS::SHADER_SPATIAL].render_mode_defines["specular_disabled"] = "#define SPECULAR_DISABLED\n"; + actions[RS::SHADER_SPATIAL].render_mode_defines["shadows_disabled"] = "#define SHADOWS_DISABLED\n"; + actions[RS::SHADER_SPATIAL].render_mode_defines["ambient_light_disabled"] = "#define AMBIENT_LIGHT_DISABLED\n"; + actions[RS::SHADER_SPATIAL].render_mode_defines["shadow_to_opacity"] = "#define USE_SHADOW_TO_OPACITY\n"; + + /* PARTICLES SHADER */ + + actions[RS::SHADER_PARTICLES].renames["COLOR"] = "out_color"; + actions[RS::SHADER_PARTICLES].renames["VELOCITY"] = "out_velocity_active.xyz"; + actions[RS::SHADER_PARTICLES].renames["MASS"] = "mass"; + actions[RS::SHADER_PARTICLES].renames["ACTIVE"] = "shader_active"; + actions[RS::SHADER_PARTICLES].renames["RESTART"] = "restart"; + actions[RS::SHADER_PARTICLES].renames["CUSTOM"] = "out_custom"; + actions[RS::SHADER_PARTICLES].renames["TRANSFORM"] = "xform"; + actions[RS::SHADER_PARTICLES].renames["TIME"] = "time"; + actions[RS::SHADER_PARTICLES].renames["LIFETIME"] = "lifetime"; + actions[RS::SHADER_PARTICLES].renames["DELTA"] = "local_delta"; + actions[RS::SHADER_PARTICLES].renames["NUMBER"] = "particle_number"; + actions[RS::SHADER_PARTICLES].renames["INDEX"] = "index"; + actions[RS::SHADER_PARTICLES].renames["GRAVITY"] = "current_gravity"; + actions[RS::SHADER_PARTICLES].renames["EMISSION_TRANSFORM"] = "emission_transform"; + actions[RS::SHADER_PARTICLES].renames["RANDOM_SEED"] = "random_seed"; + + actions[RS::SHADER_PARTICLES].render_mode_defines["disable_force"] = "#define DISABLE_FORCE\n"; + actions[RS::SHADER_PARTICLES].render_mode_defines["disable_velocity"] = "#define DISABLE_VELOCITY\n"; + actions[RS::SHADER_PARTICLES].render_mode_defines["keep_data"] = "#define ENABLE_KEEP_DATA\n"; +#endif +} diff --git a/servers/rendering/rasterizer_rd/shader_compiler_rd.h b/servers/rendering/rasterizer_rd/shader_compiler_rd.h new file mode 100644 index 0000000000..7d78469e9c --- /dev/null +++ b/servers/rendering/rasterizer_rd/shader_compiler_rd.h @@ -0,0 +1,123 @@ +/*************************************************************************/ +/* shader_compiler_rd.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef SHADER_COMPILER_RD_H +#define SHADER_COMPILER_RD_H + +#include "core/pair.h" +#include "servers/rendering/shader_language.h" +#include "servers/rendering/shader_types.h" +#include "servers/rendering_server.h" + +class ShaderCompilerRD { +public: + struct IdentifierActions { + + Map<StringName, Pair<int *, int>> render_mode_values; + Map<StringName, bool *> render_mode_flags; + Map<StringName, bool *> usage_flag_pointers; + Map<StringName, bool *> write_flag_pointers; + + Map<StringName, ShaderLanguage::ShaderNode::Uniform> *uniforms; + }; + + struct GeneratedCode { + + Vector<String> defines; + struct Texture { + StringName name; + ShaderLanguage::DataType type; + ShaderLanguage::ShaderNode::Uniform::Hint hint; + ShaderLanguage::TextureFilter filter; + ShaderLanguage::TextureRepeat repeat; + }; + + Vector<Texture> texture_uniforms; + + Vector<uint32_t> uniform_offsets; + uint32_t uniform_total_size; + String uniforms; + String vertex_global; + String vertex; + String fragment_global; + String fragment; + String light; + + bool uses_fragment_time; + bool uses_vertex_time; + }; + + struct DefaultIdentifierActions { + + Map<StringName, String> renames; + Map<StringName, String> render_mode_defines; + Map<StringName, String> usage_defines; + Map<StringName, String> custom_samplers; + ShaderLanguage::TextureFilter default_filter; + ShaderLanguage::TextureRepeat default_repeat; + String sampler_array_name; + int base_texture_binding_index = 0; + int texture_layout_set = 0; + String base_uniform_string; + uint32_t base_varying_index = 0; + }; + +private: + ShaderLanguage parser; + + String _get_sampler_name(ShaderLanguage::TextureFilter p_filter, ShaderLanguage::TextureRepeat p_repeat); + + void _dump_function_deps(const ShaderLanguage::ShaderNode *p_node, const StringName &p_for_func, const Map<StringName, String> &p_func_code, String &r_to_add, Set<StringName> &added); + String _dump_node_code(const ShaderLanguage::Node *p_node, int p_level, GeneratedCode &r_gen_code, IdentifierActions &p_actions, const DefaultIdentifierActions &p_default_actions, bool p_assigning); + + const ShaderLanguage::ShaderNode *shader; + const ShaderLanguage::FunctionNode *function; + StringName current_func_name; + StringName vertex_name; + StringName fragment_name; + StringName light_name; + StringName time_name; + Set<StringName> texture_functions; + + Set<StringName> used_name_defines; + Set<StringName> used_flag_pointers; + Set<StringName> used_rmode_defines; + Set<StringName> internal_functions; + + DefaultIdentifierActions actions; + +public: + Error compile(RS::ShaderMode p_mode, const String &p_code, IdentifierActions *p_actions, const String &p_path, GeneratedCode &r_gen_code); + + void initialize(DefaultIdentifierActions p_actions); + ShaderCompilerRD(); +}; + +#endif // SHADERCOMPILERRD_H diff --git a/servers/rendering/rasterizer_rd/shader_rd.cpp b/servers/rendering/rasterizer_rd/shader_rd.cpp new file mode 100644 index 0000000000..3dcfd0faf9 --- /dev/null +++ b/servers/rendering/rasterizer_rd/shader_rd.cpp @@ -0,0 +1,495 @@ +/*************************************************************************/ +/* shader_rd.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "shader_rd.h" + +#include "core/string_builder.h" +#include "rasterizer_rd.h" +#include "servers/rendering/rendering_device.h" + +void ShaderRD::setup(const char *p_vertex_code, const char *p_fragment_code, const char *p_compute_code, const char *p_name) { + + name = p_name; + //split vertex and shader code (thank you, shader compiler programmers from you know what company). + if (p_vertex_code) { + String defines_tag = "\nVERSION_DEFINES"; + String globals_tag = "\nVERTEX_SHADER_GLOBALS"; + String material_tag = "\nMATERIAL_UNIFORMS"; + String code_tag = "\nVERTEX_SHADER_CODE"; + String code = p_vertex_code; + + int cpos = code.find(defines_tag); + if (cpos != -1) { + vertex_codev = code.substr(0, cpos).ascii(); + code = code.substr(cpos + defines_tag.length(), code.length()); + } + + cpos = code.find(material_tag); + + if (cpos == -1) { + vertex_code0 = code.ascii(); + } else { + vertex_code0 = code.substr(0, cpos).ascii(); + code = code.substr(cpos + material_tag.length(), code.length()); + + cpos = code.find(globals_tag); + + if (cpos == -1) { + vertex_code1 = code.ascii(); + } else { + + vertex_code1 = code.substr(0, cpos).ascii(); + String code2 = code.substr(cpos + globals_tag.length(), code.length()); + + cpos = code2.find(code_tag); + if (cpos == -1) { + vertex_code2 = code2.ascii(); + } else { + + vertex_code2 = code2.substr(0, cpos).ascii(); + vertex_code3 = code2.substr(cpos + code_tag.length(), code2.length()).ascii(); + } + } + } + } + + if (p_fragment_code) { + String defines_tag = "\nVERSION_DEFINES"; + String globals_tag = "\nFRAGMENT_SHADER_GLOBALS"; + String material_tag = "\nMATERIAL_UNIFORMS"; + String code_tag = "\nFRAGMENT_SHADER_CODE"; + String light_code_tag = "\nLIGHT_SHADER_CODE"; + String code = p_fragment_code; + + int cpos = code.find(defines_tag); + if (cpos != -1) { + fragment_codev = code.substr(0, cpos).ascii(); + code = code.substr(cpos + defines_tag.length(), code.length()); + } + + cpos = code.find(material_tag); + if (cpos == -1) { + fragment_code0 = code.ascii(); + } else { + fragment_code0 = code.substr(0, cpos).ascii(); + //print_line("CODE0:\n"+String(fragment_code0.get_data())); + code = code.substr(cpos + material_tag.length(), code.length()); + cpos = code.find(globals_tag); + + if (cpos == -1) { + fragment_code1 = code.ascii(); + } else { + + fragment_code1 = code.substr(0, cpos).ascii(); + //print_line("CODE1:\n"+String(fragment_code1.get_data())); + + String code2 = code.substr(cpos + globals_tag.length(), code.length()); + cpos = code2.find(light_code_tag); + + if (cpos == -1) { + fragment_code2 = code2.ascii(); + } else { + + fragment_code2 = code2.substr(0, cpos).ascii(); + //print_line("CODE2:\n"+String(fragment_code2.get_data())); + + String code3 = code2.substr(cpos + light_code_tag.length(), code2.length()); + + cpos = code3.find(code_tag); + if (cpos == -1) { + fragment_code3 = code3.ascii(); + } else { + + fragment_code3 = code3.substr(0, cpos).ascii(); + //print_line("CODE3:\n"+String(fragment_code3.get_data())); + fragment_code4 = code3.substr(cpos + code_tag.length(), code3.length()).ascii(); + //print_line("CODE4:\n"+String(fragment_code4.get_data())); + } + } + } + } + } + + if (p_compute_code) { + is_compute = true; + + String defines_tag = "\nVERSION_DEFINES"; + String globals_tag = "\nCOMPUTE_SHADER_GLOBALS"; + String material_tag = "\nMATERIAL_UNIFORMS"; + String code_tag = "\nCOMPUTE_SHADER_CODE"; + String code = p_compute_code; + + int cpos = code.find(defines_tag); + if (cpos != -1) { + compute_codev = code.substr(0, cpos).ascii(); + code = code.substr(cpos + defines_tag.length(), code.length()); + } + + cpos = code.find(material_tag); + + if (cpos == -1) { + compute_code0 = code.ascii(); + } else { + compute_code0 = code.substr(0, cpos).ascii(); + code = code.substr(cpos + material_tag.length(), code.length()); + + cpos = code.find(globals_tag); + + if (cpos == -1) { + compute_code1 = code.ascii(); + } else { + + compute_code1 = code.substr(0, cpos).ascii(); + String code2 = code.substr(cpos + globals_tag.length(), code.length()); + + cpos = code2.find(code_tag); + if (cpos == -1) { + compute_code2 = code2.ascii(); + } else { + + compute_code2 = code2.substr(0, cpos).ascii(); + compute_code3 = code2.substr(cpos + code_tag.length(), code2.length()).ascii(); + } + } + } + } +} + +RID ShaderRD::version_create() { + + //initialize() was never called + ERR_FAIL_COND_V(variant_defines.size() == 0, RID()); + + Version version; + version.dirty = true; + version.valid = false; + version.initialize_needed = true; + version.variants = NULL; + return version_owner.make_rid(version); +} + +void ShaderRD::_clear_version(Version *p_version) { + //clear versions if they exist + if (p_version->variants) { + for (int i = 0; i < variant_defines.size(); i++) { + RD::get_singleton()->free(p_version->variants[i]); + } + + memdelete_arr(p_version->variants); + p_version->variants = NULL; + } +} + +void ShaderRD::_compile_variant(uint32_t p_variant, Version *p_version) { + + Vector<RD::ShaderStageData> stages; + + String error; + String current_source; + RD::ShaderStage current_stage = RD::SHADER_STAGE_VERTEX; + bool build_ok = true; + + if (!is_compute) { + //vertex stage + + StringBuilder builder; + + builder.append(vertex_codev.get_data()); // version info (if exists) + builder.append("\n"); //make sure defines begin at newline + builder.append(general_defines.get_data()); + builder.append(variant_defines[p_variant].get_data()); + + for (int j = 0; j < p_version->custom_defines.size(); j++) { + builder.append(p_version->custom_defines[j].get_data()); + } + + builder.append(vertex_code0.get_data()); //first part of vertex + + builder.append(p_version->uniforms.get_data()); //uniforms (same for vertex and fragment) + + builder.append(vertex_code1.get_data()); //second part of vertex + + builder.append(p_version->vertex_globals.get_data()); // vertex globals + + builder.append(vertex_code2.get_data()); //third part of vertex + + builder.append(p_version->vertex_code.get_data()); // code + + builder.append(vertex_code3.get_data()); //fourth of vertex + + current_source = builder.as_string(); + RD::ShaderStageData stage; + stage.spir_v = RD::get_singleton()->shader_compile_from_source(RD::SHADER_STAGE_VERTEX, current_source, RD::SHADER_LANGUAGE_GLSL, &error); + if (stage.spir_v.size() == 0) { + build_ok = false; + } else { + + stage.shader_stage = RD::SHADER_STAGE_VERTEX; + stages.push_back(stage); + } + } + + if (!is_compute && build_ok) { + //fragment stage + current_stage = RD::SHADER_STAGE_FRAGMENT; + + StringBuilder builder; + + builder.append(fragment_codev.get_data()); // version info (if exists) + builder.append("\n"); //make sure defines begin at newline + + builder.append(general_defines.get_data()); + builder.append(variant_defines[p_variant].get_data()); + for (int j = 0; j < p_version->custom_defines.size(); j++) { + builder.append(p_version->custom_defines[j].get_data()); + } + + builder.append(fragment_code0.get_data()); //first part of fragment + + builder.append(p_version->uniforms.get_data()); //uniforms (same for fragment and fragment) + + builder.append(fragment_code1.get_data()); //first part of fragment + + builder.append(p_version->fragment_globals.get_data()); // fragment globals + + builder.append(fragment_code2.get_data()); //third part of fragment + + builder.append(p_version->fragment_light.get_data()); // fragment light + + builder.append(fragment_code3.get_data()); //fourth part of fragment + + builder.append(p_version->fragment_code.get_data()); // fragment code + + builder.append(fragment_code4.get_data()); //fourth part of fragment + + current_source = builder.as_string(); + RD::ShaderStageData stage; + stage.spir_v = RD::get_singleton()->shader_compile_from_source(RD::SHADER_STAGE_FRAGMENT, current_source, RD::SHADER_LANGUAGE_GLSL, &error); + if (stage.spir_v.size() == 0) { + build_ok = false; + } else { + + stage.shader_stage = RD::SHADER_STAGE_FRAGMENT; + stages.push_back(stage); + } + } + + if (is_compute) { + //compute stage + current_stage = RD::SHADER_STAGE_COMPUTE; + + StringBuilder builder; + + builder.append(compute_codev.get_data()); // version info (if exists) + builder.append("\n"); //make sure defines begin at newline + builder.append(general_defines.get_data()); + builder.append(variant_defines[p_variant].get_data()); + + for (int j = 0; j < p_version->custom_defines.size(); j++) { + builder.append(p_version->custom_defines[j].get_data()); + } + + builder.append(compute_code0.get_data()); //first part of compute + + builder.append(p_version->uniforms.get_data()); //uniforms (same for compute and fragment) + + builder.append(compute_code1.get_data()); //second part of compute + + builder.append(p_version->compute_globals.get_data()); // compute globals + + builder.append(compute_code2.get_data()); //third part of compute + + builder.append(p_version->compute_code.get_data()); // code + + builder.append(compute_code3.get_data()); //fourth of compute + + current_source = builder.as_string(); + RD::ShaderStageData stage; + stage.spir_v = RD::get_singleton()->shader_compile_from_source(RD::SHADER_STAGE_COMPUTE, current_source, RD::SHADER_LANGUAGE_GLSL, &error); + if (stage.spir_v.size() == 0) { + build_ok = false; + } else { + + stage.shader_stage = RD::SHADER_STAGE_COMPUTE; + stages.push_back(stage); + } + } + + if (!build_ok) { + MutexLock lock(variant_set_mutex); //properly print the errors + ERR_PRINT("Error compiling " + String(current_stage == RD::SHADER_STAGE_COMPUTE ? "Compute " : (current_stage == RD::SHADER_STAGE_VERTEX ? "Vertex" : "Fragment")) + " shader, variant #" + itos(p_variant) + " (" + variant_defines[p_variant].get_data() + ")."); + ERR_PRINT(error); + +#ifdef DEBUG_ENABLED + ERR_PRINT("code:\n" + current_source.get_with_code_lines()); +#endif + return; + } + + RID shader = RD::get_singleton()->shader_create(stages); + { + MutexLock lock(variant_set_mutex); + p_version->variants[p_variant] = shader; + } +} + +void ShaderRD::_compile_version(Version *p_version) { + + _clear_version(p_version); + + p_version->valid = false; + p_version->dirty = false; + + p_version->variants = memnew_arr(RID, variant_defines.size()); +#if 1 + + RasterizerRD::thread_work_pool.do_work(variant_defines.size(), this, &ShaderRD::_compile_variant, p_version); +#else + for (int i = 0; i < variant_defines.size(); i++) { + + _compile_variant(i, p_version); + } +#endif + + bool all_valid = true; + for (int i = 0; i < variant_defines.size(); i++) { + if (p_version->variants[i].is_null()) { + all_valid = false; + break; + } + } + + if (!all_valid) { + //clear versions if they exist + for (int i = 0; i < variant_defines.size(); i++) { + if (!p_version->variants[i].is_null()) { + RD::get_singleton()->free(p_version->variants[i]); + } + } + memdelete_arr(p_version->variants); + p_version->variants = NULL; + return; + } + + p_version->valid = true; +} + +void ShaderRD::version_set_code(RID p_version, const String &p_uniforms, const String &p_vertex_globals, const String &p_vertex_code, const String &p_fragment_globals, const String &p_fragment_light, const String &p_fragment_code, const Vector<String> &p_custom_defines) { + + ERR_FAIL_COND(is_compute); + + Version *version = version_owner.getornull(p_version); + ERR_FAIL_COND(!version); + version->vertex_globals = p_vertex_globals.utf8(); + version->vertex_code = p_vertex_code.utf8(); + version->fragment_light = p_fragment_light.utf8(); + version->fragment_globals = p_fragment_globals.utf8(); + version->fragment_code = p_fragment_code.utf8(); + version->uniforms = p_uniforms.utf8(); + + version->custom_defines.clear(); + for (int i = 0; i < p_custom_defines.size(); i++) { + version->custom_defines.push_back(p_custom_defines[i].utf8()); + } + + version->dirty = true; + if (version->initialize_needed) { + _compile_version(version); + version->initialize_needed = false; + } +} + +void ShaderRD::version_set_compute_code(RID p_version, const String &p_uniforms, const String &p_compute_globals, const String &p_compute_code, const Vector<String> &p_custom_defines) { + + ERR_FAIL_COND(!is_compute); + + Version *version = version_owner.getornull(p_version); + ERR_FAIL_COND(!version); + version->compute_globals = p_compute_globals.utf8(); + version->compute_code = p_compute_code.utf8(); + version->uniforms = p_uniforms.utf8(); + + version->custom_defines.clear(); + for (int i = 0; i < p_custom_defines.size(); i++) { + version->custom_defines.push_back(p_custom_defines[i].utf8()); + } + + version->dirty = true; + if (version->initialize_needed) { + _compile_version(version); + version->initialize_needed = false; + } +} + +bool ShaderRD::version_is_valid(RID p_version) { + Version *version = version_owner.getornull(p_version); + ERR_FAIL_COND_V(!version, false); + + if (version->dirty) { + _compile_version(version); + } + + return version->valid; +} + +bool ShaderRD::version_free(RID p_version) { + + if (version_owner.owns(p_version)) { + Version *version = version_owner.getornull(p_version); + _clear_version(version); + version_owner.free(p_version); + } else { + return false; + } + + return true; +} + +void ShaderRD::initialize(const Vector<String> &p_variant_defines, const String &p_general_defines) { + ERR_FAIL_COND(variant_defines.size()); + ERR_FAIL_COND(p_variant_defines.size() == 0); + general_defines = p_general_defines.utf8(); + for (int i = 0; i < p_variant_defines.size(); i++) { + + variant_defines.push_back(p_variant_defines[i].utf8()); + } +} + +ShaderRD::~ShaderRD() { + List<RID> remaining; + version_owner.get_owned_list(&remaining); + if (remaining.size()) { + ERR_PRINT(itos(remaining.size()) + " shaders of type " + name + " were never freed"); + while (remaining.size()) { + version_free(remaining.front()->get()); + remaining.pop_front(); + } + } +} diff --git a/servers/rendering/rasterizer_rd/shader_rd.h b/servers/rendering/rasterizer_rd/shader_rd.h new file mode 100644 index 0000000000..6635b08cc8 --- /dev/null +++ b/servers/rendering/rasterizer_rd/shader_rd.h @@ -0,0 +1,136 @@ +/*************************************************************************/ +/* shader_rd.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef SHADER_RD_H +#define SHADER_RD_H + +#include "core/hash_map.h" +#include "core/map.h" +#include "core/os/mutex.h" +#include "core/rid_owner.h" +#include "core/variant.h" + +#include <stdio.h> +/** + @author Juan Linietsky <reduzio@gmail.com> +*/ + +class ShaderRD { + + //versions + CharString general_defines; + Vector<CharString> variant_defines; + + struct Version { + CharString uniforms; + CharString vertex_globals; + CharString vertex_code; + CharString compute_globals; + CharString compute_code; + CharString fragment_light; + CharString fragment_globals; + CharString fragment_code; + Vector<CharString> custom_defines; + + RID *variants; //same size as version defines + + bool valid; + bool dirty; + bool initialize_needed; + }; + + Mutex variant_set_mutex; + + void _compile_variant(uint32_t p_variant, Version *p_version); + + void _clear_version(Version *p_version); + void _compile_version(Version *p_version); + + RID_Owner<Version> version_owner; + + CharString fragment_codev; //for version and extensions + CharString fragment_code0; + CharString fragment_code1; + CharString fragment_code2; + CharString fragment_code3; + CharString fragment_code4; + + CharString vertex_codev; //for version and extensions + CharString vertex_code0; + CharString vertex_code1; + CharString vertex_code2; + CharString vertex_code3; + + bool is_compute = false; + + CharString compute_codev; //for version and extensions + CharString compute_code0; + CharString compute_code1; + CharString compute_code2; + CharString compute_code3; + + const char *name; + +protected: + ShaderRD() {} + void setup(const char *p_vertex_code, const char *p_fragment_code, const char *p_compute_code, const char *p_name); + +public: + RID version_create(); + + void version_set_code(RID p_version, const String &p_uniforms, const String &p_vertex_globals, const String &p_vertex_code, const String &p_fragment_globals, const String &p_fragment_light, const String &p_fragment_code, const Vector<String> &p_custom_defines); + void version_set_compute_code(RID p_version, const String &p_uniforms, const String &p_compute_globals, const String &p_compute_code, const Vector<String> &p_custom_defines); + + _FORCE_INLINE_ RID version_get_shader(RID p_version, int p_variant) { + ERR_FAIL_INDEX_V(p_variant, variant_defines.size(), RID()); + + Version *version = version_owner.getornull(p_version); + ERR_FAIL_COND_V(!version, RID()); + + if (version->dirty) { + _compile_version(version); + } + + if (!version->valid) { + return RID(); + } + + return version->variants[p_variant]; + } + + bool version_is_valid(RID p_version); + + bool version_free(RID p_version); + + void initialize(const Vector<String> &p_variant_defines, const String &p_general_defines = ""); + virtual ~ShaderRD(); +}; + +#endif diff --git a/servers/rendering/rasterizer_rd/shaders/SCsub b/servers/rendering/rasterizer_rd/shaders/SCsub new file mode 100644 index 0000000000..2dcb2a703f --- /dev/null +++ b/servers/rendering/rasterizer_rd/shaders/SCsub @@ -0,0 +1,24 @@ +#!/usr/bin/env python + +Import('env') + +if 'RD_GLSL' in env['BUILDERS']: + env.RD_GLSL('canvas.glsl'); + env.RD_GLSL('canvas_occlusion.glsl'); + env.RD_GLSL('blur.glsl'); + env.RD_GLSL('cubemap_roughness.glsl'); + env.RD_GLSL('cubemap_downsampler.glsl'); + env.RD_GLSL('cubemap_filter.glsl'); + env.RD_GLSL('scene_high_end.glsl'); + env.RD_GLSL('sky.glsl'); + env.RD_GLSL('tonemap.glsl'); + env.RD_GLSL('copy.glsl'); + env.RD_GLSL('giprobe.glsl'); + env.RD_GLSL('giprobe_debug.glsl'); + env.RD_GLSL('giprobe_sdf.glsl'); + env.RD_GLSL('luminance_reduce.glsl'); + env.RD_GLSL('bokeh_dof.glsl'); + env.RD_GLSL('ssao.glsl'); + env.RD_GLSL('ssao_minify.glsl'); + env.RD_GLSL('ssao_blur.glsl'); + env.RD_GLSL('roughness_limiter.glsl'); diff --git a/servers/rendering/rasterizer_rd/shaders/blur.glsl b/servers/rendering/rasterizer_rd/shaders/blur.glsl new file mode 100644 index 0000000000..87c20ebaef --- /dev/null +++ b/servers/rendering/rasterizer_rd/shaders/blur.glsl @@ -0,0 +1,294 @@ +/* clang-format off */ +[vertex] + +#version 450 + +VERSION_DEFINES + +#include "blur_inc.glsl" + +layout(location = 0) out vec2 uv_interp; +/* clang-format on */ + +void main() { + + vec2 base_arr[4] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0)); + uv_interp = base_arr[gl_VertexIndex]; + + if (bool(blur.flags & FLAG_USE_BLUR_SECTION)) { + uv_interp = blur.section.xy + uv_interp * blur.section.zw; + } + + gl_Position = vec4(uv_interp * 2.0 - 1.0, 0.0, 1.0); + + if (bool(blur.flags & FLAG_FLIP_Y)) { + uv_interp.y = 1.0 - uv_interp.y; + } +} + +/* clang-format off */ +[fragment] + +#version 450 + +VERSION_DEFINES + +#include "blur_inc.glsl" + +layout(location = 0) in vec2 uv_interp; +/* clang-format on */ + +layout(set = 0, binding = 0) uniform sampler2D source_color; + +#ifdef MODE_SSAO_MERGE +layout(set = 1, binding = 0) uniform sampler2D source_ssao; +#endif + +#ifdef GLOW_USE_AUTO_EXPOSURE +layout(set = 1, binding = 0) uniform sampler2D source_auto_exposure; +#endif + +layout(location = 0) out vec4 frag_color; + +//DOF +#if defined(MODE_DOF_FAR_BLUR) || defined(MODE_DOF_NEAR_BLUR) + +layout(set = 1, binding = 0) uniform sampler2D dof_source_depth; + +#ifdef DOF_NEAR_BLUR_MERGE +layout(set = 2, binding = 0) uniform sampler2D source_dof_original; +#endif + +#ifdef DOF_QUALITY_LOW +const int dof_kernel_size = 5; +const int dof_kernel_from = 2; +const float dof_kernel[5] = float[](0.153388, 0.221461, 0.250301, 0.221461, 0.153388); +#endif + +#ifdef DOF_QUALITY_MEDIUM +const int dof_kernel_size = 11; +const int dof_kernel_from = 5; +const float dof_kernel[11] = float[](0.055037, 0.072806, 0.090506, 0.105726, 0.116061, 0.119726, 0.116061, 0.105726, 0.090506, 0.072806, 0.055037); + +#endif + +#ifdef DOF_QUALITY_HIGH +const int dof_kernel_size = 21; +const int dof_kernel_from = 10; +const float dof_kernel[21] = float[](0.028174, 0.032676, 0.037311, 0.041944, 0.046421, 0.050582, 0.054261, 0.057307, 0.059587, 0.060998, 0.061476, 0.060998, 0.059587, 0.057307, 0.054261, 0.050582, 0.046421, 0.041944, 0.037311, 0.032676, 0.028174); +#endif + +#endif + +void main() { + +#ifdef MODE_MIPMAP + + vec2 pix_size = blur.pixel_size; + vec4 color = texture(source_color, uv_interp + vec2(-0.5, -0.5) * pix_size); + color += texture(source_color, uv_interp + vec2(0.5, -0.5) * pix_size); + color += texture(source_color, uv_interp + vec2(0.5, 0.5) * pix_size); + color += texture(source_color, uv_interp + vec2(-0.5, 0.5) * pix_size); + frag_color = color / 4.0; + +#endif + +#ifdef MODE_GAUSSIAN_BLUR + + //Simpler blur uses SIGMA2 for the gaussian kernel for a stronger effect + + if (bool(blur.flags & FLAG_HORIZONTAL)) { + + vec2 pix_size = blur.pixel_size; + pix_size *= 0.5; //reading from larger buffer, so use more samples + vec4 color = texture(source_color, uv_interp + vec2(0.0, 0.0) * pix_size) * 0.214607; + color += texture(source_color, uv_interp + vec2(1.0, 0.0) * pix_size) * 0.189879; + color += texture(source_color, uv_interp + vec2(2.0, 0.0) * pix_size) * 0.131514; + color += texture(source_color, uv_interp + vec2(3.0, 0.0) * pix_size) * 0.071303; + color += texture(source_color, uv_interp + vec2(-1.0, 0.0) * pix_size) * 0.189879; + color += texture(source_color, uv_interp + vec2(-2.0, 0.0) * pix_size) * 0.131514; + color += texture(source_color, uv_interp + vec2(-3.0, 0.0) * pix_size) * 0.071303; + frag_color = color; + } else { + + vec2 pix_size = blur.pixel_size; + vec4 color = texture(source_color, uv_interp + vec2(0.0, 0.0) * pix_size) * 0.38774; + color += texture(source_color, uv_interp + vec2(0.0, 1.0) * pix_size) * 0.24477; + color += texture(source_color, uv_interp + vec2(0.0, 2.0) * pix_size) * 0.06136; + color += texture(source_color, uv_interp + vec2(0.0, -1.0) * pix_size) * 0.24477; + color += texture(source_color, uv_interp + vec2(0.0, -2.0) * pix_size) * 0.06136; + frag_color = color; + } +#endif + +#ifdef MODE_GAUSSIAN_GLOW + + //Glow uses larger sigma 1 for a more rounded blur effect + +#define GLOW_ADD(m_ofs, m_mult) \ + { \ + vec2 ofs = uv_interp + m_ofs * pix_size; \ + vec4 c = texture(source_color, ofs) * m_mult; \ + if (any(lessThan(ofs, vec2(0.0))) || any(greaterThan(ofs, vec2(1.0)))) { \ + c *= 0.0; \ + } \ + color += c; \ + } + + if (bool(blur.flags & FLAG_HORIZONTAL)) { + + vec2 pix_size = blur.pixel_size; + pix_size *= 0.5; //reading from larger buffer, so use more samples + vec4 color = texture(source_color, uv_interp + vec2(0.0, 0.0) * pix_size) * 0.174938; + GLOW_ADD(vec2(1.0, 0.0), 0.165569); + GLOW_ADD(vec2(2.0, 0.0), 0.140367); + GLOW_ADD(vec2(3.0, 0.0), 0.106595); + GLOW_ADD(vec2(-1.0, 0.0), 0.165569); + GLOW_ADD(vec2(-2.0, 0.0), 0.140367); + GLOW_ADD(vec2(-3.0, 0.0), 0.106595); + color *= blur.glow_strength; + frag_color = color; + } else { + + vec2 pix_size = blur.pixel_size; + vec4 color = texture(source_color, uv_interp + vec2(0.0, 0.0) * pix_size) * 0.288713; + GLOW_ADD(vec2(0.0, 1.0), 0.233062); + GLOW_ADD(vec2(0.0, 2.0), 0.122581); + GLOW_ADD(vec2(0.0, -1.0), 0.233062); + GLOW_ADD(vec2(0.0, -2.0), 0.122581); + color *= blur.glow_strength; + frag_color = color; + } + +#undef GLOW_ADD + + if (bool(blur.flags & FLAG_GLOW_FIRST_PASS)) { +#ifdef GLOW_USE_AUTO_EXPOSURE + + frag_color /= texelFetch(source_auto_exposure, ivec2(0, 0), 0).r / blur.glow_auto_exposure_grey; +#endif + frag_color *= blur.glow_exposure; + + float luminance = max(frag_color.r, max(frag_color.g, frag_color.b)); + float feedback = max(smoothstep(blur.glow_hdr_threshold, blur.glow_hdr_threshold + blur.glow_hdr_scale, luminance), blur.glow_bloom); + + frag_color = min(frag_color * feedback, vec4(blur.glow_luminance_cap)); + } + +#endif + +#ifdef MODE_DOF_FAR_BLUR + + vec4 color_accum = vec4(0.0); + + float depth = texture(dof_source_depth, uv_interp, 0.0).r; + depth = depth * 2.0 - 1.0; + + if (bool(blur.flags & FLAG_USE_ORTHOGONAL_PROJECTION)) { + depth = ((depth + (blur.camera_z_far + blur.camera_z_near) / (blur.camera_z_far - blur.camera_z_near)) * (blur.camera_z_far - blur.camera_z_near)) / 2.0; + } else { + depth = 2.0 * blur.camera_z_near * blur.camera_z_far / (blur.camera_z_far + blur.camera_z_near - depth * (blur.camera_z_far - blur.camera_z_near)); + } + + float amount = smoothstep(blur.dof_begin, blur.dof_end, depth); + float k_accum = 0.0; + + for (int i = 0; i < dof_kernel_size; i++) { + + int int_ofs = i - dof_kernel_from; + vec2 tap_uv = uv_interp + blur.dof_dir * float(int_ofs) * amount * blur.dof_radius; + + float tap_k = dof_kernel[i]; + + float tap_depth = texture(dof_source_depth, tap_uv, 0.0).r; + tap_depth = tap_depth * 2.0 - 1.0; + + if (bool(blur.flags & FLAG_USE_ORTHOGONAL_PROJECTION)) { + + tap_depth = ((tap_depth + (blur.camera_z_far + blur.camera_z_near) / (blur.camera_z_far - blur.camera_z_near)) * (blur.camera_z_far - blur.camera_z_near)) / 2.0; + } else { + tap_depth = 2.0 * blur.camera_z_near * blur.camera_z_far / (blur.camera_z_far + blur.camera_z_near - tap_depth * (blur.camera_z_far - blur.camera_z_near)); + } + + float tap_amount = mix(smoothstep(blur.dof_begin, blur.dof_end, tap_depth), 1.0, int_ofs == 0); + tap_amount *= tap_amount * tap_amount; //prevent undesired glow effect + + vec4 tap_color = texture(source_color, tap_uv, 0.0) * tap_k; + + k_accum += tap_k * tap_amount; + color_accum += tap_color * tap_amount; + } + + if (k_accum > 0.0) { + color_accum /= k_accum; + } + + frag_color = color_accum; ///k_accum; + +#endif + +#ifdef MODE_DOF_NEAR_BLUR + + vec4 color_accum = vec4(0.0); + + float max_accum = 0.0; + + for (int i = 0; i < dof_kernel_size; i++) { + + int int_ofs = i - dof_kernel_from; + vec2 tap_uv = uv_interp + blur.dof_dir * float(int_ofs) * blur.dof_radius; + float ofs_influence = max(0.0, 1.0 - float(abs(int_ofs)) / float(dof_kernel_from)); + + float tap_k = dof_kernel[i]; + + vec4 tap_color = texture(source_color, tap_uv, 0.0); + + float tap_depth = texture(dof_source_depth, tap_uv, 0.0).r; + tap_depth = tap_depth * 2.0 - 1.0; + if (bool(blur.flags & FLAG_USE_ORTHOGONAL_PROJECTION)) { + + tap_depth = ((tap_depth + (blur.camera_z_far + blur.camera_z_near) / (blur.camera_z_far - blur.camera_z_near)) * (blur.camera_z_far - blur.camera_z_near)) / 2.0; + } else { + tap_depth = 2.0 * blur.camera_z_near * blur.camera_z_far / (blur.camera_z_far + blur.camera_z_near - tap_depth * (blur.camera_z_far - blur.camera_z_near)); + } + float tap_amount = 1.0 - smoothstep(blur.dof_end, blur.dof_begin, tap_depth); + tap_amount *= tap_amount * tap_amount; //prevent undesired glow effect + + if (bool(blur.flags & FLAG_DOF_NEAR_FIRST_TAP)) { + tap_color.a = 1.0 - smoothstep(blur.dof_end, blur.dof_begin, tap_depth); + } + + max_accum = max(max_accum, tap_amount * ofs_influence); + + color_accum += tap_color * tap_k; + } + + color_accum.a = max(color_accum.a, sqrt(max_accum)); + +#ifdef DOF_NEAR_BLUR_MERGE + { + vec4 original = texture(source_dof_original, uv_interp, 0.0); + color_accum = mix(original, color_accum, color_accum.a); + } +#endif + + if (bool(blur.flags & FLAG_DOF_NEAR_FIRST_TAP)) { + frag_color = color_accum; + } +#endif + +#ifdef MODE_SIMPLE_COPY + vec4 color = texture(source_color, uv_interp, 0.0); + if (bool(blur.flags & FLAG_COPY_FORCE_LUMINANCE)) { + color.rgb = vec3(max(max(color.r, color.g), color.b)); + } + frag_color = color; +#endif + +#ifdef MODE_SSAO_MERGE + vec4 color = texture(source_color, uv_interp, 0.0); + float ssao = texture(source_ssao, uv_interp, 0.0).r; + frag_color = vec4(mix(color.rgb, color.rgb * mix(blur.ssao_color.rgb, vec3(1.0), ssao), color.a), 1.0); + +#endif +} diff --git a/servers/rendering/rasterizer_rd/shaders/blur_inc.glsl b/servers/rendering/rasterizer_rd/shaders/blur_inc.glsl new file mode 100644 index 0000000000..33ba9de7bb --- /dev/null +++ b/servers/rendering/rasterizer_rd/shaders/blur_inc.glsl @@ -0,0 +1,35 @@ +#define FLAG_HORIZONTAL (1 << 0) +#define FLAG_USE_BLUR_SECTION (1 << 1) +#define FLAG_USE_ORTHOGONAL_PROJECTION (1 << 2) +#define FLAG_DOF_NEAR_FIRST_TAP (1 << 3) +#define FLAG_GLOW_FIRST_PASS (1 << 4) +#define FLAG_FLIP_Y (1 << 5) +#define FLAG_COPY_FORCE_LUMINANCE (1 << 6) + +layout(push_constant, binding = 1, std430) uniform Blur { + vec4 section; + vec2 pixel_size; + uint flags; + uint pad; + // Glow. + float glow_strength; + float glow_bloom; + float glow_hdr_threshold; + float glow_hdr_scale; + float glow_exposure; + float glow_white; + float glow_luminance_cap; + float glow_auto_exposure_grey; + // DOF. + float dof_begin; + float dof_end; + float dof_radius; + float dof_pad; + + vec2 dof_dir; + float camera_z_far; + float camera_z_near; + + vec4 ssao_color; +} +blur; diff --git a/servers/rendering/rasterizer_rd/shaders/bokeh_dof.glsl b/servers/rendering/rasterizer_rd/shaders/bokeh_dof.glsl new file mode 100644 index 0000000000..7153fe6b17 --- /dev/null +++ b/servers/rendering/rasterizer_rd/shaders/bokeh_dof.glsl @@ -0,0 +1,258 @@ +/* clang-format off */ +[compute] + +#version 450 + +VERSION_DEFINES + +#define BLOCK_SIZE 8 + +layout(local_size_x = BLOCK_SIZE, local_size_y = BLOCK_SIZE, local_size_z = 1) in; +/* clang-format on */ + +#ifdef MODE_GEN_BLUR_SIZE +layout(rgba16f, set = 0, binding = 0) uniform restrict image2D color_image; +layout(set = 1, binding = 0) uniform sampler2D source_depth; +#endif + +#if defined(MODE_BOKEH_BOX) || defined(MODE_BOKEH_HEXAGONAL) || defined(MODE_BOKEH_CIRCULAR) +layout(set = 1, binding = 0) uniform sampler2D color_texture; +layout(rgba16f, set = 0, binding = 0) uniform restrict writeonly image2D bokeh_image; +#endif + +#ifdef MODE_COMPOSITE_BOKEH +layout(rgba16f, set = 0, binding = 0) uniform restrict image2D color_image; +layout(set = 1, binding = 0) uniform sampler2D source_bokeh; +#endif + +// based on https://www.shadertoy.com/view/Xd3GDl + +layout(push_constant, binding = 1, std430) uniform Params { + ivec2 size; + float z_far; + float z_near; + + bool orthogonal; + float blur_size; + float blur_scale; + int blur_steps; + + bool blur_near_active; + float blur_near_begin; + float blur_near_end; + bool blur_far_active; + + float blur_far_begin; + float blur_far_end; + bool second_pass; + bool half_size; + + bool use_jitter; + float jitter_seed; + uint pad[2]; +} +params; + +//used to work around downsampling filter +#define DEPTH_GAP 0.0 + +#ifdef MODE_GEN_BLUR_SIZE + +float get_depth_at_pos(vec2 uv) { + float depth = textureLod(source_depth, uv, 0.0).x; + if (params.orthogonal) { + depth = ((depth + (params.z_far + params.z_near) / (params.z_far - params.z_near)) * (params.z_far - params.z_near)) / 2.0; + } else { + depth = 2.0 * params.z_near * params.z_far / (params.z_far + params.z_near - depth * (params.z_far - params.z_near)); + } + return depth; +} + +float get_blur_size(float depth) { + + if (params.blur_near_active && depth < params.blur_near_begin) { + return -(1.0 - smoothstep(params.blur_near_end, params.blur_near_begin, depth)) * params.blur_size - DEPTH_GAP; //near blur is negative + } + + if (params.blur_far_active && depth > params.blur_far_begin) { + return smoothstep(params.blur_far_begin, params.blur_far_end, depth) * params.blur_size + DEPTH_GAP; + } + + return 0.0; +} + +#endif + +const float GOLDEN_ANGLE = 2.39996323; + +//note: uniform pdf rand [0;1[ +float hash12n(vec2 p) { + p = fract(p * vec2(5.3987, 5.4421)); + p += dot(p.yx, p.xy + vec2(21.5351, 14.3137)); + return fract(p.x * p.y * 95.4307); +} + +#if defined(MODE_BOKEH_BOX) || defined(MODE_BOKEH_HEXAGONAL) + +vec4 weighted_filter_dir(vec2 dir, vec2 uv, vec2 pixel_size) { + + dir *= pixel_size; + vec4 color = texture(color_texture, uv); + + vec4 accum = color; + float total = 1.0; + + float blur_scale = params.blur_size / float(params.blur_steps); + + if (params.use_jitter) { + uv += dir * (hash12n(uv + params.jitter_seed) - 0.5); + } + + for (int i = -params.blur_steps; i <= params.blur_steps; i++) { + + if (i == 0) { + continue; + } + float radius = float(i) * blur_scale; + vec2 suv = uv + dir * radius; + radius = abs(radius); + + vec4 sample_color = texture(color_texture, suv); + float limit; + + if (sample_color.a < color.a) { + limit = abs(sample_color.a); + } else { + limit = abs(color.a); + } + + limit -= DEPTH_GAP; + + float m = smoothstep(radius - 0.5, radius + 0.5, limit); + + accum += mix(color, sample_color, m); + + total += 1.0; + } + + return accum / total; +} + +#endif + +void main() { + + ivec2 pos = ivec2(gl_GlobalInvocationID.xy); + + if (any(greaterThan(pos, params.size))) { //too large, do nothing + return; + } + + vec2 pixel_size = 1.0 / vec2(params.size); + vec2 uv = vec2(pos) / vec2(params.size); + +#ifdef MODE_GEN_BLUR_SIZE + uv += pixel_size * 0.5; + //precompute size in alpha channel + float depth = get_depth_at_pos(uv); + float size = get_blur_size(depth); + + vec4 color = imageLoad(color_image, pos); + color.a = size; + imageStore(color_image, pos, color); +#endif + +#ifdef MODE_BOKEH_BOX + + //pixel_size*=0.5; //resolution is doubled + if (params.second_pass || !params.half_size) { + uv += pixel_size * 0.5; //half pixel to read centers + } else { + uv += pixel_size * 0.25; //half pixel to read centers from full res + } + + vec2 dir = (params.second_pass ? vec2(0.0, 1.0) : vec2(1.0, 0.0)); + + vec4 color = weighted_filter_dir(dir, uv, pixel_size); + + imageStore(bokeh_image, pos, color); + +#endif + +#ifdef MODE_BOKEH_HEXAGONAL + + //pixel_size*=0.5; //resolution is doubled + if (params.second_pass || !params.half_size) { + uv += pixel_size * 0.5; //half pixel to read centers + } else { + uv += pixel_size * 0.25; //half pixel to read centers from full res + } + + vec2 dir = (params.second_pass ? normalize(vec2(1.0, 0.577350269189626)) : vec2(0.0, 1.0)); + + vec4 color = weighted_filter_dir(dir, uv, pixel_size); + + if (params.second_pass) { + dir = normalize(vec2(-1.0, 0.577350269189626)); + + vec4 color2 = weighted_filter_dir(dir, uv, pixel_size); + + color.rgb = min(color.rgb, color2.rgb); + color.a = (color.a + color2.a) * 0.5; + } + + imageStore(bokeh_image, pos, color); + +#endif + +#ifdef MODE_BOKEH_CIRCULAR + + if (params.half_size) { + pixel_size *= 0.5; //resolution is doubled + } + + uv += pixel_size * 0.5; //half pixel to read centers + + vec4 color = texture(color_texture, uv); + float accum = 1.0; + float radius = params.blur_scale; + + for (float ang = 0.0; radius < params.blur_size; ang += GOLDEN_ANGLE) { + + vec2 suv = uv + vec2(cos(ang), sin(ang)) * pixel_size * radius; + vec4 sample_color = texture(color_texture, suv); + float sample_size = abs(sample_color.a); + if (sample_color.a > color.a) { + sample_size = clamp(sample_size, 0.0, abs(color.a) * 2.0); + } + + float m = smoothstep(radius - 0.5, radius + 0.5, sample_size); + color += mix(color / accum, sample_color, m); + accum += 1.0; + radius += params.blur_scale / radius; + } + + color /= accum; + + imageStore(bokeh_image, pos, color); +#endif + +#ifdef MODE_COMPOSITE_BOKEH + + uv += pixel_size * 0.5; + vec4 color = imageLoad(color_image, pos); + vec4 bokeh = texture(source_bokeh, uv); + + float mix_amount; + if (bokeh.a < color.a) { + mix_amount = min(1.0, max(0.0, max(abs(color.a), abs(bokeh.a)) - DEPTH_GAP)); + } else { + mix_amount = min(1.0, max(0.0, abs(color.a) - DEPTH_GAP)); + } + + color.rgb = mix(color.rgb, bokeh.rgb, mix_amount); //blend between hires and lowres + + color.a = 0; //reset alpha + imageStore(color_image, pos, color); +#endif +} diff --git a/servers/rendering/rasterizer_rd/shaders/canvas.glsl b/servers/rendering/rasterizer_rd/shaders/canvas.glsl new file mode 100644 index 0000000000..28135fce31 --- /dev/null +++ b/servers/rendering/rasterizer_rd/shaders/canvas.glsl @@ -0,0 +1,584 @@ +/* clang-format off */ +[vertex] + +#version 450 + +VERSION_DEFINES + +#ifdef USE_ATTRIBUTES +layout(location = 0) in vec2 vertex_attrib; +/* clang-format on */ +layout(location = 3) in vec4 color_attrib; +layout(location = 4) in vec2 uv_attrib; + +layout(location = 6) in uvec4 bones_attrib; + +#endif + +#include "canvas_uniforms_inc.glsl" + +layout(location = 0) out vec2 uv_interp; +layout(location = 1) out vec4 color_interp; +layout(location = 2) out vec2 vertex_interp; + +#ifdef USE_NINEPATCH + +layout(location = 3) out vec2 pixel_size_interp; + +#endif + +#ifdef USE_MATERIAL_UNIFORMS +layout(set = 1, binding = 1, std140) uniform MaterialUniforms{ + /* clang-format off */ +MATERIAL_UNIFORMS + /* clang-format on */ +} material; +#endif + +/* clang-format off */ +VERTEX_SHADER_GLOBALS +/* clang-format on */ + +void main() { + + vec4 instance_custom = vec4(0.0); +#ifdef USE_PRIMITIVE + + //weird bug, + //this works + vec2 vertex; + vec2 uv; + vec4 color; + + if (gl_VertexIndex == 0) { + vertex = draw_data.points[0]; + uv = draw_data.uvs[0]; + color = vec4(unpackHalf2x16(draw_data.colors[0]), unpackHalf2x16(draw_data.colors[1])); + } else if (gl_VertexIndex == 1) { + vertex = draw_data.points[1]; + uv = draw_data.uvs[1]; + color = vec4(unpackHalf2x16(draw_data.colors[2]), unpackHalf2x16(draw_data.colors[3])); + } else { + vertex = draw_data.points[2]; + uv = draw_data.uvs[2]; + color = vec4(unpackHalf2x16(draw_data.colors[4]), unpackHalf2x16(draw_data.colors[5])); + } + uvec4 bones = uvec4(0, 0, 0, 0); + +#elif defined(USE_ATTRIBUTES) + + vec2 vertex = vertex_attrib; + vec4 color = color_attrib; + vec2 uv = uv_attrib; + + uvec4 bones = bones_attrib; +#else + + vec2 vertex_base_arr[4] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0)); + vec2 vertex_base = vertex_base_arr[gl_VertexIndex]; + + vec2 uv = draw_data.src_rect.xy + abs(draw_data.src_rect.zw) * ((draw_data.flags & FLAGS_TRANSPOSE_RECT) != 0 ? vertex_base.yx : vertex_base.xy); + vec4 color = draw_data.modulation; + vec2 vertex = draw_data.dst_rect.xy + abs(draw_data.dst_rect.zw) * mix(vertex_base, vec2(1.0, 1.0) - vertex_base, lessThan(draw_data.src_rect.zw, vec2(0.0, 0.0))); + uvec4 bones = uvec4(0, 0, 0, 0); + +#endif + + mat4 world_matrix = mat4(vec4(draw_data.world_x, 0.0, 0.0), vec4(draw_data.world_y, 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(draw_data.world_ofs, 0.0, 1.0)); + +#if 0 + if (draw_data.flags & FLAGS_INSTANCING_ENABLED) { + + uint offset = draw_data.flags & FLAGS_INSTANCING_STRIDE_MASK; + offset *= gl_InstanceIndex; + mat4 instance_xform = mat4( + vec4(texelFetch(instancing_buffer, offset + 0), texelFetch(instancing_buffer, offset + 1), 0.0, texelFetch(instancing_buffer, offset + 3)), + vec4(texelFetch(instancing_buffer, offset + 4), texelFetch(instancing_buffer, offset + 5), 0.0, texelFetch(instancing_buffer, offset + 7)), + vec4(0.0, 0.0, 1.0, 0.0), + vec4(0.0, 0.0, 0.0, 1.0)); + offset += 8; + if (draw_data.flags & FLAGS_INSTANCING_HAS_COLORS) { + vec4 instance_color; + if (draw_data.flags & FLAGS_INSTANCING_COLOR_8_BIT) { + uint bits = floatBitsToUint(texelFetch(instancing_buffer, offset)); + instance_color = unpackUnorm4x8(bits); + offset += 1; + } else { + instance_color = vec4(texelFetch(instancing_buffer, offset + 0), texelFetch(instancing_buffer, offset + 1), texelFetch(instancing_buffer, offset + 2), texelFetch(instancing_buffer, offset + 3)); + offser += 4; + } + + color *= instance_color; + } + if (draw_data.flags & FLAGS_INSTANCING_HAS_CUSTOM_DATA) { + if (draw_data.flags & FLAGS_INSTANCING_CUSTOM_DATA_8_BIT) { + uint bits = floatBitsToUint(texelFetch(instancing_buffer, offset)); + instance_custom = unpackUnorm4x8(bits); + } else { + instance_custom = vec4(texelFetch(instancing_buffer, offset + 0), texelFetch(instancing_buffer, offset + 1), texelFetch(instancing_buffer, offset + 2), texelFetch(instancing_buffer, offset + 3)); + } + } + } + +#endif + +#if !defined(USE_ATTRIBUTES) && !defined(USE_PRIMITIVE) + if (bool(draw_data.flags & FLAGS_USING_PARTICLES)) { + //scale by texture size + vertex /= draw_data.color_texture_pixel_size; + } +#endif + +#ifdef USE_POINT_SIZE + float point_size = 1.0; +#endif + { + /* clang-format off */ +VERTEX_SHADER_CODE + /* clang-format on */ + } + +#ifdef USE_NINEPATCH + pixel_size_interp = abs(draw_data.dst_rect.zw) * vertex_base; +#endif + +#if !defined(SKIP_TRANSFORM_USED) + vertex = (world_matrix * vec4(vertex, 0.0, 1.0)).xy; +#endif + + color_interp = color; + + if (bool(draw_data.flags & FLAGS_USE_PIXEL_SNAP)) { + + vertex = floor(vertex + 0.5); + // precision issue on some hardware creates artifacts within texture + // offset uv by a small amount to avoid + uv += 1e-5; + } + +#ifdef USE_ATTRIBUTES +#if 0 + if (bool(draw_data.flags & FLAGS_USE_SKELETON) && bone_weights != vec4(0.0)) { //must be a valid bone + //skeleton transform + + ivec4 bone_indicesi = ivec4(bone_indices); + + uvec2 tex_ofs = bone_indicesi.x * 2; + + mat2x4 m; + m = mat2x4( + texelFetch(skeleton_buffer, tex_ofs + 0), + texelFetch(skeleton_buffer, tex_ofs + 1)) * + bone_weights.x; + + tex_ofs = bone_indicesi.y * 2; + + m += mat2x4( + texelFetch(skeleton_buffer, tex_ofs + 0), + texelFetch(skeleton_buffer, tex_ofs + 1)) * + bone_weights.y; + + tex_ofs = bone_indicesi.z * 2; + + m += mat2x4( + texelFetch(skeleton_buffer, tex_ofs + 0), + texelFetch(skeleton_buffer, tex_ofs + 1)) * + bone_weights.z; + + tex_ofs = bone_indicesi.w * 2; + + m += mat2x4( + texelFetch(skeleton_buffer, tex_ofs + 0), + texelFetch(skeleton_buffer, tex_ofs + 1)) * + bone_weights.w; + + mat4 bone_matrix = skeleton_data.skeleton_transform * transpose(mat4(m[0], m[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))) * skeleton_data.skeleton_transform_inverse; + + //outvec = bone_matrix * outvec; + } +#endif +#endif + + vertex = (canvas_data.canvas_transform * vec4(vertex, 0.0, 1.0)).xy; + + vertex_interp = vertex; + uv_interp = uv; + + gl_Position = canvas_data.screen_transform * vec4(vertex, 0.0, 1.0); + +#ifdef USE_POINT_SIZE + gl_PointSize = point_size; +#endif +} + +/* clang-format off */ +[fragment] + +#version 450 + +VERSION_DEFINES + +#include "canvas_uniforms_inc.glsl" + +layout(location = 0) in vec2 uv_interp; +/* clang-format on */ +layout(location = 1) in vec4 color_interp; +layout(location = 2) in vec2 vertex_interp; + +#ifdef USE_NINEPATCH + +layout(location = 3) in vec2 pixel_size_interp; + +#endif + +layout(location = 0) out vec4 frag_color; + +#ifdef USE_MATERIAL_UNIFORMS +layout(set = 1, binding = 1, std140) uniform MaterialUniforms{ + /* clang-format off */ +MATERIAL_UNIFORMS + /* clang-format on */ +} material; +#endif + +/* clang-format off */ +FRAGMENT_SHADER_GLOBALS +/* clang-format on */ + +#ifdef LIGHT_SHADER_CODE_USED + +vec4 light_compute( + vec3 light_vertex, + vec3 light_position, + vec3 normal, + vec4 light_color, + float light_energy, + vec4 specular_shininess, + inout vec4 shadow_modulate, + vec2 screen_uv, + vec2 uv, + vec4 color) { + + vec4 light = vec4(0.0); + /* clang-format off */ +LIGHT_SHADER_CODE + /* clang-format on */ + return light; +} + +#endif + +#ifdef USE_NINEPATCH + +float map_ninepatch_axis(float pixel, float draw_size, float tex_pixel_size, float margin_begin, float margin_end, int np_repeat, inout int draw_center) { + + float tex_size = 1.0 / tex_pixel_size; + + if (pixel < margin_begin) { + return pixel * tex_pixel_size; + } else if (pixel >= draw_size - margin_end) { + return (tex_size - (draw_size - pixel)) * tex_pixel_size; + } else { + if (!bool(draw_data.flags & FLAGS_NINEPACH_DRAW_CENTER)) { + draw_center--; + } + + // np_repeat is passed as uniform using NinePatchRect::AxisStretchMode enum. + if (np_repeat == 0) { // Stretch. + // Convert to ratio. + float ratio = (pixel - margin_begin) / (draw_size - margin_begin - margin_end); + // Scale to source texture. + return (margin_begin + ratio * (tex_size - margin_begin - margin_end)) * tex_pixel_size; + } else if (np_repeat == 1) { // Tile. + // Convert to offset. + float ofs = mod((pixel - margin_begin), tex_size - margin_begin - margin_end); + // Scale to source texture. + return (margin_begin + ofs) * tex_pixel_size; + } else if (np_repeat == 2) { // Tile Fit. + // Calculate scale. + float src_area = draw_size - margin_begin - margin_end; + float dst_area = tex_size - margin_begin - margin_end; + float scale = max(1.0, floor(src_area / max(dst_area, 0.0000001) + 0.5)); + // Convert to ratio. + float ratio = (pixel - margin_begin) / src_area; + ratio = mod(ratio * scale, 1.0); + // Scale to source texture. + return (margin_begin + ratio * dst_area) * tex_pixel_size; + } else { // Shouldn't happen, but silences compiler warning. + return 0.0; + } + } +} + +#endif + +void main() { + + vec4 color = color_interp; + vec2 uv = uv_interp; + vec2 vertex = vertex_interp; + +#if !defined(USE_ATTRIBUTES) && !defined(USE_PRIMITIVE) + +#ifdef USE_NINEPATCH + + int draw_center = 2; + uv = vec2( + map_ninepatch_axis(pixel_size_interp.x, abs(draw_data.dst_rect.z), draw_data.color_texture_pixel_size.x, draw_data.ninepatch_margins.x, draw_data.ninepatch_margins.z, int(draw_data.flags >> FLAGS_NINEPATCH_H_MODE_SHIFT) & 0x3, draw_center), + map_ninepatch_axis(pixel_size_interp.y, abs(draw_data.dst_rect.w), draw_data.color_texture_pixel_size.y, draw_data.ninepatch_margins.y, draw_data.ninepatch_margins.w, int(draw_data.flags >> FLAGS_NINEPATCH_V_MODE_SHIFT) & 0x3, draw_center)); + + if (draw_center == 0) { + color.a = 0.0; + } + + uv = uv * draw_data.src_rect.zw + draw_data.src_rect.xy; //apply region if needed + +#endif + if (bool(draw_data.flags & FLAGS_CLIP_RECT_UV)) { + + uv = clamp(uv, draw_data.src_rect.xy, draw_data.src_rect.xy + abs(draw_data.src_rect.zw)); + } + +#endif + + color *= texture(sampler2D(color_texture, texture_sampler), uv); + + uint light_count = (draw_data.flags >> FLAGS_LIGHT_COUNT_SHIFT) & 0xF; //max 16 lights + + vec3 normal; + +#if defined(NORMAL_USED) + + bool normal_used = true; +#else + bool normal_used = false; +#endif + + if (normal_used || (light_count > 0 && bool(draw_data.flags & FLAGS_DEFAULT_NORMAL_MAP_USED))) { + normal.xy = texture(sampler2D(normal_texture, texture_sampler), uv).xy * vec2(2.0, -2.0) - vec2(1.0, -1.0); + normal.z = sqrt(1.0 - dot(normal.xy, normal.xy)); + normal_used = true; + } else { + normal = vec3(0.0, 0.0, 1.0); + } + + vec4 specular_shininess; + +#if defined(SPECULAR_SHININESS_USED) + + bool specular_shininess_used = true; +#else + bool specular_shininess_used = false; +#endif + + if (specular_shininess_used || (light_count > 0 && normal_used && bool(draw_data.flags & FLAGS_DEFAULT_SPECULAR_MAP_USED))) { + specular_shininess = texture(sampler2D(specular_texture, texture_sampler), uv); + specular_shininess *= unpackUnorm4x8(draw_data.specular_shininess); + specular_shininess_used = true; + } else { + specular_shininess = vec4(1.0); + } + +#if defined(SCREEN_UV_USED) + vec2 screen_uv = gl_FragCoord.xy * canvas_data.screen_pixel_size; +#else + vec2 screen_uv = vec2(0.0); +#endif + + vec3 light_vertex = vec3(vertex, 0.0); + vec2 shadow_vertex = vertex; + + { + float normal_depth = 1.0; + +#if defined(NORMALMAP_USED) + vec3 normal_map = vec3(0.0, 0.0, 1.0); + normal_used = true; +#endif + + /* clang-format off */ + +FRAGMENT_SHADER_CODE + + /* clang-format on */ + +#if defined(NORMALMAP_USED) + normal = mix(vec3(0.0, 0.0, 1.0), normal_map * vec3(2.0, -2.0, 1.0) - vec3(1.0, -1.0, 0.0), normal_depth); +#endif + } + + if (normal_used) { + //convert by item transform + normal.xy = mat2(normalize(draw_data.world_x), normalize(draw_data.world_y)) * normal.xy; + //convert by canvas transform + normal = normalize((canvas_data.canvas_normal_transform * vec4(normal, 0.0)).xyz); + } + + vec4 base_color = color; + if (bool(draw_data.flags & FLAGS_USING_LIGHT_MASK)) { + color = vec4(0.0); //invisible by default due to using light mask + } + + color *= canvas_data.canvas_modulation; +#ifdef USE_LIGHTING + for (uint i = 0; i < MAX_LIGHT_TEXTURES; i++) { + if (i >= light_count) { + break; + } + uint light_base; + if (i < 8) { + if (i < 4) { + light_base = draw_data.lights[0]; + } else { + light_base = draw_data.lights[1]; + } + } else { + if (i < 12) { + light_base = draw_data.lights[2]; + } else { + light_base = draw_data.lights[3]; + } + } + light_base >>= (i & 3) * 8; + light_base &= 0xFF; + + vec2 tex_uv = (vec4(vertex, 0.0, 1.0) * mat4(light_array.data[light_base].texture_matrix[0], light_array.data[light_base].texture_matrix[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy; //multiply inverse given its transposed. Optimizer removes useless operations. + vec4 light_color = texture(sampler2D(light_textures[i], texture_sampler), tex_uv); + vec4 light_base_color = light_array.data[light_base].color; + +#ifdef LIGHT_SHADER_CODE_USED + + vec4 shadow_modulate = vec4(1.0); + vec3 light_position = vec3(light_array.data[light_base].position, light_array.data[light_base].height); + + light_color.rgb *= light_base_color.rgb; + light_color = light_compute(light_vertex, light_position, normal, light_color, light_base_color.a, specular_shininess, shadow_modulate, screen_uv, color, uv); +#else + + light_color.rgb *= light_base_color.rgb * light_base_color.a; + + if (normal_used) { + + vec3 light_pos = vec3(light_array.data[light_base].position, light_array.data[light_base].height); + vec3 pos = light_vertex; + vec3 light_vec = normalize(light_pos - pos); + float cNdotL = max(0.0, dot(normal, light_vec)); + + if (specular_shininess_used) { + //blinn + vec3 view = vec3(0.0, 0.0, 1.0); // not great but good enough + vec3 half_vec = normalize(view + light_vec); + + float cNdotV = max(dot(normal, view), 0.0); + float cNdotH = max(dot(normal, half_vec), 0.0); + float cVdotH = max(dot(view, half_vec), 0.0); + float cLdotH = max(dot(light_vec, half_vec), 0.0); + float shininess = exp2(15.0 * specular_shininess.a + 1.0) * 0.25; + float blinn = pow(cNdotH, shininess); + blinn *= (shininess + 8.0) * (1.0 / (8.0 * M_PI)); + float s = (blinn) / max(4.0 * cNdotV * cNdotL, 0.75); + + light_color.rgb = specular_shininess.rgb * light_base_color.rgb * s + light_color.rgb * cNdotL; + } else { + light_color.rgb *= cNdotL; + } + } +#endif + if (any(lessThan(tex_uv, vec2(0.0, 0.0))) || any(greaterThanEqual(tex_uv, vec2(1.0, 1.0)))) { + //if outside the light texture, light color is zero + light_color.a = 0.0; + } + + if (bool(light_array.data[light_base].flags & LIGHT_FLAGS_HAS_SHADOW)) { + + vec2 shadow_pos = (vec4(shadow_vertex, 0.0, 1.0) * mat4(light_array.data[light_base].shadow_matrix[0], light_array.data[light_base].shadow_matrix[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy; //multiply inverse given its transposed. Optimizer removes useless operations. + + vec2 pos_norm = normalize(shadow_pos); + vec2 pos_abs = abs(pos_norm); + vec2 pos_box = pos_norm / max(pos_abs.x, pos_abs.y); + vec2 pos_rot = pos_norm * mat2(vec2(0.7071067811865476, -0.7071067811865476), vec2(0.7071067811865476, 0.7071067811865476)); //is there a faster way to 45 degrees rot? + float tex_ofs; + float distance; + if (pos_rot.y > 0) { + if (pos_rot.x > 0) { + tex_ofs = pos_box.y * 0.125 + 0.125; + distance = shadow_pos.x; + } else { + tex_ofs = pos_box.x * -0.125 + (0.25 + 0.125); + distance = shadow_pos.y; + } + } else { + if (pos_rot.x < 0) { + tex_ofs = pos_box.y * -0.125 + (0.5 + 0.125); + distance = -shadow_pos.x; + } else { + tex_ofs = pos_box.x * 0.125 + (0.75 + 0.125); + distance = -shadow_pos.y; + } + } + + //float distance = length(shadow_pos); + float shadow; + uint shadow_mode = light_array.data[light_base].flags & LIGHT_FLAGS_FILTER_MASK; + + vec4 shadow_uv = vec4(tex_ofs, 0.0, distance, 1.0); + + if (shadow_mode == LIGHT_FLAGS_SHADOW_NEAREST) { + shadow = textureProj(sampler2DShadow(shadow_textures[i], shadow_sampler), shadow_uv).x; + } else if (shadow_mode == LIGHT_FLAGS_SHADOW_PCF5) { + vec4 shadow_pixel_size = vec4(light_array.data[light_base].shadow_pixel_size, 0.0, 0.0, 0.0); + shadow = 0.0; + shadow += textureProj(sampler2DShadow(shadow_textures[i], shadow_sampler), shadow_uv - shadow_pixel_size * 2.0).x; + shadow += textureProj(sampler2DShadow(shadow_textures[i], shadow_sampler), shadow_uv - shadow_pixel_size).x; + shadow += textureProj(sampler2DShadow(shadow_textures[i], shadow_sampler), shadow_uv).x; + shadow += textureProj(sampler2DShadow(shadow_textures[i], shadow_sampler), shadow_uv + shadow_pixel_size).x; + shadow += textureProj(sampler2DShadow(shadow_textures[i], shadow_sampler), shadow_uv + shadow_pixel_size * 2.0).x; + shadow /= 5.0; + } else { //PCF13 + vec4 shadow_pixel_size = vec4(light_array.data[light_base].shadow_pixel_size, 0.0, 0.0, 0.0); + shadow = 0.0; + shadow += textureProj(sampler2DShadow(shadow_textures[i], shadow_sampler), shadow_uv - shadow_pixel_size * 6.0).x; + shadow += textureProj(sampler2DShadow(shadow_textures[i], shadow_sampler), shadow_uv - shadow_pixel_size * 5.0).x; + shadow += textureProj(sampler2DShadow(shadow_textures[i], shadow_sampler), shadow_uv - shadow_pixel_size * 4.0).x; + shadow += textureProj(sampler2DShadow(shadow_textures[i], shadow_sampler), shadow_uv - shadow_pixel_size * 3.0).x; + shadow += textureProj(sampler2DShadow(shadow_textures[i], shadow_sampler), shadow_uv - shadow_pixel_size * 2.0).x; + shadow += textureProj(sampler2DShadow(shadow_textures[i], shadow_sampler), shadow_uv - shadow_pixel_size).x; + shadow += textureProj(sampler2DShadow(shadow_textures[i], shadow_sampler), shadow_uv).x; + shadow += textureProj(sampler2DShadow(shadow_textures[i], shadow_sampler), shadow_uv + shadow_pixel_size).x; + shadow += textureProj(sampler2DShadow(shadow_textures[i], shadow_sampler), shadow_uv + shadow_pixel_size * 2.0).x; + shadow += textureProj(sampler2DShadow(shadow_textures[i], shadow_sampler), shadow_uv + shadow_pixel_size * 3.0).x; + shadow += textureProj(sampler2DShadow(shadow_textures[i], shadow_sampler), shadow_uv + shadow_pixel_size * 4.0).x; + shadow += textureProj(sampler2DShadow(shadow_textures[i], shadow_sampler), shadow_uv + shadow_pixel_size * 5.0).x; + shadow += textureProj(sampler2DShadow(shadow_textures[i], shadow_sampler), shadow_uv + shadow_pixel_size * 6.0).x; + shadow /= 13.0; + } + + vec4 shadow_color = light_array.data[light_base].shadow_color; +#ifdef LIGHT_SHADER_CODE_USED + shadow_color *= shadow_modulate; +#endif + light_color = mix(light_color, shadow_color, shadow); + } + + uint blend_mode = light_array.data[light_base].flags & LIGHT_FLAGS_BLEND_MASK; + + switch (blend_mode) { + case LIGHT_FLAGS_BLEND_MODE_ADD: { + color.rgb += light_color.rgb * light_color.a; + } break; + case LIGHT_FLAGS_BLEND_MODE_SUB: { + color.rgb -= light_color.rgb * light_color.a; + } break; + case LIGHT_FLAGS_BLEND_MODE_MIX: { + color.rgb = mix(color.rgb, light_color.rgb, light_color.a); + } break; + case LIGHT_FLAGS_BLEND_MODE_MASK: { + light_color.a *= base_color.a; + color.rgb = mix(color.rgb, light_color.rgb, light_color.a); + } break; + } + } +#endif + + frag_color = color; +} diff --git a/servers/rendering/rasterizer_rd/shaders/canvas_occlusion.glsl b/servers/rendering/rasterizer_rd/shaders/canvas_occlusion.glsl new file mode 100644 index 0000000000..7b30cc8fe9 --- /dev/null +++ b/servers/rendering/rasterizer_rd/shaders/canvas_occlusion.glsl @@ -0,0 +1,40 @@ +/* clang-format off */ +[vertex] + +#version 450 + +layout(location = 0) in highp vec3 vertex; +/* clang-format on */ + +layout(push_constant, binding = 0, std430) uniform Constants { + + mat4 projection; + mat2x4 modelview; + vec2 direction; + vec2 pad; +} +constants; + +layout(location = 0) out highp float depth; + +void main() { + + highp vec4 vtx = vec4(vertex, 1.0) * mat4(constants.modelview[0], constants.modelview[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0)); + depth = dot(constants.direction, vtx.xy); + + gl_Position = constants.projection * vtx; +} + +/* clang-format off */ +[fragment] + +#version 450 + +layout(location = 0) in highp float depth; +/* clang-format on */ +layout(location = 0) out highp float distance_buf; + +void main() { + + distance_buf = depth; +} diff --git a/servers/rendering/rasterizer_rd/shaders/canvas_uniforms_inc.glsl b/servers/rendering/rasterizer_rd/shaders/canvas_uniforms_inc.glsl new file mode 100644 index 0000000000..1ac43480cd --- /dev/null +++ b/servers/rendering/rasterizer_rd/shaders/canvas_uniforms_inc.glsl @@ -0,0 +1,141 @@ +#define M_PI 3.14159265359 + +#define FLAGS_INSTANCING_STRIDE_MASK 0xF +#define FLAGS_INSTANCING_ENABLED (1 << 4) +#define FLAGS_INSTANCING_HAS_COLORS (1 << 5) +#define FLAGS_INSTANCING_COLOR_8BIT (1 << 6) +#define FLAGS_INSTANCING_HAS_CUSTOM_DATA (1 << 7) +#define FLAGS_INSTANCING_CUSTOM_DATA_8_BIT (1 << 8) + +#define FLAGS_CLIP_RECT_UV (1 << 9) +#define FLAGS_TRANSPOSE_RECT (1 << 10) +#define FLAGS_USING_LIGHT_MASK (1 << 11) +#define FLAGS_NINEPACH_DRAW_CENTER (1 << 12) +#define FLAGS_USING_PARTICLES (1 << 13) +#define FLAGS_USE_PIXEL_SNAP (1 << 14) + +#define FLAGS_NINEPATCH_H_MODE_SHIFT 16 +#define FLAGS_NINEPATCH_V_MODE_SHIFT 18 + +#define FLAGS_LIGHT_COUNT_SHIFT 20 + +#define FLAGS_DEFAULT_NORMAL_MAP_USED (1 << 26) +#define FLAGS_DEFAULT_SPECULAR_MAP_USED (1 << 27) + +// In vulkan, sets should always be ordered using the following logic: +// Lower Sets: Sets that change format and layout less often +// Higher sets: Sets that change format and layout very often +// This is because changing a set for another with a different layout or format, +// invalidates all the upper ones. + +/* SET0: Draw Primitive */ + +layout(push_constant, binding = 0, std430) uniform DrawData { + vec2 world_x; + vec2 world_y; + vec2 world_ofs; + uint flags; + uint specular_shininess; +#ifdef USE_PRIMITIVE + vec2 points[3]; + vec2 uvs[3]; + uint colors[6]; +#else + vec4 modulation; + vec4 ninepatch_margins; + vec4 dst_rect; //for built-in rect and UV + vec4 src_rect; + vec2 pad; + +#endif + vec2 color_texture_pixel_size; + uint lights[4]; +} +draw_data; + +// The values passed per draw primitives are cached within it + +layout(set = 0, binding = 1) uniform texture2D color_texture; +layout(set = 0, binding = 2) uniform texture2D normal_texture; +layout(set = 0, binding = 3) uniform texture2D specular_texture; +layout(set = 0, binding = 4) uniform sampler texture_sampler; + +layout(set = 0, binding = 5) uniform textureBuffer instancing_buffer; + +/* SET1: Is reserved for the material */ + +#ifdef USE_MATERIAL_SAMPLERS + +layout(set = 1, binding = 0) uniform sampler material_samplers[12]; + +#endif + +/* SET2: Canvas Item State (including lighting) */ + +layout(set = 2, binding = 0, std140) uniform CanvasData { + mat4 canvas_transform; + mat4 screen_transform; + mat4 canvas_normal_transform; + vec4 canvas_modulation; + vec2 screen_pixel_size; + float time; + float time_pad; + //uint light_count; +} +canvas_data; + +layout(set = 2, binding = 1) uniform textureBuffer skeleton_buffer; + +layout(set = 2, binding = 2, std140) uniform SkeletonData { + mat4 skeleton_transform; //in world coordinates + mat4 skeleton_transform_inverse; +} +skeleton_data; + +#ifdef USE_LIGHTING + +#define LIGHT_FLAGS_BLEND_MASK (3 << 16) +#define LIGHT_FLAGS_BLEND_MODE_ADD (0 << 16) +#define LIGHT_FLAGS_BLEND_MODE_SUB (1 << 16) +#define LIGHT_FLAGS_BLEND_MODE_MIX (2 << 16) +#define LIGHT_FLAGS_BLEND_MODE_MASK (3 << 16) +#define LIGHT_FLAGS_HAS_SHADOW (1 << 20) +#define LIGHT_FLAGS_FILTER_SHIFT 22 +#define LIGHT_FLAGS_FILTER_MASK (3 << 22) +#define LIGHT_FLAGS_SHADOW_NEAREST (0 << 22) +#define LIGHT_FLAGS_SHADOW_PCF5 (1 << 22) +#define LIGHT_FLAGS_SHADOW_PCF13 (2 << 22) + +struct Light { + mat2x4 texture_matrix; //light to texture coordinate matrix (transposed) + mat2x4 shadow_matrix; //light to shadow coordinate matrix (transposed) + vec4 color; + vec4 shadow_color; + vec2 position; + uint flags; //index to light texture + float height; + float shadow_pixel_size; + float pad0; + float pad1; + float pad2; +}; + +layout(set = 2, binding = 3, std140) uniform LightData { + Light data[MAX_LIGHTS]; +} +light_array; + +layout(set = 2, binding = 4) uniform texture2D light_textures[MAX_LIGHT_TEXTURES]; +layout(set = 2, binding = 5) uniform texture2D shadow_textures[MAX_LIGHT_TEXTURES]; + +layout(set = 2, binding = 6) uniform sampler shadow_sampler; + +#endif + +/* SET3: Render Target Data */ + +#ifdef SCREEN_TEXTURE_USED + +layout(set = 3, binding = 0) uniform texture2D screen_texture; + +#endif diff --git a/servers/rendering/rasterizer_rd/shaders/copy.glsl b/servers/rendering/rasterizer_rd/shaders/copy.glsl new file mode 100644 index 0000000000..cbb9b546a3 --- /dev/null +++ b/servers/rendering/rasterizer_rd/shaders/copy.glsl @@ -0,0 +1,86 @@ +/* clang-format off */ +[vertex] + +#version 450 + +VERSION_DEFINES + +layout(location = 0) out vec2 uv_interp; +/* clang-format on */ + +void main() { + + vec2 base_arr[4] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0)); + uv_interp = base_arr[gl_VertexIndex]; + + gl_Position = vec4(uv_interp * 2.0 - 1.0, 0.0, 1.0); +} + +/* clang-format off */ +[fragment] + +#version 450 + +VERSION_DEFINES + +layout(location = 0) in vec2 uv_interp; +/* clang-format on */ + +#ifdef MODE_CUBE_TO_DP + +layout(set = 0, binding = 0) uniform samplerCube source_cube; + +layout(push_constant, binding = 0, std430) uniform Params { + float bias; + float z_far; + float z_near; + bool z_flip; +} +params; + +layout(location = 0) out float depth_buffer; + +#endif + +void main() { + +#ifdef MODE_CUBE_TO_DP + + vec3 normal = vec3(uv_interp * 2.0 - 1.0, 0.0); + + normal.z = 0.5 - 0.5 * ((normal.x * normal.x) + (normal.y * normal.y)); + normal = normalize(normal); + + normal.y = -normal.y; //needs to be flipped to match projection matrix + if (!params.z_flip) { + normal.z = -normal.z; + } + + float depth = texture(source_cube, normal).r; + + // absolute values for direction cosines, bigger value equals closer to basis axis + vec3 unorm = abs(normal); + + if ((unorm.x >= unorm.y) && (unorm.x >= unorm.z)) { + // x code + unorm = normal.x > 0.0 ? vec3(1.0, 0.0, 0.0) : vec3(-1.0, 0.0, 0.0); + } else if ((unorm.y > unorm.x) && (unorm.y >= unorm.z)) { + // y code + unorm = normal.y > 0.0 ? vec3(0.0, 1.0, 0.0) : vec3(0.0, -1.0, 0.0); + } else if ((unorm.z > unorm.x) && (unorm.z > unorm.y)) { + // z code + unorm = normal.z > 0.0 ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 0.0, -1.0); + } else { + // oh-no we messed up code + // has to be + unorm = vec3(1.0, 0.0, 0.0); + } + + float depth_fix = 1.0 / dot(normal, unorm); + + depth = 2.0 * depth - 1.0; + float linear_depth = 2.0 * params.z_near * params.z_far / (params.z_far + params.z_near - depth * (params.z_far - params.z_near)); + depth_buffer = (linear_depth * depth_fix + params.bias) / params.z_far; + +#endif +} diff --git a/servers/rendering/rasterizer_rd/shaders/cubemap_downsampler.glsl b/servers/rendering/rasterizer_rd/shaders/cubemap_downsampler.glsl new file mode 100644 index 0000000000..9f3ecf6053 --- /dev/null +++ b/servers/rendering/rasterizer_rd/shaders/cubemap_downsampler.glsl @@ -0,0 +1,188 @@ +// Copyright 2016 Activision Publishing, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +/* clang-format off */ +[compute] + +#version 450 + +VERSION_DEFINES + +#define BLOCK_SIZE 8 + +layout(local_size_x = BLOCK_SIZE, local_size_y = BLOCK_SIZE, local_size_z = 1) in; +/* clang-format on */ + +layout(set = 0, binding = 0) uniform samplerCube source_cubemap; + +layout(rgba16f, set = 1, binding = 0) uniform restrict writeonly imageCube dest_cubemap; + +layout(push_constant, binding = 1, std430) uniform Params { + uint face_size; +} +params; + +#define M_PI 3.14159265359 + +void get_dir_0(out vec3 dir, in float u, in float v) { + dir[0] = 1.0; + dir[1] = v; + dir[2] = -u; +} +void get_dir_1(out vec3 dir, in float u, in float v) { + dir[0] = -1.0; + dir[1] = v; + dir[2] = u; +} +void get_dir_2(out vec3 dir, in float u, in float v) { + dir[0] = u; + dir[1] = 1.0; + dir[2] = -v; +} +void get_dir_3(out vec3 dir, in float u, in float v) { + dir[0] = u; + dir[1] = -1.0; + dir[2] = v; +} +void get_dir_4(out vec3 dir, in float u, in float v) { + dir[0] = u; + dir[1] = v; + dir[2] = 1.0; +} +void get_dir_5(out vec3 dir, in float u, in float v) { + dir[0] = -u; + dir[1] = v; + dir[2] = -1.0; +} + +float calcWeight(float u, float v) { + float val = u * u + v * v + 1.0; + return val * sqrt(val); +} + +void main() { + uvec3 id = gl_GlobalInvocationID; + uint face_size = params.face_size; + + if (id.x < face_size && id.y < face_size) { + float inv_face_size = 1.0 / float(face_size); + + float u0 = (float(id.x) * 2.0 + 1.0 - 0.75) * inv_face_size - 1.0; + float u1 = (float(id.x) * 2.0 + 1.0 + 0.75) * inv_face_size - 1.0; + + float v0 = (float(id.y) * 2.0 + 1.0 - 0.75) * -inv_face_size + 1.0; + float v1 = (float(id.y) * 2.0 + 1.0 + 0.75) * -inv_face_size + 1.0; + + float weights[4]; + weights[0] = calcWeight(u0, v0); + weights[1] = calcWeight(u1, v0); + weights[2] = calcWeight(u0, v1); + weights[3] = calcWeight(u1, v1); + + const float wsum = 0.5 / (weights[0] + weights[1] + weights[2] + weights[3]); + for (int i = 0; i < 4; i++) { + weights[i] = weights[i] * wsum + .125; + } + + vec3 dir; + vec4 color; + switch (id.z) { + case 0: + get_dir_0(dir, u0, v0); + color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0]; + + get_dir_0(dir, u1, v0); + color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1]; + + get_dir_0(dir, u0, v1); + color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2]; + + get_dir_0(dir, u1, v1); + color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3]; + break; + case 1: + get_dir_1(dir, u0, v0); + color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0]; + + get_dir_1(dir, u1, v0); + color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1]; + + get_dir_1(dir, u0, v1); + color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2]; + + get_dir_1(dir, u1, v1); + color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3]; + break; + case 2: + get_dir_2(dir, u0, v0); + color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0]; + + get_dir_2(dir, u1, v0); + color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1]; + + get_dir_2(dir, u0, v1); + color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2]; + + get_dir_2(dir, u1, v1); + color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3]; + break; + case 3: + get_dir_3(dir, u0, v0); + color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0]; + + get_dir_3(dir, u1, v0); + color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1]; + + get_dir_3(dir, u0, v1); + color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2]; + + get_dir_3(dir, u1, v1); + color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3]; + break; + case 4: + get_dir_4(dir, u0, v0); + color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0]; + + get_dir_4(dir, u1, v0); + color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1]; + + get_dir_4(dir, u0, v1); + color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2]; + + get_dir_4(dir, u1, v1); + color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3]; + break; + default: + get_dir_5(dir, u0, v0); + color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0]; + + get_dir_5(dir, u1, v0); + color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1]; + + get_dir_5(dir, u0, v1); + color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2]; + + get_dir_5(dir, u1, v1); + color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3]; + break; + } + imageStore(dest_cubemap, ivec3(id), color); + } +} diff --git a/servers/rendering/rasterizer_rd/shaders/cubemap_filter.glsl b/servers/rendering/rasterizer_rd/shaders/cubemap_filter.glsl new file mode 100644 index 0000000000..193d0a8a3c --- /dev/null +++ b/servers/rendering/rasterizer_rd/shaders/cubemap_filter.glsl @@ -0,0 +1,328 @@ +// Copyright 2016 Activision Publishing, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +/* clang-format off */ +[compute] + +#version 450 + +VERSION_DEFINES + +#define GROUP_SIZE 64 + +layout(local_size_x = GROUP_SIZE, local_size_y = 1, local_size_z = 1) in; +/* clang-format on */ + +layout(set = 0, binding = 0) uniform samplerCube source_cubemap; +layout(rgba16f, set = 2, binding = 0) uniform restrict writeonly imageCube dest_cubemap0; +layout(rgba16f, set = 2, binding = 1) uniform restrict writeonly imageCube dest_cubemap1; +layout(rgba16f, set = 2, binding = 2) uniform restrict writeonly imageCube dest_cubemap2; +layout(rgba16f, set = 2, binding = 3) uniform restrict writeonly imageCube dest_cubemap3; +layout(rgba16f, set = 2, binding = 4) uniform restrict writeonly imageCube dest_cubemap4; +layout(rgba16f, set = 2, binding = 5) uniform restrict writeonly imageCube dest_cubemap5; +layout(rgba16f, set = 2, binding = 6) uniform restrict writeonly imageCube dest_cubemap6; + +#ifdef USE_HIGH_QUALITY +#define NUM_TAPS 32 +#else +#define NUM_TAPS 8 +#endif + +#define BASE_RESOLUTION 128 + +#ifdef USE_HIGH_QUALITY +layout(set = 1, binding = 0, std430) buffer restrict readonly Data { + vec4[7][5][3][24] coeffs; +} +data; +#else +layout(set = 1, binding = 0, std430) buffer restrict readonly Data { + vec4[7][5][6] coeffs; +} +data; +#endif + +void get_dir(out vec3 dir, in vec2 uv, in uint face) { + switch (face) { + case 0: + dir = vec3(1.0, uv[1], -uv[0]); + break; + case 1: + dir = vec3(-1.0, uv[1], uv[0]); + break; + case 2: + dir = vec3(uv[0], 1.0, -uv[1]); + break; + case 3: + dir = vec3(uv[0], -1.0, uv[1]); + break; + case 4: + dir = vec3(uv[0], uv[1], 1.0); + break; + default: + dir = vec3(-uv[0], uv[1], -1.0); + break; + } +} + +void main() { + // INPUT: + // id.x = the linear address of the texel (ignoring face) + // id.y = the face + // -> use to index output texture + // id.x = texel x + // id.y = texel y + // id.z = face + uvec3 id = gl_GlobalInvocationID; + + // determine which texel this is +#ifndef USE_TEXTURE_ARRAY + // NOTE (macOS/MoltenVK): Do not rename, "level" variable name conflicts with the Metal "level(float lod)" mipmap sampling function name. + int mip_level = 0; + if (id.x < (128 * 128)) { + mip_level = 0; + } else if (id.x < (128 * 128 + 64 * 64)) { + mip_level = 1; + id.x -= (128 * 128); + } else if (id.x < (128 * 128 + 64 * 64 + 32 * 32)) { + mip_level = 2; + id.x -= (128 * 128 + 64 * 64); + } else if (id.x < (128 * 128 + 64 * 64 + 32 * 32 + 16 * 16)) { + mip_level = 3; + id.x -= (128 * 128 + 64 * 64 + 32 * 32); + } else if (id.x < (128 * 128 + 64 * 64 + 32 * 32 + 16 * 16 + 8 * 8)) { + mip_level = 4; + id.x -= (128 * 128 + 64 * 64 + 32 * 32 + 16 * 16); + } else if (id.x < (128 * 128 + 64 * 64 + 32 * 32 + 16 * 16 + 8 * 8 + 4 * 4)) { + mip_level = 5; + id.x -= (128 * 128 + 64 * 64 + 32 * 32 + 16 * 16 + 8 * 8); + } else if (id.x < (128 * 128 + 64 * 64 + 32 * 32 + 16 * 16 + 8 * 8 + 4 * 4 + 2 * 2)) { + mip_level = 6; + id.x -= (128 * 128 + 64 * 64 + 32 * 32 + 16 * 16 + 8 * 8 + 4 * 4); + } else { + return; + } + int res = BASE_RESOLUTION >> mip_level; +#else // Using Texture Arrays so all levels are the same resolution + int res = BASE_RESOLUTION; + int mip_level = int(id.x / (BASE_RESOLUTION * BASE_RESOLUTION)); + id.x -= mip_level * BASE_RESOLUTION * BASE_RESOLUTION; +#endif + + // determine dir / pos for the texel + vec3 dir, adir, frameZ; + { + id.z = id.y; + id.y = id.x / res; + id.x -= id.y * res; + + vec2 uv; + uv.x = (float(id.x) * 2.0 + 1.0) / float(res) - 1.0; + uv.y = -(float(id.y) * 2.0 + 1.0) / float(res) + 1.0; + + get_dir(dir, uv, id.z); + frameZ = normalize(dir); + + adir = abs(dir); + } + + // GGX gather colors + vec4 color = vec4(0.0); + for (int axis = 0; axis < 3; axis++) { + const int otherAxis0 = 1 - (axis & 1) - (axis >> 1); + const int otherAxis1 = 2 - (axis >> 1); + + float frameweight = (max(adir[otherAxis0], adir[otherAxis1]) - .75) / .25; + if (frameweight > 0.0) { + // determine frame + vec3 UpVector; + switch (axis) { + case 0: + UpVector = vec3(1, 0, 0); + break; + case 1: + UpVector = vec3(0, 1, 0); + break; + default: + UpVector = vec3(0, 0, 1); + break; + } + + vec3 frameX = normalize(cross(UpVector, frameZ)); + vec3 frameY = cross(frameZ, frameX); + + // calculate parametrization for polynomial + float Nx = dir[otherAxis0]; + float Ny = dir[otherAxis1]; + float Nz = adir[axis]; + + float NmaxXY = max(abs(Ny), abs(Nx)); + Nx /= NmaxXY; + Ny /= NmaxXY; + + float theta; + if (Ny < Nx) { + if (Ny <= -0.999) + theta = Nx; + else + theta = Ny; + } else { + if (Ny >= 0.999) + theta = -Nx; + else + theta = -Ny; + } + + float phi; + if (Nz <= -0.999) + phi = -NmaxXY; + else if (Nz >= 0.999) + phi = NmaxXY; + else + phi = Nz; + + float theta2 = theta * theta; + float phi2 = phi * phi; + + // sample + for (int iSuperTap = 0; iSuperTap < NUM_TAPS / 4; iSuperTap++) { + const int index = (NUM_TAPS / 4) * axis + iSuperTap; + +#ifdef USE_HIGH_QUALITY + vec4 coeffsDir0[3]; + vec4 coeffsDir1[3]; + vec4 coeffsDir2[3]; + vec4 coeffsLevel[3]; + vec4 coeffsWeight[3]; + + for (int iCoeff = 0; iCoeff < 3; iCoeff++) { + coeffsDir0[iCoeff] = data.coeffs[mip_level][0][iCoeff][index]; + coeffsDir1[iCoeff] = data.coeffs[mip_level][1][iCoeff][index]; + coeffsDir2[iCoeff] = data.coeffs[mip_level][2][iCoeff][index]; + coeffsLevel[iCoeff] = data.coeffs[mip_level][3][iCoeff][index]; + coeffsWeight[iCoeff] = data.coeffs[mip_level][4][iCoeff][index]; + } + + for (int iSubTap = 0; iSubTap < 4; iSubTap++) { + // determine sample attributes (dir, weight, mip_level) + vec3 sample_dir = frameX * (coeffsDir0[0][iSubTap] + coeffsDir0[1][iSubTap] * theta2 + coeffsDir0[2][iSubTap] * phi2) + frameY * (coeffsDir1[0][iSubTap] + coeffsDir1[1][iSubTap] * theta2 + coeffsDir1[2][iSubTap] * phi2) + frameZ * (coeffsDir2[0][iSubTap] + coeffsDir2[1][iSubTap] * theta2 + coeffsDir2[2][iSubTap] * phi2); + + float sample_level = coeffsLevel[0][iSubTap] + coeffsLevel[1][iSubTap] * theta2 + coeffsLevel[2][iSubTap] * phi2; + + float sample_weight = coeffsWeight[0][iSubTap] + coeffsWeight[1][iSubTap] * theta2 + coeffsWeight[2][iSubTap] * phi2; +#else + vec4 coeffsDir0 = data.coeffs[mip_level][0][index]; + vec4 coeffsDir1 = data.coeffs[mip_level][1][index]; + vec4 coeffsDir2 = data.coeffs[mip_level][2][index]; + vec4 coeffsLevel = data.coeffs[mip_level][3][index]; + vec4 coeffsWeight = data.coeffs[mip_level][4][index]; + + for (int iSubTap = 0; iSubTap < 4; iSubTap++) { + // determine sample attributes (dir, weight, mip_level) + vec3 sample_dir = frameX * coeffsDir0[iSubTap] + frameY * coeffsDir1[iSubTap] + frameZ * coeffsDir2[iSubTap]; + + float sample_level = coeffsLevel[iSubTap]; + + float sample_weight = coeffsWeight[iSubTap]; +#endif + + sample_weight *= frameweight; + + // adjust for jacobian + sample_dir /= max(abs(sample_dir[0]), max(abs(sample_dir[1]), abs(sample_dir[2]))); + sample_level += 0.75 * log2(dot(sample_dir, sample_dir)); +#ifndef USE_TEXTURE_ARRAY + sample_level += float(mip_level) / 6.0; // Hack to increase the perceived roughness and reduce upscaling artifacts +#endif + // sample cubemap + color.xyz += textureLod(source_cubemap, normalize(sample_dir), sample_level).xyz * sample_weight; + color.w += sample_weight; + } + } + } + } + color /= color.w; + + // write color + color.xyz = max(vec3(0.0), color.xyz); + color.w = 1.0; +#ifdef USE_TEXTURE_ARRAY + id.xy *= uvec2(2, 2); +#endif + + switch (mip_level) { + case 0: + imageStore(dest_cubemap0, ivec3(id), color); +#ifdef USE_TEXTURE_ARRAY + imageStore(dest_cubemap0, ivec3(id) + ivec3(1.0, 0.0, 0.0), color); + imageStore(dest_cubemap0, ivec3(id) + ivec3(0.0, 1.0, 0.0), color); + imageStore(dest_cubemap0, ivec3(id) + ivec3(1.0, 1.0, 0.0), color); +#endif + break; + case 1: + imageStore(dest_cubemap1, ivec3(id), color); +#ifdef USE_TEXTURE_ARRAY + imageStore(dest_cubemap1, ivec3(id) + ivec3(1.0, 0.0, 0.0), color); + imageStore(dest_cubemap1, ivec3(id) + ivec3(0.0, 1.0, 0.0), color); + imageStore(dest_cubemap1, ivec3(id) + ivec3(1.0, 1.0, 0.0), color); +#endif + break; + case 2: + imageStore(dest_cubemap2, ivec3(id), color); +#ifdef USE_TEXTURE_ARRAY + imageStore(dest_cubemap2, ivec3(id) + ivec3(1.0, 0.0, 0.0), color); + imageStore(dest_cubemap2, ivec3(id) + ivec3(0.0, 1.0, 0.0), color); + imageStore(dest_cubemap2, ivec3(id) + ivec3(1.0, 1.0, 0.0), color); +#endif + break; + case 3: + imageStore(dest_cubemap3, ivec3(id), color); +#ifdef USE_TEXTURE_ARRAY + imageStore(dest_cubemap3, ivec3(id) + ivec3(1.0, 0.0, 0.0), color); + imageStore(dest_cubemap3, ivec3(id) + ivec3(0.0, 1.0, 0.0), color); + imageStore(dest_cubemap3, ivec3(id) + ivec3(1.0, 1.0, 0.0), color); +#endif + break; + case 4: + imageStore(dest_cubemap4, ivec3(id), color); +#ifdef USE_TEXTURE_ARRAY + imageStore(dest_cubemap4, ivec3(id) + ivec3(1.0, 0.0, 0.0), color); + imageStore(dest_cubemap4, ivec3(id) + ivec3(0.0, 1.0, 0.0), color); + imageStore(dest_cubemap4, ivec3(id) + ivec3(1.0, 1.0, 0.0), color); +#endif + break; + case 5: + imageStore(dest_cubemap5, ivec3(id), color); +#ifdef USE_TEXTURE_ARRAY + imageStore(dest_cubemap5, ivec3(id) + ivec3(1.0, 0.0, 0.0), color); + imageStore(dest_cubemap5, ivec3(id) + ivec3(0.0, 1.0, 0.0), color); + imageStore(dest_cubemap5, ivec3(id) + ivec3(1.0, 1.0, 0.0), color); +#endif + break; + default: + imageStore(dest_cubemap6, ivec3(id), color); +#ifdef USE_TEXTURE_ARRAY + imageStore(dest_cubemap6, ivec3(id) + ivec3(1.0, 0.0, 0.0), color); + imageStore(dest_cubemap6, ivec3(id) + ivec3(0.0, 1.0, 0.0), color); + imageStore(dest_cubemap6, ivec3(id) + ivec3(1.0, 1.0, 0.0), color); +#endif + break; + } +} diff --git a/servers/rendering/rasterizer_rd/shaders/cubemap_roughness.glsl b/servers/rendering/rasterizer_rd/shaders/cubemap_roughness.glsl new file mode 100644 index 0000000000..e85996fa1a --- /dev/null +++ b/servers/rendering/rasterizer_rd/shaders/cubemap_roughness.glsl @@ -0,0 +1,147 @@ +/* clang-format off */ +[compute] + +#version 450 + +VERSION_DEFINES + +#define GROUP_SIZE 8 + +layout(local_size_x = GROUP_SIZE, local_size_y = GROUP_SIZE, local_size_z = 1) in; +/* clang-format on */ + +layout(set = 0, binding = 0) uniform samplerCube source_cube; + +layout(rgba16f, set = 1, binding = 0) uniform restrict writeonly imageCube dest_cubemap; + +layout(push_constant, binding = 1, std430) uniform Params { + uint face_id; + uint sample_count; + float roughness; + bool use_direct_write; + float face_size; +} +params; + +#define M_PI 3.14159265359 + +vec3 texelCoordToVec(vec2 uv, uint faceID) { + mat3 faceUvVectors[6]; + + // -x + faceUvVectors[1][0] = vec3(0.0, 0.0, 1.0); // u -> +z + faceUvVectors[1][1] = vec3(0.0, -1.0, 0.0); // v -> -y + faceUvVectors[1][2] = vec3(-1.0, 0.0, 0.0); // -x face + + // +x + faceUvVectors[0][0] = vec3(0.0, 0.0, -1.0); // u -> -z + faceUvVectors[0][1] = vec3(0.0, -1.0, 0.0); // v -> -y + faceUvVectors[0][2] = vec3(1.0, 0.0, 0.0); // +x face + + // -y + faceUvVectors[3][0] = vec3(1.0, 0.0, 0.0); // u -> +x + faceUvVectors[3][1] = vec3(0.0, 0.0, -1.0); // v -> -z + faceUvVectors[3][2] = vec3(0.0, -1.0, 0.0); // -y face + + // +y + faceUvVectors[2][0] = vec3(1.0, 0.0, 0.0); // u -> +x + faceUvVectors[2][1] = vec3(0.0, 0.0, 1.0); // v -> +z + faceUvVectors[2][2] = vec3(0.0, 1.0, 0.0); // +y face + + // -z + faceUvVectors[5][0] = vec3(-1.0, 0.0, 0.0); // u -> -x + faceUvVectors[5][1] = vec3(0.0, -1.0, 0.0); // v -> -y + faceUvVectors[5][2] = vec3(0.0, 0.0, -1.0); // -z face + + // +z + faceUvVectors[4][0] = vec3(1.0, 0.0, 0.0); // u -> +x + faceUvVectors[4][1] = vec3(0.0, -1.0, 0.0); // v -> -y + faceUvVectors[4][2] = vec3(0.0, 0.0, 1.0); // +z face + + // out = u * s_faceUv[0] + v * s_faceUv[1] + s_faceUv[2]. + vec3 result = (faceUvVectors[faceID][0] * uv.x) + (faceUvVectors[faceID][1] * uv.y) + faceUvVectors[faceID][2]; + return normalize(result); +} + +vec3 ImportanceSampleGGX(vec2 Xi, float Roughness, vec3 N) { + float a = Roughness * Roughness; // DISNEY'S ROUGHNESS [see Burley'12 siggraph] + + // Compute distribution direction + float Phi = 2.0 * M_PI * Xi.x; + float CosTheta = sqrt((1.0 - Xi.y) / (1.0 + (a * a - 1.0) * Xi.y)); + float SinTheta = sqrt(1.0 - CosTheta * CosTheta); + + // Convert to spherical direction + vec3 H; + H.x = SinTheta * cos(Phi); + H.y = SinTheta * sin(Phi); + H.z = CosTheta; + + vec3 UpVector = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0); + vec3 TangentX = normalize(cross(UpVector, N)); + vec3 TangentY = cross(N, TangentX); + + // Tangent to world space + return TangentX * H.x + TangentY * H.y + N * H.z; +} + +// http://graphicrants.blogspot.com.au/2013/08/specular-brdf-reference.html +float GGX(float NdotV, float a) { + float k = a / 2.0; + return NdotV / (NdotV * (1.0 - k) + k); +} + +// http://graphicrants.blogspot.com.au/2013/08/specular-brdf-reference.html +float G_Smith(float a, float nDotV, float nDotL) { + return GGX(nDotL, a * a) * GGX(nDotV, a * a); +} + +float radicalInverse_VdC(uint bits) { + bits = (bits << 16u) | (bits >> 16u); + bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); + bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); + bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); + bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); + return float(bits) * 2.3283064365386963e-10; // / 0x100000000 +} + +vec2 Hammersley(uint i, uint N) { + return vec2(float(i) / float(N), radicalInverse_VdC(i)); +} + +void main() { + uvec3 id = gl_GlobalInvocationID; + id.z += params.face_id; + + vec2 uv = ((vec2(id.xy) * 2.0 + 1.0) / (params.face_size) - 1.0); + vec3 N = texelCoordToVec(uv, id.z); + + //vec4 color = color_interp; + + if (params.use_direct_write) { + + imageStore(dest_cubemap, ivec3(id), vec4(texture(source_cube, N).rgb, 1.0)); + } else { + + vec4 sum = vec4(0.0, 0.0, 0.0, 0.0); + + for (uint sampleNum = 0u; sampleNum < params.sample_count; sampleNum++) { + vec2 xi = Hammersley(sampleNum, params.sample_count); + + vec3 H = ImportanceSampleGGX(xi, params.roughness, N); + vec3 V = N; + vec3 L = (2.0 * dot(V, H) * H - V); + + float ndotl = clamp(dot(N, L), 0.0, 1.0); + + if (ndotl > 0.0) { + + sum.rgb += textureLod(source_cube, L, 0.0).rgb * ndotl; + sum.a += ndotl; + } + } + sum /= sum.a; + + imageStore(dest_cubemap, ivec3(id), vec4(sum.rgb, 1.0)); + } +} diff --git a/servers/rendering/rasterizer_rd/shaders/giprobe.glsl b/servers/rendering/rasterizer_rd/shaders/giprobe.glsl new file mode 100644 index 0000000000..fd09f96a57 --- /dev/null +++ b/servers/rendering/rasterizer_rd/shaders/giprobe.glsl @@ -0,0 +1,788 @@ +/* clang-format off */ +[compute] + +#version 450 + +VERSION_DEFINES + +#ifdef MODE_DYNAMIC +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; +#else +layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in; +#endif +/* clang-format on */ + +#ifndef MODE_DYNAMIC + +#define NO_CHILDREN 0xFFFFFFFF +#define GREY_VEC vec3(0.33333, 0.33333, 0.33333) + +struct CellChildren { + uint children[8]; +}; + +layout(set = 0, binding = 1, std430) buffer CellChildrenBuffer { + CellChildren data[]; +} +cell_children; + +struct CellData { + uint position; // xyz 10 bits + uint albedo; //rgb albedo + uint emission; //rgb normalized with e as multiplier + uint normal; //RGB normal encoded +}; + +layout(set = 0, binding = 2, std430) buffer CellDataBuffer { + CellData data[]; +} +cell_data; + +#endif // MODE DYNAMIC + +#define LIGHT_TYPE_DIRECTIONAL 0 +#define LIGHT_TYPE_OMNI 1 +#define LIGHT_TYPE_SPOT 2 + +#if defined(MODE_COMPUTE_LIGHT) || defined(MODE_DYNAMIC_LIGHTING) + +struct Light { + + uint type; + float energy; + float radius; + float attenuation; + + vec3 color; + float spot_angle_radians; + + vec3 position; + float spot_attenuation; + + vec3 direction; + bool has_shadow; +}; + +layout(set = 0, binding = 3, std140) uniform Lights { + Light data[MAX_LIGHTS]; +} +lights; + +#endif // MODE COMPUTE LIGHT + +#ifdef MODE_SECOND_BOUNCE + +layout(set = 0, binding = 5) uniform texture3D color_texture; + +#ifdef MODE_ANISOTROPIC +layout(set = 0, binding = 7) uniform texture3D aniso_pos_texture; +layout(set = 0, binding = 8) uniform texture3D aniso_neg_texture; +#endif // MODE ANISOTROPIC + +#endif // MODE_SECOND_BOUNCE + +#ifndef MODE_DYNAMIC + +layout(push_constant, binding = 0, std430) uniform Params { + ivec3 limits; + uint stack_size; + + float emission_scale; + float propagation; + float dynamic_range; + + uint light_count; + uint cell_offset; + uint cell_count; + float aniso_strength; + uint pad; +} +params; + +layout(set = 0, binding = 4, std430) buffer Outputs { + vec4 data[]; +} +outputs; + +#endif // MODE DYNAMIC + +layout(set = 0, binding = 9) uniform texture3D texture_sdf; +layout(set = 0, binding = 10) uniform sampler texture_sampler; + +#ifdef MODE_WRITE_TEXTURE + +layout(rgba8, set = 0, binding = 5) uniform restrict writeonly image3D color_tex; + +#ifdef MODE_ANISOTROPIC + +layout(r16ui, set = 0, binding = 6) uniform restrict writeonly uimage3D aniso_pos_tex; +layout(r16ui, set = 0, binding = 7) uniform restrict writeonly uimage3D aniso_neg_tex; + +#endif + +#endif + +#ifdef MODE_DYNAMIC + +layout(push_constant, binding = 0, std430) uniform Params { + ivec3 limits; + uint light_count; //when not lighting + ivec3 x_dir; + float z_base; + ivec3 y_dir; + float z_sign; + ivec3 z_dir; + float pos_multiplier; + ivec2 rect_pos; + ivec2 rect_size; + ivec2 prev_rect_ofs; + ivec2 prev_rect_size; + bool flip_x; + bool flip_y; + float dynamic_range; + bool on_mipmap; + float propagation; + float pad[3]; +} +params; + +#ifdef MODE_DYNAMIC_LIGHTING + +layout(rgba8, set = 0, binding = 5) uniform restrict readonly image2D source_albedo; +layout(rgba8, set = 0, binding = 6) uniform restrict readonly image2D source_normal; +layout(rgba8, set = 0, binding = 7) uniform restrict readonly image2D source_orm; +//layout (set=0,binding=8) uniform texture2D source_depth; +layout(rgba16f, set = 0, binding = 11) uniform restrict image2D emission; +layout(r32f, set = 0, binding = 12) uniform restrict image2D depth; + +#endif + +#ifdef MODE_DYNAMIC_SHRINK + +layout(rgba16f, set = 0, binding = 5) uniform restrict readonly image2D source_light; +layout(r32f, set = 0, binding = 6) uniform restrict readonly image2D source_depth; + +#ifdef MODE_DYNAMIC_SHRINK_WRITE + +layout(rgba16f, set = 0, binding = 7) uniform restrict writeonly image2D light; +layout(r32f, set = 0, binding = 8) uniform restrict writeonly image2D depth; + +#endif // MODE_DYNAMIC_SHRINK_WRITE + +#ifdef MODE_DYNAMIC_SHRINK_PLOT + +layout(rgba8, set = 0, binding = 11) uniform restrict image3D color_texture; + +#ifdef MODE_ANISOTROPIC + +layout(r16ui, set = 0, binding = 12) uniform restrict writeonly uimage3D aniso_pos_texture; +layout(r16ui, set = 0, binding = 13) uniform restrict writeonly uimage3D aniso_neg_texture; + +#endif // MODE ANISOTROPIC + +#endif //MODE_DYNAMIC_SHRINK_PLOT + +#endif // MODE_DYNAMIC_SHRINK + +//layout (rgba8,set=0,binding=5) uniform restrict writeonly image3D color_tex; + +#endif // MODE DYNAMIC + +#if defined(MODE_COMPUTE_LIGHT) || defined(MODE_DYNAMIC_LIGHTING) + +float raymarch(float distance, float distance_adv, vec3 from, vec3 direction) { + + vec3 cell_size = 1.0 / vec3(params.limits); + float occlusion = 1.0; + while (distance > 0.5) { //use this to avoid precision errors + float advance = texture(sampler3D(texture_sdf, texture_sampler), from * cell_size).r * 255.0 - 1.0; + if (advance < 0.0) { + occlusion = 0.0; + break; + } + + occlusion = min(advance, occlusion); + + advance = max(distance_adv, advance - mod(advance, distance_adv)); //should always advance in multiples of distance_adv + + from += direction * advance; + distance -= advance; + } + + return occlusion; //max(0.0,distance); +} + +bool compute_light_vector(uint light, vec3 pos, out float attenuation, out vec3 light_pos) { + + if (lights.data[light].type == LIGHT_TYPE_DIRECTIONAL) { + + light_pos = pos - lights.data[light].direction * length(vec3(params.limits)); + attenuation = 1.0; + + } else { + + light_pos = lights.data[light].position; + float distance = length(pos - light_pos); + if (distance >= lights.data[light].radius) { + return false; + } + + attenuation = pow(clamp(1.0 - distance / lights.data[light].radius, 0.0001, 1.0), lights.data[light].attenuation); + + if (lights.data[light].type == LIGHT_TYPE_SPOT) { + + vec3 rel = normalize(pos - light_pos); + float angle = acos(dot(rel, lights.data[light].direction)); + if (angle > lights.data[light].spot_angle_radians) { + return false; + } + + float d = clamp(angle / lights.data[light].spot_angle_radians, 0, 1); + attenuation *= pow(1.0 - d, lights.data[light].spot_attenuation); + } + } + + return true; +} + +float get_normal_advance(vec3 p_normal) { + + vec3 normal = p_normal; + vec3 unorm = abs(normal); + + if ((unorm.x >= unorm.y) && (unorm.x >= unorm.z)) { + // x code + unorm = normal.x > 0.0 ? vec3(1.0, 0.0, 0.0) : vec3(-1.0, 0.0, 0.0); + } else if ((unorm.y > unorm.x) && (unorm.y >= unorm.z)) { + // y code + unorm = normal.y > 0.0 ? vec3(0.0, 1.0, 0.0) : vec3(0.0, -1.0, 0.0); + } else if ((unorm.z > unorm.x) && (unorm.z > unorm.y)) { + // z code + unorm = normal.z > 0.0 ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 0.0, -1.0); + } else { + // oh-no we messed up code + // has to be + unorm = vec3(1.0, 0.0, 0.0); + } + + return 1.0 / dot(normal, unorm); +} + +void clip_segment(vec4 plane, vec3 begin, inout vec3 end) { + + vec3 segment = begin - end; + float den = dot(plane.xyz, segment); + + //printf("den is %i\n",den); + if (den < 0.0001) { + return; + } + + float dist = (dot(plane.xyz, begin) - plane.w) / den; + + if (dist < 0.0001 || dist > 1.0001) { + return; + } + + end = begin + segment * -dist; +} + +bool compute_light_at_pos(uint index, vec3 pos, vec3 normal, inout vec3 light, inout vec3 light_dir) { + float attenuation; + vec3 light_pos; + + if (!compute_light_vector(index, pos, attenuation, light_pos)) { + return false; + } + + light_dir = normalize(pos - light_pos); + + if (attenuation < 0.01 || (length(normal) > 0.2 && dot(normal, light_dir) >= 0)) { + return false; //not facing the light, or attenuation is near zero + } + + if (lights.data[index].has_shadow) { + + float distance_adv = get_normal_advance(light_dir); + + vec3 to = pos; + if (length(normal) > 0.2) { + to += normal * distance_adv * 0.51; + } else { + to -= sign(light_dir) * 0.45; //go near the edge towards the light direction to avoid self occlusion + } + + //clip + clip_segment(mix(vec4(-1.0, 0.0, 0.0, 0.0), vec4(1.0, 0.0, 0.0, float(params.limits.x - 1)), bvec4(light_dir.x < 0.0)), to, light_pos); + clip_segment(mix(vec4(0.0, -1.0, 0.0, 0.0), vec4(0.0, 1.0, 0.0, float(params.limits.y - 1)), bvec4(light_dir.y < 0.0)), to, light_pos); + clip_segment(mix(vec4(0.0, 0.0, -1.0, 0.0), vec4(0.0, 0.0, 1.0, float(params.limits.z - 1)), bvec4(light_dir.z < 0.0)), to, light_pos); + + float distance = length(to - light_pos); + if (distance < 0.1) { + return false; // hit + } + + distance += distance_adv - mod(distance, distance_adv); //make it reach the center of the box always + light_pos = to - light_dir * distance; + + //from -= sign(light_dir)*0.45; //go near the edge towards the light direction to avoid self occlusion + + /*float dist = raymarch(distance,distance_adv,light_pos,light_dir); + + if (dist > distance_adv) { + return false; + } + + attenuation *= 1.0 - smoothstep(0.1*distance_adv,distance_adv,dist); + */ + + float occlusion = raymarch(distance, distance_adv, light_pos, light_dir); + + if (occlusion == 0.0) { + return false; + } + + attenuation *= occlusion; //1.0 - smoothstep(0.1*distance_adv,distance_adv,dist); + } + + light = lights.data[index].color * attenuation * lights.data[index].energy; + return true; +} + +#endif // MODE COMPUTE LIGHT + +void main() { + +#ifndef MODE_DYNAMIC + + uint cell_index = gl_GlobalInvocationID.x; + if (cell_index >= params.cell_count) { + return; + } + cell_index += params.cell_offset; + + uvec3 posu = uvec3(cell_data.data[cell_index].position & 0x7FF, (cell_data.data[cell_index].position >> 11) & 0x3FF, cell_data.data[cell_index].position >> 21); + vec4 albedo = unpackUnorm4x8(cell_data.data[cell_index].albedo); + +#endif + + /////////////////COMPUTE LIGHT/////////////////////////////// + +#ifdef MODE_COMPUTE_LIGHT + + vec3 pos = vec3(posu) + vec3(0.5); + + vec3 emission = vec3(uvec3(cell_data.data[cell_index].emission & 0x1ff, (cell_data.data[cell_index].emission >> 9) & 0x1ff, (cell_data.data[cell_index].emission >> 18) & 0x1ff)) * pow(2.0, float(cell_data.data[cell_index].emission >> 27) - 15.0 - 9.0); + vec3 normal = unpackSnorm4x8(cell_data.data[cell_index].normal).xyz; + +#ifdef MODE_ANISOTROPIC + vec3 accum[6] = vec3[](vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0)); + const vec3 accum_dirs[6] = vec3[](vec3(1.0, 0.0, 0.0), vec3(-1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0), vec3(0.0, -1.0, 0.0), vec3(0.0, 0.0, 1.0), vec3(0.0, 0.0, -1.0)); +#else + vec3 accum = vec3(0.0); +#endif + + for (uint i = 0; i < params.light_count; i++) { + + vec3 light; + vec3 light_dir; + if (!compute_light_at_pos(i, pos, normal.xyz, light, light_dir)) { + continue; + } + + light *= albedo.rgb; + +#ifdef MODE_ANISOTROPIC + for (uint j = 0; j < 6; j++) { + + accum[j] += max(0.0, dot(accum_dirs[j], -light_dir)) * light; + } +#else + if (length(normal) > 0.2) { + accum += max(0.0, dot(normal, -light_dir)) * light; + } else { + //all directions + accum += light; + } +#endif + } + +#ifdef MODE_ANISOTROPIC + + for (uint i = 0; i < 6; i++) { + vec3 light = accum[i]; + if (length(normal) > 0.2) { + light += max(0.0, dot(accum_dirs[i], -normal)) * emission; + } else { + light += emission; + } + + outputs.data[cell_index * 6 + i] = vec4(light, 0.0); + } + +#else + outputs.data[cell_index] = vec4(accum + emission, 0.0); + +#endif + +#endif //MODE_COMPUTE_LIGHT + + /////////////////SECOND BOUNCE/////////////////////////////// + +#ifdef MODE_SECOND_BOUNCE + vec3 pos = vec3(posu) + vec3(0.5); + ivec3 ipos = ivec3(posu); + vec4 normal = unpackSnorm4x8(cell_data.data[cell_index].normal); + +#ifdef MODE_ANISOTROPIC + vec3 accum[6]; + const vec3 accum_dirs[6] = vec3[](vec3(1.0, 0.0, 0.0), vec3(-1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0), vec3(0.0, -1.0, 0.0), vec3(0.0, 0.0, 1.0), vec3(0.0, 0.0, -1.0)); + + /*vec3 src_color = texelFetch(sampler3D(color_texture,texture_sampler),ipos,0).rgb * params.dynamic_range; + vec3 src_aniso_pos = texelFetch(sampler3D(aniso_pos_texture,texture_sampler),ipos,0).rgb; + vec3 src_anisp_neg = texelFetch(sampler3D(anisp_neg_texture,texture_sampler),ipos,0).rgb; + accum[0]=src_col * src_aniso_pos.x; + accum[1]=src_col * src_aniso_neg.x; + accum[2]=src_col * src_aniso_pos.y; + accum[3]=src_col * src_aniso_neg.y; + accum[4]=src_col * src_aniso_pos.z; + accum[5]=src_col * src_aniso_neg.z;*/ + + accum[0] = outputs.data[cell_index * 6 + 0].rgb; + accum[1] = outputs.data[cell_index * 6 + 1].rgb; + accum[2] = outputs.data[cell_index * 6 + 2].rgb; + accum[3] = outputs.data[cell_index * 6 + 3].rgb; + accum[4] = outputs.data[cell_index * 6 + 4].rgb; + accum[5] = outputs.data[cell_index * 6 + 5].rgb; + +#else + vec3 accum = outputs.data[cell_index].rgb; + +#endif + + if (length(normal.xyz) > 0.2) { + + vec3 v0 = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 1.0, 0.0); + vec3 tangent = normalize(cross(v0, normal.xyz)); + vec3 bitangent = normalize(cross(tangent, normal.xyz)); + mat3 normal_mat = mat3(tangent, bitangent, normal.xyz); + +#define MAX_CONE_DIRS 6 + + vec3 cone_dirs[MAX_CONE_DIRS] = vec3[]( + vec3(0.0, 0.0, 1.0), + vec3(0.866025, 0.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)); + + float cone_weights[MAX_CONE_DIRS] = float[](0.25, 0.15, 0.15, 0.15, 0.15, 0.15); + float tan_half_angle = 0.577; + + for (int i = 0; i < MAX_CONE_DIRS; i++) { + + vec3 direction = normal_mat * cone_dirs[i]; + vec4 color = vec4(0.0); + { + + float dist = 1.5; + float max_distance = length(vec3(params.limits)); + vec3 cell_size = 1.0 / vec3(params.limits); + +#ifdef MODE_ANISOTROPIC + vec3 aniso_normal = mix(direction, normal.xyz, params.aniso_strength); +#endif + while (dist < max_distance && color.a < 0.95) { + float diameter = max(1.0, 2.0 * tan_half_angle * dist); + vec3 uvw_pos = (pos + dist * direction) * cell_size; + float half_diameter = diameter * 0.5; + //check if outside, then break + //if ( any(greaterThan(abs(uvw_pos - 0.5),vec3(0.5f + half_diameter * cell_size)) ) ) { + // break; + //} + + float log2_diameter = log2(diameter); + vec4 scolor = textureLod(sampler3D(color_texture, texture_sampler), uvw_pos, log2_diameter); +#ifdef MODE_ANISOTROPIC + + vec3 aniso_neg = textureLod(sampler3D(aniso_neg_texture, texture_sampler), uvw_pos, log2_diameter).rgb; + vec3 aniso_pos = textureLod(sampler3D(aniso_pos_texture, texture_sampler), uvw_pos, log2_diameter).rgb; + + scolor.rgb *= dot(max(vec3(0.0), (aniso_normal * aniso_pos)), vec3(1.0)) + dot(max(vec3(0.0), (-aniso_normal * aniso_neg)), vec3(1.0)); +#endif + float a = (1.0 - color.a); + color += a * scolor; + dist += half_diameter; + } + } + color *= cone_weights[i] * vec4(albedo.rgb, 1.0) * params.dynamic_range; //restore range +#ifdef MODE_ANISOTROPIC + for (uint j = 0; j < 6; j++) { + + accum[j] += max(0.0, dot(accum_dirs[j], direction)) * color.rgb; + } +#else + accum += color.rgb; +#endif + } + } + +#ifdef MODE_ANISOTROPIC + + outputs.data[cell_index * 6 + 0] = vec4(accum[0], 0.0); + outputs.data[cell_index * 6 + 1] = vec4(accum[1], 0.0); + outputs.data[cell_index * 6 + 2] = vec4(accum[2], 0.0); + outputs.data[cell_index * 6 + 3] = vec4(accum[3], 0.0); + outputs.data[cell_index * 6 + 4] = vec4(accum[4], 0.0); + outputs.data[cell_index * 6 + 5] = vec4(accum[5], 0.0); +#else + outputs.data[cell_index] = vec4(accum, 0.0); + +#endif + +#endif // MODE_SECOND_BOUNCE + + /////////////////UPDATE MIPMAPS/////////////////////////////// + +#ifdef MODE_UPDATE_MIPMAPS + + { +#ifdef MODE_ANISOTROPIC + vec3 light_accum[6] = vec3[](vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0)); +#else + vec3 light_accum = vec3(0.0); +#endif + float count = 0.0; + for (uint i = 0; i < 8; i++) { + uint child_index = cell_children.data[cell_index].children[i]; + if (child_index == NO_CHILDREN) { + continue; + } +#ifdef MODE_ANISOTROPIC + light_accum[0] += outputs.data[child_index * 6 + 0].rgb; + light_accum[1] += outputs.data[child_index * 6 + 1].rgb; + light_accum[2] += outputs.data[child_index * 6 + 2].rgb; + light_accum[3] += outputs.data[child_index * 6 + 3].rgb; + light_accum[4] += outputs.data[child_index * 6 + 4].rgb; + light_accum[5] += outputs.data[child_index * 6 + 5].rgb; + +#else + light_accum += outputs.data[child_index].rgb; + +#endif + + count += 1.0; + } + + float divisor = mix(8.0, count, params.propagation); +#ifdef MODE_ANISOTROPIC + outputs.data[cell_index * 6 + 0] = vec4(light_accum[0] / divisor, 0.0); + outputs.data[cell_index * 6 + 1] = vec4(light_accum[1] / divisor, 0.0); + outputs.data[cell_index * 6 + 2] = vec4(light_accum[2] / divisor, 0.0); + outputs.data[cell_index * 6 + 3] = vec4(light_accum[3] / divisor, 0.0); + outputs.data[cell_index * 6 + 4] = vec4(light_accum[4] / divisor, 0.0); + outputs.data[cell_index * 6 + 5] = vec4(light_accum[5] / divisor, 0.0); + +#else + outputs.data[cell_index] = vec4(light_accum / divisor, 0.0); +#endif + } +#endif + + ///////////////////WRITE TEXTURE///////////////////////////// + +#ifdef MODE_WRITE_TEXTURE + { + +#ifdef MODE_ANISOTROPIC + vec3 accum_total = vec3(0.0); + accum_total += outputs.data[cell_index * 6 + 0].rgb; + accum_total += outputs.data[cell_index * 6 + 1].rgb; + accum_total += outputs.data[cell_index * 6 + 2].rgb; + accum_total += outputs.data[cell_index * 6 + 3].rgb; + accum_total += outputs.data[cell_index * 6 + 4].rgb; + accum_total += outputs.data[cell_index * 6 + 5].rgb; + + float accum_total_energy = max(dot(accum_total, GREY_VEC), 0.00001); + vec3 iso_positive = vec3(dot(outputs.data[cell_index * 6 + 0].rgb, GREY_VEC), dot(outputs.data[cell_index * 6 + 2].rgb, GREY_VEC), dot(outputs.data[cell_index * 6 + 4].rgb, GREY_VEC)) / vec3(accum_total_energy); + vec3 iso_negative = vec3(dot(outputs.data[cell_index * 6 + 1].rgb, GREY_VEC), dot(outputs.data[cell_index * 6 + 3].rgb, GREY_VEC), dot(outputs.data[cell_index * 6 + 5].rgb, GREY_VEC)) / vec3(accum_total_energy); + + { + uint aniso_pos = uint(clamp(iso_positive.b * 31.0, 0.0, 31.0)); + aniso_pos |= uint(clamp(iso_positive.g * 63.0, 0.0, 63.0)) << 5; + aniso_pos |= uint(clamp(iso_positive.r * 31.0, 0.0, 31.0)) << 11; + imageStore(aniso_pos_tex, ivec3(posu), uvec4(aniso_pos)); + } + + { + uint aniso_neg = uint(clamp(iso_negative.b * 31.0, 0.0, 31.0)); + aniso_neg |= uint(clamp(iso_negative.g * 63.0, 0.0, 63.0)) << 5; + aniso_neg |= uint(clamp(iso_negative.r * 31.0, 0.0, 31.0)) << 11; + imageStore(aniso_neg_tex, ivec3(posu), uvec4(aniso_neg)); + } + + imageStore(color_tex, ivec3(posu), vec4(accum_total / params.dynamic_range, albedo.a)); + +#else + + imageStore(color_tex, ivec3(posu), vec4(outputs.data[cell_index].rgb / params.dynamic_range, albedo.a)); + +#endif + } +#endif + + ///////////////////DYNAMIC LIGHTING///////////////////////////// + +#ifdef MODE_DYNAMIC + + ivec2 pos_xy = ivec2(gl_GlobalInvocationID.xy); + if (any(greaterThanEqual(pos_xy, params.rect_size))) { + return; //out of bounds + } + + ivec2 uv_xy = pos_xy; + if (params.flip_x) { + uv_xy.x = params.rect_size.x - pos_xy.x - 1; + } + if (params.flip_y) { + uv_xy.y = params.rect_size.y - pos_xy.y - 1; + } + +#ifdef MODE_DYNAMIC_LIGHTING + + { + float z = params.z_base + imageLoad(depth, uv_xy).x * params.z_sign; + + ivec3 pos = params.x_dir * (params.rect_pos.x + pos_xy.x) + params.y_dir * (params.rect_pos.y + pos_xy.y) + abs(params.z_dir) * int(z); + + vec3 normal = imageLoad(source_normal, uv_xy).xyz * 2.0 - 1.0; + normal = vec3(params.x_dir) * normal.x * mix(1.0, -1.0, params.flip_x) + vec3(params.y_dir) * normal.y * mix(1.0, -1.0, params.flip_y) - vec3(params.z_dir) * normal.z; + + vec4 albedo = imageLoad(source_albedo, uv_xy); + + //determine the position in space + + vec3 accum = vec3(0.0); + for (uint i = 0; i < params.light_count; i++) { + + vec3 light; + vec3 light_dir; + if (!compute_light_at_pos(i, vec3(pos) * params.pos_multiplier, normal, light, light_dir)) { + continue; + } + + light *= albedo.rgb; + + accum += max(0.0, dot(normal, -light_dir)) * light; + } + + accum += imageLoad(emission, uv_xy).xyz; + + imageStore(emission, uv_xy, vec4(accum, albedo.a)); + imageStore(depth, uv_xy, vec4(z)); + } + +#endif // MODE DYNAMIC LIGHTING + +#ifdef MODE_DYNAMIC_SHRINK + + { + vec4 accum = vec4(0.0); + float accum_z = 0.0; + float count = 0.0; + + for (int i = 0; i < 4; i++) { + ivec2 ofs = pos_xy * 2 + ivec2(i & 1, i >> 1) - params.prev_rect_ofs; + if (any(lessThan(ofs, ivec2(0))) || any(greaterThanEqual(ofs, params.prev_rect_size))) { + continue; + } + if (params.flip_x) { + ofs.x = params.prev_rect_size.x - ofs.x - 1; + } + if (params.flip_y) { + ofs.y = params.prev_rect_size.y - ofs.y - 1; + } + + vec4 light = imageLoad(source_light, ofs); + if (light.a == 0.0) { //ignore empty + continue; + } + accum += light; + float z = imageLoad(source_depth, ofs).x; + accum_z += z * 0.5; //shrink half too + count += 1.0; + } + + if (params.on_mipmap) { + accum.rgb /= mix(8.0, count, params.propagation); + accum.a /= 8.0; + } else { + accum /= 4.0; + } + + if (count == 0.0) { + accum_z = 0.0; //avoid nan + } else { + accum_z /= count; + } + +#ifdef MODE_DYNAMIC_SHRINK_WRITE + + imageStore(light, uv_xy, accum); + imageStore(depth, uv_xy, vec4(accum_z)); +#endif + +#ifdef MODE_DYNAMIC_SHRINK_PLOT + + if (accum.a < 0.001) { + return; //do not blit if alpha is too low + } + + ivec3 pos = params.x_dir * (params.rect_pos.x + pos_xy.x) + params.y_dir * (params.rect_pos.y + pos_xy.y) + abs(params.z_dir) * int(accum_z); + + float z_frac = fract(accum_z); + + for (int i = 0; i < 2; i++) { + ivec3 pos3d = pos + abs(params.z_dir) * i; + if (any(lessThan(pos3d, ivec3(0))) || any(greaterThanEqual(pos3d, params.limits))) { + //skip if offlimits + continue; + } + vec4 color_blit = accum * (i == 0 ? 1.0 - z_frac : z_frac); + vec4 color = imageLoad(color_texture, pos3d); + color.rgb *= params.dynamic_range; + +#if 0 + color.rgb = mix(color.rgb,color_blit.rgb,color_blit.a); + color.a+=color_blit.a; +#else + + float sa = 1.0 - color_blit.a; + vec4 result; + result.a = color.a * sa + color_blit.a; + if (result.a == 0.0) { + result = vec4(0.0); + } else { + result.rgb = (color.rgb * color.a * sa + color_blit.rgb * color_blit.a) / result.a; + color = result; + } + +#endif + color.rgb /= params.dynamic_range; + imageStore(color_texture, pos3d, color); + //imageStore(color_texture,pos3d,vec4(1,1,1,1)); + +#ifdef MODE_ANISOTROPIC + //do not care about anisotropy for dynamic objects, just store full lit in all directions + imageStore(aniso_pos_texture, pos3d, uvec4(0xFFFF)); + imageStore(aniso_neg_texture, pos3d, uvec4(0xFFFF)); + +#endif // ANISOTROPIC + } +#endif // MODE_DYNAMIC_SHRINK_PLOT + } +#endif + +#endif // MODE DYNAMIC +} diff --git a/servers/rendering/rasterizer_rd/shaders/giprobe_debug.glsl b/servers/rendering/rasterizer_rd/shaders/giprobe_debug.glsl new file mode 100644 index 0000000000..b1784e7eee --- /dev/null +++ b/servers/rendering/rasterizer_rd/shaders/giprobe_debug.glsl @@ -0,0 +1,208 @@ +/* clang-format off */ +[vertex] + +#version 450 + +VERSION_DEFINES + +struct CellData { + uint position; // xyz 10 bits + uint albedo; //rgb albedo + uint emission; //rgb normalized with e as multiplier + uint normal; //RGB normal encoded +}; +/* clang-format on */ + +layout(set = 0, binding = 1, std140) buffer CellDataBuffer { + CellData data[]; +} +cell_data; + +layout(set = 0, binding = 2) uniform texture3D color_tex; + +layout(set = 0, binding = 3) uniform sampler tex_sampler; + +#ifdef USE_ANISOTROPY +layout(set = 0, binding = 4) uniform texture3D aniso_pos_tex; +layout(set = 0, binding = 5) uniform texture3D aniso_neg_tex; +#endif + +layout(push_constant, binding = 0, std430) uniform Params { + + mat4 projection; + uint cell_offset; + float dynamic_range; + float alpha; + uint level; + ivec3 bounds; + uint pad; +} +params; + +layout(location = 0) out vec4 color_interp; + +void main() { + + const vec3 cube_triangles[36] = vec3[]( + vec3(-1.0f, -1.0f, -1.0f), + vec3(-1.0f, -1.0f, 1.0f), + vec3(-1.0f, 1.0f, 1.0f), + vec3(1.0f, 1.0f, -1.0f), + vec3(-1.0f, -1.0f, -1.0f), + vec3(-1.0f, 1.0f, -1.0f), + vec3(1.0f, -1.0f, 1.0f), + vec3(-1.0f, -1.0f, -1.0f), + vec3(1.0f, -1.0f, -1.0f), + vec3(1.0f, 1.0f, -1.0f), + vec3(1.0f, -1.0f, -1.0f), + vec3(-1.0f, -1.0f, -1.0f), + vec3(-1.0f, -1.0f, -1.0f), + vec3(-1.0f, 1.0f, 1.0f), + vec3(-1.0f, 1.0f, -1.0f), + vec3(1.0f, -1.0f, 1.0f), + vec3(-1.0f, -1.0f, 1.0f), + vec3(-1.0f, -1.0f, -1.0f), + vec3(-1.0f, 1.0f, 1.0f), + vec3(-1.0f, -1.0f, 1.0f), + vec3(1.0f, -1.0f, 1.0f), + vec3(1.0f, 1.0f, 1.0f), + vec3(1.0f, -1.0f, -1.0f), + vec3(1.0f, 1.0f, -1.0f), + vec3(1.0f, -1.0f, -1.0f), + vec3(1.0f, 1.0f, 1.0f), + vec3(1.0f, -1.0f, 1.0f), + vec3(1.0f, 1.0f, 1.0f), + vec3(1.0f, 1.0f, -1.0f), + vec3(-1.0f, 1.0f, -1.0f), + vec3(1.0f, 1.0f, 1.0f), + vec3(-1.0f, 1.0f, -1.0f), + vec3(-1.0f, 1.0f, 1.0f), + vec3(1.0f, 1.0f, 1.0f), + vec3(-1.0f, 1.0f, 1.0f), + vec3(1.0f, -1.0f, 1.0f)); + + vec3 vertex = cube_triangles[gl_VertexIndex] * 0.5 + 0.5; +#ifdef MODE_DEBUG_LIGHT_FULL + uvec3 posu = uvec3(gl_InstanceIndex % params.bounds.x, (gl_InstanceIndex / params.bounds.x) % params.bounds.y, gl_InstanceIndex / (params.bounds.y * params.bounds.x)); +#else + uint cell_index = gl_InstanceIndex + params.cell_offset; + + uvec3 posu = uvec3(cell_data.data[cell_index].position & 0x7FF, (cell_data.data[cell_index].position >> 11) & 0x3FF, cell_data.data[cell_index].position >> 21); +#endif + +#ifdef MODE_DEBUG_EMISSION + color_interp.xyz = vec3(uvec3(cell_data.data[cell_index].emission & 0x1ff, (cell_data.data[cell_index].emission >> 9) & 0x1ff, (cell_data.data[cell_index].emission >> 18) & 0x1ff)) * pow(2.0, float(cell_data.data[cell_index].emission >> 27) - 15.0 - 9.0); +#endif + +#ifdef MODE_DEBUG_COLOR + color_interp.xyz = unpackUnorm4x8(cell_data.data[cell_index].albedo).xyz; +#endif + +#ifdef MODE_DEBUG_LIGHT + +#ifdef USE_ANISOTROPY + +#define POS_X 0 +#define POS_Y 1 +#define POS_Z 2 +#define NEG_X 3 +#define NEG_Y 4 +#define NEG_Z 5 + + const uint triangle_aniso[12] = uint[]( + NEG_X, + NEG_Z, + NEG_Y, + NEG_Z, + NEG_X, + NEG_Y, + POS_Z, + POS_X, + POS_X, + POS_Y, + POS_Y, + POS_Z); + + color_interp.xyz = texelFetch(sampler3D(color_tex, tex_sampler), ivec3(posu), int(params.level)).xyz * params.dynamic_range; + vec3 aniso_pos = texelFetch(sampler3D(aniso_pos_tex, tex_sampler), ivec3(posu), int(params.level)).xyz; + vec3 aniso_neg = texelFetch(sampler3D(aniso_neg_tex, tex_sampler), ivec3(posu), int(params.level)).xyz; + uint side = triangle_aniso[gl_VertexIndex / 3]; + + float strength = 0.0; + switch (side) { + case POS_X: strength = aniso_pos.x; break; + case POS_Y: strength = aniso_pos.y; break; + case POS_Z: strength = aniso_pos.z; break; + case NEG_X: strength = aniso_neg.x; break; + case NEG_Y: strength = aniso_neg.y; break; + case NEG_Z: strength = aniso_neg.z; break; + } + + color_interp.xyz *= strength; + +#else + color_interp = texelFetch(sampler3D(color_tex, tex_sampler), ivec3(posu), int(params.level)); + color_interp.xyz *params.dynamic_range; + +#endif + +#endif + float scale = (1 << params.level); + + gl_Position = params.projection * vec4((vec3(posu) + vertex) * scale, 1.0); + +#ifdef MODE_DEBUG_LIGHT_FULL + if (color_interp.a == 0.0) { + gl_Position = vec4(0.0); //force clip and not draw + } +#else + color_interp.a = params.alpha; +#endif +} + +/* clang-format off */ +[fragment] + +#version 450 + +VERSION_DEFINES + +layout(location = 0) in vec4 color_interp; +/* clang-format on */ +layout(location = 0) out vec4 frag_color; + +void main() { + + frag_color = color_interp; + +#ifdef MODE_DEBUG_LIGHT_FULL + + //there really is no alpha, so use dither + + int x = int(gl_FragCoord.x) % 4; + int y = int(gl_FragCoord.y) % 4; + int index = x + y * 4; + float limit = 0.0; + if (x < 8) { + if (index == 0) limit = 0.0625; + if (index == 1) limit = 0.5625; + if (index == 2) limit = 0.1875; + if (index == 3) limit = 0.6875; + if (index == 4) limit = 0.8125; + if (index == 5) limit = 0.3125; + if (index == 6) limit = 0.9375; + if (index == 7) limit = 0.4375; + if (index == 8) limit = 0.25; + if (index == 9) limit = 0.75; + if (index == 10) limit = 0.125; + if (index == 11) limit = 0.625; + if (index == 12) limit = 1.0; + if (index == 13) limit = 0.5; + if (index == 14) limit = 0.875; + if (index == 15) limit = 0.375; + } + if (frag_color.a < limit) { + discard; + } +#endif +} diff --git a/servers/rendering/rasterizer_rd/shaders/giprobe_sdf.glsl b/servers/rendering/rasterizer_rd/shaders/giprobe_sdf.glsl new file mode 100644 index 0000000000..d089236723 --- /dev/null +++ b/servers/rendering/rasterizer_rd/shaders/giprobe_sdf.glsl @@ -0,0 +1,187 @@ +/* clang-format off */ +[compute] + +#version 450 + +VERSION_DEFINES + +layout(local_size_x = 4, local_size_y = 4, local_size_z = 4) in; +/* clang-format on */ + +#define MAX_DISTANCE 100000 + +#define NO_CHILDREN 0xFFFFFFFF +#define GREY_VEC vec3(0.33333, 0.33333, 0.33333) + +struct CellChildren { + uint children[8]; +}; + +layout(set = 0, binding = 1, std430) buffer CellChildrenBuffer { + CellChildren data[]; +} +cell_children; + +struct CellData { + uint position; // xyz 10 bits + uint albedo; //rgb albedo + uint emission; //rgb normalized with e as multiplier + uint normal; //RGB normal encoded +}; + +layout(set = 0, binding = 2, std430) buffer CellDataBuffer { + CellData data[]; +} +cell_data; + +layout(r8ui, set = 0, binding = 3) uniform restrict writeonly uimage3D sdf_tex; + +layout(push_constant, binding = 0, std430) uniform Params { + uint offset; + uint end; + uint pad0; + uint pad1; +} +params; + +void main() { + + vec3 pos = vec3(gl_GlobalInvocationID); + float closest_dist = 100000.0; + + for (uint i = params.offset; i < params.end; i++) { + vec3 posu = vec3(uvec3(cell_data.data[i].position & 0x7FF, (cell_data.data[i].position >> 11) & 0x3FF, cell_data.data[i].position >> 21)); + float dist = length(pos - posu); + if (dist < closest_dist) { + closest_dist = dist; + } + } + + uint dist_8; + + if (closest_dist < 0.0001) { // same cell + dist_8 = 0; //equals to -1 + } else { + dist_8 = clamp(uint(closest_dist), 0, 254) + 1; //conservative, 0 is 1, so <1 is considered solid + } + + imageStore(sdf_tex, ivec3(gl_GlobalInvocationID), uvec4(dist_8)); + //imageStore(sdf_tex,pos,uvec4(pos*2,0)); +} + +#if 0 +layout(push_constant, binding = 0, std430) uniform Params { + + ivec3 limits; + uint stack_size; +} params; + +float distance_to_aabb(ivec3 pos, ivec3 aabb_pos, ivec3 aabb_size) { + + vec3 delta = vec3(max(ivec3(0), max(aabb_pos - pos, pos - (aabb_pos + aabb_size - ivec3(1))))); + return length(delta); +} + +void main() { + + ivec3 pos = ivec3(gl_GlobalInvocationID); + + uint stack[10] = uint[](0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + uint stack_indices[10] = uint[](0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + ivec3 stack_positions[10] = ivec3[](ivec3(0), ivec3(0), ivec3(0), ivec3(0), ivec3(0), ivec3(0), ivec3(0), ivec3(0), ivec3(0), ivec3(0)); + + const uint cell_orders[8] = uint[]( + 0x11f58d1, + 0xe2e70a, + 0xd47463, + 0xbb829c, + 0x8d11f5, + 0x70ae2e, + 0x463d47, + 0x29cbb8); + + bool cell_found = false; + bool cell_found_exact = false; + ivec3 closest_cell_pos; + float closest_distance = MAX_DISTANCE; + int stack_pos = 0; + + while (true) { + + uint index = stack_indices[stack_pos] >> 24; + + if (index == 8) { + //go up + if (stack_pos == 0) { + break; //done going through octree + } + stack_pos--; + continue; + } + + stack_indices[stack_pos] = (stack_indices[stack_pos] & ((1 << 24) - 1)) | ((index + 1) << 24); + + uint cell_index = (stack_indices[stack_pos] >> (index * 3)) & 0x7; + uint child_cell = cell_children.data[stack[stack_pos]].children[cell_index]; + + if (child_cell == NO_CHILDREN) { + continue; + } + + ivec3 child_cell_size = params.limits >> (stack_pos + 1); + ivec3 child_cell_pos = stack_positions[stack_pos]; + + child_cell_pos += mix(ivec3(0), child_cell_size, bvec3(uvec3(index & 1, index & 2, index & 4) != uvec3(0))); + + bool is_leaf = stack_pos == (params.stack_size - 2); + + if (child_cell_pos == pos && is_leaf) { + //we may actually end up in the exact cell. + //if this happens, just abort + cell_found_exact = true; + break; + } + + if (cell_found) { + //discard by distance + float distance = distance_to_aabb(pos, child_cell_pos, child_cell_size); + if (distance >= closest_distance) { + continue; //pointless, just test next child + } else if (is_leaf) { + //closer than what we have AND end of stack, save and continue + closest_cell_pos = child_cell_pos; + closest_distance = distance; + continue; + } + } else if (is_leaf) { + //first solid cell we find, save and continue + closest_distance = distance_to_aabb(pos, child_cell_pos, child_cell_size); + closest_cell_pos = child_cell_pos; + cell_found = true; + continue; + } + + bvec3 direction = greaterThan((pos - (child_cell_pos + (child_cell_size >> 1))), ivec3(0)); + uint cell_order = 0; + cell_order |= mix(0, 1, direction.x); + cell_order |= mix(0, 2, direction.y); + cell_order |= mix(0, 4, direction.z); + + stack[stack_pos + 1] = child_cell; + stack_indices[stack_pos + 1] = cell_orders[cell_order]; //start counting + stack_positions[stack_pos + 1] = child_cell_pos; + stack_pos++; //go up stack + } + + uint dist_8; + + if (cell_found_exact) { + dist_8 = 0; //equals to -1 + } else { + float closest_distance = length(vec3(pos - closest_cell_pos)); + dist_8 = clamp(uint(closest_distance), 0, 254) + 1; //conservative, 0 is 1, so <1 is considered solid + } + + imageStore(sdf_tex, pos, uvec4(dist_8)); +} +#endif diff --git a/servers/rendering/rasterizer_rd/shaders/giprobe_write.glsl b/servers/rendering/rasterizer_rd/shaders/giprobe_write.glsl new file mode 100644 index 0000000000..c832223b1e --- /dev/null +++ b/servers/rendering/rasterizer_rd/shaders/giprobe_write.glsl @@ -0,0 +1,335 @@ +/* clang-format off */ +[compute] + +#version 450 + +VERSION_DEFINES + +layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in; +/* clang-format on */ + +#define NO_CHILDREN 0xFFFFFFFF +#define GREY_VEC vec3(0.33333, 0.33333, 0.33333) + +struct CellChildren { + uint children[8]; +}; + +layout(set = 0, binding = 1, std430) buffer CellChildrenBuffer { + CellChildren data[]; +} +cell_children; + +struct CellData { + uint position; // xyz 10 bits + uint albedo; //rgb albedo + uint emission; //rgb normalized with e as multiplier + uint normal; //RGB normal encoded +}; + +layout(set = 0, binding = 2, std430) buffer CellDataBuffer { + CellData data[]; +} +cell_data; + +#define LIGHT_TYPE_DIRECTIONAL 0 +#define LIGHT_TYPE_OMNI 1 +#define LIGHT_TYPE_SPOT 2 + +#ifdef MODE_COMPUTE_LIGHT + +struct Light { + uint type; + float energy; + float radius; + float attenuation; + + vec3 color; + float spot_angle_radians; + + vec3 position; + float spot_attenuation; + + vec3 direction; + bool has_shadow; +}; + +layout(set = 0, binding = 3, std140) uniform Lights { + Light data[MAX_LIGHTS]; +} +lights; + +#endif + +layout(push_constant, binding = 0, std430) uniform Params { + ivec3 limits; + uint stack_size; + + float emission_scale; + float propagation; + float dynamic_range; + + uint light_count; + uint cell_offset; + uint cell_count; + uint pad[2]; +} +params; + +layout(set = 0, binding = 4, std140) uniform Outputs { + vec4 data[]; +} +output; + +#ifdef MODE_COMPUTE_LIGHT + +uint raymarch(float distance, float distance_adv, vec3 from, vec3 direction) { + + uint result = NO_CHILDREN; + + ivec3 size = ivec3(max(max(params.limits.x, params.limits.y), params.limits.z)); + + while (distance > -distance_adv) { //use this to avoid precision errors + + uint cell = 0; + + ivec3 pos = ivec3(from); + + if (all(greaterThanEqual(pos, ivec3(0))) && all(lessThan(pos, size))) { + + ivec3 ofs = ivec3(0); + ivec3 half_size = size / 2; + + for (int i = 0; i < params.stack_size - 1; i++) { + + bvec3 greater = greaterThanEqual(pos, ofs + half_size); + + ofs += mix(ivec3(0), half_size, greater); + + uint child = 0; //wonder if this can be done faster + if (greater.x) { + child |= 1; + } + if (greater.y) { + child |= 2; + } + if (greater.z) { + child |= 4; + } + + cell = cell_children.data[cell].children[child]; + if (cell == NO_CHILDREN) + break; + + half_size >>= ivec3(1); + } + + if (cell != NO_CHILDREN) { + return cell; //found cell! + } + } + + from += direction * distance_adv; + distance -= distance_adv; + } + + return NO_CHILDREN; +} + +bool compute_light_vector(uint light, uint cell, vec3 pos, out float attenuation, out vec3 light_pos) { + + if (lights.data[light].type == LIGHT_TYPE_DIRECTIONAL) { + + light_pos = pos - lights.data[light].direction * length(vec3(params.limits)); + attenuation = 1.0; + + } else { + + light_pos = lights.data[light].position; + float distance = length(pos - light_pos); + if (distance >= lights.data[light].radius) { + return false; + } + + attenuation = pow(clamp(1.0 - distance / lights.data[light].radius, 0.0001, 1.0), lights.data[light].attenuation); + + if (lights.data[light].type == LIGHT_TYPE_SPOT) { + + vec3 rel = normalize(pos - light_pos); + float angle = acos(dot(rel, lights.data[light].direction)); + if (angle > lights.data[light].spot_angle_radians) { + return false; + } + + float d = clamp(angle / lights.data[light].spot_angle_radians, 0, 1); + attenuation *= pow(1.0 - d, lights.data[light].spot_attenuation); + } + } + + return true; +} + +float get_normal_advance(vec3 p_normal) { + + vec3 normal = p_normal; + vec3 unorm = abs(normal); + + if ((unorm.x >= unorm.y) && (unorm.x >= unorm.z)) { + // x code + unorm = normal.x > 0.0 ? vec3(1.0, 0.0, 0.0) : vec3(-1.0, 0.0, 0.0); + } else if ((unorm.y > unorm.x) && (unorm.y >= unorm.z)) { + // y code + unorm = normal.y > 0.0 ? vec3(0.0, 1.0, 0.0) : vec3(0.0, -1.0, 0.0); + } else if ((unorm.z > unorm.x) && (unorm.z > unorm.y)) { + // z code + unorm = normal.z > 0.0 ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 0.0, -1.0); + } else { + // oh-no we messed up code + // has to be + unorm = vec3(1.0, 0.0, 0.0); + } + + return 1.0 / dot(normal, unorm); +} + +#endif + +void main() { + + uint cell_index = gl_GlobalInvocationID.x; + if (cell_index >= params.cell_count) { + return; + } + cell_index += params.cell_offset; + + uvec3 posu = uvec3(cell_data.data[cell_index].position & 0x7FF, (cell_data.data[cell_index].position >> 11) & 0x3FF, cell_data.data[cell_index].position >> 21); + vec4 albedo = unpackUnorm4x8(cell_data.data[cell_index].albedo); + +#ifdef MODE_COMPUTE_LIGHT + + vec3 pos = vec3(posu) + vec3(0.5); + + vec3 emission = vec3(ivec3(cell_data.data[cell_index].emission & 0x3FF, (cell_data.data[cell_index].emission >> 10) & 0x7FF, cell_data.data[cell_index].emission >> 21)) * params.emission_scale; + vec4 normal = unpackSnorm4x8(cell_data.data[cell_index].normal); + +#ifdef MODE_ANISOTROPIC + vec3 accum[6] = vec3[](vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0)); + const vec3 accum_dirs[6] = vec3[](vec3(1.0, 0.0, 0.0), vec3(-1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0), vec3(0.0, -1.0, 0.0), vec3(0.0, 0.0, 1.0), vec3(0.0, 0.0, -1.0)); +#else + vec3 accum = vec3(0.0); +#endif + + for (uint i = 0; i < params.light_count; i++) { + + float attenuation; + vec3 light_pos; + + if (!compute_light_vector(i, cell_index, pos, attenuation, light_pos)) { + continue; + } + + vec3 light_dir = pos - light_pos; + float distance = length(light_dir); + light_dir = normalize(light_dir); + + if (length(normal.xyz) > 0.2 && dot(normal.xyz, light_dir) >= 0) { + continue; //not facing the light + } + + if (lights.data[i].has_shadow) { + + float distance_adv = get_normal_advance(light_dir); + + distance += distance_adv - mod(distance, distance_adv); //make it reach the center of the box always + + vec3 from = pos - light_dir * distance; //approximate + from -= sign(light_dir) * 0.45; //go near the edge towards the light direction to avoid self occlusion + + uint result = raymarch(distance, distance_adv, from, light_dir); + + if (result != cell_index) { + continue; //was occluded + } + } + + vec3 light = lights.data[i].color * albedo.rgb * attenuation * lights.data[i].energy; + +#ifdef MODE_ANISOTROPIC + for (uint j = 0; j < 6; j++) { + accum[j] += max(0.0, dot(accum_dir, -light_dir)) * light + emission; + } +#else + if (length(normal.xyz) > 0.2) { + accum += max(0.0, dot(normal.xyz, -light_dir)) * light + emission; + } else { + //all directions + accum += light + emission; + } +#endif + } + +#ifdef MODE_ANISOTROPIC + + output.data[cell_index * 6 + 0] = vec4(accum[0], 0.0); + output.data[cell_index * 6 + 1] = vec4(accum[1], 0.0); + output.data[cell_index * 6 + 2] = vec4(accum[2], 0.0); + output.data[cell_index * 6 + 3] = vec4(accum[3], 0.0); + output.data[cell_index * 6 + 4] = vec4(accum[4], 0.0); + output.data[cell_index * 6 + 5] = vec4(accum[5], 0.0); +#else + output.data[cell_index] = vec4(accum, 0.0); + +#endif + +#endif //MODE_COMPUTE_LIGHT + +#ifdef MODE_UPDATE_MIPMAPS + + { +#ifdef MODE_ANISOTROPIC + vec3 light_accum[6] = vec3[](vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0)); +#else + vec3 light_accum = vec3(0.0); +#endif + float count = 0.0; + for (uint i = 0; i < 8; i++) { + uint child_index = cell_children.data[cell_index].children[i]; + if (child_index == NO_CHILDREN) { + continue; + } +#ifdef MODE_ANISOTROPIC + light_accum[1] += output.data[child_index * 6 + 0].rgb; + light_accum[2] += output.data[child_index * 6 + 1].rgb; + light_accum[3] += output.data[child_index * 6 + 2].rgb; + light_accum[4] += output.data[child_index * 6 + 3].rgb; + light_accum[5] += output.data[child_index * 6 + 4].rgb; + light_accum[6] += output.data[child_index * 6 + 5].rgb; + +#else + light_accum += output.data[child_index].rgb; + +#endif + + count += 1.0; + } + + float divisor = mix(8.0, count, params.propagation); +#ifdef MODE_ANISOTROPIC + output.data[cell_index * 6 + 0] = vec4(light_accum[0] / divisor, 0.0); + output.data[cell_index * 6 + 1] = vec4(light_accum[1] / divisor, 0.0); + output.data[cell_index * 6 + 2] = vec4(light_accum[2] / divisor, 0.0); + output.data[cell_index * 6 + 3] = vec4(light_accum[3] / divisor, 0.0); + output.data[cell_index * 6 + 4] = vec4(light_accum[4] / divisor, 0.0); + output.data[cell_index * 6 + 5] = vec4(light_accum[5] / divisor, 0.0); + +#else + output.data[cell_index] = vec4(light_accum / divisor, 0.0); +#endif + } +#endif + +#ifdef MODE_WRITE_TEXTURE + { + } +#endif +} diff --git a/servers/rendering/rasterizer_rd/shaders/luminance_reduce.glsl b/servers/rendering/rasterizer_rd/shaders/luminance_reduce.glsl new file mode 100644 index 0000000000..4bf5b7e7f1 --- /dev/null +++ b/servers/rendering/rasterizer_rd/shaders/luminance_reduce.glsl @@ -0,0 +1,87 @@ +/* clang-format off */ +[compute] + +#version 450 + +VERSION_DEFINES + +#define BLOCK_SIZE 8 + +layout(local_size_x = BLOCK_SIZE, local_size_y = BLOCK_SIZE, local_size_z = 1) in; +/* clang-format on */ + +shared float tmp_data[BLOCK_SIZE * BLOCK_SIZE]; + +#ifdef READ_TEXTURE + +//use for main texture +layout(set = 0, binding = 0) uniform sampler2D source_texture; + +#else + +//use for intermediate textures +layout(r32f, set = 0, binding = 0) uniform restrict readonly image2D source_luminance; + +#endif + +layout(r32f, set = 1, binding = 0) uniform restrict writeonly image2D dest_luminance; + +#ifdef WRITE_LUMINANCE +layout(set = 2, binding = 0) uniform sampler2D prev_luminance; +#endif + +layout(push_constant, binding = 1, std430) uniform Params { + ivec2 source_size; + float max_luminance; + float min_luminance; + float exposure_adjust; + float pad[3]; +} +params; + +void main() { + + uint t = gl_LocalInvocationID.y * BLOCK_SIZE + gl_LocalInvocationID.x; + ivec2 pos = ivec2(gl_GlobalInvocationID.xy); + + if (any(lessThan(pos, params.source_size))) { + +#ifdef READ_TEXTURE + vec3 v = texelFetch(source_texture, pos, 0).rgb; + tmp_data[t] = max(v.r, max(v.g, v.b)); +#else + tmp_data[t] = imageLoad(source_luminance, pos).r; +#endif + } else { + tmp_data[t] = 0.0; + } + + groupMemoryBarrier(); + barrier(); + + uint size = (BLOCK_SIZE * BLOCK_SIZE) >> 1; + + do { + if (t < size) { + tmp_data[t] += tmp_data[t + size]; + } + groupMemoryBarrier(); + barrier(); + + size >>= 1; + + } while (size >= 1); + + if (t == 0) { + //compute rect size + ivec2 rect_size = min(params.source_size - pos, ivec2(BLOCK_SIZE)); + float avg = tmp_data[0] / float(rect_size.x * rect_size.y); + //float avg = tmp_data[0] / float(BLOCK_SIZE*BLOCK_SIZE); + pos /= ivec2(BLOCK_SIZE); +#ifdef WRITE_LUMINANCE + float prev_lum = texelFetch(prev_luminance, ivec2(0, 0), 0).r; //1 pixel previous exposure + avg = clamp(prev_lum + (avg - prev_lum) * params.exposure_adjust, params.min_luminance, params.max_luminance); +#endif + imageStore(dest_luminance, pos, vec4(avg)); + } +} diff --git a/servers/rendering/rasterizer_rd/shaders/roughness_limiter.glsl b/servers/rendering/rasterizer_rd/shaders/roughness_limiter.glsl new file mode 100644 index 0000000000..3637b1abb2 --- /dev/null +++ b/servers/rendering/rasterizer_rd/shaders/roughness_limiter.glsl @@ -0,0 +1,73 @@ +/* clang-format off */ +[compute] + +#version 450 + +VERSION_DEFINES + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; +/* clang-format on */ + +layout(set = 0, binding = 0) uniform sampler2D source_normal; +layout(r8, set = 1, binding = 0) uniform restrict writeonly image2D dest_roughness; + +layout(push_constant, binding = 1, std430) uniform Params { + ivec2 screen_size; + float curve; + uint pad; +} +params; + +#define HALF_PI 1.5707963267948966 + +void main() { + + // Pixel being shaded + ivec2 pos = ivec2(gl_GlobalInvocationID.xy); + if (any(greaterThan(pos, params.screen_size))) { //too large, do nothing + return; + } + + vec3 normal_accum = vec3(0.0); + float accum = 0.0; + for (int i = 0; i <= 1; i++) { + for (int j = 0; j <= 1; j++) { + normal_accum += normalize(texelFetch(source_normal, pos + ivec2(i, j), 0).xyz * 2.0 - 1.0); + accum += 1.0; + } + } + + normal_accum /= accum; + + float r = length(normal_accum); + + float limit; + + if (r < 1.0) { + float threshold = 0.4; + + /* + //Formula from Filament, does not make sense to me. + + float r2 = r * r; + float kappa = (3.0f * r - r * r2) / (1.0f - r2); + float variance = 0.25f / kappa; + limit = sqrt(min(2.0f * variance, threshold * threshold)); +//*/ + /* + //Formula based on probability distribution graph + + float width = acos(max(0.0,r)); // convert to angle (width) + float roughness = pow(width,1.7)*0.854492; //approximate (crappy) formula to convert to roughness + limit = min(sqrt(roughness), threshold); //convert to perceptual roughness and apply threshold +//*/ + + limit = min(sqrt(pow(acos(max(0.0, r)) / HALF_PI, params.curve)), threshold); //convert to perceptual roughness and apply threshold + + //limit = 0.5; + } else { + limit = 0.0; + } + + imageStore(dest_roughness, pos, vec4(limit)); +} diff --git a/servers/rendering/rasterizer_rd/shaders/scene_high_end.glsl b/servers/rendering/rasterizer_rd/shaders/scene_high_end.glsl new file mode 100644 index 0000000000..07f4770b14 --- /dev/null +++ b/servers/rendering/rasterizer_rd/shaders/scene_high_end.glsl @@ -0,0 +1,1718 @@ +/* clang-format off */ +[vertex] + +#version 450 + +VERSION_DEFINES + +#include "scene_high_end_inc.glsl" + +/* INPUT ATTRIBS */ + +layout(location = 0) in vec3 vertex_attrib; +/* clang-format on */ +layout(location = 1) in vec3 normal_attrib; +#if defined(TANGENT_USED) || defined(NORMALMAP_USED) || defined(LIGHT_ANISOTROPY_USED) +layout(location = 2) in vec4 tangent_attrib; +#endif + +#if defined(COLOR_USED) +layout(location = 3) in vec4 color_attrib; +#endif + +#if defined(UV_USED) +layout(location = 4) in vec2 uv_attrib; +#endif + +#if defined(UV2_USED) || defined(USE_LIGHTMAP) +layout(location = 5) in vec2 uv2_attrib; +#endif + +layout(location = 6) in uvec4 bone_attrib; // always bound, even if unused + +/* Varyings */ + +layout(location = 0) out vec3 vertex_interp; +layout(location = 1) out vec3 normal_interp; + +#if defined(COLOR_USED) +layout(location = 2) out vec4 color_interp; +#endif + +#if defined(UV_USED) +layout(location = 3) out vec2 uv_interp; +#endif + +#if defined(UV2_USED) || defined(USE_LIGHTMAP) +layout(location = 4) out vec2 uv2_interp; +#endif + +#if defined(TANGENT_USED) || defined(NORMALMAP_USED) || defined(LIGHT_ANISOTROPY_USED) +layout(location = 5) out vec3 tangent_interp; +layout(location = 6) out vec3 binormal_interp; +#endif + +#ifdef USE_MATERIAL_UNIFORMS +layout(set = 5, binding = 0, std140) uniform MaterialUniforms{ + /* clang-format off */ +MATERIAL_UNIFORMS + /* clang-format on */ +} material; +#endif + +/* clang-format off */ + +VERTEX_SHADER_GLOBALS + +/* clang-format on */ + +// FIXME: This triggers a Mesa bug that breaks rendering, so disabled for now. +// See GH-13450 and https://bugs.freedesktop.org/show_bug.cgi?id=100316 +invariant gl_Position; + +layout(location = 7) flat out uint instance_index; + +#ifdef MODE_DUAL_PARABOLOID + +layout(location = 8) out float dp_clip; + +#endif + +void main() { + + instance_index = draw_call.instance_index; + vec4 instance_custom = vec4(0.0); +#if defined(COLOR_USED) + color_interp = color_attrib; +#endif + + mat4 world_matrix = instances.data[instance_index].transform; + mat3 world_normal_matrix = mat3(instances.data[instance_index].normal_transform); + + if (bool(instances.data[instance_index].flags & INSTANCE_FLAGS_MULTIMESH)) { + //multimesh, instances are for it + uint offset = (instances.data[instance_index].flags >> INSTANCE_FLAGS_MULTIMESH_STRIDE_SHIFT) & INSTANCE_FLAGS_MULTIMESH_STRIDE_MASK; + offset *= gl_InstanceIndex; + + mat4 matrix; + if (bool(instances.data[instance_index].flags & INSTANCE_FLAGS_MULTIMESH_FORMAT_2D)) { + matrix = mat4(transforms.data[offset + 0], transforms.data[offset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0)); + offset += 2; + } else { + matrix = mat4(transforms.data[offset + 0], transforms.data[offset + 1], transforms.data[offset + 2], vec4(0.0, 0.0, 0.0, 1.0)); + offset += 3; + } + + if (bool(instances.data[instance_index].flags & INSTANCE_FLAGS_MULTIMESH_HAS_COLOR)) { +#ifdef COLOR_USED + color_interp *= transforms.data[offset]; +#endif + offset += 1; + } + + if (bool(instances.data[instance_index].flags & INSTANCE_FLAGS_MULTIMESH_HAS_CUSTOM_DATA)) { + instance_custom = transforms.data[offset]; + } + + //transpose + matrix = transpose(matrix); + world_matrix = world_matrix * matrix; + world_normal_matrix = world_normal_matrix * mat3(matrix); + + } else { + //not a multimesh, instances are for multiple draw calls + instance_index += gl_InstanceIndex; + } + + vec3 vertex = vertex_attrib; + vec3 normal = normal_attrib; + +#if defined(TANGENT_USED) || defined(NORMALMAP_USED) || defined(LIGHT_ANISOTROPY_USED) + vec3 tangent = tangent_attrib.xyz; + float binormalf = tangent_attrib.a; + vec3 binormal = normalize(cross(normal, tangent) * binormalf); +#endif + + if (bool(instances.data[instance_index].flags & INSTANCE_FLAGS_SKELETON)) { + //multimesh, instances are for it + + uvec2 bones_01 = uvec2(bone_attrib.x & 0xFFFF, bone_attrib.x >> 16) * 3; + uvec2 bones_23 = uvec2(bone_attrib.y & 0xFFFF, bone_attrib.y >> 16) * 3; + vec2 weights_01 = unpackUnorm2x16(bone_attrib.z); + vec2 weights_23 = unpackUnorm2x16(bone_attrib.w); + + mat4 m = mat4(transforms.data[bones_01.x], transforms.data[bones_01.x + 1], transforms.data[bones_01.x + 2], vec4(0.0, 0.0, 0.0, 1.0)) * weights_01.x; + m += mat4(transforms.data[bones_01.y], transforms.data[bones_01.y + 1], transforms.data[bones_01.y + 2], vec4(0.0, 0.0, 0.0, 1.0)) * weights_01.y; + m += mat4(transforms.data[bones_23.x], transforms.data[bones_23.x + 1], transforms.data[bones_23.x + 2], vec4(0.0, 0.0, 0.0, 1.0)) * weights_23.x; + m += mat4(transforms.data[bones_23.y], transforms.data[bones_23.y + 1], transforms.data[bones_23.y + 2], vec4(0.0, 0.0, 0.0, 1.0)) * weights_23.y; + + //reverse order because its transposed + vertex = (vec4(vertex, 1.0) * m).xyz; + normal = (vec4(normal, 0.0) * m).xyz; + +#if defined(TANGENT_USED) || defined(NORMALMAP_USED) || defined(LIGHT_ANISOTROPY_USED) + + tangent = (vec4(tangent, 0.0) * m).xyz; + binormal = (vec4(binormal, 0.0) * m).xyz; +#endif + } + +#if defined(UV_USED) + uv_interp = uv_attrib; +#endif + +#if defined(UV2_USED) || defined(USE_LIGHTMAP) + uv2_interp = uv2_attrib; +#endif + +#ifdef USE_OVERRIDE_POSITION + vec4 position; +#endif + + mat4 projection_matrix = scene_data.projection_matrix; + +//using world coordinates +#if !defined(SKIP_TRANSFORM_USED) && defined(VERTEX_WORLD_COORDS_USED) + + vertex = (world_matrix * vec4(vertex, 1.0)).xyz; + + normal = world_normal_matrix * normal; + +#if defined(TANGENT_USED) || defined(NORMALMAP_USED) || defined(LIGHT_ANISOTROPY_USED) + + tangent = world_normal_matrix * tangent; + binormal = world_normal_matrix * binormal; + +#endif +#endif + + float roughness = 1.0; + + mat4 modelview = scene_data.inv_camera_matrix * world_matrix; + mat3 modelview_normal = mat3(scene_data.inv_camera_matrix) * world_normal_matrix; + + { + /* clang-format off */ + +VERTEX_SHADER_CODE + + /* clang-format on */ + } + +// using local coordinates (default) +#if !defined(SKIP_TRANSFORM_USED) && !defined(VERTEX_WORLD_COORDS_USED) + + vertex = (modelview * vec4(vertex, 1.0)).xyz; + normal = modelview_normal * normal; +#endif + +#if defined(TANGENT_USED) || defined(NORMALMAP_USED) || defined(LIGHT_ANISOTROPY_USED) + + binormal = modelview_normal * binormal; + tangent = modelview_normal * tangent; +#endif + +//using world coordinates +#if !defined(SKIP_TRANSFORM_USED) && defined(VERTEX_WORLD_COORDS_USED) + + vertex = (scene_data.inv_camera_matrix * vec4(vertex, 1.0)).xyz; + normal = mat3(scene_data.inverse_normal_matrix) * normal; + +#if defined(TANGENT_USED) || defined(NORMALMAP_USED) || defined(LIGHT_ANISOTROPY_USED) + + binormal = mat3(scene_data.camera_inverse_binormal_matrix) * binormal; + tangent = mat3(scene_data.camera_inverse_tangent_matrix) * tangent; +#endif +#endif + + vertex_interp = vertex; + normal_interp = normal; + +#if defined(TANGENT_USED) || defined(NORMALMAP_USED) || defined(LIGHT_ANISOTROPY_USED) + tangent_interp = tangent; + binormal_interp = binormal; +#endif + +#ifdef MODE_RENDER_DEPTH + +#ifdef MODE_DUAL_PARABOLOID + + vertex_interp.z *= scene_data.dual_paraboloid_side; + normal_interp.z *= scene_data.dual_paraboloid_side; + + dp_clip = vertex_interp.z; //this attempts to avoid noise caused by objects sent to the other parabolloid side due to bias + + //for dual paraboloid shadow mapping, this is the fastest but least correct way, as it curves straight edges + + vec3 vtx = vertex_interp + normalize(vertex_interp) * scene_data.z_offset; + float distance = length(vtx); + vtx = normalize(vtx); + vtx.xy /= 1.0 - vtx.z; + vtx.z = (distance / scene_data.z_far); + vtx.z = vtx.z * 2.0 - 1.0; + + vertex_interp = vtx; +#else + + float z_ofs = scene_data.z_offset; + z_ofs += max(0.0, 1.0 - abs(normalize(normal_interp).z)) * scene_data.z_slope_scale; + vertex_interp.z -= z_ofs; + +#endif + +#endif //MODE_RENDER_DEPTH + +#ifdef USE_OVERRIDE_POSITION + gl_Position = position; +#else + gl_Position = projection_matrix * vec4(vertex_interp, 1.0); +#endif +} + +/* clang-format off */ +[fragment] + +#version 450 + +VERSION_DEFINES + +#include "scene_high_end_inc.glsl" + +/* Varyings */ + +layout(location = 0) in vec3 vertex_interp; +/* clang-format on */ +layout(location = 1) in vec3 normal_interp; + +#if defined(COLOR_USED) +layout(location = 2) in vec4 color_interp; +#endif + +#if defined(UV_USED) +layout(location = 3) in vec2 uv_interp; +#endif + +#if defined(UV2_USED) || defined(USE_LIGHTMAP) +layout(location = 4) in vec2 uv2_interp; +#endif + +#if defined(TANGENT_USED) || defined(NORMALMAP_USED) || defined(LIGHT_ANISOTROPY_USED) +layout(location = 5) in vec3 tangent_interp; +layout(location = 6) in vec3 binormal_interp; +#endif + +layout(location = 7) flat in uint instance_index; + +#ifdef MODE_DUAL_PARABOLOID + +layout(location = 8) in float dp_clip; + +#endif + +//defines to keep compatibility with vertex + +#define world_matrix instances.data[instance_index].transform +#define world_normal_matrix instances.data[instance_index].normal_transform +#define projection_matrix scene_data.projection_matrix + +#ifdef USE_MATERIAL_UNIFORMS +layout(set = 5, binding = 0, std140) uniform MaterialUniforms{ + /* clang-format off */ +MATERIAL_UNIFORMS + /* clang-format on */ +} material; +#endif + +/* clang-format off */ + +FRAGMENT_SHADER_GLOBALS + +/* clang-format on */ + +#ifdef MODE_RENDER_DEPTH + +#ifdef MODE_RENDER_MATERIAL + +layout(location = 0) out vec4 albedo_output_buffer; +layout(location = 1) out vec4 normal_output_buffer; +layout(location = 2) out vec4 orm_output_buffer; +layout(location = 3) out vec4 emission_output_buffer; +layout(location = 4) out float depth_output_buffer; + +#endif + +#ifdef MODE_RENDER_NORMAL +layout(location = 0) out vec4 normal_output_buffer; +#ifdef MODE_RENDER_ROUGHNESS +layout(location = 1) out float roughness_output_buffer; +#endif //MODE_RENDER_ROUGHNESS +#endif //MODE_RENDER_NORMAL +#else // RENDER DEPTH + +#ifdef MODE_MULTIPLE_RENDER_TARGETS + +layout(location = 0) out vec4 diffuse_buffer; //diffuse (rgb) and roughness +layout(location = 1) out vec4 specular_buffer; //specular and SSS (subsurface scatter) +#else + +layout(location = 0) out vec4 frag_color; +#endif + +#endif // RENDER DEPTH + +// This returns the G_GGX function divided by 2 cos_theta_m, where in practice cos_theta_m is either N.L or N.V. +// We're dividing this factor off because the overall term we'll end up looks like +// (see, for example, the first unnumbered equation in B. Burley, "Physically Based Shading at Disney", SIGGRAPH 2012): +// +// F(L.V) D(N.H) G(N.L) G(N.V) / (4 N.L N.V) +// +// We're basically regouping this as +// +// F(L.V) D(N.H) [G(N.L)/(2 N.L)] [G(N.V) / (2 N.V)] +// +// and thus, this function implements the [G(N.m)/(2 N.m)] part with m = L or V. +// +// The contents of the D and G (G1) functions (GGX) are taken from +// E. Heitz, "Understanding the Masking-Shadowing Function in Microfacet-Based BRDFs", J. Comp. Graph. Tech. 3 (2) (2014). +// Eqns 71-72 and 85-86 (see also Eqns 43 and 80). + +#if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) + +float G_GGX_2cos(float cos_theta_m, float alpha) { + // Schlick's approximation + // C. Schlick, "An Inexpensive BRDF Model for Physically-based Rendering", Computer Graphics Forum. 13 (3): 233 (1994) + // Eq. (19), although see Heitz (2014) the about the problems with his derivation. + // It nevertheless approximates GGX well with k = alpha/2. + float k = 0.5 * alpha; + return 0.5 / (cos_theta_m * (1.0 - k) + k); + + // float cos2 = cos_theta_m * cos_theta_m; + // float sin2 = (1.0 - cos2); + // return 1.0 / (cos_theta_m + sqrt(cos2 + alpha * alpha * sin2)); +} + +float D_GGX(float cos_theta_m, float alpha) { + float alpha2 = alpha * alpha; + float d = 1.0 + (alpha2 - 1.0) * cos_theta_m * cos_theta_m; + return alpha2 / (M_PI * d * d); +} + +float G_GGX_anisotropic_2cos(float cos_theta_m, float alpha_x, float alpha_y, float cos_phi, float sin_phi) { + float cos2 = cos_theta_m * cos_theta_m; + float sin2 = (1.0 - cos2); + float s_x = alpha_x * cos_phi; + float s_y = alpha_y * sin_phi; + return 1.0 / max(cos_theta_m + sqrt(cos2 + (s_x * s_x + s_y * s_y) * sin2), 0.001); +} + +float D_GGX_anisotropic(float cos_theta_m, float alpha_x, float alpha_y, float cos_phi, float sin_phi) { + float cos2 = cos_theta_m * cos_theta_m; + float sin2 = (1.0 - cos2); + float r_x = cos_phi / alpha_x; + float r_y = sin_phi / alpha_y; + float d = cos2 + sin2 * (r_x * r_x + r_y * r_y); + return 1.0 / max(M_PI * alpha_x * alpha_y * d * d, 0.001); +} + +float SchlickFresnel(float u) { + float m = 1.0 - u; + float m2 = m * m; + return m2 * m2 * m; // pow(m,5) +} + +float GTR1(float NdotH, float a) { + if (a >= 1.0) return 1.0 / M_PI; + float a2 = a * a; + float t = 1.0 + (a2 - 1.0) * NdotH * NdotH; + return (a2 - 1.0) / (M_PI * log(a2) * t); +} + +vec3 F0(float metallic, float specular, vec3 albedo) { + float dielectric = 0.16 * specular * specular; + // use albedo * metallic as colored specular reflectance at 0 angle for metallic materials; + // see https://google.github.io/filament/Filament.md.html + return mix(vec3(dielectric), albedo, vec3(metallic)); +} + +void light_compute(vec3 N, vec3 L, vec3 V, vec3 light_color, vec3 attenuation, vec3 diffuse_color, float roughness, float metallic, float specular, float specular_blob_intensity, +#ifdef LIGHT_TRANSMISSION_USED + vec3 transmission, +#endif +#ifdef LIGHT_RIM_USED + float rim, float rim_tint, +#endif +#ifdef LIGHT_CLEARCOAT_USED + float clearcoat, float clearcoat_gloss, +#endif +#ifdef LIGHT_ANISOTROPY_USED + vec3 B, vec3 T, float anisotropy, +#endif +#ifdef USE_SHADOW_TO_OPACITY + inout float alpha, +#endif + inout vec3 diffuse_light, inout vec3 specular_light) { + +#if defined(USE_LIGHT_SHADER_CODE) + // light is written by the light shader + + vec3 normal = N; + vec3 albedo = diffuse_color; + vec3 light = L; + vec3 view = V; + + /* clang-format off */ + +LIGHT_SHADER_CODE + + /* clang-format on */ + +#else + float NdotL = dot(N, L); + float cNdotL = max(NdotL, 0.0); // clamped NdotL + float NdotV = dot(N, V); + float cNdotV = max(NdotV, 0.0); + +#if defined(DIFFUSE_BURLEY) || defined(SPECULAR_BLINN) || defined(SPECULAR_SCHLICK_GGX) || defined(LIGHT_CLEARCOAT_USED) + vec3 H = normalize(V + L); +#endif + +#if defined(SPECULAR_BLINN) || defined(SPECULAR_SCHLICK_GGX) || defined(LIGHT_CLEARCOAT_USED) + float cNdotH = max(dot(N, H), 0.0); +#endif + +#if defined(DIFFUSE_BURLEY) || defined(SPECULAR_SCHLICK_GGX) || defined(LIGHT_CLEARCOAT_USED) + float cLdotH = max(dot(L, H), 0.0); +#endif + + if (metallic < 1.0) { +#if defined(DIFFUSE_OREN_NAYAR) + vec3 diffuse_brdf_NL; +#else + float diffuse_brdf_NL; // BRDF times N.L for calculating diffuse radiance +#endif + +#if defined(DIFFUSE_LAMBERT_WRAP) + // energy conserving lambert wrap shader + diffuse_brdf_NL = max(0.0, (NdotL + roughness) / ((1.0 + roughness) * (1.0 + roughness))); + +#elif defined(DIFFUSE_OREN_NAYAR) + + { + // see http://mimosa-pudica.net/improved-oren-nayar.html + float LdotV = dot(L, V); + + float s = LdotV - NdotL * NdotV; + float t = mix(1.0, max(NdotL, NdotV), step(0.0, s)); + + float sigma2 = roughness * roughness; // TODO: this needs checking + vec3 A = 1.0 + sigma2 * (-0.5 / (sigma2 + 0.33) + 0.17 * diffuse_color / (sigma2 + 0.13)); + float B = 0.45 * sigma2 / (sigma2 + 0.09); + + diffuse_brdf_NL = cNdotL * (A + vec3(B) * s / t) * (1.0 / M_PI); + } + +#elif defined(DIFFUSE_TOON) + + diffuse_brdf_NL = smoothstep(-roughness, max(roughness, 0.01), NdotL); + +#elif defined(DIFFUSE_BURLEY) + + { + float FD90_minus_1 = 2.0 * cLdotH * cLdotH * roughness - 0.5; + float FdV = 1.0 + FD90_minus_1 * SchlickFresnel(cNdotV); + float FdL = 1.0 + FD90_minus_1 * SchlickFresnel(cNdotL); + diffuse_brdf_NL = (1.0 / M_PI) * FdV * FdL * cNdotL; + /* + float energyBias = mix(roughness, 0.0, 0.5); + float energyFactor = mix(roughness, 1.0, 1.0 / 1.51); + float fd90 = energyBias + 2.0 * VoH * VoH * roughness; + float f0 = 1.0; + float lightScatter = f0 + (fd90 - f0) * pow(1.0 - cNdotL, 5.0); + float viewScatter = f0 + (fd90 - f0) * pow(1.0 - cNdotV, 5.0); + + diffuse_brdf_NL = lightScatter * viewScatter * energyFactor; + */ + } +#else + // lambert + diffuse_brdf_NL = cNdotL * (1.0 / M_PI); +#endif + + diffuse_light += light_color * diffuse_color * diffuse_brdf_NL * attenuation; + +#if defined(LIGHT_TRANSMISSION_USED) + diffuse_light += light_color * diffuse_color * (vec3(1.0 / M_PI) - diffuse_brdf_NL) * transmission * attenuation; +#endif + +#if defined(LIGHT_RIM_USED) + float rim_light = pow(max(0.0, 1.0 - cNdotV), max(0.0, (1.0 - roughness) * 16.0)); + diffuse_light += rim_light * rim * mix(vec3(1.0), diffuse_color, rim_tint) * light_color; +#endif + } + + if (roughness > 0.0) { // FIXME: roughness == 0 should not disable specular light entirely + + // D + +#if defined(SPECULAR_BLINN) + + //normalized blinn + float shininess = exp2(15.0 * (1.0 - roughness) + 1.0) * 0.25; + float blinn = pow(cNdotH, shininess) * cNdotL; + blinn *= (shininess + 8.0) * (1.0 / (8.0 * M_PI)); + float intensity = blinn; + + specular_light += light_color * intensity * specular_blob_intensity * attenuation; + +#elif defined(SPECULAR_PHONG) + + vec3 R = normalize(-reflect(L, N)); + float cRdotV = max(0.0, dot(R, V)); + float shininess = exp2(15.0 * (1.0 - roughness) + 1.0) * 0.25; + float phong = pow(cRdotV, shininess); + phong *= (shininess + 8.0) * (1.0 / (8.0 * M_PI)); + float intensity = (phong) / max(4.0 * cNdotV * cNdotL, 0.75); + + specular_light += light_color * intensity * specular_blob_intensity * attenuation; + +#elif defined(SPECULAR_TOON) + + vec3 R = normalize(-reflect(L, N)); + float RdotV = dot(R, V); + float mid = 1.0 - roughness; + mid *= mid; + float intensity = smoothstep(mid - roughness * 0.5, mid + roughness * 0.5, RdotV) * mid; + diffuse_light += light_color * intensity * specular_blob_intensity * attenuation; // write to diffuse_light, as in toon shading you generally want no reflection + +#elif defined(SPECULAR_DISABLED) + // none.. + +#elif defined(SPECULAR_SCHLICK_GGX) + // shlick+ggx as default + +#if defined(LIGHT_ANISOTROPY_USED) + + float alpha_ggx = roughness * roughness; + float aspect = sqrt(1.0 - anisotropy * 0.9); + float ax = alpha_ggx / aspect; + float ay = alpha_ggx * aspect; + float XdotH = dot(T, H); + float YdotH = dot(B, H); + float D = D_GGX_anisotropic(cNdotH, ax, ay, XdotH, YdotH); + float G = G_GGX_anisotropic_2cos(cNdotL, ax, ay, XdotH, YdotH) * G_GGX_anisotropic_2cos(cNdotV, ax, ay, XdotH, YdotH); + +#else + float alpha_ggx = roughness * roughness; + float D = D_GGX(cNdotH, alpha_ggx); + float G = G_GGX_2cos(cNdotL, alpha_ggx) * G_GGX_2cos(cNdotV, alpha_ggx); +#endif + // F + vec3 f0 = F0(metallic, specular, diffuse_color); + float cLdotH5 = SchlickFresnel(cLdotH); + vec3 F = mix(vec3(cLdotH5), vec3(1.0), f0); + + vec3 specular_brdf_NL = cNdotL * D * F * G; + + specular_light += specular_brdf_NL * light_color * specular_blob_intensity * attenuation; +#endif + +#if defined(LIGHT_CLEARCOAT_USED) + +#if !defined(SPECULAR_SCHLICK_GGX) + float cLdotH5 = SchlickFresnel(cLdotH); +#endif + float Dr = GTR1(cNdotH, mix(.1, .001, clearcoat_gloss)); + float Fr = mix(.04, 1.0, cLdotH5); + float Gr = G_GGX_2cos(cNdotL, .25) * G_GGX_2cos(cNdotV, .25); + + float clearcoat_specular_brdf_NL = 0.25 * clearcoat * Gr * Fr * Dr * cNdotL; + + specular_light += clearcoat_specular_brdf_NL * light_color * specular_blob_intensity * attenuation; +#endif + } + +#ifdef USE_SHADOW_TO_OPACITY + alpha = min(alpha, clamp(1.0 - length(attenuation), 0.0, 1.0)); +#endif + +#endif //defined(USE_LIGHT_SHADER_CODE) +} + +#ifndef USE_NO_SHADOWS + +float sample_shadow(texture2D shadow, vec2 shadow_pixel_size, vec4 coord) { + + //todo optimize + vec2 pos = coord.xy; + float depth = coord.z; + +#ifdef SHADOW_MODE_PCF_13 + + float avg = textureProj(shadow, vec4(pos, depth, 1.0)); + avg += textureProj(sampler2DShadow(shadow, shadow_sampler), vec4(pos + vec2(shadow_pixel_size.x, 0.0), depth, 1.0)); + avg += textureProj(sampler2DShadow(shadow, shadow_sampler), vec4(pos + vec2(-shadow_pixel_size.x, 0.0), depth, 1.0)); + avg += textureProj(sampler2DShadow(shadow, shadow_sampler), vec4(pos + vec2(0.0, shadow_pixel_size.y), depth, 1.0)); + avg += textureProj(sampler2DShadow(shadow, shadow_sampler), vec4(pos + vec2(0.0, -shadow_pixel_size.y), depth, 1.0)); + avg += textureProj(sampler2DShadow(shadow, shadow_sampler), vec4(pos + vec2(shadow_pixel_size.x, shadow_pixel_size.y), depth, 1.0)); + avg += textureProj(sampler2DShadow(shadow, shadow_sampler), vec4(pos + vec2(-shadow_pixel_size.x, shadow_pixel_size.y), depth, 1.0)); + avg += textureProj(sampler2DShadow(shadow, shadow_sampler), vec4(pos + vec2(shadow_pixel_size.x, -shadow_pixel_size.y), depth, 1.0)); + avg += textureProj(sampler2DShadow(shadow, shadow_sampler), vec4(pos + vec2(-shadow_pixel_size.x, -shadow_pixel_size.y), depth, 1.0)); + avg += textureProj(sampler2DShadow(shadow, shadow_sampler), vec4(pos + vec2(shadow_pixel_size.x * 2.0, 0.0), depth, 1.0)); + avg += textureProj(sampler2DShadow(shadow, shadow_sampler), vec4(pos + vec2(-shadow_pixel_size.x * 2.0, 0.0), depth, 1.0)); + avg += textureProj(sampler2DShadow(shadow, shadow_sampler), vec4(pos + vec2(0.0, shadow_pixel_size.y * 2.0), depth, 1.0)); + avg += textureProj(sampler2DShadow(shadow, shadow_sampler), vec4(pos + vec2(0.0, -shadow_pixel_size.y * 2.0), depth, 1.0)); + return avg * (1.0 / 13.0); +#endif + +#ifdef SHADOW_MODE_PCF_5 + + float avg = textureProj(sampler2DShadow(shadow, shadow_sampler), vec4(pos, depth, 1.0)); + avg += textureProj(sampler2DShadow(shadow, shadow_sampler), vec4(pos + vec2(shadow_pixel_size.x, 0.0), depth, 1.0)); + avg += textureProj(sampler2DShadow(shadow, shadow_sampler), vec4(pos + vec2(-shadow_pixel_size.x, 0.0), depth, 1.0)); + avg += textureProj(sampler2DShadow(shadow, shadow_sampler), vec4(pos + vec2(0.0, shadow_pixel_size.y), depth, 1.0)); + avg += textureProj(sampler2DShadow(shadow, shadow_sampler), vec4(pos + vec2(0.0, -shadow_pixel_size.y), depth, 1.0)); + return avg * (1.0 / 5.0); + +#endif + +#if !defined(SHADOW_MODE_PCF_5) || !defined(SHADOW_MODE_PCF_13) + + return textureProj(sampler2DShadow(shadow, shadow_sampler), vec4(pos, depth, 1.0)); + +#endif +} + +#endif //USE_NO_SHADOWS + +void light_process_omni(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 albedo, float roughness, float metallic, float specular, float p_blob_intensity, +#ifdef LIGHT_TRANSMISSION_USED + vec3 transmission, +#endif +#ifdef LIGHT_RIM_USED + float rim, float rim_tint, +#endif +#ifdef LIGHT_CLEARCOAT_USED + float clearcoat, float clearcoat_gloss, +#endif +#ifdef LIGHT_ANISOTROPY_USED + vec3 binormal, vec3 tangent, float anisotropy, +#endif +#ifdef USE_SHADOW_TO_OPACITY + inout float alpha, +#endif + inout vec3 diffuse_light, inout vec3 specular_light) { + + vec3 light_rel_vec = lights.data[idx].position - vertex; + float light_length = length(light_rel_vec); + float normalized_distance = light_length * lights.data[idx].inv_radius; + vec2 attenuation_energy = unpackHalf2x16(lights.data[idx].attenuation_energy); + float omni_attenuation = pow(max(1.0 - normalized_distance, 0.0), attenuation_energy.x); + vec3 light_attenuation = vec3(omni_attenuation); + vec4 color_specular = unpackUnorm4x8(lights.data[idx].color_specular); + color_specular.rgb *= attenuation_energy.y; + +#ifndef USE_NO_SHADOWS + vec4 shadow_color_enabled = unpackUnorm4x8(lights.data[idx].shadow_color_enabled); + if (shadow_color_enabled.w > 0.5) { + // there is a shadowmap + + vec4 splane = (lights.data[idx].shadow_matrix * vec4(vertex, 1.0)); + float shadow_len = length(splane); + splane = normalize(splane); + vec4 clamp_rect = lights.data[idx].atlas_rect; + + if (splane.z >= 0.0) { + + splane.z += 1.0; + + clamp_rect.y += clamp_rect.w; + + } else { + + splane.z = 1.0 - splane.z; + } + + splane.xy /= splane.z; + splane.xy = splane.xy * 0.5 + 0.5; + splane.z = shadow_len * lights.data[idx].inv_radius; + splane.xy = clamp_rect.xy + splane.xy * clamp_rect.zw; + splane.w = 1.0; //needed? i think it should be 1 already + float shadow = sample_shadow(shadow_atlas, scene_data.shadow_atlas_pixel_size, splane); + + light_attenuation *= mix(shadow_color_enabled.rgb, vec3(1.0), shadow); + } +#endif //USE_NO_SHADOWS + + light_compute(normal, normalize(light_rel_vec), eye_vec, color_specular.rgb, light_attenuation, albedo, roughness, metallic, specular, color_specular.a * p_blob_intensity, +#ifdef LIGHT_TRANSMISSION_USED + transmission, +#endif +#ifdef LIGHT_RIM_USED + rim * omni_attenuation, rim_tint, +#endif +#ifdef LIGHT_CLEARCOAT_USED + clearcoat, clearcoat_gloss, +#endif +#ifdef LIGHT_ANISOTROPY_USED + binormal, tangent, anisotropy, +#endif +#ifdef USE_SHADOW_TO_OPACITY + alpha, +#endif + diffuse_light, + specular_light); +} + +void light_process_spot(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 albedo, float roughness, float metallic, float specular, float p_blob_intensity, +#ifdef LIGHT_TRANSMISSION_USED + vec3 transmission, +#endif +#ifdef LIGHT_RIM_USED + float rim, float rim_tint, +#endif +#ifdef LIGHT_CLEARCOAT_USED + float clearcoat, float clearcoat_gloss, +#endif +#ifdef LIGHT_ANISOTROPY_USED + vec3 binormal, vec3 tangent, float anisotropy, +#endif +#ifdef USE_SHADOW_TO_OPACITY + inout float alpha, +#endif + inout vec3 diffuse_light, + inout vec3 specular_light) { + + vec3 light_rel_vec = lights.data[idx].position - vertex; + float light_length = length(light_rel_vec); + float normalized_distance = light_length * lights.data[idx].inv_radius; + vec2 attenuation_energy = unpackHalf2x16(lights.data[idx].attenuation_energy); + float spot_attenuation = pow(max(1.0 - normalized_distance, 0.001), attenuation_energy.x); + vec3 spot_dir = lights.data[idx].direction; + vec2 spot_att_angle = unpackHalf2x16(lights.data[idx].cone_attenuation_angle); + float scos = max(dot(-normalize(light_rel_vec), spot_dir), spot_att_angle.y); + float spot_rim = max(0.0001, (1.0 - scos) / (1.0 - spot_att_angle.y)); + spot_attenuation *= 1.0 - pow(spot_rim, spot_att_angle.x); + vec3 light_attenuation = vec3(spot_attenuation); + vec4 color_specular = unpackUnorm4x8(lights.data[idx].color_specular); + color_specular.rgb *= attenuation_energy.y; + +/* + if (lights.data[idx].atlas_rect!=vec4(0.0)) { + //use projector texture + } + */ +#ifndef USE_NO_SHADOWS + vec4 shadow_color_enabled = unpackUnorm4x8(lights.data[idx].shadow_color_enabled); + if (shadow_color_enabled.w > 0.5) { + //there is a shadowmap + vec4 splane = (lights.data[idx].shadow_matrix * vec4(vertex, 1.0)); + splane /= splane.w; + float shadow = sample_shadow(shadow_atlas, scene_data.shadow_atlas_pixel_size, splane); + + light_attenuation *= mix(shadow_color_enabled.rgb, vec3(1.0), shadow); + } + +#endif //USE_NO_SHADOWS + + light_compute(normal, normalize(light_rel_vec), eye_vec, color_specular.rgb, light_attenuation, albedo, roughness, metallic, specular, color_specular.a * p_blob_intensity, +#ifdef LIGHT_TRANSMISSION_USED + transmission, +#endif +#ifdef LIGHT_RIM_USED + rim * spot_attenuation, rim_tint, +#endif +#ifdef LIGHT_CLEARCOAT_USED + clearcoat, clearcoat_gloss, +#endif +#ifdef LIGHT_ANISOTROPY_USED + binormal, tangent, anisotropy, +#endif +#ifdef USE_SHADOW_TO_OPACITY + alpha, +#endif + diffuse_light, specular_light); +} + +void reflection_process(uint ref_index, vec3 vertex, vec3 normal, float roughness, vec3 ambient_light, vec3 specular_light, inout vec4 ambient_accum, inout vec4 reflection_accum) { + + vec3 box_extents = reflections.data[ref_index].box_extents; + vec3 local_pos = (reflections.data[ref_index].local_matrix * vec4(vertex, 1.0)).xyz; + + if (any(greaterThan(abs(local_pos), box_extents))) { //out of the reflection box + return; + } + + vec3 ref_vec = normalize(reflect(vertex, normal)); + + vec3 inner_pos = abs(local_pos / box_extents); + float blend = max(inner_pos.x, max(inner_pos.y, inner_pos.z)); + //make blend more rounded + blend = mix(length(inner_pos), blend, blend); + blend *= blend; + blend = max(0.0, 1.0 - blend); + + if (reflections.data[ref_index].params.x > 0.0) { // compute reflection + + vec3 local_ref_vec = (reflections.data[ref_index].local_matrix * vec4(ref_vec, 0.0)).xyz; + + if (reflections.data[ref_index].params.w > 0.5) { //box project + + vec3 nrdir = normalize(local_ref_vec); + vec3 rbmax = (box_extents - local_pos) / nrdir; + vec3 rbmin = (-box_extents - local_pos) / nrdir; + + vec3 rbminmax = mix(rbmin, rbmax, greaterThan(nrdir, vec3(0.0, 0.0, 0.0))); + + float fa = min(min(rbminmax.x, rbminmax.y), rbminmax.z); + vec3 posonbox = local_pos + nrdir * fa; + local_ref_vec = posonbox - reflections.data[ref_index].box_offset; + } + + vec4 reflection; + + reflection.rgb = textureLod(samplerCubeArray(reflection_atlas, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), vec4(local_ref_vec, reflections.data[ref_index].index), roughness * MAX_ROUGHNESS_LOD).rgb; + + if (reflections.data[ref_index].params.z < 0.5) { + reflection.rgb = mix(specular_light, reflection.rgb, blend); + } + + reflection.rgb *= reflections.data[ref_index].params.x; + reflection.a = blend; + reflection.rgb *= reflection.a; + + reflection_accum += reflection; + } + +#if !defined(USE_LIGHTMAP) && !defined(USE_VOXEL_CONE_TRACING) + if (reflections.data[ref_index].ambient.a > 0.0) { //compute ambient using skybox + + vec3 local_amb_vec = (reflections.data[ref_index].local_matrix * vec4(normal, 0.0)).xyz; + + vec4 ambient_out; + + ambient_out.rgb = textureLod(samplerCubeArray(reflection_atlas, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), vec4(local_amb_vec, reflections.data[ref_index].index), MAX_ROUGHNESS_LOD).rgb; + + ambient_out.a = blend; + ambient_out.rgb = mix(reflections.data[ref_index].ambient.rgb, ambient_out.rgb, reflections.data[ref_index].ambient.a); + if (reflections.data[ref_index].params.z < 0.5) { + ambient_out.rgb = mix(ambient_light, ambient_out.rgb, blend); + } + + ambient_out.rgb *= ambient_out.a; + ambient_accum += ambient_out; + } else { + + vec4 ambient_out; + ambient_out.a = blend; + ambient_out.rgb = reflections.data[ref_index].ambient.rgb; + if (reflections.data[ref_index].params.z < 0.5) { + ambient_out.rgb = mix(ambient_light, ambient_out.rgb, blend); + } + ambient_out.rgb *= ambient_out.a; + ambient_accum += ambient_out; + } +#endif //USE_LIGHTMAP or VCT +} + +#ifdef USE_VOXEL_CONE_TRACING + +//standard voxel cone trace +vec4 voxel_cone_trace(texture3D probe, vec3 cell_size, vec3 pos, vec3 direction, float tan_half_angle, float max_distance, float p_bias) { + + float dist = p_bias; + vec4 color = vec4(0.0); + + while (dist < max_distance && color.a < 0.95) { + float diameter = max(1.0, 2.0 * tan_half_angle * dist); + vec3 uvw_pos = (pos + dist * direction) * cell_size; + float half_diameter = diameter * 0.5; + //check if outside, then break + if (any(greaterThan(abs(uvw_pos - 0.5), vec3(0.5f + half_diameter * cell_size)))) { + break; + } + vec4 scolor = textureLod(sampler3D(probe, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), uvw_pos, log2(diameter)); + float a = (1.0 - color.a); + color += a * scolor; + dist += half_diameter; + } + + return color; +} + +#ifndef GI_PROBE_HIGH_QUALITY +//faster version for 45 degrees + +#ifdef GI_PROBE_USE_ANISOTROPY + +vec4 voxel_cone_trace_anisotropic_45_degrees(texture3D probe, texture3D aniso_pos, texture3D aniso_neg, vec3 normal, vec3 cell_size, vec3 pos, vec3 direction, float tan_half_angle, float max_distance, float p_bias) { + + float dist = p_bias; + vec4 color = vec4(0.0); + float radius = max(0.5, tan_half_angle * dist); + float lod_level = log2(radius * 2.0); + + while (dist < max_distance && color.a < 0.95) { + vec3 uvw_pos = (pos + dist * direction) * cell_size; + //check if outside, then break + if (any(greaterThan(abs(uvw_pos - 0.5), vec3(0.5f + radius * cell_size)))) { + break; + } + + vec4 scolor = textureLod(sampler3D(probe, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), uvw_pos, lod_level); + vec3 aniso_neg = textureLod(sampler3D(aniso_neg, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), uvw_pos, lod_level).rgb; + vec3 aniso_pos = textureLod(sampler3D(aniso_pos, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), uvw_pos, lod_level).rgb; + + scolor.rgb *= dot(max(vec3(0.0), (normal * aniso_pos)), vec3(1.0)) + dot(max(vec3(0.0), (-normal * aniso_neg)), vec3(1.0)); + lod_level += 1.0; + + float a = (1.0 - color.a); + scolor *= a; + color += scolor; + dist += radius; + radius = max(0.5, tan_half_angle * dist); + } + + return color; +} +#else + +vec4 voxel_cone_trace_45_degrees(texture3D probe, vec3 cell_size, vec3 pos, vec3 direction, float tan_half_angle, float max_distance, float p_bias) { + + float dist = p_bias; + vec4 color = vec4(0.0); + float radius = max(0.5, tan_half_angle * dist); + float lod_level = log2(radius * 2.0); + + while (dist < max_distance && color.a < 0.95) { + vec3 uvw_pos = (pos + dist * direction) * cell_size; + + //check if outside, then break + if (any(greaterThan(abs(uvw_pos - 0.5), vec3(0.5f + radius * cell_size)))) { + break; + } + vec4 scolor = textureLod(sampler3D(probe, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), uvw_pos, lod_level); + lod_level += 1.0; + + float a = (1.0 - color.a); + scolor *= a; + color += scolor; + dist += radius; + radius = max(0.5, tan_half_angle * dist); + } + + return color; +} + +#endif + +#elif defined(GI_PROBE_USE_ANISOTROPY) + +//standard voxel cone trace +vec4 voxel_cone_trace_anisotropic(texture3D probe, texture3D aniso_pos, texture3D aniso_neg, vec3 normal, vec3 cell_size, vec3 pos, vec3 direction, float tan_half_angle, float max_distance, float p_bias) { + + float dist = p_bias; + vec4 color = vec4(0.0); + + while (dist < max_distance && color.a < 0.95) { + float diameter = max(1.0, 2.0 * tan_half_angle * dist); + vec3 uvw_pos = (pos + dist * direction) * cell_size; + float half_diameter = diameter * 0.5; + //check if outside, then break + if (any(greaterThan(abs(uvw_pos - 0.5), vec3(0.5f + half_diameter * cell_size)))) { + break; + } + float log2_diameter = log2(diameter); + vec4 scolor = textureLod(sampler3D(probe, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), uvw_pos, log2_diameter); + vec3 aniso_neg = textureLod(sampler3D(aniso_neg, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), uvw_pos, log2_diameter).rgb; + vec3 aniso_pos = textureLod(sampler3D(aniso_pos, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), uvw_pos, log2_diameter).rgb; + + scolor.rgb *= dot(max(vec3(0.0), (normal * aniso_pos)), vec3(1.0)) + dot(max(vec3(0.0), (-normal * aniso_neg)), vec3(1.0)); + + float a = (1.0 - color.a); + scolor *= a; + color += scolor; + dist += half_diameter; + } + + return color; +} + +#endif + +void gi_probe_compute(uint index, vec3 position, vec3 normal, vec3 ref_vec, mat3 normal_xform, float roughness, vec3 ambient, vec3 environment, inout vec4 out_spec, inout vec4 out_diff) { + + position = (gi_probes.data[index].xform * vec4(position, 1.0)).xyz; + ref_vec = normalize((gi_probes.data[index].xform * vec4(ref_vec, 0.0)).xyz); + normal = normalize((gi_probes.data[index].xform * vec4(normal, 0.0)).xyz); + + position += normal * gi_probes.data[index].normal_bias; + + //this causes corrupted pixels, i have no idea why.. + if (any(bvec2(any(lessThan(position, vec3(0.0))), any(greaterThan(position, gi_probes.data[index].bounds))))) { + return; + } + + vec3 blendv = abs(position / gi_probes.data[index].bounds * 2.0 - 1.0); + float blend = clamp(1.0 - max(blendv.x, max(blendv.y, blendv.z)), 0.0, 1.0); + //float blend=1.0; + + float max_distance = length(gi_probes.data[index].bounds); + vec3 cell_size = 1.0 / gi_probes.data[index].bounds; + + //radiance + +#ifdef GI_PROBE_HIGH_QUALITY + +#define MAX_CONE_DIRS 6 + vec3 cone_dirs[MAX_CONE_DIRS] = vec3[]( + vec3(0.0, 0.0, 1.0), + vec3(0.866025, 0.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)); + + float cone_weights[MAX_CONE_DIRS] = float[](0.25, 0.15, 0.15, 0.15, 0.15, 0.15); + float cone_angle_tan = 0.577; + +#elif defined(GI_PROBE_LOW_QUALITY) + +#define MAX_CONE_DIRS 1 + + vec3 cone_dirs[MAX_CONE_DIRS] = vec3[]( + vec3(0.0, 0.0, 1.0)); + + float cone_weights[MAX_CONE_DIRS] = float[](1.0); + float cone_angle_tan = 4; //~76 degrees +#else // MEDIUM QUALITY + +#define MAX_CONE_DIRS 4 + + vec3 cone_dirs[MAX_CONE_DIRS] = vec3[]( + vec3(0.707107, 0.0, 0.707107), + vec3(0.0, 0.707107, 0.707107), + vec3(-0.707107, 0.0, 0.707107), + vec3(0.0, -0.707107, 0.707107)); + + float cone_weights[MAX_CONE_DIRS] = float[](0.25, 0.25, 0.25, 0.25); + float cone_angle_tan = 0.98269; + +#endif + vec3 light = vec3(0.0); + + for (int i = 0; i < MAX_CONE_DIRS; i++) { + + vec3 dir = normalize((gi_probes.data[index].xform * vec4(normal_xform * cone_dirs[i], 0.0)).xyz); + +#if defined(GI_PROBE_HIGH_QUALITY) || defined(GI_PROBE_LOW_QUALITY) + +#ifdef GI_PROBE_USE_ANISOTROPY + vec4 cone_light = voxel_cone_trace_anisotropic(gi_probe_textures[gi_probes.data[index].texture_slot], gi_probe_textures[gi_probes.data[index].texture_slot + 1], gi_probe_textures[gi_probes.data[index].texture_slot + 2], normalize(mix(dir, normal, gi_probes.data[index].anisotropy_strength)), cell_size, position, dir, cone_angle_tan, max_distance, gi_probes.data[index].bias); +#else + + vec4 cone_light = voxel_cone_trace(gi_probe_textures[gi_probes.data[index].texture_slot], cell_size, position, dir, cone_angle_tan, max_distance, gi_probes.data[index].bias); + +#endif // GI_PROBE_USE_ANISOTROPY + +#else + +#ifdef GI_PROBE_USE_ANISOTROPY + vec4 cone_light = voxel_cone_trace_anisotropic_45_degrees(gi_probe_textures[gi_probes.data[index].texture_slot], gi_probe_textures[gi_probes.data[index].texture_slot + 1], gi_probe_textures[gi_probes.data[index].texture_slot + 2], normalize(mix(dir, normal, gi_probes.data[index].anisotropy_strength)), cell_size, position, dir, cone_angle_tan, max_distance, gi_probes.data[index].bias); +#else + vec4 cone_light = voxel_cone_trace_45_degrees(gi_probe_textures[gi_probes.data[index].texture_slot], cell_size, position, dir, cone_angle_tan, max_distance, gi_probes.data[index].bias); +#endif // GI_PROBE_USE_ANISOTROPY + +#endif + if (gi_probes.data[index].blend_ambient) { + cone_light.rgb = mix(ambient, cone_light.rgb, min(1.0, cone_light.a / 0.95)); + } + + light += cone_weights[i] * cone_light.rgb; + } + + light *= gi_probes.data[index].dynamic_range; + + if (gi_probes.data[index].ambient_occlusion > 0.001) { + + float size = 1.0 + gi_probes.data[index].ambient_occlusion_size * 7.0; + + float taps, blend; + blend = modf(size, taps); + float ao = 0.0; + for (float i = 1.0; i <= taps; i++) { + vec3 ofs = (position + normal * (i * 0.5 + 1.0)) * cell_size; + ao += textureLod(sampler3D(gi_probe_textures[gi_probes.data[index].texture_slot], material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), ofs, i - 1.0).a * i; + } + + if (blend > 0.001) { + vec3 ofs = (position + normal * ((taps + 1.0) * 0.5 + 1.0)) * cell_size; + ao += textureLod(sampler3D(gi_probe_textures[gi_probes.data[index].texture_slot], material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), ofs, taps).a * (taps + 1.0) * blend; + } + + ao = 1.0 - min(1.0, ao); + + light = mix(scene_data.ao_color.rgb, light, mix(1.0, ao, gi_probes.data[index].ambient_occlusion)); + } + + out_diff += vec4(light * blend, blend); + + //irradiance +#ifndef GI_PROBE_LOW_QUALITY + vec4 irr_light = voxel_cone_trace(gi_probe_textures[gi_probes.data[index].texture_slot], cell_size, position, ref_vec, tan(roughness * 0.5 * M_PI * 0.99), max_distance, gi_probes.data[index].bias); + if (gi_probes.data[index].blend_ambient) { + irr_light.rgb = mix(environment, irr_light.rgb, min(1.0, irr_light.a / 0.95)); + } + irr_light.rgb *= gi_probes.data[index].dynamic_range; + //irr_light=vec3(0.0); + + out_spec += vec4(irr_light.rgb * blend, blend); +#endif +} + +#endif //USE_VOXEL_CONE_TRACING + +#endif //!defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) + +void main() { + +#ifdef MODE_DUAL_PARABOLOID + + if (dp_clip > 0.0) + discard; +#endif + + //lay out everything, whathever is unused is optimized away anyway + vec3 vertex = vertex_interp; + vec3 view = -normalize(vertex_interp); + vec3 albedo = vec3(1.0); + vec3 transmission = vec3(0.0); + float metallic = 0.0; + float specular = 0.5; + vec3 emission = vec3(0.0); + float roughness = 1.0; + float rim = 0.0; + float rim_tint = 0.0; + float clearcoat = 0.0; + float clearcoat_gloss = 0.0; + float anisotropy = 0.0; + vec2 anisotropy_flow = vec2(1.0, 0.0); + +#if defined(AO_USED) + float ao = 1.0; + float ao_light_affect = 0.0; +#endif + + float alpha = 1.0; + +#if defined(ALPHA_SCISSOR_USED) + float alpha_scissor = 0.5; +#endif + +#if defined(TANGENT_USED) || defined(NORMALMAP_USED) || defined(LIGHT_ANISOTROPY_USED) + vec3 binormal = normalize(binormal_interp); + vec3 tangent = normalize(tangent_interp); +#else + vec3 binormal = vec3(0.0); + vec3 tangent = vec3(0.0); +#endif + vec3 normal = normalize(normal_interp); + +#if defined(DO_SIDE_CHECK) + if (!gl_FrontFacing) { + normal = -normal; + } +#endif + +#if defined(UV_USED) + vec2 uv = uv_interp; +#endif + +#if defined(UV2_USED) || defined(USE_LIGHTMAP) + vec2 uv2 = uv2_interp; +#endif + +#if defined(COLOR_USED) + vec4 color = color_interp; +#endif + +#if defined(NORMALMAP_USED) + + vec3 normalmap = vec3(0.5); +#endif + + float normaldepth = 1.0; + + vec2 screen_uv = gl_FragCoord.xy * scene_data.screen_pixel_size + scene_data.screen_pixel_size * 0.5; //account for center + + float sss_strength = 0.0; + + { + /* clang-format off */ + +FRAGMENT_SHADER_CODE + + /* clang-format on */ + } + +#if !defined(USE_SHADOW_TO_OPACITY) + +#if defined(ALPHA_SCISSOR_USED) + if (alpha < alpha_scissor) { + discard; + } +#endif // ALPHA_SCISSOR_USED + +#ifdef USE_OPAQUE_PREPASS + + if (alpha < opaque_prepass_threshold) { + discard; + } + +#endif // USE_OPAQUE_PREPASS + +#endif // !USE_SHADOW_TO_OPACITY + +#if defined(NORMALMAP_USED) + + normalmap.xy = normalmap.xy * 2.0 - 1.0; + normalmap.z = sqrt(max(0.0, 1.0 - dot(normalmap.xy, normalmap.xy))); //always ignore Z, as it can be RG packed, Z may be pos/neg, etc. + + normal = normalize(mix(normal, tangent * normalmap.x + binormal * normalmap.y + normal * normalmap.z, normaldepth)); + +#endif + +#if defined(LIGHT_ANISOTROPY_USED) + + if (anisotropy > 0.01) { + //rotation matrix + mat3 rot = mat3(tangent, binormal, normal); + //make local to space + tangent = normalize(rot * vec3(anisotropy_flow.x, anisotropy_flow.y, 0.0)); + binormal = normalize(rot * vec3(-anisotropy_flow.y, anisotropy_flow.x, 0.0)); + } + +#endif + +#ifdef ENABLE_CLIP_ALPHA + if (albedo.a < 0.99) { + //used for doublepass and shadowmapping + discard; + } +#endif + + /////////////////////// LIGHTING ////////////////////////////// + + //apply energy conservation + + vec3 specular_light = vec3(0.0, 0.0, 0.0); + vec3 diffuse_light = vec3(0.0, 0.0, 0.0); + vec3 ambient_light = vec3(0.0, 0.0, 0.0); + +#if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) + + if (scene_data.roughness_limiter_enabled) { + float limit = texelFetch(sampler2D(roughness_buffer, material_samplers[SAMPLER_NEAREST_CLAMP]), ivec2(gl_FragCoord.xy), 0).r; + roughness = max(roughness, limit); + } + + if (scene_data.use_reflection_cubemap) { + + vec3 ref_vec = reflect(-view, normal); + ref_vec = scene_data.radiance_inverse_xform * ref_vec; +#ifdef USE_RADIANCE_CUBEMAP_ARRAY + + float lod, blend; + blend = modf(roughness * MAX_ROUGHNESS_LOD, lod); + specular_light = texture(samplerCubeArray(radiance_cubemap, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), vec4(ref_vec, lod)).rgb; + specular_light = mix(specular_light, texture(samplerCubeArray(radiance_cubemap, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), vec4(ref_vec, lod + 1)).rgb, blend); + +#else + specular_light = textureLod(samplerCube(radiance_cubemap, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), ref_vec, roughness * MAX_ROUGHNESS_LOD).rgb; + +#endif //USE_RADIANCE_CUBEMAP_ARRAY + specular_light *= scene_data.ambient_light_color_energy.a; + } + +#ifndef USE_LIGHTMAP + //lightmap overrides everything + if (scene_data.use_ambient_light) { + + ambient_light = scene_data.ambient_light_color_energy.rgb; + + if (scene_data.use_ambient_cubemap) { + vec3 ambient_dir = scene_data.radiance_inverse_xform * normal; +#ifdef USE_RADIANCE_CUBEMAP_ARRAY + vec3 cubemap_ambient = texture(samplerCubeArray(radiance_cubemap, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), vec4(ambient_dir, MAX_ROUGHNESS_LOD)).rgb; +#else + vec3 cubemap_ambient = textureLod(samplerCube(radiance_cubemap, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), ambient_dir, MAX_ROUGHNESS_LOD).rgb; +#endif //USE_RADIANCE_CUBEMAP_ARRAY + + ambient_light = mix(ambient_light, cubemap_ambient * scene_data.ambient_light_color_energy.a, scene_data.ambient_color_sky_mix); + } + } +#endif // USE_LIGHTMAP + +#endif //!defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) + + //radiance + + float specular_blob_intensity = 1.0; + +#if defined(SPECULAR_TOON) + specular_blob_intensity *= specular * 2.0; +#endif + +#if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) + //gi probes + + //lightmap + + //lightmap capture + +#ifdef USE_VOXEL_CONE_TRACING + { // process giprobes + uint index1 = instances.data[instance_index].gi_offset & 0xFFFF; + if (index1 != 0xFFFF) { + vec3 ref_vec = normalize(reflect(normalize(vertex), normal)); + //find arbitrary tangent and bitangent, then build a matrix + vec3 v0 = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 1.0, 0.0); + vec3 tangent = normalize(cross(v0, normal)); + vec3 bitangent = normalize(cross(tangent, normal)); + mat3 normal_mat = mat3(tangent, bitangent, normal); + + vec4 amb_accum = vec4(0.0); + vec4 spec_accum = vec4(0.0); + gi_probe_compute(index1, vertex, normal, ref_vec, normal_mat, roughness * roughness, ambient_light, specular_light, spec_accum, amb_accum); + + uint index2 = instances.data[instance_index].gi_offset >> 16; + + if (index2 != 0xFFFF) { + gi_probe_compute(index2, vertex, normal, ref_vec, normal_mat, roughness * roughness, ambient_light, specular_light, spec_accum, amb_accum); + } + + if (amb_accum.a > 0.0) { + amb_accum.rgb /= amb_accum.a; + } + + if (spec_accum.a > 0.0) { + spec_accum.rgb /= spec_accum.a; + } + + specular_light = spec_accum.rgb; + ambient_light = amb_accum.rgb; + } + } +#endif + + uvec4 cluster_cell = texture(usampler3D(cluster_texture, material_samplers[SAMPLER_NEAREST_CLAMP]), vec3(screen_uv, (abs(vertex.z) - scene_data.z_near) / (scene_data.z_far - scene_data.z_near))); + + { // process reflections + + vec4 reflection_accum = vec4(0.0, 0.0, 0.0, 0.0); + vec4 ambient_accum = vec4(0.0, 0.0, 0.0, 0.0); + + uint reflection_probe_count = cluster_cell.z >> CLUSTER_COUNTER_SHIFT; + uint reflection_probe_pointer = cluster_cell.z & CLUSTER_POINTER_MASK; + + for (uint i = 0; i < reflection_probe_count; i++) { + + uint ref_index = cluster_data.indices[reflection_probe_pointer + i]; + reflection_process(ref_index, vertex, normal, roughness, ambient_light, specular_light, ambient_accum, reflection_accum); + } + + if (reflection_accum.a > 0.0) { + specular_light = reflection_accum.rgb / reflection_accum.a; + } + +#if !defined(USE_LIGHTMAP) + if (ambient_accum.a > 0.0) { + ambient_light = ambient_accum.rgb / ambient_accum.a; + } +#endif + } + + { + +#if defined(DIFFUSE_TOON) + //simplify for toon, as + specular_light *= specular * metallic * albedo * 2.0; +#else + + // scales the specular reflections, needs to be be computed before lighting happens, + // but after environment, GI, and reflection probes are added + // Environment brdf approximation (Lazarov 2013) + // see https://www.unrealengine.com/en-US/blog/physically-based-shading-on-mobile + const vec4 c0 = vec4(-1.0, -0.0275, -0.572, 0.022); + const vec4 c1 = vec4(1.0, 0.0425, 1.04, -0.04); + vec4 r = roughness * c0 + c1; + float ndotv = clamp(dot(normal, view), 0.0, 1.0); + float a004 = min(r.x * r.x, exp2(-9.28 * ndotv)) * r.x + r.y; + vec2 env = vec2(-1.04, 1.04) * a004 + r.zw; + + vec3 f0 = F0(metallic, specular, albedo); + specular_light *= env.x * f0 + env.y; +#endif + } + + { //directional light + + for (uint i = 0; i < scene_data.directional_light_count; i++) { + + if (!bool(directional_lights.data[i].mask & instances.data[instance_index].layer_mask)) { + continue; //not masked + } + + vec3 light_attenuation = vec3(1.0); + + if (directional_lights.data[i].shadow_enabled) { + float depth_z = -vertex.z; + + vec4 pssm_coord; + + if (depth_z < directional_lights.data[i].shadow_split_offsets.x) { + pssm_coord = (directional_lights.data[i].shadow_matrix1 * vec4(vertex, 1.0)); + } else if (depth_z < directional_lights.data[i].shadow_split_offsets.y) { + pssm_coord = (directional_lights.data[i].shadow_matrix2 * vec4(vertex, 1.0)); + } else if (depth_z < directional_lights.data[i].shadow_split_offsets.z) { + pssm_coord = (directional_lights.data[i].shadow_matrix3 * vec4(vertex, 1.0)); + } else { + pssm_coord = (directional_lights.data[i].shadow_matrix4 * vec4(vertex, 1.0)); + } + + pssm_coord /= pssm_coord.w; + + float shadow = sample_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size, pssm_coord); + + if (directional_lights.data[i].blend_splits) { + + float pssm_blend; + + if (depth_z < directional_lights.data[i].shadow_split_offsets.x) { + pssm_coord = (directional_lights.data[i].shadow_matrix2 * vec4(vertex, 1.0)); + pssm_blend = smoothstep(0.0, directional_lights.data[i].shadow_split_offsets.x, depth_z); + } else if (depth_z < directional_lights.data[i].shadow_split_offsets.y) { + pssm_coord = (directional_lights.data[i].shadow_matrix3 * vec4(vertex, 1.0)); + pssm_blend = smoothstep(directional_lights.data[i].shadow_split_offsets.x, directional_lights.data[i].shadow_split_offsets.y, depth_z); + } else if (depth_z < directional_lights.data[i].shadow_split_offsets.z) { + pssm_coord = (directional_lights.data[i].shadow_matrix4 * vec4(vertex, 1.0)); + pssm_blend = smoothstep(directional_lights.data[i].shadow_split_offsets.y, directional_lights.data[i].shadow_split_offsets.z, depth_z); + } else { + pssm_blend = 0.0; //if no blend, same coord will be used (divide by z will result in same value, and already cached) + } + + pssm_coord /= pssm_coord.w; + + float shadow2 = sample_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size, pssm_coord); + shadow = mix(shadow, shadow2, pssm_blend); + } + + shadow = mix(shadow, 1.0, smoothstep(directional_lights.data[i].fade_from, directional_lights.data[i].fade_to, vertex.z)); //done with negative values for performance + + light_attenuation = mix(directional_lights.data[i].shadow_color, vec3(1.0), shadow); + } + + light_compute(normal, directional_lights.data[i].direction, normalize(view), directional_lights.data[i].color * directional_lights.data[i].energy, light_attenuation, albedo, roughness, metallic, specular, directional_lights.data[i].specular * specular_blob_intensity, +#ifdef LIGHT_TRANSMISSION_USED + transmission, +#endif +#ifdef LIGHT_RIM_USED + rim, rim_tint, +#endif +#ifdef LIGHT_CLEARCOAT_USED + clearcoat, clearcoat_gloss, +#endif +#ifdef LIGHT_ANISOTROPY_USED + binormal, tangent, anisotropy, +#endif +#ifdef USE_SHADOW_TO_OPACITY + alpha, +#endif + diffuse_light, + specular_light); + } + } + + { //omni lights + + uint omni_light_count = cluster_cell.x >> CLUSTER_COUNTER_SHIFT; + uint omni_light_pointer = cluster_cell.x & CLUSTER_POINTER_MASK; + + for (uint i = 0; i < omni_light_count; i++) { + + uint light_index = cluster_data.indices[omni_light_pointer + i]; + + if (!bool(lights.data[light_index].mask & instances.data[instance_index].layer_mask)) { + continue; //not masked + } + + light_process_omni(light_index, vertex, view, normal, albedo, roughness, metallic, specular, specular_blob_intensity, +#ifdef LIGHT_TRANSMISSION_USED + transmission, +#endif +#ifdef LIGHT_RIM_USED + rim, + rim_tint, +#endif +#ifdef LIGHT_CLEARCOAT_USED + clearcoat, clearcoat_gloss, +#endif +#ifdef LIGHT_ANISOTROPY_USED + tangent, binormal, anisotropy, +#endif +#ifdef USE_SHADOW_TO_OPACITY + alpha, +#endif + diffuse_light, specular_light); + } + } + + { //spot lights + uint spot_light_count = cluster_cell.y >> CLUSTER_COUNTER_SHIFT; + uint spot_light_pointer = cluster_cell.y & CLUSTER_POINTER_MASK; + + for (uint i = 0; i < spot_light_count; i++) { + + uint light_index = cluster_data.indices[spot_light_pointer + i]; + + if (!bool(lights.data[light_index].mask & instances.data[instance_index].layer_mask)) { + continue; //not masked + } + + light_process_spot(light_index, vertex, view, normal, albedo, roughness, metallic, specular, specular_blob_intensity, +#ifdef LIGHT_TRANSMISSION_USED + transmission, +#endif +#ifdef LIGHT_RIM_USED + rim, + rim_tint, +#endif +#ifdef LIGHT_CLEARCOAT_USED + clearcoat, clearcoat_gloss, +#endif +#ifdef LIGHT_ANISOTROPY_USED + tangent, binormal, anisotropy, +#endif +#ifdef USE_SHADOW_TO_OPACITY + alpha, +#endif + diffuse_light, specular_light); + } + } + +#ifdef USE_SHADOW_TO_OPACITY + alpha = min(alpha, clamp(length(ambient_light), 0.0, 1.0)); + +#if defined(ALPHA_SCISSOR_USED) + if (alpha < alpha_scissor) { + discard; + } +#endif // ALPHA_SCISSOR_USED + +#ifdef USE_OPAQUE_PREPASS + + if (alpha < opaque_prepass_threshold) { + discard; + } + +#endif // USE_OPAQUE_PREPASS + +#endif // USE_SHADOW_TO_OPACITY + +#endif //!defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) + +#ifdef MODE_RENDER_DEPTH + +#ifdef MODE_RENDER_MATERIAL + + albedo_output_buffer.rgb = albedo; + albedo_output_buffer.a = alpha; + + normal_output_buffer.rgb = normal * 0.5 + 0.5; + normal_output_buffer.a = 0.0; + depth_output_buffer.r = -vertex.z; + +#if defined(AO_USED) + orm_output_buffer.r = ao; +#else + orm_output_buffer.r = 0.0; +#endif + orm_output_buffer.g = roughness; + orm_output_buffer.b = metallic; + orm_output_buffer.a = sss_strength; + + emission_output_buffer.rgb = emission; + emission_output_buffer.a = 0.0; +#endif + +#ifdef MODE_RENDER_NORMAL + normal_output_buffer = vec4(normal * 0.5 + 0.5, 0.0); +#ifdef MODE_RENDER_ROUGHNESS + roughness_output_buffer = roughness; +#endif //MODE_RENDER_ROUGHNESS +#endif //MODE_RENDER_NORMAL + +//nothing happens, so a tree-ssa optimizer will result in no fragment shader :) +#else + + specular_light *= scene_data.reflection_multiplier; + ambient_light *= albedo; //ambient must be multiplied by albedo at the end + +//ambient occlusion +#if defined(AO_USED) + + if (scene_data.ssao_enabled && scene_data.ssao_ao_affect > 0.0) { + float ssao = texture(sampler2D(ao_buffer, material_samplers[SAMPLER_LINEAR_CLAMP]), screen_uv).r; + ao = mix(ao, min(ao, ssao), scene_data.ssao_ao_affect); + ao_light_affect = mix(ao_light_affect, max(ao_light_affect, scene_data.ssao_light_affect), scene_data.ssao_ao_affect); + } + + ambient_light = mix(scene_data.ao_color.rgb, ambient_light, ao); + ao_light_affect = mix(1.0, ao, ao_light_affect); + specular_light = mix(scene_data.ao_color.rgb, specular_light, ao_light_affect); + diffuse_light = mix(scene_data.ao_color.rgb, diffuse_light, ao_light_affect); + +#else + + if (scene_data.ssao_enabled) { + float ao = texture(sampler2D(ao_buffer, material_samplers[SAMPLER_LINEAR_CLAMP]), screen_uv).r; + ambient_light = mix(scene_data.ao_color.rgb, ambient_light, ao); + float ao_light_affect = mix(1.0, ao, scene_data.ssao_light_affect); + specular_light = mix(scene_data.ao_color.rgb, specular_light, ao_light_affect); + diffuse_light = mix(scene_data.ao_color.rgb, diffuse_light, ao_light_affect); + } + +#endif // AO_USED + + // base color remapping + diffuse_light *= 1.0 - metallic; // TODO: avoid all diffuse and ambient light calculations when metallic == 1 up to this point + ambient_light *= 1.0 - metallic; + + //fog + +#ifdef MODE_MULTIPLE_RENDER_TARGETS + +#ifdef MODE_UNSHADED + diffuse_buffer = vec4(albedo.rgb, 0.0); + specular_buffer = vec4(0.0); + +#else + + diffuse_buffer = vec4(emission + diffuse_light + ambient_light, sss_strength); + specular_buffer = vec4(specular_light, metallic); + +#endif + +#else //MODE_MULTIPLE_RENDER_TARGETS + +#ifdef MODE_UNSHADED + frag_color = vec4(albedo, alpha); +#else + frag_color = vec4(emission + ambient_light + diffuse_light + specular_light, alpha); + //frag_color = vec4(1.0); + +#endif //USE_NO_SHADING + +#endif //MODE_MULTIPLE_RENDER_TARGETS + +#endif //MODE_RENDER_DEPTH +} diff --git a/servers/rendering/rasterizer_rd/shaders/scene_high_end_inc.glsl b/servers/rendering/rasterizer_rd/shaders/scene_high_end_inc.glsl new file mode 100644 index 0000000000..baef1e060f --- /dev/null +++ b/servers/rendering/rasterizer_rd/shaders/scene_high_end_inc.glsl @@ -0,0 +1,266 @@ +#define M_PI 3.14159265359 +#define ROUGHNESS_MAX_LOD 5 + +layout(push_constant, binding = 0, std430) uniform DrawCall { + uint instance_index; + uint pad[3]; //16 bits minimum size +} +draw_call; + +/* Set 0 Scene data that never changes, ever */ + +#define SAMPLER_NEAREST_CLAMP 0 +#define SAMPLER_LINEAR_CLAMP 1 +#define SAMPLER_NEAREST_WITH_MIPMAPS_CLAMP 2 +#define SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP 3 +#define SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_CLAMP 4 +#define SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_CLAMP 5 +#define SAMPLER_NEAREST_REPEAT 6 +#define SAMPLER_LINEAR_REPEAT 7 +#define SAMPLER_NEAREST_WITH_MIPMAPS_REPEAT 8 +#define SAMPLER_LINEAR_WITH_MIPMAPS_REPEAT 9 +#define SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_REPEAT 10 +#define SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_REPEAT 11 + +layout(set = 0, binding = 1) uniform sampler material_samplers[12]; + +layout(set = 0, binding = 2) uniform sampler shadow_sampler; + +layout(set = 0, binding = 3, std140) uniform SceneData { + + mat4 projection_matrix; + mat4 inv_projection_matrix; + + mat4 camera_matrix; + mat4 inv_camera_matrix; + + vec2 viewport_size; + vec2 screen_pixel_size; + + //used for shadow mapping only + float z_offset; + float z_slope_scale; + + float time; + float reflection_multiplier; // one normally, zero when rendering reflections + + vec4 ambient_light_color_energy; + + float ambient_color_sky_mix; + bool use_ambient_light; + bool use_ambient_cubemap; + bool use_reflection_cubemap; + + mat3 radiance_inverse_xform; + + vec2 shadow_atlas_pixel_size; + vec2 directional_shadow_pixel_size; + + uint directional_light_count; + float dual_paraboloid_side; + float z_far; + float z_near; + + bool ssao_enabled; + float ssao_light_affect; + float ssao_ao_affect; + bool roughness_limiter_enabled; + + vec4 ao_color; + +#if 0 + vec4 ambient_light_color; + vec4 bg_color; + + vec4 fog_color_enabled; + vec4 fog_sun_color_amount; + + float ambient_energy; + float bg_energy; +#endif + +#if 0 + vec2 shadow_atlas_pixel_size; + vec2 directional_shadow_pixel_size; + + float z_far; + + float subsurface_scatter_width; + float ambient_occlusion_affect_light; + float ambient_occlusion_affect_ao_channel; + float opaque_prepass_threshold; + + bool fog_depth_enabled; + float fog_depth_begin; + float fog_depth_end; + float fog_density; + float fog_depth_curve; + bool fog_transmit_enabled; + float fog_transmit_curve; + bool fog_height_enabled; + float fog_height_min; + float fog_height_max; + float fog_height_curve; +#endif +} +scene_data; + +#define INSTANCE_FLAGS_FORWARD_MASK 0x7 +#define INSTANCE_FLAGS_FORWARD_OMNI_LIGHT_SHIFT 3 +#define INSTANCE_FLAGS_FORWARD_SPOT_LIGHT_SHIFT 6 +#define INSTANCE_FLAGS_FORWARD_DECAL_SHIFT 9 + +#define INSTANCE_FLAGS_MULTIMESH (1 << 12) +#define INSTANCE_FLAGS_MULTIMESH_FORMAT_2D (1 << 13) +#define INSTANCE_FLAGS_MULTIMESH_HAS_COLOR (1 << 14) +#define INSTANCE_FLAGS_MULTIMESH_HAS_CUSTOM_DATA (1 << 15) +#define INSTANCE_FLAGS_MULTIMESH_STRIDE_SHIFT 16 +//3 bits of stride +#define INSTANCE_FLAGS_MULTIMESH_STRIDE_MASK 0x7 + +#define INSTANCE_FLAGS_SKELETON (1 << 19) + +struct InstanceData { + mat4 transform; + mat4 normal_transform; + uint flags; + uint instance_ofs; //instance_offset in instancing/skeleton buffer + uint gi_offset; //GI information when using lightmapping (VCT or lightmap) + uint layer_mask; +}; + +layout(set = 0, binding = 4, std430) buffer Instances { + InstanceData data[]; +} +instances; + +struct LightData { //this structure needs to be 128 bits + vec3 position; + float inv_radius; + vec3 direction; + uint attenuation_energy; //attenuation + uint color_specular; //rgb color, a specular (8 bit unorm) + uint cone_attenuation_angle; // attenuation and angle, (16bit float) + uint mask; + uint shadow_color_enabled; //shadow rgb color, a>0.5 enabled (8bit unorm) + vec4 atlas_rect; //used for shadow atlas uv on omni, and for projection atlas on spot + mat4 shadow_matrix; +}; + +layout(set = 0, binding = 5, std140) uniform Lights { + LightData data[MAX_LIGHT_DATA_STRUCTS]; +} +lights; + +struct ReflectionData { + + vec3 box_extents; + float index; + vec3 box_offset; + uint mask; + vec4 params; // intensity, 0, interior , boxproject + vec4 ambient; // ambient color, energy + mat4 local_matrix; // up to here for spot and omni, rest is for directional + // notes: for ambientblend, use distance to edge to blend between already existing global environment +}; + +layout(set = 0, binding = 6, std140) uniform ReflectionProbeData { + ReflectionData data[MAX_REFLECTION_DATA_STRUCTS]; +} +reflections; + +struct DirectionalLightData { + vec3 direction; + float energy; + vec3 color; + float specular; + vec3 shadow_color; + uint mask; + bool blend_splits; + bool shadow_enabled; + float fade_from; + float fade_to; + vec4 shadow_split_offsets; + mat4 shadow_matrix1; + mat4 shadow_matrix2; + mat4 shadow_matrix3; + mat4 shadow_matrix4; +}; + +layout(set = 0, binding = 7, std140) uniform DirectionalLights { + DirectionalLightData data[MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS]; +} +directional_lights; + +struct GIProbeData { + mat4 xform; + vec3 bounds; + float dynamic_range; + + float bias; + float normal_bias; + bool blend_ambient; + uint texture_slot; + + float anisotropy_strength; + float ambient_occlusion; + float ambient_occlusion_size; + uint pad2; +}; + +layout(set = 0, binding = 8, std140) uniform GIProbes { + GIProbeData data[MAX_GI_PROBES]; +} +gi_probes; + +layout(set = 0, binding = 9) uniform texture3D gi_probe_textures[MAX_GI_PROBE_TEXTURES]; + +#define CLUSTER_COUNTER_SHIFT 20 +#define CLUSTER_POINTER_MASK ((1 << CLUSTER_COUNTER_SHIFT) - 1) +#define CLUSTER_COUNTER_MASK 0xfff + +layout(set = 0, binding = 10) uniform utexture3D cluster_texture; + +layout(set = 0, binding = 11, std430) buffer ClusterData { + uint indices[]; +} +cluster_data; + +layout(set = 0, binding = 12) uniform texture2D directional_shadow_atlas; + +// decal atlas + +/* Set 1, Radiance */ + +#ifdef USE_RADIANCE_CUBEMAP_ARRAY + +layout(set = 1, binding = 0) uniform textureCubeArray radiance_cubemap; + +#else + +layout(set = 1, binding = 0) uniform textureCube radiance_cubemap; + +#endif + +/* Set 2, Reflection and Shadow Atlases (view dependant) */ + +layout(set = 2, binding = 0) uniform textureCubeArray reflection_atlas; + +layout(set = 2, binding = 1) uniform texture2D shadow_atlas; + +/* Set 1, Render Buffers */ + +layout(set = 3, binding = 0) uniform texture2D depth_buffer; +layout(set = 3, binding = 1) uniform texture2D color_buffer; +layout(set = 3, binding = 2) uniform texture2D normal_buffer; +layout(set = 3, binding = 3) uniform texture2D roughness_buffer; +layout(set = 3, binding = 4) uniform texture2D ao_buffer; + +/* Set 4 Skeleton & Instancing (Multimesh) */ + +layout(set = 4, binding = 0, std430) buffer Transforms { + vec4 data[]; +} +transforms; + +/* Set 5 User Material */ diff --git a/servers/rendering/rasterizer_rd/shaders/sky.glsl b/servers/rendering/rasterizer_rd/shaders/sky.glsl new file mode 100644 index 0000000000..3f433eb2ee --- /dev/null +++ b/servers/rendering/rasterizer_rd/shaders/sky.glsl @@ -0,0 +1,181 @@ +/* clang-format off */ +[vertex] + +#version 450 + +VERSION_DEFINES + +layout(location = 0) out vec2 uv_interp; +/* clang-format on */ + +layout(push_constant, binding = 1, std430) uniform Params { + mat3 orientation; + vec4 proj; + vec4 position_multiplier; + float time; +} +params; + +void main() { + + vec2 base_arr[4] = vec2[](vec2(-1.0, -1.0), vec2(-1.0, 1.0), vec2(1.0, 1.0), vec2(1.0, -1.0)); + uv_interp = base_arr[gl_VertexIndex]; + gl_Position = vec4(uv_interp, 1.0, 1.0); +} + +/* clang-format off */ +[fragment] + +#version 450 + +VERSION_DEFINES + +#define M_PI 3.14159265359 + +layout(location = 0) in vec2 uv_interp; +/* clang-format on */ + +layout(push_constant, binding = 1, std430) uniform Params { + mat3 orientation; + vec4 proj; + vec4 position_multiplier; + float time; //TODO consider adding vec2 screen res, and float radiance size +} +params; + +#define SAMPLER_NEAREST_CLAMP 0 +#define SAMPLER_LINEAR_CLAMP 1 +#define SAMPLER_NEAREST_WITH_MIPMAPS_CLAMP 2 +#define SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP 3 +#define SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_CLAMP 4 +#define SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_CLAMP 5 +#define SAMPLER_NEAREST_REPEAT 6 +#define SAMPLER_LINEAR_REPEAT 7 +#define SAMPLER_NEAREST_WITH_MIPMAPS_REPEAT 8 +#define SAMPLER_LINEAR_WITH_MIPMAPS_REPEAT 9 +#define SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_REPEAT 10 +#define SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_REPEAT 11 + +layout(set = 0, binding = 0) uniform sampler material_samplers[12]; + +#ifdef USE_MATERIAL_UNIFORMS +layout(set = 1, binding = 0, std140) uniform MaterialUniforms{ + /* clang-format off */ + +MATERIAL_UNIFORMS + + /* clang-format on */ +} material; +#endif + +layout(set = 2, binding = 0) uniform textureCube radiance; +#ifdef USE_CUBEMAP_PASS +layout(set = 2, binding = 1) uniform textureCube half_res; +layout(set = 2, binding = 2) uniform textureCube quarter_res; +#else +layout(set = 2, binding = 1) uniform texture2D half_res; +layout(set = 2, binding = 2) uniform texture2D quarter_res; +#endif + +#ifdef USE_CUBEMAP_PASS +#define AT_CUBEMAP_PASS true +#else +#define AT_CUBEMAP_PASS false +#endif + +#ifdef USE_HALF_RES_PASS +#define AT_HALF_RES_PASS true +#else +#define AT_HALF_RES_PASS false +#endif + +#ifdef USE_QUARTER_RES_PASS +#define AT_QUARTER_RES_PASS true +#else +#define AT_QUARTER_RES_PASS false +#endif + +struct DirectionalLightData { + vec3 direction; + float energy; + vec3 color; + bool enabled; +}; + +layout(set = 3, binding = 0, std140) uniform DirectionalLights { + DirectionalLightData data[MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS]; +} +directional_lights; + +/* clang-format off */ + +FRAGMENT_SHADER_GLOBALS + +/* clang-format on */ + +layout(location = 0) out vec4 frag_color; + +void main() { + + vec3 cube_normal; + cube_normal.z = -1.0; + cube_normal.x = (cube_normal.z * (-uv_interp.x - params.proj.x)) / params.proj.y; + cube_normal.y = -(cube_normal.z * (-uv_interp.y - params.proj.z)) / params.proj.w; + cube_normal = mat3(params.orientation) * cube_normal; + cube_normal.z = -cube_normal.z; + cube_normal = normalize(cube_normal); + + vec2 uv = uv_interp * 0.5 + 0.5; + + vec2 panorama_coords = vec2(atan(cube_normal.x, cube_normal.z), acos(cube_normal.y)); + + if (panorama_coords.x < 0.0) { + panorama_coords.x += M_PI * 2.0; + } + + panorama_coords /= vec2(M_PI * 2.0, M_PI); + + vec3 color = vec3(0.0, 0.0, 0.0); + float alpha = 1.0; // Only available to subpasses + vec4 half_res_color = vec4(1.0); + vec4 quarter_res_color = vec4(1.0); + +#ifdef USE_CUBEMAP_PASS + float using_cubemap = 1.0; +#ifdef USES_HALF_RES_COLOR + half_res_color = texture(samplerCube(half_res, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), cube_normal); +#endif +#ifdef USES_QUARTER_RES_COLOR + quarter_res_color = texture(samplerCube(quarter_res, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), cube_normal); +#endif +#else + float using_cubemap = 0.0; +#ifdef USES_HALF_RES_COLOR + half_res_color = textureLod(sampler2D(half_res, material_samplers[SAMPLER_LINEAR_CLAMP]), uv, 0.0); +#endif +#ifdef USES_QUARTER_RES_COLOR + quarter_res_color = textureLod(sampler2D(quarter_res, material_samplers[SAMPLER_LINEAR_CLAMP]), uv, 0.0); +#endif +#endif + +// unused, just here to make our compiler happy, make sure we don't execute any light code the user adds in.. +#ifndef REALLYINCLUDETHIS + { + /* clang-format off */ + +LIGHT_SHADER_CODE + + /* clang-format on */ + } +#endif + { + /* clang-format off */ + +FRAGMENT_SHADER_CODE + + /* clang-format on */ + } + + frag_color.rgb = color * params.position_multiplier.w; + frag_color.a = alpha; +} diff --git a/servers/rendering/rasterizer_rd/shaders/ssao.glsl b/servers/rendering/rasterizer_rd/shaders/ssao.glsl new file mode 100644 index 0000000000..c9d7134610 --- /dev/null +++ b/servers/rendering/rasterizer_rd/shaders/ssao.glsl @@ -0,0 +1,252 @@ +/* clang-format off */ +[compute] + +#version 450 + +VERSION_DEFINES + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; +/* clang-format on */ + +#define TWO_PI 6.283185307179586476925286766559 + +#ifdef SSAO_QUALITY_HIGH +#define NUM_SAMPLES (20) +#endif + +#ifdef SSAO_QUALITY_ULTRA +#define NUM_SAMPLES (48) +#endif + +#ifdef SSAO_QUALITY_LOW +#define NUM_SAMPLES (8) +#endif + +#if !defined(SSAO_QUALITY_LOW) && !defined(SSAO_QUALITY_HIGH) && !defined(SSAO_QUALITY_ULTRA) +#define NUM_SAMPLES (12) +#endif + +// If using depth mip levels, the log of the maximum pixel offset before we need to switch to a lower +// miplevel to maintain reasonable spatial locality in the cache +// If this number is too small (< 3), too many taps will land in the same pixel, and we'll get bad variance that manifests as flashing. +// If it is too high (> 5), we'll get bad performance because we're not using the MIP levels effectively +#define LOG_MAX_OFFSET (3) + +// This must be less than or equal to the MAX_MIP_LEVEL defined in SSAO.cpp +#define MAX_MIP_LEVEL (4) + +// This is the number of turns around the circle that the spiral pattern makes. This should be prime to prevent +// taps from lining up. This particular choice was tuned for NUM_SAMPLES == 9 + +const int ROTATIONS[] = int[]( + 1, 1, 2, 3, 2, 5, 2, 3, 2, + 3, 3, 5, 5, 3, 4, 7, 5, 5, 7, + 9, 8, 5, 5, 7, 7, 7, 8, 5, 8, + 11, 12, 7, 10, 13, 8, 11, 8, 7, 14, + 11, 11, 13, 12, 13, 19, 17, 13, 11, 18, + 19, 11, 11, 14, 17, 21, 15, 16, 17, 18, + 13, 17, 11, 17, 19, 18, 25, 18, 19, 19, + 29, 21, 19, 27, 31, 29, 21, 18, 17, 29, + 31, 31, 23, 18, 25, 26, 25, 23, 19, 34, + 19, 27, 21, 25, 39, 29, 17, 21, 27); +/* clang-format on */ + +//#define NUM_SPIRAL_TURNS (7) +const int NUM_SPIRAL_TURNS = ROTATIONS[NUM_SAMPLES - 1]; + +layout(set = 0, binding = 0) uniform sampler2D source_depth_mipmaps; +layout(r8, set = 1, binding = 0) uniform restrict writeonly image2D dest_image; + +#ifndef USE_HALF_SIZE +layout(set = 2, binding = 0) uniform sampler2D source_depth; +#endif + +layout(set = 3, binding = 0) uniform sampler2D source_normal; + +layout(push_constant, binding = 1, std430) uniform Params { + ivec2 screen_size; + float z_far; + float z_near; + + bool orthogonal; + float intensity_div_r6; + float radius; + float bias; + + vec4 proj_info; + vec2 pixel_size; + float proj_scale; + uint pad; +} +params; + +vec3 reconstructCSPosition(vec2 S, float z) { + if (params.orthogonal) { + return vec3((S.xy * params.proj_info.xy + params.proj_info.zw), z); + } else { + return vec3((S.xy * params.proj_info.xy + params.proj_info.zw) * z, z); + } +} + +vec3 getPosition(ivec2 ssP) { + vec3 P; +#ifdef USE_HALF_SIZE + P.z = texelFetch(source_depth_mipmaps, ssP, 0).r; + P.z = -P.z; +#else + P.z = texelFetch(source_depth, ssP, 0).r; + + P.z = P.z * 2.0 - 1.0; + if (params.orthogonal) { + P.z = ((P.z + (params.z_far + params.z_near) / (params.z_far - params.z_near)) * (params.z_far - params.z_near)) / 2.0; + } else { + P.z = 2.0 * params.z_near * params.z_far / (params.z_far + params.z_near - P.z * (params.z_far - params.z_near)); + } + P.z = -P.z; +#endif + // Offset to pixel center + P = reconstructCSPosition(vec2(ssP) + vec2(0.5), P.z); + return P; +} + +/** Returns a unit vector and a screen-space radius for the tap on a unit disk (the caller should scale by the actual disk radius) */ +vec2 tapLocation(int sampleNumber, float spinAngle, out float ssR) { + // Radius relative to ssR + float alpha = (float(sampleNumber) + 0.5) * (1.0 / float(NUM_SAMPLES)); + float angle = alpha * (float(NUM_SPIRAL_TURNS) * 6.28) + spinAngle; + + ssR = alpha; + return vec2(cos(angle), sin(angle)); +} + +/** Read the camera-space position of the point at screen-space pixel ssP + unitOffset * ssR. Assumes length(unitOffset) == 1 */ +vec3 getOffsetPosition(ivec2 ssP, float ssR) { + // Derivation: + // mipLevel = floor(log(ssR / MAX_OFFSET)); + + int mipLevel = clamp(int(floor(log2(ssR))) - LOG_MAX_OFFSET, 0, MAX_MIP_LEVEL); + + vec3 P; + + // We need to divide by 2^mipLevel to read the appropriately scaled coordinate from a MIP-map. + // Manually clamp to the texture size because texelFetch bypasses the texture unit + ivec2 mipP = clamp(ssP >> mipLevel, ivec2(0), (params.screen_size >> mipLevel) - ivec2(1)); + +#ifdef USE_HALF_SIZE + P.z = texelFetch(source_depth_mipmaps, mipP, mipLevel).r; + P.z = -P.z; +#else + if (mipLevel < 1) { + //read from depth buffer + P.z = texelFetch(source_depth, mipP, 0).r; + P.z = P.z * 2.0 - 1.0; + if (params.orthogonal) { + P.z = ((P.z + (params.z_far + params.z_near) / (params.z_far - params.z_near)) * (params.z_far - params.z_near)) / 2.0; + } else { + P.z = 2.0 * params.z_near * params.z_far / (params.z_far + params.z_near - P.z * (params.z_far - params.z_near)); + } + P.z = -P.z; + + } else { + //read from mipmaps + P.z = texelFetch(source_depth_mipmaps, mipP, mipLevel - 1).r; + P.z = -P.z; + } +#endif + + // Offset to pixel center + P = reconstructCSPosition(vec2(ssP) + vec2(0.5), P.z); + + return P; +} + +/** Compute the occlusion due to sample with index \a i about the pixel at \a ssC that corresponds + to camera-space point \a C with unit normal \a n_C, using maximum screen-space sampling radius \a ssDiskRadius + + Note that units of H() in the HPG12 paper are meters, not + unitless. The whole falloff/sampling function is therefore + unitless. In this implementation, we factor out (9 / radius). + + Four versions of the falloff function are implemented below +*/ +float sampleAO(in ivec2 ssC, in vec3 C, in vec3 n_C, in float ssDiskRadius, in float p_radius, in int tapIndex, in float randomPatternRotationAngle) { + // Offset on the unit disk, spun for this pixel + float ssR; + vec2 unitOffset = tapLocation(tapIndex, randomPatternRotationAngle, ssR); + ssR *= ssDiskRadius; + + ivec2 ssP = ivec2(ssR * unitOffset) + ssC; + + if (any(lessThan(ssP, ivec2(0))) || any(greaterThanEqual(ssP, params.screen_size))) { + return 0.0; + } + + // The occluding point in camera space + vec3 Q = getOffsetPosition(ssP, ssR); + + vec3 v = Q - C; + + float vv = dot(v, v); + float vn = dot(v, n_C); + + const float epsilon = 0.01; + float radius2 = p_radius * p_radius; + + // A: From the HPG12 paper + // Note large epsilon to avoid overdarkening within cracks + //return float(vv < radius2) * max((vn - bias) / (epsilon + vv), 0.0) * radius2 * 0.6; + + // B: Smoother transition to zero (lowers contrast, smoothing out corners). [Recommended] + float f = max(radius2 - vv, 0.0); + return f * f * f * max((vn - params.bias) / (epsilon + vv), 0.0); + + // C: Medium contrast (which looks better at high radii), no division. Note that the + // contribution still falls off with radius^2, but we've adjusted the rate in a way that is + // more computationally efficient and happens to be aesthetically pleasing. + // return 4.0 * max(1.0 - vv * invRadius2, 0.0) * max(vn - bias, 0.0); + + // D: Low contrast, no division operation + // return 2.0 * float(vv < radius * radius) * max(vn - bias, 0.0); +} + +void main() { + // Pixel being shaded + ivec2 ssC = ivec2(gl_GlobalInvocationID.xy); + if (any(greaterThan(ssC, params.screen_size))) { //too large, do nothing + return; + } + + // World space point being shaded + vec3 C = getPosition(ssC); + +#ifdef USE_HALF_SIZE + vec3 n_C = texelFetch(source_normal, ssC << 1, 0).xyz * 2.0 - 1.0; +#else + vec3 n_C = texelFetch(source_normal, ssC, 0).xyz * 2.0 - 1.0; +#endif + n_C = normalize(n_C); + n_C.y = -n_C.y; //because this code reads flipped + + // Hash function used in the HPG12 AlchemyAO paper + float randomPatternRotationAngle = mod(float((3 * ssC.x ^ ssC.y + ssC.x * ssC.y) * 10), TWO_PI); + + // Reconstruct normals from positions. These will lead to 1-pixel black lines + // at depth discontinuities, however the blur will wipe those out so they are not visible + // in the final image. + + // Choose the screen-space sample radius + // proportional to the projected area of the sphere + + float ssDiskRadius = -params.proj_scale * params.radius; + if (!params.orthogonal) { + ssDiskRadius = -params.proj_scale * params.radius / C.z; + } + float sum = 0.0; + for (int i = 0; i < NUM_SAMPLES; ++i) { + sum += sampleAO(ssC, C, n_C, ssDiskRadius, params.radius, i, randomPatternRotationAngle); + } + + float A = max(0.0, 1.0 - sum * params.intensity_div_r6 * (5.0 / float(NUM_SAMPLES))); + + imageStore(dest_image, ssC, vec4(A)); +} diff --git a/servers/rendering/rasterizer_rd/shaders/ssao_blur.glsl b/servers/rendering/rasterizer_rd/shaders/ssao_blur.glsl new file mode 100644 index 0000000000..e90c788e08 --- /dev/null +++ b/servers/rendering/rasterizer_rd/shaders/ssao_blur.glsl @@ -0,0 +1,157 @@ +/* clang-format off */ +[compute] + +#version 450 + +VERSION_DEFINES + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; +/* clang-format on */ + +layout(set = 0, binding = 0) uniform sampler2D source_ssao; +layout(set = 1, binding = 0) uniform sampler2D source_depth; +#ifdef MODE_UPSCALE +layout(set = 2, binding = 0) uniform sampler2D source_depth_mipmaps; +#endif + +layout(r8, set = 3, binding = 0) uniform restrict writeonly image2D dest_image; + +////////////////////////////////////////////////////////////////////////////////////////////// +// Tunable Parameters: + +layout(push_constant, binding = 1, std430) uniform Params { + float edge_sharpness; /** Increase to make depth edges crisper. Decrease to reduce flicker. */ + int filter_scale; + float z_far; + float z_near; + bool orthogonal; + uint pad0; + uint pad1; + uint pad2; + ivec2 axis; /** (1, 0) or (0, 1) */ + ivec2 screen_size; +} +params; + +/** Filter radius in pixels. This will be multiplied by SCALE. */ +#define R (4) + +////////////////////////////////////////////////////////////////////////////////////////////// + +// Gaussian coefficients +const float gaussian[R + 1] = + //float[](0.356642, 0.239400, 0.072410, 0.009869); + //float[](0.398943, 0.241971, 0.053991, 0.004432, 0.000134); // stddev = 1.0 + float[](0.153170, 0.144893, 0.122649, 0.092902, 0.062970); // stddev = 2.0 +//float[](0.111220, 0.107798, 0.098151, 0.083953, 0.067458, 0.050920, 0.036108); // stddev = 3.0 + +void main() { + + // Pixel being shaded + ivec2 ssC = ivec2(gl_GlobalInvocationID.xy); + if (any(greaterThan(ssC, params.screen_size))) { //too large, do nothing + return; + } + +#ifdef MODE_UPSCALE + + //closest one should be the same pixel, but check nearby just in case + float depth = texelFetch(source_depth, ssC, 0).r; + + depth = depth * 2.0 - 1.0; + if (params.orthogonal) { + depth = ((depth + (params.z_far + params.z_near) / (params.z_far - params.z_near)) * (params.z_far - params.z_near)) / 2.0; + } else { + depth = 2.0 * params.z_near * params.z_far / (params.z_far + params.z_near - depth * (params.z_far - params.z_near)); + } + + vec2 pixel_size = 1.0 / vec2(params.screen_size); + vec2 closest_uv = vec2(ssC) * pixel_size + pixel_size * 0.5; + vec2 from_uv = closest_uv; + vec2 ps2 = pixel_size; // * 2.0; + + float closest_depth = abs(textureLod(source_depth_mipmaps, closest_uv, 0.0).r - depth); + + vec2 offsets[4] = vec2[](vec2(ps2.x, 0), vec2(-ps2.x, 0), vec2(0, ps2.y), vec2(0, -ps2.y)); + for (int i = 0; i < 4; i++) { + vec2 neighbour = from_uv + offsets[i]; + float neighbour_depth = abs(textureLod(source_depth_mipmaps, neighbour, 0.0).r - depth); + if (neighbour_depth < closest_depth) { + closest_uv = neighbour; + closest_depth = neighbour_depth; + } + } + + float visibility = textureLod(source_ssao, closest_uv, 0.0).r; + imageStore(dest_image, ssC, vec4(visibility)); +#else + + float depth = texelFetch(source_depth, ssC, 0).r; + +#ifdef MODE_FULL_SIZE + depth = depth * 2.0 - 1.0; + + if (params.orthogonal) { + depth = ((depth + (params.z_far + params.z_near) / (params.z_far - params.z_near)) * (params.z_far - params.z_near)) / 2.0; + } else { + depth = 2.0 * params.z_near * params.z_far / (params.z_far + params.z_near - depth * (params.z_far - params.z_near)); + } + +#endif + float depth_divide = 1.0 / params.z_far; + + //depth *= depth_divide; + + /* + if (depth > params.z_far * 0.999) { + discard; //skybox + } + */ + + float sum = texelFetch(source_ssao, ssC, 0).r; + + // Base weight for depth falloff. Increase this for more blurriness, + // decrease it for better edge discrimination + float BASE = gaussian[0]; + float totalWeight = BASE; + sum *= totalWeight; + + ivec2 clamp_limit = params.screen_size - ivec2(1); + + for (int r = -R; r <= R; ++r) { + // We already handled the zero case above. This loop should be unrolled and the static branch optimized out, + // so the IF statement has no runtime cost + if (r != 0) { + + ivec2 ppos = ssC + params.axis * (r * params.filter_scale); + float value = texelFetch(source_ssao, clamp(ppos, ivec2(0), clamp_limit), 0).r; + ivec2 rpos = clamp(ppos, ivec2(0), clamp_limit); + + float temp_depth = texelFetch(source_depth, rpos, 0).r; +#ifdef MODE_FULL_SIZE + temp_depth = temp_depth * 2.0 - 1.0; + if (params.orthogonal) { + temp_depth = ((temp_depth + (params.z_far + params.z_near) / (params.z_far - params.z_near)) * (params.z_far - params.z_near)) / 2.0; + } else { + temp_depth = 2.0 * params.z_near * params.z_far / (params.z_far + params.z_near - temp_depth * (params.z_far - params.z_near)); + } + //temp_depth *= depth_divide; +#endif + // spatial domain: offset gaussian tap + float weight = 0.3 + gaussian[abs(r)]; + //weight *= max(0.0, dot(temp_normal, normal)); + + // range domain (the "bilateral" weight). As depth difference increases, decrease weight. + weight *= max(0.0, 1.0 - params.edge_sharpness * abs(temp_depth - depth)); + + sum += value * weight; + totalWeight += weight; + } + } + + const float epsilon = 0.0001; + float visibility = sum / (totalWeight + epsilon); + + imageStore(dest_image, ssC, vec4(visibility)); +#endif +} diff --git a/servers/rendering/rasterizer_rd/shaders/ssao_minify.glsl b/servers/rendering/rasterizer_rd/shaders/ssao_minify.glsl new file mode 100644 index 0000000000..8728154347 --- /dev/null +++ b/servers/rendering/rasterizer_rd/shaders/ssao_minify.glsl @@ -0,0 +1,48 @@ +/* clang-format off */ +[compute] + +#version 450 + +VERSION_DEFINES + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; +/* clang-format on */ + +layout(push_constant, binding = 1, std430) uniform Params { + vec2 pixel_size; + float z_far; + float z_near; + ivec2 source_size; + bool orthogonal; + uint pad; +} +params; + +#ifdef MINIFY_START +layout(set = 0, binding = 0) uniform sampler2D source_texture; +#else +layout(r32f, set = 0, binding = 0) uniform restrict readonly image2D source_image; +#endif +layout(r32f, set = 1, binding = 0) uniform restrict writeonly image2D dest_image; + +void main() { + + ivec2 pos = ivec2(gl_GlobalInvocationID.xy); + + if (any(greaterThan(pos, params.source_size >> 1))) { //too large, do nothing + return; + } + +#ifdef MINIFY_START + float depth = texelFetch(source_texture, pos << 1, 0).r * 2.0 - 1.0; + if (params.orthogonal) { + depth = ((depth + (params.z_far + params.z_near) / (params.z_far - params.z_near)) * (params.z_far - params.z_near)) / 2.0; + } else { + depth = 2.0 * params.z_near * params.z_far / (params.z_far + params.z_near - depth * (params.z_far - params.z_near)); + } +#else + float depth = imageLoad(source_image, pos << 1).r; +#endif + + imageStore(dest_image, pos, vec4(depth)); +} diff --git a/servers/rendering/rasterizer_rd/shaders/tonemap.glsl b/servers/rendering/rasterizer_rd/shaders/tonemap.glsl new file mode 100644 index 0000000000..524ca5e2ea --- /dev/null +++ b/servers/rendering/rasterizer_rd/shaders/tonemap.glsl @@ -0,0 +1,305 @@ +/* clang-format off */ +[vertex] + +#version 450 + +VERSION_DEFINES + +layout(location = 0) out vec2 uv_interp; +/* clang-format on */ + +void main() { + + vec2 base_arr[4] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0)); + uv_interp = base_arr[gl_VertexIndex]; + gl_Position = vec4(uv_interp * 2.0 - 1.0, 0.0, 1.0); +} + +/* clang-format off */ +[fragment] + +#version 450 + +VERSION_DEFINES + +layout(location = 0) in vec2 uv_interp; +/* clang-format on */ + +layout(set = 0, binding = 0) uniform sampler2D source_color; +layout(set = 1, binding = 0) uniform sampler2D source_auto_exposure; +layout(set = 2, binding = 0) uniform sampler2D source_glow; +layout(set = 3, binding = 0) uniform sampler3D color_correction; + +layout(push_constant, binding = 1, std430) uniform Params { + vec3 bcs; + bool use_bcs; + + bool use_glow; + bool use_auto_exposure; + bool use_color_correction; + uint tonemapper; + + uvec2 glow_texture_size; + + float glow_intensity; + uint glow_level_flags; + uint glow_mode; + + float exposure; + float white; + float auto_exposure_grey; +} +params; + +layout(location = 0) out vec4 frag_color; + +#ifdef USE_GLOW_FILTER_BICUBIC +// w0, w1, w2, and w3 are the four cubic B-spline basis functions +float w0(float a) { + return (1.0f / 6.0f) * (a * (a * (-a + 3.0f) - 3.0f) + 1.0f); +} + +float w1(float a) { + return (1.0f / 6.0f) * (a * a * (3.0f * a - 6.0f) + 4.0f); +} + +float w2(float a) { + return (1.0f / 6.0f) * (a * (a * (-3.0f * a + 3.0f) + 3.0f) + 1.0f); +} + +float w3(float a) { + return (1.0f / 6.0f) * (a * a * a); +} + +// g0 and g1 are the two amplitude functions +float g0(float a) { + return w0(a) + w1(a); +} + +float g1(float a) { + return w2(a) + w3(a); +} + +// h0 and h1 are the two offset functions +float h0(float a) { + return -1.0f + w1(a) / (w0(a) + w1(a)); +} + +float h1(float a) { + return 1.0f + w3(a) / (w2(a) + w3(a)); +} + +vec4 texture2D_bicubic(sampler2D tex, vec2 uv, int p_lod) { + float lod = float(p_lod); + vec2 tex_size = vec2(params.glow_texture_size >> p_lod); + vec2 pixel_size = vec2(1.0f) / tex_size; + + uv = uv * tex_size + vec2(0.5f); + + vec2 iuv = floor(uv); + vec2 fuv = fract(uv); + + float g0x = g0(fuv.x); + float g1x = g1(fuv.x); + float h0x = h0(fuv.x); + float h1x = h1(fuv.x); + float h0y = h0(fuv.y); + float h1y = h1(fuv.y); + + vec2 p0 = (vec2(iuv.x + h0x, iuv.y + h0y) - vec2(0.5f)) * pixel_size; + vec2 p1 = (vec2(iuv.x + h1x, iuv.y + h0y) - vec2(0.5f)) * pixel_size; + vec2 p2 = (vec2(iuv.x + h0x, iuv.y + h1y) - vec2(0.5f)) * pixel_size; + vec2 p3 = (vec2(iuv.x + h1x, iuv.y + h1y) - vec2(0.5f)) * pixel_size; + + return (g0(fuv.y) * (g0x * textureLod(tex, p0, lod) + g1x * textureLod(tex, p1, lod))) + + (g1(fuv.y) * (g0x * textureLod(tex, p2, lod) + g1x * textureLod(tex, p3, lod))); +} + +#define GLOW_TEXTURE_SAMPLE(m_tex, m_uv, m_lod) texture2D_bicubic(m_tex, m_uv, m_lod) + +#else + +#define GLOW_TEXTURE_SAMPLE(m_tex, m_uv, m_lod) textureLod(m_tex, m_uv, float(m_lod)) + +#endif + +vec3 tonemap_filmic(vec3 color, float white) { + // exposure bias: input scale (color *= bias, white *= bias) to make the brightness consistent with other tonemappers + // also useful to scale the input to the range that the tonemapper is designed for (some require very high input values) + // has no effect on the curve's general shape or visual properties + const float exposure_bias = 2.0f; + const float A = 0.22f * exposure_bias * exposure_bias; // bias baked into constants for performance + const float B = 0.30f * exposure_bias; + const float C = 0.10f; + const float D = 0.20f; + const float E = 0.01f; + const float F = 0.30f; + + vec3 color_tonemapped = ((color * (A * color + C * B) + D * E) / (color * (A * color + B) + D * F)) - E / F; + float white_tonemapped = ((white * (A * white + C * B) + D * E) / (white * (A * white + B) + D * F)) - E / F; + + return color_tonemapped / white_tonemapped; +} + +vec3 tonemap_aces(vec3 color, float white) { + const float exposure_bias = 0.85f; + const float A = 2.51f * exposure_bias * exposure_bias; + const float B = 0.03f * exposure_bias; + const float C = 2.43f * exposure_bias * exposure_bias; + const float D = 0.59f * exposure_bias; + const float E = 0.14f; + + vec3 color_tonemapped = (color * (A * color + B)) / (color * (C * color + D) + E); + float white_tonemapped = (white * (A * white + B)) / (white * (C * white + D) + E); + + return color_tonemapped / white_tonemapped; +} + +vec3 tonemap_reinhard(vec3 color, float white) { + return (white * color + color) / (color * white + white); +} + +vec3 linear_to_srgb(vec3 color) { + //if going to srgb, clamp from 0 to 1. + color = clamp(color, vec3(0.0), vec3(1.0)); + const vec3 a = vec3(0.055f); + return mix((vec3(1.0f) + a) * pow(color.rgb, vec3(1.0f / 2.4f)) - a, 12.92f * color.rgb, lessThan(color.rgb, vec3(0.0031308f))); +} + +#define TONEMAPPER_LINEAR 0 +#define TONEMAPPER_REINHARD 1 +#define TONEMAPPER_FILMIC 2 +#define TONEMAPPER_ACES 3 + +vec3 apply_tonemapping(vec3 color, float white) { // inputs are LINEAR, always outputs clamped [0;1] color + + if (params.tonemapper == TONEMAPPER_LINEAR) { + return color; + } else if (params.tonemapper == TONEMAPPER_REINHARD) { + return tonemap_reinhard(color, white); + } else if (params.tonemapper == TONEMAPPER_FILMIC) { + return tonemap_filmic(color, white); + } else { //aces + return tonemap_aces(color, white); + } +} + +vec3 gather_glow(sampler2D tex, vec2 uv) { // sample all selected glow levels + vec3 glow = vec3(0.0f); + + if (bool(params.glow_level_flags & (1 << 0))) { + glow += GLOW_TEXTURE_SAMPLE(tex, uv, 0).rgb; + } + + if (bool(params.glow_level_flags & (1 << 1))) { + glow += GLOW_TEXTURE_SAMPLE(tex, uv, 1).rgb; + } + + if (bool(params.glow_level_flags & (1 << 2))) { + glow += GLOW_TEXTURE_SAMPLE(tex, uv, 2).rgb; + } + + if (bool(params.glow_level_flags & (1 << 3))) { + glow += GLOW_TEXTURE_SAMPLE(tex, uv, 3).rgb; + } + + if (bool(params.glow_level_flags & (1 << 4))) { + glow += GLOW_TEXTURE_SAMPLE(tex, uv, 4).rgb; + } + + if (bool(params.glow_level_flags & (1 << 5))) { + glow += GLOW_TEXTURE_SAMPLE(tex, uv, 5).rgb; + } + + if (bool(params.glow_level_flags & (1 << 6))) { + glow += GLOW_TEXTURE_SAMPLE(tex, uv, 6).rgb; + } + + return glow; +} + +#define GLOW_MODE_ADD 0 +#define GLOW_MODE_SCREEN 1 +#define GLOW_MODE_SOFTLIGHT 2 +#define GLOW_MODE_REPLACE 3 +#define GLOW_MODE_MIX 4 + +vec3 apply_glow(vec3 color, vec3 glow) { // apply glow using the selected blending mode + if (params.glow_mode == GLOW_MODE_ADD) { + return color + glow; + } else if (params.glow_mode == GLOW_MODE_SCREEN) { + //need color clamping + return max((color + glow) - (color * glow), vec3(0.0)); + } else if (params.glow_mode == GLOW_MODE_SOFTLIGHT) { + //need color clamping + glow = glow * vec3(0.5f) + vec3(0.5f); + + color.r = (glow.r <= 0.5f) ? (color.r - (1.0f - 2.0f * glow.r) * color.r * (1.0f - color.r)) : (((glow.r > 0.5f) && (color.r <= 0.25f)) ? (color.r + (2.0f * glow.r - 1.0f) * (4.0f * color.r * (4.0f * color.r + 1.0f) * (color.r - 1.0f) + 7.0f * color.r)) : (color.r + (2.0f * glow.r - 1.0f) * (sqrt(color.r) - color.r))); + color.g = (glow.g <= 0.5f) ? (color.g - (1.0f - 2.0f * glow.g) * color.g * (1.0f - color.g)) : (((glow.g > 0.5f) && (color.g <= 0.25f)) ? (color.g + (2.0f * glow.g - 1.0f) * (4.0f * color.g * (4.0f * color.g + 1.0f) * (color.g - 1.0f) + 7.0f * color.g)) : (color.g + (2.0f * glow.g - 1.0f) * (sqrt(color.g) - color.g))); + color.b = (glow.b <= 0.5f) ? (color.b - (1.0f - 2.0f * glow.b) * color.b * (1.0f - color.b)) : (((glow.b > 0.5f) && (color.b <= 0.25f)) ? (color.b + (2.0f * glow.b - 1.0f) * (4.0f * color.b * (4.0f * color.b + 1.0f) * (color.b - 1.0f) + 7.0f * color.b)) : (color.b + (2.0f * glow.b - 1.0f) * (sqrt(color.b) - color.b))); + return color; + } else { //replace + return glow; + } +} + +vec3 apply_bcs(vec3 color, vec3 bcs) { + color = mix(vec3(0.0f), color, bcs.x); + color = mix(vec3(0.5f), color, bcs.y); + color = mix(vec3(dot(vec3(1.0f), color) * 0.33333f), color, bcs.z); + + return color; +} + +vec3 apply_color_correction(vec3 color, sampler3D correction_tex) { + return texture(correction_tex, color).rgb; +} + +void main() { + vec3 color = textureLod(source_color, uv_interp, 0.0f).rgb; + + // Exposure + + if (params.use_auto_exposure) { + color /= texelFetch(source_auto_exposure, ivec2(0, 0), 0).r / params.auto_exposure_grey; + } + + color *= params.exposure; + + // Early Tonemap & SRGB Conversion + + if (params.use_glow && params.glow_mode == GLOW_MODE_MIX) { + + vec3 glow = gather_glow(source_glow, uv_interp); + color.rgb = mix(color.rgb, glow, params.glow_intensity); + } + + color = apply_tonemapping(color, params.white); + + color = linear_to_srgb(color); // regular linear -> SRGB conversion + + // Glow + + if (params.use_glow && params.glow_mode != GLOW_MODE_MIX) { + + vec3 glow = gather_glow(source_glow, uv_interp) * params.glow_intensity; + + // high dynamic range -> SRGB + glow = apply_tonemapping(glow, params.white); + glow = linear_to_srgb(glow); + + color = apply_glow(color, glow); + } + + // Additional effects + + if (params.use_bcs) { + color = apply_bcs(color, params.bcs); + } + + if (params.use_color_correction) { + color = apply_color_correction(color, color_correction); + } + + frag_color = vec4(color, 1.0f); +} diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp new file mode 100644 index 0000000000..3c1795161d --- /dev/null +++ b/servers/rendering/rendering_device.cpp @@ -0,0 +1,64 @@ +/*************************************************************************/ +/* rendering_device.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "rendering_device.h" + +RenderingDevice *RenderingDevice::singleton = NULL; + +RenderingDevice *RenderingDevice::get_singleton() { + return singleton; +} + +RenderingDevice::ShaderCompileFunction RenderingDevice::compile_function = NULL; +RenderingDevice::ShaderCacheFunction RenderingDevice::cache_function = NULL; + +void RenderingDevice::shader_set_compile_function(ShaderCompileFunction p_function) { + compile_function = p_function; +} +void RenderingDevice::shader_set_cache_function(ShaderCacheFunction p_function) { + cache_function = p_function; +} + +Vector<uint8_t> RenderingDevice::shader_compile_from_source(ShaderStage p_stage, const String &p_source_code, ShaderLanguage p_language, String *r_error, bool p_allow_cache) { + if (p_allow_cache && cache_function) { + Vector<uint8_t> cache = cache_function(p_stage, p_source_code, p_language); + if (cache.size()) { + return cache; + } + } + + ERR_FAIL_COND_V(!compile_function, Vector<uint8_t>()); + + return compile_function(p_stage, p_source_code, p_language, r_error); +} + +RenderingDevice::RenderingDevice() { + singleton = this; +} diff --git a/servers/rendering/rendering_device.h b/servers/rendering/rendering_device.h new file mode 100644 index 0000000000..4705bcaa75 --- /dev/null +++ b/servers/rendering/rendering_device.h @@ -0,0 +1,1032 @@ +/*************************************************************************/ +/* rendering_device.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef RENDERING_DEVICE_H +#define RENDERING_DEVICE_H + +#include "core/object.h" +#include "servers/display_server.h" + +class RenderingDevice : public Object { + GDCLASS(RenderingDevice, Object) +public: + enum ShaderStage { + SHADER_STAGE_VERTEX, + SHADER_STAGE_FRAGMENT, + SHADER_STAGE_TESSELATION_CONTROL, + SHADER_STAGE_TESSELATION_EVALUATION, + SHADER_STAGE_COMPUTE, + SHADER_STAGE_MAX, + SHADER_STAGE_VERTEX_BIT = (1 << SHADER_STAGE_VERTEX), + SHADER_STAGE_FRAGMENT_BIT = (1 << SHADER_STAGE_FRAGMENT), + SHADER_STAGE_TESSELATION_CONTROL_BIT = (1 << SHADER_STAGE_TESSELATION_CONTROL), + SHADER_STAGE_TESSELATION_EVALUATION_BIT = (1 << SHADER_STAGE_TESSELATION_EVALUATION), + SHADER_STAGE_COMPUTE_BIT = (1 << SHADER_STAGE_COMPUTE), + }; + + enum ShaderLanguage { + SHADER_LANGUAGE_GLSL, + SHADER_LANGUAGE_HLSL + }; + + typedef Vector<uint8_t> (*ShaderCompileFunction)(ShaderStage p_stage, const String &p_source_code, ShaderLanguage p_language, String *r_error); + typedef Vector<uint8_t> (*ShaderCacheFunction)(ShaderStage p_stage, const String &p_source_code, ShaderLanguage p_language); + +private: + static ShaderCompileFunction compile_function; + static ShaderCacheFunction cache_function; + + static RenderingDevice *singleton; + +public: + //base numeric ID for all types + enum { + INVALID_ID = -1 + }; + + /*****************/ + /**** GENERIC ****/ + /*****************/ + + enum CompareOperator { + COMPARE_OP_NEVER, + COMPARE_OP_LESS, + COMPARE_OP_EQUAL, + COMPARE_OP_LESS_OR_EQUAL, + COMPARE_OP_GREATER, + COMPARE_OP_NOT_EQUAL, + COMPARE_OP_GREATER_OR_EQUAL, + COMPARE_OP_ALWAYS, + COMPARE_OP_MAX //not an actual operator, just the amount of operators :D + }; + + enum DataFormat { + DATA_FORMAT_R4G4_UNORM_PACK8, + DATA_FORMAT_R4G4B4A4_UNORM_PACK16, + DATA_FORMAT_B4G4R4A4_UNORM_PACK16, + DATA_FORMAT_R5G6B5_UNORM_PACK16, + DATA_FORMAT_B5G6R5_UNORM_PACK16, + DATA_FORMAT_R5G5B5A1_UNORM_PACK16, + DATA_FORMAT_B5G5R5A1_UNORM_PACK16, + DATA_FORMAT_A1R5G5B5_UNORM_PACK16, + DATA_FORMAT_R8_UNORM, + DATA_FORMAT_R8_SNORM, + DATA_FORMAT_R8_USCALED, + DATA_FORMAT_R8_SSCALED, + DATA_FORMAT_R8_UINT, + DATA_FORMAT_R8_SINT, + DATA_FORMAT_R8_SRGB, + DATA_FORMAT_R8G8_UNORM, + DATA_FORMAT_R8G8_SNORM, + DATA_FORMAT_R8G8_USCALED, + DATA_FORMAT_R8G8_SSCALED, + DATA_FORMAT_R8G8_UINT, + DATA_FORMAT_R8G8_SINT, + DATA_FORMAT_R8G8_SRGB, + DATA_FORMAT_R8G8B8_UNORM, + DATA_FORMAT_R8G8B8_SNORM, + DATA_FORMAT_R8G8B8_USCALED, + DATA_FORMAT_R8G8B8_SSCALED, + DATA_FORMAT_R8G8B8_UINT, + DATA_FORMAT_R8G8B8_SINT, + DATA_FORMAT_R8G8B8_SRGB, + DATA_FORMAT_B8G8R8_UNORM, + DATA_FORMAT_B8G8R8_SNORM, + DATA_FORMAT_B8G8R8_USCALED, + DATA_FORMAT_B8G8R8_SSCALED, + DATA_FORMAT_B8G8R8_UINT, + DATA_FORMAT_B8G8R8_SINT, + DATA_FORMAT_B8G8R8_SRGB, + DATA_FORMAT_R8G8B8A8_UNORM, + DATA_FORMAT_R8G8B8A8_SNORM, + DATA_FORMAT_R8G8B8A8_USCALED, + DATA_FORMAT_R8G8B8A8_SSCALED, + DATA_FORMAT_R8G8B8A8_UINT, + DATA_FORMAT_R8G8B8A8_SINT, + DATA_FORMAT_R8G8B8A8_SRGB, + DATA_FORMAT_B8G8R8A8_UNORM, + DATA_FORMAT_B8G8R8A8_SNORM, + DATA_FORMAT_B8G8R8A8_USCALED, + DATA_FORMAT_B8G8R8A8_SSCALED, + DATA_FORMAT_B8G8R8A8_UINT, + DATA_FORMAT_B8G8R8A8_SINT, + DATA_FORMAT_B8G8R8A8_SRGB, + DATA_FORMAT_A8B8G8R8_UNORM_PACK32, + DATA_FORMAT_A8B8G8R8_SNORM_PACK32, + DATA_FORMAT_A8B8G8R8_USCALED_PACK32, + DATA_FORMAT_A8B8G8R8_SSCALED_PACK32, + DATA_FORMAT_A8B8G8R8_UINT_PACK32, + DATA_FORMAT_A8B8G8R8_SINT_PACK32, + DATA_FORMAT_A8B8G8R8_SRGB_PACK32, + DATA_FORMAT_A2R10G10B10_UNORM_PACK32, + DATA_FORMAT_A2R10G10B10_SNORM_PACK32, + DATA_FORMAT_A2R10G10B10_USCALED_PACK32, + DATA_FORMAT_A2R10G10B10_SSCALED_PACK32, + DATA_FORMAT_A2R10G10B10_UINT_PACK32, + DATA_FORMAT_A2R10G10B10_SINT_PACK32, + DATA_FORMAT_A2B10G10R10_UNORM_PACK32, + DATA_FORMAT_A2B10G10R10_SNORM_PACK32, + DATA_FORMAT_A2B10G10R10_USCALED_PACK32, + DATA_FORMAT_A2B10G10R10_SSCALED_PACK32, + DATA_FORMAT_A2B10G10R10_UINT_PACK32, + DATA_FORMAT_A2B10G10R10_SINT_PACK32, + DATA_FORMAT_R16_UNORM, + DATA_FORMAT_R16_SNORM, + DATA_FORMAT_R16_USCALED, + DATA_FORMAT_R16_SSCALED, + DATA_FORMAT_R16_UINT, + DATA_FORMAT_R16_SINT, + DATA_FORMAT_R16_SFLOAT, + DATA_FORMAT_R16G16_UNORM, + DATA_FORMAT_R16G16_SNORM, + DATA_FORMAT_R16G16_USCALED, + DATA_FORMAT_R16G16_SSCALED, + DATA_FORMAT_R16G16_UINT, + DATA_FORMAT_R16G16_SINT, + DATA_FORMAT_R16G16_SFLOAT, + DATA_FORMAT_R16G16B16_UNORM, + DATA_FORMAT_R16G16B16_SNORM, + DATA_FORMAT_R16G16B16_USCALED, + DATA_FORMAT_R16G16B16_SSCALED, + DATA_FORMAT_R16G16B16_UINT, + DATA_FORMAT_R16G16B16_SINT, + DATA_FORMAT_R16G16B16_SFLOAT, + DATA_FORMAT_R16G16B16A16_UNORM, + DATA_FORMAT_R16G16B16A16_SNORM, + DATA_FORMAT_R16G16B16A16_USCALED, + DATA_FORMAT_R16G16B16A16_SSCALED, + DATA_FORMAT_R16G16B16A16_UINT, + DATA_FORMAT_R16G16B16A16_SINT, + DATA_FORMAT_R16G16B16A16_SFLOAT, + DATA_FORMAT_R32_UINT, + DATA_FORMAT_R32_SINT, + DATA_FORMAT_R32_SFLOAT, + DATA_FORMAT_R32G32_UINT, + DATA_FORMAT_R32G32_SINT, + DATA_FORMAT_R32G32_SFLOAT, + DATA_FORMAT_R32G32B32_UINT, + DATA_FORMAT_R32G32B32_SINT, + DATA_FORMAT_R32G32B32_SFLOAT, + DATA_FORMAT_R32G32B32A32_UINT, + DATA_FORMAT_R32G32B32A32_SINT, + DATA_FORMAT_R32G32B32A32_SFLOAT, + DATA_FORMAT_R64_UINT, + DATA_FORMAT_R64_SINT, + DATA_FORMAT_R64_SFLOAT, + DATA_FORMAT_R64G64_UINT, + DATA_FORMAT_R64G64_SINT, + DATA_FORMAT_R64G64_SFLOAT, + DATA_FORMAT_R64G64B64_UINT, + DATA_FORMAT_R64G64B64_SINT, + DATA_FORMAT_R64G64B64_SFLOAT, + DATA_FORMAT_R64G64B64A64_UINT, + DATA_FORMAT_R64G64B64A64_SINT, + DATA_FORMAT_R64G64B64A64_SFLOAT, + DATA_FORMAT_B10G11R11_UFLOAT_PACK32, + DATA_FORMAT_E5B9G9R9_UFLOAT_PACK32, + DATA_FORMAT_D16_UNORM, + DATA_FORMAT_X8_D24_UNORM_PACK32, + DATA_FORMAT_D32_SFLOAT, + DATA_FORMAT_S8_UINT, + DATA_FORMAT_D16_UNORM_S8_UINT, + DATA_FORMAT_D24_UNORM_S8_UINT, + DATA_FORMAT_D32_SFLOAT_S8_UINT, + DATA_FORMAT_BC1_RGB_UNORM_BLOCK, + DATA_FORMAT_BC1_RGB_SRGB_BLOCK, + DATA_FORMAT_BC1_RGBA_UNORM_BLOCK, + DATA_FORMAT_BC1_RGBA_SRGB_BLOCK, + DATA_FORMAT_BC2_UNORM_BLOCK, + DATA_FORMAT_BC2_SRGB_BLOCK, + DATA_FORMAT_BC3_UNORM_BLOCK, + DATA_FORMAT_BC3_SRGB_BLOCK, + DATA_FORMAT_BC4_UNORM_BLOCK, + DATA_FORMAT_BC4_SNORM_BLOCK, + DATA_FORMAT_BC5_UNORM_BLOCK, + DATA_FORMAT_BC5_SNORM_BLOCK, + DATA_FORMAT_BC6H_UFLOAT_BLOCK, + DATA_FORMAT_BC6H_SFLOAT_BLOCK, + DATA_FORMAT_BC7_UNORM_BLOCK, + DATA_FORMAT_BC7_SRGB_BLOCK, + DATA_FORMAT_ETC2_R8G8B8_UNORM_BLOCK, + DATA_FORMAT_ETC2_R8G8B8_SRGB_BLOCK, + DATA_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK, + DATA_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK, + DATA_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK, + DATA_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK, + DATA_FORMAT_EAC_R11_UNORM_BLOCK, + DATA_FORMAT_EAC_R11_SNORM_BLOCK, + DATA_FORMAT_EAC_R11G11_UNORM_BLOCK, + DATA_FORMAT_EAC_R11G11_SNORM_BLOCK, + DATA_FORMAT_ASTC_4x4_UNORM_BLOCK, + DATA_FORMAT_ASTC_4x4_SRGB_BLOCK, + DATA_FORMAT_ASTC_5x4_UNORM_BLOCK, + DATA_FORMAT_ASTC_5x4_SRGB_BLOCK, + DATA_FORMAT_ASTC_5x5_UNORM_BLOCK, + DATA_FORMAT_ASTC_5x5_SRGB_BLOCK, + DATA_FORMAT_ASTC_6x5_UNORM_BLOCK, + DATA_FORMAT_ASTC_6x5_SRGB_BLOCK, + DATA_FORMAT_ASTC_6x6_UNORM_BLOCK, + DATA_FORMAT_ASTC_6x6_SRGB_BLOCK, + DATA_FORMAT_ASTC_8x5_UNORM_BLOCK, + DATA_FORMAT_ASTC_8x5_SRGB_BLOCK, + DATA_FORMAT_ASTC_8x6_UNORM_BLOCK, + DATA_FORMAT_ASTC_8x6_SRGB_BLOCK, + DATA_FORMAT_ASTC_8x8_UNORM_BLOCK, + DATA_FORMAT_ASTC_8x8_SRGB_BLOCK, + DATA_FORMAT_ASTC_10x5_UNORM_BLOCK, + DATA_FORMAT_ASTC_10x5_SRGB_BLOCK, + DATA_FORMAT_ASTC_10x6_UNORM_BLOCK, + DATA_FORMAT_ASTC_10x6_SRGB_BLOCK, + DATA_FORMAT_ASTC_10x8_UNORM_BLOCK, + DATA_FORMAT_ASTC_10x8_SRGB_BLOCK, + DATA_FORMAT_ASTC_10x10_UNORM_BLOCK, + DATA_FORMAT_ASTC_10x10_SRGB_BLOCK, + DATA_FORMAT_ASTC_12x10_UNORM_BLOCK, + DATA_FORMAT_ASTC_12x10_SRGB_BLOCK, + DATA_FORMAT_ASTC_12x12_UNORM_BLOCK, + DATA_FORMAT_ASTC_12x12_SRGB_BLOCK, + DATA_FORMAT_G8B8G8R8_422_UNORM, + DATA_FORMAT_B8G8R8G8_422_UNORM, + DATA_FORMAT_G8_B8_R8_3PLANE_420_UNORM, + DATA_FORMAT_G8_B8R8_2PLANE_420_UNORM, + DATA_FORMAT_G8_B8_R8_3PLANE_422_UNORM, + DATA_FORMAT_G8_B8R8_2PLANE_422_UNORM, + DATA_FORMAT_G8_B8_R8_3PLANE_444_UNORM, + DATA_FORMAT_R10X6_UNORM_PACK16, + DATA_FORMAT_R10X6G10X6_UNORM_2PACK16, + DATA_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16, + DATA_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16, + DATA_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16, + DATA_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16, + DATA_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16, + DATA_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16, + DATA_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16, + DATA_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16, + DATA_FORMAT_R12X4_UNORM_PACK16, + DATA_FORMAT_R12X4G12X4_UNORM_2PACK16, + DATA_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16, + DATA_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16, + DATA_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16, + DATA_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16, + DATA_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16, + DATA_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16, + DATA_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16, + DATA_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16, + DATA_FORMAT_G16B16G16R16_422_UNORM, + DATA_FORMAT_B16G16R16G16_422_UNORM, + DATA_FORMAT_G16_B16_R16_3PLANE_420_UNORM, + DATA_FORMAT_G16_B16R16_2PLANE_420_UNORM, + DATA_FORMAT_G16_B16_R16_3PLANE_422_UNORM, + DATA_FORMAT_G16_B16R16_2PLANE_422_UNORM, + DATA_FORMAT_G16_B16_R16_3PLANE_444_UNORM, + DATA_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG, + DATA_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG, + DATA_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG, + DATA_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG, + DATA_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG, + DATA_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG, + DATA_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG, + DATA_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG, + DATA_FORMAT_MAX + }; + + /*****************/ + /**** TEXTURE ****/ + /*****************/ + + enum TextureType { + TEXTURE_TYPE_1D, + TEXTURE_TYPE_2D, + TEXTURE_TYPE_3D, + TEXTURE_TYPE_CUBE, + TEXTURE_TYPE_1D_ARRAY, + TEXTURE_TYPE_2D_ARRAY, + TEXTURE_TYPE_CUBE_ARRAY, + TEXTURE_TYPE_MAX + }; + + enum TextureSamples { + TEXTURE_SAMPLES_1, + TEXTURE_SAMPLES_2, + TEXTURE_SAMPLES_4, + TEXTURE_SAMPLES_8, + TEXTURE_SAMPLES_16, + TEXTURE_SAMPLES_32, + TEXTURE_SAMPLES_64, + TEXTURE_SAMPLES_MAX + }; + + enum TextureUsageBits { + TEXTURE_USAGE_SAMPLING_BIT = (1 << 0), + TEXTURE_USAGE_COLOR_ATTACHMENT_BIT = (1 << 1), + TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT = (1 << 2), + TEXTURE_USAGE_STORAGE_BIT = (1 << 3), + TEXTURE_USAGE_STORAGE_ATOMIC_BIT = (1 << 4), + TEXTURE_USAGE_CPU_READ_BIT = (1 << 5), + TEXTURE_USAGE_CAN_UPDATE_BIT = (1 << 6), + TEXTURE_USAGE_CAN_COPY_FROM_BIT = (1 << 7), + TEXTURE_USAGE_CAN_COPY_TO_BIT = (1 << 8), + TEXTURE_USAGE_RESOLVE_ATTACHMENT_BIT = (1 << 9), + }; + + enum TextureSwizzle { + TEXTURE_SWIZZLE_IDENTITY, + TEXTURE_SWIZZLE_ZERO, + TEXTURE_SWIZZLE_ONE, + TEXTURE_SWIZZLE_R, + TEXTURE_SWIZZLE_G, + TEXTURE_SWIZZLE_B, + TEXTURE_SWIZZLE_A, + TEXTURE_SWIZZLE_MAX + }; + + struct TextureFormat { + DataFormat format; + uint32_t width; + uint32_t height; + uint32_t depth; + uint32_t array_layers; + uint32_t mipmaps; + TextureType type; + TextureSamples samples; + uint32_t usage_bits; + Vector<DataFormat> shareable_formats; + + TextureFormat() { + format = DATA_FORMAT_R8_UNORM; + width = 1; + height = 1; + depth = 1; + array_layers = 1; + mipmaps = 1; + type = TEXTURE_TYPE_2D; + samples = TEXTURE_SAMPLES_1; + usage_bits = 0; + } + }; + + struct TextureView { + DataFormat format_override; + TextureSwizzle swizzle_r; + TextureSwizzle swizzle_g; + TextureSwizzle swizzle_b; + TextureSwizzle swizzle_a; + + TextureView() { + format_override = DATA_FORMAT_MAX; //means, use same as format + swizzle_r = TEXTURE_SWIZZLE_R; + swizzle_g = TEXTURE_SWIZZLE_G; + swizzle_b = TEXTURE_SWIZZLE_B; + swizzle_a = TEXTURE_SWIZZLE_A; + } + }; + + virtual RID texture_create(const TextureFormat &p_format, const TextureView &p_view, const Vector<Vector<uint8_t>> &p_data = Vector<Vector<uint8_t>>()) = 0; + virtual RID texture_create_shared(const TextureView &p_view, RID p_with_texture) = 0; + + enum TextureSliceType { + TEXTURE_SLICE_2D, + TEXTURE_SLICE_CUBEMAP, + TEXTURE_SLICE_3D, + }; + + virtual RID texture_create_shared_from_slice(const TextureView &p_view, RID p_with_texture, uint32_t p_layer, uint32_t p_mipmap, TextureSliceType p_slice_type = TEXTURE_SLICE_2D) = 0; + + virtual Error texture_update(RID p_texture, uint32_t p_layer, const Vector<uint8_t> &p_data, bool p_sync_with_draw = false) = 0; //this function can be used from any thread and it takes effect at the beginning of the frame, unless sync with draw is used, which is used to mix updates with draw calls + virtual Vector<uint8_t> texture_get_data(RID p_texture, uint32_t p_layer) = 0; // CPU textures will return immediately, while GPU textures will most likely force a flush + + virtual bool texture_is_format_supported_for_usage(DataFormat p_format, uint32_t p_usage) const = 0; + virtual bool texture_is_shared(RID p_texture) = 0; + virtual bool texture_is_valid(RID p_texture) = 0; + + virtual Error texture_copy(RID p_from_texture, RID p_to_texture, const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_size, uint32_t p_src_mipmap, uint32_t p_dst_mipmap, uint32_t p_src_layer, uint32_t p_dst_layer, bool p_sync_with_draw = false) = 0; + virtual Error texture_clear(RID p_texture, const Color &p_color, uint32_t p_base_mipmap, uint32_t p_mipmaps, uint32_t p_base_layer, uint32_t p_layers, bool p_sync_with_draw = false) = 0; + + /*********************/ + /**** FRAMEBUFFER ****/ + /*********************/ + + struct AttachmentFormat { + DataFormat format; + TextureSamples samples; + uint32_t usage_flags; + AttachmentFormat() { + format = DATA_FORMAT_R8G8B8A8_UNORM; + samples = TEXTURE_SAMPLES_1; + usage_flags = 0; + } + }; + + typedef int64_t FramebufferFormatID; + + // This ID is warranted to be unique for the same formats, does not need to be freed + virtual FramebufferFormatID framebuffer_format_create(const Vector<AttachmentFormat> &p_format) = 0; + virtual TextureSamples framebuffer_format_get_texture_samples(FramebufferFormatID p_format) = 0; + + virtual RID framebuffer_create(const Vector<RID> &p_texture_attachments, FramebufferFormatID p_format_check = INVALID_ID) = 0; + + virtual FramebufferFormatID framebuffer_get_format(RID p_framebuffer) = 0; + + /*****************/ + /**** SAMPLER ****/ + /*****************/ + + enum SamplerFilter { + SAMPLER_FILTER_NEAREST, + SAMPLER_FILTER_LINEAR, + }; + + enum SamplerRepeatMode { + SAMPLER_REPEAT_MODE_REPEAT, + SAMPLER_REPEAT_MODE_MIRRORED_REPEAT, + SAMPLER_REPEAT_MODE_CLAMP_TO_EDGE, + SAMPLER_REPEAT_MODE_CLAMP_TO_BORDER, + SAMPLER_REPEAT_MODE_MIRROR_CLAMP_TO_EDGE, + SAMPLER_REPEAT_MODE_MAX + }; + + enum SamplerBorderColor { + SAMPLER_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK, + SAMPLER_BORDER_COLOR_INT_TRANSPARENT_BLACK, + SAMPLER_BORDER_COLOR_FLOAT_OPAQUE_BLACK, + SAMPLER_BORDER_COLOR_INT_OPAQUE_BLACK, + SAMPLER_BORDER_COLOR_FLOAT_OPAQUE_WHITE, + SAMPLER_BORDER_COLOR_INT_OPAQUE_WHITE, + SAMPLER_BORDER_COLOR_MAX + }; + + struct SamplerState { + SamplerFilter mag_filter; + SamplerFilter min_filter; + SamplerFilter mip_filter; + SamplerRepeatMode repeat_u; + SamplerRepeatMode repeat_v; + SamplerRepeatMode repeat_w; + float lod_bias; + bool use_anisotropy; + float anisotropy_max; + bool enable_compare; + CompareOperator compare_op; + float min_lod; + float max_lod; + SamplerBorderColor border_color; + bool unnormalized_uvw; + + SamplerState() { + mag_filter = SAMPLER_FILTER_NEAREST; + min_filter = SAMPLER_FILTER_NEAREST; + mip_filter = SAMPLER_FILTER_NEAREST; + repeat_u = SAMPLER_REPEAT_MODE_CLAMP_TO_EDGE; + repeat_v = SAMPLER_REPEAT_MODE_CLAMP_TO_EDGE; + repeat_w = SAMPLER_REPEAT_MODE_CLAMP_TO_EDGE; + lod_bias = 0; + use_anisotropy = false; + anisotropy_max = 1.0; + enable_compare = false; + compare_op = COMPARE_OP_ALWAYS; + min_lod = 0; + max_lod = 1e20; //something very large should do + border_color = SAMPLER_BORDER_COLOR_FLOAT_OPAQUE_BLACK; + unnormalized_uvw = false; + } + }; + + virtual RID sampler_create(const SamplerState &p_state) = 0; + + /**********************/ + /**** VERTEX ARRAY ****/ + /**********************/ + + enum VertexFrequency { + VERTEX_FREQUENCY_VERTEX, + VERTEX_FREQUENCY_INSTANCE, + }; + + struct VertexDescription { + uint32_t location; //shader location + uint32_t offset; + DataFormat format; + uint32_t stride; + VertexFrequency frequency; + VertexDescription() { + location = 0; + offset = 0; + stride = 0; + format = DATA_FORMAT_MAX; + frequency = VERTEX_FREQUENCY_VERTEX; + } + }; + virtual RID vertex_buffer_create(uint32_t p_size_bytes, const Vector<uint8_t> &p_data = Vector<uint8_t>()) = 0; + + typedef int64_t VertexFormatID; + + // This ID is warranted to be unique for the same formats, does not need to be freed + virtual VertexFormatID vertex_format_create(const Vector<VertexDescription> &p_vertex_formats) = 0; + virtual RID vertex_array_create(uint32_t p_vertex_count, VertexFormatID p_vertex_format, const Vector<RID> &p_src_buffers) = 0; + + enum IndexBufferFormat { + INDEX_BUFFER_FORMAT_UINT16, + INDEX_BUFFER_FORMAT_UINT32, + }; + + virtual RID index_buffer_create(uint32_t p_size_indices, IndexBufferFormat p_format, const Vector<uint8_t> &p_data = Vector<uint8_t>(), bool p_use_restart_indices = false) = 0; + virtual RID index_array_create(RID p_index_buffer, uint32_t p_index_offset, uint32_t p_index_count) = 0; + + /****************/ + /**** SHADER ****/ + /****************/ + + virtual Vector<uint8_t> shader_compile_from_source(ShaderStage p_stage, const String &p_source_code, ShaderLanguage p_language = SHADER_LANGUAGE_GLSL, String *r_error = NULL, bool p_allow_cache = true); + + static void shader_set_compile_function(ShaderCompileFunction p_function); + static void shader_set_cache_function(ShaderCacheFunction p_function); + + struct ShaderStageData { + ShaderStage shader_stage; + Vector<uint8_t> spir_v; + + ShaderStageData() { + shader_stage = SHADER_STAGE_VERTEX; + } + }; + + virtual RID shader_create(const Vector<ShaderStageData> &p_stages) = 0; + virtual uint32_t shader_get_vertex_input_attribute_mask(RID p_shader) = 0; + + /******************/ + /**** UNIFORMS ****/ + /******************/ + + enum UniformType { + UNIFORM_TYPE_SAMPLER, //for sampling only (sampler GLSL type) + UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, // for sampling only, but includes a texture, (samplerXX GLSL type), first a sampler then a texture + UNIFORM_TYPE_TEXTURE, //only texture, (textureXX GLSL type) + UNIFORM_TYPE_IMAGE, // storage image (imageXX GLSL type), for compute mostly + UNIFORM_TYPE_TEXTURE_BUFFER, // buffer texture (or TBO, textureBuffer type) + UNIFORM_TYPE_SAMPLER_WITH_TEXTURE_BUFFER, // buffer texture with a sampler(or TBO, samplerBuffer type) + UNIFORM_TYPE_IMAGE_BUFFER, //texel buffer, (imageBuffer type), for compute mostly + UNIFORM_TYPE_UNIFORM_BUFFER, //regular uniform buffer (or UBO). + UNIFORM_TYPE_STORAGE_BUFFER, //storage buffer ("buffer" qualifier) like UBO, but supports storage, for compute mostly + UNIFORM_TYPE_INPUT_ATTACHMENT, //used for sub-pass read/write, for compute mostly + UNIFORM_TYPE_MAX + }; + + virtual RID uniform_buffer_create(uint32_t p_size_bytes, const Vector<uint8_t> &p_data = Vector<uint8_t>()) = 0; + virtual RID storage_buffer_create(uint32_t p_size, const Vector<uint8_t> &p_data = Vector<uint8_t>()) = 0; + virtual RID texture_buffer_create(uint32_t p_size_elements, DataFormat p_format, const Vector<uint8_t> &p_data = Vector<uint8_t>()) = 0; + + struct Uniform { + UniformType type; + int binding; //binding index as specified in shader + + //for single items, provide one ID, for + //multiple items (declared as arrays in shader), + //provide more + //for sampler with texture, supply two IDs for each. + //accepted IDs are: Sampler, Texture, Uniform Buffer and Texture Buffer + Vector<RID> ids; + + Uniform() { + type = UNIFORM_TYPE_IMAGE; + binding = 0; + } + }; + + virtual RID uniform_set_create(const Vector<Uniform> &p_uniforms, RID p_shader, uint32_t p_shader_set) = 0; + virtual bool uniform_set_is_valid(RID p_uniform_set) = 0; + + virtual Error buffer_update(RID p_buffer, uint32_t p_offset, uint32_t p_size, const void *p_data, bool p_sync_with_draw = false) = 0; //this function can be used from any thread and it takes effect at the beginning of the frame, unless sync with draw is used, which is used to mix updates with draw calls + virtual Vector<uint8_t> buffer_get_data(RID p_buffer) = 0; //this causes stall, only use to retrieve large buffers for saving + + /*************************/ + /**** RENDER PIPELINE ****/ + /*************************/ + + enum RenderPrimitive { + RENDER_PRIMITIVE_POINTS, + RENDER_PRIMITIVE_LINES, + RENDER_PRIMITIVE_LINES_WITH_ADJACENCY, + RENDER_PRIMITIVE_LINESTRIPS, + RENDER_PRIMITIVE_LINESTRIPS_WITH_ADJACENCY, + RENDER_PRIMITIVE_TRIANGLES, + RENDER_PRIMITIVE_TRIANGLES_WITH_ADJACENCY, + RENDER_PRIMITIVE_TRIANGLE_STRIPS, + RENDER_PRIMITIVE_TRIANGLE_STRIPS_WITH_AJACENCY, + RENDER_PRIMITIVE_TRIANGLE_STRIPS_WITH_RESTART_INDEX, + RENDER_PRIMITIVE_TESSELATION_PATCH, + RENDER_PRIMITIVE_MAX + }; + + //disable optimization, tessellate control points + + enum PolygonCullMode { + POLYGON_CULL_DISABLED, + POLYGON_CULL_FRONT, + POLYGON_CULL_BACK, + }; + + enum PolygonFrontFace { + POLYGON_FRONT_FACE_CLOCKWISE, + POLYGON_FRONT_FACE_COUNTER_CLOCKWISE, + }; + + enum StencilOperation { + STENCIL_OP_KEEP, + STENCIL_OP_ZERO, + STENCIL_OP_REPLACE, + STENCIL_OP_INCREMENT_AND_CLAMP, + STENCIL_OP_DECREMENT_AND_CLAMP, + STENCIL_OP_INVERT, + STENCIL_OP_INCREMENT_AND_WRAP, + STENCIL_OP_DECREMENT_AND_WRAP, + STENCIL_OP_MAX //not an actual operator, just the amount of operators :D + }; + + enum LogicOperation { + LOGIC_OP_CLEAR, + LOGIC_OP_AND, + LOGIC_OP_AND_REVERSE, + LOGIC_OP_COPY, + LOGIC_OP_AND_INVERTED, + LOGIC_OP_NO_OP, + LOGIC_OP_XOR, + LOGIC_OP_OR, + LOGIC_OP_NOR, + LOGIC_OP_EQUIVALENT, + LOGIC_OP_INVERT, + LOGIC_OP_OR_REVERSE, + LOGIC_OP_COPY_INVERTED, + LOGIC_OP_OR_INVERTED, + LOGIC_OP_NAND, + LOGIC_OP_SET, + LOGIC_OP_MAX //not an actual operator, just the amount of operators :D + }; + + enum BlendFactor { + BLEND_FACTOR_ZERO, + BLEND_FACTOR_ONE, + BLEND_FACTOR_SRC_COLOR, + BLEND_FACTOR_ONE_MINUS_SRC_COLOR, + BLEND_FACTOR_DST_COLOR, + BLEND_FACTOR_ONE_MINUS_DST_COLOR, + BLEND_FACTOR_SRC_ALPHA, + BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, + BLEND_FACTOR_DST_ALPHA, + BLEND_FACTOR_ONE_MINUS_DST_ALPHA, + BLEND_FACTOR_CONSTANT_COLOR, + BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR, + BLEND_FACTOR_CONSTANT_ALPHA, + BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA, + BLEND_FACTOR_SRC_ALPHA_SATURATE, + BLEND_FACTOR_SRC1_COLOR, + BLEND_FACTOR_ONE_MINUS_SRC1_COLOR, + BLEND_FACTOR_SRC1_ALPHA, + BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA, + BLEND_FACTOR_MAX + }; + + enum BlendOperation { + BLEND_OP_ADD, + BLEND_OP_SUBTRACT, + BLEND_OP_REVERSE_SUBTRACT, + BLEND_OP_MINIMUM, + BLEND_OP_MAXIMUM, //yes this one is an actual operator + BLEND_OP_MAX //not an actual operator, just the amount of operators :D + }; + + struct PipelineRasterizationState { + bool enable_depth_clamp; + bool discard_primitives; + bool wireframe; + PolygonCullMode cull_mode; + PolygonFrontFace front_face; + bool depth_bias_enable; + float depth_bias_constant_factor; + float depth_bias_clamp; + float depth_bias_slope_factor; + float line_width; + uint32_t patch_control_points; + PipelineRasterizationState() { + enable_depth_clamp = false; + discard_primitives = false; + wireframe = false; + cull_mode = POLYGON_CULL_DISABLED; + front_face = POLYGON_FRONT_FACE_CLOCKWISE; + depth_bias_enable = false; + depth_bias_constant_factor = 0; + depth_bias_clamp = 0; + depth_bias_slope_factor = 0; + line_width = 1.0; + patch_control_points = 1; + } + }; + + struct PipelineMultisampleState { + TextureSamples sample_count; + bool enable_sample_shading; + float min_sample_shading; + Vector<uint32_t> sample_mask; + bool enable_alpha_to_coverage; + bool enable_alpha_to_one; + + PipelineMultisampleState() { + sample_count = TEXTURE_SAMPLES_1; + enable_sample_shading = false; + min_sample_shading = 0; + enable_alpha_to_coverage = false; + enable_alpha_to_one = false; + } + }; + + struct PipelineDepthStencilState { + + bool enable_depth_test; + bool enable_depth_write; + CompareOperator depth_compare_operator; + bool enable_depth_range; + float depth_range_min; + float depth_range_max; + bool enable_stencil; + + struct StencilOperationState { + StencilOperation fail; + StencilOperation pass; + StencilOperation depth_fail; + CompareOperator compare; + uint32_t compare_mask; + uint32_t write_mask; + uint32_t reference; + + StencilOperationState() { + fail = STENCIL_OP_ZERO; + pass = STENCIL_OP_ZERO; + depth_fail = STENCIL_OP_ZERO; + compare = COMPARE_OP_ALWAYS; + compare_mask = 0; + write_mask = 0; + reference = 0; + } + }; + + StencilOperationState stencil_operation_front; + StencilOperationState stencil_operation_back; + + PipelineDepthStencilState() { + enable_depth_test = false; + enable_depth_write = false; + depth_compare_operator = COMPARE_OP_ALWAYS; + enable_depth_range = false; + depth_range_min = 0; + depth_range_max = 0; + enable_stencil = false; + } + }; + + struct PipelineColorBlendState { + + bool enable_logic_op; + LogicOperation logic_op; + struct Attachment { + bool enable_blend; + BlendFactor src_color_blend_factor; + BlendFactor dst_color_blend_factor; + BlendOperation color_blend_op; + BlendFactor src_alpha_blend_factor; + BlendFactor dst_alpha_blend_factor; + BlendOperation alpha_blend_op; + bool write_r; + bool write_g; + bool write_b; + bool write_a; + Attachment() { + enable_blend = false; + src_color_blend_factor = BLEND_FACTOR_ZERO; + dst_color_blend_factor = BLEND_FACTOR_ZERO; + color_blend_op = BLEND_OP_ADD; + src_alpha_blend_factor = BLEND_FACTOR_ZERO; + dst_alpha_blend_factor = BLEND_FACTOR_ZERO; + alpha_blend_op = BLEND_OP_ADD; + write_r = true; + write_g = true; + write_b = true; + write_a = true; + } + }; + + static PipelineColorBlendState create_disabled(int p_attachments = 1) { + PipelineColorBlendState bs; + for (int i = 0; i < p_attachments; i++) { + bs.attachments.push_back(Attachment()); + } + return bs; + } + + static PipelineColorBlendState create_blend(int p_attachments = 1) { + PipelineColorBlendState bs; + for (int i = 0; i < p_attachments; i++) { + + Attachment ba; + ba.enable_blend = true; + ba.src_color_blend_factor = BLEND_FACTOR_SRC_ALPHA; + ba.dst_color_blend_factor = BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + ba.src_alpha_blend_factor = BLEND_FACTOR_SRC_ALPHA; + ba.dst_alpha_blend_factor = BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + + bs.attachments.push_back(ba); + } + return bs; + } + + Vector<Attachment> attachments; //one per render target texture + Color blend_constant; + + PipelineColorBlendState() { + enable_logic_op = false; + logic_op = LOGIC_OP_CLEAR; + } + }; + + enum PipelineDynamicStateFlags { + DYNAMIC_STATE_LINE_WIDTH = (1 << 0), + DYNAMIC_STATE_DEPTH_BIAS = (1 << 1), + DYNAMIC_STATE_BLEND_CONSTANTS = (1 << 2), + DYNAMIC_STATE_DEPTH_BOUNDS = (1 << 3), + DYNAMIC_STATE_STENCIL_COMPARE_MASK = (1 << 4), + DYNAMIC_STATE_STENCIL_WRITE_MASK = (1 << 5), + DYNAMIC_STATE_STENCIL_REFERENCE = (1 << 6), + }; + + virtual RID render_pipeline_create(RID p_shader, FramebufferFormatID p_framebuffer_format, VertexFormatID p_vertex_format, RenderPrimitive p_render_primitive, const PipelineRasterizationState &p_rasterization_state, const PipelineMultisampleState &p_multisample_state, const PipelineDepthStencilState &p_depth_stencil_state, const PipelineColorBlendState &p_blend_state, int p_dynamic_state_flags = 0) = 0; + virtual bool render_pipeline_is_valid(RID p_pipeline) = 0; + + /**************************/ + /**** COMPUTE PIPELINE ****/ + /**************************/ + + virtual RID compute_pipeline_create(RID p_shader) = 0; + virtual bool compute_pipeline_is_valid(RID p_pipeline) = 0; + + /****************/ + /**** SCREEN ****/ + /****************/ + + virtual int screen_get_width(DisplayServer::WindowID p_screen = 0) const = 0; + virtual int screen_get_height(DisplayServer::WindowID p_screen = 0) const = 0; + virtual FramebufferFormatID screen_get_framebuffer_format() const = 0; + + /********************/ + /**** DRAW LISTS ****/ + /********************/ + + enum InitialAction { + INITIAL_ACTION_CLEAR, //start rendering and clear the framebuffer (supply params) + INITIAL_ACTION_KEEP, //start rendering, but keep attached color texture contents (depth will be cleared) + INITIAL_ACTION_CONTINUE, //continue rendering (framebuffer must have been left in "continue" state as final action previously) + INITIAL_ACTION_MAX + }; + + enum FinalAction { + FINAL_ACTION_READ, //will no longer render to it, allows attached textures to be read again, but depth buffer contents will be dropped (Can't be read from) + FINAL_ACTION_DISCARD, // discard contents after rendering + FINAL_ACTION_CONTINUE, //will continue rendering later, attached textures can't be read until re-bound with "finish" + FINAL_ACTION_MAX + }; + + typedef int64_t DrawListID; + + virtual DrawListID draw_list_begin_for_screen(DisplayServer::WindowID p_screen = 0, const Color &p_clear_color = Color()) = 0; + virtual DrawListID draw_list_begin(RID p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values = Vector<Color>(), float p_clear_depth = 1.0, uint32_t p_clear_stencil = 0, const Rect2 &p_region = Rect2()) = 0; + virtual Error draw_list_begin_split(RID p_framebuffer, uint32_t p_splits, DrawListID *r_split_ids, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values = Vector<Color>(), float p_clear_depth = 1.0, uint32_t p_clear_stencil = 0, const Rect2 &p_region = Rect2()) = 0; + + virtual void draw_list_bind_render_pipeline(DrawListID p_list, RID p_render_pipeline) = 0; + virtual void draw_list_bind_uniform_set(DrawListID p_list, RID p_uniform_set, uint32_t p_index) = 0; + virtual void draw_list_bind_vertex_array(DrawListID p_list, RID p_vertex_array) = 0; + virtual void draw_list_bind_index_array(DrawListID p_list, RID p_index_array) = 0; + virtual void draw_list_set_line_width(DrawListID p_list, float p_width) = 0; + virtual void draw_list_set_push_constant(DrawListID p_list, void *p_data, uint32_t p_data_size) = 0; + + virtual void draw_list_draw(DrawListID p_list, bool p_use_indices, uint32_t p_instances = 1, uint32_t p_procedural_vertices = 0) = 0; + + virtual void draw_list_enable_scissor(DrawListID p_list, const Rect2 &p_rect) = 0; + virtual void draw_list_disable_scissor(DrawListID p_list) = 0; + + virtual void draw_list_end() = 0; + + /***********************/ + /**** COMPUTE LISTS ****/ + /***********************/ + + typedef int64_t ComputeListID; + + virtual ComputeListID compute_list_begin() = 0; + virtual void compute_list_bind_compute_pipeline(ComputeListID p_list, RID p_compute_pipeline) = 0; + virtual void compute_list_bind_uniform_set(ComputeListID p_list, RID p_uniform_set, uint32_t p_index) = 0; + virtual void compute_list_set_push_constant(ComputeListID p_list, void *p_data, uint32_t p_data_size) = 0; + virtual void compute_list_dispatch(ComputeListID p_list, uint32_t p_x_groups, uint32_t p_y_groups, uint32_t p_z_groups) = 0; + virtual void compute_list_add_barrier(ComputeListID p_list) = 0; + + virtual void compute_list_end() = 0; + + /***************/ + /**** FREE! ****/ + /***************/ + + virtual void free(RID p_id) = 0; + + /****************/ + /**** Timing ****/ + /****************/ + + virtual void capture_timestamp(const String &p_name, bool p_sync_to_draw) = 0; + virtual uint32_t get_captured_timestamps_count() const = 0; + virtual uint64_t get_captured_timestamps_frame() const = 0; + virtual uint64_t get_captured_timestamp_gpu_time(uint32_t p_index) const = 0; + virtual uint64_t get_captured_timestamp_cpu_time(uint32_t p_index) const = 0; + virtual String get_captured_timestamp_name(uint32_t p_index) const = 0; + + /****************/ + /**** LIMITS ****/ + /****************/ + + enum Limit { + LIMIT_MAX_BOUND_UNIFORM_SETS, + LIMIT_MAX_FRAMEBUFFER_COLOR_ATTACHMENTS, + LIMIT_MAX_TEXTURES_PER_UNIFORM_SET, + LIMIT_MAX_SAMPLERS_PER_UNIFORM_SET, + LIMIT_MAX_STORAGE_BUFFERS_PER_UNIFORM_SET, + LIMIT_MAX_STORAGE_IMAGES_PER_UNIFORM_SET, + LIMIT_MAX_UNIFORM_BUFFERS_PER_UNIFORM_SET, + LIMIT_MAX_DRAW_INDEXED_INDEX, + LIMIT_MAX_FRAMEBUFFER_HEIGHT, + LIMIT_MAX_FRAMEBUFFER_WIDTH, + LIMIT_MAX_TEXTURE_ARRAY_LAYERS, + LIMIT_MAX_TEXTURE_SIZE_1D, + LIMIT_MAX_TEXTURE_SIZE_2D, + LIMIT_MAX_TEXTURE_SIZE_3D, + LIMIT_MAX_TEXTURE_SIZE_CUBE, + LIMIT_MAX_TEXTURES_PER_SHADER_STAGE, + LIMIT_MAX_SAMPLERS_PER_SHADER_STAGE, + LIMIT_MAX_STORAGE_BUFFERS_PER_SHADER_STAGE, + LIMIT_MAX_STORAGE_IMAGES_PER_SHADER_STAGE, + LIMIT_MAX_UNIFORM_BUFFERS_PER_SHADER_STAGE, + LIMIT_MAX_PUSH_CONSTANT_SIZE, + LIMIT_MAX_UNIFORM_BUFFER_SIZE, + LIMIT_MAX_VERTEX_INPUT_ATTRIBUTE_OFFSET, + LIMIT_MAX_VERTEX_INPUT_ATTRIBUTES, + LIMIT_MAX_VERTEX_INPUT_BINDINGS, + LIMIT_MAX_VERTEX_INPUT_BINDING_STRIDE, + LIMIT_MIN_UNIFORM_BUFFER_OFFSET_ALIGNMENT, + LIMIT_MAX_COMPUTE_SHARED_MEMORY_SIZE, + LIMIT_MAX_COMPUTE_WORKGROUP_COUNT_X, + LIMIT_MAX_COMPUTE_WORKGROUP_COUNT_Y, + LIMIT_MAX_COMPUTE_WORKGROUP_COUNT_Z, + LIMIT_MAX_COMPUTE_WORKGROUP_INVOCATIONS, + LIMIT_MAX_COMPUTE_WORKGROUP_SIZE_X, + LIMIT_MAX_COMPUTE_WORKGROUP_SIZE_Y, + LIMIT_MAX_COMPUTE_WORKGROUP_SIZE_Z, + }; + + virtual int limit_get(Limit p_limit) = 0; + + //methods below not exposed, used by RenderingDeviceRD + virtual void prepare_screen_for_drawing() = 0; + + virtual void swap_buffers() = 0; + + virtual uint32_t get_frame_delay() const = 0; + + static RenderingDevice *get_singleton(); + + RenderingDevice(); +}; + +typedef RenderingDevice RD; + +#endif // RENDERING_DEVICE_H diff --git a/servers/rendering/rendering_server_canvas.cpp b/servers/rendering/rendering_server_canvas.cpp new file mode 100644 index 0000000000..7b28c586b9 --- /dev/null +++ b/servers/rendering/rendering_server_canvas.cpp @@ -0,0 +1,1479 @@ +/*************************************************************************/ +/* rendering_server_canvas.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "rendering_server_canvas.h" +#include "rendering_server_globals.h" +#include "rendering_server_raster.h" +#include "rendering_server_viewport.h" + +static const int z_range = RS::CANVAS_ITEM_Z_MAX - RS::CANVAS_ITEM_Z_MIN + 1; + +void RenderingServerCanvas::_render_canvas_item_tree(RID p_to_render_target, Canvas::ChildItem *p_child_items, int p_child_item_count, Item *p_canvas_item, const Transform2D &p_transform, const Rect2 &p_clip_rect, const Color &p_modulate, RasterizerCanvas::Light *p_lights) { + + RENDER_TIMESTAMP("Cull CanvasItem Tree"); + + memset(z_list, 0, z_range * sizeof(RasterizerCanvas::Item *)); + memset(z_last_list, 0, z_range * sizeof(RasterizerCanvas::Item *)); + + for (int i = 0; i < p_child_item_count; i++) { + _cull_canvas_item(p_child_items[i].item, p_transform, p_clip_rect, Color(1, 1, 1, 1), 0, z_list, z_last_list, NULL, NULL); + } + if (p_canvas_item) { + _cull_canvas_item(p_canvas_item, p_transform, p_clip_rect, Color(1, 1, 1, 1), 0, z_list, z_last_list, NULL, NULL); + } + + RasterizerCanvas::Item *list = NULL; + RasterizerCanvas::Item *list_end = NULL; + + for (int i = 0; i < z_range; i++) { + if (!z_list[i]) + continue; + if (!list) { + list = z_list[i]; + list_end = z_last_list[i]; + } else { + list_end->next = z_list[i]; + list_end = z_last_list[i]; + } + } + + RENDER_TIMESTAMP("Render Canvas Items"); + + RSG::canvas_render->canvas_render_items(p_to_render_target, list, p_modulate, p_lights, p_transform); +} + +void _collect_ysort_children(RenderingServerCanvas::Item *p_canvas_item, Transform2D p_transform, RenderingServerCanvas::Item *p_material_owner, RenderingServerCanvas::Item **r_items, int &r_index) { + int child_item_count = p_canvas_item->child_items.size(); + RenderingServerCanvas::Item **child_items = p_canvas_item->child_items.ptrw(); + for (int i = 0; i < child_item_count; i++) { + if (child_items[i]->visible) { + if (r_items) { + r_items[r_index] = child_items[i]; + child_items[i]->ysort_xform = p_transform; + child_items[i]->ysort_pos = p_transform.xform(child_items[i]->xform.elements[2]); + child_items[i]->material_owner = child_items[i]->use_parent_material ? p_material_owner : NULL; + } + + r_index++; + + if (child_items[i]->sort_y) + _collect_ysort_children(child_items[i], p_transform * child_items[i]->xform, child_items[i]->use_parent_material ? p_material_owner : child_items[i], r_items, r_index); + } + } +} + +void _mark_ysort_dirty(RenderingServerCanvas::Item *ysort_owner, RID_PtrOwner<RenderingServerCanvas::Item> &canvas_item_owner) { + do { + ysort_owner->ysort_children_count = -1; + ysort_owner = canvas_item_owner.owns(ysort_owner->parent) ? canvas_item_owner.getornull(ysort_owner->parent) : NULL; + } while (ysort_owner && ysort_owner->sort_y); +} + +void RenderingServerCanvas::_cull_canvas_item(Item *p_canvas_item, const Transform2D &p_transform, const Rect2 &p_clip_rect, const Color &p_modulate, int p_z, RasterizerCanvas::Item **z_list, RasterizerCanvas::Item **z_last_list, Item *p_canvas_clip, Item *p_material_owner) { + + Item *ci = p_canvas_item; + + if (!ci->visible) + return; + + if (ci->children_order_dirty) { + + ci->child_items.sort_custom<ItemIndexSort>(); + ci->children_order_dirty = false; + } + + Rect2 rect = ci->get_rect(); + Transform2D xform = p_transform * ci->xform; + Rect2 global_rect = xform.xform(rect); + global_rect.position += p_clip_rect.position; + + if (ci->use_parent_material && p_material_owner) + ci->material_owner = p_material_owner; + else { + p_material_owner = ci; + ci->material_owner = NULL; + } + + Color modulate(ci->modulate.r * p_modulate.r, ci->modulate.g * p_modulate.g, ci->modulate.b * p_modulate.b, ci->modulate.a * p_modulate.a); + + if (modulate.a < 0.007) + return; + + int child_item_count = ci->child_items.size(); + Item **child_items = ci->child_items.ptrw(); + + if (ci->clip) { + if (p_canvas_clip != NULL) { + ci->final_clip_rect = p_canvas_clip->final_clip_rect.clip(global_rect); + } else { + ci->final_clip_rect = global_rect; + } + ci->final_clip_owner = ci; + + } else { + ci->final_clip_owner = p_canvas_clip; + } + + if (ci->sort_y) { + + if (ci->ysort_children_count == -1) { + ci->ysort_children_count = 0; + _collect_ysort_children(ci, Transform2D(), p_material_owner, NULL, ci->ysort_children_count); + } + + child_item_count = ci->ysort_children_count; + child_items = (Item **)alloca(child_item_count * sizeof(Item *)); + + int i = 0; + _collect_ysort_children(ci, Transform2D(), p_material_owner, child_items, i); + + SortArray<Item *, ItemPtrSort> sorter; + sorter.sort(child_items, child_item_count); + } + + if (ci->z_relative) + p_z = CLAMP(p_z + ci->z_index, RS::CANVAS_ITEM_Z_MIN, RS::CANVAS_ITEM_Z_MAX); + else + p_z = ci->z_index; + + for (int i = 0; i < child_item_count; i++) { + + if (!child_items[i]->behind || (ci->sort_y && child_items[i]->sort_y)) + continue; + if (ci->sort_y) { + _cull_canvas_item(child_items[i], xform * child_items[i]->ysort_xform, p_clip_rect, modulate, p_z, z_list, z_last_list, (Item *)ci->final_clip_owner, (Item *)child_items[i]->material_owner); + } else { + _cull_canvas_item(child_items[i], xform, p_clip_rect, modulate, p_z, z_list, z_last_list, (Item *)ci->final_clip_owner, p_material_owner); + } + } + + if (ci->copy_back_buffer) { + + ci->copy_back_buffer->screen_rect = xform.xform(ci->copy_back_buffer->rect).clip(p_clip_rect); + } + + if (ci->update_when_visible) { + RenderingServerRaster::redraw_request(); + } + + if ((ci->commands != NULL && p_clip_rect.intersects(global_rect, true)) || ci->vp_render || ci->copy_back_buffer) { + //something to draw? + ci->final_transform = xform; + ci->final_modulate = Color(modulate.r * ci->self_modulate.r, modulate.g * ci->self_modulate.g, modulate.b * ci->self_modulate.b, modulate.a * ci->self_modulate.a); + ci->global_rect_cache = global_rect; + ci->global_rect_cache.position -= p_clip_rect.position; + ci->light_masked = false; + + int zidx = p_z - RS::CANVAS_ITEM_Z_MIN; + + if (z_last_list[zidx]) { + z_last_list[zidx]->next = ci; + z_last_list[zidx] = ci; + + } else { + z_list[zidx] = ci; + z_last_list[zidx] = ci; + } + + ci->z_final = p_z; + + ci->next = NULL; + } + + for (int i = 0; i < child_item_count; i++) { + + if (child_items[i]->behind || (ci->sort_y && child_items[i]->sort_y)) + continue; + if (ci->sort_y) { + _cull_canvas_item(child_items[i], xform * child_items[i]->ysort_xform, p_clip_rect, modulate, p_z, z_list, z_last_list, (Item *)ci->final_clip_owner, (Item *)child_items[i]->material_owner); + } else { + _cull_canvas_item(child_items[i], xform, p_clip_rect, modulate, p_z, z_list, z_last_list, (Item *)ci->final_clip_owner, p_material_owner); + } + } +} + +void RenderingServerCanvas::_light_mask_canvas_items(int p_z, RasterizerCanvas::Item *p_canvas_item, RasterizerCanvas::Light *p_masked_lights) { + + if (!p_masked_lights) + return; + + RasterizerCanvas::Item *ci = p_canvas_item; + + while (ci) { + + RasterizerCanvas::Light *light = p_masked_lights; + while (light) { + + if (ci->light_mask & light->item_mask && p_z >= light->z_min && p_z <= light->z_max && ci->global_rect_cache.intersects_transformed(light->xform_cache, light->rect_cache)) { + ci->light_masked = true; + } + + light = light->mask_next_ptr; + } + + ci = ci->next; + } +} + +void RenderingServerCanvas::render_canvas(RID p_render_target, Canvas *p_canvas, const Transform2D &p_transform, RasterizerCanvas::Light *p_lights, RasterizerCanvas::Light *p_masked_lights, const Rect2 &p_clip_rect) { + + RENDER_TIMESTAMP(">Render Canvas"); + + if (p_canvas->children_order_dirty) { + + p_canvas->child_items.sort(); + p_canvas->children_order_dirty = false; + } + + int l = p_canvas->child_items.size(); + Canvas::ChildItem *ci = p_canvas->child_items.ptrw(); + + bool has_mirror = false; + for (int i = 0; i < l; i++) { + if (ci[i].mirror.x || ci[i].mirror.y) { + has_mirror = true; + break; + } + } + + if (!has_mirror) { + + _render_canvas_item_tree(p_render_target, ci, l, NULL, p_transform, p_clip_rect, p_canvas->modulate, p_lights); + + } else { + //used for parallaxlayer mirroring + for (int i = 0; i < l; i++) { + + const Canvas::ChildItem &ci2 = p_canvas->child_items[i]; + _render_canvas_item_tree(p_render_target, NULL, 0, ci2.item, p_transform, p_clip_rect, p_canvas->modulate, p_lights); + + //mirroring (useful for scrolling backgrounds) + if (ci2.mirror.x != 0) { + + Transform2D xform2 = p_transform * Transform2D(0, Vector2(ci2.mirror.x, 0)); + _render_canvas_item_tree(p_render_target, NULL, 0, ci2.item, xform2, p_clip_rect, p_canvas->modulate, p_lights); + } + if (ci2.mirror.y != 0) { + + Transform2D xform2 = p_transform * Transform2D(0, Vector2(0, ci2.mirror.y)); + _render_canvas_item_tree(p_render_target, NULL, 0, ci2.item, xform2, p_clip_rect, p_canvas->modulate, p_lights); + } + if (ci2.mirror.y != 0 && ci2.mirror.x != 0) { + + Transform2D xform2 = p_transform * Transform2D(0, ci2.mirror); + _render_canvas_item_tree(p_render_target, NULL, 0, ci2.item, xform2, p_clip_rect, p_canvas->modulate, p_lights); + } + } + } + + RENDER_TIMESTAMP("<End Render Canvas"); +} + +RID RenderingServerCanvas::canvas_create() { + + Canvas *canvas = memnew(Canvas); + ERR_FAIL_COND_V(!canvas, RID()); + RID rid = canvas_owner.make_rid(canvas); + + return rid; +} + +void RenderingServerCanvas::canvas_set_item_mirroring(RID p_canvas, RID p_item, const Point2 &p_mirroring) { + + Canvas *canvas = canvas_owner.getornull(p_canvas); + ERR_FAIL_COND(!canvas); + Item *canvas_item = canvas_item_owner.getornull(p_item); + ERR_FAIL_COND(!canvas_item); + + int idx = canvas->find_item(canvas_item); + ERR_FAIL_COND(idx == -1); + canvas->child_items.write[idx].mirror = p_mirroring; +} +void RenderingServerCanvas::canvas_set_modulate(RID p_canvas, const Color &p_color) { + + Canvas *canvas = canvas_owner.getornull(p_canvas); + ERR_FAIL_COND(!canvas); + canvas->modulate = p_color; +} + +void RenderingServerCanvas::canvas_set_disable_scale(bool p_disable) { + disable_scale = p_disable; +} + +void RenderingServerCanvas::canvas_set_parent(RID p_canvas, RID p_parent, float p_scale) { + + Canvas *canvas = canvas_owner.getornull(p_canvas); + ERR_FAIL_COND(!canvas); + + canvas->parent = p_parent; + canvas->parent_scale = p_scale; +} + +RID RenderingServerCanvas::canvas_item_create() { + + Item *canvas_item = memnew(Item); + ERR_FAIL_COND_V(!canvas_item, RID()); + + return canvas_item_owner.make_rid(canvas_item); +} + +void RenderingServerCanvas::canvas_item_set_parent(RID p_item, RID p_parent) { + + Item *canvas_item = canvas_item_owner.getornull(p_item); + ERR_FAIL_COND(!canvas_item); + + if (canvas_item->parent.is_valid()) { + + if (canvas_owner.owns(canvas_item->parent)) { + + Canvas *canvas = canvas_owner.getornull(canvas_item->parent); + canvas->erase_item(canvas_item); + } else if (canvas_item_owner.owns(canvas_item->parent)) { + + Item *item_owner = canvas_item_owner.getornull(canvas_item->parent); + item_owner->child_items.erase(canvas_item); + + if (item_owner->sort_y) { + _mark_ysort_dirty(item_owner, canvas_item_owner); + } + } + + canvas_item->parent = RID(); + } + + if (p_parent.is_valid()) { + if (canvas_owner.owns(p_parent)) { + + Canvas *canvas = canvas_owner.getornull(p_parent); + Canvas::ChildItem ci; + ci.item = canvas_item; + canvas->child_items.push_back(ci); + canvas->children_order_dirty = true; + } else if (canvas_item_owner.owns(p_parent)) { + + Item *item_owner = canvas_item_owner.getornull(p_parent); + item_owner->child_items.push_back(canvas_item); + item_owner->children_order_dirty = true; + + if (item_owner->sort_y) { + _mark_ysort_dirty(item_owner, canvas_item_owner); + } + + } else { + + ERR_FAIL_MSG("Invalid parent."); + } + } + + canvas_item->parent = p_parent; +} +void RenderingServerCanvas::canvas_item_set_visible(RID p_item, bool p_visible) { + + Item *canvas_item = canvas_item_owner.getornull(p_item); + ERR_FAIL_COND(!canvas_item); + + canvas_item->visible = p_visible; + + _mark_ysort_dirty(canvas_item, canvas_item_owner); +} +void RenderingServerCanvas::canvas_item_set_light_mask(RID p_item, int p_mask) { + + Item *canvas_item = canvas_item_owner.getornull(p_item); + ERR_FAIL_COND(!canvas_item); + + canvas_item->light_mask = p_mask; +} + +void RenderingServerCanvas::canvas_item_set_transform(RID p_item, const Transform2D &p_transform) { + + Item *canvas_item = canvas_item_owner.getornull(p_item); + ERR_FAIL_COND(!canvas_item); + + canvas_item->xform = p_transform; +} +void RenderingServerCanvas::canvas_item_set_clip(RID p_item, bool p_clip) { + + Item *canvas_item = canvas_item_owner.getornull(p_item); + ERR_FAIL_COND(!canvas_item); + + canvas_item->clip = p_clip; +} +void RenderingServerCanvas::canvas_item_set_distance_field_mode(RID p_item, bool p_enable) { + + Item *canvas_item = canvas_item_owner.getornull(p_item); + ERR_FAIL_COND(!canvas_item); + + canvas_item->distance_field = p_enable; +} +void RenderingServerCanvas::canvas_item_set_custom_rect(RID p_item, bool p_custom_rect, const Rect2 &p_rect) { + + Item *canvas_item = canvas_item_owner.getornull(p_item); + ERR_FAIL_COND(!canvas_item); + + canvas_item->custom_rect = p_custom_rect; + canvas_item->rect = p_rect; +} +void RenderingServerCanvas::canvas_item_set_modulate(RID p_item, const Color &p_color) { + + Item *canvas_item = canvas_item_owner.getornull(p_item); + ERR_FAIL_COND(!canvas_item); + + canvas_item->modulate = p_color; +} +void RenderingServerCanvas::canvas_item_set_self_modulate(RID p_item, const Color &p_color) { + + Item *canvas_item = canvas_item_owner.getornull(p_item); + ERR_FAIL_COND(!canvas_item); + + canvas_item->self_modulate = p_color; +} + +void RenderingServerCanvas::canvas_item_set_draw_behind_parent(RID p_item, bool p_enable) { + + Item *canvas_item = canvas_item_owner.getornull(p_item); + ERR_FAIL_COND(!canvas_item); + + canvas_item->behind = p_enable; +} + +void RenderingServerCanvas::canvas_item_set_update_when_visible(RID p_item, bool p_update) { + + Item *canvas_item = canvas_item_owner.getornull(p_item); + ERR_FAIL_COND(!canvas_item); + + canvas_item->update_when_visible = p_update; +} + +void RenderingServerCanvas::canvas_item_set_default_texture_filter(RID p_item, RS::CanvasItemTextureFilter p_filter) { + + Item *canvas_item = canvas_item_owner.getornull(p_item); + ERR_FAIL_COND(!canvas_item); + canvas_item->texture_filter = p_filter; +} + +void RenderingServerCanvas::canvas_item_set_default_texture_repeat(RID p_item, RS::CanvasItemTextureRepeat p_repeat) { + + Item *canvas_item = canvas_item_owner.getornull(p_item); + ERR_FAIL_COND(!canvas_item); + canvas_item->texture_repeat = p_repeat; +} + +void RenderingServerCanvas::canvas_item_add_line(RID p_item, const Point2 &p_from, const Point2 &p_to, const Color &p_color, float p_width) { + + Item *canvas_item = canvas_item_owner.getornull(p_item); + ERR_FAIL_COND(!canvas_item); + + Item::CommandPrimitive *line = canvas_item->alloc_command<Item::CommandPrimitive>(); + ERR_FAIL_COND(!line); + if (p_width > 1.001) { + + Vector2 t = (p_from - p_to).tangent().normalized(); + line->points[0] = p_from + t * p_width; + line->points[1] = p_from - t * p_width; + line->points[2] = p_to - t * p_width; + line->points[3] = p_to + t * p_width; + line->point_count = 4; + } else { + line->point_count = 2; + line->points[0] = p_from; + line->points[1] = p_to; + } + for (uint32_t i = 0; i < line->point_count; i++) { + line->colors[i] = p_color; + } + line->specular_shininess = Color(1, 1, 1, 1); +} + +void RenderingServerCanvas::canvas_item_add_polyline(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, float p_width) { + + ERR_FAIL_COND(p_points.size() < 2); + Item *canvas_item = canvas_item_owner.getornull(p_item); + ERR_FAIL_COND(!canvas_item); + + Item::CommandPolygon *pline = canvas_item->alloc_command<Item::CommandPolygon>(); + ERR_FAIL_COND(!pline); + + pline->texture_binding.create(canvas_item->texture_filter, canvas_item->texture_repeat, RID(), RID(), RID(), RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED, RID()); + + if (true || p_width <= 1) { +#define TODO make thick lines possible + Vector<int> indices; + int pc = p_points.size(); + indices.resize((pc - 1) * 2); + { + int *iptr = indices.ptrw(); + for (int i = 0; i < (pc - 1); i++) { + iptr[i * 2 + 0] = i; + iptr[i * 2 + 1] = i + 1; + } + } + + pline->primitive = RS::PRIMITIVE_LINES; + pline->specular_shininess = Color(1, 1, 1, 1); + pline->polygon.create(indices, p_points, p_colors); + } else { +#if 0 + //make a trianglestrip for drawing the line... + Vector2 prev_t; + pline->triangles.resize(p_points.size() * 2); + if (p_antialiased) { + pline->lines.resize(p_points.size() * 2); + } + + if (p_colors.size() == 0) { + pline->triangle_colors.push_back(Color(1, 1, 1, 1)); + if (p_antialiased) { + pline->line_colors.push_back(Color(1, 1, 1, 1)); + } + } else if (p_colors.size() == 1) { + pline->triangle_colors = p_colors; + pline->line_colors = p_colors; + } else { + if (p_colors.size() != p_points.size()) { + pline->triangle_colors.push_back(p_colors[0]); + pline->line_colors.push_back(p_colors[0]); + } else { + pline->triangle_colors.resize(pline->triangles.size()); + pline->line_colors.resize(pline->lines.size()); + } + } + + for (int i = 0; i < p_points.size(); i++) { + + Vector2 t; + if (i == p_points.size() - 1) { + t = prev_t; + } else { + t = (p_points[i + 1] - p_points[i]).normalized().tangent(); + if (i == 0) { + prev_t = t; + } + } + + Vector2 tangent = ((t + prev_t).normalized()) * p_width * 0.5; + + if (p_antialiased) { + pline->lines.write[i] = p_points[i] + tangent; + pline->lines.write[p_points.size() * 2 - i - 1] = p_points[i] - tangent; + if (pline->line_colors.size() > 1) { + pline->line_colors.write[i] = p_colors[i]; + pline->line_colors.write[p_points.size() * 2 - i - 1] = p_colors[i]; + } + } + + pline->triangles.write[i * 2 + 0] = p_points[i] + tangent; + pline->triangles.write[i * 2 + 1] = p_points[i] - tangent; + + if (pline->triangle_colors.size() > 1) { + + pline->triangle_colors.write[i * 2 + 0] = p_colors[i]; + pline->triangle_colors.write[i * 2 + 1] = p_colors[i]; + } + + prev_t = t; + } +#endif + } +} + +void RenderingServerCanvas::canvas_item_add_multiline(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, float p_width) { + + ERR_FAIL_COND(p_points.size() < 2); + Item *canvas_item = canvas_item_owner.getornull(p_item); + ERR_FAIL_COND(!canvas_item); + + Item::CommandPolygon *pline = canvas_item->alloc_command<Item::CommandPolygon>(); + ERR_FAIL_COND(!pline); + + pline->texture_binding.create(canvas_item->texture_filter, canvas_item->texture_repeat, RID(), RID(), RID(), RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED, RID()); + + if (true || p_width <= 1) { +#define TODO make thick lines possible + + pline->primitive = RS::PRIMITIVE_LINES; + pline->specular_shininess = Color(1, 1, 1, 1); + pline->polygon.create(Vector<int>(), p_points, p_colors); + } else { + } +} + +void RenderingServerCanvas::canvas_item_add_rect(RID p_item, const Rect2 &p_rect, const Color &p_color) { + + Item *canvas_item = canvas_item_owner.getornull(p_item); + ERR_FAIL_COND(!canvas_item); + + Item::CommandRect *rect = canvas_item->alloc_command<Item::CommandRect>(); + ERR_FAIL_COND(!rect); + rect->modulate = p_color; + rect->rect = p_rect; +} + +void RenderingServerCanvas::canvas_item_add_circle(RID p_item, const Point2 &p_pos, float p_radius, const Color &p_color) { + + Item *canvas_item = canvas_item_owner.getornull(p_item); + ERR_FAIL_COND(!canvas_item); + + Item::CommandPolygon *circle = canvas_item->alloc_command<Item::CommandPolygon>(); + ERR_FAIL_COND(!circle); + + circle->texture_binding.create(canvas_item->texture_filter, canvas_item->texture_repeat, RID(), RID(), RID(), RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED, RID()); + + circle->primitive = RS::PRIMITIVE_TRIANGLES; + circle->specular_shininess = Color(1, 1, 1, 1); + + Vector<int> indices; + Vector<Vector2> points; + + static const int circle_points = 64; + + points.resize(circle_points); + for (int i = 0; i < circle_points; i++) { + float angle = (i / float(circle_points)) * 2 * Math_PI; + points.write[i].x = Math::cos(angle) * p_radius; + points.write[i].y = Math::sin(angle) * p_radius; + points.write[i] += p_pos; + } + indices.resize((circle_points - 2) * 3); + + for (int i = 0; i < circle_points - 2; i++) { + indices.write[i * 3 + 0] = 0; + indices.write[i * 3 + 1] = i + 1; + indices.write[i * 3 + 2] = i + 2; + } + + Vector<Color> color; + color.push_back(p_color); + circle->polygon.create(indices, points, color); +} + +void RenderingServerCanvas::canvas_item_add_texture_rect(RID p_item, const Rect2 &p_rect, RID p_texture, bool p_tile, const Color &p_modulate, bool p_transpose, RID p_normal_map, RID p_specular_map, const Color &p_specular_color_shininess, RenderingServer::CanvasItemTextureFilter p_filter, RenderingServer::CanvasItemTextureRepeat p_repeat) { + + Item *canvas_item = canvas_item_owner.getornull(p_item); + ERR_FAIL_COND(!canvas_item); + + Item::CommandRect *rect = canvas_item->alloc_command<Item::CommandRect>(); + ERR_FAIL_COND(!rect); + rect->modulate = p_modulate; + rect->rect = p_rect; + rect->flags = 0; + if (p_tile) { + rect->flags |= RasterizerCanvas::CANVAS_RECT_TILE; + rect->flags |= RasterizerCanvas::CANVAS_RECT_REGION; + rect->source = Rect2(0, 0, fabsf(p_rect.size.width), fabsf(p_rect.size.height)); + } + + if (p_rect.size.x < 0) { + + rect->flags |= RasterizerCanvas::CANVAS_RECT_FLIP_H; + rect->rect.size.x = -rect->rect.size.x; + } + if (p_rect.size.y < 0) { + + rect->flags |= RasterizerCanvas::CANVAS_RECT_FLIP_V; + rect->rect.size.y = -rect->rect.size.y; + } + if (p_transpose) { + rect->flags |= RasterizerCanvas::CANVAS_RECT_TRANSPOSE; + SWAP(rect->rect.size.x, rect->rect.size.y); + } + rect->texture_binding.create(canvas_item->texture_filter, canvas_item->texture_repeat, p_texture, p_normal_map, p_specular_map, p_filter, p_repeat, RID()); + rect->specular_shininess = p_specular_color_shininess; +} + +void RenderingServerCanvas::canvas_item_add_texture_rect_region(RID p_item, const Rect2 &p_rect, RID p_texture, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, RID p_normal_map, RID p_specular_map, const Color &p_specular_color_shininess, bool p_clip_uv, RenderingServer::CanvasItemTextureFilter p_filter, RenderingServer::CanvasItemTextureRepeat p_repeat) { + + Item *canvas_item = canvas_item_owner.getornull(p_item); + ERR_FAIL_COND(!canvas_item); + + Item::CommandRect *rect = canvas_item->alloc_command<Item::CommandRect>(); + ERR_FAIL_COND(!rect); + rect->modulate = p_modulate; + rect->rect = p_rect; + rect->texture_binding.create(canvas_item->texture_filter, canvas_item->texture_repeat, p_texture, p_normal_map, p_specular_map, p_filter, p_repeat, RID()); + rect->specular_shininess = p_specular_color_shininess; + rect->source = p_src_rect; + rect->flags = RasterizerCanvas::CANVAS_RECT_REGION; + + if (p_rect.size.x < 0) { + + rect->flags |= RasterizerCanvas::CANVAS_RECT_FLIP_H; + rect->rect.size.x = -rect->rect.size.x; + } + if (p_src_rect.size.x < 0) { + + rect->flags ^= RasterizerCanvas::CANVAS_RECT_FLIP_H; + rect->source.size.x = -rect->source.size.x; + } + if (p_rect.size.y < 0) { + + rect->flags |= RasterizerCanvas::CANVAS_RECT_FLIP_V; + rect->rect.size.y = -rect->rect.size.y; + } + if (p_src_rect.size.y < 0) { + + rect->flags ^= RasterizerCanvas::CANVAS_RECT_FLIP_V; + rect->source.size.y = -rect->source.size.y; + } + + if (p_transpose) { + rect->flags |= RasterizerCanvas::CANVAS_RECT_TRANSPOSE; + SWAP(rect->rect.size.x, rect->rect.size.y); + } + + if (p_clip_uv) { + rect->flags |= RasterizerCanvas::CANVAS_RECT_CLIP_UV; + } +} + +void RenderingServerCanvas::canvas_item_add_nine_patch(RID p_item, const Rect2 &p_rect, const Rect2 &p_source, RID p_texture, const Vector2 &p_topleft, const Vector2 &p_bottomright, RS::NinePatchAxisMode p_x_axis_mode, RS::NinePatchAxisMode p_y_axis_mode, bool p_draw_center, const Color &p_modulate, RID p_normal_map, RID p_specular_map, const Color &p_specular_color_shininess, RenderingServer::CanvasItemTextureFilter p_filter, RenderingServer::CanvasItemTextureRepeat p_repeat) { + + Item *canvas_item = canvas_item_owner.getornull(p_item); + ERR_FAIL_COND(!canvas_item); + + Item::CommandNinePatch *style = canvas_item->alloc_command<Item::CommandNinePatch>(); + ERR_FAIL_COND(!style); + style->texture_binding.create(canvas_item->texture_filter, canvas_item->texture_repeat, p_texture, p_normal_map, p_specular_map, p_filter, p_repeat, RID()); + style->specular_shininess = p_specular_color_shininess; + style->rect = p_rect; + style->source = p_source; + style->draw_center = p_draw_center; + style->color = p_modulate; + style->margin[MARGIN_LEFT] = p_topleft.x; + style->margin[MARGIN_TOP] = p_topleft.y; + style->margin[MARGIN_RIGHT] = p_bottomright.x; + style->margin[MARGIN_BOTTOM] = p_bottomright.y; + style->axis_x = p_x_axis_mode; + style->axis_y = p_y_axis_mode; +} +void RenderingServerCanvas::canvas_item_add_primitive(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs, RID p_texture, float p_width, RID p_normal_map, RID p_specular_map, const Color &p_specular_color_shininess, RenderingServer::CanvasItemTextureFilter p_filter, RenderingServer::CanvasItemTextureRepeat p_repeat) { + + uint32_t pc = p_points.size(); + ERR_FAIL_COND(pc == 0 || pc > 4); + + Item *canvas_item = canvas_item_owner.getornull(p_item); + ERR_FAIL_COND(!canvas_item); + + Item::CommandPrimitive *prim = canvas_item->alloc_command<Item::CommandPrimitive>(); + ERR_FAIL_COND(!prim); + + for (int i = 0; i < p_points.size(); i++) { + prim->points[i] = p_points[i]; + if (i < p_uvs.size()) { + prim->uvs[i] = p_uvs[i]; + } + if (i < p_colors.size()) { + prim->colors[i] = p_colors[i]; + } else if (p_colors.size()) { + prim->colors[i] = p_colors[0]; + } else { + prim->colors[i] = Color(1, 1, 1, 1); + } + } + + prim->point_count = p_points.size(); + + prim->texture_binding.create(canvas_item->texture_filter, canvas_item->texture_repeat, p_texture, p_normal_map, p_specular_map, p_filter, p_repeat, RID()); + prim->specular_shininess = p_specular_color_shininess; +} + +void RenderingServerCanvas::canvas_item_add_polygon(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs, RID p_texture, RID p_normal_map, RID p_specular_map, const Color &p_specular_color_shininess, RenderingServer::CanvasItemTextureFilter p_filter, RenderingServer::CanvasItemTextureRepeat p_repeat) { + + Item *canvas_item = canvas_item_owner.getornull(p_item); + ERR_FAIL_COND(!canvas_item); +#ifdef DEBUG_ENABLED + int pointcount = p_points.size(); + ERR_FAIL_COND(pointcount < 3); + int color_size = p_colors.size(); + int uv_size = p_uvs.size(); + ERR_FAIL_COND(color_size != 0 && color_size != 1 && color_size != pointcount); + ERR_FAIL_COND(uv_size != 0 && (uv_size != pointcount)); +#endif + Vector<int> indices = Geometry::triangulate_polygon(p_points); + ERR_FAIL_COND_MSG(indices.empty(), "Invalid polygon data, triangulation failed."); + + Item::CommandPolygon *polygon = canvas_item->alloc_command<Item::CommandPolygon>(); + ERR_FAIL_COND(!polygon); + polygon->primitive = RS::PRIMITIVE_TRIANGLES; + polygon->texture_binding.create(canvas_item->texture_filter, canvas_item->texture_repeat, p_texture, p_normal_map, p_specular_map, p_filter, p_repeat, RID()); + polygon->specular_shininess = p_specular_color_shininess; + polygon->polygon.create(indices, p_points, p_colors, p_uvs); +} + +void RenderingServerCanvas::canvas_item_add_triangle_array(RID p_item, const Vector<int> &p_indices, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs, const Vector<int> &p_bones, const Vector<float> &p_weights, RID p_texture, int p_count, RID p_normal_map, RID p_specular_map, const Color &p_specular_color_shininess, RenderingServer::CanvasItemTextureFilter p_filter, RenderingServer::CanvasItemTextureRepeat p_repeat) { + + Item *canvas_item = canvas_item_owner.getornull(p_item); + ERR_FAIL_COND(!canvas_item); + + int vertex_count = p_points.size(); + ERR_FAIL_COND(vertex_count == 0); + ERR_FAIL_COND(!p_colors.empty() && p_colors.size() != vertex_count && p_colors.size() != 1); + ERR_FAIL_COND(!p_uvs.empty() && p_uvs.size() != vertex_count); + ERR_FAIL_COND(!p_bones.empty() && p_bones.size() != vertex_count * 4); + ERR_FAIL_COND(!p_weights.empty() && p_weights.size() != vertex_count * 4); + + Vector<int> indices = p_indices; + + Item::CommandPolygon *polygon = canvas_item->alloc_command<Item::CommandPolygon>(); + ERR_FAIL_COND(!polygon); + polygon->texture_binding.create(canvas_item->texture_filter, canvas_item->texture_repeat, p_texture, p_normal_map, p_specular_map, p_filter, p_repeat, RID()); + polygon->specular_shininess = p_specular_color_shininess; + polygon->polygon.create(indices, p_points, p_colors, p_uvs, p_bones, p_weights); + + polygon->primitive = RS::PRIMITIVE_TRIANGLES; +} + +void RenderingServerCanvas::canvas_item_add_set_transform(RID p_item, const Transform2D &p_transform) { + + Item *canvas_item = canvas_item_owner.getornull(p_item); + ERR_FAIL_COND(!canvas_item); + + Item::CommandTransform *tr = canvas_item->alloc_command<Item::CommandTransform>(); + ERR_FAIL_COND(!tr); + tr->xform = p_transform; +} + +void RenderingServerCanvas::canvas_item_add_mesh(RID p_item, const RID &p_mesh, const Transform2D &p_transform, const Color &p_modulate, RID p_texture, RID p_normal_map, RID p_specular_map, const Color &p_specular_color_shininess, RenderingServer::CanvasItemTextureFilter p_filter, RenderingServer::CanvasItemTextureRepeat p_repeat) { + + Item *canvas_item = canvas_item_owner.getornull(p_item); + ERR_FAIL_COND(!canvas_item); + + Item::CommandMesh *m = canvas_item->alloc_command<Item::CommandMesh>(); + ERR_FAIL_COND(!m); + m->mesh = p_mesh; + m->texture_binding.create(canvas_item->texture_filter, canvas_item->texture_repeat, p_texture, p_normal_map, p_specular_map, p_filter, p_repeat, RID()); + m->specular_shininess = p_specular_color_shininess; + m->transform = p_transform; + m->modulate = p_modulate; +} +void RenderingServerCanvas::canvas_item_add_particles(RID p_item, RID p_particles, RID p_texture, RID p_normal_map, RID p_specular_map, const Color &p_specular_color_shininess, RenderingServer::CanvasItemTextureFilter p_filter, RenderingServer::CanvasItemTextureRepeat p_repeat) { + + Item *canvas_item = canvas_item_owner.getornull(p_item); + ERR_FAIL_COND(!canvas_item); + + Item::CommandParticles *part = canvas_item->alloc_command<Item::CommandParticles>(); + ERR_FAIL_COND(!part); + part->particles = p_particles; + part->texture_binding.create(canvas_item->texture_filter, canvas_item->texture_repeat, p_texture, p_normal_map, p_specular_map, p_filter, p_repeat, RID()); + part->specular_shininess = p_specular_color_shininess; + + //take the chance and request processing for them, at least once until they become visible again + RSG::storage->particles_request_process(p_particles); +} + +void RenderingServerCanvas::canvas_item_add_multimesh(RID p_item, RID p_mesh, RID p_texture, RID p_normal_map, RID p_specular_map, const Color &p_specular_color_shininess, RenderingServer::CanvasItemTextureFilter p_filter, RenderingServer::CanvasItemTextureRepeat p_repeat) { + + Item *canvas_item = canvas_item_owner.getornull(p_item); + ERR_FAIL_COND(!canvas_item); + + Item::CommandMultiMesh *mm = canvas_item->alloc_command<Item::CommandMultiMesh>(); + ERR_FAIL_COND(!mm); + mm->multimesh = p_mesh; + mm->texture_binding.create(canvas_item->texture_filter, canvas_item->texture_repeat, p_texture, p_normal_map, p_specular_map, p_filter, p_repeat, mm->multimesh); + mm->specular_shininess = p_specular_color_shininess; +} + +void RenderingServerCanvas::canvas_item_add_clip_ignore(RID p_item, bool p_ignore) { + + Item *canvas_item = canvas_item_owner.getornull(p_item); + ERR_FAIL_COND(!canvas_item); + + Item::CommandClipIgnore *ci = canvas_item->alloc_command<Item::CommandClipIgnore>(); + ERR_FAIL_COND(!ci); + ci->ignore = p_ignore; +} +void RenderingServerCanvas::canvas_item_set_sort_children_by_y(RID p_item, bool p_enable) { + + Item *canvas_item = canvas_item_owner.getornull(p_item); + ERR_FAIL_COND(!canvas_item); + + canvas_item->sort_y = p_enable; + + _mark_ysort_dirty(canvas_item, canvas_item_owner); +} +void RenderingServerCanvas::canvas_item_set_z_index(RID p_item, int p_z) { + + ERR_FAIL_COND(p_z < RS::CANVAS_ITEM_Z_MIN || p_z > RS::CANVAS_ITEM_Z_MAX); + + Item *canvas_item = canvas_item_owner.getornull(p_item); + ERR_FAIL_COND(!canvas_item); + + canvas_item->z_index = p_z; +} +void RenderingServerCanvas::canvas_item_set_z_as_relative_to_parent(RID p_item, bool p_enable) { + + Item *canvas_item = canvas_item_owner.getornull(p_item); + ERR_FAIL_COND(!canvas_item); + + canvas_item->z_relative = p_enable; +} + +void RenderingServerCanvas::canvas_item_attach_skeleton(RID p_item, RID p_skeleton) { + + Item *canvas_item = canvas_item_owner.getornull(p_item); + ERR_FAIL_COND(!canvas_item); + + canvas_item->skeleton = p_skeleton; +} + +void RenderingServerCanvas::canvas_item_set_copy_to_backbuffer(RID p_item, bool p_enable, const Rect2 &p_rect) { + + Item *canvas_item = canvas_item_owner.getornull(p_item); + ERR_FAIL_COND(!canvas_item); + if (bool(canvas_item->copy_back_buffer != NULL) != p_enable) { + if (p_enable) { + canvas_item->copy_back_buffer = memnew(RasterizerCanvas::Item::CopyBackBuffer); + } else { + memdelete(canvas_item->copy_back_buffer); + canvas_item->copy_back_buffer = NULL; + } + } + + if (p_enable) { + canvas_item->copy_back_buffer->rect = p_rect; + canvas_item->copy_back_buffer->full = p_rect == Rect2(); + } +} + +void RenderingServerCanvas::canvas_item_clear(RID p_item) { + + Item *canvas_item = canvas_item_owner.getornull(p_item); + ERR_FAIL_COND(!canvas_item); + + canvas_item->clear(); +} +void RenderingServerCanvas::canvas_item_set_draw_index(RID p_item, int p_index) { + + Item *canvas_item = canvas_item_owner.getornull(p_item); + ERR_FAIL_COND(!canvas_item); + + canvas_item->index = p_index; + + if (canvas_item_owner.owns(canvas_item->parent)) { + Item *canvas_item_parent = canvas_item_owner.getornull(canvas_item->parent); + canvas_item_parent->children_order_dirty = true; + return; + } + + Canvas *canvas = canvas_owner.getornull(canvas_item->parent); + if (canvas) { + canvas->children_order_dirty = true; + return; + } +} + +void RenderingServerCanvas::canvas_item_set_material(RID p_item, RID p_material) { + + Item *canvas_item = canvas_item_owner.getornull(p_item); + ERR_FAIL_COND(!canvas_item); + + canvas_item->material = p_material; +} + +void RenderingServerCanvas::canvas_item_set_use_parent_material(RID p_item, bool p_enable) { + + Item *canvas_item = canvas_item_owner.getornull(p_item); + ERR_FAIL_COND(!canvas_item); + + canvas_item->use_parent_material = p_enable; +} + +RID RenderingServerCanvas::canvas_light_create() { + + RasterizerCanvas::Light *clight = memnew(RasterizerCanvas::Light); + clight->light_internal = RSG::canvas_render->light_create(); + return canvas_light_owner.make_rid(clight); +} +void RenderingServerCanvas::canvas_light_attach_to_canvas(RID p_light, RID p_canvas) { + + RasterizerCanvas::Light *clight = canvas_light_owner.getornull(p_light); + ERR_FAIL_COND(!clight); + + if (clight->canvas.is_valid()) { + + Canvas *canvas = canvas_owner.getornull(clight->canvas); + canvas->lights.erase(clight); + } + + if (!canvas_owner.owns(p_canvas)) + p_canvas = RID(); + + clight->canvas = p_canvas; + + if (clight->canvas.is_valid()) { + + Canvas *canvas = canvas_owner.getornull(clight->canvas); + canvas->lights.insert(clight); + } +} + +void RenderingServerCanvas::canvas_light_set_enabled(RID p_light, bool p_enabled) { + + RasterizerCanvas::Light *clight = canvas_light_owner.getornull(p_light); + ERR_FAIL_COND(!clight); + + clight->enabled = p_enabled; +} +void RenderingServerCanvas::canvas_light_set_scale(RID p_light, float p_scale) { + + RasterizerCanvas::Light *clight = canvas_light_owner.getornull(p_light); + ERR_FAIL_COND(!clight); + + clight->scale = p_scale; +} +void RenderingServerCanvas::canvas_light_set_transform(RID p_light, const Transform2D &p_transform) { + + RasterizerCanvas::Light *clight = canvas_light_owner.getornull(p_light); + ERR_FAIL_COND(!clight); + + clight->xform = p_transform; +} +void RenderingServerCanvas::canvas_light_set_texture(RID p_light, RID p_texture) { + + RasterizerCanvas::Light *clight = canvas_light_owner.getornull(p_light); + ERR_FAIL_COND(!clight); + + clight->texture = p_texture; + clight->version++; + RSG::canvas_render->light_set_texture(clight->light_internal, p_texture); +} +void RenderingServerCanvas::canvas_light_set_texture_offset(RID p_light, const Vector2 &p_offset) { + + RasterizerCanvas::Light *clight = canvas_light_owner.getornull(p_light); + ERR_FAIL_COND(!clight); + + clight->texture_offset = p_offset; +} +void RenderingServerCanvas::canvas_light_set_color(RID p_light, const Color &p_color) { + + RasterizerCanvas::Light *clight = canvas_light_owner.getornull(p_light); + ERR_FAIL_COND(!clight); + + clight->color = p_color; +} +void RenderingServerCanvas::canvas_light_set_height(RID p_light, float p_height) { + + RasterizerCanvas::Light *clight = canvas_light_owner.getornull(p_light); + ERR_FAIL_COND(!clight); + + clight->height = p_height; +} +void RenderingServerCanvas::canvas_light_set_energy(RID p_light, float p_energy) { + + RasterizerCanvas::Light *clight = canvas_light_owner.getornull(p_light); + ERR_FAIL_COND(!clight); + + clight->energy = p_energy; +} +void RenderingServerCanvas::canvas_light_set_z_range(RID p_light, int p_min_z, int p_max_z) { + + RasterizerCanvas::Light *clight = canvas_light_owner.getornull(p_light); + ERR_FAIL_COND(!clight); + + clight->z_min = p_min_z; + clight->z_max = p_max_z; +} +void RenderingServerCanvas::canvas_light_set_layer_range(RID p_light, int p_min_layer, int p_max_layer) { + + RasterizerCanvas::Light *clight = canvas_light_owner.getornull(p_light); + ERR_FAIL_COND(!clight); + + clight->layer_max = p_max_layer; + clight->layer_min = p_min_layer; +} +void RenderingServerCanvas::canvas_light_set_item_cull_mask(RID p_light, int p_mask) { + + RasterizerCanvas::Light *clight = canvas_light_owner.getornull(p_light); + ERR_FAIL_COND(!clight); + + clight->item_mask = p_mask; +} +void RenderingServerCanvas::canvas_light_set_item_shadow_cull_mask(RID p_light, int p_mask) { + + RasterizerCanvas::Light *clight = canvas_light_owner.getornull(p_light); + ERR_FAIL_COND(!clight); + + clight->item_shadow_mask = p_mask; +} +void RenderingServerCanvas::canvas_light_set_mode(RID p_light, RS::CanvasLightMode p_mode) { + + RasterizerCanvas::Light *clight = canvas_light_owner.getornull(p_light); + ERR_FAIL_COND(!clight); + + clight->mode = p_mode; +} + +void RenderingServerCanvas::canvas_light_set_shadow_enabled(RID p_light, bool p_enabled) { + + RasterizerCanvas::Light *clight = canvas_light_owner.getornull(p_light); + ERR_FAIL_COND(!clight); + + if (clight->use_shadow == p_enabled) { + return; + } + clight->use_shadow = p_enabled; + clight->version++; + RSG::canvas_render->light_set_use_shadow(clight->light_internal, clight->use_shadow, clight->shadow_buffer_size); +} + +void RenderingServerCanvas::canvas_light_set_shadow_buffer_size(RID p_light, int p_size) { + + ERR_FAIL_COND(p_size < 32 || p_size > 16384); + + RasterizerCanvas::Light *clight = canvas_light_owner.getornull(p_light); + ERR_FAIL_COND(!clight); + + int new_size = next_power_of_2(p_size); + if (new_size == clight->shadow_buffer_size) + return; + + clight->shadow_buffer_size = next_power_of_2(p_size); + clight->version++; + + RSG::canvas_render->light_set_use_shadow(clight->light_internal, clight->use_shadow, clight->shadow_buffer_size); +} + +void RenderingServerCanvas::canvas_light_set_shadow_filter(RID p_light, RS::CanvasLightShadowFilter p_filter) { + + RasterizerCanvas::Light *clight = canvas_light_owner.getornull(p_light); + ERR_FAIL_COND(!clight); + + clight->shadow_filter = p_filter; +} +void RenderingServerCanvas::canvas_light_set_shadow_color(RID p_light, const Color &p_color) { + + RasterizerCanvas::Light *clight = canvas_light_owner.getornull(p_light); + ERR_FAIL_COND(!clight); + + clight->shadow_color = p_color; +} + +void RenderingServerCanvas::canvas_light_set_shadow_smooth(RID p_light, float p_smooth) { + + RasterizerCanvas::Light *clight = canvas_light_owner.getornull(p_light); + ERR_FAIL_COND(!clight); + clight->shadow_smooth = p_smooth; +} + +RID RenderingServerCanvas::canvas_light_occluder_create() { + + RasterizerCanvas::LightOccluderInstance *occluder = memnew(RasterizerCanvas::LightOccluderInstance); + + return canvas_light_occluder_owner.make_rid(occluder); +} +void RenderingServerCanvas::canvas_light_occluder_attach_to_canvas(RID p_occluder, RID p_canvas) { + + RasterizerCanvas::LightOccluderInstance *occluder = canvas_light_occluder_owner.getornull(p_occluder); + ERR_FAIL_COND(!occluder); + + if (occluder->canvas.is_valid()) { + + Canvas *canvas = canvas_owner.getornull(occluder->canvas); + canvas->occluders.erase(occluder); + } + + if (!canvas_owner.owns(p_canvas)) + p_canvas = RID(); + + occluder->canvas = p_canvas; + + if (occluder->canvas.is_valid()) { + + Canvas *canvas = canvas_owner.getornull(occluder->canvas); + canvas->occluders.insert(occluder); + } +} +void RenderingServerCanvas::canvas_light_occluder_set_enabled(RID p_occluder, bool p_enabled) { + + RasterizerCanvas::LightOccluderInstance *occluder = canvas_light_occluder_owner.getornull(p_occluder); + ERR_FAIL_COND(!occluder); + + occluder->enabled = p_enabled; +} +void RenderingServerCanvas::canvas_light_occluder_set_polygon(RID p_occluder, RID p_polygon) { + + RasterizerCanvas::LightOccluderInstance *occluder = canvas_light_occluder_owner.getornull(p_occluder); + ERR_FAIL_COND(!occluder); + + if (occluder->polygon.is_valid()) { + LightOccluderPolygon *occluder_poly = canvas_light_occluder_polygon_owner.getornull(p_polygon); + if (occluder_poly) { + occluder_poly->owners.erase(occluder); + } + } + + occluder->polygon = p_polygon; + occluder->occluder = RID(); + + if (occluder->polygon.is_valid()) { + LightOccluderPolygon *occluder_poly = canvas_light_occluder_polygon_owner.getornull(p_polygon); + if (!occluder_poly) { + occluder->polygon = RID(); + ERR_FAIL_COND(!occluder_poly); + } else { + occluder_poly->owners.insert(occluder); + occluder->occluder = occluder_poly->occluder; + occluder->aabb_cache = occluder_poly->aabb; + occluder->cull_cache = occluder_poly->cull_mode; + } + } +} +void RenderingServerCanvas::canvas_light_occluder_set_transform(RID p_occluder, const Transform2D &p_xform) { + + RasterizerCanvas::LightOccluderInstance *occluder = canvas_light_occluder_owner.getornull(p_occluder); + ERR_FAIL_COND(!occluder); + + occluder->xform = p_xform; +} +void RenderingServerCanvas::canvas_light_occluder_set_light_mask(RID p_occluder, int p_mask) { + + RasterizerCanvas::LightOccluderInstance *occluder = canvas_light_occluder_owner.getornull(p_occluder); + ERR_FAIL_COND(!occluder); + + occluder->light_mask = p_mask; +} + +RID RenderingServerCanvas::canvas_occluder_polygon_create() { + + LightOccluderPolygon *occluder_poly = memnew(LightOccluderPolygon); + occluder_poly->occluder = RSG::canvas_render->occluder_polygon_create(); + return canvas_light_occluder_polygon_owner.make_rid(occluder_poly); +} +void RenderingServerCanvas::canvas_occluder_polygon_set_shape(RID p_occluder_polygon, const Vector<Vector2> &p_shape, bool p_closed) { + + if (p_shape.size() < 3) { + canvas_occluder_polygon_set_shape_as_lines(p_occluder_polygon, p_shape); + return; + } + + Vector<Vector2> lines; + int lc = p_shape.size() * 2; + + lines.resize(lc - (p_closed ? 0 : 2)); + { + Vector2 *w = lines.ptrw(); + const Vector2 *r = p_shape.ptr(); + + int max = lc / 2; + if (!p_closed) { + max--; + } + for (int i = 0; i < max; i++) { + + Vector2 a = r[i]; + Vector2 b = r[(i + 1) % (lc / 2)]; + w[i * 2 + 0] = a; + w[i * 2 + 1] = b; + } + } + + canvas_occluder_polygon_set_shape_as_lines(p_occluder_polygon, lines); +} +void RenderingServerCanvas::canvas_occluder_polygon_set_shape_as_lines(RID p_occluder_polygon, const Vector<Vector2> &p_shape) { + + LightOccluderPolygon *occluder_poly = canvas_light_occluder_polygon_owner.getornull(p_occluder_polygon); + ERR_FAIL_COND(!occluder_poly); + ERR_FAIL_COND(p_shape.size() & 1); + + int lc = p_shape.size(); + occluder_poly->aabb = Rect2(); + { + const Vector2 *r = p_shape.ptr(); + for (int i = 0; i < lc; i++) { + if (i == 0) + occluder_poly->aabb.position = r[i]; + else + occluder_poly->aabb.expand_to(r[i]); + } + } + + RSG::canvas_render->occluder_polygon_set_shape_as_lines(occluder_poly->occluder, p_shape); + for (Set<RasterizerCanvas::LightOccluderInstance *>::Element *E = occluder_poly->owners.front(); E; E = E->next()) { + E->get()->aabb_cache = occluder_poly->aabb; + } +} + +void RenderingServerCanvas::canvas_occluder_polygon_set_cull_mode(RID p_occluder_polygon, RS::CanvasOccluderPolygonCullMode p_mode) { + + LightOccluderPolygon *occluder_poly = canvas_light_occluder_polygon_owner.getornull(p_occluder_polygon); + ERR_FAIL_COND(!occluder_poly); + occluder_poly->cull_mode = p_mode; + RSG::canvas_render->occluder_polygon_set_cull_mode(occluder_poly->occluder, p_mode); + for (Set<RasterizerCanvas::LightOccluderInstance *>::Element *E = occluder_poly->owners.front(); E; E = E->next()) { + E->get()->cull_cache = p_mode; + } +} + +bool RenderingServerCanvas::free(RID p_rid) { + + if (canvas_owner.owns(p_rid)) { + + Canvas *canvas = canvas_owner.getornull(p_rid); + ERR_FAIL_COND_V(!canvas, false); + + while (canvas->viewports.size()) { + + RenderingServerViewport::Viewport *vp = RSG::viewport->viewport_owner.getornull(canvas->viewports.front()->get()); + ERR_FAIL_COND_V(!vp, true); + + Map<RID, RenderingServerViewport::Viewport::CanvasData>::Element *E = vp->canvas_map.find(p_rid); + ERR_FAIL_COND_V(!E, true); + vp->canvas_map.erase(p_rid); + + canvas->viewports.erase(canvas->viewports.front()); + } + + for (int i = 0; i < canvas->child_items.size(); i++) { + + canvas->child_items[i].item->parent = RID(); + } + + for (Set<RasterizerCanvas::Light *>::Element *E = canvas->lights.front(); E; E = E->next()) { + + E->get()->canvas = RID(); + } + + for (Set<RasterizerCanvas::LightOccluderInstance *>::Element *E = canvas->occluders.front(); E; E = E->next()) { + + E->get()->canvas = RID(); + } + + canvas_owner.free(p_rid); + + memdelete(canvas); + + } else if (canvas_item_owner.owns(p_rid)) { + + Item *canvas_item = canvas_item_owner.getornull(p_rid); + ERR_FAIL_COND_V(!canvas_item, true); + + if (canvas_item->parent.is_valid()) { + + if (canvas_owner.owns(canvas_item->parent)) { + + Canvas *canvas = canvas_owner.getornull(canvas_item->parent); + canvas->erase_item(canvas_item); + } else if (canvas_item_owner.owns(canvas_item->parent)) { + + Item *item_owner = canvas_item_owner.getornull(canvas_item->parent); + item_owner->child_items.erase(canvas_item); + + if (item_owner->sort_y) { + _mark_ysort_dirty(item_owner, canvas_item_owner); + } + } + } + + for (int i = 0; i < canvas_item->child_items.size(); i++) { + + canvas_item->child_items[i]->parent = RID(); + } + + /* + if (canvas_item->material) { + canvas_item->material->owners.erase(canvas_item); + } + */ + + canvas_item_owner.free(p_rid); + + memdelete(canvas_item); + + } else if (canvas_light_owner.owns(p_rid)) { + + RasterizerCanvas::Light *canvas_light = canvas_light_owner.getornull(p_rid); + ERR_FAIL_COND_V(!canvas_light, true); + + if (canvas_light->canvas.is_valid()) { + Canvas *canvas = canvas_owner.getornull(canvas_light->canvas); + if (canvas) + canvas->lights.erase(canvas_light); + } + + RSG::canvas_render->free(canvas_light->light_internal); + + canvas_light_owner.free(p_rid); + memdelete(canvas_light); + + } else if (canvas_light_occluder_owner.owns(p_rid)) { + + RasterizerCanvas::LightOccluderInstance *occluder = canvas_light_occluder_owner.getornull(p_rid); + ERR_FAIL_COND_V(!occluder, true); + + if (occluder->polygon.is_valid()) { + + LightOccluderPolygon *occluder_poly = canvas_light_occluder_polygon_owner.getornull(occluder->polygon); + if (occluder_poly) { + occluder_poly->owners.erase(occluder); + } + } + + if (occluder->canvas.is_valid() && canvas_owner.owns(occluder->canvas)) { + + Canvas *canvas = canvas_owner.getornull(occluder->canvas); + canvas->occluders.erase(occluder); + } + + canvas_light_occluder_owner.free(p_rid); + memdelete(occluder); + + } else if (canvas_light_occluder_polygon_owner.owns(p_rid)) { + + LightOccluderPolygon *occluder_poly = canvas_light_occluder_polygon_owner.getornull(p_rid); + ERR_FAIL_COND_V(!occluder_poly, true); + RSG::canvas_render->free(occluder_poly->occluder); + + while (occluder_poly->owners.size()) { + + occluder_poly->owners.front()->get()->polygon = RID(); + occluder_poly->owners.erase(occluder_poly->owners.front()); + } + + canvas_light_occluder_polygon_owner.free(p_rid); + memdelete(occluder_poly); + } else { + return false; + } + + return true; +} + +RenderingServerCanvas::RenderingServerCanvas() { + + z_list = (RasterizerCanvas::Item **)memalloc(z_range * sizeof(RasterizerCanvas::Item *)); + z_last_list = (RasterizerCanvas::Item **)memalloc(z_range * sizeof(RasterizerCanvas::Item *)); + + disable_scale = false; +} + +RenderingServerCanvas::~RenderingServerCanvas() { + + memfree(z_list); + memfree(z_last_list); +} diff --git a/servers/rendering/rendering_server_canvas.h b/servers/rendering/rendering_server_canvas.h new file mode 100644 index 0000000000..579bcd36e7 --- /dev/null +++ b/servers/rendering/rendering_server_canvas.h @@ -0,0 +1,270 @@ +/*************************************************************************/ +/* rendering_server_canvas.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef VISUALSERVERCANVAS_H +#define VISUALSERVERCANVAS_H + +#include "rasterizer.h" +#include "rendering_server_viewport.h" + +class RenderingServerCanvas { +public: + struct Item : public RasterizerCanvas::Item { + + RID parent; // canvas it belongs to + List<Item *>::Element *E; + int z_index; + bool z_relative; + bool sort_y; + Color modulate; + Color self_modulate; + bool use_parent_material; + int index; + bool children_order_dirty; + int ysort_children_count; + Color ysort_modulate; + Transform2D ysort_xform; + Vector2 ysort_pos; + RS::CanvasItemTextureFilter texture_filter; + RS::CanvasItemTextureRepeat texture_repeat; + + Vector<Item *> child_items; + + Item() { + children_order_dirty = true; + E = NULL; + z_index = 0; + modulate = Color(1, 1, 1, 1); + self_modulate = Color(1, 1, 1, 1); + sort_y = false; + use_parent_material = false; + z_relative = true; + index = 0; + ysort_children_count = -1; + ysort_xform = Transform2D(); + ysort_pos = Vector2(); + texture_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT; + texture_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT; + } + }; + + struct ItemIndexSort { + + _FORCE_INLINE_ bool operator()(const Item *p_left, const Item *p_right) const { + + return p_left->index < p_right->index; + } + }; + + struct ItemPtrSort { + + _FORCE_INLINE_ bool operator()(const Item *p_left, const Item *p_right) const { + + if (Math::is_equal_approx(p_left->ysort_pos.y, p_right->ysort_pos.y)) + return p_left->ysort_pos.x < p_right->ysort_pos.x; + + return p_left->ysort_pos.y < p_right->ysort_pos.y; + } + }; + + struct LightOccluderPolygon { + + bool active; + Rect2 aabb; + RS::CanvasOccluderPolygonCullMode cull_mode; + RID occluder; + Set<RasterizerCanvas::LightOccluderInstance *> owners; + + LightOccluderPolygon() { + active = false; + cull_mode = RS::CANVAS_OCCLUDER_POLYGON_CULL_DISABLED; + } + }; + + RID_PtrOwner<LightOccluderPolygon> canvas_light_occluder_polygon_owner; + + RID_PtrOwner<RasterizerCanvas::LightOccluderInstance> canvas_light_occluder_owner; + + struct Canvas : public RenderingServerViewport::CanvasBase { + + Set<RID> viewports; + struct ChildItem { + + Point2 mirror; + Item *item; + bool operator<(const ChildItem &p_item) const { + return item->index < p_item.item->index; + } + }; + + Set<RasterizerCanvas::Light *> lights; + + Set<RasterizerCanvas::LightOccluderInstance *> occluders; + + bool children_order_dirty; + Vector<ChildItem> child_items; + Color modulate; + RID parent; + float parent_scale; + + int find_item(Item *p_item) { + for (int i = 0; i < child_items.size(); i++) { + if (child_items[i].item == p_item) + return i; + } + return -1; + } + void erase_item(Item *p_item) { + int idx = find_item(p_item); + if (idx >= 0) + child_items.remove(idx); + } + + Canvas() { + modulate = Color(1, 1, 1, 1); + children_order_dirty = true; + parent_scale = 1.0; + } + }; + + mutable RID_PtrOwner<Canvas> canvas_owner; + RID_PtrOwner<Item> canvas_item_owner; + RID_PtrOwner<RasterizerCanvas::Light> canvas_light_owner; + + bool disable_scale; + +private: + void _render_canvas_item_tree(RID p_to_render_target, Canvas::ChildItem *p_child_items, int p_child_item_count, Item *p_canvas_item, const Transform2D &p_transform, const Rect2 &p_clip_rect, const Color &p_modulate, RasterizerCanvas::Light *p_lights); + void _cull_canvas_item(Item *p_canvas_item, const Transform2D &p_transform, const Rect2 &p_clip_rect, const Color &p_modulate, int p_z, RasterizerCanvas::Item **z_list, RasterizerCanvas::Item **z_last_list, Item *p_canvas_clip, Item *p_material_owner); + void _light_mask_canvas_items(int p_z, RasterizerCanvas::Item *p_canvas_item, RasterizerCanvas::Light *p_masked_lights); + + RasterizerCanvas::Item **z_list; + RasterizerCanvas::Item **z_last_list; + +public: + void render_canvas(RID p_render_target, Canvas *p_canvas, const Transform2D &p_transform, RasterizerCanvas::Light *p_lights, RasterizerCanvas::Light *p_masked_lights, const Rect2 &p_clip_rect); + + RID canvas_create(); + void canvas_set_item_mirroring(RID p_canvas, RID p_item, const Point2 &p_mirroring); + void canvas_set_modulate(RID p_canvas, const Color &p_color); + void canvas_set_parent(RID p_canvas, RID p_parent, float p_scale); + void canvas_set_disable_scale(bool p_disable); + + RID canvas_item_create(); + void canvas_item_set_parent(RID p_item, RID p_parent); + + void canvas_item_set_visible(RID p_item, bool p_visible); + void canvas_item_set_light_mask(RID p_item, int p_mask); + + void canvas_item_set_transform(RID p_item, const Transform2D &p_transform); + void canvas_item_set_clip(RID p_item, bool p_clip); + void canvas_item_set_distance_field_mode(RID p_item, bool p_enable); + void canvas_item_set_custom_rect(RID p_item, bool p_custom_rect, const Rect2 &p_rect = Rect2()); + void canvas_item_set_modulate(RID p_item, const Color &p_color); + void canvas_item_set_self_modulate(RID p_item, const Color &p_color); + + void canvas_item_set_draw_behind_parent(RID p_item, bool p_enable); + + void canvas_item_set_update_when_visible(RID p_item, bool p_update); + + void canvas_item_set_default_texture_filter(RID p_item, RS::CanvasItemTextureFilter p_filter); + void canvas_item_set_default_texture_repeat(RID p_item, RS::CanvasItemTextureRepeat p_repeat); + + void canvas_item_add_line(RID p_item, const Point2 &p_from, const Point2 &p_to, const Color &p_color, float p_width = 1.0); + void canvas_item_add_polyline(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, float p_width = 1.0); + void canvas_item_add_multiline(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, float p_width = 1.0); + void canvas_item_add_rect(RID p_item, const Rect2 &p_rect, const Color &p_color); + void canvas_item_add_circle(RID p_item, const Point2 &p_pos, float p_radius, const Color &p_color); + void canvas_item_add_texture_rect(RID p_item, const Rect2 &p_rect, RID p_texture, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, RID p_normal_map = RID(), RID p_specular_map = RID(), const Color &p_specular_color_shininess = Color(), RS::CanvasItemTextureFilter p_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT, RS::CanvasItemTextureRepeat p_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT); + void canvas_item_add_texture_rect_region(RID p_item, const Rect2 &p_rect, RID p_texture, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, RID p_normal_map = RID(), RID p_specular_map = RID(), const Color &p_specular_color_shininess = Color(), bool p_clip_uv = false, RS::CanvasItemTextureFilter p_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT, RS::CanvasItemTextureRepeat p_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT); + void canvas_item_add_nine_patch(RID p_item, const Rect2 &p_rect, const Rect2 &p_source, RID p_texture, const Vector2 &p_topleft, const Vector2 &p_bottomright, RS::NinePatchAxisMode p_x_axis_mode = RS::NINE_PATCH_STRETCH, RS::NinePatchAxisMode p_y_axis_mode = RS::NINE_PATCH_STRETCH, bool p_draw_center = true, const Color &p_modulate = Color(1, 1, 1), RID p_normal_map = RID(), RID p_specular_map = RID(), const Color &p_specular_color_shininess = Color(), RS::CanvasItemTextureFilter p_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT, RS::CanvasItemTextureRepeat p_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT); + void canvas_item_add_primitive(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs, RID p_texture, float p_width = 1.0, RID p_normal_map = RID(), RID p_specular_map = RID(), const Color &p_specular_color_shininess = Color(), RS::CanvasItemTextureFilter p_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT, RS::CanvasItemTextureRepeat p_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT); + void canvas_item_add_polygon(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), RID p_texture = RID(), RID p_normal_map = RID(), RID p_specular_map = RID(), const Color &p_specular_color_shininess = Color(), RS::CanvasItemTextureFilter p_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT, RS::CanvasItemTextureRepeat p_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT); + void canvas_item_add_triangle_array(RID p_item, const Vector<int> &p_indices, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), const Vector<int> &p_bones = Vector<int>(), const Vector<float> &p_weights = Vector<float>(), RID p_texture = RID(), int p_count = -1, RID p_normal_map = RID(), RID p_specular_map = RID(), const Color &p_specular_color_shininess = Color(), RS::CanvasItemTextureFilter p_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT, RS::CanvasItemTextureRepeat p_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT); + void canvas_item_add_mesh(RID p_item, const RID &p_mesh, const Transform2D &p_transform = Transform2D(), const Color &p_modulate = Color(1, 1, 1), RID p_texture = RID(), RID p_normal_map = RID(), RID p_specular_map = RID(), const Color &p_specular_color_shininess = Color(), RS::CanvasItemTextureFilter p_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT, RS::CanvasItemTextureRepeat p_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT); + void canvas_item_add_multimesh(RID p_item, RID p_mesh, RID p_texture = RID(), RID p_normal_map = RID(), RID p_specular_map = RID(), const Color &p_specular_color_shininess = Color(), RS::CanvasItemTextureFilter p_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT, RS::CanvasItemTextureRepeat p_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT); + void canvas_item_add_particles(RID p_item, RID p_particles, RID p_texture, RID p_normal_map, RID p_specular_map = RID(), const Color &p_specular_color_shininess = Color(), RS::CanvasItemTextureFilter p_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT, RS::CanvasItemTextureRepeat p_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT); + void canvas_item_add_set_transform(RID p_item, const Transform2D &p_transform); + void canvas_item_add_clip_ignore(RID p_item, bool p_ignore); + void canvas_item_set_sort_children_by_y(RID p_item, bool p_enable); + void canvas_item_set_z_index(RID p_item, int p_z); + void canvas_item_set_z_as_relative_to_parent(RID p_item, bool p_enable); + void canvas_item_set_copy_to_backbuffer(RID p_item, bool p_enable, const Rect2 &p_rect); + void canvas_item_attach_skeleton(RID p_item, RID p_skeleton); + + void canvas_item_clear(RID p_item); + void canvas_item_set_draw_index(RID p_item, int p_index); + + void canvas_item_set_material(RID p_item, RID p_material); + + void canvas_item_set_use_parent_material(RID p_item, bool p_enable); + + RID canvas_light_create(); + void canvas_light_attach_to_canvas(RID p_light, RID p_canvas); + void canvas_light_set_enabled(RID p_light, bool p_enabled); + void canvas_light_set_scale(RID p_light, float p_scale); + void canvas_light_set_transform(RID p_light, const Transform2D &p_transform); + void canvas_light_set_texture(RID p_light, RID p_texture); + void canvas_light_set_texture_offset(RID p_light, const Vector2 &p_offset); + void canvas_light_set_color(RID p_light, const Color &p_color); + void canvas_light_set_height(RID p_light, float p_height); + void canvas_light_set_energy(RID p_light, float p_energy); + void canvas_light_set_z_range(RID p_light, int p_min_z, int p_max_z); + void canvas_light_set_layer_range(RID p_light, int p_min_layer, int p_max_layer); + void canvas_light_set_item_cull_mask(RID p_light, int p_mask); + void canvas_light_set_item_shadow_cull_mask(RID p_light, int p_mask); + + void canvas_light_set_mode(RID p_light, RS::CanvasLightMode p_mode); + + void canvas_light_set_shadow_enabled(RID p_light, bool p_enabled); + void canvas_light_set_shadow_buffer_size(RID p_light, int p_size); + void canvas_light_set_shadow_filter(RID p_light, RS::CanvasLightShadowFilter p_filter); + void canvas_light_set_shadow_color(RID p_light, const Color &p_color); + void canvas_light_set_shadow_smooth(RID p_light, float p_smooth); + + RID canvas_light_occluder_create(); + void canvas_light_occluder_attach_to_canvas(RID p_occluder, RID p_canvas); + void canvas_light_occluder_set_enabled(RID p_occluder, bool p_enabled); + void canvas_light_occluder_set_polygon(RID p_occluder, RID p_polygon); + void canvas_light_occluder_set_transform(RID p_occluder, const Transform2D &p_xform); + void canvas_light_occluder_set_light_mask(RID p_occluder, int p_mask); + + RID canvas_occluder_polygon_create(); + void canvas_occluder_polygon_set_shape(RID p_occluder_polygon, const Vector<Vector2> &p_shape, bool p_closed); + void canvas_occluder_polygon_set_shape_as_lines(RID p_occluder_polygon, const Vector<Vector2> &p_shape); + + void canvas_occluder_polygon_set_cull_mode(RID p_occluder_polygon, RS::CanvasOccluderPolygonCullMode p_mode); + + bool free(RID p_rid); + RenderingServerCanvas(); + ~RenderingServerCanvas(); +}; + +#endif // VISUALSERVERCANVAS_H diff --git a/servers/rendering/rendering_server_globals.cpp b/servers/rendering/rendering_server_globals.cpp new file mode 100644 index 0000000000..60183956e0 --- /dev/null +++ b/servers/rendering/rendering_server_globals.cpp @@ -0,0 +1,40 @@ +/*************************************************************************/ +/* rendering_server_globals.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "rendering_server_globals.h" + +RasterizerStorage *RenderingServerGlobals::storage = NULL; +RasterizerCanvas *RenderingServerGlobals::canvas_render = NULL; +RasterizerScene *RenderingServerGlobals::scene_render = NULL; +Rasterizer *RenderingServerGlobals::rasterizer = NULL; + +RenderingServerCanvas *RenderingServerGlobals::canvas = NULL; +RenderingServerViewport *RenderingServerGlobals::viewport = NULL; +RenderingServerScene *RenderingServerGlobals::scene = NULL; diff --git a/servers/rendering/rendering_server_globals.h b/servers/rendering/rendering_server_globals.h new file mode 100644 index 0000000000..79abc55fea --- /dev/null +++ b/servers/rendering/rendering_server_globals.h @@ -0,0 +1,54 @@ +/*************************************************************************/ +/* rendering_server_globals.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef RENDERING_SERVER_GLOBALS_H +#define RENDERING_SERVER_GLOBALS_H + +#include "rasterizer.h" + +class RenderingServerCanvas; +class RenderingServerViewport; +class RenderingServerScene; + +class RenderingServerGlobals { +public: + static RasterizerStorage *storage; + static RasterizerCanvas *canvas_render; + static RasterizerScene *scene_render; + static Rasterizer *rasterizer; + + static RenderingServerCanvas *canvas; + static RenderingServerViewport *viewport; + static RenderingServerScene *scene; +}; + +#define RSG RenderingServerGlobals + +#endif // RENDERING_SERVER_GLOBALS_H diff --git a/servers/rendering/rendering_server_raster.cpp b/servers/rendering/rendering_server_raster.cpp new file mode 100644 index 0000000000..9b769b46fc --- /dev/null +++ b/servers/rendering/rendering_server_raster.cpp @@ -0,0 +1,271 @@ +/*************************************************************************/ +/* rendering_server_raster.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "rendering_server_raster.h" + +#include "core/io/marshalls.h" +#include "core/os/os.h" +#include "core/project_settings.h" +#include "core/sort_array.h" +#include "rendering_server_canvas.h" +#include "rendering_server_globals.h" +#include "rendering_server_scene.h" + +// careful, these may run in different threads than the visual server + +int RenderingServerRaster::changes = 0; + +/* BLACK BARS */ + +void RenderingServerRaster::black_bars_set_margins(int p_left, int p_top, int p_right, int p_bottom) { + + black_margin[MARGIN_LEFT] = p_left; + black_margin[MARGIN_TOP] = p_top; + black_margin[MARGIN_RIGHT] = p_right; + black_margin[MARGIN_BOTTOM] = p_bottom; +} + +void RenderingServerRaster::black_bars_set_images(RID p_left, RID p_top, RID p_right, RID p_bottom) { + + black_image[MARGIN_LEFT] = p_left; + black_image[MARGIN_TOP] = p_top; + black_image[MARGIN_RIGHT] = p_right; + black_image[MARGIN_BOTTOM] = p_bottom; +} + +void RenderingServerRaster::_draw_margins() { + + RSG::canvas_render->draw_window_margins(black_margin, black_image); +}; + +/* FREE */ + +void RenderingServerRaster::free(RID p_rid) { + + if (RSG::storage->free(p_rid)) + return; + if (RSG::canvas->free(p_rid)) + return; + if (RSG::viewport->free(p_rid)) + return; + if (RSG::scene->free(p_rid)) + return; + if (RSG::scene_render->free(p_rid)) + return; +} + +/* EVENT QUEUING */ + +void RenderingServerRaster::request_frame_drawn_callback(Object *p_where, const StringName &p_method, const Variant &p_userdata) { + + ERR_FAIL_NULL(p_where); + FrameDrawnCallbacks fdc; + fdc.object = p_where->get_instance_id(); + fdc.method = p_method; + fdc.param = p_userdata; + + frame_drawn_callbacks.push_back(fdc); +} + +void RenderingServerRaster::draw(bool p_swap_buffers, double frame_step) { + + //needs to be done before changes is reset to 0, to not force the editor to redraw + RS::get_singleton()->emit_signal("frame_pre_draw"); + + changes = 0; + + RSG::rasterizer->begin_frame(frame_step); + + TIMESTAMP_BEGIN() + + RSG::scene_render->update(); //update scenes stuff before updating instances + + RSG::scene->update_dirty_instances(); //update scene stuff + + RSG::scene->render_probes(); + RSG::viewport->draw_viewports(); + RSG::canvas_render->update(); + + _draw_margins(); + RSG::rasterizer->end_frame(p_swap_buffers); + + while (frame_drawn_callbacks.front()) { + + Object *obj = ObjectDB::get_instance(frame_drawn_callbacks.front()->get().object); + if (obj) { + Callable::CallError ce; + const Variant *v = &frame_drawn_callbacks.front()->get().param; + obj->call(frame_drawn_callbacks.front()->get().method, &v, 1, ce); + if (ce.error != Callable::CallError::CALL_OK) { + String err = Variant::get_call_error_text(obj, frame_drawn_callbacks.front()->get().method, &v, 1, ce); + ERR_PRINT("Error calling frame drawn function: " + err); + } + } + + frame_drawn_callbacks.pop_front(); + } + RS::get_singleton()->emit_signal("frame_post_draw"); + + if (RSG::storage->get_captured_timestamps_count()) { + Vector<FrameProfileArea> new_profile; + new_profile.resize(RSG::storage->get_captured_timestamps_count()); + + uint64_t base_cpu = RSG::storage->get_captured_timestamp_cpu_time(0); + uint64_t base_gpu = RSG::storage->get_captured_timestamp_gpu_time(0); + for (uint32_t i = 0; i < RSG::storage->get_captured_timestamps_count(); i++) { + uint64_t time_cpu = RSG::storage->get_captured_timestamp_cpu_time(i) - base_cpu; + uint64_t time_gpu = RSG::storage->get_captured_timestamp_gpu_time(i) - base_gpu; + new_profile.write[i].gpu_msec = float(time_gpu / 1000) / 1000.0; + new_profile.write[i].cpu_msec = float(time_cpu) / 1000.0; + new_profile.write[i].name = RSG::storage->get_captured_timestamp_name(i); + } + + frame_profile = new_profile; + } + + frame_profile_frame = RSG::storage->get_captured_timestamps_frame(); +} +void RenderingServerRaster::sync() { +} +bool RenderingServerRaster::has_changed() const { + + return changes > 0; +} +void RenderingServerRaster::init() { + + RSG::rasterizer->initialize(); +} +void RenderingServerRaster::finish() { + + if (test_cube.is_valid()) { + free(test_cube); + } + + RSG::rasterizer->finalize(); +} + +/* STATUS INFORMATION */ + +int RenderingServerRaster::get_render_info(RenderInfo p_info) { + + return RSG::storage->get_render_info(p_info); +} + +String RenderingServerRaster::get_video_adapter_name() const { + + return RSG::storage->get_video_adapter_name(); +} + +String RenderingServerRaster::get_video_adapter_vendor() const { + + return RSG::storage->get_video_adapter_vendor(); +} + +void RenderingServerRaster::set_frame_profiling_enabled(bool p_enable) { + RSG::storage->capturing_timestamps = p_enable; +} + +uint64_t RenderingServerRaster::get_frame_profile_frame() { + return frame_profile_frame; +} + +Vector<RenderingServer::FrameProfileArea> RenderingServerRaster::get_frame_profile() { + return frame_profile; +} + +/* TESTING */ + +void RenderingServerRaster::set_boot_image(const Ref<Image> &p_image, const Color &p_color, bool p_scale, bool p_use_filter) { + + redraw_request(); + RSG::rasterizer->set_boot_image(p_image, p_color, p_scale, p_use_filter); +} +void RenderingServerRaster::set_default_clear_color(const Color &p_color) { + RSG::viewport->set_default_clear_color(p_color); +} + +bool RenderingServerRaster::has_feature(Features p_feature) const { + + return false; +} + +RID RenderingServerRaster::get_test_cube() { + if (!test_cube.is_valid()) { + test_cube = _make_test_cube(); + } + return test_cube; +} + +bool RenderingServerRaster::has_os_feature(const String &p_feature) const { + + return RSG::storage->has_os_feature(p_feature); +} + +void RenderingServerRaster::set_debug_generate_wireframes(bool p_generate) { + + RSG::storage->set_debug_generate_wireframes(p_generate); +} + +void RenderingServerRaster::call_set_use_vsync(bool p_enable) { + DisplayServer::get_singleton()->_set_use_vsync(p_enable); +} + +bool RenderingServerRaster::is_low_end() const { + // FIXME: Commented out when rebasing vulkan branch on master, + // causes a crash, it seems rasterizer is not initialized yet the + // first time it's called. + //return RSG::rasterizer->is_low_end(); + return false; +} +RenderingServerRaster::RenderingServerRaster() { + + RSG::canvas = memnew(RenderingServerCanvas); + RSG::viewport = memnew(RenderingServerViewport); + RSG::scene = memnew(RenderingServerScene); + RSG::rasterizer = Rasterizer::create(); + RSG::storage = RSG::rasterizer->get_storage(); + RSG::canvas_render = RSG::rasterizer->get_canvas(); + RSG::scene_render = RSG::rasterizer->get_scene(); + + frame_profile_frame = 0; + + for (int i = 0; i < 4; i++) { + black_margin[i] = 0; + black_image[i] = RID(); + } +} + +RenderingServerRaster::~RenderingServerRaster() { + + memdelete(RSG::canvas); + memdelete(RSG::viewport); + memdelete(RSG::rasterizer); + memdelete(RSG::scene); +} diff --git a/servers/rendering/rendering_server_raster.h b/servers/rendering/rendering_server_raster.h new file mode 100644 index 0000000000..24fbe4577a --- /dev/null +++ b/servers/rendering/rendering_server_raster.h @@ -0,0 +1,761 @@ +/*************************************************************************/ +/* rendering_server_raster.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef RENDERING_SERVER_RASTER_H +#define RENDERING_SERVER_RASTER_H + +#include "core/math/octree.h" +#include "rendering_server_canvas.h" +#include "rendering_server_globals.h" +#include "rendering_server_scene.h" +#include "rendering_server_viewport.h" +#include "servers/rendering/rasterizer.h" +#include "servers/rendering_server.h" + +class RenderingServerRaster : public RenderingServer { + + enum { + + MAX_INSTANCE_CULL = 8192, + MAX_INSTANCE_LIGHTS = 4, + LIGHT_CACHE_DIRTY = -1, + MAX_LIGHTS_CULLED = 256, + MAX_ROOM_CULL = 32, + MAX_EXTERIOR_PORTALS = 128, + MAX_LIGHT_SAMPLERS = 256, + INSTANCE_ROOMLESS_MASK = (1 << 20) + + }; + + static int changes; + RID test_cube; + + int black_margin[4]; + RID black_image[4]; + + struct FrameDrawnCallbacks { + + ObjectID object; + StringName method; + Variant param; + }; + + List<FrameDrawnCallbacks> frame_drawn_callbacks; + + void _draw_margins(); + static void _changes_changed() {} + + uint64_t frame_profile_frame; + Vector<FrameProfileArea> frame_profile; + +public: + //if editor is redrawing when it shouldn't, enable this and put a breakpoint in _changes_changed() + //#define DEBUG_CHANGES + +#ifdef DEBUG_CHANGES + _FORCE_INLINE_ static void redraw_request() { + changes++; + _changes_changed(); + } + +#define DISPLAY_CHANGED \ + changes++; \ + _changes_changed(); + +#else + _FORCE_INLINE_ static void redraw_request() { changes++; } + +#define DISPLAY_CHANGED \ + changes++; +#endif + +#define BIND0R(m_r, m_name) \ + m_r m_name() { return BINDBASE->m_name(); } +#define BIND1R(m_r, m_name, m_type1) \ + m_r m_name(m_type1 arg1) { return BINDBASE->m_name(arg1); } +#define BIND1RC(m_r, m_name, m_type1) \ + m_r m_name(m_type1 arg1) const { return BINDBASE->m_name(arg1); } +#define BIND2R(m_r, m_name, m_type1, m_type2) \ + m_r m_name(m_type1 arg1, m_type2 arg2) { return BINDBASE->m_name(arg1, arg2); } +#define BIND2RC(m_r, m_name, m_type1, m_type2) \ + m_r m_name(m_type1 arg1, m_type2 arg2) const { return BINDBASE->m_name(arg1, arg2); } +#define BIND3RC(m_r, m_name, m_type1, m_type2, m_type3) \ + m_r m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3) const { return BINDBASE->m_name(arg1, arg2, arg3); } +#define BIND4RC(m_r, m_name, m_type1, m_type2, m_type3, m_type4) \ + m_r m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4) const { return BINDBASE->m_name(arg1, arg2, arg3, arg4); } + +#define BIND1(m_name, m_type1) \ + void m_name(m_type1 arg1) { DISPLAY_CHANGED BINDBASE->m_name(arg1); } +#define BIND2(m_name, m_type1, m_type2) \ + void m_name(m_type1 arg1, m_type2 arg2) { DISPLAY_CHANGED BINDBASE->m_name(arg1, arg2); } +#define BIND2C(m_name, m_type1, m_type2) \ + void m_name(m_type1 arg1, m_type2 arg2) const { BINDBASE->m_name(arg1, arg2); } +#define BIND3(m_name, m_type1, m_type2, m_type3) \ + void m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3) { DISPLAY_CHANGED BINDBASE->m_name(arg1, arg2, arg3); } +#define BIND4(m_name, m_type1, m_type2, m_type3, m_type4) \ + void m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4) { DISPLAY_CHANGED BINDBASE->m_name(arg1, arg2, arg3, arg4); } +#define BIND5(m_name, m_type1, m_type2, m_type3, m_type4, m_type5) \ + void m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5) { DISPLAY_CHANGED BINDBASE->m_name(arg1, arg2, arg3, arg4, arg5); } +#define BIND6(m_name, m_type1, m_type2, m_type3, m_type4, m_type5, m_type6) \ + void m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5, m_type6 arg6) { DISPLAY_CHANGED BINDBASE->m_name(arg1, arg2, arg3, arg4, arg5, arg6); } +#define BIND7(m_name, m_type1, m_type2, m_type3, m_type4, m_type5, m_type6, m_type7) \ + void m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5, m_type6 arg6, m_type7 arg7) { DISPLAY_CHANGED BINDBASE->m_name(arg1, arg2, arg3, arg4, arg5, arg6, arg7); } +#define BIND8(m_name, m_type1, m_type2, m_type3, m_type4, m_type5, m_type6, m_type7, m_type8) \ + void m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5, m_type6 arg6, m_type7 arg7, m_type8 arg8) { DISPLAY_CHANGED BINDBASE->m_name(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); } +#define BIND9(m_name, m_type1, m_type2, m_type3, m_type4, m_type5, m_type6, m_type7, m_type8, m_type9) \ + void m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5, m_type6 arg6, m_type7 arg7, m_type8 arg8, m_type9 arg9) { DISPLAY_CHANGED BINDBASE->m_name(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); } +#define BIND10(m_name, m_type1, m_type2, m_type3, m_type4, m_type5, m_type6, m_type7, m_type8, m_type9, m_type10) \ + void m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5, m_type6 arg6, m_type7 arg7, m_type8 arg8, m_type9 arg9, m_type10 arg10) { DISPLAY_CHANGED BINDBASE->m_name(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10); } +#define BIND11(m_name, m_type1, m_type2, m_type3, m_type4, m_type5, m_type6, m_type7, m_type8, m_type9, m_type10, m_type11) \ + void m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5, m_type6 arg6, m_type7 arg7, m_type8 arg8, m_type9 arg9, m_type10 arg10, m_type11 arg11) { DISPLAY_CHANGED BINDBASE->m_name(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11); } +#define BIND12(m_name, m_type1, m_type2, m_type3, m_type4, m_type5, m_type6, m_type7, m_type8, m_type9, m_type10, m_type11, m_type12) \ + void m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5, m_type6 arg6, m_type7 arg7, m_type8 arg8, m_type9 arg9, m_type10 arg10, m_type11 arg11, m_type12 arg12) { DISPLAY_CHANGED BINDBASE->m_name(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12); } +#define BIND13(m_name, m_type1, m_type2, m_type3, m_type4, m_type5, m_type6, m_type7, m_type8, m_type9, m_type10, m_type11, m_type12, m_type13) \ + void m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5, m_type6 arg6, m_type7 arg7, m_type8 arg8, m_type9 arg9, m_type10 arg10, m_type11 arg11, m_type12 arg12, m_type13 arg13) { DISPLAY_CHANGED BINDBASE->m_name(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13); } +#define BIND14(m_name, m_type1, m_type2, m_type3, m_type4, m_type5, m_type6, m_type7, m_type8, m_type9, m_type10, m_type11, m_type12, m_type13, m_type14) \ + void m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5, m_type6 arg6, m_type7 arg7, m_type8 arg8, m_type9 arg9, m_type10 arg10, m_type11 arg11, m_type12 arg12, m_type13 arg13, m_type14 arg14) { DISPLAY_CHANGED BINDBASE->m_name(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14); } +#define BIND15(m_name, m_type1, m_type2, m_type3, m_type4, m_type5, m_type6, m_type7, m_type8, m_type9, m_type10, m_type11, m_type12, m_type13, m_type14, m_type15) \ + void m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5, m_type6 arg6, m_type7 arg7, m_type8 arg8, m_type9 arg9, m_type10 arg10, m_type11 arg11, m_type12 arg12, m_type13 arg13, m_type14 arg14, m_type15 arg15) { DISPLAY_CHANGED BINDBASE->m_name(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15); } + +//from now on, calls forwarded to this singleton +#define BINDBASE RSG::storage + + /* TEXTURE API */ + + //these go pass-through, as they can be called from any thread + BIND1R(RID, texture_2d_create, const Ref<Image> &) + BIND2R(RID, texture_2d_layered_create, const Vector<Ref<Image>> &, TextureLayeredType) + BIND1R(RID, texture_3d_create, const Vector<Ref<Image>> &) + BIND1R(RID, texture_proxy_create, RID) + + //goes pass-through + BIND3(texture_2d_update_immediate, RID, const Ref<Image> &, int) + //these go through command queue if they are in another thread + BIND3(texture_2d_update, RID, const Ref<Image> &, int) + BIND4(texture_3d_update, RID, const Ref<Image> &, int, int) + BIND2(texture_proxy_update, RID, RID) + + //these also go pass-through + BIND0R(RID, texture_2d_placeholder_create) + BIND0R(RID, texture_2d_layered_placeholder_create) + BIND0R(RID, texture_3d_placeholder_create) + + BIND1RC(Ref<Image>, texture_2d_get, RID) + BIND2RC(Ref<Image>, texture_2d_layer_get, RID, int) + BIND3RC(Ref<Image>, texture_3d_slice_get, RID, int, int) + + BIND2(texture_replace, RID, RID) + + BIND3(texture_set_size_override, RID, int, int) +// FIXME: Disabled during Vulkan refactoring, should be ported. +#if 0 + BIND2(texture_bind, RID, uint32_t) +#endif + + BIND3(texture_set_detect_3d_callback, RID, TextureDetectCallback, void *) + BIND3(texture_set_detect_normal_callback, RID, TextureDetectCallback, void *) + BIND3(texture_set_detect_roughness_callback, RID, TextureDetectRoughnessCallback, void *) + + BIND2(texture_set_path, RID, const String &) + BIND1RC(String, texture_get_path, RID) + BIND1(texture_debug_usage, List<TextureInfo> *) + + BIND2(texture_set_force_redraw_if_visible, RID, bool) + + /* SHADER API */ + + BIND0R(RID, shader_create) + + BIND2(shader_set_code, RID, const String &) + BIND1RC(String, shader_get_code, RID) + + BIND2C(shader_get_param_list, RID, List<PropertyInfo> *) + + BIND3(shader_set_default_texture_param, RID, const StringName &, RID) + BIND2RC(RID, shader_get_default_texture_param, RID, const StringName &) + BIND2RC(Variant, shader_get_param_default, RID, const StringName &) + + /* COMMON MATERIAL API */ + + BIND0R(RID, material_create) + + BIND2(material_set_shader, RID, RID) + + BIND3(material_set_param, RID, const StringName &, const Variant &) + BIND2RC(Variant, material_get_param, RID, const StringName &) + + BIND2(material_set_render_priority, RID, int) + BIND2(material_set_next_pass, RID, RID) + + /* MESH API */ + + virtual RID mesh_create_from_surfaces(const Vector<SurfaceData> &p_surfaces) { + RID mesh = mesh_create(); + for (int i = 0; i < p_surfaces.size(); i++) { + mesh_add_surface(mesh, p_surfaces[i]); + } + return mesh; + } + + BIND0R(RID, mesh_create) + + BIND2(mesh_add_surface, RID, const SurfaceData &) + + BIND1RC(int, mesh_get_blend_shape_count, RID) + + BIND2(mesh_set_blend_shape_mode, RID, BlendShapeMode) + BIND1RC(BlendShapeMode, mesh_get_blend_shape_mode, RID) + + BIND4(mesh_surface_update_region, RID, int, int, const Vector<uint8_t> &) + + BIND3(mesh_surface_set_material, RID, int, RID) + BIND2RC(RID, mesh_surface_get_material, RID, int) + + BIND2RC(SurfaceData, mesh_get_surface, RID, int) + + BIND1RC(int, mesh_get_surface_count, RID) + + BIND2(mesh_set_custom_aabb, RID, const AABB &) + BIND1RC(AABB, mesh_get_custom_aabb, RID) + + BIND1(mesh_clear, RID) + + /* MULTIMESH API */ + + BIND0R(RID, multimesh_create) + + BIND5(multimesh_allocate, RID, int, MultimeshTransformFormat, bool, bool) + BIND1RC(int, multimesh_get_instance_count, RID) + + BIND2(multimesh_set_mesh, RID, RID) + BIND3(multimesh_instance_set_transform, RID, int, const Transform &) + BIND3(multimesh_instance_set_transform_2d, RID, int, const Transform2D &) + BIND3(multimesh_instance_set_color, RID, int, const Color &) + BIND3(multimesh_instance_set_custom_data, RID, int, const Color &) + + BIND1RC(RID, multimesh_get_mesh, RID) + BIND1RC(AABB, multimesh_get_aabb, RID) + + BIND2RC(Transform, multimesh_instance_get_transform, RID, int) + BIND2RC(Transform2D, multimesh_instance_get_transform_2d, RID, int) + BIND2RC(Color, multimesh_instance_get_color, RID, int) + BIND2RC(Color, multimesh_instance_get_custom_data, RID, int) + + BIND2(multimesh_set_buffer, RID, const Vector<float> &) + BIND1RC(Vector<float>, multimesh_get_buffer, RID) + + BIND2(multimesh_set_visible_instances, RID, int) + BIND1RC(int, multimesh_get_visible_instances, RID) + + /* IMMEDIATE API */ + + BIND0R(RID, immediate_create) + BIND3(immediate_begin, RID, PrimitiveType, RID) + BIND2(immediate_vertex, RID, const Vector3 &) + BIND2(immediate_normal, RID, const Vector3 &) + BIND2(immediate_tangent, RID, const Plane &) + BIND2(immediate_color, RID, const Color &) + BIND2(immediate_uv, RID, const Vector2 &) + BIND2(immediate_uv2, RID, const Vector2 &) + BIND1(immediate_end, RID) + BIND1(immediate_clear, RID) + BIND2(immediate_set_material, RID, RID) + BIND1RC(RID, immediate_get_material, RID) + + /* SKELETON API */ + + BIND0R(RID, skeleton_create) + BIND3(skeleton_allocate, RID, int, bool) + BIND1RC(int, skeleton_get_bone_count, RID) + BIND3(skeleton_bone_set_transform, RID, int, const Transform &) + BIND2RC(Transform, skeleton_bone_get_transform, RID, int) + BIND3(skeleton_bone_set_transform_2d, RID, int, const Transform2D &) + BIND2RC(Transform2D, skeleton_bone_get_transform_2d, RID, int) + BIND2(skeleton_set_base_transform_2d, RID, const Transform2D &) + + /* Light API */ + + BIND0R(RID, directional_light_create) + BIND0R(RID, omni_light_create) + BIND0R(RID, spot_light_create) + + BIND2(light_set_color, RID, const Color &) + BIND3(light_set_param, RID, LightParam, float) + BIND2(light_set_shadow, RID, bool) + BIND2(light_set_shadow_color, RID, const Color &) + BIND2(light_set_projector, RID, RID) + BIND2(light_set_negative, RID, bool) + BIND2(light_set_cull_mask, RID, uint32_t) + BIND2(light_set_reverse_cull_face_mode, RID, bool) + BIND2(light_set_use_gi, RID, bool) + + BIND2(light_omni_set_shadow_mode, RID, LightOmniShadowMode) + + BIND2(light_directional_set_shadow_mode, RID, LightDirectionalShadowMode) + BIND2(light_directional_set_blend_splits, RID, bool) + BIND2(light_directional_set_shadow_depth_range_mode, RID, LightDirectionalShadowDepthRangeMode) + + /* PROBE API */ + + BIND0R(RID, reflection_probe_create) + + BIND2(reflection_probe_set_update_mode, RID, ReflectionProbeUpdateMode) + BIND2(reflection_probe_set_intensity, RID, float) + BIND2(reflection_probe_set_interior_ambient, RID, const Color &) + BIND2(reflection_probe_set_interior_ambient_energy, RID, float) + BIND2(reflection_probe_set_interior_ambient_probe_contribution, RID, float) + BIND2(reflection_probe_set_max_distance, RID, float) + BIND2(reflection_probe_set_extents, RID, const Vector3 &) + BIND2(reflection_probe_set_origin_offset, RID, const Vector3 &) + BIND2(reflection_probe_set_as_interior, RID, bool) + BIND2(reflection_probe_set_enable_box_projection, RID, bool) + BIND2(reflection_probe_set_enable_shadows, RID, bool) + BIND2(reflection_probe_set_cull_mask, RID, uint32_t) + BIND2(reflection_probe_set_resolution, RID, int) + + /* BAKED LIGHT API */ + + BIND0R(RID, gi_probe_create) + + BIND8(gi_probe_allocate, RID, const Transform &, const AABB &, const Vector3i &, const Vector<uint8_t> &, const Vector<uint8_t> &, const Vector<uint8_t> &, const Vector<int> &) + + BIND1RC(AABB, gi_probe_get_bounds, RID) + BIND1RC(Vector3i, gi_probe_get_octree_size, RID) + BIND1RC(Vector<uint8_t>, gi_probe_get_octree_cells, RID) + BIND1RC(Vector<uint8_t>, gi_probe_get_data_cells, RID) + BIND1RC(Vector<uint8_t>, gi_probe_get_distance_field, RID) + BIND1RC(Vector<int>, gi_probe_get_level_counts, RID) + BIND1RC(Transform, gi_probe_get_to_cell_xform, RID) + + BIND2(gi_probe_set_dynamic_range, RID, float) + BIND1RC(float, gi_probe_get_dynamic_range, RID) + + BIND2(gi_probe_set_propagation, RID, float) + BIND1RC(float, gi_probe_get_propagation, RID) + + BIND2(gi_probe_set_energy, RID, float) + BIND1RC(float, gi_probe_get_energy, RID) + + BIND2(gi_probe_set_ao, RID, float) + BIND1RC(float, gi_probe_get_ao, RID) + + BIND2(gi_probe_set_ao_size, RID, float) + BIND1RC(float, gi_probe_get_ao_size, RID) + + BIND2(gi_probe_set_bias, RID, float) + BIND1RC(float, gi_probe_get_bias, RID) + + BIND2(gi_probe_set_normal_bias, RID, float) + BIND1RC(float, gi_probe_get_normal_bias, RID) + + BIND2(gi_probe_set_interior, RID, bool) + BIND1RC(bool, gi_probe_is_interior, RID) + + BIND2(gi_probe_set_use_two_bounces, RID, bool) + BIND1RC(bool, gi_probe_is_using_two_bounces, RID) + + BIND2(gi_probe_set_anisotropy_strength, RID, float) + BIND1RC(float, gi_probe_get_anisotropy_strength, 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 Vector<uint8_t> &) + BIND1RC(Vector<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) + + BIND2(particles_set_emitting, RID, bool) + BIND1R(bool, particles_get_emitting, RID) + BIND2(particles_set_amount, RID, int) + BIND2(particles_set_lifetime, RID, float) + BIND2(particles_set_one_shot, RID, bool) + BIND2(particles_set_pre_process_time, RID, float) + BIND2(particles_set_explosiveness_ratio, RID, float) + BIND2(particles_set_randomness_ratio, RID, float) + BIND2(particles_set_custom_aabb, RID, const AABB &) + BIND2(particles_set_speed_scale, RID, float) + BIND2(particles_set_use_local_coordinates, RID, bool) + BIND2(particles_set_process_material, RID, RID) + BIND2(particles_set_fixed_fps, RID, int) + BIND2(particles_set_fractional_delta, RID, bool) + BIND1R(bool, particles_is_inactive, RID) + BIND1(particles_request_process, RID) + BIND1(particles_restart, RID) + + BIND2(particles_set_draw_order, RID, RS::ParticlesDrawOrder) + + BIND2(particles_set_draw_passes, RID, int) + BIND3(particles_set_draw_pass_mesh, RID, int, RID) + + BIND1R(AABB, particles_get_current_aabb, RID) + BIND2(particles_set_emission_transform, RID, const Transform &) + +#undef BINDBASE +//from now on, calls forwarded to this singleton +#define BINDBASE RSG::scene + + /* CAMERA API */ + + BIND0R(RID, camera_create) + BIND4(camera_set_perspective, RID, float, float, float) + BIND4(camera_set_orthogonal, RID, float, float, float) + BIND5(camera_set_frustum, RID, float, Vector2, float, float) + BIND2(camera_set_transform, RID, const Transform &) + BIND2(camera_set_cull_mask, RID, uint32_t) + BIND2(camera_set_environment, RID, RID) + BIND2(camera_set_camera_effects, RID, RID) + BIND2(camera_set_use_vertical_aspect, RID, bool) + +#undef BINDBASE +//from now on, calls forwarded to this singleton +#define BINDBASE RSG::viewport + + /* VIEWPORT TARGET API */ + + BIND0R(RID, viewport_create) + + BIND2(viewport_set_use_arvr, RID, bool) + BIND3(viewport_set_size, RID, int, int) + + BIND2(viewport_set_active, RID, bool) + BIND2(viewport_set_parent_viewport, RID, RID) + + BIND2(viewport_set_clear_mode, RID, ViewportClearMode) + + BIND3(viewport_attach_to_screen, RID, const Rect2 &, int) + BIND2(viewport_set_render_direct_to_screen, RID, bool) + + BIND2(viewport_set_update_mode, RID, ViewportUpdateMode) + BIND2(viewport_set_vflip, RID, bool) + + BIND1RC(RID, viewport_get_texture, RID) + + BIND2(viewport_set_hide_scenario, RID, bool) + BIND2(viewport_set_hide_canvas, RID, bool) + BIND2(viewport_set_disable_environment, RID, bool) + + BIND2(viewport_attach_camera, RID, RID) + BIND2(viewport_set_scenario, RID, RID) + BIND2(viewport_attach_canvas, RID, RID) + + BIND2(viewport_remove_canvas, RID, RID) + BIND3(viewport_set_canvas_transform, RID, RID, const Transform2D &) + BIND2(viewport_set_transparent_background, RID, bool) + + BIND2(viewport_set_global_canvas_transform, RID, const Transform2D &) + BIND4(viewport_set_canvas_stacking, RID, RID, int, int) + BIND2(viewport_set_shadow_atlas_size, RID, int) + BIND3(viewport_set_shadow_atlas_quadrant_subdivision, RID, int, int) + BIND2(viewport_set_msaa, RID, ViewportMSAA) + + BIND2R(int, viewport_get_render_info, RID, ViewportRenderInfo) + BIND2(viewport_set_debug_draw, RID, ViewportDebugDraw) + + /* ENVIRONMENT API */ + +#undef BINDBASE +//from now on, calls forwarded to this singleton +#define BINDBASE RSG::scene_render + + BIND1(directional_shadow_atlas_set_size, int) + + /* SKY API */ + + BIND0R(RID, sky_create) + BIND2(sky_set_radiance_size, RID, int) + BIND2(sky_set_mode, RID, SkyMode) + BIND2(sky_set_material, RID, RID) + + BIND0R(RID, environment_create) + + BIND2(environment_set_background, RID, EnvironmentBG) + BIND2(environment_set_sky, RID, RID) + BIND2(environment_set_sky_custom_fov, RID, float) + BIND2(environment_set_sky_orientation, RID, const Basis &) + BIND2(environment_set_bg_color, RID, const Color &) + BIND2(environment_set_bg_energy, RID, float) + BIND2(environment_set_canvas_max_layer, RID, int) + BIND7(environment_set_ambient_light, RID, const Color &, EnvironmentAmbientSource, float, float, EnvironmentReflectionSource, const Color &) + +// FIXME: Disabled during Vulkan refactoring, should be ported. +#if 0 + BIND2(environment_set_camera_feed_id, RID, int) +#endif + BIND7(environment_set_ssr, RID, bool, int, float, float, float, bool) + BIND9(environment_set_ssao, RID, bool, float, float, float, float, float, EnvironmentSSAOBlur, float) + BIND2(environment_set_ssao_quality, EnvironmentSSAOQuality, bool) + + BIND12(environment_set_glow, RID, bool, int, float, float, float, float, EnvironmentGlowBlendMode, float, float, float, bool) + + BIND9(environment_set_tonemap, RID, EnvironmentToneMapper, float, float, bool, float, float, float, float) + + BIND6(environment_set_adjustment, RID, bool, float, float, float, RID) + + BIND5(environment_set_fog, RID, bool, const Color &, const Color &, float) + BIND7(environment_set_fog_depth, RID, bool, float, float, float, bool, float) + BIND5(environment_set_fog_height, RID, bool, float, float, float) + + BIND2(screen_space_roughness_limiter_set_active, bool, float) + + /* CAMERA EFFECTS */ + + BIND0R(RID, camera_effects_create) + + BIND2(camera_effects_set_dof_blur_quality, DOFBlurQuality, bool) + BIND1(camera_effects_set_dof_blur_bokeh_shape, DOFBokehShape) + + BIND8(camera_effects_set_dof_blur, RID, bool, float, float, bool, float, float, float) + BIND3(camera_effects_set_custom_exposure, RID, bool, float) + + /* SCENARIO API */ + +#undef BINDBASE +#define BINDBASE RSG::scene + + BIND0R(RID, scenario_create) + + BIND2(scenario_set_debug, RID, ScenarioDebugMode) + BIND2(scenario_set_environment, RID, RID) + BIND2(scenario_set_camera_effects, RID, RID) + BIND2(scenario_set_fallback_environment, RID, RID) + + /* INSTANCING API */ + BIND0R(RID, instance_create) + + BIND2(instance_set_base, RID, RID) + BIND2(instance_set_scenario, RID, RID) + BIND2(instance_set_layer_mask, RID, uint32_t) + BIND2(instance_set_transform, RID, const Transform &) + BIND2(instance_attach_object_instance_id, RID, ObjectID) + 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) + + BIND2(instance_attach_skeleton, RID, RID) + BIND2(instance_set_exterior, RID, bool) + + BIND2(instance_set_extra_visibility_margin, RID, real_t) + + // don't use these in a game! + BIND2RC(Vector<ObjectID>, instances_cull_aabb, const AABB &, RID) + BIND3RC(Vector<ObjectID>, instances_cull_ray, const Vector3 &, const Vector3 &, RID) + BIND2RC(Vector<ObjectID>, instances_cull_convex, const Vector<Plane> &, RID) + + BIND3(instance_geometry_set_flag, RID, InstanceFlags, bool) + BIND2(instance_geometry_set_cast_shadows_setting, RID, ShadowCastingSetting) + BIND2(instance_geometry_set_material_override, RID, RID) + + BIND5(instance_geometry_set_draw_range, RID, float, float, float, float) + BIND2(instance_geometry_set_as_instance_lod, RID, RID) + +#undef BINDBASE +//from now on, calls forwarded to this singleton +#define BINDBASE RSG::canvas + + /* CANVAS (2D) */ + + BIND0R(RID, canvas_create) + BIND3(canvas_set_item_mirroring, RID, RID, const Point2 &) + BIND2(canvas_set_modulate, RID, const Color &) + BIND3(canvas_set_parent, RID, RID, float) + BIND1(canvas_set_disable_scale, bool) + + BIND0R(RID, canvas_item_create) + BIND2(canvas_item_set_parent, RID, RID) + + BIND2(canvas_item_set_visible, RID, bool) + BIND2(canvas_item_set_light_mask, RID, int) + + BIND2(canvas_item_set_update_when_visible, RID, bool) + + BIND2(canvas_item_set_transform, RID, const Transform2D &) + BIND2(canvas_item_set_clip, RID, bool) + BIND2(canvas_item_set_distance_field_mode, RID, bool) + BIND3(canvas_item_set_custom_rect, RID, bool, const Rect2 &) + BIND2(canvas_item_set_modulate, RID, const Color &) + BIND2(canvas_item_set_self_modulate, RID, const Color &) + + BIND2(canvas_item_set_draw_behind_parent, RID, bool) + + BIND2(canvas_item_set_default_texture_filter, RID, CanvasItemTextureFilter) + BIND2(canvas_item_set_default_texture_repeat, RID, CanvasItemTextureRepeat) + + BIND5(canvas_item_add_line, RID, const Point2 &, const Point2 &, const Color &, float) + BIND4(canvas_item_add_polyline, RID, const Vector<Point2> &, const Vector<Color> &, float) + BIND4(canvas_item_add_multiline, RID, const Vector<Point2> &, const Vector<Color> &, float) + BIND3(canvas_item_add_rect, RID, const Rect2 &, const Color &) + BIND4(canvas_item_add_circle, RID, const Point2 &, float, const Color &) + BIND11(canvas_item_add_texture_rect, RID, const Rect2 &, RID, bool, const Color &, bool, RID, RID, const Color &, CanvasItemTextureFilter, CanvasItemTextureRepeat) + BIND12(canvas_item_add_texture_rect_region, RID, const Rect2 &, RID, const Rect2 &, const Color &, bool, RID, RID, const Color &, bool, CanvasItemTextureFilter, CanvasItemTextureRepeat) + BIND15(canvas_item_add_nine_patch, RID, const Rect2 &, const Rect2 &, RID, const Vector2 &, const Vector2 &, NinePatchAxisMode, NinePatchAxisMode, bool, const Color &, RID, RID, const Color &, CanvasItemTextureFilter, CanvasItemTextureRepeat) + BIND11(canvas_item_add_primitive, RID, const Vector<Point2> &, const Vector<Color> &, const Vector<Point2> &, RID, float, RID, RID, const Color &, CanvasItemTextureFilter, CanvasItemTextureRepeat) + BIND10(canvas_item_add_polygon, RID, const Vector<Point2> &, const Vector<Color> &, const Vector<Point2> &, RID, RID, RID, const Color &, CanvasItemTextureFilter, CanvasItemTextureRepeat) + BIND14(canvas_item_add_triangle_array, RID, const Vector<int> &, const Vector<Point2> &, const Vector<Color> &, const Vector<Point2> &, const Vector<int> &, const Vector<float> &, RID, int, RID, RID, const Color &, CanvasItemTextureFilter, CanvasItemTextureRepeat) + BIND10(canvas_item_add_mesh, RID, const RID &, const Transform2D &, const Color &, RID, RID, RID, const Color &, CanvasItemTextureFilter, CanvasItemTextureRepeat) + BIND8(canvas_item_add_multimesh, RID, RID, RID, RID, RID, const Color &, CanvasItemTextureFilter, CanvasItemTextureRepeat) + BIND8(canvas_item_add_particles, RID, RID, RID, RID, RID, const Color &, CanvasItemTextureFilter, CanvasItemTextureRepeat) + BIND2(canvas_item_add_set_transform, RID, const Transform2D &) + BIND2(canvas_item_add_clip_ignore, RID, bool) + BIND2(canvas_item_set_sort_children_by_y, RID, bool) + BIND2(canvas_item_set_z_index, RID, int) + BIND2(canvas_item_set_z_as_relative_to_parent, RID, bool) + BIND3(canvas_item_set_copy_to_backbuffer, RID, bool, const Rect2 &) + BIND2(canvas_item_attach_skeleton, RID, RID) + + BIND1(canvas_item_clear, RID) + BIND2(canvas_item_set_draw_index, RID, int) + + BIND2(canvas_item_set_material, RID, RID) + + BIND2(canvas_item_set_use_parent_material, RID, bool) + + BIND0R(RID, canvas_light_create) + BIND2(canvas_light_attach_to_canvas, RID, RID) + BIND2(canvas_light_set_enabled, RID, bool) + BIND2(canvas_light_set_scale, RID, float) + BIND2(canvas_light_set_transform, RID, const Transform2D &) + BIND2(canvas_light_set_texture, RID, RID) + BIND2(canvas_light_set_texture_offset, RID, const Vector2 &) + BIND2(canvas_light_set_color, RID, const Color &) + BIND2(canvas_light_set_height, RID, float) + BIND2(canvas_light_set_energy, RID, float) + BIND3(canvas_light_set_z_range, RID, int, int) + BIND3(canvas_light_set_layer_range, RID, int, int) + BIND2(canvas_light_set_item_cull_mask, RID, int) + BIND2(canvas_light_set_item_shadow_cull_mask, RID, int) + + BIND2(canvas_light_set_mode, RID, CanvasLightMode) + + BIND2(canvas_light_set_shadow_enabled, RID, bool) + BIND2(canvas_light_set_shadow_buffer_size, RID, int) + BIND2(canvas_light_set_shadow_filter, RID, CanvasLightShadowFilter) + BIND2(canvas_light_set_shadow_color, RID, const Color &) + BIND2(canvas_light_set_shadow_smooth, RID, float) + + BIND0R(RID, canvas_light_occluder_create) + BIND2(canvas_light_occluder_attach_to_canvas, RID, RID) + BIND2(canvas_light_occluder_set_enabled, RID, bool) + BIND2(canvas_light_occluder_set_polygon, RID, RID) + BIND2(canvas_light_occluder_set_transform, RID, const Transform2D &) + BIND2(canvas_light_occluder_set_light_mask, RID, int) + + BIND0R(RID, canvas_occluder_polygon_create) + BIND3(canvas_occluder_polygon_set_shape, RID, const Vector<Vector2> &, bool) + BIND2(canvas_occluder_polygon_set_shape_as_lines, RID, const Vector<Vector2> &) + + BIND2(canvas_occluder_polygon_set_cull_mode, RID, CanvasOccluderPolygonCullMode) + + /* BLACK BARS */ + + virtual void black_bars_set_margins(int p_left, int p_top, int p_right, int p_bottom); + virtual void black_bars_set_images(RID p_left, RID p_top, RID p_right, RID p_bottom); + + /* FREE */ + + virtual void free(RID p_rid); ///< free RIDs associated with the visual server + + /* EVENT QUEUING */ + + virtual void request_frame_drawn_callback(Object *p_where, const StringName &p_method, const Variant &p_userdata); + + virtual void draw(bool p_swap_buffers, double frame_step); + virtual void sync(); + virtual bool has_changed() const; + virtual void init(); + virtual void finish(); + + /* STATUS INFORMATION */ + + virtual int get_render_info(RenderInfo p_info); + virtual String get_video_adapter_name() const; + virtual String get_video_adapter_vendor() const; + + virtual void set_frame_profiling_enabled(bool p_enable); + virtual Vector<FrameProfileArea> get_frame_profile(); + virtual uint64_t get_frame_profile_frame(); + + virtual RID get_test_cube(); + + /* TESTING */ + + virtual void set_boot_image(const Ref<Image> &p_image, const Color &p_color, bool p_scale, bool p_use_filter = true); + virtual void set_default_clear_color(const Color &p_color); + + virtual bool has_feature(Features p_feature) const; + + virtual bool has_os_feature(const String &p_feature) const; + virtual void set_debug_generate_wireframes(bool p_generate); + + virtual void call_set_use_vsync(bool p_enable); + + virtual bool is_low_end() const; + + RenderingServerRaster(); + ~RenderingServerRaster(); + +#undef DISPLAY_CHANGED + +#undef BIND0R +#undef BIND1RC +#undef BIND2RC +#undef BIND3RC +#undef BIND4RC + +#undef BIND1 +#undef BIND2 +#undef BIND3 +#undef BIND4 +#undef BIND5 +#undef BIND6 +#undef BIND7 +#undef BIND8 +#undef BIND9 +#undef BIND10 +}; + +#endif diff --git a/servers/rendering/rendering_server_scene.cpp b/servers/rendering/rendering_server_scene.cpp new file mode 100644 index 0000000000..0530846e95 --- /dev/null +++ b/servers/rendering/rendering_server_scene.cpp @@ -0,0 +1,2839 @@ +/*************************************************************************/ +/* rendering_server_scene.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "rendering_server_scene.h" + +#include "core/os/os.h" +#include "rendering_server_globals.h" +#include "rendering_server_raster.h" + +#include <new> + +/* CAMERA API */ + +RID RenderingServerScene::camera_create() { + + Camera *camera = memnew(Camera); + return camera_owner.make_rid(camera); +} + +void RenderingServerScene::camera_set_perspective(RID p_camera, float p_fovy_degrees, float p_z_near, float p_z_far) { + + Camera *camera = camera_owner.getornull(p_camera); + ERR_FAIL_COND(!camera); + camera->type = Camera::PERSPECTIVE; + camera->fov = p_fovy_degrees; + camera->znear = p_z_near; + camera->zfar = p_z_far; +} + +void RenderingServerScene::camera_set_orthogonal(RID p_camera, float p_size, float p_z_near, float p_z_far) { + + Camera *camera = camera_owner.getornull(p_camera); + ERR_FAIL_COND(!camera); + camera->type = Camera::ORTHOGONAL; + camera->size = p_size; + camera->znear = p_z_near; + camera->zfar = p_z_far; +} + +void RenderingServerScene::camera_set_frustum(RID p_camera, float p_size, Vector2 p_offset, float p_z_near, float p_z_far) { + Camera *camera = camera_owner.getornull(p_camera); + ERR_FAIL_COND(!camera); + camera->type = Camera::FRUSTUM; + camera->size = p_size; + camera->offset = p_offset; + camera->znear = p_z_near; + camera->zfar = p_z_far; +} + +void RenderingServerScene::camera_set_transform(RID p_camera, const Transform &p_transform) { + + Camera *camera = camera_owner.getornull(p_camera); + ERR_FAIL_COND(!camera); + camera->transform = p_transform.orthonormalized(); +} + +void RenderingServerScene::camera_set_cull_mask(RID p_camera, uint32_t p_layers) { + + Camera *camera = camera_owner.getornull(p_camera); + ERR_FAIL_COND(!camera); + + camera->visible_layers = p_layers; +} + +void RenderingServerScene::camera_set_environment(RID p_camera, RID p_env) { + + Camera *camera = camera_owner.getornull(p_camera); + ERR_FAIL_COND(!camera); + camera->env = p_env; +} + +void RenderingServerScene::camera_set_camera_effects(RID p_camera, RID p_fx) { + + Camera *camera = camera_owner.getornull(p_camera); + ERR_FAIL_COND(!camera); + camera->effects = p_fx; +} + +void RenderingServerScene::camera_set_use_vertical_aspect(RID p_camera, bool p_enable) { + + Camera *camera = camera_owner.getornull(p_camera); + ERR_FAIL_COND(!camera); + camera->vaspect = p_enable; +} + +/* SCENARIO API */ + +void *RenderingServerScene::_instance_pair(void *p_self, OctreeElementID, Instance *p_A, int, OctreeElementID, Instance *p_B, int) { + + //RenderingServerScene *self = (RenderingServerScene*)p_self; + Instance *A = p_A; + Instance *B = p_B; + + //instance indices are designed so greater always contains lesser + if (A->base_type > B->base_type) { + SWAP(A, B); //lesser always first + } + + if (B->base_type == RS::INSTANCE_LIGHT && ((1 << A->base_type) & RS::INSTANCE_GEOMETRY_MASK)) { + + InstanceLightData *light = static_cast<InstanceLightData *>(B->base_data); + InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(A->base_data); + + InstanceLightData::PairInfo pinfo; + pinfo.geometry = A; + pinfo.L = geom->lighting.push_back(B); + + List<InstanceLightData::PairInfo>::Element *E = light->geometries.push_back(pinfo); + + if (geom->can_cast_shadows) { + + light->shadow_dirty = true; + } + geom->lighting_dirty = true; + + return E; //this element should make freeing faster + } else if (B->base_type == RS::INSTANCE_REFLECTION_PROBE && ((1 << A->base_type) & RS::INSTANCE_GEOMETRY_MASK)) { + + InstanceReflectionProbeData *reflection_probe = static_cast<InstanceReflectionProbeData *>(B->base_data); + InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(A->base_data); + + InstanceReflectionProbeData::PairInfo pinfo; + pinfo.geometry = A; + pinfo.L = geom->reflection_probes.push_back(B); + + List<InstanceReflectionProbeData::PairInfo>::Element *E = reflection_probe->geometries.push_back(pinfo); + + geom->reflection_dirty = true; + + return E; //this element should make freeing faster + } else if (B->base_type == RS::INSTANCE_LIGHTMAP_CAPTURE && ((1 << A->base_type) & RS::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); + ((RenderingServerScene *)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 == RS::INSTANCE_GI_PROBE && ((1 << A->base_type) & RS::INSTANCE_GEOMETRY_MASK)) { + + InstanceGIProbeData *gi_probe = static_cast<InstanceGIProbeData *>(B->base_data); + InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(A->base_data); + + InstanceGIProbeData::PairInfo pinfo; + pinfo.geometry = A; + pinfo.L = geom->gi_probes.push_back(B); + + List<InstanceGIProbeData::PairInfo>::Element *E; + if (A->dynamic_gi) { + E = gi_probe->dynamic_geometries.push_back(pinfo); + } else { + E = gi_probe->geometries.push_back(pinfo); + } + + geom->gi_probes_dirty = true; + + return E; //this element should make freeing faster + + } else if (B->base_type == RS::INSTANCE_GI_PROBE && A->base_type == RS::INSTANCE_LIGHT) { + + InstanceGIProbeData *gi_probe = static_cast<InstanceGIProbeData *>(B->base_data); + return gi_probe->lights.insert(A); + } + + return NULL; +} +void RenderingServerScene::_instance_unpair(void *p_self, OctreeElementID, Instance *p_A, int, OctreeElementID, Instance *p_B, int, void *udata) { + + //RenderingServerScene *self = (RenderingServerScene*)p_self; + Instance *A = p_A; + Instance *B = p_B; + + //instance indices are designed so greater always contains lesser + if (A->base_type > B->base_type) { + SWAP(A, B); //lesser always first + } + + if (B->base_type == RS::INSTANCE_LIGHT && ((1 << A->base_type) & RS::INSTANCE_GEOMETRY_MASK)) { + + InstanceLightData *light = static_cast<InstanceLightData *>(B->base_data); + InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(A->base_data); + + List<InstanceLightData::PairInfo>::Element *E = reinterpret_cast<List<InstanceLightData::PairInfo>::Element *>(udata); + + geom->lighting.erase(E->get().L); + light->geometries.erase(E); + + if (geom->can_cast_shadows) { + light->shadow_dirty = true; + } + geom->lighting_dirty = true; + + } else if (B->base_type == RS::INSTANCE_REFLECTION_PROBE && ((1 << A->base_type) & RS::INSTANCE_GEOMETRY_MASK)) { + + InstanceReflectionProbeData *reflection_probe = static_cast<InstanceReflectionProbeData *>(B->base_data); + InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(A->base_data); + + List<InstanceReflectionProbeData::PairInfo>::Element *E = reinterpret_cast<List<InstanceReflectionProbeData::PairInfo>::Element *>(udata); + + geom->reflection_probes.erase(E->get().L); + reflection_probe->geometries.erase(E); + + geom->reflection_dirty = true; + } else if (B->base_type == RS::INSTANCE_LIGHTMAP_CAPTURE && ((1 << A->base_type) & RS::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); + ((RenderingServerScene *)p_self)->_instance_queue_update(A, false, false); //need to update capture + + } else if (B->base_type == RS::INSTANCE_GI_PROBE && ((1 << A->base_type) & RS::INSTANCE_GEOMETRY_MASK)) { + + InstanceGIProbeData *gi_probe = static_cast<InstanceGIProbeData *>(B->base_data); + InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(A->base_data); + + List<InstanceGIProbeData::PairInfo>::Element *E = reinterpret_cast<List<InstanceGIProbeData::PairInfo>::Element *>(udata); + + geom->gi_probes.erase(E->get().L); + if (A->dynamic_gi) { + gi_probe->dynamic_geometries.erase(E); + } else { + gi_probe->geometries.erase(E); + } + + geom->gi_probes_dirty = true; + + } else if (B->base_type == RS::INSTANCE_GI_PROBE && A->base_type == RS::INSTANCE_LIGHT) { + + InstanceGIProbeData *gi_probe = static_cast<InstanceGIProbeData *>(B->base_data); + Set<Instance *>::Element *E = reinterpret_cast<Set<Instance *>::Element *>(udata); + + gi_probe->lights.erase(E); + } +} + +RID RenderingServerScene::scenario_create() { + + Scenario *scenario = memnew(Scenario); + ERR_FAIL_COND_V(!scenario, RID()); + RID scenario_rid = scenario_owner.make_rid(scenario); + scenario->self = scenario_rid; + + scenario->octree.set_pair_callback(_instance_pair, this); + scenario->octree.set_unpair_callback(_instance_unpair, this); + scenario->reflection_probe_shadow_atlas = RSG::scene_render->shadow_atlas_create(); + RSG::scene_render->shadow_atlas_set_size(scenario->reflection_probe_shadow_atlas, 1024); //make enough shadows for close distance, don't bother with rest + RSG::scene_render->shadow_atlas_set_quadrant_subdivision(scenario->reflection_probe_shadow_atlas, 0, 4); + RSG::scene_render->shadow_atlas_set_quadrant_subdivision(scenario->reflection_probe_shadow_atlas, 1, 4); + RSG::scene_render->shadow_atlas_set_quadrant_subdivision(scenario->reflection_probe_shadow_atlas, 2, 4); + RSG::scene_render->shadow_atlas_set_quadrant_subdivision(scenario->reflection_probe_shadow_atlas, 3, 8); + scenario->reflection_atlas = RSG::scene_render->reflection_atlas_create(); + return scenario_rid; +} + +void RenderingServerScene::scenario_set_debug(RID p_scenario, RS::ScenarioDebugMode p_debug_mode) { + + Scenario *scenario = scenario_owner.getornull(p_scenario); + ERR_FAIL_COND(!scenario); + scenario->debug = p_debug_mode; +} + +void RenderingServerScene::scenario_set_environment(RID p_scenario, RID p_environment) { + + Scenario *scenario = scenario_owner.getornull(p_scenario); + ERR_FAIL_COND(!scenario); + scenario->environment = p_environment; +} + +void RenderingServerScene::scenario_set_camera_effects(RID p_scenario, RID p_camera_effects) { + + Scenario *scenario = scenario_owner.getornull(p_scenario); + ERR_FAIL_COND(!scenario); + scenario->camera_effects = p_camera_effects; +} + +void RenderingServerScene::scenario_set_fallback_environment(RID p_scenario, RID p_environment) { + + Scenario *scenario = scenario_owner.getornull(p_scenario); + ERR_FAIL_COND(!scenario); + scenario->fallback_environment = p_environment; +} + +void RenderingServerScene::scenario_set_reflection_atlas_size(RID p_scenario, int p_reflection_size, int p_reflection_count) { + + Scenario *scenario = scenario_owner.getornull(p_scenario); + ERR_FAIL_COND(!scenario); + RSG::scene_render->reflection_atlas_set_size(scenario->reflection_atlas, p_reflection_size, p_reflection_count); +} + +/* INSTANCING API */ + +void RenderingServerScene::_instance_queue_update(Instance *p_instance, bool p_update_aabb, bool p_update_dependencies) { + + if (p_update_aabb) + p_instance->update_aabb = true; + if (p_update_dependencies) + p_instance->update_dependencies = true; + + if (p_instance->update_item.in_list()) + return; + + _instance_update_list.add(&p_instance->update_item); +} + +RID RenderingServerScene::instance_create() { + + Instance *instance = memnew(Instance); + ERR_FAIL_COND_V(!instance, RID()); + + RID instance_rid = instance_owner.make_rid(instance); + instance->self = instance_rid; + + return instance_rid; +} + +void RenderingServerScene::instance_set_base(RID p_instance, RID p_base) { + + Instance *instance = instance_owner.getornull(p_instance); + ERR_FAIL_COND(!instance); + + Scenario *scenario = instance->scenario; + + if (instance->base_type != RS::INSTANCE_NONE) { + //free anything related to that base + + if (scenario && instance->octree_id) { + scenario->octree.erase(instance->octree_id); //make dependencies generated by the octree go away + instance->octree_id = 0; + } + + switch (instance->base_type) { + case RS::INSTANCE_LIGHT: { + + InstanceLightData *light = static_cast<InstanceLightData *>(instance->base_data); +#ifdef DEBUG_ENABLED + if (light->geometries.size()) { + ERR_PRINT("BUG, indexing did not unpair geometries from light."); + } +#endif + if (instance->scenario && light->D) { + instance->scenario->directional_lights.erase(light->D); + light->D = NULL; + } + RSG::scene_render->free(light->instance); + } break; + case RS::INSTANCE_REFLECTION_PROBE: { + + InstanceReflectionProbeData *reflection_probe = static_cast<InstanceReflectionProbeData *>(instance->base_data); + RSG::scene_render->free(reflection_probe->instance); + if (reflection_probe->update_list.in_list()) { + reflection_probe_render_list.remove(&reflection_probe->update_list); + } + } break; + case RS::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 RS::INSTANCE_GI_PROBE: { + + InstanceGIProbeData *gi_probe = static_cast<InstanceGIProbeData *>(instance->base_data); +#ifdef DEBUG_ENABLED + if (gi_probe->geometries.size()) { + ERR_PRINT("BUG, indexing did not unpair geometries from GIProbe."); + } +#endif +#ifdef DEBUG_ENABLED + if (gi_probe->lights.size()) { + ERR_PRINT("BUG, indexing did not unpair lights from GIProbe."); + } +#endif + if (gi_probe->update_element.in_list()) { + gi_probe_update_list.remove(&gi_probe->update_element); + } + + 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(); + } + + RSG::scene_render->free(gi_probe->probe_instance); + + } break; + default: { + } + } + + if (instance->base_data) { + memdelete(instance->base_data); + instance->base_data = NULL; + } + + instance->blend_values.clear(); + instance->materials.clear(); + } + + instance->base_type = RS::INSTANCE_NONE; + instance->base = RID(); + + if (p_base.is_valid()) { + + instance->base_type = RSG::storage->get_base_type(p_base); + ERR_FAIL_COND(instance->base_type == RS::INSTANCE_NONE); + + switch (instance->base_type) { + case RS::INSTANCE_LIGHT: { + + InstanceLightData *light = memnew(InstanceLightData); + + if (scenario && RSG::storage->light_get_type(p_base) == RS::LIGHT_DIRECTIONAL) { + light->D = scenario->directional_lights.push_back(instance); + } + + light->instance = RSG::scene_render->light_instance_create(p_base); + + instance->base_data = light; + } break; + case RS::INSTANCE_MESH: + case RS::INSTANCE_MULTIMESH: + case RS::INSTANCE_IMMEDIATE: + case RS::INSTANCE_PARTICLES: { + + InstanceGeometryData *geom = memnew(InstanceGeometryData); + instance->base_data = geom; + if (instance->base_type == RS::INSTANCE_MESH) { + instance->blend_values.resize(RSG::storage->mesh_get_blend_shape_count(p_base)); + } + } break; + case RS::INSTANCE_REFLECTION_PROBE: { + + InstanceReflectionProbeData *reflection_probe = memnew(InstanceReflectionProbeData); + reflection_probe->owner = instance; + instance->base_data = reflection_probe; + + reflection_probe->instance = RSG::scene_render->reflection_probe_instance_create(p_base); + } break; + case RS::INSTANCE_LIGHTMAP_CAPTURE: { + + InstanceLightmapCaptureData *lightmap_capture = memnew(InstanceLightmapCaptureData); + instance->base_data = lightmap_capture; + //lightmap_capture->instance = RSG::scene_render->lightmap_capture_instance_create(p_base); + } break; + case RS::INSTANCE_GI_PROBE: { + + InstanceGIProbeData *gi_probe = memnew(InstanceGIProbeData); + instance->base_data = gi_probe; + gi_probe->owner = instance; + + if (scenario && !gi_probe->update_element.in_list()) { + gi_probe_update_list.add(&gi_probe->update_element); + } + + gi_probe->probe_instance = RSG::scene_render->gi_probe_instance_create(p_base); + + } break; + default: { + } + } + + instance->base = p_base; + + //forcefully update the dependency now, so if for some reason it gets removed, we can immediately clear it + RSG::storage->base_update_dependency(p_base, instance); + } + + _instance_queue_update(instance, true, true); +} +void RenderingServerScene::instance_set_scenario(RID p_instance, RID p_scenario) { + + Instance *instance = instance_owner.getornull(p_instance); + ERR_FAIL_COND(!instance); + + if (instance->scenario) { + + instance->scenario->instances.remove(&instance->scenario_item); + + if (instance->octree_id) { + instance->scenario->octree.erase(instance->octree_id); //make dependencies generated by the octree go away + instance->octree_id = 0; + } + + switch (instance->base_type) { + + case RS::INSTANCE_LIGHT: { + + InstanceLightData *light = static_cast<InstanceLightData *>(instance->base_data); +#ifdef DEBUG_ENABLED + if (light->geometries.size()) { + ERR_PRINT("BUG, indexing did not unpair geometries from light."); + } +#endif + if (light->D) { + instance->scenario->directional_lights.erase(light->D); + light->D = NULL; + } + } break; + case RS::INSTANCE_REFLECTION_PROBE: { + InstanceReflectionProbeData *reflection_probe = static_cast<InstanceReflectionProbeData *>(instance->base_data); + RSG::scene_render->reflection_probe_release_atlas_index(reflection_probe->instance); + + } break; + case RS::INSTANCE_GI_PROBE: { + + InstanceGIProbeData *gi_probe = static_cast<InstanceGIProbeData *>(instance->base_data); + +#ifdef DEBUG_ENABLED + if (gi_probe->geometries.size()) { + ERR_PRINT("BUG, indexing did not unpair geometries from GIProbe."); + } +#endif +#ifdef DEBUG_ENABLED + if (gi_probe->lights.size()) { + ERR_PRINT("BUG, indexing did not unpair lights from GIProbe."); + } +#endif + + if (gi_probe->update_element.in_list()) { + gi_probe_update_list.remove(&gi_probe->update_element); + } + } break; + default: { + } + } + + instance->scenario = NULL; + } + + if (p_scenario.is_valid()) { + + Scenario *scenario = scenario_owner.getornull(p_scenario); + ERR_FAIL_COND(!scenario); + + instance->scenario = scenario; + + scenario->instances.add(&instance->scenario_item); + + switch (instance->base_type) { + + case RS::INSTANCE_LIGHT: { + + InstanceLightData *light = static_cast<InstanceLightData *>(instance->base_data); + + if (RSG::storage->light_get_type(instance->base) == RS::LIGHT_DIRECTIONAL) { + light->D = scenario->directional_lights.push_back(instance); + } + } break; + case RS::INSTANCE_GI_PROBE: { + + InstanceGIProbeData *gi_probe = static_cast<InstanceGIProbeData *>(instance->base_data); + if (!gi_probe->update_element.in_list()) { + gi_probe_update_list.add(&gi_probe->update_element); + } + } break; + default: { + } + } + + _instance_queue_update(instance, true, true); + } +} +void RenderingServerScene::instance_set_layer_mask(RID p_instance, uint32_t p_mask) { + + Instance *instance = instance_owner.getornull(p_instance); + ERR_FAIL_COND(!instance); + + instance->layer_mask = p_mask; +} +void RenderingServerScene::instance_set_transform(RID p_instance, const Transform &p_transform) { + + Instance *instance = instance_owner.getornull(p_instance); + ERR_FAIL_COND(!instance); + + if (instance->transform == p_transform) + return; //must be checked to avoid worst evil + +#ifdef DEBUG_ENABLED + + for (int i = 0; i < 4; i++) { + const Vector3 &v = i < 3 ? p_transform.basis.elements[i] : p_transform.origin; + ERR_FAIL_COND(Math::is_inf(v.x)); + ERR_FAIL_COND(Math::is_nan(v.x)); + ERR_FAIL_COND(Math::is_inf(v.y)); + ERR_FAIL_COND(Math::is_nan(v.y)); + ERR_FAIL_COND(Math::is_inf(v.z)); + ERR_FAIL_COND(Math::is_nan(v.z)); + } + +#endif + instance->transform = p_transform; + _instance_queue_update(instance, true); +} +void RenderingServerScene::instance_attach_object_instance_id(RID p_instance, ObjectID p_id) { + + Instance *instance = instance_owner.getornull(p_instance); + ERR_FAIL_COND(!instance); + + instance->object_id = p_id; +} +void RenderingServerScene::instance_set_blend_shape_weight(RID p_instance, int p_shape, float p_weight) { + + Instance *instance = instance_owner.getornull(p_instance); + ERR_FAIL_COND(!instance); + + if (instance->update_item.in_list()) { + _update_dirty_instance(instance); + } + + ERR_FAIL_INDEX(p_shape, instance->blend_values.size()); + instance->blend_values.write[p_shape] = p_weight; +} + +void RenderingServerScene::instance_set_surface_material(RID p_instance, int p_surface, RID p_material) { + + Instance *instance = instance_owner.getornull(p_instance); + ERR_FAIL_COND(!instance); + + if (instance->base_type == RS::INSTANCE_MESH) { + //may not have been updated yet, may also have not been set yet. When updated will be correcte, worst case + instance->materials.resize(MAX(p_surface + 1, RSG::storage->mesh_get_surface_count(instance->base))); + } + + ERR_FAIL_INDEX(p_surface, instance->materials.size()); + + instance->materials.write[p_surface] = p_material; + + _instance_queue_update(instance, false, true); +} + +void RenderingServerScene::instance_set_visible(RID p_instance, bool p_visible) { + + Instance *instance = instance_owner.getornull(p_instance); + ERR_FAIL_COND(!instance); + + if (instance->visible == p_visible) + return; + + instance->visible = p_visible; + + switch (instance->base_type) { + case RS::INSTANCE_LIGHT: { + if (RSG::storage->light_get_type(instance->base) != RS::LIGHT_DIRECTIONAL && instance->octree_id && instance->scenario) { + instance->scenario->octree.set_pairable(instance->octree_id, p_visible, 1 << RS::INSTANCE_LIGHT, p_visible ? RS::INSTANCE_GEOMETRY_MASK : 0); + } + + } break; + case RS::INSTANCE_REFLECTION_PROBE: { + if (instance->octree_id && instance->scenario) { + instance->scenario->octree.set_pairable(instance->octree_id, p_visible, 1 << RS::INSTANCE_REFLECTION_PROBE, p_visible ? RS::INSTANCE_GEOMETRY_MASK : 0); + } + + } break; + case RS::INSTANCE_LIGHTMAP_CAPTURE: { + if (instance->octree_id && instance->scenario) { + instance->scenario->octree.set_pairable(instance->octree_id, p_visible, 1 << RS::INSTANCE_LIGHTMAP_CAPTURE, p_visible ? RS::INSTANCE_GEOMETRY_MASK : 0); + } + + } break; + case RS::INSTANCE_GI_PROBE: { + if (instance->octree_id && instance->scenario) { + instance->scenario->octree.set_pairable(instance->octree_id, p_visible, 1 << RS::INSTANCE_GI_PROBE, p_visible ? (RS::INSTANCE_GEOMETRY_MASK | (1 << RS::INSTANCE_LIGHT)) : 0); + } + + } break; + default: { + } + } +} +inline bool is_geometry_instance(RenderingServer::InstanceType p_type) { + return p_type == RS::INSTANCE_MESH || p_type == RS::INSTANCE_MULTIMESH || p_type == RS::INSTANCE_PARTICLES || p_type == RS::INSTANCE_IMMEDIATE; +} + +void RenderingServerScene::instance_set_use_lightmap(RID p_instance, RID p_lightmap_instance, RID p_lightmap) { + + Instance *instance = instance_owner.getornull(p_instance); + ERR_FAIL_COND(!instance); + + 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.getornull(p_lightmap_instance); + ERR_FAIL_COND(!lightmap_instance); + ERR_FAIL_COND(lightmap_instance->base_type != RS::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 RenderingServerScene::instance_set_custom_aabb(RID p_instance, AABB p_aabb) { + + Instance *instance = instance_owner.getornull(p_instance); + ERR_FAIL_COND(!instance); + ERR_FAIL_COND(!is_geometry_instance(instance->base_type)); + + if (p_aabb != AABB()) { + + // Set custom AABB + if (instance->custom_aabb == NULL) + instance->custom_aabb = memnew(AABB); + *instance->custom_aabb = p_aabb; + + } else { + + // Clear custom AABB + if (instance->custom_aabb != NULL) { + memdelete(instance->custom_aabb); + instance->custom_aabb = NULL; + } + } + + if (instance->scenario) + _instance_queue_update(instance, true, false); +} + +void RenderingServerScene::instance_attach_skeleton(RID p_instance, RID p_skeleton) { + + Instance *instance = instance_owner.getornull(p_instance); + ERR_FAIL_COND(!instance); + + if (instance->skeleton == p_skeleton) + return; + + instance->skeleton = p_skeleton; + + if (p_skeleton.is_valid()) { + //update the dependency now, so if cleared, we remove it + RSG::storage->skeleton_update_dependency(p_skeleton, instance); + } + _instance_queue_update(instance, true, true); +} + +void RenderingServerScene::instance_set_exterior(RID p_instance, bool p_enabled) { +} + +void RenderingServerScene::instance_set_extra_visibility_margin(RID p_instance, real_t p_margin) { + Instance *instance = instance_owner.getornull(p_instance); + ERR_FAIL_COND(!instance); + + instance->extra_margin = p_margin; + _instance_queue_update(instance, true, false); +} + +Vector<ObjectID> RenderingServerScene::instances_cull_aabb(const AABB &p_aabb, RID p_scenario) const { + + Vector<ObjectID> instances; + Scenario *scenario = scenario_owner.getornull(p_scenario); + ERR_FAIL_COND_V(!scenario, instances); + + const_cast<RenderingServerScene *>(this)->update_dirty_instances(); // check dirty instances before culling + + int culled = 0; + Instance *cull[1024]; + culled = scenario->octree.cull_aabb(p_aabb, cull, 1024); + + for (int i = 0; i < culled; i++) { + + Instance *instance = cull[i]; + ERR_CONTINUE(!instance); + if (instance->object_id.is_null()) + continue; + + instances.push_back(instance->object_id); + } + + return instances; +} +Vector<ObjectID> RenderingServerScene::instances_cull_ray(const Vector3 &p_from, const Vector3 &p_to, RID p_scenario) const { + + Vector<ObjectID> instances; + Scenario *scenario = scenario_owner.getornull(p_scenario); + ERR_FAIL_COND_V(!scenario, instances); + const_cast<RenderingServerScene *>(this)->update_dirty_instances(); // check dirty instances before culling + + int culled = 0; + Instance *cull[1024]; + culled = scenario->octree.cull_segment(p_from, p_from + p_to * 10000, cull, 1024); + + for (int i = 0; i < culled; i++) { + Instance *instance = cull[i]; + ERR_CONTINUE(!instance); + if (instance->object_id.is_null()) + continue; + + instances.push_back(instance->object_id); + } + + return instances; +} +Vector<ObjectID> RenderingServerScene::instances_cull_convex(const Vector<Plane> &p_convex, RID p_scenario) const { + + Vector<ObjectID> instances; + Scenario *scenario = scenario_owner.getornull(p_scenario); + ERR_FAIL_COND_V(!scenario, instances); + const_cast<RenderingServerScene *>(this)->update_dirty_instances(); // check dirty instances before culling + + int culled = 0; + Instance *cull[1024]; + + culled = scenario->octree.cull_convex(p_convex, cull, 1024); + + for (int i = 0; i < culled; i++) { + + Instance *instance = cull[i]; + ERR_CONTINUE(!instance); + if (instance->object_id.is_null()) + continue; + + instances.push_back(instance->object_id); + } + + return instances; +} + +void RenderingServerScene::instance_geometry_set_flag(RID p_instance, RS::InstanceFlags p_flags, bool p_enabled) { + + Instance *instance = instance_owner.getornull(p_instance); + ERR_FAIL_COND(!instance); + + //ERR_FAIL_COND(((1 << instance->base_type) & RS::INSTANCE_GEOMETRY_MASK)); + + switch (p_flags) { + + case RS::INSTANCE_FLAG_USE_BAKED_LIGHT: { + + instance->baked_light = p_enabled; + + } break; + case RS::INSTANCE_FLAG_USE_DYNAMIC_GI: { + + if (p_enabled == instance->dynamic_gi) { + //bye, redundant + return; + } + + if (instance->octree_id != 0) { + //remove from octree, it needs to be re-paired + instance->scenario->octree.erase(instance->octree_id); + instance->octree_id = 0; + _instance_queue_update(instance, true, true); + } + + //once out of octree, can be changed + instance->dynamic_gi = p_enabled; + + } break; + case RS::INSTANCE_FLAG_DRAW_NEXT_FRAME_IF_VISIBLE: { + + instance->redraw_if_visible = p_enabled; + + } break; + default: { + } + } +} +void RenderingServerScene::instance_geometry_set_cast_shadows_setting(RID p_instance, RS::ShadowCastingSetting p_shadow_casting_setting) { + + Instance *instance = instance_owner.getornull(p_instance); + ERR_FAIL_COND(!instance); + + instance->cast_shadows = p_shadow_casting_setting; + _instance_queue_update(instance, false, true); +} +void RenderingServerScene::instance_geometry_set_material_override(RID p_instance, RID p_material) { + + Instance *instance = instance_owner.getornull(p_instance); + ERR_FAIL_COND(!instance); + + instance->material_override = p_material; + _instance_queue_update(instance, false, true); +} + +void RenderingServerScene::instance_geometry_set_draw_range(RID p_instance, float p_min, float p_max, float p_min_margin, float p_max_margin) { +} +void RenderingServerScene::instance_geometry_set_as_instance_lod(RID p_instance, RID p_as_lod_of_instance) { +} + +void RenderingServerScene::_update_instance(Instance *p_instance) { + + p_instance->version++; + + if (p_instance->base_type == RS::INSTANCE_LIGHT) { + + InstanceLightData *light = static_cast<InstanceLightData *>(p_instance->base_data); + + RSG::scene_render->light_instance_set_transform(light->instance, p_instance->transform); + light->shadow_dirty = true; + } + + if (p_instance->base_type == RS::INSTANCE_REFLECTION_PROBE) { + + InstanceReflectionProbeData *reflection_probe = static_cast<InstanceReflectionProbeData *>(p_instance->base_data); + + RSG::scene_render->reflection_probe_instance_set_transform(reflection_probe->instance, p_instance->transform); + reflection_probe->reflection_dirty = true; + } + + if (p_instance->base_type == RS::INSTANCE_GI_PROBE) { + + InstanceGIProbeData *gi_probe = static_cast<InstanceGIProbeData *>(p_instance->base_data); + + RSG::scene_render->gi_probe_instance_set_transform_to_data(gi_probe->probe_instance, p_instance->transform); + } + + if (p_instance->base_type == RS::INSTANCE_PARTICLES) { + + RSG::storage->particles_set_emission_transform(p_instance->base, p_instance->transform); + } + + if (p_instance->aabb.has_no_surface()) { + return; + } + + if ((1 << p_instance->base_type) & RS::INSTANCE_GEOMETRY_MASK) { + + InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(p_instance->base_data); + //make sure lights are updated if it casts shadow + + 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; + } + } + + 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; + + AABB new_aabb; + + new_aabb = p_instance->transform.xform(p_instance->aabb); + + p_instance->transformed_aabb = new_aabb; + + if (!p_instance->scenario) { + + return; + } + + if (p_instance->octree_id == 0) { + + uint32_t base_type = 1 << p_instance->base_type; + uint32_t pairable_mask = 0; + bool pairable = false; + + if (p_instance->base_type == RS::INSTANCE_LIGHT || p_instance->base_type == RS::INSTANCE_REFLECTION_PROBE || p_instance->base_type == RS::INSTANCE_LIGHTMAP_CAPTURE) { + + pairable_mask = p_instance->visible ? RS::INSTANCE_GEOMETRY_MASK : 0; + pairable = true; + } + + if (p_instance->base_type == RS::INSTANCE_GI_PROBE) { + //lights and geometries + pairable_mask = p_instance->visible ? RS::INSTANCE_GEOMETRY_MASK | (1 << RS::INSTANCE_LIGHT) : 0; + pairable = true; + } + + // not inside octree + p_instance->octree_id = p_instance->scenario->octree.create(p_instance, new_aabb, 0, pairable, base_type, pairable_mask); + + } else { + + /* + if (new_aabb==p_instance->data.transformed_aabb) + return; + */ + + p_instance->scenario->octree.move(p_instance->octree_id, new_aabb); + } +} + +void RenderingServerScene::_update_instance_aabb(Instance *p_instance) { + + AABB new_aabb; + + ERR_FAIL_COND(p_instance->base_type != RS::INSTANCE_NONE && !p_instance->base.is_valid()); + + switch (p_instance->base_type) { + case RenderingServer::INSTANCE_NONE: { + + // do nothing + } break; + case RenderingServer::INSTANCE_MESH: { + + if (p_instance->custom_aabb) + new_aabb = *p_instance->custom_aabb; + else + new_aabb = RSG::storage->mesh_get_aabb(p_instance->base, p_instance->skeleton); + + } break; + + case RenderingServer::INSTANCE_MULTIMESH: { + + if (p_instance->custom_aabb) + new_aabb = *p_instance->custom_aabb; + else + new_aabb = RSG::storage->multimesh_get_aabb(p_instance->base); + + } break; + case RenderingServer::INSTANCE_IMMEDIATE: { + + if (p_instance->custom_aabb) + new_aabb = *p_instance->custom_aabb; + else + new_aabb = RSG::storage->immediate_get_aabb(p_instance->base); + + } break; + case RenderingServer::INSTANCE_PARTICLES: { + + if (p_instance->custom_aabb) + new_aabb = *p_instance->custom_aabb; + else + new_aabb = RSG::storage->particles_get_aabb(p_instance->base); + + } break; + case RenderingServer::INSTANCE_LIGHT: { + + new_aabb = RSG::storage->light_get_aabb(p_instance->base); + + } break; + case RenderingServer::INSTANCE_REFLECTION_PROBE: { + + new_aabb = RSG::storage->reflection_probe_get_aabb(p_instance->base); + + } break; + case RenderingServer::INSTANCE_GI_PROBE: { + + new_aabb = RSG::storage->gi_probe_get_bounds(p_instance->base); + + } break; + case RenderingServer::INSTANCE_LIGHTMAP_CAPTURE: { + + new_aabb = RSG::storage->lightmap_capture_get_bounds(p_instance->base); + + } break; + default: { + } + } + + // <Zylann> This is why I didn't re-use Instance::aabb to implement custom AABBs + if (p_instance->extra_margin) + new_aabb.grow_by(p_instance->extra_margin); + + 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 RenderingServerScene::_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); + + for (int i = 0; i < 12; i++) + new (&p_instance->lightmap_capture_data.ptrw()[i]) Color; + + //this could use some sort of blending.. + for (List<Instance *>::Element *E = geom->lightmap_captures.front(); E; E = E->next()) { + const Vector<RasterizerStorage::LightmapCaptureOctree> *octree = RSG::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 = RSG::storage->lightmap_capture_get_octree_cell_transform(E->get()->base); + int cell_subdiv = RSG::storage->lightmap_capture_get_octree_cell_subdiv(E->get()->base); + to_cell_xform = to_cell_xform * E->get()->transform.affine_inverse(); + + const RasterizerStorage::LightmapCaptureOctree *octree_r = octree->ptr(); + + 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, pos, dir, cone_aperture, cell_subdiv); + p_instance->lightmap_capture_data.write[i] += capture; + } + } +} + +bool RenderingServerScene::_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); + + Transform light_transform = p_instance->transform; + light_transform.orthonormalize(); //scale does not count on lights + + bool animated_material_found = false; + + switch (RSG::storage->light_get_type(p_instance->base)) { + + case RS::LIGHT_DIRECTIONAL: { + + float max_distance = p_cam_projection.get_z_far(); + float shadow_max = RSG::storage->light_get_param(p_instance->base, RS::LIGHT_PARAM_SHADOW_MAX_DISTANCE); + if (shadow_max > 0 && !p_cam_orthogonal) { //its impractical (and leads to unwanted behaviors) to set max distance in orthogonal camera + max_distance = MIN(shadow_max, max_distance); + } + max_distance = MAX(max_distance, p_cam_projection.get_z_near() + 0.001); + float min_distance = MIN(p_cam_projection.get_z_near(), max_distance); + + RS::LightDirectionalShadowDepthRangeMode depth_range_mode = RSG::storage->light_directional_get_shadow_depth_range_mode(p_instance->base); + + if (depth_range_mode == RS::LIGHT_DIRECTIONAL_SHADOW_DEPTH_RANGE_OPTIMIZED) { + //optimize min/max + Vector<Plane> planes = p_cam_projection.get_projection_planes(p_cam_transform); + int cull_count = p_scenario->octree.cull_convex(planes, instance_shadow_cull_result, MAX_INSTANCE_CULL, RS::INSTANCE_GEOMETRY_MASK); + Plane base(p_cam_transform.origin, -p_cam_transform.basis.get_axis(2)); + //check distance max and min + + bool found_items = false; + float z_max = -1e20; + float z_min = 1e20; + + for (int i = 0; i < cull_count; i++) { + + Instance *instance = instance_shadow_cull_result[i]; + if (!instance->visible || !((1 << instance->base_type) & RS::INSTANCE_GEOMETRY_MASK) || !static_cast<InstanceGeometryData *>(instance->base_data)->can_cast_shadows) { + continue; + } + + if (static_cast<InstanceGeometryData *>(instance->base_data)->material_is_animated) { + animated_material_found = true; + } + + float max, min; + instance->transformed_aabb.project_range_in_plane(base, min, max); + + if (max > z_max) { + z_max = max; + } + + if (min < z_min) { + z_min = min; + } + + found_items = true; + } + + if (found_items) { + min_distance = MAX(min_distance, z_min); + max_distance = MIN(max_distance, z_max); + } + } + + float range = max_distance - min_distance; + + int splits = 0; + switch (RSG::storage->light_directional_get_shadow_mode(p_instance->base)) { + case RS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL: splits = 1; break; + case RS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_2_SPLITS: splits = 2; break; + case RS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_4_SPLITS: splits = 4; break; + } + + float distances[5]; + + distances[0] = min_distance; + for (int i = 0; i < splits; i++) { + distances[i + 1] = min_distance + RSG::storage->light_get_param(p_instance->base, RS::LightParam(RS::LIGHT_PARAM_SHADOW_SPLIT_1_OFFSET + i)) * range; + }; + + distances[splits] = max_distance; + + float texture_size = RSG::scene_render->get_directional_light_shadow_size(light->instance); + + bool overlap = RSG::storage->light_directional_get_blend_splits(p_instance->base); + + float first_radius = 0.0; + + for (int i = 0; i < splits; i++) { + + RENDER_TIMESTAMP("Culling Directional Light split" + itos(i)); + + // setup a camera matrix for that range! + CameraMatrix camera_matrix; + + float aspect = p_cam_projection.get_aspect(); + + if (p_cam_orthogonal) { + + Vector2 vp_he = p_cam_projection.get_viewport_half_extents(); + + camera_matrix.set_orthogonal(vp_he.y * 2.0, aspect, distances[(i == 0 || !overlap) ? i : i - 1], distances[i + 1], false); + } else { + + float fov = p_cam_projection.get_fov(); + camera_matrix.set_perspective(fov, aspect, distances[(i == 0 || !overlap) ? i : i - 1], distances[i + 1], false); + } + + //obtain the frustum endpoints + + Vector3 endpoints[8]; // frustum plane endpoints + bool res = camera_matrix.get_endpoints(p_cam_transform, endpoints); + ERR_CONTINUE(!res); + + // obtain the light frustm ranges (given endpoints) + + Transform transform = light_transform; //discard scale and stabilize light + + Vector3 x_vec = transform.basis.get_axis(Vector3::AXIS_X).normalized(); + Vector3 y_vec = transform.basis.get_axis(Vector3::AXIS_Y).normalized(); + Vector3 z_vec = transform.basis.get_axis(Vector3::AXIS_Z).normalized(); + //z_vec points agsint the camera, like in default opengl + + float x_min = 0.f, x_max = 0.f; + float y_min = 0.f, y_max = 0.f; + float z_min = 0.f, z_max = 0.f; + + // FIXME: z_max_cam is defined, computed, but not used below when setting up + // ortho_camera. Commented out for now to fix warnings but should be investigated. + float x_min_cam = 0.f, x_max_cam = 0.f; + float y_min_cam = 0.f, y_max_cam = 0.f; + float z_min_cam = 0.f; + //float z_max_cam = 0.f; + + float bias_scale = 1.0; + + //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 + + 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 + + if (i == 0) { + first_radius = radius; + } else { + bias_scale = radius / first_radius; + } + + 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; + + if (depth_range_mode == RS::LIGHT_DIRECTIONAL_SHADOW_DEPTH_RANGE_STABLE) { + //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 + + 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.write[0] = Plane(x_vec, x_max); + light_frustum_planes.write[1] = Plane(-x_vec, -x_min); + //top/bottom + light_frustum_planes.write[2] = Plane(y_vec, y_max); + light_frustum_planes.write[3] = Plane(-y_vec, -y_min); + //near/far + light_frustum_planes.write[4] = Plane(z_vec, z_max + 1e6); + light_frustum_planes.write[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, RS::INSTANCE_GEOMETRY_MASK); + + // a pre pass will need to be needed to determine the actual z-near to be used + + Plane near_plane(light_transform.origin, -light_transform.basis.get_axis(2)); + + 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) & RS::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--; + continue; + } + + instance->transformed_aabb.project_range_in_plane(Plane(z_vec, 0), min, max); + instance->depth = near_plane.distance_to(instance->transform.origin); + instance->depth_layer = 0; + 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 = transform.basis; + ortho_transform.origin = x_vec * (x_min_cam + half_x) + y_vec * (y_min_cam + half_y) + z_vec * z_max; + + RSG::scene_render->light_instance_set_shadow_transform(light->instance, ortho_camera, ortho_transform, 0, distances[i + 1], i, bias_scale); + } + + RSG::scene_render->render_shadow(light->instance, p_shadow_atlas, i, (RasterizerScene::InstanceBase **)instance_shadow_cull_result, cull_count); + } + + } break; + case RS::LIGHT_OMNI: { + + RS::LightOmniShadowMode shadow_mode = RSG::storage->light_omni_get_shadow_mode(p_instance->base); + + if (shadow_mode == RS::LIGHT_OMNI_SHADOW_DUAL_PARABOLOID || !RSG::scene_render->light_instances_can_render_shadow_cube()) { + + for (int i = 0; i < 2; i++) { + + //using this one ensures that raster deferred will have it + RENDER_TIMESTAMP("Culling Shadow Paraboloid" + itos(i)); + + float radius = RSG::storage->light_get_param(p_instance->base, RS::LIGHT_PARAM_RANGE); + + float z = i == 0 ? -1 : 1; + Vector<Plane> planes; + planes.resize(5); + planes.write[0] = light_transform.xform(Plane(Vector3(0, 0, z), radius)); + planes.write[1] = light_transform.xform(Plane(Vector3(1, 0, z).normalized(), radius)); + planes.write[2] = light_transform.xform(Plane(Vector3(-1, 0, z).normalized(), radius)); + planes.write[3] = light_transform.xform(Plane(Vector3(0, 1, z).normalized(), radius)); + planes.write[4] = light_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, RS::INSTANCE_GEOMETRY_MASK); + Plane near_plane(light_transform.origin, light_transform.basis.get_axis(2) * z); + + for (int j = 0; j < cull_count; j++) { + + Instance *instance = instance_shadow_cull_result[j]; + if (!instance->visible || !((1 << instance->base_type) & RS::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--; + } else { + if (static_cast<InstanceGeometryData *>(instance->base_data)->material_is_animated) { + animated_material_found = true; + } + + instance->depth = near_plane.distance_to(instance->transform.origin); + instance->depth_layer = 0; + } + } + + RSG::scene_render->light_instance_set_shadow_transform(light->instance, CameraMatrix(), light_transform, radius, 0, i); + RSG::scene_render->render_shadow(light->instance, p_shadow_atlas, i, (RasterizerScene::InstanceBase **)instance_shadow_cull_result, cull_count); + } + } else { //shadow cube + + float radius = RSG::storage->light_get_param(p_instance->base, RS::LIGHT_PARAM_RANGE); + CameraMatrix cm; + cm.set_perspective(90, 1, 0.01, radius); + + for (int i = 0; i < 6; i++) { + + RENDER_TIMESTAMP("Culling Shadow Cube side" + itos(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 = light_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, RS::INSTANCE_GEOMETRY_MASK); + + Plane near_plane(xform.origin, -xform.basis.get_axis(2)); + for (int j = 0; j < cull_count; j++) { + + Instance *instance = instance_shadow_cull_result[j]; + if (!instance->visible || !((1 << instance->base_type) & RS::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--; + } else { + if (static_cast<InstanceGeometryData *>(instance->base_data)->material_is_animated) { + animated_material_found = true; + } + instance->depth = near_plane.distance_to(instance->transform.origin); + instance->depth_layer = 0; + } + } + + RSG::scene_render->light_instance_set_shadow_transform(light->instance, cm, xform, radius, 0, i); + RSG::scene_render->render_shadow(light->instance, p_shadow_atlas, i, (RasterizerScene::InstanceBase **)instance_shadow_cull_result, cull_count); + } + + //restore the regular DP matrix + RSG::scene_render->light_instance_set_shadow_transform(light->instance, CameraMatrix(), light_transform, radius, 0, 0); + } + + } break; + case RS::LIGHT_SPOT: { + + RENDER_TIMESTAMP("Culling Spot Light"); + + float radius = RSG::storage->light_get_param(p_instance->base, RS::LIGHT_PARAM_RANGE); + float angle = RSG::storage->light_get_param(p_instance->base, RS::LIGHT_PARAM_SPOT_ANGLE); + + CameraMatrix cm; + cm.set_perspective(angle * 2.0, 1.0, 0.01, radius); + + Vector<Plane> planes = cm.get_projection_planes(light_transform); + int cull_count = p_scenario->octree.cull_convex(planes, instance_shadow_cull_result, MAX_INSTANCE_CULL, RS::INSTANCE_GEOMETRY_MASK); + + Plane near_plane(light_transform.origin, -light_transform.basis.get_axis(2)); + for (int j = 0; j < cull_count; j++) { + + Instance *instance = instance_shadow_cull_result[j]; + if (!instance->visible || !((1 << instance->base_type) & RS::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--; + } else { + if (static_cast<InstanceGeometryData *>(instance->base_data)->material_is_animated) { + animated_material_found = true; + } + instance->depth = near_plane.distance_to(instance->transform.origin); + instance->depth_layer = 0; + } + } + + RSG::scene_render->light_instance_set_shadow_transform(light->instance, cm, light_transform, radius, 0, 0); + RSG::scene_render->render_shadow(light->instance, p_shadow_atlas, 0, (RasterizerScene::InstanceBase **)instance_shadow_cull_result, cull_count); + + } break; + } + + return animated_material_found; +} + +void RenderingServerScene::render_camera(RID p_render_buffers, RID p_camera, RID p_scenario, Size2 p_viewport_size, RID p_shadow_atlas) { +// render to mono camera +#ifndef _3D_DISABLED + + Camera *camera = camera_owner.getornull(p_camera); + ERR_FAIL_COND(!camera); + + /* STEP 1 - SETUP CAMERA */ + CameraMatrix camera_matrix; + bool ortho = false; + + switch (camera->type) { + case Camera::ORTHOGONAL: { + + camera_matrix.set_orthogonal( + camera->size, + p_viewport_size.width / (float)p_viewport_size.height, + camera->znear, + camera->zfar, + camera->vaspect); + ortho = true; + } break; + case Camera::PERSPECTIVE: { + + camera_matrix.set_perspective( + camera->fov, + p_viewport_size.width / (float)p_viewport_size.height, + camera->znear, + camera->zfar, + camera->vaspect); + ortho = false; + + } break; + case Camera::FRUSTUM: { + + camera_matrix.set_frustum( + camera->size, + p_viewport_size.width / (float)p_viewport_size.height, + camera->offset, + camera->znear, + camera->zfar, + camera->vaspect); + ortho = false; + } break; + } + + _prepare_scene(camera->transform, camera_matrix, ortho, camera->env, camera->effects, camera->visible_layers, p_scenario, p_shadow_atlas, RID()); + _render_scene(p_render_buffers, camera->transform, camera_matrix, ortho, camera->env, camera->effects, p_scenario, p_shadow_atlas, RID(), -1); +#endif +} + +void RenderingServerScene::render_camera(RID p_render_buffers, Ref<ARVRInterface> &p_interface, ARVRInterface::Eyes p_eye, RID p_camera, RID p_scenario, Size2 p_viewport_size, RID p_shadow_atlas) { + // render for AR/VR interface + + Camera *camera = camera_owner.getornull(p_camera); + ERR_FAIL_COND(!camera); + + /* SETUP CAMERA, we are ignoring type and FOV here */ + float aspect = p_viewport_size.width / (float)p_viewport_size.height; + CameraMatrix camera_matrix = p_interface->get_projection_for_eye(p_eye, aspect, camera->znear, camera->zfar); + + // We also ignore our camera position, it will have been positioned with a slightly old tracking position. + // Instead we take our origin point and have our ar/vr interface add fresh tracking data! Whoohoo! + Transform world_origin = ARVRServer::get_singleton()->get_world_origin(); + Transform cam_transform = p_interface->get_transform_for_eye(p_eye, world_origin); + + // For stereo render we only prepare for our left eye and then reuse the outcome for our right eye + if (p_eye == ARVRInterface::EYE_LEFT) { + ///@TODO possibly move responsibility for this into our ARVRServer or ARVRInterface? + + // Center our transform, we assume basis is equal. + Transform mono_transform = cam_transform; + Transform right_transform = p_interface->get_transform_for_eye(ARVRInterface::EYE_RIGHT, world_origin); + mono_transform.origin += right_transform.origin; + mono_transform.origin *= 0.5; + + // We need to combine our projection frustums for culling. + // Ideally we should use our clipping planes for this and combine them, + // however our shadow map logic uses our projection matrix. + // Note: as our left and right frustums should be mirrored, we don't need our right projection matrix. + + // - get some base values we need + float eye_dist = (mono_transform.origin - cam_transform.origin).length(); + float z_near = camera_matrix.get_z_near(); // get our near plane + float z_far = camera_matrix.get_z_far(); // get our far plane + float width = (2.0 * z_near) / camera_matrix.matrix[0][0]; + float x_shift = width * camera_matrix.matrix[2][0]; + float height = (2.0 * z_near) / camera_matrix.matrix[1][1]; + float y_shift = height * camera_matrix.matrix[2][1]; + + // printf("Eye_dist = %f, Near = %f, Far = %f, Width = %f, Shift = %f\n", eye_dist, z_near, z_far, width, x_shift); + + // - calculate our near plane size (horizontal only, right_near is mirrored) + float left_near = -eye_dist - ((width - x_shift) * 0.5); + + // - calculate our far plane size (horizontal only, right_far is mirrored) + float left_far = -eye_dist - (z_far * (width - x_shift) * 0.5 / z_near); + float left_far_right_eye = eye_dist - (z_far * (width + x_shift) * 0.5 / z_near); + if (left_far > left_far_right_eye) { + // on displays smaller then double our iod, the right eye far frustrum can overtake the left eyes. + left_far = left_far_right_eye; + } + + // - figure out required z-shift + float slope = (left_far - left_near) / (z_far - z_near); + float z_shift = (left_near / slope) - z_near; + + // - figure out new vertical near plane size (this will be slightly oversized thanks to our z-shift) + float top_near = (height - y_shift) * 0.5; + top_near += (top_near / z_near) * z_shift; + float bottom_near = -(height + y_shift) * 0.5; + bottom_near += (bottom_near / z_near) * z_shift; + + // printf("Left_near = %f, Left_far = %f, Top_near = %f, Bottom_near = %f, Z_shift = %f\n", left_near, left_far, top_near, bottom_near, z_shift); + + // - generate our frustum + CameraMatrix combined_matrix; + combined_matrix.set_frustum(left_near, -left_near, bottom_near, top_near, z_near + z_shift, z_far + z_shift); + + // and finally move our camera back + Transform apply_z_shift; + apply_z_shift.origin = Vector3(0.0, 0.0, z_shift); // z negative is forward so this moves it backwards + mono_transform *= apply_z_shift; + + // now prepare our scene with our adjusted transform projection matrix + _prepare_scene(mono_transform, combined_matrix, false, camera->env, camera->effects, camera->visible_layers, p_scenario, p_shadow_atlas, RID()); + } else if (p_eye == ARVRInterface::EYE_MONO) { + // For mono render, prepare as per usual + _prepare_scene(cam_transform, camera_matrix, false, camera->env, camera->effects, camera->visible_layers, p_scenario, p_shadow_atlas, RID()); + } + + // And render our scene... + _render_scene(p_render_buffers, cam_transform, camera_matrix, false, camera->env, camera->effects, p_scenario, p_shadow_atlas, RID(), -1); +}; + +void RenderingServerScene::_prepare_scene(const Transform p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, RID p_force_environment, RID p_force_camera_effects, uint32_t p_visible_layers, RID p_scenario, RID p_shadow_atlas, RID p_reflection_probe, bool p_using_shadows) { + // Note, in stereo rendering: + // - p_cam_transform will be a transform in the middle of our two eyes + // - p_cam_projection is a wider frustrum that encompasses both eyes + + Scenario *scenario = scenario_owner.getornull(p_scenario); + + render_pass++; + uint32_t camera_layer_mask = p_visible_layers; + + RSG::scene_render->set_scene_pass(render_pass); + + RENDER_TIMESTAMP("Frustum Culling"); + + //rasterizer->set_camera(camera->transform, camera_matrix,ortho); + + Vector<Plane> planes = p_cam_projection.get_projection_planes(p_cam_transform); + + Plane near_plane(p_cam_transform.origin, -p_cam_transform.basis.get_axis(2).normalized()); + float z_far = p_cam_projection.get_z_far(); + + /* STEP 2 - CULL */ + instance_cull_count = scenario->octree.cull_convex(planes, instance_cull_result, MAX_INSTANCE_CULL); + light_cull_count = 0; + + reflection_probe_cull_count = 0; + gi_probe_cull_count = 0; + + //light_samplers_culled=0; + + /* + print_line("OT: "+rtos( (OS::get_singleton()->get_ticks_usec()-t)/1000.0)); + print_line("OTO: "+itos(p_scenario->octree.get_octant_count())); + print_line("OTE: "+itos(p_scenario->octree.get_elem_count())); + print_line("OTP: "+itos(p_scenario->octree.get_pair_count())); + */ + + /* STEP 3 - PROCESS PORTALS, VALIDATE ROOMS */ + //removed, will replace with culling + + /* STEP 4 - REMOVE FURTHER CULLED OBJECTS, ADD LIGHTS */ + + for (int i = 0; i < instance_cull_count; i++) { + + Instance *ins = instance_cull_result[i]; + + bool keep = false; + + if ((camera_layer_mask & ins->layer_mask) == 0) { + //failure + } else if (ins->base_type == RS::INSTANCE_LIGHT && ins->visible) { + + if (light_cull_count < MAX_LIGHTS_CULLED) { + + InstanceLightData *light = static_cast<InstanceLightData *>(ins->base_data); + + if (!light->geometries.empty()) { + //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; + if (p_shadow_atlas.is_valid() && RSG::storage->light_has_shadow(ins->base)) { + RSG::scene_render->light_instance_mark_visible(light->instance); //mark it visible for shadow allocation later + } + + light_cull_count++; + } + } + } else if (ins->base_type == RS::INSTANCE_REFLECTION_PROBE && ins->visible) { + + if (reflection_probe_cull_count < MAX_REFLECTION_PROBES_CULLED) { + + InstanceReflectionProbeData *reflection_probe = static_cast<InstanceReflectionProbeData *>(ins->base_data); + + if (p_reflection_probe != reflection_probe->instance) { + //avoid entering The Matrix + + if (!reflection_probe->geometries.empty()) { + //do not add this light if no geometry is affected by it.. + + if (reflection_probe->reflection_dirty || RSG::scene_render->reflection_probe_instance_needs_redraw(reflection_probe->instance)) { + if (!reflection_probe->update_list.in_list()) { + reflection_probe->render_step = 0; + reflection_probe_render_list.add_last(&reflection_probe->update_list); + } + + reflection_probe->reflection_dirty = false; + } + + if (RSG::scene_render->reflection_probe_instance_has_reflection(reflection_probe->instance)) { + reflection_probe_instance_cull_result[reflection_probe_cull_count] = reflection_probe->instance; + reflection_probe_cull_count++; + } + } + } + } + + } else if (ins->base_type == RS::INSTANCE_GI_PROBE && ins->visible) { + + InstanceGIProbeData *gi_probe = static_cast<InstanceGIProbeData *>(ins->base_data); + if (!gi_probe->update_element.in_list()) { + gi_probe_update_list.add(&gi_probe->update_element); + } + + if (gi_probe_cull_count < MAX_GI_PROBES_CULLED) { + gi_probe_instance_cull_result[gi_probe_cull_count] = gi_probe->probe_instance; + gi_probe_cull_count++; + } + + } else if (((1 << ins->base_type) & RS::INSTANCE_GEOMETRY_MASK) && ins->visible && ins->cast_shadows != RS::SHADOW_CASTING_SETTING_SHADOWS_ONLY) { + + keep = true; + + InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(ins->base_data); + + if (ins->redraw_if_visible) { + RenderingServerRaster::redraw_request(); + } + + if (ins->base_type == RS::INSTANCE_PARTICLES) { + //particles visible? process them + if (RSG::storage->particles_is_inactive(ins->base)) { + //but if nothing is going on, don't do it. + keep = false; + } else { + RSG::storage->particles_request_process(ins->base); + //particles visible? request redraw + RenderingServerRaster::redraw_request(); + } + } + + if (geom->lighting_dirty) { + int l = 0; + //only called when lights AABB enter/exit this geometry + ins->light_instances.resize(geom->lighting.size()); + + for (List<Instance *>::Element *E = geom->lighting.front(); E; E = E->next()) { + + InstanceLightData *light = static_cast<InstanceLightData *>(E->get()->base_data); + + ins->light_instances.write[l++] = light->instance; + } + + geom->lighting_dirty = false; + } + + if (geom->reflection_dirty) { + int l = 0; + //only called when reflection probe AABB enter/exit this geometry + ins->reflection_probe_instances.resize(geom->reflection_probes.size()); + + for (List<Instance *>::Element *E = geom->reflection_probes.front(); E; E = E->next()) { + + InstanceReflectionProbeData *reflection_probe = static_cast<InstanceReflectionProbeData *>(E->get()->base_data); + + ins->reflection_probe_instances.write[l++] = reflection_probe->instance; + } + + geom->reflection_dirty = false; + } + + if (geom->gi_probes_dirty) { + int l = 0; + //only called when reflection probe AABB enter/exit this geometry + ins->gi_probe_instances.resize(geom->gi_probes.size()); + + for (List<Instance *>::Element *E = geom->gi_probes.front(); E; E = E->next()) { + + InstanceGIProbeData *gi_probe = static_cast<InstanceGIProbeData *>(E->get()->base_data); + + ins->gi_probe_instances.write[l++] = gi_probe->probe_instance; + } + + geom->gi_probes_dirty = false; + } + + ins->depth = near_plane.distance_to(ins->transform.origin); + ins->depth_layer = CLAMP(int(ins->depth * 16 / z_far), 0, 15); + } + + if (!keep) { + // remove, no reason to keep + instance_cull_count--; + SWAP(instance_cull_result[i], instance_cull_result[instance_cull_count]); + i--; + ins->last_render_pass = 0; // make invalid + } else { + + ins->last_render_pass = render_pass; + } + } + + /* STEP 5 - PROCESS LIGHTS */ + + RID *directional_light_ptr = &light_instance_cull_result[light_cull_count]; + directional_light_count = 0; + + // directional lights + { + + Instance **lights_with_shadow = (Instance **)alloca(sizeof(Instance *) * scenario->directional_lights.size()); + 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) { + break; + } + + if (!E->get()->visible) + continue; + + InstanceLightData *light = static_cast<InstanceLightData *>(E->get()->base_data); + + //check shadow.. + + if (light) { + if (p_using_shadows && p_shadow_atlas.is_valid() && RSG::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; + } + } + + RSG::scene_render->set_directional_shadow_count(directional_shadow_count); + + for (int i = 0; i < directional_shadow_count; i++) { + + RENDER_TIMESTAMP(">Rendering Directional Light " + itos(i)); + + _light_instance_update_shadow(lights_with_shadow[i], p_cam_transform, p_cam_projection, p_cam_orthogonal, p_shadow_atlas, scenario); + + RENDER_TIMESTAMP("<Rendering Directional Light " + itos(i)); + } + } + + if (p_using_shadows) { //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 (!p_shadow_atlas.is_valid() || !RSG::storage->light_has_shadow(ins->base)) + continue; + + InstanceLightData *light = static_cast<InstanceLightData *>(ins->base_data); + + float coverage = 0.f; + + { //compute coverage + + Transform cam_xf = p_cam_transform; + float zn = p_cam_projection.get_z_near(); + Plane p(cam_xf.origin + cam_xf.basis.get_axis(2) * -zn, -cam_xf.basis.get_axis(2)); //camera near plane + + // near plane half width and height + Vector2 vp_half_extents = p_cam_projection.get_viewport_half_extents(); + + switch (RSG::storage->light_get_type(ins->base)) { + + case RS::LIGHT_OMNI: { + + float radius = RSG::storage->light_get_param(ins->base, RS::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 (!p_cam_orthogonal) { + //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_half_extents.x + vp_half_extents.y); + } break; + case RS::LIGHT_SPOT: { + + float radius = RSG::storage->light_get_param(ins->base, RS::LIGHT_PARAM_RANGE); + float angle = RSG::storage->light_get_param(ins->base, RS::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 (!p_cam_orthogonal) { + //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_half_extents.x + vp_half_extents.y); + + } break; + default: { + ERR_PRINT("Invalid Light Type"); + } + } + } + + if (light->shadow_dirty) { + light->last_version++; + light->shadow_dirty = false; + } + + bool redraw = RSG::scene_render->shadow_atlas_update_light(p_shadow_atlas, light->instance, coverage, light->last_version); + + if (redraw) { + //must redraw! + RENDER_TIMESTAMP(">Rendering Light " + itos(i)); + light->shadow_dirty = _light_instance_update_shadow(ins, p_cam_transform, p_cam_projection, p_cam_orthogonal, p_shadow_atlas, scenario); + RENDER_TIMESTAMP("<Rendering Light " + itos(i)); + } + } + } +} + +void RenderingServerScene::_render_scene(RID p_render_buffers, const Transform p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, RID p_force_environment, RID p_force_camera_effects, RID p_scenario, RID p_shadow_atlas, RID p_reflection_probe, int p_reflection_probe_pass) { + + Scenario *scenario = scenario_owner.getornull(p_scenario); + + /* ENVIRONMENT */ + + RID environment; + if (p_force_environment.is_valid()) //camera has more environment priority + environment = p_force_environment; + else if (scenario->environment.is_valid()) + environment = scenario->environment; + else + environment = scenario->fallback_environment; + + RID camera_effects; + if (p_force_camera_effects.is_valid()) { + camera_effects = p_force_camera_effects; + } else { + camera_effects = scenario->camera_effects; + } + /* PROCESS GEOMETRY AND DRAW SCENE */ + + RENDER_TIMESTAMP("Render Scene "); + RSG::scene_render->render_scene(p_render_buffers, p_cam_transform, p_cam_projection, p_cam_orthogonal, (RasterizerScene::InstanceBase **)instance_cull_result, instance_cull_count, light_instance_cull_result, light_cull_count + directional_light_count, reflection_probe_instance_cull_result, reflection_probe_cull_count, gi_probe_instance_cull_result, gi_probe_cull_count, environment, camera_effects, p_shadow_atlas, p_reflection_probe.is_valid() ? RID() : scenario->reflection_atlas, p_reflection_probe, p_reflection_probe_pass); +} + +void RenderingServerScene::render_empty_scene(RID p_render_buffers, RID p_scenario, RID p_shadow_atlas) { + +#ifndef _3D_DISABLED + + Scenario *scenario = scenario_owner.getornull(p_scenario); + + RID environment; + if (scenario->environment.is_valid()) + environment = scenario->environment; + else + environment = scenario->fallback_environment; + RENDER_TIMESTAMP("Render Empty Scene "); + RSG::scene_render->render_scene(p_render_buffers, Transform(), CameraMatrix(), true, NULL, 0, NULL, 0, NULL, 0, NULL, 0, environment, RID(), p_shadow_atlas, scenario->reflection_atlas, RID(), 0); +#endif +} + +bool RenderingServerScene::_render_reflection_probe_step(Instance *p_instance, int p_step) { + + InstanceReflectionProbeData *reflection_probe = static_cast<InstanceReflectionProbeData *>(p_instance->base_data); + Scenario *scenario = p_instance->scenario; + ERR_FAIL_COND_V(!scenario, true); + + RenderingServerRaster::redraw_request(); //update, so it updates in editor + + if (p_step == 0) { + + if (!RSG::scene_render->reflection_probe_instance_begin_render(reflection_probe->instance, scenario->reflection_atlas)) { + return true; //all full + } + } + + if (p_step >= 0 && p_step < 6) { + + 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) + }; + + Vector3 extents = RSG::storage->reflection_probe_get_extents(p_instance->base); + Vector3 origin_offset = RSG::storage->reflection_probe_get_origin_offset(p_instance->base); + float max_distance = RSG::storage->reflection_probe_get_origin_max_distance(p_instance->base); + + Vector3 edge = view_normals[p_step] * extents; + float distance = ABS(view_normals[p_step].dot(edge) - view_normals[p_step].dot(origin_offset)); //distance from origin offset to actual view distance limit + + max_distance = MAX(max_distance, distance); + + //render cubemap side + CameraMatrix cm; + cm.set_perspective(90, 1, 0.01, max_distance); + + Transform local_view; + local_view.set_look_at(origin_offset, origin_offset + view_normals[p_step], view_up[p_step]); + + Transform xform = p_instance->transform * local_view; + + RID shadow_atlas; + + bool use_shadows = RSG::storage->reflection_probe_renders_shadows(p_instance->base); + if (use_shadows) { + + shadow_atlas = scenario->reflection_probe_shadow_atlas; + } + + RENDER_TIMESTAMP("Render Reflection Probe, Step " + itos(p_step)); + _prepare_scene(xform, cm, false, RID(), RID(), RSG::storage->reflection_probe_get_cull_mask(p_instance->base), p_instance->scenario->self, shadow_atlas, reflection_probe->instance, use_shadows); + _render_scene(RID(), xform, cm, false, RID(), RID(), p_instance->scenario->self, shadow_atlas, reflection_probe->instance, p_step); + + } else { + //do roughness postprocess step until it believes it's done + RENDER_TIMESTAMP("Post-Process Reflection Probe, Step " + itos(p_step)); + return RSG::scene_render->reflection_probe_instance_postprocess_step(reflection_probe->instance); + } + + return false; +} + +void RenderingServerScene::render_probes() { + + /* REFLECTION PROBES */ + + SelfList<InstanceReflectionProbeData> *ref_probe = reflection_probe_render_list.first(); + + bool busy = false; + + while (ref_probe) { + + SelfList<InstanceReflectionProbeData> *next = ref_probe->next(); + RID base = ref_probe->self()->owner->base; + + switch (RSG::storage->reflection_probe_get_update_mode(base)) { + + case RS::REFLECTION_PROBE_UPDATE_ONCE: { + if (busy) //already rendering something + break; + + bool done = _render_reflection_probe_step(ref_probe->self()->owner, ref_probe->self()->render_step); + if (done) { + reflection_probe_render_list.remove(ref_probe); + } else { + ref_probe->self()->render_step++; + } + + busy = true; //do not render another one of this kind + } break; + case RS::REFLECTION_PROBE_UPDATE_ALWAYS: { + + int step = 0; + bool done = false; + while (!done) { + done = _render_reflection_probe_step(ref_probe->self()->owner, step); + step++; + } + + reflection_probe_render_list.remove(ref_probe); + } break; + } + + ref_probe = next; + } + + /* GI PROBES */ + + SelfList<InstanceGIProbeData> *gi_probe = gi_probe_update_list.first(); + + if (gi_probe) { + RENDER_TIMESTAMP("Render GI Probes"); + } + + while (gi_probe) { + + SelfList<InstanceGIProbeData> *next = gi_probe->next(); + + InstanceGIProbeData *probe = gi_probe->self(); + //Instance *instance_probe = probe->owner; + + //check if probe must be setup, but don't do if on the lighting thread + + bool cache_dirty = false; + int cache_count = 0; + { + + int light_cache_size = probe->light_cache.size(); + const InstanceGIProbeData::LightCache *caches = probe->light_cache.ptr(); + const RID *instance_caches = probe->light_instances.ptr(); + + int idx = 0; //must count visible lights + for (Set<Instance *>::Element *E = probe->lights.front(); E; E = E->next()) { + Instance *instance = E->get(); + InstanceLightData *instance_light = (InstanceLightData *)instance->base_data; + if (!instance->visible) { + continue; + } + if (cache_dirty) { + //do nothing, since idx must count all visible lights anyway + } else if (idx >= light_cache_size) { + cache_dirty = true; + } else { + + const InstanceGIProbeData::LightCache *cache = &caches[idx]; + + if ( + instance_caches[idx] != instance_light->instance || + cache->has_shadow != RSG::storage->light_has_shadow(instance->base) || + cache->type != RSG::storage->light_get_type(instance->base) || + cache->transform != instance->transform || + cache->color != RSG::storage->light_get_color(instance->base) || + cache->energy != RSG::storage->light_get_param(instance->base, RS::LIGHT_PARAM_ENERGY) || + cache->bake_energy != RSG::storage->light_get_param(instance->base, RS::LIGHT_PARAM_INDIRECT_ENERGY) || + cache->radius != RSG::storage->light_get_param(instance->base, RS::LIGHT_PARAM_RANGE) || + cache->attenuation != RSG::storage->light_get_param(instance->base, RS::LIGHT_PARAM_ATTENUATION) || + cache->spot_angle != RSG::storage->light_get_param(instance->base, RS::LIGHT_PARAM_SPOT_ANGLE) || + cache->spot_attenuation != RSG::storage->light_get_param(instance->base, RS::LIGHT_PARAM_SPOT_ATTENUATION)) { + cache_dirty = true; + } + } + + idx++; + } + + for (List<Instance *>::Element *E = probe->owner->scenario->directional_lights.front(); E; E = E->next()) { + + Instance *instance = E->get(); + InstanceLightData *instance_light = (InstanceLightData *)instance->base_data; + if (!instance->visible) { + continue; + } + if (cache_dirty) { + //do nothing, since idx must count all visible lights anyway + } else if (idx >= light_cache_size) { + cache_dirty = true; + } else { + + const InstanceGIProbeData::LightCache *cache = &caches[idx]; + + if ( + instance_caches[idx] != instance_light->instance || + cache->has_shadow != RSG::storage->light_has_shadow(instance->base) || + cache->type != RSG::storage->light_get_type(instance->base) || + cache->transform != instance->transform || + cache->color != RSG::storage->light_get_color(instance->base) || + cache->energy != RSG::storage->light_get_param(instance->base, RS::LIGHT_PARAM_ENERGY) || + cache->bake_energy != RSG::storage->light_get_param(instance->base, RS::LIGHT_PARAM_INDIRECT_ENERGY) || + cache->radius != RSG::storage->light_get_param(instance->base, RS::LIGHT_PARAM_RANGE) || + cache->attenuation != RSG::storage->light_get_param(instance->base, RS::LIGHT_PARAM_ATTENUATION) || + cache->spot_angle != RSG::storage->light_get_param(instance->base, RS::LIGHT_PARAM_SPOT_ANGLE) || + cache->spot_attenuation != RSG::storage->light_get_param(instance->base, RS::LIGHT_PARAM_SPOT_ATTENUATION)) { + cache_dirty = true; + } + } + + idx++; + } + + if (idx != light_cache_size) { + cache_dirty = true; + } + + cache_count = idx; + } + + bool update_lights = RSG::scene_render->gi_probe_needs_update(probe->probe_instance); + + if (cache_dirty) { + probe->light_cache.resize(cache_count); + probe->light_instances.resize(cache_count); + + if (cache_count) { + InstanceGIProbeData::LightCache *caches = probe->light_cache.ptrw(); + RID *instance_caches = probe->light_instances.ptrw(); + + int idx = 0; //must count visible lights + for (Set<Instance *>::Element *E = probe->lights.front(); E; E = E->next()) { + Instance *instance = E->get(); + InstanceLightData *instance_light = (InstanceLightData *)instance->base_data; + if (!instance->visible) { + continue; + } + + InstanceGIProbeData::LightCache *cache = &caches[idx]; + + instance_caches[idx] = instance_light->instance; + cache->has_shadow = RSG::storage->light_has_shadow(instance->base); + cache->type = RSG::storage->light_get_type(instance->base); + cache->transform = instance->transform; + cache->color = RSG::storage->light_get_color(instance->base); + cache->energy = RSG::storage->light_get_param(instance->base, RS::LIGHT_PARAM_ENERGY); + cache->bake_energy = RSG::storage->light_get_param(instance->base, RS::LIGHT_PARAM_INDIRECT_ENERGY); + cache->radius = RSG::storage->light_get_param(instance->base, RS::LIGHT_PARAM_RANGE); + cache->attenuation = RSG::storage->light_get_param(instance->base, RS::LIGHT_PARAM_ATTENUATION); + cache->spot_angle = RSG::storage->light_get_param(instance->base, RS::LIGHT_PARAM_SPOT_ANGLE); + cache->spot_attenuation = RSG::storage->light_get_param(instance->base, RS::LIGHT_PARAM_SPOT_ATTENUATION); + + idx++; + } + for (List<Instance *>::Element *E = probe->owner->scenario->directional_lights.front(); E; E = E->next()) { + Instance *instance = E->get(); + InstanceLightData *instance_light = (InstanceLightData *)instance->base_data; + if (!instance->visible) { + continue; + } + + InstanceGIProbeData::LightCache *cache = &caches[idx]; + + instance_caches[idx] = instance_light->instance; + cache->has_shadow = RSG::storage->light_has_shadow(instance->base); + cache->type = RSG::storage->light_get_type(instance->base); + cache->transform = instance->transform; + cache->color = RSG::storage->light_get_color(instance->base); + cache->energy = RSG::storage->light_get_param(instance->base, RS::LIGHT_PARAM_ENERGY); + cache->bake_energy = RSG::storage->light_get_param(instance->base, RS::LIGHT_PARAM_INDIRECT_ENERGY); + cache->radius = RSG::storage->light_get_param(instance->base, RS::LIGHT_PARAM_RANGE); + cache->attenuation = RSG::storage->light_get_param(instance->base, RS::LIGHT_PARAM_ATTENUATION); + cache->spot_angle = RSG::storage->light_get_param(instance->base, RS::LIGHT_PARAM_SPOT_ANGLE); + cache->spot_attenuation = RSG::storage->light_get_param(instance->base, RS::LIGHT_PARAM_SPOT_ATTENUATION); + + idx++; + } + } + + update_lights = true; + } + + instance_cull_count = 0; + for (List<InstanceGIProbeData::PairInfo>::Element *E = probe->dynamic_geometries.front(); E; E = E->next()) { + if (instance_cull_count < MAX_INSTANCE_CULL) { + Instance *ins = E->get().geometry; + if (!ins->visible) { + continue; + } + InstanceGeometryData *geom = (InstanceGeometryData *)ins->base_data; + + if (geom->gi_probes_dirty) { + //giprobes may be dirty, so update + int l = 0; + //only called when reflection probe AABB enter/exit this geometry + ins->gi_probe_instances.resize(geom->gi_probes.size()); + + for (List<Instance *>::Element *F = geom->gi_probes.front(); F; F = F->next()) { + + InstanceGIProbeData *gi_probe2 = static_cast<InstanceGIProbeData *>(F->get()->base_data); + + ins->gi_probe_instances.write[l++] = gi_probe2->probe_instance; + } + + geom->gi_probes_dirty = false; + } + + instance_cull_result[instance_cull_count++] = E->get().geometry; + } + } + + RSG::scene_render->gi_probe_update(probe->probe_instance, update_lights, probe->light_instances, instance_cull_count, (RasterizerScene::InstanceBase **)instance_cull_result); + + gi_probe_update_list.remove(gi_probe); + + gi_probe = next; + } +} + +void RenderingServerScene::_update_dirty_instance(Instance *p_instance) { + + if (p_instance->update_aabb) { + _update_instance_aabb(p_instance); + } + + if (p_instance->update_dependencies) { + + p_instance->instance_increase_version(); + + if (p_instance->base.is_valid()) { + RSG::storage->base_update_dependency(p_instance->base, p_instance); + } + + if (p_instance->material_override.is_valid()) { + RSG::storage->material_update_dependency(p_instance->material_override, p_instance); + } + + if (p_instance->base_type == RS::INSTANCE_MESH) { + //remove materials no longer used and un-own them + + int new_mat_count = RSG::storage->mesh_get_surface_count(p_instance->base); + p_instance->materials.resize(new_mat_count); + + int new_blend_shape_count = RSG::storage->mesh_get_blend_shape_count(p_instance->base); + if (new_blend_shape_count != p_instance->blend_values.size()) { + p_instance->blend_values.resize(new_blend_shape_count); + for (int i = 0; i < new_blend_shape_count; i++) { + p_instance->blend_values.write[i] = 0; + } + } + } + + if ((1 << p_instance->base_type) & RS::INSTANCE_GEOMETRY_MASK) { + + InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(p_instance->base_data); + + bool can_cast_shadows = true; + bool is_animated = false; + + 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); + is_animated = RSG::storage->material_is_animated(p_instance->material_override); + } else { + + if (p_instance->base_type == RS::INSTANCE_MESH) { + RID mesh = p_instance->base; + + 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] : RSG::storage->mesh_surface_get_material(mesh, i); + + if (!mat.is_valid()) { + cast_shadows = true; + } else { + + if (RSG::storage->material_casts_shadows(mat)) { + cast_shadows = true; + } + + if (RSG::storage->material_is_animated(mat)) { + is_animated = true; + } + + RSG::storage->material_update_dependency(mat, p_instance); + } + } + + if (!cast_shadows) { + can_cast_shadows = false; + } + } + + } else if (p_instance->base_type == RS::INSTANCE_MULTIMESH) { + RID mesh = RSG::storage->multimesh_get_mesh(p_instance->base); + if (mesh.is_valid()) { + + bool cast_shadows = false; + + int sc = RSG::storage->mesh_get_surface_count(mesh); + for (int i = 0; i < sc; i++) { + + RID mat = RSG::storage->mesh_surface_get_material(mesh, i); + + if (!mat.is_valid()) { + cast_shadows = true; + + } else { + + if (RSG::storage->material_casts_shadows(mat)) { + cast_shadows = true; + } + if (RSG::storage->material_is_animated(mat)) { + is_animated = true; + } + + RSG::storage->material_update_dependency(mat, p_instance); + } + } + + if (!cast_shadows) { + can_cast_shadows = false; + } + + RSG::storage->base_update_dependency(mesh, p_instance); + } + } else if (p_instance->base_type == RS::INSTANCE_IMMEDIATE) { + + 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_is_animated(mat)) { + is_animated = true; + } + + if (mat.is_valid()) { + RSG::storage->material_update_dependency(mat, p_instance); + } + + } else if (p_instance->base_type == RS::INSTANCE_PARTICLES) { + + bool cast_shadows = false; + + int dp = RSG::storage->particles_get_draw_passes(p_instance->base); + + for (int i = 0; i < dp; i++) { + + RID mesh = RSG::storage->particles_get_draw_pass_mesh(p_instance->base, i); + if (!mesh.is_valid()) + continue; + + int sc = RSG::storage->mesh_get_surface_count(mesh); + for (int j = 0; j < sc; j++) { + + RID mat = RSG::storage->mesh_surface_get_material(mesh, j); + + if (!mat.is_valid()) { + cast_shadows = true; + } else { + + if (RSG::storage->material_casts_shadows(mat)) { + cast_shadows = true; + } + + if (RSG::storage->material_is_animated(mat)) { + is_animated = true; + } + + RSG::storage->material_update_dependency(mat, p_instance); + } + } + } + + 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; + } + + geom->material_is_animated = is_animated; + } + + if (p_instance->skeleton.is_valid()) { + RSG::storage->skeleton_update_dependency(p_instance->skeleton, p_instance); + } + + p_instance->clean_up_dependencies(); + } + + _instance_update_list.remove(&p_instance->update_item); + + _update_instance(p_instance); + + p_instance->update_aabb = false; + p_instance->update_dependencies = false; +} + +void RenderingServerScene::update_dirty_instances() { + + RSG::storage->update_dirty_resources(); + + while (_instance_update_list.first()) { + + _update_dirty_instance(_instance_update_list.first()->self()); + } +} + +bool RenderingServerScene::free(RID p_rid) { + + if (camera_owner.owns(p_rid)) { + + Camera *camera = camera_owner.getornull(p_rid); + + camera_owner.free(p_rid); + memdelete(camera); + + } else if (scenario_owner.owns(p_rid)) { + + Scenario *scenario = scenario_owner.getornull(p_rid); + + while (scenario->instances.first()) { + instance_set_scenario(scenario->instances.first()->self()->self, RID()); + } + RSG::scene_render->free(scenario->reflection_probe_shadow_atlas); + RSG::scene_render->free(scenario->reflection_atlas); + scenario_owner.free(p_rid); + memdelete(scenario); + + } else if (instance_owner.owns(p_rid)) { + // delete the instance + + update_dirty_instances(); + + Instance *instance = instance_owner.getornull(p_rid); + + instance_set_use_lightmap(p_rid, RID(), RID()); + instance_set_scenario(p_rid, RID()); + instance_set_base(p_rid, RID()); + instance_geometry_set_material_override(p_rid, RID()); + instance_attach_skeleton(p_rid, RID()); + + update_dirty_instances(); //in case something changed this + + instance_owner.free(p_rid); + memdelete(instance); + } else { + return false; + } + + return true; +} + +RenderingServerScene *RenderingServerScene::singleton = NULL; + +RenderingServerScene::RenderingServerScene() { + + render_pass = 1; + singleton = this; +} + +RenderingServerScene::~RenderingServerScene() { +} diff --git a/servers/rendering/rendering_server_scene.h b/servers/rendering/rendering_server_scene.h new file mode 100644 index 0000000000..6012a75660 --- /dev/null +++ b/servers/rendering/rendering_server_scene.h @@ -0,0 +1,440 @@ +/*************************************************************************/ +/* rendering_server_scene.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef VISUALSERVERSCENE_H +#define VISUALSERVERSCENE_H + +#include "servers/rendering/rasterizer.h" + +#include "core/math/geometry.h" +#include "core/math/octree.h" +#include "core/os/semaphore.h" +#include "core/os/thread.h" +#include "core/rid_owner.h" +#include "core/self_list.h" +#include "servers/arvr/arvr_interface.h" + +class RenderingServerScene { +public: + enum { + + MAX_INSTANCE_CULL = 65536, + MAX_LIGHTS_CULLED = 4096, + MAX_REFLECTION_PROBES_CULLED = 4096, + MAX_GI_PROBES_CULLED = 4096, + MAX_ROOM_CULL = 32, + MAX_EXTERIOR_PORTALS = 128, + }; + + uint64_t render_pass; + + static RenderingServerScene *singleton; + + /* CAMERA API */ + + struct Camera { + + enum Type { + PERSPECTIVE, + ORTHOGONAL, + FRUSTUM + }; + Type type; + float fov; + float znear, zfar; + float size; + Vector2 offset; + uint32_t visible_layers; + bool vaspect; + RID env; + RID effects; + + Transform transform; + + Camera() { + + visible_layers = 0xFFFFFFFF; + fov = 70; + type = PERSPECTIVE; + znear = 0.05; + zfar = 100; + size = 1.0; + offset = Vector2(); + vaspect = false; + } + }; + + mutable RID_PtrOwner<Camera> camera_owner; + + virtual RID camera_create(); + virtual void camera_set_perspective(RID p_camera, float p_fovy_degrees, float p_z_near, float p_z_far); + virtual void camera_set_orthogonal(RID p_camera, float p_size, float p_z_near, float p_z_far); + virtual void camera_set_frustum(RID p_camera, float p_size, Vector2 p_offset, float p_z_near, float p_z_far); + virtual void camera_set_transform(RID p_camera, const Transform &p_transform); + virtual void camera_set_cull_mask(RID p_camera, uint32_t p_layers); + virtual void camera_set_environment(RID p_camera, RID p_env); + virtual void camera_set_camera_effects(RID p_camera, RID p_fx); + virtual void camera_set_use_vertical_aspect(RID p_camera, bool p_enable); + + /* SCENARIO API */ + + struct Instance; + + struct Scenario { + + RS::ScenarioDebugMode debug; + RID self; + + Octree<Instance, true> octree; + + List<Instance *> directional_lights; + RID environment; + RID fallback_environment; + RID camera_effects; + RID reflection_probe_shadow_atlas; + RID reflection_atlas; + + SelfList<Instance>::List instances; + + Scenario() { debug = RS::SCENARIO_DEBUG_DISABLED; } + }; + + mutable RID_PtrOwner<Scenario> scenario_owner; + + static void *_instance_pair(void *p_self, OctreeElementID, Instance *p_A, int, OctreeElementID, Instance *p_B, int); + static void _instance_unpair(void *p_self, OctreeElementID, Instance *p_A, int, OctreeElementID, Instance *p_B, int, void *); + + virtual RID scenario_create(); + + virtual void scenario_set_debug(RID p_scenario, RS::ScenarioDebugMode p_debug_mode); + virtual void scenario_set_environment(RID p_scenario, RID p_environment); + virtual void scenario_set_camera_effects(RID p_scenario, RID p_fx); + virtual void scenario_set_fallback_environment(RID p_scenario, RID p_environment); + virtual void scenario_set_reflection_atlas_size(RID p_scenario, int p_reflection_size, int p_reflection_count); + + /* INSTANCING API */ + + struct InstanceBaseData { + + virtual ~InstanceBaseData() {} + }; + + struct Instance : RasterizerScene::InstanceBase { + + RID self; + //scenario stuff + OctreeElementID octree_id; + Scenario *scenario; + SelfList<Instance> scenario_item; + + //aabb stuff + bool update_aabb; + bool update_dependencies; + + SelfList<Instance> update_item; + + AABB *custom_aabb; // <Zylann> would using aabb directly with a bool be better? + float extra_margin; + ObjectID object_id; + + float lod_begin; + float lod_end; + float lod_begin_hysteresis; + float lod_end_hysteresis; + RID lod_instance; + + uint64_t last_render_pass; + uint64_t last_frame_pass; + + uint64_t version; // changes to this, and changes to base increase version + + InstanceBaseData *base_data; + + virtual void dependency_deleted(RID p_dependency) { + if (p_dependency == base) { + singleton->instance_set_base(self, RID()); + } else if (p_dependency == skeleton) { + singleton->instance_attach_skeleton(self, RID()); + } else { + singleton->_instance_queue_update(this, false, true); + } + } + + virtual void dependency_changed(bool p_aabb, bool p_dependencies) { + singleton->_instance_queue_update(this, p_aabb, p_dependencies); + } + + Instance() : + scenario_item(this), + update_item(this) { + + octree_id = 0; + scenario = NULL; + + update_aabb = false; + update_dependencies = false; + + extra_margin = 0; + + visible = true; + + lod_begin = 0; + lod_end = 0; + lod_begin_hysteresis = 0; + lod_end_hysteresis = 0; + + last_render_pass = 0; + last_frame_pass = 0; + version = 1; + base_data = NULL; + + custom_aabb = NULL; + } + + ~Instance() { + + if (base_data) + memdelete(base_data); + if (custom_aabb) + memdelete(custom_aabb); + } + }; + + SelfList<Instance>::List _instance_update_list; + void _instance_queue_update(Instance *p_instance, bool p_update_aabb, bool p_update_dependencies = false); + + struct InstanceGeometryData : public InstanceBaseData { + + List<Instance *> lighting; + bool lighting_dirty; + bool can_cast_shadows; + bool material_is_animated; + + List<Instance *> reflection_probes; + bool reflection_dirty; + + List<Instance *> gi_probes; + bool gi_probes_dirty; + + List<Instance *> lightmap_captures; + + InstanceGeometryData() { + + lighting_dirty = false; + reflection_dirty = true; + can_cast_shadows = true; + material_is_animated = true; + gi_probes_dirty = true; + } + }; + + struct InstanceReflectionProbeData : public InstanceBaseData { + + Instance *owner; + + struct PairInfo { + List<Instance *>::Element *L; //reflection iterator in geometry + Instance *geometry; + }; + List<PairInfo> geometries; + + RID instance; + bool reflection_dirty; + SelfList<InstanceReflectionProbeData> update_list; + + int render_step; + + InstanceReflectionProbeData() : + update_list(this) { + + reflection_dirty = true; + render_step = -1; + } + }; + + SelfList<InstanceReflectionProbeData>::List reflection_probe_render_list; + + struct InstanceLightData : public InstanceBaseData { + + struct PairInfo { + List<Instance *>::Element *L; //light iterator in geometry + Instance *geometry; + }; + + RID instance; + uint64_t last_version; + List<Instance *>::Element *D; // directional light in scenario + + bool shadow_dirty; + + List<PairInfo> geometries; + + Instance *baked_light; + + InstanceLightData() { + + shadow_dirty = true; + D = NULL; + last_version = 0; + baked_light = NULL; + } + }; + + struct InstanceGIProbeData : public InstanceBaseData { + + Instance *owner; + + struct PairInfo { + List<Instance *>::Element *L; //gi probe iterator in geometry + Instance *geometry; + }; + + List<PairInfo> geometries; + List<PairInfo> dynamic_geometries; + + Set<Instance *> lights; + + struct LightCache { + + RS::LightType type; + Transform transform; + Color color; + float energy; + float bake_energy; + float radius; + float attenuation; + float spot_angle; + float spot_attenuation; + bool has_shadow; + }; + + Vector<LightCache> light_cache; + Vector<RID> light_instances; + + RID probe_instance; + + bool invalid; + uint32_t base_version; + + SelfList<InstanceGIProbeData> update_element; + + InstanceGIProbeData() : + update_element(this) { + invalid = true; + base_version = 0; + } + }; + + 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() { + } + }; + + int instance_cull_count; + 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]; + RID light_instance_cull_result[MAX_LIGHTS_CULLED]; + int light_cull_count; + int directional_light_count; + RID reflection_probe_instance_cull_result[MAX_REFLECTION_PROBES_CULLED]; + int reflection_probe_cull_count; + RID gi_probe_instance_cull_result[MAX_GI_PROBES_CULLED]; + int gi_probe_cull_count; + + RID_PtrOwner<Instance> instance_owner; + + virtual RID instance_create(); + + virtual void instance_set_base(RID p_instance, RID p_base); + virtual void instance_set_scenario(RID p_instance, RID p_scenario); + virtual void instance_set_layer_mask(RID p_instance, uint32_t p_mask); + virtual void instance_set_transform(RID p_instance, const Transform &p_transform); + virtual void instance_attach_object_instance_id(RID p_instance, ObjectID p_id); + 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_instance, AABB p_aabb); + + virtual void instance_attach_skeleton(RID p_instance, RID p_skeleton); + virtual void instance_set_exterior(RID p_instance, bool p_enabled); + + virtual void instance_set_extra_visibility_margin(RID p_instance, real_t p_margin); + + // don't use these in a game! + virtual Vector<ObjectID> instances_cull_aabb(const AABB &p_aabb, RID p_scenario = RID()) const; + virtual Vector<ObjectID> instances_cull_ray(const Vector3 &p_from, const Vector3 &p_to, RID p_scenario = RID()) const; + virtual Vector<ObjectID> instances_cull_convex(const Vector<Plane> &p_convex, RID p_scenario = RID()) const; + + virtual void instance_geometry_set_flag(RID p_instance, RS::InstanceFlags p_flags, bool p_enabled); + virtual void instance_geometry_set_cast_shadows_setting(RID p_instance, RS::ShadowCastingSetting p_shadow_casting_setting); + virtual void instance_geometry_set_material_override(RID p_instance, RID p_material); + + virtual void instance_geometry_set_draw_range(RID p_instance, float p_min, float p_max, float p_min_margin, float p_max_margin); + virtual void instance_geometry_set_as_instance_lod(RID p_instance, RID p_as_lod_of_instance); + + _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_ bool _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); + + bool _render_reflection_probe_step(Instance *p_instance, int p_step); + void _prepare_scene(const Transform p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, RID p_force_environment, RID p_force_camera_effects, uint32_t p_visible_layers, RID p_scenario, RID p_shadow_atlas, RID p_reflection_probe, bool p_using_shadows = true); + void _render_scene(RID p_render_buffers, const Transform p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, RID p_force_environment, RID p_force_camera_effects, RID p_scenario, RID p_shadow_atlas, RID p_reflection_probe, int p_reflection_probe_pass); + void render_empty_scene(RID p_render_buffers, RID p_scenario, RID p_shadow_atlas); + + void render_camera(RID p_render_buffers, RID p_camera, RID p_scenario, Size2 p_viewport_size, RID p_shadow_atlas); + void render_camera(RID p_render_buffers, Ref<ARVRInterface> &p_interface, ARVRInterface::Eyes p_eye, RID p_camera, RID p_scenario, Size2 p_viewport_size, RID p_shadow_atlas); + void update_dirty_instances(); + + void render_probes(); + + bool free(RID p_rid); + + RenderingServerScene(); + virtual ~RenderingServerScene(); +}; + +#endif // VISUALSERVERSCENE_H diff --git a/servers/rendering/rendering_server_viewport.cpp b/servers/rendering/rendering_server_viewport.cpp new file mode 100644 index 0000000000..24b2379d97 --- /dev/null +++ b/servers/rendering/rendering_server_viewport.cpp @@ -0,0 +1,780 @@ +/*************************************************************************/ +/* rendering_server_viewport.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "rendering_server_viewport.h" + +#include "core/project_settings.h" +#include "rendering_server_canvas.h" +#include "rendering_server_globals.h" +#include "rendering_server_scene.h" + +static Transform2D _canvas_get_transform(RenderingServerViewport::Viewport *p_viewport, RenderingServerCanvas::Canvas *p_canvas, RenderingServerViewport::Viewport::CanvasData *p_canvas_data, const Vector2 &p_vp_size) { + + Transform2D xf = p_viewport->global_transform; + + float scale = 1.0; + if (p_viewport->canvas_map.has(p_canvas->parent)) { + xf = xf * p_viewport->canvas_map[p_canvas->parent].transform; + scale = p_canvas->parent_scale; + } + + xf = xf * p_canvas_data->transform; + + if (scale != 1.0 && !RSG::canvas->disable_scale) { + Vector2 pivot = p_vp_size * 0.5; + Transform2D xfpivot; + xfpivot.set_origin(pivot); + Transform2D xfscale; + xfscale.scale(Vector2(scale, scale)); + + xf = xfpivot.affine_inverse() * xf; + xf = xfscale * xf; + xf = xfpivot * xf; + } + + return xf; +} + +void RenderingServerViewport::_draw_3d(Viewport *p_viewport, ARVRInterface::Eyes p_eye) { + + RENDER_TIMESTAMP(">Begin Rendering 3D Scene"); + + Ref<ARVRInterface> arvr_interface; + if (ARVRServer::get_singleton() != NULL) { + arvr_interface = ARVRServer::get_singleton()->get_primary_interface(); + } + + if (p_viewport->use_arvr && arvr_interface.is_valid()) { + RSG::scene->render_camera(p_viewport->render_buffers, arvr_interface, p_eye, p_viewport->camera, p_viewport->scenario, p_viewport->size, p_viewport->shadow_atlas); + } else { + RSG::scene->render_camera(p_viewport->render_buffers, p_viewport->camera, p_viewport->scenario, p_viewport->size, p_viewport->shadow_atlas); + } + RENDER_TIMESTAMP("<End Rendering 3D Scene"); +} + +void RenderingServerViewport::_draw_viewport(Viewport *p_viewport, ARVRInterface::Eyes p_eye) { + + /* Camera should always be BEFORE any other 3D */ + + bool scenario_draw_canvas_bg = false; //draw canvas, or some layer of it, as BG for 3D instead of in front + int scenario_canvas_max_layer = 0; + + Color bgcolor = RSG::storage->get_default_clear_color(); + + if (!p_viewport->hide_canvas && !p_viewport->disable_environment && RSG::scene->scenario_owner.owns(p_viewport->scenario)) { + + RenderingServerScene::Scenario *scenario = RSG::scene->scenario_owner.getornull(p_viewport->scenario); + ERR_FAIL_COND(!scenario); + if (RSG::scene_render->is_environment(scenario->environment)) { + scenario_draw_canvas_bg = RSG::scene_render->environment_get_background(scenario->environment) == RS::ENV_BG_CANVAS; + + scenario_canvas_max_layer = RSG::scene_render->environment_get_canvas_max_layer(scenario->environment); + } + } + + bool can_draw_3d = RSG::scene->camera_owner.owns(p_viewport->camera); + + if (p_viewport->clear_mode != RS::VIEWPORT_CLEAR_NEVER) { + if (p_viewport->transparent_bg) { + bgcolor = Color(0, 0, 0, 0); + } + if (p_viewport->clear_mode == RS::VIEWPORT_CLEAR_ONLY_NEXT_FRAME) { + p_viewport->clear_mode = RS::VIEWPORT_CLEAR_NEVER; + } + } + + if ((scenario_draw_canvas_bg || can_draw_3d) && !p_viewport->render_buffers.is_valid()) { + //wants to draw 3D but there is no render buffer, create + p_viewport->render_buffers = RSG::scene_render->render_buffers_create(); + RSG::scene_render->render_buffers_configure(p_viewport->render_buffers, p_viewport->render_target, p_viewport->size.width, p_viewport->size.height, p_viewport->msaa); + } + + RSG::storage->render_target_request_clear(p_viewport->render_target, bgcolor); + + if (!scenario_draw_canvas_bg && can_draw_3d) { + _draw_3d(p_viewport, p_eye); + } + + if (!p_viewport->hide_canvas) { + int i = 0; + + Map<Viewport::CanvasKey, Viewport::CanvasData *> canvas_map; + + Rect2 clip_rect(0, 0, p_viewport->size.x, p_viewport->size.y); + RasterizerCanvas::Light *lights = NULL; + RasterizerCanvas::Light *lights_with_shadow = NULL; + RasterizerCanvas::Light *lights_with_mask = NULL; + Rect2 shadow_rect; + + int light_count = 0; + + RENDER_TIMESTAMP("Cull Canvas Lights"); + for (Map<RID, Viewport::CanvasData>::Element *E = p_viewport->canvas_map.front(); E; E = E->next()) { + + RenderingServerCanvas::Canvas *canvas = static_cast<RenderingServerCanvas::Canvas *>(E->get().canvas); + + Transform2D xf = _canvas_get_transform(p_viewport, canvas, &E->get(), clip_rect.size); + + //find lights in canvas + + for (Set<RasterizerCanvas::Light *>::Element *F = canvas->lights.front(); F; F = F->next()) { + + RasterizerCanvas::Light *cl = F->get(); + if (cl->enabled && cl->texture.is_valid()) { + //not super efficient.. + Size2 tsize = RSG::storage->texture_size_with_proxy(cl->texture); + tsize *= cl->scale; + + Vector2 offset = tsize / 2.0; + cl->rect_cache = Rect2(-offset + cl->texture_offset, tsize); + cl->xform_cache = xf * cl->xform; + + if (clip_rect.intersects_transformed(cl->xform_cache, cl->rect_cache)) { + + cl->filter_next_ptr = lights; + lights = cl; + // cl->texture_cache = NULL; + Transform2D scale; + scale.scale(cl->rect_cache.size); + scale.elements[2] = cl->rect_cache.position; + cl->light_shader_xform = cl->xform * scale; + //cl->light_shader_pos = cl->xform_cache[2]; + if (cl->use_shadow) { + + cl->shadows_next_ptr = lights_with_shadow; + if (lights_with_shadow == NULL) { + shadow_rect = cl->xform_cache.xform(cl->rect_cache); + } else { + shadow_rect = shadow_rect.merge(cl->xform_cache.xform(cl->rect_cache)); + } + lights_with_shadow = cl; + cl->radius_cache = cl->rect_cache.size.length(); + } + if (cl->mode == RS::CANVAS_LIGHT_MODE_MASK) { + cl->mask_next_ptr = lights_with_mask; + lights_with_mask = cl; + } + + light_count++; + } + + //guess this is not needed, but keeping because it may be + //RSG::canvas_render->light_internal_update(cl->light_internal, cl); + } + } + + canvas_map[Viewport::CanvasKey(E->key(), E->get().layer, E->get().sublayer)] = &E->get(); + } + + if (lights_with_shadow) { + //update shadows if any + + RasterizerCanvas::LightOccluderInstance *occluders = NULL; + + RENDER_TIMESTAMP(">Render 2D Shadows"); + RENDER_TIMESTAMP("Cull Occluders"); + + //make list of occluders + for (Map<RID, Viewport::CanvasData>::Element *E = p_viewport->canvas_map.front(); E; E = E->next()) { + + RenderingServerCanvas::Canvas *canvas = static_cast<RenderingServerCanvas::Canvas *>(E->get().canvas); + Transform2D xf = _canvas_get_transform(p_viewport, canvas, &E->get(), clip_rect.size); + + for (Set<RasterizerCanvas::LightOccluderInstance *>::Element *F = canvas->occluders.front(); F; F = F->next()) { + + if (!F->get()->enabled) + continue; + F->get()->xform_cache = xf * F->get()->xform; + if (shadow_rect.intersects_transformed(F->get()->xform_cache, F->get()->aabb_cache)) { + + F->get()->next = occluders; + occluders = F->get(); + } + } + } + //update the light shadowmaps with them + + RasterizerCanvas::Light *light = lights_with_shadow; + while (light) { + + RENDER_TIMESTAMP("Render Shadow"); + + RSG::canvas_render->light_update_shadow(light->light_internal, light->xform_cache.affine_inverse(), light->item_shadow_mask, light->radius_cache / 1000.0, light->radius_cache * 1.1, occluders); + light = light->shadows_next_ptr; + } + + //RSG::canvas_render->reset_canvas(); + RENDER_TIMESTAMP("<End rendering 2D Shadows"); + } + + if (scenario_draw_canvas_bg && canvas_map.front() && canvas_map.front()->key().get_layer() > scenario_canvas_max_layer) { + if (!can_draw_3d) { + RSG::scene->render_empty_scene(p_viewport->render_buffers, p_viewport->scenario, p_viewport->shadow_atlas); + } else { + _draw_3d(p_viewport, p_eye); + } + scenario_draw_canvas_bg = false; + } + + for (Map<Viewport::CanvasKey, Viewport::CanvasData *>::Element *E = canvas_map.front(); E; E = E->next()) { + + RenderingServerCanvas::Canvas *canvas = static_cast<RenderingServerCanvas::Canvas *>(E->get()->canvas); + + Transform2D xform = _canvas_get_transform(p_viewport, canvas, E->get(), clip_rect.size); + + RasterizerCanvas::Light *canvas_lights = NULL; + + RasterizerCanvas::Light *ptr = lights; + while (ptr) { + if (E->get()->layer >= ptr->layer_min && E->get()->layer <= ptr->layer_max) { + ptr->next_ptr = canvas_lights; + canvas_lights = ptr; + } + ptr = ptr->filter_next_ptr; + } + + RSG::canvas->render_canvas(p_viewport->render_target, canvas, xform, canvas_lights, lights_with_mask, clip_rect); + i++; + + if (scenario_draw_canvas_bg && E->key().get_layer() >= scenario_canvas_max_layer) { + if (!can_draw_3d) { + RSG::scene->render_empty_scene(p_viewport->render_buffers, p_viewport->scenario, p_viewport->shadow_atlas); + } else { + _draw_3d(p_viewport, p_eye); + } + + scenario_draw_canvas_bg = false; + } + } + + if (scenario_draw_canvas_bg) { + if (!can_draw_3d) { + RSG::scene->render_empty_scene(p_viewport->render_buffers, p_viewport->scenario, p_viewport->shadow_atlas); + } else { + _draw_3d(p_viewport, p_eye); + } + } + + //RSG::canvas_render->canvas_debug_viewport_shadows(lights_with_shadow); + } + + if (RSG::storage->render_target_is_clear_requested(p_viewport->render_target)) { + //was never cleared in the end, force clear it + RSG::storage->render_target_do_clear_request(p_viewport->render_target); + } +} + +void RenderingServerViewport::draw_viewports() { + +#if 0 + // get our arvr interface in case we need it + Ref<ARVRInterface> arvr_interface; + + if (ARVRServer::get_singleton() != NULL) { + arvr_interface = ARVRServer::get_singleton()->get_primary_interface(); + + // process all our active interfaces + ARVRServer::get_singleton()->_process(); + } +#endif + + if (Engine::get_singleton()->is_editor_hint()) { + set_default_clear_color(GLOBAL_GET("rendering/environment/default_clear_color")); + } + + //sort viewports + active_viewports.sort_custom<ViewportSort>(); + + Map<DisplayServer::WindowID, Vector<Rasterizer::BlitToScreen>> blit_to_screen_list; + //draw viewports + RENDER_TIMESTAMP(">Render Viewports"); + + //determine what is visible + draw_viewports_pass++; + + for (int i = active_viewports.size() - 1; i >= 0; i--) { //to compute parent dependency, must go in reverse draw order + + Viewport *vp = active_viewports[i]; + + if (vp->update_mode == RS::VIEWPORT_UPDATE_DISABLED) + continue; + + if (!vp->render_target.is_valid()) { + continue; + } + //ERR_CONTINUE(!vp->render_target.is_valid()); + + bool visible = vp->viewport_to_screen_rect != Rect2(); + + if (vp->update_mode == RS::VIEWPORT_UPDATE_ALWAYS || vp->update_mode == RS::VIEWPORT_UPDATE_ONCE) { + visible = true; + } + + if (vp->update_mode == RS::VIEWPORT_UPDATE_WHEN_VISIBLE && RSG::storage->render_target_was_used(vp->render_target)) { + visible = true; + } + + if (vp->update_mode == RS::VIEWPORT_UPDATE_WHEN_PARENT_VISIBLE) { + Viewport *parent = viewport_owner.getornull(vp->parent); + if (parent && parent->last_pass == draw_viewports_pass) { + visible = true; + } + } + + visible = visible && vp->size.x > 1 && vp->size.y > 1; + + if (visible) { + vp->last_pass = draw_viewports_pass; + } + } + + for (int i = 0; i < active_viewports.size(); i++) { + + Viewport *vp = active_viewports[i]; + + if (vp->last_pass != draw_viewports_pass) { + continue; //should not draw + } + + RENDER_TIMESTAMP(">Rendering Viewport " + itos(i)); + + RSG::storage->render_target_set_as_unused(vp->render_target); +#if 0 + if (vp->use_arvr && arvr_interface.is_valid()) { + // override our size, make sure it matches our required size + vp->size = arvr_interface->get_render_targetsize(); + RSG::storage->render_target_set_size(vp->render_target, vp->size.x, vp->size.y); + + // render mono or left eye first + ARVRInterface::Eyes leftOrMono = arvr_interface->is_stereo() ? ARVRInterface::EYE_LEFT : ARVRInterface::EYE_MONO; + + // check for an external texture destination for our left eye/mono + RSG::storage->render_target_set_external_texture(vp->render_target, arvr_interface->get_external_texture_for_eye(leftOrMono)); + + // set our render target as current + RSG::rasterizer->set_current_render_target(vp->render_target); + + // and draw left eye/mono + _draw_viewport(vp, leftOrMono); + arvr_interface->commit_for_eye(leftOrMono, vp->render_target, vp->viewport_to_screen_rect); + + // render right eye + if (leftOrMono == ARVRInterface::EYE_LEFT) { + // check for an external texture destination for our right eye + RSG::storage->render_target_set_external_texture(vp->render_target, arvr_interface->get_external_texture_for_eye(ARVRInterface::EYE_RIGHT)); + + // commit for eye may have changed the render target + RSG::rasterizer->set_current_render_target(vp->render_target); + + _draw_viewport(vp, ARVRInterface::EYE_RIGHT); + arvr_interface->commit_for_eye(ARVRInterface::EYE_RIGHT, vp->render_target, vp->viewport_to_screen_rect); + } + + // and for our frame timing, mark when we've finished committing our eyes + ARVRServer::get_singleton()->_mark_commit(); + } else { +#endif + { + RSG::storage->render_target_set_external_texture(vp->render_target, 0); + + RSG::scene_render->set_debug_draw_mode(vp->debug_draw); + RSG::storage->render_info_begin_capture(); + + // render standard mono camera + _draw_viewport(vp); + + RSG::storage->render_info_end_capture(); + vp->render_info[RS::VIEWPORT_RENDER_INFO_OBJECTS_IN_FRAME] = RSG::storage->get_captured_render_info(RS::INFO_OBJECTS_IN_FRAME); + vp->render_info[RS::VIEWPORT_RENDER_INFO_VERTICES_IN_FRAME] = RSG::storage->get_captured_render_info(RS::INFO_VERTICES_IN_FRAME); + vp->render_info[RS::VIEWPORT_RENDER_INFO_MATERIAL_CHANGES_IN_FRAME] = RSG::storage->get_captured_render_info(RS::INFO_MATERIAL_CHANGES_IN_FRAME); + vp->render_info[RS::VIEWPORT_RENDER_INFO_SHADER_CHANGES_IN_FRAME] = RSG::storage->get_captured_render_info(RS::INFO_SHADER_CHANGES_IN_FRAME); + vp->render_info[RS::VIEWPORT_RENDER_INFO_SURFACE_CHANGES_IN_FRAME] = RSG::storage->get_captured_render_info(RS::INFO_SURFACE_CHANGES_IN_FRAME); + vp->render_info[RS::VIEWPORT_RENDER_INFO_DRAW_CALLS_IN_FRAME] = RSG::storage->get_captured_render_info(RS::INFO_DRAW_CALLS_IN_FRAME); + + if (vp->viewport_to_screen != DisplayServer::INVALID_WINDOW_ID && (!vp->viewport_render_direct_to_screen || !RSG::rasterizer->is_low_end())) { + //copy to screen if set as such + Rasterizer::BlitToScreen blit; + blit.render_target = vp->render_target; + if (vp->viewport_to_screen_rect != Rect2()) { + blit.rect = vp->viewport_to_screen_rect; + } else { + blit.rect.position = Vector2(); + blit.rect.size = vp->size; + } + + if (!blit_to_screen_list.has(vp->viewport_to_screen)) { + blit_to_screen_list[vp->viewport_to_screen] = Vector<Rasterizer::BlitToScreen>(); + } + + blit_to_screen_list[vp->viewport_to_screen].push_back(blit); + } + } + + if (vp->update_mode == RS::VIEWPORT_UPDATE_ONCE) { + vp->update_mode = RS::VIEWPORT_UPDATE_DISABLED; + } + + RENDER_TIMESTAMP("<Rendering Viewport " + itos(i)); + } + RSG::scene_render->set_debug_draw_mode(RS::VIEWPORT_DEBUG_DRAW_DISABLED); + + RENDER_TIMESTAMP("<Render Viewports"); + //this needs to be called to make screen swapping more efficient + RSG::rasterizer->prepare_for_blitting_render_targets(); + + for (Map<int, Vector<Rasterizer::BlitToScreen>>::Element *E = blit_to_screen_list.front(); E; E = E->next()) { + RSG::rasterizer->blit_render_targets_to_screen(E->key(), E->get().ptr(), E->get().size()); + } +} + +RID RenderingServerViewport::viewport_create() { + + Viewport *viewport = memnew(Viewport); + + RID rid = viewport_owner.make_rid(viewport); + + viewport->self = rid; + viewport->hide_scenario = false; + viewport->hide_canvas = false; + viewport->render_target = RSG::storage->render_target_create(); + viewport->shadow_atlas = RSG::scene_render->shadow_atlas_create(); + viewport->viewport_render_direct_to_screen = false; + + return rid; +} + +void RenderingServerViewport::viewport_set_use_arvr(RID p_viewport, bool p_use_arvr) { + Viewport *viewport = viewport_owner.getornull(p_viewport); + ERR_FAIL_COND(!viewport); + + viewport->use_arvr = p_use_arvr; +} + +void RenderingServerViewport::viewport_set_size(RID p_viewport, int p_width, int p_height) { + + ERR_FAIL_COND(p_width < 0 && p_height < 0); + + Viewport *viewport = viewport_owner.getornull(p_viewport); + ERR_FAIL_COND(!viewport); + + viewport->size = Size2(p_width, p_height); + RSG::storage->render_target_set_size(viewport->render_target, p_width, p_height); + if (viewport->render_buffers.is_valid()) { + if (p_width == 0 || p_height == 0) { + RSG::scene_render->free(viewport->render_buffers); + viewport->render_buffers = RID(); + } else { + RSG::scene_render->render_buffers_configure(viewport->render_buffers, viewport->render_target, viewport->size.width, viewport->size.height, viewport->msaa); + } + } +} + +void RenderingServerViewport::viewport_set_active(RID p_viewport, bool p_active) { + + Viewport *viewport = viewport_owner.getornull(p_viewport); + ERR_FAIL_COND(!viewport); + + if (p_active) { + ERR_FAIL_COND(active_viewports.find(viewport) != -1); //already active + active_viewports.push_back(viewport); + } else { + active_viewports.erase(viewport); + } +} + +void RenderingServerViewport::viewport_set_parent_viewport(RID p_viewport, RID p_parent_viewport) { + + Viewport *viewport = viewport_owner.getornull(p_viewport); + ERR_FAIL_COND(!viewport); + + viewport->parent = p_parent_viewport; +} + +void RenderingServerViewport::viewport_set_clear_mode(RID p_viewport, RS::ViewportClearMode p_clear_mode) { + + Viewport *viewport = viewport_owner.getornull(p_viewport); + ERR_FAIL_COND(!viewport); + + viewport->clear_mode = p_clear_mode; +} + +void RenderingServerViewport::viewport_attach_to_screen(RID p_viewport, const Rect2 &p_rect, DisplayServer::WindowID p_screen) { + + Viewport *viewport = viewport_owner.getornull(p_viewport); + ERR_FAIL_COND(!viewport); + + if (p_screen != DisplayServer::INVALID_WINDOW_ID) { + // If using GLES2 we can optimize this operation by rendering directly to system_fbo + // instead of rendering to fbo and copying to system_fbo after + if (RSG::rasterizer->is_low_end() && viewport->viewport_render_direct_to_screen) { + + RSG::storage->render_target_set_size(viewport->render_target, p_rect.size.x, p_rect.size.y); + RSG::storage->render_target_set_position(viewport->render_target, p_rect.position.x, p_rect.position.y); + } + + viewport->viewport_to_screen_rect = p_rect; + viewport->viewport_to_screen = p_screen; + } else { + + // if render_direct_to_screen was used, reset size and position + if (RSG::rasterizer->is_low_end() && viewport->viewport_render_direct_to_screen) { + + RSG::storage->render_target_set_position(viewport->render_target, 0, 0); + RSG::storage->render_target_set_size(viewport->render_target, viewport->size.x, viewport->size.y); + } + + viewport->viewport_to_screen_rect = Rect2(); + viewport->viewport_to_screen = DisplayServer::INVALID_WINDOW_ID; + } +} + +void RenderingServerViewport::viewport_set_render_direct_to_screen(RID p_viewport, bool p_enable) { + Viewport *viewport = viewport_owner.getornull(p_viewport); + ERR_FAIL_COND(!viewport); + + if (p_enable == viewport->viewport_render_direct_to_screen) + return; + + // if disabled, reset render_target size and position + if (!p_enable) { + + RSG::storage->render_target_set_position(viewport->render_target, 0, 0); + RSG::storage->render_target_set_size(viewport->render_target, viewport->size.x, viewport->size.y); + } + + RSG::storage->render_target_set_flag(viewport->render_target, RasterizerStorage::RENDER_TARGET_DIRECT_TO_SCREEN, p_enable); + viewport->viewport_render_direct_to_screen = p_enable; + + // if attached to screen already, setup screen size and position, this needs to happen after setting flag to avoid an unnecessary buffer allocation + if (RSG::rasterizer->is_low_end() && viewport->viewport_to_screen_rect != Rect2() && p_enable) { + + RSG::storage->render_target_set_size(viewport->render_target, viewport->viewport_to_screen_rect.size.x, viewport->viewport_to_screen_rect.size.y); + RSG::storage->render_target_set_position(viewport->render_target, viewport->viewport_to_screen_rect.position.x, viewport->viewport_to_screen_rect.position.y); + } +} + +void RenderingServerViewport::viewport_set_update_mode(RID p_viewport, RS::ViewportUpdateMode p_mode) { + + Viewport *viewport = viewport_owner.getornull(p_viewport); + ERR_FAIL_COND(!viewport); + + viewport->update_mode = p_mode; +} + +RID RenderingServerViewport::viewport_get_texture(RID p_viewport) const { + + const Viewport *viewport = viewport_owner.getornull(p_viewport); + ERR_FAIL_COND_V(!viewport, RID()); + + return RSG::storage->render_target_get_texture(viewport->render_target); +} + +void RenderingServerViewport::viewport_set_hide_scenario(RID p_viewport, bool p_hide) { + + Viewport *viewport = viewport_owner.getornull(p_viewport); + ERR_FAIL_COND(!viewport); + + viewport->hide_scenario = p_hide; +} +void RenderingServerViewport::viewport_set_hide_canvas(RID p_viewport, bool p_hide) { + + Viewport *viewport = viewport_owner.getornull(p_viewport); + ERR_FAIL_COND(!viewport); + + viewport->hide_canvas = p_hide; +} +void RenderingServerViewport::viewport_set_disable_environment(RID p_viewport, bool p_disable) { + + Viewport *viewport = viewport_owner.getornull(p_viewport); + ERR_FAIL_COND(!viewport); + + viewport->disable_environment = p_disable; +} + +void RenderingServerViewport::viewport_attach_camera(RID p_viewport, RID p_camera) { + + Viewport *viewport = viewport_owner.getornull(p_viewport); + ERR_FAIL_COND(!viewport); + + viewport->camera = p_camera; +} +void RenderingServerViewport::viewport_set_scenario(RID p_viewport, RID p_scenario) { + + Viewport *viewport = viewport_owner.getornull(p_viewport); + ERR_FAIL_COND(!viewport); + + viewport->scenario = p_scenario; +} +void RenderingServerViewport::viewport_attach_canvas(RID p_viewport, RID p_canvas) { + + Viewport *viewport = viewport_owner.getornull(p_viewport); + ERR_FAIL_COND(!viewport); + + ERR_FAIL_COND(viewport->canvas_map.has(p_canvas)); + RenderingServerCanvas::Canvas *canvas = RSG::canvas->canvas_owner.getornull(p_canvas); + ERR_FAIL_COND(!canvas); + + canvas->viewports.insert(p_viewport); + viewport->canvas_map[p_canvas] = Viewport::CanvasData(); + viewport->canvas_map[p_canvas].layer = 0; + viewport->canvas_map[p_canvas].sublayer = 0; + viewport->canvas_map[p_canvas].canvas = canvas; +} + +void RenderingServerViewport::viewport_remove_canvas(RID p_viewport, RID p_canvas) { + + Viewport *viewport = viewport_owner.getornull(p_viewport); + ERR_FAIL_COND(!viewport); + + RenderingServerCanvas::Canvas *canvas = RSG::canvas->canvas_owner.getornull(p_canvas); + ERR_FAIL_COND(!canvas); + + viewport->canvas_map.erase(p_canvas); + canvas->viewports.erase(p_viewport); +} +void RenderingServerViewport::viewport_set_canvas_transform(RID p_viewport, RID p_canvas, const Transform2D &p_offset) { + + Viewport *viewport = viewport_owner.getornull(p_viewport); + ERR_FAIL_COND(!viewport); + + ERR_FAIL_COND(!viewport->canvas_map.has(p_canvas)); + viewport->canvas_map[p_canvas].transform = p_offset; +} +void RenderingServerViewport::viewport_set_transparent_background(RID p_viewport, bool p_enabled) { + + Viewport *viewport = viewport_owner.getornull(p_viewport); + ERR_FAIL_COND(!viewport); + + RSG::storage->render_target_set_flag(viewport->render_target, RasterizerStorage::RENDER_TARGET_TRANSPARENT, p_enabled); + viewport->transparent_bg = p_enabled; +} + +void RenderingServerViewport::viewport_set_global_canvas_transform(RID p_viewport, const Transform2D &p_transform) { + + Viewport *viewport = viewport_owner.getornull(p_viewport); + ERR_FAIL_COND(!viewport); + + viewport->global_transform = p_transform; +} +void RenderingServerViewport::viewport_set_canvas_stacking(RID p_viewport, RID p_canvas, int p_layer, int p_sublayer) { + + Viewport *viewport = viewport_owner.getornull(p_viewport); + ERR_FAIL_COND(!viewport); + + ERR_FAIL_COND(!viewport->canvas_map.has(p_canvas)); + viewport->canvas_map[p_canvas].layer = p_layer; + viewport->canvas_map[p_canvas].sublayer = p_sublayer; +} + +void RenderingServerViewport::viewport_set_shadow_atlas_size(RID p_viewport, int p_size) { + + Viewport *viewport = viewport_owner.getornull(p_viewport); + ERR_FAIL_COND(!viewport); + + viewport->shadow_atlas_size = p_size; + + RSG::scene_render->shadow_atlas_set_size(viewport->shadow_atlas, viewport->shadow_atlas_size); +} + +void RenderingServerViewport::viewport_set_shadow_atlas_quadrant_subdivision(RID p_viewport, int p_quadrant, int p_subdiv) { + + Viewport *viewport = viewport_owner.getornull(p_viewport); + ERR_FAIL_COND(!viewport); + + RSG::scene_render->shadow_atlas_set_quadrant_subdivision(viewport->shadow_atlas, p_quadrant, p_subdiv); +} + +void RenderingServerViewport::viewport_set_msaa(RID p_viewport, RS::ViewportMSAA p_msaa) { + + Viewport *viewport = viewport_owner.getornull(p_viewport); + ERR_FAIL_COND(!viewport); + + if (viewport->msaa == p_msaa) { + return; + } + viewport->msaa = p_msaa; + if (viewport->render_buffers.is_valid()) { + RSG::scene_render->render_buffers_configure(viewport->render_buffers, viewport->render_target, viewport->size.width, viewport->size.height, p_msaa); + } +} + +int RenderingServerViewport::viewport_get_render_info(RID p_viewport, RS::ViewportRenderInfo p_info) { + + ERR_FAIL_INDEX_V(p_info, RS::VIEWPORT_RENDER_INFO_MAX, -1); + + Viewport *viewport = viewport_owner.getornull(p_viewport); + if (!viewport) + return 0; //there should be a lock here.. + + return viewport->render_info[p_info]; +} + +void RenderingServerViewport::viewport_set_debug_draw(RID p_viewport, RS::ViewportDebugDraw p_draw) { + + Viewport *viewport = viewport_owner.getornull(p_viewport); + ERR_FAIL_COND(!viewport); + + viewport->debug_draw = p_draw; +} + +bool RenderingServerViewport::free(RID p_rid) { + + if (viewport_owner.owns(p_rid)) { + + Viewport *viewport = viewport_owner.getornull(p_rid); + + RSG::storage->free(viewport->render_target); + RSG::scene_render->free(viewport->shadow_atlas); + if (viewport->render_buffers.is_valid()) { + RSG::scene_render->free(viewport->render_buffers); + } + + while (viewport->canvas_map.front()) { + viewport_remove_canvas(p_rid, viewport->canvas_map.front()->key()); + } + + viewport_set_scenario(p_rid, RID()); + active_viewports.erase(viewport); + + viewport_owner.free(p_rid); + memdelete(viewport); + + return true; + } + + return false; +} + +void RenderingServerViewport::set_default_clear_color(const Color &p_color) { + RSG::storage->set_default_clear_color(p_color); +} + +RenderingServerViewport::RenderingServerViewport() { +} diff --git a/servers/rendering/rendering_server_viewport.h b/servers/rendering/rendering_server_viewport.h new file mode 100644 index 0000000000..4ae7a7f536 --- /dev/null +++ b/servers/rendering/rendering_server_viewport.h @@ -0,0 +1,208 @@ +/*************************************************************************/ +/* rendering_server_viewport.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef VISUALSERVERVIEWPORT_H +#define VISUALSERVERVIEWPORT_H + +#include "core/rid_owner.h" +#include "core/self_list.h" +#include "rasterizer.h" +#include "servers/arvr/arvr_interface.h" +#include "servers/rendering_server.h" + +class RenderingServerViewport { +public: + struct CanvasBase { + }; + + struct Viewport { + + RID self; + RID parent; + + bool use_arvr; /* use arvr interface to override camera positioning and projection matrices and control output */ + + Size2i size; + RID camera; + RID scenario; + + RS::ViewportUpdateMode update_mode; + RID render_target; + RID render_target_texture; + RID render_buffers; + + RS::ViewportMSAA msaa; + + DisplayServer::WindowID viewport_to_screen; + Rect2 viewport_to_screen_rect; + bool viewport_render_direct_to_screen; + + bool hide_scenario; + bool hide_canvas; + bool disable_environment; + bool disable_3d_by_usage; + bool keep_3d_linear; + + RID shadow_atlas; + int shadow_atlas_size; + + uint64_t last_pass = 0; + + int render_info[RS::VIEWPORT_RENDER_INFO_MAX]; + RS::ViewportDebugDraw debug_draw; + + RS::ViewportClearMode clear_mode; + + bool transparent_bg; + + struct CanvasKey { + + int64_t stacking; + RID canvas; + bool operator<(const CanvasKey &p_canvas) const { + if (stacking == p_canvas.stacking) + return canvas < p_canvas.canvas; + return stacking < p_canvas.stacking; + } + CanvasKey() { + stacking = 0; + } + CanvasKey(const RID &p_canvas, int p_layer, int p_sublayer) { + canvas = p_canvas; + int64_t sign = p_layer < 0 ? -1 : 1; + stacking = sign * (((int64_t)ABS(p_layer)) << 32) + p_sublayer; + } + int get_layer() const { return stacking >> 32; } + }; + + struct CanvasData { + + CanvasBase *canvas; + Transform2D transform; + int layer; + int sublayer; + }; + + Transform2D global_transform; + + Map<RID, CanvasData> canvas_map; + + Viewport() { + update_mode = RS::VIEWPORT_UPDATE_WHEN_VISIBLE; + clear_mode = RS::VIEWPORT_CLEAR_ALWAYS; + transparent_bg = false; + disable_environment = false; + viewport_to_screen = DisplayServer::INVALID_WINDOW_ID; + shadow_atlas_size = 0; + keep_3d_linear = false; + debug_draw = RS::VIEWPORT_DEBUG_DRAW_DISABLED; + msaa = RS::VIEWPORT_MSAA_DISABLED; + for (int i = 0; i < RS::VIEWPORT_RENDER_INFO_MAX; i++) { + render_info[i] = 0; + } + use_arvr = false; + } + }; + + uint64_t draw_viewports_pass = 0; + + mutable RID_PtrOwner<Viewport> viewport_owner; + + struct ViewportSort { + _FORCE_INLINE_ bool operator()(const Viewport *p_left, const Viewport *p_right) const { + + bool left_to_screen = p_left->viewport_to_screen_rect.size != Size2(); + bool right_to_screen = p_right->viewport_to_screen_rect.size != Size2(); + + if (left_to_screen == right_to_screen) { + + return p_right->parent == p_left->self; + } + return (right_to_screen ? 0 : 1) < (left_to_screen ? 0 : 1); + } + }; + + Vector<Viewport *> active_viewports; + +private: + void _draw_3d(Viewport *p_viewport, ARVRInterface::Eyes p_eye); + void _draw_viewport(Viewport *p_viewport, ARVRInterface::Eyes p_eye = ARVRInterface::EYE_MONO); + +public: + RID viewport_create(); + + void viewport_set_use_arvr(RID p_viewport, bool p_use_arvr); + + void viewport_set_size(RID p_viewport, int p_width, int p_height); + + void viewport_attach_to_screen(RID p_viewport, const Rect2 &p_rect = Rect2(), DisplayServer::WindowID p_screen = DisplayServer::MAIN_WINDOW_ID); + void viewport_set_render_direct_to_screen(RID p_viewport, bool p_enable); + + void viewport_set_active(RID p_viewport, bool p_active); + void viewport_set_parent_viewport(RID p_viewport, RID p_parent_viewport); + void viewport_set_update_mode(RID p_viewport, RS::ViewportUpdateMode p_mode); + void viewport_set_vflip(RID p_viewport, bool p_enable); + + void viewport_set_clear_mode(RID p_viewport, RS::ViewportClearMode p_clear_mode); + + RID viewport_get_texture(RID p_viewport) const; + + void viewport_set_hide_scenario(RID p_viewport, bool p_hide); + void viewport_set_hide_canvas(RID p_viewport, bool p_hide); + void viewport_set_disable_environment(RID p_viewport, bool p_disable); + + void viewport_attach_camera(RID p_viewport, RID p_camera); + void viewport_set_scenario(RID p_viewport, RID p_scenario); + void viewport_attach_canvas(RID p_viewport, RID p_canvas); + void viewport_remove_canvas(RID p_viewport, RID p_canvas); + void viewport_set_canvas_transform(RID p_viewport, RID p_canvas, const Transform2D &p_offset); + void viewport_set_transparent_background(RID p_viewport, bool p_enabled); + + void viewport_set_global_canvas_transform(RID p_viewport, const Transform2D &p_transform); + void viewport_set_canvas_stacking(RID p_viewport, RID p_canvas, int p_layer, int p_sublayer); + + void viewport_set_shadow_atlas_size(RID p_viewport, int p_size); + void viewport_set_shadow_atlas_quadrant_subdivision(RID p_viewport, int p_quadrant, int p_subdiv); + + void viewport_set_msaa(RID p_viewport, RS::ViewportMSAA p_msaa); + + virtual int viewport_get_render_info(RID p_viewport, RS::ViewportRenderInfo p_info); + virtual void viewport_set_debug_draw(RID p_viewport, RS::ViewportDebugDraw p_draw); + + void set_default_clear_color(const Color &p_color); + void draw_viewports(); + + bool free(RID p_rid); + + RenderingServerViewport(); + virtual ~RenderingServerViewport() {} +}; + +#endif // VISUALSERVERVIEWPORT_H diff --git a/servers/rendering/rendering_server_wrap_mt.cpp b/servers/rendering/rendering_server_wrap_mt.cpp new file mode 100644 index 0000000000..d1bb7b67c6 --- /dev/null +++ b/servers/rendering/rendering_server_wrap_mt.cpp @@ -0,0 +1,197 @@ +/*************************************************************************/ +/* rendering_server_wrap_mt.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "rendering_server_wrap_mt.h" +#include "core/os/os.h" +#include "core/project_settings.h" +#include "servers/display_server.h" + +void RenderingServerWrapMT::thread_exit() { + + exit = true; +} + +void RenderingServerWrapMT::thread_draw(bool p_swap_buffers, double frame_step) { + + if (!atomic_decrement(&draw_pending)) { + + rendering_server->draw(p_swap_buffers, frame_step); + } +} + +void RenderingServerWrapMT::thread_flush() { + + atomic_decrement(&draw_pending); +} + +void RenderingServerWrapMT::_thread_callback(void *_instance) { + + RenderingServerWrapMT *vsmt = reinterpret_cast<RenderingServerWrapMT *>(_instance); + + vsmt->thread_loop(); +} + +void RenderingServerWrapMT::thread_loop() { + + server_thread = Thread::get_caller_id(); + + DisplayServer::get_singleton()->make_rendering_thread(); + + rendering_server->init(); + + exit = false; + draw_thread_up = true; + while (!exit) { + // flush commands one by one, until exit is requested + command_queue.wait_and_flush_one(); + } + + command_queue.flush_all(); // flush all + + rendering_server->finish(); +} + +/* EVENT QUEUING */ + +void RenderingServerWrapMT::sync() { + + if (create_thread) { + + atomic_increment(&draw_pending); + command_queue.push_and_sync(this, &RenderingServerWrapMT::thread_flush); + } else { + + command_queue.flush_all(); //flush all pending from other threads + } +} + +void RenderingServerWrapMT::draw(bool p_swap_buffers, double frame_step) { + + if (create_thread) { + + atomic_increment(&draw_pending); + command_queue.push(this, &RenderingServerWrapMT::thread_draw, p_swap_buffers, frame_step); + } else { + + rendering_server->draw(p_swap_buffers, frame_step); + } +} + +void RenderingServerWrapMT::init() { + + if (create_thread) { + + print_verbose("RenderingServerWrapMT: Creating render thread"); + DisplayServer::get_singleton()->release_rendering_thread(); + if (create_thread) { + thread = Thread::create(_thread_callback, this); + print_verbose("RenderingServerWrapMT: Starting render thread"); + } + while (!draw_thread_up) { + OS::get_singleton()->delay_usec(1000); + } + print_verbose("RenderingServerWrapMT: Finished render thread"); + } else { + + rendering_server->init(); + } +} + +void RenderingServerWrapMT::finish() { + + if (thread) { + + command_queue.push(this, &RenderingServerWrapMT::thread_exit); + Thread::wait_to_finish(thread); + memdelete(thread); + + thread = NULL; + } else { + rendering_server->finish(); + } + + sky_free_cached_ids(); + shader_free_cached_ids(); + material_free_cached_ids(); + mesh_free_cached_ids(); + multimesh_free_cached_ids(); + immediate_free_cached_ids(); + skeleton_free_cached_ids(); + directional_light_free_cached_ids(); + omni_light_free_cached_ids(); + spot_light_free_cached_ids(); + reflection_probe_free_cached_ids(); + gi_probe_free_cached_ids(); + lightmap_capture_free_cached_ids(); + particles_free_cached_ids(); + camera_free_cached_ids(); + viewport_free_cached_ids(); + environment_free_cached_ids(); + camera_effects_free_cached_ids(); + scenario_free_cached_ids(); + instance_free_cached_ids(); + canvas_free_cached_ids(); + canvas_item_free_cached_ids(); + canvas_light_occluder_free_cached_ids(); + canvas_occluder_polygon_free_cached_ids(); +} + +void RenderingServerWrapMT::set_use_vsync_callback(bool p_enable) { + + singleton_mt->call_set_use_vsync(p_enable); +} + +RenderingServerWrapMT *RenderingServerWrapMT::singleton_mt = NULL; + +RenderingServerWrapMT::RenderingServerWrapMT(RenderingServer *p_contained, bool p_create_thread) : + command_queue(p_create_thread) { + + singleton_mt = this; + DisplayServer::switch_vsync_function = set_use_vsync_callback; //as this goes to another thread, make sure it goes properly + + rendering_server = p_contained; + create_thread = p_create_thread; + thread = NULL; + draw_pending = 0; + draw_thread_up = false; + pool_max_size = GLOBAL_GET("memory/limits/multithreaded_server/rid_pool_prealloc"); + + if (!p_create_thread) { + server_thread = Thread::get_caller_id(); + } else { + server_thread = 0; + } +} + +RenderingServerWrapMT::~RenderingServerWrapMT() { + + memdelete(rendering_server); + //finish(); +} diff --git a/servers/rendering/rendering_server_wrap_mt.h b/servers/rendering/rendering_server_wrap_mt.h new file mode 100644 index 0000000000..4d449ea913 --- /dev/null +++ b/servers/rendering/rendering_server_wrap_mt.h @@ -0,0 +1,678 @@ +/*************************************************************************/ +/* rendering_server_wrap_mt.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef RENDERING_SERVER_WRAP_MT_H +#define RENDERING_SERVER_WRAP_MT_H + +#include "core/command_queue_mt.h" +#include "core/os/thread.h" +#include "servers/rendering_server.h" + +class RenderingServerWrapMT : public RenderingServer { + + // the real visual server + mutable RenderingServer *rendering_server; + + mutable CommandQueueMT command_queue; + + static void _thread_callback(void *_instance); + void thread_loop(); + + Thread::ID server_thread; + volatile bool exit; + Thread *thread; + volatile bool draw_thread_up; + bool create_thread; + + uint64_t draw_pending; + void thread_draw(bool p_swap_buffers, double frame_step); + void thread_flush(); + + void thread_exit(); + + Mutex alloc_mutex; + + int pool_max_size; + + //#define DEBUG_SYNC + + static RenderingServerWrapMT *singleton_mt; + +#ifdef DEBUG_SYNC +#define SYNC_DEBUG print_line("sync on: " + String(__FUNCTION__)); +#else +#define SYNC_DEBUG +#endif + +public: +#define ServerName RenderingServer +#define ServerNameWrapMT RenderingServerWrapMT +#define server_name rendering_server +#include "servers/server_wrap_mt_common.h" + + //these go pass-through, as they can be called from any thread + virtual RID texture_2d_create(const Ref<Image> &p_image) { return rendering_server->texture_2d_create(p_image); } + virtual RID texture_2d_layered_create(const Vector<Ref<Image>> &p_layers, TextureLayeredType p_layered_type) { return rendering_server->texture_2d_layered_create(p_layers, p_layered_type); } + virtual RID texture_3d_create(const Vector<Ref<Image>> &p_slices) { return rendering_server->texture_3d_create(p_slices); } + virtual RID texture_proxy_create(RID p_base) { return rendering_server->texture_proxy_create(p_base); } + + //goes pass-through + virtual void texture_2d_update_immediate(RID p_texture, const Ref<Image> &p_image, int p_layer = 0) { rendering_server->texture_2d_update_immediate(p_texture, p_image, p_layer); } + //these go through command queue if they are in another thread + FUNC3(texture_2d_update, RID, const Ref<Image> &, int) + FUNC4(texture_3d_update, RID, const Ref<Image> &, int, int) + FUNC2(texture_proxy_update, RID, RID) + + //these also go pass-through + virtual RID texture_2d_placeholder_create() { return rendering_server->texture_2d_placeholder_create(); } + virtual RID texture_2d_layered_placeholder_create() { return rendering_server->texture_2d_layered_placeholder_create(); } + virtual RID texture_3d_placeholder_create() { return rendering_server->texture_3d_placeholder_create(); } + + FUNC1RC(Ref<Image>, texture_2d_get, RID) + FUNC2RC(Ref<Image>, texture_2d_layer_get, RID, int) + FUNC3RC(Ref<Image>, texture_3d_slice_get, RID, int, int) + + FUNC2(texture_replace, RID, RID) + + FUNC3(texture_set_size_override, RID, int, int) +// FIXME: Disabled during Vulkan refactoring, should be ported. +#if 0 + FUNC2(texture_bind, RID, uint32_t) +#endif + + FUNC3(texture_set_detect_3d_callback, RID, TextureDetectCallback, void *) + FUNC3(texture_set_detect_normal_callback, RID, TextureDetectCallback, void *) + FUNC3(texture_set_detect_roughness_callback, RID, TextureDetectRoughnessCallback, void *) + + FUNC2(texture_set_path, RID, const String &) + FUNC1RC(String, texture_get_path, RID) + FUNC1S(texture_debug_usage, List<TextureInfo> *) + + FUNC2(texture_set_force_redraw_if_visible, RID, bool) + + /* SHADER API */ + + FUNCRID(shader) + + FUNC2(shader_set_code, RID, const String &) + FUNC1RC(String, shader_get_code, RID) + + FUNC2SC(shader_get_param_list, RID, List<PropertyInfo> *) + + FUNC3(shader_set_default_texture_param, RID, const StringName &, RID) + FUNC2RC(RID, shader_get_default_texture_param, RID, const StringName &) + FUNC2RC(Variant, shader_get_param_default, RID, const StringName &) + + /* COMMON MATERIAL API */ + + FUNCRID(material) + + FUNC2(material_set_shader, RID, RID) + + FUNC3(material_set_param, RID, const StringName &, const Variant &) + FUNC2RC(Variant, material_get_param, RID, const StringName &) + + FUNC2(material_set_render_priority, RID, int) + FUNC2(material_set_next_pass, RID, RID) + + /* MESH API */ + + virtual RID mesh_create_from_surfaces(const Vector<SurfaceData> &p_surfaces) { + return rendering_server->mesh_create_from_surfaces(p_surfaces); + } + + FUNCRID(mesh) + + FUNC2(mesh_add_surface, RID, const SurfaceData &) + + FUNC1RC(int, mesh_get_blend_shape_count, RID) + + FUNC2(mesh_set_blend_shape_mode, RID, BlendShapeMode) + FUNC1RC(BlendShapeMode, mesh_get_blend_shape_mode, RID) + + FUNC4(mesh_surface_update_region, RID, int, int, const Vector<uint8_t> &) + + FUNC3(mesh_surface_set_material, RID, int, RID) + FUNC2RC(RID, mesh_surface_get_material, RID, int) + + FUNC2RC(SurfaceData, mesh_get_surface, RID, int) + + FUNC1RC(int, mesh_get_surface_count, RID) + + FUNC2(mesh_set_custom_aabb, RID, const AABB &) + FUNC1RC(AABB, mesh_get_custom_aabb, RID) + + FUNC1(mesh_clear, RID) + + /* MULTIMESH API */ + + FUNCRID(multimesh) + + FUNC5(multimesh_allocate, RID, int, MultimeshTransformFormat, bool, bool) + FUNC1RC(int, multimesh_get_instance_count, RID) + + FUNC2(multimesh_set_mesh, RID, RID) + FUNC3(multimesh_instance_set_transform, RID, int, const Transform &) + FUNC3(multimesh_instance_set_transform_2d, RID, int, const Transform2D &) + FUNC3(multimesh_instance_set_color, RID, int, const Color &) + FUNC3(multimesh_instance_set_custom_data, RID, int, const Color &) + + FUNC1RC(RID, multimesh_get_mesh, RID) + FUNC1RC(AABB, multimesh_get_aabb, RID) + + FUNC2RC(Transform, multimesh_instance_get_transform, RID, int) + FUNC2RC(Transform2D, multimesh_instance_get_transform_2d, RID, int) + FUNC2RC(Color, multimesh_instance_get_color, RID, int) + FUNC2RC(Color, multimesh_instance_get_custom_data, RID, int) + + FUNC2(multimesh_set_buffer, RID, const Vector<float> &) + FUNC1RC(Vector<float>, multimesh_get_buffer, RID) + + FUNC2(multimesh_set_visible_instances, RID, int) + FUNC1RC(int, multimesh_get_visible_instances, RID) + + /* IMMEDIATE API */ + + FUNCRID(immediate) + FUNC3(immediate_begin, RID, PrimitiveType, RID) + FUNC2(immediate_vertex, RID, const Vector3 &) + FUNC2(immediate_normal, RID, const Vector3 &) + FUNC2(immediate_tangent, RID, const Plane &) + FUNC2(immediate_color, RID, const Color &) + FUNC2(immediate_uv, RID, const Vector2 &) + FUNC2(immediate_uv2, RID, const Vector2 &) + FUNC1(immediate_end, RID) + FUNC1(immediate_clear, RID) + FUNC2(immediate_set_material, RID, RID) + FUNC1RC(RID, immediate_get_material, RID) + + /* SKELETON API */ + + FUNCRID(skeleton) + FUNC3(skeleton_allocate, RID, int, bool) + FUNC1RC(int, skeleton_get_bone_count, RID) + FUNC3(skeleton_bone_set_transform, RID, int, const Transform &) + FUNC2RC(Transform, skeleton_bone_get_transform, RID, int) + FUNC3(skeleton_bone_set_transform_2d, RID, int, const Transform2D &) + FUNC2RC(Transform2D, skeleton_bone_get_transform_2d, RID, int) + FUNC2(skeleton_set_base_transform_2d, RID, const Transform2D &) + + /* Light API */ + + FUNCRID(directional_light) + FUNCRID(omni_light) + FUNCRID(spot_light) + + FUNC2(light_set_color, RID, const Color &) + FUNC3(light_set_param, RID, LightParam, float) + FUNC2(light_set_shadow, RID, bool) + FUNC2(light_set_shadow_color, RID, const Color &) + FUNC2(light_set_projector, RID, RID) + FUNC2(light_set_negative, RID, bool) + FUNC2(light_set_cull_mask, RID, uint32_t) + FUNC2(light_set_reverse_cull_face_mode, RID, bool) + FUNC2(light_set_use_gi, RID, bool) + + FUNC2(light_omni_set_shadow_mode, RID, LightOmniShadowMode) + + FUNC2(light_directional_set_shadow_mode, RID, LightDirectionalShadowMode) + FUNC2(light_directional_set_blend_splits, RID, bool) + FUNC2(light_directional_set_shadow_depth_range_mode, RID, LightDirectionalShadowDepthRangeMode) + + /* PROBE API */ + + FUNCRID(reflection_probe) + + FUNC2(reflection_probe_set_update_mode, RID, ReflectionProbeUpdateMode) + FUNC2(reflection_probe_set_intensity, RID, float) + FUNC2(reflection_probe_set_interior_ambient, RID, const Color &) + FUNC2(reflection_probe_set_interior_ambient_energy, RID, float) + FUNC2(reflection_probe_set_interior_ambient_probe_contribution, RID, float) + FUNC2(reflection_probe_set_max_distance, RID, float) + FUNC2(reflection_probe_set_extents, RID, const Vector3 &) + FUNC2(reflection_probe_set_origin_offset, RID, const Vector3 &) + FUNC2(reflection_probe_set_as_interior, RID, bool) + FUNC2(reflection_probe_set_enable_box_projection, RID, bool) + FUNC2(reflection_probe_set_enable_shadows, RID, bool) + FUNC2(reflection_probe_set_cull_mask, RID, uint32_t) + FUNC2(reflection_probe_set_resolution, RID, int) + + /* BAKED LIGHT API */ + + FUNCRID(gi_probe) + + FUNC8(gi_probe_allocate, RID, const Transform &, const AABB &, const Vector3i &, const Vector<uint8_t> &, const Vector<uint8_t> &, const Vector<uint8_t> &, const Vector<int> &) + + FUNC1RC(AABB, gi_probe_get_bounds, RID) + FUNC1RC(Vector3i, gi_probe_get_octree_size, RID) + FUNC1RC(Vector<uint8_t>, gi_probe_get_octree_cells, RID) + FUNC1RC(Vector<uint8_t>, gi_probe_get_data_cells, RID) + FUNC1RC(Vector<uint8_t>, gi_probe_get_distance_field, RID) + FUNC1RC(Vector<int>, gi_probe_get_level_counts, RID) + FUNC1RC(Transform, gi_probe_get_to_cell_xform, RID) + + FUNC2(gi_probe_set_dynamic_range, RID, float) + FUNC1RC(float, gi_probe_get_dynamic_range, RID) + + FUNC2(gi_probe_set_propagation, RID, float) + FUNC1RC(float, gi_probe_get_propagation, RID) + + FUNC2(gi_probe_set_energy, RID, float) + FUNC1RC(float, gi_probe_get_energy, RID) + + FUNC2(gi_probe_set_ao, RID, float) + FUNC1RC(float, gi_probe_get_ao, RID) + + FUNC2(gi_probe_set_ao_size, RID, float) + FUNC1RC(float, gi_probe_get_ao_size, RID) + + FUNC2(gi_probe_set_bias, RID, float) + FUNC1RC(float, gi_probe_get_bias, RID) + + FUNC2(gi_probe_set_normal_bias, RID, float) + FUNC1RC(float, gi_probe_get_normal_bias, RID) + + FUNC2(gi_probe_set_interior, RID, bool) + FUNC1RC(bool, gi_probe_is_interior, RID) + + FUNC2(gi_probe_set_use_two_bounces, RID, bool) + FUNC1RC(bool, gi_probe_is_using_two_bounces, RID) + + FUNC2(gi_probe_set_anisotropy_strength, RID, float) + FUNC1RC(float, gi_probe_get_anisotropy_strength, 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 Vector<uint8_t> &) + FUNC1RC(Vector<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) + + FUNC2(particles_set_emitting, RID, bool) + FUNC1R(bool, particles_get_emitting, RID) + FUNC2(particles_set_amount, RID, int) + FUNC2(particles_set_lifetime, RID, float) + FUNC2(particles_set_one_shot, RID, bool) + FUNC2(particles_set_pre_process_time, RID, float) + FUNC2(particles_set_explosiveness_ratio, RID, float) + FUNC2(particles_set_randomness_ratio, RID, float) + FUNC2(particles_set_custom_aabb, RID, const AABB &) + FUNC2(particles_set_speed_scale, RID, float) + FUNC2(particles_set_use_local_coordinates, RID, bool) + FUNC2(particles_set_process_material, RID, RID) + FUNC2(particles_set_fixed_fps, RID, int) + FUNC2(particles_set_fractional_delta, RID, bool) + FUNC1R(bool, particles_is_inactive, RID) + FUNC1(particles_request_process, RID) + FUNC1(particles_restart, RID) + + FUNC2(particles_set_draw_order, RID, RS::ParticlesDrawOrder) + + FUNC2(particles_set_draw_passes, RID, int) + FUNC3(particles_set_draw_pass_mesh, RID, int, RID) + FUNC2(particles_set_emission_transform, RID, const Transform &) + + FUNC1R(AABB, particles_get_current_aabb, RID) + + /* CAMERA API */ + + FUNCRID(camera) + FUNC4(camera_set_perspective, RID, float, float, float) + FUNC4(camera_set_orthogonal, RID, float, float, float) + FUNC5(camera_set_frustum, RID, float, Vector2, float, float) + FUNC2(camera_set_transform, RID, const Transform &) + FUNC2(camera_set_cull_mask, RID, uint32_t) + FUNC2(camera_set_environment, RID, RID) + FUNC2(camera_set_camera_effects, RID, RID) + FUNC2(camera_set_use_vertical_aspect, RID, bool) + + /* VIEWPORT TARGET API */ + + FUNCRID(viewport) + + FUNC2(viewport_set_use_arvr, RID, bool) + + FUNC3(viewport_set_size, RID, int, int) + + FUNC2(viewport_set_active, RID, bool) + FUNC2(viewport_set_parent_viewport, RID, RID) + + FUNC2(viewport_set_clear_mode, RID, ViewportClearMode) + + FUNC3(viewport_attach_to_screen, RID, const Rect2 &, DisplayServer::WindowID) + FUNC2(viewport_set_render_direct_to_screen, RID, bool) + + FUNC2(viewport_set_update_mode, RID, ViewportUpdateMode) + + FUNC1RC(RID, viewport_get_texture, RID) + + FUNC2(viewport_set_hide_scenario, RID, bool) + FUNC2(viewport_set_hide_canvas, RID, bool) + FUNC2(viewport_set_disable_environment, RID, bool) + + FUNC2(viewport_attach_camera, RID, RID) + FUNC2(viewport_set_scenario, RID, RID) + FUNC2(viewport_attach_canvas, RID, RID) + + FUNC2(viewport_remove_canvas, RID, RID) + FUNC3(viewport_set_canvas_transform, RID, RID, const Transform2D &) + FUNC2(viewport_set_transparent_background, RID, bool) + + FUNC2(viewport_set_global_canvas_transform, RID, const Transform2D &) + FUNC4(viewport_set_canvas_stacking, RID, RID, int, int) + FUNC2(viewport_set_shadow_atlas_size, RID, int) + FUNC3(viewport_set_shadow_atlas_quadrant_subdivision, RID, int, int) + FUNC2(viewport_set_msaa, RID, ViewportMSAA) + + //this passes directly to avoid stalling, but it's pretty dangerous, so don't call after freeing a viewport + virtual int viewport_get_render_info(RID p_viewport, ViewportRenderInfo p_info) { + return rendering_server->viewport_get_render_info(p_viewport, p_info); + } + + FUNC2(viewport_set_debug_draw, RID, ViewportDebugDraw) + + FUNC1(directional_shadow_atlas_set_size, int) + + /* SKY API */ + + FUNCRID(sky) + FUNC2(sky_set_radiance_size, RID, int) + FUNC2(sky_set_mode, RID, SkyMode) + FUNC2(sky_set_material, RID, RID) + + /* ENVIRONMENT API */ + + FUNCRID(environment) + + FUNC2(environment_set_background, RID, EnvironmentBG) + FUNC2(environment_set_sky, RID, RID) + FUNC2(environment_set_sky_custom_fov, RID, float) + FUNC2(environment_set_sky_orientation, RID, const Basis &) + FUNC2(environment_set_bg_color, RID, const Color &) + FUNC2(environment_set_bg_energy, RID, float) + FUNC2(environment_set_canvas_max_layer, RID, int) + FUNC7(environment_set_ambient_light, RID, const Color &, EnvironmentAmbientSource, float, float, EnvironmentReflectionSource, const Color &) + +// FIXME: Disabled during Vulkan refactoring, should be ported. +#if 0 + FUNC2(environment_set_camera_feed_id, RID, int) +#endif + FUNC7(environment_set_ssr, RID, bool, int, float, float, float, bool) + FUNC9(environment_set_ssao, RID, bool, float, float, float, float, float, EnvironmentSSAOBlur, float) + + FUNC2(environment_set_ssao_quality, EnvironmentSSAOQuality, bool) + + FUNC12(environment_set_glow, RID, bool, int, float, float, float, float, EnvironmentGlowBlendMode, float, float, float, bool) + + FUNC9(environment_set_tonemap, RID, EnvironmentToneMapper, float, float, bool, float, float, float, float) + + FUNC6(environment_set_adjustment, RID, bool, float, float, float, RID) + + FUNC5(environment_set_fog, RID, bool, const Color &, const Color &, float) + FUNC7(environment_set_fog_depth, RID, bool, float, float, float, bool, float) + FUNC5(environment_set_fog_height, RID, bool, float, float, float) + + FUNC2(screen_space_roughness_limiter_set_active, bool, float) + + FUNCRID(camera_effects) + + FUNC2(camera_effects_set_dof_blur_quality, DOFBlurQuality, bool) + FUNC1(camera_effects_set_dof_blur_bokeh_shape, DOFBokehShape) + + FUNC8(camera_effects_set_dof_blur, RID, bool, float, float, bool, float, float, float) + FUNC3(camera_effects_set_custom_exposure, RID, bool, float) + + FUNCRID(scenario) + + FUNC2(scenario_set_debug, RID, ScenarioDebugMode) + FUNC2(scenario_set_environment, RID, RID) + FUNC2(scenario_set_camera_effects, RID, RID) + FUNC2(scenario_set_fallback_environment, RID, RID) + + /* INSTANCING API */ + FUNCRID(instance) + + FUNC2(instance_set_base, RID, RID) + FUNC2(instance_set_scenario, RID, RID) + FUNC2(instance_set_layer_mask, RID, uint32_t) + FUNC2(instance_set_transform, RID, const Transform &) + FUNC2(instance_attach_object_instance_id, RID, ObjectID) + 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) + FUNC2(instance_set_exterior, RID, bool) + + FUNC2(instance_set_extra_visibility_margin, RID, real_t) + + // don't use these in a game! + FUNC2RC(Vector<ObjectID>, instances_cull_aabb, const AABB &, RID) + FUNC3RC(Vector<ObjectID>, instances_cull_ray, const Vector3 &, const Vector3 &, RID) + FUNC2RC(Vector<ObjectID>, instances_cull_convex, const Vector<Plane> &, RID) + + FUNC3(instance_geometry_set_flag, RID, InstanceFlags, bool) + FUNC2(instance_geometry_set_cast_shadows_setting, RID, ShadowCastingSetting) + FUNC2(instance_geometry_set_material_override, RID, RID) + + FUNC5(instance_geometry_set_draw_range, RID, float, float, float, float) + FUNC2(instance_geometry_set_as_instance_lod, RID, RID) + + /* CANVAS (2D) */ + + FUNCRID(canvas) + FUNC3(canvas_set_item_mirroring, RID, RID, const Point2 &) + FUNC2(canvas_set_modulate, RID, const Color &) + FUNC3(canvas_set_parent, RID, RID, float) + FUNC1(canvas_set_disable_scale, bool) + + FUNCRID(canvas_item) + FUNC2(canvas_item_set_parent, RID, RID) + + FUNC2(canvas_item_set_visible, RID, bool) + FUNC2(canvas_item_set_light_mask, RID, int) + + FUNC2(canvas_item_set_update_when_visible, RID, bool) + + FUNC2(canvas_item_set_transform, RID, const Transform2D &) + FUNC2(canvas_item_set_clip, RID, bool) + FUNC2(canvas_item_set_distance_field_mode, RID, bool) + FUNC3(canvas_item_set_custom_rect, RID, bool, const Rect2 &) + FUNC2(canvas_item_set_modulate, RID, const Color &) + FUNC2(canvas_item_set_self_modulate, RID, const Color &) + + FUNC2(canvas_item_set_draw_behind_parent, RID, bool) + + FUNC2(canvas_item_set_default_texture_filter, RID, CanvasItemTextureFilter) + FUNC2(canvas_item_set_default_texture_repeat, RID, CanvasItemTextureRepeat) + + FUNC5(canvas_item_add_line, RID, const Point2 &, const Point2 &, const Color &, float) + FUNC4(canvas_item_add_polyline, RID, const Vector<Point2> &, const Vector<Color> &, float) + FUNC4(canvas_item_add_multiline, RID, const Vector<Point2> &, const Vector<Color> &, float) + FUNC3(canvas_item_add_rect, RID, const Rect2 &, const Color &) + FUNC4(canvas_item_add_circle, RID, const Point2 &, float, const Color &) + FUNC11(canvas_item_add_texture_rect, RID, const Rect2 &, RID, bool, const Color &, bool, RID, RID, const Color &, CanvasItemTextureFilter, CanvasItemTextureRepeat) + FUNC12(canvas_item_add_texture_rect_region, RID, const Rect2 &, RID, const Rect2 &, const Color &, bool, RID, RID, const Color &, bool, CanvasItemTextureFilter, CanvasItemTextureRepeat) + FUNC15(canvas_item_add_nine_patch, RID, const Rect2 &, const Rect2 &, RID, const Vector2 &, const Vector2 &, NinePatchAxisMode, NinePatchAxisMode, bool, const Color &, RID, RID, const Color &, CanvasItemTextureFilter, CanvasItemTextureRepeat) + FUNC11(canvas_item_add_primitive, RID, const Vector<Point2> &, const Vector<Color> &, const Vector<Point2> &, RID, float, RID, RID, const Color &, CanvasItemTextureFilter, CanvasItemTextureRepeat) + FUNC10(canvas_item_add_polygon, RID, const Vector<Point2> &, const Vector<Color> &, const Vector<Point2> &, RID, RID, RID, const Color &, CanvasItemTextureFilter, CanvasItemTextureRepeat) + FUNC14(canvas_item_add_triangle_array, RID, const Vector<int> &, const Vector<Point2> &, const Vector<Color> &, const Vector<Point2> &, const Vector<int> &, const Vector<float> &, RID, int, RID, RID, const Color &, CanvasItemTextureFilter, CanvasItemTextureRepeat) + FUNC10(canvas_item_add_mesh, RID, const RID &, const Transform2D &, const Color &, RID, RID, RID, const Color &, CanvasItemTextureFilter, CanvasItemTextureRepeat) + FUNC8(canvas_item_add_multimesh, RID, RID, RID, RID, RID, const Color &, CanvasItemTextureFilter, CanvasItemTextureRepeat) + FUNC8(canvas_item_add_particles, RID, RID, RID, RID, RID, const Color &, CanvasItemTextureFilter, CanvasItemTextureRepeat) + FUNC2(canvas_item_add_set_transform, RID, const Transform2D &) + FUNC2(canvas_item_add_clip_ignore, RID, bool) + FUNC2(canvas_item_set_sort_children_by_y, RID, bool) + FUNC2(canvas_item_set_z_index, RID, int) + FUNC2(canvas_item_set_z_as_relative_to_parent, RID, bool) + FUNC3(canvas_item_set_copy_to_backbuffer, RID, bool, const Rect2 &) + FUNC2(canvas_item_attach_skeleton, RID, RID) + + FUNC1(canvas_item_clear, RID) + FUNC2(canvas_item_set_draw_index, RID, int) + + FUNC2(canvas_item_set_material, RID, RID) + + FUNC2(canvas_item_set_use_parent_material, RID, bool) + + FUNC0R(RID, canvas_light_create) + FUNC2(canvas_light_attach_to_canvas, RID, RID) + FUNC2(canvas_light_set_enabled, RID, bool) + FUNC2(canvas_light_set_scale, RID, float) + FUNC2(canvas_light_set_transform, RID, const Transform2D &) + FUNC2(canvas_light_set_texture, RID, RID) + FUNC2(canvas_light_set_texture_offset, RID, const Vector2 &) + FUNC2(canvas_light_set_color, RID, const Color &) + FUNC2(canvas_light_set_height, RID, float) + FUNC2(canvas_light_set_energy, RID, float) + FUNC3(canvas_light_set_z_range, RID, int, int) + FUNC3(canvas_light_set_layer_range, RID, int, int) + FUNC2(canvas_light_set_item_cull_mask, RID, int) + FUNC2(canvas_light_set_item_shadow_cull_mask, RID, int) + + FUNC2(canvas_light_set_mode, RID, CanvasLightMode) + + FUNC2(canvas_light_set_shadow_enabled, RID, bool) + FUNC2(canvas_light_set_shadow_buffer_size, RID, int) + FUNC2(canvas_light_set_shadow_filter, RID, CanvasLightShadowFilter) + FUNC2(canvas_light_set_shadow_color, RID, const Color &) + FUNC2(canvas_light_set_shadow_smooth, RID, float) + + FUNCRID(canvas_light_occluder) + FUNC2(canvas_light_occluder_attach_to_canvas, RID, RID) + FUNC2(canvas_light_occluder_set_enabled, RID, bool) + FUNC2(canvas_light_occluder_set_polygon, RID, RID) + FUNC2(canvas_light_occluder_set_transform, RID, const Transform2D &) + FUNC2(canvas_light_occluder_set_light_mask, RID, int) + + FUNCRID(canvas_occluder_polygon) + FUNC3(canvas_occluder_polygon_set_shape, RID, const Vector<Vector2> &, bool) + FUNC2(canvas_occluder_polygon_set_shape_as_lines, RID, const Vector<Vector2> &) + + FUNC2(canvas_occluder_polygon_set_cull_mode, RID, CanvasOccluderPolygonCullMode) + + /* BLACK BARS */ + + FUNC4(black_bars_set_margins, int, int, int, int) + FUNC4(black_bars_set_images, RID, RID, RID, RID) + + /* FREE */ + + FUNC1(free, RID) + + /* EVENT QUEUING */ + + FUNC3(request_frame_drawn_callback, Object *, const StringName &, const Variant &) + + virtual void init(); + virtual void finish(); + virtual void draw(bool p_swap_buffers, double frame_step); + virtual void sync(); + FUNC0RC(bool, has_changed) + + /* RENDER INFO */ + + //this passes directly to avoid stalling + virtual int get_render_info(RenderInfo p_info) { + return rendering_server->get_render_info(p_info); + } + + virtual String get_video_adapter_name() const { + return rendering_server->get_video_adapter_name(); + } + + virtual String get_video_adapter_vendor() const { + return rendering_server->get_video_adapter_vendor(); + } + + FUNC4(set_boot_image, const Ref<Image> &, const Color &, bool, bool) + FUNC1(set_default_clear_color, const Color &) + + FUNC0R(RID, get_test_cube) + + FUNC1(set_debug_generate_wireframes, bool) + + virtual bool has_feature(Features p_feature) const { + return rendering_server->has_feature(p_feature); + } + virtual bool has_os_feature(const String &p_feature) const { + return rendering_server->has_os_feature(p_feature); + } + + FUNC1(call_set_use_vsync, bool) + + static void set_use_vsync_callback(bool p_enable); + + virtual bool is_low_end() const { + return rendering_server->is_low_end(); + } + + virtual uint64_t get_frame_profile_frame() { + return rendering_server->get_frame_profile_frame(); + } + + virtual void set_frame_profiling_enabled(bool p_enabled) { + rendering_server->set_frame_profiling_enabled(p_enabled); + } + + virtual Vector<FrameProfileArea> get_frame_profile() { + return rendering_server->get_frame_profile(); + } + + RenderingServerWrapMT(RenderingServer *p_contained, bool p_create_thread); + ~RenderingServerWrapMT(); + +#undef ServerName +#undef ServerNameWrapMT +#undef server_name +}; + +#ifdef DEBUG_SYNC +#undef DEBUG_SYNC +#endif +#undef SYNC_DEBUG + +#endif diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp new file mode 100644 index 0000000000..cf2a01c07c --- /dev/null +++ b/servers/rendering/shader_language.cpp @@ -0,0 +1,7006 @@ +/*************************************************************************/ +/* shader_language.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "shader_language.h" +#include "core/os/os.h" +#include "core/print_string.h" +#include "servers/rendering_server.h" + +static bool _is_text_char(CharType c) { + + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_'; +} + +static bool _is_number(CharType c) { + + return (c >= '0' && c <= '9'); +} + +static bool _is_hex(CharType c) { + + return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); +} + +String ShaderLanguage::get_operator_text(Operator p_op) { + + static const char *op_names[OP_MAX] = { "==", + "!=", + "<", + "<=", + ">", + ">=", + "&&", + "||", + "!", + "-", + "+", + "-", + "*", + "/", + "%", + "<<", + ">>", + "=", + "+=", + "-=", + "*=", + "/=", + "%=", + "<<=", + ">>=", + "&=", + "|=", + "^=", + "&", + "|", + "^", + "~", + "++", + "--", + "?", + ":", + "++", + "--", + "()", + "construct", + "index" }; + + return op_names[p_op]; +} + +const char *ShaderLanguage::token_names[TK_MAX] = { + "EMPTY", + "IDENTIFIER", + "TRUE", + "FALSE", + "REAL_CONSTANT", + "INT_CONSTANT", + "TYPE_VOID", + "TYPE_BOOL", + "TYPE_BVEC2", + "TYPE_BVEC3", + "TYPE_BVEC4", + "TYPE_INT", + "TYPE_IVEC2", + "TYPE_IVEC3", + "TYPE_IVEC4", + "TYPE_UINT", + "TYPE_UVEC2", + "TYPE_UVEC3", + "TYPE_UVEC4", + "TYPE_FLOAT", + "TYPE_VEC2", + "TYPE_VEC3", + "TYPE_VEC4", + "TYPE_MAT2", + "TYPE_MAT3", + "TYPE_MAT4", + "TYPE_SAMPLER2D", + "TYPE_ISAMPLER2D", + "TYPE_USAMPLER2D", + "TYPE_SAMPLER2DARRAY", + "TYPE_ISAMPLER2DARRAY", + "TYPE_USAMPLER2DARRAY", + "TYPE_SAMPLER3D", + "TYPE_ISAMPLER3D", + "TYPE_USAMPLER3D", + "TYPE_SAMPLERCUBE", + "INTERPOLATION_FLAT", + "INTERPOLATION_SMOOTH", + "CONST", + "PRECISION_LOW", + "PRECISION_MID", + "PRECISION_HIGH", + "OP_EQUAL", + "OP_NOT_EQUAL", + "OP_LESS", + "OP_LESS_EQUAL", + "OP_GREATER", + "OP_GREATER_EQUAL", + "OP_AND", + "OP_OR", + "OP_NOT", + "OP_ADD", + "OP_SUB", + "OP_MUL", + "OP_DIV", + "OP_MOD", + "OP_SHIFT_LEFT", + "OP_SHIFT_RIGHT", + "OP_ASSIGN", + "OP_ASSIGN_ADD", + "OP_ASSIGN_SUB", + "OP_ASSIGN_MUL", + "OP_ASSIGN_DIV", + "OP_ASSIGN_MOD", + "OP_ASSIGN_SHIFT_LEFT", + "OP_ASSIGN_SHIFT_RIGHT", + "OP_ASSIGN_BIT_AND", + "OP_ASSIGN_BIT_OR", + "OP_ASSIGN_BIT_XOR", + "OP_BIT_AND", + "OP_BIT_OR", + "OP_BIT_XOR", + "OP_BIT_INVERT", + "OP_INCREMENT", + "OP_DECREMENT", + "CF_IF", + "CF_ELSE", + "CF_FOR", + "CF_WHILE", + "CF_DO", + "CF_SWITCH", + "CF_CASE", + "CF_BREAK", + "CF_CONTINUE", + "CF_RETURN", + "CF_DISCARD", + "BRACKET_OPEN", + "BRACKET_CLOSE", + "CURLY_BRACKET_OPEN", + "CURLY_BRACKET_CLOSE", + "PARENTHESIS_OPEN", + "PARENTHESIS_CLOSE", + "QUESTION", + "COMMA", + "COLON", + "SEMICOLON", + "PERIOD", + "UNIFORM", + "VARYING", + "IN", + "OUT", + "INOUT", + "RENDER_MODE", + "HINT_WHITE_TEXTURE", + "HINT_BLACK_TEXTURE", + "HINT_NORMAL_TEXTURE", + "HINT_ANISO_TEXTURE", + "HINT_ALBEDO_TEXTURE", + "HINT_BLACK_ALBEDO_TEXTURE", + "HINT_COLOR", + "HINT_RANGE", + "FILTER_NEAREST", + "FILTER_LINEAR", + "FILTER_NEAREST_MIPMAP", + "FILTER_LINEAR_MIPMAP", + "FILTER_NEAREST_MIPMAP_ANISO", + "FILTER_LINEAR_MIPMAP_ANISO", + "REPEAT_ENABLE", + "REPEAT_DISABLE", + "SHADER_TYPE", + "CURSOR", + "ERROR", + "EOF", +}; + +String ShaderLanguage::get_token_text(Token p_token) { + + String name = token_names[p_token.type]; + if (p_token.type == TK_INT_CONSTANT || p_token.type == TK_REAL_CONSTANT) { + name += "(" + rtos(p_token.constant) + ")"; + } else if (p_token.type == TK_IDENTIFIER) { + name += "(" + String(p_token.text) + ")"; + } else if (p_token.type == TK_ERROR) { + name += "(" + String(p_token.text) + ")"; + } + + return name; +} + +ShaderLanguage::Token ShaderLanguage::_make_token(TokenType p_type, const StringName &p_text) { + + Token tk; + tk.type = p_type; + tk.text = p_text; + tk.line = tk_line; + if (tk.type == TK_ERROR) { + _set_error(p_text); + } + return tk; +} + +const ShaderLanguage::KeyWord ShaderLanguage::keyword_list[] = { + { TK_TRUE, "true" }, + { TK_FALSE, "false" }, + { TK_TYPE_VOID, "void" }, + { TK_TYPE_BOOL, "bool" }, + { TK_TYPE_BVEC2, "bvec2" }, + { TK_TYPE_BVEC3, "bvec3" }, + { TK_TYPE_BVEC4, "bvec4" }, + { TK_TYPE_INT, "int" }, + { TK_TYPE_IVEC2, "ivec2" }, + { TK_TYPE_IVEC3, "ivec3" }, + { TK_TYPE_IVEC4, "ivec4" }, + { TK_TYPE_UINT, "uint" }, + { TK_TYPE_UVEC2, "uvec2" }, + { TK_TYPE_UVEC3, "uvec3" }, + { TK_TYPE_UVEC4, "uvec4" }, + { TK_TYPE_FLOAT, "float" }, + { TK_TYPE_VEC2, "vec2" }, + { TK_TYPE_VEC3, "vec3" }, + { TK_TYPE_VEC4, "vec4" }, + { TK_TYPE_MAT2, "mat2" }, + { TK_TYPE_MAT3, "mat3" }, + { TK_TYPE_MAT4, "mat4" }, + { TK_TYPE_SAMPLER2D, "sampler2D" }, + { TK_TYPE_ISAMPLER2D, "isampler2D" }, + { TK_TYPE_USAMPLER2D, "usampler2D" }, + { TK_TYPE_SAMPLER2DARRAY, "sampler2DArray" }, + { TK_TYPE_ISAMPLER2DARRAY, "isampler2DArray" }, + { TK_TYPE_USAMPLER2DARRAY, "usampler2DArray" }, + { TK_TYPE_SAMPLER3D, "sampler3D" }, + { TK_TYPE_ISAMPLER3D, "isampler3D" }, + { TK_TYPE_USAMPLER3D, "usampler3D" }, + { TK_TYPE_SAMPLERCUBE, "samplerCube" }, + { TK_INTERPOLATION_FLAT, "flat" }, + { TK_INTERPOLATION_SMOOTH, "smooth" }, + { TK_CONST, "const" }, + { TK_STRUCT, "struct" }, + { TK_PRECISION_LOW, "lowp" }, + { TK_PRECISION_MID, "mediump" }, + { TK_PRECISION_HIGH, "highp" }, + { TK_CF_IF, "if" }, + { TK_CF_ELSE, "else" }, + { TK_CF_FOR, "for" }, + { TK_CF_WHILE, "while" }, + { TK_CF_DO, "do" }, + { TK_CF_SWITCH, "switch" }, + { TK_CF_CASE, "case" }, + { TK_CF_DEFAULT, "default" }, + { TK_CF_BREAK, "break" }, + { TK_CF_CONTINUE, "continue" }, + { TK_CF_RETURN, "return" }, + { TK_CF_DISCARD, "discard" }, + { TK_UNIFORM, "uniform" }, + { TK_VARYING, "varying" }, + { TK_ARG_IN, "in" }, + { TK_ARG_OUT, "out" }, + { TK_ARG_INOUT, "inout" }, + { TK_RENDER_MODE, "render_mode" }, + { TK_HINT_WHITE_TEXTURE, "hint_white" }, + { TK_HINT_BLACK_TEXTURE, "hint_black" }, + { TK_HINT_NORMAL_TEXTURE, "hint_normal" }, + { TK_HINT_ROUGHNESS_NORMAL_TEXTURE, "hint_roughness_normal" }, + { TK_HINT_ROUGHNESS_R, "hint_roughness_r" }, + { TK_HINT_ROUGHNESS_G, "hint_roughness_g" }, + { TK_HINT_ROUGHNESS_B, "hint_roughness_b" }, + { TK_HINT_ROUGHNESS_A, "hint_roughness_a" }, + { TK_HINT_ROUGHNESS_GRAY, "hint_roughness_gray" }, + { TK_HINT_ANISO_TEXTURE, "hint_aniso" }, + { TK_HINT_ALBEDO_TEXTURE, "hint_albedo" }, + { TK_HINT_BLACK_ALBEDO_TEXTURE, "hint_black_albedo" }, + { TK_HINT_COLOR, "hint_color" }, + { TK_HINT_RANGE, "hint_range" }, + { TK_FILTER_NEAREST, "filter_nearest" }, + { TK_FILTER_LINEAR, "filter_linear" }, + { TK_FILTER_NEAREST_MIPMAP, "filter_nearest_mipmap" }, + { TK_FILTER_LINEAR_MIPMAP, "filter_linear_mipmap" }, + { TK_FILTER_NEAREST_MIPMAP_ANISO, "filter_nearest_mipmap_aniso" }, + { TK_FILTER_LINEAR_MIPMAP_ANISO, "filter_linear_mipmap_aniso" }, + { TK_REPEAT_ENABLE, "repeat_enable" }, + { TK_REPEAT_DISABLE, "repeat_disable" }, + { TK_SHADER_TYPE, "shader_type" }, + { TK_ERROR, NULL } +}; + +ShaderLanguage::Token ShaderLanguage::_get_token() { + +#define GETCHAR(m_idx) (((char_idx + m_idx) < code.length()) ? code[char_idx + m_idx] : CharType(0)) + + while (true) { + char_idx++; + switch (GETCHAR(-1)) { + + case 0: + return _make_token(TK_EOF); + case 0xFFFF: + return _make_token(TK_CURSOR); //for completion + case '\t': + case '\r': + case ' ': + continue; + case '\n': + tk_line++; + continue; + case '/': { + + switch (GETCHAR(0)) { + case '*': { // block comment + + char_idx++; + while (true) { + if (GETCHAR(0) == 0) { + return _make_token(TK_EOF); + } + if (GETCHAR(0) == '*' && GETCHAR(1) == '/') { + char_idx += 2; + break; + } else if (GETCHAR(0) == '\n') { + tk_line++; + } + + char_idx++; + } + + } break; + case '/': { // line comment skip + + while (true) { + if (GETCHAR(0) == '\n') { + tk_line++; + char_idx++; + break; + } + if (GETCHAR(0) == 0) { + return _make_token(TK_EOF); + } + char_idx++; + } + + } break; + case '=': { // diveq + + char_idx++; + return _make_token(TK_OP_ASSIGN_DIV); + + } break; + default: + return _make_token(TK_OP_DIV); + } + + continue; //a comment, continue to next token + } break; + case '=': { + + if (GETCHAR(0) == '=') { + char_idx++; + return _make_token(TK_OP_EQUAL); + } + + return _make_token(TK_OP_ASSIGN); + + } break; + case '<': { + if (GETCHAR(0) == '=') { + char_idx++; + return _make_token(TK_OP_LESS_EQUAL); + } else if (GETCHAR(0) == '<') { + char_idx++; + if (GETCHAR(0) == '=') { + char_idx++; + return _make_token(TK_OP_ASSIGN_SHIFT_LEFT); + } + + return _make_token(TK_OP_SHIFT_LEFT); + } + + return _make_token(TK_OP_LESS); + + } break; + case '>': { + if (GETCHAR(0) == '=') { + char_idx++; + return _make_token(TK_OP_GREATER_EQUAL); + } else if (GETCHAR(0) == '>') { + char_idx++; + if (GETCHAR(0) == '=') { + char_idx++; + return _make_token(TK_OP_ASSIGN_SHIFT_RIGHT); + } + + return _make_token(TK_OP_SHIFT_RIGHT); + } + + return _make_token(TK_OP_GREATER); + + } break; + case '!': { + if (GETCHAR(0) == '=') { + char_idx++; + return _make_token(TK_OP_NOT_EQUAL); + } + + return _make_token(TK_OP_NOT); + + } break; + //case '"' //string - no strings in shader + //case '\'' //string - no strings in shader + case '{': + return _make_token(TK_CURLY_BRACKET_OPEN); + case '}': + return _make_token(TK_CURLY_BRACKET_CLOSE); + case '[': + return _make_token(TK_BRACKET_OPEN); + case ']': + return _make_token(TK_BRACKET_CLOSE); + case '(': + return _make_token(TK_PARENTHESIS_OPEN); + case ')': + return _make_token(TK_PARENTHESIS_CLOSE); + case ',': + return _make_token(TK_COMMA); + case ';': + return _make_token(TK_SEMICOLON); + case '?': + return _make_token(TK_QUESTION); + case ':': + return _make_token(TK_COLON); + case '^': + return _make_token(TK_OP_BIT_XOR); + case '~': + return _make_token(TK_OP_BIT_INVERT); + case '&': { + if (GETCHAR(0) == '=') { + char_idx++; + return _make_token(TK_OP_ASSIGN_BIT_AND); + } else if (GETCHAR(0) == '&') { + char_idx++; + return _make_token(TK_OP_AND); + } + return _make_token(TK_OP_BIT_AND); + } break; + case '|': { + + if (GETCHAR(0) == '=') { + char_idx++; + return _make_token(TK_OP_ASSIGN_BIT_OR); + } else if (GETCHAR(0) == '|') { + char_idx++; + return _make_token(TK_OP_OR); + } + return _make_token(TK_OP_BIT_OR); + + } break; + case '*': { + + if (GETCHAR(0) == '=') { + char_idx++; + return _make_token(TK_OP_ASSIGN_MUL); + } + return _make_token(TK_OP_MUL); + } break; + case '+': { + + if (GETCHAR(0) == '=') { + char_idx++; + return _make_token(TK_OP_ASSIGN_ADD); + } else if (GETCHAR(0) == '+') { + + char_idx++; + return _make_token(TK_OP_INCREMENT); + } + + return _make_token(TK_OP_ADD); + } break; + case '-': { + + if (GETCHAR(0) == '=') { + char_idx++; + return _make_token(TK_OP_ASSIGN_SUB); + } else if (GETCHAR(0) == '-') { + + char_idx++; + return _make_token(TK_OP_DECREMENT); + } + + return _make_token(TK_OP_SUB); + } break; + case '%': { + + if (GETCHAR(0) == '=') { + char_idx++; + return _make_token(TK_OP_ASSIGN_MOD); + } + + return _make_token(TK_OP_MOD); + } break; + default: { + + char_idx--; //go back one, since we have no idea what this is + + if (_is_number(GETCHAR(0)) || (GETCHAR(0) == '.' && _is_number(GETCHAR(1)))) { + // parse number + bool period_found = false; + bool exponent_found = false; + bool hexa_found = false; + bool sign_found = false; + bool float_suffix_found = false; + + String str; + int i = 0; + + while (true) { + if (GETCHAR(i) == '.') { + if (period_found || exponent_found || hexa_found || float_suffix_found) + return _make_token(TK_ERROR, "Invalid numeric constant"); + period_found = true; + } else if (GETCHAR(i) == 'x') { + if (hexa_found || str.length() != 1 || str[0] != '0') + return _make_token(TK_ERROR, "Invalid numeric constant"); + hexa_found = true; + } else if (GETCHAR(i) == 'e') { + if (hexa_found || exponent_found || float_suffix_found) + return _make_token(TK_ERROR, "Invalid numeric constant"); + exponent_found = true; + } else if (GETCHAR(i) == 'f') { + if (hexa_found || exponent_found) + return _make_token(TK_ERROR, "Invalid numeric constant"); + float_suffix_found = true; + } else if (_is_number(GETCHAR(i))) { + if (float_suffix_found) + return _make_token(TK_ERROR, "Invalid numeric constant"); + } else if (hexa_found && _is_hex(GETCHAR(i))) { + + } else if ((GETCHAR(i) == '-' || GETCHAR(i) == '+') && exponent_found) { + if (sign_found) + return _make_token(TK_ERROR, "Invalid numeric constant"); + sign_found = true; + } else + break; + + str += CharType(GETCHAR(i)); + i++; + } + + CharType last_char = str[str.length() - 1]; + + if (hexa_found) { + //integer(hex) + if (str.size() > 11 || !str.is_valid_hex_number(true)) { // > 0xFFFFFFFF + return _make_token(TK_ERROR, "Invalid (hexadecimal) numeric constant"); + } + } else if (period_found || exponent_found || float_suffix_found) { + //floats + if (period_found) { + if (float_suffix_found) { + //checks for eg "1.f" or "1.99f" notations + if (last_char != 'f') { + return _make_token(TK_ERROR, "Invalid (float) numeric constant"); + } + } else { + //checks for eg. "1." or "1.99" notations + if (last_char != '.' && !_is_number(last_char)) { + return _make_token(TK_ERROR, "Invalid (float) numeric constant"); + } + } + } else if (float_suffix_found) { + // if no period found the float suffix must be the last character, like in "2f" for "2.0" + if (last_char != 'f') { + return _make_token(TK_ERROR, "Invalid (float) numeric constant"); + } + } + + if (float_suffix_found) { + //strip the suffix + str = str.left(str.length() - 1); + //compensate reading cursor position + char_idx += 1; + } + + if (!str.is_valid_float()) { + return _make_token(TK_ERROR, "Invalid (float) numeric constant"); + } + } else { + //integers + if (!_is_number(last_char)) { + return _make_token(TK_ERROR, "Invalid (integer) numeric constant"); + } + if (!str.is_valid_integer()) { + return _make_token(TK_ERROR, "Invalid numeric constant"); + } + } + + char_idx += str.length(); + Token tk; + if (period_found || exponent_found || float_suffix_found) + tk.type = TK_REAL_CONSTANT; + else + tk.type = TK_INT_CONSTANT; + + if (hexa_found) { + tk.constant = (double)str.hex_to_int64(true); + } else { + tk.constant = str.to_double(); + } + tk.line = tk_line; + + return tk; + } + + if (GETCHAR(0) == '.') { + //parse period + char_idx++; + return _make_token(TK_PERIOD); + } + + if (_is_text_char(GETCHAR(0))) { + // parse identifier + String str; + + while (_is_text_char(GETCHAR(0))) { + + str += CharType(GETCHAR(0)); + char_idx++; + } + + //see if keyword + //should be converted to a static map + int idx = 0; + + while (keyword_list[idx].text) { + + if (str == keyword_list[idx].text) { + + return _make_token(keyword_list[idx].token); + } + idx++; + } + + str = str.replace("dus_", "_"); + + return _make_token(TK_IDENTIFIER, str); + } + + if (GETCHAR(0) > 32) + return _make_token(TK_ERROR, "Tokenizer: Unknown character #" + itos(GETCHAR(0)) + ": '" + String::chr(GETCHAR(0)) + "'"); + else + return _make_token(TK_ERROR, "Tokenizer: Unknown character #" + itos(GETCHAR(0))); + + } break; + } + } + ERR_PRINT("BUG"); + return Token(); + +#undef GETCHAR +} + +String ShaderLanguage::token_debug(const String &p_code) { + + clear(); + + code = p_code; + + String output; + + Token tk = _get_token(); + while (tk.type != TK_EOF && tk.type != TK_ERROR) { + + output += itos(tk_line) + ": " + get_token_text(tk) + "\n"; + tk = _get_token(); + } + + return output; +} + +bool ShaderLanguage::is_token_variable_datatype(TokenType p_type) { + return ( + p_type == TK_TYPE_VOID || + p_type == TK_TYPE_BOOL || + p_type == TK_TYPE_BVEC2 || + p_type == TK_TYPE_BVEC3 || + p_type == TK_TYPE_BVEC4 || + p_type == TK_TYPE_INT || + p_type == TK_TYPE_IVEC2 || + p_type == TK_TYPE_IVEC3 || + p_type == TK_TYPE_IVEC4 || + p_type == TK_TYPE_UINT || + p_type == TK_TYPE_UVEC2 || + p_type == TK_TYPE_UVEC3 || + p_type == TK_TYPE_UVEC4 || + p_type == TK_TYPE_FLOAT || + p_type == TK_TYPE_VEC2 || + p_type == TK_TYPE_VEC3 || + p_type == TK_TYPE_VEC4 || + p_type == TK_TYPE_MAT2 || + p_type == TK_TYPE_MAT3 || + p_type == TK_TYPE_MAT4); +} + +bool ShaderLanguage::is_token_datatype(TokenType p_type) { + + return ( + p_type == TK_TYPE_VOID || + p_type == TK_TYPE_BOOL || + p_type == TK_TYPE_BVEC2 || + p_type == TK_TYPE_BVEC3 || + p_type == TK_TYPE_BVEC4 || + p_type == TK_TYPE_INT || + p_type == TK_TYPE_IVEC2 || + p_type == TK_TYPE_IVEC3 || + p_type == TK_TYPE_IVEC4 || + p_type == TK_TYPE_UINT || + p_type == TK_TYPE_UVEC2 || + p_type == TK_TYPE_UVEC3 || + p_type == TK_TYPE_UVEC4 || + p_type == TK_TYPE_FLOAT || + p_type == TK_TYPE_VEC2 || + p_type == TK_TYPE_VEC3 || + p_type == TK_TYPE_VEC4 || + p_type == TK_TYPE_MAT2 || + p_type == TK_TYPE_MAT3 || + p_type == TK_TYPE_MAT4 || + p_type == TK_TYPE_SAMPLER2D || + p_type == TK_TYPE_ISAMPLER2D || + p_type == TK_TYPE_USAMPLER2D || + p_type == TK_TYPE_SAMPLER2DARRAY || + p_type == TK_TYPE_ISAMPLER2DARRAY || + p_type == TK_TYPE_USAMPLER2DARRAY || + p_type == TK_TYPE_SAMPLER3D || + p_type == TK_TYPE_ISAMPLER3D || + p_type == TK_TYPE_USAMPLER3D || + p_type == TK_TYPE_SAMPLERCUBE); +} + +ShaderLanguage::DataType ShaderLanguage::get_token_datatype(TokenType p_type) { + + return DataType(p_type - TK_TYPE_VOID); +} + +bool ShaderLanguage::is_token_interpolation(TokenType p_type) { + + return ( + p_type == TK_INTERPOLATION_FLAT || + p_type == TK_INTERPOLATION_SMOOTH); +} + +ShaderLanguage::DataInterpolation ShaderLanguage::get_token_interpolation(TokenType p_type) { + + if (p_type == TK_INTERPOLATION_FLAT) + return INTERPOLATION_FLAT; + else + return INTERPOLATION_SMOOTH; +} + +bool ShaderLanguage::is_token_precision(TokenType p_type) { + + return ( + p_type == TK_PRECISION_LOW || + p_type == TK_PRECISION_MID || + p_type == TK_PRECISION_HIGH); +} + +ShaderLanguage::DataPrecision ShaderLanguage::get_token_precision(TokenType p_type) { + + if (p_type == TK_PRECISION_LOW) + return PRECISION_LOWP; + else if (p_type == TK_PRECISION_HIGH) + return PRECISION_HIGHP; + else + return PRECISION_MEDIUMP; +} + +String ShaderLanguage::get_precision_name(DataPrecision p_type) { + switch (p_type) { + case PRECISION_LOWP: return "lowp"; + case PRECISION_MEDIUMP: return "mediump"; + case PRECISION_HIGHP: return "highp"; + default: + break; + } + return ""; +} + +String ShaderLanguage::get_datatype_name(DataType p_type) { + + switch (p_type) { + + case TYPE_VOID: return "void"; + case TYPE_BOOL: return "bool"; + case TYPE_BVEC2: return "bvec2"; + case TYPE_BVEC3: return "bvec3"; + case TYPE_BVEC4: return "bvec4"; + case TYPE_INT: return "int"; + case TYPE_IVEC2: return "ivec2"; + case TYPE_IVEC3: return "ivec3"; + case TYPE_IVEC4: return "ivec4"; + case TYPE_UINT: return "uint"; + case TYPE_UVEC2: return "uvec2"; + case TYPE_UVEC3: return "uvec3"; + case TYPE_UVEC4: return "uvec4"; + case TYPE_FLOAT: return "float"; + case TYPE_VEC2: return "vec2"; + case TYPE_VEC3: return "vec3"; + case TYPE_VEC4: return "vec4"; + case TYPE_MAT2: return "mat2"; + case TYPE_MAT3: return "mat3"; + case TYPE_MAT4: return "mat4"; + case TYPE_SAMPLER2D: return "sampler2D"; + case TYPE_ISAMPLER2D: return "isampler2D"; + case TYPE_USAMPLER2D: return "usampler2D"; + case TYPE_SAMPLER2DARRAY: return "sampler2DArray"; + case TYPE_ISAMPLER2DARRAY: return "isampler2DArray"; + case TYPE_USAMPLER2DARRAY: return "usampler2DArray"; + case TYPE_SAMPLER3D: return "sampler3D"; + case TYPE_ISAMPLER3D: return "isampler3D"; + case TYPE_USAMPLER3D: return "usampler3D"; + case TYPE_SAMPLERCUBE: return "samplerCube"; + case TYPE_STRUCT: return "struct"; + } + + return ""; +} + +bool ShaderLanguage::is_token_nonvoid_datatype(TokenType p_type) { + + return is_token_datatype(p_type) && p_type != TK_TYPE_VOID; +} + +void ShaderLanguage::clear() { + + current_function = StringName(); + + completion_type = COMPLETION_NONE; + completion_block = NULL; + completion_function = StringName(); + completion_class = SubClassTag::TAG_GLOBAL; + completion_struct = StringName(); + + error_line = 0; + tk_line = 1; + char_idx = 0; + error_set = false; + error_str = ""; + while (nodes) { + Node *n = nodes; + nodes = nodes->next; + memdelete(n); + } +} + +bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_reassign, const Map<StringName, BuiltInInfo> &p_builtin_types, const StringName &p_identifier, DataType *r_data_type, IdentifierType *r_type, bool *r_is_const, int *r_array_size, StringName *r_struct_name) { + + if (p_builtin_types.has(p_identifier)) { + + if (r_data_type) { + *r_data_type = p_builtin_types[p_identifier].type; + } + if (r_is_const) { + *r_is_const = p_builtin_types[p_identifier].constant; + } + if (r_type) { + *r_type = IDENTIFIER_BUILTIN_VAR; + } + + return true; + } + + FunctionNode *function = NULL; + + while (p_block) { + + if (p_block->variables.has(p_identifier)) { + if (r_data_type) { + *r_data_type = p_block->variables[p_identifier].type; + } + if (r_is_const) { + *r_is_const = p_block->variables[p_identifier].is_const; + } + if (r_array_size) { + *r_array_size = p_block->variables[p_identifier].array_size; + } + if (r_type) { + *r_type = IDENTIFIER_LOCAL_VAR; + } + if (r_struct_name) { + *r_struct_name = p_block->variables[p_identifier].struct_name; + } + + return true; + } + + if (p_block->parent_function) { + function = p_block->parent_function; + break; + } else { + if (p_allow_reassign) { + break; + } + ERR_FAIL_COND_V(!p_block->parent_block, false); + p_block = p_block->parent_block; + } + } + + if (function) { + for (int i = 0; i < function->arguments.size(); i++) { + if (function->arguments[i].name == p_identifier) { + if (r_data_type) { + *r_data_type = function->arguments[i].type; + } + if (r_type) { + *r_type = IDENTIFIER_FUNCTION_ARGUMENT; + } + if (r_struct_name) { + *r_struct_name = function->arguments[i].type_str; + } + return true; + } + } + } + + if (shader->varyings.has(p_identifier)) { + if (r_data_type) { + *r_data_type = shader->varyings[p_identifier].type; + } + if (r_array_size) { + *r_array_size = shader->varyings[p_identifier].array_size; + } + if (r_type) { + *r_type = IDENTIFIER_VARYING; + } + return true; + } + + if (shader->uniforms.has(p_identifier)) { + if (r_data_type) { + *r_data_type = shader->uniforms[p_identifier].type; + } + if (r_type) { + *r_type = IDENTIFIER_UNIFORM; + } + return true; + } + + if (shader->constants.has(p_identifier)) { + if (r_data_type) { + *r_data_type = shader->constants[p_identifier].type; + } + if (r_type) { + *r_type = IDENTIFIER_CONSTANT; + } + if (r_struct_name) { + *r_struct_name = shader->constants[p_identifier].type_str; + } + return true; + } + + for (int i = 0; i < shader->functions.size(); i++) { + + if (!shader->functions[i].callable) + continue; + + if (shader->functions[i].name == p_identifier) { + if (r_data_type) { + *r_data_type = shader->functions[i].function->return_type; + } + if (r_type) { + *r_type = IDENTIFIER_FUNCTION; + } + return true; + } + } + + return false; +} + +bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type) { + + bool valid = false; + DataType ret_type = TYPE_VOID; + + switch (p_op->op) { + case OP_EQUAL: + case OP_NOT_EQUAL: { + DataType na = p_op->arguments[0]->get_datatype(); + DataType nb = p_op->arguments[1]->get_datatype(); + valid = na == nb; + ret_type = TYPE_BOOL; + } break; + case OP_LESS: + case OP_LESS_EQUAL: + case OP_GREATER: + case OP_GREATER_EQUAL: { + DataType na = p_op->arguments[0]->get_datatype(); + DataType nb = p_op->arguments[1]->get_datatype(); + + valid = na == nb && (na == TYPE_UINT || na == TYPE_INT || na == TYPE_FLOAT); + ret_type = TYPE_BOOL; + + } break; + case OP_AND: + case OP_OR: { + DataType na = p_op->arguments[0]->get_datatype(); + DataType nb = p_op->arguments[1]->get_datatype(); + + valid = na == nb && na == TYPE_BOOL; + ret_type = TYPE_BOOL; + + } break; + case OP_NOT: { + + DataType na = p_op->arguments[0]->get_datatype(); + valid = na == TYPE_BOOL; + ret_type = TYPE_BOOL; + + } break; + case OP_INCREMENT: + case OP_DECREMENT: + case OP_POST_INCREMENT: + case OP_POST_DECREMENT: + case OP_NEGATE: { + DataType na = p_op->arguments[0]->get_datatype(); + valid = na > TYPE_BOOL && na < TYPE_MAT2; + ret_type = na; + } break; + case OP_ADD: + case OP_SUB: + case OP_MUL: + case OP_DIV: { + DataType na = p_op->arguments[0]->get_datatype(); + DataType nb = p_op->arguments[1]->get_datatype(); + + if (na > nb) { + //make things easier; + SWAP(na, nb); + } + + if (na == nb) { + valid = (na > TYPE_BOOL && na <= TYPE_MAT4); + ret_type = na; + } else if (na == TYPE_INT && nb == TYPE_IVEC2) { + valid = true; + ret_type = TYPE_IVEC2; + } else if (na == TYPE_INT && nb == TYPE_IVEC3) { + valid = true; + ret_type = TYPE_IVEC3; + } else if (na == TYPE_INT && nb == TYPE_IVEC4) { + valid = true; + ret_type = TYPE_IVEC4; + } else if (na == TYPE_UINT && nb == TYPE_UVEC2) { + valid = true; + ret_type = TYPE_UVEC2; + } else if (na == TYPE_UINT && nb == TYPE_UVEC3) { + valid = true; + ret_type = TYPE_UVEC3; + } else if (na == TYPE_UINT && nb == TYPE_UVEC4) { + valid = true; + ret_type = TYPE_UVEC4; + } else if (na == TYPE_FLOAT && nb == TYPE_VEC2) { + valid = true; + ret_type = TYPE_VEC2; + } else if (na == TYPE_FLOAT && nb == TYPE_VEC3) { + valid = true; + ret_type = TYPE_VEC3; + } else if (na == TYPE_FLOAT && nb == TYPE_VEC4) { + valid = true; + ret_type = TYPE_VEC4; + } else if (p_op->op == OP_MUL && na == TYPE_FLOAT && nb == TYPE_MAT2) { + valid = true; + ret_type = TYPE_MAT2; + } else if (p_op->op == OP_MUL && na == TYPE_FLOAT && nb == TYPE_MAT3) { + valid = true; + ret_type = TYPE_MAT3; + } else if (p_op->op == OP_MUL && na == TYPE_FLOAT && nb == TYPE_MAT4) { + valid = true; + ret_type = TYPE_MAT4; + } else if (p_op->op == OP_MUL && na == TYPE_VEC2 && nb == TYPE_MAT2) { + valid = true; + ret_type = TYPE_VEC2; + } else if (p_op->op == OP_MUL && na == TYPE_VEC3 && nb == TYPE_MAT3) { + valid = true; + ret_type = TYPE_VEC3; + } else if (p_op->op == OP_MUL && na == TYPE_VEC4 && nb == TYPE_MAT4) { + valid = true; + ret_type = TYPE_VEC4; + } + } break; + case OP_ASSIGN_MOD: + case OP_MOD: { + /* + * The operator modulus (%) operates on signed or unsigned integers or integer vectors. The operand + * types must both be signed or both be unsigned. The operands cannot be vectors of differing size. If + * one operand is a scalar and the other vector, then the scalar is applied component-wise to the vector, + * resulting in the same type as the vector. If both are vectors of the same size, the result is computed + * component-wise. + */ + + DataType na = p_op->arguments[0]->get_datatype(); + DataType nb = p_op->arguments[1]->get_datatype(); + + if (na == TYPE_INT && nb == TYPE_INT) { + valid = true; + ret_type = TYPE_INT; + } else if (na == TYPE_IVEC2 && nb == TYPE_INT) { + valid = true; + ret_type = TYPE_IVEC2; + } else if (na == TYPE_IVEC3 && nb == TYPE_INT) { + valid = true; + ret_type = TYPE_IVEC3; + } else if (na == TYPE_IVEC4 && nb == TYPE_INT) { + valid = true; + ret_type = TYPE_IVEC4; + } else if (na == TYPE_IVEC2 && nb == TYPE_IVEC2) { + valid = true; + ret_type = TYPE_IVEC2; + } else if (na == TYPE_IVEC3 && nb == TYPE_IVEC3) { + valid = true; + ret_type = TYPE_IVEC3; + } else if (na == TYPE_IVEC4 && nb == TYPE_IVEC4) { + valid = true; + ret_type = TYPE_IVEC4; + ///// + } else if (na == TYPE_UINT && nb == TYPE_UINT) { + valid = true; + ret_type = TYPE_UINT; + } else if (na == TYPE_UVEC2 && nb == TYPE_UINT) { + valid = true; + ret_type = TYPE_UVEC2; + } else if (na == TYPE_UVEC3 && nb == TYPE_UINT) { + valid = true; + ret_type = TYPE_UVEC3; + } else if (na == TYPE_UVEC4 && nb == TYPE_UINT) { + valid = true; + ret_type = TYPE_UVEC4; + } else if (na == TYPE_UVEC2 && nb == TYPE_UVEC2) { + valid = true; + ret_type = TYPE_UVEC2; + } else if (na == TYPE_UVEC3 && nb == TYPE_UVEC3) { + valid = true; + ret_type = TYPE_UVEC3; + } else if (na == TYPE_UVEC4 && nb == TYPE_UVEC4) { + valid = true; + ret_type = TYPE_UVEC4; + } + } break; + case OP_ASSIGN_SHIFT_LEFT: + case OP_ASSIGN_SHIFT_RIGHT: + case OP_SHIFT_LEFT: + case OP_SHIFT_RIGHT: { + + DataType na = p_op->arguments[0]->get_datatype(); + DataType nb = p_op->arguments[1]->get_datatype(); + + if (na == TYPE_INT && nb == TYPE_INT) { + valid = true; + ret_type = TYPE_INT; + } else if (na == TYPE_IVEC2 && nb == TYPE_INT) { + valid = true; + ret_type = TYPE_IVEC2; + } else if (na == TYPE_IVEC3 && nb == TYPE_INT) { + valid = true; + ret_type = TYPE_IVEC3; + } else if (na == TYPE_IVEC4 && nb == TYPE_INT) { + valid = true; + ret_type = TYPE_IVEC4; + } else if (na == TYPE_IVEC2 && nb == TYPE_IVEC2) { + valid = true; + ret_type = TYPE_IVEC2; + } else if (na == TYPE_IVEC3 && nb == TYPE_IVEC3) { + valid = true; + ret_type = TYPE_IVEC3; + } else if (na == TYPE_IVEC4 && nb == TYPE_IVEC4) { + valid = true; + ret_type = TYPE_IVEC4; + } else if (na == TYPE_UINT && nb == TYPE_UINT) { + valid = true; + ret_type = TYPE_UINT; + } else if (na == TYPE_UVEC2 && nb == TYPE_UINT) { + valid = true; + ret_type = TYPE_UVEC2; + } else if (na == TYPE_UVEC3 && nb == TYPE_UINT) { + valid = true; + ret_type = TYPE_UVEC3; + } else if (na == TYPE_UVEC4 && nb == TYPE_UINT) { + valid = true; + ret_type = TYPE_UVEC4; + } else if (na == TYPE_UVEC2 && nb == TYPE_UVEC2) { + valid = true; + ret_type = TYPE_UVEC2; + } else if (na == TYPE_UVEC3 && nb == TYPE_UVEC3) { + valid = true; + ret_type = TYPE_UVEC3; + } else if (na == TYPE_UVEC4 && nb == TYPE_UVEC4) { + valid = true; + ret_type = TYPE_UVEC4; + } + } break; + case OP_ASSIGN: { + DataType na = p_op->arguments[0]->get_datatype(); + DataType nb = p_op->arguments[1]->get_datatype(); + if (na == TYPE_STRUCT || nb == TYPE_STRUCT) { + valid = p_op->arguments[0]->get_datatype_name() == p_op->arguments[1]->get_datatype_name(); + } else { + valid = na == nb; + } + ret_type = na; + } break; + case OP_ASSIGN_ADD: + case OP_ASSIGN_SUB: + case OP_ASSIGN_MUL: + case OP_ASSIGN_DIV: { + + DataType na = p_op->arguments[0]->get_datatype(); + DataType nb = p_op->arguments[1]->get_datatype(); + + if (na == nb) { + valid = (na > TYPE_BOOL && na < TYPE_MAT2) || (p_op->op == OP_ASSIGN_MUL && na >= TYPE_MAT2 && na <= TYPE_MAT4); + ret_type = na; + } else if (na == TYPE_IVEC2 && nb == TYPE_INT) { + valid = true; + ret_type = TYPE_IVEC2; + } else if (na == TYPE_IVEC3 && nb == TYPE_INT) { + valid = true; + ret_type = TYPE_IVEC3; + } else if (na == TYPE_IVEC4 && nb == TYPE_INT) { + valid = true; + ret_type = TYPE_IVEC4; + } else if (na == TYPE_UVEC2 && nb == TYPE_UINT) { + valid = true; + ret_type = TYPE_UVEC2; + } else if (na == TYPE_UVEC3 && nb == TYPE_UINT) { + valid = true; + ret_type = TYPE_UVEC3; + } else if (na == TYPE_UVEC4 && nb == TYPE_UINT) { + valid = true; + ret_type = TYPE_UVEC4; + } else if (na == TYPE_VEC2 && nb == TYPE_FLOAT) { + valid = true; + ret_type = TYPE_VEC2; + } else if (na == TYPE_VEC3 && nb == TYPE_FLOAT) { + valid = true; + ret_type = TYPE_VEC3; + } else if (na == TYPE_VEC4 && nb == TYPE_FLOAT) { + valid = true; + ret_type = TYPE_VEC4; + } else if (p_op->op == OP_ASSIGN_MUL && na == TYPE_MAT2 && nb == TYPE_VEC2) { + valid = true; + ret_type = TYPE_MAT2; + } else if (p_op->op == OP_ASSIGN_MUL && na == TYPE_MAT3 && nb == TYPE_VEC3) { + valid = true; + ret_type = TYPE_MAT3; + } else if (p_op->op == OP_ASSIGN_MUL && na == TYPE_MAT4 && nb == TYPE_VEC4) { + valid = true; + ret_type = TYPE_MAT4; + } else if (p_op->op == OP_ASSIGN_MUL && na == TYPE_VEC2 && nb == TYPE_MAT2) { + valid = true; + ret_type = TYPE_VEC2; + } else if (p_op->op == OP_ASSIGN_MUL && na == TYPE_VEC3 && nb == TYPE_MAT3) { + valid = true; + ret_type = TYPE_VEC3; + } else if (p_op->op == OP_ASSIGN_MUL && na == TYPE_VEC4 && nb == TYPE_MAT4) { + valid = true; + ret_type = TYPE_VEC4; + } + } break; + case OP_ASSIGN_BIT_AND: + case OP_ASSIGN_BIT_OR: + case OP_ASSIGN_BIT_XOR: + case OP_BIT_AND: + case OP_BIT_OR: + case OP_BIT_XOR: { + + /* + * The bitwise operators and (&), exclusive-or (^), and inclusive-or (|). The operands must be of type + * signed or unsigned integers or integer vectors. The operands cannot be vectors of differing size. If + * one operand is a scalar and the other a vector, the scalar is applied component-wise to the vector, + * resulting in the same type as the vector. The fundamental types of the operands (signed or unsigned) + * must match. + */ + + DataType na = p_op->arguments[0]->get_datatype(); + DataType nb = p_op->arguments[1]->get_datatype(); + + if (na > nb && p_op->op >= OP_BIT_AND) { + //can swap for non assign + SWAP(na, nb); + } + + if (na == TYPE_INT && nb == TYPE_INT) { + valid = true; + ret_type = TYPE_INT; + } else if (na == TYPE_IVEC2 && nb == TYPE_INT) { + valid = true; + ret_type = TYPE_IVEC2; + } else if (na == TYPE_IVEC3 && nb == TYPE_INT) { + valid = true; + ret_type = TYPE_IVEC3; + } else if (na == TYPE_IVEC4 && nb == TYPE_INT) { + valid = true; + ret_type = TYPE_IVEC4; + } else if (na == TYPE_IVEC2 && nb == TYPE_IVEC2) { + valid = true; + ret_type = TYPE_IVEC2; + } else if (na == TYPE_IVEC3 && nb == TYPE_IVEC3) { + valid = true; + ret_type = TYPE_IVEC3; + } else if (na == TYPE_IVEC4 && nb == TYPE_IVEC4) { + valid = true; + ret_type = TYPE_IVEC4; + ///// + } else if (na == TYPE_UINT && nb == TYPE_UINT) { + valid = true; + ret_type = TYPE_UINT; + } else if (na == TYPE_UVEC2 && nb == TYPE_UINT) { + valid = true; + ret_type = TYPE_UVEC2; + } else if (na == TYPE_UVEC3 && nb == TYPE_UINT) { + valid = true; + ret_type = TYPE_UVEC3; + } else if (na == TYPE_UVEC4 && nb == TYPE_UINT) { + valid = true; + ret_type = TYPE_UVEC4; + } else if (na == TYPE_UVEC2 && nb == TYPE_UVEC2) { + valid = true; + ret_type = TYPE_UVEC2; + } else if (na == TYPE_UVEC3 && nb == TYPE_UVEC3) { + valid = true; + ret_type = TYPE_UVEC3; + } else if (na == TYPE_UVEC4 && nb == TYPE_UVEC4) { + valid = true; + ret_type = TYPE_UVEC4; + } + } break; + case OP_BIT_INVERT: { //unaries + DataType na = p_op->arguments[0]->get_datatype(); + valid = na >= TYPE_INT && na < TYPE_FLOAT; + ret_type = na; + } break; + case OP_SELECT_IF: { + DataType na = p_op->arguments[0]->get_datatype(); + DataType nb = p_op->arguments[1]->get_datatype(); + DataType nc = p_op->arguments[2]->get_datatype(); + + valid = na == TYPE_BOOL && (nb == nc); + ret_type = nb; + } break; + default: { + ERR_FAIL_V(false); + } + } + + if (r_ret_type) + *r_ret_type = ret_type; + return valid; +} + +const ShaderLanguage::BuiltinFuncDef ShaderLanguage::builtin_func_defs[] = { + //constructors + { "bool", TYPE_BOOL, { TYPE_BOOL, TYPE_VOID }, TAG_GLOBAL, false }, + { "bvec2", TYPE_BVEC2, { TYPE_BOOL, TYPE_VOID }, TAG_GLOBAL, false }, + { "bvec2", TYPE_BVEC2, { TYPE_BOOL, TYPE_BOOL, TYPE_VOID }, TAG_GLOBAL, false }, + { "bvec3", TYPE_BVEC3, { TYPE_BOOL, TYPE_VOID }, TAG_GLOBAL, false }, + { "bvec3", TYPE_BVEC3, { TYPE_BOOL, TYPE_BOOL, TYPE_BOOL, TYPE_VOID }, TAG_GLOBAL, false }, + { "bvec3", TYPE_BVEC3, { TYPE_BVEC2, TYPE_BOOL, TYPE_VOID }, TAG_GLOBAL, false }, + { "bvec3", TYPE_BVEC3, { TYPE_BOOL, TYPE_BVEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "bvec4", TYPE_BVEC4, { TYPE_BOOL, TYPE_VOID }, TAG_GLOBAL, false }, + { "bvec4", TYPE_BVEC4, { TYPE_BOOL, TYPE_BOOL, TYPE_BOOL, TYPE_BOOL, TYPE_VOID }, TAG_GLOBAL, false }, + { "bvec4", TYPE_BVEC4, { TYPE_BOOL, TYPE_BVEC2, TYPE_BOOL, TYPE_VOID }, TAG_GLOBAL, false }, + { "bvec4", TYPE_BVEC4, { TYPE_BVEC2, TYPE_BOOL, TYPE_BOOL, TYPE_VOID }, TAG_GLOBAL, false }, + { "bvec4", TYPE_BVEC4, { TYPE_BOOL, TYPE_BOOL, TYPE_BVEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "bvec4", TYPE_BVEC4, { TYPE_BOOL, TYPE_BVEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "bvec4", TYPE_BVEC4, { TYPE_BVEC3, TYPE_BOOL, TYPE_VOID }, TAG_GLOBAL, false }, + { "bvec4", TYPE_BVEC4, { TYPE_BVEC2, TYPE_BVEC2, TYPE_VOID }, TAG_GLOBAL, false }, + + { "float", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "vec2", TYPE_VEC2, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "vec2", TYPE_VEC2, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "vec3", TYPE_VEC3, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "vec3", TYPE_VEC3, { TYPE_FLOAT, TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "vec3", TYPE_VEC3, { TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "vec3", TYPE_VEC3, { TYPE_FLOAT, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "vec4", TYPE_VEC4, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "vec4", TYPE_VEC4, { TYPE_FLOAT, TYPE_FLOAT, TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "vec4", TYPE_VEC4, { TYPE_FLOAT, TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "vec4", TYPE_VEC4, { TYPE_VEC2, TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "vec4", TYPE_VEC4, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "vec4", TYPE_VEC4, { TYPE_FLOAT, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "vec4", TYPE_VEC4, { TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "vec4", TYPE_VEC4, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + + { "int", TYPE_INT, { TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, + { "ivec2", TYPE_IVEC2, { TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, + { "ivec2", TYPE_IVEC2, { TYPE_INT, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, + { "ivec3", TYPE_IVEC3, { TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, + { "ivec3", TYPE_IVEC3, { TYPE_INT, TYPE_INT, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, + { "ivec3", TYPE_IVEC3, { TYPE_IVEC2, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, + { "ivec3", TYPE_IVEC3, { TYPE_INT, TYPE_IVEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "ivec4", TYPE_IVEC4, { TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, + { "ivec4", TYPE_IVEC4, { TYPE_INT, TYPE_INT, TYPE_INT, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, + { "ivec4", TYPE_IVEC4, { TYPE_INT, TYPE_IVEC2, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, + { "ivec4", TYPE_IVEC4, { TYPE_IVEC2, TYPE_INT, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, + { "ivec4", TYPE_IVEC4, { TYPE_INT, TYPE_INT, TYPE_IVEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "ivec4", TYPE_IVEC4, { TYPE_INT, TYPE_IVEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "ivec4", TYPE_IVEC4, { TYPE_IVEC3, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, + { "ivec4", TYPE_IVEC4, { TYPE_IVEC2, TYPE_IVEC2, TYPE_VOID }, TAG_GLOBAL, false }, + + { "uint", TYPE_UINT, { TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, + { "uvec2", TYPE_UVEC2, { TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, + { "uvec2", TYPE_UVEC2, { TYPE_UINT, TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, + { "uvec3", TYPE_UVEC3, { TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, + { "uvec3", TYPE_UVEC3, { TYPE_UINT, TYPE_UINT, TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, + { "uvec3", TYPE_UVEC3, { TYPE_UVEC2, TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, + { "uvec3", TYPE_UVEC3, { TYPE_UINT, TYPE_UVEC2, TYPE_VOID }, TAG_GLOBAL, true }, + { "uvec4", TYPE_UVEC4, { TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, + { "uvec4", TYPE_UVEC4, { TYPE_UINT, TYPE_UINT, TYPE_UINT, TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, + { "uvec4", TYPE_UVEC4, { TYPE_UINT, TYPE_UVEC2, TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, + { "uvec4", TYPE_UVEC4, { TYPE_UVEC2, TYPE_UINT, TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, + { "uvec4", TYPE_UVEC4, { TYPE_UINT, TYPE_UINT, TYPE_UVEC2, TYPE_VOID }, TAG_GLOBAL, true }, + { "uvec4", TYPE_UVEC4, { TYPE_UINT, TYPE_UVEC3, TYPE_VOID }, TAG_GLOBAL, true }, + { "uvec4", TYPE_UVEC4, { TYPE_UVEC3, TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, + { "uvec4", TYPE_UVEC4, { TYPE_UVEC2, TYPE_UVEC2, TYPE_VOID }, TAG_GLOBAL, true }, + + { "mat2", TYPE_MAT2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "mat3", TYPE_MAT3, { TYPE_VEC3, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "mat4", TYPE_MAT4, { TYPE_VEC4, TYPE_VEC4, TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + + { "mat2", TYPE_MAT2, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "mat3", TYPE_MAT3, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "mat4", TYPE_MAT4, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + + //conversion scalars + + { "int", TYPE_INT, { TYPE_BOOL, TYPE_VOID }, TAG_GLOBAL, false }, + { "int", TYPE_INT, { TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, + { "int", TYPE_INT, { TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, + { "int", TYPE_INT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + + { "float", TYPE_FLOAT, { TYPE_BOOL, TYPE_VOID }, TAG_GLOBAL, false }, + { "float", TYPE_FLOAT, { TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, + { "float", TYPE_FLOAT, { TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, + { "float", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + + { "uint", TYPE_UINT, { TYPE_BOOL, TYPE_VOID }, TAG_GLOBAL, true }, + { "uint", TYPE_UINT, { TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, + { "uint", TYPE_UINT, { TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, + { "uint", TYPE_UINT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, + + { "bool", TYPE_BOOL, { TYPE_BOOL, TYPE_VOID }, TAG_GLOBAL, false }, + { "bool", TYPE_BOOL, { TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, + { "bool", TYPE_BOOL, { TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, + { "bool", TYPE_BOOL, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + + //conversion vectors + + { "ivec2", TYPE_IVEC2, { TYPE_BVEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "ivec2", TYPE_IVEC2, { TYPE_IVEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "ivec2", TYPE_IVEC2, { TYPE_UVEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "ivec2", TYPE_IVEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + + { "vec2", TYPE_VEC2, { TYPE_BVEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "vec2", TYPE_VEC2, { TYPE_IVEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "vec2", TYPE_VEC2, { TYPE_UVEC2, TYPE_VOID }, TAG_GLOBAL, true }, + { "vec2", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + + { "uvec2", TYPE_UVEC2, { TYPE_BVEC2, TYPE_VOID }, TAG_GLOBAL, true }, + { "uvec2", TYPE_UVEC2, { TYPE_IVEC2, TYPE_VOID }, TAG_GLOBAL, true }, + { "uvec2", TYPE_UVEC2, { TYPE_UVEC2, TYPE_VOID }, TAG_GLOBAL, true }, + { "uvec2", TYPE_UVEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, true }, + + { "bvec2", TYPE_BVEC2, { TYPE_BVEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "bvec2", TYPE_BVEC2, { TYPE_IVEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "bvec2", TYPE_BVEC2, { TYPE_UVEC2, TYPE_VOID }, TAG_GLOBAL, true }, + { "bvec2", TYPE_BVEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + + { "ivec3", TYPE_IVEC3, { TYPE_BVEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "ivec3", TYPE_IVEC3, { TYPE_IVEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "ivec3", TYPE_IVEC3, { TYPE_UVEC3, TYPE_VOID }, TAG_GLOBAL, true }, + { "ivec3", TYPE_IVEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + + { "vec3", TYPE_VEC3, { TYPE_BVEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "vec3", TYPE_VEC3, { TYPE_IVEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "vec3", TYPE_VEC3, { TYPE_UVEC3, TYPE_VOID }, TAG_GLOBAL, true }, + { "vec3", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + + { "uvec3", TYPE_UVEC3, { TYPE_BVEC3, TYPE_VOID }, TAG_GLOBAL, true }, + { "uvec3", TYPE_UVEC3, { TYPE_IVEC3, TYPE_VOID }, TAG_GLOBAL, true }, + { "uvec3", TYPE_UVEC3, { TYPE_UVEC3, TYPE_VOID }, TAG_GLOBAL, true }, + { "uvec3", TYPE_UVEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, true }, + + { "bvec3", TYPE_BVEC3, { TYPE_BVEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "bvec3", TYPE_BVEC3, { TYPE_IVEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "bvec3", TYPE_BVEC3, { TYPE_UVEC3, TYPE_VOID }, TAG_GLOBAL, true }, + { "bvec3", TYPE_BVEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + + { "ivec4", TYPE_IVEC4, { TYPE_BVEC4, TYPE_VOID }, TAG_GLOBAL, false }, + { "ivec4", TYPE_IVEC4, { TYPE_IVEC4, TYPE_VOID }, TAG_GLOBAL, false }, + { "ivec4", TYPE_IVEC4, { TYPE_UVEC4, TYPE_VOID }, TAG_GLOBAL, true }, + { "ivec4", TYPE_IVEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + + { "vec4", TYPE_VEC4, { TYPE_BVEC4, TYPE_VOID }, TAG_GLOBAL, false }, + { "vec4", TYPE_VEC4, { TYPE_IVEC4, TYPE_VOID }, TAG_GLOBAL, false }, + { "vec4", TYPE_VEC4, { TYPE_UVEC4, TYPE_VOID }, TAG_GLOBAL, true }, + { "vec4", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + + { "uvec4", TYPE_UVEC4, { TYPE_BVEC4, TYPE_VOID }, TAG_GLOBAL, true }, + { "uvec4", TYPE_UVEC4, { TYPE_IVEC4, TYPE_VOID }, TAG_GLOBAL, true }, + { "uvec4", TYPE_UVEC4, { TYPE_UVEC4, TYPE_VOID }, TAG_GLOBAL, true }, + { "uvec4", TYPE_UVEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, true }, + + { "bvec4", TYPE_BVEC4, { TYPE_BVEC4, TYPE_VOID }, TAG_GLOBAL, false }, + { "bvec4", TYPE_BVEC4, { TYPE_IVEC4, TYPE_VOID }, TAG_GLOBAL, false }, + { "bvec4", TYPE_BVEC4, { TYPE_UVEC4, TYPE_VOID }, TAG_GLOBAL, true }, + { "bvec4", TYPE_BVEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + + //conversion between matrixes + + { "mat2", TYPE_MAT2, { TYPE_MAT3, TYPE_VOID }, TAG_GLOBAL, false }, + { "mat2", TYPE_MAT2, { TYPE_MAT4, TYPE_VOID }, TAG_GLOBAL, false }, + { "mat3", TYPE_MAT3, { TYPE_MAT2, TYPE_VOID }, TAG_GLOBAL, false }, + { "mat3", TYPE_MAT3, { TYPE_MAT4, TYPE_VOID }, TAG_GLOBAL, false }, + { "mat4", TYPE_MAT4, { TYPE_MAT2, TYPE_VOID }, TAG_GLOBAL, false }, + { "mat4", TYPE_MAT4, { TYPE_MAT3, TYPE_VOID }, TAG_GLOBAL, false }, + + //builtins - trigonometry + + { "radians", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "radians", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "radians", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "radians", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + + { "degrees", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "degrees", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "degrees", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "degrees", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + + { "sin", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "sin", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "sin", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "sin", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + + { "cos", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "cos", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "cos", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "cos", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + + { "tan", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "tan", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "tan", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "tan", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + + { "asin", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "asin", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "asin", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "asin", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + + { "acos", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "acos", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "acos", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "acos", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + + { "atan", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "atan", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "atan", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "atan", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + { "atan", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "atan", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "atan", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "atan", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + + { "sinh", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "sinh", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "sinh", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "sinh", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + + { "cosh", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "cosh", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "cosh", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "cosh", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + + { "tanh", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "tanh", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "tanh", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "tanh", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + + { "asinh", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "asinh", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "asinh", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "asinh", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + + { "acosh", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "acosh", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "acosh", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "acosh", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + + { "atanh", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "atanh", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "atanh", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "atanh", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + + //builtins - exponential + { "pow", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "pow", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "pow", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "pow", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + { "exp", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "exp", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "exp", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "exp", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + { "log", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "log", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "log", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "log", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + { "exp2", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "exp2", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "exp2", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "exp2", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + { "log2", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "log2", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "log2", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "log2", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + { "sqrt", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "sqrt", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "sqrt", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "sqrt", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + { "inversesqrt", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "inversesqrt", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "inversesqrt", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "inversesqrt", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + //builtins - common + { "abs", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "abs", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "abs", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "abs", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + + { "abs", TYPE_INT, { TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, + { "abs", TYPE_IVEC2, { TYPE_IVEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "abs", TYPE_IVEC3, { TYPE_IVEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "abs", TYPE_IVEC4, { TYPE_IVEC4, TYPE_VOID }, TAG_GLOBAL, false }, + + { "sign", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "sign", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "sign", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "sign", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + + { "sign", TYPE_INT, { TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, + { "sign", TYPE_IVEC2, { TYPE_IVEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "sign", TYPE_IVEC3, { TYPE_IVEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "sign", TYPE_IVEC4, { TYPE_IVEC4, TYPE_VOID }, TAG_GLOBAL, false }, + + { "floor", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "floor", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "floor", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "floor", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + { "trunc", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "trunc", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "trunc", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "trunc", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + { "round", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "round", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "round", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "round", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + { "roundEven", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "roundEven", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "roundEven", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "roundEven", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + { "ceil", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "ceil", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "ceil", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "ceil", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + { "fract", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "fract", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "fract", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "fract", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + + { "mod", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "mod", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "mod", TYPE_VEC2, { TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "mod", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "mod", TYPE_VEC3, { TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "mod", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + { "mod", TYPE_VEC4, { TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + + { "modf", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, + { "modf", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, true }, + { "modf", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, true }, + { "modf", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, true }, + + { "min", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "min", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "min", TYPE_VEC2, { TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "min", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "min", TYPE_VEC3, { TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "min", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + { "min", TYPE_VEC4, { TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + + { "min", TYPE_INT, { TYPE_INT, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, + { "min", TYPE_IVEC2, { TYPE_IVEC2, TYPE_IVEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "min", TYPE_IVEC2, { TYPE_IVEC2, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, + { "min", TYPE_IVEC3, { TYPE_IVEC3, TYPE_IVEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "min", TYPE_IVEC3, { TYPE_IVEC3, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, + { "min", TYPE_IVEC4, { TYPE_IVEC4, TYPE_IVEC4, TYPE_VOID }, TAG_GLOBAL, false }, + { "min", TYPE_IVEC4, { TYPE_IVEC4, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, + + { "min", TYPE_UINT, { TYPE_UINT, TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, + { "min", TYPE_UVEC2, { TYPE_UVEC2, TYPE_UVEC2, TYPE_VOID }, TAG_GLOBAL, true }, + { "min", TYPE_UVEC2, { TYPE_UVEC2, TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, + { "min", TYPE_UVEC3, { TYPE_UVEC3, TYPE_UVEC3, TYPE_VOID }, TAG_GLOBAL, true }, + { "min", TYPE_UVEC3, { TYPE_UVEC3, TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, + { "min", TYPE_UVEC4, { TYPE_UVEC4, TYPE_UVEC4, TYPE_VOID }, TAG_GLOBAL, true }, + { "min", TYPE_UVEC4, { TYPE_UVEC4, TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, + + { "max", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "max", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "max", TYPE_VEC2, { TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "max", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "max", TYPE_VEC3, { TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "max", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + { "max", TYPE_VEC4, { TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + + { "max", TYPE_INT, { TYPE_INT, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, + { "max", TYPE_IVEC2, { TYPE_IVEC2, TYPE_IVEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "max", TYPE_IVEC2, { TYPE_IVEC2, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, + { "max", TYPE_IVEC3, { TYPE_IVEC3, TYPE_IVEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "max", TYPE_IVEC3, { TYPE_IVEC3, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, + { "max", TYPE_IVEC4, { TYPE_IVEC4, TYPE_IVEC4, TYPE_VOID }, TAG_GLOBAL, false }, + { "max", TYPE_IVEC4, { TYPE_IVEC4, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, + + { "max", TYPE_UINT, { TYPE_UINT, TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, + { "max", TYPE_UVEC2, { TYPE_UVEC2, TYPE_UVEC2, TYPE_VOID }, TAG_GLOBAL, true }, + { "max", TYPE_UVEC2, { TYPE_UVEC2, TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, + { "max", TYPE_UVEC3, { TYPE_UVEC3, TYPE_UVEC3, TYPE_VOID }, TAG_GLOBAL, true }, + { "max", TYPE_UVEC3, { TYPE_UVEC3, TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, + { "max", TYPE_UVEC4, { TYPE_UVEC4, TYPE_UVEC4, TYPE_VOID }, TAG_GLOBAL, true }, + { "max", TYPE_UVEC4, { TYPE_UVEC4, TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, + + { "clamp", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "clamp", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "clamp", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "clamp", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + { "clamp", TYPE_VEC2, { TYPE_VEC2, TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "clamp", TYPE_VEC3, { TYPE_VEC3, TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "clamp", TYPE_VEC4, { TYPE_VEC4, TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + + { "clamp", TYPE_INT, { TYPE_INT, TYPE_INT, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, + { "clamp", TYPE_IVEC2, { TYPE_IVEC2, TYPE_IVEC2, TYPE_IVEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "clamp", TYPE_IVEC3, { TYPE_IVEC3, TYPE_IVEC3, TYPE_IVEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "clamp", TYPE_IVEC4, { TYPE_IVEC4, TYPE_IVEC4, TYPE_IVEC4, TYPE_VOID }, TAG_GLOBAL, false }, + { "clamp", TYPE_IVEC2, { TYPE_IVEC2, TYPE_INT, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, + { "clamp", TYPE_IVEC3, { TYPE_IVEC3, TYPE_INT, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, + { "clamp", TYPE_IVEC4, { TYPE_IVEC4, TYPE_INT, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, + + { "clamp", TYPE_UINT, { TYPE_UINT, TYPE_UINT, TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, + { "clamp", TYPE_UVEC2, { TYPE_UVEC2, TYPE_UVEC2, TYPE_UVEC2, TYPE_VOID }, TAG_GLOBAL, true }, + { "clamp", TYPE_UVEC3, { TYPE_UVEC3, TYPE_UVEC3, TYPE_UVEC3, TYPE_VOID }, TAG_GLOBAL, true }, + { "clamp", TYPE_UVEC4, { TYPE_UVEC4, TYPE_UVEC4, TYPE_UVEC4, TYPE_VOID }, TAG_GLOBAL, true }, + { "clamp", TYPE_UVEC2, { TYPE_UVEC2, TYPE_UINT, TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, + { "clamp", TYPE_UVEC3, { TYPE_UVEC3, TYPE_UINT, TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, + { "clamp", TYPE_UVEC4, { TYPE_UVEC4, TYPE_UINT, TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, + + { "mix", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "mix", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "mix", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_BVEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "mix", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "mix", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "mix", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_BVEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "mix", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "mix", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "mix", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_BVEC4, TYPE_VOID }, TAG_GLOBAL, false }, + { "mix", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + + { "step", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "step", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "step", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "step", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + { "step", TYPE_VEC2, { TYPE_FLOAT, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "step", TYPE_VEC3, { TYPE_FLOAT, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "step", TYPE_VEC4, { TYPE_FLOAT, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + { "smoothstep", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "smoothstep", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "smoothstep", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "smoothstep", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + { "smoothstep", TYPE_VEC2, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "smoothstep", TYPE_VEC3, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "smoothstep", TYPE_VEC4, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + + { "isnan", TYPE_BOOL, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "isnan", TYPE_BVEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "isnan", TYPE_BVEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "isnan", TYPE_BVEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + + { "isinf", TYPE_BOOL, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "isinf", TYPE_BVEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "isinf", TYPE_BVEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "isinf", TYPE_BVEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + + { "floatBitsToInt", TYPE_INT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, + { "floatBitsToInt", TYPE_IVEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, true }, + { "floatBitsToInt", TYPE_IVEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, true }, + { "floatBitsToInt", TYPE_IVEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, true }, + + { "floatBitsToUint", TYPE_UINT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, + { "floatBitsToUint", TYPE_UVEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, true }, + { "floatBitsToUint", TYPE_UVEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, true }, + { "floatBitsToUint", TYPE_UVEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, true }, + + { "intBitsToFloat", TYPE_FLOAT, { TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, + { "intBitsToFloat", TYPE_VEC2, { TYPE_IVEC2, TYPE_VOID }, TAG_GLOBAL, true }, + { "intBitsToFloat", TYPE_VEC3, { TYPE_IVEC3, TYPE_VOID }, TAG_GLOBAL, true }, + { "intBitsToFloat", TYPE_VEC4, { TYPE_IVEC4, TYPE_VOID }, TAG_GLOBAL, true }, + + { "uintBitsToFloat", TYPE_FLOAT, { TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, + { "uintBitsToFloat", TYPE_VEC2, { TYPE_UVEC2, TYPE_VOID }, TAG_GLOBAL, true }, + { "uintBitsToFloat", TYPE_VEC3, { TYPE_UVEC3, TYPE_VOID }, TAG_GLOBAL, true }, + { "uintBitsToFloat", TYPE_VEC4, { TYPE_UVEC4, TYPE_VOID }, TAG_GLOBAL, true }, + + //builtins - geometric + { "length", TYPE_FLOAT, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "length", TYPE_FLOAT, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "length", TYPE_FLOAT, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + { "distance", TYPE_FLOAT, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "distance", TYPE_FLOAT, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "distance", TYPE_FLOAT, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + { "dot", TYPE_FLOAT, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "dot", TYPE_FLOAT, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "dot", TYPE_FLOAT, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + { "cross", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "normalize", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "normalize", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "normalize", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + { "reflect", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "refract", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + + { "faceforward", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "faceforward", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "faceforward", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + + { "matrixCompMult", TYPE_MAT2, { TYPE_MAT2, TYPE_MAT2, TYPE_VOID }, TAG_GLOBAL, false }, + { "matrixCompMult", TYPE_MAT3, { TYPE_MAT3, TYPE_MAT3, TYPE_VOID }, TAG_GLOBAL, false }, + { "matrixCompMult", TYPE_MAT4, { TYPE_MAT4, TYPE_MAT4, TYPE_VOID }, TAG_GLOBAL, false }, + + { "outerProduct", TYPE_MAT2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "outerProduct", TYPE_MAT3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "outerProduct", TYPE_MAT4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + + { "transpose", TYPE_MAT2, { TYPE_MAT2, TYPE_VOID }, TAG_GLOBAL, false }, + { "transpose", TYPE_MAT3, { TYPE_MAT3, TYPE_VOID }, TAG_GLOBAL, false }, + { "transpose", TYPE_MAT4, { TYPE_MAT4, TYPE_VOID }, TAG_GLOBAL, false }, + + { "determinant", TYPE_FLOAT, { TYPE_MAT2, TYPE_VOID }, TAG_GLOBAL, false }, + { "determinant", TYPE_FLOAT, { TYPE_MAT3, TYPE_VOID }, TAG_GLOBAL, false }, + { "determinant", TYPE_FLOAT, { TYPE_MAT4, TYPE_VOID }, TAG_GLOBAL, false }, + + { "inverse", TYPE_MAT2, { TYPE_MAT2, TYPE_VOID }, TAG_GLOBAL, false }, + { "inverse", TYPE_MAT3, { TYPE_MAT3, TYPE_VOID }, TAG_GLOBAL, false }, + { "inverse", TYPE_MAT4, { TYPE_MAT4, TYPE_VOID }, TAG_GLOBAL, false }, + + { "lessThan", TYPE_BVEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "lessThan", TYPE_BVEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "lessThan", TYPE_BVEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + + { "lessThan", TYPE_BVEC2, { TYPE_IVEC2, TYPE_IVEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "lessThan", TYPE_BVEC3, { TYPE_IVEC3, TYPE_IVEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "lessThan", TYPE_BVEC4, { TYPE_IVEC4, TYPE_IVEC4, TYPE_VOID }, TAG_GLOBAL, false }, + + { "lessThan", TYPE_BVEC2, { TYPE_UVEC2, TYPE_UVEC2, TYPE_VOID }, TAG_GLOBAL, true }, + { "lessThan", TYPE_BVEC3, { TYPE_UVEC3, TYPE_UVEC3, TYPE_VOID }, TAG_GLOBAL, true }, + { "lessThan", TYPE_BVEC4, { TYPE_UVEC4, TYPE_UVEC4, TYPE_VOID }, TAG_GLOBAL, true }, + + { "greaterThan", TYPE_BVEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "greaterThan", TYPE_BVEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "greaterThan", TYPE_BVEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + + { "greaterThan", TYPE_BVEC2, { TYPE_IVEC2, TYPE_IVEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "greaterThan", TYPE_BVEC3, { TYPE_IVEC3, TYPE_IVEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "greaterThan", TYPE_BVEC4, { TYPE_IVEC4, TYPE_IVEC4, TYPE_VOID }, TAG_GLOBAL, false }, + + { "greaterThan", TYPE_BVEC2, { TYPE_UVEC2, TYPE_UVEC2, TYPE_VOID }, TAG_GLOBAL, true }, + { "greaterThan", TYPE_BVEC3, { TYPE_UVEC3, TYPE_UVEC3, TYPE_VOID }, TAG_GLOBAL, true }, + { "greaterThan", TYPE_BVEC4, { TYPE_UVEC4, TYPE_UVEC4, TYPE_VOID }, TAG_GLOBAL, true }, + + { "lessThanEqual", TYPE_BVEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "lessThanEqual", TYPE_BVEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "lessThanEqual", TYPE_BVEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + + { "lessThanEqual", TYPE_BVEC2, { TYPE_IVEC2, TYPE_IVEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "lessThanEqual", TYPE_BVEC3, { TYPE_IVEC3, TYPE_IVEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "lessThanEqual", TYPE_BVEC4, { TYPE_IVEC4, TYPE_IVEC4, TYPE_VOID }, TAG_GLOBAL, false }, + + { "lessThanEqual", TYPE_BVEC2, { TYPE_UVEC2, TYPE_UVEC2, TYPE_VOID }, TAG_GLOBAL, true }, + { "lessThanEqual", TYPE_BVEC3, { TYPE_UVEC3, TYPE_UVEC3, TYPE_VOID }, TAG_GLOBAL, true }, + { "lessThanEqual", TYPE_BVEC4, { TYPE_UVEC4, TYPE_UVEC4, TYPE_VOID }, TAG_GLOBAL, true }, + + { "greaterThanEqual", TYPE_BVEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "greaterThanEqual", TYPE_BVEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "greaterThanEqual", TYPE_BVEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + + { "greaterThanEqual", TYPE_BVEC2, { TYPE_IVEC2, TYPE_IVEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "greaterThanEqual", TYPE_BVEC3, { TYPE_IVEC3, TYPE_IVEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "greaterThanEqual", TYPE_BVEC4, { TYPE_IVEC4, TYPE_IVEC4, TYPE_VOID }, TAG_GLOBAL, false }, + + { "greaterThanEqual", TYPE_BVEC2, { TYPE_UVEC2, TYPE_UVEC2, TYPE_VOID }, TAG_GLOBAL, true }, + { "greaterThanEqual", TYPE_BVEC3, { TYPE_UVEC3, TYPE_UVEC3, TYPE_VOID }, TAG_GLOBAL, true }, + { "greaterThanEqual", TYPE_BVEC4, { TYPE_UVEC4, TYPE_UVEC4, TYPE_VOID }, TAG_GLOBAL, true }, + + { "equal", TYPE_BVEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "equal", TYPE_BVEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "equal", TYPE_BVEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + + { "equal", TYPE_BVEC2, { TYPE_IVEC2, TYPE_IVEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "equal", TYPE_BVEC3, { TYPE_IVEC3, TYPE_IVEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "equal", TYPE_BVEC4, { TYPE_IVEC4, TYPE_IVEC4, TYPE_VOID }, TAG_GLOBAL, false }, + + { "equal", TYPE_BVEC2, { TYPE_UVEC2, TYPE_UVEC2, TYPE_VOID }, TAG_GLOBAL, true }, + { "equal", TYPE_BVEC3, { TYPE_UVEC3, TYPE_UVEC3, TYPE_VOID }, TAG_GLOBAL, true }, + { "equal", TYPE_BVEC4, { TYPE_UVEC4, TYPE_UVEC4, TYPE_VOID }, TAG_GLOBAL, true }, + + { "equal", TYPE_BVEC2, { TYPE_BVEC2, TYPE_BVEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "equal", TYPE_BVEC3, { TYPE_BVEC3, TYPE_BVEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "equal", TYPE_BVEC4, { TYPE_BVEC4, TYPE_BVEC4, TYPE_VOID }, TAG_GLOBAL, false }, + + { "notEqual", TYPE_BVEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "notEqual", TYPE_BVEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "notEqual", TYPE_BVEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + + { "notEqual", TYPE_BVEC2, { TYPE_IVEC2, TYPE_IVEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "notEqual", TYPE_BVEC3, { TYPE_IVEC3, TYPE_IVEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "notEqual", TYPE_BVEC4, { TYPE_IVEC4, TYPE_IVEC4, TYPE_VOID }, TAG_GLOBAL, false }, + + { "notEqual", TYPE_BVEC2, { TYPE_UVEC2, TYPE_UVEC2, TYPE_VOID }, TAG_GLOBAL, true }, + { "notEqual", TYPE_BVEC3, { TYPE_UVEC3, TYPE_UVEC3, TYPE_VOID }, TAG_GLOBAL, true }, + { "notEqual", TYPE_BVEC4, { TYPE_UVEC4, TYPE_UVEC4, TYPE_VOID }, TAG_GLOBAL, true }, + + { "notEqual", TYPE_BVEC2, { TYPE_BVEC2, TYPE_BVEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "notEqual", TYPE_BVEC3, { TYPE_BVEC3, TYPE_BVEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "notEqual", TYPE_BVEC4, { TYPE_BVEC4, TYPE_BVEC4, TYPE_VOID }, TAG_GLOBAL, false }, + + { "any", TYPE_BOOL, { TYPE_BVEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "any", TYPE_BOOL, { TYPE_BVEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "any", TYPE_BOOL, { TYPE_BVEC4, TYPE_VOID }, TAG_GLOBAL, false }, + + { "all", TYPE_BOOL, { TYPE_BVEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "all", TYPE_BOOL, { TYPE_BVEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "all", TYPE_BOOL, { TYPE_BVEC4, TYPE_VOID }, TAG_GLOBAL, false }, + + { "not", TYPE_BVEC2, { TYPE_BVEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "not", TYPE_BVEC3, { TYPE_BVEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "not", TYPE_BVEC4, { TYPE_BVEC4, TYPE_VOID }, TAG_GLOBAL, false }, + + //builtins - texture + { "textureSize", TYPE_IVEC2, { TYPE_SAMPLER2D, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, + { "textureSize", TYPE_IVEC2, { TYPE_ISAMPLER2D, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, + { "textureSize", TYPE_IVEC2, { TYPE_USAMPLER2D, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, + { "textureSize", TYPE_IVEC3, { TYPE_SAMPLER2DARRAY, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, + { "textureSize", TYPE_IVEC3, { TYPE_ISAMPLER2DARRAY, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, + { "textureSize", TYPE_IVEC3, { TYPE_USAMPLER2DARRAY, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, + { "textureSize", TYPE_IVEC3, { TYPE_SAMPLER3D, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, + { "textureSize", TYPE_IVEC3, { TYPE_ISAMPLER3D, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, + { "textureSize", TYPE_IVEC3, { TYPE_USAMPLER3D, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, + { "textureSize", TYPE_IVEC2, { TYPE_SAMPLERCUBE, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, + + { "texture", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, + { "texture", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "texture", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, true }, + { "texture", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, + { "texture", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, true }, + { "texture", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, + { "texture", TYPE_VEC4, { TYPE_SAMPLER2DARRAY, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "texture", TYPE_VEC4, { TYPE_SAMPLER2DARRAY, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "texture", TYPE_UVEC4, { TYPE_USAMPLER2DARRAY, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, true }, + { "texture", TYPE_UVEC4, { TYPE_USAMPLER2DARRAY, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, + { "texture", TYPE_IVEC4, { TYPE_ISAMPLER2DARRAY, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, true }, + { "texture", TYPE_IVEC4, { TYPE_ISAMPLER2DARRAY, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, + { "texture", TYPE_VEC4, { TYPE_SAMPLER3D, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "texture", TYPE_VEC4, { TYPE_SAMPLER3D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "texture", TYPE_UVEC4, { TYPE_USAMPLER3D, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, true }, + { "texture", TYPE_UVEC4, { TYPE_USAMPLER3D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, + { "texture", TYPE_IVEC4, { TYPE_ISAMPLER3D, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, true }, + { "texture", TYPE_IVEC4, { TYPE_ISAMPLER3D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, + { "texture", TYPE_VEC4, { TYPE_SAMPLERCUBE, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, + { "texture", TYPE_VEC4, { TYPE_SAMPLERCUBE, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + + { "textureProj", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, true }, + { "textureProj", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, true }, + { "textureProj", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, + { "textureProj", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, + { "textureProj", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, true }, + { "textureProj", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, true }, + { "textureProj", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, + { "textureProj", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, + { "textureProj", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, true }, + { "textureProj", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, true }, + { "textureProj", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, + { "textureProj", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, + { "textureProj", TYPE_VEC4, { TYPE_SAMPLER3D, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, true }, + { "textureProj", TYPE_VEC4, { TYPE_SAMPLER3D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, + { "textureProj", TYPE_IVEC4, { TYPE_ISAMPLER3D, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, true }, + { "textureProj", TYPE_IVEC4, { TYPE_ISAMPLER3D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, + { "textureProj", TYPE_UVEC4, { TYPE_USAMPLER3D, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, true }, + { "textureProj", TYPE_UVEC4, { TYPE_USAMPLER3D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, + + { "textureLod", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "textureLod", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, + { "textureLod", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, + { "textureLod", TYPE_VEC4, { TYPE_SAMPLER2DARRAY, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "textureLod", TYPE_IVEC4, { TYPE_ISAMPLER2DARRAY, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, + { "textureLod", TYPE_UVEC4, { TYPE_USAMPLER2DARRAY, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, + { "textureLod", TYPE_VEC4, { TYPE_SAMPLER3D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "textureLod", TYPE_IVEC4, { TYPE_ISAMPLER3D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, + { "textureLod", TYPE_UVEC4, { TYPE_USAMPLER3D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, + { "textureLod", TYPE_VEC4, { TYPE_SAMPLERCUBE, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + + { "texelFetch", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_IVEC2, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, + { "texelFetch", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_IVEC2, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, + { "texelFetch", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_IVEC2, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, + { "texelFetch", TYPE_VEC4, { TYPE_SAMPLER2DARRAY, TYPE_IVEC3, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, + { "texelFetch", TYPE_IVEC4, { TYPE_ISAMPLER2DARRAY, TYPE_IVEC3, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, + { "texelFetch", TYPE_UVEC4, { TYPE_USAMPLER2DARRAY, TYPE_IVEC3, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, + { "texelFetch", TYPE_VEC4, { TYPE_SAMPLER3D, TYPE_IVEC3, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, + { "texelFetch", TYPE_IVEC4, { TYPE_ISAMPLER3D, TYPE_IVEC3, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, + { "texelFetch", TYPE_UVEC4, { TYPE_USAMPLER3D, TYPE_IVEC3, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, + + { "textureProjLod", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, + { "textureProjLod", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, + { "textureProjLod", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, + { "textureProjLod", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, + { "textureProjLod", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, + { "textureProjLod", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, + { "textureProjLod", TYPE_VEC4, { TYPE_SAMPLER3D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, + { "textureProjLod", TYPE_IVEC4, { TYPE_ISAMPLER3D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, + { "textureProjLod", TYPE_UVEC4, { TYPE_USAMPLER3D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, + + { "textureGrad", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC2, TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, true }, + { "textureGrad", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC2, TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, true }, + { "textureGrad", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC2, TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, true }, + { "textureGrad", TYPE_VEC4, { TYPE_SAMPLER2DARRAY, TYPE_VEC3, TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, true }, + { "textureGrad", TYPE_IVEC4, { TYPE_ISAMPLER2DARRAY, TYPE_VEC3, TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, true }, + { "textureGrad", TYPE_UVEC4, { TYPE_USAMPLER2DARRAY, TYPE_VEC3, TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, true }, + { "textureGrad", TYPE_VEC4, { TYPE_SAMPLER3D, TYPE_VEC3, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, true }, + { "textureGrad", TYPE_IVEC4, { TYPE_ISAMPLER3D, TYPE_VEC3, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, true }, + { "textureGrad", TYPE_UVEC4, { TYPE_USAMPLER3D, TYPE_VEC3, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, true }, + { "textureGrad", TYPE_VEC4, { TYPE_SAMPLERCUBE, TYPE_VEC3, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, true }, + + { "dFdx", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, + { "dFdx", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, true }, + { "dFdx", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, true }, + { "dFdx", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, true }, + + { "dFdy", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, + { "dFdy", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, true }, + { "dFdy", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, true }, + { "dFdy", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, true }, + + { "fwidth", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, + { "fwidth", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, true }, + { "fwidth", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, true }, + { "fwidth", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, true }, + + //sub-functions + + //array + { "length", TYPE_INT, { TYPE_VOID }, TAG_ARRAY, true }, + + { NULL, TYPE_VOID, { TYPE_VOID }, TAG_GLOBAL, false } + +}; + +const ShaderLanguage::BuiltinFuncOutArgs ShaderLanguage::builtin_func_out_args[] = { + //constructors + { "modf", 1 }, + { NULL, 0 } +}; + +bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types, OperatorNode *p_func, DataType *r_ret_type, StringName *r_ret_type_str) { + + ERR_FAIL_COND_V(p_func->op != OP_CALL && p_func->op != OP_CONSTRUCT, false); + + Vector<DataType> args; + Vector<StringName> args2; + + ERR_FAIL_COND_V(p_func->arguments[0]->type != Node::TYPE_VARIABLE, false); + + StringName name = static_cast<VariableNode *>(p_func->arguments[0])->name.operator String(); + + for (int i = 1; i < p_func->arguments.size(); i++) { + args.push_back(p_func->arguments[i]->get_datatype()); + args2.push_back(p_func->arguments[i]->get_datatype_name()); + } + + int argcount = args.size(); + + bool failed_builtin = false; + bool unsupported_builtin = false; + int builtin_idx = 0; + + if (argcount <= 4) { + // test builtins + int idx = 0; + + while (builtin_func_defs[idx].name) { + + if (completion_class != builtin_func_defs[idx].tag) { + idx++; + continue; + } + + if (name == builtin_func_defs[idx].name) { + + failed_builtin = true; + bool fail = false; + for (int i = 0; i < argcount; i++) { + + if (get_scalar_type(args[i]) == args[i] && p_func->arguments[i + 1]->type == Node::TYPE_CONSTANT && convert_constant(static_cast<ConstantNode *>(p_func->arguments[i + 1]), builtin_func_defs[idx].args[i])) { + //all good, but needs implicit conversion later + } else if (args[i] != builtin_func_defs[idx].args[i]) { + fail = true; + break; + } + } + + if (!fail) { + if (RenderingServer::get_singleton()->is_low_end()) { + if (builtin_func_defs[idx].high_end) { + fail = true; + unsupported_builtin = true; + builtin_idx = idx; + } + } + } + + if (!fail && argcount < 4 && builtin_func_defs[idx].args[argcount] != TYPE_VOID) + fail = true; //make sure the number of arguments matches + + if (!fail) { + + //make sure its not an out argument used in the wrong way + int outarg_idx = 0; + while (builtin_func_out_args[outarg_idx].name) { + + if (String(name) == builtin_func_out_args[outarg_idx].name) { + int arg_idx = builtin_func_out_args[outarg_idx].argument; + + if (arg_idx < argcount) { + + if (p_func->arguments[arg_idx + 1]->type != Node::TYPE_VARIABLE && p_func->arguments[arg_idx + 1]->type != Node::TYPE_MEMBER && p_func->arguments[arg_idx + 1]->type != Node::TYPE_ARRAY) { + _set_error("Argument " + itos(arg_idx + 1) + " of function '" + String(name) + "' is not a variable, array or member."); + return false; + } + + if (p_func->arguments[arg_idx + 1]->type == Node::TYPE_ARRAY) { + ArrayNode *mn = static_cast<ArrayNode *>(p_func->arguments[arg_idx + 1]); + if (mn->is_const) { + fail = true; + } + } else if (p_func->arguments[arg_idx + 1]->type == Node::TYPE_MEMBER) { + MemberNode *mn = static_cast<MemberNode *>(p_func->arguments[arg_idx + 1]); + if (mn->basetype_const) { + fail = true; + } + } else { // TYPE_VARIABLE + VariableNode *vn = static_cast<VariableNode *>(p_func->arguments[arg_idx + 1]); + if (vn->is_const) { + fail = true; + } else { + StringName varname = vn->name; + if (shader->uniforms.has(varname)) { + fail = true; + } else { + if (p_builtin_types.has(varname)) { + BuiltInInfo info = p_builtin_types[varname]; + if (info.constant) { + fail = true; + } + } + } + } + } + if (fail) { + _set_error(vformat("Constant value cannot be passed for '%s' parameter!", "out")); + return false; + } + + StringName var_name; + if (p_func->arguments[arg_idx + 1]->type == Node::TYPE_ARRAY) { + var_name = static_cast<const ArrayNode *>(p_func->arguments[arg_idx + 1])->name; + } else if (p_func->arguments[arg_idx + 1]->type == Node::TYPE_MEMBER) { + Node *n = static_cast<const MemberNode *>(p_func->arguments[arg_idx + 1])->owner; + while (n->type == Node::TYPE_MEMBER) { + n = static_cast<const MemberNode *>(n)->owner; + } + if (n->type != Node::TYPE_VARIABLE && n->type != Node::TYPE_ARRAY) { + _set_error("Argument " + itos(arg_idx + 1) + " of function '" + String(name) + "' is not a variable, array or member."); + return false; + } + if (n->type == Node::TYPE_VARIABLE) { + var_name = static_cast<const VariableNode *>(n)->name; + } else { // TYPE_ARRAY + var_name = static_cast<const ArrayNode *>(n)->name; + } + } else { // TYPE_VARIABLE + var_name = static_cast<const VariableNode *>(p_func->arguments[arg_idx + 1])->name; + } + const BlockNode *b = p_block; + bool valid = false; + while (b) { + if (b->variables.has(var_name) || p_builtin_types.has(var_name)) { + valid = true; + break; + } + if (b->parent_function) { + for (int i = 0; i < b->parent_function->arguments.size(); i++) { + if (b->parent_function->arguments[i].name == var_name) { + valid = true; + break; + } + } + } + b = b->parent_block; + } + + if (!valid) { + _set_error("Argument " + itos(arg_idx + 1) + " of function '" + String(name) + "' can only take a local variable, array or member."); + return false; + } + } + } + + outarg_idx++; + } + //implicitly convert values if possible + for (int i = 0; i < argcount; i++) { + + if (get_scalar_type(args[i]) != args[i] || args[i] == builtin_func_defs[idx].args[i] || p_func->arguments[i + 1]->type != Node::TYPE_CONSTANT) { + //can't do implicit conversion here + continue; + } + + //this is an implicit conversion + ConstantNode *constant = static_cast<ConstantNode *>(p_func->arguments[i + 1]); + ConstantNode *conversion = alloc_node<ConstantNode>(); + + conversion->datatype = builtin_func_defs[idx].args[i]; + conversion->values.resize(1); + + convert_constant(constant, builtin_func_defs[idx].args[i], conversion->values.ptrw()); + p_func->arguments.write[i + 1] = conversion; + } + + if (r_ret_type) + *r_ret_type = builtin_func_defs[idx].rettype; + + return true; + } + } + + idx++; + } + } + + if (unsupported_builtin) { + + String arglist = ""; + for (int i = 0; i < argcount; i++) { + if (i > 0) { + arglist += ", "; + } + arglist += get_datatype_name(builtin_func_defs[builtin_idx].args[i]); + } + + String err = "Built-in function \"" + String(name) + "(" + arglist + ")\" is supported only on high-end platform!"; + _set_error(err); + return false; + } + + if (failed_builtin) { + String err = "Invalid arguments for built-in function: " + String(name) + "("; + for (int i = 0; i < argcount; i++) { + if (i > 0) + err += ","; + + if (p_func->arguments[i + 1]->type == Node::TYPE_CONSTANT && p_func->arguments[i + 1]->get_datatype() == TYPE_INT && static_cast<ConstantNode *>(p_func->arguments[i + 1])->values[0].sint < 0) { + err += "-"; + } + err += get_datatype_name(args[i]); + } + err += ")"; + _set_error(err); + return false; + } + + // try existing functions.. + + StringName exclude_function; + BlockNode *block = p_block; + + while (block) { + + if (block->parent_function) { + exclude_function = block->parent_function->name; + } + block = block->parent_block; + } + + if (name == exclude_function) { + _set_error("Recursion is not allowed"); + return false; + } + + for (int i = 0; i < shader->functions.size(); i++) { + + if (name != shader->functions[i].name) + continue; + + if (!shader->functions[i].callable) { + _set_error("Function '" + String(name) + " can't be called from source code."); + return false; + } + + FunctionNode *pfunc = shader->functions[i].function; + + if (pfunc->arguments.size() != args.size()) + continue; + + bool fail = false; + + for (int j = 0; j < args.size(); j++) { + if (args[j] == TYPE_STRUCT && args2[j] != pfunc->arguments[j].type_str) { + fail = true; + break; + } + if (get_scalar_type(args[j]) == args[j] && p_func->arguments[j + 1]->type == Node::TYPE_CONSTANT && convert_constant(static_cast<ConstantNode *>(p_func->arguments[j + 1]), pfunc->arguments[j].type)) { + //all good, but it needs implicit conversion later + } else if (args[j] != pfunc->arguments[j].type) { + fail = true; + break; + } + } + + if (!fail) { + + //implicitly convert values if possible + for (int k = 0; k < args.size(); k++) { + + if (get_scalar_type(args[k]) != args[k] || args[k] == pfunc->arguments[k].type || p_func->arguments[k + 1]->type != Node::TYPE_CONSTANT) { + //can't do implicit conversion here + continue; + } + + //this is an implicit conversion + ConstantNode *constant = static_cast<ConstantNode *>(p_func->arguments[k + 1]); + ConstantNode *conversion = alloc_node<ConstantNode>(); + + conversion->datatype = pfunc->arguments[k].type; + conversion->values.resize(1); + + convert_constant(constant, pfunc->arguments[k].type, conversion->values.ptrw()); + p_func->arguments.write[k + 1] = conversion; + } + + if (r_ret_type) { + *r_ret_type = pfunc->return_type; + if (pfunc->return_type == TYPE_STRUCT) { + *r_ret_type_str = pfunc->return_struct_name; + } + } + + return true; + } + } + + return false; +} + +bool ShaderLanguage::_compare_datatypes_in_nodes(Node *a, Node *b) const { + if (a->get_datatype() != b->get_datatype()) { + return false; + } + if (a->get_datatype() == TYPE_STRUCT || b->get_datatype() == TYPE_STRUCT) { + if (a->get_datatype_name() != b->get_datatype_name()) { + return false; + } + } + return true; +} + +bool ShaderLanguage::_parse_function_arguments(BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types, OperatorNode *p_func, int *r_complete_arg) { + + TkPos pos = _get_tkpos(); + Token tk = _get_token(); + + if (tk.type == TK_PARENTHESIS_CLOSE) { + return true; + } + + _set_tkpos(pos); + + while (true) { + + if (r_complete_arg) { + pos = _get_tkpos(); + tk = _get_token(); + + if (tk.type == TK_CURSOR) { + + *r_complete_arg = p_func->arguments.size() - 1; + } else { + + _set_tkpos(pos); + } + } + + Node *arg = _parse_and_reduce_expression(p_block, p_builtin_types); + + if (!arg) { + + return false; + } + + p_func->arguments.push_back(arg); + + tk = _get_token(); + + if (tk.type == TK_PARENTHESIS_CLOSE) { + + return true; + } else if (tk.type != TK_COMMA) { + // something is broken + _set_error("Expected ',' or ')' after argument"); + return false; + } + } + + return true; +} + +bool ShaderLanguage::is_token_operator(TokenType p_type) { + + return (p_type == TK_OP_EQUAL || + p_type == TK_OP_NOT_EQUAL || + p_type == TK_OP_LESS || + p_type == TK_OP_LESS_EQUAL || + p_type == TK_OP_GREATER || + p_type == TK_OP_GREATER_EQUAL || + p_type == TK_OP_AND || + p_type == TK_OP_OR || + p_type == TK_OP_NOT || + p_type == TK_OP_ADD || + p_type == TK_OP_SUB || + p_type == TK_OP_MUL || + p_type == TK_OP_DIV || + p_type == TK_OP_MOD || + p_type == TK_OP_SHIFT_LEFT || + p_type == TK_OP_SHIFT_RIGHT || + p_type == TK_OP_ASSIGN || + p_type == TK_OP_ASSIGN_ADD || + p_type == TK_OP_ASSIGN_SUB || + p_type == TK_OP_ASSIGN_MUL || + p_type == TK_OP_ASSIGN_DIV || + p_type == TK_OP_ASSIGN_MOD || + p_type == TK_OP_ASSIGN_SHIFT_LEFT || + p_type == TK_OP_ASSIGN_SHIFT_RIGHT || + p_type == TK_OP_ASSIGN_BIT_AND || + p_type == TK_OP_ASSIGN_BIT_OR || + p_type == TK_OP_ASSIGN_BIT_XOR || + p_type == TK_OP_BIT_AND || + p_type == TK_OP_BIT_OR || + p_type == TK_OP_BIT_XOR || + p_type == TK_OP_BIT_INVERT || + p_type == TK_OP_INCREMENT || + p_type == TK_OP_DECREMENT || + p_type == TK_QUESTION || + p_type == TK_COLON); +} + +bool ShaderLanguage::convert_constant(ConstantNode *p_constant, DataType p_to_type, ConstantNode::Value *p_value) { + + if (p_constant->datatype == p_to_type) { + if (p_value) { + for (int i = 0; i < p_constant->values.size(); i++) { + p_value[i] = p_constant->values[i]; + } + } + return true; + } else if (p_constant->datatype == TYPE_INT && p_to_type == TYPE_FLOAT) { + + if (p_value) { + p_value->real = p_constant->values[0].sint; + } + return true; + } else if (p_constant->datatype == TYPE_UINT && p_to_type == TYPE_FLOAT) { + + if (p_value) { + p_value->real = p_constant->values[0].uint; + } + return true; + } else if (p_constant->datatype == TYPE_INT && p_to_type == TYPE_UINT) { + if (p_constant->values[0].sint < 0) { + return false; + } + if (p_value) { + p_value->uint = p_constant->values[0].sint; + } + return true; + } else if (p_constant->datatype == TYPE_UINT && p_to_type == TYPE_INT) { + + if (p_constant->values[0].uint > 0x7FFFFFFF) { + return false; + } + if (p_value) { + p_value->sint = p_constant->values[0].uint; + } + return true; + } else + return false; +} + +bool ShaderLanguage::is_scalar_type(DataType p_type) { + + return p_type == TYPE_BOOL || p_type == TYPE_INT || p_type == TYPE_UINT || p_type == TYPE_FLOAT; +} + +bool ShaderLanguage::is_sampler_type(DataType p_type) { + + return p_type == TYPE_SAMPLER2D || + p_type == TYPE_ISAMPLER2D || + p_type == TYPE_USAMPLER2D || + p_type == TYPE_SAMPLER2DARRAY || + p_type == TYPE_ISAMPLER2DARRAY || + p_type == TYPE_USAMPLER2DARRAY || + p_type == TYPE_SAMPLER3D || + p_type == TYPE_ISAMPLER3D || + p_type == TYPE_USAMPLER3D || + p_type == TYPE_SAMPLERCUBE; +} + +Variant ShaderLanguage::constant_value_to_variant(const Vector<ShaderLanguage::ConstantNode::Value> &p_value, DataType p_type, ShaderLanguage::ShaderNode::Uniform::Hint p_hint) { + if (p_value.size() > 0) { + Variant value; + switch (p_type) { + case ShaderLanguage::TYPE_BOOL: + value = Variant(p_value[0].boolean); + break; + case ShaderLanguage::TYPE_BVEC2: + case ShaderLanguage::TYPE_BVEC3: + case ShaderLanguage::TYPE_BVEC4: + case ShaderLanguage::TYPE_INT: + value = Variant(p_value[0].sint); + break; + case ShaderLanguage::TYPE_IVEC2: + value = Variant(Vector2(p_value[0].sint, p_value[1].sint)); + break; + case ShaderLanguage::TYPE_IVEC3: + value = Variant(Vector3(p_value[0].sint, p_value[1].sint, p_value[2].sint)); + break; + case ShaderLanguage::TYPE_IVEC4: + value = Variant(Plane(p_value[0].sint, p_value[1].sint, p_value[2].sint, p_value[3].sint)); + break; + case ShaderLanguage::TYPE_UINT: + value = Variant(p_value[0].uint); + break; + case ShaderLanguage::TYPE_UVEC2: + value = Variant(Vector2(p_value[0].uint, p_value[1].uint)); + break; + case ShaderLanguage::TYPE_UVEC3: + value = Variant(Vector3(p_value[0].uint, p_value[1].uint, p_value[2].uint)); + break; + case ShaderLanguage::TYPE_UVEC4: + value = Variant(Plane(p_value[0].uint, p_value[1].uint, p_value[2].uint, p_value[3].uint)); + break; + case ShaderLanguage::TYPE_FLOAT: + value = Variant(p_value[0].real); + break; + case ShaderLanguage::TYPE_VEC2: + value = Variant(Vector2(p_value[0].real, p_value[1].real)); + break; + case ShaderLanguage::TYPE_VEC3: + value = Variant(Vector3(p_value[0].real, p_value[1].real, p_value[2].real)); + break; + case ShaderLanguage::TYPE_VEC4: + if (p_hint == ShaderLanguage::ShaderNode::Uniform::HINT_COLOR) { + value = Variant(Color(p_value[0].real, p_value[1].real, p_value[2].real, p_value[3].real)); + } else { + value = Variant(Plane(p_value[0].real, p_value[1].real, p_value[2].real, p_value[3].real)); + } + break; + case ShaderLanguage::TYPE_MAT2: + value = Variant(Transform2D(p_value[0].real, p_value[2].real, p_value[1].real, p_value[3].real, 0.0, 0.0)); + break; + case ShaderLanguage::TYPE_MAT3: { + Basis p; + p[0][0] = p_value[0].real; + p[0][1] = p_value[1].real; + p[0][2] = p_value[2].real; + p[1][0] = p_value[3].real; + p[1][1] = p_value[4].real; + p[1][2] = p_value[5].real; + p[2][0] = p_value[6].real; + p[2][1] = p_value[7].real; + p[2][2] = p_value[8].real; + value = Variant(p); + break; + } + case ShaderLanguage::TYPE_MAT4: { + Basis p; + p[0][0] = p_value[0].real; + p[0][1] = p_value[1].real; + p[0][2] = p_value[2].real; + p[1][0] = p_value[4].real; + p[1][1] = p_value[5].real; + p[1][2] = p_value[6].real; + p[2][0] = p_value[8].real; + p[2][1] = p_value[9].real; + p[2][2] = p_value[10].real; + Transform t = Transform(p, Vector3(p_value[3].real, p_value[7].real, p_value[11].real)); + value = Variant(t); + break; + } + case ShaderLanguage::TYPE_ISAMPLER2DARRAY: + case ShaderLanguage::TYPE_ISAMPLER2D: + case ShaderLanguage::TYPE_ISAMPLER3D: + case ShaderLanguage::TYPE_SAMPLER2DARRAY: + case ShaderLanguage::TYPE_SAMPLER2D: + case ShaderLanguage::TYPE_SAMPLER3D: + case ShaderLanguage::TYPE_USAMPLER2DARRAY: + case ShaderLanguage::TYPE_USAMPLER2D: + case ShaderLanguage::TYPE_USAMPLER3D: + case ShaderLanguage::TYPE_SAMPLERCUBE: { + // Texture types, likely not relevant here. + break; + } + case ShaderLanguage::TYPE_STRUCT: + break; + case ShaderLanguage::TYPE_VOID: + break; + } + return value; + } + return Variant(); +} + +PropertyInfo ShaderLanguage::uniform_to_property_info(const ShaderNode::Uniform &p_uniform) { + PropertyInfo pi; + switch (p_uniform.type) { + case ShaderLanguage::TYPE_VOID: pi.type = Variant::NIL; break; + case ShaderLanguage::TYPE_BOOL: pi.type = Variant::BOOL; break; + case ShaderLanguage::TYPE_BVEC2: + pi.type = Variant::INT; + pi.hint = PROPERTY_HINT_FLAGS; + pi.hint_string = "x,y"; + break; + case ShaderLanguage::TYPE_BVEC3: + pi.type = Variant::INT; + pi.hint = PROPERTY_HINT_FLAGS; + pi.hint_string = "x,y,z"; + break; + case ShaderLanguage::TYPE_BVEC4: + pi.type = Variant::INT; + pi.hint = PROPERTY_HINT_FLAGS; + pi.hint_string = "x,y,z,w"; + break; + case ShaderLanguage::TYPE_UINT: + case ShaderLanguage::TYPE_INT: { + pi.type = Variant::INT; + if (p_uniform.hint == ShaderLanguage::ShaderNode::Uniform::HINT_RANGE) { + pi.hint = PROPERTY_HINT_RANGE; + pi.hint_string = rtos(p_uniform.hint_range[0]) + "," + rtos(p_uniform.hint_range[1]) + "," + rtos(p_uniform.hint_range[2]); + } + + } break; + case ShaderLanguage::TYPE_IVEC2: + case ShaderLanguage::TYPE_IVEC3: + case ShaderLanguage::TYPE_IVEC4: + case ShaderLanguage::TYPE_UVEC2: + case ShaderLanguage::TYPE_UVEC3: + case ShaderLanguage::TYPE_UVEC4: { + + pi.type = Variant::PACKED_INT32_ARRAY; + } break; + case ShaderLanguage::TYPE_FLOAT: { + pi.type = Variant::FLOAT; + if (p_uniform.hint == ShaderLanguage::ShaderNode::Uniform::HINT_RANGE) { + pi.hint = PROPERTY_HINT_RANGE; + pi.hint_string = rtos(p_uniform.hint_range[0]) + "," + rtos(p_uniform.hint_range[1]) + "," + rtos(p_uniform.hint_range[2]); + } + + } break; + case ShaderLanguage::TYPE_VEC2: pi.type = Variant::VECTOR2; break; + case ShaderLanguage::TYPE_VEC3: pi.type = Variant::VECTOR3; break; + case ShaderLanguage::TYPE_VEC4: { + if (p_uniform.hint == ShaderLanguage::ShaderNode::Uniform::HINT_COLOR) { + pi.type = Variant::COLOR; + } else { + pi.type = Variant::PLANE; + } + } break; + case ShaderLanguage::TYPE_MAT2: pi.type = Variant::TRANSFORM2D; break; + case ShaderLanguage::TYPE_MAT3: pi.type = Variant::BASIS; break; + case ShaderLanguage::TYPE_MAT4: pi.type = Variant::TRANSFORM; break; + case ShaderLanguage::TYPE_SAMPLER2D: + case ShaderLanguage::TYPE_ISAMPLER2D: + case ShaderLanguage::TYPE_USAMPLER2D: { + + pi.type = Variant::OBJECT; + pi.hint = PROPERTY_HINT_RESOURCE_TYPE; + pi.hint_string = "Texture2D"; + } break; + case ShaderLanguage::TYPE_SAMPLER2DARRAY: + case ShaderLanguage::TYPE_ISAMPLER2DARRAY: + case ShaderLanguage::TYPE_USAMPLER2DARRAY: { + + pi.type = Variant::OBJECT; + pi.hint = PROPERTY_HINT_RESOURCE_TYPE; + pi.hint_string = "TextureArray"; + } break; + case ShaderLanguage::TYPE_SAMPLER3D: + case ShaderLanguage::TYPE_ISAMPLER3D: + case ShaderLanguage::TYPE_USAMPLER3D: { + pi.type = Variant::OBJECT; + pi.hint = PROPERTY_HINT_RESOURCE_TYPE; + pi.hint_string = "Texture3D"; + } break; + case ShaderLanguage::TYPE_SAMPLERCUBE: { + + pi.type = Variant::OBJECT; + pi.hint = PROPERTY_HINT_RESOURCE_TYPE; + pi.hint_string = "CubeMap"; + } break; + case ShaderLanguage::TYPE_STRUCT: { + // FIXME: Implement this. + } break; + } + return pi; +} + +uint32_t ShaderLanguage::get_type_size(DataType p_type) { + switch (p_type) { + case TYPE_VOID: + return 0; + case TYPE_BOOL: + case TYPE_INT: + case TYPE_UINT: + case TYPE_FLOAT: + return 4; + case TYPE_BVEC2: + case TYPE_IVEC2: + case TYPE_UVEC2: + case TYPE_VEC2: + return 8; + case TYPE_BVEC3: + case TYPE_IVEC3: + case TYPE_UVEC3: + case TYPE_VEC3: + return 12; + case TYPE_BVEC4: + case TYPE_IVEC4: + case TYPE_UVEC4: + case TYPE_VEC4: + return 16; + case TYPE_MAT2: + return 8; + case TYPE_MAT3: + return 12; + case TYPE_MAT4: + return 16; + case TYPE_SAMPLER2D: + case TYPE_ISAMPLER2D: + case TYPE_USAMPLER2D: + case TYPE_SAMPLER2DARRAY: + case TYPE_ISAMPLER2DARRAY: + case TYPE_USAMPLER2DARRAY: + case TYPE_SAMPLER3D: + case TYPE_ISAMPLER3D: + case TYPE_USAMPLER3D: + case TYPE_SAMPLERCUBE: + return 4; //not really, but useful for indices + case TYPE_STRUCT: + // FIXME: Implement. + return 0; + } + return 0; +} + +void ShaderLanguage::get_keyword_list(List<String> *r_keywords) { + + Set<String> kws; + + int idx = 0; + + while (keyword_list[idx].text) { + + kws.insert(keyword_list[idx].text); + idx++; + } + + idx = 0; + + while (builtin_func_defs[idx].name) { + + kws.insert(builtin_func_defs[idx].name); + + idx++; + } + + for (Set<String>::Element *E = kws.front(); E; E = E->next()) { + r_keywords->push_back(E->get()); + } +} + +void ShaderLanguage::get_builtin_funcs(List<String> *r_keywords) { + + Set<String> kws; + + int idx = 0; + + while (builtin_func_defs[idx].name) { + + kws.insert(builtin_func_defs[idx].name); + + idx++; + } + + for (Set<String>::Element *E = kws.front(); E; E = E->next()) { + r_keywords->push_back(E->get()); + } +} + +ShaderLanguage::DataType ShaderLanguage::get_scalar_type(DataType p_type) { + + static const DataType scalar_types[] = { + TYPE_VOID, + TYPE_BOOL, + TYPE_BOOL, + TYPE_BOOL, + TYPE_BOOL, + TYPE_INT, + TYPE_INT, + TYPE_INT, + TYPE_INT, + TYPE_UINT, + TYPE_UINT, + TYPE_UINT, + TYPE_UINT, + TYPE_FLOAT, + TYPE_FLOAT, + TYPE_FLOAT, + TYPE_FLOAT, + TYPE_FLOAT, + TYPE_FLOAT, + TYPE_FLOAT, + TYPE_FLOAT, + TYPE_INT, + TYPE_UINT, + TYPE_FLOAT, + }; + + return scalar_types[p_type]; +} + +int ShaderLanguage::get_cardinality(DataType p_type) { + static const int cardinality_table[] = { + 0, + 1, + 2, + 3, + 4, + 1, + 2, + 3, + 4, + 1, + 2, + 3, + 4, + 1, + 2, + 3, + 4, + 4, + 9, + 16, + 1, + 1, + 1, + 1, + }; + + return cardinality_table[p_type]; +} + +bool ShaderLanguage::_get_completable_identifier(BlockNode *p_block, CompletionType p_type, StringName &identifier) { + + identifier = StringName(); + + TkPos pos = { 0, 0 }; + + Token tk = _get_token(); + + if (tk.type == TK_IDENTIFIER) { + identifier = tk.text; + pos = _get_tkpos(); + tk = _get_token(); + } + + if (tk.type == TK_CURSOR) { + + completion_type = p_type; + completion_line = tk_line; + completion_block = p_block; + + pos = _get_tkpos(); + tk = _get_token(); + + if (tk.type == TK_IDENTIFIER) { + identifier = identifier.operator String() + tk.text.operator String(); + } else { + _set_tkpos(pos); + } + return true; + } else if (identifier != StringName()) { + _set_tkpos(pos); + } + + return false; +} + +bool ShaderLanguage::_is_operator_assign(Operator p_op) const { + switch (p_op) { + case OP_ASSIGN: + case OP_ASSIGN_ADD: + case OP_ASSIGN_SUB: + case OP_ASSIGN_MUL: + case OP_ASSIGN_DIV: + case OP_ASSIGN_MOD: + case OP_ASSIGN_SHIFT_LEFT: + case OP_ASSIGN_SHIFT_RIGHT: + case OP_ASSIGN_BIT_AND: + case OP_ASSIGN_BIT_OR: + case OP_ASSIGN_BIT_XOR: + return true; + default: + return false; + } + + return false; +} + +bool ShaderLanguage::_validate_assign(Node *p_node, const Map<StringName, BuiltInInfo> &p_builtin_types, String *r_message) { + + if (p_node->type == Node::TYPE_OPERATOR) { + + OperatorNode *op = static_cast<OperatorNode *>(p_node); + + if (op->op == OP_INDEX) { + return _validate_assign(op->arguments[0], p_builtin_types, r_message); + + } else if (_is_operator_assign(op->op)) { + //chained assignment + return _validate_assign(op->arguments[1], p_builtin_types, r_message); + + } else if (op->op == OP_CALL) { + if (r_message) + *r_message = RTR("Assignment to function."); + return false; + } + + } else if (p_node->type == Node::TYPE_MEMBER) { + + MemberNode *member = static_cast<MemberNode *>(p_node); + + if (member->has_swizzling_duplicates) { + if (r_message) + *r_message = RTR("Swizzling assignment contains duplicates."); + return false; + } + + return _validate_assign(member->owner, p_builtin_types, r_message); + + } else if (p_node->type == Node::TYPE_VARIABLE) { + + VariableNode *var = static_cast<VariableNode *>(p_node); + + if (shader->uniforms.has(var->name)) { + if (r_message) + *r_message = RTR("Assignment to uniform."); + return false; + } + + if (shader->varyings.has(var->name) && current_function != String("vertex")) { + if (r_message) + *r_message = RTR("Varyings can only be assigned in vertex function."); + return false; + } + + if (shader->constants.has(var->name) || var->is_const) { + if (r_message) + *r_message = RTR("Constants cannot be modified."); + return false; + } + + if (!(p_builtin_types.has(var->name) && p_builtin_types[var->name].constant)) { + return true; + } + } else if (p_node->type == Node::TYPE_ARRAY) { + + ArrayNode *arr = static_cast<ArrayNode *>(p_node); + + if (arr->is_const) { + if (r_message) + *r_message = RTR("Constants cannot be modified."); + return false; + } + + if (shader->varyings.has(arr->name) && current_function != String("vertex")) { + if (r_message) + *r_message = RTR("Varyings can only be assigned in vertex function."); + return false; + } + + return true; + } + + if (r_message) + *r_message = "Assignment to constant expression."; + return false; +} + +bool ShaderLanguage::_propagate_function_call_sampler_uniform_settings(StringName p_name, int p_argument, TextureFilter p_filter, TextureRepeat p_repeat) { + for (int i = 0; shader->functions.size(); i++) { + if (shader->functions[i].name == p_name) { + + ERR_FAIL_INDEX_V(p_argument, shader->functions[i].function->arguments.size(), false); + FunctionNode::Argument *arg = &shader->functions[i].function->arguments.write[p_argument]; + if (arg->tex_builtin_check) { + _set_error("Sampler argument #" + itos(p_argument) + " of function '" + String(p_name) + "' called more than once using both built-ins and uniform textures, this is not supported (use either one or the other)."); + return false; + } else if (arg->tex_argument_check) { + //was checked, verify that filter and repeat are the same + if (arg->tex_argument_filter == p_filter && arg->tex_argument_repeat == p_repeat) { + return true; + } else { + + _set_error("Sampler argument #" + itos(p_argument) + " of function '" + String(p_name) + "' called more than once using textures that differ in either filter or repeat setting."); + return false; + } + } else { + + arg->tex_argument_check = true; + arg->tex_argument_filter = p_filter; + arg->tex_argument_repeat = p_repeat; + for (Map<StringName, Set<int>>::Element *E = arg->tex_argument_connect.front(); E; E = E->next()) { + for (Set<int>::Element *F = E->get().front(); F; F = F->next()) { + if (!_propagate_function_call_sampler_uniform_settings(E->key(), F->get(), p_filter, p_repeat)) { + return false; + } + } + } + return true; + } + } + } + ERR_FAIL_V(false); //bug? function not found +} +bool ShaderLanguage::_propagate_function_call_sampler_builtin_reference(StringName p_name, int p_argument, const StringName &p_builtin) { + for (int i = 0; shader->functions.size(); i++) { + if (shader->functions[i].name == p_name) { + + ERR_FAIL_INDEX_V(p_argument, shader->functions[i].function->arguments.size(), false); + FunctionNode::Argument *arg = &shader->functions[i].function->arguments.write[p_argument]; + if (arg->tex_argument_check) { + _set_error("Sampler argument #" + itos(p_argument) + " of function '" + String(p_name) + "' called more than once using both built-ins and uniform textures, this is not supported (use either one or the other)."); + return false; + } else if (arg->tex_builtin_check) { + //was checked, verify that the built-in is the same + if (arg->tex_builtin == p_builtin) { + return true; + } else { + _set_error("Sampler argument #" + itos(p_argument) + " of function '" + String(p_name) + "' called more than once using different built-ins. Only calling with the same built-in is supported."); + return false; + } + } else { + + arg->tex_builtin_check = true; + arg->tex_builtin = p_builtin; + + for (Map<StringName, Set<int>>::Element *E = arg->tex_argument_connect.front(); E; E = E->next()) { + for (Set<int>::Element *F = E->get().front(); F; F = F->next()) { + if (!_propagate_function_call_sampler_builtin_reference(E->key(), F->get(), p_builtin)) { + return false; + } + } + } + return true; + } + } + } + ERR_FAIL_V(false); //bug? function not found +} + +ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types) { + + Vector<Expression> expression; + + //Vector<TokenType> operators; + + while (true) { + + Node *expr = NULL; + TkPos prepos = _get_tkpos(); + Token tk = _get_token(); + TkPos pos = _get_tkpos(); + + bool is_const = false; + + if (tk.type == TK_PARENTHESIS_OPEN) { + //handle subexpression + + expr = _parse_and_reduce_expression(p_block, p_builtin_types); + if (!expr) + return NULL; + + tk = _get_token(); + + if (tk.type != TK_PARENTHESIS_CLOSE) { + + _set_error("Expected ')' in expression"); + return NULL; + } + + } else if (tk.type == TK_REAL_CONSTANT) { + + ConstantNode *constant = alloc_node<ConstantNode>(); + ConstantNode::Value v; + v.real = tk.constant; + constant->values.push_back(v); + constant->datatype = TYPE_FLOAT; + expr = constant; + + } else if (tk.type == TK_INT_CONSTANT) { + + ConstantNode *constant = alloc_node<ConstantNode>(); + ConstantNode::Value v; + v.sint = tk.constant; + constant->values.push_back(v); + constant->datatype = TYPE_INT; + expr = constant; + + } else if (tk.type == TK_TRUE) { + + //handle true constant + ConstantNode *constant = alloc_node<ConstantNode>(); + ConstantNode::Value v; + v.boolean = true; + constant->values.push_back(v); + constant->datatype = TYPE_BOOL; + expr = constant; + + } else if (tk.type == TK_FALSE) { + + //handle false constant + ConstantNode *constant = alloc_node<ConstantNode>(); + ConstantNode::Value v; + v.boolean = false; + constant->values.push_back(v); + constant->datatype = TYPE_BOOL; + expr = constant; + + } else if (tk.type == TK_TYPE_VOID) { + + //make sure void is not used in expression + _set_error("Void value not allowed in Expression"); + return NULL; + } else if (is_token_nonvoid_datatype(tk.type)) { + //basic type constructor + + OperatorNode *func = alloc_node<OperatorNode>(); + func->op = OP_CONSTRUCT; + + if (is_token_precision(tk.type)) { + + func->return_precision_cache = get_token_precision(tk.type); + tk = _get_token(); + } + + VariableNode *funcname = alloc_node<VariableNode>(); + funcname->name = get_datatype_name(get_token_datatype(tk.type)); + func->arguments.push_back(funcname); + + tk = _get_token(); + if (tk.type != TK_PARENTHESIS_OPEN) { + _set_error("Expected '(' after type name"); + return NULL; + } + + int carg = -1; + + bool ok = _parse_function_arguments(p_block, p_builtin_types, func, &carg); + + if (carg >= 0) { + completion_type = COMPLETION_CALL_ARGUMENTS; + completion_line = tk_line; + completion_block = p_block; + completion_function = funcname->name; + completion_argument = carg; + } + + if (!ok) + return NULL; + + if (!_validate_function_call(p_block, p_builtin_types, func, &func->return_cache, &func->struct_name)) { + _set_error("No matching constructor found for: '" + String(funcname->name) + "'"); + return NULL; + } + + expr = _reduce_expression(p_block, func); + + } else if (tk.type == TK_IDENTIFIER) { + + _set_tkpos(prepos); + + StringName identifier; + + StructNode *pstruct = NULL; + bool struct_init = false; + + _get_completable_identifier(p_block, COMPLETION_IDENTIFIER, identifier); + + if (shader->structs.has(identifier)) { + pstruct = shader->structs[identifier].shader_struct; + struct_init = true; + } + + tk = _get_token(); + if (tk.type == TK_PARENTHESIS_OPEN) { + + if (struct_init) { //a struct constructor + + const StringName &name = identifier; + + OperatorNode *func = alloc_node<OperatorNode>(); + func->op = OP_STRUCT; + func->struct_name = name; + func->return_cache = TYPE_STRUCT; + VariableNode *funcname = alloc_node<VariableNode>(); + funcname->name = name; + func->arguments.push_back(funcname); + + for (int i = 0; i < pstruct->members.size(); i++) { + Node *nexpr; + + if (pstruct->members[i]->array_size != 0) { + + DataType type = pstruct->members[i]->get_datatype(); + String struct_name = pstruct->members[i]->struct_name; + int array_size = pstruct->members[i]->array_size; + + DataType type2; + String struct_name2 = ""; + int array_size2 = 0; + + bool auto_size = false; + + tk = _get_token(); + + if (tk.type == TK_CURLY_BRACKET_OPEN) { + auto_size = true; + } else { + + if (shader->structs.has(tk.text)) { + type2 = TYPE_STRUCT; + struct_name2 = tk.text; + } else { + if (!is_token_variable_datatype(tk.type)) { + _set_error("Invalid data type for array"); + return NULL; + } + type2 = get_token_datatype(tk.type); + } + + tk = _get_token(); + if (tk.type == TK_BRACKET_OPEN) { + TkPos pos2 = _get_tkpos(); + tk = _get_token(); + if (tk.type == TK_BRACKET_CLOSE) { + array_size2 = array_size; + tk = _get_token(); + } else { + _set_tkpos(pos2); + + Node *n = _parse_and_reduce_expression(p_block, p_builtin_types); + if (!n || n->type != Node::TYPE_CONSTANT || n->get_datatype() != TYPE_INT) { + _set_error("Expected single integer constant > 0"); + return NULL; + } + + ConstantNode *cnode = (ConstantNode *)n; + if (cnode->values.size() == 1) { + array_size2 = cnode->values[0].sint; + if (array_size2 <= 0) { + _set_error("Expected single integer constant > 0"); + return NULL; + } + } else { + _set_error("Expected single integer constant > 0"); + return NULL; + } + + tk = _get_token(); + if (tk.type != TK_BRACKET_CLOSE) { + _set_error("Expected ']'"); + return NULL; + } else { + tk = _get_token(); + } + } + } else { + _set_error("Expected '['"); + return NULL; + } + + if (type != type2 || struct_name != struct_name2 || array_size != array_size2) { + String error_str = "Cannot convert from '"; + if (type2 == TYPE_STRUCT) { + error_str += struct_name2; + } else { + error_str += get_datatype_name(type2); + } + error_str += "["; + error_str += itos(array_size2); + error_str += "]'"; + error_str += " to '"; + if (type == TYPE_STRUCT) { + error_str += struct_name; + } else { + error_str += get_datatype_name(type); + } + error_str += "["; + error_str += itos(array_size); + error_str += "]'"; + _set_error(error_str); + return NULL; + } + } + + ArrayConstructNode *an = alloc_node<ArrayConstructNode>(); + an->datatype = type; + an->struct_name = struct_name; + + if (tk.type == TK_PARENTHESIS_OPEN || auto_size) { // initialization + while (true) { + + Node *n = _parse_and_reduce_expression(p_block, p_builtin_types); + if (!n) { + return NULL; + } + + if (type != n->get_datatype() || struct_name != n->get_datatype_name()) { + _set_error("Invalid assignment of '" + (n->get_datatype() == TYPE_STRUCT ? n->get_datatype_name() : get_datatype_name(n->get_datatype())) + "' to '" + (type == TYPE_STRUCT ? struct_name : get_datatype_name(type)) + "'"); + return NULL; + } + + tk = _get_token(); + if (tk.type == TK_COMMA) { + an->initializer.push_back(n); + continue; + } else if (!auto_size && tk.type == TK_PARENTHESIS_CLOSE) { + an->initializer.push_back(n); + break; + } else if (auto_size && tk.type == TK_CURLY_BRACKET_CLOSE) { + an->initializer.push_back(n); + break; + } else { + if (auto_size) + _set_error("Expected '}' or ','"); + else + _set_error("Expected ')' or ','"); + return NULL; + } + } + if (an->initializer.size() != array_size) { + _set_error("Array size mismatch"); + return NULL; + } + } else { + _set_error("Expected array initialization!"); + return NULL; + } + + nexpr = an; + } else { + nexpr = _parse_and_reduce_expression(p_block, p_builtin_types); + if (!nexpr) { + return NULL; + } + Node *node = pstruct->members[i]; + if (!_compare_datatypes_in_nodes(pstruct->members[i], nexpr)) { + String type_name = nexpr->get_datatype() == TYPE_STRUCT ? nexpr->get_datatype_name() : get_datatype_name(nexpr->get_datatype()); + String type_name2 = node->get_datatype() == TYPE_STRUCT ? node->get_datatype_name() : get_datatype_name(node->get_datatype()); + _set_error("Invalid assignment of '" + type_name + "' to '" + type_name2 + "'"); + return NULL; + } + } + + if (i + 1 < pstruct->members.size()) { + tk = _get_token(); + if (tk.type != TK_COMMA) { + _set_error("Expected ','"); + return NULL; + } + } + func->arguments.push_back(nexpr); + } + tk = _get_token(); + if (tk.type != TK_PARENTHESIS_CLOSE) { + _set_error("Expected ')'"); + return NULL; + } + + expr = func; + + } else { //a function + + const StringName &name = identifier; + + OperatorNode *func = alloc_node<OperatorNode>(); + func->op = OP_CALL; + VariableNode *funcname = alloc_node<VariableNode>(); + funcname->name = name; + func->arguments.push_back(funcname); + + int carg = -1; + + bool ok = _parse_function_arguments(p_block, p_builtin_types, func, &carg); + + // Check if block has a variable with the same name as function to prevent shader crash. + ShaderLanguage::BlockNode *bnode = p_block; + while (bnode) { + if (bnode->variables.has(name)) { + _set_error("Expected function name"); + return NULL; + } + bnode = bnode->parent_block; + } + + //test if function was parsed first + int function_index = -1; + for (int i = 0; i < shader->functions.size(); i++) { + if (shader->functions[i].name == name) { + //add to current function as dependency + for (int j = 0; j < shader->functions.size(); j++) { + if (shader->functions[j].name == current_function) { + shader->functions.write[j].uses_function.insert(name); + break; + } + } + + //see if texture arguments must connect + function_index = i; + break; + } + } + + if (carg >= 0) { + completion_type = COMPLETION_CALL_ARGUMENTS; + completion_line = tk_line; + completion_block = p_block; + completion_function = funcname->name; + completion_argument = carg; + } + + if (!ok) + return NULL; + + if (!_validate_function_call(p_block, p_builtin_types, func, &func->return_cache, &func->struct_name)) { + _set_error("No matching function found for: '" + String(funcname->name) + "'"); + return NULL; + } + completion_class = TAG_GLOBAL; // reset sub-class + if (function_index >= 0) { + //connect texture arguments, so we can cache in the + //argument what type of filter and repeat to use + + FunctionNode *call_function = shader->functions[function_index].function; + if (call_function) { + + //get current base function + FunctionNode *base_function = NULL; + { + BlockNode *b = p_block; + + while (b) { + + if (b->parent_function) { + base_function = b->parent_function; + break; + } else { + b = b->parent_block; + } + } + } + + ERR_FAIL_COND_V(!base_function, NULL); //bug, wtf + + for (int i = 0; i < call_function->arguments.size(); i++) { + int argidx = i + 1; + if (argidx < func->arguments.size()) { + if (call_function->arguments[i].qualifier == ArgumentQualifier::ARGUMENT_QUALIFIER_OUT || call_function->arguments[i].qualifier == ArgumentQualifier::ARGUMENT_QUALIFIER_INOUT) { + bool error = false; + Node *n = func->arguments[argidx]; + if (n->type == Node::TYPE_CONSTANT || n->type == Node::TYPE_OPERATOR) { + error = true; + } else if (n->type == Node::TYPE_ARRAY) { + ArrayNode *an = static_cast<ArrayNode *>(n); + if (an->call_expression != NULL) { + error = true; + } + } else if (n->type == Node::TYPE_VARIABLE) { + VariableNode *vn = static_cast<VariableNode *>(n); + if (vn->is_const) { + error = true; + } else { + StringName varname = vn->name; + if (shader->uniforms.has(varname)) { + error = true; + } else { + if (p_builtin_types.has(varname)) { + BuiltInInfo info = p_builtin_types[varname]; + if (info.constant) { + error = true; + } + } + } + } + } else if (n->type == Node::TYPE_MEMBER) { + MemberNode *mn = static_cast<MemberNode *>(n); + if (mn->basetype_const) { + error = true; + } + } + if (error) { + _set_error(vformat("Constant value cannot be passed for '%s' parameter!", _get_qualifier_str(call_function->arguments[i].qualifier))); + return NULL; + } + } + if (is_sampler_type(call_function->arguments[i].type)) { + //let's see where our argument comes from + Node *n = func->arguments[argidx]; + ERR_CONTINUE(n->type != Node::TYPE_VARIABLE); //bug? this should always be a variable + VariableNode *vn = static_cast<VariableNode *>(n); + StringName varname = vn->name; + if (shader->uniforms.has(varname)) { + //being sampler, this either comes from a uniform + ShaderNode::Uniform *u = &shader->uniforms[varname]; + ERR_CONTINUE(u->type != call_function->arguments[i].type); //this should have been validated previously + //propagate + if (!_propagate_function_call_sampler_uniform_settings(name, i, u->filter, u->repeat)) { + return NULL; + } + } else if (p_builtin_types.has(varname)) { + //a built-in + if (!_propagate_function_call_sampler_builtin_reference(name, i, varname)) { + return NULL; + } + } else { + //or this comes from an argument, but nothing else can be a sampler + bool found = false; + for (int j = 0; j < base_function->arguments.size(); j++) { + if (base_function->arguments[j].name == varname) { + if (!base_function->arguments[j].tex_argument_connect.has(call_function->name)) { + base_function->arguments.write[j].tex_argument_connect[call_function->name] = Set<int>(); + } + base_function->arguments.write[j].tex_argument_connect[call_function->name].insert(i); + found = true; + break; + } + } + ERR_CONTINUE(!found); + } + } + } else { + break; + } + } + } + } + expr = func; + } + } else { + //an identifier + + _set_tkpos(pos); + + DataType data_type; + IdentifierType ident_type; + int array_size = 0; + StringName struct_name; + + if (p_block && p_block->block_tag != SubClassTag::TAG_GLOBAL) { + int idx = 0; + bool found = false; + + while (builtin_func_defs[idx].name) { + if (builtin_func_defs[idx].tag == p_block->block_tag && builtin_func_defs[idx].name == identifier) { + found = true; + break; + } + idx++; + } + if (!found) { + _set_error("Unknown identifier in expression: " + String(identifier)); + return NULL; + } + } else { + + if (!_find_identifier(p_block, false, p_builtin_types, identifier, &data_type, &ident_type, &is_const, &array_size, &struct_name)) { + _set_error("Unknown identifier in expression: " + String(identifier)); + return NULL; + } + + if (ident_type == IDENTIFIER_FUNCTION) { + _set_error("Can't use function as identifier: " + String(identifier)); + return NULL; + } + } + + Node *index_expression = NULL; + Node *call_expression = NULL; + + if (array_size > 0) { + tk = _get_token(); + + if (tk.type != TK_BRACKET_OPEN && tk.type != TK_PERIOD) { + _set_error("Expected '[' or '.'"); + return NULL; + } + + if (tk.type == TK_PERIOD) { + completion_class = TAG_ARRAY; + p_block->block_tag = SubClassTag::TAG_ARRAY; + call_expression = _parse_and_reduce_expression(p_block, p_builtin_types); + p_block->block_tag = SubClassTag::TAG_GLOBAL; + if (!call_expression) + return NULL; + data_type = call_expression->get_datatype(); + } else { // indexing + + index_expression = _parse_and_reduce_expression(p_block, p_builtin_types); + if (!index_expression) + return NULL; + + if (index_expression->get_datatype() != TYPE_INT && index_expression->get_datatype() != TYPE_UINT) { + _set_error("Only integer expressions are allowed for indexing"); + return NULL; + } + + if (index_expression->type == Node::TYPE_CONSTANT) { + ConstantNode *cnode = (ConstantNode *)index_expression; + if (cnode) { + if (!cnode->values.empty()) { + int value = cnode->values[0].sint; + if (value < 0 || value >= array_size) { + _set_error(vformat("Index [%s] out of range [%s..%s]", value, 0, array_size - 1)); + return NULL; + } + } + } + } + + tk = _get_token(); + if (tk.type != TK_BRACKET_CLOSE) { + _set_error("Expected ']'"); + return NULL; + } + } + + ArrayNode *arrname = alloc_node<ArrayNode>(); + arrname->name = identifier; + arrname->datatype_cache = data_type; + arrname->struct_name = struct_name; + arrname->index_expression = index_expression; + arrname->call_expression = call_expression; + arrname->is_const = is_const; + expr = arrname; + + } else { + + VariableNode *varname = alloc_node<VariableNode>(); + varname->name = identifier; + varname->datatype_cache = data_type; + varname->is_const = is_const; + varname->struct_name = struct_name; + expr = varname; + } + } + } else if (tk.type == TK_OP_ADD) { + continue; //this one does nothing + } else if (tk.type == TK_OP_SUB || tk.type == TK_OP_NOT || tk.type == TK_OP_BIT_INVERT || tk.type == TK_OP_INCREMENT || tk.type == TK_OP_DECREMENT) { + + Expression e; + e.is_op = true; + + switch (tk.type) { + case TK_OP_SUB: e.op = OP_NEGATE; break; + case TK_OP_NOT: e.op = OP_NOT; break; + case TK_OP_BIT_INVERT: e.op = OP_BIT_INVERT; break; + case TK_OP_INCREMENT: e.op = OP_INCREMENT; break; + case TK_OP_DECREMENT: e.op = OP_DECREMENT; break; + default: ERR_FAIL_V(NULL); + } + + expression.push_back(e); + continue; + } else { + _set_error("Expected expression, found: " + get_token_text(tk)); + return NULL; + //nothing + } + + ERR_FAIL_COND_V(!expr, NULL); + + /* OK now see what's NEXT to the operator.. */ + /* OK now see what's NEXT to the operator.. */ + /* OK now see what's NEXT to the operator.. */ + + while (true) { + TkPos pos2 = _get_tkpos(); + tk = _get_token(); + + if (tk.type == TK_CURSOR) { + //do nothing + } else if (tk.type == TK_IDENTIFIER) { + + } else if (tk.type == TK_PERIOD) { + + DataType dt = expr->get_datatype(); + String st = expr->get_datatype_name(); + + StringName identifier; + if (_get_completable_identifier(p_block, dt == TYPE_STRUCT ? COMPLETION_STRUCT : COMPLETION_INDEX, identifier)) { + if (dt == TYPE_STRUCT) { + completion_struct = st; + } else { + completion_base = dt; + } + } + + if (identifier == StringName()) { + _set_error("Expected identifier as member"); + return NULL; + } + String ident = identifier; + + bool ok = true; + bool repeated = false; + DataType member_type = TYPE_VOID; + StringName member_struct_name = ""; + int array_size = 0; + + Set<char> position_symbols; + Set<char> color_symbols; + Set<char> texture_symbols; + + bool mix_error = false; + + switch (dt) { + case TYPE_STRUCT: { + ok = false; + String member_name = String(ident.ptr()); + if (shader->structs.has(st)) { + StructNode *n = shader->structs[st].shader_struct; + for (List<MemberNode *>::Element *E = n->members.front(); E; E = E->next()) { + if (String(E->get()->name) == member_name) { + member_type = E->get()->datatype; + array_size = E->get()->array_size; + if (member_type == TYPE_STRUCT) { + member_struct_name = E->get()->struct_name; + } + ok = true; + break; + } + } + } + + } break; + case TYPE_BVEC2: + case TYPE_IVEC2: + case TYPE_UVEC2: + case TYPE_VEC2: { + + int l = ident.length(); + if (l == 1) { + member_type = DataType(dt - 1); + } else if (l == 2) { + member_type = dt; + } else if (l == 3) { + member_type = DataType(dt + 1); + } else if (l == 4) { + member_type = DataType(dt + 2); + } else { + ok = false; + break; + } + + const CharType *c = ident.ptr(); + for (int i = 0; i < l; i++) { + + switch (c[i]) { + case 'r': + case 'g': + if (position_symbols.size() > 0 || texture_symbols.size() > 0) { + mix_error = true; + break; + } + if (!color_symbols.has(c[i])) { + color_symbols.insert(c[i]); + } else { + repeated = true; + } + break; + case 'x': + case 'y': + if (color_symbols.size() > 0 || texture_symbols.size() > 0) { + mix_error = true; + break; + } + if (!position_symbols.has(c[i])) { + position_symbols.insert(c[i]); + } else { + repeated = true; + } + break; + case 's': + case 't': + if (color_symbols.size() > 0 || position_symbols.size() > 0) { + mix_error = true; + break; + } + if (!texture_symbols.has(c[i])) { + texture_symbols.insert(c[i]); + } else { + repeated = true; + } + break; + default: + ok = false; + break; + } + } + + } break; + case TYPE_BVEC3: + case TYPE_IVEC3: + case TYPE_UVEC3: + case TYPE_VEC3: { + + int l = ident.length(); + if (l == 1) { + member_type = DataType(dt - 2); + } else if (l == 2) { + member_type = DataType(dt - 1); + } else if (l == 3) { + member_type = dt; + } else if (l == 4) { + member_type = DataType(dt + 1); + } else { + ok = false; + break; + } + + const CharType *c = ident.ptr(); + for (int i = 0; i < l; i++) { + + switch (c[i]) { + case 'r': + case 'g': + case 'b': + if (position_symbols.size() > 0 || texture_symbols.size() > 0) { + mix_error = true; + break; + } + if (!color_symbols.has(c[i])) { + color_symbols.insert(c[i]); + } else { + repeated = true; + } + break; + case 'x': + case 'y': + case 'z': + if (color_symbols.size() > 0 || texture_symbols.size() > 0) { + mix_error = true; + break; + } + if (!position_symbols.has(c[i])) { + position_symbols.insert(c[i]); + } else { + repeated = true; + } + break; + case 's': + case 't': + case 'p': + if (color_symbols.size() > 0 || position_symbols.size() > 0) { + mix_error = true; + break; + } + if (!texture_symbols.has(c[i])) { + texture_symbols.insert(c[i]); + } else { + repeated = true; + } + break; + default: + ok = false; + break; + } + } + + } break; + case TYPE_BVEC4: + case TYPE_IVEC4: + case TYPE_UVEC4: + case TYPE_VEC4: { + + int l = ident.length(); + if (l == 1) { + member_type = DataType(dt - 3); + } else if (l == 2) { + member_type = DataType(dt - 2); + } else if (l == 3) { + member_type = DataType(dt - 1); + } else if (l == 4) { + member_type = dt; + } else { + ok = false; + break; + } + + const CharType *c = ident.ptr(); + for (int i = 0; i < l; i++) { + + switch (c[i]) { + case 'r': + case 'g': + case 'b': + case 'a': + if (position_symbols.size() > 0 || texture_symbols.size() > 0) { + mix_error = true; + break; + } + if (!color_symbols.has(c[i])) { + color_symbols.insert(c[i]); + } else { + repeated = true; + } + break; + case 'x': + case 'y': + case 'z': + case 'w': + if (color_symbols.size() > 0 || texture_symbols.size() > 0) { + mix_error = true; + break; + } + if (!position_symbols.has(c[i])) { + position_symbols.insert(c[i]); + } else { + repeated = true; + } + break; + case 's': + case 't': + case 'p': + case 'q': + if (color_symbols.size() > 0 || position_symbols.size() > 0) { + mix_error = true; + break; + } + if (!texture_symbols.has(c[i])) { + texture_symbols.insert(c[i]); + } else { + repeated = true; + } + break; + default: + ok = false; + break; + } + } + + } break; + + default: { + ok = false; + } + } + + if (mix_error) { + _set_error("Cannot combine symbols from different sets in expression ." + ident); + return NULL; + } + + if (!ok) { + _set_error("Invalid member for " + (dt == TYPE_STRUCT ? st : get_datatype_name(dt)) + " expression: ." + ident); + return NULL; + } + + MemberNode *mn = alloc_node<MemberNode>(); + mn->basetype = dt; + mn->basetype_const = is_const; + mn->datatype = member_type; + mn->base_struct_name = st; + mn->struct_name = member_struct_name; + mn->array_size = array_size; + mn->name = ident; + mn->owner = expr; + mn->has_swizzling_duplicates = repeated; + + if (array_size > 0) { + + tk = _get_token(); + if (tk.type == TK_PERIOD) { + _set_error("Nested array length() is not yet implemented"); + return NULL; + } else if (tk.type == TK_BRACKET_OPEN) { + + Node *index_expression = _parse_and_reduce_expression(p_block, p_builtin_types); + if (!index_expression) + return NULL; + + if (index_expression->get_datatype() != TYPE_INT && index_expression->get_datatype() != TYPE_UINT) { + _set_error("Only integer expressions are allowed for indexing"); + return NULL; + } + + if (index_expression->type == Node::TYPE_CONSTANT) { + ConstantNode *cnode = (ConstantNode *)index_expression; + if (cnode) { + if (!cnode->values.empty()) { + int value = cnode->values[0].sint; + if (value < 0 || value >= array_size) { + _set_error(vformat("Index [%s] out of range [%s..%s]", value, 0, array_size - 1)); + return NULL; + } + } + } + } + + tk = _get_token(); + if (tk.type != TK_BRACKET_CLOSE) { + _set_error("Expected ']'"); + return NULL; + } + mn->index_expression = index_expression; + + } else { + _set_error("Expected '[' or '.'"); + return NULL; + } + } + + expr = mn; + + //todo + //member (period) has priority over any operator + //creates a subindexing expression in place + + /*} else if (tk.type==TK_BRACKET_OPEN) { + //todo + //subindexing has priority over any operator + //creates a subindexing expression in place + + */ + } else if (tk.type == TK_BRACKET_OPEN) { + + Node *index = _parse_and_reduce_expression(p_block, p_builtin_types); + if (!index) + return NULL; + + if (index->get_datatype() != TYPE_INT && index->get_datatype() != TYPE_UINT) { + _set_error("Only integer datatypes are allowed for indexing"); + return NULL; + } + + DataType member_type = TYPE_VOID; + + switch (expr->get_datatype()) { + case TYPE_BVEC2: + case TYPE_VEC2: + case TYPE_IVEC2: + case TYPE_UVEC2: + case TYPE_MAT2: + if (index->type == Node::TYPE_CONSTANT) { + uint32_t index_constant = static_cast<ConstantNode *>(index)->values[0].uint; + if (index_constant >= 2) { + _set_error("Index out of range (0-1)"); + return NULL; + } + } + + switch (expr->get_datatype()) { + case TYPE_BVEC2: member_type = TYPE_BOOL; break; + case TYPE_VEC2: member_type = TYPE_FLOAT; break; + case TYPE_IVEC2: member_type = TYPE_INT; break; + case TYPE_UVEC2: member_type = TYPE_UINT; break; + case TYPE_MAT2: member_type = TYPE_VEC2; break; + default: break; + } + + break; + case TYPE_BVEC3: + case TYPE_VEC3: + case TYPE_IVEC3: + case TYPE_UVEC3: + case TYPE_MAT3: + if (index->type == Node::TYPE_CONSTANT) { + uint32_t index_constant = static_cast<ConstantNode *>(index)->values[0].uint; + if (index_constant >= 3) { + _set_error("Index out of range (0-2)"); + return NULL; + } + } + + switch (expr->get_datatype()) { + case TYPE_BVEC3: member_type = TYPE_BOOL; break; + case TYPE_VEC3: member_type = TYPE_FLOAT; break; + case TYPE_IVEC3: member_type = TYPE_INT; break; + case TYPE_UVEC3: member_type = TYPE_UINT; break; + case TYPE_MAT3: member_type = TYPE_VEC3; break; + default: break; + } + break; + case TYPE_BVEC4: + case TYPE_VEC4: + case TYPE_IVEC4: + case TYPE_UVEC4: + case TYPE_MAT4: + if (index->type == Node::TYPE_CONSTANT) { + uint32_t index_constant = static_cast<ConstantNode *>(index)->values[0].uint; + if (index_constant >= 4) { + _set_error("Index out of range (0-3)"); + return NULL; + } + } + + switch (expr->get_datatype()) { + case TYPE_BVEC4: member_type = TYPE_BOOL; break; + case TYPE_VEC4: member_type = TYPE_FLOAT; break; + case TYPE_IVEC4: member_type = TYPE_INT; break; + case TYPE_UVEC4: member_type = TYPE_UINT; break; + case TYPE_MAT4: member_type = TYPE_VEC4; break; + default: break; + } + break; + default: { + _set_error("Object of type '" + (expr->get_datatype() == TYPE_STRUCT ? expr->get_datatype_name() : get_datatype_name(expr->get_datatype())) + "' can't be indexed"); + return NULL; + } + } + + OperatorNode *op = alloc_node<OperatorNode>(); + op->op = OP_INDEX; + op->return_cache = member_type; + op->arguments.push_back(expr); + op->arguments.push_back(index); + expr = op; + + tk = _get_token(); + if (tk.type != TK_BRACKET_CLOSE) { + _set_error("Expected ']' after indexing expression"); + return NULL; + } + + } else if (tk.type == TK_OP_INCREMENT || tk.type == TK_OP_DECREMENT) { + + OperatorNode *op = alloc_node<OperatorNode>(); + op->op = tk.type == TK_OP_DECREMENT ? OP_POST_DECREMENT : OP_POST_INCREMENT; + op->arguments.push_back(expr); + + if (!_validate_operator(op, &op->return_cache)) { + _set_error("Invalid base type for increment/decrement operator"); + return NULL; + } + + if (!_validate_assign(expr, p_builtin_types)) { + _set_error("Invalid use of increment/decrement operator in constant expression."); + return NULL; + } + expr = op; + } else { + + _set_tkpos(pos2); + break; + } + } + + Expression e; + e.is_op = false; + e.node = expr; + expression.push_back(e); + + pos = _get_tkpos(); + tk = _get_token(); + + if (is_token_operator(tk.type)) { + + Expression o; + o.is_op = true; + + switch (tk.type) { + + case TK_OP_EQUAL: o.op = OP_EQUAL; break; + case TK_OP_NOT_EQUAL: o.op = OP_NOT_EQUAL; break; + case TK_OP_LESS: o.op = OP_LESS; break; + case TK_OP_LESS_EQUAL: o.op = OP_LESS_EQUAL; break; + case TK_OP_GREATER: o.op = OP_GREATER; break; + case TK_OP_GREATER_EQUAL: o.op = OP_GREATER_EQUAL; break; + case TK_OP_AND: o.op = OP_AND; break; + case TK_OP_OR: o.op = OP_OR; break; + case TK_OP_ADD: o.op = OP_ADD; break; + case TK_OP_SUB: o.op = OP_SUB; break; + case TK_OP_MUL: o.op = OP_MUL; break; + case TK_OP_DIV: o.op = OP_DIV; break; + case TK_OP_MOD: o.op = OP_MOD; break; + case TK_OP_SHIFT_LEFT: o.op = OP_SHIFT_LEFT; break; + case TK_OP_SHIFT_RIGHT: o.op = OP_SHIFT_RIGHT; break; + case TK_OP_ASSIGN: o.op = OP_ASSIGN; break; + case TK_OP_ASSIGN_ADD: o.op = OP_ASSIGN_ADD; break; + case TK_OP_ASSIGN_SUB: o.op = OP_ASSIGN_SUB; break; + case TK_OP_ASSIGN_MUL: o.op = OP_ASSIGN_MUL; break; + case TK_OP_ASSIGN_DIV: o.op = OP_ASSIGN_DIV; break; + case TK_OP_ASSIGN_MOD: o.op = OP_ASSIGN_MOD; break; + case TK_OP_ASSIGN_SHIFT_LEFT: o.op = OP_ASSIGN_SHIFT_LEFT; break; + case TK_OP_ASSIGN_SHIFT_RIGHT: o.op = OP_ASSIGN_SHIFT_RIGHT; break; + case TK_OP_ASSIGN_BIT_AND: o.op = OP_ASSIGN_BIT_AND; break; + case TK_OP_ASSIGN_BIT_OR: o.op = OP_ASSIGN_BIT_OR; break; + case TK_OP_ASSIGN_BIT_XOR: o.op = OP_ASSIGN_BIT_XOR; break; + case TK_OP_BIT_AND: o.op = OP_BIT_AND; break; + case TK_OP_BIT_OR: o.op = OP_BIT_OR; break; + case TK_OP_BIT_XOR: o.op = OP_BIT_XOR; break; + case TK_QUESTION: o.op = OP_SELECT_IF; break; + case TK_COLON: o.op = OP_SELECT_ELSE; break; + default: { + _set_error("Invalid token for operator: " + get_token_text(tk)); + return NULL; + } + } + + expression.push_back(o); + + } else { + _set_tkpos(pos); //something else, so rollback and end + break; + } + } + + /* Reduce the set set of expressions and place them in an operator tree, respecting precedence */ + + while (expression.size() > 1) { + + int next_op = -1; + int min_priority = 0xFFFFF; + bool is_unary = false; + bool is_ternary = false; + + for (int i = 0; i < expression.size(); i++) { + + if (!expression[i].is_op) { + + continue; + } + + bool unary = false; + bool ternary = false; + + int priority; + switch (expression[i].op) { + case OP_EQUAL: priority = 8; break; + case OP_NOT_EQUAL: priority = 8; break; + case OP_LESS: priority = 7; break; + case OP_LESS_EQUAL: priority = 7; break; + case OP_GREATER: priority = 7; break; + case OP_GREATER_EQUAL: priority = 7; break; + case OP_AND: priority = 12; break; + case OP_OR: priority = 14; break; + case OP_NOT: + priority = 3; + unary = true; + break; + case OP_NEGATE: + priority = 3; + unary = true; + break; + case OP_ADD: priority = 5; break; + case OP_SUB: priority = 5; break; + case OP_MUL: priority = 4; break; + case OP_DIV: priority = 4; break; + case OP_MOD: priority = 4; break; + case OP_SHIFT_LEFT: priority = 6; break; + case OP_SHIFT_RIGHT: priority = 6; break; + case OP_ASSIGN: priority = 16; break; + case OP_ASSIGN_ADD: priority = 16; break; + case OP_ASSIGN_SUB: priority = 16; break; + case OP_ASSIGN_MUL: priority = 16; break; + case OP_ASSIGN_DIV: priority = 16; break; + case OP_ASSIGN_MOD: priority = 16; break; + case OP_ASSIGN_SHIFT_LEFT: priority = 16; break; + case OP_ASSIGN_SHIFT_RIGHT: priority = 16; break; + case OP_ASSIGN_BIT_AND: priority = 16; break; + case OP_ASSIGN_BIT_OR: priority = 16; break; + case OP_ASSIGN_BIT_XOR: priority = 16; break; + case OP_BIT_AND: priority = 9; break; + case OP_BIT_OR: priority = 11; break; + case OP_BIT_XOR: priority = 10; break; + case OP_BIT_INVERT: + priority = 3; + unary = true; + break; + case OP_INCREMENT: + priority = 3; + unary = true; + break; + case OP_DECREMENT: + priority = 3; + unary = true; + break; + case OP_SELECT_IF: + priority = 15; + ternary = true; + break; + case OP_SELECT_ELSE: + priority = 15; + ternary = true; + break; + + default: + ERR_FAIL_V(NULL); //unexpected operator + } + + if (priority < min_priority) { + // < is used for left to right (default) + // <= is used for right to left + next_op = i; + min_priority = priority; + is_unary = unary; + is_ternary = ternary; + } + } + + ERR_FAIL_COND_V(next_op == -1, NULL); + + // OK! create operator.. + // OK! create operator.. + if (is_unary) { + + int expr_pos = next_op; + while (expression[expr_pos].is_op) { + + expr_pos++; + if (expr_pos == expression.size()) { + //can happen.. + _set_error("Unexpected end of expression..."); + return NULL; + } + } + + //consecutively do unary operators + for (int i = expr_pos - 1; i >= next_op; i--) { + + OperatorNode *op = alloc_node<OperatorNode>(); + op->op = expression[i].op; + if ((op->op == OP_INCREMENT || op->op == OP_DECREMENT) && !_validate_assign(expression[i + 1].node, p_builtin_types)) { + + _set_error("Can't use increment/decrement operator in constant expression."); + return NULL; + } + op->arguments.push_back(expression[i + 1].node); + + expression.write[i].is_op = false; + expression.write[i].node = op; + + if (!_validate_operator(op, &op->return_cache)) { + + String at; + for (int j = 0; j < op->arguments.size(); j++) { + if (j > 0) + at += " and "; + at += get_datatype_name(op->arguments[j]->get_datatype()); + } + _set_error("Invalid arguments to unary operator '" + get_operator_text(op->op) + "' :" + at); + return NULL; + } + expression.remove(i + 1); + } + + } else if (is_ternary) { + + if (next_op < 1 || next_op >= (expression.size() - 1)) { + _set_error("Parser bug..."); + ERR_FAIL_V(NULL); + } + + if (next_op + 2 >= expression.size() || !expression[next_op + 2].is_op || expression[next_op + 2].op != OP_SELECT_ELSE) { + _set_error("Missing matching ':' for select operator"); + return NULL; + } + + OperatorNode *op = alloc_node<OperatorNode>(); + op->op = expression[next_op].op; + op->arguments.push_back(expression[next_op - 1].node); + op->arguments.push_back(expression[next_op + 1].node); + op->arguments.push_back(expression[next_op + 3].node); + + expression.write[next_op - 1].is_op = false; + expression.write[next_op - 1].node = op; + if (!_validate_operator(op, &op->return_cache)) { + + String at; + for (int i = 0; i < op->arguments.size(); i++) { + if (i > 0) + at += " and "; + at += get_datatype_name(op->arguments[i]->get_datatype()); + } + _set_error("Invalid argument to ternary ?: operator: " + at); + return NULL; + } + + for (int i = 0; i < 4; i++) { + expression.remove(next_op); + } + + } else { + + if (next_op < 1 || next_op >= (expression.size() - 1)) { + _set_error("Parser bug..."); + ERR_FAIL_V(NULL); + } + + OperatorNode *op = alloc_node<OperatorNode>(); + op->op = expression[next_op].op; + + if (expression[next_op - 1].is_op) { + + _set_error("Parser bug..."); + ERR_FAIL_V(NULL); + } + + if (_is_operator_assign(op->op)) { + + String assign_message; + if (!_validate_assign(expression[next_op - 1].node, p_builtin_types, &assign_message)) { + + _set_error(assign_message); + return NULL; + } + } + + if (expression[next_op + 1].is_op) { + // this is not invalid and can really appear + // but it becomes invalid anyway because no binary op + // can be followed by a unary op in a valid combination, + // due to how precedence works, unaries will always disappear first + + _set_error("Parser bug..."); + } + + op->arguments.push_back(expression[next_op - 1].node); //expression goes as left + op->arguments.push_back(expression[next_op + 1].node); //next expression goes as right + expression.write[next_op - 1].node = op; + + //replace all 3 nodes by this operator and make it an expression + + if (!_validate_operator(op, &op->return_cache)) { + + String at; + for (int i = 0; i < op->arguments.size(); i++) { + if (i > 0) + at += " and "; + if (op->arguments[i]->get_datatype() == TYPE_STRUCT) { + at += op->arguments[i]->get_datatype_name(); + } else { + at += get_datatype_name(op->arguments[i]->get_datatype()); + } + } + _set_error("Invalid arguments to operator '" + get_operator_text(op->op) + "' :" + at); + return NULL; + } + + expression.remove(next_op); + expression.remove(next_op); + } + } + + return expression[0].node; +} + +ShaderLanguage::Node *ShaderLanguage::_reduce_expression(BlockNode *p_block, ShaderLanguage::Node *p_node) { + + if (p_node->type != Node::TYPE_OPERATOR) + return p_node; + + //for now only reduce simple constructors + OperatorNode *op = static_cast<OperatorNode *>(p_node); + + if (op->op == OP_CONSTRUCT) { + + ERR_FAIL_COND_V(op->arguments[0]->type != Node::TYPE_VARIABLE, p_node); + + DataType type = op->get_datatype(); + DataType base = get_scalar_type(type); + int cardinality = get_cardinality(type); + + Vector<ConstantNode::Value> values; + + for (int i = 1; i < op->arguments.size(); i++) { + + op->arguments.write[i] = _reduce_expression(p_block, op->arguments[i]); + if (op->arguments[i]->type == Node::TYPE_CONSTANT) { + ConstantNode *cn = static_cast<ConstantNode *>(op->arguments[i]); + + if (get_scalar_type(cn->datatype) == base) { + for (int j = 0; j < cn->values.size(); j++) { + values.push_back(cn->values[j]); + } + } else if (get_scalar_type(cn->datatype) == cn->datatype) { + + ConstantNode::Value v; + if (!convert_constant(cn, base, &v)) { + return p_node; + } + values.push_back(v); + } else { + return p_node; + } + + } else { + return p_node; + } + } + + if (values.size() == 1) { + if (type >= TYPE_MAT2 && type <= TYPE_MAT4) { + ConstantNode::Value value = values[0]; + ConstantNode::Value zero; + zero.real = 0.0f; + int size = 2 + (type - TYPE_MAT2); + + values.clear(); + for (int i = 0; i < size; i++) { + for (int j = 0; j < size; j++) { + values.push_back(i == j ? value : zero); + } + } + } else { + ConstantNode::Value value = values[0]; + for (int i = 1; i < cardinality; i++) { + values.push_back(value); + } + } + } else if (values.size() != cardinality) { + ERR_PRINT("Failed to reduce expression, values and cardinality mismatch."); + return p_node; + } + + ConstantNode *cn = alloc_node<ConstantNode>(); + cn->datatype = op->get_datatype(); + cn->values = values; + return cn; + } else if (op->op == OP_NEGATE) { + + op->arguments.write[0] = _reduce_expression(p_block, op->arguments[0]); + if (op->arguments[0]->type == Node::TYPE_CONSTANT) { + + ConstantNode *cn = static_cast<ConstantNode *>(op->arguments[0]); + + DataType base = get_scalar_type(cn->datatype); + + Vector<ConstantNode::Value> values; + + for (int i = 0; i < cn->values.size(); i++) { + + ConstantNode::Value nv; + switch (base) { + case TYPE_BOOL: { + nv.boolean = !cn->values[i].boolean; + } break; + case TYPE_INT: { + nv.sint = -cn->values[i].sint; + } break; + case TYPE_UINT: { + // Intentionally wrap the unsigned int value, because GLSL does. + nv.uint = 0 - cn->values[i].uint; + } break; + case TYPE_FLOAT: { + nv.real = -cn->values[i].real; + } break; + default: { + } + } + + values.push_back(nv); + } + + cn->values = values; + return cn; + } + } + + return p_node; +} + +ShaderLanguage::Node *ShaderLanguage::_parse_and_reduce_expression(BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types) { + + ShaderLanguage::Node *expr = _parse_expression(p_block, p_builtin_types); + if (!expr) //errored + return NULL; + + expr = _reduce_expression(p_block, expr); + + return expr; +} + +Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types, bool p_just_one, bool p_can_break, bool p_can_continue) { + + while (true) { + + TkPos pos = _get_tkpos(); + + Token tk = _get_token(); + + if (p_block && p_block->block_type == BlockNode::BLOCK_TYPE_SWITCH) { + if (tk.type != TK_CF_CASE && tk.type != TK_CF_DEFAULT && tk.type != TK_CURLY_BRACKET_CLOSE) { + _set_error("Switch may contains only case and default blocks"); + return ERR_PARSE_ERROR; + } + } + + bool is_struct = shader->structs.has(tk.text); + + if (tk.type == TK_CURLY_BRACKET_CLOSE) { //end of block + if (p_just_one) { + _set_error("Unexpected '}'"); + return ERR_PARSE_ERROR; + } + + return OK; + + } else if (tk.type == TK_CONST || is_token_precision(tk.type) || is_token_nonvoid_datatype(tk.type) || is_struct) { + String struct_name = ""; + if (is_struct) { + struct_name = tk.text; + } + + bool is_const = false; + + if (tk.type == TK_CONST) { + is_const = true; + tk = _get_token(); + + if (!is_struct) { + is_struct = shader->structs.has(tk.text); // check again. + struct_name = tk.text; + } + } + + DataPrecision precision = PRECISION_DEFAULT; + if (is_token_precision(tk.type)) { + precision = get_token_precision(tk.type); + tk = _get_token(); + + if (!is_struct) { + is_struct = shader->structs.has(tk.text); // check again. + } + if (is_struct && precision != PRECISION_DEFAULT) { + _set_error("Precision modifier cannot be used on structs."); + return ERR_PARSE_ERROR; + } + if (!is_token_nonvoid_datatype(tk.type)) { + _set_error("Expected datatype after precision"); + return ERR_PARSE_ERROR; + } + } + + if (!is_struct) { + if (!is_token_variable_datatype(tk.type)) { + _set_error("Invalid data type for variable (samplers not allowed)"); + return ERR_PARSE_ERROR; + } + } + + DataType type = is_struct ? TYPE_STRUCT : get_token_datatype(tk.type); + + if (_validate_datatype(type) != OK) { + return ERR_PARSE_ERROR; + } + + tk = _get_token(); + + Node *vardecl = NULL; + + while (true) { + + if (tk.type != TK_IDENTIFIER) { + _set_error("Expected identifier after type"); + return ERR_PARSE_ERROR; + } + + StringName name = tk.text; + ShaderLanguage::IdentifierType itype; + if (_find_identifier(p_block, true, p_builtin_types, name, (ShaderLanguage::DataType *)0, &itype)) { + if (itype != IDENTIFIER_FUNCTION) { + _set_error("Redefinition of '" + String(name) + "'"); + return ERR_PARSE_ERROR; + } + } + + BlockNode::Variable var; + var.type = type; + var.precision = precision; + var.line = tk_line; + var.array_size = 0; + var.is_const = is_const; + var.struct_name = struct_name; + + tk = _get_token(); + + if (tk.type == TK_BRACKET_OPEN) { + bool unknown_size = false; + + if (RenderingServer::get_singleton()->is_low_end() && is_const) { + _set_error("Local const arrays are supported only on high-end platform!"); + return ERR_PARSE_ERROR; + } + + ArrayDeclarationNode *node = alloc_node<ArrayDeclarationNode>(); + if (is_struct) { + node->struct_name = struct_name; + node->datatype = TYPE_STRUCT; + } else { + node->datatype = type; + } + node->precision = precision; + node->is_const = is_const; + vardecl = (Node *)node; + + ArrayDeclarationNode::Declaration decl; + decl.name = name; + decl.size = 0U; + + tk = _get_token(); + + if (tk.type == TK_BRACKET_CLOSE) { + unknown_size = true; + } else { + + if (tk.type != TK_INT_CONSTANT || ((int)tk.constant) <= 0) { + _set_error("Expected integer constant > 0 or ']'"); + return ERR_PARSE_ERROR; + } + + decl.size = ((uint32_t)tk.constant); + tk = _get_token(); + + if (tk.type != TK_BRACKET_CLOSE) { + _set_error("Expected ']'"); + return ERR_PARSE_ERROR; + } + var.array_size = decl.size; + } + + bool full_def = false; + + tk = _get_token(); + if (tk.type == TK_OP_ASSIGN) { + + if (RenderingServer::get_singleton()->is_low_end()) { + _set_error("Array initialization is supported only on high-end platform!"); + return ERR_PARSE_ERROR; + } + + tk = _get_token(); + + if (tk.type != TK_CURLY_BRACKET_OPEN) { + + if (unknown_size) { + _set_error("Expected '{'"); + return ERR_PARSE_ERROR; + } + + full_def = true; + + DataPrecision precision2 = PRECISION_DEFAULT; + if (is_token_precision(tk.type)) { + precision2 = get_token_precision(tk.type); + tk = _get_token(); + if (shader->structs.has(tk.text)) { + _set_error("Precision modifier cannot be used on structs."); + return ERR_PARSE_ERROR; + } + if (!is_token_nonvoid_datatype(tk.type)) { + _set_error("Expected datatype after precision"); + return ERR_PARSE_ERROR; + } + } + + DataType type2; + String struct_name2 = ""; + + if (shader->structs.has(tk.text)) { + type2 = TYPE_STRUCT; + struct_name2 = tk.text; + } else { + if (!is_token_variable_datatype(tk.type)) { + _set_error("Invalid data type for array"); + return ERR_PARSE_ERROR; + } + type2 = get_token_datatype(tk.type); + } + + int array_size2 = 0; + + tk = _get_token(); + if (tk.type == TK_BRACKET_OPEN) { + TkPos pos2 = _get_tkpos(); + tk = _get_token(); + if (tk.type == TK_BRACKET_CLOSE) { + array_size2 = var.array_size; + tk = _get_token(); + } else { + _set_tkpos(pos2); + + Node *n = _parse_and_reduce_expression(p_block, p_builtin_types); + if (!n || n->type != Node::TYPE_CONSTANT || n->get_datatype() != TYPE_INT) { + _set_error("Expected single integer constant > 0"); + return ERR_PARSE_ERROR; + } + + ConstantNode *cnode = (ConstantNode *)n; + if (cnode->values.size() == 1) { + array_size2 = cnode->values[0].sint; + if (array_size2 <= 0) { + _set_error("Expected single integer constant > 0"); + return ERR_PARSE_ERROR; + } + } else { + _set_error("Expected single integer constant > 0"); + return ERR_PARSE_ERROR; + } + + tk = _get_token(); + if (tk.type != TK_BRACKET_CLOSE) { + _set_error("Expected ']'"); + return ERR_PARSE_ERROR; + } else { + tk = _get_token(); + } + } + } else { + _set_error("Expected '['"); + return ERR_PARSE_ERROR; + } + + if (precision != precision2 || type != type2 || struct_name != struct_name2 || var.array_size != array_size2) { + String error_str = "Cannot convert from '"; + if (precision2 != PRECISION_DEFAULT) { + error_str += get_precision_name(precision2); + error_str += " "; + } + if (type2 == TYPE_STRUCT) { + error_str += struct_name2; + } else { + error_str += get_datatype_name(type2); + } + error_str += "["; + error_str += itos(array_size2); + error_str += "]'"; + error_str += " to '"; + if (precision != PRECISION_DEFAULT) { + error_str += get_precision_name(precision); + error_str += " "; + } + if (type == TYPE_STRUCT) { + error_str += struct_name; + } else { + error_str += get_datatype_name(type); + } + error_str += "["; + error_str += itos(var.array_size); + error_str += "]'"; + _set_error(error_str); + return ERR_PARSE_ERROR; + } + } + + bool curly = tk.type == TK_CURLY_BRACKET_OPEN; + + if (unknown_size) { + if (!curly) { + _set_error("Expected '{'"); + return ERR_PARSE_ERROR; + } + } else { + if (full_def) { + if (curly) { + _set_error("Expected '('"); + return ERR_PARSE_ERROR; + } + } + } + + if (tk.type == TK_PARENTHESIS_OPEN || curly) { // initialization + while (true) { + + Node *n = _parse_and_reduce_expression(p_block, p_builtin_types); + if (!n) { + return ERR_PARSE_ERROR; + } + + if (node->is_const && n->type == Node::TYPE_OPERATOR && ((OperatorNode *)n)->op == OP_CALL) { + _set_error("Expected constant expression"); + return ERR_PARSE_ERROR; + } + + if (var.type != n->get_datatype() || struct_name != n->get_datatype_name()) { + _set_error("Invalid assignment of '" + (n->get_datatype() == TYPE_STRUCT ? n->get_datatype_name() : get_datatype_name(n->get_datatype())) + "' to '" + (var.type == TYPE_STRUCT ? struct_name : get_datatype_name(var.type)) + "'"); + return ERR_PARSE_ERROR; + } + + tk = _get_token(); + if (tk.type == TK_COMMA) { + decl.initializer.push_back(n); + continue; + } else if (!curly && tk.type == TK_PARENTHESIS_CLOSE) { + decl.initializer.push_back(n); + break; + } else if (curly && tk.type == TK_CURLY_BRACKET_CLOSE) { + decl.initializer.push_back(n); + break; + } else { + if (curly) + _set_error("Expected '}' or ','"); + else + _set_error("Expected ')' or ','"); + return ERR_PARSE_ERROR; + } + } + if (unknown_size) { + decl.size = decl.initializer.size(); + var.array_size = decl.initializer.size(); + } else if (decl.initializer.size() != var.array_size) { + _set_error("Array size mismatch"); + return ERR_PARSE_ERROR; + } + tk = _get_token(); + } + } else { + if (unknown_size) { + _set_error("Expected array initialization"); + return ERR_PARSE_ERROR; + } + if (is_const) { + _set_error("Expected initialization of constant"); + return ERR_PARSE_ERROR; + } + } + + node->declarations.push_back(decl); + } else if (tk.type == TK_OP_ASSIGN) { + + VariableDeclarationNode *node = alloc_node<VariableDeclarationNode>(); + if (is_struct) { + node->struct_name = struct_name; + node->datatype = TYPE_STRUCT; + } else { + node->datatype = type; + } + node->precision = precision; + node->is_const = is_const; + vardecl = (Node *)node; + + VariableDeclarationNode::Declaration decl; + decl.name = name; + decl.initializer = NULL; + + //variable created with assignment! must parse an expression + Node *n = _parse_and_reduce_expression(p_block, p_builtin_types); + if (!n) + return ERR_PARSE_ERROR; + if (node->is_const && n->type == Node::TYPE_OPERATOR && ((OperatorNode *)n)->op == OP_CALL) { + _set_error("Expected constant expression after '='"); + return ERR_PARSE_ERROR; + } + decl.initializer = n; + + if (var.type == TYPE_STRUCT ? (var.struct_name != n->get_datatype_name()) : (var.type != n->get_datatype())) { + _set_error("Invalid assignment of '" + (n->get_datatype() == TYPE_STRUCT ? n->get_datatype_name() : get_datatype_name(n->get_datatype())) + "' to '" + (var.type == TYPE_STRUCT ? String(var.struct_name) : get_datatype_name(var.type)) + "'"); + return ERR_PARSE_ERROR; + } + tk = _get_token(); + node->declarations.push_back(decl); + } else { + if (is_const) { + _set_error("Expected initialization of constant"); + return ERR_PARSE_ERROR; + } + + VariableDeclarationNode *node = alloc_node<VariableDeclarationNode>(); + if (is_struct) { + node->struct_name = struct_name; + node->datatype = TYPE_STRUCT; + } else { + node->datatype = type; + } + node->precision = precision; + vardecl = (Node *)node; + + VariableDeclarationNode::Declaration decl; + decl.name = name; + decl.initializer = NULL; + node->declarations.push_back(decl); + } + + p_block->statements.push_back(vardecl); + + p_block->variables[name] = var; + + if (tk.type == TK_COMMA) { + tk = _get_token(); + //another variable + } else if (tk.type == TK_SEMICOLON) { + break; + } else { + _set_error("Expected ',' or ';' after variable"); + return ERR_PARSE_ERROR; + } + } + } else if (tk.type == TK_CURLY_BRACKET_OPEN) { + //a sub block, just because.. + BlockNode *block = alloc_node<BlockNode>(); + block->parent_block = p_block; + if (_parse_block(block, p_builtin_types, false, p_can_break, p_can_continue) != OK) { + return ERR_PARSE_ERROR; + } + p_block->statements.push_back(block); + } else if (tk.type == TK_CF_IF) { + //if () {} + tk = _get_token(); + if (tk.type != TK_PARENTHESIS_OPEN) { + _set_error("Expected '(' after if"); + return ERR_PARSE_ERROR; + } + + ControlFlowNode *cf = alloc_node<ControlFlowNode>(); + cf->flow_op = FLOW_OP_IF; + Node *n = _parse_and_reduce_expression(p_block, p_builtin_types); + if (!n) + return ERR_PARSE_ERROR; + + if (n->get_datatype() != TYPE_BOOL) { + _set_error("Expected boolean expression"); + return ERR_PARSE_ERROR; + } + + tk = _get_token(); + if (tk.type != TK_PARENTHESIS_CLOSE) { + _set_error("Expected ')' after expression"); + return ERR_PARSE_ERROR; + } + + BlockNode *block = alloc_node<BlockNode>(); + block->parent_block = p_block; + cf->expressions.push_back(n); + cf->blocks.push_back(block); + p_block->statements.push_back(cf); + + Error err = _parse_block(block, p_builtin_types, true, p_can_break, p_can_continue); + if (err) + return err; + + pos = _get_tkpos(); + tk = _get_token(); + if (tk.type == TK_CF_ELSE) { + + block = alloc_node<BlockNode>(); + block->parent_block = p_block; + cf->blocks.push_back(block); + err = _parse_block(block, p_builtin_types, true, p_can_break, p_can_continue); + + } else { + _set_tkpos(pos); //rollback + } + } else if (tk.type == TK_CF_SWITCH) { + + if (RenderingServer::get_singleton()->is_low_end()) { + _set_error("\"switch\" operator is supported only on high-end platform!"); + return ERR_PARSE_ERROR; + } + + // switch() {} + tk = _get_token(); + if (tk.type != TK_PARENTHESIS_OPEN) { + _set_error("Expected '(' after switch"); + return ERR_PARSE_ERROR; + } + ControlFlowNode *cf = alloc_node<ControlFlowNode>(); + cf->flow_op = FLOW_OP_SWITCH; + Node *n = _parse_and_reduce_expression(p_block, p_builtin_types); + if (!n) + return ERR_PARSE_ERROR; + if (n->get_datatype() != TYPE_INT) { + _set_error("Expected integer expression"); + return ERR_PARSE_ERROR; + } + tk = _get_token(); + if (tk.type != TK_PARENTHESIS_CLOSE) { + _set_error("Expected ')' after expression"); + return ERR_PARSE_ERROR; + } + tk = _get_token(); + if (tk.type != TK_CURLY_BRACKET_OPEN) { + _set_error("Expected '{' after switch statement"); + return ERR_PARSE_ERROR; + } + BlockNode *switch_block = alloc_node<BlockNode>(); + switch_block->block_type = BlockNode::BLOCK_TYPE_SWITCH; + switch_block->parent_block = p_block; + cf->expressions.push_back(n); + cf->blocks.push_back(switch_block); + p_block->statements.push_back(cf); + + int prev_type = TK_CF_CASE; + while (true) { // Go-through multiple cases. + + if (_parse_block(switch_block, p_builtin_types, true, true, false) != OK) { + return ERR_PARSE_ERROR; + } + pos = _get_tkpos(); + tk = _get_token(); + if (tk.type == TK_CF_CASE || tk.type == TK_CF_DEFAULT) { + if (prev_type == TK_CF_DEFAULT) { + if (tk.type == TK_CF_CASE) { + _set_error("Cases must be defined before default case."); + return ERR_PARSE_ERROR; + } else if (prev_type == TK_CF_DEFAULT) { + _set_error("Default case must be defined only once."); + return ERR_PARSE_ERROR; + } + } + prev_type = tk.type; + _set_tkpos(pos); + continue; + } else { + Set<int> constants; + for (int i = 0; i < switch_block->statements.size(); i++) { // Checks for duplicates. + ControlFlowNode *flow = (ControlFlowNode *)switch_block->statements[i]; + if (flow) { + if (flow->flow_op == FLOW_OP_CASE) { + ConstantNode *n2 = static_cast<ConstantNode *>(flow->expressions[0]); + if (!n2) { + return ERR_PARSE_ERROR; + } + if (n2->values.empty()) { + return ERR_PARSE_ERROR; + } + if (constants.has(n2->values[0].sint)) { + _set_error("Duplicated case label: '" + itos(n2->values[0].sint) + "'"); + return ERR_PARSE_ERROR; + } + constants.insert(n2->values[0].sint); + } else if (flow->flow_op == FLOW_OP_DEFAULT) { + continue; + } else { + return ERR_PARSE_ERROR; + } + } else { + return ERR_PARSE_ERROR; + } + } + break; + } + } + + } else if (tk.type == TK_CF_CASE) { + // case x : break; | return; + + if (p_block && p_block->block_type == BlockNode::BLOCK_TYPE_CASE) { + _set_tkpos(pos); + return OK; + } + + if (!p_block || (p_block->block_type != BlockNode::BLOCK_TYPE_SWITCH)) { + _set_error("case must be placed within switch block"); + return ERR_PARSE_ERROR; + } + + tk = _get_token(); + + int sign = 1; + + if (tk.type == TK_OP_SUB) { + sign = -1; + tk = _get_token(); + } + + if (tk.type != TK_INT_CONSTANT) { + _set_error("Expected integer constant"); + return ERR_PARSE_ERROR; + } + + int constant = (int)tk.constant * sign; + + tk = _get_token(); + + if (tk.type != TK_COLON) { + _set_error("Expected ':'"); + return ERR_PARSE_ERROR; + } + + ControlFlowNode *cf = alloc_node<ControlFlowNode>(); + cf->flow_op = FLOW_OP_CASE; + + ConstantNode *n = alloc_node<ConstantNode>(); + ConstantNode::Value v; + v.sint = constant; + n->values.push_back(v); + n->datatype = TYPE_INT; + + BlockNode *case_block = alloc_node<BlockNode>(); + case_block->block_type = BlockNode::BLOCK_TYPE_CASE; + case_block->parent_block = p_block; + cf->expressions.push_back(n); + cf->blocks.push_back(case_block); + p_block->statements.push_back(cf); + + Error err = _parse_block(case_block, p_builtin_types, false, true, false); + if (err) + return err; + + return OK; + + } else if (tk.type == TK_CF_DEFAULT) { + + if (p_block && p_block->block_type == BlockNode::BLOCK_TYPE_CASE) { + _set_tkpos(pos); + return OK; + } + + if (!p_block || (p_block->block_type != BlockNode::BLOCK_TYPE_SWITCH)) { + _set_error("default must be placed within switch block"); + return ERR_PARSE_ERROR; + } + + tk = _get_token(); + + if (tk.type != TK_COLON) { + _set_error("Expected ':'"); + return ERR_PARSE_ERROR; + } + + ControlFlowNode *cf = alloc_node<ControlFlowNode>(); + cf->flow_op = FLOW_OP_DEFAULT; + + BlockNode *default_block = alloc_node<BlockNode>(); + default_block->block_type = BlockNode::BLOCK_TYPE_DEFAULT; + default_block->parent_block = p_block; + cf->blocks.push_back(default_block); + p_block->statements.push_back(cf); + + Error err = _parse_block(default_block, p_builtin_types, false, true, false); + if (err) + return err; + + return OK; + + } else if (tk.type == TK_CF_DO || tk.type == TK_CF_WHILE) { + // do {} while() + // while() {} + bool is_do = tk.type == TK_CF_DO; + + BlockNode *do_block = NULL; + if (is_do) { + + do_block = alloc_node<BlockNode>(); + do_block->parent_block = p_block; + + Error err = _parse_block(do_block, p_builtin_types, true, true, true); + if (err) + return err; + + tk = _get_token(); + if (tk.type != TK_CF_WHILE) { + _set_error("Expected while after do"); + return ERR_PARSE_ERROR; + } + } + tk = _get_token(); + + if (tk.type != TK_PARENTHESIS_OPEN) { + _set_error("Expected '(' after while"); + return ERR_PARSE_ERROR; + } + + ControlFlowNode *cf = alloc_node<ControlFlowNode>(); + if (is_do) { + cf->flow_op = FLOW_OP_DO; + } else { + cf->flow_op = FLOW_OP_WHILE; + } + Node *n = _parse_and_reduce_expression(p_block, p_builtin_types); + if (!n) + return ERR_PARSE_ERROR; + + tk = _get_token(); + if (tk.type != TK_PARENTHESIS_CLOSE) { + _set_error("Expected ')' after expression"); + return ERR_PARSE_ERROR; + } + if (!is_do) { + BlockNode *block = alloc_node<BlockNode>(); + block->parent_block = p_block; + cf->expressions.push_back(n); + cf->blocks.push_back(block); + p_block->statements.push_back(cf); + + Error err = _parse_block(block, p_builtin_types, true, true, true); + if (err) + return err; + } else { + + cf->expressions.push_back(n); + cf->blocks.push_back(do_block); + p_block->statements.push_back(cf); + + tk = _get_token(); + if (tk.type != TK_SEMICOLON) { + _set_error("Expected ';'"); + return ERR_PARSE_ERROR; + } + } + } else if (tk.type == TK_CF_FOR) { + // for() {} + tk = _get_token(); + if (tk.type != TK_PARENTHESIS_OPEN) { + _set_error("Expected '(' after for"); + return ERR_PARSE_ERROR; + } + + ControlFlowNode *cf = alloc_node<ControlFlowNode>(); + cf->flow_op = FLOW_OP_FOR; + + BlockNode *init_block = alloc_node<BlockNode>(); + init_block->parent_block = p_block; + init_block->single_statement = true; + cf->blocks.push_back(init_block); + if (_parse_block(init_block, p_builtin_types, true, false, false) != OK) { + return ERR_PARSE_ERROR; + } + + Node *n = _parse_and_reduce_expression(init_block, p_builtin_types); + if (!n) + return ERR_PARSE_ERROR; + + if (n->get_datatype() != TYPE_BOOL) { + _set_error("Middle expression is expected to be boolean."); + return ERR_PARSE_ERROR; + } + + tk = _get_token(); + if (tk.type != TK_SEMICOLON) { + _set_error("Expected ';' after middle expression"); + return ERR_PARSE_ERROR; + } + + cf->expressions.push_back(n); + + n = _parse_and_reduce_expression(init_block, p_builtin_types); + if (!n) + return ERR_PARSE_ERROR; + + cf->expressions.push_back(n); + + tk = _get_token(); + if (tk.type != TK_PARENTHESIS_CLOSE) { + _set_error("Expected ')' after third expression"); + return ERR_PARSE_ERROR; + } + + BlockNode *block = alloc_node<BlockNode>(); + block->parent_block = init_block; + cf->blocks.push_back(block); + p_block->statements.push_back(cf); + + Error err = _parse_block(block, p_builtin_types, true, true, true); + if (err) + return err; + + } else if (tk.type == TK_CF_RETURN) { + + //check return type + BlockNode *b = p_block; + while (b && !b->parent_function) { + b = b->parent_block; + } + + if (!b) { + _set_error("Bug"); + return ERR_BUG; + } + + ControlFlowNode *flow = alloc_node<ControlFlowNode>(); + flow->flow_op = FLOW_OP_RETURN; + + pos = _get_tkpos(); + tk = _get_token(); + if (tk.type == TK_SEMICOLON) { + //all is good + if (b->parent_function->return_type != TYPE_VOID) { + _set_error("Expected return with expression of type '" + get_datatype_name(b->parent_function->return_type) + "'"); + return ERR_PARSE_ERROR; + } + } else { + _set_tkpos(pos); //rollback, wants expression + Node *expr = _parse_and_reduce_expression(p_block, p_builtin_types); + if (!expr) + return ERR_PARSE_ERROR; + + if (b->parent_function->return_type != expr->get_datatype()) { + _set_error("Expected return expression of type '" + get_datatype_name(b->parent_function->return_type) + "'"); + return ERR_PARSE_ERROR; + } + + tk = _get_token(); + if (tk.type != TK_SEMICOLON) { + _set_error("Expected ';' after return expression"); + return ERR_PARSE_ERROR; + } + + flow->expressions.push_back(expr); + } + + p_block->statements.push_back(flow); + + BlockNode *block = p_block; + while (block) { + if (block->block_type == BlockNode::BLOCK_TYPE_CASE || block->block_type == BlockNode::BLOCK_TYPE_DEFAULT) { + return OK; + } + block = block->parent_block; + } + } else if (tk.type == TK_CF_DISCARD) { + + //check return type + BlockNode *b = p_block; + while (b && !b->parent_function) { + b = b->parent_block; + } + if (!b) { + _set_error("Bug"); + return ERR_BUG; + } + + if (!b->parent_function->can_discard) { + _set_error("Use of 'discard' is not allowed here."); + return ERR_PARSE_ERROR; + } + + ControlFlowNode *flow = alloc_node<ControlFlowNode>(); + flow->flow_op = FLOW_OP_DISCARD; + + pos = _get_tkpos(); + tk = _get_token(); + if (tk.type != TK_SEMICOLON) { + //all is good + _set_error("Expected ';' after discard"); + } + + p_block->statements.push_back(flow); + } else if (tk.type == TK_CF_BREAK) { + + if (!p_can_break) { + //all is good + _set_error("Breaking is not allowed here"); + } + + ControlFlowNode *flow = alloc_node<ControlFlowNode>(); + flow->flow_op = FLOW_OP_BREAK; + + pos = _get_tkpos(); + tk = _get_token(); + if (tk.type != TK_SEMICOLON) { + //all is good + _set_error("Expected ';' after break"); + } + + p_block->statements.push_back(flow); + + BlockNode *block = p_block; + while (block) { + if (block->block_type == BlockNode::BLOCK_TYPE_CASE || block->block_type == BlockNode::BLOCK_TYPE_DEFAULT) { + return OK; + } + block = block->parent_block; + } + + } else if (tk.type == TK_CF_CONTINUE) { + + if (!p_can_continue) { + //all is good + _set_error("Continuing is not allowed here"); + } + + ControlFlowNode *flow = alloc_node<ControlFlowNode>(); + flow->flow_op = FLOW_OP_CONTINUE; + + pos = _get_tkpos(); + tk = _get_token(); + if (tk.type != TK_SEMICOLON) { + //all is good + _set_error("Expected ';' after continue"); + } + + p_block->statements.push_back(flow); + + } else { + + //nothing else, so expression + _set_tkpos(pos); //rollback + Node *expr = _parse_and_reduce_expression(p_block, p_builtin_types); + if (!expr) + return ERR_PARSE_ERROR; + p_block->statements.push_back(expr); + tk = _get_token(); + + if (tk.type != TK_SEMICOLON) { + _set_error("Expected ';' after statement"); + return ERR_PARSE_ERROR; + } + } + + if (p_just_one) + break; + } + + return OK; +} + +String ShaderLanguage::_get_shader_type_list(const Set<String> &p_shader_types) const { + + // Return a list of shader types as an human-readable string + String valid_types; + for (const Set<String>::Element *E = p_shader_types.front(); E; E = E->next()) { + if (valid_types != String()) { + valid_types += ", "; + } + + valid_types += "'" + E->get() + "'"; + } + + return valid_types; +} + +String ShaderLanguage::_get_qualifier_str(ArgumentQualifier p_qualifier) const { + switch (p_qualifier) { + case ArgumentQualifier::ARGUMENT_QUALIFIER_IN: + return "in"; + case ArgumentQualifier::ARGUMENT_QUALIFIER_OUT: + return "out"; + case ArgumentQualifier::ARGUMENT_QUALIFIER_INOUT: + return "inout"; + } + return ""; +} + +Error ShaderLanguage::_validate_datatype(DataType p_type) { + if (RenderingServer::get_singleton()->is_low_end()) { + bool invalid_type = false; + + switch (p_type) { + case TYPE_UINT: + case TYPE_UVEC2: + case TYPE_UVEC3: + case TYPE_UVEC4: + case TYPE_ISAMPLER2D: + case TYPE_USAMPLER2D: + case TYPE_ISAMPLER3D: + case TYPE_USAMPLER3D: + case TYPE_USAMPLER2DARRAY: + case TYPE_ISAMPLER2DARRAY: + invalid_type = true; + break; + default: + break; + } + + if (invalid_type) { + _set_error(vformat("\"%s\" type is supported only on high-end platform!", get_datatype_name(p_type))); + return ERR_UNAVAILABLE; + } + } + return OK; +} + +Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_functions, const Vector<StringName> &p_render_modes, const Set<String> &p_shader_types) { + + Token tk = _get_token(); + + if (tk.type != TK_SHADER_TYPE) { + _set_error("Expected 'shader_type' at the beginning of shader. Valid types are: " + _get_shader_type_list(p_shader_types)); + return ERR_PARSE_ERROR; + } + + tk = _get_token(); + + if (tk.type != TK_IDENTIFIER) { + _set_error("Expected identifier after 'shader_type', indicating type of shader. Valid types are: " + _get_shader_type_list(p_shader_types)); + return ERR_PARSE_ERROR; + } + + String shader_type_identifier; + + shader_type_identifier = tk.text; + + if (!p_shader_types.has(shader_type_identifier)) { + _set_error("Invalid shader type. Valid types are: " + _get_shader_type_list(p_shader_types)); + return ERR_PARSE_ERROR; + } + + tk = _get_token(); + + if (tk.type != TK_SEMICOLON) { + _set_error("Expected ';' after 'shader_type <type>'."); + } + + tk = _get_token(); + + int texture_uniforms = 0; + int uniforms = 0; + + while (tk.type != TK_EOF) { + + switch (tk.type) { + case TK_RENDER_MODE: { + + while (true) { + + StringName mode; + _get_completable_identifier(NULL, COMPLETION_RENDER_MODE, mode); + + if (mode == StringName()) { + _set_error("Expected identifier for render mode"); + return ERR_PARSE_ERROR; + } + + if (p_render_modes.find(mode) == -1) { + _set_error("Invalid render mode: '" + String(mode) + "'"); + return ERR_PARSE_ERROR; + } + + if (shader->render_modes.find(mode) != -1) { + _set_error("Duplicate render mode: '" + String(mode) + "'"); + return ERR_PARSE_ERROR; + } + + shader->render_modes.push_back(mode); + + tk = _get_token(); + if (tk.type == TK_COMMA) { + //all good, do nothing + } else if (tk.type == TK_SEMICOLON) { + break; //done + } else { + _set_error("Unexpected token: " + get_token_text(tk)); + return ERR_PARSE_ERROR; + } + } + } break; + case TK_STRUCT: { + ShaderNode::Struct st; + DataType type; + + tk = _get_token(); + if (tk.type == TK_IDENTIFIER) { + st.name = tk.text; + tk = _get_token(); + if (tk.type != TK_CURLY_BRACKET_OPEN) { + _set_error("Expected '{'"); + return ERR_PARSE_ERROR; + } + } else { + _set_error("Expected struct identifier!"); + return ERR_PARSE_ERROR; + } + + StructNode *st_node = alloc_node<StructNode>(); + st.shader_struct = st_node; + + int member_count = 0; + Set<String> member_names; + while (true) { // variables list + tk = _get_token(); + if (tk.type == TK_CURLY_BRACKET_CLOSE) { + break; + } + StringName struct_name = ""; + bool struct_dt = false; + bool use_precision = false; + DataPrecision precision = DataPrecision::PRECISION_DEFAULT; + + if (tk.type == TK_STRUCT) { + _set_error("nested structs are not allowed!"); + return ERR_PARSE_ERROR; + } + + if (is_token_precision(tk.type)) { + precision = get_token_precision(tk.type); + use_precision = true; + tk = _get_token(); + } + + if (shader->structs.has(tk.text)) { + struct_name = tk.text; + struct_dt = true; + if (use_precision) { + _set_error("Precision modifier cannot be used on structs."); + return ERR_PARSE_ERROR; + } + } + + if (!is_token_datatype(tk.type) && !struct_dt) { + _set_error("Expected datatype."); + return ERR_PARSE_ERROR; + } else { + type = struct_dt ? TYPE_STRUCT : get_token_datatype(tk.type); + + if (is_sampler_type(type)) { + _set_error("sampler datatype not allowed here"); + return ERR_PARSE_ERROR; + } else if (type == TYPE_VOID) { + _set_error("void datatype not allowed here"); + return ERR_PARSE_ERROR; + } + + tk = _get_token(); + if (tk.type != TK_IDENTIFIER) { + _set_error("Expected identifier!"); + return ERR_PARSE_ERROR; + } + + MemberNode *member = alloc_node<MemberNode>(); + member->precision = precision; + member->datatype = type; + member->struct_name = struct_name; + member->name = tk.text; + + if (member_names.has(member->name)) { + _set_error("Redefinition of '" + String(member->name) + "'"); + return ERR_PARSE_ERROR; + } + member_names.insert(member->name); + + tk = _get_token(); + if (tk.type == TK_BRACKET_OPEN) { + tk = _get_token(); + if (tk.type == TK_INT_CONSTANT && tk.constant > 0) { + member->array_size = (int)tk.constant; + + tk = _get_token(); + if (tk.type == TK_BRACKET_CLOSE) { + tk = _get_token(); + if (tk.type != TK_SEMICOLON) { + _set_error("Expected ';'"); + return ERR_PARSE_ERROR; + } + } else { + _set_error("Expected ']'"); + return ERR_PARSE_ERROR; + } + } else { + _set_error("Expected single integer constant > 0"); + return ERR_PARSE_ERROR; + } + } + st_node->members.push_back(member); + + if (tk.type != TK_SEMICOLON) { + _set_error("Expected ']' or ';'"); + return ERR_PARSE_ERROR; + } + member_count++; + } + } + if (member_count == 0) { + _set_error("Empty structs are not allowed!"); + return ERR_PARSE_ERROR; + } + + tk = _get_token(); + if (tk.type != TK_SEMICOLON) { + _set_error("Expected ';'"); + return ERR_PARSE_ERROR; + } + shader->structs[st.name] = st; + shader->vstructs.push_back(st); // struct's order is important! + + } break; + case TK_UNIFORM: + case TK_VARYING: { + + bool uniform = tk.type == TK_UNIFORM; + DataPrecision precision = PRECISION_DEFAULT; + DataInterpolation interpolation = INTERPOLATION_SMOOTH; + DataType type; + StringName name; + + tk = _get_token(); + if (is_token_interpolation(tk.type)) { + interpolation = get_token_interpolation(tk.type); + tk = _get_token(); + } + + if (is_token_precision(tk.type)) { + precision = get_token_precision(tk.type); + tk = _get_token(); + } + + if (!is_token_datatype(tk.type)) { + _set_error("Expected datatype. "); + return ERR_PARSE_ERROR; + } + + type = get_token_datatype(tk.type); + + if (type == TYPE_VOID) { + _set_error("void datatype not allowed here"); + return ERR_PARSE_ERROR; + } + + if (!uniform && (type < TYPE_FLOAT || type > TYPE_MAT4)) { + _set_error("Invalid type for varying, only float,vec2,vec3,vec4,mat2,mat3,mat4 or array of these types allowed."); + return ERR_PARSE_ERROR; + } + + tk = _get_token(); + if (tk.type != TK_IDENTIFIER) { + _set_error("Expected identifier!"); + return ERR_PARSE_ERROR; + } + + name = tk.text; + + if (_find_identifier(NULL, false, Map<StringName, BuiltInInfo>(), name)) { + _set_error("Redefinition of '" + String(name) + "'"); + return ERR_PARSE_ERROR; + } + + if (has_builtin(p_functions, name)) { + _set_error("Redefinition of '" + String(name) + "'"); + return ERR_PARSE_ERROR; + } + + if (uniform) { + + ShaderNode::Uniform uniform2; + + if (is_sampler_type(type)) { + uniform2.texture_order = texture_uniforms++; + uniform2.order = -1; + if (_validate_datatype(type) != OK) { + return ERR_PARSE_ERROR; + } + } else { + uniform2.texture_order = -1; + uniform2.order = uniforms++; + } + uniform2.type = type; + uniform2.precision = precision; + + //todo parse default value + + tk = _get_token(); + + if (tk.type == TK_COLON) { + //hint + do { + tk = _get_token(); + if (tk.type == TK_HINT_WHITE_TEXTURE) { + uniform2.hint = ShaderNode::Uniform::HINT_WHITE; + } else if (tk.type == TK_HINT_BLACK_TEXTURE) { + uniform2.hint = ShaderNode::Uniform::HINT_BLACK; + } else if (tk.type == TK_HINT_NORMAL_TEXTURE) { + uniform2.hint = ShaderNode::Uniform::HINT_NORMAL; + } else if (tk.type == TK_HINT_ROUGHNESS_NORMAL_TEXTURE) { + uniform2.hint = ShaderNode::Uniform::HINT_ROUGHNESS_NORMAL; + } else if (tk.type == TK_HINT_ROUGHNESS_R) { + uniform2.hint = ShaderNode::Uniform::HINT_ROUGHNESS_R; + } else if (tk.type == TK_HINT_ROUGHNESS_G) { + uniform2.hint = ShaderNode::Uniform::HINT_ROUGHNESS_G; + } else if (tk.type == TK_HINT_ROUGHNESS_B) { + uniform2.hint = ShaderNode::Uniform::HINT_ROUGHNESS_B; + } else if (tk.type == TK_HINT_ROUGHNESS_A) { + uniform2.hint = ShaderNode::Uniform::HINT_ROUGHNESS_A; + } else if (tk.type == TK_HINT_ROUGHNESS_GRAY) { + uniform2.hint = ShaderNode::Uniform::HINT_ROUGHNESS_GRAY; + } else if (tk.type == TK_HINT_ANISO_TEXTURE) { + uniform2.hint = ShaderNode::Uniform::HINT_ANISO; + } else if (tk.type == TK_HINT_ALBEDO_TEXTURE) { + uniform2.hint = ShaderNode::Uniform::HINT_ALBEDO; + } else if (tk.type == TK_HINT_BLACK_ALBEDO_TEXTURE) { + uniform2.hint = ShaderNode::Uniform::HINT_BLACK_ALBEDO; + } else if (tk.type == TK_HINT_COLOR) { + if (type != TYPE_VEC4) { + _set_error("Color hint is for vec4 only"); + return ERR_PARSE_ERROR; + } + uniform2.hint = ShaderNode::Uniform::HINT_COLOR; + } else if (tk.type == TK_HINT_RANGE) { + + uniform2.hint = ShaderNode::Uniform::HINT_RANGE; + if (type != TYPE_FLOAT && type != TYPE_INT) { + _set_error("Range hint is for float and int only"); + return ERR_PARSE_ERROR; + } + + tk = _get_token(); + if (tk.type != TK_PARENTHESIS_OPEN) { + _set_error("Expected '(' after hint_range"); + return ERR_PARSE_ERROR; + } + + tk = _get_token(); + + float sign = 1.0; + + if (tk.type == TK_OP_SUB) { + sign = -1.0; + tk = _get_token(); + } + + if (tk.type != TK_REAL_CONSTANT && tk.type != TK_INT_CONSTANT) { + _set_error("Expected integer constant"); + return ERR_PARSE_ERROR; + } + + uniform2.hint_range[0] = tk.constant; + uniform2.hint_range[0] *= sign; + + tk = _get_token(); + + if (tk.type != TK_COMMA) { + _set_error("Expected ',' after integer constant"); + return ERR_PARSE_ERROR; + } + + tk = _get_token(); + + sign = 1.0; + + if (tk.type == TK_OP_SUB) { + sign = -1.0; + tk = _get_token(); + } + + if (tk.type != TK_REAL_CONSTANT && tk.type != TK_INT_CONSTANT) { + _set_error("Expected integer constant after ','"); + return ERR_PARSE_ERROR; + } + + uniform2.hint_range[1] = tk.constant; + uniform2.hint_range[1] *= sign; + + tk = _get_token(); + + if (tk.type == TK_COMMA) { + tk = _get_token(); + + if (tk.type != TK_REAL_CONSTANT && tk.type != TK_INT_CONSTANT) { + _set_error("Expected integer constant after ','"); + return ERR_PARSE_ERROR; + } + + uniform2.hint_range[2] = tk.constant; + tk = _get_token(); + } else { + if (type == TYPE_INT) { + uniform2.hint_range[2] = 1; + } else { + uniform2.hint_range[2] = 0.001; + } + } + + if (tk.type != TK_PARENTHESIS_CLOSE) { + _set_error("Expected ','"); + return ERR_PARSE_ERROR; + } + } else if (tk.type == TK_FILTER_LINEAR) { + uniform2.filter = FILTER_LINEAR; + } else if (tk.type == TK_FILTER_NEAREST) { + uniform2.filter = FILTER_NEAREST; + } else if (tk.type == TK_FILTER_NEAREST_MIPMAP) { + uniform2.filter = FILTER_NEAREST_MIPMAP; + } else if (tk.type == TK_FILTER_LINEAR_MIPMAP) { + uniform2.filter = FILTER_LINEAR_MIPMAP; + } else if (tk.type == TK_FILTER_NEAREST_MIPMAP_ANISO) { + uniform2.filter = FILTER_NEAREST_MIPMAP_ANISO; + } else if (tk.type == TK_FILTER_LINEAR_MIPMAP_ANISO) { + uniform2.filter = FILTER_LINEAR_MIPMAP_ANISO; + } else if (tk.type == TK_REPEAT_DISABLE) { + uniform2.repeat = REPEAT_DISABLE; + } else if (tk.type == TK_REPEAT_ENABLE) { + uniform2.repeat = REPEAT_ENABLE; + } else { + _set_error("Expected valid type hint after ':'."); + } + + if (uniform2.hint != ShaderNode::Uniform::HINT_RANGE && uniform2.hint != ShaderNode::Uniform::HINT_NONE && uniform2.hint != ShaderNode::Uniform::HINT_COLOR && type <= TYPE_MAT4) { + _set_error("This hint is only for sampler types"); + return ERR_PARSE_ERROR; + } + + tk = _get_token(); + + } while (tk.type == TK_COMMA); + } + + if (tk.type == TK_OP_ASSIGN) { + + Node *expr = _parse_and_reduce_expression(NULL, Map<StringName, BuiltInInfo>()); + if (!expr) + return ERR_PARSE_ERROR; + if (expr->type != Node::TYPE_CONSTANT) { + _set_error("Expected constant expression after '='"); + return ERR_PARSE_ERROR; + } + + ConstantNode *cn = static_cast<ConstantNode *>(expr); + + uniform2.default_value.resize(cn->values.size()); + + if (!convert_constant(cn, uniform2.type, uniform2.default_value.ptrw())) { + _set_error("Can't convert constant to " + get_datatype_name(uniform2.type)); + return ERR_PARSE_ERROR; + } + tk = _get_token(); + } + + shader->uniforms[name] = uniform2; + + if (tk.type != TK_SEMICOLON) { + _set_error("Expected ';'"); + return ERR_PARSE_ERROR; + } + } else { + + ShaderNode::Varying varying; + varying.type = type; + varying.precision = precision; + varying.interpolation = interpolation; + + tk = _get_token(); + if (tk.type != TK_SEMICOLON && tk.type != TK_BRACKET_OPEN) { + _set_error("Expected ';' or '['"); + return ERR_PARSE_ERROR; + } + + if (tk.type == TK_BRACKET_OPEN) { + tk = _get_token(); + if (tk.type == TK_INT_CONSTANT && tk.constant > 0) { + varying.array_size = (int)tk.constant; + + tk = _get_token(); + if (tk.type == TK_BRACKET_CLOSE) { + tk = _get_token(); + if (tk.type != TK_SEMICOLON) { + _set_error("Expected ';'"); + return ERR_PARSE_ERROR; + } + } else { + _set_error("Expected ']'"); + return ERR_PARSE_ERROR; + } + } else { + _set_error("Expected single integer constant > 0"); + return ERR_PARSE_ERROR; + } + } + + shader->varyings[name] = varying; + } + + } break; + default: { + //function or constant variable + + bool is_constant = false; + bool is_struct = false; + StringName struct_name; + DataPrecision precision = PRECISION_DEFAULT; + DataType type; + StringName name; + + if (tk.type == TK_CONST) { + is_constant = true; + tk = _get_token(); + } + + if (is_token_precision(tk.type)) { + precision = get_token_precision(tk.type); + tk = _get_token(); + } + + if (shader->structs.has(tk.text)) { + if (precision != PRECISION_DEFAULT) { + _set_error("Precision modifier cannot be used on structs."); + return ERR_PARSE_ERROR; + } + is_struct = true; + struct_name = tk.text; + } else { + + if (!is_token_datatype(tk.type)) { + _set_error("Expected constant, function, uniform or varying"); + return ERR_PARSE_ERROR; + } + + if (!is_token_variable_datatype(tk.type)) { + _set_error("Invalid data type for constants or function return (samplers not allowed)"); + return ERR_PARSE_ERROR; + } + } + + if (is_struct) { + type = TYPE_STRUCT; + } else { + type = get_token_datatype(tk.type); + } + TkPos prev_pos = _get_tkpos(); + tk = _get_token(); + if (tk.type == TK_BRACKET_OPEN) { + _set_error("Cannot use arrays as return types"); + return ERR_PARSE_ERROR; + } + _set_tkpos(prev_pos); + + _get_completable_identifier(NULL, COMPLETION_MAIN_FUNCTION, name); + + if (name == StringName()) { + _set_error("Expected function name after datatype"); + return ERR_PARSE_ERROR; + } + + if (_find_identifier(NULL, false, Map<StringName, BuiltInInfo>(), name)) { + _set_error("Redefinition of '" + String(name) + "'"); + return ERR_PARSE_ERROR; + } + + if (has_builtin(p_functions, name)) { + _set_error("Redefinition of '" + String(name) + "'"); + return ERR_PARSE_ERROR; + } + + tk = _get_token(); + if (tk.type != TK_PARENTHESIS_OPEN) { + if (type == TYPE_VOID) { + _set_error("Expected '(' after function identifier"); + return ERR_PARSE_ERROR; + } + + //variable + + while (true) { + ShaderNode::Constant constant; + constant.type = is_struct ? TYPE_STRUCT : type; + constant.type_str = struct_name; + constant.precision = precision; + constant.initializer = NULL; + + if (tk.type == TK_OP_ASSIGN) { + + if (!is_constant) { + _set_error("Expected 'const' keyword before constant definition"); + return ERR_PARSE_ERROR; + } + + //variable created with assignment! must parse an expression + Node *expr = _parse_and_reduce_expression(NULL, Map<StringName, BuiltInInfo>()); + if (!expr) + return ERR_PARSE_ERROR; + if (expr->type == Node::TYPE_OPERATOR && ((OperatorNode *)expr)->op == OP_CALL) { + _set_error("Expected constant expression after '='"); + return ERR_PARSE_ERROR; + } + + constant.initializer = static_cast<ConstantNode *>(expr); + + if (is_struct) { + if (expr->get_datatype_name() != struct_name) { + _set_error("Invalid assignment of '" + (expr->get_datatype() == TYPE_STRUCT ? expr->get_datatype_name() : get_datatype_name(expr->get_datatype())) + "' to '" + struct_name + "'"); + return ERR_PARSE_ERROR; + } + } else if (type != expr->get_datatype()) { + _set_error("Invalid assignment of '" + get_datatype_name(expr->get_datatype()) + "' to '" + get_datatype_name(type) + "'"); + return ERR_PARSE_ERROR; + } + tk = _get_token(); + } else { + _set_error("Expected initialization of constant"); + return ERR_PARSE_ERROR; + } + + shader->constants[name] = constant; + if (tk.type == TK_COMMA) { + tk = _get_token(); + if (tk.type != TK_IDENTIFIER) { + _set_error("Expected identifier after type"); + return ERR_PARSE_ERROR; + } + + name = tk.text; + if (_find_identifier(NULL, false, Map<StringName, BuiltInInfo>(), name)) { + _set_error("Redefinition of '" + String(name) + "'"); + return ERR_PARSE_ERROR; + } + + if (has_builtin(p_functions, name)) { + _set_error("Redefinition of '" + String(name) + "'"); + return ERR_PARSE_ERROR; + } + + tk = _get_token(); + + } else if (tk.type == TK_SEMICOLON) { + break; + } else { + _set_error("Expected ',' or ';' after constant"); + return ERR_PARSE_ERROR; + } + } + + break; + } + + Map<StringName, BuiltInInfo> builtin_types; + if (p_functions.has(name)) { + builtin_types = p_functions[name].built_ins; + } + + if (p_functions.has("global")) { // Adds global variables: 'TIME' + for (Map<StringName, BuiltInInfo>::Element *E = p_functions["global"].built_ins.front(); E; E = E->next()) { + builtin_types.insert(E->key(), E->value()); + } + } + + ShaderNode::Function function; + + function.callable = !p_functions.has(name); + function.name = name; + + FunctionNode *func_node = alloc_node<FunctionNode>(); + + function.function = func_node; + + shader->functions.push_back(function); + + func_node->name = name; + func_node->return_type = type; + func_node->return_struct_name = struct_name; + func_node->return_precision = precision; + + if (p_functions.has(name)) { + func_node->can_discard = p_functions[name].can_discard; + } + + func_node->body = alloc_node<BlockNode>(); + func_node->body->parent_function = func_node; + + tk = _get_token(); + + while (true) { + if (tk.type == TK_PARENTHESIS_CLOSE) { + break; + } + + ArgumentQualifier qualifier = ARGUMENT_QUALIFIER_IN; + + if (tk.type == TK_ARG_IN) { + qualifier = ARGUMENT_QUALIFIER_IN; + tk = _get_token(); + } else if (tk.type == TK_ARG_OUT) { + qualifier = ARGUMENT_QUALIFIER_OUT; + tk = _get_token(); + } else if (tk.type == TK_ARG_INOUT) { + qualifier = ARGUMENT_QUALIFIER_INOUT; + tk = _get_token(); + } + + DataType ptype; + StringName pname; + StringName param_struct_name; + DataPrecision pprecision = PRECISION_DEFAULT; + bool use_precision = false; + + if (is_token_precision(tk.type)) { + pprecision = get_token_precision(tk.type); + tk = _get_token(); + use_precision = true; + } + + is_struct = false; + + if (shader->structs.has(tk.text)) { + is_struct = true; + param_struct_name = tk.text; + if (use_precision) { + _set_error("Precision modifier cannot be used on structs."); + return ERR_PARSE_ERROR; + } + } + + if (!is_struct && !is_token_datatype(tk.type)) { + _set_error("Expected a valid datatype for argument"); + return ERR_PARSE_ERROR; + } + + if (qualifier == ARGUMENT_QUALIFIER_OUT || qualifier == ARGUMENT_QUALIFIER_INOUT) { + if (is_sampler_type(get_token_datatype(tk.type))) { + _set_error("Opaque types cannot be output parameters."); + return ERR_PARSE_ERROR; + } + } + + if (is_struct) { + ptype = TYPE_STRUCT; + } else { + ptype = get_token_datatype(tk.type); + if (_validate_datatype(ptype) != OK) { + return ERR_PARSE_ERROR; + } + if (ptype == TYPE_VOID) { + _set_error("void not allowed in argument"); + return ERR_PARSE_ERROR; + } + } + + tk = _get_token(); + + if (tk.type == TK_BRACKET_OPEN) { + _set_error("Arrays as parameters are not implemented yet"); + return ERR_PARSE_ERROR; + } + if (tk.type != TK_IDENTIFIER) { + _set_error("Expected identifier for argument name"); + return ERR_PARSE_ERROR; + } + + pname = tk.text; + + ShaderLanguage::IdentifierType itype; + if (_find_identifier(func_node->body, false, builtin_types, pname, (ShaderLanguage::DataType *)0, &itype)) { + if (itype != IDENTIFIER_FUNCTION) { + _set_error("Redefinition of '" + String(pname) + "'"); + return ERR_PARSE_ERROR; + } + } + + if (has_builtin(p_functions, pname)) { + _set_error("Redefinition of '" + String(pname) + "'"); + return ERR_PARSE_ERROR; + } + + FunctionNode::Argument arg; + arg.type = ptype; + arg.name = pname; + arg.type_str = param_struct_name; + arg.precision = pprecision; + arg.qualifier = qualifier; + arg.tex_argument_check = false; + arg.tex_builtin_check = false; + arg.tex_argument_filter = FILTER_DEFAULT; + arg.tex_argument_repeat = REPEAT_DEFAULT; + + func_node->arguments.push_back(arg); + + tk = _get_token(); + if (tk.type == TK_BRACKET_OPEN) { + _set_error("Arrays as parameters are not implemented yet"); + return ERR_PARSE_ERROR; + } + + if (tk.type == TK_COMMA) { + tk = _get_token(); + //do none and go on + } else if (tk.type != TK_PARENTHESIS_CLOSE) { + _set_error("Expected ',' or ')' after identifier"); + return ERR_PARSE_ERROR; + } + } + + if (p_functions.has(name)) { + //if one of the core functions, make sure they are of the correct form + if (func_node->arguments.size() > 0) { + _set_error("Function '" + String(name) + "' expects no arguments."); + return ERR_PARSE_ERROR; + } + if (func_node->return_type != TYPE_VOID) { + _set_error("Function '" + String(name) + "' must be of void return type."); + return ERR_PARSE_ERROR; + } + } + + //all good let's parse inside the function! + tk = _get_token(); + if (tk.type != TK_CURLY_BRACKET_OPEN) { + _set_error("Expected '{' to begin function"); + return ERR_PARSE_ERROR; + } + + current_function = name; + + Error err = _parse_block(func_node->body, builtin_types); + if (err) + return err; + + if (func_node->return_type != DataType::TYPE_VOID) { + + BlockNode *block = func_node->body; + if (_find_last_flow_op_in_block(block, FlowOperation::FLOW_OP_RETURN) != OK) { + _set_error("Expected at least one return statement in a non-void function."); + return ERR_PARSE_ERROR; + } + } + current_function = StringName(); + } + } + + tk = _get_token(); + } + + return OK; +} + +bool ShaderLanguage::has_builtin(const Map<StringName, ShaderLanguage::FunctionInfo> &p_functions, const StringName &p_name) { + + if (p_functions.has("vertex")) { + if (p_functions["vertex"].built_ins.has(p_name)) { + return true; + } + } + if (p_functions.has("fragment")) { + if (p_functions["fragment"].built_ins.has(p_name)) { + return true; + } + } + if (p_functions.has("light")) { + if (p_functions["light"].built_ins.has(p_name)) { + return true; + } + } + return false; +} + +Error ShaderLanguage::_find_last_flow_op_in_op(ControlFlowNode *p_flow, FlowOperation p_op) { + + bool found = false; + + for (int i = p_flow->blocks.size() - 1; i >= 0; i--) { + if (p_flow->blocks[i]->type == Node::TYPE_BLOCK) { + BlockNode *last_block = (BlockNode *)p_flow->blocks[i]; + if (_find_last_flow_op_in_block(last_block, p_op) == OK) { + found = true; + break; + } + } + } + if (found) { + return OK; + } + return FAILED; +} + +Error ShaderLanguage::_find_last_flow_op_in_block(BlockNode *p_block, FlowOperation p_op) { + + bool found = false; + + for (int i = p_block->statements.size() - 1; i >= 0; i--) { + + if (p_block->statements[i]->type == Node::TYPE_CONTROL_FLOW) { + ControlFlowNode *flow = (ControlFlowNode *)p_block->statements[i]; + if (flow->flow_op == p_op) { + found = true; + break; + } else { + if (_find_last_flow_op_in_op(flow, p_op) == OK) { + found = true; + break; + } + } + } else if (p_block->statements[i]->type == Node::TYPE_BLOCK) { + BlockNode *block = (BlockNode *)p_block->statements[i]; + if (_find_last_flow_op_in_block(block, p_op) == OK) { + found = true; + break; + } + } + } + + if (found) { + return OK; + } + return FAILED; +} + +// skips over whitespace and /* */ and // comments +static int _get_first_ident_pos(const String &p_code) { + + int idx = 0; + +#define GETCHAR(m_idx) (((idx + m_idx) < p_code.length()) ? p_code[idx + m_idx] : CharType(0)) + + while (true) { + if (GETCHAR(0) == '/' && GETCHAR(1) == '/') { + idx += 2; + while (true) { + if (GETCHAR(0) == 0) return 0; + if (GETCHAR(0) == '\n') { + idx++; + break; // loop + } + idx++; + } + } else if (GETCHAR(0) == '/' && GETCHAR(1) == '*') { + idx += 2; + while (true) { + if (GETCHAR(0) == 0) return 0; + if (GETCHAR(0) == '*' && GETCHAR(1) == '/') { + idx += 2; + break; // loop + } + idx++; + } + } else { + switch (GETCHAR(0)) { + case ' ': + case '\t': + case '\r': + case '\n': { + idx++; + } break; // switch + default: + return idx; + } + } + } + +#undef GETCHAR +} + +String ShaderLanguage::get_shader_type(const String &p_code) { + + bool reading_type = false; + + String cur_identifier; + + for (int i = _get_first_ident_pos(p_code); i < p_code.length(); i++) { + + if (p_code[i] == ';') { + break; + + } else if (p_code[i] <= 32) { + if (cur_identifier != String()) { + if (!reading_type) { + if (cur_identifier != "shader_type") { + return String(); + } + + reading_type = true; + cur_identifier = String(); + } else { + return cur_identifier; + } + } + } else { + cur_identifier += String::chr(p_code[i]); + } + } + + if (reading_type) + return cur_identifier; + + return String(); +} + +Error ShaderLanguage::compile(const String &p_code, const Map<StringName, FunctionInfo> &p_functions, const Vector<StringName> &p_render_modes, const Set<String> &p_shader_types) { + + clear(); + + code = p_code; + + nodes = NULL; + + shader = alloc_node<ShaderNode>(); + Error err = _parse_shader(p_functions, p_render_modes, p_shader_types); + + if (err != OK) { + return err; + } + return OK; +} + +Error ShaderLanguage::complete(const String &p_code, const Map<StringName, FunctionInfo> &p_functions, const Vector<StringName> &p_render_modes, const Set<String> &p_shader_types, List<ScriptCodeCompletionOption> *r_options, String &r_call_hint) { + + clear(); + + code = p_code; + + nodes = NULL; + + shader = alloc_node<ShaderNode>(); + _parse_shader(p_functions, p_render_modes, p_shader_types); + + switch (completion_type) { + + case COMPLETION_NONE: { + //do nothing + return OK; + } break; + case COMPLETION_RENDER_MODE: { + for (int i = 0; i < p_render_modes.size(); i++) { + ScriptCodeCompletionOption option(p_render_modes[i], ScriptCodeCompletionOption::KIND_ENUM); + r_options->push_back(option); + } + + return OK; + } break; + case COMPLETION_STRUCT: { + + if (shader->structs.has(completion_struct)) { + StructNode *node = shader->structs[completion_struct].shader_struct; + for (int i = 0; i < node->members.size(); i++) { + ScriptCodeCompletionOption option(node->members[i]->name, ScriptCodeCompletionOption::KIND_MEMBER); + r_options->push_back(option); + } + } + + return OK; + } break; + case COMPLETION_MAIN_FUNCTION: { + + for (const Map<StringName, FunctionInfo>::Element *E = p_functions.front(); E; E = E->next()) { + ScriptCodeCompletionOption option(E->key(), ScriptCodeCompletionOption::KIND_FUNCTION); + r_options->push_back(option); + } + + return OK; + } break; + case COMPLETION_IDENTIFIER: + case COMPLETION_FUNCTION_CALL: { + + bool comp_ident = completion_type == COMPLETION_IDENTIFIER; + Map<String, ScriptCodeCompletionOption::Kind> matches; + StringName skip_function; + BlockNode *block = completion_block; + + if (completion_class == TAG_GLOBAL) { + while (block) { + if (comp_ident) { + for (const Map<StringName, BlockNode::Variable>::Element *E = block->variables.front(); E; E = E->next()) { + + if (E->get().line < completion_line) { + matches.insert(E->key(), ScriptCodeCompletionOption::KIND_VARIABLE); + } + } + } + + if (block->parent_function) { + if (comp_ident) { + for (int i = 0; i < block->parent_function->arguments.size(); i++) { + matches.insert(block->parent_function->arguments[i].name, ScriptCodeCompletionOption::KIND_VARIABLE); + } + } + skip_function = block->parent_function->name; + } + block = block->parent_block; + } + + if (comp_ident) { + if (p_functions.has("global")) { + for (Map<StringName, BuiltInInfo>::Element *E = p_functions["global"].built_ins.front(); E; E = E->next()) { + ScriptCodeCompletionOption::Kind kind = ScriptCodeCompletionOption::KIND_MEMBER; + if (E->get().constant) { + kind = ScriptCodeCompletionOption::KIND_CONSTANT; + } + matches.insert(E->key(), kind); + } + } + + if (skip_function != StringName() && p_functions.has(skip_function)) { + for (Map<StringName, BuiltInInfo>::Element *E = p_functions[skip_function].built_ins.front(); E; E = E->next()) { + ScriptCodeCompletionOption::Kind kind = ScriptCodeCompletionOption::KIND_MEMBER; + if (E->get().constant) { + kind = ScriptCodeCompletionOption::KIND_CONSTANT; + } + matches.insert(E->key(), kind); + } + } + + for (const Map<StringName, ShaderNode::Varying>::Element *E = shader->varyings.front(); E; E = E->next()) { + matches.insert(E->key(), ScriptCodeCompletionOption::KIND_VARIABLE); + } + for (const Map<StringName, ShaderNode::Uniform>::Element *E = shader->uniforms.front(); E; E = E->next()) { + matches.insert(E->key(), ScriptCodeCompletionOption::KIND_MEMBER); + } + } + + for (int i = 0; i < shader->functions.size(); i++) { + if (!shader->functions[i].callable || shader->functions[i].name == skip_function) + continue; + matches.insert(String(shader->functions[i].name), ScriptCodeCompletionOption::KIND_FUNCTION); + } + + int idx = 0; + bool low_end = RenderingServer::get_singleton()->is_low_end(); + + while (builtin_func_defs[idx].name) { + if (low_end && builtin_func_defs[idx].high_end) { + idx++; + continue; + } + matches.insert(String(builtin_func_defs[idx].name), ScriptCodeCompletionOption::KIND_FUNCTION); + idx++; + } + + } else { // sub-class + int idx = 0; + bool low_end = RenderingServer::get_singleton()->is_low_end(); + + while (builtin_func_defs[idx].name) { + if (low_end && builtin_func_defs[idx].high_end) { + idx++; + continue; + } + if (builtin_func_defs[idx].tag == completion_class) { + matches.insert(String(builtin_func_defs[idx].name), ScriptCodeCompletionOption::KIND_FUNCTION); + } + idx++; + } + } + + for (Map<String, ScriptCodeCompletionOption::Kind>::Element *E = matches.front(); E; E = E->next()) { + ScriptCodeCompletionOption option(E->key(), E->value()); + if (E->value() == ScriptCodeCompletionOption::KIND_FUNCTION) { + option.insert_text += "("; + } + r_options->push_back(option); + } + + return OK; + } break; + case COMPLETION_CALL_ARGUMENTS: { + + for (int i = 0; i < shader->functions.size(); i++) { + if (!shader->functions[i].callable) + continue; + if (shader->functions[i].name == completion_function) { + + String calltip; + + calltip += get_datatype_name(shader->functions[i].function->return_type); + calltip += " "; + calltip += shader->functions[i].name; + calltip += "("; + + for (int j = 0; j < shader->functions[i].function->arguments.size(); j++) { + + if (j > 0) + calltip += ", "; + else + calltip += " "; + + if (j == completion_argument) { + calltip += CharType(0xFFFF); + } + + if (shader->functions[i].function->arguments[j].qualifier != ArgumentQualifier::ARGUMENT_QUALIFIER_IN) { + if (shader->functions[i].function->arguments[j].qualifier == ArgumentQualifier::ARGUMENT_QUALIFIER_OUT) { + calltip += "out "; + } else { // ArgumentQualifier::ARGUMENT_QUALIFIER_INOUT + calltip += "inout "; + } + } + + calltip += get_datatype_name(shader->functions[i].function->arguments[j].type); + calltip += " "; + calltip += shader->functions[i].function->arguments[j].name; + + if (j == completion_argument) { + calltip += CharType(0xFFFF); + } + } + + if (shader->functions[i].function->arguments.size()) + calltip += " "; + calltip += ")"; + + r_call_hint = calltip; + return OK; + } + } + + int idx = 0; + + String calltip; + bool low_end = RenderingServer::get_singleton()->is_low_end(); + + while (builtin_func_defs[idx].name) { + + if (low_end && builtin_func_defs[idx].high_end) { + idx++; + continue; + } + + int idx2 = 0; + int out_arg = -1; + while (builtin_func_out_args[idx2].name != nullptr) { + if (builtin_func_out_args[idx2].name == builtin_func_defs[idx].name) { + out_arg = builtin_func_out_args[idx2].argument; + break; + } + idx2++; + } + + if (completion_function == builtin_func_defs[idx].name) { + + if (builtin_func_defs[idx].tag != completion_class) { + idx++; + continue; + } + + if (calltip.length()) + calltip += "\n"; + + calltip += get_datatype_name(builtin_func_defs[idx].rettype); + calltip += " "; + calltip += builtin_func_defs[idx].name; + calltip += "("; + + bool found_arg = false; + for (int i = 0; i < 4; i++) { + + if (builtin_func_defs[idx].args[i] == TYPE_VOID) + break; + + if (i > 0) + calltip += ", "; + else + calltip += " "; + + if (i == completion_argument) { + calltip += CharType(0xFFFF); + } + + if (out_arg >= 0 && i == out_arg) { + calltip += "out "; + } + + calltip += get_datatype_name(builtin_func_defs[idx].args[i]); + + if (i == completion_argument) { + calltip += CharType(0xFFFF); + } + + found_arg = true; + } + + if (found_arg) + calltip += " "; + calltip += ")"; + } + idx++; + } + + r_call_hint = calltip; + + return OK; + + } break; + case COMPLETION_INDEX: { + + const char colv[4] = { 'r', 'g', 'b', 'a' }; + const char coordv[4] = { 'x', 'y', 'z', 'w' }; + const char coordt[4] = { 's', 't', 'p', 'q' }; + + int limit = 0; + + switch (completion_base) { + case TYPE_BVEC2: + case TYPE_IVEC2: + case TYPE_UVEC2: + case TYPE_VEC2: { + limit = 2; + + } break; + case TYPE_BVEC3: + case TYPE_IVEC3: + case TYPE_UVEC3: + case TYPE_VEC3: { + + limit = 3; + + } break; + case TYPE_BVEC4: + case TYPE_IVEC4: + case TYPE_UVEC4: + case TYPE_VEC4: { + + limit = 4; + + } break; + case TYPE_MAT2: limit = 2; break; + case TYPE_MAT3: limit = 3; break; + case TYPE_MAT4: limit = 4; break; + default: { + } + } + + for (int i = 0; i < limit; i++) { + r_options->push_back(ScriptCodeCompletionOption(String::chr(colv[i]), ScriptCodeCompletionOption::KIND_PLAIN_TEXT)); + r_options->push_back(ScriptCodeCompletionOption(String::chr(coordv[i]), ScriptCodeCompletionOption::KIND_PLAIN_TEXT)); + r_options->push_back(ScriptCodeCompletionOption(String::chr(coordt[i]), ScriptCodeCompletionOption::KIND_PLAIN_TEXT)); + } + + } break; + } + + return ERR_PARSE_ERROR; +} + +String ShaderLanguage::get_error_text() { + + return error_str; +} + +int ShaderLanguage::get_error_line() { + + return error_line; +} + +ShaderLanguage::ShaderNode *ShaderLanguage::get_shader() { + + return shader; +} + +ShaderLanguage::ShaderLanguage() { + + nodes = NULL; + completion_class = TAG_GLOBAL; +} + +ShaderLanguage::~ShaderLanguage() { + + clear(); +} diff --git a/servers/rendering/shader_language.h b/servers/rendering/shader_language.h new file mode 100644 index 0000000000..bdc15cc4aa --- /dev/null +++ b/servers/rendering/shader_language.h @@ -0,0 +1,900 @@ +/*************************************************************************/ +/* shader_language.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef SHADER_LANGUAGE_H +#define SHADER_LANGUAGE_H + +#include "core/list.h" +#include "core/map.h" +#include "core/script_language.h" +#include "core/string_name.h" +#include "core/typedefs.h" +#include "core/ustring.h" +#include "core/variant.h" + +class ShaderLanguage { + +public: + enum TokenType { + TK_EMPTY, + TK_IDENTIFIER, + TK_TRUE, + TK_FALSE, + TK_REAL_CONSTANT, + TK_INT_CONSTANT, + TK_TYPE_VOID, + TK_TYPE_BOOL, + TK_TYPE_BVEC2, + TK_TYPE_BVEC3, + TK_TYPE_BVEC4, + TK_TYPE_INT, + TK_TYPE_IVEC2, + TK_TYPE_IVEC3, + TK_TYPE_IVEC4, + TK_TYPE_UINT, + TK_TYPE_UVEC2, + TK_TYPE_UVEC3, + TK_TYPE_UVEC4, + TK_TYPE_FLOAT, + TK_TYPE_VEC2, + TK_TYPE_VEC3, + TK_TYPE_VEC4, + TK_TYPE_MAT2, + TK_TYPE_MAT3, + TK_TYPE_MAT4, + TK_TYPE_SAMPLER2D, + TK_TYPE_ISAMPLER2D, + TK_TYPE_USAMPLER2D, + TK_TYPE_SAMPLER2DARRAY, + TK_TYPE_ISAMPLER2DARRAY, + TK_TYPE_USAMPLER2DARRAY, + TK_TYPE_SAMPLER3D, + TK_TYPE_ISAMPLER3D, + TK_TYPE_USAMPLER3D, + TK_TYPE_SAMPLERCUBE, + TK_INTERPOLATION_FLAT, + TK_INTERPOLATION_SMOOTH, + TK_CONST, + TK_STRUCT, + TK_PRECISION_LOW, + TK_PRECISION_MID, + TK_PRECISION_HIGH, + TK_OP_EQUAL, + TK_OP_NOT_EQUAL, + TK_OP_LESS, + TK_OP_LESS_EQUAL, + TK_OP_GREATER, + TK_OP_GREATER_EQUAL, + TK_OP_AND, + TK_OP_OR, + TK_OP_NOT, + TK_OP_ADD, + TK_OP_SUB, + TK_OP_MUL, + TK_OP_DIV, + TK_OP_MOD, + TK_OP_SHIFT_LEFT, + TK_OP_SHIFT_RIGHT, + TK_OP_ASSIGN, + TK_OP_ASSIGN_ADD, + TK_OP_ASSIGN_SUB, + TK_OP_ASSIGN_MUL, + TK_OP_ASSIGN_DIV, + TK_OP_ASSIGN_MOD, + TK_OP_ASSIGN_SHIFT_LEFT, + TK_OP_ASSIGN_SHIFT_RIGHT, + TK_OP_ASSIGN_BIT_AND, + TK_OP_ASSIGN_BIT_OR, + TK_OP_ASSIGN_BIT_XOR, + TK_OP_BIT_AND, + TK_OP_BIT_OR, + TK_OP_BIT_XOR, + TK_OP_BIT_INVERT, + TK_OP_INCREMENT, + TK_OP_DECREMENT, + TK_CF_IF, + TK_CF_ELSE, + TK_CF_FOR, + TK_CF_WHILE, + TK_CF_DO, + TK_CF_SWITCH, + TK_CF_CASE, + TK_CF_DEFAULT, + TK_CF_BREAK, + TK_CF_CONTINUE, + TK_CF_RETURN, + TK_CF_DISCARD, + TK_BRACKET_OPEN, + TK_BRACKET_CLOSE, + TK_CURLY_BRACKET_OPEN, + TK_CURLY_BRACKET_CLOSE, + TK_PARENTHESIS_OPEN, + TK_PARENTHESIS_CLOSE, + TK_QUESTION, + TK_COMMA, + TK_COLON, + TK_SEMICOLON, + TK_PERIOD, + TK_UNIFORM, + TK_VARYING, + TK_ARG_IN, + TK_ARG_OUT, + TK_ARG_INOUT, + TK_RENDER_MODE, + TK_HINT_WHITE_TEXTURE, + TK_HINT_BLACK_TEXTURE, + TK_HINT_NORMAL_TEXTURE, + TK_HINT_ROUGHNESS_NORMAL_TEXTURE, + TK_HINT_ROUGHNESS_R, + TK_HINT_ROUGHNESS_G, + TK_HINT_ROUGHNESS_B, + TK_HINT_ROUGHNESS_A, + TK_HINT_ROUGHNESS_GRAY, + TK_HINT_ANISO_TEXTURE, + TK_HINT_ALBEDO_TEXTURE, + TK_HINT_BLACK_ALBEDO_TEXTURE, + TK_HINT_COLOR, + TK_HINT_RANGE, + TK_FILTER_NEAREST, + TK_FILTER_LINEAR, + TK_FILTER_NEAREST_MIPMAP, + TK_FILTER_LINEAR_MIPMAP, + TK_FILTER_NEAREST_MIPMAP_ANISO, + TK_FILTER_LINEAR_MIPMAP_ANISO, + TK_REPEAT_ENABLE, + TK_REPEAT_DISABLE, + TK_SHADER_TYPE, + TK_CURSOR, + TK_ERROR, + TK_EOF, + TK_MAX + }; + +/* COMPILER */ + +// lame work around to Apple defining this as a macro in 10.12 SDK +#ifdef TYPE_BOOL +#undef TYPE_BOOL +#endif + + enum DataType { + TYPE_VOID, + TYPE_BOOL, + TYPE_BVEC2, + TYPE_BVEC3, + TYPE_BVEC4, + TYPE_INT, + TYPE_IVEC2, + TYPE_IVEC3, + TYPE_IVEC4, + TYPE_UINT, + TYPE_UVEC2, + TYPE_UVEC3, + TYPE_UVEC4, + TYPE_FLOAT, + TYPE_VEC2, + TYPE_VEC3, + TYPE_VEC4, + TYPE_MAT2, + TYPE_MAT3, + TYPE_MAT4, + TYPE_SAMPLER2D, + TYPE_ISAMPLER2D, + TYPE_USAMPLER2D, + TYPE_SAMPLER2DARRAY, + TYPE_ISAMPLER2DARRAY, + TYPE_USAMPLER2DARRAY, + TYPE_SAMPLER3D, + TYPE_ISAMPLER3D, + TYPE_USAMPLER3D, + TYPE_SAMPLERCUBE, + TYPE_STRUCT, + }; + + enum DataPrecision { + PRECISION_LOWP, + PRECISION_MEDIUMP, + PRECISION_HIGHP, + PRECISION_DEFAULT, + }; + + enum DataInterpolation { + INTERPOLATION_FLAT, + INTERPOLATION_SMOOTH, + }; + + enum Operator { + OP_EQUAL, + OP_NOT_EQUAL, + OP_LESS, + OP_LESS_EQUAL, + OP_GREATER, + OP_GREATER_EQUAL, + OP_AND, + OP_OR, + OP_NOT, + OP_NEGATE, + OP_ADD, + OP_SUB, + OP_MUL, + OP_DIV, + OP_MOD, + OP_SHIFT_LEFT, + OP_SHIFT_RIGHT, + OP_ASSIGN, + OP_ASSIGN_ADD, + OP_ASSIGN_SUB, + OP_ASSIGN_MUL, + OP_ASSIGN_DIV, + OP_ASSIGN_MOD, + OP_ASSIGN_SHIFT_LEFT, + OP_ASSIGN_SHIFT_RIGHT, + OP_ASSIGN_BIT_AND, + OP_ASSIGN_BIT_OR, + OP_ASSIGN_BIT_XOR, + OP_BIT_AND, + OP_BIT_OR, + OP_BIT_XOR, + OP_BIT_INVERT, + OP_INCREMENT, + OP_DECREMENT, + OP_SELECT_IF, + OP_SELECT_ELSE, //used only internally, then only IF appears with 3 arguments + OP_POST_INCREMENT, + OP_POST_DECREMENT, + OP_CALL, + OP_CONSTRUCT, + OP_STRUCT, + OP_INDEX, + OP_MAX + }; + + enum FlowOperation { + FLOW_OP_IF, + FLOW_OP_RETURN, + FLOW_OP_FOR, + FLOW_OP_WHILE, + FLOW_OP_DO, + FLOW_OP_BREAK, + FLOW_OP_SWITCH, + FLOW_OP_CASE, + FLOW_OP_DEFAULT, + FLOW_OP_CONTINUE, + FLOW_OP_DISCARD + }; + + enum ArgumentQualifier { + ARGUMENT_QUALIFIER_IN, + ARGUMENT_QUALIFIER_OUT, + ARGUMENT_QUALIFIER_INOUT, + }; + + enum SubClassTag { + TAG_GLOBAL, + TAG_ARRAY, + }; + + enum TextureFilter { + FILTER_NEAREST, + FILTER_LINEAR, + FILTER_NEAREST_MIPMAP, + FILTER_LINEAR_MIPMAP, + FILTER_NEAREST_MIPMAP_ANISO, + FILTER_LINEAR_MIPMAP_ANISO, + FILTER_DEFAULT, + }; + + enum TextureRepeat { + REPEAT_DISABLE, + REPEAT_ENABLE, + REPEAT_DEFAULT, + }; + + struct Node { + Node *next; + + enum Type { + TYPE_SHADER, + TYPE_FUNCTION, + TYPE_BLOCK, + TYPE_VARIABLE, + TYPE_VARIABLE_DECLARATION, + TYPE_CONSTANT, + TYPE_OPERATOR, + TYPE_CONTROL_FLOW, + TYPE_MEMBER, + TYPE_ARRAY, + TYPE_ARRAY_DECLARATION, + TYPE_ARRAY_CONSTRUCT, + TYPE_STRUCT, + }; + + Type type; + + virtual DataType get_datatype() const { return TYPE_VOID; } + virtual String get_datatype_name() const { return ""; } + + Node(Type t) : + next(NULL), + type(t) {} + virtual ~Node() {} + }; + + template <class T> + T *alloc_node() { + T *node = memnew(T); + node->next = nodes; + nodes = node; + return node; + } + + Node *nodes; + + struct OperatorNode : public Node { + DataType return_cache; + DataPrecision return_precision_cache; + Operator op; + StringName struct_name; + Vector<Node *> arguments; + virtual DataType get_datatype() const { return return_cache; } + virtual String get_datatype_name() const { return String(struct_name); } + + OperatorNode() : + Node(TYPE_OPERATOR), + return_cache(TYPE_VOID), + return_precision_cache(PRECISION_DEFAULT), + op(OP_EQUAL), + struct_name("") {} + }; + + struct VariableNode : public Node { + DataType datatype_cache; + StringName name; + StringName struct_name; + virtual DataType get_datatype() const { return datatype_cache; } + virtual String get_datatype_name() const { return String(struct_name); } + bool is_const; + + VariableNode() : + Node(TYPE_VARIABLE), + datatype_cache(TYPE_VOID), + is_const(false) {} + }; + + struct VariableDeclarationNode : public Node { + DataPrecision precision; + DataType datatype; + String struct_name; + bool is_const; + + struct Declaration { + StringName name; + Node *initializer; + }; + + Vector<Declaration> declarations; + virtual DataType get_datatype() const { return datatype; } + + VariableDeclarationNode() : + Node(TYPE_VARIABLE_DECLARATION), + precision(PRECISION_DEFAULT), + datatype(TYPE_VOID), + is_const(false) {} + }; + + struct ArrayNode : public Node { + DataType datatype_cache; + StringName struct_name; + StringName name; + Node *index_expression; + Node *call_expression; + bool is_const; + + virtual DataType get_datatype() const { return datatype_cache; } + virtual String get_datatype_name() const { return String(struct_name); } + + ArrayNode() : + Node(TYPE_ARRAY), + datatype_cache(TYPE_VOID), + index_expression(NULL), + call_expression(NULL), + is_const(false) {} + }; + + struct ArrayConstructNode : public Node { + DataType datatype; + String struct_name; + Vector<Node *> initializer; + + ArrayConstructNode() : + Node(TYPE_ARRAY_CONSTRUCT), + datatype(TYPE_VOID) { + } + }; + + struct ArrayDeclarationNode : public Node { + DataPrecision precision; + DataType datatype; + String struct_name; + bool is_const; + + struct Declaration { + StringName name; + uint32_t size; + Vector<Node *> initializer; + }; + + Vector<Declaration> declarations; + virtual DataType get_datatype() const { return datatype; } + + ArrayDeclarationNode() : + Node(TYPE_ARRAY_DECLARATION), + precision(PRECISION_DEFAULT), + datatype(TYPE_VOID), + is_const(false) {} + }; + + struct ConstantNode : public Node { + DataType datatype; + + union Value { + bool boolean; + float real; + int32_t sint; + uint32_t uint; + }; + + Vector<Value> values; + virtual DataType get_datatype() const { return datatype; } + + ConstantNode() : + Node(TYPE_CONSTANT), + datatype(TYPE_VOID) {} + }; + + struct FunctionNode; + + struct BlockNode : public Node { + FunctionNode *parent_function; + BlockNode *parent_block; + + enum BlockType { + BLOCK_TYPE_STANDART, + BLOCK_TYPE_SWITCH, + BLOCK_TYPE_CASE, + BLOCK_TYPE_DEFAULT, + }; + + int block_type; + SubClassTag block_tag; + + struct Variable { + DataType type; + StringName struct_name; + DataPrecision precision; + int line; //for completion + int array_size; + bool is_const; + }; + + Map<StringName, Variable> variables; + List<Node *> statements; + bool single_statement; + + BlockNode() : + Node(TYPE_BLOCK), + parent_function(NULL), + parent_block(NULL), + block_type(BLOCK_TYPE_STANDART), + block_tag(SubClassTag::TAG_GLOBAL), + single_statement(false) {} + }; + + struct ControlFlowNode : public Node { + FlowOperation flow_op; + Vector<Node *> expressions; + Vector<BlockNode *> blocks; + + ControlFlowNode() : + Node(TYPE_CONTROL_FLOW), + flow_op(FLOW_OP_IF) {} + }; + + struct MemberNode : public Node { + DataType basetype; + bool basetype_const; + StringName base_struct_name; + DataPrecision precision; + DataType datatype; + int array_size; + StringName struct_name; + StringName name; + Node *owner; + Node *index_expression; + bool has_swizzling_duplicates; + + virtual DataType get_datatype() const { return datatype; } + virtual String get_datatype_name() const { return String(struct_name); } + + MemberNode() : + Node(TYPE_MEMBER), + basetype(TYPE_VOID), + basetype_const(false), + datatype(TYPE_VOID), + array_size(0), + owner(NULL), + index_expression(NULL), + has_swizzling_duplicates(false) {} + }; + + struct StructNode : public Node { + + List<MemberNode *> members; + StructNode() : + Node(TYPE_STRUCT) {} + }; + + struct FunctionNode : public Node { + + struct Argument { + ArgumentQualifier qualifier; + StringName name; + DataType type; + StringName type_str; + DataPrecision precision; + //for passing textures as arguments + bool tex_argument_check; + TextureFilter tex_argument_filter; + TextureRepeat tex_argument_repeat; + bool tex_builtin_check; + StringName tex_builtin; + + Map<StringName, Set<int>> tex_argument_connect; + }; + + StringName name; + DataType return_type; + StringName return_struct_name; + DataPrecision return_precision; + Vector<Argument> arguments; + BlockNode *body; + bool can_discard; + + FunctionNode() : + Node(TYPE_FUNCTION), + return_type(TYPE_VOID), + return_precision(PRECISION_DEFAULT), + body(NULL), + can_discard(false) {} + }; + + struct ShaderNode : public Node { + + struct Constant { + DataType type; + StringName type_str; + DataPrecision precision; + ConstantNode *initializer; + }; + + struct Function { + StringName name; + FunctionNode *function; + Set<StringName> uses_function; + bool callable; + }; + + struct Struct { + StringName name; + StructNode *shader_struct; + }; + + struct Varying { + DataType type; + DataInterpolation interpolation; + DataPrecision precision; + int array_size; + + Varying() : + type(TYPE_VOID), + interpolation(INTERPOLATION_FLAT), + precision(PRECISION_DEFAULT), + array_size(0) {} + }; + + struct Uniform { + enum Hint { + HINT_NONE, + HINT_COLOR, + HINT_RANGE, + HINT_ALBEDO, + HINT_BLACK_ALBEDO, + HINT_NORMAL, + HINT_ROUGHNESS_NORMAL, + HINT_ROUGHNESS_R, + HINT_ROUGHNESS_G, + HINT_ROUGHNESS_B, + HINT_ROUGHNESS_A, + HINT_ROUGHNESS_GRAY, + HINT_BLACK, + HINT_WHITE, + HINT_ANISO, + HINT_MAX + }; + + int order; + int texture_order; + DataType type; + DataPrecision precision; + Vector<ConstantNode::Value> default_value; + Hint hint; + TextureFilter filter; + TextureRepeat repeat; + float hint_range[3]; + + Uniform() : + order(0), + texture_order(0), + type(TYPE_VOID), + precision(PRECISION_DEFAULT), + hint(HINT_NONE), + filter(FILTER_DEFAULT), + repeat(REPEAT_DEFAULT) { + hint_range[0] = 0.0f; + hint_range[1] = 1.0f; + hint_range[2] = 0.001f; + } + }; + + Map<StringName, Constant> constants; + Map<StringName, Varying> varyings; + Map<StringName, Uniform> uniforms; + Map<StringName, Struct> structs; + Vector<StringName> render_modes; + + Vector<Function> functions; + Vector<Struct> vstructs; + + ShaderNode() : + Node(TYPE_SHADER) {} + }; + + struct Expression { + bool is_op; + union { + Operator op; + Node *node; + }; + }; + + struct VarInfo { + StringName name; + DataType type; + }; + + enum CompletionType { + COMPLETION_NONE, + COMPLETION_RENDER_MODE, + COMPLETION_MAIN_FUNCTION, + COMPLETION_IDENTIFIER, + COMPLETION_FUNCTION_CALL, + COMPLETION_CALL_ARGUMENTS, + COMPLETION_INDEX, + COMPLETION_STRUCT, + }; + + struct Token { + TokenType type; + StringName text; + double constant; + uint16_t line; + }; + + static String get_operator_text(Operator p_op); + static String get_token_text(Token p_token); + + static bool is_token_datatype(TokenType p_type); + static bool is_token_variable_datatype(TokenType p_type); + static DataType get_token_datatype(TokenType p_type); + static bool is_token_interpolation(TokenType p_type); + static DataInterpolation get_token_interpolation(TokenType p_type); + static bool is_token_precision(TokenType p_type); + static DataPrecision get_token_precision(TokenType p_type); + static String get_precision_name(DataPrecision p_type); + static String get_datatype_name(DataType p_type); + static bool is_token_nonvoid_datatype(TokenType p_type); + static bool is_token_operator(TokenType p_type); + + static bool convert_constant(ConstantNode *p_constant, DataType p_to_type, ConstantNode::Value *p_value = NULL); + static DataType get_scalar_type(DataType p_type); + static int get_cardinality(DataType p_type); + static bool is_scalar_type(DataType p_type); + static bool is_sampler_type(DataType p_type); + static Variant constant_value_to_variant(const Vector<ShaderLanguage::ConstantNode::Value> &p_value, DataType p_type, ShaderLanguage::ShaderNode::Uniform::Hint p_hint = ShaderLanguage::ShaderNode::Uniform::HINT_NONE); + static PropertyInfo uniform_to_property_info(const ShaderNode::Uniform &p_uniform); + static uint32_t get_type_size(DataType p_type); + + static void get_keyword_list(List<String> *r_keywords); + static void get_builtin_funcs(List<String> *r_keywords); + + struct BuiltInInfo { + DataType type; + bool constant; + + BuiltInInfo() : + type(TYPE_VOID), + constant(false) {} + + BuiltInInfo(DataType p_type, bool p_constant = false) : + type(p_type), + constant(p_constant) {} + }; + + struct FunctionInfo { + Map<StringName, BuiltInInfo> built_ins; + bool can_discard; + }; + static bool has_builtin(const Map<StringName, ShaderLanguage::FunctionInfo> &p_functions, const StringName &p_name); + +private: + struct KeyWord { + TokenType token; + const char *text; + }; + + static const KeyWord keyword_list[]; + + bool error_set; + String error_str; + int error_line; + + String code; + int char_idx; + int tk_line; + + StringName current_function; + + struct TkPos { + int char_idx; + int tk_line; + }; + + TkPos _get_tkpos() { + TkPos tkp; + tkp.char_idx = char_idx; + tkp.tk_line = tk_line; + return tkp; + } + + void _set_tkpos(TkPos p_pos) { + char_idx = p_pos.char_idx; + tk_line = p_pos.tk_line; + } + + void _set_error(const String &p_str) { + if (error_set) + return; + + error_line = tk_line; + error_set = true; + error_str = p_str; + } + + static const char *token_names[TK_MAX]; + + Token _make_token(TokenType p_type, const StringName &p_text = StringName()); + Token _get_token(); + + ShaderNode *shader; + + enum IdentifierType { + IDENTIFIER_FUNCTION, + IDENTIFIER_UNIFORM, + IDENTIFIER_VARYING, + IDENTIFIER_FUNCTION_ARGUMENT, + IDENTIFIER_LOCAL_VAR, + IDENTIFIER_BUILTIN_VAR, + IDENTIFIER_CONSTANT, + }; + + bool _find_identifier(const BlockNode *p_block, bool p_allow_reassign, const Map<StringName, BuiltInInfo> &p_builtin_types, const StringName &p_identifier, DataType *r_data_type = NULL, IdentifierType *r_type = NULL, bool *r_is_const = NULL, int *r_array_size = NULL, StringName *r_struct_name = NULL); + bool _is_operator_assign(Operator p_op) const; + bool _validate_assign(Node *p_node, const Map<StringName, BuiltInInfo> &p_builtin_types, String *r_message = NULL); + bool _validate_operator(OperatorNode *p_op, DataType *r_ret_type = NULL); + + struct BuiltinFuncDef { + enum { MAX_ARGS = 5 }; + const char *name; + DataType rettype; + const DataType args[MAX_ARGS]; + SubClassTag tag; + bool high_end; + }; + + struct BuiltinFuncOutArgs { //arguments used as out in built in functions + const char *name; + int argument; + }; + + CompletionType completion_type; + int completion_line; + BlockNode *completion_block; + DataType completion_base; + SubClassTag completion_class; + StringName completion_function; + StringName completion_struct; + int completion_argument; + + bool _get_completable_identifier(BlockNode *p_block, CompletionType p_type, StringName &identifier); + static const BuiltinFuncDef builtin_func_defs[]; + static const BuiltinFuncOutArgs builtin_func_out_args[]; + + Error _validate_datatype(DataType p_type); + bool _compare_datatypes_in_nodes(Node *a, Node *b) const; + + bool _validate_function_call(BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types, OperatorNode *p_func, DataType *r_ret_type, StringName *r_ret_type_str); + bool _parse_function_arguments(BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types, OperatorNode *p_func, int *r_complete_arg = NULL); + bool _propagate_function_call_sampler_uniform_settings(StringName p_name, int p_argument, TextureFilter p_filter, TextureRepeat p_repeat); + bool _propagate_function_call_sampler_builtin_reference(StringName p_name, int p_argument, const StringName &p_builtin); + + Node *_parse_expression(BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types); + ShaderLanguage::Node *_reduce_expression(BlockNode *p_block, ShaderLanguage::Node *p_node); + + Node *_parse_and_reduce_expression(BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types); + Error _parse_block(BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types, bool p_just_one = false, bool p_can_break = false, bool p_can_continue = false); + String _get_shader_type_list(const Set<String> &p_shader_types) const; + String _get_qualifier_str(ArgumentQualifier p_qualifier) const; + + Error _parse_shader(const Map<StringName, FunctionInfo> &p_functions, const Vector<StringName> &p_render_modes, const Set<String> &p_shader_types); + + Error _find_last_flow_op_in_block(BlockNode *p_block, FlowOperation p_op); + Error _find_last_flow_op_in_op(ControlFlowNode *p_flow, FlowOperation p_op); + +public: + //static void get_keyword_list(ShaderType p_type,List<String> *p_keywords); + + void clear(); + + static String get_shader_type(const String &p_code); + Error compile(const String &p_code, const Map<StringName, FunctionInfo> &p_functions, const Vector<StringName> &p_render_modes, const Set<String> &p_shader_types); + Error complete(const String &p_code, const Map<StringName, FunctionInfo> &p_functions, const Vector<StringName> &p_render_modes, const Set<String> &p_shader_types, List<ScriptCodeCompletionOption> *r_options, String &r_call_hint); + + String get_error_text(); + int get_error_line(); + + ShaderNode *get_shader(); + + String token_debug(const String &p_code); + + ShaderLanguage(); + ~ShaderLanguage(); +}; + +#endif // SHADER_LANGUAGE_H diff --git a/servers/rendering/shader_types.cpp b/servers/rendering/shader_types.cpp new file mode 100644 index 0000000000..b144d6d612 --- /dev/null +++ b/servers/rendering/shader_types.cpp @@ -0,0 +1,328 @@ +/*************************************************************************/ +/* shader_types.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "shader_types.h" + +const Map<StringName, ShaderLanguage::FunctionInfo> &ShaderTypes::get_functions(RS::ShaderMode p_mode) { + + return shader_modes[p_mode].functions; +} + +const Vector<StringName> &ShaderTypes::get_modes(RS::ShaderMode p_mode) { + + return shader_modes[p_mode].modes; +} + +const Set<String> &ShaderTypes::get_types() { + return shader_types; +} + +ShaderTypes *ShaderTypes::singleton = NULL; + +static ShaderLanguage::BuiltInInfo constt(ShaderLanguage::DataType p_type) { + + return ShaderLanguage::BuiltInInfo(p_type, true); +} + +ShaderTypes::ShaderTypes() { + singleton = this; + + /*************** SPATIAL ***********************/ + + shader_modes[RS::SHADER_SPATIAL].functions["global"].built_ins["TIME"] = constt(ShaderLanguage::TYPE_FLOAT); + + shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["VERTEX"] = ShaderLanguage::TYPE_VEC3; + shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["NORMAL"] = ShaderLanguage::TYPE_VEC3; + shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["TANGENT"] = ShaderLanguage::TYPE_VEC3; + shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["BINORMAL"] = ShaderLanguage::TYPE_VEC3; + shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["POSITION"] = ShaderLanguage::TYPE_VEC4; + shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["UV"] = ShaderLanguage::TYPE_VEC2; + shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["UV2"] = ShaderLanguage::TYPE_VEC2; + shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["COLOR"] = ShaderLanguage::TYPE_VEC4; + shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["POINT_SIZE"] = ShaderLanguage::TYPE_FLOAT; + shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["INSTANCE_ID"] = constt(ShaderLanguage::TYPE_INT); + shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["INSTANCE_CUSTOM"] = constt(ShaderLanguage::TYPE_VEC4); + shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["ROUGHNESS"] = ShaderLanguage::TYPE_FLOAT; + shader_modes[RS::SHADER_SPATIAL].functions["vertex"].can_discard = false; + + //builtins + shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["WORLD_MATRIX"] = ShaderLanguage::TYPE_MAT4; + shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["WORLD_NORMAL_MATRIX"] = ShaderLanguage::TYPE_MAT3; + shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["INV_CAMERA_MATRIX"] = constt(ShaderLanguage::TYPE_MAT4); + shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["CAMERA_MATRIX"] = constt(ShaderLanguage::TYPE_MAT4); + shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["PROJECTION_MATRIX"] = ShaderLanguage::TYPE_MAT4; + shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["INV_PROJECTION_MATRIX"] = constt(ShaderLanguage::TYPE_MAT4); + shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["MODELVIEW_MATRIX"] = ShaderLanguage::TYPE_MAT4; + shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["MODELVIEW_NORMAL_MATRIX"] = ShaderLanguage::TYPE_MAT3; + shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["VIEWPORT_SIZE"] = constt(ShaderLanguage::TYPE_VEC2); + shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["OUTPUT_IS_SRGB"] = constt(ShaderLanguage::TYPE_BOOL); + + shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["VERTEX"] = constt(ShaderLanguage::TYPE_VEC3); + shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["FRAGCOORD"] = constt(ShaderLanguage::TYPE_VEC4); + shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["FRONT_FACING"] = constt(ShaderLanguage::TYPE_BOOL); + shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["NORMAL"] = ShaderLanguage::TYPE_VEC3; + shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["TANGENT"] = ShaderLanguage::TYPE_VEC3; + shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["BINORMAL"] = ShaderLanguage::TYPE_VEC3; + shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["VIEW"] = constt(ShaderLanguage::TYPE_VEC3); + shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["NORMALMAP"] = ShaderLanguage::TYPE_VEC3; + shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["NORMALMAP_DEPTH"] = ShaderLanguage::TYPE_FLOAT; + shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["UV"] = constt(ShaderLanguage::TYPE_VEC2); + shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["UV2"] = constt(ShaderLanguage::TYPE_VEC2); + shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["COLOR"] = constt(ShaderLanguage::TYPE_VEC4); + shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["ALBEDO"] = ShaderLanguage::TYPE_VEC3; + shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["ALPHA"] = ShaderLanguage::TYPE_FLOAT; + shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["METALLIC"] = ShaderLanguage::TYPE_FLOAT; + shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["SPECULAR"] = ShaderLanguage::TYPE_FLOAT; + shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["ROUGHNESS"] = ShaderLanguage::TYPE_FLOAT; + shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["RIM"] = ShaderLanguage::TYPE_FLOAT; + shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["RIM_TINT"] = ShaderLanguage::TYPE_FLOAT; + shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["CLEARCOAT"] = ShaderLanguage::TYPE_FLOAT; + shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["CLEARCOAT_GLOSS"] = ShaderLanguage::TYPE_FLOAT; + shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["ANISOTROPY"] = ShaderLanguage::TYPE_FLOAT; + shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["ANISOTROPY_FLOW"] = ShaderLanguage::TYPE_VEC2; + shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["SSS_STRENGTH"] = ShaderLanguage::TYPE_FLOAT; + shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["TRANSMISSION"] = ShaderLanguage::TYPE_VEC3; + shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["AO"] = ShaderLanguage::TYPE_FLOAT; + shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["AO_LIGHT_AFFECT"] = ShaderLanguage::TYPE_FLOAT; + shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["EMISSION"] = ShaderLanguage::TYPE_VEC3; + shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["SCREEN_TEXTURE"] = ShaderLanguage::TYPE_SAMPLER2D; + shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["DEPTH_TEXTURE"] = ShaderLanguage::TYPE_SAMPLER2D; + shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["DEPTH"] = ShaderLanguage::TYPE_FLOAT; + shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["SCREEN_UV"] = ShaderLanguage::TYPE_VEC2; + shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["POINT_COORD"] = constt(ShaderLanguage::TYPE_VEC2); + + shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["OUTPUT_IS_SRGB"] = constt(ShaderLanguage::TYPE_BOOL); + + shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["WORLD_MATRIX"] = constt(ShaderLanguage::TYPE_MAT4); + shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["WORLD_NORMAL_MATRIX"] = constt(ShaderLanguage::TYPE_MAT3); + shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["INV_CAMERA_MATRIX"] = constt(ShaderLanguage::TYPE_MAT4); + shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["CAMERA_MATRIX"] = constt(ShaderLanguage::TYPE_MAT4); + shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["PROJECTION_MATRIX"] = constt(ShaderLanguage::TYPE_MAT4); + shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["INV_PROJECTION_MATRIX"] = constt(ShaderLanguage::TYPE_MAT4); + shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["VIEWPORT_SIZE"] = constt(ShaderLanguage::TYPE_VEC2); + shader_modes[RS::SHADER_SPATIAL].functions["fragment"].can_discard = true; + + shader_modes[RS::SHADER_SPATIAL].functions["light"].built_ins["WORLD_MATRIX"] = constt(ShaderLanguage::TYPE_MAT4); + shader_modes[RS::SHADER_SPATIAL].functions["light"].built_ins["INV_CAMERA_MATRIX"] = constt(ShaderLanguage::TYPE_MAT4); + shader_modes[RS::SHADER_SPATIAL].functions["light"].built_ins["CAMERA_MATRIX"] = constt(ShaderLanguage::TYPE_MAT4); + shader_modes[RS::SHADER_SPATIAL].functions["light"].built_ins["PROJECTION_MATRIX"] = constt(ShaderLanguage::TYPE_MAT4); + shader_modes[RS::SHADER_SPATIAL].functions["light"].built_ins["INV_PROJECTION_MATRIX"] = constt(ShaderLanguage::TYPE_MAT4); + shader_modes[RS::SHADER_SPATIAL].functions["light"].built_ins["VIEWPORT_SIZE"] = constt(ShaderLanguage::TYPE_VEC2); + + shader_modes[RS::SHADER_SPATIAL].functions["light"].built_ins["FRAGCOORD"] = constt(ShaderLanguage::TYPE_VEC4); + shader_modes[RS::SHADER_SPATIAL].functions["light"].built_ins["NORMAL"] = constt(ShaderLanguage::TYPE_VEC3); + shader_modes[RS::SHADER_SPATIAL].functions["light"].built_ins["UV"] = constt(ShaderLanguage::TYPE_VEC2); + shader_modes[RS::SHADER_SPATIAL].functions["light"].built_ins["UV2"] = constt(ShaderLanguage::TYPE_VEC2); + shader_modes[RS::SHADER_SPATIAL].functions["light"].built_ins["VIEW"] = constt(ShaderLanguage::TYPE_VEC3); + shader_modes[RS::SHADER_SPATIAL].functions["light"].built_ins["LIGHT"] = constt(ShaderLanguage::TYPE_VEC3); + shader_modes[RS::SHADER_SPATIAL].functions["light"].built_ins["LIGHT_COLOR"] = constt(ShaderLanguage::TYPE_VEC3); + shader_modes[RS::SHADER_SPATIAL].functions["light"].built_ins["ATTENUATION"] = constt(ShaderLanguage::TYPE_VEC3); + shader_modes[RS::SHADER_SPATIAL].functions["light"].built_ins["ALBEDO"] = constt(ShaderLanguage::TYPE_VEC3); + shader_modes[RS::SHADER_SPATIAL].functions["light"].built_ins["TRANSMISSION"] = constt(ShaderLanguage::TYPE_VEC3); + shader_modes[RS::SHADER_SPATIAL].functions["light"].built_ins["ROUGHNESS"] = constt(ShaderLanguage::TYPE_FLOAT); + shader_modes[RS::SHADER_SPATIAL].functions["light"].built_ins["DIFFUSE_LIGHT"] = ShaderLanguage::TYPE_VEC3; + shader_modes[RS::SHADER_SPATIAL].functions["light"].built_ins["SPECULAR_LIGHT"] = ShaderLanguage::TYPE_VEC3; + shader_modes[RS::SHADER_SPATIAL].functions["light"].built_ins["OUTPUT_IS_SRGB"] = constt(ShaderLanguage::TYPE_BOOL); + shader_modes[RS::SHADER_SPATIAL].functions["light"].built_ins["ALPHA"] = ShaderLanguage::TYPE_FLOAT; + + shader_modes[RS::SHADER_SPATIAL].functions["light"].can_discard = true; + + //order used puts first enum mode (default) first + shader_modes[RS::SHADER_SPATIAL].modes.push_back("blend_mix"); + shader_modes[RS::SHADER_SPATIAL].modes.push_back("blend_add"); + shader_modes[RS::SHADER_SPATIAL].modes.push_back("blend_sub"); + shader_modes[RS::SHADER_SPATIAL].modes.push_back("blend_mul"); + + shader_modes[RS::SHADER_SPATIAL].modes.push_back("depth_draw_opaque"); + shader_modes[RS::SHADER_SPATIAL].modes.push_back("depth_draw_always"); + shader_modes[RS::SHADER_SPATIAL].modes.push_back("depth_draw_never"); + + shader_modes[RS::SHADER_SPATIAL].modes.push_back("depth_prepass_alpha"); + + shader_modes[RS::SHADER_SPATIAL].modes.push_back("depth_test_disabled"); + + shader_modes[RS::SHADER_SPATIAL].modes.push_back("cull_back"); + shader_modes[RS::SHADER_SPATIAL].modes.push_back("cull_front"); + shader_modes[RS::SHADER_SPATIAL].modes.push_back("cull_disabled"); + + shader_modes[RS::SHADER_SPATIAL].modes.push_back("unshaded"); + shader_modes[RS::SHADER_SPATIAL].modes.push_back("wireframe"); + + shader_modes[RS::SHADER_SPATIAL].modes.push_back("diffuse_lambert"); + shader_modes[RS::SHADER_SPATIAL].modes.push_back("diffuse_lambert_wrap"); + shader_modes[RS::SHADER_SPATIAL].modes.push_back("diffuse_oren_nayar"); + shader_modes[RS::SHADER_SPATIAL].modes.push_back("diffuse_burley"); + shader_modes[RS::SHADER_SPATIAL].modes.push_back("diffuse_toon"); + + shader_modes[RS::SHADER_SPATIAL].modes.push_back("specular_schlick_ggx"); + shader_modes[RS::SHADER_SPATIAL].modes.push_back("specular_blinn"); + shader_modes[RS::SHADER_SPATIAL].modes.push_back("specular_phong"); + shader_modes[RS::SHADER_SPATIAL].modes.push_back("specular_toon"); + shader_modes[RS::SHADER_SPATIAL].modes.push_back("specular_disabled"); + + shader_modes[RS::SHADER_SPATIAL].modes.push_back("skip_vertex_transform"); + shader_modes[RS::SHADER_SPATIAL].modes.push_back("world_vertex_coords"); + shader_modes[RS::SHADER_SPATIAL].modes.push_back("ensure_correct_normals"); + + shader_modes[RS::SHADER_SPATIAL].modes.push_back("shadows_disabled"); + shader_modes[RS::SHADER_SPATIAL].modes.push_back("ambient_light_disabled"); + shader_modes[RS::SHADER_SPATIAL].modes.push_back("shadow_to_opacity"); + + shader_modes[RS::SHADER_SPATIAL].modes.push_back("vertex_lighting"); + + /************ CANVAS ITEM **************************/ + + shader_modes[RS::SHADER_CANVAS_ITEM].functions["global"].built_ins["TIME"] = constt(ShaderLanguage::TYPE_FLOAT); + + shader_modes[RS::SHADER_CANVAS_ITEM].functions["vertex"].built_ins["VERTEX"] = ShaderLanguage::TYPE_VEC2; + shader_modes[RS::SHADER_CANVAS_ITEM].functions["vertex"].built_ins["UV"] = ShaderLanguage::TYPE_VEC2; + shader_modes[RS::SHADER_CANVAS_ITEM].functions["vertex"].built_ins["COLOR"] = ShaderLanguage::TYPE_VEC4; + shader_modes[RS::SHADER_CANVAS_ITEM].functions["vertex"].built_ins["POINT_SIZE"] = ShaderLanguage::TYPE_FLOAT; + + shader_modes[RS::SHADER_CANVAS_ITEM].functions["vertex"].built_ins["WORLD_MATRIX"] = constt(ShaderLanguage::TYPE_MAT4); + shader_modes[RS::SHADER_CANVAS_ITEM].functions["vertex"].built_ins["CANVAS_MATRIX"] = constt(ShaderLanguage::TYPE_MAT4); + shader_modes[RS::SHADER_CANVAS_ITEM].functions["vertex"].built_ins["SCREEN_MATRIX"] = constt(ShaderLanguage::TYPE_MAT4); + shader_modes[RS::SHADER_CANVAS_ITEM].functions["vertex"].built_ins["INSTANCE_CUSTOM"] = constt(ShaderLanguage::TYPE_VEC4); + shader_modes[RS::SHADER_CANVAS_ITEM].functions["vertex"].built_ins["AT_LIGHT_PASS"] = constt(ShaderLanguage::TYPE_BOOL); + shader_modes[RS::SHADER_CANVAS_ITEM].functions["vertex"].built_ins["TEXTURE_PIXEL_SIZE"] = constt(ShaderLanguage::TYPE_VEC2); + shader_modes[RS::SHADER_CANVAS_ITEM].functions["vertex"].can_discard = false; + + shader_modes[RS::SHADER_CANVAS_ITEM].functions["fragment"].built_ins["VERTEX"] = ShaderLanguage::TYPE_VEC2; + shader_modes[RS::SHADER_CANVAS_ITEM].functions["fragment"].built_ins["SHADOW_VERTEX"] = ShaderLanguage::TYPE_VEC2; + shader_modes[RS::SHADER_CANVAS_ITEM].functions["fragment"].built_ins["LIGHT_VERTEX"] = ShaderLanguage::TYPE_VEC3; + shader_modes[RS::SHADER_CANVAS_ITEM].functions["fragment"].built_ins["FRAGCOORD"] = constt(ShaderLanguage::TYPE_VEC4); + shader_modes[RS::SHADER_CANVAS_ITEM].functions["fragment"].built_ins["NORMAL"] = ShaderLanguage::TYPE_VEC3; + shader_modes[RS::SHADER_CANVAS_ITEM].functions["fragment"].built_ins["NORMALMAP"] = ShaderLanguage::TYPE_VEC3; + shader_modes[RS::SHADER_CANVAS_ITEM].functions["fragment"].built_ins["NORMALMAP_DEPTH"] = ShaderLanguage::TYPE_FLOAT; + shader_modes[RS::SHADER_CANVAS_ITEM].functions["fragment"].built_ins["UV"] = constt(ShaderLanguage::TYPE_VEC2); + shader_modes[RS::SHADER_CANVAS_ITEM].functions["fragment"].built_ins["COLOR"] = ShaderLanguage::TYPE_VEC4; + shader_modes[RS::SHADER_CANVAS_ITEM].functions["fragment"].built_ins["TEXTURE"] = constt(ShaderLanguage::TYPE_SAMPLER2D); + shader_modes[RS::SHADER_CANVAS_ITEM].functions["fragment"].built_ins["TEXTURE_PIXEL_SIZE"] = constt(ShaderLanguage::TYPE_VEC2); + shader_modes[RS::SHADER_CANVAS_ITEM].functions["fragment"].built_ins["NORMAL_TEXTURE"] = constt(ShaderLanguage::TYPE_SAMPLER2D); + shader_modes[RS::SHADER_CANVAS_ITEM].functions["fragment"].built_ins["SPECULAR_SHININESS_TEXTURE"] = constt(ShaderLanguage::TYPE_SAMPLER2D); + shader_modes[RS::SHADER_CANVAS_ITEM].functions["fragment"].built_ins["SPECULAR_SHININESS"] = constt(ShaderLanguage::TYPE_VEC4); + shader_modes[RS::SHADER_CANVAS_ITEM].functions["fragment"].built_ins["SCREEN_UV"] = constt(ShaderLanguage::TYPE_VEC2); + shader_modes[RS::SHADER_CANVAS_ITEM].functions["fragment"].built_ins["SCREEN_PIXEL_SIZE"] = constt(ShaderLanguage::TYPE_VEC2); + shader_modes[RS::SHADER_CANVAS_ITEM].functions["fragment"].built_ins["POINT_COORD"] = constt(ShaderLanguage::TYPE_VEC2); + shader_modes[RS::SHADER_CANVAS_ITEM].functions["fragment"].built_ins["AT_LIGHT_PASS"] = constt(ShaderLanguage::TYPE_BOOL); + shader_modes[RS::SHADER_CANVAS_ITEM].functions["fragment"].built_ins["SCREEN_TEXTURE"] = constt(ShaderLanguage::TYPE_SAMPLER2D); + shader_modes[RS::SHADER_CANVAS_ITEM].functions["fragment"].can_discard = true; + + shader_modes[RS::SHADER_CANVAS_ITEM].functions["light"].built_ins["FRAGCOORD"] = constt(ShaderLanguage::TYPE_VEC4); + shader_modes[RS::SHADER_CANVAS_ITEM].functions["light"].built_ins["NORMAL"] = constt(ShaderLanguage::TYPE_VEC3); + shader_modes[RS::SHADER_CANVAS_ITEM].functions["light"].built_ins["COLOR"] = constt(ShaderLanguage::TYPE_VEC4); + shader_modes[RS::SHADER_CANVAS_ITEM].functions["light"].built_ins["UV"] = constt(ShaderLanguage::TYPE_VEC2); + shader_modes[RS::SHADER_CANVAS_ITEM].functions["light"].built_ins["SPECULAR_SHININESS"] = constt(ShaderLanguage::TYPE_VEC4); + shader_modes[RS::SHADER_CANVAS_ITEM].functions["light"].built_ins["LIGHT_COLOR"] = constt(ShaderLanguage::TYPE_VEC4); + shader_modes[RS::SHADER_CANVAS_ITEM].functions["light"].built_ins["LIGHT_POSITION"] = constt(ShaderLanguage::TYPE_VEC3); + shader_modes[RS::SHADER_CANVAS_ITEM].functions["light"].built_ins["LIGHT_VERTEX"] = constt(ShaderLanguage::TYPE_VEC3); + shader_modes[RS::SHADER_CANVAS_ITEM].functions["light"].built_ins["LIGHT"] = ShaderLanguage::TYPE_VEC4; + shader_modes[RS::SHADER_CANVAS_ITEM].functions["light"].built_ins["SHADOW_MODULATE"] = ShaderLanguage::TYPE_VEC4; + shader_modes[RS::SHADER_CANVAS_ITEM].functions["light"].built_ins["SCREEN_UV"] = constt(ShaderLanguage::TYPE_VEC2); + shader_modes[RS::SHADER_CANVAS_ITEM].functions["light"].built_ins["TEXTURE"] = constt(ShaderLanguage::TYPE_SAMPLER2D); + shader_modes[RS::SHADER_CANVAS_ITEM].functions["light"].built_ins["TEXTURE_PIXEL_SIZE"] = constt(ShaderLanguage::TYPE_VEC2); + shader_modes[RS::SHADER_CANVAS_ITEM].functions["light"].built_ins["POINT_COORD"] = constt(ShaderLanguage::TYPE_VEC2); + shader_modes[RS::SHADER_CANVAS_ITEM].functions["light"].can_discard = true; + + shader_modes[RS::SHADER_CANVAS_ITEM].modes.push_back("skip_vertex_transform"); + + shader_modes[RS::SHADER_CANVAS_ITEM].modes.push_back("blend_mix"); + shader_modes[RS::SHADER_CANVAS_ITEM].modes.push_back("blend_add"); + shader_modes[RS::SHADER_CANVAS_ITEM].modes.push_back("blend_sub"); + shader_modes[RS::SHADER_CANVAS_ITEM].modes.push_back("blend_mul"); + shader_modes[RS::SHADER_CANVAS_ITEM].modes.push_back("blend_premul_alpha"); + shader_modes[RS::SHADER_CANVAS_ITEM].modes.push_back("blend_disabled"); + + shader_modes[RS::SHADER_CANVAS_ITEM].modes.push_back("unshaded"); + shader_modes[RS::SHADER_CANVAS_ITEM].modes.push_back("light_only"); + + /************ PARTICLES **************************/ + + shader_modes[RS::SHADER_PARTICLES].functions["global"].built_ins["TIME"] = constt(ShaderLanguage::TYPE_FLOAT); + shader_modes[RS::SHADER_PARTICLES].functions["vertex"].built_ins["COLOR"] = ShaderLanguage::TYPE_VEC4; + shader_modes[RS::SHADER_PARTICLES].functions["vertex"].built_ins["VELOCITY"] = ShaderLanguage::TYPE_VEC3; + shader_modes[RS::SHADER_PARTICLES].functions["vertex"].built_ins["MASS"] = ShaderLanguage::TYPE_FLOAT; + shader_modes[RS::SHADER_PARTICLES].functions["vertex"].built_ins["ACTIVE"] = ShaderLanguage::TYPE_BOOL; + shader_modes[RS::SHADER_PARTICLES].functions["vertex"].built_ins["RESTART"] = constt(ShaderLanguage::TYPE_BOOL); + shader_modes[RS::SHADER_PARTICLES].functions["vertex"].built_ins["CUSTOM"] = ShaderLanguage::TYPE_VEC4; + shader_modes[RS::SHADER_PARTICLES].functions["vertex"].built_ins["TRANSFORM"] = ShaderLanguage::TYPE_MAT4; + shader_modes[RS::SHADER_PARTICLES].functions["vertex"].built_ins["LIFETIME"] = constt(ShaderLanguage::TYPE_FLOAT); + shader_modes[RS::SHADER_PARTICLES].functions["vertex"].built_ins["DELTA"] = constt(ShaderLanguage::TYPE_FLOAT); + shader_modes[RS::SHADER_PARTICLES].functions["vertex"].built_ins["NUMBER"] = constt(ShaderLanguage::TYPE_UINT); + shader_modes[RS::SHADER_PARTICLES].functions["vertex"].built_ins["INDEX"] = constt(ShaderLanguage::TYPE_INT); + shader_modes[RS::SHADER_PARTICLES].functions["vertex"].built_ins["EMISSION_TRANSFORM"] = constt(ShaderLanguage::TYPE_MAT4); + shader_modes[RS::SHADER_PARTICLES].functions["vertex"].built_ins["RANDOM_SEED"] = constt(ShaderLanguage::TYPE_UINT); + shader_modes[RS::SHADER_PARTICLES].functions["vertex"].can_discard = false; + + shader_modes[RS::SHADER_PARTICLES].modes.push_back("disable_force"); + shader_modes[RS::SHADER_PARTICLES].modes.push_back("disable_velocity"); + shader_modes[RS::SHADER_PARTICLES].modes.push_back("keep_data"); + + /************ SKY **************************/ + + shader_modes[RS::SHADER_SKY].functions["global"].built_ins["TIME"] = constt(ShaderLanguage::TYPE_FLOAT); + shader_modes[RS::SHADER_SKY].functions["global"].built_ins["POSITION"] = constt(ShaderLanguage::TYPE_VEC3); + shader_modes[RS::SHADER_SKY].functions["global"].built_ins["RADIANCE"] = constt(ShaderLanguage::TYPE_SAMPLERCUBE); + shader_modes[RS::SHADER_SKY].functions["global"].built_ins["AT_HALF_RES_PASS"] = constt(ShaderLanguage::TYPE_BOOL); + shader_modes[RS::SHADER_SKY].functions["global"].built_ins["AT_QUARTER_RES_PASS"] = constt(ShaderLanguage::TYPE_BOOL); + shader_modes[RS::SHADER_SKY].functions["global"].built_ins["AT_CUBEMAP_PASS"] = constt(ShaderLanguage::TYPE_BOOL); + shader_modes[RS::SHADER_SKY].functions["global"].built_ins["LIGHT0_ENABLED"] = constt(ShaderLanguage::TYPE_BOOL); + shader_modes[RS::SHADER_SKY].functions["global"].built_ins["LIGHT0_DIRECTION"] = constt(ShaderLanguage::TYPE_VEC3); + shader_modes[RS::SHADER_SKY].functions["global"].built_ins["LIGHT0_ENERGY"] = constt(ShaderLanguage::TYPE_FLOAT); + shader_modes[RS::SHADER_SKY].functions["global"].built_ins["LIGHT0_COLOR"] = constt(ShaderLanguage::TYPE_VEC3); + shader_modes[RS::SHADER_SKY].functions["global"].built_ins["LIGHT1_ENABLED"] = constt(ShaderLanguage::TYPE_BOOL); + shader_modes[RS::SHADER_SKY].functions["global"].built_ins["LIGHT1_DIRECTION"] = constt(ShaderLanguage::TYPE_VEC3); + shader_modes[RS::SHADER_SKY].functions["global"].built_ins["LIGHT1_ENERGY"] = constt(ShaderLanguage::TYPE_FLOAT); + shader_modes[RS::SHADER_SKY].functions["global"].built_ins["LIGHT1_COLOR"] = constt(ShaderLanguage::TYPE_VEC3); + shader_modes[RS::SHADER_SKY].functions["global"].built_ins["LIGHT2_ENABLED"] = constt(ShaderLanguage::TYPE_BOOL); + shader_modes[RS::SHADER_SKY].functions["global"].built_ins["LIGHT2_DIRECTION"] = constt(ShaderLanguage::TYPE_VEC3); + shader_modes[RS::SHADER_SKY].functions["global"].built_ins["LIGHT2_ENERGY"] = constt(ShaderLanguage::TYPE_FLOAT); + shader_modes[RS::SHADER_SKY].functions["global"].built_ins["LIGHT2_COLOR"] = constt(ShaderLanguage::TYPE_VEC3); + shader_modes[RS::SHADER_SKY].functions["global"].built_ins["LIGHT3_ENABLED"] = constt(ShaderLanguage::TYPE_BOOL); + shader_modes[RS::SHADER_SKY].functions["global"].built_ins["LIGHT3_DIRECTION"] = constt(ShaderLanguage::TYPE_VEC3); + shader_modes[RS::SHADER_SKY].functions["global"].built_ins["LIGHT3_ENERGY"] = constt(ShaderLanguage::TYPE_FLOAT); + shader_modes[RS::SHADER_SKY].functions["global"].built_ins["LIGHT3_COLOR"] = constt(ShaderLanguage::TYPE_VEC3); + + shader_modes[RS::SHADER_SKY].functions["fragment"].built_ins["COLOR"] = ShaderLanguage::TYPE_VEC3; + shader_modes[RS::SHADER_SKY].functions["fragment"].built_ins["ALPHA"] = ShaderLanguage::TYPE_FLOAT; + shader_modes[RS::SHADER_SKY].functions["fragment"].built_ins["EYEDIR"] = constt(ShaderLanguage::TYPE_VEC3); + shader_modes[RS::SHADER_SKY].functions["fragment"].built_ins["SCREEN_UV"] = constt(ShaderLanguage::TYPE_VEC2); + shader_modes[RS::SHADER_SKY].functions["fragment"].built_ins["SKY_COORDS"] = constt(ShaderLanguage::TYPE_VEC2); + shader_modes[RS::SHADER_SKY].functions["fragment"].built_ins["HALF_RES_COLOR"] = constt(ShaderLanguage::TYPE_VEC4); + shader_modes[RS::SHADER_SKY].functions["fragment"].built_ins["QUARTER_RES_COLOR"] = constt(ShaderLanguage::TYPE_VEC4); + + shader_modes[RS::SHADER_SKY].modes.push_back("use_half_res_pass"); + shader_modes[RS::SHADER_SKY].modes.push_back("use_quarter_res_pass"); + + shader_types.insert("spatial"); + shader_types.insert("canvas_item"); + shader_types.insert("particles"); + shader_types.insert("sky"); +} diff --git a/servers/rendering/shader_types.h b/servers/rendering/shader_types.h new file mode 100644 index 0000000000..499a761265 --- /dev/null +++ b/servers/rendering/shader_types.h @@ -0,0 +1,62 @@ +/*************************************************************************/ +/* shader_types.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef SHADERTYPES_H +#define SHADERTYPES_H + +#include "core/ordered_hash_map.h" +#include "servers/rendering_server.h" +#include "shader_language.h" + +class ShaderTypes { + + struct Type { + + Map<StringName, ShaderLanguage::FunctionInfo> functions; + Vector<StringName> modes; + }; + + Map<RS::ShaderMode, Type> shader_modes; + + static ShaderTypes *singleton; + + Set<String> shader_types; + +public: + static ShaderTypes *get_singleton() { return singleton; } + + const Map<StringName, ShaderLanguage::FunctionInfo> &get_functions(RS::ShaderMode p_mode); + const Vector<StringName> &get_modes(RS::ShaderMode p_mode); + const Set<String> &get_types(); + + ShaderTypes(); +}; + +#endif // SHADERTYPES_H |