Establish native deterministic codegen framework (#45)
Some checks failed
Native code generation / deterministic (macos-latest) (push) Has been cancelled
Native code generation / deterministic (ubuntu-latest) (push) Has been cancelled
Native code generation / deterministic (windows-latest) (push) Has been cancelled
Imaging and meshing gate / native (macos-latest) (push) Has been cancelled
Imaging and meshing gate / native (ubuntu-latest) (push) Has been cancelled
Imaging and meshing gate / native (windows-latest) (push) Has been cancelled
JPEG 2000 feature / linux (push) Has been cancelled
JPEG 2000 feature / macos (push) Has been cancelled
JPEG 2000 feature / windows (push) Has been cancelled
Skia feature / linux (push) Has been cancelled
Skia feature / macos (push) Has been cancelled
Skia feature / windows (push) Has been cancelled

This commit is contained in:
2026-08-09 05:26:47 +00:00
parent 9a62922afa
commit 9f7a4e323f
19 changed files with 34867 additions and 1 deletions

View File

@@ -0,0 +1,31 @@
name: Native code generation
on:
push:
pull_request:
jobs:
deterministic:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy,rustfmt
- name: Test diagnostics and byte-identical generation
run: cargo test -p libremetaverse-codegen
- name: Verify vendored hashes and checked-in output
run: cargo run -p libremetaverse-codegen -- check
- name: Regenerate twice without reference checkout access
run: |
cargo run -p libremetaverse-codegen -- generate
cargo run -p libremetaverse-codegen -- generate
git diff --exit-code -- codegen/generated
- name: Lint native generator
run: cargo clippy -p libremetaverse-codegen --all-targets -- -D warnings
- name: Prove normal consumer build does not execute generation
run: cargo build -p libremetaverse --locked

10
Cargo.lock generated
View File

@@ -370,6 +370,15 @@ dependencies = [
"libremetaverse-types", "libremetaverse-types",
] ]
[[package]]
name = "libremetaverse-codegen"
version = "0.0.1"
dependencies = [
"serde",
"serde_json",
"sha2",
]
[[package]] [[package]]
name = "libremetaverse-compat-tests" name = "libremetaverse-compat-tests"
version = "0.0.1" version = "0.0.1"
@@ -691,6 +700,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba"
dependencies = [ dependencies = [
"serde_core", "serde_core",
"serde_derive",
] ]
[[package]] [[package]]

View File

@@ -17,6 +17,7 @@ members = [
"crates/libremetaverse-voice-webrtc", "crates/libremetaverse-voice-webrtc",
"programs", "programs",
"tests/compat", "tests/compat",
"tools/codegen",
] ]
[workspace.package] [workspace.package]

View File

@@ -178,3 +178,6 @@ baselines are documented in
[`docs/imaging-meshing.md`](docs/imaging-meshing.md). [`docs/imaging-meshing.md`](docs/imaging-meshing.md).
The controlled audit aggregates every expected failure by standardized C# The controlled audit aggregates every expected failure by standardized C#
member ID and rejects unrelated fixture, assertion, compile, or symbol errors. member ID and rejects unrelated fixture, assertion, compile, or symbol errors.
The deterministic, Rust-only source-data generator framework and its complete
pinned input inventory are documented in
[`codegen/README.md`](codegen/README.md).

60
codegen/README.md Normal file
View File

@@ -0,0 +1,60 @@
# Native data generation
`libremetaverse-codegen` is the Rust-only framework for the six source
generators present at LibreMetaverse commit
`2aa70bb68513b39795da5d13c88f31b86e85a3ba`. Normal builds compile only the
checked-in Rust outputs. They do not run this tool, invoke .NET or Roslyn, read
an environment-dependent path, access the network, or open the sibling
reference checkout.
## Provenance inventory
[`sources.json`](sources.json) is the machine-readable inventory. It records
the upstream repository and commit, source-generator hashes, every actual
`AdditionalFile`, its vendored path, SHA-256, format, and BSD-3-Clause license.
| Golden generator | Pinned data inputs |
| --- | --- |
| PacketSourceGenerator | `message_template.msg` |
| VisualParamGenerator | `avatar_lad.xml` |
| SkeletonGenerator | `avatar_skeleton.xml` |
| AttentionsGenerator | `attentions.xml`, `attentionsN.xml` |
| TreesGenerator | `trees.xml`, `grass.xml` |
| GenepoolGenerator | `genepool.xml` |
The C# project does not provide the optional `visualparamtemplate.cs` file.
The pinned VisualParam generator therefore uses the template embedded in its
own source; the inventory records that generator source and hash as provenance.
The files under `inputs/` are exact, unmodified snapshots. Their license and
copyright attribution are covered by the repository
[`LICENSE.md`](../LICENSE.md). The generated source manifest repeats each hash
and license in its header and data table.
## Commands
Regenerate every currently registered Rust output using only checked-in data:
```console
cargo run -p libremetaverse-codegen -- generate
```
Verify hashes and reject stale output without writing:
```console
cargo run -p libremetaverse-codegen -- check
```
Maintainers can refresh the vendored snapshots from an explicitly supplied
checkout. The command verifies all six golden generator hashes and all input
hashes before writing, so a different upstream revision is rejected:
```console
cargo run -p libremetaverse-codegen -- vendor ../libremetaverse
```
Generation uses UTF-8 with an optional BOM, normalizes CRLF/CR to LF, rejects
NUL input, reports stable `path:line:column: severity[code]` diagnostics, sorts
manifest records by stable IDs, emits provenance-rich generated headers, and
always terminates output with one LF. Tests generate twice in memory and
require byte identity before comparing with the checked-in file.

View File

@@ -0,0 +1,32 @@
// @generated by libremetaverse-codegen; DO NOT EDIT.
// Regenerate: cargo run -p libremetaverse-codegen -- generate
// Generator: source-manifest
// Source: codegen/inputs/attentions.xml sha256=bdbc4ca02dd3bde78b86e796f7eb4ada0000e47be9a127e35d2b77f3010a20af license=BSD-3-Clause
// Source: codegen/inputs/attentionsN.xml sha256=80055e12a04d05ef5bb16e8c542cd6c218735f8c72f7e48317b8a34282d1ac5b license=BSD-3-Clause
// Source: codegen/inputs/avatar_lad.xml sha256=6f97a1408fa8f0ab5c09043da433c701701fe075e8a97867fcbcda207adf9eaf license=BSD-3-Clause
// Source: codegen/inputs/avatar_skeleton.xml sha256=addb3cc4cd582ed4343d778e06daf90c6b77b61b4ecb4a20d4bf202c2b93908a license=BSD-3-Clause
// Source: codegen/inputs/genepool.xml sha256=6fa1391cd98fddec0d7fe762343ea21e53a655a8ddd4f33af52f8a02aaa3f56d license=BSD-3-Clause
// Source: codegen/inputs/grass.xml sha256=90015f9768afe3cfee761d04239bf9e284b2739b095b7ab8331966fa1fbfcb4e license=BSD-3-Clause
// Source: codegen/inputs/message_template.msg sha256=15e524a46a9a449ead33c26d495a292ae2e100e641a408ac66ef63b8a7a2489d license=BSD-3-Clause
// Source: codegen/inputs/trees.xml sha256=f54e626b00bd2c4fec9f59a574a45860f209e4440bbb02b81d27050b9df8ffba license=BSD-3-Clause
pub const UPSTREAM_COMMIT: &str = "2aa70bb68513b39795da5d13c88f31b86e85a3ba";
pub const UPSTREAM_REPOSITORY: &str = "https://github.com/cinderblocks/libremetaverse";
pub const SOURCES: &[(&str, &str, &str, &str)] = &[
("attentions", "codegen/inputs/attentions.xml", "bdbc4ca02dd3bde78b86e796f7eb4ada0000e47be9a127e35d2b77f3010a20af", "BSD-3-Clause"),
("attentions_updated", "codegen/inputs/attentionsN.xml", "80055e12a04d05ef5bb16e8c542cd6c218735f8c72f7e48317b8a34282d1ac5b", "BSD-3-Clause"),
("avatar_lad", "codegen/inputs/avatar_lad.xml", "6f97a1408fa8f0ab5c09043da433c701701fe075e8a97867fcbcda207adf9eaf", "BSD-3-Clause"),
("avatar_skeleton", "codegen/inputs/avatar_skeleton.xml", "addb3cc4cd582ed4343d778e06daf90c6b77b61b4ecb4a20d4bf202c2b93908a", "BSD-3-Clause"),
("genepool", "codegen/inputs/genepool.xml", "6fa1391cd98fddec0d7fe762343ea21e53a655a8ddd4f33af52f8a02aaa3f56d", "BSD-3-Clause"),
("grass", "codegen/inputs/grass.xml", "90015f9768afe3cfee761d04239bf9e284b2739b095b7ab8331966fa1fbfcb4e", "BSD-3-Clause"),
("message_template", "codegen/inputs/message_template.msg", "15e524a46a9a449ead33c26d495a292ae2e100e641a408ac66ef63b8a7a2489d", "BSD-3-Clause"),
("trees", "codegen/inputs/trees.xml", "f54e626b00bd2c4fec9f59a574a45860f209e4440bbb02b81d27050b9df8ffba", "BSD-3-Clause"),
];
pub const GENERATORS: &[(&str, &str, &str, &str)] = &[
("attentions", "SourceGenerators/AttentionsGenerator/AttentionsGenerator.cs", "b2870ae020c42a8a6ec455363fbb70a1b3c94c814b205bad9b61609d5230da3e", "BSD-3-Clause"),
("genepool", "SourceGenerators/GenepoolGenerator/GenepoolGenerator.cs", "a48e914fc62efbb1bc4b2e17162121621eebd4cf03872cc226bc8a83a9230cd2", "BSD-3-Clause"),
("packets", "SourceGenerators/PacketSourceGenerator/PacketSourceGenerator.cs", "8e63c0356cd0269e10521d15fbcde780cf58f821935601b4f223933930db758f", "BSD-3-Clause"),
("skeleton", "SourceGenerators/SkeletonGenerator/SkeletonGenerator.cs", "6e1a5dd95b2604319cf65037c59b69a739af563e49cfb29e5db00c3d9c371d49", "BSD-3-Clause"),
("trees", "SourceGenerators/TreesGenerator/TreesGenerator.cs", "151a593ba215400361f9bf20848537a5c01bb0ab4a95b15c1e16fa4b1897f8bb", "BSD-3-Clause"),
("visual_params", "SourceGenerators/VisualParamGenerator/VisualParamGenerator.cs", "bff0ec0f823b415e811f0c84210b3ee8d7baa65cc21de19ed19b3c26560c6dac", "BSD-3-Clause"),
];

View File

