Ok

En poursuivant votre navigation sur ce site, vous acceptez l'utilisation de cookies. Ces derniers assurent le bon fonctionnement de nos services. En savoir plus.

05/11/2009

Middleware day: UDK, Offload, OpenFeint...

Retour sur des choses concrètes: le monde des middlewares est en constantes évolutions, donc flash forward sur les petits nouveaux intéressants...

 

 

devHeaderBG.png

 

OpenFeint: On parlais l'autre jour dans les commentaires au sujet de l'évolution des jeux IPhone vers des modèles de plus en plus proche de l'éxpérience plateforme du Xbox Live : jeux en lignes, persistance des informations du joueurs, trophées ou achievements, réseau sociaux...

 

OpenFeint est donc un middleware dédié aux titres IPhone cherchant à implémenter une telle plateforme:

 

Want multi-player for your game? Want to see the latest OpenFeint features? Sign up today to get an early peak at the latest feature and help us get it ready for primetime.

 

Let your players instantly see who has bragging rights for your game. Now OpenFeint has a concise comparison screen between two players for every single game.


Stop building leaderboards for players that opt-out of OpenFeint. Now our leaderboards will work for everyone. And if a player who opted-out decides to opt-in, her scores will automatically be uploaded to her new profile. Magic!


By player request, players can now check out leaderboards, achievements, challenges, who's playing, and chat for your game from within any other OpenFeint enabled title.


[ OpenFeint ]

 

 

debuggerscreenshotsmall.png

 

Offload Codeplay: Ceux qui ont eu à travailler sur PS3 au plus bas niveau savent que c'est une plateforme qui est à la fois enrichissante et parfois très frustrantes. Pour en retirer le meilleur, il faut investir du temps et des moyens. Et ça commence par correctement exploiter les SPU.

 

Offload codeplay se propose de simplifier la démarche, en automatisant le port de certaines portions de codes sur SPU (d'une façon un peu similaire à d'autres outils d'optimisations parrallèles à la compilation). Je ne sais pas si ça fonctionne (j'en ferai bien une évaluation un de ces 4), mais ce processus pourrait permettre à des stusios moins bien doté en terme d'expertise technique de combler une partie du retard par rapport aux studios first party...

 

Easily offload code to SPU's. Codeplay Offload takes C++ code and puts it onto SPUs, letting programmers optimize code easily afterwards.

Offload: Community Edition is a development tool suite for writing real-world C++ applications for the Cell Broadband Engine™. Offload provides a simple method for offloading the parts of your code you want to execute on the SPE's of the Cell processor. All you need to do is mark up the code you want offloaded and Offload will automatically handle the scheduling, data transfer and execution.

The Offload tool suite includes everything you need; Compiler, Multi-Core Runtime Library, Source Level Debugger and an Eclipse IDE plugin for Linux developers or a Visual Studio plugin for games developers. All of these components can integrate seamlessly with your existing development tools.

 

[ Offload Codeplay ]

 

 

logo-udk.gif

 

Unreal Development Kit: Le petit dernier n'est donc autre que le fameux Unreal, donc pas la peine de faire une introduction ^_^

 

A vue de nez, le kit semble contenir la totalité de la toolchain plus l'interpréteur de script minus le code source. C'est donc un formidable outil qui devrais vous permettre d'évaluer Unreal, de prototyper gratuitement vos jeux, et éventuellement de comparer vos outils ou votre toolchain à moindre frais.

 

The world’s best game engine...
Now at your fingertips.



Experience the power and potential of Unreal Engine 3. Create amazing games, lifelike scenes and fantastic worlds... for free.



DK is Unreal Engine 3 – the complete professional development framework. All the tools you need to create great games, advanced visualizations and detailed 3D simulations. The best tools in the industry are in your hands.

 

[ UDK ]

 

De là à penser qu'Unreal Engine 3 arrive à maturité, il n'y a qu'un pas... UE4 bientôt ?

07/07/2009

Tools of The Trade: Introduction to virtual machines and scripting languages

De nos jours, l’utilisation des langages de script en cours de développent s’est grandement généralisé, pourtant il suffit de mentionner le besoin de debugger une machine virtuelle (ou VM de son petit nom) pour en général envoyer des frissons glacés dans le dos de tout programmeur un tant soit peu sensé.

 

