diff --git a/addons/keh_general/data/quantize.gd b/addons/keh_general/data/quantize.gd new file mode 100644 index 0000000..b2ee140 --- /dev/null +++ b/addons/keh_general/data/quantize.gd @@ -0,0 +1,264 @@ +############################################################################### +# Copyright (c) 2019-2020 Yuri Sarudiansky +# +# 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. +############################################################################### + +# The floating point quantization code was adapted. The original code was taken from +# the Game Engine Architecture book by Jason Gregory + +extends Reference +class_name Quantize + +const ROTATION_BOUNDS: float = 0.707107 + +# Define some masks to help pack/unpack quantized rotation quaternions into integers +const MASK_A_9BIT: int = 511 # 511 = 111111111 +const MASK_B_9BIT: int = 511 << 9 +const MASK_C_9BIT: int = 511 << 18 +const MASK_INDEX_9BIT: int = 3 << 27 +const MASK_SIGNAL_9BIT: int = 1 << 30 + + +const MASK_A_10BIT: int = 1023 # 1023 = 1111111111 +const MASK_B_10BIT: int = 1023 << 10 +const MASK_C_10BIT: int = 1023 << 20 +const MASK_INDEX_10BIT: int = 3 << 30 + +const MASK_A_15BIT: int = 32767 +const MASK_B_15BIT: int = 32767 << 15 +# The C component is packed into a secondary integer but having dedicated +# constant may help reduce confusion when reading the code +const MASK_C_15BIT: int = 32767 +const MASK_INDEX_15BIT: int = 3 << 30 +# When packing compressed quaternion data using 15 bits per component, one +# bit becomes "wasted". While not entirely necessary, the system here uses +# that bit to restore the signals of the original quaternion in case those +# got flipped because the largest component were negative. +const MASK_SIGNAL_15BIT: int = 1 << 15 + + +# Quantize a unit float (range [0..1]) into an integer of the specified number of bits) +static func quantize_unit_float(val: float, numbits: int) -> int: + # Number of bits cannot exceed 32 bits + assert(numbits <= 32 && numbits > 0) + + var intervals: int = 1 << numbits + var scaled: float = val * (intervals - 1) + var rounded: int = int(floor(scaled + 0.5)) + + if (rounded > intervals - 1): + rounded = intervals - 1 + + return rounded + +static func restore_unit_float(quantized: int, numbits: int) -> float: + assert(numbits <= 32 && numbits > 0) + + var intervals: int = 1 << numbits + var intervalsize: float = 1.0 / (intervals - 1) + var approxfloat: float = float(quantized) * intervalsize + + + return approxfloat + +# Quantize a float in the range [minval..maxval] +static func quantize_float(val: float, minval: float, maxval: float, numbits: int) -> int: + var unitfloat: float = (val - minval) / (maxval - minval) + var quantized: int = quantize_unit_float(unitfloat, numbits) + + return quantized + +# Restore float in arbitrary range +static func restore_float(quantized: int, minval: float, maxval: float, numbits: int) -> float: + var unitfloat: float = restore_unit_float(quantized, numbits) + var val: float = minval + (unitfloat * (maxval - minval)) + + return val + + +# Compress the given rotation quaternion using the specified number of bits per component +# using the smallest three method. The returned dictionary contains 5 fields: +# a, b, c -> the smallest three quantized components +# index -> the index [0..3] of the dropped (largest) component +# sig -> The "signal" of the dropped component (1.0 if >= 0, -1.0 if negative) +# Note: Signal is not exactly necessary, but is provided just so if there is any desire to encode it +static func compress_rotation_quat(q: Quat, numbits: int) -> Dictionary: + # Unfortunately it's not possible to directly iterate through the quaternion's components + # using a loop, so create a temporary array to store them + var aq: Array = [q.x, q.y, q.z, q.w] + var mindex: int = 0 # Index of largest component + var mval: float = -1.0 # Largest component value + var sig: float = 1.0 # "Signal" of the dropped component + + # Locate the largest component, storing its absolute value as well as the index + # (0 = x, 1 = y, 2 = z and 3 = w) + for i in 4: + var abval: float = abs(aq[i]) + + if (abval > mval): + mval = abval + mindex = i + + if (aq[mindex] < 0.0): + sig = -1.0 + + # Drop the largest component + aq.erase(aq[mindex]) + + # Now loop again through the components, quantizing them + for i in 3: + var fl: float = aq[i] * sig + aq[i] = quantize_float(fl, -ROTATION_BOUNDS, ROTATION_BOUNDS, numbits) + + + return { + "a": aq[0], + "b": aq[1], + "c": aq[2], + "index": mindex, + "sig": 1 if (sig == 1.0) else 0, + } + + +# Restore the rotation quaternion. The quantized values must be given in a dictionary with +# the same format of the one returned by the compress_rotation_quat() function. +static func restore_rotation_quat(quant: Dictionary, numbits: int) -> Quat: + # Take the signal (just a multiplier) + var sig: float = 1.0 if quant.sig == 1 else -1.0 + + # Restore components a, b and c + var ra: float = restore_float(quant.a, -ROTATION_BOUNDS, ROTATION_BOUNDS, numbits) * sig + var rb: float = restore_float(quant.b, -ROTATION_BOUNDS, ROTATION_BOUNDS, numbits) * sig + var rc: float = restore_float(quant.c, -ROTATION_BOUNDS, ROTATION_BOUNDS, numbits) * sig + # Restore the dropped component + var dropped: float = sqrt(1.0 - ra*ra - rb*rb - rc*rc) * sig + + var ret: Quat = Quat() + + match quant.index: + 0: + # X was dropped + ret = Quat(dropped, ra, rb, rc) + + 1: + # Y was dropped + ret = Quat(ra, dropped, rb, rc) + + 2: + # Z was dropped + ret = Quat(ra, rb, dropped, rc) + + 3: + # W was dropped + ret = Quat(ra, rb, rc, dropped) + + return ret + + + +# Compress the given rotation quaternion using 9 bits per component. This is a "wrapper" +# function that packs the quantized value into a single integer. Because there is still +# some "room" (only 29 bits of the 32 are used), the original signal of the quaternion is +# also stored, meaning that it can be restored. +static func compress_rquat_9bits(q: Quat) -> int: + # Compress the components using the generalized Quat compression + var c: Dictionary = compress_rotation_quat(q, 9) + return ( ((c.sig << 30) & MASK_SIGNAL_9BIT) | + ((c.index << 27) & MASK_INDEX_9BIT) | + ((c.c << 18) & MASK_C_9BIT) | + ((c.b << 9) & MASK_B_9BIT) | + (c.a & MASK_A_9BIT) ) + +# Restores a quaternion that was previously quantized into a single integer using 9 bits +# per component. In this case the original signal of the quaternion will be restored. +static func restore_rquat_9bits(compressed: int) -> Quat: + var unpacked: Dictionary = { + "a": compressed & MASK_A_9BIT, + "b": (compressed & MASK_B_9BIT) >> 9, + "c": (compressed & MASK_C_9BIT) >> 18, + "index": (compressed & MASK_INDEX_9BIT) >> 27, + "sig": (compressed & MASK_SIGNAL_9BIT) >> 30, + } + + return restore_rotation_quat(unpacked, 9) + + +# Compress the given rotation quaternion using 10 bits per component. This is a "wrapper" +# function that packs the quantized values into a single integer. Note that in this case +# the restored quaternion may be entirely "flipped" as the original signal cannot be +# stored within the packed integer. +static func compress_rquat_10bits(q: Quat) -> int: + # Compress the components using the generalized function + var c: Dictionary = compress_rotation_quat(q, 10) + return ( ((c.index << 30) & MASK_INDEX_10BIT) | + ((c.c << 20) & MASK_C_10BIT) | + ((c.b << 10) & MASK_B_10BIT) | + (c.a & MASK_A_10BIT) ) + +# Restores a quaternion that was previously quantized into a single integer using 10 bits +# per component. In this case the original signal may not be restored. +static func restore_rquat_10bits(c: int) -> Quat: + # Unpack the components + var unpacked: Dictionary = { + "a": c & MASK_A_10BIT, + "b": (c & MASK_B_10BIT) >> 10, + "c": (c & MASK_C_10BIT) >> 20, + "index": (c & MASK_INDEX_10BIT) >> 30, + "sig": 1, # Use 1.0 as multiplier because the signal cannot be restored in this case + } + + return restore_rotation_quat(unpacked, 10) + + +# Compress the given rotation quaternion using 15 bits per component. This is a "wrapper" +# function that packs the quantized values into two intergers (using PoolIntArray). In +# memory thsi will still use the full range of the integer values, but the second entry in +# the returned array can safely discard 16 bits, which is basically the desired usage when +# sending data through network. Note that in this case, using a full 32 bit + 16 bit leaves +# room for a single bit, which is used to encode the original quaternion signal. +static func compress_rquat_15bits(q: Quat) -> PoolIntArray: + # Obtain the compressed data + var c: Dictionary = compress_rotation_quat(q, 15) + + # Pack the first element of the array - contains index, A and B elements + var packed0: int = ( ((c.index << 30) & MASK_INDEX_15BIT) | + ((c.b << 15) & MASK_B_15BIT) | + (c.a & MASK_A_15BIT) ) + + # Pack the second element of the array - contains signal and C element + var packed1: int = (((c.sig & MASK_SIGNAL_15BIT) << 15) | (c.c & MASK_C_15BIT)) + + return PoolIntArray([packed0, packed1]) + +# Restores a quaternion compressed using 15 bits per component. The input must be integers +# within the PoolIntArray of the compression function, in the same order for the arguments. +static func restore_rquat_15bits(pack0: int, pack1: int) -> Quat: + # Unpack the elements + var unpacked: Dictionary = { + "a": pack0 & MASK_A_15BIT, + "b": (pack0 & MASK_B_15BIT) >> 15, + "c": pack1 & MASK_C_15BIT, + "index": (pack0 & MASK_INDEX_15BIT) >> 30, + "sig": (pack1 & MASK_SIGNAL_15BIT) >> 15 + } + + return restore_rotation_quat(unpacked, 15) + + diff --git a/changelog.md b/changelog.md index b064dc3..e65d777 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,8 @@ Some smaller commits related to minor fixes (specially comment corrections) are not going to be listed here. +#### 2020 May 20 +* Added a new "sub-addon" into the General addon directory, `quantize.gd`. It provides means to quantize floating point numbers as well as compression of rotation quaternions using the smallest three method. Tutorial (http://kehomsforge.com/tutorials/multi/GodotAddonPack) has been updated. + #### 2020 May 15 * (Network) Replicated floating point numbers (even compound ones like Vector2, Vector3 etc) can use tolerance to compare them. Tutorial (http://kehomsforge.com/tutorials/multi/GodotAddonPack) has been updated to show how to use this (topic `Floating Point Comparison`) diff --git a/demos/general/quantize_checkgroup.tres b/demos/general/quantize_checkgroup.tres new file mode 100644 index 0000000..0e55d74 --- /dev/null +++ b/demos/general/quantize_checkgroup.tres @@ -0,0 +1,3 @@ +[gd_resource type="ButtonGroup" format=2] + +[resource] diff --git a/demos/general/quantizedemo.gd b/demos/general/quantizedemo.gd new file mode 100644 index 0000000..5ded1a8 --- /dev/null +++ b/demos/general/quantizedemo.gd @@ -0,0 +1,140 @@ +# This demo is meant to focus on the keh_general/data/quantize.gd addon +# The idea here is to use quaternion quantization to coompress it using the smallest three +# method, which basically drops the largest component and quantize the rest of them into +# an smaller number of bits. This method only works for rotation quaternions. +# To test things, a cube is used to "generate" the orientation, which will then be +# compressed and immediately uncompressed to be applied into a secondary cube +# +# Important note: In the code bellow, two orientations are "replicated" to the "secondary" +# cube. One for the "pivot" point and the other is the cube itself. This is done because +# part of the motion performed here is done directly into the pivot point while the other +# part is on the cube. The idea here is to deal only with the orientations and "forget" the +# translation. However, it is possible to deal with the global orientation of the cubes in +# order to avoid having to deal with two compression/decompression operations. In that case +# the translation has be taken into account. To keep the separation of "left cube" and +# "right cube" the X coordinate of the right cube (the replicated one) must be flipped in +# comparison to the one from the left cube. + +extends Spatial + +var pivot_yrotation: float = -1.5 # Negative value for clockwise rotation +var pivot_zrotation: float = 45.0 + +var cube_xrotation: float = 2.0 + + +var _pivot_zstate: float = 0.0 +var _pivot_zdir: float = 1.0 + +func _ready() -> void: + _setup_hud() + + + +func _physics_process(dt: float) -> void: + _pivot_zstate += pivot_zrotation * dt * _pivot_zdir + + if (_pivot_zstate >= 45.0): + _pivot_zdir = -1.0 + elif (_pivot_zstate <= -45.0): + _pivot_zdir = 1.0 + + $source.rotation.y += pivot_yrotation * dt + $source.rotation.z = deg2rad(_pivot_zstate) + + $source/cube.rotation.x += cube_xrotation * dt + + if ($hud/pnl/chk_9bits.is_pressed()): + simulate_replication_9bits() + elif ($hud/pnl/chk_10bits.is_pressed()): + simulate_replication_10bits() + elif ($hud/pnl/chk_15bits.is_pressed()): + simulate_replication_15bits() + + +func simulate_replication_9bits() -> void: + # First replicate the pivot orientation + var pivq: Quat = Quat($source.transform.basis) + # Quantize it using 9 bits per component + var pivc: int = Quantize.compress_rquat_9bits(pivq) + # Restore the quaternion (as it would be done on a remote machine, for example) + var restpivq: Quat = Quantize.restore_rquat_9bits(pivc) + # Apply to the replicated pivot + $replicated.transform.basis = Basis(restpivq) + + # Take the source cube orientation + var srccubeq: Quat = Quat($source/cube.transform.basis) + # Quantize it using 9 bits per component + var srccubec: int = Quantize.compress_rquat_9bits(srccubeq) + # Restore the quaternion (as it would be done on a remote machine, for example) + var restcubeq: Quat = Quantize.restore_rquat_9bits(srccubec) + # Apply the restored orientantion to the "replicated" cube + $replicated/cube.transform.basis = Basis(restcubeq) + + +func simulate_replication_10bits() -> void: + # First replicate the pivot orientation + var pivq: Quat = Quat($source.transform.basis) + # Quantize it using 9 bits per component + var pivc: int = Quantize.compress_rquat_10bits(pivq) + # Restore the quaternion (as it would be done on a remote machine, for example) + var restpivq: Quat = Quantize.restore_rquat_10bits(pivc) + # Apply to the replicated pivot + $replicated.transform.basis = Basis(restpivq) + + + # Take the source cube orientation + var srccubeq: Quat = Quat($source/cube.transform.basis) + # Quantize it using 10 bits per component + var srccubc: int = Quantize.compress_rquat_10bits(srccubeq) + # Restore the quaternion (as it would be done on a remote machine, for example) + var restcubeq: Quat = Quantize.restore_rquat_10bits(srccubc) + # Apply the restored orientation to the replicated cube + $replicated/cube.transform.basis = Basis(restcubeq) + + +func simulate_replication_15bits() -> void: + # First replicate the pivot orientation + var pivq: Quat = Quat($source.transform.basis) + # Quantize it using 9 bits per component + var pivc: PoolIntArray = Quantize.compress_rquat_15bits(pivq) + # Restore the quaternion (as it would be done on a remote machine, for example) + var restpivq: Quat = Quantize.restore_rquat_15bits(pivc[0], pivc[1]) + # Apply to the replicated pivot + $replicated.transform.basis = Basis(restpivq) + + # Take the source cube orientation + var srccubeq: Quat = Quat($source/cube.transform.basis) + # Quantize it using 15 bits per component + var srccubec: PoolIntArray = Quantize.compress_rquat_15bits(srccubeq) + # Restore the quaternion (as it would be done on a remote machine, for example) + var restcubeq: Quat = Quantize.restore_rquat_15bits(srccubec[0], srccubec[1]) + # Apply the restored orientation to the replicated cube + $replicated/cube.transform.basis = Basis(restcubeq) + + + +func _setup_hud() -> void: + $hud/pnl/sl_pivyrot.value = pivot_yrotation + $hud/pnl/sl_pivzrot.value = pivot_zrotation + $hud/pnl/sl_cubexrot.value = cube_xrotation + + SharedUtils.connector($hud/pnl/sl_pivyrot, "value_changed", self, "_on_pivoty_changed") + SharedUtils.connector($hud/pnl/sl_pivzrot, "value_changed", self, "_on_pivotz_changed") + SharedUtils.connector($hud/pnl/sl_cubexrot, "value_changed", self, "_on_cubex_changed") + + +func _on_pivoty_changed(val: float) -> void: + pivot_yrotation = val + +func _on_pivotz_changed(val: float) -> void: + pivot_zrotation = val + +func _on_cubex_changed(val: float) -> void: + cube_xrotation = val + + +func _on_bt_back_pressed() -> void: + # warning-ignore:return_value_discarded + get_tree().change_scene("res://main.tscn") + diff --git a/demos/general/quantizedemo.tscn b/demos/general/quantizedemo.tscn new file mode 100644 index 0000000..98c4cb6 --- /dev/null +++ b/demos/general/quantizedemo.tscn @@ -0,0 +1,164 @@ +[gd_scene load_steps=6 format=2] + +[ext_resource path="res://demos/general/quantize_checkgroup.tres" type="ButtonGroup" id=1] +[ext_resource path="res://demos/general/quantizedemo.gd" type="Script" id=2] + +[sub_resource type="CubeMesh" id=1] + +[sub_resource type="SpatialMaterial" id=2] +albedo_color = Color( 0.258824, 0.309804, 0.694118, 1 ) + +[sub_resource type="CubeMesh" id=3] +material = SubResource( 2 ) + +[node name="quantmain" type="Spatial"] +script = ExtResource( 2 ) + +[node name="hud" type="CanvasLayer" parent="."] + +[node name="pnl" type="Panel" parent="hud"] +self_modulate = Color( 1, 1, 1, 0.529412 ) +anchor_bottom = 1.0 +margin_right = 209.0 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="lbl_pivot" type="Label" parent="hud/pnl"] +margin_left = 8.0 +margin_top = 8.0 +margin_right = 48.0 +margin_bottom = 22.0 +text = "Pivot" + +[node name="lbl_yrotation" type="Label" parent="hud/pnl"] +margin_left = 8.0 +margin_top = 24.0 +margin_right = 48.0 +margin_bottom = 38.0 +text = "Y Rotation" +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="sl_pivyrot" type="HSlider" parent="hud/pnl"] +margin_left = 16.0 +margin_top = 40.0 +margin_right = 192.0 +margin_bottom = 56.0 +min_value = -3.0 +max_value = 3.0 +step = 0.1 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="lbl_zrotation" type="Label" parent="hud/pnl"] +margin_left = 8.0 +margin_top = 64.0 +margin_right = 72.0 +margin_bottom = 78.0 +text = "Z Rotation" +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="sl_pivzrot" type="HSlider" parent="hud/pnl"] +margin_left = 16.0 +margin_top = 80.0 +margin_right = 192.0 +margin_bottom = 96.0 +max_value = 70.0 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="lbl_cube" type="Label" parent="hud/pnl"] +margin_left = 8.0 +margin_top = 112.0 +margin_right = 48.0 +margin_bottom = 126.0 +text = "Cube X Rotation" +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="sl_cubexrot" type="HSlider" parent="hud/pnl"] +margin_left = 16.0 +margin_top = 128.0 +margin_right = 192.0 +margin_bottom = 144.0 +min_value = -3.5 +max_value = 3.5 +step = 0.1 + +[node name="lbl_bitspercomp" type="Label" parent="hud/pnl"] +margin_left = 8.0 +margin_top = 176.0 +margin_right = 48.0 +margin_bottom = 190.0 +text = "Bits per Quat component:" + +[node name="chk_9bits" type="CheckBox" parent="hud/pnl"] +margin_left = 16.0 +margin_top = 192.0 +margin_right = 80.0 +margin_bottom = 216.0 +pressed = true +group = ExtResource( 1 ) +text = "9 bits" +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="chk_10bits" type="CheckBox" parent="hud/pnl"] +margin_left = 16.0 +margin_top = 216.0 +margin_right = 80.0 +margin_bottom = 240.0 +group = ExtResource( 1 ) +text = "10 bits" +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="chk_15bits" type="CheckBox" parent="hud/pnl"] +margin_left = 16.0 +margin_top = 240.0 +margin_right = 88.0 +margin_bottom = 264.0 +group = ExtResource( 1 ) +text = "15 bits" +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="bt_back" type="Button" parent="hud/pnl"] +margin_left = 8.0 +margin_top = 568.0 +margin_right = 88.0 +margin_bottom = 588.0 +text = "Back" +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="source" type="Spatial" parent="."] +transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -3, 0, 0 ) + +[node name="cube" type="MeshInstance" parent="source"] +transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -1.5, 0, 0 ) +mesh = SubResource( 1 ) +material/0 = null + +[node name="replicated" type="Spatial" parent="."] +transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 3, 0, 0 ) + +[node name="cube" type="MeshInstance" parent="replicated"] +transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -1.5, 0, 0 ) +mesh = SubResource( 3 ) +material/0 = null + +[node name="cam" type="Camera" parent="."] +transform = Transform( 1, 0, 0, 0, 0.756801, 0.653645, 0, -0.653645, 0.756801, 0, 7.3352, 10.6415 ) +[connection signal="pressed" from="hud/pnl/bt_back" to="." method="_on_bt_back_pressed"] diff --git a/demos/general/utilities.tscn b/demos/general/utilities.tscn deleted file mode 100644 index b171e8d..0000000 --- a/demos/general/utilities.tscn +++ /dev/null @@ -1,3 +0,0 @@ -[gd_scene format=2] - -[node name="Node2D" type="Node2D"] diff --git a/main.gd b/main.gd index 9490e30..8931be0 100644 --- a/main.gd +++ b/main.gd @@ -19,7 +19,7 @@ func _ready() -> void: # Connect the "toggled" signal on all of the "demo buttons", binding the tab index as payload set_tab_button("bt_encdec", "encdecbuffer") - set_tab_button("bt_utils", "utilities") + set_tab_button("bt_quantize", "quantize") set_tab_button("bt_network", "network") set_tab_button("bt_cam3d", "cam3d") set_tab_button("bt_fancyle", "fancyle") @@ -145,9 +145,9 @@ func _on_bt_encdecload_pressed() -> void: open_scene("res://demos/general/edbuffer.tscn") -### The utilities demo +### The quantize (previously named utilities) demo func _on_bt_utilsload_pressed() -> void: - open_scene("res://demos/general/utilities.tscn") + open_scene("res://demos/general/quantizedemo.tscn") ### Cam3D demo diff --git a/main.tscn b/main.tscn index ef26d6c..e4b35c0 100644 --- a/main.tscn +++ b/main.tscn @@ -63,56 +63,55 @@ pressed = true group = ExtResource( 2 ) text = "EncDecBuffer" -[node name="bt_utils" type="Button" parent="mpnl/demo_list/pnl/vbox"] -visible = false +[node name="bt_quantize" type="Button" parent="mpnl/demo_list/pnl/vbox"] margin_top = 30.0 margin_right = 206.0 margin_bottom = 50.0 toggle_mode = true group = ExtResource( 2 ) -text = "Utilities" +text = "Quantize" [node name="bt_smooth" type="Button" parent="mpnl/demo_list/pnl/vbox"] -margin_top = 30.0 +margin_top = 60.0 margin_right = 206.0 -margin_bottom = 50.0 +margin_bottom = 80.0 toggle_mode = true group = ExtResource( 2 ) text = "Smoothing" [node name="bt_cam3d" type="Button" parent="mpnl/demo_list/pnl/vbox"] -margin_top = 60.0 +margin_top = 90.0 margin_right = 206.0 -margin_bottom = 80.0 +margin_bottom = 110.0 toggle_mode = true group = ExtResource( 2 ) text = "Cam3D" [node name="bt_fancyle" type="Button" parent="mpnl/demo_list/pnl/vbox"] -margin_top = 90.0 +margin_top = 120.0 margin_right = 206.0 -margin_bottom = 110.0 +margin_bottom = 140.0 toggle_mode = true group = ExtResource( 2 ) text = "Fancy Line Edit" [node name="bt_network" type="Button" parent="mpnl/demo_list/pnl/vbox"] -margin_top = 120.0 +margin_top = 150.0 margin_right = 206.0 -margin_bottom = 140.0 +margin_bottom = 170.0 toggle_mode = true group = ExtResource( 2 ) text = "Network" [node name="separator" type="HSeparator" parent="mpnl/demo_list/pnl/vbox"] -margin_top = 150.0 +margin_top = 180.0 margin_right = 206.0 -margin_bottom = 154.0 +margin_bottom = 184.0 [node name="bt_megademo" type="Button" parent="mpnl/demo_list/pnl/vbox"] -margin_top = 164.0 +margin_top = 194.0 margin_right = 206.0 -margin_bottom = 184.0 +margin_bottom = 214.0 toggle_mode = true group = ExtResource( 2 ) text = "Complete" @@ -173,7 +172,7 @@ __meta__ = { "_edit_use_anchors_": false } -[node name="utilities" type="Panel" parent="mpnl/stabs"] +[node name="quantize" type="Panel" parent="mpnl/stabs"] visible = false anchor_right = 1.0 anchor_bottom = 1.0 @@ -182,27 +181,26 @@ margin_top = 8.0 margin_right = -4.0 margin_bottom = -4.0 -[node name="description" type="Label" parent="mpnl/stabs/utilities"] +[node name="description" type="Label" parent="mpnl/stabs/quantize"] margin_left = 14.0 margin_top = 43.0 margin_right = 744.0 margin_bottom = 580.0 text = "Interdependency: None -The addon has been removed (hopefully temporarily) from the project because it needs proper testing (things didn't work as expected)... +This addon is meant to provide means to quantize floating point numbers and also use those to compress rotation quaternions using the smallest three method. -This demo page has been created mostly to remind myself about this fact." +The example here does not directly use any of the floating point quantization functions, however it uses the quaternion compression to simulate replication of orientation. Basically, a cube is used to \"generate\" the original orientation, which is then compressed using either 9, 10 or 15 bits per component. Soon after the quaternion is restaured and applied into a second cube. In a way, to simulate the replication of the orientation through networked multiplayer games." autowrap = true __meta__ = { "_edit_use_anchors_": false } -[node name="bt_utilsload" type="Button" parent="mpnl/stabs/utilities"] +[node name="bt_utilsload" type="Button" parent="mpnl/stabs/quantize"] margin_left = 14.0 margin_top = 11.0 margin_right = 122.0 margin_bottom = 31.0 -disabled = true text = "Load" __meta__ = { "_edit_use_anchors_": false @@ -533,7 +531,7 @@ __meta__ = { } [connection signal="pressed" from="mpnl/demo_list/pnl/bt_quit" to="." method="_on_bt_quit_pressed"] [connection signal="pressed" from="mpnl/stabs/encdecbuffer/bt_encdecload" to="." method="_on_bt_encdecload_pressed"] -[connection signal="pressed" from="mpnl/stabs/utilities/bt_utilsload" to="." method="_on_bt_utilsload_pressed"] +[connection signal="pressed" from="mpnl/stabs/quantize/bt_utilsload" to="." method="_on_bt_utilsload_pressed"] [connection signal="pressed" from="mpnl/stabs/cam3d/bt_cam3dload" to="." method="_on_bt_cam3dload_pressed"] [connection signal="pressed" from="mpnl/stabs/smooth/bt_smoothload" to="." method="_on_bt_smoothload_pressed"] [connection signal="pressed" from="mpnl/stabs/network/bt_single" to="." method="_on_bt_single_pressed"] diff --git a/project.godot b/project.godot index 51fb5ef..52a709b 100644 --- a/project.godot +++ b/project.godot @@ -114,6 +114,11 @@ _global_script_classes=[ { "language": "GDScript", "path": "res://addons/keh_network/network.gd" }, { +"base": "Reference", +"class": "Quantize", +"language": "GDScript", +"path": "res://addons/keh_general/data/quantize.gd" +}, { "base": "Node2D", "class": "RectangleDrawer", "language": "GDScript", @@ -166,6 +171,7 @@ _global_script_class_icons={ "NetSnapshot": "", "NetSnapshotData": "", "Network": "", +"Quantize": "", "RectangleDrawer": "", "SharedUtils": "", "Smooth2D": "", @@ -187,6 +193,7 @@ network="*res://addons/keh_network/network.gd" [debug] gdscript/completion/autocomplete_setters_and_getters=true +gdscript/warnings/unused_class_variable=true [editor_plugins] @@ -195,6 +202,7 @@ enabled=PoolStringArray( "keh_network", "keh_smooth", "keh_ui" ) [global] warning=true +unused=true [input] diff --git a/readme.md b/readme.md index 482c0cb..fb8dbe5 100644 --- a/readme.md +++ b/readme.md @@ -39,6 +39,13 @@ Requires Activation: no Implements a class (`EncDecBuffer`) that wraps a `PoolByteArray` and provides means to add or extract data into the wrapped array. One of the features is that it allows "short integers" (8 or 16 bits) to be encoded/decoded. The main reason for this buffer to exist is to strip out variant headers (4 bytes per property) from the encoded data, mostly for packing properties to be sent through networks. Although this can be useful for other things, like binary save files. +#### data/quantize.gd + +Interdependency: none +Requires Activation: no + +Provides means to quantize floating point numbers as well as compress rotation quaternions using the *smallest three* method. The entire functionality is provided through static functions, meaning that it's not necessary to create instances of the class (`Quantize`). Although the returned quantized data are still using the full GDScript variant data, the resulting integers can be packed into others through bit masking. Also, this data can be directly used with the encdecbuffer.gd script, meaning the two complement each other rather well. + ### Network Interdependency: data/encdecbuffer.gd