-
Notifications
You must be signed in to change notification settings - Fork 1
Creating a New App
SLProject provides an framework to develop cross-platform apps that run on Windows, macOS, Linux, Android, iOS, and on the Web. This guide explains how to create a small example app.
To get started, create a subdirectory inside the apps
directory called app_example
with the following structure:
app_example/
├── AppExample.cpp
└── CMakeLists.txt
Next, we have to tell CMake about our new app. Append the following line to apps/CMakeLists.txt
:
add_subdirectory(app_example)
To create the CMake target for the app, paste the following snippet into apps/app_example/CMakeLists.txt
:
sl_add_app(
TARGET "app-example"
PLATFORMS "GLFW;EMSCRIPTEN"
SOURCES "AppExample.cpp"
)
This creates a new CMake target called app-example
that runs on GLFW (Windows, macOS, Linux) and Emscripten (the Web). We'll add support for mobile platforms later on. In our example, we only have one source file to add: AppExample.cpp
, which will contain our main function.
We're now ready to write some C++ code. Paste the following snippet into apps/app_example/AppExample.cpp
:
#include <App.h>
#include <SLScene.h>
static SLScene* createScene(SLSceneID sceneID)
{
return new SLScene("Example Scene");
}
int SL_MAIN_FUNCTION(int argc, char* argv[])
{
return App::run({.windowTitle = "Example App",
.onNewScene = createScene});
}
In this snippet, we include the App.h
header that contains everything required to get a cross-platform app running. Our main function is defined using a macro (SL_MAIN_FUNCTION
) that expands to the platform-dependent main function name. The main function calls App::run
with two arguments: the name of the app and a callback function to create a scene. Currently, we just create an empty default scene that shows a gray background.
Building and running the app involves some platform-specific steps.
Open a Developer PowerShell in the SLProject root directory and run the following commands:
cmake -Bbuild .
cmake --build build --target app-example --config Debug --parallel
.\build\Debug\app-example-debug
Open a terminal in the SLProject root directory and run the following commands:
cmake -Bbuild -DCMAKE_BUILD_TYPE=Debug .
cmake --build build --target app-example --parallel
./build/app-example-debug
Read the build page for Emscripten on how to setup emscripten. After installation, open a terminal in the SLProject root directory, activate your Emscripten environment, and run the following commands:
emcmake cmake -Bbuild-emscripten -DCMAKE_BUILD_TYPE=Debug .
cmake --build build-emscripten --target app-example --parallel
For every app, SLProject generates a Python script that serves its content over HTTP.
Serving on Windows: cd build-emscripten; python .\serve-app-example.py
Serving on macOS and Linux: cd build-emscripten && python3 ./serve-app-example.py
To view your app, open a browser and go to http://localhost:8080.
Now that we have set up a basic app, we can start adding some objects into our scene.
Add the following includes at the top of AppExample.cpp
:
#include <SLSceneView.h>
#include <SLCamera.h>
#include <SLLightDirect.h>
#include <SLMaterial.h>
#include <SLNode.h>
#include <SLBox.h>
Next, add the following class below the includes:
class AppExampleScene : public SLScene
{
public:
AppExampleScene() : SLScene("Example Scene") {}
void assemble(SLAssetManager* am, SLSceneView* sv) override
{
SLCamera* camera = new SLCamera();
camera->translation(2, 1, 3);
camera->lookAt(0, 0, 0);
camera->focalDist(camera->translationWS().distance(SLVec3f::ZERO));
SLLightDirect* light = new SLLightDirect(am, this);
light->translate(-0.5f, 1.0f, 0.75f);
light->lookAt(0, 0, 0);
SLMaterial* material = new SLMaterial(am, "Red Material", SLCol4f::RED);
SLNode* box = new SLNode("Box Node");
box->addMesh(new SLBox(am, -0.5f, -0.5f, -0.5f, 0.5f, 0.5f, 0.5f, "Box", material));
SLNode* scene = new SLNode();
scene->addChild(light);
scene->addChild(camera);
scene->addChild(box);
root3D(scene);
sv->camera(camera);
}
};
AppExampleScene
defines the contents of our example scene. It inherits from SLScene
and overrides the method assemble
. This method takes care of instantiating our scene content such as cameras, lights, materials, etc.
The last change is to update our createScene
function such that it creates an instance of AppExampleScene
instead of the generic SLScene
:
static SLScene* createScene(SLSceneID sceneID)
{
return new AppExampleScene();
}
If you run the app now, you should see a red cube illuminated by a directional light.
Assets such as textures or models are usually stored in files and can be loaded by SLProject.
First, we need some more includes:
#include <SLAssetLoader.h>
#include <AppDemo.h>
Now update the AppExampleScene
so it looks like this:
class AppExampleScene : public SLScene
{
public:
AppExampleScene() : SLScene("Example Scene") {}
void registerAssetsToLoad(SLAssetLoader& al) override
{
al.addTextureToLoad(_woodTexture, AppDemo::texturePath + "wood0_0512_C.jpg");
}
void assemble(SLAssetManager* am, SLSceneView* sv) override
{
// ...
}
private:
SLGLTexture* _woodTexture;
};
In SLProject, all file I/O has to be performed before scene assembly. For this purpose, we override the registerAssetsToLoad
method of SLScene
. In there, we tell the asset loader which files need to be loaded. In this example, we load a texture asset and store it in a class member called _woodTexture
.
Now that we have a wood texture, we can replace the red color on our material with that texture:
void assemble(SLAssetManager* am, SLSceneView* sv) override
{
// ...
SLMaterial* material = new SLMaterial(am, "Wood Material", _woodTexture);
// ...
}
If you run the app now, you should see a wooden cube instead of the previous red cube.
SLProject uses Dear ImGui for its GUI. The Dear ImGui setup is done behind the scenes, in our app we only have to define a function that uses Dear ImGui to build the controls.
First, we need to add another include:
#include <SLGLImGui.h>
Now define a function to create the GUI:
static void buildGui(SLScene* s, SLSceneView* sv)
{
if (ImGui::BeginMainMenuBar())
{
if (ImGui::BeginMenu("View"))
{
if (ImGui::MenuItem("Reset Camera"))
{
sv->camera()->translation(2, 1, 3);
sv->camera()->lookAt(0, 0, 0);
}
ImGui::EndMenu();
}
ImGui::EndMainMenuBar();
}
}
This function creates a menu bar with a menu View
that contains a button Reset Camera
that resets the camera to its initial state.
To tell SLProject about this function, pass it to App::run
:
int SL_MAIN_FUNCTION(int argc, char* argv[])
{
return App::run({.windowTitle = "Example App",
.onNewScene = createScene,
.onGuiBuild = buildGui});
}
To build our app for mobile platforms, we have to specify that our app runs on Android and iOS. Open apps/app_example/CMakeLists.txt
and add ANDROID
and IOS
to the list of supported platforms:
sl_add_app(
TARGET "app-example"
PLATFORMS "GLFW;EMSCRIPTEN;ANDROID;IOS"
SOURCES "AppExample.cpp"
)
Next, we set some platform-specific configuration options. On Android, we need to specify the directory of our Android app module. We use the app
module in SLProject's example Android project at apps/source/platforms/android/example_project
. Add the following configuration option:
sl_add_app(
...
ANDROID_APP_DIR "${SL_PROJECT_ROOT}/apps/source/platforms/android/example_project/app"
)
On iOS, we specify an Information Property List (Info.plist) template and values for its variables. We use the Info.plist
template from SLProject's example iOS project at apps/source/platforms/ios/example_project
.
sl_add_app(
...
IOS_INFO_PLIST "${SL_PROJECT_ROOT}/apps/source/platforms/ios/example_project/plist.in"
IOS_DISPLAY_NAME "SL Example"
IOS_COPYRIGHT "Copyright [Your Name]"
IOS_ICON_NAME "AppIcon"
)
We also have to add some additional resources for iOS like asset catalogs or storyboards:
sl_add_app(
...
IOS_RESOURCES
"${SL_PROJECT_ROOT}/apps/source/platforms/ios/example_project/Base.lproj/ViewController_iPad.xib"
"${SL_PROJECT_ROOT}/apps/source/platforms/ios/example_project/Base.lproj/ViewController_iPhone.xib"
"${SL_PROJECT_ROOT}/apps/source/platforms/ios/example_project/LaunchScreenSLDemo.storyboard"
"${SL_PROJECT_ROOT}/apps/source/platforms/ios/example_project/Images/Images.xcassets"
"${SL_PROJECT_ROOT}/apps/source/platforms/ios/example_project/Images/LaunchImage_1024x768.png"
)
This is how the final CMake configuration for your example app should look like in order to run on Windows, macOS, Linux, Android, iOS, and the Web:
sl_add_app(
TARGET "app-example"
PLATFORMS "GLFW;EMSCRIPTEN;ANDROID;IOS"
SOURCES "AppExample.cpp"
ANDROID_APP_DIR "${SL_PROJECT_ROOT}/apps/source/platforms/android/example_project/app"
IOS_INFO_PLIST "${SL_PROJECT_ROOT}/apps/source/platforms/ios/example_project/plist.in"
IOS_DISPLAY_NAME "app-example"
IOS_COPYRIGHT "Copyright [Your Name]"
IOS_ICON_NAME "AppIcon"
IOS_RESOURCES
"${SL_PROJECT_ROOT}/apps/source/platforms/ios/example_project/Base.lproj/ViewController_iPad.xib"
"${SL_PROJECT_ROOT}/apps/source/platforms/ios/example_project/Base.lproj/ViewController_iPhone.xib"
"${SL_PROJECT_ROOT}/apps/source/platforms/ios/example_project/LaunchScreenSLDemo.storyboard"
"${SL_PROJECT_ROOT}/apps/source/platforms/ios/example_project/Images/Images.xcassets"
"${SL_PROJECT_ROOT}/apps/source/platforms/ios/example_project/Images/LaunchImage_1024x768.png"
)
To actually build and run the app on your mobile device, follow the build guides for Android and iOS.
Now that you have created a basic example app, you can take a look at the online documentation or at the demo application to learn more.