Retour sur un sujet plus ou moins à la mode, et en tout cas membre de la trousse à outil moteurs des jeux modernes.

 

Le langage de script (et la VM généralement associées) participent de la notion de moteur « data driven » i.e permettre de séparer la logique du jeu (règle, scénarisation, entité, IA…) de la logique système (rendu, inputs, gestion des assets…). Dans un monde idéal, et si on pousse cette logique au bout, le langage de script doit permettre de décrire n’importe quel type de jeu avec le même moteur (ce qui reste à ce jour une complète utopie).

 

Pour ce faire, au fond, peu importe le langage. Javascript, Python, Lua, même Lisp, ils ont tous des avantages et des inconvénients qui doivent être mesuré au regard de considérations comme la productivité, la lisibilité, la maintenance ou les performances de la VM. Si l’idéal cité plus haut est l’objectif, plus le langage sera générique (et donc expressif), plus l’on pourra décrire de jeu différent.

 

Mais ce n’est parfois pas la bonne voie.

 

Après tout, à quoi bon prendre un langage de script si il faut qu’il soit « Turing Complete » ? Autant garder le bon vieux C ou C++. Du coup, il y a une autre approche qui tient plus du « Domain Specific Langage », ou DSL pour les intimes.

 

L’utilisation d’un langage de script propriétaire (Unreal Script par exemple) trouve une origine simple : pour développer un jeu moderne, on a besoin de manipuler un certain nombre de notions de haut niveau qui sont plus ou moins liés aux spécificités du moteur ou du game design, notions qui ne sont tout simplement pas disponible de façon native dans des langages plus bas niveau comme le C ou le C++, ou plus générique comme LUA.

 

Décrire de nouveaux types d’acteurs en les composant plutôt qu’en les dérivant, abstraire et nommer des notions spécifiques au gameplay, ajouter des meta data avec des mots clefs qui serviront pour l’édition ou le streaming ou la sérialisation ou la sauvegarde ou encore la réflexion, créer des structures de contrôles permettant de décrire des machine à état (Finite State Machine) ou un système de messagerie nativement supporté par le langage, etc etc…

 

Le langage propriétaire permet de façonner les scripts pour intégrer toutes les notions haut niveau spécifique au domaine du jeu, ce qui est parfois plus délicat avec des langages génériques. C’est en cela qu’ils tiennent plus des DSL, mais le jeu en vaut parfois la chandelle.

 

Dans tout les cas, on se retrouve dans la grande majorité des choix de langage à devoir générer du bytecode (version compilé des scripts), bytecode qui sera exécuté en temps réel dans le jeu par une (ou plusieurs) machine virtuelle. Et c’est là que la magie opère.

 

Comme son nom l’indique, la VM n’est ni plus ni moins qu’une émulation d’un système qui n’existe pas dans la réalité. Comme toute machine, la VM est souvent doté d’un certain nombre de registre, d’une stack d’exécution, d’un système d’interruptions, d’un gestionnaire mémoire (si les allocations sont permises) etc etc. A ce titre, le bytecode lui opère comme de l’assembleur classique que l’on exécute grâce à la VM.

 

Du coup, le bytecode devient lui-même donné, au même titre que toutes les autres assets (assets que le bytecode peut être amené à référencer dynamiquement). En séparant le code logique du code système, on augmente la stabilité et la réutilisabilité du moteur, et, si tout se passe bien, l’expressivité et la productivité du programmeur script. Et dans certain cas, on permet peut être même de supporter le live editing des scripts (après tout, le bytecode n’est qu’une donnée comme une autre).

 

Mais évidemment, tout cela à un prix. En premier lieu, le bytecode va obscurcir ce que le jeu doit faire du point de vue du moteur. Sans un bon outil de debug du script, bonjour les nuits blanches. La VM elle-même est un composant complexe, peut être l’un des plus délicat à l’œuvre dans le moteur, et debugger son fonctionnement est en général assez intimidant.

 

Le compilateur de script générant le bytecode, étant en principe dissocié des considérations hardwares de la machine, ne peut pas effectuer beaucoup d’optimisations efficaces (à la différence du compilateur C ou C++). Et d’ailleurs, ce n’est pas forcément son propos : la VM agissant comme une couche d’émulation sur le hardware, il ne faut pas en espérer grand-chose du point de vue des performances.

 

