3D 模型 (3D Model)

Lite3D 是由 Realtek 自主研发的轻量级、跨平台的 3D 图形渲染库,支持软件渲染与硬件加速扩展,具有以下特点:

  • 轻量级:代码体积小,便于集成。

  • 跨平台:适配 HoneyGUI、LVGL 等 GUI 框架,支持 FreeRTOS 和 Zephyr 环境运行。

  • 灵活渲染:提供软件渲染管线,支持扩展 GPU 加速接口。

Lite3D 支持加载由 .obj.mtl 文件组成的 3D 模型,能够处理模型的几何形状和材质信息,并支持为模型添加丰富的动画特效以增强视觉表现力。该引擎的工作流程如下:

https://foruda.gitee.com/images/1755252209828076518/c91dad94_13408154.png

Lite3D 工作流程

3D 模型组成要素

完整的 3D 模型包含三个核心组件:

  1. .obj 文件

    • 存储几何数据,包括:

      • 顶点坐标

      • 法线向量

      • 纹理坐标(UV 映射)

      • 面定义

    • 需引用 .mtl 文件中的材质信息。

  2. .mtl 文件(材质库)

    • 定义表面属性,包括:

      • 环境光/漫反射/镜面反射颜色

      • 折射率

      • 透明度

      • 光照模型

      • 纹理贴图引用

  3. 纹理图片

    • 通常为 PNG 格式,用于:

      • 漫反射贴图

      • 法线贴图

      • 高光贴图

      • 透明贴图

https://foruda.gitee.com/images/1735113754178839767/916a3f95_13408154.png

3D 模型组成示例

3D 模型预处理

在绘制 3D 模型前,需要将其转换为二进制格式。以下是处理流程:

  1. 定位转换工具

    • 在 HoneyGUI 安装目录下找到以下工具:

      • your_HoneyGUI_dir/tool/3D-tool/png2c.py

      • your_HoneyGUI_dir/tool/3D-tool/extract_desc.exe

  2. 准备模型目录

    • 将上述工具复制到模型目录。

    • 该模型目录确保包含:

      • .obj 文件

      • .mtl 文件

      • 所有引用的纹理图片

  3. 生成描述文件

    • 使用提取器处理模型: extract_desc.exe xxx.obj,该可执行文件会自动调用 png2c.py 将所有的 PNG 纹理转换为二进制数组。

    https://foruda.gitee.com/images/1735540370568112173/cf1c0126_13408154.png

    脚本处理

    • 生成的 desc.txtdesc.bin 文件包含以下内容:

      • obj 解析数据

      • mtl 解析数据

      • 内嵌纹理数据

    https://foruda.gitee.com/images/1735114445910760790/2a41eeab_13408154.png

    生成二进制数组

3D 模型生成

创建模型

调用 Lite3D 库中的 l3_create_model(void *desc_addr, L3_DRAW_TYPE draw_type, int16_t x, int16_t y, int16_t view_w, int16_t view_h) 函数创建 3D 模型,导入的 desc_addr 文件即为脚本中提取的解析数据, draw_type 为模型绘制方式,支持以下三种方式:

  • L3_DRAW_FRONT_ONLY:仅绘制模型的正面,适用于需要隐藏背面的场景,如蝴蝶模型。

  • L3_DRAW_FRONT_AND_BACK:绘制模型的正面和背面,适用于需要双面可见的场景,如棱镜模型。

  • L3_DRAW_FRONT_AND_SORT:绘制模型的正面并排序,适用于前后景遮挡的场景,如人脸模型。

变换控制

全局变换

使用 l3_set_global_transform(l3_model_t *_this, l3_global_transform_cb cb) 函数对 3D 模型进行整体变换,其中 l3_global_transform_cb 类型的回调函数可以为物体的所有面设置相同的形状变换。例如 world 世界坐标变换和 camera 相机视角投影。

典型应用场景:

  • 模型整体旋转/平移

  • 场景视角切换

相机变换

