Jump to content

Problem with Typescript example


jsgirald
 Share

Recommended Posts

Hi, I'm testing the typescript example from BabylonJS page and there's an error that keeps  showing up.

import * as BABYLON from './babylon';

class Game {
    private _canvas: HTMLCanvasElement;
    private _engine: BABYLON.Engine;
    private _scene: BABYLON.Scene;
    private _camera: BABYLON.FreeCamera;
    private _light: BABYLON.Light;

    constructor(canvasElement: string) {
        // Create canvas and engine
        this._canvas = <HTMLCanvasElement>document.getElementById(canvasElement);
        this._engine = new BABYLON.Engine(this._canvas, true);
    }

My VS Code doesn't like the line where the Engine is created, the error is:

Quote

Argument of type 'HTMLCanvasElement' is not assignable to parameter of type 'HTMLCanvasElement'.
Types of property 'getContext' are incompatible.
Type '{ (contextId: "2d", contextAttributes?: Canvas2DContextAttributes): CanvasRenderingContext2D; (co...' is not assignable to type '(contextId: "webgl2" | "experimental-webgl2", contextAttributes?: WebGLContextAttributes) => WebG...'.
Types of parameters 'contextId' and 'contextId' are incompatible.
Type '"webgl2" | "experimental-webgl2"' is not assignable to type '"2d"'.
Type '"webgl2"' is not assignable to type '"2d"'.

(property) Game._canvas: HTMLCanvasElement

I've tried type casting, in different combos, to no avail.

This same error shows when I try to do this in an Angular component, if I just comment out the offending lines and run the app it doesn't find babylonjs at all at runtime (gives a webpack error) , although it seems to be able to import without a problem from the IDE. It's all a little bit frustrating, to be honest. 

Also, in the import statement I followed this SO answer by Raanan W, but didn't work as expected, I had to tweak it a little (from babylon, instead of babylonjs).

Funnily enough, if I ignore Code and compile everything works fine, even without bothering to import!

Summing it up, I must have been overlooking something pretty obvious but I've run out of ideas, so any help would be appreciated.

 

Link to comment
Share on other sites

Man, honest, do you ever take a day off? Amazing! :D

I did try and the HTMLCanvasElement error went away. 

However, I did have to use RaananW trick of typing:

export = BABYLON; 

at the end of the file. 

I'll try the Angular component as soon as I can.

Cheers.

Link to comment
Share on other sites

Ok, I got this to work in Ionic 2!

Now a quick walkthrough in case anyone is interested, I'm assuming some familiarity with Ionic/Angular 2, otherwise check out the excellent tutorials available in their websites:

1) Start a new Ionic project, I used the tutorial template and removed all unnecessary stuff.

2) Within the project:

npm install [email protected] --save

2.1) Be careful not to write babylon (different package) and specify version. 

2.2) If you don't specify version, for some reason npm will install the preview branch and things will fall apart at runtime!

2.3) DON'T ask me how do I know! 

3) Go to the last line of babylon.d.ts (in node_modules/babylonjs/dist) and type:

export = BABYLON;

All this will create the proper entry in the node_modules folder and will let you do:

import BABYLON from 'babylonjs'

wherever you need to.

4) After this I generated an Angular Directive:

ionic g directive Canvas3dDirective

With this code:

import { Directive, ElementRef } from '@angular/core';
import BABYLON from 'babylonjs';

@Directive({
  selector: '[canvas3d]',
})
export class Canvas3dDirective {
  private _canvas: any;
  private _engine: BABYLON.Engine;
  private _scene: BABYLON.Scene;
  private _camera: BABYLON.FreeCamera;
  private _light: BABYLON.Light;

  constructor(private _ref: ElementRef) {
    console.log('Hello Canvas3d Component');
    this._canvas = <HTMLCanvasElement>this._ref.nativeElement;
    this._engine = new BABYLON.Engine(this._canvas, true);
    this._canvas.width = window.innerWidth * .9;
    this._canvas.height = window.innerHeight / 2;
    this.createScene();
    this.animate();
  }

  createScene(): void {
    console.log('createScene() called');
    // create a basic BJS Scene object
    this._scene = new BABYLON.Scene(this._engine);

    // create a FreeCamera, and set its position to (x:0, y:5, z:-10)
    this._camera = new BABYLON.FreeCamera('camera1', new BABYLON.Vector3(0, 5, -10), this._scene);

    // target the camera to scene origin
    this._camera.setTarget(BABYLON.Vector3.Zero());

    // attach the camera to the canvas
    this._camera.attachControl(this._canvas, false);

    // create a basic light, aiming 0,1,0 - meaning, to the sky
    this._light = new BABYLON.HemisphericLight('light1', new BABYLON.Vector3(0, 1, 0), this._scene);

    // create a built-in "sphere" shape; with 16 segments and diameter of 2
    let sphere = BABYLON.MeshBuilder.CreateSphere('sphere1', { segments: 16, diameter: 2 }, this._scene);

    // move the sphere upward 1/2 of its height
    sphere.position.y = 1;

    // create a built-in "ground" shape
    let ground = BABYLON.MeshBuilder.CreateGround('ground1', { width: 6, height: 6, subdivisions: 2 }, this._scene);

    if (this._scene.isReady) { //this._engine.resize();
      console.log('READY!');
      this._scene.updateTransformMatrix(true);
    }
  }

  animate(): void {
    console.log('animate() called');
    // run the render loop
    this._engine.runRenderLoop(() => {
      this._scene.render();
    });

    // the canvas/window resize event handler
    window.addEventListener('resize', () => {
      this._engine.resize();
    });
  }
}

