#include "bella_engine_sdk/src/bella_sdk/bella_engine.h" // Core rendering engine #include "bella_engine_sdk/src/bella_sdk/bella_scene.h" // Scene management #include "bella_engine_sdk/src/dl_core/dl_logging.h" // Logging utilities #include // For std::this_thread (C++ standard library) #include // For timing operations (C++ standard library) // ============================================================================ // 🔍 C++ Concept: Observer Pattern // This lets us "listen" for events from the rendering engine. // When something happens (rendering starts, progress updates, etc.), // our functions get called automatically. // ============================================================================ struct MyEngineObserver : public dl::bella_sdk::EngineObserver { // 🔔 Called when rendering begins void onStarted(dl::String pass) override { dl::logInfo("🚀 Rendering started: %s", pass.buf()); } // 🔔 Called repeatedly as rendering progresses (0.0 to 1.0) void onProgress(dl::String pass, dl::bella_sdk::Progress progress) override { dl::logInfo("⏳ Progress: %.1f%% - %s", progress.progress() * 100.0, progress.toString().buf()); } // 🔔 Called when we get the final rendered image void onImage(dl::String pass, dl::bella_sdk::Image image) override { dl::logInfo("🖼️ Got image: %dx%d pixels", image.width(), image.height()); } // 🔔 Called when rendering finishes void onStopped(dl::String pass) override { dl::logInfo("✅ Rendering complete: %s", pass.buf()); } }; // ============================================================================ // 🎬 Main Function: Building Our 3D Scene // ============================================================================ int main() { // Step 1: Create the rendering engine and scene dl::bella_sdk::Engine engine; // The renderer itself dl::bella_sdk::Scene scene = engine.scene(); // Container for all our objects scene.loadDefs(); // Load built-in object types // Get the "world" - this is the root of our scene tree dl::bella_sdk::Node world = scene.world(); // ======================================================================== // 📷 Step 2: Create the Camera System // Every camera needs: sensor (film) + lens + positioning // ======================================================================== // Create a digital "film" sensor (30mm x 30mm) dl::bella_sdk::Node sensor = scene.createNode("sensor"); sensor["size"] = dl::Vec2::make(30.0, 30.0); // Create a simple lens (like a magnifying glass) auto lens = scene.createNode("thinLens"); // 'auto' = let C++ figure out the type // Create camera positioning system + the camera itself auto cameraXform = scene.createNode("xform"); // Transform = position/rotation cameraXform.parentTo(world); // Attach to our world auto camera = scene.createNode("camera"); // The actual camera camera["resolution"] = dl::Vec2::make(320, 320); // 320x320 pixel image camera["lens"] = lens; // Connect our lens camera["sensor"] = sensor; // Connect our sensor camera.parentTo(cameraXform); // Put camera in the positioning system // Tell the scene to use this camera for rendering auto settings = scene.settings(); settings["camera"] = camera; // ======================================================================== // ☀️ Step 3: Add Lighting // ======================================================================== auto skyDome = scene.createNode("skyDome"); // Simulates sky lighting skyDome.parentTo(world); settings["environment"] = skyDome; // Use this for scene lighting // ======================================================================== // 🎨 Step 4: Create Materials (like paint or metal finishes) // ======================================================================== // Golden metal material for our sphere auto sphereMaterial = scene.createNode("conductor"); // Metal material sphereMaterial["reflectance"] = dl::Rgba::make(0.8, 0.6, 0.2, 1.0); // Golden color (R,G,B,Alpha) sphereMaterial["roughness"] = 26.1; // Slightly rough surface // Glass-like material for the ground auto groundMaterial = scene.createNode("dielectric"); // Glass/plastic material groundMaterial["roughness"] = dl::Real(41.0); // Rough surface // ======================================================================== // 🏀 Step 5: Create Objects (Sphere + Ground) // ======================================================================== // Golden sphere in the center auto sphereXform = scene.createNode("xform"); // Position controller for sphere sphereXform["material"] = sphereMaterial; // Apply golden material sphereXform.parentTo(world); // Add to scene auto sphere = scene.createNode("sphere"); // The actual sphere shape sphere["radius"] = dl::Real(1.0); // 1 unit radius sphere.parentTo(sphereXform); // Put sphere in its position controller // Large disk as ground plane auto groundXform = scene.createNode("xform"); // Position controller for ground groundXform["name"] = dl::String("groundXform"); // Give it a name (optional) groundXform["material"] = groundMaterial; // Apply glass material groundXform.parentTo(world); // Add to scene auto ground = scene.createNode("disk"); // Flat circular disk ground["radius"] = 10.0; // Large radius (10 units) ground.parentTo(groundXform); // Put ground in its position controller // ======================================================================== // 🎥 Step 6: Setup Rendering (what/where to save) // ======================================================================== auto beautyPass = scene.createNode("beautyPass"); // Defines what to render beautyPass["saveImage"] = true; // Save the image to disk beautyPass["outputName"] = "poomer-bella-basic-renderer"; // Filename (will be my_render.png) auto outputImagePath = scene.createNode("outputImagePath"); // Where to save outputImagePath["dir"] = "."; // Current directory beautyPass["overridePath"] = outputImagePath; // Connect save location settings["beautyPass"] = beautyPass; // Tell scene to use this render setup // ======================================================================== // 📱 Step 7: Connect Our Observer + Position Camera // ======================================================================== MyEngineObserver observer; // Create our notification listener engine.subscribe(&observer); // Connect it to the engine // Position camera to look at the scene (like framing a photo) dl::bella_sdk::zoomExtents( scene.cameraPath(), // Camera's position controller (not camera itself!) dl::Vec3{ 0,0,0 }, // Look at the origin (center of sphere) 3.0 // Distance from center ); // Tilt camera down 75 degrees (like looking down from above) dl::bella_sdk::orbitCamera(scene.cameraPath(), dl::Vec2{ 0.0, -75.0 }); // ======================================================================== // 🚀 Step 8: Render! // ======================================================================== engine.start(); // Begin rendering // Wait for rendering to complete (check every 100ms) while (engine.rendering()) { std::this_thread::sleep_for(std::chrono::milliseconds(100)); } engine.stop(); // Clean shutdown engine.unsubscribe(&observer); // Disconnect our listener return 0; // Tell operating system: "Success!" }