@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="US-ASCII" standalone="yes"?>
<!--
Default values for "Look At" controls.
Various events compete for avatar attention. I.E. control where the
user's avatar looks.
The avatar's head will turn to look towards the source of those events
as much as is possible.
If the head cannot turn due to an animation override, the eyes will still
track those events.
Avatar attention will be taken by events of equal of greater priorities
and will last for timeout number of seconds before reverting to idle.
Negative timeout values indicate attentions that never time out.
Avatar sex determines which set of attention settings are used.
Within each set, the following named attentions relate to the indicated events:
IDLE: Tracks the mouse pointer movement.
AUTO LISTEN: Tracks nearby chat.
FREELOOK: Tracks "target objects" and a case of mouse moved in 3rd person mode.
RESPOND: Tracks the beginning of typing.
HOVER: Tracks objects the mouse lingers over when "show hover tooltips" is on.
CONVERSATION: Tracks avatars and other objects clicked on.
SEOECT: Tracks objects grabbed and being moved.
FOCUS: Freezes during avatar customization and when focused on object or point.
MOUSELOOK: Tracks center of view when in mouselook view mode.
-->
<linden_attentions version="1.0">
<gender name="Masculine">
<param attention="idle" priority="1.0" timeout="3.0" />
<param attention="auto_listen" priority="3.0" timeout="4.0" />
<param attention="freelook" priority="2.0" timeout="2.0" />
<param attention="respond" priority="3.0" timeout="4.0" />
<param attention="hover" priority="4.0" timeout="1.0" />
<param attention="conversation" priority="0.0" timeout="-1" />
<param attention="select" priority="6.0" timeout="-1" />
<param attention="focus" priority="6.0" timeout="-1" />
<param attention="mouselook" priority="7.0" timeout="-1" />
</gender>
<gender name="Feminine">
<param attention="idle" priority="1.0" timeout="3.0" />
<param attention="auto_listen" priority="3.0" timeout="4.0" />
<param attention="freelook" priority="2.0" timeout="2.0" />
<param attention="respond" priority="3.0" timeout="4.0" />
<param attention="hover" priority="4.0" timeout="1.0" />
<param attention="conversation" priority="0.0" timeout="-1" />
<param attention="select" priority="6.0" timeout="-1" />
<param attention="focus" priority="6.0" timeout="-1" />
<param attention="mouselook" priority="7.0" timeout="-1" />
</gender>
</linden_attentions>

View File

@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="US-ASCII" standalone="yes"?>
<!--
Default values for "Look At" controls.
Various events compete for avatar attention. I.E. control where the
user's avatar looks.
The avatar's head will turn to look towards the source of those events
as much as is possible.
If the head cannot turn due to an animation override, the eyes will still
track those events.
Avatar attention will be taken by events of equal of greater priorities
and will last for timeout number of seconds before reverting to idle.
Negative timeout values indicate attentions that never time out.
Avatar sex determines which set of attention settings are used.
Within each set, the following named attentions relate to the indicated events:
IDLE: Tracks the mouse pointer movement.
AUTO LISTEN: Tracks nearby chat.
FREELOOK: Tracks "target objects" and a case of mouse moved in 3rd person mode.
RESPOND: Tracks the beginning of typing.
HOVER: Tracks objects the mouse lingers over when "show hover tooltips" is on.
CONVERSATION: Tracks avatars and other objects clicked on.
SEOECT: Tracks objects grabbed and being moved.
FOCUS: Freezes during avatar customization and when focused on object or point.
MOUSELOOK: Tracks center of view when in mouselook view mode.
-->
<linden_attentions version="1.0">
<gender name="Masculine">
<param attention="idle" priority="1.0" timeout="3.0" />
<param attention="auto_listen" priority="3.0" timeout="4.0" />
<param attention="freelook" priority="2.0" timeout="2.0" />
<param attention="respond" priority="3.0" timeout="3.0" />
<param attention="hover" priority="4.0" timeout="1.0" />
<param attention="conversation" priority="5.0" timeout="3.0" />
<param attention="select" priority="3.5" timeout="-1" />
<param attention="focus" priority="3.5" timeout="20.0" />
<param attention="mouselook" priority="7.0" timeout="-1" />
</gender>
<gender name="Feminine">
<param attention="idle" priority="1.0" timeout="3.0" />
<param attention="auto_listen" priority="3.0" timeout="4.0" />
<param attention="freelook" priority="2.0" timeout="2.0" />
<param attention="respond" priority="3.0" timeout="6.0" />
<param attention="hover" priority="4.0" timeout="1.0" />
<param attention="conversation" priority="5.0" timeout="6.0" />
<param attention="select" priority="2.5" timeout="-1" />
<param attention="focus" priority="2.5" timeout="10.0" />
<param attention="mouselook" priority="7.0" timeout="-1" />
</gender>
</linden_attentions>