This basically creates an html attribute that, when set on a canvas tag, inserts a BABYLON scene in it. Note that I set height and width for the canvas in the constructor, I found that the scene doesn't render properly if you don't, and setting it in html doesn't work either (I think Angular messes it up somehow).

5) Now, just include this attribute in a canvas element, note the selector 'canvas3d' in the canvas tag. This is the html template for the ionic view, minus the app menu and header stuff:


<ion-content padding>

  <h3>Welcome to your first Ionic app!</h3>

  <p>
    This starter project is our way of helping you get a functional app running in record time.
  </p>
  <p>
    Follow along on the tutorial section of the Ionic docs!
  </p>
  <p>
    <button ion-button color="primary" menuToggle>Toggle Menu</button>
  </p>

  <canvas id="surface" style="touch-action: none;" canvas3d></canvas>

</ion-content>

When this renders, this is what you should see:

screen.thumb.png.ad4e292f7ea70dbd2e2c8a26305bfa0a.png

So, this is your fantastic BABYLON scene rendered within what it's basically an Angular component!

Now, there are some problems here, probably a directive is not the best way to implement this.

The life cycle of the class has to be carefully managed to prevent a memory leak with the app creating new rendering contexts as you navigate through different views. 

The best way would likely be an Angular service (a Singleton) but I haven't tested it yet!

Cheers,

Link to comment
Share on other sites

11 hours ago, Deltakosh said:

Would you like to write a small tutorial for the doc? I think this could be helpful for a lot of people

 

Excellent, if you don't mind I'll try to improve it a little (solve the couple of issues I mentioned in my post) and will share the definitive version. Can upload it to Github too so people can have a look at the whole thing.

Link to comment
Share on other sites

  • 2 weeks later...

Just a comment on something I found doing this app.

I had to change the faceUV field of the options parameter of a box, this is how I did:

  ionViewWillEnter() {
    if (this.config.hasChanged) { // has anything changed?
      // then reset uv settings for both dice
      let options = {
        size: 1.25,
        faceUV: this.config.getFaceUV(this.config.colorOne),
        updatable: false
      }
      let vertexData = VertexData.CreateBox(options);
      vertexData.applyToMesh(this.firstDie, options.updatable);      

      options = {
        size: 1.25,
        faceUV: this.config.getFaceUV(this.config.colorTwo),
        updatable: false
      }
      vertexData = VertexData.CreateBox(options);
      vertexData.applyToMesh(this.secondDie, options.updatable);      
    }
  }

Would it be possible to add a method to the mesh generated by CreateBox to handle this use case?

Something like this:

resetOptions(newOptions): void {
    let vertexData = VertexData.CreateBox(newOptions);
    vertexData.applyToMesh(myBox, newOptions.updatable);
}

Not sure where this should go, maybe added to the mesh by the Meshbuilder.CreateXXX static methods?

Link to comment
Share on other sites

2 hours ago, Deltakosh said:

Hello this is really great

You should add it to our doc as well!

 

I've never done this before, so I'll read carefully the instructions!

If I got it right first I fork the documentation from github, clone locally, create the new content, grunt build and create a pull request, right?

 

2 hours ago, Deltakosh said:

Regarding your question, as you are recreating a whole new box, why no disposing the previous one directly instead of reapplying the changes to the current one?

At first I thought of doing it, but if you dispose of the previous one you lose position, rotation and physics impostor. Yes, you can copy all that, but I'm lazy ;).

Then I thought there must be a simpler way, with my method you mantain all that and only change the options parameters.

In fact, all the CreateXXX methods can add this resetOptions(options) method to the resulting mesh. However I don't know if this would break something else. 

The thing would look like this: 

let myBox = BABYLON.MeshBuilder.CreateBox("box", options, scene);

CreateBox adds the resetOptions method to the mesh, and when you need to change the options parameter just:

myBox.restOptions(newOptions);

This would only work for meshes created the CreateXXX way, but AFAIK those are the only use cases for the option parameters.

The main limitation is that you keep the same texture object, but if you use the faceUV parameter it works just as a spritesheet where you keep all the different textures and only change the UVs. 

Link to comment
Share on other sites

On 6/26/2017 at 2:16 PM, jsgirald said:

f I got it right first I fork the documentation from github, clone locally, create the new content, grunt build and create a pull request, right?

Correct :)

For the reset option, instead of having it on mesh, I would prefer having it on MeshBuilder to keep it related to MeshBuilder meshes

Link to comment
Share on other sites

Very cool. I have experimented with using Angular and Babylonjs before. One thing I ran into before I understood what was happening was ngZone causing horrible performance due to the way it monkey patches all asynchronous calls (including requestAnimationFrame) for its change detection. I found I had to disable change detection for these calls like so:

 

this.zone.runOutsideAngular(() => {
      this.engine.runRenderLoop(() => {
        this.scene.render();
      });
    })

 

Link to comment
Share on other sites

5 minutes ago, unintellisense said:

Very cool. I have experimented with using Angular and Babylonjs before. One thing I ran into before I understood what was happening was ngZone causing horrible performance due to the way it monkey patches all asynchronous calls (including requestAnimationFrame) for its change detection. I found I had to disable change detection for these calls like so:

 


this.zone.runOutsideAngular(() => {
      this.engine.runRenderLoop(() => {
        this.scene.render();
      });
    })

 

Thanks for the tip. I still have to learn and undestand a lot in Angular!

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...