Giter Club home page Giter Club logo

playcanvas-gltf's Introduction

THIS REPO IS NOW DEPRECATED

Please visit:

playcanvas-gltf

gtTF viewer

This repo extends PlayCanvas to support glTF. It contains:

  • A loader script that can convert a glTF or glB file into a PlayCanvas hierarchy.
  • A viewer application that supports drag and drop of glTF and glB files.

The loader script returns a pc.Model structure. It can be used with the standalone Engine or in conjunction with the PlayCanvas Editor.

To see an example of using the loader with the Engine, check out the viewer app in this repo.

To use the loader with the Editor, simply add playcanvas-gltf.js and playcanvas-anim.js into your project (ensuring they are first in your loading order) and call the following API:

API

loadGlb

Parses an ArrayBuffer holding a binary-encoded glTF scene.

loadGlb(glb, device, done);
  • glb - An ArrayBuffer holding the binary glb file data.
  • device - A pc.GraphicsDevice.
  • done - A Function called when the glb has loaded (either successfully or with an error). The function has the following signature: function (err, res) {}. The parameters of the done function are as follows:
    • err - A String describing any error encountered on load. If err is null, the load operation completed successfully.
    • res - An Object holding the loaded PlayCanvas objects.
    • res.model - A pc.Model object representing the glTF scene
    • res.textures - An array of pc.Texture objects
    • res.animations - An array of AnimationClip objects

Example

app.assets.loadFromUrl('assets/monkey/monkey.glb', 'binary', function (err, asset) {
    var glb = asset.resource;
    loadGlb(glb, app.graphicsDevice, function (err, res) {
        // Wrap the model as an asset and add to the asset registry
        var asset = new pc.Asset('gltf', 'model', {
            url: ''
        });
        asset.resource = res.model;
        asset.loaded = true;
        app.assets.add(asset);

        // Add the loaded scene to the hierarchy
        var gltf = new pc.Entity('gltf');
        gltf.addComponent('model', {
            asset: asset
        });
        app.root.addChild(gltf);
    });
});

loadGltf

Parses an in-memory Object hierarchy holding a glTF scene.

loadGltf(gltf, device, done, options);
  • gltf - An Object representing the root of the glTF scene.
  • device - A pc.GraphicsDevice.
  • done - A Function called when the glb has loaded (either successfully or with an error). The function has the following signature: function (err, res) {}. The parameters of the done function are as follows:
    • err - A String describing any error encountered on load. If err is null, the load operation completed successfully.
    • res - An Object holding the loaded PlayCanvas objects.
    • res.model - A pc.Model object representing the glTF scene
    • res.textures - An array of pc.Texture objects
    • res.animations - An array of AnimationClip objects
  • options - An Object specifying optional parameters for the function.
  • options.buffers - An Array of preloaded ArrayBuffer objects holding the glTF file's buffer data.
  • options.basePath - A String set to the relative path of the glTF file.
  • options.processUri - A Function that provides custom loading behavior for URIs encountered during the loading process.

Example

app.assets.loadFromUrl('assets/monkey/monkey.gltf', 'json', function (err, asset) {
    var json = asset.resource;
    var gltf = JSON.parse(json);
    loadGltf(gltf, app.graphicsDevice, function (err, res) {
        // Wrap the model as an asset and add to the asset registry
        var asset = new pc.Asset('gltf', 'model', {
            url: ''
        });
        asset.resource = res.model;
        asset.loaded = true;
        app.assets.add(asset);

        // Add the loaded scene to the hierarchy
        var gltf = new pc.Entity('gltf');
        gltf.addComponent('model', {
            asset: asset
        });
        app.root.addChild(gltf);
    }, {
        basePath: 'assets/monkey/'
    });
});

glTF Viewer

To load the glTF viewer, run a local web-server and load viewer/index.html. You can then drag a glb or gltf file onto the tab's client area to load it. For non-embedded glTF files (with external buffer and image files), you need to drag the containing folder of the glTF file onto the viewer's client area. Binaries for the viewer can be found here.

playcanvas-gltf's People

Contributors

blaizer avatar dependabot[bot] avatar derickwelman avatar ellthompson avatar gameatp avatar guycalledfrank avatar kungfooman avatar mackeyk24 avatar mvaligursky avatar thomasbiang avatar willeastcott avatar yaustar avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

playcanvas-gltf's Issues

Failed to load VoxelCorgi.gltf

I tried to display VoxelCorgi.gltf (output by glTF Exporter of Three.js) on PlayCanvas.
However, it seems to fail to load the model.

PlayCanvas + VoxelCorgi.gltf result:
http://jsdo.it/cx20/MNU4

