Beg-in / vue-babylonjs

A ready-to-go 3d environment for Vue.js using Babylon.js
https://vuebabylonjs.com
MIT License
463 stars 73 forks source link

Loader component #2

Closed stephanedemotte closed 5 years ago

stephanedemotte commented 6 years ago

Hello, this project look amazing :)

Loader component exist ?

Thanks

BrainBacon commented 6 years ago

Do you mean something like Vue-Loader? Could you elaborate on your request? I'm currently working on a rollup implementation for the library that should support UMD, CommonJS, and ESM imports, would those satisfy your use-case?

stephanedemotte commented 6 years ago

For load an assets :)

https://doc.babylonjs.com/how_to/load_from_any_file_type

Aframe have something like this https://aframe.io/docs/0.8.0/primitives/a-gltf-model.html

:)

BrainBacon commented 6 years ago

Asset management was something I was interested in building at some point, but I didn't know how best to approach it.

Right now it seems like the ideal environment for this library is one where you leverage Webpack. It seems almost better to let Webpack handle asset management since it would result in optimized bundles, one could choose to load assets over a URL via url-loader, or load the asset into the bundle using raw-loader.

However, with that being said, there's definitely an opportunity to optimize the organization of assets. I would like to do that while getting the best of both worlds though, leverage the power of bundlers like Webpack and allow for in-app caching and sharing of assets. My thoughts were to create some sort of VueX store that shares these assets to components that are capable of loading assets. I'd love to hear your thoughts though!

natewallis commented 6 years ago

Great library - thanks for your efforts. Works well out of the box and since my project is using vue.js, it just dropped in nicely. Praise the lord for modern web frameworks :)

I was interested in SceneLoader too.. maybe I could help out...

BrainBacon commented 6 years ago

Absolutely. I am currently in the process of switching the library over to an ES Module style so we can do code-splitting and tree-shaking. This includes a few built versions of the library for various environments, but is not completely tested yet. You can check out the esm branch if you want to have a look at the current structure. I haven't entirely thought through a loader component yet, so just give me a heads up of what you think the implementation would look like.

natewallis commented 6 years ago

Looking good - just installed and built it locally.

I think you have done a pretty good job of translating concepts between vue.js and babylon.js. It feels nice to be able to construct a scene with HTML.

I have really enjoyed writing my component definitions in HTML first and then moving across to Javascript to flesh it out.

For want of a better name - I'll call it ModelLoader

<model-loader source="http://example.org/model.babylon">
<model-animation start="0" end="400" loop="true" name="walk"></model-animation>
<model-animation start="401" end="600" loop="true" name="run" @complete="run_complete"></model-animation>
</model-loader>

model-animation

model-loader

I am totally spitballing here - but might get the ball rolling..

BrainBacon commented 6 years ago

@nwallis thanks for the feedback! I'm glad to hear that the library is working well. Make sure you upgrade to beta.5 since it has some fixes and optimizations. As for your suggestions, I have some thoughts:

I think the Animation component will work for you instead of needing a new model-animation component. There is a prop called animatable that you can pass to Animation that accepts any Babylon.js animatable object. If we need to, I think expanding the functionality of the Animation component is fine if it means keeping a simpler API.

As for the "complete" function, we are talking about events and observables in #5 and I'm specifically thinking about some sort of more scalable solution than just doing specific props on components as-needed. Right now this should be possible by using the Property component with the any type prop. I'd love some feedback on using the Property system for this use-case. Does it satisfy your use-case, is it ergonomic, and does it stay organized easily enough? The property system is kinda experimental, but allows this library to not worry about Babylon.js API changes over time. I am, however, worried it could become cumbersome and difficult to decide which API features should become component props and which should stay out of this library and worried that it may lead to unexpected errors since it places the burden of knowledge of the Babylon.js API on the developer.

I do think we need some sort of "model-loader" type component, and maybe a scene-loader component as well. Maybe an"Asset" component, or we could go the way of A-Frame and have an Assets parent element that has Asset children? My concerns about implementing the system in that way is that it doesn't compartmentalize very well. Do you have a global Assets and define all of them in one place, or do you allow Asset to be placed anywhere so they can be loaded per vue? Seems like you would arbitrarily decide where to place the component in your markup in that scenario, so I'm conflicted. Another problem I foresee is how to reference these assets throughout your scene. Does the Asset component bind a name property that you can retrieve the id and then share throughout that vue or to a Vuex store? I am trying to think if there's a nice way to communicate how to keep that organized. On the technical side there is also the problem of optimizing asset loading and making sure we're not loading the asset more than once if it's referenced in multiple places or loading a vue multiple times. I'd like to make it possible to use more than just URL loading, but that would make the optimization implementation challenging. Is there a legitimate reason you wouldn't just load over a URL?

