Es mostren els missatges amb l'etiqueta de comentaris tutorial. Mostrar tots els missatges
Es mostren els missatges amb l'etiqueta de comentaris tutorial. Mostrar tots els missatges

dilluns, 14 de novembre del 2011

Crear un motor 3D Multiplataforma - Motor Antagonista [ I ]

Bones.

Començo el meu "diatari" sobre com vaig fent un motor multiplataforma (Linux i Windows i, opcionalment, Android i, opcionalíssimament, iOs).

Aquest motor serà bàsicament una reescriptura del motor que hem fet al màster (mirar entrada anterior), però "ben fet" (no és que digui que el motor que hem fet està mal fet [fet, fet, fet, parèntesi, parèntesi, parèntesi], sinó que, al anar aprenent no teníem la visió de conjunt necessària per fer certes coses, que ara si que tinc).

En aquest projecte em proposo diverses coses, entre elles aprendre C++11 (la raó principal per la que no faci el joc en Java) i distreure'm mentre no trobi feina.

Els objectius principals seran tenir la mateixa funcionalitat que al master, més la capacitat de ser multiplataforma (i alguna millora gràfica gràcies a no dependre del DirectX, visca OpenGL!!).


  • Interfície amb el sistema operatius via SDL 1.3 (té llicència ZLib, la qual em permet fer-ne el que em doni la santa gana).
  • Gràfics 2D via SDL o OpenGL (ES?).
  • Gràfics 3D via OpenGL amb diferents perfils "intercanviables".
    • Perfil bàsic: 1.5 o ES 1.1
    • Perfil normal: 2.0
    • Perfil avançat: 4.2 (3.3? Qui el vol?)
  • Configuració amb fitxers YAML
  • Scripting via LUA (no he aconseguit fer funcionar Boost::Python, ja que té un memory leak gros com ell sol, així que tornem al lua).
  • So via SDL (amb l'ajuda del mixer per llegir "ogg"s. Bass costa 2.700€, així que queda descartat.
  • Física via Bullet (si hi ha temps per fer-la). Sé que n'hi ha d'altres, però ja estic una mica familiaritzat amb Bullet, així que no hi ha d'haver masses problemes al respecte.
  • DCCs Blender i Gimp.
  • D'animacions m'agradaria crear el meu pròpi sistema, però sempre hi ha Cal3D allà per si no me'n surto.
No sé si m'he deixat res, però és igual.

Com a eines uso Mercurial com a control de versions (és distribuit i mola lo suficient) i Eclipse com a IDE.

De moment simplement m'he creat una carpeta a l'Ubuntu controlada amb el Hg (mercurial) de nom "Motor Antagonista" i a dintre hi aniré creant els diferents projectes de l'Eclipse.

Començo per el base.

Nou projecte -> C++ Static Library -> Toolchain "Cross GCC"

A partir d'aqui el primer que faig és retocar les configuracions (botó dret sobre el projecte, propietats al final de tot). Dintre de "C/C++ Build" -> Settings et deixa mirar les configuracions. Per defecte n'hi ha 2: "debug" i "release"; els hi canvio el nom per "GNU|Linux Debug" i "GNU|Linux Release" (per després crear les configuracions corresponents per windows) i n'afegeixo una de nova "Production" (cópia de Release) a Release li engego les opcions de debug (així que queda optimitzada a tope i amb debug, sent una build "intermitja"). També defineixo els símbols "_DEBUG" (per la build de debug) i "NDEBUG" (per la resta), per si de cas. Tot això s'ha de afegir 2 vegades, una a la configuració del "gcc" i l'altra a la del "g++" (les 2 són a "Settings", on he dit abans). Finalment, i per totes les configuracions, afegeixo la opció "-std=c++0x" a la línea de comandes de "g++" per que em compili amb les coses del C++11 (bé, amb les coses "provisionals", ja que encara no tinc la versió de gcc que compila C++11 correctament).

 

Després de fer tot això, em dedico a afegir llibreries.

Creo una carpeta "include" i a dintre i foto tots els arxius de "GLI"[0.3.0.3] i "GLM"[0.9.2.7], trets de http://www.g-truc.net/ (nota: els 2 tenen un "dummy.cpp" que cal esborrar, aquestes llibreries són "només capçaleres" i per tant relativament fàcils de incloure | nota2: GLI té un memory leak. No és molt preocupant i ja he avisat de la seva existència i l'autor diu que a la pròxima versió estarà corregit).

Afegeixo també els includes de "yaml-cpp" i creo una carpeta nova "lib/GNULinux" on hi afegeixo la llibreria estàtica (libyaml-cpp.a). Faig el mateix amb les llibreries de LUA (5.1), de SDL (1.3) i Luabind (0.9.1)** (aquestes embruten considerablement la carpeta "include", espero que això no em passi factura després)*.

Una bona estona me la passo fent "./config; make; make install;", per cert (a veure en windows com ho faig tot això).

Afegeixo per acabar "http://sourceforge.net/projects/nvwa/" i GLEW. Finalment faig una carpeta anomenada "Licences" on vaig copiant totes les llicències per tenir-les controlades.

I de moment això és tot. Després intentaré configurar el Mercurial correctament.

*: Per lua, només cal "lua.h" "lua.hpp" "luaconf.h" "lualib.h" i "lauxlib.h"
**: Per al luabind... és més fàcil passar de "bjam"s i limitar-se a copiar tot el codi a dintre del projecte.

dissabte, 5 de febrer del 2011

Suficiència d'OpenGL ES 2.0 Part II (Framebuffers)

Continuant el post anterior, i una mica en la mateixa línea, avordem un tema "important", sobretot si es tracta de fer efectes abançats: Els Framebuffers.

D'acord, primer de tot, necessitem entendre una mica de teoria sobre on dibuixem les coses i quins espais de memòria ens són disponibles.

Aquests espais de memòria als que em refereixo són els buffers de renderització; allà on guardem informació de color, profunditat, etc... En l'exemple més senzill de OpenGL, disposem de 2 buffers: el front buffer i el back buffer. Aquests buffers guarden informació de color (RGB i a vegades un Alpha que indica "quantitat d'opacitat"), el front buffer es mostra per pantalla mentre que el backbuffer és on dibuixem. Un cop acavem de dibuixar un frame, intercanviem el front i el back, així no es veu mai "mentre dibuixem" sinó que veiem sempre dibuixos sencers.

Addicionalment se li poden afegir 2 buffers: el depth buffer i el stencil buffer. Sobre l'stencil no en parlarè gaire, per no dir gens; mai l'he fet servir i no sé ben bé per a què serveix, però el depth buffer és molt útil. Aquest búffer de profunditat (depth) guarda la profunditat on se suposa que cada pixel està pintat. Això ens permet un mètode senzill per renderitzar les coses que estàn més a prop: si anem a dibuixar una cosa més llunyana que la que hi ha dibuixada, no la dibuixem. Si és més propera, la dibuixem i actualitzem el depth.

Molt bé, ara veiem que per renderitzar necessitem un buffer de color i, opcionalment, un de profunditat i/o un de stencil. Per defecte normalment en tenim un de cada (més el front buffer), però a vegades ens interessa dibuixar coses "fora de la pantalla". Per exemple: Imaginem-nos que estem en un joc, en una sala amb càmeres de videovigilància. Aquestes càmeres mostren, en temps real, altres habitacions. Per a acomplir aquest efecte, podem renderitzar aquestes altres habitacions en una textura cada una i, posteriorment, renderitzar la textura a la càmera.

Això és el que es coneix com a off-screen rendering, i consisteix en crear un "Framebuffer object", que és una col·lecció de buffers de distínta índole, i dir-li a l'openGL que renderitzi sobre aquest. Els buffer poden ser de 2 maneres: o bé una textura, o bé un Renderbuffer. La diferència essencial és que no tenim fàcilment accessible la informació dels Renderbuffers, mentre que una textura la tenim sempre a mà.

Codi:

//------------------------------------------------------------

//Primer creem els Renderbuffers, que poden servir com a color,
//profunditat o stencil
GLuint renderbuffers[2];
glGenRenderbuffers(2, &(renderbuffers[0]));

//activem el renderbuffer
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffers[0]);

