quinta-feira, 7 de agosto de 2014

Testing the "similar concept".


Recently i launched a similar (to another game "Piano Tiles") game on Google Play to test the repercussion, marketing and stuff.

For the marketing side its worth nothing, nobody noticed my game more because its similar to another more famous.

Some people liked it because of the already tested mechanics, but theres a lot of people who throw some hates on it. 
I understand it and personally don't like clones, but as a indie trying to make a living of games, i needed the try! :)

Check it out!

[]s

domingo, 5 de janeiro de 2014

Android Game Graphics Library

Recently, i needed to make little interactions (or little games) for Android. Using opengl or any game framework/engine would be overkill.
So i created a graphics library to help me with those tasks. It is a hierarchy based "gameobject" system with a SurfaceView for drawing and a separate thread for the updates.
Check out the repository.

Cheers!

quinta-feira, 7 de novembro de 2013

Cubes: Unity Minecraft Clone [Part 2]

PART 1
Hi! The main modifications of this release are:
- Easy uvs on cubes.
- Block lightning.

Doing the light was very trick and is very nice to see how a tile based engine works with a LOT of iterations. When nothing is changed you have an incredible performance, but when something needs to be recalculated the iterations eat your performance away. Most of it optimizations are ways of iterate faster or less. Kudos to the Mojang crew!

About the light: It spreads over the air tiles, and the solid tiles iluminates its faces based on the air neighbors.
The way i found for making the sun light works with an acceptable performance are more or less this:

- Iterate over the blocks on the height limit.
- If the block is air, iluminate it with the maximum light(1.0F).
- If the bottom block is already air, iluminate it with the maximum light and check for solid neighbors (left, right, front, back).
- Repeat the previous until some solid neighbor are found.
- If a solid neighbor is found, apply a fill algorithm with attenuation in that direction.

Check the code
private void SunFill(int x, int y, int z, float lightValue, bool[] startSpreading)
    {
        Cube tile = WorldMap.Instance.GetTile(new Vector3(x, y, z));
        if (tile == null || !tile.isTranslucent) { return; }
        tile.meshInfo.sunLight = lightValue;

        if (startSpreading[0]) { FillAttenuation(x - 1, y, z, lightValue - WorldAttribs.LIGHT_MIN); }
        if (startSpreading[1]) { FillAttenuation(x + 1, y, z, lightValue - WorldAttribs.LIGHT_MIN); }
        if (startSpreading[2]) { FillAttenuation(x, y, z - 1, lightValue - WorldAttribs.LIGHT_MIN); }
        if (startSpreading[3]) { FillAttenuation(x, y, z + 1, lightValue - WorldAttribs.LIGHT_MIN); }

        Cube tileBack = WorldMap.Instance.GetTile(new Vector3(x, y, z - 1));
        Cube tileFront = WorldMap.Instance.GetTile(new Vector3(x, y, z + 1));
        Cube tileLeft = WorldMap.Instance.GetTile(new Vector3(x - 1, y, z));
        Cube tileRight = WorldMap.Instance.GetTile(new Vector3(x + 1, y, z));

        if (tileLeft != null && !tileLeft.isTranslucent) { startSpreading[0] = true; }
        if (tileRight != null && !tileRight.isTranslucent) { startSpreading[1] = true; }
        if (tileBack != null && !tileBack.isTranslucent) { startSpreading[2] = true; }
        if (tileFront != null && !tileFront.isTranslucent) { startSpreading[3] = true; }

        SunFill(x, y - 1, z, lightValue, startSpreading);
    }

    private void FillAttenuation(int x, int y, int z, float lightValue)
    {
        Cube tile = GetTile(new Vector3(x, y, z));
        if (tile == null ||
            !tile.isTranslucent ||
            lightValue <= WorldAttribs.LIGHT_MIN ||
            tile.meshInfo.sunLight >= lightValue)
        {
            return;
        }

        tile.meshInfo.sunLight = lightValue;

        if (lightValue == 1f) { FillAttenuation(x, y - 1, z, lightValue); }
        else { FillAttenuation(x, y - 1, z, lightValue - WorldAttribs.LIGHT_MIN); } 
        FillAttenuation(x, y + 1, z, lightValue - WorldAttribs.LIGHT_MIN);        
        FillAttenuation(x, y, z - 1, lightValue - WorldAttribs.LIGHT_MIN);
        FillAttenuation(x, y, z + 1, lightValue - WorldAttribs.LIGHT_MIN);
        FillAttenuation(x + 1, y, z, lightValue - WorldAttribs.LIGHT_MIN);
        FillAttenuation(x - 1, y, z, lightValue - WorldAttribs.LIGHT_MIN);  
    }