I apologize for the mind dump, but I'd like to get an outside perspective.

natewallis commented 6 years ago

Ok - beta 5 is in master branch? I was trying the esm branch yesterday and had issues trying to get that running with my phoenix application (which uses brunch by default)... acorn was cracking it when trying to bring in umd.js... anyway - I'll figure that out some other time and just head back to master branch for now.

I think the Animation component will work for you instead of needing a new model-animation component. There is a prop called animatable that you can pass to Animation that accepts any Babylon.js animatable object. If we need to, I think expanding the functionality of the Animation component is fine if it means keeping a simpler API.

Ok, I didn't know about that component, I will check it out. I have only done a basic install and run demo code so far.. I'll have to check it out.

As for the "complete" function, we are talking about events and observables in #5 and I'm specifically thinking about some sort of more scalable solution than just doing specific props on components as-needed.

Yes, agreed... I was just thinking about myself here. Being new to babylon, I don't actually know what events exist for animations... would be nice to have a more abstract solution like you are describing

Does the Asset component bind a name property that you can retrieve the id and then share throughout that vue or to a Vuex store? I am trying to think if there's a nice way to communicate how to keep that organized

I think Vuex solves this problem... Are there any issues with using Vuex for this that you could see? Not sure the wheel needs to be reinvented here..

On the technical side there is also the problem of optimizing asset loading and making sure we're not loading the asset more than once if it's referenced in multiple places or loading a vue multiple times. I'd like to make it possible to use more than just URL loading, but that would make the optimization implementation challenging. Is there a legitimate reason you wouldn't just load over a URL?

Maybe using Vuex here would be a way of tracking a global store of what models are loaded and using a cached (in application memory) version if already loaded..

I only imagined a URL situation... I can't imagine what the use case would be where you want to load from elsewhere (in a web situation).. Unless some models can be convereted to Javascript classes or something like that? Not sure.

Do you have a global Assets and define all of them in one place, or do you allow Asset to be placed anywhere so they can be loaded per vue?

I like the idea of building my scene with HTML, its easy to visualise the initial state of your scene... even if the heirarchy were to change at runtime (maybe consider how this could happen?).. I guess (spitballing again) you could customise how the global store listens for load progress for different elements of your scene.. maybe you want to react accordingly when subsections of the scene are loaded...? <- my turn for a brain dump ;)

BrainBacon commented 6 years ago

Ok - beta 5 is in master branch?

Yeah there are only some minor differences between the latest on npm and the master branch. I may spin off a asset branch.

acorn was cracking it when trying to bring in umd.js

Hmm that's odd, there was a little bit of foolery going on with the dependencies before my push yesterday, maybe the current master will solve your issue since it's a fresh build with some clean-up.

I think Vuex solves this problem... Are there any issues with using Vuex for this that you could see?

You may be right here. The only problem I foresee is how to manage it if the developer is already using Vuex. Should we bind to their instance, or try to keep the library's state management separate? Also something to be mindful of is that the library sort of supports multiple scenes on a page. I haven't yet tested this use-case, but maybe a unified state for Assets wouldn't matter much in this scenario anyways.

Unless some models can be convereted to Javascript classes or something like that?

Possibly via data-uri, but perhaps the URL logic would handle that just fine? There have been some instances where I've used GLSL scripts, for example, via something like raw-loader to load the text directly into the bundle. I don't know if Babylon.js even supports a scenario like that for assets, but I can briefly look into it. It may not be even worth worrying about for an initial version.

I guess (spitballing again) you could customise how the global store listens for load progress for different elements of your scene.. maybe you want to react accordingly when subsections of the scene are loaded...?

I like this, I think you're right that Vuex is the way to go, because we can have integration with watchers and computeds. I have done some fancy stuff in the past with namespaced modules and createNamespacedHelpers. This could work out very cleanly.

natewallis commented 6 years ago

