JavaScript code re-org (#443)
This removes the old CPP-based web samples in favor of the new JS API. The `web/samples` folder will be added in a subsequent commit.
This commit is contained in:
@@ -290,10 +290,9 @@ endif()
|
||||
set (FILAMENT_SAMPLES_BINARY_DIR ${PROJECT_BINARY_DIR}/samples)
|
||||
|
||||
if (WEBGL)
|
||||
add_subdirectory(${FILAMENT}/samples/web)
|
||||
add_subdirectory(web/filamentjs)
|
||||
|
||||
add_subdirectory(${LIBRARIES}/filagui)
|
||||
add_subdirectory(${LIBRARIES}/filamentjs)
|
||||
|
||||
add_subdirectory(${EXTERNAL}/imgui/tnt)
|
||||
add_subdirectory(${EXTERNAL}/stb/tnt)
|
||||
|
||||
11
build.sh
11
build.sh
@@ -219,14 +219,14 @@ function build_webgl_with_target {
|
||||
${BUILD_COMMAND} ${BUILD_TARGETS}
|
||||
fi
|
||||
|
||||
if [ -d "samples/web/public" ]; then
|
||||
if [ -d "web/filamentjs" ]; then
|
||||
if [ "$ISSUE_ARCHIVES" == "true" ]; then
|
||||
|
||||
which -s python3
|
||||
if [ $? == 0 ]; then
|
||||
echo "Generating JavaScript documentation..."
|
||||
local DOCS_FOLDER="libs/filamentjs/jsdocs"
|
||||
local DOCS_SCRIPT="../../libs/filamentjs/docs/build.py"
|
||||
local DOCS_FOLDER="web/docs"
|
||||
local DOCS_SCRIPT="../../web/docs/build.py"
|
||||
python3 ${DOCS_SCRIPT} --disable-demo \
|
||||
--output-folder ${DOCS_FOLDER} \
|
||||
--build-folder ${PWD}
|
||||
@@ -236,13 +236,12 @@ function build_webgl_with_target {
|
||||
# The web archive has the following subfolders:
|
||||
# dist...core WASM module and accompanying JS file.
|
||||
# docs...HTML tutorials for the JS API, accompanying demos, and a reference page.
|
||||
cd libs
|
||||
cd web
|
||||
tar -cvf ../../filament-${LC_TARGET}-web.tar -s /^filamentjs/dist/ \
|
||||
filamentjs/filament.js
|
||||
tar -rvf ../../filament-${LC_TARGET}-web.tar -s /^filamentjs/dist/ \
|
||||
filamentjs/filament.wasm
|
||||
tar -rvf ../../filament-${LC_TARGET}-web.tar -s /^filamentjs.jsdocs/docs/ \
|
||||
filamentjs/jsdocs
|
||||
tar -rvf ../../filament-${LC_TARGET}-web.tar docs
|
||||
cd -
|
||||
gzip -c ../filament-${LC_TARGET}-web.tar > ../filament-${LC_TARGET}-web.tgz
|
||||
rm ../filament-${LC_TARGET}-web.tar
|
||||
|
||||
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
@@ -1,204 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.1)
|
||||
project(web-samples)
|
||||
|
||||
# ==================================================================================================
|
||||
# To fetch materials, the web samples can either: (1) use #include or (2) download filamat files.
|
||||
# For simplicity, we generate both types of files for each material.
|
||||
# ==================================================================================================
|
||||
|
||||
set(MATERIAL_NAMES
|
||||
sandboxCloth
|
||||
sandboxLit
|
||||
sandboxLitFade
|
||||
sandboxLitTransparent
|
||||
sandboxSubsurface
|
||||
sandboxUnlit
|
||||
texturedLit
|
||||
bakedColor)
|
||||
|
||||
if (CMAKE_CROSSCOMPILING)
|
||||
include(${IMPORT_EXECUTABLES})
|
||||
endif()
|
||||
|
||||
set(MATC_FLAGS -a opengl -m material -p mobile)
|
||||
if (CMAKE_BUILD_TYPE MATCHES Release)
|
||||
set(MATC_FLAGS -O ${MATC_FLAGS})
|
||||
endif()
|
||||
|
||||
set(GENERATION_ROOT ${CMAKE_CURRENT_BINARY_DIR})
|
||||
file(MAKE_DIRECTORY "${GENERATION_ROOT}/generated/material/")
|
||||
file(MAKE_DIRECTORY "public/material/")
|
||||
|
||||
set(MATERIAL_BINS)
|
||||
foreach (NAME ${MATERIAL_NAMES})
|
||||
set(mat_src "../materials/${NAME}.mat")
|
||||
get_filename_component(localname "${mat_src}" NAME_WE)
|
||||
get_filename_component(fullname "${mat_src}" ABSOLUTE)
|
||||
set(output_inc "${GENERATION_ROOT}/generated/material/${localname}.inc")
|
||||
set(output_bin "public/material/${localname}.filamat")
|
||||
add_custom_command(
|
||||
OUTPUT ${output_inc} ${output_bin}
|
||||
COMMAND matc ${MATC_FLAGS} -f header -o ${output_inc} ${fullname}
|
||||
COMMAND matc ${MATC_FLAGS} -o ${output_bin} ${fullname}
|
||||
MAIN_DEPENDENCY ${mat_src}
|
||||
DEPENDS matc
|
||||
COMMENT "Compiling material ${mat_src} to ${output_path}")
|
||||
list(APPEND MATERIAL_BINS ${output_inc} ${output_bin})
|
||||
endforeach()
|
||||
|
||||
add_custom_target(sample_materials DEPENDS ${MATERIAL_BINS})
|
||||
|
||||
# ==================================================================================================
|
||||
# Build Assets.
|
||||
#
|
||||
# The goal is to create a "public" folder that can be the root of a simple file server:
|
||||
#
|
||||
# /suzanne.js built by em++
|
||||
# /suzanne.wasm built by em++
|
||||
# /suzanne.html copied from REPO/samples/web
|
||||
# /filaweb.js copied from REPO/samples/web
|
||||
# /favicon.png copied from REPO/samples/web
|
||||
# /monkey/*.ktx generated by mipgen from REPO/assets/models
|
||||
# /monkey/*.filamesh built by filamesh
|
||||
# /syferfontein_18d_clear_2k/* built by cmgen
|
||||
#
|
||||
# ==================================================================================================
|
||||
|
||||
function(copy_assets PATH NAME)
|
||||
file(COPY ${PATH}/${NAME} DESTINATION ${PROJECT_BINARY_DIR}/public)
|
||||
add_custom_target(${NAME} ALL DEPENDS ${NAME})
|
||||
add_dependencies(filament ${NAME})
|
||||
endfunction()
|
||||
|
||||
copy_assets("." filaweb.js)
|
||||
copy_assets("." favicon.png)
|
||||
|
||||
# Generate mipmapped KTX files from various PNG files using mipgen.
|
||||
function(add_ktxfiles SOURCE TARGET EXTRA_ARGS)
|
||||
set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/../../${SOURCE}")
|
||||
set(target_path "public/${TARGET}")
|
||||
set(target_ktxfiles ${target_ktxfiles} ${target_path} PARENT_SCOPE)
|
||||
add_custom_command(
|
||||
OUTPUT ${target_path}
|
||||
COMMAND mipgen --strip-alpha ${EXTRA_ARGS} ${source_path} ${target_path}
|
||||
MAIN_DEPENDENCY ${source_path}
|
||||
DEPENDS mipgen)
|
||||
endfunction()
|
||||
|
||||
set(ETC_R11_ARGS "--grayscale;--compression=etc_r11_numeric_40")
|
||||
|
||||
# TODO: Instead of "rgb8" we should be using "rg11", but that causes an assertion in etc2comp.
|
||||
set(ETC_NORMALS_ARGS "--kernel=NORMALS;--linear;--compression=etc_rgb8_normalxyz_40")
|
||||
|
||||
add_ktxfiles("assets/models/monkey/albedo.png" "monkey/albedo.ktx" "")
|
||||
add_ktxfiles("assets/models/monkey/albedo.png" "monkey/albedo_astc.ktx" "--compression=astc_fast_ldr_4x4")
|
||||
add_ktxfiles("assets/models/monkey/albedo.png" "monkey/albedo_s3tc.ktx" "--compression=s3tc_rgb_dxt1")
|
||||
add_ktxfiles("assets/models/monkey/normal.png" "monkey/normal.ktx" "--kernel=NORMALS;--linear")
|
||||
add_ktxfiles("assets/models/monkey/normal.png" "monkey/normal_etc.ktx" "${ETC_NORMALS_ARGS}")
|
||||
add_ktxfiles("assets/models/monkey/roughness.png" "monkey/roughness.ktx" "--grayscale")
|
||||
add_ktxfiles("assets/models/monkey/roughness.png" "monkey/roughness_etc.ktx" "${ETC_R11_ARGS}")
|
||||
add_ktxfiles("assets/models/monkey/metallic.png" "monkey/metallic.ktx" "--grayscale")
|
||||
add_ktxfiles("assets/models/monkey/metallic.png" "monkey/metallic_etc.ktx" "${ETC_R11_ARGS}")
|
||||
add_ktxfiles("assets/models/monkey/ao.png" "monkey/ao.ktx" "--grayscale")
|
||||
add_ktxfiles("assets/models/monkey/ao.png" "monkey/ao_etc.ktx" "${ETC_R11_ARGS}")
|
||||
|
||||
# Convert OBJ files into filamesh files.
|
||||
function(add_mesh SOURCE TARGET)
|
||||
set(source_mesh "${CMAKE_CURRENT_SOURCE_DIR}/../../${SOURCE}")
|
||||
set(target_mesh "public/${TARGET}")
|
||||
set(target_meshes ${target_meshes} ${target_mesh} PARENT_SCOPE)
|
||||
add_custom_command(
|
||||
OUTPUT ${target_mesh}
|
||||
COMMAND filamesh ${source_mesh} ${target_mesh}
|
||||
MAIN_DEPENDENCY ${source_mesh}
|
||||
DEPENDS filamesh)
|
||||
endfunction()
|
||||
|
||||
add_mesh("assets/models/monkey/monkey.obj" "monkey/mesh.filamesh")
|
||||
add_mesh("third_party/shader_ball/shader_ball.obj" "shaderball/mesh.filamesh")
|
||||
|
||||
# Generate IBL and skybox images using cmgen.
|
||||
set(CMGEN_ARGS -x public --format=ktx --size=256 --extract-blur=0.1)
|
||||
set(CMGEN_ARGS_S3TC ${CMGEN_ARGS} --compression=s3tc_rgba_dxt5)
|
||||
set(CMGEN_ARGS_ETC ${CMGEN_ARGS} --compression=etc_rgba8_rgba_40)
|
||||
function(add_envmap SOURCE TARGET)
|
||||
set(source_envmap "${CMAKE_CURRENT_SOURCE_DIR}/../../${SOURCE}")
|
||||
|
||||
file(MAKE_DIRECTORY "public/${TARGET}")
|
||||
|
||||
set(target_skybox "public/${TARGET}/${TARGET}_skybox.ktx")
|
||||
set(target_envmap "public/${TARGET}/${TARGET}_ibl.ktx")
|
||||
|
||||
set(target_envmap_etc "public/${TARGET}/${TARGET}_ibl_etc.ktx")
|
||||
set(target_envmap_s3tc "public/${TARGET}/${TARGET}_ibl_s3tc.ktx")
|
||||
|
||||
set(target_skyboxes ${target_skyboxes} ${target_skybox} PARENT_SCOPE)
|
||||
|
||||
set(target_envmaps ${target_envmaps}
|
||||
${target_envmap} ${target_envmap_etc} ${target_envmap_s3tc} PARENT_SCOPE)
|
||||
|
||||
add_custom_command(OUTPUT ${target_skybox}
|
||||
${target_envmap} ${target_envmap_etc} ${target_envmap_s3tc}
|
||||
|
||||
# First create an S3TC-encoded envmap, then rename it.
|
||||
COMMAND cmgen ${CMGEN_ARGS_S3TC} ${source_envmap}
|
||||
COMMAND mv ${target_envmap} ${target_envmap_s3tc}
|
||||
|
||||
# Now create an ETC-encoded envmap, then rename it.
|
||||
COMMAND cmgen ${CMGEN_ARGS_ETC} ${source_envmap}
|
||||
COMMAND mv ${target_envmap} ${target_envmap_etc}
|
||||
|
||||
# Finally, create KTX files for the uncompressed envmap.
|
||||
COMMAND cmgen ${CMGEN_ARGS} ${source_envmap}
|
||||
|
||||
MAIN_DEPENDENCY ${source_envmap}
|
||||
DEPENDS cmgen)
|
||||
endfunction()
|
||||
|
||||
add_envmap("third_party/environments/syferfontein_18d_clear_2k.hdr" "syferfontein_18d_clear_2k")
|
||||
add_envmap("third_party/environments/pillars_2k.hdr" "pillars_2k")
|
||||
|
||||
add_custom_target(sample_assets DEPENDS
|
||||
${target_ktxfiles}
|
||||
${target_meshes}
|
||||
${target_envmaps}
|
||||
${target_skyboxes})
|
||||
|
||||
# ==================================================================================================
|
||||
# For each demo, build a pair of wasm/js files and copy over an HTML file.
|
||||
# ==================================================================================================
|
||||
|
||||
set(EXPORTS "'_launch','_main','_render','_resize','_mouse'")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s EXPORTED_FUNCTIONS=[${EXPORTS}]")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ALLOW_MEMORY_GROWTH=1")
|
||||
if (CMAKE_BUILD_TYPE MATCHES Debug)
|
||||
# Note that emcc offers a SAFE_HEAP=1 option to perform extra memory checks. Ideally we would
|
||||
# enable it for debug builds, but currently it triggers an abort due to the custom memory
|
||||
# alignments that Filament uses for various objects. This merits further investigation.
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s ASSERTIONS=2")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s DEMANGLE_SUPPORT=1")
|
||||
endif()
|
||||
|
||||
function(add_demo NAME)
|
||||
include_directories(${GENERATION_ROOT})
|
||||
add_executable(${NAME} ${NAME}.cpp
|
||||
../app/CameraManipulator.cpp
|
||||
filaweb.h
|
||||
filaweb.cpp)
|
||||
add_dependencies(${NAME} sample_materials sample_assets)
|
||||
target_link_libraries(${NAME} PRIVATE filament filagui filameshio image math utils)
|
||||
|
||||
# Copy the generated js and wasm files into the public folder, as well as the app-specific
|
||||
# manually-written HTML file.
|
||||
add_custom_command(
|
||||
OUTPUT ${PROJECT_BINARY_DIR}/public/${NAME}.js
|
||||
DEPENDS ${NAME} ${PROJECT_BINARY_DIR}/${NAME}.js ${CMAKE_CURRENT_SOURCE_DIR}/${NAME}.html
|
||||
COMMAND cmake -E copy ${CMAKE_CURRENT_SOURCE_DIR}/${NAME}.html ${PROJECT_BINARY_DIR}/public
|
||||
COMMAND cmake -E copy ${PROJECT_BINARY_DIR}/${NAME}.* ${PROJECT_BINARY_DIR}/public)
|
||||
add_custom_target(${NAME}_public ALL DEPENDS ${PROJECT_BINARY_DIR}/public/${NAME}.js)
|
||||
|
||||
endfunction()
|
||||
|
||||
add_demo(suzanne)
|
||||
add_demo(triangle)
|
||||
add_demo(sandbox)
|
||||
@@ -1,346 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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 "filaweb.h"
|
||||
|
||||
#include <utils/Path.h>
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
#include <emscripten.h>
|
||||
|
||||
#include <image/KtxBundle.h>
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
using namespace filament;
|
||||
using namespace image;
|
||||
using namespace std;
|
||||
using namespace utils;
|
||||
|
||||
extern "C" void render() {
|
||||
filaweb::Application::get()->render();
|
||||
}
|
||||
|
||||
extern "C" void resize(uint32_t width, uint32_t height, double pixelRatio) {
|
||||
filaweb::Application::get()->resize(width, height, pixelRatio);
|
||||
}
|
||||
|
||||
extern "C" void mouse(int x, int y, int wx, int wy, int buttons) {
|
||||
// We are careful not to pass down negative numbers, doing so would cause a numeric
|
||||
// representation exception in WebAssembly.
|
||||
x = std::max(0, x);
|
||||
y = std::max(0, y);
|
||||
filaweb::Application::get()->mouse(x, y, wx, wy, buttons);
|
||||
}
|
||||
|
||||
namespace filaweb {
|
||||
|
||||
void Application::run(SetupCallback setup, AnimCallback animation, ImGuiCallback imgui) {
|
||||
mAnimation = animation;
|
||||
mGuiCallback = imgui;
|
||||
mEngine = Engine::create(Engine::Backend::OPENGL);
|
||||
mSwapChain = mEngine->createSwapChain(nullptr);
|
||||
mScene = mEngine->createScene();
|
||||
mRenderer = mEngine->createRenderer();
|
||||
mView = mEngine->createView();
|
||||
mView->setScene(mScene);
|
||||
mGuiCam = mEngine->createCamera();
|
||||
mGuiView = mEngine->createView();
|
||||
mGuiView->setClearTargets(false, false, false);
|
||||
mGuiView->setRenderTarget(View::TargetBufferFlags::DEPTH_AND_STENCIL);
|
||||
mGuiView->setPostProcessingEnabled(false);
|
||||
mGuiView->setShadowsEnabled(false);
|
||||
mGuiView->setCamera(mGuiCam);
|
||||
mGuiHelper = new filagui::ImGuiHelper(mEngine, mGuiView, "");
|
||||
setup(mEngine, mView, mScene);
|
||||
|
||||
// File I/O in WebAssembly does not exist, so tell ImGui to not bother with the ini file.
|
||||
ImGui::GetIO().IniFilename = nullptr;
|
||||
}
|
||||
|
||||
void Application::resize(uint32_t width, uint32_t height, double pixelRatio) {
|
||||
mPixelRatio = pixelRatio;
|
||||
mView->setViewport({0, 0, width, height});
|
||||
mGuiView->setViewport({0, 0, width, height});
|
||||
mManipulator.setViewport(width, height);
|
||||
mGuiCam->setProjection(filament::Camera::Projection::ORTHO,
|
||||
0.0, width / pixelRatio,
|
||||
height / pixelRatio, 0.0,
|
||||
0.0, 1.0);
|
||||
mGuiHelper->setDisplaySize(width / pixelRatio, height / pixelRatio, pixelRatio, pixelRatio);
|
||||
}
|
||||
|
||||
void Application::mouse(uint32_t x, uint32_t y, int32_t wx, int32_t wy, uint16_t buttons) {
|
||||
// First, pass the current pointer state to ImGui.
|
||||
auto& io = ImGui::GetIO();
|
||||
if (wx > 0) io.MouseWheelH += 1;
|
||||
if (wx < 0) io.MouseWheelH -= 1;
|
||||
if (wy > 0) io.MouseWheel += 1;
|
||||
if (wy < 0) io.MouseWheel -= 1;
|
||||
io.MousePos.x = x;
|
||||
io.MousePos.y = y;
|
||||
io.MouseDown[0] = buttons & 1;
|
||||
io.MouseDown[1] = buttons & 2;
|
||||
io.MouseDown[2] = buttons & 4;
|
||||
|
||||
// Negate Y before pushing values to the manipulator.
|
||||
y = -y;
|
||||
wy = -wy;
|
||||
|
||||
// Pass values to the camera manipulator to enable dolly and rotate.
|
||||
// We do not call call track() because two-button mouse usage is less useful on web.
|
||||
using namespace math;
|
||||
static double2 previousMousePosition = double2(x, y);
|
||||
static uint16_t previousMouseButtons = buttons;
|
||||
double2 delta = double2(x, y) - previousMousePosition;
|
||||
previousMousePosition = double2(x, y);
|
||||
mManipulator.dolly(wy);
|
||||
if (!io.WantCaptureMouse && buttons == 1 && buttons == previousMouseButtons) {
|
||||
mManipulator.rotate(delta);
|
||||
}
|
||||
previousMouseButtons = buttons;
|
||||
}
|
||||
|
||||
void Application::render() {
|
||||
using namespace std::chrono;
|
||||
|
||||
mManipulator.updateCameraTransform();
|
||||
|
||||
auto milliseconds_since_epoch = system_clock::now().time_since_epoch() / milliseconds(1);
|
||||
mAnimation(mEngine, mView, milliseconds_since_epoch / 1000.0);
|
||||
|
||||
double now = milliseconds_since_epoch / 1000.0;
|
||||
static double previous = now;
|
||||
mGuiHelper->render(now - previous, mGuiCallback);
|
||||
previous = now;
|
||||
|
||||
if (mRenderer->beginFrame(mSwapChain)) {
|
||||
mRenderer->render(mView);
|
||||
mRenderer->render(mGuiView);
|
||||
mRenderer->endFrame();
|
||||
}
|
||||
mEngine->execute();
|
||||
}
|
||||
|
||||
Asset getRawFile(const char* name) {
|
||||
// Obtain size and URL from JavaScript.
|
||||
Asset result = {};
|
||||
EM_ASM({
|
||||
var nbytes = $0 >> 2;
|
||||
var name = UTF8ToString($1);
|
||||
HEAP32[nbytes] = assets[name].data.byteLength;
|
||||
stringToUTF8(assets[name].url, $2, $3);
|
||||
}, &result.rawSize, name, &result.rawUrl[0], sizeof(result.rawUrl));
|
||||
|
||||
// Move the data from JavaScript.
|
||||
uint8_t* data = new uint8_t[result.rawSize];
|
||||
EM_ASM({
|
||||
var data = $0;
|
||||
var name = UTF8ToString($1);
|
||||
HEAPU8.set(assets[name].data, data);
|
||||
assets[name].data = null;
|
||||
}, data, name);
|
||||
result.rawData = decltype(Asset::rawData)(data);
|
||||
return result;
|
||||
}
|
||||
|
||||
static Asset getKtxTexture(const Asset& rawfile) {
|
||||
Asset result = {};
|
||||
result.texture.reset(new KtxBundle(rawfile.rawData.get(), rawfile.rawSize));
|
||||
return result;
|
||||
}
|
||||
|
||||
Asset getTexture(const char* name) {
|
||||
Asset rawfile = getRawFile(name);
|
||||
return getKtxTexture(rawfile);
|
||||
}
|
||||
|
||||
Asset getCubemap(const char* name) {
|
||||
string prefix = string(name) + "/";
|
||||
|
||||
// Deserialize the KtxBundle for the IBL.
|
||||
Asset* envFaces = new Asset;
|
||||
string key = prefix + "ibl";
|
||||
*envFaces = getKtxTexture(getRawFile(key.c_str()));
|
||||
|
||||
// Ditto but for the blurry sky.
|
||||
Asset* skyFaces = new Asset;
|
||||
key = prefix + "skybox";
|
||||
*skyFaces = getKtxTexture(getRawFile(key.c_str()));
|
||||
|
||||
Asset result = {};
|
||||
result.envIBL.reset(envFaces);
|
||||
result.envSky.reset(skyFaces);
|
||||
return result;
|
||||
}
|
||||
|
||||
static Texture* createCubemapTexture(filament::Engine& engine, image::KtxBundle* ktxbundle) {
|
||||
auto info = ktxbundle->getInfo();
|
||||
uint32_t nmips = ktxbundle->getNumMipLevels();
|
||||
auto builder = Texture::Builder()
|
||||
.width(info.pixelWidth)
|
||||
.height(info.pixelHeight)
|
||||
.levels(nmips)
|
||||
.rgbm(true)
|
||||
.sampler(Texture::Sampler::SAMPLER_CUBEMAP);
|
||||
|
||||
// Compressed textures in KTX always have a glFormat of 0.
|
||||
if (info.glFormat == 0) {
|
||||
builder.format(filaweb::toTextureFormat(info.glInternalFormat));
|
||||
} else {
|
||||
builder.format(Texture::InternalFormat::RGBA8);
|
||||
}
|
||||
Texture* texture = builder.build(engine);
|
||||
size_t size = info.pixelWidth;
|
||||
uint32_t i = 0;
|
||||
for (uint32_t mip = 0; mip < nmips; ++mip, size >>= 1) {
|
||||
|
||||
uint8_t* ktxPixels;
|
||||
uint32_t faceSize;
|
||||
ktxbundle->getBlob({mip}, &ktxPixels, &faceSize);
|
||||
|
||||
Texture::FaceOffsets offsets;
|
||||
offsets.px = faceSize * 0;
|
||||
offsets.nx = faceSize * 1;
|
||||
offsets.py = faceSize * 2;
|
||||
offsets.ny = faceSize * 3;
|
||||
offsets.pz = faceSize * 4;
|
||||
offsets.nz = faceSize * 5;
|
||||
|
||||
// If this is a compressed texture, use a special PixelBufferDescriptor constructor.
|
||||
Texture::PixelBufferDescriptor* buffer;
|
||||
if (info.glFormat == 0) {
|
||||
auto datatype = filaweb::toPixelDataType(info.glInternalFormat);
|
||||
buffer = new Texture::PixelBufferDescriptor(
|
||||
malloc(faceSize * 6), faceSize * 6,
|
||||
datatype, faceSize,
|
||||
[](void* buffer, size_t size, void* user) {
|
||||
free(buffer);
|
||||
}, /* user = */ nullptr);
|
||||
} else {
|
||||
buffer = new Texture::PixelBufferDescriptor(
|
||||
malloc(faceSize * 6), faceSize * 6,
|
||||
Texture::Format::RGBM, Texture::Type::UBYTE,
|
||||
[](void* buffer, size_t size, void* user) {
|
||||
free(buffer);
|
||||
}, /* user = */ nullptr);
|
||||
}
|
||||
|
||||
// Copy 6 faces from the KTX bundle into the Filament pixel buffer.
|
||||
uint8_t* pbdPixels = static_cast<uint8_t*>(buffer->buffer);
|
||||
memcpy(pbdPixels, ktxPixels, faceSize * 6);
|
||||
texture->setImage(engine, mip, std::move(*buffer), offsets);
|
||||
delete buffer;
|
||||
}
|
||||
return texture;
|
||||
}
|
||||
|
||||
SkyLight getSkyLight(Engine& engine, const char* name) {
|
||||
SkyLight result;
|
||||
using size_t = std::size_t;
|
||||
|
||||
// Pull the data out of JavaScript.
|
||||
static auto asset = filaweb::getCubemap(name);
|
||||
|
||||
// Parse the coefficients.
|
||||
const char* shtext = asset.envIBL->texture->getMetadata("sh");
|
||||
if (!shtext) {
|
||||
cerr << name << " is missing spherical harmonics coefficients." << endl;
|
||||
abort();
|
||||
}
|
||||
std::istringstream shReader(shtext);
|
||||
shReader >> std::skipws;
|
||||
for (size_t i = 0; i < 9; i++) {
|
||||
shReader >> result.bands[i].r >> result.bands[i].g >> result.bands[i].b;
|
||||
}
|
||||
|
||||
// Copy over the miplevels for the indirect light.
|
||||
Texture* texture = createCubemapTexture(engine, asset.envIBL->texture.get());
|
||||
asset.envIBL->texture.reset();
|
||||
|
||||
result.indirectLight = IndirectLight::Builder()
|
||||
.reflections(texture)
|
||||
.irradiance(3, result.bands)
|
||||
.intensity(30000.0f)
|
||||
.build(engine);
|
||||
|
||||
// Copy a single miplevel for the blurry skybox
|
||||
Texture* skybox = createCubemapTexture(engine, asset.envSky->texture.get());
|
||||
asset.envSky->texture.reset();
|
||||
result.skybox = Skybox::Builder().environment(skybox).build(engine);
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T toFilamentEnum(uint32_t format) {
|
||||
switch (format) {
|
||||
case KtxBundle::RGB_S3TC_DXT1: return T::DXT1_RGB;
|
||||
case KtxBundle::RGBA_S3TC_DXT1: return T::DXT1_RGBA;
|
||||
case KtxBundle::RGBA_S3TC_DXT3: return T::DXT3_RGBA;
|
||||
case KtxBundle::RGBA_S3TC_DXT5: return T::DXT5_RGBA;
|
||||
case KtxBundle::RGBA_ASTC_4x4: return T::RGBA_ASTC_4x4;
|
||||
case KtxBundle::RGBA_ASTC_5x4: return T::RGBA_ASTC_5x4;
|
||||
case KtxBundle::RGBA_ASTC_5x5: return T::RGBA_ASTC_5x5;
|
||||
case KtxBundle::RGBA_ASTC_6x5: return T::RGBA_ASTC_6x5;
|
||||
case KtxBundle::RGBA_ASTC_6x6: return T::RGBA_ASTC_6x6;
|
||||
case KtxBundle::RGBA_ASTC_8x5: return T::RGBA_ASTC_8x5;
|
||||
case KtxBundle::RGBA_ASTC_8x6: return T::RGBA_ASTC_8x6;
|
||||
case KtxBundle::RGBA_ASTC_8x8: return T::RGBA_ASTC_8x8;
|
||||
case KtxBundle::RGBA_ASTC_10x5: return T::RGBA_ASTC_10x5;
|
||||
case KtxBundle::RGBA_ASTC_10x6: return T::RGBA_ASTC_10x6;
|
||||
case KtxBundle::RGBA_ASTC_10x8: return T::RGBA_ASTC_10x8;
|
||||
case KtxBundle::RGBA_ASTC_10x10: return T::RGBA_ASTC_10x10;
|
||||
case KtxBundle::RGBA_ASTC_12x10: return T::RGBA_ASTC_12x10;
|
||||
case KtxBundle::RGBA_ASTC_12x12: return T::RGBA_ASTC_12x12;
|
||||
case KtxBundle::SRGB8_ALPHA8_ASTC_4x4: return T::SRGB8_ALPHA8_ASTC_4x4;
|
||||
case KtxBundle::SRGB8_ALPHA8_ASTC_5x4: return T::SRGB8_ALPHA8_ASTC_5x4;
|
||||
case KtxBundle::SRGB8_ALPHA8_ASTC_5x5: return T::SRGB8_ALPHA8_ASTC_5x5;
|
||||
case KtxBundle::SRGB8_ALPHA8_ASTC_6x5: return T::SRGB8_ALPHA8_ASTC_6x5;
|
||||
case KtxBundle::SRGB8_ALPHA8_ASTC_6x6: return T::SRGB8_ALPHA8_ASTC_6x6;
|
||||
case KtxBundle::SRGB8_ALPHA8_ASTC_8x5: return T::SRGB8_ALPHA8_ASTC_8x5;
|
||||
case KtxBundle::SRGB8_ALPHA8_ASTC_8x6: return T::SRGB8_ALPHA8_ASTC_8x6;
|
||||
case KtxBundle::SRGB8_ALPHA8_ASTC_8x8: return T::SRGB8_ALPHA8_ASTC_8x8;
|
||||
case KtxBundle::SRGB8_ALPHA8_ASTC_10x5: return T::SRGB8_ALPHA8_ASTC_10x5;
|
||||
case KtxBundle::SRGB8_ALPHA8_ASTC_10x6: return T::SRGB8_ALPHA8_ASTC_10x6;
|
||||
case KtxBundle::SRGB8_ALPHA8_ASTC_10x8: return T::SRGB8_ALPHA8_ASTC_10x8;
|
||||
case KtxBundle::SRGB8_ALPHA8_ASTC_10x10: return T::SRGB8_ALPHA8_ASTC_10x10;
|
||||
case KtxBundle::SRGB8_ALPHA8_ASTC_12x10: return T::SRGB8_ALPHA8_ASTC_12x10;
|
||||
case KtxBundle::SRGB8_ALPHA8_ASTC_12x12: return T::SRGB8_ALPHA8_ASTC_12x12;
|
||||
case KtxBundle::R11_EAC: return T::EAC_R11;
|
||||
case KtxBundle::SIGNED_R11_EAC: return T::EAC_R11_SIGNED;
|
||||
case KtxBundle::RG11_EAC: return T::EAC_RG11;
|
||||
case KtxBundle::SIGNED_RG11_EAC: return T::EAC_RG11_SIGNED;
|
||||
case KtxBundle::RGB8_ETC2: return T::ETC2_RGB8;
|
||||
case KtxBundle::SRGB8_ETC2: return T::ETC2_SRGB8;
|
||||
case KtxBundle::RGB8_ALPHA1_ETC2: return T::ETC2_RGB8_A1;
|
||||
case KtxBundle::SRGB8_ALPHA1_ETC: return T::ETC2_SRGB8_A1;
|
||||
case KtxBundle::RGBA8_ETC2_EAC: return T::ETC2_EAC_RGBA8;
|
||||
case KtxBundle::SRGB8_ALPHA8_ETC2_EAC: return T::ETC2_EAC_SRGBA8;
|
||||
}
|
||||
return (T) 0xffff;
|
||||
}
|
||||
|
||||
filament::driver::CompressedPixelDataType toPixelDataType(uint32_t format) {
|
||||
return toFilamentEnum<filament::driver::CompressedPixelDataType>(format);
|
||||
}
|
||||
|
||||
filament::driver::TextureFormat toTextureFormat(uint32_t format) {
|
||||
return toFilamentEnum<filament::driver::TextureFormat>(format);
|
||||
}
|
||||
|
||||
} // namespace filaweb
|
||||
@@ -1,107 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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_FILAMENT_SAMPLES_FILAWEB_H
|
||||
#define TNT_FILAMENT_SAMPLES_FILAWEB_H
|
||||
|
||||
#include "../app/CameraManipulator.h"
|
||||
|
||||
#include <image/KtxBundle.h>
|
||||
|
||||
#include <filagui/ImGuiHelper.h>
|
||||
|
||||
#include <filament/Camera.h>
|
||||
#include <filament/Engine.h>
|
||||
#include <filament/IndirectLight.h>
|
||||
#include <filament/Renderer.h>
|
||||
#include <filament/Scene.h>
|
||||
#include <filament/Skybox.h>
|
||||
#include <filament/Texture.h>
|
||||
#include <filament/View.h>
|
||||
#include <filament/Viewport.h>
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace filaweb {
|
||||
|
||||
// Filaweb defines three kinds of assets: textures, environments, and raw files.
|
||||
// For simplicity all three kinds are represented with a single struct.
|
||||
struct Asset {
|
||||
std::unique_ptr<image::KtxBundle> texture;
|
||||
std::unique_ptr<Asset> envIBL;
|
||||
std::unique_ptr<Asset> envSky;
|
||||
std::unique_ptr<uint8_t[]> rawData;
|
||||
uint32_t rawSize;
|
||||
char rawUrl[256];
|
||||
};
|
||||
|
||||
Asset getRawFile(const char* name);
|
||||
Asset getTexture(const char* name);
|
||||
Asset getCubemap(const char* name);
|
||||
|
||||
struct SkyLight {
|
||||
math::float3 bands[9];
|
||||
filament::IndirectLight* indirectLight;
|
||||
filament::Skybox* skybox;
|
||||
};
|
||||
|
||||
SkyLight getSkyLight(filament::Engine& engine, const char* name);
|
||||
|
||||
filament::driver::CompressedPixelDataType toPixelDataType(uint32_t format);
|
||||
filament::driver::TextureFormat toTextureFormat(uint32_t format);
|
||||
|
||||
static const auto NoopCallback = [](filament::Engine*, filament::View*) {};
|
||||
|
||||
class Application {
|
||||
public:
|
||||
using Engine = filament::Engine;
|
||||
using View = filament::View;
|
||||
using Scene = filament::Scene;
|
||||
using SetupCallback = std::function<void(Engine*, View*, Scene*)>;
|
||||
using ImGuiCallback = std::function<void(Engine*, View*)>;
|
||||
using AnimCallback = std::function<void(Engine*, View*, double now)>;
|
||||
|
||||
static Application* get() {
|
||||
static Application app;
|
||||
return &app;
|
||||
}
|
||||
|
||||
void run(SetupCallback setup, AnimCallback animation, ImGuiCallback imgui = NoopCallback);
|
||||
void resize(uint32_t width, uint32_t height, double pixelRatio);
|
||||
void mouse(uint32_t x, uint32_t y, int32_t wx, int32_t wy, uint16_t buttons);
|
||||
void render();
|
||||
|
||||
CameraManipulator& getManipulator() { return mManipulator; }
|
||||
|
||||
private:
|
||||
Application() { }
|
||||
filagui::ImGuiHelper* mGuiHelper = nullptr;
|
||||
Engine* mEngine = nullptr;
|
||||
Scene* mScene = nullptr;
|
||||
View* mView = nullptr;
|
||||
View* mGuiView = nullptr;
|
||||
filament::Camera* mGuiCam = nullptr;
|
||||
filament::Renderer* mRenderer = nullptr;
|
||||
filament::SwapChain* mSwapChain = nullptr;
|
||||
CameraManipulator mManipulator;
|
||||
AnimCallback mAnimation;
|
||||
ImGuiCallback mGuiCallback;
|
||||
double mPixelRatio = 1.0;
|
||||
};
|
||||
|
||||
} // namespace filaweb
|
||||
|
||||
#endif // TNT_FILAMENT_SAMPLES_FILAWEB_H
|
||||
@@ -1,141 +0,0 @@
|
||||
let assets = {};
|
||||
let assets_ready = false;
|
||||
let context_ready = false;
|
||||
let previous_mouse_buttons = 0;
|
||||
let queued_mouse_events = [];
|
||||
|
||||
let use_astc = false;
|
||||
let use_s3tc = false;
|
||||
let use_etc = false;
|
||||
|
||||
let canvas = document.getElementById('filament-canvas');
|
||||
let ctx = GL.createContext(canvas, {
|
||||
majorVersion: 2,
|
||||
minorVersion: 0,
|
||||
antialias: false,
|
||||
depth: false,
|
||||
alpha: false
|
||||
});
|
||||
GL.makeContextCurrent(ctx);
|
||||
for (let ext of Module.ctx.getSupportedExtensions()) {
|
||||
if (ext == "WEBGL_compressed_texture_s3tc") {
|
||||
Module.ctx.getExtension('WEBGL_compressed_texture_s3tc');
|
||||
use_s3tc = true;
|
||||
} else if (ext == "WEBGL_compressed_texture_astc") {
|
||||
Module.ctx.getExtension('WEBGL_compressed_texture_astc');
|
||||
use_astc = true;
|
||||
} else if (ext == "WEBGL_compressed_texture_etc") {
|
||||
Module.ctx.getExtension('WEBGL_compressed_texture_etc');
|
||||
use_etc = true;
|
||||
}
|
||||
}
|
||||
|
||||
// This is usually (not always) called before the wasm has finished JIT compiling.
|
||||
async function load(promises) {
|
||||
for (let name in promises) {
|
||||
assets[name] = await promises[name];
|
||||
}
|
||||
assets_ready = true;
|
||||
maybe_launch();
|
||||
}
|
||||
|
||||
// This is called as soon as the wasm has finished JIT compilation.
|
||||
Module.postRun = function() {
|
||||
context_ready = true;
|
||||
maybe_launch();
|
||||
}
|
||||
|
||||
// This is called twice: once after assets have loaded, and once after WASM compilation. We try to
|
||||
// be robust with either order, launching the C++ code only after both requirements are fulfilled.
|
||||
function maybe_launch() {
|
||||
if (assets_ready && context_ready) {
|
||||
_launch();
|
||||
canvas_resize();
|
||||
window.addEventListener("resize", canvas_resize);
|
||||
let canvas = document.getElementById('filament-canvas');
|
||||
canvas.addEventListener("wheel", canvas_mouse);
|
||||
canvas.addEventListener("pointermove", canvas_mouse);
|
||||
canvas.addEventListener("pointerdown", canvas_mouse);
|
||||
canvas.addEventListener("pointerup", canvas_mouse);
|
||||
canvas_render();
|
||||
}
|
||||
}
|
||||
|
||||
// Update a tiny queue of pointer events. We don't send them to WASM immediately because ImGui
|
||||
// detects click events by looking for three consecutive frames of down-up-down.
|
||||
function canvas_mouse(evt) {
|
||||
if (evt.pointerType == 'touch' && !evt.isPrimary) {
|
||||
return;
|
||||
}
|
||||
let args = [evt.clientX, evt.clientY, evt.deltaX || 0, evt.deltaY || 0, evt.buttons];
|
||||
if (evt.buttons != previous_mouse_buttons || queued_mouse_events.length == 0) {
|
||||
queued_mouse_events.push(args);
|
||||
} else {
|
||||
// Clobber the previous event if the button state doesn't change, there's no point in
|
||||
// creating latency.
|
||||
queued_mouse_events[queued_mouse_events.length - 1] = args;
|
||||
}
|
||||
previous_mouse_buttons = evt.buttons;
|
||||
}
|
||||
|
||||
function canvas_render() {
|
||||
if (queued_mouse_events.length > 0) {
|
||||
let args = queued_mouse_events.splice(0, 1)[0];
|
||||
_mouse(args[0], args[1], args[2], args[3], args[4]);
|
||||
}
|
||||
_render();
|
||||
window.requestAnimationFrame(canvas_render);
|
||||
}
|
||||
|
||||
function canvas_resize() {
|
||||
let canvas = document.getElementById('filament-canvas');
|
||||
let pr = window.devicePixelRatio;
|
||||
let w = window.innerWidth;
|
||||
let h = window.innerHeight;
|
||||
canvas.width = (w * pr) | 0;
|
||||
canvas.height = (h * pr) | 0;
|
||||
_resize((w * pr) | 0, (h * pr) | 0, pr);
|
||||
}
|
||||
|
||||
function load_rawfile(url) {
|
||||
let promise = new Promise((success, failure) => {
|
||||
fetch(url).then(resp => {
|
||||
resp.arrayBuffer().then(filedata => success({
|
||||
kind: 'rawfile',
|
||||
url: url,
|
||||
data: new Uint8Array(filedata)
|
||||
}));
|
||||
});
|
||||
});
|
||||
return promise;
|
||||
}
|
||||
|
||||
let load_texture = load_rawfile;
|
||||
|
||||
function load_cubemap(name, suffix) {
|
||||
let compressed_suffix = suffix;
|
||||
if (use_etc) {
|
||||
compressed_suffix = '_etc' + suffix;
|
||||
} else if (use_s3tc) {
|
||||
compressed_suffix = '_s3tc' + suffix;
|
||||
}
|
||||
let urlprefix = name + '/';
|
||||
let promises = {};
|
||||
promises['ibl'] = load_rawfile(urlprefix + name + '_ibl' + compressed_suffix);
|
||||
promises['skybox'] = load_rawfile(urlprefix + name + '_skybox' + suffix);
|
||||
let numberRemaining = Object.keys(promises).length;
|
||||
var promise = new Promise((success) => {
|
||||
for (let key in promises) {
|
||||
promises[key].then((result) => {
|
||||
assets[urlprefix + key] = result;
|
||||
if (--numberRemaining == 0) {
|
||||
success({
|
||||
kind: 'cubemap',
|
||||
name: urlprefix,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
return promise;
|
||||
}
|
||||
@@ -1,193 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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 "../material_sandbox.h"
|
||||
|
||||
#include "filaweb.h"
|
||||
|
||||
#include <filament/Engine.h>
|
||||
#include <filament/Material.h>
|
||||
#include <filament/MaterialInstance.h>
|
||||
#include <filament/RenderableManager.h>
|
||||
#include <filament/Scene.h>
|
||||
#include <filament/View.h>
|
||||
|
||||
#include <filameshio/MeshIO.h>
|
||||
|
||||
#include <math/vec3.h>
|
||||
|
||||
#include <utils/Entity.h>
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
#include <chrono>
|
||||
|
||||
using namespace filament;
|
||||
using namespace filagui;
|
||||
using namespace math;
|
||||
using namespace utils;
|
||||
|
||||
using MagFilter = TextureSampler::MagFilter;
|
||||
using WrapMode = TextureSampler::WrapMode;
|
||||
using Format = Texture::InternalFormat;
|
||||
|
||||
struct SandboxApp {
|
||||
MeshIO::Mesh filamesh;
|
||||
Camera* cam;
|
||||
SandboxParameters params;
|
||||
filaweb::SkyLight skylight;
|
||||
Scene* scene;
|
||||
};
|
||||
|
||||
static SandboxApp app;
|
||||
|
||||
void setup(Engine* engine, View* view, Scene* scene) {
|
||||
app.scene = scene;
|
||||
|
||||
// These initial values seem reasonable with the "pillars_2k" envmap.
|
||||
app.params.iblIntensity = 10000.0f;
|
||||
app.params.lightDirection.x = 1;
|
||||
app.params.lightDirection.y = -1;
|
||||
app.params.lightDirection.z = -0.4;
|
||||
app.params.iblRotation = 289 * M_PI / 180;
|
||||
|
||||
// These initial values look pretty nice with the shaderball.
|
||||
app.params.clearCoat = 0.7f;
|
||||
app.params.metallic = 0.0f;
|
||||
app.params.reflectance = 0.5f;
|
||||
app.params.color.x = 158 / 255.0f;
|
||||
app.params.color.y = 118 / 255.0f;
|
||||
app.params.color.z = 74 / 255.0f;
|
||||
|
||||
// Create material.
|
||||
createInstances(app.params, *engine);
|
||||
|
||||
// Move raw asset data from JavaScript to C++ static storage. Their held data will be freed via
|
||||
// BufferDescriptor callbacks after Filament creates the corresponding GPU objects.
|
||||
static auto mesh = filaweb::getRawFile("mesh");
|
||||
|
||||
// Create mesh.
|
||||
const uint8_t* mdata = mesh.rawData.get();
|
||||
const auto destructor = [](void* buffer, size_t size, void* user) {
|
||||
// This destruction lambda is called for the vertex buffer AND index buffer, so release CPU
|
||||
// memory only after both have been uploaded to the GPU. The static count trick here works
|
||||
// only because we create a single mesh in this demo.
|
||||
static int count = 0;
|
||||
if (++count == 2) {
|
||||
auto asset = (filaweb::Asset*) user;
|
||||
asset->rawData.reset();
|
||||
}
|
||||
};
|
||||
MaterialInstance* materialInstance = app.params.materialInstance[MATERIAL_LIT];
|
||||
app.filamesh = MeshIO::loadMeshFromBuffer(engine, mdata, destructor, &mesh, materialInstance);
|
||||
scene->addEntity(app.filamesh.renderable);
|
||||
|
||||
// Create the sun.
|
||||
scene->addEntity(app.params.light);
|
||||
|
||||
// Create skybox and image-based light source.
|
||||
app.skylight = filaweb::getSkyLight(*engine, "pillars_2k");
|
||||
scene->setIndirectLight(app.skylight.indirectLight);
|
||||
scene->setSkybox(app.skylight.skybox);
|
||||
|
||||
app.cam = engine->createCamera();
|
||||
app.cam->setExposure(16.0f, 1 / 125.0f, 100.0f);
|
||||
view->setCamera(app.cam);
|
||||
|
||||
// Set up an interesting orientation for the shader ball.
|
||||
auto& manip = filaweb::Application::get()->getManipulator();
|
||||
manip.setCamera(app.cam);
|
||||
manip.lookAt(math::double3({0, 1, 6}), math::double3({0, 1, 0}));
|
||||
};
|
||||
|
||||
void animate(Engine* engine, View* view, double now) {
|
||||
static double previous = now;
|
||||
|
||||
// Adjust camera on every frame in case window size changes.
|
||||
using Fov = Camera::Fov;
|
||||
const uint32_t width = view->getViewport().width;
|
||||
const uint32_t height = view->getViewport().height;
|
||||
double ratio = double(width) / height;
|
||||
app.cam->setProjection(45.0, ratio, 0.1, 50.0, ratio < 1 ? Fov::HORIZONTAL : Fov::VERTICAL);
|
||||
};
|
||||
|
||||
void ui(Engine* engine, View* view) {
|
||||
auto& params = app.params;
|
||||
// Initial window size and position are somewhat optimized for a Pixel 2 portrait screen.
|
||||
ImGui::SetNextWindowSize(ImVec2(300.0f, 150.0f), ImGuiCond_Always);
|
||||
ImGui::SetNextWindowPos(ImVec2(8, 6), ImGuiCond_Once);
|
||||
ImGui::Begin("Material", nullptr, ImGuiWindowFlags_NoResize);
|
||||
{
|
||||
ImGui::ColorEdit3("baseColor", ¶ms.color.r);
|
||||
if (params.currentMaterialModel > MATERIAL_MODEL_UNLIT) {
|
||||
if (params.currentBlending == BLENDING_TRANSPARENT ||
|
||||
params.currentBlending == BLENDING_FADE) {
|
||||
ImGui::SliderFloat("alpha", ¶ms.alpha, 0.0f, 1.0f);
|
||||
}
|
||||
ImGui::SliderFloat("roughness", ¶ms.roughness, 0.0f, 1.0f);
|
||||
if (params.currentMaterialModel != MATERIAL_MODEL_CLOTH) {
|
||||
ImGui::SliderFloat("metallic", ¶ms.metallic, 0.0f, 1.0f);
|
||||
ImGui::SliderFloat("reflectance", ¶ms.reflectance, 0.0f, 1.0f);
|
||||
}
|
||||
if (params.currentMaterialModel != MATERIAL_MODEL_CLOTH &&
|
||||
params.currentMaterialModel != MATERIAL_MODEL_SUBSURFACE) {
|
||||
ImGui::SliderFloat("clearCoat", ¶ms.clearCoat, 0.0f, 1.0f);
|
||||
}
|
||||
if (params.currentMaterialModel == MATERIAL_MODEL_SUBSURFACE) {
|
||||
ImGui::SliderFloat("thickness", ¶ms.thickness, 0.0f, 1.0f);
|
||||
ImGui::SliderFloat("subsurfacePower", ¶ms.subsurfacePower, 1.0f, 24.0f);
|
||||
ImGui::ColorEdit3("subsurfaceColor", ¶ms.subsurfaceColor.r);
|
||||
}
|
||||
if (params.currentMaterialModel == MATERIAL_MODEL_CLOTH) {
|
||||
ImGui::ColorEdit3("sheenColor", ¶ms.sheenColor.r);
|
||||
ImGui::ColorEdit3("subsurfaceColor", ¶ms.subsurfaceColor.r);
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
MaterialInstance* materialInstance = updateInstances(params, *engine);
|
||||
|
||||
auto& rcm = engine->getRenderableManager();
|
||||
auto instance = rcm.getInstance(app.filamesh.renderable);
|
||||
for (size_t i = 0; i < rcm.getPrimitiveCount(instance); i++) {
|
||||
rcm.setMaterialInstanceAt(instance, i, materialInstance);
|
||||
}
|
||||
rcm.setCastShadows(instance, params.castShadows);
|
||||
|
||||
if (params.directionalLightEnabled && !params.hasDirectionalLight) {
|
||||
app.scene->addEntity(params.light);
|
||||
params.hasDirectionalLight = true;
|
||||
} else if (!params.directionalLightEnabled && params.hasDirectionalLight) {
|
||||
app.scene->remove(params.light);
|
||||
params.hasDirectionalLight = false;
|
||||
}
|
||||
|
||||
app.skylight.indirectLight->setIntensity(params.iblIntensity);
|
||||
app.skylight.indirectLight->setRotation(
|
||||
mat3f::rotate(params.iblRotation, float3{ 0, 1, 0 }));
|
||||
}
|
||||
|
||||
// This is called only after the JavaScript layer has created a WebGL 2.0 context and all assets
|
||||
// have been downloaded.
|
||||
extern "C" void launch() {
|
||||
filaweb::Application::get()->run(setup, animate, ui);
|
||||
}
|
||||
|
||||
// The main() entry point is implicitly called after JIT compilation, but potentially before the
|
||||
// WebGL context has been created or assets have finished loading.
|
||||
int main() { }
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Sandbox</title>
|
||||
<link href="favicon.png" rel="icon" type="image/x-icon"/>
|
||||
<meta name="viewport" content="width=device-width,user-scalable=no,initial-scale=1">
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
#filament-canvas {
|
||||
touch-action: none;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="filament-canvas"></canvas>
|
||||
<script src="sandbox.js"></script>
|
||||
<script src="filaweb.js"></script>
|
||||
<script>
|
||||
load({
|
||||
'mesh': load_rawfile('shaderball/mesh.filamesh'),
|
||||
'pillars_2k': load_cubemap('pillars_2k', '.ktx')
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,268 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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 "filaweb.h"
|
||||
|
||||
#include <filament/Engine.h>
|
||||
#include <filament/IndexBuffer.h>
|
||||
#include <filament/LightManager.h>
|
||||
#include <filament/Material.h>
|
||||
#include <filament/MaterialInstance.h>
|
||||
#include <filament/RenderableManager.h>
|
||||
#include <filament/Scene.h>
|
||||
#include <filament/TransformManager.h>
|
||||
#include <filament/VertexBuffer.h>
|
||||
#include <filament/View.h>
|
||||
|
||||
#include <filameshio/MeshIO.h>
|
||||
|
||||
#include <utils/Entity.h>
|
||||
#include <utils/EntityManager.h>
|
||||
|
||||
#include <math/vec3.h>
|
||||
|
||||
using namespace filament;
|
||||
using namespace math;
|
||||
using namespace std;
|
||||
using namespace utils;
|
||||
|
||||
using MagFilter = TextureSampler::MagFilter;
|
||||
using WrapMode = TextureSampler::WrapMode;
|
||||
using Format = Texture::InternalFormat;
|
||||
|
||||
struct SuzanneApp {
|
||||
MeshIO::Mesh filamesh;
|
||||
Material* mat;
|
||||
MaterialInstance* mi;
|
||||
Camera* cam;
|
||||
Entity sun;
|
||||
Entity ptlight[4];
|
||||
};
|
||||
|
||||
static constexpr uint8_t MATERIAL_LIT_PACKAGE[] = {
|
||||
#include "generated/material/texturedLit.inc"
|
||||
};
|
||||
|
||||
static SuzanneApp app;
|
||||
|
||||
static Texture* setTextureParameter(Engine& engine, filaweb::Asset& asset, string name, bool linear,
|
||||
TextureSampler const &sampler) {
|
||||
const auto& info = asset.texture->getInfo();
|
||||
const uint32_t nmips = asset.texture->getNumMipLevels();
|
||||
|
||||
// This little structure tracks how many miplevels have been uploaded to the GPU so that we can
|
||||
// free the CPU memory at the right time.
|
||||
struct Uploader {
|
||||
filaweb::Asset* asset;
|
||||
uint32_t refcount;
|
||||
};
|
||||
|
||||
Uploader* uploader = new Uploader {&asset, nmips};
|
||||
|
||||
const auto destroy = [](void* buffer, size_t size, void* user) {
|
||||
auto uploader = (Uploader*) user;
|
||||
if (--uploader->refcount == 0) {
|
||||
uploader->asset->texture.reset();
|
||||
delete uploader;
|
||||
}
|
||||
};
|
||||
|
||||
uint8_t* data;
|
||||
uint32_t nbytes;
|
||||
|
||||
// Compressed textures in KTX always have a glFormat of 0.
|
||||
if (info.glFormat == 0) {
|
||||
assert(info.pixelWidth == info.pixelHeight);
|
||||
driver::CompressedPixelDataType datatype = filaweb::toPixelDataType(info.glInternalFormat);
|
||||
driver::TextureFormat texformat = filaweb::toTextureFormat(info.glInternalFormat);
|
||||
|
||||
auto texture = Texture::Builder()
|
||||
.width(info.pixelWidth)
|
||||
.height(info.pixelHeight)
|
||||
.levels(nmips)
|
||||
.sampler(Texture::Sampler::SAMPLER_2D)
|
||||
.format(texformat)
|
||||
.build(engine);
|
||||
|
||||
for (uint32_t level = 0; level < nmips; ++level) {
|
||||
asset.texture->getBlob({level}, &data, &nbytes);
|
||||
Texture::PixelBufferDescriptor pb(data, nbytes, datatype, nbytes, destroy, uploader);
|
||||
texture->setImage(engine, level, std::move(pb));
|
||||
}
|
||||
app.mi->setParameter(name.c_str(), texture, sampler);
|
||||
return texture;
|
||||
}
|
||||
|
||||
Texture::Format format;
|
||||
switch (info.glTypeSize) {
|
||||
case 1: format = Texture::Format::R; break;
|
||||
case 2: format = Texture::Format::RG; break;
|
||||
case 3: format = Texture::Format::RGB; break;
|
||||
case 4: format = Texture::Format::RGBA; break;
|
||||
}
|
||||
|
||||
Format internalFormat;
|
||||
switch (info.glTypeSize) {
|
||||
case 1: internalFormat = Format::R8; break;
|
||||
case 2: internalFormat = Format::RG8; break;
|
||||
case 3: internalFormat = linear ? Format::RGB8 : Format::SRGB8; break;
|
||||
case 4: internalFormat = linear ? Format::RGBA8 : Format::SRGB8_A8; break;
|
||||
}
|
||||
|
||||
auto texture = Texture::Builder()
|
||||
.width(info.pixelWidth)
|
||||
.height(info.pixelHeight)
|
||||
.levels(nmips)
|
||||
.sampler(Texture::Sampler::SAMPLER_2D)
|
||||
.format(internalFormat)
|
||||
.build(engine);
|
||||
|
||||
for (uint32_t level = 0; level < nmips; ++level) {
|
||||
asset.texture->getBlob({level}, &data, &nbytes);
|
||||
Texture::PixelBufferDescriptor pb(data, nbytes, format, Texture::Type::UBYTE,
|
||||
destroy, uploader);
|
||||
texture->setImage(engine, level, std::move(pb));
|
||||
}
|
||||
|
||||
app.mi->setParameter(name.c_str(), texture, sampler);
|
||||
return texture;
|
||||
}
|
||||
|
||||
void setup(Engine* engine, View* view, Scene* scene) {
|
||||
|
||||
// Create material.
|
||||
app.mat = Material::Builder()
|
||||
.package((void*) MATERIAL_LIT_PACKAGE, sizeof(MATERIAL_LIT_PACKAGE))
|
||||
.build(*engine);
|
||||
app.mi = app.mat->createInstance();
|
||||
app.mi->setParameter("clearCoat", 0.0f);
|
||||
|
||||
// Move raw asset data from JavaScript to C++ static storage. Their held data will be freed via
|
||||
// BufferDescriptor callbacks after Filament creates the corresponding GPU objects.
|
||||
static auto mesh = filaweb::getRawFile("mesh");
|
||||
static auto albedo = filaweb::getTexture("albedo");
|
||||
static auto metallic = filaweb::getTexture("metallic");
|
||||
static auto roughness = filaweb::getTexture("roughness");
|
||||
static auto normal = filaweb::getTexture("normal");
|
||||
static auto ao = filaweb::getTexture("ao");
|
||||
|
||||
// Create mesh.
|
||||
printf("%s: %d bytes\n", "mesh", mesh.rawSize);
|
||||
const uint8_t* mdata = mesh.rawData.get();
|
||||
const auto destructor = [](void* buffer, size_t size, void* user) {
|
||||
// This destruction lambda is called for the vertex buffer AND index buffer, so release CPU
|
||||
// memory only after both have been uploaded to the GPU. The static count trick here works
|
||||
// only because we create a single mesh in this demo.
|
||||
static int count = 0;
|
||||
if (++count == 2) {
|
||||
auto asset = (filaweb::Asset*) user;
|
||||
asset->rawData.reset();
|
||||
}
|
||||
};
|
||||
|
||||
app.filamesh = MeshIO::loadMeshFromBuffer(engine, mdata, destructor, &mesh, app.mi);
|
||||
scene->addEntity(app.filamesh.renderable);
|
||||
|
||||
// Create textures.
|
||||
TextureSampler sampler(TextureSampler::MinFilter::LINEAR_MIPMAP_LINEAR, MagFilter::LINEAR,
|
||||
WrapMode::REPEAT);
|
||||
auto setTexture = [engine, sampler] (filaweb::Asset& asset, const char* name, bool linear) {
|
||||
setTextureParameter(*engine, asset, name, linear, sampler);
|
||||
};
|
||||
setTexture(albedo, "albedo", false);
|
||||
setTexture(metallic, "metallic", false);
|
||||
setTexture(roughness, "roughness", false);
|
||||
setTexture(normal, "normal", true);
|
||||
setTexture(ao, "ao", false);
|
||||
|
||||
// Create the sun.
|
||||
auto& em = EntityManager::get();
|
||||
app.sun = em.create();
|
||||
LightManager::Builder(LightManager::Type::SUN)
|
||||
.color(Color::toLinear<ACCURATE>({ 0.98f, 0.92f, 0.89f }))
|
||||
.intensity(110000)
|
||||
.direction({ 0.7, -1, -0.8 })
|
||||
.sunAngularRadius(1.2f)
|
||||
.castShadows(true)
|
||||
.build(*engine, app.sun);
|
||||
scene->addEntity(app.sun);
|
||||
|
||||
// Create point lights.
|
||||
em.create(4, app.ptlight);
|
||||
LightManager::Builder(LightManager::Type::POINT)
|
||||
.color(Color::toLinear<ACCURATE>({0.98f, 0.92f, 0.89f}))
|
||||
.intensity(LightManager::EFFICIENCY_LED, 300.0f)
|
||||
.position({0.0f, -0.2f, -3.0f})
|
||||
.falloff(4.0f)
|
||||
.build(*engine, app.ptlight[0]);
|
||||
LightManager::Builder(LightManager::Type::POINT)
|
||||
.color(Color::toLinear<ACCURATE>({0.98f, 0.12f, 0.19f}))
|
||||
.intensity(LightManager::EFFICIENCY_LED, 200.0f)
|
||||
.position({0.6f, 0.6f, -3.2f})
|
||||
.falloff(2.0f)
|
||||
.build(*engine, app.ptlight[1]);
|
||||
LightManager::Builder(LightManager::Type::POINT)
|
||||
.color(Color::toLinear<ACCURATE>({0.18f, 0.12f, 0.89f}))
|
||||
.intensity(LightManager::EFFICIENCY_LED, 200.0f)
|
||||
.position({-0.6f, 0.6f, -3.2f})
|
||||
.falloff(2.0f)
|
||||
.build(*engine, app.ptlight[2]);
|
||||
LightManager::Builder(LightManager::Type::POINT)
|
||||
.color(Color::toLinear<ACCURATE>({0.88f, 0.82f, 0.29f}))
|
||||
.intensity(LightManager::EFFICIENCY_LED, 200.0f)
|
||||
.position({0.0f, 1.5f, -3.5f})
|
||||
.falloff(2.0f)
|
||||
.build(*engine, app.ptlight[3]);
|
||||
scene->addEntity(app.ptlight[0]);
|
||||
scene->addEntity(app.ptlight[1]);
|
||||
scene->addEntity(app.ptlight[2]);
|
||||
scene->addEntity(app.ptlight[3]);
|
||||
|
||||
// Create skybox and image-based light source.
|
||||
auto skylight = filaweb::getSkyLight(*engine, "syferfontein_18d_clear_2k");
|
||||
scene->setIndirectLight(skylight.indirectLight);
|
||||
scene->setSkybox(skylight.skybox);
|
||||
skylight.indirectLight->setIntensity(100000);
|
||||
|
||||
app.cam = engine->createCamera();
|
||||
app.cam->setExposure(16.0f, 1 / 125.0f, 100.0f);
|
||||
app.cam->lookAt(float3{0}, float3{0, 0, -4});
|
||||
view->setCamera(app.cam);
|
||||
view->setClearColor({0.1, 0.125, 0.25, 1.0});
|
||||
};
|
||||
|
||||
void animate(Engine* engine, View* view, double now) {
|
||||
using Fov = Camera::Fov;
|
||||
const uint32_t width = view->getViewport().width;
|
||||
const uint32_t height = view->getViewport().height;
|
||||
double ratio = double(width) / height;
|
||||
app.cam->setProjection(45.0, ratio, 0.1, 50.0, ratio < 1 ? Fov::HORIZONTAL : Fov::VERTICAL);
|
||||
auto& tcm = engine->getTransformManager();
|
||||
tcm.setTransform(tcm.getInstance(app.filamesh.renderable),
|
||||
mat4f{mat3f{1.0}, float3{0.0f, 0.0f, -4.0f}} *
|
||||
mat4f::rotate(now, math::float3{0, 1, 0}));
|
||||
};
|
||||
|
||||
// This is called only after the JavaScript layer has created a WebGL 2.0 context and all assets
|
||||
// have been downloaded.
|
||||
extern "C" void launch() {
|
||||
filaweb::Application::get()->run(setup, animate);
|
||||
}
|
||||
|
||||
// The main() entry point is implicitly called after JIT compilation, but potentially before the
|
||||
// WebGL context has been created or assets have finished loading.
|
||||
int main() { }
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Suzanne</title>
|
||||
<link href="favicon.png" rel="icon" type="image/x-icon"/>
|
||||
<meta name="viewport" content="width=device-width,user-scalable=no,initial-scale=1">
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
#filament-canvas {
|
||||
touch-action: none;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="filament-canvas"></canvas>
|
||||
<script src="suzanne.js"></script>
|
||||
<script src="filaweb.js"></script>
|
||||
<script>
|
||||
|
||||
function load_etcfile(basename) {
|
||||
return use_etc ? load_rawfile(basename + "_etc.ktx") : load_rawfile(basename + ".ktx");
|
||||
}
|
||||
|
||||
load({
|
||||
'albedo': (use_s3tc ? load_rawfile('monkey/albedo_s3tc.ktx') :
|
||||
(use_astc ? load_rawfile('monkey/albedo_astc.ktx') :
|
||||
load_rawfile('monkey/albedo.ktx'))),
|
||||
'metallic': load_etcfile('monkey/metallic'),
|
||||
'roughness': load_etcfile('monkey/roughness'),
|
||||
'normal': load_etcfile('monkey/normal'),
|
||||
'ao': load_etcfile('monkey/ao'),
|
||||
'mesh': load_rawfile('monkey/mesh.filamesh'),
|
||||
'syferfontein_18d_clear_2k': load_cubemap('syferfontein_18d_clear_2k', '.ktx')
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,120 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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 "filaweb.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/TransformManager.h>
|
||||
#include <filament/VertexBuffer.h>
|
||||
#include <filament/View.h>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
using namespace filament;
|
||||
|
||||
using utils::Entity;
|
||||
using utils::EntityManager;
|
||||
|
||||
struct TriangleApp {
|
||||
VertexBuffer* vb;
|
||||
IndexBuffer* ib;
|
||||
Material* mat;
|
||||
Camera* cam;
|
||||
Entity renderable;
|
||||
};
|
||||
|
||||
struct Vertex {
|
||||
math::float2 position;
|
||||
uint32_t color;
|
||||
};
|
||||
|
||||
static const Vertex TRIANGLE_VERTICES[3] = {
|
||||
{{1, 0}, 0xffff0000u},
|
||||
{{cos(M_PI * 2 / 3), sin(M_PI * 2 / 3)}, 0xff00ff00u},
|
||||
{{cos(M_PI * 4 / 3), sin(M_PI * 4 / 3)}, 0xff0000ffu},
|
||||
};
|
||||
|
||||
static constexpr uint16_t TRIANGLE_INDICES[3] = { 0, 1, 2 };
|
||||
|
||||
static TriangleApp app;
|
||||
|
||||
void setup(Engine* engine, View* view, Scene* scene) {
|
||||
view->setClearColor({0.1, 0.125, 0.25, 1.0});
|
||||
view->setPostProcessingEnabled(false);
|
||||
view->setDepthPrepass(filament::View::DepthPrepass::DISABLED);
|
||||
static_assert(sizeof(Vertex) == 12, "Strange vertex size.");
|
||||
app.vb = VertexBuffer::Builder()
|
||||
.vertexCount(3)
|
||||
.bufferCount(1)
|
||||
.attribute(VertexAttribute::POSITION, 0, VertexBuffer::AttributeType::FLOAT2, 0, 12)
|
||||
.attribute(VertexAttribute::COLOR, 0, VertexBuffer::AttributeType::UBYTE4, 8, 12)
|
||||
.normalized(VertexAttribute::COLOR)
|
||||
.build(*engine);
|
||||
app.vb->setBufferAt(*engine, 0,
|
||||
VertexBuffer::BufferDescriptor(TRIANGLE_VERTICES, 36, nullptr));
|
||||
app.ib = IndexBuffer::Builder()
|
||||
.indexCount(3)
|
||||
.bufferType(IndexBuffer::IndexType::USHORT)
|
||||
.build(*engine);
|
||||
app.ib->setBuffer(*engine,
|
||||
IndexBuffer::BufferDescriptor(TRIANGLE_INDICES, 6, nullptr));
|
||||
|
||||
auto bakedColor = filaweb::getRawFile("bakedColor");
|
||||
app.mat = Material::Builder()
|
||||
.package(bakedColor.rawData.get(), bakedColor.rawSize)
|
||||
.build(*engine);
|
||||
|
||||
app.renderable = EntityManager::get().create();
|
||||
RenderableManager::Builder(1)
|
||||
.boundingBox({{ -1, -1, -1 }, { 1, 1, 1 }})
|
||||
.material(0, app.mat->getDefaultInstance())
|
||||
.geometry(0, RenderableManager::PrimitiveType::TRIANGLES, app.vb, app.ib, 0, 3)
|
||||
.culling(false)
|
||||
.receiveShadows(false)
|
||||
.castShadows(false)
|
||||
.build(*engine, app.renderable);
|
||||
scene->addEntity(app.renderable);
|
||||
app.cam = engine->createCamera();
|
||||
view->setCamera(app.cam);
|
||||
};
|
||||
|
||||
void animate(Engine* engine, View* view, double now) {
|
||||
constexpr float ZOOM = 1.5f;
|
||||
const uint32_t w = view->getViewport().width;
|
||||
const uint32_t h = view->getViewport().height;
|
||||
const float aspect = (float) w / h;
|
||||
app.cam->setProjection(Camera::Projection::ORTHO,
|
||||
-aspect * ZOOM, aspect * ZOOM,
|
||||
-ZOOM, ZOOM, 0, 1);
|
||||
auto& tcm = engine->getTransformManager();
|
||||
tcm.setTransform(tcm.getInstance(app.renderable),
|
||||
math::mat4f::rotate(now, math::float3{0, 0, 1}));
|
||||
};
|
||||
|
||||
// This is called only after the JavaScript layer has created a WebGL 2.0 context and all assets
|
||||
// have been downloaded.
|
||||
extern "C" void launch() {
|
||||
filaweb::Application::get()->run(setup, animate);
|
||||
}
|
||||
|
||||
// The main() entry point is implicitly called after JIT compilation, but potentially before the
|
||||
// WebGL context has been created or assets have finished loading.
|
||||
int main() { }
|
||||
@@ -1,28 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Triangle</title>
|
||||
<link href="favicon.png" rel="icon" type="image/x-icon"/>
|
||||
<meta name="viewport" content="width=device-width,user-scalable=no,initial-scale=1">
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
#filament-canvas {
|
||||
touch-action: none;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="filament-canvas"></canvas>
|
||||
<script src="triangle.js"></script>
|
||||
<script src="filaweb.js"></script>
|
||||
<script>
|
||||
load({'bakedColor': load_rawfile('material/bakedColor.filamat')});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -32,7 +32,7 @@ generate a markdown string, which then produces HTML.
|
||||
import os
|
||||
|
||||
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) + '/'
|
||||
ROOT_DIR = SCRIPT_DIR + '../../../'
|
||||
ROOT_DIR = SCRIPT_DIR + '../../'
|
||||
OUTPUT_DIR = ROOT_DIR + 'docs/webgl/'
|
||||
ENABLE_EMBEDDED_DEMO = True
|
||||
BUILD_DIR = ROOT_DIR + 'out/cmake-webgl-release/'
|
||||
@@ -442,11 +442,11 @@ def build_reference_markdown(doctree):
|
||||
|
||||
def build_reference():
|
||||
doctree = gather_docstrings([
|
||||
ROOT_DIR + 'libs/filamentjs/jsbindings.cpp',
|
||||
ROOT_DIR + 'libs/filamentjs/jsenums.cpp',
|
||||
ROOT_DIR + 'libs/filamentjs/utilities.js',
|
||||
ROOT_DIR + 'libs/filamentjs/wasmloader.js',
|
||||
ROOT_DIR + 'libs/filamentjs/extensions.js',
|
||||
ROOT_DIR + 'web/filamentjs/jsbindings.cpp',
|
||||
ROOT_DIR + 'web/filamentjs/jsenums.cpp',
|
||||
ROOT_DIR + 'web/filamentjs/utilities.js',
|
||||
ROOT_DIR + 'web/filamentjs/wasmloader.js',
|
||||
ROOT_DIR + 'web/filamentjs/extensions.js',
|
||||
])
|
||||
markdown = build_reference_markdown(doctree)
|
||||
rendered = mistletoe.markdown(markdown, PygmentsRenderer)
|
||||
@@ -487,12 +487,10 @@ if __name__ == "__main__":
|
||||
tangle(name)
|
||||
generate_demo_html(name)
|
||||
|
||||
copy_src_file(ROOT_DIR + 'libs/filamentjs/docs/main.css')
|
||||
copy_src_file(ROOT_DIR + 'web/docs/main.css')
|
||||
copy_src_file(ROOT_DIR + 'third_party/gl-matrix/gl-matrix-min.js')
|
||||
copy_built_file('libs/filamentjs/filament.js')
|
||||
copy_built_file('libs/filamentjs/filament.wasm')
|
||||
copy_built_file('samples/web/public/pillars_2k/pillars_2k_skybox.ktx')
|
||||
copy_built_file('samples/web/public/pillars_2k/pillars_2k_ibl.ktx')
|
||||
copy_built_file('web/filamentjs/filament.js')
|
||||
copy_built_file('web/filamentjs/filament.wasm')
|
||||
build_filamat('triangle')
|
||||
build_filamat('plastic')
|
||||
build_reference()
|
||||
Reference in New Issue
Block a user