Monday, May 28, 2007

models instead of primitives


The same world drawn on Google Earth sattelite layer, and the same world moved to swf Flash file using Kml as data. Distances are preserved. Two visible models (torus and cone-cube) were modeled using 3D Studio Max and placed into Google Earth.

Now in the last post I described how to render simple view, however static image is not so fascinating, it just shows that the idea works. Let’s give some moves to camera. We have to implement a simple walking, observation mechanism based on keyboard and mouse. Remember two other listeners that we implemented? Here is the place where we are going to make use of them. DisplayObject3D object like camera have already implemented methods like moveForward, moveBackward, so what we do is create Boolean variables for all four arrows. Set them true when KEY_DOWN event is dispatched, false if KEY_UP. Then we create a method which we will invoke every time before renderCamera is invoked, so that the camera view changes and we get the illusion of moving in space. In that method we set two values, one for moving forward and backward, the second for rotating the view (all that gives FPP style navigation). We set those values in that method regarding to the Boolean values for keyboard that were pressed. For instance if KEY_DOWN for up arrow is true we set the move value to 10 if for down arrow we set it to -10, if none then value is 0. The second value we change while left and right arrows are involved. Now after setting the values we have to update the camera position which will be simply invoking the two methods using those two calculated values as parameters. We use them like that:

camera.moveForward(firstValue)

camera.rotationZ(secondValue)

So summarizing the simple navigation system we get three methods which will have to be invoked along with each frame that is rendered.

· calculateCamera – sets the values regarding to key flags that were set with key handlers.

· updateCamera – invokes two methods for camera (move and rotate).

· renderCamera – renders the image from the given camera.

Those three methods are of course inside the onEnterFrame method which is executed at framerate speed. To add the mouse navigation we will use some of the Sprite properties which hold mouseX and mouseY values. In our case they would have to be connected with camera Z and X axes. We tweak the rotation and movement speed with some fixed values. Outline of code should be more-less like that:

camera.roationZ = 0 – pv3DSprite.mouseX * 0.6

camera.roationX = 90 – pv3DSprite.mouseY * 0.2

And placed inside onEnterFrame method or any of three presented above (they are invoked by onEnterFrame after all). The onEnterFrame method generates an image that is put onto Sprite object which is connected with our Canvas3D component lying inside panel component in mxml file. Compile the whole application along with a simple kml file into swf file. If you now run the file in the browser you should be able to fly like a free bird among the KML buildings, inspiring view, isn’t it?

Now that we can see some primitives, the smartest move to improve the city view would be to develop a way for viewing 3D models which give much better visualizations and immersion level. There are plenty of modelers available these days, some are commercial and some are on free license. The one thing that connects them is that they accepted the Collada file as one of the standards that we save models to. Now as you might remember the Google Earth in its latest version supports the Collada file. And also as you might remember our speculations in the beginning of this document, the PV3D engine comes also with two formats available to adopt, ASE and DAE. So, where to start? First of all let us see how the Collada models are added into Google Earth.

In Google Earth press the Shift+Ctrl+M combination or go to Menu, Add, and choose Model. You will be presented with panel to enter the longitude and latitude values for model center. By default the values from the center point of the actual view are entered. Click browse and a File Chooser menu will pop-up for you to point a DAE Collada file model. If you now tilt the view you will see your model that you just added. One thing worth noticing while projecting models in Google Earth is that they need to have dimensions counted in thousands units (3D Studio Max case) in order to have natural size when placed in Google Earth environment. So let us now save it along with other polygons into Kml file. The most interesting things including the model tag and link to local DAE file is presented in Kml file structure very simply, and can be seen by opening the kml file structure.

Worth noticing is that inside this tag there are still some other very interesting options available (inside Model tag), like scale or orientation of model which we can use later. If we have that structure we go back to our Kml class and add a bit of ActionScript code. First of all we will need two arrays just like for Mesh3D objects. Here we will create a COLLADA_FILENAMES array with String objects in it (URLs to Collada files) and COLLADA_TRANSLATIONS which will hold translation vector from WORLD_PIVOT, just like it was made before. Then we add new tags to continue with recursion if a Model tag is found and lead us to Location tag where we can find the longitude and latitude values for actual model. We again use gnomonic projection to convert the coordinates to local area. If the Model is first object read from kml file, its latitude and longitude would be used as WORLD_PIVOT. We save the translation vector and the filename into our COLLADA arrays preserving the indices order. If we did everything properly we would end up with two new arrays that we can use within the PV3D engine class, just like we extracted Mesh3D objects from MESH_ARRAY.

Inside the PV3D engine class we look for the line code where we’ve put the loop for MESH_ARRAY. Now we have to do the same with COLLADA_ARRAY with few extensions. Let’s loop through the COLLADA_ARRAY elements and for each of them create a new Collada object (class available from PV3D package) using the String URL in constructor. Then we can use the translation vector from COLLADA_TRANLASTIONS array just like we did for Mesh3D objects and finally add the Collada object to rootNode. Sounds simple, and it is simple! But it’s not going to work that way, it will run but we are not going to see any of the models that we attached to kml file. The Collada files are XML structures that need to be parsed from file before the PV3D engine can access the data that they hold. It takes some time to parse the file. It is kind of slow, so that when PV3D wants to access the data, the data is not ready yet. We need to develop a loader for XML files that will inform us when the whole file is loaded into virtual structure and is available for us.I did the same while loading the Kml file in the beginning, so now its a good time to make an explanation to that process.

We will use the URLLoader class that can be found in AS class package provided with Flex SDK. The URLLoader class downloads data from given URL as text, binary or XML variables. The URLLoader object captures all the data before making it available to ActionScript. During the capturing process it dispatches events that we can catch and invoke proper actions (there are also properties like bytesLoaded, bytesTotal available to create progress bars for instance). To invoke the loading process from URLLoader object we need an URL object. The URL we use is URLRequest object with String filename from COLLADA_ARRAY as a parameter used in constructor. By default URLRequest works using HTTP GET method. However you can change that by setting the method property on the URLRequest object like that.

var request:URLRequest = new URLRequest(“hola.txt”);

request.method = URLRequestMethod.POST;

Next we fetch it to URLLoader load method as parameter. Then we have to add a listener that will listen for EVENT_COMPLETE event to our URLLoader and invoke proper method, onModelComplete in our case. The onModelComplete method would be responsible for several things, but no magic here, we just put the code for creating Collada objects from array, then the code for moving along the axes and adding the child to rootNode. We can also put some additional code, like model rotation before placing it on stage. The full structure of that loader can be found in walk3D.as source file (more detailed description of that process can be found in Adobe Flex Builder Help). Now just check if it works. If so, you should be able to see the models inside the city. The important thing is the scale here, as I mentioned before it depends on modeler that you are using, it should be much bigger (thousands units) to be placed inside the Google Earth. Then the size has to be diminished to use it inside PV3D engine. In KML class there is a COLLADA_SIZE parameter which you can use when using Collada object constructor (second parameter is scale) from PV3D bundle. By default this parameter is set to 0.03 so that models fit the scene. Again, you can tweak them if you are using other modeler than 3D Studio Max so that they fit the 3D world.



By Marcin Czech

No comments: