summaryrefslogtreecommitdiffstats
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/text_server_adv/text_server_adv.cpp47
-rw-r--r--modules/text_server_adv/text_server_adv.h1
-rw-r--r--modules/text_server_fb/text_server_fb.cpp2
-rw-r--r--modules/webxr/doc_classes/WebXRInterface.xml24
-rw-r--r--modules/webxr/godot_webxr.h4
-rw-r--r--modules/webxr/native/library_godot_webxr.js50
-rw-r--r--modules/webxr/native/webxr.externs.js16
-rw-r--r--modules/webxr/webxr_interface.cpp4
-rw-r--r--modules/webxr/webxr_interface.h3
-rw-r--r--modules/webxr/webxr_interface_js.cpp24
-rw-r--r--modules/webxr/webxr_interface_js.h4
11 files changed, 167 insertions, 12 deletions
diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp
index 43c7f49395..dfc820050f 100644
--- a/modules/text_server_adv/text_server_adv.cpp
+++ b/modules/text_server_adv/text_server_adv.cpp
@@ -4138,6 +4138,7 @@ RID TextServerAdvanced::_shaped_text_substr(const RID &p_shaped, int64_t p_start
new_sd->direction = sd->direction;
new_sd->custom_punct = sd->custom_punct;
new_sd->para_direction = sd->para_direction;
+ new_sd->base_para_direction = sd->base_para_direction;
for (int i = 0; i < TextServer::SPACING_MAX; i++) {
new_sd->extra_spacing[i] = sd->extra_spacing[i];
}
@@ -4188,9 +4189,33 @@ bool TextServerAdvanced::_shape_substr(ShapedTextDataAdvanced *p_new_sd, const S
if (U_SUCCESS(err)) {
ubidi_setLine(p_sd->bidi_iter[ov], start, end, bidi_iter, &err);
if (U_FAILURE(err)) {
- ubidi_close(bidi_iter);
- bidi_iter = nullptr;
- ERR_PRINT(vformat("BiDi reordering for the line failed: %s", u_errorName(err)));
+ // Line BiDi failed (string contains incompatible control characters), try full paragraph BiDi instead.
+ err = U_ZERO_ERROR;
+ const UChar *data = p_sd->utf16.get_data();
+ switch (static_cast<TextServer::Direction>(p_sd->bidi_override[ov].z)) {
+ case DIRECTION_LTR: {
+ ubidi_setPara(bidi_iter, data + start, end - start, UBIDI_LTR, nullptr, &err);
+ } break;
+ case DIRECTION_RTL: {
+ ubidi_setPara(bidi_iter, data + start, end - start, UBIDI_RTL, nullptr, &err);
+ } break;
+ case DIRECTION_INHERITED: {
+ ubidi_setPara(bidi_iter, data + start, end - start, p_sd->base_para_direction, nullptr, &err);
+ } break;
+ case DIRECTION_AUTO: {
+ UBiDiDirection direction = ubidi_getBaseDirection(data + start, end - start);
+ if (direction != UBIDI_NEUTRAL) {
+ ubidi_setPara(bidi_iter, data + start, end - start, direction, nullptr, &err);
+ } else {
+ ubidi_setPara(bidi_iter, data + start, end - start, p_sd->base_para_direction, nullptr, &err);
+ }
+ } break;
+ }
+ if (U_FAILURE(err)) {
+ ubidi_close(bidi_iter);
+ bidi_iter = nullptr;
+ ERR_PRINT(vformat("BiDi reordering for the line failed: %s", u_errorName(err)));
+ }
}
} else {
bidi_iter = nullptr;
@@ -5619,25 +5644,25 @@ bool TextServerAdvanced::_shaped_text_shape(const RID &p_shaped) {
sd->script_iter = memnew(ScriptIterator(sd->text, 0, sd->text.length()));
}
- int base_para_direction = UBIDI_DEFAULT_LTR;
+ sd->base_para_direction = UBIDI_DEFAULT_LTR;
switch (sd->direction) {
case DIRECTION_LTR: {
sd->para_direction = DIRECTION_LTR;
- base_para_direction = UBIDI_LTR;
+ sd->base_para_direction = UBIDI_LTR;
} break;
case DIRECTION_RTL: {
sd->para_direction = DIRECTION_RTL;
- base_para_direction = UBIDI_RTL;
+ sd->base_para_direction = UBIDI_RTL;
} break;
case DIRECTION_INHERITED:
case DIRECTION_AUTO: {
UBiDiDirection direction = ubidi_getBaseDirection(data, sd->utf16.length());
if (direction != UBIDI_NEUTRAL) {
sd->para_direction = (direction == UBIDI_RTL) ? DIRECTION_RTL : DIRECTION_LTR;
- base_para_direction = direction;
+ sd->base_para_direction = direction;
} else {
sd->para_direction = DIRECTION_LTR;
- base_para_direction = UBIDI_DEFAULT_LTR;
+ sd->base_para_direction = UBIDI_DEFAULT_LTR;
}
} break;
}
@@ -5666,14 +5691,14 @@ bool TextServerAdvanced::_shaped_text_shape(const RID &p_shaped) {
ubidi_setPara(bidi_iter, data + start, end - start, UBIDI_RTL, nullptr, &err);
} break;
case DIRECTION_INHERITED: {
- ubidi_setPara(bidi_iter, data + start, end - start, base_para_direction, nullptr, &err);
+ ubidi_setPara(bidi_iter, data + start, end - start, sd->base_para_direction, nullptr, &err);
} break;
case DIRECTION_AUTO: {
UBiDiDirection direction = ubidi_getBaseDirection(data + start, end - start);
if (direction != UBIDI_NEUTRAL) {
ubidi_setPara(bidi_iter, data + start, end - start, direction, nullptr, &err);
} else {
- ubidi_setPara(bidi_iter, data + start, end - start, base_para_direction, nullptr, &err);
+ ubidi_setPara(bidi_iter, data + start, end - start, sd->base_para_direction, nullptr, &err);
}
} break;
}
@@ -5760,7 +5785,7 @@ bool TextServerAdvanced::_shaped_text_shape(const RID &p_shaped) {
gl.start = span.start;
gl.end = span.end;
gl.count = 1;
- gl.flags = GRAPHEME_IS_VALID | GRAPHEME_IS_VIRTUAL;
+ gl.flags = GRAPHEME_IS_VALID | GRAPHEME_IS_EMBEDDED_OBJECT;
if (sd->orientation == ORIENTATION_HORIZONTAL) {
gl.advance = sd->objects[span.embedded_key].rect.size.x;
} else {
diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h
index af3a0c7876..4b8c3f7cd3 100644
--- a/modules/text_server_adv/text_server_adv.h
+++ b/modules/text_server_adv/text_server_adv.h
@@ -476,6 +476,7 @@ class TextServerAdvanced : public TextServerExtension {
/* Shaped data */
TextServer::Direction para_direction = DIRECTION_LTR; // Detected text direction.
+ int base_para_direction = UBIDI_DEFAULT_LTR;
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.
diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp
index 9095be46af..8b210bdc38 100644
--- a/modules/text_server_fb/text_server_fb.cpp
+++ b/modules/text_server_fb/text_server_fb.cpp
@@ -3670,7 +3670,7 @@ bool TextServerFallback::_shaped_text_shape(const RID &p_shaped) {
gl.end = span.end;
gl.count = 1;
gl.index = 0;
- gl.flags = GRAPHEME_IS_VALID | GRAPHEME_IS_VIRTUAL;
+ gl.flags = GRAPHEME_IS_VALID | GRAPHEME_IS_EMBEDDED_OBJECT;
if (sd->orientation == ORIENTATION_HORIZONTAL) {
gl.advance = sd->objects[span.embedded_key].rect.size.x;
} else {
diff --git a/modules/webxr/doc_classes/WebXRInterface.xml b/modules/webxr/doc_classes/WebXRInterface.xml
index 758c37e2ad..ed162b1da2 100644
--- a/modules/webxr/doc_classes/WebXRInterface.xml
+++ b/modules/webxr/doc_classes/WebXRInterface.xml
@@ -93,6 +93,18 @@
<link title="How to make a VR game for WebXR with Godot 4">https://www.snopekgames.com/tutorial/2023/how-make-vr-game-webxr-godot-4</link>
</tutorials>
<methods>
+ <method name="get_available_display_refresh_rates" qualifiers="const">
+ <return type="Array" />
+ <description>
+ Returns display refresh rates supported by the current HMD. Only returned if this feature is supported by the web browser and after the interface has been initialized.
+ </description>
+ </method>
+ <method name="get_display_refresh_rate" qualifiers="const">
+ <return type="float" />
+ <description>
+ Returns the display refresh rate for the current HMD. Not supported on all HMDs and browsers. It may not report an accurate value until after using [method set_display_refresh_rate].
+ </description>
+ </method>
<method name="get_input_source_target_ray_mode" qualifiers="const">
<return type="int" enum="WebXRInterface.TargetRayMode" />
<param index="0" name="input_source_id" type="int" />
@@ -132,6 +144,13 @@
This method returns nothing, instead it emits the [signal session_supported] signal with the result.
</description>
</method>
+ <method name="set_display_refresh_rate">
+ <return type="void" />
+ <param index="0" name="refresh_rate" type="float" />
+ <description>
+ Sets the display refresh rate for the current HMD. Not supported on all HMDs and browsers. It won't take effect right away until after [signal display_refresh_rate_changed] is emitted.
+ </description>
+ </method>
</methods>
<members>
<member name="optional_features" type="String" setter="set_optional_features" getter="get_optional_features">
@@ -167,6 +186,11 @@
</member>
</members>
<signals>
+ <signal name="display_refresh_rate_changed">
+ <description>
+ Emitted after the display's refresh rate has changed.
+ </description>
+ </signal>
<signal name="reference_space_reset">
<description>
Emitted to indicate that the reference space has been reset or reconfigured.
diff --git a/modules/webxr/godot_webxr.h b/modules/webxr/godot_webxr.h
index c636be1576..554ca0398b 100644
--- a/modules/webxr/godot_webxr.h
+++ b/modules/webxr/godot_webxr.h
@@ -90,6 +90,10 @@ extern bool godot_webxr_update_input_source(
extern char *godot_webxr_get_visibility_state();
extern int godot_webxr_get_bounds_geometry(float **r_points);
+extern float godot_webxr_get_frame_rate();
+extern void godot_webxr_update_target_frame_rate(float p_frame_rate);
+extern int godot_webxr_get_supported_frame_rates(float **r_frame_rates);
+
#ifdef __cplusplus
}
#endif
diff --git a/modules/webxr/native/library_godot_webxr.js b/modules/webxr/native/library_godot_webxr.js
index 5c01d88a30..fe47de02b0 100644
--- a/modules/webxr/native/library_godot_webxr.js
+++ b/modules/webxr/native/library_godot_webxr.js
@@ -42,6 +42,7 @@ const GodotWebXR = {
view_count: 1,
input_sources: new Array(16),
touches: new Array(5),
+ onsimpleevent: null,
// Monkey-patch the requestAnimationFrame() used by Emscripten for the main
// loop, so that we can swap it out for XRSession.requestAnimationFrame()
@@ -283,6 +284,9 @@ const GodotWebXR = {
GodotRuntime.free(c_str);
});
+ // Store onsimpleevent so we can use it later.
+ GodotWebXR.onsimpleevent = onsimpleevent;
+
const gl_context_handle = _emscripten_webgl_get_current_context(); // eslint-disable-line no-undef
const gl = GL.getContext(gl_context_handle).GLctx;
GodotWebXR.gl = gl;
@@ -368,6 +372,7 @@ const GodotWebXR = {
GodotWebXR.view_count = 1;
GodotWebXR.input_sources = new Array(16);
GodotWebXR.touches = new Array(5);
+ GodotWebXR.onsimpleevent = null;
// Disable the monkey-patched window.requestAnimationFrame() and
// pause/restart the main loop to activate it on all platforms.
@@ -594,6 +599,51 @@ const GodotWebXR = {
return point_count;
},
+
+ godot_webxr_get_frame_rate__proxy: 'sync',
+ godot_webxr_get_frame_rate__sig: 'i',
+ godot_webxr_get_frame_rate: function () {
+ if (!GodotWebXR.session || GodotWebXR.session.frameRate === undefined) {
+ return 0;
+ }
+ return GodotWebXR.session.frameRate;
+ },
+
+ godot_webxr_update_target_frame_rate__proxy: 'sync',
+ godot_webxr_update_target_frame_rate__sig: 'vi',
+ godot_webxr_update_target_frame_rate: function (p_frame_rate) {
+ if (!GodotWebXR.session || GodotWebXR.session.updateTargetFrameRate === undefined) {
+ return;
+ }
+
+ GodotWebXR.session.updateTargetFrameRate(p_frame_rate).then(() => {
+ const c_str = GodotRuntime.allocString('display_refresh_rate_changed');
+ GodotWebXR.onsimpleevent(c_str);
+ GodotRuntime.free(c_str);
+ });
+ },
+
+ godot_webxr_get_supported_frame_rates__proxy: 'sync',
+ godot_webxr_get_supported_frame_rates__sig: 'ii',
+ godot_webxr_get_supported_frame_rates: function (r_frame_rates) {
+ if (!GodotWebXR.session || GodotWebXR.session.supportedFrameRates === undefined) {
+ return 0;
+ }
+
+ const frame_rate_count = GodotWebXR.session.supportedFrameRates.length;
+ if (frame_rate_count === 0) {
+ return 0;
+ }
+
+ const buf = GodotRuntime.malloc(frame_rate_count * 4);
+ for (let i = 0; i < frame_rate_count; i++) {
+ GodotRuntime.setHeapValue(buf + (i * 4), GodotWebXR.session.supportedFrameRates[i], 'float');
+ }
+ GodotRuntime.setHeapValue(r_frame_rates, buf, 'i32');
+
+ return frame_rate_count;
+ },
+
};
autoAddDeps(GodotWebXR, '$GodotWebXR');
diff --git a/modules/webxr/native/webxr.externs.js b/modules/webxr/native/webxr.externs.js
index 4b88820b19..7f7c297acc 100644
--- a/modules/webxr/native/webxr.externs.js
+++ b/modules/webxr/native/webxr.externs.js
@@ -68,6 +68,16 @@ XRSession.prototype.inputSources;
XRSession.prototype.visibilityState;
/**
+ * @type {?number}
+ */
+XRSession.prototype.frameRate;
+
+/**
+ * @type {?Float32Array}
+ */
+XRSession.prototype.supportedFrameRates;
+
+/**
* @type {?function (Event)}
*/
XRSession.prototype.onend;
@@ -142,6 +152,12 @@ XRSession.prototype.end = function () {};
XRSession.prototype.requestReferenceSpace = function (referenceSpaceType) {};
/**
+ * @param {number} rate
+ * @return {Promise<undefined>}
+ */
+XRSession.prototype.updateTargetFrameRate = function (rate) {};
+
+/**
* @typedef {function(number, XRFrame): undefined}
*/
var XRFrameRequestCallback;
diff --git a/modules/webxr/webxr_interface.cpp b/modules/webxr/webxr_interface.cpp
index d7f4247768..5e30d0c996 100644
--- a/modules/webxr/webxr_interface.cpp
+++ b/modules/webxr/webxr_interface.cpp
@@ -46,6 +46,9 @@ void WebXRInterface::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_input_source_tracker", "input_source_id"), &WebXRInterface::get_input_source_tracker);
ClassDB::bind_method(D_METHOD("get_input_source_target_ray_mode", "input_source_id"), &WebXRInterface::get_input_source_target_ray_mode);
ClassDB::bind_method(D_METHOD("get_visibility_state"), &WebXRInterface::get_visibility_state);
+ ClassDB::bind_method(D_METHOD("get_display_refresh_rate"), &WebXRInterface::get_display_refresh_rate);
+ ClassDB::bind_method(D_METHOD("set_display_refresh_rate", "refresh_rate"), &WebXRInterface::set_display_refresh_rate);
+ ClassDB::bind_method(D_METHOD("get_available_display_refresh_rates"), &WebXRInterface::get_available_display_refresh_rates);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "session_mode", PROPERTY_HINT_NONE), "set_session_mode", "get_session_mode");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "required_features", PROPERTY_HINT_NONE), "set_required_features", "get_required_features");
@@ -68,6 +71,7 @@ void WebXRInterface::_bind_methods() {
ADD_SIGNAL(MethodInfo("visibility_state_changed"));
ADD_SIGNAL(MethodInfo("reference_space_reset"));
+ ADD_SIGNAL(MethodInfo("display_refresh_rate_changed"));
BIND_ENUM_CONSTANT(TARGET_RAY_MODE_UNKNOWN);
BIND_ENUM_CONSTANT(TARGET_RAY_MODE_GAZE);
diff --git a/modules/webxr/webxr_interface.h b/modules/webxr/webxr_interface.h
index f18b4b40e6..abaa8c01f8 100644
--- a/modules/webxr/webxr_interface.h
+++ b/modules/webxr/webxr_interface.h
@@ -66,6 +66,9 @@ public:
virtual Ref<XRPositionalTracker> get_input_source_tracker(int p_input_source_id) const = 0;
virtual TargetRayMode get_input_source_target_ray_mode(int p_input_source_id) const = 0;
virtual String get_visibility_state() const = 0;
+ virtual float get_display_refresh_rate() const = 0;
+ virtual void set_display_refresh_rate(float p_refresh_rate) = 0;
+ virtual Array get_available_display_refresh_rates() const = 0;
};
VARIANT_ENUM_CAST(WebXRInterface::TargetRayMode);
diff --git a/modules/webxr/webxr_interface_js.cpp b/modules/webxr/webxr_interface_js.cpp
index d3710bd0df..e1df2ea94e 100644
--- a/modules/webxr/webxr_interface_js.cpp
+++ b/modules/webxr/webxr_interface_js.cpp
@@ -202,6 +202,30 @@ PackedVector3Array WebXRInterfaceJS::get_play_area() const {
return ret;
}
+float WebXRInterfaceJS::get_display_refresh_rate() const {
+ return godot_webxr_get_frame_rate();
+}
+
+void WebXRInterfaceJS::set_display_refresh_rate(float p_refresh_rate) {
+ godot_webxr_update_target_frame_rate(p_refresh_rate);
+}
+
+Array WebXRInterfaceJS::get_available_display_refresh_rates() const {
+ Array ret;
+
+ float *rates;
+ int rate_count = godot_webxr_get_supported_frame_rates(&rates);
+ if (rate_count > 0) {
+ ret.resize(rate_count);
+ for (int i = 0; i < rate_count; i++) {
+ ret[i] = rates[i];
+ }
+ free(rates);
+ }
+
+ return ret;
+}
+
StringName WebXRInterfaceJS::get_name() const {
return "WebXR";
};
diff --git a/modules/webxr/webxr_interface_js.h b/modules/webxr/webxr_interface_js.h
index 20516e89e2..d85eb8cad7 100644
--- a/modules/webxr/webxr_interface_js.h
+++ b/modules/webxr/webxr_interface_js.h
@@ -102,6 +102,10 @@ public:
virtual String get_visibility_state() const override;
virtual PackedVector3Array get_play_area() const override;
+ virtual float get_display_refresh_rate() const override;
+ virtual void set_display_refresh_rate(float p_refresh_rate) override;
+ virtual Array get_available_display_refresh_rates() const override;
+
virtual StringName get_name() const override;
virtual uint32_t get_capabilities() const override;