Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

.obj + .mtl #379

Open
alphaCastor-zz opened this issue Dec 24, 2018 · 9 comments
Open

.obj + .mtl #379

alphaCastor-zz opened this issue Dec 24, 2018 · 9 comments

Comments

@alphaCastor-zz
Copy link

Hi. Is there any way to implement .obj + .mtl?

@h-ikeda
Copy link
Member

h-ikeda commented Dec 30, 2018

Maybe OBJLoader can be used in computed property like VglTexture, since this loader returns an Object3D instance.

@travisstaloch
Copy link

First of all, thank you for sharing this great project. Very cool!.

I started looking into loading .obj and .mtl files with OBJLoader and MTLLoader. There is a three.js issue importing the loaders as modules as described here: mrdoob/three.js#9562

I used a work around described here: https://threejs.org/docs/index.html#manual/en/introduction/Loading-3D-models

I'm trying to test this out in a vue project and I've been able to load a .mtl and .obj file after placing them in public/obj/ from mounted() (code below). But I'm not sure where to go from there. OBJLoader.load() returns a Group object which I thought might work somehow with a <vgl-group> element. But I'm not sure how to put this into the scene.

Is there some way to get access to the vgl components underlying data such as with v-model?

        <vgl-group v-model="group"></vgl-group>

Here is where this is being done in vue-threejs: https://github.com/fritx/vue-threejs/blob/chore/vuecli3/src/mesh/MObjMtl.vue

Maybe its just not currently possible from client code. Any thoughts about how to do this?

var THREE = (window.THREE = require("three"));
require("three/examples/js/loaders/OBJLoader");
require("three/examples/js/loaders/MTLLoader");

    let baseUrl = "obj/";
    let objUrl = "table.obj";
    let mtlUrl = "table.mtl";
    let mtlLoader = new THREE.MTLLoader();
    if (baseUrl) {
      mtlLoader.setPath(baseUrl);
    }
    mtlLoader.load(
      mtlUrl,
      materials => {
        materials.preload();
        const objLoader = new THREE.OBJLoader();
        objLoader.setMaterials(materials);
        if (baseUrl) {
          objLoader.setPath(baseUrl);
        }
        objLoader.load(
          objUrl,
          group => {
            this.group = group;
          },
          xhr => {
            console.log("obj loading");
          },
          err => {
            console.error(err);
          }
        );
      },
      xhr => {
        console.log("mtl loading");
      },
      err => {
        console.error(err);
      }
    );

@travisstaloch
Copy link

Update: I managed to build and depend on this project locally now from windows after merging changes from https://github.com/Kjue/vue-gl

That project also uses

"peerDependencies": {
        "three-full": "^11.3.2"
}

instead of three which allows OBJLoader and MTLLoader to be imported instead of using require.

Following the idea above, I started modifying objects/vgl-group to see if I could load the files in inst() like vgl-texture does. However, the load() methods for these are async and return their data in callbacks. So I'm not sure how to save the returned Group object.

Do you have any ideas about how to do this? Perhaps use Promise.all()? Thanks for any input.

@h-ikeda
Copy link
Member

h-ikeda commented Jan 29, 2019

umm, I have currently no ideas to load objects asynchronously. Vue's computed property does not support promise.
If performance is a second matter, how about adding each children of loaded group to inst group object? (and call this.vglNamespace.update() to render them.)

@travisstaloch
Copy link

Thanks a lot for the feedback. I did as you said in the vgl-group created() method and it works fine. One minor difference, rather than adding the loaded children, I tried just assigning this.inst.children = group.children before calling this.vglNamespace.update() and it works.

I can submit a patch if you'd like. This relies on changes made to package.json (adding "three-full": "^11.3.2" as dependency) and three.js (adding exports for OBJLoader and MTLLoader). Let me know if you see anything that needs improvement in style or otherwise.

in html

    <vgl-renderer style="min-height: 800px;">
      <vgl-scene>
        <vgl-group base-url="obj/" obj-url="table.obj" mtl-url="table.mtl"></vgl-group>
        <vgl-ambient-light color="#ffeecc"></vgl-ambient-light>
        <vgl-directional-light position="0 10 1" cast-shadow></vgl-directional-light>
      </vgl-scene>
      <vgl-perspective-camera orbit-position="20 1 0.5"></vgl-perspective-camera>
    </vgl-renderer>

vgl-group.js

import VglObject3d from '../core/vgl-object3d.js';
import { Group, OBJLoader, MTLLoader, } from '../three.js';
import { string } from '../validators.js';

/**
 * A component for grouping objects,
 * corresponding [THREE.Group](https://threejs.org/docs/index.html#api/objects/Group).
 * Its purpose is to make working with groups of objects syntactically clearer.
 *
 * Properties of [VglObject3d](vgl-object3d) are also available as mixin.
 */

export default {
  inject: ['vglNamespace'],
  mixins: [VglObject3d],
  props: {
    baseUrl: string,
    objUrl: string,
    mtlUrl: string,
  },
  created() {
    if (this.objUrl == null && this.mtlUrl == null) {
      this.inst = new Group()
    } else {
      let mtlLoader = new MTLLoader();
      if (this.baseUrl) mtlLoader.setPath(this.baseUrl)

      mtlLoader.load(this.mtlUrl, (materials) => {
        materials.preload()
        let objLoader = new OBJLoader()
        objLoader.setMaterials(materials)
        if (this.baseUrl) objLoader.setPath(this.baseUrl)
        objLoader.load(this.objUrl, (group) => {
          this.inst.children = group.children
          this.vglNamespace.update()
        }, () => {
        },
          err => {
            console.error(err)
          }
        )
      },
        () => {
        },
        err => {
          console.error(err)
        }
      );
    }
  },
}

@h-ikeda
Copy link
Member

h-ikeda commented Feb 16, 2019

Thank you very much for your suggestion.
But I think it will be better to provide these functions as another module rather than merge them, because loaders are not core classes of three.js and three-full uses a little older version of three.js.
You can create a new package, otherwise I might do it in the future.
Anyway, your code must be a help for us!

@Sharlaan
Copy link

umm, I have currently no ideas to load objects asynchronously. Vue's computed property does not support promise.

How about using vue-async-computed ?

We are looking for a way to use your awesome library, importing .stl models from our backend service.
Is it possible ?

@h-ikeda
Copy link
Member

h-ikeda commented Apr 16, 2019

@Sharlaan
It sounds nice :-)

But I'm not sure that asyncComputed can be treated by vue's normal watcher. If so, it must work.
VglObject3d and components inheriting it are re-rendered after the inst computed property has changed. Our normal watcher will copy the old scale, rotation, position and some other properties to the new instance.

Also I wonder if asyncComputed overrides normal computed that has same name or not.
If not, we might have to remove existing inst computed property before using the component.

inst async computed property must have default value of THREE.Object3D. Otherwise component will try to set some properties to null and cause an error.

I hope it will work without problems, thank you!

@Sharlaan
Copy link

It works "partially", ie ok in basic html integration, but not in a Vue environment.

Check vue-async-computed issue#25

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants