Until the 4th part of the articles, we have finished our terrain editing feature of our world editor. We can alter the shape of our terrain as we want. We can also paint the texture to our terrain to make it looked more realistic. But we haven’t been able to put another objects on our terrain. For example we want to put some characters, buildings, or maybe some trees. This article will cover that. I think this part is not to hard to follow since you got the basic already. I will tell you the basics you need to put your models onto the terrain, but you should think of how to manage every models you have on your terrain properly by yourself.
Before I begin to explain how to put the models onto the terrain, I will start with the basic explanation with 3D model. First of all, jME is one of many engines that support many kind of model format. The other engines usually can load several different 3D file formats, and jME is equipped with many loaders that we can choose. From those many formats, I think the most reliable one is ogrexml. It is because some features that I can’t find in the other format loader. For example, to control the animation, ogrexml can use the animation name instead of the starting and ending frame. It can also support animation blending. Ogrexml can also be exported from some 3D modeling application such as 3dsmax and blender (with additional plug-in). I think the jME developer will also agree with this because they use ogrexml as their only supported model format in jME 3. If I’m not wrong, jME 3 also support wavefront obj, but it’s only for static object. For that reason, I will only use ogrexml file format in my world editor.
When the 3D scene is exported into ogrexml format, it usually divided into some parts. There’s a file to contain the scene information (.scene). There’re files to contain the mesh data (.mesh.xml) and the skeleton data if the model has skeleton (.skeleton.xml). The three of them are in xml format, so we can read and edit it without any editor (of course we won’t do that). Then the last one is a file containing material information which is not in xml format but still readable (.material). To load them into our jME application we have two options. First is we can load the .scene file only and the second one is we can load the .mesh.xml and .material files separately. We will never need to load .skeleton file as it will be loaded when we load the .mesh file. I choose the first method for my world editor. It means I won’t need to store 2 file paths (mesh and material) to load the model. I only need to know the path for scene file.
If you use ogremax (3dsmax plug-in to export the scene into ogrexml format), you will need to add something into the scene file. It seems when exporting using ogremax, the ogrexml version is different than the one jME use or maybe there are some options in ogremax that I have to change(it’s only my thought though). This is the original .scene file exported by ogremax
Then I also modify the material file. The picture below is the original one. I delete the settings for ambient, diffuse, and specular. Those settings will affect the lightning state in our jME application. Deleting them would make no change to our lightning state.
Loading 3D Models
Since we will only use ogrexml, it is easier to load the objects. I made a static method to load the ogrexml scene and return it as a Node. This is the code.
Placing the Objects
If you have ever done flagrush tutorial on the jME website, there is a part which explain about how to place the object exactly on the terrain. It means no matter how high the terrain is on some point, the object will always be placed on the terrain. That tutorial also explains how we place the object to follow the tilt of the ground. In my world editor, I only allow my object to move in 2D plane, thus are xz-plane. It means, for the movement in y-axis I move it automatically to the terrain height. Of course this kind of movement is lack of flexibility. If the user wants to make the object floating or planted on the ground, they have to change the model’s pivot position manually. But everything will depend on the game you want to create with this world editor. My game won’t allow any object to be placed not on the ground, so there’s no point to make it able to move on y-axis.
You can also choose your own way to move the objects. You can use the help of 3D xyz-axes to move the object (similar to many 3D authoring tools) or just drag the object around to move it. In my case, I will choose to drag the object to move it. I don’t need 3D xyz-axes since my object can only move on xy-plane. It is also harder to implement since we have to know which axis the user click and then where the user drag the axis. But if we drag the object directly, we can reduce some unneeded operations. You can choose one that suits your need.
If you choose to drag the object directly, you only have to know which object do you click. Then do a little mouse picking to the terrain, and move the clicked object to that position. It should be very easy to do.
If you use 3D xyz axes, first you have to create the axis when you click the object. Then you have to check which axis the user click. Let’s says the user click on the x-axis. Then we have to check how far the user drags the mouse around. Here we have to decide which mouse coordinate will affect the object movement. For example for the x-axis, is the only one that affects the movement is the x coordinate movement of the mouse? Of course we also have to decide how it will affect he object. For example if the mouse is dragged to the right, usually the object will also move to the right or translated by a positive number along the x-axis. But how about if we look the object from different angle, let’s says from behind, is dragging the mouse to the right still move the object to the right? Of course it shouldn’t, or else it will look weird. You have to take all of them for more consideration.
Rotating the Objects
For this type of transformation, you have to decide which axis you will use to rotate the object. For almost every 3D authoring tools, they can use all three axes to rotate the object. But of course it is more complicated to make than using only one axis to rotate. For my world editor, I only use y-axis as the rotation pivot for my object. It means my object can only be rotated to the left or right. My game doesn’t need the objects to be rotated in the other ways. To rotate it around y-axis, I pick the clicked object, then calculating how far the mouse dragged to the left or right. Then I convert that distance into the rotating angle. Finally, I rotate the object.
If you use all three axes to rotate the object, you will find the same problem as in translating the object. You need to take the viewer position and angle into the consideration when rotating the object.
Scaling the Objects
To scale the object, you also have to decide whether you want uniform scaling or non uniform scaling. I prefer uniform scaling as it is easier to implement (the reasons are similar to translating and rotating the object) and also, doing non uniform scaling will create undesired effect. The texture maybe distorted because we can’t dynamically change the texture uv coordinate. For scaling, I will pick the clicked object and then calculating how far the mouse is dragged as the scaling ratio. It is not too different from rotating.
Trees are important elements in the scene. But it is very hard to make it looked real but only needs very little processing power to render into the scene. Some games use billboard node for their trees. But sometime it won’t work for some cases. Some other games use a low poly model for the trees. Of course it won’t be looked very real. But for my game, I think a low poly model should be enough. So I will use that one. To place it on our terrain, you can use the same way as the other object or you can use scattering brush to place it randomly.
Those are the basic to place the objects on our terrain. There are still many things that you can add to your scene. You can add water surface to your scene. You can also give skybox or something else to cover the sky. You may also let the user to change the direction, color, and intensity of the lightning. There are still many rooms for improvement to make you world editor more powerful and flexible. You may want to make your world editor as generic as possible.
At the last article, we have our terrain deformed by using the tools we have created. Then if we look back at the second part, we have prepared the terrain so it can have multiple detail texture layer with their alpha map. But we ony have one texture for our terrain now. In this article I will explain how to add more detail textures and then paint the terrain with the textures. I need to remind you this part is the hard one. It will need you to modify the existing TerrainPass to make it work. You will also need to maintain the list of texture layers which is quite complicated. As in the second article, maybe I won’t tell you much about the classes design. You can always design your classes as you want, just make sure they can be used properly.
More importantly, this article is the hard one but not that hard. It is hard for me because when I created that, I have nothing to guide or to tell me about it. I have to think and do some experiments to find how to do things I present in this article. But I’m sure it will be easier for you because I will tell you everything you need to add to make this work. If you have some difficulty following this article, feel free to ask me.
Adding New Tools
This is the easiest part of this article. We only need to add more tools to support us. For my world editor, I add a JList for showing the existing texture layer on my terrain, two buttons to add and remove texture layer, two toggle buttons for paint and erase action. I will also add two sliders for the brush radius and for the texture scale. And before I forget, for the sliders, you can always change them to textfield or spinner for more precise number or you can combine them. Do what you what to make it better. Here is my user interface
Modifying Existing Code
First of all, we need to modify the existing TerrainPass code. Because we will enable the texture editing feature of our world editor, we need the TerrainPass to be able to recalculate the texture layer, removing detail texture, and also changing the texture’s scale. The existing TerrainPass doesn’t have those functionality, or at least I can’t find them. So we have to add new methods to the TerrainPass to make it work.
First we need a method to refresh the terrain’s texture layer. The actual TerrainPass only generate the texture information once, that is the first time it is rendered. So if we make some changes to the texture, we won’t see the result immadiately unless we make the TerrainPass to regenerate the texture information. Here is the code snippet that we have to add in TerrainPass class.
We need to clear the existing passList inside TerrainPass because when TerrainPass render the scene, it will check whether the passList is empty or not. If it is empty, TerrainPass will generate the texture information.
The next method to add is to remove the detail texture layer. The actual TerrainPass has a method to add the detail, but has no method to remove it. So we have to add it. The detail texture that I talk about are not included the base texture (we add it by passing null alpha map to addDetail() method). The base texture can’t be removed cause it will create an error. I have attempted to remove base texture then make the next layer became base layer. It works but it isn’t rendered properly. So I will only remove the detail layer. Here is the code.
We also have to call refresh() at the end of addDetail() method from TerrainPass so it will refresh the texture each time a new layer is added.
The next one is to rescale the texture layer. When we add a detail layer, we can also specify the scale of the layer, that is how many times the texture will be tiled over the terrain. If we only use one scale for every layer, we will find that some textures may not appear as we want. It may to small or to big. So we need a way to rescale the layer after we add the detail. Here is the code snippet.
But it won’t work immediately. I found that at addLayer() method in SplatEnv, every time a layer is added, the scale is set to default scale. I don’t know why they make it like that. So I have to remove some lines of code.
Maybe that’s all of the modification we have to do. Now lets move to the next part.
Adding and Removing Texture Layer
This seems to be an easy task. But it can become complicated. If you look at the other applications that use more than one layer, for example Photoshop or Flash, you will find that the top layer usually drawn at the top. No other layers will cover the top layer. If we want to create that texture list with JList, we have to put the top layer at the first index at the list. But the layer list in the TerrainPass works differently. The top layer will be at the last index of the list, not the first one. So we have to maintain two different list, one at the TerrainPass and the other for the JList. Sometimes it can cause confusion. It is better to be careful when accessing the each list.
Adding a new texture layer consist of some steps. First we have to create a new image for the new alpha map. You can create it with BufferedImage and draw a fully transparent rectangle all over the new image. Then you can store all of the needed information (For me, I store the path of detail texture, the path of newly created alpha map, and the scale of the layer). Finally, you can call addDetail() method from TerrainPass. Your new texture should be visible on the terrain. The truth is you can’t actually see the texture because you make a transparent alpha map for the new texture. But you can change the alpha map transparency to see the new texture layer.
As for removing the layer, we can do the reverse version of adding new layer. We have to remove the layer information which we store when we add new layer. Optionally, we can also delete the alpha map file. Then remove the detail from TerrainPass by using the new method we create above, removeDetail(). Make sure you use correct index when adding and removing the layer.
I can’t show you any screenshot of what the result would looked like because at this point the new layers aren’t visible to out eyes. But you can check if you have properly doing this by drawing something opaque on each alpha map. Make sure you do everything alright before you move to the next part.
But before we move to the next part, maybe you can try to add more than 4 layers (this includes base layer). There’s something strange when you add the fifth layer. You will see then terrain color will change drastically. Actually it’s not the color that is changing, but the lightning. I don’t know why but the texture on the first until fourth layer don’t receive any light anymore. But the fifth layer and the next layers after that will meet no problem. This thing isn’t happening if we use lightmap (which we don’t). For now, I will assume four layers of texture are enough to make a good looking terrain.
Painting the Textures
This part works in similar way as deforming the terrain. Actually, I use the same method that we created at the previous article. I use mouseClick() method to call the painting method. I believe you still remember the method, if you have forgotten about it, try to re-read the article.
The process of painting the texture and deforming the terrain are similar. The difference is when we deform the terrain, we modify the height map value of the terrain, but when we paint the texture, we modify the alpha map transparency. But what we have is the Instances of jME Texture, not BufferedImage. So we can’t easily draw new pixels to them. Fortunately, we can obtain the texture image data in jME’s Image class format. That class has some ways to change the pixel data values.
The first statement in that method is just to get the alpha map that we want to change. So you can use your own way to get the required texture. As for the next part, we take the image data from the texture. Actually it is in the form of ByteBuffer. To navigate the pixel data, we can use position() method. Then after that, we have to put the color value one by one. put() method will put the value into the buffer position and then moving to the next byte. After that the position must be moved back to the first position.
We can call the method above from the mouseClick() method. It is just like terrain deformation. If you try that, you won’t find the difference on the terrain, the texture doesn’t change at all. This is because we only change the actual texture data. jME mainly uses LWJGL as the renderer. LWJGL maintains the textures that are rendered to the screen. So we also need to update the texture data maintained by LWJGL. We can do that by using this method.
We can call that method after we finish the painting loop. You have to remember that this method is an expensive one, so make sure you only call it if you need to update the texture.
For erasing the texture, we can use changeTexturePixel() method. Just give 0 to its value. It will create transparent pixel on the alpha map.
That’s it. With that you should be able to paint you terrain. This is the example of the painted terrain that I made. I gave them some types of sand as the texture.
At the last article, we already have our terrain rendered at out application. We can create some different types of terrain. In my application, I provide blank (flat) height map, hill height map, fractal height map, and fluid simulation height map. Of course we can’t just use the proceduraly generated height map to create our game world, although it looks quite nice and natural. We will want to have more control of where the ground is high, where the ground is flat, where the ground create a mountain, or create a small bump. In this article, I will explain how to make the tools that we created before work on our terrain.
And before I forget, the terrains that I created on the previous tutorial don’t have holes on their sides. Usually, terrains will have holes on their sides so we can see through their botton from their sides (not from below). It is because I modify the height map value after it is generated. I make that so every side or terrain edge has zero height.
Now we will start with the tools. But as always, I assume you have already done with the previous article.
It common for 3D application to have mouse picking. It is the action of detecting what object the mouse is clicking in the 3D world. I won’t give you detail explanation on how to do mouse picking, you can find it on the jME website. The difference is while in the ordinary jME mouse picking use the jME mouse handler, our world editor will use AWT mouse adapter (we have created one in the first article). The truth is we have used mouse picking at the second article where we create our brush, but looks like I forgot to mention it. From the AWT mouse handler, we can call a method from our implementor class and send the mouse coordinate to that method. That method will do the mouse picking and then do some action (remove and create brush).
Left Mouse Click Action
In the case of the left mouse button is clicked, we will call another method which will also do the mouse picking but with different action. We will do some distance checking to find which vertex will be affected. For that, I have to check the distance of every vertex on the terrain with the position where our mouse is clicking. But I won’t use the actual distance. I ignore the y-axis so it means I assume the vertex and the mouse picked position are on the 2 dimentional space.
After getting the distance, I will check it with my brush radius. If the distance smaller than the brush radius (the vertex is inside the brush circle), I will use brush full strength to alter the the vertex position. If the distance is bigger than the brush radius but still smaller than the falloff radius, I will scale down the brush strength according to the distance, the farther, the smaller it becomes. It will create a nice interpolation on our terrain. You can choose to do linear interpolation or using some kind of formula to create non linear interpolation. What you have to remember is we want to get the distance of the vertex and the position of the vertex on the height map(the index of the vertex). We don’t need the actual position of the vertex on the terrain as we don’t need it to alter the terrain. For that, I have to scale down the actual vertex position by the terrain step scale. This is my code outline
Actually, I have to admit that my code is not efficient. But that’s what I got when I was creating it. I have no time yet to optimize it. But you can try to optimize it by yourself as long you can achieve the same result. Before I do any action, you can see in the code that I calculate the scale for the brush. I only use linear interpolation because it’s easier and faster to calculate (to compensate my other inefficient codes).
That method also receive ‘mode’. That is a variable to distinguish our action, whether it is raising terrain, lowering terrain, smoothing terrain, or the other action. I use bit mask for that variable because in my application I need it to have more than one modes at a time.
Then after we apply the brush change (after the loop), I write three statements. The first one is to update the terrain vertices from the height map. It is needed because what we actually modify is the height map, not the terrain’s vertices. The second statement will fix the normal vectors of every terrain vertex. If you don’t do that, you will see seams on the terrain after you alter it as you can see on the picture below. The third statement will update the collision tree for the terrain so it will act properly when we do mouse picking.
Now let’s do the action. I will explain six actions, raise, lower, flatten, smooth, noise, and set height.
Raise & Lower
Raise action and lower action are pretty much the same action. The only difference is in raise action we add the height of the terrain and in lower action we substract the height. Because of that, I only make one method to do both of them. It’s very simple, I only take the brush strength and multiply it with the scale of brush strength that we calculated based on vertex distance. Then add the result to the height map. If I want the terrain to lower, then I just have to pass the negative form of the scale.
Flatten & Set Height
These action are a little bit more complex than the previous one. The two of them are also very similar. They try to adjust the the height until some predefined height. The difference is flatten action will try to adjust the height to the heigth of the clicked terrain and set height will adjust it to an exact height defined by user. The action also need to consider if the vertex is higher than the target height, it has to reduce the height and vice versa. When I set the terrain height, I use brush strength as the target height and I use the clicked terrain as the target height when I flatten the terrain.
For this action, I think not to different with raising and lowering the terrain. The code is pretty much the same. The difference is when we raise or lower the terrain, we use brush strength value to add or substract the current height, but in noise action I use random number between 0 and brush strength – 1 to add the current height. It gives a little bumps when the brush strength value is small, but it creates a spike-like terrain when the value is big. You can also use another noise algorithm to create a more realistic noise.
This is the last and most complex action that I will explain in this article. I don’t know if this is the right method to smooth the terrain. But as far as I know, in image processing, smoothing process is implemented by blurring the image. I think our terrain can be assumed as an image (actually, the height map itself is a grayscale image that contain the height data of every vertex on the terrain). So I think we can accomplish smoothing on our terrain by blurring the terrain. If you search the blurring algorithm, you will find that to blur an image, we have to convolve the image with a matrix. This matrix can be in any size as long as it is odd number. But you have to remember the bigger the matrix is then the longer it will take to calculate it. So in this case, I only use 3×3 matrix. Then the value of each element of the matrix can be in any value. But a different value can lead to a different result. This algorithm can also be used to do sharpening, edge detection, or other image processing algorithm. For my application, I will use gaussian blur which use matrix calculated from gaussian formula. You can also find another matrix to use to get the similar result.
We can call all of those methods from the mouseClick() method that I write above. Of course we have to check the mode we are in at the time to decide what method we have to call. For short, it will be like this
Those are all the basic terrain deformation techniques that I can tell you right now. As I said before, I can’t guarantee the codes above will run efficiently. You can always try to optimize the code by yourself. Also, if you see the other existing world editor, maybe you can find another deformation techniques which I haven’t explained in this article. You can also try to figure out how they work and then implement them to your world editor. Actually I do have planned to add a tool to create a ramp. But I just don’t have time to do that yet.
For the next article I will explain about how we change the texture of our terrain, or maybe I should say how we paint it. I think it will be the most complicated work that I have done for this world editor. It’s because the TerrainPass itself still has the weaknesses (or maybe it’s just me who lack the required knowledge to do what I want).
© William Salim – WS08-1 – Software Laboratory Center
From the last article, we have created a user interface for our world editor. It can handle simple camera movements such as pan, rotate, dan zoom. In this article, I will cover about how we set up the terrain and prepare some tools that will be used to edit the terrain. I won’t give a lot of code snippet here because the code in this article will depend on your own code style and design. I assume you have already done with the previous article.
You may wonder why I write an article for just creating a terrain while it’s already covered in the jME flagrush tutorial. It’s because we will use a slightly different method to create our terrain. Unlike the terrain explained in the flagrush tutorial, our terrain need more flexibility. It has to be able to be edited while we run the world editor. The texture should be able to be edited as well. This flexibility can’t be achieved with the method explained in flagrush tutorial because it can only create an uneditable terrain.
To get the editable terrain, we will use TerrainPass. Actually, it’s only used for the texture. So, what is TerrainPass? jMonkeyEngine can use several kind of pass rendering to achieve some kinds of rendering effects such as bloom, shadow, motion blur, sketch, etc. TerrainPass is a pass renderer which allow us to create texture splatting effect on the terrain. With TerrainPass, we can give our terrain some layers of detail texture and specify which part of the layer will be displayed, while with ordinary renderer, we can only have one layer and it will cover entire terrain. So TerrainPass has everything we need to make our terrain.
First of all, you should get your hands on the source code of TerrainPass. It isn’t a part of jME stable release. But you can get it from http://mfkarpg.110mb.com/jme/TerrainPass-jME2.zip. It comes with a test application, so you can see the example code from that. After you get the source code, you can copy all the files into your project directory. You may get some errors in the file CXTerrainPass.java (I do), if you do, you can just delete the file as we won’t use that class. Unfortunately, the existing TerrainPass doesn’t has all the methods we need. So you have two options, you can extend TerrainPass into your own class then add the new methods in your class or you can just directly modify the existing TerrainPass. In my case, since I have the source code, I prefer to modify the the source code. You can choose either that suits your style. But in this article, I won’t add anything to the TerrainPass as we don’t need it yet.
Now we go back to our previous code. If you see our implementor class, it is extended from SimplePassCanvasImpl. As I have explained before, it behaves like SimplePassGame which manages the renderer to draw from some passes.
I will now explain how to setup the terrain by using TerrainPass. But before I explain further, I would suggest you to encapsulate all your scene data into a class, especially everything that you can change dynamically from the world editor. They include but not limited to terrain, textures, static objects, animated objects, lights, etc. For the terrain mesh, you can make it like what you usually do. You can use any type of height map provided by jME to create you initial terrain. I provide some type of height map for user to use. I also let the user to choose the size of the terrain. I suggest you to use TerrainPage because we may be dealing with a large terrain. TerrainPage also allows us to change the individual height map value at a certain position (In TerrainBlock, we need to reset the entire height map to change it).
Because we want our terrain to be rendered by TerrainPass, we don’t need to attach the terrain we’ve just created to the root node. Adding it to root node will make the default renderer renders it. Instead, you have to create an instance of TerrainPass, and add the terrain to it by using addTerrain() method from TerrainPass. Then by using getManager() method from you implementor class, you will get the pass manager which contains the list of passes that will be used to draw the scene (these include the default render pass which draws the root node). You have to add your TerrainPass to this manager to let the implementor class draws it.
Before you can run your application, you have to give at least one texture to the terrain. But assigning the texture state to the terrain won’t work because we don’t use general rendering method. Instead of that, we have to assign the texture to the TerrainPass to make it work. The texture on TerrainPass is divided into two types. The first type is the detail texture. It repeats itself across the terrain surface just like the detail texture of the terrain. The other type is the alpha texture. This texture defines which area of the terrain that will have the detail texture drawn onto it based on alpha value of the texture at certain point. These two kinds of texture have to be added in pair to the TerrainPass by using addDetail() method from TerrainPass. If the given alpha texture is null, it will set the given detail texture to be the base texture. This base texture is what the TerrainPass needs to render the terrain. Later, we will need to edit the textures, so I think you should consider to store the textures to another place because we have no way to obtain the texture data from the TerrainPass.
This is what we get so far. On the left we have a flat all zero height map terrain, while on the right we have hill based height map terrain. It’s not very pretty, is it? On the hill based terrain we can’t see the difference in height because we haven’t added the light yet. To add light or other rendering states, you can use setPassState() method from TerrainPass. Alternatively, you can use lightmap (texture that acts like the light) to produce light, but that method can only be used if we have the lightmap and the terrain shape is fixed, so we will ignore it for now.
This is what you will get after you add some render states to the TerrainPass. It’s nicer than before. In my application, I add LightState, CullState, and ZBufferState.
Now let’s add some user interface for tools that will be used to modify the terrain. I use JToogleButton to create the tools. For the time being, I don’t have time to create the icon for the tools by myself, so I will use the ones found from Radakan’s world editor (http://code.google.com/p/radakan/).
This is what I have for now.
From left to right and top to bottom, they are view, raise, lower, flatten, smooth, noise, and set height. I can’t find a proper icon for set height, so I just put the text for now. The idea is to do something according to the active tool if we left click the terrain. Actually, the view button won’t do anything, it will only deactivate the other tool. I will cover about how each tool works in the next article.
If we activate the other tools, for example raise tool, and then click the terrain, we want the effect applies to a certain area, not just at the clicked point. In other world editor, we usually find they use some kind of brush to show the affected area on the terrain. The world editor user usually want to be able to control how big the affected area. I will add some controls using JSlider. The first is brush radius. This parameter will show the radius of the fully affected area. The second will control brush falloff. This parameter will show the radius of the farthest area that are still affected but with gradually reduced effect. The last one will be brush strength which will show how much it will affect the area.
Now we have to draw the brush. There are several ways to do this for example we can create a 3D plane and give it a texture or we can create a 3D lines as the brush. But we have to remember that our terrain won’t stay flat forever, it will change dynamically. So the brush should also can adapt to the terrain’s shape. Because of that, choosing to use lines would be a wise choice. Lines are more inexpensive to draw than textured plane. They are also easier to construct and may contain less visible artifact because of its intersection with the terrain. Here is the code to construct two circled lines on the terrain.
I make it so it creates new brush every time it want to move the brush to another place. It is easier to do than trying to move every points in the brush to the new location. So we need a method to remove the old brush.
Next step is to call those two methods above. We can create another method which will be called each time the mouse moves over the canvas. I call the method from mouseDragged(), mouseMoved(), and mouseWheelMoved() methods from the mouse handler. The method should do mouse picking to find the coordinate of the terrain which is pointed by the mouse. The old brush should be removed first and then we can draw a new brush. It’s pretty simple.
This is what your result should show
I will explain in depth about how we actually deform the terrain shape on the next article.
© William Salim – WS08-1 – Software Laboratory Center
World editor is an application that is used to create or compose every visual element in the other application. It is usually used to create game worlds (In this article, I assume ‘game’ will refer to ‘3D game’ unless it is specified). World editor is an important aspect of game development as it allows the game artists to set up the worlds as what they want them to be appeared in game. World editor itself is not a modeling tool, as it isn’t used to create the 3D models (though, it is used to create the terrain). In this article I will cover about how to combine java’s swing user interface with jMonkeyEngine (jME). I will assume you know the basic of java language, swing and jME.
This first step is quite easy. You only have to create the user interface with swing. You can design your own user interface. You just have to make sure that you have some place to put your editing tools and a canvas for your 3D viewport. For the canvas, we will use LWJGLCanvas which you can find at com.jmex.awt.lwjgl. It is inherited from java’s Canvas, so we can put it in our swing user interface.
It is the initial design of my UI. As you can see on the left side, I put JtabbedPane to place my editing tools, and on the right side I put LWJGLCanvas. For now, the canvas is still empty.
You will also need an implementor class to implement your jME process on the canvas. We will create a simple one. Actually, this class behaves like SimplePassGame class which you should have already familiar with (If you don’t know, you can assume it like BaseGame with pass rendering capability added). We use pass rendering because we will need it to render our terrain which will use TerrainPass.
Here is the code snippet. You should know what the statements in the simpleSetup() do.
For this article, we will create a simple scene to check if our canvas is correctly set. You can add jME code insde the implementor class that you just created. You may override some methods which are inherited from SimplePassCanvasImpl such as simpleSetup(), simpleUpdate(), and simpleRender(). I will create a simple box.
You can change the scene with anything you like. Actually, we will change it with the terrain later.
Adding Camera Movement
Now we have to add control to the scene. We have to make the user can control what they want see in their editor. We will add some movements such as panning, rotating, and zooming the scene.
The first thing to do is creating a new mouse handler for the canvas. We can use MouseAdapter to do this. It has every mouse action we need.
I will explain from the simplest one, zooming. First we have to know how much we want out camera to move forward or backward. Then we have to get the camera from our implementor class. Then get the distance from the camera to the focus position. Initially, the focus will be at the center of the world. This focus position will be used again when we do rotation and its position will change as we pan the camera. For this reason, we have to store the focus position to a variable. The distance is only needed if we want to restrict our zooming capability, in this case we don’t want the scene is zoomed too close or surpassing the focus position. If you want to restrict your zooming, recalculate the zooming amount so it becamo zero after the camera reaches certain position, otherwise you can use your initial amount. After that, we only have to scale the normalized camera direction with the zooming amount and store the result as the new camera position. Here is the code to do that.
Maybe you wonder how we get the zooming amount. The answer is very simple, from your mouse input. The truth is you can also use keyboard to do that, but I prefer to use mouse because it is easier to calculate and combined with the other camera transformation. I will use mouse wheel scroll amount which we can get from MouseAdapter method as presented below. I also add the condition if the wheel is scrolled when left shift is pressed down, the zooming amount will be only half from normal. You can experiment on your own the value that suits you. You will notice that I use GameTaskQueueManager in this method. We need that to ensure the thread in our jME application and the awt thread run correctly.
Next action is panning the camera. Actually, it’s not too different with zooming. The differences are panning needs two direction to move the camera and when we pan the camera, we also have to pan the focus position. Moving in two directions is just as easy as moving in one direction in zooming. We only have to get the moving amount for each direction and also camera’s up and left direction. Then scale each camera’s direction with the corresponding moving amount and add them together. This way, we get the required Vector3f to move the camera and the focus position. Just add the vector to camera to move the camera and add it to focus to move the focus. Here is the code
Now we arrive at the hardest part of the camera transformation, rotation. It requires you to understand the operation of quaternion, process to rotate object about any point in 3D world, and if you want to restrict the rotation elevation, you also have to know some trigonometry. In rotation we have to use two axes as a pivot, world’s up axis to rotate right and left and camera’s left axis to rotate up and down. But unlike panning, we can’t directly calculate two axes rotation, so we have to rotate it twice. First, we have to get the amount of rotation that we want to occur. Because we only want to restrict rotation to up and down direction, we only need to recalculate the amount when we rotate it about left axis of the camera. Here we use some trigonometry to find the elevation (I won’t explain it here, learn some trigonometry if you want to know). For the rotation, we have to get the vector to move the camera to the focus position. Then rotate the vector about the axis by the amount you have calculated before. Finally, move the vector back with the rotated vector. You can now move the camera by the resulting vector. And also don’t forget to make your camera to look at the focus again (moving it with the vector doesn’t rotate the camera direction). Here is the code snippet.
The next part is about how to call the two previous methods we wrote. I present them together because actually they get similar input from the mouse. The actions happen when the user drags the mouse. The difference is the mouse button which is clicked. For panning I use middle button, and for rotation I use right button.
This is what we got so far. We will start to create the terrain in the next article.
© William Salim – WS08-1 – Software Laboratory Center