cosmogonies.net Blog of cosmogonies.net

11Aug/150

Please consider stereoscopic gameplay once again

This article was also published as a featured blogpost @gamasutra(link)
2016 will be the year of VR. Maybe. Before, let’s look back to the stereo game feel:

Please consider stereoscopic gameplay once again

For ten years now I have worked in CGI movies and TV-shows, and I witnessed the transition from 2D into 3D Animation, and then into Stereoscopic.

I must say up front, I am like 90% of my colleagues, not convinced about the utility of stereoscopic vision into passive video medias:

It works well for establishing shots but I feel confused when action sequences are divided in shorter cuts, (I read somewhere that our brain needs more time to apprehend a 3D picture than a 2D)
And most directors are not familiar with the fact that strong depth of field (the in-focus region in the frame) is friendly with stereoscopic vision (I want to look at that Pandora butterfly, but even though my eyes are targeting it, it remains blurry), which does not happen if I’m in control of the camera...

Therefore what could be right for passive video medias is not necessarily so for videogames. But let me try to convince you to give it one more try.

I have been playing in stereoscopic vision for quite some time now, and I love it.

After all, a videogame (cinematics excluded) contains one continuous shot !
Nvidia’s Nvision allows to toggle between classic and stereo (with just Ctrl+T), and it is jaw-dropping with a lot of games. Let me try to summarize what is, for some games, better in stereo:

Gameplay Improved.

Platformers need stereo. I mean it. Mario in full 3D relies a classic “noon shadow” just bellow him to help the player anticipate his landing zone when jumping. But try New Super Mario World 3DS, and you won’t require this anymore! You know exactly where your character is, because you sense it, he is included into the set, not projected on a flat screen.

So for games that require great precision with the player’s placement (and punish him for any tiny mistake), playing in stereo improve a lot the game feel. Regarding my platformer experience, I played these in stereo: Darksiders2, Lord of shadow, Styx and DevilMayCry, and I was very happy with it!

Immersion: more drown than drawn

We could agree the more we sense a virtual world, the better we feel immersed in it.
I’m not talking about being emotionally implicated into a game, of course any game can achieve that (and it is probably more related to the narration), but I think photo-realistic games misses a point: they are beautiful: But beautifully flatten.
You have to play them in stereo to have the one little something they lacks: Volume.
I can suggest you to play, Tomb Raider, Assassins Creed’s, Evil Within, Remember Me, MGSV, Batman Arkham’s.
Specifically in this last, feeling the whole Arkham city when slowly gliding is really priceless, it is quite an experience on its own!

And what is a videogame talk nowadays without mentioning FPS ?
Well, the main focus here is to be the character, and to see the world through his eyes, his own perspective; stereo play very fit this point of view (if the camera shake is not crazy), and it gives the player the feeling of its avatar’s depth perception.
I successfully stereo played Skyrim, FarCry, Deus Ex, Dishonored, totally worth a try!

Relative set elements make more detailed sets !

This is a point I was not thinking about as a direct consequence to a stereo session, but now I have to write here that stereo make your image “prettier”, but in a weird way !
Of course, stereo needs the computation of two pictures instead of one (but does NOT half your framerate), and some fx could be glitched, so basically the graphics are not better.
What I mean by a prettier picture is that you are able to catch every little object that dresses the set, and distinct them from each other. When the camera moves, every single props silhouette is changing, its volume is very neatly cut from what’s behind.
In a flatten picture, a set of props is a mass, reduced to a global perception of several stuff, a stack of mixed colors. With stereoscopic vision, and when the camera moves, you feel every volume, as tiny as they are, and the general feel about that is an image more “accurate”, more precise... it feel almost like when you improve the resolution parameter!
I could not illustrate better that feeling with Luigi’s Mansion, when on top of the beautiful graphics and stereo, turning on the vacuum makes the whole props of the room jiggling, which improves a lot this effect.
I can also quote here Mirror of Fate or Witcher 2, but also some First person Explorer like Dear Esther, Ethan Carter which gives you the time to completely explore the set in every little details.

The “miniaturization effect”

As stranger as that could sound, I feel sometimes that playing in stereo miniaturize the assets!
You may feel in stereo, particularly on a very wide screen, that the assets are very near you, “at arm reach”. Their perception, accurate in volume, feels real, but often represent biggest objects for a desktop screen in the real life! So my brain fight this conflict by faking their scale: they feel like toys.

The biggest “toyification” I had was in LA: Noire, and I think that is quite related to its stylization.
Some could tell that it deserves the purpose of the game, but due to the artistic direction of the game, I do not feel it, and I quite appreciate it to be honest!
After all, we play games here; So it is natural to play games with toys !
I can list other games in which I had this miniaturization-feel and it is often the most cartoonist-ones, like Alice, Saints Row, Sonic All Stars Racing Transformed, and the wonderful Stacking.

The darkside, and its cure.

