openvibers/openvibe.games Commits
Scraplandia: far avatars stand on their feet (the merged copy's parts went through their joints twice: a clone keeps its parent) (#120)
Changes
@@@ -8,6 +8,10 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).### Fixed++- **Far avatars floated, twisted.** The merged copy that stands in for an avatar past 18 m baked each part's world++ position, but a Babylon clone keeps its source's parent, so the joint chain applied a second time: far NPCs hung++ 0.4 m up, out of shape. Each copy now drops its parent before baking, and a test checks that the copy stands on its++ feet at a person's height.- **NPCs cost 150 draw calls in town.** Each avatar is about 14 meshes, so eleven townsfolk and wardens in view drew150 times a frame. Past 18 m an avatar, NPC or remote player, now draws as one merged copy of itself in its restpose and skips the animation work. It switches back to the full rig inside 16 m. In town, NPC draws went from 150@@@ -82,6 +82,8 @@ export class Avatar {if (!(part instanceof Mesh) || !part.getTotalVertices() || !material?.diffuseColor) continueconst copy = part.clone(`${this.name}:lodpart`, null, true)copy.makeGeometryUnique()++ // A clone keeps its source's parent (its joint), which would apply the joint chain again on top of the bake.++ copy.parent = nullcopy.position.setAll(0)copy.rotationQuaternion = nullcopy.rotation.setAll(0)@@@ -43,6 +43,14 @@ describe('avatar level of detail', () => {const lod = lodOf()!expect(lod).not.toBeNull()expect(lod.isVerticesDataPresent(VertexBuffer.ColorKind)).toBe(true)++ // It stands on its feet at its root, a person's height (each part placed once, not through its joints twice).++ lod.refreshBoundingInfo(false, false)++ const box = lod.getBoundingInfo().boundingBox++ expect(box.minimumWorld.y).toBeGreaterThan(-0.05)++ expect(box.minimumWorld.y).toBeLessThan(0.15)++ expect(box.maximumWorld.y).toBeGreaterThan(1.5)++ expect(box.maximumWorld.y).toBeLessThan(2.1)++ expect(Math.abs(box.centerWorld.x)).toBeLessThan(0.2)avatar.update(at(5))expect(lod.isEnabled()).toBe(false)
@@ -8,6 +8,10 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
@@ -8,6 +8,10 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
### Fixed
### Fixed
+- **Far avatars floated, twisted.** The merged copy that stands in for an avatar past 18 m baked each part's world
+ position, but a Babylon clone keeps its source's parent, so the joint chain applied a second time: far NPCs hung
+ 0.4 m up, out of shape. Each copy now drops its parent before baking, and a test checks that the copy stands on its
+ feet at a person's height.
- **NPCs cost 150 draw calls in town.** Each avatar is about 14 meshes, so eleven townsfolk and wardens in view drew
- **NPCs cost 150 draw calls in town.** Each avatar is about 14 meshes, so eleven townsfolk and wardens in view drew
150 times a frame. Past 18 m an avatar, NPC or remote player, now draws as one merged copy of itself in its rest
150 times a frame. Past 18 m an avatar, NPC or remote player, now draws as one merged copy of itself in its rest
pose and skips the animation work. It switches back to the full rig inside 16 m. In town, NPC draws went from 150
pose and skips the animation work. It switches back to the full rig inside 16 m. In town, NPC draws went from 150
@@ -82,6 +82,8 @@ export class Avatar {
@@ -82,6 +82,8 @@ export class Avatar {
if (!(part instanceof Mesh) || !part.getTotalVertices() || !material?.diffuseColor) continue
if (!(part instanceof Mesh) || !part.getTotalVertices() || !material?.diffuseColor) continue
const copy = part.clone(`${this.name}:lodpart`, null, true)
const copy = part.clone(`${this.name}:lodpart`, null, true)
copy.makeGeometryUnique()
copy.makeGeometryUnique()
+ // A clone keeps its source's parent (its joint), which would apply the joint chain again on top of the bake.
+ copy.parent = null
copy.position.setAll(0)
copy.position.setAll(0)
copy.rotationQuaternion = null
copy.rotationQuaternion = null
copy.rotation.setAll(0)
copy.rotation.setAll(0)
@@ -43,6 +43,14 @@ describe('avatar level of detail', () => {
@@ -43,6 +43,14 @@ describe('avatar level of detail', () => {
const lod = lodOf()!
const lod = lodOf()!
expect(lod).not.toBeNull()
expect(lod).not.toBeNull()
expect(lod.isVerticesDataPresent(VertexBuffer.ColorKind)).toBe(true)
expect(lod.isVerticesDataPresent(VertexBuffer.ColorKind)).toBe(true)
+ // It stands on its feet at its root, a person's height (each part placed once, not through its joints twice).
+ lod.refreshBoundingInfo(false, false)
+ const box = lod.getBoundingInfo().boundingBox
+ expect(box.minimumWorld.y).toBeGreaterThan(-0.05)
+ expect(box.minimumWorld.y).toBeLessThan(0.15)
+ expect(box.maximumWorld.y).toBeGreaterThan(1.5)
+ expect(box.maximumWorld.y).toBeLessThan(2.1)
+ expect(Math.abs(box.centerWorld.x)).toBeLessThan(0.2)
avatar.update(at(5))
avatar.update(at(5))
expect(lod.isEnabled()).toBe(false)
expect(lod.isEnabled()).toBe(false)