B4J Tutorial [BANano3D] Beginning 3D with Three.JS

Ola there...

BANano3D

This is also part of my bucket list things...:p .Now that I'm a little comfortable with the html5 games outlook using BANanoCreateJS, today I thought why not try 3D? This is going to be a long road. ThreeJS is kinda cumbersome but I will do my best to stick to what is quick and enable us to get the job done.

BANano3D_Lesson_0.gif


In this example, we just create a block randomly (on website refresh).

I have defined some skeleton classes so far just to talk to this example. This used WebGL and if your browser does not support WEBGL, well, watch this space, will do an example that uses the canvas soon.

So let's take a peek at the code.

B4X:
Sub Init
    Math.Initialize("Math")
    
    'do some webpage body settings
    body = BANano.GetElement("#body")
    body.SetStyle(BANano.ToJson(CreateMap("margin": "0", "overflow": "hidden")))
    body.Empty
    
    'lets get the window inner height and width
    WindowInnerWidth = BANano.Window.InnerWidth
    WindowInnerHeight = BANano.Window.InnerHeight
    
    ' this will hold all elements
    scene.Initialize
    
    ' camera, what we will see
    camera.Initialize(45, WindowInnerWidth / WindowInnerHeight, 0.1, 1000)
    
    'create a renderer, set background and size
    renderer.Initialize
    renderer.setClearColor("0x000000", 1.0)
    renderer.setSize(WindowInnerWidth, WindowInnerHeight)
    
    Dim cubeGeometry As TDBoxGeometry
    Dim rnd1 As Double = Math.RunMethod("random", Null)
    Dim rnd2 As Double = Math.RunMethod("random", Null)
    Dim rnd3 As Double = Math.RunMethod("random", Null)
    rnd1 = 10 * rnd1
    rnd2 = 10 * rnd2
    rnd3 = 10 * rnd3
    cubeGeometry.Initialize(rnd1, rnd2, rnd3)
    '
    Dim cubeMaterial As TDMeshNormalMaterial
    cubeMaterial.Initialize
    
    Dim cube As TDMesh
    cube.Initialize(cubeGeometry.BoxGeometry, cubeMaterial.MeshNormalMaterial)
    scene.add(cube.Mesh)
    
    '
    'position and point the camera to the center of the scene
    camera.SetPosition(15, 16, 13)
    camera.LookAt(scene.GetPosition)
        
    'add the output of the renderer to the html element
    body.Append(renderer.GetDomElement)
    'render stuff
    renderer.render(scene.Scene, camera.PerspectiveCamera)
    
    
End Sub

Later!
 

Mashiane

Expert
Licensed User
Longtime User
Lesson 2

In lesson 1 we rendered the 3D cube using WebGL, in lesson 2, we render the cube using the canvas. We do this by changing..

B4X:
renderer.Initialize

To

B4X:
renderer.InitializeCanvas

There is no other code we need to change on the code. One might use the canvas renderer when the browser does not support WebGL.
 

Mashiane

Expert
Licensed User
Longtime User
Lesson 3

Here we render an HTML element in 3D to our canvas. This is made up of H1, Span and a TextArea

BANano3D_Lesson_3.gif


The magic that runs this code is embedded in this code.

B4X:
Sub createCSS3DObject As TDCSS3DObject
    'add the html elements
    Dim html As UOEHTML
    html.Initialize("").SetTag("div")
    html.AddH1("This is an H1 Element.")
    html.AddSpan("Hello BANano3D...","large",Null)
    html.AddTextArea(""," A Mashy Creation!","",Null)
    '
    Dim doc As BANanoObject = BANano.Window.GetField("document")
    Dim wrapper As BANanoObject = doc.RunMethod("createElement", Array("div"))
    wrapper.SetField("innerHTML", html.html)
    
    'generate a random color
    Dim rnd1 As Double = Math.RunMethod("random", Null)
    Dim colorValue As Double = 16777215 * rnd1
    Dim color As TDColor
    color.Initialize(colorValue)
    Dim colorStyle As Object = color.getStyle
    '
    Dim div As BANanoObject = wrapper.GetField("firstChild")
    div.GetField("style").SetField("width", "370px")
    div.GetField("style").SetField("height", "370px")
    div.GetField("style").SetField("opacity", 0.7)
    div.GetField("style").SetField("background", colorStyle)
    '
    'create a CSS3Dobject And Return it.
    Dim cssobject As TDCSS3DObject
    cssobject.Initialize(div)
    Return cssobject
End Sub
 

Mashiane

Expert
Licensed User
Longtime User
Lesson 4

Depending on your browser, you might want to check if WebGL supports it or not so that you can code accordingly. In this lesson, you run a method to check if WebGL is supported or not.

B4X:
Sub Init
    'does webgl exist
    Dim webGL As Boolean = BANano3D.WebGLAvailable
    If webGL Then
        BANano.Window.Alert("WebGL is supported!")
    Else
        BANano.Window.Alert("WebGL is NOT supported!")
    End If
End Sub
 