Be sure of one thing, game developer mostly have no time, no money, and no interest into putting efforts into the stereoscopic effect of their games. I imagine that the number of gamers that play in stereo will not worth it.
So yes, playing PC games in stereo could be a nightmare:
There are glitches: UI with no depth, shadow not in their volume, volumetric fogs or fx blinking, objects waaaaay to close to the camera.
But don’t worry, there is a salvation, http://helixmod.blogspot.fr (I’m not related to this website)
This site gather home-made stereo patch that fix almost every glitch per game !
I’m not related to that website, but be sure the day this guys stop doing this, stereo gaming will die.

Conclusion

I’m not playing 2D games in stereo, but with slow/platformer/explorer games, I really want you to believe me that stereo is definitely an improvement into the realtime interactive entertainement.
Yes VR is coming, but for a lot of actually available games, stereo is compatible and enhance the experience.
Please, give it a try ^^.
My rig is an ASUS VG278HR screen, with a Nvidia GForce and Nvision driver installed.

 

home
blog
13Apr/150

Unity’s Mono maniacs

Be sure of one thing: I love Unity and cannot let a day end without doing something with it^^.
But: there's a but.
I'm very disappointed about the transparency regarding its mono version, integration and... modification.
Everything in Unity is well documented, tutorialized, blogged and tweeted... everything but Mono !

What are we talking about exactly ?

