Skip to content
Open
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
65 changes: 65 additions & 0 deletions BLENDER_INTEGRATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# MoonRay Blender Integration (macOS)

This branch adds a Blender integration to the MoonRay render engine, plus
the system-compatibility fixes needed to build MoonRay on current macOS
(macOS 27 / Apple Silicon / AppleClang 21).

## What's here

| Path | Content |
|------|---------|
| `blender_addon/` | The Blender add-on (render engine integration) |
| `COMPATIBILITY.md` | All macOS compatibility fixes applied (10 items) |
| `patches/` | The openmoonray superbuild/preset changes used for the build |
| `install_addon.sh` | Symlinks the add-on into Blender |
| `build_moonray.sh` | Configures + builds MoonRay itself (`macos-release-ninja` preset) |
| `verify_moonray.sh` | Renders the official sphere test scene with the built binary |
| `finish_build_and_test.sh` | One-shot: wait for deps → build → verify → Blender E2E |
| `moonray_env.sh` | Terminal environment for running moonray/denoise directly |

## Build (macOS, Apple Silicon)

The engine repo itself uses DreamWorks' internal build system; the public
build lives in the [`OpenMoonRay/openmoonray`](https://github.com/OpenMoonRay/openmoonray)
superproject (this repo is its `moonray/moonray` submodule). Steps:

```bash
# 1. clone the superproject next to this checkout
git clone --recurse-submodules https://github.com/OpenMoonRay/openmoonray.git

# 2. build dependencies (patches/CMakeUserPresets.json + the superbuild
# changes in patches/openmoonray-building-macOS.patch are applied to it)
mkdir -p installs/{bin,lib,include} build-deps
cmake -DSKIP_QT=ON ../openmoonray/building/macOS # in build-deps/
cmake --build . # ~2-4 h, serial chain

# 3. build MoonRay
# (copy patches/CMakeUserPresets.json into openmoonray/ first)
cd openmoonray && cmake --preset macos-release-ninja
cmake --build --preset macos-release-ninja
```

See `COMPATIBILITY.md` for the reasoning behind each patch.

## Blender add-on

- Registers **MoonRay** as a render engine (F12 / Render Image button /
animation rendering).
- Compiles Blender shader-node graphs to MoonRay Dwa materials (Principled,
Diffuse, Glossy, Glass, Transparent, Emission, Mix/Add Shader, image
textures, normal maps, procedural noise, static baking of color/scalar
subgraphs, texture Mapping nodes).
- Exports meshes (UVs/normals), instancing, lights, camera (DOF, shift),
world (constant or HDRI), optional motion blur, optional OIDN denoise.
- The intermediate `.rdla` scene is temporary by default and kept only when
**Save RDLA Scene** is enabled.

Install and test:

```bash
./install_addon.sh
/Applications/Blender.app/Contents/MacOS/Blender --background --factory-startup \
--python blender_addon/tests/test_render.py -- /tmp/render.png
```

Full test instructions in `blender_addon/README.md`.
127 changes: 127 additions & 0 deletions COMPATIBILITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
# System compatibility fixes (macOS 27 / Apple Silicon / clang 21 / CMake 4.4)

The user's machine is an **M5 MacBook Air, macOS 27, Xcode 26.6 (CLT active),
AppleClang 21, CMake 4.4.3, Blender 5.2 Alpha**. MoonRay officially supports
macOS 14/15 with Xcode 15/16, so several adjustments were needed.

## Repository layout

- `OpenMoonRay/moonray` (the repo the user asked to clone) is the **render
engine component** and uses DreamWorks' internal rez/SCons build system
(`package.py`, `SDKScript`) that only works inside the studio infrastructure.
It is checked out at the workspace root.
- `OpenMoonRay/openmoonray` is the **official superproject** that references
this engine repo as the `moonray/moonray` Git submodule and carries the
public CMake build + macOS support. It is checked out at `openmoonray/`
and is what we build.

## Fixes applied

### 1. Generator mismatch in the dependency superbuild
`building/macOS/CMakeLists.txt` hardcodes `make ${JOBS_ARG}` as the build
command for several ExternalProjects. Configuring the superbuild with Ninja
(which we wanted) made the inner builds generate `build.ninja` while `make`
ran → "No targets specified and no makefile found" on Blosc.
**Fix:** configure the superbuild with the default Unix Makefiles generator
(the documented path; the superbuild itself only orchestrates stamps).

### 2. Qt 5.12.12 cannot build with modern toolchains (and is unneeded)
Qt 5.12 predates clang 15+ and fails on current macOS SDKs. The Blender
integration only needs the `moonray` CLI, not `moonray_gui`.
**Fix:** added `option(SKIP_QT)` to `building/macOS/CMakeLists.txt` and guard
the `qt5` ExternalProject; build with `-DSKIP_QT=ON`. The main build is
configured with `-DBUILD_QT_APPS=NO`.

### 3. Memory-bounded parallelism
24 GB RAM is not enough for `-j10` on the biggest deps (Boost/USD).
**Fix:** added `MAX_BUILD_JOBS` (default 6) cap in the superbuild.

### 4. Xcode generator unusable (CLT-only developer dir)
The official `macos-release` preset uses the Xcode generator, which requires
`xcodebuild`, but this machine's active developer directory is the Command
Line Tools. Switching to full Xcode needs sudo, which is unavailable.
**Fix:** `CMakeUserPresets.json` adds `macos-release-ninja` (inherits the
official preset, overrides the generator to Ninja). AppleClang from CLT is
used for the whole build.

### 5. Blender 5.x API removals in the add-on
Blender 5.2 removed `Mesh.loops`, `Mesh.calc_normals_split()` and
`MeshUVLoopLayer.data` (renamed to `corners`/`uv`).
**Fix:** `blender_addon/exporter.py` uses the new API with fallbacks for
Blender 4.x.

### 6. `installs/{bin,lib,include}` must exist before the superbuild runs
The Lua dependency's install step copies `lua`/`luac` into
`${InstallRoot}/bin` without creating the directory ("cp: .../bin: Not a
directory" failure). The official docs Step 1 pre-creates these folders.
**Fix:** `mkdir -p installs/{bin,lib,include}` before building deps.

### 7. Skip the unit tests in the main build
`moonray/CMakeLists.txt` gates `add_subdirectory(tests)` on
`CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME AND BUILD_TESTING`, which is true
for the top-level superproject build (and `include(CTest)` defaults
`BUILD_TESTING` to ON). Building the test suite would multiply compile time
and requires CppUnit to behave under clang 21.
**Fix:** `-DBUILD_TESTING=OFF` in `CMakeUserPresets.json`.

### 8. Unreliable GitHub clones on this network
Full clones repeatedly died with "fetch-pack: invalid index-pack output" /
"RPC failed; curl 56", and ExternalProject hung on the dead clone.
**Fix:** `GIT_SHALLOW TRUE` + `GIT_PROGRESS TRUE` on every git-based
dependency in the superbuild, plus global git hardening
(`http.postBuffer`, `http.version HTTP/1.1`, low-speed timeout).
Note: changing ExternalProject arguments invalidates its stamps, so already
built deps were re-run once (object caches made this cheap).

### 9. Blender 5.2 alpha RenderEngine API regressions
- Any *instance attribute* access on the engine raises `ReferenceError:
StructRNA ... has been removed` (only built-in methods like `report`/
`update_stats`/`test_break`/`begin_result` work through `self`).
- A bare `def __init__(self, *args): pass` swallows Blender's struct-creation
call, leaving the engine unbound — every subsequent method call (even
`update_stats`) raises `ReferenceError` and the render silently produces
black. The class must NOT define `__init__` at all.
- After the render, Blender calls `render()` a second time on the
already-released engine struct.
**Fix:** the engine stores NO instance state and keeps ALL helper logic in
module-level functions receiving the engine instance explicitly (custom
methods are also unreachable through `self`); it defines NO `__init__`;
`render()` catches `ReferenceError` from the phantom second invocation.

### 12. MoonRay beauty channels vs Blender's "Combined" pass
MoonRay writes its beauty EXR with channels `R/G/B/A`, but Blender's
`RenderLayer.load_from_file()` only maps `Combined.R/G/B/A` into the
render result; any other channel names make the final composite silently
black (`Reading render result: expected channel "Combined.R" ... not found`).
**Fix:** the engine renames the channels to `Combined.*` with `oiiotool`
(from the dependency install) before loading, and passes `-out` to the
moonray CLI so the output path is explicit (the mock renderer test relies
on the same contract).

### 10. libc++ "selected platform no longer supported" warning
embree (and possibly other old deps) request a very old macOS deployment
target; the macOS 27 libc++ warns about it during compilation. It is a
warning only (`-W#warnings`) and does not fail the build.

### 11. Anaconda environment pollution breaks OpenColorIO
With Anaconda's `bin` on `PATH`, CMake's find_* commands derive search
prefixes from PATH entries and pick up Anaconda packages. OpenColorIO then
linked against Anaconda's yaml-cpp 0.8 headers (via the expat imported
target's interface include dirs) while linking its own yaml-cpp 0.6.3 →
undefined symbols (`YAML::FpToString`, `YAML::Emitter::Write(char const*,
unsigned long)`).
**Fix:** build with Anaconda removed from `PATH` and `CONDA_*` env vars
unset (also `PYTHONPATH`, `CMAKE_PREFIX_PATH`), after deleting the
OpenColorIO build/stamp directories so its configure re-runs cleanly.

## Status

- Dependency superbuild: complete (all deps installed to `installs/`).
- Main build: complete, installed to `installs/openmoonray/`; `moonray` CLI
renders the reference `sphere.rdla` correctly (verified by
`verify_moonray.sh`).
- Add-on: complete and tested — export, full scene (18/18), materials
(11/11), motion blur, robustness, renderer, registration, animation mock,
engine mock end-to-end, and real end-to-end render (Blender → moonray →
`Combined.*` EXR → non-black PNG, mean ≈ 0.25) all pass.
Installed into Blender via `install_addon.sh`.
93 changes: 93 additions & 0 deletions blender_addon/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# MoonRay for Blender

Blender integration for the [MoonRay](https://github.com/OpenMoonRay/openmoonray)
production path tracer (DreamWorks / Academy Software Foundation).

The add-on registers **MoonRay** as a render engine in Blender:

1. exports the Blender scene to MoonRay's RDLA scene format
(meshes, UVs, normals, instancing, materials, lights, camera, world),
2. runs the `moonray` command-line renderer,
3. loads the result back into the Render Result (F12 / animation rendering),
4. optionally denoises with MoonRay's OIDN `denoise` tool.

## Requirements

- macOS (Apple Silicon) with a working MoonRay installation built with the
official `macos-release` CMake preset (see `openmoonray/building/macOS`).
Linux installations (Rocky Linux 9) should work as well; the add-on itself
only shells out to the `moonray` binary.
- Blender 4.0 or newer (tested with Blender 5.2 alpha).

## Installation

1. `./install_addon.sh` (symlinks the add-on into Blender's add-ons folder)
2. In Blender: *Edit → Preferences → Add-ons → Render → MoonRay Render*,
enable it and set:
- **MoonRay Installation** — the directory containing `bin/moonray`
(e.g. `/Users/<you>/Documents/wave-tracer/installs/openmoonray`)
- **Dependencies Install Root** — the directory containing the
third-party `lib/` (e.g. `/Users/<you>/Documents/wave-tracer/installs`)

## Usage

1. Switch the render engine to **MoonRay** in *Render Properties*.
2. Tune samples (MoonRay `pixel_samples` is the square root of the spp),
threads, denoise, etc. in the *MoonRay* panel.
3. Render with the **Render Image** button in the panel, the regular
*Render → Render Image* menu item, or F12. The scene is exported to a
temporary `.rdla`, rendered, and the EXR is loaded into the Render
Result. Animation rendering (Ctrl+F12) is supported frame by frame.

The intermediate `.rdla` scene file is deleted automatically after the
render; enable **Save RDLA Scene** in the panel to keep it (next to the
render output or at a custom path).

## Supported Blender features

| Feature | Status |
|--------------------|----------------------------------------------------|
| Meshes (quads/ngons, triangulated) | ✔ with UVs and split normals |
| Curves/surfaces/text (via to_mesh) | ✔ |
| Instancing (linked duplicates) | ✔ exported as RdlInstancerGeometry |
| Shader nodes | ✔ Principled / Diffuse / Glossy / Glass / Transparent / Emission / Mix Shader / Add Shader |
| Color/scalar nodes | ✔ static baking: Mix, Math, Gamma, Bright/Contrast, Hue/Sat, Invert, RGB→BW, ColorRamp, Map Range, Clamp |
| Textures | ✔ image textures (ImageMap) + procedural noise (NoiseMap_v2) |
| Normal maps | ✔ ImageNormalMap via the Normal Map node |
| Point / Sun / Spot / Area lights | ✔ with energy-based intensity mapping |
| World background | ✔ constant color or HDRI (Environment Texture node) |
| Depth of field | ✔ (camera DOF settings) |
| Motion blur | camera shutter + vertex velocities when Blender provides the velocity attribute (Blender 4.x; Blender 5.x currently skips object MB) |
| Volumetrics | not yet |

## Notes

- MoonRay is Y-up while Blender is Z-up; the exporter applies the standard
axis conversion (`x, z, -y`) to all transforms.
- Light intensities are converted from Blender watts to MoonRay radiance-ish
units; use the global *Light Intensity Scale* in the add-on preferences to
compensate for scene scale.
- Packed image textures (without a file on disk) fall back to the
material's base color.
- Bump nodes are approximated via normal strength (`input_normal_dial`).

## Tests

Headless test suite (run from this directory):

```
# exporter: full feature coverage (16 checks)
/Applications/Blender.app/Contents/MacOS/Blender --background --factory-startup \
--python blender_addon/tests/test_full_scene.py -- /tmp/full.exr
# material node compiler (9 checks)
/Applications/Blender.app/Contents/MacOS/Blender --background --factory-startup \
--python blender_addon/tests/test_materials.py
# engine end-to-end with a mock moonray binary
/Applications/Blender.app/Contents/MacOS/Blender --background --factory-startup \
--python blender_addon/tests/test_engine_mock.py -- /tmp/mock.png
# renderer process plumbing unit test
python3 blender_addon/tests/test_renderer.py
# real end-to-end render (requires a working MoonRay install)
/Applications/Blender.app/Contents/MacOS/Blender --background --factory-startup \
--python blender_addon/tests/test_render.py -- /tmp/render.png
```
62 changes: 62 additions & 0 deletions blender_addon/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# MoonRay for Blender
# Integrates the DreamWorks MoonRay production path tracer into Blender.
# Copyright (C) 2026 MoonRay Blender contributors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# ##### END GPL LICENSE BLOCK #####

bl_info = {
"name": "MoonRay Render",
"author": "MoonRay Blender contributors",
"version": (0, 1, 0),
"blender": (4, 0, 0),
"location": "Render Properties > Render Engine",
"description": "Render with the DreamWorks MoonRay production path tracer "
"(scene export to MoonRay RDLA + moonray CLI)",
"category": "Render",
"support": "COMMUNITY",
}

import bpy

from . import properties
from . import operators
from . import engine
from . import ui


classes = (
properties.MoonRayAddonPreferences,
properties.MoonRayRenderSettings,
operators.MOONRAY_OT_export_scene,
operators.MOONRAY_OT_render,
operators.MOONRAY_OT_open_moonray_root,
ui.MOONRAY_PT_render_panel,
engine.MoonRayRenderEngine,
)


def register():
for cls in classes:
bpy.utils.register_class(cls)
properties.register()


def unregister():
properties.unregister()
for cls in reversed(classes):
bpy.utils.unregister_class(cls)
Loading