初始化函数为 l3_camera_UVN_initialize(l3_camera_t *camera, l3_4d_point_t cameraPosition, l3_4d_point_t cameraTarget, float near, float far, float fov, float viewPortWidth, float viewPortHeight)

  • camera:指向相机结构体的指针,用于初始化相机的属性。

  • cameraPosition:相机在世界坐标系中的位置。

  • cameraTarget:相机所指向的目标点,即相机视线的焦点。

  • near:近裁剪平面距离,定义了相机截取视锥体的近端平面到相机的距离,所有靠近相机而小于这个距离的物体将被裁剪掉。

  • far:远裁剪平面距离,定义了视锥体远端平面到相机的距离,所有远离相机而大于这个距离的物体将被裁剪掉。

  • fov:视野范围,通常以垂直角度(单位:度)表示,定义了相机的开阔程度,即相机视锥体的张开角度。

  • viewPortWidth:视口的宽度,定义渲染目标或窗口的横向尺寸。

  • viewPortHeight:视口的高度,定义渲染目标或窗口的纵向尺寸。

作用:

  1. 相机变换定义了观察者在场景中的位置和方向,它将世界坐标系转换到摄像机坐标系。

  2. 通过操作相机,可以实现不同的视角,例如平移摄像机位置和改变观察方向。

世界变换

初始化函数为 l3_world_initialize(l3_4x4_matrix_t *world, float x, float y, float z, float rotX, float rotY, float rotZ, float scale)

  • world:指向世界变换矩阵的指针,将 3D 对象从模型坐标系转换到世界坐标系。

  • x:沿 X 轴进行平移的距离,用以确定对象在世界坐标系中 X 方向上的位置。

  • y:沿 Y 轴进行平移的距离,用以确定对象在世界坐标系中 Y 方向上的位置。

  • z:沿 Z 轴进行平移的距离,用以确定对象在世界坐标系中 Z 方向上的位置。

  • rotX:绕 X 轴旋转的角度(单位:度)。

  • rotY:绕 Y 轴旋转的角度(单位:度)。

  • rotZ:绕 Z 轴旋转的角度(单位:度)。

  • scale:统一缩放系数,用于在各个方向上等比例地缩放对象。

作用:

  1. 世界变换定义了模型在世界坐标系中的位置和方向,负责将模型坐标系转换到世界坐标系。

  2. 通过世界变换矩阵可以实现模型整体的平移、旋转和缩放。

面变换

使用 l3_set_face_transform(l3_model_t *_this, l3_face_transform_cb cb) 函数对 3D 模型进行局部变换,其中 l3_face_transform_cb 类型的回调函数可以为物体的每个面设置不同的形状变换, face_index 为指定变换的面。

功能特点:

  • 支持按面片索引独立控制。

  • 使用 l3_calculator_4x4_matrix(l3_4x4_matrix_t *matrix, float tx, float ty, float tz, l3_4d_point_t point, l3_4d_vector_t vector, float degrees, float scale) 为每个面生成不同的矩阵以实现个性化的局部动画。

3D 模型绘制

创建控件

HoneyGUI 中已集成了 Lite3D 库,并封装成 gui_lite3d 控件,使用 gui_lite3d_create() 函数可以创建 3D 模型控件。

设置点击事件

gui_lite3d_on_click() 函数可以为 3D 模型控件设置点击事件,当用户点击模型时,会触发回调函数。

设置动画

gui_obj_create_timer() 函数可以为 3D 模型控件设置动画属性,其中 callback 为动画更新的回调函数。

示例

3D 蝴蝶

该模型由 8 个矩形面构成,每个面都有相应的纹理贴图,绘制方式为 L3_DRAW_FRONT_ONLY。通过调用 l3_set_face_transform(l3_model_t *_this, l3_face_transform_cb cb) 函数,可以为不同的面设置局部变换,从而实现动画效果。

/*============================================================================*
 *                        Header Files
 *============================================================================*/
#include "root_image/ui_resource.h"
#include "gui_img.h"
#include "gui_win.h"
#include "gui_text.h"
#include "time.h"
#include "tp_algo.h"
#include <math.h>
#include "app_main_watch.h"
#include "gui_view.h"
#include "gui_lite3d.h"

/*============================================================================*
 *                            Macros
 *============================================================================*/
#define CURRENT_VIEW_NAME "butterfly_view"

/*============================================================================*
 *                           Function Declaration
 *============================================================================*/
static void butterfly_app(gui_view_t *view);

/*============================================================================*
 *                            Variables
 *============================================================================*/
/* View Management */
static gui_view_t *current_view = NULL;
const static gui_view_descriptor_t *menu_view = NULL;

static gui_view_descriptor_t const descriptor =
{
    /* change Here for current view */
    .name = (const char *)CURRENT_VIEW_NAME,
    .pView = &current_view,
    .on_switch_in = butterfly_app,
};

/* Animation Variables */
static float wing_angle = 0.0f;
static float butterfly_x = 0.0f;
static float butterfly_y = 0.0f;
static float butterfly_z = 0.0f;
static float butterfly_rz = 0.0f;

bool is_moving_to_target = false;
static float target_dx = 0.0f;
static float target_dy = 0.0f;
static float source_dx = 0.0f;
static float source_dy = 0.0f;
static const float move_speed = 0.02f;
static float wing_time = 0.0f;
static float total_flight_distance = 0.0f;
static float flight_progress = 1.0f;

/*============================================================================*
 *                           Private Functions
 *============================================================================*/
static int gui_view_descriptor_register_init(void)
{
    gui_view_descriptor_register(&descriptor);
    gui_log("File: %s, Function: %s\n", __FILE__, __func__);
    return 0;
}
static GUI_INIT_VIEW_DESCRIPTOR_REGISTER(gui_view_descriptor_register_init);

static int gui_view_get_other_view_descriptor_init(void)
{
    /* you can get other view descriptor point here */
    menu_view = gui_view_descriptor_get("menu_view");
    gui_log("File: %s, Function: %s\n", __FILE__, __func__);
    return 0;
}
static GUI_INIT_VIEW_DESCRIPTOR_GET(gui_view_get_other_view_descriptor_init);


static void update_animation(void *param)
{
    touch_info_t *tp = tp_get_info();
    gui_dispdev_t *dc = gui_get_dc();
    (void)param;

    if (tp->pressed)
    {
        target_dx = (tp->x - dc->screen_width / 2) / 4.0f;
        target_dy = (tp->y - dc->screen_height / 2) / 4.0f;
        is_moving_to_target = true;

        float dx = target_dx - source_dx;
        float dy = target_dy - source_dy;

        total_flight_distance = sqrtf(dx * dx + dy * dy);
        flight_progress = 0.0f;
    }

    if (is_moving_to_target)
    {
        float current_dx = target_dx - source_dx;
        float current_dy = target_dy - source_dy;

        float remaining_distance = sqrtf(current_dx * current_dx + current_dy * current_dy);
        flight_progress = fminf(1.0f - (remaining_distance / total_flight_distance), 1.0f);

        if (flight_progress < 0.8f)
        {
            // Acceleration and deceleration
            // float speed_factor = fminf(remaining_distance / 40.0f, 1.0f);
            source_dx += current_dx * move_speed * (1.0f - flight_progress);
            source_dy += current_dy * move_speed * (1.0f - flight_progress);

            // Caculate new rotate angle
            float desired_angle = atan2f(current_dy, current_dx) * (180.0f / M_PI_F) + 90;
            float angle_difference = desired_angle - butterfly_rz;

            if (angle_difference > 180.0f)
            {
                angle_difference -= 360.0f;
            }
            if (angle_difference < -180.0f)
            {
                angle_difference += 360.0f;
            }
            butterfly_rz += angle_difference * 0.1f;

            // Adjust wing flapping frequency based on speed
            wing_time += 0.2f + (1.0f - flight_progress) * 0.2f;
            wing_angle = 60.0f * sinf(wing_time);

            butterfly_x = source_dx;
            butterfly_y = source_dy;

            butterfly_z = 30.0f * (4.0f * flight_progress * (0.8f - flight_progress));
        }
        else
        {
            is_moving_to_target = false;
        }
    }
    else
    {
        wing_time += 0.1f;
        wing_angle = 50.0f * sinf(wing_time);
        butterfly_z = -5.0f * sinf(wing_time);
    }

}