Unity is using the C# language, but it stand on top of a modified version of Mono 2.6 .
Mono (http://www.mono-project.com/) is a framework : a utility toolkit that extends the language/compiler by high-abstraction primitives, like List, Dictionaries, garbage collector, Self-Introspection.
Mono was made as an open-source alternative to the .NET Framework, made by Microsoft (which also became open-source recently ! sources here )
You can see Mono as the foundation of Unity's kernel, and its secret recipe for its huge portability.

So, I have one request and one only:

As Unity provides its manual documentation : $INSTALLPATH/Unity/Editor/Data/Documentation/en/Manual/index.html
... and its API documentation : $INSTALLPATH/Unity/Editor/Data/Documentation/en/ScriptReference/index.html
Why do you not provide your framework documentation ?
I'm quite sure 90% of Unity users uses MSDN website when working on their games.
But think about it; That's like reading MS Word's help when using LibreOffice right ?

So you can find documentation of classes or methods that are not available in unity, especially when integrating other c# library, external to unity.
I found myself unable to use the namespace "System.Threading.Tasks", System.Drawing.Image or also all LINQ-relative syntax that are not available also.

What can we know ?

So, "Internet" says Unity runs a modified Mono 2.6, but from my own very very old post here :
http://answers.unity3d.com/questions/259448/how-to-determine-mono-version-of-unity-.html
I tried to get dynamically the Mono version, and that is a mess :

#$INSTALLPATH\Unity\Editor\Data\Mono\bin\mono.exe" -V
>Mono JIT compiler version 2.0 (Visual Studio built mono)
>Copyright (C) 2002-2010 Novell, Inc and Contributors. www.mono-project.com
>TLS: normal
>GC: Included Boehm (with typed GC)
>SIGSEGV: normal Notification: Thread + polling
>Architecture: x86
>Disabled: none

To be more precise :

#$INSTALLPATH\Unity\Editor\Data\Mono\bin>monop2 --runtime-version mono.exe
>runtime version: 2.0.50727.1433

In script you can also :

//you will need "using System.Reflection;" in your file header
System.Type type = System.Type.GetType("Mono.Runtime");
if (type != null) {
System.Reflection.MethodInfo displayName = type.GetMethod("GetDisplayName", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
if (displayName != null)
    Debug.Log(displayName.Invoke(null, null)); // On Unity5.0 it printed "2.0 (Visual Studio built mono)"
}

Worse a dedicated "Mono Compatibility page" has disappeared since 4.1.2 :
http://docs.unity3d.com/412/Documentation/ScriptReference/MonoCompatibility.html
http://docs.unity3d.com/ScriptReference/MonoCompatibility.html => page not found !
Apparently, the modified version of Mono is open-source, but the github link seems quite frozen to me...
So please, help us at least find the real and correct documentation of our everyday framework !

By the way, taken how things move fast :

I know the biggest feature in Unity is its huge portability, and it was named from that, but maybe we can improve our workflow by following the two main directions of .NET and Mono ?
When you looked at the history of Mono releases  (Yes Mono 2.6 is dated from 2009) and the Microsoft's ones (.NET 4.0 and .NET 4.5 ) there is great features and Unity could benefits from them.
(To follow the equivalent rule between these twos, Let's say Mono 2.6 is equivalent to C# 3.0 (in terms of language features). Mono 2.6.1 started adding .NET 4.0 features).
But what about the "modified version" Unity uses ?
Did it evolves along side of the Editor ?
Maybe I'm wrong, and if this kind of documentation exists, I will be  very thankful to anyone who can link it to me !
But if not, please please provides us something to work with ^^.

contact
profile
5May/141

Funfact, some lines of code for “Ghost of a tale” are writen from Alcatraz !

Ghost of a tale, coded from Alcatraz

Let me set the scene here :

The GDC14 was just finished, I only had the saturday to visit San Francisco a little more.
Last year I've done a great tour, but couldn't reach the Rock !
What a great opportunity to capture all sort of textures, of rotten and twisted metals to the famous decaying prison^^.

But, on my way to it, I just crossed the visitable WorldWarII submarine, the mighty USS PAMPANITO .
I was like a kid in a toy-store ; you can't imagine how many pictures of pipes and "steampunk devices" I took !
Enough to drain a lot my camera's battery...
So the boat was boarding, and I had no juice left to make my camera work... but I have always my work with me ^^.
... I just sat in Alcatraz library, refilling my camera on my computer, while killing time... on Unity's game development of course !
Wanna hear more about the game ? Have a look at : http://www.ghostofatale.com/

Ghost of a tale, coded from Alcatraz

Ghost of a tale, coded from Alcatraz

 

rss
18Apr/130

Shotgun (DAM) python object wrapper

shotgun

Asset Manager is a great way to fluidify your workflow.

As a DAM (Digital Asset Manager) has a fuzzy definition, I will quickly describe how I see Shotgun.
Shotgun does the boring thing you do not want to do when dealing with digital assets.
Shotgun types them, calling them Entity, record them and all kind of metadatas attached to them (string, float, date, entites, etc.. and lists), and call that Fields.
In facts, Shotgun is a web-page (internet or intranet), displaying in a very user-friendly way those datas (a lot more elaborated and cgi-oriented than a phpMyAdmin for example).
So basically, it is a back-end database (postGre), and a front-end browsing page.

What it does NOT, is storing files, or have any further connection with a filesystem but some string fields where you can put paths.
Now the new TANK extension of shotgun could do that, I will talk about it later.

So the method I present here is how deal very easily with shotgun entities as objects, manipulating very semantically with calls like

print myAsset.description, myScene.created_at, myScene.created_by

Using the analogy: Entity=Object, Field=Member of python class instances.

This is how I organized the package (ALK_ prefix stands for the studio I currently work, Alkymia):
ALK_Entity\ (Package name)
ALK_Entity\__init__.py (Main superclass to inherit from shotgun API)
ALK_Entity\Scenes.py (sub-classes definition, one representing the exact shotgun entity, the other to simply add your additional stuff. So the module name is the Entity name in plural.)
ALK_Entity\Shots.py
ALK_Entity\Assets.py
... #and so on

ShotgunWrapperUML

From this UML diagram on the left, you can see how the package hierarchy works.

With that in mind, you can graph easily a real network of all your assets.
You will obtain a dependency graph, of relationships as "needs" or "is a part of" that is really appreciated in object-oriented scripting. It is really powerful.

The dark side of it, as you may notice parsing my code, is that every entity is requested with all fields, which is not very performance.
Good usability versus good performance is often a very tricky balance to handle.
(Mistake already done: In my first code iteration, I had put the request of filed as entity into the constructor, not the attribute getter.
Result: any simple entity was bouncing all over dependencies and links, putting the whole f** database into RAM for one single asset^^.)

So, this is the final result looks like, and this is how I deal with our metadatas in the pipeline tools I made:

###
import ALK_Entity.Scenes

myScene = ALK_Entity.Scenes.ALK_Scene("S004_M001")
print type(myScene)  # <class 'ALK_Entity.Scenes.ALK_Scene'>
print myScene.id, myScene.code  # 794 S004_M001

print myScene.sg_sequence  # [<ALK_Entity.Sequences.ALK_Sequence object at 0x0000000038BADE80>]
print myScene.sg_sequence[0].code  # S004

mySceneById = ALK_Entity.Scenes.ALK_Scene(794)
print mySceneById.code  # S004_M001
###

If you are interested, here is an example of python scripts I wrote.
I shared them "as it is", and with quick mods to be more clear and subject-centric.
This modifications could have altered functionality, you have my apologies if so.
( In another blog post, I will describe how I setup my production versioning system linked to shotgun entities. )

""" File: ...\ALK_Entity\__init__.py  """

# -*- coding: utf-8 -*-
""" Contains all connections to the Asset Management (Shotgun API wrapper). """
import os
import shotgun_api3.shotgun
_PROJECT_ID = 00  # We only worked on one project so I made very few cases of multi-project instances.

class ShotgunEntity(object):
  """Representation in python object of an entity in shotgun."""

  def getShotgunHandle(self):
    """ Default method that needs to be overloaded in sub-classes """
    import shotgun_api3.shotgun
    SERVER_PATH = "http://your_shotgun_url"
    SCRIPT_NAME = 'Framework_Connector'
    SCRIPT_KEY = '##########################################'
    return shotgun_api3.shotgun.Shotgun(SERVER_PATH, SCRIPT_NAME, SCRIPT_KEY)

  def getEntityType(self):
    """ Default method that needs to be overloaded in sub-classes """
    return 'DefaultValue'

  def getNameFilter(self,_name):
    """ Default method that needs to be overloaded in sub-classes """
    #Must overload that, for example tasks has no 'code' but 'content'...
    return ['code','contains',_name]

  def getProjectFilter(self):
    """ Default method that needs to be overloaded in sub-classes """
    return ['project','is',{'type':'Project','id':_PROJECT_ID}]

  def updateFromDataBase(self,_sg_Id):
    """Looking for the shotgun Asset and retrieve its metadatas.
    Be aware that you can use shotgun Id or Name to find the Asset."""

    shotgunHandle = self.getShotgunHandle()
    filters =[]

    if self.getProjectFilter()!=None:
      filters.append(self.getProjectFilter())

    if type(_sg_Id) == str:
      filters.append(self.getNameFilter(_sg_Id))
    elif type(_sg_Id) == int:
      filters.append(['id','is',_sg_Id])
    else:
      pass
      #put here your throwing error system

    fields = self.__dict__.keys()
    #All fields, that does not fit well for high performance or big requests on huge database of course.
    Assetdata = shotgunHandle.find_one(self.getEntityType(), filters, fields)
    shotgunHandle.close()

    if not Assetdata:
      print("ERROR, no entry in shotgun DB for entity with id = "+str(_sg_Id)+" ("+str(self.getEntityType())+")  ",eVerboseLevel.kError)
      print("Filters were ="+str(filters),eVerboseLevel.kError)
      raise ALK_InputOutput.Debug.ShotGunEntryMissing("ERROR, no entry in shotgun DB for Asset !")
    else:
      for currentmember in fields:
        if not Assetdata.has_key(currentmember):
          print("ERROR, no entry in shotgun DB for member :"+currentmember,eVerboseLevel.kError)
        else:
          setattr(self, currentmember, Assetdata[currentmember] )

  def __getattribute__(self,name):
    """ When a member of the instance of the class is queried, we get its data. """

    myValue = object.__getattribute__(self, name)  # getting a member value given its name as a string.

    if(isinstance(myValue, list)):

      entityList=[]
      for currentSubObj in myValue:
        newEntity = self.__castFromDictToEntity(currentSubObj)
        entityList.append(newEntity)
      setattr(self, name, entityList)
      return entityList

    else:
      if(isinstance(myValue, dict)):
        newEntity = self.__castFromDictToEntity(myValue)
        setattr(self, name, newEntity)
        return newEntity
      else:
        return myValue

    return None

  def __castFromDictToEntity(self, _data):
    """ With the native python's shotgun wrapper, every entity is represented as a dict, this will cast them """

    #every shotgun dict has an id and a type (every id is unique based on its type, often show as entity name)
    if(isinstance(_data, dict)):
      if(_data.has_key('id') and _data.has_key('type')):

        myType = _data['type']
        myALK_Entity = _createEntityByName(myType, _data['id'])
        return myALK_Entity
    return _data

  def updateIntoDataBase(self):
    """" Publish all members from python RAM into shotgun Database."""
    #import shotgun_api3.shotgun

    shotgunHandle = self.getShotgunHandle()
    fields = self.__dict__.keys()
    memberDict = {}
    for currentmember in fields:
      memberDict[currentmember] = getattr(self, currentmember)

    AssetdataNew = shotgunHandle.update(self.getEntityType(), self.id, memberDict)
    shotgunHandle.close()

  def updateMemberIntoDataBase(self,_memberNameToUpdate):
    """" Publish given member from python RAM into shotgun Database."""

    shotgunHandle = self.getShotgunHandle()

    memberDict={}
    memberDict[_memberNameToUpdate] = getattr(self, _memberNameToUpdate)
    AssetdataNew = shotgunHandle.update(self.getEntityType(), self.id, memberDict)

    print("Asset Updated in shotgun :" + str(AssetdataNew))
    shotgunHandle.close()

  def _createEntityByName(_TypeName, _Id):
    """ Cast from a type as string into a ALK_Entity wrapped instance. """

    if _Id==None:
      return None

    if _TypeName == 'Asset':
      import ALK_Entity.Assets
      return ALK_Entity.Assets.ALK_Asset(_Id)

    if _TypeName=='Scene':
      import ALK_Entity.Scenes
      return ALK_Entity.Scenes.ALK_Scene(_Id)

    if _TypeName=='Version':
      import ALK_Entity.Versions
      return ALK_Entity.Versions.ALK_Version(_Id)

    #And so on for all your managed entities.
    printt("Un-managed shogun type = "+_TypeName)

And now this is an entity wrap of a scene:
""" File: ...\ALK_Entity\Scenes.py  """

# -*- coding: utf-8 -*-
""" Wrapper for Scenes into the Asset Manager Database. """
import os
import ALK_InputOutput.Debug
import ALK_Entity

_SERVER_PATH = "http://your_shotgun_url" # make sure to change this to https if your studio uses it.
_SCRIPT_NAME = 'ALK_Scene' #In your administration panel, be sure to create as many scripts as you have entities wrapper.
_SCRIPT_KEY = '############################'

class __Shotgun_Scene(ALK_Entity.ShotgunEntity):
    """Representation in python object of a Scene entity in shotgun.
    We assume that all members of this class is a field with a same name."""

    def __init__(self,_sg_Id):
        """Constructor of the class with defaultValues and next updated them from database."""

        self.addressings_cc = 'DefautValue'
        self.assets = 'DefautValue'
        self.code = 'DefautValue'
        self.created_at = 'DefautValue'
        self.created_by = 'DefautValue'
        self.description = 'DefautValue'
        self.id = 'DefautValue'
        self.image = 'DefautValue'
        self.notes = 'DefautValue'
        self.open_notes = 'DefautValue'
        self.open_notes_count = 'DefautValue'
        self.project = 'DefautValue'
        self.sg_scene_type = 'DefautValue'
        self.sg_status_list = 'DefautValue'
        self.shoot_days = 'DefautValue'
        self.shots = 'DefautValue'
        self.step_0 = 'DefautValue'
        self.tag_list = 'DefautValue'
        self.tasks = 'DefautValue'
        self.task_template = 'DefautValue'
        self.updated_at = 'DefautValue'
        self.updated_by = 'DefautValue'
        #Custom fields
        self.sg_cameras = 'DefautValue'
        self.sg_sequence = 'DefautValue'#Warning it is a list of dictionnaries

        self.updateFromDataBase(_sg_Id)

    def getShotgunHandle(self):
        import shotgun_api3.shotgun
        return shotgun_api3.shotgun.Shotgun(_SERVER_PATH, _SCRIPT_NAME, _SCRIPT_KEY)

    def getEntityType(self):
        return 'Scene'

    def getNameFilter(self,_name):
        return ['code','contains',_name]

class ALK_Scene(__Shotgun_Scene):
    """Custom class to implement metadatas of Scenes without shotgun synchronisation."""

    def __init__(self,_sg_Id):
        """ Constructor. """
        #First calling the inherited constructor:
        super(ALK_Scene,self).__init__(_sg_Id)

        #TODO verif if sequence.code is in scene.code

    def getFolder(self,_task):
        import ALK_Entity.Projects
        import ALK_Entity.Sequences

        if len(self.sg_sequence)>=1:
            mySeq = self.sg_sequence[0]
            #print mySeq.code
            #print _task.step.code
            if _task!=None:
                myFilePath = ALK_Entity.Projects.getZeProject().getProjectDirectory()+"\\Exploitation\\"+mySeq.code+"\\scenes\\"+_task.step.code+"\\"+_task.content
            #maybe replace myFilePath = ALK_Entity.Projects.getZeProject().getProjectDirectory()+"\\Exploitation\\"+mySeq.code+"     by ALK_Seq.getFolder()
                return myFilePath
            else:
                printt("No task associated to this scene !", eVerboseLevel.kError)
                raise ALK_InputOutput.Debug.ShotGunEntryMissing("ERROR, no entry in shotgun DB for this task !"+self.code)
        else:
            printt("No sequence associated to this scene !",eVerboseLevel.kError)

            #raise

    def getRange(self):
        import sys
        import ALK_Entity.Versions

        StartFrame = sys.maxint
        EndFrame = 0

        for (currentVersion,currentShot,currentCamera) in ALK_Entity.Versions.getAllValidated(self):
            print currentVersion.code , currentCamera.sg_short_name, "In=" ,currentVersion.sg_cut_in,"Out=",currentVersion.sg_cut_out

            if currentVersion.sg_cut_inEndFrame:
                EndFrame = currentVersion.sg_cut_out

        return (StartFrame, EndFrame)

def getSceneList():
    import shotgun_api3.shotgun
    myShotgunHandle = shotgun_api3.shotgun.Shotgun(_SERVER_PATH, _SCRIPT_NAME, _SCRIPT_KEY)

    allItems = myShotgunHandle.find('Scene', [['project','is',{'type':'Project','id':ALK_Entity._PROJECT_ID}]])
    result=[]
    for currentItem in allItems:
        newItem = ALK_Scene(currentItem['id'])
        result.append(newItem)
    return result

18Mar/130

How to handle the unexpected

Thanks to a french blog, I just discovered that python allow you to handle the unexecpected python errors.
It is called "excepthook" and it is a must have in a studio framework !
So I urged myself to put a routine into the userSetup.py of anyone in the studio that starts Maya or MotionBuilder:
 

import sys

def handleCrash(_ErrorType, _Value, _TraceBack):
  """ do your stuff """
  sys.__excepthook__(_ErrorType, _Value, _TraceBack) #as Sam says, be polite.

sys.excepthook = handleCrash
 
With that, you can send you an email with a HTML-formated text, with all the details, but be careful about the possible lag when sending email.
My best guest right now is to insert an sql request into a debug.db file using sqlite3.
With a little additional efforts (or a database reading app), you can track anything happening in the studio.
My advice is to get exact time, the user name, machine name and its opened-file's name to fill the logging.
Here is the final snippet:

import sys

def handleCrash(_ErrorType, _Value, _TraceBack):
  import traceback
  import sys
  import datetime
  import os

  UserName = os.getenv('USERNAME')
  MachineName = os.getenv('COMPUTERNAME')

  FileName = "" # could be, regarding your current environment,
                #maya.cmds.file(query=True,sceneName=True,shortName=False)
                #pyfbsdk.FBApplication().FBXFileName

  HtmlBuffer="<body>"
  HtmlBuffer+="UNEXPECTED:"+str(_ErrorType) +" ==> "+ str(_Value)+"<br>"
  HtmlBuffer+= UserName+" on "+MachineName+"<br>"
  HtmlBuffer+= "on File ="+FileName+"<br>"

  dt = datetime.datetime.now()
  DateNiceName = dt.strftime("%A, %d. %B %Y %I:%M%p")
  HtmlBuffer+= "At "+DateNiceName+"<br>"

  formatted_lines = traceback.format_tb(_TraceBack)
  for cur in  formatted_lines:
    HtmlBuffer+=str(cur)+"<br>"
  HtmlBuffer+="</body>"

  #do your stuff

  sys.__excepthook__(_ErrorType, _Value, _TraceBack)

sys.excepthook = handleCrash

By the way, in Maya you will find that some of your errors will not be caught by that system.
Cyrille Fauvel wrote about that in the awesome "Around the corner" dev blog.
http://around-the-corner.typepad.com/adn/2013/03/my-entry.html
import maya.utils

def handleCrash_Maya(etype, value, tb, detail=2):
handleCrash(etype, value, tb)
  return maya.utils._formatGuiException(etype, value, tb, detail)

maya.utils.formatGuiException = handleCrash_Maya

PS: Coming back from GDC2013, I attended to an awesome conference called "Spend Time Where it Matters: Friction Free Bug Reporting" by Raphael Saint-Pierre (Ubisoft) , talking about auto-BugReporting.
I learned here a very great tips to identify and index a bug into a database without creating some clones of similar ones everytime a new occurence pop. How ? Very easy: give an Id coming from a hash of the call stack ! Tested... and approved !

home
blog
5Mar/133

How to batch Motionbuilder: FBX SDK to the rescue.

When I suddently discovered that MotionBuilder does not have a batch mode, I fainted, as I consider doing my job is half the time coding batch tools.

As a reminder, the Batch version of a software is the access of this one in command-line; means without interfaces, but more importantly it is also scriptable so you can process infinite auto-tasks management with it.
Maya comes with a mayabatch.exe, and xsi has its xsibatch.exe, but you can just start MotionBuilder.exe with a -console flag, which allow you having an external output, nothing really fancy.
This is a real issue here: How can building a python framework, multi-environment, and keep tools doing full-auto pipeline processes.
Thankful to Autodesk policy, they gave us a solution : FBX SDK.

  • Explanations:

FBX SDK is written in c++, but hopefully Autodesk managed to wrapped it with SWIG to propose a binding python (if the link is dead, try a search with fbx sdk python binding in Area)
The full doc is actually here, but it is the c++ one. Do not worry about it, python equivalent are found easily (remember dir statement).

So, Is this another environement to my framework: FBX ? Not quite.
We are still in standalone python, but we are using a library to PARSE a file format.
As Maya uses .MA as a 3d structure of succession of MEL Commands, and .MB as a binay, FBX provides both ascii and binary, but ascii fbx are very hard to parse.
FBX SDK help you to read, modify and save any file of FBX, but require only python, ANY python installed on your computer.
For example, I use FBX SDK to open, read, and manage a fbx file, running into a mayabatch.exe (and its mayapy python).
You can also used FBX SDK into MotionBuilder UI, but that is REALLY the snake eating its own tail !

  • Several snippets:

Here I share with you several usefull snippets:


def getHandle(_FilePath):
  import PyFBX
  import PyFBX.fbx

  lSdkManager = PyFBX.fbx.KFbxSdkManager.Create()
  ios = PyFBX.fbx.KFbxIOSettings.Create(lSdkManager, PyFBX.fbx.IOSROOT)
  lImporter = PyFBX.fbx.KFbxImporter.Create(lSdkManager, "")
  lImporter.Initialize(_FilePath, -1, lSdkManager.GetIOSettings())
  MobuFileHandle = PyFBX.fbx.KFbxScene.Create(lSdkManager, "myScene")
  lImporter.Import(MobuFileHandle)
  return MobuFileHandle

If you use that to parse a lot of files, be really sure to free the RAM from
.Import here will literally copy the file into the RAM of your computer, and as the reference is still alive, the garbage collector will not free the RAM.
My advice here is to explicitly calling lSdkManager.Destroy() and recreate a fileHandle from scratch every time you switch from a file to another.


def saveAs(_FBXHandle, _FilePath):
  import PyFBX
  import PyFBX.fbx
  import os.path
  lSdkManager = PyFBX.fbx.KFbxSdkManager.Create()
  ios = PyFBX.fbx.KFbxIOSettings.Create(lSdkManager,PyFBX.fbx.IOSROOT)
  lExporter = PyFBX.fbx.KFbxExporter.Create(lSdkManager,"")
  lExporter.Initialize(_FilePath,-1,lSdkManager.GetIOSettings())
  res = lExporter.Export(_FBXHandle)

  if res and os.path.exists(_FilePath):
    print("File was exported succesfully at ="+_FilePath)

Remember how Motionbuilder manage their types of objects in Navigator Models aside, and all objects ordered by types?
Same thing here in FBX, everything is stored by types first, and you have a Method of Scene for everyone:
Finally, notice how most of them are only accessible with an index, GetCharacter, GetControlSetPlug, GetCharacterPose, GetPose , GetMaterial, GetTexture, GetVideo
Here are some snippets for parsing:


def getVideoFiles(_Scene):
  VideoList=[]
  for currentIdx in range(_Scene.GetVideoCount()):
    VideoList.append( _Scene.GetVideo(currentIdx).GetFileName() )
  return VideoList

def getMaterials(_Scene):
  MaterialList=[]
  for currentIdx in range(_Scene.GetMaterialCount()):
    MaterialList.append( _Scene.GetMaterial(currentIdx) )
  return MaterialList

def getTextures(_Scene):
  TextureList=[]
  for currentIdx in range(_Scene.GetTextureCount()):
    TextureList.append( _Scene.GetTexture(currentIdx) )
  return TextureList

def getChildren(_Node):
  ChildrenList=[]
  for currentIdx in range(_Node.GetChildCount()):
    ChildrenList.append( _Node.GetChild(currentIdx) )
  return ChildrenList

So you can easily explore the graph of a top node:


def getAllNodes(_TopNode):
  import PyFBX
  import PyFBX.fbx
  theNodeList=[]

  def recurseExplore(currentNode):
    print currentNode.GetTypeName(), currentNode.GetName()

    for i in range(currentNode.GetChildCount()):
      cur = currentNode.GetChild(i)
      theNodeList.append(cur)
    recurseExplore(cur)

  recurseExplore(_TopNode)
  return theNodeList
fd = getHandle(r"\\...\Your\Path\FileName.fbx")
test = getAllNodes(fd.GetRootNode())

  • Avoiding traps and shortcuts:

Finally, you can of course look for a specific property.
You have in the doc all the methods for getting rotation or translation in Nodes.
Here is some snippets to get a value of a property by name, for example your own custom ones.


#currentNode= fd.GetRootNode()
value = currentNode.FindProperty(_PropertyName).Get()

But be very careful about not mixing both SDK in your mind!
FBX is NOT Motionbuilder, and experienced python coders in Mobu will find some similarities, but it differs a lot.
For example, forget about .Data member of a node to get its value, but use .Get() method.

Precisely, python in Mobu is called "Open Reality SDK" and is not FBX SDK, but the python package name in mobu, pyfbsdk, can be really confusing (FB prefix comes from FilmBox).
Here are some others examples:


if type(currentNode) == PyFBX.fbx.KFbxNode:
  value = currentNode.Translation.Get()
  print type(value), type(value[0]),value[0]
#<class 'fbx.fbxDouble3'> <type 'float'> 0.0
  value = currentNode.GetPreRotation(0)
  print type(value), value, value[0]
#<class 'fbx.KFbxVector4'> fbx.KFbxVector4(0.000000, 0.000000, 0.000000, 1.000000) 0.0

  • The bright and dark sides

You can open the scenes quickly, automatically.
You can safely open/close a lot of files like this.

I encountered some issues while saving cleaned file, so be sure to increment and not overwrite (as usual of course!)
You cannot blast, cannot plot, etc. ; well basically, you are not in mobu, nor using pyfbsdk module, but you can go very far (edit keys for example)

In my present experience with MotionBuilder, I used FBX SDK to:

- Scan all characters assets to parse a specific PreRotation joint value.
- Scan all scenes to list their content, sync it with asset management system.
- Done some stats about the artists worked, draw dependencies graph and find obsolete assets.

contact
profile
14Feb/130

Invisible chars could be a nightmare

... so avoid them at all costs. Show these shy bastards !

The main trick about whitespaces is about not being able to see them, or count them.

Because you could fail an == operator due to these discreet ghosts,
or also badly concatenate paths or batch commands,
please always configure your IDE to show whitespaces and co.

  • Tabs too !

Especially dealing with python, I found very usefull to display an arrow (Eclipse, NotePad++) or a line (Sublime) to easily count the level of indentation.
But it happens sometimes you have to merge foreign code, from others snippets, from internet, from samples in doc of your Maya/MotionBuilder/whatever, and you will have to conform everything.
So let's make a rule of showing every tabs as whitespaces (4) and display whitespaces.

  • my IDE Configuration reminders.
  1. Eclipse: Window > Preferences > General > Editors > Text Editors  "whitespace characters"
  2. Sublime: on file Preferences.sublime-settings
    "draw_white_space": "all",
    "tab_size": 4,
    "translate_tabs_to_spaces": true,
  3. MonoDevelop: Tools>Options: Text Editor>Markers and Rulers "Whitespace Markers"
  • Log it right!

    When displaying a variable, for a quick debug of a value, I prefer to always finish by an equal without spaces like that:

    print("MyVar ="+str(MyVar))

    It's a not nice to read, but displaying like that you will have a real certitude about the start of the value. Just after the marker "=". You cannot be more sure.

  • End of the line...

At last, I do not show EndOfTheLine carret. Because they are blurrying the view, especially from braces opening.
And I never encounter a case where it is helpful to display. Please sure if you have !

 

rss
16Dec/120

Relasing on iOS App Store

After releasing on Android, this time for my second game I decided to test Apple iOS system and realising also on the App Store.
My reasons were :
1) Great opportunity to learn about this OS, and on all this MAC-world I knew nothing about.
2) A lots of people are using theirs devices, so let's see if they are interested in my games.

Well, if I put aside the iPhone/iPad devices to have (mandatory to real debugging), this cost me a lot!
After rented an 2007 white macbook, I discovered that I need a very recent XCode (4.5) to target latest iOS6, so... I had to buy myself a recent macbook.
iOS6 ==needs==> XCode4.5 ==needs==> Lion ==needs==> recent MAC (see this webpage for compatibility)

But the nightmare was just started. As I already created my encryption keys in my old mac, and synchronised with my Apple Developer account...
That was a mess: it was bloody, it was insane... and if you want a step by step tutorial on how I finally managed to sign my App: I can't !
Honestly I really do not know how, after trying everything with that KeyHolder application, How I was successfull. It was just pure random...

I think I'm cursed, because I always have encryption-key issues !
Joke apart, this really freezed me about doing free trial versions, or with micro-payment management...
because that is as much keys to manage !

Filed under: Dev-illogics No Comments
16Dec/120

Failing @publishing on Android PlayStore: Signing package with correct encryption !

I record here my painful experience as releasing my second game on Google Android Market: the "Play Store".

I find that I signed my apk with a key with the latest JAVA 1.7 keytool, that used as crypto algorithm "SHA256withRSA"
I found this algorithm is not supported by Android Publishing, and provoke an error : "Package file was not signed correctly" when installed (so AFTER being purchased!)
The very epic fail is that the apk was authorized and google let me release on the market a deficient application !

In the rush, I uninstalled JDK 1.7, rolling back to 1.6, delete my key from my current keystore, and re-create one (same name) with "SHA1withDSA" encryption...
When I uploaded the new release, well encrypted this time, I get this FANTASTIC error just after the upload process in my dev web-page: "The apk must be signed with the same certificates as the previous version."
Ok then... so I can NOT fix my release because of my first wrong encryption (caused by a better default algorithm remember). Wonderfull.

After several mails unanswered and some forum topics , I decided to unpublished my whole game. And create a new one, same name, same description but with a good SHA1-encrypted first apk upload.
I hope my bad experience will be useful for someone...

Lesson learned ? Not use latest jdk Well understand all releasing/publishing processes and do not let any commands or steps as default value.
If you do not understand an argument, an option in Unity3d (which create your key user-frendly but with default values, do NOT ignore it, and give you TIME to read about it. Painful BUT Safe.

Little step by step processes:
1°) Install JDK 1.6, and to not forget to add this environment variables:
JAVA_HOME C:\Program Files\Java\jdk1.6.0_26
Path C:\Program Files\Java\jdk1.6.0_26\bin
2°) In CommandLine interpreter, type:
keytool -genkey -alias TheNameYouWant -keystore TheNameYouWantPrivateKey.store
3°) Verify your key like that:
keytool -v -list -keystore "TheNameYouWantPrivateKey.keystore"
4°) Check apk signature:
jarsigner -verify -certs -verbose D:\The\Path\To\Your\Application\MyAwesomeGame.apk