//definim com és el renderbuffer
glRenderbufferStorage(GL_RENDERBUFFER,
                      format,         //Format intern
                      width, height); //dimensions

// Formats estàndard:
// GL_RGB565, GL_RGBA4, GL_RGB5_A1,         -- Color
// GL_DEPTH_COMPONENT16, GL_STENCIL_INDEX8
//
// Formats amb l'extensió GL_OES_rgb8_rgba8
// GL_RGB8_OES, GL_RGBA8_OES
//
// Formats amb l'extensió GL_OES_depth24 / GL_OES_depth32
// GL_DEPTH_COMPONENT24_OES, GL_DEPTH_COMPONENT32_OES
//
// Formats amb l'extensió GL_OES_stencil1 / GL_OES_stencil4
// GL_STENCIL_INDEX1, GL_STENCIL_INDEX4
//
// Formats amb l'extensió GL_OES_packed_depth_stencil
// GL_DEPTH24_STENCIL8_OES

// Les dimensions cal que siguin iguals o menors a
// GL_MAX_RENDERBUFFER_SIZE
 
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffers[0]);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, 800, 600);
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffers[1]);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, 800, 600);



//Crear identificadors de frambuffer,
// de forma similar a les textures:
GLuint framebufferID;
glGenFramebuffers(1,                //nombre d'id a generar
                  &framebufferID);

// Activar el framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, framebufferID);

// Aquesta funció ens serveix tant per especificar les propietats
//del framebuffer (punts d'anclatge) com per indicar que volem
//que es renderitzi a aquest. Per tornar a renderitzar als buffer
//"normals" hem de fer:
glBindFramebuffer(GL_FRAMEBUFFER, 0);

// Lligar un renderbuffer al framebuffer
gl_FramebufferRenderbuffer(GL_FRAMEBUFFER,
                           punt_d_anclatge,
                           GL_RENDERBUFFER,
                           renderbufferID); //0 per no anclar-hi res

// Punts d'anclatge:
// GL_COLOR_ATTACHMENT0, GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT
// Com a nota important, saber que en OpenGL estàndard hi ha més
//punts d'anclatge, concretament, els de color 1, 2, 3... Això 
//serveix per poder pintar a més d'un lloc a la vegada, i ho 
//explicarè als fragment shaders.

//Continuant amb l'exemple
gl_FramebufferRenderbuffer(GL_FRAMEBUFFER,
                           GL_COLOR_ATTACHMENT0,
                           GL_RENDERBUFFER,
                           renderbuffers[0]);
gl_FramebufferRenderbuffer(GL_FRAMEBUFFER,
                           GL_DEPTH_ATTACHMENT,
                           GL_RENDERBUFFER,
                           renderbuffers[1]);
gl_FramebufferRenderbuffer(GL_FRAMEBUFFER,
                           GL_STENCIL_ATTACHMENT,
                           GL_RENDERBUFFER,
                           0);

//Comprovar errors
GLenum result = glCheckFramebufferStatus(GL_FRAMEBUFFER);

//Result pot ser:
// GL_FRAMEBUFFER_COMPLETE -- OK
// GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT -- Algun error
//                                      -- en algun anclatge.
// GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT -- No hi ha cap
//                                              --anclatge
// GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS -- Anclatges amb
//                                      -- dimensions diferents
// GL_FRAMEBUFFER_INCOMPLETE_FORMATS -- Algun format no suportat
// GL_FRAMEBUFFER_UNSUPPORTED -- Combinació d'anclatges i formats
//                            -- no suportats.


// Com s'ha dit anteriorment, la gràcia de tot plegat és la de
//dibuixar a una textura, per això anclem una textura al 
//framebuffer com ho anclavem un renderbuffer
glFramebufferTexture2D(GL_FRAMEBUFFER,
                       punt_d_anclatge, //Només color o profunditat
                       target,
                       texture_id,
                       0); //nivell mipmap.

// Targets:
// GL_TEXTURE_2D, GL_TEXTURE_CUBE_MAP_POSITIVE_X, ...

//EXTRA: Textures 3D
glFramebufferTexture3DOES(GL_FRAMEBUFFER,
                          GL_COLOR_ATTACHMENT0, //No depth
                          GL_TEXTURE_3D_OES,
                          texture_id,
                          0, //nivell mipmap.
                          z_offset); //profunditat

// Finalment i nogensmenys important:
glDeleteRenderbuffers(2, &(renderbuffers[0])); //La pots liar si 
                           //encara està en ús en un framebuffer
glDeleteFramebuffers(1, &framebufferID);

//------------------------------------------------------------