static void butterfly_global_cb(l3_model_t *this)
{
    l3_camera_UVN_initialize(&this->camera, l3_4d_point(0, 0, 0), l3_4d_point(0, 0, 45), 1,
                             32767,
                             90, this->viewPortWidth, this->viewPortHeight);

    l3_world_initialize(&this->world, butterfly_x, butterfly_y, 45.0f - butterfly_z, 0, 0, butterfly_rz,
                        5);

}

static l3_4x4_matrix_t butterfly_face_cb(l3_model_t *this, size_t face_index/*face offset*/)
{
    l3_4x4_matrix_t face_matrix;
    l3_4x4_matrix_t transform_matrix;

    if (face_index == 0 || face_index == 2)
    {
        l3_calculator_4x4_matrix(&face_matrix, 0, 0, 0, l3_4d_point(0, 0, 0), l3_4d_vector(0, 1, 0),
                                 wing_angle, 1);
    }
    else if (face_index == 1 || face_index == 3)
    {
        l3_calculator_4x4_matrix(&face_matrix, 0, 0, 0, l3_4d_point(0, 0, 0), l3_4d_vector(0, 1, 0),
                                 -wing_angle, 1);
    }
    else
    {
        l3_calculator_4x4_matrix(&face_matrix, 0, 0, 0, l3_4d_point(0, 0, 0), l3_4d_vector(0, 1, 0), 0,
                                 1);
    }

    l3_4x4_matrix_mul(&this->world, &face_matrix, &transform_matrix);

    return transform_matrix;

}

static void butterfly_app(gui_view_t *view)
{
    gui_obj_t *obj = GUI_BASE(view);
    gui_view_switch_on_event(view, menu_view, SWITCH_OUT_ANIMATION_FADE,
                             SWITCH_IN_ANIMATION_FADE,
                             GUI_EVENT_KB_SHORT_CLICKED);


    l3_model_t *butterfly_3d = l3_create_model(DESC_BUTTERFLY_BIN, L3_DRAW_FRONT_ONLY, 0, 0, 410, 502);

    l3_set_global_transform(butterfly_3d, (l3_global_transform_cb)butterfly_global_cb);
    l3_set_face_transform(butterfly_3d, (l3_face_transform_cb)butterfly_face_cb);

    gui_lite3d_t *lite3d_butterfly = gui_lite3d_create(obj, "lite3d-widget", butterfly_3d,
                                                       0, 0, 410, 502);

    gui_obj_create_timer(GUI_BASE(lite3d_butterfly), 10, true, update_animation);
}


3D 人脸

该模型由 1454 个三角形面构成,使用默认材质基础色进行填充,绘制方式为 L3_DRAW_FRONT_AND_SORT

/*============================================================================*
 *                        Header Files
 *============================================================================*/
#include "root_image/ui_resource.h"
#include "gui_img.h"
#include "gui_win.h"
#include "gui_text.h"
#include "time.h"
#include "tp_algo.h"
#include <math.h>
#include "app_main_watch.h"
#include "gui_view.h"
#include "gui_lite3d.h"

/*============================================================================*
 *                            Macros
 *============================================================================*/
#define CURRENT_VIEW_NAME "face_view"

/*============================================================================*
 *                           Function Declaration
 *============================================================================*/
static void face_app(gui_view_t *view);

/*============================================================================*
 *                            Variables
 *============================================================================*/
