feat: enhance build system and documentation for SDK installation and shader support
- Updated .gitignore to include new build and output directories for SDK packaging. - Modified CMakeLists.txt to include installation script for SDK. - Enhanced Makefile to support new SDK targets for various platforms. - Improved documentation to reflect new shader capabilities and 3D model support. - Added new shader functions and updated rendering systems to utilize custom shaders. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
847e14b67a
commit
8e0221c552
66 files changed
+14147
-200
No files matched your search
@@ -1,9 +1,11 @@
|
||||
build*/
|
||||
dist/
|
||||
third-party/
|
||||
third-party-binary/
|
||||
.history/
|
||||
docs/superpowers/
|
||||
.worktrees/
|
||||
examples/native-plugin/build/
|
||||
platform/android/apk/local.properties
|
||||
platform/android/apk/.gradle/
|
||||
platform/android/apk/app/build/
|
||||
|
||||
@@ -62,6 +62,8 @@ function(main)
|
||||
if(BUILD_TESTING)
|
||||
add_subdirectory(test)
|
||||
endif()
|
||||
|
||||
include(${CMAKE_SOURCE_DIR}/cmake/install_sdk.cmake)
|
||||
endfunction()
|
||||
|
||||
|
||||
|
||||
@@ -104,6 +104,8 @@ GAME ?= example
|
||||
run/win32-debug run/linux-debug run/macosx-debug \
|
||||
sync/android-libs sync/android-assets install/android-debug run/android-debug log/android \
|
||||
install/ios-debug run/ios-debug log/ios \
|
||||
sdk/win32 sdk/linux sdk/macosx sdk/android sdk/ios \
|
||||
sdk/win32-debug sdk/linux-debug sdk/macosx-debug sdk/android-debug sdk/ios-debug \
|
||||
reinstall/third-party reinstall/third-party/win32 reinstall/third-party/win32-debug \
|
||||
reinstall/third-party/linux reinstall/third-party/linux-debug \
|
||||
reinstall/third-party/macosx reinstall/third-party/macosx-debug \
|
||||
@@ -124,6 +126,7 @@ show-targets:
|
||||
@echo "all -> $(ALL_DEBUG_TARGETS)"
|
||||
@echo "debug -> build/$(PLATFORM)-debug"
|
||||
@echo "release -> build/$(PLATFORM)"
|
||||
@echo "sdk -> sdk/$(PLATFORM) (Release) or sdk/$(PLATFORM)-debug"
|
||||
@echo "run -> run/$(PLATFORM)-debug (GAME=$(GAME))"
|
||||
|
||||
# clangd: build/compile_commands.json -> host platform debug CDB
|
||||
@@ -143,19 +146,25 @@ wsl/linux-debug:
|
||||
wsl/linux:
|
||||
wsl --cd "$(CURDIR)" -- make build/linux
|
||||
|
||||
# win32: Ninja/MSVC helpers(debug 用 Ninja+cl;Release VS 生成器也可借 vcvars)
|
||||
WITH_MSVC = cmake\with-msvc.cmd
|
||||
|
||||
build/win32: build/win32/EVEngine.sln
|
||||
cmake.exe --build $@ --config Release --target deps -j 32
|
||||
cmake.exe --build $@ --config Release -j 32
|
||||
|
||||
build/win32/EVEngine.sln:
|
||||
cmake.exe -G "Visual Studio 18 2026" -DCMAKE_BUILD_TYPE=Release -A x64 -B build/win32 -S .
|
||||
|
||||
build/linux: build/linux/Makefile
|
||||
cmake --build $@ --target deps -j 32
|
||||
cmake --build $@ -j 32
|
||||
|
||||
build/linux/Makefile:
|
||||
cmake -G 'Unix Makefiles' -DCMAKE_BUILD_TYPE=Release -DBUILD_PLATFORM=linux -B build/linux -S .
|
||||
|
||||
build/macosx: build/macosx/Makefile
|
||||
cmake --build $@ --target deps -j 32
|
||||
cmake --build $@ -j 32
|
||||
|
||||
build/macosx/Makefile:
|
||||
@@ -179,23 +188,22 @@ build/android/build.ninja:
|
||||
-DBUILD_TESTING=OFF \
|
||||
-B build/android -S .
|
||||
|
||||
# win32-debug: Ninja + MSVC cl;via cmake/with-msvc.cmd (vcvars) so STL headers resolve
|
||||
# Emits build/win32-debug/compile_commands.json with MSVC INCLUDE paths for clangd
|
||||
WITH_MSVC = cmake\with-msvc.cmd
|
||||
|
||||
build/win32-debug: build/win32-debug/build.ninja
|
||||
$(WITH_MSVC) cmake.exe --build $@ --target deps -j 32
|
||||
$(WITH_MSVC) cmake.exe --build $@ -j 32
|
||||
|
||||
build/win32-debug/build.ninja:
|
||||
$(WITH_MSVC) cmake.exe -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_COMPILER=cl -DCMAKE_CXX_COMPILER=cl -B build/win32-debug -S .
|
||||
|
||||
build/linux-debug: build/linux-debug/Makefile
|
||||
cmake --build $@ --target deps -j 32
|
||||
cmake --build $@ -j 32
|
||||
|
||||
build/linux-debug/Makefile:
|
||||
cmake -G 'Unix Makefiles' -DCMAKE_BUILD_TYPE=Debug -DBUILD_PLATFORM=linux -B build/linux-debug -S .
|
||||
|
||||
build/macosx-debug: build/macosx-debug/Makefile
|
||||
cmake --build $@ --target deps -j 32
|
||||
cmake --build $@ -j 32
|
||||
|
||||
build/macosx-debug/Makefile:
|
||||
@@ -220,6 +228,7 @@ build/android-debug/build.ninja:
|
||||
-B build/android-debug -S .
|
||||
|
||||
build/ios: build/ios/EVEngine.xcodeproj
|
||||
cmake --build build/ios --target deps -j 8
|
||||
@if [ -z "$(IOS_DEVELOPMENT_TEAM)" ]; then \
|
||||
echo "WARNING: IOS_DEVELOPMENT_TEAM unset; building unsigned (install will fail)"; \
|
||||
cd build/ios && xcodebuild -scheme eve -configuration Release \
|
||||
@@ -444,6 +453,26 @@ run/linux:
|
||||
run/macosx:
|
||||
cd $(GAME) && ../build/macosx/src/engine/eve run
|
||||
|
||||
# Target-platform SDK install (independent prefix per plat; for publishing games TO that plat).
|
||||
# Release: make sdk/macosx → builds build/macosx then dist/eve-sdk/macosx
|
||||
# Debug: make sdk/macosx-debug
|
||||
sdk/win32: build/win32
|
||||
sdk/linux: build/linux
|
||||
sdk/macosx: build/macosx
|
||||
sdk/android: build/android
|
||||
sdk/ios: build/ios
|
||||
sdk/win32-debug: build/win32-debug
|
||||
sdk/linux-debug: build/linux-debug
|
||||
sdk/macosx-debug: build/macosx-debug
|
||||
sdk/android-debug: build/android-debug
|
||||
sdk/ios-debug: build/ios-debug
|
||||
|
||||
sdk/win32 sdk/linux sdk/macosx sdk/android sdk/ios \
|
||||
sdk/win32-debug sdk/linux-debug sdk/macosx-debug sdk/android-debug sdk/ios-debug:
|
||||
@plat=$@; plat=$${plat#sdk/}; \
|
||||
cmake --install "build/$$plat" --prefix "dist/eve-sdk/$$plat"; \
|
||||
echo "Installed target SDK -> dist/eve-sdk/$$plat"
|
||||
|
||||
example: run/$(PLATFORM)-debug
|
||||
# make: build/.build-docker
|
||||
# $(DOCKER) /bin/bash -c "cmake -H./src -B./src/build && cmake --build src/build --parallel 8"
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
@PACKAGE_INIT@
|
||||
|
||||
set(EVENGINE_VERSION "@EVENGINE_SDK_VERSION@")
|
||||
set(EVENGINE_TARGET_PLATFORM "@EVENGINE_TARGET_PLATFORM@")
|
||||
|
||||
get_filename_component(EVENGINE_ROOT "${CMAKE_CURRENT_LIST_DIR}/.." ABSOLUTE)
|
||||
set(EVENGINE_INCLUDE_DIR "${EVENGINE_ROOT}/include/eve")
|
||||
set(EVENGINE_THIRD_PARTY_INCLUDE_DIR "${EVENGINE_ROOT}/include")
|
||||
set(EVENGINE_BIN_DIR "${EVENGINE_ROOT}/bin")
|
||||
set(EVENGINE_LIB_DIR "${EVENGINE_ROOT}/lib")
|
||||
set(EVENGINE_PLATFORM_DIR "${EVENGINE_ROOT}/platform")
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/EVEnginePlugin.cmake")
|
||||
|
||||
check_required_components(EVEngine)
|
||||
@@ -0,0 +1,47 @@
|
||||
# Helpers for building native plugins against a target-platform EVEngine SDK.
|
||||
# Plugins are SHARED libraries loaded by eve.plugins.load; they do not link libeve.
|
||||
|
||||
function(add_eve_plugin _eve_plugin_name)
|
||||
cmake_parse_arguments(_EVE_P "" "" "SOURCES;INCLUDE_DIRS;LIBRARIES" ${ARGN})
|
||||
if(NOT _EVE_P_SOURCES)
|
||||
message(FATAL_ERROR "add_eve_plugin(${_eve_plugin_name}): SOURCES required")
|
||||
endif()
|
||||
if(NOT EVENGINE_ROOT)
|
||||
message(FATAL_ERROR "add_eve_plugin: EVENGINE_ROOT unset (call find_package(EVEngine) first)")
|
||||
endif()
|
||||
|
||||
add_library(${_eve_plugin_name} SHARED ${_EVE_P_SOURCES})
|
||||
target_compile_features(${_eve_plugin_name} PUBLIC cxx_std_20)
|
||||
target_compile_definitions(${_eve_plugin_name} PRIVATE EVENGINE_PLUGIN=1)
|
||||
target_include_directories(${_eve_plugin_name} PRIVATE
|
||||
"${EVENGINE_INCLUDE_DIR}"
|
||||
"${EVENGINE_THIRD_PARTY_INCLUDE_DIR}"
|
||||
${_EVE_P_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
# Link against the import library generated from eve.exe exports.
|
||||
if(EXISTS "${EVENGINE_LIB_DIR}/eve.lib")
|
||||
target_link_libraries(${_eve_plugin_name} PRIVATE "${EVENGINE_LIB_DIR}/eve.lib")
|
||||
elseif(EXISTS "${EVENGINE_LIB_DIR}/eve.dll.a")
|
||||
target_link_libraries(${_eve_plugin_name} PRIVATE "${EVENGINE_LIB_DIR}/eve.dll.a")
|
||||
else()
|
||||
message(WARNING "add_eve_plugin: eve import library not found under ${EVENGINE_LIB_DIR}")
|
||||
endif()
|
||||
elseif(APPLE)
|
||||
# Resolve ModuleManager etc. from the host at load time.
|
||||
target_link_options(${_eve_plugin_name} PRIVATE
|
||||
"LINKER:-undefined,dynamic_lookup"
|
||||
)
|
||||
endif()
|
||||
# Linux / Android: leave host symbols undefined; resolved when eve dlopens the plugin.
|
||||
|
||||
if(_EVE_P_LIBRARIES)
|
||||
target_link_libraries(${_eve_plugin_name} PRIVATE ${_EVE_P_LIBRARIES})
|
||||
endif()
|
||||
|
||||
set_target_properties(${_eve_plugin_name} PROPERTIES
|
||||
PREFIX ""
|
||||
OUTPUT_NAME "${_eve_plugin_name}"
|
||||
)
|
||||
endfunction()
|
||||
@@ -0,0 +1,176 @@
|
||||
# EVEngine SDK install rules — one independent SDK per BUILD_PLATFORM (target publish platform).
|
||||
# Usage:
|
||||
# cmake --install build/<plat>[-debug] --prefix dist/eve-sdk/<plat>[-debug]
|
||||
|
||||
include(CMakePackageConfigHelpers)
|
||||
include(GNUInstallDirs)
|
||||
|
||||
set(EVENGINE_SDK_VERSION
|
||||
"${EVENGINE_MAJOR_VERSION}.${EVENGINE_MINOR_VERSION}.${EVENGINE_PATCH_VERSION}")
|
||||
|
||||
# ---- Host binary ----
|
||||
if(ANDROID OR (CMAKE_SYSTEM_NAME STREQUAL "Android"))
|
||||
install(TARGETS ${EVENGINE_NATIVE_TARGET}
|
||||
LIBRARY DESTINATION lib
|
||||
ARCHIVE DESTINATION lib
|
||||
RUNTIME DESTINATION lib
|
||||
)
|
||||
elseif(CMAKE_SYSTEM_NAME STREQUAL "iOS" OR EVENGINE_IOS)
|
||||
install(TARGETS ${EVENGINE_NATIVE_TARGET}
|
||||
BUNDLE DESTINATION bin
|
||||
RUNTIME DESTINATION bin
|
||||
)
|
||||
else()
|
||||
install(TARGETS ${EVENGINE_NATIVE_TARGET}
|
||||
RUNTIME DESTINATION bin
|
||||
# Windows import library for plugins linking against eve.exe exports
|
||||
ARCHIVE DESTINATION lib
|
||||
LIBRARY DESTINATION lib
|
||||
)
|
||||
endif()
|
||||
|
||||
# ---- Public headers (engine common + module façades) ----
|
||||
install(DIRECTORY "${CMAKE_SOURCE_DIR}/src/engine/common/"
|
||||
DESTINATION include/eve/common
|
||||
FILES_MATCHING
|
||||
PATTERN "*.h"
|
||||
PATTERN "*.hpp"
|
||||
)
|
||||
install(FILES "${CMAKE_BINARY_DIR}/src/engine/common/config.h"
|
||||
DESTINATION include/eve/common
|
||||
)
|
||||
|
||||
set(_eve_module_dirs
|
||||
audio data event filesystem graphics image joystick keyboard mouse
|
||||
model3d network particles plugins sound timer touch ui window
|
||||
)
|
||||
foreach(_eve_mod IN LISTS _eve_module_dirs)
|
||||
if(EXISTS "${CMAKE_SOURCE_DIR}/src/modules/${_eve_mod}")
|
||||
install(DIRECTORY "${CMAKE_SOURCE_DIR}/src/modules/${_eve_mod}/"
|
||||
DESTINATION include/eve/${_eve_mod}
|
||||
FILES_MATCHING
|
||||
PATTERN "*.h"
|
||||
PATTERN "*.hpp"
|
||||
PATTERN "sdl" EXCLUDE
|
||||
PATTERN "vulkan" EXCLUDE
|
||||
PATTERN "physfs" EXCLUDE
|
||||
PATTERN "openal" EXCLUDE
|
||||
PATTERN "imgui" EXCLUDE
|
||||
PATTERN "cppfs" EXCLUDE
|
||||
PATTERN "include_shim" EXCLUDE
|
||||
)
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
# ---- Target-platform packaging template only ----
|
||||
set(_eve_plat_src "${CMAKE_SOURCE_DIR}/platform/${BUILD_PLATFORM}")
|
||||
if(EXISTS "${_eve_plat_src}")
|
||||
if(BUILD_PLATFORM STREQUAL "android")
|
||||
install(DIRECTORY "${_eve_plat_src}/"
|
||||
DESTINATION platform
|
||||
PATTERN "apk/app/build" EXCLUDE
|
||||
PATTERN "apk/.gradle" EXCLUDE
|
||||
PATTERN "apk/build" EXCLUDE
|
||||
PATTERN "apk/local.properties" EXCLUDE
|
||||
PATTERN "apk/app/src/main/jniLibs" EXCLUDE
|
||||
PATTERN "apk/app/src/main/assets/game/*" EXCLUDE
|
||||
PATTERN "CMakeCache.txt" EXCLUDE
|
||||
PATTERN "*.o" EXCLUDE
|
||||
PATTERN "*.a" EXCLUDE
|
||||
)
|
||||
# Keep assets/game placeholder
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/platform/android/apk/app/src/main/assets/game/.gitkeep"
|
||||
DESTINATION platform/apk/app/src/main/assets/game
|
||||
OPTIONAL
|
||||
)
|
||||
else()
|
||||
install(DIRECTORY "${_eve_plat_src}/"
|
||||
DESTINATION platform
|
||||
PATTERN "CMakeCache.txt" EXCLUDE
|
||||
PATTERN "*.o" EXCLUDE
|
||||
PATTERN "*.a" EXCLUDE
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# ---- Android runtime shared libs needed to assemble an APK ----
|
||||
if(BUILD_PLATFORM STREQUAL "android")
|
||||
set(_eve_tp_lib "${CMAKE_SOURCE_DIR}/build/third-party-binary/${BUILD_PLATFORM}")
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
set(_eve_tp_lib "${CMAKE_SOURCE_DIR}/build/third-party-binary/${BUILD_PLATFORM}-debug")
|
||||
endif()
|
||||
if(EXISTS "${_eve_tp_lib}/lib/libSDL2.so")
|
||||
install(FILES "${_eve_tp_lib}/lib/libSDL2.so" DESTINATION lib)
|
||||
endif()
|
||||
if(EXISTS "${_eve_tp_lib}/lib/libhidapi.so")
|
||||
install(FILES "${_eve_tp_lib}/lib/libhidapi.so" DESTINATION lib)
|
||||
endif()
|
||||
# libc++_shared.so from NDK (best-effort; packaging docs note NDK fallback)
|
||||
if(DEFINED ANDROID_NDK AND EXISTS "${ANDROID_NDK}")
|
||||
file(GLOB _eve_cxx_shared
|
||||
"${ANDROID_NDK}/toolchains/llvm/prebuilt/*/sysroot/usr/lib/aarch64-linux-android/libc++_shared.so")
|
||||
if(_eve_cxx_shared)
|
||||
list(GET _eve_cxx_shared 0 _eve_cxx_one)
|
||||
install(FILES "${_eve_cxx_one}" DESTINATION lib)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# ---- Selected third-party headers needed to compile plugins (SSQ bindings) ----
|
||||
set(_eve_tp_inc "${CMAKE_SOURCE_DIR}/build/third-party-binary/${BUILD_PLATFORM}")
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
set(_eve_tp_inc "${CMAKE_SOURCE_DIR}/build/third-party-binary/${BUILD_PLATFORM}-debug")
|
||||
endif()
|
||||
if(EXISTS "${_eve_tp_inc}/include")
|
||||
foreach(_eve_hdr_dir IN ITEMS simplesquirrel squirrel SQLiteCpp)
|
||||
if(EXISTS "${_eve_tp_inc}/include/${_eve_hdr_dir}")
|
||||
install(DIRECTORY "${_eve_tp_inc}/include/${_eve_hdr_dir}"
|
||||
DESTINATION include
|
||||
)
|
||||
endif()
|
||||
endforeach()
|
||||
# Flat squirrel headers sometimes live at include/*.h
|
||||
file(GLOB _eve_sq_headers
|
||||
"${_eve_tp_inc}/include/squirrel.h"
|
||||
"${_eve_tp_inc}/include/sqstdio.h"
|
||||
"${_eve_tp_inc}/include/sqstdblob.h"
|
||||
"${_eve_tp_inc}/include/sqstdmath.h"
|
||||
"${_eve_tp_inc}/include/sqstdsystem.h"
|
||||
"${_eve_tp_inc}/include/sqstdstring.h"
|
||||
"${_eve_tp_inc}/include/sqconfig.h"
|
||||
)
|
||||
if(_eve_sq_headers)
|
||||
install(FILES ${_eve_sq_headers} DESTINATION include)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# ---- Marker files ----
|
||||
install(CODE "
|
||||
file(WRITE \"\${CMAKE_INSTALL_PREFIX}/share/eve/VERSION\" \"${EVENGINE_SDK_VERSION}\\n\")
|
||||
file(WRITE \"\${CMAKE_INSTALL_PREFIX}/share/eve/TARGET_PLATFORM\" \"${BUILD_PLATFORM}\\n\")
|
||||
")
|
||||
|
||||
# ---- CMake package config (find_package(EVEngine)) ----
|
||||
set(EVENGINE_TARGET_PLATFORM "${BUILD_PLATFORM}")
|
||||
configure_package_config_file(
|
||||
"${CMAKE_SOURCE_DIR}/cmake/EVEngineConfig.cmake.in"
|
||||
"${CMAKE_BINARY_DIR}/EVEngineConfig.cmake"
|
||||
INSTALL_DESTINATION cmake
|
||||
)
|
||||
write_basic_package_version_file(
|
||||
"${CMAKE_BINARY_DIR}/EVEngineConfigVersion.cmake"
|
||||
VERSION "${EVENGINE_SDK_VERSION}"
|
||||
COMPATIBILITY SameMajorVersion
|
||||
)
|
||||
|
||||
install(FILES
|
||||
"${CMAKE_BINARY_DIR}/EVEngineConfig.cmake"
|
||||
"${CMAKE_BINARY_DIR}/EVEngineConfigVersion.cmake"
|
||||
"${CMAKE_SOURCE_DIR}/cmake/EVEnginePlugin.cmake"
|
||||
DESTINATION cmake
|
||||
)
|
||||
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/cmake/sdk/README.md"
|
||||
DESTINATION share/eve
|
||||
OPTIONAL
|
||||
)
|
||||
@@ -0,0 +1,47 @@
|
||||
# EVEngine target-platform SDK
|
||||
|
||||
Each install prefix is an **independent SDK for one publish target**
|
||||
(`win32` / `linux` / `macosx` / `android` / `ios`).
|
||||
|
||||
It is used on a **dev machine** to create a game that ships **to that target**.
|
||||
It is not meant to be “installed and run” on a phone or as a universal multi-platform kit.
|
||||
|
||||
## Layout
|
||||
|
||||
- `bin/` — host (`eve` / `eve.exe` / iOS `.app`) when applicable
|
||||
- `lib/` — Android `libmain.so` + packaging `.so`s; Windows `eve.lib` for plugins
|
||||
- `include/eve/` — public headers for native plugins
|
||||
- `cmake/` — `find_package(EVEngine)` + `add_eve_plugin()`
|
||||
- `platform/` — **this target only** packaging template
|
||||
- `share/eve/TARGET_PLATFORM` — must match the directory name
|
||||
|
||||
## Install (from engine source tree)
|
||||
|
||||
```bash
|
||||
cmake --install build/macosx-debug --prefix dist/eve-sdk/macosx-debug
|
||||
cmake --install build/android-debug --prefix dist/eve-sdk/android-debug
|
||||
```
|
||||
|
||||
Or: `make sdk/macosx-debug` / `make sdk/android-debug`.
|
||||
|
||||
Do **not** install multiple platforms into the same prefix.
|
||||
|
||||
## Native plugin
|
||||
|
||||
```cmake
|
||||
find_package(EVEngine REQUIRED PATHS /path/to/eve-sdk/macosx-debug)
|
||||
add_eve_plugin(myplugin SOURCES myplugin.cpp)
|
||||
```
|
||||
|
||||
Export `eve_plugin_init` (see `examples/native-plugin`). From Squirrel:
|
||||
|
||||
```squirrel
|
||||
local plugins = eve.Plugins();
|
||||
plugins.load("./myplugin.dylib"); // or .so / .dll
|
||||
```
|
||||
|
||||
## Android packaging sketch
|
||||
|
||||
1. Copy SDK `lib/libmain.so`, `libSDL2.so`, `libc++_shared.so` into the template `jniLibs/<abi>/`.
|
||||
2. Copy game scripts into `assets/game/`.
|
||||
3. Build the Gradle project under `platform/apk/`.
|
||||
+24
-2
@@ -147,7 +147,7 @@ function eve::update(dt) {
|
||||
| 组件 | 字段(草案) | 说明 |
|
||||
|------|----------------|------|
|
||||
| `Transform2D` | `x,y,rot,sx,sy` | 2D 变换;可后续拆 Parent 链接 |
|
||||
| `Sprite` | texture、quad、color、layer、visible | 主 Renderable |
|
||||
| `Sprite` | texture、shader、quad、color、layer、visible | 主 Renderable;`shader=null` 用默认管线 |
|
||||
| `Camera2D`(P1) | 位置、zoom、清屏色 | 缺省一台绑定主窗口 |
|
||||
| `CanvasTarget`(P1) | 离屏目标 | 声明画到哪 |
|
||||
|
||||
@@ -164,7 +164,29 @@ function eve::update(dt) {
|
||||
| `present` / `setViewportSize` / `clear` | P0 | 帧与窗口;由 RenderSystem 或 boot 调用 |
|
||||
| Batcher + 纹理 upload | P0 | Sprite 绘制后端 |
|
||||
| 图元 tessellate | P2 | 调试形状、编辑器 gizmo |
|
||||
| 自定义 Shader | P1 | 先进内部,再考虑组件字段 |
|
||||
| 自定义 Shader | P1 | `Graphics::newShader` / `newShaderFromSpv*`;`Sprite.shader` |
|
||||
|
||||
### 3.4 自定义 Shader 约定(2D)
|
||||
|
||||
```cpp
|
||||
auto *sh = gfx->newShader(R"(#version 450
|
||||
layout(location=0) in vec4 fragColor;
|
||||
layout(location=1) in vec2 fragUV;
|
||||
layout(location=0) out vec4 outColor;
|
||||
layout(binding=0) uniform sampler2D MainTex;
|
||||
layout(push_constant) uniform Externals { float data[32]; } u;
|
||||
void main() { outColor = texture(MainTex, fragUV) * fragColor * u.data[0]; }
|
||||
)");
|
||||
sh->declareFloat("factor");
|
||||
sh->sendFloat("factor", 0.5f);
|
||||
entity->sprite()->shader = sh; // 声明式
|
||||
// 或 gfx->setShader(sh); // 即时模式逃生舱
|
||||
```
|
||||
|
||||
- 顶点输入固定为 TexturedVertex(pos / color / uv);省略 vert 时用内置 `textured.vert`
|
||||
- `MainTex`:`set=0, binding=0`
|
||||
- Uniform:先 `declare*` 再 `send*`,按声明顺序写入 push-constant `data[]`(最多 32 floats)
|
||||
- GLSL 编译依赖本机 `glslc`;无 glslc 时用预编译 SPIR-V(`newShaderFromSpv` / `newShaderFromSpvFile`)
|
||||
|
||||
|
||||
## 4. VKBuilder 映射表
|
||||
|
||||
+4
-4
@@ -69,8 +69,8 @@ Love2D 风格即时模式仅作 C++/DevTools 内部逃生舱。
|
||||
- [ ] 纹理 / Quad 作为 Sprite 资源(image 解码 → GPU Texture)
|
||||
- [ ] Vulkan 后端基础管线(VKBuilder Present / Pipeline / Texture)
|
||||
- [ ] `clear` / `present`、与 SDL Window Surface 对接
|
||||
- [ ] 离屏 Canvas(组件或内部目标,P1)
|
||||
- [ ] 用户自定义 Shader(P1,先进内部)
|
||||
- [x] 离屏 Canvas(组件或内部目标,P1)
|
||||
- [x] 用户自定义 Shader(P1:`Shader` 资源 + `Sprite.shader` + `Graphics::newShader` / SPIR-V)
|
||||
|
||||
**高级能力(B,依赖 A,见设计文档第 8 节):**
|
||||
|
||||
@@ -79,7 +79,7 @@ Love2D 风格即时模式仅作 C++/DevTools 内部逃生舱。
|
||||
- [ ] 地图类接口:TileLayer + tilemap
|
||||
- [ ] 摄像机:2D 光照与法线贴图
|
||||
- [ ] 低像素优化:小图旋转等
|
||||
- [ ] 简单 3D model 支持
|
||||
- [x] 简单 3D model 支持(`Model3D` 加载 + `Graphics::newMeshFromAssimp` / `RenderSystem3D`)
|
||||
|
||||
### 5. 数据抽象 — `data`
|
||||
|
||||
@@ -99,7 +99,7 @@ Love2D 风格即时模式仅作 C++/DevTools 内部逃生舱。
|
||||
- [x] 立体声播放 — `audio`(OpenAL)
|
||||
- [ ] 视频资源 — `video`
|
||||
- [ ] 字体资源 — `fonts`
|
||||
- [ ] 3D 模型资源 — `3dmodel`
|
||||
- [x] 3D 模型资源 — `3dmodel`(`model3d` / `eve.Model3D`,medialoader Assimp → `ModelData`)
|
||||
- [ ] 动画资源载入(与 `animation` 配合)
|
||||
- [ ] 统一 cache、引用计数与热重载标记
|
||||
|
||||
|
||||
@@ -118,6 +118,7 @@
|
||||
| data | `src/modules/data/DataModule.h` | 可用 | 已展开 | **33/35 已覆盖**(2 部分) | `test/data.cpp`;22 个 `data.*` 用例 |
|
||||
| image | `src/modules/image/Image.h` | 可用 | 已展开 | **见 §12** | `test/image.cpp`(10 用例) |
|
||||
| sound | `src/modules/sound/Sound.h` | 可用 | 已展开 | **见 §13** | `test/sound.cpp`(7 用例) |
|
||||
| model3d / 3dmodel | `src/modules/model3d/Model3D.h` | 可用 | 已展开 | **见 §13b** | `test/model3d.cpp`(5 用例);medialoader Assimp |
|
||||
| audio | `src/modules/audio/Audio.h` | 可用 | 已展开 | **见 §14** | `test/audio.cpp`(9 用例);需 OpenAL |
|
||||
| plugins | — | 未实现 | N/A | - | |
|
||||
| system | — | 未实现 | N/A | - | |
|
||||
@@ -320,6 +321,20 @@
|
||||
|
||||
程序化 demo 音效已迁至可选 `demo` 模块(`test/demo.cpp`)。
|
||||
|
||||
## 13b. API 明细:`model3d`(资源名 `3dmodel`)
|
||||
|
||||
用例:`test/model3d.cpp`(9)。CPU 解码 + 可选 GPU 渲染冒烟;上传见 `Graphics::newMeshFromAssimp` / `RenderSystem3D`。
|
||||
|
||||
| API | 头文件 | 状态 | 用例 | 缺口 |
|
||||
|-----|--------|------|------|------|
|
||||
| `Model3D::newModelData(Data*, hintExt)` | Model3D.h | 已覆盖 | `model3d.newModelData.objFromMemory`, `model3d.newModelData.emptyThrows`, `model3d.newModelData.rock1Fbx` | FileData 扩展名自动 hint 未单测 |
|
||||
| `Model3D::newModelDataFromFile(path)` | Model3D.h | 已覆盖 | `model3d.newModelDataFromFile.missingThrows`, `model3d.newModelDataFromFile.sofaObjMtl` | |
|
||||
| 渲染冒烟(ModelData → Mesh → RenderSystem3D) | — | 已覆盖 | `model3d.render.rock1Fbx`, `model3d.render.sofaObjMtl` | rock1 用 `Textures/Rock1_Diffuse.png` |
|
||||
| `Model3D::getName` | Model3D.h | 已覆盖 | `model3d.Model3D.getName` | |
|
||||
| `ModelData::getMeshCount` / `getVertexCount` / `getFaceCount` / `empty` / `getMesh` / `getScene` | ModelData.h | 已覆盖 | `model3d.newModelData.objFromMemory` | |
|
||||
| `ModelData::getMaterialCount` / `hasNormals` / `hasTexCoords` | ModelData.h | 部分 | 间接经 obj 加载 | 非法 index 仅测了 getVertexCount |
|
||||
| `ModelData` 非法 mesh index | ModelData.h | 已覆盖 | `model3d.newModelData.invalidMeshIndexThrows` | |
|
||||
|
||||
## 14. API 明细:`audio`
|
||||
|
||||
用例:`test/audio.cpp`(9)。无 OpenAL 时新用例 N/A skip。
|
||||
@@ -364,6 +379,7 @@
|
||||
|------|------|
|
||||
| 2026-08-05 | 初稿:规范 + 全模块总表 + `data`/`filesystem` API 展开与现状 |
|
||||
| 2026-08-05 | 样板轮落地:`test/data.cpp` +13 用例;新建 `test/filesystem_cpp.cpp`(8 用例);更新 §5–§7 进度;回归 `data`+`filesystem`+`FilesystemTest` 33/33 PASS |
|
||||
| 2026-08-06 | 新增 `model3d`(`3dmodel`):medialoader Assimp → `ModelData`;`test/model3d.cpp` 5 用例 PASS |
|
||||
| 2026-08-05 | Round 2:event/mouse/touch/window/image/sound/audio/network/timer C++ 补测;keyboard/joystick 标 N/A;`EVTouch` 链入 unit_test;回归过滤 69 PASS;放宽 `audio.source.seekTell`;更新 §5 与 §9–§16 |
|
||||
| 2026-08-05 | 修复:`File::read`/`open("r")` PhysFS 模式;Mouse WarpMouse 窗口句柄;Audio `stopAll` 野指针;HttpRequest verifySsl 继承(HTTPS 仍 stub `tls`);补强相关断言 |
|
||||
| 2026-08-06 | Window API 轮收尾:`Window.h` 范围 B 纯虚齐全;`test/window_cpp.cpp` 14 用例;`^window\..*$` 14/14 PASS;更新 §5 / §11 |
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
cmake_minimum_required(VERSION 3.21)
|
||||
project(eve_hello_plugin LANGUAGES CXX)
|
||||
|
||||
# Point at a target-platform SDK, e.g.:
|
||||
# cmake -B build -DEVEngine_DIR=/path/to/dist/eve-sdk/macosx-debug/cmake
|
||||
find_package(EVEngine REQUIRED)
|
||||
|
||||
add_eve_plugin(hello_plugin SOURCES hello_plugin.cpp)
|
||||
@@ -0,0 +1,17 @@
|
||||
# Native plugin example (no engine source required)
|
||||
|
||||
Build against an installed **target-platform** SDK:
|
||||
|
||||
```bash
|
||||
cmake -B build -DEVEngine_DIR=$PWD/../../dist/eve-sdk/macosx-debug/cmake
|
||||
cmake --build build
|
||||
```
|
||||
|
||||
Then from a game directory (with the matching `eve` host):
|
||||
|
||||
```squirrel
|
||||
local plugins = eve.Plugins();
|
||||
plugins.load("./hello_plugin.dylib"); // .so on Linux/Android, .dll on Windows
|
||||
local hi = eve.HelloPlugin();
|
||||
print(hi.greet() + "\n");
|
||||
```
|
||||
@@ -0,0 +1,37 @@
|
||||
#include "common/Export.h"
|
||||
#include "common/Module.h"
|
||||
|
||||
#include <simplesquirrel/simplesquirrel.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
// Minimal sample plugin: registers a Squirrel-visible module "HelloPlugin".
|
||||
namespace eve::hello {
|
||||
|
||||
class HelloPlugin : public Module {
|
||||
public:
|
||||
Module_REG(HelloPlugin);
|
||||
HelloPlugin() = default;
|
||||
~HelloPlugin() override = default;
|
||||
|
||||
std::string greet() const { return "hello from native plugin"; }
|
||||
};
|
||||
|
||||
Module_IMPL(HelloPlugin, new HelloPlugin());
|
||||
|
||||
void HelloPlugin::expose(ssq::Table& table) {
|
||||
auto cls = table.addClass(name, HelloPlugin::create, false);
|
||||
expose(cls);
|
||||
}
|
||||
|
||||
void HelloPlugin::expose(ssq::Class& cls) {
|
||||
cls.addFunc("getName", &HelloPlugin::getName);
|
||||
cls.addFunc("greet", &HelloPlugin::greet);
|
||||
}
|
||||
|
||||
} // namespace eve::hello
|
||||
|
||||
EVE_PLUGIN_EXPORT int eve_plugin_init(void) {
|
||||
// Module_IMPL already registered HelloPlugin via static ModuleRegister.
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// Quick plugin load smoke (no window — may still init SDL via modules).
|
||||
// Prefer running under example/ with a one-off script if needed.
|
||||
|
||||
function file_exists(path) {
|
||||
try { file(path, "r").close(); return true; } catch (e) { return false; }
|
||||
}
|
||||
|
||||
print("plugins smoke\n");
|
||||
local plugins = eve.Plugins();
|
||||
local path = "build/hello_plugin.dylib";
|
||||
try {
|
||||
plugins.load(path);
|
||||
local hi = eve.HelloPlugin();
|
||||
print(hi.greet() + "\n");
|
||||
print("OK\n");
|
||||
} catch (e) {
|
||||
print("FAIL: " + e + "\n");
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
# android target SDK notes
|
||||
|
||||
This tree (under the android SDK's `platform/`) is the APK packaging template.
|
||||
|
||||
On a **dev machine** (not on-device):
|
||||
|
||||
1. Copy SDK `lib/libmain.so`, `libSDL2.so`, `libc++_shared.so` (and `libhidapi.so` if present)
|
||||
into `apk/app/src/main/jniLibs/<abi>/`.
|
||||
2. Copy your game into `apk/app/src/main/assets/game/`.
|
||||
3. Build with Gradle (`./gradlew assembleDebug` / `assembleRelease`).
|
||||
|
||||
Native plugins (`.so`) for the same ABI go into `jniLibs` and are loaded from script
|
||||
with `eve.Plugins().load(...)`.
|
||||
@@ -0,0 +1,12 @@
|
||||
# ios target SDK notes
|
||||
|
||||
This tree is the iOS packaging template inside `dist/eve-sdk/ios[-debug]`.
|
||||
|
||||
On a **macOS dev machine** with Xcode:
|
||||
|
||||
1. Use the prebuilt `bin/eve.app` (or rebuild packaging around the SDK host) as the app shell.
|
||||
2. Place game assets under the app's `game/` resources (see `game-shell/`).
|
||||
3. Ensure MoltenVK is embedded as in the engine build.
|
||||
4. Sign and archive for device / App Store.
|
||||
|
||||
Native plugins are typically embeddable `.dylib` / frameworks loaded via `eve.Plugins().load(...)`.
|
||||
@@ -1,3 +1,11 @@
|
||||
在linux下,我们构建一个全静态链接的库,对于发布则可以选择动态链接或裁剪后自己编译纯静态链接版本。
|
||||
# linux target SDK notes
|
||||
|
||||
This directory is the packaging template shipped inside `dist/eve-sdk/linux[-debug]`.
|
||||
|
||||
Publish a Linux game by shipping:
|
||||
|
||||
1. `bin/eve` from the SDK
|
||||
2. Your game folder (`config.nut`, `main.nut`, assets)
|
||||
3. Optional native plugins (`.so`) loaded via `eve.Plugins().load(...)`
|
||||
|
||||
Prefer a fully static host when possible; plugins remain shared objects.
|
||||
@@ -0,0 +1,11 @@
|
||||
# macosx target SDK notes
|
||||
|
||||
This directory is the packaging template shipped inside `dist/eve-sdk/macosx[-debug]`.
|
||||
|
||||
Publish a macOS game by shipping:
|
||||
|
||||
1. `bin/eve` from the SDK
|
||||
2. Your game folder (`config.nut`, `main.nut`, assets)
|
||||
3. Optional native plugins (`.dylib`) loaded via `eve.Plugins().load(...)`
|
||||
|
||||
Requires a Vulkan-capable environment (MoltenVK) on the player machine, same as the engine host.
|
||||
@@ -0,0 +1,11 @@
|
||||
# win32 target SDK notes
|
||||
|
||||
This directory is the packaging template shipped inside `dist/eve-sdk/win32[-debug]`.
|
||||
|
||||
Publish a Windows game by shipping:
|
||||
|
||||
1. `bin/eve.exe` from the SDK
|
||||
2. Your game folder (`config.nut`, `main.nut`, assets)
|
||||
3. Optional native plugins (`.dll`) next to the game; load via `eve.Plugins().load(...)`
|
||||
|
||||
Develop on a Windows machine (or cross-build plugins matching the MSVC ABI of the SDK).
|
||||
@@ -9,14 +9,14 @@ set(EVENGINE_IOS_DEPLOYMENT_TARGET "13.0" CACHE STRING "Minimum iOS/iPadOS deplo
|
||||
if(ANDROID OR (CMAKE_SYSTEM_NAME STREQUAL "Android"))
|
||||
# SDLActivity loads libmain.so and calls SDL_main (via SDL_main.h macro on main).
|
||||
add_library(main SHARED main.cpp)
|
||||
set(EVENGINE_NATIVE_TARGET main)
|
||||
set(EVENGINE_NATIVE_TARGET main CACHE INTERNAL "Host target for SDK install")
|
||||
elseif(CMAKE_SYSTEM_NAME STREQUAL "iOS" OR EVENGINE_IOS)
|
||||
# UIKit SDL entry: MACOSX_BUNDLE .app with SDL_main.
|
||||
add_executable(eve MACOSX_BUNDLE main.cpp)
|
||||
set(EVENGINE_NATIVE_TARGET eve)
|
||||
set(EVENGINE_NATIVE_TARGET eve CACHE INTERNAL "Host target for SDK install")
|
||||
else()
|
||||
add_executable(eve main.cpp)
|
||||
set(EVENGINE_NATIVE_TARGET eve)
|
||||
set(EVENGINE_NATIVE_TARGET eve CACHE INTERNAL "Host target for SDK install")
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
@@ -112,9 +112,9 @@ else()
|
||||
endif()
|
||||
|
||||
if(ANDROID OR (CMAKE_SYSTEM_NAME STREQUAL "Android") OR CMAKE_SYSTEM_NAME STREQUAL "iOS" OR EVENGINE_IOS)
|
||||
set(EVELIBS EVWindow EVMouse EVKeyboard EVJoystick EVEvent EVTimer EVTouch EVCmdLine EVScripts EVCommon EVGraphics EVImage EVFileSystem EVNetwork EVData EVSound EVAudio EVUI eve_imgui EVParticles)
|
||||
set(EVELIBS EVWindow EVMouse EVKeyboard EVJoystick EVEvent EVTimer EVTouch EVCmdLine EVScripts EVCommon EVGraphics EVImage EVFileSystem EVNetwork EVData EVSound EVAudio EVUI eve_imgui EVParticles EVModel3D EVPlugins)
|
||||
else()
|
||||
set(EVELIBS EVWindow EVMouse EVKeyboard EVJoystick EVEvent EVTimer EVTouch EVCmdLine EVDevTools EVScripts EVCommon EVGraphics EVImage EVFileSystem EVNetwork EVData EVSound EVAudio EVUI eve_imgui EVParticles)
|
||||
set(EVELIBS EVWindow EVMouse EVKeyboard EVJoystick EVEvent EVTimer EVTouch EVCmdLine EVDevTools EVScripts EVCommon EVGraphics EVImage EVFileSystem EVNetwork EVData EVSound EVAudio EVUI eve_imgui EVParticles EVModel3D EVPlugins)
|
||||
endif()
|
||||
|
||||
if(EVENGINE_BUILD_DEMO)
|
||||
@@ -209,3 +209,22 @@ if(CMAKE_SYSTEM_NAME STREQUAL "iOS" OR EVENGINE_IOS)
|
||||
COMMENT "Embed and sign MoltenVK.framework into EVEngine.app"
|
||||
)
|
||||
endif()
|
||||
|
||||
# Plugins resolve ModuleManager / ModuleRegister symbols from the host process.
|
||||
target_compile_definitions(${EVENGINE_NATIVE_TARGET} PRIVATE EVENGINE_BUILDING_HOST=1)
|
||||
if(WIN32)
|
||||
set_target_properties(${EVENGINE_NATIVE_TARGET} PROPERTIES
|
||||
ENABLE_EXPORTS ON
|
||||
WINDOWS_EXPORT_ALL_SYMBOLS ON
|
||||
)
|
||||
elseif(APPLE AND NOT (CMAKE_SYSTEM_NAME STREQUAL "iOS" OR EVENGINE_IOS))
|
||||
target_link_options(${EVENGINE_NATIVE_TARGET} PRIVATE "LINKER:-export_dynamic")
|
||||
elseif(UNIX AND NOT ANDROID AND NOT (CMAKE_SYSTEM_NAME STREQUAL "Android"))
|
||||
target_link_options(${EVENGINE_NATIVE_TARGET} PRIVATE "LINKER:--export-dynamic")
|
||||
elseif(ANDROID OR (CMAKE_SYSTEM_NAME STREQUAL "Android"))
|
||||
# libmain.so: keep default-visible EVENGINE_API symbols for dlopened plugins.
|
||||
set_target_properties(${EVENGINE_NATIVE_TARGET} PROPERTIES
|
||||
CXX_VISIBILITY_PRESET default
|
||||
VISIBILITY_INLINES_HIDDEN OFF
|
||||
)
|
||||
endif()
|
||||
@@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "common/Export.h"
|
||||
|
||||
#include <exception>
|
||||
#include <string>
|
||||
|
||||
@@ -8,7 +10,7 @@ namespace eve
|
||||
{
|
||||
|
||||
|
||||
class Exception : public std::exception
|
||||
class EVENGINE_API Exception : public std::exception
|
||||
{
|
||||
public:
|
||||
Exception(const char *fmt, ...);
|
||||
@@ -28,4 +30,4 @@ private:
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
// Host (eve / libmain) exports; plugins import the same symbols from the process.
|
||||
#if defined(EVENGINE_PLUGIN)
|
||||
# if defined(_WIN32)
|
||||
# define EVENGINE_API __declspec(dllimport)
|
||||
# else
|
||||
# define EVENGINE_API
|
||||
# endif
|
||||
#elif defined(_WIN32)
|
||||
# define EVENGINE_API __declspec(dllexport)
|
||||
#else
|
||||
# if defined(__GNUC__) || defined(__clang__)
|
||||
# define EVENGINE_API __attribute__((visibility("default")))
|
||||
# else
|
||||
# define EVENGINE_API
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// C ABI entry required by eve.plugins.load (return 0 on success).
|
||||
#if defined(_WIN32)
|
||||
# define EVE_PLUGIN_EXPORT extern "C" __declspec(dllexport)
|
||||
#else
|
||||
# define EVE_PLUGIN_EXPORT extern "C" __attribute__((visibility("default")))
|
||||
#endif
|
||||
@@ -33,12 +33,38 @@ void ModuleManager::register_module(const char* name, creator_t c, exposer_t e)
|
||||
}
|
||||
|
||||
void ModuleManager::expose(ssq::VM& vm) {
|
||||
inst().active_vm_ = &vm;
|
||||
auto table = vm.addTable("eve");
|
||||
for (auto& D : inst().registered_modules) {
|
||||
D.second.exposer(table);
|
||||
D.second.exposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
void ModuleManager::set_vm(ssq::VM* vm) { inst().active_vm_ = vm; }
|
||||
|
||||
ssq::VM* ModuleManager::vm() { return inst().active_vm_; }
|
||||
|
||||
int ModuleManager::expose_pending() {
|
||||
ssq::VM* v = inst().active_vm_;
|
||||
if (!v)
|
||||
return -1;
|
||||
int count = 0;
|
||||
try {
|
||||
ssq::Table table(v->find("eve"));
|
||||
for (auto& D : inst().registered_modules) {
|
||||
if (D.second.exposed || !D.second.exposer)
|
||||
continue;
|
||||
D.second.exposer(table);
|
||||
D.second.exposed = true;
|
||||
++count;
|
||||
}
|
||||
} catch (...) {
|
||||
return -1;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
ModuleRegister::ModuleRegister(const char* name,
|
||||
ModuleManager::creator_t c, ModuleManager::exposer_t e) {
|
||||
ModuleManager::register_module(name, c, e);
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#pragma once
|
||||
#include "common/Export.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
@@ -36,13 +38,13 @@ class Class;
|
||||
|
||||
namespace eve {
|
||||
|
||||
class Module {
|
||||
class EVENGINE_API Module {
|
||||
public:
|
||||
virtual ~Module() {}
|
||||
virtual std::string getName() const = 0;
|
||||
};
|
||||
|
||||
class ModuleManager {
|
||||
class EVENGINE_API ModuleManager {
|
||||
public:
|
||||
typedef Module* (*creator_t)();
|
||||
typedef void (*exposer_t)(ssq::Table&);
|
||||
@@ -54,6 +56,10 @@ public:
|
||||
|
||||
static void register_module(const char* name, creator_t c, exposer_t e);
|
||||
static void expose(ssq::VM& vm);
|
||||
// Expose modules registered after the initial expose() (e.g. plugins).
|
||||
static int expose_pending();
|
||||
static void set_vm(ssq::VM* vm);
|
||||
static ssq::VM* vm();
|
||||
|
||||
template <typename T>
|
||||
static T* getInstance(const char* name) {
|
||||
@@ -72,12 +78,14 @@ protected:
|
||||
creator_t creator = nullptr;
|
||||
exposer_t exposer = nullptr;
|
||||
Module* instance = nullptr;
|
||||
bool exposed = false;
|
||||
};
|
||||
|
||||
std::unordered_map<std::string, ModuleInfo> registered_modules;
|
||||
ssq::VM* active_vm_ = nullptr;
|
||||
};
|
||||
|
||||
struct ModuleRegister {
|
||||
struct EVENGINE_API ModuleRegister {
|
||||
ModuleRegister(const char* name, ModuleManager::creator_t c, ModuleManager::exposer_t e);
|
||||
};
|
||||
|
||||
|
||||
@@ -48,6 +48,8 @@ target_link_libraries(EVUI PUBLIC eve_imgui)
|
||||
target_include_directories(EVUI PUBLIC ${CMAKE_SOURCE_DIR}/third-party/imgui)
|
||||
|
||||
create_module(EVParticles particles)
|
||||
create_module(EVModel3D model3d)
|
||||
create_module(EVPlugins plugins)
|
||||
if(EVENGINE_BUILD_DEMO)
|
||||
create_module(EVDemo demo)
|
||||
endif()
|
||||
@@ -45,8 +45,9 @@
|
||||
6. 管理系统 - system
|
||||
负责系统版本、时间、状态等的查询和设置
|
||||
|
||||
7. 资源管理系统 - image sound video fonts 3dmodel animation
|
||||
7. 资源管理系统 - image sound video fonts 3dmodel(`model3d`/`Model3D`) animation
|
||||
负责加载图形、声音、视频、字体、3d模型、动画等不同格式数据
|
||||
3dmodel:`Model3D::newModelData` / `newModelDataFromFile` → `ModelData`(medialoader Assimp)
|
||||
|
||||
8. 网络系统 - network
|
||||
封装网络通讯的各种基础操作
|
||||
|
||||
@@ -26,6 +26,20 @@ void Graphics::expose(ssq::Table &table) {
|
||||
table.addClass<Texture>("Texture", std::function<Texture *()>([]() -> Texture * { return nullptr; }),
|
||||
true);
|
||||
|
||||
auto shader = table.addClass<Shader>(
|
||||
"Shader", std::function<Shader *()>([]() -> Shader * { return nullptr; }), true);
|
||||
shader.addFunc("declareFloat", &Shader::declareFloat);
|
||||
shader.addFunc("declareVec2", &Shader::declareVec2);
|
||||
shader.addFunc("declareVec3", &Shader::declareVec3);
|
||||
shader.addFunc("declareVec4", &Shader::declareVec4);
|
||||
shader.addFunc("declareMatrix", &Shader::declareMatrix);
|
||||
shader.addFunc("sendFloat", &Shader::sendFloat);
|
||||
shader.addFunc("sendVec2", &Shader::sendVec2);
|
||||
shader.addFunc("sendVec3", &Shader::sendVec3);
|
||||
shader.addFunc("sendVec4", &Shader::sendVec4);
|
||||
shader.addFunc("hasUniform", &Shader::hasUniform);
|
||||
shader.addFunc("getUniformIndex", &Shader::getUniformIndex);
|
||||
|
||||
auto cam = table.addClass<Camera3D>(
|
||||
"Camera3D", std::function<Camera3D *()>([]() { return Camera3D::createCamera(); }), true);
|
||||
cam.addFunc("setEye", &Camera3D::setEye);
|
||||
@@ -43,6 +57,7 @@ void Graphics::expose(ssq::Table &table) {
|
||||
ent.addFunc("setScale", &Renderable3D::setScale);
|
||||
ent.addFunc("setMesh", &Renderable3D::setMesh);
|
||||
ent.addFunc("setTexture", &Renderable3D::setTexture);
|
||||
ent.addFunc("setShader", &Renderable3D::setShader);
|
||||
ent.addFunc("setTint", &Renderable3D::setTint);
|
||||
ent.addFunc("setVisible", &Renderable3D::setVisible);
|
||||
ent.addFunc("setCamera", &Renderable3D::setCamera);
|
||||
@@ -56,11 +71,23 @@ void Graphics::expose(ssq::Class &cls) {
|
||||
cls.addFunc("setBackgroundColor", &Graphics::setBackgroundColorRGBA);
|
||||
cls.addFunc("drawSolidRect", &Graphics::drawSolidRectRGBA);
|
||||
cls.addFunc("newMeshSphere", &Graphics::newMeshSphere);
|
||||
cls.addFunc("newShader",
|
||||
static_cast<Shader *(Graphics::*)(const std::string &)>(&Graphics::newShader));
|
||||
cls.addFunc("newMeshShader",
|
||||
static_cast<Shader *(Graphics::*)(const std::string &)>(&Graphics::newMeshShader));
|
||||
cls.addFunc("newShaderFromSpvFile",
|
||||
static_cast<Shader *(Graphics::*)(const std::string &)>(&Graphics::newShaderFromSpvFile));
|
||||
cls.addFunc("setShader", static_cast<void (Graphics::*)(Shader *)>(&Graphics::setShader));
|
||||
cls.addFunc("getShader", &Graphics::getShader);
|
||||
cls.addFunc("render3D", &Graphics::render3D);
|
||||
cls.addFunc("setDirectionalLight", &Graphics::setDirectionalLight);
|
||||
}
|
||||
|
||||
void Graphics::reset() {}
|
||||
void Graphics::reset() { currentShader = nullptr; }
|
||||
|
||||
void Graphics::setShader(Shader *shader) { currentShader = shader; }
|
||||
|
||||
void Graphics::setShader() { currentShader = nullptr; }
|
||||
|
||||
void Graphics::clearScreen() {
|
||||
clear(std::nullopt, std::nullopt, std::nullopt);
|
||||
|
||||
@@ -68,10 +68,15 @@ public:
|
||||
/** Load file via Filesystem + Image decode, then upload (RGBA8). Throws on failure. */
|
||||
virtual Texture *newTextureFromFile(const std::string &filename) = 0;
|
||||
|
||||
/** Draw a textured quad (full UV 0..1). texture may be null → solid. */
|
||||
/** Draw a textured quad (full UV 0..1). texture may be null → solid.
|
||||
* Uses currentShader when set (or per-call override via drawTexturedRectShader). */
|
||||
virtual void drawTexturedRect(Texture *texture, float x, float y, float w, float h,
|
||||
const Color &color) = 0;
|
||||
|
||||
/** Draw with an explicit Shader (nullptr = default textured pipeline). */
|
||||
virtual void drawTexturedRectShader(Texture *texture, Shader *shader, float x, float y, float w,
|
||||
float h, const Color &color) = 0;
|
||||
|
||||
/** Upload triangulated mesh from Assimp (pos/normal/uv + indices). Owned by Graphics. */
|
||||
virtual Mesh *newMeshFromAssimp(const ::aiMesh &mesh) = 0;
|
||||
|
||||
@@ -99,9 +104,16 @@ virtual void begin3DFrame() = 0;
|
||||
/** Draw one mesh with model matrix. Requires begin3DFrame() (or an open swapchain pass). */
|
||||
virtual void drawMesh(Mesh *mesh, const glm::mat4 &model, Texture *texture, const Color &tint) = 0;
|
||||
|
||||
/** Draw mesh with an explicit Mesh3D Shader (nullptr = default lit pipeline). */
|
||||
virtual void drawMeshShader(Mesh *mesh, const glm::mat4 &model, Texture *texture, const Color &tint,
|
||||
Shader *shader) = 0;
|
||||
|
||||
/** Directional light for subsequent drawMesh calls (world-space direction toward surface). */
|
||||
virtual void setMesh3DLight(const glm::vec3 &dir, const glm::vec3 &color) = 0;
|
||||
|
||||
/** Camera eye used by mesh shaders that need view/rim (stored in Mesh3DUBO). */
|
||||
virtual void setMesh3DCameraPos(const glm::vec3 &eye) = 0;
|
||||
|
||||
/** True after begin3DFrame until present completes. */
|
||||
bool consumeFrameHad3D() {
|
||||
bool v = frameHad3D;
|
||||
@@ -155,7 +167,38 @@ virtual void begin3DFrame() = 0;
|
||||
void setShader(Shader *shader);
|
||||
void setShader();
|
||||
|
||||
Shader *getShader() const;
|
||||
Shader *getShader() const { return currentShader; }
|
||||
|
||||
/**
|
||||
* Create a custom 2D shader from SPIR-V words (vert + frag).
|
||||
* Owned by Graphics. Vertex stage may be empty → uses the default textured vertex shader.
|
||||
*/
|
||||
virtual Shader *newShaderFromSpv(const std::vector<uint32_t> &vertSpv,
|
||||
const std::vector<uint32_t> &fragSpv) = 0;
|
||||
|
||||
/** Load SPIR-V from files via Filesystem (empty vertPath → default textured vert). */
|
||||
virtual Shader *newShaderFromSpvFile(const std::string &vertPath, const std::string &fragPath) = 0;
|
||||
Shader *newShaderFromSpvFile(const std::string &fragPath) {
|
||||
return newShaderFromSpvFile(std::string(), fragPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile GLSL source with glslc (must be on PATH). Empty vertGlsl → default textured vert.
|
||||
* Throws if compilation fails.
|
||||
*/
|
||||
virtual Shader *newShader(const std::string &vertGlsl, const std::string &fragGlsl) = 0;
|
||||
Shader *newShader(const std::string &fragGlsl) { return newShader(std::string(), fragGlsl); }
|
||||
|
||||
/**
|
||||
* Create a Mesh3D custom shader (MeshVertex + Frame UBO + albedo).
|
||||
* Empty vert → default mesh3d.vert. Owned by Graphics.
|
||||
*/
|
||||
virtual Shader *newMeshShaderFromSpv(const std::vector<uint32_t> &vertSpv,
|
||||
const std::vector<uint32_t> &fragSpv) = 0;
|
||||
virtual Shader *newMeshShader(const std::string &vertGlsl, const std::string &fragGlsl) = 0;
|
||||
Shader *newMeshShader(const std::string &fragGlsl) {
|
||||
return newMeshShader(std::string(), fragGlsl);
|
||||
}
|
||||
|
||||
/** Create an offscreen render target (sampleable). Owned by Graphics. */
|
||||
virtual Canvas *newCanvas(int width, int height) = 0;
|
||||
@@ -287,6 +330,7 @@ protected:
|
||||
bool graphicsActive = true;
|
||||
PresentOverlayFn presentOverlayFn_ = nullptr;
|
||||
void *presentOverlayUser_ = nullptr;
|
||||
Shader *currentShader = nullptr;
|
||||
};
|
||||
|
||||
} // namespace eve::graphics
|
||||
@@ -75,6 +75,7 @@ void RenderSystem::render(Graphics &gfx) {
|
||||
Color color;
|
||||
int layer;
|
||||
Texture *texture;
|
||||
Shader *shader;
|
||||
Canvas *canvas;
|
||||
ViewCam cam;
|
||||
};
|
||||
@@ -99,6 +100,7 @@ void RenderSystem::render(Graphics &gfx) {
|
||||
item.color = Color(sp->r, sp->g, sp->b, sp->a);
|
||||
item.layer = sp->layer;
|
||||
item.texture = sp->texture;
|
||||
item.shader = sp->shader;
|
||||
item.canvas = sp->canvas;
|
||||
item.cam = fromEntity(camEnt);
|
||||
items.push_back(item);
|
||||
@@ -110,6 +112,7 @@ void RenderSystem::render(Graphics &gfx) {
|
||||
if (aOff != bOff) return aOff && !bOff;
|
||||
if (a.canvas != b.canvas) return a.canvas < b.canvas;
|
||||
if (a.layer != b.layer) return a.layer < b.layer;
|
||||
if (a.shader != b.shader) return a.shader < b.shader;
|
||||
return a.texture < b.texture;
|
||||
});
|
||||
|
||||
@@ -134,7 +137,7 @@ void RenderSystem::render(Graphics &gfx) {
|
||||
float sx, sy, sw, sh;
|
||||
applyCamera(it.x, it.y, it.w, it.h, it.cam, viewW, viewH, sx, sy, sw, sh);
|
||||
if (it.texture)
|
||||
gfx.drawTexturedRect(it.texture, sx, sy, sw, sh, it.color);
|
||||
gfx.drawTexturedRectShader(it.texture, it.shader, sx, sy, sw, sh, it.color);
|
||||
else
|
||||
gfx.drawSolidRect(sx, sy, sw, sh, it.color);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/ECS.h"
|
||||
#include "graphics/Shader.h"
|
||||
#include "graphics/Texture.h"
|
||||
#include "zeroerr/assert.h"
|
||||
#include <cstdint>
|
||||
@@ -60,6 +61,7 @@ public:
|
||||
int layer = 0;
|
||||
bool visible = true;
|
||||
Texture *texture = nullptr;
|
||||
Shader *shader = nullptr; // nullptr → default textured / solid pipeline
|
||||
Canvas *canvas = nullptr; // nullptr → screen
|
||||
Camera2D *camera = nullptr; // nullptr → default active camera for canvas
|
||||
};
|
||||
|
||||
@@ -88,6 +88,8 @@ void Renderable3D::setMesh(Mesh *mesh) { meshRenderer()->mesh = mesh; }
|
||||
|
||||
void Renderable3D::setTexture(Texture *texture) { meshRenderer()->texture = texture; }
|
||||
|
||||
void Renderable3D::setShader(Shader *shader) { meshRenderer()->shader = shader; }
|
||||
|
||||
void Renderable3D::setTint(float r, float g, float b, float a) {
|
||||
auto mr = meshRenderer();
|
||||
mr->r = r;
|
||||
@@ -137,10 +139,11 @@ void RenderSystem3D::render(Graphics &gfx) {
|
||||
const float fovRad = cd->fovYDeg * 0.017453292519943295f;
|
||||
const glm::mat4 projM = glm::perspectiveRH_ZO(fovRad, aspect, cd->nearZ, cd->farZ);
|
||||
gfx.setMesh3DViewProj(projM * viewM);
|
||||
gfx.setMesh3DCameraPos(eye);
|
||||
|
||||
const glm::mat4 model = modelFromTransform(*xf);
|
||||
const Color tint(mr->r, mr->g, mr->b, mr->a);
|
||||
gfx.drawMesh(mr->mesh, model, mr->texture, tint);
|
||||
gfx.drawMeshShader(mr->mesh, model, mr->texture, tint, mr->shader);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "common/ECS.h"
|
||||
#include "graphics/Mesh.h"
|
||||
#include "graphics/Shader.h"
|
||||
#include "graphics/Texture.h"
|
||||
#include "zeroerr/assert.h"
|
||||
|
||||
@@ -56,6 +57,7 @@ public:
|
||||
struct MeshRenderer {
|
||||
Mesh *mesh = nullptr;
|
||||
Texture *texture = nullptr;
|
||||
Shader *shader = nullptr; // nullptr → default mesh3d lit pipeline
|
||||
float r = 1, g = 1, b = 1, a = 1;
|
||||
bool visible = true;
|
||||
Camera3D *camera = nullptr;
|
||||
@@ -71,6 +73,7 @@ public:
|
||||
void setScale(float sx, float sy, float sz);
|
||||
void setMesh(Mesh *mesh);
|
||||
void setTexture(Texture *texture);
|
||||
void setShader(Shader *shader);
|
||||
void setTint(float r, float g, float b, float a = 1.f);
|
||||
void setVisible(bool visible);
|
||||
void setCamera(Camera3D *camera);
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
#include "graphics/Shader.h"
|
||||
#include "common/Exception.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
namespace eve::graphics {
|
||||
|
||||
Shader::Shader() = default;
|
||||
|
||||
Shader::~Shader() = default;
|
||||
|
||||
int Shader::declareUniform(const std::string &name, int floatCount) {
|
||||
if (name.empty()) throw Exception("Shader::declare: empty uniform name");
|
||||
if (floatCount <= 0) throw Exception("Shader::declare: invalid float count");
|
||||
if (auto *existing = findUniform(name)) {
|
||||
if (existing->floatCount != floatCount)
|
||||
throw Exception("Shader::declare: '%s' already declared with different size", name.c_str());
|
||||
return existing->index;
|
||||
}
|
||||
if (usedFloats_ + floatCount > kMaxFloats)
|
||||
throw Exception("Shader::declare: push-constant block full (max %d floats)", kMaxFloats);
|
||||
Uniform u;
|
||||
u.index = usedFloats_;
|
||||
u.floatCount = floatCount;
|
||||
uniforms_[name] = u;
|
||||
usedFloats_ += floatCount;
|
||||
return u.index;
|
||||
}
|
||||
|
||||
int Shader::declareFloat(const std::string &name) { return declareUniform(name, 1); }
|
||||
int Shader::declareVec2(const std::string &name) { return declareUniform(name, 2); }
|
||||
int Shader::declareVec3(const std::string &name) { return declareUniform(name, 3); }
|
||||
int Shader::declareVec4(const std::string &name) { return declareUniform(name, 4); }
|
||||
int Shader::declareMatrix(const std::string &name) { return declareUniform(name, 16); }
|
||||
|
||||
Shader::Uniform *Shader::findUniform(const std::string &name) {
|
||||
auto it = uniforms_.find(name);
|
||||
return it == uniforms_.end() ? nullptr : &it->second;
|
||||
}
|
||||
|
||||
const Shader::Uniform *Shader::findUniform(const std::string &name) const {
|
||||
auto it = uniforms_.find(name);
|
||||
return it == uniforms_.end() ? nullptr : &it->second;
|
||||
}
|
||||
|
||||
bool Shader::hasUniform(const std::string &name) const { return findUniform(name) != nullptr; }
|
||||
|
||||
int Shader::getUniformIndex(const std::string &name) const {
|
||||
auto *u = findUniform(name);
|
||||
if (!u) throw Exception("Shader: unknown uniform '%s'", name.c_str());
|
||||
return u->index;
|
||||
}
|
||||
|
||||
int Shader::getUniformFloatCount(const std::string &name) const {
|
||||
auto *u = findUniform(name);
|
||||
if (!u) throw Exception("Shader: unknown uniform '%s'", name.c_str());
|
||||
return u->floatCount;
|
||||
}
|
||||
|
||||
int Shader::sendToVar(const std::string &name, const void *data, size_t size) {
|
||||
auto *u = findUniform(name);
|
||||
if (!u) throw Exception("Shader::send: unknown uniform '%s' (declare it first)", name.c_str());
|
||||
if (!data) throw Exception("Shader::send: null data for '%s'", name.c_str());
|
||||
const size_t expected = size_t(u->floatCount) * sizeof(float);
|
||||
if (size != expected)
|
||||
throw Exception("Shader::send: '%s' expects %zu bytes, got %zu", name.c_str(), expected, size);
|
||||
std::memcpy(floats_.data() + u->index, data, size);
|
||||
return int(size);
|
||||
}
|
||||
|
||||
int Shader::getFromVar(const std::string &name, void *data, size_t size) const {
|
||||
auto *u = findUniform(name);
|
||||
if (!u) throw Exception("Shader::get: unknown uniform '%s'", name.c_str());
|
||||
if (!data) throw Exception("Shader::get: null data for '%s'", name.c_str());
|
||||
const size_t expected = size_t(u->floatCount) * sizeof(float);
|
||||
if (size != expected)
|
||||
throw Exception("Shader::get: '%s' expects %zu bytes, got %zu", name.c_str(), expected, size);
|
||||
std::memcpy(data, floats_.data() + u->index, size);
|
||||
return int(size);
|
||||
}
|
||||
|
||||
void Shader::sendFloat(const std::string &name, float x) { sendToVar(name, &x, sizeof(x)); }
|
||||
|
||||
void Shader::sendVec2(const std::string &name, float x, float y) {
|
||||
const float v[2] = {x, y};
|
||||
sendToVar(name, v, sizeof(v));
|
||||
}
|
||||
|
||||
void Shader::sendVec3(const std::string &name, float x, float y, float z) {
|
||||
const float v[3] = {x, y, z};
|
||||
sendToVar(name, v, sizeof(v));
|
||||
}
|
||||
|
||||
void Shader::sendVec4(const std::string &name, float x, float y, float z, float w) {
|
||||
const float v[4] = {x, y, z, w};
|
||||
sendToVar(name, v, sizeof(v));
|
||||
}
|
||||
|
||||
void Shader::sendMatrix(const std::string &name, const glm::mat4 &m) {
|
||||
sendToVar(name, &m[0][0], sizeof(float) * 16);
|
||||
}
|
||||
|
||||
} // namespace eve::graphics
|
||||
@@ -1,45 +1,103 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <stddef.h>
|
||||
|
||||
#include <glm/mat4x4.hpp>
|
||||
|
||||
namespace eve::graphics
|
||||
{
|
||||
namespace eve::graphics {
|
||||
|
||||
class Graphics;
|
||||
|
||||
// A GLSL shader
|
||||
class Shader
|
||||
{
|
||||
/**
|
||||
* Custom GPU program.
|
||||
*
|
||||
* **2D sprites** (`newShader` / `newShaderFromSpv`):
|
||||
* Vertex = TexturedVertex; MainTex @ set0 binding0; optional push-constant data[32].
|
||||
*
|
||||
* **3D meshes** (`newMeshShader` / `newMeshShaderFromSpv`):
|
||||
* Vertex = MeshVertex; Frame UBO @ binding0 + albedo @ binding1 (same as mesh3d);
|
||||
* optional push-constant data[32] for style knobs.
|
||||
*/
|
||||
class Shader {
|
||||
public:
|
||||
enum ShaderType
|
||||
{
|
||||
eCompute,
|
||||
static constexpr int kMaxFloats = 32;
|
||||
static constexpr uint32_t kPushConstantBytes = uint32_t(kMaxFloats * sizeof(float));
|
||||
|
||||
enum class Kind { eSprite2D, eMesh3D };
|
||||
|
||||
enum ShaderType {
|
||||
eCompute,
|
||||
eVertex,
|
||||
eFragment,
|
||||
eGeometry,
|
||||
eTessCtrl,
|
||||
eTessEval
|
||||
};
|
||||
};
|
||||
|
||||
// Pointer to currently active Shader.
|
||||
static Shader *current;
|
||||
Shader();
|
||||
~Shader();
|
||||
|
||||
// Pointer to the default Shader.
|
||||
static Shader *defaultShaders[3];
|
||||
Shader(const Shader &) = delete;
|
||||
Shader &operator=(const Shader &) = delete;
|
||||
|
||||
Shader();
|
||||
virtual ~Shader();
|
||||
Kind getKind() const { return kind_; }
|
||||
void setKind(Kind k) { kind_ = k; }
|
||||
|
||||
int sendToVar(const std::string &name, const void *data, size_t size);
|
||||
int getFromVar(const std::string &name, void *data, size_t size);
|
||||
/** Reserve sequential float slots in the push-constant block. Returns start index. */
|
||||
int declareFloat(const std::string &name);
|
||||
int declareVec2(const std::string &name);
|
||||
int declareVec3(const std::string &name);
|
||||
int declareVec4(const std::string &name);
|
||||
int declareMatrix(const std::string &name);
|
||||
|
||||
|
||||
void sendFloat(const std::string &name, float x);
|
||||
void sendVec2(const std::string &name, float x, float y);
|
||||
void sendVec3(const std::string &name, float x, float y, float z);
|
||||
void sendVec4(const std::string &name, float x, float y, float z, float w);
|
||||
void sendMatrix(const std::string &name, const glm::mat4 &m);
|
||||
|
||||
/** Low-level blob write into a declared uniform (size must match declaration). */
|
||||
int sendToVar(const std::string &name, const void *data, size_t size);
|
||||
int getFromVar(const std::string &name, void *data, size_t size) const;
|
||||
|
||||
}; // Shader
|
||||
bool hasUniform(const std::string &name) const;
|
||||
int getUniformIndex(const std::string &name) const;
|
||||
int getUniformFloatCount(const std::string &name) const;
|
||||
|
||||
} // eve::graphics
|
||||
const float *pushConstantData() const { return floats_.data(); }
|
||||
uint32_t pushConstantSize() const { return uint32_t(usedFloats_ * sizeof(float)); }
|
||||
int usedFloats() const { return usedFloats_; }
|
||||
|
||||
const std::vector<uint32_t> &vertexSpirv() const { return vertSpv_; }
|
||||
const std::vector<uint32_t> &fragmentSpirv() const { return fragSpv_; }
|
||||
|
||||
void setSpirv(std::vector<uint32_t> vert, std::vector<uint32_t> frag) {
|
||||
vertSpv_ = std::move(vert);
|
||||
fragSpv_ = std::move(frag);
|
||||
}
|
||||
|
||||
/** Backend-private GPU object (vulkan::GpuShader*). */
|
||||
void *gpuHandle = nullptr;
|
||||
|
||||
private:
|
||||
struct Uniform {
|
||||
int index = 0;
|
||||
int floatCount = 0;
|
||||
};
|
||||
|
||||
int declareUniform(const std::string &name, int floatCount);
|
||||
Uniform *findUniform(const std::string &name);
|
||||
const Uniform *findUniform(const std::string &name) const;
|
||||
|
||||
Kind kind_ = Kind::eSprite2D;
|
||||
std::vector<uint32_t> vertSpv_;
|
||||
std::vector<uint32_t> fragSpv_;
|
||||
std::map<std::string, Uniform> uniforms_;
|
||||
std::array<float, kMaxFloats> floats_{};
|
||||
int usedFloats_ = 0;
|
||||
};
|
||||
|
||||
} // namespace eve::graphics
|
||||
@@ -0,0 +1,16 @@
|
||||
#version 450
|
||||
// Custom 2D fragment shader sample: tint RGB by push-constant factor.
|
||||
// Uniform layout (declare before send):
|
||||
// declareFloat("factor") → u.data[0]
|
||||
layout(location = 0) in vec4 fragColor;
|
||||
layout(location = 1) in vec2 fragUV;
|
||||
layout(location = 0) out vec4 outColor;
|
||||
layout(binding = 0) uniform sampler2D MainTex;
|
||||
layout(push_constant) uniform Externals {
|
||||
float data[32];
|
||||
} u;
|
||||
void main() {
|
||||
vec4 c = texture(MainTex, fragUV) * fragColor;
|
||||
float f = u.data[0];
|
||||
outColor = vec4(c.rgb * f, c.a);
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,46 @@
|
||||
static const uint32_t custom2d_frag_spv[] = {
|
||||
0x07230203, 0x00010000, 0x000d000b, 0x00000033, 0x00000000, 0x00020011, 0x00000001, 0x0006000b,
|
||||
0x00000001, 0x4c534c47, 0x6474732e, 0x3035342e, 0x00000000, 0x0003000e, 0x00000000, 0x00000001,
|
||||
0x0008000f, 0x00000004, 0x00000004, 0x6e69616d, 0x00000000, 0x00000011, 0x00000015, 0x00000026,
|
||||
0x00030010, 0x00000004, 0x00000007, 0x00030003, 0x00000002, 0x000001c2, 0x000a0004, 0x475f4c47,
|
||||
0x4c474f4f, 0x70635f45, 0x74735f70, 0x5f656c79, 0x656e696c, 0x7269645f, 0x69746365, 0x00006576,
|
||||
0x00080004, 0x475f4c47, 0x4c474f4f, 0x6e695f45, 0x64756c63, 0x69645f65, 0x74636572, 0x00657669,
|
||||
0x00040005, 0x00000004, 0x6e69616d, 0x00000000, 0x00030005, 0x00000009, 0x00000063, 0x00040005,
|
||||
0x0000000d, 0x6e69614d, 0x00786554, 0x00040005, 0x00000011, 0x67617266, 0x00005655, 0x00050005,
|
||||
0x00000015, 0x67617266, 0x6f6c6f43, 0x00000072, 0x00030005, 0x00000019, 0x00000066, 0x00050005,
|
||||
0x0000001d, 0x65747845, 0x6c616e72, 0x00000073, 0x00050006, 0x0000001d, 0x00000000, 0x61746164,
|
||||
0x00000000, 0x00030005, 0x0000001f, 0x00000075, 0x00050005, 0x00000026, 0x4374756f, 0x726f6c6f,
|
||||
0x00000000, 0x00040047, 0x0000000d, 0x00000021, 0x00000000, 0x00040047, 0x0000000d, 0x00000022,
|
||||
0x00000000, 0x00040047, 0x00000011, 0x0000001e, 0x00000001, 0x00040047, 0x00000015, 0x0000001e,
|
||||
0x00000000, 0x00040047, 0x0000001c, 0x00000006, 0x00000004, 0x00030047, 0x0000001d, 0x00000002,
|
||||
0x00050048, 0x0000001d, 0x00000000, 0x00000023, 0x00000000, 0x00040047, 0x00000026, 0x0000001e,
|
||||
0x00000000, 0x00020013, 0x00000002, 0x00030021, 0x00000003, 0x00000002, 0x00030016, 0x00000006,
|
||||
0x00000020, 0x00040017, 0x00000007, 0x00000006, 0x00000004, 0x00040020, 0x00000008, 0x00000007,
|
||||
0x00000007, 0x00090019, 0x0000000a, 0x00000006, 0x00000001, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000001, 0x00000000, 0x0003001b, 0x0000000b, 0x0000000a, 0x00040020, 0x0000000c, 0x00000000,
|
||||
0x0000000b, 0x0004003b, 0x0000000c, 0x0000000d, 0x00000000, 0x00040017, 0x0000000f, 0x00000006,
|
||||
0x00000002, 0x00040020, 0x00000010, 0x00000001, 0x0000000f, 0x0004003b, 0x00000010, 0x00000011,
|
||||
0x00000001, 0x00040020, 0x00000014, 0x00000001, 0x00000007, 0x0004003b, 0x00000014, 0x00000015,
|
||||
0x00000001, 0x00040020, 0x00000018, 0x00000007, 0x00000006, 0x00040015, 0x0000001a, 0x00000020,
|
||||
0x00000000, 0x0004002b, 0x0000001a, 0x0000001b, 0x00000020, 0x0004001c, 0x0000001c, 0x00000006,
|
||||
0x0000001b, 0x0003001e, 0x0000001d, 0x0000001c, 0x00040020, 0x0000001e, 0x00000009, 0x0000001d,
|
||||
0x0004003b, 0x0000001e, 0x0000001f, 0x00000009, 0x00040015, 0x00000020, 0x00000020, 0x00000001,
|
||||
0x0004002b, 0x00000020, 0x00000021, 0x00000000, 0x00040020, 0x00000022, 0x00000009, 0x00000006,
|
||||
0x00040020, 0x00000025, 0x00000003, 0x00000007, 0x0004003b, 0x00000025, 0x00000026, 0x00000003,
|
||||
0x00040017, 0x00000027, 0x00000006, 0x00000003, 0x0004002b, 0x0000001a, 0x0000002c, 0x00000003,
|
||||
0x00050036, 0x00000002, 0x00000004, 0x00000000, 0x00000003, 0x000200f8, 0x00000005, 0x0004003b,
|
||||
0x00000008, 0x00000009, 0x00000007, 0x0004003b, 0x00000018, 0x00000019, 0x00000007, 0x0004003d,
|
||||
0x0000000b, 0x0000000e, 0x0000000d, 0x0004003d, 0x0000000f, 0x00000012, 0x00000011, 0x00050057,
|
||||
0x00000007, 0x00000013, 0x0000000e, 0x00000012, 0x0004003d, 0x00000007, 0x00000016, 0x00000015,
|
||||
0x00050085, 0x00000007, 0x00000017, 0x00000013, 0x00000016, 0x0003003e, 0x00000009, 0x00000017,
|
||||
0x00060041, 0x00000022, 0x00000023, 0x0000001f, 0x00000021, 0x00000021, 0x0004003d, 0x00000006,
|
||||
0x00000024, 0x00000023, 0x0003003e, 0x00000019, 0x00000024, 0x0004003d, 0x00000007, 0x00000028,
|
||||
0x00000009, 0x0008004f, 0x00000027, 0x00000029, 0x00000028, 0x00000028, 0x00000000, 0x00000001,
|
||||
0x00000002, 0x0004003d, 0x00000006, 0x0000002a, 0x00000019, 0x0005008e, 0x00000027, 0x0000002b,
|
||||
0x00000029, 0x0000002a, 0x00050041, 0x00000018, 0x0000002d, 0x00000009, 0x0000002c, 0x0004003d,
|
||||
0x00000006, 0x0000002e, 0x0000002d, 0x00050051, 0x00000006, 0x0000002f, 0x0000002b, 0x00000000,
|
||||
0x00050051, 0x00000006, 0x00000030, 0x0000002b, 0x00000001, 0x00050051, 0x00000006, 0x00000031,
|
||||
0x0000002b, 0x00000002, 0x00070050, 0x00000007, 0x00000032, 0x0000002f, 0x00000030, 0x00000031,
|
||||
0x0000002e, 0x0003003e, 0x00000026, 0x00000032, 0x000100fd, 0x00010038,
|
||||
};
|
||||
static const size_t custom2d_frag_spv_count = sizeof(custom2d_frag_spv)/sizeof(custom2d_frag_spv[0]);
|
||||
@@ -15,7 +15,9 @@ void main() {
|
||||
vec3 N = normalize(vNormal);
|
||||
vec3 L = normalize(vLightDir);
|
||||
float ndotl = max(dot(N, L), 0.0);
|
||||
float ambient = 0.15;
|
||||
vec3 lit = base.rgb * (ambient + ndotl * vLightColor);
|
||||
// Soften just a little so silhouette isn't pure black, but keep contrast.
|
||||
float diffuse = mix(ndotl, ndotl * 0.5 + 0.5, 0.25);
|
||||
float ambient = 0.22;
|
||||
vec3 lit = base.rgb * (ambient + diffuse * vLightColor);
|
||||
outColor = vec4(lit, base.a);
|
||||
}
|
||||
Binary file not shown.
@@ -1,59 +1,62 @@
|
||||
static const uint32_t mesh3d_frag_spv[] = {
|
||||
0x07230203, 0x00010000, 0x000d000b, 0x00000042, 0x00000000, 0x00020011, 0x00000001, 0x0006000b,
|
||||
0x07230203, 0x00010000, 0x0008000b, 0x0000004a, 0x00000000, 0x00020011, 0x00000001, 0x0006000b,
|
||||
0x00000001, 0x4c534c47, 0x6474732e, 0x3035342e, 0x00000000, 0x0003000e, 0x00000000, 0x00000001,
|
||||
0x000b000f, 0x00000004, 0x00000004, 0x6e69616d, 0x00000000, 0x00000011, 0x00000015, 0x0000001c,
|
||||
0x00000020, 0x00000031, 0x00000038, 0x00030010, 0x00000004, 0x00000007, 0x00030003, 0x00000002,
|
||||
0x000001c2, 0x000a0004, 0x475f4c47, 0x4c474f4f, 0x70635f45, 0x74735f70, 0x5f656c79, 0x656e696c,
|
||||
0x7269645f, 0x69746365, 0x00006576, 0x00080004, 0x475f4c47, 0x4c474f4f, 0x6e695f45, 0x64756c63,
|
||||
0x69645f65, 0x74636572, 0x00657669, 0x00040005, 0x00000004, 0x6e69616d, 0x00000000, 0x00040005,
|
||||
0x00000009, 0x65736162, 0x00000000, 0x00040005, 0x0000000d, 0x65626c61, 0x00006f64, 0x00030005,
|
||||
0x00000011, 0x00565576, 0x00040005, 0x00000015, 0x6e695476, 0x00000074, 0x00030005, 0x0000001a,
|
||||
0x0000004e, 0x00040005, 0x0000001c, 0x726f4e76, 0x006c616d, 0x00030005, 0x0000001f, 0x0000004c,
|
||||
0x00050005, 0x00000020, 0x67694c76, 0x69447468, 0x00000072, 0x00040005, 0x00000024, 0x746f646e,
|
||||
0x0000006c, 0x00040005, 0x0000002a, 0x69626d61, 0x00746e65, 0x00030005, 0x0000002c, 0x0074696c,
|
||||
0x00050005, 0x00000031, 0x67694c76, 0x6f437468, 0x00726f6c, 0x00050005, 0x00000038, 0x4374756f,
|
||||
0x726f6c6f, 0x00000000, 0x00040047, 0x0000000d, 0x00000021, 0x00000001, 0x00040047, 0x0000000d,
|
||||
0x00000022, 0x00000000, 0x00040047, 0x00000011, 0x0000001e, 0x00000001, 0x00040047, 0x00000015,
|
||||
0x0000001e, 0x00000002, 0x00040047, 0x0000001c, 0x0000001e, 0x00000000, 0x00040047, 0x00000020,
|
||||
0x0000001e, 0x00000003, 0x00040047, 0x00000031, 0x0000001e, 0x00000004, 0x00040047, 0x00000038,
|
||||
0x0000001e, 0x00000000, 0x00020013, 0x00000002, 0x00030021, 0x00000003, 0x00000002, 0x00030016,
|
||||
0x00000006, 0x00000020, 0x00040017, 0x00000007, 0x00000006, 0x00000004, 0x00040020, 0x00000008,
|
||||
0x00000007, 0x00000007, 0x00090019, 0x0000000a, 0x00000006, 0x00000001, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000001, 0x00000000, 0x0003001b, 0x0000000b, 0x0000000a, 0x00040020, 0x0000000c,
|
||||
0x00000000, 0x0000000b, 0x0004003b, 0x0000000c, 0x0000000d, 0x00000000, 0x00040017, 0x0000000f,
|
||||
0x00000006, 0x00000002, 0x00040020, 0x00000010, 0x00000001, 0x0000000f, 0x0004003b, 0x00000010,
|
||||
0x00000011, 0x00000001, 0x00040020, 0x00000014, 0x00000001, 0x00000007, 0x0004003b, 0x00000014,
|
||||
0x00000015, 0x00000001, 0x00040017, 0x00000018, 0x00000006, 0x00000003, 0x00040020, 0x00000019,
|
||||
0x00000007, 0x00000018, 0x00040020, 0x0000001b, 0x00000001, 0x00000018, 0x0004003b, 0x0000001b,
|
||||
0x0000001c, 0x00000001, 0x0004003b, 0x0000001b, 0x00000020, 0x00000001, 0x00040020, 0x00000023,
|
||||
0x00000007, 0x00000006, 0x0004002b, 0x00000006, 0x00000028, 0x00000000, 0x0004002b, 0x00000006,
|
||||
0x0000002b, 0x3e19999a, 0x0004003b, 0x0000001b, 0x00000031, 0x00000001, 0x00040020, 0x00000037,
|
||||
0x00000003, 0x00000007, 0x0004003b, 0x00000037, 0x00000038, 0x00000003, 0x00040015, 0x0000003a,
|
||||
0x00000020, 0x00000000, 0x0004002b, 0x0000003a, 0x0000003b, 0x00000003, 0x00050036, 0x00000002,
|
||||
0x00000004, 0x00000000, 0x00000003, 0x000200f8, 0x00000005, 0x0004003b, 0x00000008, 0x00000009,
|
||||
0x00000007, 0x0004003b, 0x00000019, 0x0000001a, 0x00000007, 0x0004003b, 0x00000019, 0x0000001f,
|
||||
0x00000007, 0x0004003b, 0x00000023, 0x00000024, 0x00000007, 0x0004003b, 0x00000023, 0x0000002a,
|
||||
0x00000007, 0x0004003b, 0x00000019, 0x0000002c, 0x00000007, 0x0004003d, 0x0000000b, 0x0000000e,
|
||||
0x0000000d, 0x0004003d, 0x0000000f, 0x00000012, 0x00000011, 0x00050057, 0x00000007, 0x00000013,
|
||||
0x0000000e, 0x00000012, 0x0004003d, 0x00000007, 0x00000016, 0x00000015, 0x00050085, 0x00000007,
|
||||
0x00000017, 0x00000013, 0x00000016, 0x0003003e, 0x00000009, 0x00000017, 0x0004003d, 0x00000018,
|
||||
0x0000001d, 0x0000001c, 0x0006000c, 0x00000018, 0x0000001e, 0x00000001, 0x00000045, 0x0000001d,
|
||||
0x0003003e, 0x0000001a, 0x0000001e, 0x0004003d, 0x00000018, 0x00000021, 0x00000020, 0x0006000c,
|
||||
0x00000018, 0x00000022, 0x00000001, 0x00000045, 0x00000021, 0x0003003e, 0x0000001f, 0x00000022,
|
||||
0x0004003d, 0x00000018, 0x00000025, 0x0000001a, 0x0004003d, 0x00000018, 0x00000026, 0x0000001f,
|
||||
0x00050094, 0x00000006, 0x00000027, 0x00000025, 0x00000026, 0x0007000c, 0x00000006, 0x00000029,
|
||||
0x00000001, 0x00000028, 0x00000027, 0x00000028, 0x0003003e, 0x00000024, 0x00000029, 0x0003003e,
|
||||
0x0000002a, 0x0000002b, 0x0004003d, 0x00000007, 0x0000002d, 0x00000009, 0x0008004f, 0x00000018,
|
||||
0x0000002e, 0x0000002d, 0x0000002d, 0x00000000, 0x00000001, 0x00000002, 0x0004003d, 0x00000006,
|
||||
0x0000002f, 0x0000002a, 0x0004003d, 0x00000006, 0x00000030, 0x00000024, 0x0004003d, 0x00000018,
|
||||
0x00000032, 0x00000031, 0x0005008e, 0x00000018, 0x00000033, 0x00000032, 0x00000030, 0x00060050,
|
||||
0x00000018, 0x00000034, 0x0000002f, 0x0000002f, 0x0000002f, 0x00050081, 0x00000018, 0x00000035,
|
||||
0x00000034, 0x00000033, 0x00050085, 0x00000018, 0x00000036, 0x0000002e, 0x00000035, 0x0003003e,
|
||||
0x0000002c, 0x00000036, 0x0004003d, 0x00000018, 0x00000039, 0x0000002c, 0x00050041, 0x00000023,
|
||||
0x0000003c, 0x00000009, 0x0000003b, 0x0004003d, 0x00000006, 0x0000003d, 0x0000003c, 0x00050051,
|
||||
0x00000006, 0x0000003e, 0x00000039, 0x00000000, 0x00050051, 0x00000006, 0x0000003f, 0x00000039,
|
||||
0x00000001, 0x00050051, 0x00000006, 0x00000040, 0x00000039, 0x00000002, 0x00070050, 0x00000007,
|
||||
0x00000041, 0x0000003e, 0x0000003f, 0x00000040, 0x0000003d, 0x0003003e, 0x00000038, 0x00000041,
|
||||
0x000100fd, 0x00010038
|
||||
0x00000020, 0x00000039, 0x00000040, 0x00030010, 0x00000004, 0x00000007, 0x00030003, 0x00000002,
|
||||
0x000001c2, 0x00040005, 0x00000004, 0x6e69616d, 0x00000000, 0x00040005, 0x00000009, 0x65736162,
|
||||
0x00000000, 0x00040005, 0x0000000d, 0x65626c61, 0x00006f64, 0x00030005, 0x00000011, 0x00565576,
|
||||
0x00040005, 0x00000015, 0x6e695476, 0x00000074, 0x00030005, 0x0000001a, 0x0000004e, 0x00040005,
|
||||
0x0000001c, 0x726f4e76, 0x006c616d, 0x00030005, 0x0000001f, 0x0000004c, 0x00050005, 0x00000020,
|
||||
0x67694c76, 0x69447468, 0x00000072, 0x00040005, 0x00000024, 0x746f646e, 0x0000006c, 0x00040005,
|
||||
0x0000002a, 0x66666964, 0x00657375, 0x00040005, 0x00000032, 0x69626d61, 0x00746e65, 0x00030005,
|
||||
0x00000034, 0x0074696c, 0x00050005, 0x00000039, 0x67694c76, 0x6f437468, 0x00726f6c, 0x00050005,
|
||||
0x00000040, 0x4374756f, 0x726f6c6f, 0x00000000, 0x00040047, 0x0000000d, 0x00000021, 0x00000001,
|
||||
0x00040047, 0x0000000d, 0x00000022, 0x00000000, 0x00040047, 0x00000011, 0x0000001e, 0x00000001,
|
||||
0x00040047, 0x00000015, 0x0000001e, 0x00000002, 0x00040047, 0x0000001c, 0x0000001e, 0x00000000,
|
||||
0x00040047, 0x00000020, 0x0000001e, 0x00000003, 0x00040047, 0x00000039, 0x0000001e, 0x00000004,
|
||||
0x00040047, 0x00000040, 0x0000001e, 0x00000000, 0x00020013, 0x00000002, 0x00030021, 0x00000003,
|
||||
0x00000002, 0x00030016, 0x00000006, 0x00000020, 0x00040017, 0x00000007, 0x00000006, 0x00000004,
|
||||
0x00040020, 0x00000008, 0x00000007, 0x00000007, 0x00090019, 0x0000000a, 0x00000006, 0x00000001,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000000, 0x0003001b, 0x0000000b, 0x0000000a,
|
||||
0x00040020, 0x0000000c, 0x00000000, 0x0000000b, 0x0004003b, 0x0000000c, 0x0000000d, 0x00000000,
|
||||
0x00040017, 0x0000000f, 0x00000006, 0x00000002, 0x00040020, 0x00000010, 0x00000001, 0x0000000f,
|
||||
0x0004003b, 0x00000010, 0x00000011, 0x00000001, 0x00040020, 0x00000014, 0x00000001, 0x00000007,
|
||||
0x0004003b, 0x00000014, 0x00000015, 0x00000001, 0x00040017, 0x00000018, 0x00000006, 0x00000003,
|
||||
0x00040020, 0x00000019, 0x00000007, 0x00000018, 0x00040020, 0x0000001b, 0x00000001, 0x00000018,
|
||||
0x0004003b, 0x0000001b, 0x0000001c, 0x00000001, 0x0004003b, 0x0000001b, 0x00000020, 0x00000001,
|
||||
0x00040020, 0x00000023, 0x00000007, 0x00000006, 0x0004002b, 0x00000006, 0x00000028, 0x00000000,
|
||||
0x0004002b, 0x00000006, 0x0000002d, 0x3f000000, 0x0004002b, 0x00000006, 0x00000030, 0x3e800000,
|
||||
0x0004002b, 0x00000006, 0x00000033, 0x3e6147ae, 0x0004003b, 0x0000001b, 0x00000039, 0x00000001,
|
||||
0x00040020, 0x0000003f, 0x00000003, 0x00000007, 0x0004003b, 0x0000003f, 0x00000040, 0x00000003,
|
||||
0x00040015, 0x00000042, 0x00000020, 0x00000000, 0x0004002b, 0x00000042, 0x00000043, 0x00000003,
|
||||
0x00050036, 0x00000002, 0x00000004, 0x00000000, 0x00000003, 0x000200f8, 0x00000005, 0x0004003b,
|
||||
0x00000008, 0x00000009, 0x00000007, 0x0004003b, 0x00000019, 0x0000001a, 0x00000007, 0x0004003b,
|
||||
0x00000019, 0x0000001f, 0x00000007, 0x0004003b, 0x00000023, 0x00000024, 0x00000007, 0x0004003b,
|
||||
0x00000023, 0x0000002a, 0x00000007, 0x0004003b, 0x00000023, 0x00000032, 0x00000007, 0x0004003b,
|
||||
0x00000019, 0x00000034, 0x00000007, 0x0004003d, 0x0000000b, 0x0000000e, 0x0000000d, 0x0004003d,
|
||||
0x0000000f, 0x00000012, 0x00000011, 0x00050057, 0x00000007, 0x00000013, 0x0000000e, 0x00000012,
|
||||
0x0004003d, 0x00000007, 0x00000016, 0x00000015, 0x00050085, 0x00000007, 0x00000017, 0x00000013,
|
||||
0x00000016, 0x0003003e, 0x00000009, 0x00000017, 0x0004003d, 0x00000018, 0x0000001d, 0x0000001c,
|
||||
0x0006000c, 0x00000018, 0x0000001e, 0x00000001, 0x00000045, 0x0000001d, 0x0003003e, 0x0000001a,
|
||||
0x0000001e, 0x0004003d, 0x00000018, 0x00000021, 0x00000020, 0x0006000c, 0x00000018, 0x00000022,
|
||||
0x00000001, 0x00000045, 0x00000021, 0x0003003e, 0x0000001f, 0x00000022, 0x0004003d, 0x00000018,
|
||||
0x00000025, 0x0000001a, 0x0004003d, 0x00000018, 0x00000026, 0x0000001f, 0x00050094, 0x00000006,
|
||||
0x00000027, 0x00000025, 0x00000026, 0x0007000c, 0x00000006, 0x00000029, 0x00000001, 0x00000028,
|
||||
0x00000027, 0x00000028, 0x0003003e, 0x00000024, 0x00000029, 0x0004003d, 0x00000006, 0x0000002b,
|
||||
0x00000024, 0x0004003d, 0x00000006, 0x0000002c, 0x00000024, 0x00050085, 0x00000006, 0x0000002e,
|
||||
0x0000002c, 0x0000002d, 0x00050081, 0x00000006, 0x0000002f, 0x0000002e, 0x0000002d, 0x0008000c,
|
||||
0x00000006, 0x00000031, 0x00000001, 0x0000002e, 0x0000002b, 0x0000002f, 0x00000030, 0x0003003e,
|
||||
0x0000002a, 0x00000031, 0x0003003e, 0x00000032, 0x00000033, 0x0004003d, 0x00000007, 0x00000035,
|
||||
0x00000009, 0x0008004f, 0x00000018, 0x00000036, 0x00000035, 0x00000035, 0x00000000, 0x00000001,
|
||||
0x00000002, 0x0004003d, 0x00000006, 0x00000037, 0x00000032, 0x0004003d, 0x00000006, 0x00000038,
|
||||
0x0000002a, 0x0004003d, 0x00000018, 0x0000003a, 0x00000039, 0x0005008e, 0x00000018, 0x0000003b,
|
||||
0x0000003a, 0x00000038, 0x00060050, 0x00000018, 0x0000003c, 0x00000037, 0x00000037, 0x00000037,
|
||||
0x00050081, 0x00000018, 0x0000003d, 0x0000003c, 0x0000003b, 0x00050085, 0x00000018, 0x0000003e,
|
||||
0x00000036, 0x0000003d, 0x0003003e, 0x00000034, 0x0000003e, 0x0004003d, 0x00000018, 0x00000041,
|
||||
0x00000034, 0x00050041, 0x00000023, 0x00000044, 0x00000009, 0x00000043, 0x0004003d, 0x00000006,
|
||||
0x00000045, 0x00000044, 0x00050051, 0x00000006, 0x00000046, 0x00000041, 0x00000000, 0x00050051,
|
||||
0x00000006, 0x00000047, 0x00000041, 0x00000001, 0x00050051, 0x00000006, 0x00000048, 0x00000041,
|
||||
0x00000002, 0x00070050, 0x00000007, 0x00000049, 0x00000046, 0x00000047, 0x00000048, 0x00000045,
|
||||
0x0003003e, 0x00000040, 0x00000049, 0x000100fd, 0x00010038,
|
||||
};
|
||||
static const size_t mesh3d_frag_spv_count = 442;
|
||||
static const size_t mesh3d_frag_spv_count = 469;
|
||||
@@ -0,0 +1,55 @@
|
||||
#version 450
|
||||
// Stylized cel / toon fragment for Mesh3D (Rock demo).
|
||||
// Uses same Frame UBO + albedo sampler as mesh3d.frag.
|
||||
// Push constants (optional): data[0]=bands, data[1]=rimPower,
|
||||
// data[2]=rimStrength, data[3]=posterizeSteps
|
||||
|
||||
layout(location = 0) in vec3 vNormal;
|
||||
layout(location = 1) in vec2 vUV;
|
||||
layout(location = 2) in vec4 vTint;
|
||||
layout(location = 3) in vec3 vLightDir;
|
||||
layout(location = 4) in vec3 vLightColor;
|
||||
layout(location = 5) in vec3 vWorldPos;
|
||||
layout(location = 6) in vec3 vCameraPos;
|
||||
|
||||
layout(set = 0, binding = 1) uniform sampler2D albedo;
|
||||
|
||||
layout(push_constant) uniform Externals {
|
||||
float data[32];
|
||||
} u;
|
||||
|
||||
layout(location = 0) out vec4 outColor;
|
||||
|
||||
void main() {
|
||||
vec4 base = texture(albedo, vUV) * vTint;
|
||||
vec3 N = normalize(vNormal);
|
||||
vec3 L = normalize(vLightDir);
|
||||
float ndotl = max(dot(N, L), 0.0);
|
||||
|
||||
float bands = max(u.data[0], 2.0);
|
||||
float rimPower = max(u.data[1], 0.5);
|
||||
float rimStrength = u.data[2];
|
||||
float posterize = max(u.data[3], 2.0);
|
||||
|
||||
// Soft cel bands
|
||||
float cel = floor(ndotl * bands) / max(bands - 1.0, 1.0);
|
||||
// Warm shadow / cool mid / warm highlight ramp
|
||||
vec3 shadowCol = vec3(0.18, 0.14, 0.28);
|
||||
vec3 midCol = vec3(0.85, 0.72, 0.55);
|
||||
vec3 hiCol = vec3(1.15, 1.05, 0.90);
|
||||
vec3 ramp = mix(shadowCol, midCol, clamp(cel * 1.4, 0.0, 1.0));
|
||||
ramp = mix(ramp, hiCol, smoothstep(0.65, 1.0, cel));
|
||||
|
||||
// Posterize albedo toward painted look
|
||||
vec3 alb = base.rgb;
|
||||
alb = floor(alb * posterize + 0.5) / posterize;
|
||||
|
||||
// Rim light (view-facing edges)
|
||||
vec3 V = normalize(vCameraPos - vWorldPos);
|
||||
float rim = pow(clamp(1.0 - max(dot(N, V), 0.0), 0.0, 1.0), rimPower);
|
||||
vec3 rimCol = vec3(0.55, 0.85, 1.15) * rim * rimStrength;
|
||||
|
||||
float ambient = 0.18;
|
||||
vec3 lit = alb * (ambient + ramp * vLightColor) + rimCol * alb;
|
||||
outColor = vec4(lit, base.a);
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,36 @@
|
||||
#version 450
|
||||
// Mesh3D vertex for stylized shaders — same UBO as mesh3d.vert, plus world/camera for rim.
|
||||
|
||||
layout(location = 0) in vec3 inPos;
|
||||
layout(location = 1) in vec3 inNormal;
|
||||
layout(location = 2) in vec2 inUV;
|
||||
|
||||
layout(set = 0, binding = 0) uniform Frame {
|
||||
mat4 mvp;
|
||||
mat4 model;
|
||||
vec4 lightDirIntensity;
|
||||
vec4 lightColor;
|
||||
vec4 tint;
|
||||
vec4 cameraPos; // xyz
|
||||
} ubo;
|
||||
|
||||
layout(location = 0) out vec3 vNormal;
|
||||
layout(location = 1) out vec2 vUV;
|
||||
layout(location = 2) out vec4 vTint;
|
||||
layout(location = 3) out vec3 vLightDir;
|
||||
layout(location = 4) out vec3 vLightColor;
|
||||
layout(location = 5) out vec3 vWorldPos;
|
||||
layout(location = 6) out vec3 vCameraPos;
|
||||
|
||||
void main() {
|
||||
gl_Position = ubo.mvp * vec4(inPos, 1.0);
|
||||
vec4 world = ubo.model * vec4(inPos, 1.0);
|
||||
vWorldPos = world.xyz;
|
||||
mat3 normalMat = mat3(ubo.model);
|
||||
vNormal = normalize(normalMat * inNormal);
|
||||
vUV = inUV;
|
||||
vTint = ubo.tint;
|
||||
vLightDir = normalize(ubo.lightDirIntensity.xyz);
|
||||
vLightColor = ubo.lightColor.rgb;
|
||||
vCameraPos = ubo.cameraPos.xyz;
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,139 @@
|
||||
static const uint32_t mesh3d_toon_frag_spv[] = {
|
||||
0x07230203, 0x00010000, 0x000d000b, 0x000000ab, 0x00000000, 0x00020011, 0x00000001, 0x0006000b,
|
||||
0x00000001, 0x4c534c47, 0x6474732e, 0x3035342e, 0x00000000, 0x0003000e, 0x00000000, 0x00000001,
|
||||
0x000d000f, 0x00000004, 0x00000004, 0x6e69616d, 0x00000000, 0x00000011, 0x00000015, 0x0000001c,
|
||||
0x00000020, 0x0000007d, 0x0000007f, 0x00000097, 0x000000a2, 0x00030010, 0x00000004, 0x00000007,
|
||||
0x00030003, 0x00000002, 0x000001c2, 0x000a0004, 0x475f4c47, 0x4c474f4f, 0x70635f45, 0x74735f70,
|
||||
0x5f656c79, 0x656e696c, 0x7269645f, 0x69746365, 0x00006576, 0x00080004, 0x475f4c47, 0x4c474f4f,
|
||||
0x6e695f45, 0x64756c63, 0x69645f65, 0x74636572, 0x00657669, 0x00040005, 0x00000004, 0x6e69616d,
|
||||
0x00000000, 0x00040005, 0x00000009, 0x65736162, 0x00000000, 0x00040005, 0x0000000d, 0x65626c61,
|
||||
0x00006f64, 0x00030005, 0x00000011, 0x00565576, 0x00040005, 0x00000015, 0x6e695476, 0x00000074,
|
||||
0x00030005, 0x0000001a, 0x0000004e, 0x00040005, 0x0000001c, 0x726f4e76, 0x006c616d, 0x00030005,
|
||||
0x0000001f, 0x0000004c, 0x00050005, 0x00000020, 0x67694c76, 0x69447468, 0x00000072, 0x00040005,
|
||||
0x00000024, 0x746f646e, 0x0000006c, 0x00040005, 0x0000002a, 0x646e6162, 0x00000073, 0x00050005,
|
||||
0x0000002e, 0x65747845, 0x6c616e72, 0x00000073, 0x00050006, 0x0000002e, 0x00000000, 0x61746164,
|
||||
0x00000000, 0x00030005, 0x00000030, 0x00000075, 0x00050005, 0x00000038, 0x506d6972, 0x7265776f,
|
||||
0x00000000, 0x00050005, 0x0000003e, 0x536d6972, 0x6e657274, 0x00687467, 0x00050005, 0x00000042,
|
||||
0x74736f70, 0x7a697265, 0x00000065, 0x00030005, 0x00000047, 0x006c6563, 0x00050005, 0x00000051,
|
||||
0x64616873, 0x6f43776f, 0x0000006c, 0x00040005, 0x00000056, 0x4364696d, 0x00006c6f, 0x00040005,
|
||||
0x0000005b, 0x6f436968, 0x0000006c, 0x00040005, 0x00000060, 0x706d6172, 0x00000000, 0x00030005,
|
||||
0x00000070, 0x00626c61, 0x00030005, 0x0000007c, 0x00000056, 0x00050005, 0x0000007d, 0x6d614376,
|
||||
0x50617265, 0x0000736f, 0x00050005, 0x0000007f, 0x726f5776, 0x6f50646c, 0x00000073, 0x00030005,
|
||||
0x00000083, 0x006d6972, 0x00040005, 0x0000008c, 0x436d6972, 0x00006c6f, 0x00040005, 0x00000092,
|
||||
0x69626d61, 0x00746e65, 0x00030005, 0x00000093, 0x0074696c, 0x00050005, 0x00000097, 0x67694c76,
|
||||
0x6f437468, 0x00726f6c, 0x00050005, 0x000000a2, 0x4374756f, 0x726f6c6f, 0x00000000, 0x00040047,
|
||||
0x0000000d, 0x00000021, 0x00000001, 0x00040047, 0x0000000d, 0x00000022, 0x00000000, 0x00040047,
|
||||
0x00000011, 0x0000001e, 0x00000001, 0x00040047, 0x00000015, 0x0000001e, 0x00000002, 0x00040047,
|
||||
0x0000001c, 0x0000001e, 0x00000000, 0x00040047, 0x00000020, 0x0000001e, 0x00000003, 0x00040047,
|
||||
0x0000002d, 0x00000006, 0x00000004, 0x00030047, 0x0000002e, 0x00000002, 0x00050048, 0x0000002e,
|
||||
0x00000000, 0x00000023, 0x00000000, 0x00040047, 0x0000007d, 0x0000001e, 0x00000006, 0x00040047,
|
||||
0x0000007f, 0x0000001e, 0x00000005, 0x00040047, 0x00000097, 0x0000001e, 0x00000004, 0x00040047,
|
||||
0x000000a2, 0x0000001e, 0x00000000, 0x00020013, 0x00000002, 0x00030021, 0x00000003, 0x00000002,
|
||||
0x00030016, 0x00000006, 0x00000020, 0x00040017, 0x00000007, 0x00000006, 0x00000004, 0x00040020,
|
||||
0x00000008, 0x00000007, 0x00000007, 0x00090019, 0x0000000a, 0x00000006, 0x00000001, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000001, 0x00000000, 0x0003001b, 0x0000000b, 0x0000000a, 0x00040020,
|
||||
0x0000000c, 0x00000000, 0x0000000b, 0x0004003b, 0x0000000c, 0x0000000d, 0x00000000, 0x00040017,
|
||||
0x0000000f, 0x00000006, 0x00000002, 0x00040020, 0x00000010, 0x00000001, 0x0000000f, 0x0004003b,
|
||||
0x00000010, 0x00000011, 0x00000001, 0x00040020, 0x00000014, 0x00000001, 0x00000007, 0x0004003b,
|
||||
0x00000014, 0x00000015, 0x00000001, 0x00040017, 0x00000018, 0x00000006, 0x00000003, 0x00040020,
|
||||
0x00000019, 0x00000007, 0x00000018, 0x00040020, 0x0000001b, 0x00000001, 0x00000018, 0x0004003b,
|
||||
0x0000001b, 0x0000001c, 0x00000001, 0x0004003b, 0x0000001b, 0x00000020, 0x00000001, 0x00040020,
|
||||
0x00000023, 0x00000007, 0x00000006, 0x0004002b, 0x00000006, 0x00000028, 0x00000000, 0x00040015,
|
||||
0x0000002b, 0x00000020, 0x00000000, 0x0004002b, 0x0000002b, 0x0000002c, 0x00000020, 0x0004001c,
|
||||
0x0000002d, 0x00000006, 0x0000002c, 0x0003001e, 0x0000002e, 0x0000002d, 0x00040020, 0x0000002f,
|
||||
0x00000009, 0x0000002e, 0x0004003b, 0x0000002f, 0x00000030, 0x00000009, 0x00040015, 0x00000031,
|
||||
0x00000020, 0x00000001, 0x0004002b, 0x00000031, 0x00000032, 0x00000000, 0x00040020, 0x00000033,
|
||||
0x00000009, 0x00000006, 0x0004002b, 0x00000006, 0x00000036, 0x40000000, 0x0004002b, 0x00000031,
|
||||
0x00000039, 0x00000001, 0x0004002b, 0x00000006, 0x0000003c, 0x3f000000, 0x0004002b, 0x00000031,
|
||||
0x0000003f, 0x00000002, 0x0004002b, 0x00000031, 0x00000043, 0x00000003, 0x0004002b, 0x00000006,
|
||||
0x0000004d, 0x3f800000, 0x0004002b, 0x00000006, 0x00000052, 0x3e3851ec, 0x0004002b, 0x00000006,
|
||||
0x00000053, 0x3e0f5c29, 0x0004002b, 0x00000006, 0x00000054, 0x3e8f5c29, 0x0006002c, 0x00000018,
|
||||
0x00000055, 0x00000052, 0x00000053, 0x00000054, 0x0004002b, 0x00000006, 0x00000057, 0x3f59999a,
|
||||
0x0004002b, 0x00000006, 0x00000058, 0x3f3851ec, 0x0004002b, 0x00000006, 0x00000059, 0x3f0ccccd,
|
||||
0x0006002c, 0x00000018, 0x0000005a, 0x00000057, 0x00000058, 0x00000059, 0x0004002b, 0x00000006,
|
||||
0x0000005c, 0x3f933333, 0x0004002b, 0x00000006, 0x0000005d, 0x3f866666, 0x0004002b, 0x00000006,
|
||||
0x0000005e, 0x3f666666, 0x0006002c, 0x00000018, 0x0000005f, 0x0000005c, 0x0000005d, 0x0000005e,
|
||||
0x0004002b, 0x00000006, 0x00000064, 0x3fb33333, 0x0004002b, 0x00000006, 0x0000006b, 0x3f266666,
|
||||
0x0004003b, 0x0000001b, 0x0000007d, 0x00000001, 0x0004003b, 0x0000001b, 0x0000007f, 0x00000001,
|
||||
0x0006002c, 0x00000018, 0x0000008d, 0x00000059, 0x00000057, 0x0000005c, 0x0004003b, 0x0000001b,
|
||||
0x00000097, 0x00000001, 0x00040020, 0x000000a1, 0x00000003, 0x00000007, 0x0004003b, 0x000000a1,
|
||||
0x000000a2, 0x00000003, 0x0004002b, 0x0000002b, 0x000000a4, 0x00000003, 0x00050036, 0x00000002,
|
||||
0x00000004, 0x00000000, 0x00000003, 0x000200f8, 0x00000005, 0x0004003b, 0x00000008, 0x00000009,
|
||||
0x00000007, 0x0004003b, 0x00000019, 0x0000001a, 0x00000007, 0x0004003b, 0x00000019, 0x0000001f,
|
||||
0x00000007, 0x0004003b, 0x00000023, 0x00000024, 0x00000007, 0x0004003b, 0x00000023, 0x0000002a,
|
||||
0x00000007, 0x0004003b, 0x00000023, 0x00000038, 0x00000007, 0x0004003b, 0x00000023, 0x0000003e,
|
||||
0x00000007, 0x0004003b, 0x00000023, 0x00000042, 0x00000007, 0x0004003b, 0x00000023, 0x00000047,
|
||||
0x00000007, 0x0004003b, 0x00000019, 0x00000051, 0x00000007, 0x0004003b, 0x00000019, 0x00000056,
|
||||
0x00000007, 0x0004003b, 0x00000019, 0x0000005b, 0x00000007, 0x0004003b, 0x00000019, 0x00000060,
|
||||
0x00000007, 0x0004003b, 0x00000019, 0x00000070, 0x00000007, 0x0004003b, 0x00000019, 0x0000007c,
|
||||
0x00000007, 0x0004003b, 0x00000023, 0x00000083, 0x00000007, 0x0004003b, 0x00000019, 0x0000008c,
|
||||
0x00000007, 0x0004003b, 0x00000023, 0x00000092, 0x00000007, 0x0004003b, 0x00000019, 0x00000093,
|
||||
0x00000007, 0x0004003d, 0x0000000b, 0x0000000e, 0x0000000d, 0x0004003d, 0x0000000f, 0x00000012,
|
||||
0x00000011, 0x00050057, 0x00000007, 0x00000013, 0x0000000e, 0x00000012, 0x0004003d, 0x00000007,
|
||||
0x00000016, 0x00000015, 0x00050085, 0x00000007, 0x00000017, 0x00000013, 0x00000016, 0x0003003e,
|
||||
0x00000009, 0x00000017, 0x0004003d, 0x00000018, 0x0000001d, 0x0000001c, 0x0006000c, 0x00000018,
|
||||
0x0000001e, 0x00000001, 0x00000045, 0x0000001d, 0x0003003e, 0x0000001a, 0x0000001e, 0x0004003d,
|
||||
0x00000018, 0x00000021, 0x00000020, 0x0006000c, 0x00000018, 0x00000022, 0x00000001, 0x00000045,
|
||||
0x00000021, 0x0003003e, 0x0000001f, 0x00000022, 0x0004003d, 0x00000018, 0x00000025, 0x0000001a,
|
||||
0x0004003d, 0x00000018, 0x00000026, 0x0000001f, 0x00050094, 0x00000006, 0x00000027, 0x00000025,
|
||||
0x00000026, 0x0007000c, 0x00000006, 0x00000029, 0x00000001, 0x00000028, 0x00000027, 0x00000028,
|
||||
0x0003003e, 0x00000024, 0x00000029, 0x00060041, 0x00000033, 0x00000034, 0x00000030, 0x00000032,
|
||||
0x00000032, 0x0004003d, 0x00000006, 0x00000035, 0x00000034, 0x0007000c, 0x00000006, 0x00000037,
|
||||
0x00000001, 0x00000028, 0x00000035, 0x00000036, 0x0003003e, 0x0000002a, 0x00000037, 0x00060041,
|
||||
0x00000033, 0x0000003a, 0x00000030, 0x00000032, 0x00000039, 0x0004003d, 0x00000006, 0x0000003b,
|
||||
0x0000003a, 0x0007000c, 0x00000006, 0x0000003d, 0x00000001, 0x00000028, 0x0000003b, 0x0000003c,
|
||||
0x0003003e, 0x00000038, 0x0000003d, 0x00060041, 0x00000033, 0x00000040, 0x00000030, 0x00000032,
|
||||
0x0000003f, 0x0004003d, 0x00000006, 0x00000041, 0x00000040, 0x0003003e, 0x0000003e, 0x00000041,
|
||||
0x00060041, 0x00000033, 0x00000044, 0x00000030, 0x00000032, 0x00000043, 0x0004003d, 0x00000006,
|
||||
0x00000045, 0x00000044, 0x0007000c, 0x00000006, 0x00000046, 0x00000001, 0x00000028, 0x00000045,
|
||||
0x00000036, 0x0003003e, 0x00000042, 0x00000046, 0x0004003d, 0x00000006, 0x00000048, 0x00000024,
|
||||
0x0004003d, 0x00000006, 0x00000049, 0x0000002a, 0x00050085, 0x00000006, 0x0000004a, 0x00000048,
|
||||
0x00000049, 0x0006000c, 0x00000006, 0x0000004b, 0x00000001, 0x00000008, 0x0000004a, 0x0004003d,
|
||||
0x00000006, 0x0000004c, 0x0000002a, 0x00050083, 0x00000006, 0x0000004e, 0x0000004c, 0x0000004d,
|
||||
0x0007000c, 0x00000006, 0x0000004f, 0x00000001, 0x00000028, 0x0000004e, 0x0000004d, 0x00050088,
|
||||
0x00000006, 0x00000050, 0x0000004b, 0x0000004f, 0x0003003e, 0x00000047, 0x00000050, 0x0003003e,
|
||||
0x00000051, 0x00000055, 0x0003003e, 0x00000056, 0x0000005a, 0x0003003e, 0x0000005b, 0x0000005f,
|
||||
0x0004003d, 0x00000018, 0x00000061, 0x00000051, 0x0004003d, 0x00000018, 0x00000062, 0x00000056,
|
||||
0x0004003d, 0x00000006, 0x00000063, 0x00000047, 0x00050085, 0x00000006, 0x00000065, 0x00000063,
|
||||
0x00000064, 0x0008000c, 0x00000006, 0x00000066, 0x00000001, 0x0000002b, 0x00000065, 0x00000028,
|
||||
0x0000004d, 0x00060050, 0x00000018, 0x00000067, 0x00000066, 0x00000066, 0x00000066, 0x0008000c,
|
||||
0x00000018, 0x00000068, 0x00000001, 0x0000002e, 0x00000061, 0x00000062, 0x00000067, 0x0003003e,
|
||||
0x00000060, 0x00000068, 0x0004003d, 0x00000018, 0x00000069, 0x00000060, 0x0004003d, 0x00000018,
|
||||
0x0000006a, 0x0000005b, 0x0004003d, 0x00000006, 0x0000006c, 0x00000047, 0x0008000c, 0x00000006,
|
||||
0x0000006d, 0x00000001, 0x00000031, 0x0000006b, 0x0000004d, 0x0000006c, 0x00060050, 0x00000018,
|
||||
0x0000006e, 0x0000006d, 0x0000006d, 0x0000006d, 0x0008000c, 0x00000018, 0x0000006f, 0x00000001,
|
||||
0x0000002e, 0x00000069, 0x0000006a, 0x0000006e, 0x0003003e, 0x00000060, 0x0000006f, 0x0004003d,
|
||||
0x00000007, 0x00000071, 0x00000009, 0x0008004f, 0x00000018, 0x00000072, 0x00000071, 0x00000071,
|
||||
0x00000000, 0x00000001, 0x00000002, 0x0003003e, 0x00000070, 0x00000072, 0x0004003d, 0x00000018,
|
||||
0x00000073, 0x00000070, 0x0004003d, 0x00000006, 0x00000074, 0x00000042, 0x0005008e, 0x00000018,
|
||||
0x00000075, 0x00000073, 0x00000074, 0x00060050, 0x00000018, 0x00000076, 0x0000003c, 0x0000003c,
|
||||
0x0000003c, 0x00050081, 0x00000018, 0x00000077, 0x00000075, 0x00000076, 0x0006000c, 0x00000018,
|
||||
0x00000078, 0x00000001, 0x00000008, 0x00000077, 0x0004003d, 0x00000006, 0x00000079, 0x00000042,
|
||||
0x00060050, 0x00000018, 0x0000007a, 0x00000079, 0x00000079, 0x00000079, 0x00050088, 0x00000018,
|
||||
0x0000007b, 0x00000078, 0x0000007a, 0x0003003e, 0x00000070, 0x0000007b, 0x0004003d, 0x00000018,
|
||||
0x0000007e, 0x0000007d, 0x0004003d, 0x00000018, 0x00000080, 0x0000007f, 0x00050083, 0x00000018,
|
||||
0x00000081, 0x0000007e, 0x00000080, 0x0006000c, 0x00000018, 0x00000082, 0x00000001, 0x00000045,
|
||||
0x00000081, 0x0003003e, 0x0000007c, 0x00000082, 0x0004003d, 0x00000018, 0x00000084, 0x0000001a,
|
||||
0x0004003d, 0x00000018, 0x00000085, 0x0000007c, 0x00050094, 0x00000006, 0x00000086, 0x00000084,
|
||||
0x00000085, 0x0007000c, 0x00000006, 0x00000087, 0x00000001, 0x00000028, 0x00000086, 0x00000028,
|
||||
0x00050083, 0x00000006, 0x00000088, 0x0000004d, 0x00000087, 0x0008000c, 0x00000006, 0x00000089,
|
||||
0x00000001, 0x0000002b, 0x00000088, 0x00000028, 0x0000004d, 0x0004003d, 0x00000006, 0x0000008a,
|
||||
0x00000038, 0x0007000c, 0x00000006, 0x0000008b, 0x00000001, 0x0000001a, 0x00000089, 0x0000008a,
|
||||
0x0003003e, 0x00000083, 0x0000008b, 0x0004003d, 0x00000006, 0x0000008e, 0x00000083, 0x0005008e,
|
||||
0x00000018, 0x0000008f, 0x0000008d, 0x0000008e, 0x0004003d, 0x00000006, 0x00000090, 0x0000003e,
|
||||
0x0005008e, 0x00000018, 0x00000091, 0x0000008f, 0x00000090, 0x0003003e, 0x0000008c, 0x00000091,
|
||||
0x0003003e, 0x00000092, 0x00000052, 0x0004003d, 0x00000018, 0x00000094, 0x00000070, 0x0004003d,
|
||||
0x00000006, 0x00000095, 0x00000092, 0x0004003d, 0x00000018, 0x00000096, 0x00000060, 0x0004003d,
|
||||
0x00000018, 0x00000098, 0x00000097, 0x00050085, 0x00000018, 0x00000099, 0x00000096, 0x00000098,
|
||||
0x00060050, 0x00000018, 0x0000009a, 0x00000095, 0x00000095, 0x00000095, 0x00050081, 0x00000018,
|
||||
0x0000009b, 0x0000009a, 0x00000099, 0x00050085, 0x00000018, 0x0000009c, 0x00000094, 0x0000009b,
|
||||
0x0004003d, 0x00000018, 0x0000009d, 0x0000008c, 0x0004003d, 0x00000018, 0x0000009e, 0x00000070,
|
||||
0x00050085, 0x00000018, 0x0000009f, 0x0000009d, 0x0000009e, 0x00050081, 0x00000018, 0x000000a0,
|
||||
0x0000009c, 0x0000009f, 0x0003003e, 0x00000093, 0x000000a0, 0x0004003d, 0x00000018, 0x000000a3,
|
||||
0x00000093, 0x00050041, 0x00000023, 0x000000a5, 0x00000009, 0x000000a4, 0x0004003d, 0x00000006,
|
||||
0x000000a6, 0x000000a5, 0x00050051, 0x00000006, 0x000000a7, 0x000000a3, 0x00000000, 0x00050051,
|
||||
0x00000006, 0x000000a8, 0x000000a3, 0x00000001, 0x00050051, 0x00000006, 0x000000a9, 0x000000a3,
|
||||
0x00000002, 0x00070050, 0x00000007, 0x000000aa, 0x000000a7, 0x000000a8, 0x000000a9, 0x000000a6,
|
||||
0x0003003e, 0x000000a2, 0x000000aa, 0x000100fd, 0x00010038,
|
||||
};
|
||||
static const size_t mesh3d_toon_frag_spv_count = sizeof(mesh3d_toon_frag_spv)/sizeof(mesh3d_toon_frag_spv[0]);
|
||||
@@ -0,0 +1,100 @@
|
||||
static const uint32_t mesh3d_toon_vert_spv[] = {
|
||||
0x07230203, 0x00010000, 0x000d000b, 0x0000005f, 0x00000000, 0x00020011, 0x00000001, 0x0006000b,
|
||||
0x00000001, 0x4c534c47, 0x6474732e, 0x3035342e, 0x00000000, 0x0003000e, 0x00000000, 0x00000001,
|
||||
0x0010000f, 0x00000000, 0x00000004, 0x6e69616d, 0x00000000, 0x0000000d, 0x00000019, 0x0000002f,
|
||||
0x0000003e, 0x00000040, 0x00000046, 0x00000048, 0x0000004a, 0x0000004f, 0x00000055, 0x0000005a,
|
||||
0x00030003, 0x00000002, 0x000001c2, 0x000a0004, 0x475f4c47, 0x4c474f4f, 0x70635f45, 0x74735f70,
|
||||
0x5f656c79, 0x656e696c, 0x7269645f, 0x69746365, 0x00006576, 0x00080004, 0x475f4c47, 0x4c474f4f,
|
||||
0x6e695f45, 0x64756c63, 0x69645f65, 0x74636572, 0x00657669, 0x00040005, 0x00000004, 0x6e69616d,
|
||||
0x00000000, 0x00060005, 0x0000000b, 0x505f6c67, 0x65567265, 0x78657472, 0x00000000, 0x00060006,
|
||||
0x0000000b, 0x00000000, 0x505f6c67, 0x7469736f, 0x006e6f69, 0x00070006, 0x0000000b, 0x00000001,
|
||||
0x505f6c67, 0x746e696f, 0x657a6953, 0x00000000, 0x00070006, 0x0000000b, 0x00000002, 0x435f6c67,
|
||||
0x4470696c, 0x61747369, 0x0065636e, 0x00070006, 0x0000000b, 0x00000003, 0x435f6c67, 0x446c6c75,
|
||||
0x61747369, 0x0065636e, 0x00030005, 0x0000000d, 0x00000000, 0x00040005, 0x00000011, 0x6d617246,
|
||||
0x00000065, 0x00040006, 0x00000011, 0x00000000, 0x0070766d, 0x00050006, 0x00000011, 0x00000001,
|
||||
0x65646f6d, 0x0000006c, 0x00080006, 0x00000011, 0x00000002, 0x6867696c, 0x72694474, 0x65746e49,
|
||||
0x7469736e, 0x00000079, 0x00060006, 0x00000011, 0x00000003, 0x6867696c, 0x6c6f4374, 0x0000726f,
|
||||
0x00050006, 0x00000011, 0x00000004, 0x746e6974, 0x00000000, 0x00060006, 0x00000011, 0x00000005,
|
||||
0x656d6163, 0x6f506172, 0x00000073, 0x00030005, 0x00000013, 0x006f6275, 0x00040005, 0x00000019,
|
||||
0x6f506e69, 0x00000073, 0x00040005, 0x00000024, 0x6c726f77, 0x00000064, 0x00050005, 0x0000002f,
|
||||
0x726f5776, 0x6f50646c, 0x00000073, 0x00050005, 0x00000034, 0x6d726f6e, 0x614d6c61, 0x00000074,
|
||||
0x00040005, 0x0000003e, 0x726f4e76, 0x006c616d, 0x00050005, 0x00000040, 0x6f4e6e69, 0x6c616d72,
|
||||
0x00000000, 0x00030005, 0x00000046, 0x00565576, 0x00040005, 0x00000048, 0x56556e69, 0x00000000,
|
||||
0x00040005, 0x0000004a, 0x6e695476, 0x00000074, 0x00050005, 0x0000004f, 0x67694c76, 0x69447468,
|
||||
0x00000072, 0x00050005, 0x00000055, 0x67694c76, 0x6f437468, 0x00726f6c, 0x00050005, 0x0000005a,
|
||||
0x6d614376, 0x50617265, 0x0000736f, 0x00030047, 0x0000000b, 0x00000002, 0x00050048, 0x0000000b,
|
||||
0x00000000, 0x0000000b, 0x00000000, 0x00050048, 0x0000000b, 0x00000001, 0x0000000b, 0x00000001,
|
||||
0x00050048, 0x0000000b, 0x00000002, 0x0000000b, 0x00000003, 0x00050048, 0x0000000b, 0x00000003,
|
||||
0x0000000b, 0x00000004, 0x00030047, 0x00000011, 0x00000002, 0x00040048, 0x00000011, 0x00000000,
|
||||
0x00000005, 0x00050048, 0x00000011, 0x00000000, 0x00000007, 0x00000010, 0x00050048, 0x00000011,
|
||||
0x00000000, 0x00000023, 0x00000000, 0x00040048, 0x00000011, 0x00000001, 0x00000005, 0x00050048,
|
||||
0x00000011, 0x00000001, 0x00000007, 0x00000010, 0x00050048, 0x00000011, 0x00000001, 0x00000023,
|
||||
0x00000040, 0x00050048, 0x00000011, 0x00000002, 0x00000023, 0x00000080, 0x00050048, 0x00000011,
|
||||
0x00000003, 0x00000023, 0x00000090, 0x00050048, 0x00000011, 0x00000004, 0x00000023, 0x000000a0,
|
||||
0x00050048, 0x00000011, 0x00000005, 0x00000023, 0x000000b0, 0x00040047, 0x00000013, 0x00000021,
|
||||
0x00000000, 0x00040047, 0x00000013, 0x00000022, 0x00000000, 0x00040047, 0x00000019, 0x0000001e,
|
||||
0x00000000, 0x00040047, 0x0000002f, 0x0000001e, 0x00000005, 0x00040047, 0x0000003e, 0x0000001e,
|
||||
0x00000000, 0x00040047, 0x00000040, 0x0000001e, 0x00000001, 0x00040047, 0x00000046, 0x0000001e,
|
||||
0x00000001, 0x00040047, 0x00000048, 0x0000001e, 0x00000002, 0x00040047, 0x0000004a, 0x0000001e,
|
||||
0x00000002, 0x00040047, 0x0000004f, 0x0000001e, 0x00000003, 0x00040047, 0x00000055, 0x0000001e,
|
||||
0x00000004, 0x00040047, 0x0000005a, 0x0000001e, 0x00000006, 0x00020013, 0x00000002, 0x00030021,
|
||||
0x00000003, 0x00000002, 0x00030016, 0x00000006, 0x00000020, 0x00040017, 0x00000007, 0x00000006,
|
||||
0x00000004, 0x00040015, 0x00000008, 0x00000020, 0x00000000, 0x0004002b, 0x00000008, 0x00000009,
|
||||
0x00000001, 0x0004001c, 0x0000000a, 0x00000006, 0x00000009, 0x0006001e, 0x0000000b, 0x00000007,
|
||||
0x00000006, 0x0000000a, 0x0000000a, 0x00040020, 0x0000000c, 0x00000003, 0x0000000b, 0x0004003b,
|
||||
0x0000000c, 0x0000000d, 0x00000003, 0x00040015, 0x0000000e, 0x00000020, 0x00000001, 0x0004002b,
|
||||
0x0000000e, 0x0000000f, 0x00000000, 0x00040018, 0x00000010, 0x00000007, 0x00000004, 0x0008001e,
|
||||
0x00000011, 0x00000010, 0x00000010, 0x00000007, 0x00000007, 0x00000007, 0x00000007, 0x00040020,
|
||||
0x00000012, 0x00000002, 0x00000011, 0x0004003b, 0x00000012, 0x00000013, 0x00000002, 0x00040020,
|
||||
0x00000014, 0x00000002, 0x00000010, 0x00040017, 0x00000017, 0x00000006, 0x00000003, 0x00040020,
|
||||
0x00000018, 0x00000001, 0x00000017, 0x0004003b, 0x00000018, 0x00000019, 0x00000001, 0x0004002b,
|
||||
0x00000006, 0x0000001b, 0x3f800000, 0x00040020, 0x00000021, 0x00000003, 0x00000007, 0x00040020,
|
||||
0x00000023, 0x00000007, 0x00000007, 0x0004002b, 0x0000000e, 0x00000025, 0x00000001, 0x00040020,
|
||||
0x0000002e, 0x00000003, 0x00000017, 0x0004003b, 0x0000002e, 0x0000002f, 0x00000003, 0x00040018,
|
||||
0x00000032, 0x00000017, 0x00000003, 0x00040020, 0x00000033, 0x00000007, 0x00000032, 0x0004003b,
|
||||
0x0000002e, 0x0000003e, 0x00000003, 0x0004003b, 0x00000018, 0x00000040, 0x00000001, 0x00040017,
|
||||
0x00000044, 0x00000006, 0x00000002, 0x00040020, 0x00000045, 0x00000003, 0x00000044, 0x0004003b,
|
||||
0x00000045, 0x00000046, 0x00000003, 0x00040020, 0x00000047, 0x00000001, 0x00000044, 0x0004003b,
|
||||
0x00000047, 0x00000048, 0x00000001, 0x0004003b, 0x00000021, 0x0000004a, 0x00000003, 0x0004002b,
|
||||
0x0000000e, 0x0000004b, 0x00000004, 0x00040020, 0x0000004c, 0x00000002, 0x00000007, 0x0004003b,
|
||||
0x0000002e, 0x0000004f, 0x00000003, 0x0004002b, 0x0000000e, 0x00000050, 0x00000002, 0x0004003b,
|
||||
0x0000002e, 0x00000055, 0x00000003, 0x0004002b, 0x0000000e, 0x00000056, 0x00000003, 0x0004003b,
|
||||
0x0000002e, 0x0000005a, 0x00000003, 0x0004002b, 0x0000000e, 0x0000005b, 0x00000005, 0x00050036,
|
||||
0x00000002, 0x00000004, 0x00000000, 0x00000003, 0x000200f8, 0x00000005, 0x0004003b, 0x00000023,
|
||||
0x00000024, 0x00000007, 0x0004003b, 0x00000033, 0x00000034, 0x00000007, 0x00050041, 0x00000014,
|
||||
0x00000015, 0x00000013, 0x0000000f, 0x0004003d, 0x00000010, 0x00000016, 0x00000015, 0x0004003d,
|
||||
0x00000017, 0x0000001a, 0x00000019, 0x00050051, 0x00000006, 0x0000001c, 0x0000001a, 0x00000000,
|
||||
0x00050051, 0x00000006, 0x0000001d, 0x0000001a, 0x00000001, 0x00050051, 0x00000006, 0x0000001e,
|
||||
0x0000001a, 0x00000002, 0x00070050, 0x00000007, 0x0000001f, 0x0000001c, 0x0000001d, 0x0000001e,
|
||||
0x0000001b, 0x00050091, 0x00000007, 0x00000020, 0x00000016, 0x0000001f, 0x00050041, 0x00000021,
|
||||
0x00000022, 0x0000000d, 0x0000000f, 0x0003003e, 0x00000022, 0x00000020, 0x00050041, 0x00000014,
|
||||
0x00000026, 0x00000013, 0x00000025, 0x0004003d, 0x00000010, 0x00000027, 0x00000026, 0x0004003d,
|
||||
0x00000017, 0x00000028, 0x00000019, 0x00050051, 0x00000006, 0x00000029, 0x00000028, 0x00000000,
|
||||
0x00050051, 0x00000006, 0x0000002a, 0x00000028, 0x00000001, 0x00050051, 0x00000006, 0x0000002b,
|
||||
0x00000028, 0x00000002, 0x00070050, 0x00000007, 0x0000002c, 0x00000029, 0x0000002a, 0x0000002b,
|
||||
0x0000001b, 0x00050091, 0x00000007, 0x0000002d, 0x00000027, 0x0000002c, 0x0003003e, 0x00000024,
|
||||
0x0000002d, 0x0004003d, 0x00000007, 0x00000030, 0x00000024, 0x0008004f, 0x00000017, 0x00000031,
|
||||
0x00000030, 0x00000030, 0x00000000, 0x00000001, 0x00000002, 0x0003003e, 0x0000002f, 0x00000031,
|
||||
0x00050041, 0x00000014, 0x00000035, 0x00000013, 0x00000025, 0x0004003d, 0x00000010, 0x00000036,
|
||||
0x00000035, 0x00050051, 0x00000007, 0x00000037, 0x00000036, 0x00000000, 0x0008004f, 0x00000017,
|
||||
0x00000038, 0x00000037, 0x00000037, 0x00000000, 0x00000001, 0x00000002, 0x00050051, 0x00000007,
|
||||
0x00000039, 0x00000036, 0x00000001, 0x0008004f, 0x00000017, 0x0000003a, 0x00000039, 0x00000039,
|
||||
0x00000000, 0x00000001, 0x00000002, 0x00050051, 0x00000007, 0x0000003b, 0x00000036, 0x00000002,
|
||||
0x0008004f, 0x00000017, 0x0000003c, 0x0000003b, 0x0000003b, 0x00000000, 0x00000001, 0x00000002,
|
||||
0x00060050, 0x00000032, 0x0000003d, 0x00000038, 0x0000003a, 0x0000003c, 0x0003003e, 0x00000034,
|
||||
0x0000003d, 0x0004003d, 0x00000032, 0x0000003f, 0x00000034, 0x0004003d, 0x00000017, 0x00000041,
|
||||
0x00000040, 0x00050091, 0x00000017, 0x00000042, 0x0000003f, 0x00000041, 0x0006000c, 0x00000017,
|
||||
0x00000043, 0x00000001, 0x00000045, 0x00000042, 0x0003003e, 0x0000003e, 0x00000043, 0x0004003d,
|
||||
0x00000044, 0x00000049, 0x00000048, 0x0003003e, 0x00000046, 0x00000049, 0x00050041, 0x0000004c,
|
||||
0x0000004d, 0x00000013, 0x0000004b, 0x0004003d, 0x00000007, 0x0000004e, 0x0000004d, 0x0003003e,
|
||||
0x0000004a, 0x0000004e, 0x00050041, 0x0000004c, 0x00000051, 0x00000013, 0x00000050, 0x0004003d,
|
||||
0x00000007, 0x00000052, 0x00000051, 0x0008004f, 0x00000017, 0x00000053, 0x00000052, 0x00000052,
|
||||
0x00000000, 0x00000001, 0x00000002, 0x0006000c, 0x00000017, 0x00000054, 0x00000001, 0x00000045,
|
||||
0x00000053, 0x0003003e, 0x0000004f, 0x00000054, 0x00050041, 0x0000004c, 0x00000057, 0x00000013,
|
||||
0x00000056, 0x0004003d, 0x00000007, 0x00000058, 0x00000057, 0x0008004f, 0x00000017, 0x00000059,
|
||||
0x00000058, 0x00000058, 0x00000000, 0x00000001, 0x00000002, 0x0003003e, 0x00000055, 0x00000059,
|
||||
0x00050041, 0x0000004c, 0x0000005c, 0x00000013, 0x0000005b, 0x0004003d, 0x00000007, 0x0000005d,
|
||||
0x0000005c, 0x0008004f, 0x00000017, 0x0000005e, 0x0000005d, 0x0000005d, 0x00000000, 0x00000001,
|
||||
0x00000002, 0x0003003e, 0x0000005a, 0x0000005e, 0x000100fd, 0x00010038,
|
||||
};
|
||||
static const size_t mesh3d_toon_vert_spv_count = sizeof(mesh3d_toon_vert_spv)/sizeof(mesh3d_toon_vert_spv[0]);
|
||||
@@ -8,9 +8,13 @@
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
#if !defined(_WIN32)
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "common/Exception.h"
|
||||
#include "common/config.h"
|
||||
@@ -44,13 +48,23 @@ Graphics::~Graphics() {
|
||||
if (g->sampler) device->destroySampler(g->sampler);
|
||||
}
|
||||
ownedGpuTextures.clear();
|
||||
for (auto &g : ownedGpuShaders) {
|
||||
if (g->swapchainPipeline) device->destroyPipeline(g->swapchainPipeline);
|
||||
if (g->offscreenPipeline) device->destroyPipeline(g->offscreenPipeline);
|
||||
if (g->mesh3dPipeline) device->destroyPipeline(g->mesh3dPipeline);
|
||||
// pipelineLayout is shared; do not destroy per-shader
|
||||
}
|
||||
ownedGpuShaders.clear();
|
||||
ownedShaders.clear();
|
||||
destroySwapchainResources();
|
||||
if (pipeline) device->destroyPipeline(pipeline);
|
||||
if (pipelineLayout) device->destroyPipelineLayout(pipelineLayout);
|
||||
if (texPipeline) device->destroyPipeline(texPipeline);
|
||||
if (texPipelineLayout) device->destroyPipelineLayout(texPipelineLayout);
|
||||
if (shaderPipelineLayout) device->destroyPipelineLayout(shaderPipelineLayout);
|
||||
if (mesh3dPipeline) device->destroyPipeline(mesh3dPipeline);
|
||||
if (mesh3dPipelineLayout) device->destroyPipelineLayout(mesh3dPipelineLayout);
|
||||
if (mesh3dShaderPipelineLayout) device->destroyPipelineLayout(mesh3dShaderPipelineLayout);
|
||||
mesh3dSetLayoutUnique.reset();
|
||||
mesh3dUboSlots.clear();
|
||||
if (offscreenSolidPipeline) device->destroyPipeline(offscreenSolidPipeline);
|
||||
@@ -342,8 +356,25 @@ void Graphics::createTexturedPipeline() {
|
||||
plInfo.pSetLayouts = &texSetLayout;
|
||||
texPipelineLayout = device->createPipelineLayout(plInfo);
|
||||
|
||||
vk::PushConstantRange pcr{};
|
||||
pcr.stageFlags = vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment;
|
||||
pcr.offset = 0;
|
||||
pcr.size = Shader::kPushConstantBytes;
|
||||
vk::PipelineLayoutCreateInfo shaderPlInfo{};
|
||||
shaderPlInfo.setLayoutCount = 1;
|
||||
shaderPlInfo.pSetLayouts = &texSetLayout;
|
||||
shaderPlInfo.pushConstantRangeCount = 1;
|
||||
shaderPlInfo.pPushConstantRanges = &pcr;
|
||||
shaderPipelineLayout = device->createPipelineLayout(shaderPlInfo);
|
||||
|
||||
std::vector<uint32_t> vert(textured_vert_spv, textured_vert_spv + textured_vert_spv_count);
|
||||
std::vector<uint32_t> frag(textured_frag_spv, textured_frag_spv + textured_frag_spv_count);
|
||||
texPipeline = createTexturedStylePipeline(vert, frag, renderpass, texPipelineLayout);
|
||||
}
|
||||
|
||||
vk::Pipeline Graphics::createTexturedStylePipeline(const std::vector<uint32_t> &vert,
|
||||
const std::vector<uint32_t> &frag,
|
||||
vk::RenderPass rp, vk::PipelineLayout layout) {
|
||||
vk::ShaderModule vertModule =
|
||||
vkb::PipelineBuilder::createShaderModule(device.instance, vert);
|
||||
vk::ShaderModule fragModule =
|
||||
@@ -416,17 +447,16 @@ void Graphics::createTexturedPipeline() {
|
||||
pci.pDepthStencilState = &ds;
|
||||
pci.pColorBlendState = &blend;
|
||||
pci.pDynamicState = &dyn;
|
||||
pci.layout = texPipelineLayout;
|
||||
pci.renderPass = renderpass;
|
||||
pci.layout = layout;
|
||||
pci.renderPass = rp;
|
||||
pci.subpass = 0;
|
||||
|
||||
auto result = device->createGraphicsPipeline(nullptr, pci);
|
||||
if (result.result != vk::Result::eSuccess)
|
||||
throw Exception("failed to create textured pipeline");
|
||||
texPipeline = result.value;
|
||||
|
||||
device->destroyShaderModule(vertModule);
|
||||
device->destroyShaderModule(fragModule);
|
||||
if (result.result != vk::Result::eSuccess)
|
||||
throw Exception("failed to create textured-style pipeline");
|
||||
return result.value;
|
||||
}
|
||||
|
||||
void Graphics::createMesh3DPipeline() {
|
||||
@@ -447,12 +477,29 @@ void Graphics::createMesh3DPipeline() {
|
||||
plInfo.pSetLayouts = &mesh3dSetLayout;
|
||||
mesh3dPipelineLayout = device->createPipelineLayout(plInfo);
|
||||
|
||||
vk::PushConstantRange pcr{};
|
||||
pcr.stageFlags = vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment;
|
||||
pcr.offset = 0;
|
||||
pcr.size = Shader::kPushConstantBytes;
|
||||
vk::PipelineLayoutCreateInfo shaderPl{};
|
||||
shaderPl.setLayoutCount = 1;
|
||||
shaderPl.pSetLayouts = &mesh3dSetLayout;
|
||||
shaderPl.pushConstantRangeCount = 1;
|
||||
shaderPl.pPushConstantRanges = &pcr;
|
||||
mesh3dShaderPipelineLayout = device->createPipelineLayout(shaderPl);
|
||||
|
||||
// UBO + descriptor sets are allocated lazily per draw (see mesh3dSetFor).
|
||||
mesh3dUboSlots.clear();
|
||||
mesh3dDrawIndex = 0;
|
||||
|
||||
std::vector<uint32_t> vert(mesh3d_vert_spv, mesh3d_vert_spv + mesh3d_vert_spv_count);
|
||||
std::vector<uint32_t> frag(mesh3d_frag_spv, mesh3d_frag_spv + mesh3d_frag_spv_count);
|
||||
mesh3dPipeline = createMesh3DStylePipeline(vert, frag, mesh3dPipelineLayout);
|
||||
}
|
||||
|
||||
vk::Pipeline Graphics::createMesh3DStylePipeline(const std::vector<uint32_t> &vert,
|
||||
const std::vector<uint32_t> &frag,
|
||||
vk::PipelineLayout layout) {
|
||||
vk::ShaderModule vertModule = vkb::PipelineBuilder::createShaderModule(device.instance, vert);
|
||||
vk::ShaderModule fragModule = vkb::PipelineBuilder::createShaderModule(device.instance, frag);
|
||||
|
||||
@@ -518,17 +565,16 @@ void Graphics::createMesh3DPipeline() {
|
||||
pci.pDepthStencilState = &ds;
|
||||
pci.pColorBlendState = &blend;
|
||||
pci.pDynamicState = &dyn;
|
||||
pci.layout = mesh3dPipelineLayout;
|
||||
pci.layout = layout;
|
||||
pci.renderPass = renderpass;
|
||||
pci.subpass = 0;
|
||||
|
||||
auto result = device->createGraphicsPipeline(nullptr, pci);
|
||||
if (result.result != vk::Result::eSuccess)
|
||||
throw Exception("failed to create mesh3d pipeline");
|
||||
mesh3dPipeline = result.value;
|
||||
|
||||
device->destroyShaderModule(vertModule);
|
||||
device->destroyShaderModule(fragModule);
|
||||
if (result.result != vk::Result::eSuccess)
|
||||
throw Exception("failed to create mesh3d-style pipeline");
|
||||
return result.value;
|
||||
}
|
||||
|
||||
void Graphics::ensureOffscreenPipelines() {
|
||||
@@ -566,74 +612,19 @@ void Graphics::ensureOffscreenPipelines() {
|
||||
// Textured offscreen pipeline mirrors createTexturedPipeline but with offscreen RP.
|
||||
std::vector<uint32_t> tvert(textured_vert_spv, textured_vert_spv + textured_vert_spv_count);
|
||||
std::vector<uint32_t> tfrag(textured_frag_spv, textured_frag_spv + textured_frag_spv_count);
|
||||
vk::ShaderModule vertModule = vkb::PipelineBuilder::createShaderModule(device.instance, tvert);
|
||||
vk::ShaderModule fragModule = vkb::PipelineBuilder::createShaderModule(device.instance, tfrag);
|
||||
offscreenTexPipeline =
|
||||
createTexturedStylePipeline(tvert, tfrag, offscreenRenderPass, texPipelineLayout);
|
||||
|
||||
vk::PipelineShaderStageCreateInfo stages[2]{};
|
||||
stages[0].stage = vk::ShaderStageFlagBits::eVertex;
|
||||
stages[0].module = vertModule;
|
||||
stages[0].pName = "main";
|
||||
stages[1].stage = vk::ShaderStageFlagBits::eFragment;
|
||||
stages[1].module = fragModule;
|
||||
stages[1].pName = "main";
|
||||
// Lazily create offscreen pipelines for any custom shaders already loaded.
|
||||
for (auto &sh : ownedShaders) ensureShaderOffscreenPipeline(sh.get());
|
||||
}
|
||||
|
||||
auto binding = TexturedVertex::getBindingDescription(0);
|
||||
auto attrs = TexturedVertex::getAttributeDescription(0);
|
||||
vk::PipelineVertexInputStateCreateInfo vi{};
|
||||
vi.vertexBindingDescriptionCount = 1;
|
||||
vi.pVertexBindingDescriptions = &binding;
|
||||
vi.vertexAttributeDescriptionCount = uint32_t(attrs.size());
|
||||
vi.pVertexAttributeDescriptions = attrs.data();
|
||||
|
||||
vk::PipelineInputAssemblyStateCreateInfo ia{};
|
||||
ia.topology = vk::PrimitiveTopology::eTriangleList;
|
||||
vk::PipelineViewportStateCreateInfo vp{};
|
||||
vp.viewportCount = 1;
|
||||
vp.scissorCount = 1;
|
||||
vk::PipelineRasterizationStateCreateInfo rs{};
|
||||
rs.polygonMode = vk::PolygonMode::eFill;
|
||||
rs.cullMode = vk::CullModeFlagBits::eNone;
|
||||
rs.frontFace = vk::FrontFace::eCounterClockwise;
|
||||
rs.lineWidth = 1.0f;
|
||||
vk::PipelineMultisampleStateCreateInfo ms{};
|
||||
ms.rasterizationSamples = vk::SampleCountFlagBits::e1;
|
||||
vk::PipelineColorBlendAttachmentState blendAtt{};
|
||||
blendAtt.colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG |
|
||||
vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA;
|
||||
blendAtt.blendEnable = true;
|
||||
blendAtt.srcColorBlendFactor = vk::BlendFactor::eSrcAlpha;
|
||||
blendAtt.dstColorBlendFactor = vk::BlendFactor::eOneMinusSrcAlpha;
|
||||
blendAtt.colorBlendOp = vk::BlendOp::eAdd;
|
||||
blendAtt.srcAlphaBlendFactor = vk::BlendFactor::eOne;
|
||||
blendAtt.dstAlphaBlendFactor = vk::BlendFactor::eOneMinusSrcAlpha;
|
||||
blendAtt.alphaBlendOp = vk::BlendOp::eAdd;
|
||||
vk::PipelineColorBlendStateCreateInfo blend{};
|
||||
blend.attachmentCount = 1;
|
||||
blend.pAttachments = &blendAtt;
|
||||
vk::DynamicState dynStates[] = {vk::DynamicState::eViewport, vk::DynamicState::eScissor};
|
||||
vk::PipelineDynamicStateCreateInfo dyn{};
|
||||
dyn.dynamicStateCount = 2;
|
||||
dyn.pDynamicStates = dynStates;
|
||||
|
||||
vk::GraphicsPipelineCreateInfo pci{};
|
||||
pci.stageCount = 2;
|
||||
pci.pStages = stages;
|
||||
pci.pVertexInputState = &vi;
|
||||
pci.pInputAssemblyState = &ia;
|
||||
pci.pViewportState = &vp;
|
||||
pci.pRasterizationState = &rs;
|
||||
pci.pMultisampleState = &ms;
|
||||
pci.pColorBlendState = &blend;
|
||||
pci.pDynamicState = &dyn;
|
||||
pci.layout = texPipelineLayout;
|
||||
pci.renderPass = offscreenRenderPass;
|
||||
pci.subpass = 0;
|
||||
auto result = device->createGraphicsPipeline(nullptr, pci);
|
||||
if (result.result != vk::Result::eSuccess)
|
||||
throw Exception("failed to create offscreen textured pipeline");
|
||||
offscreenTexPipeline = result.value;
|
||||
device->destroyShaderModule(vertModule);
|
||||
device->destroyShaderModule(fragModule);
|
||||
void Graphics::ensureShaderOffscreenPipeline(Shader *shader) {
|
||||
if (!shader || !shader->gpuHandle || !offscreenRenderPass) return;
|
||||
auto *gpu = static_cast<GpuShader *>(shader->gpuHandle);
|
||||
if (gpu->offscreenPipeline) return;
|
||||
gpu->offscreenPipeline = createTexturedStylePipeline(shader->vertexSpirv(), shader->fragmentSpirv(),
|
||||
offscreenRenderPass, shaderPipelineLayout);
|
||||
}
|
||||
|
||||
Texture *Graphics::getTexture() { return nullptr; }
|
||||
@@ -884,16 +875,187 @@ Texture *Graphics::newTextureFromFile(const std::string &filename) {
|
||||
}
|
||||
|
||||
void Graphics::drawTexturedRect(Texture *texture, float x, float y, float w, float h, const Color &color) {
|
||||
drawTexturedRectShader(texture, currentShader, x, y, w, h, color);
|
||||
}
|
||||
|
||||
void Graphics::drawTexturedRectShader(Texture *texture, Shader *shader, float x, float y, float w,
|
||||
float h, const Color &color) {
|
||||
if (!texture) {
|
||||
drawSolidRect(x, y, w, h, color);
|
||||
return;
|
||||
}
|
||||
if (texturedBatches.empty() || texturedBatches.back().texture != texture) {
|
||||
texturedBatches.push_back(TexturedBatch{texture, Batcher{}});
|
||||
if (texturedBatches.empty() || texturedBatches.back().texture != texture ||
|
||||
texturedBatches.back().shader != shader) {
|
||||
texturedBatches.push_back(TexturedBatch{texture, shader, Batcher{}});
|
||||
}
|
||||
texturedBatches.back().batch.addTexturedRect(x, y, w, h, color, 0, 0, 1, 1);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
std::vector<uint32_t> loadSpirvBytes(const void *data, size_t size) {
|
||||
if (!data || size < 4 || (size % 4) != 0)
|
||||
throw Exception("SPIR-V: invalid size %zu", size);
|
||||
const auto *words = static_cast<const uint32_t *>(data);
|
||||
if (words[0] != 0x07230203)
|
||||
throw Exception("SPIR-V: bad magic (expected 0x07230203)");
|
||||
return std::vector<uint32_t>(words, words + size / 4);
|
||||
}
|
||||
|
||||
std::vector<uint32_t> readSpirvFile(const std::string &path) {
|
||||
auto *fs = filesystem::Filesystem::create();
|
||||
std::unique_ptr<filesystem::FileData> fd(fs->read(path));
|
||||
if (!fd) throw Exception("newShaderFromSpvFile: failed to read '%s'", path.c_str());
|
||||
return loadSpirvBytes(fd->getData(), fd->getSize());
|
||||
}
|
||||
|
||||
std::vector<uint32_t> compileGlslWithGlslc(const std::string &source, const char *stage) {
|
||||
if (source.empty()) throw Exception("newShader: empty %s GLSL", stage);
|
||||
#if defined(_WIN32)
|
||||
(void)source;
|
||||
(void)stage;
|
||||
throw Exception("newShader: GLSL compile via glslc is not supported on Windows; "
|
||||
"use newShaderFromSpv / newShaderFromSpvFile");
|
||||
#else
|
||||
char inPath[] = "/tmp/eve_shader_XXXXXX";
|
||||
int fd = mkstemp(inPath);
|
||||
if (fd < 0) throw Exception("newShader: mkstemp failed");
|
||||
std::string outPath = std::string(inPath) + ".spv";
|
||||
{
|
||||
ssize_t n = write(fd, source.data(), source.size());
|
||||
close(fd);
|
||||
if (n < 0 || size_t(n) != source.size()) {
|
||||
unlink(inPath);
|
||||
throw Exception("newShader: failed to write temp GLSL");
|
||||
}
|
||||
}
|
||||
|
||||
std::string cmd = std::string("glslc -fshader-stage=") + stage + " \"" + inPath + "\" -o \"" +
|
||||
outPath + "\" 2>&1";
|
||||
FILE *pipe = popen(cmd.c_str(), "r");
|
||||
std::string err;
|
||||
if (pipe) {
|
||||
char buf[256];
|
||||
while (fgets(buf, sizeof(buf), pipe)) err += buf;
|
||||
int status = pclose(pipe);
|
||||
unlink(inPath);
|
||||
if (status != 0) {
|
||||
unlink(outPath.c_str());
|
||||
throw Exception("newShader: glslc failed for %s:\n%s", stage, err.c_str());
|
||||
}
|
||||
} else {
|
||||
unlink(inPath);
|
||||
throw Exception("newShader: glslc not available (popen failed)");
|
||||
}
|
||||
|
||||
FILE *f = fopen(outPath.c_str(), "rb");
|
||||
if (!f) {
|
||||
unlink(outPath.c_str());
|
||||
throw Exception("newShader: failed to open compiled SPIR-V");
|
||||
}
|
||||
fseek(f, 0, SEEK_END);
|
||||
long sz = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
std::vector<uint8_t> bytes(static_cast<size_t>(sz > 0 ? sz : 0));
|
||||
if (sz > 0 && fread(bytes.data(), 1, static_cast<size_t>(sz), f) != static_cast<size_t>(sz)) {
|
||||
fclose(f);
|
||||
unlink(outPath.c_str());
|
||||
throw Exception("newShader: failed to read compiled SPIR-V");
|
||||
}
|
||||
fclose(f);
|
||||
unlink(outPath.c_str());
|
||||
return loadSpirvBytes(bytes.data(), bytes.size());
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Shader *Graphics::newShaderFromSpv(const std::vector<uint32_t> &vertSpv,
|
||||
const std::vector<uint32_t> &fragSpv) {
|
||||
ASSERT(initialized);
|
||||
if (!initialized) throw Exception("newShaderFromSpv: graphics not initialized");
|
||||
if (fragSpv.empty()) throw Exception("newShaderFromSpv: empty fragment SPIR-V");
|
||||
|
||||
std::vector<uint32_t> vert = vertSpv;
|
||||
if (vert.empty())
|
||||
vert.assign(textured_vert_spv, textured_vert_spv + textured_vert_spv_count);
|
||||
if (vert[0] != 0x07230203 || fragSpv[0] != 0x07230203)
|
||||
throw Exception("newShaderFromSpv: SPIR-V magic mismatch");
|
||||
|
||||
auto gpu = std::make_unique<GpuShader>();
|
||||
gpu->pipelineLayout = shaderPipelineLayout;
|
||||
gpu->swapchainPipeline =
|
||||
createTexturedStylePipeline(vert, fragSpv, renderpass, shaderPipelineLayout);
|
||||
if (offscreenRenderPass) {
|
||||
gpu->offscreenPipeline =
|
||||
createTexturedStylePipeline(vert, fragSpv, offscreenRenderPass, shaderPipelineLayout);
|
||||
}
|
||||
|
||||
auto sh = std::make_unique<Shader>();
|
||||
sh->setSpirv(std::move(vert), fragSpv);
|
||||
sh->gpuHandle = gpu.get();
|
||||
|
||||
Shader *raw = sh.get();
|
||||
ownedShaders.push_back(std::move(sh));
|
||||
ownedGpuShaders.push_back(std::move(gpu));
|
||||
return raw;
|
||||
}
|
||||
|
||||
Shader *Graphics::newShaderFromSpvFile(const std::string &vertPath, const std::string &fragPath) {
|
||||
if (fragPath.empty()) throw Exception("newShaderFromSpvFile: empty fragPath");
|
||||
std::vector<uint32_t> vert;
|
||||
if (!vertPath.empty()) vert = readSpirvFile(vertPath);
|
||||
auto frag = readSpirvFile(fragPath);
|
||||
return newShaderFromSpv(vert, frag);
|
||||
}
|
||||
|
||||
Shader *Graphics::newShader(const std::string &vertGlsl, const std::string &fragGlsl) {
|
||||
if (fragGlsl.empty()) throw Exception("newShader: empty fragment GLSL");
|
||||
std::vector<uint32_t> vert;
|
||||
if (!vertGlsl.empty()) vert = compileGlslWithGlslc(vertGlsl, "vert");
|
||||
auto frag = compileGlslWithGlslc(fragGlsl, "frag");
|
||||
return newShaderFromSpv(vert, frag);
|
||||
}
|
||||
|
||||
Shader *Graphics::newMeshShaderFromSpv(const std::vector<uint32_t> &vertSpv,
|
||||
const std::vector<uint32_t> &fragSpv) {
|
||||
ASSERT(initialized);
|
||||
if (!initialized) throw Exception("newMeshShaderFromSpv: graphics not initialized");
|
||||
if (fragSpv.empty()) throw Exception("newMeshShaderFromSpv: empty fragment SPIR-V");
|
||||
if (!mesh3dShaderPipelineLayout)
|
||||
throw Exception("newMeshShaderFromSpv: mesh3d pipeline layout missing");
|
||||
|
||||
std::vector<uint32_t> vert = vertSpv;
|
||||
if (vert.empty())
|
||||
vert.assign(mesh3d_vert_spv, mesh3d_vert_spv + mesh3d_vert_spv_count);
|
||||
if (vert[0] != 0x07230203 || fragSpv[0] != 0x07230203)
|
||||
throw Exception("newMeshShaderFromSpv: SPIR-V magic mismatch");
|
||||
|
||||
auto gpu = std::make_unique<GpuShader>();
|
||||
gpu->isMesh3D = true;
|
||||
gpu->pipelineLayout = mesh3dShaderPipelineLayout;
|
||||
gpu->mesh3dPipeline =
|
||||
createMesh3DStylePipeline(vert, fragSpv, mesh3dShaderPipelineLayout);
|
||||
|
||||
auto sh = std::make_unique<Shader>();
|
||||
sh->setKind(Shader::Kind::eMesh3D);
|
||||
sh->setSpirv(std::move(vert), fragSpv);
|
||||
sh->gpuHandle = gpu.get();
|
||||
|
||||
Shader *raw = sh.get();
|
||||
ownedShaders.push_back(std::move(sh));
|
||||
ownedGpuShaders.push_back(std::move(gpu));
|
||||
return raw;
|
||||
}
|
||||
|
||||
Shader *Graphics::newMeshShader(const std::string &vertGlsl, const std::string &fragGlsl) {
|
||||
if (fragGlsl.empty()) throw Exception("newMeshShader: empty fragment GLSL");
|
||||
std::vector<uint32_t> vert;
|
||||
if (!vertGlsl.empty()) vert = compileGlslWithGlslc(vertGlsl, "vert");
|
||||
auto frag = compileGlslWithGlslc(fragGlsl, "frag");
|
||||
return newMeshShaderFromSpv(vert, frag);
|
||||
}
|
||||
|
||||
void Graphics::flushBatch() {
|
||||
if (!initialized) return;
|
||||
if (isCanvasActive()) {
|
||||
@@ -967,9 +1129,28 @@ void Graphics::flushToOffscreen(OffscreenCanvas *canvas) {
|
||||
gpuVerts.push_back(TexturedVertex{v.pos, v.color, v.uv});
|
||||
texBufs.emplace_back();
|
||||
texBufs.back().allocate<TexturedVertex>(device, gpuVerts);
|
||||
cb.bindPipeline(vk::PipelineBindPoint::eGraphics, offscreenTexPipeline);
|
||||
cb.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, texPipelineLayout, 0, 1,
|
||||
&gpu->descriptorSet, 0, nullptr);
|
||||
|
||||
if (tb.shader && tb.shader->gpuHandle) {
|
||||
ensureShaderOffscreenPipeline(tb.shader);
|
||||
auto *gs = static_cast<GpuShader *>(tb.shader->gpuHandle);
|
||||
if (!gs->offscreenPipeline) continue;
|
||||
cb.bindPipeline(vk::PipelineBindPoint::eGraphics,
|
||||
gs->offscreenPipeline);
|
||||
cb.bindDescriptorSets(vk::PipelineBindPoint::eGraphics,
|
||||
shaderPipelineLayout, 0, 1,
|
||||
&gpu->descriptorSet, 0, nullptr);
|
||||
cb.pushConstants(shaderPipelineLayout,
|
||||
vk::ShaderStageFlagBits::eVertex |
|
||||
vk::ShaderStageFlagBits::eFragment,
|
||||
0, Shader::kPushConstantBytes,
|
||||
tb.shader->pushConstantData());
|
||||
} else {
|
||||
cb.bindPipeline(vk::PipelineBindPoint::eGraphics,
|
||||
offscreenTexPipeline);
|
||||
cb.bindDescriptorSets(vk::PipelineBindPoint::eGraphics,
|
||||
texPipelineLayout, 0, 1,
|
||||
&gpu->descriptorSet, 0, nullptr);
|
||||
}
|
||||
vk::DeviceSize offset = 0;
|
||||
cb.bindVertexBuffers(0, 1, texBufs.back(), &offset);
|
||||
cb.draw(uint32_t(gpuVerts.size()), 1, 0, 0);
|
||||
@@ -1039,9 +1220,20 @@ void Graphics::flushToSwapchain() {
|
||||
|
||||
texBufs.emplace_back();
|
||||
texBufs.back().allocate<TexturedVertex>(device, gpuVerts);
|
||||
cb.bindPipeline(vk::PipelineBindPoint::eGraphics, texPipeline);
|
||||
cb.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, texPipelineLayout, 0, 1,
|
||||
&gpu->descriptorSet, 0, nullptr);
|
||||
|
||||
if (tb.shader && tb.shader->gpuHandle) {
|
||||
auto *gs = static_cast<GpuShader *>(tb.shader->gpuHandle);
|
||||
cb.bindPipeline(vk::PipelineBindPoint::eGraphics, gs->swapchainPipeline);
|
||||
cb.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, shaderPipelineLayout, 0, 1,
|
||||
&gpu->descriptorSet, 0, nullptr);
|
||||
cb.pushConstants(shaderPipelineLayout,
|
||||
vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment, 0,
|
||||
Shader::kPushConstantBytes, tb.shader->pushConstantData());
|
||||
} else {
|
||||
cb.bindPipeline(vk::PipelineBindPoint::eGraphics, texPipeline);
|
||||
cb.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, texPipelineLayout, 0, 1,
|
||||
&gpu->descriptorSet, 0, nullptr);
|
||||
}
|
||||
vk::DeviceSize offset = 0;
|
||||
cb.bindVertexBuffers(0, 1, texBufs.back(), &offset);
|
||||
cb.draw(uint32_t(gpuVerts.size()), 1, 0, 0);
|
||||
@@ -1100,6 +1292,10 @@ void Graphics::setMesh3DLight(const glm::vec3 &dir, const glm::vec3 &color) {
|
||||
mesh3dFrameUbo.lightColor = glm::vec4(color, 1.f);
|
||||
}
|
||||
|
||||
void Graphics::setMesh3DCameraPos(const glm::vec3 &eye) {
|
||||
mesh3dFrameUbo.cameraPos = glm::vec4(eye, 0.f);
|
||||
}
|
||||
|
||||
vk::DescriptorSet Graphics::mesh3dSetFor(GpuTexture *gpuTex, size_t uboSlot) {
|
||||
ASSERT(gpuTex != nullptr);
|
||||
while (mesh3dUboSlots.size() <= uboSlot) {
|
||||
@@ -1280,6 +1476,11 @@ Mesh *Graphics::newMeshSphere(int slices, int stacks) {
|
||||
}
|
||||
|
||||
void Graphics::drawMesh(Mesh *mesh, const glm::mat4 &model, Texture *texture, const Color &tint) {
|
||||
drawMeshShader(mesh, model, texture, tint, nullptr);
|
||||
}
|
||||
|
||||
void Graphics::drawMeshShader(Mesh *mesh, const glm::mat4 &model, Texture *texture, const Color &tint,
|
||||
Shader *shader) {
|
||||
ASSERT(initialized);
|
||||
ASSERT(mesh != nullptr);
|
||||
if (!initialized) throw Exception("drawMesh: graphics not initialized");
|
||||
@@ -1287,14 +1488,17 @@ void Graphics::drawMesh(Mesh *mesh, const glm::mat4 &model, Texture *texture, co
|
||||
if (!swapchainPassOpen) throw Exception("drawMesh: call begin3DFrame first");
|
||||
if (!mesh3dPipeline) throw Exception("drawMesh: mesh3d pipeline missing");
|
||||
|
||||
if (shader) {
|
||||
if (shader->getKind() != Shader::Kind::eMesh3D)
|
||||
throw Exception("drawMesh: shader is not a Mesh3D shader (use newMeshShader*)");
|
||||
if (!shader->gpuHandle) throw Exception("drawMesh: shader has no GPU pipeline");
|
||||
}
|
||||
|
||||
auto *gpuMesh = static_cast<GpuMesh *>(mesh->gpuHandle);
|
||||
Texture *tex = texture ? texture : whiteTexture;
|
||||
if (!tex || !tex->gpuHandle) throw Exception("drawMesh: missing texture");
|
||||
auto *gpuTex = static_cast<GpuTexture *>(tex->gpuHandle);
|
||||
|
||||
// Caller supplies view*proj via model? No — store viewProj in mesh3dFrameUbo.mvp temporarily.
|
||||
// drawMesh receives model; mvp = viewProj * model where viewProj is in mesh3dFrameUbo.mvp
|
||||
// before multiply. RenderSystem3D sets mesh3dFrameUbo.mvp = viewProj first via setMesh3DViewProj.
|
||||
Mesh3DUBO ubo = mesh3dFrameUbo;
|
||||
ubo.model = model;
|
||||
ubo.mvp = mesh3dFrameUbo.mvp * model;
|
||||
@@ -1305,9 +1509,19 @@ void Graphics::drawMesh(Mesh *mesh, const glm::mat4 &model, Texture *texture, co
|
||||
mesh3dUboSlots[slot].ubo.updateLocal(ubo);
|
||||
|
||||
auto &cb = presentModel.getCurrentCommandBuffer();
|
||||
cb.bindPipeline(vk::PipelineBindPoint::eGraphics, mesh3dPipeline);
|
||||
cb.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, mesh3dPipelineLayout, 0, 1, &set, 0,
|
||||
nullptr);
|
||||
if (shader) {
|
||||
auto *gs = static_cast<GpuShader *>(shader->gpuHandle);
|
||||
cb.bindPipeline(vk::PipelineBindPoint::eGraphics, gs->mesh3dPipeline);
|
||||
cb.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, mesh3dShaderPipelineLayout, 0, 1, &set,
|
||||
0, nullptr);
|
||||
cb.pushConstants(mesh3dShaderPipelineLayout,
|
||||
vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment, 0,
|
||||
Shader::kPushConstantBytes, shader->pushConstantData());
|
||||
} else {
|
||||
cb.bindPipeline(vk::PipelineBindPoint::eGraphics, mesh3dPipeline);
|
||||
cb.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, mesh3dPipelineLayout, 0, 1, &set, 0,
|
||||
nullptr);
|
||||
}
|
||||
vk::DeviceSize offset = 0;
|
||||
cb.bindVertexBuffers(0, 1, gpuMesh->vertices, &offset);
|
||||
cb.bindIndexBuffer(gpuMesh->indices.buffer, 0, vk::IndexType::eUint32);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "graphics/Batcher.h"
|
||||
#include "graphics/Texture.h"
|
||||
#include "graphics/Mesh.h"
|
||||
#include "graphics/Shader.h"
|
||||
#include "vkbuilder.hpp"
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
@@ -82,6 +83,7 @@ struct Mesh3DUBO {
|
||||
glm::vec4 lightDir{0.4f, 1.f, 0.3f, 0.f};
|
||||
glm::vec4 lightColor{1.f, 1.f, 1.f, 1.f};
|
||||
glm::vec4 tint{1.f, 1.f, 1.f, 1.f};
|
||||
glm::vec4 cameraPos{0.f, 0.f, 3.f, 0.f}; // xyz = eye (for rim / custom shaders)
|
||||
};
|
||||
|
||||
struct GpuTexture {
|
||||
@@ -98,6 +100,14 @@ struct GpuMesh {
|
||||
uint32_t indexCount = 0;
|
||||
};
|
||||
|
||||
struct GpuShader {
|
||||
vk::Pipeline swapchainPipeline;
|
||||
vk::Pipeline offscreenPipeline;
|
||||
vk::Pipeline mesh3dPipeline;
|
||||
vk::PipelineLayout pipelineLayout;
|
||||
bool isMesh3D = false;
|
||||
};
|
||||
|
||||
class Graphics final : public eve::graphics::Graphics {
|
||||
public:
|
||||
~Graphics() override;
|
||||
@@ -112,12 +122,24 @@ public:
|
||||
Texture *newTexture(image::ImageData *data) override;
|
||||
Texture *newTextureFromFile(const std::string &filename) override;
|
||||
void drawTexturedRect(Texture *texture, float x, float y, float w, float h, const Color &color) override;
|
||||
void drawTexturedRectShader(Texture *texture, Shader *shader, float x, float y, float w, float h,
|
||||
const Color &color) override;
|
||||
Shader *newShaderFromSpv(const std::vector<uint32_t> &vertSpv,
|
||||
const std::vector<uint32_t> &fragSpv) override;
|
||||
Shader *newShaderFromSpvFile(const std::string &vertPath, const std::string &fragPath) override;
|
||||
Shader *newShader(const std::string &vertGlsl, const std::string &fragGlsl) override;
|
||||
Shader *newMeshShaderFromSpv(const std::vector<uint32_t> &vertSpv,
|
||||
const std::vector<uint32_t> &fragSpv) override;
|
||||
Shader *newMeshShader(const std::string &vertGlsl, const std::string &fragGlsl) override;
|
||||
Mesh *newMeshFromAssimp(const ::aiMesh &mesh) override;
|
||||
Mesh *newMeshSphere(int slices = 32, int stacks = 16) override;
|
||||
void begin3DFrame() override;
|
||||
void setMesh3DViewProj(const glm::mat4 &viewProj) override;
|
||||
void drawMesh(Mesh *mesh, const glm::mat4 &model, Texture *texture, const Color &tint) override;
|
||||
void drawMeshShader(Mesh *mesh, const glm::mat4 &model, Texture *texture, const Color &tint,
|
||||
Shader *shader) override;
|
||||
void setMesh3DLight(const glm::vec3 &dir, const glm::vec3 &color) override;
|
||||
void setMesh3DCameraPos(const glm::vec3 &eye) override;
|
||||
|
||||
Canvas *newCanvas(int width, int height) override;
|
||||
void setCanvas(Canvas *canvas) override;
|
||||
@@ -150,6 +172,13 @@ private:
|
||||
void createTexturedPipeline();
|
||||
void createMesh3DPipeline();
|
||||
void ensureOffscreenPipelines();
|
||||
void ensureShaderOffscreenPipeline(Shader *shader);
|
||||
vk::Pipeline createTexturedStylePipeline(const std::vector<uint32_t> &vert,
|
||||
const std::vector<uint32_t> &frag,
|
||||
vk::RenderPass rp, vk::PipelineLayout layout);
|
||||
vk::Pipeline createMesh3DStylePipeline(const std::vector<uint32_t> &vert,
|
||||
const std::vector<uint32_t> &frag,
|
||||
vk::PipelineLayout layout);
|
||||
void destroySwapchainResources();
|
||||
void flushBatch();
|
||||
void flushToSwapchain();
|
||||
@@ -198,6 +227,7 @@ private:
|
||||
vk::DescriptorPool descriptorPool;
|
||||
vk::Pipeline texPipeline;
|
||||
vk::PipelineLayout texPipelineLayout;
|
||||
vk::PipelineLayout shaderPipelineLayout; // tex set + push constants
|
||||
vk::CommandPool uploadPool;
|
||||
|
||||
vk::RenderPass offscreenRenderPass;
|
||||
@@ -207,6 +237,7 @@ private:
|
||||
vk::DescriptorSetLayout mesh3dSetLayout;
|
||||
vk::UniqueDescriptorSetLayout mesh3dSetLayoutUnique;
|
||||
vk::PipelineLayout mesh3dPipelineLayout;
|
||||
vk::PipelineLayout mesh3dShaderPipelineLayout; // + push constants for custom mesh shaders
|
||||
vk::Pipeline mesh3dPipeline;
|
||||
// One UBO (+ per-texture descriptor sets) per draw in the current 3D frame.
|
||||
// Avoids vkUpdateDescriptorSets on a set already bound in a recording /
|
||||
@@ -226,6 +257,7 @@ private:
|
||||
Batcher solidBatch;
|
||||
struct TexturedBatch {
|
||||
Texture *texture = nullptr;
|
||||
Shader *shader = nullptr;
|
||||
Batcher batch;
|
||||
};
|
||||
std::vector<TexturedBatch> texturedBatches;
|
||||
@@ -237,6 +269,8 @@ private:
|
||||
std::vector<std::unique_ptr<GpuTexture>> ownedGpuTextures;
|
||||
std::vector<std::unique_ptr<Mesh>> ownedMeshes;
|
||||
std::vector<std::unique_ptr<GpuMesh>> ownedGpuMeshes;
|
||||
std::vector<std::unique_ptr<Shader>> ownedShaders;
|
||||
std::vector<std::unique_ptr<GpuShader>> ownedGpuShaders;
|
||||
std::vector<std::unique_ptr<eve::graphics::Canvas>> ownedCanvases;
|
||||
|
||||
bool swapchainPassOpen = false;
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
#include "model3d/EveFileSystem.h"
|
||||
|
||||
#include "filesystem/File.h"
|
||||
#include "filesystem/Filesystem.h"
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
namespace eve {
|
||||
namespace model3d {
|
||||
|
||||
namespace {
|
||||
|
||||
struct FileHandle {
|
||||
filesystem::File *file = nullptr;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
EveFileSystem::EveFileSystem(filesystem::Filesystem *fs) : fs_(fs) {}
|
||||
|
||||
medialoader::FileHandle *EveFileSystem::open(const char *path, const char *mode) {
|
||||
if (!fs_ || !path || !mode)
|
||||
return nullptr;
|
||||
try {
|
||||
filesystem::File *file = fs_->newFile(path);
|
||||
if (!file)
|
||||
return nullptr;
|
||||
if (!file->open(mode)) {
|
||||
delete file;
|
||||
return nullptr;
|
||||
}
|
||||
auto *h = new FileHandle();
|
||||
h->file = file;
|
||||
return reinterpret_cast<medialoader::FileHandle *>(h);
|
||||
} catch (...) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
size_t EveFileSystem::read(medialoader::FileHandle *h, void *buf, size_t size) {
|
||||
auto *fh = reinterpret_cast<FileHandle *>(h);
|
||||
if (!fh || !fh->file || !buf || size == 0)
|
||||
return 0;
|
||||
try {
|
||||
int64_t n = fh->file->read(buf, static_cast<int64_t>(size));
|
||||
return n > 0 ? static_cast<size_t>(n) : 0;
|
||||
} catch (...) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool EveFileSystem::seek(medialoader::FileHandle *h, int64_t offset, int whence) {
|
||||
auto *fh = reinterpret_cast<FileHandle *>(h);
|
||||
if (!fh || !fh->file)
|
||||
return false;
|
||||
try {
|
||||
int64_t pos = 0;
|
||||
if (whence == SEEK_SET)
|
||||
pos = offset;
|
||||
else if (whence == SEEK_CUR)
|
||||
pos = fh->file->tell() + offset;
|
||||
else if (whence == SEEK_END)
|
||||
pos = fh->file->getSize() + offset;
|
||||
else
|
||||
return false;
|
||||
if (pos < 0)
|
||||
return false;
|
||||
return fh->file->seek(static_cast<uint64_t>(pos));
|
||||
} catch (...) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int64_t EveFileSystem::tell(medialoader::FileHandle *h) {
|
||||
auto *fh = reinterpret_cast<FileHandle *>(h);
|
||||
if (!fh || !fh->file)
|
||||
return -1;
|
||||
try {
|
||||
return fh->file->tell();
|
||||
} catch (...) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int64_t EveFileSystem::size(medialoader::FileHandle *h) {
|
||||
auto *fh = reinterpret_cast<FileHandle *>(h);
|
||||
if (!fh || !fh->file)
|
||||
return -1;
|
||||
try {
|
||||
return fh->file->getSize();
|
||||
} catch (...) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
void EveFileSystem::close(medialoader::FileHandle *h) {
|
||||
auto *fh = reinterpret_cast<FileHandle *>(h);
|
||||
if (!fh)
|
||||
return;
|
||||
if (fh->file) {
|
||||
try {
|
||||
if (fh->file->isOpen())
|
||||
fh->file->close();
|
||||
} catch (...) {
|
||||
}
|
||||
delete fh->file;
|
||||
fh->file = nullptr;
|
||||
}
|
||||
delete fh;
|
||||
}
|
||||
|
||||
bool EveFileSystem::exists(const char *path) const {
|
||||
if (!fs_ || !path)
|
||||
return false;
|
||||
try {
|
||||
filesystem::Filesystem::Info info;
|
||||
return fs_->getInfo(path, info);
|
||||
} catch (...) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace model3d
|
||||
} // namespace eve
|
||||
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include "medialoader/model/FileSystem.h"
|
||||
|
||||
namespace eve {
|
||||
namespace filesystem {
|
||||
class Filesystem;
|
||||
}
|
||||
|
||||
namespace model3d {
|
||||
|
||||
/** medialoader::FileSystem adapter over eve::filesystem (physfs / VFS). */
|
||||
class EveFileSystem : public medialoader::FileSystem {
|
||||
public:
|
||||
explicit EveFileSystem(filesystem::Filesystem *fs);
|
||||
|
||||
medialoader::FileHandle *open(const char *path, const char *mode) override;
|
||||
size_t read(medialoader::FileHandle *h, void *buf, size_t size) override;
|
||||
bool seek(medialoader::FileHandle *h, int64_t offset, int whence) override;
|
||||
int64_t tell(medialoader::FileHandle *h) override;
|
||||
int64_t size(medialoader::FileHandle *h) override;
|
||||
void close(medialoader::FileHandle *h) override;
|
||||
bool exists(const char *path) const override;
|
||||
|
||||
private:
|
||||
filesystem::Filesystem *fs_;
|
||||
};
|
||||
|
||||
} // namespace model3d
|
||||
} // namespace eve
|
||||
@@ -0,0 +1,125 @@
|
||||
#include "model3d/Model3D.h"
|
||||
#include "model3d/EveFileSystem.h"
|
||||
|
||||
#include "common/Data.h"
|
||||
#include "common/Exception.h"
|
||||
#include "filesystem/FileData.h"
|
||||
#include "filesystem/Filesystem.h"
|
||||
|
||||
#include "medialoader/Exception.h"
|
||||
#include "medialoader/model/ModelLoader.h"
|
||||
|
||||
#include <simplesquirrel/simplesquirrel.hpp>
|
||||
|
||||
#include <cctype>
|
||||
#include <functional>
|
||||
|
||||
namespace eve {
|
||||
namespace model3d {
|
||||
|
||||
Module_IMPL(Model3D, new Model3D());
|
||||
|
||||
Model3D::Model3D() = default;
|
||||
Model3D::~Model3D() = default;
|
||||
|
||||
namespace {
|
||||
|
||||
std::string lowerExt(std::string ext) {
|
||||
for (char &c : ext)
|
||||
c = static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
|
||||
return ext;
|
||||
}
|
||||
|
||||
std::string ensureDotExt(std::string ext) {
|
||||
ext = lowerExt(std::move(ext));
|
||||
if (ext.empty())
|
||||
return {};
|
||||
if (ext[0] != '.')
|
||||
ext.insert(ext.begin(), '.');
|
||||
return ext;
|
||||
}
|
||||
|
||||
medialoader::ModelScene loadOrThrow(medialoader::ModelLoader &loader, const void *data, size_t size,
|
||||
const char *hint) {
|
||||
try {
|
||||
return loader.loadFromMemory(data, size, hint);
|
||||
} catch (const medialoader::Exception &e) {
|
||||
throw eve::Exception("%s", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ModelData *Model3D::newModelData(Data *data, std::string hintExt) {
|
||||
if (data == nullptr || data->getData() == nullptr || data->getSize() == 0)
|
||||
throw eve::Exception("Cannot decode empty model data");
|
||||
|
||||
std::string hint = ensureDotExt(std::move(hintExt));
|
||||
if (hint.empty()) {
|
||||
if (auto *fd = dynamic_cast<filesystem::FileData *>(data))
|
||||
hint = ensureDotExt(fd->getExtension());
|
||||
}
|
||||
|
||||
// Prefer VFS for sidecar resolution when FileData carries a filename.
|
||||
filesystem::Filesystem *fs = ModuleManager::getInstance<filesystem::Filesystem>("Filesystem");
|
||||
if (!fs)
|
||||
fs = filesystem::Filesystem::create();
|
||||
|
||||
EveFileSystem eveFs(fs);
|
||||
medialoader::ModelLoader loader(&eveFs);
|
||||
|
||||
auto scene = loadOrThrow(loader, data->getData(), data->getSize(), hint.c_str());
|
||||
if (scene.empty())
|
||||
throw eve::Exception("Could not decode model data");
|
||||
|
||||
std::string uri;
|
||||
if (auto *fd = dynamic_cast<filesystem::FileData *>(data))
|
||||
uri = "file://" + fd->getFilename();
|
||||
|
||||
return new ModelData(std::move(scene), std::move(uri));
|
||||
}
|
||||
|
||||
ModelData *Model3D::newModelDataFromFile(std::string path) {
|
||||
if (path.empty())
|
||||
throw eve::Exception("Model3D::newModelDataFromFile: empty path");
|
||||
|
||||
filesystem::Filesystem *fs = ModuleManager::getInstance<filesystem::Filesystem>("Filesystem");
|
||||
if (!fs)
|
||||
fs = filesystem::Filesystem::create();
|
||||
|
||||
EveFileSystem eveFs(fs);
|
||||
medialoader::ModelLoader loader(&eveFs);
|
||||
|
||||
try {
|
||||
auto scene = loader.loadFromPath(path.c_str());
|
||||
if (scene.empty())
|
||||
throw eve::Exception("Could not load model: %s", path.c_str());
|
||||
return new ModelData(std::move(scene), "file://" + path);
|
||||
} catch (const medialoader::Exception &e) {
|
||||
throw eve::Exception("%s", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void Model3D::expose(ssq::Table &table) {
|
||||
auto cls = table.addClass(name, Model3D::create, false);
|
||||
expose(cls);
|
||||
|
||||
auto md = table.addClass<ModelData>(
|
||||
"ModelData", std::function<ModelData *()>([]() -> ModelData * { return nullptr; }), true);
|
||||
md.addFunc("empty", &ModelData::empty);
|
||||
md.addFunc("getMeshCount", &ModelData::getMeshCount);
|
||||
md.addFunc("getMaterialCount", &ModelData::getMaterialCount);
|
||||
md.addFunc("getVertexCount", &ModelData::getVertexCount);
|
||||
md.addFunc("getFaceCount", &ModelData::getFaceCount);
|
||||
md.addFunc("hasNormals", &ModelData::hasNormals);
|
||||
md.addFunc("hasTexCoords", &ModelData::hasTexCoords);
|
||||
}
|
||||
|
||||
void Model3D::expose(ssq::Class &cls) {
|
||||
cls.addFunc("getName", &Model3D::getName);
|
||||
cls.addFunc("newModelData", &Model3D::newModelData);
|
||||
cls.addFunc("newModelDataFromFile", &Model3D::newModelDataFromFile);
|
||||
}
|
||||
|
||||
} // namespace model3d
|
||||
} // namespace eve
|
||||
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/Module.h"
|
||||
#include "model3d/ModelData.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace eve {
|
||||
class Data;
|
||||
|
||||
namespace model3d {
|
||||
|
||||
/**
|
||||
* Resource module for decoding 3D models via medialoader (Assimp).
|
||||
* Produces ModelData; GPU upload is graphics' responsibility.
|
||||
*/
|
||||
class Model3D : public Module {
|
||||
public:
|
||||
Module_REG(Model3D);
|
||||
|
||||
Model3D();
|
||||
~Model3D() override;
|
||||
|
||||
/**
|
||||
* Decode model from in-memory bytes.
|
||||
* @param data Encoded model bytes (e.g. FileData / ByteData).
|
||||
* @param hintExt Extension hint for Assimp (e.g. ".obj"). If empty and
|
||||
* data is FileData, uses FileData::getExtension().
|
||||
*/
|
||||
ModelData *newModelData(Data *data, std::string hintExt = "");
|
||||
|
||||
/**
|
||||
* Decode model from a VFS path (physfs). Uses EveFileSystem so sidecar
|
||||
* files (.mtl, textures) resolve through the same filesystem.
|
||||
*/
|
||||
ModelData *newModelDataFromFile(std::string path);
|
||||
};
|
||||
|
||||
} // namespace model3d
|
||||
} // namespace eve
|
||||
@@ -0,0 +1,72 @@
|
||||
#include "model3d/ModelData.h"
|
||||
|
||||
#include "common/Exception.h"
|
||||
|
||||
#include <assimp/mesh.h>
|
||||
#include <assimp/scene.h>
|
||||
|
||||
namespace eve {
|
||||
namespace model3d {
|
||||
|
||||
ModelData::ModelData(medialoader::ModelScene scene, std::string uri)
|
||||
: Resource(std::move(uri)), scene(std::move(scene)) {}
|
||||
|
||||
ModelData::~ModelData() = default;
|
||||
|
||||
bool ModelData::empty() const { return scene.empty(); }
|
||||
|
||||
const aiScene *ModelData::getScene() const {
|
||||
if (scene.empty())
|
||||
return nullptr;
|
||||
return scene.get();
|
||||
}
|
||||
|
||||
const aiMesh *ModelData::meshAt(int meshIndex) const {
|
||||
const aiScene *sc = getScene();
|
||||
if (!sc || meshIndex < 0 || static_cast<unsigned>(meshIndex) >= sc->mNumMeshes)
|
||||
return nullptr;
|
||||
return sc->mMeshes[meshIndex];
|
||||
}
|
||||
|
||||
const aiMesh *ModelData::getMesh(int meshIndex) const { return meshAt(meshIndex); }
|
||||
|
||||
int ModelData::getMeshCount() const {
|
||||
const aiScene *sc = getScene();
|
||||
return sc ? static_cast<int>(sc->mNumMeshes) : 0;
|
||||
}
|
||||
|
||||
int ModelData::getMaterialCount() const {
|
||||
const aiScene *sc = getScene();
|
||||
return sc ? static_cast<int>(sc->mNumMaterials) : 0;
|
||||
}
|
||||
|
||||
int ModelData::getVertexCount(int meshIndex) const {
|
||||
const aiMesh *m = meshAt(meshIndex);
|
||||
if (!m)
|
||||
throw eve::Exception("ModelData::getVertexCount: invalid mesh index");
|
||||
return static_cast<int>(m->mNumVertices);
|
||||
}
|
||||
|
||||
int ModelData::getFaceCount(int meshIndex) const {
|
||||
const aiMesh *m = meshAt(meshIndex);
|
||||
if (!m)
|
||||
throw eve::Exception("ModelData::getFaceCount: invalid mesh index");
|
||||
return static_cast<int>(m->mNumFaces);
|
||||
}
|
||||
|
||||
bool ModelData::hasNormals(int meshIndex) const {
|
||||
const aiMesh *m = meshAt(meshIndex);
|
||||
if (!m)
|
||||
throw eve::Exception("ModelData::hasNormals: invalid mesh index");
|
||||
return m->HasNormals();
|
||||
}
|
||||
|
||||
bool ModelData::hasTexCoords(int meshIndex) const {
|
||||
const aiMesh *m = meshAt(meshIndex);
|
||||
if (!m)
|
||||
throw eve::Exception("ModelData::hasTexCoords: invalid mesh index");
|
||||
return m->HasTextureCoords(0);
|
||||
}
|
||||
|
||||
} // namespace model3d
|
||||
} // namespace eve
|
||||
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/Resource.h"
|
||||
|
||||
#include "medialoader/model/ModelScene.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
struct aiMesh;
|
||||
struct aiScene;
|
||||
|
||||
namespace eve {
|
||||
namespace model3d {
|
||||
|
||||
/**
|
||||
* CPU-side decoded 3D model (Assimp scene owned via medialoader::ModelScene).
|
||||
* Does not upload to GPU — use graphics::Graphics::newMeshFromAssimp on getMesh().
|
||||
*/
|
||||
class ModelData : public Resource {
|
||||
public:
|
||||
explicit ModelData(medialoader::ModelScene scene, std::string uri = "");
|
||||
~ModelData() override;
|
||||
|
||||
bool empty() const;
|
||||
int getMeshCount() const;
|
||||
int getMaterialCount() const;
|
||||
int getVertexCount(int meshIndex) const;
|
||||
int getFaceCount(int meshIndex) const;
|
||||
bool hasNormals(int meshIndex) const;
|
||||
bool hasTexCoords(int meshIndex) const;
|
||||
|
||||
const aiScene *getScene() const;
|
||||
const aiMesh *getMesh(int meshIndex) const;
|
||||
|
||||
private:
|
||||
const aiMesh *meshAt(int meshIndex) const;
|
||||
|
||||
medialoader::ModelScene scene;
|
||||
};
|
||||
|
||||
} // namespace model3d
|
||||
} // namespace eve
|
||||
@@ -0,0 +1,111 @@
|
||||
#include "plugins/Plugins.h"
|
||||
|
||||
#include "common/Exception.h"
|
||||
|
||||
#include <simplesquirrel/simplesquirrel.hpp>
|
||||
|
||||
#if defined(_WIN32)
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
# include <windows.h>
|
||||
#else
|
||||
# include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
namespace eve::plugins {
|
||||
|
||||
using PluginInitFn = int (*)();
|
||||
|
||||
Module_IMPL(Plugins, new Plugins());
|
||||
|
||||
Plugins::Plugins() = default;
|
||||
|
||||
Plugins::~Plugins() {
|
||||
for (auto& kv : loaded_) {
|
||||
#if defined(_WIN32)
|
||||
if (kv.second.native)
|
||||
FreeLibrary(static_cast<HMODULE>(kv.second.native));
|
||||
#else
|
||||
if (kv.second.native)
|
||||
dlclose(kv.second.native);
|
||||
#endif
|
||||
}
|
||||
loaded_.clear();
|
||||
}
|
||||
|
||||
bool Plugins::load(const std::string& path) {
|
||||
if (path.empty())
|
||||
throw Exception("plugins.load: empty path");
|
||||
if (loaded_.count(path))
|
||||
return true;
|
||||
|
||||
#if defined(_WIN32)
|
||||
HMODULE mod = LoadLibraryA(path.c_str());
|
||||
if (!mod)
|
||||
throw Exception("plugins.load: LoadLibrary failed for '%s' (err=%lu)", path.c_str(),
|
||||
GetLastError());
|
||||
auto init = reinterpret_cast<PluginInitFn>(GetProcAddress(mod, "eve_plugin_init"));
|
||||
if (!init) {
|
||||
FreeLibrary(mod);
|
||||
throw Exception("plugins.load: missing eve_plugin_init in '%s'", path.c_str());
|
||||
}
|
||||
int rc = init();
|
||||
if (rc != 0) {
|
||||
FreeLibrary(mod);
|
||||
throw Exception("plugins.load: eve_plugin_init returned %d for '%s'", rc, path.c_str());
|
||||
}
|
||||
loaded_[path] = Handle{mod, path};
|
||||
#else
|
||||
void* mod = dlopen(path.c_str(), RTLD_NOW | RTLD_GLOBAL);
|
||||
if (!mod)
|
||||
throw Exception("plugins.load: dlopen failed for '%s': %s", path.c_str(), dlerror());
|
||||
dlerror();
|
||||
auto init = reinterpret_cast<PluginInitFn>(dlsym(mod, "eve_plugin_init"));
|
||||
const char* err = dlerror();
|
||||
if (err != nullptr || !init) {
|
||||
dlclose(mod);
|
||||
throw Exception("plugins.load: missing eve_plugin_init in '%s'%s%s", path.c_str(),
|
||||
err ? ": " : "", err ? err : "");
|
||||
}
|
||||
int rc = init();
|
||||
if (rc != 0) {
|
||||
dlclose(mod);
|
||||
throw Exception("plugins.load: eve_plugin_init returned %d for '%s'", rc, path.c_str());
|
||||
}
|
||||
loaded_[path] = Handle{mod, path};
|
||||
#endif
|
||||
|
||||
if (ModuleManager::expose_pending() < 0)
|
||||
throw Exception("plugins.load: expose_pending failed after loading '%s'", path.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Plugins::unload(const std::string& path) {
|
||||
auto it = loaded_.find(path);
|
||||
if (it == loaded_.end())
|
||||
return false;
|
||||
#if defined(_WIN32)
|
||||
FreeLibrary(static_cast<HMODULE>(it->second.native));
|
||||
#else
|
||||
dlclose(it->second.native);
|
||||
#endif
|
||||
loaded_.erase(it);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Plugins::isLoaded(const std::string& path) const {
|
||||
return loaded_.count(path) > 0;
|
||||
}
|
||||
|
||||
void Plugins::expose(ssq::Table& table) {
|
||||
auto cls = table.addClass(name, Plugins::create, false);
|
||||
expose(cls);
|
||||
}
|
||||
|
||||
void Plugins::expose(ssq::Class& cls) {
|
||||
cls.addFunc("getName", &Plugins::getName);
|
||||
cls.addFunc("load", &Plugins::load);
|
||||
cls.addFunc("unload", &Plugins::unload);
|
||||
cls.addFunc("isLoaded", &Plugins::isLoaded);
|
||||
}
|
||||
|
||||
} // namespace eve::plugins
|
||||
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/Module.h"
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace eve::plugins {
|
||||
|
||||
// Loads native shared libraries (dll / so / dylib) that export eve_plugin_init.
|
||||
class Plugins : public Module {
|
||||
public:
|
||||
Module_REG(Plugins);
|
||||
Plugins();
|
||||
~Plugins() override;
|
||||
|
||||
// Absolute or game-relative path. Returns true on success.
|
||||
bool load(const std::string& path);
|
||||
bool unload(const std::string& path);
|
||||
bool isLoaded(const std::string& path) const;
|
||||
|
||||
private:
|
||||
struct Handle {
|
||||
void* native = nullptr;
|
||||
std::string path;
|
||||
};
|
||||
|
||||
std::unordered_map<std::string, Handle> loaded_;
|
||||
};
|
||||
|
||||
} // namespace eve::plugins
|
||||
@@ -44,6 +44,7 @@ mouse <- eve.Mouse();
|
||||
touch <- eve.Touch();
|
||||
sound <- eve.Sound();
|
||||
audio <- eve.Audio();
|
||||
model3d <- eve.Model3D();
|
||||
|
||||
eve_init <- function() {};
|
||||
eve_update <- function(dt) {};
|
||||
|
||||
+5
-1
@@ -22,8 +22,10 @@ file(GLOB all_test_cpp
|
||||
UIPhaseAdv.cpp
|
||||
UIOverlaySmoke.cpp
|
||||
particles.cpp
|
||||
shader.cpp
|
||||
simplesquirrel.cpp
|
||||
sound.cpp
|
||||
model3d.cpp
|
||||
audio.cpp
|
||||
window.cpp
|
||||
window_cpp.cpp
|
||||
@@ -121,7 +123,7 @@ else()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(EVELIBS EVCmdLine EVScripts EVWindow EVFileSystem EVMouse EVKeyboard EVJoystick EVTouch EVGraphics EVImage EVDevTools EVCommon EVEvent EVTimer EVNetwork EVData EVSound EVAudio EVUI eve_imgui EVParticles)
|
||||
set(EVELIBS EVCmdLine EVScripts EVWindow EVFileSystem EVMouse EVKeyboard EVJoystick EVTouch EVGraphics EVImage EVDevTools EVCommon EVEvent EVTimer EVNetwork EVData EVSound EVAudio EVUI eve_imgui EVParticles EVModel3D)
|
||||
|
||||
if(EVENGINE_BUILD_DEMO)
|
||||
list(APPEND EVELIBS EVDemo)
|
||||
@@ -132,6 +134,8 @@ if(EVENGINE_PLATFORM_LIB)
|
||||
endif()
|
||||
|
||||
target_link_libraries(unit_test ${EVELIBS} zeroerr ${ThirdParty} ${EVENGINE_VULKAN_LIB})
|
||||
target_compile_definitions(unit_test PRIVATE
|
||||
EVENGINE_TEST_BINARY_DIR="${CMAKE_CURRENT_BINARY_DIR}")
|
||||
|
||||
include(${CMAKE_SOURCE_DIR}/cmake/ZeroErrDiscoverTests.cmake)
|
||||
zeroerr_discover_tests(unit_test)
|
||||
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 2.7 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.2 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.3 MiB |
@@ -0,0 +1,648 @@
|
||||
#include "zeroerr/assert.h"
|
||||
#include "zeroerr/unittest.h"
|
||||
|
||||
#include "model3d/Model3D.h"
|
||||
#include "model3d/ModelData.h"
|
||||
#include "data/ByteData.h"
|
||||
#include "filesystem/Filesystem.h"
|
||||
#include "common/Exception.h"
|
||||
|
||||
#include "graphics/Graphics.h"
|
||||
#include "graphics/Mesh.h"
|
||||
#include "graphics/RenderSystem.h"
|
||||
#include "graphics/RenderSystem3D.h"
|
||||
#include "image/Image.h"
|
||||
#include "image/ImageData.h"
|
||||
#include "window/Window.h"
|
||||
|
||||
#include <assimp/mesh.h>
|
||||
#include <assimp/scene.h>
|
||||
#include <assimp/material.h>
|
||||
#include <assimp/texture.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "graphics/shaders/mesh3d_toon_frag_spv.inc"
|
||||
#include "graphics/shaders/mesh3d_toon_vert_spv.inc"
|
||||
|
||||
using namespace eve::graphics;
|
||||
|
||||
namespace {
|
||||
|
||||
std::string pathBesideThisSource(const char *filename) {
|
||||
std::string here = __FILE__;
|
||||
auto slash = here.find_last_of("/\\");
|
||||
std::string dir = (slash == std::string::npos) ? std::string(".") : here.substr(0, slash);
|
||||
return dir + "/" + filename;
|
||||
}
|
||||
|
||||
std::vector<char> readBinaryFile(const std::string &path) {
|
||||
std::ifstream in(path, std::ios::binary);
|
||||
if (!in)
|
||||
return {};
|
||||
return std::vector<char>((std::istreambuf_iterator<char>(in)), std::istreambuf_iterator<char>());
|
||||
}
|
||||
|
||||
float luma(const Color &c) { return (c.r + c.g + c.b) / 3.f; }
|
||||
|
||||
void openGfxWindow(eve::window::Window *&win, Graphics *&gfx, int w = 640, int h = 480) {
|
||||
win = eve::window::Window::create();
|
||||
gfx = Graphics::create();
|
||||
REQUIRE(win != nullptr);
|
||||
REQUIRE(gfx != nullptr);
|
||||
win->setGraphics(gfx);
|
||||
eve::window::WindowSettings s;
|
||||
s.width = w;
|
||||
s.height = h;
|
||||
s.centered = true;
|
||||
REQUIRE(win->setWindowSettings(s));
|
||||
}
|
||||
|
||||
void resetScene3D() {
|
||||
if (ecs::ComponentManager<Renderable3D>::inst().registy != nullptr) {
|
||||
auto view = ecs::View<Renderable3D, Renderable3D::MeshRenderer>();
|
||||
for (auto it = view.begin(); it != view.end(); ++it) {
|
||||
auto [mr] = *it;
|
||||
mr->visible = false;
|
||||
}
|
||||
}
|
||||
if (ecs::ComponentManager<Camera3D>::inst().registy != nullptr) {
|
||||
auto camView = ecs::View<Camera3D, Camera3D::Data>();
|
||||
for (auto it = camView.begin(); it != camView.end(); ++it) {
|
||||
auto [data] = *it;
|
||||
data->active = false;
|
||||
}
|
||||
}
|
||||
if (ecs::ComponentManager<Renderable2D>::inst().registy != nullptr) {
|
||||
auto view = ecs::View<Renderable2D, Renderable2D::Sprite>();
|
||||
for (auto it = view.begin(); it != view.end(); ++it) {
|
||||
auto [sp] = *it;
|
||||
sp->visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Texture *makeSolidGray(Graphics *gfx, uint8_t g) {
|
||||
uint8_t px[4] = {g, g, g, 255};
|
||||
return gfx->newTexture(1, 1, px);
|
||||
}
|
||||
|
||||
Texture *makeSolidRGB(Graphics *gfx, uint8_t r, uint8_t g, uint8_t b) {
|
||||
uint8_t px[4] = {r, g, b, 255};
|
||||
return gfx->newTexture(1, 1, px);
|
||||
}
|
||||
|
||||
Texture *textureFromImageData(Graphics *gfx, eve::image::ImageData *img) {
|
||||
if (!img)
|
||||
return nullptr;
|
||||
return gfx->newTexture(img);
|
||||
}
|
||||
|
||||
Texture *loadAssimpDiffuseTexture(Graphics *gfx, const aiScene *scene, const aiMaterial *mat) {
|
||||
if (!scene || !mat)
|
||||
return nullptr;
|
||||
|
||||
aiString path;
|
||||
if (mat->GetTexture(aiTextureType_DIFFUSE, 0, &path) != AI_SUCCESS)
|
||||
return nullptr;
|
||||
|
||||
const char *p = path.C_Str();
|
||||
if (!p || !p[0])
|
||||
return nullptr;
|
||||
|
||||
eve::image::Image::create();
|
||||
|
||||
// Embedded: "*0", "*1", ...
|
||||
if (p[0] == '*') {
|
||||
int idx = std::atoi(p + 1);
|
||||
if (idx < 0 || static_cast<unsigned>(idx) >= scene->mNumTextures)
|
||||
return nullptr;
|
||||
const aiTexture *tex = scene->mTextures[idx];
|
||||
if (!tex || !tex->pcData)
|
||||
return nullptr;
|
||||
|
||||
if (tex->mHeight == 0) {
|
||||
// Compressed blob (png/jpg/...).
|
||||
eve::data::ByteData bytes(tex->pcData, static_cast<size_t>(tex->mWidth));
|
||||
try {
|
||||
eve::image::ImageData *img = eve::image::Image::create()->newImageData(&bytes);
|
||||
Texture *t = textureFromImageData(gfx, img);
|
||||
delete img;
|
||||
return t;
|
||||
} catch (...) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Uncompressed BGRA8888.
|
||||
const unsigned w = tex->mWidth;
|
||||
const unsigned h = tex->mHeight;
|
||||
std::vector<uint8_t> rgba(size_t(w) * size_t(h) * 4);
|
||||
const aiTexel *src = tex->pcData;
|
||||
for (unsigned i = 0; i < w * h; ++i) {
|
||||
rgba[i * 4 + 0] = src[i].r;
|
||||
rgba[i * 4 + 1] = src[i].g;
|
||||
rgba[i * 4 + 2] = src[i].b;
|
||||
rgba[i * 4 + 3] = src[i].a;
|
||||
}
|
||||
return gfx->newTexture(int(w), int(h), rgba.data());
|
||||
}
|
||||
|
||||
// External file — try VFS then OS path beside test assets.
|
||||
try {
|
||||
auto *fs = eve::filesystem::Filesystem::create();
|
||||
std::unique_ptr<eve::filesystem::FileData> fd(fs->read(p));
|
||||
if (fd && fd->getSize() > 0) {
|
||||
eve::image::ImageData *img = eve::image::Image::create()->newImageData(fd.get());
|
||||
Texture *t = textureFromImageData(gfx, img);
|
||||
delete img;
|
||||
return t;
|
||||
}
|
||||
} catch (...) {
|
||||
}
|
||||
|
||||
// Basename / common relative layouts under test/
|
||||
std::string base = p;
|
||||
auto slash = base.find_last_of("/\\");
|
||||
if (slash != std::string::npos)
|
||||
base = base.substr(slash + 1);
|
||||
|
||||
const char *candidates[] = {
|
||||
base.c_str(),
|
||||
nullptr, // filled below for Textures/
|
||||
};
|
||||
std::string underTextures = std::string("Textures/") + base;
|
||||
candidates[1] = underTextures.c_str();
|
||||
|
||||
for (const char *rel : candidates) {
|
||||
if (!rel || !rel[0])
|
||||
continue;
|
||||
auto fileBytes = readBinaryFile(pathBesideThisSource(rel));
|
||||
if (fileBytes.empty())
|
||||
continue;
|
||||
try {
|
||||
eve::data::ByteData bytes(fileBytes.data(), fileBytes.size());
|
||||
eve::image::ImageData *img = eve::image::Image::create()->newImageData(&bytes);
|
||||
Texture *t = textureFromImageData(gfx, img);
|
||||
delete img;
|
||||
return t;
|
||||
} catch (...) {
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Texture *loadTextureFile(Graphics *gfx, const char *relPathBesideTest) {
|
||||
if (!relPathBesideTest || !relPathBesideTest[0])
|
||||
return nullptr;
|
||||
auto fileBytes = readBinaryFile(pathBesideThisSource(relPathBesideTest));
|
||||
if (fileBytes.empty())
|
||||
return nullptr;
|
||||
eve::image::Image::create();
|
||||
try {
|
||||
eve::data::ByteData bytes(fileBytes.data(), fileBytes.size());
|
||||
eve::image::ImageData *img = eve::image::Image::create()->newImageData(&bytes);
|
||||
Texture *t = textureFromImageData(gfx, img);
|
||||
delete img;
|
||||
return t;
|
||||
} catch (...) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
struct Bounds {
|
||||
float minX = 0, minY = 0, minZ = 0;
|
||||
float maxX = 0, maxY = 0, maxZ = 0;
|
||||
bool valid = false;
|
||||
|
||||
void expand(float x, float y, float z) {
|
||||
if (!valid) {
|
||||
minX = maxX = x;
|
||||
minY = maxY = y;
|
||||
minZ = maxZ = z;
|
||||
valid = true;
|
||||
return;
|
||||
}
|
||||
minX = std::min(minX, x);
|
||||
minY = std::min(minY, y);
|
||||
minZ = std::min(minZ, z);
|
||||
maxX = std::max(maxX, x);
|
||||
maxY = std::max(maxY, y);
|
||||
maxZ = std::max(maxZ, z);
|
||||
}
|
||||
|
||||
float centerX() const { return 0.5f * (minX + maxX); }
|
||||
float centerY() const { return 0.5f * (minY + maxY); }
|
||||
float centerZ() const { return 0.5f * (minZ + maxZ); }
|
||||
float radius() const {
|
||||
const float dx = maxX - minX;
|
||||
const float dy = maxY - minY;
|
||||
const float dz = maxZ - minZ;
|
||||
return 0.5f * std::sqrt(dx * dx + dy * dy + dz * dz);
|
||||
}
|
||||
};
|
||||
|
||||
Bounds boundsOf(eve::model3d::ModelData *md) {
|
||||
Bounds b;
|
||||
for (int i = 0; i < md->getMeshCount(); ++i) {
|
||||
const aiMesh *mesh = md->getMesh(i);
|
||||
if (!mesh)
|
||||
continue;
|
||||
for (unsigned v = 0; v < mesh->mNumVertices; ++v)
|
||||
b.expand(mesh->mVertices[v].x, mesh->mVertices[v].y, mesh->mVertices[v].z);
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
/** Upload every mesh with Assimp material tint/texture; frame camera; save PNG.
|
||||
* @param fallbackDiffuse optional path relative to test/ when Assimp has no map.
|
||||
* @param makeStyle optional factory: given Graphics*, return Mesh3D Shader for all entities. */
|
||||
void renderModelSmoke(eve::model3d::ModelData *md, const char *pngName,
|
||||
const char *fallbackDiffuse = nullptr,
|
||||
std::function<Shader *(Graphics *)> makeStyle = {}) {
|
||||
REQUIRE(md != nullptr);
|
||||
REQUIRE(md->getMeshCount() >= 1);
|
||||
REQUIRE(pngName != nullptr);
|
||||
const aiScene *scene = md->getScene();
|
||||
REQUIRE(scene != nullptr);
|
||||
|
||||
eve::window::Window *win = nullptr;
|
||||
Graphics *gfx = nullptr;
|
||||
openGfxWindow(win, gfx);
|
||||
resetScene3D();
|
||||
|
||||
Shader *styleShader = makeStyle ? makeStyle(gfx) : nullptr;
|
||||
|
||||
Bounds b = boundsOf(md);
|
||||
REQUIRE(b.valid);
|
||||
const float cx = b.centerX();
|
||||
const float cy = b.centerY();
|
||||
const float cz = b.centerZ();
|
||||
const float rad = std::max(0.5f, b.radius());
|
||||
|
||||
Texture *white = makeSolidGray(gfx, 255);
|
||||
Texture *fallbackTex = loadTextureFile(gfx, fallbackDiffuse);
|
||||
struct MatLook {
|
||||
Texture *tex = nullptr;
|
||||
float tr = 1.f, tg = 1.f, tb = 1.f;
|
||||
};
|
||||
std::unordered_map<unsigned, MatLook> matCache;
|
||||
|
||||
int spawned = 0;
|
||||
for (int i = 0; i < md->getMeshCount(); ++i) {
|
||||
const aiMesh *ai = md->getMesh(i);
|
||||
if (!ai || ai->mNumFaces == 0)
|
||||
continue;
|
||||
Mesh *mesh = gfx->newMeshFromAssimp(*ai);
|
||||
REQUIRE(mesh != nullptr);
|
||||
REQUIRE_GT(mesh->indexCount, 0);
|
||||
|
||||
MatLook look{white, 1.f, 1.f, 1.f};
|
||||
const unsigned matIndex = ai->mMaterialIndex;
|
||||
auto cached = matCache.find(matIndex);
|
||||
if (cached != matCache.end()) {
|
||||
look = cached->second;
|
||||
} else if (scene->mMaterials && matIndex < scene->mNumMaterials) {
|
||||
const aiMaterial *mat = scene->mMaterials[matIndex];
|
||||
aiColor3D kd(1.f, 1.f, 1.f);
|
||||
mat->Get(AI_MATKEY_COLOR_DIFFUSE, kd);
|
||||
|
||||
Texture *fromMat = loadAssimpDiffuseTexture(gfx, scene, mat);
|
||||
if (fromMat) {
|
||||
look.tex = fromMat;
|
||||
look.tr = kd.r;
|
||||
look.tg = kd.g;
|
||||
look.tb = kd.b;
|
||||
} else if (fallbackTex) {
|
||||
look.tex = fallbackTex;
|
||||
look.tr = look.tg = look.tb = 1.f;
|
||||
} else {
|
||||
look.tex = makeSolidRGB(gfx, uint8_t(std::clamp(kd.r, 0.f, 1.f) * 255.f + 0.5f),
|
||||
uint8_t(std::clamp(kd.g, 0.f, 1.f) * 255.f + 0.5f),
|
||||
uint8_t(std::clamp(kd.b, 0.f, 1.f) * 255.f + 0.5f));
|
||||
look.tr = look.tg = look.tb = 1.f;
|
||||
}
|
||||
matCache[matIndex] = look;
|
||||
} else if (fallbackTex) {
|
||||
look.tex = fallbackTex;
|
||||
matCache[matIndex] = look;
|
||||
}
|
||||
|
||||
auto *ent = Renderable3D::create();
|
||||
ent->meshRenderer()->mesh = mesh;
|
||||
ent->meshRenderer()->texture = look.tex;
|
||||
ent->meshRenderer()->shader = styleShader;
|
||||
ent->meshRenderer()->visible = true;
|
||||
ent->setTint(look.tr, look.tg, look.tb, 1.f);
|
||||
++spawned;
|
||||
}
|
||||
REQUIRE(spawned >= 1);
|
||||
|
||||
auto *cam = Camera3D::createCamera();
|
||||
// 3/4 view: above + along +Z. Fit clip planes to asset scale (Rock1 is ~1e3 units).
|
||||
cam->setTarget(cx, cy, cz);
|
||||
cam->setEye(cx + rad * 0.9f, cy + rad * 0.55f, cz + rad * 1.8f);
|
||||
cam->data()->nearZ = std::max(0.05f, rad * 0.01f);
|
||||
cam->data()->farZ = std::max(100.f, rad * 20.f);
|
||||
|
||||
auto *hud = Renderable2D::create();
|
||||
hud->transform()->x = 0;
|
||||
hud->transform()->y = 0;
|
||||
hud->sprite()->width = 2;
|
||||
hud->sprite()->height = 2;
|
||||
hud->sprite()->r = 0.f;
|
||||
hud->sprite()->g = 0.f;
|
||||
hud->sprite()->b = 0.f;
|
||||
|
||||
gfx->setScreenReadbackEnabled(true);
|
||||
gfx->setBackgroundColor(Color(0.12f, 0.14f, 0.18f, 1.f));
|
||||
|
||||
// Light rays travel from camera toward the subject (matches RenderSystem3D lighting test).
|
||||
const float lx = cx - (cx + rad * 0.9f);
|
||||
const float ly = cy - (cy + rad * 0.55f);
|
||||
const float lz = cz - (cz + rad * 1.8f);
|
||||
RenderSystem3D::setDirectionalLight(lx, ly + rad * 1.2f, lz, 1.8f, 1.8f, 1.8f);
|
||||
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
if (ecs::ComponentManager<Renderable3D>::inst().registy != nullptr) {
|
||||
auto view = ecs::View<Renderable3D, Renderable3D::Transform3D, Renderable3D::MeshRenderer>();
|
||||
for (auto it = view.begin(); it != view.end(); ++it) {
|
||||
auto [xf, mr] = *it;
|
||||
if (!mr->visible)
|
||||
continue;
|
||||
// Gentle yaw so materials stay readable in the last saved frame.
|
||||
xf->yaw = float(i) * 0.06f;
|
||||
}
|
||||
}
|
||||
RenderSystem3D::render(*gfx);
|
||||
RenderSystem::render(*gfx);
|
||||
}
|
||||
|
||||
Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2);
|
||||
CHECK(luma(mid) > 0.05f);
|
||||
|
||||
eve::image::Image::create();
|
||||
eve::image::ImageData *frame = gfx->newImageData();
|
||||
REQUIRE(frame != nullptr);
|
||||
eve::filesystem::FileData *png =
|
||||
frame->encode(medialoader::FormatHandler::ENCODED_PNG, pngName, false);
|
||||
REQUIRE(png != nullptr);
|
||||
REQUIRE(png->getSize() > 0);
|
||||
|
||||
// Under the CMake test binary dir (build/.../test/out), already gitignored via build*/.
|
||||
const std::string outDir = std::string(EVENGINE_TEST_BINARY_DIR) + "/out";
|
||||
std::error_code ec;
|
||||
std::filesystem::create_directories(outDir, ec);
|
||||
const std::string outPath = outDir + "/" + pngName;
|
||||
{
|
||||
std::ofstream out(outPath, std::ios::binary);
|
||||
REQUIRE(out.good());
|
||||
out.write(static_cast<const char *>(png->getData()),
|
||||
static_cast<std::streamsize>(png->getSize()));
|
||||
REQUIRE(out.good());
|
||||
}
|
||||
std::printf("model3d render saved: %s (materials=%u cached=%zu style=%s)\n", outPath.c_str(),
|
||||
scene->mNumMaterials, matCache.size(), styleShader ? "yes" : "no");
|
||||
delete png;
|
||||
delete frame;
|
||||
|
||||
win->close();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// Minimal unit cube as Wavefront OBJ (same shape as medialoader model tests).
|
||||
static const char kCubeObj[] =
|
||||
"v -0.5 -0.5 -0.5\n"
|
||||
"v 0.5 -0.5 -0.5\n"
|
||||
"v 0.5 0.5 -0.5\n"
|
||||
"v -0.5 0.5 -0.5\n"
|
||||
"v -0.5 -0.5 0.5\n"
|
||||
"v 0.5 -0.5 0.5\n"
|
||||
"v 0.5 0.5 0.5\n"
|
||||
"v -0.5 0.5 0.5\n"
|
||||
"f 1 2 3\n"
|
||||
"f 1 3 4\n"
|
||||
"f 5 6 7\n"
|
||||
"f 5 7 8\n"
|
||||
"f 1 2 6\n"
|
||||
"f 1 6 5\n"
|
||||
"f 2 3 7\n"
|
||||
"f 2 7 6\n"
|
||||
"f 3 4 8\n"
|
||||
"f 3 8 7\n"
|
||||
"f 4 1 5\n"
|
||||
"f 4 5 8\n";
|
||||
|
||||
TEST_CASE("model3d.newModelData.objFromMemory") {
|
||||
auto *mod = eve::model3d::Model3D::create();
|
||||
eve::data::ByteData data(kCubeObj, sizeof(kCubeObj) - 1);
|
||||
auto *md = mod->newModelData(&data, ".obj");
|
||||
REQUIRE(md != nullptr);
|
||||
CHECK(!md->empty());
|
||||
CHECK(md->getMeshCount() >= 1);
|
||||
CHECK(md->getVertexCount(0) > 0);
|
||||
CHECK(md->getFaceCount(0) > 0);
|
||||
CHECK(md->getMesh(0) != nullptr);
|
||||
CHECK(md->getScene() != nullptr);
|
||||
delete md;
|
||||
}
|
||||
|
||||
TEST_CASE("model3d.newModelData.emptyThrows") {
|
||||
auto *mod = eve::model3d::Model3D::create();
|
||||
bool threw = false;
|
||||
try {
|
||||
mod->newModelData(nullptr, ".obj");
|
||||
} catch (const eve::Exception &) {
|
||||
threw = true;
|
||||
}
|
||||
CHECK(threw);
|
||||
}
|
||||
|
||||
TEST_CASE("model3d.newModelData.invalidMeshIndexThrows") {
|
||||
auto *mod = eve::model3d::Model3D::create();
|
||||
eve::data::ByteData data(kCubeObj, sizeof(kCubeObj) - 1);
|
||||
auto *md = mod->newModelData(&data, ".obj");
|
||||
REQUIRE(md != nullptr);
|
||||
bool threw = false;
|
||||
try {
|
||||
md->getVertexCount(999);
|
||||
} catch (const eve::Exception &) {
|
||||
threw = true;
|
||||
}
|
||||
CHECK(threw);
|
||||
delete md;
|
||||
}
|
||||
|
||||
TEST_CASE("model3d.newModelDataFromFile.missingThrows") {
|
||||
auto *mod = eve::model3d::Model3D::create();
|
||||
bool threw = false;
|
||||
try {
|
||||
mod->newModelDataFromFile("definitely_missing_eve_model3d.obj");
|
||||
} catch (const eve::Exception &) {
|
||||
threw = true;
|
||||
}
|
||||
CHECK(threw);
|
||||
}
|
||||
|
||||
TEST_CASE("model3d.Model3D.getName") {
|
||||
auto *mod = eve::model3d::Model3D::create();
|
||||
CHECK(mod->getName() == "Model3D");
|
||||
}
|
||||
|
||||
TEST_CASE("model3d.newModelData.rock1Fbx") {
|
||||
const std::string path = pathBesideThisSource("Rock1.fbx");
|
||||
auto bytes = readBinaryFile(path);
|
||||
REQUIRE(!bytes.empty());
|
||||
|
||||
auto *mod = eve::model3d::Model3D::create();
|
||||
eve::data::ByteData data(bytes.data(), bytes.size());
|
||||
auto *md = mod->newModelData(&data, ".fbx");
|
||||
REQUIRE(md != nullptr);
|
||||
CHECK(!md->empty());
|
||||
CHECK(md->getMeshCount() >= 1);
|
||||
CHECK(md->getScene() != nullptr);
|
||||
CHECK(md->hasTexCoords(0));
|
||||
|
||||
int totalVerts = 0;
|
||||
int totalFaces = 0;
|
||||
for (int i = 0; i < md->getMeshCount(); ++i) {
|
||||
CHECK(md->getMesh(i) != nullptr);
|
||||
int verts = md->getVertexCount(i);
|
||||
int faces = md->getFaceCount(i);
|
||||
CHECK(verts > 0);
|
||||
CHECK(faces > 0);
|
||||
totalVerts += verts;
|
||||
totalFaces += faces;
|
||||
}
|
||||
CHECK(totalVerts > 0);
|
||||
CHECK(totalFaces > 0);
|
||||
delete md;
|
||||
}
|
||||
|
||||
TEST_CASE("model3d.newModelDataFromFile.sofaObjMtl") {
|
||||
const std::string objPath = pathBesideThisSource("sofa.obj");
|
||||
const std::string mtlPath = pathBesideThisSource("sofa.mtl");
|
||||
REQUIRE(!readBinaryFile(objPath).empty());
|
||||
REQUIRE(!readBinaryFile(mtlPath).empty());
|
||||
|
||||
std::string testDir = pathBesideThisSource("");
|
||||
if (!testDir.empty() && (testDir.back() == '/' || testDir.back() == '\\'))
|
||||
testDir.pop_back();
|
||||
|
||||
auto *fs = eve::filesystem::Filesystem::create();
|
||||
REQUIRE(fs->setIdentity("ev_ut_model3d_sofa", true));
|
||||
REQUIRE(fs->setupWriteDirectory());
|
||||
fs->allowMountingForPath(testDir);
|
||||
REQUIRE(fs->mount(testDir, "", false));
|
||||
|
||||
auto *mod = eve::model3d::Model3D::create();
|
||||
auto *md = mod->newModelDataFromFile("sofa.obj");
|
||||
REQUIRE(md != nullptr);
|
||||
CHECK(!md->empty());
|
||||
CHECK(md->getMeshCount() >= 1);
|
||||
// Blender MTL lists 5 materials; Assimp may add a default.
|
||||
CHECK(md->getMaterialCount() >= 5);
|
||||
CHECK(md->getScene() != nullptr);
|
||||
|
||||
int totalVerts = 0;
|
||||
int totalFaces = 0;
|
||||
for (int i = 0; i < md->getMeshCount(); ++i) {
|
||||
CHECK(md->getMesh(i) != nullptr);
|
||||
int verts = md->getVertexCount(i);
|
||||
int faces = md->getFaceCount(i);
|
||||
CHECK(verts > 0);
|
||||
CHECK(faces > 0);
|
||||
totalVerts += verts;
|
||||
totalFaces += faces;
|
||||
}
|
||||
CHECK(totalVerts > 0);
|
||||
CHECK(totalFaces > 0);
|
||||
delete md;
|
||||
|
||||
CHECK(fs->unmount(testDir));
|
||||
}
|
||||
|
||||
TEST_CASE("model3d.render.rock1Fbx") {
|
||||
const std::string path = pathBesideThisSource("Rock1.fbx");
|
||||
auto bytes = readBinaryFile(path);
|
||||
REQUIRE(!bytes.empty());
|
||||
REQUIRE(!readBinaryFile(pathBesideThisSource("Textures/Rock1_Diffuse.png")).empty());
|
||||
|
||||
auto *mod = eve::model3d::Model3D::create();
|
||||
eve::data::ByteData data(bytes.data(), bytes.size());
|
||||
auto *md = mod->newModelData(&data, ".fbx");
|
||||
REQUIRE(md != nullptr);
|
||||
// FBX material has no embedded map path; bind companion diffuse next to the asset.
|
||||
renderModelSmoke(md, "model3d_rock1.png", "Textures/Rock1_Diffuse.png");
|
||||
delete md;
|
||||
}
|
||||
|
||||
TEST_CASE("model3d.render.rock1ToonShader") {
|
||||
const std::string path = pathBesideThisSource("Rock1.fbx");
|
||||
auto bytes = readBinaryFile(path);
|
||||
REQUIRE(!bytes.empty());
|
||||
REQUIRE(!readBinaryFile(pathBesideThisSource("Textures/Rock1_Diffuse.png")).empty());
|
||||
|
||||
auto *mod = eve::model3d::Model3D::create();
|
||||
eve::data::ByteData data(bytes.data(), bytes.size());
|
||||
auto *md = mod->newModelData(&data, ".fbx");
|
||||
REQUIRE(md != nullptr);
|
||||
|
||||
renderModelSmoke(md, "model3d_rock1_toon.png", "Textures/Rock1_Diffuse.png",
|
||||
[](Graphics *gfx) -> Shader * {
|
||||
std::vector<uint32_t> vert(mesh3d_toon_vert_spv,
|
||||
mesh3d_toon_vert_spv + mesh3d_toon_vert_spv_count);
|
||||
std::vector<uint32_t> frag(mesh3d_toon_frag_spv,
|
||||
mesh3d_toon_frag_spv + mesh3d_toon_frag_spv_count);
|
||||
Shader *toon = gfx->newMeshShaderFromSpv(vert, frag);
|
||||
if (!toon || !toon->gpuHandle)
|
||||
throw eve::Exception("failed to create toon mesh shader");
|
||||
toon->declareFloat("bands");
|
||||
toon->declareFloat("rimPower");
|
||||
toon->declareFloat("rimStrength");
|
||||
toon->declareFloat("posterize");
|
||||
toon->sendFloat("bands", 4.f);
|
||||
toon->sendFloat("rimPower", 2.8f);
|
||||
toon->sendFloat("rimStrength", 0.55f);
|
||||
toon->sendFloat("posterize", 6.f);
|
||||
return toon;
|
||||
});
|
||||
delete md;
|
||||
}
|
||||
|
||||
TEST_CASE("model3d.render.sofaObjMtl") {
|
||||
const std::string objPath = pathBesideThisSource("sofa.obj");
|
||||
const std::string mtlPath = pathBesideThisSource("sofa.mtl");
|
||||
REQUIRE(!readBinaryFile(objPath).empty());
|
||||
REQUIRE(!readBinaryFile(mtlPath).empty());
|
||||
|
||||
std::string testDir = pathBesideThisSource("");
|
||||
if (!testDir.empty() && (testDir.back() == '/' || testDir.back() == '\\'))
|
||||
testDir.pop_back();
|
||||
|
||||
auto *fs = eve::filesystem::Filesystem::create();
|
||||
REQUIRE(fs->setIdentity("ev_ut_model3d_sofa_render", true));
|
||||
REQUIRE(fs->setupWriteDirectory());
|
||||
fs->allowMountingForPath(testDir);
|
||||
REQUIRE(fs->mount(testDir, "", false));
|
||||
|
||||
auto *mod = eve::model3d::Model3D::create();
|
||||
auto *md = mod->newModelDataFromFile("sofa.obj");
|
||||
REQUIRE(md != nullptr);
|
||||
renderModelSmoke(md, "model3d_sofa.png");
|
||||
delete md;
|
||||
|
||||
CHECK(fs->unmount(testDir));
|
||||
}
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
#include "zeroerr/assert.h"
|
||||
#include "zeroerr/unittest.h"
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
#include "common/Exception.h"
|
||||
#include "filesystem/Filesystem.h"
|
||||
#include "graphics/Graphics.h"
|
||||
#include "graphics/RenderSystem.h"
|
||||
#include "graphics/Shader.h"
|
||||
#include "graphics/shaders/custom2d_frag_spv.inc"
|
||||
#include "window/Window.h"
|
||||
|
||||
using namespace eve::graphics;
|
||||
|
||||
TEST_CASE("graphics.Shader.declareAndSendUniforms") {
|
||||
Shader sh;
|
||||
CHECK_EQ(sh.declareFloat("time"), 0);
|
||||
CHECK_EQ(sh.declareVec4("tint"), 1);
|
||||
CHECK_EQ(sh.usedFloats(), 5);
|
||||
CHECK(sh.hasUniform("time"));
|
||||
CHECK(sh.hasUniform("tint"));
|
||||
|
||||
sh.sendFloat("time", 1.5f);
|
||||
sh.sendVec4("tint", 0.2f, 0.4f, 0.6f, 1.f);
|
||||
|
||||
float t = 0.f;
|
||||
REQUIRE_EQ(sh.getFromVar("time", &t, sizeof(t)), int(sizeof(t)));
|
||||
CHECK(std::abs(t - 1.5f) < 1e-5f);
|
||||
|
||||
float tint[4] = {};
|
||||
REQUIRE_EQ(sh.getFromVar("tint", tint, sizeof(tint)), int(sizeof(tint)));
|
||||
CHECK(std::abs(tint[0] - 0.2f) < 1e-5f);
|
||||
CHECK(std::abs(tint[3] - 1.f) < 1e-5f);
|
||||
|
||||
bool threw = false;
|
||||
try {
|
||||
sh.sendFloat("missing", 1.f);
|
||||
} catch (const eve::Exception &) {
|
||||
threw = true;
|
||||
}
|
||||
CHECK(threw);
|
||||
|
||||
threw = false;
|
||||
try {
|
||||
sh.declareFloat("time"); // same size OK
|
||||
sh.declareVec2("time"); // different size → error
|
||||
} catch (const eve::Exception &) {
|
||||
threw = true;
|
||||
}
|
||||
CHECK(threw);
|
||||
}
|
||||
|
||||
TEST_CASE("graphics.Shader.customSpvOnSprite") {
|
||||
auto *win = eve::window::Window::create();
|
||||
auto *gfx = Graphics::create();
|
||||
REQUIRE(win != nullptr);
|
||||
REQUIRE(gfx != nullptr);
|
||||
|
||||
win->setGraphics(gfx);
|
||||
eve::window::WindowSettings s;
|
||||
s.width = 320;
|
||||
s.height = 240;
|
||||
s.centered = true;
|
||||
REQUIRE(win->setWindowSettings(s));
|
||||
|
||||
std::vector<uint32_t> frag(custom2d_frag_spv, custom2d_frag_spv + custom2d_frag_spv_count);
|
||||
Shader *shader = gfx->newShaderFromSpv({}, frag);
|
||||
REQUIRE(shader != nullptr);
|
||||
REQUIRE(shader->gpuHandle != nullptr);
|
||||
CHECK_EQ(shader->declareFloat("factor"), 0);
|
||||
shader->sendFloat("factor", 0.5f);
|
||||
|
||||
// White 2x2 texture
|
||||
const uint8_t px[16] = {255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255};
|
||||
Texture *tex = gfx->newTexture(2, 2, px);
|
||||
REQUIRE(tex != nullptr);
|
||||
|
||||
auto *ent = Renderable2D::create();
|
||||
ent->transform()->x = 40;
|
||||
ent->transform()->y = 40;
|
||||
ent->sprite()->width = 80;
|
||||
ent->sprite()->height = 80;
|
||||
ent->sprite()->texture = tex;
|
||||
ent->sprite()->shader = shader;
|
||||
ent->sprite()->visible = true;
|
||||
|
||||
gfx->setScreenReadbackEnabled(true);
|
||||
gfx->setBackgroundColor(Color(0.f, 0.f, 0.f, 1.f));
|
||||
|
||||
// Warm up a couple frames (swapchain acquire can soft-fail once).
|
||||
for (int i = 0; i < 3; ++i) RenderSystem::render(*gfx);
|
||||
|
||||
Color c = gfx->getPixel(80, 80);
|
||||
// factor 0.5 on white → ~0.5 RGB (allow tolerance for sRGB/swapchain format)
|
||||
CHECK_GT(c.r, 0.15f);
|
||||
CHECK_LT(c.r, 0.85f);
|
||||
CHECK(std::abs(c.r - c.g) < 0.15f);
|
||||
}
|
||||
|
||||
#if !defined(_WIN32)
|
||||
TEST_CASE("graphics.Shader.newShaderGlslc") {
|
||||
auto *win = eve::window::Window::create();
|
||||
auto *gfx = Graphics::create();
|
||||
REQUIRE(win != nullptr);
|
||||
REQUIRE(gfx != nullptr);
|
||||
win->setGraphics(gfx);
|
||||
eve::window::WindowSettings s;
|
||||
s.width = 160;
|
||||
s.height = 120;
|
||||
s.centered = true;
|
||||
if (!win->setWindowSettings(s)) return;
|
||||
|
||||
const char *frag = R"(#version 450
|
||||
layout(location = 0) in vec4 fragColor;
|
||||
layout(location = 1) in vec2 fragUV;
|
||||
layout(location = 0) out vec4 outColor;
|
||||
layout(binding = 0) uniform sampler2D MainTex;
|
||||
layout(push_constant) uniform Externals { float data[32]; } u;
|
||||
void main() {
|
||||
outColor = texture(MainTex, fragUV) * fragColor * u.data[0];
|
||||
}
|
||||
)";
|
||||
try {
|
||||
Shader *sh = gfx->newShader(frag);
|
||||
REQUIRE(sh != nullptr);
|
||||
sh->declareFloat("factor");
|
||||
sh->sendFloat("factor", 1.f);
|
||||
gfx->setShader(sh);
|
||||
CHECK(gfx->getShader() == sh);
|
||||
gfx->setShader();
|
||||
CHECK(gfx->getShader() == nullptr);
|
||||
} catch (const eve::Exception &e) {
|
||||
// glslc missing on CI hosts is acceptable — mark as soft skip
|
||||
const char *msg = e.what();
|
||||
bool missing = msg && (std::strstr(msg, "glslc") != nullptr);
|
||||
CHECK(missing);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,52 @@
|
||||
# Blender MTL File: 'sofa01.blend'
|
||||
# Material Count: 5
|
||||
|
||||
newmtl Material.002
|
||||
Ns 225.000000
|
||||
Ka 1.000000 1.000000 1.000000
|
||||
Kd 0.390722 0.186268 0.428691
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.0 0.0 0.0
|
||||
Ni 1.450000
|
||||
d 1.000000
|
||||
illum 2
|
||||
|
||||
newmtl Material.003
|
||||
Ns 225.000000
|
||||
Ka 1.000000 1.000000 1.000000
|
||||
Kd 0.428691 0.132868 0.114435
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.0 0.0 0.0
|
||||
Ni 1.450000
|
||||
d 1.000000
|
||||
illum 2
|
||||
|
||||
newmtl Material.004
|
||||
Ns 225.000000
|
||||
Ka 1.000000 1.000000 1.000000
|
||||
Kd 0.428691 0.205878 0.063647
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.0 0.0 0.0
|
||||
Ni 1.450000
|
||||
d 1.000000
|
||||
illum 2
|
||||
|
||||
newmtl m_sofa
|
||||
Ns 9.000004
|
||||
Ka 1.000000 1.000000 1.000000
|
||||
Kd 0.661863 0.556516 0.421331
|
||||
Ks 0.000000 0.000000 0.000000
|
||||
Ke 0.0 0.0 0.0
|
||||
Ni 1.450000
|
||||
d 1.000000
|
||||
illum 1
|
||||
|
||||
newmtl m_sofa_bottom
|
||||
Ns 225.000000
|
||||
Ka 1.000000 1.000000 1.000000
|
||||
Kd 0.132863 0.112958 0.087189
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.0 0.0 0.0
|
||||
Ni 1.450000
|
||||
d 1.000000
|
||||
illum 2
|
||||
+11058
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user