Per altra banda, i de manera molt ràpida, m'agradaria introduir una altra funcionalitat molt similar, encara que amb un rendiment més baix (tot i que a vegades necessitem fer-la servir en front de framebuffers). Aquesta és simplement copiar el contingut de l'actual framebuffer (el back buffer en cas de no tenir un framebuffer activat) a una textura.


//------------------------------------------------------------
glCopyTexImage2D(target, // GL_TEXTURE_2D, GL_TEXTURE_CUBE...
                 0,      // nivell mipmap
                 texture_format,
                 x, y,   // On de la pantalla comencem a copiar
                 w, h,   // tamany de la textura / àrea a copiar
                 0);     // border

// El format pot ser
// GL_RGBA, GL_RGB, GL_LUMINANCE, GL_LUMINANCE_ALPHA, GL_ALPHA
// A més, en OpenGL estàndard (MAI en ES) es pot copiar el depth
// buffer si s'especifica el format
// GL_DEPTH_COMPONENT


// Per copiar a una subregió d'una textura
glCopyTexSubImage2D(target, 0, texture_format,
                    xoffset, // coordenades on començar a copiar
                    yoffset, // a la textura
                    x, y, w, h, 0);

// Les 3D només poden fer servir la "SubImage"
glCopyTexSubImage3DOES(target, 0, texture_format,
                       xoffset, yoffset,
                       zoffset, // coordenada z a on es copia
                       x, y, w, h, 0);
//------------------------------------------------------------

 I fins aqui tots els temes relacionats amb renderitzar múltiples vegades un frame. Amb això es poden aconseguir efectes xulos, ja sigui:
Glow, Blur, HDR, Ombres (Shadowmap)...

divendres, 28 de gener del 2011

Guia ràpida d'OpenGL (ES)

Molts bons el que sigui.

He estat llegint un llibre d'OpenGL ES 2.0, la versió per sistemes integrats d'OpenGL. La gràcia (explicat ràpidament) de la versió ES és que és essencialment el mateix que la versió "mare" però simplificada, o sigui, amb totes les funcions "redundants" eliminades. Un programa fet doncs, en OpenGL ES és pràcticament un 100% portable a OpenGL normal (excepte que les extensions són diferens, petits detallets).

Què vui fer jo ara? Escriure'm un resumet del llibre, ja que l'he de tornar a la biblioteca eventualment.

Així que, què, saveu com va OpenGL, més o menys? El de veritat, el dels Shaders, eh!

Essencialment, el que un ha de fer perque es dibuixi alguna cosa és especificar un seguit de geometria (Triangles, línees o "punts"), que recorre un seguit de transformacions fins que són pintades. Quines transformacions?