Du coup, il est parfois primordial de garantir que seul du « code froid » soit décrit en script (code exécuté peu de fois par frame), à l’opposé du « code chaud » (implémentation d’algorithmes faisant appels à des centaines de boucles par frames et/ou nécessitant l’accès à une masse de donnée conséquente). En script, on cherche plus à décrire des structures de données qu’à implémenter des algorithmes complexes.

 

Implémenter le pathfinding ou la gestion des listes de rendu en script n’est pas la meilleure façon d’obtenir le frame rate optimal.

 

Et je n’ai abordé là que les features les plus simples d’un langage de script.

 

Parfois, on veut pouvoir faire appel à une méthode moteur directement depuis le script, ce qui implique encore plus de travail en terme de RTTI. L’inverse est vrai aussi d’ailleurs, puisque l’on peut désirer que le moteur face appel à une routine script depuis le code C.

 

On peut souhaiter suspendre l’exécution d’un script, le temps que le moteur effectue un calcul complexe (collisions, IA avancée), puis reprendre le script là où on l’avait arrêté. Enfin, on peut aussi imaginer de décrire des portions de code parralèlisable, comme deux FSM travaillant sur deux entités différentes par exemple.

 

Pire, certains langages permettent d’insérer directement dans le script du code C ou C++, ce qui implique que le jeu doit être compilé en plusieurs passes (compiler le script, puis compiler le jeu).

 

Bref, si la VM part d’une bonne intention, c’est aussi probablement l’un des composants les plus problématique dans un moteur moderne, et en tout cas l’un de ceux qui peux monopoliser un temps de développement conséquent. Après tout, c’est le cœur du jeu en principe.

 

Personnellement, pour mes outils de prototypage, j’ai choisi une autre approche.

 

Je fais en sorte que le langage de script que je défini puisse être compilé en code natif C et C++. Ca me permet d’effectuer toute une passe d’optimisation et d’extraction d’informations en amont, tout en bénéficiant de la facilité de debug et de liberté d’implémentation dans le moteur.

 

Le code ainsi généré ne matche pas celui du script à 100%, les abstractions moteurs ne sont pas forcément équivalentes à celle de mon script, mais le process de conversion est suffisamment simple pour permettre d’annoter le code afin de permettre un debug tout à fait raisonnable, sans sacrifier mes performances.

 

Et vous, quel est votre approche ?

06/02/2009

Debug: Deja Insight

Ce n'est pas un secret que la condition d'une production réussi dépend majoritairement de la qualité du pipeline et des outils associées. Et de fait, beaucoup d'efforts sont consacrés aux exporteurs, éditeurs et autres outils de distribution des calculs.

 

Par contre, il y a un autre aspect qui est remarquablement peu souligné, c'est le debuggage. S'il est vrai que la plupart des constructeurs fournissent des outils pour debugger, profiler et tuner votre application, il n'en reste pas moins que ces outils interviennent souvent au plus bas niveau.

 

Nul autre mieux que vous sais ce que votre moteur est sensé faire, quels abstractions sont utilisés, comment votre VM fonctionne avec son language de script, comment est agencé votre mémoire etc etc.

 

Il y a une opportunité massive pour créer des outils qui font plus loin grace à la connaissance interne de votre architecture logique. Et c'est cette opportunité qui semble passé à la trappe un peu partout.

 

Comment d'outils de debug des allocations mémoires ont été écrites à l'arrache pour chasser un leak particulièrement vicieux ? Combien de façon de loguer des messages utiliser vous ? Humm ?

 

C'est dans ce point aveugle que s'engouffre Deja Insight:

 

What is Deja Insight? At its core, Insight is a logging system. But, Insight takes the ideas behind "logging" and pushes them to new and previously unexplored extremes. In addition to being an excellent logger, Insight includes functionality normally associated with debuggers, profilers, and various other development tools. All state information is tracked over the entire application execution history, thus allowing Insight to display the state of the heap or any instrumented object instance for any point in time. Furthermore, all data is cross referenced so Insight can, for example, display all log entries generated by a specific object instance.

 

[ Deja Tools ]

 

 

Heap_small.jpg

 

Alors, rendez vous service, et jetez un oeil à ces outils, ne serait ce que pour réver.

 