17504
codegen/inputs/avatar_lad.xml Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,232 @@
<linden_skeleton num_bones="133" num_collision_volumes="26" version="2.0">
<bone aliases="hip avatar_mPelvis" connected="false" end="0.000 0.000 0.084" group="Torso" name="mPelvis" pivot="0.000000 0.000000 1.067015" pos="0.000 0.000 1.067" rot="0.000000 0.000000 0.000000" scale="1.000 1.000 1.000" support="base" reposition="true">
<collision_volume end="0.030 0.000 0.095" group="Collision" name="PELVIS" pos="-0.01 0 -0.02" rot="0.000000 8.00000 0.000000" scale="0.12 0.16 0.17" support="base" reposition="true"/>
<collision_volume end="-0.100 0.000 0.000" group="Collision" name="BUTT" pos="-0.06 0 -0.1" rot="0.000000 0.00000 0.000000" scale="0.1 0.1 0.1" support="base" reposition="true"/>
<bone aliases="avatar_mSpine1" connected="true" end="0.000 0.000 -0.084" group="Spine" name="mSpine1" pivot="0.000000 0.000000 0.084073" pos="0.000 0.000 0.084" rot="0.000000 0.000000 0.000000" scale="1.00 1.00 1.00" support="extended">
<bone aliases="avatar_mSpine2" connected="true" end="0.000 0.000 0.084" group="Spine" name="mSpine2" pivot="0.000000 0.000000 -0.084073" pos="0.000 0.000 -0.084" rot="0.000000 0.000000 0.000000" scale="1.00 1.00 1.00" support="extended">
<bone aliases="abdomen avatar_mTorso" connected="true" end="-0.015 0.000 0.205" group="Torso" name="mTorso" pivot="0.000000 0.000000 0.084073" pos="0.000 0.000 0.084" rot="0.000000 0.000000 0.000000" scale="1.000 1.000 1.000" support="base">
<collision_volume end="0.028 0.000 0.094" group="Collision" name="BELLY" pos="0.028 0 0.04" rot="0.000000 8.00000 0.000000" scale="0.09 0.13 0.15" support="base" reposition="true"/>
<collision_volume end="0.000 0.100 0.000" group="Collision" name="LEFT_HANDLE" pos="0.0 0.10 0.058" rot="0.000000 0.00000 0.000000" scale="0.05 0.05 0.05" support="base" reposition="true"/>
<collision_volume end="0.000 -0.100 0.000" group="Collision" name="RIGHT_HANDLE" pos="0.0 -0.10 0.058" rot="0.000000 0.00000 0.000000" scale="0.05 0.05 0.05" support="base" reposition="true"/>
<collision_volume end="-0.100 0.000 0.000" group="Collision" name="LOWER_BACK" pos="0.0 0.0 0.023" rot="0.000000 0.00000 0.000000" scale="0.09 0.13 0.15" support="base" reposition="true"/>
<bone aliases="avatar_mSpine3" connected="true" end="0.015 0.000 -0.205" group="Spine" name="mSpine3" pivot="-0.015368 0.000000 0.204877" pos="-0.015 0.000 0.205" rot="0.000000 0.000000 0.000000" scale="1.00 1.00 1.00" support="extended">
<bone aliases="avatar_mSpine4" connected="true" end="-0.015 0.000 0.205" group="Spine" name="mSpine4" pivot="0.015368 0.000000 -0.204877" pos="0.015 0.000 -0.205" rot="0.000000 0.000000 0.000000" scale="1.00 1.00 1.00" support="extended">
<bone aliases="chest avatar_mChest" connected="true" end="-0.010 0.000 0.250" group="Torso" name="mChest" pivot="-0.015368 0.000000 0.204877" pos="-0.015 0.000 0.205" rot="0.000000 0.000000 0.000000" scale="1.000 1.000 1.000" support="base">
<collision_volume end="-0.096 0.000 0.152" group="Collision" name="CHEST" pos="0.028 0 0.07" rot="0.000000 -10.00000 0.000000" scale="0.11 0.15 0.2" support="base" reposition="true"/>
<collision_volume end="0.080 0.000 -0.006" group="Collision" name="LEFT_PEC" pos="0.119 0.082 0.042" rot="0.000000 4.29000 0.000000" scale="0.05 0.05 0.05" support="base" reposition="true"/>
<collision_volume end="0.080 0.000 -0.006" group="Collision" name="RIGHT_PEC" pos="0.119 -0.082 0.042" rot="0.000000 4.29000 0.000000" scale="0.05 0.05 0.05" support="base" reposition="true"/>
<collision_volume end="-0.100 0.000 0.000" group="Collision" name="UPPER_BACK" pos="0.0 0.0 0.017" rot="0.000000 0.00000 0.000000" scale="0.09 0.13 0.15" support="base" reposition="true"/>
<bone aliases="neck avatar_mNeck" connected="true" end="0.000 0.000 0.077" group="Torso" name="mNeck" pivot="-0.009507 0.000000 0.251108" pos="-0.010 0.000 0.251" rot="0.000000 0.000000 0.000000" scale="1.000 1.000 1.000" support="base">
<collision_volume end="0.000 0.000 0.080" group="Collision" name="NECK" pos="0.0 0 0.02" rot="0.000000 0.000000 0.000000" scale="0.05 0.06 0.08" support="base"/>
<bone aliases="head avatar_mHead" connected="true" end="0.000 0.000 0.079" group="Torso" name="mHead" pivot="0.000000 -0.000000 0.075630" pos="0.000 -0.000 0.076" rot="0.000000 0.000000 0.000000" scale="1.000 1.000 1.000" support="base">
<collision_volume end="0.000 0.000 0.100" group="Collision" name="HEAD" pos="0.02 0 0.07" rot="0.000000 0.000000 0.000000" scale="0.11 0.09 0.12" support="base" reposition="true"/>
<bone aliases="figureHair avatar_mSkull" connected="false" end="0.000 0.000 0.033" group="Extra" name="mSkull" pivot="0.000000 0.000000 0.079000" pos="0.000 0.000 0.079" rot="0.000000 0.000000 0.000000" scale="1.000 1.000 1.000" support="base" reposition="true"/>
<bone aliases="avatar_mEyeRight" connected="false" end="0.025 0.000 0.000" group="Extra" name="mEyeRight" pivot="0.098466 -0.036000 0.079000" pos="0.098 -0.036 0.079" rot="0.000000 0.000000 -0.000000" scale="1.000 1.000 1.000" support="base" reposition="true"/>
<bone aliases="avatar_mEyeLeft" connected="false" end="0.025 0.000 0.000" group="Extra" name="mEyeLeft" pivot="0.098461 0.036000 0.079000" pos="0.098 0.036 0.079" rot="0.000000 -0.000000 0.000000" scale="1.000 1.000 1.000" support="base" reposition="true"/>
<bone aliases="avatar_mFaceRoot" connected="false" end="0.020 0.000 0.000" group="Face" name="mFaceRoot" pivot="0.025000 0.000000 0.045000" pos="0.025 0.000 0.045" rot="0.000000 0.000000 0.000000" scale="1.00 1.00 1.00" support="extended">
<bone aliases="avatar_mFaceEyeAltRight" connected="false" end="0.025 0.000 0.000" group="Face" name="mFaceEyeAltRight" pivot="0.073466 -0.036000 0.0339300" pos="0.073 -0.036 0.034" rot="0.000000 0.000000 0.000000" scale="1.00 1.00 1.00" support="extended" reposition="true"/>
<bone aliases="avatar_mFaceEyeAltLeft" connected="false" end="0.025 0.000 0.000" group="Face" name="mFaceEyeAltLeft" pivot="0.073461 0.036000 0.0339300" pos="0.073 0.036 0.034" rot="0.000000 0.000000 0.000000" scale="1.00 1.00 1.00" support="extended" reposition="true"/>
<bone aliases="avatar_mFaceForeheadLeft" connected="false" end="0.024 0.004 0.018" group="Face" name="mFaceForeheadLeft" pivot="0.061 0.035 0.083" pos="0.061 0.035 0.083" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true"/>
<bone aliases="avatar_mFaceForeheadRight" connected="false" end="0.024 -0.004 0.018" group="Face" name="mFaceForeheadRight" pivot="0.061 -0.035 0.083" pos="0.061 -0.035 0.083" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true"/>
<bone aliases="avatar_mFaceEyebrowOuterLeft" connected="false" end="0.023 0.013 0.000" group="Eyes" name="mFaceEyebrowOuterLeft" pivot="0.064 0.051 0.048" pos="0.064 0.051 0.048" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true"/>
<bone aliases="avatar_mFaceEyebrowCenterLeft" connected="false" end="0.027 0.000 0.000" group="Eyes" name="mFaceEyebrowCenterLeft" pivot="0.070 0.043 0.056" pos="0.070 0.043 0.056" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true"/>
<bone aliases="avatar_mFaceEyebrowInnerLeft" connected="false" end="0.026 0.000 0.000" group="Eyes" name="mFaceEyebrowInnerLeft" pivot="0.075 0.022 0.051" pos="0.075 0.022 0.051" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true"/>
<bone aliases="avatar_mFaceEyebrowOuterRight" connected="false" end="0.023 -0.013 0.000" group="Eyes" name="mFaceEyebrowOuterRight" pivot="0.064 -0.051 0.048" pos="0.064 -0.051 0.048" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true"/>
<bone aliases="avatar_mFaceEyebrowCenterRight" connected="false" end="0.027 0.000 0.000" group="Eyes" name="mFaceEyebrowCenterRight" pivot="0.070 -0.043 0.056" pos="0.070 -0.043 0.056" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true"/>
<bone aliases="avatar_mFaceEyebrowInnerRight" connected="false" end="0.026 0.000 0.000" group="Eyes" name="mFaceEyebrowInnerRight" pivot="0.075 -0.022 0.051" pos="0.075 -0.022 0.051" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true"/>
<bone aliases="avatar_mFaceEyeLidUpperLeft" connected="false" end="0.027 0.000 0.005" group="Eyes" name="mFaceEyeLidUpperLeft" pivot="0.073 0.036 0.034" pos="0.073 0.036 0.034" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true"/>
<bone aliases="avatar_mFaceEyeLidLowerLeft" connected="false" end="0.024 0.000 -0.007" group="Eyes" name="mFaceEyeLidLowerLeft" pivot="0.073 0.036 0.034" pos="0.073 0.036 0.034" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true"/>
<bone aliases="avatar_mFaceEyeLidUpperRight" connected="false" end="0.027 0.000 0.005" group="Eyes" name="mFaceEyeLidUpperRight" pivot="0.073 -0.036 0.034" pos="0.073 -0.036 0.034" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true"/>
<bone aliases="avatar_mFaceEyeLidLowerRight" connected="false" end="0.024 0.000 -0.007" group="Eyes" name="mFaceEyeLidLowerRight" pivot="0.073 -0.036 0.034" pos="0.073 -0.036 0.034" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true"/>
<bone aliases="avatar_mFaceEar1Left" connected="false" end="-0.019 0.018 0.025" group="Ears" name="mFaceEar1Left" pivot="0.000 0.080 0.002" pos="0.000 0.080 0.002" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true">
<bone aliases="avatar_mFaceEar2Left" connected="true" end="0.000 0.000 0.033" group="Ears" name="mFaceEar2Left" pivot="-0.019 0.018 0.025" pos="-0.019 0.018 0.025" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true"/>
</bone>
<bone aliases="avatar_mFaceEar1Right" connected="false" end="-0.019 -0.018 0.025" group="Ears" name="mFaceEar1Right" pivot="0.000 -0.080 0.002" pos="0.000 -0.080 0.002" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true">
<bone aliases="avatar_mFaceEar2Right" connected="true" end="0.000 0.000 0.033" group="Ears" name="mFaceEar2Right" pivot="-0.019 -0.018 0.025" pos="-0.019 -0.018 0.025" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true"/>
</bone>
<bone aliases="avatar_mFaceNoseLeft" connected="false" end="0.015 0.004 0.000" group="Face" name="mFaceNoseLeft" pivot="0.086 0.015 -0.004" pos="0.086 0.015 -0.004" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true"/>
<bone aliases="avatar_mFaceNoseCenter" connected="false" end="0.025 0.000 0.000" group="Face" name="mFaceNoseCenter" pivot="0.102 0.000 0.000" pos="0.102 0.000 0.000" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true"/>
<bone aliases="avatar_mFaceNoseRight" connected="false" end="0.015 -0.004 0.000" group="Face" name="mFaceNoseRight" pivot="0.086 -0.015 -0.004" pos="0.086 -0.015 -0.004" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true"/>
<bone aliases="avatar_mFaceCheekLowerLeft" connected="false" end="0.013 0.030 0.000" group="Face" name="mFaceCheekLowerLeft" pivot="0.050 0.034 -0.031" pos="0.050 0.034 -0.031" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true"/>
<bone aliases="avatar_mFaceCheekUpperLeft" connected="false" end="0.022 0.015 0.000" group="Face" name="mFaceCheekUpperLeft" pivot="0.070 0.034 -0.005" pos="0.070 0.034 -0.005" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true"/>
<bone aliases="avatar_mFaceCheekLowerRight" connected="false" end="0.013 -0.030 0.000" group="Face" name="mFaceCheekLowerRight" pivot="0.050 -0.034 -0.031" pos="0.050 -0.034 -0.031" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true"/>
<bone aliases="avatar_mFaceCheekUpperRight" connected="false" end="0.022 -0.015 0.000" group="Face" name="mFaceCheekUpperRight" pivot="0.070 -0.034 -0.005" pos="0.070 -0.034 -0.005" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true"/>
<bone aliases="avatar_mFaceJaw" connected="false" end="0.059 0.000 -0.039" group="Mouth" name="mFaceJaw" pivot="-0.001 0.000 -0.015" pos="-0.001 0.000 -0.015" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
<bone aliases="avatar_mFaceChin" connected="false" end="0.021 0.000 -0.018" group="Mouth" name="mFaceChin" pivot="0.074 0.000 -0.054" pos="0.074 0.000 -0.054" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true"/>
<bone aliases="avatar_mFaceTeethLower" connected="false" end="0.035 0.000 0.000" group="Mouth" name="mFaceTeethLower" pivot="0.021 0.000 -0.039" pos="0.021 0.000 -0.039" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true">
<bone aliases="avatar_mFaceLipLowerLeft" connected="false" end="0.034 0.017 0.005" group="Lips" name="mFaceLipLowerLeft" pivot="0.045 0.000 0.000" pos="0.045 0.000 0.000" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true"/>
<bone aliases="avatar_mFaceLipLowerRight" connected="false" end="0.034 -0.017 0.005" group="Lips" name="mFaceLipLowerRight" pivot="0.045 0.000 0.000" pos="0.045 0.000 0.000" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true"/>
<bone aliases="avatar_mFaceLipLowerCenter" connected="false" end="0.040 0.000 0.002" group="Lips" name="mFaceLipLowerCenter" pivot="0.045 0.000 0.000" pos="0.045 0.000 0.000" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true"/>
<bone aliases="avatar_mFaceTongueBase" connected="false" end="0.022 0.000 0.007" group="Mouth" name="mFaceTongueBase" pivot="0.039 0.000 0.005" pos="0.039 0.000 0.005" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
<bone aliases="avatar_mFaceTongueTip" connected="true" end="0.010 0.000 0.000" group="Mouth" name="mFaceTongueTip" pivot="0.022 0.000 0.007" pos="0.022 0.000 0.007" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true"/>
</bone>
</bone>
</bone>
<bone aliases="avatar_mFaceJawShaper" connected="false" end="-0.017 0.000 0.000" group="Face" name="mFaceJawShaper" pivot="0.000 0.000 0.000" pos="0.000 0.000 0.000" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true"/>
<bone aliases="avatar_mFaceForeheadCenter" connected="false" end="0.036 0.000 0.000" group="Face" name="mFaceForeheadCenter" pivot="0.069 0.000 0.065" pos="0.069 0.000 0.065" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true"/>
<bone aliases="avatar_mFaceNoseBase" connected="false" end="0.014 0.000 0.000" group="Nose" name="mFaceNoseBase" pivot="0.094 0.000 -0.016" pos="0.094 0.000 -0.016" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true"/>
<bone aliases="avatar_mFaceTeethUpper" connected="false" end="0.035 0.000 0.000" group="Mouth" name="mFaceTeethUpper" pivot="0.020 0.000 -0.030" pos="0.020 0.000 -0.030" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true">
<bone aliases="avatar_mFaceLipUpperLeft" connected="false" end="0.041 0.015 0.000" group="Lips" name="mFaceLipUpperLeft" pivot="0.045 0.000 -0.003" pos="0.045 0.000 -0.003" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true"/>
<bone aliases="avatar_mFaceLipUpperRight" connected="false" end="0.041 -0.015 0.000" group="Lips" name="mFaceLipUpperRight" pivot="0.045 0.000 -0.003" pos="0.045 0.000 -0.003" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true"/>
<bone aliases="avatar_mFaceLipCornerLeft" connected="false" end="0.045 0.051 0.000" group="Lips" name="mFaceLipCornerLeft" pivot="0.028 -0.019 -0.010" pos="0.028 -0.019 -0.010" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true"/>
<bone aliases="avatar_mFaceLipCornerRight" connected="false" end="0.045 -0.051 0.000" group="Lips" name="mFaceLipCornerRight" pivot="0.028 0.019 -0.010" pos="0.028 0.019 -0.010" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true"/>
<bone aliases="avatar_mFaceLipUpperCenter" connected="false" end="0.043 0.000 0.002" group="Lips" name="mFaceLipUpperCenter" pivot="0.045 0.000 -0.003" pos="0.045 0.000 -0.003" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true"/>
</bone>
<bone aliases="avatar_mFaceEyecornerInnerLeft" connected="false" end="0.016 0.000 0.000" group="Face" name="mFaceEyecornerInnerLeft" pivot="0.075 0.017 0.032" pos="0.075 0.017 0.032" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true"/>
<bone aliases="avatar_mFaceEyecornerInnerRight" connected="false" end="0.016 0.000 0.000" group="Face" name="mFaceEyecornerInnerRight" pivot="0.075 -0.017 0.032" pos="0.075 -0.017 0.032" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true"/>
<bone aliases="avatar_mFaceNoseBridge" connected="false" end="0.015 0.000 0.008" group="Nose" name="mFaceNoseBridge" pivot="0.091 0.000 0.020" pos="0.091 0.000 0.020" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true"/>
</bone>
</bone>
</bone>
<bone aliases="lCollar avatar_mCollarLeft" connected="false" end="0.000 0.079 0.000" group="Arms" name="mCollarLeft" pivot="-0.020927 0.084665 0.165396" pos="-0.021 0.085 0.165" rot="0.000000 0.000000 0.000000" scale="1.000 1.000 1.000" support="base">
<collision_volume end="0.000 0.100 0.000" group="Collision" name="L_CLAVICLE" pos="0.02 0 0.02" rot="0.000000 0.00000 0.000000" scale="0.07 0.14 0.05" support="base" reposition="true"/>
<bone aliases="lShldr avatar_mShoulderLeft" connected="true" end="0.000 0.247 0.000" group="Arms" name="mShoulderLeft" pivot="0.000000 0.079000 -0.000000" pos="0.000 0.079 -0.000" rot="0.000000 0.000000 0.000000" scale="1.000 1.000 1.000" support="base">
<collision_volume end="0.000 0.130 -0.003" group="Collision" name="L_UPPER_ARM" pos="0.0 0.12 0.01" rot="-5.000000 0.00000 0.000000" scale="0.05 0.17 0.05" support="base" reposition="true"/>
<bone aliases="lForeArm avatar_mElbowLeft" connected="true" end="0.000 0.205 0.000" group="Arms" name="mElbowLeft" pivot="0.000000 0.248000 0.000000" pos="0.000 0.248 0.000" rot="0.000000 0.000000 0.000000" scale="1.000 1.000 1.000" support="base">
<collision_volume end="0.000 0.100 -0.001" group="Collision" name="L_LOWER_ARM" pos="0.0 0.1 0.0" rot="-3.000000 0.00000 0.000000" scale="0.04 0.14 0.04" support="base" reposition="true"/>
<bone aliases="lHand avatar_mWristLeft" connected="true" end="0.000 0.060 0.000" group="Arms" name="mWristLeft" pivot="-0.000000 0.204846 0.000000" pos="-0.000 0.205 0.000" rot="0.000000 0.000000 0.000000" scale="1.000 1.000 1.000" support="base">
<collision_volume end="0.005 0.049 -0.001" group="Collision" name="L_HAND" pos="0.01 0.05 0.0" rot="-3.000000 0.00000 -10.000000" scale="0.05 0.08 0.03" support="base"/>
<bone aliases="avatar_mHandMiddle1Left" connected="false" end="-0.001 0.040 -0.006" group="Hand" name="mHandMiddle1Left" pivot="0.013 0.101 0.015" pos="0.013 0.101 0.015" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
<bone aliases="avatar_mHandMiddle2Left" connected="true" end="-0.001 0.049 -0.008" group="Hand" name="mHandMiddle2Left" pivot="-0.001 0.040 -0.006" pos="-0.001 0.040 -0.006" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
<bone aliases="avatar_mHandMiddle3Left" connected="true" end="-0.002 0.033 -0.006" group="Hand" name="mHandMiddle3Left" pivot="-0.001 0.049 -0.008" pos="-0.001 0.049 -0.008" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
</bone>
</bone>
<bone aliases="avatar_mHandIndex1Left" connected="false" end="0.017 0.036 -0.006" group="Hand" name="mHandIndex1Left" pivot="0.038 0.097 0.015" pos="0.038 0.097 0.015" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
<bone aliases="avatar_mHandIndex2Left" connected="true" end="0.014 0.032 -0.006" group="Hand" name="mHandIndex2Left" pivot="0.017 0.036 -0.006" pos="0.017 0.036 -0.006" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
<bone aliases="avatar_mHandIndex3Left" connected="true" end="0.011 0.025 -0.004" group="Hand" name="mHandIndex3Left" pivot="0.014 0.032 -0.006" pos="0.014 0.032 -0.006" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
</bone>
</bone>
<bone aliases="avatar_mHandRing1Left" connected="false" end="-0.013 0.038 -0.008" group="Hand" name="mHandRing1Left" pivot="-0.010 0.099 0.009" pos="-0.010 0.099 0.009" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
<bone aliases="avatar_mHandRing2Left" connected="true" end="-0.013 0.040 -0.009" group="Hand" name="mHandRing2Left" pivot="-0.013 0.038 -0.008" pos="-0.013 0.038 -0.008" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
<bone aliases="avatar_mHandRing3Left" connected="true" end="-0.010 0.028 -0.006" group="Hand" name="mHandRing3Left" pivot="-0.013 0.040 -0.009" pos="-0.013 0.040 -0.009" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
</bone>
</bone>
<bone aliases="avatar_mHandPinky1Left" connected="false" end="-0.024 0.025 -0.006" group="Hand" name="mHandPinky1Left" pivot="-0.031 0.095 0.003" pos="-0.031 0.095 0.003" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
<bone aliases="avatar_mHandPinky2Left" connected="true" end="-0.015 0.018 -0.004" group="Hand" name="mHandPinky2Left" pivot="-0.024 0.025 -0.006" pos="-0.024 0.025 -0.006" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
<bone aliases="avatar_mHandPinky3Left" connected="true" end="-0.013 0.016 -0.004" group="Hand" name="mHandPinky3Left" pivot="-0.015 0.018 -0.004" pos="-0.015 0.018 -0.004" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
</bone>
</bone>
<bone aliases="avatar_mHandThumb1Left" connected="false" end="0.028 0.032 0.000" group="Hand" name="mHandThumb1Left" pivot="0.031 0.026 0.004" pos="0.031 0.026 0.004" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
<bone aliases="avatar_mHandThumb2Left" connected="true" end="0.023 0.031 0.000" group="Hand" name="mHandThumb2Left" pivot="0.028 0.032 -0.001" pos="0.028 0.032 -0.001" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
<bone aliases="avatar_mHandThumb3Left" connected="true" end="0.015 0.025 0.000" group="Hand" name="mHandThumb3Left" pivot="0.023 0.031 -0.001" pos="0.023 0.031 -0.001" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
</bone>
</bone>
</bone>
</bone>
</bone>
</bone>
<bone aliases="rCollar avatar_mCollarRight" connected="false" end="0.000 -0.079 0.000" group="Arms" name="mCollarRight" pivot="-0.020927 -0.085000 0.165396" pos="-0.021 -0.085 0.165" rot="0.000000 0.000000 0.000000" scale="1.000 1.000 1.000" support="base">
<collision_volume end="0.000 -0.100 0.000" group="Collision" name="R_CLAVICLE" pos="0.02 0 0.02" rot="0.000000 0.00000 0.000000" scale="0.07 0.14 0.05" support="base" reposition="true"/>
<bone aliases="rShldr avatar_mShoulderRight" connected="true" end="0.000 -0.247 0.000" group="Arms" name="mShoulderRight" pivot="0.000000 -0.079418 -0.000000" pos="0.000 -0.079 -0.000" rot="0.000000 0.000000 0.000000" scale="1.000 1.000 1.000" support="base">
<collision_volume end="0.000 -0.130 -0.003" group="Collision" name="R_UPPER_ARM" pos="0.0 -0.12 0.01" rot="5.000000 0.00000 0.000000" scale="0.05 0.17 0.05" support="base" reposition="true"/>
<bone aliases="rForeArm avatar_mElbowRight" connected="true" end="0.000 -0.205 0.000" group="Arms" name="mElbowRight" pivot="0.000000 -0.248000 -0.000000" pos="0.000 -0.248 -0.000" rot="0.000000 0.000000 0.000000" scale="1.000 1.000 1.000" support="base">
<collision_volume end="0.000 -0.100 -0.001" group="Collision" name="R_LOWER_ARM" pos="0.0 -0.1 0.0" rot="3.000000 0.00000 0.000000" scale="0.04 0.14 0.04" support="base" reposition="true"/>
<bone aliases="rHand avatar_mWristRight" connected="true" end="0.000 -0.060 0.000" group="Arms" name="mWristRight" pivot="-0.000000 -0.205000 -0.000000" pos="0.000 -0.205 -0.000" rot="0.000000 0.000000 0.000000" scale="1.000 1.000 1.000" support="base">
<collision_volume end="0.005 -0.049 -0.001" group="Collision" name="R_HAND" pos="0.01 -0.05 0.0" rot="3.000000 0.00000 10.000000" scale="0.05 0.08 0.03" support="base"/>
<bone aliases="avatar_mHandMiddle1Right" connected="false" end="-0.001 -0.040 -0.006" group="Hand" name="mHandMiddle1Right" pivot="0.013 -0.101 0.015" pos="0.013 -0.101 0.015" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
<bone aliases="avatar_mHandMiddle2Right" connected="true" end="-0.001 -0.049 -0.008" group="Hand" name="mHandMiddle2Right" pivot="-0.001 -0.040 -0.006" pos="-0.001 -0.040 -0.006" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
<bone aliases="avatar_mHandMiddle3Right" connected="true" end="-0.002 -0.033 -0.006" group="Hand" name="mHandMiddle3Right" pivot="-0.001 -0.049 -0.008" pos="-0.001 -0.049 -0.008" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
</bone>
</bone>
<bone aliases="avatar_mHandIndex1Right" connected="false" end="0.017 -0.036 -0.006" group="Hand" name="mHandIndex1Right" pivot="0.038 -0.097 0.015" pos="0.038 -0.097 0.015" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
<bone aliases="avatar_mHandIndex2Right" connected="true" end="0.014 -0.032 -0.006" group="Hand" name="mHandIndex2Right" pivot="0.017 -0.036 -0.006" pos="0.017 -0.036 -0.006" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
<bone aliases="avatar_mHandIndex3Right" connected="true" end="0.011 -0.025 -0.004" group="Hand" name="mHandIndex3Right" pivot="0.014 -0.032 -0.006" pos="0.014 -0.032 -0.006" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
</bone>
</bone>
<bone aliases="avatar_mHandRing1Right" connected="false" end="-0.013 -0.038 -0.008" group="Hand" name="mHandRing1Right" pivot="-0.010 -0.099 0.009" pos="-0.010 -0.099 0.009" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
<bone aliases="avatar_mHandRing2Right" connected="true" end="-0.013 -0.040 -0.009" group="Hand" name="mHandRing2Right" pivot="-0.013 -0.038 -0.008" pos="-0.013 -0.038 -0.008" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
<bone aliases="avatar_mHandRing3Right" connected="true" end="-0.010 -0.028 -0.006" group="Hand" name="mHandRing3Right" pivot="-0.013 -0.040 -0.009" pos="-0.013 -0.040 -0.009" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
</bone>
</bone>
<bone aliases="avatar_mHandPinky1Right" connected="false" end="-0.024 -0.025 -0.006" group="Hand" name="mHandPinky1Right" pivot="-0.031 -0.095 0.003" pos="-0.031 -0.095 0.003" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
<bone aliases="avatar_mHandPinky2Right" connected="true" end="-0.015 -0.018 -0.004" group="Hand" name="mHandPinky2Right" pivot="-0.024 -0.025 -0.006" pos="-0.024 -0.025 -0.006" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
<bone aliases="avatar_mHandPinky3Right" connected="true" end="-0.013 -0.016 -0.004" group="Hand" name="mHandPinky3Right" pivot="-0.015 -0.018 -0.004" pos="-0.015 -0.018 -0.004" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
</bone>
</bone>
<bone aliases="avatar_mHandThumb1Right" connected="false" end="0.028 -0.032 0.000" group="Hand" name="mHandThumb1Right" pivot="0.031 -0.026 0.004" pos="0.031 -0.026 0.004" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
<bone aliases="avatar_mHandThumb2Right" connected="true" end="0.023 -0.031 0.000" group="Hand" name="mHandThumb2Right" pivot="0.028 -0.032 -0.001" pos="0.028 -0.032 -0.001" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
<bone aliases="avatar_mHandThumb3Right" connected="true" end="0.015 -0.025 0.000" group="Hand" name="mHandThumb3Right" pivot="0.023 -0.031 -0.001" pos="0.023 -0.031 -0.001" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
</bone>
</bone>
</bone>
</bone>
</bone>
</bone>
<bone aliases="avatar_mWingsRoot" connected="false" end="-0.061 0.000 0.000" group="Wing" name="mWingsRoot" pivot="-0.014 0.000 0.000" pos="-0.014 0.000 0.000" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
<bone aliases="avatar_mWing1Left" connected="false" end="-0.168 0.169 0.067" group="Wing" name="mWing1Left" pivot="-0.099 0.105 0.181" pos="-0.099 0.105 0.181" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true">
<bone aliases="avatar_mWing2Left" connected="true" end="-0.181 0.183 0.000" group="Wing" name="mWing2Left" pivot="-0.168 0.169 0.067" pos="-0.168 0.169 0.067" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true">
<bone aliases="avatar_mWing3Left" connected="true" end="-0.171 0.173 0.000" group="Wing" name="mWing3Left" pivot="-0.181 0.183 0.000" pos="-0.181 0.183 0.000" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true">
<bone aliases="avatar_mWing4Left" connected="true" end="-0.146 0.132 0.000" group="Wing" name="mWing4Left" pivot="-0.171 0.173 0.000" pos="-0.171 0.173 0.000" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true"/>
<bone aliases="avatar_mWing4FanLeft" connected="true" end="-0.068 0.062 -0.159" group="Wing" name="mWing4FanLeft" pivot="-0.171 0.173 0.000" pos="-0.171 0.173 0.000" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true"/>
</bone>
</bone>
</bone>
<bone aliases="avatar_mWing1Right" connected="false" end="-0.168 -0.169 0.067" group="Wing" name="mWing1Right" pivot="-0.099 -0.105 0.181" pos="-0.099 -0.105 0.181" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true">
<bone aliases="avatar_mWing2Right" connected="true" end="-0.181 -0.183 0.000" group="Wing" name="mWing2Right" pivot="-0.168 -0.169 0.067" pos="-0.168 -0.169 0.067" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true">
<bone aliases="avatar_mWing3Right" connected="true" end="-0.171 -0.173 0.000" group="Wing" name="mWing3Right" pivot="-0.181 -0.183 0.000" pos="-0.181 -0.183 0.000" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true">
<bone aliases="avatar_mWing4Right" connected="true" end="-0.146 -0.132 0.000" group="Wing" name="mWing4Right" pivot="-0.171 -0.173 0.000" pos="-0.171 -0.173 0.000" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true"/>
<bone aliases="avatar_mWing4FanRight" connected="true" end="-0.068 -0.062 -0.159" group="Wing" name="mWing4FanRight" pivot="-0.171 -0.173 0.000" pos="-0.171 -0.173 0.000" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true"/>
</bone>
</bone>
</bone>
</bone>
</bone>
</bone>
</bone>
</bone>
</bone>
</bone>
<bone aliases="rThigh avatar_mHipRight" connected="false" end="-0.001 0.049 -0.491" group="Legs" name="mHipRight" pivot="0.033620 -0.128806 -0.041086" pos="0.034 -0.129 -0.041" rot="0.000000 0.000000 0.000000" scale="1.000 1.000 1.000" support="base" reposition="true">
<collision_volume end="0.000 0.000 -0.200" group="Collision" name="R_UPPER_LEG" pos="-0.02 0.05 -0.22" rot="0.000000 0.00000 0.000000" scale="0.09 0.09 0.32" support="base" reposition="true"/>
<bone aliases="rShin avatar_mKneeRight" connected="true" end="-0.029 0.000 -0.469" group="Legs" name="mKneeRight" pivot="-0.000780 0.048635 -0.490922" pos="-0.001 0.049 -0.491" rot="0.000000 0.000000 0.000000" scale="1.000 1.000 1.000" support="base">
<collision_volume end="-0.010 0.000 -0.150" group="Collision" name="R_LOWER_LEG" pos="-0.02 0.0 -0.2" rot="0.000000 0.00000 0.000000" scale="0.06 0.06 0.25" support="base" reposition="true"/>
<bone aliases="rFoot avatar_mAnkleRight" connected="true" end="0.112 0.000 -0.061" group="Legs" name="mAnkleRight" pivot="-0.028869 0.000000 -0.468494" pos="-0.029 0.000 -0.468" rot="0.000000 0.000000 0.000000" scale="1.000 1.000 1.000" support="base">
<collision_volume end="0.089 0.000 -0.026" group="Collision" name="R_FOOT" pos="0.077 0.0 -0.041" rot="0.000000 10.00000 0.000000" scale="0.13 0.05 0.05" support="base" reposition="true"/>
<bone aliases="avatar_mFootRight" connected="true" end="0.105 -0.010 0.000" group="Extra" name="mFootRight" pivot="0.111956 -0.000000 -0.060637" pos="0.112 -0.000 -0.061" rot="0.000000 0.000000 0.000000" scale="1.000 1.000 1.000" support="base" reposition="true">
<bone aliases="avatar_mToeRight" connected="false" end="0.020 0.000 0.000" group="Extra" name="mToeRight" pivot="0.105399 -0.010408 -0.000104" pos="0.109 0.000 0.000" rot="0.000000 0.000000 0.000000" scale="1.000 1.000 1.000" support="base"/>
</bone>
</bone>
</bone>
</bone>
<bone aliases="lThigh avatar_mHipLeft" connected="false" end="-0.001 -0.046 -0.491" group="Legs" name="mHipLeft" pivot="0.033757 0.126765 -0.040998" pos="0.034 0.127 -0.041" rot="0.000000 0.000000 0.000000" scale="1.000 1.000 1.000" support="base" reposition="true">
<collision_volume end="0.000 0.000 -0.200" group="Collision" name="L_UPPER_LEG" pos="-0.02 -0.05 -0.22" rot="0.000000 0.00000 0.000000" scale="0.09 0.09 0.32" support="base" reposition="true"/>
<bone aliases="lShin avatar_mKneeLeft" connected="true" end="-0.029 0.001 -0.469" group="Legs" name="mKneeLeft" pivot="-0.000887 -0.045568 -0.491053" pos="-0.001 -0.046 -0.491" rot="0.000000 0.000000 0.000000" scale="1.000 1.000 1.000" support="base">
<collision_volume end="-0.010 0.000 -0.150" group="Collision" name="L_LOWER_LEG" pos="-0.02 0.0 -0.2" rot="0.000000 0.00000 0.000000" scale="0.06 0.06 0.25" support="base" reposition="true"/>
<bone aliases="lFoot avatar_mAnkleLeft" connected="true" end="0.112 0.000 -0.061" group="Legs" name="mAnkleLeft" pivot="-0.028887 0.001378 -0.468449" pos="-0.029 0.001 -0.468" rot="0.000000 0.000000 0.000000" scale="1.000 1.000 1.000" support="base">
<collision_volume end="0.089 0.000 -0.026" group="Collision" name="L_FOOT" pos="0.077 0.0 -0.041" rot="0.000000 10.00000 0.000000" scale="0.13 0.05 0.05" support="base" reposition="true"/>
<bone aliases="avatar_mFootLeft" connected="true" end="0.105 0.008 0.001" group="Extra" name="mFootLeft" pivot="0.111956 -0.000000 -0.060620" pos="0.112 -0.000 -0.061" rot="0.000000 0.000000 0.000000" scale="1.000 1.000 1.000" support="base" reposition="true">
<bone aliases="avatar_mToeLeft" connected="false" end="0.020 0.000 0.000" group="Extra" name="mToeLeft" pivot="0.105387 0.008270 0.000871" pos="0.109 0.000 0.000" rot="0.000000 0.000000 0.000000" scale="1.000 1.000 1.000" support="base"/>
</bone>
</bone>
</bone>
</bone>
<bone aliases="avatar_mTail1" connected="false" end="-0.197 0.000 0.000" group="Tail" name="mTail1" pivot="-0.116 0.000 0.047" pos="-0.116 0.000 0.047" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true">
<bone aliases="avatar_mTail2" connected="true" end="-0.168 0.000 0.000" group="Tail" name="mTail2" pivot="-0.197 0.000 0.000" pos="-0.197 0.000 0.000" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true">
<bone aliases="avatar_mTail3" connected="true" end="-0.142 0.000 0.000" group="Tail" name="mTail3" pivot="-0.168 0.000 0.000" pos="-0.168 0.000 0.000" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true">
<bone aliases="avatar_mTail4" connected="true" end="-0.112 0.000 0.000" group="Tail" name="mTail4" pivot="-0.142 0.000 0.000" pos="-0.142 0.000 0.000" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true">
<bone aliases="avatar_mTail5" connected="true" end="-0.094 0.000 0.000" group="Tail" name="mTail5" pivot="-0.112 0.000 0.000" pos="-0.112 0.000 0.000" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true">
<bone aliases="avatar_mTail6" connected="true" end="-0.089 0.000 0.000" group="Tail" name="mTail6" pivot="-0.094 0.000 0.000" pos="-0.094 0.000 0.000" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true"/>
</bone>
</bone>
</bone>
</bone>
</bone>
<bone aliases="avatar_mGroin" connected="false" end="0.004 0.000 -0.066" group="Groin" name="mGroin" pivot="0.064 0.000 -0.097" pos="0.064 0.000 -0.097" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
<bone aliases="avatar_mHindLimbsRoot" connected="false" end="-0.204 0.000 0.000" group="Limb" name="mHindLimbsRoot" pivot="-0.200 0.000 0.084" pos="-0.200 0.000 0.084" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
<bone aliases="avatar_mHindLimb1Left" connected="false" end="0.002 -0.046 -0.491" group="Limb" name="mHindLimb1Left" pivot="-0.204 0.129 -0.125" pos="-0.204 0.129 -0.125" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true">
<bone aliases="avatar_mHindLimb2Left" connected="true" end="-0.030 -0.003 -0.468" group="Limb" name="mHindLimb2Left" pivot="0.002 -0.046 -0.491" pos="0.002 -0.046 -0.491" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
<bone aliases="avatar_mHindLimb3Left" connected="true" end="0.112 0.000 -0.061" group="Limb" name="mHindLimb3Left" pivot="-0.030 -0.003 -0.468" pos="-0.030 -0.003 -0.468" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
<bone aliases="avatar_mHindLimb4Left" connected="true" end="0.105 0.008 0.000" group="Limb" name="mHindLimb4Left" pivot="0.112 0.000 -0.061" pos="0.112 0.000 -0.061" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true"/>
</bone>
</bone>
</bone>
<bone aliases="avatar_mHindLimb1Right" connected="false" end="0.002 0.046 -0.491" group="Limb" name="mHindLimb1Right" pivot="-0.204 -0.129 -0.125" pos="-0.204 -0.129 -0.125" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true">
<bone aliases="avatar_mHindLimb2Right" connected="true" end="-0.030 0.003 -0.468" group="Limb" name="mHindLimb2Right" pivot="0.002 0.046 -0.491" pos="0.002 0.046 -0.491" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
<bone aliases="avatar_mHindLimb3Right" connected="true" end="0.112 0.000 -0.061" group="Limb" name="mHindLimb3Right" pivot="-0.030 0.003 -0.468" pos="-0.030 0.003 -0.468" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
<bone aliases="avatar_mHindLimb4Right" connected="true" end="0.105 -0.008 0.000" group="Limb" name="mHindLimb4Right" pivot="0.112 0.000 -0.061" pos="0.112 0.000 -0.061" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended" reposition="true"/>
</bone>
</bone>
</bone>
</bone>
</bone>
</linden_skeleton>

