Tutorial: Complete Scene Example
A fully-featured MC3 scene demonstrating every major subsystem: environment, lights, cameras, textures, materials, CSG, definitions, keyframe animation, and interactive states.
What We're Building
This tutorial walks through a complete outdoor garden scene with:
- Sky color and distance fog
- Sun directional light + ambient fill
- A perspective camera
- Textured PBR materials (brick, wood, glass, grass)
- Reusable tree definition instanced three times
- A house body built from CSG union
- A front wall with a door hole cut out (CSG difference)
- A door with open/closed states
- A bouncing ball keyframe animation
The complete file is ~120 lines of XML and uses all format sections.
Step 1 — Root Element and Environment
Start with the root element specifying format version, unit, and coordinate system. Add environment settings and lights.
<?xml version="1.0" encoding="UTF-8"?>
<mc3 version="0.3"
model="GardenScene"
unit="meter"
coordinate_system="right_handed_y_up"
default_camera="Main">
<environment>
<background color="0.53 0.81 0.98"/>
<fog mode="linear" color="0.53 0.81 0.98"
start="40" end="120"/>
</environment>
<lights>
<ambient color="0.3 0.35 0.4" brightness="0.4"/>
<directional name="Sun"
color="1 0.95 0.8"
brightness="2.0"
direction="0.58 -0.58 0.58"
cast_shadows="true"/>
</lights>
<cameras default="Main">
<camera name="Main" type="perspective"
fov="55" near="0.1" far="500"
position="12 8 20" target="0 2 0"/>
<camera name="TopDown" type="orthographic"
size="25" near="0.1" far="200"
position="0 40 0" target="0 0 0"/>
</cameras>
The default_camera="Main" on the root element, together with default="Main" on <cameras>, ensures the perspective view is used when no explicit camera is selected.
Step 2 — Textures and Materials
<textures>
<texture id="brick_albedo" uri="textures/brick.png"
wrap_u="repeat" wrap_v="repeat" filter="linear"/>
<texture id="brick_normal" uri="textures/brick_n.png"
color_space="linear"/>
<texture id="wood_albedo" uri="textures/wood.png"
wrap_u="repeat" wrap_v="repeat"/>
<texture id="grass_albedo" uri="textures/grass.png"
wrap_u="repeat" wrap_v="repeat"/>
</textures>
<materials>
<material id="brick" roughness="0.85" metallic="0.0">
<base_color_texture>brick_albedo</base_color_texture>
<normal_texture>brick_normal</normal_texture>
</material>
<material id="painted_wood" roughness="0.6">
<base_color_texture>wood_albedo</base_color_texture>
</material>
<material id="glass" roughness="0.05" alpha_mode="blend" double_sided="true">
<base_color>0.7 0.9 1.0 0.35</base_color>
</material>
<material id="grass" roughness="0.95">
<base_color_texture>grass_albedo</base_color_texture>
</material>
<material id="bark" roughness="0.9"><base_color>0.35 0.22 0.1 1.0</base_color></material>
<material id="leaves" roughness="0.8"><base_color>0.15 0.55 0.1 1.0</base_color></material>
<material id="red"><base_color>0.9 0.1 0.1 1.0</base_color></material>
</materials>
Step 3 — Reusable Tree Definition
Define a tree once and instance it multiple times.
<definitions>
<definition id="Tree">
<group>
<cylinder name="Trunk" radius="0.18" height="2.5"
position="0 1.25 0" material="bark"/>
<sphere name="Canopy" radius="1.4"
position="0 3.5 0" material="leaves"/>
</group>
</definition>
</definitions>
Step 4 — Scene Objects
Ground plane and trees
<objects>
<plane name="Ground" size="50 50" axis="y"
material="grass" collision="static">
<uv_mapping scale="10 10"/>
</plane>
<instance name="Tree1" definition="Tree" position="-8 0 -4"/>
<instance name="Tree2" definition="Tree" position=" 9 0 -5" rotation="0 45 0"/>
<instance name="Tree3" definition="Tree" position="-6 0 8" scale="1.3 1.3 1.3"/>
House body (CSG union)
<union name="HouseBody">
<box name="MainBlock" size="8 3.5 6" position="0 1.75 0" material="brick"/>
<box name="RoofBase" size="8.6 0.3 6.6" position="0 3.65 0" material="brick"/>
</union>
Front wall with door hole (CSG difference)
<difference name="FrontWall">
<box name="FrontWallBase" size="8 3.5 0.3"
position="0 1.75 -3" material="brick"/>
<box name="DoorHole" size="1.2 2.3 0.6"
position="0 1.15 -3"
role="cutter" visible="false"/>
<box name="WindowHole" size="1.4 1.1 0.6"
position="2.5 2.1 -3"
role="cutter" visible="false"/>
</difference>
Interactive door (pivot + states)
<box name="FrontDoor"
size="1.1 2.2 0.07"
position="0 1.1 -3.08"
pivot="-0.55 0 0"
material="painted_wood"
collision="kinematic"
state="closed">
<states>
<state id="closed" rotation="0 0 0"/>
<state id="open" rotation="0 90 0"/>
</states>
</box>
<box name="WindowGlass"
size="1.3 1.0 0.05"
position="2.5 2.1 -3.1"
material="glass"/>
Roof arch (extrude)
<extrude name="RoofRidge" position="0 3.65 0"
material="painted_wood" segments="1">
<cross_section type="rect" width="0.15" height="0.15"/>
<path type="line" length="6.6" axis="z"/>
</extrude>
Bouncing ball (with keyframe animation target)
<sphere name="Ball"
radius="0.35"
position="4 0.35 4"
material="red"/>
</objects>
Step 5 — Interactive Actions and Keyframe Animation
<actions>
<!-- Interactive door control -->
<action id="open_door" target="FrontDoor"
type="set_state" state="open"
duration="0.6" easing="ease_out">
<trigger type="interact" prompt="Open door"/>
</action>
<action id="close_door" target="FrontDoor"
type="set_state" state="closed"
duration="0.6" easing="ease_in">
<trigger type="interact" prompt="Close door"/>
</action>
<!-- Keyframe animation — bouncing ball -->
<action name="BallBounce" duration="1.0" loop="true">
<channel target="Ball" property="position.y">
<keyframe time="0.0" value="0.35" interp="cubic">
<handle_left dt="-0.1" dv="0.0"/>
<handle_right dt=" 0.1" dv="3.5"/>
</keyframe>
<keyframe time="0.5" value="3.5" interp="cubic">
<handle_left dt="-0.1" dv="3.5"/>
<handle_right dt=" 0.1" dv="-3.5"/>
</keyframe>
<keyframe time="1.0" value="0.35" interp="linear"/>
</channel>
</action>
</actions>
</mc3>
Key Concepts Demonstrated
| Feature | Where | Purpose |
|---|---|---|
| Environment + fog | <environment> | Sky color and distance fade |
| Lights | <lights> | Ambient fill + directional sun |
| Multiple cameras | <cameras> | Perspective main view + top-down ortho |
| Normal map texture | brick_normal in material | Surface micro-detail without geometry |
| Reusable definition | Tree definition + 3 instances | DRY — one definition, three placements |
| CSG union | HouseBody | Merge main block and roof base |
| CSG difference | FrontWall | Cut door hole and window hole from wall |
| Extrusion | RoofRidge | Beam swept along Z axis |
| Pivot on door | pivot="-0.55 0 0" | Rotation around hinge edge, not center |
| Object states | <states> on FrontDoor | Named closed/open configurations |
| Interactive actions | type="set_state" with trigger | Player-triggered door open/close |
| Keyframe animation | BallBounce action | Cubic-bezier ball bounce, exportable to glTF |
| UV mapping | <uv_mapping scale="10 10"/> on Ground | Tile the grass texture across a 50 m plane |
Opening in the Editor
./cmake-build-debug/MeshCraft garden_scene.mc3.xml
You should see the garden with three trees, the house body, and a red sphere. Press Ctrl+T to open the Timeline panel, select BallBounce, and press Play to watch the sphere bounce.
The door can be toggled by selecting FrontDoor in the hierarchy and editing its state attribute in the Properties panel.
Exporting to GLB
./mc3togltf/build/mc3togltf garden_scene.mc3.xml garden_scene.glb
The GLB export will include all geometry (primitives, CSG children as separate meshes, extrusion) and the BallBounce keyframe animation clip. Interactive door actions are not exported — they are a runtime game concept.
Texture files are not embedded in the GLB by default — they are referenced as relative paths. Ensure the textures/ folder is kept alongside the GLB when deploying to a game engine.