Pipeline:
  1. Vertex Shader: Agafa paràmetres que li dónes directament i les passes a la següent fase. El paràmetre important a passar són les coordenades de "clip" (coordenades de retallada), que essencialment són les coordenades dels vertexos normalitzades.
  2. Rasterització: Es divideix en diferents fases, l'objectiu és trobar els fragments que faràn el "Fragment Shader.
    1. Divisió de perspectiva: S'agafen les coordenades de clip (xc, yc, zc, wc) i es transformen a coordenades de Device (xd, yd, zd) = (xc/wc, yc/wc, zc/wc). Aquelles coordenades fóra del rang [-1, 1] són descartades.
    2. Transformació de Viewport: Transforma les anteriors coordenades a coordenades de pantalla (les x i y) i també transforma proporcionalment les z a un rang que tu mateix has especificat.
    3. Rasterització: Un cop tens cada vèrtex situat en un pixel, "s'envien" tots els pixels intermitjos al Fragment Shader. Prèviament es fa el culling (Eliminar els triangles que estiguin mirant en contra la càmera.
  3. Fragment Shader: Agafa paràmetres interpolats del Vertex Shader i els utilitza per definir el color del fragment. A més disposa de les coordenades del fragment en pantalla, les coordenades dels point-sprites, i si la cara mira o no a càmera.
  4. Operacions per fragment: També n'hi ha vàries.
    1. Scissor Testing: Fa que només es pintin pixels definits en un rectangle definit.
    2. Stencil Test: No ho he entès mai, ni mai he vist cap exemple de ningú fent-ho servir.
    3. Depth Test: Decideix si un fragment es dibuixa o no segons la seva profunditat.
    4. Blending: Barreja el color amb el color anterior.
    5. Dithering: Tampoc ho he fet servir mai.
Coses Ràpides que cal saber de shaders:

Els Shaders tenen 3 tipus de "variables globals":
  • Uniform: Són paràmetres que comparteix tota la geometria, on la geometria és allò que dibuixes d'un cop, un model, vamos. Exemples serien les matrius de transformació, les llums, etc...
  • Attribute: Són paràmetres per vèrtex. Per exemple Coordenades-Objecte, Normals, coordenades de textura i altres.
  • Varying: Aquestes s'escriuen al Vertex Shader i es llegeixen al Fragment Shader. És la manera de passar informació de l'una a l'altre.
Cada shader es defineix amb una funció "main" de tota la vida.

Com es creen els shaders? "Fàcil"!

Primer cal crear ambdós shaders:


//---------------------------------------------------------
GLenum type; //GL_VERTEX_SHADER | GL_FRAGMENT_SHADER

GLuint shader_object = glCreateShader(type);

glShaderSource( shader_object, 
                count,   // número d'strings que passes
                strings, // array de char*, amb el codi
                lenghts);// array de int, amb la llargada de cada
                         // string. Si és NULL se suposa que
                         // acaven en '\0'. Si un element és
                         // negatiu se suposa que aquell acava
                         // en '\0'.

glCompileShader( shader_object );
glGetShaderiv( shader_object,
               param,    // GL_COMPILE_STATUS
                         // GL_DELETE_STATUS
                         // GL_INFO_LOG_LENGTH
                         // GL_SHADER_SOURCE_STATUS
                         // GL_SHADER_TYPE
               &result);

//Comprovar que el compile status sigui true, sino agafar el log
if(!result) {
  int len;
  glGetShaderiv( shader_object, GL_INFO_LOG_LENGTH, len);

  char* infoLog = new char[len];
  glGetShaderInfoLog(shader_object,
                     len,
                     NULL, //llargada, com que ja la savem, null
                     infoLog);

  printf("Log: %s", infoLog);
}

//Eventualment
glDeleteShader(shader_object);
//---------------------------------------------------------

Un cop creats els shaders (Vertex i Fragment), cal lligar-los:

//---------------------------------------------------------
GLuint vertex_shader, fragment_shader;

GLuint shader_program = glCreateProgram();

glAttachShader(shader_program, vertex_shader);
glAttachShader(shader_program, fragment_shader);

//glDetachShader( ... )
glLinkProgram(shader_program); //Fer el link de les "coses"

//glValidateProgram(shader_program) serveix per validar i agafar
//info per el log, però és lent i només s'ha d'usar per "debugar"
//i buscar errors.

glProgramiv( ... ); 
//similar al de shader objects, amb paràmetres
//GL_ACTIVE_ATTRIBUTES             --nombre d'atributs
//GL_ACTIVE_ATTRIBUTES_MAX_LENGTH  --len de l'atribut més llarg

//GL_ACTIVE_UNIFORMS               -- "
//GL_ACTIVE_UNIFORMS_MAX_LENGTH    -- "
//GL_ATTACHED_SHADERS              -- nombre de shaders afegits
//GL_DELETE_STATUS                 -- si es vol eliminar
//GL_INFO_LOG_LENGHT               --
//GL_LINK_STATUS                   -- si s'ha linkad bé
//GL_VALIDATE_STATUS               -- si ha validat

glGetProgramInfoLog( ... ); // com el dels objectes.

//Eventualment
glDeleteProgram( shader_program );
//---------------------------------------------------------

Un cop tenim els shaders compilats i tot, cal agafar les localitzacions dels "uniforms". Amb les localitzacions serem capaços d'escriure aquests valors. "Passar-los" al shader.

Hi ha una funció, glGetActiveUniform, que serveix per saber quina "uniform" hi ha en cada posició i quin tipus té, però aquesta no és la manera normal de buscar-ho (ja sabem quines "uniform" hi ha en el shader, l'hem escrit nosaltres!).

GLint uniformLocation = glGetUniformLocation( shader_program, "nom_uniform");

Així tenim la localització, que podem fer servir així:

void glUniform1f(GLint location, GLfloat v0);

Coses a tenir en compte: Per fer-ho servir el shader ha d'estar "actiu" (més, després) i tmb que n'hi ha moltes, de funcions, i es troben a [1].

Els atributs s'agafen de manera similar.

GLint attributeLocation = glGetAttributeLocation( shader_program, "nom_attribute");

Com preparar la "Geometria"

No m'estaré de métodes arcaics per definir geometria i aniré directament al métode més "eficient".

Conceptualment es tracta de pujar a memória VRAM, memòria de la targeta gràfica, totes les dades necessàries per dibuixar la geometria, i posteriorment cridar-la quan calgui. L'algoritme vé a ser el següent:
  1. En una fase de "init", definir i omplir els buffers.
  2. En una fase de "render":
    1. Activar el Shader que s'utilitza ( glUseProgram(program_id); ).
    2. Definir totes les Uniforms que fa servir el Shader
    3. Fer la crida de "Pinta la geometria".
  3. En una fase de "cleanup" eliminar els buffers.
Què són i com funcionen els buffers? Imaginem-nos que volem renderitzar el següent cuadrat:

[0] ----- [1]
 |         |
 |         |
 |         |
[2] ----- [3]

Aquest té 4 vertexos, amb coordenades [0 => (0, 5, 0)], [1 => (5, 5, 0)], [2 => (0, 0, 0)], [3 => (5, 0, 0)], i amb coordenades de textura [0 => (0, 1)], [1 => (1, 1)], [2 => (0, 0)], [3 => (1, 0)]. Podríem fer un array de la següent manera:

float vertex_data[] = {0, 5, 0, 0, 1, //primer vertex
                       5, 5, 0, 1, 1,
                       0, 0, 0, 0, 0,
                       5, 0, 0, 1, 0  //últim vertex
                      };

Per indicar com s'ha de dibuixar, es pot fer indexat. Primer de tot, cal dividir la geometria en triangles, podem fer 2 triangles en aquest cas: el {0, 2, 1} i el {1, 2, 3}. Així li podem indicar a OpenGL que dibuixi la geometria {0, 2, 1, 1, 2, 3}.

uint16 index_data[] = {0, 2, 1, 1, 2, 3};

Abdós buffers es poden guardar a VRAM i posteriorment activar i utilitzar.

Creant Buffers

Aqui expliquem com crear els buffers, o sigui, la part de "init"


//----------------------------------------------------------
GLuint bufferIds[2];

glGenBufferData(2, bufferIds); //Generem 2 buffers

glBindBuffer(GL_ARRAY_BUFFER, bufferIds[0]); //activem el buffer
                                             //com a buffer de
                                             //dades
glBufferData(GL_ARRAY_BUFFER,   
             numVert * sizeVert, //Tamany que tindran les dades
             vertex_data,        //Dades
             GL_STATIC_DRAW);    //Mode. Li dóna pistes a la gràfica
             //GL_STATIC_DRAW  -- escrit 1, usat molt
             //GL_DYNAMIC_DRAW -- escrit molt, usat molt
             //GL_STREAM_DRAW  -- escrit 1, usat poc

           //GL_ELEMENT... serveix per indexos
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufferIds[1]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, index_data, GL_STATIC_DRAW);

//----------------------------------------------------------
Com a nota adicional, sempre es pot re-crear les dades amb "glBufferData" o canviar una part amb "glBufferSubData" (tot i que si l'has creat amb "STATIC_DRAW" puteges al driver).

Emprant Buffers

Aqui expliquem com renderitzar usant buffers, o sigui la crida a "pintar geometria".


//-------------------------------------------------------------

  //Activem el buffer de vertexos que farem servir
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferId);

  //Activem el buffer d'indexos que farem servir
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vertexBufferId);


  //activem els atributs del shader
glEnableVertexAttribArray(position_attrib); 
glEnableVertexAttribArray(texcoord_attrib); 

glVertexAttribPointer(position_attrib,  // atribut que usem
                      3,         //nº de dades a l'atribut
                      GL_FLOAT,  //tipus de dades
                      GL_FALSE,  //normalització
                      vtxStride, //distancia entre un vtx
                                 //  i el següent
                      0);        //distancia entre l'inici del
                                 //  vtx i les dades en qüestió.     

     /**********************************************************\
     *   Sobre la normalització, serveix per quan usem un tipus *
     * de dades diferent a float i volem que al passar-les a    *
     * float OpenGL les transformi al rang [-1, 1] o no.        *
     \**********************************************************/

     /**********************************************************\
     *   Sobre els tipus de dades, poden ser GL_BYTE,           *
     * GL_UNSIGNED_BYTE, GL_SHORT, GL_UNSIGNED_SHORT, GL_FIXED, *
     * GL_FLOAT, GL_HALF_FLOAT_OES/GL_HALF_FLOAT_ARB            *
     \**********************************************************/