/* View Management */
static gui_view_t *current_view = NULL;
const static gui_view_descriptor_t *menu_view = NULL;
static gui_view_descriptor_t const descriptor =
{
    /* change Here for current view */
    .name = (const char *)CURRENT_VIEW_NAME,
    .pView = &current_view,
    .on_switch_in = face_app,
};

/* Animation Variables */
static float rot_angle = 5.0f;

/*============================================================================*
 *                           Private Functions
 *============================================================================*/
static int gui_view_descriptor_register_init(void)
{
    gui_view_descriptor_register(&descriptor);
    gui_log("File: %s, Function: %s\n", __FILE__, __func__);
    return 0;
}
static GUI_INIT_VIEW_DESCRIPTOR_REGISTER(gui_view_descriptor_register_init);

static int gui_view_get_other_view_descriptor_init(void)
{
    /* you can get other view descriptor point here */
    menu_view = gui_view_descriptor_get("menu_view");
    gui_log("File: %s, Function: %s\n", __FILE__, __func__);
    return 0;
}
static GUI_INIT_VIEW_DESCRIPTOR_GET(gui_view_get_other_view_descriptor_init);


static void update_face_animation(void *param)
{
    touch_info_t *tp = tp_get_info();
    (void)param;

    if (tp->pressed || tp->pressing)
    {
        if (tp->x > 20 && tp->x < 390)
        {
            rot_angle += tp->deltaX / 5.0f;
        }

    }
}

static void face_global_cb(l3_model_t *this)
{
    l3_camera_UVN_initialize(&this->camera, l3_4d_point(0, 0, 0), l3_4d_point(0, 0, 65), 1,
                             32767,
                             90, this->viewPortWidth, this->viewPortHeight);

    l3_world_initialize(&this->world, 0, 22, 65, 0, rot_angle, 0, 5);

}

static void face_app(gui_view_t *view)
{
    gui_obj_t *obj = GUI_BASE(view);
    gui_view_switch_on_event(view, menu_view, SWITCH_OUT_ANIMATION_FADE,
                             SWITCH_IN_ANIMATION_FADE,
                             GUI_EVENT_KB_SHORT_CLICKED);

    l3_model_t *face_3d = l3_create_model(DESC_FACE_BIN, L3_DRAW_FRONT_AND_SORT, 0, 0, 410, 502);
    l3_set_global_transform(face_3d, (l3_global_transform_cb)face_global_cb);

    gui_lite3d_t *lite3d_face = gui_lite3d_create(obj, "lite3d-widget", face_3d,
                                                  0, 0, 410, 502);

    gui_obj_create_timer(GUI_BASE(lite3d_face), 10, true, update_face_animation);


}


3D 小狗

menu_config.h 中开启宏定义 CONFIG_REALTEK_BUILD_REAL_DOG_3D 来运行此示例。该模型由 774 个三角形面组成,支持自定义材质基础色的填充。

#include "guidef.h"
#include "gui_img.h"
#include "gui_obj.h"
#include "string.h"
#include "stdio.h"
#include "stdlib.h"
#include "gui_server.h"
#include "gui_components_init.h"

#include "gui_lite3d.h"
#include "tp_algo.h"
#include "dog3d/desc.txt"

static float rot_angle = 0.0f;

static void update_dog_animation()
{
    touch_info_t *tp = tp_get_info();

    if (tp->pressed || tp->pressing)
    {
        rot_angle += tp->deltaX / 5.0f;
    }
}

static void dog_global_cb(l3_model_t *this)
{
    l3_camera_UVN_initialize(&this->camera, l3_4d_point(0, 0, 0), l3_4d_point(0, 3, 30), 1, 32767,
                             90,  this->viewPortWidth, this->viewPortHeight);

    l3_world_initialize(&this->world, 0, 15, 30, 0, rot_angle, 0, 5);
}

