Process SMIL directives in XML data: Difference between revisions

From Rosetta Code
Content added Content deleted
m ({{omit from|GUISS}})
m (flag for difficulty issues, use template magic to put solution in page)
Line 1: Line 1:
{{difficulty}}
{{draft task}}
{{draft task}}
<!-- {{task|3D}} -->
<!-- {{task|3D}} -->
Line 64: Line 65:


=={{header|OCaml}}==
=={{header|OCaml}}==
{{:Loading animated 3D data/OCaml}}

See [[Loading animated 3D data/OCaml]]


{{omit from|Batch File|No way of writing complex parsers and no access to GUI functions, let alone OpenGL.}}
{{omit from|Batch File|No way of writing complex parsers and no access to GUI functions, let alone OpenGL.}}

Revision as of 11:13, 11 May 2014

This task has been flagged for clarification due to it being believed to be too difficult to implement in a reasonable amount of effort in more than one (usually very specialised) language. It may need to be divided into multiple tasks or modified substantially so that multiple implementations are practical, and that may cause code on this page in its current state to be flagged incorrect afterwards. See this page's Talk page for discussion.
Process SMIL directives in XML data is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

OpenGL commonly requires the facility to be able to obtain geometric data and supplementary information, such as animation data, from a file. The task is to obtain data from a given file and place into appropriately organized X3D data structures. The Smil animation statements found within the file should be stored in an XML record, because X3D data structures are not supposed to contain Smil data.

Here is the pure X3D base of our file containing only geometry:

<lang xml><?xml version="1.0" ?> <X3D>

 <Scene>
   <Viewpoint position="0 0 8" orientation="0 0 1 0"/>
   <PointLight color='1 1 1' location='0 2 0'/>
   <Transform translation='0.0 0.0 0.0' scale='1 1 1'>
     <Shape>
       <Box size='2 1 2'/>
       <Appearance>
         <Material diffuseColor='0.0 0.6 1.0'/>
       </Appearance>
     </Shape>
   </Transform>
 </Scene>

</X3D></lang>

And here is the same data with additional Smil statments that you should load, display and animate with OpenGL (or something else):

<lang xml><?xml version="1.0" ?> <smil> <X3D>

 <Scene>
   <Viewpoint position="0 0 8" orientation="0 0 1 0"/>
   <PointLight color='1 1 1' location='0 2 0'/>
   <Transform translation='0.0 0.0 0.0' scale='1 1 1'>
     <Shape>
       <Box size='2 1 2'>
         <animate attributeName="size" from="2 1 2"
                                         to="1 2 1" begin="0s" dur="10s"/>
       </Box>
       <Appearance>
         <Material diffuseColor='0.0 0.6 1.0'>
           <animate attributeName="diffuseColor" from="0.0 0.6 1.0"
                                                   to="1.0 0.4 0.0" begin="0s" dur="10s"/>
         </Material>
       </Appearance>
     </Shape>
   </Transform>
 </Scene>

</X3D></smil></lang>

In the example file, there is only a <Box> primitive, try to implement the primitives below too: <lang xml> <Sphere radius="1"/>

 <Cylinder radius="1" height="2"/>
 <Cone bottomRadius="1" height="2"/></lang>

If you are in shape, try to implement the <seq> tag too (def, ex.), we could for example put successivelly a Box, a Sphere, a Cone into it.

It should be possible to insert "animate" tags against every attribute of the base file and it is important to be able to insert timelines into the data structure.

Warnings:

  • This task is a draft, you can modify it if you find a way to enhance it,
  • The coordinates in the X3D data are not important. For example, the provided box data may not be positioned centrally to the viewpoint, Please fix this if you can.


OCaml

Library: glMLite
Library: xml-light
Library: GLUT

<lang ocaml>(* loading the libraries *)

  1. directory "+xml-light" (* or maybe "+site-lib/xml-light" *)
  2. load "xml-light.cma"
  1. directory "+glMLite"
  2. load "GL.cma"
  3. load "Glu.cma"
  4. load "Glut.cma"


(* types, and scene graph *)

type time = float (* in seconds *)

type 'a anim = At of time * 'a | Change of time * time * 'a * 'a

type 'a timed = Static of 'a | Animated of 'a anim list

type float1 = float timed type float3 = (float * float * float) timed type float4 = (float * float * float * float) timed

type scene = scene_elem list and scene_elem =

 | Viewpoint of float3 * float4  (* position, orientation *)
 | PointLight of float3 * float3  (* location, color *)
 | Transform of transform_attr list

and transform_attr = Translation of float3 | Scale of float3

 | Rotation of float4
 | Contents of shape list

and shape = geom * appearance and appearance = appearance_attr list and appearance_attr = DiffuseColor of float3 and geom =

 | Box of float3
 | Sphere of float1  (* radius *)
 | Cylinder of float1 * float1  (* radius, height *)
 | Cone of float1 * float1  (* bottomRadius, height *)