glVertexAttribPointer(texcoord_attrib,

                      2,        
                      GL_FLOAT, 

                      GL_FALSE, 

                      vtxStride,
                      sizeof(float)*3);

glDrawElements(GL_TRIANGLES,      //Tipus de primitiva
               6,                 //nº de indexos
               GL_UNSIGNED_SHORT, //tipus de dades
               0);                //distància entre l'inici del 
                                  //  buffer i el primer index. 

// Tipus de primitives:
//GL_POINTS, GL_LINES, GL_LINE_STRIP, GL_LINE_LOOP,
//GL_TRIANGLES, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN

// Tipus de dades als indexos
//GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT, GL_UNSIGNED_INT
//La darrera necessita de l'extensió OES_element_index_uint
       
glDisableVertexAttribArray(position_attrib); 
glDisableVertexAttribArray(texcoord_attrib); 
//-------------------------------------------------------------

Textures:

Les textures en OpenGL sempre han estat un petit percal. No m'extendré massa, però coses "importants":


//----------------------------------------------
glGenTextures(num, &id); //genera textures
glDeleteTextures(num, &id); //ejem

  //Activar textura
glBindTexture(tipus, //GL_TEXTURE_2D, GL_TEXTURE_CUBE_MAP
              id);

  //Definir la informació
glTexImage(capa,
           nivell,         //Nivell de mipmap. Deixeu-lo a 0
           internalFormat, //format intern
           width,         
           height,
           border,         //en OpenGL ES, 0.
           externalFormat, //cal que sigui el mateix que l'intern.
           dataType,       //tipus de dades
           data);          //dades, per "files"

// Capa:
// Per textures 2D, GL_TEXTURE_2D
// Per Cubemap s'han de definir totes 6 cares:
// GL_TEXTURE_CUBE_MAP_POSITIVE_X, ...NEGATIVE_X, ...

// Formats:
//GL_RGBA, GL_RGB, GL_LUMINANCE_ALPHA, GL_LUMINANCE, GL_ALPHA
//Pensar en la luminance com a "escala de grisos"

// Tipus de dades:
//GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT_4_4_4_4,
//GL_UNSIGNED_SHORT_5_5_5_1, GL_UNSIGNED_SHORT_5_6_5

  //Definir com es filtra la imatge quan es fa més gran.
glTexParameteri( tipus,
                 GL_TEXTURE_MAG_FILTER,
                 param); //GL_NEAREST / GL_LINEAR

  //Definir com es filtra la imatge quan es fa més petita.
glTexParameteri( tipus,
                 GL_TEXTURE_MIN_FILTER,
                 param); 
              //GL_NEAREST, GL_LINEAR
              //GL_NEAREST_MIPMAP_NEAREST x4 combinacions
//Definir què fa una textura quan té una coordenada fora de [0,1]
glTexParameteri( tipus,
                 GL_TEXTURE_WRAP_S, //O t
                 GL_REPEAT); //GL_CLAMP_TO_EDGE, GL_MIRRORED_REPEAT



glGenerateMipmap(tipus); //Genera automàticament un mipmap per la 
                         //textura activa

//Finalment parlem de com agafar una textura des del shader.
//Per tal de fer-ho, necessitem una "uniform" especial, anomenada
//"sampler" ("sampler2D" o "samplerCube")

//Activem la textura en un la unitat 0:
glActiveTexture(GL_TEXTURE0); //GL_TEXTURE1, ...
glBindTexture(tipus, textureId);

//Fem que el sampler apunti a la unitat 0
glUniform1i(sampler_uniform, 0);

//----------------------------------------------
També hi ha un altre tipus de textura, la textura3D, que és estandard a OpenGL i a OpenGL ES necessita de l'extenció "GL_OES_texture_3D". Funciona pràcticament igual amb una funció extra per carregar-la "glTexImage3D"[OES] amb un paràmetre adicional indicant la profunditat.

Adicionalment podem crear textures de "profunditat" si l'extenció "GL_OES_depth_texture" està disponible. Això serveix essencialment amb el tema dels pBuffers (vist més endavant) i es creen amb un format "GL_DEPTH_COMPONENT" i tipus "GL_UNSIGNED_SHORT" o "GL_UNSIGNED_INT" si estàn disponibles.


Continuaré en un altre moment. Falta per fer:

  • pBuffers
  • Vertex Shaders
  • Fragment Shaders
  • Operacions post-shader
  • Exemples

[1] http://www.opengl.org/sdk/docs/man/xhtml/glUniform.xml

dijous, 29 d’octubre del 2009

Tutorial de Java I [Què és Java?]

Bé, aquest és un petit tutorial de Java. Malauradament, primer us haurieu de llegir el seguent post: Programació Orientada a Objectes.

Fet? Perfecte.


Tot és una classe.

Aquesta frase primer de tot és falsa. Però la idea inicial que s'ha de fical el programador de Java al cap és que: Tot és una classe.

Per tant, el programador de Java el que fa és definir un seguit de classes i com aquestes es comporten. És important tenir això a la ment a l'hora de programar Java, ja que si intentes fer-ho d'alguna altra manera, encara que puguis, no seràs tot l'eficient que podríes ser.

Una mica de història.

Bé, estic escrivint les coses de forma certament desordenada. Així que a partir d'ara començarem per al començament. D'on va sortir Java?

Java va aparèixer com una plataforma per fer arribar als usuaris de Internet coses que els navegadors "normals" no podíen fer. Volíen poder llençar programes a partir de pàgines Web, i per tal d'aconseguir-ho, calia dissenyar Java d'una forma molt específica.

Aquí hi ha les 5 directrius del llenguatge:

  1. It should be "simple, object oriented, and familiar".
  2. It should be "robust and secure".
  3. It should be "architecture neutral and portable".
  4. It should execute with "high performance".
  5. It should be "interpreted, threaded, and dynamic".
