commit 71307f3e5362287ad917ff8501f5e86af4ca27f5 Author: thejinchao Date: Wed Jun 11 22:45:11 2025 +0800 init commit diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..f794701 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,93 @@ +# +#Davinci (a software render) +# + +cmake_minimum_required (VERSION 3.15) + +project(davinci) + +######## +#system +######## +if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + set(DV_SYS_LINUX TRUE) +elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows") + set(DV_SYS_WINDOWS TRUE) +elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin") + set(DV_SYS_MACOS TRUE) +else() + message(FATAL_ERROR "Unknown target system \"${CMAKE_SYSTEM_NAME}\".") +endif() + +######## +#set compile flag +######## +set(CMAKE_CXX_STANDARD 17) + +######## +#set davinci source files path +######## +set(DV_CORE_INCLUDE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/source/core/include) +set(DV_UTILS_INCLUDE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/source/utils/include) + +######## +#check include file +######## +include(CheckIncludeFile) +check_include_file(unistd.h DV_HAVE_UNISTD_H) +check_include_file(inttypes.h DV_HAVE_INTTYPES_H) +check_include_file(limits.h DV_HAVE_LIMITS_H) + +######## +#make configure files +######## +set(DV_AUTO_INCLUDE_PATH ${CMAKE_CURRENT_BINARY_DIR}/source) +set(DV_AUTO_CONFIG_FILE ${DV_AUTO_INCLUDE_PATH}/dv_config.h) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/source/dv_config.h.in ${DV_AUTO_CONFIG_FILE}) + +######## +#Thirdparty libraries +######## + +# rapidjson +set(RapidJSON_DIR $ENV{LIBRARIES_SDK_ROOT}/rapidjson/cmake) +find_package(RapidJSON REQUIRED) + +#zlib +set(ZLIB_USE_STATIC_LIBS True) +set(ZLIB_ROOT $ENV{LIBRARIES_SDK_ROOT}/zlib) +find_package(ZLIB REQUIRED) + +#libpng +set(PNG_DIR $ENV{LIBRARIES_SDK_ROOT}/libpng/lib/cmake/PNG) +find_package(PNG CONFIG REQUIRED) + +#glm +set(glm_DIR $ENV{LIBRARIES_SDK_ROOT}/glm/share/glm) +find_package(glm CONFIG REQUIRED) + +#tinygltf +set(TinyGLTF_DIR $ENV{LIBRARIES_SDK_ROOT}/tinygltf/lib/cmake/tinygltf) +find_package(TinyGLTF CONFIG REQUIRED) + +set(ImGui_DIR "$ENV{LIBRARIES_SDK_ROOT}/imgui/lib/cmake/imgui") +find_package(ImGui REQUIRED) + +#set(glfw3_DIR $ENV{LIBRARIES_SDK_ROOT}/glfw/lib/cmake/glfw3) +#find_package(glfw3 CONFIG REQUIRED) + +set(SDL3_DIR "$ENV{LIBRARIES_SDK_ROOT}/SDL/cmake") +find_package(SDL3 CONFIG REQUIRED) + +find_package(OpenGL REQUIRED) + +######## +#sub dictionary +######## +set_property(GLOBAL PROPERTY USE_FOLDERS ON) +add_subdirectory(source) + +######## +#test dictionary +######## +add_subdirectory(test) diff --git a/runtime/font/FiraCode-Regular.ttf b/runtime/font/FiraCode-Regular.ttf new file mode 100644 index 0000000..bd73685 Binary files /dev/null and b/runtime/font/FiraCode-Regular.ttf differ diff --git a/runtime/scene/cube.bin b/runtime/scene/cube.bin new file mode 100644 index 0000000..29df9d9 Binary files /dev/null and b/runtime/scene/cube.bin differ diff --git a/runtime/scene/cube.gltf b/runtime/scene/cube.gltf new file mode 100644 index 0000000..8c14a08 --- /dev/null +++ b/runtime/scene/cube.gltf @@ -0,0 +1,191 @@ +{ + "asset":{ + "generator":"Khronos glTF Blender I/O v4.3.47", + "version":"2.0" + }, + "extensionsUsed":[ + "KHR_lights_punctual" + ], + "extensionsRequired":[ + "KHR_lights_punctual" + ], + "extensions":{ + "KHR_lights_punctual":{ + "lights":[ + { + "color":[ + 1, + 1, + 1 + ], + "intensity":54351.41306588226, + "type":"point", + "name":"Light" + } + ] + } + }, + "scene":0, + "scenes":[ + { + "name":"Scene", + "nodes":[ + 0, + 1, + 2 + ] + } + ], + "nodes":[ + { + "mesh":0, + "name":"Cube" + }, + { + "extensions":{ + "KHR_lights_punctual":{ + "light":0 + } + }, + "name":"Light", + "rotation":[ + -0.28416627645492554, + 0.7269423007965088, + 0.34203392267227173, + 0.5232754945755005 + ], + "translation":[ + 4.076245307922363, + 5.903861999511719, + -1.0054539442062378 + ] + }, + { + "camera":0, + "name":"Camera", + "rotation":[ + -0.20997299253940582, + 0.3857799470424652, + 0.09062844514846802, + 0.8937962055206299 + ], + "translation":[ + 7.358891487121582, + 4.958309173583984, + 6.925790786743164 + ] + } + ], + "cameras":[ + { + "name":"Camera", + "perspective":{ + "aspectRatio":1.7777777777777777, + "yfov":0.39959652046304894, + "zfar":100, + "znear":0.10000000149011612 + }, + "type":"perspective" + } + ], + "materials":[ + { + "doubleSided":true, + "name":"Material", + "pbrMetallicRoughness":{ + "baseColorFactor":[ + 0.800000011920929, + 0.800000011920929, + 0.800000011920929, + 1 + ], + "metallicFactor":0, + "roughnessFactor":0.5 + } + } + ], + "meshes":[ + { + "name":"Cube", + "primitives":[ + { + "attributes":{ + "POSITION":0, + "NORMAL":1, + "TEXCOORD_0":2 + }, + "indices":3, + "material":0 + } + ] + } + ], + "accessors":[ + { + "bufferView":0, + "componentType":5126, + "count":24, + "max":[ + 1, + 1, + 1 + ], + "min":[ + -1, + -1, + -1 + ], + "type":"VEC3" + }, + { + "bufferView":1, + "componentType":5126, + "count":24, + "type":"VEC3" + }, + { + "bufferView":2, + "componentType":5126, + "count":24, + "type":"VEC2" + }, + { + "bufferView":3, + "componentType":5123, + "count":36, + "type":"SCALAR" + } + ], + "bufferViews":[ + { + "buffer":0, + "byteLength":288, + "byteOffset":0, + "target":34962 + }, + { + "buffer":0, + "byteLength":288, + "byteOffset":288, + "target":34962 + }, + { + "buffer":0, + "byteLength":192, + "byteOffset":576, + "target":34962 + }, + { + "buffer":0, + "byteLength":72, + "byteOffset":768, + "target":34963 + } + ], + "buffers":[ + { + "byteLength":840, + "uri":"cube.bin" + } + ] +} diff --git a/runtime/scene/monkey.bin b/runtime/scene/monkey.bin new file mode 100644 index 0000000..e7992f3 Binary files /dev/null and b/runtime/scene/monkey.bin differ diff --git a/runtime/scene/monkey.gltf b/runtime/scene/monkey.gltf new file mode 100644 index 0000000..78bbb89 --- /dev/null +++ b/runtime/scene/monkey.gltf @@ -0,0 +1,174 @@ +{ + "asset":{ + "generator":"Khronos glTF Blender I/O v4.3.47", + "version":"2.0" + }, + "extensionsUsed":[ + "KHR_lights_punctual" + ], + "extensionsRequired":[ + "KHR_lights_punctual" + ], + "extensions":{ + "KHR_lights_punctual":{ + "lights":[ + { + "color":[ + 1, + 1, + 1 + ], + "intensity":54351.41306588226, + "type":"point", + "name":"Light" + } + ] + } + }, + "scene":0, + "scenes":[ + { + "name":"Scene", + "nodes":[ + 0, + 1, + 2 + ] + } + ], + "nodes":[ + { + "extensions":{ + "KHR_lights_punctual":{ + "light":0 + } + }, + "name":"Light", + "rotation":[ + -0.28416627645492554, + 0.7269423007965088, + 0.34203392267227173, + 0.5232754945755005 + ], + "translation":[ + 4.076245307922363, + 5.903861999511719, + -1.0054539442062378 + ] + }, + { + "camera":0, + "name":"Camera", + "rotation":[ + -0.20997299253940582, + 0.3857799470424652, + 0.09062844514846802, + 0.8937962055206299 + ], + "translation":[ + 4.200888156890869, + 2.8001465797424316, + 3.949000835418701 + ] + }, + { + "mesh":0, + "name":"Suzanne" + } + ], + "cameras":[ + { + "name":"Camera", + "perspective":{ + "aspectRatio":1.7777777777777777, + "yfov":0.39959652046304894, + "zfar":100, + "znear":0.10000000149011612 + }, + "type":"perspective" + } + ], + "meshes":[ + { + "name":"Suzanne", + "primitives":[ + { + "attributes":{ + "POSITION":0, + "NORMAL":1, + "TEXCOORD_0":2 + }, + "indices":3 + } + ] + } + ], + "accessors":[ + { + "bufferView":0, + "componentType":5126, + "count":1966, + "max":[ + 1.3671875, + 0.984375, + 0.8515625 + ], + "min":[ + -1.3671875, + -0.984375, + -0.8515625 + ], + "type":"VEC3" + }, + { + "bufferView":1, + "componentType":5126, + "count":1966, + "type":"VEC3" + }, + { + "bufferView":2, + "componentType":5126, + "count":1966, + "type":"VEC2" + }, + { + "bufferView":3, + "componentType":5123, + "count":2904, + "type":"SCALAR" + } + ], + "bufferViews":[ + { + "buffer":0, + "byteLength":23592, + "byteOffset":0, + "target":34962 + }, + { + "buffer":0, + "byteLength":23592, + "byteOffset":23592, + "target":34962 + }, + { + "buffer":0, + "byteLength":15728, + "byteOffset":47184, + "target":34962 + }, + { + "buffer":0, + "byteLength":5808, + "byteOffset":62912, + "target":34963 + } + ], + "buffers":[ + { + "byteLength":68720, + "uri":"monkey.bin" + } + ] +} diff --git a/runtime/scene/plane.bin b/runtime/scene/plane.bin new file mode 100644 index 0000000..8e2a83a Binary files /dev/null and b/runtime/scene/plane.bin differ diff --git a/runtime/scene/plane.gltf b/runtime/scene/plane.gltf new file mode 100644 index 0000000..ef4d3be --- /dev/null +++ b/runtime/scene/plane.gltf @@ -0,0 +1,193 @@ +{ + "asset":{ + "generator":"Khronos glTF Blender I/O v4.3.47", + "version":"2.0" + }, + "extensionsUsed":[ + "KHR_lights_punctual" + ], + "extensionsRequired":[ + "KHR_lights_punctual" + ], + "extensions":{ + "KHR_lights_punctual":{ + "lights":[ + { + "color":[ + 1, + 1, + 1 + ], + "intensity":54351.41306588226, + "type":"point", + "name":"Light" + } + ] + } + }, + "scene":0, + "scenes":[ + { + "name":"Scene", + "nodes":[ + 0, + 1, + 2 + ] + } + ], + "nodes":[ + { + "extensions":{ + "KHR_lights_punctual":{ + "light":0 + } + }, + "name":"Light", + "rotation":[ + -0.28416627645492554, + 0.7269423007965088, + 0.34203392267227173, + 0.5232754945755005 + ], + "translation":[ + 4.076245307922363, + 5.903861999511719, + -1.0054539442062378 + ] + }, + { + "camera":0, + "name":"Camera", + "rotation":[ + -0.12186934053897858, + 0, + 0, + 0.9925462007522583 + ], + "translation":[ + 0, + 3.2521567344665527, + 10 + ] + }, + { + "mesh":0, + "name":"Plane", + "scale":[ + 1, + 1, + 3 + ] + } + ], + "cameras":[ + { + "name":"Camera", + "perspective":{ + "aspectRatio":1.7777777777777777, + "yfov":0.39959652046304894, + "zfar":50, + "znear":0.10000000149011612 + }, + "type":"perspective" + } + ], + "meshes":[ + { + "name":"Plane", + "primitives":[ + { + "attributes":{ + "POSITION":0, + "NORMAL":1, + "TEXCOORD_0":2, + "COLOR_0":3 + }, + "indices":4 + } + ] + } + ], + "accessors":[ + { + "bufferView":0, + "componentType":5126, + "count":3, + "max":[ + 1, + 0.4310761094093323, + 1 + ], + "min":[ + -1, + 0, + -9.953811645507812 + ], + "type":"VEC3" + }, + { + "bufferView":1, + "componentType":5126, + "count":3, + "type":"VEC3" + }, + { + "bufferView":2, + "componentType":5126, + "count":3, + "type":"VEC2" + }, + { + "bufferView":3, + "componentType":5123, + "count":3, + "normalized":true, + "type":"VEC4" + }, + { + "bufferView":4, + "componentType":5123, + "count":3, + "type":"SCALAR" + } + ], + "bufferViews":[ + { + "buffer":0, + "byteLength":36, + "byteOffset":0, + "target":34962 + }, + { + "buffer":0, + "byteLength":36, + "byteOffset":36, + "target":34962 + }, + { + "buffer":0, + "byteLength":24, + "byteOffset":72, + "target":34962 + }, + { + "buffer":0, + "byteLength":24, + "byteOffset":96, + "target":34962 + }, + { + "buffer":0, + "byteLength":6, + "byteOffset":120, + "target":34963 + } + ], + "buffers":[ + { + "byteLength":128, + "uri":"plane.bin" + } + ] +} diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt new file mode 100644 index 0000000..e8cbac5 --- /dev/null +++ b/source/CMakeLists.txt @@ -0,0 +1,13 @@ +################# +# sub dictionary +################# +add_subdirectory(core) +add_subdirectory(utils) +add_subdirectory(console) + + +################# +# install +################# +install(FILES ${DV_AUTO_CONFIG_FILE} + DESTINATION include) diff --git a/source/console/CMakeLists.txt b/source/console/CMakeLists.txt new file mode 100644 index 0000000..0ce0bc5 --- /dev/null +++ b/source/console/CMakeLists.txt @@ -0,0 +1,43 @@ +include_directories( + ${DV_AUTO_INCLUDE_PATH} + ${DV_CORE_INCLUDE_PATH} + ${DV_UTILS_INCLUDE_PATH} +) + +set(DVC_IMGUI_SOURCE_FILES + ui/ui_main.h + ui/ui_main.cpp +) +source_group("imgui" FILES ${DVC_IMGUI_SOURCE_FILES}) + +set(DVC_IMGUI_IMPL_SOURCE_FILES + ui/imgui/imgui_impl_sdl3.cpp + ui/imgui/imgui_impl_sdl3.h + ui/imgui/imgui_impl_opengl3.h + ui/imgui/imgui_impl_opengl3.cpp + ui/imgui/imgui_impl_opengl3_loader.h +) +source_group("imgui/impl" FILES ${DVC_IMGUI_IMPL_SOURCE_FILES}) + +set(DVC_ADAPTER_SOURCE_FILES + adapter/adapter_texture.cpp + adapter/adapter_texture.h +) +source_group("adapter" FILES ${DVC_ADAPTER_SOURCE_FILES}) + +add_executable(dv_console WIN32 + dv_console_main.cpp + ${DVC_IMGUI_SOURCE_FILES} + ${DVC_IMGUI_IMPL_SOURCE_FILES} + ${DVC_ADAPTER_SOURCE_FILES} +) +set_property(TARGET dv_console PROPERTY FOLDER "console") + +target_link_libraries(dv_console + dv_core + dv_utils + + ImGui::imgui + SDL3::SDL3-static + OpenGL::GL +) diff --git a/source/console/adapter/adapter_texture.cpp b/source/console/adapter/adapter_texture.cpp new file mode 100644 index 0000000..943fd4c --- /dev/null +++ b/source/console/adapter/adapter_texture.cpp @@ -0,0 +1,185 @@ +#include "adapter_texture.h" +#include + +#include "device/dvc_render_target.h" +#include "math/dvc_math_util.h" + +AdapterTexture::AdapterTexture() + : m_canvasTexture(0) + , m_canvasWidth(0) + , m_canvasHeight(0) + , m_textureWidth(0) + , m_textureHeight(0) + , m_textureBuff(nullptr) +{ +} + +AdapterTexture::~AdapterTexture() +{ + if (m_textureBuff != nullptr) + { + delete[] m_textureBuff; + m_textureBuff = nullptr; + } +} + +void AdapterTexture::init(int32_t canvasWidth, int32_t canvasHeight) +{ + // load and create a texture + glGenTextures(1, &m_canvasTexture); + glBindTexture(GL_TEXTURE_2D, m_canvasTexture); + + // set texture filtering parameters + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + //create texture buff + resize(canvasWidth, canvasHeight); +} + +void AdapterTexture::resize(int32_t canvasWidth, int32_t canvasHeight) +{ + m_canvasWidth = canvasWidth; + m_canvasHeight = canvasHeight; + + //upbound to 4 + int32_t textureWidth = canvasWidth; + if ((textureWidth & 3) != 0) { + textureWidth = (textureWidth & (~3)) + 4; + } + int32_t textureHeight = canvasHeight; + if ((textureHeight & 3) != 0) { + textureHeight = (textureHeight & (~3)) + 4; + } + + //same texture size? + if (m_textureWidth == textureWidth && m_textureHeight == textureHeight) + { + return; + } + m_textureWidth = textureWidth; + m_textureHeight = textureHeight; + + if (m_textureBuff != nullptr) + { + delete[] m_textureBuff; + } + + //alloc new texture memory buf + m_textureBuff = new uint8_t[textureWidth * textureHeight * 3]; + memset(m_textureBuff, 0, textureWidth * textureHeight * 3); +} + +void AdapterTexture::updateTexture(void) +{ + if (m_textureBuff == nullptr || m_textureWidth == 0 || m_textureHeight == 0) + { + //not ready? + return; + } + + //fill as blank texture + const int32_t BLOCK_SIZE = 10; + for (int32_t i = 0; i < m_canvasHeight; i++) + { + uint8_t* p = m_textureBuff + i * m_textureWidth * 3; + for (int32_t j = 0; j < m_canvasWidth; j++) + { + int ii = i / BLOCK_SIZE; + int jj = j / BLOCK_SIZE; + bool i_odd = (ii & 1) != 0; + bool j_odd = (jj & 1) != 0; + + if (i_odd != j_odd) + { + *p++ = (uint8_t)(1 * 255); + *p++ = (uint8_t)(1 * 255); + *p++ = (uint8_t)(1 * 255); + } + else + { + *p++ = (uint8_t)(1 * 220); + *p++ = (uint8_t)(1 * 220); + *p++ = (uint8_t)(1 * 220); + } + } + } + + glBindTexture(GL_TEXTURE_2D, m_canvasTexture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, m_textureWidth, m_textureHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, m_textureBuff); +} + +void AdapterTexture::updateTexture(int32_t start_x, int32_t start_y, const davinci::PixelBuffer& buffer) +{ + int32_t width = buffer.getWidth(); + int32_t height = buffer.getHeight(); + + for (int32_t i = 0; i < height; i++) + { + int32_t y = start_y + i; + if (y >= m_canvasHeight) break; + + uint8_t* ps = m_textureBuff + y * m_textureWidth * 3; + for (int32_t j = 0; j < width; j++) + { + int32_t x = start_x + j; + if (x >= m_canvasWidth) break; + + uint8_t* p = ps + 3 * x; + + const davinci::fVector4 color = buffer.getPixel(j, i); + + *p++ = (uint8_t)(255 * color.x); + *p++ = (uint8_t)(255 * color.y); + *p++ = (uint8_t)(255 * color.z); + } + + } + glBindTexture(GL_TEXTURE_2D, m_canvasTexture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, m_textureWidth, m_textureHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, m_textureBuff); +} + +void AdapterTexture::updateTexture(int32_t start_x, int32_t start_y, const davinci::PixelBuffer& buffer) +{ + int32_t width = buffer.getWidth(); + int32_t height = buffer.getHeight(); + + for (int32_t i = 0; i < height; i++) + { + int32_t y = start_y + i; + if (y >= m_canvasHeight) break; + + uint8_t* ps = m_textureBuff + y * m_textureWidth * 3; + for (int32_t j = 0; j < width; j++) + { + int32_t x = start_x + j; + if (x >= m_canvasWidth) break; + + uint8_t* p = ps + 3 * x; + + const float32_t color = davinci::MathUtil::saturate(buffer.getPixel(j, i)); + + *p++ = (uint8_t)(255 * color); + *p++ = (uint8_t)(255 * color); + *p++ = (uint8_t)(255 * color); + } + + } + glBindTexture(GL_TEXTURE_2D, m_canvasTexture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, m_textureWidth, m_textureHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, m_textureBuff); +} + +void AdapterTexture::draw(void) +{ + glBindTexture(GL_TEXTURE_2D, getTextureID()); + glBegin(GL_QUADS); + + float top_pos = (float)m_textureHeight*2.0f / (float)m_canvasHeight-1.0f; + float right_pos = (float)m_textureWidth*2.0f / (float)m_canvasWidth-1.0f; + + glTexCoord2f(0.0, 0.0); glVertex3f(-1.0f, -1.0f, 0); + glTexCoord2f(1.0, 0.0); glVertex3f(right_pos, -1.0f, 0); + glTexCoord2f(1.0, 1.0); glVertex3f(right_pos, top_pos, 0); + glTexCoord2f(0.0, 1.0); glVertex3f(-1.0f, top_pos, 0); + glEnd(); +} diff --git a/source/console/adapter/adapter_texture.h b/source/console/adapter/adapter_texture.h new file mode 100644 index 0000000..711c575 --- /dev/null +++ b/source/console/adapter/adapter_texture.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include +#include "math/dvc_vector4.h" +#include "device/dvc_pixel_buffer.h" + +class AdapterTexture +{ +public: + void init(int32_t canvasWidth, int32_t canvasHeight); + void resize(int32_t canvasWidth, int32_t canvasHeight); + void updateTexture(void); + void updateTexture(int32_t start_x, int32_t start_y, const davinci::PixelBuffer& buffer); + void updateTexture(int32_t start_x, int32_t start_y, const davinci::PixelBuffer& buffer); + void draw(void); + + uint32_t getTextureID(void) const { + return m_canvasTexture; + } +private: + uint32_t m_canvasTexture; + + int32_t m_canvasWidth; + int32_t m_canvasHeight; + + int32_t m_textureWidth; + int32_t m_textureHeight; + + uint8_t* m_textureBuff; +public: + AdapterTexture(); + ~AdapterTexture(); +}; diff --git a/source/console/dv_console_main.cpp b/source/console/dv_console_main.cpp new file mode 100644 index 0000000..dcb2420 --- /dev/null +++ b/source/console/dv_console_main.cpp @@ -0,0 +1,174 @@ +#include +#include +#include +#include + +#define SDL_MAIN_USE_CALLBACKS 1 /* use the callbacks instead of main() */ +#include +#include +#include + +#include "ui/ui_main.h" +#include "adapter/adapter_texture.h" +#include "device/dvc_render_target.h" + +#include + +int32_t g_screenWidth = 1024; +int32_t g_screenHeight = 768; +static SDL_Window* window = NULL; +AdapterTexture g_adapterTexture; +SDL_GLContext gl_context; +davinci::RenderTarget g_renderTarget; + +davinci::Scene g_scene; + +//------------------------------------------------------------------------------------- +//int main(int argc, char* argv[]) +SDL_AppResult SDL_AppInit(void** appstate, int argc, char* argv[]) +{ + // Setup SDL + SDL_SetAppMetadata("Davinci Console Program", "1.0", "com.thecodeway.davinci"); + + if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD)) + { + printf("Error: SDL_Init(): %s\n", SDL_GetError()); + return SDL_APP_FAILURE; + } + + // Decide GL+GLSL versions +#if defined(IMGUI_IMPL_OPENGL_ES2) + // GL ES 2.0 + GLSL 100 (WebGL 1.0) + const char* glsl_version = "#version 100"; + SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); +#elif defined(IMGUI_IMPL_OPENGL_ES3) + // GL ES 3.0 + GLSL 300 es (WebGL 2.0) + const char* glsl_version = "#version 300 es"; + SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); +#elif defined(__APPLE__) + // GL 3.2 Core + GLSL 150 + const char* glsl_version = "#version 150"; + SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); // Always required on Mac + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2); +#else + // GL 3.0 + GLSL 130 + const char* glsl_version = "#version 130"; + SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); +#endif + + // Create window with graphics context + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); + SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); + Uint32 window_flags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIDDEN; + window = SDL_CreateWindow("Davinci Console Program", g_screenWidth, g_screenHeight, window_flags); + if (window == nullptr) + { + printf("Error: SDL_CreateWindow(): %s\n", SDL_GetError()); + return SDL_APP_FAILURE; + } + SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED); + gl_context = SDL_GL_CreateContext(window); + if (gl_context == nullptr) + { + printf("Error: SDL_GL_CreateContext(): %s\n", SDL_GetError()); + return SDL_APP_FAILURE; + } + + SDL_GL_MakeCurrent(window, gl_context); + SDL_GL_SetSwapInterval(1); // Enable vsync + SDL_ShowWindow(window); + + //init main ui + initMainUI(window, gl_context, glsl_version); + + // create adapter texture + g_adapterTexture.init(g_screenWidth, g_screenHeight); + glEnable(GL_TEXTURE_2D); + + // init davinci + g_scene.init(); + g_renderTarget.init(512, 256); + g_adapterTexture.updateTexture(); + + return SDL_APP_CONTINUE; /* carry on with the program! */ +} + +/* This function runs when a new event (mouse input, keypresses, etc) occurs. */ +SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event) +{ + processUIEvent(event); + + switch (event->type) + { + case SDL_EVENT_QUIT: + return SDL_APP_SUCCESS; + break; + case SDL_EVENT_WINDOW_CLOSE_REQUESTED: + { + if (event->window.windowID == SDL_GetWindowID(window)) + { + return SDL_APP_SUCCESS; + } + } + break; + case SDL_EVENT_WINDOW_RESIZED: + { + g_screenWidth = event->window.data1; + g_screenHeight = event->window.data2; + g_adapterTexture.resize(g_screenWidth, g_screenHeight); + g_adapterTexture.updateTexture(); + } + break; + } + return SDL_APP_CONTINUE; +} + +/* This function runs once per frame, and is the heart of the program. */ +SDL_AppResult SDL_AppIterate(void* appstate) +{ + if (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED) + { + SDL_Delay(10); + return SDL_APP_CONTINUE; + } + + // render + glViewport(0, 0, (int)g_screenWidth, (int)g_screenHeight); + + glClearColor(0.f, 0.f, 0.f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glLoadIdentity(); + + //draw render result + g_adapterTexture.updateTexture(0, 0, g_renderTarget.getColorBuffer()); + g_adapterTexture.updateTexture(512, 0, g_renderTarget.getDepthBuffer()); + g_adapterTexture.draw(); + + //draw ui + drawMainUI(); + + SDL_GL_SwapWindow(window); + + return SDL_APP_CONTINUE; +} + +/* This function runs once at shutdown. */ +void SDL_AppQuit(void* appstate, SDL_AppResult result) +{ + /* SDL will clean up the window/renderer for us. */ + cleanMainUI(); + SDL_GL_DestroyContext(gl_context); +} + diff --git a/source/console/ui/imgui/imgui_impl_glfw.cpp b/source/console/ui/imgui/imgui_impl_glfw.cpp new file mode 100644 index 0000000..1345d16 --- /dev/null +++ b/source/console/ui/imgui/imgui_impl_glfw.cpp @@ -0,0 +1,925 @@ +// dear imgui: Platform Backend for GLFW +// This needs to be used along with a Renderer (e.g. OpenGL3, Vulkan, WebGPU..) +// (Info: GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) +// (Requires: GLFW 3.1+. Prefer GLFW 3.3+ or GLFW 3.4+ for full feature support.) + +// Implemented features: +// [X] Platform: Clipboard support. +// [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen/Pen (Windows only). +// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy GLFW_KEY_* values are obsolete since 1.87 and not supported since 1.91.5] +// [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. +// [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Resizing cursors requires GLFW 3.4+! Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. + +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. +// Learn about Dear ImGui: +// - FAQ https://dearimgui.com/faq +// - Getting Started https://dearimgui.com/getting-started +// - Documentation https://dearimgui.com/docs (same as your local docs/ folder). +// - Introduction, links and more at the top of imgui.cpp + +// About Emscripten support: +// - Emscripten provides its own GLFW (3.2.1) implementation (syntax: "-sUSE_GLFW=3"), but Joystick is broken and several features are not supported (multiple windows, clipboard, timer, etc.) +// - A third-party Emscripten GLFW (3.4.0) implementation (syntax: "--use-port=contrib.glfw3") fixes the Joystick issue and implements all relevant features for the browser. +// See https://github.com/pongasoft/emscripten-glfw/blob/master/docs/Comparison.md for details. + +// CHANGELOG +// (minor and older changes stripped away, please see git history for details) +// 2024-08-22: moved some OS/backend related function pointers from ImGuiIO to ImGuiPlatformIO: +// - io.GetClipboardTextFn -> platform_io.Platform_GetClipboardTextFn +// - io.SetClipboardTextFn -> platform_io.Platform_SetClipboardTextFn +// - io.PlatformOpenInShellFn -> platform_io.Platform_OpenInShellFn +// 2024-07-31: Added ImGui_ImplGlfw_Sleep() helper function for usage by our examples app, since GLFW doesn't provide one. +// 2024-07-08: *BREAKING* Renamed ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback to ImGui_ImplGlfw_InstallEmscriptenCallbacks(), added GLFWWindow* parameter. +// 2024-07-08: Emscripten: Added support for GLFW3 contrib port (GLFW 3.4.0 features + bug fixes): to enable, replace -sUSE_GLFW=3 with --use-port=contrib.glfw3 (requires emscripten 3.1.59+) (https://github.com/pongasoft/emscripten-glfw) +// 2024-07-02: Emscripten: Added io.PlatformOpenInShellFn() handler for Emscripten versions. +// 2023-12-19: Emscripten: Added ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback() to register canvas selector and auto-resize GLFW window. +// 2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F24 function keys. +// 2023-07-18: Inputs: Revert ignoring mouse data on GLFW_CURSOR_DISABLED as it can be used differently. User may set ImGuiConfigFLags_NoMouse if desired. (#5625, #6609) +// 2023-06-12: Accept glfwGetTime() not returning a monotonically increasing value. This seems to happens on some Windows setup when peripherals disconnect, and is likely to also happen on browser + Emscripten. (#6491) +// 2023-04-04: Inputs: Added support for io.AddMouseSourceEvent() to discriminate ImGuiMouseSource_Mouse/ImGuiMouseSource_TouchScreen/ImGuiMouseSource_Pen on Windows ONLY, using a custom WndProc hook. (#2702) +// 2023-03-16: Inputs: Fixed key modifiers handling on secondary viewports (docking branch). Broken on 2023/01/04. (#6248, #6034) +// 2023-03-14: Emscripten: Avoid using glfwGetError() and glfwGetGamepadState() which are not correctly implemented in Emscripten emulation. (#6240) +// 2023-02-03: Emscripten: Registering custom low-level mouse wheel handler to get more accurate scrolling impulses on Emscripten. (#4019, #6096) +// 2023-01-04: Inputs: Fixed mods state on Linux when using Alt-GR text input (e.g. German keyboard layout), could lead to broken text input. Revert a 2022/01/17 change were we resumed using mods provided by GLFW, turns out they were faulty. +// 2022-11-22: Perform a dummy glfwGetError() read to cancel missing names with glfwGetKeyName(). (#5908) +// 2022-10-18: Perform a dummy glfwGetError() read to cancel missing mouse cursors errors. Using GLFW_VERSION_COMBINED directly. (#5785) +// 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11. +// 2022-09-26: Inputs: Renamed ImGuiKey_ModXXX introduced in 1.87 to ImGuiMod_XXX (old names still supported). +// 2022-09-01: Inputs: Honor GLFW_CURSOR_DISABLED by not setting mouse position *EDIT* Reverted 2023-07-18. +// 2022-04-30: Inputs: Fixed ImGui_ImplGlfw_TranslateUntranslatedKey() for lower case letters on OSX. +// 2022-03-23: Inputs: Fixed a regression in 1.87 which resulted in keyboard modifiers events being reported incorrectly on Linux/X11. +// 2022-02-07: Added ImGui_ImplGlfw_InstallCallbacks()/ImGui_ImplGlfw_RestoreCallbacks() helpers to facilitate user installing callbacks after initializing backend. +// 2022-01-26: Inputs: replaced short-lived io.AddKeyModsEvent() (added two weeks ago) with io.AddKeyEvent() using ImGuiKey_ModXXX flags. Sorry for the confusion. +// 2021-01-20: Inputs: calling new io.AddKeyAnalogEvent() for gamepad support, instead of writing directly to io.NavInputs[]. +// 2022-01-17: Inputs: calling new io.AddMousePosEvent(), io.AddMouseButtonEvent(), io.AddMouseWheelEvent() API (1.87+). +// 2022-01-17: Inputs: always update key mods next and before key event (not in NewFrame) to fix input queue with very low framerates. +// 2022-01-12: *BREAKING CHANGE*: Now using glfwSetCursorPosCallback(). If you called ImGui_ImplGlfw_InitXXX() with install_callbacks = false, you MUST install glfwSetCursorPosCallback() and forward it to the backend via ImGui_ImplGlfw_CursorPosCallback(). +// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. +// 2022-01-05: Inputs: Converting GLFW untranslated keycodes back to translated keycodes (in the ImGui_ImplGlfw_KeyCallback() function) in order to match the behavior of every other backend, and facilitate the use of GLFW with lettered-shortcuts API. +// 2021-08-17: *BREAKING CHANGE*: Now using glfwSetWindowFocusCallback() to calling io.AddFocusEvent(). If you called ImGui_ImplGlfw_InitXXX() with install_callbacks = false, you MUST install glfwSetWindowFocusCallback() and forward it to the backend via ImGui_ImplGlfw_WindowFocusCallback(). +// 2021-07-29: *BREAKING CHANGE*: Now using glfwSetCursorEnterCallback(). MousePos is correctly reported when the host platform window is hovered but not focused. If you called ImGui_ImplGlfw_InitXXX() with install_callbacks = false, you MUST install glfwSetWindowFocusCallback() callback and forward it to the backend via ImGui_ImplGlfw_CursorEnterCallback(). +// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX). +// 2020-01-17: Inputs: Disable error callback while assigning mouse cursors because some X11 setup don't have them and it generates errors. +// 2019-12-05: Inputs: Added support for new mouse cursors added in GLFW 3.4+ (resizing cursors, not allowed cursor). +// 2019-10-18: Misc: Previously installed user callbacks are now restored on shutdown. +// 2019-07-21: Inputs: Added mapping for ImGuiKey_KeyPadEnter. +// 2019-05-11: Inputs: Don't filter value from character callback before calling AddInputCharacter(). +// 2019-03-12: Misc: Preserve DisplayFramebufferScale when main window is minimized. +// 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window. +// 2018-11-07: Inputs: When installing our GLFW callbacks, we save user's previously installed ones - if any - and chain call them. +// 2018-08-01: Inputs: Workaround for Emscripten which doesn't seem to handle focus related calls. +// 2018-06-29: Inputs: Added support for the ImGuiMouseCursor_Hand cursor. +// 2018-06-08: Misc: Extracted imgui_impl_glfw.cpp/.h away from the old combined GLFW+OpenGL/Vulkan examples. +// 2018-03-20: Misc: Setup io.BackendFlags ImGuiBackendFlags_HasMouseCursors flag + honor ImGuiConfigFlags_NoMouseCursorChange flag. +// 2018-02-20: Inputs: Added support for mouse cursors (ImGui::GetMouseCursor() value, passed to glfwSetCursor()). +// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. +// 2018-02-06: Inputs: Added mapping for ImGuiKey_Space. +// 2018-01-25: Inputs: Added gamepad support if ImGuiConfigFlags_NavEnableGamepad is set. +// 2018-01-25: Inputs: Honoring the io.WantSetMousePos by repositioning the mouse (when using navigation and ImGuiConfigFlags_NavMoveMouse is set). +// 2018-01-20: Inputs: Added Horizontal Mouse Wheel support. +// 2018-01-18: Inputs: Added mapping for ImGuiKey_Insert. +// 2017-08-25: Inputs: MousePos set to -FLT_MAX,-FLT_MAX when mouse is unavailable/missing (instead of -1,-1). +// 2016-10-15: Misc: Added a void* user_data parameter to Clipboard function handlers. + +#include "imgui/imgui.h" +#ifndef IMGUI_DISABLE +#include "imgui_impl_glfw.h" + +// Clang warnings with -Weverything +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast +#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness +#endif + +// GLFW +#include + +#ifdef _WIN32 +#undef APIENTRY +#ifndef GLFW_EXPOSE_NATIVE_WIN32 +#define GLFW_EXPOSE_NATIVE_WIN32 +#endif +#include // for glfwGetWin32Window() +#endif +#ifdef __APPLE__ +#ifndef GLFW_EXPOSE_NATIVE_COCOA +#define GLFW_EXPOSE_NATIVE_COCOA +#endif +#include // for glfwGetCocoaWindow() +#endif +#ifndef _WIN32 +#include // for usleep() +#endif + +#ifdef __EMSCRIPTEN__ +#include +#include +#ifdef EMSCRIPTEN_USE_PORT_CONTRIB_GLFW3 +#include +#else +#define EMSCRIPTEN_USE_EMBEDDED_GLFW3 +#endif +#endif + +// We gather version tests as define in order to easily see which features are version-dependent. +#define GLFW_VERSION_COMBINED (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 + GLFW_VERSION_REVISION) +#ifdef GLFW_RESIZE_NESW_CURSOR // Let's be nice to people who pulled GLFW between 2019-04-16 (3.4 define) and 2019-11-29 (cursors defines) // FIXME: Remove when GLFW 3.4 is released? +#define GLFW_HAS_NEW_CURSORS (GLFW_VERSION_COMBINED >= 3400) // 3.4+ GLFW_RESIZE_ALL_CURSOR, GLFW_RESIZE_NESW_CURSOR, GLFW_RESIZE_NWSE_CURSOR, GLFW_NOT_ALLOWED_CURSOR +#else +#define GLFW_HAS_NEW_CURSORS (0) +#endif +#define GLFW_HAS_GAMEPAD_API (GLFW_VERSION_COMBINED >= 3300) // 3.3+ glfwGetGamepadState() new api +#define GLFW_HAS_GETKEYNAME (GLFW_VERSION_COMBINED >= 3200) // 3.2+ glfwGetKeyName() +#define GLFW_HAS_GETERROR (GLFW_VERSION_COMBINED >= 3300) // 3.3+ glfwGetError() + +// GLFW data +enum GlfwClientApi +{ + GlfwClientApi_Unknown, + GlfwClientApi_OpenGL, + GlfwClientApi_Vulkan, +}; + +struct ImGui_ImplGlfw_Data +{ + GLFWwindow* Window; + GlfwClientApi ClientApi; + double Time; + GLFWwindow* MouseWindow; + GLFWcursor* MouseCursors[ImGuiMouseCursor_COUNT]; + ImVec2 LastValidMousePos; + bool InstalledCallbacks; + bool CallbacksChainForAllWindows; +#ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3 + const char* CanvasSelector; +#endif + + // Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any. + GLFWwindowfocusfun PrevUserCallbackWindowFocus; + GLFWcursorposfun PrevUserCallbackCursorPos; + GLFWcursorenterfun PrevUserCallbackCursorEnter; + GLFWmousebuttonfun PrevUserCallbackMousebutton; + GLFWscrollfun PrevUserCallbackScroll; + GLFWkeyfun PrevUserCallbackKey; + GLFWcharfun PrevUserCallbackChar; + GLFWmonitorfun PrevUserCallbackMonitor; +#ifdef _WIN32 + WNDPROC PrevWndProc; +#endif + + ImGui_ImplGlfw_Data() { memset((void*)this, 0, sizeof(*this)); } +}; + +// Backend data stored in io.BackendPlatformUserData to allow support for multiple Dear ImGui contexts +// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts. +// FIXME: multi-context support is not well tested and probably dysfunctional in this backend. +// - Because glfwPollEvents() process all windows and some events may be called outside of it, you will need to register your own callbacks +// (passing install_callbacks=false in ImGui_ImplGlfw_InitXXX functions), set the current dear imgui context and then call our callbacks. +// - Otherwise we may need to store a GLFWWindow* -> ImGuiContext* map and handle this in the backend, adding a little bit of extra complexity to it. +// FIXME: some shared resources (mouse cursor shape, gamepad) are mishandled when using multi-context. +static ImGui_ImplGlfw_Data* ImGui_ImplGlfw_GetBackendData() +{ + return ImGui::GetCurrentContext() ? (ImGui_ImplGlfw_Data*)ImGui::GetIO().BackendPlatformUserData : nullptr; +} + +// Functions + +// Not static to allow third-party code to use that if they want to (but undocumented) +ImGuiKey ImGui_ImplGlfw_KeyToImGuiKey(int keycode, int scancode); +ImGuiKey ImGui_ImplGlfw_KeyToImGuiKey(int keycode, int scancode) +{ + IM_UNUSED(scancode); + switch (keycode) + { + case GLFW_KEY_TAB: return ImGuiKey_Tab; + case GLFW_KEY_LEFT: return ImGuiKey_LeftArrow; + case GLFW_KEY_RIGHT: return ImGuiKey_RightArrow; + case GLFW_KEY_UP: return ImGuiKey_UpArrow; + case GLFW_KEY_DOWN: return ImGuiKey_DownArrow; + case GLFW_KEY_PAGE_UP: return ImGuiKey_PageUp; + case GLFW_KEY_PAGE_DOWN: return ImGuiKey_PageDown; + case GLFW_KEY_HOME: return ImGuiKey_Home; + case GLFW_KEY_END: return ImGuiKey_End; + case GLFW_KEY_INSERT: return ImGuiKey_Insert; + case GLFW_KEY_DELETE: return ImGuiKey_Delete; + case GLFW_KEY_BACKSPACE: return ImGuiKey_Backspace; + case GLFW_KEY_SPACE: return ImGuiKey_Space; + case GLFW_KEY_ENTER: return ImGuiKey_Enter; + case GLFW_KEY_ESCAPE: return ImGuiKey_Escape; + case GLFW_KEY_APOSTROPHE: return ImGuiKey_Apostrophe; + case GLFW_KEY_COMMA: return ImGuiKey_Comma; + case GLFW_KEY_MINUS: return ImGuiKey_Minus; + case GLFW_KEY_PERIOD: return ImGuiKey_Period; + case GLFW_KEY_SLASH: return ImGuiKey_Slash; + case GLFW_KEY_SEMICOLON: return ImGuiKey_Semicolon; + case GLFW_KEY_EQUAL: return ImGuiKey_Equal; + case GLFW_KEY_LEFT_BRACKET: return ImGuiKey_LeftBracket; + case GLFW_KEY_BACKSLASH: return ImGuiKey_Backslash; + case GLFW_KEY_RIGHT_BRACKET: return ImGuiKey_RightBracket; + case GLFW_KEY_GRAVE_ACCENT: return ImGuiKey_GraveAccent; + case GLFW_KEY_CAPS_LOCK: return ImGuiKey_CapsLock; + case GLFW_KEY_SCROLL_LOCK: return ImGuiKey_ScrollLock; + case GLFW_KEY_NUM_LOCK: return ImGuiKey_NumLock; + case GLFW_KEY_PRINT_SCREEN: return ImGuiKey_PrintScreen; + case GLFW_KEY_PAUSE: return ImGuiKey_Pause; + case GLFW_KEY_KP_0: return ImGuiKey_Keypad0; + case GLFW_KEY_KP_1: return ImGuiKey_Keypad1; + case GLFW_KEY_KP_2: return ImGuiKey_Keypad2; + case GLFW_KEY_KP_3: return ImGuiKey_Keypad3; + case GLFW_KEY_KP_4: return ImGuiKey_Keypad4; + case GLFW_KEY_KP_5: return ImGuiKey_Keypad5; + case GLFW_KEY_KP_6: return ImGuiKey_Keypad6; + case GLFW_KEY_KP_7: return ImGuiKey_Keypad7; + case GLFW_KEY_KP_8: return ImGuiKey_Keypad8; + case GLFW_KEY_KP_9: return ImGuiKey_Keypad9; + case GLFW_KEY_KP_DECIMAL: return ImGuiKey_KeypadDecimal; + case GLFW_KEY_KP_DIVIDE: return ImGuiKey_KeypadDivide; + case GLFW_KEY_KP_MULTIPLY: return ImGuiKey_KeypadMultiply; + case GLFW_KEY_KP_SUBTRACT: return ImGuiKey_KeypadSubtract; + case GLFW_KEY_KP_ADD: return ImGuiKey_KeypadAdd; + case GLFW_KEY_KP_ENTER: return ImGuiKey_KeypadEnter; + case GLFW_KEY_KP_EQUAL: return ImGuiKey_KeypadEqual; + case GLFW_KEY_LEFT_SHIFT: return ImGuiKey_LeftShift; + case GLFW_KEY_LEFT_CONTROL: return ImGuiKey_LeftCtrl; + case GLFW_KEY_LEFT_ALT: return ImGuiKey_LeftAlt; + case GLFW_KEY_LEFT_SUPER: return ImGuiKey_LeftSuper; + case GLFW_KEY_RIGHT_SHIFT: return ImGuiKey_RightShift; + case GLFW_KEY_RIGHT_CONTROL: return ImGuiKey_RightCtrl; + case GLFW_KEY_RIGHT_ALT: return ImGuiKey_RightAlt; + case GLFW_KEY_RIGHT_SUPER: return ImGuiKey_RightSuper; + case GLFW_KEY_MENU: return ImGuiKey_Menu; + case GLFW_KEY_0: return ImGuiKey_0; + case GLFW_KEY_1: return ImGuiKey_1; + case GLFW_KEY_2: return ImGuiKey_2; + case GLFW_KEY_3: return ImGuiKey_3; + case GLFW_KEY_4: return ImGuiKey_4; + case GLFW_KEY_5: return ImGuiKey_5; + case GLFW_KEY_6: return ImGuiKey_6; + case GLFW_KEY_7: return ImGuiKey_7; + case GLFW_KEY_8: return ImGuiKey_8; + case GLFW_KEY_9: return ImGuiKey_9; + case GLFW_KEY_A: return ImGuiKey_A; + case GLFW_KEY_B: return ImGuiKey_B; + case GLFW_KEY_C: return ImGuiKey_C; + case GLFW_KEY_D: return ImGuiKey_D; + case GLFW_KEY_E: return ImGuiKey_E; + case GLFW_KEY_F: return ImGuiKey_F; + case GLFW_KEY_G: return ImGuiKey_G; + case GLFW_KEY_H: return ImGuiKey_H; + case GLFW_KEY_I: return ImGuiKey_I; + case GLFW_KEY_J: return ImGuiKey_J; + case GLFW_KEY_K: return ImGuiKey_K; + case GLFW_KEY_L: return ImGuiKey_L; + case GLFW_KEY_M: return ImGuiKey_M; + case GLFW_KEY_N: return ImGuiKey_N; + case GLFW_KEY_O: return ImGuiKey_O; + case GLFW_KEY_P: return ImGuiKey_P; + case GLFW_KEY_Q: return ImGuiKey_Q; + case GLFW_KEY_R: return ImGuiKey_R; + case GLFW_KEY_S: return ImGuiKey_S; + case GLFW_KEY_T: return ImGuiKey_T; + case GLFW_KEY_U: return ImGuiKey_U; + case GLFW_KEY_V: return ImGuiKey_V; + case GLFW_KEY_W: return ImGuiKey_W; + case GLFW_KEY_X: return ImGuiKey_X; + case GLFW_KEY_Y: return ImGuiKey_Y; + case GLFW_KEY_Z: return ImGuiKey_Z; + case GLFW_KEY_F1: return ImGuiKey_F1; + case GLFW_KEY_F2: return ImGuiKey_F2; + case GLFW_KEY_F3: return ImGuiKey_F3; + case GLFW_KEY_F4: return ImGuiKey_F4; + case GLFW_KEY_F5: return ImGuiKey_F5; + case GLFW_KEY_F6: return ImGuiKey_F6; + case GLFW_KEY_F7: return ImGuiKey_F7; + case GLFW_KEY_F8: return ImGuiKey_F8; + case GLFW_KEY_F9: return ImGuiKey_F9; + case GLFW_KEY_F10: return ImGuiKey_F10; + case GLFW_KEY_F11: return ImGuiKey_F11; + case GLFW_KEY_F12: return ImGuiKey_F12; + case GLFW_KEY_F13: return ImGuiKey_F13; + case GLFW_KEY_F14: return ImGuiKey_F14; + case GLFW_KEY_F15: return ImGuiKey_F15; + case GLFW_KEY_F16: return ImGuiKey_F16; + case GLFW_KEY_F17: return ImGuiKey_F17; + case GLFW_KEY_F18: return ImGuiKey_F18; + case GLFW_KEY_F19: return ImGuiKey_F19; + case GLFW_KEY_F20: return ImGuiKey_F20; + case GLFW_KEY_F21: return ImGuiKey_F21; + case GLFW_KEY_F22: return ImGuiKey_F22; + case GLFW_KEY_F23: return ImGuiKey_F23; + case GLFW_KEY_F24: return ImGuiKey_F24; + default: return ImGuiKey_None; + } +} + +// X11 does not include current pressed/released modifier key in 'mods' flags submitted by GLFW +// See https://github.com/ocornut/imgui/issues/6034 and https://github.com/glfw/glfw/issues/1630 +static void ImGui_ImplGlfw_UpdateKeyModifiers(GLFWwindow* window) +{ + ImGuiIO& io = ImGui::GetIO(); + io.AddKeyEvent(ImGuiMod_Ctrl, (glfwGetKey(window, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS) || (glfwGetKey(window, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS)); + io.AddKeyEvent(ImGuiMod_Shift, (glfwGetKey(window, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS) || (glfwGetKey(window, GLFW_KEY_RIGHT_SHIFT) == GLFW_PRESS)); + io.AddKeyEvent(ImGuiMod_Alt, (glfwGetKey(window, GLFW_KEY_LEFT_ALT) == GLFW_PRESS) || (glfwGetKey(window, GLFW_KEY_RIGHT_ALT) == GLFW_PRESS)); + io.AddKeyEvent(ImGuiMod_Super, (glfwGetKey(window, GLFW_KEY_LEFT_SUPER) == GLFW_PRESS) || (glfwGetKey(window, GLFW_KEY_RIGHT_SUPER) == GLFW_PRESS)); +} + +static bool ImGui_ImplGlfw_ShouldChainCallback(GLFWwindow* window) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + return bd->CallbacksChainForAllWindows ? true : (window == bd->Window); +} + +void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + if (bd->PrevUserCallbackMousebutton != nullptr && ImGui_ImplGlfw_ShouldChainCallback(window)) + bd->PrevUserCallbackMousebutton(window, button, action, mods); + + ImGui_ImplGlfw_UpdateKeyModifiers(window); + + ImGuiIO& io = ImGui::GetIO(); + if (button >= 0 && button < ImGuiMouseButton_COUNT) + io.AddMouseButtonEvent(button, action == GLFW_PRESS); +} + +void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + if (bd->PrevUserCallbackScroll != nullptr && ImGui_ImplGlfw_ShouldChainCallback(window)) + bd->PrevUserCallbackScroll(window, xoffset, yoffset); + +#ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3 + // Ignore GLFW events: will be processed in ImGui_ImplEmscripten_WheelCallback(). + return; +#endif + + ImGuiIO& io = ImGui::GetIO(); + io.AddMouseWheelEvent((float)xoffset, (float)yoffset); +} + +// FIXME: should this be baked into ImGui_ImplGlfw_KeyToImGuiKey()? then what about the values passed to io.SetKeyEventNativeData()? +static int ImGui_ImplGlfw_TranslateUntranslatedKey(int key, int scancode) +{ +#if GLFW_HAS_GETKEYNAME && !defined(EMSCRIPTEN_USE_EMBEDDED_GLFW3) + // GLFW 3.1+ attempts to "untranslate" keys, which goes the opposite of what every other framework does, making using lettered shortcuts difficult. + // (It had reasons to do so: namely GLFW is/was more likely to be used for WASD-type game controls rather than lettered shortcuts, but IHMO the 3.1 change could have been done differently) + // See https://github.com/glfw/glfw/issues/1502 for details. + // Adding a workaround to undo this (so our keys are translated->untranslated->translated, likely a lossy process). + // This won't cover edge cases but this is at least going to cover common cases. + if (key >= GLFW_KEY_KP_0 && key <= GLFW_KEY_KP_EQUAL) + return key; + GLFWerrorfun prev_error_callback = glfwSetErrorCallback(nullptr); + const char* key_name = glfwGetKeyName(key, scancode); + glfwSetErrorCallback(prev_error_callback); +#if GLFW_HAS_GETERROR && !defined(EMSCRIPTEN_USE_EMBEDDED_GLFW3) // Eat errors (see #5908) + (void)glfwGetError(nullptr); +#endif + if (key_name && key_name[0] != 0 && key_name[1] == 0) + { + const char char_names[] = "`-=[]\\,;\'./"; + const int char_keys[] = { GLFW_KEY_GRAVE_ACCENT, GLFW_KEY_MINUS, GLFW_KEY_EQUAL, GLFW_KEY_LEFT_BRACKET, GLFW_KEY_RIGHT_BRACKET, GLFW_KEY_BACKSLASH, GLFW_KEY_COMMA, GLFW_KEY_SEMICOLON, GLFW_KEY_APOSTROPHE, GLFW_KEY_PERIOD, GLFW_KEY_SLASH, 0 }; + IM_ASSERT(IM_ARRAYSIZE(char_names) == IM_ARRAYSIZE(char_keys)); + if (key_name[0] >= '0' && key_name[0] <= '9') { key = GLFW_KEY_0 + (key_name[0] - '0'); } + else if (key_name[0] >= 'A' && key_name[0] <= 'Z') { key = GLFW_KEY_A + (key_name[0] - 'A'); } + else if (key_name[0] >= 'a' && key_name[0] <= 'z') { key = GLFW_KEY_A + (key_name[0] - 'a'); } + else if (const char* p = strchr(char_names, key_name[0])) { key = char_keys[p - char_names]; } + } + // if (action == GLFW_PRESS) printf("key %d scancode %d name '%s'\n", key, scancode, key_name); +#else + IM_UNUSED(scancode); +#endif + return key; +} + +void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int keycode, int scancode, int action, int mods) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + if (bd->PrevUserCallbackKey != nullptr && ImGui_ImplGlfw_ShouldChainCallback(window)) + bd->PrevUserCallbackKey(window, keycode, scancode, action, mods); + + if (action != GLFW_PRESS && action != GLFW_RELEASE) + return; + + ImGui_ImplGlfw_UpdateKeyModifiers(window); + + keycode = ImGui_ImplGlfw_TranslateUntranslatedKey(keycode, scancode); + + ImGuiIO& io = ImGui::GetIO(); + ImGuiKey imgui_key = ImGui_ImplGlfw_KeyToImGuiKey(keycode, scancode); + io.AddKeyEvent(imgui_key, (action == GLFW_PRESS)); + io.SetKeyEventNativeData(imgui_key, keycode, scancode); // To support legacy indexing (<1.87 user code) +} + +void ImGui_ImplGlfw_WindowFocusCallback(GLFWwindow* window, int focused) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + if (bd->PrevUserCallbackWindowFocus != nullptr && ImGui_ImplGlfw_ShouldChainCallback(window)) + bd->PrevUserCallbackWindowFocus(window, focused); + + ImGuiIO& io = ImGui::GetIO(); + io.AddFocusEvent(focused != 0); +} + +void ImGui_ImplGlfw_CursorPosCallback(GLFWwindow* window, double x, double y) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + if (bd->PrevUserCallbackCursorPos != nullptr && ImGui_ImplGlfw_ShouldChainCallback(window)) + bd->PrevUserCallbackCursorPos(window, x, y); + + ImGuiIO& io = ImGui::GetIO(); + io.AddMousePosEvent((float)x, (float)y); + bd->LastValidMousePos = ImVec2((float)x, (float)y); +} + +// Workaround: X11 seems to send spurious Leave/Enter events which would make us lose our position, +// so we back it up and restore on Leave/Enter (see https://github.com/ocornut/imgui/issues/4984) +void ImGui_ImplGlfw_CursorEnterCallback(GLFWwindow* window, int entered) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + if (bd->PrevUserCallbackCursorEnter != nullptr && ImGui_ImplGlfw_ShouldChainCallback(window)) + bd->PrevUserCallbackCursorEnter(window, entered); + + ImGuiIO& io = ImGui::GetIO(); + if (entered) + { + bd->MouseWindow = window; + io.AddMousePosEvent(bd->LastValidMousePos.x, bd->LastValidMousePos.y); + } + else if (!entered && bd->MouseWindow == window) + { + bd->LastValidMousePos = io.MousePos; + bd->MouseWindow = nullptr; + io.AddMousePosEvent(-FLT_MAX, -FLT_MAX); + } +} + +void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + if (bd->PrevUserCallbackChar != nullptr && ImGui_ImplGlfw_ShouldChainCallback(window)) + bd->PrevUserCallbackChar(window, c); + + ImGuiIO& io = ImGui::GetIO(); + io.AddInputCharacter(c); +} + +void ImGui_ImplGlfw_MonitorCallback(GLFWmonitor*, int) +{ + // Unused in 'master' branch but 'docking' branch will use this, so we declare it ahead of it so if you have to install callbacks you can install this one too. +} + +#ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3 +static EM_BOOL ImGui_ImplEmscripten_WheelCallback(int, const EmscriptenWheelEvent* ev, void*) +{ + // Mimic Emscripten_HandleWheel() in SDL. + // Corresponding equivalent in GLFW JS emulation layer has incorrect quantizing preventing small values. See #6096 + float multiplier = 0.0f; + if (ev->deltaMode == DOM_DELTA_PIXEL) { multiplier = 1.0f / 100.0f; } // 100 pixels make up a step. + else if (ev->deltaMode == DOM_DELTA_LINE) { multiplier = 1.0f / 3.0f; } // 3 lines make up a step. + else if (ev->deltaMode == DOM_DELTA_PAGE) { multiplier = 80.0f; } // A page makes up 80 steps. + float wheel_x = ev->deltaX * -multiplier; + float wheel_y = ev->deltaY * -multiplier; + ImGuiIO& io = ImGui::GetIO(); + io.AddMouseWheelEvent(wheel_x, wheel_y); + //IMGUI_DEBUG_LOG("[Emsc] mode %d dx: %.2f, dy: %.2f, dz: %.2f --> feed %.2f %.2f\n", (int)ev->deltaMode, ev->deltaX, ev->deltaY, ev->deltaZ, wheel_x, wheel_y); + return EM_TRUE; +} +#endif + +#ifdef _WIN32 +// GLFW doesn't allow to distinguish Mouse vs TouchScreen vs Pen. +// Add support for Win32 (based on imgui_impl_win32), because we rely on _TouchScreen info to trickle inputs differently. +static ImGuiMouseSource GetMouseSourceFromMessageExtraInfo() +{ + LPARAM extra_info = ::GetMessageExtraInfo(); + if ((extra_info & 0xFFFFFF80) == 0xFF515700) + return ImGuiMouseSource_Pen; + if ((extra_info & 0xFFFFFF80) == 0xFF515780) + return ImGuiMouseSource_TouchScreen; + return ImGuiMouseSource_Mouse; +} +static LRESULT CALLBACK ImGui_ImplGlfw_WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + switch (msg) + { + case WM_MOUSEMOVE: case WM_NCMOUSEMOVE: + case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK: case WM_LBUTTONUP: + case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK: case WM_RBUTTONUP: + case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK: case WM_MBUTTONUP: + case WM_XBUTTONDOWN: case WM_XBUTTONDBLCLK: case WM_XBUTTONUP: + ImGui::GetIO().AddMouseSourceEvent(GetMouseSourceFromMessageExtraInfo()); + break; + } + return ::CallWindowProcW(bd->PrevWndProc, hWnd, msg, wParam, lParam); +} +#endif + +void ImGui_ImplGlfw_InstallCallbacks(GLFWwindow* window) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + IM_ASSERT(bd->InstalledCallbacks == false && "Callbacks already installed!"); + IM_ASSERT(bd->Window == window); + + bd->PrevUserCallbackWindowFocus = glfwSetWindowFocusCallback(window, ImGui_ImplGlfw_WindowFocusCallback); + bd->PrevUserCallbackCursorEnter = glfwSetCursorEnterCallback(window, ImGui_ImplGlfw_CursorEnterCallback); + bd->PrevUserCallbackCursorPos = glfwSetCursorPosCallback(window, ImGui_ImplGlfw_CursorPosCallback); + bd->PrevUserCallbackMousebutton = glfwSetMouseButtonCallback(window, ImGui_ImplGlfw_MouseButtonCallback); + bd->PrevUserCallbackScroll = glfwSetScrollCallback(window, ImGui_ImplGlfw_ScrollCallback); + bd->PrevUserCallbackKey = glfwSetKeyCallback(window, ImGui_ImplGlfw_KeyCallback); + bd->PrevUserCallbackChar = glfwSetCharCallback(window, ImGui_ImplGlfw_CharCallback); + bd->PrevUserCallbackMonitor = glfwSetMonitorCallback(ImGui_ImplGlfw_MonitorCallback); + bd->InstalledCallbacks = true; +} + +void ImGui_ImplGlfw_RestoreCallbacks(GLFWwindow* window) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + IM_ASSERT(bd->InstalledCallbacks == true && "Callbacks not installed!"); + IM_ASSERT(bd->Window == window); + + glfwSetWindowFocusCallback(window, bd->PrevUserCallbackWindowFocus); + glfwSetCursorEnterCallback(window, bd->PrevUserCallbackCursorEnter); + glfwSetCursorPosCallback(window, bd->PrevUserCallbackCursorPos); + glfwSetMouseButtonCallback(window, bd->PrevUserCallbackMousebutton); + glfwSetScrollCallback(window, bd->PrevUserCallbackScroll); + glfwSetKeyCallback(window, bd->PrevUserCallbackKey); + glfwSetCharCallback(window, bd->PrevUserCallbackChar); + glfwSetMonitorCallback(bd->PrevUserCallbackMonitor); + bd->InstalledCallbacks = false; + bd->PrevUserCallbackWindowFocus = nullptr; + bd->PrevUserCallbackCursorEnter = nullptr; + bd->PrevUserCallbackCursorPos = nullptr; + bd->PrevUserCallbackMousebutton = nullptr; + bd->PrevUserCallbackScroll = nullptr; + bd->PrevUserCallbackKey = nullptr; + bd->PrevUserCallbackChar = nullptr; + bd->PrevUserCallbackMonitor = nullptr; +} + +// Set to 'true' to enable chaining installed callbacks for all windows (including secondary viewports created by backends or by user. +// This is 'false' by default meaning we only chain callbacks for the main viewport. +// We cannot set this to 'true' by default because user callbacks code may be not testing the 'window' parameter of their callback. +// If you set this to 'true' your user callback code will need to make sure you are testing the 'window' parameter. +void ImGui_ImplGlfw_SetCallbacksChainForAllWindows(bool chain_for_all_windows) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + bd->CallbacksChainForAllWindows = chain_for_all_windows; +} + +#ifdef __EMSCRIPTEN__ +#if EMSCRIPTEN_USE_PORT_CONTRIB_GLFW3 >= 34020240817 +void ImGui_ImplGlfw_EmscriptenOpenURL(const char* url) { if (url) emscripten::glfw3::OpenURL(url); } +#else +EM_JS(void, ImGui_ImplGlfw_EmscriptenOpenURL, (const char* url), { url = url ? UTF8ToString(url) : null; if (url) window.open(url, '_blank'); }); +#endif +#endif + +static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, GlfwClientApi client_api) +{ + ImGuiIO& io = ImGui::GetIO(); + IMGUI_CHECKVERSION(); + IM_ASSERT(io.BackendPlatformUserData == nullptr && "Already initialized a platform backend!"); + //printf("GLFW_VERSION: %d.%d.%d (%d)", GLFW_VERSION_MAJOR, GLFW_VERSION_MINOR, GLFW_VERSION_REVISION, GLFW_VERSION_COMBINED); + + // Setup backend capabilities flags + ImGui_ImplGlfw_Data* bd = IM_NEW(ImGui_ImplGlfw_Data)(); + io.BackendPlatformUserData = (void*)bd; + io.BackendPlatformName = "imgui_impl_glfw"; + io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) + io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) + + bd->Window = window; + bd->Time = 0.0; + + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + platform_io.Platform_SetClipboardTextFn = [](ImGuiContext*, const char* text) { glfwSetClipboardString(nullptr, text); }; + platform_io.Platform_GetClipboardTextFn = [](ImGuiContext*) { return glfwGetClipboardString(nullptr); }; +#ifdef __EMSCRIPTEN__ + platform_io.Platform_OpenInShellFn = [](ImGuiContext*, const char* url) { ImGui_ImplGlfw_EmscriptenOpenURL(url); return true; }; +#endif + + // Create mouse cursors + // (By design, on X11 cursors are user configurable and some cursors may be missing. When a cursor doesn't exist, + // GLFW will emit an error which will often be printed by the app, so we temporarily disable error reporting. + // Missing cursors will return nullptr and our _UpdateMouseCursor() function will use the Arrow cursor instead.) + GLFWerrorfun prev_error_callback = glfwSetErrorCallback(nullptr); + bd->MouseCursors[ImGuiMouseCursor_Arrow] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); + bd->MouseCursors[ImGuiMouseCursor_TextInput] = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR); + bd->MouseCursors[ImGuiMouseCursor_ResizeNS] = glfwCreateStandardCursor(GLFW_VRESIZE_CURSOR); + bd->MouseCursors[ImGuiMouseCursor_ResizeEW] = glfwCreateStandardCursor(GLFW_HRESIZE_CURSOR); + bd->MouseCursors[ImGuiMouseCursor_Hand] = glfwCreateStandardCursor(GLFW_HAND_CURSOR); +#if GLFW_HAS_NEW_CURSORS + bd->MouseCursors[ImGuiMouseCursor_ResizeAll] = glfwCreateStandardCursor(GLFW_RESIZE_ALL_CURSOR); + bd->MouseCursors[ImGuiMouseCursor_ResizeNESW] = glfwCreateStandardCursor(GLFW_RESIZE_NESW_CURSOR); + bd->MouseCursors[ImGuiMouseCursor_ResizeNWSE] = glfwCreateStandardCursor(GLFW_RESIZE_NWSE_CURSOR); + bd->MouseCursors[ImGuiMouseCursor_NotAllowed] = glfwCreateStandardCursor(GLFW_NOT_ALLOWED_CURSOR); +#else + bd->MouseCursors[ImGuiMouseCursor_ResizeAll] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); + bd->MouseCursors[ImGuiMouseCursor_ResizeNESW] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); + bd->MouseCursors[ImGuiMouseCursor_ResizeNWSE] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); + bd->MouseCursors[ImGuiMouseCursor_NotAllowed] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); +#endif + glfwSetErrorCallback(prev_error_callback); +#if GLFW_HAS_GETERROR && !defined(__EMSCRIPTEN__) // Eat errors (see #5908) + (void)glfwGetError(nullptr); +#endif + + // Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any. + if (install_callbacks) + ImGui_ImplGlfw_InstallCallbacks(window); + + // Set platform dependent data in viewport + ImGuiViewport* main_viewport = ImGui::GetMainViewport(); + main_viewport->PlatformHandle = (void*)bd->Window; +#ifdef _WIN32 + main_viewport->PlatformHandleRaw = glfwGetWin32Window(bd->Window); +#elif defined(__APPLE__) + main_viewport->PlatformHandleRaw = (void*)glfwGetCocoaWindow(bd->Window); +#else + IM_UNUSED(main_viewport); +#endif + + // Windows: register a WndProc hook so we can intercept some messages. +#ifdef _WIN32 + bd->PrevWndProc = (WNDPROC)::GetWindowLongPtrW((HWND)main_viewport->PlatformHandleRaw, GWLP_WNDPROC); + IM_ASSERT(bd->PrevWndProc != nullptr); + ::SetWindowLongPtrW((HWND)main_viewport->PlatformHandleRaw, GWLP_WNDPROC, (LONG_PTR)ImGui_ImplGlfw_WndProc); +#endif + + // Emscripten: the same application can run on various platforms, so we detect the Apple platform at runtime + // to override io.ConfigMacOSXBehaviors from its default (which is always false in Emscripten). +#ifdef __EMSCRIPTEN__ +#if EMSCRIPTEN_USE_PORT_CONTRIB_GLFW3 >= 34020240817 + if (emscripten::glfw3::IsRuntimePlatformApple()) + { + ImGui::GetIO().ConfigMacOSXBehaviors = true; + + // Due to how the browser (poorly) handles the Meta Key, this line essentially disables repeats when used. + // This means that Meta + V only registers a single key-press, even if the keys are held. + // This is a compromise for dealing with this issue in ImGui since ImGui implements key repeat itself. + // See https://github.com/pongasoft/emscripten-glfw/blob/v3.4.0.20240817/docs/Usage.md#the-problem-of-the-super-key + emscripten::glfw3::SetSuperPlusKeyTimeouts(10, 10); + } +#endif +#endif + + bd->ClientApi = client_api; + return true; +} + +bool ImGui_ImplGlfw_InitForOpenGL(GLFWwindow* window, bool install_callbacks) +{ + return ImGui_ImplGlfw_Init(window, install_callbacks, GlfwClientApi_OpenGL); +} + +bool ImGui_ImplGlfw_InitForVulkan(GLFWwindow* window, bool install_callbacks) +{ + return ImGui_ImplGlfw_Init(window, install_callbacks, GlfwClientApi_Vulkan); +} + +bool ImGui_ImplGlfw_InitForOther(GLFWwindow* window, bool install_callbacks) +{ + return ImGui_ImplGlfw_Init(window, install_callbacks, GlfwClientApi_Unknown); +} + +void ImGui_ImplGlfw_Shutdown() +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + IM_ASSERT(bd != nullptr && "No platform backend to shutdown, or already shutdown?"); + ImGuiIO& io = ImGui::GetIO(); + + if (bd->InstalledCallbacks) + ImGui_ImplGlfw_RestoreCallbacks(bd->Window); +#ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3 + if (bd->CanvasSelector) + emscripten_set_wheel_callback(bd->CanvasSelector, nullptr, false, nullptr); +#endif + + for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++) + glfwDestroyCursor(bd->MouseCursors[cursor_n]); + + // Windows: restore our WndProc hook +#ifdef _WIN32 + ImGuiViewport* main_viewport = ImGui::GetMainViewport(); + ::SetWindowLongPtrW((HWND)main_viewport->PlatformHandleRaw, GWLP_WNDPROC, (LONG_PTR)bd->PrevWndProc); + bd->PrevWndProc = nullptr; +#endif + + io.BackendPlatformName = nullptr; + io.BackendPlatformUserData = nullptr; + io.BackendFlags &= ~(ImGuiBackendFlags_HasMouseCursors | ImGuiBackendFlags_HasSetMousePos | ImGuiBackendFlags_HasGamepad); + IM_DELETE(bd); +} + +static void ImGui_ImplGlfw_UpdateMouseData() +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + ImGuiIO& io = ImGui::GetIO(); + + // (those braces are here to reduce diff with multi-viewports support in 'docking' branch) + { + GLFWwindow* window = bd->Window; +#ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3 + const bool is_window_focused = true; +#else + const bool is_window_focused = glfwGetWindowAttrib(window, GLFW_FOCUSED) != 0; +#endif + if (is_window_focused) + { + // (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when io.ConfigNavMoveSetMousePos is enabled by user) + if (io.WantSetMousePos) + glfwSetCursorPos(window, (double)io.MousePos.x, (double)io.MousePos.y); + + // (Optional) Fallback to provide mouse position when focused (ImGui_ImplGlfw_CursorPosCallback already provides this when hovered or captured) + if (bd->MouseWindow == nullptr) + { + double mouse_x, mouse_y; + glfwGetCursorPos(window, &mouse_x, &mouse_y); + bd->LastValidMousePos = ImVec2((float)mouse_x, (float)mouse_y); + io.AddMousePosEvent((float)mouse_x, (float)mouse_y); + } + } + } +} + +static void ImGui_ImplGlfw_UpdateMouseCursor() +{ + ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + if ((io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) || glfwGetInputMode(bd->Window, GLFW_CURSOR) == GLFW_CURSOR_DISABLED) + return; + + ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor(); + // (those braces are here to reduce diff with multi-viewports support in 'docking' branch) + { + GLFWwindow* window = bd->Window; + if (imgui_cursor == ImGuiMouseCursor_None || io.MouseDrawCursor) + { + // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); + } + else + { + // Show OS mouse cursor + // FIXME-PLATFORM: Unfocused windows seems to fail changing the mouse cursor with GLFW 3.2, but 3.3 works here. + glfwSetCursor(window, bd->MouseCursors[imgui_cursor] ? bd->MouseCursors[imgui_cursor] : bd->MouseCursors[ImGuiMouseCursor_Arrow]); + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + } + } +} + +// Update gamepad inputs +static inline float Saturate(float v) { return v < 0.0f ? 0.0f : v > 1.0f ? 1.0f : v; } +static void ImGui_ImplGlfw_UpdateGamepads() +{ + ImGuiIO& io = ImGui::GetIO(); + if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) // FIXME: Technically feeding gamepad shouldn't depend on this now that they are regular inputs. + return; + + io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad; +#if GLFW_HAS_GAMEPAD_API && !defined(EMSCRIPTEN_USE_EMBEDDED_GLFW3) + GLFWgamepadstate gamepad; + if (!glfwGetGamepadState(GLFW_JOYSTICK_1, &gamepad)) + return; + #define MAP_BUTTON(KEY_NO, BUTTON_NO, _UNUSED) do { io.AddKeyEvent(KEY_NO, gamepad.buttons[BUTTON_NO] != 0); } while (0) + #define MAP_ANALOG(KEY_NO, AXIS_NO, _UNUSED, V0, V1) do { float v = gamepad.axes[AXIS_NO]; v = (v - V0) / (V1 - V0); io.AddKeyAnalogEvent(KEY_NO, v > 0.10f, Saturate(v)); } while (0) +#else + int axes_count = 0, buttons_count = 0; + const float* axes = glfwGetJoystickAxes(GLFW_JOYSTICK_1, &axes_count); + const unsigned char* buttons = glfwGetJoystickButtons(GLFW_JOYSTICK_1, &buttons_count); + if (axes_count == 0 || buttons_count == 0) + return; + #define MAP_BUTTON(KEY_NO, _UNUSED, BUTTON_NO) do { io.AddKeyEvent(KEY_NO, (buttons_count > BUTTON_NO && buttons[BUTTON_NO] == GLFW_PRESS)); } while (0) + #define MAP_ANALOG(KEY_NO, _UNUSED, AXIS_NO, V0, V1) do { float v = (axes_count > AXIS_NO) ? axes[AXIS_NO] : V0; v = (v - V0) / (V1 - V0); io.AddKeyAnalogEvent(KEY_NO, v > 0.10f, Saturate(v)); } while (0) +#endif + io.BackendFlags |= ImGuiBackendFlags_HasGamepad; + MAP_BUTTON(ImGuiKey_GamepadStart, GLFW_GAMEPAD_BUTTON_START, 7); + MAP_BUTTON(ImGuiKey_GamepadBack, GLFW_GAMEPAD_BUTTON_BACK, 6); + MAP_BUTTON(ImGuiKey_GamepadFaceLeft, GLFW_GAMEPAD_BUTTON_X, 2); // Xbox X, PS Square + MAP_BUTTON(ImGuiKey_GamepadFaceRight, GLFW_GAMEPAD_BUTTON_B, 1); // Xbox B, PS Circle + MAP_BUTTON(ImGuiKey_GamepadFaceUp, GLFW_GAMEPAD_BUTTON_Y, 3); // Xbox Y, PS Triangle + MAP_BUTTON(ImGuiKey_GamepadFaceDown, GLFW_GAMEPAD_BUTTON_A, 0); // Xbox A, PS Cross + MAP_BUTTON(ImGuiKey_GamepadDpadLeft, GLFW_GAMEPAD_BUTTON_DPAD_LEFT, 13); + MAP_BUTTON(ImGuiKey_GamepadDpadRight, GLFW_GAMEPAD_BUTTON_DPAD_RIGHT, 11); + MAP_BUTTON(ImGuiKey_GamepadDpadUp, GLFW_GAMEPAD_BUTTON_DPAD_UP, 10); + MAP_BUTTON(ImGuiKey_GamepadDpadDown, GLFW_GAMEPAD_BUTTON_DPAD_DOWN, 12); + MAP_BUTTON(ImGuiKey_GamepadL1, GLFW_GAMEPAD_BUTTON_LEFT_BUMPER, 4); + MAP_BUTTON(ImGuiKey_GamepadR1, GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER, 5); + MAP_ANALOG(ImGuiKey_GamepadL2, GLFW_GAMEPAD_AXIS_LEFT_TRIGGER, 4, -0.75f, +1.0f); + MAP_ANALOG(ImGuiKey_GamepadR2, GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER, 5, -0.75f, +1.0f); + MAP_BUTTON(ImGuiKey_GamepadL3, GLFW_GAMEPAD_BUTTON_LEFT_THUMB, 8); + MAP_BUTTON(ImGuiKey_GamepadR3, GLFW_GAMEPAD_BUTTON_RIGHT_THUMB, 9); + MAP_ANALOG(ImGuiKey_GamepadLStickLeft, GLFW_GAMEPAD_AXIS_LEFT_X, 0, -0.25f, -1.0f); + MAP_ANALOG(ImGuiKey_GamepadLStickRight, GLFW_GAMEPAD_AXIS_LEFT_X, 0, +0.25f, +1.0f); + MAP_ANALOG(ImGuiKey_GamepadLStickUp, GLFW_GAMEPAD_AXIS_LEFT_Y, 1, -0.25f, -1.0f); + MAP_ANALOG(ImGuiKey_GamepadLStickDown, GLFW_GAMEPAD_AXIS_LEFT_Y, 1, +0.25f, +1.0f); + MAP_ANALOG(ImGuiKey_GamepadRStickLeft, GLFW_GAMEPAD_AXIS_RIGHT_X, 2, -0.25f, -1.0f); + MAP_ANALOG(ImGuiKey_GamepadRStickRight, GLFW_GAMEPAD_AXIS_RIGHT_X, 2, +0.25f, +1.0f); + MAP_ANALOG(ImGuiKey_GamepadRStickUp, GLFW_GAMEPAD_AXIS_RIGHT_Y, 3, -0.25f, -1.0f); + MAP_ANALOG(ImGuiKey_GamepadRStickDown, GLFW_GAMEPAD_AXIS_RIGHT_Y, 3, +0.25f, +1.0f); + #undef MAP_BUTTON + #undef MAP_ANALOG +} + +void ImGui_ImplGlfw_NewFrame() +{ + ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplGlfw_InitForXXX()?"); + + // Setup display size (every frame to accommodate for window resizing) + int w, h; + int display_w, display_h; + glfwGetWindowSize(bd->Window, &w, &h); + glfwGetFramebufferSize(bd->Window, &display_w, &display_h); + io.DisplaySize = ImVec2((float)w, (float)h); + if (w > 0 && h > 0) + io.DisplayFramebufferScale = ImVec2((float)display_w / (float)w, (float)display_h / (float)h); + + // Setup time step + // (Accept glfwGetTime() not returning a monotonically increasing value. Seems to happens on disconnecting peripherals and probably on VMs and Emscripten, see #6491, #6189, #6114, #3644) + double current_time = glfwGetTime(); + if (current_time <= bd->Time) + current_time = bd->Time + 0.00001f; + io.DeltaTime = bd->Time > 0.0 ? (float)(current_time - bd->Time) : (float)(1.0f / 60.0f); + bd->Time = current_time; + + ImGui_ImplGlfw_UpdateMouseData(); + ImGui_ImplGlfw_UpdateMouseCursor(); + + // Update game controllers (if enabled and available) + ImGui_ImplGlfw_UpdateGamepads(); +} + +// GLFW doesn't provide a portable sleep function +void ImGui_ImplGlfw_Sleep(int milliseconds) +{ +#ifdef _WIN32 + ::Sleep(milliseconds); +#else + usleep(milliseconds * 1000); +#endif +} + +#ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3 +static EM_BOOL ImGui_ImplGlfw_OnCanvasSizeChange(int event_type, const EmscriptenUiEvent* event, void* user_data) +{ + ImGui_ImplGlfw_Data* bd = (ImGui_ImplGlfw_Data*)user_data; + double canvas_width, canvas_height; + emscripten_get_element_css_size(bd->CanvasSelector, &canvas_width, &canvas_height); + glfwSetWindowSize(bd->Window, (int)canvas_width, (int)canvas_height); + return true; +} + +static EM_BOOL ImGui_ImplEmscripten_FullscreenChangeCallback(int event_type, const EmscriptenFullscreenChangeEvent* event, void* user_data) +{ + ImGui_ImplGlfw_Data* bd = (ImGui_ImplGlfw_Data*)user_data; + double canvas_width, canvas_height; + emscripten_get_element_css_size(bd->CanvasSelector, &canvas_width, &canvas_height); + glfwSetWindowSize(bd->Window, (int)canvas_width, (int)canvas_height); + return true; +} + +// 'canvas_selector' is a CSS selector. The event listener is applied to the first element that matches the query. +// STRING MUST PERSIST FOR THE APPLICATION DURATION. PLEASE USE A STRING LITERAL OR ENSURE POINTER WILL STAY VALID. +void ImGui_ImplGlfw_InstallEmscriptenCallbacks(GLFWwindow*, const char* canvas_selector) +{ + IM_ASSERT(canvas_selector != nullptr); + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplGlfw_InitForXXX()?"); + + bd->CanvasSelector = canvas_selector; + emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, bd, false, ImGui_ImplGlfw_OnCanvasSizeChange); + emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, bd, false, ImGui_ImplEmscripten_FullscreenChangeCallback); + + // Change the size of the GLFW window according to the size of the canvas + ImGui_ImplGlfw_OnCanvasSizeChange(EMSCRIPTEN_EVENT_RESIZE, {}, bd); + + // Register Emscripten Wheel callback to workaround issue in Emscripten GLFW Emulation (#6096) + // We intentionally do not check 'if (install_callbacks)' here, as some users may set it to false and call GLFW callback themselves. + // FIXME: May break chaining in case user registered their own Emscripten callback? + emscripten_set_wheel_callback(bd->CanvasSelector, nullptr, false, ImGui_ImplEmscripten_WheelCallback); +} +#elif defined(EMSCRIPTEN_USE_PORT_CONTRIB_GLFW3) +// When using --use-port=contrib.glfw3 for the GLFW implementation, you can override the behavior of this call +// by invoking emscripten_glfw_make_canvas_resizable afterward. +// See https://github.com/pongasoft/emscripten-glfw/blob/master/docs/Usage.md#how-to-make-the-canvas-resizable-by-the-user for an explanation +void ImGui_ImplGlfw_InstallEmscriptenCallbacks(GLFWwindow* window, const char* canvas_selector) +{ + GLFWwindow* w = (GLFWwindow*)(EM_ASM_INT({ return Module.glfwGetWindow(UTF8ToString($0)); }, canvas_selector)); + IM_ASSERT(window == w); // Sanity check + IM_UNUSED(w); + emscripten_glfw_make_canvas_resizable(window, "window", nullptr); +} +#endif // #ifdef EMSCRIPTEN_USE_PORT_CONTRIB_GLFW3 + +//----------------------------------------------------------------------------- + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + +#endif // #ifndef IMGUI_DISABLE diff --git a/source/console/ui/imgui/imgui_impl_glfw.h b/source/console/ui/imgui/imgui_impl_glfw.h new file mode 100644 index 0000000..1c091cc --- /dev/null +++ b/source/console/ui/imgui/imgui_impl_glfw.h @@ -0,0 +1,63 @@ +// dear imgui: Platform Backend for GLFW +// This needs to be used along with a Renderer (e.g. OpenGL3, Vulkan, WebGPU..) +// (Info: GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) + +// Implemented features: +// [X] Platform: Clipboard support. +// [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen/Pen (Windows only). +// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy GLFW_KEY_* values are obsolete since 1.87 and not supported since 1.91.5] +// [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. +// [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Resizing cursors requires GLFW 3.4+! Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. + +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. +// Learn about Dear ImGui: +// - FAQ https://dearimgui.com/faq +// - Getting Started https://dearimgui.com/getting-started +// - Documentation https://dearimgui.com/docs (same as your local docs/ folder). +// - Introduction, links and more at the top of imgui.cpp + +#pragma once +#include "imgui/imgui.h" // IMGUI_IMPL_API +#ifndef IMGUI_DISABLE + +struct GLFWwindow; +struct GLFWmonitor; + +// Follow "Getting Started" link and check examples/ folder to learn about using backends! +IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForOpenGL(GLFWwindow* window, bool install_callbacks); +IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForVulkan(GLFWwindow* window, bool install_callbacks); +IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForOther(GLFWwindow* window, bool install_callbacks); +IMGUI_IMPL_API void ImGui_ImplGlfw_Shutdown(); +IMGUI_IMPL_API void ImGui_ImplGlfw_NewFrame(); + +// Emscripten related initialization phase methods (call after ImGui_ImplGlfw_InitForOpenGL) +#ifdef __EMSCRIPTEN__ +IMGUI_IMPL_API void ImGui_ImplGlfw_InstallEmscriptenCallbacks(GLFWwindow* window, const char* canvas_selector); +//static inline void ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback(const char* canvas_selector) { ImGui_ImplGlfw_InstallEmscriptenCallbacks(nullptr, canvas_selector); } } // Renamed in 1.91.0 +#endif + +// GLFW callbacks install +// - When calling Init with 'install_callbacks=true': ImGui_ImplGlfw_InstallCallbacks() is called. GLFW callbacks will be installed for you. They will chain-call user's previously installed callbacks, if any. +// - When calling Init with 'install_callbacks=false': GLFW callbacks won't be installed. You will need to call individual function yourself from your own GLFW callbacks. +IMGUI_IMPL_API void ImGui_ImplGlfw_InstallCallbacks(GLFWwindow* window); +IMGUI_IMPL_API void ImGui_ImplGlfw_RestoreCallbacks(GLFWwindow* window); + +// GFLW callbacks options: +// - Set 'chain_for_all_windows=true' to enable chaining callbacks for all windows (including secondary viewports created by backends or by user) +IMGUI_IMPL_API void ImGui_ImplGlfw_SetCallbacksChainForAllWindows(bool chain_for_all_windows); + +// GLFW callbacks (individual callbacks to call yourself if you didn't install callbacks) +IMGUI_IMPL_API void ImGui_ImplGlfw_WindowFocusCallback(GLFWwindow* window, int focused); // Since 1.84 +IMGUI_IMPL_API void ImGui_ImplGlfw_CursorEnterCallback(GLFWwindow* window, int entered); // Since 1.84 +IMGUI_IMPL_API void ImGui_ImplGlfw_CursorPosCallback(GLFWwindow* window, double x, double y); // Since 1.87 +IMGUI_IMPL_API void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods); +IMGUI_IMPL_API void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset); +IMGUI_IMPL_API void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods); +IMGUI_IMPL_API void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c); +IMGUI_IMPL_API void ImGui_ImplGlfw_MonitorCallback(GLFWmonitor* monitor, int event); + +// GLFW helpers +IMGUI_IMPL_API void ImGui_ImplGlfw_Sleep(int milliseconds); + +#endif // #ifndef IMGUI_DISABLE diff --git a/source/console/ui/imgui/imgui_impl_opengl3.cpp b/source/console/ui/imgui/imgui_impl_opengl3.cpp new file mode 100644 index 0000000..015caa5 --- /dev/null +++ b/source/console/ui/imgui/imgui_impl_opengl3.cpp @@ -0,0 +1,968 @@ +// dear imgui: Renderer Backend for modern OpenGL with shaders / programmatic pipeline +// - Desktop GL: 2.x 3.x 4.x +// - Embedded GL: ES 2.0 (WebGL 1.0), ES 3.0 (WebGL 2.0) +// This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..) + +// Implemented features: +// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID! +// [x] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset) [Desktop OpenGL only!] + +// About WebGL/ES: +// - You need to '#define IMGUI_IMPL_OPENGL_ES2' or '#define IMGUI_IMPL_OPENGL_ES3' to use WebGL or OpenGL ES. +// - This is done automatically on iOS, Android and Emscripten targets. +// - For other targets, the define needs to be visible from the imgui_impl_opengl3.cpp compilation unit. If unsure, define globally or in imconfig.h. + +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. +// Learn about Dear ImGui: +// - FAQ https://dearimgui.com/faq +// - Getting Started https://dearimgui.com/getting-started +// - Documentation https://dearimgui.com/docs (same as your local docs/ folder). +// - Introduction, links and more at the top of imgui.cpp + +// CHANGELOG +// (minor and older changes stripped away, please see git history for details) +// 2024-10-07: OpenGL: Changed default texture sampler to Clamp instead of Repeat/Wrap. +// 2024-06-28: OpenGL: ImGui_ImplOpenGL3_NewFrame() recreates font texture if it has been destroyed by ImGui_ImplOpenGL3_DestroyFontsTexture(). (#7748) +// 2024-05-07: OpenGL: Update loader for Linux to support EGL/GLVND. (#7562) +// 2024-04-16: OpenGL: Detect ES3 contexts on desktop based on version string, to e.g. avoid calling glPolygonMode() on them. (#7447) +// 2024-01-09: OpenGL: Update GL3W based imgui_impl_opengl3_loader.h to load "libGL.so" and variants, fixing regression on distros missing a symlink. +// 2023-11-08: OpenGL: Update GL3W based imgui_impl_opengl3_loader.h to load "libGL.so" instead of "libGL.so.1", accommodating for NetBSD systems having only "libGL.so.3" available. (#6983) +// 2023-10-05: OpenGL: Rename symbols in our internal loader so that LTO compilation with another copy of gl3w is possible. (#6875, #6668, #4445) +// 2023-06-20: OpenGL: Fixed erroneous use glGetIntegerv(GL_CONTEXT_PROFILE_MASK) on contexts lower than 3.2. (#6539, #6333) +// 2023-05-09: OpenGL: Support for glBindSampler() backup/restore on ES3. (#6375) +// 2023-04-18: OpenGL: Restore front and back polygon mode separately when supported by context. (#6333) +// 2023-03-23: OpenGL: Properly restoring "no shader program bound" if it was the case prior to running the rendering function. (#6267, #6220, #6224) +// 2023-03-15: OpenGL: Fixed GL loader crash when GL_VERSION returns NULL. (#6154, #4445, #3530) +// 2023-03-06: OpenGL: Fixed restoration of a potentially deleted OpenGL program, by calling glIsProgram(). (#6220, #6224) +// 2022-11-09: OpenGL: Reverted use of glBufferSubData(), too many corruptions issues + old issues seemingly can't be reproed with Intel drivers nowadays (revert 2021-12-15 and 2022-05-23 changes). +// 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11. +// 2022-09-27: OpenGL: Added ability to '#define IMGUI_IMPL_OPENGL_DEBUG'. +// 2022-05-23: OpenGL: Reworking 2021-12-15 "Using buffer orphaning" so it only happens on Intel GPU, seems to cause problems otherwise. (#4468, #4825, #4832, #5127). +// 2022-05-13: OpenGL: Fixed state corruption on OpenGL ES 2.0 due to not preserving GL_ELEMENT_ARRAY_BUFFER_BINDING and vertex attribute states. +// 2021-12-15: OpenGL: Using buffer orphaning + glBufferSubData(), seems to fix leaks with multi-viewports with some Intel HD drivers. +// 2021-08-23: OpenGL: Fixed ES 3.0 shader ("#version 300 es") use normal precision floats to avoid wobbly rendering at HD resolutions. +// 2021-08-19: OpenGL: Embed and use our own minimal GL loader (imgui_impl_opengl3_loader.h), removing requirement and support for third-party loader. +// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX). +// 2021-06-25: OpenGL: Use OES_vertex_array extension on Emscripten + backup/restore current state. +// 2021-06-21: OpenGL: Destroy individual vertex/fragment shader objects right after they are linked into the main shader. +// 2021-05-24: OpenGL: Access GL_CLIP_ORIGIN when "GL_ARB_clip_control" extension is detected, inside of just OpenGL 4.5 version. +// 2021-05-19: OpenGL: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement) +// 2021-04-06: OpenGL: Don't try to read GL_CLIP_ORIGIN unless we're OpenGL 4.5 or greater. +// 2021-02-18: OpenGL: Change blending equation to preserve alpha in output buffer. +// 2021-01-03: OpenGL: Backup, setup and restore GL_STENCIL_TEST state. +// 2020-10-23: OpenGL: Backup, setup and restore GL_PRIMITIVE_RESTART state. +// 2020-10-15: OpenGL: Use glGetString(GL_VERSION) instead of glGetIntegerv(GL_MAJOR_VERSION, ...) when the later returns zero (e.g. Desktop GL 2.x) +// 2020-09-17: OpenGL: Fix to avoid compiling/calling glBindSampler() on ES or pre 3.3 context which have the defines set by a loader. +// 2020-07-10: OpenGL: Added support for glad2 OpenGL loader. +// 2020-05-08: OpenGL: Made default GLSL version 150 (instead of 130) on OSX. +// 2020-04-21: OpenGL: Fixed handling of glClipControl(GL_UPPER_LEFT) by inverting projection matrix. +// 2020-04-12: OpenGL: Fixed context version check mistakenly testing for 4.0+ instead of 3.2+ to enable ImGuiBackendFlags_RendererHasVtxOffset. +// 2020-03-24: OpenGL: Added support for glbinding 2.x OpenGL loader. +// 2020-01-07: OpenGL: Added support for glbinding 3.x OpenGL loader. +// 2019-10-25: OpenGL: Using a combination of GL define and runtime GL version to decide whether to use glDrawElementsBaseVertex(). Fix building with pre-3.2 GL loaders. +// 2019-09-22: OpenGL: Detect default GL loader using __has_include compiler facility. +// 2019-09-16: OpenGL: Tweak initialization code to allow application calling ImGui_ImplOpenGL3_CreateFontsTexture() before the first NewFrame() call. +// 2019-05-29: OpenGL: Desktop GL only: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag. +// 2019-04-30: OpenGL: Added support for special ImDrawCallback_ResetRenderState callback to reset render state. +// 2019-03-29: OpenGL: Not calling glBindBuffer more than necessary in the render loop. +// 2019-03-15: OpenGL: Added a GL call + comments in ImGui_ImplOpenGL3_Init() to detect uninitialized GL function loaders early. +// 2019-03-03: OpenGL: Fix support for ES 2.0 (WebGL 1.0). +// 2019-02-20: OpenGL: Fix for OSX not supporting OpenGL 4.5, we don't try to read GL_CLIP_ORIGIN even if defined by the headers/loader. +// 2019-02-11: OpenGL: Projecting clipping rectangles correctly using draw_data->FramebufferScale to allow multi-viewports for retina display. +// 2019-02-01: OpenGL: Using GLSL 410 shaders for any version over 410 (e.g. 430, 450). +// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window. +// 2018-11-13: OpenGL: Support for GL 4.5's glClipControl(GL_UPPER_LEFT) / GL_CLIP_ORIGIN. +// 2018-08-29: OpenGL: Added support for more OpenGL loaders: glew and glad, with comments indicative that any loader can be used. +// 2018-08-09: OpenGL: Default to OpenGL ES 3 on iOS and Android. GLSL version default to "#version 300 ES". +// 2018-07-30: OpenGL: Support for GLSL 300 ES and 410 core. Fixes for Emscripten compilation. +// 2018-07-10: OpenGL: Support for more GLSL versions (based on the GLSL version string). Added error output when shaders fail to compile/link. +// 2018-06-08: Misc: Extracted imgui_impl_opengl3.cpp/.h away from the old combined GLFW/SDL+OpenGL3 examples. +// 2018-06-08: OpenGL: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle. +// 2018-05-25: OpenGL: Removed unnecessary backup/restore of GL_ELEMENT_ARRAY_BUFFER_BINDING since this is part of the VAO state. +// 2018-05-14: OpenGL: Making the call to glBindSampler() optional so 3.2 context won't fail if the function is a nullptr pointer. +// 2018-03-06: OpenGL: Added const char* glsl_version parameter to ImGui_ImplOpenGL3_Init() so user can override the GLSL version e.g. "#version 150". +// 2018-02-23: OpenGL: Create the VAO in the render function so the setup can more easily be used with multiple shared GL context. +// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplSdlGL3_RenderDrawData() in the .h file so you can call it yourself. +// 2018-01-07: OpenGL: Changed GLSL shader version from 330 to 150. +// 2017-09-01: OpenGL: Save and restore current bound sampler. Save and restore current polygon mode. +// 2017-05-01: OpenGL: Fixed save and restore of current blend func state. +// 2017-05-01: OpenGL: Fixed save and restore of current GL_ACTIVE_TEXTURE. +// 2016-09-05: OpenGL: Fixed save and restore of current scissor rectangle. +// 2016-07-29: OpenGL: Explicitly setting GL_UNPACK_ROW_LENGTH to reduce issues because SDL changes it. (#752) + +//---------------------------------------- +// OpenGL GLSL GLSL +// version version string +//---------------------------------------- +// 2.0 110 "#version 110" +// 2.1 120 "#version 120" +// 3.0 130 "#version 130" +// 3.1 140 "#version 140" +// 3.2 150 "#version 150" +// 3.3 330 "#version 330 core" +// 4.0 400 "#version 400 core" +// 4.1 410 "#version 410 core" +// 4.2 420 "#version 410 core" +// 4.3 430 "#version 430 core" +// ES 2.0 100 "#version 100" = WebGL 1.0 +// ES 3.0 300 "#version 300 es" = WebGL 2.0 +//---------------------------------------- + +#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include "imgui/imgui.h" +#ifndef IMGUI_DISABLE +#include "imgui_impl_opengl3.h" +#include +#include // intptr_t +#if defined(__APPLE__) +#include +#endif + +// Clang/GCC warnings with -Weverything +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunknown-warning-option" // warning: ignore unknown flags +#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast +#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness +#pragma clang diagnostic ignored "-Wunused-macros" // warning: macro is not used +#pragma clang diagnostic ignored "-Wnonportable-system-include-path" +#pragma clang diagnostic ignored "-Wcast-function-type" // warning: cast between incompatible function types (for loader) +#endif +#if defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind +#pragma GCC diagnostic ignored "-Wunknown-warning-option" // warning: unknown warning group 'xxx' +#pragma GCC diagnostic ignored "-Wcast-function-type" // warning: cast between incompatible function types (for loader) +#pragma GCC diagnostic ignored "-Wstrict-overflow" // warning: assuming signed overflow does not occur when simplifying division / ..when changing X +- C1 cmp C2 to X cmp C2 -+ C1 +#endif + +// GL includes +#if defined(IMGUI_IMPL_OPENGL_ES2) +#if (defined(__APPLE__) && (TARGET_OS_IOS || TARGET_OS_TV)) +#include // Use GL ES 2 +#else +#include // Use GL ES 2 +#endif +#if defined(__EMSCRIPTEN__) +#ifndef GL_GLEXT_PROTOTYPES +#define GL_GLEXT_PROTOTYPES +#endif +#include +#endif +#elif defined(IMGUI_IMPL_OPENGL_ES3) +#if (defined(__APPLE__) && (TARGET_OS_IOS || TARGET_OS_TV)) +#include // Use GL ES 3 +#else +#include // Use GL ES 3 +#endif +#elif !defined(IMGUI_IMPL_OPENGL_LOADER_CUSTOM) +// Modern desktop OpenGL doesn't have a standard portable header file to load OpenGL function pointers. +// Helper libraries are often used for this purpose! Here we are using our own minimal custom loader based on gl3w. +// In the rest of your app/engine, you can use another loader of your choice (gl3w, glew, glad, glbinding, glext, glLoadGen, etc.). +// If you happen to be developing a new feature for this backend (imgui_impl_opengl3.cpp): +// - You may need to regenerate imgui_impl_opengl3_loader.h to add new symbols. See https://github.com/dearimgui/gl3w_stripped +// Typically you would run: python3 ./gl3w_gen.py --output ../imgui/backends/imgui_impl_opengl3_loader.h --ref ../imgui/backends/imgui_impl_opengl3.cpp ./extra_symbols.txt +// - You can temporarily use an unstripped version. See https://github.com/dearimgui/gl3w_stripped/releases +// Changes to this backend using new APIs should be accompanied by a regenerated stripped loader version. +#define IMGL3W_IMPL +#include "imgui_impl_opengl3_loader.h" +#endif + +// Vertex arrays are not supported on ES2/WebGL1 unless Emscripten which uses an extension +#ifndef IMGUI_IMPL_OPENGL_ES2 +#define IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY +#elif defined(__EMSCRIPTEN__) +#define IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY +#define glBindVertexArray glBindVertexArrayOES +#define glGenVertexArrays glGenVertexArraysOES +#define glDeleteVertexArrays glDeleteVertexArraysOES +#define GL_VERTEX_ARRAY_BINDING GL_VERTEX_ARRAY_BINDING_OES +#endif + +// Desktop GL 2.0+ has extension and glPolygonMode() which GL ES and WebGL don't have.. +// A desktop ES context can technically compile fine with our loader, so we also perform a runtime checks +#if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3) +#define IMGUI_IMPL_OPENGL_HAS_EXTENSIONS // has glGetIntegerv(GL_NUM_EXTENSIONS) +#define IMGUI_IMPL_OPENGL_MAY_HAVE_POLYGON_MODE // may have glPolygonMode() +#endif + +// Desktop GL 2.1+ and GL ES 3.0+ have glBindBuffer() with GL_PIXEL_UNPACK_BUFFER target. +#if !defined(IMGUI_IMPL_OPENGL_ES2) +#define IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_BUFFER_PIXEL_UNPACK +#endif + +// Desktop GL 3.1+ has GL_PRIMITIVE_RESTART state +#if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3) && defined(GL_VERSION_3_1) +#define IMGUI_IMPL_OPENGL_MAY_HAVE_PRIMITIVE_RESTART +#endif + +// Desktop GL 3.2+ has glDrawElementsBaseVertex() which GL ES and WebGL don't have. +#if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3) && defined(GL_VERSION_3_2) +#define IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET +#endif + +// Desktop GL 3.3+ and GL ES 3.0+ have glBindSampler() +#if !defined(IMGUI_IMPL_OPENGL_ES2) && (defined(IMGUI_IMPL_OPENGL_ES3) || defined(GL_VERSION_3_3)) +#define IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER +#endif + +// [Debugging] +//#define IMGUI_IMPL_OPENGL_DEBUG +#ifdef IMGUI_IMPL_OPENGL_DEBUG +#include +#define GL_CALL(_CALL) do { _CALL; GLenum gl_err = glGetError(); if (gl_err != 0) fprintf(stderr, "GL error 0x%x returned from '%s'.\n", gl_err, #_CALL); } while (0) // Call with error check +#else +#define GL_CALL(_CALL) _CALL // Call without error check +#endif + +// OpenGL Data +struct ImGui_ImplOpenGL3_Data +{ + GLuint GlVersion; // Extracted at runtime using GL_MAJOR_VERSION, GL_MINOR_VERSION queries (e.g. 320 for GL 3.2) + char GlslVersionString[32]; // Specified by user or detected based on compile time GL settings. + bool GlProfileIsES2; + bool GlProfileIsES3; + bool GlProfileIsCompat; + GLint GlProfileMask; + GLuint FontTexture; + GLuint ShaderHandle; + GLint AttribLocationTex; // Uniforms location + GLint AttribLocationProjMtx; + GLuint AttribLocationVtxPos; // Vertex attributes location + GLuint AttribLocationVtxUV; + GLuint AttribLocationVtxColor; + unsigned int VboHandle, ElementsHandle; + GLsizeiptr VertexBufferSize; + GLsizeiptr IndexBufferSize; + bool HasPolygonMode; + bool HasClipOrigin; + bool UseBufferSubData; + + ImGui_ImplOpenGL3_Data() { memset((void*)this, 0, sizeof(*this)); } +}; + +// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts +// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts. +static ImGui_ImplOpenGL3_Data* ImGui_ImplOpenGL3_GetBackendData() +{ + return ImGui::GetCurrentContext() ? (ImGui_ImplOpenGL3_Data*)ImGui::GetIO().BackendRendererUserData : nullptr; +} + +// OpenGL vertex attribute state (for ES 1.0 and ES 2.0 only) +#ifndef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY +struct ImGui_ImplOpenGL3_VtxAttribState +{ + GLint Enabled, Size, Type, Normalized, Stride; + GLvoid* Ptr; + + void GetState(GLint index) + { + glGetVertexAttribiv(index, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &Enabled); + glGetVertexAttribiv(index, GL_VERTEX_ATTRIB_ARRAY_SIZE, &Size); + glGetVertexAttribiv(index, GL_VERTEX_ATTRIB_ARRAY_TYPE, &Type); + glGetVertexAttribiv(index, GL_VERTEX_ATTRIB_ARRAY_NORMALIZED, &Normalized); + glGetVertexAttribiv(index, GL_VERTEX_ATTRIB_ARRAY_STRIDE, &Stride); + glGetVertexAttribPointerv(index, GL_VERTEX_ATTRIB_ARRAY_POINTER, &Ptr); + } + void SetState(GLint index) + { + glVertexAttribPointer(index, Size, Type, (GLboolean)Normalized, Stride, Ptr); + if (Enabled) glEnableVertexAttribArray(index); else glDisableVertexAttribArray(index); + } +}; +#endif + +// Functions +bool ImGui_ImplOpenGL3_Init(const char* glsl_version) +{ + ImGuiIO& io = ImGui::GetIO(); + IMGUI_CHECKVERSION(); + IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!"); + + // Initialize our loader +#if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3) && !defined(IMGUI_IMPL_OPENGL_LOADER_CUSTOM) + if (imgl3wInit() != 0) + { + fprintf(stderr, "Failed to initialize OpenGL loader!\n"); + return false; + } +#endif + + // Setup backend capabilities flags + ImGui_ImplOpenGL3_Data* bd = IM_NEW(ImGui_ImplOpenGL3_Data)(); + io.BackendRendererUserData = (void*)bd; + io.BackendRendererName = "imgui_impl_opengl3"; + + // Query for GL version (e.g. 320 for GL 3.2) + const char* gl_version_str = (const char*)glGetString(GL_VERSION); +#if defined(IMGUI_IMPL_OPENGL_ES2) + // GLES 2 + bd->GlVersion = 200; + bd->GlProfileIsES2 = true; +#else + // Desktop or GLES 3 + GLint major = 0; + GLint minor = 0; + glGetIntegerv(GL_MAJOR_VERSION, &major); + glGetIntegerv(GL_MINOR_VERSION, &minor); + if (major == 0 && minor == 0) + sscanf(gl_version_str, "%d.%d", &major, &minor); // Query GL_VERSION in desktop GL 2.x, the string will start with "." + bd->GlVersion = (GLuint)(major * 100 + minor * 10); +#if defined(GL_CONTEXT_PROFILE_MASK) + if (bd->GlVersion >= 320) + glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &bd->GlProfileMask); + bd->GlProfileIsCompat = (bd->GlProfileMask & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT) != 0; +#endif + +#if defined(IMGUI_IMPL_OPENGL_ES3) + bd->GlProfileIsES3 = true; +#else + if (strncmp(gl_version_str, "OpenGL ES 3", 11) == 0) + bd->GlProfileIsES3 = true; +#endif + + bd->UseBufferSubData = false; + /* + // Query vendor to enable glBufferSubData kludge +#ifdef _WIN32 + if (const char* vendor = (const char*)glGetString(GL_VENDOR)) + if (strncmp(vendor, "Intel", 5) == 0) + bd->UseBufferSubData = true; +#endif + */ +#endif + +#ifdef IMGUI_IMPL_OPENGL_DEBUG + printf("GlVersion = %d, \"%s\"\nGlProfileIsCompat = %d\nGlProfileMask = 0x%X\nGlProfileIsES2/IsEs3 = %d/%d\nGL_VENDOR = '%s'\nGL_RENDERER = '%s'\n", bd->GlVersion, gl_version_str, bd->GlProfileIsCompat, bd->GlProfileMask, bd->GlProfileIsES2, bd->GlProfileIsES3, (const char*)glGetString(GL_VENDOR), (const char*)glGetString(GL_RENDERER)); // [DEBUG] +#endif + +#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET + if (bd->GlVersion >= 320) + io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. +#endif + + // Store GLSL version string so we can refer to it later in case we recreate shaders. + // Note: GLSL version is NOT the same as GL version. Leave this to nullptr if unsure. + if (glsl_version == nullptr) + { +#if defined(IMGUI_IMPL_OPENGL_ES2) + glsl_version = "#version 100"; +#elif defined(IMGUI_IMPL_OPENGL_ES3) + glsl_version = "#version 300 es"; +#elif defined(__APPLE__) + glsl_version = "#version 150"; +#else + glsl_version = "#version 130"; +#endif + } + IM_ASSERT((int)strlen(glsl_version) + 2 < IM_ARRAYSIZE(bd->GlslVersionString)); + strcpy(bd->GlslVersionString, glsl_version); + strcat(bd->GlslVersionString, "\n"); + + // Make an arbitrary GL call (we don't actually need the result) + // IF YOU GET A CRASH HERE: it probably means the OpenGL function loader didn't do its job. Let us know! + GLint current_texture; + glGetIntegerv(GL_TEXTURE_BINDING_2D, ¤t_texture); + + // Detect extensions we support +#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_POLYGON_MODE + bd->HasPolygonMode = (!bd->GlProfileIsES2 && !bd->GlProfileIsES3); +#endif + bd->HasClipOrigin = (bd->GlVersion >= 450); +#ifdef IMGUI_IMPL_OPENGL_HAS_EXTENSIONS + GLint num_extensions = 0; + glGetIntegerv(GL_NUM_EXTENSIONS, &num_extensions); + for (GLint i = 0; i < num_extensions; i++) + { + const char* extension = (const char*)glGetStringi(GL_EXTENSIONS, i); + if (extension != nullptr && strcmp(extension, "GL_ARB_clip_control") == 0) + bd->HasClipOrigin = true; + } +#endif + + return true; +} + +void ImGui_ImplOpenGL3_Shutdown() +{ + ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); + IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?"); + ImGuiIO& io = ImGui::GetIO(); + + ImGui_ImplOpenGL3_DestroyDeviceObjects(); + io.BackendRendererName = nullptr; + io.BackendRendererUserData = nullptr; + io.BackendFlags &= ~ImGuiBackendFlags_RendererHasVtxOffset; + IM_DELETE(bd); +} + +void ImGui_ImplOpenGL3_NewFrame() +{ + ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); + IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplOpenGL3_Init()?"); + + if (!bd->ShaderHandle) + ImGui_ImplOpenGL3_CreateDeviceObjects(); + if (!bd->FontTexture) + ImGui_ImplOpenGL3_CreateFontsTexture(); +} + +static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_width, int fb_height, GLuint vertex_array_object) +{ + ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); + + // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill + glEnable(GL_BLEND); + glBlendEquation(GL_FUNC_ADD); + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + glDisable(GL_STENCIL_TEST); + glEnable(GL_SCISSOR_TEST); +#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_PRIMITIVE_RESTART + if (bd->GlVersion >= 310) + glDisable(GL_PRIMITIVE_RESTART); +#endif +#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_POLYGON_MODE + if (bd->HasPolygonMode) + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); +#endif + + // Support for GL 4.5 rarely used glClipControl(GL_UPPER_LEFT) +#if defined(GL_CLIP_ORIGIN) + bool clip_origin_lower_left = true; + if (bd->HasClipOrigin) + { + GLenum current_clip_origin = 0; glGetIntegerv(GL_CLIP_ORIGIN, (GLint*)¤t_clip_origin); + if (current_clip_origin == GL_UPPER_LEFT) + clip_origin_lower_left = false; + } +#endif + + // Setup viewport, orthographic projection matrix + // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps. + GL_CALL(glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height)); + float L = draw_data->DisplayPos.x; + float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x; + float T = draw_data->DisplayPos.y; + float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y; +#if defined(GL_CLIP_ORIGIN) + if (!clip_origin_lower_left) { float tmp = T; T = B; B = tmp; } // Swap top and bottom if origin is upper left +#endif + const float ortho_projection[4][4] = + { + { 2.0f/(R-L), 0.0f, 0.0f, 0.0f }, + { 0.0f, 2.0f/(T-B), 0.0f, 0.0f }, + { 0.0f, 0.0f, -1.0f, 0.0f }, + { (R+L)/(L-R), (T+B)/(B-T), 0.0f, 1.0f }, + }; + glUseProgram(bd->ShaderHandle); + glUniform1i(bd->AttribLocationTex, 0); + glUniformMatrix4fv(bd->AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]); + +#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER + if (bd->GlVersion >= 330 || bd->GlProfileIsES3) + glBindSampler(0, 0); // We use combined texture/sampler state. Applications using GL 3.3 and GL ES 3.0 may set that otherwise. +#endif + + (void)vertex_array_object; +#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY + glBindVertexArray(vertex_array_object); +#endif + + // Bind vertex/index buffers and setup attributes for ImDrawVert + GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, bd->VboHandle)); + GL_CALL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bd->ElementsHandle)); + GL_CALL(glEnableVertexAttribArray(bd->AttribLocationVtxPos)); + GL_CALL(glEnableVertexAttribArray(bd->AttribLocationVtxUV)); + GL_CALL(glEnableVertexAttribArray(bd->AttribLocationVtxColor)); + GL_CALL(glVertexAttribPointer(bd->AttribLocationVtxPos, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)offsetof(ImDrawVert, pos))); + GL_CALL(glVertexAttribPointer(bd->AttribLocationVtxUV, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)offsetof(ImDrawVert, uv))); + GL_CALL(glVertexAttribPointer(bd->AttribLocationVtxColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid*)offsetof(ImDrawVert, col))); +} + +// OpenGL3 Render function. +// Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL state explicitly. +// This is in order to be able to run within an OpenGL engine that doesn't do so. +void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) +{ + // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) + int fb_width = (int)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x); + int fb_height = (int)(draw_data->DisplaySize.y * draw_data->FramebufferScale.y); + if (fb_width <= 0 || fb_height <= 0) + return; + + ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); + + // Backup GL state + GLenum last_active_texture; glGetIntegerv(GL_ACTIVE_TEXTURE, (GLint*)&last_active_texture); + glActiveTexture(GL_TEXTURE0); + GLuint last_program; glGetIntegerv(GL_CURRENT_PROGRAM, (GLint*)&last_program); + GLuint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint*)&last_texture); +#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER + GLuint last_sampler; if (bd->GlVersion >= 330 || bd->GlProfileIsES3) { glGetIntegerv(GL_SAMPLER_BINDING, (GLint*)&last_sampler); } else { last_sampler = 0; } +#endif + GLuint last_array_buffer; glGetIntegerv(GL_ARRAY_BUFFER_BINDING, (GLint*)&last_array_buffer); +#ifndef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY + // This is part of VAO on OpenGL 3.0+ and OpenGL ES 3.0+. + GLint last_element_array_buffer; glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &last_element_array_buffer); + ImGui_ImplOpenGL3_VtxAttribState last_vtx_attrib_state_pos; last_vtx_attrib_state_pos.GetState(bd->AttribLocationVtxPos); + ImGui_ImplOpenGL3_VtxAttribState last_vtx_attrib_state_uv; last_vtx_attrib_state_uv.GetState(bd->AttribLocationVtxUV); + ImGui_ImplOpenGL3_VtxAttribState last_vtx_attrib_state_color; last_vtx_attrib_state_color.GetState(bd->AttribLocationVtxColor); +#endif +#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY + GLuint last_vertex_array_object; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, (GLint*)&last_vertex_array_object); +#endif +#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_POLYGON_MODE + GLint last_polygon_mode[2]; if (bd->HasPolygonMode) { glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode); } +#endif + GLint last_viewport[4]; glGetIntegerv(GL_VIEWPORT, last_viewport); + GLint last_scissor_box[4]; glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box); + GLenum last_blend_src_rgb; glGetIntegerv(GL_BLEND_SRC_RGB, (GLint*)&last_blend_src_rgb); + GLenum last_blend_dst_rgb; glGetIntegerv(GL_BLEND_DST_RGB, (GLint*)&last_blend_dst_rgb); + GLenum last_blend_src_alpha; glGetIntegerv(GL_BLEND_SRC_ALPHA, (GLint*)&last_blend_src_alpha); + GLenum last_blend_dst_alpha; glGetIntegerv(GL_BLEND_DST_ALPHA, (GLint*)&last_blend_dst_alpha); + GLenum last_blend_equation_rgb; glGetIntegerv(GL_BLEND_EQUATION_RGB, (GLint*)&last_blend_equation_rgb); + GLenum last_blend_equation_alpha; glGetIntegerv(GL_BLEND_EQUATION_ALPHA, (GLint*)&last_blend_equation_alpha); + GLboolean last_enable_blend = glIsEnabled(GL_BLEND); + GLboolean last_enable_cull_face = glIsEnabled(GL_CULL_FACE); + GLboolean last_enable_depth_test = glIsEnabled(GL_DEPTH_TEST); + GLboolean last_enable_stencil_test = glIsEnabled(GL_STENCIL_TEST); + GLboolean last_enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST); +#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_PRIMITIVE_RESTART + GLboolean last_enable_primitive_restart = (bd->GlVersion >= 310) ? glIsEnabled(GL_PRIMITIVE_RESTART) : GL_FALSE; +#endif + + // Setup desired GL state + // Recreate the VAO every time (this is to easily allow multiple GL contexts to be rendered to. VAO are not shared among GL contexts) + // The renderer would actually work without any VAO bound, but then our VertexAttrib calls would overwrite the default one currently bound. + GLuint vertex_array_object = 0; +#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY + GL_CALL(glGenVertexArrays(1, &vertex_array_object)); +#endif + ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object); + + // Will project scissor/clipping rectangles into framebuffer space + ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports + ImVec2 clip_scale = draw_data->FramebufferScale; // (1,1) unless using retina display which are often (2,2) + + // Render command lists + for (int n = 0; n < draw_data->CmdListsCount; n++) + { + const ImDrawList* draw_list = draw_data->CmdLists[n]; + + // Upload vertex/index buffers + // - OpenGL drivers are in a very sorry state nowadays.... + // During 2021 we attempted to switch from glBufferData() to orphaning+glBufferSubData() following reports + // of leaks on Intel GPU when using multi-viewports on Windows. + // - After this we kept hearing of various display corruptions issues. We started disabling on non-Intel GPU, but issues still got reported on Intel. + // - We are now back to using exclusively glBufferData(). So bd->UseBufferSubData IS ALWAYS FALSE in this code. + // We are keeping the old code path for a while in case people finding new issues may want to test the bd->UseBufferSubData path. + // - See https://github.com/ocornut/imgui/issues/4468 and please report any corruption issues. + const GLsizeiptr vtx_buffer_size = (GLsizeiptr)draw_list->VtxBuffer.Size * (int)sizeof(ImDrawVert); + const GLsizeiptr idx_buffer_size = (GLsizeiptr)draw_list->IdxBuffer.Size * (int)sizeof(ImDrawIdx); + if (bd->UseBufferSubData) + { + if (bd->VertexBufferSize < vtx_buffer_size) + { + bd->VertexBufferSize = vtx_buffer_size; + GL_CALL(glBufferData(GL_ARRAY_BUFFER, bd->VertexBufferSize, nullptr, GL_STREAM_DRAW)); + } + if (bd->IndexBufferSize < idx_buffer_size) + { + bd->IndexBufferSize = idx_buffer_size; + GL_CALL(glBufferData(GL_ELEMENT_ARRAY_BUFFER, bd->IndexBufferSize, nullptr, GL_STREAM_DRAW)); + } + GL_CALL(glBufferSubData(GL_ARRAY_BUFFER, 0, vtx_buffer_size, (const GLvoid*)draw_list->VtxBuffer.Data)); + GL_CALL(glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, idx_buffer_size, (const GLvoid*)draw_list->IdxBuffer.Data)); + } + else + { + GL_CALL(glBufferData(GL_ARRAY_BUFFER, vtx_buffer_size, (const GLvoid*)draw_list->VtxBuffer.Data, GL_STREAM_DRAW)); + GL_CALL(glBufferData(GL_ELEMENT_ARRAY_BUFFER, idx_buffer_size, (const GLvoid*)draw_list->IdxBuffer.Data, GL_STREAM_DRAW)); + } + + for (int cmd_i = 0; cmd_i < draw_list->CmdBuffer.Size; cmd_i++) + { + const ImDrawCmd* pcmd = &draw_list->CmdBuffer[cmd_i]; + if (pcmd->UserCallback != nullptr) + { + // User callback, registered via ImDrawList::AddCallback() + // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.) + if (pcmd->UserCallback == ImDrawCallback_ResetRenderState) + ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object); + else + pcmd->UserCallback(draw_list, pcmd); + } + else + { + // Project scissor/clipping rectangles into framebuffer space + ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y); + ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y); + if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y) + continue; + + // Apply scissor/clipping rectangle (Y is inverted in OpenGL) + GL_CALL(glScissor((int)clip_min.x, (int)((float)fb_height - clip_max.y), (int)(clip_max.x - clip_min.x), (int)(clip_max.y - clip_min.y))); + + // Bind texture, Draw + GL_CALL(glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->GetTexID())); +#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET + if (bd->GlVersion >= 320) + GL_CALL(glDrawElementsBaseVertex(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx)), (GLint)pcmd->VtxOffset)); + else +#endif + GL_CALL(glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx)))); + } + } + } + + // Destroy the temporary VAO +#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY + GL_CALL(glDeleteVertexArrays(1, &vertex_array_object)); +#endif + + // Restore modified GL state + // This "glIsProgram()" check is required because if the program is "pending deletion" at the time of binding backup, it will have been deleted by now and will cause an OpenGL error. See #6220. + if (last_program == 0 || glIsProgram(last_program)) glUseProgram(last_program); + glBindTexture(GL_TEXTURE_2D, last_texture); +#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER + if (bd->GlVersion >= 330 || bd->GlProfileIsES3) + glBindSampler(0, last_sampler); +#endif + glActiveTexture(last_active_texture); +#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY + glBindVertexArray(last_vertex_array_object); +#endif + glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer); +#ifndef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, last_element_array_buffer); + last_vtx_attrib_state_pos.SetState(bd->AttribLocationVtxPos); + last_vtx_attrib_state_uv.SetState(bd->AttribLocationVtxUV); + last_vtx_attrib_state_color.SetState(bd->AttribLocationVtxColor); +#endif + glBlendEquationSeparate(last_blend_equation_rgb, last_blend_equation_alpha); + glBlendFuncSeparate(last_blend_src_rgb, last_blend_dst_rgb, last_blend_src_alpha, last_blend_dst_alpha); + if (last_enable_blend) glEnable(GL_BLEND); else glDisable(GL_BLEND); + if (last_enable_cull_face) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE); + if (last_enable_depth_test) glEnable(GL_DEPTH_TEST); else glDisable(GL_DEPTH_TEST); + if (last_enable_stencil_test) glEnable(GL_STENCIL_TEST); else glDisable(GL_STENCIL_TEST); + if (last_enable_scissor_test) glEnable(GL_SCISSOR_TEST); else glDisable(GL_SCISSOR_TEST); +#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_PRIMITIVE_RESTART + if (bd->GlVersion >= 310) { if (last_enable_primitive_restart) glEnable(GL_PRIMITIVE_RESTART); else glDisable(GL_PRIMITIVE_RESTART); } +#endif + +#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_POLYGON_MODE + // Desktop OpenGL 3.0 and OpenGL 3.1 had separate polygon draw modes for front-facing and back-facing faces of polygons + if (bd->HasPolygonMode) { if (bd->GlVersion <= 310 || bd->GlProfileIsCompat) { glPolygonMode(GL_FRONT, (GLenum)last_polygon_mode[0]); glPolygonMode(GL_BACK, (GLenum)last_polygon_mode[1]); } else { glPolygonMode(GL_FRONT_AND_BACK, (GLenum)last_polygon_mode[0]); } } +#endif // IMGUI_IMPL_OPENGL_MAY_HAVE_POLYGON_MODE + + glViewport(last_viewport[0], last_viewport[1], (GLsizei)last_viewport[2], (GLsizei)last_viewport[3]); + glScissor(last_scissor_box[0], last_scissor_box[1], (GLsizei)last_scissor_box[2], (GLsizei)last_scissor_box[3]); + (void)bd; // Not all compilation paths use this +} + +bool ImGui_ImplOpenGL3_CreateFontsTexture() +{ + ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); + + // Build texture atlas + unsigned char* pixels; + int width, height; + io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bit (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory. + + // Upload texture to graphics system + // (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling) + GLint last_texture; + GL_CALL(glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture)); + GL_CALL(glGenTextures(1, &bd->FontTexture)); + GL_CALL(glBindTexture(GL_TEXTURE_2D, bd->FontTexture)); + GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); + GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); + GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); +#ifdef GL_UNPACK_ROW_LENGTH // Not on WebGL/ES + GL_CALL(glPixelStorei(GL_UNPACK_ROW_LENGTH, 0)); +#endif + GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels)); + + // Store identifier + io.Fonts->SetTexID((ImTextureID)(intptr_t)bd->FontTexture); + + // Restore state + GL_CALL(glBindTexture(GL_TEXTURE_2D, last_texture)); + + return true; +} + +void ImGui_ImplOpenGL3_DestroyFontsTexture() +{ + ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); + if (bd->FontTexture) + { + glDeleteTextures(1, &bd->FontTexture); + io.Fonts->SetTexID(0); + bd->FontTexture = 0; + } +} + +// If you get an error please report on github. You may try different GL context version or GLSL version. See GL<>GLSL version table at the top of this file. +static bool CheckShader(GLuint handle, const char* desc) +{ + ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); + GLint status = 0, log_length = 0; + glGetShaderiv(handle, GL_COMPILE_STATUS, &status); + glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &log_length); + if ((GLboolean)status == GL_FALSE) + fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to compile %s! With GLSL: %s\n", desc, bd->GlslVersionString); + if (log_length > 1) + { + ImVector buf; + buf.resize((int)(log_length + 1)); + glGetShaderInfoLog(handle, log_length, nullptr, (GLchar*)buf.begin()); + fprintf(stderr, "%s\n", buf.begin()); + } + return (GLboolean)status == GL_TRUE; +} + +// If you get an error please report on GitHub. You may try different GL context version or GLSL version. +static bool CheckProgram(GLuint handle, const char* desc) +{ + ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); + GLint status = 0, log_length = 0; + glGetProgramiv(handle, GL_LINK_STATUS, &status); + glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &log_length); + if ((GLboolean)status == GL_FALSE) + fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to link %s! With GLSL %s\n", desc, bd->GlslVersionString); + if (log_length > 1) + { + ImVector buf; + buf.resize((int)(log_length + 1)); + glGetProgramInfoLog(handle, log_length, nullptr, (GLchar*)buf.begin()); + fprintf(stderr, "%s\n", buf.begin()); + } + return (GLboolean)status == GL_TRUE; +} + +bool ImGui_ImplOpenGL3_CreateDeviceObjects() +{ + ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); + + // Backup GL state + GLint last_texture, last_array_buffer; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); + glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer); +#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_BUFFER_PIXEL_UNPACK + GLint last_pixel_unpack_buffer = 0; + if (bd->GlVersion >= 210) { glGetIntegerv(GL_PIXEL_UNPACK_BUFFER_BINDING, &last_pixel_unpack_buffer); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); } +#endif +#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY + GLint last_vertex_array; + glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array); +#endif + + // Parse GLSL version string + int glsl_version = 130; + sscanf(bd->GlslVersionString, "#version %d", &glsl_version); + + const GLchar* vertex_shader_glsl_120 = + "uniform mat4 ProjMtx;\n" + "attribute vec2 Position;\n" + "attribute vec2 UV;\n" + "attribute vec4 Color;\n" + "varying vec2 Frag_UV;\n" + "varying vec4 Frag_Color;\n" + "void main()\n" + "{\n" + " Frag_UV = UV;\n" + " Frag_Color = Color;\n" + " gl_Position = ProjMtx * vec4(Position.xy,0,1);\n" + "}\n"; + + const GLchar* vertex_shader_glsl_130 = + "uniform mat4 ProjMtx;\n" + "in vec2 Position;\n" + "in vec2 UV;\n" + "in vec4 Color;\n" + "out vec2 Frag_UV;\n" + "out vec4 Frag_Color;\n" + "void main()\n" + "{\n" + " Frag_UV = UV;\n" + " Frag_Color = Color;\n" + " gl_Position = ProjMtx * vec4(Position.xy,0,1);\n" + "}\n"; + + const GLchar* vertex_shader_glsl_300_es = + "precision highp float;\n" + "layout (location = 0) in vec2 Position;\n" + "layout (location = 1) in vec2 UV;\n" + "layout (location = 2) in vec4 Color;\n" + "uniform mat4 ProjMtx;\n" + "out vec2 Frag_UV;\n" + "out vec4 Frag_Color;\n" + "void main()\n" + "{\n" + " Frag_UV = UV;\n" + " Frag_Color = Color;\n" + " gl_Position = ProjMtx * vec4(Position.xy,0,1);\n" + "}\n"; + + const GLchar* vertex_shader_glsl_410_core = + "layout (location = 0) in vec2 Position;\n" + "layout (location = 1) in vec2 UV;\n" + "layout (location = 2) in vec4 Color;\n" + "uniform mat4 ProjMtx;\n" + "out vec2 Frag_UV;\n" + "out vec4 Frag_Color;\n" + "void main()\n" + "{\n" + " Frag_UV = UV;\n" + " Frag_Color = Color;\n" + " gl_Position = ProjMtx * vec4(Position.xy,0,1);\n" + "}\n"; + + const GLchar* fragment_shader_glsl_120 = + "#ifdef GL_ES\n" + " precision mediump float;\n" + "#endif\n" + "uniform sampler2D Texture;\n" + "varying vec2 Frag_UV;\n" + "varying vec4 Frag_Color;\n" + "void main()\n" + "{\n" + " gl_FragColor = Frag_Color * texture2D(Texture, Frag_UV.st);\n" + "}\n"; + + const GLchar* fragment_shader_glsl_130 = + "uniform sampler2D Texture;\n" + "in vec2 Frag_UV;\n" + "in vec4 Frag_Color;\n" + "out vec4 Out_Color;\n" + "void main()\n" + "{\n" + " Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n" + "}\n"; + + const GLchar* fragment_shader_glsl_300_es = + "precision mediump float;\n" + "uniform sampler2D Texture;\n" + "in vec2 Frag_UV;\n" + "in vec4 Frag_Color;\n" + "layout (location = 0) out vec4 Out_Color;\n" + "void main()\n" + "{\n" + " Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n" + "}\n"; + + const GLchar* fragment_shader_glsl_410_core = + "in vec2 Frag_UV;\n" + "in vec4 Frag_Color;\n" + "uniform sampler2D Texture;\n" + "layout (location = 0) out vec4 Out_Color;\n" + "void main()\n" + "{\n" + " Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n" + "}\n"; + + // Select shaders matching our GLSL versions + const GLchar* vertex_shader = nullptr; + const GLchar* fragment_shader = nullptr; + if (glsl_version < 130) + { + vertex_shader = vertex_shader_glsl_120; + fragment_shader = fragment_shader_glsl_120; + } + else if (glsl_version >= 410) + { + vertex_shader = vertex_shader_glsl_410_core; + fragment_shader = fragment_shader_glsl_410_core; + } + else if (glsl_version == 300) + { + vertex_shader = vertex_shader_glsl_300_es; + fragment_shader = fragment_shader_glsl_300_es; + } + else + { + vertex_shader = vertex_shader_glsl_130; + fragment_shader = fragment_shader_glsl_130; + } + + // Create shaders + const GLchar* vertex_shader_with_version[2] = { bd->GlslVersionString, vertex_shader }; + GLuint vert_handle; + GL_CALL(vert_handle = glCreateShader(GL_VERTEX_SHADER)); + glShaderSource(vert_handle, 2, vertex_shader_with_version, nullptr); + glCompileShader(vert_handle); + CheckShader(vert_handle, "vertex shader"); + + const GLchar* fragment_shader_with_version[2] = { bd->GlslVersionString, fragment_shader }; + GLuint frag_handle; + GL_CALL(frag_handle = glCreateShader(GL_FRAGMENT_SHADER)); + glShaderSource(frag_handle, 2, fragment_shader_with_version, nullptr); + glCompileShader(frag_handle); + CheckShader(frag_handle, "fragment shader"); + + // Link + bd->ShaderHandle = glCreateProgram(); + glAttachShader(bd->ShaderHandle, vert_handle); + glAttachShader(bd->ShaderHandle, frag_handle); + glLinkProgram(bd->ShaderHandle); + CheckProgram(bd->ShaderHandle, "shader program"); + + glDetachShader(bd->ShaderHandle, vert_handle); + glDetachShader(bd->ShaderHandle, frag_handle); + glDeleteShader(vert_handle); + glDeleteShader(frag_handle); + + bd->AttribLocationTex = glGetUniformLocation(bd->ShaderHandle, "Texture"); + bd->AttribLocationProjMtx = glGetUniformLocation(bd->ShaderHandle, "ProjMtx"); + bd->AttribLocationVtxPos = (GLuint)glGetAttribLocation(bd->ShaderHandle, "Position"); + bd->AttribLocationVtxUV = (GLuint)glGetAttribLocation(bd->ShaderHandle, "UV"); + bd->AttribLocationVtxColor = (GLuint)glGetAttribLocation(bd->ShaderHandle, "Color"); + + // Create buffers + glGenBuffers(1, &bd->VboHandle); + glGenBuffers(1, &bd->ElementsHandle); + + ImGui_ImplOpenGL3_CreateFontsTexture(); + + // Restore modified GL state + glBindTexture(GL_TEXTURE_2D, last_texture); + glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer); +#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_BUFFER_PIXEL_UNPACK + if (bd->GlVersion >= 210) { glBindBuffer(GL_PIXEL_UNPACK_BUFFER, last_pixel_unpack_buffer); } +#endif +#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY + glBindVertexArray(last_vertex_array); +#endif + + return true; +} + +void ImGui_ImplOpenGL3_DestroyDeviceObjects() +{ + ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); + if (bd->VboHandle) { glDeleteBuffers(1, &bd->VboHandle); bd->VboHandle = 0; } + if (bd->ElementsHandle) { glDeleteBuffers(1, &bd->ElementsHandle); bd->ElementsHandle = 0; } + if (bd->ShaderHandle) { glDeleteProgram(bd->ShaderHandle); bd->ShaderHandle = 0; } + ImGui_ImplOpenGL3_DestroyFontsTexture(); +} + +//----------------------------------------------------------------------------- + +#if defined(__GNUC__) +#pragma GCC diagnostic pop +#endif +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + +#endif // #ifndef IMGUI_DISABLE diff --git a/source/console/ui/imgui/imgui_impl_opengl3.h b/source/console/ui/imgui/imgui_impl_opengl3.h new file mode 100644 index 0000000..fe31cc6 --- /dev/null +++ b/source/console/ui/imgui/imgui_impl_opengl3.h @@ -0,0 +1,66 @@ +// dear imgui: Renderer Backend for modern OpenGL with shaders / programmatic pipeline +// - Desktop GL: 2.x 3.x 4.x +// - Embedded GL: ES 2.0 (WebGL 1.0), ES 3.0 (WebGL 2.0) +// This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..) + +// Implemented features: +// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID! +// [x] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset) [Desktop OpenGL only!] + +// About WebGL/ES: +// - You need to '#define IMGUI_IMPL_OPENGL_ES2' or '#define IMGUI_IMPL_OPENGL_ES3' to use WebGL or OpenGL ES. +// - This is done automatically on iOS, Android and Emscripten targets. +// - For other targets, the define needs to be visible from the imgui_impl_opengl3.cpp compilation unit. If unsure, define globally or in imconfig.h. + +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. +// Learn about Dear ImGui: +// - FAQ https://dearimgui.com/faq +// - Getting Started https://dearimgui.com/getting-started +// - Documentation https://dearimgui.com/docs (same as your local docs/ folder). +// - Introduction, links and more at the top of imgui.cpp + +// About GLSL version: +// The 'glsl_version' initialization parameter should be nullptr (default) or a "#version XXX" string. +// On computer platform the GLSL version default to "#version 130". On OpenGL ES 3 platform it defaults to "#version 300 es" +// Only override if your GL version doesn't handle this GLSL version. See GLSL version table at the top of imgui_impl_opengl3.cpp. + +#pragma once +#include "imgui/imgui.h" // IMGUI_IMPL_API +#ifndef IMGUI_DISABLE + +// Follow "Getting Started" link and check examples/ folder to learn about using backends! +IMGUI_IMPL_API bool ImGui_ImplOpenGL3_Init(const char* glsl_version = nullptr); +IMGUI_IMPL_API void ImGui_ImplOpenGL3_Shutdown(); +IMGUI_IMPL_API void ImGui_ImplOpenGL3_NewFrame(); +IMGUI_IMPL_API void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data); + +// (Optional) Called by Init/NewFrame/Shutdown +IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateFontsTexture(); +IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyFontsTexture(); +IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateDeviceObjects(); +IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyDeviceObjects(); + +// Configuration flags to add in your imconfig file: +//#define IMGUI_IMPL_OPENGL_ES2 // Enable ES 2 (Auto-detected on Emscripten) +//#define IMGUI_IMPL_OPENGL_ES3 // Enable ES 3 (Auto-detected on iOS/Android) + +// You can explicitly select GLES2 or GLES3 API by using one of the '#define IMGUI_IMPL_OPENGL_LOADER_XXX' in imconfig.h or compiler command-line. +#if !defined(IMGUI_IMPL_OPENGL_ES2) \ + && !defined(IMGUI_IMPL_OPENGL_ES3) + +// Try to detect GLES on matching platforms +#if defined(__APPLE__) +#include +#endif +#if (defined(__APPLE__) && (TARGET_OS_IOS || TARGET_OS_TV)) || (defined(__ANDROID__)) +#define IMGUI_IMPL_OPENGL_ES3 // iOS, Android -> GL ES 3, "#version 300 es" +#elif defined(__EMSCRIPTEN__) || defined(__amigaos4__) +#define IMGUI_IMPL_OPENGL_ES2 // Emscripten -> GL ES 2, "#version 100" +#else +// Otherwise imgui_impl_opengl3_loader.h will be used. +#endif + +#endif + +#endif // #ifndef IMGUI_DISABLE diff --git a/source/console/ui/imgui/imgui_impl_opengl3_loader.h b/source/console/ui/imgui/imgui_impl_opengl3_loader.h new file mode 100644 index 0000000..d6ffa5a --- /dev/null +++ b/source/console/ui/imgui/imgui_impl_opengl3_loader.h @@ -0,0 +1,916 @@ +//----------------------------------------------------------------------------- +// About imgui_impl_opengl3_loader.h: +// +// We embed our own OpenGL loader to not require user to provide their own or to have to use ours, +// which proved to be endless problems for users. +// Our loader is custom-generated, based on gl3w but automatically filtered to only include +// enums/functions that we use in our imgui_impl_opengl3.cpp source file in order to be small. +// +// YOU SHOULD NOT NEED TO INCLUDE/USE THIS DIRECTLY. THIS IS USED BY imgui_impl_opengl3.cpp ONLY. +// THE REST OF YOUR APP SHOULD USE A DIFFERENT GL LOADER: ANY GL LOADER OF YOUR CHOICE. +// +// IF YOU GET BUILD ERRORS IN THIS FILE (commonly macro redefinitions or function redefinitions): +// IT LIKELY MEANS THAT YOU ARE BUILDING 'imgui_impl_opengl3.cpp' OR INCLUDING 'imgui_impl_opengl3_loader.h' +// IN THE SAME COMPILATION UNIT AS ONE OF YOUR FILE WHICH IS USING A THIRD-PARTY OPENGL LOADER. +// (e.g. COULD HAPPEN IF YOU ARE DOING A UNITY/JUMBO BUILD, OR INCLUDING .CPP FILES FROM OTHERS) +// YOU SHOULD NOT BUILD BOTH IN THE SAME COMPILATION UNIT. +// BUT IF YOU REALLY WANT TO, you can '#define IMGUI_IMPL_OPENGL_LOADER_CUSTOM' and imgui_impl_opengl3.cpp +// WILL NOT BE USING OUR LOADER, AND INSTEAD EXPECT ANOTHER/YOUR LOADER TO BE AVAILABLE IN THE COMPILATION UNIT. +// +// Regenerate with: +// python3 gl3w_gen.py --output ../imgui/backends/imgui_impl_opengl3_loader.h --ref ../imgui/backends/imgui_impl_opengl3.cpp ./extra_symbols.txt +// +// More info: +// https://github.com/dearimgui/gl3w_stripped +// https://github.com/ocornut/imgui/issues/4445 +//----------------------------------------------------------------------------- + +/* + * This file was generated with gl3w_gen.py, part of imgl3w + * (hosted at https://github.com/dearimgui/gl3w_stripped) + * + * This is free and unencumbered software released into the public domain. + * + * Anyone is free to copy, modify, publish, use, compile, sell, or + * distribute this software, either in source code form or as a compiled + * binary, for any purpose, commercial or non-commercial, and by any + * means. + * + * In jurisdictions that recognize copyright laws, the author or authors + * of this software dedicate any and all copyright interest in the + * software to the public domain. We make this dedication for the benefit + * of the public at large and to the detriment of our heirs and + * successors. We intend this dedication to be an overt act of + * relinquishment in perpetuity of all present and future rights to this + * software under copyright law. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __gl3w_h_ +#define __gl3w_h_ + +// Adapted from KHR/khrplatform.h to avoid including entire file. +#ifndef __khrplatform_h_ +typedef float khronos_float_t; +typedef signed char khronos_int8_t; +typedef unsigned char khronos_uint8_t; +typedef signed short int khronos_int16_t; +typedef unsigned short int khronos_uint16_t; +#ifdef _WIN64 +typedef signed long long int khronos_intptr_t; +typedef signed long long int khronos_ssize_t; +#else +typedef signed long int khronos_intptr_t; +typedef signed long int khronos_ssize_t; +#endif + +#if defined(_MSC_VER) && !defined(__clang__) +typedef signed __int64 khronos_int64_t; +typedef unsigned __int64 khronos_uint64_t; +#elif (defined(__clang__) || defined(__GNUC__)) && (__cplusplus < 201100) +#include +typedef int64_t khronos_int64_t; +typedef uint64_t khronos_uint64_t; +#else +typedef signed long long khronos_int64_t; +typedef unsigned long long khronos_uint64_t; +#endif +#endif // __khrplatform_h_ + +#ifndef __gl_glcorearb_h_ +#define __gl_glcorearb_h_ 1 +#ifdef __cplusplus +extern "C" { +#endif +/* +** Copyright 2013-2020 The Khronos Group Inc. +** SPDX-License-Identifier: MIT +** +** This header is generated from the Khronos OpenGL / OpenGL ES XML +** API Registry. The current version of the Registry, generator scripts +** used to make the header, and the header can be found at +** https://github.com/KhronosGroup/OpenGL-Registry +*/ +#if defined(_WIN32) && !defined(APIENTRY) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN 1 +#endif +#include +#endif +#ifndef APIENTRY +#define APIENTRY +#endif +#ifndef APIENTRYP +#define APIENTRYP APIENTRY * +#endif +#ifndef GLAPI +#define GLAPI extern +#endif +/* glcorearb.h is for use with OpenGL core profile implementations. +** It should should be placed in the same directory as gl.h and +** included as . +** +** glcorearb.h includes only APIs in the latest OpenGL core profile +** implementation together with APIs in newer ARB extensions which +** can be supported by the core profile. It does not, and never will +** include functionality removed from the core profile, such as +** fixed-function vertex and fragment processing. +** +** Do not #include both and either of or +** in the same source file. +*/ +/* Generated C header for: + * API: gl + * Profile: core + * Versions considered: .* + * Versions emitted: .* + * Default extensions included: glcore + * Additional extensions included: _nomatch_^ + * Extensions removed: _nomatch_^ + */ +#ifndef GL_VERSION_1_0 +typedef void GLvoid; +typedef unsigned int GLenum; + +typedef khronos_float_t GLfloat; +typedef int GLint; +typedef int GLsizei; +typedef unsigned int GLbitfield; +typedef double GLdouble; +typedef unsigned int GLuint; +typedef unsigned char GLboolean; +typedef khronos_uint8_t GLubyte; +#define GL_COLOR_BUFFER_BIT 0x00004000 +#define GL_FALSE 0 +#define GL_TRUE 1 +#define GL_TRIANGLES 0x0004 +#define GL_ONE 1 +#define GL_SRC_ALPHA 0x0302 +#define GL_ONE_MINUS_SRC_ALPHA 0x0303 +#define GL_FRONT 0x0404 +#define GL_BACK 0x0405 +#define GL_FRONT_AND_BACK 0x0408 +#define GL_POLYGON_MODE 0x0B40 +#define GL_CULL_FACE 0x0B44 +#define GL_DEPTH_TEST 0x0B71 +#define GL_STENCIL_TEST 0x0B90 +#define GL_VIEWPORT 0x0BA2 +#define GL_BLEND 0x0BE2 +#define GL_SCISSOR_BOX 0x0C10 +#define GL_SCISSOR_TEST 0x0C11 +#define GL_UNPACK_ROW_LENGTH 0x0CF2 +#define GL_PACK_ALIGNMENT 0x0D05 +#define GL_TEXTURE_2D 0x0DE1 +#define GL_UNSIGNED_BYTE 0x1401 +#define GL_UNSIGNED_SHORT 0x1403 +#define GL_UNSIGNED_INT 0x1405 +#define GL_FLOAT 0x1406 +#define GL_RGBA 0x1908 +#define GL_FILL 0x1B02 +#define GL_VENDOR 0x1F00 +#define GL_RENDERER 0x1F01 +#define GL_VERSION 0x1F02 +#define GL_EXTENSIONS 0x1F03 +#define GL_LINEAR 0x2601 +#define GL_TEXTURE_MAG_FILTER 0x2800 +#define GL_TEXTURE_MIN_FILTER 0x2801 +#define GL_TEXTURE_WRAP_S 0x2802 +#define GL_TEXTURE_WRAP_T 0x2803 +#define GL_REPEAT 0x2901 +typedef void (APIENTRYP PFNGLPOLYGONMODEPROC) (GLenum face, GLenum mode); +typedef void (APIENTRYP PFNGLSCISSORPROC) (GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLTEXPARAMETERIPROC) (GLenum target, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLTEXIMAGE2DPROC) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLCLEARPROC) (GLbitfield mask); +typedef void (APIENTRYP PFNGLCLEARCOLORPROC) (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +typedef void (APIENTRYP PFNGLDISABLEPROC) (GLenum cap); +typedef void (APIENTRYP PFNGLENABLEPROC) (GLenum cap); +typedef void (APIENTRYP PFNGLFLUSHPROC) (void); +typedef void (APIENTRYP PFNGLPIXELSTOREIPROC) (GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLREADPIXELSPROC) (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels); +typedef GLenum (APIENTRYP PFNGLGETERRORPROC) (void); +typedef void (APIENTRYP PFNGLGETINTEGERVPROC) (GLenum pname, GLint *data); +typedef const GLubyte *(APIENTRYP PFNGLGETSTRINGPROC) (GLenum name); +typedef GLboolean (APIENTRYP PFNGLISENABLEDPROC) (GLenum cap); +typedef void (APIENTRYP PFNGLVIEWPORTPROC) (GLint x, GLint y, GLsizei width, GLsizei height); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPolygonMode (GLenum face, GLenum mode); +GLAPI void APIENTRY glScissor (GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glTexParameteri (GLenum target, GLenum pname, GLint param); +GLAPI void APIENTRY glTexImage2D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glClear (GLbitfield mask); +GLAPI void APIENTRY glClearColor (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +GLAPI void APIENTRY glDisable (GLenum cap); +GLAPI void APIENTRY glEnable (GLenum cap); +GLAPI void APIENTRY glFlush (void); +GLAPI void APIENTRY glPixelStorei (GLenum pname, GLint param); +GLAPI void APIENTRY glReadPixels (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels); +GLAPI GLenum APIENTRY glGetError (void); +GLAPI void APIENTRY glGetIntegerv (GLenum pname, GLint *data); +GLAPI const GLubyte *APIENTRY glGetString (GLenum name); +GLAPI GLboolean APIENTRY glIsEnabled (GLenum cap); +GLAPI void APIENTRY glViewport (GLint x, GLint y, GLsizei width, GLsizei height); +#endif +#endif /* GL_VERSION_1_0 */ +#ifndef GL_VERSION_1_1 +typedef khronos_float_t GLclampf; +typedef double GLclampd; +#define GL_TEXTURE_BINDING_2D 0x8069 +typedef void (APIENTRYP PFNGLDRAWELEMENTSPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices); +typedef void (APIENTRYP PFNGLBINDTEXTUREPROC) (GLenum target, GLuint texture); +typedef void (APIENTRYP PFNGLDELETETEXTURESPROC) (GLsizei n, const GLuint *textures); +typedef void (APIENTRYP PFNGLGENTEXTURESPROC) (GLsizei n, GLuint *textures); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawElements (GLenum mode, GLsizei count, GLenum type, const void *indices); +GLAPI void APIENTRY glBindTexture (GLenum target, GLuint texture); +GLAPI void APIENTRY glDeleteTextures (GLsizei n, const GLuint *textures); +GLAPI void APIENTRY glGenTextures (GLsizei n, GLuint *textures); +#endif +#endif /* GL_VERSION_1_1 */ +#ifndef GL_VERSION_1_2 +#define GL_CLAMP_TO_EDGE 0x812F +#endif /* GL_VERSION_1_2 */ +#ifndef GL_VERSION_1_3 +#define GL_TEXTURE0 0x84C0 +#define GL_ACTIVE_TEXTURE 0x84E0 +typedef void (APIENTRYP PFNGLACTIVETEXTUREPROC) (GLenum texture); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glActiveTexture (GLenum texture); +#endif +#endif /* GL_VERSION_1_3 */ +#ifndef GL_VERSION_1_4 +#define GL_BLEND_DST_RGB 0x80C8 +#define GL_BLEND_SRC_RGB 0x80C9 +#define GL_BLEND_DST_ALPHA 0x80CA +#define GL_BLEND_SRC_ALPHA 0x80CB +#define GL_FUNC_ADD 0x8006 +typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEPROC) (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +typedef void (APIENTRYP PFNGLBLENDEQUATIONPROC) (GLenum mode); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendFuncSeparate (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +GLAPI void APIENTRY glBlendEquation (GLenum mode); +#endif +#endif /* GL_VERSION_1_4 */ +#ifndef GL_VERSION_1_5 +typedef khronos_ssize_t GLsizeiptr; +typedef khronos_intptr_t GLintptr; +#define GL_ARRAY_BUFFER 0x8892 +#define GL_ELEMENT_ARRAY_BUFFER 0x8893 +#define GL_ARRAY_BUFFER_BINDING 0x8894 +#define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895 +#define GL_STREAM_DRAW 0x88E0 +typedef void (APIENTRYP PFNGLBINDBUFFERPROC) (GLenum target, GLuint buffer); +typedef void (APIENTRYP PFNGLDELETEBUFFERSPROC) (GLsizei n, const GLuint *buffers); +typedef void (APIENTRYP PFNGLGENBUFFERSPROC) (GLsizei n, GLuint *buffers); +typedef void (APIENTRYP PFNGLBUFFERDATAPROC) (GLenum target, GLsizeiptr size, const void *data, GLenum usage); +typedef void (APIENTRYP PFNGLBUFFERSUBDATAPROC) (GLenum target, GLintptr offset, GLsizeiptr size, const void *data); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBindBuffer (GLenum target, GLuint buffer); +GLAPI void APIENTRY glDeleteBuffers (GLsizei n, const GLuint *buffers); +GLAPI void APIENTRY glGenBuffers (GLsizei n, GLuint *buffers); +GLAPI void APIENTRY glBufferData (GLenum target, GLsizeiptr size, const void *data, GLenum usage); +GLAPI void APIENTRY glBufferSubData (GLenum target, GLintptr offset, GLsizeiptr size, const void *data); +#endif +#endif /* GL_VERSION_1_5 */ +#ifndef GL_VERSION_2_0 +typedef char GLchar; +typedef khronos_int16_t GLshort; +typedef khronos_int8_t GLbyte; +typedef khronos_uint16_t GLushort; +#define GL_BLEND_EQUATION_RGB 0x8009 +#define GL_VERTEX_ATTRIB_ARRAY_ENABLED 0x8622 +#define GL_VERTEX_ATTRIB_ARRAY_SIZE 0x8623 +#define GL_VERTEX_ATTRIB_ARRAY_STRIDE 0x8624 +#define GL_VERTEX_ATTRIB_ARRAY_TYPE 0x8625 +#define GL_VERTEX_ATTRIB_ARRAY_POINTER 0x8645 +#define GL_BLEND_EQUATION_ALPHA 0x883D +#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED 0x886A +#define GL_FRAGMENT_SHADER 0x8B30 +#define GL_VERTEX_SHADER 0x8B31 +#define GL_COMPILE_STATUS 0x8B81 +#define GL_LINK_STATUS 0x8B82 +#define GL_INFO_LOG_LENGTH 0x8B84 +#define GL_CURRENT_PROGRAM 0x8B8D +#define GL_UPPER_LEFT 0x8CA2 +typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEPROC) (GLenum modeRGB, GLenum modeAlpha); +typedef void (APIENTRYP PFNGLATTACHSHADERPROC) (GLuint program, GLuint shader); +typedef void (APIENTRYP PFNGLCOMPILESHADERPROC) (GLuint shader); +typedef GLuint (APIENTRYP PFNGLCREATEPROGRAMPROC) (void); +typedef GLuint (APIENTRYP PFNGLCREATESHADERPROC) (GLenum type); +typedef void (APIENTRYP PFNGLDELETEPROGRAMPROC) (GLuint program); +typedef void (APIENTRYP PFNGLDELETESHADERPROC) (GLuint shader); +typedef void (APIENTRYP PFNGLDETACHSHADERPROC) (GLuint program, GLuint shader); +typedef void (APIENTRYP PFNGLDISABLEVERTEXATTRIBARRAYPROC) (GLuint index); +typedef void (APIENTRYP PFNGLENABLEVERTEXATTRIBARRAYPROC) (GLuint index); +typedef GLint (APIENTRYP PFNGLGETATTRIBLOCATIONPROC) (GLuint program, const GLchar *name); +typedef void (APIENTRYP PFNGLGETPROGRAMIVPROC) (GLuint program, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETPROGRAMINFOLOGPROC) (GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +typedef void (APIENTRYP PFNGLGETSHADERIVPROC) (GLuint shader, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETSHADERINFOLOGPROC) (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +typedef GLint (APIENTRYP PFNGLGETUNIFORMLOCATIONPROC) (GLuint program, const GLchar *name); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIVPROC) (GLuint index, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVPROC) (GLuint index, GLenum pname, void **pointer); +typedef GLboolean (APIENTRYP PFNGLISPROGRAMPROC) (GLuint program); +typedef void (APIENTRYP PFNGLLINKPROGRAMPROC) (GLuint program); +typedef void (APIENTRYP PFNGLSHADERSOURCEPROC) (GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length); +typedef void (APIENTRYP PFNGLUSEPROGRAMPROC) (GLuint program); +typedef void (APIENTRYP PFNGLUNIFORM1IPROC) (GLint location, GLint v0); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendEquationSeparate (GLenum modeRGB, GLenum modeAlpha); +GLAPI void APIENTRY glAttachShader (GLuint program, GLuint shader); +GLAPI void APIENTRY glCompileShader (GLuint shader); +GLAPI GLuint APIENTRY glCreateProgram (void); +GLAPI GLuint APIENTRY glCreateShader (GLenum type); +GLAPI void APIENTRY glDeleteProgram (GLuint program); +GLAPI void APIENTRY glDeleteShader (GLuint shader); +GLAPI void APIENTRY glDetachShader (GLuint program, GLuint shader); +GLAPI void APIENTRY glDisableVertexAttribArray (GLuint index); +GLAPI void APIENTRY glEnableVertexAttribArray (GLuint index); +GLAPI GLint APIENTRY glGetAttribLocation (GLuint program, const GLchar *name); +GLAPI void APIENTRY glGetProgramiv (GLuint program, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetProgramInfoLog (GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +GLAPI void APIENTRY glGetShaderiv (GLuint shader, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetShaderInfoLog (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +GLAPI GLint APIENTRY glGetUniformLocation (GLuint program, const GLchar *name); +GLAPI void APIENTRY glGetVertexAttribiv (GLuint index, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetVertexAttribPointerv (GLuint index, GLenum pname, void **pointer); +GLAPI GLboolean APIENTRY glIsProgram (GLuint program); +GLAPI void APIENTRY glLinkProgram (GLuint program); +GLAPI void APIENTRY glShaderSource (GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length); +GLAPI void APIENTRY glUseProgram (GLuint program); +GLAPI void APIENTRY glUniform1i (GLint location, GLint v0); +GLAPI void APIENTRY glUniformMatrix4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glVertexAttribPointer (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer); +#endif +#endif /* GL_VERSION_2_0 */ +#ifndef GL_VERSION_2_1 +#define GL_PIXEL_UNPACK_BUFFER 0x88EC +#define GL_PIXEL_UNPACK_BUFFER_BINDING 0x88EF +#endif /* GL_VERSION_2_1 */ +#ifndef GL_VERSION_3_0 +typedef khronos_uint16_t GLhalf; +#define GL_MAJOR_VERSION 0x821B +#define GL_MINOR_VERSION 0x821C +#define GL_NUM_EXTENSIONS 0x821D +#define GL_FRAMEBUFFER_SRGB 0x8DB9 +#define GL_VERTEX_ARRAY_BINDING 0x85B5 +typedef void (APIENTRYP PFNGLGETBOOLEANI_VPROC) (GLenum target, GLuint index, GLboolean *data); +typedef void (APIENTRYP PFNGLGETINTEGERI_VPROC) (GLenum target, GLuint index, GLint *data); +typedef const GLubyte *(APIENTRYP PFNGLGETSTRINGIPROC) (GLenum name, GLuint index); +typedef void (APIENTRYP PFNGLBINDVERTEXARRAYPROC) (GLuint array); +typedef void (APIENTRYP PFNGLDELETEVERTEXARRAYSPROC) (GLsizei n, const GLuint *arrays); +typedef void (APIENTRYP PFNGLGENVERTEXARRAYSPROC) (GLsizei n, GLuint *arrays); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI const GLubyte *APIENTRY glGetStringi (GLenum name, GLuint index); +GLAPI void APIENTRY glBindVertexArray (GLuint array); +GLAPI void APIENTRY glDeleteVertexArrays (GLsizei n, const GLuint *arrays); +GLAPI void APIENTRY glGenVertexArrays (GLsizei n, GLuint *arrays); +#endif +#endif /* GL_VERSION_3_0 */ +#ifndef GL_VERSION_3_1 +#define GL_VERSION_3_1 1 +#define GL_PRIMITIVE_RESTART 0x8F9D +#endif /* GL_VERSION_3_1 */ +#ifndef GL_VERSION_3_2 +#define GL_VERSION_3_2 1 +typedef struct __GLsync *GLsync; +typedef khronos_uint64_t GLuint64; +typedef khronos_int64_t GLint64; +#define GL_CONTEXT_COMPATIBILITY_PROFILE_BIT 0x00000002 +#define GL_CONTEXT_PROFILE_MASK 0x9126 +typedef void (APIENTRYP PFNGLDRAWELEMENTSBASEVERTEXPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex); +typedef void (APIENTRYP PFNGLGETINTEGER64I_VPROC) (GLenum target, GLuint index, GLint64 *data); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawElementsBaseVertex (GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex); +#endif +#endif /* GL_VERSION_3_2 */ +#ifndef GL_VERSION_3_3 +#define GL_VERSION_3_3 1 +#define GL_SAMPLER_BINDING 0x8919 +typedef void (APIENTRYP PFNGLBINDSAMPLERPROC) (GLuint unit, GLuint sampler); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBindSampler (GLuint unit, GLuint sampler); +#endif +#endif /* GL_VERSION_3_3 */ +#ifndef GL_VERSION_4_1 +typedef void (APIENTRYP PFNGLGETFLOATI_VPROC) (GLenum target, GLuint index, GLfloat *data); +typedef void (APIENTRYP PFNGLGETDOUBLEI_VPROC) (GLenum target, GLuint index, GLdouble *data); +#endif /* GL_VERSION_4_1 */ +#ifndef GL_VERSION_4_3 +typedef void (APIENTRY *GLDEBUGPROC)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); +#endif /* GL_VERSION_4_3 */ +#ifndef GL_VERSION_4_5 +#define GL_CLIP_ORIGIN 0x935C +typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKI_VPROC) (GLuint xfb, GLenum pname, GLuint index, GLint *param); +typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKI64_VPROC) (GLuint xfb, GLenum pname, GLuint index, GLint64 *param); +#endif /* GL_VERSION_4_5 */ +#ifndef GL_ARB_bindless_texture +typedef khronos_uint64_t GLuint64EXT; +#endif /* GL_ARB_bindless_texture */ +#ifndef GL_ARB_cl_event +struct _cl_context; +struct _cl_event; +#endif /* GL_ARB_cl_event */ +#ifndef GL_ARB_clip_control +#define GL_ARB_clip_control 1 +#endif /* GL_ARB_clip_control */ +#ifndef GL_ARB_debug_output +typedef void (APIENTRY *GLDEBUGPROCARB)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); +#endif /* GL_ARB_debug_output */ +#ifndef GL_EXT_EGL_image_storage +typedef void *GLeglImageOES; +#endif /* GL_EXT_EGL_image_storage */ +#ifndef GL_EXT_direct_state_access +typedef void (APIENTRYP PFNGLGETFLOATI_VEXTPROC) (GLenum pname, GLuint index, GLfloat *params); +typedef void (APIENTRYP PFNGLGETDOUBLEI_VEXTPROC) (GLenum pname, GLuint index, GLdouble *params); +typedef void (APIENTRYP PFNGLGETPOINTERI_VEXTPROC) (GLenum pname, GLuint index, void **params); +typedef void (APIENTRYP PFNGLGETVERTEXARRAYINTEGERI_VEXTPROC) (GLuint vaobj, GLuint index, GLenum pname, GLint *param); +typedef void (APIENTRYP PFNGLGETVERTEXARRAYPOINTERI_VEXTPROC) (GLuint vaobj, GLuint index, GLenum pname, void **param); +#endif /* GL_EXT_direct_state_access */ +#ifndef GL_NV_draw_vulkan_image +typedef void (APIENTRY *GLVULKANPROCNV)(void); +#endif /* GL_NV_draw_vulkan_image */ +#ifndef GL_NV_gpu_shader5 +typedef khronos_int64_t GLint64EXT; +#endif /* GL_NV_gpu_shader5 */ +#ifndef GL_NV_vertex_buffer_unified_memory +typedef void (APIENTRYP PFNGLGETINTEGERUI64I_VNVPROC) (GLenum value, GLuint index, GLuint64EXT *result); +#endif /* GL_NV_vertex_buffer_unified_memory */ +#ifdef __cplusplus +} +#endif +#endif + +#ifndef GL3W_API +#define GL3W_API +#endif + +#ifndef __gl_h_ +#define __gl_h_ +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define GL3W_OK 0 +#define GL3W_ERROR_INIT -1 +#define GL3W_ERROR_LIBRARY_OPEN -2 +#define GL3W_ERROR_OPENGL_VERSION -3 + +typedef void (*GL3WglProc)(void); +typedef GL3WglProc (*GL3WGetProcAddressProc)(const char *proc); + +/* gl3w api */ +GL3W_API int imgl3wInit(void); +GL3W_API int imgl3wInit2(GL3WGetProcAddressProc proc); +GL3W_API int imgl3wIsSupported(int major, int minor); +GL3W_API GL3WglProc imgl3wGetProcAddress(const char *proc); + +/* gl3w internal state */ +union ImGL3WProcs { + GL3WglProc ptr[59]; + struct { + PFNGLACTIVETEXTUREPROC ActiveTexture; + PFNGLATTACHSHADERPROC AttachShader; + PFNGLBINDBUFFERPROC BindBuffer; + PFNGLBINDSAMPLERPROC BindSampler; + PFNGLBINDTEXTUREPROC BindTexture; + PFNGLBINDVERTEXARRAYPROC BindVertexArray; + PFNGLBLENDEQUATIONPROC BlendEquation; + PFNGLBLENDEQUATIONSEPARATEPROC BlendEquationSeparate; + PFNGLBLENDFUNCSEPARATEPROC BlendFuncSeparate; + PFNGLBUFFERDATAPROC BufferData; + PFNGLBUFFERSUBDATAPROC BufferSubData; + PFNGLCLEARPROC Clear; + PFNGLCLEARCOLORPROC ClearColor; + PFNGLCOMPILESHADERPROC CompileShader; + PFNGLCREATEPROGRAMPROC CreateProgram; + PFNGLCREATESHADERPROC CreateShader; + PFNGLDELETEBUFFERSPROC DeleteBuffers; + PFNGLDELETEPROGRAMPROC DeleteProgram; + PFNGLDELETESHADERPROC DeleteShader; + PFNGLDELETETEXTURESPROC DeleteTextures; + PFNGLDELETEVERTEXARRAYSPROC DeleteVertexArrays; + PFNGLDETACHSHADERPROC DetachShader; + PFNGLDISABLEPROC Disable; + PFNGLDISABLEVERTEXATTRIBARRAYPROC DisableVertexAttribArray; + PFNGLDRAWELEMENTSPROC DrawElements; + PFNGLDRAWELEMENTSBASEVERTEXPROC DrawElementsBaseVertex; + PFNGLENABLEPROC Enable; + PFNGLENABLEVERTEXATTRIBARRAYPROC EnableVertexAttribArray; + PFNGLFLUSHPROC Flush; + PFNGLGENBUFFERSPROC GenBuffers; + PFNGLGENTEXTURESPROC GenTextures; + PFNGLGENVERTEXARRAYSPROC GenVertexArrays; + PFNGLGETATTRIBLOCATIONPROC GetAttribLocation; + PFNGLGETERRORPROC GetError; + PFNGLGETINTEGERVPROC GetIntegerv; + PFNGLGETPROGRAMINFOLOGPROC GetProgramInfoLog; + PFNGLGETPROGRAMIVPROC GetProgramiv; + PFNGLGETSHADERINFOLOGPROC GetShaderInfoLog; + PFNGLGETSHADERIVPROC GetShaderiv; + PFNGLGETSTRINGPROC GetString; + PFNGLGETSTRINGIPROC GetStringi; + PFNGLGETUNIFORMLOCATIONPROC GetUniformLocation; + PFNGLGETVERTEXATTRIBPOINTERVPROC GetVertexAttribPointerv; + PFNGLGETVERTEXATTRIBIVPROC GetVertexAttribiv; + PFNGLISENABLEDPROC IsEnabled; + PFNGLISPROGRAMPROC IsProgram; + PFNGLLINKPROGRAMPROC LinkProgram; + PFNGLPIXELSTOREIPROC PixelStorei; + PFNGLPOLYGONMODEPROC PolygonMode; + PFNGLREADPIXELSPROC ReadPixels; + PFNGLSCISSORPROC Scissor; + PFNGLSHADERSOURCEPROC ShaderSource; + PFNGLTEXIMAGE2DPROC TexImage2D; + PFNGLTEXPARAMETERIPROC TexParameteri; + PFNGLUNIFORM1IPROC Uniform1i; + PFNGLUNIFORMMATRIX4FVPROC UniformMatrix4fv; + PFNGLUSEPROGRAMPROC UseProgram; + PFNGLVERTEXATTRIBPOINTERPROC VertexAttribPointer; + PFNGLVIEWPORTPROC Viewport; + } gl; +}; + +GL3W_API extern union ImGL3WProcs imgl3wProcs; + +/* OpenGL functions */ +#define glActiveTexture imgl3wProcs.gl.ActiveTexture +#define glAttachShader imgl3wProcs.gl.AttachShader +#define glBindBuffer imgl3wProcs.gl.BindBuffer +#define glBindSampler imgl3wProcs.gl.BindSampler +#define glBindTexture imgl3wProcs.gl.BindTexture +#define glBindVertexArray imgl3wProcs.gl.BindVertexArray +#define glBlendEquation imgl3wProcs.gl.BlendEquation +#define glBlendEquationSeparate imgl3wProcs.gl.BlendEquationSeparate +#define glBlendFuncSeparate imgl3wProcs.gl.BlendFuncSeparate +#define glBufferData imgl3wProcs.gl.BufferData +#define glBufferSubData imgl3wProcs.gl.BufferSubData +#define glClear imgl3wProcs.gl.Clear +#define glClearColor imgl3wProcs.gl.ClearColor +#define glCompileShader imgl3wProcs.gl.CompileShader +#define glCreateProgram imgl3wProcs.gl.CreateProgram +#define glCreateShader imgl3wProcs.gl.CreateShader +#define glDeleteBuffers imgl3wProcs.gl.DeleteBuffers +#define glDeleteProgram imgl3wProcs.gl.DeleteProgram +#define glDeleteShader imgl3wProcs.gl.DeleteShader +#define glDeleteTextures imgl3wProcs.gl.DeleteTextures +#define glDeleteVertexArrays imgl3wProcs.gl.DeleteVertexArrays +#define glDetachShader imgl3wProcs.gl.DetachShader +#define glDisable imgl3wProcs.gl.Disable +#define glDisableVertexAttribArray imgl3wProcs.gl.DisableVertexAttribArray +#define glDrawElements imgl3wProcs.gl.DrawElements +#define glDrawElementsBaseVertex imgl3wProcs.gl.DrawElementsBaseVertex +#define glEnable imgl3wProcs.gl.Enable +#define glEnableVertexAttribArray imgl3wProcs.gl.EnableVertexAttribArray +#define glFlush imgl3wProcs.gl.Flush +#define glGenBuffers imgl3wProcs.gl.GenBuffers +#define glGenTextures imgl3wProcs.gl.GenTextures +#define glGenVertexArrays imgl3wProcs.gl.GenVertexArrays +#define glGetAttribLocation imgl3wProcs.gl.GetAttribLocation +#define glGetError imgl3wProcs.gl.GetError +#define glGetIntegerv imgl3wProcs.gl.GetIntegerv +#define glGetProgramInfoLog imgl3wProcs.gl.GetProgramInfoLog +#define glGetProgramiv imgl3wProcs.gl.GetProgramiv +#define glGetShaderInfoLog imgl3wProcs.gl.GetShaderInfoLog +#define glGetShaderiv imgl3wProcs.gl.GetShaderiv +#define glGetString imgl3wProcs.gl.GetString +#define glGetStringi imgl3wProcs.gl.GetStringi +#define glGetUniformLocation imgl3wProcs.gl.GetUniformLocation +#define glGetVertexAttribPointerv imgl3wProcs.gl.GetVertexAttribPointerv +#define glGetVertexAttribiv imgl3wProcs.gl.GetVertexAttribiv +#define glIsEnabled imgl3wProcs.gl.IsEnabled +#define glIsProgram imgl3wProcs.gl.IsProgram +#define glLinkProgram imgl3wProcs.gl.LinkProgram +#define glPixelStorei imgl3wProcs.gl.PixelStorei +#define glPolygonMode imgl3wProcs.gl.PolygonMode +#define glReadPixels imgl3wProcs.gl.ReadPixels +#define glScissor imgl3wProcs.gl.Scissor +#define glShaderSource imgl3wProcs.gl.ShaderSource +#define glTexImage2D imgl3wProcs.gl.TexImage2D +#define glTexParameteri imgl3wProcs.gl.TexParameteri +#define glUniform1i imgl3wProcs.gl.Uniform1i +#define glUniformMatrix4fv imgl3wProcs.gl.UniformMatrix4fv +#define glUseProgram imgl3wProcs.gl.UseProgram +#define glVertexAttribPointer imgl3wProcs.gl.VertexAttribPointer +#define glViewport imgl3wProcs.gl.Viewport + +#ifdef __cplusplus +} +#endif + +#endif + +#ifdef IMGL3W_IMPL +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#define GL3W_ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +#if defined(_WIN32) +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN 1 +#endif +#include + +static HMODULE libgl; +typedef PROC(__stdcall* GL3WglGetProcAddr)(LPCSTR); +static GL3WglGetProcAddr wgl_get_proc_address; + +static int open_libgl(void) +{ + libgl = LoadLibraryA("opengl32.dll"); + if (!libgl) + return GL3W_ERROR_LIBRARY_OPEN; + wgl_get_proc_address = (GL3WglGetProcAddr)GetProcAddress(libgl, "wglGetProcAddress"); + return GL3W_OK; +} + +static void close_libgl(void) { FreeLibrary(libgl); } +static GL3WglProc get_proc(const char *proc) +{ + GL3WglProc res; + res = (GL3WglProc)wgl_get_proc_address(proc); + if (!res) + res = (GL3WglProc)GetProcAddress(libgl, proc); + return res; +} +#elif defined(__APPLE__) +#include + +static void *libgl; +static int open_libgl(void) +{ + libgl = dlopen("/System/Library/Frameworks/OpenGL.framework/OpenGL", RTLD_LAZY | RTLD_LOCAL); + if (!libgl) + return GL3W_ERROR_LIBRARY_OPEN; + return GL3W_OK; +} + +static void close_libgl(void) { dlclose(libgl); } + +static GL3WglProc get_proc(const char *proc) +{ + GL3WglProc res; + *(void **)(&res) = dlsym(libgl, proc); + return res; +} +#else +#include + +static void* libgl; // OpenGL library +static void* libglx; // GLX library +static void* libegl; // EGL library +static GL3WGetProcAddressProc gl_get_proc_address; + +static void close_libgl(void) +{ + if (libgl) { + dlclose(libgl); + libgl = NULL; + } + if (libegl) { + dlclose(libegl); + libegl = NULL; + } + if (libglx) { + dlclose(libglx); + libglx = NULL; + } +} + +static int is_library_loaded(const char* name, void** lib) +{ + *lib = dlopen(name, RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD); + return *lib != NULL; +} + +static int open_libs(void) +{ + // On Linux we have two APIs to get process addresses: EGL and GLX. + // EGL is supported under both X11 and Wayland, whereas GLX is X11-specific. + + libgl = NULL; + libegl = NULL; + libglx = NULL; + + // First check what's already loaded, the windowing library might have + // already loaded either EGL or GLX and we want to use the same one. + + if (is_library_loaded("libEGL.so.1", &libegl) || + is_library_loaded("libGLX.so.0", &libglx)) { + libgl = dlopen("libOpenGL.so.0", RTLD_LAZY | RTLD_LOCAL); + if (libgl) + return GL3W_OK; + else + close_libgl(); + } + + if (is_library_loaded("libGL.so", &libgl)) + return GL3W_OK; + if (is_library_loaded("libGL.so.1", &libgl)) + return GL3W_OK; + if (is_library_loaded("libGL.so.3", &libgl)) + return GL3W_OK; + + // Neither is already loaded, so we have to load one. Try EGL first + // because it is supported under both X11 and Wayland. + + // Load OpenGL + EGL + libgl = dlopen("libOpenGL.so.0", RTLD_LAZY | RTLD_LOCAL); + libegl = dlopen("libEGL.so.1", RTLD_LAZY | RTLD_LOCAL); + if (libgl && libegl) + return GL3W_OK; + else + close_libgl(); + + // Fall back to legacy libGL, which includes GLX + // While most systems use libGL.so.1, NetBSD seems to use that libGL.so.3. See https://github.com/ocornut/imgui/issues/6983 + libgl = dlopen("libGL.so", RTLD_LAZY | RTLD_LOCAL); + if (!libgl) + libgl = dlopen("libGL.so.1", RTLD_LAZY | RTLD_LOCAL); + if (!libgl) + libgl = dlopen("libGL.so.3", RTLD_LAZY | RTLD_LOCAL); + + if (libgl) + return GL3W_OK; + + return GL3W_ERROR_LIBRARY_OPEN; +} + +static int open_libgl(void) +{ + int res = open_libs(); + if (res) + return res; + + if (libegl) + *(void**)(&gl_get_proc_address) = dlsym(libegl, "eglGetProcAddress"); + else if (libglx) + *(void**)(&gl_get_proc_address) = dlsym(libglx, "glXGetProcAddressARB"); + else + *(void**)(&gl_get_proc_address) = dlsym(libgl, "glXGetProcAddressARB"); + + if (!gl_get_proc_address) { + close_libgl(); + return GL3W_ERROR_LIBRARY_OPEN; + } + + return GL3W_OK; +} + +static GL3WglProc get_proc(const char* proc) +{ + GL3WglProc res = NULL; + + // Before EGL version 1.5, eglGetProcAddress doesn't support querying core + // functions and may return a dummy function if we try, so try to load the + // function from the GL library directly first. + if (libegl) + *(void**)(&res) = dlsym(libgl, proc); + + if (!res) + res = gl_get_proc_address(proc); + + if (!libegl && !res) + *(void**)(&res) = dlsym(libgl, proc); + + return res; +} +#endif + +static struct { int major, minor; } version; + +static int parse_version(void) +{ + if (!glGetIntegerv) + return GL3W_ERROR_INIT; + glGetIntegerv(GL_MAJOR_VERSION, &version.major); + glGetIntegerv(GL_MINOR_VERSION, &version.minor); + if (version.major == 0 && version.minor == 0) + { + // Query GL_VERSION in desktop GL 2.x, the string will start with "." + if (const char* gl_version = (const char*)glGetString(GL_VERSION)) + sscanf(gl_version, "%d.%d", &version.major, &version.minor); + } + if (version.major < 2) + return GL3W_ERROR_OPENGL_VERSION; + return GL3W_OK; +} + +static void load_procs(GL3WGetProcAddressProc proc); + +int imgl3wInit(void) +{ + int res = open_libgl(); + if (res) + return res; + atexit(close_libgl); + return imgl3wInit2(get_proc); +} + +int imgl3wInit2(GL3WGetProcAddressProc proc) +{ + load_procs(proc); + return parse_version(); +} + +int imgl3wIsSupported(int major, int minor) +{ + if (major < 2) + return 0; + if (version.major == major) + return version.minor >= minor; + return version.major >= major; +} + +GL3WglProc imgl3wGetProcAddress(const char *proc) { return get_proc(proc); } + +static const char *proc_names[] = { + "glActiveTexture", + "glAttachShader", + "glBindBuffer", + "glBindSampler", + "glBindTexture", + "glBindVertexArray", + "glBlendEquation", + "glBlendEquationSeparate", + "glBlendFuncSeparate", + "glBufferData", + "glBufferSubData", + "glClear", + "glClearColor", + "glCompileShader", + "glCreateProgram", + "glCreateShader", + "glDeleteBuffers", + "glDeleteProgram", + "glDeleteShader", + "glDeleteTextures", + "glDeleteVertexArrays", + "glDetachShader", + "glDisable", + "glDisableVertexAttribArray", + "glDrawElements", + "glDrawElementsBaseVertex", + "glEnable", + "glEnableVertexAttribArray", + "glFlush", + "glGenBuffers", + "glGenTextures", + "glGenVertexArrays", + "glGetAttribLocation", + "glGetError", + "glGetIntegerv", + "glGetProgramInfoLog", + "glGetProgramiv", + "glGetShaderInfoLog", + "glGetShaderiv", + "glGetString", + "glGetStringi", + "glGetUniformLocation", + "glGetVertexAttribPointerv", + "glGetVertexAttribiv", + "glIsEnabled", + "glIsProgram", + "glLinkProgram", + "glPixelStorei", + "glPolygonMode", + "glReadPixels", + "glScissor", + "glShaderSource", + "glTexImage2D", + "glTexParameteri", + "glUniform1i", + "glUniformMatrix4fv", + "glUseProgram", + "glVertexAttribPointer", + "glViewport", +}; + +GL3W_API union ImGL3WProcs imgl3wProcs; + +static void load_procs(GL3WGetProcAddressProc proc) +{ + size_t i; + for (i = 0; i < GL3W_ARRAY_SIZE(proc_names); i++) + imgl3wProcs.ptr[i] = proc(proc_names[i]); +} + +#ifdef __cplusplus +} +#endif +#endif diff --git a/source/console/ui/imgui/imgui_impl_sdl3.cpp b/source/console/ui/imgui/imgui_impl_sdl3.cpp new file mode 100644 index 0000000..68d4cee --- /dev/null +++ b/source/console/ui/imgui/imgui_impl_sdl3.cpp @@ -0,0 +1,776 @@ +// dear imgui: Platform Backend for SDL3 +// This needs to be used along with a Renderer (e.g. SDL_GPU, DirectX11, OpenGL3, Vulkan..) +// (Info: SDL3 is a cross-platform general purpose library for handling windows, inputs, graphics context creation, etc.) + +// Implemented features: +// [X] Platform: Clipboard support. +// [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen. +// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy SDL_SCANCODE_* values are obsolete since 1.87 and not supported since 1.91.5] +// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. +// [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. +// [X] Platform: IME support. + +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. +// Learn about Dear ImGui: +// - FAQ https://dearimgui.com/faq +// - Getting Started https://dearimgui.com/getting-started +// - Documentation https://dearimgui.com/docs (same as your local docs/ folder). +// - Introduction, links and more at the top of imgui.cpp + +// CHANGELOG +// (minor and older changes stripped away, please see git history for details) +// 2025-01-20: Made ImGui_ImplSDL3_SetGamepadMode(ImGui_ImplSDL3_GamepadMode_Manual) accept an empty array. +// 2024-10-24: Emscripten: SDL_EVENT_MOUSE_WHEEL event doesn't require dividing by 100.0f on Emscripten. +// 2024-09-03: Update for SDL3 api changes: SDL_GetGamepads() memory ownership revert. (#7918, #7898, #7807) +// 2024-08-22: moved some OS/backend related function pointers from ImGuiIO to ImGuiPlatformIO: +// - io.GetClipboardTextFn -> platform_io.Platform_GetClipboardTextFn +// - io.SetClipboardTextFn -> platform_io.Platform_SetClipboardTextFn +// - io.PlatformSetImeDataFn -> platform_io.Platform_SetImeDataFn +// 2024-08-19: Storing SDL_WindowID inside ImGuiViewport::PlatformHandle instead of SDL_Window*. +// 2024-08-19: ImGui_ImplSDL3_ProcessEvent() now ignores events intended for other SDL windows. (#7853) +// 2024-07-22: Update for SDL3 api changes: SDL_GetGamepads() memory ownership change. (#7807) +// 2024-07-18: Update for SDL3 api changes: SDL_GetClipboardText() memory ownership change. (#7801) +// 2024-07-15: Update for SDL3 api changes: SDL_GetProperty() change to SDL_GetPointerProperty(). (#7794) +// 2024-07-02: Update for SDL3 api changes: SDLK_x renames and SDLK_KP_x removals (#7761, #7762). +// 2024-07-01: Update for SDL3 api changes: SDL_SetTextInputRect() changed to SDL_SetTextInputArea(). +// 2024-06-26: Update for SDL3 api changes: SDL_StartTextInput()/SDL_StopTextInput()/SDL_SetTextInputRect() functions signatures. +// 2024-06-24: Update for SDL3 api changes: SDL_EVENT_KEY_DOWN/SDL_EVENT_KEY_UP contents. +// 2024-06-03; Update for SDL3 api changes: SDL_SYSTEM_CURSOR_ renames. +// 2024-05-15: Update for SDL3 api changes: SDLK_ renames. +// 2024-04-15: Inputs: Re-enable calling SDL_StartTextInput()/SDL_StopTextInput() as SDL3 no longer enables it by default and should play nicer with IME. +// 2024-02-13: Inputs: Fixed gamepad support. Handle gamepad disconnection. Added ImGui_ImplSDL3_SetGamepadMode(). +// 2023-11-13: Updated for recent SDL3 API changes. +// 2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F24 function keys, app back/forward keys. +// 2023-05-04: Fixed build on Emscripten/iOS/Android. (#6391) +// 2023-04-06: Inputs: Avoid calling SDL_StartTextInput()/SDL_StopTextInput() as they don't only pertain to IME. It's unclear exactly what their relation is to IME. (#6306) +// 2023-04-04: Inputs: Added support for io.AddMouseSourceEvent() to discriminate ImGuiMouseSource_Mouse/ImGuiMouseSource_TouchScreen. (#2702) +// 2023-02-23: Accept SDL_GetPerformanceCounter() not returning a monotonically increasing value. (#6189, #6114, #3644) +// 2023-02-07: Forked "imgui_impl_sdl2" into "imgui_impl_sdl3". Removed version checks for old feature. Refer to imgui_impl_sdl2.cpp for older changelog. + +#include "imgui/imgui.h" +#ifndef IMGUI_DISABLE +#include "imgui_impl_sdl3.h" + +// Clang warnings with -Weverything +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision +#endif + +// SDL +#include +#if defined(__APPLE__) +#include +#endif +#ifdef _WIN32 +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#endif + +#if !defined(__EMSCRIPTEN__) && !defined(__ANDROID__) && !(defined(__APPLE__) && TARGET_OS_IOS) && !defined(__amigaos4__) +#define SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE 1 +#else +#define SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE 0 +#endif + +// FIXME-LEGACY: remove when SDL 3.1.3 preview is released. +#ifndef SDLK_APOSTROPHE +#define SDLK_APOSTROPHE SDLK_QUOTE +#endif +#ifndef SDLK_GRAVE +#define SDLK_GRAVE SDLK_BACKQUOTE +#endif + +// SDL Data +struct ImGui_ImplSDL3_Data +{ + SDL_Window* Window; + SDL_WindowID WindowID; + SDL_Renderer* Renderer; + Uint64 Time; + char* ClipboardTextData; + + // IME handling + SDL_Window* ImeWindow; + + // Mouse handling + Uint32 MouseWindowID; + int MouseButtonsDown; + SDL_Cursor* MouseCursors[ImGuiMouseCursor_COUNT]; + SDL_Cursor* MouseLastCursor; + int MousePendingLeaveFrame; + bool MouseCanUseGlobalState; + + // Gamepad handling + ImVector Gamepads; + ImGui_ImplSDL3_GamepadMode GamepadMode; + bool WantUpdateGamepadsList; + + ImGui_ImplSDL3_Data() { memset((void*)this, 0, sizeof(*this)); } +}; + +// Backend data stored in io.BackendPlatformUserData to allow support for multiple Dear ImGui contexts +// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts. +// FIXME: multi-context support is not well tested and probably dysfunctional in this backend. +// FIXME: some shared resources (mouse cursor shape, gamepad) are mishandled when using multi-context. +static ImGui_ImplSDL3_Data* ImGui_ImplSDL3_GetBackendData() +{ + return ImGui::GetCurrentContext() ? (ImGui_ImplSDL3_Data*)ImGui::GetIO().BackendPlatformUserData : nullptr; +} + +// Functions +static const char* ImGui_ImplSDL3_GetClipboardText(ImGuiContext*) +{ + ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); + if (bd->ClipboardTextData) + SDL_free(bd->ClipboardTextData); + const char* sdl_clipboard_text = SDL_GetClipboardText(); + bd->ClipboardTextData = sdl_clipboard_text ? SDL_strdup(sdl_clipboard_text) : nullptr; + return bd->ClipboardTextData; +} + +static void ImGui_ImplSDL3_SetClipboardText(ImGuiContext*, const char* text) +{ + SDL_SetClipboardText(text); +} + +static void ImGui_ImplSDL3_PlatformSetImeData(ImGuiContext*, ImGuiViewport* viewport, ImGuiPlatformImeData* data) +{ + ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); + SDL_WindowID window_id = (SDL_WindowID)(intptr_t)viewport->PlatformHandle; + SDL_Window* window = SDL_GetWindowFromID(window_id); + if ((data->WantVisible == false || bd->ImeWindow != window) && bd->ImeWindow != nullptr) + { + SDL_StopTextInput(bd->ImeWindow); + bd->ImeWindow = nullptr; + } + if (data->WantVisible) + { + SDL_Rect r; + r.x = (int)data->InputPos.x; + r.y = (int)data->InputPos.y; + r.w = 1; + r.h = (int)data->InputLineHeight; + SDL_SetTextInputArea(window, &r, 0); + SDL_StartTextInput(window); + bd->ImeWindow = window; + } +} + +// Not static to allow third-party code to use that if they want to (but undocumented) +ImGuiKey ImGui_ImplSDL3_KeyEventToImGuiKey(SDL_Keycode keycode, SDL_Scancode scancode); +ImGuiKey ImGui_ImplSDL3_KeyEventToImGuiKey(SDL_Keycode keycode, SDL_Scancode scancode) +{ + // Keypad doesn't have individual key values in SDL3 + switch (scancode) + { + case SDL_SCANCODE_KP_0: return ImGuiKey_Keypad0; + case SDL_SCANCODE_KP_1: return ImGuiKey_Keypad1; + case SDL_SCANCODE_KP_2: return ImGuiKey_Keypad2; + case SDL_SCANCODE_KP_3: return ImGuiKey_Keypad3; + case SDL_SCANCODE_KP_4: return ImGuiKey_Keypad4; + case SDL_SCANCODE_KP_5: return ImGuiKey_Keypad5; + case SDL_SCANCODE_KP_6: return ImGuiKey_Keypad6; + case SDL_SCANCODE_KP_7: return ImGuiKey_Keypad7; + case SDL_SCANCODE_KP_8: return ImGuiKey_Keypad8; + case SDL_SCANCODE_KP_9: return ImGuiKey_Keypad9; + case SDL_SCANCODE_KP_PERIOD: return ImGuiKey_KeypadDecimal; + case SDL_SCANCODE_KP_DIVIDE: return ImGuiKey_KeypadDivide; + case SDL_SCANCODE_KP_MULTIPLY: return ImGuiKey_KeypadMultiply; + case SDL_SCANCODE_KP_MINUS: return ImGuiKey_KeypadSubtract; + case SDL_SCANCODE_KP_PLUS: return ImGuiKey_KeypadAdd; + case SDL_SCANCODE_KP_ENTER: return ImGuiKey_KeypadEnter; + case SDL_SCANCODE_KP_EQUALS: return ImGuiKey_KeypadEqual; + default: break; + } + switch (keycode) + { + case SDLK_TAB: return ImGuiKey_Tab; + case SDLK_LEFT: return ImGuiKey_LeftArrow; + case SDLK_RIGHT: return ImGuiKey_RightArrow; + case SDLK_UP: return ImGuiKey_UpArrow; + case SDLK_DOWN: return ImGuiKey_DownArrow; + case SDLK_PAGEUP: return ImGuiKey_PageUp; + case SDLK_PAGEDOWN: return ImGuiKey_PageDown; + case SDLK_HOME: return ImGuiKey_Home; + case SDLK_END: return ImGuiKey_End; + case SDLK_INSERT: return ImGuiKey_Insert; + case SDLK_DELETE: return ImGuiKey_Delete; + case SDLK_BACKSPACE: return ImGuiKey_Backspace; + case SDLK_SPACE: return ImGuiKey_Space; + case SDLK_RETURN: return ImGuiKey_Enter; + case SDLK_ESCAPE: return ImGuiKey_Escape; + case SDLK_APOSTROPHE: return ImGuiKey_Apostrophe; + case SDLK_COMMA: return ImGuiKey_Comma; + case SDLK_MINUS: return ImGuiKey_Minus; + case SDLK_PERIOD: return ImGuiKey_Period; + case SDLK_SLASH: return ImGuiKey_Slash; + case SDLK_SEMICOLON: return ImGuiKey_Semicolon; + case SDLK_EQUALS: return ImGuiKey_Equal; + case SDLK_LEFTBRACKET: return ImGuiKey_LeftBracket; + case SDLK_BACKSLASH: return ImGuiKey_Backslash; + case SDLK_RIGHTBRACKET: return ImGuiKey_RightBracket; + case SDLK_GRAVE: return ImGuiKey_GraveAccent; + case SDLK_CAPSLOCK: return ImGuiKey_CapsLock; + case SDLK_SCROLLLOCK: return ImGuiKey_ScrollLock; + case SDLK_NUMLOCKCLEAR: return ImGuiKey_NumLock; + case SDLK_PRINTSCREEN: return ImGuiKey_PrintScreen; + case SDLK_PAUSE: return ImGuiKey_Pause; + case SDLK_LCTRL: return ImGuiKey_LeftCtrl; + case SDLK_LSHIFT: return ImGuiKey_LeftShift; + case SDLK_LALT: return ImGuiKey_LeftAlt; + case SDLK_LGUI: return ImGuiKey_LeftSuper; + case SDLK_RCTRL: return ImGuiKey_RightCtrl; + case SDLK_RSHIFT: return ImGuiKey_RightShift; + case SDLK_RALT: return ImGuiKey_RightAlt; + case SDLK_RGUI: return ImGuiKey_RightSuper; + case SDLK_APPLICATION: return ImGuiKey_Menu; + case SDLK_0: return ImGuiKey_0; + case SDLK_1: return ImGuiKey_1; + case SDLK_2: return ImGuiKey_2; + case SDLK_3: return ImGuiKey_3; + case SDLK_4: return ImGuiKey_4; + case SDLK_5: return ImGuiKey_5; + case SDLK_6: return ImGuiKey_6; + case SDLK_7: return ImGuiKey_7; + case SDLK_8: return ImGuiKey_8; + case SDLK_9: return ImGuiKey_9; + case SDLK_A: return ImGuiKey_A; + case SDLK_B: return ImGuiKey_B; + case SDLK_C: return ImGuiKey_C; + case SDLK_D: return ImGuiKey_D; + case SDLK_E: return ImGuiKey_E; + case SDLK_F: return ImGuiKey_F; + case SDLK_G: return ImGuiKey_G; + case SDLK_H: return ImGuiKey_H; + case SDLK_I: return ImGuiKey_I; + case SDLK_J: return ImGuiKey_J; + case SDLK_K: return ImGuiKey_K; + case SDLK_L: return ImGuiKey_L; + case SDLK_M: return ImGuiKey_M; + case SDLK_N: return ImGuiKey_N; + case SDLK_O: return ImGuiKey_O; + case SDLK_P: return ImGuiKey_P; + case SDLK_Q: return ImGuiKey_Q; + case SDLK_R: return ImGuiKey_R; + case SDLK_S: return ImGuiKey_S; + case SDLK_T: return ImGuiKey_T; + case SDLK_U: return ImGuiKey_U; + case SDLK_V: return ImGuiKey_V; + case SDLK_W: return ImGuiKey_W; + case SDLK_X: return ImGuiKey_X; + case SDLK_Y: return ImGuiKey_Y; + case SDLK_Z: return ImGuiKey_Z; + case SDLK_F1: return ImGuiKey_F1; + case SDLK_F2: return ImGuiKey_F2; + case SDLK_F3: return ImGuiKey_F3; + case SDLK_F4: return ImGuiKey_F4; + case SDLK_F5: return ImGuiKey_F5; + case SDLK_F6: return ImGuiKey_F6; + case SDLK_F7: return ImGuiKey_F7; + case SDLK_F8: return ImGuiKey_F8; + case SDLK_F9: return ImGuiKey_F9; + case SDLK_F10: return ImGuiKey_F10; + case SDLK_F11: return ImGuiKey_F11; + case SDLK_F12: return ImGuiKey_F12; + case SDLK_F13: return ImGuiKey_F13; + case SDLK_F14: return ImGuiKey_F14; + case SDLK_F15: return ImGuiKey_F15; + case SDLK_F16: return ImGuiKey_F16; + case SDLK_F17: return ImGuiKey_F17; + case SDLK_F18: return ImGuiKey_F18; + case SDLK_F19: return ImGuiKey_F19; + case SDLK_F20: return ImGuiKey_F20; + case SDLK_F21: return ImGuiKey_F21; + case SDLK_F22: return ImGuiKey_F22; + case SDLK_F23: return ImGuiKey_F23; + case SDLK_F24: return ImGuiKey_F24; + case SDLK_AC_BACK: return ImGuiKey_AppBack; + case SDLK_AC_FORWARD: return ImGuiKey_AppForward; + default: break; + } + return ImGuiKey_None; +} + +static void ImGui_ImplSDL3_UpdateKeyModifiers(SDL_Keymod sdl_key_mods) +{ + ImGuiIO& io = ImGui::GetIO(); + io.AddKeyEvent(ImGuiMod_Ctrl, (sdl_key_mods & SDL_KMOD_CTRL) != 0); + io.AddKeyEvent(ImGuiMod_Shift, (sdl_key_mods & SDL_KMOD_SHIFT) != 0); + io.AddKeyEvent(ImGuiMod_Alt, (sdl_key_mods & SDL_KMOD_ALT) != 0); + io.AddKeyEvent(ImGuiMod_Super, (sdl_key_mods & SDL_KMOD_GUI) != 0); +} + + +static ImGuiViewport* ImGui_ImplSDL3_GetViewportForWindowID(SDL_WindowID window_id) +{ + ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); + return (window_id == bd->WindowID) ? ImGui::GetMainViewport() : nullptr; +} + +// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. +// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data. +// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data. +// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. +// If you have multiple SDL events and some of them are not meant to be used by dear imgui, you may need to filter events based on their windowID field. +bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event) +{ + ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); + IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplSDL3_Init()?"); + ImGuiIO& io = ImGui::GetIO(); + + switch (event->type) + { + case SDL_EVENT_MOUSE_MOTION: + { + if (ImGui_ImplSDL3_GetViewportForWindowID(event->motion.windowID) == nullptr) + return false; + ImVec2 mouse_pos((float)event->motion.x, (float)event->motion.y); + io.AddMouseSourceEvent(event->motion.which == SDL_TOUCH_MOUSEID ? ImGuiMouseSource_TouchScreen : ImGuiMouseSource_Mouse); + io.AddMousePosEvent(mouse_pos.x, mouse_pos.y); + return true; + } + case SDL_EVENT_MOUSE_WHEEL: + { + if (ImGui_ImplSDL3_GetViewportForWindowID(event->wheel.windowID) == nullptr) + return false; + //IMGUI_DEBUG_LOG("wheel %.2f %.2f, precise %.2f %.2f\n", (float)event->wheel.x, (float)event->wheel.y, event->wheel.preciseX, event->wheel.preciseY); + float wheel_x = -event->wheel.x; + float wheel_y = event->wheel.y; + io.AddMouseSourceEvent(event->wheel.which == SDL_TOUCH_MOUSEID ? ImGuiMouseSource_TouchScreen : ImGuiMouseSource_Mouse); + io.AddMouseWheelEvent(wheel_x, wheel_y); + return true; + } + case SDL_EVENT_MOUSE_BUTTON_DOWN: + case SDL_EVENT_MOUSE_BUTTON_UP: + { + if (ImGui_ImplSDL3_GetViewportForWindowID(event->button.windowID) == nullptr) + return false; + int mouse_button = -1; + if (event->button.button == SDL_BUTTON_LEFT) { mouse_button = 0; } + if (event->button.button == SDL_BUTTON_RIGHT) { mouse_button = 1; } + if (event->button.button == SDL_BUTTON_MIDDLE) { mouse_button = 2; } + if (event->button.button == SDL_BUTTON_X1) { mouse_button = 3; } + if (event->button.button == SDL_BUTTON_X2) { mouse_button = 4; } + if (mouse_button == -1) + break; + io.AddMouseSourceEvent(event->button.which == SDL_TOUCH_MOUSEID ? ImGuiMouseSource_TouchScreen : ImGuiMouseSource_Mouse); + io.AddMouseButtonEvent(mouse_button, (event->type == SDL_EVENT_MOUSE_BUTTON_DOWN)); + bd->MouseButtonsDown = (event->type == SDL_EVENT_MOUSE_BUTTON_DOWN) ? (bd->MouseButtonsDown | (1 << mouse_button)) : (bd->MouseButtonsDown & ~(1 << mouse_button)); + return true; + } + case SDL_EVENT_TEXT_INPUT: + { + if (ImGui_ImplSDL3_GetViewportForWindowID(event->text.windowID) == nullptr) + return false; + io.AddInputCharactersUTF8(event->text.text); + return true; + } + case SDL_EVENT_KEY_DOWN: + case SDL_EVENT_KEY_UP: + { + if (ImGui_ImplSDL3_GetViewportForWindowID(event->key.windowID) == nullptr) + return false; + ImGui_ImplSDL3_UpdateKeyModifiers((SDL_Keymod)event->key.mod); + //IMGUI_DEBUG_LOG("SDL_EVENT_KEY_%s : key=%d ('%s'), scancode=%d ('%s'), mod=%X\n", + // (event->type == SDL_EVENT_KEY_DOWN) ? "DOWN" : "UP ", event->key.key, SDL_GetKeyName(event->key.key), event->key.scancode, SDL_GetScancodeName(event->key.scancode), event->key.mod); + ImGuiKey key = ImGui_ImplSDL3_KeyEventToImGuiKey(event->key.key, event->key.scancode); + io.AddKeyEvent(key, (event->type == SDL_EVENT_KEY_DOWN)); + io.SetKeyEventNativeData(key, event->key.key, event->key.scancode, event->key.scancode); // To support legacy indexing (<1.87 user code). Legacy backend uses SDLK_*** as indices to IsKeyXXX() functions. + return true; + } + case SDL_EVENT_WINDOW_MOUSE_ENTER: + { + if (ImGui_ImplSDL3_GetViewportForWindowID(event->window.windowID) == nullptr) + return false; + bd->MouseWindowID = event->window.windowID; + bd->MousePendingLeaveFrame = 0; + return true; + } + // - In some cases, when detaching a window from main viewport SDL may send SDL_WINDOWEVENT_ENTER one frame too late, + // causing SDL_WINDOWEVENT_LEAVE on previous frame to interrupt drag operation by clear mouse position. This is why + // we delay process the SDL_WINDOWEVENT_LEAVE events by one frame. See issue #5012 for details. + // FIXME: Unconfirmed whether this is still needed with SDL3. + case SDL_EVENT_WINDOW_MOUSE_LEAVE: + { + if (ImGui_ImplSDL3_GetViewportForWindowID(event->window.windowID) == nullptr) + return false; + bd->MousePendingLeaveFrame = ImGui::GetFrameCount() + 1; + return true; + } + case SDL_EVENT_WINDOW_FOCUS_GAINED: + case SDL_EVENT_WINDOW_FOCUS_LOST: + { + if (ImGui_ImplSDL3_GetViewportForWindowID(event->window.windowID) == nullptr) + return false; + io.AddFocusEvent(event->type == SDL_EVENT_WINDOW_FOCUS_GAINED); + return true; + } + case SDL_EVENT_GAMEPAD_ADDED: + case SDL_EVENT_GAMEPAD_REMOVED: + { + bd->WantUpdateGamepadsList = true; + return true; + } + } + return false; +} + +static void ImGui_ImplSDL3_SetupPlatformHandles(ImGuiViewport* viewport, SDL_Window* window) +{ + viewport->PlatformHandle = (void*)(intptr_t)SDL_GetWindowID(window); + viewport->PlatformHandleRaw = nullptr; +#if defined(_WIN32) && !defined(__WINRT__) + viewport->PlatformHandleRaw = (HWND)SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_WIN32_HWND_POINTER, nullptr); +#elif defined(__APPLE__) && defined(SDL_VIDEO_DRIVER_COCOA) + viewport->PlatformHandleRaw = SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_COCOA_WINDOW_POINTER, nullptr); +#endif +} + +static bool ImGui_ImplSDL3_Init(SDL_Window* window, SDL_Renderer* renderer, void* sdl_gl_context) +{ + ImGuiIO& io = ImGui::GetIO(); + IMGUI_CHECKVERSION(); + IM_ASSERT(io.BackendPlatformUserData == nullptr && "Already initialized a platform backend!"); + IM_UNUSED(sdl_gl_context); // Unused in this branch + + // Check and store if we are on a SDL backend that supports global mouse position + // ("wayland" and "rpi" don't support it, but we chose to use a white-list instead of a black-list) + bool mouse_can_use_global_state = false; +#if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE + const char* sdl_backend = SDL_GetCurrentVideoDriver(); + const char* global_mouse_whitelist[] = { "windows", "cocoa", "x11", "DIVE", "VMAN" }; + for (int n = 0; n < IM_ARRAYSIZE(global_mouse_whitelist); n++) + if (strncmp(sdl_backend, global_mouse_whitelist[n], strlen(global_mouse_whitelist[n])) == 0) + mouse_can_use_global_state = true; +#endif + + // Setup backend capabilities flags + ImGui_ImplSDL3_Data* bd = IM_NEW(ImGui_ImplSDL3_Data)(); + io.BackendPlatformUserData = (void*)bd; + io.BackendPlatformName = "imgui_impl_sdl3"; + io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) + io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) + + bd->Window = window; + bd->WindowID = SDL_GetWindowID(window); + bd->Renderer = renderer; + bd->MouseCanUseGlobalState = mouse_can_use_global_state; + + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + platform_io.Platform_SetClipboardTextFn = ImGui_ImplSDL3_SetClipboardText; + platform_io.Platform_GetClipboardTextFn = ImGui_ImplSDL3_GetClipboardText; + platform_io.Platform_SetImeDataFn = ImGui_ImplSDL3_PlatformSetImeData; + + // Gamepad handling + bd->GamepadMode = ImGui_ImplSDL3_GamepadMode_AutoFirst; + bd->WantUpdateGamepadsList = true; + + // Load mouse cursors + bd->MouseCursors[ImGuiMouseCursor_Arrow] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_DEFAULT); + bd->MouseCursors[ImGuiMouseCursor_TextInput] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_TEXT); + bd->MouseCursors[ImGuiMouseCursor_ResizeAll] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_MOVE); + bd->MouseCursors[ImGuiMouseCursor_ResizeNS] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NS_RESIZE); + bd->MouseCursors[ImGuiMouseCursor_ResizeEW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_EW_RESIZE); + bd->MouseCursors[ImGuiMouseCursor_ResizeNESW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NESW_RESIZE); + bd->MouseCursors[ImGuiMouseCursor_ResizeNWSE] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NWSE_RESIZE); + bd->MouseCursors[ImGuiMouseCursor_Hand] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_POINTER); + bd->MouseCursors[ImGuiMouseCursor_NotAllowed] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NOT_ALLOWED); + + // Set platform dependent data in viewport + // Our mouse update function expect PlatformHandle to be filled for the main viewport + ImGuiViewport* main_viewport = ImGui::GetMainViewport(); + ImGui_ImplSDL3_SetupPlatformHandles(main_viewport, window); + + // From 2.0.5: Set SDL hint to receive mouse click events on window focus, otherwise SDL doesn't emit the event. + // Without this, when clicking to gain focus, our widgets wouldn't activate even though they showed as hovered. + // (This is unfortunately a global SDL setting, so enabling it might have a side-effect on your application. + // It is unlikely to make a difference, but if your app absolutely needs to ignore the initial on-focus click: + // you can ignore SDL_EVENT_MOUSE_BUTTON_DOWN events coming right after a SDL_WINDOWEVENT_FOCUS_GAINED) +#ifdef SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH + SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1"); +#endif + + // From 2.0.22: Disable auto-capture, this is preventing drag and drop across multiple windows (see #5710) +#ifdef SDL_HINT_MOUSE_AUTO_CAPTURE + SDL_SetHint(SDL_HINT_MOUSE_AUTO_CAPTURE, "0"); +#endif + + return true; +} + +bool ImGui_ImplSDL3_InitForOpenGL(SDL_Window* window, void* sdl_gl_context) +{ + IM_UNUSED(sdl_gl_context); // Viewport branch will need this. + return ImGui_ImplSDL3_Init(window, nullptr, sdl_gl_context); +} + +bool ImGui_ImplSDL3_InitForVulkan(SDL_Window* window) +{ + return ImGui_ImplSDL3_Init(window, nullptr, nullptr); +} + +bool ImGui_ImplSDL3_InitForD3D(SDL_Window* window) +{ +#if !defined(_WIN32) + IM_ASSERT(0 && "Unsupported"); +#endif + return ImGui_ImplSDL3_Init(window, nullptr, nullptr); +} + +bool ImGui_ImplSDL3_InitForMetal(SDL_Window* window) +{ + return ImGui_ImplSDL3_Init(window, nullptr, nullptr); +} + +bool ImGui_ImplSDL3_InitForSDLRenderer(SDL_Window* window, SDL_Renderer* renderer) +{ + return ImGui_ImplSDL3_Init(window, renderer, nullptr); +} + +bool ImGui_ImplSDL3_InitForSDLGPU(SDL_Window* window) +{ + return ImGui_ImplSDL3_Init(window, nullptr, nullptr); +} + +bool ImGui_ImplSDL3_InitForOther(SDL_Window* window) +{ + return ImGui_ImplSDL3_Init(window, nullptr, nullptr); +} + +static void ImGui_ImplSDL3_CloseGamepads(); + +void ImGui_ImplSDL3_Shutdown() +{ + ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); + IM_ASSERT(bd != nullptr && "No platform backend to shutdown, or already shutdown?"); + ImGuiIO& io = ImGui::GetIO(); + + if (bd->ClipboardTextData) + SDL_free(bd->ClipboardTextData); + for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++) + SDL_DestroyCursor(bd->MouseCursors[cursor_n]); + ImGui_ImplSDL3_CloseGamepads(); + + io.BackendPlatformName = nullptr; + io.BackendPlatformUserData = nullptr; + io.BackendFlags &= ~(ImGuiBackendFlags_HasMouseCursors | ImGuiBackendFlags_HasSetMousePos | ImGuiBackendFlags_HasGamepad); + IM_DELETE(bd); +} + +static void ImGui_ImplSDL3_UpdateMouseData() +{ + ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); + ImGuiIO& io = ImGui::GetIO(); + + // We forward mouse input when hovered or captured (via SDL_EVENT_MOUSE_MOTION) or when focused (below) +#if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE + // SDL_CaptureMouse() let the OS know e.g. that our imgui drag outside the SDL window boundaries shouldn't e.g. trigger other operations outside + SDL_CaptureMouse(bd->MouseButtonsDown != 0); + SDL_Window* focused_window = SDL_GetKeyboardFocus(); + const bool is_app_focused = (bd->Window == focused_window); +#else + SDL_Window* focused_window = bd->Window; + const bool is_app_focused = (SDL_GetWindowFlags(bd->Window) & SDL_WINDOW_INPUT_FOCUS) != 0; // SDL 2.0.3 and non-windowed systems: single-viewport only +#endif + if (is_app_focused) + { + // (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when io.ConfigNavMoveSetMousePos is enabled by user) + if (io.WantSetMousePos) + SDL_WarpMouseInWindow(bd->Window, io.MousePos.x, io.MousePos.y); + + // (Optional) Fallback to provide mouse position when focused (SDL_EVENT_MOUSE_MOTION already provides this when hovered or captured) + if (bd->MouseCanUseGlobalState && bd->MouseButtonsDown == 0) + { + // Single-viewport mode: mouse position in client window coordinates (io.MousePos is (0,0) when the mouse is on the upper-left corner of the app window) + float mouse_x_global, mouse_y_global; + int window_x, window_y; + SDL_GetGlobalMouseState(&mouse_x_global, &mouse_y_global); + SDL_GetWindowPosition(focused_window, &window_x, &window_y); + io.AddMousePosEvent(mouse_x_global - window_x, mouse_y_global - window_y); + } + } +} + +static void ImGui_ImplSDL3_UpdateMouseCursor() +{ + ImGuiIO& io = ImGui::GetIO(); + if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) + return; + ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); + + ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor(); + if (io.MouseDrawCursor || imgui_cursor == ImGuiMouseCursor_None) + { + // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor + SDL_HideCursor(); + } + else + { + // Show OS mouse cursor + SDL_Cursor* expected_cursor = bd->MouseCursors[imgui_cursor] ? bd->MouseCursors[imgui_cursor] : bd->MouseCursors[ImGuiMouseCursor_Arrow]; + if (bd->MouseLastCursor != expected_cursor) + { + SDL_SetCursor(expected_cursor); // SDL function doesn't have an early out (see #6113) + bd->MouseLastCursor = expected_cursor; + } + SDL_ShowCursor(); + } +} + +static void ImGui_ImplSDL3_CloseGamepads() +{ + ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); + if (bd->GamepadMode != ImGui_ImplSDL3_GamepadMode_Manual) + for (SDL_Gamepad* gamepad : bd->Gamepads) + SDL_CloseGamepad(gamepad); + bd->Gamepads.resize(0); +} + +void ImGui_ImplSDL3_SetGamepadMode(ImGui_ImplSDL3_GamepadMode mode, SDL_Gamepad** manual_gamepads_array, int manual_gamepads_count) +{ + ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); + ImGui_ImplSDL3_CloseGamepads(); + if (mode == ImGui_ImplSDL3_GamepadMode_Manual) + { + IM_ASSERT(manual_gamepads_array != nullptr || manual_gamepads_count <= 0); + for (int n = 0; n < manual_gamepads_count; n++) + bd->Gamepads.push_back(manual_gamepads_array[n]); + } + else + { + IM_ASSERT(manual_gamepads_array == nullptr && manual_gamepads_count <= 0); + bd->WantUpdateGamepadsList = true; + } + bd->GamepadMode = mode; +} + +static void ImGui_ImplSDL3_UpdateGamepadButton(ImGui_ImplSDL3_Data* bd, ImGuiIO& io, ImGuiKey key, SDL_GamepadButton button_no) +{ + bool merged_value = false; + for (SDL_Gamepad* gamepad : bd->Gamepads) + merged_value |= SDL_GetGamepadButton(gamepad, button_no) != 0; + io.AddKeyEvent(key, merged_value); +} + +static inline float Saturate(float v) { return v < 0.0f ? 0.0f : v > 1.0f ? 1.0f : v; } +static void ImGui_ImplSDL3_UpdateGamepadAnalog(ImGui_ImplSDL3_Data* bd, ImGuiIO& io, ImGuiKey key, SDL_GamepadAxis axis_no, float v0, float v1) +{ + float merged_value = 0.0f; + for (SDL_Gamepad* gamepad : bd->Gamepads) + { + float vn = Saturate((float)(SDL_GetGamepadAxis(gamepad, axis_no) - v0) / (float)(v1 - v0)); + if (merged_value < vn) + merged_value = vn; + } + io.AddKeyAnalogEvent(key, merged_value > 0.1f, merged_value); +} + +static void ImGui_ImplSDL3_UpdateGamepads() +{ + ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); + + // Update list of gamepads to use + if (bd->WantUpdateGamepadsList && bd->GamepadMode != ImGui_ImplSDL3_GamepadMode_Manual) + { + ImGui_ImplSDL3_CloseGamepads(); + int sdl_gamepads_count = 0; + SDL_JoystickID* sdl_gamepads = SDL_GetGamepads(&sdl_gamepads_count); + for (int n = 0; n < sdl_gamepads_count; n++) + if (SDL_Gamepad* gamepad = SDL_OpenGamepad(sdl_gamepads[n])) + { + bd->Gamepads.push_back(gamepad); + if (bd->GamepadMode == ImGui_ImplSDL3_GamepadMode_AutoFirst) + break; + } + bd->WantUpdateGamepadsList = false; + SDL_free(sdl_gamepads); + } + + // FIXME: Technically feeding gamepad shouldn't depend on this now that they are regular inputs. + if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) + return; + io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad; + if (bd->Gamepads.Size == 0) + return; + io.BackendFlags |= ImGuiBackendFlags_HasGamepad; + + // Update gamepad inputs + const int thumb_dead_zone = 8000; // SDL_gamepad.h suggests using this value. + ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadStart, SDL_GAMEPAD_BUTTON_START); + ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadBack, SDL_GAMEPAD_BUTTON_BACK); + ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadFaceLeft, SDL_GAMEPAD_BUTTON_WEST); // Xbox X, PS Square + ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadFaceRight, SDL_GAMEPAD_BUTTON_EAST); // Xbox B, PS Circle + ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadFaceUp, SDL_GAMEPAD_BUTTON_NORTH); // Xbox Y, PS Triangle + ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadFaceDown, SDL_GAMEPAD_BUTTON_SOUTH); // Xbox A, PS Cross + ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadDpadLeft, SDL_GAMEPAD_BUTTON_DPAD_LEFT); + ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadDpadRight, SDL_GAMEPAD_BUTTON_DPAD_RIGHT); + ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadDpadUp, SDL_GAMEPAD_BUTTON_DPAD_UP); + ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadDpadDown, SDL_GAMEPAD_BUTTON_DPAD_DOWN); + ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadL1, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER); + ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadR1, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER); + ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadL2, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, 0.0f, 32767); + ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadR2, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, 0.0f, 32767); + ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadL3, SDL_GAMEPAD_BUTTON_LEFT_STICK); + ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadR3, SDL_GAMEPAD_BUTTON_RIGHT_STICK); + ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadLStickLeft, SDL_GAMEPAD_AXIS_LEFTX, -thumb_dead_zone, -32768); + ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadLStickRight, SDL_GAMEPAD_AXIS_LEFTX, +thumb_dead_zone, +32767); + ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadLStickUp, SDL_GAMEPAD_AXIS_LEFTY, -thumb_dead_zone, -32768); + ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadLStickDown, SDL_GAMEPAD_AXIS_LEFTY, +thumb_dead_zone, +32767); + ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadRStickLeft, SDL_GAMEPAD_AXIS_RIGHTX, -thumb_dead_zone, -32768); + ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadRStickRight, SDL_GAMEPAD_AXIS_RIGHTX, +thumb_dead_zone, +32767); + ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadRStickUp, SDL_GAMEPAD_AXIS_RIGHTY, -thumb_dead_zone, -32768); + ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadRStickDown, SDL_GAMEPAD_AXIS_RIGHTY, +thumb_dead_zone, +32767); +} + +void ImGui_ImplSDL3_NewFrame() +{ + ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); + IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplSDL3_Init()?"); + ImGuiIO& io = ImGui::GetIO(); + + // Setup display size (every frame to accommodate for window resizing) + int w, h; + int display_w, display_h; + SDL_GetWindowSize(bd->Window, &w, &h); + if (SDL_GetWindowFlags(bd->Window) & SDL_WINDOW_MINIMIZED) + w = h = 0; + SDL_GetWindowSizeInPixels(bd->Window, &display_w, &display_h); + io.DisplaySize = ImVec2((float)w, (float)h); + if (w > 0 && h > 0) + io.DisplayFramebufferScale = ImVec2((float)display_w / w, (float)display_h / h); + + // Setup time step (we don't use SDL_GetTicks() because it is using millisecond resolution) + // (Accept SDL_GetPerformanceCounter() not returning a monotonically increasing value. Happens in VMs and Emscripten, see #6189, #6114, #3644) + static Uint64 frequency = SDL_GetPerformanceFrequency(); + Uint64 current_time = SDL_GetPerformanceCounter(); + if (current_time <= bd->Time) + current_time = bd->Time + 1; + io.DeltaTime = bd->Time > 0 ? (float)((double)(current_time - bd->Time) / frequency) : (float)(1.0f / 60.0f); + bd->Time = current_time; + + if (bd->MousePendingLeaveFrame && bd->MousePendingLeaveFrame >= ImGui::GetFrameCount() && bd->MouseButtonsDown == 0) + { + bd->MouseWindowID = 0; + bd->MousePendingLeaveFrame = 0; + io.AddMousePosEvent(-FLT_MAX, -FLT_MAX); + } + + ImGui_ImplSDL3_UpdateMouseData(); + ImGui_ImplSDL3_UpdateMouseCursor(); + + // Update game controllers (if enabled and available) + ImGui_ImplSDL3_UpdateGamepads(); +} + +//----------------------------------------------------------------------------- + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + +#endif // #ifndef IMGUI_DISABLE diff --git a/source/console/ui/imgui/imgui_impl_sdl3.h b/source/console/ui/imgui/imgui_impl_sdl3.h new file mode 100644 index 0000000..a60f8ed --- /dev/null +++ b/source/console/ui/imgui/imgui_impl_sdl3.h @@ -0,0 +1,47 @@ +// dear imgui: Platform Backend for SDL3 +// This needs to be used along with a Renderer (e.g. SDL_GPU, DirectX11, OpenGL3, Vulkan..) +// (Info: SDL3 is a cross-platform general purpose library for handling windows, inputs, graphics context creation, etc.) + +// Implemented features: +// [X] Platform: Clipboard support. +// [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen. +// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy SDL_SCANCODE_* values are obsolete since 1.87 and not supported since 1.91.5] +// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. +// [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. +// [X] Platform: IME support. + +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. +// Learn about Dear ImGui: +// - FAQ https://dearimgui.com/faq +// - Getting Started https://dearimgui.com/getting-started +// - Documentation https://dearimgui.com/docs (same as your local docs/ folder). +// - Introduction, links and more at the top of imgui.cpp + +#pragma once +#include "imgui/imgui.h" // IMGUI_IMPL_API +#ifndef IMGUI_DISABLE + +struct SDL_Window; +struct SDL_Renderer; +struct SDL_Gamepad; +typedef union SDL_Event SDL_Event; + +// Follow "Getting Started" link and check examples/ folder to learn about using backends! +IMGUI_IMPL_API bool ImGui_ImplSDL3_InitForOpenGL(SDL_Window* window, void* sdl_gl_context); +IMGUI_IMPL_API bool ImGui_ImplSDL3_InitForVulkan(SDL_Window* window); +IMGUI_IMPL_API bool ImGui_ImplSDL3_InitForD3D(SDL_Window* window); +IMGUI_IMPL_API bool ImGui_ImplSDL3_InitForMetal(SDL_Window* window); +IMGUI_IMPL_API bool ImGui_ImplSDL3_InitForSDLRenderer(SDL_Window* window, SDL_Renderer* renderer); +IMGUI_IMPL_API bool ImGui_ImplSDL3_InitForSDLGPU(SDL_Window* window); +IMGUI_IMPL_API bool ImGui_ImplSDL3_InitForOther(SDL_Window* window); +IMGUI_IMPL_API void ImGui_ImplSDL3_Shutdown(); +IMGUI_IMPL_API void ImGui_ImplSDL3_NewFrame(); +IMGUI_IMPL_API bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event); + +// Gamepad selection automatically starts in AutoFirst mode, picking first available SDL_Gamepad. You may override this. +// When using manual mode, caller is responsible for opening/closing gamepad. +enum ImGui_ImplSDL3_GamepadMode { ImGui_ImplSDL3_GamepadMode_AutoFirst, ImGui_ImplSDL3_GamepadMode_AutoAll, ImGui_ImplSDL3_GamepadMode_Manual }; +IMGUI_IMPL_API void ImGui_ImplSDL3_SetGamepadMode(ImGui_ImplSDL3_GamepadMode mode, SDL_Gamepad** manual_gamepads_array = nullptr, int manual_gamepads_count = -1); + +#endif // #ifndef IMGUI_DISABLE diff --git a/source/console/ui/ui_main.cpp b/source/console/ui/ui_main.cpp new file mode 100644 index 0000000..ffc3115 --- /dev/null +++ b/source/console/ui/ui_main.cpp @@ -0,0 +1,130 @@ +#include "ui_main.h" + +#include + +#include +#include "imgui/imgui_impl_sdl3.h" +#include "imgui/imgui_impl_opengl3.h" + +#include "scene/dvu_load_gltf.h" +#include "pipe/dvc_render_queue.h" +#include "device/dvc_render_target.h" +#include "shader/dvc_shader_interface.h" +#include "buffer/dvu_dump_buffer.h" +#include "device/dvc_uniform_buffer.h" +#include "scene/dvc_scene_node.h" +#include "scene/component/dvc_light_component.h" +#include "../adapter/adapter_texture.h" + + +SDL_Window* main_window; + +extern davinci::Scene g_scene; +extern davinci::RenderTarget g_renderTarget; + +bool initMainUI(SDL_Window* window, SDL_GLContext gl_context, const char* glsl_version) +{ + main_window = window; + + // Setup Dear ImGui context + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImGuiIO& io = ImGui::GetIO(); (void)io; + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + + // Setup Dear ImGui style + //ImGui::StyleColorsDark(); + ImGui::StyleColorsLight(); + + // Setup Platform/Renderer backends + ImGui_ImplSDL3_InitForOpenGL(window, gl_context); + ImGui_ImplOpenGL3_Init(glsl_version); + + // Load Fonts + // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. + // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple. + // - If the file cannot be loaded, the function will return a nullptr. Please handle those errors in your application (e.g. use an assertion, or display an error and quit). + // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. + // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use Freetype for higher quality font rendering. + // - Read 'docs/FONTS.md' for more instructions and details. + // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! + // - Our Emscripten build process allows embedding fonts to be accessible at runtime from the "fonts/" folder. See Makefile.emscripten for details. + //io.Fonts->AddFontDefault(); + io.Fonts->AddFontFromFileTTF("./font/FiraCode-Regular.ttf", 18.0f); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); + //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, nullptr, io.Fonts->GetGlyphRangesJapanese()); + //IM_ASSERT(font != nullptr); + + return true; +} + +void processUIEvent(const SDL_Event* event) +{ + ImGui_ImplSDL3_ProcessEvent(event); +} + +void OpenFileDialogFileCallback(void* userdata, const char* const* filelist, int filter) +{ + if (filelist == nullptr || *filelist == nullptr) return; + + g_scene.clear(); + + const char* szFileName = *filelist; + davinci_utils::load_scene_from_gltf(szFileName, g_scene); +} + +void drawMainUI() +{ + // Start the Dear ImGui frame + ImGui_ImplOpenGL3_NewFrame(); + ImGui_ImplSDL3_NewFrame(); + ImGui::NewFrame(); + + ImGui::Begin("Davinci"); // Create a window called "Hello, world!" and append into it. + if (ImGui::Button("OpenFile...")) + { + const SDL_DialogFileFilter filters[] = { + { "gltf files(*.gltf;*.glb)", "gltf;glb" }, + { "All files(*.*)", "*" } + }; + SDL_ShowOpenFileDialog(OpenFileDialogFileCallback, nullptr, main_window, filters, sizeof(filters)/sizeof(SDL_DialogFileFilter), nullptr, false); + } + if (ImGui::Button("Export")) + { + davinci_utils::export_mesh_to_obj(g_scene); + } + if (ImGui::Button("Render")) + { + davinci::UniformBuffer uniformBuffer; + uniformBuffer.updateStandardAttribute(g_scene); + + //find Light node + davinci::SceneNodeConstPtr lightNode = g_scene.getNodeWithComponent(); + if (lightNode) + { + uniformBuffer.setAttribute("light0:position", lightNode->getWorldPosition()); + } + + davinci::RenderQueue queue; + queue.build(g_scene); + + g_renderTarget.clear(); + queue.process(uniformBuffer, g_renderTarget); + } + ImGui::End(); + + // Rendering + ImGui::Render(); + + ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); +} + +void cleanMainUI() +{ + ImGui_ImplOpenGL3_Shutdown(); + ImGui_ImplSDL3_Shutdown(); + ImGui::DestroyContext(); +} \ No newline at end of file diff --git a/source/console/ui/ui_main.h b/source/console/ui/ui_main.h new file mode 100644 index 0000000..d0c10a1 --- /dev/null +++ b/source/console/ui/ui_main.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +//init main ui +bool initMainUI(SDL_Window* window, SDL_GLContext gl_context, const char* glsl_version); + +//process input event +void processUIEvent(const SDL_Event* event); + +//draw main ui +void drawMainUI(); + +//clean main ui +void cleanMainUI(); diff --git a/source/core/CMakeLists.txt b/source/core/CMakeLists.txt new file mode 100644 index 0000000..6f001e6 --- /dev/null +++ b/source/core/CMakeLists.txt @@ -0,0 +1,207 @@ + +set(DV_CORE_INCLUDE_FILES + include/dvc_config.h + include/dvc_pre_declare.h +) +source_group("include" FILES ${DV_CORE_INCLUDE_FILES}) + +################# +# math +################# +set(DV_CORE_MATH_INCLUDE_FILES + include/math/dvc_vector2.h + include/math/dvc_vector3.h + include/math/dvc_vector4.h + include/math/dvc_matrix3.h + include/math/dvc_matrix4.h + include/math/dvc_math_util.h + include/math/dvc_fixmath.h +) +source_group("include/math" FILES ${DV_CORE_MATH_INCLUDE_FILES}) + +set(DV_CORE_MATH_SOURCE_FILES + source/math/dvc_vector2.cpp + source/math/dvc_vector3.cpp + source/math/dvc_vector4.cpp + source/math/dvc_matrix3.cpp + source/math/dvc_matrix4.cpp + source/math/dvc_math_util.cpp +) +source_group("source/math" FILES ${DV_CORE_MATH_SOURCE_FILES}) + +################# +# scene +################# +set(DV_CORE_SCENE_INCLUDE_FILES + include/scene/dvc_scene.h + include/scene/dvc_scene_node.h +) +source_group("include/scene" FILES ${DV_CORE_SCENE_INCLUDE_FILES}) + +set(DV_CORE_SCENE_SOURCE_FILES + source/scene/dvc_scene.cpp + source/scene/dvc_scene_node.cpp +) +source_group("source/scene" FILES ${DV_CORE_SCENE_SOURCE_FILES}) + +################# +# scene component +################# +set(DV_CORE_SCENE_COMPONENT_INCLUDE_FILES + include/scene/component/dvc_component.h + include/scene/component/dvc_mesh_component.h + include/scene/component/dvc_camera_component.h + include/scene/component/dvc_light_component.h +) +source_group("include/scene/component" FILES ${DV_CORE_SCENE_COMPONENT_INCLUDE_FILES}) + +set(DV_CORE_SCENE_COMPONENT_SOURCE_FILES + source/scene/component/dvc_component.cpp + source/scene/component/dvc_mesh_component.cpp + source/scene/component/dvc_camera_component.cpp + source/scene/component/dvc_light_component.cpp +) +source_group("source/scene/component" FILES ${DV_CORE_SCENE_COMPONENT_SOURCE_FILES}) + +################# +# asset +################# +set(DV_CORE_ASSET_INCLUDE_FILES + include/asset/dvc_mesh.h +) +source_group("include/asset" FILES ${DV_CORE_ASSET_INCLUDE_FILES}) + +set(DV_CORE_ASSET_SOURCE_FILES + source/asset/dvc_mesh.cpp +) +source_group("source/asset" FILES ${DV_CORE_ASSET_SOURCE_FILES}) + +################# +# device +################# +set(DV_CORE_DEVICE_INCLUDE_FILES + include/device/dvc_buffer.h + include/device/dvc_buffer_view.h + include/device/dvc_buffer_accessor.h + include/device/dvc_render_target.h + include/device/dvc_pixel_buffer.h + include/device/dvc_uniform_buffer.h + include/device/dvc_vertex_desc.h +) +source_group("include/device" FILES ${DV_CORE_DEVICE_INCLUDE_FILES}) + +set(DV_CORE_DEVICE_SOURCE_FILES + source/device/dvc_buffer.cpp + source/device/dvc_buffer_view.cpp + source/device/dvc_buffer_accessor.cpp + source/device/dvc_render_target.cpp + source/device/dvc_pixel_buffer.cpp + source/device/dvc_uniform_buffer.cpp + source/device/dvc_vertex_desc.cpp +) +source_group("source/device" FILES ${DV_CORE_DEVICE_SOURCE_FILES}) + +################# +# pipe +################# +set(DV_CORE_PIPE_INCLUDE_FILES + include/pipe/dvc_render_queue.h + include/pipe/dvc_renderable.h + include/pipe/dvc_renderable_entity.h + include/pipe/dvc_render_step_IA.h + include/pipe/dvc_render_step_VS.h + include/pipe/dvc_render_step_PS.h + include/pipe/dvc_rasterizer.h +) +source_group("include/pipe" FILES ${DV_CORE_PIPE_INCLUDE_FILES}) + +set(DV_CORE_PIPE_SOURCE_FILES + source/pipe/dvc_render_queue.cpp + source/pipe/dvc_renderable.cpp + source/pipe/dvc_renderable_entity.cpp + source/pipe/dvc_render_step_IA.cpp + source/pipe/dvc_render_step_VS.cpp + source/pipe/dvc_render_step_PS.cpp + source/pipe/dvc_rasterizer_triangle.cpp +) +source_group("source/pipe" FILES ${DV_CORE_PIPE_SOURCE_FILES}) + +################# +# shader +################# +set(DV_CORE_SHADER_INCLUDE_FILES + include/shader/dvc_shader_interface.h +) +source_group("include/shader" FILES ${DV_CORE_SHADER_INCLUDE_FILES}) + +set(DV_CORE_SHADER_SOURCE_FILES + source/shader/dvc_shader_interface.cpp +) +source_group("source/shader" FILES ${DV_CORE_SHADER_SOURCE_FILES}) + +################# +# core library +################# +include_directories( + ${DV_AUTO_INCLUDE_PATH} + ${CMAKE_CURRENT_SOURCE_DIR}/include +) + +add_library(dv_core SHARED + ${DV_CORE_INCLUDE_FILES} + ${DV_CORE_MATH_INCLUDE_FILES} + ${DV_CORE_MATH_SOURCE_FILES} + ${DV_CORE_SCENE_INCLUDE_FILES} + ${DV_CORE_SCENE_SOURCE_FILES} + ${DV_CORE_SCENE_COMPONENT_INCLUDE_FILES} + ${DV_CORE_SCENE_COMPONENT_SOURCE_FILES} + ${DV_CORE_DEVICE_INCLUDE_FILES} + ${DV_CORE_DEVICE_SOURCE_FILES} + ${DV_CORE_ASSET_INCLUDE_FILES} + ${DV_CORE_ASSET_SOURCE_FILES} + ${DV_CORE_PIPE_INCLUDE_FILES} + ${DV_CORE_PIPE_SOURCE_FILES} + ${DV_CORE_SHADER_INCLUDE_FILES} + ${DV_CORE_SHADER_SOURCE_FILES} +) + +target_compile_definitions(dv_core + PRIVATE DV_CORE_EXPORTS +) + +################# +# copy runtime dll(for debug) +################# +add_custom_command( + TARGET dv_core POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different $ ${CMAKE_BINARY_DIR}/source/console/$<$:Debug>$<$:Release>) + +add_custom_command( + TARGET dv_core POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different $ ${CMAKE_BINARY_DIR}/test/unit/$<$:Debug>$<$:Release>) + +add_custom_command( + TARGET dv_core POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different $ ${CMAKE_BINARY_DIR}/test/rasterization/$<$:Debug>$<$:Release>) + +################# +# install +################# + +install(DIRECTORY include/ + DESTINATION include/core + FILES_MATCHING + PATTERN "*.h" +) + +if(MSVC) + install(TARGETS dv_core + RUNTIME DESTINATION bin/$<$:Debug>$<$:Release> + ARCHIVE DESTINATION lib/$<$:Debug>$<$:Release> +) +else(MSVC) +install(TARGETS dv_core + RUNTIME DESTINATION bin + ARCHIVE DESTINATION lib +) +endif(MSVC) diff --git a/source/core/include/asset/dvc_mesh.h b/source/core/include/asset/dvc_mesh.h new file mode 100644 index 0000000..aa2018c --- /dev/null +++ b/source/core/include/asset/dvc_mesh.h @@ -0,0 +1,42 @@ +#pragma once + +#include "dvc_config.h" +#include "dvc_pre_declare.h" + +DV_CORE_BEGIN_NAMESPACE + +class DV_CORE_API Mesh +{ +public: + typedef std::map PrimitiveAttributes; + struct Primitive + { + PrimitiveType type; + PrimitiveAttributes attributes; + DeviceBufferAccessorConstPtr indices; + }; + typedef std::shared_ptr PrimitivePtr; + typedef std::shared_ptr PrimitiveConstPtr; + typedef std::vector PrimitiveArray; + +public: + const std::string& getName(void) const { + return m_name; + } + + PrimitivePtr newPrimitive(void); + int32_t getPrimitiveCounts(void) const { + return (int32_t)m_primitiveArray.size(); + } + PrimitiveConstPtr getPrimitive(int32_t index) const; + +private: + std::string m_name; + PrimitiveArray m_primitiveArray; + +public: + Mesh(const std::string& name); + ~Mesh(); +}; + +DV_CORE_END_NAMESPACE diff --git a/source/core/include/device/dvc_buffer.h b/source/core/include/device/dvc_buffer.h new file mode 100644 index 0000000..7627cf5 --- /dev/null +++ b/source/core/include/device/dvc_buffer.h @@ -0,0 +1,32 @@ +#pragma once + +#include "dvc_config.h" + +DV_CORE_BEGIN_NAMESPACE + +class DV_CORE_API DeviceBuffer +{ +public: + void init(size_t _length); + void clear(void); + + size_t length(void) const { return m_length; } + + const uint8_t* ptr(size_t offseet) const { + return m_ptr + offseet; + } + + uint8_t* ptr(size_t offseet) { + return m_ptr + offseet; + } +private: + uint8_t* m_ptr; + size_t m_length; + +public: + DeviceBuffer(); + DeviceBuffer(size_t _length); + ~DeviceBuffer(); +}; + +DV_CORE_END_NAMESPACE diff --git a/source/core/include/device/dvc_buffer_accessor.h b/source/core/include/device/dvc_buffer_accessor.h new file mode 100644 index 0000000..2bbd48c --- /dev/null +++ b/source/core/include/device/dvc_buffer_accessor.h @@ -0,0 +1,58 @@ +#pragma once + +#include "dvc_buffer_view.h" + +DV_CORE_BEGIN_NAMESPACE + +class DV_CORE_API DeviceBufferAccessor +{ +public: + const DeviceBufferViewConstPtr getBufferView(void) const { + return m_bufferView; + } + + void walk(std::function callback, size_t start = 0, size_t end = ((size_t)-1)) const; + void walk2(std::function callback, size_t start = 0, size_t end = ((size_t)-1)) const; + void walk3(std::function callback, size_t start = 0, size_t end = ((size_t)-1)) const; + + const uint8_t* getElement(size_t index) const; + + size_t getOffset(void) const { + return m_offset; + } + + size_t getCount(void) const { + return m_count; + } + + ComponentType getComponentType(void) const { + return m_componentType; + } + + DataType getDataType(void) const { + return m_dataType; + } + + size_t getElementSize(void) const { + return m_elementSize; + } + + +private: + DeviceBufferViewConstPtr m_bufferView; + size_t m_offset; + size_t m_count; + ComponentType m_componentType; + DataType m_dataType; + + size_t m_elementSize; + +public: + DeviceBufferAccessor(DeviceBufferViewConstPtr bufferView, size_t offset, size_t count, ComponentType componentType, DataType dataType); + ~DeviceBufferAccessor(); +}; + + + + +DV_CORE_END_NAMESPACE diff --git a/source/core/include/device/dvc_buffer_view.h b/source/core/include/device/dvc_buffer_view.h new file mode 100644 index 0000000..11ef25a --- /dev/null +++ b/source/core/include/device/dvc_buffer_view.h @@ -0,0 +1,38 @@ +#pragma once + +#include "dvc_buffer.h" +#include "dvc_pre_declare.h" + +DV_CORE_BEGIN_NAMESPACE + +class DV_CORE_API DeviceBufferView +{ +public: + const DeviceBufferConstPtr getBuffer(void) const { + return m_buffer; + } + + size_t getOffset(void) const { + return m_offset; + } + + size_t getLength(void) const { + return m_length; + } + + size_t getStride(void) const { + return m_stride; + } + +private: + DeviceBufferConstPtr m_buffer; + size_t m_offset; + size_t m_length; + size_t m_stride; + +public: + DeviceBufferView(DeviceBufferConstPtr buffer, size_t offset, size_t length, size_t stride); + ~DeviceBufferView(); +}; + +DV_CORE_END_NAMESPACE diff --git a/source/core/include/device/dvc_pixel_buffer.h b/source/core/include/device/dvc_pixel_buffer.h new file mode 100644 index 0000000..ba7aede --- /dev/null +++ b/source/core/include/device/dvc_pixel_buffer.h @@ -0,0 +1,55 @@ +#pragma once + +#include "dvc_config.h" + +DV_CORE_BEGIN_NAMESPACE + +template +class PixelBuffer +{ +public: + void init(int width, int height, const T& pixel) { + assert(width > 0 && width < 0xFFFF); + assert(height > 0 && height < 0xFFFF); + + m_width = width; + m_height = height; + m_pixelBuffer.resize((size_t)(width * height)); + clear(pixel); + } + + void clear(const T& pixel) { + std::fill(m_pixelBuffer.begin(), m_pixelBuffer.end(), pixel); + } + + void setPixel(int32_t x, int32_t y, const T& pixel) { + assert(x >= 0 && x < m_width); + assert(y >= 0 && y < m_height); + + m_pixelBuffer[(size_t)(y * m_width + x)] = pixel; + } + + const T& getPixel(int32_t x, int32_t y) const { + assert(x >= 0 && x < m_width); + assert(y >= 0 && y < m_height); + + return m_pixelBuffer[(size_t)(y * m_width + x)]; + } + + const T* ptr(void) const { + return &(m_pixelBuffer[0]); + } + + int32_t getWidth(void) const { return m_width; } + int32_t getHeight(void) const { return m_height; } + +protected: + std::vector m_pixelBuffer; + int32_t m_width; + int32_t m_height; + +public: + PixelBuffer() : m_width(0), m_height(0) {} +}; + +DV_CORE_END_NAMESPACE diff --git a/source/core/include/device/dvc_render_target.h b/source/core/include/device/dvc_render_target.h new file mode 100644 index 0000000..fa85431 --- /dev/null +++ b/source/core/include/device/dvc_render_target.h @@ -0,0 +1,33 @@ +#pragma once + +#include "dvc_config.h" +#include "math/dvc_vector4.h" +#include "dvc_pixel_buffer.h" + +DV_CORE_BEGIN_NAMESPACE + +class DV_CORE_API RenderTarget +{ +public: + void init(int32_t width, int32_t height); + void clear(); + void setPixel(int32_t x, int32_t y, const fVector4& color, float32_t depth); + + int32_t getWidth(void) const { return m_width; } + int32_t getHeight(void) const { return m_height; } + + const PixelBuffer& getColorBuffer(void) const { return m_colorBuffer; } + const PixelBuffer& getDepthBuffer(void) const { return m_depthBuffer; } + +private: + int32_t m_width; + int32_t m_height; + PixelBuffer m_colorBuffer; + PixelBuffer m_depthBuffer; + +public: + RenderTarget(); + ~RenderTarget() {} +}; + +DV_CORE_END_NAMESPACE diff --git a/source/core/include/device/dvc_uniform_buffer.h b/source/core/include/device/dvc_uniform_buffer.h new file mode 100644 index 0000000..31435c8 --- /dev/null +++ b/source/core/include/device/dvc_uniform_buffer.h @@ -0,0 +1,51 @@ +#pragma once + +#include "dvc_config.h" +#include "math/dvc_vector2.h" +#include "math/dvc_vector3.h" +#include "math/dvc_vector4.h" +#include "math/dvc_matrix3.h" +#include "math/dvc_matrix4.h" + +DV_CORE_BEGIN_NAMESPACE + +class DV_CORE_API UniformBuffer +{ +public: + struct AttributeValue + { + std::string name; + ComponentType componentType; + DataType dataType; + void* value; + }; + typedef std::map AttributeMap; + +public: + void clear(); + + void updateStandardAttribute(const Scene& scene); + + void setAttribute(const std::string& name, ComponentType componentType, DataType dataType, const void* value); + + void setAttribute(const std::string& name, const int32_t& value); + void setAttribute(const std::string& name, const float32_t& value); + void setAttribute(const std::string& name, const fVector2& value); + void setAttribute(const std::string& name, const fVector3& value); + void setAttribute(const std::string& name, const fVector4& value); + void setAttribute(const std::string& name, const fMatrix3& value); + void setAttribute(const std::string& name, const fMatrix4& value); + + void* getAttribute(const std::string& name) const; + + void removeAttribute(const std::string& name); + +private: + void* mallocMemory(size_t size); + void freeMemory(void* memory); + +protected: + AttributeMap m_attributeMap; +}; + +DV_CORE_END_NAMESPACE diff --git a/source/core/include/device/dvc_vertex_desc.h b/source/core/include/device/dvc_vertex_desc.h new file mode 100644 index 0000000..aaa46ce --- /dev/null +++ b/source/core/include/device/dvc_vertex_desc.h @@ -0,0 +1,47 @@ +#pragma once + +#include "dvc_config.h" + +DV_CORE_BEGIN_NAMESPACE + +//Description of a vertex data +class DV_CORE_API VertexDesc +{ +public: + struct Element + { + std::string name; + VertexElementType elementType; + ComponentType componentType; + DataType dataType; + size_t offset; //byte offset + size_t size; //byte size + }; + +public: + bool addElement(const std::string& name, VertexElementType elementType, + ComponentType componentType, DataType dataType); + + const Element* getElement(size_t index) const; + const Element* getElement(VertexElementType elementType) const; + const Element* getElement(const std::string& name) const; + + //total byte size of each vertex + size_t vertexByteSize(void) const { return m_vertexSize; } + +private: + typedef std::vector< Element > ElementBuf; + ElementBuf m_elements; + + size_t m_vertexSize; + + std::map< VertexElementType, ElementBuf::size_type> m_elementTypeMap; + std::map< std::string, ElementBuf::size_type> m_nameMap; + +public: + VertexDesc(); + VertexDesc(const VertexDesc& other); + ~VertexDesc() {} +}; + +DV_CORE_END_NAMESPACE diff --git a/source/core/include/dvc_config.h b/source/core/include/dvc_config.h new file mode 100644 index 0000000..7bb1ca5 --- /dev/null +++ b/source/core/include/dvc_config.h @@ -0,0 +1,105 @@ +#pragma once + +#include "dv_config.h" + +#ifdef DV_CORE_EXPORTS +#define DV_CORE_API __declspec(dllexport) +#else +#define DV_CORE_API __declspec(dllimport) +#endif + +//core namespace +#define DV_CORE_BEGIN_NAMESPACE namespace davinci { +#define DV_CORE_END_NAMESPACE } + +//basic types +DV_CORE_BEGIN_NAMESPACE + +enum ComponentType +{ + CT_UNKNOWN, + + CT_Int8, + CT_Uint8, + CT_Int16, + CT_Uint16, + CT_Int32, + CT_Uint32, + CT_Float32, + CT_Float64, +}; + +enum DataType +{ + DT_UNKNOWN, + + DT_Scalar, + DT_Vec2, + DT_Vec3, + DT_Vec4, + DT_Matrix2, + DT_Matrix3, + DT_Matrix4, +}; + +enum VertexElementType +{ + VET_UNKNOWN, + + VET_POSITION, + VET_NORMAL, + VET_TANGENT, + VET_BINORMAL, + + VET_TEXCOORD_0, VET_TEXCOORD_1, VET_TEXCOORD_2, VET_TEXCOORD_3, + VET_TEXCOORD_4, VET_TEXCOORD_5, VET_TEXCOORD_6, VET_TEXCOORD_7, + + VET_TEXCOORD_LAST=VET_TEXCOORD_7, + + VET_COLOR_0, VET_COLOR_1, VET_COLOR_2, VET_COLOR_3, + VET_COLOR_4, VET_COLOR_5, VET_COLOR_6, VET_COLOR_7, + + VET_COLOR_LAST=VET_COLOR_7, + + VET_JOINTS_0, VET_JOINTS_1, VET_JOINTS_2, VET_JOINTS_3, + VET_JOINTS_4, VET_JOINTS_5, VET_JOINTS_6, VET_JOINTS_7, + + VET_JOINTS_LAST=VET_JOINTS_7, + + VET_WEIGHTS_0, VET_WEIGHTS_1, VET_WEIGHTS_2, VET_WEIGHTS_3, + VET_WEIGHTS_4, VET_WEIGHTS_5, VET_WEIGHTS_6, VET_WEIGHTS_7, + + VET_WEIGHTS_LAST=VET_WEIGHTS_7, + + VET_STANDARD_LAST=VET_WEIGHTS_LAST, +}; + +enum PrimitiveType +{ + PT_UNKNOWN, + + PT_POINT_LIST, + PT_LINE_LIST, + PT_LINE_LOOP, + PT_LINE_STRIP, + PT_TRIANGLE_LIST, + PT_TRIANGLE_STRIP, + PT_TRIANGLE_FAN, +}; + +enum PixelFormat +{ + //96-bit pixel format, 32 bits (float) for red, 32 bits (float) for green, 32 bits (float) for blue + PF_FLOAT32_RGB, + + /// 128-bit pixel format, 32 bits (float) for red, 32 bits (float) for green, 32 bits (float) for blue, 32 bits (float) for alpha + PF_FLOAT32_RGBA, + + // 32-bit pixel format, 32 bits (float) for red + PF_FLOAT32_R, + + /// 132-bit pixel format, 8 bits (float) for red, 8 bits (float) for green, 8 bits (float) for blue, 8 bits (float) for alpha + PF_UINT8_RGBA, +}; + +DV_CORE_END_NAMESPACE diff --git a/source/core/include/dvc_pre_declare.h b/source/core/include/dvc_pre_declare.h new file mode 100644 index 0000000..61b16ed --- /dev/null +++ b/source/core/include/dvc_pre_declare.h @@ -0,0 +1,73 @@ +#pragma once + +#include + +// Pre-declare classes + +DV_CORE_BEGIN_NAMESPACE + +template +class TVector2; + +template +class TVector3; + +template +class TVector4; + +template +class TMatrix4; + +class DeviceBuffer; +typedef std::shared_ptr DeviceBufferPtr; +typedef std::shared_ptr DeviceBufferConstPtr; + +class DeviceBufferView; +typedef std::shared_ptr DeviceBufferViewPtr; +typedef std::shared_ptr DeviceBufferViewConstPtr; + +class DeviceBufferAccessor; +typedef std::shared_ptr DeviceBufferAccessorPtr; +typedef std::shared_ptr DeviceBufferAccessorConstPtr; + +class RenderTarget; + +class Mesh; +typedef std::shared_ptr MeshPtr; +typedef std::shared_ptr MeshConstPtr; + +class Scene; + +class SceneNode; +typedef std::shared_ptr SceneNodePtr; +typedef std::shared_ptr SceneNodeConstPtr; +typedef std::vector SceneNodeArray; + +class SceneComponent; +typedef std::shared_ptr SceneComponentPtr; +typedef std::shared_ptr SceneComponentConstPtr; +typedef std::vector SceneComponentArray; + +class MeshComponent; +typedef std::shared_ptr MeshComponentPtr; +typedef std::shared_ptr MeshComponentConstPtr; + +class CameraComponent; +typedef std::shared_ptr CameraComponentPtr; +typedef std::shared_ptr CameraComponentConstPtr; + +class LightComponent; +typedef std::shared_ptr LightComponentPtr; +typedef std::shared_ptr LightComponentConstPtr; + +class RenderQueue; + +class Renderable; +typedef std::shared_ptr RenderablePtr; +typedef std::shared_ptr RenderableConstPtr; + +class VertexShaderInterface; + +class UniformBuffer; + +DV_CORE_END_NAMESPACE diff --git a/source/core/include/math/dvc_fixmath.h b/source/core/include/math/dvc_fixmath.h new file mode 100644 index 0000000..2d4b21a --- /dev/null +++ b/source/core/include/math/dvc_fixmath.h @@ -0,0 +1,98 @@ +#pragma once + +#include "dvc_config.h" + +DV_CORE_BEGIN_NAMESPACE + +typedef int64_t ifloat_t; +//typedef int64_t ifloat2_t[2]; +//typedef int64_t ifloat3_t[3]; + +#define IFLOAT_SHIFT (16) +#define IFLOAT_SCALE ((float64_t)(1<> IFLOAT_SHIFT); +} + +//------------------------------------------------------------------------------------- +//inline void ifloat3_add(ifloat3_t& a, const ifloat3_t& b) +//{ +// a[0] += b[0]; a[1] += b[1]; a[2] += b[2]; +//} + +//------------------------------------------------------------------------------------- +//inline void ifloat3_sub(ifloat3_t& a, const ifloat3_t& b) +//{ +// a[0] -= b[0]; a[1] -= b[1]; a[2] -= b[2]; +//} + +//------------------------------------------------------------------------------------- +//inline void ifloat3_cpy(ifloat3_t& a, const ifloat3_t& b) +//{ +// a[0] = b[0]; a[1] = b[1]; a[2] = b[2]; +//} + +//------------------------------------------------------------------------------------- +struct ifloat2_t +{ + ifloat_t x, y; + + ifloat2_t() : x(0ll), y(0ll) {} + ifloat2_t(const ifloat2_t& other) : x(other.x), y(other.y) {} + ifloat2_t(float32_t _x, float32_t _y) : x(ifloat_init(_x)), y(ifloat_init(_y)) {} +}; + +struct ifloat3_t +{ + ifloat_t x, y, z; + + inline void add(const ifloat3_t& other) { + x += other.x; + y += other.y; + z += other.z; + } + + inline void sub(const ifloat3_t& other) { + x -= other.x; + y -= other.y; + z -= other.z; + } + + inline ifloat_t operator [] (const size_t i) const { + return *(&x + i); + } + + inline ifloat_t& operator [] (const size_t i) { + return *(&x + i); + } + + ifloat3_t() : x(0ll), y(0ll), z(0ll) {} + ifloat3_t(const ifloat3_t& other) : x(other.x), y(other.y), z(other.z) {} + ifloat3_t(float32_t _x, float32_t _y, float32_t _z) : x(ifloat_init(_x)), y(ifloat_init(_y)), z(ifloat_init(_z)) {} + ifloat3_t(ifloat_t _x, ifloat_t _y, ifloat_t _z) : x(_x), y(_y), z(_z) {} +}; + + +DV_CORE_END_NAMESPACE + diff --git a/source/core/include/math/dvc_math_util.h b/source/core/include/math/dvc_math_util.h new file mode 100644 index 0000000..a99f227 --- /dev/null +++ b/source/core/include/math/dvc_math_util.h @@ -0,0 +1,141 @@ +#pragma once + +#include "dvc_config.h" +#include "dvc_pre_declare.h" + +DV_CORE_BEGIN_NAMESPACE + +class MathUtil +{ +public: + static size_t ComponentSize(ComponentType ct); + static size_t DataTypeSize(DataType dt); + + static bool floatEqual(float a, float b, + float tolerance = std::numeric_limits::epsilon()) { + return std::abs(b - a) <= tolerance; + } + + //Square root function. + static float sqrt(float f) { + return std::sqrt(f); + } + + //min + template + static V min2(const V& v1, const V& v2) { + return (v1 < v2) ? v1 : v2; + } + + template + static V min3(const V& v1, const V& v2, const V& v3) { + return (v1 < v2) ? ((v1 < v3) ? v1 : v3) : ((v2 < v3) ? v2 : v3); + } + + //max + template + static V max2(const V& v1, const V& v2) { + return (v1 > v2) ? v1 : v2; + } + + template + static V max3(const V& v1, const V& v2, const V& v3) { + return (v1 > v2) ? ((v1 > v3) ? v1 : v3) : ((v2 > v3) ? v2 : v3); + } + + /* Simulate the shader function lerp which performers linear interpolation + given 3 parameters v0, v1 and t the function returns the value of (1 - t)* v0 + t * v1. + where v0 and v1 are matching vector or scalar types and t can be either a scalar or a + vector of the same type as a and b. + */ + template + static V lerp(const V& v0, const V& v1, const T& t) { + return v0 * (1 - t) + v1 * t; + } + + template + static V lerp3(const V& v0, const V& v1, const V& v2, const T& t) { + return v0 * t[0] + v1 * t[1] + v2 * t[2]; + } + + // A random number in the range from [0,1] + static float DV_CORE_API unitRandom(void); + // A random number in the range from [low, high]. + static float rangeRandom(float low, float high) { + return (high - low)*unitRandom() + low; + } + static int32_t rangeRandom(int32_t low, int32_t high) { + return ((high - low) >= RAND_MAX) ? ((rand()<<16|rand()) % (high - low) + low) : (rand() % (high - low) + low); + } + //clamps the specified value within the range of min to max. + template + static T saturate(T v, const T& min=(T)0, const T& max=(T)1) { + return (v < min) ? min : ((v > max) ? max : v); + } + + //cosine function + static float cos(float a) { + return std::cos(a); + } + //Sine function + static float sin(float a) { + return std::sin(a); + } + + //return a right-handed, look-at matrix. + template + static TMatrix4 lookatRH(const TVector3& eye, const TVector3& lookat, const TVector3& up); + + //return a right-handed perspective projection matrix based on a field of view. + template + static TMatrix4 perspectiveFovRH(T fov, T aspect, T zNear, T zFar); + + static DV_CORE_API const float POS_INFINITY; + static DV_CORE_API const float NEG_INFINITY; + static DV_CORE_API const float PI; + static DV_CORE_API const float PI_X2; + static DV_CORE_API const float PI_DIV2; + static DV_CORE_API const float PI_DIV4; + static DV_CORE_API const float fDeg2Rad; + static DV_CORE_API const float fRad2Deg; +}; + +//------------------------------------------------------------------------------------- +template +TMatrix4 MathUtil::lookatRH(const TVector3& eye, const TVector3& lookat, const TVector3& up) +{ + assert(lookat != eye); + assert(up != TVector3::ZERO); + + TVector3 zaxis = (eye - lookat).normalise(); + TVector3 xaxis = up.crossProduct(zaxis).normalise(); + TVector3 yaxis = zaxis.crossProduct(xaxis).normalise(); + + return TMatrix4( + xaxis.x, xaxis.y, xaxis.z, -xaxis.dotProduct(eye), + yaxis.x, yaxis.y, yaxis.z, -yaxis.dotProduct(eye), + zaxis.x, zaxis.y, zaxis.z, -zaxis.dotProduct(eye), + 0, 0, 0, 1.f); +} + +//------------------------------------------------------------------------------------- +template +TMatrix4 MathUtil::perspectiveFovRH(T fov, T aspect, T zNear, T zFar) +{ + assert(zNear > T(0) && zFar > T(0)); + assert(!MathUtil::floatEqual(fov, T(0), T(0.00001) * 2)); + assert(!MathUtil::floatEqual(aspect, T(0), T(0.00001))); + assert(!MathUtil::floatEqual(zNear, zFar, T(0.00001))); + + T cotanHalfFovy = T(1.0) / tanf(fov * T(0.5)); + + return TMatrix4( + cotanHalfFovy / aspect, 0, 0, 0, + 0, cotanHalfFovy, 0, 0, + 0, 0, -zFar / (zFar - zNear), -(zFar * zNear) / (zFar - zNear), + 0, 0, T(-1), 0 + ); +} + +DV_CORE_END_NAMESPACE + diff --git a/source/core/include/math/dvc_matrix3.h b/source/core/include/math/dvc_matrix3.h new file mode 100644 index 0000000..a226e8b --- /dev/null +++ b/source/core/include/math/dvc_matrix3.h @@ -0,0 +1,294 @@ +#pragma once + +#include "dvc_config.h" +#include "dvc_pre_declare.h" + +DV_CORE_BEGIN_NAMESPACE + +template +class TMatrix3 +{ +protected: + /* The matrix entries, indexed by [row][col]. + | m[0][0] m[0][1] m[0][2] | + | m[1][0] m[1][1] m[1][2] | + | m[2][0] m[2][1] m[2][2] | + */ + union { + T m[3][3]; + T _m[9]; + }; + +public: + // constructor, It does NOT initialize the matrix for efficiency. + TMatrix3() {} + inline explicit TMatrix3(const T arr[3][3]) { + memcpy(m, arr, 9 * sizeof(T)); + } + inline TMatrix3(const TMatrix3& rkMatrix) { + memcpy(m, rkMatrix.m, 9 * sizeof(T)); + } + TMatrix3( + T m00, T m01, T m02, + T m10, T m11, T m12, + T m20, T m21, T m22) + { + m[0][0] = m00; m[0][1] = m01; m[0][2] = m02; + m[1][0] = m10; m[1][1] = m11; m[1][2] = m12; + m[2][0] = m20; m[2][1] = m21; m[2][2] = m22; + } + +public: + inline const T* operator[] (size_t iRow) const { + assert(iRow < 3); + return m[iRow]; + } + + inline T* operator[] (size_t iRow) { + assert(iRow < 3); + return m[iRow]; + } +public: + // Operations + inline TMatrix3& operator = (const TMatrix3& rkMatrix) { + memcpy(m, rkMatrix.m, 9 * sizeof(T)); + return *this; + } + + bool operator == (const TMatrix3& rkMatrix) const; + inline bool operator != (const TMatrix3& rkMatrix) const { + return !operator==(rkMatrix); + } + + // return this + rkMatrix + TMatrix3 operator + (const TMatrix3& rkMatrix) const; + + // return this - Matrix + TMatrix3 operator - (const TMatrix3& rkMatrix) const; + + // return this * rkMatrix + TMatrix3 operator * (const TMatrix3& rkMatrix) const; + + // return -this + TMatrix3 operator - () const; + + /* Matrix * Vector + | m[0][0] m[0][1] m[0][2] | |x| + | m[1][0] m[1][1] m[1][2] | * |y| + | m[2][0] m[2][1] m[2][2] | |z| + */ + TVector3 operator * (const TVector3& rkVector) const; + + /* Vector * Matrix + | m[0][0] m[0][1] m[0][2] | + [x, y, z] * | m[1][0] m[1][1] m[1][2] | + | m[2][0] m[2][1] m[2][2] | + */ + template + friend TVector3 operator * (const TVector3& rkVector, const TMatrix3& rkMatrix); + + /// Matrix * scalar + TMatrix3 operator * (T fScalar) const; + + /// Scalar * matrix + template + friend TMatrix3 operator * (U fScalar, const TMatrix3& rkMatrix); + +public: + // Utilities + + //return this^T + TMatrix3 transpose(void) const; + //rkInverse = this^{-1}?? return false mean no solution + bool inverse(TMatrix3& rkInverse, T fTolerance = 1e-06f) const; + //return this^{-1}, return ZERO if no solution + TMatrix3 inverse(T fTolerance = 1e-06f) const; + +public: + static DV_CORE_API const TMatrix3 ZERO; + static DV_CORE_API const TMatrix3 IDENTITY; +}; + +//------------------------------------------------------------------------------------- +template +bool TMatrix3::operator== (const TMatrix3& rkMatrix) const +{ + for (size_t iRow = 0; iRow < 3; iRow++) { + for (size_t iCol = 0; iCol < 3; iCol++) { + if (m[iRow][iCol] != rkMatrix.m[iRow][iCol]) + return false; + } + } + + return true; +} + +//------------------------------------------------------------------------------------- +template +TMatrix3 TMatrix3::operator + (const TMatrix3& rkMatrix) const +{ + TMatrix3 kSum; + for (size_t iRow = 0; iRow < 3; iRow++) { + for (size_t iCol = 0; iCol < 3; iCol++) { + kSum.m[iRow][iCol] = m[iRow][iCol] + rkMatrix.m[iRow][iCol]; + } + } + return kSum; +} + +//------------------------------------------------------------------------------------- +template +TMatrix3 TMatrix3::operator - (const TMatrix3& rkMatrix) const +{ + TMatrix3 kDiff; + for (size_t iRow = 0; iRow < 3; iRow++) { + for (size_t iCol = 0; iCol < 3; iCol++) { + kDiff.m[iRow][iCol] = m[iRow][iCol] - rkMatrix.m[iRow][iCol]; + } + } + return kDiff; +} + +//------------------------------------------------------------------------------------- +template +TMatrix3 TMatrix3::operator * (const TMatrix3& rkMatrix) const +{ + TMatrix3 kProd; + for (size_t iRow = 0; iRow < 3; iRow++) { + for (size_t iCol = 0; iCol < 3; iCol++) { + kProd.m[iRow][iCol] = + m[iRow][0] * rkMatrix.m[0][iCol] + + m[iRow][1] * rkMatrix.m[1][iCol] + + m[iRow][2] * rkMatrix.m[2][iCol]; + } + } + return kProd; +} + +//------------------------------------------------------------------------------------- +template +TMatrix3 TMatrix3::operator - () const +{ + TMatrix3 kNeg; + for (size_t i = 0; i < 9; i++) { + kNeg._m[i] = -_m[i]; + } + return kNeg; +} + +//------------------------------------------------------------------------------------- +template +TVector3 TMatrix3::operator* (const TVector3& rkPoint) const +{ + TVector3 kProd( + m[0][0] * rkPoint[0] + m[0][1] * rkPoint[1] + m[0][2] * rkPoint[2], + m[1][0] * rkPoint[0] + m[1][1] * rkPoint[1] + m[1][2] * rkPoint[2], + m[2][0] * rkPoint[0] + m[2][1] * rkPoint[1] + m[2][2] * rkPoint[2]); + + return kProd; +} + +//------------------------------------------------------------------------------------- +template +inline TVector3 operator* (const TVector3& rkPoint, const TMatrix3& rkMatrix) +{ + TVector3 kProd( + rkPoint[0] * rkMatrix.m[0][0] + rkPoint[1] * rkMatrix.m[1][0] + rkPoint[2] * rkMatrix.m[2][0], + rkPoint[0] * rkMatrix.m[0][1] + rkPoint[1] * rkMatrix.m[1][1] + rkPoint[2] * rkMatrix.m[2][1], + rkPoint[0] * rkMatrix.m[0][2] + rkPoint[1] * rkMatrix.m[1][2] + rkPoint[2] * rkMatrix.m[2][2]); + + return kProd; +} + +//------------------------------------------------------------------------------------- +template +TMatrix3 TMatrix3::operator* (T fScalar) const +{ + TMatrix3 kProd; + for (size_t iRow = 0; iRow < 3; iRow++) { + for (size_t iCol = 0; iCol < 3; iCol++) + kProd[iRow][iCol] = fScalar * m[iRow][iCol]; + } + return kProd; +} + +//------------------------------------------------------------------------------------- +template +TMatrix3 operator* (T fScalar, const TMatrix3& rkMatrix) +{ + TMatrix3 kProd; + for (size_t iRow = 0; iRow < 3; iRow++) { + for (size_t iCol = 0; iCol < 3; iCol++) + kProd[iRow][iCol] = fScalar * rkMatrix.m[iRow][iCol]; + } + return kProd; +} + +//------------------------------------------------------------------------------------- +template +TMatrix3 TMatrix3::transpose(void) const +{ + TMatrix3 kTranspose; + for (size_t iRow = 0; iRow < 3; iRow++) { + for (size_t iCol = 0; iCol < 3; iCol++) + kTranspose[iRow][iCol] = m[iCol][iRow]; + } + return kTranspose; +} + +//------------------------------------------------------------------------------------- +template +bool TMatrix3::inverse(TMatrix3& rkInverse, T fTolerance) const +{ + // Invert a 3x3 using cofactors. This is about 8 times faster than + // the Numerical Recipes code which uses Gaussian elimination. + + rkInverse[0][0] = m[1][1] * m[2][2] - + m[1][2] * m[2][1]; + rkInverse[0][1] = m[0][2] * m[2][1] - + m[0][1] * m[2][2]; + rkInverse[0][2] = m[0][1] * m[1][2] - + m[0][2] * m[1][1]; + rkInverse[1][0] = m[1][2] * m[2][0] - + m[1][0] * m[2][2]; + rkInverse[1][1] = m[0][0] * m[2][2] - + m[0][2] * m[2][0]; + rkInverse[1][2] = m[0][2] * m[1][0] - + m[0][0] * m[1][2]; + rkInverse[2][0] = m[1][0] * m[2][1] - + m[1][1] * m[2][0]; + rkInverse[2][1] = m[0][1] * m[2][0] - + m[0][0] * m[2][1]; + rkInverse[2][2] = m[0][0] * m[1][1] - + m[0][1] * m[1][0]; + + T fDet = + m[0][0] * rkInverse[0][0] + + m[0][1] * rkInverse[1][0] + + m[0][2] * rkInverse[2][0]; + + if (std::abs(fDet) <= fTolerance) + return false; + + T fInvDet = T(1.0) / fDet; + for (size_t iRow = 0; iRow < 3; iRow++) { + for (size_t iCol = 0; iCol < 3; iCol++) + rkInverse[iRow][iCol] *= fInvDet; + } + + return true; +} + +//------------------------------------------------------------------------------------- +template +TMatrix3 TMatrix3::inverse(T fTolerance) const +{ + TMatrix3 kInverse = TMatrix3::ZERO; + inverse(kInverse, fTolerance); + return kInverse; +} + +typedef TMatrix3 fMatrix3; + +DV_CORE_END_NAMESPACE + diff --git a/source/core/include/math/dvc_matrix4.h b/source/core/include/math/dvc_matrix4.h new file mode 100644 index 0000000..e0159f2 --- /dev/null +++ b/source/core/include/math/dvc_matrix4.h @@ -0,0 +1,463 @@ +#pragma once + +#include "dvc_config.h" +#include "dvc_pre_declare.h" + +DV_CORE_BEGIN_NAMESPACE + +template +class TMatrix4 +{ +protected: + /* + | m[0][0] m[0][1] m[0][2] m[0][3] | + | m[1][0] m[1][1] m[1][2] m[1][3] | + | m[2][0] m[2][1] m[2][2] m[2][3] | + | m[3][0] m[3][1] m[3][2] m[3][3] | + */ + union { + T m[4][4]; + T _m[16]; + }; +public: + // constructor, It does NOT initialize the matrix for efficiency. + TMatrix4() {} + inline explicit TMatrix4(const T arr[4][4]) { + memcpy(m, arr, 16 * sizeof(T)); + } + inline TMatrix4(const TMatrix4& rkMatrix) { + memcpy(m, rkMatrix.m, 16 * sizeof(T)); + } + TMatrix4( + T m00, T m01, T m02, T m03, + T m10, T m11, T m12, T m13, + T m20, T m21, T m22, T m23, + T m30, T m31, T m32, T m33) + { + m[0][0] = m00; m[0][1] = m01; m[0][2] = m02; m[0][3] = m03; + m[1][0] = m10; m[1][1] = m11; m[1][2] = m12; m[1][3] = m13; + m[2][0] = m20; m[2][1] = m21; m[2][2] = m22; m[2][3] = m23; + m[3][0] = m30; m[3][1] = m31; m[3][2] = m32; m[3][3] = m33; + } + +public: + inline const T* operator[] (size_t iRow) const { + assert(iRow < 4); + return m[iRow]; + } + + inline T* operator[] (size_t iRow) { + assert(iRow < 4); + return m[iRow]; + } + +public: + // Operators + inline TMatrix4& operator = (const TMatrix4& rkMatrix) { + memcpy(m, rkMatrix.m, 16 * sizeof(T)); + return *this; + } + + bool operator == (const TMatrix4& rkMatrix) const; + inline bool operator != (const TMatrix4& rkMatrix) const { + return !operator==(rkMatrix); + } + + // return this + m2 + TMatrix4 operator + (const TMatrix4 &m2) const; + // return this - m2 + TMatrix4 operator - (const TMatrix4 &m2) const; + //return this * m2 + TMatrix4 operator * (const TMatrix4 &m2) const; + //return this * scalar + inline TMatrix4 operator*(T scalar) const { + return TMatrix4( + scalar*m[0][0], scalar*m[0][1], scalar*m[0][2], scalar*m[0][3], + scalar*m[1][0], scalar*m[1][1], scalar*m[1][2], scalar*m[1][3], + scalar*m[2][0], scalar*m[2][1], scalar*m[2][2], scalar*m[2][3], + scalar*m[3][0], scalar*m[3][1], scalar*m[3][2], scalar*m[3][3]); + } + + /// return scalar * matrix + friend TMatrix4 operator* (T scalar, const TMatrix4& rkMatrix) { + return TMatrix4( + scalar*rkMatrix[0][0], scalar*rkMatrix[0][1], scalar*rkMatrix[0][2], scalar*rkMatrix[0][3], + scalar*rkMatrix[1][0], scalar*rkMatrix[1][1], scalar*rkMatrix[1][2], scalar*rkMatrix[1][3], + scalar*rkMatrix[2][0], scalar*rkMatrix[2][1], scalar*rkMatrix[2][2], scalar*rkMatrix[2][3], + scalar*rkMatrix[3][0], scalar*rkMatrix[3][1], scalar*rkMatrix[3][2], scalar*rkMatrix[3][3]); + } + + // return -this + TMatrix4 operator - () const { + return TMatrix4( + -m[0][0], -m[0][1], -m[0][2], -m[0][3], + -m[1][0], -m[1][1], -m[1][2], -m[1][3], + -m[2][0], -m[2][1], -m[2][2], -m[2][3], + -m[3][0], -m[3][1], -m[3][2], -m[3][3]); + } + + /* return Matrix * fVector4 + | m[0][0] m[0][1] m[0][2] m[0][3] | |x| + | m[1][0] m[1][1] m[1][2] m[1][3] | * |y| + | m[2][0] m[2][1] m[2][2] m[2][3] | |z| + | m[3][0] m[3][1] m[3][2] m[3][3] | |w| + */ + inline TVector4 operator * (const TVector4& v) const { + return TVector4( + (m[0][0] * v.x + m[0][1] * v.y) + (m[0][2] * v.z + m[0][3] * v.w), + (m[1][0] * v.x + m[1][1] * v.y) + (m[1][2] * v.z + m[1][3] * v.w), + (m[2][0] * v.x + m[2][1] * v.y) + (m[2][2] * v.z + m[2][3] * v.w), + (m[3][0] * v.x + m[3][1] * v.y) + (m[3][2] * v.z + m[3][3] * v.w) + ); + } + + /* + Transforms the given 3-D vector by the matrix, projecting the + result back into w = 1. + @note + This means that the initial w is considered to be 1.0, + and then all the tree elements of the resulting 3-D vector are + divided by the resulting w. + */ + inline TVector3 operator * (const TVector3 &v) const { + TVector3 r; + T fInvW = 1.0f / (m[3][0] * v.x + m[3][1] * v.y + m[3][2] * v.z + m[3][3]); + + r.x = (m[0][0] * v.x + m[0][1] * v.y + m[0][2] * v.z + m[0][3]) * fInvW; + r.y = (m[1][0] * v.x + m[1][1] * v.y + m[1][2] * v.z + m[1][3]) * fInvW; + r.z = (m[2][0] * v.x + m[2][1] * v.y + m[2][2] * v.z + m[2][3]) * fInvW; + return r; + } + + /* return fVector4 * Matrix + | m[0][0] m[0][1] m[0][2] m[0][3] | + [x,y,z,w] * | m[1][0] m[1][1] m[1][2] m[1][3] | + | m[2][0] m[2][1] m[2][2] m[2][3] | + | m[3][0] m[3][1] m[3][2] m[3][3] | + */ + friend TVector4 operator * (const TVector4& v, const TMatrix4& mat) { + return TVector4( + (v.x*mat[0][0] + v.y*mat[1][0]) + (v.z*mat[2][0] + v.w*mat[3][0]), + (v.x*mat[0][1] + v.y*mat[1][1]) + (v.z*mat[2][1] + v.w*mat[3][1]), + (v.x*mat[0][2] + v.y*mat[1][2]) + (v.z*mat[2][2] + v.w*mat[3][2]), + (v.x*mat[0][3] + v.y*mat[1][3]) + (v.z*mat[2][3] + v.w*mat[3][3]) + ); + } + + friend TVector3 operator * (const TVector3& v, const TMatrix4& mat) { + TVector3 r; + T fInvW = 1.0f / (mat.m[0][3] * v.x + mat.m[1][3] * v.y + mat.m[2][3] * v.z + mat.m[3][3]); + + r.x = (mat.m[0][0] * v.x + mat.m[1][0] * v.y + mat.m[2][0] * v.z + mat.m[3][0]) * fInvW; + r.y = (mat.m[0][1] * v.x + mat.m[1][1] * v.y + mat.m[2][1] * v.z + mat.m[3][1]) * fInvW; + r.z = (mat.m[0][2] * v.x + mat.m[1][2] * v.y + mat.m[2][2] * v.z + mat.m[3][2]) * fInvW; + return r; + } +public: + static inline TMatrix4 makeTrans(T x, T y, T z) { + return TMatrix4( + 1, 0, 0, x, + 0, 1, 0, y, + 0, 0, 1, z, + 0, 0, 0, 1); + } + + static inline TMatrix4 makeTrans(const TVector3& pos) { + return TMatrix4( + 1, 0, 0, pos.x, + 0, 1, 0, pos.y, + 0, 0, 1, pos.z, + 0, 0, 0, 1); + } + + static inline TMatrix4 makeScale(T x, T y, T z) { + return TMatrix4( + x, 0, 0, 0, + 0, y, 0, 0, + 0, 0, z, 0, + 0, 0, 0, 1); + } + + //rotate matrix(x), left multiplication + static inline TMatrix4 makeRotate_X(T angle) { + T s = sin(angle); + T c = cos(angle); + return TMatrix4( + 1, 0, 0, 0, + 0, c, -s, 0, + 0, s, c, 0, + 0, 0, 0, 1); + } + + //rotate matrix(y), left multiplication + static inline TMatrix4 makeRotate_Y(T angle) { + T s = sin(angle); + T c = cos(angle); + return TMatrix4( + c, 0, s, 0, + 0, 1, 0, 0, + -s, 0, c, 0, + 0, 0, 0, 1); + } + + //rotate matrix(z), left multiplication + static inline TMatrix4 makeRotate_Z(T angle) { + T s = sin(angle); + T c = cos(angle); + return TMatrix4( + c, -s, 0, 0, + s, c, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1); + } + + //rotate matrix(rotate angle about the v axis), left multiplication + static TMatrix4 makeRotate(const TVector3& v, T angle) { + T c = cos(angle); + T s = sin(angle); + TMatrix4 result; + + TVector3 axis = v.normalise(); + + result[0][0] = c + (T(1) - c) * axis.x * axis.x; + result[1][0] = (T(1) - c) * axis.x * axis.y + s * axis.z; + result[2][0] = (T(1) - c) * axis.x * axis.z - s * axis.y; + result[3][0] = T(0); + + result[0][1] = (T(1) - c) * axis.y * axis.x - s * axis.z; + result[1][1] = c + (T(1) - c) * axis.y * axis.y; + result[2][1] = (T(1) - c) * axis.y * axis.z + s * axis.x; + result[3][1] = T(0); + + result[0][2] = (T(1) - c) * axis.z * axis.x + s * axis.y; + result[1][2] = (T(1) - c) * axis.z * axis.y - s * axis.x; + result[2][2] = c + (T(1) - c) * axis.z * axis.z; + result[3][2] = T(0); + + result[0][3] = 0; + result[1][3] = 0; + result[2][3] = 0; + result[3][3] = T(1); + + return result; + } + + //rotate matrix(from quaternion) + static TMatrix4 makeRotate(const TVector4& q) { + TMatrix4 result; + + T qxx(q.x * q.x); + T qyy(q.y * q.y); + T qzz(q.z * q.z); + T qxz(q.x * q.z); + T qxy(q.x * q.y); + T qyz(q.y * q.z); + T qwx(q.w * q.x); + T qwy(q.w * q.y); + T qwz(q.w * q.z); + + result[0][0] = T(1) - T(2) * (qyy + qzz); + result[1][0] = T(2) * (qxy + qwz); + result[2][0] = T(2) * (qxz - qwy); + result[3][0] = 0; + + result[0][1] = T(2) * (qxy - qwz); + result[1][1] = T(1) - T(2) * (qxx + qzz); + result[2][1] = T(2) * (qyz + qwx); + result[3][1] = 0; + + result[0][2] = T(2) * (qxz + qwy); + result[1][2] = T(2) * (qyz - qwx); + result[2][2] = T(1) - T(2) * (qxx + qyy); + result[3][2] = 0; + + result[0][3] = 0; + result[1][3] = 0; + result[2][3] = 0; + result[3][3] = T(1); + + return result; + } + + inline TMatrix4 transpose(void) const { + return TMatrix4( + m[0][0], m[1][0], m[2][0], m[3][0], + m[0][1], m[1][1], m[2][1], m[3][1], + m[0][2], m[1][2], m[2][2], m[3][2], + m[0][3], m[1][3], m[2][3], m[3][3]); + } + + //return this^{-1}, return ZERO if no solution + TMatrix4 inverse(void) const; + +public: + static DV_CORE_API const TMatrix4 ZERO; + static DV_CORE_API const TMatrix4 IDENTITY; + +}; + +//------------------------------------------------------------------------------------- +template +bool TMatrix4::operator == (const TMatrix4& m2) const { + if ( + m[0][0] != m2.m[0][0] || m[0][1] != m2.m[0][1] || m[0][2] != m2.m[0][2] || m[0][3] != m2.m[0][3] || + m[1][0] != m2.m[1][0] || m[1][1] != m2.m[1][1] || m[1][2] != m2.m[1][2] || m[1][3] != m2.m[1][3] || + m[2][0] != m2.m[2][0] || m[2][1] != m2.m[2][1] || m[2][2] != m2.m[2][2] || m[2][3] != m2.m[2][3] || + m[3][0] != m2.m[3][0] || m[3][1] != m2.m[3][1] || m[3][2] != m2.m[3][2] || m[3][3] != m2.m[3][3]) + return false; + return true; +} + +//------------------------------------------------------------------------------------- +template +TMatrix4 TMatrix4::operator * (const TMatrix4& m2) const +{ + TMatrix4 r; + r.m[0][0] = m[0][0] * m2.m[0][0] + m[0][1] * m2.m[1][0] + m[0][2] * m2.m[2][0] + m[0][3] * m2.m[3][0]; + r.m[0][1] = m[0][0] * m2.m[0][1] + m[0][1] * m2.m[1][1] + m[0][2] * m2.m[2][1] + m[0][3] * m2.m[3][1]; + r.m[0][2] = m[0][0] * m2.m[0][2] + m[0][1] * m2.m[1][2] + m[0][2] * m2.m[2][2] + m[0][3] * m2.m[3][2]; + r.m[0][3] = m[0][0] * m2.m[0][3] + m[0][1] * m2.m[1][3] + m[0][2] * m2.m[2][3] + m[0][3] * m2.m[3][3]; + + r.m[1][0] = m[1][0] * m2.m[0][0] + m[1][1] * m2.m[1][0] + m[1][2] * m2.m[2][0] + m[1][3] * m2.m[3][0]; + r.m[1][1] = m[1][0] * m2.m[0][1] + m[1][1] * m2.m[1][1] + m[1][2] * m2.m[2][1] + m[1][3] * m2.m[3][1]; + r.m[1][2] = m[1][0] * m2.m[0][2] + m[1][1] * m2.m[1][2] + m[1][2] * m2.m[2][2] + m[1][3] * m2.m[3][2]; + r.m[1][3] = m[1][0] * m2.m[0][3] + m[1][1] * m2.m[1][3] + m[1][2] * m2.m[2][3] + m[1][3] * m2.m[3][3]; + + r.m[2][0] = m[2][0] * m2.m[0][0] + m[2][1] * m2.m[1][0] + m[2][2] * m2.m[2][0] + m[2][3] * m2.m[3][0]; + r.m[2][1] = m[2][0] * m2.m[0][1] + m[2][1] * m2.m[1][1] + m[2][2] * m2.m[2][1] + m[2][3] * m2.m[3][1]; + r.m[2][2] = m[2][0] * m2.m[0][2] + m[2][1] * m2.m[1][2] + m[2][2] * m2.m[2][2] + m[2][3] * m2.m[3][2]; + r.m[2][3] = m[2][0] * m2.m[0][3] + m[2][1] * m2.m[1][3] + m[2][2] * m2.m[2][3] + m[2][3] * m2.m[3][3]; + + r.m[3][0] = m[3][0] * m2.m[0][0] + m[3][1] * m2.m[1][0] + m[3][2] * m2.m[2][0] + m[3][3] * m2.m[3][0]; + r.m[3][1] = m[3][0] * m2.m[0][1] + m[3][1] * m2.m[1][1] + m[3][2] * m2.m[2][1] + m[3][3] * m2.m[3][1]; + r.m[3][2] = m[3][0] * m2.m[0][2] + m[3][1] * m2.m[1][2] + m[3][2] * m2.m[2][2] + m[3][3] * m2.m[3][2]; + r.m[3][3] = m[3][0] * m2.m[0][3] + m[3][1] * m2.m[1][3] + m[3][2] * m2.m[2][3] + m[3][3] * m2.m[3][3]; + + return r; +} + +//------------------------------------------------------------------------------------- +template +TMatrix4 TMatrix4::operator + (const TMatrix4& m2) const +{ + TMatrix4 r; + + r.m[0][0] = m[0][0] + m2.m[0][0]; + r.m[0][1] = m[0][1] + m2.m[0][1]; + r.m[0][2] = m[0][2] + m2.m[0][2]; + r.m[0][3] = m[0][3] + m2.m[0][3]; + + r.m[1][0] = m[1][0] + m2.m[1][0]; + r.m[1][1] = m[1][1] + m2.m[1][1]; + r.m[1][2] = m[1][2] + m2.m[1][2]; + r.m[1][3] = m[1][3] + m2.m[1][3]; + + r.m[2][0] = m[2][0] + m2.m[2][0]; + r.m[2][1] = m[2][1] + m2.m[2][1]; + r.m[2][2] = m[2][2] + m2.m[2][2]; + r.m[2][3] = m[2][3] + m2.m[2][3]; + + r.m[3][0] = m[3][0] + m2.m[3][0]; + r.m[3][1] = m[3][1] + m2.m[3][1]; + r.m[3][2] = m[3][2] + m2.m[3][2]; + r.m[3][3] = m[3][3] + m2.m[3][3]; + + return r; +} + +//------------------------------------------------------------------------------------- +template +TMatrix4 TMatrix4::operator - (const TMatrix4& m2) const +{ + TMatrix4 r; + r.m[0][0] = m[0][0] - m2.m[0][0]; + r.m[0][1] = m[0][1] - m2.m[0][1]; + r.m[0][2] = m[0][2] - m2.m[0][2]; + r.m[0][3] = m[0][3] - m2.m[0][3]; + + r.m[1][0] = m[1][0] - m2.m[1][0]; + r.m[1][1] = m[1][1] - m2.m[1][1]; + r.m[1][2] = m[1][2] - m2.m[1][2]; + r.m[1][3] = m[1][3] - m2.m[1][3]; + + r.m[2][0] = m[2][0] - m2.m[2][0]; + r.m[2][1] = m[2][1] - m2.m[2][1]; + r.m[2][2] = m[2][2] - m2.m[2][2]; + r.m[2][3] = m[2][3] - m2.m[2][3]; + + r.m[3][0] = m[3][0] - m2.m[3][0]; + r.m[3][1] = m[3][1] - m2.m[3][1]; + r.m[3][2] = m[3][2] - m2.m[3][2]; + r.m[3][3] = m[3][3] - m2.m[3][3]; + + return r; +} + +//------------------------------------------------------------------------------------- +template +TMatrix4 TMatrix4::inverse(void) const +{ + T m00 = m[0][0], m01 = m[0][1], m02 = m[0][2], m03 = m[0][3]; + T m10 = m[1][0], m11 = m[1][1], m12 = m[1][2], m13 = m[1][3]; + T m20 = m[2][0], m21 = m[2][1], m22 = m[2][2], m23 = m[2][3]; + T m30 = m[3][0], m31 = m[3][1], m32 = m[3][2], m33 = m[3][3]; + + T v0 = m20 * m31 - m21 * m30; + T v1 = m20 * m32 - m22 * m30; + T v2 = m20 * m33 - m23 * m30; + T v3 = m21 * m32 - m22 * m31; + T v4 = m21 * m33 - m23 * m31; + T v5 = m22 * m33 - m23 * m32; + + T t00 = +(v5 * m11 - v4 * m12 + v3 * m13); + T t10 = -(v5 * m10 - v2 * m12 + v1 * m13); + T t20 = +(v4 * m10 - v2 * m11 + v0 * m13); + T t30 = -(v3 * m10 - v1 * m11 + v0 * m12); + + T invDet = 1 / (t00 * m00 + t10 * m01 + t20 * m02 + t30 * m03); + + T d00 = t00 * invDet; + T d10 = t10 * invDet; + T d20 = t20 * invDet; + T d30 = t30 * invDet; + + T d01 = -(v5 * m01 - v4 * m02 + v3 * m03) * invDet; + T d11 = +(v5 * m00 - v2 * m02 + v1 * m03) * invDet; + T d21 = -(v4 * m00 - v2 * m01 + v0 * m03) * invDet; + T d31 = +(v3 * m00 - v1 * m01 + v0 * m02) * invDet; + + v0 = m10 * m31 - m11 * m30; + v1 = m10 * m32 - m12 * m30; + v2 = m10 * m33 - m13 * m30; + v3 = m11 * m32 - m12 * m31; + v4 = m11 * m33 - m13 * m31; + v5 = m12 * m33 - m13 * m32; + + T d02 = +(v5 * m01 - v4 * m02 + v3 * m03) * invDet; + T d12 = -(v5 * m00 - v2 * m02 + v1 * m03) * invDet; + T d22 = +(v4 * m00 - v2 * m01 + v0 * m03) * invDet; + T d32 = -(v3 * m00 - v1 * m01 + v0 * m02) * invDet; + + v0 = m21 * m10 - m20 * m11; + v1 = m22 * m10 - m20 * m12; + v2 = m23 * m10 - m20 * m13; + v3 = m22 * m11 - m21 * m12; + v4 = m23 * m11 - m21 * m13; + v5 = m23 * m12 - m22 * m13; + + T d03 = -(v5 * m01 - v4 * m02 + v3 * m03) * invDet; + T d13 = +(v5 * m00 - v2 * m02 + v1 * m03) * invDet; + T d23 = -(v4 * m00 - v2 * m01 + v0 * m03) * invDet; + T d33 = +(v3 * m00 - v1 * m01 + v0 * m02) * invDet; + + return TMatrix4( + d00, d01, d02, d03, + d10, d11, d12, d13, + d20, d21, d22, d23, + d30, d31, d32, d33); +} + +typedef TMatrix4 fMatrix4; + +DV_CORE_END_NAMESPACE + diff --git a/source/core/include/math/dvc_vector2.h b/source/core/include/math/dvc_vector2.h new file mode 100644 index 0000000..87a91d2 --- /dev/null +++ b/source/core/include/math/dvc_vector2.h @@ -0,0 +1,239 @@ +#pragma once + +#include "dvc_config.h" + +DV_CORE_BEGIN_NAMESPACE + +template +class TVector2 +{ +public: + T x, y; + +public: + // Construct + TVector2() : x(0), y(0) { } + TVector2(T _x, T _y) : x(_x), y(_y){ } + inline explicit TVector2(const T s) : x(s), y(s) { } + inline explicit TVector2(const T f[2]) : x(f[0]), y(f[1]) { } + inline explicit TVector2(T* const f) : x(f[0]), y(f[1]) { } + inline explicit TVector2(const int32_t f[2]) : x((T)f[0]), y((T)f[1]) { } + +public: + inline T operator [] (const size_t i) const { + assert(i < 2); + return *(&x + i); + } + + inline T& operator [] (const size_t i) { + assert(i < 2); + return *(&x + i); + } + + inline T* ptr(void) { + return &x; + } + + inline const T* ptr(void) const { + return &x; + } +public: + // Operations + inline TVector2& operator = (const TVector2& rkVector) { + x = rkVector.x; + y = rkVector.y; + return *this; + } + + inline TVector2& operator = (const T fScalar) { + x = fScalar; + y = fScalar; + return *this; + } + + inline bool operator == (const TVector2& rkVector) const { + return (x == rkVector.x && y == rkVector.y); + } + + inline bool operator != (const TVector2& rkVector) const { + return (x != rkVector.x || y != rkVector.y); + } + +public: + // Arithmetic operations + inline TVector2 operator + (const TVector2& rkVector) const { + return TVector2( x + rkVector.x, y + rkVector.y); + } + + inline TVector2 operator - (const TVector2& rkVector) const { + return TVector2( x - rkVector.x, y - rkVector.y); + } + + inline TVector2 operator * (const T fScalar) const { + return TVector2( x * fScalar, y * fScalar); + } + + inline TVector2 operator * (const TVector2& rhs) const { + return TVector2(x * rhs.x, y * rhs.y); + } + + inline TVector2 operator / (const T fScalar) const { + assert(fScalar != 0.0); + return TVector2(x / fScalar, y / fScalar); + } + + inline TVector2 operator / (const TVector2& rhs) const { + return TVector2( x / rhs.x, y / rhs.y); + } + + inline const TVector2& operator + () const { + return *this; + } + + inline TVector2 operator - () const { + return TVector2(-x, -y); + } + +public: + // overloaded operators to help TVector2 + inline friend TVector2 operator * (const T fScalar, const TVector2& rkVector) { + return TVector2(fScalar * rkVector.x, fScalar * rkVector.y); + } + + inline friend TVector2 operator / (const T fScalar, const TVector2& rkVector) { + return TVector2( fScalar / rkVector.x, fScalar / rkVector.y); + } + + inline friend TVector2 operator + (const TVector2& lhs, const T rhs) { + return TVector2( lhs.x + rhs, lhs.y + rhs); + } + + inline friend TVector2 operator + (const T lhs, const TVector2& rhs) { + return TVector2( lhs + rhs.x, lhs + rhs.y); + } + + inline friend TVector2 operator - (const TVector2& lhs, const T rhs) { + return TVector2( lhs.x - rhs, lhs.y - rhs); + } + + inline friend TVector2 operator - (const T lhs, const TVector2& rhs) { + return TVector2( lhs - rhs.x, lhs - rhs.y); + } + +public: + // arithmetic updates + inline TVector2& operator += (const TVector2& rkVector) { + x += rkVector.x; + y += rkVector.y; + return *this; + } + + inline TVector2& operator += (const T fScaler) { + x += fScaler; + y += fScaler; + return *this; + } + + inline TVector2& operator -= (const TVector2& rkVector) { + x -= rkVector.x; + y -= rkVector.y; + return *this; + } + + inline TVector2& operator -= (const T fScaler) { + x -= fScaler; + y -= fScaler; + return *this; + } + + inline TVector2& operator *= (const T fScalar) { + x *= fScalar; + y *= fScalar; + return *this; + } + + inline TVector2& operator *= (const TVector2& rkVector) { + x *= rkVector.x; + y *= rkVector.y; + return *this; + } + + inline TVector2& operator /= (const T fScalar) { + assert(fScalar != 0.0); + + x /= fScalar; + y /= fScalar; + return *this; + } + + inline TVector2& operator /= (const TVector2& rkVector) { + x /= rkVector.x; + y /= rkVector.y; + return *this; + } + + inline bool operator < (const TVector2& rhs) const { + return (x < rhs.x && y < rhs.y); + } + + inline bool operator > (const TVector2& rhs) const { + return (x > rhs.x && y > rhs.y); + } + +public: + inline T length(void) const { + return MathUtil::sqrt(x * x + y * y); + } + + inline T squaredLength(void) const { + return x * x + y * y; + } + + inline T distance(const TVector2& rhs) const { + return (*this - rhs).length(); + } + + inline T squaredDistance(const TVector2& rhs) const { + return (*this - rhs).squaredLength(); + } + + inline T dotProduct(const TVector2& vec) const { + return x * vec.x + y * vec.y; + } + + inline T crossProduct(const TVector2& rkVector) const { + return x * rkVector.y - y * rkVector.x; + } + + inline TVector2& normalise(void) { + T fLength = length(); + if (fLength > 0.0f) { + T invLength = T(1.0) / fLength; + x *= invLength; + y *= invLength; + } + + return *this; + } + + inline TVector2& saturate(void) { + x = MathUtil::saturate(x); + y = MathUtil::saturate(y); + + return *this; + } +public: + // special points + static DV_CORE_API const TVector2 ZERO; + static DV_CORE_API const TVector2 ONE; + static DV_CORE_API const TVector2 UNIT_X; + static DV_CORE_API const TVector2 UNIT_Y; + static DV_CORE_API const TVector2 NEGATIVE_UNIT_X; + static DV_CORE_API const TVector2 NEGATIVE_UNIT_Y; + static DV_CORE_API const TVector2 UNIT_SCALE; +}; + + +typedef TVector2 fVector2; + +DV_CORE_END_NAMESPACE diff --git a/source/core/include/math/dvc_vector3.h b/source/core/include/math/dvc_vector3.h new file mode 100644 index 0000000..673ce18 --- /dev/null +++ b/source/core/include/math/dvc_vector3.h @@ -0,0 +1,289 @@ +#pragma once + +#include "dvc_config.h" +#include "dvc_pre_declare.h" +#include "dvc_math_util.h" + +DV_CORE_BEGIN_NAMESPACE + +template +class TVector3 +{ +public: + T x, y, z; + +public: + // Construct + TVector3() : x(0.), y(0.), z(0.) { } + TVector3(T _x, T _y, T _z) : x(_x), y(_y), z(_z){ } + TVector3(const TVector2& _xy, T _z) : x(_xy.x), y(_xy.y), z(_z) { } + TVector3(T _x, const TVector2& _yz) : x(_x), y(_yz.x), z(_yz.y) { } + inline explicit TVector3(const T s) : x(s), y(s), z(s) { } + inline explicit TVector3(const T f[3]) : x(f[0]), y(f[1]), z(f[2]) { } + inline explicit TVector3(T* const f) : x(f[0]), y(f[1]), z(f[2]) { } + inline explicit TVector3(const int32_t f[3]) : x((T)f[0]), y((T)f[1]), z((T)f[2]) {} + +public: + inline T operator [] (const size_t i) const { + assert(i < 3); + return *(&x + i); + } + + inline T& operator [] (const size_t i) { + assert(i < 3); + return *(&x + i); + } + + inline T* ptr(void) { + return &x; + } + + inline const T* ptr(void) const { + return &x; + } + + inline TVector2 xy(void) const { return TVector2(x, y); } + inline TVector2 xz(void) const { return TVector2(x, z); } + inline TVector2 yz(void) const { return TVector2(y, z); } + +public: + // Operations + inline TVector3& operator = (const TVector3& rkVector) { + x = rkVector.x; + y = rkVector.y; + z = rkVector.z; + return *this; + } + + inline TVector3& operator = (const T fScalar) { + x = fScalar; + y = fScalar; + z = fScalar; + return *this; + } + + inline bool operator == (const TVector3& rkVector) const { + return (x == rkVector.x && y == rkVector.y && z == rkVector.z); + } + + inline bool operator != (const TVector3& rkVector) const { + return (x != rkVector.x || y != rkVector.y || z != rkVector.z); + } + +public: + // Arithmetic operations + inline TVector3 operator + (const TVector3& rkVector) const { + return TVector3( x + rkVector.x, y + rkVector.y, z + rkVector.z); + } + + inline TVector3 operator - (const TVector3& rkVector) const { + return TVector3( x - rkVector.x, y - rkVector.y, z - rkVector.z); + } + + inline TVector3 operator * (const T fScalar) const { + return TVector3( x * fScalar, y * fScalar, z * fScalar); + } + + inline TVector3 operator * (const TVector3& rhs) const { + return TVector3(x * rhs.x, y * rhs.y, z * rhs.z); + } + + inline TVector3 operator / (const T fScalar) const { + assert(fScalar != 0.0); + return TVector3(x / fScalar, y / fScalar, z / fScalar); + } + + inline TVector3 operator / (const TVector3& rhs) const { + return TVector3( x / rhs.x, y / rhs.y, z / rhs.z); + } + + inline const TVector3& operator + () const { + return *this; + } + + inline TVector3 operator - () const { + return TVector3(-x, -y, -z); + } + +public: + // overloaded operators to help TVector3 + inline friend TVector3 operator * (const T fScalar, const TVector3& rkVector) { + return TVector3(fScalar * rkVector.x, fScalar * rkVector.y, fScalar * rkVector.z); + } + + inline friend TVector3 operator / (const T fScalar, const TVector3& rkVector) { + return TVector3( fScalar / rkVector.x, fScalar / rkVector.y, fScalar / rkVector.z); + } + + inline friend TVector3 operator + (const TVector3& lhs, const T rhs) { + return TVector3( lhs.x + rhs, lhs.y + rhs, lhs.z + rhs); + } + + inline friend TVector3 operator + (const T lhs, const TVector3& rhs) { + return TVector3( lhs + rhs.x, lhs + rhs.y, lhs + rhs.z); + } + + inline friend TVector3 operator - (const TVector3& lhs, const T rhs) { + return TVector3( lhs.x - rhs, lhs.y - rhs, lhs.z - rhs); + } + + inline friend TVector3 operator - (const T lhs, const TVector3& rhs) { + return TVector3( lhs - rhs.x, lhs - rhs.y, lhs - rhs.z); + } + + inline friend TVector3 reflect(const TVector3& incident, const TVector3& normal) { + return incident - 2.f * normal.dotProduct(incident) * normal; + } +public: + // arithmetic updates + inline TVector3& operator += (const TVector3& rkVector) { + x += rkVector.x; + y += rkVector.y; + z += rkVector.z; + return *this; + } + + inline TVector3& operator += (const T fScaler) { + x += fScaler; + y += fScaler; + z += fScaler; + return *this; + } + + inline TVector3& operator -= (const TVector3& rkVector) { + x -= rkVector.x; + y -= rkVector.y; + z -= rkVector.z; + return *this; + } + + inline TVector3& operator -= (const T fScaler) { + x -= fScaler; + y -= fScaler; + z -= fScaler; + return *this; + } + + inline TVector3& operator *= (const T fScalar) { + x *= fScalar; + y *= fScalar; + z *= fScalar; + return *this; + } + + inline TVector3& operator *= (const TVector3& rkVector) { + x *= rkVector.x; + y *= rkVector.y; + z *= rkVector.z; + return *this; + } + + inline TVector3& operator /= (const T fScalar) { + assert(fScalar != 0.0); + + x /= fScalar; + y /= fScalar; + z /= fScalar; + return *this; + } + + inline TVector3& operator /= (const TVector3& rkVector) { + x /= rkVector.x; + y /= rkVector.y; + z /= rkVector.z; + return *this; + } + + inline bool operator < (const TVector3& rhs) const { + return (x < rhs.x && y < rhs.y && z < rhs.z); + } + + inline bool operator > (const TVector3& rhs) const { + return (x > rhs.x && y > rhs.y && z > rhs.z); + } + +public: + inline T length(void) const { + return MathUtil::sqrt(x * x + y * y + z * z); + } + + inline T squaredLength(void) const { + return x * x + y * y + z * z; + } + + inline T distance(const TVector3& rhs) const { + return (*this - rhs).length(); + } + + inline T squaredDistance(const TVector3& rhs) const { + return (*this - rhs).squaredLength(); + } + + inline T dotProduct(const TVector3& vec) const { + return x * vec.x + y * vec.y + z * vec.z; + } + + inline TVector3 crossProduct(const TVector3& rkVector) const { + return TVector3( + y * rkVector.z - z * rkVector.y, + z * rkVector.x - x * rkVector.z, + x * rkVector.y - y * rkVector.x); + } + + inline TVector3& normalise(void) { + T fLength = length(); + if (fLength > 0.0f) { + T invLength = 1.f / fLength; + x *= invLength; + y *= invLength; + z *= invLength; + } + + return *this; + } + + inline TVector3 normalise(void) const { + TVector3 result(x,y,z); + + T fLength = length(); + if (fLength > 0.0f) { + T invLength = 1.f / fLength; + result.x *= invLength; + result.y *= invLength; + result.z *= invLength; + } + + return result; + } + + inline TVector3& saturate(void) { + x = MathUtil::saturate(x); + y = MathUtil::saturate(y); + z = MathUtil::saturate(z); + + return *this; + } + +public: + // special points + static DV_CORE_API const TVector3 ZERO; + static DV_CORE_API const TVector3 ONE; + static DV_CORE_API const TVector3 UNIT_X; + static DV_CORE_API const TVector3 UNIT_Y; + static DV_CORE_API const TVector3 UNIT_Z; + static DV_CORE_API const TVector3 NEGATIVE_UNIT_X; + static DV_CORE_API const TVector3 NEGATIVE_UNIT_Y; + static DV_CORE_API const TVector3 NEGATIVE_UNIT_Z; + static DV_CORE_API const TVector3 UNIT_SCALE; + static DV_CORE_API const TVector3 WHITE; + static DV_CORE_API const TVector3 BLACK; + static DV_CORE_API const TVector3 RED; + static DV_CORE_API const TVector3 GREEN; + static DV_CORE_API const TVector3 BLUE; + static DV_CORE_API const TVector3 PURPLE; + static DV_CORE_API const TVector3 GRAY; +}; + +typedef TVector3 fVector3; + +DV_CORE_END_NAMESPACE diff --git a/source/core/include/math/dvc_vector4.h b/source/core/include/math/dvc_vector4.h new file mode 100644 index 0000000..7d43828 --- /dev/null +++ b/source/core/include/math/dvc_vector4.h @@ -0,0 +1,249 @@ +#pragma once + +#include "dvc_config.h" +#include "dvc_pre_declare.h" + +DV_CORE_BEGIN_NAMESPACE + +template +class TVector4 +{ +public: + T x, y, z, w; + +public: + // Construct + TVector4() : x(0.f), y(0.f), z(0.f), w(0.f) { } + TVector4(T _x, T _y, T _z, T _w) : x(_x), y(_y), z(_z), w(_w){ } + TVector4(const TVector3& _xyz, T _w) : x(_xyz.x), y(_xyz.y), z(_xyz.z), w(_w) { } + inline explicit TVector4(const T s) : x(s), y(s), z(s), w(s) { } + inline explicit TVector4(const T f[4]) : x(f[0]), y(f[1]), z(f[2]), w(f[3]) { } + inline explicit TVector4(T* const f) : x(f[0]), y(f[1]), z(f[2]), w(f[3]) { } + inline explicit TVector4(const int32_t f[4]) : x((T)f[0]), y((T)f[1]), z((T)f[2]), w((T)f[3]) { } + //inline explicit TVector4(const uint8_t* f) + // : x((T)(f[0] / T(std::numeric_limits::max()))) + // , y((T)(f[1] / T(std::numeric_limits::max()))) + // , z((T)(f[2] / T(std::numeric_limits::max()))) + // , w((T)(f[3] / T(std::numeric_limits::max()))) + //{ + //} + //inline explicit TVector4(const uint16_t* f) + // : x((T)(f[0] / T(std::numeric_limits::max()))) + // , y((T)(f[1] / T(std::numeric_limits::max()))) + // , z((T)(f[2] / T(std::numeric_limits::max()))) + // , w((T)(f[3] / T(std::numeric_limits::max()))) + //{ + //} +public: + inline T operator [] (const size_t i) const { + assert(i < 4); + return *(&x + i); + } + + inline T& operator [] (const size_t i) { + assert(i < 4); + return *(&x + i); + } + + inline T* ptr(void) { + return &x; + } + + inline const T* ptr(void) const { + return &x; + } + + inline TVector3 xyz(void) const { return TVector3(x, y, z); } + +public: + // Operations + inline TVector4& operator = (const TVector4& rkVector) { + x = rkVector.x; + y = rkVector.y; + z = rkVector.z; + w = rkVector.w; + return *this; + } + + inline TVector4& operator = (const T fScalar) { + x = fScalar; + y = fScalar; + z = fScalar; + w = fScalar; + return *this; + } + + inline bool operator == (const TVector4& rkVector) const { + return (x == rkVector.x && y == rkVector.y && z == rkVector.z && w == rkVector.w); + } + + inline bool operator != (const TVector4& rkVector) const { + return (x != rkVector.x || y != rkVector.y || z != rkVector.z || w != rkVector.w); + } + +public: + // Arithmetic operations + inline TVector4 operator + (const TVector4& rkVector) const { + return TVector4( x + rkVector.x, y + rkVector.y, z + rkVector.z, w + rkVector.w); + } + + inline TVector4 operator - (const TVector4& rkVector) const { + return TVector4( x - rkVector.x, y - rkVector.y, z - rkVector.z, w - rkVector.w); + } + + inline TVector4 operator * (const T fScalar) const { + return TVector4( x * fScalar, y * fScalar, z * fScalar, w * fScalar); + } + + inline TVector4 operator * (const TVector4& rhs) const { + return TVector4(x * rhs.x, y * rhs.y, z * rhs.z, w * rhs.w); + } + + inline TVector4 operator / (const T fScalar) const { + assert(fScalar != 0.0); + return TVector4(x / fScalar, y / fScalar, z / fScalar, w / fScalar); + } + + inline TVector4 operator / (const TVector4& rhs) const { + return TVector4( x / rhs.x, y / rhs.y, z / rhs.z, w / rhs.w); + } + + inline const TVector4& operator + () const { + return *this; + } + + inline TVector4 operator - () const { + return TVector4(-x, -y, -z, -w); + } + +public: + // overloaded operators to help TVector4 + inline friend TVector4 operator * (const T fScalar, const TVector4& rkVector) { + return TVector4(fScalar * rkVector.x, fScalar * rkVector.y, fScalar * rkVector.z, fScalar * rkVector.w); + } + + inline friend TVector4 operator / (const T fScalar, const TVector4& rkVector) { + return TVector4( fScalar / rkVector.x, fScalar / rkVector.y, fScalar / rkVector.z, fScalar / rkVector.w); + } + + inline friend TVector4 operator + (const TVector4& lhs, const T rhs) { + return TVector4( lhs.x + rhs, lhs.y + rhs, lhs.z + rhs, lhs.w + rhs); + } + + inline friend TVector4 operator + (const T lhs, const TVector4& rhs) { + return TVector4( lhs + rhs.x, lhs + rhs.y, lhs + rhs.z, lhs + rhs.w); + } + + inline friend TVector4 operator - (const TVector4& lhs, const T rhs) { + return TVector4( lhs.x - rhs, lhs.y - rhs, lhs.z - rhs, lhs.w - rhs); + } + + inline friend TVector4 operator - (const T lhs, const TVector4& rhs) { + return TVector4( lhs - rhs.x, lhs - rhs.y, lhs - rhs.z, lhs - rhs.z); + } + +public: + // arithmetic updates + inline TVector4& operator += (const TVector4& rkVector) { + x += rkVector.x; + y += rkVector.y; + z += rkVector.z; + w += rkVector.w; + return *this; + } + + inline TVector4& operator += (const T fScaler) { + x += fScaler; + y += fScaler; + z += fScaler; + w += fScaler; + return *this; + } + + inline TVector4& operator -= (const TVector4& rkVector) { + x -= rkVector.x; + y -= rkVector.y; + z -= rkVector.z; + w -= rkVector.w; + return *this; + } + + inline TVector4& operator -= (const T fScaler) { + x -= fScaler; + y -= fScaler; + z -= fScaler; + w -= fScaler; + return *this; + } + + inline TVector4& operator *= (const T fScalar) { + x *= fScalar; + y *= fScalar; + z *= fScalar; + w *= fScalar; + return *this; + } + + inline TVector4& operator *= (const TVector4& rkVector) { + x *= rkVector.x; + y *= rkVector.y; + z *= rkVector.z; + w *= rkVector.w; + return *this; + } + + inline TVector4& operator /= (const T fScalar) { + assert(fScalar != 0.0); + + x /= fScalar; + y /= fScalar; + z /= fScalar; + w /= fScalar; + return *this; + } + + inline TVector4& operator /= (const TVector4& rkVector) { + x /= rkVector.x; + y /= rkVector.y; + z /= rkVector.z; + w /= rkVector.w; + return *this; + } + + inline bool operator < (const TVector4& rhs) const { + return (x < rhs.x && y < rhs.y && z < rhs.z && w < rhs.w); + } + + inline bool operator > (const TVector4& rhs) const { + return (x > rhs.x && y > rhs.y && z > rhs.z && w > rhs.w); + } + +public: + + inline T dotProduct(const TVector4& vec) const { + return (x * vec.x + y * vec.y) + (z * vec.z + w * vec.w); + } + + inline TVector4& saturate(void) { + x = MathUtil::saturate(x); + y = MathUtil::saturate(y); + z = MathUtil::saturate(z); + w = MathUtil::saturate(w); + + return *this; + } +public: + // special points + static DV_CORE_API const TVector4 ZERO; + static DV_CORE_API const TVector4 WHITE; + static DV_CORE_API const TVector4 BLACK; + static DV_CORE_API const TVector4 RED; + static DV_CORE_API const TVector4 GREEN; + static DV_CORE_API const TVector4 BLUE; + static DV_CORE_API const TVector4 PURPLE; + static DV_CORE_API const TVector4 GRAY; +}; + +typedef TVector4 fVector4; + +DV_CORE_END_NAMESPACE diff --git a/source/core/include/pipe/dvc_rasterizer.h b/source/core/include/pipe/dvc_rasterizer.h new file mode 100644 index 0000000..cd5298d --- /dev/null +++ b/source/core/include/pipe/dvc_rasterizer.h @@ -0,0 +1,35 @@ +#pragma once + +#include "dvc_config.h" +#include "math/dvc_vector2.h" +#include "math/dvc_vector3.h" + +DV_CORE_BEGIN_NAMESPACE + +class DV_CORE_API Rasterizer +{ + //Draw Line +public: + typedef std::function&, float)> DrawLineCallback; + + //Bresenham's line algorithm + static void drawLine(int32_t canvasWidth, int32_t canvasHeight, + const fVector2& start, const fVector2& end, + DrawLineCallback callback); + + //Draw Triangle +public: + typedef std::function& pos, const fVector3& percent)> DrawTriangleCallback; + + //Larrabee algorithm + static void drawTriangleLarrabee(int32_t canvasWidth, int32_t canvasHeight, + const fVector2& v0, const fVector2& v1, const fVector2& v2, + DrawTriangleCallback callback, bool ccw=true); + + //Scaleline algorithm + static void drawTriangleScanline(int32_t canvasWidth, int32_t canvasHeight, + const fVector3& v0, const fVector3& v1, const fVector3& v2, + DrawTriangleCallback callback); +}; + +DV_CORE_END_NAMESPACE diff --git a/source/core/include/pipe/dvc_render_queue.h b/source/core/include/pipe/dvc_render_queue.h new file mode 100644 index 0000000..c4aa0dc --- /dev/null +++ b/source/core/include/pipe/dvc_render_queue.h @@ -0,0 +1,30 @@ +#pragma once + +#include "dvc_config.h" +#include "scene/dvc_scene.h" +#include "device/dvc_render_target.h" + +DV_CORE_BEGIN_NAMESPACE + +class DV_CORE_API RenderQueue +{ +public: + void pushRenderable(RenderableConstPtr renderable); + + size_t getRenderableCounts() const { + return m_queue.size(); + } + RenderableConstPtr getRenderable(size_t index) const; + + void build(const Scene& scene); + void process(UniformBuffer& uniformBuffer, RenderTarget& renderTarget); + +protected: + std::vector m_queue; + +public: + RenderQueue(); + ~RenderQueue(); +}; + +DV_CORE_END_NAMESPACE diff --git a/source/core/include/pipe/dvc_render_step_IA.h b/source/core/include/pipe/dvc_render_step_IA.h new file mode 100644 index 0000000..e3ee92f --- /dev/null +++ b/source/core/include/pipe/dvc_render_step_IA.h @@ -0,0 +1,35 @@ +#pragma once + +#include "dvc_config.h" +#include "math/dvc_matrix4.h" +#include "dvc_render_queue.h" + +DV_CORE_BEGIN_NAMESPACE + +class InputAssemberStep +{ +public: + typedef std::map PrimitiveAttributes; + + struct Node + { + fMatrix4 transform; + PrimitiveType primitiveType; + PrimitiveAttributes vertexData; + DeviceBufferAccessorConstPtr indices; + VertexShaderInterface* vs; + }; + typedef std::vector NodeArray; + +public: + void process(const RenderQueue& renderQueue); + const NodeArray& getNodes(void) const { + return m_nodes; + } + +protected: + NodeArray m_nodes; +}; + + +DV_CORE_END_NAMESPACE diff --git a/source/core/include/pipe/dvc_render_step_PS.h b/source/core/include/pipe/dvc_render_step_PS.h new file mode 100644 index 0000000..df3c41a --- /dev/null +++ b/source/core/include/pipe/dvc_render_step_PS.h @@ -0,0 +1,15 @@ +#pragma once + +#include "dvc_config.h" +#include "dvc_render_step_VS.h" + +DV_CORE_BEGIN_NAMESPACE + +class PixelShaderStep +{ +public: + void process(const VertexShaderStep::NodeArray& vsNodeArray, RenderTarget& renderTarget); + +}; + +DV_CORE_END_NAMESPACE diff --git a/source/core/include/pipe/dvc_render_step_VS.h b/source/core/include/pipe/dvc_render_step_VS.h new file mode 100644 index 0000000..6e7ef5b --- /dev/null +++ b/source/core/include/pipe/dvc_render_step_VS.h @@ -0,0 +1,35 @@ +#pragma once + +#include "dvc_config.h" +#include "dvc_render_step_IA.h" +#include "device/dvc_uniform_buffer.h" +#include "device/dvc_vertex_desc.h" + +DV_CORE_BEGIN_NAMESPACE + +class VertexShaderStep +{ +public: + struct Node + { + DeviceBufferPtr vertexBuf; + VertexDesc vertexDesc; + size_t vertexCounts; + std::vector depth; + std::vector invZ; + }; + typedef std::vector NodeArray; + +public: + const NodeArray& getNodes() const { + return m_nodes; + } + +public: + void process(UniformBuffer& uniformBuffer, const InputAssemberStep::NodeArray& assembedNodeArray); + +protected: + NodeArray m_nodes; +}; + +DV_CORE_END_NAMESPACE diff --git a/source/core/include/pipe/dvc_renderable.h b/source/core/include/pipe/dvc_renderable.h new file mode 100644 index 0000000..63b8151 --- /dev/null +++ b/source/core/include/pipe/dvc_renderable.h @@ -0,0 +1,21 @@ +#pragma once + +#include "dvc_config.h" +#include "math/dvc_matrix4.h" +#include "pipe/dvc_render_step_IA.h" + +DV_CORE_BEGIN_NAMESPACE + +class Renderable +{ +public: + virtual bool buildAssemberNode(InputAssemberStep::Node& node) const = 0; + +protected: + fMatrix4 m_worldTransform; + +public: + Renderable(const fMatrix4& worldTransform); +}; + +DV_CORE_END_NAMESPACE diff --git a/source/core/include/pipe/dvc_renderable_entity.h b/source/core/include/pipe/dvc_renderable_entity.h new file mode 100644 index 0000000..2142d17 --- /dev/null +++ b/source/core/include/pipe/dvc_renderable_entity.h @@ -0,0 +1,21 @@ +#pragma once + +#include "dvc_config.h" +#include "dvc_renderable.h" +#include "asset/dvc_mesh.h" + +DV_CORE_BEGIN_NAMESPACE + +class RenderableEntity : public Renderable +{ +public: + virtual bool buildAssemberNode(InputAssemberStep::Node& node) const; + +protected: + Mesh::PrimitiveConstPtr m_primitive; + +public: + RenderableEntity(Mesh::PrimitiveConstPtr primitive, const fMatrix4& worldTransform); +}; + +DV_CORE_END_NAMESPACE diff --git a/source/core/include/scene/component/dvc_camera_component.h b/source/core/include/scene/component/dvc_camera_component.h new file mode 100644 index 0000000..6bc8111 --- /dev/null +++ b/source/core/include/scene/component/dvc_camera_component.h @@ -0,0 +1,52 @@ +#pragma once + +#include "dvc_component.h" +#include "math/dvc_vector3.h" +#include "math/dvc_matrix4.h" + +DV_CORE_BEGIN_NAMESPACE + +class DV_CORE_API CameraComponent : public SceneComponent +{ +public: + struct CameraDefine + { + float32_t fov; + float32_t aspect; + float32_t nearClip; + float32_t farClip; + }; +public: + virtual const std::string& getTypeName() const { return typeName(); } + virtual void render(const fMatrix4& transParent, RenderQueue& queue) const; + + fVector3 getEye(void) const; + fVector3 getLookat(void) const; + fVector3 getUp(void) const; + + void setCameraDefine(const CameraDefine& cameraDefine); + CameraDefine getCameraDefine() const { + return m_cameraDefine; + } + + const fMatrix4& getViewMatrix(void) const { + return m_viewMatrix; + } + + const fMatrix4& getViewProjMatrix(void) const { + return m_viewProjMatrix; + } + +protected: + CameraDefine m_cameraDefine; + fMatrix4 m_viewMatrix; + fMatrix4 m_viewProjMatrix; + +public: + CameraComponent(SceneNodePtr owner); + virtual ~CameraComponent(); + + static const std::string& typeName(); +}; + +DV_CORE_END_NAMESPACE diff --git a/source/core/include/scene/component/dvc_component.h b/source/core/include/scene/component/dvc_component.h new file mode 100644 index 0000000..c0fc45d --- /dev/null +++ b/source/core/include/scene/component/dvc_component.h @@ -0,0 +1,22 @@ +#pragma once + +#include "dvc_config.h" +#include "math/dvc_matrix4.h" + +DV_CORE_BEGIN_NAMESPACE + +class DV_CORE_API SceneComponent +{ +public: + virtual const std::string& getTypeName() const = 0; + virtual void render(const fMatrix4& transParent, RenderQueue& queue) const; + +protected: + SceneNodePtr m_owner; + +public: + SceneComponent(SceneNodePtr owner); + virtual ~SceneComponent(); +}; + +DV_CORE_END_NAMESPACE diff --git a/source/core/include/scene/component/dvc_light_component.h b/source/core/include/scene/component/dvc_light_component.h new file mode 100644 index 0000000..fa98a3c --- /dev/null +++ b/source/core/include/scene/component/dvc_light_component.h @@ -0,0 +1,43 @@ +#pragma once + +#include "dvc_component.h" +#include "math/dvc_vector3.h" +//#include + +DV_CORE_BEGIN_NAMESPACE + +class DV_CORE_API LightComponent : public SceneComponent +{ +public: + enum LightType + { + LT_Point, + LT_Directional, + LT_Spot, + }; + struct LightDefine + { + LightType type; + fVector3 color; //default is "1.0, 1.0, 1.0" + float32_t intensity; //default is "1.0" + float32_t range; //default is "-1", means infinite + + float32_t innerConeAngle; //only for spot light, default is 0 + float32_t outerConeAngle; //only for spot light, default is PI/4.0 + }; +public: + virtual const std::string& getTypeName() const { return typeName(); } + virtual void render(const fMatrix4& transParent, RenderQueue& queue) const; + + void setLightDefine(const LightDefine& lightDefine); +protected: + LightDefine m_define; + +public: + LightComponent(SceneNodePtr owner); + virtual ~LightComponent(); + + static const std::string& typeName(); +}; + +DV_CORE_END_NAMESPACE diff --git a/source/core/include/scene/component/dvc_mesh_component.h b/source/core/include/scene/component/dvc_mesh_component.h new file mode 100644 index 0000000..3dd9c13 --- /dev/null +++ b/source/core/include/scene/component/dvc_mesh_component.h @@ -0,0 +1,26 @@ +#pragma once + +#include "dvc_component.h" +#include "dvc_pre_declare.h" + +DV_CORE_BEGIN_NAMESPACE + +class DV_CORE_API MeshComponent : public SceneComponent +{ +public: + virtual const std::string& getTypeName() const { return typeName(); } + virtual void render(const fMatrix4& transParent, RenderQueue& queue) const; + + void setMesh(MeshConstPtr mesh); + +protected: + MeshConstPtr m_mesh; + +public: + MeshComponent(SceneNodePtr owner); + virtual ~MeshComponent(); + + static const std::string& typeName(); +}; + +DV_CORE_END_NAMESPACE diff --git a/source/core/include/scene/dvc_scene.h b/source/core/include/scene/dvc_scene.h new file mode 100644 index 0000000..5c9e40b --- /dev/null +++ b/source/core/include/scene/dvc_scene.h @@ -0,0 +1,95 @@ +#pragma once + +#include "dvc_config.h" +#include "dvc_pre_declare.h" + +DV_CORE_BEGIN_NAMESPACE + +class DV_CORE_API Scene +{ +public: + void init(void); + void clear(void); + +public: + DeviceBufferPtr newDeviceBuffer(void); + int32_t getDeviceBufferCounts(void) const { + return (int32_t)m_deviceBufferArray.size(); + } + DeviceBufferConstPtr getDeviceBuffer(int32_t index) const; + + DeviceBufferViewPtr newDeviceBufferView(DeviceBufferConstPtr buffer, size_t offset, size_t length, size_t stride); + int32_t getDeviceBufferViewCounts(void) const { + return (int32_t)m_bufferViewArray.size(); + } + DeviceBufferViewPtr getDeviceBufferView(int32_t index) const; + + DeviceBufferAccessorPtr newDeviceBufferAccessor(DeviceBufferViewConstPtr bufferView, size_t offset, + size_t count, ComponentType componentType, DataType dataType); + int32_t getDeviceBufferAccessorCounts(void) const { + return (int32_t)m_bufferAccessorArray.size(); + } + DeviceBufferAccessorPtr getDeviceBufferAccessor(int32_t index) const; + + int32_t pushNewMeshAsset(MeshPtr mesh); + int32_t getMeshCounts(void) const { + return (int32_t)m_meshArray.size(); + } + MeshConstPtr getMesh(int32_t index) const; + +public: + SceneNodePtr newSceneNode(const std::string& name); + SceneNodePtr getRootSceneNode() const; + +public: + template + SceneNodeConstPtr getNodeWithComponent(const char* name = nullptr) const + { + return getNodeWithComponent(name, getRootSceneNode()); + } + + template + SceneNodeConstPtr getNodeWithComponent(const char* name, SceneNodeConstPtr node) const + { + size_t counts = node->getChildCounts(); + for (size_t i = 0; i < counts; i++) + { + SceneNodeConstPtr childNodePtr = node->getChild(i); + if (name != nullptr && childNodePtr->getName() != name) + { + continue; + } + + std::shared_ptr component = childNodePtr->getComponent(); + if (component) + { + return childNodePtr; + } + + //get from child + SceneNodeConstPtr cameraNode = getNodeWithComponent(name, childNodePtr); + if (cameraNode != nullptr) + { + return cameraNode; + } + } + return nullptr; + } + +protected: + SceneNodePtr m_rootSceneNode; + + //device resources + std::vector m_deviceBufferArray; + std::vector m_bufferViewArray; + std::vector m_bufferAccessorArray; + + //asset resources + std::vector m_meshArray; + +public: + Scene(); + ~Scene(); +}; + +DV_CORE_END_NAMESPACE diff --git a/source/core/include/scene/dvc_scene_node.h b/source/core/include/scene/dvc_scene_node.h new file mode 100644 index 0000000..f33b993 --- /dev/null +++ b/source/core/include/scene/dvc_scene_node.h @@ -0,0 +1,73 @@ +#pragma once + +#include "dvc_config.h" +#include "math/dvc_matrix4.h" +#include "math/dvc_vector3.h" +#include "dvc_pre_declare.h" + +DV_CORE_BEGIN_NAMESPACE + +class DV_CORE_API SceneNode : public std::enable_shared_from_this +{ +public: + void clear(void); + void addChild(SceneNodePtr child); + void addComponent(SceneComponentPtr component); + + void setName(const std::string& name); + const std::string& getName() const; + + void setParent(SceneNodePtr parent); + + void setTransform(const fMatrix4& transform); + const fMatrix4& getTransform() const { + return m_transform; + } + fMatrix4 getWorldTransform() const; + + fVector3 getPosition() const; + fVector3 getWorldPosition() const; + + size_t getChildCounts() const { + return m_childen.size(); + } + + SceneNodeConstPtr getChild(size_t index) const; + + size_t getComponentCounts() const { + return m_componentArray.size(); + } + + SceneComponentConstPtr getComponent(size_t index) const; + + template + const std::shared_ptr getComponent() const + { + for (size_t i = 0; i < getComponentCounts(); i++) + { + auto component = getComponent(i); + if (component->getTypeName() == T::typeName()) + { + return std::dynamic_pointer_cast(component); + } + } + return nullptr; + } + +public: + void buildRenderQueue(const fMatrix4& transParent, RenderQueue& queue); + +protected: + std::string m_name; + fMatrix4 m_transform; + SceneNodePtr m_parent; + SceneNodeArray m_childen; + SceneComponentArray m_componentArray; + +public: + SceneNode(); + virtual ~SceneNode(); +}; + +DV_CORE_END_NAMESPACE + diff --git a/source/core/include/shader/dvc_shader_interface.h b/source/core/include/shader/dvc_shader_interface.h new file mode 100644 index 0000000..a254dfb --- /dev/null +++ b/source/core/include/shader/dvc_shader_interface.h @@ -0,0 +1,46 @@ +#pragma once + +#include "dvc_config.h" +#include "dvc_pre_declare.h" +#include "math/dvc_vector3.h" +#include "math/dvc_vector4.h" +#include "device/dvc_vertex_desc.h" + +DV_CORE_BEGIN_NAMESPACE + +class VertexShaderInterface +{ +public: + virtual const char* getName(void) const = 0; + virtual const VertexDesc& getOutputVertexDesc() const = 0; + + typedef std::function SetInputAttributeFunc; + virtual void prepare(const UniformBuffer* uniformBuffer, SetInputAttributeFunc setInputAttributeFunc) = 0; + + virtual void process(size_t index, void* outputVertex, fVector3& vertexWorldPos) = 0; +}; + +class PixelShaderInterface +{ +public: + virtual const char* getName(void) const = 0; + + typedef std::function SetInputAttributeFunc; + virtual void prepare(const UniformBuffer* uniformBuffer, SetInputAttributeFunc setInputAttributeFunc) = 0; + + virtual void process(const VertexDesc& veretexDesc, const float32_t* pixelData, fVector4& color) = 0; +}; + +class ShaderFileInterface +{ +public: + virtual VertexShaderInterface* getVertexShader(const char* szName) = 0; + virtual PixelShaderInterface* getPixelShader(const char* szName) = 0; +}; + +typedef ShaderFileInterface* (*ShaderFileInterfaceFunc)(); + +DV_CORE_API ShaderFileInterface* LoadShaderFile(const char* szShaderFile); + +DV_CORE_END_NAMESPACE + diff --git a/source/core/source/asset/dvc_mesh.cpp b/source/core/source/asset/dvc_mesh.cpp new file mode 100644 index 0000000..14ffefc --- /dev/null +++ b/source/core/source/asset/dvc_mesh.cpp @@ -0,0 +1,31 @@ +#include "asset/dvc_mesh.h" + +DV_CORE_BEGIN_NAMESPACE + +Mesh::Mesh(const std::string& name) + : m_name(name) +{ +} + +Mesh::~Mesh() +{ + m_primitiveArray.clear(); +} + +Mesh::PrimitivePtr Mesh::newPrimitive(void) +{ + PrimitivePtr primitive = std::make_shared(); + m_primitiveArray.push_back(primitive); + return primitive; +} + +Mesh::PrimitiveConstPtr Mesh::getPrimitive(int32_t index) const +{ + if (index < 0 || index >= m_primitiveArray.size()) + { + return nullptr; + } + return m_primitiveArray[index]; +} + +DV_CORE_END_NAMESPACE diff --git a/source/core/source/device/dvc_buffer.cpp b/source/core/source/device/dvc_buffer.cpp new file mode 100644 index 0000000..f75a0d8 --- /dev/null +++ b/source/core/source/device/dvc_buffer.cpp @@ -0,0 +1,43 @@ +#include "device/dvc_buffer.h" + +DV_CORE_BEGIN_NAMESPACE + +DeviceBuffer::DeviceBuffer() + : m_ptr(nullptr) + , m_length(0) +{ +} + +DeviceBuffer::DeviceBuffer(size_t _length) + : m_ptr(nullptr) + , m_length(0) +{ + init(_length); +} + +DeviceBuffer::~DeviceBuffer() +{ + clear(); +} + +void DeviceBuffer::init(size_t _length) +{ + assert(_length > 0); + clear(); + + m_ptr = (uint8_t*)malloc(_length); + memset(m_ptr, 0, _length); + m_length = _length; +} + +void DeviceBuffer::clear(void) +{ + if (m_ptr) { + free(m_ptr); + m_ptr = nullptr; + } + m_length = 0; +} + +DV_CORE_END_NAMESPACE + diff --git a/source/core/source/device/dvc_buffer_accessor.cpp b/source/core/source/device/dvc_buffer_accessor.cpp new file mode 100644 index 0000000..b480839 --- /dev/null +++ b/source/core/source/device/dvc_buffer_accessor.cpp @@ -0,0 +1,93 @@ +#include "device/dvc_buffer_accessor.h" +#include "math/dvc_math_util.h" + +DV_CORE_BEGIN_NAMESPACE + +DeviceBufferAccessor::DeviceBufferAccessor(DeviceBufferViewConstPtr bufferView, size_t offset, size_t count, ComponentType componentType, DataType dataType) + : m_bufferView(bufferView) + , m_offset(offset) + , m_count(count) + , m_componentType(componentType) + , m_dataType(dataType) +{ + m_elementSize = MathUtil::DataTypeSize(m_dataType) * MathUtil::ComponentSize(m_componentType); +} + +DeviceBufferAccessor::~DeviceBufferAccessor() +{ + m_bufferView.reset(); +} + +void DeviceBufferAccessor::walk(std::function callback, size_t start, size_t end) const +{ + assert(start < m_count); + assert((end == ((size_t)-1)) || (end>=start && end <=m_count)); + + DeviceBufferConstPtr buffer = m_bufferView->getBuffer(); + const uint8_t* ptr = buffer->ptr(m_bufferView->getOffset()) + m_offset; + + size_t startIndex = start; + size_t endIndex = (end == ((size_t)-1)) ? m_count : end; + for (size_t index = startIndex; index < endIndex; index++, ptr+=m_elementSize) + { + if (callback(ptr, index)) + { + break; + } + } +} + +void DeviceBufferAccessor::walk2(std::function callback, size_t start, size_t end) const +{ + assert(start < m_count); + assert((end == ((size_t)-1)) || (end >= start && end <= m_count)); + + DeviceBufferConstPtr buffer = m_bufferView->getBuffer(); + const uint8_t* ptr = buffer->ptr(m_bufferView->getOffset()) + m_offset; + + size_t startIndex = start; + size_t endIndex = (end == ((size_t)-1)) ? m_count : end; + for (size_t index = startIndex; index < endIndex; index+=2, ptr+=m_elementSize*2) + { + if (callback(ptr, ((index + 1) < endIndex) ? (ptr + m_elementSize) : nullptr, index)) + { + break; + } + } +} + +void DeviceBufferAccessor::walk3(std::function callback, size_t start, size_t end) const +{ + assert(start < m_count); + assert((end == ((size_t)-1)) || (end >= start && end <= m_count)); + + DeviceBufferConstPtr buffer = m_bufferView->getBuffer(); + const uint8_t* ptr = buffer->ptr(m_bufferView->getOffset()) + m_offset; + + size_t startIndex = start; + size_t endIndex = (end == ((size_t)-1)) ? m_count : end; + for (size_t index = startIndex; index < endIndex; index += 3, ptr += m_elementSize * 3) + { + if(callback(ptr, + ((index + 1) < endIndex) ? (ptr + m_elementSize) : nullptr, + ((index + 2) < endIndex) ? (ptr + m_elementSize*2) : nullptr, + index)) + { + break; + } + } +} + +const uint8_t* DeviceBufferAccessor::getElement(size_t index) const +{ + if (index >= m_count) { + return nullptr; + } + + DeviceBufferConstPtr buffer = m_bufferView->getBuffer(); + const uint8_t* pData = buffer->ptr(m_bufferView->getOffset()) + m_offset + index * m_elementSize; + + return pData; +} + +DV_CORE_END_NAMESPACE diff --git a/source/core/source/device/dvc_buffer_view.cpp b/source/core/source/device/dvc_buffer_view.cpp new file mode 100644 index 0000000..a736893 --- /dev/null +++ b/source/core/source/device/dvc_buffer_view.cpp @@ -0,0 +1,21 @@ +#include "device/dvc_buffer_view.h" + +DV_CORE_BEGIN_NAMESPACE + +DeviceBufferView::DeviceBufferView(DeviceBufferConstPtr buffer, size_t offset, size_t length, size_t stride) + : m_buffer(buffer) + , m_offset(offset) + , m_length(length) + , m_stride(stride) +{ + +} + +DeviceBufferView::~DeviceBufferView() +{ + m_buffer.reset(); + m_offset = m_length = m_stride = 0; +} + +DV_CORE_END_NAMESPACE + diff --git a/source/core/source/device/dvc_pixel_buffer.cpp b/source/core/source/device/dvc_pixel_buffer.cpp new file mode 100644 index 0000000..f739b3f --- /dev/null +++ b/source/core/source/device/dvc_pixel_buffer.cpp @@ -0,0 +1,5 @@ +#include "device/dvc_pixel_buffer.h" + +DV_CORE_BEGIN_NAMESPACE + +DV_CORE_END_NAMESPACE diff --git a/source/core/source/device/dvc_render_target.cpp b/source/core/source/device/dvc_render_target.cpp new file mode 100644 index 0000000..6db705d --- /dev/null +++ b/source/core/source/device/dvc_render_target.cpp @@ -0,0 +1,34 @@ +#include "device/dvc_render_target.h" + +DV_CORE_BEGIN_NAMESPACE + +RenderTarget::RenderTarget() +{ +} + +void RenderTarget::init(int32_t width, int32_t height) +{ + m_width = width; + m_height = height; + m_colorBuffer.init(width, height, fVector4::BLACK); + m_depthBuffer.init(width, height, std::numeric_limits::max()); +} + +void RenderTarget::clear() +{ + m_colorBuffer.clear(fVector4::BLACK); + m_depthBuffer.clear(std::numeric_limits::max()); +} + +void RenderTarget::setPixel(int32_t x, int32_t y, const fVector4& color, float32_t depth) +{ + if (x < 0 || x >= m_width || y < 0 || y >= m_height) return; + + if (depth > m_depthBuffer.getPixel(x, y)) { + return; + } + m_colorBuffer.setPixel(x, y, color); + m_depthBuffer.setPixel(x, y, depth); +} + +DV_CORE_END_NAMESPACE diff --git a/source/core/source/device/dvc_uniform_buffer.cpp b/source/core/source/device/dvc_uniform_buffer.cpp new file mode 100644 index 0000000..4bf3af5 --- /dev/null +++ b/source/core/source/device/dvc_uniform_buffer.cpp @@ -0,0 +1,130 @@ +#include "device/dvc_uniform_buffer.h" +#include "math/dvc_math_util.h" +#include "scene/dvc_scene.h" +#include "scene/dvc_scene_node.h" +#include "scene/component/dvc_camera_component.h" + +DV_CORE_BEGIN_NAMESPACE + +void UniformBuffer::clear() +{ + for (AttributeMap::iterator it = m_attributeMap.begin(); it != m_attributeMap.end(); ++it) + { + AttributeValue& value = it->second; + freeMemory(value.value); value.value = nullptr; + } + m_attributeMap.clear(); +} + +void UniformBuffer::updateStandardAttribute(const Scene& scene) +{ + //Find main camera + davinci::SceneNodeConstPtr cameraNode = scene.getNodeWithComponent(); + assert(cameraNode.get()); + + CameraComponentConstPtr camera = cameraNode->getComponent(); + if (camera) + { + setAttribute("camera:eye_pos", camera->getEye()); + setAttribute("camera:near", camera->getCameraDefine().nearClip); + setAttribute("camera:far", camera->getCameraDefine().farClip); + setAttribute("camera:view_mat", camera->getViewMatrix()); + setAttribute("camera:view_proj_mat", camera->getViewProjMatrix()); + } +} + +void UniformBuffer::setAttribute(const std::string& name, ComponentType componentType, DataType dataType, const void* value) +{ + size_t valueSize = MathUtil::ComponentSize(componentType) * MathUtil::DataTypeSize(dataType); + + AttributeMap::iterator it = m_attributeMap.find(name); + if (it != m_attributeMap.end()) + { + if (it->second.componentType != componentType || it->second.dataType != dataType) + { + //TODO: error type + return; + } + + memcpy(it->second.value, value, valueSize); + return; + } + + //insert new value + AttributeValue newValue; + newValue.name = name; + newValue.componentType = componentType; + newValue.dataType = dataType; + newValue.value = mallocMemory(valueSize); + memcpy(newValue.value, value, valueSize); + + m_attributeMap.insert(std::make_pair(name, newValue)); +} + +void UniformBuffer::setAttribute(const std::string& name, const int32_t& value) +{ + setAttribute(name, ComponentType::CT_Int32, DataType::DT_Scalar, &value); +} + +void UniformBuffer::setAttribute(const std::string& name, const float32_t& value) +{ + setAttribute(name, ComponentType::CT_Float32, DataType::DT_Scalar, &value); +} + +void UniformBuffer::setAttribute(const std::string& name, const fVector2& value) +{ + setAttribute(name, ComponentType::CT_Float32, DataType::DT_Vec2, &value); +} + +void UniformBuffer::setAttribute(const std::string& name, const fVector3& value) +{ + setAttribute(name, ComponentType::CT_Float32, DataType::DT_Vec3, &value); +} + +void UniformBuffer::setAttribute(const std::string& name, const fVector4& value) +{ + setAttribute(name, ComponentType::CT_Float32, DataType::DT_Vec4, &value); +} + +void UniformBuffer::setAttribute(const std::string& name, const fMatrix3& value) +{ + setAttribute(name, ComponentType::CT_Float32, DataType::DT_Matrix3, &value); +} + +void UniformBuffer::setAttribute(const std::string& name, const fMatrix4& value) +{ + setAttribute(name, ComponentType::CT_Float32, DataType::DT_Matrix4, &value); +} + +void* UniformBuffer::getAttribute(const std::string& name) const +{ + AttributeMap::const_iterator it = m_attributeMap.find(name); + if (it == m_attributeMap.end()) + { + return nullptr; + } + return it->second.value; +} + +void UniformBuffer::removeAttribute(const std::string& name) +{ + AttributeMap::iterator it = m_attributeMap.find(name); + if (it != m_attributeMap.end()) + { + freeMemory(it->second.value); it->second.value = nullptr; + m_attributeMap.erase(it); + return; + } +} + +void* UniformBuffer::mallocMemory(size_t size) +{ + return malloc(size); +} + +void UniformBuffer::freeMemory(void* memory) +{ + free(memory); +} + +DV_CORE_END_NAMESPACE diff --git a/source/core/source/device/dvc_vertex_desc.cpp b/source/core/source/device/dvc_vertex_desc.cpp new file mode 100644 index 0000000..991db49 --- /dev/null +++ b/source/core/source/device/dvc_vertex_desc.cpp @@ -0,0 +1,107 @@ +#include "device/dvc_vertex_desc.h" +#include "math/dvc_math_util.h" + +DV_CORE_BEGIN_NAMESPACE + +VertexDesc::VertexDesc() + : m_vertexSize(0) +{ +} + +VertexDesc::VertexDesc(const VertexDesc& other) +{ + m_elements = other.m_elements; + m_vertexSize = other.m_vertexSize; + m_elementTypeMap = other.m_elementTypeMap; + m_nameMap = other.m_nameMap; +} + +bool VertexDesc::addElement(const std::string& name, VertexElementType elementType, ComponentType componentType, DataType dataType) +{ + bool defineType = (elementType != VertexElementType::VET_UNKNOWN); + bool defineName = (name.length() > 0); + assert(defineType || defineName); + if (!(defineType || defineName)) + { + return false; + } + + bool conflictType = defineType && (m_elementTypeMap.find(elementType) != m_elementTypeMap.end()); + assert(!conflictType); + if (conflictType) + { + return false; + } + + bool conflictName = defineName && (m_nameMap.find(name) != m_nameMap.end()); + assert(!conflictName); + if (conflictName) + { + return false; + } + + assert(componentType != ComponentType::CT_UNKNOWN); + if (componentType == ComponentType::CT_UNKNOWN) + { + return false; + } + + assert(dataType != DataType::DT_UNKNOWN); + if (dataType == DataType::DT_UNKNOWN) + { + return false; + } + + Element element; + element.name = name; + element.elementType = elementType; + element.componentType = componentType; + element.dataType = dataType; + element.offset = m_vertexSize; + element.size = MathUtil::DataTypeSize(element.dataType) * MathUtil::ComponentSize(element.componentType); + + m_vertexSize += element.size; + + if (defineType) + { + m_elementTypeMap.insert(std::make_pair(elementType, m_elements.size())); + } + if (defineName) + { + m_nameMap.insert(std::make_pair(name, m_elements.size())); + } + + m_elements.push_back(element); + return true; +} + +const VertexDesc::Element* VertexDesc::getElement(size_t index) const +{ + if (index >= m_elements.size()) + { + return nullptr; + } + return &(m_elements[index]); +} + +const VertexDesc::Element* VertexDesc::getElement(VertexElementType type) const +{ + auto it = m_elementTypeMap.find(type); + if (it == m_elementTypeMap.end()) + { + return nullptr; + } + return getElement(it->second); +} + +const VertexDesc::Element* VertexDesc::getElement(const std::string& name) const +{ + auto it = m_nameMap.find(name); + if (it == m_nameMap.end()) + { + return nullptr; + } + return getElement(it->second); +} + +DV_CORE_END_NAMESPACE diff --git a/source/core/source/dvc_module.cpp b/source/core/source/dvc_module.cpp new file mode 100644 index 0000000..9fbdf60 --- /dev/null +++ b/source/core/source/dvc_module.cpp @@ -0,0 +1,7 @@ +#include "dv_config.h" +#include "dvc_prerequisites.h" + +DV_CORE_API int foo() +{ + return 0; +} diff --git a/source/core/source/math/dvc_math_util.cpp b/source/core/source/math/dvc_math_util.cpp new file mode 100644 index 0000000..cbf0d5e --- /dev/null +++ b/source/core/source/math/dvc_math_util.cpp @@ -0,0 +1,72 @@ +#include "math/dvc_math_util.h" + +DV_CORE_BEGIN_NAMESPACE + +const float MathUtil::POS_INFINITY = std::numeric_limits::infinity(); +const float MathUtil::NEG_INFINITY = -std::numeric_limits::infinity(); +const float MathUtil::PI = float(4.0 * atan(1.0)); +const float MathUtil::PI_X2 = float(2.0 * PI); +const float MathUtil::PI_DIV2 = float(0.5 * PI); +const float MathUtil::PI_DIV4 = float(0.25 * PI); +const float MathUtil::fDeg2Rad = PI / float(180.0); +const float MathUtil::fRad2Deg = float(180.0) / PI; + +size_t MathUtil::ComponentSize(ComponentType ct) +{ + switch (ct) + { + case CT_Int8: + return sizeof(int8_t); + case CT_Uint8: + return sizeof(uint8_t); + case CT_Int16: + return sizeof(int16_t); + case CT_Uint16: + return sizeof(uint16_t); + case CT_Int32: + return sizeof(int32_t); + case CT_Uint32: + return sizeof(uint32_t); + case CT_Float32: + return sizeof(float32_t); + case CT_Float64: + return sizeof(float64_t); + default: + break; + } + + assert(false); + return 0; +} + +size_t MathUtil::DataTypeSize(DataType dt) +{ + switch (dt) + { + case DT_Scalar: + return 1; + case DT_Vec2: + return 2; + case DT_Vec3: + return 3; + case DT_Vec4: + return 4; + case DT_Matrix2: + return 4; + case DT_Matrix3: + return 9; + case DT_Matrix4: + return 16; + default: + break; + } + assert(false); + return 0; +} + +float MathUtil::unitRandom(void) +{ + return float(rand()) / RAND_MAX; +} + +DV_CORE_END_NAMESPACE diff --git a/source/core/source/math/dvc_matrix3.cpp b/source/core/source/math/dvc_matrix3.cpp new file mode 100644 index 0000000..e49fb01 --- /dev/null +++ b/source/core/source/math/dvc_matrix3.cpp @@ -0,0 +1,9 @@ +#include "math/dvc_matrix3.h" + +DV_CORE_BEGIN_NAMESPACE + +const fMatrix3 fMatrix3::ZERO(0, 0, 0, 0, 0, 0, 0, 0, 0); +const fMatrix3 fMatrix3::IDENTITY(1, 0, 0, 0, 1, 0, 0, 0, 1); + +DV_CORE_END_NAMESPACE + diff --git a/source/core/source/math/dvc_matrix4.cpp b/source/core/source/math/dvc_matrix4.cpp new file mode 100644 index 0000000..3fe3448 --- /dev/null +++ b/source/core/source/math/dvc_matrix4.cpp @@ -0,0 +1,9 @@ +#include "math/dvc_matrix4.h" + +DV_CORE_BEGIN_NAMESPACE + +const fMatrix4 fMatrix4::ZERO(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); +const fMatrix4 fMatrix4::IDENTITY(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); + +DV_CORE_END_NAMESPACE + diff --git a/source/core/source/math/dvc_vector2.cpp b/source/core/source/math/dvc_vector2.cpp new file mode 100644 index 0000000..f3f736a --- /dev/null +++ b/source/core/source/math/dvc_vector2.cpp @@ -0,0 +1,15 @@ +#include "math/dvc_vector2.h" + +DV_CORE_BEGIN_NAMESPACE + +const fVector2 fVector2::ZERO(0, 0); +const fVector2 fVector2::ONE(0, 0); +const fVector2 fVector2::UNIT_X(1, 0); +const fVector2 fVector2::UNIT_Y(0, 1); +const fVector2 fVector2::NEGATIVE_UNIT_X(-1, 0); +const fVector2 fVector2::NEGATIVE_UNIT_Y(0, -1); +const fVector2 fVector2::UNIT_SCALE(1, 1); + +DV_CORE_END_NAMESPACE + + diff --git a/source/core/source/math/dvc_vector3.cpp b/source/core/source/math/dvc_vector3.cpp new file mode 100644 index 0000000..7ca0f33 --- /dev/null +++ b/source/core/source/math/dvc_vector3.cpp @@ -0,0 +1,24 @@ +#include "math/dvc_vector3.h" + +DV_CORE_BEGIN_NAMESPACE + +const fVector3 fVector3::ZERO(0, 0, 0); +const fVector3 fVector3::ONE(1, 1, 1); +const fVector3 fVector3::UNIT_X(1, 0, 0); +const fVector3 fVector3::UNIT_Y(0, 1, 0); +const fVector3 fVector3::UNIT_Z(0, 0, 1); +const fVector3 fVector3::NEGATIVE_UNIT_X(-1, 0, 0); +const fVector3 fVector3::NEGATIVE_UNIT_Y(0, -1, 0); +const fVector3 fVector3::NEGATIVE_UNIT_Z(0, 0, -1); +const fVector3 fVector3::UNIT_SCALE(1, 1, 1); +const fVector3 fVector3::WHITE(1, 1, 1); +const fVector3 fVector3::BLACK(0, 0, 0); +const fVector3 fVector3::RED(1, 0, 0); +const fVector3 fVector3::GREEN(0, 1, 0); +const fVector3 fVector3::BLUE(0, 0, 1); +const fVector3 fVector3::PURPLE(1, 0, 1); +const fVector3 fVector3::GRAY(0.5f, 0.5f, 0.5f); + +DV_CORE_END_NAMESPACE + + diff --git a/source/core/source/math/dvc_vector4.cpp b/source/core/source/math/dvc_vector4.cpp new file mode 100644 index 0000000..3642a11 --- /dev/null +++ b/source/core/source/math/dvc_vector4.cpp @@ -0,0 +1,16 @@ +#include "math/dvc_vector4.h" + +DV_CORE_BEGIN_NAMESPACE + + +const fVector4 fVector4::ZERO(0, 0, 0, 0); +const fVector4 fVector4::WHITE(1, 1, 1, 1); +const fVector4 fVector4::BLACK(0, 0, 0, 1); +const fVector4 fVector4::RED(1, 0, 0, 1); +const fVector4 fVector4::GREEN(0, 1, 0, 1); +const fVector4 fVector4::BLUE(0, 0, 1, 1); +const fVector4 fVector4::PURPLE(1, 0, 1, 1); +const fVector4 fVector4::GRAY(0.5f, 0.5f, 0.5f, 1); + +DV_CORE_END_NAMESPACE + diff --git a/source/core/source/pipe/dvc_rasterizer_triangle.cpp b/source/core/source/pipe/dvc_rasterizer_triangle.cpp new file mode 100644 index 0000000..2edfd1c --- /dev/null +++ b/source/core/source/pipe/dvc_rasterizer_triangle.cpp @@ -0,0 +1,496 @@ +#include "pipe/dvc_rasterizer.h" +#include "math/dvc_fixmath.h" +#include "math/dvc_math_util.h" + +/* +https://github.com/nlguillemot/vigilant-system +https://wiki.joak.org/public:cs:rasterization_on_larrabee +*/ + +DV_CORE_BEGIN_NAMESPACE + +//------------------------------------------------------------------------------------- +struct DrawTriangleParam +{ + int32_t widthInTiles; + bool ccw; + fVector2 verts[3]; + float32_t bbox_min_x; + float32_t bbox_max_x; + float32_t bbox_min_y; + float32_t bbox_max_y; + bool tlBorder[3]; + float32_t area; + ifloat3_t edgesDX, edgesDY; +}; + +//------------------------------------------------------------------------------------- +enum { TILE_WIDTH_IN_PIXELS = 64, COARSE_BLOCK_WIDTH_IN_PIXELS = 16, FINE_BLOCK_WIDTH_IN_PIXELS = 4 }; + +//------------------------------------------------------------------------------------- +void _drawTriangle_Fine(int32_t tile_id, int32_t coarse_id, int32_t fine_id, + const DrawTriangleParam& param, const ifloat3_t& edges0, uint32_t testEdgeMask, + Rasterizer::DrawTriangleCallback callback) +{ + ifloat3_t pixelEdges(edges0); + + const fVector2 v0(param.verts[0]), v1(param.verts[1]), v2(param.verts[2]); + const bool edgeMask[3] = { (testEdgeMask & 1) != 0 , (testEdgeMask & 2) != 0 , (testEdgeMask & 4) != 0 }; + + int32_t fine_start_x = (tile_id % param.widthInTiles) * TILE_WIDTH_IN_PIXELS + + (coarse_id % 4) * COARSE_BLOCK_WIDTH_IN_PIXELS + (fine_id % 4) * FINE_BLOCK_WIDTH_IN_PIXELS; + + int32_t fine_start_y = (tile_id / param.widthInTiles) * TILE_WIDTH_IN_PIXELS + + (coarse_id / 4) * COARSE_BLOCK_WIDTH_IN_PIXELS + (fine_id / 4)*FINE_BLOCK_WIDTH_IN_PIXELS; + + for (int32_t y_index=0, y= fine_start_y; y_index < 4; y_index++, y++) + { + ifloat3_t edgesRow(pixelEdges); + + for (int32_t x_index = 0, x=fine_start_x; x_index < 4; x_index++, x++) + { + bool rejected = + (edgeMask[0] && (edgesRow.x < 0 || (!param.tlBorder[0] && edgesRow.x == 0))) || + (edgeMask[1] && (edgesRow.y < 0 || (!param.tlBorder[1] && edgesRow.y == 0))) || + (edgeMask[2] && (edgesRow.z < 0 || (!param.tlBorder[2] && edgesRow.z == 0))); + + if (!rejected) + { + fVector2 pixel(x + 0.5f, y + 0.5f); + + float32_t w0 = (v2 - v1).crossProduct(pixel - v2); + float32_t w1 = (v0 - v2).crossProduct(pixel - v0); + float32_t w2 = (v1 - v0).crossProduct(pixel - v1); + + fVector3 t = fVector3(w0 / param.area, (param.ccw ? w1 : w2) / param.area, (param.ccw ? w2 : w1) / param.area); + + callback(std::make_pair(x, y), t); + } + edgesRow.sub(param.edgesDY); + } + pixelEdges.add(param.edgesDX); + } +} + +//------------------------------------------------------------------------------------- +void _drawTriangle_Coarse(int32_t tile_id, int32_t coarse_id, + DrawTriangleParam& param, const ifloat3_t& edges0, uint32_t testEdgeMask, + Rasterizer::DrawTriangleCallback callback) +{ + const ifloat3_t blockEdgesDX( + param.edgesDX.x * FINE_BLOCK_WIDTH_IN_PIXELS, + param.edgesDX.y * FINE_BLOCK_WIDTH_IN_PIXELS, + param.edgesDX.z * FINE_BLOCK_WIDTH_IN_PIXELS + ); + const ifloat3_t blockEdgesDY( + param.edgesDY.x * FINE_BLOCK_WIDTH_IN_PIXELS, + param.edgesDY.y * FINE_BLOCK_WIDTH_IN_PIXELS, + param.edgesDY.z * FINE_BLOCK_WIDTH_IN_PIXELS + ); + const bool edgeMask[3] = { (testEdgeMask & 1) != 0 , (testEdgeMask & 2) != 0 , (testEdgeMask & 4) != 0 }; + + ifloat3_t blockReject(edges0); + ifloat3_t blockAccept(edges0); + ifloat3_t blockEdges(edges0); + + for (size_t v = 0; v < 3; v++) + { + if (edgeMask[v]) + { + if (blockEdgesDX[v] > 0) blockReject[v] += blockEdgesDX[v]; + else if (blockEdgesDX[v] < 0) blockAccept[v] += blockEdgesDX[v]; + + if (blockEdgesDY[v] > 0) blockAccept[v] -= blockEdgesDY[v]; + else if (blockEdgesDY[v] < 0) blockReject[v] -= blockEdgesDY[v]; + } + } + + int32_t block_start_x = (tile_id % param.widthInTiles) * TILE_WIDTH_IN_PIXELS + (coarse_id % 4) * COARSE_BLOCK_WIDTH_IN_PIXELS; + int32_t block_start_y = (tile_id / param.widthInTiles) * TILE_WIDTH_IN_PIXELS + (coarse_id / 4) * COARSE_BLOCK_WIDTH_IN_PIXELS; + + for (int32_t y_index = 0; y_index < 4; y_index++) + { + if (block_start_y + y_index * FINE_BLOCK_WIDTH_IN_PIXELS > param.bbox_max_y) + { + break; + } + + if (block_start_y + (y_index + 1)*FINE_BLOCK_WIDTH_IN_PIXELS >= param.bbox_min_y) + { + ifloat3_t edgesRow(blockEdges); + ifloat3_t edgesRowReject, edgesRowAccept; + + for (size_t v = 0; v < 3; v++) + { + if (edgeMask[v]) + { + edgesRowReject[v] = blockReject[v]; + edgesRowAccept[v] = blockAccept[v]; + } + } + for (int32_t x_index = 0; x_index < 4; x_index++) + { + if (block_start_x + x_index * FINE_BLOCK_WIDTH_IN_PIXELS > param.bbox_max_x) break; + if (block_start_x + (x_index + 1)*FINE_BLOCK_WIDTH_IN_PIXELS >= param.bbox_min_x) + { + bool rejected = + (edgeMask[0] && (edgesRowReject[0] < 0 || (!param.tlBorder[0] && edgesRowReject[0] == 0))) || + (edgeMask[1] && (edgesRowReject[1] < 0 || (!param.tlBorder[1] && edgesRowReject[1] == 0))) || + (edgeMask[2] && (edgesRowReject[2] < 0 || (!param.tlBorder[2] && edgesRowReject[2] == 0))); + + if (!rejected) + { + uint32_t newTestEdgeMask = testEdgeMask; + for (size_t v = 0; v < 3; v++) + { + if (edgeMask[v]) + { + if (edgesRowAccept[v] > 0 || (edgesRowAccept[v] == 0 && param.tlBorder[v])) + { + newTestEdgeMask &= ~(1 << v); + } + } + } + + _drawTriangle_Fine(tile_id, coarse_id, y_index * 4 + x_index, param, edgesRow, newTestEdgeMask, callback); + } + } + + edgesRow.sub(blockEdgesDY); + for (size_t v = 0; v < 3; v++) + { + if (edgeMask[v]) + { + edgesRowReject[v] -= blockEdgesDY[v]; + edgesRowAccept[v] -= blockEdgesDY[v]; + } + } + } + } + + blockEdges.add(blockEdgesDX); + for (size_t v = 0; v < 3; v++) + { + if (edgeMask[v]) + { + blockReject[v] += blockEdgesDX[v]; + blockAccept[v] += blockEdgesDX[v]; + } + } + } +} + +//------------------------------------------------------------------------------------- +void _drawTriangle_Title(int32_t tile_id, + DrawTriangleParam& param, const ifloat3_t& edges0, uint32_t testEdgeMask, + Rasterizer::DrawTriangleCallback callback) +{ + const ifloat3_t blockEdgesDX( + param.edgesDX[0] * COARSE_BLOCK_WIDTH_IN_PIXELS, + param.edgesDX[1] * COARSE_BLOCK_WIDTH_IN_PIXELS, + param.edgesDX[2] * COARSE_BLOCK_WIDTH_IN_PIXELS + ); + + const ifloat3_t blockEdgesDY( + param.edgesDY[0] * COARSE_BLOCK_WIDTH_IN_PIXELS, + param.edgesDY[1] * COARSE_BLOCK_WIDTH_IN_PIXELS, + param.edgesDY[2] * COARSE_BLOCK_WIDTH_IN_PIXELS + ); + const bool edgeMask[3] = { (testEdgeMask & 1) != 0 , (testEdgeMask & 2) != 0 , (testEdgeMask & 4) != 0 }; + + ifloat3_t blockReject(edges0); + ifloat3_t blockAccept(edges0); + ifloat3_t blockEdges(edges0); + + for (size_t v = 0; v < 3; v++) + { + if (edgeMask[v]) + { + if (blockEdgesDX[v] > 0) blockReject[v] += blockEdgesDX[v]; + else if (blockEdgesDX[v] < 0) blockAccept[v] += blockEdgesDX[v]; + + if (blockEdgesDY[v] > 0) blockAccept[v] -= blockEdgesDY[v]; + else if (blockEdgesDY[v] < 0) blockReject[v] -= blockEdgesDY[v]; + } + } + + int32_t block_start_x = (tile_id % param.widthInTiles) * TILE_WIDTH_IN_PIXELS; + int32_t block_start_y = (tile_id / param.widthInTiles) * TILE_WIDTH_IN_PIXELS; + + for (int32_t y_index = 0; y_index < 4; y_index++) + { + if (block_start_y + y_index * COARSE_BLOCK_WIDTH_IN_PIXELS > param.bbox_max_y) + { + break; + } + + if (block_start_y + (y_index + 1)*COARSE_BLOCK_WIDTH_IN_PIXELS >= param.bbox_min_y) + { + ifloat3_t edges(blockEdges); + ifloat3_t edgesRowReject, edgesRowAccept; + + for (size_t v = 0; v < 3; v++) + { + if (edgeMask[v]) + { + edgesRowReject[v] = blockReject[v]; + edgesRowAccept[v] = blockAccept[v]; + } + } + + for (int32_t x_index = 0; x_index < 4; x_index++) + { + if (block_start_x + x_index * COARSE_BLOCK_WIDTH_IN_PIXELS > param.bbox_max_x) + { + break; + } + + if (block_start_x + (x_index + 1)*COARSE_BLOCK_WIDTH_IN_PIXELS >= param.bbox_min_x) + { + bool rejected = + (edgeMask[0] && (edgesRowReject[0] < 0 || (!param.tlBorder[0] && edgesRowReject[0] == 0))) || + (edgeMask[1] && (edgesRowReject[1] < 0 || (!param.tlBorder[1] && edgesRowReject[1] == 0))) || + (edgeMask[2] && (edgesRowReject[2] < 0 || (!param.tlBorder[2] && edgesRowReject[2] == 0))); + + if (!rejected) + { + uint32_t newTestEdgeMask = testEdgeMask; + for (size_t v = 0; v < 3; v++) + { + if (edgeMask[v]) + { + if (edgesRowAccept[v] > 0 || (edgesRowAccept[v] == 0 && param.tlBorder[v])) + { + newTestEdgeMask &= ~(1 << v); + } + } + } + + _drawTriangle_Coarse(tile_id, y_index * 4 + x_index, param, edges, newTestEdgeMask, callback); + } + } + + edges.sub(blockEdgesDY); + + for (size_t v = 0; v < 3; v++) + { + if (edgeMask[v]) + { + edgesRowReject[v] -= blockEdgesDY[v]; + edgesRowAccept[v] -= blockEdgesDY[v]; + } + } + } + } + + blockEdges.add(blockEdgesDX); + for (size_t v = 0; v < 3; v++) + { + if (edgeMask[v]) + { + blockReject[v] += blockEdgesDX[v]; + blockAccept[v] += blockEdgesDX[v]; + } + } + } +} + +//------------------------------------------------------------------------------------- +void Rasterizer::drawTriangleLarrabee(int32_t canvasWidth, int32_t canvasHeight, + const fVector2& v0, const fVector2& v1, const fVector2& v2, + DrawTriangleCallback callback, bool ccw) +{ + assert(canvasWidth >= TILE_WIDTH_IN_PIXELS && canvasHeight >= TILE_WIDTH_IN_PIXELS); + assert(canvasWidth%TILE_WIDTH_IN_PIXELS == 0 && canvasHeight%TILE_WIDTH_IN_PIXELS == 0); + + DrawTriangleParam param; + param.widthInTiles = canvasWidth / TILE_WIDTH_IN_PIXELS; + + //ccw + bool isCCW = (v1 - v0).crossProduct(v2 - v1)>0; + if (isCCW != ccw) return; + + param.ccw = ccw; + param.verts[0] = v0; + param.verts[1] = ccw ? v1 : v2; + param.verts[2] = ccw ? v2 : v1; + param.area = (param.verts[1] - param.verts[0]).crossProduct(param.verts[2] - param.verts[1]); + + ifloat2_t vertices[3]; + for (size_t i = 0; i < 3; i++) + { + vertices[i] = ifloat2_t(param.verts[i].x, param.verts[i].y); + } + + //get window coordinates bounding box + param.bbox_min_x = MathUtil::min3(v0.x, v1.x, v2.x); + param.bbox_max_x = MathUtil::max3(v0.x, v1.x, v2.x); + param.bbox_min_y = MathUtil::min3(v0.y, v1.y, v2.y); + param.bbox_max_y = MathUtil::max3(v0.y, v1.y, v2.y); + + //canvas size + ifloat_t iCanvasWidth = ifloat_init(canvasWidth); + ifloat_t iCanvasHeight = ifloat_init(canvasHeight); + + // clip triangles that are fully outside the scissor rect (scissor rect = whole window) + if (param.bbox_max_x < 0 || param.bbox_max_y < 0 || param.bbox_min_x >= canvasWidth || param.bbox_min_y >= canvasHeight) + { + return; + } + if (param.bbox_min_x < 0) param.bbox_min_x = 0; + if (param.bbox_min_y < 0) param.bbox_min_y = 0; + if (param.bbox_max_x > canvasWidth - 1.f) param.bbox_max_x = canvasWidth - 1.f; + if (param.bbox_max_y > canvasHeight - 1.f) param.bbox_max_y = canvasHeight - 1.f; + + // tile range + int32_t first_tile_x = (int32_t)(param.bbox_min_x / TILE_WIDTH_IN_PIXELS); + int32_t first_tile_y = (int32_t)(param.bbox_min_y / TILE_WIDTH_IN_PIXELS); + int32_t last_tile_x = (int32_t)(param.bbox_max_x / TILE_WIDTH_IN_PIXELS); + int32_t last_tile_y = (int32_t)(param.bbox_max_y / TILE_WIDTH_IN_PIXELS); + + // evaluate edge equation at the top left tile + ifloat_t firstTileX = first_tile_x * ifloat_init(TILE_WIDTH_IN_PIXELS); + ifloat_t firstTileY = first_tile_y * ifloat_init(TILE_WIDTH_IN_PIXELS); + + ifloat3_t edges0; + ifloat3_t tileEdgesDX, tileEdgesDY, edgesReject, edgesAccept; + const ifloat_t kZeroPointFive = ifloat_init(0.5f); + + for (size_t v = 0; v < 3; v++) + { + size_t v_end = (v + 1) % 3; + param.edgesDX[v] = vertices[v_end].x - vertices[v].x; + param.edgesDY[v] = vertices[v_end].y - vertices[v].y; + + tileEdgesDX[v] = param.edgesDX[v] * TILE_WIDTH_IN_PIXELS; + tileEdgesDY[v] = param.edgesDY[v] * TILE_WIDTH_IN_PIXELS; + + //(V0,V1) X (V0, P) = (x1-x0)(py-y0)-(y1-y0)(px-x0) + edges0[v] = ifloat_multiply(param.edgesDX[v], (firstTileY + kZeroPointFive - vertices[v].y)) + - ifloat_multiply(param.edgesDY[v], (firstTileX + kZeroPointFive - vertices[v].x)); + + // Top-left rule: + // shift top-left edges ever so slightly outward to make the top-left edges be + // the tie-breakers when rasterizing adjacent triangles + if ((vertices[v_end].y == vertices[v].y && vertices[v_end].x < vertices[v].x) || + vertices[v_end].y < vertices[v].y) + { + param.tlBorder[v] = true; + } + else + { + param.tlBorder[v] = false; + } + + edgesReject[v] = edgesAccept[v] = edges0[v]; + + if (tileEdgesDX[v] > 0) edgesReject[v] += tileEdgesDX[v]; + else if (tileEdgesDX[v] < 0) edgesAccept[v] += tileEdgesDX[v]; + + if (tileEdgesDY[v] > 0) edgesAccept[v] -= tileEdgesDY[v]; + else if (tileEdgesDY[v] < 0) edgesReject[v] -= tileEdgesDY[v]; + } + + ifloat3_t rowEdges(edges0); + + int32_t tile_row_start = first_tile_y * param.widthInTiles + first_tile_x; + for (int32_t tile_y = first_tile_y; tile_y <= last_tile_y; tile_y++) + { + ifloat3_t edges(rowEdges); + ifloat3_t tileEdgesReject(edgesReject); + ifloat3_t tileEdgesAccept(edgesAccept); + + int32_t tile_i = tile_row_start; + for (int32_t tile_x = first_tile_x; tile_x <= last_tile_x; tile_x++) + { + bool rejected = + ((tileEdgesReject[0] < 0 || (!param.tlBorder[0] && tileEdgesReject[0] == 0))) || + ((tileEdgesReject[1] < 0 || (!param.tlBorder[1] && tileEdgesReject[1] == 0))) || + ((tileEdgesReject[2] < 0 || (!param.tlBorder[2] && tileEdgesReject[2] == 0))); + + if (!rejected) + { + //draw title + uint32_t testEdgeMask = 0; + for (size_t v = 0; v < 3; v++) + { + if (tileEdgesAccept[v] < 0 || (tileEdgesAccept[v] == 0 && !param.tlBorder[v])) + { + testEdgeMask += (1 << v); + } + } + + _drawTriangle_Title(tile_i, param, edges, testEdgeMask, callback); + } + + //x setp + edges.sub(tileEdgesDY); + tileEdgesReject.sub(tileEdgesDY); + tileEdgesAccept.sub(tileEdgesDY); + tile_i++; + } + + //y step + rowEdges.add(tileEdgesDX); + edgesReject.add(tileEdgesDX); + edgesAccept.add(tileEdgesDX); + tile_row_start += param.widthInTiles; + } +} + +//------------------------------------------------------------------------------------- +void Rasterizer::drawTriangleScanline(int32_t canvasWidth, int32_t canvasHeight, const fVector3& v0, const fVector3& v1, const fVector3& v2, DrawTriangleCallback callback) +{ +#if 0 + //back cull + fVector3 edge0 = v2 - v1; + fVector3 edge1 = v0 - v2; + fVector3 vnormal = edge0.crossProduct(edge1); + if (vnormal.z > 0) return; + + fVector3 edge2 = v1 - v0; + + float xmin = MathUtil::min3(v0.x, v1.x, v2.x); + float ymin = MathUtil::min3(v0.y, v1.y, v2.y); + float xmax = MathUtil::max3(v0.x, v1.x, v2.x); + float ymax = MathUtil::max3(v0.y, v1.y, v2.y); + + float area = _edge(v0, v1, v2); + + int32_t x0 = MathUtil::max2(0, (int32_t)std::floor(xmin)); + int32_t x1 = MathUtil::min2(canvasWidth - 1, (int32_t)std::floor(xmax)); + int32_t y0 = MathUtil::max2(0, (int32_t)std::floor(ymin)); + int32_t y1 = MathUtil::min2(canvasHeight - 1, (int32_t)std::floor(ymax)); + + for (int32_t y = y0; y <= y1; y++) { + for (int32_t x = x0; x <= x1; x++) { + + fVector3 pixel(x + 0.5f, y + 0.5f, 0.f); + + bool overlaps = true; + + float w0 = _edge(v1, v2, pixel); + overlaps &= (w0 == 0 ? ((edge0.y == 0 && edge0.x > 0) || edge0.y > 0) : (w0 > 0)); + if (!overlaps) continue; + + float w1 = _edge(v2, v0, pixel); + overlaps &= (w1 == 0 ? ((edge1.y == 0 && edge1.x > 0) || edge1.y > 0) : (w1 > 0)); + if (!overlaps) continue; + + float w2 = _edge(v0, v1, pixel); + overlaps &= (w2 == 0 ? ((edge2.y == 0 && edge2.x > 0) || edge2.y > 0) : (w2 > 0)); + if (!overlaps) continue; + + + fVector3 t = fVector3(w0 / area, w1 / area, w2 / area); + + float invZ = MathUtil::lerp3(v0.z, v1.z, v2.z, t); + t *= fVector3(v0.z / invZ, v1.z / invZ, v2.z / invZ); + + callback(std::make_pair(x, y), t); + } + } +#endif +} + +DV_CORE_END_NAMESPACE diff --git a/source/core/source/pipe/dvc_render_queue.cpp b/source/core/source/pipe/dvc_render_queue.cpp new file mode 100644 index 0000000..0dba60f --- /dev/null +++ b/source/core/source/pipe/dvc_render_queue.cpp @@ -0,0 +1,56 @@ +#include "pipe/dvc_render_queue.h" +#include "scene/dvc_scene_node.h" +#include "pipe/dvc_render_step_IA.h" +#include "pipe/dvc_render_step_VS.h" +#include "pipe/dvc_render_step_PS.h" +#include "scene/component/dvc_camera_component.h" + +DV_CORE_BEGIN_NAMESPACE + +RenderQueue::RenderQueue() +{ + +} + +RenderQueue::~RenderQueue() +{ + +} + +void RenderQueue::pushRenderable(RenderableConstPtr renderable) +{ + m_queue.push_back(renderable); +} + +RenderableConstPtr RenderQueue::getRenderable(size_t index) const +{ + assert(index < m_queue.size()); + if (index < m_queue.size()) + { + return m_queue[index]; + } + return nullptr; +} + +void RenderQueue::build(const Scene& scene) +{ + //Build render queue + scene.getRootSceneNode()->buildRenderQueue(fMatrix4::IDENTITY, *this); +} + +void RenderQueue::process(UniformBuffer& uniformBuffer, RenderTarget& renderTarget) +{ + //Assembere input + InputAssemberStep IA; + IA.process(*this); + + //Vertex shader + VertexShaderStep VS; + VS.process(uniformBuffer, IA.getNodes()); + + //Pixel shader + PixelShaderStep PS; + PS.process(VS.getNodes(), renderTarget); +} + +DV_CORE_END_NAMESPACE diff --git a/source/core/source/pipe/dvc_render_step_IA.cpp b/source/core/source/pipe/dvc_render_step_IA.cpp new file mode 100644 index 0000000..cd93695 --- /dev/null +++ b/source/core/source/pipe/dvc_render_step_IA.cpp @@ -0,0 +1,25 @@ +#include "pipe/dvc_render_step_IA.h" +#include "pipe/dvc_renderable.h" + +DV_CORE_BEGIN_NAMESPACE + +void InputAssemberStep::process(const RenderQueue& renderQueue) +{ + size_t counts = renderQueue.getRenderableCounts(); + for (size_t i = 0; i < counts; i++) + { + RenderableConstPtr renderable = renderQueue.getRenderable(i); + if (renderable) + { + Node node; + if (!(renderable->buildAssemberNode(node))) + { + continue; + } + + m_nodes.push_back(node); + } + } +} + +DV_CORE_END_NAMESPACE diff --git a/source/core/source/pipe/dvc_render_step_PS.cpp b/source/core/source/pipe/dvc_render_step_PS.cpp new file mode 100644 index 0000000..4e7d3d9 --- /dev/null +++ b/source/core/source/pipe/dvc_render_step_PS.cpp @@ -0,0 +1,75 @@ +#include "pipe/dvc_render_step_PS.h" +#include "shader/dvc_shader_interface.h" +#include "device/dvc_buffer.h" +#include "pipe/dvc_rasterizer.h" +#include "math/dvc_math_util.h" + +DV_CORE_BEGIN_NAMESPACE + +void PixelShaderStep::process(const VertexShaderStep::NodeArray& vsNodeArray, RenderTarget& renderTarget) +{ + ShaderFileInterface* shaderInterface = davinci::LoadShaderFile("TestShader.dll"); + PixelShaderInterface* pixelShader = shaderInterface->getPixelShader("xxx"); + + int32_t targetWidth = renderTarget.getWidth(); + int32_t targetHeight = renderTarget.getHeight(); + + fMatrix4 view_trans = + fMatrix4::makeTrans(targetWidth / 2.f, targetHeight / 2.f, 0.f) * + fMatrix4::makeScale(targetWidth / 2.f, targetHeight / 2.f, 1.f); + + VertexShaderStep::NodeArray::const_iterator it, end = vsNodeArray.end(); + for (it = vsNodeArray.begin(); it != end; ++it) + { + const VertexShaderStep::Node& node = *it; + + size_t vertexSize = node.vertexDesc.vertexByteSize(); + size_t POS_offset = node.vertexDesc.getElement(VET_POSITION)->offset; + + for (size_t i = 0; i < node.vertexCounts; i += 3) + { + const float32_t* vertex0 = (const float32_t*)(node.vertexBuf->ptr(i * vertexSize)); + const float32_t* vertex1 = (const float32_t*)(node.vertexBuf->ptr((i+1) * vertexSize)); + const float32_t* vertex2 = (const float32_t*)(node.vertexBuf->ptr((i+2) * vertexSize)); + + fVector3 pos0 = view_trans * (*(const fVector3*)(vertex0 + POS_offset)); + fVector3 pos1 = view_trans * (*(const fVector3*)(vertex1 + POS_offset)); + fVector3 pos2 = view_trans * (*(const fVector3*)(vertex2 + POS_offset)); + + std::vector invZ = { node.invZ[i + 0], node.invZ[i + 1], node.invZ[i + 2] }; + std::vector vertexDepth = { node.depth[i+0], node.depth[i + 1], node.depth[i + 2]}; + + Rasterizer::drawTriangleLarrabee(targetWidth, targetHeight, pos0.xy(), pos1.xy(), pos2.xy(), + [pixelShader, vertex0, vertex1, vertex2, invZ, vertexDepth, &node, &renderTarget] + (const std::pair& dot, const fVector3& percent) + { + fVector3 percentCorrected(percent); + + float temp = MathUtil::lerp3(invZ[0], invZ[1], invZ[2], percent); + percentCorrected *= fVector3(invZ[0] / temp, invZ[1] / temp, invZ[2] / temp); + + std::vector pixelData; + + const size_t floatCounts = node.vertexDesc.vertexByteSize() / sizeof(float32_t); + pixelData.resize(floatCounts); + + for (size_t j = 0; j < floatCounts; j++) + { + pixelData[j] = MathUtil::lerp3(vertex0[j], vertex1[j], vertex2[j], percentCorrected); + } + + fVector4 color; + pixelShader->process(node.vertexDesc, &(pixelData[0]), color); + + float32_t depth = MathUtil::lerp3(vertexDepth[0], vertexDepth[1], vertexDepth[2], percentCorrected); + + int32_t x = dot.first; + int32_t y = dot.second; + renderTarget.setPixel(x, y, color, depth); + } + ); + } + } +} + +DV_CORE_END_NAMESPACE diff --git a/source/core/source/pipe/dvc_render_step_VS.cpp b/source/core/source/pipe/dvc_render_step_VS.cpp new file mode 100644 index 0000000..b95b746 --- /dev/null +++ b/source/core/source/pipe/dvc_render_step_VS.cpp @@ -0,0 +1,84 @@ +#include "pipe/dvc_render_step_VS.h" +#include "shader/dvc_shader_interface.h" +#include "device/dvc_buffer_accessor.h" +#include "math/dvc_math_util.h" + +DV_CORE_BEGIN_NAMESPACE + +void VertexShaderStep::process(UniformBuffer& uniformBuffer, const InputAssemberStep::NodeArray& assembedNodeArray) +{ + ShaderFileInterface* shaderInterface = davinci::LoadShaderFile("TestShader.dll"); + VertexShaderInterface* standardVS = shaderInterface->getVertexShader("xxxx"); + + const fVector3* eyePos = (const fVector3*)(uniformBuffer.getAttribute("camera:eye_pos")); + const fMatrix4* cameraView = (const fMatrix4*)(uniformBuffer.getAttribute("camera:view_mat")); + float32_t cameraNear = *((const float32_t*)(uniformBuffer.getAttribute("camera:near"))); + float32_t cameraFar = *((const float32_t*)(uniformBuffer.getAttribute("camera:far"))); + + //int debug_index = 0; + InputAssemberStep::NodeArray::const_iterator it, end = assembedNodeArray.end(); + for (it = assembedNodeArray.begin(); it != end; ++it) + { + const InputAssemberStep::Node& inputNode = *it; + + //update self uniform value + uniformBuffer.setAttribute("self:world_transform", inputNode.transform); + + standardVS->prepare(&uniformBuffer, + [inputNode](VertexElementType type) -> DeviceBufferAccessorConstPtr + { + InputAssemberStep::PrimitiveAttributes::const_iterator it = inputNode.vertexData.find(type); + if (it != inputNode.vertexData.end()) + { + return it->second; + } + return nullptr; + } + ); + + size_t vertexCounts = inputNode.indices->getCount(); + + Node outputNode; + outputNode.vertexCounts = vertexCounts; + outputNode.vertexDesc = standardVS->getOutputVertexDesc(); + outputNode.vertexBuf = std::make_shared(outputNode.vertexDesc.vertexByteSize() * vertexCounts); + outputNode.depth.resize(vertexCounts); + outputNode.invZ.resize(vertexCounts); + + for (size_t i = 0; i < vertexCounts; i++) + { + size_t index = 0; + if (inputNode.indices->getComponentType() == CT_Uint16) + { + index = (size_t)(*(uint16_t*)(inputNode.indices->getElement(i))); + } + else if(inputNode.indices->getComponentType() == CT_Uint32) + { + index = (size_t)(*(uint32_t*)(inputNode.indices->getElement(i))); + } + else + { + //error + } + + fVector3 vertexWorldPos; + void* outputVertexMemory = outputNode.vertexBuf->ptr(i* outputNode.vertexDesc.vertexByteSize()); + standardVS->process(index, outputVertexMemory, vertexWorldPos); + + //TODO: clip + + + //TODO: div-zero + fVector3 cameraSpacePos = (*cameraView) * vertexWorldPos; + outputNode.depth[i] = (std::fabsf(cameraSpacePos.z) - cameraNear) / (cameraFar - cameraNear); + + float32_t cameraSpaceZ = cameraSpacePos.z; + outputNode.invZ[i] = 1.f/std::fabsf(cameraSpacePos.z); + } + + m_nodes.push_back(outputNode); + } + +} + +DV_CORE_END_NAMESPACE diff --git a/source/core/source/pipe/dvc_renderable.cpp b/source/core/source/pipe/dvc_renderable.cpp new file mode 100644 index 0000000..154837d --- /dev/null +++ b/source/core/source/pipe/dvc_renderable.cpp @@ -0,0 +1,11 @@ +#include "pipe/dvc_renderable.h" + +DV_CORE_BEGIN_NAMESPACE + +Renderable::Renderable(const fMatrix4& worldTransform) + : m_worldTransform(worldTransform) +{ + +} + +DV_CORE_END_NAMESPACE diff --git a/source/core/source/pipe/dvc_renderable_entity.cpp b/source/core/source/pipe/dvc_renderable_entity.cpp new file mode 100644 index 0000000..b990816 --- /dev/null +++ b/source/core/source/pipe/dvc_renderable_entity.cpp @@ -0,0 +1,22 @@ +#include "pipe/dvc_renderable_entity.h" + +DV_CORE_BEGIN_NAMESPACE + +RenderableEntity::RenderableEntity(Mesh::PrimitiveConstPtr primitive, const fMatrix4& worldTransform) + : m_primitive(primitive) + , Renderable(worldTransform) +{ + +} + +bool RenderableEntity::buildAssemberNode(InputAssemberStep::Node& node) const +{ + node.primitiveType = m_primitive->type; + node.transform = m_worldTransform; + node.vertexData = m_primitive->attributes; + node.indices = m_primitive->indices; + + return true; +} + +DV_CORE_END_NAMESPACE diff --git a/source/core/source/scene/component/dvc_camera_component.cpp b/source/core/source/scene/component/dvc_camera_component.cpp new file mode 100644 index 0000000..1a720c3 --- /dev/null +++ b/source/core/source/scene/component/dvc_camera_component.cpp @@ -0,0 +1,59 @@ +#include "scene/component/dvc_camera_component.h" +#include "math/dvc_math_util.h" +#include "scene/dvc_scene_node.h" +#include "math/dvc_matrix4.h" +#include "math/dvc_vector4.h" + +DV_CORE_BEGIN_NAMESPACE + +CameraComponent::CameraComponent(SceneNodePtr owner) + : SceneComponent(owner) +{ + +} + +CameraComponent::~CameraComponent() +{ + +} + +const std::string& CameraComponent::typeName() +{ + static std::string s_typename = "Camera"; + return s_typename; +} + +fVector3 CameraComponent::getEye(void) const +{ + return m_owner->getWorldPosition(); +} + +fVector3 CameraComponent::getLookat(void) const +{ + //negative z-axis. + fVector4 p = (m_owner->getTransform() * fVector4(0, 0, -1, 1)); + return p.xyz() / p.w; +} + +fVector3 CameraComponent::getUp(void) const +{ + //y-axis + return (m_owner->getTransform() * fVector4(0, 1, 0, 0)).xyz(); +} + +void CameraComponent::setCameraDefine(const CameraComponent::CameraDefine& cameraDefine) +{ + m_cameraDefine = cameraDefine; + + fMatrix4 matView = MathUtil::lookatRH(getEye(), getLookat(), getUp()); + fMatrix4 matProj = MathUtil::perspectiveFovRH(m_cameraDefine.fov, m_cameraDefine.aspect, m_cameraDefine.nearClip, m_cameraDefine.farClip); + m_viewMatrix = matView; + m_viewProjMatrix = matProj * matView; +} + +void CameraComponent::render(const fMatrix4& transParent, RenderQueue& queue) const +{ + +} + +DV_CORE_END_NAMESPACE diff --git a/source/core/source/scene/component/dvc_component.cpp b/source/core/source/scene/component/dvc_component.cpp new file mode 100644 index 0000000..b7f4d5f --- /dev/null +++ b/source/core/source/scene/component/dvc_component.cpp @@ -0,0 +1,21 @@ +#include "scene/component/dvc_component.h" + +DV_CORE_BEGIN_NAMESPACE + +SceneComponent::SceneComponent(SceneNodePtr owner) + : m_owner(owner) +{ + +} + +SceneComponent::~SceneComponent() +{ + +} + +void SceneComponent::render(const fMatrix4& transParent, RenderQueue& queue) const +{ + +} + +DV_CORE_END_NAMESPACE diff --git a/source/core/source/scene/component/dvc_light_component.cpp b/source/core/source/scene/component/dvc_light_component.cpp new file mode 100644 index 0000000..b06fede --- /dev/null +++ b/source/core/source/scene/component/dvc_light_component.cpp @@ -0,0 +1,33 @@ +#include "scene/component/dvc_light_component.h" + +DV_CORE_BEGIN_NAMESPACE + +LightComponent::LightComponent(SceneNodePtr owner) + : SceneComponent(owner) +{ + +} + +LightComponent::~LightComponent() +{ + +} + +const std::string& LightComponent::typeName() +{ + static std::string s_typename = "Light"; + return s_typename; +} + +void LightComponent::setLightDefine(const LightDefine& lightDefine) +{ + m_define = lightDefine; +} + +void LightComponent::render(const fMatrix4& transParent, RenderQueue& queue) const +{ + +} + +DV_CORE_END_NAMESPACE + diff --git a/source/core/source/scene/component/dvc_mesh_component.cpp b/source/core/source/scene/component/dvc_mesh_component.cpp new file mode 100644 index 0000000..2015558 --- /dev/null +++ b/source/core/source/scene/component/dvc_mesh_component.cpp @@ -0,0 +1,44 @@ +#include "scene/component/dvc_mesh_component.h" +#include "asset/dvc_mesh.h" +#include "pipe/dvc_renderable_entity.h" +#include "pipe/dvc_render_queue.h" + +DV_CORE_BEGIN_NAMESPACE + +MeshComponent::MeshComponent(SceneNodePtr owner) + : SceneComponent(owner) +{ + +} + +MeshComponent::~MeshComponent() +{ + +} + +const std::string& MeshComponent::typeName() +{ + static std::string s_typename = "Mesh"; + return s_typename; +} + +void MeshComponent::setMesh(MeshConstPtr mesh) +{ + m_mesh = mesh; +} + +void MeshComponent::render(const fMatrix4& transParent, RenderQueue& queue) const +{ + SceneComponent::render(transParent, queue); + + int32_t primitiveCounts = m_mesh->getPrimitiveCounts(); + for (int32_t i = 0; i < primitiveCounts; i++) + { + Mesh::PrimitiveConstPtr primitivePtr = m_mesh->getPrimitive(i); + + RenderablePtr renderable = std::make_shared(primitivePtr, transParent); + queue.pushRenderable(renderable); + } +} + +DV_CORE_END_NAMESPACE diff --git a/source/core/source/scene/dvc_scene.cpp b/source/core/source/scene/dvc_scene.cpp new file mode 100644 index 0000000..6f05f31 --- /dev/null +++ b/source/core/source/scene/dvc_scene.cpp @@ -0,0 +1,123 @@ +#include "scene/dvc_scene.h" +#include "scene/dvc_scene_node.h" +#include "device/dvc_buffer.h" +#include "device/dvc_buffer_view.h" +#include "device/dvc_buffer_accessor.h" +#include "scene/component/dvc_camera_component.h" + +DV_CORE_BEGIN_NAMESPACE + +Scene::Scene() +{ +} + +Scene::~Scene() +{ + clear(); +} + +void Scene::init(void) +{ + //clear + clear(); + + //set a new root + m_rootSceneNode = std::shared_ptr(new SceneNode()); +} + +void Scene::clear(void) +{ + //clear scene nodes + if (m_rootSceneNode.get()) + { + m_rootSceneNode->clear(); + } + m_rootSceneNode = nullptr; + + //clear assets + m_meshArray.clear(); + + //clear device resources + m_bufferAccessorArray.clear(); + m_bufferViewArray.clear(); + m_deviceBufferArray.clear(); +} + +DeviceBufferPtr Scene::newDeviceBuffer() +{ + DeviceBufferPtr deviceBuffer = std::make_shared(); + m_deviceBufferArray.push_back(deviceBuffer); + return deviceBuffer; +} + +DeviceBufferConstPtr Scene::getDeviceBuffer(int32_t index) const +{ + if (index < 0 || index >= m_deviceBufferArray.size()) + { + return nullptr; + } + return m_deviceBufferArray[index]; +} + +DeviceBufferViewPtr Scene::newDeviceBufferView(DeviceBufferConstPtr buffer, size_t offset, size_t length, size_t stride) +{ + DeviceBufferViewPtr deviceBufferView = std::make_shared(buffer, offset, length, stride); + m_bufferViewArray.push_back(deviceBufferView); + return deviceBufferView; +} + +DeviceBufferViewPtr Scene::getDeviceBufferView(int32_t index) const +{ + if (index < 0 || index >= m_bufferViewArray.size()) + { + return nullptr; + } + return m_bufferViewArray[index]; +} + +DeviceBufferAccessorPtr Scene::newDeviceBufferAccessor(DeviceBufferViewConstPtr bufferView, size_t offset, + size_t count, ComponentType componentType, DataType dataType) +{ + DeviceBufferAccessorPtr deviceBufferAccessor = std::make_shared( + bufferView, offset, count, componentType, dataType); + m_bufferAccessorArray.push_back(deviceBufferAccessor); + return deviceBufferAccessor; +} + +DeviceBufferAccessorPtr Scene::getDeviceBufferAccessor(int32_t index) const +{ + if (index < 0 || index >= m_bufferAccessorArray.size()) + { + return nullptr; + } + return m_bufferAccessorArray[index]; +} + +int32_t Scene::pushNewMeshAsset(MeshPtr mesh) +{ + m_meshArray.push_back(mesh); + return (int32_t)(m_meshArray.size()); +} + +MeshConstPtr Scene::getMesh(int32_t index) const +{ + if (index < 0 || index >= m_meshArray.size()) + { + return nullptr; + } + return m_meshArray[index]; +} + +SceneNodePtr Scene::newSceneNode(const std::string& name) +{ + SceneNodePtr sceneNode = std::make_shared(); + sceneNode->setName(name); + return sceneNode; +} + +SceneNodePtr Scene::getRootSceneNode(void) const +{ + return m_rootSceneNode; +} + +DV_CORE_END_NAMESPACE diff --git a/source/core/source/scene/dvc_scene_node.cpp b/source/core/source/scene/dvc_scene_node.cpp new file mode 100644 index 0000000..888f610 --- /dev/null +++ b/source/core/source/scene/dvc_scene_node.cpp @@ -0,0 +1,108 @@ +#include "scene/dvc_scene_node.h" +#include "scene/component/dvc_component.h" + +DV_CORE_BEGIN_NAMESPACE + +SceneNode::SceneNode() + : m_transform(fMatrix4::IDENTITY) +{ + +} + +SceneNode::~SceneNode() +{ + +} + +void SceneNode::setName(const std::string& name) +{ + m_name = name; +} + +const std::string& SceneNode::getName() const +{ + return m_name; +} + +void SceneNode::clear(void) +{ + for (size_t i = 0; i < m_childen.size(); i++) + { + m_childen[i]->clear(); + } + m_childen.clear(); +} + +void SceneNode::addChild(SceneNodePtr child) +{ + child->setParent(shared_from_this()); + m_childen.push_back(child); +} + +void SceneNode::setParent(SceneNodePtr parent) +{ + m_parent = parent; +} + +void SceneNode::addComponent(SceneComponentPtr component) +{ + m_componentArray.push_back(component); +} + +void SceneNode::setTransform(const fMatrix4& transform) +{ + m_transform = transform; +} + +fMatrix4 SceneNode::getWorldTransform() const +{ + if (m_parent) + { + return m_parent->getWorldTransform() * m_transform; + } + return m_transform; +} + +fVector3 SceneNode::getPosition() const +{ + return getTransform() * fVector3::ZERO; +} + +fVector3 SceneNode::getWorldPosition() const +{ + return getWorldTransform() * fVector3::ZERO; +} + +SceneNodeConstPtr SceneNode::getChild(size_t index) const +{ + if (index >= getChildCounts()) { + return nullptr; + } + return m_childen[index]; +} + +SceneComponentConstPtr SceneNode::getComponent(size_t index) const +{ + if (index >= getComponentCounts()) { + return nullptr; + } + return m_componentArray[index]; +} + +void SceneNode::buildRenderQueue(const fMatrix4& transParent, RenderQueue& queue) +{ + fMatrix4 transform = m_transform * transParent; + + for (auto component : m_componentArray) + { + component->render(transform, queue); + } + + for (auto child : m_childen) + { + child->buildRenderQueue(transform, queue); + } +} + +DV_CORE_END_NAMESPACE + diff --git a/source/core/source/shader/dvc_shader_interface.cpp b/source/core/source/shader/dvc_shader_interface.cpp new file mode 100644 index 0000000..7fe2c38 --- /dev/null +++ b/source/core/source/shader/dvc_shader_interface.cpp @@ -0,0 +1,24 @@ +#include "shader/dvc_shader_interface.h" + +#include + +DV_CORE_BEGIN_NAMESPACE + +ShaderFileInterface* LoadShaderFile(const char* szShaderFile) +{ + HMODULE hDllModule = LoadLibraryA(szShaderFile); + if (hDllModule == NULL) + { + return nullptr; + } + + ShaderFileInterfaceFunc shaderFileInterfaceFunc = (ShaderFileInterfaceFunc)GetProcAddress(hDllModule, "ShaderFileInterface"); + if (shaderFileInterfaceFunc == NULL) + { + return nullptr; + } + + return shaderFileInterfaceFunc(); +} + +DV_CORE_END_NAMESPACE diff --git a/source/dv_config.h.in b/source/dv_config.h.in new file mode 100644 index 0000000..9bab598 --- /dev/null +++ b/source/dv_config.h.in @@ -0,0 +1,46 @@ +#pragma once + +#cmakedefine DV_HAVE_UNISTD_H 1 +#cmakedefine DV_HAVE_INTTYPES_H 1 +#cmakedefine DV_HAVE_LIMITS_H 1 + +#cmakedefine DV_SYS_LINUX 1 +#cmakedefine DV_SYS_WINDOWS 1 +#cmakedefine DV_SYS_MACOS 1 + +#include +#include +#include +#include +#include +#include + +#ifdef DV_HAVE_UNISTD_H +#include +#endif + +#ifdef DV_HAVE_INTTYPES_H +#include +#endif + +#ifdef DV_HAVE_LIMITS_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef DV_SYS_WINDOWS + #define snprintf _snprintf + typedef signed __int64 ssize_t; +#endif + +typedef float float32_t; +typedef double float64_t; diff --git a/source/utils/CMakeLists.txt b/source/utils/CMakeLists.txt new file mode 100644 index 0000000..857a98f --- /dev/null +++ b/source/utils/CMakeLists.txt @@ -0,0 +1,88 @@ + +set(DV_UTILS_INCLUDE_FILES + include/dvu_config.h +) +source_group("include" FILES ${DV_UTILS_INCLUDE_FILES}) + +################# +# buffer +################# +set(DV_UTILS_BUFFER_INCLUDE_FILES + include/buffer/dvu_dump_buffer.h +) +source_group("include/buffer" FILES ${DV_UTILS_BUFFER_INCLUDE_FILES}) + +set(DV_UTILS_BUFFER_SOURCE_FILES + source/buffer/dvu_dump_buffer.cpp +) +source_group("source/buffer" FILES ${DV_UTILS_BUFFER_SOURCE_FILES}) + +################# +# scene +################# +set(DV_UTILS_SCENE_INCLUDE_FILES + include/scene/dvu_load_gltf.h +) +source_group("include/scene" FILES ${DV_UTILS_SCENE_INCLUDE_FILES}) + +set(DV_UTILS_SCENE_SOURCE_FILES + source/scene/dvu_load_gltf.cpp +) +source_group("source/scene" FILES ${DV_UTILS_SCENE_SOURCE_FILES}) + +include_directories( + ${DV_AUTO_INCLUDE_PATH} + ${DV_CORE_INCLUDE_PATH} + + ${CMAKE_CURRENT_SOURCE_DIR}/include +) + +add_library(dv_utils SHARED + ${DV_UTILS_INCLUDE_FILES} + ${DV_UTILS_SCENE_INCLUDE_FILES} + ${DV_UTILS_SCENE_SOURCE_FILES} + ${DV_UTILS_BUFFER_INCLUDE_FILES} + ${DV_UTILS_BUFFER_SOURCE_FILES} +) + +target_link_libraries(dv_utils + dv_core + tinygltf::tinygltf + PNG::png_static +) + +target_compile_definitions(dv_utils + PRIVATE DV_UTILS_EXPORTS +) + +################# +# copy runtime dll(for debug) +################# +add_custom_command( + TARGET dv_utils POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different $ ${CMAKE_BINARY_DIR}/source/console/$<$:Debug>$<$:Release>) + +add_custom_command( + TARGET dv_utils POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different $ ${CMAKE_BINARY_DIR}/test/unit/$<$:Debug>$<$:Release>) + +################# +# install +################# +install(DIRECTORY include/ + DESTINATION include/utils + FILES_MATCHING + PATTERN "*.h" +) + +if(MSVC) + install(TARGETS dv_utils + RUNTIME DESTINATION bin/$<$:Debug>$<$:Release> + ARCHIVE DESTINATION lib/$<$:Debug>$<$:Release> +) +else(MSVC) + install(TARGETS dv_utils + RUNTIME DESTINATION bin + ARCHIVE DESTINATION lib + ) +endif(MSVC) diff --git a/source/utils/include/buffer/dvu_dump_buffer.h b/source/utils/include/buffer/dvu_dump_buffer.h new file mode 100644 index 0000000..f7e4efb --- /dev/null +++ b/source/utils/include/buffer/dvu_dump_buffer.h @@ -0,0 +1,13 @@ +#pragma once + +#include "dvu_config.h" +#include "device/dvc_render_target.h" + +DV_UTILS_BEGIN_NAMESPACE + +bool DV_UTILS_API dump_pixel_buffer(const char* filename, int32_t width, int32_t height, + const void* pixelData, davinci::PixelFormat pixelFormat); + +bool DV_UTILS_API dump_render_target(const char* baseFilename, const davinci::RenderTarget& renderTarget); + +DV_UTILS_END_NAMESPACE diff --git a/source/utils/include/dvu_config.h b/source/utils/include/dvu_config.h new file mode 100644 index 0000000..fea2cc5 --- /dev/null +++ b/source/utils/include/dvu_config.h @@ -0,0 +1,13 @@ +#pragma once + +#include "dv_config.h" + +#ifdef DV_UTILS_EXPORTS +#define DV_UTILS_API __declspec(dllexport) +#else +#define DV_UTILS_API __declspec(dllimport) +#endif + +//utils namespace +#define DV_UTILS_BEGIN_NAMESPACE namespace davinci_utils { +#define DV_UTILS_END_NAMESPACE } diff --git a/source/utils/include/scene/dvu_load_gltf.h b/source/utils/include/scene/dvu_load_gltf.h new file mode 100644 index 0000000..99946a4 --- /dev/null +++ b/source/utils/include/scene/dvu_load_gltf.h @@ -0,0 +1,12 @@ +#pragma once + +#include "dvu_config.h" +#include "scene/dvc_scene.h" + +DV_UTILS_BEGIN_NAMESPACE + +bool DV_UTILS_API load_scene_from_gltf(const char* szSceneFiles, davinci::Scene& scene); + +bool DV_UTILS_API export_mesh_to_obj(const davinci::Scene& scene); + +DV_UTILS_END_NAMESPACE diff --git a/source/utils/source/buffer/dvu_dump_buffer.cpp b/source/utils/source/buffer/dvu_dump_buffer.cpp new file mode 100644 index 0000000..89863ff --- /dev/null +++ b/source/utils/source/buffer/dvu_dump_buffer.cpp @@ -0,0 +1,174 @@ +#include "buffer/dvu_dump_buffer.h" +#include "math/dvc_vector3.h" +#include "math/dvc_math_util.h" + +#include + +DV_UTILS_BEGIN_NAMESPACE + +bool dump_pixel_buffer(const char* filename, int32_t width, int32_t height, + const void* pixelData, davinci::PixelFormat pixelFormat) +{ + int32_t bit_depth = 8; + + int32_t color_type; + switch (pixelFormat) { + case davinci::PF_FLOAT32_RGBA: + case davinci::PF_UINT8_RGBA: + color_type = PNG_COLOR_TYPE_RGB_ALPHA; break; + case davinci::PF_FLOAT32_RGB: + color_type = PNG_COLOR_TYPE_RGB; break; + case davinci::PF_FLOAT32_R: + color_type = PNG_COLOR_TYPE_GRAY; break; + default: + //not support yet! + return false; + } + + //create png_struct + png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); + if (png_ptr == 0) return false; + + //create png info + png_infop info_ptr = png_create_info_struct(png_ptr); + if (png_ptr == 0) { + png_destroy_write_struct(&png_ptr, 0); + return false; + } + //set default error function + setjmp(png_jmpbuf(png_ptr)); + + FILE* fp = fopen(filename, "wb"); + if (fp == 0) { + png_destroy_write_struct(&png_ptr, 0); + return false; + } + + //init write io + png_init_io(png_ptr, fp); + + //write png head info + png_set_IHDR(png_ptr, info_ptr, (png_uint_32)width, (png_uint_32)height, + bit_depth, color_type, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + png_write_info(png_ptr, info_ptr); + + //integer pixelformat + if (davinci::PF_UINT8_RGBA == pixelFormat) { + for (int32_t y = 0; y < height; y++) { + const uint32_t* p = (const uint32_t*)pixelData + (height - 1 - y) * width; + png_write_rows(png_ptr, (png_bytepp)&p, 1); + } + } + else { + //write rows + png_size_t row_bytes = png_get_rowbytes(png_ptr, info_ptr); + png_byte* row = (png_byte*)malloc(row_bytes); + + switch (color_type) { + case PNG_COLOR_TYPE_RGB: + { + for (int32_t y = 0; y < height; y++) { + const davinci::fVector3* p = (const davinci::fVector3*)pixelData + (height - 1 - y) * width; + for (int32_t x = 0; x < width; x++) { + row[x * 3 + 0] = (uint8_t)(p->x * 255); + row[x * 3 + 1] = (uint8_t)(p->y * 255); + row[x * 3 + 2] = (uint8_t)(p->z * 255); + + p++; + } + png_write_rows(png_ptr, &row, 1); + } + } + break; + + case PNG_COLOR_TYPE_RGB_ALPHA: + { + for (int32_t y = 0; y < height; y++) { + const davinci::fVector4* p = (const davinci::fVector4*)pixelData + (height - 1 - y) * width; + for (int32_t x = 0; x < width; x++) { + row[x * 4 + 0] = (uint8_t)(p->x * 255); + row[x * 4 + 1] = (uint8_t)(p->y * 255); + row[x * 4 + 2] = (uint8_t)(p->z * 255); + row[x * 4 + 3] = (uint8_t)(p->w * 255); + + p++; + } + png_write_rows(png_ptr, &row, 1); + } + } + break; + + case PNG_COLOR_TYPE_GRAY: + { + for (int32_t y = 0; y < height; y++) { + const float* p = (const float*)pixelData + (height - 1 - y) * width; + for (int32_t x = 0; x < width; x++) { + row[x] = (uint8_t)((*p) * 255); + p++; + } + png_write_rows(png_ptr, &row, 1); + } + } + break; + default: + break; + } + free(row); + } + png_write_end(png_ptr, NULL); + fclose(fp); + return true; +} + +bool dump_render_target(const char* baseFilename, const davinci::RenderTarget& renderTarget) +{ + char szFileName[256] = { 0 }; + _snprintf(szFileName, 256, "%s_color.png", baseFilename); + + //save rgba + dump_pixel_buffer(szFileName, + renderTarget.getWidth(), renderTarget.getHeight(), + renderTarget.getColorBuffer().ptr(), davinci::PF_FLOAT32_RGBA + ); + + //save depth + float32_t depth_min = std::numeric_limits::max(); + float32_t depth_max = -std::numeric_limits::max(); + + std::vector depth_data((size_t)(renderTarget.getWidth() * renderTarget.getHeight())); + const float* p = renderTarget.getDepthBuffer().ptr(); + + for (size_t i = 0; i < (size_t)(renderTarget.getWidth() * renderTarget.getHeight()); i++) + { + depth_data[i] = davinci::MathUtil::saturate(*p++); +// depth_data[i] = *p++; + if (depth_data[i] > depth_max) + { + depth_max = depth_data[i]; + } + if (depth_data[i] < depth_min) + { + depth_min = depth_data[i]; + } + } + /* + if (depth_max - depth_min > 0.001) + { + for (size_t i = 0; i < (size_t)(renderTarget.getWidth() * renderTarget.getHeight()); i++) + { + //depth_data[i] = davinci::MathUtil::saturate(*p++); + depth_data[i] = (depth_data[i] - depth_min) / (depth_max - depth_min) + depth_min; + } + } + */ + _snprintf(szFileName, 256, "%s_depth.png", baseFilename); + dump_pixel_buffer(szFileName, + renderTarget.getWidth(), renderTarget.getHeight(), + &(depth_data[0]), davinci::PF_FLOAT32_R + ); + + return true; +} + +DV_UTILS_END_NAMESPACE diff --git a/source/utils/source/scene/dvu_load_gltf.cpp b/source/utils/source/scene/dvu_load_gltf.cpp new file mode 100644 index 0000000..0544e47 --- /dev/null +++ b/source/utils/source/scene/dvu_load_gltf.cpp @@ -0,0 +1,665 @@ +#include "scene/dvu_load_gltf.h" +#include "scene/dvc_scene.h" +#include "scene/dvc_scene_node.h" +#include "scene/component/dvc_mesh_component.h" +#include "scene/component/dvc_camera_component.h" +#include "scene/component/dvc_light_component.h" +#include "device/dvc_buffer_accessor.h" +#include "asset/dvc_mesh.h" +#include "math/dvc_vector3.h" +#include "math/dvc_vector4.h" + +#include + +#include +#include + +DV_UTILS_BEGIN_NAMESPACE + +//from TINYGLTF_COMPONENT_TYPE_*** to CT_*** +davinci::ComponentType _getComponentTypeFromGltf(int gltfComponentType) +{ + switch (gltfComponentType) + { + case TINYGLTF_COMPONENT_TYPE_BYTE: + return davinci::CT_Int8; + case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE: + return davinci::CT_Uint8; + case TINYGLTF_COMPONENT_TYPE_SHORT: + return davinci::CT_Int16; + case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT: + return davinci::CT_Uint16; + case TINYGLTF_COMPONENT_TYPE_INT: + return davinci::CT_Int32; + case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT: + return davinci::CT_Uint32; + case TINYGLTF_COMPONENT_TYPE_FLOAT: + return davinci::CT_Float32; + case TINYGLTF_COMPONENT_TYPE_DOUBLE: + return davinci::CT_Float64; + default: + return davinci::CT_UNKNOWN; + } +} + +//from TINYGLTF_TYPE_*** to DT_*** +davinci::DataType _getDataTypeFromGltf(int gltfDataType) +{ + switch (gltfDataType) + { + case TINYGLTF_TYPE_SCALAR: + return davinci::DT_Scalar; + case TINYGLTF_TYPE_VEC2: + return davinci::DT_Vec2; + case TINYGLTF_TYPE_VEC3: + return davinci::DT_Vec3; + case TINYGLTF_TYPE_VEC4: + return davinci::DT_Vec4; + case TINYGLTF_TYPE_MAT2: + return davinci::DT_Matrix2; + case TINYGLTF_TYPE_MAT3: + return davinci::DT_Matrix3; + case TINYGLTF_TYPE_MAT4: + return davinci::DT_Matrix4; + default: + return davinci::DT_UNKNOWN; + } +} + +//from TINYGLTF_MODE_*** to PT_*** +davinci::PrimitiveType _getPrimitiveTypeFromGltf(int gltfModelType) +{ + switch (gltfModelType) + { + case TINYGLTF_MODE_POINTS: + return davinci::PT_POINT_LIST; + case TINYGLTF_MODE_LINE: + return davinci::PT_LINE_LIST; + case TINYGLTF_MODE_LINE_LOOP: + return davinci::PT_LINE_LOOP; + case TINYGLTF_MODE_LINE_STRIP: + return davinci::PT_LINE_STRIP; + case TINYGLTF_MODE_TRIANGLES: + return davinci::PT_TRIANGLE_LIST; + case TINYGLTF_MODE_TRIANGLE_STRIP: + return davinci::PT_TRIANGLE_STRIP; + case TINYGLTF_MODE_TRIANGLE_FAN: + return davinci::PT_TRIANGLE_FAN; + default: + return davinci::PT_UNKNOWN; + } +} + +//from "POSITION/NORMAL/..." to VET_*** +davinci::VertexElementType _getElementTypeFromGltf(const std::string& gltfElementType) +{ + #define IS_MATCH_RANGE_TYPE(Prefix, PrefixLength, FirstValue, LastValue) \ + else if (gltfElementType.length() == PrefixLength + 1 && gltfElementType.rfind(Prefix, 0) == 0) \ + { \ + int32_t index = gltfElementType[PrefixLength] - '0'; \ + if (index<0 || index>(LastValue - FirstValue)) \ + { \ + return davinci::VET_UNKNOWN; \ + } \ + return static_cast(static_cast(FirstValue) + index); \ + } + + if (gltfElementType == "POSITION") + { + return davinci::VET_POSITION; + } + else if(gltfElementType == "NORMAL") + { + return davinci::VET_NORMAL; + } + else if (gltfElementType == "TANGENT") + { + return davinci::VET_TANGENT; + } + else if (gltfElementType == "BINORMAL") + { + return davinci::VET_BINORMAL; + } + IS_MATCH_RANGE_TYPE("COLOR_", 6, davinci::VET_COLOR_0, davinci::VET_COLOR_LAST) + IS_MATCH_RANGE_TYPE("TEXCOORD_", 9, davinci::VET_TEXCOORD_0, davinci::VET_TEXCOORD_LAST) + IS_MATCH_RANGE_TYPE("JOINTS_", 7, davinci::VET_JOINTS_0, davinci::VET_JOINTS_LAST) + IS_MATCH_RANGE_TYPE("WEIGHTS_", 8, davinci::VET_WEIGHTS_0, davinci::VET_WEIGHTS_LAST) + + return davinci::VET_UNKNOWN; +} + +bool _loadDeviceBuffer(const tinygltf::Model& gltfModel, davinci::Scene& scene) +{ + for (size_t i = 0; i < gltfModel.buffers.size(); i++) + { + const tinygltf::Buffer& gltfBuffer = gltfModel.buffers[i]; + + davinci::DeviceBufferPtr deviceBufferPtr = scene.newDeviceBuffer(); + deviceBufferPtr->init(gltfBuffer.data.size()); + + //fill data + memcpy(deviceBufferPtr->ptr(0), gltfBuffer.data.data(), gltfBuffer.data.size()); + } + return true; +} + +bool _loadBufferView(const tinygltf::Model& gltfModel, davinci::Scene& scene) +{ + for (size_t i = 0; i < gltfModel.bufferViews.size(); i++) + { + const tinygltf::BufferView& gltfBufferView = gltfModel.bufferViews[i]; + int32_t bufferIndex = gltfBufferView.buffer; + davinci::DeviceBufferConstPtr deviceBufferPtr = scene.getDeviceBuffer(bufferIndex); + if (deviceBufferPtr == nullptr) + { + return false; + } + davinci::DeviceBufferViewPtr bufferViewPtr = scene.newDeviceBufferView( + deviceBufferPtr, gltfBufferView.byteOffset, gltfBufferView.byteLength, gltfBufferView.byteStride); + } + + return true; +} + +bool _loadBufferAccessor(const tinygltf::Model& gltfModel, davinci::Scene& scene) +{ + for (size_t i = 0; i < gltfModel.accessors.size(); i++) + { + const tinygltf::Accessor& gltfAccessor = gltfModel.accessors[i]; + + int32_t bufferViewIndex = gltfAccessor.bufferView; + davinci::DeviceBufferViewPtr deviceBufferView = scene.getDeviceBufferView(bufferViewIndex); + if (deviceBufferView==nullptr) + { + return false; + } + + davinci::ComponentType componentType = _getComponentTypeFromGltf(gltfAccessor.componentType); + if (componentType == davinci::CT_UNKNOWN) + { + return false; + } + + davinci::DataType dataType = _getDataTypeFromGltf(gltfAccessor.type); + if (dataType == davinci::DT_UNKNOWN) + { + return false; + } + + davinci::DeviceBufferAccessorPtr bufferAccessor = scene.newDeviceBufferAccessor( + deviceBufferView, gltfAccessor.byteOffset, gltfAccessor.count, componentType, dataType); + } + + return true; +} + +bool _loadMesh(const tinygltf::Model& gltfModel, davinci::Scene& scene) +{ + for (size_t i=0; i(gltfMesh.name); + + for (size_t j=0; jnewPrimitive(); + primitive->type = _getPrimitiveTypeFromGltf(gltfPrimitive.mode); + + std::map::const_iterator it(gltfPrimitive.attributes.begin()); + std::map::const_iterator itEnd(gltfPrimitive.attributes.end()); + for (; it != itEnd; it++) + { + davinci::VertexElementType vetype = _getElementTypeFromGltf(it->first); + + int32_t gltfAccessor = it->second; + davinci::DeviceBufferAccessorPtr deviceBufferAccessor = scene.getDeviceBufferAccessor(gltfAccessor); + if (deviceBufferAccessor ==nullptr) + { + return false; + } + + primitive->attributes.insert(std::make_pair(vetype, deviceBufferAccessor)); + } + + davinci::DeviceBufferAccessorPtr indicesBufferAccessor = scene.getDeviceBufferAccessor(gltfPrimitive.indices); + if (indicesBufferAccessor == nullptr) + { + return false; + } + primitive->indices = indicesBufferAccessor; + } + + scene.pushNewMeshAsset(mesh); + } + return true; +} + +bool _loadLightExternsion(const tinygltf::Model& gltfModel, std::vector& lightDefineArray) +{ + lightDefineArray.clear(); + + //read lights define from externsions + tinygltf::Value lightsArray; + auto lightExternsions = gltfModel.extensions.find("KHR_lights_punctual"); + if (lightExternsions == gltfModel.extensions.end()) + { + return true; + } + + lightsArray = lightExternsions->second.Get("lights"); + if (!lightsArray.IsArray()) + { + return false; + } + + size_t lightCounts = lightsArray.ArrayLen(); + for (size_t i = 0; i < lightCounts; i++) + { + auto lightObj = lightsArray.Get(i); + + davinci::LightComponent::LightDefine lightDefine; + + auto lightType = lightObj.Get("type"); + if (!lightType.IsString()) { + lightDefineArray.clear(); + return false; + } + + std::string lightTypeString = lightType.Get(); + if (lightTypeString == "point") { + lightDefine.type = davinci::LightComponent::LT_Point; + } + else if (lightTypeString == "directional") { + lightDefine.type = davinci::LightComponent::LT_Directional; + } + else if (lightTypeString == "spot") { + lightDefine.type = davinci::LightComponent::LT_Spot; + } + else { + //unknown type? + lightDefineArray.clear(); + return false; + } + + //TODO: read other defines... + lightDefine.color = davinci::fVector3(1.0, 1.0, 1.0); + lightDefine.intensity = 1.0f; + lightDefine.range = -1.0f; + + lightDefineArray.push_back(lightDefine); + } + return true; +} + + +bool _loadSceneNodes(const tinygltf::Model& gltfModel, davinci::Scene& scene, const std::vector& lightsArray) +{ + std::vector> sceneNodeArray; + + //read scene nodes + for (size_t i = 0; i < gltfModel.nodes.size(); i++) + { + const tinygltf::Node& gltfNode = gltfModel.nodes[i]; + + davinci::SceneNodePtr sceneNode = scene.newSceneNode(gltfNode.name); + davinci::fMatrix4 transform = davinci::fMatrix4::IDENTITY; + + if (!gltfNode.scale.empty()) + { + transform = + davinci::fMatrix4::makeScale(gltfNode.scale[0], gltfNode.scale[1], gltfNode.scale[2]) * transform; + } + if (!gltfNode.rotation.empty()) + { + davinci::fVector4 quaternion(gltfNode.rotation[0], gltfNode.rotation[1], gltfNode.rotation[2], gltfNode.rotation[3]); + transform = davinci::fMatrix4::makeRotate(quaternion) * transform; + } + if (!gltfNode.translation.empty()) + { + transform = davinci::fMatrix4::makeTrans(gltfNode.translation[0], gltfNode.translation[1], gltfNode.translation[2]) * transform; + } + sceneNode->setTransform(transform); + + //add mesh component + if (gltfNode.mesh != -1) + { + davinci::MeshConstPtr meshPtr = scene.getMesh(gltfNode.mesh); + if (meshPtr) + { + davinci::MeshComponentPtr meshComponet = std::make_shared(sceneNode); + + meshComponet->setMesh(meshPtr); + + sceneNode->addComponent(std::dynamic_pointer_cast(meshComponet)); + } + } + //add camera component + if (gltfNode.camera != -1) + { + const tinygltf::Camera& gltfCamera = gltfModel.cameras[gltfNode.camera]; + if (gltfCamera.type == "perspective") + { + davinci::CameraComponentPtr cameraComponent = std::make_shared(sceneNode); + + davinci::CameraComponent::CameraDefine cameraDefine; + cameraDefine.aspect = gltfCamera.perspective.aspectRatio; + cameraDefine.fov = gltfCamera.perspective.yfov; + cameraDefine.nearClip = gltfCamera.perspective.znear; + cameraDefine.farClip = gltfCamera.perspective.zfar; + cameraComponent->setCameraDefine(cameraDefine); + + sceneNode->addComponent(std::dynamic_pointer_cast(cameraComponent)); + } + } + + //add light component(KHR_lights_punctual) + do + { + auto node_light_it = gltfNode.extensions.find("KHR_lights_punctual"); + if (node_light_it == gltfNode.extensions.end()) { + break; + } + + auto lightValue = node_light_it->second.Get("light"); + if (!lightValue.IsNumber()) { + break; + } + + int32_t lightIndex = lightValue.GetNumberAsInt(); + if (lightIndex < 0 || lightIndex >= lightsArray.size()) { + break; + } + + davinci::LightComponentPtr lightComponent = std::make_shared(sceneNode); + lightComponent->setLightDefine(lightsArray[lightIndex]); + sceneNode->addComponent(std::dynamic_pointer_cast(lightComponent)); + + } while (false); + + sceneNodeArray.push_back(std::make_pair(true, sceneNode)); + } + + assert(sceneNodeArray.size() == gltfModel.nodes.size()); + + for (size_t i = 0; i < gltfModel.nodes.size(); i++) + { + const tinygltf::Node& gltfNode = gltfModel.nodes[i]; + if (gltfNode.children.empty()) continue; + + //attach children nodes + for (size_t j = 0; j < gltfNode.children.size(); j++) + { + int32_t childrenIndex = gltfNode.children[j]; + if (childrenIndex < 0 || childrenIndex >= gltfModel.nodes.size()) + { + //error! + continue; + } + if (!(sceneNodeArray[childrenIndex].first)) //already attached? + { + //error! + continue; + } + sceneNodeArray[childrenIndex].first = false; + sceneNodeArray[i].second->addChild(sceneNodeArray[childrenIndex].second); + } + } + + //add root node + for (size_t i = 0; i < gltfModel.scenes.size(); i++) + { + const tinygltf::Scene& gltfScene = gltfModel.scenes[i]; + for (size_t j = 0; j < gltfScene.nodes.size(); j++) + { + int sceneNodeIndex = gltfScene.nodes[j]; + if(sceneNodeIndex<0 || sceneNodeIndex>= gltfModel.nodes.size()) + { + //error! + continue; + } + if (!(sceneNodeArray[sceneNodeIndex].first)) //already attached? + { + //error! + continue; + } + scene.getRootSceneNode()->addChild(sceneNodeArray[sceneNodeIndex].second); + } + } + + //clear temp scene node + sceneNodeArray.clear(); + return true; +} + +bool load_scene_from_gltf(const char* szSceneFiles, davinci::Scene& scene) +{ + std::filesystem::path sceneFilePath = std::filesystem::path(szSceneFiles); + + //is file exist? + if (!std::filesystem::exists(std::string(szSceneFiles))) + { + return false; + } + + tinygltf::Model gltfModel; + tinygltf::TinyGLTF gltf_ctx; + std::string err; + std::string warn; + bool ret = false; + + //get file ext + std::filesystem::path ext = sceneFilePath.extension(); + + scene.init(); + + //-------------------------- + //load gltf + if (ext == ".glb") + { + //binary file + ret = gltf_ctx.LoadBinaryFromFile(&gltfModel, &err, &warn, szSceneFiles); + } + else + { + //text file + ret = gltf_ctx.LoadASCIIFromFile(&gltfModel, &err, &warn, szSceneFiles); + } + + //error + if (!ret) + { + //TODO: log something + return false; + } + + //-------------------------- + //dump to davinci scene + + //load device buffer + if (!_loadDeviceBuffer(gltfModel, scene)) + { + return false; + } + + //load buffer view + if (!_loadBufferView(gltfModel, scene)) + { + return false; + } + + //load buffer accessor + if (!_loadBufferAccessor(gltfModel, scene)) + { + return false; + } + + //load mesh + if (!_loadMesh(gltfModel, scene)) + { + return false; + } + + //load light externsion + std::vector lightDefine; + if (!_loadLightExternsion(gltfModel, lightDefine)) + { + return false; + } + + //load scene nodes + if (!_loadSceneNodes(gltfModel, scene, lightDefine)) + { + return false; + } + + return true; +} + + +bool export_mesh_to_obj(const davinci::Scene& scene) +{ + + int32_t meshCounts = scene.getMeshCounts(); + for (int32_t meshIndex = 0; meshIndex < meshCounts; meshIndex++) + { + davinci::MeshConstPtr mesh = scene.getMesh(meshIndex); + + const std::string& meshName = mesh->getName(); + if (meshName.empty()) { + continue; + } + + char szTemp[32] = { 0 }; + _snprintf(szTemp, 32, "d:\\%s.obj", meshName.c_str()); + + FILE* fp = fopen(szTemp, "w"); + fprintf(fp, "o %s\n", meshName.c_str()); + + int32_t primitiveCounts = mesh->getPrimitiveCounts(); + for (int32_t primitiveIndex = 0; primitiveIndex < primitiveCounts; primitiveIndex++) + { + davinci::Mesh::PrimitiveConstPtr primitive = mesh->getPrimitive(primitiveIndex); + + //Output "POSITION" + davinci::Mesh::PrimitiveAttributes::const_iterator posAttribute = primitive->attributes.find(davinci::VET_POSITION); + if (posAttribute != primitive->attributes.end()) + { + davinci::DeviceBufferAccessorConstPtr posBuffer = posAttribute->second; + posBuffer->walk([=](const uint8_t* ptr, size_t index) { + const float32_t* pPos = (const float32_t*)ptr; + + fprintf(fp, "v %f %f %f\n", pPos[0], pPos[1], pPos[2]); + return false; + }); + } + + //Output "NORMAL" + davinci::Mesh::PrimitiveAttributes::const_iterator normalAttribute = primitive->attributes.find(davinci::VET_NORMAL); + bool bHasNormal = (normalAttribute != primitive->attributes.end()); + if (bHasNormal) + { + davinci::DeviceBufferAccessorConstPtr normalBuffer = normalAttribute->second; + normalBuffer->walk([=](const uint8_t* ptr, size_t index) { + const float32_t* pNormal = (const float32_t*)ptr; + + fprintf(fp, "vn %f %f %f\n", pNormal[0], pNormal[1], pNormal[2]); + return false; + }); + } + + //Output "TEXCOORD_0" + davinci::Mesh::PrimitiveAttributes::const_iterator tex0Attribute = primitive->attributes.find(davinci::VET_TEXCOORD_0); + bool bHasTex0 = (tex0Attribute != primitive->attributes.end()); + if (bHasTex0) + { + davinci::DeviceBufferAccessorConstPtr tex0Buffer = tex0Attribute->second; + tex0Buffer->walk([=](const uint8_t* ptr, size_t index) { + const float32_t* pTex0 = (const float32_t*)ptr; + + fprintf(fp, "vt %f %f\n", pTex0[0], pTex0[1]); + return false; + }); + } + + //fprintf(fp, "TYPE:%d\n", primitive->type); + + davinci::DeviceBufferAccessorConstPtr indexBufferAccessor = primitive->indices; + indexBufferAccessor->walk3([=](const uint8_t* ptr0, const uint8_t* ptr1, const uint8_t* ptr2, size_t index) { + + uint32_t indice0, indice1, indice2; + if (indexBufferAccessor->getComponentType() == davinci::CT_Uint16) + { + indice0 = *((uint16_t*)ptr0) + 1; + indice1 = *((uint16_t*)ptr1) + 1; + indice2 = *((uint16_t*)ptr2) + 1; + } + else if (indexBufferAccessor->getComponentType() == davinci::CT_Uint32) + { + indice0 = *((uint32_t*)ptr0) + 1; + indice1 = *((uint32_t*)ptr1) + 1; + indice2 = *((uint32_t*)ptr2) + 1; + } + else if (indexBufferAccessor->getComponentType() == davinci::CT_Uint8) + { + indice0 = *((uint8_t*)ptr0) + 1; + indice1 = *((uint8_t*)ptr1) + 1; + indice2 = *((uint8_t*)ptr2) + 1; + } + else + { + //error + return true; + } + + if (bHasTex0) + { + if (bHasNormal) + { + fprintf(fp, "f %d/%d/%d %d/%d/%d %d/%d/%d\n", + indice0, indice0, indice0, + indice1, indice1, indice1, + indice2, indice2, indice2 + ); + } + else + { + fprintf(fp, "f %d/%d %d/%d %d/%d\n", + indice0, indice0, + indice1, indice1, + indice2, indice2 + ); + } + } + else + { + if (bHasNormal) + { + fprintf(fp, "f %d//%d %d//%d %d//%d\n", + indice0, indice0, + indice1, indice1, + indice2, indice2 + ); + } + else + { + fprintf(fp, "f %d %d %d\n", + indice0, + indice1, + indice2 + ); + } + } + return false; + }); + + } + + fclose(fp); + } + return true; +} + +DV_UTILS_END_NAMESPACE diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..95ecbf9 --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,6 @@ +set(GTest_DIR $ENV{LIBRARIES_SDK_ROOT}/gtest/lib/cmake/GTest) +find_package(GTest CONFIG REQUIRED) + +add_subdirectory(unit) +add_subdirectory(shader) +add_subdirectory(rasterization) diff --git a/test/rasterization/CMakeLists.txt b/test/rasterization/CMakeLists.txt new file mode 100644 index 0000000..371305e --- /dev/null +++ b/test/rasterization/CMakeLists.txt @@ -0,0 +1,19 @@ +include_directories( + ${DV_AUTO_INCLUDE_PATH} + ${DV_CORE_INCLUDE_PATH} + ${DV_UTILS_INCLUDE_PATH} +) + +add_executable(dvt_rasterization WIN32 + dvr_main.cpp + dvr_triangle.h + dvr_triangle.cpp +) + +set_property(TARGET dvt_rasterization PROPERTY FOLDER "test") + +target_link_libraries(dvt_rasterization + dv_core + SDL3::SDL3-static + OpenGL::GL +) diff --git a/test/rasterization/dvr_main.cpp b/test/rasterization/dvr_main.cpp new file mode 100644 index 0000000..b4ef4c9 --- /dev/null +++ b/test/rasterization/dvr_main.cpp @@ -0,0 +1,505 @@ +#include +#include +#include +#include + +#define SDL_MAIN_USE_CALLBACKS 1 /* use the callbacks instead of main() */ +#include +#include +#include + +#include "math/dvc_vector2.h" +#include "math/dvc_vector3.h" + +#include "dvr_triangle.h" + +using namespace davinci; + +enum MouseAction +{ + MT_NULL, + MT_FIRST_LINE, + MT_TRIANGLE +}; + +const float MIN_SCALE = 4.f; +const float MAX_SCALE = 60.f; +const float TRIANGLE_BORDER_WIDTH = 1.5f; +const float ACTION_WIDTH = 1.f; +const fVector3 GRID_BORDER_COLOR = fVector3(0.f, 0.f, 0.f); +const fVector3 GRID_CROSS_COLOR = fVector3(0.8f, 0.8f, 0.8f); +const fVector3 TRIANGLE_BORDER_COLOR = fVector3(0.f, 0.f, 0.f); +const fVector3 ACTION_COLOR = fVector3(1.f, 0.f, 0.f); + + +int32_t g_viewWidth = 2048; +int32_t g_viewHeight = 1440; +int32_t g_canvasWidth; +int32_t g_canvasHeight; +float32_t g_scale = MAX_SCALE; +int32_t g_lbCorner_x = 20; +int32_t g_lbCorner_y = 20; +int32_t g_nDrawGridLevel = 0; //0:all 1:fine+coarse+tile 2:coarse+tile 3: tile +float32_t g_mousePosX, g_mousePosY; +bool g_bSnapMode = true; +float g_fSnapSize = 0.25f; +MouseAction g_currentAction = MT_NULL; +bool g_bDrawExtension = false; + +Triangle g_currentTriangle; +TriangleVector g_allTriangles; + +SDL_Window* g_window = NULL; +SDL_Renderer* g_renderer = NULL; + +#define trans_x(x) ((float32_t)(x) * g_scale + g_lbCorner_x) +#define trans_y(y) ((float32_t)(y) * g_scale + g_lbCorner_y) + +void _onFramebufferSize(int32_t w, int32_t h) +{ + g_viewWidth = w; + g_viewHeight = h > 0 ? h : 1; + + g_canvasWidth = ((int32_t)floorf(g_viewWidth / (g_scale * 64)) + 1) * 64; + g_canvasHeight = ((int32_t)floorf(g_viewHeight / (g_scale * 64)) + 1) * 64; + + if (g_scale < 10.f) g_nDrawGridLevel = 3; + else if (g_scale < 20.f) g_nDrawGridLevel = 2; + else if (g_scale < 50.f) g_nDrawGridLevel = 1; + else g_nDrawGridLevel = 0; +} + +static void _drawGrid(void) +{ + int32_t horLength = (int32_t)floorf(g_viewWidth / g_scale); + int32_t verLength = (int32_t)floorf(g_viewHeight / g_scale); + + fVector3 lineColor[] = { fVector3(0.f, 0.f, 0.f), fVector3(0.5f, 0.5f, 0.5f), fVector3(0.7f,0.7f,0.7f), fVector3(0.9f, 0.9f,0.9f) }; + + // Horizontal lines + for (int i = 0; i <= verLength; i++) { + int level = 3; + + if (i % 64 == 0)level = 0; + else if (i % 16 == 0) level = 1; + else if (i % 4 == 0) level = 2; + if (level > 3 - g_nDrawGridLevel) continue; + + SDL_SetRenderDrawColorFloat(g_renderer, lineColor[level].x, lineColor[level].y, lineColor[level].z, SDL_ALPHA_OPAQUE_FLOAT); + + SDL_RenderLine(g_renderer, trans_x(0), trans_y(i), trans_x(horLength), trans_y(i)); + } + + // Vertical lines + for (int j = 0; j <= horLength; j++) { + int level = 3; + + if (j % 64 == 0)level = 0; + else if (j % 16 == 0) level = 1; + else if (j % 4 == 0) level = 2; + if (level > 3 - g_nDrawGridLevel) continue; + + SDL_SetRenderDrawColorFloat(g_renderer, lineColor[level].x, lineColor[level].y, lineColor[level].z, SDL_ALPHA_OPAQUE_FLOAT); + SDL_RenderLine(g_renderer, trans_x(j), trans_y(0), trans_x(j), trans_y(verLength)); + } + + //cross + if (g_nDrawGridLevel < 3) + { + SDL_SetRenderDrawColorFloat(g_renderer, GRID_CROSS_COLOR.x, GRID_CROSS_COLOR.y, GRID_CROSS_COLOR.z, SDL_ALPHA_OPAQUE_FLOAT); + + float crossLength = 0.2f; + for (int i = 0; i < verLength; i++) + { + for (int j = 0; j < horLength; j++) + { + SDL_RenderLine(g_renderer, + trans_x(j + 0.5f - crossLength), trans_y(i + 0.5f - crossLength), + trans_x(j + 0.5f + crossLength), trans_y(i + 0.5f + crossLength) + ); + + SDL_RenderLine(g_renderer, + trans_x(j + 0.5f - crossLength), trans_y(i + 0.5f + crossLength), + trans_x(j + 0.5f + crossLength), trans_y(i + 0.5f - crossLength) + ); + } + } + } +} + +static void _drawTrianglePixel(const Triangle& triangle) +{ + SDL_SetRenderDrawBlendMode(g_renderer, SDL_BLENDMODE_BLEND); + SDL_SetRenderDrawColorFloat(g_renderer, triangle.pixelColor[0], triangle.pixelColor[1], triangle.pixelColor[2], 0.5f); + + for (const auto& dot : triangle.pixels) + { + SDL_FRect rect; + rect.x = trans_x(dot.first); + rect.y = trans_y(dot.second); + rect.w = g_scale; + rect.h = g_scale; + + SDL_RenderFillRect(g_renderer, &rect); + } +} + +void _drawTriangleBorder(const Triangle& triangle, const fVector3& color, float lineWidth) +{ + SDL_SetRenderDrawColorFloat(g_renderer, color[0], color[1], color[2], SDL_ALPHA_OPAQUE_FLOAT); + + SDL_FPoint points[4] = { + {trans_x(triangle.p0.x), trans_y(triangle.p0.y)}, + {trans_x(triangle.p1.x), trans_y(triangle.p1.y)}, + {trans_x(triangle.p2.x), trans_y(triangle.p2.y)}, + {trans_x(triangle.p0.x), trans_y(triangle.p0.y)} + }; + SDL_RenderLines(g_renderer, points, 4); +} + + +void _drawTriangles(void) +{ + for (const auto& triangle : g_allTriangles) { + _drawTrianglePixel(triangle); + } + + for (const auto& triangle : g_allTriangles) { + _drawTriangleBorder(triangle, TRIANGLE_BORDER_COLOR, TRIANGLE_BORDER_WIDTH); + } +} + +void _drawSegmentInAABB(const fVector2& p0, const fVector2& p1, float xMin, float xMax, float yMin, float yMax) +{ + float32_t dx = p1.x - p0.x; + float32_t dy = p1.y - p0.y; + if (std::abs(dx) > std::abs(dy)) + { + float _ymin = (xMin - p0.x) * dy / dx + p0.y; + float _ymax = (xMax - p0.x) * dy / dx + p0.y; + + if (dx > 0) { + SDL_RenderLine(g_renderer, trans_x(xMin), trans_y(_ymin), trans_x(p0.x), trans_y(p0.y) ); + SDL_RenderLine(g_renderer, trans_x(p1.x), trans_y(p1.y), trans_x(xMax), trans_y(_ymax) ); + } + else { + SDL_RenderLine(g_renderer, trans_x(xMin), trans_y(_ymin), trans_x(p1.x), trans_y(p1.y)); + SDL_RenderLine(g_renderer, trans_x(p0.x), trans_y(p0.y), trans_x(xMax), trans_y(_ymax)); + } + } + else { + float _xmin = (yMin - p0.y) * dx / dy + p0.x; + float _xmax = (yMax - p0.y) * dx / dy + p0.x; + + if (dy > 0) { + SDL_RenderLine(g_renderer, trans_x(_xmin), trans_y(yMin), trans_x(p0.x), trans_y(p0.y)); + SDL_RenderLine(g_renderer, trans_x(p1.x), trans_y(p1.y), trans_x(_xmax), trans_y(yMax)); + } + else { + SDL_RenderLine(g_renderer, trans_x(_xmin), trans_y(yMin), trans_x(p1.x), trans_y(p1.y)); + SDL_RenderLine(g_renderer, trans_x(p0.x), trans_y(p0.y), trans_x(_xmax), trans_y(yMax)); + } + } +} + +void _drawAction(void) +{ + if (g_bDrawExtension) { + SDL_SetRenderDrawColorFloat(g_renderer, ACTION_COLOR.x/2.f, ACTION_COLOR.y/2.f, ACTION_COLOR.z/2.f, SDL_ALPHA_OPAQUE_FLOAT); + + _drawSegmentInAABB(g_currentTriangle.p0.xy(), g_currentTriangle.p1.xy(), 0, (float)g_canvasWidth, 0, (float)g_canvasHeight); + _drawSegmentInAABB(g_currentTriangle.p1.xy(), g_currentTriangle.p2.xy(), 0, (float)g_canvasWidth, 0, (float)g_canvasHeight); + _drawSegmentInAABB(g_currentTriangle.p2.xy(), g_currentTriangle.p0.xy(), 0, (float)g_canvasWidth, 0, (float)g_canvasHeight); + } + + SDL_SetRenderDrawColorFloat(g_renderer, ACTION_COLOR.x, ACTION_COLOR.y, ACTION_COLOR.z, SDL_ALPHA_OPAQUE_FLOAT); + switch (g_currentAction) + { + case MT_FIRST_LINE: + { + SDL_RenderLine(g_renderer, + trans_x(g_currentTriangle.p0.x), trans_y(g_currentTriangle.p0.y), + trans_x(g_currentTriangle.p1.x), trans_y(g_currentTriangle.p1.y) + ); + } + break; + + case MT_TRIANGLE: + { + g_currentTriangle.build(ACTION_COLOR, g_canvasWidth, g_canvasHeight); + + _drawTrianglePixel(g_currentTriangle); + _drawTriangleBorder(g_currentTriangle, ACTION_COLOR, ACTION_WIDTH); + } + break; + + default: + break; + } +} + +void _drawMouseCursor(void) +{ + if (g_bSnapMode) + { + SDL_SetRenderDrawColorFloat(g_renderer, 0, 0, 0, SDL_ALPHA_OPAQUE_FLOAT); + + const float32_t CursorLength = 10.f / g_scale; + + SDL_RenderLine(g_renderer, + trans_x(g_mousePosX - CursorLength), trans_y(g_mousePosY), + trans_x(g_mousePosX + CursorLength), trans_y(g_mousePosY) + ); + + SDL_RenderLine(g_renderer, + trans_x(g_mousePosX), trans_y(g_mousePosY - CursorLength), + trans_x(g_mousePosX), trans_y(g_mousePosY + CursorLength) + ); + } +} + +void _drawDebugText() +{ + const float32_t TextHeight = (float32_t)SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE*1.5; + + SDL_SetRenderDrawColorFloat(g_renderer, 0, 0, 0, SDL_ALPHA_OPAQUE_FLOAT); + + SDL_SetRenderScale(g_renderer, 1.5f, 1.5f); + SDL_RenderDebugText(g_renderer, g_lbCorner_x, g_lbCorner_y, "Press 'S' to turn on/off snap mode."); + SDL_RenderDebugText(g_renderer, g_lbCorner_x, g_lbCorner_y+TextHeight, "Click 'LeftMouseButton' to draw new triangle."); + + SDL_SetRenderScale(g_renderer, 1.0f, 1.0f); +} + +inline float _snapPosition(float pos) +{ + if (g_fSnapSize < 1) { + int invSize = (int)(1.f / g_fSnapSize); + + float x_f = floorf(((pos - floorf(pos)) * invSize + 0.5f)) / (float)invSize; + return floorf(pos) + x_f; + } + else { + return floorf(pos - floorf(pos / g_fSnapSize) + 0.5f) * g_fSnapSize; + } +} + + +void _onMouseMove(float32_t x, float32_t y) +{ + g_mousePosX = (float)(x - g_lbCorner_x) / g_scale; + g_mousePosY = (float)(y - g_lbCorner_y) / g_scale; + + if (g_bSnapMode) { + g_mousePosX = _snapPosition(g_mousePosX); + g_mousePosY = _snapPosition(g_mousePosY); + } + + switch (g_currentAction) + { + case MT_FIRST_LINE: + { + g_currentTriangle.p1 = fVector3(g_mousePosX, g_mousePosY, 0); + } + break; + + case MT_TRIANGLE: + { + g_currentTriangle.p2 = fVector3(g_mousePosX, g_mousePosY, 0); + } + break; + + default: break; + } +} + +void _onMouseWheel(float32_t x, float32_t y) +{ + g_scale += (float)y * g_scale * 0.05f; + if (g_scale < MIN_SCALE) g_scale = MIN_SCALE; + if (g_scale > MAX_SCALE) g_scale = MAX_SCALE; + + _onFramebufferSize(g_viewWidth, g_viewHeight); +} + +void _onKeyDown(SDL_Keycode key) +{ + if (SDLK_S == key) + { + g_bSnapMode = !g_bSnapMode; + } + else if (SDLK_ESCAPE == key) + { + g_currentAction = MT_NULL; + g_bDrawExtension = false; + } +} + +void _onMouseClick(Uint8 button) +{ + if (button == SDL_BUTTON_LEFT) + { + switch (g_currentAction) + { + case MT_NULL: + { + g_currentTriangle.p0 = g_currentTriangle.p1 = fVector3(g_mousePosX, g_mousePosY, 0); + g_currentAction = MT_FIRST_LINE; + g_bDrawExtension = false; + } + break; + + case MT_FIRST_LINE: + { + g_currentTriangle.p1 = g_currentTriangle.p2 = fVector3(g_mousePosX, g_mousePosY, 0); + g_currentAction = MT_TRIANGLE; + g_bDrawExtension = true; + } + break; + + case MT_TRIANGLE: + { + g_currentTriangle.p2 = fVector3(g_mousePosX, g_mousePosY, 0); + g_currentAction = MT_NULL; + + //build triangle + fVector3 randColor = fVector3(rand() * 0.8f / RAND_MAX, rand() * 0.8f / RAND_MAX, rand() * 0.8f / RAND_MAX); + + g_currentTriangle.build(randColor, g_canvasWidth, g_canvasHeight, true); + g_allTriangles.push_back(g_currentTriangle); + g_bDrawExtension = true; + } + break; + + default: + break; + } + } + else if (button== SDL_BUTTON_RIGHT) + { + g_currentAction = MT_NULL; + g_bDrawExtension = false; + } +} + +void _buildDemoTriangles(void) +{ + // https://msdn.microsoft.com/en-us/library/windows/desktop/cc627092(v=vs.85).aspx + + float32_t triangleData[] = { + 1, 2,7, 4,5, 2, + 5, 2,7, 4,8, 1, + 8, 1,7, 4,9.5f, 2.5f, + 1, 7,6, 6,2, 4, + 5.25f, 6.75f, 6.25f, 7.75,6.25f, 6.75f, + 6.5f, 5.5f,7.5f, 7.5f,7.5f, 6.5f, + 11.5f, 1.5f,11.5f, 3.5f, 12.5f, 2.5f, + 13.5f, 0.5f,13.5f, 2.5f, 15.5f, 2.5f, + 13.5f, 0.5f,15.5f, 2.5f, 15.5f, 0.5f, + 13.5f, 6.5f,14.5f, 5.5f, 14.5f, 3.5f, + 13.5f, 6.5f,15, 8, 14.5f, 5.5f, + 9.5f, 0.5f, 10.5f, 0.5f,9.5f, -1.5f, + 7.6f, 5.5f,9.75f, 7.25f, 11.6f, 5.5f, + 7.6f, 5.5f, 11.6f, 5.5f, 9.5f, 2.6f, + }; + + size_t triangleCounts = sizeof(triangleData) / (6 * sizeof(float)); + for (size_t i = 0; i < triangleCounts; i++) { + + fVector3 p0(triangleData[i * 6 + 0], triangleData[i * 6 + 1], 0.f); + fVector3 p1(triangleData[i * 6 + 2], triangleData[i * 6 + 3], 0.f); + fVector3 p2(triangleData[i * 6 + 4], triangleData[i * 6 + 5], 0.f); + fVector3 randColor = fVector3(rand() * 0.8f / RAND_MAX, rand() * 0.8f / RAND_MAX, rand() * 0.8f / RAND_MAX); + + Triangle triangle(p0, p1, p2); + triangle.build(randColor, g_canvasWidth, g_canvasHeight, false); + g_allTriangles.emplace_back(triangle); + } +} + +SDL_AppResult SDL_AppInit(void** appstate, int argc, char* argv[]) +{ + // Setup SDL + SDL_SetAppMetadata("Davinci Rasterization Test Program", "1.0", "com.thecodeway.davinci"); + + if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD)) + { + printf("Error: SDL_Init(): %s\n", SDL_GetError()); + return SDL_APP_FAILURE; + } + + if (!SDL_CreateWindowAndRenderer("Davinci Rasterization Test Program", g_viewWidth, g_viewHeight, SDL_WINDOW_RESIZABLE, &g_window, &g_renderer)) + { + SDL_Log("Couldn't create window/renderer: %s", SDL_GetError()); + return SDL_APP_FAILURE; + } + + _onFramebufferSize(g_viewWidth, g_viewHeight); + _buildDemoTriangles(); + + return SDL_APP_CONTINUE; /* carry on with the program! */ +} + +/* This function runs when a new event (mouse input, keypresses, etc) occurs. */ +SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event) +{ + switch (event->type) + { + case SDL_EVENT_QUIT: + return SDL_APP_SUCCESS; + break; + + case SDL_EVENT_WINDOW_RESIZED: + { + _onFramebufferSize(event->window.data1, event->window.data2); + } + break; + + case SDL_EVENT_MOUSE_MOTION: + { + _onMouseMove(event->motion.x, event->motion.y); + } + break; + + case SDL_EVENT_MOUSE_WHEEL: + { + _onMouseWheel(event->wheel.x, event->wheel.y); + } + break; + + case SDL_EVENT_KEY_DOWN: + { + _onKeyDown(event->key.key); + } + break; + + case SDL_EVENT_MOUSE_BUTTON_UP: + { + _onMouseClick(event->button.button); + } + break; + } + return SDL_APP_CONTINUE; +} + +SDL_AppResult SDL_AppIterate(void* appstate) +{ + SDL_FRect rect; + + SDL_SetRenderDrawColor(g_renderer, 255, 255, 255, SDL_ALPHA_OPAQUE); + SDL_RenderClear(g_renderer); + + _drawGrid(); + _drawTriangles(); + _drawAction(); + _drawMouseCursor(); + _drawDebugText(); + + SDL_RenderPresent(g_renderer); /* put it all on the screen! */ + return SDL_APP_CONTINUE; +} + +/* This function runs once at shutdown. */ +void SDL_AppQuit(void* appstate, SDL_AppResult result) +{ +} diff --git a/test/rasterization/dvr_triangle.cpp b/test/rasterization/dvr_triangle.cpp new file mode 100644 index 0000000..e12cd97 --- /dev/null +++ b/test/rasterization/dvr_triangle.cpp @@ -0,0 +1,24 @@ +#include "dvr_triangle.h" +#include "pipe/dvc_rasterizer.h" + +void Triangle::build(const davinci::fVector3& _pixelColor, int32_t canvasWidth, int32_t canvasHeight, bool debug) +{ + pixelColor = _pixelColor; + pixels.clear(); + + //make sure it's cw + bool ccw = false; + if ((p2 - p1).crossProduct(p0 - p2).z > 0) { + ccw = true; + } + + //build pixel data + davinci::Rasterizer::drawTriangleLarrabee(canvasWidth, canvasHeight, + p0.xy(), p1.xy(), p2.xy(), + [this](std::pair pos, const davinci::fVector3& t) + { + pixels.push_back(pos); + }, + ccw + ); +} diff --git a/test/rasterization/dvr_triangle.h b/test/rasterization/dvr_triangle.h new file mode 100644 index 0000000..74cdb3c --- /dev/null +++ b/test/rasterization/dvr_triangle.h @@ -0,0 +1,21 @@ +#pragma once + +#include "math/dvc_vector3.h" + +struct Triangle +{ +public: + davinci::fVector3 p0, p1, p2; + davinci::fVector3 pixelColor; + std::vector< std::pair > pixels; + +public: + Triangle(){} + Triangle(const davinci::fVector3& _p0, const davinci::fVector3& _p1, const davinci::fVector3& _p2) : p0(_p0), p1(_p1), p2(_p2) { } + Triangle(const Triangle& other) : p0(other.p0), p1(other.p1), p2(other.p2), pixelColor(other.pixelColor), pixels(other.pixels) {} + +public: + void build(const davinci::fVector3& _pixelColor, int32_t canvasWidth, int32_t canvasHeight, bool debug = false); +}; + +typedef std::vector TriangleVector; diff --git a/test/shader/CMakeLists.txt b/test/shader/CMakeLists.txt new file mode 100644 index 0000000..89728a4 --- /dev/null +++ b/test/shader/CMakeLists.txt @@ -0,0 +1,28 @@ +add_library(TestShader SHARED + dll_main.cpp + test_vertex_shader.h + test_vertex_shader.cpp + test_vertex_shader.osl + test_pixel_shader.h + test_pixel_shader.cpp + test_pixel_shader.osl +) + +add_definitions(-DDV_SHADER_EXPORTS) + +target_include_directories(TestShader +PRIVATE + ${DV_AUTO_INCLUDE_PATH} + ${DV_CORE_INCLUDE_PATH} +) + +target_link_libraries(TestShader + dv_core +) + +################# +# copy runtime dll(for debug) +################# +add_custom_command( + TARGET TestShader POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different $ ${CMAKE_BINARY_DIR}/source/console/$<$:Debug>$<$:Release>) diff --git a/test/shader/dll_main.cpp b/test/shader/dll_main.cpp new file mode 100644 index 0000000..6b64d79 --- /dev/null +++ b/test/shader/dll_main.cpp @@ -0,0 +1,31 @@ +#include "dv_config.h" +#include "shader/dvc_shader_interface.h" + +#include "test_vertex_shader.h" +#include "test_pixel_shader.h" + +class TestShaderInterface : public davinci::ShaderFileInterface +{ +public: + virtual davinci::VertexShaderInterface* getVertexShader(const char* szName) + { + static TestVertexShader testVertexShader; + return &testVertexShader; + } + virtual davinci::PixelShaderInterface* getPixelShader(const char* szName) + { + static TestPixelShader testPixelShader; + return &testPixelShader; + } +}; + +extern "C" +{ + +__declspec(dllexport) davinci::ShaderFileInterface* ShaderFileInterface() +{ + static TestShaderInterface theInterface; + return &theInterface; +} + +} diff --git a/test/shader/phong_shader.oso b/test/shader/phong_shader.oso new file mode 100644 index 0000000..9fd9ebb --- /dev/null +++ b/test/shader/phong_shader.oso @@ -0,0 +1,109 @@ +OpenShadingLanguage 1.00 +# Compiled by oslc 1.13.12.0 +# options: +shader phong_shader +param point v_position 0 0 0 %read{2147483647,-1} %write{0,0} %initexpr +param vector v_normal 0 1 0 %read{3,3} %write{2147483647,-1} +param vector v_toCamera 0 0 0 %read{2,2} %write{2147483647,-1} +param vector v_toLight 0 0 0 %read{1,1} %write{2147483647,-1} +oparam color resultRGB 0 0 0 %read{2147483647,-1} %write{27,27} +local vector vL %read{4,14} %write{1,1} +local vector vV %read{8,8} %write{2,2} +local vector vN %read{4,18} %write{3,3} +local vector vR %read{2147483647,-1} %write{7,7} +local vector vH %read{18,18} %write{9,9} +local color u_ambientColor %read{13,13} %write{10,10} +local color u_matColor %read{13,23} %write{11,11} +local color u_lightColor %read{17,24} %write{12,12} +local color colAmbient %read{25,25} %write{13,13} +local float ndotl %read{16,20} %write{15,15} +local color colDiff %read{25,25} %write{17,17} +local float spec %read{21,21} %write{19,19} +local color colSpec %read{26,26} %write{24,24} +local color final %read{27,27} %write{26,26} +const vector $const1 0 0 0 %read{0,0} %write{2147483647,-1} +temp float $tmp1 %read{5,5} %write{4,4} +temp float $tmp2 %read{6,6} %write{5,5} +const float $const3 2 %read{5,5} %write{2147483647,-1} +temp vector $tmp3 %read{7,7} %write{6,6} +temp vector $tmp4 %read{9,9} %write{8,8} +temp int $tmp5 %read{2147483647,-1} %write{10,10} +const string $const4 "env:ambient_color" %read{10,10} %write{2147483647,-1} +temp int $tmp6 %read{2147483647,-1} %write{11,11} +const string $const5 "self:mat_color" %read{11,11} %write{2147483647,-1} +temp int $tmp7 %read{2147483647,-1} %write{12,12} +const string $const6 "light0:color" %read{12,12} %write{2147483647,-1} +temp float $tmp8 %read{15,15} %write{14,14} +const float $const8 0 %read{15,20} %write{2147483647,-1} +temp color $tmp9 %read{17,17} %write{16,16} +temp float $tmp10 %read{19,19} %write{18,18} +temp float $tmp11 %read{22,22} %write{20,20} +temp float $tmp12 %read{22,22} %write{21,21} +const float $const10 64 %read{21,21} %write{2147483647,-1} +temp float $tmp13 %read{23,23} %write{22,22} +temp color $tmp14 %read{24,24} %write{23,23} +temp color $tmp15 %read{26,26} %write{25,25} +const vector $const11 0.454545438 0.454545438 0.454545438 %read{27,27} %write{2147483647,-1} +code v_position +# test_pixel_shader.osl:4 +# point v_position = vector(0,0,0), + assign v_position $const1 %filename{"test_pixel_shader.osl"} %line{4} %argrw{"wr"} +code ___main___ +# test_pixel_shader.osl:11 +# vector vL = normalize(v_toLight); + normalize vL v_toLight %filename{"test_pixel_shader.osl"} %line{11} %argrw{"wr"} +# test_pixel_shader.osl:12 +# vector vV = normalize(v_toCamera); + normalize vV v_toCamera %line{12} %argrw{"wr"} +# test_pixel_shader.osl:13 +# vector vN = normalize(v_normal); + normalize vN v_normal %line{13} %argrw{"wr"} +# test_pixel_shader.osl:14 +# vector vR = 2 * dot(vL, vN)*vN - vL; + dot $tmp1 vL vN %line{14} %argrw{"wrr"} + mul $tmp2 $const3 $tmp1 %argrw{"wrr"} + mul $tmp3 $tmp2 vN %argrw{"wrr"} + sub vR $tmp3 vL %argrw{"wrr"} +# test_pixel_shader.osl:15 +# vector vH = normalize(vL + vV); + add $tmp4 vL vV %line{15} %argrw{"wrr"} + normalize vH $tmp4 %argrw{"wr"} +# test_pixel_shader.osl:18 +# getattribute("env:ambient_color", u_ambientColor); + getattribute $tmp5 $const4 u_ambientColor %line{18} %argrw{"wrw"} +# test_pixel_shader.osl:21 +# getattribute("self:mat_color", u_matColor); + getattribute $tmp6 $const5 u_matColor %line{21} %argrw{"wrw"} +# test_pixel_shader.osl:24 +# getattribute("light0:color", u_lightColor); + getattribute $tmp7 $const6 u_lightColor %line{24} %argrw{"wrw"} +# test_pixel_shader.osl:26 +# color colAmbient = u_ambientColor*u_matColor; + mul colAmbient u_ambientColor u_matColor %line{26} %argrw{"wrr"} +# test_pixel_shader.osl:28 +# float ndotl = max(dot(vN, vL), 0); + dot $tmp8 vN vL %line{28} %argrw{"wrr"} + max ndotl $tmp8 $const8 %argrw{"wrr"} +# test_pixel_shader.osl:29 +# color colDiff = ndotl * u_matColor * u_lightColor; + mul $tmp9 ndotl u_matColor %line{29} %argrw{"wrr"} + mul colDiff $tmp9 u_lightColor %argrw{"wrr"} +# test_pixel_shader.osl:31 +# float spec = max(dot(vN, vH), 0); + dot $tmp10 vN vH %line{31} %argrw{"wrr"} + max spec $tmp10 $const8 %argrw{"wrr"} +# test_pixel_shader.osl:32 +# color colSpec = step(0, ndotl) * pow(spec, 64) * u_matColor * u_lightColor; + step $tmp11 $const8 ndotl %line{32} %argrw{"wrr"} + pow $tmp12 spec $const10 %argrw{"wrr"} + mul $tmp13 $tmp11 $tmp12 %argrw{"wrr"} + mul $tmp14 $tmp13 u_matColor %argrw{"wrr"} + mul colSpec $tmp14 u_lightColor %argrw{"wrr"} +# test_pixel_shader.osl:34 +# color final = colAmbient + colDiff + colSpec; + add $tmp15 colAmbient colDiff %line{34} %argrw{"wrr"} + add final $tmp15 colSpec %argrw{"wrr"} +# test_pixel_shader.osl:37 +# resultRGB = pow(final, vector(1.0/2.2)); + pow resultRGB final $const11 %line{37} %argrw{"wrr"} + end diff --git a/test/shader/standard_shader.oso b/test/shader/standard_shader.oso new file mode 100644 index 0000000..daa1e48 --- /dev/null +++ b/test/shader/standard_shader.oso @@ -0,0 +1,64 @@ +OpenShadingLanguage 1.00 +# Compiled by oslc 1.13.12.0 +# options: +shader standard_shader +param point a_position 0 0 0 %meta{string,attribute,"POSITION"} %read{5,5} %write{0,0} %initexpr +param vector a_normal 0 1 0 %meta{string,attribute,"NORMAL"} %read{7,7} %write{2147483647,-1} +param float[2] a_uv 0 0 %meta{string,attribute,"TEXCOORD_0"} %read{8,8} %write{2147483647,-1} +oparam point v_position 0 0 0 %read{9,11} %write{6,6} +oparam vector v_normal 0 1 0 %read{2147483647,-1} %write{7,7} +oparam float[2] v_uv 0 0 %read{2147483647,-1} %write{8,8} +oparam vector v_toCamera 0 0 0 %read{2147483647,-1} %write{10,10} +oparam vector v_toLight 0 0 0 %read{2147483647,-1} %write{12,12} +local matrix mat_world %read{5,5} %write{1,1} +local matrix mat_camera_view_proj %read{6,6} %write{2,2} +local point camera_position %read{9,9} %write{3,3} +local point light0_position %read{11,11} %write{4,4} +const vector $const1 0 0 0 %read{0,0} %write{2147483647,-1} +temp int $tmp1 %read{2147483647,-1} %write{1,1} +const string $const2 "self:world_transform" %read{1,1} %write{2147483647,-1} +temp int $tmp2 %read{2147483647,-1} %write{2,2} +const string $const3 "camera:view_proj" %read{2,2} %write{2147483647,-1} +temp int $tmp3 %read{2147483647,-1} %write{3,3} +const string $const4 "camera:position" %read{3,3} %write{2147483647,-1} +temp int $tmp4 %read{2147483647,-1} %write{4,4} +const string $const5 "light0:position" %read{4,4} %write{2147483647,-1} +temp point $tmp5 %read{6,6} %write{5,5} +temp vector $tmp6 %read{10,10} %write{9,9} +temp vector $tmp7 %read{12,12} %write{11,11} +code a_position +# test_vertex_shader.osl:6 +# point a_position = vector(0, 0, 0) [[ string attribute="POSITION"]], + assign a_position $const1 %filename{"test_vertex_shader.osl"} %line{6} %argrw{"wr"} +code ___main___ +# test_vertex_shader.osl:19 +# getattribute("self:world_transform", mat_world); + getattribute $tmp1 $const2 mat_world %filename{"test_vertex_shader.osl"} %line{19} %argrw{"wrw"} +# test_vertex_shader.osl:25 +# getattribute("camera:view_proj", mat_camera_view_proj); + getattribute $tmp2 $const3 mat_camera_view_proj %line{25} %argrw{"wrw"} +# test_vertex_shader.osl:28 +# getattribute("camera:position", camera_position); + getattribute $tmp3 $const4 camera_position %line{28} %argrw{"wrw"} +# test_vertex_shader.osl:31 +# getattribute("light0:position", light0_position); + getattribute $tmp4 $const5 light0_position %line{31} %argrw{"wrw"} +# test_vertex_shader.osl:34 +# v_position = transform(mat_camera_view_proj, transform(mat_world, a_position)); + transform $tmp5 mat_world a_position %line{34} %argrw{"wrr"} + transform v_position mat_camera_view_proj $tmp5 %argrw{"wrr"} +# test_vertex_shader.osl:35 +# v_normal = a_normal; + assign v_normal a_normal %line{35} %argrw{"wr"} +# test_vertex_shader.osl:36 +# v_uv = a_uv; + arraycopy v_uv a_uv %line{36} %argrw{"wr"} +# test_vertex_shader.osl:38 +# v_toCamera = normalize(camera_position-v_position); + sub $tmp6 camera_position v_position %line{38} %argrw{"wrr"} + normalize v_toCamera $tmp6 %argrw{"wr"} +# test_vertex_shader.osl:39 +# v_toLight = normalize(light0_position-v_position); + sub $tmp7 light0_position v_position %line{39} %argrw{"wrr"} + normalize v_toLight $tmp7 %argrw{"wr"} + end diff --git a/test/shader/test_pixel_shader.cpp b/test/shader/test_pixel_shader.cpp new file mode 100644 index 0000000..c48cb2a --- /dev/null +++ b/test/shader/test_pixel_shader.cpp @@ -0,0 +1,76 @@ +#include "test_pixel_shader.h" + +const char* TestPixelShader::getName(void) const +{ + return "test_pixel_shader"; +} + +void TestPixelShader::prepare(const davinci::UniformBuffer* uniformBuffer, SetInputAttributeFunc setInputAttributeFunc) +{ + +} + +void TestPixelShader::process(const davinci::VertexDesc& veretexDesc, const float32_t* pixelData, davinci::fVector4& color) +{ + const davinci::VertexDesc::Element* v_position_Element = veretexDesc.getElement("v_position"); + const davinci::fVector3* v_position = (const davinci::fVector3*)(pixelData + (v_position_Element->offset / sizeof(float32_t))); + + const davinci::VertexDesc::Element* v_normal_Element = veretexDesc.getElement("v_normal"); + const davinci::fVector3* v_normal = (const davinci::fVector3*)(pixelData + (v_normal_Element->offset / sizeof(float32_t))); + + const davinci::VertexDesc::Element* v_uv_Element = veretexDesc.getElement("v_uv"); + const float32_t* v_uv = (const float32_t*)(pixelData + (v_uv_Element->offset / sizeof(float32_t))); + + const davinci::VertexDesc::Element* v_toCamera_Element = veretexDesc.getElement("v_toCamera"); + const davinci::fVector3* v_toCamera = (const davinci::fVector3*)(pixelData + (v_toCamera_Element->offset / sizeof(float32_t))); + + const davinci::VertexDesc::Element* v_toLight_Element = veretexDesc.getElement("v_toLight"); + const davinci::fVector3* v_toLight = (const davinci::fVector3*)(pixelData + (v_toLight_Element->offset / sizeof(float32_t))); + + const davinci::VertexDesc::Element* v_color0_Element = veretexDesc.getElement("v_color0"); + const davinci::fVector3* v_color0 = (const davinci::fVector3*)(pixelData + (v_color0_Element->offset / sizeof(float32_t))); + + //vector vL = normalize(v_toLight); + davinci::fVector3 vL = *v_toLight; + vL.normalise(); + + //vector vV = normalize(v_toCamera); + davinci::fVector3 vV = *v_toCamera; + vV.normalise(); + + //vector vN = normalize(v_normal); + davinci::fVector3 vN = *v_normal; + vN.normalise(); + + //vector vR = 2 * dot(vL, vN)*vN - vL; + davinci::fVector3 vR = 2 * vL.dotProduct(vN) * vN - vL; + + //vector vH = normalize(vL + vV); + davinci::fVector3 vH = vL + vV; + vH.normalise(); + + davinci::fVector3 u_ambientColor = davinci::fVector3(0.1, 0.1, 0.1); + + davinci::fVector3 u_matColor = davinci::fVector3(1.0, 0.5, 0.7); + + davinci::fVector3 u_lightColor = davinci::fVector3(1.0, 1.0, 1.0); + + //color colAmbient = u_ambientColor*u_matColor; + davinci::fVector3 colAmbient = u_ambientColor * u_matColor; + + //float ndotl = max(dot(vN, vL), 0); + float32_t ndotl = std::max(vN.dotProduct(vL), 0.0f); + //color colDiff = ndotl * u_matColor * u_lightColor; + davinci::fVector3 colDiff = ndotl * u_matColor * u_lightColor; + + //float spec = max(dot(vN, vH), 0); + float32_t spec = std::max(vN.dotProduct(vH), 0.0f); + //color colSpec = step(0, ndotl) * pow(spec, 64) * u_matColor * u_lightColor; + davinci::fVector3 colSpec = (ndotl < 0.f ? 0.0 : 1.0) * std::powf(spec, 64.f) * u_matColor * u_lightColor; + + //color final = colAmbient + colDiff + colSpec; + davinci::fVector3 final = colAmbient + colDiff + colSpec; + //davinci::fVector3 final = *v_color0; + + color = davinci::fVector4(final.saturate(), 1.0f);// davinci::fVector4::RED; +} \ No newline at end of file diff --git a/test/shader/test_pixel_shader.h b/test/shader/test_pixel_shader.h new file mode 100644 index 0000000..4e45807 --- /dev/null +++ b/test/shader/test_pixel_shader.h @@ -0,0 +1,15 @@ +#pragma once + +#include "dvc_config.h" +#include "shader/dvc_shader_interface.h" + +class TestPixelShader : public davinci::PixelShaderInterface +{ +public: + virtual const char* getName(void) const; + + virtual void prepare(const davinci::UniformBuffer* uniformBuffer, SetInputAttributeFunc setInputAttributeFunc); + + virtual void process(const davinci::VertexDesc& veretexDesc, const float32_t* pixelData, + davinci::fVector4& color); +}; \ No newline at end of file diff --git a/test/shader/test_pixel_shader.osl b/test/shader/test_pixel_shader.osl new file mode 100644 index 0000000..da646c9 --- /dev/null +++ b/test/shader/test_pixel_shader.osl @@ -0,0 +1,38 @@ + +shader +phong_shader( + point v_position = vector(0,0,0), + vector v_normal = vector(0,1,0), + vector v_toCamera = vector(0,0,0), + vector v_toLight = vector(0,0,0), + output color resultRGB = 0 +) +{ + vector vL = normalize(v_toLight); + vector vV = normalize(v_toCamera); + vector vN = normalize(v_normal); + vector vR = 2 * dot(vL, vN)*vN - vL; + vector vH = normalize(vL + vV); + + color u_ambientColor; + getattribute("env:ambient_color", u_ambientColor); + + color u_matColor; + getattribute("self:mat_color", u_matColor); + + color u_lightColor; + getattribute("light0:color", u_lightColor); + + color colAmbient = u_ambientColor*u_matColor; + + float ndotl = max(dot(vN, vL), 0); + color colDiff = ndotl * u_matColor * u_lightColor; + + float spec = max(dot(vN, vH), 0); + color colSpec = step(0, ndotl) * pow(spec, 64) * u_matColor * u_lightColor; + + color final = colAmbient + colDiff + colSpec; + + //gamma + resultRGB = pow(final, vector(1.0/2.2)); +} diff --git a/test/shader/test_vertex_shader.cpp b/test/shader/test_vertex_shader.cpp new file mode 100644 index 0000000..c53b79b --- /dev/null +++ b/test/shader/test_vertex_shader.cpp @@ -0,0 +1,122 @@ +#include "test_vertex_shader.h" +#include "device/dvc_buffer_accessor.h" +#include "math/dvc_vector2.h" +#include "math/dvc_vector3.h" +#include "math/dvc_matrix4.h" +#include "device/dvc_uniform_buffer.h" + +TestVertexShader::TestVertexShader() +{ + //output point v_position = point(0, 0, 0) [[ string attribute="POSITION"]], + m_outputVertexDesc.addElement("v_position", davinci::VET_POSITION, davinci::CT_Float32, davinci::DT_Vec3); + //output vector v_normal = vector(0,1,0) [[ string attribute="NORMAL"]], + m_outputVertexDesc.addElement("v_normal", davinci::VET_NORMAL, davinci::CT_Float32, davinci::DT_Vec3); + //output float v_uv[2] = {0, 0} [[ string attribute="TEXCOORD_0"]], + m_outputVertexDesc.addElement("v_uv", davinci::VET_TEXCOORD_0, davinci::CT_Float32, davinci::DT_Vec2); + //output vector v_toCamera = vector(0, 0, 0), + m_outputVertexDesc.addElement("v_toCamera", davinci::VET_UNKNOWN, davinci::CT_Float32, davinci::DT_Vec3); + //output vector v_toLight = vector(0,0,0) + m_outputVertexDesc.addElement("v_toLight", davinci::VET_UNKNOWN, davinci::CT_Float32, davinci::DT_Vec3); + + m_outputVertexDesc.addElement("v_color0", davinci::VET_UNKNOWN, davinci::CT_Float32, davinci::DT_Vec3); +} + +TestVertexShader::~TestVertexShader() +{ + +} + +const char* TestVertexShader::getName(void) const +{ + return "test_vertex_shader"; +} + +size_t TestVertexShader::getOutputVertexByteSize(void) const +{ + return m_outputVertexDesc.vertexByteSize(); +} + +const davinci::VertexDesc& TestVertexShader::getOutputVertexDesc() const +{ + return m_outputVertexDesc; +} + +void TestVertexShader::prepare(const davinci::UniformBuffer* uniformBuffer, SetInputAttributeFunc setInputAttributeFunc) +{ + m_uniformBuffer = uniformBuffer; + + //point a_position = vector(0, 0, 0) [[ string attribute="POSITION"]], + m_POSITION = setInputAttributeFunc(davinci::VET_POSITION); + //vector a_normal = vector(0, 1, 0) [[ string attribute="NORMAL"]], + m_NORMAL = setInputAttributeFunc(davinci::VET_NORMAL); + //float a_uv[2] = {0,0} [[ string attribute="TEXCOORD_0"]], + m_TEXCOORD_0 = setInputAttributeFunc(davinci::VET_TEXCOORD_0); + // + m_COLOR_0 = setInputAttributeFunc(davinci::VET_COLOR_0); +} + +void TestVertexShader::process(size_t index, void* outputVertex, davinci::fVector3& vertexWorldPos) +{ + static const float DEFAULT_POSITION[] = {0.f, 0.f, 0.f}; + static const float DEFAULT_NORMAL[] = { 0.f, 0.f, 0.f }; + static const float DEFAULT_TEXCOORD0[] = { 0.f, 0.f}; + static const uint16_t DEFAULT_COLOR0[] = { 0, 0, 0 }; + + davinci::fVector3 a_position(m_POSITION ? (const float*)(m_POSITION->getElement(index)) : DEFAULT_POSITION); + davinci::fVector3 a_normal(m_NORMAL ? (const float*)(m_NORMAL->getElement(index)) : DEFAULT_NORMAL); + const float32_t* a_texcoord_0(m_TEXCOORD_0 ? (const float*)(m_TEXCOORD_0->getElement(index)) : DEFAULT_TEXCOORD0); + const uint16_t* color0 = m_COLOR_0 ? (const uint16_t*)(m_COLOR_0->getElement(index)) : DEFAULT_COLOR0; + + davinci::fVector3 a_color_0( + color0[0] / float(std::numeric_limits::max()), + color0[1] / float(std::numeric_limits::max()), + color0[2] / float(std::numeric_limits::max()) + ); + + davinci::fMatrix4 mat_world = davinci::fMatrix4::IDENTITY; + if (davinci::fMatrix4* p_mat_world = (davinci::fMatrix4*)(m_uniformBuffer->getAttribute("self:world_transform"))) + { + mat_world = *p_mat_world; + } + + davinci::fMatrix4 mat_camera_view_proj = davinci::fMatrix4::IDENTITY; + if (davinci::fMatrix4* p_mat_camera_view_proj = (davinci::fMatrix4*)(m_uniformBuffer->getAttribute("camera:view_proj_mat"))) + { + mat_camera_view_proj = *p_mat_camera_view_proj; + } + + davinci::fVector3 camera_position = davinci::fVector3::ZERO; + if (davinci::fVector3* p_camera_position = (davinci::fVector3*)(m_uniformBuffer->getAttribute("camera:eye_pos"))) + { + camera_position = *p_camera_position; + } + + davinci::fVector3 light0_position = davinci::fVector3::ZERO; + if (davinci::fVector3* p_light0_position = (davinci::fVector3*)(m_uniformBuffer->getAttribute("light0:position"))) + { + light0_position = *p_light0_position; + } + + davinci::fVector3 temp1 = mat_world * a_position; + davinci::fVector3 v_position = mat_camera_view_proj * temp1; + + davinci::fVector3 v_normal = a_normal; + + davinci::fVector3 v_color_0 = a_color_0; + + float32_t v_uv[2]; + memcpy(v_uv, a_texcoord_0, sizeof(float32_t) * 2); + + davinci::fVector3 v_to_camera = (camera_position - v_position).normalise(); + davinci::fVector3 v_to_light = (light0_position - v_position).normalise(); + + memcpy((char*)outputVertex + m_outputVertexDesc.getElement(0)->offset, &v_position, sizeof(davinci::fVector3)); + memcpy((char*)outputVertex + m_outputVertexDesc.getElement(1)->offset, &v_normal, sizeof(davinci::fVector3)); + memcpy((char*)outputVertex + m_outputVertexDesc.getElement(2)->offset, v_uv, sizeof(float32_t) * 2); + memcpy((char*)outputVertex + m_outputVertexDesc.getElement(3)->offset, &v_to_camera, sizeof(davinci::fVector3)); + memcpy((char*)outputVertex + m_outputVertexDesc.getElement(4)->offset, &v_to_light, sizeof(davinci::fVector3)); + memcpy((char*)outputVertex + m_outputVertexDesc.getElement(5)->offset, &v_color_0, sizeof(davinci::fVector3)); + + vertexWorldPos = mat_world * a_position; +} + diff --git a/test/shader/test_vertex_shader.h b/test/shader/test_vertex_shader.h new file mode 100644 index 0000000..9a451f2 --- /dev/null +++ b/test/shader/test_vertex_shader.h @@ -0,0 +1,33 @@ +#pragma once + +#include "dvc_config.h" +#include "dvc_pre_declare.h" +#include "shader/dvc_shader_interface.h" +#include "device/dvc_vertex_desc.h" + +class TestVertexShader : public davinci::VertexShaderInterface +{ +public: + virtual const char* getName(void) const; + + virtual const davinci::VertexDesc& getOutputVertexDesc() const; + virtual size_t getOutputVertexByteSize(void) const; + + virtual void prepare(const davinci::UniformBuffer* uniformBuffer, SetInputAttributeFunc setInputAttributeFunc); + + virtual void process(size_t index, void* outputVertex, davinci::fVector3& vertexWorldPos); + +private: + const davinci::UniformBuffer* m_uniformBuffer; + davinci::DeviceBufferAccessorConstPtr m_POSITION; + davinci::DeviceBufferAccessorConstPtr m_NORMAL; + davinci::DeviceBufferAccessorConstPtr m_TEXCOORD_0; + davinci::DeviceBufferAccessorConstPtr m_COLOR_0; + + davinci::VertexDesc m_outputVertexDesc; + +public: + TestVertexShader(); + virtual ~TestVertexShader(); +}; + diff --git a/test/shader/test_vertex_shader.osl b/test/shader/test_vertex_shader.osl new file mode 100644 index 0000000..60fabbb --- /dev/null +++ b/test/shader/test_vertex_shader.osl @@ -0,0 +1,40 @@ + +// + +shader +standard_shader( + point a_position = vector(0, 0, 0) [[ string attribute="POSITION"]], + vector a_normal = vector(0, 1, 0) [[ string attribute="NORMAL"]], + float a_uv[2] = {0,0} [[ string attribute="TEXCOORD_0"]], + + output point v_position = point(0, 0, 0) [[ string attribute="POSITION"]], + output vector v_normal = vector(0,1,0) [[ string attribute="NORMAL"]], + output float v_uv[2] = {0, 0} [[ string attribute="TEXCOORD_0"]], + + output vector v_toCamera = vector(0, 0, 0), + output vector v_toLight = vector(0, 0, 0) +) +{ + matrix mat_world; + getattribute("self:world_transform", mat_world); + + //matrix mat_normal; + //getattribute("self:normal_transform", mat_normal); + + matrix mat_camera_view_proj; + getattribute("camera:view_proj", mat_camera_view_proj); + + point camera_position; + getattribute("camera:eye_pos", camera_position); + + point light0_position; + getattribute("light0:position", light0_position); + + + v_position = transform(mat_camera_view_proj, transform(mat_world, a_position)); + v_normal = a_normal; + v_uv = a_uv; + + v_toCamera = normalize(camera_position-v_position); + v_toLight = normalize(light0_position-v_position); +} diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt new file mode 100644 index 0000000..54ab28b --- /dev/null +++ b/test/unit/CMakeLists.txt @@ -0,0 +1,39 @@ +enable_testing() + +include_directories( + ${DV_AUTO_INCLUDE_PATH} + ${DV_CORE_INCLUDE_PATH} + ${DV_UTILS_INCLUDE_PATH} +) + +set(DVT_UNIT_SOURCE_FILES + dvt_unit_main.cpp + dvt_unit_vector2.cpp + dvt_unit_vector3.cpp + dvt_unit_vector4.cpp + dvt_unit_matrix3.cpp + dvt_unit_matrix4.cpp + dvt_unit_math_util.cpp + dvt_unit_rasterizer.cpp +) + +add_definitions(-DGLM_FORCE_PURE -DGLM_FORCE_LEFT_HANDED -DGLM_FORCE_DEPTH_ZERO_TO_ONE) + +add_executable(dvt_unit + dvt_unit_common.h + ${DVT_UNIT_SOURCE_FILES} +) + +set_property(TARGET dvt_unit PROPERTY FOLDER "test") + +target_link_libraries(dvt_unit + dv_core + dv_utils + + GTest::gtest + GTest::gtest_main + glm::glm +) + +include(GoogleTest) +gtest_discover_tests(dvt_unit) diff --git a/test/unit/dvt_unit_common.h b/test/unit/dvt_unit_common.h new file mode 100644 index 0000000..166407c --- /dev/null +++ b/test/unit/dvt_unit_common.h @@ -0,0 +1,47 @@ +#pragma once + +#include + +using namespace davinci; + +//------------------------------------------------------------------------------------- +inline float _randomFloat(void) { + return MathUtil::rangeRandom(-65521 / 16.f, 65521 / 16.f); +} + +//------------------------------------------------------------------------------------- +inline bool _floatEqualWithRange(float a, float b, float t = std::numeric_limits::epsilon()) { + if (MathUtil::floatEqual(a, b)) return true; + + float d = std::abs(a - b) / std::min(std::abs(a), std::abs(b)); + return MathUtil::floatEqual(d, 0.f, t); +} + +//------------------------------------------------------------------------------------- +#define TWO_RANDOM_FLOAT _randomFloat(), _randomFloat() +#define THREE_RANDOM_FLOAT _randomFloat(), _randomFloat(), _randomFloat() +#define FOUR_RANDOM_FLOAT _randomFloat(), _randomFloat(), _randomFloat(), _randomFloat() + +#define NINE_RANDOM_FLOAT THREE_RANDOM_FLOAT, THREE_RANDOM_FLOAT, THREE_RANDOM_FLOAT +#define SIXTEEN_RANDOM_FLOAT FOUR_RANDOM_FLOAT, FOUR_RANDOM_FLOAT, FOUR_RANDOM_FLOAT, FOUR_RANDOM_FLOAT + +#define THREE_ELEMENT(data) data[0], data[1], data[2] +#define FOUR_ELEMENT(data) data[0], data[1], data[2], data[3] +#define NINE_ELEMENT_ROW(data) data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7], data[8] +#define NINE_ELEMENT_COL(data) data[0], data[3], data[6], data[1], data[4], data[7], data[2], data[5], data[8] +#define SIXTEEN_ELEMENT_ROW(data) data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7], data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15] +#define SIXTEEN_ELEMENT_COL(data) data[0], data[4], data[8], data[12], data[1], data[5], data[9], data[13], data[2], data[6], data[10], data[14], data[3], data[7], data[11], data[15] + +#define EXPECT_EQ_3X3(a, b) for(int32_t i=0; i<3; i++) for(int32_t j=0; j<3; j++) EXPECT_EQ(a[i][j], b[i][j]); +#define EXPECT_EQ_3X3_T(a, b) for(int32_t i=0; i<3; i++) for(int32_t j=0; j<3; j++) EXPECT_EQ(a[i][j], b[j][i]); +#define EXPECT_EQ_4X4(a, b) for(int32_t i=0; i<4; i++) for(int32_t j=0; j<4; j++) EXPECT_EQ(a[i][j], b[i][j]); +#define EXPECT_EQ_4X4_T(a, b) for(int32_t i=0; i<4; i++) for(int32_t j=0; j<4; j++) EXPECT_EQ(a[i][j], b[j][i]); + +//------------------------------------------------------------------------------------- +#define SIXTEEN_RANDOM_FLOAT2 THREE_RANDOM_FLOAT, 0.f, THREE_RANDOM_FLOAT, 0.f, THREE_RANDOM_FLOAT, 0.f, THREE_RANDOM_FLOAT, 1.f + +#define EXPECT_EQ_4X4_APPROX(a, b, t) for(int32_t ii=0; ii<4; ii++) for(int32_t jj=0; jj<4; jj++) \ + EXPECT_TRUE(_floatEqualWithRange(a[ii][jj], b[ii][jj], t)); + +#define EXPECT_EQ_4X4_T_APPROX(a, b, t) for(int32_t ii=0; ii<4; ii++) for(int32_t jj=0; jj<4; jj++) \ + EXPECT_TRUE(_floatEqualWithRange(a[ii][jj], b[jj][ii], t)); diff --git a/test/unit/dvt_unit_main.cpp b/test/unit/dvt_unit_main.cpp new file mode 100644 index 0000000..c185673 --- /dev/null +++ b/test/unit/dvt_unit_main.cpp @@ -0,0 +1,14 @@ +#include +#include + +//------------------------------------------------------------------------------------- +int main(int argc, char* argv[]) +{ + testing::InitGoogleTest(&argc, argv); + uint32_t seed = (uint32_t)::time(0); + //seed = 1748961760; + srand(seed); + printf("seed=%d\n", seed); + + return RUN_ALL_TESTS(); +} diff --git a/test/unit/dvt_unit_math_util.cpp b/test/unit/dvt_unit_math_util.cpp new file mode 100644 index 0000000..6b0a49e --- /dev/null +++ b/test/unit/dvt_unit_math_util.cpp @@ -0,0 +1,79 @@ +#include +#include +#include +#include + +#include +#include +#include + +#include "dvt_unit_common.h" + +using namespace davinci; + +//------------------------------------------------------------------------------------- +TEST(MathUtil, Basic) +{ + + //lookat view matrix + { + float dEye[3] = { 0.0f, 4.0f, -10.0f }; + float dAt[3] = { 0.0f, 1.0f, 0.0f }; + float dUp[3] = { 0.0f, 1.0f, 0.0f }; + + fMatrix4 m1 = MathUtil::lookatRH( + fVector3(THREE_ELEMENT(dEye)), + fVector3(THREE_ELEMENT(dAt)), + fVector3(THREE_ELEMENT(dUp))); + + glm::mat4 gm1 = glm::lookAtRH(glm::vec3(THREE_ELEMENT(dEye)), glm::vec3(THREE_ELEMENT(dAt)), glm::vec3(THREE_ELEMENT(dUp))); + EXPECT_EQ_4X4_T(m1, gm1); + } + + //lookat view matrix(random) + { + for (int i = 0; i < 10; i++) { + fVector3 dEye, dAt, dUp; + do { + dEye[0] = _randomFloat(); dEye[1] = _randomFloat(); dEye[2] = _randomFloat(); + dAt[0] = _randomFloat(); dAt[1] = _randomFloat(); dAt[2] = _randomFloat(); + dUp[0] = _randomFloat(); dUp[1] = _randomFloat(); dUp[2] = _randomFloat(); + + } while ((dEye == dAt) || (dUp == fVector3::ZERO)); + + fMatrix4 m1 = MathUtil::lookatRH( + fVector3(THREE_ELEMENT(dEye)), + fVector3(THREE_ELEMENT(dAt)), + fVector3(THREE_ELEMENT(dUp))); + + glm::mat4 gm1 = glm::lookAtRH(glm::vec3(THREE_ELEMENT(dEye)), glm::vec3(THREE_ELEMENT(dAt)), glm::vec3(THREE_ELEMENT(dUp))); + + EXPECT_EQ_4X4_T_APPROX(m1, gm1, std::numeric_limits::epsilon() * 1000); + } + } + + //perspective + { + float width = 640.f, height = 480.f; + + fMatrix4 m1 = MathUtil::perspectiveFovRH(MathUtil::PI_DIV4, width / height, 0.01f, 100.f); + glm::mat4 gm1 = glm::perspectiveRH(MathUtil::PI_DIV4, width / height, 0.01f, 100.f); + + EXPECT_EQ_4X4_T_APPROX(m1, gm1, std::numeric_limits::epsilon()); + } + + //perspective(random) + { + for (int i = 0; i < 10; i++) { + float fov = MathUtil::rangeRandom(0.1f, MathUtil::PI); + float aspect = MathUtil::rangeRandom(0.1f, 100.f); + float zNear = MathUtil::rangeRandom(0.00001f, 0.01f); + float zFar = MathUtil::rangeRandom(100.f, 10000.f); + + fMatrix4 m1 = MathUtil::perspectiveFovRH(fov, aspect, zNear, zFar); + glm::mat4 gm1 = glm::perspectiveRH(fov, aspect, zNear, zFar); + + EXPECT_EQ_4X4_T_APPROX(m1, gm1, std::numeric_limits::epsilon() * 100); + } + } +} diff --git a/test/unit/dvt_unit_matrix3.cpp b/test/unit/dvt_unit_matrix3.cpp new file mode 100644 index 0000000..8ee916e --- /dev/null +++ b/test/unit/dvt_unit_matrix3.cpp @@ -0,0 +1,182 @@ +#include +#include + +#include +#include + +#include "dvt_unit_common.h" + +using namespace davinci; + +//------------------------------------------------------------------------------------- +TEST(Math_Matrix3, Basic) +{ + { + float data[3][3] = { { THREE_RANDOM_FLOAT }, { THREE_RANDOM_FLOAT }, { THREE_RANDOM_FLOAT } }; + + fMatrix3 m1(data), m2(m1); + fMatrix3 m3( + data[0][0], data[0][1], data[0][2], + data[1][0], data[1][1], data[1][2], + data[2][0], data[2][1], data[2][2] + ); + + EXPECT_EQ_3X3(m1, data); + EXPECT_EQ_3X3(m2, data); + EXPECT_EQ_3X3(m3, data); + } + + { + fMatrix3 m1(NINE_RANDOM_FLOAT), m2; + + m2 = m1; + EXPECT_EQ_3X3(m1, m2); + EXPECT_TRUE(m1 == m2); + + m2[0][0] += 1.0; + EXPECT_TRUE(m1 != m2); + } +} + +//------------------------------------------------------------------------------------- +TEST(Math_Matrix3, Algorithm) +{ + //+ + { + float data1[9] = { NINE_RANDOM_FLOAT }; + float data2[9] = { NINE_RANDOM_FLOAT }; + + fMatrix3 m1(NINE_ELEMENT_ROW(data1)), m2(NINE_ELEMENT_ROW(data2)); + glm::mat3 gm1(NINE_ELEMENT_COL(data1)), gm2(NINE_ELEMENT_COL(data2)); + + fMatrix3 m3 = m1 + m2; + glm::mat3 gm3 = gm1 + gm2; + + EXPECT_EQ_3X3_T(m3, gm3); + } + + //- + { + float data1[9] = { NINE_RANDOM_FLOAT }; + float data2[9] = { NINE_RANDOM_FLOAT }; + + fMatrix3 m1(NINE_ELEMENT_ROW(data1)), m2(NINE_ELEMENT_ROW(data2)); + glm::mat3 gm1(NINE_ELEMENT_COL(data1)), gm2(NINE_ELEMENT_COL(data2)); + + fMatrix3 m3 = m1 - m2; + glm::mat3 gm3 = gm1 - gm2; + + EXPECT_EQ_3X3_T(m3, gm3); + } + + //* + { + float data1[9] = { NINE_RANDOM_FLOAT }; + float data2[9] = { NINE_RANDOM_FLOAT }; + + fMatrix3 m1(NINE_ELEMENT_ROW(data1)), m2(NINE_ELEMENT_ROW(data2)); + glm::mat3 gm1(NINE_ELEMENT_COL(data1)), gm2(NINE_ELEMENT_COL(data2)); + + fMatrix3 m3 = m1 * m2; + glm::mat3 gm3 = gm1 * gm2; + + EXPECT_EQ_3X3_T(m3, gm3); + } + + // - + { + float data1[9] = { NINE_RANDOM_FLOAT }; + + fMatrix3 m1(NINE_ELEMENT_ROW(data1)); + glm::mat3 gm1(NINE_ELEMENT_COL(data1)); + + fMatrix3 m2 = -m1; + glm::mat3 gm2 = -gm1; + + EXPECT_EQ_3X3_T(m2, gm2); + } + + + // vector * matrix + { + float data1[9] = { NINE_RANDOM_FLOAT }; + float data2[3] = { THREE_RANDOM_FLOAT }; + + fMatrix3 m1(NINE_ELEMENT_ROW(data1)); + fVector3 v1(data2); + fVector3 v2 = v1 * m1; + + glm::mat3 gm1(NINE_ELEMENT_COL(data1)); + glm::vec3 gv1(data2[0], data2[1], data2[2]); + glm::vec3 gv2 = gv1*gm1; + + EXPECT_EQ(v2.x, gv2.x); + EXPECT_EQ(v2.y, gv2.y); + EXPECT_EQ(v2.z, gv2.z); + } + + // matrix * vector + { + float data1[9] = { NINE_RANDOM_FLOAT }; + float data2[3] = { THREE_RANDOM_FLOAT }; + + fMatrix3 m1(NINE_ELEMENT_ROW(data1)); + fVector3 v1(data2); + fVector3 v2 = m1 * v1; + + glm::mat3 gm1(NINE_ELEMENT_COL(data1)); + glm::vec3 gv1(data2[0], data2[1], data2[2]); + glm::vec3 gv2 = gm1 * gv1; + + EXPECT_EQ(v2.x, gv2.x); + EXPECT_EQ(v2.y, gv2.y); + EXPECT_EQ(v2.z, gv2.z); + } + + // * float + { + float data1[9] = { NINE_RANDOM_FLOAT }; + float a = _randomFloat(); + + fMatrix3 m1(NINE_ELEMENT_ROW(data1)); + fMatrix3 m2 = m1 * a, m3 = a * m1; + + glm::mat3 gm1(NINE_ELEMENT_COL(data1)); + glm::mat3 gm2 = gm1 * a; + + + EXPECT_EQ_3X3_T(m2, gm2); + EXPECT_TRUE(m2 == m3); + } + + // transpose + { + float data1[9] = { NINE_RANDOM_FLOAT }; + + fMatrix3 m1(NINE_ELEMENT_ROW(data1)); + fMatrix3 m2 = m1.transpose(); + + glm::mat3 gm1(NINE_ELEMENT_COL(data1)); + glm::mat3 gm2 = glm::transpose(gm1); + + EXPECT_EQ_3X3_T(m2, gm2); + } + + // inverse + { + float data1[9] = { NINE_RANDOM_FLOAT }; + + fMatrix3 m1(NINE_ELEMENT_ROW(data1)), m2; + bool inv = m1.inverse(m2); + + glm::mat3 gm1(NINE_ELEMENT_COL(data1)); + glm::mat3 gm2 = glm::inverse(gm1); + + if (inv) + EXPECT_EQ_3X3_T(m2, gm2); + + EXPECT_FALSE(fMatrix3::ZERO.inverse(m2)); + } + +} + diff --git a/test/unit/dvt_unit_matrix4.cpp b/test/unit/dvt_unit_matrix4.cpp new file mode 100644 index 0000000..1d78e0a --- /dev/null +++ b/test/unit/dvt_unit_matrix4.cpp @@ -0,0 +1,285 @@ +#include +#include +#include + +#include + +#define GLM_FORCE_CTOR_INIT +#include +#include +#include + +#include "dvt_unit_common.h" + +using namespace davinci; + +//------------------------------------------------------------------------------------- +TEST(Math_Matrix4, Basic) +{ + { + float data[4][4] = { { FOUR_RANDOM_FLOAT },{ FOUR_RANDOM_FLOAT },{ FOUR_RANDOM_FLOAT }, { FOUR_RANDOM_FLOAT } }; + + fMatrix4 m1(data), m2(m1); + fMatrix4 m3( + data[0][0], data[0][1], data[0][2], data[0][3], + data[1][0], data[1][1], data[1][2], data[1][3], + data[2][0], data[2][1], data[2][2], data[2][3], + data[3][0], data[3][1], data[3][2], data[3][3] + ); + + EXPECT_EQ_4X4(m1, data); + EXPECT_EQ_4X4(m2, data); + EXPECT_EQ_4X4(m3, data); + } + + { + fMatrix4 m1(SIXTEEN_RANDOM_FLOAT), m2; + + m2 = m1; + EXPECT_EQ_4X4(m1, m2); + EXPECT_TRUE(m1 == m2); + + m2[0][0] += 1.0; + EXPECT_TRUE(m1 != m2); + } +} + +//------------------------------------------------------------------------------------- +TEST(Math_Matrix4, Algorithm) +{ + //+ + { + float data1[16] = { SIXTEEN_RANDOM_FLOAT }; + float data2[16] = { SIXTEEN_RANDOM_FLOAT }; + + fMatrix4 m1(SIXTEEN_ELEMENT_ROW(data1)), m2(SIXTEEN_ELEMENT_ROW(data2)); + glm::mat4 gm1(SIXTEEN_ELEMENT_COL(data1)), gm2(SIXTEEN_ELEMENT_COL(data2)); + + fMatrix4 m3 = m1 + m2; + glm::mat4 gm3 = gm1 + gm2; + + EXPECT_EQ_4X4_T(m3, gm3); + } + + //- + { + float data1[16] = { SIXTEEN_RANDOM_FLOAT }; + float data2[16] = { SIXTEEN_RANDOM_FLOAT }; + + fMatrix4 m1(SIXTEEN_ELEMENT_ROW(data1)), m2(SIXTEEN_ELEMENT_ROW(data2)); + glm::mat4 gm1(SIXTEEN_ELEMENT_COL(data1)), gm2(SIXTEEN_ELEMENT_COL(data2)); + + fMatrix4 m3 = m1 - m2; + glm::mat4 gm3 = gm1 - gm2; + + EXPECT_EQ_4X4_T(m3, gm3); + } + + //* + { + float data1[16] = { SIXTEEN_RANDOM_FLOAT }; + float data2[16] = { SIXTEEN_RANDOM_FLOAT }; + + fMatrix4 m1(SIXTEEN_ELEMENT_ROW(data1)), m2(SIXTEEN_ELEMENT_ROW(data2)); + glm::mat4 gm1(SIXTEEN_ELEMENT_COL(data1)), gm2(SIXTEEN_ELEMENT_COL(data2)); + + fMatrix4 m3 = m1 * m2; + glm::mat4 gm3 = gm1 * gm2; + + EXPECT_EQ_4X4_T(m3, gm3); + } + + // - + { + float data1[16] = { SIXTEEN_RANDOM_FLOAT }; + + fMatrix4 m1(SIXTEEN_ELEMENT_ROW(data1)); + glm::mat4 gm1(SIXTEEN_ELEMENT_COL(data1)); + + fMatrix4 m2 = -m1; + glm::mat4 gm2 = -gm1; + + EXPECT_EQ_4X4_T(m2, gm2); + } + + // vector4 * matrix + { + float data1[16] = { SIXTEEN_RANDOM_FLOAT }; + float data2[4] = { FOUR_RANDOM_FLOAT }; + + fMatrix4 m1(SIXTEEN_ELEMENT_ROW(data1)); + fVector4 v1(data2); + fVector4 v2 = v1 * m1; + + glm::mat4 gm1(SIXTEEN_ELEMENT_COL(data1)); + glm::vec4 gv1(FOUR_ELEMENT(data2)); + glm::vec4 gv2 = gv1 * gm1; + + //fVector4 v2 = v1 * m1; + EXPECT_TRUE(_floatEqualWithRange(v2.x, gv2.x, std::numeric_limits::epsilon() * 100)); + EXPECT_TRUE(_floatEqualWithRange(v2.y, gv2.y, std::numeric_limits::epsilon() * 100)); + EXPECT_TRUE(_floatEqualWithRange(v2.z, gv2.z, std::numeric_limits::epsilon() * 100)); + EXPECT_TRUE(_floatEqualWithRange(v2.w, gv2.w, std::numeric_limits::epsilon() * 100)); + } + + // matrix * vector4 + { + float data1[16] = { SIXTEEN_RANDOM_FLOAT }; + float data2[4] = { FOUR_RANDOM_FLOAT }; + + fMatrix4 m1(SIXTEEN_ELEMENT_ROW(data1)); + fVector4 v1(data2); + fVector4 v2 = m1 * v1; + + glm::mat4 gm1(SIXTEEN_ELEMENT_COL(data1)); + glm::vec4 gv1(FOUR_ELEMENT(data2)); + glm::vec4 gv2 = gm1 * gv1; + + //fVector4 v2 = v1 * m1; + EXPECT_TRUE(_floatEqualWithRange(v2.x, gv2.x, std::numeric_limits::epsilon() * 100)); + EXPECT_TRUE(_floatEqualWithRange(v2.y, gv2.y, std::numeric_limits::epsilon() * 100)); + EXPECT_TRUE(_floatEqualWithRange(v2.z, gv2.z, std::numeric_limits::epsilon() * 100)); + EXPECT_TRUE(_floatEqualWithRange(v2.w, gv2.w, std::numeric_limits::epsilon() * 100)); + } + + // matrix * vector3 + { + float data1[16] = { SIXTEEN_RANDOM_FLOAT }; + float data2[3] = { THREE_RANDOM_FLOAT}; + + fMatrix4 m1(SIXTEEN_ELEMENT_ROW(data1)); + fVector3 v1(data2); + fVector3 v2 = m1 * v1; + + glm::mat4 gm1(SIXTEEN_ELEMENT_COL(data1)); + glm::vec4 gv1(THREE_ELEMENT(data2), 1); + glm::vec4 gv2 = gm1 * gv1; + gv2 /= gv2.w; + + //fVector4 v2 = v1 * m1; + EXPECT_TRUE(_floatEqualWithRange(v2.x, gv2.x, std::numeric_limits::epsilon() * 100)); + EXPECT_TRUE(_floatEqualWithRange(v2.y, gv2.y, std::numeric_limits::epsilon() * 100)); + EXPECT_TRUE(_floatEqualWithRange(v2.z, gv2.z, std::numeric_limits::epsilon() * 100)); + } + + // vector3 * matrix + { + float data1[16] = { SIXTEEN_RANDOM_FLOAT }; + float data2[3] = { THREE_RANDOM_FLOAT }; + + fMatrix4 m1(SIXTEEN_ELEMENT_ROW(data1)); + fVector3 v1(data2); + fVector3 v2 = v1 * m1; + + glm::mat4 gm1(SIXTEEN_ELEMENT_COL(data1)); + glm::vec4 gv1(THREE_ELEMENT(data2), 1); + glm::vec4 gv2 = gv1 * gm1; + gv2 /= gv2.w; + + EXPECT_TRUE(_floatEqualWithRange(v2.x, gv2.x, std::numeric_limits::epsilon() * 100)); + EXPECT_TRUE(_floatEqualWithRange(v2.y, gv2.y, std::numeric_limits::epsilon() * 100)); + EXPECT_TRUE(_floatEqualWithRange(v2.z, gv2.z, std::numeric_limits::epsilon() * 100)); + } + + // * float + { + float data[16] = { SIXTEEN_RANDOM_FLOAT }; + float a = _randomFloat(); + + fMatrix4 m1(SIXTEEN_ELEMENT_ROW(data)); + fMatrix4 m2 = m1 * a, m3 = a * m1; + + glm::mat4 gm1(SIXTEEN_ELEMENT_COL(data)); + glm::mat4 gm2 = gm1 * a; + + EXPECT_EQ_4X4_T(m2, gm2); + EXPECT_TRUE(m2 == m3); + } + + // translation + { + float trans_data[] = { THREE_RANDOM_FLOAT }; + + fMatrix4 m1 = fMatrix4::makeTrans(THREE_ELEMENT(trans_data)); + glm::mat4 gm1 = glm::translate(glm::mat4(1), glm::vec3(THREE_ELEMENT(trans_data))); + + EXPECT_EQ_4X4_T(m1, gm1); + + fVector3 p0(THREE_RANDOM_FLOAT); + fVector3 p1 = m1 * p0; + EXPECT_TRUE(p1 == p0 + fVector3(trans_data)); + } + + // scale + { + float scale_data[] = { THREE_RANDOM_FLOAT }; + + fMatrix4 m1 = fMatrix4::makeScale(THREE_ELEMENT(scale_data)); + glm::mat4 gm1 = glm::scale(glm::mat4(1.0f), glm::vec3(THREE_ELEMENT(scale_data))); + + EXPECT_EQ_4X4_T(m1, gm1); + } + + // transpose + { + float data[16] = { SIXTEEN_RANDOM_FLOAT }; + + fMatrix4 m1(SIXTEEN_ELEMENT_ROW(data)); + fMatrix4 m2 = m1.transpose(); + + glm::mat4 gm1(SIXTEEN_ELEMENT_COL(data)); + glm::mat4 gm2 = glm::transpose(gm1); + + EXPECT_EQ_4X4_T(m2, gm2); + } + + // rotation + { + float data[3] = { THREE_RANDOM_FLOAT }; + + fVector3 axis(data); + axis = axis.normalise(); + + float angle(_randomFloat()); + float c = cos(angle*0.5); + float s = sin(angle*0.5); + + fMatrix4 mx = fMatrix4::makeRotate_X(angle); + fMatrix4 my = fMatrix4::makeRotate_Y(angle); + fMatrix4 mz = fMatrix4::makeRotate_Z(angle); + fMatrix4 mv = fMatrix4::makeRotate(axis, angle); + fMatrix4 mq = fMatrix4::makeRotate(fVector4(s * axis.x, s * axis.y, s * axis.z, c)); + + EXPECT_EQ_4X4_APPROX(mv, mq, std::numeric_limits::epsilon() * 1000); + + glm::vec3 gmaxis(data[0], data[1], data[2]); + gmaxis = glm::normalize(gmaxis); + + glm::mat4 gmx = glm::rotate(glm::mat4(), angle, glm::vec3(1, 0, 0)); + glm::mat4 gmy = glm::rotate(glm::mat4(), angle, glm::vec3(0, 1, 0)); + glm::mat4 gmz = glm::rotate(glm::mat4(), angle, glm::vec3(0, 0, 1)); + glm::mat4 gmv = glm::rotate(glm::mat4(), angle, gmaxis); + + glm::qua quaternion = glm::qua(c, s * gmaxis.x, s * gmaxis.y, s * gmaxis.z); + glm::mat4 gmq = glm::mat4_cast(quaternion); + + EXPECT_EQ_4X4_T_APPROX(mx, gmx, std::numeric_limits::epsilon() * 1000); + EXPECT_EQ_4X4_T_APPROX(my, gmy, std::numeric_limits::epsilon() * 1000); + EXPECT_EQ_4X4_T_APPROX(mz, gmz, std::numeric_limits::epsilon() * 1000); + EXPECT_EQ_4X4_T_APPROX(mv, gmv, std::numeric_limits::epsilon() * 1000); + EXPECT_EQ_4X4_T_APPROX(mq, gmq, std::numeric_limits::epsilon() * 1000); + } + + // inverse + { + float data[16] = { SIXTEEN_RANDOM_FLOAT }; + + fMatrix4 m1(SIXTEEN_ELEMENT_ROW(data)); + fMatrix4 m2 = m1.inverse(); + + glm::mat4 gm1(SIXTEEN_ELEMENT_COL(data)); + glm::mat4 gm2 = glm::inverse(gm1); + + EXPECT_EQ_4X4_T_APPROX(m2, gm2, std::numeric_limits::epsilon()*10000); + } +} diff --git a/test/unit/dvt_unit_rasterizer.cpp b/test/unit/dvt_unit_rasterizer.cpp new file mode 100644 index 0000000..202a425 --- /dev/null +++ b/test/unit/dvt_unit_rasterizer.cpp @@ -0,0 +1,416 @@ +#include +#include + +#include + +#include + +using namespace davinci; + +class Edge +{ +public: + + Edge(const fVector2 &_p1, const fVector2 &_p2) : p1(_p1), p2(_p2) {}; + Edge(const Edge &e) : p1(e.p1), p2(e.p2) {}; + + fVector2 p1; + fVector2 p2; +}; + +//------------------------------------------------------------------------------------- +inline bool operator == (const Edge & e1, const Edge & e2) +{ + return (e1.p1 == e2.p1 && e1.p2 == e2.p2) || + (e1.p1 == e2.p2 && e1.p2 == e2.p1); +} + +//------------------------------------------------------------------------------------- +class Triangle +{ +public: + + Triangle(const fVector2 &_p1, const fVector2 &_p2, const fVector2 &_p3) + : p1(_p1), p2(_p2), p3(_p3) + {} + + bool containsVertex(const fVector2 &v) + { + return p1 == v || p2 == v || p3 == v; + } + + bool circumCircleContains(const fVector2 &v) + { + float ab = (p1.x * p1.x) + (p1.y * p1.y); + float cd = (p2.x * p2.x) + (p2.y * p2.y); + float ef = (p3.x * p3.x) + (p3.y * p3.y); + + float circum_x = (ab * (p3.y - p2.y) + cd * (p1.y - p3.y) + ef * (p2.y - p1.y)) / (p1.x * (p3.y - p2.y) + p2.x * (p1.y - p3.y) + p3.x * (p2.y - p1.y)) / 2.f; + float circum_y = (ab * (p3.x - p2.x) + cd * (p1.x - p3.x) + ef * (p2.x - p1.x)) / (p1.y * (p3.x - p2.x) + p2.y * (p1.x - p3.x) + p3.y * (p2.x - p1.x)) / 2.f; + float circum_radius = sqrtf(((p1.x - circum_x) * (p1.x - circum_x)) + ((p1.y - circum_y) * (p1.y - circum_y))); + + float dist = sqrtf(((v.x - circum_x) * (v.x - circum_x)) + ((v.y - circum_y) * (v.y - circum_y))); + return dist <= circum_radius; + } + + fVector2 p1; + fVector2 p2; + fVector2 p3; +}; + +//------------------------------------------------------------------------------------- +inline bool operator == (const Triangle &t1, const Triangle &t2) +{ + return (t1.p1 == t2.p1 || t1.p1 == t2.p2 || t1.p1 == t2.p3) && + (t1.p2 == t2.p1 || t1.p2 == t2.p2 || t1.p2 == t2.p3) && + (t1.p3 == t2.p1 || t1.p3 == t2.p2 || t1.p3 == t2.p3); +} + +//------------------------------------------------------------------------------------- +class Delaunay +{ + /* + https://github.com/Bl4ckb0ne/delaunay-triangulation + */ +public: + static void triangulate(const std::vector & vertices, std::vector& _triangles) + { + // Determinate the super triangle + float minX = vertices[0].x; + float minY = vertices[0].y; + float maxX = minX; + float maxY = minY; + + for (std::size_t i = 0; i < vertices.size(); ++i) + { + if (vertices[i].x < minX) minX = vertices[i].x; + if (vertices[i].y < minY) minY = vertices[i].y; + if (vertices[i].x > maxX) maxX = vertices[i].x; + if (vertices[i].y > maxY) maxY = vertices[i].y; + } + + float dx = maxX - minX; + float dy = maxY - minY; + float deltaMax = std::max(dx, dy); + float midx = (minX + maxX) / 2.f; + float midy = (minY + maxY) / 2.f; + + fVector2 p1(midx - 20 * deltaMax, midy - deltaMax); + fVector2 p2(midx, midy + 20 * deltaMax); + fVector2 p3(midx + 20 * deltaMax, midy - deltaMax); + + // Create a list of triangles, and add the supertriangle in it + _triangles.push_back(Triangle(p1, p2, p3)); + + for (auto p = begin(vertices); p != end(vertices); p++) { + std::vector badTriangles; + std::vector polygon; + + for (auto t = begin(_triangles); t != end(_triangles); t++) { + if (t->circumCircleContains(*p)) { + badTriangles.push_back(*t); + + polygon.push_back(Edge(t->p1, t->p2)); + polygon.push_back(Edge(t->p2, t->p3)); + polygon.push_back(Edge(t->p3, t->p1)); + } + } + + _triangles.erase(std::remove_if(begin(_triangles), end(_triangles), [badTriangles](Triangle &t) { + for (auto bt = begin(badTriangles); bt != end(badTriangles); bt++) { + if (*bt == t) { + return true; + } + } + return false; + }), end(_triangles)); + + std::vector badEdges; + for (auto e1 = begin(polygon); e1 != end(polygon); e1++) { + for (auto e2 = begin(polygon); e2 != end(polygon); e2++) { + if (e1 == e2) continue; + + if (*e1 == *e2) { + badEdges.push_back(*e1); + badEdges.push_back(*e2); + } + } + } + + polygon.erase(std::remove_if(begin(polygon), end(polygon), [badEdges](Edge &e) { + for (auto it = begin(badEdges); it != end(badEdges); it++) { + if (*it == e) return true; + } + return false; + }), end(polygon)); + + for (auto e = begin(polygon); e != end(polygon); e++) + _triangles.push_back(Triangle(e->p1, e->p2, *p)); + } + + _triangles.erase(std::remove_if(begin(_triangles), end(_triangles), [p1, p2, p3](Triangle &t) { + return t.containsVertex(p1) || t.containsVertex(p2) || t.containsVertex(p3); + }), end(_triangles)); + } +}; + +//------------------------------------------------------------------------------------- +struct Pixel +{ + int32_t x, y; +}; +typedef std::vector PixelBuf; + +//------------------------------------------------------------------------------------- +class Canvas +{ +public: + Canvas() : m_width(0), m_height(0), m_pixelBuf(nullptr), m_errorFlag(false) { } + ~Canvas() { + delete[] m_pixelBuf; + m_pixelBuf = nullptr; + } + + int32_t getWidth(void) const { return m_width; } + int32_t getHeight(void) const { return m_height; } + + void init(int32_t width, int32_t height) { + if (m_width != width || m_height != height) { + if (m_pixelBuf) delete[] m_pixelBuf; + + m_width = width; + m_height = height; + //alloc pixels + m_pixelBuf = new std::atomic[m_width*m_height]; + } + + for (size_t i = 0; i < (size_t)(m_width*m_height); i++) { + m_pixelBuf[i] = 0; + } + m_errorFlag = false; + } + + void clear(void) { + for (size_t i = 0; i < (size_t)(m_width*m_height); i++) { + m_pixelBuf[i] = 0; + } + m_errorFlag = false; + } + + void razFunction_fetchadd(const std::pair& pixel, const fVector3& b) { + if (m_errorFlag.load()) return; + + if (pixel.first < 0 || pixel.first >= m_width || pixel.second < 0 || pixel.second >= m_height) { + bool _false = false; + m_errorFlag.compare_exchange_strong(_false, true); + return; + } + m_pixelBuf[pixel.second*m_width + pixel.first].fetch_add(1); + } + + void razFunction(const std::pair& pixel, const fVector3& b, uint32_t randCol) { + if (m_errorFlag.load()) return; + + if (pixel.first < 0 || pixel.first >= m_width || pixel.second < 0 || pixel.second >= m_height) { + bool _false = false; + m_errorFlag.compare_exchange_strong(_false, true); + return; + } + m_pixelBuf[pixel.second*m_width + pixel.first] = randCol; + } + + bool checkResult(bool debug=false) { + if (m_errorFlag) return false; + + if (debug) { + bool succ = true; + uint32_t* colBuf = new uint32_t[m_width*m_height]; + + for (int32_t i = 0; i < m_width*m_height; i++) { + colBuf[i] = m_pixelBuf[i].load(); + if (m_pixelBuf[i] == 0) { + if (debug) printf("error: %d,%d=%d\n", i%m_width, i/ m_width, m_pixelBuf[(size_t)i].load()); + succ = false; + //return false; + } + } + + davinci_utils::dump_pixel_buffer("debug.png", m_width, m_height, colBuf, PF_UINT8_RGBA); + delete[] colBuf; + return succ; + } + else { + for (size_t i = 0; i < (size_t)(m_width*m_height); i++) { + if (m_pixelBuf[i] == 0) return false; + } + } + return true; + } + + bool checkResult(const PixelBuf& pixelExpected) { + if (m_errorFlag) return false; + + for (const auto& pixel : pixelExpected) { + assert(pixel.x >= 0 && pixel.x < m_width && pixel.y >= 0 && pixel.y < m_height); + auto& p = m_pixelBuf[pixel.y*m_width + pixel.x]; + if (p != 1) return false; + p = 0; + } + for (size_t i = 0; i < (size_t)(m_width*m_height); i++) { + if (m_pixelBuf[i] != 0) return false; + } + return true; + } + +private: + int32_t m_width, m_height; + std::atomic* m_pixelBuf; + + std::atomic m_errorFlag; + std::string m_strError; +}; + +//------------------------------------------------------------------------------------- +static bool _triangleRasterizationRules(Canvas& canvas, const Triangle& triangle, const PixelBuf& pixelExpect, bool ccw=true) +{ + auto razFunc = std::bind(&Canvas::razFunction_fetchadd, &canvas, std::placeholders::_1, std::placeholders::_2); + + canvas.clear(); + + Rasterizer::drawTriangleLarrabee(canvas.getWidth(), canvas.getHeight(), + triangle.p1, triangle.p2, triangle.p3, + razFunc, ccw); + + return canvas.checkResult(pixelExpect); +} + +//------------------------------------------------------------------------------------- +TEST(Rasterizer, Basic) +{ + const int32_t canvasWidth = 64, canvasHeight = 64; + + Canvas canvas; + canvas.init(canvasWidth, canvasHeight); + + /* + Triangle Rasterization rules test(Without Multisampling) + https://msdn.microsoft.com/en-us/library/windows/desktop/cc627092(v=vs.85).aspx + */ + { + Triangle triangle(fVector2(4.5f, 7.5f), fVector2(4.5f, 7.5f), fVector2(4.5f, 7.5f)); + PixelBuf pixels = {}; + EXPECT_TRUE(_triangleRasterizationRules(canvas, triangle, pixels, false)); + } + { + Triangle triangle(fVector2(1, 2), fVector2(7, 4), fVector2(5, 2)); + PixelBuf pixels = { {2, 2}, {3, 2}, {4, 2}, {5, 3} }; + EXPECT_TRUE(_triangleRasterizationRules(canvas, triangle, pixels, false)); + } + { + Triangle triangle(fVector2(5, 2), fVector2(7, 4), fVector2(8, 1)); + PixelBuf pixels = { { 5, 2 },{ 6, 1 },{ 6, 2 },{ 6, 3 }, {7, 1} }; + EXPECT_TRUE(_triangleRasterizationRules(canvas, triangle, pixels, false)); + } + { + Triangle triangle(fVector2(8, 1), fVector2(7, 4), fVector2(9.5f, 2.5f)); + PixelBuf pixels = { { 7, 2 },{ 7, 3 }, { 8, 2 } }; + EXPECT_TRUE(_triangleRasterizationRules(canvas, triangle, pixels, false)); + } + { + Triangle triangle(fVector2(1, 7), fVector2(6, 6), fVector2(2, 4)); + PixelBuf pixels = { { 2, 4 }, {1, 5 }, { 2, 5 }, { 3, 5 }, { 4, 5 }, { 1, 6 }, { 2, 6 }, }; + EXPECT_TRUE(_triangleRasterizationRules(canvas, triangle, pixels, false)); + } + { + Triangle triangle(fVector2(5.25f, 6.75f), fVector2(6.25f, 7.75f), fVector2(6.25f, 6.75f)); + PixelBuf pixels = { }; + EXPECT_TRUE(_triangleRasterizationRules(canvas, triangle, pixels, false)); + } + { + Triangle triangle(fVector2(6.5f, 5.5f), fVector2(7.5f, 7.5f), fVector2(7.5f, 6.5f)); + PixelBuf pixels = {}; + EXPECT_TRUE(_triangleRasterizationRules(canvas, triangle, pixels, false)); + } + { + Triangle triangle(fVector2(7.6f, 5.5f), fVector2(9.75f, 7.25f), fVector2(11.6f, 5.5f)); + PixelBuf pixels = { { 9,6 },{ 10, 6 } }; + EXPECT_TRUE(_triangleRasterizationRules(canvas, triangle, pixels, false)); + } + { + Triangle triangle(fVector2(7.6f, 5.5f), fVector2(11.6f, 5.5f), fVector2(9.5f, 2.6f)); + PixelBuf pixels = { {8, 5}, { 9, 5 }, { 10, 5 }, { 11, 5 }, { 8, 4 }, { 9, 4 }, { 10, 4 }, {9, 3} }; + EXPECT_TRUE(_triangleRasterizationRules(canvas, triangle, pixels, false)); + } + { + Triangle triangle(fVector2(11.5f, 1.5f), fVector2(11.5f, 3.5f), fVector2(12.5f, 2.5f)); + PixelBuf pixels = { {11,2} }; + EXPECT_TRUE(_triangleRasterizationRules(canvas, triangle, pixels, false)); + } + { + Triangle triangle(fVector2(9.5f, 0.5f), fVector2(10.5f, 0.5f), fVector2(9.5f, -1.5f)); + PixelBuf pixels = { { 9,0 } }; + EXPECT_TRUE(_triangleRasterizationRules(canvas, triangle, pixels, false)); + } + { + Triangle triangle(fVector2(13.5f, 0.5f), fVector2(13.5f, 2.5f), fVector2(15.5f, 2.5f)); + PixelBuf pixels = { { 13,1 }, {13, 2}, {14, 2} }; + EXPECT_TRUE(_triangleRasterizationRules(canvas, triangle, pixels, false)); + } + { + Triangle triangle(fVector2(13.5f, 0.5f), fVector2(15.5f, 2.5f), fVector2(15.5f, 0.5f)); + PixelBuf pixels = { { 14, 1 } }; + EXPECT_TRUE(_triangleRasterizationRules(canvas, triangle, pixels, false)); + } + { + Triangle triangle(fVector2(13.5f, 6.5f), fVector2(14.5f, 5.5f), fVector2(14.5f, 3.5f)); + PixelBuf pixels = { }; + EXPECT_TRUE(_triangleRasterizationRules(canvas, triangle, pixels, false)); + } + { + Triangle triangle(fVector2(13.5f, 6.5f), fVector2(15, 8), fVector2(14.5f, 5.5f) ); + PixelBuf pixels = { {13, 6}, {14, 6}, {14, 7} }; + EXPECT_TRUE(_triangleRasterizationRules(canvas, triangle, pixels, false)); + } +} + +//------------------------------------------------------------------------------------- +TEST(Rasterizer, Delaunay) +{ + //seed=1499743811, counts=5, range=0.8f; + //seed=1499670829, counts=5, range=1.f; + + const int32_t width = 1024, height = 1024; + const int32_t verticesCounts = MathUtil::rangeRandom(5, 30); + + float range = 0.8f; + std::vector vertices; + for (int32_t i = 0; i < verticesCounts; i++) { + float32_t x = MathUtil::rangeRandom(width * (1 - range) / 2.f, width * (1 + range) / 2.f); + float32_t y = MathUtil::rangeRandom(height * (1 - range) / 2.f, height * (1 + range) / 2.f); + vertices.push_back(fVector2(x, y)); + } + vertices.push_back(fVector2(0, 0)); + vertices.push_back(fVector2((float)width, 0)); + vertices.push_back(fVector2((float)width, (float)height)); + vertices.push_back(fVector2(0, (float)height)); + + std::vector triangles; + Delaunay::triangulate(vertices, triangles); + + Canvas canvas; + canvas.init(width, height); + canvas.clear(); + + for (size_t i = 0; i < triangles.size(); i++) { + const Triangle& triangle = triangles[i]; + + uint32_t randCol = 0xFF000000 + ((uint32_t)(rand() / 240) << 16) + ((uint32_t)(rand() / 240) << 8) + ((uint32_t)(rand() / 240)); + + auto razFunc = std::bind(&Canvas::razFunction, &canvas, std::placeholders::_1, std::placeholders::_2, randCol); + Rasterizer::drawTriangleLarrabee(canvas.getWidth(), canvas.getHeight(), + triangle.p1, triangle.p2, triangle.p3, + razFunc, false); + } + EXPECT_TRUE(canvas.checkResult(true)); +} + diff --git a/test/unit/dvt_unit_vector2.cpp b/test/unit/dvt_unit_vector2.cpp new file mode 100644 index 0000000..65f8610 --- /dev/null +++ b/test/unit/dvt_unit_vector2.cpp @@ -0,0 +1,268 @@ +#include + +#include +#include + +#include "dvt_unit_common.h" + +using namespace davinci; + +//------------------------------------------------------------------------------------- +TEST(Math_Vector2, Basic) +{ + { + fVector2 v1; + EXPECT_EQ(v1.x, 0.f); + EXPECT_EQ(v1.y, 0.f); + } + { + float a = _randomFloat(), b = _randomFloat(); + fVector2 v1(a, b), v2(v1); + EXPECT_EQ(v1.x, a); + EXPECT_EQ(v1.y, b); + + EXPECT_EQ(v2.x, a); + EXPECT_EQ(v2.y, b); + } + { + float a = _randomFloat(), b = _randomFloat(); + fVector2 v1; + v1[0] = a; v1[1] = b; + + EXPECT_EQ(v1.x, a); + EXPECT_EQ(v1.y, b); + + EXPECT_EQ(v1[0], a); + EXPECT_EQ(v1[1], b); + } + { + float a = _randomFloat(); + fVector2 v2(a); + EXPECT_EQ(v2.x, a); + EXPECT_EQ(v2.y, a); + } + { + float a[2] = { TWO_RANDOM_FLOAT }; + fVector2 v2(a); + EXPECT_EQ(v2.x, a[0]); + EXPECT_EQ(v2.y, a[1]); + } + { + int a[2] = { rand() - RAND_MAX/2, rand() - RAND_MAX / 2 }; + fVector2 v2(a); + EXPECT_EQ(v2.x, (float)a[0]); + EXPECT_EQ(v2.y, (float)a[1]); + } + + //operator + { + fVector2 v1(TWO_RANDOM_FLOAT), v2; + v2 = v1; + EXPECT_EQ(v1.x, v2.x); + EXPECT_EQ(v1.y, v2.y); + } + { + float a = _randomFloat(); + fVector2 v1; + v1 = a; + EXPECT_EQ(v1.x, a); + EXPECT_EQ(v1.y, a); + } + { + fVector2 v1(TWO_RANDOM_FLOAT), v2(v1); + EXPECT_TRUE(v1 == v2); + v2.x += 1.f; + EXPECT_TRUE(v1 != v2); + } + + { + float a = 0.f; + while (a == 0.f) { a = _randomFloat(); } + + fVector2 v1(TWO_RANDOM_FLOAT); + + fVector2 v2;// (_randomFloat(), _randomFloat()); + while (v2.x == 0.f || v2.y == 0.f) { + v2 = fVector2(TWO_RANDOM_FLOAT); + } + fVector2 v3; + + v3 = v1 + v2; + EXPECT_EQ(v3.x, v1.x + v2.x); + EXPECT_EQ(v3.y, v1.y + v2.y); + v3 = v1 - v2; + EXPECT_EQ(v3.x, v1.x - v2.x); + EXPECT_EQ(v3.y, v1.y - v2.y); + v3 = v1 * a; + EXPECT_EQ(v3.x, v1.x * a); + EXPECT_EQ(v3.y, v1.y * a); + v3 = v1 * v2; + EXPECT_EQ(v3.x, v1.x * v2.x); + EXPECT_EQ(v3.y, v1.y * v2.y); + v3 = v1 / a; + EXPECT_EQ(v3.x, v1.x / a); + EXPECT_EQ(v3.y, v1.y / a); + v3 = v1 / v2; + EXPECT_EQ(v3.x, v1.x / v2.x); + EXPECT_EQ(v3.y, v1.y / v2.y); + } + + { + fVector2 v1(TWO_RANDOM_FLOAT), v2; + v2 = +v1; + EXPECT_TRUE(v1 == v2); + + v2 = -v1; + EXPECT_EQ(v1.x, -v2.x); + EXPECT_EQ(v1.y, -v2.y); + } + + { + float a = _randomFloat(); + fVector2 v1, v2; + while (v1.x == 0.f || v1.y == 0.f) { + v1 = fVector2(TWO_RANDOM_FLOAT); + } + + v2 = a * v1; + EXPECT_EQ(v2.x, a*v1.x); + EXPECT_EQ(v2.y, a*v1.y); + + v2 = a / v1; + EXPECT_EQ(v2.x, a/v1.x); + EXPECT_EQ(v2.y, a/v1.y); + + v2 = v1 + a; + EXPECT_EQ(v2.x, a + v1.x); + EXPECT_EQ(v2.y, a + v1.y); + + v2 = a + v1; + EXPECT_EQ(v2.x, a + v1.x); + EXPECT_EQ(v2.y, a + v1.y); + + v2 = v1 - a; + EXPECT_EQ(v2.x, v1.x - a); + EXPECT_EQ(v2.y, v1.y - a); + + v2 = a + v1; + EXPECT_EQ(v2.x, a + v1.x); + EXPECT_EQ(v2.y, a + v1.y); + } + + { + fVector2 v1(TWO_RANDOM_FLOAT), v2, v3; + while (v2.x == 0.f || v2.y == 0.f) { + v2 = fVector2(TWO_RANDOM_FLOAT); + } + + float a = _randomFloat(); + while (a == 0.f) { a = _randomFloat(); } + + v3 = v1; + v3 += v2; + EXPECT_TRUE(v3 == v1+v2); + + v3 = v1; + v3 += a; + EXPECT_TRUE(v3 == v1 + a); + + v3 = v1; + v3 -= v2; + EXPECT_TRUE(v3 == v1 - v2); + + v3 = v1; + v3 -= a; + EXPECT_TRUE(v3 == v1 - a); + + v3 = v1; + v3 *= v2; + EXPECT_TRUE(v3 == v1 * v2); + + v3 = v1; + v3 *= a; + EXPECT_TRUE(v3 == v1 * a); + + v3 = v1; + v3 /= v2; + EXPECT_TRUE(v3 == v1 / v2); + + v3 = v1; + v3 /= a; + EXPECT_TRUE(v3 == v1 / a); + } + + { + fVector2 v1(TWO_RANDOM_FLOAT), v2(v1); + + v2 += 1.f; + EXPECT_TRUE(v2 > v1); + + v2 -= 2.f; + EXPECT_TRUE(v2 < v1); + } +} + +//------------------------------------------------------------------------------------- +TEST(Math_Vector2, Algorithm) +{ + //length + { + fVector2 v1(TWO_RANDOM_FLOAT); + glm::vec2 gv1(v1.x, v1.y); + + EXPECT_EQ(v1.length(), glm::length(gv1)); + } + + //squaredLength + { + fVector2 v1(TWO_RANDOM_FLOAT); + glm::vec2 gv1(v1.x, v1.y); + + EXPECT_EQ(v1.squaredLength(), glm::dot(gv1, gv1)); + } + + //distance + { + fVector2 v1(TWO_RANDOM_FLOAT), v2(TWO_RANDOM_FLOAT); + glm::vec2 gv1(v1.x, v1.y), gv2(v2.x, v2.y); + + EXPECT_EQ(v1.distance(v2), glm::distance(gv1, gv2)); + } + + //squaredDistance + { + fVector2 v1(TWO_RANDOM_FLOAT), v2(TWO_RANDOM_FLOAT); + glm::vec2 gv1(v1.x, v1.y), gv2(v2.x, v2.y); + glm::vec2 dis = gv1 - gv2; + + EXPECT_EQ(v1.squaredDistance(v2), glm::dot(dis, dis)); + } + + //dotProduct + { + fVector2 v1(TWO_RANDOM_FLOAT), v2(TWO_RANDOM_FLOAT); + glm::vec2 gv1(v1.x, v1.y), gv2(v2.x, v2.y); + + EXPECT_EQ(v1.dotProduct(v2), glm::dot(gv1, gv2)); + } + + //crossProduct + { + fVector2 v1(TWO_RANDOM_FLOAT), v2(TWO_RANDOM_FLOAT); + glm::vec3 gv1(v1.x, v1.y, 0), gv2(v2.x, v2.y, 0); + + EXPECT_EQ(v1.crossProduct(v2), glm::cross(gv1, gv2).z); + } + + //normalise + { + fVector2 v1(TWO_RANDOM_FLOAT); + glm::vec2 gv1(v1.x, v1.y); + + v1.normalise(); + glm::vec2 gv1_normal = glm::normalize(gv1); + + EXPECT_EQ(v1.x, gv1_normal.x); + EXPECT_EQ(v1.y, gv1_normal.y); + } +} diff --git a/test/unit/dvt_unit_vector3.cpp b/test/unit/dvt_unit_vector3.cpp new file mode 100644 index 0000000..ae0dee8 --- /dev/null +++ b/test/unit/dvt_unit_vector3.cpp @@ -0,0 +1,322 @@ +#include +#include + +#include +#include + +#include "dvt_unit_common.h" + +using namespace davinci; + +//------------------------------------------------------------------------------------- +TEST(Math_Vector3, Basic) +{ + { + fVector3 v1; + EXPECT_EQ(v1.x, 0.f); + EXPECT_EQ(v1.y, 0.f); + EXPECT_EQ(v1.z, 0.f); + } + { + float a = _randomFloat(), b = _randomFloat(), c = _randomFloat(); + fVector3 v1(a, b, c), v2(v1); + EXPECT_EQ(v1.x, a); + EXPECT_EQ(v1.y, b); + EXPECT_EQ(v1.z, c); + + EXPECT_EQ(v2.x, a); + EXPECT_EQ(v2.y, b); + EXPECT_EQ(v2.z, c); + } + { + float a = _randomFloat(), b = _randomFloat(), c = _randomFloat(); + fVector3 v1; + v1[0] = a; v1[1] = b; v1[2] = c; + + EXPECT_EQ(v1.x, a); + EXPECT_EQ(v1.y, b); + EXPECT_EQ(v1.z, c); + + EXPECT_EQ(v1[0], a); + EXPECT_EQ(v1[1], b); + EXPECT_EQ(v1[2], c); + + fVector2 v1_xy = v1.xy(); + EXPECT_EQ(v1_xy.x, a); + EXPECT_EQ(v1_xy.y, b); + + fVector2 v1_xz = v1.xz(); + EXPECT_EQ(v1_xz.x, a); + EXPECT_EQ(v1_xz.y, c); + + fVector2 v1_yz = v1.yz(); + EXPECT_EQ(v1_yz.x, b); + EXPECT_EQ(v1_yz.y, c); + } + { + float a = _randomFloat(); + fVector3 v2(a); + EXPECT_EQ(v2.x, a); + EXPECT_EQ(v2.y, a); + EXPECT_EQ(v2.z, a); + } + { + float a[3] = { THREE_RANDOM_FLOAT }; + fVector3 v2(a); + EXPECT_EQ(v2.x, a[0]); + EXPECT_EQ(v2.y, a[1]); + EXPECT_EQ(v2.z, a[2]); + } + { + int a[3] = { rand() - RAND_MAX / 2, rand() - RAND_MAX / 2, rand() - RAND_MAX / 2 }; + fVector3 v2(a); + EXPECT_EQ(v2.x, (float)a[0]); + EXPECT_EQ(v2.y, (float)a[1]); + EXPECT_EQ(v2.z, (float)a[2]); + } + { + float a = _randomFloat(), b = _randomFloat(), c = _randomFloat(); + fVector2 v1(a, b); + fVector3 v2(v1, c); + EXPECT_EQ(v2.x, a); + EXPECT_EQ(v2.y, b); + EXPECT_EQ(v2.z, c); + + fVector2 v3(a, b); + fVector3 v4(c, v3); + EXPECT_EQ(v4.x, c); + EXPECT_EQ(v4.y, a); + EXPECT_EQ(v4.z, b); + } + + //operator + { + fVector3 v1(THREE_RANDOM_FLOAT), v2; + v2 = v1; + EXPECT_EQ(v1.x, v2.x); + EXPECT_EQ(v1.y, v2.y); + EXPECT_EQ(v1.z, v2.z); + } + { + float a = _randomFloat(); + fVector3 v1; + v1 = a; + EXPECT_EQ(v1.x, a); + EXPECT_EQ(v1.y, a); + EXPECT_EQ(v1.z, a); + } + { + fVector3 v1(THREE_RANDOM_FLOAT), v2(v1); + EXPECT_TRUE(v1 == v2); + v2.x += 1.f; + EXPECT_TRUE(v1 != v2); + } + { + float a = 0.f; + while (a == 0.f) { a = _randomFloat(); } + + fVector3 v1(THREE_RANDOM_FLOAT); + + fVector3 v2; + while (v2.x == 0.f || v2.y == 0.f || v2.z == 0.f) { + v2 = fVector3(THREE_RANDOM_FLOAT); + } + fVector3 v3; + + v3 = v1 + v2; + EXPECT_EQ(v3.x, v1.x + v2.x); + EXPECT_EQ(v3.y, v1.y + v2.y); + EXPECT_EQ(v3.z, v1.z + v2.z); + v3 = v1 - v2; + EXPECT_EQ(v3.x, v1.x - v2.x); + EXPECT_EQ(v3.y, v1.y - v2.y); + EXPECT_EQ(v3.z, v1.z - v2.z); + v3 = v1 * a; + EXPECT_EQ(v3.x, v1.x * a); + EXPECT_EQ(v3.y, v1.y * a); + EXPECT_EQ(v3.z, v1.z * a); + v3 = v1 * v2; + EXPECT_EQ(v3.x, v1.x * v2.x); + EXPECT_EQ(v3.y, v1.y * v2.y); + EXPECT_EQ(v3.z, v1.z * v2.z); + v3 = v1 / a; + EXPECT_EQ(v3.x, v1.x / a); + EXPECT_EQ(v3.y, v1.y / a); + EXPECT_EQ(v3.z, v1.z / a); + v3 = v1 / v2; + EXPECT_EQ(v3.x, v1.x / v2.x); + EXPECT_EQ(v3.y, v1.y / v2.y); + EXPECT_EQ(v3.z, v1.z / v2.z); + } + { + fVector3 v1(THREE_RANDOM_FLOAT), v2; + v2 = +v1; + EXPECT_TRUE(v1 == v2); + + v2 = -v1; + EXPECT_EQ(v1.x, -v2.x); + EXPECT_EQ(v1.y, -v2.y); + EXPECT_EQ(v1.z, -v2.z); + } + + { + float a = _randomFloat(); + fVector3 v1, v2; + while (v1.x == 0.f || v1.y == 0.f || v1.z == 0.f) { + v1 = fVector3(THREE_RANDOM_FLOAT); + } + + v2 = a * v1; + EXPECT_EQ(v2.x, a*v1.x); + EXPECT_EQ(v2.y, a*v1.y); + EXPECT_EQ(v2.z, a*v1.z); + + v2 = a / v1; + EXPECT_EQ(v2.x, a / v1.x); + EXPECT_EQ(v2.y, a / v1.y); + EXPECT_EQ(v2.z, a / v1.z); + + v2 = v1 + a; + EXPECT_EQ(v2.x, a + v1.x); + EXPECT_EQ(v2.y, a + v1.y); + EXPECT_EQ(v2.z, a + v1.z); + + v2 = a + v1; + EXPECT_EQ(v2.x, a + v1.x); + EXPECT_EQ(v2.y, a + v1.y); + EXPECT_EQ(v2.z, a + v1.z); + + v2 = v1 - a; + EXPECT_EQ(v2.x, v1.x - a); + EXPECT_EQ(v2.y, v1.y - a); + EXPECT_EQ(v2.z, v1.z - a); + + v2 = a + v1; + EXPECT_EQ(v2.x, a + v1.x); + EXPECT_EQ(v2.y, a + v1.y); + EXPECT_EQ(v2.z, a + v1.z); + } + + { + fVector3 v1(THREE_RANDOM_FLOAT), v2, v3; + while (v2.x == 0.f || v2.y == 0.f || v2.z == 0.f) { + v2 = fVector3(THREE_RANDOM_FLOAT); + } + + float a = _randomFloat(); + while (a == 0.f) { a = _randomFloat(); } + + v3 = v1; + v3 += v2; + EXPECT_TRUE(v3 == v1 + v2); + + v3 = v1; + v3 += a; + EXPECT_TRUE(v3 == v1 + a); + + v3 = v1; + v3 -= v2; + EXPECT_TRUE(v3 == v1 - v2); + + v3 = v1; + v3 -= a; + EXPECT_TRUE(v3 == v1 - a); + + v3 = v1; + v3 *= v2; + EXPECT_TRUE(v3 == v1 * v2); + + v3 = v1; + v3 *= a; + EXPECT_TRUE(v3 == v1 * a); + + v3 = v1; + v3 /= v2; + EXPECT_TRUE(v3 == v1 / v2); + + v3 = v1; + v3 /= a; + EXPECT_TRUE(v3 == v1 / a); + } + + { + fVector3 v1(THREE_RANDOM_FLOAT), v2(v1); + + v2 += 1.f; + EXPECT_TRUE(v2 > v1); + + v2 -= 2.f; + EXPECT_TRUE(v2 < v1); + } +} + +//------------------------------------------------------------------------------------- +TEST(Math_Vector3, Algorithm) +{ + //length + { + fVector3 v1(THREE_RANDOM_FLOAT); + glm::vec3 gv1(v1.x, v1.y, v1.z); + + EXPECT_EQ(v1.length(), glm::length(gv1)); + } + + //squaredLength + { + fVector3 v1(THREE_RANDOM_FLOAT); + glm::vec3 gv1(v1.x, v1.y, v1.z); + + EXPECT_EQ(v1.squaredLength(), glm::dot(gv1, gv1)); + } + + //distance + { + fVector3 v1(THREE_RANDOM_FLOAT), v2(THREE_RANDOM_FLOAT); + glm::vec3 gv1(v1.x, v1.y, v1.z), gv2(v2.x, v2.y, v2.z); + + EXPECT_EQ(v1.distance(v2), glm::distance(gv1, gv2)); + } + + //squaredDistance + { + fVector3 v1(THREE_RANDOM_FLOAT), v2(THREE_RANDOM_FLOAT); + glm::vec3 gv1(v1.x, v1.y, v1.z), gv2(v2.x, v2.y, v2.z); + glm::vec3 len(gv1 - gv2); + + EXPECT_EQ(v1.squaredDistance(v2), glm::dot(len, len)); + } + + //dotProduct + { + fVector3 v1(THREE_RANDOM_FLOAT), v2(THREE_RANDOM_FLOAT); + glm::vec3 gv1(v1.x, v1.y, v1.z), gv2(v2.x, v2.y, v2.z); + + EXPECT_EQ(v1.dotProduct(v2), glm::dot(gv1, gv2)); + } + + //crossProduct + { + fVector3 v1(THREE_RANDOM_FLOAT), v2(THREE_RANDOM_FLOAT); + glm::vec3 gv1(v1.x, v1.y, v1.z), gv2(v2.x, v2.y, v2.z); + + fVector3 v3 = v1.crossProduct(v2); + glm::vec3 gv1_cross = glm::cross(gv1, gv2); + + EXPECT_EQ(v3.x, gv1_cross.x); + EXPECT_EQ(v3.y, gv1_cross.y); + EXPECT_EQ(v3.z, gv1_cross.z); + } + + //normalise + { + fVector3 v1(THREE_RANDOM_FLOAT); + glm::vec3 gv1(v1.x, v1.y, v1.z); + + v1.normalise(); + glm::vec3 gv1_nor = glm::normalize(gv1); + + EXPECT_EQ(v1.x, gv1_nor.x); + EXPECT_EQ(v1.y, gv1_nor.y); + EXPECT_EQ(v1.z, gv1_nor.z); + } +} diff --git a/test/unit/dvt_unit_vector4.cpp b/test/unit/dvt_unit_vector4.cpp new file mode 100644 index 0000000..48a41bc --- /dev/null +++ b/test/unit/dvt_unit_vector4.cpp @@ -0,0 +1,262 @@ +#include + +#include +#include + +#include "dvt_unit_common.h" + +using namespace davinci; + +//------------------------------------------------------------------------------------- +TEST(Math_Vector4, Basic) +{ + { + fVector4 v1; + EXPECT_EQ(v1.x, 0.f); + EXPECT_EQ(v1.y, 0.f); + EXPECT_EQ(v1.z, 0.f); + EXPECT_EQ(v1.w, 0.f); + } + { + float a = _randomFloat(), b = _randomFloat(), c = _randomFloat(), d = _randomFloat(); + fVector4 v1(a, b, c, d), v2(v1); + EXPECT_EQ(v1.x, a); + EXPECT_EQ(v1.y, b); + EXPECT_EQ(v1.z, c); + EXPECT_EQ(v1.w, d); + + EXPECT_EQ(v2.x, a); + EXPECT_EQ(v2.y, b); + EXPECT_EQ(v2.z, c); + EXPECT_EQ(v2.w, d); + } + { + float a = _randomFloat(), b = _randomFloat(), c = _randomFloat(), d = _randomFloat(); + fVector4 v1; + v1[0] = a; v1[1] = b; v1[2] = c; v1[3] = d; + + EXPECT_EQ(v1.x, a); + EXPECT_EQ(v1.y, b); + EXPECT_EQ(v1.z, c); + EXPECT_EQ(v1.w, d); + + EXPECT_EQ(v1[0], a); + EXPECT_EQ(v1[1], b); + EXPECT_EQ(v1[2], c); + EXPECT_EQ(v1[3], d); + } + { + float a = _randomFloat(); + fVector4 v2(a); + EXPECT_EQ(v2.x, a); + EXPECT_EQ(v2.y, a); + EXPECT_EQ(v2.z, a); + EXPECT_EQ(v2.w, a); + } + { + float a[4] = { FOUR_RANDOM_FLOAT }; + fVector4 v2(a); + EXPECT_EQ(v2.x, a[0]); + EXPECT_EQ(v2.y, a[1]); + EXPECT_EQ(v2.z, a[2]); + EXPECT_EQ(v2.w, a[3]); + } + { + int a[4] = { rand() - RAND_MAX / 2, rand() - RAND_MAX / 2, rand() - RAND_MAX / 2, rand() - RAND_MAX / 2 }; + fVector4 v2(a); + EXPECT_EQ(v2.x, (float)a[0]); + EXPECT_EQ(v2.y, (float)a[1]); + EXPECT_EQ(v2.z, (float)a[2]); + EXPECT_EQ(v2.w, (float)a[3]); + } + + //operator + { + fVector4 v1(FOUR_RANDOM_FLOAT), v2; + v2 = v1; + EXPECT_EQ(v1.x, v2.x); + EXPECT_EQ(v1.y, v2.y); + EXPECT_EQ(v1.z, v2.z); + EXPECT_EQ(v1.w, v2.w); + } + { + float a = _randomFloat(); + fVector4 v1; + v1 = a; + EXPECT_EQ(v1.x, a); + EXPECT_EQ(v1.y, a); + EXPECT_EQ(v1.z, a); + EXPECT_EQ(v1.w, a); + } + { + fVector4 v1(FOUR_RANDOM_FLOAT), v2(v1); + EXPECT_TRUE(v1 == v2); + v2.x += 1.f; + EXPECT_TRUE(v1 != v2); + } + { + float a = 0.f; + while (a == 0.f) { a = _randomFloat(); } + + fVector4 v1(FOUR_RANDOM_FLOAT); + + fVector4 v2; + while (v2.x == 0.f || v2.y == 0.f || v2.z == 0.f || v2.w == 0.f) { + v2 = fVector4(FOUR_RANDOM_FLOAT); + } + fVector4 v3; + + v3 = v1 + v2; + EXPECT_EQ(v3.x, v1.x + v2.x); + EXPECT_EQ(v3.y, v1.y + v2.y); + EXPECT_EQ(v3.z, v1.z + v2.z); + EXPECT_EQ(v3.w, v1.w + v2.w); + v3 = v1 - v2; + EXPECT_EQ(v3.x, v1.x - v2.x); + EXPECT_EQ(v3.y, v1.y - v2.y); + EXPECT_EQ(v3.z, v1.z - v2.z); + EXPECT_EQ(v3.w, v1.w - v2.w); + v3 = v1 * a; + EXPECT_EQ(v3.x, v1.x * a); + EXPECT_EQ(v3.y, v1.y * a); + EXPECT_EQ(v3.z, v1.z * a); + EXPECT_EQ(v3.w, v1.w * a); + v3 = v1 * v2; + EXPECT_EQ(v3.x, v1.x * v2.x); + EXPECT_EQ(v3.y, v1.y * v2.y); + EXPECT_EQ(v3.z, v1.z * v2.z); + EXPECT_EQ(v3.w, v1.w * v2.w); + v3 = v1 / a; + EXPECT_EQ(v3.x, v1.x / a); + EXPECT_EQ(v3.y, v1.y / a); + EXPECT_EQ(v3.z, v1.z / a); + EXPECT_EQ(v3.w, v1.w / a); + v3 = v1 / v2; + EXPECT_EQ(v3.x, v1.x / v2.x); + EXPECT_EQ(v3.y, v1.y / v2.y); + EXPECT_EQ(v3.z, v1.z / v2.z); + EXPECT_EQ(v3.w, v1.w / v2.w); + } + { + fVector4 v1(FOUR_RANDOM_FLOAT), v2; + v2 = +v1; + EXPECT_TRUE(v1 == v2); + + v2 = -v1; + EXPECT_EQ(v1.x, -v2.x); + EXPECT_EQ(v1.y, -v2.y); + EXPECT_EQ(v1.z, -v2.z); + EXPECT_EQ(v1.w, -v2.w); + } + + { + float a = _randomFloat(); + fVector4 v1, v2; + while (v1.x == 0.f || v1.y == 0.f || v1.z == 0.f || v1.w == 0.f) { + v1 = fVector4(FOUR_RANDOM_FLOAT); + } + + v2 = a * v1; + EXPECT_EQ(v2.x, a*v1.x); + EXPECT_EQ(v2.y, a*v1.y); + EXPECT_EQ(v2.z, a*v1.z); + EXPECT_EQ(v2.w, a*v1.w); + + v2 = a / v1; + EXPECT_EQ(v2.x, a / v1.x); + EXPECT_EQ(v2.y, a / v1.y); + EXPECT_EQ(v2.z, a / v1.z); + EXPECT_EQ(v2.w, a / v1.w); + + v2 = v1 + a; + EXPECT_EQ(v2.x, a + v1.x); + EXPECT_EQ(v2.y, a + v1.y); + EXPECT_EQ(v2.z, a + v1.z); + EXPECT_EQ(v2.w, a + v1.w); + + v2 = a + v1; + EXPECT_EQ(v2.x, a + v1.x); + EXPECT_EQ(v2.y, a + v1.y); + EXPECT_EQ(v2.z, a + v1.z); + EXPECT_EQ(v2.w, a + v1.w); + + v2 = v1 - a; + EXPECT_EQ(v2.x, v1.x - a); + EXPECT_EQ(v2.y, v1.y - a); + EXPECT_EQ(v2.z, v1.z - a); + EXPECT_EQ(v2.w, v1.w - a); + + v2 = a + v1; + EXPECT_EQ(v2.x, a + v1.x); + EXPECT_EQ(v2.y, a + v1.y); + EXPECT_EQ(v2.z, a + v1.z); + EXPECT_EQ(v2.w, a + v1.w); + } + + { + fVector4 v1(FOUR_RANDOM_FLOAT), v2, v3; + while (v2.x == 0.f || v2.y == 0.f || v2.z == 0.f || v2.w == 0.f) { + v2 = fVector4(FOUR_RANDOM_FLOAT); + } + + float a = _randomFloat(); + while (a == 0.f) { a = _randomFloat(); } + + v3 = v1; + v3 += v2; + EXPECT_TRUE(v3 == v1 + v2); + + v3 = v1; + v3 += a; + EXPECT_TRUE(v3 == v1 + a); + + v3 = v1; + v3 -= v2; + EXPECT_TRUE(v3 == v1 - v2); + + v3 = v1; + v3 -= a; + EXPECT_TRUE(v3 == v1 - a); + + v3 = v1; + v3 *= v2; + EXPECT_TRUE(v3 == v1 * v2); + + v3 = v1; + v3 *= a; + EXPECT_TRUE(v3 == v1 * a); + + v3 = v1; + v3 /= v2; + EXPECT_TRUE(v3 == v1 / v2); + + v3 = v1; + v3 /= a; + EXPECT_TRUE(v3 == v1 / a); + } + + { + fVector4 v1(FOUR_RANDOM_FLOAT), v2(v1); + + v2 += 1.f; + EXPECT_TRUE(v2 > v1); + + v2 -= 2.f; + EXPECT_TRUE(v2 < v1); + } +} + + +//------------------------------------------------------------------------------------- +TEST(Math_Vector4, Algorithm) +{ + + //dotProduct + { + fVector4 v1(FOUR_RANDOM_FLOAT), v2(FOUR_RANDOM_FLOAT); + glm::vec4 gv1(v1.x, v1.y, v1.z, v1.w), gv2(v2.x, v2.y, v2.z, v2.w); + + EXPECT_EQ(v1.dotProduct(v2), glm::dot(gv1, gv2)); + } + +}