In this tutorial we're going to create a new NAP application that renders a textured cube to a window. The rotation speed of the cube can be controlled with a slider. Instruction videos are provided to guide you through the individual stages. Upon completion you will have a better understanding of working with NAP, hopefully something clicks! It takes about 45 minutes to complete. Open the rotatingtexcube
demo for inspiration if you get stuck.
This tutorial assumes you are working from a pre-compiled distributable NAP package. However, some people prefer working with NAP directly from source. Fortunately most instructions in this document are the same for both contexts, except some paths. Additional information is provided when this is the case.
Use a terminal to navigate to the tools
directory inside the NAP root directory. Run the create_app
script with rotatingcube
as the first argument, the name of your application:
After creation your new application is located at apps/rotatingcube
. This directory contains your source-code, assets, solution and build instructions. The app.json
file in the root of the directory defines various project specific settings, such as: the name of your app , which modules to include and which content to load:
The most important module here is naprotatingcube
. This is your application module, located inside the apps/rotatingcube/module
directory. This directory contains the source-code of resources
and components
that are specific to your project.
Read the Project Management documentation to learn more about managing NAP applications and modules.
Navigate to apps/rotatingcube
and run the build script
to compile the application:
The app is compiled to the bin
directory of your app. Navigate to the bin/Release-*
folder and launch the rotatingcube
executable in that directory. You should see the following window:
Note that when working from source the app is compiled to the bin
directory in the nap root, not the application root
We're going to use Napkin to edit the application content.
From the NAP root directory browse to the tools/napkin
and launch the napkin
executable.
Note that when working from source you must compile Napkin first. The compiled Napkin binary can be found in the bin/Release-*/napkin
directory.
Project > Open...
apps/rotatingcube
app.json
The app.json
points to an external file that holds the content of your application. By default, application content is stored in data/objects.json
(relative to the application root). You use Napkin to author this content.
All externally sourced assets (such as images, audio, video, shaders etc.) that your content references must be placed in the data
directory of your application, in this case apps/rotatingcube/data
.
If Napkin fails to load the project make sure to build the application (in Release
mode) at least once before loading it. This ensures that the custom application module naprotatingcube
is compiled for you. The editor can then load and inspect it. All other modules (render, audio etc.) are pre-compiled and should work out of the box.
The AppRunner
allows you to start / stop the application you are working on. This is very useful when you are editing application content and frequently have to re-launch the application. Click on the play
button in the app runner panel to launch the app.
Let's begin by adding the resources that define, when combined, a textured cube in NAP.
Start by creating a uniform box mesh at the center of the scene.
In the resources panel:
Resources
itemCreate Resource
nap::BoxMesh
CubeMesh
Let's add the image that we want to apply as a texture. Following the steps above: create a nap::ImageFromFile
resource and rename it to CubeTexture
.
If we now save the file and start the application it will fail to initialize because the CubeTexture
doesn't point to a valid image on disk. We must provide it with one.
Download cube_texture.jpg and move it to apps/rotatingcube/data
.
Select the CubeTexture
in the resources panel. Link in the image by clicking on the folder icon next to ImagePath
in the inspector panel. Browse to the texture in the data
directory and select it.
Next we create a shader program that we use to render the cube. Create a nap::ShaderFromFile
resource and rename it to CubeShader
.
Download the cube.vert and cube.frag shader files and move them to apps/rotatingcube/data/shaders
.
Select the CubeShader
in the resources panel. In the inspector panel click on the folder icon next to VertShader
and select the cube.vert
shader file. Continue by clicking on the folder icon next to Fragshader
and select the cube.frag
shader file.
Let's add a material, so we can bind a texture to the shader and give it a color. Create a nap::Material
resource, rename it to CubeMaterial
and select it.
In the inspector panel click on the icon next to Shader
and select the CubeShader
in the popup.
Right-click on Uniforms
in the inspector panel and add a nap::UniformStruct
to it. From the popup menu, choose UBO
, then navigate to the color
uniform that was added. Set its value to 1 1 1. By doing this, you have set the default color of the cube to white by creating a binding in the material that targets the UBO.color
uniform in the shader:
Right-click on Samplers
in the inspector panel and add a nap::Sampler2D
to it. From the popup menu, choose inTexture
and expand the newly inserted item. Now click on the icon next to Texture
and select the CubeTexture
in the popup. By doing this, you set the default texture of the cube to CubeTexture
by creating a binding in the material that targets the inTexture
sampler in the shader.
Continue by adding an entity that renders the cube to screen.
In the resources panel:
Entities
itemCreate Entity
CubeEntity
The CubeEntity
needs 3 components: Transformcomponent to position it, a RotateComponent to rotate it and a RenderableMeshComponent to render it.
In the resources panel:
CubeEntity
Add Component...
nap::TransformComponent
CubeTransformComponent
Repeat these steps for the nap::RotateComponent
and nap::RenderableMeshComponent
. Rename them to CubeRotateComponent
and CubeRenderComponent
.
Select the CubeTransformComponent
in the resources panel and change the UniformScale
in the inspector panel to 4.0. This makes the box 4 times as large.
Select the CubeRotateComponent
in the resources panel. Expand the Axis
property in the inspector panel and change it to 0 1 0
. Next change the Speed
to 0.1. This tells the component to rotate the cube 360 degrees over the Y-axis in 10 seconds.
We need to tell the component which mesh to render using what material.
Select the CubeRenderComponent
. In the inspector panel click on the icon next to Mesh
and select the CubeMesh
in the popup. Continue by expanding the MaterialInstance
item. Click on the icon next to Material
and select the CubeMaterial
in the popup.
What's left on the content side is to add the entity to the scene, otherwise it is not created (instantiated) on startup.
In the scene panel:
Scene
itemAdd Entity...
CubeEntity
Save the file File -> Save
and launch the app. You should see the same window popup as before without any notable changes. That's because we did not tell the app to render the cube. NAP created and validated the cube entity and resources but has no instructions to render it. We have to add some logic to the app that instructs the system to draw it.
If at this point the application fails to initialize check the ouput of the log. You probably missed a step. If that's the case try to fix it by tracing the error message. You can look at the rotatingtexcube
demo for additional guidance.
Close Napkin and open the rotatingcubeapp.h
file located inside the apps/rotatingcube/src
directory. This document, together with the .cpp
file, contains the application runtime code. It allows you to control the flow of data and render specific objects to screen using the resources we just created.
The init method is used to initialize important parts of your application and store references to resources. For this example we need access to the CubeEntity
. Add the following line of code to your application class declaration, right after mGnomonEntity
in rotatingcubeapp.h
:
And add the following line of code to the end of the ::init()
method of your application in rotatingcubeapp.cpp
:
We just created a link to the cube entity. We can use this link to manipulate the entity and it's components when the app is running.
The update
method is called every frame. The parameter deltaTime
indicates how many seconds have passed since the last update call. You should perform any app specific logic in here that does not concern rendering.
Let's add a slider that controls the rotation speed of the cube. Add the following include directive to rotatingcubeapp.cpp
:
.. and add the following block of code to the update
method, right after processWindowEvents
:
render
is called after update
. You use this call to render geometry and UI elements to a window or render target. You have to tell the renderer what you want to render and where to render it to. We want to render our cube instead of the gnomon. To do that replace the following line:
with:
This tells the render engine to render the cube instead of the gnomon. The GUI is drawn last, on top of the rest, when mGuiService->draw()
is called. To learn more about rendering with NAP take a look at our render documentation.
The editor and application share the same content. That content is loaded on startup of your application and monitored for changes by NAP. This means that changes you make in the editor are picked up by the running application in real-time. This system is called hot-reloading and allows you to quickly iterate on changes without having to restart the application. It is a feature we rely on heavily during development and is used by artists and designers to edit configurations on the fly. Experment with this feature by changing some settings in the editor and saving your file whilst the app is running, as demonstrated below:
Navigate to apps/rotatingcube
and run:
This creates a distributable package of your application. Append --help
for additional information. By default the application including Napkin and all assets is packaged for you.
Note that when working from source the package
script is located in the NAP root directory.
We've covered a lot of ground in this introduction. From here it's recommended to study some of the many demos that ship with NAP and continue reading the online manual, beginning with system architecture. From there you can try and create your own resource or component. Read the project management documentation if you want to know more about modules, how to add them to your project or share them with others.