7107
codegen/inputs/genepool.xml Normal file

File diff suppressed because it is too large Load Diff

45
codegen/inputs/grass.xml Normal file
View File

@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<grass_defs>
<grass
name="Grass 0"
species_id="0"
texture_id="6c4727b8-ac79-ba44-3b81-f9aa887b47eb"
blade_size_x="1.35"
blade_size_y="1.35"
/>
<grass
name="Grass 1"
species_id="1"
texture_id="79504bf5-c3ec-0763-6563-d843de66d0a1"
blade_size_x="1.0"
blade_size_y="0.66"
/>
<grass
name="Grass 2"
species_id="2"
texture_id="6c4727b8-ac79-ba44-3b81-f9aa887b47eb"
blade_size_x="1.8"
blade_size_y="1.8"
/>
<grass
name="Grass 3"
species_id="3"
texture_id="99bd60a2-3250-efc9-2e39-2fbcadefbecc"
blade_size_x="1.0"
blade_size_y="1.0"
/>
<grass
name="Grass 4"
species_id="4"
texture_id="7a2b3a4a-53c2-53ac-5716-aac7d743c020"
blade_size_x="2.25"
blade_size_y="2.25"
/>
<grass
name="undergrowth_1"
species_id="5"
texture_id="8f458549-173b-23ff-d4ff-bfaa5ea2371b"
blade_size_x="2.0"
blade_size_y="2.0"
/>
</grass_defs>

