mirror of
https://github.com/openglobus/openglobus.git
synced 2025-12-08 19:25:27 +00:00
partial implementation
This commit is contained in:
parent
d55bbc535c
commit
15fb55f897
BIN
sandbox/modelLoad/CesiumMilkTruck.glb
Normal file
BIN
sandbox/modelLoad/CesiumMilkTruck.glb
Normal file
Binary file not shown.
25
sandbox/modelLoad/dracoLoader.html
Normal file
25
sandbox/modelLoad/dracoLoader.html
Normal file
@ -0,0 +1,25 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Draco loader sample</title>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<script
|
||||
type="text/javascript"
|
||||
src="https://www.gstatic.com/draco/versioned/decoders/1.5.7/draco_decoder.js"
|
||||
></script>
|
||||
<script src="./dracoLoader.js" type="module"></script>
|
||||
<link rel="stylesheet" href="../../css/og.css" type="text/css" />
|
||||
<style>
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div style="width: 100%; height: 100%">
|
||||
<canvas id="frame" style="width: 100%; height: 100%"> </canvas>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
253
sandbox/modelLoad/dracoLoader.js
Normal file
253
sandbox/modelLoad/dracoLoader.js
Normal file
@ -0,0 +1,253 @@
|
||||
/* eslint-disable no-undef */
|
||||
import {
|
||||
Globe,
|
||||
control,
|
||||
Vector,
|
||||
LonLat,
|
||||
Entity,
|
||||
OpenStreetMap,
|
||||
EmptyTerrain,
|
||||
RgbTerrain,
|
||||
GlobusRgbTerrain,
|
||||
Object3d,
|
||||
mercator,
|
||||
Bing,
|
||||
GeoVideo,
|
||||
XYZ,
|
||||
utils,
|
||||
PlanetCamera,
|
||||
Framebuffer,
|
||||
input,
|
||||
Program,
|
||||
Vec4,
|
||||
Vec2,
|
||||
GeoImage,
|
||||
Renderer,
|
||||
Vec3,
|
||||
Mat4,
|
||||
RenderNode,
|
||||
EntityCollection,
|
||||
scene,
|
||||
Gltf
|
||||
} from "../../lib/og.es.js";
|
||||
|
||||
let renderer = new Renderer("frame", {
|
||||
msaa: 8,
|
||||
controls: [new control.SimpleNavigation({ speed: 0.01 }), new control.GeoObjectEditor()],
|
||||
autoActivate: true
|
||||
});
|
||||
|
||||
class MyScene extends RenderNode {
|
||||
constructor() {
|
||||
super("MyScene");
|
||||
}
|
||||
|
||||
init() {
|
||||
const baseObj = Object3d.createCube(0.4, 2, 0.4).translate(new Vec3(0, 1, 0)).setMaterial({
|
||||
ambient: "#882a2a",
|
||||
diffuse: "#fb3434",
|
||||
shininess: 1
|
||||
});
|
||||
|
||||
const frustumObj = Object3d.createFrustum(3, 2, 1).setMaterial({
|
||||
ambient: "#236028",
|
||||
diffuse: "#1cdd23",
|
||||
shininess: 1
|
||||
});
|
||||
|
||||
const cylinderObj = Object3d.createCylinder(1, 0, 1)
|
||||
.applyMat4(new Mat4().setRotation(new Vec3(1, 0, 0), (90 * Math.PI) / 180))
|
||||
.setMaterial({
|
||||
ambient: "#773381",
|
||||
diffuse: "#ef00ff",
|
||||
shininess: 1
|
||||
});
|
||||
|
||||
let parentEntity = new Entity({
|
||||
cartesian: new Vec3(0, 0, 0),
|
||||
independentPicking: true
|
||||
// geoObject: {
|
||||
// color: "rgb(90,90,90)",
|
||||
// scale: 1,
|
||||
// instanced: true,
|
||||
// tag: `baseObj`,
|
||||
// object3d: baseObj
|
||||
// }
|
||||
});
|
||||
|
||||
let childEntity = new Entity({
|
||||
cartesian: new Vec3(0, 1, 0),
|
||||
independentPicking: true,
|
||||
relativePosition: true,
|
||||
geoObject: {
|
||||
color: "rgb(90,90,90)",
|
||||
instanced: true,
|
||||
tag: `frustumObj`,
|
||||
object3d: frustumObj
|
||||
}
|
||||
});
|
||||
|
||||
let childChildEntity = new Entity({
|
||||
cartesian: new Vec3(0, 3, -1),
|
||||
independentPicking: true,
|
||||
relativePosition: true,
|
||||
geoObject: {
|
||||
color: "rgb(90,90,90)",
|
||||
instanced: true,
|
||||
tag: `cylinderObj`,
|
||||
object3d: cylinderObj
|
||||
}
|
||||
});
|
||||
|
||||
childEntity.appendChild(childChildEntity);
|
||||
parentEntity.appendChild(childEntity);
|
||||
|
||||
let collection = new EntityCollection({
|
||||
entities: [parentEntity]
|
||||
});
|
||||
|
||||
collection.addTo(this);
|
||||
|
||||
this.renderer.activeCamera.set(new Vec3(-4, 21, 23), new Vec3(1, 0, 0));
|
||||
|
||||
this.renderer.activeCamera.update();
|
||||
DracoDecoderModule().then((decoderModule) => {
|
||||
Gltf.connectDracoDecoderModule(decoderModule);
|
||||
Gltf.loadGlb("./maxwell_the_cat.glb").then((gltf) => {
|
||||
// const models = gltf.getObjects3d();
|
||||
const entities = gltf.toEntities();
|
||||
console.log("entities", entities);
|
||||
const cat = entities[0];
|
||||
cat.setScale(0.1);
|
||||
childChildEntity.appendChild(entities[0]);
|
||||
// childChildEntity.appendChild(
|
||||
// new Entity({
|
||||
// cartesian: new Vec3(0, 3, -1),
|
||||
// independentPicking: true,
|
||||
// relativePosition: true,
|
||||
// geoObject: {
|
||||
// color: "rgb(90,90,90)",
|
||||
// instanced: true,
|
||||
// tag: `circleObj`,
|
||||
// object3d: models[0]
|
||||
// }
|
||||
// })
|
||||
// );
|
||||
this.renderer.activeCamera.update();
|
||||
});
|
||||
});
|
||||
|
||||
// Gltf.loadGlb("./CesiumMilkTruck.glb").then((gltf) => {
|
||||
// const entity = gltf.toEntities()[0];
|
||||
// entity.setScale(1);
|
||||
// console.log('truck entity', entity);
|
||||
// childChildEntity.appendChild(entity);
|
||||
// this.renderer.activeCamera.update();
|
||||
// });
|
||||
}
|
||||
}
|
||||
|
||||
renderer.addNodes([new scene.Axes(), new MyScene()]);
|
||||
|
||||
async function loadGLB(url) {
|
||||
const response = await fetch(url);
|
||||
const buffer = response.arrayBuffer();
|
||||
return await buffer;
|
||||
}
|
||||
|
||||
function parseGLB(arrayBuffer) {
|
||||
const dv = new DataView(arrayBuffer);
|
||||
const magic = dv.getUint32(0, true);
|
||||
if (magic !== 0x46546c67) throw new Error("Not a valid GLB");
|
||||
|
||||
const jsonChunkLength = dv.getUint32(12, true);
|
||||
const jsonChunkStart = 20;
|
||||
const jsonChunk = new TextDecoder().decode(
|
||||
new Uint8Array(arrayBuffer, jsonChunkStart, jsonChunkLength)
|
||||
);
|
||||
const gltf = JSON.parse(jsonChunk);
|
||||
const chunks = [];
|
||||
const binChunkStart = 20 + jsonChunkLength;
|
||||
for (let i = 0; i < gltf.bufferViews.length; i++) {
|
||||
const bufferView = gltf.bufferViews[i];
|
||||
const offset = i + 1;
|
||||
const start = binChunkStart + 8 * offset + bufferView.byteOffset;
|
||||
chunks.push(arrayBuffer.slice(start, start + bufferView.byteLength));
|
||||
}
|
||||
|
||||
return { gltf, chunks };
|
||||
}
|
||||
|
||||
function getDracoCompressedAccessor(gltf) {
|
||||
for (const mesh of gltf.meshes) {
|
||||
for (const primitive of mesh.primitives) {
|
||||
if (primitive.extensions && primitive.extensions["KHR_draco_mesh_compression"]) {
|
||||
return primitive.extensions["KHR_draco_mesh_compression"];
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function decodeDraco(decoderModule, binChunk, gltf) {
|
||||
console.log(gltf);
|
||||
const byteOffset = 0; // optional: read from bufferView
|
||||
const dracoBufferView = new Uint8Array(binChunk, byteOffset); // if bufferView.byteOffset is available, use it
|
||||
const decoder = new decoderModule.Decoder();
|
||||
const buffer = new decoderModule.DecoderBuffer();
|
||||
buffer.Init(dracoBufferView, dracoBufferView.byteLength);
|
||||
|
||||
const geometryType = decoder.GetEncodedGeometryType(buffer);
|
||||
if (geometryType !== decoderModule.TRIANGULAR_MESH) {
|
||||
throw new Error("Not a mesh");
|
||||
}
|
||||
|
||||
const mesh = new decoderModule.Mesh();
|
||||
const status = decoder.DecodeBufferToMesh(buffer, mesh);
|
||||
if (!status.ok() || mesh.ptr === 0) {
|
||||
console.log(status.error_msg());
|
||||
throw new Error("Failed to decode Draco mesh");
|
||||
}
|
||||
|
||||
const posAttrId = decoder.GetAttributeId(mesh, decoderModule.POSITION);
|
||||
const posAttr = decoder.GetAttribute(mesh, posAttrId);
|
||||
const numPoints = mesh.num_points();
|
||||
|
||||
const pos = new decoderModule.DracoFloat32Array();
|
||||
decoder.GetAttributeFloatForAllPoints(mesh, posAttr, pos);
|
||||
|
||||
const positions = new Float32Array(numPoints * 3);
|
||||
for (let i = 0; i < positions.length; i++) {
|
||||
positions[i] = pos.GetValue(i);
|
||||
}
|
||||
|
||||
decoderModule.destroy(pos);
|
||||
decoderModule.destroy(mesh);
|
||||
decoderModule.destroy(decoder);
|
||||
decoderModule.destroy(buffer);
|
||||
|
||||
return positions;
|
||||
}
|
||||
|
||||
const test = async () => {
|
||||
// const arrayBuffer = await loadGLB("./CesiumMilkTruck.glb");
|
||||
// const { gltf, chunks } = parseGLB(arrayBuffer);
|
||||
// // const dracoExt = getDracoCompressedAccessor(gltf);
|
||||
// console.log(gltf, chunks);
|
||||
// // eslint-disable-next-line no-undef
|
||||
// const decoderModule = await DracoDecoderModule();
|
||||
// await decoderModule.ready;
|
||||
// const positions = decodeDraco(decoderModule, chunks[2], gltf);
|
||||
// console.log(positions);
|
||||
// const buffer = new decoderModule.DecoderBuffer();
|
||||
// buffer.Init(cat, cat.length);
|
||||
// const decoder = new decoderModule.Decoder();
|
||||
// const geometryType = decoder.GetEncodedGeometryType(buffer);
|
||||
// console.log(decoderModule, geometryType);
|
||||
// const gltf = await Gltf.loadGlb("./CesiumMilkTruck.glb");
|
||||
// const objects = gltf.getObjects3d();
|
||||
// console.log(objects);
|
||||
// await Gltf.loadGlb("./maxwell_the_cat.glb");
|
||||
};
|
||||
|
||||
test();
|
||||
BIN
sandbox/modelLoad/maxwell_the_cat.glb
Normal file
BIN
sandbox/modelLoad/maxwell_the_cat.glb
Normal file
Binary file not shown.
@ -13,7 +13,7 @@ function getColor(color?: number[] | TypedArray | string): Float32Array {
|
||||
} else if (typeof color === 'string') {
|
||||
return htmlColorToFloat32Array(color);
|
||||
}
|
||||
return new Float32Array([1.0, 1.0, 1.0, 1.0]);
|
||||
return new Float32Array([0.5, 0.5, 0.5, 1]);
|
||||
}
|
||||
|
||||
function getColor3v(color?: NumberArray3 | TypedArray | string): Float32Array {
|
||||
@ -44,9 +44,12 @@ interface IObject3dParams {
|
||||
diffuse?: string | NumberArray3;
|
||||
specular?: string | NumberArray3;
|
||||
shininess?: number;
|
||||
colorTexture?: string;
|
||||
normalTexture?: string;
|
||||
metallicRoughnessTexture?: string;
|
||||
colorTextureSrc?: string;
|
||||
normalTextureSrc?: string;
|
||||
metallicRoughnessTextureSrc?: string;
|
||||
colorTextureImage?: HTMLImageElement;
|
||||
normalTextureImage?: HTMLImageElement;
|
||||
metallicRoughnessTextureImage?: HTMLImageElement;
|
||||
}
|
||||
|
||||
type MaterialParams = Pick<IObject3dParams, 'ambient' | 'diffuse' | 'specular' | 'shininess'>;
|
||||
@ -67,13 +70,15 @@ class Object3d {
|
||||
public diffuse: Float32Array;
|
||||
public specular: Float32Array;
|
||||
public shininess: number;
|
||||
public colorTexture: string;
|
||||
public normalTexture: string;
|
||||
public metallicRoughnessTexture: string;
|
||||
public colorTextureSrc: string | null;
|
||||
public colorTextureImage: HTMLImageElement | null;
|
||||
public normalTextureSrc: string | null;
|
||||
public normalTextureImage: HTMLImageElement | null;
|
||||
public metallicRoughnessTextureSrc: string | null;
|
||||
public metallicRoughnessTextureImage: HTMLImageElement | null;
|
||||
public center: Vec3;
|
||||
|
||||
constructor(data: IObject3dParams = {}) {
|
||||
|
||||
this._name = data.name || "noname";
|
||||
this._vertices = data.vertices || [];
|
||||
this._numVertices = this._vertices.length / 3;
|
||||
@ -90,9 +95,12 @@ class Object3d {
|
||||
this.diffuse = getColor3v(data.diffuse);
|
||||
this.specular = getColor3v(data.specular);
|
||||
this.shininess = data.shininess || 100;
|
||||
this.colorTexture = data.colorTexture || "";
|
||||
this.normalTexture = data.normalTexture || "";
|
||||
this.metallicRoughnessTexture = data.metallicRoughnessTexture || "";
|
||||
this.colorTextureSrc = data.colorTextureSrc || null;
|
||||
this.colorTextureImage = data.colorTextureImage || null;
|
||||
this.normalTextureSrc = data.normalTextureSrc || null;
|
||||
this.normalTextureImage = data.normalTextureImage || null;
|
||||
this.metallicRoughnessTextureSrc = data.metallicRoughnessTextureSrc || null;
|
||||
this.metallicRoughnessTextureImage = data.metallicRoughnessTextureImage || null;
|
||||
|
||||
if (data.scale) {
|
||||
let s = data.scale;
|
||||
@ -629,9 +637,9 @@ class Object3d {
|
||||
specular: mat.specular,
|
||||
shininess: mat.shininess,
|
||||
color: mat.color,
|
||||
colorTexture: baseUrl ? `${baseUrl}/${mat.colorTexture}` : mat.colorTexture,
|
||||
normalTexture: baseUrl ? `${baseUrl}/${mat.normalTexture}` : mat.normalTexture,
|
||||
metallicRoughnessTexture: baseUrl ? `${baseUrl}/${mat.metallicRoughnessTexture}` : mat.metallicRoughnessTexture
|
||||
colorTextureSrc: baseUrl ? `${baseUrl}/${mat.colorTexture}` : mat.colorTexture,
|
||||
normalTextureSrc: baseUrl ? `${baseUrl}/${mat.normalTexture}` : mat.normalTexture,
|
||||
metallicRoughnessTextureSrc: baseUrl ? `${baseUrl}/${mat.metallicRoughnessTexture}` : mat.metallicRoughnessTexture
|
||||
})
|
||||
}
|
||||
);
|
||||
@ -658,9 +666,9 @@ class Object3d {
|
||||
specular: mat.specular,
|
||||
shininess: mat.shininess,
|
||||
color: mat.color,
|
||||
colorTexture: mat.colorTexture,
|
||||
normalTexture: mat.normalTexture,
|
||||
metallicRoughnessTexture: mat.metallicRoughnessTexture
|
||||
colorTextureSrc: mat.colorTexture,
|
||||
normalTextureSrc: mat.normalTexture,
|
||||
metallicRoughnessTextureSrc: mat.metallicRoughnessTexture
|
||||
})
|
||||
}
|
||||
);
|
||||
|
||||
@ -113,7 +113,11 @@ class GeoObject {
|
||||
|
||||
this._localPosition = new Vec3();
|
||||
|
||||
this._color = utils.createColorRGBA(options.color, new Vec4(0.15, 0.15, 0.15, 1.0));
|
||||
this._color = utils.createColorRGBA(
|
||||
options.color, options.object3d?.color
|
||||
? new Vec4(...Array.from(options.object3d.color))
|
||||
: new Vec4(0.15, 0.15, 0.15, 1.0)
|
||||
);
|
||||
|
||||
this._handler = null;
|
||||
this._handlerIndex = -1;
|
||||
|
||||
@ -122,28 +122,49 @@ export class GeoObjectHandler {
|
||||
this.update();
|
||||
}
|
||||
|
||||
public setColorTextureTag(src: string, tag: string) {
|
||||
public setColorTextureTag(src: string | HTMLImageElement, tag: string) {
|
||||
const tagData = this._instanceDataMap.get(tag);
|
||||
if (tagData) {
|
||||
tagData._colorTextureSrc = src;
|
||||
if (typeof src === 'string') {
|
||||
tagData._colorTextureSrc = src;
|
||||
tagData._colorTextureImage = null;
|
||||
}
|
||||
if (src instanceof HTMLImageElement) {
|
||||
tagData._colorTextureSrc = null;
|
||||
tagData._colorTextureImage = src;
|
||||
}
|
||||
this._instanceDataMap.set(tag, tagData);
|
||||
this._loadColorTexture(tagData);
|
||||
}
|
||||
}
|
||||
|
||||
public setNormalTextureTag(src: string, tag: string) {
|
||||
public setNormalTextureTag(src: string | HTMLImageElement, tag: string) {
|
||||
const tagData = this._instanceDataMap.get(tag);
|
||||
if (tagData) {
|
||||
tagData._normalTextureSrc = src;
|
||||
if (typeof src === 'string') {
|
||||
tagData._normalTextureSrc = src;
|
||||
tagData._normalTextureImage = null;
|
||||
}
|
||||
if (src instanceof HTMLImageElement) {
|
||||
tagData._normalTextureSrc = null;
|
||||
tagData._normalTextureImage = src;
|
||||
}
|
||||
this._instanceDataMap.set(tag, tagData);
|
||||
this._loadNormalTexture(tagData);
|
||||
}
|
||||
}
|
||||
|
||||
public setMetallicRoughnessTextureTag(src: string, tag: string) {
|
||||
public setMetallicRoughnessTextureTag(src: string | HTMLImageElement, tag: string) {
|
||||
const tagData = this._instanceDataMap.get(tag);
|
||||
if (tagData) {
|
||||
tagData._metallicRoughnessTextureSrc = src;
|
||||
if (typeof src === 'string') {
|
||||
tagData._metallicRoughnessTextureSrc = src;
|
||||
tagData._metallicRoughnessTextureImage = null;
|
||||
}
|
||||
if (src instanceof HTMLImageElement) {
|
||||
tagData._metallicRoughnessTextureSrc = null;
|
||||
tagData._metallicRoughnessTextureImage = src;
|
||||
}
|
||||
this._instanceDataMap.set(tag, tagData);
|
||||
this._loadMetallicRoughnessTexture(tagData);
|
||||
}
|
||||
@ -182,9 +203,13 @@ export class GeoObjectHandler {
|
||||
tagData._changedBuffers[TEXCOORD_BUFFER] = true;
|
||||
}
|
||||
|
||||
tagData._colorTextureSrc = object.colorTexture;
|
||||
tagData._normalTextureSrc = object.normalTexture;
|
||||
tagData._metallicRoughnessTexture = object.metallicRoughnessTexture;
|
||||
tagData._colorTextureSrc = object.colorTextureSrc;
|
||||
tagData._normalTextureSrc = object.normalTextureSrc;
|
||||
tagData._metallicRoughnessTexture = object.metallicRoughnessTextureSrc;
|
||||
tagData._colorTextureImage = object.colorTextureImage;
|
||||
tagData._normalTextureImage = object.normalTextureImage;
|
||||
tagData._metallicRoughnessTextureImage = object.metallicRoughnessTextureImage;
|
||||
|
||||
|
||||
this._loadColorTexture(tagData);
|
||||
this._loadNormalTexture(tagData);
|
||||
@ -212,9 +237,12 @@ export class GeoObjectHandler {
|
||||
tagData._indicesArr = geoObject.indices;
|
||||
tagData._texCoordArr = geoObject.texCoords;
|
||||
|
||||
tagData._colorTextureSrc = geoObject.object3d.colorTexture;
|
||||
tagData._normalTextureSrc = geoObject.object3d.normalTexture;
|
||||
tagData._metallicRoughnessTextureSrc = geoObject.object3d.metallicRoughnessTexture;
|
||||
tagData._colorTextureSrc = geoObject.object3d.colorTextureSrc;
|
||||
tagData._normalTextureSrc = geoObject.object3d.normalTextureSrc;
|
||||
tagData._metallicRoughnessTextureSrc = geoObject.object3d.metallicRoughnessTextureSrc;
|
||||
tagData._colorTextureImage = geoObject.object3d.colorTextureImage;
|
||||
tagData._normalTextureImage = geoObject.object3d.normalTextureImage;
|
||||
tagData._metallicRoughnessTextureImage = geoObject.object3d.metallicRoughnessTextureImage;
|
||||
|
||||
tagData.setMaterialParams(
|
||||
geoObject.object3d.ambient,
|
||||
@ -489,23 +517,50 @@ export class GeoObjectHandler {
|
||||
}
|
||||
|
||||
async _loadColorTexture(tagData: InstanceData) {
|
||||
if (this._renderer && tagData._colorTextureSrc) {
|
||||
if (!this._renderer) {
|
||||
return;
|
||||
}
|
||||
if (tagData._colorTextureSrc) {
|
||||
const image = await loadImage(tagData._colorTextureSrc);
|
||||
tagData.createColorTexture(image);
|
||||
return;
|
||||
}
|
||||
if (tagData._colorTextureImage) {
|
||||
await tagData._colorTextureImage.decode();
|
||||
tagData.createColorTexture(tagData._colorTextureImage);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
async _loadNormalTexture(tagData: InstanceData) {
|
||||
if (this._renderer && tagData._normalTextureSrc) {
|
||||
if (!this._renderer) {
|
||||
return;
|
||||
}
|
||||
if (tagData._normalTextureSrc) {
|
||||
const image = await loadImage(tagData._normalTextureSrc);
|
||||
tagData.createNormalTexture(image);
|
||||
return;
|
||||
}
|
||||
if (tagData._normalTextureImage) {
|
||||
await tagData._normalTextureImage.decode();
|
||||
tagData.createNormalTexture(tagData._normalTextureImage);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
async _loadMetallicRoughnessTexture(tagData: InstanceData) {
|
||||
if (this._renderer && tagData._metallicRoughnessTextureSrc) {
|
||||
if (!this._renderer) {
|
||||
return;
|
||||
}
|
||||
if (tagData._metallicRoughnessTextureSrc) {
|
||||
const image = await loadImage(tagData._metallicRoughnessTextureSrc);
|
||||
tagData.createMetallicRoughnessTexture(image);
|
||||
return;
|
||||
}
|
||||
if (tagData._metallicRoughnessTextureImage) {
|
||||
await tagData._metallicRoughnessTextureImage.decode();
|
||||
tagData.createMetallicRoughnessTexture(tagData._metallicRoughnessTextureImage);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -46,6 +46,9 @@ export class InstanceData {
|
||||
public _colorTextureSrc: string | null;
|
||||
public _normalTextureSrc: string | null;
|
||||
public _metallicRoughnessTextureSrc: string | null;
|
||||
public _colorTextureImage: HTMLImageElement | null;
|
||||
public _normalTextureImage: HTMLImageElement | null;
|
||||
public _metallicRoughnessTextureImage: HTMLImageElement | null;
|
||||
|
||||
public _objectSrc?: string;
|
||||
|
||||
@ -99,12 +102,15 @@ export class InstanceData {
|
||||
|
||||
this._colorTexture = null;
|
||||
this._colorTextureSrc = null;
|
||||
this._colorTextureImage = null;
|
||||
|
||||
this._normalTexture = null;
|
||||
this._normalTextureSrc = null;
|
||||
this._normalTextureImage = null;
|
||||
|
||||
this._metallicRoughnessTexture = null;
|
||||
this._metallicRoughnessTextureSrc = null;
|
||||
this._metallicRoughnessTextureImage = null;
|
||||
|
||||
this._sizeArr = [];
|
||||
this._translateArr = [];
|
||||
@ -413,7 +419,6 @@ export class InstanceData {
|
||||
}
|
||||
|
||||
this._sizeArr = makeArrayTyped(this._sizeArr);
|
||||
|
||||
h.setStreamArrayBuffer(this._sizeBuffer, this._sizeArr as Float32Array);
|
||||
}
|
||||
|
||||
|
||||
@ -86,6 +86,7 @@ import {
|
||||
} from './terrain/index';
|
||||
|
||||
import {MoveAxisEntity} from "./control/geoObjectEditor/MoveAxisEntity";
|
||||
import { Gltf } from './utils/gltf/gltfParser';
|
||||
|
||||
export {
|
||||
bv,
|
||||
@ -142,6 +143,6 @@ export {
|
||||
EarthQuadTreeStrategy,
|
||||
Wgs84QuadTreeStrategy,
|
||||
Object3d,
|
||||
|
||||
Gltf,
|
||||
MoveAxisEntity,
|
||||
};
|
||||
87
src/utils/gltf/glbParser.ts
Normal file
87
src/utils/gltf/glbParser.ts
Normal file
@ -0,0 +1,87 @@
|
||||
import { GltfData, GltfMetadata } from "./types";
|
||||
|
||||
interface GlbChunk {
|
||||
length: number;
|
||||
type: ChunkType;
|
||||
chunkData: ArrayBuffer;
|
||||
}
|
||||
|
||||
enum ChunkType {
|
||||
JSON,
|
||||
BIN,
|
||||
INVALID
|
||||
}
|
||||
|
||||
export class Glb {
|
||||
public static async load(src: string): Promise<GltfData> {
|
||||
const response = await fetch(src);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Unable to load '${src}'`);
|
||||
}
|
||||
|
||||
const buffer = await response.arrayBuffer();
|
||||
|
||||
const dv = new DataView(buffer);
|
||||
const magic = dv.getUint32(0, true);
|
||||
|
||||
if (magic !== 0x46546c67) {
|
||||
throw new Error("Not a valid GLB");
|
||||
}
|
||||
const version = dv.getUint32(4, true);
|
||||
const chunks = this.getChunks(dv);
|
||||
return this.parseChunks(chunks);
|
||||
}
|
||||
|
||||
private static getChunks(dv: DataView): GlbChunk[] {
|
||||
const chunks: GlbChunk[] = [];
|
||||
let currentOffset = 12; // skip magic, version and total length
|
||||
do {
|
||||
const chunk = this.getChunk(dv, currentOffset);
|
||||
currentOffset += 8 + chunk.length;
|
||||
chunks.push(chunk);
|
||||
} while (currentOffset < dv.byteLength);
|
||||
return chunks;
|
||||
}
|
||||
|
||||
private static getChunk(dv: DataView, offset: number): GlbChunk {
|
||||
const length = dv.getUint32(offset, true);
|
||||
const type = this.getChunkType(dv.getUint32(offset + 4, true));
|
||||
const chunkData = dv.buffer.slice(offset + 8, offset + 8 + length);
|
||||
return { length, type, chunkData };
|
||||
}
|
||||
|
||||
private static getChunkType(type: number): ChunkType {
|
||||
switch (type) {
|
||||
case 0x4e4f534a:
|
||||
return ChunkType.JSON;
|
||||
case 0x004e4942:
|
||||
return ChunkType.BIN;
|
||||
default:
|
||||
return ChunkType.INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
private static parseChunks(chunks: GlbChunk[]): GltfData {
|
||||
const result = {
|
||||
gltf: null as unknown as GltfMetadata,
|
||||
bin: [] as ArrayBuffer[],
|
||||
};
|
||||
for (const chunk of chunks) {
|
||||
switch (chunk.type) {
|
||||
case ChunkType.JSON:
|
||||
result.gltf = this.parseJsonChunk(chunk);
|
||||
break;
|
||||
case ChunkType.BIN:
|
||||
result.bin.push(chunk.chunkData);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static parseJsonChunk(chunk: GlbChunk): GltfMetadata {
|
||||
return JSON.parse(new TextDecoder().decode(chunk.chunkData));
|
||||
}
|
||||
}
|
||||
442
src/utils/gltf/gltfParser.ts
Normal file
442
src/utils/gltf/gltfParser.ts
Normal file
@ -0,0 +1,442 @@
|
||||
import { Entity } from "../../entity";
|
||||
import { Vec3 } from "../../math/Vec3";
|
||||
import { Object3d } from "../../Object3d";
|
||||
import { Glb } from "./glbParser";
|
||||
import {
|
||||
Accessor,
|
||||
AccessorComponentType,
|
||||
AccessorDataType,
|
||||
GltfData,
|
||||
TextureImage,
|
||||
Material,
|
||||
Mesh,
|
||||
Primitive,
|
||||
PrimitiveMode,
|
||||
Texture,
|
||||
MimeType,
|
||||
GltfNode,
|
||||
GltfMesh,
|
||||
GltfPrimitive
|
||||
} from "./types";
|
||||
|
||||
export class Gltf {
|
||||
private static dracoDecoderModule: any = null;
|
||||
public static connectDracoDecoderModule(decoder: any): void {
|
||||
this.dracoDecoderModule = decoder;
|
||||
}
|
||||
public static async loadGlb(url: string) {
|
||||
const data = await Glb.load(url);
|
||||
if (this.dracoDecoderModule !== null) {
|
||||
await this.dracoDecoderModule.ready;
|
||||
}
|
||||
console.log("load glb", data);
|
||||
return new Gltf(data);
|
||||
}
|
||||
|
||||
private materials: Material[] = [];
|
||||
private images: TextureImage[] = [];
|
||||
public meshes: Mesh[] = [];
|
||||
|
||||
constructor(private gltf: GltfData) {
|
||||
if (
|
||||
gltf.gltf.extensionsRequired?.includes("KHR_draco_mesh_compression") &&
|
||||
Gltf.dracoDecoderModule === null
|
||||
) {
|
||||
throw new Error("Unable to import GLTF. Draco decoder module is not connected");
|
||||
}
|
||||
this.initImages();
|
||||
this.initMaterials();
|
||||
this.initMeshes();
|
||||
}
|
||||
|
||||
public getObjects3d(): Object3d[] {
|
||||
return this.meshes
|
||||
.map((mesh) => mesh.primitives.map((primitive) => Gltf.toObject3d(primitive)))
|
||||
.flat();
|
||||
}
|
||||
|
||||
public toEntities(): Entity[] {
|
||||
const result: Entity[] = [];
|
||||
for (const scene of this.gltf.gltf.scenes) {
|
||||
if (scene === undefined || scene.nodes === undefined) {
|
||||
return [];
|
||||
}
|
||||
for (const node of scene.nodes) {
|
||||
const nodeData = this.gltf.gltf.nodes[node];
|
||||
if (nodeData.mesh === undefined && nodeData.children === undefined) {
|
||||
continue;
|
||||
}
|
||||
result.push(this.nodeToEntity(nodeData));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private nodeToEntity(node: GltfNode): Entity {
|
||||
const entity = new Entity({
|
||||
name: `node_${node.name}`,
|
||||
cartesian: new Vec3(0, 0, 0),
|
||||
relativePosition: true,
|
||||
});
|
||||
let meshEntity: Entity | null = null;
|
||||
if (node.translation !== undefined) {
|
||||
entity.relativePosition = true;
|
||||
entity.setCartesian(node.translation[0], node.translation[1], node.translation[2]);
|
||||
}
|
||||
if (node.rotation !== undefined) {
|
||||
// TODO: implement rotation by quaternion
|
||||
}
|
||||
if (node.matrix !== undefined) {
|
||||
// TODO: implement matrix apply
|
||||
}
|
||||
if (node.scale !== undefined) {
|
||||
entity.setScale3v(new Vec3(node.scale[0], node.scale[1], node.scale[2]));
|
||||
}
|
||||
if (node.mesh !== undefined) {
|
||||
meshEntity = this.meshToEntity(this.meshes[node.mesh]);
|
||||
entity.appendChild(meshEntity);
|
||||
}
|
||||
if (node.children !== undefined) {
|
||||
for (const child of node.children) {
|
||||
const childEntity = this.nodeToEntity(this.gltf.gltf.nodes[child]);
|
||||
if (meshEntity) {
|
||||
meshEntity.appendChild(childEntity);
|
||||
} else {
|
||||
entity.appendChild(childEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
|
||||
public meshToEntity(mesh: Mesh): Entity {
|
||||
const entity = new Entity({
|
||||
name: mesh.name,
|
||||
cartesian: new Vec3(0, 0, 0),
|
||||
relativePosition: true,
|
||||
independentPicking: true
|
||||
});
|
||||
mesh.primitives.map((primitive) => {
|
||||
entity.appendChild(
|
||||
new Entity({
|
||||
name: primitive.name,
|
||||
relativePosition: true,
|
||||
geoObject: {
|
||||
object3d: primitive.object3d,
|
||||
tag: primitive.name
|
||||
}
|
||||
})
|
||||
);
|
||||
});
|
||||
return entity;
|
||||
}
|
||||
|
||||
private initImages() {
|
||||
if (!this.gltf.gltf.images) {
|
||||
return;
|
||||
}
|
||||
for (const image of this.gltf.gltf.images) {
|
||||
this.images.push({
|
||||
src: image.uri,
|
||||
element: this.getImage(image.mimeType, image.bufferView),
|
||||
mimeType: image.mimeType,
|
||||
name: image.name
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private getImage(mimeType?: MimeType, bufferView?: number): HTMLImageElement | undefined {
|
||||
if (bufferView && mimeType) {
|
||||
const view = this.gltf.gltf.bufferViews[bufferView];
|
||||
const url = URL.createObjectURL(
|
||||
new Blob(
|
||||
[
|
||||
this.gltf.bin[view.buffer].slice(
|
||||
view.byteOffset,
|
||||
view.byteOffset + view.byteLength
|
||||
)
|
||||
],
|
||||
{
|
||||
type: mimeType
|
||||
}
|
||||
)
|
||||
);
|
||||
const img = new Image();
|
||||
img.src = url;
|
||||
return img;
|
||||
}
|
||||
}
|
||||
|
||||
private initMaterials() {
|
||||
if (!this.gltf.gltf.materials) {
|
||||
return;
|
||||
}
|
||||
for (const material of this.gltf.gltf.materials) {
|
||||
const mat: Material = {
|
||||
name: material.name,
|
||||
emissiveFactor: material.emissiveFactor,
|
||||
alphaMode: material.alphaMode,
|
||||
alphaCutoff: material.alphaCutoff,
|
||||
doubleSided: material.doubleSided
|
||||
};
|
||||
if (material.pbrMetallicRoughness) {
|
||||
if (material.pbrMetallicRoughness.baseColorFactor) {
|
||||
mat.baseColorFactor = material.pbrMetallicRoughness.baseColorFactor;
|
||||
}
|
||||
if (material.pbrMetallicRoughness.baseColorTexture) {
|
||||
const source =
|
||||
this.gltf.gltf.textures[
|
||||
material.pbrMetallicRoughness.baseColorTexture.index
|
||||
].source;
|
||||
if (source !== undefined) {
|
||||
mat.baseColorTexture = {
|
||||
image: this.images[source],
|
||||
texCoord: material.pbrMetallicRoughness.baseColorTexture.texCoord
|
||||
};
|
||||
}
|
||||
}
|
||||
if (material.pbrMetallicRoughness.metallicRoughnessTexture) {
|
||||
const source =
|
||||
this.gltf.gltf.textures[
|
||||
material.pbrMetallicRoughness.metallicRoughnessTexture.index
|
||||
].source;
|
||||
if (source !== undefined) {
|
||||
mat.metallicRoughnessTexture = {
|
||||
image: this.images[source],
|
||||
texCoord:
|
||||
material.pbrMetallicRoughness.metallicRoughnessTexture.texCoord
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
if (material.normalTexture) {
|
||||
const source = this.gltf.gltf.textures[material.normalTexture.index].source;
|
||||
if (source !== undefined) {
|
||||
mat.normalTexture = {
|
||||
image: this.images[source],
|
||||
texCoord: material.normalTexture.texCoord,
|
||||
scale: material.normalTexture.scale
|
||||
};
|
||||
}
|
||||
}
|
||||
if (material.occlusionTexture) {
|
||||
const source = this.gltf.gltf.textures[material.occlusionTexture.index].source;
|
||||
if (source !== undefined) {
|
||||
mat.occlusionTexture = {
|
||||
image: this.images[source],
|
||||
texCoord: material.occlusionTexture.texCoord,
|
||||
strength: material.occlusionTexture.strength
|
||||
};
|
||||
}
|
||||
}
|
||||
if (material.emissiveTexture) {
|
||||
const source = this.gltf.gltf.textures[material.emissiveTexture.index].source;
|
||||
if (source !== undefined) {
|
||||
mat.emissiveTexture = {
|
||||
image: this.images[source],
|
||||
texCoord: material.emissiveTexture.texCoord
|
||||
};
|
||||
}
|
||||
}
|
||||
this.materials.push(mat);
|
||||
}
|
||||
}
|
||||
|
||||
private initMeshes() {
|
||||
this.meshes = [];
|
||||
for (const meshData of this.gltf.gltf.meshes) {
|
||||
const mesh: Mesh = {
|
||||
name: meshData.name,
|
||||
primitives: []
|
||||
};
|
||||
for (let i = 0; i < meshData.primitives.length; i++) {
|
||||
mesh.primitives.push(this.buildPrimitive(meshData, meshData.primitives[i], i));
|
||||
}
|
||||
this.meshes.push(mesh);
|
||||
}
|
||||
}
|
||||
|
||||
private buildPrimitive(
|
||||
meshData: GltfMesh,
|
||||
primitiveData: GltfPrimitive,
|
||||
index: number = 0
|
||||
): Primitive {
|
||||
let primitive: Primitive | null = null;
|
||||
const material = this.materials[primitiveData.material || 0];
|
||||
const texcoord = material.baseColorTexture?.texCoord
|
||||
? `TEXCOORD_${material.baseColorTexture.texCoord}`
|
||||
: `TEXCOORD_0`;
|
||||
if (primitiveData.extensions?.KHR_draco_mesh_compression) {
|
||||
const dracoExt = primitiveData.extensions.KHR_draco_mesh_compression;
|
||||
const bufferView = this.gltf.gltf.bufferViews[dracoExt.bufferView];
|
||||
const bvOffset = bufferView.byteOffset || 0;
|
||||
const draco = Gltf.dracoDecoderModule;
|
||||
const decoder = new draco.Decoder();
|
||||
const decoderBuffer = new draco.DecoderBuffer();
|
||||
decoderBuffer.Init(
|
||||
new Uint8Array(
|
||||
this.gltf.bin[bufferView.buffer].slice(
|
||||
bvOffset,
|
||||
bvOffset + bufferView.byteLength
|
||||
)
|
||||
),
|
||||
bufferView.byteLength
|
||||
);
|
||||
|
||||
const geometryType = decoder.GetEncodedGeometryType(decoderBuffer);
|
||||
if (geometryType !== draco.TRIANGULAR_MESH) {
|
||||
throw new Error("Draco compressed data is not a mesh");
|
||||
}
|
||||
|
||||
const mesh = new draco.Mesh();
|
||||
const status = decoder.DecodeBufferToMesh(decoderBuffer, mesh);
|
||||
if (!status.ok() || mesh.ptr === 0) {
|
||||
throw new Error("Failed to decode Draco mesh");
|
||||
}
|
||||
|
||||
const numFaces = mesh.num_faces();
|
||||
const numIndices = numFaces * 3;
|
||||
const indices = new Uint32Array(numIndices);
|
||||
const ia = new draco.DracoInt32Array();
|
||||
for (let i = 0; i < numFaces; i++) {
|
||||
decoder.GetFaceFromMesh(mesh, i, ia);
|
||||
indices[i * 3] = ia.GetValue(0);
|
||||
indices[i * 3 + 1] = ia.GetValue(1);
|
||||
indices[i * 3 + 2] = ia.GetValue(2);
|
||||
}
|
||||
draco.destroy(ia);
|
||||
|
||||
const attributes: { [name: string]: Float32Array } = {};
|
||||
for (const gltfAttrName in dracoExt.attributes) {
|
||||
const attrId = dracoExt.attributes[gltfAttrName];
|
||||
const dracoAttr = decoder.GetAttributeByUniqueId(mesh, attrId);
|
||||
const numPoints = mesh.num_points();
|
||||
const numComponents = dracoAttr.num_components(); // 3 for POSITION, 2 for UVs, etc.
|
||||
|
||||
const attrArray = new draco.DracoFloat32Array();
|
||||
decoder.GetAttributeFloatForAllPoints(mesh, dracoAttr, attrArray);
|
||||
|
||||
const typedArray = new Float32Array(numPoints * numComponents);
|
||||
for (let i = 0; i < typedArray.length; i++) {
|
||||
typedArray[i] = attrArray.GetValue(i);
|
||||
}
|
||||
draco.destroy(attrArray);
|
||||
|
||||
attributes[gltfAttrName] = typedArray;
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
draco.destroy(mesh);
|
||||
draco.destroy(decoderBuffer);
|
||||
draco.destroy(decoder);
|
||||
|
||||
primitive = {
|
||||
name: `${meshData.name}_${material.name}_${index}`,
|
||||
vertices: attributes.POSITION,
|
||||
indices: indices,
|
||||
mode: primitiveData.mode ? primitiveData.mode : PrimitiveMode.triangles,
|
||||
material: this.materials[primitiveData.material || 0] || undefined,
|
||||
normals: attributes.NORMAL,
|
||||
texCoords: undefined
|
||||
};
|
||||
} else {
|
||||
const texcoordAccessorKey = texcoord ? primitiveData.attributes[texcoord] : undefined;
|
||||
const texcoordAccessor = texcoordAccessorKey
|
||||
? this.gltf.gltf.accessors[texcoordAccessorKey]
|
||||
: undefined;
|
||||
primitive = {
|
||||
name: `${meshData.name}_${material.name}_${index}`,
|
||||
indices: primitiveData.indices
|
||||
? Gltf.access(this.gltf.gltf.accessors[primitiveData.indices], this.gltf)
|
||||
: undefined,
|
||||
mode: primitiveData.mode ? primitiveData.mode : PrimitiveMode.triangles,
|
||||
material: this.materials[primitiveData.material || 0] || undefined,
|
||||
vertices: Gltf.access(
|
||||
this.gltf.gltf.accessors[primitiveData.attributes.POSITION],
|
||||
this.gltf
|
||||
),
|
||||
normals: Gltf.access(
|
||||
this.gltf.gltf.accessors[primitiveData.attributes.NORMAL],
|
||||
this.gltf
|
||||
),
|
||||
texCoords: texcoordAccessor ? Gltf.access(texcoordAccessor, this.gltf) : undefined
|
||||
};
|
||||
}
|
||||
if (primitive === null) {
|
||||
throw new Error("Unable to build primitive");
|
||||
}
|
||||
primitive.object3d = Gltf.toObject3d(primitive);
|
||||
return primitive;
|
||||
}
|
||||
|
||||
private static toObject3d(primitive: Primitive): Object3d {
|
||||
console.log('building object3d', primitive);
|
||||
return new Object3d({
|
||||
name: primitive.name,
|
||||
vertices: Array.from(primitive.vertices as Float32Array),
|
||||
normals: Array.from(primitive.normals as Float32Array),
|
||||
texCoords: primitive.texCoords
|
||||
? Array.from(primitive.texCoords as Float32Array)
|
||||
: undefined,
|
||||
indices: Array.from(primitive.indices as Uint8Array),
|
||||
normalTextureImage: primitive.material?.normalTexture?.image.element,
|
||||
normalTextureSrc: primitive.material?.normalTexture?.image.src,
|
||||
colorTextureImage: primitive.material?.baseColorTexture?.image.element,
|
||||
colorTextureSrc: primitive.material?.baseColorTexture?.image.src,
|
||||
metallicRoughnessTextureImage: primitive.material?.occlusionTexture?.image.element,
|
||||
metallicRoughnessTextureSrc: primitive.material?.occlusionTexture?.image.src,
|
||||
color: primitive.material?.baseColorFactor,
|
||||
});
|
||||
}
|
||||
|
||||
private static access(accessor: Accessor, gltf: GltfData): ArrayBufferLike {
|
||||
const bufferView = gltf.gltf.bufferViews[accessor.bufferView];
|
||||
const arrbuff = gltf.bin[bufferView.buffer];
|
||||
const offset = bufferView.byteOffset || 0;
|
||||
const dv = arrbuff.slice(offset, offset + bufferView.byteLength);
|
||||
switch (accessor.type) {
|
||||
case AccessorDataType.scalar:
|
||||
return this.getTensor(dv, accessor, 1);
|
||||
case AccessorDataType.vec2:
|
||||
return this.getTensor(dv, accessor, 2);
|
||||
case AccessorDataType.vec3:
|
||||
return this.getTensor(dv, accessor, 3);
|
||||
case AccessorDataType.vec4:
|
||||
case AccessorDataType.mat2:
|
||||
return this.getTensor(dv, accessor, 4);
|
||||
case AccessorDataType.mat3:
|
||||
return this.getTensor(dv, accessor, 9);
|
||||
case AccessorDataType.mat4:
|
||||
return this.getTensor(dv, accessor, 16);
|
||||
default:
|
||||
throw new Error("Unknown accessor type");
|
||||
}
|
||||
}
|
||||
|
||||
private static getTensor(
|
||||
buffer: ArrayBuffer,
|
||||
accessor: Accessor,
|
||||
numOfComponents: number
|
||||
): ArrayBufferLike {
|
||||
if (accessor.componentType === AccessorComponentType.ushort) {
|
||||
return new Uint16Array(buffer, 0, accessor.count * numOfComponents);
|
||||
}
|
||||
if (accessor.componentType === AccessorComponentType.short) {
|
||||
return new Int16Array(buffer, 0, accessor.count * numOfComponents);
|
||||
}
|
||||
if (accessor.componentType === AccessorComponentType.uint) {
|
||||
return new Uint32Array(buffer, 0, accessor.count * numOfComponents);
|
||||
}
|
||||
if (accessor.componentType === AccessorComponentType.float) {
|
||||
return new Float32Array(buffer, 0, accessor.count * numOfComponents);
|
||||
}
|
||||
if (accessor.componentType === AccessorComponentType.ubyte) {
|
||||
return new Uint8Array(buffer, 0, accessor.count * numOfComponents);
|
||||
}
|
||||
if (accessor.componentType === AccessorComponentType.byte) {
|
||||
return new Int8Array(buffer, 0, accessor.count * numOfComponents);
|
||||
}
|
||||
throw new Error("Unknown component type");
|
||||
}
|
||||
}
|
||||
216
src/utils/gltf/types.ts
Normal file
216
src/utils/gltf/types.ts
Normal file
@ -0,0 +1,216 @@
|
||||
import { Object3d } from "../../Object3d";
|
||||
|
||||
export interface GltfData {
|
||||
bin: ArrayBuffer[];
|
||||
gltf: GltfMetadata;
|
||||
}
|
||||
|
||||
export interface GltfMetadata {
|
||||
accessors: Accessor[];
|
||||
bufferViews: BufferView[];
|
||||
meshes: GltfMesh[];
|
||||
materials: {
|
||||
name: string;
|
||||
pbrMetallicRoughness?: {
|
||||
baseColorFactor?: number[];
|
||||
metallicFactor?: number;
|
||||
roughnessFactor?: number;
|
||||
baseColorTexture?: {
|
||||
index: number;
|
||||
texCoord?: number;
|
||||
};
|
||||
metallicRoughnessTexture?: {
|
||||
index: number;
|
||||
texCoord?: number;
|
||||
};
|
||||
};
|
||||
normalTexture?: {
|
||||
index: number;
|
||||
texCoord?: number;
|
||||
scale?: number;
|
||||
};
|
||||
occlusionTexture?: {
|
||||
index: number;
|
||||
texCoord?: number;
|
||||
strength?: number;
|
||||
};
|
||||
emissiveTexture?: {
|
||||
index: number;
|
||||
texCoord?: number;
|
||||
};
|
||||
emissiveFactor?: number[];
|
||||
alphaMode?: AlphaMode;
|
||||
alphaCutoff?: number;
|
||||
doubleSided?: boolean;
|
||||
}[];
|
||||
textures: {
|
||||
sampler?: number;
|
||||
source?: number;
|
||||
name?: string;
|
||||
}[];
|
||||
images?: {
|
||||
uri?: string;
|
||||
mimeType?: MimeType;
|
||||
bufferView?: number;
|
||||
name?: string;
|
||||
}[];
|
||||
scene: number;
|
||||
scenes: {
|
||||
nodes?: number[];
|
||||
name?: string;
|
||||
}[];
|
||||
nodes: GltfNode[];
|
||||
extensionsRequired?: string[];
|
||||
extensionsUsed?: string[];
|
||||
}
|
||||
|
||||
export interface GltfMesh {
|
||||
name: string;
|
||||
primitives: GltfPrimitive[];
|
||||
}
|
||||
|
||||
export interface GltfPrimitive {
|
||||
indices?: number;
|
||||
material?: number;
|
||||
mode?: PrimitiveMode;
|
||||
attributes: {
|
||||
POSITION: number;
|
||||
NORMAL: number;
|
||||
[key: string]: any;
|
||||
};
|
||||
extensions?: {
|
||||
[key: string]: any;
|
||||
KHR_draco_mesh_compression?: {
|
||||
bufferView: number;
|
||||
attributes: {
|
||||
POSITION: number;
|
||||
NORMAL: number;
|
||||
[key: string]: any;
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export interface GltfNode {
|
||||
camera?: number;
|
||||
children?: number[];
|
||||
skin?: number;
|
||||
matrix?: number[];
|
||||
mesh?: number;
|
||||
rotation?: number[];
|
||||
scale?: number[];
|
||||
translation?: number[];
|
||||
weights?: number[];
|
||||
name?: string;
|
||||
}
|
||||
|
||||
export enum MimeType {
|
||||
JPEG = "image/jpeg",
|
||||
PNG = "image/png"
|
||||
}
|
||||
|
||||
export interface Accessor {
|
||||
bufferView: number;
|
||||
componentType: AccessorComponentType;
|
||||
count: number;
|
||||
type: AccessorDataType;
|
||||
max: number[];
|
||||
min: number[];
|
||||
}
|
||||
|
||||
export interface BufferView {
|
||||
buffer: number;
|
||||
byteLength: number;
|
||||
byteOffset: number;
|
||||
target?: BufferViewTarget;
|
||||
}
|
||||
|
||||
export enum BufferViewTarget {
|
||||
ARRAY_BUFFER = 34962,
|
||||
ELEMENT_ARRAY_BUFFER = 34963
|
||||
}
|
||||
|
||||
export enum AccessorComponentType {
|
||||
byte = 5120,
|
||||
ubyte = 5121,
|
||||
short = 5122,
|
||||
ushort = 5123,
|
||||
uint = 5125,
|
||||
float = 5126
|
||||
}
|
||||
|
||||
export enum AccessorDataType {
|
||||
scalar = "SCALAR",
|
||||
vec2 = "VEC2",
|
||||
vec3 = "VEC3",
|
||||
vec4 = "VEC4",
|
||||
mat2 = "MAT2",
|
||||
mat3 = "MAT3",
|
||||
mat4 = "MAT4"
|
||||
}
|
||||
|
||||
export interface Mesh {
|
||||
name: string;
|
||||
primitives: Primitive[];
|
||||
}
|
||||
|
||||
export interface Primitive {
|
||||
name: string;
|
||||
indices?: ArrayBufferLike;
|
||||
vertices: ArrayBufferLike;
|
||||
normals: ArrayBufferLike;
|
||||
texCoords?: ArrayBufferLike;
|
||||
material?: Material;
|
||||
mode: PrimitiveMode;
|
||||
object3d?: Object3d;
|
||||
}
|
||||
|
||||
export interface Material {
|
||||
name: string;
|
||||
baseColorFactor?: number[];
|
||||
baseColorTexture?: Texture;
|
||||
metallicRoughnessTexture?: Texture;
|
||||
normalTexture?: NormalTexture;
|
||||
occlusionTexture?: OcclusionTexture;
|
||||
emissiveTexture?: Texture;
|
||||
emissiveFactor?: number[];
|
||||
alphaMode?: AlphaMode;
|
||||
alphaCutoff?: number;
|
||||
doubleSided?: boolean;
|
||||
}
|
||||
|
||||
export interface Texture {
|
||||
image: TextureImage;
|
||||
texCoord?: number;
|
||||
}
|
||||
|
||||
export interface NormalTexture extends Texture {
|
||||
scale?: number;
|
||||
}
|
||||
|
||||
export interface OcclusionTexture extends Texture {
|
||||
strength?: number;
|
||||
}
|
||||
|
||||
export interface TextureImage {
|
||||
src?: string;
|
||||
element?: HTMLImageElement;
|
||||
mimeType?: MimeType;
|
||||
name?: string;
|
||||
}
|
||||
|
||||
export enum AlphaMode {
|
||||
OPAQUE = "OPAQUE",
|
||||
MASK = "MASK",
|
||||
BLEND = "BLEND"
|
||||
}
|
||||
|
||||
export enum PrimitiveMode {
|
||||
points = 0,
|
||||
lines = 1,
|
||||
lineLoop = 2,
|
||||
lineStrip = 3,
|
||||
triangles = 4,
|
||||
triangleStrip = 5,
|
||||
triangleFan = 6
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user