laycanvas-latest.js:16005 
Uncaught TypeError: Cannot read property 'length' of undefined
    at Object.pc.calculateTangents (playcanvas-latest.js:16005)
    at playcanvas-gltf.js:674
    at Array.forEach (<anonymous>)
    at translateMesh (playcanvas-gltf.js:511)
    at playcanvas-gltf.js:1131
    at Array.map (<anonymous>)
    at parse (playcanvas-gltf.js:1130)
    at playcanvas-gltf.js:1257
    at parse (playcanvas-gltf.js:1119)
    at playcanvas-gltf.js:1255

Three.js + VoxelCorgi.gltf result:
http://jsdo.it/cx20/ahsW

image

Plans for supporting glTF export?

We are in need of a way to serialize or save a PlayCanvas scene or at-least part of a scene to glTF format. In our case, our 3D configurator procedurally builds a scene (ex a vehicle, room or instrument) based on 100-1000+ rules and we want to, for example, allow the user to download the scene as a .glb file and open it in a glTF-supporting AR-capable 3D viewer app on their phone. This is just one of multiple use-cases for us.

Both Babylon.js and three.js support this. Are there any plans on adding support for this in PlayCanvas?

glTF animation will result in an error

I tried displaying the glTF animation model in gltf-test.
However, it seems that an error occurs when trying to display it using the latest playcanvas-anim.js.

Below is the display result of BrainStem.gltf.
https://cdn.rawgit.com/cx20/gltf-test/73431dfccfa70cb753c615079c8b8f77a55d4375/examples/playcanvas/index.html?category=sampleModels&model=BrainStem&scale=1&type=glTF

playcanvas-anim.js:883 
Uncaught ReferenceError: clip is not defined
    at AnimationClip.getAnimTargets (playcanvas-anim.js:883)
    at AnimationSession.play (playcanvas-anim.js:1519)
    at AnimationClip.play (playcanvas-anim.js:902)
    at AnimationComponent.playClip (playcanvas-anim.js:1671)
    at loadGltf.decoderModule (index.js:199)
    at playcanvas-gltf.js:1343
    at parse (playcanvas-gltf.js:1198)
    at playcanvas-gltf.js:1334
    at XMLHttpRequest.xhr.onload (playcanvas-gltf.js:1184)

glTF Draco model of 32-bit index is not displayed correctly

Failed to load Duck.gltf model

I tried loading Duck.gltf using PlayCanvas + glTF Loader.
http://jsdo.it/cx20/yh4S

However, glTF-Embedded format can be load, but otherwise it got an error.

[OK] https://github.com/KhronosGroup/glTF-Sample-Models/blob/master/2.0/Duck/glTF-Embedded/Duck.gltf
[NG] https://github.com/KhronosGroup/glTF-Sample-Models/blob/master/2.0/Duck/glTF/Duck.gltf

gltf-loader.js:5 
Uncaught DOMException: Failed to execute 'atob' on 'Window': The string to be decoded is not correctly encoded.
    at dataURItoArrayBuffer (https://cdn.rawgit.com/playcanvas/playcanvas-gltf/5489ff62/src/gltf-loader.js:5:26)
    at https://cdn.rawgit.com/playcanvas/playcanvas-gltf/5489ff62/src/gltf-loader.js:516:35
    at Array.forEach (<anonymous>)
    at loadBuffers (https://cdn.rawgit.com/playcanvas/playcanvas-gltf/5489ff62/src/gltf-loader.js:515:26)
    at loadGltf (https://cdn.rawgit.com/playcanvas/playcanvas-gltf/5489ff62/src/gltf-loader.js:602:54)
    at XMLHttpRequest.req.onload (http://jsrun.it/cx20/yh4S:132:16)

https://github.com/playcanvas/playcanvas-gltf/blob/master/src/gltf-loader.js#L5

If the normal glTF format is supported, I will add playcanvas-gltf to gltf-test.

PlayCanvas Online glTF Viewer does not support Binary glTF format

I tried displaying two kinds of glTF formats using PlayCanvas Online glTF Viewer.

Regular glTF file is displayed correctly.
https://playcanvas.github.io/playcanvas-gltf/viewer/index.html?assetUrl=https://cdn.rawgit.com/KhronosGroup/glTF-Sample-Models/master/2.0/Duck/glTF/Duck.gltf

However, Binary glTF file is an error and will not be displayed.
https://playcanvas.github.io/playcanvas-gltf/viewer/index.html?assetUrl=https://cdn.rawgit.com/KhronosGroup/glTF-Sample-Models/master/2.0/Duck/glTF-Binary/Duck.glb

Uncaught (in promise) SyntaxError: Unexpected token g in JSON at position 0
Promise.then (async)
(anonymous) @ viewer.js:218
Promise.then (async)
main @ viewer.js:217
onload @ index.html?assetUrl=https://cdn.rawgit.com/KhronosGroup/glTF-Sample-Models/master/2.0/Duck/glTF-Binary/Duck.glb:15

I confirmed that I can display correctly when I drag & drop the glb file into Viewer. Therefore, I think that it should be displayed also when using assetUrl.

Collision Mesh

Hi!
I am currently testing gltf loader in playcanvas.
I was wondering what would be the best way to attach a Mesh collision to the loaded Model, as GLTF models loaded in Runtime don't have pc.Assets.

I am not sure if this is feature that this loader should provide or developers have to calculate this mesh collider by themselves...
Anyway, I hope you can help me.
Thanks!

[u, v] = [u, 1-v]

Hi, first of all, thanks so much for this library, it is super useful.

I noticed you are using <img> elements to flip images to fix them for the uvs, since e.g. glTF-Blender-Exporter is rewriting them:

https://github.com/KhronosGroup/glTF-Blender-Exporter/blob/master/scripts/addons/io_scene_gltf2/gltf2_extract.py#L634

            if blender_mesh.uv_layers.active:
                for texcoord_index in range(0, texcoord_max):
                    uv = blender_mesh.uv_layers[texcoord_index].data[loop_index].uv
                    uvs.append([uv.x, 1.0 - uv.y])

The image resampling is causing quite some lag for 1-2 seconds per model because in that time the browser is stuck in the event loop:

specgloss

The only reason I investigated this is because my spec-gloss blender .gltf model was so messed up (the one in image). I don't know what setting messes it so up, but I just ended up loading the material myself and setting it to the meshInstance, which made me realize the uv issue.

Long story short, my current "solution" is to rewrite the glTF-Blender-Exporter with unchanged uvs, but I would like to keep it "gltf standard" AND fast. So my idea is: why resample the textures with lots of lag when playcanvas-gltf could simply flip v = 1 - v;?

Does playcanvas-gltf need to lag because the specification says so?

    // Specification:
    //   https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#image
    function resampleImage(image) {

Edit: oops, I just realized the resampleImage function isn't flipping the image, just ensuring that its a power-of-two. I would like to rewrite the issue headline, but I can't. And it's only called because I had non-power-of-two images (I wasn't aware of). Im going to resize my textures and hope the lag is gone. I also realized I can simply remove the flipY option in new pc.Texture, so there is no need to rewrite the uv data.

Replace curve names (strings) with IDs (numbers)

I figured that curves names are mostly pretty pointless (e.g. "curve" + id), so I wondered if a can just completely replace them with IDs to reduce memory and optimize performance. And it works surprisingly well: https://github.com/KILLTUBE/playcanvas-gltf/commit/99f1c484e04ef134033f5ec38e7ed04eb088d05d

Instead of the AnimationCurve.count++ I simply do curve.name = this.animCurves.length; inside AnimationClip#addCurve.

I tested some models and some clip blending and it all still works.

I don't know yet if I will run into problems later on, so I will just leave this issue here so everybody could sleep over it.

KHR_materials_unlit not correctly handled

Description

The unlit material extension KHR_materials_unlit is not correctly handled in PlayCanvas glTF loader. Lighting calculations are still somewhat being applied to an un-lit material. For more information on how it should look, please see: KhronosGroup/glTF-Sample-Models/2.0/UnlitTest

Steps to Reproduce

  1. Download model: KhronosGroup/glTF-Sample-Models/2.0/UnlitTest/glTF-Binary/UnlitTest.glb
  2. Go to PlayCanvas viewer: https://playcanvas.github.io/playcanvas-gltf/viewer/
  3. Drag and drop model UnlitTest.glb into PlayCanvas viewer.

What I expected to see

khronosgroup-gltf-sample-unlittest

What I see instead

playcanvas-gltf-viewer-unlittest

[Feature Request] Support error handler

Description

Now the loader API:

loadGltf(gltf, device, success, options);
loadGlb(glb, device, success, options);

It only contains success handler, no error handler. But the loader may be failed.

For example:

 function loadGlb(glb, device, success, options) {
        var dataView = new DataView(glb);

        // Read header
        var magic = dataView.getUint32(0, true);
        if (magic !== 0x46546C67) {
            console.error("Invalid magic number found in glb header. Expected 0x46546C67, found 0x" + magic.toString(16));
            return null;
        }
}

If the magic does not match, it will be failed. It will output error in console only in development environment. When the app deployed to production environment, the console API often removed by uglifier like uglifyjs or terser.
If we want to add error handler now, we need to do something like:

var result = loadGlb(glb, device, success);
if (result === null) {
    // handle error
}

This is not intuitive. The more common error handler should be

// as callback
loadGlb(glb, device, function (err, res) {

});
// as promise
loadGlb(glb, device, options)
  .then(fn)
  .catch(fn);

Assigned texture not mapped correctly

I am importing a .glb model without embedded textures. When I do import a texture and assign it, it is incorrectly mapped.

I have tried loading a model with the texture embedded, which works, but when I then assign a texture it is also wrong.
first model is .glb without embedded textures + assigned texture
second model is .glb with embedded textures
third is .glb with embedded textures + assigned texture

//add glb and set texture
app.assets.loadFromUrl("model.glb", 'binary', function (err, asset) {
    var glb = asset.resource;
    loadGlb(glb, app.graphicsDevice, function (err, res) {
        // Wrap the model as an asset and add to the asset registry
        var asset = new pc.Asset('gltf', 'model', { url: '' });
        asset.resource = res.model;
        asset.loaded = true;
        app.assets.add(asset);

        // Add the loaded scene to the hierarchy
        var glb = new pc.Entity('gltf');
        glb.addComponent('model');
        glb.model.model = res.model;
        glb.setLocalScale(.1, .1, .1);
        glb.setPosition(0, 0.8, 0);

        var asset = new pc.Asset("diffuse", "texture", {
            url: "diffuse.png"
        });
        
        app.assets.add(asset);
        
        asset.on("error", function (message) {
            console.log(message);
        });
        
        asset.on("load", function (asset) {        
            glb.model.meshInstances.forEach( function(meshInstance){
                meshInstance.material.diffuseMap = asset.resource;
                meshInstance.material.update();
            });

            app.root.addChild(glb);
        });
        app.assets.load(asset);
    });
});

//add embedded glb
app.assets.loadFromUrl("embedded.glb", 'binary', function (err, asset) {
    var glb = asset.resource;
    loadGlb(glb, app.graphicsDevice, function (err, res) {
        // Wrap the model as an asset and add to the asset registry
        var asset = new pc.Asset('gltf', 'model', { url: '' });
        asset.resource = res.model;
        asset.loaded = true;
        app.assets.add(asset);

        // Add the loaded scene to the hierarchy
        var glb = new pc.Entity('gltf');
        glb.addComponent('model');
        glb.model.model = res.model;
        glb.setLocalScale(.1, .1, .1);
        glb.setPosition(0, -.4, 0);

        app.root.addChild(glb);
    });
});

//add embedded glb and override texture
app.assets.loadFromUrl("embedded.glb", 'binary', function (err, asset) {
    var glb = asset.resource;
    loadGlb(glb, app.graphicsDevice, function (err, res) {
        // Wrap the model as an asset and add to the asset registry
        var asset = new pc.Asset('gltf', 'model', { url: '' });
        asset.resource = res.model;
        asset.loaded = true;
        app.assets.add(asset);

        // Add the loaded scene to the hierarchy
        var glb = new pc.Entity('gltf');
        glb.addComponent('model');
        glb.model.model = res.model;
        glb.setLocalScale(.1, .1, .1);
        glb.setPosition(0, -1.6, 0);

        var asset = new pc.Asset("diffuse", "texture", {
            url: "diffuse.png"
        });
        
        app.assets.add(asset);
        
        asset.on("error", function (message) {
            console.log(message);
        });
        
        asset.on("load", function (asset) {        
            glb.model.meshInstances.forEach( function(meshInstance){
                meshInstance.material.diffuseMap = asset.resource;
                meshInstance.material.update();
            });

            app.root.addChild(glb);
        });
        app.assets.load(asset);
    });
});

Whole project download: swapTexture.zip
PlayCanvas forum post: https://forum.playcanvas.com/t/cant-change-texture-of-a-glb-model/11284/10?u=ivodidutch

Error in loadGlb function on Android 6.0 or older systems

I've tried to load a GLB file and it only works on Android 7.0 or newer.
When the function "loadGlb" is called with a GLB array buffer, the Android OS fires an error:

Uncaught TypeError: First argument to DataView constructor must be an ArrayBuffer
at new DataView()
at loadGlb(line1280:24)

It simply don't recognize the array buffer passed and consider it as null

By testing with other devices, i found out that is related with WebGL2 support. All Androids 6.0 or older don't present this support

OBS: On Android 7.0, newer versions and computer works normally

This project shows the problem

https://playcanvas.com/project/545400/overview/test-gltf

Lightmap Texture Support

Hey @willeastcott I am trying to add lightmap support to gltf.

Is there some SPECIAL format lightmap png needs to be in ... I am trying to use a lightmap generated from Unity.

Here is Lightmap Texture:

testscene_lightmap-0_comp_light

I am trying to load in gltf like so:

        if (data.hasOwnProperty('commonConstant') && data.commonConstant.hasOwnProperty('lightmapTexture')) {
            var lightmapTexture = data.commonConstant.lightmapTexture;
            material.lightMap = resources.textures[lightmapTexture.index];
            //material.lightMapChannel = 'rgb';
            if (lightmapTexture.hasOwnProperty('texCoord')) {
                material.lightMapUv = lightmapTexture.texCoord;
            } else {
                material.lightMapUv = 1;
            }
            if (lightmapTexture.hasOwnProperty('extensions') && lightmapTexture.extensions.hasOwnProperty('KHR_texture_transform')) {
                var lightmapTransformData = lightmapTexture.extensions.KHR_texture_transform;
                if (lightmapTransformData.hasOwnProperty('scale')) {
                    material.lightMapTiling = new pc.Vec2(lightmapTransformData.scale[0], lightmapTransformData.scale[1]);
                }
                if (lightmapTransformData.hasOwnProperty('offset')) {
                    material.lightMapOffset = new pc.Vec2(lightmapTransformData.offset[0], lightmapTransformData.offset[1]);
                }
            }
        }

And the GLTF Material Snippet:

    {
      "pbrMetallicRoughness": {
        "metallicFactor": 0.0,
        "roughnessFactor": 0.5
      },
      "commonConstant": {
        "lightmapTexture": {
          "index": 1,
          "texCoord": 1,
          "extensions": {
            "KHR_texture_transform": {
              "offset": [
                0.239268735,
                0.7841906
              ],
              "scale": [
                0.238089427,
                0.238089427
              ],
              "texCoord": 1
            }
          }
        },
        "extras": {
          "metadata": {
            "hdrpacking": "none"
          }
        }
      },
      "name": "Default-Material.Instance.00d31c29005a4668a044cb688552a494",
      "extras": {
        "metadata": {
          "glossyreflections": true,
          "specularhighlights": true
        }
      }
    }

But it does not look right... It should look like this:

screen shot 2018-10-08 at 10 37 36 pm

But instead it looks like this:

screen shot 2018-10-08 at 10 37 19 pm

I dont see any of the shadows baked into the lightmap texture... What am i doing wrong with lightmaps in PlayCanvas ???

Failure to load the texture of the cross origin

I tried to access github's resource from RawGit service from jsdo.it.

http://jsdo.it/cx20/4b3J

However, because of the cross origin restriction, it could not be displayed.

playcanvas-latest.js:5280 
Uncaught DOMException: Failed to execute 'texImage2D' on 'WebGL2RenderingContext': The image element contains cross-origin data, and may not be loaded.
    at GraphicsDevice.uploadTexture (https://cdn.rawgit.com/cx20/gltf-test/e63efa65/libs/playcanvas/v0.223.0-dev/playcanvas-latest.js:5280:16)
    at GraphicsDevice.setTexture (https://cdn.rawgit.com/cx20/gltf-test/e63efa65/libs/playcanvas/v0.223.0-dev/playcanvas-latest.js:5386:12)
    at GraphicsDevice.draw (https://cdn.rawgit.com/cx20/gltf-test/e63efa65/libs/playcanvas/v0.223.0-dev/playcanvas-latest.js:5446:14)
    at ForwardRenderer.drawInstance (https://cdn.rawgit.com/cx20/gltf-test/e63efa65/libs/playcanvas/v0.223.0-dev/playcanvas-latest.js:9957:14)
    at ForwardRenderer.renderForward (https://cdn.rawgit.com/cx20/gltf-test/e63efa65/libs/playcanvas/v0.223.0-dev/playcanvas-latest.js:10549:21)
    at ForwardRenderer.render (https://cdn.rawgit.com/cx20/gltf-test/e63efa65/libs/playcanvas/v0.223.0-dev/playcanvas-latest.js:10895:10)
    at Application.render (https://cdn.rawgit.com/cx20/gltf-test/e63efa65/libs/playcanvas/v0.223.0-dev/playcanvas-latest.js:20677:16)
    at https://cdn.rawgit.com/cx20/gltf-test/e63efa65/libs/playcanvas/v0.223.0-dev/playcanvas-latest.js:20995:13

The resources you attempted to access are as follows.
https://cdn.rawgit.com/cx20/gltf-test/e63efa65/tutorialModels/FlightHelmet/glTF/FlightHelmet.gltf

Perhaps when accessing via RawGit, you can access it by specifying anonymous as the crossOrigin attribute.

img.crossOrigin = "anonymous";

Please provide a way to access the resources of the cross origin constraint with playcanvas-gltf.

new `pc.Vec3`/`pc.Quat` in `AnimationCurve.prototype.evalCUBICSPLINE_GLTF_cache`

Since every AnimationKeyable allocates its own value via constructor (in init method), it should always be initialized and new pc.Vec3()/new pc.Quat() can be removed. I don't see any special cases where an AnimationKeyable could be uninitialized.

} else if (this.keyableType === AnimationKeyableType.VEC) {
resKey.value = new pc.Vec3();
resKey.value.x = AnimationCurve.cubicHermite(g * key1.outTangent.x, key1.value.x, g * key2.inTangent.x, key2.value.x, p);
resKey.value.y = AnimationCurve.cubicHermite(g * key1.outTangent.y, key1.value.y, g * key2.inTangent.y, key2.value.y, p);
resKey.value.z = AnimationCurve.cubicHermite(g * key1.outTangent.z, key1.value.z, g * key2.inTangent.z, key2.value.z, p);
} else if (this.keyableType === AnimationKeyableType.QUAT) {
resKey.value = new pc.Quat();
resKey.value.w = AnimationCurve.cubicHermite(g * key1.outTangent.w, key1.value.w, g * key2.inTangent.w, key2.value.w, p);
resKey.value.x = AnimationCurve.cubicHermite(g * key1.outTangent.x, key1.value.x, g * key2.inTangent.x, key2.value.x, p);
resKey.value.y = AnimationCurve.cubicHermite(g * key1.outTangent.y, key1.value.y, g * key2.inTangent.y, key2.value.y, p);
resKey.value.z = AnimationCurve.cubicHermite(g * key1.outTangent.z, key1.value.z, g * key2.inTangent.z, key2.value.z, p);

/**
* @param {AnimationKeyableType} [type]
* @param {number} [time]
* @param {BlendValue} [value]
* @returns {AnimationKeyable}
*/
AnimationKeyable.prototype.init = function (type, time, value) {
this.type = type || AnimationKeyableType.NUM;
this.time = time || 0;
if (value)
this.value = value;
else {
switch (type) {
case AnimationKeyableType.NUM: this.value = 0; break;
case AnimationKeyableType.VEC: this.value = new pc.Vec3(); break;
case AnimationKeyableType.QUAT: this.value = new pc.Quat(); break;
}
}
return this;
};

Some models render incorrectly with playcanvas-gltf

Some models render incorrectly with playcanvas-gltf and in game
but in other programs they render correctly
Here is the screenshot of this behaviour
model
and this issue comes not only with this model on image
some models render correctly some not with playcanvas-gltf and in game

Error loading Draco model with latest version of glTF Loader

I tried to read Draco format files with the latest version of glTF Loader.
However, an error is output on the console and the model is not displayed.

PlayCanvas v1.22.0dev + glTF Loader + Draco format model result:

playcanvas-gltf.js:671 Uncaught TypeError: Cannot read property 'DecoderBuffer' of null
    at playcanvas-gltf.js:671
    at Array.forEach (<anonymous>)
    at translateMesh (playcanvas-gltf.js:644)
    at playcanvas-gltf.js:1274
    at Array.map (<anonymous>)
    at parse (playcanvas-gltf.js:1273)
    at playcanvas-gltf.js:1412
    at Image.onLoad (playcanvas-gltf.js:276)

I think the following fixes are affecting. The reason is that old code can load Draco format models correctly.
e945af5#diff-d6638a7dc049e197f0b086bececddae0

AnimationSession for curves

Hi, can anybody explain what the point is for an AnimationSession on every single AnimationCurve?

The code is already resource intensive (spawning 64 models uses like > 900 MB):

image

But if I comment out the AnimationSession in the curve constructor:

var AnimationCurve = function () {
    AnimationCurve.count ++;
    this.name = "curve" + AnimationCurve.count.toString();
    this.type = AnimationCurveType.LINEAR;
    this.keyableType = AnimationKeyableType.NUM;
    this.tension = 0.5;// 0.5 catmul-rom
    this.animTargets = [];// allow multiple targets
    this.duration = 0;
    this.animKeys = [];
    //this.session = new AnimationSession(this);
};

Then the code is using consistently like 100 MB less for the same scene:

image

I tested multiple models and blending between clips and everything still works. Why does each curve need a session?

Missing CUBICSPLINE interpolation mode

https://youtu.be/H1nRE2ipqQU

animation bug

I tested threejs, babylon and win10 "3d builder", they all show the same animation (still buggy animation, but I guess thats the fault of the khronos blender exporter), but something in playcanvas-gltf animation is definitive more wrong

I uploaded door.gltf, door.bin and door.blend here when anybody wants to use this as simple/small reference bug: http://killtube.org/downloads/gltf/door/ (asset is public domain, i just made it for debugging all the gltf animation problems)

Exported model from Blender 2.83 LTS breaks ForwardRenderer

Hi, I used this model all the time for testing (mostly exported by using Blender 2.79), but for some reason it breaks multiple things when exported from Blender 2.83 LTS:

image

Supposed to look like:

image

playcanvas-stable.js:8109 Uncaught TypeError: Cannot read property '_aabbVer' of undefined
    at MeshInstance.get (playcanvas-stable.js:8109)
    at scriptType.OrbitCamera._buildAabb (orbit-camera.js:600)
    at scriptType.OrbitCamera._buildAabb (orbit-camera.js:608)
    at scriptType.OrbitCamera.focus (orbit-camera.js:400)
    at scriptType.<anonymous> (orbit-camera.js:540)
    at scriptType.fire (playcanvas-stable.js:326)
    at scriptType.set (playcanvas-stable.js:41555)
    at Viewer.initializeScene (viewer.js:209)
    at playcanvas-gltf.js:1457
    at parse (playcanvas-gltf.js:1300)
get @ playcanvas-stable.js:8109
OrbitCamera._buildAabb @ orbit-camera.js:600
OrbitCamera._buildAabb @ orbit-camera.js:608
OrbitCamera.focus @ orbit-camera.js:400
(anonymous) @ orbit-camera.js:540
fire @ playcanvas-stable.js:326
set @ playcanvas-stable.js:41555
initializeScene @ viewer.js:209
(anonymous) @ playcanvas-gltf.js:1457
parse @ playcanvas-gltf.js:1300
(anonymous) @ playcanvas-gltf.js:1445
(anonymous) @ playcanvas-gltf.js:1267
loadBuffers @ playcanvas-gltf.js:1248
loadGltf @ playcanvas-gltf.js:1443
loadGltf @ viewer.js:220
fr.onload @ viewer.js:434
load (async)
loadFile @ viewer.js:424
(anonymous) @ viewer.js:489
(anonymous) @ viewer.js:456
6804playcanvas-stable.js:12010 Uncaught TypeError: Cannot read property 'worldTransform' of undefined
    at ForwardRenderer.drawInstance (playcanvas-stable.js:12010)
    at ForwardRenderer.renderForward (playcanvas-stable.js:12477)
    at ForwardRenderer.renderComposition (playcanvas-stable.js:13189)
    at Application.render (playcanvas-stable.js:47367)
    at playcanvas-stable.js:47948

The glTF model for testing:

maila.zip

Vec2/Vec3/Vec4 data property

I finished the "typification" and stumbled over these errors:

// each element in vector
if (key1.type === AnimationKeyableType.VEC) {
resKey.value = key1.value.clone();
for (var i = 0; i < resKey.value.data.length; i ++) {
m1 = factor * (key2.value.data[i] - key1.value.data[i]) / (key2.time - key1.time);
if (key0)
m1 = 2 * factor * (key2.value.data[i] - key0.value.data[i]) / (key2.time - key0.time);
m2 = factor * (key2.value.data[i] - key1.value.data[i]) / (key2.time - key1.time);
if (key3)
m2 = 2 * factor * (key3.value.data[i] - key1.value.data[i]) / (key3.time - key1.time);
resKey.value.data[i] = AnimationCurve.cubicHermite(m1, key1.value.data[i], m2, key2.value.data[i], p);
}
}

vec_data

I tried a bunch of animated GLTF models, but I couldn't trigger to run this code for testing actual values. But it looks like old code which depends on Vec2/Vec3/Vec4 data property, which was removed some time ago.

FlightHelmet.gltf goggles are opacity

I tried to display a new sample model FlightHelmet.gltf with the library of the latest version of playcanvas-gltf.
However, in PlayCanvas, the appearance of goggles seems to be different from other libraries.

Three.js Babylon.js PlayCanvas
image image image

Sparse Accessors

The glTF loader currently has no support for sparse accessors.

MorphInstances Missing Initial Weights

The current implementation DOES NOT set the initial MorphInstance weights.

As A workaround I am currently HACKING the translateMesh function:

var morphWeights = null;
if (data.hasOwnProperty('weights')) {
     morphWeights = data.weights;
}

Then later in that function right before parsing primitive.targets

mesh._morphWeights = morphWeights;

if (primitive.hasOwnProperty('targets')) {
      var targets = [];
      etc.....
}

As you can see I am hacking the initial morph weights onto the MESH. So it can be carried over to where we actually create the MorphInstance:

Then finally in the createModel function:

// HACK: need to force calculation of the morph's AABB due to a bug
// in the engine. This is a private function and will be removed!
morphInstance.updateBounds(meshInstance.mesh);

// Mackey Kinard - HACK: Set Initial Morph Instance Weights
 if (meshGroup[i].hasOwnProperty('_morphWeights')) {
   const morphWeights = meshGroup[i]._morphWeights;
   if (morphWeights != null && morphWeights.length > 0) {
         for (let mi = 0; mi < morphWeights.length; mi++) {
              try { morphInstance.setWeight(mi, morphWeights[mi]); } catch {}
         }
   }
}
morphInstances.push(morphInstance);

And the cleanup:

if (meshGroup[i].hasOwnProperty('_morphWeights')) {
  delete(meshGroup[i]._morphWeights);
}

So as you can see... I am HACKING a solution by temporarily storing the initial morph weights onto the MESH object. Then in the CreateModel I am looping thru the initial weights and calling morphInstance.setWeight()

We need to come up with a way to CARRY OVER or something to support initial morph target weights.

Yo @willeastcott and @emackey ... What do think we should do here ???

AnimationKeyable#clone ignores inTangent/outTangent

They are assigned here, for cubicspline curves only:

if ((path === 'translation') || (path === 'scale')) {
keyable.inTangent = new pc.Vec3(values[9 * i + 0], values[9 * i + 1], values[9 * i + 2]);
keyable.value = new pc.Vec3(values[9 * i + 3], values[9 * i + 4], values[9 * i + 5]);
keyable.outTangent = new pc.Vec3(values[9 * i + 6], values[9 * i + 7], values[9 * i + 8]);
} else if (path === 'rotation') {
keyable.inTangent = new pc.Quat(values[12 * i + 0], values[12 * i + 1], values[12 * i + 2], values[12 * i + 3]);
keyable.value = new pc.Quat(values[12 * i + 4], values[12 * i + 5], values[12 * i + 6], values[12 * i + 7]);
keyable.outTangent = new pc.Quat(values[12 * i + 8], values[12 * i + 9], values[12 * i + 10], values[12 * i + 11]);
} else {
keyable.inTangent = values[3 * i];
keyable.value = values[3 * i + 1];
keyable.outTangent = values[3 * i + 2];
}

But never cloned:

AnimationKeyable.prototype.clone = function () {
var value = this.value;
if (this.value instanceof pc.Vec3 || this.value instanceof pc.Quat)
value = this.value.clone();
var cloned = new AnimationKeyable(this.type, this.time, value);
return cloned;
};

So cloned models with cubicspline curves currently don't work.

If I see it right, there are currently six kinds of AnimationKeyable's:

  • AnimationKeyableNum
  • AnimationKeyableVec
  • AnimationKeyableQuat
  • AnimationKeyableNumCubicspline
  • AnimationKeyableVecCubicspline
  • AnimationKeyableQuatCubicspline

Would it be a good design choice to make six explicit standalone classes?

I currently have three arguments for it:

Memory consumption

Every model (has) clips (has) curves (has) keyables... so keyables are like one of the most allocated objects. Hence the memory consumption of each one should be as low as possible. E.g. there is no need to save this.type when the class/object itself represents it.

Performance

It will remove like a dozen of runtime checks like:

  • keyable.type === AnimationKeyableType.VEC
  • if (this.value instanceof pc.Vec3 || this.value instanceof pc.Quat)
  • switch (keyable.type) { ... }

Ability to just call the methods and they know what to do.

Type safety

Dedicated classes make it harder to introduce bugs, because they don't need to work for five other use cases.

(TLDR: small, fast and type safe)

Probably @thomasbiang knows most about it.

Huge Garbage Collector times

I don't know yet what is causing these GC times, but I guess it's worth an investigation:

gc

The smaller GC is already 17ms and the average complete frame time is ~110ms.

TypeScript any1?

Currently I am adding jsdoc to playcanvas-anim.js and using proper types always leads to finding some bugs like these:

cloned.fadtTime = this.fadeTime;

image

But I seriously wonder: why not switch completely to TypeScript (for PlayCanvas engine too)? jsdoc is helpful, as this image shows, but using full TypeScript is still 10x better. The refactoring in vscode is epic.

Whoever knows JavaScript well enough will learn TypeScript in two days.

Only first Blender NLA animclip works

Hi, I tried to make some proper NLA animations splitted into "clips" in Blender, but for some reason it is only ever playing the first clip:

https://www.youtube.com/watch?v=sw2e-tGwzh0

If somebody wants to debug this, I uploaded the minimal example here (door.gltf, door.bin, door.blend):

https://killtube.org/downloads/playcanvas/door/

I am using this exporter: https://github.com/KhronosGroup/glTF-Blender-Exporter

I didn't change any export settings, simply File -> Export -> .gltf

Tested with the latest playcanvas-anim.js which is currently in this repo.

The gltf loading code for the gltf file is:

GLTF = function() {}

// door = await GLTF.fetch("http://127.0.0.1/assets/door/door.gltf");
GLTF.fetch = async function(url, options) {
	var basePath = url.substring(0, url.lastIndexOf("/")) + "/";
	if (typeof options === "undefined")
		options = {};
	if ("layers" in options === false)
		options.layers = [0]; // should be worldLayer.id
	console.log("GLTF.fetch", url, options);
	return new Promise(function(resolve, reject) {
		app.assets.loadFromUrl(url, 'json', function (err, asset) {
			var json = asset.resource;
			var gltf = JSON.parse(json);
			loadGltf(gltf, app.graphicsDevice, function (model, textures, animationClips) {
				var asset = new pc.Asset('gltf', 'model', {
					url: ''
				});
				asset.resource = model;
				asset.loaded = true;
				app.assets.add(asset);
				var gltf = new pc.Entity('gltf');
				gltf.addComponent('model', {
					asset: asset,
					layers: options.layers
				});
				if ( animationClips && animationClips.length > 0 ) {
					gltf.animComponent = new AnimationComponent();
					for (var i = 0; i < animationClips.length; i++) {
						animationClips[i].transferToRoot(gltf);
						gltf.animComponent.addClip(animationClips[i]);
					}
					gltf.animComponent.playClip(animationClips[0].name);
				}
				resolve(gltf)
			}, {
				basePath: basePath
			});
		});
	})
}

AnimationKeyable - normalize() is not a function

So I wanted to test the glTF viewer with a simple mesh with simple animation and it just doesn't work.

image

The issue is here:

image

It calls "normalize" when the resKey (which is an AnimationKeyable), doesn't contain a normalize function.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.