And when something changes:

- Turn off the light of [Spread Steps] tiles radius.
- Get the neighbors of this radius chunk with some light in it.
- Apply the FillAttenuation on this guys to recover the light.

Thats pretty much it. I know there is a lot of room for optimizations, but this are ways i found to be easy to implement with an acceptable performance.
In the next updates i will try to focus on weapons and GUI.

The textures i´m using are from John Smith Texture Pack: http://www.jslegacy.com/
The source code is available on Bitbucket: https://bitbucket.org/sunwar/cubes (i will try to do frequent updates here)
Or you can check the webplayer, executable rar or project rar.

[ ]s

segunda-feira, 21 de outubro de 2013

Cubes: Unity Minecraft Clone

Hi! I´m really enjoying Unity so i decided to establish my Minecraft Clone as a solid study project. Currently features:
- Async chunk generation.
- Build/Destroy mechanics.
- Cube occlusion shadows.
- AABB collisions.
There are still a lot of bugs and unnecessary GC collections to fix.
The source code is available on Bitbucket: https://bitbucket.org/sunwar/cubes (i will try to do frequent updates here)
Or you can check the webplayer, executable rar or project rar.

[ ]s

sexta-feira, 18 de outubro de 2013

Export from Maya using a Python script

As you can see or will see in my posts, i´m very used to code using Actionscript to create tools for export coordinates, textures, instructions, etc. But what if i needed to export 3D data? When i discovered that Maya supports python i was very excited to try something with it. So i coded a very simple FPS using lwjgl (opengl and another useful libraries wrapped for java), and a Maya level exporter using python.
The rules for using the exporter are:
- Hitboxes (AABB collision) must be named HitBox on Maya. They can't be transformed (scale, rotate).
- All models need to be triangulated.
- Select everything.
- Copy the textures images to the "texture" folder of the FPS Engine.
- Name the file "map.sam" and save on the "maps" folder.

Grab the source files HERE. It contains an eclipse project of the Java lwjgl fps and the python scripts + maya 2012 scene i used for testing (you will probably need to adjust the textures paths). (Hulk model by 3dregenerator)
Here´s the python exporter code.
import maya.OpenMaya as OpenMaya
import pymel.core as pm
import math

#Function to get the texture name.
def GetFileTextureName(plug):
    connectedPlugs = OpenMaya.MPlugArray()
    plug.connectedTo(connectedPlugs, True, False)
    for k in range(connectedPlugs.length()):
        if (connectedPlugs[k].node().apiType() == OpenMaya.MFn.kFileTexture):
            depNode = OpenMaya.MFnDependencyNode(connectedPlugs[k].node())
            ftn = depNode.findPlug("ftn")
            filepath = str(ftn.asString())
            filesplit = filepath.split('/')
            filename = filesplit[filesplit.__len__()-1]
            return filename
    return "none"
    

selections = OpenMaya.MSelectionList();
#Grab the selections..
OpenMaya.MGlobal.getActiveSelectionList(selections);

print "Total selected objects:", selections.length();

dagpath = OpenMaya.MDagPath()
objeto = OpenMaya.MObject()

#List iterator.
iter = OpenMaya.MItSelectionList(selections);
iter.reset();

maptxt = ''
objtxt = ''
hittxt = ''

