2020.9.27.1202 - viseme analysis, skinned mesh and value streams optimizations
Some more optimizations and related functionality! Skinned Meshes now have new modes for updating bounds, which should help fix the culling issues with some avatars without taking too much performance. If you're experiencing issues with parts of your avatar disappearing at certain angles, do following:
- If your avatar is in multiple pieces, try baking the meshes with the Glue tooltip first if possible. This is optimal for performance and allow the avatar to share a single big bounding box.
- If you can't bake it (e.g. you swap pieces in realtime), switch the affected meshes to "FastDisjointRootApproximate" method. This has an extra performance impact, but is much lower than the SlowRealtimeAccurate method (the previous default)
I've also made changes to Viseme analysis, which is surpringly CPU costly (~1 ms per user per frame) and the internal OVRLipSync library locking was source of major background thread starvation (blocking other computations) with multiple people in the world. Visemes are now computed on each user and streamed to others. There is no explicit mechanism to sync it with voice, but both voice and viseme data are sent as soon as possible and from internal testing this achieves really good latency.
There are some smaller optimizations, like MultiValueStream which significantly reduces overhead of multiple ValueStream objects (network impact is unchanged right now) and more things, I'm going after the most impactful low hanging fruits now and working way to larger reworks and refactoring. More to come!
Also Neos is now available in Dutch thanks to @jeana and @Beaned! I've got the dash and other things to work on Android again, Android/Quest builds incoming!
[h2]New Features:[/h2]
- SkinnedMeshRenderer now has 4 methods to compute bounds and handle culling (big thanks to @DeliriousJax for providing avatar sample for experimentation!):
-- Static (default) - fastest, based on the mesh alone, no realtime computations
-- FastDisjointRootApproximate - merges all bones into disjoint groups (any overlapping bones are merged into a single one) to reduce overall number of bones and uses those to approximate bounds in realtime. Fastest, recommended if part of mesh gets culled
-- MediumPerBoneApproximate - computes mesh bounds from bounds of every single bone. More accurate, but also much slower
-- SlowRealtimeAccurate - uses actual transformed geometry, requiring the skinned mesh to be processed all the time. Very heavy, but will respect things like blendshapes in addition to bones
-- NOTE: When the renderer is in user's hierarchy and set to Static, it'll use FastDisjointRootApproximate method internally, so existing avatars work without changes and don't get culled
- Added "Strip Empty Bones" on SkinnedMeshRenderer, which will remove all empty bones from the mesh
-- You can use this to optimize your meshes/rigs, reducing their CPU, memory and some GPU overhead
-- This only removes them from the mesh, the actual objects representing the bones are still kept in the hierarchy, so it should be generally safe to run this optimization
- Added "Visualize bone bounding boxes" to SkinnedMeshRenderer, which will visualize boudning boxes for each bone affecting the mesh (this includes all vertices that given bone affects)
-- Also added "Remove bone bounding box visuals" to quickly clear them
-- The visuals are non-persitent so won't save
- Added "Visualize approximate merged bone sphere bounds" which will visualize merged bone bounding spheres, representing a fast approximation of disjoint areas of influence of the rig
- Static Mesh "Get Bone List" now prints how many vertices are affected by each bone and shows bones with no bound vertices as red
- Added Dutch locale by @jeana and @Beaned
- Added pluralizer for Polish (requested by @art0007i)
[h2]Optimizations:[/h2]
- Added MultiValueStream, which handles multiple values in a single stream rather than using stream-per-value, significantly reducing associated CPU and memory overhead
-- Switched finger pose estimation to use a single stream for all fingers, rather than individual ValueStream for each bone
- VisemeAnalyzer now streams results of analysis to other users for voice input, rather than having each user compute it on their end independently
-- This should greatly improve CPU load in heavily populated worlds, as analysis takes roughly 1 ms per user per frame (it is done on background thread though, so this will free up lots of background processing resources)
-- As a bonus, Linux users will now see visemes of all other non-Linux users. Windows users will see visemes of all Linux users, because the system will compute them locally
-- Other audio sources (e.g. using it for visualizing music) are still computed locally
-- Currently there's no explicit synchronization mechanism with voice, but the streams are sent out as soon as possible, so they should generally match up close enough (possibly even improving visual latency of the analysis). Let me know if there are issues though
- Fixed VisemeAnalysis causing severe background thread starvation with multiple scheduled analysis due to internal locking mechanism of the OVRlipsync library (this should help some cases of things getting stuck and taking a while to load)
- Reduced CPU and memory usage for LogiX display nodes when they're packed
- Fixed dash projection geometry still being rendered even if it's closed
- Reduced duplicate work with explicit stream encoding (also fixing bug where forced explicit update points wouldn't encode correctly)
- Small low level optimizations when passing in LocaleString values
[h2]Tweaks:[/h2]
- Implemented system to embed the UI locale files in the Android builds for Quest build
- SkinnedMeshRenderer inspector actions are now localizable
- BipedRig inspector actions are now localizable
- Upgraded to Unity 2019.4.11f1 (form 2019.4.10f1)
- Merged French locale new strings by @FreakyWaves Music
- Merged Finnish locale updates by @Nammi and @AdmiralSoap
- Merged "Friends -> Contacts" replacements in English locale by @Alex from Alaska
- Merged "Silence" string localization to Japanese, Esperanto and Chinese by @Melnus
- Merged German locale updates by @Elektrospy
[h2]Bugfixes:[/h2]
- Fixed UserPermissions being misconfigured in some cases, allowing Guest roles to Silence users (reported by @Zane)
- Fixed DynamicReferenceVariableReset having invalid type check, not allowing any proper types (reported by @Earthmark)
- Fixed "Strip Empty Blendshapes" no SkinnedMeshRenderer not releasing read lock on the mesh data
- Fixed "Switch Lomotion Module" not using the locale key when "Exact Match" is enabled (reported by @Shifty | Quality Control Lead)
- Fixed internal UVSphereCapsule procedural generator not setting up triangle correctly when there's pre-existing geometry in the mesh
- Fixed bounding sphere encapsulation producing incorrect result when a smaller sphere is fully contained within another one
- Fixed sending items through messaging system not respecting the item save permissions (reported by @Dante)

- If your avatar is in multiple pieces, try baking the meshes with the Glue tooltip first if possible. This is optimal for performance and allow the avatar to share a single big bounding box.
- If you can't bake it (e.g. you swap pieces in realtime), switch the affected meshes to "FastDisjointRootApproximate" method. This has an extra performance impact, but is much lower than the SlowRealtimeAccurate method (the previous default)
I've also made changes to Viseme analysis, which is surpringly CPU costly (~1 ms per user per frame) and the internal OVRLipSync library locking was source of major background thread starvation (blocking other computations) with multiple people in the world. Visemes are now computed on each user and streamed to others. There is no explicit mechanism to sync it with voice, but both voice and viseme data are sent as soon as possible and from internal testing this achieves really good latency.
There are some smaller optimizations, like MultiValueStream which significantly reduces overhead of multiple ValueStream objects (network impact is unchanged right now) and more things, I'm going after the most impactful low hanging fruits now and working way to larger reworks and refactoring. More to come!
Also Neos is now available in Dutch thanks to @jeana and @Beaned! I've got the dash and other things to work on Android again, Android/Quest builds incoming!
[h2]New Features:[/h2]
- SkinnedMeshRenderer now has 4 methods to compute bounds and handle culling (big thanks to @DeliriousJax for providing avatar sample for experimentation!):
-- Static (default) - fastest, based on the mesh alone, no realtime computations
-- FastDisjointRootApproximate - merges all bones into disjoint groups (any overlapping bones are merged into a single one) to reduce overall number of bones and uses those to approximate bounds in realtime. Fastest, recommended if part of mesh gets culled
-- MediumPerBoneApproximate - computes mesh bounds from bounds of every single bone. More accurate, but also much slower
-- SlowRealtimeAccurate - uses actual transformed geometry, requiring the skinned mesh to be processed all the time. Very heavy, but will respect things like blendshapes in addition to bones
-- NOTE: When the renderer is in user's hierarchy and set to Static, it'll use FastDisjointRootApproximate method internally, so existing avatars work without changes and don't get culled
- Added "Strip Empty Bones" on SkinnedMeshRenderer, which will remove all empty bones from the mesh
-- You can use this to optimize your meshes/rigs, reducing their CPU, memory and some GPU overhead
-- This only removes them from the mesh, the actual objects representing the bones are still kept in the hierarchy, so it should be generally safe to run this optimization
- Added "Visualize bone bounding boxes" to SkinnedMeshRenderer, which will visualize boudning boxes for each bone affecting the mesh (this includes all vertices that given bone affects)
-- Also added "Remove bone bounding box visuals" to quickly clear them
-- The visuals are non-persitent so won't save
- Added "Visualize approximate merged bone sphere bounds" which will visualize merged bone bounding spheres, representing a fast approximation of disjoint areas of influence of the rig
- Static Mesh "Get Bone List" now prints how many vertices are affected by each bone and shows bones with no bound vertices as red
- Added Dutch locale by @jeana and @Beaned
- Added pluralizer for Polish (requested by @art0007i)
[h2]Optimizations:[/h2]
- Added MultiValueStream, which handles multiple values in a single stream rather than using stream-per-value, significantly reducing associated CPU and memory overhead
-- Switched finger pose estimation to use a single stream for all fingers, rather than individual ValueStream for each bone
- VisemeAnalyzer now streams results of analysis to other users for voice input, rather than having each user compute it on their end independently
-- This should greatly improve CPU load in heavily populated worlds, as analysis takes roughly 1 ms per user per frame (it is done on background thread though, so this will free up lots of background processing resources)
-- As a bonus, Linux users will now see visemes of all other non-Linux users. Windows users will see visemes of all Linux users, because the system will compute them locally
-- Other audio sources (e.g. using it for visualizing music) are still computed locally
-- Currently there's no explicit synchronization mechanism with voice, but the streams are sent out as soon as possible, so they should generally match up close enough (possibly even improving visual latency of the analysis). Let me know if there are issues though
- Fixed VisemeAnalysis causing severe background thread starvation with multiple scheduled analysis due to internal locking mechanism of the OVRlipsync library (this should help some cases of things getting stuck and taking a while to load)
- Reduced CPU and memory usage for LogiX display nodes when they're packed
- Fixed dash projection geometry still being rendered even if it's closed
- Reduced duplicate work with explicit stream encoding (also fixing bug where forced explicit update points wouldn't encode correctly)
- Small low level optimizations when passing in LocaleString values
[h2]Tweaks:[/h2]
- Implemented system to embed the UI locale files in the Android builds for Quest build
- SkinnedMeshRenderer inspector actions are now localizable
- BipedRig inspector actions are now localizable
- Upgraded to Unity 2019.4.11f1 (form 2019.4.10f1)
- Merged French locale new strings by @FreakyWaves Music
- Merged Finnish locale updates by @Nammi and @AdmiralSoap
- Merged "Friends -> Contacts" replacements in English locale by @Alex from Alaska
- Merged "Silence" string localization to Japanese, Esperanto and Chinese by @Melnus
- Merged German locale updates by @Elektrospy
[h2]Bugfixes:[/h2]
- Fixed UserPermissions being misconfigured in some cases, allowing Guest roles to Silence users (reported by @Zane)
- Fixed DynamicReferenceVariableReset having invalid type check, not allowing any proper types (reported by @Earthmark)
- Fixed "Strip Empty Blendshapes" no SkinnedMeshRenderer not releasing read lock on the mesh data
- Fixed "Switch Lomotion Module" not using the locale key when "Exact Match" is enabled (reported by @Shifty | Quality Control Lead)
- Fixed internal UVSphereCapsule procedural generator not setting up triangle correctly when there's pre-existing geometry in the mesh
- Fixed bounding sphere encapsulation producing incorrect result when a smaller sphere is fully contained within another one
- Fixed sending items through messaging system not respecting the item save permissions (reported by @Dante)


