xeokit / xeokit-sdk

Open source JavaScript SDK for viewing high-detail, full-precision 3D BIM and AEC models in the Web browser.
https://xeokit.io
Other
737 stars 291 forks source link

XKT V2 #152

Closed xeolabs closed 4 years ago

xeolabs commented 5 years ago

Upgrade the XKT format to support geometry reuse.

Amoki commented 5 years ago

When loading an XKT from a file with

this.model = this.xktLoader.load({
  xkt: file,
  edges: false,
});

performanceModel.fire("loaded", true, true); is never called

xeolabs commented 5 years ago

@Amoki fixed in https://github.com/xeokit/xeokit-sdk/commit/0a5905639df77ef5b0998602e5535f9ad27c398f

Amoki commented 5 years ago

I have few issues with XKT v2 :/

Many elements are flattened : Screenshot from 2019-09-26 11-28-41 All fire hoses and many pipes are flat, as if they're in 2D.

Here is the conversion log:

npx @bimdata/gltf-to-xkt bugged.gltf bugged.xkt    # Converting gltf to xtk is very simple!
npx: installed 4 in 1.022s
[INFO] Converting glTF bugged.gltf to xkt bugged.xkt
Number of objects: 12474
Only once meshes: 11856
More than once meshes: 618
Total mesh instances: 24925
Instancing factor 52.43%
arrayBuffer takes 6065.123 kB

When unsuccessfully trying to isolate the bug (I can't share the full GLTF), I found another:

GLTF: Screenshot from 2019-09-26 11-14-52

XKT: Screenshot from 2019-09-26 11-15-42

Here are the files: bug1.zip

I'm still trying to isolate the first bug

xeolabs commented 5 years ago

@Amoki I'm going to add a switch to xeokit-gltf-to-xkt so that we can make it convert to XKT V1, so we can isolate whether it's a XKT V2 specific bug.

Amoki commented 5 years ago

The first bug (plat pipes) is XKTv2 only.

The second bug is both XKT 1 and 2

tmarti commented 5 years ago

Just a question: could this be the case of nested GLTF nodes?

(I'm on the mobile now and can't open the GLTF file with a text editor.)

tmarti commented 5 years ago

@Amoki, just downloaded your bug1.zip file and visualized the GLTF file with a GLTF model viewer installed from Google Play.

The pipe appers flat.

(Maybe I misunderstood the problem, but) could this be a problem with your GLTF generator?

Amoki commented 5 years ago

On the bug1.zip GLTF, there are two fire hoses and two red marks that are flat. The bug is there are two of them in the GLTF and only one is shown on the XKT.

On the full model that I can't share, the GLTF is OK (so the generation doesn't seem to be a problem), but pipes are flat in the XKT.

Here are more screenshots: GLTF: Screenshot from 2019-09-26 18-27-38 Screenshot from 2019-09-26 18-30-00

XKTv2: Screenshot from 2019-09-26 18-28-32

Screenshot from 2019-09-26 18-29-18

tmarti commented 5 years ago

Now I see.

On the deflateData function of modelToXKT.js, can you add the following line (as first line in the function)?

console.log (data.positionsDecodeMatrix);

And put here the generated output?

That comes from an idea from Lindsay about quantization problems.

tmarti commented 5 years ago

Also, another request:

On the last line of the finalize method of Model.js, can you add:

console.log (aabb)

And put the output here?

tmarti commented 5 years ago

If the idea from @xeolabs about the quantization problem is right, it's possible that you won't be able to reproduce the problem by using an isolated case with a simplified GLTF model.

If the idea is right, the problem comes from the fact that the bounding box the complete scene is large, and instanced meshes (meshes, not nodes) bounding boxes are small when compared to the complete scene bounding box.

This will be seen from the two requested console.log statements.

The allowed accuracy is about scene_bbox/2^16.

This means that if the bbox of the scene is 100m, you get an accuracy of 100/65536, which is about 1.5mm.

tmarti commented 5 years ago

Just another comment (sorry this looks like a twitter thread 😜).

If the supposition is true, it simply means different position decode matrices are needed for instanced meshes and for non-instanced meshes.

The problem is as follows, when it comes to calculate the global bounding box:

Let's put the example that scene X extent is 100m, but the left side of the bouding box is at X coordinate +10000. And there is some instanced mesh defined in local coordinates centered at X = 0.

Then, due to the mixed way of calculating bounding boxes, the generation time scene bounding box has a bounding box with X size = 10000.

And then the accuracy is not anymore 100/2^16=1.5mm (as it would be expected from the scene bbox), but 10000/2^16=15cm (due to perceived bounding box caused by the encoding mechanism).

Still suppositon, but if that is true, a simple way to have all meshes encoded in absolute world coordinates (and thus precenting that bounding box expansion (that leads to decreaded accuracy)), would be, for each instanced mesh, apply the placement matrix of the first node that uses the mesh, and for further nodes using the same mesh, calculate a composite matrix M1*M2, where M1 undoes the placement done by the first-using node, and M2 are further nodes' placement matrices.

