Changing the pieces with my own set

Get ready


Please setup your starting environment configuration as explained in our example My basic chess starter kit

You should now be able to run a regular chess version and have basic mode/restart/take-back/fullscreen controls and view options.

Steady
In this tutorial we will replace default Staunton Jocly library pieces by new Nishapur pieces that will be located on your server. Nishapur is a persian city (Iran) where some of the oldest chess sets have been found.

For our Nishapur set we need:
 * sprites for the 2D skin
 * 3D files for the 3D skin objects

This is just a basic tutorial to show the main principles. For 3D, we will load simple meshes in the game and just set colors and a few material parameters, nothing complicated (no texture or UV edition involved). So please don’t expect to achieve high quality pieces. Not yet ;)

2D skin
The 2D skins uses one single file including all pieces sprites. We will provide this file to Jocly with the clipping info, it means the size and the position of the portion of bitmap to use for each piece.

We won’t go into details for the making of this file, I just made a quick version for this example. It generally looks better if we use masks to draw pieces with transparent backgrounds, you can use any software like Gimp for this. PNG is a good file format for bitmaps with alpha channel to be used in web pages.

Our image is a 600x200 png file with a 100x100 pixels clipping. Pieces are placed in the same order in the 2 rows corresponding to the 2 opponents (black and white).



Tools
For a few 3D editing we will use the free and open source software Blender from http://www.blender.org/.

Download and install it if you don’t have it.

The 3D skins of jocly use Threejs library for WebGL rendering. We need to create 3D objects that can be loaded by this library. That's why we need to add a Threejs exporter to blender once installed. You can find this exporter on the Threejs GitHub repository.

You won’t need special 3D graphics skills, but let’s just remind some basic vocabulary that might help.

In a 3D world, we can say an object is defined by
 * points we call vertices (one vertex, two vertices)
 * lines joining these points we call edges
 * and faces which have edges as borders and vertices as extremities.

You can edit any of these three components of your object to modify it (position, scale, rotation). The whole thing is called a mesh.



Workflow
We choose to not create the pieces but find free pieces shared by someone

Things to be done:
 * Find 3D files
 * import 3D files in blender
 * edit a few little things in blender
 * export pieces one by one to separate ThreeJS files
 * import pieces in the chess game
 * play

The first thing to do is to find 3D meshes of the Nishapur set. There are a lot of sites sharing 3D files. Today we will use a free set by Mark Durbin originally made for 3D printing but which will perfectly suit our needs. You can get it here https://www.thingiverse.com/thing:19961



Choose the polysoup.stl file and save it.

Open Blender, delete the cube (right click on it to select it, then press x to delete) and from the file menu choose import > Stl (.stl)

Select the polysoup.stl file you just downloaded.

The dimensions of the object are very big in the scene, if what you see looks strange, you might be inside the object: use the mouse wheel to zoom out, hold the middle button down to rotate the view.

In the end your screen should show something like this.



As you can see you have in your scene one object made of 6 pieces. We will have to split these set into 6 different pieces.


 * if not selected, select the object (right button click)
 * resize the object to make it bit smaller in the scene, something that is not bigger than the grid plane (S => move the mouse => left button click when done)
 * enter the Edit mode (tab)

Now we are in “object mode” edition, it means we can only edit vertices, edges and faces of the mesh. We are like trapped in the selected object’s world.

For each piece we will:
 * select all the vertices of the piece
 * detach them from the main object so it can have its own life (and become a new separated object).

Let’s go:


 * switch to orthographic view (= no perspective) (Numpad 5).
 * go to front view (Numpad 1)
 * unselect all vertices (A)

You should have something like this



If not well centered in the view, use the mouse wheel to zoom in/zoom out, and [Shift+middle-click+drag] to pan the view.

Make sure you unchecked “limit selection to visible”, we want to select every vertices of the piece, visible or not.



If necessary use A to be sure nothing is selected (it selects/unselects all)

Let’s start with the pawn on the right.


 * press B to start a square selection.
 * Click left button and start dragging until you surrounded the entire pawn. When you release the left button, everything should be select.

If not retry by first unselecting all (A) and restart selection (B)

When you’re done (the entire pawn is orange), press P to separate, select ‘selection’.



As you can see the pawn is no longer a part of our world, you can not see its vertices anymore because we are in the object made of the 5 remaining pieces, it has been separated from us. Don’t be too sad, we’ll get back to him very soon.