File diff suppressed because it is too large Load Diff

24
codegen/inputs/trees.xml Normal file
View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<tree_defs>
<tree name="Pine 1" species_id="0" texture_id="0187babf-6c0d-5891-ebed-4ecab1426683" droop="60.0" twist="5.0" branches="5.0" depth="1" scale_step="0.7" trunk_depth="6" branch_length="8.0" trunk_length="11.5" leaf_scale="22.0" billboard_scale="39.5" billboard_ratio="1.1" trunk_aspect="0.1" branch_aspect="0.05" leaf_rotate="20.0" noise_mag="0.5" noise_scale="2.5" taper="0.8" repeat_z="3" />
<tree name="Oak" species_id="1" texture_id="8a515889-eac9-fb55-8eba-d2dc09eb32c8" droop="35.0" twist="3.0" branches="4.0" depth="3" scale_step="0.7" trunk_depth="0" branch_length="3.0" trunk_length="3.8" leaf_scale="7.0" billboard_scale="10.25" billboard_ratio="1.0" trunk_aspect="0.15" branch_aspect="0.07" leaf_rotate="0.0" noise_mag="1.2" noise_scale="4.0" taper="0.3" repeat_z="4" />
<tree name="Tropical Bush 1" species_id="2" texture_id="5bc11cd6-2f40-071e-a8da-0903394204f9" droop="10.0" twist="0.0" branches="6.0" depth="1" scale_step="0.5" trunk_depth="1" branch_length="0.5" trunk_length="0.15" leaf_scale="7.5" billboard_scale="5.0" billboard_ratio="1.25" trunk_aspect="1.0" branch_aspect="0.08" leaf_rotate="0.0" noise_mag="1.0" noise_scale="1.0" taper="0.2" repeat_z="1" />
<tree name="Palm 1" species_id="3" texture_id="ca4e8c27-473c-eb1c-2f5d-50ee3f07d85c" droop="0.0" twist="0.0" branches="3.0" depth="1" scale_step="0.5" trunk_depth="0" branch_length="0.7" trunk_length="9.0" leaf_scale="10.0" billboard_scale="13.25" billboard_ratio="1.0" trunk_aspect="0.035" branch_aspect="0.03" leaf_rotate="0.0" noise_mag="0.2" noise_scale="6.0" taper="0.7" repeat_z="10" />
<tree name="Dogwood" species_id="4" texture_id="64367bd1-697e-b3e6-0b65-3f862a577366" droop="30.0" twist="0.0" branches="3.0" depth="2" scale_step="0.7" trunk_depth="1" branch_length="2.75" trunk_length="4.0" leaf_scale="5.5" billboard_scale="10.0" billboard_ratio="1.0" trunk_aspect="0.06" branch_aspect="0.05" leaf_rotate="0.0" noise_mag="1.5" noise_scale="2.0" taper="0.8" repeat_z="3" />
<tree name="Tropical Bush 2" species_id="5" texture_id="cdd9a9fc-6d0b-f90d-8416-c72b6019bca8" droop="10.0" twist="0.0" branches="3.0" depth="1" scale_step="0.5" trunk_depth="1" branch_length="0.5" trunk_length="0.15" leaf_scale="6.0" billboard_scale="4.5" billboard_ratio="0.9" trunk_aspect="1.0" branch_aspect="0.08" leaf_rotate="0.0" noise_mag="1.0" noise_scale="1.0" taper="0.2" repeat_z="1" />
<tree name="Palm 2" species_id="6" texture_id="2d784476-d0db-9979-0cff-9408745a7cf3" droop="0.0" twist="0.0" branches="3.0" depth="1" scale_step="0.5" trunk_depth="0" branch_length="0.7" trunk_length="10.0" leaf_scale="7.5" billboard_scale="13.5" billboard_ratio="1.0" trunk_aspect="0.035" branch_aspect="0.03" leaf_rotate="0.0" noise_mag="0.2" noise_scale="6.0" taper="0.6" repeat_z="12" />
<tree name="Cypress 1" species_id="7" texture_id="fb2ae204-3fd1-df33-594f-c9f882830e66" droop="30.0" twist="0.0" branches="3.0" depth="4" scale_step="0.5" trunk_depth="0" branch_length="10.0" trunk_length="10.0" leaf_scale="70.0" billboard_scale="22.5" billboard_ratio="1.0" trunk_aspect="0.05" branch_aspect="0.03" leaf_rotate="0.0" noise_mag="1.2" noise_scale="1.0" taper="0.5" repeat_z="6" />
<tree name="Cypress 2" species_id="8" texture_id="30047cec-269d-408e-0c30-b2603b887268" droop="30.0" twist="0.0" branches="3.0" depth="4" scale_step="0.6" trunk_depth="3.0" branch_length="7.5" trunk_length="10.0" leaf_scale="35.0" billboard_scale="25.0" billboard_ratio="0.8" trunk_aspect="0.05" branch_aspect="0.04" leaf_rotate="0.0" noise_mag="1.2" noise_scale="1.0" taper="0.5" repeat_z="5" />
<tree name="Pine 2" species_id="9" texture_id="d691a01c-13b7-578d-57c0-5caef0b4e7e1" droop="50.0" twist="7.5" branches="4.0" depth="2" scale_step="0.7" trunk_depth="6.0" branch_length="6.0" trunk_length="10.0" leaf_scale="15.5" billboard_scale="33.0" billboard_ratio="1.35" trunk_aspect="0.1" branch_aspect="0.08" leaf_rotate="5.0" noise_mag="0.5" noise_scale="2.5" taper="0.7" repeat_z="3" />
<tree name="Plumeria" species_id="10" texture_id="6de37e4e-7029-61f5-54b8-f5e63f983f58" droop="8.0" twist="7.0" branches="3.0" depth="2" scale_step="0.6" trunk_depth="0" branch_length="3.0" trunk_length="0.1" leaf_scale="20" billboard_scale="10.0" billboard_ratio="1.35" trunk_aspect="0.10" branch_aspect="0.075" leaf_rotate="0.0" noise_mag="0" noise_scale="0" taper="0.85" repeat_z="2" />
<tree name="Winter Pine 1" species_id="11" texture_id="10d2a01a-0818-84b9-4b96-c2eb63256519" droop="90.0" twist="2.5" branches="6.0" depth="1" scale_step="0.66" trunk_depth="8" branch_length="0.0" trunk_length="4" leaf_scale="6.75" billboard_scale="12.5" billboard_ratio="0.6" trunk_aspect="0.1" branch_aspect="0.05" leaf_rotate="0.0" noise_mag="0.0" noise_scale="2.5" taper="0.85" repeat_z="2" />
<tree name="Winter Aspen" species_id="12" texture_id="7c0cf89b-44b1-1ce2-dd74-07102a98ac2a" droop="85.0" twist="3.0" branches="5" depth="1" scale_step="0.6" trunk_depth="8" branch_length="3.0" trunk_length="4.5" leaf_scale="8" billboard_scale="12" billboard_ratio=".675" trunk_aspect="0.06" branch_aspect="0.05" leaf_rotate="0.0" noise_mag="0.75" noise_scale="2.5" taper="0.8" repeat_z="2" />
<tree name="Winter Pine 2" species_id="13" texture_id="67931331-0c02-4876-1255-28770896c6a2" droop="140.0" twist="5.0" branches="6.0" depth="1" scale_step="0.6" trunk_depth="7" branch_length="0.0" trunk_length="3" leaf_scale="5.0" billboard_scale="7.5" billboard_ratio="0.5" trunk_aspect="0.1" branch_aspect="0.05" leaf_rotate="0.0" noise_mag="0.750" noise_scale="2.5" taper="0.5" repeat_z="2" />
<tree name="Eucalyptus" species_id="14" texture_id="a6162133-724b-54df-a12f-51cd070ad6f3" droop="20.0" twist="5.0" branches="3.6" depth="4" scale_step="0.6" trunk_depth="0" branch_length="12.0" trunk_length="8.0" leaf_scale="33.0" billboard_scale="24" billboard_ratio="1.3" trunk_aspect="0.15" branch_aspect="0.08" leaf_rotate="0.0" noise_mag="0" noise_scale="0" taper="0.675" repeat_z="3" />
<tree name="Fern" species_id="15" texture_id="8872f2b8-31db-42d8-580a-b3e4a91262de" droop="12.0" twist="0.0" branches="7.0" depth="1" scale_step="0.5" trunk_depth="0.1" branch_length="0.01" trunk_length="0.0" leaf_scale="4" billboard_scale="3.5" billboard_ratio="0.85" trunk_aspect="1.0" branch_aspect="0.08" leaf_rotate="0.0" noise_mag="1.0" noise_scale="1.0" taper="0.2" repeat_z="1" />
<tree name="Eelgrass" species_id="16" texture_id="96b4de31-f4fa-337d-ec78-451e3609769e" droop="0.0" twist="0.0" branches="5.0" depth="1" scale_step="0.5" trunk_depth="1" branch_length="0.5" trunk_length="0.15" leaf_scale="5.0" billboard_scale="3.0" billboard_ratio="1.0" trunk_aspect="1.0" branch_aspect="0.08" leaf_rotate="0.0" noise_mag="1.0" noise_scale="1.0" taper="0.2" repeat_z="1" />
<tree name="Sea Sword" species_id="17" texture_id="5894e2e7-ab8d-edfa-e61c-18cf16854ba3" droop="0.0" twist="0.0" branches="6.0" depth="1" scale_step="0.7" trunk_depth="1" branch_length="0.0" trunk_length="0.0" leaf_scale="2.0" billboard_scale="2.0" billboard_ratio="1.0" trunk_aspect="1.0" branch_aspect="1.0" leaf_rotate="0.0" noise_mag="0.5" noise_scale="0.0" taper="0.0" repeat_z="1" />
<tree name="Kelp 1" species_id="18" texture_id="2caf1179-7861-6ff3-4b7d-46e17780bdfa" droop="-15.0" twist="0.0" branches="1.0" depth="1" scale_step="1.0" trunk_depth="3" branch_length="2.5" trunk_length="0.75" leaf_scale="1.85" billboard_scale="4.9" billboard_ratio="1.0" trunk_aspect="0.04" branch_aspect="0.05" leaf_rotate="0.0" noise_mag="1.0" noise_scale="2.0" taper="0.8" repeat_z="2" />
<tree name="Beach Grass 1" species_id="19" texture_id="18fb888b-e8f1-dce7-7da7-321d651ea6b0" droop="0.0" twist="0.0" branches="4.0" depth="1" scale_step="0.7" trunk_depth="1" branch_length="0.0" trunk_length="0.0" leaf_scale="4.0" billboard_scale="2.5" billboard_ratio="1.2" trunk_aspect="1.0" branch_aspect="1.0" leaf_rotate="0.0" noise_mag="0.5" noise_scale="0.0" taper="0.0" repeat_z="1" />
<tree name="Kelp 2" species_id="20" texture_id="2a4880b6-b7a3-690a-2049-bfbe38eafb9f" droop="-15.0" twist="0.0" branches="1.0" depth="1" scale_step="1.0" trunk_depth="3" branch_length="2.5" trunk_length="1.35" leaf_scale="2.0" billboard_scale="4.9" billboard_ratio="1.0" trunk_aspect="0.025" branch_aspect="0.05" leaf_rotate="0.0" noise_mag="1.0" noise_scale="2.0" taper="0.8" repeat_z="2" />
</tree_defs>

23
codegen/sources.json Normal file
View File

@@ -0,0 +1,23 @@
{
"schema": 1,
"upstream_commit": "2aa70bb68513b39795da5d13c88f31b86e85a3ba",
"upstream_repository": "https://github.com/cinderblocks/libremetaverse",
"generators": [
{"id":"attentions","reference_source":"SourceGenerators/AttentionsGenerator/AttentionsGenerator.cs","sha256":"b2870ae020c42a8a6ec455363fbb70a1b3c94c814b205bad9b61609d5230da3e","license":"BSD-3-Clause","inputs":["attentions","attentions_updated"]},
{"id":"genepool","reference_source":"SourceGenerators/GenepoolGenerator/GenepoolGenerator.cs","sha256":"a48e914fc62efbb1bc4b2e17162121621eebd4cf03872cc226bc8a83a9230cd2","license":"BSD-3-Clause","inputs":["genepool"]},
{"id":"packets","reference_source":"SourceGenerators/PacketSourceGenerator/PacketSourceGenerator.cs","sha256":"8e63c0356cd0269e10521d15fbcde780cf58f821935601b4f223933930db758f","license":"BSD-3-Clause","inputs":["message_template"]},
{"id":"skeleton","reference_source":"SourceGenerators/SkeletonGenerator/SkeletonGenerator.cs","sha256":"6e1a5dd95b2604319cf65037c59b69a739af563e49cfb29e5db00c3d9c371d49","license":"BSD-3-Clause","inputs":["avatar_skeleton"]},
{"id":"trees","reference_source":"SourceGenerators/TreesGenerator/TreesGenerator.cs","sha256":"151a593ba215400361f9bf20848537a5c01bb0ab4a95b15c1e16fa4b1897f8bb","license":"BSD-3-Clause","inputs":["trees","grass"]},
{"id":"visual_params","reference_source":"SourceGenerators/VisualParamGenerator/VisualParamGenerator.cs","sha256":"bff0ec0f823b415e811f0c84210b3ee8d7baa65cc21de19ed19b3c26560c6dac","license":"BSD-3-Clause","inputs":["avatar_lad"],"note":"The project supplies no visualparamtemplate.cs AdditionalFile; the pinned C# generator uses its embedded template."}
],
"inputs": [
{"id":"message_template","reference_path":"data/message_template.msg","vendored_path":"codegen/inputs/message_template.msg","sha256":"15e524a46a9a449ead33c26d495a292ae2e100e641a408ac66ef63b8a7a2489d","license":"BSD-3-Clause","format":"message-template"},
{"id":"avatar_lad","reference_path":"LibreMetaverse/linden/character/avatar_lad.xml","vendored_path":"codegen/inputs/avatar_lad.xml","sha256":"6f97a1408fa8f0ab5c09043da433c701701fe075e8a97867fcbcda207adf9eaf","license":"BSD-3-Clause","format":"xml"},
{"id":"avatar_skeleton","reference_path":"LibreMetaverse/linden/character/avatar_skeleton.xml","vendored_path":"codegen/inputs/avatar_skeleton.xml","sha256":"addb3cc4cd582ed4343d778e06daf90c6b77b61b4ecb4a20d4bf202c2b93908a","license":"BSD-3-Clause","format":"xml"},
{"id":"attentions","reference_path":"LibreMetaverse/linden/character/attentions.xml","vendored_path":"codegen/inputs/attentions.xml","sha256":"bdbc4ca02dd3bde78b86e796f7eb4ada0000e47be9a127e35d2b77f3010a20af","license":"BSD-3-Clause","format":"xml"},
{"id":"attentions_updated","reference_path":"LibreMetaverse/linden/character/attentionsN.xml","vendored_path":"codegen/inputs/attentionsN.xml","sha256":"80055e12a04d05ef5bb16e8c542cd6c218735f8c72f7e48317b8a34282d1ac5b","license":"BSD-3-Clause","format":"xml"},
{"id":"genepool","reference_path":"LibreMetaverse/linden/character/genepool.xml","vendored_path":"codegen/inputs/genepool.xml","sha256":"6fa1391cd98fddec0d7fe762343ea21e53a655a8ddd4f33af52f8a02aaa3f56d","license":"BSD-3-Clause","format":"xml"},
{"id":"trees","reference_path":"LibreMetaverse/linden/trees.xml","vendored_path":"codegen/inputs/trees.xml","sha256":"f54e626b00bd2c4fec9f59a574a45860f209e4440bbb02b81d27050b9df8ffba","license":"BSD-3-Clause","format":"xml"},
{"id":"grass","reference_path":"LibreMetaverse/linden/grass.xml","vendored_path":"codegen/inputs/grass.xml","sha256":"90015f9768afe3cfee761d04239bf9e284b2739b095b7ab8331966fa1fbfcb4e","license":"BSD-3-Clause","format":"xml"}
]
}

View File

@@ -146,5 +146,5 @@
"LibreMetaverse.Tests/XmlLLSDTests.cs::XmlSDTests.DeserializeUUID::test", "LibreMetaverse.Tests/XmlLLSDTests.cs::XmlSDTests.DeserializeUUID::test",
"LibreMetaverse.Tests/XmlLLSDTests.cs::XmlSDTests.DeserializeUndef::test" "LibreMetaverse.Tests/XmlLLSDTests.cs::XmlSDTests.DeserializeUndef::test"
], ],
"support_passes": 134 "support_passes": 137
} }

17
tools/codegen/Cargo.toml Normal file
View File

@@ -0,0 +1,17 @@
[package]
name = "libremetaverse-codegen"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
license.workspace = true
repository.workspace = true
description = "Deterministic native generators for pinned LibreMetaverse data"
publish = false
[dependencies]
serde = { version = "1", features = ["derive"] }
serde_json = "1"
sha2 = "0.11"
[lints]
workspace = true

365
tools/codegen/src/lib.rs Normal file
View File

@@ -0,0 +1,365 @@
//! Deterministic framework shared by the `LibreMetaverse` data generators.
#![allow(clippy::missing_errors_doc)] // The CLI renders the complete error at its boundary.
#![allow(clippy::must_use_candidate)] // Generator helpers are also exercised for validation.
use serde::Deserialize;
use sha2::{Digest, Sha256};
use std::collections::BTreeSet;
use std::fmt;
use std::fmt::Write as _;
use std::fs;
use std::path::{Component, Path, PathBuf};
pub const INVENTORY_PATH: &str = "codegen/sources.json";
pub const MANIFEST_OUTPUT: &str = "codegen/generated/source_manifest.rs";
#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
pub struct Inventory {
pub schema: u32,
pub upstream_commit: String,
pub upstream_repository: String,
pub generators: Vec<GeneratorSpec>,
pub inputs: Vec<InputSpec>,
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
pub struct GeneratorSpec {
pub id: String,
pub reference_source: String,
pub sha256: String,
pub license: String,
pub inputs: Vec<String>,
#[serde(default)]
pub note: String,
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
pub struct InputSpec {
pub id: String,
pub reference_path: String,
pub vendored_path: String,
pub sha256: String,
pub license: String,
pub format: String,
}
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub enum Severity {
Error,
Warning,
}
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct Diagnostic {
pub code: &'static str,
pub severity: Severity,
pub path: String,
pub line: usize,
pub column: usize,
pub message: String,
}
impl fmt::Display for Diagnostic {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
let severity = match self.severity {
Severity::Error => "error",
Severity::Warning => "warning",
};
write!(
formatter,
"{}:{}:{}: {severity}[{}]: {}",
self.path, self.line, self.column, self.code, self.message
)
}
}
pub fn workspace_root() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR")).join("../..")
}
pub fn normalize_text(path: &str, bytes: &[u8]) -> Result<String, Diagnostic> {
let bytes = bytes.strip_prefix(&[0xef, 0xbb, 0xbf]).unwrap_or(bytes);
let text = std::str::from_utf8(bytes).map_err(|error| {
let valid = &bytes[..error.valid_up_to()];
let (line, column) = line_column(valid);
Diagnostic {
code: "CG001",
severity: Severity::Error,
path: path.replace('\\', "/"),
line,
column,
message: "input is not valid UTF-8".to_owned(),
}
})?;
if let Some(position) = text.as_bytes().iter().position(|byte| *byte == 0) {
let prefix = &text.as_bytes()[..position];
let (line, column) = line_column(prefix);
return Err(Diagnostic {
code: "CG002",
severity: Severity::Error,
path: path.replace('\\', "/"),
line,
column,
message: "input contains a NUL byte".to_owned(),
});
}
Ok(text.replace("\r\n", "\n").replace('\r', "\n"))
}
fn line_column(bytes: &[u8]) -> (usize, usize) {
let mut line = 1;
let mut column = 1;
for byte in bytes {
if *byte == b'\n' {
line += 1;
column = 1;
} else {
column += 1;
}
}
(line, column)
}
pub fn generated_rust(generator: &str, sources: &[&InputSpec], body: &str) -> Vec<u8> {
let mut output = String::new();
output.push_str("// @generated by libremetaverse-codegen; DO NOT EDIT.\n");
output.push_str("// Regenerate: cargo run -p libremetaverse-codegen -- generate\n");
let _ = writeln!(output, "// Generator: {generator}");
for source in sources {
let _ = writeln!(
output,
"// Source: {} sha256={} license={}",
source.vendored_path, source.sha256, source.license
);
}
output.push('\n');
output.push_str(body.trim_end());
output.push('\n');
output.into_bytes()
}
pub fn load_inventory(root: &Path) -> Result<Inventory, String> {
let path = root.join(INVENTORY_PATH);
let bytes = fs::read(&path).map_err(|error| format!("{}: {error}", path.display()))?;
let inventory: Inventory =
serde_json::from_slice(&bytes).map_err(|error| format!("{}: {error}", path.display()))?;
validate_inventory(&inventory)?;
Ok(inventory)
}
fn validate_inventory(inventory: &Inventory) -> Result<(), String> {
if inventory.schema != 1 || inventory.upstream_commit.len() != 40 {
return Err("unsupported or malformed source inventory header".to_owned());
}
let mut input_ids = BTreeSet::new();
for input in &inventory.inputs {
validate_relative(&input.reference_path)?;
validate_relative(&input.vendored_path)?;
validate_hash(&input.sha256)?;
if input.license != "BSD-3-Clause" || !input_ids.insert(&input.id) {
return Err(format!("invalid or duplicate input {}", input.id));
}
}
let mut generator_ids = BTreeSet::new();
for generator in &inventory.generators {
validate_relative(&generator.reference_source)?;
validate_hash(&generator.sha256)?;
if generator.license != "BSD-3-Clause" || !generator_ids.insert(&generator.id) {
return Err(format!("invalid or duplicate generator {}", generator.id));
}
for input in &generator.inputs {
if !input_ids.contains(input) {
return Err(format!(
"generator {} references unknown input {input}",
generator.id
));
}
}
}
Ok(())
}
fn validate_relative(value: &str) -> Result<(), String> {
let path = Path::new(value);
if path.is_absolute()
|| path
.components()
.any(|part| !matches!(part, Component::Normal(_)))
{
return Err(format!("unsafe inventory path {value}"));
}
Ok(())
}
fn validate_hash(value: &str) -> Result<(), String> {
if value.len() == 64 && value.bytes().all(|byte| byte.is_ascii_hexdigit()) {
Ok(())
} else {
Err(format!("invalid SHA-256 {value}"))
}
}
fn sha256(bytes: &[u8]) -> String {
const HEX: &[u8; 16] = b"0123456789abcdef";
let mut output = String::with_capacity(64);
for byte in Sha256::digest(bytes) {
output.push(char::from(HEX[usize::from(byte >> 4)]));
output.push(char::from(HEX[usize::from(byte & 0x0f)]));
}
output
}
pub fn verify_inputs(root: &Path, inventory: &Inventory) -> Result<(), String> {
for input in &inventory.inputs {
let path = root.join(&input.vendored_path);
let bytes = fs::read(&path).map_err(|error| format!("{}: {error}", path.display()))?;
let actual = sha256(&bytes);
if actual != input.sha256 {
return Err(format!(
"{}: SHA-256 mismatch: expected {}, found {actual}",
path.display(),
input.sha256
));
}
normalize_text(&input.vendored_path, &bytes).map_err(|error| error.to_string())?;
}
Ok(())
}
pub fn vendor_inputs(root: &Path, reference: &Path) -> Result<(), String> {
let inventory = load_inventory(root)?;
for generator in &inventory.generators {
let source = reference.join(&generator.reference_source);
let bytes = fs::read(&source).map_err(|error| format!("{}: {error}", source.display()))?;
let actual = sha256(&bytes);
if actual != generator.sha256 {
return Err(format!(
"{}: pinned generator SHA-256 mismatch: expected {}, found {actual}",
source.display(),
generator.sha256
));
}
}
let mut snapshots = Vec::with_capacity(inventory.inputs.len());
for input in &inventory.inputs {
let source = reference.join(&input.reference_path);
let bytes = fs::read(&source).map_err(|error| format!("{}: {error}", source.display()))?;
let actual = sha256(&bytes);
if actual != input.sha256 {
return Err(format!(
"{}: pinned SHA-256 mismatch: expected {}, found {actual}",
source.display(),
input.sha256
));
}
snapshots.push((root.join(&input.vendored_path), bytes));
}
for (target, bytes) in snapshots {
fs::create_dir_all(target.parent().ok_or("vendored input has no parent")?)
.map_err(|error| error.to_string())?;
fs::write(&target, bytes).map_err(|error| format!("{}: {error}", target.display()))?;
}
Ok(())
}
pub fn source_manifest_bytes(root: &Path) -> Result<Vec<u8>, String> {
let inventory = load_inventory(root)?;
verify_inputs(root, &inventory)?;
let mut inputs: Vec<&InputSpec> = inventory.inputs.iter().collect();
inputs.sort_by_key(|input| &input.id);
let mut generators: Vec<&GeneratorSpec> = inventory.generators.iter().collect();
generators.sort_by_key(|generator| &generator.id);
let mut body = String::new();
let _ = writeln!(
body,
"pub const UPSTREAM_COMMIT: &str = {:?};",
inventory.upstream_commit
);
let _ = writeln!(
body,
"pub const UPSTREAM_REPOSITORY: &str = {:?};",
inventory.upstream_repository
);
body.push_str("pub const SOURCES: &[(&str, &str, &str, &str)] = &[\n");
for input in &inputs {
let _ = writeln!(
body,
" ({:?}, {:?}, {:?}, {:?}),",
input.id, input.vendored_path, input.sha256, input.license
);
}
body.push_str("];\n");
body.push_str("pub const GENERATORS: &[(&str, &str, &str, &str)] = &[\n");
for generator in generators {
let _ = writeln!(
body,
" ({:?}, {:?}, {:?}, {:?}),",
generator.id, generator.reference_source, generator.sha256, generator.license
);
}
body.push_str("];\n");
Ok(generated_rust("source-manifest", &inputs, &body))
}
pub fn regenerate(root: &Path, check: bool) -> Result<(), String> {
let expected = source_manifest_bytes(root)?;
let path = root.join(MANIFEST_OUTPUT);
if check {
let actual = fs::read(&path).map_err(|error| format!("{}: {error}", path.display()))?;
if actual != expected {
return Err(format!(
"{} is stale; run the regeneration command",
path.display()
));
}
} else {
fs::create_dir_all(path.parent().ok_or("generated output has no parent")?)
.map_err(|error| error.to_string())?;
fs::write(&path, expected).map_err(|error| format!("{}: {error}", path.display()))?;
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn checked_in_inputs_and_manifest_are_current() {
let root = workspace_root();
let first = source_manifest_bytes(&root).expect("first deterministic generation");
let second = source_manifest_bytes(&root).expect("second deterministic generation");
assert_eq!(first, second);
regenerate(&root, true).expect("checked-in manifest");
}
#[test]
fn text_normalization_and_diagnostics_are_stable() {
assert_eq!(
normalize_text("a\\b.xml", b"\xef\xbb\xbfa\r\nb\r").unwrap(),
"a\nb\n"
);
let error = normalize_text("a\\b.xml", b"one\n\0two").unwrap_err();
assert_eq!(
error.to_string(),
"a/b.xml:2:1: error[CG002]: input contains a NUL byte"
);
}
#[test]
fn inventory_rejects_unsafe_paths_hashes_and_unknown_inputs() {
let root = workspace_root();
let mut inventory = load_inventory(&root).expect("pinned inventory");
inventory.inputs[0].vendored_path = "../escape".to_owned();
assert!(validate_inventory(&inventory).is_err());
let mut inventory = load_inventory(&root).expect("pinned inventory");
inventory.generators[0].sha256 = "not-a-hash".to_owned();
assert!(validate_inventory(&inventory).is_err());
let mut inventory = load_inventory(&root).expect("pinned inventory");
inventory.generators[0].inputs.push("missing".to_owned());
assert!(validate_inventory(&inventory).is_err());
}
}

19
tools/codegen/src/main.rs Normal file
View File

@@ -0,0 +1,19 @@
use libremetaverse_codegen::{regenerate, vendor_inputs, workspace_root};
use std::path::Path;
fn main() {
let arguments: Vec<String> = std::env::args().skip(1).collect();
let root = workspace_root();
let result = match arguments.as_slice() {
[command] if command == "generate" => regenerate(&root, false),
[command] if command == "check" => regenerate(&root, true),
[command, reference] if command == "vendor" => vendor_inputs(&root, Path::new(reference)),
_ => Err(
"usage: libremetaverse-codegen <generate|check|vendor REFERENCE_CHECKOUT>".to_owned(),
),
};
if let Err(error) = result {
eprintln!("{error}");
std::process::exit(2);
}
}