while not iter.isDone():
    
    iter.getDagPath(dagpath, objeto);

    print "object:", dagpath.partialPathName();
    
    # HIT HANDLER ==============================
    if "HitBox" in str(dagpath.partialPathName()):
        print "Hit Object detected."
        
        hittxt += '\nobj\n'
        
        #=== WORLD POSITIONING ======================
        trans = OpenMaya.MFnTransform( dagpath );        
        position = trans.getTranslation(OpenMaya.MSpace.kWorld);
        scaleArray = OpenMaya.MScriptUtil()
        scaleArray.createFromList( [0.0, 0.0, 0.0], 3 )
        scale = scaleArray.asDoublePtr()
        trans.getScale(scale)
        rotation = OpenMaya.MEulerRotation();
        trans.getRotation(rotation);
        
        hittxt += '\npart\n'
        hittxt += '\ndiv '
        hittxt += str(position.x);
        hittxt += '\ndiv '
        hittxt += str(position.y);
        hittxt += '\ndiv '
        hittxt += str(position.z);
        hittxt += '\ndiv '
        hittxt += str(math.degrees(rotation.x));
        hittxt += '\ndiv '
        hittxt += str(math.degrees(rotation.y));
        hittxt += '\ndiv '
        hittxt += str(math.degrees(rotation.z));
        hittxt += '\ndiv '
        hittxt += str(OpenMaya.MScriptUtil().getDoubleArrayItem( scale, 0 ));
        hittxt += '\ndiv '
        hittxt += str(OpenMaya.MScriptUtil().getDoubleArrayItem( scale, 1 ));
        hittxt += '\ndiv '
        hittxt += str(OpenMaya.MScriptUtil().getDoubleArrayItem( scale, 2 ));
        #===============================================
        
        hittxt += '\npart\n'
        
        # cache the points
        mesh = OpenMaya.MFnMesh( dagpath );
        
        meshPoints = OpenMaya.MPointArray()
        mesh.getPoints( meshPoints, OpenMaya.MSpace.kObject )
        for i in range( meshPoints.length() ):
            hittxt += '\ndiv '
            hittxt += str(meshPoints[i].x);
            hittxt += '\ndiv '
            hittxt += str(meshPoints[i].y);
            hittxt += '\ndiv '
            hittxt += str(meshPoints[i].z);
        
    # MESH HANDLER ==============================   
    else:
        print "Mesh Object detected."
        
        objtxt += '\nobj\n'
        objtextura = ''
       
        uvs = []
        pts = []
        normals = []
        vertexindices = []      
        
        instanceNumber = dagpath.instanceNumber()
        mesh = OpenMaya.MFnMesh( dagpath );
        
        #=== POSICIONAMENTO NO MUNDO ======================
        trans = OpenMaya.MFnTransform( dagpath );        
        position = trans.getTranslation(OpenMaya.MSpace.kWorld);
        scaleArray = OpenMaya.MScriptUtil()
        scaleArray.createFromList( [0.0, 0.0, 0.0], 3 )
        scale = scaleArray.asDoublePtr()
        trans.getScale(scale)
        rotation = OpenMaya.MEulerRotation();
        trans.getRotation(rotation);
        
        objtxt += '\npart\n'
        objtxt += '\ndiv '
        objtxt += str(position.x);
        objtxt += '\ndiv '
        objtxt += str(position.y);
        objtxt += '\ndiv '
        objtxt += str(position.z);
        objtxt += '\ndiv '
        objtxt += str(math.degrees(rotation.x));
        objtxt += '\ndiv '
        objtxt += str(math.degrees(rotation.y));
        objtxt += '\ndiv '
        objtxt += str(math.degrees(rotation.z));
        objtxt += '\ndiv '
        objtxt += str(OpenMaya.MScriptUtil().getDoubleArrayItem( scale, 0 ));
        objtxt += '\ndiv '
        objtxt += str(OpenMaya.MScriptUtil().getDoubleArrayItem( scale, 1 ));
        objtxt += '\ndiv '
        objtxt += str(OpenMaya.MScriptUtil().getDoubleArrayItem( scale, 2 ));
        #print OpenMaya.MScriptUtil().getDoubleArrayItem( scale, 0 );
        #print math.degrees(rotation.x);
        #==================================================
        
        objtxt += '\npart\n'
        #TEXTURA ========================================
        # Process the materials
        sets = OpenMaya.MObjectArray()
        components = OpenMaya.MObjectArray()
        mesh.getConnectedSetsAndMembers(instanceNumber, sets, components, True)
        for i in range(sets.length()):

            # Current set and component
            set = sets[i]
            component = components[i]
            
            # Maya handles
            fnSet = OpenMaya.MFnSet(set)
            dependencyNode = OpenMaya.MFnDependencyNode(set)
            
            # Get the shaders
            surfaceShaderAttribute = dependencyNode.attribute("surfaceShader");
            surfaceShaderPlug = OpenMaya.MPlug(set, surfaceShaderAttribute)
            
            # Get the connections
            sourcePlugArray = OpenMaya.MPlugArray()
            surfaceShaderPlug.connectedTo(sourcePlugArray, True, False)
            
            if sourcePlugArray.length() == 0:
                continue
            
            # Get the relevant material connection
            sourceNode = sourcePlugArray[0].node()
            materialDepNode = OpenMaya.MFnDependencyNode(sourceNode)
            
            # Process basic material (lambert) parameters
            if sourceNode.hasFn(OpenMaya.MFn.kLambert):
                lambertShader = OpenMaya.MFnLambertShader(sourceNode)
            
            try:
                color = lambertShader.findPlug("color", True)
                objtextura = GetFileTextureName(color)
                break
            except RuntimeError:
                objtextura = "none"
                
            
        print objtextura
        objtxt += '\nsession\n'
        objtxt += objtextura
        #==================================================
               
        #cache the UVs
        UVSets = []
        # Get UV sets for this mesh
        mesh.getUVSetNames( UVSets )
      
        # cache the points
        meshPoints = OpenMaya.MPointArray()
        mesh.getPoints( meshPoints, OpenMaya.MSpace.kObject )        
       
        print "vertices: ", pts.__len__()
        print "uvs: ", uvs.__len__()             
        
        #cache normals for each vtx
        meshNormals = OpenMaya.MFloatVectorArray()
        # normals are per-vertex per-face
        # use MtMeshPolygon.normalIndex() for index
        mesh.getNormals( meshNormals )
        
        for i in range( meshNormals.length() ):
                        
            normals.append(meshNormals[i])
            
        print "normais: ", normals.__len__()
        
        polyIter = OpenMaya.MItMeshPolygon( dagpath, objeto )
        
        pointsarray = []
        indicesarray = []
        normalsarray = []
        uvsarray = []
        
        vindices = []
        indices = []        
        points = {}
        normals = {}
        uniqueIndex = 0

        polyIter.reset();
        while not polyIter.isDone():
            
            polygonVertexIndices = OpenMaya.MIntArray()
            polygonVertexPoints = OpenMaya.MPointArray()
            polygonVertexNormals = OpenMaya.MVectorArray()
            uvSetNames = []
            uvSets = {}
            
            # Get polygon information
            polyIter.getVertices(polygonVertexIndices)
            polyIter.getPoints(polygonVertexPoints, OpenMaya.MSpace.kObject)
            polyIter.getNormals(polygonVertexNormals, OpenMaya.MSpace.kObject)
            polyIter.getUVSetNames(uvSetNames)
            
            Us = OpenMaya.MFloatArray();
            Vs = OpenMaya.MFloatArray();
            
            polyIter.getUVs(Us, Vs, UVSets[0]);
            
            for i in range (Us.length()):
                uvsarray.append(Us[i]);
                uvsarray.append(Vs[i]);
            
            # Get current polygons triangles
            pntAry = OpenMaya.MPointArray()
            intAry = OpenMaya.MIntArray()

            # Get the vertices and vertex positions of all the triangles in the current face's triangulation.
            polyIter.getTriangles(pntAry, intAry, OpenMaya.MSpace.kObject)
            
            for i in range( polygonVertexPoints.length() ):
                pointsarray.append(OpenMaya.MPoint(polygonVertexPoints[i]));
                #print polygonVertexPoints[i].x;
                
            for i in range(polygonVertexIndices.length()):
                indicesarray.append(polygonVertexIndices[i]+uniqueIndex);
                
            for i in range( polygonVertexNormals.length() ):
                normalsarray.append(OpenMaya.MVector(polygonVertexNormals[i]));
            
            uniqueIndex += 1
            polyIter.next();
        
        objtxt += '\nsession\n'
        #for key, value in points.iteritems():
        #print '-------------------'
        for i in range (pointsarray.__len__()):
            objtxt += '\ndiv '
            objtxt += str(pointsarray[i].x);
            objtxt += '\ndiv '
            objtxt += str(pointsarray[i].y);
            objtxt += '\ndiv '
            objtxt += str(pointsarray[i].z);
        
        objtxt += '\nsession\n'
        for i in range( uvsarray.__len__() ):
            objtxt += '\ndiv '
            objtxt += str(uvsarray[i]);
        
        objtxt += '\nsession\n'
        for i in range (normalsarray.__len__()):
            objtxt += '\ndiv '
            objtxt += str(normalsarray[i].x);
            objtxt += '\ndiv '
            objtxt += str(normalsarray[i].y);
            objtxt += '\ndiv '
            objtxt += str(normalsarray[i].z);

        print "FINAL Vertices tamanho", pointsarray.__len__();
        print "FINAL Normais tamanho: ", normalsarray.__len__();
        print "FINAL Uvs tamanho: ", uvsarray.__len__();
        print "FINAL Indices: ", indicesarray
        print "FINAL Total poligonos: ", uniqueIndex
                                
    iter.next();

