Docs/Tutorials/Complete Scene Example

What We're Building

This tutorial walks through a complete outdoor garden scene with:

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

FeatureWherePurpose
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 texturebrick_normal in materialSurface micro-detail without geometry
Reusable definitionTree definition + 3 instancesDRY — one definition, three placements
CSG unionHouseBodyMerge main block and roof base
CSG differenceFrontWallCut door hole and window hole from wall
ExtrusionRoofRidgeBeam swept along Z axis
Pivot on doorpivot="-0.55 0 0"Rotation around hinge edge, not center
Object states<states> on FrontDoorNamed closed/open configurations
Interactive actionstype="set_state" with triggerPlayer-triggered door open/close
Keyframe animationBallBounce actionCubic-bezier ball bounce, exportable to glTF
UV mapping<uv_mapping scale="10 10"/> on GroundTile 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.