static int app_init(void)
{
    l3_model_t *dog_3d = l3_create_model((void *)_acdesc, L3_DRAW_FRONT_AND_SORT, 50, 50, 380, 380);

    l3_set_global_transform(dog_3d, (l3_global_transform_cb)dog_global_cb);

    gui_lite3d_t *lite3d_dog = gui_lite3d_create(gui_obj_get_root(), "lite3d-widget", dog_3d,
                                                 0, 0, 480, 480);

    gui_obj_create_timer(GUI_BASE(lite3d_dog), 17, true, update_dog_animation);


    return 0;

}



3D 应用列表

该界面由 6 个 3D 应用图标组成,通过调用 l3_set_face_image(l3_model_t *_this, uint8_t face_index, void *image_addr) 函数可以为 3D 模型的指定面替换纹理贴图。

/*============================================================================*
 *                        Header Files
 *============================================================================*/
#include "root_image/ui_resource.h"
#include "gui_img.h"
#include "gui_win.h"
#include "gui_text.h"
#include "time.h"
#include "tp_algo.h"
#include <math.h>
#include "app_main_watch.h"
#include "gui_view.h"
#include "gui_lite3d.h"

/*============================================================================*
 *                            Macros
 *============================================================================*/
#define CURRENT_VIEW_NAME "applist_view"
#define APP_NUM 6

/*============================================================================*
*                             Types
*============================================================================*/
typedef struct
{
    int16_t pos_x;
    int16_t pos_y;
} Position;

/*============================================================================*
 *                           Function Declaration
 *============================================================================*/
static void applist_app(gui_view_t *view);

/*============================================================================*
 *                            Variables
 *============================================================================*/
/* View Management */
static gui_view_t *current_view = NULL;
const static gui_view_descriptor_t *menu_view = NULL;

static gui_view_descriptor_t const descriptor =
{
    /* change Here for current view */
    .name = (const char *)CURRENT_VIEW_NAME,
    .pView = &current_view,
    .on_switch_in = applist_app,
};

/* 3D Applications List */
static gui_lite3d_t *app_3d_list[APP_NUM];  // Static array
static const Position app_positions[APP_NUM] =
{
    {0, 50},
    {100, 50},
    {200, 50},
    {0, 200},
    {100, 200},
    {200, 200}
};

/* Animation Variables */
static float rot_y_angle = 0.0f;   // Current angle
static float velocity = 0.0f;       // Angular velocity
static bool isAnimating = false;    // Whether animation is ongoing
static const float amplitude = 30.0f;     // Initial amplitude
static const float damping =
    0.95f;        // Damping coefficient (between 0.9~0.99, higher values result in longer oscillation)
static const float frequency =
    0.2f;      // Oscillation frequency (higher values result in faster oscillation)

static bool click_amplify = false;
static float click_on_rot_y = 0.0f;
static float click_on_shift_z = 0.0f;

/*============================================================================*
 *                           Private Functions
 *============================================================================*/
static int gui_view_descriptor_register_init(void)
{
    gui_view_descriptor_register(&descriptor);
    gui_log("File: %s, Function: %s\n", __FILE__, __func__);
    return 0;
}
static GUI_INIT_VIEW_DESCRIPTOR_REGISTER(gui_view_descriptor_register_init);

static int gui_view_get_other_view_descriptor_init(void)
{
    /* you can get other view descriptor point here */
    menu_view = gui_view_descriptor_get("menu_view");
    gui_log("File: %s, Function: %s\n", __FILE__, __func__);
    return 0;
}
static GUI_INIT_VIEW_DESCRIPTOR_GET(gui_view_get_other_view_descriptor_init);


static void update_applist_animation(void *param)
{
    touch_info_t *tp = tp_get_info();
    (void)param;

    if (tp->pressed)
    {
        isAnimating = true;        // Start animation
        rot_y_angle = 0.0f;        // Reset angle
        velocity = amplitude * frequency; // Initial velocity
    }

    if (isAnimating)
    {
        // Harmonic motion + damping decay
        float acceleration = -frequency * frequency *
                             rot_y_angle; // Acceleration (towards equilibrium point)
        velocity += acceleration;  // Update velocity
        velocity *= damping;      // Apply damping decay
        rot_y_angle += velocity;   // Update angle

        // Stop animation when oscillation is small enough
        if (fabsf(rot_y_angle) < 0.1f && fabsf(velocity) < 0.1f)
        {
            rot_y_angle = 0.0f;    // Ensure complete stop
            velocity = 0.0f;
            isAnimating = false;
        }
    }
}