(* parsing functions *)

let scan_float3 s =

 Scanf.sscanf s "%f %f %f" (fun x y z -> x,y,z) ;;

let scan_float4 s =

 Scanf.sscanf s "%f %f %f %f" (fun a x y z -> a,x,y,z) ;;

let scan_time s =

 Scanf.sscanf s "%fs" (fun sec -> sec) ;;


let mk_float3 v = Static(scan_float3 v) let mk_float4 v = Static(scan_float4 v)


let assoc_opt v li =

 try Some(List.assoc v li)
 with Not_found -> None

let find_opt f li =

 try Some(List.find f li)
 with Not_found -> None


let get_anim scan attr_name childs =

 List.fold_left (fun acc -> function
   Xml.Element ("animate", attrs, _) ->
     let this_attr_name = List.assoc "attributeName" attrs in
     if this_attr_name <> attr_name
     then (acc)
     else
       let from = scan(List.assoc "from" attrs)
       and to_  = scan(List.assoc "to" attrs)
       and begin_ = scan_time(List.assoc "begin" attrs)
       and dur    = scan_time(List.assoc "dur" attrs)
       in
       Change(begin_, begin_ +. dur, from, to_) :: acc
   | _ -> (acc)
 ) [] childs


let to_param scan attr_name attrs default childs =

 match assoc_opt attr_name attrs,
       get_anim scan attr_name childs with
 | None,   [] -> Static(default)
 | Some v, [] -> Static(scan v)
 | None,   anim -> Animated(anim)
 | Some v, anim -> Animated(At(0.0, scan v)::anim)

let to_float3 = to_param scan_float3 ;; let to_float4 = to_param scan_float4 ;; let to_float1 = to_param float_of_string ;;


let parse_geom = function

 | Xml.Element ("Box", attrs, childs) ->
     Box(to_float3 "size" attrs (2., 2., 2.) childs)
 | Xml.Element ("Sphere", attrs, childs) ->
     Sphere(to_float1 "radius" attrs (1.0) childs)
 | Xml.Element ("Cylinder", attrs, childs) ->
     let radius = (to_float1 "radius" attrs (1.0) childs)
     and height = (to_float1 "height" attrs (2.0) childs) in
     Cylinder(radius, height)
 | Xml.Element ("Cone", attrs, childs) ->
     let botRad = (to_float1 "bottomRadius" attrs (1.0) childs)
     and height = (to_float1 "height"       attrs (2.0) childs) in
     Cone(botRad, height)
 | _ -> assert false


let appearance_fold acc = function

 | Xml.Element ("Appearance", [], [
     Xml.Element ("Material", attrs, childs)]) ->
       DiffuseColor(to_float3 "diffuseColor" attrs (0.8, 0.8, 0.8) childs)::acc
 | _ -> (acc)


let filter_geom = function

 | Xml.Element ("Box",_,_)
 | Xml.Element ("Sphere",_,_)
 | Xml.Element ("Cylinder",_,_)
 | Xml.Element ("Cone",_,_) -> true
 | _ -> false

let parse_shape_contents c =

 let geom = parse_geom(List.find filter_geom c)
 and appearance = List.fold_left appearance_fold [] c in
 let shape = (geom, appearance) in
 (shape)


let parse_shape = function

 | Xml.Element("Shape", [], contents) ->
     (parse_shape_contents contents)
 | _ -> assert false

let filter_shape = function Xml.Element("Shape",_,_) -> true | _ -> false


let map_scene_elem = function

 | Xml.Element ("Viewpoint", attrs, childs) ->
     let position    = (to_float3 "position"    attrs (0., 0., 10.) childs)
     and orientation = (to_float4 "orientation" attrs (0.,0.,1.,0.) childs) in
     Viewpoint(position, orientation)
 | Xml.Element ("PointLight", attrs, childs) ->
     let location = (to_float3 "location" attrs (0., 0., 0.) childs)
     and color    = (to_float3 "color"    attrs (1., 1., 1.) childs) in
     PointLight(location, color)
 | Xml.Element ("Transform", attrs, contents) ->
     let transform_attrs = [] in
     let transform_attrs =
       match assoc_opt "translation" attrs with
       | Some translation -> Translation(mk_float3 translation)::transform_attrs
       | None -> (transform_attrs)
     in
     let transform_attrs =
       match assoc_opt "scale" attrs with
       | Some scale -> Scale(mk_float3 scale)::transform_attrs
       | None -> (transform_attrs)
     in
     let transform_attrs =
       match assoc_opt "rotation" attrs with
       | Some rotation -> Rotation(mk_float4 rotation)::transform_attrs
       | None -> (transform_attrs)
     in
     let shapes = List.filter filter_shape contents in
     let shapes = List.map parse_shape shapes in
     let transform_attrs = (Contents shapes)::transform_attrs in
     (* TODO animate (translation, rotation, scale) from childs *)
     Transform(transform_attrs)
 | _ -> assert false


