WIP: glTF demo

This commit is contained in:
shrekshao
2019-05-10 18:43:41 -07:00
parent 2323c2ff7a
commit 8670ffd9c0
12 changed files with 11557 additions and 0 deletions

View File

@@ -0,0 +1,281 @@
{
"accessors": [
{
"name": "Node-Mesh_0_positions",
"componentType": 5126,
"count": 426519,
"min": [
-263.67950439453125,
-280.67950439453125,
-52.20021438598633
],
"max": [
299.67950439453125,
247.67950439453125,
-13.959484100341797
],
"type": "VEC3",
"bufferView": 0,
"byteOffset": 0
},
{
"name": "Node-Mesh_0_texcoords",
"componentType": 5126,
"count": 426519,
"min": [
0.001038654474541545,
0.0010396799771115184
],
"max": [
0.9990835189819336,
0.9990107417106628
],
"type": "VEC2",
"bufferView": 1,
"byteOffset": 0
},
{
"name": "Node-Mesh_0_indices",
"componentType": 5125,
"count": 2055249,
"min": [
0
],
"max": [
426518
],
"type": "SCALAR",
"bufferView": 2,
"byteOffset": 0
},
{
"name": "Node-Mesh_1_positions",
"componentType": 5126,
"count": 513603,
"min": [
-263.67950439453125,
-280.67950439453125,
-52.742218017578125
],
"max": [
299.67950439453125,
247.67950439453125,
-12.004308700561523
],
"type": "VEC3",
"bufferView": 0,
"byteOffset": 5118228
},
{
"name": "Node-Mesh_1_texcoords",
"componentType": 5126,
"count": 513603,
"min": [
0.0010405026841908693,
0.0010377200087532401
],
"max": [
0.999070942401886,
0.9744246006011963
],
"type": "VEC2",
"bufferView": 1,
"byteOffset": 3412152
},
{
"name": "Node-Mesh_1_indices",
"componentType": 5125,
"count": 2128626,
"min": [
0
],
"max": [
513602
],
"type": "SCALAR",
"bufferView": 2,
"byteOffset": 8220996
}
],
"asset": {
"generator": "obj2gltf",
"version": "2.0"
},
"buffers": [
{
"name": "Model",
"byteLength": 35537940,
"uri": "Model.bin"
}
],
"bufferViews": [
{
"name": "bufferView_0",
"buffer": 0,
"byteLength": 11281464,
"byteOffset": 0,
"byteStride": 12,
"target": 34962
},
{
"name": "bufferView_1",
"buffer": 0,
"byteLength": 7520976,
"byteOffset": 11281464,
"byteStride": 8,
"target": 34962
},
{
"name": "bufferView_2",
"buffer": 0,
"byteLength": 16735500,
"byteOffset": 18802440,
"target": 34963
}
],
"materials": [
{
"name": "Model_0",
"pbrMetallicRoughness": {
"baseColorTexture": {
"index": 0
},
"baseColorFactor": [
1,
1,
1,
1
],
"metallicFactor": 0,
"roughnessFactor": 1
},
"emissiveFactor": [
0,
0,
0
],
"alphaMode": "OPAQUE",
"doubleSided": false
},
{
"name": "Model_1",
"pbrMetallicRoughness": {
"baseColorTexture": {
"index": 1
},
"baseColorFactor": [
1,
1,
1,
1
],
"metallicFactor": 0,
"roughnessFactor": 1
},
"emissiveFactor": [
0,
0,
0
],
"alphaMode": "OPAQUE",
"doubleSided": false
}
],
"meshes": [
{
"name": "Node-Mesh",
"primitives": [
{
"attributes": {
"POSITION": 0,
"TEXCOORD_0": 1
},
"indices": 2,
"material": 0,
"mode": 4
},
{
"attributes": {
"POSITION": 3,
"TEXCOORD_0": 4
},
"indices": 5,
"material": 1,
"mode": 4
}
]
}
],
"nodes": [
{
"rotation": [
0.7071067811865476,
0,
0,
-0.7071067811865476
],
"name": "Node",
"mesh": 0
},
{
"children": [
0
],
"name": "Root"
}
],
"samplers": [
{
"magFilter": 9729,
"minFilter": 9729,
"wrapS": 10497,
"wrapT": 10497
}
],
"scene": 0,
"scenes": [
{
"nodes": [
1
]
}
],
"images": [
{
"name": "Model_0",
"mimeType": "image/basis",
"uri": "Model_0_4096.basis"
},
{
"name": "Model_1",
"mimeType": "image/basis",
"uri": "Model_1_4096.basis"
}
],
"textures": [
{
"name": "Model_0",
"sampler": 0,
"extensions": {
"GOOGLE_texture_basis": {
"source": 0
}
}
},
{
"name": "Model_1",
"sampler": 0,
"extensions": {
"GOOGLE_texture_basis": {
"source": 1
}
}
}
],
"extensionsUsed": [
"GOOGLE_texture_basis"
],
"extensionsRequired": [
"GOOGLE_texture_basis"
]
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

1
webgl/gltf-demo/LICENSE Normal file
View File

@@ -0,0 +1 @@
TODO: third party license (Three.js)

View File

216
webgl/gltf-demo/index.html Normal file
View File

@@ -0,0 +1,216 @@
<!DOCTYPE html>
<head>
<script src="https://cdn.jsdelivr.net/npm/three@v0.104.0"></script>
<script src="GLTFLoader.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@v0.104.0/examples/js/controls/OrbitControls.js"></script>
<style>
html, body { width:100%; height:100%; margin:0; padding:0; }
canvas { display:block; }
#pannel { position: absolute; top: 10px; left: 10px; color: white; background-color:rgba(0.3, 0.3, 0.3, 0.3); padding: 0.5em; max-width: 25%;}
</style>
</head>
<body>
<script src="./wasm/build/basis_transcoder.js"></script>
<script>
function log(s) {
var div = document.createElement('div');
div.innerHTML = s;
document.getElementById('log').appendChild(div);
}
function logTime(desc, t) {
log(desc + t.toFixed(1) + ' ms');
}
Module.onRuntimeInitialized = () => {
const { BasisFile, initializeBasis } = Module;
// DXT formats, from:
// http://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_s3tc/
COMPRESSED_RGB_S3TC_DXT1_EXT = 0x83F0;
COMPRESSED_RGBA_S3TC_DXT1_EXT = 0x83F1;
COMPRESSED_RGBA_S3TC_DXT3_EXT = 0x83F2;
COMPRESSED_RGBA_S3TC_DXT5_EXT = 0x83F3;
const BASIS_FORMAT = {
cTFETC1: 0,
cTFBC1: 1,
cTFBC4: 2,
cTFPVRTC1_4_OPAQUE_ONLY: 3,
cTFBC7_M6_OPAQUE_ONLY: 4,
cTFETC2: 5,
cTFBC3: 6,
cTFBC5: 7,
};
BASIS_FORMAT_NAMES = {};
for (var name in BASIS_FORMAT) {
BASIS_FORMAT_NAMES[BASIS_FORMAT[name]] = name;
}
DXT_FORMAT_MAP = {};
DXT_FORMAT_MAP[BASIS_FORMAT.cTFBC1] = COMPRESSED_RGB_S3TC_DXT1_EXT;
DXT_FORMAT_MAP[BASIS_FORMAT.cTFBC3] = COMPRESSED_RGBA_S3TC_DXT5_EXT;
initializeBasis();
/**
* ThreeJS glTF demo created by donmccurdy@, austin@, shrekshao@ below.
*/
(function () {
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.gammaOutput = true;
renderer.gammaFactor = 2.2;
const scene = new THREE.Scene();
scene.background = new THREE.Color(0xf0f0f0);
const light = new THREE.AmbientLight();
scene.add(light);
const light2 = new THREE.PointLight();
scene.add(light2);
const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100);
camera.position.set(12, 8, 10);
camera.lookAt(new THREE.Vector3(0, -2, 0));
const controls = new THREE.OrbitControls( camera, renderer.domElement );
controls.autoRotate = true;
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}, false);
const etcSupported = renderer.context.getExtension('WEBGL_compressed_texture_etc1');
const dxtSupported = renderer.context.getExtension('WEBGL_compressed_texture_s3tc');
let format = etcSupported ? BASIS_FORMAT.cTFETC1 : BASIS_FORMAT.cTFBC1;
log('Transcode to Compressed Texture Format: <strong>' + BASIS_FORMAT_NAMES[format] + '</strong>');
class BasisTextureLoader {
constructor() {
this.count = 0;
}
load(url, onLoad, onProgress, onError) {
fetch(url).then(res => res.arrayBuffer()).then(buf => {
log('<strong> Texture ' + (this.count++) + '</strong>');
const startTime = performance.now();
const basisFile = new BasisFile(new Uint8Array(buf));
const width = basisFile.getImageWidth(0, 0);
const height = basisFile.getImageHeight(0, 0);
const images = basisFile.getNumImages();
const levels = basisFile.getNumLevels(0);
if (!width || !height || !images || !levels) {
console.warn('Invalid .basis file');
basisFile.close();
basisFile.delete();
return;
}
if (!basisFile.startTranscoding()) {
console.warn('startTranscoding failed');
basisFile.close();
basisFile.delete();
return;
}
const dst = new Uint8Array(basisFile.getImageTranscodedSizeInBytes(0, 0, format));
if (!basisFile.transcodeImage(dst, 0, 0, format, etcSupported ? 0 : (dxtSupported ? 1 : 0), 0)) {
console.warn('transcodeImage failed');
basisFile.close();
basisFile.delete();
return;
}
const elapsed = performance.now() - startTime;
basisFile.close();
basisFile.delete();
console.log('Transcode success!', dst);
console.log('Time:', elapsed, 'ms');
logTime('Transcode Time: ', elapsed);
const mipmaps = [ { data: dst, width, height } ];
let texture;
if (!etcSupported) {
const type = dxtSupported ? THREE.UnsignedByteType : THREE.UnsignedShort565Type;
const f = dxtSupported ? DXT_FORMAT_MAP[ format ] : THREE.RGBFormat;
texture = new THREE.CompressedTexture( mipmaps, width, height, f, type );
} else {
texture = new THREE.CompressedTexture( mipmaps, width, height, THREE.RGB_ETC1_Format );
}
texture.minFilter = THREE.LinearFilter;
texture.magFilter = THREE.LinearFilter;
texture.encoding = THREE.sRGBEncoding;
texture.generateMipmaps = false;
texture.flipY = false;
texture.needsUpdate = true;
onLoad( texture );
});
}
}
THREE.Loader.Handlers.add(/\.basis$/, new BasisTextureLoader());
const loader = new THREE.GLTFLoader();
function animate() {
requestAnimationFrame(animate);
controls.update();
renderer.render(scene, camera);
}
loader.load('AGI_HQ/AgiHQ.gltf', (gltf) => {
const model = gltf.scene;
model.scale.set(0.01, 0.01, 0.01);
const bbox = new THREE.Box3();
bbox.setFromObject(model);
console.log(bbox);
scene.add(model);
document.body.appendChild(renderer.domElement);
animate();
}, undefined, (e) => console.error(e));
}());
}
</script>
<div id="pannel">
<strong>Basis Texture Transcoder glTF Demo</strong>
<div id="log"></div>
</div>
</body>
</html>