Hmm that's odd, there was a little bit of foolery going on with the dependencies before my push yesterday, maybe the current master will solve your issue since it's a fresh build with some clean-up.

Yep, I ended up reverting to master which is beta5... esm was beta4 yesterday. Working for me now.

I don't know if Babylon.js even supports a scenario like that for assets, but I can briefly look into it. It may not be even worth worrying about for an initial version.

Agree.. it will cover 95% of use cases

Let me know if there is anything I can do for this. I have a decent background knowledge of 3D, but I am relatively new to 3D in the browser and intermediate with vue.js... but happy to help out wherever I can.

benoitemile commented 6 years ago

Hello guys, awesome project there, been trying to getting that up and working with custom .obj file import but i'm struggling. Without waiting the loader component, is there a way to access the current scene or engine programatically to be able to use SceneLoader ? (just like if we could access this.engine / this.scene)

BrainBacon commented 6 years ago

Hey @benoitemile you can access the current scene by using the v-model prop on the Scene component. See the section titled "Retrieving Babylonjs Objects" on the homepage of the documentation website.

Depending on what you're doing with Scene, you may be able to just use the Property component as a direct child of Scene.

Additionally there are a couple advanced ways of accessing it; you can use the Entity component as a mixin which exposes this.$scene, and You can also use Vue's provide/inject functionality to access a promise called SceneReady as long as you are doing it within a child component of the Scene component.

Once you have access to the Scene, you can simply use the getEngine method on that object to retrieve the Engine.

Hope that helps!

benoitemile commented 6 years ago

Thank you very much @BrainBacon will try this 👍