El que traduit al català vol dir:

  1. Cal que el llenguatge sigui simple, orientat a objectes i familiar. O sigui, que se sembli prou a C/C++ (El llenguatge més extés aleshores) per a què a la gent no li costi molt canviar, i sense masses complicacions per que no sigui difícil d'apendre.
  2. Robust i segur. Essencialment el llenguatge ha d'aportar la màxima seguretat possible. És impossible que un compilador impedeixi al programador de fer certs errors, però el dur la gestió de memòria internament i descarregar al programador de tasques com aquesta es simplifiquen molts errors.
  3. "Portable" i d'arquitectura neutre. Cal que el mateix programa pugui ser executat en diverses màquines sense cap canvi. Java ho fa mitjançant la màquina virtual de java. Els programes fets en Java es compilen per a aquesta JVM i s'executen sobre una màquina virtual, i no sobre el processador real. Això fa que puguis distribuir el teu programa per Internet sense preocupar-te de si el usuari ha de usar Windows i x86.
  4. D'altes prestacions. Malgrat el llenguatge s'executi moltes vegades interpretat, cal que sigui ràpid. Per això s'usa avui dia JIT (Just-in-time compilation, el programa es compila per a la màquina on s'executa quan s'executa) o hi ha la possibilitat de programar una part en codi nadiu, perdent la portabilitat però guanyant velocitat de processament.
  5. Interpretat (Alleugera temps de compilació i perment desenvolupament mès ràpid, al escurçar els temps de test), amb suport a programació de diferents fils d'execució a nivell de llenguatge i dinàmic en el sentit que es poden carregar noves classes i objectes a temps d'execució, descarregant-los des d'internet, per exemple.
Bé, ara que ja he descarregat els primers pensaments de Java, toca començar a explicar com funciona. Malauradament, ho hauré de deixar per un altre dia, que la feina (de veritat) se'm tira sobre, així que... sort!

diumenge, 26 d’abril del 2009

Programació Orientada a Objectes

Què és la programació orientada a objectes?
Per què s'usa tant?
Quines són els seus avantatges i els seus inconvenients?

Bé, aquests són alguns dels interrogants, no els únics, que espero contestar avui en aquest post.

Primer de tot, i per explicar què és la programació orientada a objectes, cal explicar els 3 principis fonamentals sobre el quals està construït.

I si expliques abans què punyetes és un objecte i què és una classe, com la gen normal?

Bé, jo volia...

Tu volies, tu volies, comencem per el començament:
Un objecte és una entitat construïda per un estat intern (atributs) i unes accions que pot fer (mètodes) per a canviar-los.

Bé, jo no ho hagués definit millor.

Gràcies :-D

Ehmmmmmmm doncs... per on anava?

Els 3 super-principis de la OOP.

Gràcies, és que perdo el fil.

En fi, aquí teniu els principis fonamentals de la OOP:
  • Encapsulament.
  • Herència.
  • Polimorfisme.
Estàs segur que existeix "encapsulament"?

No massa. Puc continuar?

Va, continua...

Doncs bé, la clau per a programar bé OOP és entendre aquests conceptes. Aneu amb compte, són gradualment més complexes, i cal que entengueu cada un bé abans de continuar.

Encapsulament [Abstracció]

A opinió meva, el atribut que menys importància se li dóna i, en canvi, el més important. Al ser el més senzill, hom acostuma a menysprear-lo lleugerament, també força moltes vegades que el programa sigui menys eficient, més lent, o que ocupi més. Si això és un problema, probablement hauries de pensar-te a moure't a un altre llenguatge, no a saltar-te el pilar principal del OOP. En tot cas aquí tenim:

L'abstracció es tracta simplement d'això: abstraure la implementació d'un objecte de la seva interfície.

Què vol dir això? Que no ha de fer falta MAI, PER A RES, saber com un objecte funciona internament per a poder-lo fer funcionar.

Saber com funciona un objecte ens dona, evidentment, avantatges: podem fer-lo servir millor, etc... però no ens hem de deixar temptar, abstraure correctament els objectes ens dóna moltes avantatges també, la primera de totes que deixem de dependre de com estigui fet.

I aquest és el salt que fa que la OOP funcioni tan bé. No us enganyeu, no és cap de les coses "guais" que fa (polimorfisme, sobrecarrega [que no mencionaré per què encara que pot facilitar la vida, el seu sobre ús la complica més que no ajuda, a més, no és una característica original i comuna del OOP], herència), sinó la capacitat de poder fer "d'acord, això funciona, continuem", i no estar-se sempre amb detalls.

Per a què ens entenem, a algú li fa falta saber com, exactament, funciona internament una radio per fer-la anar? El que el 99% (i pico) de la gen sap és, simplement, que quan l'engeguen i posen la freqüència que desitges sona la música que volen. I si necessitéssim saber fer anàlisi de Fourier i transformacions de Laplace per fer-les servir haurien tingut cap èxit les ràdios? Jo crec que no.

Resumint, un programador de OOP necessita, per fer servir un objecte, a quina classe pertany i què fa aquesta classe (no com ho fa).


Herència.

Una altra de les famoses característiques. Què és l'herència? Doncs el que el seu nom indica, que els fills hereten coses dels pares!

Què vol dir això?

Cada objecte pertany a una classe. Això és senzill d'entendre. Per tal de simplificar les coses, un pot definir una classe "Radio de Cotxe" i "Radio portàtil". Està clar que tant "Radio de Cotxe" com "Radio portàtil" tenen moltes coses comuns, entre elles, que són "Ràdios". Així, un hauria de repetir moltes coses de "Radio de Cotxe" a "Radio portàtil", per tal de simplificar això el que es fa és definir una classe "Radio" que és pare tant de "Radio de Cotxe" com de "Radio portàtil". Aquestes dues es comporten doncs, com "Radio" amb els seus matisos.

Això simplifica moltes vegades el codi, sobretot si se segueix la màxima de no repetir mai codi innecessàriament i tenir les coses sempre agrupades lògicament.

Polimorfisme.

El polimorfisme no és més que una petita volta al concepte d'herència.

Continuant amb l'exemple anterior, tant "Radio de Cotxe" com "Radio portàtil" són, a la vegada, "Radio"s. El polimorfisme s'aprofita d'aquest concepte i et permet usar un objecte de la classe "Radio de Cotxe" com si fos de la classe "Radio", i és que, en el fons, també ho és!

Com funciona això? Imaginem-nos que tens un programa que treballa tant amb "Radio de Cotxe" com amb "Radio portàtil". En un moment donat, necessites engegar un seguit d'aquestes ràdios. Pots crear un procediment que engegui primer totes les "Radio de Cotxe" i després les "Radio portàtil", o un que simplement agafi un seguit de "Radio" i les engegui totes, sense preguntar-se de quina mena de ràdio és exactament.