That would be a very simple way of solving this concrete problem 😊 : would keep the encoding process very simple, and would prevent adding additional position decode matrices in the binary file. After all, if some geometry is instanced and some not, the accuracy would be still limited by the REAL scene bbox, not the expanded one.

What do you think, @xeolabs?

Amoki commented 5 years ago

Convert output:

AABB Float32Array [
  -11.51853084564209,
  -157.57345581054688,
  -2.8572535514831543,
  253.8859405517578,
  21.95928955078125,
  29637.595703125 ]
Number of objects: 12474
Only once meshes: 11856
More than once meshes: 618
Total mesh instances: 24925
Instancing factor 52.43%
positionsDecodeMatrix Float32Array [
  0.004050430841743946,
  0,
  0,
  0,
  0,
  0.0027399121318012476,
  0,
  0,
  0,
  0,
  0.4523533582687378,
  0,
  -11.51853084564209,
  -157.57345581054688,
  -2.8572535514831543,
  1 ]
arrayBuffer takes 6065.123 kB

The model is about 250m long.

yes, we couldn't reproduce the bug with a subset of the model...

tmarti commented 5 years ago

Ok:

On the bug1.zip GLTF, there are two fire hoses and two red marks that are flat. The bug is there are two of them in the GLTF and only one is shown on the XKT.

There is indeed a bug in XKTLoaderPlugin.js bug that prevents loading the last entity (sic!).

The following line of code...

https://github.com/xeokit/xeokit-sdk/blob/d596400e057187d55eab2e444b1eb208a1063856/src/plugins/XKTLoaderPlugin/XKTLoaderPlugin.js#L735

... should be replaced with:

for (let j = entityMeshes [i], jlen = lastEntity ? entityMeshIds.length : entityMeshes [i + 1]; j < jlen; j++) {
tmarti commented 5 years ago

That's it!

Convert output:

 -11.51853084564209,
 -157.57345581054688,
 -2.8572535514831543, => z_min
 253.8859405517578,
 21.95928955078125,
 *29637.595703125 => z_max
]

The Z extent of the bbox (z_max - z_min) is 29.640 km.

This gives us an accuracy of 29640 m / 2^16 = 45cm, and for this reason the objects appear flattened.

Lets try that idea of positions decode matrices :), will come back here later.

tmarti commented 5 years ago

Indeed, those factors in the positionsDecodeMatrix are the accuracy in model units (meters in our use cases):

positionsDecodeMatrix Float32Array [
 0.004050430841743946, => X_accuracy
 0,
 0,
 0,
 0,
 0.0027399121318012476, => Y_accuracy
 0,
 0,
 0,
 0,
 0.4523533582687378, => Z_accuracy
 0,
 -11.51853084564209,
 -157.57345581054688,
 -2.8572535514831543,
 1 ]
tmarti commented 5 years ago

Ok, for the flattening bug, can you try to replace in the following file...

https://github.com/xeokit/xeokit-gltf-to-xkt/blob/xkt-v2/src/lib/glTFToModel.js

... the following code...

if (meshOnlyUsedOnce) {
    meshMatrix = matrix ? matrix.slice() : math.identityMat4();
    entityMatrix = math.identityMat4();
} else {
    meshMatrix = math.identityMat4();
    entityMatrix = matrix ? matrix.slice() : math.identityMat4();
}

... with the following one?

if (meshOnlyUsedOnce) {
    meshMatrix = matrix ? matrix.slice() : math.identityMat4();
    entityMatrix = math.identityMat4();
} else {
    if (meshInfo._firstMatrixInverse === undefined) {
        meshInfo._firstMatrixInverse = math.inverseMat4 (
            matrix ? matrix.slice() : math.identityMat4()
        ); 

        meshMatrix = matrix ? matrix.slice() : math.identityMat4();
        entityMatrix = math.identityMat4();
    } else {
        meshMatrix = math.identityMat4();
        entityMatrix = math.mulMat4 (
            matrix ? matrix.slice() : math.identityMat4(),
            meshInfo._firstMatrixInverse,
            math.identityMat4 ()
        );
    }
}

And see it that solves the flattening problem?

Amoki commented 5 years ago

It works! :tada: :champagne:

The size increases from 6065.123 kB to 7680.298 kB but it's still awesome compared to the 60MB of the GLTF.

Do you want me to submit the PRs?

tmarti commented 5 years ago

Do you want me to submit the PRs?

👍

The size increases from 6065.123 kB to 7680.298 kB

The size increase is due to the fact that the "lost information" due to wrong quantization was "recovered" 😉

Do you want me to submit the PRs?

Go for it!

PS: The idea that this was a quantization problem came from @xeolabs.