Et si vous n'êtes pas codeur, filer le lien à votre codeur préféré - si si, vous voyez très bien de qui je parle, celui que vous allez toujours voir quand vous avez un problème parce que même si c'est en dehors de son champ de compétence il trouvera une réponse pertinente. Toutes les boites en ont un (ou alors elles vont pas tarder d'avoir de gros problèmes).

31/01/2008

Adobe Thermo...

Je fais pas mal de Windows Forms en ce moment. Et dire que je ne regrette pas les MFC, c'est un euphémisme!
 
 
Alors cette news m'a attiré le regard. Adobe semble préparer une techno qui lorgne du coté des GUI dynamiques...
 
 
c93b92ac4c282a260cefa2acbf4480cb.jpg
 
 

"Thermo" is an upcoming Adobe product that makes it easy for designers to create rich Internet application UIs. Thermo allows designers to build on familiar workflows to visually create working applications that easily flow into production and development.

 

Features

  • Use drawing tools to create original graphics, wireframe an application design, or manipulate artwork imported from Adobe Creative Suite tools.

  • Turn artwork from Adobe Photoshop, Illustrator, or Fireworks directly into functional components that use the original artwork as a “skin”.

  • Define and wire up interactive behavior, such as what to do when a user clicks on something, without having to write code.

  • Easily design UIs that work with dynamic data, such as a list of contacts or product information, without having access to the actual data source. Design-time sample data can be used as a realistic placeholder when laying out an application, testing interactivity, and choreographing motion.

 

Applications created in Thermo are Flex applications that can be loaded directly into Flex Builder, providing a great roundtrip workflow for designers collaborating with developers. The designer's work can be incorporated directly into the production application with no loss of fidelity, and designers can continue to refine the design throughout the iterative development process.

 
 
 
 

14/01/2008

20 outils pour faire des jeux ^_^

We all play games, but not many of us have tried to actually create games. Have you been scared due to all the programming knowledge you think it takes? Well, thanks to the wonders of the web, now there are numerous tools that enable almost anyone to create a video game. You can give it a try using the 20+ tools we gathered!

 

[ Mashable via Intellident Artifice ^_^ ] 

18/11/2007

Google Code Hosting

Le plus cool avec l'open source, c'est toutes les solutions de hosting pour heberger le code et les autres ressources. On peut passer des heures sur certains services SourceForge !! ^_^
 
 
Depuis quelques temps, google a un service equivalent, qui en jette pas mal...
 
 

Google Project Hosting Features:

The interface is clean, compact and concise. It is missing the abundant clutter that permeates Sourceforge and most programming tools. Avoiding “too many options” is something Google understands well.

After the break, all the features of Google Code Project Hosting as of launch 2006/07/28.

Storage and Version Control

Google is offering 100 mb storage, and uses Subversion (currently kicking CVS’ butt) for version control. Subversion access control is tied to your Google Account (using a password that is generated by Google).
 
 
 

31/10/2007

TimeSnapper: Du bénéfice de s'auto-espionner

Si il y a une pratique que je conseillerai, c'est bien celle de garder un oeil sur sa gestion du temps. Quand les projets se multiplient, les sources de distractions deviennent de plus en plus nombreuses. Comme le temp est le seul capital que je possède réellement, savoir où passe ce temps est une préoccupation majeure.

 

Pour ce faire, j'ai plusieurs stratégie à ma disposition. Je divise par exemple mon temps de travail en module plus ou moins court que j'assigne à une seule tache donnée. J'utilise Basecamp pour planifier mes projets. 

 

Et enfin, je m'auto-espionne :-)

 

73241c02089f0a2ff2df6901013f0d0d.png

 

 

TimeSnapper est un petit logiciel très simple. Il permet de déterminer deux choses: visualiser une session de travail (mode movie), et établir une statistique des logiciels que vous avez utilisé. 

 

La première fois, on a souvent un choc quand on réalise le temps réel passé dans sa messagerie, usenet, firefox, ou msn! Quand on a comme moi que quelques soirées ou journées dans un week end par mois pour s'autoformer, développer, ou tenir un blog, on apprend vite à éduquer ses habitudes!

23/10/2007

ANTLR: Alternative à LEX and YACC

Lex, Yacc, Flex, Bison... Mais de quoi donc parle-t-il là ? :-)

 