Do the same thing for every piece: Unselect all (A), starts selection (B), separate (P) by selection.

When the 6 pieces have been separated, you have to leave the edit mode of this empty object, nothing remains here. Press tab.

To work on each piece, we will put them in different layers.


 * use A to unselect all.
 * select one piece with a right click button. Let’s start with the pawn.
 * press M. An array of available layers appear, choose the first left of the bottom row.

Yes, the piece disappeared. Your view displays all the selected layers (see at the bottom of the 3d view). The pawn is gone to a layer which is not selected. If you want to make it visible, select the layer you just put it in. You can select several layers to be displayed by using Shift.



Let’s go back to first layer. Do the same for all pieces. Select (right click), move to an empty layer (M).

Before exporting the mesh of one piece, we need to center it. Imagine your piece must stand centered on the the point (0,0,0).

For each piece:
 * select its layer to see it
 * use N to display if not already visible the Transform panel on the right of the view
 * select the piece
 * make sure the position of the object is (0,0,0). Click on it to change it if necessary.



Now the object is centered, but not the piece.
 * enter the Edit mode (tab). We will move the vertices so that the piece is centered in its container object.
 * select all meshes (A)
 * in location panel, set X to 0.



Your piece should be centered.

Quit the Edit Mode to go back to Object Mode (tab)

Do the same for the 6 pieces.

Once everything is ok, we just need to export these brand new pieces to Threejs files.

For each piece:
 * select its containing layer to see it (be sure to be in object mode to see the layers controls in the bottom view menu).
 * select your object (right click)
 * go the the main menu File > Export > Threejs (.js)

You will be asked for a place on your computer to save this file but first look on the left panel of this dialog box and scroll it down to reach ThreeJS export parameters. Only select Vertices, Faces, Normals, Embed meshes and Flip YZ options. Make sure “All meshes” is unchecked (it would export all objects in a same file)



You can choose a sub folder to store your 6 files:
 * np-pawn.js
 * nb-rook.js
 * nb-knight.js
 * np-firs.js (firs or elephant instead of bishop)
 * np-vizier.js (vizier instead of queen)
 * np-king.js

A bit of coding
At this point, you should have in the directory of your cutsom game on your server (or local machine):
 * the 3 files of the "My basic chess starter kit"
 * one file for 2D skin pieces (sprites)
 * 6 js files (3D pieces)

I have created a 'res' directory which includes the 'pieces' resources sub-directory



We are now going to make all these things alive.

Our custom game is based on classic chess so go to the Jocly game inspector, search for Basic Chess and download the file basic-view.js, save it to your development directory under the name np-custom-view.js

In our example, we will only change the view, not the model of the game. We choose nishapur-custom as machine name for the game. We will have to provide the list of JS files used in this game.

Edit the dev-stub.html file to add this section between the &lt;head&gt; and &lt;/head&gt; tags:  {       "view": { "js": ["base-view.js", "grid-board-view.js", "staunton-set-view.js", "np-custom-view.js"] }   } The game is based on the basic-chess game, that is the reason of the data-jocly-game="basic-chess/nishapur-custom"

base-view.js, grid-board-view.js and staunton-set-view.js are necessary files located on the server, np-custom-view.js is on your side so you will have to specifiy its key name. We will use the same name as a key: in dev-stub.html, under the last insertion, add this section:  {       "np-custom-view.js": "np-custom-view.js" } now search for localPlay in this file and replace the line with $("#applet").jocly("localPlay","nishapur-custom",{ });

This will tell the jocly plugin to load the nishapur-custom game.

At this point, you should be able to reload your web page and see that... nothing has changed :)

You actually changed something: most of the script files are properly loaded from the remote server, but a part of the code now comes from your 'local' file, np-custom-view.js, where we will be able to insert the modifications we want.

In dev-stub.js, let's add key names for all our 'local' resources.