View File

@@ -0,0 +1,19 @@
cmake_minimum_required(VERSION 3.0)
project(basisu_transcoder_js)
if (EMSCRIPTEN)
set(CMAKE_CXX_STANDARD 11)
add_executable(basis_transcoder.js
../../../transcoder/basisu_transcoder.cpp
basis_wrappers.cpp
)
target_include_directories(basis_transcoder.js PRIVATE ../transcoder)
set_target_properties(basis_transcoder.js PROPERTIES
OUTPUT_NAME "basis_transcoder"
SUFFIX ".js"
LINK_FLAGS "--bind -s ALLOW_MEMORY_GROWTH=1 -s ASSERTIONS=0")
endif()

View File

@@ -0,0 +1,205 @@
// basis_wrappers.cpp - Simple C-style wrappers to the C++ transcoder for WebGL use.
#include "basisu_transcoder.h"
#include <emscripten/bind.h>
#include <iostream>
using namespace emscripten;
using namespace basist;
static basist::etc1_global_selector_codebook *g_pGlobal_codebook;
void basis_init()
{
basisu_transcoder_init();
if (!g_pGlobal_codebook)
g_pGlobal_codebook = new basist::etc1_global_selector_codebook(g_global_selector_cb_size, g_global_selector_cb);
}
#define MAGIC 0xDEADBEE1
struct basis_file
{
int m_magic = 0;
basisu_transcoder m_transcoder;
std::vector<uint8_t> m_file;
basis_file(const emscripten::val& jsBuffer)
: m_file([&]() {
size_t byteLength = jsBuffer["byteLength"].as<size_t>();
return std::vector<uint8_t>(byteLength);
}()),
m_transcoder(g_pGlobal_codebook)
{
unsigned int length = jsBuffer["length"].as<unsigned int>();
emscripten::val memory = emscripten::val::module_property("HEAP8")["buffer"];
emscripten::val memoryView = jsBuffer["constructor"].new_(memory, reinterpret_cast<uintptr_t>(m_file.data()), length);
memoryView.call<void>("set", jsBuffer);
if (!m_transcoder.validate_header(m_file.data(), m_file.size())) {
m_file.clear();
std::cerr << "Invalid Basis header" << std::endl;
}
// Initialized after validation
m_magic = MAGIC;
}
void close() {
assert(m_magic == MAGIC);
m_file.clear();
}
uint32_t getHasAlpha() {
assert(m_magic == MAGIC);
if (m_magic != MAGIC)
return 0;
basisu_image_level_info li;
if (!m_transcoder.get_image_level_info(m_file.data(), m_file.size(), li, 0, 0))
return 0;
return li.m_alpha_flag;
}
uint32_t getNumImages() {
assert(m_magic == MAGIC);
if (m_magic != MAGIC)
return 0;
return m_transcoder.get_total_images(m_file.data(), m_file.size());
}
uint32_t getNumLevels(uint32_t image_index) {
assert(m_magic == MAGIC);
if (m_magic != MAGIC)
return 0;
basisu_image_info ii;
if (!m_transcoder.get_image_info(m_file.data(), m_file.size(), ii, image_index))
return 0;
return ii.m_total_levels;
}
uint32_t getImageWidth(uint32_t image_index, uint32_t level_index) {
assert(m_magic == MAGIC);
if (m_magic != MAGIC)
return 0;
uint32_t orig_width, orig_height, total_blocks;
if (!m_transcoder.get_image_level_desc(m_file.data(), m_file.size(), image_index, level_index, orig_width, orig_height, total_blocks))
return 0;
return orig_width;
}
uint32_t getImageHeight(uint32_t image_index, uint32_t level_index) {
assert(m_magic == MAGIC);
if (m_magic != MAGIC)
return 0;
uint32_t orig_width, orig_height, total_blocks;
if (!m_transcoder.get_image_level_desc(m_file.data(), m_file.size(), image_index, level_index, orig_width, orig_height, total_blocks))
return 0;
return orig_height;
}
uint32_t getImageTranscodedSizeInBytes(uint32_t image_index, uint32_t level_index, uint32_t format) {
assert(m_magic == MAGIC);
if (m_magic != MAGIC)
return 0;
if (format >= cTFTotalTextureFormats)
return 0;
uint32_t bytes_per_block = basis_get_bytes_per_block(static_cast<transcoder_texture_format>(format));
uint32_t orig_width, orig_height, total_blocks;
if (!m_transcoder.get_image_level_desc(m_file.data(), m_file.size(), image_index, level_index, orig_width, orig_height, total_blocks))
return 0;
return total_blocks * bytes_per_block;
}
uint32_t startTranscoding() {
assert(m_magic == MAGIC);
if (m_magic != MAGIC)
return 0;
return m_transcoder.start_transcoding(m_file.data(), m_file.size());
}
uint32_t transcodeImage(const emscripten::val& dst, uint32_t image_index, uint32_t level_index, uint32_t format, uint32_t pvrtc_wrap_addressing, uint32_t get_alpha_for_opaque_formats) {
assert(m_magic == MAGIC);
if (m_magic != MAGIC)
return 0;
if (format >= cTFTotalTextureFormats)
return 0;
uint32_t bytes_per_block = basis_get_bytes_per_block(static_cast<transcoder_texture_format>(format));
uint32_t orig_width, orig_height, total_blocks;
if (!m_transcoder.get_image_level_desc(m_file.data(), m_file.size(), image_index, level_index, orig_width, orig_height, total_blocks))
return 0;
uint32_t required_size = total_blocks * bytes_per_block;
std::vector<uint8_t> dst_data(required_size);
uint32_t status = m_transcoder.transcode_image_level(
m_file.data(), m_file.size(), image_index, level_index,
dst_data.data(), dst_data.size() / bytes_per_block,
static_cast<basist::transcoder_texture_format>(format),
(
(pvrtc_wrap_addressing ? basisu_transcoder::cDecodeFlagsPVRTCWrapAddressing : 0) |
(get_alpha_for_opaque_formats ? basisu_transcoder::cDecodeFlagsTranscodeAlphaDataToOpaqueFormats : 0)
));
emscripten::val memory = emscripten::val::module_property("HEAP8")["buffer"];
emscripten::val memoryView = emscripten::val::global("Uint8Array").new_(memory, reinterpret_cast<uintptr_t>(dst_data.data()), dst_data.size());
dst.call<void>("set", memoryView);
return status;
}
};
EMSCRIPTEN_BINDINGS(basis_transcoder) {
function("initializeBasis", &basis_init);
class_<basis_file>("BasisFile")
.constructor<const emscripten::val&>()
.function("close", optional_override([](basis_file& self) {
return self.close();
}))
.function("getHasAlpha", optional_override([](basis_file& self) {
return self.getHasAlpha();
}))
.function("getNumImages", optional_override([](basis_file& self) {
return self.getNumImages();
}))
.function("getNumLevels", optional_override([](basis_file& self, uint32_t imageIndex) {
return self.getNumLevels(imageIndex);
}))
.function("getImageWidth", optional_override([](basis_file& self, uint32_t imageIndex, uint32_t levelIndex) {
return self.getImageWidth(imageIndex, levelIndex);
}))
.function("getImageHeight", optional_override([](basis_file& self, uint32_t imageIndex, uint32_t levelIndex) {
return self.getImageHeight(imageIndex, levelIndex);
}))
.function("getImageTranscodedSizeInBytes", optional_override([](basis_file& self, uint32_t imageIndex, uint32_t levelIndex, uint32_t format) {
return self.getImageTranscodedSizeInBytes(imageIndex, levelIndex, format);
}))
.function("startTranscoding", optional_override([](basis_file& self) {
return self.startTranscoding();
}))
.function("transcodeImage", optional_override([](basis_file& self, const emscripten::val& dst, uint32_t imageIndex, uint32_t levelIndex, uint32_t format, uint32_t pvrtcWrapAddressing, uint32_t getAlphaForOpaqueFormats) {
return self.transcodeImage(dst, imageIndex, levelIndex, format, pvrtcWrapAddressing, getAlphaForOpaqueFormats);
}))
;
}

File diff suppressed because it is too large Load Diff

Binary file not shown.