Pour ceux qui ne connaissent pas, Lex et Yacc sont deux outils fondamentaux pour quiconques veut créer son propre langage. Dans les grandes lignes, il s'agit donc de définir une syntaxe et une grammaire, permettant de créer un compilateur afin de transformer un texte en un code "executable" (la définition du terme éxécutable est vaste).

 

C'est parfois pratique de définir son propre langage, pour les besoins d'un script, ou pour des applications très spécifiques (Domain Specific Language).

 

Le problème, c'est que ç'est une tache qui devient vite obscure, pour ne pas dire barbare. Lex et Yacc ont été écrits il y a des années par des programmeurs du genre barbu fortement porté sur Unix et bien avant la moindre UI avancée...

 

Autrement dit, à moins d'être un expert dans l'exercice, c'est une tache qui comporte un risque non négligeable d'échec.

 

Intervient ANTLR, une alternative sensé être beaucoup plus "user friendly". Tout de suite, avoir un IDE, ça aide...

 

 

06ab25a8172b8d25cf1ae4117f9721a4.jpg

 

ANTLR, ANother Tool for Language Recognition, is a language tool that provides a framework for constructing recognizers, interpreters, compilers, and translators from grammatical descriptions containing actions in a variety of target languages.

 

ANTLR provides excellent support for tree construction, tree walking, translation, error recovery, and error reporting.

 

ANTLR has a sophisticated grammar development environment called ANTLRWorks, written by Jean Bovet. 

 

