-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
97aedfd
commit fe21801
Showing
28 changed files
with
2,189 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
# Normalize EOL for all files that Git considers text files. | ||
* text=auto eol=lf |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
# Workflow to automatically create deliverables | ||
name: Build on push | ||
|
||
on: | ||
[push, pull_request] | ||
|
||
jobs: | ||
build: | ||
name: Assembling artifacts | ||
runs-on: ubuntu-20.04 | ||
|
||
# Note, to satisfy the asset library we need to make sure our zip files have a root folder | ||
# this is why we checkout into demo/godot_rpm_avatar | ||
# and build plugin/godot_rpm_avatar | ||
steps: | ||
- name: Checkout | ||
uses: actions/checkout@v2 | ||
with: | ||
path: demo/godot_rpm_avatar | ||
- name: Create Godot RPM Avatar plugin | ||
run: | | ||
mkdir plugin | ||
mkdir plugin/godot_rpm_avatar | ||
mkdir plugin/godot_rpm_avatar/addons | ||
cp -r demo/godot_rpm_avatar/addons/godot_rpm_avatar plugin/godot_rpm_avatar/addons | ||
cp demo/godot_rpm_avatar/LICENSE plugin/godot_rpm_avatar/addons/godot_rpm_avatar | ||
cp demo/godot_rpm_avatar/CONTRIBUTORS.md plugin/godot_rpm_avatar/addons/godot_rpm_avatar | ||
cp demo/godot_rpm_avatar/VERSIONS.md plugin/godot_rpm_avatar/addons/godot_rpm_avatar | ||
rm -rf demo/godot_rpm_avatar/.git | ||
rm -rf demo/godot_rpm_avatar/.github | ||
- name: Create Godot RPM Avatar library artifact | ||
uses: actions/upload-artifact@v2 | ||
with: | ||
name: godot_rpm_avatar | ||
path: | | ||
plugin | ||
- name: Create Godot RPM Avatar demo artifact | ||
uses: actions/upload-artifact@v2 | ||
with: | ||
name: godot_rpm_avatar_demo | ||
path: | | ||
demo | ||
- name: Zip asset | ||
run: | | ||
cd plugin | ||
zip -qq -r ../godot_rpm_avatar.zip godot_rpm_avatar | ||
cd ../demo | ||
zip -qq -r ../godot_rpm_avatar_demo.zip godot_rpm_avatar | ||
cd .. | ||
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') | ||
- name: Create and upload asset | ||
uses: ncipollo/release-action@v1 | ||
with: | ||
allowUpdates: true | ||
artifacts: "godot_rpm_avatar.zip,godot_rpm_avatar_demo.zip" | ||
omitNameDuringUpdate: true | ||
omitBodyDuringUpdate: true | ||
token: ${{ secrets.GITHUB_TOKEN }} | ||
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
# Workflow to automatically lint gdscript code | ||
name: gdlint on push | ||
|
||
on: | ||
[push, pull_request] | ||
|
||
jobs: | ||
gdlint: | ||
name: gdlint scripts | ||
runs-on: ubuntu-latest | ||
|
||
steps: | ||
- name: Checkout | ||
uses: actions/checkout@v3 | ||
- name: Set up Python | ||
uses: actions/setup-python@v4 | ||
with: | ||
python-version: '3.10' | ||
- name: Install Dependencies | ||
run: | | ||
python -m pip install --upgrade pip | ||
python -m pip install 'gdtoolkit==4.*' | ||
- name: Lint Godot VMC Tracker | ||
run: | | ||
gdlint addons/godot_rpm_avatar | ||
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# Godot 4+ specific ignores | ||
.godot/ | ||
android/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
Contributors | ||
============ | ||
|
||
The main author of this project is [Malcolm Nixon](https://github.com/Malcolmnixon) who manages the source repository found at: | ||
https://github.com/Malcolmnixon/GodotReadyPlayerMeAvatar | ||
|
||
Other people who have helped out by submitting fixes, enhancements, etc are: | ||
|
||
- TODO | ||
|
||
Want to be on this list? We would love your help. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,32 @@ | ||
# GodotReadyPlayerMeAvatar | ||
Godot ReadyPlayerMe Avatar Reader | ||
# Godot ReadyPlayerMe Avatar | ||
|
||
![GitHub forks](https://img.shields.io/github/forks/Malcolmnixon/GodotReadyPlayerMeAvatar?style=plastic) | ||
![GitHub Repo stars](https://img.shields.io/github/stars/Malcolmnixon/GodotReadyPlayerMeAvatar?style=plastic) | ||
![GitHub contributors](https://img.shields.io/github/contributors/Malcolmnixon/GodotReadyPlayerMeAvatar?style=plastic) | ||
![GitHub](https://img.shields.io/github/license/Malcolmnixon/GodotReadyPlayerMeAvatar?style=plastic) | ||
|
||
This repository contains a ReadyPlayerMe avatar loader for Godot that can | ||
load avatars at runtime from the internet or local files, and can configure | ||
them to be driven through the XR tracker system. | ||
|
||
## Versions | ||
|
||
Official releases are tagged and can be found [here](https://github.com/Malcolmnixon/GodotXRAxisStudioTracker/releases). | ||
|
||
The following branches are in active development: | ||
| Branch | Description | Godot version | | ||
|-----------|-------------------------------|------------------| | ||
| master | Current development branch | Godot 4.3-dev6+ | | ||
|
||
## Licensing | ||
|
||
Code in this repository is licensed under the MIT license. | ||
|
||
## About this repository | ||
|
||
This repository was created by Malcolm Nixon | ||
|
||
It is primarily maintained by: | ||
- [Malcolm Nixon](https://github.com/Malcolmnixon/) | ||
|
||
For further contributors please see `CONTRIBUTORS.md` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
# 1.0.0 | ||
- Initial Revision |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
[plugin] | ||
|
||
name="Godot ReadyPlayerMe Avatar" | ||
description="Godot ReadyPlayerMe Avatar plugin" | ||
author="Malcolm Nixon and Contributors" | ||
version="1.0.0" | ||
script="plugin.gd" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
@tool | ||
extends EditorPlugin | ||
|
||
|
||
func _enter_tree(): | ||
# Register our autoload downloader object | ||
add_autoload_singleton( | ||
"RpmLoader", | ||
"res://addons/godot_rpm_avatar/rpm_loader.gd") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,182 @@ | ||
class_name RpmBody | ||
|
||
|
||
## ReadyPlayerMe Body Script | ||
## | ||
## This script converts ReadyPlayerMe avatars into Godot Humanoid format by | ||
## renaming and rotating the bones. | ||
|
||
|
||
# Mapping from RPM to Godot Humanoid bones | ||
const _RPM_TO_HUMANOID = { | ||
"Hips" : "Hips", | ||
"Spine" : "Spine", | ||
"Spine1" : "Chest", | ||
"Spine2" : "UpperChest", | ||
"Neck" : "Neck", | ||
"Head" : "Head", | ||
"LeftEye" : "LeftEye", | ||
"RightEye" : "RightEye", | ||
"LeftShoulder" : "LeftShoulder", | ||
"LeftArm" : "LeftUpperArm", | ||
"LeftForeArm" : "LeftLowerArm", | ||
"LeftHand" : "LeftHand", | ||
"LeftHandThumb1" : "LeftThumbMetacarpal", | ||
"LeftHandThumb2" : "LeftThumbProximal", | ||
"LeftHandThumb3" : "LeftThumbDistal", | ||
"LeftHandIndex1" : "LeftIndexProximal", | ||
"LeftHandIndex2" : "LeftIndexIntermediate", | ||
"LeftHandIndex3" : "LeftIndexDistal", | ||
"LeftHandMiddle1" : "LeftMiddleProximal", | ||
"LeftHandMiddle2" : "LeftMiddleIntermediate", | ||
"LeftHandMiddle3" : "LeftMiddleDistal", | ||
"LeftHandRing1" : "LeftRingProximal", | ||
"LeftHandRing2" : "LeftRingIntermediate", | ||
"LeftHandRing3" : "LeftRingDistal", | ||
"LeftHandPinky1" : "LeftLittleProximal", | ||
"LeftHandPinky2" : "LeftLittleIntermediate", | ||
"LeftHandPinky3" : "LeftLittleDistal", | ||
"RightShoulder" : "RightShoulder", | ||
"RightArm" : "RightUpperArm", | ||
"RightForeArm" : "RightLowerArm", | ||
"RightHand" : "RightHand", | ||
"RightHandThumb1" : "RightThumbMetacarpal", | ||
"RightHandThumb2" : "RightThumbProximal", | ||
"RightHandThumb3" : "RightThumbDistal", | ||
"RightHandIndex1" : "RightIndexProximal", | ||
"RightHandIndex2" : "RightIndexIntermediate", | ||
"RightHandIndex3" : "RightIndexDistal", | ||
"RightHandMiddle1" : "RightMiddleProximal", | ||
"RightHandMiddle2" : "RightMiddleIntermediate", | ||
"RightHandMiddle3" : "RightMiddleDistal", | ||
"RightHandRing1" : "RightRingProximal", | ||
"RightHandRing2" : "RightRingIntermediate", | ||
"RightHandRing3" : "RightRingDistal", | ||
"RightHandPinky1" : "RightLittleProximal", | ||
"RightHandPinky2" : "RightLittleIntermediate", | ||
"RightHandPinky3" : "RightLittleDistal", | ||
"LeftUpLeg" : "LeftUpperLeg", | ||
"LeftLeg" : "LeftLowerLeg", | ||
"LeftFoot" : "LeftFoot", | ||
"LeftToeBase" : "LeftToes", | ||
"RightUpLeg" : "RightUpperLeg", | ||
"RightLeg" : "RightLowerLeg", | ||
"RightFoot" : "RightFoot", | ||
"RightToeBase" : "RightToes" | ||
} | ||
|
||
|
||
## Retarget a skeleton mesh to conform to the Godot Humanoid standard | ||
static func retarget(src_skeleton : Skeleton3D) -> void: | ||
# Rename the bones to Godot Humanoid | ||
_rename_bones(src_skeleton) | ||
|
||
# Save the original skeleton global rest | ||
var original_global_rest : Array[Transform3D] = [] | ||
for i in src_skeleton.get_bone_count(): | ||
original_global_rest.append(src_skeleton.get_bone_global_rest(i)) | ||
|
||
# Rotate the bones | ||
_rotate_bones(src_skeleton) | ||
|
||
# Fix the skin to counteract the bone rotation | ||
for mesh : MeshInstance3D in src_skeleton.find_children("*", "MeshInstance3D"): | ||
var skin := mesh.skin | ||
if not skin: continue | ||
for i in skin.get_bind_count(): | ||
var bone_name := skin.get_bind_name(i) | ||
var bone_idx := src_skeleton.find_bone(bone_name) | ||
if bone_idx < 0: continue | ||
var adjust_transform := \ | ||
src_skeleton.get_bone_global_rest(bone_idx).affine_inverse() * \ | ||
original_global_rest[bone_idx] | ||
skin.set_bind_pose(i, adjust_transform * skin.get_bind_pose(i)) | ||
|
||
# Move skeleton to rest | ||
_to_rest(src_skeleton) | ||
|
||
|
||
# This method renames the bones in the skeleton to the Godot Humanoid standard | ||
static func _rename_bones(src_skeleton : Skeleton3D) -> void: | ||
# Rename bones from RPM to Godot Humanoid | ||
for i in src_skeleton.get_bone_count(): | ||
var old_name := src_skeleton.get_bone_name(i) | ||
var new_name := _RPM_TO_HUMANOID.get(old_name, "") | ||
if new_name != "": | ||
src_skeleton.set_bone_name(i, new_name) | ||
|
||
# Rename skin binds to match the new bone names | ||
for mesh : MeshInstance3D in src_skeleton.find_children("*", "MeshInstance3D"): | ||
var skin := mesh.skin | ||
if not skin: continue | ||
for i in skin.get_bind_count(): | ||
var old_name := skin.get_bind_name(i) | ||
var new_name := _RPM_TO_HUMANOID.get(old_name, "") | ||
if new_name != "": | ||
skin.set_bind_name(i, new_name) | ||
|
||
|
||
# This method rotates the bones as defined in the Godot Humanoid standard | ||
static func _rotate_bones(src_skeleton : Skeleton3D) -> void: | ||
# Build the Godot Humanoid profile skeleton | ||
var profile := SkeletonProfileHumanoid.new() | ||
var prof_skeleton := Skeleton3D.new() | ||
for i in profile.bone_size: # Create bones | ||
prof_skeleton.add_bone(profile.get_bone_name(i)) | ||
prof_skeleton.set_bone_rest(i, profile.get_reference_pose(i)) | ||
for i in profile.bone_size: # Set bone parents | ||
var parent := profile.find_bone(profile.get_bone_parent(i)) | ||
if parent >= 0: | ||
prof_skeleton.set_bone_parent(i, parent) | ||
|
||
# Save the diffs when rotating | ||
var diffs : Array[Basis] = [] | ||
diffs.resize(src_skeleton.get_bone_count()) | ||
diffs.fill(Basis.IDENTITY) | ||
|
||
# Overwrite the axes | ||
var bones_to_process := src_skeleton.get_parentless_bones() | ||
while bones_to_process.size(): # Walk bones from root to leaf | ||
var src_idx := bones_to_process[0] | ||
bones_to_process.remove_at(0) | ||
bones_to_process.append_array(src_skeleton.get_bone_children(src_idx)) | ||
|
||
# Get the parent global rest | ||
var src_pg := Basis.IDENTITY | ||
var src_parent_idx := src_skeleton.get_bone_parent(src_idx) | ||
if src_parent_idx >= 0: | ||
src_pg = src_skeleton.get_bone_global_rest(src_parent_idx).basis | ||
|
||
# Get the rotation as defined by the profile | ||
var tgt_rot := Basis.IDENTITY | ||
var prof_idx := profile.find_bone(src_skeleton.get_bone_name(src_idx)) | ||
if prof_idx >= 0: | ||
tgt_rot = src_pg.inverse() * prof_skeleton.get_bone_global_rest(prof_idx).basis | ||
|
||
# Save the differences for each bone | ||
if src_parent_idx >= 0: | ||
diffs[src_idx] = \ | ||
tgt_rot.inverse() * \ | ||
diffs[src_parent_idx] * \ | ||
src_skeleton.get_bone_rest(src_idx).basis | ||
else: | ||
diffs[src_idx] = tgt_rot.inverse() * src_skeleton.get_bone_rest(src_idx).basis | ||
|
||
var diff := Basis.IDENTITY | ||
if src_parent_idx >= 0: | ||
diff = diffs[src_parent_idx] | ||
|
||
src_skeleton.set_bone_rest( | ||
src_idx, | ||
Transform3D( | ||
tgt_rot, | ||
diff * src_skeleton.get_bone_rest(src_idx).origin)) | ||
|
||
|
||
# This method moves the skeleton pose to the rest pose | ||
static func _to_rest(src_skeleton : Skeleton3D) -> void: | ||
# Init skeleton pose to new rest | ||
for i in src_skeleton.get_bone_count(): | ||
var fixed_rest := src_skeleton.get_bone_rest(i) | ||
src_skeleton.set_bone_pose_position(i, fixed_rest.origin) | ||
src_skeleton.set_bone_pose_rotation(i, fixed_rest.basis.get_rotation_quaternion()) |
Oops, something went wrong.