let parse_scene = function

 | Xml.Element ("smil", [], [
     Xml.Element ("X3D", [], [
       Xml.Element ("Scene", [], scene_elems)])])
 | Xml.Element ("X3D", [], [
     Xml.Element ("Scene", [], scene_elems)]) -> List.map map_scene_elem scene_elems
 | _ -> assert false

(* end of parsing the datas *)


(* timeline functions *)

let inter1 t t1 t2 v1 v2 =

 v1 +. ((t -. t1) /. (t2 -. t1) *. (v2 -. v1))

let inter3 t t1 t2 (a1,b1,c1) (a2,b2,c2) =

 let m = (t -. t1) /. (t2 -. t1) in
 ( a1 +. (m *. (a2 -. a1)),
   b1 +. (m *. (b2 -. b1)),
   c1 +. (m *. (c2 -. c1)) )

let inter4 t t1 t2 (a1,b1,c1,d1) (a2,b2,c2,d2) =

 let m = (t -. t1) /. (t2 -. t1) in
 ( a1 +. (m *. (a2 -. a1)),
   b1 +. (m *. (b2 -. b1)),
   c1 +. (m *. (c2 -. c1)),
   d1 +. (m *. (d2 -. d1)) )

let rec val_at inter t = function

 | At(t1, v) :: At(t2,_) :: _
 | At(t1, v) :: Change(t2,_,_,_) :: _
   when t1 <= t && t < t2 -> (v)
 | At(t, v) :: [] -> (v)
 | Change(_,t2,_,v2) :: []
   when t >= t2 -> (v2)
 | Change(t1,t2,v1,v2) :: _
   when t1 <= t && t <= t2 -> inter t t1 t2 v1 v2
 | _ :: tl -> val_at inter t tl
 | [] -> assert false

let get_val inter t = function

 | Static v -> v
 | Animated anim -> val_at inter t anim

let get_val1 = get_val inter1 ;; let get_val3 = get_val inter3 ;; let get_val4 = get_val inter4 ;;


(* OpenGL rendering *)

open GL open Glu open Glut

let t = ref 0.0

let neg_vec (x, y, z) = (-. x, -. y, -. z)


let display scene = function () ->

 glClear [GL_COLOR_BUFFER_BIT; GL_DEPTH_BUFFER_BIT];
 glLoadIdentity ();
 List.iter (function
 | Viewpoint (position, orientation) ->
     let position    = get_val3 !t position
     and orientation = get_val4 !t orientation in
     let angle, x, y, z = orientation in
     glRotate ~angle ~x ~y ~z;
     glTranslatev (neg_vec position)
 | PointLight (location, color) ->
     let location = get_val3 !t location
     and color    = get_val3 !t color in
     ignore(location, color)
 | Transform cl ->
     List.iter (function
     | Scale scale ->
         let v = get_val3 !t scale in
         glScalev v
     | Translation vec ->
         let v = get_val3 !t vec in
         glTranslatev v
     | _ -> ()
     ) cl;
     List.iter (function
     | Contents cl ->
         List.iter (function
         | (Box size, appearance) ->
             List.iter (function
             | DiffuseColor color ->
                 glColor3v (get_val3 !t color)
             ) appearance;
             glPushMatrix ();
              glScalev (get_val3 !t size);
               glutSolidCube ~size:1.0;
             glPopMatrix ();
         | _ -> ()  (* TODO other primitives *)
         ) cl
     | _ -> ()
     ) cl;
 ) scene;
 glFlush ();
 glutSwapBuffers ();


let reshape ~width ~height =

 glMatrixMode GL_PROJECTION;
 glLoadIdentity ();
 gluPerspective 30. (float width /. float height) 2. 30.;
 glViewport 0 0 width height;
 glMatrixMode GL_MODELVIEW;
 glutPostRedisplay ();

let keyboard ~key ~x ~y =

 match key with
 | '\027' | 'q' -> exit(0)
 | _ -> ()

(* main *) let () =

 ignore(glutInit Sys.argv);
 glutInitDisplayMode [GLUT_RGBA; GLUT_DOUBLE; GLUT_DEPTH];
 glutInitWindowPosition ~x:200 ~y:200;
 glutInitWindowSize ~width:400 ~height:300;
 ignore(glutCreateWindow ~title:Sys.argv.(0));
 glEnable GL_DEPTH_TEST;
 let my_scene = parse_scene (Xml.parse_file Sys.argv.(1)) in
 let rec timer ~value =
   t := !t +. 0.01;
   glutTimerFunc ~msecs:value ~timer ~value;
 in
 let msecs = 10 in
 glutTimerFunc ~msecs ~timer ~value:msecs;
 glutIdleFunc ~idle:glutPostRedisplay;
 glutDisplayFunc ~display:(display my_scene);
 glutReshapeFunc ~reshape;
 glutKeyboardFunc ~keyboard;
 glutMainLoop ();
</lang>