[ ANTLR

04/05/2007

Turtle 4...

Ces dernières années, le rendu temps réel est devenu un processus incroyablement compliqué. De fait, nombres d’algorithmes de rendu avancées sont élaborés autour d’une idée simple : pré calculer ce qui est long, coûteux ou compliqué, et « décompresser/reconstruire » en temps réel.

 

Du coup, le pipeline de production des données a pris une importance cruciale. La gestion des temps de pré calculs, leurs stabilités et leurs robustesses, la distribution de la charge de calcul entre plusieurs machines, tous ces aspects sont devenus de plus en plus complexes.

 

Parmi les outils que j’ai vu prendre appui dans les studios ces derniers temps, Turtle est un exemple parfait. Turtle est un outil de rendu et de pré calcul intégrant un grand nombre de features aujourd’hui primordiale dans de nombreuses productions.

 

Turtle 4 is the only product on the market that combines advanced rendering technology with sophisticated baking functionality in a single integrated toolset.

 

By integrating the baking functions into the core of Turtle, standard rendering and baking can be handled in the same way. For you, this means a better workflow while baking and the ability to bake with all of Turtle’s render features.

 

Turtle Render Overview

  • Final Gather and Global Illumination
  • “Indirect Lights” to control the shape of the GI-solution
  • Advanced pass system – any shader can be a seperate pass
  • Ambient and Reflection Occlusion
  • HDRI and Image Based Lighting
  • New 3D Motion Blur
  • Glossy Refractions & Reflections
  • Adaptive pre-tessellation of displacement maps
  • Micropolygon displacement at render-time
  • Render polymeshes as subdivision surfaces
  • New Unified Super Sampler: Combine 3D mb/glossy/DOF as efficiently as possible
  • Anti-aliasing with advanced edge tracing
  • Direct compatibility with ZBrush displacement and normal maps
  • Lua script shaders
  • Environment Map Camera
  • Output shaders
  • Realistic material shaders (such as Oren Nayar)
  • Fast subsurface scattering with support for blocking geometries
 

Turtle Baking Overview

  • Renderview Baking
  • Renderview Surface Transfer
  • Directional Occlusion Mapping
  • Radiosity Normal Mapping
  • Spherical Harmonics
  • Polynomial Texture Maps
  • Programmable baking
  • Batch baking with adaptive texture resolutions
  • Different render passes, including albedo
  • Custom shader for all baking needs

 

 

[ Turtle ]

 

medium_ninjacastle.gif

27/03/2007

FreeSound

Pour les développeurs amateurs, la principale difficulté est l’obtention des ressources. Texture, modèle 3D, sprites, animation, toutes ces données sont difficiles à obtenir sans l’aide d’un artiste dédié. Mais il y a pire : le son !

 

Nombreux sont ceux qui sous estime l’importance de l’ambiance sonore et des effets associe. Essayez donc de jouer à un jeu sans le son, et il est probable que dans 90% des cas, le jeu deviennent lent, ennuyeux ou plat.

 

Il y a une bonne raison a cela : Le son est probablement le vecteur émotionnel auquel nous sommes le plus sensible. Pour preuve, il suffit de quelques arrangements pour induire la peur, la mélancolie ou la joie de vivre.

 

Comparativement, l’image peine à trouver la bonne composition, le bon cadre ou le bon éclairage. Et la ou le cinéma peut se permettre de jouer sur le montage, la direction de la photo ou l’influence de la lumière, un jeu en temps réel n’a pas ce luxe.  De même, la lecture requiert un bagage intellectuel conséquent avant de pouvoir transmettre une quelconque émotion.

 

Bref, le son, c’est important (voir majeur). FreeSound est un site se proposant de regrouper samples et compositions pour les mettre a disposition des musiciens et autres programmeurs de jeux, avec la licence Creative commons.

  

The Freesound Project aims to create a huge collaborative database of audio snippets, samples, recordings, bleeps, ... released under the Creative Commons Sampling Plus License.

 

The Freesound Project provides new and interesting ways of accessing these samples, allowing users to:

 

  • browse the sounds in new ways using keywords, a "sounds-like" type of browsing and more
  • up and download sounds to and from the database, under the same creative commons license
  • interact with fellow sound-artists!
 
We also aim to create an open database of sounds that can also be used for scientific research. Many audio research institutions have trouble finding correctly licensed audio to test their algorithms. Many have voiced this problem, but so far there hasn't been a solution.
 
[ FreeSound

 

25/03/2007

Simple XNA Assets Builder

Ce qu’il y a de bien avec XNA, c’est le très haut niveau d’intégration du « Content Pipeline ». Il faut à peu près 5 minutes pour écrire un programme permettant d’afficher un modèle FBX à l’écran.

 

La ou le bas blesse, c’est que les assets sont convertis à la compilation du programme. En d’autres termes, pour mettre a jour le fichier FBX, il faut recompiler le programme. O_0

 

Bref, inutile de dire à quel point c’est absurde.

 

En farfouillant un peu, on comprend vite que les assets sont convertis par msbuild.exe à l’aide d’un fichier de configuration xml csproj. La suite est donc logique, pour convertir des assets, il suffit de générer le bon fichier xml et de lancer msbuild.

 

C’est le propos de ce petit outil en ligne de commande. Il permet de convertir simplement une donne compatible XNA (FBX, X, BMP, TGA…). Il suffit de lancer SAB MonMesh.fbx, et voila.

 

Avec ça, c'est un bon debut pour batcher vos assets et donc de les éditer sans avoir a recompiler votre programme XNA :-)

 

TELECHARGEMENT: SAB.RAR 

25/01/2007

L’évolution de LUA

LUA est peut être devenu le langage de script de référence pour un grand nombre de studio, des petits indépendants au plus gros éditeurs. Ce document explique l’évolution de ce langage fourre-tout, et dessine les futurs évolutions de cet outil de plus en plus fondamental.

 

Lua is a scripting language born in 1993 at PUC-Rio, the Pontifical Catholic University of Rio de Janeiro in Brazil. Since then, Lua has evolved to become widely used in all kinds of industrial applications, such as robotics, literate programming, distributed business, image processing, extensible text editors, Ethernet switches,bioinformatics, finite-element packages, web development, and more. In particular, Lua is one of the leading scripting languagesin game development.

 

Lua has gone far beyond our most optimistic expectations. Indeed, while almost all programming languages come from NorthAmerica and Western Europe (with the notable exception of Ruby, from Japan), Lua is the only language create  in a developing country to have achieved global relevance.

 

From the start, Lua was designed to be simple, small, portable, fast, and easily embedded into applications. These design principles are still in force, and we believe that they account for Lua’s success in industry.

 

The main characteristic of Lua, and a vivid expressionof its simplicity, is probably that it offers a single kind of data structure, the table, which is the Lua term for an associative array. Although most scripting languages offer associative arrays,in no other language do associative arrays play such a central role.

 

Lua tables provide simple and efficient implementations for modules, prototype-based objects, class-based objects, records, arrays,sets, bags, lists, and many other data structures.

 

  [ The evolution of LUA ] [ via Lambda The Ultimate ]

 

medium_lua.gif