Replace  {       "np-custom-view.js": "np-custom-view.js" } by     {       "np-custom-view.js": "np-custom-view.js", "image|nishapur-2d-sprites.png": "res/pieces/nishapur-2d-sprites.png", "smoothedfilegeo|0|np-pawn.js": "res/pieces/np-pawn.js", "smoothedfilegeo|0|np-knight.js": "res/pieces/np-knight.js", "smoothedfilegeo|0|np-rook.js": "res/pieces/np-rook.js", "smoothedfilegeo|0|np-vizier.js": "res/pieces/np-vizier.js", "smoothedfilegeo|0|np-fils.js": "res/pieces/np-fils.js", "smoothedfilegeo|0|np-king.js": "res/pieces/np-king.js" } Key names are needed by jocly plugin and might seem a bit complicated for resources. The thing to remember for each line is:
 * the part before the last "|" is a resource type prefix (pink)
 * what follows after the last "|" will be the actual key used later in the code to invoke the resource (blue)
 * the second string after ":" is the local relative path to the resource (green)



Everything is now properly declared, let's change these pieces.

It's time for editing our np-custom-view.js file. We will add big parts of code, for more details, a commented version of the finished file is available at the end of the tutorial.

The View.Game.cbDefineView function defines boards and pieces. In the object returned by this function you will find a "pieces" section. In basic chess, we use a Staunton jocly library called cbStauntonPieceStyle. Some sizing parameters are added for fine tuning for both 2D and 3D skins.

pieces: this.cbStauntonPieceStyle({				"default": {					"2d": {						width: 1200,						height: 1200,											},					"3d": {						scale: [.6,.6,.6],					},				},			}),

We will now define new pieces specifications for our new Nishapur set.

Before the View.Game.cbDefineView, add the following code:

View.Game.cbNishapurPieceStyle = function(modifier) { return $.extend(true,{			"1": {				"default": {					"2d": {						clipy: 0,					},					"3d":{						rotate: 180,					}				},			},			"-1": {				"default": {					"2d": {						clipy: 100,					},					"3d":{						rotate: 0,					}				},			},			"default": {				"3d": {					display: this.cbDisplayPieceFn(this.cbNishapurPieceStyle3D),					flatShading: true,				},				"2d": {					file: this.mViewOptions.fullPath + "nishapur-2d-sprites.png",					clipwidth: 100,					clipheight: 100,				},			},			'pawn': {				"2d": {					clipx: 0,				},			},						'knight': {				"2d": {					clipx: 500,				},			},						'bishop': {				"2d": {					clipx: 200,				},			},						'rook': {				"2d": {					clipx: 100,				},			},						'king': {				"2d": {					clipx: 300,				},			},						'queen': {				"2d": {					clipx: 400,				},			},		},modifier); }	View.Game.cbNishapurPieceStyle3D = $.extend(true,{},View.Game.cbPhongPieceStyle3D,{       'pawn': {			'mesh': {				jsFile:"np-pawn.js",			},                    },        'king': {			'mesh': {				jsFile:"np-king.js",			},                    },        'queen': {			'mesh': {				jsFile:"np-vizier.js",			},                    },        'bishop': {			'mesh': {				jsFile:"np-fils.js",			},                    },        'knight': {			'mesh': {				jsFile:"np-knight.js",			},                    },        'rook': {			'mesh': {				jsFile:"np-rook.js",			},                    },

});

Now our new pieces set is defined, in View.Game.cbDefineView function search the pieces member to use this.cbNishapurPieceStyle instead of staunton set: pieces: this.cbNishapurPieceStyle({				"default": {					"2d": {						width: 1200,						height: 1200,											},					"3d": {						scale: [.6,.6,.6],					},				},			}),

That's what you should get now:



We loaded the pieces in the game. We need to adjust the size and set colors for each opponent.

For the size, in View.Game.cbDefineView change the pieces "3d" scale from [.6, .6, .6] to [.3, .3, .3]

pieces: this.cbNishapurPieceStyle({				"default": {					"2d": {						width: 1200,						height: 1200,											},					"3d": {						scale: [.3,.3,.3],					},				},			}),

For the color, we will change it in the View.Game.cbNishapurPieceStyle3D definition. At the begining before 'pawn' section add:

'1':{         'default':{ phongProperties: { color: "#00bb00", shininess: 200, specular: "#ff0000", },           }        },        '-1':{          'default':{ phongProperties: { color: "#0000bb", shininess: 10, specular: "#888888", },           }        },

"1" means white, "-1" means black, the parameters we just changed are Phong material properties as defined in Threejs library. See MeshPhongMaterial

You should now have a fully functional chess with your imported pieces.



In the options panel on the right choose the 2D skin to check everything is in place.

]

