{"id":1085,"date":"2013-06-05T11:57:37","date_gmt":"2013-06-05T11:57:37","guid":{"rendered":"http:\/\/www.osd.net\/blog\/?p=1085"},"modified":"2013-06-05T11:57:37","modified_gmt":"2013-06-05T11:57:37","slug":"3d-board-game-in-a-browser-using-webgl-and-three-js-part-2","status":"publish","type":"post","link":"https:\/\/www.osd.net\/blog\/web-development\/3d-board-game-in-a-browser-using-webgl-and-three-js-part-2\/","title":{"rendered":"3D board game in a browser using WebGL and Three.js, part 2"},"content":{"rendered":"<p>In a <a title=\"3D board game in a browser using WebGL and Three.js, part 1\" href=\"https:\/\/www.osd.net\/blog\/web-development\/3d-board-game-in-a-browser-using-webgl-and-three-js-part-1\/\">previous article<\/a> we&#8217;ve built the basic structure of our 3D checkers game and ended up with drawing a simple cube. In this article we&#8217;ll add the board and the pieces. By the end of this part you&#8217;ll have something like that looks like this:<\/p>\n<p><a href=\"https:\/\/www.osd.net\/blog\/wp-content\/uploads\/2013\/06\/webgl_3d_checkers_board_with_pieces2.jpg\"><img loading=\"lazy\" class=\"alignnone size-full wp-image-1093\" alt=\"webgl_3d_checkers_board_with_pieces2\" src=\"https:\/\/www.osd.net\/blog\/wp-content\/uploads\/2013\/06\/webgl_3d_checkers_board_with_pieces2.jpg\" width=\"599\" height=\"397\" srcset=\"https:\/\/www.osd.net\/blog\/wp-content\/uploads\/2013\/06\/webgl_3d_checkers_board_with_pieces2.jpg 599w, https:\/\/www.osd.net\/blog\/wp-content\/uploads\/2013\/06\/webgl_3d_checkers_board_with_pieces2-300x198.jpg 300w\" sizes=\"(max-width: 599px) 100vw, 599px\" \/><\/a><\/p>\n<p>First, download and copy the content from the 3D assets\u00a0<a href=\"https:\/\/www.osd.net\/blog\/wp-content\/uploads\/2013\/06\/webgl_checkers_3d_assets.zip\">archive<\/a> into <em>3d_assets\/<\/em> folder.<\/p>\n<p><strong>Note:<\/strong> Unless specified otherwise, most of the code from this article will go into <strong>js\/BoardController.js<\/strong>.<\/p>\n<p>Let&#8217;s start with some variable declarations:<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n\r\nvar cameraController;\r\n\r\nvar lights = {};\r\nvar materials = {};\r\n\r\nvar pieceGeometry = null;\r\nvar boardModel;\r\nvar groundModel;\r\n\r\nvar squareSize = 10;\r\n\r\n<\/pre>\n<p>The name of the variables should be self-explanatory and as we go along they will make more sense.<\/p>\n<h2>Creating a light<\/h2>\n<p>Before adding any objects into our scene, we need to create at least one light or else the objects will appear black. So, create a <strong>initLights<\/strong> function after <em>initEngine:<\/em><\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n\r\nfunction initLights() {\r\n    \/\/ top light\r\n    lights.topLight = new THREE.PointLight();\r\n    lights.topLight.position.set(0, 150, 0);\r\n    lights.topLight.intensity = 1.0;\r\n\r\n    \/\/ add the lights in the scene\r\n    scene.add(lights.topLight);\r\n}\r\n\r\n<\/pre>\n<p>For now just a point light is created and placed above the scene. The newly created function must be called from <em>drawBoard<\/em> after the <em>initEngine<\/em> call.<\/p>\n<h2>Creating the materials<\/h2>\n<p>Now we need to create some materials for the objects so that they will not appear in wireframe like the cube from the previous article. So, create a <strong>initMaterials<\/strong> function after <em>initLights<\/em>:<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\nfunction initMaterials() {\r\n    \/\/ board material\r\n    materials.boardMaterial = new THREE.MeshLambertMaterial({\r\n        map: THREE.ImageUtils.loadTexture(assetsUrl + 'board_texture.jpg')\r\n    });\r\n\r\n    \/\/ ground material\r\n    materials.groundMaterial = new THREE.MeshBasicMaterial({\r\n    \ttransparent: true,\r\n    \tmap: THREE.ImageUtils.loadTexture(assetsUrl + 'ground.png')\r\n    });\r\n\r\n    \/\/ dark square material\r\n    materials.darkSquareMaterial = new THREE.MeshLambertMaterial({\r\n    \tmap: THREE.ImageUtils.loadTexture(assetsUrl + 'square_dark_texture.jpg')\r\n    });\r\n    \/\/\r\n    \/\/ light square material\r\n    materials.lightSquareMaterial = new THREE.MeshLambertMaterial({\r\n    \tmap: THREE.ImageUtils.loadTexture(assetsUrl + 'square_light_texture.jpg')\r\n    });\r\n\r\n    \/\/ white piece material\r\n    materials.whitePieceMaterial = new THREE.MeshPhongMaterial({\r\n    \tcolor: 0xe9e4bd,\r\n    \tshininess: 20\r\n    });\r\n\r\n    \/\/ black piece material\r\n    materials.blackPieceMaterial = new THREE.MeshPhongMaterial({\r\n    \tcolor: 0x9f2200,\r\n    \tshininess: 20\r\n\t});\r\n\r\n    \/\/ pieces shadow plane material\r\n    materials.pieceShadowPlane = new THREE.MeshBasicMaterial({\r\n        transparent: true,\r\n        map: THREE.ImageUtils.loadTexture(assetsUrl + 'piece_shadow.png')\r\n    });\r\n}\r\n\r\n<\/pre>\n<p>On line 3 a non-shiny material for the board is created with a texture for the color map.<\/p>\n<p>On line 8 a flat material is created for the ground. The ground will fake the board shadow, so a transparent texture is loaded for the color map. When using transparent images the material&#8217;s <em>transparent<\/em> property must be set to <em>true<\/em>.<\/p>\n<p>The board&#8217;s squares will be created as individual objects, so on lines 14 and 19 the materials for the dark and light square is created.<\/p>\n<p>On lines 24 and 30 a material is created for each piece type. Because we want some shininess on the pieces, a <a title=\"Three.js MeshPhongMaterial\" href=\"http:\/\/threejs.org\/docs\/58\/#Reference\/Materials\/MeshPhongMaterial\">MeshPhongMaterial<\/a> was chosen.<\/p>\n<p>The shadow cast by the pieces on the board will be faked using planes placed under them with a material as shown on line 36.<\/p>\n<p>Now that we have the <em>initMaterials<\/em> function created, let&#8217;s call it from <em>drawBoard<\/em> just after the <em>initLights<\/em> call.<\/p>\n<h2>Loading 3D objects from external files<\/h2>\n<p>With the lights and materials created, we&#8217;re ready to load the board and the piece. If you look in the <em>3d_assets\/<\/em> folder you&#8217;ll find the following files: <strong>board.js<\/strong>, board.obj, <strong>piece.js<\/strong> and piece.obj.<\/p>\n<p>The .js files are the ones that will be loaded. These are created from the .obj files using the python script from <em>three.js\/utils\/converters\/obj\/convert_obj_three.py<\/em> like this:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n\r\npython\u00a0convert_obj_three.py\u00a0-i board.obj -o board.js\r\n\r\n<\/pre>\n<p><strong>Note:<\/strong> You&#8217;ll need Python 2.x installed on you computer to run that command.<\/p>\n<p>If you want to modify the 3D objects or maybe to just have a look at them, you can open the .obj files in a 3D modeling application like Autodesk Maya or 3ds Max.<\/p>\n<p><strong>Note:<\/strong> The <em>pivot point<\/em> for the board was modified to sit on top of it, at the top-left corner; the pivot point for the piece was placed at the base.<\/p>\n<p>Now let&#8217;s modify our <em>initObjects<\/em> function like this:<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\nfunction initObjects(callback) {\r\n    var loader = new THREE.JSONLoader();\r\n    var totalObjectsToLoad = 2; \/\/ board + the piece\r\n    var loadedObjects = 0; \/\/ count the loaded pieces\r\n\r\n    \/\/ checks if all the objects have been loaded\r\n    function checkLoad() {\r\n        loadedObjects++;\r\n\r\n        if (loadedObjects === totalObjectsToLoad &amp;&amp; callback) {\r\n            callback();\r\n        }\r\n    }\r\n\r\n    \/\/ load board\r\n    loader.load(assetsUrl + 'board.js', function (geom) {\r\n        boardModel = new THREE.Mesh(geom, materials.boardMaterial);\r\n\r\n        scene.add(boardModel);\r\n\r\n        checkLoad();\r\n    });\r\n\r\n    \/\/ load piece\r\n    loader.load(assetsUrl + 'piece.js', function (geometry) {\r\n        pieceGeometry = geometry;\r\n\r\n        checkLoad();\r\n    });\r\n\r\n    scene.add(new THREE.AxisHelper(200));\r\n}\r\n<\/pre>\n<p>On line 2 an instance of <a title=\"Three.js JSONLoader\" href=\"http:\/\/threejs.org\/docs\/58\/#Reference\/Loaders\/JSONLoader\">JSONLoader<\/a> is created that will be used to load the external 3D models. Since the objects are loaded via AJAX we can&#8217;t call the success callback right away, but after they are loaded. So between the lines 3 and 13 we have some code that will help us to do that.<\/p>\n<p>On line 16 the board model is loaded by passing a URL and a callback function to the loader&#8217;s load method. After the board is loaded we create a mesh from the loaded geometry and add it to the scene. On line 21 we verify if all the needed objects have been loaded so that we could call the success callback passed to <em>initObjects<\/em> function.<\/p>\n<p>On line 25 the piece is loaded. Since we&#8217;ll need to create multiple pieces from the loaded geometry we save it for later use. We&#8217;ll add the pieces into our scene\u00a0 later.<\/p>\n<p>On line 31 we&#8217;ll add an <strong>AxisHelper<\/strong> into the scene to be able to see the XYZ axes to help us visualize the objects position.<\/p>\n<p>Loading our project in the browser right now you should see something like this:<\/p>\n<p><a href=\"https:\/\/www.osd.net\/blog\/wp-content\/uploads\/2013\/06\/webgl_3d_checkers_board1.jpg\"><img loading=\"lazy\" class=\"alignnone size-full wp-image-1090\" alt=\"webgl_3d_checkers_board1\" src=\"https:\/\/www.osd.net\/blog\/wp-content\/uploads\/2013\/06\/webgl_3d_checkers_board1.jpg\" width=\"599\" height=\"377\" srcset=\"https:\/\/www.osd.net\/blog\/wp-content\/uploads\/2013\/06\/webgl_3d_checkers_board1.jpg 599w, https:\/\/www.osd.net\/blog\/wp-content\/uploads\/2013\/06\/webgl_3d_checkers_board1-300x188.jpg 300w\" sizes=\"(max-width: 599px) 100vw, 599px\" \/><\/a><\/p>\n<p>As you can see the board is automatically positioned so that the top-left corner is at (0,0,0). This happened because the pivot point position was changed in the 3D modeling application before exporting the model. In this way we&#8217;ll only have to work with positive values for X and Z axes when placing the squares and pieces.<\/p>\n<p>Now let&#8217;s add the squares and the ground that will fake the board shadow:<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n\r\n\/\/ load board\r\nloader.load(assetsUrl + 'board.js', function (geom) {\r\n    boardModel = new THREE.Mesh(geom, materials.boardMaterial);\r\n    boardModel.position.y = -0.02;\r\n\r\n...\r\n\r\n\/\/ load piece\r\n...\r\n\r\n\/\/ add ground\r\ngroundModel = new THREE.Mesh(new THREE.PlaneGeometry(100, 100, 1, 1), materials.groundMaterial);\r\ngroundModel.position.set(squareSize * 4, -1.52, squareSize * 4);\r\ngroundModel.rotation.x = -90 * Math.PI \/ 180;\r\n\/\/\r\nscene.add(groundModel);\r\n\r\n\/\/ create the board squares\r\nvar squareMaterial;\r\n\/\/\r\nfor (var row = 0; row &lt; 8; row++) {\r\n    for (var col = 0; col &lt; 8; col++) {\r\n        if ((row + col) % 2 === 0) { \/\/ light square\r\n            squareMaterial = materials.lightSquareMaterial;\r\n        } else { \/\/ dark square\r\n            squareMaterial = materials.darkSquareMaterial;\r\n        }\r\n\r\n        var square = new THREE.Mesh(new THREE.PlaneGeometry(squareSize, squareSize, 1, 1), squareMaterial);\r\n\r\n        square.position.x = col * squareSize + squareSize \/ 2;\r\n        square.position.z = row * squareSize + squareSize \/ 2;\r\n        square.position.y = -0.01;\r\n\r\n        square.rotation.x = -90 * Math.PI \/ 180;\r\n\r\n        scene.add(square);\r\n    }\r\n}\r\n\r\n<\/pre>\n<p>To be sure that the pieces and the squares do not intersect the board is moved slightly in negative Y axis on line 4. We&#8217;ve used a value of -0.02 because the squares we&#8217;ll be positioned at -0.01 along the Y axis.<\/p>\n<p>On line 12 the ground is created and on line 13 it&#8217;s moved to match the board position using the <em>squareSize<\/em> property. Remember that we&#8217;ve set the value for this property to 10 in the beginning. The board and the piece have been modeled especially for that value. The -1.52 value for the Y axis was taken from the 3D modeling software by measuring the board height and by adding the 0.02 value used to move the board down.<\/p>\n<p>On line 14 the ground needs to be rotated -90 degree along the X axis because a THREE.PlaneGeometry will be positioned along the XY plane.<\/p>\n<p><strong>Note:<\/strong> Three.js works with radians, so if we want to use degrees we need to convert the values.<\/p>\n<p><strong><\/strong>From line 19 the board&#8217;s squares are added.<\/p>\n<p>Right now the camera will look and orbit around the board&#8217;s top-left corner, around the (0,0,0) position. Let&#8217;s change that by going into the initEngine function and modifying the camera properties:<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n\/\/ create camera\r\ncamera = new THREE.PerspectiveCamera(35, viewWidth \/ viewHeight, 1, 1000);\r\ncamera.position.set(squareSize * 4, 120, 150);\r\ncameraController = new THREE.OrbitControls(camera, containerEl);\r\ncameraController.center = new THREE.Vector3(squareSize * 4, 0, squareSize * 4);\r\n<\/pre>\n<p>On line 3 we&#8217;ve modified the X position of the camera and on line 5 we&#8217;ve told the camera controller to look at and move around the board&#8217;s center position.<\/p>\n<p>The top light also needs to be repositioned:<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n\r\nlights.topLight.position.set(squareSize * 4, 150, squareSize * 4);\r\n\r\n<\/pre>\n<p>Refresh the browser and you should see this:<\/p>\n<p><a href=\"https:\/\/www.osd.net\/blog\/wp-content\/uploads\/2013\/06\/webgl_3d_checkers_board2.jpg\"><img loading=\"lazy\" class=\"alignnone size-full wp-image-1091\" alt=\"webgl_3d_checkers_board2\" src=\"https:\/\/www.osd.net\/blog\/wp-content\/uploads\/2013\/06\/webgl_3d_checkers_board2.jpg\" width=\"599\" height=\"385\" srcset=\"https:\/\/www.osd.net\/blog\/wp-content\/uploads\/2013\/06\/webgl_3d_checkers_board2.jpg 599w, https:\/\/www.osd.net\/blog\/wp-content\/uploads\/2013\/06\/webgl_3d_checkers_board2-300x192.jpg 300w\" sizes=\"(max-width: 599px) 100vw, 599px\" \/><\/a><\/p>\n<h2>Adding the checkers pieces<\/h2>\n<p>With the board added and looking nice we are now ready to add the pieces in their starting position. To make that happen we&#8217;ll need a variable that will hold the pieces and one public method to add tthem. With that in mind modify the BoardController.js file like this:<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n...\r\nvar squareSize = 10;\r\n\r\nvar board = [\r\n    [0, 0, 0, 0, 0, 0, 0, 0],\r\n    [0, 0, 0, 0, 0, 0, 0, 0],\r\n    [0, 0, 0, 0, 0, 0, 0, 0],\r\n    [0, 0, 0, 0, 0, 0, 0, 0],\r\n    [0, 0, 0, 0, 0, 0, 0, 0],\r\n    [0, 0, 0, 0, 0, 0, 0, 0],\r\n    [0, 0, 0, 0, 0, 0, 0, 0],\r\n    [0, 0, 0, 0, 0, 0, 0, 0]\r\n];\r\n\r\n...\r\n\r\nthis.drawBoard = function () {\r\n...\r\n};\r\n\r\nthis.addPiece = function (piece) {\r\n    var pieceMesh = new THREE.Mesh(pieceGeometry);\r\n    var pieceObjGroup = new THREE.Object3D();\r\n    \/\/\r\n    if (piece.color === CHECKERS.WHITE) {\r\n        pieceObjGroup.color = CHECKERS.WHITE;\r\n        pieceMesh.material = materials.whitePieceMaterial;\r\n    } else {\r\n        pieceObjGroup.color = CHECKERS.BLACK;\r\n        pieceMesh.material = materials.blackPieceMaterial;\r\n    }\r\n\r\n    \/\/ create shadow plane\r\n    var shadowPlane = new THREE.Mesh(new THREE.PlaneGeometry(squareSize, squareSize, 1, 1), materials.pieceShadowPlane);\r\n    shadowPlane.rotation.x = -90 * Math.PI \/ 180;\r\n\r\n    pieceObjGroup.add(pieceMesh);\r\n    pieceObjGroup.add(shadowPlane);\r\n\r\n    pieceObjGroup.position = boardToWorld(piece.pos);\r\n\r\n    board[ piece.pos[0] ][ piece.pos[1] ] = pieceObjGroup;\r\n\r\n    scene.add(pieceObjGroup);\r\n};\r\n\r\n<\/pre>\n<p>On line 4 we&#8217;ll use a 2 dimensional 8&#215;8 array to create an internal board representation.<\/p>\n<p>From line 21 the <strong>addPiece<\/strong> method is defined. This function will expect as its only parameter an object with properties related to the piece, like color and position.<\/p>\n<p>Because we&#8217;ll use a textured plane under the piece to fake the shadow on the board, on line 23 an instance of <a title=\"Three.js Object3D\" href=\"http:\/\/threejs.org\/docs\/58\/#Reference\/Core\/Object3D\">Object3D<\/a> is created that will group the piece mesh and the shadow plane together.<\/p>\n<p>Between line 25 and 31 the piece group gets assigned a WHITE or BLACK color and the right material is assigned to the piece mesh.<\/p>\n<p>On line 34 the shadow plane is created and on line 37 and 38 the piece mesh and the shadow plane are grouped together.<\/p>\n<p>On line 40 the piece group is positioned using the <em>pos<\/em> property from the function&#8217;s parameter object. Since its value will be an array holding the position in a 8&#215;8 array, we need to find out the position in our 3D space using the <em>boardToWorld<\/em> function that will be created immediately.<\/p>\n<p>On line 42 the piece group is stored in the internal board representation and also added in the scene on line 44.<\/p>\n<p>Now let&#8217;s add the <strong>boardToWorld<\/strong> function right after <em>onAnimationFrame<\/em>:<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n\r\nfunction boardToWorld (pos) {\r\n    var x = (1 + pos[1]) * squareSize - squareSize \/ 2;\r\n    var z = (1 + pos[0]) * squareSize - squareSize \/ 2;\r\n\r\n    return new THREE.Vector3(x, 0, z);\r\n}\r\n\r\n<\/pre>\n<p>The above function will return the center position of the right square.<\/p>\n<p>The last thing we need to do in BoardController.js is to make the <em>drawBoard<\/em> function receive a callback:<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n\r\nthis.drawBoard = function (callback) {\r\n    ...\r\n    initObjects(function () {\r\n        onAnimationFrame();\r\n\r\n        callback();\r\n    });\r\n}\r\n\r\n<\/pre>\n<p>Now open the <strong>Game.js<\/strong> file and modify it like this:<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n\r\nvar CHECKERS = {\r\n\tWHITE: 1,\r\n    BLACK: 2\r\n};\r\n\r\n...\r\n\r\nvar boardController = null;\r\n\r\nvar board = [\r\n    [0, 0, 0, 0, 0, 0, 0, 0],\r\n    [0, 0, 0, 0, 0, 0, 0, 0],\r\n    [0, 0, 0, 0, 0, 0, 0, 0],\r\n    [0, 0, 0, 0, 0, 0, 0, 0],\r\n    [0, 0, 0, 0, 0, 0, 0, 0],\r\n    [0, 0, 0, 0, 0, 0, 0, 0],\r\n    [0, 0, 0, 0, 0, 0, 0, 0],\r\n    [0, 0, 0, 0, 0, 0, 0, 0]\r\n];\r\n\r\n...\r\n\r\nfunction init() {\r\n    ...\r\n    boardController.drawBoard(onBoardReady);\r\n}\r\n\r\nfunction onBoardReady() {\r\n    \/\/ setup the board pieces\r\n    var row, col, piece;\r\n    \/\/\r\n    for (row = 0; row &lt; board.length; row++) {\r\n        for (col = 0; col &lt; board[row].length; col++) {\r\n            if (row &lt; 3 &amp;&amp; (row + col) % 2) { \/\/ black piece\r\n                piece = {\r\n                    color: CHECKERS.BLACK,\r\n                    pos: [row, col]\r\n                };\r\n            } else if (row &gt; 4 &amp;&amp; (row + col) % 2) { \/\/ white piece\r\n                piece = {\r\n                    color: CHECKERS.WHITE,\r\n                    pos: [row, col]\r\n                };\r\n            } else { \/\/ empty square\r\n                piece = 0;\r\n            }\r\n\r\n            board[row][col] = piece;\r\n\r\n            if (piece) {\r\n                boardController.addPiece(piece);\r\n            }\r\n        }\r\n    }\r\n}\r\n<\/pre>\n<p>The above code should be self-explanatory and if you do a refresh in the browser you should see something like this:<\/p>\n<p><a href=\"https:\/\/www.osd.net\/blog\/wp-content\/uploads\/2013\/06\/webgl_3d_checkers_board_with_pieces.jpg\"><img loading=\"lazy\" class=\"alignnone size-full wp-image-1092\" alt=\"webgl_3d_checkers_board_with_pieces\" src=\"https:\/\/www.osd.net\/blog\/wp-content\/uploads\/2013\/06\/webgl_3d_checkers_board_with_pieces.jpg\" width=\"599\" height=\"380\" srcset=\"https:\/\/www.osd.net\/blog\/wp-content\/uploads\/2013\/06\/webgl_3d_checkers_board_with_pieces.jpg 599w, https:\/\/www.osd.net\/blog\/wp-content\/uploads\/2013\/06\/webgl_3d_checkers_board_with_pieces-300x190.jpg 300w\" sizes=\"(max-width: 599px) 100vw, 599px\" \/><\/a><\/p>\n<p>You&#8217;ll notice that the pieces don&#8217;t look as nice as in the image from the beginning of the article. That&#8217;s because more lights are needed in the scene. You can download the sample code and have a look in <em>initLights<\/em> function and <em>onAnimationFrame<\/em> to see how to add the new lights.<\/p>\n<h2>End of part 2<\/h2>\n<p>That\u2019s all folks! In the next and final article we will allow the pieces to be dragged and some checkers game logic will be added also.<\/p>\n<p>You can download the current phase of the project from <a href=\"https:\/\/www.osd.net\/blog\/wp-content\/uploads\/2013\/06\/webgl_3d_checkers_part2.zip\">here<\/a>.<\/p>\n<p>If you have questions, suggestions or improvements don\u2019t hesitate to add a comment and let me know about them.<\/p>\n<div id=\"share-and-beer-container\">\t<div id=\"buy_me_a_beer_div\" class=\"single beer\">\n\t \t\n\t\t<div class=\"buy-beer\" onclick=\"document.getElementById('buy_me_a_beer_form').submit();\">\n\t\t\t<form action=\"https:\/\/www.paypal.com\/cgi-bin\/webscr\" id=\"buy_me_a_beer_form\" method=\"post\">\n\t\t\t\t<input type=\"hidden\" name=\"cmd\" value=\"_xclick\" \/>\n\t\t\t\t<input type=\"hidden\" name=\"business\" value=\"info@directaccess.ro\" \/>  \n\t\t\t\t<input type=\"hidden\" name=\"item_name\" value=\"A Beer For 3D board game in a browser using WebGL and Three.js, part 2\" \/>  \n\t\t\t\t<input type=\"hidden\" name=\"item_number\" value=\"1\" \/>  \n\t\t\t\t<input type=\"hidden\" name=\"return\" value=\"https:\/\/www.osd.net\/blog\/web-development\/3d-board-game-in-a-browser-using-webgl-and-three-js-part-2\/\" \/>  \n\t\t\t\t<input type=\"hidden\" name=\"amount\" value=\"5\" \/>  \n\t\t\t\t<input type=\"hidden\" name=\"undefined_quantity\" value=\"1\" \/>  \n\t\t\t\t<input type=\"hidden\" name=\"currency_code\" value=\"USD\" \/>  \n\t\t\t<\/form>\n\t\t\t<p class=\"buy-beer-text\">If you liked this post <br \/> you can <strong>buy me a beer<\/strong><\/p>\n\t\t<\/div>\n\t<\/div><\/div>","protected":false},"excerpt":{"rendered":"<p>In a previous article we&#8217;ve built the basic structure of our 3D checkers game and ended up with drawing a simple cube. In this article we&#8217;ll add the board and the pieces. By the end of this part you&#8217;ll have something like that looks like this: First, download and copy the content from the 3D &hellip;<\/p>\n<div class=\"cta1\"><a href=\"https:\/\/www.osd.net\/blog\/web-development\/3d-board-game-in-a-browser-using-webgl-and-three-js-part-2\/\">Read more<\/a><\/div>\n<div class=\"like-excerpt\"><div\n        class=\"fb-like\"\n        data-href=\"https:\/\/www.osd.net\/blog\/web-development\/3d-board-game-in-a-browser-using-webgl-and-three-js-part-2\/\"\n        data-layout=\"button_count\"\n        data-action=\"like\"\n        data-show-faces=\"false\"\n        data-share=\"false\">\n        <\/div><\/div>","protected":false},"author":4,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[18,13],"tags":[122,121,120],"_links":{"self":[{"href":"https:\/\/www.osd.net\/blog\/wp-json\/wp\/v2\/posts\/1085"}],"collection":[{"href":"https:\/\/www.osd.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.osd.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.osd.net\/blog\/wp-json\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/www.osd.net\/blog\/wp-json\/wp\/v2\/comments?post=1085"}],"version-history":[{"count":5,"href":"https:\/\/www.osd.net\/blog\/wp-json\/wp\/v2\/posts\/1085\/revisions"}],"predecessor-version":[{"id":1098,"href":"https:\/\/www.osd.net\/blog\/wp-json\/wp\/v2\/posts\/1085\/revisions\/1098"}],"wp:attachment":[{"href":"https:\/\/www.osd.net\/blog\/wp-json\/wp\/v2\/media?parent=1085"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.osd.net\/blog\/wp-json\/wp\/v2\/categories?post=1085"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.osd.net\/blog\/wp-json\/wp\/v2\/tags?post=1085"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}