feat(gltf): add gltf support (#8643)
Arduino Lint / lint (push) Has been cancelled
Build Examples with C++ Compiler / build-examples (push) Has been cancelled
MicroPython CI / Build esp32 port (push) Has been cancelled
MicroPython CI / Build rp2 port (push) Has been cancelled
MicroPython CI / Build stm32 port (push) Has been cancelled
MicroPython CI / Build unix port (push) Has been cancelled
C/C++ CI / Build OPTIONS_16BIT - Ubuntu (push) Has been cancelled
C/C++ CI / Build OPTIONS_24BIT - Ubuntu (push) Has been cancelled
C/C++ CI / Build OPTIONS_FULL_32BIT - Ubuntu (push) Has been cancelled
C/C++ CI / Build OPTIONS_NORMAL_8BIT - Ubuntu (push) Has been cancelled
C/C++ CI / Build OPTIONS_SDL - Ubuntu (push) Has been cancelled
C/C++ CI / Build OPTIONS_VG_LITE - Ubuntu (push) Has been cancelled
C/C++ CI / Build OPTIONS_16BIT - cl - Windows (push) Has been cancelled
C/C++ CI / Build OPTIONS_16BIT - gcc - Windows (push) Has been cancelled
C/C++ CI / Build OPTIONS_24BIT - cl - Windows (push) Has been cancelled
C/C++ CI / Build OPTIONS_24BIT - gcc - Windows (push) Has been cancelled
C/C++ CI / Build OPTIONS_FULL_32BIT - cl - Windows (push) Has been cancelled
C/C++ CI / Build OPTIONS_FULL_32BIT - gcc - Windows (push) Has been cancelled
C/C++ CI / Build OPTIONS_VG_LITE - cl - Windows (push) Has been cancelled
C/C++ CI / Build OPTIONS_VG_LITE - gcc - Windows (push) Has been cancelled
C/C++ CI / Build ESP IDF ESP32S3 (push) Has been cancelled
C/C++ CI / Run tests with 32bit build (push) Has been cancelled
C/C++ CI / Run tests with 64bit build (push) Has been cancelled
BOM Check / bom-check (push) Has been cancelled
Verify that lv_conf_internal.h matches repository state / verify-conf-internal (push) Has been cancelled
Verify the widget property name / verify-property-name (push) Has been cancelled
Verify code formatting / verify-formatting (push) Has been cancelled
Compare file templates with file names / template-check (push) Has been cancelled
Build docs / build-and-deploy (push) Has been cancelled
Test API JSON generator / Test API JSON (push) Has been cancelled
Check Makefile / Build using Makefile (push) Has been cancelled
Check Makefile for UEFI / Build using Makefile for UEFI (push) Has been cancelled
Emulated Performance Test / ARM Emulated Benchmark - Script Check (scripts/perf/tests/benchmark_results_comment/test.sh) (push) Has been cancelled
Emulated Performance Test / ARM Emulated Benchmark - Script Check (scripts/perf/tests/filter_docker_logs/test.sh) (push) Has been cancelled
Emulated Performance Test / ARM Emulated Benchmark - Script Check (scripts/perf/tests/serialize_results/test.sh) (push) Has been cancelled
Emulated Performance Test / ARM Emulated Benchmark 32b - lv_conf_perf32b (push) Has been cancelled
Emulated Performance Test / ARM Emulated Benchmark 64b - lv_conf_perf64b (push) Has been cancelled
Emulated Performance Test / ARM Emulated Benchmark - Save PR Number (push) Has been cancelled
Hardware Performance Test / Hardware Performance Benchmark (push) Has been cancelled
Hardware Performance Test / HW Benchmark - Save PR Number (push) Has been cancelled
Performance Tests CI / Perf Tests OPTIONS_TEST_PERF_32B - Ubuntu (push) Has been cancelled
Performance Tests CI / Perf Tests OPTIONS_TEST_PERF_64B - Ubuntu (push) Has been cancelled
Port repo release update / run-release-branch-updater (push) Has been cancelled
Verify Font License / verify-font-license (push) Has been cancelled
Verify Kconfig / verify-kconfig (push) Has been cancelled
Close stale issues and PRs / stale (push) Has been cancelled

Co-authored-by: Matt Kimball <greyworld@gmail.com>
Co-authored-by: Liam Howatt <30486941+liamHowatt@users.noreply.github.com>
This commit is contained in:
André Costa
2025-08-26 18:59:28 +02:00
committed by GitHub
parent b10ddac8a4
commit 3b58434ed4
60 changed files with 24426 additions and 31 deletions
File diff suppressed because it is too large Load Diff
+45
View File
@@ -0,0 +1,45 @@
/**
* @file lv_demo_gltf.h
*
*/
#ifndef LV_DEMO_GLTF_H
#define LV_DEMO_GLTF_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_demos.h"
#if LV_USE_DEMO_GLTF
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
lv_obj_t * lv_demo_gltf(const char * path);
/**********************
* MACROS
**********************/
#endif /*LV_USE_DEMO_GLTF*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DEMO_GLTF_H*/
+4
View File
@@ -43,6 +43,10 @@ extern "C" {
#include "render/lv_demo_render.h"
#endif
#if LV_USE_DEMO_GLTF
#include "gltf/lv_demo_gltf.h"
#endif
/*********************
* DEFINES
*********************/
+404
View File
@@ -0,0 +1,404 @@
.. _gltf:
====
glTF
====
**glTF** (GL Transmission Format) is a royalty-free specification for efficient transmission and loading of 3D scenes and models.
The glTF extension in LVGL provides 3D model loading, rendering, and animation capabilities. It supports the full glTF 2.0 specification with advanced rendering features.
For a detailed introduction to glTF, see: https://www.khronos.org/gltf/
Features
********
LVGL's glTF implementation provides comprehensive 3D rendering capabilities:
**File Format Support:**
* Loading glTF (.gltf) and binary GLB (.glb) files from local filesystem
* Support for hex-encoded bytes in source include files when filesystem is not available
* JPG, PNG, and WebP compressed texture support
* External texture files or textures embedded within the glTF source
**Rendering & Lighting:**
* Image-Based Lighting (IBL) for realistic environmental lighting
* Punctual lighting support with animated light sources
* Physically Based Rendering (PBR) materials with full texture support:
* Diffuse/albedo textures
* Roughness and metallic workflow textures
* Normal maps for surface detail
* Ambient occlusion textures
**Advanced Materials:**
* Emissive materials for glowing effects and self-illuminated surfaces
* Refractive materials with realistic distortion effects revealing geometry behind surfaces
* Clearcoat material support for multi-layer surface effects (like automotive paint finishes)
* Full transparency support with alpha blending
**Animation System:**
* Keyframe-based animations for object transformations
* Single-skeleton skinned character animations
* Animated punctual lights with dynamic lighting effects
* Parameter binding for real-time monitoring and control of object properties (position, rotation, scale)
* Dynamic parameter override from host application
**Camera & Viewport:**
* Programmatic viewport control with full camera manipulation
* Support for cameras defined within 3D editor software
* Multiple camera switching for different scene perspectives
* Orthographic and perspective projection modes
**Rendering Quality:**
* Configurable antialiasing with multiple modes:
* Always-on for consistent quality
* Always-off for maximum performance
* Auto-on that activates when scene movement stops
* Flexible background rendering:
* Environment-based backgrounds using IBL
* Solid color backgrounds
* Transparent backgrounds for overlay effects
What is glTF?
*************
glTF (Graphics Library Transmission Format) is a 3D file format that stores 3D model information in JSON format. It supports:
- **Geometry**: Meshes, primitives, and vertex data
- **Materials**: PBR (Physically Based Rendering) materials with textures
- **Animations**: Keyframe-based animations for nodes
- **Cameras**: Perspective and orthographic camera definitions
- **Scenes**: Hierarchical scene graphs with node transformations
glTF files can be stored as:
- **.gltf**: JSON format with external binary and image files
- **.glb**: Binary format with all assets embedded in a single file
Features
********
LVGL's glTF implementation provides comprehensive 3D rendering capabilities:
**File Format Support:**
* Loading glTF (.gltf) and binary GLB (.glb) files from local filesystem
* Support for hex-encoded bytes in source include files when filesystem is not available
* JPG, PNG, and WebP compressed texture support
* External texture files or textures embedded within the glTF source
**Rendering & Lighting:**
* Image-Based Lighting (IBL) for realistic environmental lighting
* Punctual lighting support with animated light sources
* Physically Based Rendering (PBR) materials with full texture support:
* Diffuse/albedo textures
* Roughness and metallic workflow textures
* Normal maps for surface detail
* Ambient occlusion textures
**Advanced Materials:**
* Emissive materials for glowing effects and self-illuminated surfaces
* Refractive materials with realistic distortion effects revealing geometry behind surfaces
* Clearcoat material support for multi-layer surface effects (like automotive paint finishes)
* Full transparency support with alpha blending
**Animation System:**
* Keyframe-based animations for object transformations
* Single-skeleton skinned character animations
* Animated punctual lights with dynamic lighting effects
* Parameter binding for real-time monitoring and control of object properties (position, rotation, scale)
* Dynamic parameter override from host application
**Camera & Viewport:**
* Programmatic viewport control with full camera manipulation
* Support for cameras defined within 3D editor software
* Multiple camera switching for different scene perspectives
* Orthographic and perspective projection modes
**Rendering Quality:**
* Configurable antialiasing with multiple modes:
* Always-on for consistent quality
* Always-off for maximum performance
* Auto-on that activates when scene movement stops
* Flexible background rendering:
* Environment-based backgrounds using IBL
* Solid color backgrounds
* Transparent backgrounds for overlay effects
Requirements
************
The glTF extension relies on **OpenGL ES 3.0** for 3D rendering. LVGL includes built-in support for OpenGL ES through the GLFW driver, which provides cross-platform window management and OpenGL context creation.
The renderer uses OpenGL ES 3.0 shaders (GLSL version 300 es) to provide modern PBR (Physically Based Rendering) capabilities.
Dependencies
------------
The glTF extension requires the following external libraries:
:fastgltf: A C++20 library for parsing glTF files (https://github.com/spnda/fastgltf)
:libwebp: WebP image format support for textures (https://github.com/webmproject/libwebp)
Setup
*****
1. **Install Dependencies with CMake**
The recommended way to integrate the required libraries is using CMake's FetchContent:
.. code-block:: cmake
include(FetchContent)
# Set policy to allow to run the target_link_libraries cmd on targets that are
# build in another directory. Currently, the linking is not handled by
cmake_policy(SET CMP0079 NEW)
# Fetch fastgltf library
FetchContent_Declare(
fastgltf
GIT_REPOSITORY https://github.com/spnda/fastgltf
GIT_TAG 4e2261350888bae7c35a1f39991f6233d57795f5)
set(FASTGLTF_ENABLE_DEPRECATED_EXT
ON
CACHE BOOL "" FORCE)
set(FASTGLTF_DIFFUSE_TRANSMISSION_SUPPORT
ON
CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(fastgltf)
# Fetch libwebp library
FetchContent_Declare(
webp
GIT_REPOSITORY https://github.com/webmproject/libwebp
GIT_TAG fa6f56496a442eed59b103250021e4b14ebf1427)
FetchContent_MakeAvailable(webp)
# Link libraries to LVGL
target_link_libraries(lvgl PUBLIC webp fastgltf)
2. **Enable glTF Support**
Set :c:macro:`LV_USE_GLTF` to ``1`` in ``lv_conf.h``.
Also enable other required dependencies by setting the following defines to ``1``:
- :c:macro:`LV_USE_OPENGLES`
- :c:macro:`LV_USE_DRAW_OPENGLES`
- :c:macro:`LV_USE_3DTEXTURE`
3. **Setup OpenGL ES Driver**
Follow the OpenGL ES driver setup documentation (:ref:``opengl_es_driver``) to configure GLFW and OpenGL ES support for your platform.
4. **Basic Setup Example**
.. code-block:: c
int main(void)
{
/* Initialize LVGL */
lv_init();
/* GLFW setup */
lv_glfw_window_t *window = lv_glfw_window_create(WINDOW_WIDTH, WINDOW_HEIGHT, true);
lv_display_t *display = lv_opengles_texture_create(WINDOW_WIDTH, WINDOW_HEIGHT);
unsigned int texture_id = lv_opengles_texture_get_texture_id(display);
lv_glfw_window_add_texture(window, texture_id, WINDOW_WIDTH, WINDOW_HEIGHT);
/* Load and display glTF demo */
lv_demo_gltf("A:<path/to/gltf>");
while (1) {
uint32_t time_until_next = lv_timer_handler();
if (time_until_next == LV_NO_TIMER_READY) {
time_until_next = LV_DEF_REFR_PERIOD;
}
lv_delay_ms(time_until_next);
}
return 0;
}
Usage
*****
Demo Application
----------------
LVGL provides a complete glTF demo that showcases the capabilities of the 3D viewer:
.. code-block:: c
lv_demo_gltf("A:<path/to/your/model.glb>");
This demo creates an interactive 3D viewer with:
- Mouse/touch controls for camera rotation
- Zoom controls
- Animation playback controls
- Camera switching
- Visual settings adjustment
Basic glTF Viewer Creation
--------------------------
Here's how to create a basic glTF viewer and load a model:
.. code-block:: c
/* Create a glTF viewer object */
lv_obj_t * gltf = lv_gltf_create(lv_screen_active());
/* Load a glTF model from file */
lv_gltf_model_t * model = lv_gltf_load_model_from_file(gltf,
"A:path/to/your/model.glb");
if (model == NULL) {
LV_LOG_ERROR("Failed to load glTF model");
return;
}
Camera Controls
---------------
The glTF viewer provides comprehensive camera controls:
.. code-block:: c
/* Set camera rotation */
lv_gltf_set_yaw(gltf, 45.0f); /* Horizontal rotation */
lv_gltf_set_pitch(gltf, -30.0f); /* Vertical rotation */
/* Set camera distance */
lv_gltf_set_distance(gltf, 5.0f);
/* Set field of view (0 for orthographic) */
lv_gltf_set_fov(gltf, 60.0f);
/* Set the focal point - The position the camera is pointing at */
lv_gltf_set_focal_x(gltf, 0.0f);
lv_gltf_set_focal_y(gltf, 0.0f);
lv_gltf_set_focal_z(gltf, 0.0f);
/* Recenter camera on model */
lv_gltf_recenter(gltf, model);
Animation Control
-----------------
Control model animations with these functions:
.. code-block:: c
/* Get number of animations */
size_t anim_count = lv_gltf_model_get_animation_count(model);
/* Play a specific animation */
lv_gltf_model_play_animation(model, 0);
/* Control animation speed */
lv_gltf_set_animation_speed(gltf, LV_GLTF_ANIM_SPEED_2X);
/* Pause/resume animation */
lv_gltf_model_pause_animation(model);
bool is_paused = lv_gltf_model_is_animation_paused(model);
Visual Settings
---------------
Customize the visual appearance of your 3D scene:
.. code-block:: c
/* Background settings */
lv_gltf_set_background_mode(gltf, LV_GLTF_BG_ENVIRONMENT);
lv_gltf_set_background_blur(gltf, 50);
/* Lighting and exposure */
lv_gltf_set_env_brightness(gltf, 150);
lv_gltf_set_image_exposure(gltf, 1.2f);
/* Anti-aliasing */
lv_gltf_set_antialiasing_mode(gltf, LV_GLTF_AA_DYNAMIC);
Multi-Model Support
-------------------
Load and manage multiple glTF models in a single viewer:
.. code-block:: c
/* Load multiple models */
lv_gltf_model_t * model1 = lv_gltf_load_model_from_file(gltf, "A:model1.glb");
lv_gltf_model_t * model2 = lv_gltf_load_model_from_file(gltf, "A:model2.glb");
/* Get model information */
size_t model_count = lv_gltf_get_model_count(gltf);
lv_gltf_model_t * primary = lv_gltf_get_primary_model(gltf);
lv_gltf_model_t * specific = lv_gltf_get_model_by_index(gltf, 1);
Model Inspection
----------------
Query model properties to understand its structure:
.. code-block:: c
/* Get model component counts */
size_t mesh_count = lv_gltf_model_get_mesh_count(model);
size_t material_count = lv_gltf_model_get_material_count(model);
size_t texture_count = lv_gltf_model_get_texture_count(model);
size_t image_count = lv_gltf_model_get_image_count(model);
size_t node_count = lv_gltf_model_get_node_count(model);
size_t scene_count = lv_gltf_model_get_scene_count(model);
size_t camera_count = lv_gltf_model_get_camera_count(model);
Widget Architecture
*******************
The glTF widget extends the ``lv_3dtexture`` widget, which means:
- All standard ``lv_obj`` functions work with glTF widgets (positioning, sizing, styling, etc.)
- All ``lv_3dtexture`` functions are also available for advanced 3D texture management
Animation Speed System
----------------------
Animation speeds use integer values to avoid floating-point arithmetic:
- The speed ratio is calculated as: ``speed_value / LV_GLTF_ANIM_SPEED_NORMAL``
- ``LV_GLTF_ANIM_SPEED_NORMAL`` equals 1000, representing 1.0x (normal) speed
- Values greater than 1000 speed up animations (e.g., 2000 = 2.0x speed)
- Values less than 1000 slow down animations (e.g., 500 = 0.5x speed)
.. _gltf_example:
Examples
********
.. include:: ../../examples/libs/gltf/index.rst
.. _gltf_api:
API
***
.. API startswith: lv_gltf_
+1
View File
@@ -17,6 +17,7 @@
freetype
fs
gif
gltf
lfs
libjpeg_turbo
libpng
+1
View File
@@ -59,6 +59,7 @@ get_directory_property(HAS_PARENT_SCOPE PARENT_DIRECTORY)
# Set sources used for LVGL components
file(GLOB_RECURSE SOURCES ${LVGL_ROOT_DIR}/src/*.c
${LVGL_ROOT_DIR}/src/*.cpp
${LVGL_ROOT_DIR}/src/*.S)
file(GLOB_RECURSE EXAMPLE_SOURCES ${LVGL_ROOT_DIR}/examples/*.c)
file(GLOB_RECURSE DEMO_SOURCES ${LVGL_ROOT_DIR}/demos/*.c)
+12
View File
@@ -0,0 +1,12 @@
Open a GLTF from a file and make it spin forever like a platter
---------------------------------------------------------------
.. lv_example:: libs/gltf/lv_example_gltf_1
:language: c
Open a GLTF from a file and iterate through each camera
-------------------------------------------------------
.. lv_example:: libs/gltf/lv_example_gltf_2
:language: c
+41
View File
@@ -0,0 +1,41 @@
/**
* @file lv_example_gltf.h
*
*/
#ifndef LV_EXAMPLE_GLTF_H
#define LV_EXAMPLE_GLTF_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../lv_examples.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
void lv_example_gltf_1(void);
void lv_example_gltf_2(void);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_EXAMPLE_GLTF_H*/
+49
View File
@@ -0,0 +1,49 @@
#include "lv_example_gltf.h"
#if LV_BUILD_EXAMPLES
#if LV_USE_GLTF
/**
* Timer callback to continuously rotate the model
*/
static void spin_timer_cb(lv_timer_t * timer)
{
lv_obj_t * gltf = lv_timer_get_user_data(timer);
float yaw = lv_gltf_get_yaw(gltf);
yaw += 1.0f;
if(yaw >= 360.0f) {
yaw = 0.0f;
}
lv_gltf_set_yaw(gltf, yaw);
}
/**
* Open a GLTF from a file and make it spin forever like a platter
*/
void lv_example_gltf_1(void)
{
lv_obj_t * gltf = lv_gltf_create(lv_screen_active());
lv_gltf_model_t * model = lv_gltf_load_model_from_file(gltf,
"A:lvgl/examples/libs/gltf/webp_diffuse_transmission_plant.glb");
lv_obj_set_size(gltf, LV_PCT(100), LV_PCT(100));
lv_gltf_model_play_animation(model, 0);
lv_gltf_set_pitch(gltf, -45.f);
lv_timer_create(spin_timer_cb, LV_DEF_REFR_PERIOD, gltf);
}
#else
void lv_example_gltf_1(void)
{
/*TODO
*fallback for online examples*/
lv_obj_t * label = lv_label_create(lv_screen_active());
lv_label_set_text(label, "glTF web support is coming soon");
lv_obj_center(label);
}
#endif
#endif
+60
View File
@@ -0,0 +1,60 @@
#include "lv_example_gltf.h"
#if LV_BUILD_EXAMPLES
#if LV_USE_GLTF
static size_t camera_count;
static size_t current_camera;
static uint32_t current_speed = LV_GLTF_ANIM_SPEED_HALF;
/**
* Timer callback that updates the current camera and the current animation speed
*/
static void timer_cb(lv_timer_t * timer)
{
lv_obj_t * gltf = (lv_obj_t *) lv_timer_get_user_data(timer);
current_camera = (current_camera + 1) % (camera_count + 1);
current_speed *= 2;
if(current_speed > LV_GLTF_ANIM_SPEED_4X) {
current_speed = LV_GLTF_ANIM_SPEED_HALF;
}
LV_LOG_USER("Setting camera %zu and animation speed %" PRIu32, current_camera, current_speed);
lv_gltf_set_camera(gltf, current_camera);
lv_gltf_set_animation_speed(gltf, current_speed);
}
/**
* Open a GLTF from a file and loop through the model cameras and multiple animation speeds
*/
void lv_example_gltf_2(void)
{
lv_obj_t * gltf = lv_gltf_create(lv_screen_active());
lv_gltf_model_t * model = lv_gltf_load_model_from_file(gltf,
"A:lvgl/examples/libs/gltf/webp_diffuse_transmission_plant.glb");
lv_gltf_set_animation_speed(gltf, current_speed);
lv_gltf_model_play_animation(model, 0);
lv_obj_set_size(gltf, LV_PCT(100), LV_PCT(100));
camera_count = lv_gltf_get_camera_count(gltf);
lv_gltf_set_pitch(gltf, -45.f);
lv_timer_create(timer_cb, 5000, gltf);
}
#else
void lv_example_gltf_2(void)
{
/*TODO
*fallback for online examples*/
lv_obj_t * label = lv_label_create(lv_screen_active());
lv_label_set_text(label, "glTF web support is coming soon");
lv_obj_center(label);
}
#endif
#endif
+1
View File
@@ -18,6 +18,7 @@ extern "C" {
#include "ffmpeg/lv_example_ffmpeg.h"
#include "freetype/lv_example_freetype.h"
#include "gif/lv_example_gif.h"
#include "gltf/lv_example_gltf.h"
#include "lodepng/lv_example_lodepng.h"
#include "libpng/lv_example_libpng.h"
#include "qrcode/lv_example_qrcode.h"
+6
View File
@@ -986,6 +986,9 @@
/** Rlottie library */
#define LV_USE_RLOTTIE 0
/** Requires `LV_USE_3DTEXTURE = 1` */
#define LV_USE_GLTF 0
/** Enable Vector Graphic APIs
* - Requires `LV_USE_MATRIX = 1` */
#define LV_USE_VECTOR_GRAPHIC 0
@@ -1409,6 +1412,9 @@
/** Vector graphic demo */
#define LV_USE_DEMO_VECTOR_GRAPHIC 0
/** GLTF demo */
#define LV_USE_DEMO_GLTF 0
/*---------------------------
* Demos from lvgl/lv_demos
---------------------------*/
+2
View File
@@ -107,6 +107,8 @@ extern "C" {
#include "src/libs/fsdrv/lv_fsdrv.h"
#include "src/libs/lodepng/lv_lodepng.h"
#include "src/libs/libpng/lv_libpng.h"
#include "src/libs/gltf/gltf_data/lv_gltf_model.h"
#include "src/libs/gltf/gltf_view/lv_gltf.h"
#include "src/libs/gif/lv_gif.h"
#include "src/libs/qrcode/lv_qrcode.h"
#include "src/libs/tjpgd/lv_tjpgd.h"
+2
View File
@@ -41,6 +41,8 @@ void lv_draw_3d_dsc_init(lv_draw_3d_dsc_t * dsc)
lv_memzero(dsc, sizeof(lv_draw_3d_dsc_t));
dsc->base.dsc_size = sizeof(lv_draw_3d_dsc_t);
dsc->tex_id = LV_3DTEXTURE_ID_NULL;
dsc->h_flip = false;
dsc->v_flip = false;
dsc->opa = LV_OPA_COVER;
}
+2
View File
@@ -30,6 +30,8 @@ extern "C" {
typedef struct {
lv_draw_dsc_base_t base;
lv_3dtexture_id_t tex_id;
bool h_flip;
bool v_flip;
lv_opa_t opa;
} lv_draw_3d_dsc_t;
+19 -4
View File
@@ -416,7 +416,15 @@ static void blend_texture_layer(lv_draw_task_t * t)
lv_opengles_viewport(0, 0, targ_tex_w, targ_tex_h);
// TODO rotation
lv_opengles_render_texture(src_texture, &area, draw_dsc->opa, targ_tex_w, targ_tex_h, &t->clip_area, false);
bool h_flip = false;
bool v_flip = false;
if(t->type == LV_DRAW_TASK_TYPE_3D) {
lv_draw_3d_dsc_t * _3d_dsc = (lv_draw_3d_dsc_t *)t->draw_dsc;
h_flip = _3d_dsc->h_flip;
v_flip = _3d_dsc->v_flip;
}
lv_opengles_render_texture(src_texture, &area, draw_dsc->opa, targ_tex_w, targ_tex_h, &t->clip_area, h_flip,
v_flip);
GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, 0));
@@ -430,7 +438,13 @@ static void draw_from_cached_texture(lv_draw_task_t * t)
lv_draw_opengles_unit_t * u = (lv_draw_opengles_unit_t *)t->draw_unit;
cache_data_t data_to_find;
data_to_find.draw_dsc = (lv_draw_dsc_base_t *)t->draw_dsc;
bool h_flip = false;
bool v_flip = false;
if(t->type == LV_DRAW_TASK_TYPE_3D) {
lv_draw_3d_dsc_t * _3d_dsc = (lv_draw_3d_dsc_t *)t->draw_dsc;
h_flip = _3d_dsc->h_flip;
v_flip = _3d_dsc->v_flip;
}
data_to_find.w = lv_area_get_width(&t->_real_area);
data_to_find.h = lv_area_get_height(&t->_real_area);
data_to_find.texture = 0;
@@ -503,7 +517,7 @@ static void draw_from_cached_texture(lv_draw_task_t * t)
lv_area_move(&t->clip_area, -dest_layer->buf_area.x1, -dest_layer->buf_area.y1);
lv_area_t render_area = t->_real_area;
lv_area_move(&render_area, -dest_layer->buf_area.x1, -dest_layer->buf_area.y1);
lv_opengles_render_texture(texture, &render_area, 0xff, targ_tex_w, targ_tex_h, &t->clip_area, true);
lv_opengles_render_texture(texture, &render_area, 0xff, targ_tex_w, targ_tex_h, &t->clip_area, h_flip, v_flip);
GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, 0));
@@ -624,7 +638,8 @@ static void lv_draw_opengles_3d(lv_draw_task_t * t, const lv_draw_3d_dsc_t * dsc
lv_area_t clip_area = t->clip_area;
lv_area_move(&clip_area, -dest_layer->buf_area.x1, -dest_layer->buf_area.y1);
lv_opengles_render_texture(dsc->tex_id, coords, dsc->opa, targ_tex_w, targ_tex_h, &clip_area, true);
lv_opengles_render_texture(dsc->tex_id, coords, dsc->opa, targ_tex_w, targ_tex_h, &clip_area, dsc->h_flip,
!dsc->v_flip);
GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, 0));
GL_CALL(glBindTexture(GL_TEXTURE_2D, 0));
+46 -8
View File
@@ -10,6 +10,7 @@
#if LV_USE_OPENGLES
#include <stdlib.h>
#include "../../core/lv_refr.h"
#include "../../stdlib/lv_sprintf.h"
#include "../../stdlib/lv_string.h"
#include "../../core/lv_global.h"
#include "../../display/lv_display_private.h"
@@ -67,30 +68,41 @@ static lv_ll_t glfw_window_ll;
* GLOBAL FUNCTIONS
**********************/
lv_glfw_window_t * lv_glfw_window_create(int32_t hor_res, int32_t ver_res, bool use_mouse_indev)
lv_glfw_window_t * lv_glfw_window_create_ex(int32_t hor_res, int32_t ver_res, bool use_mouse_indev, bool h_flip,
bool v_flip, const char * title)
{
LV_ASSERT_NULL(title);
if(lv_glfw_init() != 0) {
LV_LOG_ERROR("Failed to init glfw");
return NULL;
}
lv_glfw_window_t * window = lv_ll_ins_tail(&glfw_window_ll);
LV_ASSERT_MALLOC(window);
if(window == NULL) return NULL;
if(window == NULL) {
LV_LOG_ERROR("Failed to create glfw window");
return NULL;
}
lv_memzero(window, sizeof(*window));
/* Create window with graphics context */
lv_glfw_window_t * existing_window = lv_ll_get_head(&glfw_window_ll);
window->window = glfwCreateWindow(hor_res, ver_res, "LVGL Simulator", NULL,
window->window = glfwCreateWindow(hor_res, ver_res, title, NULL,
existing_window ? existing_window->window : NULL);
if(window->window == NULL) {
LV_LOG_ERROR("glfwCreateWindow fail.");
LV_LOG_ERROR("glfwCreateWindow fail");
lv_ll_remove(&glfw_window_ll, window);
lv_free(window);
return NULL;
}
glfwSetWindowTitle(window->window, title);
window->h_flip = h_flip;
window->v_flip = v_flip;
window->hor_res = hor_res;
window->ver_res = ver_res;
lv_ll_init(&window->textures, sizeof(lv_glfw_texture_t));
window->use_indev = use_mouse_indev;
@@ -104,6 +116,16 @@ lv_glfw_window_t * lv_glfw_window_create(int32_t hor_res, int32_t ver_res, bool
return window;
}
lv_glfw_window_t * lv_glfw_window_create(int32_t hor_res, int32_t ver_res, bool use_mouse_indev)
{
return lv_glfw_window_create_ex(hor_res, ver_res, use_mouse_indev, false, false, "LVGL Simulator");
}
void lv_glfw_window_set_title(lv_glfw_window_t * window, const char * new_title)
{
glfwSetWindowTitle(window->window, new_title);
}
void lv_glfw_window_delete(lv_glfw_window_t * window)
{
glfwDestroyWindow(window->window);
@@ -127,6 +149,12 @@ void * lv_glfw_window_get_glfw_window(lv_glfw_window_t * window)
return (void *)(window->window);
}
void lv_glfw_window_set_flip(lv_glfw_window_t * window, bool h_flip, bool v_flip)
{
window->h_flip = h_flip;
window->v_flip = v_flip;
}
lv_glfw_texture_t * lv_glfw_window_add_texture(lv_glfw_window_t * window, unsigned int texture_id, int32_t w, int32_t h)
{
lv_glfw_texture_t * texture = lv_ll_ins_tail(&window->textures);
@@ -308,10 +336,10 @@ static void window_update_handler(lv_timer_t * t)
lv_area_t clip_area = texture->area;
#if LV_USE_DRAW_OPENGLES
lv_opengles_render_texture(texture->texture_id, &texture->area, texture->opa, window->hor_res, window->ver_res,
&clip_area, texture_disp == NULL);
&clip_area, window->h_flip, texture_disp == NULL ? window->v_flip : !window->v_flip);
#else
lv_opengles_render_texture(texture->texture_id, &texture->area, texture->opa, window->hor_res, window->ver_res,
&clip_area, true);
&clip_area, window->h_flip, window->v_flip);
#endif
}
@@ -371,8 +399,18 @@ static void proc_mouse(lv_glfw_window_t * window)
LV_LL_READ_BACK(&window->textures, texture) {
if(lv_area_is_point_on(&texture->area, &window->mouse_last_point, 0)) {
/* adjust the mouse pointer coordinates so that they are relative to the texture */
texture->indev_last_point.x = window->mouse_last_point.x - texture->area.x1;
texture->indev_last_point.y = window->mouse_last_point.y - texture->area.y1;
if(window->h_flip) {
texture->indev_last_point.x = texture->area.x2 - window->mouse_last_point.x;
}
else {
texture->indev_last_point.x = window->mouse_last_point.x - texture->area.x1;
}
if(window->v_flip) {
texture->indev_last_point.y = (texture->area.y2 - window->mouse_last_point.y);
}
else {
texture->indev_last_point.y = (window->mouse_last_point.y - texture->area.y1);
}
texture->indev_last_state = window->mouse_last_state;
lv_indev_read(texture->indev);
break;
+28
View File
@@ -41,12 +41,40 @@ extern "C" {
*/
lv_glfw_window_t * lv_glfw_window_create(int32_t hor_res, int32_t ver_res, bool use_mouse_indev);
/**
* Create a GLFW window with no textures and initialize OpenGL
* @param hor_res width in pixels of the window
* @param ver_res height in pixels of the window
* @param use_mouse_indev send pointer indev input to LVGL display textures
* @param h_flip Should the window contents be horizontally mirrored?
* @param v_flip Should the window contents be vertically mirrored?
* @param title The window title
* @return the new GLFW window handle
*/
lv_glfw_window_t * lv_glfw_window_create_ex(int32_t hor_res, int32_t ver_res, bool use_mouse_indev, bool h_flip,
bool v_flip, const char * title);
/**
* Set the window's title text
* @param window GLFW window to configure
* @param new_title The new title text
*/
void lv_glfw_window_set_title(lv_glfw_window_t * window, const char * new_title);
/**
* Delete a GLFW window. If it is the last one, the process will exit
* @param window GLFW window to delete
*/
void lv_glfw_window_delete(lv_glfw_window_t * window);
/**
* Set the horizontal / vertical flipping of a GLFW window
* @param window GLFW window to configure
* @param h_flip Should the window contents be horizontally mirrored?
* @param v_flip Should the window contents be vertically mirrored?
*/
void lv_glfw_window_set_flip(lv_glfw_window_t * window, bool h_flip, bool v_flip);
/**
* Get the GLFW window handle for an lv_glfw_window
* @param window GLFW window to return the handle of
@@ -36,6 +36,8 @@ struct _lv_glfw_window_t {
GLFWwindow * window;
int32_t hor_res;
int32_t ver_res;
bool h_flip;
bool v_flip;
lv_ll_t textures;
lv_point_t mouse_last_point;
lv_indev_state_t mouse_last_state;
+24 -18
View File
@@ -11,6 +11,7 @@
#if LV_USE_OPENGLES
#include "../../misc/lv_types.h"
#include "lv_opengles_debug.h"
#include "lv_opengles_driver.h"
@@ -28,7 +29,8 @@
* STATIC PROTOTYPES
**********************/
static void lv_opengles_render_internal(unsigned int texture, const lv_area_t * texture_area, lv_opa_t opa,
int32_t disp_w, int32_t disp_h, const lv_area_t * texture_clip_area, bool flip, lv_color_t fill_color);
int32_t disp_w, int32_t disp_h, const lv_area_t * texture_clip_area,
bool h_flip, bool v_flip, lv_color_t fill_color);
static void lv_opengles_enable_blending(void);
static void lv_opengles_vertex_buffer_init(const void * data, unsigned int size);
static void lv_opengles_vertex_buffer_deinit(void);
@@ -178,14 +180,15 @@ void lv_opengles_deinit(void)
}
void lv_opengles_render_texture(unsigned int texture, const lv_area_t * texture_area, lv_opa_t opa, int32_t disp_w,
int32_t disp_h, const lv_area_t * texture_clip_area, bool flip)
int32_t disp_h, const lv_area_t * texture_clip_area, bool h_flip, bool v_flip)
{
lv_opengles_render_internal(texture, texture_area, opa, disp_w, disp_h, texture_clip_area, flip, lv_color_black());
lv_opengles_render_internal(texture, texture_area, opa, disp_w, disp_h, texture_clip_area, h_flip, v_flip,
lv_color_black());
}
void lv_opengles_render_fill(lv_color_t color, const lv_area_t * area, lv_opa_t opa, int32_t disp_w, int32_t disp_h)
{
lv_opengles_render_internal(0, area, opa, disp_w, disp_h, area, false, color);
lv_opengles_render_internal(0, area, opa, disp_w, disp_h, area, false, false, color);
}
void lv_opengles_render_clear(void)
@@ -203,7 +206,7 @@ void lv_opengles_viewport(int32_t x, int32_t y, int32_t w, int32_t h)
**********************/
static void lv_opengles_render_internal(unsigned int texture, const lv_area_t * texture_area, lv_opa_t opa,
int32_t disp_w, int32_t disp_h, const lv_area_t * texture_clip_area, bool flip, lv_color_t fill_color)
int32_t disp_w, int32_t disp_h, const lv_area_t * texture_clip_area, bool h_flip, bool v_flip, lv_color_t fill_color)
{
lv_area_t intersection;
if(!lv_area_intersect(&intersection, texture_area, texture_clip_area)) return;
@@ -218,7 +221,8 @@ static void lv_opengles_render_internal(unsigned int texture, const lv_area_t *
float ver_scale = tex_h / (float)disp_h;
float hor_translate = (float)intersection.x1 / (float)disp_w * 2.0f - (1.0f - hor_scale);
float ver_translate = -((float)intersection.y1 / (float)disp_h * 2.0f - (1.0f - ver_scale));
if(flip) ver_scale = -ver_scale;
hor_scale = h_flip ? -hor_scale : hor_scale;
ver_scale = v_flip ? -ver_scale : ver_scale;
float matrix[9] = {
hor_scale, 0.0f, hor_translate,
0.0f, ver_scale, ver_translate,
@@ -228,20 +232,22 @@ static void lv_opengles_render_internal(unsigned int texture, const lv_area_t *
if(texture != 0) {
float x_coef = 1.0f / (float)(2 * lv_area_get_width(texture_area));
float y_coef = 1.0f / (float)(2 * lv_area_get_height(texture_area));
float tex_clip_x1 = lv_opengles_map_float(texture_clip_area->x1, texture_area->x1, texture_area->x2, x_coef,
1.0f - x_coef);
float tex_clip_x2 = lv_opengles_map_float(texture_clip_area->x2, texture_area->x1, texture_area->x2, x_coef,
1.0f - x_coef);
float tex_clip_y1 = lv_opengles_map_float(texture_clip_area->y1, texture_area->y1, texture_area->y2, y_coef,
1.0f - y_coef);
float tex_clip_y2 = lv_opengles_map_float(texture_clip_area->y2, texture_area->y1, texture_area->y2, y_coef,
1.0f - y_coef);
float ix_co = 1.0f - x_coef;
float iy_co = 1.0f - y_coef;
float clip_x1 = h_flip ? lv_opengles_map_float(texture_clip_area->x2, texture_area->x2, texture_area->x1, x_coef, ix_co)
: lv_opengles_map_float(texture_clip_area->x1, texture_area->x1, texture_area->x2, x_coef, ix_co);
float clip_x2 = h_flip ? lv_opengles_map_float(texture_clip_area->x1, texture_area->x2, texture_area->x1, x_coef, ix_co)
: lv_opengles_map_float(texture_clip_area->x2, texture_area->x1, texture_area->x2, x_coef, ix_co);
float clip_y1 = v_flip ? lv_opengles_map_float(texture_clip_area->y1, texture_area->y1, texture_area->y2, y_coef, iy_co)
: lv_opengles_map_float(texture_clip_area->y2, texture_area->y2, texture_area->y1, y_coef, iy_co);
float clip_y2 = v_flip ? lv_opengles_map_float(texture_clip_area->y2, texture_area->y1, texture_area->y2, y_coef, iy_co)
: lv_opengles_map_float(texture_clip_area->y1, texture_area->y2, texture_area->y1, y_coef, iy_co);
float positions[LV_OPENGLES_VERTEX_BUFFER_LEN] = {
-1.0f, 1.0f, tex_clip_x1, tex_clip_y2,
1.0f, 1.0f, tex_clip_x2, tex_clip_y2,
1.0f, -1.0f, tex_clip_x2, tex_clip_y1,
-1.0f, -1.0f, tex_clip_x1, tex_clip_y1
-1.f, 1.0f, clip_x1, clip_y2,
1.0f, 1.0f, clip_x2, clip_y2,
1.0f, -1.0f, clip_x2, clip_y1,
-1.f, -1.0f, clip_x1, clip_y1
};
lv_opengles_vertex_buffer_init(positions, sizeof(positions));
}
+3 -1
View File
@@ -51,9 +51,11 @@ void lv_opengles_deinit(void);
* @param opa opacity to blend the texture with existing contents
* @param disp_w width of the window/framebuffer being rendered to
* @param disp_h height of the window/framebuffer being rendered to
* @param h_flip horizontal flip
* @param v_flip vertical flip
*/
void lv_opengles_render_texture(unsigned int texture, const lv_area_t * texture_area, lv_opa_t opa, int32_t disp_w,
int32_t disp_h, const lv_area_t * texture_clip_area, bool flip);
int32_t disp_h, const lv_area_t * texture_clip_area, bool h_flip, bool v_flip);
/**
* Render a fill
+124
View File
@@ -0,0 +1,124 @@
/**
* @file lv_fastgltf.hpp
*
*/
/*********************
* INCLUDES
*********************/
#include "../../../lv_conf_internal.h"
#if LV_USE_GLTF
#include <fastgltf/math.hpp>
#include <fastgltf/util.hpp>
#include <functional>
#include <string>
namespace fastgltf
{
FASTGLTF_EXPORT template <typename AssetType, typename Callback>
#if FASTGLTF_HAS_CONCEPTS
requires std::same_as<std::remove_cvref_t<AssetType>, Asset> &&
std::is_invocable_v<Callback, fastgltf::Node &, std::string &,
std::string &, std::size_t, std::size_t>
#endif
void namegen_iterate_scene_nodes(AssetType &&asset,
std::size_t sceneIndex,
Callback callback)
{
auto &scene = asset.scenes[sceneIndex];
std::string _id = std::string("");
std::string _ip = std::string("");
if (asset.scenes.size() > 1) {
_id = "scene_" + std::to_string(sceneIndex);
_ip = std::to_string(sceneIndex);
}
auto function = [&](std::size_t nodeIndex, std::string &parentId,
std::string &parentIp, std::size_t __child_index,
auto &self) -> void {
assert(asset.nodes.size() > nodeIndex);
auto &node = asset.nodes[nodeIndex];
std::string _nodeId =
parentId + std::string("/") + std::string(node.name);
std::string _nodeIp = parentIp + std::string(".") +
std::to_string(__child_index);
std::invoke(callback, node, _nodeId, _nodeIp, nodeIndex,
__child_index);
std::size_t ____child_index = 0;
for (auto &child : node.children) {
self(child, _nodeId, _nodeIp, ____child_index, self);
____child_index += 1;
}
};
std::size_t child_index = 0;
for (auto &sceneNode : scene.nodeIndices) {
function(sceneNode, _id, _ip, child_index, function);
child_index += 1;
}
}
FASTGLTF_EXPORT template <typename AssetType, typename Callback>
#if FASTGLTF_HAS_CONCEPTS
requires std::same_as<std::remove_cvref_t<AssetType>, Asset> &&
std::is_invocable_v<Callback, fastgltf::Node &, fastgltf::math::fmat4x4 &, fastgltf::math::fmat4x4 &>
#endif
void
findlight_iterate_scene_nodes(AssetType &&asset, std::size_t sceneIndex,
math::fmat4x4 *initial, Callback callback)
{
auto &scene = asset.scenes[sceneIndex];
auto function = [&](std::size_t nodeIndex,
math::fmat4x4 &parentWorldMatrix,
auto &self) -> void {
assert(asset.nodes.size() > nodeIndex);
auto &node = asset.nodes[nodeIndex];
auto _localMat = getTransformMatrix(node, math::fmat4x4());
std::invoke(callback, node, parentWorldMatrix, _localMat);
for (auto &child : node.children) {
math::fmat4x4 _parentWorldTemp =
parentWorldMatrix * _localMat;
self(child, _parentWorldTemp, self);
}
};
for (auto &sceneNode : scene.nodeIndices) {
auto tmat2 = fastgltf::math::fmat4x4(*initial);
function(sceneNode, tmat2, function);
}
}
FASTGLTF_EXPORT template <typename AssetType, typename Callback>
#if FASTGLTF_HAS_CONCEPTS
requires std::same_as<std::remove_cvref_t<AssetType>, Asset>
&& std::is_invocable_v<Callback, fastgltf::Node &, fastgltf::math::fmat4x4 &, fastgltf::math::fmat4x4 &>
#endif
inline void custom_iterate_scene_nodes(AssetType&& asset, std::size_t sceneIndex, math::fmat4x4 * initial,
Callback callback)
{
auto & scene = asset.scenes[sceneIndex];
auto & nodes = asset.nodes;
auto function = [&](std::size_t nodeIndex, math::fmat4x4 & parentWorldMatrix, auto & self) -> void {
//assert(asset.nodes.size() > nodeIndex);
auto & node = nodes[nodeIndex];
auto _localMat = getTransformMatrix(node, math::fmat4x4());
std::invoke(callback, node, parentWorldMatrix, _localMat);
uint32_t num_children = node.children.size();
if(num_children > 0) {
math::fmat4x4 _parentWorldTemp = parentWorldMatrix * _localMat;
if(num_children > 1) {
math::fmat4x4 per_child_copy = math::fmat4x4(_parentWorldTemp);
for(auto & child : node.children) self(child, per_child_copy, self);
}
else {
self(node.children[0], _parentWorldTemp, self);
}
}
};
// auto tempmat = fastgltf::math::fmat4x4(*initial);
//for (auto& sceneNode : scene.nodeIndices) function(sceneNode, &tempmat, function);
for(auto & sceneNode : scene.nodeIndices) function(sceneNode, *initial, function);
}
}
#endif /*LV_USE_GLTF*/
+155
View File
@@ -0,0 +1,155 @@
#include "lv_gltf_data_internal.hpp"
#if LV_USE_GLTF
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <cstdint>
#include <lvgl.h>
#include <fastgltf/core.hpp>
#include <fastgltf/types.hpp>
#include <fastgltf/tools.hpp>
#include "lv_gltf_bind.h"
static uint32_t bind_count = 0;
void lv_gltf_bind_set(lv_gltf_bind_t * bind, uint8_t channel, float data)
{
LV_ASSERT_NULL(bind);
LV_ASSERT(channel < LV_GLTF_BIND_MAX_CHANNELS);
if(data != bind->data[channel]) {
bind->data[channel] = data;
bind->dirty = true;
}
}
float lv_gltf_bind_get(lv_gltf_bind_t * bind, uint8_t channel)
{
LV_ASSERT_NULL(bind);
LV_ASSERT(channel < LV_GLTF_BIND_MAX_CHANNELS);
return bind->data[channel];
}
void lv_gltf_bind_bind_clean(lv_gltf_bind_t * bind)
{
bind->dirty = false;
}
lv_gltf_bind_t * add_by_node(lv_gltf_model_t * gltf_data, fastgltf::Node * node, lv_gltf_bind_prop_t which_prop,
uint32_t data_mask,
lv_gltf_bind_dir_t dir)
{
if(node == nullptr) {
return nullptr;
}
lv_gltf_bind_t new_bind;
new_bind.id = bind_count++;
new_bind.prop = which_prop;
new_bind.data_mask = data_mask;
new_bind.data[0] = new_bind.data[1] = new_bind.data[2] = which_prop == LV_GLTF_BIND_PROP_SCALE ? 1.f : 0.f;
new_bind.dir = dir;
new_bind.dirty = true;
new_bind.next_bind = nullptr;
// Check if an bind already exists for this node
if(gltf_data->node_binds.find(node) != gltf_data->node_binds.end()) {
// Get the existing bind
lv_gltf_bind_t * existingbind = gltf_data->node_binds[node];
// Traverse to the end of the linked list of binds
while(existingbind->next_bind != nullptr)
existingbind = existingbind->next_bind;
lv_array_push_back(&gltf_data->binds, &new_bind);
existingbind->next_bind = (lv_gltf_bind_t *)lv_array_at(&gltf_data->binds, lv_array_size(&gltf_data->binds) - 1);
return existingbind->next_bind;
}
else {
// No existing bind, insert the new one
lv_array_push_back(&gltf_data->binds, &new_bind);
gltf_data->node_binds[node] = (lv_gltf_bind_t *)lv_array_at(&gltf_data->binds, lv_array_size(&gltf_data->binds) - 1);
return gltf_data->node_binds[node];
}
return nullptr;
}
lv_gltf_bind_t * lv_gltf_bind_add_by_index(lv_gltf_model_t * data, size_t index, lv_gltf_bind_prop_t which_prop,
uint32_t data_mask,
lv_gltf_bind_dir_t dir)
{
lv_gltf_data_node_t * node = lv_gltf_data_node_get_by_index(data, index);
if(!node) {
return nullptr;
}
return add_by_node(data, node->node, which_prop, data_mask, dir);
}
lv_gltf_bind_t * lv_gltf_bind_add_by_ip(lv_gltf_model_t * data, const char * ip, lv_gltf_bind_prop_t which_prop,
uint32_t data_mask,
lv_gltf_bind_dir_t dir)
{
lv_gltf_data_node_t * node = lv_gltf_data_node_get_by_ip(data, ip);
if(!node) {
return nullptr;
}
return add_by_node(data, node->node, which_prop, data_mask, dir);
}
lv_gltf_bind_t * lv_gltf_bind_add_by_path(lv_gltf_model_t * data, const char * path, lv_gltf_bind_prop_t which_prop,
uint32_t data_mask, lv_gltf_bind_dir_t dir)
{
lv_gltf_data_node_t * node = lv_gltf_data_node_get_by_path(data, path);
if(!node) {
return nullptr;
}
return add_by_node(data, node->node, which_prop, data_mask, dir);
}
lv_result_t lv_gltf_bind_remove(lv_gltf_model_t * gltf_data, lv_gltf_bind_t * bind_to_remove)
{
for(auto pair : gltf_data->node_binds) {
lv_gltf_bind_t * currentbind = pair.second;
lv_gltf_bind_t * previousbind = nullptr;
while(currentbind != nullptr) {
if(currentbind->id == bind_to_remove->id) {
// Found the bind to remove
if(previousbind != nullptr) {
// Link the previous bind to the next one
previousbind->next_bind = currentbind->next_bind;
}
else {
gltf_data->node_binds[pair.first] = currentbind->next_bind;
}
for(uint32_t i = 0; i < lv_array_size(&gltf_data->binds); ++i) {
const lv_gltf_bind_t * current_entry = (const lv_gltf_bind_t *)lv_array_at(&gltf_data->binds, i);
if(current_entry->id == bind_to_remove->id) {
lv_array_remove(&gltf_data->binds, i);
}
}
return LV_RESULT_OK;
}
previousbind = currentbind;
if(currentbind != nullptr) {
if(currentbind->next_bind != nullptr) {
currentbind = currentbind->next_bind;
}
else {
currentbind = nullptr;
}
}
}
}
return LV_RESULT_INVALID;
}
#endif /*LV_USE_GLTF*/
+106
View File
@@ -0,0 +1,106 @@
#ifndef LV_GLTF_BIND_H
#define LV_GLTF_BIND_H
#include "lv_gltf_model.h"
#if LV_USE_GLTF
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
LV_GLTF_BIND_PROP_VISIBILITY,
LV_GLTF_BIND_PROP_POSITION,
LV_GLTF_BIND_PROP_ROTATION,
LV_GLTF_BIND_PROP_SCALE,
LV_GLTF_BIND_PROP_BASE_COLOR,
LV_GLTF_BIND_PROP_ALPHA_FACTOR,
LV_GLTF_BIND_PROP_EMIS_COLOR,
LV_GLTF_BIND_PROP_WORLD_POSITION
} lv_gltf_bind_prop_t;
#define LV_GLTF_BIND_MAX_CHANNELS 4
#define LV_GLTF_BIND_CHANNEL_0 (0x01)
#define LV_GLTF_BIND_CHANNEL_1 (0x02)
#define LV_GLTF_BIND_CHANNEL_2 (0x04)
#define LV_GLTF_BIND_CHANNEL_3 (0x08)
typedef enum { LV_GLTF_BIND_DIR_READ, LV_GLTF_BIND_DIR_WRITE } lv_gltf_bind_dir_t;
struct _lv_gltf_bind {
struct _lv_gltf_bind * next_bind;
lv_gltf_bind_prop_t prop;
lv_gltf_bind_dir_t dir;
uint32_t id;
uint32_t data_mask;
float data[LV_GLTF_BIND_MAX_CHANNELS];
bool dirty;
};
typedef struct _lv_gltf_bind lv_gltf_bind_t;
void lv_gltf_bind_set(lv_gltf_bind_t * bind, uint8_t channel, float data);
float lv_gltf_bind_get(lv_gltf_bind_t * bind, uint8_t channel);
/**
* @brief Reset the dirty flag for a given bind.
*
* @param bind Pointer to the lv_gltf_bind_t to reset the dirty flag for.
*/
void lv_gltf_bind_bind_clean(lv_gltf_bind_t * bind);
/**
* @brief Add an bind to a GLTF data object by node index.
*
* @param gltf_data Pointer to the lv_gltf_data_t object to which the bind will be added.
* @param nodeIndex The index of the node to bind.
* @param which_prop The property to bind.
* @param data_mask A mask indicating which data fields to bind.
* @return Pointer to the newly created lv_gltf_bind_t object, or NULL if the operation failed.
*/
lv_gltf_bind_t * lv_gltf_bind_add_by_index(lv_gltf_model_t * data, size_t index, lv_gltf_bind_prop_t which_prop,
uint32_t data_mask,
lv_gltf_bind_dir_t dir);
/**
* @brief Add an bind to a GLTF data object by node IP address.
*
* @param gltf_data Pointer to the lv_gltf_data_t object to which the bind will be added.
* @param nodeIp The IP address of the node to bind.
* @param which_prop The property to bind.
* @param data_mask A mask indicating which data fields to bind.
* @return Pointer to the newly created lv_gltf_bind_t object, or NULL if the operation failed.
*/
lv_gltf_bind_t * lv_gltf_bind_add_by_ip(lv_gltf_model_t * data, const char * node_ip, lv_gltf_bind_prop_t which_prop,
uint32_t data_mask, lv_gltf_bind_dir_t dir);
/**
* @brief Add an bind to a GLTF data object by node ID.
*
* @param gltf_data Pointer to the lv_gltf_data_t object to which the bind will be added.
* @param nodeId The ID of the node to bind.
* @param which_prop The property to bind.
* @param data_mask A mask indicating which data fields to bind.
* @return Pointer to the newly created lv_gltf_bind_t object, or NULL if the operation failed.
*/
lv_gltf_bind_t * lv_gltf_bind_add_by_path(lv_gltf_model_t * data, const char * path, lv_gltf_bind_prop_t which_prop,
uint32_t data_mask, lv_gltf_bind_dir_t dir);
/**
* @brief Remove an bind from a GLTF data object.
*
* @param gltf_data Pointer to the lv_gltf_data_t object from which the bind will be removed.
* @param bind The bind to be removed.
* @param which_prop The property to bind.
* @param data_mask A mask indicating which data fields to bind.
* @return True on success, False on failure.
*/
lv_result_t lv_gltf_bind_remove(lv_gltf_model_t * _data, lv_gltf_bind_t * bind);
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_USE_GLTF*/
#endif /*LV_GLTF_BIND_H*/
+271
View File
@@ -0,0 +1,271 @@
/**
* @file lv_gltf_data.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_gltf_data_internal.hpp"
#if LV_USE_GLTF
#include <fastgltf/tools.hpp>
#include "../../../misc/lv_assert.h"
#include "../../../core/lv_obj_pos.h"
#include "../../../misc/lv_timer.h"
#include "../gltf_view/lv_gltf_view_internal.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void update_animation_cb(lv_timer_t * timer);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_gltf_model_t * lv_gltf_data_create_internal(const char * gltf_path,
fastgltf::Asset asset)
{
lv_gltf_model_t * data = (lv_gltf_model_t *)lv_zalloc(sizeof(*data));
LV_ASSERT_MALLOC(data);
new(data) lv_gltf_model_t;
new(&data->asset) fastgltf::Asset(std::move(asset));
data->filename = gltf_path;
data->last_camera_index = -5;
data->last_anim_num = -5;
data->current_animation_max_time = 0;
data->local_timestamp = 0.0f;
data->last_material_index = 99999;
data->last_frame_was_antialiased = false;
data->last_frame_no_motion = false;
data->_last_frame_no_motion = false;
data->animation_update_timer = lv_timer_create(update_animation_cb, LV_DEF_REFR_PERIOD, data);
lv_timer_pause(data->animation_update_timer);
LV_ASSERT_NULL(data->animation_update_timer);
new(&data->node_binds) NodeOverrideMap();
new(&data->node_transform_cache) NodeTransformMap();
new(&data->opaque_nodes_by_material_index) MaterialIndexMap();
new(&data->blended_nodes_by_material_index) MaterialIndexMap();
new(&data->validated_skins) LongVector();
new(&data->skin_tex) IntVector();
new(&data->local_mesh_to_center_points_by_primitive)
NodePrimCenterMap();
new(&data->node_by_light_index) NodeVector();
new(&data->meshes) std::vector<lv_gltf_mesh_data_t>();
new(&data->textures) std::vector<GLuint>();
lv_array_init(&data->binds, 0, sizeof(lv_gltf_bind_t));
lv_array_init(&data->compiled_shaders, 1, sizeof(lv_gltf_compiled_shader_t));
return data;
}
void lv_gltf_data_destroy(lv_gltf_model_t * data)
{
lv_gltf_data_destroy_textures(data);
lv_free(data);
}
const char * lv_gltf_get_filename(const lv_gltf_model_t * data)
{
LV_ASSERT_NULL(data);
return data->filename;
}
size_t lv_gltf_model_get_image_count(const lv_gltf_model_t * data)
{
LV_ASSERT_NULL(data);
return data->asset.images.size();
}
size_t lv_gltf_model_get_texture_count(const lv_gltf_model_t * data)
{
LV_ASSERT_NULL(data);
return data->asset.textures.size();
}
GLuint lv_gltf_data_get_texture(lv_gltf_model_t * data, size_t index)
{
LV_ASSERT_NULL(data);
LV_ASSERT(index < data->textures.size());
return data->textures[index];
}
size_t lv_gltf_model_get_material_count(const lv_gltf_model_t * data)
{
LV_ASSERT_NULL(data);
return data->asset.materials.size();
}
size_t lv_gltf_model_get_camera_count(const lv_gltf_model_t * data)
{
LV_ASSERT_NULL(data);
return data->asset.cameras.size();
}
size_t lv_gltf_model_get_node_count(const lv_gltf_model_t * data)
{
LV_ASSERT_NULL(data);
return data->asset.nodes.size();
}
size_t lv_gltf_model_get_mesh_count(const lv_gltf_model_t * data)
{
LV_ASSERT_NULL(data);
return data->asset.meshes.size();
}
size_t lv_gltf_model_get_scene_count(const lv_gltf_model_t * data)
{
LV_ASSERT_NULL(data);
return data->asset.scenes.size();
}
size_t lv_gltf_model_get_animation_count(const lv_gltf_model_t * data)
{
LV_ASSERT_NULL(data);
return data->asset.animations.size();
}
lv_result_t lv_gltf_model_play_animation(lv_gltf_model_t * model, size_t index)
{
LV_ASSERT_NULL(model);
if(index >= model->asset.animations.size()) {
return LV_RESULT_INVALID;
}
if(lv_timer_get_paused(model->animation_update_timer)) {
model->last_tick = lv_tick_get();
lv_timer_resume(model->animation_update_timer);
}
model->current_animation_max_time = lv_gltf_data_get_animation_total_time(model, index);
model->current_animation = index;
model->is_animation_enabled = true;
return LV_RESULT_OK;
}
void lv_gltf_model_pause_animation(lv_gltf_model_t * model)
{
LV_ASSERT_NULL(model);
model->is_animation_enabled = false;
lv_timer_pause(model->animation_update_timer);
}
bool lv_gltf_model_is_animation_paused(lv_gltf_model_t * model)
{
LV_ASSERT_NULL(model);
return !model->is_animation_enabled;
}
size_t lv_gltf_model_get_animation(lv_gltf_model_t * model)
{
LV_ASSERT_NULL(model);
return model->current_animation;
}
lv_gltf_model_t *
lv_gltf_data_load_from_file(const char * file_path,
lv_opengl_shader_manager_t * shader_manager)
{
return lv_gltf_data_load_internal(file_path, 0, shader_manager);
}
lv_gltf_model_t *
lv_gltf_data_load_from_bytes(const uint8_t * data, size_t data_size,
lv_opengl_shader_manager_t * shader_manager)
{
return lv_gltf_data_load_internal(data, data_size, shader_manager);
}
fastgltf::Asset * lv_gltf_data_get_asset(lv_gltf_model_t * data)
{
LV_ASSERT_NULL(data);
return &data->asset;
}
double lv_gltf_data_get_radius(lv_gltf_model_t * data)
{
LV_ASSERT_NULL(data);
return data->bound_radius;
}
fastgltf::math::fvec3 lv_gltf_data_get_center(const lv_gltf_model_t * data)
{
LV_ASSERT_NULL(data);
return data->vertex_cen;
}
fastgltf::math::fvec3 lv_gltf_data_get_bounds_min(const lv_gltf_model_t * data)
{
LV_ASSERT_NULL(data);
return data->vertex_min;
}
fastgltf::math::fvec3 lv_gltf_data_get_bounds_max(const lv_gltf_model_t * data)
{
LV_ASSERT_NULL(data);
return data->vertex_max;
}
void lv_gltf_data_copy_bounds_info(lv_gltf_model_t * to, lv_gltf_model_t * from)
{
{
to->vertex_min[0] = from->vertex_min[0];
to->vertex_min[1] = from->vertex_min[1];
to->vertex_min[2] = from->vertex_min[2];
}
{
to->vertex_max[0] = from->vertex_max[0];
to->vertex_max[1] = from->vertex_max[1];
to->vertex_max[2] = from->vertex_max[2];
}
{
to->vertex_cen[0] = from->vertex_cen[0];
to->vertex_cen[1] = from->vertex_cen[1];
to->vertex_cen[2] = from->vertex_cen[2];
}
to->bound_radius = from->bound_radius;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void update_animation_cb(lv_timer_t * timer)
{
lv_gltf_model_t * model = (lv_gltf_model_t *)lv_timer_get_user_data(timer);
const uint32_t current_tick = lv_tick_get();
const uint32_t delta = lv_tick_diff(current_tick, model->last_tick);
model->last_tick = current_tick;
model->local_timestamp += (delta * model->viewer->desc.animation_speed_ratio) / 1000;
if(model->local_timestamp >= model->current_animation_max_time) {
model->local_timestamp = 50;
}
lv_obj_invalidate((lv_obj_t *)model->viewer);
}
#endif /*LV_USE_GLTF*/
@@ -0,0 +1,244 @@
/**
* @file lv_gltf_data_animations.cpp
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_gltf_data_internal.hpp"
#if LV_USE_GLTF
#include <fastgltf/tools.hpp>
/*********************
* DEFINES
*********************/
#define TIME_LOC_PREPASS_COUNT 16
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
#include "fastgltf/math.hpp"
#include "lv_gltf_data_internal.hpp"
static fastgltf::math::fvec3 animation_get_vec3_at_timestamp(lv_gltf_model_t * data,
fastgltf::AnimationSampler * sampler,
float seconds);
static fastgltf::math::fquat animation_get_quat_at_timestamp(lv_gltf_model_t * data,
fastgltf::AnimationSampler * sampler,
float _seconds);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
uint32_t lv_gltf_data_get_animation_total_time(lv_gltf_model_t * data, uint32_t index)
{
LV_ASSERT(data->asset.animations.size() > index);
auto & animation = data->asset.animations[index];
float max_time = -1.0f;
for(uint64_t i = 0; i < animation.channels.size(); i++) {
auto & accessor = data->asset.accessors[animation.samplers[i].inputAccessor];
max_time = std::max(max_time, fastgltf::getAccessorElement<float>(data->asset, accessor, accessor.count - 1));
}
return (uint32_t)(max_time * 1000);
}
std::vector<uint32_t> * lv_gltf_data_animation_get_channel_set(std::size_t anim_num, lv_gltf_model_t * data,
fastgltf::Node & node)
{
const auto & asset = lv_gltf_data_get_asset(data);
size_t animation_count = lv_gltf_model_get_animation_count(data);
if(data->channel_set_cache.find(&node) == data->channel_set_cache.end()) {
std::vector<uint32_t> new_cache = std::vector<uint32_t>();
if(animation_count > anim_num) {
auto & anim = asset->animations[anim_num];
for(uint64_t c = 0; c < anim.channels.size(); c++) {
auto & channel = anim.channels[c];
if(&(asset->nodes[channel.nodeIndex.value()]) == &node) {
new_cache.push_back(c);
}
}
}
data->channel_set_cache[&node] = new_cache;
}
return &data->channel_set_cache[&node];
}
void lv_gltf_data_animation_matrix_apply(float timestamp, std::size_t anim_num, lv_gltf_model_t * gltf_data,
fastgltf::Node & node,
fastgltf::math::fmat4x4 & matrix)
{
const auto & asset = lv_gltf_data_get_asset(gltf_data);
size_t animation_count = lv_gltf_model_get_animation_count(gltf_data);
auto _channel_set = lv_gltf_data_animation_get_channel_set(anim_num, gltf_data, node);
if(_channel_set->size() == 0) {
return;
}
if(animation_count > anim_num) {
auto & anim = asset->animations[anim_num];
fastgltf::math::fvec3 newPos, newScale;
fastgltf::math::fmat3x3 rotmat;
for(const auto & c : (*_channel_set)) {
switch(anim.channels[c].path) {
case fastgltf::AnimationPath::Translation:
newPos = animation_get_vec3_at_timestamp(gltf_data, &anim.samplers[c], timestamp);
matrix[3][0] = newPos[0];
matrix[3][1] = newPos[1];
matrix[3][2] = newPos[2];
break;
case fastgltf::AnimationPath::Rotation:
rotmat = fastgltf::math::asMatrix(animation_get_quat_at_timestamp(gltf_data, &anim.samplers[c], timestamp));
matrix[0][0] = rotmat[0][0];
matrix[0][1] = rotmat[0][1];
matrix[0][2] = rotmat[0][2];
matrix[1][0] = rotmat[1][0];
matrix[1][1] = rotmat[1][1];
matrix[1][2] = rotmat[1][2];
matrix[2][0] = rotmat[2][0];
matrix[2][1] = rotmat[2][1];
matrix[2][2] = rotmat[2][2];
break;
case fastgltf::AnimationPath::Scale:
newScale = animation_get_vec3_at_timestamp(gltf_data, &anim.samplers[c], timestamp);
for(int32_t rs = 0; rs < 3; ++rs) {
matrix[0][rs] *= newScale[0];
matrix[1][rs] *= newScale[1];
matrix[2][rs] *= newScale[2];
}
break;
case fastgltf::AnimationPath::Weights:
LV_LOG_WARN("Unhandled weights animation");
break;
}
}
}
}
/**********************
* STATIC FUNCTIONS
**********************/
static fastgltf::math::fquat animation_get_quat_at_timestamp(lv_gltf_model_t * data,
fastgltf::AnimationSampler * sampler,
float _seconds)
{
const auto & asset = lv_gltf_data_get_asset(data);
auto & _inAcc = asset->accessors[sampler->inputAccessor];
auto & _outAcc = asset->accessors[sampler->outputAccessor];
std::size_t _inAccCount = _inAcc.count;
float _maxTime = fastgltf::getAccessorElement<float>(*asset, _inAcc, _inAccCount - 1);
std::size_t _lowerIndex = 0;
float _lowerTimestamp = 0.0f;
if(_seconds < 0.001f) {
_lowerIndex = 0;
}
else {
std::size_t _firstCheckOffset = 0;
std::size_t _lastCheckOffset = _inAccCount;
std::size_t _prepassLeft = TIME_LOC_PREPASS_COUNT;
while(_prepassLeft > 0) {
_prepassLeft -= 1;
if(_seconds >= fastgltf::getAccessorElement<float>(*asset, _inAcc, (_firstCheckOffset + _lastCheckOffset) >> 1)) {
_firstCheckOffset = (_firstCheckOffset + _lastCheckOffset) >> 1;
}
else {
_lastCheckOffset = (_firstCheckOffset + _lastCheckOffset) >> 1;
if(_lastCheckOffset <= _firstCheckOffset + 1) {
_prepassLeft = 0;
}
}
}
for(uint64_t ii = _firstCheckOffset; ii < _inAccCount; ii++) {
float _stampTime = fastgltf::getAccessorElement<float>(*asset, _inAcc, ii);
if(_stampTime > _seconds) {
_lowerIndex = ii - 1;
break;
}
_lowerTimestamp = _stampTime;
}
}
fastgltf::math::fquat _lowerValue = fastgltf::getAccessorElement<fastgltf::math::fquat>(*asset, _outAcc, _lowerIndex);
if(_seconds >= _maxTime || _seconds <= 0.0f) {
return _lowerValue;
}
std::size_t _upperIndex = _lowerIndex + 1;
float _linDist = fastgltf::getAccessorElement<float>(*asset, _inAcc, _upperIndex) - _lowerTimestamp;
return fastgltf::math::slerp(_lowerValue, fastgltf::getAccessorElement<fastgltf::math::fquat>(*asset, _outAcc,
_upperIndex), (_seconds - _lowerTimestamp) / _linDist);
}
fastgltf::math::fvec3 animation_get_vec3_at_timestamp(lv_gltf_model_t * data, fastgltf::AnimationSampler * sampler,
float _seconds)
{
const auto & asset = lv_gltf_data_get_asset(data);
auto & _inAcc = asset->accessors[sampler->inputAccessor];
auto & _outAcc = asset->accessors[sampler->outputAccessor];
std::size_t _inAccCount = _inAcc.count;
float _maxTime = fastgltf::getAccessorElement<float>(*asset, _inAcc, _inAccCount - 1);
std::size_t _lowerIndex = 0;
float _lowerTimestamp = 0.0f;
if(_seconds < 0.001f) {
_lowerIndex = 0;
}
else {
std::size_t _firstCheckOffset = 0;
std::size_t _lastCheckOffset = _inAccCount;
std::size_t _prepassLeft = TIME_LOC_PREPASS_COUNT;
while(_prepassLeft > 0) {
_prepassLeft -= 1;
if(_seconds >= fastgltf::getAccessorElement<float>(*asset, _inAcc, (_firstCheckOffset + _lastCheckOffset) >> 1)) {
_firstCheckOffset = (_firstCheckOffset + _lastCheckOffset) >> 1;
}
else {
_lastCheckOffset = (_firstCheckOffset + _lastCheckOffset) >> 1;
if(_lastCheckOffset <= _firstCheckOffset + 1) {
_prepassLeft = 0;
}
}
}
for(uint64_t ii = _firstCheckOffset; ii < _inAccCount; ii++) {
float _stampTime = fastgltf::getAccessorElement<float>(*asset, _inAcc, ii);
if(_stampTime > _seconds) {
_lowerIndex = ii - 1;
break;
}
_lowerTimestamp = _stampTime;
}
}
fastgltf::math::fvec3 _lowerValue = fastgltf::getAccessorElement<fastgltf::math::fvec3>(*asset, _outAcc, _lowerIndex);
if(_seconds >= _maxTime || _seconds <= 0.0f) {
return _lowerValue;
}
std::size_t _upperIndex = _lowerIndex + 1;
fastgltf::math::fvec3 _upperValue = fastgltf::getAccessorElement<fastgltf::math::fvec3>(*asset, _outAcc, _upperIndex);
float _upperTimestamp = fastgltf::getAccessorElement<float>(*asset, _inAcc, _upperIndex);
return fastgltf::math::lerp(_lowerValue, _upperValue,
(_seconds - _lowerTimestamp) / (_upperTimestamp - _lowerTimestamp));
}
#endif /*LV_USE_GLTF*/
@@ -0,0 +1,103 @@
/**
* @file lv_gltf_data_cache.cpp
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_gltf_data_internal.hpp"
#if LV_USE_GLTF
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
fastgltf::math::fmat4x4 lv_gltf_data_get_cached_transform(lv_gltf_model_t * data,
fastgltf::Node * node)
{
return data->node_transform_cache[node];
}
bool lv_gltf_data_has_cached_transform(lv_gltf_model_t * data, fastgltf::Node * node)
{
return (data->node_transform_cache.find(node) !=
data->node_transform_cache.end());
}
void lv_gltf_data_set_cached_transform(lv_gltf_model_t * data, fastgltf::Node * node,
fastgltf::math::fmat4x4 M)
{
data->node_transform_cache[node] = M;
}
void lv_gltf_data_clear_transform_cache(lv_gltf_model_t * data)
{
data->node_transform_cache.clear();
}
bool lv_gltf_data_transform_cache_is_empty(lv_gltf_model_t * data)
{
return data->node_transform_cache.size() == 0;
}
void recache_centerpoint(lv_gltf_model_t * data, size_t index_mesh, int32_t primitive)
{
data->local_mesh_to_center_points_by_primitive[index_mesh][primitive] =
lv_gltf_get_primitive_centerpoint(data, data->asset.meshes[index_mesh],
primitive);
}
fastgltf::math::fvec3 lv_gltf_data_get_centerpoint(lv_gltf_model_t * gltf_data,
fastgltf::math::fmat4x4 matrix,
size_t mesh_index, int32_t elem)
{
if(!lv_gltf_data_centerpoint_cache_contains(gltf_data, mesh_index, elem)) {
recache_centerpoint(gltf_data, mesh_index, elem);
}
return get_cached_centerpoint(gltf_data, mesh_index, elem, matrix);
}
bool lv_gltf_data_centerpoint_cache_contains(lv_gltf_model_t * data, size_t index, int32_t element)
{
return data->local_mesh_to_center_points_by_primitive.find(index) !=
data->local_mesh_to_center_points_by_primitive.end() &&
data->local_mesh_to_center_points_by_primitive[index].find(element) !=
data->local_mesh_to_center_points_by_primitive[index].end();
}
fastgltf::math::fvec3 get_cached_centerpoint(lv_gltf_model_t * data, size_t index,
int32_t element,
fastgltf::math::fmat4x4 matrix)
{
fastgltf::math::fvec4 tv = fastgltf::math::fvec4(
data->local_mesh_to_center_points_by_primitive[index][element]);
tv[3] = 1.f;
tv = matrix * tv;
return fastgltf::math::fvec3(tv[0], tv[1], tv[2]);
}
/**********************
* STATIC FUNCTIONS
**********************/
#endif /*LV_USE_GLTF*/
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,284 @@
#ifndef LV_GLTFDATA_PRIVATE_H
#define LV_GLTFDATA_PRIVATE_H
#include "../../../lv_conf_internal.h"
#if LV_USE_GLTF
#include <GL/glew.h>
#include <GL/gl.h>
#include "../opengl_shader/lv_opengl_shader_internal.h"
#include "../../../draw/lv_image_dsc.h"
#include "../../../misc/lv_types.h"
typedef struct {
GLuint count;
GLuint instanceCount;
GLuint firstIndex;
GLint baseVertex;
GLuint baseInstance;
} IndirectDrawCommand;
typedef struct {
IndirectDrawCommand draw;
GLenum primitiveType;
GLenum indexType;
GLuint vertexArray;
GLuint vertexBuffer;
GLuint indexBuffer;
GLuint materialUniformsIndex;
GLuint albedoTexture;
GLuint emissiveTexture;
GLuint metalRoughTexture;
GLuint occlusionTexture;
GLuint normalTexture;
GLuint diffuseTransmissionTexture;
GLuint diffuseTransmissionColorTexture;
GLuint transmissionTexture;
GLuint transmissionTexcoordIndex;
GLint baseColorTexcoordIndex;
GLint emissiveTexcoordIndex;
GLint metallicRoughnessTexcoordIndex;
GLint occlusionTexcoordIndex;
GLint normalTexcoordIndex;
GLint diffuseTransmissionTexcoordIndex;
GLint diffuseTransmissionColorTexcoordIndex;
GLint clearcoatTexture;
GLint clearcoatRoughnessTexture;
GLint clearcoatNormalTexture;
GLint clearcoatTexcoordIndex;
GLint clearcoatRoughnessTexcoordIndex;
GLint clearcoatNormalTexcoordIndex;
GLuint thicknessTexture;
GLint thicknessTexcoordIndex;
GLuint diffuseTexture;
GLint diffuseTexcoordIndex;
GLuint specularGlossinessTexture;
GLint specularGlossinessTexcoordIndex;
} lv_gltf_primitive_t;
typedef struct {
GLint camera;
GLint view_projection_matrix;
GLint model_matrix;
GLint view_matrix;
GLint projection_matrix;
GLint env_intensity;
GLint env_diffuse_sampler;
GLint env_specular_sampler;
GLint env_sheen_sampler;
GLint env_ggx_lut_sampler;
GLint env_charlie_lut_sampler;
GLint env_mip_count;
GLint exposure;
GLint roughness_factor;
GLint base_color_factor;
GLint base_color_sampler;
GLint base_color_uv_set;
GLint base_color_uv_transform;
GLint emissive_factor;
GLint emissive_sampler;
GLint emissive_uv_set;
GLint emissive_uv_transform;
GLint emissive_strength;
GLint metallic_factor;
GLint metallic_roughness_sampler;
GLint metallic_roughness_uv_set;
GLint metallic_roughness_uv_transform;
GLint occlusion_strength;
GLint occlusion_sampler;
GLint occlusion_uv_set;
GLint occlusion_uv_transform;
GLint normal_scale;
GLint normal_sampler;
GLint normal_uv_set;
GLint normal_uv_transform;
GLint clearcoat_factor;
GLint clearcoat_roughness_factor;
GLint clearcoat_sampler;
GLint clearcoat_uv_set;
GLint clearcoat_uv_transform;
GLint clearcoat_roughness_sampler;
GLint clearcoat_roughness_uv_set;
GLint clearcoat_roughness_uv_transform;
GLint clearcoat_normal_scale;
GLint clearcoat_normal_sampler;
GLint clearcoat_normal_uv_set;
GLint clearcoat_normal_uv_transform;
GLint thickness;
GLint thickness_sampler;
GLint thickness_uv_set;
GLint thickness_uv_transform;
GLint diffuse_transmission_sampler;
GLint diffuse_transmission_uv_set;
GLint diffuse_transmission_uv_transform;
GLint diffuse_transmission_color_sampler;
GLint diffuse_transmission_color_uv_set;
GLint diffuse_transmission_color_uv_transform;
GLint sheen_color_factor;
GLint sheen_roughness_factor;
GLint specular_color_factor;
GLint specular_factor;
GLint diffuse_transmission_color_factor;
GLint diffuse_transmission_factor;
GLint ior;
GLint alpha_cutoff;
GLint dispersion;
GLint screen_size;
GLint transmission_factor;
GLint transmission_sampler;
GLint transmission_uv_set;
GLint transmission_uv_transform;
GLint transmission_framebuffer_sampler;
GLint transmission_framebuffer_size;
GLint attenuation_distance;
GLint attenuation_color;
GLint joints_sampler;
GLint diffuse_factor;
GLint glossiness_factor;
GLint diffuse_sampler;
GLint diffuse_uv_set;
GLint diffuse_uv_transform;
GLint specular_glossiness_sampler;
GLint specular_glossiness_uv_set;
GLint specular_glossiness_uv_transform;
} lv_gltf_uniform_locations_t;
lv_gltf_uniform_locations_t lv_gltf_uniform_locations_create(GLuint program);
typedef struct {
GLuint program;
uint32_t bg_program;
uint32_t vert;
uint32_t frag;
} lv_gltf_shaderset_t;
typedef struct {
lv_gltf_uniform_locations_t uniforms;
lv_gltf_shaderset_t shaderset;
} lv_gltf_compiled_shader_t;
void lv_gltf_store_compiled_shader(lv_gltf_model_t * data, size_t identifier, lv_gltf_compiled_shader_t * shader);
lv_gltf_compiled_shader_t * lv_gltf_get_compiled_shader(lv_gltf_model_t * data, size_t identifier);
/**
* @brief Load the gltf file at the specified filepath
*
* @param gltf_path The gltf filename
* @param ret_data Pointer to the data container that will be populated.
* @param shaders Pointer to the shader cache object this file uses.
*/
lv_gltf_model_t *
lv_gltf_data_load_from_file(const char * file_path,
lv_opengl_shader_manager_t * shader_manager);
/**
* @brief Load the gltf file encoded within the supplied byte array
*
* @param gltf_path The gltf filename
* @param gltf_data_size if gltf_path is instead a byte array, pass the size of that array in through this variable (or 0 if it's a file path).
* @param ret_data Pointer to the data container that will be populated.
* @param shaders Pointer to the shader cache object this file uses.
*/
lv_gltf_model_t *
lv_gltf_data_load_from_bytes(const uint8_t * data, size_t data_size,
lv_opengl_shader_manager_t * shader_manager);
/**
* @brief Retrieve the radius of the GLTF data object.
*
* @param D Pointer to the lv_gltf_data_t object from which to get the radius.
* @return The radius of the GLTF data object.
*/
double lv_gltf_data_get_radius(lv_gltf_model_t * D);
/**
* @brief Destroy a GLTF data object and free associated resources.
*
* @param _data Pointer to the lv_gltf_data_t object to be destroyed.
*/
void lv_gltf_data_destroy(lv_gltf_model_t * _data);
/**
* @brief Copy the bounds information from one GLTF data object to another.
*
* @param to Pointer to the destination lv_gltf_data_t object.
* @param from Pointer to the source lv_gltf_data_t object.
*/
void lv_gltf_data_copy_bounds_info(lv_gltf_model_t * to, lv_gltf_model_t * from);
/**
* @brief Retrieve information about a specific texture in a GLTF model.
*
* @param data_obj Pointer to the lv_gltf_data_t object containing the model data.
* @param model_texture_index The index of the texture in the model.
* @param mipmapnum The mipmap level to retrieve information for.
* @param byte_count Pointer to a size_t variable to store the byte count of the texture.
* @param width Pointer to a uint32_t variable to store the width of the texture.
* @param height Pointer to a uint32_t variable to store the height of the texture.
* @param has_alpha Pointer to a bool variable to indicate if the texture has an alpha channel.
* @return True if the texture information was successfully retrieved, false otherwise.
*/
bool lv_gltf_data_get_texture_info(lv_gltf_model_t * data_obj,
uint32_t model_texture_index,
uint32_t mipmapnum, size_t * byte_count,
uint32_t * width, uint32_t * height,
bool * has_alpha);
/**
* @brief Swap the red and blue channels in a pixel buffer.
*
* @param pixel_buffer Pointer to the pixel buffer containing the image data.
* @param byte_total_count The total number of bytes in the pixel buffer.
* @param has_alpha Flag indicating whether the pixel buffer includes an alpha channel.
*/
void lv_gltf_data_rgb_to_bgr(uint8_t * pixel_buffer,
size_t byte_total_count,
bool has_alpha);
/**
* @brief Convert a texture from a GLTF model to an image descriptor.
*
* @param new_image_dsc Pointer to the lv_image_dsc_t structure to be populated with the image data.
* @param data_obj Pointer to the lv_gltf_data_t object containing the model data.
* @param model_texture_index The index of the texture in the model to convert.
*/
void lv_gltf_data_texture_to_image_dsc(lv_image_dsc_t * new_image_dsc,
lv_gltf_model_t * data_obj,
uint32_t model_texture_index);
#endif /*LV_USE_GLTF*/
#endif /* LV_GLTFDATA_PRIVATE_H */
@@ -0,0 +1,373 @@
#ifndef LV_GLTFDATA_HPP
#define LV_GLTFDATA_HPP
#include "../../../lv_conf_internal.h"
#if LV_USE_GLTF
#include <fastgltf/math.hpp>
#include "lv_gltf_data_internal.h"
#include "lv_gltf_bind.h"
#include "../../../misc/lv_array.h"
#include <GL/gl.h>
#ifdef __cplusplus
#include <string>
#include <vector>
#include <map>
#include <fastgltf/types.hpp>
// Vector of int32_t's
using UintVector = std::vector<uint32_t>;
// Vector of int32_t's
using IntVector = std::vector<int32_t>;
// Vector of int64_t's
using LongVector = std::vector<int64_t>;
// Pointer to fastgltf::Node
using NodePtr = fastgltf::Node *;
// A standard 4x4 transform matrix
using Transform = fastgltf::math::fmat4x4;
// Pair of Node pointer and int32_t
using NodeIndexPair = std::pair<NodePtr, size_t>;
// Pair of float and Node/Index pair
using NodeIndexDistancePair = std::pair<float, NodeIndexPair>;
// Vector of NodeIndexPair
using NodePairVector = std::vector<NodeIndexPair>;
// Vector of NodeIndexDistancePair
using NodeDistanceVector = std::vector<NodeIndexDistancePair>;
// Map of uint32_t to NodePairVector
using MaterialIndexMap = std::map<uint32_t, NodePairVector>;
// Map of Node Pointers to Transforms
using NodeTransformMap = std::map<NodePtr, Transform>;
// Map of Nodes by string (name)
using StringNodeMap = std::map<std::string, NodePtr>;
// Map of Nodes by string (name)
using NodeIntMap = std::map<NodePtr, uint32_t>;
// Map of Nodes by string (name)
using NodeVector = std::vector<NodePtr>;
// Map of Node Index to Map of Prim Index to CenterXYZ+RadiusW Vec4
using NodePrimCenterMap = std::map<uint32_t, std::map<uint32_t, fastgltf::math::fvec4> >;
// Map of Overrides by Node
using NodeOverrideMap = std::map<fastgltf::Node *, lv_gltf_bind_t *>;
// Map of Overrides by Node
using OverrideVector = std::vector<lv_gltf_bind_t>;
typedef struct {
GLuint drawsBuffer;
std::vector<lv_gltf_primitive_t> primitives;
} lv_gltf_mesh_data_t;
typedef struct {
const char *ip;
const char *path;
fastgltf::Node *node;
} lv_gltf_data_node_t;
struct _lv_gltf_model_t {
const char *filename;
fastgltf::Asset asset;
lv_array_t nodes;
NodeVector node_by_light_index;
NodeTransformMap node_transform_cache;
MaterialIndexMap opaque_nodes_by_material_index;
MaterialIndexMap blended_nodes_by_material_index;
NodeOverrideMap node_binds;
lv_array_t binds;
std::vector<size_t> validated_skins;
std::vector<GLuint> skin_tex;
NodePrimCenterMap local_mesh_to_center_points_by_primitive;
lv_gltf_t* viewer;
std::vector<lv_gltf_mesh_data_t> meshes;
std::vector<GLuint> textures;
lv_array_t compiled_shaders;
std::map<fastgltf::Node *, std::vector<uint32_t> > channel_set_cache;
fastgltf::math::fmat4x4 view_mat;
fastgltf::math::fvec3 view_pos;
fastgltf::math::fvec3 vertex_max;
fastgltf::math::fvec3 vertex_min;
fastgltf::math::fvec3 vertex_cen;
lv_timer_t* animation_update_timer;
size_t current_animation;
size_t last_material_index;
uint32_t last_camera_index;
int32_t last_anim_num;
float bound_radius;
uint32_t current_animation_max_time;
uint32_t local_timestamp;
uint32_t last_tick;
uint32_t camera;
bool is_animation_enabled;
bool last_pass_was_transmission;
bool last_frame_was_antialiased;
bool last_frame_no_motion;
bool _last_frame_no_motion;
struct _lv_gltf_model_t *linked_view_source;
};
/**
* @brief Retrieve a specific texture from the GLTF model data.
*
* @param data Pointer to the lv_gltf_data_t object containing the model data.
* @param index The index of the texture to retrieve.
* @return Pointer to the texture object.
*/
GLuint lv_gltf_data_get_texture(lv_gltf_model_t *data, size_t index);
/**
* @brief Retrieve the minimum bounds (X/Y/Z) of the model from the GLTF data.
*
* @param data Pointer to the lv_gltf_data_t object containing the model data.
* @return Pointer to a 3-element float array representing the minimum bounds.
*/
fastgltf::math::fvec3 lv_gltf_data_get_bounds_min(const lv_gltf_model_t *data);
/**
* @brief Retrieve the maximum bounds (X/Y/Z) of the model from the GLTF data.
*
* @param D Pointer to the lv_gltf_data_t object containing the model data.
* @return Pointer to a 3-element float array representing the maximum bounds.
*/
fastgltf::math::fvec3 lv_gltf_data_get_bounds_max(const lv_gltf_model_t *data);
/**
* @brief Retrieve the center coordinates of the GLTF data object.
*
* @param D Pointer to the lv_gltf_data_t object from which to get the center.
* @return Pointer to an array containing the center coordinates (x, y, z).
*/
fastgltf::math::fvec3 lv_gltf_data_get_center(const lv_gltf_model_t *data);
/**
* @brief Retrieve the filename of the GLTF model.
*
* @param D Pointer to the lv_gltf_data_t object containing the model data.
* @return Pointer to a constant character string representing the filename.
*/
const char *lv_gltf_get_filename(const lv_gltf_model_t *data);
/**
* @brief Check if the centerpoint cache contains a specific entry.
*
* @param data Pointer to the lv_gltf_data_t object containing the model data.
* @param index The index of the entry to check.
* @param element The specific parameter to check within the cache.
* @return True if the cache contains the entry, false otherwise.
*/
bool lv_gltf_data_centerpoint_cache_contains(lv_gltf_model_t *data, size_t index, int32_t element);
/**
* @brief Retrieve a specific primitive from a mesh.
*
* @param M Pointer to the MeshData structure containing the mesh data.
* @param I The index of the primitive to retrieve.
* @return Pointer to the primitive data.
*/
lv_gltf_primitive_t *lv_gltf_data_get_primitive_from_mesh(lv_gltf_mesh_data_t *M, size_t I);
/**
* @brief Retrieve the asset associated with the GLTF model data.
*
* @param D Pointer to the lv_gltf_data_t object containing the model data.
* @return Pointer to the asset data.
*/
fastgltf::Asset *lv_gltf_data_get_asset(lv_gltf_model_t *data);
/**
* @brief Retrieve mesh data for a specific index from the GLTF model data.
*
* @param D Pointer to the lv_gltf_data_t object containing the model data.
* @param I The index of the mesh data to retrieve.
* @return Pointer to the MeshData structure containing the mesh data.
*/
lv_gltf_mesh_data_t *lv_gltf_data_get_mesh(lv_gltf_model_t *data, size_t index);
/**
* @brief Retrieve the skin texture index for a specific entry in the GLTF model data.
*
* @param data Pointer to the lv_gltf_data_t object containing the model data.
* @param index The index of the entry for which to retrieve the skin texture index.
* @return The skin texture index.
*/
GLuint lv_gltf_data_get_skin_texture_at(lv_gltf_model_t *data, size_t index);
/**
* @brief Check if the validated skins contain a specific entry.
*
* @param data Pointer to the lv_gltf_data_t object containing the model data.
* @param index The index of the skin to check.
* @return True if the validated skins contain the entry, false otherwise.
*/
bool lv_gltf_data_validated_skins_contains(lv_gltf_model_t *data, size_t index);
/**
* @brief Validate a specific skin in the GLTF model data.
*
* @param data Pointer to the lv_gltf_data_t object containing the model data.
* @param index The index of the skin to validate.
*/
void lv_gltf_data_validate_skin(lv_gltf_model_t *data, size_t index);
/**
* @brief Add an opaque node primitive to the GLTF model data.
*
* @param D Pointer to the lv_gltf_data_t object containing the model data.
* @param I The index of the primitive to add.
* @param N Pointer to the NodePtr representing the node to add.
* @param P The specific parameter associated with the primitive.
*/
void lv_gltf_data_add_opaque_node_primitive(lv_gltf_model_t *data, size_t index, fastgltf::Node *node, size_t primitive_index);
/**
* @brief Add a blended node primitive to the GLTF model data.
*
* @param D Pointer to the lv_gltf_data_t object containing the model data.
* @param I The index of the primitive to add.
* @param N Pointer to the NodePtr representing the node to add.
* @param P The specific parameter associated with the primitive.
*/
void lv_gltf_data_add_blended_node_primitive(lv_gltf_model_t *data, size_t mesh_index, fastgltf::Node *node,
size_t primitive_index);
/**
* @brief Set the cached transformation matrix for a specific node in the GLTF model data.
*
* @param D Pointer to the lv_gltf_data_t object containing the model data.
* @param N Pointer to the NodePtr representing the node for which to set the transformation.
* @param M The transformation matrix to cache.
*/
void lv_gltf_data_set_cached_transform(lv_gltf_model_t* data, fastgltf::Node* node, fastgltf::math::fmat4x4 M);
/**
* @brief Clear the transformation cache for the GLTF model data.
*
* @param D Pointer to the lv_gltf_data_t object containing the model data.
*/
void lv_gltf_data_clear_transform_cache(lv_gltf_model_t* data);
/**
* @brief Retrieve the cached transformation matrix for a specific node in the GLTF model data.
*
* @param D Pointer to the lv_gltf_data_t object containing the model data.
* @param N Pointer to the NodePtr representing the node for which to retrieve the transformation.
* @return The cached transformation matrix.
*/
fastgltf::math::fmat4x4 lv_gltf_data_get_cached_transform(lv_gltf_model_t* data, fastgltf::Node* node);
/**
* @brief Check if a cached transformation matrix exists for a given node.
*
* @param D Pointer to the lv_gltf_data_t object containing the model data.
* @param N Pointer to the NodePtr representing the node for which to retrieve the transformation.
* @return true if a cache item exists, false otherwise
int32_t*/
bool lv_gltf_data_has_cached_transform(lv_gltf_model_t* data, fastgltf::Node* node);
/**
* @brief Check if the transformation cache is empty.
*
* @param D Pointer to the lv_gltf_data_t object containing the model data.
* @return True if the transformation cache is empty, false otherwise.
*/
bool lv_gltf_data_transform_cache_is_empty(lv_gltf_model_t* data);
/**
* @brief Retrieve the size of the skins in the GLTF model data.
*
* @param D Pointer to the lv_gltf_data_t object containing the model data.
* @return The size of the skins.
*/
size_t lv_gltf_data_get_skins_size(lv_gltf_model_t *data);
/**
* @brief Retrieve a specific skin from the GLTF model data.
*
* @param D Pointer to the lv_gltf_data_t object containing the model data.
* @param I The index of the skin to retrieve.
* @return The skin index.
*/
size_t lv_gltf_data_get_skin(lv_gltf_model_t *data, size_t index);
/**
* @brief Ingest and discover defines for a specific node and primitive in the GLTF model data.
*
* @param data_obj Pointer to the lv_gltf_data_t object containing the model data.
* @param node Pointer to the node for which to ingest defines.
* @param prim Pointer to the primitive for which to ingest defines.
*/
void lv_gltf_data_injest_discover_defines(lv_gltf_model_t *data, fastgltf::Node *node, fastgltf::Primitive *prim);
/**
* @brief Retrieve the center point of a specific mesh element from the GLTF model data.
*
* @param gltf_data Pointer to the lv_gltf_data_t object containing the model data.
* @param matrix The transformation matrix to apply when calculating the center point.
* @param meshIndex The index of the mesh from which to retrieve the center point.
* @param elem The specific element index within the mesh.
* @return The center point as a fastgltf::math::fvec3 structure.
*/
fastgltf::math::fvec3 lv_gltf_data_get_centerpoint(lv_gltf_model_t *gltf_data, fastgltf::math::fmat4x4 matrix, size_t mesh_index,
int32_t elem);
lv_gltf_mesh_data_t *lv_gltf_get_new_meshdata(lv_gltf_model_t *_data);
lv_gltf_model_t *lv_gltf_data_create_internal(const char *gltf_path, fastgltf::Asset);
lv_gltf_model_t *lv_gltf_data_load_internal(const void *data_source, size_t data_size, lv_opengl_shader_manager_t *shaders);
/*void set_node_at_path(lv_gltf_data_t *data, const std::string &path,*/
/* fastgltf::Node *node);*/
/*void set_node_at_ip(lv_gltf_data_t *data, const std::string &ip,*/
/* fastgltf::Node *node);*/
/*void set_node_index(lv_gltf_data_t *data, size_t index, fastgltf::Node *node);*/
fastgltf::math::fvec4 lv_gltf_get_primitive_centerpoint(lv_gltf_model_t *data, fastgltf::Mesh &mesh, uint32_t prim_num);
fastgltf::math::fvec3 get_cached_centerpoint(lv_gltf_model_t *data, size_t index, int32_t element,
fastgltf::math::fmat4x4 matrix);
void lv_gltf_data_destroy_textures(lv_gltf_model_t *data);
GLuint lv_gltf_data_create_texture(lv_gltf_model_t *data);
void lv_gltf_data_nodes_init(lv_gltf_model_t *data, size_t size);
void lv_gltf_data_node_init(lv_gltf_data_node_t * node, fastgltf::Node * fastgltf_node, const char * path, const char * ip);
void lv_gltf_data_node_add(lv_gltf_model_t *data, const lv_gltf_data_node_t *data_node);
void lv_gltf_data_node_delete(lv_gltf_data_node_t *node);
/**
* @brief Retrieve the pixel data for a specific texture in a GLTF model.
*
* @param pixels Pointer to the memory where the pixel data will be stored.
* @param data_obj Pointer to the lv_gltf_data_t object containing the model data.
* @param model_texture_index The index of the texture in the model.
* @param mipmapnum The mipmap level to retrieve pixel data for.
* @param width The width of the texture.
* @param height The height of the texture.
* @param has_alpha Flag indicating whether the texture includes an alpha channel.
* @return True if the pixel data was successfully retrieved, false otherwise.
*/
bool lv_gltf_data_get_texture_pixels(void *pixels, lv_gltf_model_t *data_obj, uint32_t model_texture_index, uint32_t mipmapnum,
uint32_t width, uint32_t height, bool has_alpha);
lv_gltf_data_node_t *lv_gltf_data_node_get_by_index(lv_gltf_model_t *data, size_t index);
lv_gltf_data_node_t *lv_gltf_data_node_get_by_ip(lv_gltf_model_t *data, const char *ip);
lv_gltf_data_node_t *lv_gltf_data_node_get_by_path(lv_gltf_model_t *data, const char *path);
uint32_t lv_gltf_data_get_animation_total_time(lv_gltf_model_t *data, uint32_t index);
std::vector<uint32_t> *lv_gltf_data_animation_get_channel_set(std::size_t anim_num, lv_gltf_model_t *data, fastgltf::Node &node);
void lv_gltf_data_animation_matrix_apply(float timestamp, std::size_t anim_num, lv_gltf_model_t *gltf_data, fastgltf::Node &node,
fastgltf::math::fmat4x4 &matrix);
#endif
#endif /*LV_USE_GLTF*/
#endif /*LV_GLTFVIEW_H*/
@@ -0,0 +1,52 @@
/**
* @file lv_gltf_data_mesh.cpp
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_gltf_data_internal.hpp"
#if LV_USE_GLTF
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_gltf_mesh_data_t * lv_gltf_get_new_meshdata(lv_gltf_model_t * data)
{
data->meshes.emplace_back(lv_gltf_mesh_data_t {});
return &(data->meshes[data->meshes.size() - 1]);
}
lv_gltf_mesh_data_t * lv_gltf_data_get_mesh(lv_gltf_model_t * data, size_t index)
{
return &data->meshes[index];
}
/**********************
* STATIC FUNCTIONS
**********************/
#endif /*LV_USE_GLTF*/
@@ -0,0 +1,103 @@
/**
* @file lv_gltf_data_node.cpp
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_gltf_data_internal.hpp"
#if LV_USE_GLTF
#include "fastgltf/types.hpp"
#include "lv_gltf_model.h"
#include "lv_gltf_data_internal.hpp"
#include "../../../misc/lv_array.h"
#include "../../../misc/lv_assert.h"
#include "../../../misc/lv_types.h"
#include "../../../stdlib/lv_string.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_gltf_data_nodes_init(lv_gltf_model_t * data, size_t size)
{
lv_array_init(&data->nodes, size, sizeof(lv_gltf_data_node_t));
}
void lv_gltf_data_node_init(lv_gltf_data_node_t * node, fastgltf::Node * fastgltf_node, const char * path,
const char * ip)
{
LV_ASSERT_NULL(node);
node->node = fastgltf_node;
node->path = lv_strdup(path);
node->ip = lv_strdup(ip);
LV_ASSERT_MALLOC(path);
LV_ASSERT_MALLOC(ip);
}
void lv_gltf_data_node_add(lv_gltf_model_t * data, const lv_gltf_data_node_t * data_node)
{
LV_ASSERT_NULL(data);
lv_array_push_back(&data->nodes, data_node);
}
lv_gltf_data_node_t * lv_gltf_data_node_get_by_index(lv_gltf_model_t * data, size_t index)
{
LV_ASSERT_NULL(data);
if(index >= lv_array_size(&data->nodes)) {
return nullptr;
}
return (lv_gltf_data_node_t *)lv_array_at(&data->nodes, index);
}
lv_gltf_data_node_t * lv_gltf_data_node_get_by_ip(lv_gltf_model_t * data, const char * ip)
{
for(size_t i = 0; i < data->nodes.size; ++i) {
lv_gltf_data_node_t * entry = (lv_gltf_data_node_t *) lv_array_at(&data->nodes, i);
if(lv_streq(entry->ip, ip)) {
return entry;
}
}
return nullptr;
}
lv_gltf_data_node_t * lv_gltf_data_node_get_by_path(lv_gltf_model_t * data, const char * path)
{
for(size_t i = 0; i < data->nodes.size; ++i) {
lv_gltf_data_node_t * entry = (lv_gltf_data_node_t *) lv_array_at(&data->nodes, i);
if(lv_streq(entry->path, path)) {
return entry;
}
}
return nullptr;
}
/**********************
* STATIC FUNCTIONS
**********************/
#endif /*LV_USE_GLTF*/
@@ -0,0 +1,126 @@
/**
* @file lv_gltf_data_primitive.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_gltf_data_internal.hpp"
#if LV_USE_GLTF
#include "../../../misc/lv_math.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_gltf_primitive_t * lv_gltf_data_get_primitive_from_mesh(lv_gltf_mesh_data_t * mesh, uint64_t index)
{
return &(mesh->primitives[index]);
}
void lv_gltf_data_add_opaque_node_primitive(lv_gltf_model_t * data, size_t index,
fastgltf::Node * node, size_t primitive_index)
{
data->opaque_nodes_by_material_index[index].emplace_back(
std::make_pair(node, primitive_index));
}
void lv_gltf_data_add_blended_node_primitive(lv_gltf_model_t * data, size_t index,
fastgltf::Node * node, size_t primitive_index)
{
data->blended_nodes_by_material_index[index].push_back(
std::make_pair(node, primitive_index));
}
fastgltf::math::fvec4 lv_gltf_get_primitive_centerpoint(lv_gltf_model_t * data,
fastgltf::Mesh & mesh,
uint32_t prim_num)
{
fastgltf::math::fvec4 result{ 0.f };
fastgltf::math::fvec3 v_min{ 999999999.f };
fastgltf::math::fvec3 v_max{ -999999999.f };
fastgltf::math::fvec3 v_cen{ 0.f };
float radius = 0.f;
if(mesh.primitives.size() <= prim_num) {
return result;
}
const auto & it = mesh.primitives[prim_num];
const auto & asset = data->asset;
const auto * positionIt = it.findAttribute("POSITION");
const auto & positionAccessor =
asset.accessors[positionIt->accessorIndex];
if(!positionAccessor.bufferViewIndex.has_value()) {
return result;
}
if(!(positionAccessor.min.has_value() &&
positionAccessor.max.has_value())) {
LV_LOG_ERROR(
"Could not get primitive center point. Missing min/max values");
return result;
}
fastgltf::math::fvec4 t_min{
(float)(positionAccessor.min.value().get<double>((size_t)0)),
(float)(positionAccessor.min.value().get<double>((size_t)1)),
(float)(positionAccessor.min.value().get<double>((size_t)2)),
0.f
};
fastgltf::math::fvec4 t_max{
(float)(positionAccessor.max.value().get<double>((size_t)0)),
(float)(positionAccessor.max.value().get<double>((size_t)1)),
(float)(positionAccessor.max.value().get<double>((size_t)2)),
0.f
};
v_max[0] = LV_MAX(t_min.x(), t_max.x());
v_max[1] = LV_MAX(t_min.y(), t_max.y());
v_max[2] = LV_MAX(t_min.z(), t_max.z());
v_min[0] = LV_MIN(t_min.x(), t_max.x());
v_min[1] = LV_MIN(t_min.y(), t_max.y());
v_min[2] = LV_MIN(t_min.z(), t_max.z());
v_cen[0] = (v_max[0] + v_min[0]) / 2.0f;
v_cen[1] = (v_max[1] + v_min[1]) / 2.0f;
v_cen[2] = (v_max[2] + v_min[2]) / 2.0f;
float size_x = v_max[0] - v_min[0];
float size_y = v_max[1] - v_min[1];
float size_z = v_max[2] - v_min[2];
radius = std::sqrt((size_x * size_x) + (size_y * size_y) +
(size_z * size_z)) /
2.0f;
result[0] = v_cen[0];
result[1] = v_cen[1];
result[2] = v_cen[2];
result[3] = radius;
return result;
}
/**********************
* STATIC FUNCTIONS
**********************/
#endif /*LV_USE_GLTF*/
@@ -0,0 +1,60 @@
/**
* @file lv_gltf_data_shader.cpp
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_gltf_data_internal.hpp"
#if LV_USE_GLTF
#include "../../../misc/lv_array.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_gltf_store_compiled_shader(lv_gltf_model_t * data, size_t identifier, lv_gltf_compiled_shader_t * shader)
{
const size_t index = identifier - 1;
bool has_to_resize = index >= lv_array_size(&data->compiled_shaders);
if(!has_to_resize) {
lv_array_assign(&data->compiled_shaders, index, shader);
}
while(index >= lv_array_size(&data->compiled_shaders)) {
lv_array_push_back(&data->compiled_shaders, shader);
}
}
lv_gltf_compiled_shader_t * lv_gltf_get_compiled_shader(lv_gltf_model_t * data, size_t identifier)
{
const size_t index = identifier - 1;
LV_ASSERT(index < lv_array_size(&data->compiled_shaders));
return (lv_gltf_compiled_shader_t *)lv_array_at(&data->compiled_shaders, index);
}
/**********************
* STATIC FUNCTIONS
**********************/
#endif /*LV_USE_GLTF*/
@@ -0,0 +1,68 @@
/**
* @file lv_gltf_data_skin.cpp
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_gltf_data_internal.hpp"
#if LV_USE_GLTF
#include <algorithm>
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
GLuint lv_gltf_data_get_skin_texture_at(lv_gltf_model_t * data, size_t index)
{
return data->skin_tex[index];
}
bool lv_gltf_data_validated_skins_contains(lv_gltf_model_t * data, size_t index)
{
return ((std::find(data->validated_skins.begin(),
data->validated_skins.end(),
index) != data->validated_skins.end()));
}
void lv_gltf_data_validate_skin(lv_gltf_model_t * data, size_t index)
{
data->validated_skins.push_back(index);
}
size_t lv_gltf_data_get_skins_size(lv_gltf_model_t * data)
{
return data->validated_skins.size();
}
size_t lv_gltf_data_get_skin(lv_gltf_model_t * data, size_t index)
{
return data->validated_skins[index];
}
/**********************
* STATIC FUNCTIONS
**********************/
#endif /*LV_USE_GLTF*/
@@ -0,0 +1,193 @@
/**
* @file lv_gltf_data_texture.cpp
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_gltf_data_internal.hpp"
#if LV_USE_GLTF
#include <cstdint>
#include "../../../misc/lv_color.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_gltf_data_destroy_textures(lv_gltf_model_t * data)
{
glDeleteTextures(data->skin_tex.size(), data->skin_tex.data());
data->skin_tex.clear();
}
GLuint lv_gltf_data_create_texture(lv_gltf_model_t * data)
{
GLuint texture;
GL_CALL(glGenTextures(1, &texture));
data->skin_tex.push_back(texture);
return texture;
}
bool lv_gltf_data_get_texture_pixels(uint8_t * pixels, lv_gltf_model_t * data_obj, uint32_t model_texture_index,
uint32_t mipmapnum,
uint32_t width, uint32_t height, bool has_alpha)
{
// This parameter is specified because WebGL can't read a texture's width from the GPU, however this isn't yet implemented so for now it either uses the GPU or it fails.
LV_UNUSED(width);
LV_UNUSED(height);
if(model_texture_index >= data_obj->textures.size()) {
return false;
}
GLuint texid = data_obj->textures[model_texture_index];
// Bind the texture
GL_CALL(glBindTexture(GL_TEXTURE_2D, texid));
GL_CALL(glGetTexImage(GL_TEXTURE_2D, mipmapnum, (has_alpha) ? GL_RGBA : GL_RGB, GL_UNSIGNED_BYTE, pixels));
GL_CALL(glBindTexture(GL_TEXTURE_2D, 0));
return true;
}
void lv_gltf_data_texture_to_image_dsc(lv_image_dsc_t * new_image_dsc, lv_gltf_model_t * data_obj,
uint32_t model_texture_index)
{
size_t byte_total_count = 0;
uint32_t source_pixel_width = 0;
uint32_t source_pixel_height = 0;
bool has_alpha = false;
uint8_t * pixel_buffer;
if(lv_gltf_data_get_texture_info(data_obj, model_texture_index, 0, &byte_total_count, &source_pixel_width,
&source_pixel_height, &has_alpha)) {
pixel_buffer = (uint8_t *)lv_malloc(byte_total_count);
if(lv_gltf_data_get_texture_pixels(pixel_buffer, data_obj, model_texture_index, 0, source_pixel_width,
source_pixel_height, has_alpha)) {
if(pixel_buffer == NULL || byte_total_count == 0 || source_pixel_width == 0)
return;
if(new_image_dsc->data_size > 0) {
lv_free((uint8_t *)new_image_dsc->data);
new_image_dsc->data = NULL;
new_image_dsc->data_size = 0;
}
lv_gltf_data_rgb_to_bgr(pixel_buffer, byte_total_count, has_alpha);
size_t bytes_per_pixel = has_alpha ? 4 : 3;
size_t pixel_count = (byte_total_count / bytes_per_pixel);
new_image_dsc->data = (const uint8_t *)pixel_buffer;
new_image_dsc->data_size = byte_total_count;
new_image_dsc->header.w = source_pixel_width;
new_image_dsc->header.h = (uint16_t)(pixel_count / source_pixel_width);
new_image_dsc->header.cf = has_alpha ? LV_COLOR_FORMAT_ARGB8888 : LV_COLOR_FORMAT_RGB888;
}
}
}
bool lv_gltf_data_get_texture_info(lv_gltf_model_t * data_obj, uint32_t model_texture_index, uint32_t mipmapnum,
size_t * byte_count,
uint32_t * width, uint32_t * height, bool * has_alpha)
{
*byte_count = 0;
if(model_texture_index >= data_obj->textures.size()) {
return false;
}
GLuint texid = data_obj->textures[model_texture_index];
// Bind the texture
GL_CALL(glBindTexture(GL_TEXTURE_2D, texid));
int32_t gl_color_format;
GL_CALL(glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &gl_color_format));
// Determine if the texture has an alpha channel
*has_alpha = false;
bool not_valid = false;
switch(gl_color_format) {
case GL_RGBA:
case GL_BGRA:
case GL_RGBA8:
*has_alpha = true;
break;
case GL_RGB:
case GL_BGR:
case GL_RGB8:
*has_alpha = false;
break;
default:
LV_LOG_ERROR("Unhandled texture color format %d", gl_color_format);
not_valid = true;
break;
}
// even if the pixel format is invalid, we can still get the width and height
GLint texture_width;
GLint texture_height;
GL_CALL(glGetTexLevelParameteriv(GL_TEXTURE_2D, mipmapnum, GL_TEXTURE_WIDTH, &texture_width));
GL_CALL(glGetTexLevelParameteriv(GL_TEXTURE_2D, mipmapnum, GL_TEXTURE_HEIGHT, &texture_height));
*width = (uint32_t)(texture_width);
*height = (uint32_t)(texture_height);
GL_CALL(glBindTexture(GL_TEXTURE_2D, 0));
if(not_valid) {
return false;
}
*byte_count = texture_width * texture_height * (*has_alpha ? 4 : 3);
return true;
}
void lv_gltf_data_rgb_to_bgr(uint8_t * pixels, size_t byte_total_count, bool has_alpha)
{
size_t bytes_per_pixel = has_alpha ? 4 : 3;
size_t pixel_count = (byte_total_count / bytes_per_pixel);
if(bytes_per_pixel == 4) {
for(size_t p = 0; p < pixel_count; p++) {
size_t index = p << 2;
uint8_t r = pixels[index + 0];
uint8_t g = pixels[index + 1];
uint8_t b = pixels[index + 2];
uint8_t a = pixels[index + 3];
pixels[index + 0] = b;
pixels[index + 1] = g;
pixels[index + 2] = r;
pixels[index + 3] = a;
}
}
else {
for(size_t p = 0; p < pixel_count; p++) {
size_t index = p * 3;
uint8_t r = pixels[index + 0];
uint8_t g = pixels[index + 1];
uint8_t b = pixels[index + 2];
pixels[index + 0] = b;
pixels[index + 1] = g;
pixels[index + 2] = r;
}
}
}
/**********************
* STATIC FUNCTIONS
**********************/
#endif /*LV_USE_GLTF*/
+135
View File
@@ -0,0 +1,135 @@
#ifndef LV_GLTF_MODEL_H
#define LV_GLTF_MODEL_H
#include "../../../lv_conf_internal.h"
#if LV_USE_GLTF
#include "../../../misc/lv_types.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Get the number of images in the glTF model
*
* Images in glTF are used as sources for textures and can be stored either as external files
* or embedded as base64-encoded model within the glTF file.
*
* @param model Pointer to the glTF model data structure
* @return Number of images in the model
*/
size_t lv_gltf_model_get_image_count(const lv_gltf_model_t * model);
/**
* @brief Get the number of textures in the glTF model
*
* Textures define how images are sampled and applied to materials. Each texture references
* an image and may specify sampling parameters like filtering and wrapping modes.
*
* @param model Pointer to the glTF model data structure
* @return Number of textures in the model
*/
size_t lv_gltf_model_get_texture_count(const lv_gltf_model_t * model);
/**
* @brief Get the number of materials in the glTF model
*
* Materials define the visual appearance of mesh primitives, including properties like
* base color, metallic/roughness values, normal maps, and other surface characteristics.
*
* @param model Pointer to the glTF model data structure
* @return Number of materials in the model
*/
size_t lv_gltf_model_get_material_count(const lv_gltf_model_t * model);
/**
* @brief Get the number of cameras in the glTF model
*
* Cameras define viewpoints within the 3D scene and can be either perspective or
* orthographic. They are typically attached to nodes in the scene graph.
*
* @param model Pointer to the glTF model data structure
* @return Number of cameras in the model
*/
size_t lv_gltf_model_get_camera_count(const lv_gltf_model_t * model);
/**
* @brief Get the number of nodes in the glTF model
*
* Nodes form the scene graph hierarchy and can contain transformations, meshes, cameras,
* or other nodes as children. They define the spatial relationships between objects in the scene.
*
* @param model Pointer to the glTF model data structure
* @return Number of nodes in the model
*/
size_t lv_gltf_model_get_node_count(const lv_gltf_model_t * model);
/**
* @brief Get the number of meshes in the glTF model
*
* Meshes contain the geometric model for 3D objects, including vertex positions, normals,
* texture coordinates, and indices. Each mesh can have multiple primitives with different materials.
*
* @param model Pointer to the glTF model data structure
* @return Number of meshes in the model
*/
size_t lv_gltf_model_get_mesh_count(const lv_gltf_model_t * model);
/**
* @brief Get the number of scenes in the glTF model
*
* Scenes define the root nodes of the scene graph. A glTF file can contain multiple scenes,
* though typically only one is designated as the default scene to be displayed.
*
* @param model Pointer to the glTF model data structure
* @return Number of scenes in the model
*/
size_t lv_gltf_model_get_scene_count(const lv_gltf_model_t * model);
/**
* @brief Get the number of animations in the glTF model
*
* Animations define keyframe-based motion for nodes in the scene, including transformations
* like translation, rotation, and scaling over time.
*
* @param model Pointer to the glTF model data structure
* @return Number of animations in the model
*/
size_t lv_gltf_model_get_animation_count(const lv_gltf_model_t * model);
/**
* @brief Select and start playing an animation
*
* @param model Pointer to the glTF model structure
* @param index Animation number to start playing
* @return LV_RESULT_OK if the animation was started else LV_RESULT_INVALID
*/
lv_result_t lv_gltf_model_play_animation(lv_gltf_model_t * model, size_t index);
/**
* @brief Pause the current animation
*
* @param model Pointer to the glTF model structure
*/
void lv_gltf_model_pause_animation(lv_gltf_model_t * model);
/**
* @brief Check if an animation is currently being played
*
* @param model Pointer to the glTF model structure
*/
bool lv_gltf_model_is_animation_paused(lv_gltf_model_t * model);
/**
* @brief Get the current selected animation. To see if it's playing see `lv_gltf_model_is_animation_paused`
*
* @param model Pointer to the glTF model structure
*/
size_t lv_gltf_model_get_animation(lv_gltf_model_t * model);
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_USE_GLTF*/
#endif /*LV_GLTF_MODEL_H*/
@@ -0,0 +1,115 @@
#include "lv_gltf_data_internal.hpp"
#if LV_USE_GLTF
lv_gltf_uniform_locations_t lv_gltf_uniform_locations_create(GLuint program)
{
lv_gltf_uniform_locations_t uniforms;
lv_memset(&uniforms, 0, sizeof(uniforms));
// *** IMAGE QUALITY UNIFORMS ***********************************************************************
uniforms.exposure = glGetUniformLocation(program, "u_Exposure");
// *** CAMERA/VIEW/PROJECTION/MODEL MATRIX UNIFORMS *************************************************
uniforms.camera = glGetUniformLocation(program, "u_Camera");
uniforms.model_matrix = glGetUniformLocation(program, "u_ModelMatrix");
uniforms.view_projection_matrix = glGetUniformLocation(program, "u_ViewProjectionMatrix");
uniforms.view_matrix = glGetUniformLocation(program, "u_ViewMatrix");
uniforms.projection_matrix = glGetUniformLocation(program, "u_ProjectionMatrix");
// *** IMAGE BASED LIGHTING (IBL) UNIFORMS **********************************************************
uniforms.env_intensity = glGetUniformLocation(program, "u_EnvIntensity");
uniforms.env_diffuse_sampler = glGetUniformLocation(program, "u_LambertianEnvSampler");
uniforms.env_specular_sampler = glGetUniformLocation(program, "u_GGXEnvSampler");
uniforms.env_sheen_sampler = glGetUniformLocation(program, "u_CharlieEnvSampler");
uniforms.env_ggx_lut_sampler = glGetUniformLocation(program, "u_GGXLUT");
uniforms.env_charlie_lut_sampler = glGetUniformLocation(program, "u_CharlieLUT");
uniforms.env_mip_count = glGetUniformLocation(program, "u_MipCount");
// *** BASE COLOR / TEXTURE UNIFORMS ****************************************************************
uniforms.base_color_factor = glGetUniformLocation(program, "u_BaseColorFactor");
uniforms.base_color_sampler = glGetUniformLocation(program, "u_BaseColorSampler");
uniforms.base_color_uv_set = glGetUniformLocation(program, "u_BaseColorUVSet");
uniforms.base_color_uv_transform = glGetUniformLocation(program, "u_BaseColorUVTransform");
// *** CUTOFF / IOR / DISPERSION UNIFORMS ***********************************************************
uniforms.alpha_cutoff = glGetUniformLocation(program, "u_AlphaCutoff");
uniforms.ior = glGetUniformLocation(program, "u_Ior");
uniforms.dispersion = glGetUniformLocation(program, "u_Dispersion");
// *** METALLIC / ROUGHNESS UNIFORMS ****************************************************************
uniforms.metallic_factor = glGetUniformLocation(program, "u_MetallicFactor");
uniforms.roughness_factor = glGetUniformLocation(program, "u_RoughnessFactor");
uniforms.metallic_roughness_sampler = glGetUniformLocation(program, "u_MetallicRoughnessSampler");
uniforms.metallic_roughness_uv_set = glGetUniformLocation(program, "u_MetallicRoughnessUVSet");
uniforms.metallic_roughness_uv_transform = glGetUniformLocation(program, "u_MetallicRoughnessUVTransform");
// *** EMISSION UNIFORMS ****************************************************************************
uniforms.emissive_factor = glGetUniformLocation(program, "u_EmissiveFactor");
uniforms.emissive_sampler = glGetUniformLocation(program, "u_EmissiveSampler");
uniforms.emissive_uv_set = glGetUniformLocation(program, "u_EmissiveUVSet");
uniforms.emissive_uv_transform = glGetUniformLocation(program, "u_EmissiveUVTransform");
uniforms.emissive_strength = glGetUniformLocation(program, "u_EmissiveStrength");
// *** OCCLUSION UNIFORMS ***************************************************************************
uniforms.occlusion_strength = glGetUniformLocation(program, "u_OcclusionStrength");
uniforms.occlusion_sampler = glGetUniformLocation(program, "u_OcclusionSampler");
uniforms.occlusion_uv_set = glGetUniformLocation(program, "u_OcclusionUVSet");
uniforms.occlusion_uv_transform = glGetUniformLocation(program, "u_OcclusionUVTransform");
// *** NORMAL MAP UNIFORMS **************************************************************************
uniforms.normal_sampler = glGetUniformLocation(program, "u_NormalSampler");
uniforms.normal_scale = glGetUniformLocation(program, "u_NormalScale");
uniforms.normal_uv_set = glGetUniformLocation(program, "u_NormalUVSet");
uniforms.normal_uv_transform = glGetUniformLocation(program, "u_NormalUVTransform");
// *** VOLUME / TRANSMISSION UNIFORMS ***************************************************************
uniforms.attenuation_distance = glGetUniformLocation(program, "u_AttenuationDistance");
uniforms.attenuation_color = glGetUniformLocation(program, "u_AttenuationColor");
uniforms.transmission_factor = glGetUniformLocation(program, "u_TransmissionFactor");
uniforms.transmission_sampler = glGetUniformLocation(program, "u_TransmissionSampler");
uniforms.transmission_uv_set = glGetUniformLocation(program, "u_TransmissionUVSet");
uniforms.transmission_uv_transform = glGetUniformLocation(program, "u_TransmissionUVTransform");
uniforms.transmission_framebuffer_sampler = glGetUniformLocation(program, "u_TransmissionFramebufferSampler");
uniforms.transmission_framebuffer_size = glGetUniformLocation(program, "u_TransmissionFramebufferSize");
uniforms.screen_size = glGetUniformLocation(program, "u_ScreenSize");
uniforms.thickness = glGetUniformLocation(program, "u_ThicknessFactor");
uniforms.thickness_sampler = glGetUniformLocation(program, "u_ThicknessSampler");
uniforms.thickness_uv_set = glGetUniformLocation(program, "u_ThicknessUVSet");
uniforms.thickness_uv_transform = glGetUniformLocation(program, "u_ThicknessUVTransform");
// *** CLEARCOAT UNIFORMS ***************************************************************************
uniforms.clearcoat_factor = glGetUniformLocation(program, "u_ClearcoatFactor");
uniforms.clearcoat_roughness_factor = glGetUniformLocation(program, "u_ClearcoatRoughnessFactor");
uniforms.clearcoat_sampler = glGetUniformLocation(program, "u_ClearcoatSampler");
uniforms.clearcoat_uv_set = glGetUniformLocation(program, "u_ClearcoatUVSet");
uniforms.clearcoat_uv_transform = glGetUniformLocation(program, "u_ClearcoatUVTransform");
uniforms.clearcoat_roughness_sampler = glGetUniformLocation(program, "u_ClearcoatRoughnessSampler");
uniforms.clearcoat_roughness_uv_set = glGetUniformLocation(program, "u_ClearcoatRoughnessUVSet");
uniforms.clearcoat_roughness_uv_transform = glGetUniformLocation(program, "u_ClearcoatRoughnessUVTransform");
uniforms.clearcoat_normal_scale = glGetUniformLocation(program, "u_ClearcoatNormalScale");
uniforms.clearcoat_normal_sampler = glGetUniformLocation(program, "u_ClearcoatNormalSampler");
uniforms.clearcoat_normal_uv_set = glGetUniformLocation(program, "u_ClearcoatNormalUVSet");
uniforms.clearcoat_normal_uv_transform = glGetUniformLocation(program, "u_ClearcoatNormalUVTransform");
// *** DIFFUSE TRANSMISSION UNIFORMS ****************************************************************
uniforms.diffuse_transmission_factor = glGetUniformLocation(program, "u_DiffuseTransmissionFactor");
uniforms.diffuse_transmission_sampler = glGetUniformLocation(program, "u_DiffuseTransmissionSampler");
uniforms.diffuse_transmission_uv_set = glGetUniformLocation(program, "u_DiffuseTransmissionUVSet");
uniforms.diffuse_transmission_uv_transform = glGetUniformLocation(program, "u_DiffuseTransmissionUVTransform");
uniforms.diffuse_transmission_color_factor = glGetUniformLocation(program, "u_DiffuseTransmissionColorFactor");
uniforms.diffuse_transmission_color_sampler = glGetUniformLocation(program, "u_DiffuseTransmissionColorSampler");
uniforms.diffuse_transmission_color_uv_set = glGetUniformLocation(program, "u_DiffuseTransmissionColorUVSet");
uniforms.diffuse_transmission_color_uv_transform = glGetUniformLocation(program,
"u_DiffuseTransmissionColorUVTransform");
// *** LEGACY SUPPORT - PBR_SPECULARGLOSS ***********************************************************
uniforms.diffuse_factor = glGetUniformLocation(program, "u_DiffuseFactor");
uniforms.specular_factor = glGetUniformLocation(program, "u_SpecularFactor");
uniforms.glossiness_factor = glGetUniformLocation(program, "u_GlossinessFactor");
uniforms.diffuse_sampler = glGetUniformLocation(program, "u_DiffuseSampler");
uniforms.diffuse_uv_set = glGetUniformLocation(program, "u_DiffuseUVSet");
uniforms.diffuse_uv_transform = glGetUniformLocation(program, "u_DiffuseUVTransform");
uniforms.specular_glossiness_sampler = glGetUniformLocation(program, "u_SpecularGlossinessSampler");
uniforms.specular_glossiness_uv_set = glGetUniformLocation(program, "u_SpecularGlossinessUVSet");
uniforms.specular_glossiness_uv_transform = glGetUniformLocation(program, "u_SpecularGlossinessUVTransform");
// *** [PARTIALLY SUPPORTED / IN DEVELOPMENT] UNIFORMS **********************************************
uniforms.sheen_color_factor = glGetUniformLocation(program, "u_SheenColorFactor");
uniforms.sheen_roughness_factor = glGetUniformLocation(program, "u_SheenRoughnessFactor");
//
uniforms.specular_color_factor = glGetUniformLocation(program, "u_KHR_materials_specular_specularColorFactor");
uniforms.specular_factor = glGetUniformLocation(program, "u_KHR_materials_specular_specularFactor");
//
uniforms.joints_sampler = glGetUniformLocation(program, "u_jointsSampler");
return uniforms;
}
#endif /*LV_USE_GLTF*/
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,55 @@
/**
* @file lv_gltf_view_shader.h
*
*/
#ifndef LV_GLTF_VIEW_SHADER_H
#define LV_GLTF_VIEW_SHADER_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../opengl_shader/lv_opengl_shader_internal.h"
#if LV_USE_GLTF
/*********************
* DEFINES
*********************/
#define GLSL_VERSION_PREFIX "#version 300 es\n"
/**********************
* TYPEDEFS
**********************/
typedef struct {
const lv_opengl_shader_t *shader_list;
size_t count;
} lv_gltf_view_shader_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
char *lv_gltf_view_shader_get_vertex(void);
char *lv_gltf_view_shader_get_fragment(void);
void lv_gltf_view_shader_get_src(lv_gltf_view_shader_t *shaders);
void lv_gltf_view_shader_get_env(lv_gltf_view_shader_t *shaders);
/**********************
* MACROS
**********************/
#endif /*LV_USE_GLTF*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_GLTF_VIEW_SHADER_H*/
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,474 @@
/**
* @file lv_gltf_ibl_sampler.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_gltf_ibl_sampler.h"
#if LV_USE_GLTF
#include "../../../../misc/lv_math.h"
#include "../../../../misc/lv_log.h"
#include "../../../../stdlib/lv_string.h"
#include "../../../../drivers/glfw/lv_opengles_debug.h"
#include "../../opengl_shader/lv_opengl_shader_internal.h"
#include "../lv_gltf_view_internal.h"
#include "../assets/lv_gltf_view_shader.h"
#define STB_IMAGE_IMPLEMENTATION
#include "../../stb_image/stb_image.h"
/*********************
* DEFINES
*********************/
#define INTERNAL_FORMAT GL_RGBA8
#define TEXTURE_TARGET_TYPE GL_UNSIGNED_BYTE
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void ibl_sampler_init(lv_gltf_ibl_sampler_t * sampler);
static void ibl_sampler_load(lv_gltf_ibl_sampler_t * sampler, const char * path);
static void ibl_sampler_filter(lv_gltf_ibl_sampler_t * sampler);
static void ibl_sampler_destroy(lv_gltf_ibl_sampler_t * sampler);
static bool ibl_gl_has_extension(const char * extension);
static void ibl_texture_from_image(lv_gltf_ibl_sampler_t * sampler, lv_gltf_ibl_texture_t * texture,
const lv_gltf_ibl_image_t * image);
static GLuint ibl_load_texture_hdr(lv_gltf_ibl_sampler_t * sampler, const lv_gltf_ibl_image_t * image);
static GLuint ibl_create_cubemap_texture(const lv_gltf_ibl_sampler_t * sampler, bool with_mipmaps);
static uint32_t ibl_create_lut_texture(const lv_gltf_ibl_sampler_t * sampler);
static void ibl_panorama_to_cubemap(const lv_gltf_ibl_sampler_t * sampler);
static void ibl_apply_filter(const lv_gltf_ibl_sampler_t * sampler, uint32_t distribution, float roughness,
uint32_t target_mip_level, GLuint target_texture, uint32_t sample_count, float lod_bias);
static void ibl_cubemap_to_lambertian(const lv_gltf_ibl_sampler_t * sampler);
static void ibl_cubemap_to_ggx(const lv_gltf_ibl_sampler_t * sampler);
static void ibl_cubemap_to_sheen(const lv_gltf_ibl_sampler_t * sampler);
static void ibl_sample_lut(const lv_gltf_ibl_sampler_t * sampler, uint32_t distribution, uint32_t targetTexture,
uint32_t currentTextureSize);
static void ibl_sample_ggx_lut(lv_gltf_ibl_sampler_t * sampler);
static void ibl_sample_charlie_lut(lv_gltf_ibl_sampler_t * sampler);
static int ibl_count_bits(int value);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_gltf_ibl_generate_env_textures(lv_gltf_view_env_textures_t * env, const char * path, float env_rotation)
{
lv_gltf_ibl_sampler_t sampler;
ibl_sampler_init(&sampler);
ibl_sampler_load(&sampler, path);
ibl_sampler_filter(&sampler);
env->angle = env_rotation;
env->diffuse = sampler.lambertian_texture_id;
env->specular = sampler.ggx_texture_id;
env->sheen = sampler.sheen_texture_id;
env->ggxLut = sampler.ggxlut_texture_id;
env->charlie_lut = sampler.charlielut_texture_id;
env->mip_count = sampler.mipmap_levels;
env->ibl_intensity_scale = sampler.scale_value;
ibl_sampler_destroy(&sampler);
}
/**********************
* STATIC FUNCTIONS
**********************/
static void ibl_sampler_init(lv_gltf_ibl_sampler_t * sampler)
{
lv_memset(sampler, 0, sizeof(*sampler));
sampler->texture_size = 128;
sampler->ggx_sample_count = 128;
sampler->lambertian_sample_count = 256;
sampler->sheen_sample_count = 32;
sampler->lod_bias = 0.0;
sampler->lowest_mip_level = 3;
sampler->lut_resolution = 1024;
sampler->lut_sample_count = 64;
sampler->scale_value = 1.0;
lv_gltf_view_shader_t env_shader;
lv_gltf_view_shader_get_env(&env_shader);
sampler->shader_manager = lv_opengl_shader_manager_create(env_shader.shader_list, env_shader.count, NULL, NULL);
}
static void ibl_sampler_load(lv_gltf_ibl_sampler_t * sampler, const char * path)
{
// vv -- WebGL Naming
if(ibl_gl_has_extension("GL_NV_float") && ibl_gl_has_extension("GL_ARB_color_buffer_float")) {
LV_LOG_INFO("Device supports float format textures");
}
// Native naming #2
if(ibl_gl_has_extension("GL_ARB_color_buffer_float") || ibl_gl_has_extension("GL_NV_half_float")) {
LV_LOG_INFO("Device supports half_float format textures");
}
int32_t src_width, src_height, src_nrChannels;
float * data;
if(path != NULL) {
data = stbi_loadf(path, &src_width, &src_height, &src_nrChannels, 3);
}
else {
extern unsigned char pisa_jpg[];
extern unsigned int pisa_jpg_len;
data = stbi_loadf_from_memory(pisa_jpg, pisa_jpg_len, &src_width, &src_height, &src_nrChannels, 3);
}
{
lv_gltf_ibl_image_t panorama_image = {
.data = (float *)lv_malloc(src_width * src_height * 3 * sizeof(float)),
.data_len = src_width * src_height * 3,
.width = src_width,
.height = src_height,
};
LV_ASSERT_MALLOC(panorama_image.data);
lv_memcpy(panorama_image.data, data, panorama_image.data_len * sizeof(*panorama_image.data));
stbi_image_free(data);
sampler->input_texture_id = ibl_load_texture_hdr(sampler, &panorama_image);
lv_free(panorama_image.data);
}
GL_CALL(glGenFramebuffers(1, &sampler->framebuffer));
GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, sampler->framebuffer));
sampler->cubemap_texture_id = ibl_create_cubemap_texture(sampler, true);
sampler->lambertian_texture_id = ibl_create_cubemap_texture(sampler, false);
sampler->ggx_texture_id = ibl_create_cubemap_texture(sampler, true);
sampler->sheen_texture_id = ibl_create_cubemap_texture(sampler, true);
GL_CALL(glBindTexture(GL_TEXTURE_CUBE_MAP, sampler->ggx_texture_id));
GL_CALL(glGenerateMipmap(GL_TEXTURE_CUBE_MAP));
GL_CALL(glBindTexture(GL_TEXTURE_CUBE_MAP, sampler->sheen_texture_id));
GL_CALL(glGenerateMipmap(GL_TEXTURE_CUBE_MAP));
sampler->mipmap_levels = ibl_count_bits(sampler->texture_size) + 1 - sampler->lowest_mip_level;
}
static void ibl_sampler_filter(lv_gltf_ibl_sampler_t * sampler)
{
GLint prev_framebuffer;
GL_CALL(glGetIntegerv(GL_FRAMEBUFFER_BINDING, &prev_framebuffer));
ibl_panorama_to_cubemap(sampler);
GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, prev_framebuffer));
ibl_cubemap_to_lambertian(sampler);
GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, prev_framebuffer));
ibl_cubemap_to_ggx(sampler);
GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, prev_framebuffer));
ibl_cubemap_to_sheen(sampler);
GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, prev_framebuffer));
ibl_sample_ggx_lut(sampler);
GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, prev_framebuffer));
ibl_sample_charlie_lut(sampler);
GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, prev_framebuffer));
}
static void ibl_sampler_destroy(lv_gltf_ibl_sampler_t * sampler)
{
lv_opengl_shader_manager_destroy(sampler->shader_manager);
}
static void ibl_texture_from_image(lv_gltf_ibl_sampler_t * sampler, lv_gltf_ibl_texture_t * texture,
const lv_gltf_ibl_image_t * image)
{
const size_t src_format_bpp = 3;
const size_t dst_format_bpp = 4;
texture->internal_format = INTERNAL_FORMAT;
texture->format = GL_RGBA;
texture->type = TEXTURE_TARGET_TYPE;
size_t pixel_num = image->data_len / src_format_bpp;
texture->data = (uint8_t *)lv_malloc(pixel_num * 4);
LV_ASSERT_MALLOC(texture->data);
float max_value = 0.0;
float clamped_sum = 0.0;
float diff_sum = 0.0;
size_t src = 0;
size_t dst = 0;
for(size_t i = 0; i < pixel_num; i++) {
const float r = image->data[src + 0];
const float g = image->data[src + 1];
const float b = image->data[src + 2];
const float max_component = LV_MAX(LV_MAX(r, g), b);
if(max_component > 1.0) {
diff_sum += max_component - 1.0;
}
clamped_sum += LV_MIN(max_component, 1.0f);
max_value = LV_MAX(max_component, max_value);
texture->data[dst + 0] = LV_MIN(r * 255, 255);
texture->data[dst + 1] = LV_MIN(g * 255, 255);
texture->data[dst + 2] = LV_MIN(b * 255, 255);
texture->data[dst + 3] = 0xFF;
src += src_format_bpp;
dst += dst_format_bpp;
}
float scale_factor = 1.0;
if(clamped_sum > 1.0) {
// Apply global scale factor to compensate for intensity lost when clamping
scale_factor = (clamped_sum + diff_sum) / clamped_sum;
LV_LOG_INFO("HDR Intensity Scale %f\n", scale_factor);
}
sampler->scale_value = scale_factor;
}
static uint32_t ibl_load_texture_hdr(lv_gltf_ibl_sampler_t * sampler, const lv_gltf_ibl_image_t * image)
{
lv_gltf_ibl_texture_t texture;
ibl_texture_from_image(sampler, &texture, image);
GLuint texture_id;
GL_CALL(glGenTextures(1, &texture_id));
GL_CALL(glBindTexture(GL_TEXTURE_2D, texture_id));
GL_CALL(glTexImage2D(GL_TEXTURE_2D, // target
0, // level
texture.internal_format, image->width, image->height,
0, // border
texture.format, // format of the pixel data
texture.type, // type of the pixel data
texture.data));
lv_free(texture.data);
GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT));
GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT));
GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
return texture_id;
}
static GLuint ibl_create_cubemap_texture(const lv_gltf_ibl_sampler_t * sampler, bool with_mipmaps)
{
uint32_t targetTexture;
GL_CALL(glGenTextures(1, &targetTexture));
GL_CALL(glBindTexture(GL_TEXTURE_CUBE_MAP, targetTexture));
for(int32_t i = 0; i < 6; ++i) {
GL_CALL(glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, INTERNAL_FORMAT, sampler->texture_size,
sampler->texture_size, 0, GL_RGBA, TEXTURE_TARGET_TYPE, NULL));
}
if(with_mipmaps) {
GL_CALL(glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR));
}
else {
GL_CALL(glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
}
GL_CALL(glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
GL_CALL(glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
GL_CALL(glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
return targetTexture;
}
static GLuint ibl_create_lut_texture(const lv_gltf_ibl_sampler_t * sampler)
{
GLuint texture;
GL_CALL(glGenTextures(1, &texture));
GL_CALL(glBindTexture(GL_TEXTURE_2D, texture));
GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, INTERNAL_FORMAT, sampler->lut_resolution, sampler->lut_resolution, 0, GL_RGBA,
TEXTURE_TARGET_TYPE, NULL));
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));
return texture;
}
static void ibl_panorama_to_cubemap(const lv_gltf_ibl_sampler_t * sampler)
{
for(int32_t i = 0; i < 6; ++i) {
GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, sampler->framebuffer));
GL_CALL(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i,
sampler->cubemap_texture_id, 0));
GL_CALL(glBindTexture(GL_TEXTURE_CUBE_MAP, sampler->cubemap_texture_id));
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
while(status != GL_FRAMEBUFFER_COMPLETE) {
status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
LV_LOG_ERROR("Environnement render error not complete. Expected %d. Got %d", GL_FRAMEBUFFER_COMPLETE,
status);
}
GL_CALL(glViewport(0, 0, sampler->texture_size, sampler->texture_size));
GL_CALL(glClearColor(1.0, 0.0, 0.0, 0.0));
GL_CALL(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
uint32_t frag_shader =
lv_opengl_shader_manager_select_shader(sampler->shader_manager, "panorama_to_cubemap.frag", NULL, 0);
uint32_t vert_shader = lv_opengl_shader_manager_select_shader(sampler->shader_manager, "fullscreen.vert", NULL, 0);
lv_opengl_shader_program_t * program =
lv_opengl_shader_manager_get_program(sampler->shader_manager, frag_shader, vert_shader);
GLuint program_id = lv_opengl_shader_program_get_id(program);
GL_CALL(glUseProgram(program_id));
GL_CALL(glActiveTexture(GL_TEXTURE0 + 0));
// Bind texture ID to active texture
GL_CALL(glBindTexture(GL_TEXTURE_2D, sampler->input_texture_id));
// map shader uniform to texture unit (TEXTURE0)
GLuint location;
GL_CALL(location = glGetUniformLocation(program_id, "u_panorama"));
GL_CALL(glUniform1i(location, 0));
program->update_uniform_1i(program, "u_currentFace", i);
//fullscreen triangle
GL_CALL(glDrawArrays(GL_TRIANGLES, 0, 3));
}
GL_CALL(glBindTexture(GL_TEXTURE_CUBE_MAP, sampler->cubemap_texture_id));
GL_CALL(glGenerateMipmap(GL_TEXTURE_CUBE_MAP));
}
static void ibl_apply_filter(const lv_gltf_ibl_sampler_t * sampler, uint32_t distribution, float roughness,
uint32_t target_mip_level, GLuint target_texture, uint32_t sample_count, float lod_bias)
{
uint32_t current_texture_size = sampler->texture_size >> target_mip_level;
for(uint32_t i = 0; i < 6; ++i) {
GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, sampler->framebuffer));
GL_CALL(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i,
target_texture, target_mip_level));
GL_CALL(glBindTexture(GL_TEXTURE_CUBE_MAP, target_texture));
GL_CALL(glViewport(0, 0, current_texture_size, current_texture_size));
GL_CALL(glClearColor(0.0, 1.0, 0.0, 0.0));
GL_CALL(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
uint32_t frag_shader =
lv_opengl_shader_manager_select_shader(sampler->shader_manager, "ibl_filtering.frag", NULL, 0);
uint32_t vert_shader = lv_opengl_shader_manager_select_shader(sampler->shader_manager, "fullscreen.vert", NULL, 0);
lv_opengl_shader_program_t * program =
lv_opengl_shader_manager_get_program(sampler->shader_manager, frag_shader, vert_shader);
GLuint program_id = lv_opengl_shader_program_get_id(program);
GL_CALL(glUseProgram(program_id));
GL_CALL(glActiveTexture(GL_TEXTURE0));
// Bind texture ID to active texture
GL_CALL(glBindTexture(GL_TEXTURE_CUBE_MAP, sampler->cubemap_texture_id));
// map shader uniform to texture unit (TEXTURE0)
uint32_t location = glGetUniformLocation(program_id, "u_cubemapTexture");
GL_CALL(glUniform1i(location, 0)); // texture unit 0
program->update_uniform_1f(program, "u_roughness", roughness);
program->update_uniform_1i(program, "u_sampleCount", sample_count);
/* Software rendered mode looks better with this and horrible with below */
/*program->update_uniform_1i(program, "u_width", current_texture_size); */
/* Standard mode looks best with this and somewhat worse with above */
program->update_uniform_1i(program, "u_width", sampler->texture_size);
program->update_uniform_1f(program, "u_lodBias", lod_bias);
program->update_uniform_1i(program, "u_distribution", distribution);
program->update_uniform_1i(program, "u_currentFace", i);
program->update_uniform_1i(program, "u_isGeneratingLUT", 0);
program->update_uniform_1i(program, "u_floatTexture", 0);
program->update_uniform_1f(program, "u_intensityScale", sampler->scale_value);
//fullscreen triangle
GL_CALL(glDrawArrays(GL_TRIANGLES, 0, 3));
}
}
static void ibl_cubemap_to_lambertian(const lv_gltf_ibl_sampler_t * sampler)
{
ibl_apply_filter(sampler, 0, 0.0, 0, sampler->lambertian_texture_id, sampler->lambertian_sample_count, 0.0);
}
static void ibl_cubemap_to_ggx(const lv_gltf_ibl_sampler_t * sampler)
{
LV_ASSERT(sampler->mipmap_levels != 1);
for(uint32_t current_mip_level = 0; current_mip_level <= sampler->mipmap_levels; ++current_mip_level) {
float roughness = (current_mip_level) / (float)(sampler->mipmap_levels - 1);
ibl_apply_filter(sampler, 1, roughness, current_mip_level, sampler->ggx_texture_id, sampler->ggx_sample_count,
0.0);
}
}
static void ibl_cubemap_to_sheen(const lv_gltf_ibl_sampler_t * sampler)
{
LV_ASSERT(sampler->mipmap_levels != 1);
for(uint32_t current_mip_level = 0; current_mip_level <= sampler->mipmap_levels; ++current_mip_level) {
float roughness = (current_mip_level) / (float)(sampler->mipmap_levels - 1);
ibl_apply_filter(sampler, 2, roughness, current_mip_level, sampler->sheen_texture_id,
sampler->sheen_sample_count, 0.0);
}
}
static void ibl_sample_lut(const lv_gltf_ibl_sampler_t * sampler, uint32_t distribution, uint32_t targetTexture,
uint32_t currentTextureSize)
{
GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, sampler->framebuffer));
GL_CALL(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, targetTexture, 0));
GL_CALL(glBindTexture(GL_TEXTURE_2D, targetTexture));
GL_CALL(glViewport(0, 0, currentTextureSize, currentTextureSize));
GL_CALL(glClearColor(0.0, 1.0, 1.0, 0.0));
GL_CALL(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
uint32_t frag_shader = lv_opengl_shader_manager_select_shader(sampler->shader_manager, "ibl_filtering.frag", NULL, 0);
uint32_t vert_shader = lv_opengl_shader_manager_select_shader(sampler->shader_manager, "fullscreen.vert", NULL, 0);
lv_opengl_shader_program_t * program = lv_opengl_shader_manager_get_program(sampler->shader_manager, frag_shader,
vert_shader);
GLuint program_id = lv_opengl_shader_program_get_id(program);
GL_CALL(glUseProgram(program_id));
// TEXTURE0 = active.
GL_CALL(glActiveTexture(GL_TEXTURE0 + 0));
// Bind texture ID to active texture
GL_CALL(glBindTexture(GL_TEXTURE_CUBE_MAP, sampler->cubemap_texture_id));
// map shader uniform to texture unit (TEXTURE0)
uint32_t location = glGetUniformLocation(program_id, "u_cubemapTexture");
GL_CALL(glUniform1i(location, 0)); // texture unit 0
program->update_uniform_1f(program, "u_roughness", 0.0);
program->update_uniform_1i(program, "u_sampleCount", sampler->lut_sample_count);
//shader->update_uniform_1i( shader, "u_sampleCount", 512);
program->update_uniform_1i(program, "u_width", 0.0);
program->update_uniform_1f(program, "u_lodBias", 0.0);
program->update_uniform_1i(program, "u_distribution", distribution);
program->update_uniform_1i(program, "u_currentFace", 0);
program->update_uniform_1i(program, "u_isGeneratingLUT", 1);
//fullscreen triangle
GL_CALL(glDrawArrays(GL_TRIANGLES, 0, 3));
}
static void ibl_sample_ggx_lut(lv_gltf_ibl_sampler_t * sampler)
{
sampler->ggxlut_texture_id = ibl_create_lut_texture(sampler);
ibl_sample_lut(sampler, 1, sampler->ggxlut_texture_id, sampler->lut_resolution);
}
static void ibl_sample_charlie_lut(lv_gltf_ibl_sampler_t * sampler)
{
sampler->charlielut_texture_id = ibl_create_lut_texture(sampler);
ibl_sample_lut(sampler, 2, sampler->charlielut_texture_id, sampler->lut_resolution);
}
static bool ibl_gl_has_extension(const char * extension)
{
int32_t extension_count;
glGetIntegerv(GL_NUM_EXTENSIONS, &extension_count);
for(uint32_t i = 0; i < (uint32_t)extension_count; i++) {
const GLubyte * curr_extension = glGetStringi(GL_EXTENSIONS, i);
if(lv_streq((const char *)curr_extension, extension)) {
return true;
}
}
return false;
}
static int ibl_count_bits(int value)
{
int count = 0;
while(value > 1) {
value >>= 1;
count++;
}
return count;
}
#endif /*LV_USE_GLTF*/
@@ -0,0 +1,92 @@
/**
* @file lv_gltf_ibl_sampler.h
*
*/
#ifndef LV_GLTF_IBL_SAMPLER_H
#define LV_GLTF_IBL_SAMPLER_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../../lv_conf_internal.h"
#if LV_USE_GLTF
#include "../../../../misc/lv_types.h"
#include "../../opengl_shader/lv_opengl_shader_internal.h"
#include "../lv_gltf_view_internal.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
uint32_t texture_size;
float lod_bias;
uint32_t lowest_mip_level;
uint32_t input_texture_id;
uint32_t cubemap_texture_id;
uint32_t framebuffer;
uint32_t mipmap_count;
uint32_t lambertian_texture_id;
uint32_t lambertian_sample_count;
uint32_t ggx_sample_count;
uint32_t ggx_texture_id;
uint32_t sheen_texture_id;
uint32_t sheen_sample_count;
uint32_t ggxlut_texture_id;
uint32_t lut_sample_count;
uint32_t lut_resolution;
uint32_t charlielut_texture_id;
float scale_value;
uint32_t mipmap_levels;
lv_opengl_shader_manager_t * shader_manager;
} lv_gltf_ibl_sampler_t;
typedef struct {
uint8_t * data;
uint32_t internal_format;
uint32_t format;
uint32_t type;
} lv_gltf_ibl_texture_t;
typedef struct {
float * data;
size_t data_len;
uint32_t width;
uint32_t height;
} lv_gltf_ibl_image_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
void lv_gltf_ibl_generate_env_textures(lv_gltf_view_env_textures_t * env, const char * env_file_path,
float env_rotation);
/**********************
* MACROS
**********************/
#endif /*LV_USE_GLTF*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_GLTF_IBL_SAMPLER_H*/
+351
View File
@@ -0,0 +1,351 @@
/**
* @file lv_gltf.h
*
*/
#ifndef LV_GLTF_H
#define LV_GLTF_H
/*********************
* INCLUDES
*********************/
#include "../../../lv_conf_internal.h"
#if LV_USE_GLTF
#include "../../../misc/lv_types.h"
#include "../../../misc/lv_color.h"
#include "../gltf_data/lv_gltf_model.h"
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef enum {
LV_GLTF_AA_MODE_OFF = 0, /** Anti aliasing off*/
LV_GLTF_AA_MODE_ON = 1, /** Anti aliasing on*/
LV_GLTF_AA_MODE_DYNAMIC = 2, /** Anti aliasing on only when frame has no movement*/
} lv_gltf_aa_mode_t;
typedef enum {
LV_GLTF_BG_MODE_SOLID = 0, /** Solid background. Use `lv_obj_set_style_bg_color` to set the background color*/
LV_GLTF_BG_MODE_ENVIRONMENT = 1, /** Environnement background*/
} lv_gltf_bg_mode_t;
#define LV_GLTF_ANIM_SPEED_TENTH 100
#define LV_GLTF_ANIM_SPEED_QUARTER 250
#define LV_GLTF_ANIM_SPEED_HALF 500
#define LV_GLTF_ANIM_SPEED_NORMAL 1000
#define LV_GLTF_ANIM_SPEED_2X 2000
#define LV_GLTF_ANIM_SPEED_3X 3000
#define LV_GLTF_ANIM_SPEED_4X 4000
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a GLTF viewer object
* @param parent pointer to the parent object
* @return pointer to the created GLTF viewer object
*/
lv_obj_t * lv_gltf_create(lv_obj_t * parent);
/**
* Load a GLTF model from a file into the viewer
* @param obj pointer to a GLTF viewer object
* @param path file path to the GLTF model to load
* @return pointer to the loaded GLTF model, or NULL on failure
*/
lv_gltf_model_t * lv_gltf_load_model_from_file(lv_obj_t * obj, const char * path);
/**
* Get the number of models loaded in the GLTF viewer
* @param obj pointer to a GLTF viewer object
* @return the total number of models in the viewer
*/
size_t lv_gltf_get_model_count(lv_obj_t * obj);
/**
* Get a specific model by its index
* @param obj pointer to a GLTF viewer object
* @param id index of the model to retrieve (0-based)
* @return pointer to the model at the specified index, or NULL if index is invalid
*/
lv_gltf_model_t * lv_gltf_get_model_by_index(lv_obj_t * obj, size_t id);
/**
* Get the primary model from the GLTF viewer
* The primary model is the first model added to the viewer and can be used
* for camera selection and other primary operations
* @param obj pointer to a GLTF viewer object
* @return pointer to the primary model, or NULL if no models are loaded
*/
lv_gltf_model_t * lv_gltf_get_primary_model(lv_obj_t * obj);
/**
* Set the yaw (horizontal rotation) of the camera
* @param obj pointer to a GLTF viewer object
* @param yaw yaw angle in degrees
*/
void lv_gltf_set_yaw(lv_obj_t * obj, float yaw);
/**
* Get the yaw (horizontal rotation) of the camera
* @param obj pointer to a GLTF viewer object
* @return yaw angle in degrees
*/
float lv_gltf_get_yaw(const lv_obj_t * obj);
/**
* Set the pitch (vertical rotation) of the camera
* @param obj pointer to a GLTF viewer object
* @param pitch pitch angle in degrees
*/
void lv_gltf_set_pitch(lv_obj_t * obj, float pitch);
/**
* Get the pitch (vertical rotation) of the camera
* @param obj pointer to a GLTF viewer object
* @return pitch angle in degrees
*/
float lv_gltf_get_pitch(const lv_obj_t * obj);
/**
* Set the camera distance from the focal point
* @param obj pointer to a GLTF viewer object
* @param value distance value
*/
void lv_gltf_set_distance(lv_obj_t * obj, float value);
/**
* Get the camera distance from the focal point
* @param obj pointer to a GLTF viewer object
* @return distance value
*/
float lv_gltf_get_distance(const lv_obj_t * obj);
/**********************
* Viewport Functions
**********************/
/**
* Set the field of view
* @param obj pointer to a GLTF viewer object
* @param value vertical FOV in degrees. If zero, the view will be orthographic (non-perspective)
*/
void lv_gltf_set_fov(lv_obj_t * obj, float value);
/**
* Get the field of view
* @param obj pointer to a GLTF viewer object
* @return vertical FOV in degrees
*/
float lv_gltf_get_fov(const lv_obj_t * obj);
/**********************
* Focal Point Functions
**********************/
/**
* Set the X coordinate of the camera focal point
* @param obj pointer to a GLTF viewer object
* @param value X coordinate
*/
void lv_gltf_set_focal_x(lv_obj_t * obj, float value);
/**
* Get the X coordinate of the camera focal point
* @param obj pointer to a GLTF viewer object
* @return X coordinate
*/
float lv_gltf_get_focal_x(const lv_obj_t * obj);
/**
* Set the Y coordinate of the camera focal point
* @param obj pointer to a GLTF viewer object
* @param value Y coordinate
*/
void lv_gltf_set_focal_y(lv_obj_t * obj, float value);
/**
* Get the Y coordinate of the camera focal point
* @param obj pointer to a GLTF viewer object
* @return Y coordinate
*/
float lv_gltf_get_focal_y(const lv_obj_t * obj);
/**
* Set the Z coordinate of the camera focal point
* @param obj pointer to a GLTF viewer object
* @param value Z coordinate
*/
void lv_gltf_set_focal_z(lv_obj_t * obj, float value);
/**
* Get the Z coordinate of the camera focal point
* @param obj pointer to a GLTF viewer object
* @return Z coordinate
*/
float lv_gltf_get_focal_z(const lv_obj_t * obj);
/**
* Set the focal coordinates to the center point of the model object
* @param obj pointer to a GLTF viewer object
* @param model a model attached to this viewer or NULL for the first model
*/
void lv_gltf_recenter(lv_obj_t * obj, lv_gltf_model_t * model);
/**********************
* Scene Control Functions
**********************/
/**
* Set the active camera index
* The camera is selected from the first GLTF model added to the viewer
*
* @param obj pointer to a GLTF viewer object
* @param value camera index (0 for default camera, 1+ for scene camera index)
* @note Values higher than the scene's camera count will be clamped to the maximum available camera index
*/
void lv_gltf_set_camera(lv_obj_t * obj, uint32_t value);
/**
* Get the active camera index
* @param obj pointer to a GLTF viewer object
* @return active camera index
*/
uint32_t lv_gltf_get_camera(const lv_obj_t * obj);
/**
* Get the number of cameras in the first GLTF model added to the viewer
* This count represents the valid range for the camera index parameter
* used with lv_gltf_set_camera()
*
* To get the camera count of other models, call
* lv_gltf_model_get_camera_count(model) directly with the specific model
*
* @param obj pointer to a GLTF viewer object
* @return number of available cameras
*/
uint32_t lv_gltf_get_camera_count(const lv_obj_t * obj);
/**
* Set the animation speed ratio
*
* The actual ratio is the value parameter / LV_GLTF_ANIM_SPEED_NORMAL
* Values greater than LV_GLTF_ANIM_SPEED_NORMAL will speed-up the animation
* Values less than LV_GLTF_ANIM_SPEED_NORMAL will slow down the animation
*
* @param obj pointer to a GLTF viewer object
* @param value speed-up ratio of the animation
*/
void lv_gltf_set_animation_speed(lv_obj_t * obj, uint32_t value);
/**
* Get the animation speed ratio
*
* The actual ratio is the return value / LV_GLTF_ANIM_SPEED_NORMAL
*
* @param obj pointer to a GLTF viewer object
*/
uint32_t lv_gltf_get_animation_speed(const lv_obj_t * obj);
/**********************
* Visual Settings Functions
**********************/
/**
* Set the background mode
* @param obj pointer to a GLTF viewer object
* @param value background mode
*/
void lv_gltf_set_background_mode(lv_obj_t * obj, lv_gltf_bg_mode_t value);
/**
* Get the background mode
* @param obj pointer to a GLTF viewer object
* @return background mode
*/
lv_gltf_bg_mode_t lv_gltf_get_background_mode(const lv_obj_t * obj);
/**
* Set the background blur amount
* @param obj pointer to a GLTF viewer object
* @param value blur amount between 0 and 100
*/
void lv_gltf_set_background_blur(lv_obj_t * obj, uint32_t value);
/**
* Get the background blur amount
* @param obj pointer to a GLTF viewer object
* @return blur amount between 0 and 100
*/
uint32_t lv_gltf_get_background_blur(const lv_obj_t * obj);
/**
* Set the environmental brightness/power
* @param obj pointer to a GLTF viewer object
* @param value brightness multiplier
*/
void lv_gltf_set_env_brightness(lv_obj_t * obj, uint32_t value);
/**
* Get the environmental brightness/power
* @param obj pointer to a GLTF viewer object
* @return brightness multiplier
*/
uint32_t lv_gltf_get_env_brightness(const lv_obj_t * obj);
/**
* Set the image exposure level
* @param obj pointer to a GLTF viewer object
* @param value exposure level (1.0 is default)
*/
void lv_gltf_set_image_exposure(lv_obj_t * obj, float value);
/**
* Get the image exposure level
* @param obj pointer to a GLTF viewer object
* @return exposure level
*/
float lv_gltf_get_image_exposure(const lv_obj_t * obj);
/**********************
* Rendering Functions
**********************/
/**
* Set the anti-aliasing mode
* @param obj pointer to a GLTF viewer object
* @param value anti-aliasing mode
*/
void lv_gltf_set_antialiasing_mode(lv_obj_t * obj, lv_gltf_aa_mode_t value);
/**
* Get the anti-aliasing mode
* @param obj pointer to a GLTF viewer object
* @return anti-aliasing mode
*/
lv_gltf_aa_mode_t lv_gltf_get_antialiasing_mode(const lv_obj_t * obj);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
}
#endif
#endif /*LV_USE_GLTF*/
#endif /*LV_GLTF_H*/
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,138 @@
/**
* @file lv_gltf_view_internal.h
*
*/
#ifndef LV_GLTF_VIEW_INTERNAL_H
#define LV_GLTF_VIEW_INTERNAL_H
/*********************
* INCLUDES
*********************/
#include "../../../lv_conf_internal.h"
#if LV_USE_GLTF
#include "lv_gltf.h"
#include "../../../misc/lv_types.h"
#include "../opengl_shader/lv_opengl_shader_internal.h"
#include "../../../widgets/3dtexture/lv_3dtexture_private.h"
#include "../gltf_data/lv_gltf_data_internal.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
#ifdef __cplusplus
extern "C" {
#endif/* __cplusplus*/
typedef struct {
uint32_t texture;
uint32_t renderbuffer;
unsigned framebuffer;
} lv_gltf_renwin_state_t;
typedef struct {
lv_gltf_renwin_state_t render_state;
lv_gltf_renwin_state_t opaque_render_state;
uint64_t opaque_frame_buffer_width;
uint64_t opaque_frame_buffer_height;
uint32_t material_variant;
bool render_state_ready;
bool render_opaque_buffer;
} lv_gltf_view_state_t;
typedef struct {
float pitch;
float yaw;
float distance;
float fov; // The vertical FOV, in degrees. If this is zero, the view will be orthographic (non-perspective)
int32_t render_width; // If anti-aliasing is not applied this frame, these are the same as width/height, if antialiasing
int32_t render_height; // is enabled, these are width/height * antialias upscale power (currently 2.0)
float focal_x;
float focal_y;
float focal_z;
bool frame_was_antialiased;
int32_t animation_speed_ratio;
lv_gltf_aa_mode_t aa_mode;
lv_gltf_bg_mode_t bg_mode;
float blur_bg; /** How much to blur the environment background, between 0.0 and 1.0 */
float env_pow; /** Environmental brightness, 1.8 by default */
float exposure; /** Image exposure level, 1.0 default */
} lv_gltf_view_desc_t;
typedef struct {
GLboolean blend_enabled;
GLint blend_src;
GLint blend_dst;
GLint blend_equation;
GLfloat clear_depth;
GLfloat clear_color[4];
} lv_opengl_state_t;
typedef struct {
uint32_t diffuse;
uint32_t specular;
uint32_t sheen;
uint32_t ggxLut;
uint32_t charlie_lut;
uint32_t mip_count;
float ibl_intensity_scale;
float angle;
} lv_gltf_view_env_textures_t;
#ifdef __cplusplus
}
#include <fastgltf/math.hpp>
#include <fastgltf/types.hpp>
#include <map>
struct _lv_gltf_t {
lv_3dtexture_t texture;
lv_array_t models;
lv_gltf_view_state_t state;
lv_gltf_view_desc_t desc;
lv_gltf_view_desc_t last_desc;
lv_opengl_shader_manager_t * shader_manager;
lv_gltf_view_env_textures_t env_textures;
fastgltf::math::fmat4x4 view_matrix;
fastgltf::math::fmat4x4 projection_matrix;
fastgltf::math::fmat4x4 view_projection_matrix;
fastgltf::math::fvec3 camera_pos;
std::map<int32_t, std::map<fastgltf::Node *, fastgltf::math::fmat4x4>> ibm_by_skin_then_node;
};
/**********************
* GLOBAL PROTOTYPES
**********************/
GLuint lv_gltf_view_render(lv_gltf_t * viewer);
lv_result_t lv_gltf_view_shader_injest_discover_defines(lv_array_t * result, lv_gltf_model_t * data,
fastgltf::Node * node,
fastgltf::Primitive * prim);
lv_gltf_shaderset_t lv_gltf_view_shader_compile_program(lv_gltf_t * view, const lv_opengl_shader_define_t * defines,
size_t n);
/**********************
* MACROS
**********************/
#endif/* __cplusplus*/
#endif /*LV_USE_GLTF*/
#endif /*LV_GLTF_VIEW_INTERNAL_H*/
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,396 @@
/**
* @file lv_gltf_view_shader.cpp
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_gltf_view_internal.h"
#if LV_USE_GLTF
#include "fastgltf/types.hpp"
#include "../gltf_data/lv_gltf_data_internal.hpp"
#include "../gltf_data/lv_gltf_data_internal.h"
#include "../opengl_shader/lv_opengl_shader_internal.h"
#include "../../../misc/lv_array.h"
#include "../../../misc/lv_assert.h"
#include "../../../misc/lv_types.h"
#include "../../../stdlib/lv_sprintf.h"
#include "../../../stdlib/lv_string.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static lv_result_t add_define(lv_array_t * array, const char * defsymbol, const char * value, bool value_allocated);
static lv_result_t add_define_if_primitive_attribute_exists(lv_array_t * array, const fastgltf::Asset & asset,
const fastgltf::Primitive * primitive, const char * attribute,
const char * define);
static lv_result_t add_texture_defines_impl(lv_array_t * array, const fastgltf::TextureInfo & material_prop,
const char * define,
const char * uv_define);
static lv_result_t add_texture_defines(lv_array_t * array,
const fastgltf::Optional<fastgltf::TextureInfo> & material_prop,
const char * define, const char * uv_define);
static lv_result_t add_texture_defines(lv_array_t * array,
const fastgltf::Optional<fastgltf::NormalTextureInfo> & material_prop,
const char * define, const char * uv_define);
static lv_result_t add_texture_defines(lv_array_t * array,
const fastgltf::Optional<fastgltf::OcclusionTextureInfo> & material_prop,
const char * define, const char * uv_define);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_result_t lv_gltf_view_shader_injest_discover_defines(lv_array_t * result, lv_gltf_model_t * data,
fastgltf::Node * node,
fastgltf::Primitive * prim)
{
const auto & asset = data->asset;
if(add_define(result, "_OPAQUE", "0", false) == LV_RESULT_INVALID) {
return LV_RESULT_INVALID;
}
if(add_define(result, "_MASK", "1", false) == LV_RESULT_INVALID) {
return LV_RESULT_INVALID;
}
if(add_define(result, "_BLEND", "2", false) == LV_RESULT_INVALID) {
return LV_RESULT_INVALID;
}
LV_ASSERT_MSG(prim->findAttribute("POSITION") != prim->attributes.end(),
"A mesh primitive is required to hold the POSITION attribute");
LV_ASSERT_MSG(prim->indicesAccessor.has_value(),
"We specify fastgltf::Options::GenerateMeshIndices, so we should always have indices");
if(!prim->materialIndex.has_value()) {
if(add_define(result, "ALPHAMODE", "_OPAQUE", false) == LV_RESULT_INVALID) {
return LV_RESULT_INVALID;
}
}
else {
const auto & material = asset.materials[prim->materialIndex.value()];
if(add_define(result, "TONEMAP_KHR_PBR_NEUTRAL", NULL, false) == LV_RESULT_INVALID) {
return LV_RESULT_INVALID;
}
if(material.unlit) {
if(add_define(result, "MATERIAL_UNLIT", NULL, false) == LV_RESULT_INVALID) {
return LV_RESULT_INVALID;
}
if(add_define(result, "LINEAR_OUTPUT", NULL, false) == LV_RESULT_INVALID) {
return LV_RESULT_INVALID;
}
}
else {
if(add_define(result, "MATERIAL_METALLICROUGHNESS", NULL, false) == LV_RESULT_INVALID) {
return LV_RESULT_INVALID;
}
if(add_define(result, "LINEAR_OUTPUT", NULL, false) == LV_RESULT_INVALID) {
return LV_RESULT_INVALID;
}
}
const size_t light_count = data->node_by_light_index.size();
if(add_define(result, "USE_IBL", NULL, false) == LV_RESULT_INVALID) {
return LV_RESULT_INVALID;
}
if(light_count > 10) {
LV_LOG_ERROR("Too many scene lights, max is 10");
}
else if(light_count > 0) {
if(add_define(result, "USE_PUNCTUAL", NULL, false) == LV_RESULT_INVALID) {
return LV_RESULT_INVALID;
}
char * count = (char *) lv_zalloc(5);
lv_snprintf(count, 5, "%zu", light_count);
if(add_define(result, "LIGHT_COUNT", count, true) == LV_RESULT_INVALID) {
return LV_RESULT_INVALID;
}
}
else {
if(add_define(result, "LIGHT_COUNT", "0", false) == LV_RESULT_INVALID) {
return LV_RESULT_INVALID;
}
}
// only set cutoff value for mask material
if(material.alphaMode == fastgltf::AlphaMode::Mask) {
if(add_define(result, "ALPHAMODE", "_MASK", false) == LV_RESULT_INVALID) {
return LV_RESULT_INVALID;
}
}
else if(material.alphaMode == fastgltf::AlphaMode::Opaque) {
if(add_define(result, "ALPHAMODE", "_OPAQUE", false) == LV_RESULT_INVALID) {
return LV_RESULT_INVALID;
}
}
else {
if(add_define(result, "ALPHAMODE", "_BLEND", false) == LV_RESULT_INVALID) {
return LV_RESULT_INVALID;
}
}
if(add_texture_defines(result, material.pbrData.baseColorTexture, "HAS_BASE_COLOR_MAP",
"HAS_BASECOLOR_UV_TRANSFORM") == LV_RESULT_INVALID) {
return LV_RESULT_INVALID;
}
if(add_texture_defines(result, material.pbrData.metallicRoughnessTexture, "HAS_METALLIC_ROUGHNESS_MAP",
"HAS_METALLICROUGHNESS_UV_TRANSFORM") == LV_RESULT_INVALID) {
return LV_RESULT_INVALID;
}
if(add_texture_defines(result, material.occlusionTexture, "HAS_OCCLUSION_MAP", "HAS_OCCLUSION_UV_TRANSFORM") ==
LV_RESULT_INVALID) {
return LV_RESULT_INVALID;
}
if(add_texture_defines(result, material.normalTexture, "HAS_NORMAL_MAP", "HAS_NORMAL_UV_TRANSFORM") ==
LV_RESULT_INVALID) {
return LV_RESULT_INVALID;
}
if(add_texture_defines(result, material.emissiveTexture, "HAS_EMISSIVE_MAP", "HAS_EMISSIVE_UV_TRANSFORM") ==
LV_RESULT_INVALID) {
return LV_RESULT_INVALID;
}
if(add_define(result, "MATERIAL_EMISSIVE_STRENGTH", NULL, false) == LV_RESULT_INVALID) {
return LV_RESULT_INVALID;
}
if(material.sheen)
if(add_define(result, "MATERIAL_SHEEN", NULL, false) == LV_RESULT_INVALID) {
return LV_RESULT_INVALID;
}
if(material.specular)
if(add_define(result, "MATERIAL_SPECULAR", NULL, false) == LV_RESULT_INVALID) {
return LV_RESULT_INVALID;
}
if(material.specularGlossiness) {
if(add_define(result, "MATERIAL_SPECULARGLOSSINESS", NULL, false) == LV_RESULT_INVALID) {
return LV_RESULT_INVALID;
}
if(add_texture_defines(result, material.specularGlossiness->diffuseTexture, "HAS_DIFFUSE_MAP",
"HAS_DIFFUSE_UV_TRANSFORM")) {
return LV_RESULT_INVALID;
}
if(add_texture_defines(result, material.specularGlossiness->specularGlossinessTexture,
"HAS_SPECULARGLOSSINESS_MAP", "HAS_SPECULARGLOSSINESS_UV_TRANSFORM")) {
return LV_RESULT_INVALID;
}
}
if(material.transmission) {
if(add_define(result, "MATERIAL_TRANSMISSION", NULL, false) == LV_RESULT_INVALID) {
return LV_RESULT_INVALID;
}
if(add_define(result, "MATERIAL_DISPERSION", NULL, false) == LV_RESULT_INVALID) {
return LV_RESULT_INVALID;
}
if(add_define(result, "MATERIAL_VOLUME", NULL, false) == LV_RESULT_INVALID) {
return LV_RESULT_INVALID;
}
if(material.transmission->transmissionTexture.has_value())
if(add_define(result, "HAS_TRANSMISSION_MAP", NULL, false) == LV_RESULT_INVALID) {
return LV_RESULT_INVALID;
}
if(material.volume) {
add_texture_defines(result, material.volume->thicknessTexture, "HAS_THICKNESS_MAP",
"HAS_THICKNESS_UV_TRANSFORM");
}
}
if(material.clearcoat) {
if(add_define(result, "MATERIAL_CLEARCOAT", NULL, false) == LV_RESULT_INVALID) {
return LV_RESULT_INVALID;
}
if(add_texture_defines(result, material.clearcoat->clearcoatTexture, "HAS_CLEARCOAT_MAP",
"HAS_CLEARCOAT_UV_TRANSFORM") == LV_RESULT_INVALID) {
return LV_RESULT_INVALID;
}
if(add_texture_defines(result, material.clearcoat->clearcoatRoughnessTexture,
"HAS_CLEARCOAT_ROUGHNESS_MAP",
"HAS_CLEARCOATROUGHNESS_UV_TRANSFORM") == LV_RESULT_INVALID) {
return LV_RESULT_INVALID;
}
if(add_texture_defines(result, material.clearcoat->clearcoatNormalTexture, "HAS_CLEARCOAT_NORMAL_MAP",
"HAS_CLEARCOATNORMAL_UV_TRANSFORM") == LV_RESULT_INVALID) {
return LV_RESULT_INVALID;
}
}
if(material.diffuseTransmission) {
if(add_define(result, "MATERIAL_DIFFUSE_TRANSMISSION", NULL, false) == LV_RESULT_INVALID) {
return LV_RESULT_INVALID;
}
if(material.diffuseTransmission->diffuseTransmissionTexture.has_value()) {
if(add_define(result, "HAS_DIFFUSE_TRANSMISSION_MAP", NULL, false) == LV_RESULT_INVALID) {
return LV_RESULT_INVALID;
}
}
if(material.diffuseTransmission->diffuseTransmissionColorTexture.has_value()) {
if(add_define(result, "HAS_DIFFUSE_TRANSMISSION_COLOR_MAP", NULL, false) == LV_RESULT_INVALID) {
return LV_RESULT_INVALID;
}
}
}
}
if(add_define_if_primitive_attribute_exists(result, asset, prim, "NORMAL", "HAS_NORMAL_VEC3") == LV_RESULT_INVALID) {
return LV_RESULT_INVALID;
}
if(add_define_if_primitive_attribute_exists(result, asset, prim, "TANGENT", "HAS_TANGENT_VEC4") == LV_RESULT_INVALID) {
return LV_RESULT_INVALID;
}
if(add_define_if_primitive_attribute_exists(result, asset, prim, "TEXCOORD_0", "HAS_TEXCOORD_0_VEC2") ==
LV_RESULT_INVALID) {
return LV_RESULT_INVALID;
}
if(add_define_if_primitive_attribute_exists(result, asset, prim, "TEXCOORD_1", "HAS_TEXCOORD_1_VEC2") ==
LV_RESULT_INVALID) {
return LV_RESULT_INVALID;
}
if(add_define_if_primitive_attribute_exists(result, asset, prim, "JOINTS_0",
"HAS_JOINTS_0_VEC4") == LV_RESULT_INVALID) {
return LV_RESULT_INVALID;
}
if(add_define_if_primitive_attribute_exists(result, asset, prim, "JOINTS_1",
"HAS_JOINTS_1_VEC4") == LV_RESULT_INVALID) {
return LV_RESULT_INVALID;
}
if(add_define_if_primitive_attribute_exists(result, asset, prim, "WEIGHTS_0", "HAS_WEIGHTS_0_VEC4") ==
LV_RESULT_INVALID) {
return LV_RESULT_INVALID;
}
if(add_define_if_primitive_attribute_exists(result, asset, prim, "WEIGHTS_1", "HAS_WEIGHTS_1_VEC4") ==
LV_RESULT_INVALID) {
return LV_RESULT_INVALID;
}
const auto * joints0it = prim->findAttribute("JOINTS_0");
const auto * weights0it = prim->findAttribute("WEIGHTS_0");
if((node->skinIndex.has_value()) && (joints0it != prim->attributes.end()) && (weights0it != prim->attributes.end())) {
if(add_define(result, "USE_SKINNING", NULL, false) == LV_RESULT_INVALID) {
return LV_RESULT_INVALID;
}
}
return LV_RESULT_OK;
}
/**
* @brief Compile and load shaders.
*
* This function compiles and loads the shaders from the specified shader cache, preparing them
* for use in rendering operations. It returns a structure containing the shader set information.
*
* @param shaders Pointer to the lv_opengl_shader_cache_t structure containing the shader cache.
* @return A gl_renwin_shaderset_t structure representing the compiled and loaded shaders.
*/
lv_gltf_shaderset_t lv_gltf_view_shader_compile_program(lv_gltf_t * view, const lv_opengl_shader_define_t * defines,
size_t n)
{
uint32_t frag_shader_hash = lv_opengl_shader_manager_select_shader(view->shader_manager, "__MAIN__.frag",
defines, n);
uint32_t vert_shader_hash = lv_opengl_shader_manager_select_shader(view->shader_manager, "__MAIN__.vert",
defines, n);
lv_opengl_shader_program_t * program =
lv_opengl_shader_manager_get_program(view->shader_manager, frag_shader_hash, vert_shader_hash);
LV_ASSERT_NULL(program);
GLuint program_id = lv_opengl_shader_program_get_id(program);
GL_CALL(glUseProgram(program_id));
lv_gltf_shaderset_t shader_prog;
shader_prog.program = program_id;
return shader_prog;
}
/**********************
* STATIC FUNCTIONS
**********************/
static lv_result_t add_define(lv_array_t * array, const char * name, const char * value, bool value_allocated)
{
const size_t n = lv_array_size(array);
for(size_t i = 0; i < n; ++i) {
lv_opengl_shader_define_t * define = (lv_opengl_shader_define_t *)lv_array_at(array, i);
if(lv_streq(define->name, name)) {
return LV_RESULT_OK;
}
}
lv_opengl_shader_define_t entry = { name, value, value_allocated };
return lv_array_push_back(array, &entry);
}
static lv_result_t add_define_if_primitive_attribute_exists(lv_array_t * array, const fastgltf::Asset & asset,
const fastgltf::Primitive * primitive, const char * attribute,
const char * define)
{
const auto & it = primitive->findAttribute(attribute);
if(it == primitive->attributes.end() || !asset.accessors[it->accessorIndex].bufferViewIndex.has_value()) {
return LV_RESULT_OK;
}
return add_define(array, define, NULL, false);
}
static lv_result_t add_texture_defines_impl(lv_array_t * array, const fastgltf::TextureInfo & material_prop,
const char * define,
const char * uv_define)
{
if(add_define(array, define, NULL, false) == LV_RESULT_INVALID) {
return LV_RESULT_INVALID;
}
if(!material_prop.transform) {
return LV_RESULT_OK;
}
return add_define(array, uv_define, NULL, false);
}
static lv_result_t add_texture_defines(lv_array_t * array,
const fastgltf::Optional<fastgltf::TextureInfo> & material_prop,
const char * define, const char * uv_define)
{
if(!material_prop.has_value()) {
return LV_RESULT_OK;
}
return add_texture_defines_impl(array, material_prop.value(), define, uv_define);
}
static lv_result_t add_texture_defines(lv_array_t * array,
const fastgltf::Optional<fastgltf::NormalTextureInfo> & material_prop,
const char * define, const char * uv_define)
{
if(!material_prop.has_value()) {
return LV_RESULT_OK;
}
return add_texture_defines_impl(array, material_prop.value(), define, uv_define);
}
static lv_result_t add_texture_defines(lv_array_t * array,
const fastgltf::Optional<fastgltf::OcclusionTextureInfo> & material_prop,
const char * define, const char * uv_define)
{
if(!material_prop.has_value()) {
return LV_RESULT_OK;
}
return add_texture_defines_impl(array, material_prop.value(), define, uv_define);
}
#endif /*LV_USE_GLTF*/
+78
View File
@@ -0,0 +1,78 @@
/**
* @file lv_gltf_math.cpp
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_gltf_math.hpp"
#if LV_USE_GLTF
#include <fastgltf/math.hpp>
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/** Creates a right-handed view matrix */
fastgltf::math::fmat4x4 lv_gltf_math_look_at_rh(const fastgltf::math::fvec3 & eye, const fastgltf::math::fvec3 & center,
const fastgltf::math::fvec3 & up) noexcept
{
auto dir = normalize(center - eye);
auto lft = normalize(cross(dir, up));
auto rup = cross(lft, dir);
fastgltf::math::fmat4x4 ret(1.f);
ret.col(0) = { lft.x(), rup.x(), -dir.x(), 0.f };
ret.col(1) = { lft.y(), rup.y(), -dir.y(), 0.f };
ret.col(2) = { lft.z(), rup.z(), -dir.z(), 0.f };
ret.col(3) = { -dot(lft, eye), -dot(rup, eye), dot(dir, eye), 1.f };
return ret;
}
/**
* Creates a right-handed perspective matrix, with the near and far clips at -1 and +1, respectively.
* @param fov The FOV in radians
*/
[[nodiscard]] fastgltf::math::fmat4x4 lv_gltf_math_perspective_rh(float fov, float ratio, float z_near,
float z_far) noexcept
{
fastgltf::math::fmat4x4 ret(0.f);
auto tanHalfFov = std::tan(fov / 2.f);
ret.col(0).x() = 1.f / (ratio * tanHalfFov);
ret.col(1).y() = 1.f / tanHalfFov;
ret.col(2).z() = -(z_far + z_near) / (z_far - z_near);
ret.col(2).w() = -1.f;
ret.col(3).z() = -(2.f * z_far * z_near) / (z_far - z_near);
return ret;
}
/**********************
* STATIC FUNCTIONS
**********************/
#endif /*LV_USE_GLTF*/

Some files were not shown because too many files have changed in this diff Show More