static void applist_global_cb(l3_model_t *this)
{
    l3_camera_UVN_initialize(&this->camera, l3_4d_point(0, 0, 0), l3_4d_point(0, 0, 15), 1,
                             32767,
                             90, this->viewPortWidth, this->viewPortHeight);

    l3_world_initialize(&this->world, 0, 0, 15, 0, 0, 0, 5);
}

static l3_4x4_matrix_t applist_face_cb(l3_model_t *this, size_t face_index)
{
    (void)face_index;
    l3_4x4_matrix_t face_matrix;
    l3_4x4_matrix_t transform_matrix;

    l3_calculator_4x4_matrix(&face_matrix, 0, 0, 0, l3_4d_point(0, 0, 0), l3_4d_vector(0, 1, 0),
                             rot_y_angle, 1);

    l3_4x4_matrix_mul(&this->world, &face_matrix, &transform_matrix);

    return transform_matrix;
}

static void app_click_on_cb(l3_model_t *this)
{
    if (click_on_rot_y < 720.f)
    {
        click_on_rot_y += 10.0f;
    }
    if (click_on_shift_z < 5.0f)
    {
        click_on_shift_z += 0.2f;
    }
    this->x = 0;
    this->y = 46;
    this->viewPortWidth = 410;
    this->viewPortHeight = 410;
    l3_camera_UVN_initialize(&this->camera, l3_4d_point(0, 0, 0), l3_4d_point(0, 0, 15), 1,
                             32767,
                             90, this->viewPortWidth, this->viewPortHeight);

    l3_world_initialize(&this->world, 0, 0, 15.0f - click_on_shift_z, 0, click_on_rot_y, 0, 5);
}

static int get_app_index(gui_lite3d_t *target)
{
    for (int i = 0; i < APP_NUM; i++)
    {
        if (app_3d_list[i] == target)
        {
            return i;
        }
    }
    return -1;
}

static void gui_app_switch(gui_lite3d_t *this)
{
    gui_log("clicked app: %s\n", this->base.name);

    if (!click_amplify)
    {
        click_amplify = true;
        for (int i = 0; i < APP_NUM; i++)
        {
            gui_lite3d_t *app = app_3d_list[i];

            if (app != this)
            {
                app->base.not_show = true;
            }
        }

        click_on_rot_y = 0.0f;
        click_on_shift_z = 0.0f;
        l3_set_global_transform(this->model, (l3_global_transform_cb)app_click_on_cb);
    }
    else
    {
        click_amplify = false;

        int clicked_index = get_app_index(this);
        if (clicked_index == -1)
        {
            gui_log("Error: Target app not found!");
            return;
        }
        this->model->x = app_positions[clicked_index].pos_x;
        this->model->y = app_positions[clicked_index].pos_y;
        this->model->viewPortWidth = 200;
        this->model->viewPortHeight = 200;
        l3_set_global_transform(this->model, (l3_global_transform_cb)applist_global_cb);

        for (int i = 0; i < APP_NUM; i++)
        {
            app_3d_list[i]->base.not_show = false;
        }
    }

}