Potser les "Radio de Cotxe" són molt antigues, analògiques, mentre que les "Radio portàtil" són modernes i digitals i ambdues funcionen de formes força diferents. Malgrat tot, "engegar" és una cosa que poden fer totes les ràdios, i en totes es fa igual: prement el botó amb un triangle.


Bones pràctiques.

Hem vist, de forma genèrica, com funciona la OOP. Pròximament mirarem un cas concret, Java, però abans he de fer unes petites advertències.

Polimorfisme i herència donen eines molt potents i capaces de fer coses molt complicades. No caiguem en la temptació, cal sempre mantenir les coses el més simples possible per tal de seguir l'esperit de OOP: fer les coses senzilles. Un pot veure's temptat, per exemple, que quan un prem el botó d'engegar d'una ràdio faci una cosa diferent, que potser sembla adequada en una situació però (com el meu exemple vol demostrar) pot portar problemes de confusió, per molt bé que ho documentis.

I aquest és el problema que moltes vegades vé amb la sobrecàrrega de mètodes i operacions. Per tu pot semblar clar que quan multipliques 2 vectors et dóna la multiplicació element a element. Però algú pot interpretar que és la multiplicació vectorial, cosa que no és exactament el mateix, tot i donar un resultat similar (un altre vector).

Així que recordeu: amb un gran poder, bé una gran responsabilitat!

dimarts, 7 d’abril del 2009

História vertical de la programació II

Continua de Història vertical de la programació.

Resum:
Un ordinador, en la seva forma més bàsica, és una maquina mecànica (ejem electrònica, ejem) que llegeix una llista de instruccions i les executa. Tan simple com això.

Aquestes instruccions estan codificades en números, hi ha la instrucció 1, la 2, la 3, etc... que cada una fa una cosa determinada. Com que recordar la correspondència entre cada número i la seva corresponent instrucció és complicat, es varen inventar els ensambladors, que són uns programes que agafen paraules (mnemònics que corresponen a cada instrucció ADD per la suma, MUL per la multiplicació, etc...) i els converteixen al seu nombre corresponent.


L'evolució lògica fou trobar un llenguatge una mica més desenvolupat i abstracte de la màquina. Per exemple hi ha màquines on hi ha operacions complexes que en altres no tenen (A := A + C*D), o tenen un nom (nombre) diferent, etc... El llenguatge insígnia d'aquesta mena és el C.

C

C és un llenguatge que es va crear quan un grup de investigadors intentava crear un sistema operatiu que pogués funcionar sobre màquines diferents. Aquesta gen es va donar conte que era pràcticament impossible haver de fer cada vegada el mateix en el llenguatge ensamblador de cada un dels processadors, amb les seves limitacions, així que, agafant B com a referència [un llenguatge amb el mateix objectiu però força més simple] van crear un llenguatge i un seguit de programes que el traduïen a l'idioma concret de cada màquina. Així va néixer C, que va permetre la creació de Unix [EL sistema operatiu més influent de la història].

Paradigmes de la programació.

I aquí és on neixen els paradigmes de programació.

Què és un paradigma de programació? Doncs bona pregunta, amb difícil resposta. La meva seria alguna cosa així:

Un paradigma de programació és un seguit de estils i característiques que comparteixen uns llenguatges de programació que ajuden al programador a fer programes de manera més senzilla.

Bé, estic més o menys satisfet amb aquesta definició, però del tot obert a crítiques.

Hi ha, però, forces coses a destacar. La primera és que no existeix un paradigma millor que un altre, en principi. I dic en principi per què un cop analitzes el problema que busques solucionar n'hi ha de més adequats que d'altres. O sigui, cara paradigma és dissenyat amb un objectiu i per tal d'assolir-lo a agafat un seguit de característiques que el poden convertir en un problema en certes situacions.

Això fa que es puguin "adoptar" conceptes no natius del paradigma on es treballa per tal de millorar el programa que es fa, sempre i quan sàpigues què fas.

Finalment, és força freqüent que un sol llenguatge nadi entre varis paradigmes, fent servir característiques de cada un.

Ara us faig una llista amb els més importants i els seus pros i contres.

Paradigma imperatiu.
Aquest és el paradigma natural. Aquí el programador va manant pas a pas què és el que la màquina ha de fer (suma a i b i guarda-ho a c, després incrementa a en 1, ...).

Quan un llenguatge és imperatiu pur (com C o BASIC) les instruccions tenen una traducció si no directe, molt evident a ensamblador i és on es fan els programes més eficients (en quant a velocitat i/o memòria utilitzada), malgrat això es pagui amb la dificultat que suposa crear un programa de cert tamany amb aquest paradigma, ja que el programador necessita estar molt atent als detalls.

C n'és un bon exemple. Amb C ets capaç de controlar el que fa l'ordinador amb gran detall, això fa que es puguin magnificar els teus errors, ja que hi ha moltes operacions "estranyes" permeses. També es veu com un codi en C a partir de cert nombre de línies és quasi il·legible.


Paradigma Funcional (o declaratiu).
Aquí estem en l'extrem oposat al llenguatge imperatiu. Tu en un llenguatge funcional no li expliques a la màquina com ha de fer les coses, sinó què ha de fer.

Per a que ens entenem, un llenguatge funcional o declaratiu es vasa en un seguit de declaracions (de funcions o valors) a l'estil matemàtic. Així doncs, tu per calcular el factorial d'un nombre no li has de dir al programa "per calcular el factorial comença des de 1 i ves multiplicant cada vegada per un nombre més gran fins a arribar al nombre", sinó "el factorial del nombre és ell mateix multiplicat per el factorial del nombre anterior. El factorial de 0 és 1". D'aquesta manera el compilador té certa llibertat a l'hora de decidir com fer les coses, a vegades permetent-li agafar dreceres invisibles per al programador, i la forma de programar així és matemàticament molt més elegant.

SML, SQL, HTML són llenguatges declaratius.

Programació Orientada a Objectes.
La vaca sagrada de la programació moderna.

Explicaré amb més detall de què es tracta la programació orientada objectes en el següent tutorial, però de moment explico que en un programa orientat a objectes es tracta, principalment, de programar objectes.