def saveMap( fileName, fileType):
    
    print fileName;
    
    maptxt = ''
    maptxt += '\ntype\n'+objtxt+'\ntype\n'+hittxt
    mfile = open(fileName, 'w')
    mfile.write(maptxt);
    mfile.close();
   
    return 1

pm.fileBrowserDialog( m=1, fc=saveMap, fl="Some Map File (*.sam),*.sam", ft="Some Map File (*.sam)", om='SaveAs' )

Flash Based Ortogonal Tiles Engine

A couple years ago i developed a Tile Engine for Flash, using AS3, in order to use it in Facebook Games (There are a Facebook SDK included, but i think is a little old). The engine uses a php on the server side to save and load data, it features an A* pathfinding algorithm and the world is created using ASCII data. There's an Avatar system, and easy extendables classes for new items and world blocks.
If you want to take a look on the unfinished game i was building on top of it, Click here! And if you want to grab the source code, Click Here! Feel free to use it!
[]s

quinta-feira, 17 de outubro de 2013

Actionscript 3D

Back in the days when i was working at a Marketing Company doing a 2D Artist job, i was hungry about programming but i didn't know how or where to start. I remember reading articles about John Carmack, Romero and the beginning of hardware accelerated graphics and imagining these guys in their extremely cool offices working on extremely cool machines, programming games and being the underground rockstars of the nerd world(my favorite world). So i decided that i needed to do something, and do something 3D! After spend a night with my scarce Actionscrip 2 knowledge and a lot of research on the Kirupa forums, i had done the first "3D" programming of my life: (Click HERE to open the flash, use WASD and Arrows).
If you have flash, just open a new document, click on the first frame, F9 and paste this:
MovieClip.prototype.glQuad = function(ax,ay,az,
           bx,by,bz,
           cx,cy,cz,
           dx,dy,dz,
           color) {
 
 p1 = new Object();
 p1.x = ax;
 p1.y = ay;
 p1.z = az;
 xy2d(p1);
 
 p2 = new Object();
 p2.x = bx;
 p2.y = by;
 p2.z = bz;
 xy2d(p2);
 
 p3 = new Object();
 p3.x = cx;
 p3.y = cy;
 p3.z = cz;
 xy2d(p3);
 
 p4 = new Object();
 p4.x = dx;
 p4.y = dy;
 p4.z = dz;
 xy2d(p4);
 
 mediaz = ((p1.z + p2.z + p3.z + p4.z)/4);
 
 if (isVisibleBetween(p1,p2,p3)){
  this.lineStyle(1, 0x000000, 100);
  this.moveTo(p1.x,p1.y);
  this.beginFill(color, 100);
  this.lineTo(p1.x,p1.y);
  this.lineTo(p2.x,p2.y);
  this.lineTo(p3.x,p3.y);
  this.lineTo(p4.x,p4.y);
  this.lineTo(p1.x,p1.y);
  this.endFill();
  this.swapDepths(Math.round(1000000-mediaz));
 }
 
 function xy2d (vertex:Object) {
  
  var sx = Math.sin(cameraView.rotationX*(Math.PI/180));
  var cx = Math.cos(cameraView.rotationX*(Math.PI/180));
  var sy = Math.sin(cameraView.rotationY*(Math.PI/180));
  var cy = Math.cos(cameraView.rotationY*(Math.PI/180));
  var sz = Math.sin(cameraView.rotationZ*(Math.PI/180));
  var cz = Math.cos(cameraView.rotationZ*(Math.PI/180));
  
  
  // rotation around x
  xy = cx*cameraView.y - sx*cameraView.z;
  xz = sx*cameraView.y + cx*cameraView.z;
  // rotation around y
  yz = cy*xz - sy*cameraView.x;
  yx = sy*xz + cy*cameraView.x;
  // rotation around z
  zx = cz*yx - sz*xy;
  zy = sz*yx + cz*xy;
  
  vertex.x = vertex.x - cameraView.x; //+ yx;
  vertex.y = vertex.y - cameraView.y;
  vertex.z = vertex.z - cameraView.z; //+ yz;
 
  limite = -500;
  if ( vertex.z < limite ) { vertex.z = limite; }
  
  focalLength = _root.fov;
  var scaleRatio = focalLength/(focalLength + vertex.z);
   
  vertex.x = (vertex.x - cameraView.x) * scaleRatio;
  vertex.y = (vertex.y - cameraView.y) * scaleRatio;

  vertex.x += pontoFugaInicial.x;
  vertex.y += pontoFugaInicial.y;

 };
  
 function isVisibleBetween(a,b,c){
  if (((b.y-a.y)/(b.x-a.x)-(c.y-a.y)/(c.x-a.x)<0)^(a.x<=b.x == a.x>c.x)){
   return true;
  }else{
   return false;
  }
 };

}

