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;

terça-feira, 8 de outubro de 2013

Unity (Very) Basic Minecraft Clone

Hi, this is my first post and i think a good way to start is with a Minecraft Clone! :) Everyone loves Minecraft, and i personally like to use the Blocks/Chunks/TileMaps system to test and understand new languages and frameworks. This is one of my first studies using Unity3D, it is like an early prototype, many bugs and non-optimized. Oh! And of course it is always very nice to see the wonders of Perlin´s Noise! Thank you Mr Perlin.

Update: I´m currently updating this project, check this POST!

Sorry for the english mistakes! I hope i can make myself understandable. :)
[ ]s