Mashiane

Expert
Licensed User
Longtime User
Lesson 5

Here we actually animate something...

BANano3D_Lesson_5.gif


We read the rotation of the cube and increment the X rotation with 0.05. Here we created a cube the same way as in the first lesson. We then separated the render functionality as we need to call that during the animation after we increment the X rotation.

So our render method is...

B4X:
Sub render
    renderer.render(scene.Scene, camera.PerspectiveCamera)
    'get the cube
    Dim cube As TDMesh = scene.GetMeshByName("cube")
    cube.IncrementRotationX(0.05)
    '
    BANano3D.requestAnimationFrame(BANano.CallBack(Me,"render", Null))
End Sub

We gave our cube a name called "cube", then we get it, increment the rotation and call requestAnimationFrame. Very impressed with BANano here. We call render and also pass it to requestAnimationFrame...
 

Mashiane

Expert
Licensed User
Longtime User
Lesson 6

This deals with detecting and displaying FPS (frame per second) for your 3D scene. See on top left of the scene.

BANano3D_Lesson_06.gif


To do this, we create a stats component and add it to the scene..

B4X:
'create the stats
    stats = createStats
    body.Append(stats.GetDomElement)

B4X:
Sub createStats As TDStats
    Dim stats1 As TDStats
    stats1.Initialize
    stats1.setMode(0)
    '
    Dim DEStyle As BANanoObject = stats1.DomElementStyle
    DEStyle.SetField("position", "absolute")
    DEStyle.SetField("left", "0px")
    DEStyle.SetField("top", "0px")
    Return stats1
End Sub
 

Mashiane

Expert
Licensed User
Longtime User
Lesson 7

This deals with changing variables during runtime using dat.GUI. This acts like a property bag.

BANano3D_Lesson_07.gif


First, we create some elements to add to the property bag, we use a map to do that..

B4X:
control = CreateMap()
    control.Put("rotationSpeed", 0.005)
    control.Put("scale", 1)
    
    'add control
    addControls(control)

We then add these to the 'property bag'

B4X:
Sub addControls(controlObject As Object)
    Dim gui As TDDatUI
    gui.Initialize
    gui.add(controlObject, "rotationSpeed", -0.1, 0.1)
    gui.Add(controlObject, "scale", 0.01, 2)
End Sub

When the values are changed, these need to update the object we need, so we update our render code to use the affected properties.

B4X:
Sub render
    renderer.render(scene.Scene, camera.PerspectiveCamera)
    'get the cube
    Dim cube As TDMesh = scene.GetMeshByName("cube")
    '
    Dim rotSpeed As Double = control.GetDefault("rotationSpeed", 0.5)
    Dim scale As Double = control.GetDefault("scale", 1)
    cube.IncrementRotationX(rotSpeed)
    cube.SetScale(scale, scale, scale)
    '
    BANano3D.requestAnimationFrame(BANano.CallBack(Me,"render", Null))
End Sub
 

Mashiane

Expert
Licensed User
Longtime User
Lesson 8

This is just a sneek preview on textures. We apply a texture to an object. We have been using a MeshNormalMaterial to do this, so we need to use a MeshBasicMaterial.

BANano3D_Lesson_08.jpg


B4X:
'we use a mesh basic material
    Dim cubeMaterial As TDMeshBasicMaterial
    cubeMaterial.Initialize
    
    'add texture
    cubeMaterial.SetMap(BANano3D.LoadTexture("./assets/wood_1-1024x1024.png"))
    
    Dim cube As TDMesh
    cube.Initialize(cubeGeometry.BoxGeometry, cubeMaterial.MeshBasicMaterial)
    cube.SetName("cube")
    scene.add(cube.Mesh)
 

Mashiane

Expert
Licensed User
Longtime User
Lesson 9

Here we want to move the object via keyboard key presses. So we detect left, right, up and down keys on the document. Ensure the page has the focus (click it)

BANano3D_Lesson_09.gif


So just after rendering the body, we attach an event to the body.

B4X:
body.Append(renderer.GetDomElement)
    '
    Dim e As BANanoEvent
    Dim cb As BANanoObject = BANano.CallBack(Me, "setupKeyControls", Array(e))
    body.AddEventListener("keydown", cb, True)

This calls the setupKeyControls sub routine. As noted, in our render, we are not changing the rotation at all and this is done during keyboard key press.

B4X:
Sub render
    renderer.render(scene.Scene, camera.PerspectiveCamera)
    BANano3D.requestAnimationFrame(BANano.CallBack(Me,"render", Null))
End Sub

Sub setupKeyControls(e As BANanoEvent)
    'get the cube
    Dim cube As TDMesh = scene.GetMeshByName("cube")
    '
    Select Case e.KeyCode
    Case 37
        cube.IncrementRotationX(0.2)
    Case 38
        cube.DecrementRotationZ(0.2)
    Case 39
        cube.DecrementRotationX(0.2)
    Case 40
        cube.IncrementRotationZ(0.2)
    End Select   
End Sub
 
Top