diff options
Diffstat (limited to 'servers')
57 files changed, 2925 insertions, 288 deletions
diff --git a/servers/audio/effects/audio_effect_chorus.h b/servers/audio/effects/audio_effect_chorus.h index 81af948530..b32b300dfa 100644 --- a/servers/audio/effects/audio_effect_chorus.h +++ b/servers/audio/effects/audio_effect_chorus.h @@ -60,7 +60,6 @@ class AudioEffectChorus : public AudioEffect { public: enum { - MAX_DELAY_MS = 50, MAX_DEPTH_MS = 20, MAX_WIDTH_MS = 50, diff --git a/servers/audio/effects/audio_effect_delay.h b/servers/audio/effects/audio_effect_delay.h index a55000af4b..3b7f2ea458 100644 --- a/servers/audio/effects/audio_effect_delay.h +++ b/servers/audio/effects/audio_effect_delay.h @@ -62,7 +62,6 @@ class AudioEffectDelay : public AudioEffect { friend class AudioEffectDelayInstance; enum { - MAX_DELAY_MS = 3000, MAX_TAPS = 2 }; diff --git a/servers/audio/effects/audio_effect_pitch_shift.cpp b/servers/audio/effects/audio_effect_pitch_shift.cpp index fb6b56d984..fdba1b59a3 100644 --- a/servers/audio/effects/audio_effect_pitch_shift.cpp +++ b/servers/audio/effects/audio_effect_pitch_shift.cpp @@ -95,14 +95,12 @@ void SMBPitchShift::PitchShift(float pitchShift, long numSampsToProcess, long ff expct = 2.*Math_PI*(double)stepSize/(double)fftFrameSize; inFifoLatency = fftFrameSize-stepSize; if (gRover == 0) { gRover = inFifoLatency; - } /* initialize our static arrays */ /* main processing loop */ for (i = 0; i < numSampsToProcess; i++){ - /* As long as we have not yet collected enough data just read in */ gInFIFO[gRover] = indata[i*stride]; outdata[i*stride] = gOutFIFO[gRover-inFifoLatency]; @@ -126,7 +124,6 @@ void SMBPitchShift::PitchShift(float pitchShift, long numSampsToProcess, long ff /* this is the analysis step */ for (k = 0; k <= fftFrameSize2; k++) { - /* de-interlace FFT buffer */ real = gFFTworksp[2*k]; imag = gFFTworksp[2*k+1]; @@ -146,7 +143,6 @@ void SMBPitchShift::PitchShift(float pitchShift, long numSampsToProcess, long ff qpd = tmp/Math_PI; if (qpd >= 0) { qpd += qpd&1; } else { qpd -= qpd&1; - } tmp -= Math_PI*(double)qpd; @@ -177,7 +173,6 @@ void SMBPitchShift::PitchShift(float pitchShift, long numSampsToProcess, long ff /* ***************** SYNTHESIS ******************* */ /* this is the synthesis step */ for (k = 0; k <= fftFrameSize2; k++) { - /* get magnitude and true frequency from synthesis arrays */ magn = gSynMagn[k]; tmp = gSynFreq[k]; @@ -205,7 +200,6 @@ void SMBPitchShift::PitchShift(float pitchShift, long numSampsToProcess, long ff /* zero negative frequencies */ for (k = fftFrameSize+2; k < 2*fftFrameSize; k++) { gFFTworksp[k] = 0.; - } /* do inverse transform */ @@ -217,7 +211,6 @@ void SMBPitchShift::PitchShift(float pitchShift, long numSampsToProcess, long ff gOutputAccum[k] += 2.*window*gFFTworksp[2*k]/(fftFrameSize2*osamp); } for (k = 0; k < stepSize; k++) { gOutFIFO[k] = gOutputAccum[k]; - } /* shift accumulator */ @@ -225,13 +218,9 @@ void SMBPitchShift::PitchShift(float pitchShift, long numSampsToProcess, long ff /* move input FIFO */ for (k = 0; k < inFifoLatency; k++) { gInFIFO[k] = gInFIFO[k+stepSize]; - } } } - - - } @@ -256,7 +245,6 @@ void SMBPitchShift::smbFft(float *fftBuffer, long fftFrameSize, long sign) for (i = 2; i < 2*fftFrameSize-2; i += 2) { for (bitm = 2, j = 0; bitm < 2*fftFrameSize; bitm <<= 1) { if (i & bitm) { j++; - } j <<= 1; } diff --git a/servers/audio/effects/audio_effect_stereo_enhance.h b/servers/audio/effects/audio_effect_stereo_enhance.h index 7fb32bd8ec..98ee18ba5a 100644 --- a/servers/audio/effects/audio_effect_stereo_enhance.h +++ b/servers/audio/effects/audio_effect_stereo_enhance.h @@ -41,7 +41,6 @@ class AudioEffectStereoEnhanceInstance : public AudioEffectInstance { Ref<AudioEffectStereoEnhance> base; enum { - MAX_DELAY_MS = 50 }; diff --git a/servers/audio/effects/eq.h b/servers/audio/effects/eq.h index 720c4dfcaf..c908c9c8fb 100644 --- a/servers/audio/effects/eq.h +++ b/servers/audio/effects/eq.h @@ -43,7 +43,6 @@ class EQ { public: enum Preset { - PRESET_6_BANDS, PRESET_8_BANDS, PRESET_10_BANDS, diff --git a/servers/audio/effects/reverb.cpp b/servers/audio/effects/reverb.cpp index 7c35d88ced..1deb1499b5 100644 --- a/servers/audio/effects/reverb.cpp +++ b/servers/audio/effects/reverb.cpp @@ -127,13 +127,11 @@ void Reverb::process(float *p_src, float *p_dst, int p_frames) { int ap_size_limit[MAX_ALLPASS]; for (int i=0;i<MAX_ALLPASS;i++) { - AllPass &a=allpass[i]; ap_size_limit[i]=a.size-lrintf((float)a.extra_spread_frames*(1.0-params.extra_spread)); } for (int i=0;i<p_frames;i++) { - float sample=p_dst[i]; float aux,in; float AllPass*ap; diff --git a/servers/audio/effects/reverb.h b/servers/audio/effects/reverb.h index 614de0c534..7dd88f9faf 100644 --- a/servers/audio/effects/reverb.h +++ b/servers/audio/effects/reverb.h @@ -46,7 +46,6 @@ public: private: enum { - MAX_COMBS = 8, MAX_ALLPASS = 4, MAX_ECHO_MS = 500 diff --git a/servers/audio_server.h b/servers/audio_server.h index 507aea7b89..83377a5e9e 100644 --- a/servers/audio_server.h +++ b/servers/audio_server.h @@ -122,7 +122,6 @@ public: class AudioDriverManager { enum { - MAX_DRIVERS = 10 }; diff --git a/servers/display_server.h b/servers/display_server.h index c2ffb23531..59bee794b8 100644 --- a/servers/display_server.h +++ b/servers/display_server.h @@ -177,7 +177,6 @@ public: } virtual bool screen_is_touchscreen(int p_screen = SCREEN_OF_MAIN_WINDOW) const; enum ScreenOrientation { - SCREEN_LANDSCAPE, SCREEN_PORTRAIT, SCREEN_REVERSE_LANDSCAPE, diff --git a/servers/physics_2d/physics_server_2d_sw.cpp b/servers/physics_2d/physics_server_2d_sw.cpp index 9d00d01759..755804fe36 100644 --- a/servers/physics_2d/physics_server_2d_sw.cpp +++ b/servers/physics_2d/physics_server_2d_sw.cpp @@ -1230,8 +1230,6 @@ void PhysicsServer2DSW::step(real_t p_step) { _update_shapes(); - doing_sync = false; - last_step = p_step; PhysicsDirectBodyState2DSW::singleton->step = p_step; island_count = 0; diff --git a/servers/physics_2d/space_2d_sw.cpp b/servers/physics_2d/space_2d_sw.cpp index a8ab731ead..edadcabe0b 100644 --- a/servers/physics_2d/space_2d_sw.cpp +++ b/servers/physics_2d/space_2d_sw.cpp @@ -603,7 +603,6 @@ int Space2DSW::test_body_ray_separation(Body2DSW *p_body, const Transform2D &p_t * direction. Use a short ray shape if you want to achieve a similar effect. * if (col_obj->is_shape_set_as_one_way_collision(shape_idx)) { - cbk.valid_dir = col_obj_shape_xform.get_axis(1).normalized(); cbk.valid_depth = p_margin; //only valid depth is the collision margin cbk.invalid_by_dir = 0; diff --git a/servers/physics_2d/space_2d_sw.h b/servers/physics_2d/space_2d_sw.h index 5c5144ae22..93b62e0ba4 100644 --- a/servers/physics_2d/space_2d_sw.h +++ b/servers/physics_2d/space_2d_sw.h @@ -105,7 +105,6 @@ private: real_t test_motion_min_contact_depth; enum { - INTERSECTION_QUERY_MAX = 2048 }; diff --git a/servers/physics_3d/body_3d_sw.cpp b/servers/physics_3d/body_3d_sw.cpp index d1f16cb4ae..841e593ba3 100644 --- a/servers/physics_3d/body_3d_sw.cpp +++ b/servers/physics_3d/body_3d_sw.cpp @@ -628,7 +628,6 @@ void Body3DSW::integrate_velocities(real_t p_step) { /* void BodySW::simulate_motion(const Transform& p_xform,real_t p_step) { - Transform inv_xform = p_xform.affine_inverse(); if (!get_space()) { _set_transform(p_xform); @@ -655,8 +654,6 @@ void BodySW::simulate_motion(const Transform& p_xform,real_t p_step) { get_space()->body_add_to_state_query_list(&direct_state_query_list); simulated_motion=true; _set_transform(p_xform); - - } */ diff --git a/servers/physics_3d/body_pair_3d_sw.h b/servers/physics_3d/body_pair_3d_sw.h index 59e36e7ea5..5f08d0cfa9 100644 --- a/servers/physics_3d/body_pair_3d_sw.h +++ b/servers/physics_3d/body_pair_3d_sw.h @@ -36,7 +36,6 @@ class BodyPair3DSW : public Constraint3DSW { enum { - MAX_CONTACTS = 4 }; diff --git a/servers/physics_3d/gjk_epa.cpp b/servers/physics_3d/gjk_epa.cpp index d99a2532f8..e14949543e 100644 --- a/servers/physics_3d/gjk_epa.cpp +++ b/servers/physics_3d/gjk_epa.cpp @@ -102,7 +102,6 @@ typedef unsigned char U1; // MinkowskiDiff struct MinkowskiDiff { - const Shape3DSW* m_shapes[2]; Transform transform_A; @@ -127,7 +126,6 @@ struct MinkowskiDiff { return ( Support1 ( d ) ); } else { return ( Support0 ( d ) ); - } } }; @@ -281,7 +279,6 @@ struct GJK } } if(mask==15) { m_status=eStatus::Inside; - } } else @@ -312,12 +309,10 @@ struct GJK axis[i]=1; appendvertice(*m_simplex, axis); if(EncloseOrigin()) { return(true); - } removevertice(*m_simplex); appendvertice(*m_simplex,-axis); if(EncloseOrigin()) { return(true); - } removevertice(*m_simplex); } @@ -335,12 +330,10 @@ struct GJK { appendvertice(*m_simplex, p); if(EncloseOrigin()) { return(true); - } removevertice(*m_simplex); appendvertice(*m_simplex,-p); if(EncloseOrigin()) { return(true); - } removevertice(*m_simplex); } @@ -355,12 +348,10 @@ struct GJK { appendvertice(*m_simplex,n); if(EncloseOrigin()) { return(true); - } removevertice(*m_simplex); appendvertice(*m_simplex,-n); if(EncloseOrigin()) { return(true); - } removevertice(*m_simplex); } @@ -372,7 +363,6 @@ struct GJK m_simplex->c[1]->w-m_simplex->c[3]->w, m_simplex->c[2]->w-m_simplex->c[3]->w))>0) { return(true); - } } break; @@ -580,7 +570,6 @@ struct GJK face->l[0] = nullptr; face->l[1] = list.root; if(list.root) { list.root->l[0]=face; - } list.root = face; ++list.count; @@ -588,13 +577,10 @@ struct GJK static inline void remove(sList& list,sFace* face) { if(face->l[1]) { face->l[1]->l[0]=face->l[0]; - } if(face->l[0]) { face->l[0]->l[1]=face->l[1]; - } if(face==list.root) { list.root=face->l[1]; - } --list.count; } @@ -616,7 +602,6 @@ struct GJK GJK::sSimplex& simplex=*gjk.m_simplex; if((simplex.rank>1)&&gjk.EncloseOrigin()) { - /* Clean up */ while(m_hull.root) { @@ -677,7 +662,6 @@ struct GJK append(m_stock,best); best=findbest(); if(best->p>=outer.p) { outer=*best; - } } else { m_status=eStatus::InvalidHull;break; } } else { m_status=eStatus::AccuraryReached;break; } @@ -711,7 +695,6 @@ struct GJK m_normal = m_normal/nl; } else { m_normal = Vector3(1,0,0); - } m_depth = 0; m_result.rank=1; @@ -747,10 +730,8 @@ struct GJK { return(face); } else { m_status=eStatus::NonConvex; - } } else { m_status=eStatus::Degenerated; - } remove(m_hull,face); append(m_stock,face); @@ -793,7 +774,6 @@ struct GJK { bind(nf,0,f,e); if(horizon.cf) { bind(horizon.cf,1,nf,2); } else { horizon.ff=nf; - } horizon.cf=nf; ++horizon.nf; @@ -917,7 +897,6 @@ bool Penetration( const Shape3DSW* shape0, results.distance = -epa.m_depth; return(true); } else { results.status=sResults::EPA_Failed; - } } break; @@ -948,8 +927,6 @@ bool Penetration( const Shape3DSW* shape0, #undef EPA_FALLBACK #undef EPA_PLANE_EPS #undef EPA_INSIDE_EPS - - } // end of namespace /* clang-format on */ diff --git a/servers/physics_3d/joints/generic_6dof_joint_3d_sw.cpp b/servers/physics_3d/joints/generic_6dof_joint_3d_sw.cpp index fede40ca65..9ce5ebb7ed 100644 --- a/servers/physics_3d/joints/generic_6dof_joint_3d_sw.cpp +++ b/servers/physics_3d/joints/generic_6dof_joint_3d_sw.cpp @@ -253,7 +253,6 @@ void Generic6DOFJoint3DSW::calculateAngleInfo() { /* if(m_debugDrawer) { - char buff[300]; sprintf(buff,"\n X: %.2f ; Y: %.2f ; Z: %.2f ", m_calculatedAxisAngleDiff[0], diff --git a/servers/physics_3d/joints/hinge_joint_3d_sw.cpp b/servers/physics_3d/joints/hinge_joint_3d_sw.cpp index 52c7389e1f..c6fbc0f55f 100644 --- a/servers/physics_3d/joints/hinge_joint_3d_sw.cpp +++ b/servers/physics_3d/joints/hinge_joint_3d_sw.cpp @@ -365,7 +365,6 @@ void HingeJoint3DSW::solve(real_t p_step) { void HingeJointSW::updateRHS(real_t timeStep) { (void)timeStep; - } */ diff --git a/servers/physics_3d/joints/slider_joint_3d_sw.cpp b/servers/physics_3d/joints/slider_joint_3d_sw.cpp index 43bd49b4b5..dd6cc04f7c 100644 --- a/servers/physics_3d/joints/slider_joint_3d_sw.cpp +++ b/servers/physics_3d/joints/slider_joint_3d_sw.cpp @@ -105,7 +105,6 @@ void SliderJoint3DSW::initParams() { m_targetAngMotorVelocity = real_t(0.); m_maxAngMotorForce = real_t(0.); m_accumulatedAngMotorImpulse = real_t(0.0); - } // SliderJointSW::initParams() //----------------------------------------------------------------------------- diff --git a/servers/physics_3d/physics_server_3d_sw.cpp b/servers/physics_3d/physics_server_3d_sw.cpp index 143cc9ebbd..07a7498fec 100644 --- a/servers/physics_3d/physics_server_3d_sw.cpp +++ b/servers/physics_3d/physics_server_3d_sw.cpp @@ -174,7 +174,7 @@ real_t PhysicsServer3DSW::space_get_param(RID p_space, SpaceParameter p_param) c PhysicsDirectSpaceState3D *PhysicsServer3DSW::space_get_direct_state(RID p_space) { Space3DSW *space = space_owner.getornull(p_space); ERR_FAIL_COND_V(!space, nullptr); - ERR_FAIL_COND_V_MSG(!doing_sync || space->is_locked(), nullptr, "Space state is inaccessible right now, wait for iteration or physics process notification."); + ERR_FAIL_COND_V_MSG(space->is_locked(), nullptr, "Space state is inaccessible right now, wait for iteration or physics process notification."); return space->get_direct_state(); } @@ -888,7 +888,7 @@ int PhysicsServer3DSW::body_test_ray_separation(RID p_body, const Transform &p_t PhysicsDirectBodyState3D *PhysicsServer3DSW::body_get_direct_state(RID p_body) { Body3DSW *body = body_owner.getornull(p_body); ERR_FAIL_COND_V(!body, nullptr); - ERR_FAIL_COND_V_MSG(!doing_sync || body->get_space()->is_locked(), nullptr, "Body state is inaccessible right now, wait for iteration or physics process notification."); + ERR_FAIL_COND_V_MSG(body->get_space()->is_locked(), nullptr, "Body state is inaccessible right now, wait for iteration or physics process notification."); direct_state->body = body; return direct_state; @@ -1287,7 +1287,6 @@ void PhysicsServer3DSW::set_active(bool p_active) { }; void PhysicsServer3DSW::init() { - doing_sync = true; last_step = 0.001; iterations = 8; // 8? stepper = memnew(Step3DSW); @@ -1303,8 +1302,6 @@ void PhysicsServer3DSW::step(real_t p_step) { _update_shapes(); - doing_sync = false; - last_step = p_step; PhysicsDirectBodyState3DSW::singleton->step = p_step; @@ -1327,8 +1324,6 @@ void PhysicsServer3DSW::flush_queries() { return; } - doing_sync = true; - flushing_queries = true; uint64_t time_beg = OS::get_singleton()->get_ticks_usec(); diff --git a/servers/physics_3d/physics_server_3d_sw.h b/servers/physics_3d/physics_server_3d_sw.h index a45ee7c2c8..f96a8863c3 100644 --- a/servers/physics_3d/physics_server_3d_sw.h +++ b/servers/physics_3d/physics_server_3d_sw.h @@ -44,7 +44,6 @@ class PhysicsServer3DSW : public PhysicsServer3D { friend class PhysicsDirectSpaceState3DSW; bool active; int iterations; - bool doing_sync; real_t last_step; int island_count; @@ -365,7 +364,6 @@ public: virtual void set_active(bool p_active) override; virtual void init() override; virtual void step(real_t p_step) override; - virtual void sync() override {} virtual void flush_queries() override; virtual void finish() override; diff --git a/servers/physics_3d/shape_3d_sw.cpp b/servers/physics_3d/shape_3d_sw.cpp index b8b01de3c3..107f850ebd 100644 --- a/servers/physics_3d/shape_3d_sw.cpp +++ b/servers/physics_3d/shape_3d_sw.cpp @@ -333,7 +333,6 @@ void BoxShape3DSW::get_supports(const Vector3 &p_normal, int p_max, Vector3 *r_s int i_n2 = next2[i]; static const real_t sign[4][2] = { - { -1.0, 1.0 }, { 1.0, 1.0 }, { 1.0, -1.0 }, diff --git a/servers/physics_3d/space_3d_sw.h b/servers/physics_3d/space_3d_sw.h index 6380bcc6dc..22535a6adb 100644 --- a/servers/physics_3d/space_3d_sw.h +++ b/servers/physics_3d/space_3d_sw.h @@ -97,7 +97,6 @@ private: real_t test_motion_min_contact_depth; enum { - INTERSECTION_QUERY_MAX = 2048 }; diff --git a/servers/physics_server_2d.h b/servers/physics_server_2d.h index 283a2ab58c..f7607d5dd5 100644 --- a/servers/physics_server_2d.h +++ b/servers/physics_server_2d.h @@ -276,7 +276,6 @@ public: virtual bool space_is_active(RID p_space) const = 0; enum SpaceParameter { - SPACE_PARAM_CONTACT_RECYCLE_RADIUS, SPACE_PARAM_CONTACT_MAX_SEPARATION, SPACE_PARAM_BODY_MAX_ALLOWED_PENETRATION, @@ -526,7 +525,6 @@ public: /* JOINT API */ enum JointType { - JOINT_PIN, JOINT_GROOVE, JOINT_DAMPED_SPRING @@ -587,7 +585,6 @@ public: virtual bool is_flushing_queries() const = 0; enum ProcessInfo { - INFO_ACTIVE_OBJECTS, INFO_COLLISION_PAIRS, INFO_ISLAND_COUNT diff --git a/servers/physics_server_3d.h b/servers/physics_server_3d.h index 90ef2bb8dd..3f7ad26257 100644 --- a/servers/physics_server_3d.h +++ b/servers/physics_server_3d.h @@ -259,7 +259,6 @@ public: virtual bool space_is_active(RID p_space) const = 0; enum SpaceParameter { - SPACE_PARAM_CONTACT_RECYCLE_RADIUS, SPACE_PARAM_CONTACT_MAX_SEPARATION, SPACE_PARAM_BODY_MAX_ALLOWED_PENETRATION, @@ -578,7 +577,6 @@ public: /* JOINT API */ enum JointType { - JOINT_PIN, JOINT_HINGE, JOINT_SLIDER, @@ -613,7 +611,6 @@ public: virtual Vector3 pin_joint_get_local_b(RID p_joint) const = 0; enum HingeJointParam { - HINGE_JOINT_BIAS, HINGE_JOINT_LIMIT_UPPER, HINGE_JOINT_LIMIT_LOWER, @@ -714,7 +711,6 @@ public: }; enum G6DOFJointAxisFlag { - G6DOF_JOINT_FLAG_ENABLE_LINEAR_LIMIT, G6DOF_JOINT_FLAG_ENABLE_ANGULAR_LIMIT, G6DOF_JOINT_FLAG_ENABLE_ANGULAR_SPRING, @@ -749,14 +745,12 @@ public: virtual void set_active(bool p_active) = 0; virtual void init() = 0; virtual void step(float p_step) = 0; - virtual void sync() = 0; virtual void flush_queries() = 0; virtual void finish() = 0; virtual bool is_flushing_queries() const = 0; enum ProcessInfo { - INFO_ACTIVE_OBJECTS, INFO_COLLISION_PAIRS, INFO_ISLAND_COUNT diff --git a/servers/register_server_types.cpp b/servers/register_server_types.cpp index 34980aaf66..0ba9ce9e76 100644 --- a/servers/register_server_types.cpp +++ b/servers/register_server_types.cpp @@ -65,9 +65,9 @@ #include "rendering/rasterizer.h" #include "rendering/rendering_device.h" #include "rendering/rendering_device_binds.h" - #include "rendering_server.h" #include "servers/rendering/shader_types.h" +#include "text_server.h" #include "xr/xr_interface.h" #include "xr/xr_positional_tracker.h" #include "xr_server.h" @@ -102,6 +102,11 @@ void register_server_types() { ClassDB::register_virtual_class<DisplayServer>(); ClassDB::register_virtual_class<RenderingServer>(); ClassDB::register_class<AudioServer>(); + + ClassDB::register_class<TextServerManager>(); + ClassDB::register_virtual_class<TextServer>(); + TextServer::initialize_hex_code_box_fonts(); + ClassDB::register_virtual_class<PhysicsServer2D>(); ClassDB::register_virtual_class<PhysicsServer3D>(); ClassDB::register_virtual_class<NavigationServer2D>(); @@ -209,6 +214,7 @@ void register_server_types() { void unregister_server_types() { memdelete(shader_types); + TextServer::finish_hex_code_box_fonts(); } void register_server_singletons() { @@ -219,6 +225,7 @@ void register_server_singletons() { Engine::get_singleton()->add_singleton(Engine::Singleton("PhysicsServer3D", PhysicsServer3D::get_singleton())); Engine::get_singleton()->add_singleton(Engine::Singleton("NavigationServer2D", NavigationServer2D::get_singleton_mut())); Engine::get_singleton()->add_singleton(Engine::Singleton("NavigationServer3D", NavigationServer3D::get_singleton_mut())); + Engine::get_singleton()->add_singleton(Engine::Singleton("TextServerManager", TextServerManager::get_singleton())); Engine::get_singleton()->add_singleton(Engine::Singleton("XRServer", XRServer::get_singleton())); Engine::get_singleton()->add_singleton(Engine::Singleton("CameraServer", CameraServer::get_singleton())); } diff --git a/servers/rendering/rasterizer.h b/servers/rendering/rasterizer.h index efaa8f138a..30577e6247 100644 --- a/servers/rendering/rasterizer.h +++ b/servers/rendering/rasterizer.h @@ -109,7 +109,7 @@ public: 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_adjustment(RID p_env, bool p_enable, float p_brightness, float p_contrast, float p_saturation, bool p_use_1d_color_correction, RID p_color_correction) = 0; virtual void environment_set_fog(RID p_env, bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density, float p_aerial_perspective) = 0; @@ -522,6 +522,8 @@ public: 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_sky_only(RID p_light, bool p_sky_only) = 0; + virtual bool light_directional_is_sky_only(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; @@ -752,6 +754,9 @@ public: 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 void render_target_set_sdf_size_and_scale(RID p_render_target, RS::ViewportSDFOversize p_size, RS::ViewportSDFScale p_scale) = 0; + virtual Rect2i render_target_get_sdf_rect(RID p_render_target) const = 0; + virtual RS::InstanceType get_base_type(RID p_rid) const = 0; virtual bool free(RID p_rid) = 0; @@ -809,7 +814,6 @@ public: static RasterizerCanvas *singleton; enum CanvasRectFlags { - CANVAS_RECT_REGION = 1, CANVAS_RECT_TILE = 2, CANVAS_RECT_FLIP_H = 4, @@ -943,7 +947,6 @@ public: struct Command { enum Type { - TYPE_RECT, TYPE_NINEPATCH, TYPE_POLYGON, @@ -1326,7 +1329,7 @@ public: } }; - virtual void canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, Light *p_directional_list, const Transform2D &p_canvas_transform, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel) = 0; + virtual void canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, Light *p_directional_list, const Transform2D &p_canvas_transform, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, bool &r_sdf_used) = 0; virtual void canvas_debug_viewport_shadows(Light *p_lights_with_shadow) = 0; struct LightOccluderInstance { @@ -1338,12 +1341,14 @@ public: Transform2D xform; Transform2D xform_cache; int light_mask; + bool sdf_collision; RS::CanvasOccluderPolygonCullMode cull_cache; LightOccluderInstance *next; LightOccluderInstance() { enabled = true; + sdf_collision = false; next = nullptr; light_mask = 1; cull_cache = RS::CANVAS_OCCLUDER_POLYGON_CULL_DISABLED; @@ -1356,8 +1361,10 @@ public: virtual void light_update_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders) = 0; virtual void light_update_directional_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_cull_distance, const Rect2 &p_clip_rect, LightOccluderInstance *p_occluders) = 0; + virtual void render_sdf(RID p_render_target, 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_shape(RID p_occluder, const Vector<Vector2> &p_points, bool p_closed) = 0; virtual void occluder_polygon_set_cull_mode(RID p_occluder, RS::CanvasOccluderPolygonCullMode p_mode) = 0; virtual void set_shadow_texture_size(int p_size) = 0; diff --git a/servers/rendering/rasterizer_rd/rasterizer_canvas_rd.cpp b/servers/rendering/rasterizer_rd/rasterizer_canvas_rd.cpp index 5d9e68f2b4..921a7b966e 100644 --- a/servers/rendering/rasterizer_rd/rasterizer_canvas_rd.cpp +++ b/servers/rendering/rasterizer_rd/rasterizer_canvas_rd.cpp @@ -30,6 +30,7 @@ #include "rasterizer_canvas_rd.h" #include "core/config/project_settings.h" +#include "core/math/geometry_2d.h" #include "core/math/math_funcs.h" #include "rasterizer_rd.h" @@ -687,7 +688,6 @@ void RasterizerCanvasRD::_render_item(RD::DrawListID p_draw_list, const Item *p_ #if 0 case Item::Command::TYPE_MESH: { - Item::CommandMesh *mesh = static_cast<Item::CommandMesh *>(c); _set_texture_rect_mode(false); @@ -702,7 +702,6 @@ void RasterizerCanvasRD::_render_item(RD::DrawListID p_draw_list, const Item *p_ 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 @@ -723,7 +722,6 @@ void RasterizerCanvasRD::_render_item(RD::DrawListID p_draw_list, const Item *p_ } break; case Item::Command::TYPE_MULTIMESH: { - Item::CommandMultiMesh *mmesh = static_cast<Item::CommandMultiMesh *>(c); RasterizerStorageGLES3::MultiMesh *multi_mesh = storage->multimesh_owner.getornull(mmesh->multimesh); @@ -786,7 +784,6 @@ void RasterizerCanvasRD::_render_item(RD::DrawListID p_draw_list, const Item *p_ int custom_data_ofs = color_ofs; switch (multi_mesh->color_format) { - case RS::MULTIMESH_COLOR_NONE: { glDisableVertexAttribArray(11); glVertexAttrib4f(11, 1, 1, 1, 1); @@ -807,7 +804,6 @@ void RasterizerCanvasRD::_render_item(RD::DrawListID p_draw_list, const Item *p_ } switch (multi_mesh->custom_data_format) { - case RS::MULTIMESH_CUSTOM_DATA_NONE: { glDisableVertexAttribArray(12); glVertexAttrib4f(12, 1, 1, 1, 1); @@ -841,7 +837,6 @@ void RasterizerCanvasRD::_render_item(RD::DrawListID p_draw_list, const Item *p_ } 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); @@ -875,7 +870,6 @@ void RasterizerCanvasRD::_render_item(RD::DrawListID p_draw_list, const Item *p_ } 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)); @@ -893,7 +887,6 @@ void RasterizerCanvasRD::_render_item(RD::DrawListID p_draw_list, const Item *p_ 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); @@ -1059,10 +1052,19 @@ RID RasterizerCanvasRD::_create_base_uniform_set(RID p_to_render_target, bool p_ } { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_TEXTURE; + u.binding = 7; + RID sdf = storage->render_target_get_sdf_texture(p_to_render_target); + u.ids.push_back(sdf); + uniforms.push_back(u); + } + + { //needs samplers for the material (uses custom textures) create them RD::Uniform u; u.type = RD::UNIFORM_TYPE_SAMPLER; - u.binding = 7; + u.binding = 8; 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); @@ -1083,7 +1085,7 @@ RID RasterizerCanvasRD::_create_base_uniform_set(RID p_to_render_target, bool p_ { RD::Uniform u; u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER; - u.binding = 8; + u.binding = 9; u.ids.push_back(storage->global_variables_get_storage_buffer()); uniforms.push_back(u); } @@ -1190,7 +1192,8 @@ void RasterizerCanvasRD::_render_items(RID p_to_render_target, int p_item_count, 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, Light *p_directional_light_list, const Transform2D &p_canvas_transform, RenderingServer::CanvasItemTextureFilter p_default_filter, RenderingServer::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel) { +void RasterizerCanvasRD::canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, Light *p_directional_light_list, const Transform2D &p_canvas_transform, RenderingServer::CanvasItemTextureFilter p_default_filter, RenderingServer::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, bool &r_sdf_used) { + r_sdf_used = false; int item_count = 0; //setup canvas state uniforms if needed @@ -1373,6 +1376,25 @@ void RasterizerCanvasRD::canvas_render_items(RID p_to_render_target, Item *p_ite state_buffer.directional_light_count = directional_light_count; + Vector2 canvas_scale = p_canvas_transform.get_scale(); + + state_buffer.sdf_to_screen[0] = render_target_size.width / canvas_scale.x; + state_buffer.sdf_to_screen[1] = render_target_size.height / canvas_scale.y; + + state_buffer.screen_to_sdf[0] = 1.0 / state_buffer.sdf_to_screen[0]; + state_buffer.screen_to_sdf[1] = 1.0 / state_buffer.sdf_to_screen[1]; + + Rect2 sdf_rect = storage->render_target_get_sdf_rect(p_to_render_target); + Rect2 sdf_tex_rect(sdf_rect.position / canvas_scale, sdf_rect.size / canvas_scale); + + state_buffer.sdf_to_tex[0] = 1.0 / sdf_tex_rect.size.width; + state_buffer.sdf_to_tex[1] = 1.0 / sdf_tex_rect.size.height; + state_buffer.sdf_to_tex[2] = -sdf_tex_rect.position.x / sdf_tex_rect.size.width; + state_buffer.sdf_to_tex[3] = -sdf_tex_rect.position.y / sdf_tex_rect.size.height; + + //print_line("w: " + itos(ssize.width) + " s: " + rtos(canvas_scale)); + state_buffer.tex_to_sdf = 1.0 / ((canvas_scale.x + canvas_scale.y) * 0.5); + RD::get_singleton()->buffer_update(state.canvas_state_buffer, 0, sizeof(State::Buffer), &state_buffer, true); } @@ -1410,6 +1432,9 @@ void RasterizerCanvasRD::canvas_render_items(RID p_to_render_target, Item *p_ite } } + if (md->shader_data->uses_sdf) { + r_sdf_used = true; + } if (md->last_frame != RasterizerRD::singleton->get_frame_number()) { md->last_frame = RasterizerRD::singleton->get_frame_number(); if (!RD::get_singleton()->uniform_set_is_valid(md->uniform_set)) { @@ -1695,18 +1720,102 @@ void RasterizerCanvasRD::light_update_directional_shadow(RID p_rid, int p_shadow cl->shadow.directional_xform = to_shadow * to_light_xform; } +void RasterizerCanvasRD::render_sdf(RID p_render_target, LightOccluderInstance *p_occluders) { + RID fb = storage->render_target_get_sdf_framebuffer(p_render_target); + Rect2i rect = storage->render_target_get_sdf_rect(p_render_target); + + Transform2D to_sdf; + to_sdf.elements[0] *= rect.size.width; + to_sdf.elements[1] *= rect.size.height; + to_sdf.elements[2] = rect.position; + + Transform2D to_clip; + to_clip.elements[0] *= 2.0; + to_clip.elements[1] *= 2.0; + to_clip.elements[2] = -Vector2(1.0, 1.0); + + to_clip = to_clip * to_sdf.affine_inverse(); + + Vector<Color> cc; + cc.push_back(Color(0, 0, 0, 0)); + + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(fb, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, cc); + + CameraMatrix projection; + + 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]; + } + } + + push_constant.direction[0] = 0.0; + push_constant.direction[1] = 0.0; + push_constant.z_far = 0; + push_constant.pad = 0; + + LightOccluderInstance *instance = p_occluders; + + while (instance) { + OccluderPolygon *co = occluder_polygon_owner.getornull(instance->occluder); + + if (!co || co->sdf_index_array.is_null()) { + instance = instance->next; + continue; + } + + _update_transform_2d_to_mat2x4(to_clip * instance->xform_cache, push_constant.modelview); + + RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, shadow_render.sdf_render_pipelines[co->sdf_is_lines ? SHADOW_RENDER_SDF_LINES : SHADOW_RENDER_SDF_TRIANGLES]); + RD::get_singleton()->draw_list_bind_vertex_array(draw_list, co->sdf_vertex_array); + RD::get_singleton()->draw_list_bind_index_array(draw_list, co->sdf_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(); + + storage->render_target_sdf_process(p_render_target); //done rendering, process it +} + RID RasterizerCanvasRD::occluder_polygon_create() { OccluderPolygon occluder; - occluder.point_count = 0; + occluder.line_point_count = 0; + occluder.sdf_point_count = 0; + occluder.sdf_index_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) { +void RasterizerCanvasRD::occluder_polygon_set_shape(RID p_occluder, const Vector<Vector2> &p_points, bool p_closed) { OccluderPolygon *oc = occluder_polygon_owner.getornull(p_occluder); ERR_FAIL_COND(!oc); - if (oc->point_count != p_lines.size() && oc->vertex_array.is_valid()) { + Vector<Vector2> lines; + int lc = p_points.size() * 2; + + lines.resize(lc - (p_closed ? 0 : 2)); + { + Vector2 *w = lines.ptrw(); + const Vector2 *r = p_points.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; + } + } + + if (oc->line_point_count != 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); @@ -1716,12 +1825,14 @@ void RasterizerCanvasRD::occluder_polygon_set_shape_as_lines(RID p_occluder, con oc->vertex_buffer = RID(); oc->index_array = RID(); oc->index_buffer = RID(); + + oc->line_point_count = lines.size(); } - if (p_lines.size()) { + if (lines.size()) { Vector<uint8_t> geometry; Vector<uint8_t> indices; - int lc = p_lines.size(); + lc = lines.size(); geometry.resize(lc * 6 * sizeof(float)); indices.resize(lc * 3 * sizeof(uint16_t)); @@ -1732,7 +1843,7 @@ void RasterizerCanvasRD::occluder_polygon_set_shape_as_lines(RID p_occluder, con uint8_t *iw = indices.ptrw(); uint16_t *iwptr = (uint16_t *)iw; - const Vector2 *lr = p_lines.ptr(); + const Vector2 *lr = lines.ptr(); const int POLY_HEIGHT = 16384; @@ -1786,6 +1897,62 @@ void RasterizerCanvasRD::occluder_polygon_set_shape_as_lines(RID p_occluder, con RD::get_singleton()->buffer_update(oc->index_buffer, 0, indices.size(), ir); } } + + // sdf + + Vector<int> sdf_indices; + + if (p_closed) { + sdf_indices = Geometry2D::triangulate_polygon(p_points); + oc->sdf_is_lines = false; + } else { + int max = p_points.size(); + sdf_indices.resize(max * 2); + + int *iw = sdf_indices.ptrw(); + for (int i = 0; i < max; i++) { + iw[i * 2 + 0] = i; + iw[i * 2 + 1] = (i + 1) % max; + } + oc->sdf_is_lines = true; + } + + if (oc->sdf_index_count != sdf_indices.size() && oc->sdf_point_count != p_points.size() && oc->sdf_vertex_array.is_valid()) { + RD::get_singleton()->free(oc->sdf_vertex_array); + RD::get_singleton()->free(oc->sdf_vertex_buffer); + RD::get_singleton()->free(oc->sdf_index_array); + RD::get_singleton()->free(oc->sdf_index_buffer); + + oc->sdf_vertex_array = RID(); + oc->sdf_vertex_buffer = RID(); + oc->sdf_index_array = RID(); + oc->sdf_index_buffer = RID(); + + oc->sdf_index_count = sdf_indices.size(); + oc->sdf_point_count = p_points.size(); + + oc->sdf_is_lines = false; + } + + if (sdf_indices.size()) { + if (oc->sdf_vertex_array.is_null()) { + //create from scratch + //vertices + oc->sdf_vertex_buffer = RD::get_singleton()->vertex_buffer_create(p_points.size() * 2 * sizeof(real_t), p_points.to_byte_array()); + oc->sdf_index_buffer = RD::get_singleton()->index_buffer_create(sdf_indices.size(), RD::INDEX_BUFFER_FORMAT_UINT32, sdf_indices.to_byte_array()); + oc->sdf_index_array = RD::get_singleton()->index_array_create(oc->sdf_index_buffer, 0, sdf_indices.size()); + + Vector<RID> buffer; + buffer.push_back(oc->sdf_vertex_buffer); + oc->sdf_vertex_array = RD::get_singleton()->vertex_array_create(p_points.size(), shadow_render.sdf_vertex_format, buffer); + //indices + + } else { + //update existing + RD::get_singleton()->buffer_update(oc->vertex_buffer, 0, sizeof(real_t) * 2 * p_points.size(), p_points.ptr()); + RD::get_singleton()->buffer_update(oc->index_buffer, 0, sdf_indices.size() * sizeof(int32_t), sdf_indices.ptr()); + } + } } void RasterizerCanvasRD::occluder_polygon_set_cull_mode(RID p_occluder, RS::CanvasOccluderPolygonCullMode p_mode) { @@ -1802,6 +1969,7 @@ void RasterizerCanvasRD::ShaderData::set_code(const String &p_code) { ubo_size = 0; uniforms.clear(); uses_screen_texture = false; + uses_sdf = false; if (code == String()) { return; //just invalid, but no error @@ -1809,7 +1977,6 @@ void RasterizerCanvasRD::ShaderData::set_code(const String &p_code) { ShaderCompilerRD::GeneratedCode gen_code; - int light_mode = LIGHT_MODE_NORMAL; int blend_mode = BLEND_MODE_MIX; uses_screen_texture = false; @@ -1822,10 +1989,8 @@ void RasterizerCanvasRD::ShaderData::set_code(const String &p_code) { 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.usage_flag_pointers["texture_sdf"] = &uses_sdf; actions.uniforms = &uniforms; @@ -2046,6 +2211,7 @@ Variant RasterizerCanvasRD::ShaderData::get_default_parameter(const StringName & RasterizerCanvasRD::ShaderData::ShaderData() { valid = false; uses_screen_texture = false; + uses_sdf = false; } RasterizerCanvasRD::ShaderData::~ShaderData() { @@ -2310,6 +2476,11 @@ RasterizerCanvasRD::RasterizerCanvasRD(RasterizerStorageRD *p_storage) { actions.renames["LIGHT"] = "light"; actions.renames["SHADOW_MODULATE"] = "shadow_modulate"; + actions.renames["texture_sdf"] = "texture_sdf"; + actions.renames["texture_sdf_normal"] = "texture_sdf_normal"; + actions.renames["sdf_to_screen_uv"] = "sdf_to_screen_uv"; + actions.renames["screen_uv_to_sdf"] = "screen_uv_to_sdf"; + 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"; @@ -2319,6 +2490,8 @@ RasterizerCanvasRD::RasterizerCanvasRD(RasterizerStorageRD *p_storage) { actions.usage_defines["LIGHT"] = "#define LIGHT_SHADER_CODE_USED\n"; actions.render_mode_defines["skip_vertex_transform"] = "#define SKIP_TRANSFORM_USED\n"; + actions.render_mode_defines["unshaded"] = "#define MODE_UNSHADED\n"; + actions.render_mode_defines["light_only"] = "#define MODE_LIGHT_ONLY\n"; actions.custom_samplers["TEXTURE"] = "texture_sampler"; actions.custom_samplers["NORMAL_TEXTURE"] = "texture_sampler"; @@ -2339,7 +2512,8 @@ RasterizerCanvasRD::RasterizerCanvasRD(RasterizerStorageRD *p_storage) { { //shadow rendering Vector<String> versions; - versions.push_back(String()); //no versions + versions.push_back("\n#define MODE_SHADOW\n"); //shadow + versions.push_back("\n#define MODE_SDF\n"); //sdf shadow_render.shader.initialize(versions); { @@ -2360,16 +2534,34 @@ RasterizerCanvasRD::RasterizerCanvasRD(RasterizerStorageRD *p_storage) { shadow_render.framebuffer_format = RD::get_singleton()->framebuffer_format_create(attachments); } + { + Vector<RD::AttachmentFormat> attachments; + + RD::AttachmentFormat af_color; + af_color.format = RD::DATA_FORMAT_R8_UNORM; + af_color.usage_flags = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; + + attachments.push_back(af_color); + + shadow_render.sdf_framebuffer_format = RD::get_singleton()->framebuffer_format_create(attachments); + } + //pipelines Vector<RD::VertexAttribute> vf; RD::VertexAttribute vd; - vd.format = RD::DATA_FORMAT_R32G32B32_SFLOAT; + vd.format = sizeof(real_t) == sizeof(float) ? RD::DATA_FORMAT_R32G32B32_SFLOAT : RD::DATA_FORMAT_R64G64B64_SFLOAT; vd.location = 0; vd.offset = 0; - vd.stride = sizeof(float) * 3; + vd.stride = sizeof(real_t) * 3; vf.push_back(vd); shadow_render.vertex_format = RD::get_singleton()->vertex_format_create(vf); + vd.format = sizeof(real_t) == sizeof(float) ? RD::DATA_FORMAT_R32G32_SFLOAT : RD::DATA_FORMAT_R64G64_SFLOAT; + vd.stride = sizeof(real_t) * 2; + + vf.write[0] = vd; + shadow_render.sdf_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++) { @@ -2379,7 +2571,11 @@ RasterizerCanvasRD::RasterizerCanvasRD(RasterizerStorageRD *p_storage) { 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); + shadow_render.render_pipelines[i] = RD::get_singleton()->render_pipeline_create(shadow_render.shader.version_get_shader(shadow_render.shader_version, SHADOW_RENDER_MODE_SHADOW), shadow_render.framebuffer_format, shadow_render.vertex_format, RD::RENDER_PRIMITIVE_TRIANGLES, rs, RD::PipelineMultisampleState(), ds, RD::PipelineColorBlendState::create_disabled(), 0); + } + + for (int i = 0; i < 2; i++) { + shadow_render.sdf_render_pipelines[i] = RD::get_singleton()->render_pipeline_create(shadow_render.shader.version_get_shader(shadow_render.shader_version, SHADOW_RENDER_MODE_SDF), shadow_render.sdf_framebuffer_format, shadow_render.sdf_vertex_format, i == 0 ? RD::RENDER_PRIMITIVE_TRIANGLES : RD::RENDER_PRIMITIVE_LINES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_disabled(), 0); } } @@ -2490,7 +2686,7 @@ bool RasterizerCanvasRD::free(RID p_rid) { light_set_use_shadow(p_rid, false); 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_set_shape(p_rid, Vector<Vector2>(), false); occluder_polygon_owner.free(p_rid); } else { return false; diff --git a/servers/rendering/rasterizer_rd/rasterizer_canvas_rd.h b/servers/rendering/rasterizer_rd/rasterizer_canvas_rd.h index b516f63cbf..b09d6578f3 100644 --- a/servers/rendering/rasterizer_rd/rasterizer_canvas_rd.h +++ b/servers/rendering/rasterizer_rd/rasterizer_canvas_rd.h @@ -161,12 +161,6 @@ class RasterizerCanvasRD : public RasterizerCanvas { BLEND_MODE_DISABLED, }; - enum LightMode { - LIGHT_MODE_NORMAL, - LIGHT_MODE_UNSHADED, - LIGHT_MODE_LIGHT_ONLY - }; - bool valid; RID version; PipelineVariants pipeline_variants; @@ -181,7 +175,8 @@ class RasterizerCanvasRD : public RasterizerCanvas { String code; Map<StringName, RID> default_texture_params; - bool uses_screen_texture; + bool uses_screen_texture = false; + bool uses_sdf = false; virtual void set_code(const String &p_Code); virtual void set_default_texture_param(const StringName &p_name, RID p_texture); @@ -284,11 +279,19 @@ class RasterizerCanvasRD : public RasterizerCanvas { struct OccluderPolygon { RS::CanvasOccluderPolygonCullMode cull_mode; - int point_count; + int line_point_count; RID vertex_buffer; RID vertex_array; RID index_buffer; RID index_array; + + int sdf_point_count; + int sdf_index_count; + RID sdf_vertex_buffer; + RID sdf_vertex_array; + RID sdf_index_buffer; + RID sdf_index_array; + bool sdf_is_lines; }; struct LightUniform { @@ -310,12 +313,25 @@ class RasterizerCanvasRD : public RasterizerCanvas { RID_Owner<OccluderPolygon> occluder_polygon_owner; + enum ShadowRenderMode { + SHADOW_RENDER_MODE_SHADOW, + SHADOW_RENDER_MODE_SDF, + }; + + enum { + SHADOW_RENDER_SDF_TRIANGLES, + SHADOW_RENDER_SDF_LINES, + }; + struct { CanvasOcclusionShaderRD shader; RID shader_version; RID render_pipelines[3]; + RID sdf_render_pipelines[2]; RD::VertexFormatID vertex_format; + RD::VertexFormatID sdf_vertex_format; RD::FramebufferFormatID framebuffer_format; + RD::FramebufferFormatID sdf_framebuffer_format; } shadow_render; /***************/ @@ -336,8 +352,14 @@ class RasterizerCanvasRD : public RasterizerCanvas { float time; uint32_t use_pixel_snap; + float sdf_to_tex[4]; + float sdf_to_screen[2]; + float screen_to_sdf[2]; + uint32_t directional_light_count; - uint32_t pad[3]; + float tex_to_sdf; + uint32_t pad1; + uint32_t pad2; }; LightUniform *light_uniforms; @@ -423,11 +445,13 @@ public: void light_update_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders); void light_update_directional_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_cull_distance, const Rect2 &p_clip_rect, LightOccluderInstance *p_occluders); + virtual void render_sdf(RID p_render_target, 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_shape(RID p_occluder, const Vector<Vector2> &p_points, bool p_closed); 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, Light *p_directional_light_list, const Transform2D &p_canvas_transform, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel); + void canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, Light *p_directional_light_list, const Transform2D &p_canvas_transform, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, bool &r_sdf_used); void canvas_debug_viewport_shadows(Light *p_lights_with_shadow) {} diff --git a/servers/rendering/rasterizer_rd/rasterizer_effects_rd.cpp b/servers/rendering/rasterizer_rd/rasterizer_effects_rd.cpp index 97c1e7ba70..df94921652 100644 --- a/servers/rendering/rasterizer_rd/rasterizer_effects_rd.cpp +++ b/servers/rendering/rasterizer_rd/rasterizer_effects_rd.cpp @@ -94,7 +94,7 @@ RID RasterizerEffectsRD::_get_uniform_set_from_texture(RID p_texture, bool p_use 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 + //anything with the same configuration (one texture in binding 0 for set 0), is good RID uniform_set = RD::get_singleton()->uniform_set_create(uniforms, tonemap.shader.version_get_shader(tonemap.shader_version, 0), 0); texture_to_uniform_set_cache[p_texture] = uniform_set; @@ -718,7 +718,10 @@ void RasterizerEffectsRD::tonemapper(RID p_source_color, RID p_dst_framebuffer, 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; + int mode = p_settings.glow_use_bicubic_upscale ? TONEMAP_MODE_BICUBIC_GLOW_FILTER : TONEMAP_MODE_NORMAL; + if (p_settings.use_1d_color_correction) { + mode += 2; + } tonemap.push_constant.tonemapper = p_settings.tonemap_mode; tonemap.push_constant.use_auto_exposure = p_settings.use_auto_exposure; @@ -1423,6 +1426,8 @@ RasterizerEffectsRD::RasterizerEffectsRD() { Vector<String> tonemap_modes; tonemap_modes.push_back("\n"); tonemap_modes.push_back("\n#define USE_GLOW_FILTER_BICUBIC\n"); + tonemap_modes.push_back("\n#define USE_1D_LUT\n"); + tonemap_modes.push_back("\n#define USE_GLOW_FILTER_BICUBIC\n#define USE_1D_LUT\n"); tonemap.shader.initialize(tonemap_modes); diff --git a/servers/rendering/rasterizer_rd/rasterizer_effects_rd.h b/servers/rendering/rasterizer_rd/rasterizer_effects_rd.h index a0bdd59fd2..0b8d3a8f27 100644 --- a/servers/rendering/rasterizer_rd/rasterizer_effects_rd.h +++ b/servers/rendering/rasterizer_rd/rasterizer_effects_rd.h @@ -167,6 +167,8 @@ class RasterizerEffectsRD { enum TonemapMode { TONEMAP_MODE_NORMAL, TONEMAP_MODE_BICUBIC_GLOW_FILTER, + TONEMAP_MODE_1D_LUT, + TONEMAP_MODE_BICUBIC_GLOW_FILTER_1D_LUT, TONEMAP_MODE_MAX }; @@ -198,7 +200,7 @@ class RasterizerEffectsRD { /* tonemap actually writes to a framebuffer, which is * better to do using the raster pipeline rather than - * comptute, as that framebuffer might be in different formats + * compute, as that framebuffer might be in different formats */ struct Tonemap { TonemapPushConstant push_constant; @@ -654,6 +656,7 @@ public: float saturation = 1.0; bool use_color_correction = false; + bool use_1d_color_correction = false; RID color_correction_texture; bool use_fxaa = false; diff --git a/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.cpp b/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.cpp index a275e46473..313188ba87 100644 --- a/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.cpp +++ b/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.cpp @@ -1238,7 +1238,6 @@ void RasterizerSceneHighEndRD::_setup_environment(RID p_environment, RID p_rende } #if 0 if (p_render_buffers.is_valid() && render_buffers_is_sdfgi_enabled(p_render_buffers)) { - scene_state.ubo.sdfgi_cascade_count = render_buffers_get_sdfgi_cascade_count(p_render_buffers); scene_state.ubo.sdfgi_probe_axis_size = render_buffers_get_sdfgi_cascade_probe_count(p_render_buffers); scene_state.ubo.sdfgi_cascade_probe_size[0] = scene_state.ubo.sdfgi_probe_axis_size - 1; //float version for performance @@ -1583,7 +1582,6 @@ void RasterizerSceneHighEndRD::_fill_render_list(InstanceBase **p_cull_result, i } break; #if 0 case RS::INSTANCE_IMMEDIATE: { - RasterizerStorageGLES3::Immediate *immediate = storage->immediate_owner.getornull(inst->base); ERR_CONTINUE(!immediate); diff --git a/servers/rendering/rasterizer_rd/rasterizer_scene_rd.cpp b/servers/rendering/rasterizer_rd/rasterizer_scene_rd.cpp index e1be9b0ef4..3c4cac7ba9 100644 --- a/servers/rendering/rasterizer_rd/rasterizer_scene_rd.cpp +++ b/servers/rendering/rasterizer_rd/rasterizer_scene_rd.cpp @@ -351,8 +351,7 @@ void RasterizerSceneRD::sdfgi_update(RID p_render_buffers, RID p_environment, co RD::TextureFormat tf_probe_average = tf_probes; tf_probe_average.format = RD::DATA_FORMAT_R32G32B32A32_SINT; //signed integer because SH are signed - tf_probe_average.type = RD::TEXTURE_TYPE_2D_ARRAY; - tf_probe_average.array_layers = 1; + tf_probe_average.type = RD::TEXTURE_TYPE_2D; sdfgi->lightprobe_history_scroll = RD::get_singleton()->texture_create(tf_probe_history, RD::TextureView()); sdfgi->lightprobe_average_scroll = RD::get_singleton()->texture_create(tf_probe_average, RD::TextureView()); @@ -1187,6 +1186,11 @@ void RasterizerSceneRD::sdfgi_update_probes(RID p_render_buffers, RID p_environm LightInstance *li = light_instance_owner.getornull(p_directional_light_instances[j]); ERR_CONTINUE(!li); + + if (storage->light_directional_is_sky_only(li->light)) { + continue; + } + Vector3 dir = -li->transform.basis.get_axis(Vector3::AXIS_Z); dir.y *= rb->sdfgi->y_mult; dir.normalize(); @@ -1471,7 +1475,9 @@ void RasterizerSceneRD::_setup_giprobes(RID p_render_buffers, const Transform &p } if (giprobes_changed) { - RD::get_singleton()->free(rb->gi_uniform_set); + if (RD::get_singleton()->uniform_set_is_valid(rb->gi_uniform_set)) { + RD::get_singleton()->free(rb->gi_uniform_set); + } rb->gi_uniform_set = RID(); if (rb->volumetric_fog) { if (RD::get_singleton()->uniform_set_is_valid(rb->volumetric_fog->uniform_set)) { @@ -4391,6 +4397,11 @@ void RasterizerSceneRD::gi_probe_update(RID p_probe, bool p_update_light_instanc RID light = light_instance_get_base_light(light_instance); l.type = storage->light_get_type(light); + if (l.type == RS::LIGHT_DIRECTIONAL && storage->light_directional_is_sky_only(light)) { + light_count--; + continue; + } + 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(); @@ -5289,8 +5300,6 @@ void RasterizerSceneRD::_render_buffers_post_process_and_tonemap(RID p_render_bu //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; @@ -5327,6 +5336,21 @@ void RasterizerSceneRD::_render_buffers_post_process_and_tonemap(RID p_render_bu tonemap.exposure = env->exposure; } + if (can_use_effects && env) { + tonemap.use_bcs = env->adjustments_enabled; + tonemap.brightness = env->adjustments_brightness; + tonemap.contrast = env->adjustments_contrast; + tonemap.saturation = env->adjustments_saturation; + tonemap.use_1d_color_correction = env->use_1d_color_correction; + if (env->adjustments_enabled && env->color_correction.is_valid()) { + tonemap.use_color_correction = true; + tonemap.color_correction_texture = storage->texture_get_rd_texture(env->color_correction); + } else { + tonemap.use_color_correction = false; + tonemap.color_correction_texture = storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_WHITE); + } + } + storage->get_effects()->tonemapper(rb->texture, storage->render_target_get_rd_framebuffer(rb->render_target), tonemap); } @@ -5394,6 +5418,18 @@ void RasterizerSceneRD::_render_buffers_debug_draw(RID p_render_buffers, RID p_s } } +void RasterizerSceneRD::environment_set_adjustment(RID p_env, bool p_enable, float p_brightness, float p_contrast, float p_saturation, bool p_use_1d_color_correction, RID p_color_correction) { + Environment *env = environment_owner.getornull(p_env); + ERR_FAIL_COND(!env); + + env->adjustments_enabled = p_enable; + env->adjustments_brightness = p_brightness; + env->adjustments_contrast = p_contrast; + env->adjustments_saturation = p_saturation; + env->use_1d_color_correction = p_use_1d_color_correction; + env->color_correction = p_color_correction; +} + void RasterizerSceneRD::_sdfgi_debug_draw(RID p_render_buffers, const CameraMatrix &p_projection, const Transform &p_transform) { RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers); ERR_FAIL_COND(!rb); @@ -5930,7 +5966,40 @@ void RasterizerSceneRD::_setup_lights(RID *p_light_cull_result, int p_light_cull RS::LightType type = storage->light_get_type(base); switch (type) { case RS::LIGHT_DIRECTIONAL: { - if (r_directional_light_count >= cluster.max_directional_lights) { + // Copy to SkyDirectionalLightData + if (r_directional_light_count < sky_scene_state.max_directional_lights) { + SkyDirectionalLightData &sky_light_data = sky_scene_state.directional_lights[r_directional_light_count]; + Transform light_transform = light_instance_get_base_transform(li); + 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; + + float sign = storage->light_is_negative(base) ? -1 : 1; + sky_light_data.energy = sign * storage->light_get_param(base, RS::LIGHT_PARAM_ENERGY); + + Color linear_col = storage->light_get_color(base).to_linear(); + sky_light_data.color[0] = linear_col.r; + sky_light_data.color[1] = linear_col.g; + sky_light_data.color[2] = linear_col.b; + + sky_light_data.enabled = true; + + float angular_diameter = storage->light_get_param(base, RS::LIGHT_PARAM_SIZE); + if (angular_diameter > 0.0) { + // I know tan(0) is 0, but let's not risk it with numerical precision. + // technically this will keep expanding until reaching the sun, but all we care + // is expand until we reach the radius of the near plane (there can't be more occluders than that) + angular_diameter = Math::tan(Math::deg2rad(angular_diameter)); + } else { + angular_diameter = 0.0; + } + sky_light_data.size = angular_diameter; + sky_scene_state.ubo.directional_light_count++; + } + + if (r_directional_light_count >= cluster.max_directional_lights || storage->light_directional_is_sky_only(base)) { continue; } @@ -6073,27 +6142,6 @@ void RasterizerSceneRD::_setup_lights(RID *p_light_cull_result, int p_light_cull } } - // Copy to SkyDirectionalLightData - if (r_directional_light_count < sky_scene_state.max_directional_lights) { - SkyDirectionalLightData &sky_light_data = sky_scene_state.directional_lights[r_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_light_data.size = angular_diameter; - sky_scene_state.ubo.directional_light_count++; - } - r_directional_light_count++; } break; case RS::LIGHT_SPOT: @@ -8216,7 +8264,7 @@ RasterizerSceneRD::RasterizerSceneRD(RasterizerStorageRD *p_storage) { RD::Uniform u; u.type = RD::UNIFORM_TYPE_TEXTURE; u.binding = 0; - u.ids.push_back(storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE)); + u.ids.push_back(storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_CUBEMAP_WHITE)); uniforms.push_back(u); } { diff --git a/servers/rendering/rasterizer_rd/rasterizer_scene_rd.h b/servers/rendering/rasterizer_rd/rasterizer_scene_rd.h index 3d5310bb7e..6aa79208ea 100644 --- a/servers/rendering/rasterizer_rd/rasterizer_scene_rd.h +++ b/servers/rendering/rasterizer_rd/rasterizer_scene_rd.h @@ -763,6 +763,15 @@ private: float sdfgi_normal_bias = 1.1; float sdfgi_probe_bias = 1.1; RS::EnvironmentSDFGIYScale sdfgi_y_scale = RS::ENV_SDFGI_Y_SCALE_DISABLED; + + /// Adjustments + + bool adjustments_enabled = false; + float adjustments_brightness = 1.0f; + float adjustments_contrast = 1.0f; + float adjustments_saturation = 1.0f; + bool use_1d_color_correction = false; + RID color_correction = RID(); }; RS::EnvironmentSSAOQuality ssao_quality = RS::ENV_SSAO_QUALITY_MEDIUM; @@ -1571,7 +1580,7 @@ public: RS::EnvironmentSSRRoughnessQuality environment_get_ssr_roughness_quality() 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_adjustment(RID p_env, bool p_enable, float p_brightness, float p_contrast, float p_saturation, bool p_use_1d_color_correction, RID p_color_correction); virtual Ref<Image> environment_bake_panorama(RID p_env, bool p_bake_irradiance, const Size2i &p_size); diff --git a/servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp b/servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp index 8bd4362637..a4d79ffc87 100644 --- a/servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp +++ b/servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp @@ -5120,6 +5120,20 @@ bool RasterizerStorageRD::light_directional_get_blend_splits(RID p_light) const return light->directional_blend_splits; } +void RasterizerStorageRD::light_directional_set_sky_only(RID p_light, bool p_sky_only) { + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND(!light); + + light->directional_sky_only = p_sky_only; +} + +bool RasterizerStorageRD::light_directional_is_sky_only(RID p_light) const { + const Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND_V(!light, false); + + return light->directional_sky_only; +} + 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); @@ -5549,7 +5563,6 @@ void RasterizerStorageRD::gi_probe_allocate(RID p_gi_probe, const Transform &p_t } 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); @@ -6030,6 +6043,8 @@ void RasterizerStorageRD::_clear_render_target(RenderTarget *rt) { rt->backbuffer_uniform_set = RID(); //chain deleted } + _render_target_clear_sdf(rt); + rt->framebuffer = RID(); rt->color = RID(); } @@ -6300,6 +6315,275 @@ void RasterizerStorageRD::render_target_do_clear_request(RID p_render_target) { rt->clear_requested = false; } +void RasterizerStorageRD::render_target_set_sdf_size_and_scale(RID p_render_target, RS::ViewportSDFOversize p_size, RS::ViewportSDFScale p_scale) { + RenderTarget *rt = render_target_owner.getornull(p_render_target); + ERR_FAIL_COND(!rt); + if (rt->sdf_oversize == p_size && rt->sdf_scale == p_scale) { + return; + } + + rt->sdf_oversize = p_size; + rt->sdf_scale = p_scale; + + _render_target_clear_sdf(rt); +} + +Rect2i RasterizerStorageRD::_render_target_get_sdf_rect(const RenderTarget *rt) const { + Size2i margin; + int scale; + switch (rt->sdf_oversize) { + case RS::VIEWPORT_SDF_OVERSIZE_100_PERCENT: { + scale = 100; + } break; + case RS::VIEWPORT_SDF_OVERSIZE_120_PERCENT: { + scale = 120; + } break; + case RS::VIEWPORT_SDF_OVERSIZE_150_PERCENT: { + scale = 150; + } break; + case RS::VIEWPORT_SDF_OVERSIZE_200_PERCENT: { + scale = 200; + } break; + default: { + } + } + + margin = (rt->size * scale / 100) - rt->size; + + Rect2i r(Vector2i(), rt->size); + r.position -= margin; + r.size += margin * 2; + + return r; +} + +Rect2i RasterizerStorageRD::render_target_get_sdf_rect(RID p_render_target) const { + const RenderTarget *rt = render_target_owner.getornull(p_render_target); + ERR_FAIL_COND_V(!rt, Rect2i()); + + return _render_target_get_sdf_rect(rt); +} + +RID RasterizerStorageRD::render_target_get_sdf_texture(RID p_render_target) { + RenderTarget *rt = render_target_owner.getornull(p_render_target); + ERR_FAIL_COND_V(!rt, RID()); + if (rt->sdf_buffer_read.is_null()) { + // no texture, create a dummy one for the 2D uniform set + RD::TextureFormat tformat; + tformat.format = RD::DATA_FORMAT_R8G8B8A8_UNORM; + tformat.width = 4; + tformat.height = 4; + tformat.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT; + tformat.type = RD::TEXTURE_TYPE_2D; + + Vector<uint8_t> pv; + pv.resize(16 * 4); + zeromem(pv.ptrw(), 16 * 4); + Vector<Vector<uint8_t>> vpv; + + rt->sdf_buffer_read = RD::get_singleton()->texture_create(tformat, RD::TextureView(), vpv); + } + + return rt->sdf_buffer_read; +} + +void RasterizerStorageRD::_render_target_allocate_sdf(RenderTarget *rt) { + ERR_FAIL_COND(rt->sdf_buffer_write_fb.is_valid()); + if (rt->sdf_buffer_read.is_valid()) { + RD::get_singleton()->free(rt->sdf_buffer_read); + rt->sdf_buffer_read = RID(); + } + + Size2i size = _render_target_get_sdf_rect(rt).size; + + RD::TextureFormat tformat; + tformat.format = RD::DATA_FORMAT_R8_UNORM; + tformat.width = size.width; + tformat.height = size.height; + tformat.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; + tformat.type = RD::TEXTURE_TYPE_2D; + + rt->sdf_buffer_write = RD::get_singleton()->texture_create(tformat, RD::TextureView()); + + { + Vector<RID> write_fb; + write_fb.push_back(rt->sdf_buffer_write); + rt->sdf_buffer_write_fb = RD::get_singleton()->framebuffer_create(write_fb); + } + + int scale; + switch (rt->sdf_scale) { + case RS::VIEWPORT_SDF_SCALE_100_PERCENT: { + scale = 100; + } break; + case RS::VIEWPORT_SDF_SCALE_50_PERCENT: { + scale = 50; + } break; + case RS::VIEWPORT_SDF_SCALE_25_PERCENT: { + scale = 25; + } break; + default: { + scale = 100; + } break; + } + + rt->process_size = size * scale / 100; + rt->process_size.x = MAX(rt->process_size.x, 1); + rt->process_size.y = MAX(rt->process_size.y, 1); + + tformat.format = RD::DATA_FORMAT_R16G16_UINT; + tformat.width = rt->process_size.width; + tformat.height = rt->process_size.height; + tformat.usage_bits = RD::TEXTURE_USAGE_STORAGE_BIT; + + rt->sdf_buffer_process[0] = RD::get_singleton()->texture_create(tformat, RD::TextureView()); + rt->sdf_buffer_process[1] = RD::get_singleton()->texture_create(tformat, RD::TextureView()); + + tformat.format = RD::DATA_FORMAT_R16_UNORM; + tformat.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT; + + rt->sdf_buffer_read = RD::get_singleton()->texture_create(tformat, RD::TextureView()); + + { + Vector<RD::Uniform> uniforms; + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 1; + u.ids.push_back(rt->sdf_buffer_write); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 2; + u.ids.push_back(rt->sdf_buffer_read); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 3; + u.ids.push_back(rt->sdf_buffer_process[0]); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 4; + u.ids.push_back(rt->sdf_buffer_process[1]); + uniforms.push_back(u); + } + + rt->sdf_buffer_process_uniform_sets[0] = RD::get_singleton()->uniform_set_create(uniforms, rt_sdf.shader.version_get_shader(rt_sdf.shader_version, 0), 0); + SWAP(uniforms.write[2].ids.write[0], uniforms.write[3].ids.write[0]); + rt->sdf_buffer_process_uniform_sets[1] = RD::get_singleton()->uniform_set_create(uniforms, rt_sdf.shader.version_get_shader(rt_sdf.shader_version, 0), 0); + } +} + +void RasterizerStorageRD::_render_target_clear_sdf(RenderTarget *rt) { + if (rt->sdf_buffer_read.is_valid()) { + RD::get_singleton()->free(rt->sdf_buffer_read); + rt->sdf_buffer_read = RID(); + } + if (rt->sdf_buffer_write_fb.is_valid()) { + RD::get_singleton()->free(rt->sdf_buffer_write); + RD::get_singleton()->free(rt->sdf_buffer_process[0]); + RD::get_singleton()->free(rt->sdf_buffer_process[1]); + rt->sdf_buffer_write = RID(); + rt->sdf_buffer_write_fb = RID(); + rt->sdf_buffer_process[0] = RID(); + rt->sdf_buffer_process[1] = RID(); + rt->sdf_buffer_process_uniform_sets[0] = RID(); + rt->sdf_buffer_process_uniform_sets[1] = RID(); + } +} + +RID RasterizerStorageRD::render_target_get_sdf_framebuffer(RID p_render_target) { + RenderTarget *rt = render_target_owner.getornull(p_render_target); + ERR_FAIL_COND_V(!rt, RID()); + + if (rt->sdf_buffer_write_fb.is_null()) { + _render_target_allocate_sdf(rt); + } + + return rt->sdf_buffer_write_fb; +} +void RasterizerStorageRD::render_target_sdf_process(RID p_render_target) { + RenderTarget *rt = render_target_owner.getornull(p_render_target); + ERR_FAIL_COND(!rt); + ERR_FAIL_COND(rt->sdf_buffer_write_fb.is_null()); + + RenderTargetSDF::PushConstant push_constant; + + Rect2i r = _render_target_get_sdf_rect(rt); + + push_constant.size[0] = r.size.width; + push_constant.size[1] = r.size.height; + push_constant.stride = 0; + push_constant.shift = 0; + push_constant.base_size[0] = r.size.width; + push_constant.base_size[1] = r.size.height; + + bool shrink = false; + + switch (rt->sdf_scale) { + case RS::VIEWPORT_SDF_SCALE_50_PERCENT: { + push_constant.size[0] >>= 1; + push_constant.size[1] >>= 1; + push_constant.shift = 1; + shrink = true; + } break; + case RS::VIEWPORT_SDF_SCALE_25_PERCENT: { + push_constant.size[0] >>= 2; + push_constant.size[1] >>= 2; + push_constant.shift = 2; + shrink = true; + } break; + default: { + }; + } + + RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); + + /* Load */ + + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, rt_sdf.pipelines[shrink ? RenderTargetSDF::SHADER_LOAD_SHRINK : RenderTargetSDF::SHADER_LOAD]); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rt->sdf_buffer_process_uniform_sets[1], 0); //fill [0] + RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(RenderTargetSDF::PushConstant)); + + RD::get_singleton()->compute_list_dispatch_threads(compute_list, push_constant.size[0], push_constant.size[1], 1, 8, 8, 1); + + /* Process */ + + int stride = nearest_power_of_2_templated(MAX(push_constant.size[0], push_constant.size[1]) / 2); + + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, rt_sdf.pipelines[RenderTargetSDF::SHADER_PROCESS]); + + RD::get_singleton()->compute_list_add_barrier(compute_list); + bool swap = false; + + //jumpflood + while (stride > 0) { + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rt->sdf_buffer_process_uniform_sets[swap ? 1 : 0], 0); + push_constant.stride = stride; + RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(RenderTargetSDF::PushConstant)); + RD::get_singleton()->compute_list_dispatch_threads(compute_list, push_constant.size[0], push_constant.size[1], 1, 8, 8, 1); + stride /= 2; + swap = !swap; + RD::get_singleton()->compute_list_add_barrier(compute_list); + } + + /* Store */ + + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, rt_sdf.pipelines[shrink ? RenderTargetSDF::SHADER_STORE_SHRINK : RenderTargetSDF::SHADER_STORE]); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rt->sdf_buffer_process_uniform_sets[swap ? 1 : 0], 0); + RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(RenderTargetSDF::PushConstant)); + RD::get_singleton()->compute_list_dispatch_threads(compute_list, push_constant.size[0], push_constant.size[1], 1, 8, 8, 1); + + RD::get_singleton()->compute_list_end(); +} + void RasterizerStorageRD::render_target_copy_to_back_buffer(RID p_render_target, const Rect2i &p_region, bool p_gen_mipmaps) { RenderTarget *rt = render_target_owner.getornull(p_render_target); ERR_FAIL_COND(!rt); @@ -7756,6 +8040,34 @@ RasterizerStorageRD::RasterizerStorageRD() { } } + { //create default cubemap white 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, 255); + pv.set(i * 4 + 1, 255); + pv.set(i * 4 + 2, 255); + pv.set(i * 4 + 3, 255); + } + + { + Vector<Vector<uint8_t>> vpv; + for (int i = 0; i < 6; i++) { + vpv.push_back(pv); + } + default_rd_textures[DEFAULT_RD_TEXTURE_CUBEMAP_WHITE] = RD::get_singleton()->texture_create(tformat, RD::TextureView(), vpv); + } + } + { //create default 3D RD::TextureFormat tformat; @@ -8128,6 +8440,24 @@ RasterizerStorageRD::RasterizerStorageRD() { particles_shader.copy_pipelines[i] = RD::get_singleton()->compute_pipeline_create(particles_shader.copy_shader.version_get_shader(particles_shader.copy_shader_version, i)); } } + + { + Vector<String> sdf_modes; + sdf_modes.push_back("\n#define MODE_LOAD\n"); + sdf_modes.push_back("\n#define MODE_LOAD_SHRINK\n"); + sdf_modes.push_back("\n#define MODE_PROCESS\n"); + sdf_modes.push_back("\n#define MODE_PROCESS_OPTIMIZED\n"); + sdf_modes.push_back("\n#define MODE_STORE\n"); + sdf_modes.push_back("\n#define MODE_STORE_SHRINK\n"); + + rt_sdf.shader.initialize(sdf_modes); + + rt_sdf.shader_version = rt_sdf.shader.version_create(); + + for (int i = 0; i < RenderTargetSDF::SHADER_MAX; i++) { + rt_sdf.pipelines[i] = RD::get_singleton()->compute_pipeline_create(rt_sdf.shader.version_get_shader(rt_sdf.shader_version, i)); + } + } } RasterizerStorageRD::~RasterizerStorageRD() { diff --git a/servers/rendering/rasterizer_rd/rasterizer_storage_rd.h b/servers/rendering/rasterizer_rd/rasterizer_storage_rd.h index 05cb1b4a73..42dd0616b0 100644 --- a/servers/rendering/rasterizer_rd/rasterizer_storage_rd.h +++ b/servers/rendering/rasterizer_rd/rasterizer_storage_rd.h @@ -35,6 +35,7 @@ #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/canvas_sdf.glsl.gen.h" #include "servers/rendering/rasterizer_rd/shaders/giprobe_sdf.glsl.gen.h" #include "servers/rendering/rasterizer_rd/shaders/particles.glsl.gen.h" #include "servers/rendering/rasterizer_rd/shaders/particles_copy.glsl.gen.h" @@ -155,6 +156,7 @@ public: DEFAULT_RD_TEXTURE_MULTIMESH_BUFFER, DEFAULT_RD_TEXTURE_CUBEMAP_BLACK, DEFAULT_RD_TEXTURE_CUBEMAP_ARRAY_BLACK, + DEFAULT_RD_TEXTURE_CUBEMAP_WHITE, DEFAULT_RD_TEXTURE_3D_WHITE, DEFAULT_RD_TEXTURE_2D_ARRAY_WHITE, DEFAULT_RD_TEXTURE_2D_UINT, @@ -848,6 +850,7 @@ private: 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; + bool directional_sky_only = false; uint64_t version = 0; RasterizerScene::InstanceDependency instance_dependency; @@ -1002,6 +1005,15 @@ private: RID framebuffer_uniform_set; RID backbuffer_uniform_set; + RID sdf_buffer_write; + RID sdf_buffer_write_fb; + RID sdf_buffer_process[2]; + RID sdf_buffer_read; + RID sdf_buffer_process_uniform_sets[2]; + RS::ViewportSDFOversize sdf_oversize = RS::VIEWPORT_SDF_OVERSIZE_120_PERCENT; + RS::ViewportSDFScale sdf_scale = RS::VIEWPORT_SDF_SCALE_50_PERCENT; + Size2i process_size; + //texture generated for this owner (nor RD). RID texture; bool was_used; @@ -1011,11 +1023,38 @@ private: Color clear_color; }; - RID_Owner<RenderTarget> render_target_owner; + mutable 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); + void _render_target_allocate_sdf(RenderTarget *rt); + void _render_target_clear_sdf(RenderTarget *rt); + Rect2i _render_target_get_sdf_rect(const RenderTarget *rt) const; + + struct RenderTargetSDF { + enum { + SHADER_LOAD, + SHADER_LOAD_SHRINK, + SHADER_PROCESS, + SHADER_PROCESS_OPTIMIZED, + SHADER_STORE, + SHADER_STORE_SHRINK, + SHADER_MAX + }; + + struct PushConstant { + int32_t size[2]; + int32_t stride; + int32_t shift; + int32_t base_size[2]; + int32_t pad[2]; + }; + + CanvasSdfShaderRD shader; + RID shader_version; + RID pipelines[SHADER_MAX]; + } rt_sdf; /* GLOBAL SHADER VARIABLES */ @@ -1499,6 +1538,8 @@ public: 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_sky_only(RID p_light, bool p_sky_only); + bool light_directional_is_sky_only(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; @@ -1929,6 +1970,12 @@ public: virtual void render_target_disable_clear_request(RID p_render_target); virtual void render_target_do_clear_request(RID p_render_target); + virtual void render_target_set_sdf_size_and_scale(RID p_render_target, RS::ViewportSDFOversize p_size, RS::ViewportSDFScale p_scale); + RID render_target_get_sdf_texture(RID p_render_target); + RID render_target_get_sdf_framebuffer(RID p_render_target); + void render_target_sdf_process(RID p_render_target); + virtual Rect2i render_target_get_sdf_rect(RID p_render_target) const; + Size2 render_target_get_size(RID p_render_target); RID render_target_get_rd_framebuffer(RID p_render_target); RID render_target_get_rd_texture(RID p_render_target); diff --git a/servers/rendering/rasterizer_rd/shader_compiler_rd.cpp b/servers/rendering/rasterizer_rd/shader_compiler_rd.cpp index 1a33e9a567..df5513435a 100644 --- a/servers/rendering/rasterizer_rd/shader_compiler_rd.cpp +++ b/servers/rendering/rasterizer_rd/shader_compiler_rd.cpp @@ -671,7 +671,6 @@ String ShaderCompilerRD::_dump_node_code(const SL::Node *p_node, int p_level, Ge } /* for(Map<StringName,SL::ShaderNode::Uniform>::Element *E=pnode->uniforms.front();E;E=E->next()) { - if (SL::is_sampler_type(E->get().type)) { continue; } @@ -1073,6 +1072,11 @@ String ShaderCompilerRD::_dump_node_code(const SL::Node *p_node, int p_level, Ge } else if (onode->op == SL::OP_CONSTRUCT) { code += String(vnode->name); } else { + 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 (internal_functions.has(vnode->name)) { code += vnode->name; is_texture_func = texture_functions.has(vnode->name); diff --git a/servers/rendering/rasterizer_rd/shaders/SCsub b/servers/rendering/rasterizer_rd/shaders/SCsub index 9d531d63ad..4cddf0f685 100644 --- a/servers/rendering/rasterizer_rd/shaders/SCsub +++ b/servers/rendering/rasterizer_rd/shaders/SCsub @@ -5,6 +5,7 @@ Import("env") if "RD_GLSL" in env["BUILDERS"]: env.RD_GLSL("canvas.glsl") env.RD_GLSL("canvas_occlusion.glsl") + env.RD_GLSL("canvas_sdf.glsl") env.RD_GLSL("copy.glsl") env.RD_GLSL("copy_to_fb.glsl") env.RD_GLSL("cubemap_roughness.glsl") diff --git a/servers/rendering/rasterizer_rd/shaders/canvas.glsl b/servers/rendering/rasterizer_rd/shaders/canvas.glsl index 2a0f94e733..51d7193a03 100644 --- a/servers/rendering/rasterizer_rd/shaders/canvas.glsl +++ b/servers/rendering/rasterizer_rd/shaders/canvas.glsl @@ -233,6 +233,30 @@ MATERIAL_UNIFORMS } material; #endif +vec2 screen_uv_to_sdf(vec2 p_uv) { + return canvas_data.screen_to_sdf * p_uv; +} + +float texture_sdf(vec2 p_sdf) { + vec2 uv = p_sdf * canvas_data.sdf_to_tex.xy + canvas_data.sdf_to_tex.zw; + float d = texture(sampler2D(sdf_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uv).r; + d = d * SDF_MAX_LENGTH - 1.0; + return d * canvas_data.tex_to_sdf; +} + +vec2 texture_sdf_normal(vec2 p_sdf) { + vec2 uv = p_sdf * canvas_data.sdf_to_tex.xy + canvas_data.sdf_to_tex.zw; + + const float EPSILON = 0.001; + return normalize(vec2( + texture(sampler2D(sdf_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uv + vec2(EPSILON, 0.0)).r - texture(sampler2D(sdf_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uv - vec2(EPSILON, 0.0)).r, + texture(sampler2D(sdf_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uv + vec2(0.0, EPSILON)).r - texture(sampler2D(sdf_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uv - vec2(0.0, EPSILON)).r)); +} + +vec2 sdf_to_screen_uv(vec2 p_sdf) { + return p_sdf * canvas_data.sdf_to_screen; +} + /* clang-format off */ FRAGMENT_SHADER_GLOBALS /* clang-format on */ @@ -500,8 +524,13 @@ FRAGMENT_SHADER_CODE color = vec4(0.0); //invisible by default due to using light mask } +#ifdef MODE_LIGHT_ONLY + color = vec4(0.0); +#else color *= canvas_data.canvas_modulation; -#ifdef USE_LIGHTING +#endif + +#if defined(USE_LIGHTING) && !defined(MODE_UNSHADED) // Directional Lights diff --git a/servers/rendering/rasterizer_rd/shaders/canvas_occlusion.glsl b/servers/rendering/rasterizer_rd/shaders/canvas_occlusion.glsl index 421282cd4d..5c25235c58 100644 --- a/servers/rendering/rasterizer_rd/shaders/canvas_occlusion.glsl +++ b/servers/rendering/rasterizer_rd/shaders/canvas_occlusion.glsl @@ -2,6 +2,8 @@ #version 450 +VERSION_DEFINES + layout(location = 0) in highp vec3 vertex; layout(push_constant, binding = 0, std430) uniform Constants { @@ -13,12 +15,16 @@ layout(push_constant, binding = 0, std430) uniform Constants { } constants; +#ifdef MODE_SHADOW layout(location = 0) out highp float depth; +#endif 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); +#ifdef MODE_SHADOW + depth = dot(constants.direction, vtx.xy); +#endif gl_Position = constants.projection * vtx; } @@ -26,6 +32,8 @@ void main() { #version 450 +VERSION_DEFINES + layout(push_constant, binding = 0, std430) uniform Constants { mat4 projection; mat2x4 modelview; @@ -35,9 +43,17 @@ layout(push_constant, binding = 0, std430) uniform Constants { } constants; +#ifdef MODE_SHADOW layout(location = 0) in highp float depth; layout(location = 0) out highp float distance_buf; +#else +layout(location = 0) out highp float sdf_buf; +#endif void main() { +#ifdef MODE_SHADOW distance_buf = depth / constants.z_far; +#else + sdf_buf = 1.0; +#endif } diff --git a/servers/rendering/rasterizer_rd/shaders/canvas_sdf.glsl b/servers/rendering/rasterizer_rd/shaders/canvas_sdf.glsl new file mode 100644 index 0000000000..302ad03b41 --- /dev/null +++ b/servers/rendering/rasterizer_rd/shaders/canvas_sdf.glsl @@ -0,0 +1,135 @@ +#[compute] + +#version 450 + +VERSION_DEFINES + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +layout(r8, set = 0, binding = 1) uniform restrict readonly image2D src_pixels; +layout(r16, set = 0, binding = 2) uniform restrict writeonly image2D dst_sdf; + +layout(rg16i, set = 0, binding = 3) uniform restrict readonly iimage2D src_process; +layout(rg16i, set = 0, binding = 4) uniform restrict writeonly iimage2D dst_process; + +layout(push_constant, binding = 0, std430) uniform Params { + ivec2 size; + int stride; + int shift; + ivec2 base_size; + uvec2 pad; +} +params; + +#define SDF_MAX_LENGTH 16384.0 + +void main() { + ivec2 pos = ivec2(gl_GlobalInvocationID.xy); + if (any(greaterThanEqual(pos, params.size))) { //too large, do nothing + return; + } + +#ifdef MODE_LOAD + + bool solid = imageLoad(src_pixels, pos).r > 0.5; + imageStore(dst_process, pos, solid ? ivec4(pos, 0, 0) : ivec4(ivec2(32767), 0, 0)); +#endif + +#ifdef MODE_LOAD_SHRINK + + int s = 1 << params.shift; + ivec2 base = pos << params.shift; + ivec2 center = base + ivec2(params.shift); + + ivec2 rel = ivec2(32767); + float d = 1e20; + for (int i = 0; i < s; i++) { + for (int j = 0; j < s; j++) { + ivec2 src_pos = base + ivec2(i, j); + if (any(greaterThanEqual(src_pos, params.base_size))) { + continue; + } + bool solid = imageLoad(src_pixels, src_pos).r > 0.5; + if (solid) { + float dist = length(vec2(src_pos - center)); + if (dist < d) { + d = dist; + rel = src_pos; + } + } + } + } + + imageStore(dst_process, pos, ivec4(rel, 0, 0)); +#endif + +#ifdef MODE_PROCESS + + ivec2 base = pos << params.shift; + ivec2 center = base + ivec2(params.shift); + + ivec2 rel = imageLoad(src_process, pos).xy; + + if (center != rel) { + //only process if it does not point to itself + const int ofs_table_size = 8; + const ivec2 ofs_table[ofs_table_size] = ivec2[]( + ivec2(-1, -1), + ivec2(0, -1), + ivec2(+1, -1), + + ivec2(-1, 0), + ivec2(+1, 0), + + ivec2(-1, +1), + ivec2(0, +1), + ivec2(+1, +1)); + + float dist = length(vec2(rel - center)); + for (int i = 0; i < ofs_table_size; i++) { + ivec2 src_pos = pos + ofs_table[i] * params.stride; + if (any(lessThan(src_pos, ivec2(0))) || any(greaterThanEqual(src_pos, params.size))) { + continue; + } + ivec2 src_rel = imageLoad(src_process, src_pos).xy; + float src_dist = length(vec2(src_rel - center)); + if (src_dist < dist) { + dist = src_dist; + rel = src_rel; + } + } + } + + imageStore(dst_process, pos, ivec4(rel, 0, 0)); +#endif + +#ifdef MODE_STORE + + ivec2 rel = imageLoad(src_process, pos).xy; + float d = length(vec2(rel - pos)); + if (d > 0.01) { + d += 1.0; //make it signed + } + d /= SDF_MAX_LENGTH; + d = clamp(d, 0.0, 1.0); + imageStore(dst_sdf, pos, vec4(d)); + +#endif + +#ifdef MODE_STORE_SHRINK + + ivec2 base = pos << params.shift; + ivec2 center = base + ivec2(params.shift); + + ivec2 rel = imageLoad(src_process, pos).xy; + float d = length(vec2(rel - center)); + + if (d > 0.01) { + d += 1.0; //make it signed + } + d /= SDF_MAX_LENGTH; + d = clamp(d, 0.0, 1.0); + imageStore(dst_sdf, pos, vec4(d)); + +#endif +} diff --git a/servers/rendering/rasterizer_rd/shaders/canvas_uniforms_inc.glsl b/servers/rendering/rasterizer_rd/shaders/canvas_uniforms_inc.glsl index bb39584cbb..cf7678ea31 100644 --- a/servers/rendering/rasterizer_rd/shaders/canvas_uniforms_inc.glsl +++ b/servers/rendering/rasterizer_rd/shaders/canvas_uniforms_inc.glsl @@ -3,6 +3,8 @@ #define M_PI 3.14159265359 +#define SDF_MAX_LENGTH 16384.0 + #define FLAGS_INSTANCING_STRIDE_MASK 0xF #define FLAGS_INSTANCING_ENABLED (1 << 4) #define FLAGS_INSTANCING_HAS_COLORS (1 << 5) @@ -24,6 +26,19 @@ #define FLAGS_DEFAULT_NORMAL_MAP_USED (1 << 26) #define FLAGS_DEFAULT_SPECULAR_MAP_USED (1 << 27) +#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 + // Push Constant layout(push_constant, binding = 0, std430) uniform DrawData { @@ -68,8 +83,12 @@ layout(set = 0, binding = 1, std140) uniform CanvasData { float time; bool use_pixel_snap; + vec4 sdf_to_tex; + vec2 screen_to_sdf; + vec2 sdf_to_screen; + uint directional_light_count; - uint pad0; + float tex_to_sdf; uint pad1; uint pad2; } @@ -115,10 +134,11 @@ layout(set = 0, binding = 4) uniform texture2D shadow_atlas_texture; layout(set = 0, binding = 5) uniform sampler shadow_sampler; layout(set = 0, binding = 6) uniform texture2D screen_texture; +layout(set = 0, binding = 7) uniform texture2D sdf_texture; -layout(set = 0, binding = 7) uniform sampler material_samplers[12]; +layout(set = 0, binding = 8) uniform sampler material_samplers[12]; -layout(set = 0, binding = 8, std430) restrict readonly buffer GlobalVariableData { +layout(set = 0, binding = 9, std430) restrict readonly buffer GlobalVariableData { vec4 data[]; } global_variables; diff --git a/servers/rendering/rasterizer_rd/shaders/scene_high_end.glsl b/servers/rendering/rasterizer_rd/shaders/scene_high_end.glsl index da3c60af04..285698f060 100644 --- a/servers/rendering/rasterizer_rd/shaders/scene_high_end.glsl +++ b/servers/rendering/rasterizer_rd/shaders/scene_high_end.glsl @@ -868,7 +868,6 @@ void light_process_omni(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 v 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; @@ -1158,7 +1157,6 @@ void light_process_spot(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 v #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; diff --git a/servers/rendering/rasterizer_rd/shaders/tonemap.glsl b/servers/rendering/rasterizer_rd/shaders/tonemap.glsl index 4cc4fd3f64..7de91fd541 100644 --- a/servers/rendering/rasterizer_rd/shaders/tonemap.glsl +++ b/servers/rendering/rasterizer_rd/shaders/tonemap.glsl @@ -23,7 +23,11 @@ layout(location = 0) in vec2 uv_interp; 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; +#ifdef USE_1D_LUT +layout(set = 3, binding = 0) uniform sampler2D source_color_correction; +#else +layout(set = 3, binding = 0) uniform sampler3D source_color_correction; +#endif layout(push_constant, binding = 1, std430) uniform Params { vec3 bcs; @@ -35,9 +39,9 @@ layout(push_constant, binding = 1, std430) uniform Params { uint tonemapper; uvec2 glow_texture_size; - float glow_intensity; uint pad3; + uint glow_mode; float glow_levels[7]; @@ -255,10 +259,18 @@ vec3 apply_bcs(vec3 color, vec3 bcs) { return color; } - -vec3 apply_color_correction(vec3 color, sampler3D correction_tex) { - return texture(correction_tex, color).rgb; +#ifdef USE_1D_LUT +vec3 apply_color_correction(vec3 color) { + color.r = texture(source_color_correction, vec2(color.r, 0.0f)).r; + color.g = texture(source_color_correction, vec2(color.g, 0.0f)).g; + color.b = texture(source_color_correction, vec2(color.b, 0.0f)).b; + return color; +} +#else +vec3 apply_color_correction(vec3 color) { + return textureLod(source_color_correction, color, 0.0).rgb; } +#endif vec3 do_fxaa(vec3 color, float exposure, vec2 uv_interp) { const float FXAA_REDUCE_MIN = (1.0 / 128.0); @@ -367,7 +379,7 @@ void main() { } if (params.use_color_correction) { - color = apply_color_correction(color, color_correction); + color = apply_color_correction(color); } frag_color = vec4(color, 1.0f); diff --git a/servers/rendering/rendering_server_canvas.cpp b/servers/rendering/rendering_server_canvas.cpp index 4480b79f75..0e61d53866 100644 --- a/servers/rendering/rendering_server_canvas.cpp +++ b/servers/rendering/rendering_server_canvas.cpp @@ -68,7 +68,11 @@ void RenderingServerCanvas::_render_canvas_item_tree(RID p_to_render_target, Can RENDER_TIMESTAMP("Render Canvas Items"); - RSG::canvas_render->canvas_render_items(p_to_render_target, list, p_modulate, p_lights, p_directional_lights, p_transform, p_default_filter, p_default_repeat, p_snap_2d_vertices_to_pixel); + bool sdf_flag; + RSG::canvas_render->canvas_render_items(p_to_render_target, list, p_modulate, p_lights, p_directional_lights, p_transform, p_default_filter, p_default_repeat, p_snap_2d_vertices_to_pixel, sdf_flag); + if (sdf_flag) { + sdf_used = true; + } } void _collect_ysort_children(RenderingServerCanvas::Item *p_canvas_item, Transform2D p_transform, RenderingServerCanvas::Item *p_material_owner, RenderingServerCanvas::Item **r_items, int &r_index) { @@ -115,7 +119,7 @@ void RenderingServerCanvas::_cull_canvas_item(Item *p_canvas_item, const Transfo Rect2 rect = ci->get_rect(); Transform2D xform = ci->xform; if (snapping_2d_transforms_to_pixel) { - xform.elements[2].floor(); + xform.elements[2] = xform.elements[2].floor(); } xform = p_transform * xform; @@ -301,6 +305,7 @@ void RenderingServerCanvas::_cull_canvas_item(Item *p_canvas_item, const Transfo void RenderingServerCanvas::render_canvas(RID p_render_target, Canvas *p_canvas, const Transform2D &p_transform, RasterizerCanvas::Light *p_lights, RasterizerCanvas::Light *p_directional_lights, const Rect2 &p_clip_rect, RenderingServer::CanvasItemTextureFilter p_default_filter, RenderingServer::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_transforms_to_pixel, bool p_snap_2d_vertices_to_pixel) { RENDER_TIMESTAMP(">Render Canvas"); + sdf_used = false; snapping_2d_transforms_to_pixel = p_snap_2d_transforms_to_pixel; if (p_canvas->children_order_dirty) { @@ -347,6 +352,10 @@ void RenderingServerCanvas::render_canvas(RID p_render_target, Canvas *p_canvas, RENDER_TIMESTAMP("<End Render Canvas"); } +bool RenderingServerCanvas::was_sdf_used() { + return sdf_used; +} + RID RenderingServerCanvas::canvas_create() { Canvas *canvas = memnew(Canvas); ERR_FAIL_COND_V(!canvas, RID()); @@ -531,60 +540,64 @@ void RenderingServerCanvas::canvas_item_add_line(RID p_item, const Point2 &p_fro } } -void RenderingServerCanvas::canvas_item_add_polyline(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, float p_width) { +void RenderingServerCanvas::canvas_item_add_polyline(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, float p_width, bool p_antialiased) { ERR_FAIL_COND(p_points.size() < 2); Item *canvas_item = canvas_item_owner.getornull(p_item); ERR_FAIL_COND(!canvas_item); + Color color = Color(1, 1, 1, 1); + + Vector<int> indices; + int pc = p_points.size(); + int pc2 = pc * 2; + + Vector2 prev_t; + int j2; + Item::CommandPolygon *pline = canvas_item->alloc_command<Item::CommandPolygon>(); ERR_FAIL_COND(!pline); - 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; - } - } + PackedColorArray colors; + PackedVector2Array points; - pline->primitive = RS::PRIMITIVE_LINES; - 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); - } + colors.resize(pc2); + points.resize(pc2); - 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()); - } - } + Vector2 *points_ptr = points.ptrw(); + Color *colors_ptr = colors.ptrw(); + + if (p_antialiased) { + Color color2 = Color(1, 1, 1, 0); + + PackedColorArray colors_top; + PackedVector2Array points_top; + + colors_top.resize(pc2); + points_top.resize(pc2); + + PackedColorArray colors_bottom; + PackedVector2Array points_bottom; + + colors_bottom.resize(pc2); + points_bottom.resize(pc2); + + Item::CommandPolygon *pline_top = canvas_item->alloc_command<Item::CommandPolygon>(); + ERR_FAIL_COND(!pline_top); + + Item::CommandPolygon *pline_bottom = canvas_item->alloc_command<Item::CommandPolygon>(); + ERR_FAIL_COND(!pline_bottom); + + //make three trianglestrip's for drawing the antialiased line... + + Vector2 *points_top_ptr = points_top.ptrw(); + Vector2 *points_bottom_ptr = points_bottom.ptrw(); - for (int i = 0; i < p_points.size(); i++) { + Color *colors_top_ptr = colors_top.ptrw(); + Color *colors_bottom_ptr = colors_bottom.ptrw(); + for (int i = 0, j = 0; i < pc; i++, j += 2) { Vector2 t; - if (i == p_points.size() - 1) { + if (i == pc - 1) { t = prev_t; } else { t = (p_points[i + 1] - p_points[i]).normalized().tangent(); @@ -593,30 +606,77 @@ void RenderingServerCanvas::canvas_item_add_polyline(RID p_item, const Vector<Po } } + j2 = j + 1; + Vector2 tangent = ((t + prev_t).normalized()) * p_width * 0.5; + Vector2 pos = p_points[i]; + + points_ptr[j] = pos + tangent; + points_ptr[j2] = pos - tangent; + + points_top_ptr[j] = pos + tangent + tangent; + points_top_ptr[j2] = pos + tangent; + + points_bottom_ptr[j] = pos - tangent; + points_bottom_ptr[j2] = pos - tangent - tangent; + + if (i < p_colors.size()) { + color = p_colors[i]; + color2 = Color(color.r, color.g, color.b, 0); + } + + colors_ptr[j] = color; + colors_ptr[j2] = color; + + colors_top_ptr[j] = color2; + colors_top_ptr[j2] = color; + + colors_bottom_ptr[j] = color; + colors_bottom_ptr[j2] = color2; - 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]; + prev_t = t; + } + + pline_top->primitive = RS::PRIMITIVE_TRIANGLE_STRIP; + pline_top->polygon.create(indices, points_top, colors_top); + + pline_bottom->primitive = RS::PRIMITIVE_TRIANGLE_STRIP; + pline_bottom->polygon.create(indices, points_bottom, colors_bottom); + } else { + //make a trianglestrip for drawing the line... + + for (int i = 0, j = 0; i < pc; i++, j += 2) { + Vector2 t; + if (i == pc - 1) { + t = prev_t; + } else { + t = (p_points[i + 1] - p_points[i]).normalized().tangent(); + if (i == 0) { + prev_t = t; } } - pline->triangles.write[i * 2 + 0] = p_points[i] + tangent; - pline->triangles.write[i * 2 + 1] = p_points[i] - tangent; + j2 = j + 1; + + Vector2 tangent = ((t + prev_t).normalized()) * p_width * 0.5; + Vector2 pos = p_points[i]; - if (pline->triangle_colors.size() > 1) { + points_ptr[j] = pos + tangent; + points_ptr[j2] = pos - tangent; - pline->triangle_colors.write[i * 2 + 0] = p_colors[i]; - pline->triangle_colors.write[i * 2 + 1] = p_colors[i]; + if (i < p_colors.size()) { + color = p_colors[i]; } + colors_ptr[j] = color; + colors_ptr[j2] = color; + prev_t = t; } -#endif } + + pline->primitive = RS::PRIMITIVE_TRIANGLE_STRIP; + pline->polygon.create(indices, points, colors); } void RenderingServerCanvas::canvas_item_add_multiline(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, float p_width) { @@ -1268,6 +1328,11 @@ void RenderingServerCanvas::canvas_light_occluder_set_polygon(RID p_occluder, RI } } +void RenderingServerCanvas::canvas_light_occluder_set_as_sdf_collision(RID p_occluder, bool p_enable) { + RasterizerCanvas::LightOccluderInstance *occluder = canvas_light_occluder_owner.getornull(p_occluder); + ERR_FAIL_COND(!occluder); +} + 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); @@ -1289,53 +1354,24 @@ RID RenderingServerCanvas::canvas_occluder_polygon_create() { } 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(); + uint32_t pc = p_shape.size(); + ERR_FAIL_COND(pc < 2); + 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]); - } + const Vector2 *r = p_shape.ptr(); + for (uint32_t i = 0; i < pc; 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); + RSG::canvas_render->occluder_polygon_set_shape(occluder_poly->occluder, p_shape, p_closed); + for (Set<RasterizerCanvas::LightOccluderInstance *>::Element *E = occluder_poly->owners.front(); E; E = E->next()) { E->get()->aabb_cache = occluder_poly->aabb; } diff --git a/servers/rendering/rendering_server_canvas.h b/servers/rendering/rendering_server_canvas.h index 36e2f77e95..89f511a8fb 100644 --- a/servers/rendering/rendering_server_canvas.h +++ b/servers/rendering/rendering_server_canvas.h @@ -153,6 +153,7 @@ public: RID_PtrOwner<RasterizerCanvas::Light> canvas_light_owner; bool disable_scale; + bool sdf_used = false; bool snapping_2d_transforms_to_pixel = false; private: @@ -165,6 +166,8 @@ private: public: void render_canvas(RID p_render_target, Canvas *p_canvas, const Transform2D &p_transform, RasterizerCanvas::Light *p_lights, RasterizerCanvas::Light *p_directional_lights, const Rect2 &p_clip_rect, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_transforms_to_pixel, bool p_snap_2d_vertices_to_pixel); + bool was_sdf_used(); + 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); @@ -189,7 +192,7 @@ public: void canvas_item_set_update_when_visible(RID p_item, bool p_update); 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_polyline(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, float p_width = 1.0, bool p_antialiased = false); 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); @@ -247,12 +250,12 @@ public: 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_as_sdf_collision(RID p_occluder, bool p_enable); 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); diff --git a/servers/rendering/rendering_server_raster.h b/servers/rendering/rendering_server_raster.h index daad706f8e..76ceccb3c5 100644 --- a/servers/rendering/rendering_server_raster.h +++ b/servers/rendering/rendering_server_raster.h @@ -41,7 +41,6 @@ class RenderingServerRaster : public RenderingServer { enum { - MAX_INSTANCE_CULL = 8192, MAX_INSTANCE_LIGHTS = 4, LIGHT_CACHE_DIRTY = -1, @@ -337,6 +336,7 @@ public: BIND2(light_directional_set_shadow_mode, RID, LightDirectionalShadowMode) BIND2(light_directional_set_blend_splits, RID, bool) + BIND2(light_directional_set_sky_only, RID, bool) BIND2(light_directional_set_shadow_depth_range_mode, RID, LightDirectionalShadowDepthRangeMode) /* PROBE API */ @@ -537,6 +537,7 @@ public: 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_sdf_oversize_and_scale, RID, ViewportSDFOversize, ViewportSDFScale) BIND3(viewport_set_shadow_atlas_quadrant_subdivision, RID, int, int) BIND2(viewport_set_msaa, RID, ViewportMSAA) BIND2(viewport_set_screen_space_aa, RID, ViewportScreenSpaceAA) @@ -593,7 +594,7 @@ public: BIND9(environment_set_tonemap, RID, EnvironmentToneMapper, float, float, bool, float, float, float, float) - BIND6(environment_set_adjustment, RID, bool, float, float, float, RID) + BIND7(environment_set_adjustment, RID, bool, float, float, float, bool, RID) BIND9(environment_set_fog, RID, bool, const Color &, float, float, float, float, float, float) BIND9(environment_set_volumetric_fog, RID, bool, float, const Color &, float, float, float, float, EnvVolumetricFogShadowFilter) @@ -717,7 +718,7 @@ public: BIND2(canvas_item_set_draw_behind_parent, RID, bool) 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) + BIND5(canvas_item_add_polyline, RID, const Vector<Point2> &, const Vector<Color> &, float, bool) 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 &) @@ -777,12 +778,12 @@ public: 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_as_sdf_collision, RID, bool) 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) diff --git a/servers/rendering/rendering_server_scene.cpp b/servers/rendering/rendering_server_scene.cpp index ae6786090a..b933a550e2 100644 --- a/servers/rendering/rendering_server_scene.cpp +++ b/servers/rendering/rendering_server_scene.cpp @@ -2169,7 +2169,7 @@ void RenderingServerScene::_prepare_scene(const Transform p_cam_transform, const //check shadow.. if (light) { - if (p_using_shadows && p_shadow_atlas.is_valid() && RSG::storage->light_has_shadow(E->get()->base)) { + if (p_using_shadows && p_shadow_atlas.is_valid() && RSG::storage->light_has_shadow(E->get()->base) && !(RSG::storage->light_get_type(E->get()->base) == RS::LIGHT_DIRECTIONAL && RSG::storage->light_directional_is_sky_only(E->get()->base))) { lights_with_shadow[directional_shadow_count++] = E->get(); } //add to list @@ -2595,7 +2595,8 @@ void RenderingServerScene::render_probes() { 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->spot_attenuation != RSG::storage->light_get_param(instance->base, RS::LIGHT_PARAM_SPOT_ATTENUATION) || + cache->sky_only != RSG::storage->light_directional_is_sky_only(instance->base)) { cache_dirty = true; } } @@ -2664,6 +2665,7 @@ void RenderingServerScene::render_probes() { 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->sky_only = RSG::storage->light_directional_is_sky_only(instance->base); idx++; } diff --git a/servers/rendering/rendering_server_scene.h b/servers/rendering/rendering_server_scene.h index a30aac3bb1..eb438be273 100644 --- a/servers/rendering/rendering_server_scene.h +++ b/servers/rendering/rendering_server_scene.h @@ -45,7 +45,6 @@ class RenderingServerScene { public: enum { - MAX_INSTANCE_CULL = 65536, MAX_LIGHTS_CULLED = 4096, MAX_REFLECTION_PROBES_CULLED = 4096, @@ -351,6 +350,7 @@ public: float spot_angle; float spot_attenuation; bool has_shadow; + bool sky_only; }; Vector<LightCache> light_cache; diff --git a/servers/rendering/rendering_server_viewport.cpp b/servers/rendering/rendering_server_viewport.cpp index c6682b1fcd..c048aa381f 100644 --- a/servers/rendering/rendering_server_viewport.cpp +++ b/servers/rendering/rendering_server_viewport.cpp @@ -146,6 +146,36 @@ void RenderingServerViewport::_draw_viewport(Viewport *p_viewport, XRInterface:: RasterizerCanvas::Light *directional_lights = nullptr; RasterizerCanvas::Light *directional_lights_with_shadow = nullptr; + if (p_viewport->sdf_active) { + //process SDF + + Rect2 sdf_rect = RSG::storage->render_target_get_sdf_rect(p_viewport->render_target); + + RasterizerCanvas::LightOccluderInstance *occluders = nullptr; + + //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 (sdf_rect.intersects_transformed(F->get()->xform_cache, F->get()->aabb_cache)) { + F->get()->next = occluders; + occluders = F->get(); + } + } + } + + RSG::canvas_render->render_sdf(p_viewport->render_target, occluders); + + p_viewport->sdf_active = false; // if used, gets set active again + } + Rect2 shadow_rect; int light_count = 0; @@ -195,7 +225,6 @@ void RenderingServerViewport::_draw_viewport(Viewport *p_viewport, XRInterface:: } //guess this is not needed, but keeping because it may be - //RSG::canvas_render->light_internal_update(cl->light_internal, cl); } } @@ -256,7 +285,6 @@ void RenderingServerViewport::_draw_viewport(Viewport *p_viewport, XRInterface:: light = light->shadows_next_ptr; } - //RSG::canvas_render->reset_canvas(); RENDER_TIMESTAMP("<End rendering 2D Shadows"); } @@ -340,7 +368,6 @@ void RenderingServerViewport::_draw_viewport(Viewport *p_viewport, XRInterface:: light = light->shadows_next_ptr; } - //RSG::canvas_render->reset_canvas(); RENDER_TIMESTAMP("<Render Directional 2D Shadows"); } @@ -380,6 +407,9 @@ void RenderingServerViewport::_draw_viewport(Viewport *p_viewport, XRInterface:: } RSG::canvas->render_canvas(p_viewport->render_target, canvas, xform, canvas_lights, canvas_directional_lights, clip_rect, p_viewport->texture_filter, p_viewport->texture_repeat, p_viewport->snap_2d_transforms_to_pixel, p_viewport->snap_2d_vertices_to_pixel); + if (RSG::canvas->was_sdf_used()) { + p_viewport->sdf_active = true; + } i++; if (scenario_draw_canvas_bg && E->key().get_layer() >= scenario_canvas_max_layer) { @@ -400,8 +430,6 @@ void RenderingServerViewport::_draw_viewport(Viewport *p_viewport, XRInterface:: _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)) { @@ -925,6 +953,13 @@ void RenderingServerViewport::viewport_set_default_canvas_item_texture_repeat(RI viewport->texture_repeat = p_repeat; } +void RenderingServerViewport::viewport_set_sdf_oversize_and_scale(RID p_viewport, RS::ViewportSDFOversize p_size, RS::ViewportSDFScale p_scale) { + Viewport *viewport = viewport_owner.getornull(p_viewport); + ERR_FAIL_COND(!viewport); + + RSG::storage->render_target_set_sdf_size_and_scale(viewport->render_target, p_size, p_scale); +} + bool RenderingServerViewport::free(RID p_rid) { if (viewport_owner.owns(p_rid)) { Viewport *viewport = viewport_owner.getornull(p_rid); diff --git a/servers/rendering/rendering_server_viewport.h b/servers/rendering/rendering_server_viewport.h index 081f63690b..ba55b2e66e 100644 --- a/servers/rendering/rendering_server_viewport.h +++ b/servers/rendering/rendering_server_viewport.h @@ -82,6 +82,8 @@ public: RID shadow_atlas; int shadow_atlas_size; + bool sdf_active; + uint64_t last_pass = 0; int render_info[RS::VIEWPORT_RENDER_INFO_MAX]; @@ -146,6 +148,7 @@ public: render_info[i] = 0; } use_xr = false; + sdf_active = false; time_cpu_begin = 0; time_cpu_end = 0; @@ -232,6 +235,8 @@ public: void viewport_set_default_canvas_item_texture_filter(RID p_viewport, RS::CanvasItemTextureFilter p_filter); void viewport_set_default_canvas_item_texture_repeat(RID p_viewport, RS::CanvasItemTextureRepeat p_repeat); + void viewport_set_sdf_oversize_and_scale(RID p_viewport, RS::ViewportSDFOversize p_over_size, RS::ViewportSDFScale p_scale); + void handle_timestamp(String p_timestamp, uint64_t p_cpu_time, uint64_t p_gpu_time); void set_default_clear_color(const Color &p_color); diff --git a/servers/rendering/rendering_server_wrap_mt.h b/servers/rendering/rendering_server_wrap_mt.h index d27b851d1d..2c230aaee4 100644 --- a/servers/rendering/rendering_server_wrap_mt.h +++ b/servers/rendering/rendering_server_wrap_mt.h @@ -244,6 +244,7 @@ public: FUNC2(light_directional_set_shadow_mode, RID, LightDirectionalShadowMode) FUNC2(light_directional_set_blend_splits, RID, bool) + FUNC2(light_directional_set_sky_only, RID, bool) FUNC2(light_directional_set_shadow_depth_range_mode, RID, LightDirectionalShadowDepthRangeMode) /* PROBE API */ @@ -440,6 +441,8 @@ public: 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_sdf_oversize_and_scale, RID, ViewportSDFOversize, ViewportSDFScale) + FUNC3(viewport_set_shadow_atlas_quadrant_subdivision, RID, int, int) FUNC2(viewport_set_msaa, RID, ViewportMSAA) FUNC2(viewport_set_screen_space_aa, RID, ViewportScreenSpaceAA) @@ -504,7 +507,7 @@ public: FUNC9(environment_set_tonemap, RID, EnvironmentToneMapper, float, float, bool, float, float, float, float) - FUNC6(environment_set_adjustment, RID, bool, float, float, float, RID) + FUNC7(environment_set_adjustment, RID, bool, float, float, float, bool, RID) FUNC9(environment_set_fog, RID, bool, const Color &, float, float, float, float, float, float) @@ -616,7 +619,7 @@ public: FUNC2(canvas_item_set_draw_behind_parent, RID, bool) 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) + FUNC5(canvas_item_add_polyline, RID, const Vector<Point2> &, const Vector<Color> &, float, bool) 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 &) @@ -676,12 +679,12 @@ public: 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_as_sdf_collision, RID, bool) 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) diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp index 046c6ba580..1ab353c9d0 100644 --- a/servers/rendering/shader_language.cpp +++ b/servers/rendering/shader_language.cpp @@ -2157,7 +2157,6 @@ const ShaderLanguage::BuiltinFuncDef ShaderLanguage::builtin_func_defs[] = { { "fma", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, { nullptr, TYPE_VOID, { TYPE_VOID }, TAG_GLOBAL, false } - }; const ShaderLanguage::BuiltinFuncOutArgs ShaderLanguage::builtin_func_out_args[] = { diff --git a/servers/rendering/shader_types.cpp b/servers/rendering/shader_types.cpp index 4d21807735..bd61f2a549 100644 --- a/servers/rendering/shader_types.cpp +++ b/servers/rendering/shader_types.cpp @@ -252,6 +252,27 @@ ShaderTypes::ShaderTypes() { 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; + { + ShaderLanguage::StageFunctionInfo func; + func.arguments.push_back(ShaderLanguage::StageFunctionInfo::Argument("sdf_pos", ShaderLanguage::TYPE_VEC2)); + func.return_type = ShaderLanguage::TYPE_FLOAT; //whether it could emit + shader_modes[RS::SHADER_CANVAS_ITEM].functions["fragment"].stage_functions["texture_sdf"] = func; + shader_modes[RS::SHADER_CANVAS_ITEM].functions["light"].stage_functions["texture_sdf"] = func; + func.return_type = ShaderLanguage::TYPE_VEC2; //whether it could emit + shader_modes[RS::SHADER_CANVAS_ITEM].functions["fragment"].stage_functions["sdf_to_screen_uv"] = func; + shader_modes[RS::SHADER_CANVAS_ITEM].functions["light"].stage_functions["sdf_to_screen_uv"] = func; + shader_modes[RS::SHADER_CANVAS_ITEM].functions["fragment"].stage_functions["texture_sdf_normal"] = func; + shader_modes[RS::SHADER_CANVAS_ITEM].functions["light"].stage_functions["texture_sdf_normal"] = func; + } + + { + ShaderLanguage::StageFunctionInfo func; + func.arguments.push_back(ShaderLanguage::StageFunctionInfo::Argument("uv", ShaderLanguage::TYPE_VEC2)); + func.return_type = ShaderLanguage::TYPE_VEC2; //whether it could emit + shader_modes[RS::SHADER_CANVAS_ITEM].functions["fragment"].stage_functions["screen_uv_to_sdf"] = func; + shader_modes[RS::SHADER_CANVAS_ITEM].functions["light"].stage_functions["screen_uv_to_sdf"] = func; + } + 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); diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index cc959afdaa..9c8342b34d 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -1431,7 +1431,6 @@ Array RenderingServer::mesh_create_arrays_from_surface_data(const SurfaceData &p } #if 0 Array RenderingServer::_mesh_surface_get_skeleton_aabb_bind(RID p_mesh, int p_surface) const { - Vector<AABB> vec = RS::get_singleton()->mesh_surface_get_skeleton_aabb(p_mesh, p_surface); Array arr; for (int i = 0; i < vec.size(); i++) { @@ -1618,6 +1617,7 @@ void RenderingServer::_bind_methods() { ClassDB::bind_method(D_METHOD("light_directional_set_shadow_mode", "light", "mode"), &RenderingServer::light_directional_set_shadow_mode); ClassDB::bind_method(D_METHOD("light_directional_set_blend_splits", "light", "enable"), &RenderingServer::light_directional_set_blend_splits); + ClassDB::bind_method(D_METHOD("light_directional_set_sky_only", "light", "enable"), &RenderingServer::light_directional_set_sky_only); ClassDB::bind_method(D_METHOD("light_directional_set_shadow_depth_range_mode", "light", "range_mode"), &RenderingServer::light_directional_set_shadow_depth_range_mode); ClassDB::bind_method(D_METHOD("reflection_probe_create"), &RenderingServer::reflection_probe_create); @@ -1750,7 +1750,7 @@ void RenderingServer::_bind_methods() { ClassDB::bind_method(D_METHOD("environment_set_ambient_light", "env", "color", "ambient", "energy", "sky_contibution", "reflection_source", "ao_color"), &RenderingServer::environment_set_ambient_light, DEFVAL(RS::ENV_AMBIENT_SOURCE_BG), DEFVAL(1.0), DEFVAL(0.0), DEFVAL(RS::ENV_REFLECTION_SOURCE_BG), DEFVAL(Color())); ClassDB::bind_method(D_METHOD("environment_set_glow", "env", "enable", "levels", "intensity", "strength", "mix", "bloom_threshold", "blend_mode", "hdr_bleed_threshold", "hdr_bleed_scale", "hdr_luminance_cap"), &RenderingServer::environment_set_glow); ClassDB::bind_method(D_METHOD("environment_set_tonemap", "env", "tone_mapper", "exposure", "white", "auto_exposure", "min_luminance", "max_luminance", "auto_exp_speed", "auto_exp_grey"), &RenderingServer::environment_set_tonemap); - ClassDB::bind_method(D_METHOD("environment_set_adjustment", "env", "enable", "brightness", "contrast", "saturation", "ramp"), &RenderingServer::environment_set_adjustment); + ClassDB::bind_method(D_METHOD("environment_set_adjustment", "env", "enable", "brightness", "contrast", "saturation", "use_1d_color_correction", "color_correction"), &RenderingServer::environment_set_adjustment); ClassDB::bind_method(D_METHOD("environment_set_ssr", "env", "enable", "max_steps", "fade_in", "fade_out", "depth_tolerance"), &RenderingServer::environment_set_ssr); ClassDB::bind_method(D_METHOD("environment_set_ssao", "env", "enable", "radius", "intensity", "bias", "light_affect", "ao_channel_affect", "blur", "bilateral_sharpness"), &RenderingServer::environment_set_ssao); ClassDB::bind_method(D_METHOD("environment_set_fog", "env", "enable", "light_color", "light_energy", "sun_scatter", "density", "height", "height_density", "aerial_perspective"), &RenderingServer::environment_set_fog); @@ -1859,7 +1859,6 @@ void RenderingServer::_bind_methods() { ClassDB::bind_method(D_METHOD("canvas_occluder_polygon_create"), &RenderingServer::canvas_occluder_polygon_create); ClassDB::bind_method(D_METHOD("canvas_occluder_polygon_set_shape", "occluder_polygon", "shape", "closed"), &RenderingServer::canvas_occluder_polygon_set_shape); - ClassDB::bind_method(D_METHOD("canvas_occluder_polygon_set_shape_as_lines", "occluder_polygon", "shape"), &RenderingServer::canvas_occluder_polygon_set_shape_as_lines); ClassDB::bind_method(D_METHOD("canvas_occluder_polygon_set_cull_mode", "occluder_polygon", "mode"), &RenderingServer::canvas_occluder_polygon_set_cull_mode); ClassDB::bind_method(D_METHOD("global_variable_add", "name", "type", "default_value"), &RenderingServer::global_variable_add); diff --git a/servers/rendering_server.h b/servers/rendering_server.h index d676a1ba01..6102958aaa 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -436,6 +436,7 @@ public: virtual void light_directional_set_shadow_mode(RID p_light, LightDirectionalShadowMode p_mode) = 0; virtual void light_directional_set_blend_splits(RID p_light, bool p_enable) = 0; + virtual void light_directional_set_sky_only(RID p_light, bool p_sky_only) = 0; enum LightDirectionalShadowDepthRangeMode { LIGHT_DIRECTIONAL_SHADOW_DEPTH_RANGE_STABLE, @@ -730,6 +731,23 @@ public: virtual void viewport_set_global_canvas_transform(RID p_viewport, const Transform2D &p_transform) = 0; virtual void viewport_set_canvas_stacking(RID p_viewport, RID p_canvas, int p_layer, int p_sublayer) = 0; + enum ViewportSDFOversize { + VIEWPORT_SDF_OVERSIZE_100_PERCENT, + VIEWPORT_SDF_OVERSIZE_120_PERCENT, + VIEWPORT_SDF_OVERSIZE_150_PERCENT, + VIEWPORT_SDF_OVERSIZE_200_PERCENT, + VIEWPORT_SDF_OVERSIZE_MAX + }; + + enum ViewportSDFScale { + VIEWPORT_SDF_SCALE_100_PERCENT, + VIEWPORT_SDF_SCALE_50_PERCENT, + VIEWPORT_SDF_SCALE_25_PERCENT, + VIEWPORT_SDF_SCALE_MAX + }; + + virtual void viewport_set_sdf_oversize_and_scale(RID p_viewport, ViewportSDFOversize p_oversize, ViewportSDFScale p_scale) = 0; + virtual void viewport_set_shadow_atlas_size(RID p_viewport, int p_size) = 0; virtual void viewport_set_shadow_atlas_quadrant_subdivision(RID p_viewport, int p_quadrant, int p_subdiv) = 0; @@ -872,7 +890,7 @@ public: }; virtual void environment_set_tonemap(RID p_env, 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_grey) = 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_adjustment(RID p_env, bool p_enable, float p_brightness, float p_contrast, float p_saturation, bool p_use_1d_color_correction, RID p_color_correction) = 0; virtual void environment_set_ssr(RID p_env, bool p_enable, int p_max_steps, float p_fade_in, float p_fade_out, float p_depth_tolerance) = 0; @@ -1158,7 +1176,7 @@ public: }; virtual 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) = 0; - virtual void canvas_item_add_polyline(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, float p_width = 1.0) = 0; + virtual void canvas_item_add_polyline(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, float p_width = 1.0, bool p_antialiased = false) = 0; virtual void canvas_item_add_multiline(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, float p_width = 1.0) = 0; virtual void canvas_item_add_rect(RID p_item, const Rect2 &p_rect, const Color &p_color) = 0; virtual void canvas_item_add_circle(RID p_item, const Point2 &p_pos, float p_radius, const Color &p_color) = 0; @@ -1245,12 +1263,12 @@ public: virtual void canvas_light_occluder_attach_to_canvas(RID p_occluder, RID p_canvas) = 0; virtual void canvas_light_occluder_set_enabled(RID p_occluder, bool p_enabled) = 0; virtual void canvas_light_occluder_set_polygon(RID p_occluder, RID p_polygon) = 0; + virtual void canvas_light_occluder_set_as_sdf_collision(RID p_occluder, bool p_enable) = 0; virtual void canvas_light_occluder_set_transform(RID p_occluder, const Transform2D &p_xform) = 0; virtual void canvas_light_occluder_set_light_mask(RID p_occluder, int p_mask) = 0; virtual RID canvas_occluder_polygon_create() = 0; virtual void canvas_occluder_polygon_set_shape(RID p_occluder_polygon, const Vector<Vector2> &p_shape, bool p_closed) = 0; - virtual void canvas_occluder_polygon_set_shape_as_lines(RID p_occluder_polygon, const Vector<Vector2> &p_shape) = 0; enum CanvasOccluderPolygonCullMode { CANVAS_OCCLUDER_POLYGON_CULL_DISABLED, diff --git a/servers/text_server.cpp b/servers/text_server.cpp new file mode 100644 index 0000000000..74bf437a25 --- /dev/null +++ b/servers/text_server.cpp @@ -0,0 +1,1246 @@ +/*************************************************************************/ +/* text_server.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 "servers/text_server.h" +#include "scene/main/canvas_item.h" + +TextServerManager *TextServerManager::singleton = nullptr; +TextServer *TextServerManager::server = nullptr; +TextServerManager::TextServerCreate TextServerManager::server_create_functions[TextServerManager::MAX_SERVERS]; +int TextServerManager::server_create_count = 0; + +void TextServerManager::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_interface_count"), &TextServerManager::_get_interface_count); + ClassDB::bind_method(D_METHOD("get_interface_name", "index"), &TextServerManager::_get_interface_name); + ClassDB::bind_method(D_METHOD("get_interface_features", "index"), &TextServerManager::_get_interface_features); + ClassDB::bind_method(D_METHOD("get_interface", "index"), &TextServerManager::_get_interface); + ClassDB::bind_method(D_METHOD("get_interfaces"), &TextServerManager::_get_interfaces); + ClassDB::bind_method(D_METHOD("find_interface", "name"), &TextServerManager::_find_interface); + + ClassDB::bind_method(D_METHOD("set_primary_interface", "index"), &TextServerManager::_set_primary_interface); + ClassDB::bind_method(D_METHOD("get_primary_interface"), &TextServerManager::_get_primary_interface); +} + +void TextServerManager::register_create_function(const String &p_name, uint32_t p_features, TextServerManager::CreateFunction p_function, void *p_user_data) { + ERR_FAIL_COND(server_create_count == MAX_SERVERS); + server_create_functions[server_create_count].name = p_name; + server_create_functions[server_create_count].create_function = p_function; + server_create_functions[server_create_count].user_data = p_user_data; + server_create_functions[server_create_count].features = p_features; + server_create_count++; +} + +int TextServerManager::get_interface_count() { + return server_create_count; +} + +String TextServerManager::get_interface_name(int p_index) { + ERR_FAIL_INDEX_V(p_index, server_create_count, String()); + return server_create_functions[p_index].name; +} + +uint32_t TextServerManager::get_interface_features(int p_index) { + ERR_FAIL_INDEX_V(p_index, server_create_count, 0); + return server_create_functions[p_index].features; +} + +TextServer *TextServerManager::initialize(int p_index, Error &r_error) { + ERR_FAIL_INDEX_V(p_index, server_create_count, nullptr); + if (server_create_functions[p_index].instance == nullptr) { + server_create_functions[p_index].instance = server_create_functions[p_index].create_function(r_error, server_create_functions[p_index].user_data); + if (server_create_functions[p_index].instance != nullptr) { + server_create_functions[p_index].instance->load_support_data(""); // Try loading default data. + } + } + if (server_create_functions[p_index].instance != nullptr) { + server = server_create_functions[p_index].instance; + if (OS::get_singleton()->get_main_loop()) { + OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_TEXT_SERVER_CHANGED); + } + } + return server_create_functions[p_index].instance; +} + +TextServer *TextServerManager::get_primary_interface() { + return server; +} + +int TextServerManager::_get_interface_count() const { + return server_create_count; +} + +String TextServerManager::_get_interface_name(int p_index) const { + return get_interface_name(p_index); +} + +uint32_t TextServerManager::_get_interface_features(int p_index) const { + return get_interface_features(p_index); +} + +TextServer *TextServerManager::_get_interface(int p_index) const { + ERR_FAIL_INDEX_V(p_index, server_create_count, nullptr); + if (server_create_functions[p_index].instance == nullptr) { + Error error; + server_create_functions[p_index].instance = server_create_functions[p_index].create_function(error, server_create_functions[p_index].user_data); + if (server_create_functions[p_index].instance != nullptr) { + server_create_functions[p_index].instance->load_support_data(""); // Try loading default data. + } + } + return server_create_functions[p_index].instance; +} + +TextServer *TextServerManager::_find_interface(const String &p_name) const { + for (int i = 0; i < server_create_count; i++) { + if (server_create_functions[i].name == p_name) { + return _get_interface(i); + } + } + return nullptr; +} + +Array TextServerManager::_get_interfaces() const { + Array ret; + + for (int i = 0; i < server_create_count; i++) { + Dictionary iface_info; + + iface_info["id"] = i; + iface_info["name"] = server_create_functions[i].name; + + ret.push_back(iface_info); + }; + + return ret; +}; + +bool TextServerManager::_set_primary_interface(int p_index) { + Error error; + TextServerManager::initialize(p_index, error); + return (error == OK); +} + +TextServer *TextServerManager::_get_primary_interface() const { + return server; +} + +TextServerManager::TextServerManager() { + singleton = this; +} + +TextServerManager::~TextServerManager() { + singleton = nullptr; + for (int i = 0; i < server_create_count; i++) { + if (server_create_functions[i].instance != nullptr) { + memdelete(server_create_functions[i].instance); + server_create_functions[i].instance = nullptr; + } + } +} + +/*************************************************************************/ + +bool TextServer::Glyph::operator==(const Glyph &p_a) const { + return (p_a.index == index) && (p_a.font_rid == font_rid) && (p_a.font_size == font_size) && (p_a.start == start); +} + +bool TextServer::Glyph::operator!=(const Glyph &p_a) const { + return (p_a.index != index) || (p_a.font_rid != font_rid) || (p_a.font_size != font_size) || (p_a.start != start); +} + +bool TextServer::Glyph::operator<(const Glyph &p_a) const { + if (p_a.start == start) { + if (p_a.count == count) { + if ((p_a.flags & GRAPHEME_IS_VIRTUAL) == GRAPHEME_IS_VIRTUAL) { + return true; + } else { + return false; + } + } + return p_a.count > count; + } + return p_a.start < start; +} + +bool TextServer::Glyph::operator>(const Glyph &p_a) const { + if (p_a.start == start) { + if (p_a.count == count) { + if ((p_a.flags & GRAPHEME_IS_VIRTUAL) == GRAPHEME_IS_VIRTUAL) { + return false; + } else { + return true; + } + } + return p_a.count < count; + } + return p_a.start > start; +} + +void TextServer::_bind_methods() { + ClassDB::bind_method(D_METHOD("has_feature", "feature"), &TextServer::has_feature); + ClassDB::bind_method(D_METHOD("get_name"), &TextServer::get_name); + ClassDB::bind_method(D_METHOD("load_support_data", "filename"), &TextServer::load_support_data); + + ClassDB::bind_method(D_METHOD("is_locale_right_to_left", "locale"), &TextServer::is_locale_right_to_left); + + ClassDB::bind_method(D_METHOD("name_to_tag", "name"), &TextServer::name_to_tag); + ClassDB::bind_method(D_METHOD("tag_to_name", "tag"), &TextServer::tag_to_name); + + ClassDB::bind_method(D_METHOD("has", "rid"), &TextServer::has); + ClassDB::bind_method(D_METHOD("free_rid", "rid"), &TextServer::free); // shouldn't conflict with Object::free() + + /* Font Interface */ + ClassDB::bind_method(D_METHOD("create_font_system", "name", "base_size"), &TextServer::create_font_system, DEFVAL(16)); + ClassDB::bind_method(D_METHOD("create_font_resource", "filename", "base_size"), &TextServer::create_font_resource, DEFVAL(16)); + ClassDB::bind_method(D_METHOD("create_font_memory", "data", "type", "base_size"), &TextServer::_create_font_memory, DEFVAL(16)); + + ClassDB::bind_method(D_METHOD("font_get_height", "font", "size"), &TextServer::font_get_height); + ClassDB::bind_method(D_METHOD("font_get_ascent", "font", "size"), &TextServer::font_get_ascent); + ClassDB::bind_method(D_METHOD("font_get_descent", "font", "size"), &TextServer::font_get_descent); + + ClassDB::bind_method(D_METHOD("font_get_underline_position", "font", "size"), &TextServer::font_get_underline_position); + ClassDB::bind_method(D_METHOD("font_get_underline_thickness", "font", "size"), &TextServer::font_get_underline_thickness); + + ClassDB::bind_method(D_METHOD("font_set_antialiased", "font", "antialiased"), &TextServer::font_set_antialiased); + ClassDB::bind_method(D_METHOD("font_get_antialiased", "font"), &TextServer::font_get_antialiased); + + ClassDB::bind_method(D_METHOD("font_get_feature_list", "font"), &TextServer::font_get_feature_list); + + ClassDB::bind_method(D_METHOD("font_set_hinting", "font", "hinting"), &TextServer::font_set_hinting); + ClassDB::bind_method(D_METHOD("font_get_hinting", "font"), &TextServer::font_get_hinting); + + ClassDB::bind_method(D_METHOD("font_set_distance_field_hint", "font", "distance_field"), &TextServer::font_set_distance_field_hint); + ClassDB::bind_method(D_METHOD("font_get_distance_field_hint", "font"), &TextServer::font_get_distance_field_hint); + + ClassDB::bind_method(D_METHOD("font_set_force_autohinter", "font", "enabeld"), &TextServer::font_set_force_autohinter); + ClassDB::bind_method(D_METHOD("font_get_force_autohinter", "font"), &TextServer::font_get_force_autohinter); + + ClassDB::bind_method(D_METHOD("font_has_char", "font", "char"), &TextServer::font_has_char); + ClassDB::bind_method(D_METHOD("font_get_supported_chars", "font"), &TextServer::font_get_supported_chars); + + ClassDB::bind_method(D_METHOD("font_has_outline", "font"), &TextServer::font_has_outline); + ClassDB::bind_method(D_METHOD("font_get_base_size", "font"), &TextServer::font_get_base_size); + + ClassDB::bind_method(D_METHOD("font_is_language_supported", "font", "language"), &TextServer::font_is_language_supported); + ClassDB::bind_method(D_METHOD("font_set_language_support_override", "font", "language", "supported"), &TextServer::font_set_language_support_override); + + ClassDB::bind_method(D_METHOD("font_get_language_support_override", "font", "language"), &TextServer::font_get_language_support_override); + ClassDB::bind_method(D_METHOD("font_remove_language_support_override", "font", "language"), &TextServer::font_remove_language_support_override); + ClassDB::bind_method(D_METHOD("font_get_language_support_overrides", "font"), &TextServer::font_get_language_support_overrides); + + ClassDB::bind_method(D_METHOD("font_is_script_supported", "font", "script"), &TextServer::font_is_script_supported); + ClassDB::bind_method(D_METHOD("font_set_script_support_override", "font", "script", "supported"), &TextServer::font_set_script_support_override); + + ClassDB::bind_method(D_METHOD("font_get_script_support_override", "font", "script"), &TextServer::font_get_script_support_override); + ClassDB::bind_method(D_METHOD("font_remove_script_support_override", "font", "script"), &TextServer::font_remove_script_support_override); + ClassDB::bind_method(D_METHOD("font_get_script_support_overrides", "font"), &TextServer::font_get_script_support_overrides); + + ClassDB::bind_method(D_METHOD("font_get_glyph_index", "font", "char", "variation_selector"), &TextServer::font_get_glyph_index, DEFVAL(0x0000)); + ClassDB::bind_method(D_METHOD("font_get_glyph_advance", "font", "index", "size"), &TextServer::font_get_glyph_advance); + ClassDB::bind_method(D_METHOD("font_get_glyph_kerning", "font", "index_a", "index_b", "size"), &TextServer::font_get_glyph_kerning); + + ClassDB::bind_method(D_METHOD("font_draw_glyph", "font", "canvas", "size", "pos", "index", "color"), &TextServer::font_draw_glyph, DEFVAL(Color(1, 1, 1))); + ClassDB::bind_method(D_METHOD("font_draw_glyph_outline", "font", "canvas", "size", "outline_size", "pos", "index", "color"), &TextServer::font_draw_glyph_outline, DEFVAL(Color(1, 1, 1))); + + ClassDB::bind_method(D_METHOD("font_get_oversampling"), &TextServer::font_get_oversampling); + ClassDB::bind_method(D_METHOD("font_set_oversampling", "oversampling"), &TextServer::font_set_oversampling); + + ClassDB::bind_method(D_METHOD("get_system_fonts"), &TextServer::get_system_fonts); + + ClassDB::bind_method(D_METHOD("get_hex_code_box_size", "size", "index"), &TextServer::get_hex_code_box_size); + ClassDB::bind_method(D_METHOD("draw_hex_code_box", "canvas", "size", "pos", "index", "color"), &TextServer::draw_hex_code_box); + + /* Shaped text buffer interface */ + + ClassDB::bind_method(D_METHOD("create_shaped_text", "direction", "orientation"), &TextServer::create_shaped_text, DEFVAL(DIRECTION_AUTO), DEFVAL(ORIENTATION_HORIZONTAL)); + + ClassDB::bind_method(D_METHOD("shaped_text_clear"), &TextServer::shaped_text_clear); + + ClassDB::bind_method(D_METHOD("shaped_text_set_direction", "shaped", "direction"), &TextServer::shaped_text_set_direction, DEFVAL(DIRECTION_AUTO)); + ClassDB::bind_method(D_METHOD("shaped_text_get_direction", "shaped"), &TextServer::shaped_text_get_direction); + + ClassDB::bind_method(D_METHOD("shaped_text_set_bidi_override", "shaped", "override"), &TextServer::_shaped_text_set_bidi_override); + + ClassDB::bind_method(D_METHOD("shaped_text_set_orientation", "shaped", "orientation"), &TextServer::shaped_text_set_orientation, DEFVAL(ORIENTATION_HORIZONTAL)); + ClassDB::bind_method(D_METHOD("shaped_text_get_orientation", "shaped"), &TextServer::shaped_text_get_orientation); + + ClassDB::bind_method(D_METHOD("shaped_text_set_preserve_invalid", "shaped", "enabled"), &TextServer::shaped_text_set_preserve_invalid); + ClassDB::bind_method(D_METHOD("shaped_text_get_preserve_invalid", "shaped"), &TextServer::shaped_text_get_preserve_invalid); + + ClassDB::bind_method(D_METHOD("shaped_text_set_preserve_control", "shaped", "enabled"), &TextServer::shaped_text_set_preserve_control); + ClassDB::bind_method(D_METHOD("shaped_text_get_preserve_control", "shaped"), &TextServer::shaped_text_get_preserve_control); + + ClassDB::bind_method(D_METHOD("shaped_text_add_string", "shaped", "text", "fonts", "size", "opentype_features", "language"), &TextServer::shaped_text_add_string, DEFVAL(Dictionary()), DEFVAL("")); + ClassDB::bind_method(D_METHOD("shaped_text_add_object", "shaped", "key", "size", "inline_align", "length"), &TextServer::shaped_text_add_object, DEFVAL(VALIGN_CENTER), DEFVAL(1)); + ClassDB::bind_method(D_METHOD("shaped_text_resize_object", "shaped", "key", "size", "inline_align"), &TextServer::shaped_text_resize_object, DEFVAL(VALIGN_CENTER)); + + ClassDB::bind_method(D_METHOD("shaped_text_substr", "shaped", "start", "length"), &TextServer::shaped_text_substr); + ClassDB::bind_method(D_METHOD("shaped_text_get_parent", "shaped"), &TextServer::shaped_text_get_parent); + ClassDB::bind_method(D_METHOD("shaped_text_fit_to_width", "shaped", "width", "jst_flags"), &TextServer::shaped_text_fit_to_width, DEFVAL(JUSTIFICATION_WORD_BOUND | JUSTIFICATION_KASHIDA)); + ClassDB::bind_method(D_METHOD("shaped_text_tab_align", "shaped", "tab_stops"), &TextServer::shaped_text_tab_align); + + ClassDB::bind_method(D_METHOD("shaped_text_shape", "shaped"), &TextServer::shaped_text_shape); + ClassDB::bind_method(D_METHOD("shaped_text_is_ready", "shaped"), &TextServer::shaped_text_is_ready); + + ClassDB::bind_method(D_METHOD("shaped_text_get_glyphs", "shaped"), &TextServer::_shaped_text_get_glyphs); + + ClassDB::bind_method(D_METHOD("shaped_text_get_range", "shaped"), &TextServer::shaped_text_get_range); + ClassDB::bind_method(D_METHOD("shaped_text_get_line_breaks_adv", "shaped", "width", "start", "once", "break_flags"), &TextServer::_shaped_text_get_line_breaks_adv, DEFVAL(0), DEFVAL(true), DEFVAL(BREAK_MANDATORY | BREAK_WORD_BOUND)); + ClassDB::bind_method(D_METHOD("shaped_text_get_line_breaks", "shaped", "width", "start", "break_flags"), &TextServer::_shaped_text_get_line_breaks, DEFVAL(0), DEFVAL(BREAK_MANDATORY | BREAK_WORD_BOUND)); + ClassDB::bind_method(D_METHOD("shaped_text_get_word_breaks", "shaped"), &TextServer::_shaped_text_get_word_breaks); + ClassDB::bind_method(D_METHOD("shaped_text_get_objects", "shaped"), &TextServer::shaped_text_get_objects); + ClassDB::bind_method(D_METHOD("shaped_text_get_object_rect", "shaped", "key"), &TextServer::shaped_text_get_object_rect); + + ClassDB::bind_method(D_METHOD("shaped_text_get_size", "shaped"), &TextServer::shaped_text_get_size); + ClassDB::bind_method(D_METHOD("shaped_text_get_ascent", "shaped"), &TextServer::shaped_text_get_ascent); + ClassDB::bind_method(D_METHOD("shaped_text_get_descent", "shaped"), &TextServer::shaped_text_get_descent); + ClassDB::bind_method(D_METHOD("shaped_text_get_width", "shaped"), &TextServer::shaped_text_get_width); + ClassDB::bind_method(D_METHOD("shaped_text_get_underline_position", "shaped"), &TextServer::shaped_text_get_underline_position); + ClassDB::bind_method(D_METHOD("shaped_text_get_underline_thickness", "shaped"), &TextServer::shaped_text_get_underline_thickness); + + ClassDB::bind_method(D_METHOD("shaped_text_get_carets", "shaped", "position"), &TextServer::_shaped_text_get_carets); + ClassDB::bind_method(D_METHOD("shaped_text_get_selection", "shaped", "start", "end"), &TextServer::_shaped_text_get_selection); + + ClassDB::bind_method(D_METHOD("shaped_text_hit_test_grapheme", "shaped", "coords"), &TextServer::shaped_text_hit_test_grapheme); + ClassDB::bind_method(D_METHOD("shaped_text_hit_test_position", "shaped", "coords"), &TextServer::shaped_text_hit_test_position); + + ClassDB::bind_method(D_METHOD("shaped_text_next_grapheme_pos", "shaped", "pos"), &TextServer::shaped_text_next_grapheme_pos); + ClassDB::bind_method(D_METHOD("shaped_text_prev_grapheme_pos", "shaped", "pos"), &TextServer::shaped_text_prev_grapheme_pos); + + ClassDB::bind_method(D_METHOD("shaped_text_draw", "shaped", "canvas", "pos", "clip_l", "clip_r", "color"), &TextServer::shaped_text_draw, DEFVAL(-1), DEFVAL(-1), DEFVAL(Color(1, 1, 1))); + ClassDB::bind_method(D_METHOD("shaped_text_draw_outline", "shaped", "canvas", "pos", "clip_l", "clip_r", "outline_size", "color"), &TextServer::shaped_text_draw_outline, DEFVAL(-1), DEFVAL(-1), DEFVAL(1), DEFVAL(Color(1, 1, 1))); + + ClassDB::bind_method(D_METHOD("shaped_text_get_dominant_direciton_in_range", "shaped", "start", "end"), &TextServer::shaped_text_get_dominant_direciton_in_range); + + ClassDB::bind_method(D_METHOD("format_number", "number", "language"), &TextServer::format_number, DEFVAL("")); + ClassDB::bind_method(D_METHOD("parse_number", "number", "language"), &TextServer::parse_number, DEFVAL("")); + ClassDB::bind_method(D_METHOD("percent_sign", "language"), &TextServer::percent_sign, DEFVAL("")); + + /* Direction */ + BIND_ENUM_CONSTANT(DIRECTION_AUTO); + BIND_ENUM_CONSTANT(DIRECTION_LTR); + BIND_ENUM_CONSTANT(DIRECTION_RTL); + + /* Orientation */ + BIND_ENUM_CONSTANT(ORIENTATION_HORIZONTAL); + BIND_ENUM_CONSTANT(ORIENTATION_VERTICAL); + + /* JustificationFlag */ + BIND_ENUM_CONSTANT(JUSTIFICATION_NONE); + BIND_ENUM_CONSTANT(JUSTIFICATION_KASHIDA); + BIND_ENUM_CONSTANT(JUSTIFICATION_WORD_BOUND); + BIND_ENUM_CONSTANT(JUSTIFICATION_TRIM_EDGE_SPACES); + BIND_ENUM_CONSTANT(JUSTIFICATION_AFTER_LAST_TAB); + + /* LineBreakFlag */ + BIND_ENUM_CONSTANT(BREAK_NONE); + BIND_ENUM_CONSTANT(BREAK_MANDATORY); + BIND_ENUM_CONSTANT(BREAK_WORD_BOUND); + BIND_ENUM_CONSTANT(BREAK_GRAPHEME_BOUND); + + /* GraphemeFlag */ + BIND_ENUM_CONSTANT(GRAPHEME_IS_RTL); + BIND_ENUM_CONSTANT(GRAPHEME_IS_VIRTUAL); + BIND_ENUM_CONSTANT(GRAPHEME_IS_SPACE); + BIND_ENUM_CONSTANT(GRAPHEME_IS_BREAK_HARD); + BIND_ENUM_CONSTANT(GRAPHEME_IS_BREAK_SOFT); + BIND_ENUM_CONSTANT(GRAPHEME_IS_TAB); + BIND_ENUM_CONSTANT(GRAPHEME_IS_ELONGATION); + + /* Hinting */ + BIND_ENUM_CONSTANT(HINTING_NONE); + BIND_ENUM_CONSTANT(HINTING_LIGHT); + BIND_ENUM_CONSTANT(HINTING_NORMAL); + + /* Feature */ + BIND_ENUM_CONSTANT(FEATURE_BIDI_LAYOUT); + BIND_ENUM_CONSTANT(FEATURE_VERTICAL_LAYOUT); + BIND_ENUM_CONSTANT(FEATURE_SHAPING); + BIND_ENUM_CONSTANT(FEATURE_KASHIDA_JUSTIFICATION); + BIND_ENUM_CONSTANT(FEATURE_BREAK_ITERATORS); + BIND_ENUM_CONSTANT(FEATURE_FONT_SYSTEM); + BIND_ENUM_CONSTANT(FEATURE_USE_SUPPORT_DATA); +} + +Vector3 TextServer::hex_code_box_font_size[2] = { Vector3(5, 5, 1), Vector3(10, 10, 2) }; +Ref<CanvasTexture> TextServer::hex_code_box_font_tex[2] = { nullptr, nullptr }; + +void TextServer::initialize_hex_code_box_fonts() { + static unsigned int tamsyn5x9_png_len = 175; + static unsigned char tamsyn5x9_png[] = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x05, + 0x04, 0x03, 0x00, 0x00, 0x00, 0x20, 0x7c, 0x76, 0xda, 0x00, 0x00, 0x00, + 0x0f, 0x50, 0x4c, 0x54, 0x45, 0xfd, 0x07, 0x00, 0x00, 0x00, 0x00, 0x06, + 0x7e, 0x74, 0x00, 0x40, 0xc6, 0xff, 0xff, 0xff, 0x47, 0x9a, 0xd4, 0xc7, + 0x00, 0x00, 0x00, 0x01, 0x74, 0x52, 0x4e, 0x53, 0x00, 0x40, 0xe6, 0xd8, + 0x66, 0x00, 0x00, 0x00, 0x4e, 0x49, 0x44, 0x41, 0x54, 0x08, 0x1d, 0x05, + 0xc1, 0x21, 0x01, 0x00, 0x00, 0x00, 0x83, 0x30, 0x04, 0xc1, 0x10, 0xef, + 0x9f, 0xe9, 0x1b, 0x86, 0x2c, 0x17, 0xb9, 0xcc, 0x65, 0x0c, 0x73, 0x38, + 0xc7, 0xe6, 0x22, 0x19, 0x88, 0x98, 0x10, 0x48, 0x4a, 0x29, 0x85, 0x14, + 0x02, 0x89, 0x10, 0xa3, 0x1c, 0x0b, 0x31, 0xd6, 0xe6, 0x08, 0x69, 0x39, + 0x48, 0x44, 0xa0, 0x0d, 0x4a, 0x22, 0xa1, 0x94, 0x42, 0x0a, 0x01, 0x63, + 0x6d, 0x0e, 0x72, 0x18, 0x61, 0x8c, 0x74, 0x38, 0xc7, 0x26, 0x1c, 0xf3, + 0x71, 0x16, 0x15, 0x27, 0x6a, 0xc2, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x49, + 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + }; + + static unsigned int tamsyn10x20_png_len = 270; + static unsigned char tamsyn10x20_png[] = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x0a, + 0x04, 0x03, 0x00, 0x00, 0x00, 0xc1, 0x66, 0x48, 0x96, 0x00, 0x00, 0x00, + 0x0f, 0x50, 0x4c, 0x54, 0x45, 0x00, 0x00, 0x00, 0xf9, 0x07, 0x00, 0x5d, + 0x71, 0xa5, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x49, 0xdb, 0xcb, 0x7f, + 0x00, 0x00, 0x00, 0x01, 0x74, 0x52, 0x4e, 0x53, 0x00, 0x40, 0xe6, 0xd8, + 0x66, 0x00, 0x00, 0x00, 0xad, 0x49, 0x44, 0x41, 0x54, 0x28, 0xcf, 0xa5, + 0x92, 0x4b, 0x0e, 0x03, 0x31, 0x08, 0x43, 0xdf, 0x82, 0x83, 0x79, 0xe1, + 0xfb, 0x9f, 0xa9, 0x0b, 0x3e, 0x61, 0xa6, 0x1f, 0x55, 0xad, 0x14, 0x31, + 0x66, 0x42, 0x1c, 0x70, 0x0c, 0xb6, 0x00, 0x01, 0xb6, 0x08, 0xdb, 0x00, + 0x8d, 0xc2, 0x14, 0xb2, 0x55, 0xa1, 0xfe, 0x09, 0xc2, 0x26, 0xdc, 0x25, + 0x75, 0x22, 0x97, 0x1a, 0x25, 0x77, 0x28, 0x31, 0x02, 0x80, 0xc8, 0xdd, + 0x2c, 0x11, 0x1a, 0x54, 0x9f, 0xc8, 0xa2, 0x8a, 0x06, 0xa9, 0x93, 0x22, + 0xbd, 0xd4, 0xd0, 0x0c, 0xcf, 0x81, 0x2b, 0xca, 0xbb, 0x83, 0xe0, 0x10, + 0xe6, 0xad, 0xff, 0x10, 0x2a, 0x66, 0x34, 0x41, 0x58, 0x35, 0x54, 0x49, + 0x5a, 0x63, 0xa5, 0xc2, 0x87, 0xab, 0x52, 0x76, 0x9a, 0xba, 0xc6, 0xf4, + 0x75, 0x7a, 0x9e, 0x3c, 0x46, 0x86, 0x5c, 0xa3, 0xfd, 0x87, 0x0e, 0x75, + 0x08, 0x7b, 0xee, 0x7e, 0xea, 0x21, 0x5c, 0x4f, 0xf6, 0xc5, 0xc8, 0x4b, + 0xb9, 0x11, 0xf2, 0xd6, 0xe1, 0x8f, 0x84, 0x62, 0x7b, 0x67, 0xf9, 0x24, + 0xde, 0x6d, 0xbc, 0xb2, 0xcd, 0xb1, 0xf3, 0xf2, 0x2f, 0xe8, 0xe2, 0xe4, + 0xae, 0x4b, 0x4f, 0xcf, 0x2b, 0xdc, 0x8d, 0x0d, 0xf0, 0x00, 0x8f, 0x22, + 0x26, 0x65, 0x75, 0x8a, 0xe6, 0x84, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, + 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + }; + + if (RenderingServer::get_singleton() != nullptr) { + Vector<uint8_t> hex_box_data; + + Ref<Image> image; + image.instance(); + + Ref<ImageTexture> hex_code_image_tex[2]; + + hex_box_data.resize(tamsyn5x9_png_len); + memcpy(hex_box_data.ptrw(), tamsyn5x9_png, tamsyn5x9_png_len); + image->load_png_from_buffer(hex_box_data); + hex_code_image_tex[0].instance(); + hex_code_image_tex[0]->create_from_image(image); + hex_code_box_font_tex[0].instance(); + hex_code_box_font_tex[0]->set_diffuse_texture(hex_code_image_tex[0]); + hex_code_box_font_tex[0]->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST); + hex_box_data.clear(); + + hex_box_data.resize(tamsyn10x20_png_len); + memcpy(hex_box_data.ptrw(), tamsyn10x20_png, tamsyn10x20_png_len); + image->load_png_from_buffer(hex_box_data); + hex_code_image_tex[1].instance(); + hex_code_image_tex[1]->create_from_image(image); + hex_code_box_font_tex[1].instance(); + hex_code_box_font_tex[1]->set_diffuse_texture(hex_code_image_tex[1]); + hex_code_box_font_tex[1]->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST); + hex_box_data.clear(); + } +} + +void TextServer::finish_hex_code_box_fonts() { + if (hex_code_box_font_tex[0].is_valid()) { + hex_code_box_font_tex[0].unref(); + } + if (hex_code_box_font_tex[1].is_valid()) { + hex_code_box_font_tex[1].unref(); + } +} + +Vector2 TextServer::get_hex_code_box_size(int p_size, char32_t p_index) const { + int fnt = (p_size < 20) ? 0 : 1; + + float w = ((p_index <= 0xFF) ? 1 : ((p_index <= 0xFFFF) ? 2 : 3)) * hex_code_box_font_size[fnt].x; + float h = 2 * hex_code_box_font_size[fnt].y; + return Vector2(w + 4, h + 3 + 2 * hex_code_box_font_size[fnt].z); +} + +void TextServer::draw_hex_code_box(RID p_canvas, int p_size, const Vector2 &p_pos, char32_t p_index, const Color &p_color) const { + int fnt = (p_size < 20) ? 0 : 1; + + ERR_FAIL_COND(hex_code_box_font_tex[fnt].is_null()); + + uint8_t a = p_index & 0x0F; + uint8_t b = (p_index >> 4) & 0x0F; + uint8_t c = (p_index >> 8) & 0x0F; + uint8_t d = (p_index >> 12) & 0x0F; + uint8_t e = (p_index >> 16) & 0x0F; + uint8_t f = (p_index >> 20) & 0x0F; + + Vector2 pos = p_pos; + Rect2 dest = Rect2(Vector2(), Vector2(hex_code_box_font_size[fnt].x, hex_code_box_font_size[fnt].y)); + + float w = ((p_index <= 0xFF) ? 1 : ((p_index <= 0xFFFF) ? 2 : 3)) * hex_code_box_font_size[fnt].x; + float h = 2 * hex_code_box_font_size[fnt].y; + + pos.y -= Math::floor((h + 3 + hex_code_box_font_size[fnt].z) * 0.75); + + RenderingServer::get_singleton()->canvas_item_add_rect(p_canvas, Rect2(pos + Point2(0, 0), Size2(1, h + 2 + 2 * hex_code_box_font_size[fnt].z)), p_color); + RenderingServer::get_singleton()->canvas_item_add_rect(p_canvas, Rect2(pos + Point2(w + 2, 0), Size2(1, h + 2 + 2 * hex_code_box_font_size[fnt].z)), p_color); + RenderingServer::get_singleton()->canvas_item_add_rect(p_canvas, Rect2(pos + Point2(0, 0), Size2(w + 2, 1)), p_color); + RenderingServer::get_singleton()->canvas_item_add_rect(p_canvas, Rect2(pos + Point2(0, h + 2 + 2 * hex_code_box_font_size[fnt].z), Size2(w + 2, 1)), p_color); + + pos += Point2(2, 2); + if (p_index <= 0xFF) { + dest.position = pos + Vector2(hex_code_box_font_size[fnt].x, hex_code_box_font_size[fnt].y) * Point2(0, 0); + RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, dest, hex_code_box_font_tex[fnt]->get_rid(), Rect2(Point2(b * hex_code_box_font_size[fnt].x, 0), dest.size), p_color, false, false); + dest.position = pos + Vector2(hex_code_box_font_size[fnt].x, hex_code_box_font_size[fnt].y) * Point2(0, 1) + Point2(0, hex_code_box_font_size[fnt].z); + RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, dest, hex_code_box_font_tex[fnt]->get_rid(), Rect2(Point2(a * hex_code_box_font_size[fnt].x, 0), dest.size), p_color, false, false); + } else if (p_index <= 0xFFFF) { + dest.position = pos + Vector2(hex_code_box_font_size[fnt].x, hex_code_box_font_size[fnt].y) * Point2(0, 0); + RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, dest, hex_code_box_font_tex[fnt]->get_rid(), Rect2(Point2(d * hex_code_box_font_size[fnt].x, 0), dest.size), p_color, false, false); + dest.position = pos + Vector2(hex_code_box_font_size[fnt].x, hex_code_box_font_size[fnt].y) * Point2(1, 0); + RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, dest, hex_code_box_font_tex[fnt]->get_rid(), Rect2(Point2(c * hex_code_box_font_size[fnt].x, 0), dest.size), p_color, false, false); + dest.position = pos + Vector2(hex_code_box_font_size[fnt].x, hex_code_box_font_size[fnt].y) * Point2(0, 1) + Point2(0, hex_code_box_font_size[fnt].z); + RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, dest, hex_code_box_font_tex[fnt]->get_rid(), Rect2(Point2(b * hex_code_box_font_size[fnt].x, 0), dest.size), p_color, false, false); + dest.position = pos + Vector2(hex_code_box_font_size[fnt].x, hex_code_box_font_size[fnt].y) * Point2(1, 1) + Point2(0, hex_code_box_font_size[fnt].z); + RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, dest, hex_code_box_font_tex[fnt]->get_rid(), Rect2(Point2(a * hex_code_box_font_size[fnt].x, 0), dest.size), p_color, false, false); + } else { + dest.position = pos + Vector2(hex_code_box_font_size[fnt].x, hex_code_box_font_size[fnt].y) * Point2(0, 0); + RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, dest, hex_code_box_font_tex[fnt]->get_rid(), Rect2(Point2(f * hex_code_box_font_size[fnt].x, 0), dest.size), p_color, false, false); + dest.position = pos + Vector2(hex_code_box_font_size[fnt].x, hex_code_box_font_size[fnt].y) * Point2(1, 0); + RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, dest, hex_code_box_font_tex[fnt]->get_rid(), Rect2(Point2(e * hex_code_box_font_size[fnt].x, 0), dest.size), p_color, false, false); + dest.position = pos + Vector2(hex_code_box_font_size[fnt].x, hex_code_box_font_size[fnt].y) * Point2(2, 0); + RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, dest, hex_code_box_font_tex[fnt]->get_rid(), Rect2(Point2(d * hex_code_box_font_size[fnt].x, 0), dest.size), p_color, false, false); + dest.position = pos + Vector2(hex_code_box_font_size[fnt].x, hex_code_box_font_size[fnt].y) * Point2(0, 1) + Point2(0, hex_code_box_font_size[fnt].z); + RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, dest, hex_code_box_font_tex[fnt]->get_rid(), Rect2(Point2(c * hex_code_box_font_size[fnt].x, 0), dest.size), p_color, false, false); + dest.position = pos + Vector2(hex_code_box_font_size[fnt].x, hex_code_box_font_size[fnt].y) * Point2(1, 1) + Point2(0, hex_code_box_font_size[fnt].z); + RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, dest, hex_code_box_font_tex[fnt]->get_rid(), Rect2(Point2(b * hex_code_box_font_size[fnt].x, 0), dest.size), p_color, false, false); + dest.position = pos + Vector2(hex_code_box_font_size[fnt].x, hex_code_box_font_size[fnt].y) * Point2(2, 1) + Point2(0, hex_code_box_font_size[fnt].z); + RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, dest, hex_code_box_font_tex[fnt]->get_rid(), Rect2(Point2(a * hex_code_box_font_size[fnt].x, 0), dest.size), p_color, false, false); + } +} + +Vector<Vector2i> TextServer::shaped_text_get_line_breaks_adv(RID p_shaped, const Vector<float> &p_width, int p_start, bool p_once, uint8_t /*TextBreakFlag*/ p_break_flags) const { + Vector<Vector2i> lines; + + ERR_FAIL_COND_V(p_width.empty(), lines); + + const_cast<TextServer *>(this)->shaped_text_update_breaks(p_shaped); + const Vector<Glyph> &logical = const_cast<TextServer *>(this)->shaped_text_sort_logical(p_shaped); + const Vector2i &range = shaped_text_get_range(p_shaped); + + float width = 0.f; + int line_start = MAX(p_start, range.x); + int last_safe_break = -1; + int chunk = 0; + for (int i = 0; i < logical.size(); i++) { + if (logical[i].start < p_start) { + continue; + } + if (logical[i].count > 0) { + if ((p_width[chunk] > 0) && (width + logical[i].advance > p_width[chunk]) && (last_safe_break >= 0)) { + lines.push_back(Vector2i(line_start, logical[last_safe_break].end)); + line_start = logical[last_safe_break].end; + i = last_safe_break; + last_safe_break = -1; + width = 0; + chunk++; + if (chunk >= p_width.size()) { + chunk = 0; + if (p_once) { + return lines; + } + } + continue; + } + if ((p_break_flags & BREAK_MANDATORY) == BREAK_MANDATORY) { + if ((logical[i].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD) { + lines.push_back(Vector2i(line_start, logical[i].end)); + line_start = logical[i].end; + last_safe_break = -1; + width = 0; + chunk = 0; + if (p_once) { + return lines; + } + continue; + } + } + if ((p_break_flags & BREAK_WORD_BOUND) == BREAK_WORD_BOUND) { + if ((logical[i].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT) { + last_safe_break = i; + } + } + if ((p_break_flags & BREAK_GRAPHEME_BOUND) == BREAK_GRAPHEME_BOUND) { + last_safe_break = i; + } + } + width += logical[i].advance; + } + + if (logical.size() > 0) { + lines.push_back(Vector2i(line_start, range.y)); + } else { + lines.push_back(Vector2i(0, 0)); + } + + return lines; +} + +Vector<Vector2i> TextServer::shaped_text_get_line_breaks(RID p_shaped, float p_width, int p_start, uint8_t /*TextBreakFlag*/ p_break_flags) const { + Vector<Vector2i> lines; + + const_cast<TextServer *>(this)->shaped_text_update_breaks(p_shaped); + const Vector<Glyph> &logical = const_cast<TextServer *>(this)->shaped_text_sort_logical(p_shaped); + const Vector2i &range = shaped_text_get_range(p_shaped); + + float width = 0.f; + int line_start = MAX(p_start, range.x); + int last_safe_break = -1; + for (int i = 0; i < logical.size(); i++) { + if (logical[i].start < p_start) { + continue; + } + if (logical[i].count > 0) { + if ((p_width > 0) && (width + logical[i].advance > p_width) && (last_safe_break >= 0)) { + lines.push_back(Vector2i(line_start, logical[last_safe_break].end)); + line_start = logical[last_safe_break].end; + i = last_safe_break; + last_safe_break = -1; + width = 0; + continue; + } + if ((p_break_flags & BREAK_MANDATORY) == BREAK_MANDATORY) { + if ((logical[i].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD) { + lines.push_back(Vector2i(line_start, logical[i].end)); + line_start = logical[i].end; + last_safe_break = -1; + width = 0; + continue; + } + } + if ((p_break_flags & BREAK_WORD_BOUND) == BREAK_WORD_BOUND) { + if ((logical[i].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT) { + last_safe_break = i; + } + } + if ((p_break_flags & BREAK_GRAPHEME_BOUND) == BREAK_GRAPHEME_BOUND) { + last_safe_break = i; + } + } + width += logical[i].advance; + } + + if (logical.size() > 0) { + if (lines.size() == 0 || lines[lines.size() - 1].y < range.y) { + lines.push_back(Vector2i(line_start, range.y)); + } + } else { + lines.push_back(Vector2i(0, 0)); + } + + return lines; +} + +Vector<Vector2i> TextServer::shaped_text_get_word_breaks(RID p_shaped) const { + Vector<Vector2i> words; + + const_cast<TextServer *>(this)->shaped_text_update_breaks(p_shaped); + const Vector<Glyph> &logical = const_cast<TextServer *>(this)->shaped_text_sort_logical(p_shaped); + const Vector2i &range = shaped_text_get_range(p_shaped); + + int word_start = range.x; + for (int i = 0; i < logical.size(); i++) { + if (logical[i].count > 0) { + if ((logical[i].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE) { + words.push_back(Vector2i(word_start, logical[i].end - 1)); + word_start = logical[i].end; + } + } + } + if (logical.size() > 0) { + words.push_back(Vector2i(word_start, range.y)); + } + + return words; +} + +void TextServer::shaped_text_get_carets(RID p_shaped, int p_position, Rect2 &p_leading_caret, Direction &p_leading_dir, Rect2 &p_trailing_caret, Direction &p_trailing_dir) const { + Vector<Rect2> carets; + const Vector<TextServer::Glyph> glyphs = shaped_text_get_glyphs(p_shaped); + TextServer::Orientation orientation = shaped_text_get_orientation(p_shaped); + const Vector2 &range = shaped_text_get_range(p_shaped); + float ascent = shaped_text_get_ascent(p_shaped); + float descent = shaped_text_get_descent(p_shaped); + float height = (ascent + descent) / 2; + + float off = 0.0f; + p_leading_dir = DIRECTION_AUTO; + p_trailing_dir = DIRECTION_AUTO; + for (int i = 0; i < glyphs.size(); i++) { + if (glyphs[i].count > 0) { + // Caret before grapheme (top / left). + if (p_position == glyphs[i].start && ((glyphs[i].flags & GRAPHEME_IS_VIRTUAL) != GRAPHEME_IS_VIRTUAL)) { + Rect2 cr; + if (orientation == ORIENTATION_HORIZONTAL) { + if (glyphs[i].start == range.x) { + cr.size.y = height * 2; + } else { + cr.size.y = height; + } + cr.position.y = -ascent; + cr.position.x = off; + if ((glyphs[i].flags & GRAPHEME_IS_RTL) == GRAPHEME_IS_RTL) { + p_trailing_dir = DIRECTION_RTL; + for (int j = 0; j < glyphs[i].count; j++) { + cr.position.x += glyphs[i + j].advance * glyphs[i + j].repeat; + cr.size.x -= glyphs[i + j].advance * glyphs[i + j].repeat; + } + } else { + p_trailing_dir = DIRECTION_LTR; + for (int j = 0; j < glyphs[i].count; j++) { + cr.size.x += glyphs[i + j].advance * glyphs[i + j].repeat; + } + } + } else { + if (glyphs[i].start == range.x) { + cr.size.x = height * 2; + } else { + cr.size.x = height; + } + cr.position.x = -ascent; + cr.position.y = off; + if ((glyphs[i].flags & GRAPHEME_IS_RTL) == GRAPHEME_IS_RTL) { + p_trailing_dir = DIRECTION_RTL; + for (int j = 0; j < glyphs[i].count; j++) { + cr.position.y += glyphs[i + j].advance * glyphs[i + j].repeat; + cr.size.y -= glyphs[i + j].advance * glyphs[i + j].repeat; + } + } else { + p_trailing_dir = DIRECTION_LTR; + for (int j = 0; j < glyphs[i].count; j++) { + cr.size.y += glyphs[i + j].advance * glyphs[i + j].repeat; + } + } + } + p_trailing_caret = cr; + } + // Caret after grapheme (bottom / right). + if (p_position == glyphs[i].end && ((glyphs[i].flags & GRAPHEME_IS_VIRTUAL) != GRAPHEME_IS_VIRTUAL)) { + Rect2 cr; + if (orientation == ORIENTATION_HORIZONTAL) { + if (glyphs[i].end == range.y) { + cr.size.y = height * 2; + cr.position.y = -ascent; + } else { + cr.size.y = height; + cr.position.y = -ascent + height; + } + cr.position.x = off; + if ((glyphs[i].flags & GRAPHEME_IS_RTL) != GRAPHEME_IS_RTL) { + p_leading_dir = DIRECTION_LTR; + for (int j = 0; j < glyphs[i].count; j++) { + cr.position.x += glyphs[i + j].advance * glyphs[i + j].repeat; + cr.size.x -= glyphs[i + j].advance * glyphs[i + j].repeat; + } + } else { + p_leading_dir = DIRECTION_RTL; + for (int j = 0; j < glyphs[i].count; j++) { + cr.size.x += glyphs[i + j].advance * glyphs[i + j].repeat; + } + } + } else { + cr.size.y = 1.0f; + if (glyphs[i].end == range.y) { + cr.size.x = height * 2; + cr.position.x = -ascent; + } else { + cr.size.x = height; + cr.position.x = -ascent + height; + } + cr.position.y = off; + if ((glyphs[i].flags & GRAPHEME_IS_RTL) != GRAPHEME_IS_RTL) { + p_leading_dir = DIRECTION_LTR; + for (int j = 0; j < glyphs[i].count; j++) { + cr.position.y += glyphs[i + j].advance * glyphs[i + j].repeat; + cr.size.y -= glyphs[i + j].advance * glyphs[i + j].repeat; + } + } else { + p_leading_dir = DIRECTION_RTL; + for (int j = 0; j < glyphs[i].count; j++) { + cr.size.y += glyphs[i + j].advance * glyphs[i + j].repeat; + } + } + } + p_leading_caret = cr; + } + // Caret inside grapheme (middle). + if (p_position > glyphs[i].start && p_position < glyphs[i].end && (glyphs[i].flags & GRAPHEME_IS_VIRTUAL) != GRAPHEME_IS_VIRTUAL) { + float advance = 0.f; + for (int j = 0; j < glyphs[i].count; j++) { + advance += glyphs[i + j].advance * glyphs[i + j].repeat; + } + float char_adv = advance / (float)(glyphs[i].end - glyphs[i].start); + Rect2 cr; + if (orientation == ORIENTATION_HORIZONTAL) { + cr.size.x = 1.0f; + cr.size.y = height * 2; + cr.position.y = -ascent; + if ((glyphs[i].flags & GRAPHEME_IS_RTL) == GRAPHEME_IS_RTL) { + cr.position.x = off + char_adv * (glyphs[i].end - p_position); + } else { + cr.position.x = off + char_adv * (p_position - glyphs[i].start); + } + } else { + cr.size.y = 1.0f; + cr.size.x = height * 2; + cr.position.x = -ascent; + if ((glyphs[i].flags & GRAPHEME_IS_RTL) == GRAPHEME_IS_RTL) { + cr.position.y = off + char_adv * (glyphs[i].end - p_position); + } else { + cr.position.y = off + char_adv * (p_position - glyphs[i].start); + } + } + p_trailing_caret = cr; + p_leading_caret = cr; + } + } + off += glyphs[i].advance * glyphs[i].repeat; + } +} + +TextServer::Direction TextServer::shaped_text_get_dominant_direciton_in_range(RID p_shaped, int p_start, int p_end) const { + const Vector<TextServer::Glyph> glyphs = shaped_text_get_glyphs(p_shaped); + + if (p_start == p_end) { + return DIRECTION_AUTO; + } + + int start = MIN(p_start, p_end); + int end = MAX(p_start, p_end); + + int rtl = 0; + int ltr = 0; + + for (int i = 0; i < glyphs.size(); i++) { + if ((glyphs[i].end > start) && (glyphs[i].start < end)) { + if (glyphs[i].count > 0) { + if ((glyphs[i].flags & GRAPHEME_IS_RTL) == GRAPHEME_IS_RTL) { + rtl++; + } else { + ltr++; + } + } + } + } + if (ltr == rtl) { + return DIRECTION_AUTO; + } else if (ltr > rtl) { + return DIRECTION_LTR; + } else { + return DIRECTION_RTL; + } +} + +Vector<Vector2> TextServer::shaped_text_get_selection(RID p_shaped, int p_start, int p_end) const { + Vector<Vector2> ranges; + const Vector<TextServer::Glyph> glyphs = shaped_text_get_glyphs(p_shaped); + + if (p_start == p_end) { + return ranges; + } + + int start = MIN(p_start, p_end); + int end = MAX(p_start, p_end); + + float off = 0.0f; + for (int i = 0; i < glyphs.size(); i++) { + for (int k = 0; k < glyphs[i].repeat; k++) { + if (glyphs[i].count > 0 && glyphs[i].index != 0) { + if (glyphs[i].start < end && glyphs[i].end > start) { + // Grapheme fully in selection range. + if (glyphs[i].start >= start && glyphs[i].end <= end) { + float advance = 0.f; + for (int j = 0; j < glyphs[i].count; j++) { + advance += glyphs[i + j].advance; + } + ranges.push_back(Vector2(off, off + advance)); + } + // Only start of grapheme is in selection range. + if (glyphs[i].start >= start && glyphs[i].end > end) { + float advance = 0.f; + for (int j = 0; j < glyphs[i].count; j++) { + advance += glyphs[i + j].advance; + } + float char_adv = advance / (float)(glyphs[i].end - glyphs[i].start); + if ((glyphs[i].flags & GRAPHEME_IS_RTL) == GRAPHEME_IS_RTL) { + ranges.push_back(Vector2(off + char_adv * (glyphs[i].end - end), off + advance)); + } else { + ranges.push_back(Vector2(off, off + char_adv * (end - glyphs[i].start))); + } + } + // Only end of grapheme is in selection range. + if (glyphs[i].start < start && glyphs[i].end <= end) { + float advance = 0.f; + for (int j = 0; j < glyphs[i].count; j++) { + advance += glyphs[i + j].advance; + } + float char_adv = advance / (float)(glyphs[i].end - glyphs[i].start); + if ((glyphs[i].flags & GRAPHEME_IS_RTL) == GRAPHEME_IS_RTL) { + ranges.push_back(Vector2(off, off + char_adv * (start - glyphs[i].start))); + } else { + ranges.push_back(Vector2(off + char_adv * (glyphs[i].end - start), off + advance)); + } + } + // Selection range is within grapheme + if (glyphs[i].start < start && glyphs[i].end > end) { + float advance = 0.f; + for (int j = 0; j < glyphs[i].count; j++) { + advance += glyphs[i + j].advance; + } + float char_adv = advance / (float)(glyphs[i].end - glyphs[i].start); + if ((glyphs[i].flags & GRAPHEME_IS_RTL) == GRAPHEME_IS_RTL) { + ranges.push_back(Vector2(off + char_adv * (glyphs[i].end - end), off + char_adv * (glyphs[i].end - start))); + } else { + ranges.push_back(Vector2(off + char_adv * (start - glyphs[i].start), off + char_adv * (end - glyphs[i].start))); + } + } + } + } + off += glyphs[i].advance; + } + } + + // Merge intersecting ranges. + int i = 0; + while (i < ranges.size()) { + int j = i + 1; + while (j < ranges.size()) { + if (Math::is_equal_approx(ranges[i].y, ranges[j].x, UNIT_EPSILON)) { + ranges.write[i].y = ranges[j].y; + ranges.remove(j); + continue; + } + j++; + } + i++; + } + + return ranges; +} + +int TextServer::shaped_text_hit_test_grapheme(RID p_shaped, float p_coords) const { + const Vector<TextServer::Glyph> glyphs = shaped_text_get_glyphs(p_shaped); + + // Exact grapheme hit test, return -1 if missed. + float off = 0.0f; + for (int i = 0; i < glyphs.size(); i++) { + for (int j = 0; j < glyphs[i].repeat; j++) { + if (p_coords >= off && p_coords < off + glyphs[i].advance) { + return i; + } + off += glyphs[i].advance; + } + } + return -1; +} + +int TextServer::shaped_text_hit_test_position(RID p_shaped, float p_coords) const { + const Vector<TextServer::Glyph> glyphs = shaped_text_get_glyphs(p_shaped); + + // Cursor placement hit test. + + // Place caret to the left of the leftmost grapheme, or to position 0 if string is empty. + if (p_coords <= 0) { + if (glyphs.size() > 0) { + if ((glyphs[0].flags & GRAPHEME_IS_RTL) == GRAPHEME_IS_RTL) { + return glyphs[0].end; + } else { + return glyphs[0].start; + } + } else { + return 0; + } + } + + // Place caret to the right of the rightmost grapheme, or to position 0 if string is empty. + if (p_coords >= shaped_text_get_width(p_shaped)) { + if (glyphs.size() > 0) { + if ((glyphs[glyphs.size() - 1].flags & GRAPHEME_IS_RTL) == GRAPHEME_IS_RTL) { + return glyphs[glyphs.size() - 1].start; + } else { + return glyphs[glyphs.size() - 1].end; + } + } else { + return 0; + } + } + + float off = 0.0f; + for (int i = 0; i < glyphs.size(); i++) { + for (int k = 0; k < glyphs[i].repeat; k++) { + if (glyphs[i].count > 0) { + float advance = 0.f; + for (int j = 0; j < glyphs[i].count; j++) { + advance += glyphs[i + j].advance; + } + // Place caret to the left of clicked grapheme. + if (p_coords >= off && p_coords < off + advance / 2) { + if ((glyphs[i].flags & GRAPHEME_IS_RTL) == GRAPHEME_IS_RTL) { + return glyphs[i].end; + } else { + return glyphs[i].start; + } + } + // Place caret to the right of clicked grapheme. + if (p_coords >= off + advance / 2 && p_coords < off + advance) { + if ((glyphs[i].flags & GRAPHEME_IS_RTL) == GRAPHEME_IS_RTL) { + return glyphs[i].start; + } else { + return glyphs[i].end; + } + } + } + off += glyphs[i].advance; + } + } + return 0; +} + +int TextServer::shaped_text_next_grapheme_pos(RID p_shaped, int p_pos) { + const Vector<TextServer::Glyph> glyphs = shaped_text_get_glyphs(p_shaped); + for (int i = 0; i < glyphs.size(); i++) { + if (p_pos >= glyphs[i].start && p_pos < glyphs[i].end) { + return glyphs[i].end; + } + } + return p_pos; +} + +int TextServer::shaped_text_prev_grapheme_pos(RID p_shaped, int p_pos) { + const Vector<TextServer::Glyph> glyphs = shaped_text_get_glyphs(p_shaped); + for (int i = 0; i < glyphs.size(); i++) { + if (p_pos > glyphs[i].start && p_pos <= glyphs[i].end) { + return glyphs[i].start; + } + } + + return p_pos; +} + +void TextServer::shaped_text_draw(RID p_shaped, RID p_canvas, const Vector2 &p_pos, float p_clip_l, float p_clip_r, const Color &p_color) const { + const Vector<TextServer::Glyph> glyphs = shaped_text_get_glyphs(p_shaped); + TextServer::Orientation orientation = shaped_text_get_orientation(p_shaped); + bool hex_codes = shaped_text_get_preserve_control(p_shaped) || shaped_text_get_preserve_invalid(p_shaped); + + Vector2 ofs = p_pos; + // Draw at the baseline. + for (int i = 0; i < glyphs.size(); i++) { + for (int j = 0; j < glyphs[i].repeat; j++) { + if (p_clip_r > 0) { + // Clip right / bottom. + if (orientation == ORIENTATION_HORIZONTAL) { + if (ofs.x - p_pos.x > p_clip_r) { + return; + } + } else { + if (ofs.y - p_pos.y > p_clip_r) { + return; + } + } + } + if (p_clip_l > 0) { + // Clip left / top. + if (orientation == ORIENTATION_HORIZONTAL) { + if (ofs.x - p_pos.x < p_clip_l) { + ofs.x += glyphs[i].advance; + continue; + } + } else { + if (ofs.y - p_pos.y < p_clip_l) { + ofs.y += glyphs[i].advance; + continue; + } + } + } + if (glyphs[i].font_rid != RID()) { + font_draw_glyph(glyphs[i].font_rid, p_canvas, glyphs[i].font_size, ofs + Vector2(glyphs[i].x_off, glyphs[i].y_off), glyphs[i].index, p_color); + } else if (hex_codes && ((glyphs[i].flags & GRAPHEME_IS_VIRTUAL) != GRAPHEME_IS_VIRTUAL)) { + TextServer::draw_hex_code_box(p_canvas, glyphs[i].font_size, ofs + Vector2(glyphs[i].x_off, glyphs[i].y_off), glyphs[i].index, p_color); + } + if (orientation == ORIENTATION_HORIZONTAL) { + ofs.x += glyphs[i].advance; + } else { + ofs.y += glyphs[i].advance; + } + } + } +} + +void TextServer::shaped_text_draw_outline(RID p_shaped, RID p_canvas, const Vector2 &p_pos, float p_clip_l, float p_clip_r, int p_outline_size, const Color &p_color) const { + const Vector<TextServer::Glyph> glyphs = shaped_text_get_glyphs(p_shaped); + TextServer::Orientation orientation = shaped_text_get_orientation(p_shaped); + + Vector2 ofs = p_pos; + // Draw at the baseline. + for (int i = 0; i < glyphs.size(); i++) { + for (int j = 0; j < glyphs[i].repeat; j++) { + if (p_clip_r > 0) { + // Clip right / bottom. + if (orientation == ORIENTATION_HORIZONTAL) { + if (ofs.x - p_pos.x > p_clip_r) { + return; + } + } else { + if (ofs.y - p_pos.y > p_clip_r) { + return; + } + } + } + if (p_clip_l > 0) { + // Clip left / top. + if (orientation == ORIENTATION_HORIZONTAL) { + if (ofs.x - p_pos.x < p_clip_l) { + ofs.x += glyphs[i].advance; + continue; + } + } else { + if (ofs.y - p_pos.y < p_clip_l) { + ofs.y += glyphs[i].advance; + continue; + } + } + } + if (glyphs[i].font_rid != RID()) { + font_draw_glyph_outline(glyphs[i].font_rid, p_canvas, glyphs[i].font_size, p_outline_size, ofs + Vector2(glyphs[i].x_off, glyphs[i].y_off), glyphs[i].index, p_color); + } + if (orientation == ORIENTATION_HORIZONTAL) { + ofs.x += glyphs[i].advance; + } else { + ofs.y += glyphs[i].advance; + } + } + } +} + +RID TextServer::_create_font_memory(const PackedByteArray &p_data, const String &p_type, int p_base_size) { + return create_font_memory(p_data.ptr(), p_data.size(), p_type, p_base_size); +} + +void TextServer::_shaped_text_set_bidi_override(RID p_shaped, const Array &p_override) { + Vector<Vector2i> overrides; + for (int i = 0; i < p_override.size(); i++) { + overrides.push_back(p_override[i]); + } + shaped_text_set_bidi_override(p_shaped, overrides); +} + +Array TextServer::_shaped_text_get_glyphs(RID p_shaped) const { + Array ret; + + Vector<Glyph> glyphs = shaped_text_get_glyphs(p_shaped); + for (int i = 0; i < glyphs.size(); i++) { + Dictionary glyph; + + glyph["start"] = glyphs[i].start; + glyph["end"] = glyphs[i].end; + glyph["repeat"] = glyphs[i].repeat; + glyph["count"] = glyphs[i].count; + glyph["flags"] = glyphs[i].flags; + glyph["offset"] = Vector2(glyphs[i].x_off, glyphs[i].y_off); + glyph["advance"] = glyphs[i].advance; + glyph["font_rid"] = glyphs[i].font_rid; + glyph["font_size"] = glyphs[i].font_size; + glyph["index"] = glyphs[i].index; + + ret.push_back(glyph); + } + + return ret; +} + +Array TextServer::_shaped_text_get_line_breaks_adv(RID p_shaped, const PackedFloat32Array &p_width, int p_start, bool p_once, uint8_t p_break_flags) const { + Array ret; + + Vector<Vector2i> lines = shaped_text_get_line_breaks_adv(p_shaped, p_width, p_start, p_once, p_break_flags); + for (int i = 0; i < lines.size(); i++) { + ret.push_back(lines[i]); + } + + return ret; +} + +Array TextServer::_shaped_text_get_line_breaks(RID p_shaped, float p_width, int p_start, uint8_t p_break_flags) const { + Array ret; + + Vector<Vector2i> lines = shaped_text_get_line_breaks(p_shaped, p_width, p_start, p_break_flags); + for (int i = 0; i < lines.size(); i++) { + ret.push_back(lines[i]); + } + + return ret; +} + +Array TextServer::_shaped_text_get_word_breaks(RID p_shaped) const { + Array ret; + + Vector<Vector2i> words = shaped_text_get_word_breaks(p_shaped); + for (int i = 0; i < words.size(); i++) { + ret.push_back(words[i]); + } + + return ret; +} + +Dictionary TextServer::_shaped_text_get_carets(RID p_shaped, int p_position) const { + Dictionary ret; + + Rect2 l_caret, t_caret; + Direction l_dir, t_dir; + shaped_text_get_carets(p_shaped, p_position, l_caret, l_dir, t_caret, t_dir); + + ret["leading_rect"] = l_caret; + ret["leading_direction"] = l_dir; + ret["trailing_rect"] = t_caret; + ret["trailing_direction"] = t_dir; + + return ret; +} + +Array TextServer::_shaped_text_get_selection(RID p_shaped, int p_start, int p_end) const { + Array ret; + + Vector<Vector2> ranges = shaped_text_get_selection(p_shaped, p_start, p_end); + for (int i = 0; i < ranges.size(); i++) { + ret.push_back(ranges[i]); + } + + return ret; +} + +TextServer::TextServer() { +} + +TextServer::~TextServer() { +} diff --git a/servers/text_server.h b/servers/text_server.h index e69de29bb2..6796b7f1d6 100644 --- a/servers/text_server.h +++ b/servers/text_server.h @@ -0,0 +1,453 @@ +/*************************************************************************/ +/* text_server.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 TEXT_SERVER_H +#define TEXT_SERVER_H + +#include "core/object/reference.h" +#include "core/os/os.h" +#include "core/templates/rid.h" +#include "core/variant/variant.h" +#include "scene/resources/texture.h" + +class CanvasTexture; + +class TextServer : public Object { + GDCLASS(TextServer, Object); + +public: + enum Direction { + DIRECTION_AUTO, + DIRECTION_LTR, + DIRECTION_RTL + }; + + enum Orientation { + ORIENTATION_HORIZONTAL, + ORIENTATION_VERTICAL + }; + + enum JustificationFlag { + JUSTIFICATION_NONE = 0, + JUSTIFICATION_KASHIDA = 1 << 0, + JUSTIFICATION_WORD_BOUND = 1 << 1, + JUSTIFICATION_TRIM_EDGE_SPACES = 1 << 2, + JUSTIFICATION_AFTER_LAST_TAB = 1 << 3 + }; + + enum LineBreakFlag { + BREAK_NONE = 0, + BREAK_MANDATORY = 1 << 4, + BREAK_WORD_BOUND = 1 << 5, + BREAK_GRAPHEME_BOUND = 1 << 6 + //RESERVED = 1 << 7 + }; + + enum GraphemeFlag { + GRAPHEME_IS_VALID = 1 << 0, // Glyph is valid. + GRAPHEME_IS_RTL = 1 << 1, // Glyph is right-to-left. + GRAPHEME_IS_VIRTUAL = 1 << 2, // Glyph is not part of source string (added by fit_to_width function, do not affect caret movement). + GRAPHEME_IS_SPACE = 1 << 3, // Is whitespace (for justification). + GRAPHEME_IS_BREAK_HARD = 1 << 4, // Is line break (mandatory break, e.g "\n") + GRAPHEME_IS_BREAK_SOFT = 1 << 5, // Is line break (optional break, e.g space) + GRAPHEME_IS_TAB = 1 << 6, // Is tab or vertical tab + GRAPHEME_IS_ELONGATION = 1 << 7 // Elongation (e.g kashida), glyph can be duplicated or truncated to fit line to width. + }; + + enum Hinting { + HINTING_NONE, + HINTING_LIGHT, + HINTING_NORMAL + }; + + enum Feature { + FEATURE_BIDI_LAYOUT = 1 << 0, + FEATURE_VERTICAL_LAYOUT = 1 << 1, + FEATURE_SHAPING = 1 << 2, + FEATURE_KASHIDA_JUSTIFICATION = 1 << 3, + FEATURE_BREAK_ITERATORS = 1 << 4, + FEATURE_FONT_SYSTEM = 1 << 5, + FEATURE_USE_SUPPORT_DATA = 1 << 6 + }; + + struct Glyph { + int start = -1; // Start offset in the source string. + int end = -1; // End offset in the source string. + + uint8_t count = 0; // Number of glyphs in the grapheme, set in the first glyph only. + uint8_t repeat = 1; // Draw multiple times in the row. + uint8_t flags = 0; // Grapheme flags (valid, rtl, virtual), set in the first glyph only. + + float x_off = 0.f; // Offset from the origin of the glyph on baseline. + float y_off = 0.f; + float advance = 0.f; // Advance to the next glyph along baseline(x for horizontal layout, y for vertical). + + RID font_rid; // Font resource. + int font_size = 0; // Font size; + uint32_t index = 0; // Glyph index (font specific) or UTF-32 codepoint (for the invalid glyphs). + + bool operator==(const Glyph &p_a) const; + bool operator!=(const Glyph &p_a) const; + + bool operator<(const Glyph &p_a) const; + bool operator>(const Glyph &p_a) const; + }; + + struct GlyphCompare { // For line breaking reordering. + _FORCE_INLINE_ bool operator()(const Glyph &l, const Glyph &r) const { + if (l.start == r.start) { + if (l.count == r.count) { + if ((l.flags & GRAPHEME_IS_VIRTUAL) == GRAPHEME_IS_VIRTUAL) { + return false; + } else { + return true; + } + } + return l.count > r.count; // Sort first glyoh with count & flags, order of the rest are irrelevant. + } else { + return l.start < r.start; + } + } + }; + + struct ShapedTextData { + /* Source data */ + RID parent; // Substring parent ShapedTextData. + + int start = 0; // Substring start offset in the parent string. + int end = 0; // Substring end offset in the parent string. + + String text; + TextServer::Direction direction = DIRECTION_LTR; // Desired text direction. + TextServer::Orientation orientation = ORIENTATION_HORIZONTAL; + + struct Span { + int start = -1; + int end = -1; + + Vector<RID> fonts; + int font_size = 0; + + Variant embedded_key; + + String language; + Dictionary features; + }; + Vector<Span> spans; + + struct EmbeddedObject { + int pos = 0; + VAlign inline_align = VALIGN_TOP; + Rect2 rect; + }; + Map<Variant, EmbeddedObject> objects; + + /* Shaped data */ + TextServer::Direction para_direction = DIRECTION_LTR; // Detected text direction. + bool valid = false; // String is shaped. + bool line_breaks_valid = false; // Line and word break flags are populated (and virtual zero width spaces inserted). + bool justification_ops_valid = false; // Virtual elongation glyphs are added to the string. + bool sort_valid = false; + + bool preserve_invalid = true; // Draw hex code box instead of missing characters. + bool preserve_control = false; // Draw control characters. + + float ascent = 0.f; // Ascent for horizontal layout, 1/2 of width for vertical. + float descent = 0.f; // Descent for horizontal layout, 1/2 of width for vertical. + float width = 0.f; // Width for horizontal layout, height for vertical. + + float upos = 0.f; + float uthk = 0.f; + + Vector<TextServer::Glyph> glyphs; + Vector<TextServer::Glyph> glyphs_logical; + }; + + struct BitmapFontData { + int height = 0; + int ascent = 0; + int charcount = 0; + const int *char_rects = nullptr; + int kerning_count = 0; + const int *kernings = nullptr; + int w = 0; + int h = 0; + const unsigned char *img = nullptr; + }; + +protected: + static void _bind_methods(); + + static Vector3 hex_code_box_font_size[2]; + static Ref<CanvasTexture> hex_code_box_font_tex[2]; + +public: + static void initialize_hex_code_box_fonts(); + static void finish_hex_code_box_fonts(); + + virtual bool has_feature(Feature p_feature) = 0; + virtual String get_name() const = 0; + + virtual void free(RID p_rid) = 0; + virtual bool has(RID p_rid) = 0; + virtual bool load_support_data(const String &p_filename) = 0; + +#ifdef TOOLS_ENABLED + virtual String get_support_data_filename() = 0; + virtual String get_support_data_info() = 0; + virtual bool save_support_data(const String &p_filename) = 0; +#endif + + virtual bool is_locale_right_to_left(const String &p_locale) = 0; + + virtual int32_t name_to_tag(const String &p_name) { return 0; }; + virtual String tag_to_name(int32_t p_tag) { return ""; }; + + /* Font interface */ + virtual RID create_font_system(const String &p_name, int p_base_size = 16) = 0; + virtual RID create_font_resource(const String &p_filename, int p_base_size = 16) = 0; + virtual RID create_font_memory(const uint8_t *p_data, size_t p_size, const String &p_type, int p_base_size = 16) = 0; + + virtual float font_get_height(RID p_font, int p_size) const = 0; + virtual float font_get_ascent(RID p_font, int p_size) const = 0; + virtual float font_get_descent(RID p_font, int p_size) const = 0; + + virtual float font_get_underline_position(RID p_font, int p_size) const = 0; + virtual float font_get_underline_thickness(RID p_font, int p_size) const = 0; + + virtual void font_set_antialiased(RID p_font, bool p_antialiased) = 0; + virtual bool font_get_antialiased(RID p_font) const = 0; + + virtual Dictionary font_get_feature_list(RID p_font) const { return Dictionary(); }; + + virtual void font_set_distance_field_hint(RID p_font, bool p_distance_field) = 0; + virtual bool font_get_distance_field_hint(RID p_font) const = 0; + + virtual void font_set_hinting(RID p_font, Hinting p_hinting) = 0; + virtual Hinting font_get_hinting(RID p_font) const = 0; + + virtual void font_set_force_autohinter(RID p_font, bool p_enabeld) = 0; + virtual bool font_get_force_autohinter(RID p_font) const = 0; + + virtual bool font_has_char(RID p_font, char32_t p_char) const = 0; + virtual String font_get_supported_chars(RID p_font) const = 0; + + virtual bool font_has_outline(RID p_font) const = 0; + virtual float font_get_base_size(RID p_font) const = 0; + + virtual bool font_is_language_supported(RID p_font, const String &p_language) const = 0; + virtual void font_set_language_support_override(RID p_font, const String &p_language, bool p_supported) = 0; + virtual bool font_get_language_support_override(RID p_font, const String &p_language) = 0; + virtual void font_remove_language_support_override(RID p_font, const String &p_language) = 0; + virtual Vector<String> font_get_language_support_overrides(RID p_font) = 0; + + virtual bool font_is_script_supported(RID p_font, const String &p_script) const = 0; + virtual void font_set_script_support_override(RID p_font, const String &p_script, bool p_supported) = 0; + virtual bool font_get_script_support_override(RID p_font, const String &p_script) = 0; + virtual void font_remove_script_support_override(RID p_font, const String &p_script) = 0; + virtual Vector<String> font_get_script_support_overrides(RID p_font) = 0; + + virtual uint32_t font_get_glyph_index(RID p_font, char32_t p_char, char32_t p_variation_selector = 0x0000) const = 0; + virtual Vector2 font_get_glyph_advance(RID p_font, uint32_t p_index, int p_size) const = 0; + virtual Vector2 font_get_glyph_kerning(RID p_font, uint32_t p_index_a, uint32_t p_index_b, int p_size) const = 0; + + virtual Vector2 font_draw_glyph(RID p_font, RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color = Color(1, 1, 1)) const = 0; + virtual Vector2 font_draw_glyph_outline(RID p_font, RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color = Color(1, 1, 1)) const = 0; + + virtual float font_get_oversampling() const = 0; + virtual void font_set_oversampling(float p_oversampling) = 0; + + Vector2 get_hex_code_box_size(int p_size, char32_t p_index) const; + void draw_hex_code_box(RID p_canvas, int p_size, const Vector2 &p_pos, char32_t p_index, const Color &p_color) const; + + virtual Vector<String> get_system_fonts() const = 0; + + /* Shaped text buffer interface */ + + virtual RID create_shaped_text(Direction p_direction = DIRECTION_AUTO, Orientation p_orientation = ORIENTATION_HORIZONTAL) = 0; + + virtual void shaped_text_clear(RID p_shaped) = 0; + + virtual void shaped_text_set_direction(RID p_shaped, Direction p_direction = DIRECTION_AUTO) = 0; + virtual Direction shaped_text_get_direction(RID p_shaped) const = 0; + + virtual void shaped_text_set_bidi_override(RID p_shaped, const Vector<Vector2i> &p_override) = 0; + + virtual void shaped_text_set_orientation(RID p_shaped, Orientation p_orientation = ORIENTATION_HORIZONTAL) = 0; + virtual Orientation shaped_text_get_orientation(RID p_shaped) const = 0; + + virtual void shaped_text_set_preserve_invalid(RID p_shaped, bool p_enabled) = 0; + virtual bool shaped_text_get_preserve_invalid(RID p_shaped) const = 0; + + virtual void shaped_text_set_preserve_control(RID p_shaped, bool p_enabled) = 0; + virtual bool shaped_text_get_preserve_control(RID p_shaped) const = 0; + + virtual bool shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "") = 0; + virtual bool shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, VAlign p_inline_align = VALIGN_CENTER, int p_length = 1) = 0; + virtual bool shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, VAlign p_inline_align = VALIGN_CENTER) = 0; + + virtual RID shaped_text_substr(RID p_shaped, int p_start, int p_length) const = 0; // Copy shaped substring (e.g. line break) without reshaping, but correctly reordered, preservers range. + virtual RID shaped_text_get_parent(RID p_shaped) const = 0; + + virtual float shaped_text_fit_to_width(RID p_shaped, float p_width, uint8_t /*JustificationFlag*/ p_jst_flags = JUSTIFICATION_WORD_BOUND | JUSTIFICATION_KASHIDA) = 0; + virtual float shaped_text_tab_align(RID p_shaped, const Vector<float> &p_tab_stops) = 0; + + virtual bool shaped_text_shape(RID p_shaped) = 0; + virtual bool shaped_text_update_breaks(RID p_shaped) = 0; + virtual bool shaped_text_update_justification_ops(RID p_shaped) = 0; + + virtual bool shaped_text_is_ready(RID p_shaped) const = 0; + + virtual Vector<Glyph> shaped_text_get_glyphs(RID p_shaped) const = 0; + + virtual Vector2i shaped_text_get_range(RID p_shaped) const = 0; + + virtual Vector<Glyph> shaped_text_sort_logical(RID p_shaped) = 0; + + virtual Vector<Vector2i> shaped_text_get_line_breaks_adv(RID p_shaped, const Vector<float> &p_width, int p_start = 0, bool p_once = true, uint8_t /*TextBreakFlag*/ p_break_flags = BREAK_MANDATORY | BREAK_WORD_BOUND) const; + virtual Vector<Vector2i> shaped_text_get_line_breaks(RID p_shaped, float p_width, int p_start = 0, uint8_t /*TextBreakFlag*/ p_break_flags = BREAK_MANDATORY | BREAK_WORD_BOUND) const; + virtual Vector<Vector2i> shaped_text_get_word_breaks(RID p_shaped) const; + virtual Array shaped_text_get_objects(RID p_shaped) const = 0; + virtual Rect2 shaped_text_get_object_rect(RID p_shaped, Variant p_key) const = 0; + + virtual Size2 shaped_text_get_size(RID p_shaped) const = 0; + virtual float shaped_text_get_ascent(RID p_shaped) const = 0; + virtual float shaped_text_get_descent(RID p_shaped) const = 0; + virtual float shaped_text_get_width(RID p_shaped) const = 0; + virtual float shaped_text_get_underline_position(RID p_shaped) const = 0; + virtual float shaped_text_get_underline_thickness(RID p_shaped) const = 0; + + virtual Direction shaped_text_get_dominant_direciton_in_range(RID p_shaped, int p_start, int p_end) const; + + virtual void shaped_text_get_carets(RID p_shaped, int p_position, Rect2 &p_leading_caret, Direction &p_leading_dir, Rect2 &p_trailing_caret, Direction &p_trailing_dir) const; + virtual Vector<Vector2> shaped_text_get_selection(RID p_shaped, int p_start, int p_end) const; + + virtual int shaped_text_hit_test_grapheme(RID p_shaped, float p_coords) const; // Return grapheme index. + virtual int shaped_text_hit_test_position(RID p_shaped, float p_coords) const; // Return caret/selection position. + + virtual int shaped_text_next_grapheme_pos(RID p_shaped, int p_pos); + virtual int shaped_text_prev_grapheme_pos(RID p_shaped, int p_pos); + + // The pen position is always placed on the baseline and moveing left to right. + virtual void shaped_text_draw(RID p_shaped, RID p_canvas, const Vector2 &p_pos, float p_clip_l = -1.f, float p_clip_r = -1.f, const Color &p_color = Color(1, 1, 1)) const; + virtual void shaped_text_draw_outline(RID p_shaped, RID p_canvas, const Vector2 &p_pos, float p_clip_l = -1.f, float p_clip_r = -1.f, int p_outline_size = 1, const Color &p_color = Color(1, 1, 1)) const; + + // Number conversion. + virtual String format_number(const String &p_string, const String &p_language = "") const { return p_string; }; + virtual String parse_number(const String &p_string, const String &p_language = "") const { return p_string; }; + virtual String percent_sign(const String &p_language = "") const { return "%"; }; + + /* GDScript wrappers */ + RID _create_font_memory(const PackedByteArray &p_data, const String &p_type, int p_base_size = 16); + + Array _shaped_text_get_glyphs(RID p_shaped) const; + Dictionary _shaped_text_get_carets(RID p_shaped, int p_position) const; + + void _shaped_text_set_bidi_override(RID p_shaped, const Array &p_override); + + Array _shaped_text_get_line_breaks_adv(RID p_shaped, const PackedFloat32Array &p_width, int p_start, bool p_once, uint8_t p_break_flags) const; + Array _shaped_text_get_line_breaks(RID p_shaped, float p_width, int p_start, uint8_t p_break_flags) const; + Array _shaped_text_get_word_breaks(RID p_shaped) const; + + Array _shaped_text_get_selection(RID p_shaped, int p_start, int p_end) const; + + TextServer(); + ~TextServer(); +}; + +/*************************************************************************/ + +class TextServerManager : public Object { + GDCLASS(TextServerManager, Object); + +public: + typedef TextServer *(*CreateFunction)(Error &r_error, void *p_user_data); + +protected: + static void _bind_methods(); + +private: + static TextServerManager *singleton; + static TextServer *server; + enum { + MAX_SERVERS = 64 + }; + + struct TextServerCreate { + String name; + CreateFunction create_function = nullptr; + uint32_t features = 0; + TextServer *instance = nullptr; + void *user_data = nullptr; + }; + + static TextServerCreate server_create_functions[MAX_SERVERS]; + static int server_create_count; + +public: + _FORCE_INLINE_ static TextServerManager *get_singleton() { + return singleton; + } + + static void register_create_function(const String &p_name, uint32_t p_features, CreateFunction p_function, void *p_user_data); + static int get_interface_count(); + static String get_interface_name(int p_index); + static uint32_t get_interface_features(int p_index); + static TextServer *initialize(int p_index, Error &r_error); + static TextServer *get_primary_interface(); + + /* GDScript wrappers */ + int _get_interface_count() const; + String _get_interface_name(int p_index) const; + uint32_t _get_interface_features(int p_index) const; + TextServer *_get_interface(int p_index) const; + Array _get_interfaces() const; + TextServer *_find_interface(const String &p_name) const; + + bool _set_primary_interface(int p_index); + TextServer *_get_primary_interface() const; + + TextServerManager(); + ~TextServerManager(); +}; + +/*************************************************************************/ + +#define TS TextServerManager::get_primary_interface() + +VARIANT_ENUM_CAST(TextServer::Direction); +VARIANT_ENUM_CAST(TextServer::Orientation); +VARIANT_ENUM_CAST(TextServer::JustificationFlag); +VARIANT_ENUM_CAST(TextServer::LineBreakFlag); +VARIANT_ENUM_CAST(TextServer::GraphemeFlag); +VARIANT_ENUM_CAST(TextServer::Hinting); +VARIANT_ENUM_CAST(TextServer::Feature); + +#endif // TEXT_SERVER_H |