21Nov/120

Bloody Typing (my second smartphone game) is realeased !

Bloody Typing

My second game is realeased on both Apple and Android Market !

It is a typing game: you kill ennemies when you type their "names".
It is fun, it is arcade, and it WILL improve your typing skills !

Apple iOS App Store link    &     Android Play Store link
It is done in Unity3d, in c#.


It contains:

MODE STORY: unlock one by one all words categories to unlock next levels.
4 environments are currently infested by evil vampires !
Words categories are:
Graveyard: "Color","Sport","Common"
Misty Forest: "Animal","Stars","Plants"
Ruined Castle: "Body", "Bitter biters", "Phobia"
Mad Lab: "Science","Dino","Extinct species", "Medical"

Each story levels end by a BOSS, that can be only killed by a famous quote!
MODE ARCADE: Scoring challenge to type faster, making big scores on longer words.
TIMED CHALLENGES: Beat your score on a fixed duration, perfect mode for public transports!

To have tested my game myself, I can assure you it will really help you to type faster.
It helps memorize the keyboard layout, and on top of that you also learn some vocabulary !

Three difficulty modes assures that casuals could access every corner of the game,
by the way hard difficulty and scoring let the game "Easy to learn, Hard to master".

3 Virtual keyboard layouts are available right now: QWERTY, DVORAK, AZERTY
You also had the option to hide the keyboard, for a very challenging blind game !
Learning or improving how to type with fun ! Write enemies names to kill them.

blog
international
profile
blog
contact