Compare commits
3 Commits
v1.64.1
...
mhoff/gltf
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fdd710c3b0 | ||
|
|
208e936dab | ||
|
|
bbea03224d |
@@ -287,6 +287,9 @@ set(FILAMENT ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
# Where our tools are
|
||||
set(TOOLS ${CMAKE_CURRENT_SOURCE_DIR}/tools)
|
||||
|
||||
# Where our tools are
|
||||
set(TESTS ${CMAKE_CURRENT_SOURCE_DIR}/test)
|
||||
|
||||
# ==================================================================================================
|
||||
# Compiler check
|
||||
# ==================================================================================================
|
||||
@@ -896,6 +899,8 @@ if (IS_HOST_PLATFORM)
|
||||
add_subdirectory(${TOOLS}/roughness-prefilter)
|
||||
add_subdirectory(${TOOLS}/specular-color)
|
||||
add_subdirectory(${TOOLS}/uberz)
|
||||
|
||||
add_subdirectory(${TESTS}/gltf-comparison)
|
||||
endif()
|
||||
|
||||
# Generate exported executables for cross-compiled builds (Android, WebGL, and iOS)
|
||||
|
||||
@@ -21,9 +21,9 @@
|
||||
#include "utils/Hash.h"
|
||||
#include <fstream>
|
||||
|
||||
#include "BackendTest.h"
|
||||
//#include "BackendTest.h"
|
||||
#include "backend/PixelBufferDescriptor.h"
|
||||
#include "private/backend/DriverApi.h"
|
||||
//#include "private/backend/DriverApi.h"
|
||||
|
||||
#ifndef FILAMENT_IOS
|
||||
|
||||
|
||||
150
test/gltf-comparison/CMakeLists.txt
Normal file
@@ -0,0 +1,150 @@
|
||||
cmake_minimum_required(VERSION 3.19)
|
||||
project(filament C ASM)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
set(ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../..)
|
||||
set(GENERATION_ROOT ${CMAKE_CURRENT_BINARY_DIR})
|
||||
set(RESOURCE_DIR "${GENERATION_ROOT}/generated/resources")
|
||||
set(MATERIAL_DIR "${GENERATION_ROOT}/generated/material")
|
||||
set(TEXTURE_DIR "${GENERATION_ROOT}/generated/texture")
|
||||
set(RESOURCE_BINS)
|
||||
|
||||
# Materials
|
||||
set(MATERIAL_SRCS
|
||||
${ROOT_DIR}/samples/materials/aiDefaultMat.mat
|
||||
${ROOT_DIR}/samples/materials/bakedColor.mat
|
||||
${ROOT_DIR}/samples/materials/bakedTexture.mat
|
||||
${ROOT_DIR}/samples/materials/aoPreview.mat
|
||||
${ROOT_DIR}/samples/materials/arrayTexture.mat
|
||||
${ROOT_DIR}/samples/materials/groundShadow.mat
|
||||
${ROOT_DIR}/samples/materials/heightfield.mat
|
||||
${ROOT_DIR}/samples/materials/image.mat
|
||||
${ROOT_DIR}/samples/materials/mirror.mat
|
||||
${ROOT_DIR}/samples/materials/overdraw.mat
|
||||
${ROOT_DIR}/samples/materials/sandboxCloth.mat
|
||||
${ROOT_DIR}/samples/materials/sandboxLit.mat
|
||||
${ROOT_DIR}/samples/materials/sandboxLitFade.mat
|
||||
${ROOT_DIR}/samples/materials/sandboxLitTransparent.mat
|
||||
${ROOT_DIR}/samples/materials/sandboxLitThinRefraction.mat
|
||||
${ROOT_DIR}/samples/materials/sandboxLitThinRefractionSsr.mat
|
||||
${ROOT_DIR}/samples/materials/sandboxLitSolidRefraction.mat
|
||||
${ROOT_DIR}/samples/materials/sandboxLitSolidRefractionSsr.mat
|
||||
${ROOT_DIR}/samples/materials/sandboxSpecGloss.mat
|
||||
${ROOT_DIR}/samples/materials/sandboxSubsurface.mat
|
||||
${ROOT_DIR}/samples/materials/sandboxUnlit.mat
|
||||
${ROOT_DIR}/samples/materials/texturedLit.mat
|
||||
)
|
||||
|
||||
# Tint does not support setting gl_PointSize, disable the relevant sample if using WebGPU
|
||||
# https://github.com/gpuweb/gpuweb/issues/1190
|
||||
if (NOT FILAMENT_SUPPORTS_WEBGPU)
|
||||
set(MATERIAL_SRCS ${MATERIAL_SRCS} materials/pointSprites.mat)
|
||||
endif ()
|
||||
|
||||
if (CMAKE_CROSSCOMPILING)
|
||||
include(${IMPORT_EXECUTABLES})
|
||||
endif()
|
||||
|
||||
file(MAKE_DIRECTORY ${MATERIAL_DIR})
|
||||
|
||||
set (MATC_FLAGS ${MATC_BASE_FLAGS})
|
||||
if (FILAMENT_SAMPLES_STEREO_TYPE STREQUAL "instanced")
|
||||
set (MATC_FLAGS ${MATC_FLAGS} -PstereoscopicType=instanced)
|
||||
add_definitions(-DFILAMENT_SAMPLES_STEREO_TYPE_INSTANCED)
|
||||
elseif (FILAMENT_SAMPLES_STEREO_TYPE STREQUAL "multiview")
|
||||
set (MATC_FLAGS ${MATC_FLAGS} -PstereoscopicType=multiview)
|
||||
add_definitions(-DFILAMENT_SAMPLES_STEREO_TYPE_MULTIVIEW)
|
||||
endif ()
|
||||
|
||||
foreach (mat_src ${MATERIAL_SRCS})
|
||||
get_filename_component(localname "${mat_src}" NAME_WE)
|
||||
get_filename_component(fullname "${mat_src}" ABSOLUTE)
|
||||
set(output_path "${MATERIAL_DIR}/${localname}.filamat")
|
||||
add_custom_command(
|
||||
OUTPUT ${output_path}
|
||||
COMMAND matc ${MATC_FLAGS} -o ${output_path} ${fullname}
|
||||
MAIN_DEPENDENCY ${mat_src}
|
||||
DEPENDS matc
|
||||
COMMENT "Compiling material ${mat_src} to ${output_path}"
|
||||
)
|
||||
list(APPEND RESOURCE_BINS ${output_path})
|
||||
endforeach()
|
||||
|
||||
# Resources
|
||||
file(MAKE_DIRECTORY ${RESOURCE_DIR})
|
||||
|
||||
get_resgen_vars(${RESOURCE_DIR} resources)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${RESGEN_OUTPUTS}
|
||||
COMMAND resgen ${RESGEN_FLAGS} ${RESOURCE_BINS}
|
||||
DEPENDS resgen ${RESOURCE_BINS}
|
||||
COMMENT "Aggregating resources"
|
||||
)
|
||||
|
||||
if (DEFINED RESGEN_SOURCE_FLAGS)
|
||||
set_source_files_properties(${RESGEN_SOURCE} PROPERTIES COMPILE_FLAGS ${RESGEN_SOURCE_FLAGS})
|
||||
endif()
|
||||
|
||||
add_library(gltf-resources ${DUMMY_SRC})
|
||||
set_target_properties(gltf-resources PROPERTIES FOLDER Samples/Resources)
|
||||
|
||||
set(GLTF_DEMO_RESOURCES
|
||||
${ROOT_DIR}/third_party/models/DamagedHelmet/DamagedHelmet.glb
|
||||
${MATERIAL_DIR}/groundShadow.filamat
|
||||
${MATERIAL_DIR}/overdraw.filamat
|
||||
)
|
||||
|
||||
get_resgen_vars(${RESOURCE_DIR} gltf_demo)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${RESGEN_OUTPUTS}
|
||||
COMMAND resgen ${RESGEN_FLAGS} ${GLTF_DEMO_RESOURCES}
|
||||
DEPENDS resgen ${GLTF_DEMO_RESOURCES}
|
||||
)
|
||||
|
||||
if (DEFINED RESGEN_SOURCE_FLAGS)
|
||||
set_source_files_properties(${RESGEN_SOURCE} PROPERTIES COMPILE_FLAGS ${RESGEN_SOURCE_FLAGS})
|
||||
endif()
|
||||
|
||||
target_sources(gltf-resources PRIVATE ${RESGEN_SOURCE})
|
||||
|
||||
|
||||
# CMake fails to invoke ar on Windows unless there is at least one C/C++ file in the library.
|
||||
set(DUMMY_SRC "${RESOURCE_DIR}/dummy.c")
|
||||
add_custom_command(OUTPUT ${DUMMY_SRC} COMMAND echo "//" > ${DUMMY_SRC})
|
||||
|
||||
# Test binary
|
||||
set(GLTF_COMPARISON_SOURCES
|
||||
src/test_CompareGLTF.cpp
|
||||
src/GLTFViewer.cpp
|
||||
src/ImageExpectations.cpp
|
||||
src/RunOnMain.cpp
|
||||
src/CrossThreadTask.cpp
|
||||
src/main.cpp
|
||||
)
|
||||
|
||||
file(COPY glTF_cases DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
|
||||
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/images/actual_images)
|
||||
file(COPY expected_images DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/images)
|
||||
|
||||
enable_testing()
|
||||
|
||||
add_executable(gltf_comparison ${GLTF_COMPARISON_SOURCES})
|
||||
target_include_directories(gltf_comparison PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||
target_link_libraries(gltf_comparison
|
||||
gtest
|
||||
filamentapp
|
||||
absl::str_format
|
||||
uberarchive
|
||||
gltf-resources
|
||||
gltfio
|
||||
viewer
|
||||
)
|
||||
|
||||
|
||||
|
||||
include(GoogleTest)
|
||||
gtest_discover_tests(gltf_comparison)
|
||||
BIN
test/gltf-comparison/expected_images/GLTF_Box.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
test/gltf-comparison/expected_images/GLTF_BoxTextured.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
15
test/gltf-comparison/glTF_cases/Models/Box/LICENSE.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# LICENSE file for the model: Box
|
||||
|
||||
All files in this directory tree are licensed as indicated below.
|
||||
|
||||
* All files directly associated with the model including all text, image and binary files:
|
||||
|
||||
* [CC BY 4.0 International]("https://creativecommons.org/licenses/by/4.0/legalcode") [SPDX license identifier: "CC-BY-4.0"]
|
||||
|
||||
* This file and all other metadocumentation files including "metadata.json":
|
||||
|
||||
* [Creative Commons Attribtution 4.0 International]("https://creativecommons.org/licenses/by/4.0/legalcode") [SPDX license identifier: "CC-BY-4.0"]
|
||||
|
||||
Full license text of these licenses are available at the links above
|
||||
|
||||
#### Generated by modelmetadata
|
||||
@@ -0,0 +1,7 @@
|
||||
## Screenshot
|
||||
|
||||

|
||||
|
||||
## Description
|
||||
|
||||
Simple cube model.
|
||||
31
test/gltf-comparison/glTF_cases/Models/Box/README.md
Normal file
@@ -0,0 +1,31 @@
|
||||
# Box
|
||||
|
||||
## Tags
|
||||
|
||||
[core](../../Models-core.md), [testing](../../Models-testing.md)
|
||||
|
||||
## Summary
|
||||
|
||||
One mesh and one material. Start with this.
|
||||
|
||||
## Operations
|
||||
|
||||
* [Display](https://github.khronos.org/glTF-Sample-Viewer-Release/?model=https://raw.GithubUserContent.com/KhronosGroup/glTF-Sample-Assets/main/./Models/Box/glTF-Binary/Box.glb) in SampleViewer
|
||||
* [Download GLB](https://raw.GithubUserContent.com/KhronosGroup/glTF-Sample-Assets/main/./Models/Box/glTF-Binary/Box.glb)
|
||||
* [Model Directory](./)
|
||||
|
||||
## Screenshot
|
||||
|
||||

|
||||
|
||||
## Description
|
||||
|
||||
Simple cube model.
|
||||
|
||||
## Legal
|
||||
|
||||
© 2017, Cesium. [CC BY 4.0 International](https://creativecommons.org/licenses/by/4.0/legalcode)
|
||||
|
||||
- Cesium for Everything
|
||||
|
||||
#### Assembled by modelmetadata
|
||||
BIN
test/gltf-comparison/glTF_cases/Models/Box/glTF-Binary/Box.glb
Normal file
BIN
test/gltf-comparison/glTF_cases/Models/Box/glTF-Draco/Box.bin
Normal file
152
test/gltf-comparison/glTF_cases/Models/Box/glTF-Draco/Box.gltf
Normal file
@@ -0,0 +1,152 @@
|
||||
{
|
||||
"asset": {
|
||||
"generator": "COLLADA2GLTF",
|
||||
"version": "2.0"
|
||||
},
|
||||
"scene": 0,
|
||||
"scenes": [
|
||||
{
|
||||
"nodes": [
|
||||
0
|
||||
]
|
||||
}
|
||||
],
|
||||
"nodes": [
|
||||
{
|
||||
"children": [
|
||||
1
|
||||
],
|
||||
"matrix": [
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
-1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
"mesh": 0
|
||||
}
|
||||
],
|
||||
"meshes": [
|
||||
{
|
||||
"primitives": [
|
||||
{
|
||||
"attributes": {
|
||||
"NORMAL": 1,
|
||||
"POSITION": 2
|
||||
},
|
||||
"indices": 0,
|
||||
"mode": 4,
|
||||
"material": 0,
|
||||
"extensions": {
|
||||
"KHR_draco_mesh_compression": {
|
||||
"bufferView": 0,
|
||||
"attributes": {
|
||||
"NORMAL": 0,
|
||||
"POSITION": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"name": "Mesh"
|
||||
}
|
||||
],
|
||||
"accessors": [
|
||||
{
|
||||
"componentType": 5123,
|
||||
"count": 36,
|
||||
"max": [
|
||||
23
|
||||
],
|
||||
"min": [
|
||||
0
|
||||
],
|
||||
"type": "SCALAR"
|
||||
},
|
||||
{
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"max": [
|
||||
1.007843137254902,
|
||||
1.007843137254902,
|
||||
1.007843137254902
|
||||
],
|
||||
"min": [
|
||||
-1.007843137254902,
|
||||
-1.007843137254902,
|
||||
-1.007843137254902
|
||||
],
|
||||
"type": "VEC3"
|
||||
},
|
||||
{
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"max": [
|
||||
0.5004885197850513,
|
||||
0.5004885197850513,
|
||||
0.5004885197850513
|
||||
],
|
||||
"min": [
|
||||
-0.5004885197850513,
|
||||
-0.5004885197850513,
|
||||
-0.5004885197850513
|
||||
],
|
||||
"type": "VEC3"
|
||||
}
|
||||
],
|
||||
"materials": [
|
||||
{
|
||||
"pbrMetallicRoughness": {
|
||||
"baseColorFactor": [
|
||||
0.800000011920929,
|
||||
0,
|
||||
0,
|
||||
1
|
||||
],
|
||||
"metallicFactor": 0,
|
||||
"roughnessFactor": 1
|
||||
},
|
||||
"name": "Red",
|
||||
"emissiveFactor": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"alphaMode": "OPAQUE",
|
||||
"doubleSided": false
|
||||
}
|
||||
],
|
||||
"bufferViews": [
|
||||
{
|
||||
"buffer": 0,
|
||||
"byteOffset": 0,
|
||||
"byteLength": 118
|
||||
}
|
||||
],
|
||||
"buffers": [
|
||||
{
|
||||
"name": "Box",
|
||||
"byteLength": 120,
|
||||
"uri": "Box.bin"
|
||||
}
|
||||
],
|
||||
"extensionsRequired": [
|
||||
"KHR_draco_mesh_compression"
|
||||
],
|
||||
"extensionsUsed": [
|
||||
"KHR_draco_mesh_compression"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
{
|
||||
"asset": {
|
||||
"generator": "COLLADA2GLTF",
|
||||
"version": "2.0"
|
||||
},
|
||||
"scene": 0,
|
||||
"scenes": [
|
||||
{
|
||||
"nodes": [
|
||||
0
|
||||
]
|
||||
}
|
||||
],
|
||||
"nodes": [
|
||||
{
|
||||
"children": [
|
||||
1
|
||||
],
|
||||
"matrix": [
|
||||
1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
-1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
1.0
|
||||
]
|
||||
},
|
||||
{
|
||||
"mesh": 0
|
||||
}
|
||||
],
|
||||
"meshes": [
|
||||
{
|
||||
"primitives": [
|
||||
{
|
||||
"attributes": {
|
||||
"NORMAL": 1,
|
||||
"POSITION": 2
|
||||
},
|
||||
"indices": 0,
|
||||
"mode": 4,
|
||||
"material": 0
|
||||
}
|
||||
],
|
||||
"name": "Mesh"
|
||||
}
|
||||
],
|
||||
"accessors": [
|
||||
{
|
||||
"bufferView": 0,
|
||||
"byteOffset": 0,
|
||||
"componentType": 5123,
|
||||
"count": 36,
|
||||
"max": [
|
||||
23
|
||||
],
|
||||
"min": [
|
||||
0
|
||||
],
|
||||
"type": "SCALAR"
|
||||
},
|
||||
{
|
||||
"bufferView": 1,
|
||||
"byteOffset": 0,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"max": [
|
||||
1.0,
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
"min": [
|
||||
-1.0,
|
||||
-1.0,
|
||||
-1.0
|
||||
],
|
||||
"type": "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView": 1,
|
||||
"byteOffset": 288,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"max": [
|
||||
0.5,
|
||||
0.5,
|
||||
0.5
|
||||
],
|
||||
"min": [
|
||||
-0.5,
|
||||
-0.5,
|
||||
-0.5
|
||||
],
|
||||
"type": "VEC3"
|
||||
}
|
||||
],
|
||||
"materials": [
|
||||
{
|
||||
"pbrMetallicRoughness": {
|
||||
"baseColorFactor": [
|
||||
0.800000011920929,
|
||||
0.0,
|
||||
0.0,
|
||||
1.0
|
||||
],
|
||||
"metallicFactor": 0.0
|
||||
},
|
||||
"name": "Red"
|
||||
}
|
||||
],
|
||||
"bufferViews": [
|
||||
{
|
||||
"buffer": 0,
|
||||
"byteOffset": 576,
|
||||
"byteLength": 72,
|
||||
"target": 34963
|
||||
},
|
||||
{
|
||||
"buffer": 0,
|
||||
"byteOffset": 0,
|
||||
"byteLength": 576,
|
||||
"byteStride": 12,
|
||||
"target": 34962
|
||||
}
|
||||
],
|
||||
"buffers": [
|
||||
{
|
||||
"byteLength": 648,
|
||||
"uri": "data:application/octet-stream;base64,AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAvwAAAL8AAAA/AAAAPwAAAL8AAAA/AAAAvwAAAD8AAAA/AAAAPwAAAD8AAAA/AAAAPwAAAL8AAAA/AAAAvwAAAL8AAAA/AAAAPwAAAL8AAAC/AAAAvwAAAL8AAAC/AAAAPwAAAD8AAAA/AAAAPwAAAL8AAAA/AAAAPwAAAD8AAAC/AAAAPwAAAL8AAAC/AAAAvwAAAD8AAAA/AAAAPwAAAD8AAAA/AAAAvwAAAD8AAAC/AAAAPwAAAD8AAAC/AAAAvwAAAL8AAAA/AAAAvwAAAD8AAAA/AAAAvwAAAL8AAAC/AAAAvwAAAD8AAAC/AAAAvwAAAL8AAAC/AAAAvwAAAD8AAAC/AAAAPwAAAL8AAAC/AAAAPwAAAD8AAAC/AAABAAIAAwACAAEABAAFAAYABwAGAAUACAAJAAoACwAKAAkADAANAA4ADwAOAA0AEAARABIAEwASABEAFAAVABYAFwAWABUA"
|
||||
}
|
||||
]
|
||||
}
|
||||
142
test/gltf-comparison/glTF_cases/Models/Box/glTF/Box.gltf
Normal file
@@ -0,0 +1,142 @@
|
||||
{
|
||||
"asset": {
|
||||
"generator": "COLLADA2GLTF",
|
||||
"version": "2.0"
|
||||
},
|
||||
"scene": 0,
|
||||
"scenes": [
|
||||
{
|
||||
"nodes": [
|
||||
0
|
||||
]
|
||||
}
|
||||
],
|
||||
"nodes": [
|
||||
{
|
||||
"children": [
|
||||
1
|
||||
],
|
||||
"matrix": [
|
||||
1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
-1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
1.0
|
||||
]
|
||||
},
|
||||
{
|
||||
"mesh": 0
|
||||
}
|
||||
],
|
||||
"meshes": [
|
||||
{
|
||||
"primitives": [
|
||||
{
|
||||
"attributes": {
|
||||
"NORMAL": 1,
|
||||
"POSITION": 2
|
||||
},
|
||||
"indices": 0,
|
||||
"mode": 4,
|
||||
"material": 0
|
||||
}
|
||||
],
|
||||
"name": "Mesh"
|
||||
}
|
||||
],
|
||||
"accessors": [
|
||||
{
|
||||
"bufferView": 0,
|
||||
"byteOffset": 0,
|
||||
"componentType": 5123,
|
||||
"count": 36,
|
||||
"max": [
|
||||
23
|
||||
],
|
||||
"min": [
|
||||
0
|
||||
],
|
||||
"type": "SCALAR"
|
||||
},
|
||||
{
|
||||
"bufferView": 1,
|
||||
"byteOffset": 0,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"max": [
|
||||
1.0,
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
"min": [
|
||||
-1.0,
|
||||
-1.0,
|
||||
-1.0
|
||||
],
|
||||
"type": "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView": 1,
|
||||
"byteOffset": 288,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"max": [
|
||||
0.5,
|
||||
0.5,
|
||||
0.5
|
||||
],
|
||||
"min": [
|
||||
-0.5,
|
||||
-0.5,
|
||||
-0.5
|
||||
],
|
||||
"type": "VEC3"
|
||||
}
|
||||
],
|
||||
"materials": [
|
||||
{
|
||||
"pbrMetallicRoughness": {
|
||||
"baseColorFactor": [
|
||||
0.800000011920929,
|
||||
0.0,
|
||||
0.0,
|
||||
1.0
|
||||
],
|
||||
"metallicFactor": 0.0
|
||||
},
|
||||
"name": "Red"
|
||||
}
|
||||
],
|
||||
"bufferViews": [
|
||||
{
|
||||
"buffer": 0,
|
||||
"byteOffset": 576,
|
||||
"byteLength": 72,
|
||||
"target": 34963
|
||||
},
|
||||
{
|
||||
"buffer": 0,
|
||||
"byteOffset": 0,
|
||||
"byteLength": 576,
|
||||
"byteStride": 12,
|
||||
"target": 34962
|
||||
}
|
||||
],
|
||||
"buffers": [
|
||||
{
|
||||
"byteLength": 648,
|
||||
"uri": "Box0.bin"
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
test/gltf-comparison/glTF_cases/Models/Box/glTF/Box0.bin
Normal file
25
test/gltf-comparison/glTF_cases/Models/Box/metadata.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"version": 2,
|
||||
"legal": [
|
||||
{
|
||||
"license": "CC-BY 4.0",
|
||||
"licenseUrl": "https://creativecommons.org/licenses/by/4.0/legalcode",
|
||||
"artist": "Cesium",
|
||||
"year": "2017",
|
||||
"owner": "Cesium",
|
||||
"what": "Everything",
|
||||
"text": "CC BY 4.0 International",
|
||||
"spdx": "CC-BY-4.0",
|
||||
"icon": "https://licensebuttons.net/l/by/3.0/88x31.png"
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"core",
|
||||
"testing"
|
||||
],
|
||||
"screenshot": "screenshot/screenshot.png",
|
||||
"name": "Box",
|
||||
"path": "./Models/Box",
|
||||
"summary": "One mesh and one material. Start with this.",
|
||||
"createReadme": true
|
||||
}
|
||||
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
@@ -0,0 +1,17 @@
|
||||
# LICENSE file for the model: Box Textured
|
||||
|
||||
All files in this directory tree are licensed as indicated below.
|
||||
|
||||
* All files directly associated with the model including all text, image and binary files:
|
||||
|
||||
* [CC-BY 4.0 International with Trademark Limitations]("") [SPDX license identifier: "LicenseRef-CC-BY-TM"]
|
||||
|
||||
* [Cesium Trademark or Logo]("") [SPDX license identifier: "LicenseRef-LegalMark-Cesium"]
|
||||
|
||||
* This file and all other metadocumentation files including "metadata.json":
|
||||
|
||||
* [Creative Commons Attribtution 4.0 International]("https://creativecommons.org/licenses/by/4.0/legalcode") [SPDX license identifier: "CC-BY-4.0"]
|
||||
|
||||
Full license text of these licenses are available at the links above
|
||||
|
||||
#### Generated by modelmetadata
|
||||
@@ -0,0 +1,7 @@
|
||||
## Screenshot
|
||||
|
||||

|
||||
|
||||
## Description
|
||||
|
||||
Box with a power-of-2 texture, using the Cesium logo.
|
||||
35
test/gltf-comparison/glTF_cases/Models/BoxTextured/README.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# Box Textured
|
||||
|
||||
## Tags
|
||||
|
||||
[core](../../Models-core.md), [issues](../../Models-issues.md), [testing](../../Models-testing.md)
|
||||
|
||||
## Summary
|
||||
|
||||
Box with a power-of-2 texture. [Issues: non-Khronos mark]
|
||||
|
||||
## Operations
|
||||
|
||||
* [Display](https://github.khronos.org/glTF-Sample-Viewer-Release/?model=https://raw.GithubUserContent.com/KhronosGroup/glTF-Sample-Assets/main/./Models/BoxTextured/glTF-Binary/BoxTextured.glb) in SampleViewer
|
||||
* [Download GLB](https://raw.GithubUserContent.com/KhronosGroup/glTF-Sample-Assets/main/./Models/BoxTextured/glTF-Binary/BoxTextured.glb)
|
||||
* [Model Directory](./)
|
||||
|
||||
## Screenshot
|
||||
|
||||

|
||||
|
||||
## Description
|
||||
|
||||
Box with a power-of-2 texture, using the Cesium logo.
|
||||
|
||||
## Legal
|
||||
|
||||
© 2017, Cesium. [CC-BY 4.0 International with Trademark Limitations]()
|
||||
|
||||
- Cesium for Everything
|
||||
|
||||
© 2015, Cesium. [Cesium Trademark or Logo]()
|
||||
|
||||
- Non-copyrightable logo for Cesium logo
|
||||
|
||||
#### Assembled by modelmetadata
|
||||
@@ -0,0 +1,181 @@
|
||||
{
|
||||
"asset": {
|
||||
"generator": "COLLADA2GLTF",
|
||||
"version": "2.0"
|
||||
},
|
||||
"scene": 0,
|
||||
"scenes": [
|
||||
{
|
||||
"nodes": [
|
||||
0
|
||||
]
|
||||
}
|
||||
],
|
||||
"nodes": [
|
||||
{
|
||||
"children": [
|
||||
1
|
||||
],
|
||||
"matrix": [
|
||||
1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
-1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
1.0
|
||||
]
|
||||
},
|
||||
{
|
||||
"mesh": 0
|
||||
}
|
||||
],
|
||||
"meshes": [
|
||||
{
|
||||
"primitives": [
|
||||
{
|
||||
"attributes": {
|
||||
"NORMAL": 1,
|
||||
"POSITION": 2,
|
||||
"TEXCOORD_0": 3
|
||||
},
|
||||
"indices": 0,
|
||||
"mode": 4,
|
||||
"material": 0
|
||||
}
|
||||
],
|
||||
"name": "Mesh"
|
||||
}
|
||||
],
|
||||
"accessors": [
|
||||
{
|
||||
"bufferView": 0,
|
||||
"byteOffset": 0,
|
||||
"componentType": 5123,
|
||||
"count": 36,
|
||||
"max": [
|
||||
23
|
||||
],
|
||||
"min": [
|
||||
0
|
||||
],
|
||||
"type": "SCALAR"
|
||||
},
|
||||
{
|
||||
"bufferView": 1,
|
||||
"byteOffset": 0,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"max": [
|
||||
1.0,
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
"min": [
|
||||
-1.0,
|
||||
-1.0,
|
||||
-1.0
|
||||
],
|
||||
"type": "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView": 1,
|
||||
"byteOffset": 288,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"max": [
|
||||
0.5,
|
||||
0.5,
|
||||
0.5
|
||||
],
|
||||
"min": [
|
||||
-0.5,
|
||||
-0.5,
|
||||
-0.5
|
||||
],
|
||||
"type": "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView": 2,
|
||||
"byteOffset": 0,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"max": [
|
||||
6.0,
|
||||
1.0
|
||||
],
|
||||
"min": [
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"type": "VEC2"
|
||||
}
|
||||
],
|
||||
"materials": [
|
||||
{
|
||||
"pbrMetallicRoughness": {
|
||||
"baseColorTexture": {
|
||||
"index": 0
|
||||
},
|
||||
"metallicFactor": 0.0
|
||||
},
|
||||
"name": "Texture"
|
||||
}
|
||||
],
|
||||
"textures": [
|
||||
{
|
||||
"sampler": 0,
|
||||
"source": 0
|
||||
}
|
||||
],
|
||||
"images": [
|
||||
{
|
||||
"uri": "CesiumLogoFlat.png"
|
||||
}
|
||||
],
|
||||
"samplers": [
|
||||
{
|
||||
"magFilter": 9729,
|
||||
"minFilter": 9986,
|
||||
"wrapS": 10497,
|
||||
"wrapT": 10497
|
||||
}
|
||||
],
|
||||
"bufferViews": [
|
||||
{
|
||||
"buffer": 0,
|
||||
"byteOffset": 768,
|
||||
"byteLength": 72,
|
||||
"target": 34963
|
||||
},
|
||||
{
|
||||
"buffer": 0,
|
||||
"byteOffset": 0,
|
||||
"byteLength": 576,
|
||||
"byteStride": 12,
|
||||
"target": 34962
|
||||
},
|
||||
{
|
||||
"buffer": 0,
|
||||
"byteOffset": 576,
|
||||
"byteLength": 192,
|
||||
"byteStride": 8,
|
||||
"target": 34962
|
||||
}
|
||||
],
|
||||
"buffers": [
|
||||
{
|
||||
"byteLength": 840,
|
||||
"uri": "BoxTextured0.bin"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 4.2 KiB |
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"version": 2,
|
||||
"legal": [
|
||||
{
|
||||
"license": "LicenseRef-CC-BY-TM",
|
||||
"licenseUrl": "",
|
||||
"artist": "Cesium",
|
||||
"year": "2017",
|
||||
"owner": "Cesium",
|
||||
"what": "Everything",
|
||||
"text": "CC-BY 4.0 International with Trademark Limitations",
|
||||
"spdx": "LicenseRef-CC-BY-TM",
|
||||
"icon": "https://licensebuttons.net/l/by/3.0/88x31.png"
|
||||
},
|
||||
{
|
||||
"license": "LicenseRef-LegalMark-Cesium",
|
||||
"licenseUrl": "",
|
||||
"artist": "Non-copyrightable logo",
|
||||
"year": "2015",
|
||||
"owner": "Cesium",
|
||||
"what": "Cesium logo",
|
||||
"text": "Cesium Trademark or Logo",
|
||||
"spdx": "LicenseRef-LegalMark-Cesium",
|
||||
"icon": ""
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"core",
|
||||
"issues",
|
||||
"testing"
|
||||
],
|
||||
"screenshot": "screenshot/screenshot.png",
|
||||
"name": "Box Textured",
|
||||
"path": "./Models/BoxTextured",
|
||||
"summary": "Box with a power-of-2 texture. [Issues: non-Khronos mark]",
|
||||
"createReadme": true
|
||||
}
|
||||
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 11 KiB |
3
test/gltf-comparison/glTF_cases/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
Files here are copied directly from https://github.com/KhronosGroup/glTF-Sample-Assets
|
||||
|
||||
Each glTF model includes its own license and attribution in LICENSE.md and metadata.json.
|
||||
71
test/gltf-comparison/include/CrossThreadTask.h
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_CROSSTHREADTASK_H
|
||||
#define TNT_CROSSTHREADTASK_H
|
||||
|
||||
#include <optional>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
|
||||
template <typename T, typename... Args>
|
||||
class CrossThreadTask {
|
||||
public:
|
||||
bool queueTask(std::function<T> task);
|
||||
void runTask(Args...);
|
||||
bool isClosed() const;
|
||||
void setClosed();
|
||||
|
||||
private:
|
||||
std::mutex mMutex;
|
||||
std::optional<std::function<T>> mTask;
|
||||
std::atomic<bool> mClosed = false;
|
||||
};
|
||||
|
||||
template <typename T, typename... Args>
|
||||
bool CrossThreadTask<T, Args...>::queueTask(std::function<T> task) {
|
||||
std::unique_lock<std::mutex> lock(mMutex);
|
||||
if (mTask.has_value()) {
|
||||
return false;
|
||||
}
|
||||
mTask = task;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
void CrossThreadTask<T, Args...>::runTask(Args... args) {
|
||||
std::optional<std::function<T>> taskToRun;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mMutex);
|
||||
std::swap(mTask, taskToRun);
|
||||
}
|
||||
if (taskToRun.has_value()) {
|
||||
(*taskToRun)(args...);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
bool CrossThreadTask<T, Args...>::isClosed() const {
|
||||
return mClosed;
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
void CrossThreadTask<T, Args...>::setClosed() {
|
||||
mClosed = true;
|
||||
}
|
||||
|
||||
#endif // TNT_CROSSTHREADTASK_H
|
||||
88
test/gltf-comparison/include/GLTFViewer.h
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_GLTFVIEWER_H
|
||||
#define TNT_GLTFVIEWER_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <filamentapp/Config.h>
|
||||
#include <filamentapp/FilamentApp.h>
|
||||
#include <filamentapp/IBL.h>
|
||||
|
||||
#include <filament/Camera.h>
|
||||
#include <filament/Engine.h>
|
||||
#include <filament/IndexBuffer.h>
|
||||
#include <filament/Material.h>
|
||||
#include <filament/MaterialInstance.h>
|
||||
#include <filament/RenderableManager.h>
|
||||
#include <filament/Scene.h>
|
||||
#include <filament/Skybox.h>
|
||||
#include <filament/TransformManager.h>
|
||||
#include <filament/VertexBuffer.h>
|
||||
#include <filament/View.h>
|
||||
|
||||
#include <gltfio/AssetLoader.h>
|
||||
#include <gltfio/FilamentAsset.h>
|
||||
#include <gltfio/ResourceLoader.h>
|
||||
#include <gltfio/TextureProvider.h>
|
||||
|
||||
#include <viewer/ViewerGui.h>
|
||||
|
||||
#include <utils/EntityManager.h>
|
||||
|
||||
class GLTFViewer {
|
||||
public:
|
||||
void setFilename(std::string fileName);
|
||||
|
||||
void runApp(std::function<void(filament::Engine*, filament::View*, filament::Scene*,
|
||||
filament::Renderer*)>
|
||||
postRender);
|
||||
|
||||
private:
|
||||
void setup(filament::Engine* engine, filament::View* view, filament::Scene* scene);
|
||||
void cleanup(filament::Engine* engine, filament::View* view, filament::Scene* scene);
|
||||
|
||||
void animate(filament::Engine* engine, filament::View* view, double now);
|
||||
|
||||
void loadAsset(utils::Path filename);
|
||||
void loadResources(utils::Path filename);
|
||||
|
||||
enum MaterialSource {
|
||||
JITSHADER,
|
||||
UBERSHADER,
|
||||
};
|
||||
struct App {
|
||||
filament::Engine* engine;
|
||||
filament::viewer::ViewerGui* viewer;
|
||||
Config config;
|
||||
filament::gltfio::AssetLoader* loader;
|
||||
filament::gltfio::FilamentAsset* asset = nullptr;
|
||||
utils::NameComponentManager* names;
|
||||
filament::gltfio::MaterialProvider* materials;
|
||||
MaterialSource materialSource = UBERSHADER;
|
||||
filament::gltfio::ResourceLoader* resourceLoader = nullptr;
|
||||
filament::gltfio::TextureProvider* stbDecoder = nullptr;
|
||||
filament::gltfio::TextureProvider* ktxDecoder = nullptr;
|
||||
filament::gltfio::FilamentInstance* instance;
|
||||
bool enableMSAA = false;
|
||||
};
|
||||
App mApp;
|
||||
|
||||
std::string mFilename;
|
||||
};
|
||||
|
||||
#endif // TNT_GLTFVIEWER_H
|
||||
169
test/gltf-comparison/include/ImageExpectations.h
Normal file
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_IMAGE_EXPECTATIONS_H
|
||||
#define TNT_IMAGE_EXPECTATIONS_H
|
||||
|
||||
#include <vector>
|
||||
#include <backend/PixelBufferDescriptor.h>
|
||||
#include <filament/Renderer.h>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
// Arguments are (Renderer* renderer, ImageExpectations& expectations,
|
||||
// ScreenshotParams screenshotParams)
|
||||
#define EXPECT_IMAGE(renderer, expectations, screenshotParams) \
|
||||
do { \
|
||||
expectations.addExpectation( \
|
||||
__FILE__, \
|
||||
__LINE__, \
|
||||
renderer, \
|
||||
screenshotParams); \
|
||||
} while (0)
|
||||
|
||||
namespace test {
|
||||
|
||||
/**
|
||||
* Stores user-provided configuration values for an image expectation
|
||||
*/
|
||||
class ScreenshotParams {
|
||||
public:
|
||||
// TODO(b/422804941): Add a set of environments where this test should use a different golden.
|
||||
ScreenshotParams(int width, int height, std::string fileName,
|
||||
bool isSrgb = false);
|
||||
|
||||
int width() const;
|
||||
int height() const;
|
||||
bool isSrgb() const;
|
||||
|
||||
static std::string actualDirectoryPath();
|
||||
std::string actualFileName() const;
|
||||
std::string actualFilePath() const;
|
||||
static std::string expectedDirectoryPath();
|
||||
std::string expectedFileName() const;
|
||||
std::string expectedFilePath() const;
|
||||
const std::string filePrefix() const;
|
||||
|
||||
private:
|
||||
int mWidth;
|
||||
int mHeight;
|
||||
bool mIsSrgb;
|
||||
std::string mFileName;
|
||||
};
|
||||
|
||||
/**
|
||||
* When created adds a command to the GPU pipeline to copy the render target into a buffer and
|
||||
* stores the result.
|
||||
* If this object is destroyed before the GPU pipeline is flushed it will leak memory in order to
|
||||
* avoid the GPU pipeline callback being a use-after-free.
|
||||
*/
|
||||
class RenderTargetDump {
|
||||
public:
|
||||
using ReadPixels = std::function<void(uint32_t, uint32_t, uint32_t, uint32_t,
|
||||
filament::backend::PixelBufferDescriptor)>;
|
||||
static ReadPixels readFromRenderer(filament::Renderer* renderer);
|
||||
|
||||
RenderTargetDump(const ReadPixels& readPixels, const ScreenshotParams& params);
|
||||
RenderTargetDump(RenderTargetDump&& other) = default;
|
||||
RenderTargetDump& operator=(RenderTargetDump&& other) = default;
|
||||
~RenderTargetDump();
|
||||
|
||||
/**
|
||||
* Should only bue used if BytesFilled returns true.
|
||||
* @return The hash of the stored bytes.
|
||||
*/
|
||||
uint32_t hash() const;
|
||||
/**
|
||||
* Gets the bytes of the render target. The hash should usually be preferable for comparisons
|
||||
* but this is available for debugging.
|
||||
* @return The stored bytes.
|
||||
*/
|
||||
const std::vector<unsigned char>& bytes() const;
|
||||
/**
|
||||
* Thread safe as this is backed by an atomic.
|
||||
* Once this returns true it will never return false.
|
||||
* @return Whether the bytes have actually been copied from the GPU to the buffer.
|
||||
*/
|
||||
bool bytesFilled() const;
|
||||
|
||||
private:
|
||||
struct Internal {
|
||||
explicit Internal(const ScreenshotParams& params);
|
||||
ScreenshotParams params;
|
||||
std::atomic<bool> bytesFilled = false;
|
||||
std::vector<unsigned char> bytes;
|
||||
|
||||
uint32_t hash() const;
|
||||
};
|
||||
|
||||
// We need a memory location that won't be invalidated to pass to GPU callbacks as they can't
|
||||
// be canceled during the destructor.
|
||||
std::unique_ptr<Internal> mInternal;
|
||||
};
|
||||
|
||||
class LoadedPng {
|
||||
public:
|
||||
explicit LoadedPng(std::string filePath);
|
||||
|
||||
uint32_t hash() const;
|
||||
|
||||
const std::vector<unsigned char>& bytes() const;
|
||||
|
||||
private:
|
||||
std::string mFilePath;
|
||||
std::vector<unsigned char> mBytes;
|
||||
};
|
||||
|
||||
class ImageExpectation {
|
||||
public:
|
||||
ImageExpectation(const char* fileName, int lineNumber, RenderTargetDump::ReadPixels readPixels,
|
||||
ScreenshotParams params);
|
||||
|
||||
void evaluate();
|
||||
|
||||
private:
|
||||
void compareImage() const;
|
||||
|
||||
bool mEvaluated = false;
|
||||
const char* mFileName;
|
||||
int mLineNumber;
|
||||
ScreenshotParams mParams;
|
||||
RenderTargetDump mResult;
|
||||
};
|
||||
|
||||
class ImageExpectations {
|
||||
public:
|
||||
explicit ImageExpectations() = default;
|
||||
~ImageExpectations();
|
||||
|
||||
/**
|
||||
* Not meant to be called directly, use EXPECT_IMAGE to get the file name and line number
|
||||
*/
|
||||
void addExpectation(const char* fileName, int lineNumber,
|
||||
filament::Renderer* renderer, ScreenshotParams params);
|
||||
|
||||
void evaluate();
|
||||
|
||||
static void markImageAsFailure(const std::string& imagePrefix);
|
||||
|
||||
private:
|
||||
// Store expectations in unique pointers because they are self referential.
|
||||
std::vector<std::unique_ptr<ImageExpectation>> mExpectations;
|
||||
};
|
||||
|
||||
} // namespace test
|
||||
|
||||
#endif //TNT_IMAGE_EXPECTATIONS_H
|
||||
30
test/gltf-comparison/include/RunOnMain.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_RUNONMAIN_H
|
||||
#define TNT_RUNONMAIN_H
|
||||
|
||||
#include <mutex>
|
||||
#include <functional>
|
||||
|
||||
#include "CrossThreadTask.h"
|
||||
|
||||
class RunOnMain {
|
||||
public:
|
||||
static CrossThreadTask<void()> sTask;
|
||||
};
|
||||
|
||||
#endif // TNT_RUNONMAIN_H
|
||||
17
test/gltf-comparison/src/CrossThreadTask.cpp
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "CrossThreadTask.h"
|
||||
176
test/gltf-comparison/src/GLTFViewer.cpp
Normal file
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "GLTFViewer.h"
|
||||
|
||||
#include <filamentapp/Config.h>
|
||||
#include <filamentapp/FilamentApp.h>
|
||||
#include <filamentapp/IBL.h>
|
||||
|
||||
#include <filament/Camera.h>
|
||||
#include <filament/Engine.h>
|
||||
#include <filament/IndexBuffer.h>
|
||||
#include <filament/Material.h>
|
||||
#include <filament/MaterialInstance.h>
|
||||
#include <filament/RenderableManager.h>
|
||||
#include <filament/Scene.h>
|
||||
#include <filament/Skybox.h>
|
||||
#include <filament/TransformManager.h>
|
||||
#include <filament/VertexBuffer.h>
|
||||
#include <filament/View.h>
|
||||
|
||||
#include <utils/EntityManager.h>
|
||||
#include <utils/NameComponentManager.h>
|
||||
|
||||
#include <gltfio/TextureProvider.h>
|
||||
|
||||
//#include "generated/resources/gltf.h"
|
||||
#include "materials/uberarchive.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
using namespace filament;
|
||||
using utils::Entity;
|
||||
using utils::EntityManager;
|
||||
|
||||
|
||||
void GLTFViewer::runApp(std::function<void(filament::Engine*, filament::View*, filament::Scene*,
|
||||
filament::Renderer*)> postRender) {
|
||||
FilamentApp::get().animate(
|
||||
[this](Engine* engine, View* view, double now) { animate(engine, view, now); });
|
||||
|
||||
// TODO: Remove
|
||||
mApp.config.backend = filament::Engine::Backend::METAL;
|
||||
|
||||
FilamentApp::get().run(
|
||||
mApp.config,
|
||||
[this](Engine* engine, View* view, Scene* scene) { setup(engine, view, scene); },
|
||||
[this](Engine* engine, View* view, Scene* scene) { cleanup(engine, view, scene); },
|
||||
FilamentApp::ImGuiCallback(), FilamentApp::PreRenderCallback(), std::move(postRender));
|
||||
}
|
||||
|
||||
void GLTFViewer::setup(Engine* engine, filament::View* view, filament::Scene* scene) {
|
||||
if (mApp.enableMSAA) {
|
||||
// TODO Investigate why
|
||||
// Enabling MSAA fixes the "transmission" sample but breaks the others
|
||||
view->setMultiSampleAntiAliasingOptions({ .enabled = true});
|
||||
}
|
||||
mApp.engine = engine;
|
||||
mApp.names = new utils::NameComponentManager(EntityManager::get());
|
||||
mApp.viewer = new viewer::ViewerGui(engine, scene, view);
|
||||
|
||||
// TODO: Support JIT
|
||||
mApp.materials = //(mApp.materialSource == JITSHADER)
|
||||
//? createJitShaderProvider(engine, false /* optimize */,
|
||||
// samples::getJitMaterialVariantFilter(mApp.config.backend))
|
||||
//:
|
||||
filament::gltfio::createUbershaderProvider(engine, UBERARCHIVE_DEFAULT_DATA,
|
||||
UBERARCHIVE_DEFAULT_SIZE);
|
||||
|
||||
mApp.loader = gltfio::AssetLoader::create({ engine, mApp.materials, mApp.names });
|
||||
|
||||
loadAsset(mFilename);
|
||||
loadResources(mFilename);
|
||||
mApp.viewer->setAsset(mApp.asset, mApp.instance);
|
||||
}
|
||||
|
||||
void GLTFViewer::cleanup(Engine* engine, filament::View* view, filament::Scene* scene) {
|
||||
mApp.loader->destroyAsset(mApp.asset);
|
||||
mApp.materials->destroyMaterials();
|
||||
|
||||
delete mApp.materials;
|
||||
delete mApp.names;
|
||||
delete mApp.viewer;
|
||||
delete mApp.resourceLoader;
|
||||
delete mApp.stbDecoder;
|
||||
delete mApp.ktxDecoder;
|
||||
|
||||
gltfio::AssetLoader::destroy(&mApp.loader);
|
||||
}
|
||||
|
||||
void GLTFViewer::animate(filament::Engine* engine, filament::View* view, double now) {
|
||||
mApp.resourceLoader->asyncUpdateLoad();
|
||||
|
||||
mApp.viewer->updateRootTransform();
|
||||
mApp.viewer->populateScene();
|
||||
|
||||
mApp.viewer->applyAnimation(now, mApp.instance);
|
||||
}
|
||||
|
||||
void GLTFViewer::setFilename(std::string filename) {
|
||||
mFilename = std::move(filename);
|
||||
}
|
||||
|
||||
static std::ifstream::pos_type getFileSize(const char* filename) {
|
||||
std::ifstream in(filename, std::ifstream::ate | std::ifstream::binary);
|
||||
return in.tellg();
|
||||
}
|
||||
|
||||
void GLTFViewer::loadAsset(utils::Path filename) {
|
||||
// Peek at the file size to allow pre-allocation.
|
||||
long contentSize = static_cast<long>(getFileSize(filename.c_str()));
|
||||
if (contentSize <= 0) {
|
||||
std::cerr << "Unable to open " << filename << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Consume the glTF file.
|
||||
std::ifstream in(filename.c_str(), std::ifstream::binary | std::ifstream::in);
|
||||
std::vector<uint8_t> buffer(static_cast<unsigned long>(contentSize));
|
||||
if (!in.read((char*) buffer.data(), contentSize)) {
|
||||
std::cerr << "Unable to read " << filename << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Parse the glTF file and create Filament entities.
|
||||
mApp.asset = mApp.loader->createInstancedAsset(buffer.data(), buffer.size(),
|
||||
&(mApp.instance), 1);
|
||||
buffer.clear();
|
||||
buffer.shrink_to_fit();
|
||||
|
||||
if (!mApp.asset) {
|
||||
std::cerr << "Unable to parse " << filename << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void GLTFViewer::loadResources(utils::Path filename) {
|
||||
// Load external textures and buffers.
|
||||
std::string gltfPath = filename.getAbsolutePath();
|
||||
gltfio::ResourceConfiguration configuration;
|
||||
configuration.engine = mApp.engine;
|
||||
configuration.gltfPath = gltfPath.c_str();
|
||||
configuration.normalizeSkinningWeights = true;
|
||||
if (!mApp.resourceLoader) {
|
||||
mApp.resourceLoader = new gltfio::ResourceLoader(configuration);
|
||||
mApp.stbDecoder = gltfio::createStbProvider(mApp.engine);
|
||||
mApp.ktxDecoder = gltfio::createKtx2Provider(mApp.engine);
|
||||
mApp.resourceLoader->addTextureProvider("image/png", mApp.stbDecoder);
|
||||
mApp.resourceLoader->addTextureProvider("image/jpeg", mApp.stbDecoder);
|
||||
mApp.resourceLoader->addTextureProvider("image/ktx2", mApp.ktxDecoder);
|
||||
}
|
||||
|
||||
if (!mApp.resourceLoader->asyncBeginLoad(mApp.asset)) {
|
||||
std::cerr << "Unable to start loading resources for " << filename << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
auto ibl = FilamentApp::get().getIBL();
|
||||
if (ibl) {
|
||||
mApp.viewer->setIndirectLight(ibl->getIndirectLight(), ibl->getSphericalHarmonics());
|
||||
}
|
||||
}
|
||||
248
test/gltf-comparison/src/ImageExpectations.cpp
Normal file
@@ -0,0 +1,248 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "ImageExpectations.h"
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "utils/Hash.h"
|
||||
#include <fstream>
|
||||
|
||||
#include <imageio/ImageEncoder.h>
|
||||
#include <imageio/ImageDecoder.h>
|
||||
#include <image/ColorTransform.h>
|
||||
|
||||
namespace test {
|
||||
|
||||
ScreenshotParams::ScreenshotParams(int width, int height, std::string fileName, bool isSrgb)
|
||||
: mWidth(width),
|
||||
mHeight(height),
|
||||
mIsSrgb(isSrgb),
|
||||
mFileName(std::move(fileName)) {}
|
||||
|
||||
int ScreenshotParams::width() const {
|
||||
return mWidth;
|
||||
}
|
||||
|
||||
int ScreenshotParams::height() const {
|
||||
return mHeight;
|
||||
}
|
||||
|
||||
bool ScreenshotParams::isSrgb() const {
|
||||
return mIsSrgb;
|
||||
}
|
||||
|
||||
std::string ScreenshotParams::actualDirectoryPath() {
|
||||
return "images/actual_images";
|
||||
}
|
||||
|
||||
std::string ScreenshotParams::actualFileName() const {
|
||||
return absl::StrFormat("%s_actual.png", mFileName);
|
||||
}
|
||||
|
||||
std::string ScreenshotParams::actualFilePath() const {
|
||||
return absl::StrFormat("%s/%s", actualDirectoryPath(), actualFileName());
|
||||
}
|
||||
|
||||
std::string ScreenshotParams::expectedDirectoryPath() {
|
||||
return "images/expected_images";
|
||||
}
|
||||
|
||||
std::string ScreenshotParams::expectedFileName() const {
|
||||
return absl::StrFormat("%s.png", mFileName);
|
||||
}
|
||||
|
||||
std::string ScreenshotParams::expectedFilePath() const {
|
||||
return absl::StrFormat("%s/%s", expectedDirectoryPath(), expectedFileName());
|
||||
}
|
||||
|
||||
const std::string ScreenshotParams::filePrefix() const {
|
||||
// TODO(b/422804941): If there are platform specific goldens, when on those platforms append a
|
||||
// unique platform identifying string to this.
|
||||
return mFileName;
|
||||
}
|
||||
|
||||
ImageExpectation::ImageExpectation(const char* fileName, int lineNumber,
|
||||
RenderTargetDump::ReadPixels readPixels, ScreenshotParams params)
|
||||
: mFileName(fileName),
|
||||
mLineNumber(lineNumber),
|
||||
mParams(std::move(params)),
|
||||
mResult(readPixels, mParams) {}
|
||||
|
||||
void ImageExpectation::evaluate() {
|
||||
// Ensure this is only evaluated once.
|
||||
if (mEvaluated) {
|
||||
return;
|
||||
}
|
||||
mEvaluated = true;
|
||||
|
||||
// Do the actual image comparison inside a scoped trace with the stored file and line.
|
||||
{
|
||||
testing::ScopedTrace trace(mFileName, mLineNumber, "");
|
||||
compareImage();
|
||||
}
|
||||
}
|
||||
|
||||
void ImageExpectation::compareImage() const {
|
||||
bool bytesFilled = mResult.bytesFilled();
|
||||
// If this fails, it likely means that BackendTest::flushAndWait needs to be called before
|
||||
// ImageExpectations is evaluated or destroyed.
|
||||
EXPECT_THAT(bytesFilled, testing::IsTrue())
|
||||
<< "Render target wasn't copied to the buffer for " << mFileName;
|
||||
if (bytesFilled) {
|
||||
// Rather than directly compare the two images compare their hashes because comparing very
|
||||
// large arrays generates way too much debug output to be useful.
|
||||
uint32_t actualHash = mResult.hash();
|
||||
#ifndef FILAMENT_IOS
|
||||
LoadedPng loadedImage(mParams.expectedFilePath());
|
||||
uint32_t loadedImageHash = loadedImage.hash();
|
||||
auto compareToImageMatcher = testing::Eq(loadedImageHash);
|
||||
if (!testing::Matches(compareToImageMatcher)(actualHash)) {
|
||||
ImageExpectations::markImageAsFailure(mParams.filePrefix());
|
||||
}
|
||||
EXPECT_THAT(actualHash, compareToImageMatcher) << mParams.expectedFileName();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
ImageExpectations::~ImageExpectations() {
|
||||
// Guarantee that all expectations are evaluated when this leaves scope even if the caller
|
||||
// forgot to manually evaluate them.
|
||||
evaluate();
|
||||
}
|
||||
|
||||
void ImageExpectations::addExpectation(const char* fileName, int lineNumber,
|
||||
filament::Renderer* renderer, ScreenshotParams params) {
|
||||
mExpectations.emplace_back(std::make_unique<ImageExpectation>(
|
||||
fileName, lineNumber,
|
||||
[renderer](uint32_t x, uint32_t y, uint32_t width, uint32_t height,
|
||||
filament::backend::PixelBufferDescriptor&& pb) {
|
||||
renderer->readPixels(x, y, width, height, std::move(pb));
|
||||
},
|
||||
std::move(params)));
|
||||
}
|
||||
|
||||
void ImageExpectations::evaluate() {
|
||||
for (auto& expectation: mExpectations) {
|
||||
expectation->evaluate();
|
||||
}
|
||||
mExpectations.clear();
|
||||
}
|
||||
|
||||
void ImageExpectations::markImageAsFailure(const std::string& imagePrefix) {
|
||||
|
||||
}
|
||||
|
||||
RenderTargetDump::RenderTargetDump(const RenderTargetDump::ReadPixels& readPixels,
|
||||
const ScreenshotParams& params)
|
||||
: mInternal(std::make_unique<RenderTargetDump::Internal>(params)) {
|
||||
const size_t size = mInternal->params.width() * mInternal->params.height() * 4;
|
||||
mInternal->bytes.resize(size);
|
||||
|
||||
auto cb = [](void* buffer, size_t size, void* user) {
|
||||
auto* internal = static_cast<RenderTargetDump::Internal*>(user);
|
||||
internal->bytesFilled = true;
|
||||
#ifndef FILAMENT_IOS
|
||||
image::LinearImage image;
|
||||
if (internal->params.isSrgb()) {
|
||||
image = image::toLinearWithAlpha<uint8_t>(internal->params.width(),
|
||||
internal->params.height(),
|
||||
internal->params.width() * 4, (uint8_t*)buffer);
|
||||
} else {
|
||||
// The image data is already linear, so pass in transforms that simply go from uint8_t
|
||||
// to float. toLinearWithAlpha divides the float values by uint8_t max so there's no
|
||||
// need to scale it to [0, 1]
|
||||
image = image::toLinearWithAlpha<uint8_t>(
|
||||
internal->params.width(), internal->params.height(),
|
||||
internal->params.width() * 4, (uint8_t*) buffer,
|
||||
[](uint8_t value) -> float { return value; },
|
||||
[](filament::math::float4 rgba) -> filament::math::float4 { return rgba; });
|
||||
}
|
||||
std::string filePath = internal->params.actualFilePath();
|
||||
std::ofstream pngStream(filePath, std::ios::binary | std::ios::trunc);
|
||||
// To avoid going from linear -> sRGB -> linear save the PNG as linear.
|
||||
image::ImageEncoder::encode(pngStream, image::ImageEncoder::Format::PNG_LINEAR, image, "",
|
||||
filePath);
|
||||
#endif
|
||||
};
|
||||
filament::backend::PixelBufferDescriptor pb(mInternal->bytes.data(), size,
|
||||
filament::backend::PixelDataFormat::RGBA, filament::backend::PixelDataType::UBYTE, cb,
|
||||
(void*)mInternal.get());
|
||||
readPixels(0, 0, mInternal->params.width(), mInternal->params.height(), std::move(pb));
|
||||
}
|
||||
|
||||
RenderTargetDump::~RenderTargetDump() {
|
||||
// If the GPU callback hasn't been made yet then there's a callback elsewhere that has a copy of
|
||||
// the internal pointer. But there's no guarantee that the callback will be ever made if the GPU
|
||||
// pipeline wasn't run for some reason. So it is necessary to leak the memory.
|
||||
// It would be possible to try to coordinate with the callback to have it clean up the memory,
|
||||
// but if this condition happens there's already an issue with the test case so there's no need.
|
||||
if (!bytesFilled()) {
|
||||
mInternal.release();
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t RenderTargetDump::Internal::hash() const {
|
||||
return utils::hash::murmur3((uint32_t*)bytes.data(), bytes.size() / 4, 0);
|
||||
}
|
||||
|
||||
uint32_t RenderTargetDump::hash() const {
|
||||
return mInternal->hash();
|
||||
}
|
||||
|
||||
const std::vector<unsigned char>& RenderTargetDump::bytes() const {
|
||||
return mInternal->bytes;
|
||||
}
|
||||
|
||||
bool RenderTargetDump::bytesFilled() const {
|
||||
return mInternal->bytesFilled;
|
||||
}
|
||||
|
||||
RenderTargetDump::Internal::Internal(const ScreenshotParams& params) : params(params) {}
|
||||
|
||||
LoadedPng::LoadedPng(std::string filePath) : mFilePath(std::move(filePath)) {
|
||||
#ifndef FILAMENT_IOS
|
||||
std::ifstream pngStream(mFilePath, std::ios::binary);
|
||||
image::LinearImage loadedImage = image::ImageDecoder::decode(pngStream, filePath,
|
||||
image::ImageDecoder::ColorSpace::LINEAR);
|
||||
size_t valuesInImage = loadedImage.getWidth() * loadedImage.getHeight() *
|
||||
loadedImage.getChannels();
|
||||
// The linear image is loaded with each component as [0.0, 1.0] but should be [0, 255], so
|
||||
// convert them.
|
||||
mBytes = std::vector<unsigned char>(valuesInImage);
|
||||
for (int i = 0; i < valuesInImage; ++i) {
|
||||
mBytes[i] = static_cast<uint8_t>(loadedImage.get<float>()[i] * 255.0f);
|
||||
}
|
||||
#endif
|
||||
// For platforms that don't support the image loading library, leave the loaded data blank.
|
||||
}
|
||||
|
||||
uint32_t LoadedPng::hash() const {
|
||||
EXPECT_THAT(mBytes, testing::Not(testing::IsEmpty()))
|
||||
<< "Failed to load expected test result: " << mFilePath << ".\n"
|
||||
<< "Did you forget to sync CMake after updating the expected image in the source "
|
||||
"directory?";
|
||||
if (mBytes.empty()) {
|
||||
return 0;
|
||||
}
|
||||
return utils::hash::murmur3((uint32_t*)mBytes.data(), mBytes.size() / 4, 0);
|
||||
}
|
||||
|
||||
const std::vector<unsigned char>& LoadedPng::bytes() const {
|
||||
return mBytes;
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
19
test/gltf-comparison/src/RunOnMain.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "RunOnMain.h"
|
||||
|
||||
CrossThreadTask<void()> RunOnMain::sTask;
|
||||
40
test/gltf-comparison/src/main.cpp
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <thread>
|
||||
|
||||
#include "RunOnMain.h"
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
|
||||
int testResult = -1;
|
||||
|
||||
std::thread testThread([&](){
|
||||
testResult = RUN_ALL_TESTS();
|
||||
RunOnMain::sTask.setClosed();
|
||||
});
|
||||
|
||||
while (!RunOnMain::sTask.isClosed()) {
|
||||
RunOnMain::sTask.runTask();
|
||||
}
|
||||
|
||||
testThread.join();
|
||||
|
||||
return testResult;
|
||||
}
|
||||
147
test/gltf-comparison/src/test_CompareGLTF.cpp
Normal file
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <filamentapp/Config.h>
|
||||
#include <filamentapp/FilamentApp.h>
|
||||
#include <filamentapp/IBL.h>
|
||||
|
||||
#include <filament/Camera.h>
|
||||
#include <filament/Engine.h>
|
||||
#include <filament/IndexBuffer.h>
|
||||
#include <filament/Material.h>
|
||||
#include <filament/MaterialInstance.h>
|
||||
#include <filament/RenderableManager.h>
|
||||
#include <filament/Scene.h>
|
||||
#include <filament/Skybox.h>
|
||||
#include <filament/TransformManager.h>
|
||||
#include <filament/VertexBuffer.h>
|
||||
#include <filament/View.h>
|
||||
|
||||
#include <utils/EntityManager.h>
|
||||
|
||||
#include "absl/strings/str_format.h"
|
||||
|
||||
#include "ImageExpectations.h"
|
||||
#include "RunOnMain.h"
|
||||
#include "GLTFViewer.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <optional>
|
||||
|
||||
using namespace filament;
|
||||
using utils::Entity;
|
||||
using utils::EntityManager;
|
||||
|
||||
constexpr int kFramesToWait = 1000;
|
||||
|
||||
class CompareGLTFContext {
|
||||
public:
|
||||
CompareGLTFContext();
|
||||
~CompareGLTFContext();
|
||||
|
||||
using Command = std::function<void(Engine*, View*, Scene*, Renderer*)>;
|
||||
|
||||
void postRender(Engine* engine, View* view, Scene* scene, Renderer* renderer);
|
||||
|
||||
void addPostRenderCommand(Command command);
|
||||
|
||||
CrossThreadTask<void(Engine*, View*, Scene*, Renderer*), Engine*, View*, Scene*, Renderer*>
|
||||
mPostRenderCommand;
|
||||
std::atomic<bool> mStarted = false;
|
||||
|
||||
GLTFViewer mViewer;
|
||||
};
|
||||
|
||||
class CompareGLTFTest : public testing::TestWithParam<std::string> {
|
||||
public:
|
||||
test::ImageExpectations mExpectations;
|
||||
|
||||
static void SetUpTestSuite();
|
||||
void SetUp() override;
|
||||
static void TearDownTestSuite();
|
||||
|
||||
static std::optional<CompareGLTFContext> sContext;
|
||||
std::atomic<bool> mTestFinished;
|
||||
};
|
||||
|
||||
|
||||
std::optional<CompareGLTFContext> CompareGLTFTest::sContext;
|
||||
|
||||
void CompareGLTFTest::SetUpTestSuite() {
|
||||
sContext.emplace();
|
||||
}
|
||||
|
||||
void CompareGLTFTest::SetUp() {
|
||||
while (!sContext->mStarted) {}
|
||||
}
|
||||
|
||||
void CompareGLTFTest::TearDownTestSuite() {
|
||||
sContext.reset();
|
||||
}
|
||||
|
||||
CompareGLTFContext::CompareGLTFContext() {
|
||||
mViewer.setFilename("glTF_cases/Models/Box/glTF/Box.gltf");
|
||||
addPostRenderCommand([this](Engine* engine, View* view, Scene* scene, Renderer* renderer){
|
||||
mStarted = true;
|
||||
});
|
||||
RunOnMain::sTask.queueTask([=,this]() {
|
||||
mViewer.runApp([this](Engine* engine, View* view, Scene* scene, Renderer* renderer){
|
||||
postRender(engine, view, scene, renderer);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
CompareGLTFContext::~CompareGLTFContext() {
|
||||
std::atomic<bool> appClosed = false;
|
||||
addPostRenderCommand([&, this](Engine* engine, View* view, Scene* scene, Renderer* renderer) {
|
||||
FilamentApp::get().close();
|
||||
appClosed = true;
|
||||
});
|
||||
|
||||
while (!appClosed) {}
|
||||
}
|
||||
|
||||
void CompareGLTFContext::addPostRenderCommand(CompareGLTFContext::Command command) {
|
||||
while (!mPostRenderCommand.queueTask(command)) {}
|
||||
}
|
||||
|
||||
void CompareGLTFContext::postRender(Engine* engine, View* view, Scene* scene, Renderer* renderer) {
|
||||
mPostRenderCommand.runTask(engine, view, scene, renderer);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(GLTFFiles, CompareGLTFTest, testing::Values("BoxTextured", "Box"),
|
||||
[](const testing::TestParamInfo<std::string>& info) { return info.param; });
|
||||
|
||||
TEST_P(CompareGLTFTest, Compare) {
|
||||
sContext->addPostRenderCommand([this](Engine*, View* view, Scene*, Renderer* renderer) {
|
||||
EXPECT_IMAGE(renderer, mExpectations,
|
||||
test::ScreenshotParams(512, 512, absl::StrFormat("GLTF_%s", GetParam().c_str())));
|
||||
|
||||
std::cout << "postRender: " << GetParam() << std::endl;
|
||||
mTestFinished = true;
|
||||
});
|
||||
while (!mTestFinished) {}
|
||||
|
||||
std::atomic<int> counter = 0;
|
||||
auto incrementCounter = [&](Engine*, View* view, Scene*, Renderer* renderer) {
|
||||
counter++;
|
||||
};
|
||||
while (counter < kFramesToWait) {
|
||||
sContext->addPostRenderCommand(incrementCounter);
|
||||
}
|
||||
}
|
||||