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:
Philip Rideout
2018-11-01 11:56:15 -07:00
parent 4e3a88bbab
commit a42dcd2291
37 changed files with 15 additions and 1500 deletions

View File

@@ -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)

View File

@@ -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

View File

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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", &params.color.r);
if (params.currentMaterialModel > MATERIAL_MODEL_UNLIT) {
if (params.currentBlending == BLENDING_TRANSPARENT ||
params.currentBlending == BLENDING_FADE) {
ImGui::SliderFloat("alpha", &params.alpha, 0.0f, 1.0f);
}
ImGui::SliderFloat("roughness", &params.roughness, 0.0f, 1.0f);
if (params.currentMaterialModel != MATERIAL_MODEL_CLOTH) {
ImGui::SliderFloat("metallic", &params.metallic, 0.0f, 1.0f);
ImGui::SliderFloat("reflectance", &params.reflectance, 0.0f, 1.0f);
}
if (params.currentMaterialModel != MATERIAL_MODEL_CLOTH &&
params.currentMaterialModel != MATERIAL_MODEL_SUBSURFACE) {
ImGui::SliderFloat("clearCoat", &params.clearCoat, 0.0f, 1.0f);
}
if (params.currentMaterialModel == MATERIAL_MODEL_SUBSURFACE) {
ImGui::SliderFloat("thickness", &params.thickness, 0.0f, 1.0f);
ImGui::SliderFloat("subsurfacePower", &params.subsurfacePower, 1.0f, 24.0f);
ImGui::ColorEdit3("subsurfaceColor", &params.subsurfaceColor.r);
}
if (params.currentMaterialModel == MATERIAL_MODEL_CLOTH) {
ImGui::ColorEdit3("sheenColor", &params.sheenColor.r);
ImGui::ColorEdit3("subsurfaceColor", &params.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() { }

View File

@@ -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>

View File

@@ -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() { }

View File

@@ -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>

View File

@@ -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() { }

View File

@@ -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>

View File

@@ -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()