function glRedraw() {

 drawGl();
 controles();
  
}

function controles() {
 
 if (Key.isDown(Key.RIGHT)) { cameraView.rotationY += 10; } 
 if (Key.isDown(Key.LEFT)) { cameraView.rotationY -= 10;}  
 
 if (Key.isDown(65)) { cameraView.x -= 10; } //A
 if (Key.isDown(68)) { cameraView.x += 10; } //D
 if (Key.isDown(87)) { cameraView.z += 40; } //W
 if (Key.isDown(83)) { cameraView.z -= 40; } //S
 
 
 var movement = 0;
 if (Key.isDown(Key.UP)) { cameraView.y -= 10; }
 if (Key.isDown(Key.DOWN)) { cameraView.y += 10; }
 
 if (_root.debugz) {
  _root.debugz.text = "z: "+(cameraView.z);
  _root.debugy.text = "y: "+(cameraView.y);
  _root.debugx.text = "x: "+(cameraView.x);
 }
 
}

function drawGl() {
 
 //box1
 box.clear();
 box.glQuad(100,100,600,
      200,100,600,
      200,200,600,
      100,200,600,
      0xFFFFFF);
 box.glQuad(100,100,700,
      100,100,600,
      100,200,600,
      100,200,700,
      0xCCCCCC);
 box.glQuad(200,100,700,
      100,100,700,
      100,200,700,
      200,200,700,
      0xAAAAAA);
 box.glQuad(200,100,600,
      200,100,700,
      200,200,700,
      200,200,600,
      0xAAAAAA);
 box.glQuad(100,100,700,
      200,100,700,
      200,100,600,
      100,100,600,
      0xAAAAAA);
 
 
 //box2
 box2.clear();
 box2.glQuad(-100,100,900,
    0,100,900,
    0,200,900,
    -100,200,900,
      0xCC77AA);
 box2.glQuad(-100,100,1000,
    -100,100,900,
    -100,200,900,
    -100,200,1000,
      0xCC77AA);
 box2.glQuad(0,100,1000,
    -100,100,1000,
    -100,200,1000,
    0,200,1000,
      0xCC77AA);
 box2.glQuad(0,100,900,
    0,100,1000,
    0,200,1000,
    0,200,900,
      0xCC77AA);
 box2.glQuad(-100,100,1000,
    0,100,1000,
    0,100,900,
    -100,100,900,
      0xCC77AA);
 
 //sala
 sala.clear();
 sala.glQuad(-1000,200,7000,
    1000,200,7000,
    1000,200,-7000,
    -1000,200,-7000,
      0x457908);
 
}

function glLoad() {
 
 objeto = 0;
 strafe = 0;
 forward = 0;
 
 cameraView = new Object();
 cameraView.x = 0;
 cameraView.y = 0;
 cameraView.z = 0;
 cameraView.rotationX = 0;
 cameraView.rotationY = 0;
 cameraView.rotationZ = 0;
 
 pontoFugaInicial = new Object();
 pontoFugaInicial.x = (Stage.width/2);
 pontoFugaInicial.y = (Stage.height/2);
 
 if (_root.fuga) {
  _root.fuga._x = pontoFugaInicial.x;
  _root.fuga._y = pontoFugaInicial.y;
 }
 
 fov = 600;
 zAngle = 0;
 zmouse = 0;
 
 createEmptyMovieClip("box",1);
 createEmptyMovieClip("box2",2);
 createEmptyMovieClip("sala",3);
  
}

onLoad = glLoad;
onEnterFrame = glRedraw;