I tried it and it worked like a charm, thank you very much (:

jmartinezuk commented 6 years ago

@benoitemile can you share how you did it? I'm having problems myself. Thanks!

BrainBacon commented 6 years ago

Just a quick update on this. I'm am running into dependency issues with babylonjs-loaders and the esm implementation of this library. Additionally, it's dependent on the 3.3.0 preview branch of Babylon.js to get the parenting system to work properly. When I get everything working if 3.3.0 hasn't hit stable yet I'll release this as a preview on npm, but I won't be pushing it to the latest tag until proper release by the Babylon.js team.

jmartinezuk commented 6 years ago

Thanks for the update. I was indeed trying to load it with babylonjs-loaders and I was getting errors. Is there an alternative way of loading an OBJ model onto the Scene? Can @benoitemile share how he got it to work?

BrainBacon commented 6 years ago

You can check out src/asset/index.js and examples/asset/template.pug in the asset branch for some ideas.

Right now I am able to load in all the file types when running the build via webpack on the unbuilt source files. However, since adding the babylonjs-loaders dependency it has broken my rollup build. That unfortunately stalls a preview release since I can't build the umd or es module.

My theory is that the current version of the loaders extension is not compatible with the es6 module I am using from babylon.js and webpack ends up figuring out the dependency, but in rollup it loads in an additional Babylon.js during compilation resulting in a massive build.

jmartinezuk commented 6 years ago

I kind of managed to get it to work by delaying the loading of the assets until the scene is fully ready. I'm using Nuxt by the way, and it's not ready when it mounts, so I can't ask it to load those assets yet.

Thanks for the tip on where to look, glad that the solution is almost there. Cheers.

BrainBacon commented 6 years ago

You could try using Entity as a mixin in your component as long as it's a child of Scene. That will give you the ability to use the onScene lifecycle hook.

If you're using es modules

import { Entity } from 'vue-babylonjs';

export default {
  mixins: [Entity],

  async onScene() {
    // do asset loading here
  },
};

See the entity documentation for more on the mixin functionality and lifecycle hooks. Unfortunately I haven't gotten to updating that part of the documentation since I created the UMD and ES Module builds, so the importing documentation is incorrect; I have not yet created an export for the AbstractEntity, and If you're using the UMD module (require) I do not have a method of obtaining a reference to these mixins. I will investigate a solution for that and update the documentation when I get done with the Asset component.

benoitemile commented 6 years ago

hi @jmartinezuk I'm also using it on nuxt.

I did success on that by having the following :

[preview is not that good so i'll make a gist] Here's the gist : https://gist.github.com/benoitemile/3bc75e37d16e55b9110f3933035420b2

I'm currently mounting this component asynchronously after a user has clicked a view 3D button and only at this moment I'm loading babylonjs and all it's stuff; to preserve good performance on page load and defer those bytes when required, async chunked via webpack.

BrainBacon commented 6 years ago

So, given the dependency issues I'm running into, I'm thinking it might be appropriate if I release the Asset component in preview that will require you guys to load the vue-loaders dependency yourselves. I can't recommend it for production use until these dependency issues are sorted out by the Babylon.js team since it could result in a massive bundle by loading multiple copies of Babylon.js.

jmartinezuk commented 6 years ago

@benoitemile thanks for that. I figured something similar out, but still, external to vue-babylonjs

@BrainBacon I think until 3.3.0 is released, is not a critical issue. We will update our code and method of loading 3d models so it works nicely with the rest of the code when it comes.

BrainBacon commented 6 years ago

Quick update on this; I've been keeping track of the esm implementation on the BabylonJS main repo. The latest update was that viewer, gui, and inspector were just updated to support esm which means that loaders should be coming up next! You can follow along on their issue

RaananW commented 6 years ago

Loaders, materials and serializers are next, and the core component right after. Will be awesome!

BrainBacon commented 6 years ago

Thanks @RaananW for following up with us and great work!

Also, I've updated the asset branch with the updates from 1.0.0-beta.6 for anyone that wants a preview with double the dependencies 😆

differentMonster commented 5 years ago

@BrainBacon i tried a few methods to load obj into the stage but still cant make it work , tried @benoitemile way too only the stage is loaded , i dont know what am i missing.

here is my test https://codesandbox.io/s/8p289q2lwl , can you guys pin point what wrong with it ?

BrainBacon commented 5 years ago

This has been shipped in the latest version 1.0.0-beta.7 you can see the documentation here: https://vuebabylonjs.com/#/asset

davidAlittle commented 5 years ago

Loading from a remote URL, as in the example, works fine, but I've not found any way to load local files. Can someone verify that they've been able to get a .gltf or .babylon file stored locally to load?

I was finally able to get things working by putting my assets in the public folder so they were completely ignored by webpack, and then

<Scene>
      <Asset :src="publicPath" :scaling="myAsset.scaling" :position="myAsset.position"></Asset>
    </Scene>

and

  data: function(){
    return {
      publicPath: `${process.env.BASE_URL}pallet.gltf`,
      myAsset: {
        scaling:[1, 1, 1],
        position:[0,0,0]
      }
    }
},

No combination of webpack rules or loaders seemed to work.

BrainBacon commented 5 years ago

@davidAlittle You can use file-loader. I've been using that successfully for some personal projects. I'll probably will put an example up on Glitch at some point since showing it in the docs won't give you a good idea of how the Webpack configs work because the docs site uses a pretty custom Webpack setup.

davidAlittle commented 5 years ago

@BrainBacon I tried that, but I could have done it wrong. I'm using Vue CLI so the webpack config is abstracted somewhat.

Also (and this is off in the weeds now), that solution won't work in production, it seems, for an Electron build. So if you could demonstrate how to get webpack to parse these correctly and have the paths work for local files, that would be awesome.

BrainBacon commented 5 years ago

@davidAlittle please forgive me, I have very little experience with Electron. Does it have a mechanism for loading files over a file:// protocol or something? That could be what you set your output.publicPath to? I guess a hacky solution might be to use a datauri loader of some kind?

Unfortunately anything beyond "loading from a public URL" is probably going to involve some sort of custom Webpack loader and some really intense engine-level loading mechanism that's definitely out-of-scope from the Asset compoent. My understanding of Babylon.js is that the loaders are expecting the model to come from a http request.

davidAlittle commented 5 years ago

@BrainBacon After some digging, I discovered there is some special care that is required with static assets in Electron. Changing publicPath in the above code to

publicPath: path.join(__static, 'pallet.gltf'),

Works in Electron in both dev and build. See https://github.com/nklayman/vue-cli-plugin-electron-builder/blob/master/docs/guide/guide.md for details.

Because this is in the Public folder, and ignored by Webpack, this just works out of the box without any Webpack mods. Now that I understand the caveats of static files in Electron, I'll take another stab at getting it to work with file-loader so it can be handled. gltf is just json, so something may be possible.

As mentioned, this is way off topic now, but it could be of use in the docs if anyone else comes looking.

(const path = require('path') is required for that to work, btw)