<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>peix.org</title>
	<atom:link href="http://www.peix.org/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.peix.org</link>
	<description></description>
	<lastBuildDate>Thu, 29 Jul 2010 15:44:12 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>Doctrine con columnas en mayúsculas</title>
		<link>http://www.peix.org/2010/07/doctrine-con-columnas-en-mayusculas/</link>
		<comments>http://www.peix.org/2010/07/doctrine-con-columnas-en-mayusculas/#comments</comments>
		<pubDate>Wed, 28 Jul 2010 08:42:07 +0000</pubDate>
		<dc:creator>zero</dc:creator>
				<category><![CDATA[programación]]></category>
		<category><![CDATA[doctrine]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[symfony]]></category>

		<guid isPermaLink="false">http://www.peix.org/?p=238</guid>
		<description><![CDATA[Por defecto el ORM Doctrine 1.4 no soporta columnas de base de datos en mayúsculas, porque algunas de las bases de datos soportadas tampoco las aceptan. Pero si te encuentras con una base de datos legacy MySQL que ya las tiene y que no se puede cambiar, es muy fácil modificar el código de la [...]]]></description>
			<content:encoded><![CDATA[<p>Por defecto el <a href="http://www.doctrine-project.org/">ORM Doctrine 1.4</a> no soporta columnas de base de datos en mayúsculas, porque algunas de las bases de datos soportadas tampoco las aceptan. Pero si te encuentras con una base de datos legacy MySQL que ya las tiene y que no se puede cambiar, es muy fácil modificar el código de la clase <code>Doctrine_Table</code> para que no se queje. A continuación el diff de cambios.</p>
<p><span id="more-238"></span></p>

<div class="wp_syntax"><div class="code"><pre class="diff" style="font-family:monospace;color: #ccc; font: 12px Consolas, Lucida Console, Monaco, monospace;">--- Doctrine_before/Table.php   <span class="nu0">2010</span>-07-<span class="nu0">29</span> <span class="nu0">17</span>:<span class="nu0">18</span>:<span class="nu0">08.681913517</span> +0200
<span class="re4">+++ Doctrine_after/Table.php    <span class="nu0">2010</span>-07-<span class="nu0">29</span> <span class="nu0">17</span>:<span class="nu0">43</span>:<span class="nu0">25.933158157</span> +0200</span>
<span class="re6">@@ -<span class="nu0">1160</span>,<span class="nu0">7</span> +<span class="nu0">1160</span>,<span class="nu0">7</span> @@</span>
             return $this-&gt;_columnNames<span class="br0">&#91;</span>$fieldName<span class="br0">&#93;</span>;
         <span class="br0">&#125;</span>
&nbsp;
<span class="re7">-        return strtolower<span class="br0">&#40;</span>$fieldName<span class="br0">&#41;</span>;</span>
<span class="re8">+        return $fieldName;</span>
     <span class="br0">&#125;</span>
&nbsp;
     /**
<span class="re6">@@ -<span class="nu0">1187</span>,<span class="nu0">8</span> +<span class="nu0">1187</span>,<span class="nu0">10</span> @@</span>
      */
     public function getFieldName<span class="br0">&#40;</span>$columnName<span class="br0">&#41;</span>
     <span class="br0">&#123;</span>
<span class="re7">-        if <span class="br0">&#40;</span>isset<span class="br0">&#40;</span>$this-&gt;_fieldNames<span class="br0">&#91;</span>$columnName<span class="br0">&#93;</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span></span>
<span class="re7">-            return $this-&gt;_fieldNames<span class="br0">&#91;</span>$columnName<span class="br0">&#93;</span>;</span>
<span class="re8">+        foreach<span class="br0">&#40;</span>$this-&gt;_fieldNames as $k=&gt;$v<span class="br0">&#41;</span><span class="br0">&#123;</span></span>
<span class="re8">+            if<span class="br0">&#40;</span>strtolower<span class="br0">&#40;</span>$k<span class="br0">&#41;</span>==strtolower<span class="br0">&#40;</span>$columnName<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#123;</span></span>
<span class="re8">+                return $v;</span>
<span class="re8">+            <span class="br0">&#125;</span></span>
         <span class="br0">&#125;</span>
         return $columnName;
     <span class="br0">&#125;</span>
<span class="re6">@@ -<span class="nu0">1309</span>,<span class="nu0">10</span> +<span class="nu0">1311</span>,<span class="nu0">9</span> @@</span>
                 $fieldName = $parts<span class="br0">&#91;</span><span class="nu0">0</span><span class="br0">&#93;</span>;
             <span class="br0">&#125;</span>
&nbsp;
<span class="re7">-            $name = strtolower<span class="br0">&#40;</span>$parts<span class="br0">&#91;</span><span class="nu0">0</span><span class="br0">&#93;</span><span class="br0">&#41;</span>;</span>
<span class="re8">+            $name = $parts<span class="br0">&#91;</span><span class="nu0">0</span><span class="br0">&#93;</span>;</span>
         <span class="br0">&#125;</span> else <span class="br0">&#123;</span>
             $fieldName = $name;
<span class="re7">-            $name = strtolower<span class="br0">&#40;</span>$name<span class="br0">&#41;</span>;</span>
         <span class="br0">&#125;</span>
&nbsp;
         $name = trim<span class="br0">&#40;</span>$name<span class="br0">&#41;</span>;</pre></div></div>

<p>La clave es <code>getFieldName</code> que acepte <code>$columName</code> en mayúsculas y en minúsculas porque a veces se pasa de una forma y a veces de otra.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.peix.org/2010/07/doctrine-con-columnas-en-mayusculas/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>miMergeEmbedFormPlugin</title>
		<link>http://www.peix.org/2010/07/mimergeembedformplugin/</link>
		<comments>http://www.peix.org/2010/07/mimergeembedformplugin/#comments</comments>
		<pubDate>Mon, 26 Jul 2010 08:48:14 +0000</pubDate>
		<dc:creator>zero</dc:creator>
				<category><![CDATA[programación]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[symfony]]></category>

		<guid isPermaLink="false">http://www.peix.org/?p=192</guid>
		<description><![CDATA[Symfony 1.4 tiene un sistema de formularios muy potente. Cada modelo tiene una clase de formulario auto-generada. Las clases de formulario permiten embedirse unas dentro de otras. Esto es muy útil para incluir un formulario de un modelo hijo relacionado dentro de otro. El problema es que la función embedForm crea un decorador hijo, normalmente [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.symfony.es/">Symfony 1.4</a> tiene un sistema de formularios muy potente. Cada modelo tiene una clase de formulario auto-generada. Las clases de formulario permiten embedirse unas dentro de otras. Esto es muy útil para incluir un formulario de un modelo hijo relacionado dentro de otro. El problema es que la función <code>embedForm</code> crea un decorador hijo, normalmente una tabla dentro de la tabla del formulario padre. Yo estaba buscando lo mismo pero que los campos del formulario hijo no se diferenciaran de los del padre. Encontré <a href="http://www.blogs.uni-osnabrueck.de/rotapken/2009/03/13/symfony-merge-embedded-form/">esta entrada de blog</a> que explicaba cómo hacerlo. Luego estuve modificando el código hasta que lo convertí en un plugin, lo podéis encontrar <a href="http://github.com/miguelibero/miMergeEmbedFormPlugin">aquí</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.peix.org/2010/07/mimergeembedformplugin/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Estructura básica de un plugin jQuery</title>
		<link>http://www.peix.org/2010/07/estructura-basica-de-un-plugin-jquery/</link>
		<comments>http://www.peix.org/2010/07/estructura-basica-de-un-plugin-jquery/#comments</comments>
		<pubDate>Thu, 01 Jul 2010 10:00:17 +0000</pubDate>
		<dc:creator>zero</dc:creator>
				<category><![CDATA[programación]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[jquery]]></category>

		<guid isPermaLink="false">http://www.peix.org/?p=185</guid>
		<description><![CDATA[Esta es la estructura básica que uso para mis plugins jQuery. Es un poco mas avanzada que los ejemplos que he visto porque permite métodos públicos de la siguiente manera: // primero llamar al constructor $&#40;'div'&#41;.pluginName&#40;&#123;name:'other_value'&#125;&#41;; // luego llamar al método desde donde sea $&#40;'div'&#41;.pluginName&#40;'publicSubfunction','arg1_value'&#41;; El código del plugin sería: &#40;function&#40;$&#41; &#123; &#160; var ver [...]]]></description>
			<content:encoded><![CDATA[<p>Esta es la estructura básica que uso para mis plugins jQuery. Es un poco mas avanzada que los ejemplos que he visto porque permite métodos públicos de la siguiente manera:</p>

<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;color: #ccc; font: 12px Consolas, Lucida Console, Monaco, monospace;"><span class="co1">// primero llamar al constructor</span>
$<span class="br0">&#40;</span><span class="st0">'div'</span><span class="br0">&#41;</span>.<span class="me1">pluginName</span><span class="br0">&#40;</span><span class="br0">&#123;</span><span class="kw3">name</span><span class="sy0">:</span><span class="st0">'other_value'</span><span class="br0">&#125;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="co1">// luego llamar al método desde donde sea</span>
$<span class="br0">&#40;</span><span class="st0">'div'</span><span class="br0">&#41;</span>.<span class="me1">pluginName</span><span class="br0">&#40;</span><span class="st0">'publicSubfunction'</span><span class="sy0">,</span><span class="st0">'arg1_value'</span><span class="br0">&#41;</span><span class="sy0">;</span></pre></div></div>

<p><span id="more-185"></span></p>
<p>El código del plugin sería:</p>

<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;color: #ccc; font: 12px Consolas, Lucida Console, Monaco, monospace;"><span class="br0">&#40;</span><span class="kw2">function</span><span class="br0">&#40;</span>$<span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp;
<span class="kw2">var</span> ver <span class="sy0">=</span> <span class="st0">'0.1'</span><span class="sy0">;</span>
&nbsp;
<span class="kw2">function</span> debug<span class="br0">&#40;</span>s<span class="br0">&#41;</span> <span class="br0">&#123;</span>
  <span class="kw1">if</span> <span class="br0">&#40;</span>$.<span class="me1">fn</span>.<span class="me1">pluginName</span>.<span class="me1">debug</span><span class="br0">&#41;</span>
    log<span class="br0">&#40;</span>s<span class="br0">&#41;</span><span class="sy0">;</span>
<span class="br0">&#125;</span>
&nbsp;
<span class="kw2">function</span> log<span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
  <span class="kw1">if</span><span class="br0">&#40;</span>window.<span class="me1">console</span> <span class="sy0">&amp;</span>amp<span class="sy0">;&amp;</span>amp<span class="sy0">;</span> window.<span class="me1">console</span>.<span class="me1">log</span><span class="br0">&#41;</span>
    window.<span class="me1">console</span>.<span class="me1">log</span><span class="br0">&#40;</span><span class="st0">'[image_list] '</span> <span class="sy0">+</span> Array.<span class="me1">prototype</span>.<span class="me1">join</span>.<span class="me1">call</span><span class="br0">&#40;</span>arguments<span class="sy0">,</span><span class="st0">' '</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="br0">&#125;</span><span class="sy0">;</span>
&nbsp;
$.<span class="me1">fn</span>.<span class="me1">pluginName</span> <span class="sy0">=</span> <span class="kw2">function</span><span class="br0">&#40;</span>options<span class="br0">&#41;</span><span class="br0">&#123;</span>
  <span class="kw1">if</span><span class="br0">&#40;</span><span class="kw1">typeof</span> options <span class="sy0">==</span> <span class="st0">'string'</span><span class="br0">&#41;</span><span class="br0">&#123;</span>
    <span class="kw1">return</span> $.<span class="me1">fn</span>.<span class="me1">pluginName</span><span class="br0">&#91;</span>options<span class="br0">&#93;</span>.<span class="me1">apply</span><span class="br0">&#40;</span><span class="kw1">this</span><span class="sy0">,</span>Array.<span class="me1">splice</span><span class="br0">&#40;</span>arguments<span class="sy0">,</span><span class="nu0">1</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
  <span class="br0">&#125;</span>
  options <span class="sy0">=</span> $.<span class="me1">extend</span><span class="br0">&#40;</span><span class="br0">&#123;</span><span class="br0">&#125;</span><span class="sy0">,</span> $.<span class="me1">fn</span>.<span class="me1">pluginName</span>.<span class="me1">defaults</span><span class="sy0">,</span> $<span class="br0">&#40;</span><span class="kw1">this</span><span class="br0">&#41;</span>.<span class="me1">data</span><span class="br0">&#40;</span><span class="st0">'pluginName'</span><span class="br0">&#41;</span><span class="sy0">,</span> options<span class="br0">&#41;</span><span class="sy0">;</span>
  $<span class="br0">&#40;</span><span class="kw1">this</span><span class="br0">&#41;</span>.<span class="me1">data</span><span class="br0">&#40;</span><span class="st0">'image_list'</span><span class="sy0">,</span>options<span class="br0">&#41;</span><span class="sy0">;</span>
  <span class="kw1">return</span> <span class="kw1">this</span><span class="sy0">;</span>
<span class="br0">&#125;</span><span class="sy0">;</span>
&nbsp;
$.<span class="me1">fn</span>.<span class="me1">pluginName</span>.<span class="me1">defaults</span> <span class="sy0">=</span> <span class="br0">&#123;</span>
  <span class="kw3">name</span><span class="sy0">:</span> <span class="st0">'value'</span>
<span class="br0">&#125;</span><span class="sy0">;</span>
&nbsp;
<span class="kw2">var</span> getOptions <span class="sy0">=</span> <span class="kw2">function</span><span class="br0">&#40;</span>options<span class="br0">&#41;</span><span class="br0">&#123;</span>
  <span class="kw1">return</span> $.<span class="me1">extend</span><span class="br0">&#40;</span><span class="br0">&#123;</span><span class="br0">&#125;</span><span class="sy0">,</span> $<span class="br0">&#40;</span><span class="kw1">this</span><span class="br0">&#41;</span>.<span class="me1">data</span><span class="br0">&#40;</span><span class="st0">'pluginName'</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="br0">&#125;</span><span class="sy0">;</span>
&nbsp;
<span class="kw2">var</span> privateFunction <span class="sy0">=</span> <span class="kw2">function</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#123;</span>
<span class="br0">&#125;</span><span class="sy0">;</span>
&nbsp;
$.<span class="me1">fn</span>.<span class="me1">pluginName</span>.<span class="me1">publicSubfunction</span> <span class="sy0">=</span> <span class="kw2">function</span><span class="br0">&#40;</span>arg1<span class="br0">&#41;</span><span class="br0">&#123;</span>
  <span class="kw2">var</span> options <span class="sy0">=</span> getOptions.<span class="me1">call</span><span class="br0">&#40;</span><span class="kw1">this</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="br0">&#125;</span><span class="sy0">;</span>
&nbsp;
<span class="br0">&#125;</span><span class="br0">&#41;</span><span class="br0">&#40;</span>jQuery<span class="br0">&#41;</span><span class="sy0">;</span></pre></div></div>

]]></content:encoded>
			<wfw:commentRss>http://www.peix.org/2010/07/estructura-basica-de-un-plugin-jquery/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Campos de fichero en formularios embedidos en Symfony 1.4</title>
		<link>http://www.peix.org/2010/06/campos-de-fichero-en-formularios-embedidos-en-symfony-1-4/</link>
		<comments>http://www.peix.org/2010/06/campos-de-fichero-en-formularios-embedidos-en-symfony-1-4/#comments</comments>
		<pubDate>Fri, 25 Jun 2010 14:39:20 +0000</pubDate>
		<dc:creator>zero</dc:creator>
				<category><![CDATA[programación]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[symfony]]></category>

		<guid isPermaLink="false">http://www.peix.org/?p=143</guid>
		<description><![CDATA[En Symfony 1.4, para añadir un campo de fichero hay que definir en la clase de formulario un widget sfWidgetFormInputFile o sfWidgetFormInputFileEditable y luego definiendo un validador tipo sfValidatorFile. Si el formulario con el campo de fichero se embede en otro formulario usando embedForm, al guardar los datos la cosa no funciona. Investigando, he llegado [...]]]></description>
			<content:encoded><![CDATA[<p>En <a href="http://www.symfony-project.org/">Symfony 1.4</a>, para añadir un campo de fichero hay que definir en la clase de formulario un widget <code>sfWidgetFormInputFile</code> o <code>sfWidgetFormInputFileEditable</code> y luego definiendo un validador tipo <code>sfValidatorFile</code>. Si el formulario con el campo de fichero se embede en otro formulario usando <code>embedForm</code>, al guardar los datos la cosa no funciona.<br />
<span id="more-143"></span></p>
<p>Investigando, he llegado a la conclusión de que el problema está en que el validador crea un objecto sfValidatedFile, pero al estar el formulario embedido, este objecto no se guarda correctamente. La solución es tan fácil como añadir el siguiente código a la clase de formulario base.</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;color: #ccc; font: 12px Consolas, Lucida Console, Monaco, monospace;">abstract <span class="kw2">class</span> BaseFormDoctrine <span class="kw2">extends</span> sfFormDoctrine
<span class="br0">&#123;</span>
  protected <span class="kw2">function</span> doProcessValuesUpdateObject<span class="br0">&#40;</span><span class="re0">$values</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="re0">$values</span> <span class="sy0">=</span> <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">processValues</span><span class="br0">&#40;</span><span class="re0">$values</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="kw1">foreach</span><span class="br0">&#40;</span><span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">embeddedForms</span> <span class="kw1">as</span> <span class="re0">$k</span><span class="sy0">=&gt;</span><span class="re0">$form</span><span class="br0">&#41;</span><span class="br0">&#123;</span>
      <span class="kw1">if</span><span class="br0">&#40;</span><span class="re0">$form</span> instanceof sfFormObject <span class="sy0">&amp;&amp;</span> <a href="http://www.php.net/isset"><span class="kw3">isset</span></a><span class="br0">&#40;</span><span class="re0">$values</span><span class="br0">&#91;</span><span class="re0">$k</span><span class="br0">&#93;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#123;</span>
        <span class="re0">$values</span><span class="br0">&#91;</span><span class="re0">$k</span><span class="br0">&#93;</span> <span class="sy0">=</span> <span class="re0">$form</span><span class="sy0">-&gt;</span><span class="me1">doProcessValuesUpdateObject</span><span class="br0">&#40;</span><span class="re0">$values</span><span class="br0">&#91;</span><span class="re0">$k</span><span class="br0">&#93;</span><span class="br0">&#41;</span><span class="sy0">;</span>
      <span class="br0">&#125;</span>
    <span class="br0">&#125;</span>
    <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">doUpdateObject</span><span class="br0">&#40;</span><span class="re0">$values</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="kw1">return</span> <span class="re0">$values</span><span class="sy0">;</span>
  <span class="br0">&#125;</span>
&nbsp;
  <span class="kw2">public</span> <span class="kw2">function</span> updateObject<span class="br0">&#40;</span><span class="re0">$values</span> <span class="sy0">=</span> <span class="kw4">null</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="kw1">if</span> <span class="br0">&#40;</span><span class="kw4">null</span> <span class="sy0">===</span> <span class="re0">$values</span><span class="br0">&#41;</span><span class="br0">&#123;</span>
      <span class="re0">$values</span> <span class="sy0">=</span> <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">values</span><span class="sy0">;</span>
    <span class="br0">&#125;</span>
    <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">doProcessValuesUpdateObject</span><span class="br0">&#40;</span><span class="re0">$values</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="kw1">return</span> <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">getObject</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
  <span class="br0">&#125;</span>
&nbsp;
<span class="br0">&#125;</span></pre></div></div>

<p>Y nada más de momento. Espero que la próxima versión 2.0 de Symfony corrija éste y otros errores en el código, porque por lo demás el framework me encanta.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.peix.org/2010/06/campos-de-fichero-en-formularios-embedidos-en-symfony-1-4/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Programando con Ogre3D</title>
		<link>http://www.peix.org/2010/05/programando-con-ogre3D/</link>
		<comments>http://www.peix.org/2010/05/programando-con-ogre3D/#comments</comments>
		<pubDate>Thu, 01 Jan 1970 00:00:00 +0000</pubDate>
		<dc:creator>zero</dc:creator>
				<category><![CDATA[programación]]></category>
		<category><![CDATA[c++]]></category>
		<category><![CDATA[ogre]]></category>

		<guid isPermaLink="false">http://www.peix.org/?p=130</guid>
		<description><![CDATA[Estoy empezando a programar un poco usando Ogre3D, una librería multiplataforma para hacer juegos 3D en C++. A parte estoy usando otra librería, CEGUI, para dibujar los botones y ventanas de las diferentes opciones. Y uno de los primeros problemas que me he encontrado es que Ogre3D no tiene soporte nativo para cargar archivos que [...]]]></description>
			<content:encoded><![CDATA[<p>Estoy empezando a programar un poco usando <a href="http://www.ogre3d.org/">Ogre3D</a>, una librería multiplataforma para hacer juegos 3D en C++. A parte estoy usando otra librería, <a href="http://www.cegui.org.uk/">CEGUI</a>, para dibujar los botones y ventanas de las diferentes opciones. Y uno de los primeros problemas que me he encontrado es que Ogre3D no tiene soporte nativo para cargar archivos que contengan varias entidades (polígonos, cámaras, etc&#8230;). En la Wiki del proyecto hay <a href="http://www.ogre3d.org/wiki/index.php/New_DotScene_Loader">varios</a> <a href="http://www.ogre3d.org/wiki/index.php/MOGRE_dotSceneLoader">códigos</a> de ejemplo para cargar archivos xml en formato .scene.<br />
<span id="more-130"></span><br />
Lo que no me molaba es que usaban una librería en concreto, <a href="http://www.grinninglizard.com/tinyxml/">tinyxml</a>. Así que he hecho mi propia versión usando la clase CEGUIXMLParser que viene con CEGUI, de esa manera se puede seleccionar la librería XML cuando se configura CEGUI (Xerces, libxml, tinyxml o otras).</p>
<pre name="code" class="cpp">
#include "DotSceneAttributes.h"

DotSceneAttributes::DotSceneAttributes(const String &#038;n, const CEGUI::XMLAttributes &#038;attribs)
{
    mName = n;
    mAttribs = attribs;
    children.clear();
    parent = 0;
}

DotSceneAttributes::~DotSceneAttributes()
{
    children.clear();
}

const String DotSceneAttributes::getName()
{
    return mName;
}

bool DotSceneAttributes::hasAttribute(const String &#038;attribName)
{
    return mAttribs.exists(attribName);
}

const String DotSceneAttributes::getString(const String &#038;attribName, const String &#038;defaultValue)
{
    try{
        if(mAttribs.exists(attribName))
            return mAttribs.getValue(attribName).c_str();
    }catch(...)
    {
    }
    return defaultValue;
}

Real DotSceneAttributes::getReal(const String &#038;attribName, Real defaultValue)
{
    try{
        if(mAttribs.exists(attribName))
            return StringConverter::parseReal(mAttribs.getValue(attribName).c_str());
    }catch(...)
    {
    }
    return defaultValue;
}

bool DotSceneAttributes::getBool(const String &#038;attribName, bool defaultValue)
{
    try{
        if(mAttribs.exists(attribName))
            return mAttribs.getValueAsBool(attribName);
    }catch(...)
    {
    }
    return defaultValue;
}

Vector3 DotSceneAttributes::getVector3()
{
	return Vector3(
		StringConverter::parseReal(getString("x","0")),
		StringConverter::parseReal(getString("y","0")),
		StringConverter::parseReal(getString("z","0"))
	);
}

Quaternion DotSceneAttributes::getQuaternion()
{
	Quaternion orientation;

	if(hasAttribute("qx"))
	{
		orientation.x = StringConverter::parseReal(getString("qx","0"));
		orientation.y = StringConverter::parseReal(getString("qy","0"));
		orientation.z = StringConverter::parseReal(getString("qz","0"));
		orientation.w = StringConverter::parseReal(getString("qw","0"));
	}
	else if(hasAttribute("axisX"))
	{
		Vector3 axis;
		axis.x = StringConverter::parseReal(getString("axisX","0"));
		axis.y = StringConverter::parseReal(getString("axisY","0"));
		axis.z = StringConverter::parseReal(getString("axisZ","0"));
		Real angle = StringConverter::parseReal(getString("angle","0"));;
		orientation.FromAngleAxis(Angle(angle), axis);
	}
	else if(hasAttribute("angleX"))
	{
		Vector3 axis;
		axis.x = StringConverter::parseReal(getString("angleX","0"));
		axis.y = StringConverter::parseReal(getString("angleY","0"));
		axis.z = StringConverter::parseReal(getString("angleZ","0"));
		orientation.FromAxes(&#038;axis);
	}

	return orientation;
}

ColourValue DotSceneAttributes::getColour()
{
	return ColourValue(
		StringConverter::parseReal(getString("r","0")),
		StringConverter::parseReal(getString("g","0")),
		StringConverter::parseReal(getString("b","0")),
		StringConverter::parseReal(getString("a","1"))
	);
}

DotSceneAttributes* DotSceneAttributes::addChild(DotSceneAttributes* child)
{
    child->setParent(this);
    children.push_back(*child);
    return child;
}

DotSceneAttributes* DotSceneAttributes::addChild(const String &#038;n, const CEGUI::XMLAttributes &#038;attribs)
{
    DotSceneAttributes *child = new DotSceneAttributes(n,attribs);
    return addChild(child);
}

void DotSceneAttributes::setParent(DotSceneAttributes* p)
{
    parent = p;
}

DotSceneAttributes* DotSceneAttributes::getParent()
{
    return parent;
}

DotSceneAttributes* DotSceneAttributes::getImmediateChild(const String &#038;name)
{
    itChildren    = children.begin();
    itChildrenEnd = children.end();
    for(; itChildren != itChildrenEnd; ++itChildren ) {
	    if( itChildren->getName() == name ) {
			return &#038;(*itChildren);
		}
    }
}

bool DotSceneAttributes::hasImmediateChild(const String &#038;name)
{
    itChildren    = children.begin();
    itChildrenEnd = children.end();
    for(; itChildren != itChildrenEnd; ++itChildren ) {
	    if( itChildren->getName() == name ) {
			return true;
		}
    }
    return false;
}

DotSceneAttributes* DotSceneAttributes::getChild(const String &#038;name)
{
    itChildren    = children.begin();
    itChildrenEnd = children.end();
    for(; itChildren != itChildrenEnd; ++itChildren ) {
	    if( itChildren->getName() == name ) {
			return &#038;(*itChildren);
		}else if(itChildren->hasChild(name)){
            itChildren->getChild(name);
        }
    }
}

bool DotSceneAttributes::hasChild(const String &#038;name)
{
    itChildren    = children.begin();
    itChildrenEnd = children.end();
    for(; itChildren != itChildrenEnd; ++itChildren ) {
	    if( itChildren->getName() == name ) {
			return true;
		}else if(itChildren->hasChild(name)){
			return true;
        }
    }
    return false;
}
</pre>
<p>Como el parser llama a dos callbacks, uno cuando empieza el elemento y uno cuando acaba, pasando como parámetro un objecto CEGUI::XMLAttributes que contiene los atributos del elemento XML. Así que primero he creado una clase adicional para encapsular XMLAttributes de todos los hijos del elemento, ya que hay varios elementos que necesitan acceder a valores de los hijos.</p>
<pre name="code" class="cpp">
#include "DotSceneLoader.h"

const char DotSceneLoader::s_sSceneTagName[]               = "scene";
const char DotSceneLoader::s_sNodesTagName[]               = "nodes";
const char DotSceneLoader::s_sNodeTagName[]                = "node";
const char DotSceneLoader::s_sExternalsTagName[]           = "externals";
const char DotSceneLoader::s_sEnvironmentTagName[]         = "environment";
const char DotSceneLoader::s_sTerrainTagName[]             = "terrain";
const char DotSceneLoader::s_sOctreeTagName[]              = "octree";
const char DotSceneLoader::s_sLightTagName[]               = "light";
const char DotSceneLoader::s_sCameraTagName[]              = "camera";
const char DotSceneLoader::s_sEntityTagName[]              = "entity";
const char DotSceneLoader::s_sVertexBufferTagName[]        = "vertexbuffer";
const char DotSceneLoader::s_sIndexBufferTagName[]         = "indexbuffer";
const char DotSceneLoader::s_sParticleSystemTagName[]      = "particlesystem";
const char DotSceneLoader::s_sBillboardSetTagName[]        = "billboardset";
const char DotSceneLoader::s_sPlaneTagName[]               = "plane";
const char DotSceneLoader::s_sSkyPlaneTagName[]            = "skyplane";
const char DotSceneLoader::s_sLookTargetTagName[]          = "looktarget";
const char DotSceneLoader::s_sTrackTargetTagName[]         = "tracktarget";
const char DotSceneLoader::s_sFogTagName[]                 = "fog";
const char DotSceneLoader::s_sSkyBoxTagName[]              = "skybox";
const char DotSceneLoader::s_sSkyDomeTagName[]             = "skydome";
const char DotSceneLoader::s_sUserDataReferenceTagName[]   = "userdatareference";

const char DotSceneLoader::s_sLightTypePoint[]         = "point";
const char DotSceneLoader::s_sLightTypeDirectional[]   = "directional";
const char DotSceneLoader::s_sLightTypeSpot[]          = "spot";
const char DotSceneLoader::s_sLightTypeRadPoint[]      = "radpoint";

const char DotSceneLoader::s_sCameraProjectionPerspective[] = "perspective";
const char DotSceneLoader::s_sCameraProjectionOrtographic[] = "ortographic";

const char DotSceneLoader::s_sTargetRelativeToLocal[] = "local";
const char DotSceneLoader::s_sTargetRelativeToParent[] = "parent";
const char DotSceneLoader::s_sTargetRelativeToWorld[] = "world";

const char DotSceneLoader::s_sFogModeNone[] = "none";
const char DotSceneLoader::s_sFogModeExp[] = "exp";
const char DotSceneLoader::s_sFogModeExp2[] = "exp2";
const char DotSceneLoader::s_sFogModeLinear[] = "linear";

DotSceneLoader::DotSceneLoader(SceneManager *yourSceneMgr, SceneNode *pAttachNode, const String &#038;sPrependNode)
{
	mSceneMgr = yourSceneMgr;
	m_sPrependNode = sPrependNode;
    pParentNode = pAttachNode;
    mAttributes = 0;
}

DotSceneLoader::~DotSceneLoader() {
    if(mAttributes){
        delete mAttributes;
    }
}

void DotSceneLoader::parseDotScene(const String &#038;sceneName, const String &#038;groupName)
{
    m_sGroupName = groupName;
	// set up shared object values
    try{
        CEGUI::System::getSingleton().getXMLParser()->parseXMLFile(*this, sceneName.c_str(), "", groupName);
	} catch(...) {
		//We'll just log, and continue on gracefully
		LogManager::getSingleton().logMessage("[DotSceneLoader] Error creating XmlDocument");
		return;
	}
}

void DotSceneLoader::elementStart(const CEGUI::String&#038; name, const CEGUI::XMLAttributes&#038; XMLattribs)
{
    DotSceneAttributes *attribs = new DotSceneAttributes(String(name.c_str()), XMLattribs);
    if(mAttributes){
        mAttributes->addChild(attribs);
    }
    mAttributes = attribs;

    if(name == s_sSceneTagName){
        processScene(mAttributes);
    }else if(name == s_sNodeTagName){
        processNode(mAttributes);
    }
}

void DotSceneLoader::elementEnd(const CEGUI::String&#038; name)
{
    if(name == s_sNodesTagName){
        finishNodes(mAttributes);
    }else if(name == s_sNodeTagName){
        finishNode(mAttributes);
    }else if(name == s_sExternalsTagName){
        finishExternals(mAttributes);
    }else if(name == s_sEnvironmentTagName){
        finishEnvironment(mAttributes);
    }else if(name == s_sTerrainTagName){
        finishTerrain(mAttributes);
    }else if(name == s_sOctreeTagName){
        finishOctree(mAttributes);
    }else if(name == s_sLightTagName){
        finishLight(mAttributes);
    }else if(name == s_sCameraTagName){
        finishCamera(mAttributes);
    }else if(name == s_sEntityTagName){
        finishEntity(mAttributes);
    }else if(name == s_sVertexBufferTagName){
        finishVertexBuffer(mAttributes);
    }else if(name == s_sIndexBufferTagName){
        finishIndexBuffer(mAttributes);
    }else if(name == s_sParticleSystemTagName){
        finishParticleSystem(mAttributes);
    }else if(name == s_sBillboardSetTagName){
        finishBillboardSet(mAttributes);
    }else if(name == s_sPlaneTagName){
        finishPlane(mAttributes);
    }else if(name == s_sSkyPlaneTagName){
        finishSkyPlane(mAttributes);
    }else if(name == s_sLookTargetTagName){
        finishLookTarget(mAttributes);
    }else if(name == s_sTrackTargetTagName){
        finishTrackTarget(mAttributes);
    }else if(name == s_sFogTagName){
        finishFog(mAttributes);
    }else if(name == s_sSkyBoxTagName){
        finishSkyBox(mAttributes);
    }else if(name == s_sSkyDomeTagName){
        finishSkyDome(mAttributes);
    }
    // add user data
    for ( unsigned int i = 0 ; i < mAttributes->children.size(); i++ ){
        if ( mAttributes->children[i].getName() == s_sUserDataReferenceTagName){
            pParentNode->setUserAny(Any(mAttributes->children[i].getString("id","")));
        }
    }
    if(mAttributes->getParent()){
        mAttributes = mAttributes->getParent();
    }
}

void DotSceneLoader::text(const String&#038; text)
{
}

void DotSceneLoader::processScene(DotSceneAttributes *attribs)
{
	// Process the scene parameters
	String version = attribs->getString("formatVersion", "unknown");

	String message = "[DotSceneLoader] Parsing dotScene file with version " + version;
	if(attribs->hasAttribute("ID"))
		message += ", id " + String(attribs->getString("ID",""));
	if(attribs->hasAttribute("sceneManager"))
		message += ", scene manager " + String(attribs->getString("sceneManager",""));
	if(attribs->hasAttribute("minOgreVersion"))
		message += ", min. Ogre version " + String(attribs->getString("minOgreVersion",""));
	if(attribs->hasAttribute("author"))
		message += ", author " + String(attribs->getString("author",""));

	LogManager::getSingleton().logMessage(message);
}

void DotSceneLoader::finishNodes(DotSceneAttributes *attribs)
{
}

void DotSceneLoader::processNode(DotSceneAttributes *attribs)
{
	// Construct the node's name
	String name = m_sPrependNode + attribs->getString("name","");

	String id = attribs->getString("id","");
	bool isTarget = attribs->getBool("isTarget",false);

	// Create the scene node
	SceneNode *pNode;
    if(pParentNode){
        if(attribs->hasAttribute("name")) {
            // Let Ogre choose the name
            pNode = pParentNode->createChildSceneNode();
        }else{
            // Provide the name
            pNode = pParentNode->createChildSceneNode(name);
        }
        pParentNode = pNode;
    }
}

void DotSceneLoader::finishNode(DotSceneAttributes *attribs)
{
	//finish other attributes
    if(pParentNode){
        if(attribs->hasImmediateChild("position")){
            pParentNode->setPosition(attribs->getImmediateChild("position")->getVector3());
        }
        if(attribs->hasImmediateChild("rotation")){
            Quaternion orientation = attribs->getImmediateChild("rotation")->getQuaternion();
            if(orientation != Quaternion::ZERO){
                pParentNode->setOrientation(orientation);
            }
        }
        if(attribs->hasImmediateChild("scale")){
            pParentNode->setScale(attribs->getImmediateChild("scale")->getVector3());
        }
        pParentNode->setInitialState();
        pParentNode = pParentNode->getParentSceneNode();
    }
}

void DotSceneLoader::finishExternals(DotSceneAttributes *attribs)
{
}

void DotSceneLoader::finishEnvironment(DotSceneAttributes *attribs)
{
    mSceneMgr->setAmbientLight(attribs->getImmediateChild("colourAmbient")->getColour());
	//! @todo Set the background colour of all viewports (RenderWindow has to be provided then)
	//mViewport->setBackgroundColour(attribs->getImmediateChild("colourBackground"))->getColour();

}

void DotSceneLoader::finishTerrain(DotSceneAttributes *attribs)
{
	//! @todo Implement this
}

void DotSceneLoader::finishOctree(DotSceneAttributes *attribs)
{
	//! @todo Implement this
}

void DotSceneLoader::finishLight(DotSceneAttributes *attribs)
{
	//finish attributes
	String name = attribs->getString("name","");
	String id = attribs->getString("id","");

	// Create the light
	Light *pLight = mSceneMgr->createLight(name);
	if(pParentNode)
		pParentNode->attachObject(pLight);

	String sValue = attribs->getString("type","");
	if(sValue == s_sLightTypePoint)
		pLight->setType(Light::LT_POINT);
	else if(sValue == s_sLightTypeDirectional)
		pLight->setType(Light::LT_DIRECTIONAL);
	else if(sValue == s_sLightTypeSpot)
		pLight->setType(Light::LT_SPOTLIGHT);
	else if(sValue == s_sLightTypeRadPoint)
		pLight->setType(Light::LT_POINT);

	pLight->setVisible(attribs->getBool("visible", true));
	pLight->setCastShadows(attribs->getBool("castshadows", true));
    if(attribs->hasImmediateChild("normal")){
        pLight->setDirection(attribs->getImmediateChild("normal")->getVector3());
    }
    if(attribs->hasImmediateChild("colourdiffuse")){
        pLight->setDiffuseColour(attribs->getImmediateChild("colourdiffuse")->getColour());
    }
    if(attribs->hasImmediateChild("coloursepcular")){
        pLight->setDiffuseColour(attribs->getImmediateChild("colourspecular")->getColour());
    }
    if(attribs->hasImmediateChild("lightrange")){
        DotSceneAttributes *range = attribs->getImmediateChild("lightrange");
        pLight->setSpotlightRange(Angle(range->getReal("inner",0.0)), Angle(range->getReal("outer",0.0)), range->getReal("falloff",1.0));
    }
    if(attribs->hasImmediateChild("lightattenuation")){
        DotSceneAttributes *atten = attribs->getImmediateChild("lightattenuation");
        pLight->setAttenuation(atten->getReal("range",0.0), atten->getReal("constant",0.0),
                atten->getReal("linear",0.0), atten->getReal("quadratic",0.0));
    }
}

void DotSceneLoader::finishCamera(DotSceneAttributes *attribs)
{
	//finish attributes
	String name = attribs->getString("name","");
	String id = attribs->getString("id","");
	Real fov = attribs->getReal("fov", 45);
	Real aspectRatio = attribs->getReal("aspectRatio", 1.3333);
	String projectionType = attribs->getString("projectionType", s_sCameraProjectionPerspective);

	// Create the camera
	Camera *pCamera = mSceneMgr->createCamera(name);
	if(pParentNode)
		pParentNode->attachObject(pCamera);

	// Set the field-of-view
	//! @todo Is this always in degrees?
	pCamera->setFOVy(Ogre::Degree(fov));

	// Set the aspect ratio
	pCamera->setAspectRatio(aspectRatio);

	// Set the projection type
	if(projectionType == s_sCameraProjectionPerspective)
		pCamera->setProjectionType(PT_PERSPECTIVE);
	else if(projectionType == s_sCameraProjectionOrtographic)
		pCamera->setProjectionType(PT_ORTHOGRAPHIC);

    if(attribs->hasImmediateChild("position")){
        pCamera->setPosition(attribs->getImmediateChild("position")->getVector3());
    }
    if(attribs->hasImmediateChild("rotation")){
        pCamera->setOrientation(attribs->getImmediateChild("rotation")->getQuaternion());
    }
    if(attribs->hasImmediateChild("lookat")){
        pCamera->lookAt(attribs->getImmediateChild("lookat")->getVector3());
    }
    if(attribs->hasImmediateChild("clipping")){
        DotSceneAttributes *clip = attribs->getImmediateChild("clipping");
        if(clip->hasAttribute("near")){
            pCamera->setNearClipDistance(clip->getReal("near",0));
        }
        if(clip->hasAttribute("far")){
            pCamera->setFarClipDistance(clip->getReal("far",0));
        }
    }
}

void DotSceneLoader::finishEntity(DotSceneAttributes *attribs)
{
	//finish attributes
	String name = attribs->getString("name","");
	String id = attribs->getString("id","");
	String meshFile = attribs->getString("meshFile","");
	String materialFile = attribs->getString("materialFile","");
	bool isStatic = attribs->getBool("static", false);;
	bool castShadows = attribs->getBool("castShadows", true);

	// Create the entity
	Entity *pEntity = 0;
	try {
		MeshManager::getSingleton().load(meshFile, m_sGroupName);
		pEntity = mSceneMgr->createEntity(name, meshFile);
		pEntity->setCastShadows(castShadows);
        if(pParentNode){
            pParentNode->attachObject(pEntity);
        }
		if(attribs->hasAttribute("materialFile")){
			pEntity->setMaterialName(materialFile);
        }
	} catch(Ogre::Exception &#038;) {
		LogManager::getSingleton().logMessage("[DotSceneLoader] Error loading an entity!");
	}
}

void DotSceneLoader::finishVertexBuffer(DotSceneAttributes *attribs)
{
}

void DotSceneLoader::finishIndexBuffer(DotSceneAttributes *attribs)
{
}

void DotSceneLoader::finishParticleSystem(DotSceneAttributes *attribs)
{
	//finish attributes
	String name = attribs->getString("name","");
	String id = attribs->getString("id","");
	String file = attribs->getString("file","");

	// Create the particle system
	try {
		ParticleSystem *pParticles = mSceneMgr->createParticleSystem(name, file);
		pParentNode->attachObject(pParticles);
	} catch(Ogre::Exception &#038;/*e*/) {
		LogManager::getSingleton().logMessage("[DotSceneLoader] Error creating a particle system!");
	}
}

void DotSceneLoader::finishBillboardSet(DotSceneAttributes *attribs)
{
	//! @todo Implement this
}

void DotSceneLoader::finishPlane(DotSceneAttributes *attribs)
{
	//! @todo Implement this
    String name = attribs->getString("name","");
    Real distance  = attribs->getReal("distance",0.0);
    Real width = attribs->getReal("width",4000);
    Real height = attribs->getReal("height",4000);

    int xSegments = (int)attribs->getReal("xSegments",1);
    int ySegments = (int)attribs->getReal("ySegments",1);
    short unsigned int numTexCoordSets = (int)attribs->getReal("numTexCoordSets",1);
    Real uTile = attribs->getReal("uTile",1.0);
    Real vTile = attribs->getReal("vTile",1.0);
    String material = attribs->getString("material","");
    bool normals = attribs->getBool("normals",true);
    bool movablePlane = attribs->getBool("movablePlane",false);
    bool castShadows = attribs->getBool("castShadows",false);
    bool receiveShadows = attribs->getBool("receiveShadows",true);
    Vector3 normal= Vector3::UNIT_Y;
    Vector3 upVector = Vector3::ZERO;

    if(attribs->hasImmediateChild("normal")){
        normal = attribs->getImmediateChild("normal")->getVector3();
    }

    if(attribs->hasImmediateChild("upvector")){
        upVector = attribs->getImmediateChild("upvector")->getVector3();
    }
    Plane pPlane(normal, upVector);
    Entity *pEntity;

    try {
        MeshPtr ptr = MeshManager::getSingleton().createPlane(name, m_sGroupName, pPlane, width, height, xSegments,
                ySegments, normals, numTexCoordSets, uTile, vTile, upVector);
        pEntity = mSceneMgr->createEntity(name, name);
        pEntity->setMaterialName(material,m_sGroupName);
        pParentNode->attachObject(pEntity);
    }catch (Exception e){
        LogManager::getSingleton().logMessage("[DotSceneLoader] Error loading a plane");
    }

}

void DotSceneLoader::finishSkyPlane(DotSceneAttributes *attribs)
{
	//finish attributes
	String material = attribs->getString("material","");
	Real planeX = attribs->getReal("planeX", 0);
	Real planeY = attribs->getReal("planeY", -1);
	Real planeZ = attribs->getReal("planeZ", 0);
	Real planeD = attribs->getReal("planeD", 4000);
	Real scale = attribs->getReal("scale", 1000);
	Real bow = attribs->getReal("bow", 0);
	Real tiling = attribs->getReal("tiling", 10);
	bool drawFirst = attribs->getBool("drawFirst", true);

	// Setup the sky plane
	Plane plane;
	plane.normal = Vector3(planeX, planeY, planeZ);
	plane.d = planeD;
	mSceneMgr->setSkyPlane(true, plane, material, scale, tiling, drawFirst, bow, 1, 1, m_sGroupName);
}

void DotSceneLoader::finishLookTarget(DotSceneAttributes *attribs)
{
	//finish attributes
	String nodeName = attribs->getString("nodeName","");
    Vector3 localDirection = Vector3::ZERO;
    Vector3 position = Vector3::ZERO;
	Node::TransformSpace relativeTo = Node::TS_PARENT;
	String sValue = attribs->getString("relativeTo", s_sTargetRelativeToLocal);

    if(attribs->hasImmediateChild("position")){
        localDirection = attribs->getImmediateChild("position")->getVector3();
    }
    if(attribs->hasImmediateChild("localdirection")){
        localDirection = attribs->getImmediateChild("localDirection")->getVector3();
    }
	if(sValue ==  s_sTargetRelativeToLocal)
		relativeTo = Node::TS_LOCAL;
	else if(sValue == s_sTargetRelativeToParent)
		relativeTo = Node::TS_PARENT;
	else if(sValue ==  s_sTargetRelativeToWorld)
		relativeTo = Node::TS_WORLD;

	//! @todo Is this correct? Cause I don't have a clue actually
	// Setup the look target
	try
	{
		if(!nodeName.empty())
		{
			SceneNode *pLookNode = mSceneMgr->getSceneNode(nodeName);
			position = pLookNode->_getDerivedPosition();
		}

		pParentNode->lookAt(position, relativeTo, localDirection);
	}
	catch(Ogre::Exception &#038;/*e*/)
	{
		LogManager::getSingleton().logMessage("[DotSceneLoader] Error processing a look target!");
	}

}

void DotSceneLoader::finishTrackTarget(DotSceneAttributes *attribs)
{
	// Process attributes
	String nodeName = attribs->getString("nodeName","");
    Vector3 offset = attribs->getImmediateChild("offset")->getVector3();
    Vector3 localDirection = Vector3::ZERO;

    if(attribs->hasImmediateChild("localdirection")){
        localDirection = attribs->getImmediateChild("localDirection")->getVector3();
    }
	// Setup the track target
	try
	{
		SceneNode *pTrackNode = mSceneMgr->getSceneNode(nodeName);
		pParentNode->setAutoTracking(true, pTrackNode, localDirection, offset);
	}
	catch(Ogre::Exception &#038;/*e*/)
	{
		LogManager::getSingleton().logMessage("[DotSceneLoader] Error processing a track target!");
	}
}

void DotSceneLoader::finishFog(DotSceneAttributes *attribs)
{
	// Process attributes
	Real expDensity = attribs->getReal("expdensity", 0.001);
	Real linearStart = attribs->getReal("linearStart", 0.0);
	Real linearEnd = attribs->getReal("linearEnd", 1.0);
    ColourValue colourDiffuse = ColourValue::ZERO;

    if(attribs->hasImmediateChild("colourdiffuse")){
        colourDiffuse = attribs->getImmediateChild("colourdiffuse")->getColour();
    }

	FogMode mode = FOG_NONE;
	String sMode = attribs->getString("mode",s_sFogModeExp);
	if(sMode == s_sFogModeNone)
		mode = FOG_NONE;
	else if(sMode == s_sFogModeExp)
		mode = FOG_EXP;
	else if(sMode == s_sFogModeExp2)
		mode = FOG_EXP2;
	else if(sMode == s_sFogModeLinear)
		mode = FOG_LINEAR;

	mSceneMgr->setFog(mode, colourDiffuse, expDensity, linearStart, linearEnd);
}

void DotSceneLoader::finishSkyBox(DotSceneAttributes *attribs)
{
	// Process attributes
	String material = attribs->getString("material","");
	Real distance = attribs->getReal("distance", 5000);
	bool drawFirst = attribs->getBool("drawFirst", true);
    Quaternion rotation = Quaternion::ZERO;

    if(attribs->hasImmediateChild("rotation")){
        rotation = attribs->getImmediateChild("rotation")->getQuaternion();
    }

	// Setup the sky box
	mSceneMgr->setSkyBox(true, material, distance, drawFirst, rotation, m_sGroupName);
}

void DotSceneLoader::finishSkyDome(DotSceneAttributes *attribs)
{
	// Process attributes
	String material = attribs->getString("material","");
	Real curvature = attribs->getReal("curvature", 10);
	Real tiling = attribs->getReal("tiling", 8);
	Real distance = attribs->getReal("distance", 4000);
	bool drawFirst = attribs->getBool("drawfirst", true);
    Quaternion rotation = Quaternion::ZERO;

    if(attribs->hasImmediateChild("rotation")){
        rotation = attribs->getImmediateChild("rotation")->getQuaternion();
    }

	// Setup the sky dome
	mSceneMgr->setSkyDome(true, material, curvature, tiling, distance, drawFirst, rotation, 16, 16, -1, m_sGroupName);
}
</pre>
<p>Y esto es todo de momento. Seguiré informando, ahora estoy con el tema de las animaciones de esqueleto.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.peix.org/2010/05/programando-con-ogre3D/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Importando de Plog a WordPress</title>
		<link>http://www.peix.org/2010/05/importando-de-plog-a-wordpress/</link>
		<comments>http://www.peix.org/2010/05/importando-de-plog-a-wordpress/#comments</comments>
		<pubDate>Mon, 17 May 2010 22:21:49 +0000</pubDate>
		<dc:creator>zero</dc:creator>
				<category><![CDATA[programación]]></category>
		<category><![CDATA[hipocondría]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[plog]]></category>
		<category><![CDATA[wordpress]]></category>

		<guid isPermaLink="false">http://www.peix.org/?p=117</guid>
		<description><![CDATA[Bueno, me estoy pegando un curro de importar todos los datos de mi antiguo blog, Hipocondría Demagógica, de Plog a WordPress. En su momento, Hipocondría funcionaba con Plog 1.0, los desarroladores luego le cambiaron el nombre a LifeType y ahora va por la version 1.2.10. Sigo pensando que es superior en calidad de código al [...]]]></description>
			<content:encoded><![CDATA[<p>Bueno, me estoy pegando un curro de importar todos los datos de mi antiguo blog, <a href="http://tardemalnunca.blogspot.com/2007/10/el-mejor-blog-del-mundo-hipocondria.html">Hipocondría Demagógica</a>, de Plog a WordPress. En su momento, Hipocondría funcionaba con Plog 1.0, los desarroladores <a href="http://lifetype.net/page/aboutus">luego le cambiaron el nombre a LifeType</a> y ahora va por la version 1.2.10. Sigo pensando que es superior en calidad de código al WordPress, pero como el último se ha convertido en el estándar, pues he pensado que sería más fácil mantenerlo. Además WordPress está mejorando, lo que les pasa es que tienen que mantener la compatibilidad hacia atrás de unas funciones que son un desastre, en vez de una estructura más clara basada en patrones orientados a objetos.</p>
<p>De momento he escrito <a href="http://www.peix.org/code/wordpress-plog-0-3-importer-plugin/">un plugin</a> para importar los datos de una base de datos a la otra que incluso importa los multimedias. El problema que tengo ahora es que muchas historias estaban escritas usando <a href="http://en.wikipedia.org/wiki/Textile_%28markup_language%29">Textile</a>, así que me toca ir formateándolas de nuevo. A parte hay muchas imágenes que están subidas directamente al ftp de la época que usaba Movable Type. También estoy haciendo un tema parecido al original. En cuanto esté todo acabado, el dominio de hipocondria.org volverá a funciona, eso sí con los comentarios desactivados.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.peix.org/2010/05/importando-de-plog-a-wordpress/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Lifestreaming</title>
		<link>http://www.peix.org/2010/05/lifestreaming/</link>
		<comments>http://www.peix.org/2010/05/lifestreaming/#comments</comments>
		<pubDate>Fri, 14 May 2010 14:14:12 +0000</pubDate>
		<dc:creator>zero</dc:creator>
				<category><![CDATA[programación]]></category>
		<category><![CDATA[lifestreaming]]></category>
		<category><![CDATA[wordpress]]></category>

		<guid isPermaLink="false">http://www.peix.org/?p=104</guid>
		<description><![CDATA[Lifestreaming, éso es lo que quería yo hacer con este dominio, pero no sabía que se llamara así&#8230; Es como lo que tengo puesto debajo de las entradas, pero ordenado cronológicamente. Ya hay varias alternativas opensource, pero supongo que tiraré por algún plugin de wordpress.]]></description>
			<content:encoded><![CDATA[<p><a href="http://lifestreamblog.com/">Lifestreaming</a>, éso es lo que quería yo hacer con este dominio, pero no sabía que se llamara así&#8230; Es como lo que tengo puesto debajo de las entradas, pero ordenado cronológicamente. Ya hay <a href="http://code.google.com/p/storytlr/">varias</a> <a href="http://www.renalias.net/parklife.html">alternativas</a> opensource, pero supongo que tiraré por algún plugin de wordpress.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.peix.org/2010/05/lifestreaming/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Facebook peer-2-peer y opensource? Sí, por favor!</title>
		<link>http://www.peix.org/2010/05/facebook-peer-2-pear-y-opensource-si-por-favor/</link>
		<comments>http://www.peix.org/2010/05/facebook-peer-2-pear-y-opensource-si-por-favor/#comments</comments>
		<pubDate>Thu, 13 May 2010 16:37:12 +0000</pubDate>
		<dc:creator>zero</dc:creator>
				<category><![CDATA[programación]]></category>
		<category><![CDATA[facebook]]></category>
		<category><![CDATA[opensource]]></category>

		<guid isPermaLink="false">http://www.peix.org/?p=89</guid>
		<description><![CDATA[El otro día descubrí Diaspora, un proyecto que acaban de empezar unos estudiantes universitarios y que pretende ser un sustituto libre, descentralizado y cifrado del Facebook. Espero que tenga éxito porque el rollo de las redes sociales cerradas no mola nada.]]></description>
			<content:encoded><![CDATA[<p>El otro día descubrí <a href="http://joindiaspora.com/blog.html">Diaspora</a>, un proyecto que acaban de empezar unos estudiantes universitarios y que pretende ser un sustituto libre, descentralizado y cifrado del Facebook. Espero que tenga éxito porque el rollo de las redes sociales cerradas no mola nada.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.peix.org/2010/05/facebook-peer-2-pear-y-opensource-si-por-favor/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Facua denuncia a Powerbalance</title>
		<link>http://www.peix.org/2010/05/facua-denuncia-a-powerbalance/</link>
		<comments>http://www.peix.org/2010/05/facua-denuncia-a-powerbalance/#comments</comments>
		<pubDate>Tue, 11 May 2010 16:45:34 +0000</pubDate>
		<dc:creator>zero</dc:creator>
				<category><![CDATA[escepticismo]]></category>
		<category><![CDATA[powerbalance]]></category>
		<category><![CDATA[timo]]></category>

		<guid isPermaLink="false">http://www.peix.org/?p=85</guid>
		<description><![CDATA[Ya era hora de que alguien denunciara el timo de las pulseritas.]]></description>
			<content:encoded><![CDATA[<p>Ya era hora de que <a href="https://www.facua.org/es/noticia.php?Id=4991">alguien denunciara</a> <a href="http://cnho.wordpress.com/2010/05/06/las-pulseras-power-balance-historia-de-un-fraude-largamente-anunciado/#more-9818">el timo de las pulseritas</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.peix.org/2010/05/facua-denuncia-a-powerbalance/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Generar código Brainfck</title>
		<link>http://www.peix.org/2010/05/generar-codigo-brainfck/</link>
		<comments>http://www.peix.org/2010/05/generar-codigo-brainfck/#comments</comments>
		<pubDate>Tue, 11 May 2010 11:23:45 +0000</pubDate>
		<dc:creator>zero</dc:creator>
				<category><![CDATA[programación]]></category>
		<category><![CDATA[brainfck]]></category>

		<guid isPermaLink="false">http://www.peix.org/?p=52</guid>
		<description><![CDATA[En este artículo explico como escribir un programa que genere código Brainfck. El Brainfck es un lenguaje de programación creado por Urban Müller en 1993 con la intención de ser muy minimalista. Por eso, aunque sólo consta de 8 instrucciones (&#62;&#60;+-.,[]), es un lenguaje turing-completo. Veamos un poco como funciona y luego como escribir algunas [...]]]></description>
			<content:encoded><![CDATA[<p>En este artículo explico como escribir un programa que genere código <a href="http://en.wikipedia.org/wiki/Brainfuck">Brainfck</a>. El Brainfck es un lenguaje de programación creado por Urban Müller en 1993 con la intención de ser muy minimalista. Por eso, aunque sólo consta de 8 instrucciones (<code>&gt;&lt;+-.,[]</code>), es un lenguaje turing-completo. Veamos un poco como funciona y luego como escribir algunas estructuras típicas de otros lenguajes usando F*llacerebros.</p>
<p><span id="more-52"></span></p>
<p>Brainfck parte de un espacio de bytes de memoria que podemos considerar infinito y en el que sólo disponemos de un puntero. Las instrucciones disponibles hacen lo siguiente:</p>
<ul>
<li><code>&gt;</code> mueve el puntero un espacio hacia delante</li>
<li><code>&lt;</code> mueve el puntero un espacio hacia atrás</li>
<li><code>+</code> aumenta el valor del espacio apuntado en uno</li>
<li><code>-</code> decrementa el valor del espacio apuntado en uno</li>
<li><code>.</code> imprime el valor del espacio apuntado en formato char</li>
<li><code>,</code> lee un char y lo guarda en el espacio apuntado</li>
<li><code>[</code> comienzo de bucle que se repite mientras el espacio apuntado no sea cero</li>
<li><code>[</code> final de bucle</li>
</ul>
<p>Con estos sencillos bloques tenemos que ir construyendo patrones que podamos utilizar para realizar tareas repetitivas que en un programa en otro lenguaje serían obvias. La idea es que cada patrón utilice un bloque de bytes lo más reducido posible sin modificar el resto de la memoria.</p>
<h4>Vaciar un espacio</h4>
<pre name="code" class="brainck">[-]
</pre>
<p>Este código vacía el espacio apuntado, ya que repite el bucle hasta que el espacio queda a cero. Otra opción es mover el puntero por encima de todas las direcciones en las que hemos trabajado, ya que las direcciones no usadas empiezan estando a cero.</p>
<h4>Imprimir una cadena</h4>
<p>La manera óptima en cuanto a memoria de imprimir una cadena es coger un espacio, reducirlo a cero, luego sumar hasta obtener el carácter ascii deseado, imprimirlo y luego volver a reducirlo a cero y sumar hasta obtener el siguiente carácter.</p>
<pre name="code" class="brainck">++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.
[-]
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.
[-]
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.
[-]
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.
[-]
</pre>
<p>Como escribir tantos mases es un coñazo, lo mejor es escribir una función en otro lenguaje más sencillo que luego genere el código Brainfck. Una función en Python que generara este código a partir de una cadena dada sería:</p>
<pre name="code" class="python">def print_string(str):
    bf = ""
    for c in str:
        for i in range(ord(c)):
            bf += "+"
        bf += ".[-]"
    return bf
</pre>
<p>El código se podría optimizar para que nunca se resetee la posición, que se sume o se reste cada vez para obtener el valor deseado.</p>
<pre name="code" class="python">def print_string_opti(str):
    bf = ""
    l = 0;
    for c in str:
        c = ord(c)
        if l < c:
            for i in range(c-l):
                bf += "+"
        elif l > c:
            for i in range(l-c):
                bf += "-"
        bf += "."
        l = c
    bf += "[-]"
    return bf
</pre>
<h4>Mover un espacio</h4>
<p>Mover un espacio es bastante fácil. Nos metemos en un bucle y cada vez restamos uno del espacio actual y sumamos uno del siguiente, que al final quedará con el mismo valor que el original. El original quedará a cero.</p>
<pre name="code" class="brainfck">>[-]<
[->+<]</pre>
<p>Este código también se puede adaptar para hacer una negación, osea si el puntero actual vale cero, el siguiente no lo vale y si no vale cero, el siguiente vale cero. Ponemos el siguiente al principio a 1, luego entramos en el bucle con el anterior sólo si no vale cero y entonces vaciamos el siguiente.</p>
<pre name="code" class="brainfck">>[-]+<
[>[-]<[-]]</pre>
<h4>Copiar un espacio</h4>
<p>Copiar un espacio es parecido a moverlo, pero en vez de moverlo a un sitio lo movemos a dos.</p>
<pre name="code" class="brainfck">>[-]>[-]<<
[->+>+<<]</pre>
<p>Claro que el problema aquí es que hemos avanzado una posición. Lo que podemos hacer es volver a mover el valor 2 posiciones hacia atrás para tener la posición original igual y la copia en la siguiente posición.</p>
<pre name="code" class="brainfck">>[-]>[-]<<
[->+>+<<]
>>[-<<+>>]<<
</pre>
<h4>Una condición</h4>
<p>Una cosa muy normal en otros lenguajes es la extresión <code>if</code>, que en Brainfck no existe. Veamos como podemos simularla.</p>
<p>En Brainfck lo más sencillo es la condición de si el espacio actual no vale cero, porque al hacer un bucle, sólo se entra si el espacio actual no vale cero. Así que entramos en el bucle, subimos el puntero, ejecutamos el código, bajamos el puntero y luego vaciamos el espacio actual para no volver a entrar en el bucle. Veamos como queda:</p>
<pre name="code" class="brainfck">[> código si espacio no es cero <[-]]</pre>
<p>Para hacer una condición sólo si el puntero vale cero, usaremos primero la negación y luego el mismo código.</p>
<pre name="code" class="brainfck">>[-]+<
[>[-]<[-]]&gt;
[> código si espacio es cero <[-]]</pre>
<p>Para hacer un <code>if</code> <code>else</code> podemos usar el mismo sistema:</p>
<pre name="code" class="brainfck">>[-]+<
[>> código si el espacio no es cero <[-]<[-]]&gt;
[> código si espacio es cero <[-]]</pre>
<p>Algo más complicado es comparar una posición con un valor estático. Esto se debe a que al restar la posición, no queremos restar a cero, ya que algunos compiladores de Brainfck podrían dar un error. Así que cada vez que restamos, tenemos que hacer una copia del valor para ver si es cero. Así que copiaremos el valor un elemento por encima y luego si ese elemento no es cero, restaremos al original. Y así repetimos. Así no restará nunca a cero, pero si llegamos antes a cero de lo esperado, se contará como válido. Necesitaremos un registro de memoria adicional que empezará con el valor esperado y al que iremos restando sólo si el introducido no vale cero. Al acabar, si ese valor no es cero querá decir que no hemos llegado al final. Como es muy coñazo ponerlo en Brainfck, lo pongo directamente en Python</p>
<pre name="code" class="python">def copy():
    return  ">[-]>[-]<<[->+>+<<]>>[-<<+>>]<<"

def compare(c):
    bf = ""
    for i in range(ord(c)):
        bf += copy()
        bf += ">>>+<<<"
        bf += ">[>>-<<<->[-]]<"
    bf += ">>>[<<<+>>>[-]]<<<"
    return bf</pre>
<h4>Comparar cadenas</h4>
<p>Para comparar una cadena, solo hace falta concadenar varias comparaciones de las anteriores. Tenemos una posición que marca si se ha detectado un carácter erróneo, vale 1 si todo va bien o 0 si ha fallado. Así la siguiente comparación sólo se hace si el marcador vale 1.</p>
<pre name="code" class="python">def compare_str(str):
    bf = "[-]+"
    for c in str:
        bf += "[>,"
        bf += compare(c)
        bf += "<[-]]"
        bf += "+>[<->[-]]<"
    return bf
</pre>
<p>Pues eso es todo desde el mundo de Brainfck por ahora.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.peix.org/2010/05/generar-codigo-brainfck/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
