From b83d03d83a6caae274d5df767678f01a6d9983f1 Mon Sep 17 00:00:00 2001 From: Vladimir Pankov Date: Tue, 17 Feb 2026 14:07:21 +0200 Subject: [PATCH 1/2] Add Velocity transmission toggle and calculation logic. --- .../src/main/java/dev/slimevr/VRServer.kt | 18 +++++ .../main/java/dev/slimevr/config/VRConfig.kt | 14 ++++ .../java/dev/slimevr/config/VelocityConfig.kt | 10 +++ .../processor/skeleton/HumanSkeleton.kt | 1 + .../dev/slimevr/tracking/trackers/Tracker.kt | 71 +++++++++++++++++++ 5 files changed, 114 insertions(+) create mode 100644 server/core/src/main/java/dev/slimevr/config/VelocityConfig.kt diff --git a/server/core/src/main/java/dev/slimevr/VRServer.kt b/server/core/src/main/java/dev/slimevr/VRServer.kt index 4db758d06d..e107a98a09 100644 --- a/server/core/src/main/java/dev/slimevr/VRServer.kt +++ b/server/core/src/main/java/dev/slimevr/VRServer.kt @@ -182,6 +182,21 @@ class VRServer @JvmOverloads constructor( instance = this } + /** + * TODO: The design of this method is chosen for future expandability in case we want to have more complex velocity policies that depend on tracker properties or other config values. + * Initiates the velocity policy application process for the specified [Tracker] (or all trackers if null). + * + * This method serves as the initiator. + * The actual policy logic is not handled here, + * but is delegated to [dev.slimevr.config.VRConfig.applyVelocityPolicy]. + */ + private fun applyVelocityPolicyTo(tracker: Tracker?) { + val targets = tracker?.let { listOf(it) } ?: trackers + for (t in targets) { + configManager.vrConfig.applyVelocityPolicy(t) + } + } + fun hasBridge(bridgeClass: Class): Boolean { for (bridge in bridges) { if (bridgeClass.isAssignableFrom(bridge.javaClass)) { @@ -226,6 +241,9 @@ class VRServer @JvmOverloads constructor( refreshTrackersDriftCompensationEnabled() configManager.vrConfig.writeTrackerConfig(tracker) configManager.saveConfig() + + // Requires a fresh TrackerConfig on Update, so executed after we save a new state. + applyVelocityPolicyTo(tracker) } } diff --git a/server/core/src/main/java/dev/slimevr/config/VRConfig.kt b/server/core/src/main/java/dev/slimevr/config/VRConfig.kt index 9ef33f5e00..dd100f55ef 100644 --- a/server/core/src/main/java/dev/slimevr/config/VRConfig.kt +++ b/server/core/src/main/java/dev/slimevr/config/VRConfig.kt @@ -58,6 +58,8 @@ class VRConfig { val trackingChecklist: TrackingChecklistConfig = TrackingChecklistConfig() + var velocityConfig: VelocityConfig = VelocityConfig() + val vrcConfig: VRCConfig = VRCConfig() init { @@ -104,6 +106,17 @@ class VRConfig { return config } + /** + * Applies the velocity policy to the given [Tracker]. + * + * This method determines whether the tracker—be it a physical sensor or a computed virtual device—is + * permitted to calculate and broadcast velocity data. + * It resolves the active [VelocityConfig] value, updating [Tracker.allowVelocity] accordingly. + */ + fun applyVelocityPolicy(tracker: Tracker) { + tracker.allowVelocity = velocityConfig.sendDerivedVelocity + } + fun readTrackerConfig(tracker: Tracker) { if (tracker.userEditable) { val config = getTracker(tracker) @@ -119,6 +132,7 @@ class VRConfig { .readFilteringConfig(filters, tracker.getRotation()) } } + applyVelocityPolicy(tracker) } fun writeTrackerConfig(tracker: Tracker?) { diff --git a/server/core/src/main/java/dev/slimevr/config/VelocityConfig.kt b/server/core/src/main/java/dev/slimevr/config/VelocityConfig.kt new file mode 100644 index 0000000000..a6516b1e24 --- /dev/null +++ b/server/core/src/main/java/dev/slimevr/config/VelocityConfig.kt @@ -0,0 +1,10 @@ +package dev.slimevr.config + +/** + * Allows to enable/disable sending of optional derived velocity data via Protobuf. + * Enables Natural Locomotion Support + * May create overprediction in certain titles causing excessive jitter when moving upper body. + */ +class VelocityConfig { + var sendDerivedVelocity: Boolean = false // Disables derived velocity for all trackers. Driver zeroes out velocity if nothing is returned in protobuf message. +} diff --git a/server/core/src/main/java/dev/slimevr/tracking/processor/skeleton/HumanSkeleton.kt b/server/core/src/main/java/dev/slimevr/tracking/processor/skeleton/HumanSkeleton.kt index 91a7d76035..bc3b5ac014 100644 --- a/server/core/src/main/java/dev/slimevr/tracking/processor/skeleton/HumanSkeleton.kt +++ b/server/core/src/main/java/dev/slimevr/tracking/processor/skeleton/HumanSkeleton.kt @@ -1171,6 +1171,7 @@ class HumanSkeleton( it.position = trackerBone.getTailPosition() it.setRotation(trackerBone.getGlobalRotation() * trackerBone.rotationOffset.inv()) it.dataTick() + it.updateDerivedVelocity(System.nanoTime()) } } diff --git a/server/core/src/main/java/dev/slimevr/tracking/trackers/Tracker.kt b/server/core/src/main/java/dev/slimevr/tracking/trackers/Tracker.kt index bb70fbc830..c287c9e14d 100644 --- a/server/core/src/main/java/dev/slimevr/tracking/trackers/Tracker.kt +++ b/server/core/src/main/java/dev/slimevr/tracking/trackers/Tracker.kt @@ -75,6 +75,11 @@ class Tracker @JvmOverloads constructor( */ val allowMounting: Boolean = false, + /** + * If true, the tracker will send Derived Velocity. + */ + var allowVelocity: Boolean = false, + val isHmd: Boolean = false, /** @@ -105,7 +110,19 @@ class Tracker @JvmOverloads constructor( // IMU: +z forward, +x left, +y up // SlimeVR: +z backward, +x right, +y up private var _acceleration = Vector3.NULL + private var _velocity = Vector3.NULL private var _magVector = Vector3.NULL + + /** + * Velocity state server-side differentiation based on sent poses + */ + private data class VelocityState( + var prevTimeNs: Long = 0L, + var prevPos: Vector3 = Vector3(0f, 0f, 0f), + ) + + private var velocityState: VelocityState? = null + var position = Vector3.NULL val resetsHandler: TrackerResetsHandler = TrackerResetsHandler(this) val filteringHandler: TrackerFilteringHandler = TrackerFilteringHandler() @@ -329,6 +346,42 @@ class Tracker @JvmOverloads constructor( } } + /** + * Updates the derived velocity of the tracker by differentiating position over time. + * + * This method enforces the [allowVelocity] policy and checks for valid position data before + * proceeding. If conditions are met, it calculates velocity based on the displacement since the + * last update, applying a sanity check on the time delta to filter out noise and ensure data stability. + * + */ + fun updateDerivedVelocity(nowNs: Long) { + if (!allowVelocity || !hasPosition) { + velocityState = null + _velocity = Vector3.NULL + return + } + + val pos = position + val state = velocityState ?: VelocityState().also { + velocityState = it + } + + if (state.prevTimeNs != 0L) { + val dt = (nowNs - state.prevTimeNs) * 1e-9 + if (dt in 1e-4..0.25) { + _velocity = Vector3( + ((pos.x - state.prevPos.x) / dt).toFloat(), + ((pos.y - state.prevPos.y) / dt).toFloat(), + ((pos.z - state.prevPos.z) / dt).toFloat(), + ) + } else { + _velocity = Vector3.NULL + } + } + state.prevTimeNs = nowNs + state.prevPos = pos + } + /** * Gets the identity-adjusted tracker rotation after the resetsHandler's corrections * (identity reset, drift and identity mounting). @@ -405,6 +458,24 @@ class Tracker @JvmOverloads constructor( this._acceleration = vec } + /** + * Sets the derived velocity of the tracker. + */ + fun setVelocity(vec: Vector3) { + this._velocity = if (allowVelocity) vec else Vector3.NULL + } + + /** + * Gets the derived velocity of the tracker. + */ + fun getVelocity(): Vector3 = _velocity + + /** + * True if the tracker has valid velocity data. + */ + val hasVelocity: Boolean + get() = allowVelocity && _velocity != Vector3.NULL + /** * True if the raw rotation is coming directly from an IMU (no cameras or lighthouses) * For example, flex sensor trackers are not considered as IMU trackers (see TrackerDataType) From 64292fd80cd34b73bc1dd7cc129ce6710f56b202 Mon Sep 17 00:00:00 2001 From: Vladimir Pankov Date: Tue, 17 Feb 2026 14:33:10 +0200 Subject: [PATCH 2/2] Add Velocity data send via ProtobufBridge. Update ProtobufMessages to latest definition from the Driver's main branch. --- .../desktop/platform/ProtobufBridge.kt | 42 +- .../desktop/platform/ProtobufMessages.java | 445 ++++++++++++++++-- 2 files changed, 446 insertions(+), 41 deletions(-) diff --git a/server/desktop/src/main/java/dev/slimevr/desktop/platform/ProtobufBridge.kt b/server/desktop/src/main/java/dev/slimevr/desktop/platform/ProtobufBridge.kt index fdbee06725..40dfb056df 100644 --- a/server/desktop/src/main/java/dev/slimevr/desktop/platform/ProtobufBridge.kt +++ b/server/desktop/src/main/java/dev/slimevr/desktop/platform/ProtobufBridge.kt @@ -82,6 +82,7 @@ abstract class ProtobufBridge(@JvmField protected val bridgeName: String) : ISte target.position = source.position target.setRotation(source.getRotation()) target.status = source.status + target.setVelocity(source.getVelocity()) target.batteryLevel = source.batteryLevel target.batteryVoltage = source.batteryVoltage target.dataTick() @@ -103,23 +104,38 @@ abstract class ProtobufBridge(@JvmField protected val bridgeName: String) : ISte @VRServerThread protected fun writeTrackerUpdate(localTracker: Tracker?) { - val builder = ProtobufMessages.Position.newBuilder().setTrackerId( - localTracker!!.id, - ) - if (localTracker.hasPosition) { - val pos = localTracker.position + val tracker = localTracker ?: return + + val builder = ProtobufMessages.Position.newBuilder() + .setTrackerId(tracker.id) + + if (tracker.hasPosition) { + val pos = tracker.position builder.setX(pos.x) builder.setY(pos.y) builder.setZ(pos.z) } - if (localTracker.hasRotation) { - val rot = localTracker.getRotation() + + if (tracker.hasRotation) { + val rot = tracker.getRotation() builder.setQx(rot.x) builder.setQy(rot.y) builder.setQz(rot.z) builder.setQw(rot.w) } - sendMessage(ProtobufMessage.newBuilder().setPosition(builder).build()) + + if (tracker.allowVelocity) { + val vel = tracker.getVelocity() + builder.setVx(vel.x) + builder.setVy(vel.y) + builder.setVz(vel.z) + } + + sendMessage( + ProtobufMessage.newBuilder() + .setPosition(builder) + .build(), + ) } @VRServerThread @@ -169,6 +185,16 @@ abstract class ProtobufBridge(@JvmField protected val bridgeName: String) : ISte ), ) + if (positionMessage.hasVx()) { + tracker + .setVelocity( + Vector3( + positionMessage.vx, + positionMessage.vy, + positionMessage.vz, + ), + ) + } tracker.dataTick() } } diff --git a/server/desktop/src/main/java/dev/slimevr/desktop/platform/ProtobufMessages.java b/server/desktop/src/main/java/dev/slimevr/desktop/platform/ProtobufMessages.java index ebb8e0f03c..703938ad6d 100644 --- a/server/desktop/src/main/java/dev/slimevr/desktop/platform/ProtobufMessages.java +++ b/server/desktop/src/main/java/dev/slimevr/desktop/platform/ProtobufMessages.java @@ -1052,6 +1052,48 @@ public interface PositionOrBuilder extends * @return The dataSource. */ dev.slimevr.desktop.platform.ProtobufMessages.Position.DataSource getDataSource(); + + /** + * optional float vx = 10; + * + * @return Whether the vx field is set. + */ + boolean hasVx(); + + /** + * optional float vx = 10; + * + * @return The vx. + */ + float getVx(); + + /** + * optional float vy = 11; + * + * @return Whether the vy field is set. + */ + boolean hasVy(); + + /** + * optional float vy = 11; + * + * @return The vy. + */ + float getVy(); + + /** + * optional float vz = 12; + * + * @return Whether the vz field is set. + */ + boolean hasVz(); + + /** + * optional float vz = 12; + * + * @return The vz. + */ + float getVz(); } /** @@ -1416,6 +1458,75 @@ public dev.slimevr.desktop.platform.ProtobufMessages.Position.DataSource getData : result; } + public static final int VX_FIELD_NUMBER = 10; + private float vx_ = 0F; + + /** + * optional float vx = 10; + * + * @return Whether the vx field is set. + */ + @java.lang.Override + public boolean hasVx() { + return ((bitField0_ & 0x00000010) != 0); + } + + /** + * optional float vx = 10; + * + * @return The vx. + */ + @java.lang.Override + public float getVx() { + return vx_; + } + + public static final int VY_FIELD_NUMBER = 11; + private float vy_ = 0F; + + /** + * optional float vy = 11; + * + * @return Whether the vy field is set. + */ + @java.lang.Override + public boolean hasVy() { + return ((bitField0_ & 0x00000020) != 0); + } + + /** + * optional float vy = 11; + * + * @return The vy. + */ + @java.lang.Override + public float getVy() { + return vy_; + } + + public static final int VZ_FIELD_NUMBER = 12; + private float vz_ = 0F; + + /** + * optional float vz = 12; + * + * @return Whether the vz field is set. + */ + @java.lang.Override + public boolean hasVz() { + return ((bitField0_ & 0x00000040) != 0); + } + + /** + * optional float vz = 12; + * + * @return The vz. + */ + @java.lang.Override + public float getVz() { + return vz_; + } + private byte memoizedIsInitialized = -1; @java.lang.Override @@ -1460,6 +1571,15 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) if (((bitField0_ & 0x00000008) != 0)) { output.writeEnum(9, dataSource_); } + if (((bitField0_ & 0x00000010) != 0)) { + output.writeFloat(10, vx_); + } + if (((bitField0_ & 0x00000020) != 0)) { + output.writeFloat(11, vy_); + } + if (((bitField0_ & 0x00000040) != 0)) { + output.writeFloat(12, vz_); + } getUnknownFields().writeTo(output); } @@ -1506,6 +1626,18 @@ public int getSerializedSize() { size += com.google.protobuf.CodedOutputStream .computeEnumSize(9, dataSource_); } + if (((bitField0_ & 0x00000010) != 0)) { + size += com.google.protobuf.CodedOutputStream + .computeFloatSize(10, vx_); + } + if (((bitField0_ & 0x00000020) != 0)) { + size += com.google.protobuf.CodedOutputStream + .computeFloatSize(11, vy_); + } + if (((bitField0_ & 0x00000040) != 0)) { + size += com.google.protobuf.CodedOutputStream + .computeFloatSize(12, vz_); + } size += getUnknownFields().getSerializedSize(); memoizedSize = size; return size; @@ -1600,6 +1732,42 @@ public boolean equals(final java.lang.Object obj) { if (dataSource_ != other.dataSource_) return false; } + if (hasVx() != other.hasVx()) + return false; + if (hasVx()) { + if ( + java.lang.Float.floatToIntBits(getVx()) + != java.lang.Float + .floatToIntBits( + other.getVx() + ) + ) + return false; + } + if (hasVy() != other.hasVy()) + return false; + if (hasVy()) { + if ( + java.lang.Float.floatToIntBits(getVy()) + != java.lang.Float + .floatToIntBits( + other.getVy() + ) + ) + return false; + } + if (hasVz() != other.hasVz()) + return false; + if (hasVz()) { + if ( + java.lang.Float.floatToIntBits(getVz()) + != java.lang.Float + .floatToIntBits( + other.getVz() + ) + ) + return false; + } if (!getUnknownFields().equals(other.getUnknownFields())) return false; return true; @@ -1666,6 +1834,30 @@ public int hashCode() { hash = (37 * hash) + DATA_SOURCE_FIELD_NUMBER; hash = (53 * hash) + dataSource_; } + if (hasVx()) { + hash = (37 * hash) + VX_FIELD_NUMBER; + hash = (53 * hash) + + java.lang.Float + .floatToIntBits( + getVx() + ); + } + if (hasVy()) { + hash = (37 * hash) + VY_FIELD_NUMBER; + hash = (53 * hash) + + java.lang.Float + .floatToIntBits( + getVy() + ); + } + if (hasVz()) { + hash = (37 * hash) + VZ_FIELD_NUMBER; + hash = (53 * hash) + + java.lang.Float + .floatToIntBits( + getVz() + ); + } hash = (29 * hash) + getUnknownFields().hashCode(); memoizedHashCode = hash; return hash; @@ -1841,6 +2033,9 @@ public Builder clear() { qz_ = 0F; qw_ = 0F; dataSource_ = 0; + vx_ = 0F; + vy_ = 0F; + vz_ = 0F; return this; } @@ -1911,6 +2106,18 @@ private void buildPartial0( result.dataSource_ = dataSource_; to_bitField0_ |= 0x00000008; } + if (((from_bitField0_ & 0x00000200) != 0)) { + result.vx_ = vx_; + to_bitField0_ |= 0x00000010; + } + if (((from_bitField0_ & 0x00000400) != 0)) { + result.vy_ = vy_; + to_bitField0_ |= 0x00000020; + } + if (((from_bitField0_ & 0x00000800) != 0)) { + result.vz_ = vz_; + to_bitField0_ |= 0x00000040; + } result.bitField0_ |= to_bitField0_; } @@ -1960,6 +2167,15 @@ public Builder mergeFrom(dev.slimevr.desktop.platform.ProtobufMessages.Position if (other.hasDataSource()) { setDataSourceValue(other.getDataSourceValue()); } + if (other.hasVx()) { + setVx(other.getVx()); + } + if (other.hasVy()) { + setVy(other.getVy()); + } + if (other.hasVz()) { + setVz(other.getVz()); + } this.mergeUnknownFields(other.getUnknownFields()); onChanged(); return this; @@ -2032,6 +2248,21 @@ public Builder mergeFrom( bitField0_ |= 0x00000100; break; } // case 72 + case 85: { + vx_ = input.readFloat(); + bitField0_ |= 0x00000200; + break; + } // case 85 + case 93: { + vy_ = input.readFloat(); + bitField0_ |= 0x00000400; + break; + } // case 93 + case 101: { + vz_ = input.readFloat(); + bitField0_ |= 0x00000800; + break; + } // case 101 default: { if (!super.parseUnknownField(input, extensionRegistry, tag)) { done = true; // was an endgroup tag @@ -2464,6 +2695,150 @@ public Builder clearDataSource() { return this; } + private float vx_; + + /** + * optional float vx = 10; + * + * @return Whether the vx field is set. + */ + @java.lang.Override + public boolean hasVx() { + return ((bitField0_ & 0x00000200) != 0); + } + + /** + * optional float vx = 10; + * + * @return The vx. + */ + @java.lang.Override + public float getVx() { + return vx_; + } + + /** + * optional float vx = 10; + * + * @param value The vx to set. + * @return This builder for chaining. + */ + public Builder setVx(float value) { + + vx_ = value; + bitField0_ |= 0x00000200; + onChanged(); + return this; + } + + /** + * optional float vx = 10; + * + * @return This builder for chaining. + */ + public Builder clearVx() { + bitField0_ = (bitField0_ & ~0x00000200); + vx_ = 0F; + onChanged(); + return this; + } + + private float vy_; + + /** + * optional float vy = 11; + * + * @return Whether the vy field is set. + */ + @java.lang.Override + public boolean hasVy() { + return ((bitField0_ & 0x00000400) != 0); + } + + /** + * optional float vy = 11; + * + * @return The vy. + */ + @java.lang.Override + public float getVy() { + return vy_; + } + + /** + * optional float vy = 11; + * + * @param value The vy to set. + * @return This builder for chaining. + */ + public Builder setVy(float value) { + + vy_ = value; + bitField0_ |= 0x00000400; + onChanged(); + return this; + } + + /** + * optional float vy = 11; + * + * @return This builder for chaining. + */ + public Builder clearVy() { + bitField0_ = (bitField0_ & ~0x00000400); + vy_ = 0F; + onChanged(); + return this; + } + + private float vz_; + + /** + * optional float vz = 12; + * + * @return Whether the vz field is set. + */ + @java.lang.Override + public boolean hasVz() { + return ((bitField0_ & 0x00000800) != 0); + } + + /** + * optional float vz = 12; + * + * @return The vz. + */ + @java.lang.Override + public float getVz() { + return vz_; + } + + /** + * optional float vz = 12; + * + * @param value The vz to set. + * @return This builder for chaining. + */ + public Builder setVz(float value) { + + vz_ = value; + bitField0_ |= 0x00000800; + onChanged(); + return this; + } + + /** + * optional float vz = 12; + * + * @return This builder for chaining. + */ + public Builder clearVz() { + bitField0_ = (bitField0_ & ~0x00000800); + vz_ = 0F; + onChanged(); + return this; + } + // @@protoc_insertion_point(builder_scope:messages.Position) } @@ -9023,7 +9398,7 @@ public static com.google.protobuf.Descriptors.FileDescriptor getDescriptor() { + "ngPong\"#\n\007Version\022\030\n\020protocol_version\030\001 " + - "\001(\005\"\223\002\n\010Position\022\022\n\ntracker_id\030\001 \001(\005\022\016\n\001" + "\001(\005\"\333\002\n\010Position\022\022\n\ntracker_id\030\001 \001(\005\022\016\n\001" + "x\030\002 \001(\002H\000\210\001\001\022\016\n\001y\030\003 \001(\002H\001\210\001\001\022\016\n\001z\030\004 \001(\002H" + @@ -9031,67 +9406,71 @@ public static com.google.protobuf.Descriptors.FileDescriptor getDescriptor() { + "\022\n\n\002qw\030\010 \001(\002\0227\n\013data_source\030\t \001(\0162\035.mess" + - "ages.Position.DataSourceH\003\210\001\001\"8\n\nDataSou" + "ages.Position.DataSourceH\003\210\001\001\022\017\n\002vx\030\n \001(" + + + "\002H\004\210\001\001\022\017\n\002vy\030\013 \001(\002H\005\210\001\001\022\017\n\002vz\030\014 \001(\002H\006\210\001\001" + + + "\"8\n\nDataSource\022\010\n\004NONE\020\000\022\007\n\003IMU\020\001\022\r\n\tPRE" + - "rce\022\010\n\004NONE\020\000\022\007\n\003IMU\020\001\022\r\n\tPRECISION\020\002\022\010\n" + "CISION\020\002\022\010\n\004FULL\020\003B\004\n\002_xB\004\n\002_yB\004\n\002_zB\016\n\014" + - "\004FULL\020\003B\004\n\002_xB\004\n\002_yB\004\n\002_zB\016\n\014_data_sourc" + "_data_sourceB\005\n\003_vxB\005\n\003_vyB\005\n\003_vz\"\227\001\n\nUs" + - "e\"\227\001\n\nUserAction\022\014\n\004name\030\001 \001(\t\022C\n\020action" + "erAction\022\014\n\004name\030\001 \001(\t\022C\n\020action_argumen" + - "_arguments\030\002 \003(\0132).messages.UserAction.A" + "ts\030\002 \003(\0132).messages.UserAction.ActionArg" + - "ctionArgumentsEntry\0326\n\024ActionArgumentsEn" + "umentsEntry\0326\n\024ActionArgumentsEntry\022\013\n\003k" + - "try\022\013\n\003key\030\001 \001(\t\022\r\n\005value\030\002 \001(\t:\0028\001\"|\n\014T" + "ey\030\001 \001(\t\022\r\n\005value\030\002 \001(\t:\0028\001\"|\n\014TrackerAd" + - "rackerAdded\022\022\n\ntracker_id\030\001 \001(\005\022\026\n\016track" + "ded\022\022\n\ntracker_id\030\001 \001(\005\022\026\n\016tracker_seria" + - "er_serial\030\002 \001(\t\022\024\n\014tracker_name\030\003 \001(\t\022\024\n" + "l\030\002 \001(\t\022\024\n\014tracker_name\030\003 \001(\t\022\024\n\014tracker" + - "\014tracker_role\030\004 \001(\005\022\024\n\014manufacturer\030\005 \001(" + "_role\030\004 \001(\005\022\024\n\014manufacturer\030\005 \001(\t\"\374\002\n\rTr" + - "\t\"\374\002\n\rTrackerStatus\022\022\n\ntracker_id\030\001 \001(\005\022" + "ackerStatus\022\022\n\ntracker_id\030\001 \001(\005\022.\n\006statu" + - ".\n\006status\030\002 \001(\0162\036.messages.TrackerStatus" + "s\030\002 \001(\0162\036.messages.TrackerStatus.Status\022" + - ".Status\0221\n\005extra\030\003 \003(\0132\".messages.Tracke" + "1\n\005extra\030\003 \003(\0132\".messages.TrackerStatus." + - "rStatus.ExtraEntry\022;\n\nconfidence\030\004 \001(\0162\"" + "ExtraEntry\022;\n\nconfidence\030\004 \001(\0162\".message" + - ".messages.TrackerStatus.ConfidenceH\000\210\001\001\032" + "s.TrackerStatus.ConfidenceH\000\210\001\001\032,\n\nExtra" + - ",\n\nExtraEntry\022\013\n\003key\030\001 \001(\t\022\r\n\005value\030\002 \001(" + "Entry\022\013\n\003key\030\001 \001(\t\022\r\n\005value\030\002 \001(\t:\0028\001\"E\n" + - "\t:\0028\001\"E\n\006Status\022\020\n\014DISCONNECTED\020\000\022\006\n\002OK\020" + "\006Status\022\020\n\014DISCONNECTED\020\000\022\006\n\002OK\020\001\022\010\n\004BUS" + - "\001\022\010\n\004BUSY\020\002\022\t\n\005ERROR\020\003\022\014\n\010OCCLUDED\020\004\"3\n\n" + "Y\020\002\022\t\n\005ERROR\020\003\022\014\n\010OCCLUDED\020\004\"3\n\nConfiden" + - "Confidence\022\006\n\002NO\020\000\022\007\n\003LOW\020\001\022\n\n\006MEDIUM\020\005\022" + "ce\022\006\n\002NO\020\000\022\007\n\003LOW\020\001\022\n\n\006MEDIUM\020\005\022\010\n\004HIGH\020" + - "\010\n\004HIGH\020\nB\r\n\013_confidence\"I\n\007Battery\022\022\n\nt" + "\nB\r\n\013_confidence\"I\n\007Battery\022\022\n\ntracker_i" + - "racker_id\030\001 \001(\005\022\025\n\rbattery_level\030\002 \001(\002\022\023" + "d\030\001 \001(\005\022\025\n\rbattery_level\030\002 \001(\002\022\023\n\013is_cha" + - "\n\013is_charging\030\003 \001(\010\"\241\002\n\017ProtobufMessage\022" + "rging\030\003 \001(\010\"\241\002\n\017ProtobufMessage\022&\n\010posit" + - "&\n\010position\030\001 \001(\0132\022.messages.PositionH\000\022" + "ion\030\001 \001(\0132\022.messages.PositionH\000\022+\n\013user_" + - "+\n\013user_action\030\002 \001(\0132\024.messages.UserActi" + "action\030\002 \001(\0132\024.messages.UserActionH\000\022/\n\r" + - "onH\000\022/\n\rtracker_added\030\003 \001(\0132\026.messages.T" + "tracker_added\030\003 \001(\0132\026.messages.TrackerAd" + - "rackerAddedH\000\0221\n\016tracker_status\030\004 \001(\0132\027." + "dedH\000\0221\n\016tracker_status\030\004 \001(\0132\027.messages" + - "messages.TrackerStatusH\000\022$\n\007battery\030\005 \001(" + ".TrackerStatusH\000\022$\n\007battery\030\005 \001(\0132\021.mess" + - "\0132\021.messages.BatteryH\000\022$\n\007version\030\006 \001(\0132" + "ages.BatteryH\000\022$\n\007version\030\006 \001(\0132\021.messag" + - "\021.messages.VersionH\000B\t\n\007messageB2\n\034dev.s" + "es.VersionH\000B\t\n\007messageB2\n\034dev.slimevr.d" + - "limevr.desktop.platformB\020ProtobufMessage" + "esktop.platformB\020ProtobufMessagesH\003b\006pro" + - "sH\003b\006proto3" + "to3" }; descriptor = com.google.protobuf.Descriptors.FileDescriptor .internalBuildGeneratedFileFrom( @@ -9113,7 +9492,7 @@ public static com.google.protobuf.Descriptors.FileDescriptor getDescriptor() { internal_static_messages_Position_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_messages_Position_descriptor, new java.lang.String[] { "TrackerId", "X", "Y", "Z", "Qx", "Qy", "Qz", "Qw", - "DataSource", } + "DataSource", "Vx", "Vy", "Vz", } ); internal_static_messages_UserAction_descriptor = getDescriptor().getMessageTypes().get(3); internal_static_messages_UserAction_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable(