Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 24 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ jobs:
run: |
apt-get update
apt-get install --yes --no-install-recommends \
ca-certificates curl git sudo zstd lsb-release pkg-config
ca-certificates curl git sudo zstd lsb-release pkg-config python3
mkdir -p "${RUNNER_TEMP}/example-captures"
apt-cache policy libunwind-dev libunwind8 libgstreamer1.0-dev \
| tee "${RUNNER_TEMP}/example-captures/dependency-policy.log"
Expand Down Expand Up @@ -130,3 +130,26 @@ jobs:
path: ${{ runner.temp }}/example-captures
if-no-files-found: error
retention-days: 7

retarget-contracts:
name: Retarget input and path contracts (no motion solve)
runs-on: ubuntu-24.04
timeout-minutes: 10
env:
OPENBLAS_NUM_THREADS: "1"
OMP_NUM_THREADS: "1"
steps:
- name: Check out repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Isolated Python 3.12 test environment
run: |
python3.12 -m venv "${RUNNER_TEMP}/g1-contract-tests"
"${RUNNER_TEMP}/g1-contract-tests/bin/python" -m pip install \
-r tools/g1-retarget/requirements.txt
"${RUNNER_TEMP}/g1-contract-tests/bin/python" -m pip check
- name: Source parser, velocity paths, and guarded export tests
run: |
"${RUNNER_TEMP}/g1-contract-tests/bin/python" -m unittest discover \
-s tools/g1-retarget/preflight -p 'test_*.py' -v
"${RUNNER_TEMP}/g1-contract-tests/bin/python" -m unittest discover \
-s tools/g1-retarget/retarget -p 'test_*.py' -v
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
**/obj/
tests/**/bin/
.DS_Store
example-g1-motion-lab/bin/*.app/
example-g1-motion-lab/bin/example-g1-motion-lab
example-g1-motion-lab/captures/
3 changes: 3 additions & 0 deletions LICENSES.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,11 @@ single combined work.
| `ofxBvh` | [MIT](ofxBvh/LICENSE) | Copyright (c) 2012 Daito Manabe; originally published under the Perfume Dev Team name |
| `example-motion-ribbons` | [MIT](example-motion-ribbons/LICENSE) | Copyright (c) 2026 Daito Manabe |
| `example-motion-field` | [MIT](example-motion-field/LICENSE) | Copyright (c) 2026 Daito Manabe |
| `example-g1-motion-lab` source and shaders | [MIT](example-g1-motion-lab/LICENSE) | Copyright (c) 2026 Daito Manabe; excludes motion and model assets |
| `example-g1-motion-lab/bin/data/g1-model.json` derived robot geometry | [BSD-3-Clause](example-g1-motion-lab/bin/data/MODEL-LICENSE) | Upstream Unitree Robotics notice retained; not licensed by the viewer's MIT grant |
| `shared` | [MIT](shared/LICENSE) | Copyright (c) 2026 Daito Manabe |
| `scripts` | [MIT](scripts/LICENSE) | Copyright (c) 2026 Daito Manabe |
| `tools/g1-retarget` new source and documentation | [MIT](tools/g1-retarget/LICENSE) | Copyright (c) 2026 Daito Manabe; excludes model assets, dependencies and performance data |
| `tests/ofxBvh-boundary` | [MIT](tests/ofxBvh-boundary/LICENSE) | Copyright (c) 2026 Daito Manabe |
| `third_party/ofxMarchingCubes` | [LGPL-2.1-or-later](third_party/ofxMarchingCubes/LICENSE.txt) | Copyright (c) 2009 Rui Madeira; adapted by Greg Borenstein |

Expand Down
34 changes: 32 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ The six examples from the 2012 Perfume “Global Site Project” now use the
programmable OpenGL 3.2 renderer, `ofApp`, member-owned state, and mesh-based
drawing. Two new shader studies turn the bundled motion into luminous ribbons
and animated contour fields.
The [G1 Motion Lab](example-g1-motion-lab) adds a Unitree G1 kinematic-reference
study, with an original-human-skeleton overlay and explicit validation status.

## Start with the shader studies

Expand Down Expand Up @@ -65,6 +67,7 @@ notices.
| `marching-cubes` | A metaball surface driven by skeletal endpoints |
| `example-motion-ribbons` | Geometry-shader ribbons following the bundled motion |
| `example-motion-field` | A joint-driven fragment-shader contour field |
| `example-g1-motion-lab` | G1 robot reference playback, rigid-link meshes and source skeleton comparison; no robot control |
| `ofxBvh` | The bundled BVH loader and player used by every example |

## Build with openFrameworks 0.12.1
Expand All @@ -81,7 +84,7 @@ cd "$OF_ROOT/apps/myApps/example-openFrameworks"
./scripts/build-all.sh "$OF_ROOT"
```

The script builds all eight applications in Release mode and runs the `ofxBvh`
The script builds all nine applications in Release mode and runs the `ofxBvh`
boundary/malformed-input and marching-cubes lifecycle regression tests. An
individual application can be built with, for example:

Expand All @@ -97,7 +100,7 @@ To render every example in a hidden test window and save PNG evidence:
```

The GitHub workflow builds and runs regression tests on macOS, then independently
builds and renders all eight examples in a clean, digest-pinned Ubuntu 22.04
builds and renders all nine examples in a clean, digest-pinned Ubuntu 22.04
container with Mesa llvmpipe and Xvfb. The container isolates OF dependencies
from the hosted runner's preinstalled compiler SDKs.
This supplies a real software OpenGL context where hosted macOS runners cannot
Expand Down Expand Up @@ -150,3 +153,30 @@ The maintained example code uses GLM vectors and mesh drawing. `ofxBvh` retains
its older public vector/matrix types so existing addon consumers continue to
compile; its example rendering supports the programmable renderer. The vendored
marching-cubes dependency retains its upstream public interface and attribution.

## Robot reference study

[G1 Motion Lab](example-g1-motion-lab/README.md) displays the same A/B/C recordings
adapted to a pinned 29-DOF Unitree G1 model. Geometry comes from the robot asset,
not a procedural humanoid. Computation is separate from the lightweight viewer;
there is no CUDA, Python, learned policy, or robot SDK in the application.

![Actual G1 Motion Lab capture; kinematic references, physics not validated](docs/images/g1-motion-lab.png)

The separate [remote reproduction kit](tools/g1-retarget/README.md) records
source/model hashes, explicit coordinate conventions, dependency versions and
the independent full-frame export gate. It does not provision cloud resources
or automatically install or publish generated motion.

The source-motion transform and complete original-frame pose checks are separate
from rendering checks. The warning **KINEMATIC REFERENCE — PHYSICS NOT VALIDATED**
is always visible. A reference that looks right is not evidence of balance,
self-collision clearance, feasible torque, or safe hardware operation. The
first independent gravity/contact experiment did not pass its collision gate;
this example does not send any actuator commands. Model geometry retains its
Unitree BSD-3-Clause license; the new viewer's MIT license does not relicense the
original or derived performance data.

All three bundled references now pass complete 40 Hz pose checks. See the
[measured results and physical limitations](example-g1-motion-lab/VALIDATION.md)
for the exact scope; no hardware execution is approved.
Binary file added docs/images/g1-motion-lab.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
25 changes: 25 additions & 0 deletions example-g1-motion-lab/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
MIT License

Copyright (c) 2026 Daito Manabe

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

This license covers the new viewer source and shaders only. Referenced Perfume
motion data and Unitree robot assets / derived geometry retain their original
terms and notices; this file does not relicense them.
5 changes: 5 additions & 0 deletions example-g1-motion-lab/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
ifneq ($(wildcard config.make),)
include config.make
endif
OF_ROOT ?= ../../../..
include $(OF_ROOT)/libs/openFrameworksCompiled/project/makefileCommon/compile.project.mk
88 changes: 88 additions & 0 deletions example-g1-motion-lab/Project.xcconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
//THE PATH TO THE ROOT OF OUR OF PATH RELATIVE TO THIS PROJECT.
//THIS NEEDS TO BE DEFINED BEFORE CoreOF.xcconfig IS INCLUDED
OF_PATH = ../../../..

//THIS HAS ALL THE HEADER AND LIBS FOR OF CORE
#include "../../../../libs/openFrameworksCompiled/project/osx/CoreOF.xcconfig"

//UNCOMMENT BELOW TO ENABLE C++ 17 and std::filesystem
CLANG_CXX_LANGUAGE_STANDARD = c++23
CLANG_C_LANGUAGE_STANDARD = c17
MACOSX_DEPLOYMENT_TARGET = 11.5

// App Settings
PRODUCT_NAME = $(TARGET_NAME)
PRODUCT_NAME[config=Debug] = $(TARGET_NAME)Debug
PRODUCT_BUNDLE_IDENTIFIER = cc.openFrameworks.${TARGET_NAME:rfc1034identifier}
//PRODUCT_BUNDLE_IDENTIFIER[config=Debug] = cc.openFrameworks.$(TARGET_NAME)Debug
DEVELOPMENT_LANGUAGE = English
CODE_SIGN_IDENTITY = -
INFOPLIST_FILE = openFrameworks-Info.plist
GENERATE_INFOPLIST_FILE = YES

// set application category to games, this is required to enable Game mode
// note: this sets the initial value in the Xcode UI
INFOPLIST_KEY_LSApplicationCategoryType = public.app-category.education

// VERSIONING - overridden if changed in Xcode UI

// this is "Version" in the Xcode target Identity UI
// suggested to use semantic versioning format ala #.#.#
MARKETING_VERSION = 0.1.0

// If users upgrade project to recommended settings it enables script sandboxing which breaks our post build script
ENABLE_USER_SCRIPT_SANDBOXING=NO

// this is "Build" in the Xcode target Identity UI, an incremental build number
// important for the App Store as new build submissions need a diff number even
// if MARKETING_VERSION is the same
CURRENT_PROJECT_VERSION = 1

// ICONS

// default oF app icon
ICON_NAME = of.icns
ICON_NAME[config=Debug] = of_debug.icns
ICON_FILE = $(OF_PATH)/libs/openFrameworksCompiled/project/osx/$(ICON_NAME)

// custom app icon, placed in main project folder
//ICON_NAME = MyApp.icns
//ICON_FILE = $(ICON_NAME)

// custom app icon with separate Release and Debug versions placed in bin/data
//ICON_NAME = icon.icns
//ICON_NAME[config=Debug] = icon-debug.icns
//ICON_FILE = bin/data/$(ICON_NAME)

// note: oF 0.7.2 - 0.11 used ICON_FILE_PATH which is no longer used in oF 0.12+
// ex. change ICON_FILE_PATH = bin/data/ -> ICON_FILE = bin/data/$(ICON_NAME) &
// in a custom openFrameworks-Info.plist, set CFBundleIconFile to ICON_NAME

//APPSTORE, uncomment next lines to bundle data folder and code sign
//OF_CODESIGN = 1
//OF_BUNDLE_DATA_FOLDER = 1
//OF_BUNDLE_DYLIBS = 1

HIGH_RESOLUTION_CAPABLE = NO

// Optional include to keep any permanent settings as CODE_SIGN_IDENTITY.
#include? "App.xcconfig"

//FOR AV ENGINE SOUND PLAYER UNCOMMENT THREE LINES BELOW
OF_NO_FMOD=1
USER_PREPROCESSOR_DEFINITIONS="OF_NO_FMOD=1"
LIB_FMOD=""
GCC_PREPROCESSOR_DEFINITIONS=$(inherited) $(USER_PREPROCESSOR_DEFINITIONS)

OTHER_CFLAGS = $(OF_CORE_CFLAGS)
OTHER_LDFLAGS = $(OF_CORE_LIBS) $(OF_CORE_FRAMEWORKS)
HEADER_SEARCH_PATHS = $(OF_CORE_HEADERS)

//THIS MAKES SURE THE APP BUILDS INSIDE THE BIN FOLDER
//If you comment this line out the app will be run from DerrivedData and your data/ files won't be accessible unless you uncomment OF_BUNDLE_DATA_FOLDER = 1 above
CONFIGURATION_BUILD_DIR = ${SRCROOT}/bin

//OF_CORE_BUILD_COMMAND = echo \"💾 Compiling openFrameworks\"\nxcodebuild -project \"$OF_PATH/libs/openFrameworksCompiled/project/osx/openFrameworksLib.xcodeproj\" -target openFrameworks -configuration \"${CONFIGURATION}\" CLANG_CXX_LANGUAGE_STANDARD=$CLANG_CXX_LANGUAGE_STANDARD MACOSX_DEPLOYMENT_TARGET=$MACOSX_DEPLOYMENT_TARGET GCC_PREPROCESSOR_DEFINITIONS='$USER_PREPROCESSOR_DEFINITIONS'


OF_CORE_BUILD_COMMAND = xcodebuild -project $OF_PATH/libs/openFrameworksCompiled/project/osx/openFrameworksLib.xcodeproj -target openFrameworks -configuration ${CONFIGURATION} CLANG_CXX_LANGUAGE_STANDARD=$CLANG_CXX_LANGUAGE_STANDARD MACOSX_DEPLOYMENT_TARGET=$MACOSX_DEPLOYMENT_TARGET GCC_PREPROCESSOR_DEFINITIONS=$USER_PREPROCESSOR_DEFINITIONS
130 changes: 130 additions & 0 deletions example-g1-motion-lab/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# G1 Motion Lab

A quiet, inspectable **kinematic reference viewer** for the Perfume A / B / C
motion studies retargeted to the Unitree G1. The actual G1 body geometry is drawn
using lightweight convex hulls derived from the official robot model, not a
generic stick figure. White, ink, and short cyan hand trails keep the robot and
its motion easy to read.

**This example does not contain a physics engine or a learned controller.** It
does not establish dynamic balance, contact feasibility, torque feasibility, or
real-robot safety. The warning remains visible even when help is hidden.

If `validation.pose_status` is anything other than `pass`, a second persistent
warning reads **POSE QA FAILED — DIAGNOSTIC ONLY**. A successful render check of
such an export validates the graphics path only; it does not approve that
motion for publication or physical tracking. Missing pose status is not a pass.

The bundled A/B/C references pass the independent pose gates at all 1,299
retained samples per clip, at the original 40 Hz. This is a constrained
kinematic result only; physical tracking remains unvalidated. See the supplied
`bin/data/pose_retarget_qa.json` and `bin/data/provenance.json` for the measured
criteria, original-data hashes, model revision, and limitations.

## Run

Use openFrameworks **0.12.1**, a desktop OpenGL 3.2 context, and the repository's
local `ofxBvh` addon (required by the shared `ExampleRuntime.h` include). Place
the repository at `OF_ROOT/apps/myApps/example-openFrameworks` to use the included
Xcode project, or pass an explicit `OF_ROOT` to Make:

```sh
make Release -j4 OF_ROOT=/absolute/path/to/openFrameworks
make RunRelease OF_ROOT=/absolute/path/to/openFrameworks
```

The viewer requires real exported `bin/data/g1-motion.json` and
`bin/data/g1-model.json`. Missing or malformed data causes an explicit error and
a nonzero exit. There is no synthetic fallback or successful dummy capture.

## Controls

| Control | Action |
| --- | --- |
| A / B / C or 1 / 2 / 3 | Inspect one clip |
| 0 or 4 | Show all three side by side |
| Space | Pause / resume |
| R | Reset playback and trails; remains paused if already paused |
| S | Toggle the original BVH skeleton overlay |
| H | Toggle help, not the validation warning |
| Drag / scroll | Orbit / zoom |
| V | Reset camera framing |

Each robot is recentered horizontally at its root for presentation. In the
three-robot view, the columns are display offsets, **not the original stage
formation**. The same display transform is applied to its original BVH overlay.
The exported vertical coordinate is preserved: floor penetration and floating
are not corrected or hidden. The floor is a reference grid, not a collider.

Only the robot root's world translation is linearly interpolated. Rotations
are converted to parent-relative quaternions, interpolated with slerp, then
composed through the hierarchy with the model's fixed body offsets. This keeps
rigid links attached between samples; independently interpolating every body's
world position would shorten or separate links during fast rotations. Original
key poses are retained within the export's rounding precision. The source BVH
comparison overlay uses linear position interpolation.

Trails reset at clip boundaries and view switches. Time is calculated from the
exported FPS and sample count; it is not solver time. Original reference frame 0
is excluded, so the viewer's first sample corresponds to source time 0.025 s.

## Render checks

On macOS, from this example directory:

```sh
bin/example-g1-motion-lab.app/Contents/MacOS/example-g1-motion-lab \
--smoke-test --capture=/absolute/path/g1-all.png --capture-frame=480
bin/example-g1-motion-lab.app/Contents/MacOS/example-g1-motion-lab \
--smoke-test --clip=A --source-overlay \
--capture /absolute/path/g1-source.png --capture-frame 960
bin/example-g1-motion-lab.app/Contents/MacOS/example-g1-motion-lab \
--smoke-test --clip=A --source-overlay \
--capture /absolute/path/g1-source-hard.png --capture-frame 860
```

On Linux the executable is `bin/example-g1-motion-lab`; use an actual GL context
(Mesa / Xvfb is suitable for software rendering). `--frames=N` aliases
`--capture-frame=N`. Any capture run is deterministic and uses a hidden GLFW
window without taking focus. The shared runtime requires nonempty geometry,
non-flat and changed rendered content, no GL error, a hidden unfocused window,
and a successfully saved capture before reporting `SMOKE_TEST_OK`. Pause,
paused reset, clip selection, source overlay, and help controls are also checked.
Every retained keyframe is checked against the exported world pose. Every
adjacent pair is sampled at one-third, one-half and two-thirds to check rigid
anchor consistency. These checks validate interpolation geometry, not a
different retarget or a physical controller.

These are **render checks, not physics validation**. The smoke test does not
evaluate retargeting quality or infer balance from an attractive image.
From the repository root, `scripts/smoke-all.sh /absolute/path/captures --g1-only`
runs four G1 views plus all input-rejection checks without rerunning the other
examples. Omitting `--g1-only` preserves the full nine-example suite.

## Data and drawing pipeline

`g1-motion.json` schema 1 contains `fps`, robot `body_names` / `body_parents`,
and `body_offsets` (one finite XYZ vector per body, from the model's fixed
parent-relative body positions). Body 0 must be the root and each remaining
parent must precede its children. The root's stored rest offset is not added
to its independently exported world position. The model offsets must agree
with every exported key pose within 0.1 mm, otherwise loading fails. It also contains
`source_names` / `source_parents`, and exactly three clips named A, B, C. Each
frame has flattened arrays `positions` (world metres, Z-up), `rotations` (world
quaternions, **wxyz**), and `source_positions` (original BVH transformed into the
same robot axes and metres). The required validation label is
`KINEMATIC REFERENCE — PHYSICS NOT VALIDATED`.

`g1-model.json` contains indexed triangle meshes. Each mesh identifies a `body`
and stores local `vertices`, `indices`, body-local `position` / `quaternion`
(wxyz), and RGBA `color` in 0..1. Drawing composes the body's world transform
with this local mesh transform. It does not integrate forces or rewrite the
exported motion. The vertex shader transforms normals into view space; the
fragment shader adds restrained key / fill / rim lighting. Both use GLSL 150.

## Rights

New viewer source and shaders: MIT, Copyright (c) 2026 Daito Manabe.
The original motion data retains its original terms. Robot model geometry and
derived hulls retain the upstream Unitree model license and notices supplied
with the export. This example's MIT license does not relicense either asset.
Loading