Skip to content

Commit

Permalink
Add examples
Browse files Browse the repository at this point in the history
For now, there is only one example - `BasicWidgets`.
  • Loading branch information
congard committed Oct 3, 2023
1 parent ac7ddd6 commit 8aa4394
Show file tree
Hide file tree
Showing 13 changed files with 378 additions and 0 deletions.
22 changes: 22 additions & 0 deletions examples/BasicWidgets/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
cmake_minimum_required(VERSION 3.18)
project(BasicWidgets)

set(CMAKE_CXX_STANDARD 20)

add_executable(BasicWidgets
src/main.cpp
src/MainContent.cpp
src/MainContent.h
src/ui/MainScene.cpp
src/ui/MainScene.h
src/ui/MainLayer.cpp
src/ui/MainLayer.h)

target_link_libraries(BasicWidgets PRIVATE algine)

if (WIN32)
algine_target_mklink(
TARGET BasicWidgets
ALGINE_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/../algine"
BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/../bin")
endif()
110 changes: 110 additions & 0 deletions examples/BasicWidgets/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# BasicWidgets

Supported systems: Linux, Windows

This very basic example shows how to use basic widgets using XML.

> [!NOTE]
> Working directory must be set to the `resources` directory.
This example uses
[Noto Sans Black](https://fonts.google.com/noto/specimen/Noto+Sans) font.

## Introduction

Algine is a modular, easy-to-use 3D rendering engine. Like any other
framework, Algine has its own features.

### Objects

All classes are divided into two types: objects and ordinary classes.
Objects are special classes, which inherit the `algine::Object` class.
Such classes have some important features: they can have many children,
but only one parent. All objects by default have `algine::GlobalScene`
as the parent and must be created on the heap, using the `new` operator.

#### Lifecycle

As mentioned before, objects must be created using operator `new`.

> [!IMPORTANT]
> Allocating on the heap is crucial, since it will allow to correctly
> destroy all children while destroying the parent. Otherwise, you will
> get a segmentation fault.
If you want to manually delete an object, you can use operator `delete`.
In that case, all its children will be also deleted, moreover, the object
will be automatically removed from the children list of its parent.

> [!WARNING]
> If you want to use objects like regular classes, you can just pass
> `nullptr` as the parent. However, it is highly unrecommended and can be
> really dangerous. Do it on your own risk.
### Display hierarchy

Okay, now you already know what is an object. But how we can render things?

#### `Window`

As the name suggests, `Window` is a Window. It is the base class for
platform-depended windows like e.g. `GLFWWindow` or `QtWindow`. This class
receives events, executes tasks, is responsible for the render loop. But
how exactly this class renders your content?

#### `Content`

`Content` is an entry point for rendering. It has an abstract function
`render` which needs to be overridden. It also has virtual functions like
`onShow` and `onHide`. `Content` has width and height, received from the
window. But how we can receive more events?

##### `UnifiedEventHandler`

`UnifiedEventHandler` is a cross-platform event handler. Typically, you
want your custom content to inherit this class, and then, somewhere in
`YourContent::onShow`, register a new event handler by using

```cpp
getWindow()->setEventHandler(this);
```
Interesting, but `Content` will not render my scene, will it?
#### `Scene`
As the name suggests, this is scene. Scene is an object, which has some
useful functions like `show`, `hide`, `render`, `addChild` etc. `Scene`
represents your scene. Feel free to create as many class members as you
wish. By default, a scene is hidden, so you should call `show` before
`render`, if you want to see something, of course. But can I render my
whole scene just here? Well, yes, but no. In order to render something,
you need to use `Renderer`.
#### `Renderer` and `RenderPass`
Finally! `Renderer` performs `RenderPass` rendering. `RenderPass` has
virtual function `render`, which needs to be overridden. So yes, now you
can place all your rendering code in `YourRenderPass::render` and add it
to your renderer. Do not forget to call `Renderer::render` in `YourSceene`!
## Summary
So, generally speaking, normally we want to have the following structure:
- `Window`
- `Content`
- `UnifiedEventHandler`
- `Scene`
- `Renderer`
- `RenderPass`
Of course, in the current engine architecture, some stages are not obligatory,
but who knows, what will be changed in the future?
## Conclusion
After reading this loooong document, you may have some basic knowledge about
some parts of the engine. Congratulations! Now, you can enjoy the code :)
[//]: # (TODO: move this documentation to the HelloCube example)
27 changes: 27 additions & 0 deletions examples/BasicWidgets/resources/BasicWidgets.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<RelativeLayout
name="container0"
horizontalSizePolicy="match_parent"
verticalSizePolicy="match_parent"
padding="64 64 64 64"
backgroundColor="#ff333333">

<RelativeLayout
name="container1"
horizontalSizePolicy="match_parent"
verticalSizePolicy="match_parent"
padding="0 0 0 0"
backgroundColor="#ff555555"
p_layoutAlignment="left|top">

<Label
name="label0"
text="Test label"
fontSrc="NotoSans-Black.ttf"
rotate="30"
scale="1.5"
backgroundColor="#ff777777"
p_layoutAlignment="center" />

</RelativeLayout>

</RelativeLayout>
Binary file not shown.
24 changes: 24 additions & 0 deletions examples/BasicWidgets/src/MainContent.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#include "MainContent.h"
#include "ui/MainScene.h"

#include <algine/core/Framebuffer.h>
#include <algine/core/Window.h>

MainContent::MainContent()
: m_scene(new UI::MainScene(this))
{
m_scene->listen(this);
}

void MainContent::onShow() {
getWindow()->setEventHandler(this);
m_scene->setSize(width(), height());
m_scene->show();
}

void MainContent::render() {
Framebuffer::getDefault()->bind();
Framebuffer::getDefault()->clear(Framebuffer::ColorBuffer | Framebuffer::DepthBuffer);
Engine::setViewport(width(), height());
m_scene->render();
}
24 changes: 24 additions & 0 deletions examples/BasicWidgets/src/MainContent.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#ifndef ALGINE_EXAMPLES_MAINCONTENT_H
#define ALGINE_EXAMPLES_MAINCONTENT_H

#include <algine/core/unified/UnifiedEventHandler.h>
#include <algine/core/Content.h>

using namespace algine;

namespace UI {
class MainScene;
}

class MainContent: public Content, public UnifiedEventHandler {
public:
MainContent();

void onShow() override;
void render() override;

private:
UI::MainScene *m_scene;
};

#endif//ALGINE_EXAMPLES_MAINCONTENT_H
41 changes: 41 additions & 0 deletions examples/BasicWidgets/src/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#include <algine/core/Engine.h>

#ifdef ALGINE_QT_PLATFORM
#include <algine/core/window/QtWindow.h>
#else
#include <algine/core/window/GLFWWindow.h>
#endif

#include "MainContent.h"

using namespace algine;

static void exec() {
Engine::setDPI(130);

#ifdef ALGINE_QT_PLATFORM
auto window = new QtWindow();
window->setDimensions(512, 512);

window->addOnInitializedListener([window]() {
window->setContent(new MainContent());
window->renderLoop(1000);
});

window->show();
#else
GLFWWindow window("BasicWidgets", 1366, 768);
window.setFullscreenDimensions(1366, 768);
window.setMouseTracking(true);
window.setKeyboardTracking(true);
window.setWindowStateTracking(true);
//window.setCursorMode(Window::CursorMode::Disabled);
window.setContentLater<MainContent>();
window.renderLoop();
#endif
}

int main(int argc, char **argv) {
Engine::exec(argc, argv, &exec);
return 0;
}
45 changes: 45 additions & 0 deletions examples/BasicWidgets/src/ui/MainLayer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#include "MainLayer.h"

#include <algine/core/widgets/Container.h>
#include <algine/core/log/Log.h>
#include <algine/core/log/logger/glm.h>

#include <tulz/demangler.h>

namespace dmg = tulz::demangler;

namespace algine {
template<typename T, typename L>
Logger&& operator<<(L &&logger, const Point<T> &p) {
auto tName = typeid(T).name();
logger << "Point<" << dmg::demangle(tName) << "> {" << p.getX() << ", " << p.getY() << "}";
return std::move(logger);
}
}

namespace UI {
MainLayer::MainLayer(Widgets::Scene *parent)
: Widgets::Layer(parent)
{
auto container = Widget::constructFromXMLFile<Container*>("BasicWidgets.xml", this);
setContainer(container);

container->findChild<Widget*>("label0")->setEventListener(Event::Click, [](Widget *widget, const Event &event) {
auto v2pi = []<typename T>(const glm::vec<2, T> &v) -> PointI {
return {(int) v.x, (int) v.y};
};

auto &info = event.getPointerInfo();

Log::verbose("MainLayer") << "label0 has detected click:\n"
<< "label0 local coords: " << info.getPos() << "\n"
<< "parent local coords: " << widget->mapFromParent(v2pi(info.getPos()));
});

container->findChild<Widget*>("container1")->setEventListener(Event::Click, [](Widget*, const Event &event) {
auto &info = event.getPointerInfo();
Log::verbose("MainLayer") << "container1 has detected click:\n"
<< "container1 local coords: " << info.getPos();
});
}
}// namespace ui
15 changes: 15 additions & 0 deletions examples/BasicWidgets/src/ui/MainLayer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#ifndef ALGINE_EXAMPLES_MAINLAYER_H
#define ALGINE_EXAMPLES_MAINLAYER_H

#include <algine/core/widgets/Layer.h>

using namespace algine;

namespace UI {
class MainLayer: public Widgets::Layer {
public:
explicit MainLayer(Widgets::Scene *parent);
};
}// namespace UI

#endif//ALGINE_EXAMPLES_MAINLAYER_H
11 changes: 11 additions & 0 deletions examples/BasicWidgets/src/ui/MainScene.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#include "MainScene.h"
#include "MainLayer.h"

namespace UI {
MainScene::MainScene(Object *parent)
: Widgets::Scene(parent)
{
auto layer = new MainLayer(this);
layer->show();
}
}
15 changes: 15 additions & 0 deletions examples/BasicWidgets/src/ui/MainScene.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#ifndef ALGINE_EXAMPLES_MAINSCENE_H
#define ALGINE_EXAMPLES_MAINSCENE_H

#include <algine/core/widgets/Scene.h>

using namespace algine;

namespace UI {
class MainScene : public Widgets::Scene {
public:
explicit MainScene(Object *parent);
};
}

#endif//ALGINE_EXAMPLES_MAINSCENE_H
24 changes: 24 additions & 0 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
cmake_minimum_required(VERSION 3.18)
project(algine_examples)

set(CMAKE_CXX_STANDARD 20)

# silence warnings from sol2
add_compile_options(-Wno-unknown-warning-option)

include(../cmake/mold.cmake)
try_use_mold()

macro(set_global name value)
set(${name} ${value} CACHE INTERNAL ${name})
endmacro()

set_global(ASSIMP_BUILD_ALL_IMPORTERS_BY_DEFAULT OFF)
set_global(ASSIMP_BUILD_OBJ_IMPORTER ON)
set_global(ASSIMP_BUILD_ZLIB OFF)
set_global(ASSIMP_BUILD_ASSIMP_TOOLS OFF)
set_global(ASSIMP_BUILD_TESTS OFF)

add_subdirectory(../ algine)

add_subdirectory(BasicWidgets)
20 changes: 20 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Examples

> [!NOTE]
> In order to load examples, open `CMakeLists.txt` in the current directory.
> Do not open examples directly.
## List

1. [Basic widgets](BasicWidgets)

## TODO

- [ ] Hello Cube
- [x] Basic widgets
- [ ] UI Overlay: complete example (with all abstractions included)
- [ ] Basic Lua scripting
- [ ] Cross-platform example: Linux, Android, Windows

These examples should be like mini-lessons, with all necessary documentation
included.

0 comments on commit 8aa4394

Please sign in to comment.