static void applist_app(gui_view_t *view)
{
    gui_obj_t *obj = GUI_BASE(view);
    gui_view_switch_on_event(view, menu_view, SWITCH_OUT_ANIMATION_FADE,
                             SWITCH_IN_ANIMATION_FADE,
                             GUI_EVENT_KB_SHORT_CLICKED);

    click_amplify = false;

    // App0
    l3_model_t *app0 = l3_create_model(DESC_APP_BIN, L3_DRAW_FRONT_ONLY,
                                       app_positions[0].pos_x, app_positions[0].pos_y, 200, 200);
    app_3d_list[0] = gui_lite3d_create(obj, "app0", app0, 0, 0, 0, 0);

    // App1
    l3_model_t *app1 = l3_create_model(DESC_APP_BIN, L3_DRAW_FRONT_ONLY,
                                       app_positions[1].pos_x, app_positions[1].pos_y, 200, 200);
    l3_set_face_image(app1, 5, FILM_BIN);
    app_3d_list[1] = gui_lite3d_create(obj, "app1", app1, 0, 0, 0, 0);

    // App2
    l3_model_t *app2 = l3_create_model(DESC_APP_BIN, L3_DRAW_FRONT_ONLY,
                                       app_positions[2].pos_x, app_positions[2].pos_y, 200, 200);
    l3_set_face_image(app2, 5, MUSIC_BIN);
    app_3d_list[2] = gui_lite3d_create(obj, "app2", app2, 0, 0, 0, 0);

    // App3
    l3_model_t *app3 = l3_create_model(DESC_APP_BIN, L3_DRAW_FRONT_ONLY,
                                       app_positions[3].pos_x, app_positions[3].pos_y, 200, 200);
    l3_set_face_image(app3, 5, MAIL_BIN);
    app_3d_list[3] = gui_lite3d_create(obj, "app3", app3, 0, 0, 0, 0);

    // App4
    l3_model_t *app4 = l3_create_model(DESC_APP_BIN, L3_DRAW_FRONT_ONLY,
                                       app_positions[4].pos_x, app_positions[4].pos_y, 200, 200);
    l3_set_face_image(app4, 5, VOICE_BIN);
    app_3d_list[4] = gui_lite3d_create(obj, "app4", app4, 0, 0, 0, 0);

    // App5
    l3_model_t *app5 = l3_create_model(DESC_APP_BIN, L3_DRAW_FRONT_ONLY,
                                       app_positions[5].pos_x, app_positions[5].pos_y, 200, 200);
    l3_set_face_image(app5, 5, WECHAT_BIN);
    app_3d_list[5] = gui_lite3d_create(obj, "app5", app5, 0, 0, 0, 0);

    for (int i = 0; i < APP_NUM; i++)
    {
        l3_set_global_transform(app_3d_list[i]->model, (l3_global_transform_cb)applist_global_cb);
        l3_set_face_transform(app_3d_list[i]->model, (l3_face_transform_cb)applist_face_cb);

        gui_lite3d_on_click(app_3d_list[i], (gui_event_cb_t)gui_app_switch, app_3d_list[i]);
    }


    gui_obj_create_timer(GUI_BASE(app_3d_list[0]), 10, true, update_applist_animation);

}


帧率测试

以下表格展示了不同芯片平台上,各个示例的帧率表现。编译环境采用 ARMCLANG V6.22,编译选项为 -O3 LTO

帧率测试结果

芯片型号

处理器主频

分辨率

3D 蝴蝶

3D 人脸

3D 小狗

3D 应用列表

RTL8773E

100MHz

410 x 502

33 FPS

13 FPS

22 FPS

24 FPS

RTL8773G

200MHz

410 x 502

58 FPS

24 FPS

46 FPS

56 FPS

API

Functions

gui_lite3d_t *gui_lite3d_create(void *parent, const char *name, l3_model_t *model, int16_t x, int16_t y, int16_t w, int16_t h)

Lite3D Widget Create.

参数:
  • parent – Parent widget.

  • name – Widget name.

  • model – Lite3D model.

  • x – The X-axis coordinate relative to parent widget.

  • y – The Y-axis coordinate relative to parent widget.

  • w – Width.

  • h – Height.

返回:

The widget object pointer.

void gui_lite3d_on_click(gui_lite3d_t *this, void *callback, void *parameter)

Lite3D Widget On Click.

参数:
  • this – Widget object pointer.

  • callback – Callback function.

  • parameter – Parameter.

struct gui_lite3d_t

Public Members

gui_obj_t base
l3_model_t *model