Specialized components
World editor
Scene operations and GLTF management
22 min
overview the scene operations and gltf management component is a fundamental element of the ir engine's world editor that handles the creation, loading, and saving of 3d environments it provides a comprehensive system for managing scene files using the gl transmission format (gltf), enabling users to preserve their work and share it across different platforms by implementing specialized functions for scene serialization and deserialization, this component ensures that complex 3d worlds can be reliably stored and reconstructed this chapter explores the implementation, workflow, and management of scenes within the world editor core concepts scene structure a scene in the world editor represents a complete 3d environment entity hierarchy the organizational structure of all objects in the scene component data properties and configurations attached to entities spatial information positions, rotations, and scales of objects asset references links to models, textures, and other resources environment settings lighting, physics, and other scene wide configurations this structure forms the foundation of all 3d worlds created in the editor gltf format the gltf (gl transmission format) serves as the primary file format for scenes industry standard widely supported across 3d applications and engines comprehensive structure capable of representing complex 3d scenes binary efficiency supports both json ( gltf) and binary ( glb) formats asset embedding can include or reference textures and other resources extension system supports custom data through standardized extensions this format provides a reliable and efficient way to store and share 3d content scene operations the scene management system provides essential operations creation starting new scenes from templates or empty states loading opening existing scenes from files saving preserving the current state of a scene exporting generating optimized versions for different platforms importing bringing external scenes into the current project these operations enable a complete workflow for scene management implementation scene state management the scene state service manages the current scene information // simplified from src/services/scenestate ts import { definestate, getmutablestate } from '@ir engine/hyperflux'; import { entity } from '@ir engine/ecs'; / state management for scenes / export const scenestate = definestate({ name 'scenestate', // initial state initial () => ({ currentscenepath '', ismodified false, rootentity null as entity | null, entities \[] as entity\[], selectedentityid '' as string | null }), // load a scene from a file loadscene async (scenepath string) => { const state = getmutablestate(scenestate); try { // set loading state state currentscenepath set(scenepath); // load the scene file const scenedata = await loadscenefile(scenepath); // parse the scene data const { rootentity, entities } = parsescenedata(scenedata); // update state with loaded scene state rootentity set(rootentity); state entities set(entities); state ismodified set(false); console log(`scene loaded ${scenepath}`); return true; } catch (error) { console error(`failed to load scene ${error}`); return false; } }, // mark the scene as modified setmodified (modified boolean = true) => { const state = getmutablestate(scenestate); state ismodified set(modified); }, // select an entity in the scene selectentity (entityid string | null) => { const state = getmutablestate(scenestate); state selectedentityid set(entityid); } }); this service defines a state structure for tracking scene information provides methods for loading scenes and managing selection tracks whether the scene has unsaved changes maintains references to all entities in the scene uses hyperflux for reactive state management scene creation the scene creation function initializes new scenes // simplified from src/functions/scenefunctions ts import { scenestate } from ' /services/scenestate'; import { entity, createentity, addcomponent } from '@ir engine/ecs'; / creates a new empty scene @returns promise resolving to the created scene / export const createnewscene = async () promise\<boolean> => { try { // create root entity const rootentity = createentity(); addcomponent(rootentity, 'transform', { position { x 0, y 0, z 0 }, rotation { x 0, y 0, z 0 }, scale { x 1, y 1, z 1 } }); // create default camera const cameraentity = createentity(); addcomponent(cameraentity, 'transform', { position { x 0, y 2, z 5 }, rotation { x 0 3, y 0, z 0 }, scale { x 1, y 1, z 1 } }); addcomponent(cameraentity, 'camera', { fov 60, near 0 1, far 1000 }); // create default light const lightentity = createentity(); addcomponent(lightentity, 'transform', { position { x 1, y 3, z 2 }, rotation { x 0 5, y 0 5, z 0 }, scale { x 1, y 1, z 1 } }); addcomponent(lightentity, 'directionallight', { color '#ffffff', intensity 1 }); // add camera and light as children of root addcomponent(rootentity, 'hierarchy', { children \[cameraentity id, lightentity id] }); // update scene state const state = getmutablestate(scenestate); state rootentity set(rootentity); state entities set(\[rootentity, cameraentity, lightentity]); state currentscenepath set(''); state ismodified set(true); console log('new scene created'); return true; } catch (error) { console error(`failed to create new scene ${error}`); return false; } }; this function creates a root entity for the scene adds default camera and light entities sets up basic components and hierarchy updates the scene state with the new entities marks the scene as modified (unsaved) scene saving the scene saving function serializes the scene to a gltf file // simplified from src/functions/scenefunctions ts import { scenestate } from ' /services/scenestate'; import { exportscenetogltf } from ' /exportgltf'; import { filesstate } from ' /services/filesstate'; / saves the current scene to a file @param path optional path to save to (uses current path if not provided) @returns promise resolving to success status / export const savescene = async (path? string) promise\<boolean> => { try { const state = getmutablestate(scenestate); const scenepath = path || state currentscenepath value; // if no path is provided and no current path exists, show save dialog if (!scenepath) { return savesceneas(); } // get the root entity and all entities const rootentity = state rootentity value; if (!rootentity) { throw new error('no scene to save'); } // export the scene to gltf format const gltfdata = await exportscenetogltf(rootentity); // save the gltf data to the specified path await filesstate savefile(scenepath, gltfdata); // update scene state state currentscenepath set(scenepath); state ismodified set(false); console log(`scene saved to ${scenepath}`); return true; } catch (error) { console error(`failed to save scene ${error}`); return false; } }; / saves the current scene to a new file @returns promise resolving to success status / export const savesceneas = async () promise\<boolean> => { // show save dialog to get new path const newpath = await showsavedialog({ title 'save scene as', defaultpath 'new scene gltf', filters \[{ name 'gltf files', extensions \['gltf', 'glb'] }] }); if (!newpath) { return false; // user cancelled } // save to the new path return savescene(newpath); }; this function retrieves the current scene data from state exports the scene to gltf format saves the gltf data to the specified file updates the scene state to reflect the save provides a "save as" option for creating new files gltf export the gltf export function converts scene data to the gltf format // simplified from src/functions/exportgltf ts import { entity, getcomponent } from '@ir engine/ecs'; import { gltfexporter } from '@ir engine/gltf'; / exports a scene to gltf format @param rootentity root entity of the scene @returns promise resolving to gltf data / export const exportscenetogltf = async (rootentity entity) promise\<arraybuffer> => { // create a new gltf exporter const exporter = new gltfexporter(); // process the entity hierarchy const scene = processentityhierarchy(rootentity); // export the scene to gltf return new promise((resolve, reject) => { exporter parse( scene, (gltfdata) => { resolve(gltfdata); }, (error) => { reject(error); }, { binary true } // export as glb (binary gltf) ); }); }; / processes an entity hierarchy for gltf export @param entity root entity to process @returns processed scene object / const processentityhierarchy = (entity entity) => { // create a scene object const scene = { type 'scene', children \[], userdata { entityid entity id } }; // process the root entity processentity(entity, scene); // process children recursively const hierarchycomponent = getcomponent(entity, 'hierarchy'); if (hierarchycomponent && hierarchycomponent children) { for (const childid of hierarchycomponent children) { const childentity = getentitybyid(childid); if (childentity) { const childobject = { type 'object3d', userdata { entityid childid } }; processentity(childentity, childobject); scene children push(childobject); } } } return scene; }; / processes a single entity for gltf export @param entity entity to process @param object target object to populate / const processentity = (entity entity, object any) => { // process transform component const transform = getcomponent(entity, 'transform'); if (transform) { object position = transform position; object rotation = transform rotation; object scale = transform scale; } // process mesh component const mesh = getcomponent(entity, 'mesh'); if (mesh) { object geometry = mesh geometry; object material = mesh material; } // process other components // implementation details omitted for brevity }; this function creates a gltf exporter instance processes the entity hierarchy into a format suitable for export extracts component data from entities converts entity relationships to gltf scene structure generates a binary glb file containing the scene data gltf import the gltf import function loads scene data from a gltf file // simplified from src/functions/importgltf ts import { gltfloader } from '@ir engine/gltf'; import { createentity, addcomponent } from '@ir engine/ecs'; / loads a scene from a gltf file @param url url of the gltf file to load @returns promise resolving to the loaded scene data / export const loadscenefromgltf = async (url string) promise<{ rootentity entity; entities entity\[]; }> => { // create a new gltf loader const loader = new gltfloader(); // load the gltf file return new promise((resolve, reject) => { loader load( url, (gltf) => { // process the loaded gltf data const { rootentity, entities } = processgltfscene(gltf scene); resolve({ rootentity, entities }); }, undefined, (error) => { reject(error); } ); }); }; / processes a gltf scene into entities @param gltfscene the loaded gltf scene @returns processed entities / const processgltfscene = (gltfscene any) { rootentity entity; entities entity\[]; } => { const entities entity\[] = \[]; // create root entity const rootentity = createentity(); entities push(rootentity); // add transform component to root addcomponent(rootentity, 'transform', { position { x gltfscene position x, y gltfscene position y, z gltfscene position z }, rotation { x gltfscene rotation x, y gltfscene rotation y, z gltfscene rotation z }, scale { x gltfscene scale x, y gltfscene scale y, z gltfscene scale z } }); // process children const childentities = processgltfchildren(gltfscene children); entities push( childentities entities); // add hierarchy component to root addcomponent(rootentity, 'hierarchy', { children childentities entityids }); return { rootentity, entities }; }; / processes gltf child objects into entities @param children gltf child objects @returns processed child entities / const processgltfchildren = (children any\[]) { entities entity\[]; entityids string\[]; } => { const entities entity\[] = \[]; const entityids string\[] = \[]; for (const child of children) { // create entity for child const entity = createentity(); entities push(entity); entityids push(entity id); // process components based on object properties // implementation details omitted for brevity } return { entities, entityids }; }; this function creates a gltf loader instance loads the gltf file from the provided url processes the loaded scene into entities extracts transform and other component data reconstructs the entity hierarchy from the gltf structure scene workflow the complete scene workflow follows this sequence sequencediagram participant user participant editorui participant scenefunctions participant scenestate participant gltfexporter participant filesystem user >>editorui clicks "new scene" editorui >>scenefunctions createnewscene() scenefunctions >>scenefunctions create default entities scenefunctions >>scenestate update state with new scene scenestate >>editorui notify of scene change editorui >>user display new empty scene user >>editorui edits scene (add/modify entities) editorui >>scenestate update entities scenestate >>scenestate mark scene as modified user >>editorui clicks "save scene" editorui >>scenefunctions savescene() scenefunctions >>scenestate get current scene data scenefunctions >>gltfexporter exportscenetogltf() gltfexporter >>scenefunctions return gltf data scenefunctions >>filesystem save gltf file filesystem >>scenefunctions confirm save scenefunctions >>scenestate mark scene as unmodified scenestate >>editorui update ui state editorui >>user show save confirmation user >>editorui clicks "load scene" editorui >>scenefunctions loadscene() scenefunctions >>filesystem request gltf file filesystem >>scenefunctions return gltf data scenefunctions >>gltfexporter loadscenefromgltf() gltfexporter >>scenefunctions return parsed entities scenefunctions >>scenestate update state with loaded scene scenestate >>editorui notify of scene change editorui >>user display loaded scene this diagram illustrates the user creates a new scene, which initializes default entities the user edits the scene, which updates the scene state the user saves the scene, which exports it to gltf and writes to a file the user loads a scene, which reads a gltf file and reconstructs the entities integration with other components the scene operations system integrates with several other components of the world editor entity component system scenes are built on the entity component system architecture // example of ecs integration import { scenestate } from ' /services/scenestate'; import { createentity, addcomponent, removeentity } from '@ir engine/ecs'; / adds a new entity to the scene @param parentid optional parent entity id @returns the created entity / export const addentitytoscene = (parentid? string) entity => { // create a new entity const entity = createentity(); // add transform component addcomponent(entity, 'transform', { position { x 0, y 0, z 0 }, rotation { x 0, y 0, z 0 }, scale { x 1, y 1, z 1 } }); // get scene state const state = getmutablestate(scenestate); // add to parent if specified if (parentid) { const parententity = state entities value find(e => e id === parentid); if (parententity) { const hierarchycomponent = getcomponent(parententity, 'hierarchy') || { children \[] }; hierarchycomponent children push(entity id); addcomponent(parententity, 'hierarchy', hierarchycomponent); } } else { // add to root if no parent specified const rootentity = state rootentity value; if (rootentity) { const hierarchycomponent = getcomponent(rootentity, 'hierarchy') || { children \[] }; hierarchycomponent children push(entity id); addcomponent(rootentity, 'hierarchy', hierarchycomponent); } } // update scene state state entities merge(\[entity]); state ismodified set(true); return entity; }; this integration uses the ecs to create and manage entities in the scene maintains parent child relationships through hierarchy components updates the scene state when entities are added or modified marks the scene as modified when changes occur provides a consistent entity structure for scene operations asset system scenes reference assets from the asset system // example of asset system integration import { scenestate } from ' /services/scenestate'; import { filesstate } from ' /services/filesstate'; import { addcomponent } from '@ir engine/ecs'; / adds a model asset to an entity @param entityid id of the target entity @param assetid id of the model asset @returns success status / export const addmodeltoentity = (entityid string, assetid string) boolean => { // get the entity const state = getmutablestate(scenestate); const entity = state entities value find(e => e id === entityid); if (!entity) { return false; } // get the asset const asset = filesstate getfilebyid(assetid); if (!asset) { return false; } // add model component to the entity addcomponent(entity, 'model', { url asset url, type asset contenttype }); // mark scene as modified state ismodified set(true); return true; }; this integration connects assets from the file system to entities in the scene adds components that reference asset urls updates the scene state when assets are added to entities maintains relationships between entities and their assets enables asset based workflows in scene creation gizmo system scene operations interact with the gizmo system for manipulation // example of gizmo system integration import { scenestate } from ' /services/scenestate'; import { transformgizmosystem } from ' /systems/transformgizmosystem'; import { getcomponent, updatecomponent } from '@ir engine/ecs'; / updates an entity's transform based on gizmo manipulation @param entityid id of the entity being transformed @param newtransform new transform values @returns success status / export const updateentitytransform = ( entityid string, newtransform { position? vector3; rotation? vector3; scale? vector3; } ) boolean => { // get the entity const state = getmutablestate(scenestate); const entity = state entities value find(e => e id === entityid); if (!entity) { return false; } // get current transform const transform = getcomponent(entity, 'transform'); if (!transform) { return false; } // update transform with new values const updatedtransform = { transform, newtransform }; // apply the updated transform updatecomponent(entity, 'transform', updatedtransform); // mark scene as modified state ismodified set(true); return true; }; this integration receives transform updates from the gizmo system applies changes to entity transform components updates the scene state to reflect modifications marks the scene as modified when transforms change enables visual manipulation of scene elements benefits of scene operations the scene operations and gltf management component provides several key advantages persistence enables saving and loading of complex 3d environments standardization uses industry standard gltf format for compatibility workflow provides a complete set of operations for scene management integration connects with other editor systems for a cohesive experience efficiency optimizes scene data for storage and loading performance collaboration facilitates sharing of scenes between users and applications iteration supports versioning and experimentation through "save as" functionality these benefits create a reliable and flexible foundation for 3d world creation next steps with an understanding of scene operations and gltf management, the next chapter explores how the entity component system is integrated into the world editor next entity component system integration docid\ fflzngptppkps0z6obxi