Completed tutorial
The result of this tutorial is available in this archive:.

Annotated custom view script
I added a few comments in the the final version of the np-custom-view.js file included in the archive. Hope it helps.

Here is this final version:

(function {   // definition of our nishapur set	View.Game.cbNishapurPieceStyle = function(modifier) {		return $.extend(true,{ "1": { // will be applied apply to white opponent "default": { "2d": { // in our 2D skin sprites bitmap, white pieces are in the first row which starts at y=0 clipy: 0, },					"3d":{ // puts the 3D pieces in the correct direction for white (z axis rotation, in degrees) rotate: 180, }				},			},			"-1": { // will be applied apply to black opponent "default": { "2d": { // in our 2D skin sprites bitmap, white pieces are in the first row which starts at y=0 clipy: 100, },					"3d":{ // puts the 3D pieces in the correct direction for black (z axis rotation, in degrees) rotate: 0, }				},			},			"default": { // for all pieces "3d": { display: this.cbDisplayPieceFn(this.cbNishapurPieceStyle3D), flatShading: true, },				"2d": { file: this.mViewOptions.fullPath + "nishapur-2d-sprites.png", clipwidth: 100, // clipping width in 2D skin sprites bitmap clipheight: 100, // clipping width in 2D skin sprites bitmap },			},			'pawn': { "2d": { clipx: 0, // pawn clipping start in 2D skin sprites bitmap (for both black and white) },			},						'knight': { "2d": { clipx: 500, // knight clipping start in 2D skin sprites bitmap (for both black and white) },			},						'bishop': { "2d": { clipx: 200, // bishop clipping start in 2D skin sprites bitmap (for both black and white) },			},						'rook': { "2d": { clipx: 100, // rook clipping start in 2D skin sprites bitmap (for both black and white) },			},						'king': { "2d": { clipx: 300, // king clipping start in 2D skin sprites bitmap (for both black and white) },			},						'queen': { "2d": { clipx: 400, // queen clipping start in 2D skin sprites bitmap (for both black and white) },			},		},modifier);	}   // 3D pieces definitions	View.Game.cbNishapurPieceStyle3D = $.extend(true,{},View.Game.cbPhongPieceStyle3D,{ '1':{         'default':{ // default params for white phongProperties: { color: "#00bb00", shininess: 200, specular: "#ff0000", },           }        },        '-1':{          'default':{ // default params for black phongProperties: { color: "#0000bb", shininess: 10, specular: "#888888", },           }        },        // meshes for each piece. That's were we use our key names defined in dev-stub head section. 'pawn': { 'mesh': { jsFile:"np-pawn.js", },                   },        'king': { 'mesh': { jsFile:"np-king.js", },                   },        'queen': { 'mesh': { jsFile:"np-vizier.js", },                   },        'bishop': { 'mesh': { jsFile:"np-fils.js", },                   },        'knight': { 'mesh': { jsFile:"np-knight.js", },                   },        'rook': { 'mesh': { jsFile:"np-rook.js", },                   },

});	View.Game.cbDefineView = function {		return {			coords: {				"2d": this.cbGridBoard.coordsFn.call(this,this.cbGridBoardClassic2DMargin),				"3d": this.cbGridBoard.coordsFn.call(this,this.cbGridBoardClassic3DMargin),			},			boardLayout: [	     		".#.#.#.#",	     		"#.#.#.#.",	     		".#.#.#.#",	     		"#.#.#.#.",	     		".#.#.#.#",	     		"#.#.#.#.",	     		".#.#.#.#",	     		"#.#.#.#.",							],			board: {				"2d": {					draw: this.cbDrawBoardFn(this.cbGridBoardClassic2DMargin),														},				"3d": {					display: this.cbDisplayBoardFn(this.cbGridBoardClassic3DMargin),									},			},			clicker: {				"2d": {					width: 1300,					height: 1300,				},				"3d": {					scale: [.9,.9,.9],				},			},			pieces: this.cbNishapurPieceStyle({ "default": { "2d": { width: 1200, height: 1200, },					"3d": { scale: [.3,.3,.3], },				},			}),		};	}

/* Make the knight jump when moving */ View.Board.cbMoveMidZ = function(aGame,aMove,zFrom,zTo) { if(aMove.a=='N') return Math.max(zFrom,zTo)+1500; else return (zFrom+zTo)/2; } });