CWindow is cross-platform multi renderer lib for creating simple meshes, shaders, and computing on GPU. It unify multiple renderers to simple most often used operations like, binding shaders, rendering mashes, swapping window etc. Good to use in simple project or just learning shaders and rendering.
![]() |
![]() |
- About
- Screenshots
- Table of Contents
- Installation
- Configurations flags
- Gui Usage
- Renderer Usage
- WindowData
- InputData
- Uniform
- Shader
- ComputeShader
- Mesh
- FreeCamera3D
- Implemented optimizations
- Full Example
- Features
- Libraries
- License
- Prerequisites
- Other projects that use it
git clone --recursive https://github.com/Daynlight/CWindow.git git submodule init
git submodule update cmake_minimum_required(VERSION 3.15)
project(Example LANGUAGES CXX C)
add_subdirectory(CWindow/CWindow)
set(src "Main.cpp")
set(headers "Mandelbrot.h")
add_executable(Example ${src})
target_link_libraries(Example CWindow) mkdir build/
cd build/
cmake .. mkdir build/
cd build/
cmake .. -DRENDERER="DIRECTX" -DPLATFORM="WIN32" ./Example.exe- WIN32 - windows platform
- UNIX - linux platform
- OPENGL - OpenGL (glad 4.3, glfw cross-platform)
- Platform is detected in cmake
- Default renderer is OpenGL
- Initialize renderer and window
- Initialize gui. Here you can provide custom gui style with ImGuiIo parameter
You can provide workspace
You have to provide std::function<void()> render_windows that specify place where window will be render
gui->setWorkspace([](std::function<void()> render_windows){
const ImGuiViewport* viewport = ImGui::GetMainViewport();
ImGui::SetNextWindowPos(viewport->WorkPos);
ImGui::SetNextWindowSize(viewport->WorkSize);
render_windows();
});- You need to specify unique name for renderer window. It is used for fast look up
- If you want update it you need add window with same name
- You can provide custom destruction function as second param
gui->addWindow("Example", {[](CW::Renderer::iRenderer *renderer){
ImGui::Begin("Example", nullptr);
ImGui::Text("Hello Gui");
ImGui::End();
}});#include "Renderer.h"
#include "Gui.h"
int main(){
// init renderer
CW::Renderer::Renderer renderer;
renderer.createWindow();
renderer.createRenderer();
// init gui and add Settings Window
CW::Gui::Gui gui(&renderer);
gui.addWindow("Example", {[](CW::Renderer::iRenderer *renderer){
ImGui::Begin("Example", nullptr);
ImGui::Text("Hello Gui");
ImGui::End();
}});
// main loop
while(renderer.getWindowData()->should_close){
gui.render();
renderer.windowEvents();
renderer.swapBuffer();
};
return 0;
}- Platform is detected automatically.
- When Renderer is initialized, auto window creation and renderer setup occur.
- On creation, you can pass
truefor a windowless renderer.
You can edit the window properties using the following functions:
// Set window mode (e.g., fullscreen, windowed)
renderer.setWindowMode(CW::Renderer::WindowMode::FULLSCREEN);
// Set window title
renderer.setWindowTitle("My Application Title");
// Enable or disable vertical sync
renderer.setVsync(true);
// Minimize or maximize the window
renderer.minimizedSwitch();
renderer.maximizeSwitch();The main loop of your application should include the following steps:
// Start a new frame
renderer.beginFrame();
// Handle window events (input, resizing, etc.)
renderer.windowEvents();
// Swap the buffers to display the rendered frame
renderer.swapBuffer(); You can get a reference to Renderer window
APIWindow* windowRef = renderer.getWindow(); // where APIWindow is your Renderer WindowYou can access InputData by renderer->getWindowData()
- should_close
- vsync
- window_mode
- title
- is_focused
- is_minimize
- is_maximize
- delta_time
You can access InputData by renderer->getInputData()
- mouse_x;
- mouse_y;
- scroll_x;
- scroll_y;
- scroll_is_down;
- left_mouse_button_is_down;
- right_mouse_button_is_down;
- Uniform stores shader variables that can be modified from CPU
- Variables are automatically bound to shader when shader is bound
- Uniform compiles automatically when used first time
- Can store multiple types of data (int, float, double, vec2, vec3, dvec2, dvec3)
CW::Renderer::Uniform uniform;// Using operator[] and set<T>
uniform["variableName"]->set<float>(1.0f);
uniform["position"]->set<glm::vec2>({x, y});
uniform["color"]->set<glm::vec3>({r, g, b});// Using operator[] and get<T>
float value = uniform["variableName"]->get<float>();
glm::vec2 position = uniform["position"]->get<glm::vec2>();
glm::vec3 color = uniform["color"]->get<glm::vec3>();intfloatdoubleglm::vec2(2D vector)glm::vec3(3D vector)glm::dvec2(2D double vector)glm::dvec3(3D double vector)
compile()- Manually compile uniform buffer (called automatically when needed)destroy()- Free uniform buffer resources
- Shader combines multiple shaders to one program for rendering
- Automatically compiles when first used via
bind() - Supports multiple uniform bindings
- Supports lazy loading
- Provides shader hot-reloading via
setVertexShader()andsetFragmentShader() - Add any other type of shader to program via
setShader()
// Initialize with vertex and fragment shader sources
CW::Renderer::Shader shader(vertexSource, fragmentSource);shader.setShader(geometrySource, GL_GEOMETRY_SHADER);// Create uniform and add to shader
CW::Renderer::Uniform uniform;
shader.getUniforms().emplace_back(&uniform);
// Set uniform values
uniform["position"]->set<glm::vec2>({0.0f, 0.0f});// Basic render cycle
shader.bind(); // Automatically compiles and binds uniforms
mesh.render(); // Render associated mesh
shader.unbind(); // Unbind shader// Update shaders at runtime
shader.setVertexShader(newVertexSource); // Update vertex shader
shader.setFragmentShader(newFragmentSource); // Update fragment shader
// Next bind() will recompile automaticallysetVertexShader()- Set vertex shadersetFragmentShader()- Set fragment shadersetShader()- Set shaderremoveShaders()- Remove shaderclearShaders()- Remove all shadergetUniforms()- Manage uniformscompile()- Manually compile shader (called automatically by bind)destroy()- Free shader resourcesbind()- Activate shader and bind uniformsunbind()- Deactivate shader
// set sources
std::string vertex = R"(
#version 430
layout(location = 0) in vec3 aPos;
void main() {
gl_Position = vec4(aPos, 1.0);
}
)";
std::string fragment = R"(
#version 430
uniform vec3 color;
out vec4 FragColor;
void main() {
FragColor = vec4(color, 1.0);
}
)"
// Create shader with sources
CW::Renderer::DrawShader shader(vertex, fragment);
// Add uniform
CW::Renderer::Uniform uniform;
uniform["color"]->set<glm::vec3>({1.0f, 0.0f, 0.0f});
shader.getUniforms().emplace_back(&uniform);
// Render cycle
shader.bind();
mesh.render();
shader.unbind();- It is used for computing data on GPU
- You need to provide compute shader on creation
- Is automatically compiled when ran
- On Run you must provide data and threads along X axis
- Optional you can provide threads along Y and Z axis
run()andget()are templated functions
// Create a compute shader
CW::Renderer::ComputeShader computeShader(R"(
#version 430
layout(local_size_x = 1) in;
layout(std430, binding = 0) buffer DataBuffer {
float data[];
};
void main() {
uint index = gl_GlobalInvocationID.x;
data[index] = data[index] * 2.0; // Double each value
}
)");// Prepare input data
std::vector<float> inputData = {1.0f, 2.0f, 3.0f, 4.0f};
// Run computation with 4 threads
computeShader.run<float>(inputData, 4);
// Get results
std::vector<float> results = computeShader.get<float>();void compile(); // Manually compile shader
void destroy(); // Free resources
template<typename T>
void run(std::vector<T> data, // Input data
unsigned int x, // X threads
unsigned int y = 1, // Y threads (optional)
unsigned int z = 1); // Z threads (optional)
template<typename T>
std::vector<T> get(); // Get results- Mesh moves data from vectors to own registers.
- Mesh automatically generate buffer and set locations(
layouts) on gpu. - We prefer using
addVertices()for generating culling box. - You can pass any other data to gpu via
setData<T>().
- vertices via
addVertices() - indices via
addIndices() - any other data via
setData<T>()
setData<T>()removeData()clearData()getCullingBoxExists()getCullingBox()compile()destroy()render()
// Create mesh
CW::Renderer::Mesh mesh;
// Vertices
std::vector<float> vertices({
// Vertices (vec3)
-0.5f, -0.5f, 0.0f, // bottom left
0.5f, -0.5f, 0.0f, // bottom right
0.0f, 0.5f, 0.0f // top
});
mesh.addVertices(vertices, 3, 0);
// Indices
std::vector<unsigned int> indices({
0, 1, 2 // triangle
})
mesh.addIndices(indices);
// Colors
std::vector<float> colors({1.0f, 0.0f, 1.0f});
square.setData<GLfloat>(colors, 3, 1, GL_FLOAT);
// Render cycle
shader.bind();
mesh.render();
shader.unbind();- FreeCamera3D is simple camera in 3d that gives movement rotation and transform matrix(
mat4) - When change mouse movement or exist focus mode use
resetMouse().
transformationreturns transformation matrix for objects.resetMouse()resets mouse positionevent()mouse events and camera movement
CW::Renderer::FreeCamera3D camera(&window); // Init camera
// Variables for swap camera event
float cursor_visible_lock = 0.0f;
bool cursor_lock = true;
// Hide Cursor/Unhide cursor
if(cursor_lock) window.setCursorOn(true);
else window.setCursorOn(false);
// ESC button operation with cooldown
if(window.getInputData()->is_key_down("ESC") && cursor_visible_lock <= 0.0f) {
cursor_lock = !cursor_lock;
cursor_visible_lock = 0.5f;
camera.resetMouse();
}
else if(cursor_visible_lock > 0.0f) cursor_visible_lock -= window.getWindowData()->delta_time;
// Camera events
if(!cursor_lock) camera.event(&window);- Unordered _map for window fast look up
- On run shader compilation and reusing it
- Mesh and Shader lifetime control by
compileanddestroy - Mesh and Shader auto compile when used
- Storing Data every
windowEvent()instead of running all api commands
#include "Renderer.h"
#include "Gui.h"
#include "Shaders.h"
const float scroll_sensitivity = 0.02f;
const float sensitivity = 20.0f;
const float zoom_speed = 0.005;
glm::vec2 last_world_pos;
glm::vec2 last_mouse_pos;
bool animation = false;
float current_zoom_speed = 0.005;
inline std::function<void(CW::Renderer::iRenderer *window)> renderSettingsWindow(CW::Renderer::Uniform* uniform) {
return [uniform](CW::Renderer::iRenderer *window){
glm::vec2 z = (*uniform)["z"]->get<glm::vec2>();
int maxIter = (*uniform)["maxIter"]->get<int>();
glm::vec3 colors = (*uniform)["colors"]->get<glm::vec3>();
colors /= 255;
ImGui::Begin("Settings", nullptr);
if(window->getWindowData()->delta_time >= 0.0f)
ImGui::Text("FPS: %.f", 1.0f / window->getWindowData()->delta_time);
ImGui::InputFloat2("Z_0", &z[0], "%.3f");
ImGui::SliderFloat2("Z_0 Sidler", &z[0], -3, 3, "%.3f");
ImGui::InputFloat3("colors", &colors[0], "%.3f");
ImGui::ColorPicker3("colors", &colors[0]);
ImGui::InputInt("MaxIter", &maxIter);
if(ImGui::Button("Animation"))
animation = !animation;
ImGui::End();
if(animation){
if((*uniform)["zoom"]->get<float>() < 0.002)
current_zoom_speed = -1 * (zoom_speed);
if((*uniform)["zoom"]->get<float>() > 3)
current_zoom_speed = (zoom_speed);
(*uniform)["zoom"]->set<float>((*uniform)["zoom"]->get<float>() - (*uniform)["zoom"]->get<float>() * current_zoom_speed);
}
(*uniform)["z"]->set<glm::vec2>(z);
(*uniform)["maxIter"]->set<int>(maxIter);
(*uniform)["colors"]->set<glm::vec3>(colors * 255.0f);
};
};
int main(){
// init window and renderer
CW::Renderer::Renderer window;
window.setVsync(0);
window.setWindowTitle("Malgenbrota and Julia");
// create uniform and malgenbrota shader
CW::Renderer::Uniform uniform;
CW::Renderer::DrawShader malgenbrot(Fractal::vertex, Fractal::fragment);
malgenbrot.getUniforms().emplace_back(&uniform);
// uniform default values
uniform["z"]->set<glm::vec2>({0.394f, 0.355f});
uniform["maxIter"]->set<int>(500);
uniform["colors"]->set<glm::vec3>({20.0f, 100.0f, 5.0f});
uniform["world_pos"]->set<glm::vec2>({20.0f, 0.0f});
uniform["zoom"]->set<float>(3.0f);
uniform["window_ratio"]->set<glm::vec2>({
window.getWindowData()->width,
window.getWindowData()->height
});
// init gui and add Settings Window
CW::Gui::Gui gui(&window);
gui.addWindow("Settings", renderSettingsWindow(&uniform));
// create viewport mesh
CW::Renderer::Mesh viewport = CW::Renderer::Mesh(
{
-1.0f, 1.0f, 0.0f,
-1.0f, -1.0f, 0.0f,
1.0f, 1.0f, 0.0f,
1.0f, -1.0f, 0.0f,
},
{
0, 1, 2,
1, 3, 2
});
// main loop
while(window.getWindowData()->should_close){
window.beginFrame();
malgenbrot.bind();
viewport.render();
malgenbrot.unbind();
uniform["window_ratio"]->set<glm::vec2>({
window.getWindowData()->width,
window.getWindowData()->height
});
if(window.getInputData()->right_mouse_button_is_down){
uniform["z"]->set<glm::vec2>({
3 * (window.getWindowData()->width / 2 - window.getInputData()->mouse_x) / window.getWindowData()->width,
3 * (window.getWindowData()->height / 2 - window.getInputData()->mouse_y) / window.getWindowData()->height
});
}
if(window.getInputData()->scroll_is_down){
uniform["world_pos"]->set<glm::vec2>({
last_world_pos.x - (window.getInputData()->mouse_x - last_mouse_pos.x) * uniform["zoom"]->get<float>(),
last_world_pos.y + (window.getInputData()->mouse_y - last_mouse_pos.y) * uniform["zoom"]->get<float>()
});
}
else{
last_world_pos = uniform["world_pos"]->get<glm::vec2>();
last_mouse_pos = {window.getInputData()->mouse_x, window.getInputData()->mouse_y};
};
float zoom = uniform["zoom"]->get<float>();
zoom += window.getInputData()->scroll_y * scroll_sensitivity * zoom;
zoom = glm::clamp(zoom, 0.000001f, 10.0f);
uniform["zoom"]->set<float>(zoom);
gui.render();
window.windowEvents();
window.swapBuffer();
};
return 0;
}- Automatic Uniform parameters binding to shader
- Autocompletion when Mesh, Uniform or Shader used
- Platform detection
- Creating window and renderer
- Creating Modular Shader
- Creating Modular Meshes
- Creating Modular Uniform list with references by name
- Binding Uniforms to shader and using as
uniform vec2 name - Compute Shader form computation on gpu
- Editing Window
- Getting user input
- Getting window parameters and store it at once
GNU GENERAL PUBLIC LICENSE Version 2, June 1991
- CMake 3.15 or higher
- C++ compiler with C++17 support
- OpenGL 4.3 compatible graphics card
- Git (for cloning with submodules)