Aquest paradigma està expressament dissenyat per a que sigui senzill, ja que acosta molt la programació a la manera de pensar humana. Dintre d'aquest programa es manipulen Objectes, cada un pertanyent a una certa Classe. Així, conceptualment, és senzill que si un objecte pertany a la classe Matriu i el multiplico per un objecte de la mateixa classe tindré un altre objecte de la mateixa classe, i que serà el resultat de la multiplicació de les anteriors.

Així doncs, el secret de la programació orientada a objectes radia en definir correctament les classes a les quals poden pertànyer aquests objectes i les seves possibles interaccions.

Aquesta facilitat però, té un cost. El primer, computacional: moltes de les característiques principals de l'OOP [Object-Oriented Programming] tenen un cert cost en temps i espai, encara que força reduït. L'altre és que el programador té certa responsabilitat de programar els objectes amb cura, ja que a vegades el posar noms comfusos i/o documentar incorrectament pot portar a seriosos problemes.

Java és un llenguatge orientat a objectes força complert (que també comparteix paradigma amb el imperatiu. C# és l'equivalent Microsoft a Java, i C++ és l'ampliació de C a l'orientació a Objectes (tot i que arrossega moltes de les característiques de C, cosa que el fa potencialment eficient i amb moltes bombes de rellotgeria internes si no es programa amb molta cura).

Altres: Ada, Eiffel, Python, Ruby...

dilluns, 6 d’abril del 2009

Epopeya a mitja llum

Com és semi-conegut, faig de programador amateur en diversos eternament inacabats projectes. L'últim en qüestió és JAL3D, una llibreria d'animació feta en Java.

Avui dia hi ha 3 maneres de programar en Java, i una d'elles no la considero decent:
  • Utilitzar javac, el compilador de línea de comandes, juntament amb kate/gedit/vim/emacs/notepad++ o el bloc de notes de torn (malgrat notepad++ sigui una entitat pròxima a divina, no puc considerar això una opció decent).
  • Utilitzar NetBeans, el IDE (Entorn integrat de desenvolupament) de Sun (els creadors de Java).
  • Utilitzar Eclipse, l'IDE de IBM.
Bé, no entraré a discutir per què faig servir Eclipse i no NetBeans, diguéssim que una raó és que el tenim a la facultat i punt.

En fi, per a passar les coses d'un ordinador a l'altre utilitzo el que se'n diu un repositori. Siguem sincers, no sé prou bé com funcionen els repositoris, així que explicaré el que sé: Són "bases de dades" on guardes els canvis que vas fent al teu projecte, de tal manera que ets capaç després de tornar enrere i de saber qui ha canviat què (i si afegeix un comentari al "commit", perquè).

Antigament, i per el meu projecte Aresource (projecte inacabat #N) feia servir un repositori de sourceforge.net que vaig aconseguir configurar per a que fes servir CVS. Ara, amb la nova versió de la forja, només aconsegueixo tenir SVN (SVN i CVS són els 2 programes principals de repositoris) i malgrat SVN sigui millor que CVS (no deixa de ser una mena de fork [separació del projecte, però continuant amb la feina feta] canviant d'arrels els problemes que arrossegava CVS des dels inicis) hi ha un gran problema: Eclipse no el du per defecte. I instal·lar plugins a l'Eclipse és dolor al forat del cul, com diuen els americans.

En fi, aquí teniu unes indicacions faciletes de seguir (comentaré coses, però si no voleu saber que feu, salteu-vos les explicacions) per tal d'instal·lar l'últim Eclipse a l'últim Ubuntu fins la data:

Eclipse Ganymede 3.4
Ubuntu Intrepid Ibex 8.10

  1. Descarregar-se Eclipse manualment. El que hi ha al repositori oficial d'Ubuntu és vell. No sé si és el 3.2 encara...

    1. Cal anar a la pàgina d'Eclipse [link] i descarregar-se aquell que posa "Eclipse IDE for Java Developers (85 MB)"
    2. És un zip, només cal descomprimir-lo i apretant 2 vegades a l'icona "eclipse" ja el teniu funcionant!
  2. Descarregar-se els paquets adequats de subversion per ubuntu. Atenció: al moment de escriure el post, la última versió de Subversion és la 1.6, mentre que a Ubuntu només hi ha fins la 1.5. AQUEST ha estat el principal problema que he tingut per a fer-ho funcionar. Us proposo 2 maneres:

    1. Utilitzar Synaptic. Al menú "inici" de Ubuntu: Sistema -> Administració -> Gestor de paquets Synaptic. Aquesta eina serveix per afegir programes al sistema [demanará la contrasenya de root]. En aquest cas ens toca buscar "subversion", "libsvn-java", "libsvn1". Apreteu amb el botó dret del ratolí sobre cada opció i assegureu-vos que estigui marcada. Després preneu "aplica". Aquesta via té l'avantatge que sabrem quina versió instalem (si 1.5 o superior)
    2. Utilitzar la línea de comandes: sudo apt-get install subversion libsvn-java libsvn1. Sudo és la comanda que indica "super usuari", així que ens demanarà el password de root (algun dia explicaré això de root, suposo). Aquesta opció no ens diu sempre quina versio instal·lem...
  3. Instalar Subclipse. Aquí ens haurem de pegar amb el sistema de plugins de l'Eclipse, i aquest és deixeble de Chuck Norris, esteu avisats!

    1. Tenint obert Eclipse, anar al menú Help -> Software Updates...(Perquè les actualitzacions són a "Help"??????)
    2. Se'ns obre una finestra. Anar a la pestanya "Avaliable Software"
    3. Afegir una pàgina de plugins: "Add Site" -> "http://subclipse.tigris.org/update_1.4.x" ////// "http://subclipse.tigris.org/update_1.6.x"
    4. Obrir les opcions del que acabem d'afegir, activar "subclipse" i prémer "Install".
    5. ATENCIÓ!!!!!!!!! subclipse 1.6 necessita subversion 1.6. Què vol dir? Que aquí no explico com instal·lar el 1.6 perquè només sé instal·lar el 1.5 que és el que vé al repositori d'Ubuntu. Espero que això canvii d'aquí poc, però mentrestant, aneu amb compte i instaleu-vos subclipse 1.4 per anar sobre segurs. Sinó us tocarà desinstalar el 1.6 i instalar el 1.4, com vaig haver de fer...

Bé, espero haver estat d'ajuda per algú, si mes no per a mi mateix, que segur que em carregaré l'ordinador qualsevol d'aquests dies sense voler i necessitaré això per no perdre una altra tarda maleint Eclipse...