<?xml version="1.0"?>
<!--
XSL transformation to convert a COLLADA file (http://www.collada.org/)
to a BVH (Biovision Hierarchy) file
(http://www.cs.wisc.edu/graphics/Courses/cs-838-1999/Jeff/BVH.html).

COLLADA and BVH are both standard animation file formats supported by the
MotionNode system. BVH is an older format used for motion capture data.
COLLADA is a general 3D asset file format, a subset of which can be used
for motion capture data.

This transformation is not intended to handle all animations and poses
possible in a COLLADA file. The COLLADA format is much more flexible than
the BVH format.

+ Limitations
  - Does not handle rotation order conversions. It expects an X-Y-Z
    rotation order for the input animations.
  - Requires a single animation clip, with a uniform time step between each
    sample. All channels should have the same number of samples, or we will
    zero pad them for the BVH output.
  - Does not include any rotations that are part of the COLLADA node tree.
    Since COLLADA can specify any number of composed rotations this would
    be outside the scope of this transformation.
    
+ Usage
  - Requires an XSLT processor. You could try "xsltproc" available
    at http://www.xmlsoft.org/XSLT/.
    xsltproc collada_to_bvh.xsl take.dae >take.bvh
  - Or, use your web browser. See an example of this at
    http://www.motionnode.com/tools/take.dae. This file is transformed
    to a BVH by your web browser if it supports XSLT.
    Simply add the directive to the preamble of your COLLADA document,
    <?xml-stylesheet type="text/xsl" href="collada_to_bvh.xsl"?>
 
@file    http://www.motionnode.com/tools/collada_to_bvh.xsl
@author  Luke Tokheim, luke@motionnode.com
@version 1.0

(C) Copyright GLI Interactive LLC 2007. All rights reserved.

Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:

The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
-->
<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:dae="http://www.collada.org/2005/11/COLLADASchema"
                xmlns:str="http://xsltsl.org/string">
 <!--
 These functions are inlined at the end of this file.
 <xsl:import href="http://xsltsl.sourceforge.net/modules/stdlib.xsl"/>
 -->

 <!-- Plain text output. -->
 <xsl:output method="text"/>

 <!-- Strip all whitespace. -->
 <xsl:strip-space elements="*"/>
 
 <!-- Squash all nodes by default. -->
 <xsl:template match="*"/>

 <!-- Define a string for indentation. -->
 <xsl:variable name="indent_string">
  <xsl:text>  </xsl:text>
 </xsl:variable>

 <xsl:template match="/">
  <xsl:apply-templates select="dae:COLLADA"/>
 </xsl:template>

 <!-- COLLADA root element. -->
 <xsl:template match="dae:COLLADA">
  <xsl:variable name="frames">
   <xsl:call-template name="frame_count"/>
  </xsl:variable>
  <xsl:variable name="frame_time">
   <xsl:call-template name="frame_rate"/>
  </xsl:variable>

  
  <xsl:text>HIERARCHY
</xsl:text>  
  
  <xsl:apply-templates select="dae:library_visual_scenes/dae:visual_scene/dae:node"/>
  
  
  <xsl:text>MOTION
</xsl:text>

  <xsl:text>Frames: </xsl:text>
  <xsl:value-of select="$frames"/>
  <xsl:text>
</xsl:text>
  <xsl:text>Frame Time: </xsl:text>
  <xsl:value-of select="$frame_time"/>
  <xsl:text>
</xsl:text>

  <!-- Dump the motion frame data. -->
  <xsl:call-template name="motion_frame">
   <xsl:with-param name="count" select="$frames"/>
  </xsl:call-template>
 </xsl:template>

 <!-- Root node rule. -->
 <xsl:template match="dae:visual_scene/dae:node[not(@type) and (count(dae:node[@type='JOINT']) &gt; 0)]">
  <xsl:text>ROOT </xsl:text>
  <xsl:value-of select="@id"/>
  <xsl:text>
{
</xsl:text>
  <xsl:value-of select="$indent_string"/>
  <xsl:text>OFFSET 0 0 0
</xsl:text>
  <xsl:value-of select="$indent_string"/>
  <xsl:text>CHANNELS 6 Xposition Yposition Zposition Xrotation Yrotation Zrotation
</xsl:text>
  <xsl:apply-templates select="dae:node"/>
  <xsl:text>}
</xsl:text>
 </xsl:template>
 
 <!-- Joint node rule. -->
 <xsl:template match="dae:node/dae:node[@type='JOINT']">
  <xsl:call-template name="indentation"/>
  <xsl:text>JOINT </xsl:text>
  <xsl:value-of select="@id"/>
  <xsl:text>
</xsl:text>
  <xsl:call-template name="indentation"/>
  <xsl:text>{
</xsl:text>
  <xsl:apply-templates select="dae:translate"/>
  <xsl:call-template name="indentation"/>
  <xsl:value-of select="$indent_string"/>
  <xsl:text>CHANNELS 3 Xrotation Yrotation Zrotation
</xsl:text>
  <xsl:apply-templates select="dae:node"/>

  <xsl:if test="count(dae:node)=0">
   <xsl:call-template name="indentation"/>
   <xsl:text>  </xsl:text>
   <xsl:text>End Site
</xsl:text>
   <xsl:call-template name="indentation"/>
   <xsl:value-of select="$indent_string"/>
   <xsl:text>{
</xsl:text>
   <xsl:call-template name="indentation"/>
   <xsl:value-of select="$indent_string"/>
   <xsl:value-of select="$indent_string"/>
   <xsl:text>OFFSET 0 0 0
</xsl:text>
   <xsl:call-template name="indentation"/>
   <xsl:value-of select="$indent_string"/>
   <xsl:text>}
</xsl:text> 
  </xsl:if>
  
  <xsl:call-template name="indentation"/>
  <xsl:text>}
</xsl:text>
 </xsl:template>

 <!-- Use the COLLDA node translation to define the BVH offset. -->
 <xsl:template match="dae:node/dae:translate">
  <xsl:call-template name="indentation"/>
  <xsl:text>OFFSET </xsl:text>
  <xsl:apply-templates/>
  <xsl:text>
</xsl:text> 
 </xsl:template>

 <!--
 Dump a frame of motion, recurse until we have "count" of
 them. This is so complicated because the COLLADA channels
 are handled completely differently than the BVH motion
 data channels.
 -->
 <xsl:template name="motion_frame">
  <xsl:param name="count" select="10"/>
  <xsl:param name="iterator" select="0"/>

  <!-- Iterate through the frames of motion data. -->
  <xsl:if test="$iterator &lt; $count">
   <xsl:call-template name="str:generate-string">
    <xsl:with-param name="text" select="'0 '"/>
    <xsl:with-param name="count" select="6"/>
   </xsl:call-template>
   

   <!-- Iterate through the available animation channels. -->
   <xsl:for-each select="/dae:COLLADA/dae:library_animations/dae:animation/dae:channel">
    <!-- Get the id of the associated <sampler> element. -->
    <xsl:variable name="sampler_id">
     <xsl:value-of select="substring-after(@source,'#')"/>
    </xsl:variable>

    <!--
    Reference the <sampler> element by id. Get the id of the associated
    <input semantic="OUTPUT"> element.
    -->
    <xsl:variable name="source_id">
     <xsl:value-of select="substring-after(../dae:sampler[@id=$sampler_id]/dae:input[@semantic='OUTPUT']/@source,'#')"/>
    </xsl:variable>

    <!--
    Look up the ith element of the float_array associated with the
    <sampler> element.
    -->
    <xsl:call-template name="array_random_access">
     <xsl:with-param name="index" select="$iterator"/>
     <xsl:with-param name="array">
      <!-- Flatten the text nodes into a string. -->
      <xsl:value-of select="../dae:source[@id=$source_id]/dae:float_array"/>
     </xsl:with-param>
    </xsl:call-template>
    <xsl:text> </xsl:text>
   </xsl:for-each> 
   
   <!-- End of this sample, new line. -->
   <xsl:text>
</xsl:text>

   <!-- Recursion, increment the iterator variable. -->
   <xsl:call-template name="motion_frame">
    <xsl:with-param name="count" select="$count"/>
    <xsl:with-param name="iterator" select="$iterator + 1"/>
   </xsl:call-template>
  </xsl:if>
  
 </xsl:template>

 <!--
 Implement random access for a COLLADA array. For example,
 if x = "1.1 9.2 6.8" then x[0] = 1.1, x[1] = 9.2, and
 x[2] = 6.8. Use this to grab single elements from an array
 of data.
  
 Example:
   <xsl:call-template name="array_random_access">
    <xsl:with-param name="index" select="2"/>
    <xsl:with-param name="array" select="'1.1 9.2 6.8 4.2'">
   </xsl:call-template>
  
 Produces output "6.8". If the item is not found, then
 return the "default" value.
 -->
 <xsl:template name="array_random_access">
  <xsl:param name="array"/>
  <xsl:param name="default" select="0"/>
  <xsl:param name="index" select="0"/>

  <xsl:variable name="result">
   <xsl:call-template name="str:substring-after-at">
    <xsl:with-param name="text" select="$array"/>
    <xsl:with-param name="chars" select="' '"/>
    <xsl:with-param name="position" select="$index"/>
   </xsl:call-template>
  </xsl:variable>

  <xsl:choose>
   <xsl:when test="string-length($result)=0">
    <xsl:value-of select="$default"/>
   </xsl:when>
   <xsl:otherwise>
    <xsl:value-of select="$result"/>
   </xsl:otherwise>
  </xsl:choose>
 </xsl:template>

 <xsl:template name="frame_count">
  <!-- Iterate through the available animation channels. -->
  <xsl:for-each select="/dae:COLLADA/dae:library_animations/dae:animation[1]/dae:channel[1]">
   <!-- Get the id of the associated <sampler> element. -->
   <xsl:variable name="sampler_id">
    <xsl:value-of select="substring-after(@source,'#')"/>
   </xsl:variable>

   <!--
    Reference the <sampler> element by id. Get the id of the associated
    <input semantic="OUTPUT"> element.
    -->
   <xsl:variable name="source_id">
    <xsl:value-of select="substring-after(../dae:sampler[@id=$sampler_id]/dae:input[@semantic='OUTPUT']/@source,'#')"/>
   </xsl:variable>

   <!--
   Output the number of elements in the reference array.
   -->
   <xsl:value-of select="../dae:source[@id=$source_id]/dae:float_array/@count"/>
  </xsl:for-each>
 </xsl:template>

 <xsl:template name="frame_rate">
  <!-- Iterate through the available animation channels. -->
  <xsl:for-each select="/dae:COLLADA/dae:library_animations/dae:animation[1]/dae:channel[1]">
   <!-- Get the id of the associated <sampler> element. -->
   <xsl:variable name="sampler_id">
    <xsl:value-of select="substring-after(@source,'#')"/>
   </xsl:variable>

   <!--
   Reference the <sampler> element by id. Get the id of the associated
   <input semantic="OUTPUT"> element.
   -->
   <xsl:variable name="source_id">
    <xsl:value-of select="substring-after(../dae:sampler[@id=$sampler_id]/dae:input[@semantic='INPUT']/@source,'#')"/>
   </xsl:variable>

   <!--
   Read the first two sample time stamps. Compute the global
   frame rate as the difference between the two.
   -->
   <xsl:variable name="a">
    <xsl:call-template name="array_random_access">
     <xsl:with-param name="index" select="0"/>
     <xsl:with-param name="array">
      <!-- Flatten the text nodes into a string. -->
      <xsl:value-of select="../dae:source[@id=$source_id]/dae:float_array"/>
     </xsl:with-param>
    </xsl:call-template>
   </xsl:variable>

   <xsl:variable name="b">
    <xsl:call-template name="array_random_access">
     <xsl:with-param name="index" select="1"/>
     <xsl:with-param name="array">
      <!-- Flatten the text nodes into a string. -->
      <xsl:value-of select="../dae:source[@id=$source_id]/dae:float_array"/>
     </xsl:with-param>
    </xsl:call-template>
   </xsl:variable> 

   <xsl:value-of select="$b - $a"/>
   
  </xsl:for-each>
 </xsl:template> 

 <!--
 Generate context based indentation. Count the node elements.
 -->
 <xsl:template name="indentation">
  <xsl:call-template name="str:generate-string">
   <xsl:with-param name="text" select="$indent_string"/>
   <xsl:with-param name="count" select="count(ancestor::dae:node)"/>
  </xsl:call-template>
 </xsl:template>


 <!--
 The following two functions were taken from the XSLT Standard Library
 available at: http://xsltsl.sourceforge.net/.
  
 They can be removed if you want to use a newer version of the XSLTSL
 library. I believe this is adheres to the LGPL license of the XSLT
 Standard Library.
 -->
 <xsl:template name="str:substring-after-at">
  <xsl:param name="text"/>
  <xsl:param name="chars"/>
  <xsl:param name="position"/>
  <xsl:param name="all" select='false()'/>

  <xsl:choose>
   <xsl:when test='number($position) = 0 and $all'>
    <xsl:value-of select='$text'/>
   </xsl:when>
   <xsl:when test='number($position) = 0 and not($chars)'>
    <xsl:value-of select='$text'/>
   </xsl:when>
   <xsl:when test='number($position) = 0 and not(contains($text, $chars))'>
    <xsl:value-of select='$text'/>
   </xsl:when>
   <xsl:when test='not(contains($text, $chars))'>
   </xsl:when>
   <xsl:when test="number($position) = 0">
    <xsl:value-of select="substring-before($text, $chars)"/>
   </xsl:when>
   <xsl:otherwise>
    <xsl:call-template name="str:substring-after-at">
     <xsl:with-param name="text" select="substring-after($text, $chars)"/>
     <xsl:with-param name="chars" select="$chars"/>
     <xsl:with-param name="all" select="$all"/>
     <xsl:with-param name="position" select="$position - 1"/>
    </xsl:call-template>
   </xsl:otherwise>
  </xsl:choose>
 </xsl:template>
 
 <xsl:template name="str:generate-string">
  <xsl:param name="text"/>
  <xsl:param name="count"/>

  <xsl:choose>
   <xsl:when test="string-length($text) = 0 or $count &lt;= 0"/>
   <xsl:otherwise>
    <xsl:value-of select="$text"/>
    <xsl:call-template name="str:generate-string">
     <xsl:with-param name="text" select="$text"/>
     <xsl:with-param name="count" select="$count - 1"/>
    </xsl:call-template>
   </xsl:otherwise>
  </xsl:choose>
 </xsl:template>
 
</xsl:stylesheet>