[COMMITED]OgreXML-Importer: add userdefined properties to SceneObjects (14 posts)

  • Profile picture of ttrocha ttrocha32p said 2 years, 11 months ago:

    In ogre-format you can also add user-defined properties(meta-data) to scene-objects.
    e.g. in blender you can select an object swtich to LogicView(F4) and add a property
    (STRING,FLOAT,TIME,BOOL,INT)

    In the scene-xml you can find it here:
    * <scene formatVersion="1.0.0">
    *  <nodes>
    *    <node>
    *      …
    *      <userData>
    *        <property type="STRING" name="specialName" data="xyz"/>
    *        <property type="FLOAT" name="prop" data="0.0"/>
    *        <property type="BOOL" name="prop1" data="1"/>
    *        <property type="INT" name="prop2" data="0"/>
    *        <property type="TIME" name="prop3" data="3.0"/>
    *      </userData>
    *    </node>
    * </scene>

    That tag was not implemented yet. To do so I created a specialized DotScene-Object that extends  com.jme.scene.Node and has a HashMap for collecting the properties. After the scene is loaded you can
    access the data via the corresponding dotScene-node's getUserProperty(String name)-Method

    Here the new class:

    package com.jmex.model.ogrexml;
    
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.Set;
    
    import com.jme.scene.Node;
    
    public class DotSceneNode extends Node{
    
       /**
        *
        * DotSceneNode for ogre dotscene files
        *
        * Adds user defined properties to scene-objects that can be exported
        * by the OgreScene-Exporter
        *
        * There a 5 different types that are mapped to Java-Wrappers:
        * STRING->String
        * FLOAT->Float
        * TIME->Float
        * BOOL->Boolean
        * INT->Integer
        *
        * (e.g. blender:
        *          - LogicPanel(F4)->Add Property
        *          - select LogicProperties in OgreScene-Exporter
        * )
        *
        * It can be found in the dotscene.xml - file:
        *
        * <scene formatVersion="1.0.0">
        *     <nodes>
        *       <node>
        *         ...
        *         <userData>
        *           <property type="STRING" name="specialName" data="xyz"/>
        *           <property type="FLOAT" name="prop" data="0.0"/>
        *           <property type="BOOL" name="prop1" data="1"/>
        *           <property type="INT" name="prop2" data="0"/>
        *           <property type="TIME" name="prop3" data="3.0"/>
        *         </userData>
        *       </node>
        * </scene>
        *
        * @author Thomas Trocha (thomas.trocha (at) gmail.com)
        *
        */
       private static final long serialVersionUID = 1L;
       private HashMap<String,Object> userProperties;
       
       public DotSceneNode() {
          super();
          userProperties = new HashMap<String, Object>();
       }
    
       public DotSceneNode(String name) {
          super(name);
          userProperties = new HashMap<String, Object>();
       }
    
       protected void addUserProperty(String key,Object value)
       {
          userProperties.put(key, value);
       }
       
       /**
        *
        * get user-defined property
        *
        * @param key
        * @return
        */
       public Object getUserProperty(String key)
       {
          return userProperties.get(key);
       }
       
       public Set<String> getUserPropertyKeys()
       {
          return userProperties.keySet();
       }
    }
    

    Here the patch for SceneLoader.java

    Index: src/com/jmex/model/ogrexml/SceneLoader.java
    ===================================================================
    --- src/com/jmex/model/ogrexml/SceneLoader.java   (revision 4366)
    +++ src/com/jmex/model/ogrexml/SceneLoader.java   (working copy)
    @@ -276,10 +276,45 @@
                         lightNode = childNode;
                     }
                 } else if (tagName.equals("node")) {
    -                com.jme.scene.Node newNode = new com.jme.scene.Node();
    +                DotSceneNode newNode = new DotSceneNode();
                     loadNode(newNode, childNode);  // This is the recurse!
                     targetJmeNode.attachChild(newNode);
    -            } else if (!(childNode instanceof Text)) {
    +            }
    +            else if (tagName.equals("userData"))
    +            {
    +               NodeList props = childNode.getChildNodes();
    +               for (int j=0;j<props.getLength();j++)
    +               {
    +                  Node propNode = props.item(j);
    +                  tagName = propNode.getNodeName();
    +                  if (tagName.equals("property"))
    +                    {
    +                       String propType = getAttribute(propNode, "type");
    +                       String propKey = getAttribute(propNode,"name");
    +                       String propValue = getAttribute(propNode,"data");
    +                       Object property;
    +                       if (propType.equalsIgnoreCase("FLOAT") || propType.equalsIgnoreCase("TIME"))
    +                       {
    +                          property = new Float(propValue);
    +                       }
    +                       else if (propType.equalsIgnoreCase("BOOL"))
    +                       {
    +                          property = new Boolean(propValue);
    +                       }
    +                       else if (propType.equalsIgnoreCase("INT"))
    +                       {
    +                          property = new Integer(propValue);
    +                       }
    +                       else
    +                       {
    +                          property = new String(propValue);
    +                       }
    +                       ((DotSceneNode)targetJmeNode).addUserProperty(propKey, property);
    +                    }
    +                  System.out.println(tagName);
    +               }
    +            }
    +            else if (!(childNode instanceof Text)) {
                     logger.warning("Ignoring unexpected element '" + tagName
                             + "' of type " + childNode.getClass().getName());
                 }
    

    Actually that would be the first time I will comit something….so I will have to ask for write access!

  • Profile picture of Core-Dump Core-Dump7p said 2 years, 11 months ago:

    Seems a very useful feature.
    To request write access you have to write mojomonk, see sticky post.

    If its a one time thing, someone else can commit it also.

  • Profile picture of ttrocha ttrocha32p said 2 years, 11 months ago:

    The userData|Property-Tags seems to be non standard and only available in the blender dotscene-exporter.
    In the dotscene-dtd (http://www.ogre3d.org/wiki/index.php/DotSceneFormat) there is a tag "userDataReference" that seems to be similar.

    Can anyone who is using OgreMax-Export create a simple dotscene file with an object that has simple (String/Float) userdata added? (http://www.ogremax.com/Documents/OgreMaxSceneExporter-3DSMax/user-data-page.html) If that is possible. Maybe there is something like a "String" or a "Float"-Class.

  • Profile picture of Haladria Haladria said 2 years, 11 months ago:

    Well, the current OgreXMl importer doesn't work for OgreMax-Export anyway so… At least not in the form it's on svn.

  • Profile picture of ttrocha ttrocha32p said 2 years, 11 months ago:

    Ah…didn't know that! One point more to change that! So some sample data from OgreMax-Exporter would be great….

  • Profile picture of Haladria Haladria said 2 years, 11 months ago:

    Here's one not working with the current importer…

    Scene file, although without user data (I might ask my artist to create one later to post here):

    <scene formatVersion="1.0" upAxis="y" unitsPerMeter="39.3701" minOgreVersion="1.7" author="OgreMax Scene Exporter by Derek Nedelman (www.ogremax.com)">
        <environment>
            <colourAmbient r="0.521569" g="0.521569" b="0.521569" />
            <colourBackground r="0.760784" g="0.760784" b="0.760784" />
        </environment>
        <nodes>
            <node name="Box04">
                <scale x="1" y="1.0" z="1" />
                <position x="58.1994" y="0.0" z="21.5434" />
                <rotation qx="0" qy="0" qz="0" qw="1" />
                <entity name="Box04" id="6" meshFile="Box04.mesh" castShadows="true" receiveShadows="true">
                    <subentities>
                        <subentity index="0" materialName="mirror" />
                    </subentities>
                </entity>
            </node>
        </nodes>
        <renderTextures>
            <renderTexture name="Map4" width="512" height="512" textureType="2d" clearEveryFrame="true" autoUpdate="true" hideRenderObject="true">
                <backgroundColor r="0.760784" g="0.760784" b="0.760784" />
                <materials>
                    <material name="mirror" technique="0" pass="0" textureUnit="0" />
                </materials>
            </renderTexture>
        </renderTextures>
    </scene>
    

  • Profile picture of ttrocha ttrocha32p said 2 years, 11 months ago:

    Is only the OrgeMax-Scenefile not working or the mesh-files as well?

    Most helpful would be a scene with scene-files, meshes and material-files.

    The problem why the scene file is not working is that the jME-OgreImporter needs
    to load an external material-file with all materials of all entities in the scene. The needed
    xml-tag is not included==>NULLPOINTER-EXCEPTION

    So in order to make it work with the ogreMax-scene you should just need that:

    Index: src/com/jmex/model/ogrexml/SceneLoader.java
    ===================================================================
    --- src/com/jmex/model/ogrexml/SceneLoader.java   (revision 4375)
    +++ src/com/jmex/model/ogrexml/SceneLoader.java   (working copy)
    @@ -344,7 +344,14 @@
             // transformation attributes.  We should not ignore them.
             Node environment = getChildNode(sceneXmlNode, "environment");
     
    -        loadExternals(externals);
    +        try
    +        {
    +           loadExternals(externals);
    +        }
    +        catch (Exception e)
    +        {
    +           e.printStackTrace();
    +        }
             if (!modelsOnly) {
                 loadEnvironment(environment);
             }
    

    But I don't know what is about the materials. Are they exported by OgreMax for every Entity?

  • Profile picture of Haladria Haladria said 2 years, 11 months ago:

    The scene loader needs some minor changes to be able to work at all. The mesh files should be quite ok. The material loader requires a lot more work to fully support the ogre format.

  • Profile picture of Momoko_Fan Momoko_Fan366p said 2 years, 11 months ago:

    That scene file seems to have some sort of mirror effect applied to the box mesh. I doubt that you will ever be able to load such a file.. Really the Ogre3D importer in jME is only intended to support animated mesh support, the scene importer is there as an extra.

  • Profile picture of ttrocha ttrocha32p said 2 years, 11 months ago:

    I see it the same away. For me a Scene is just a composition of entities with given Translation,Rotation and Scale.
    I was just wondering why there was no external material-file specified in the OgreMax(like in every Blender dotscene-file). I think the cause is that OgreMax is build for handling .mesh files (is that right) and .mesh files have the material data already included. So there is no need for an external material file. But as long I can't see a complete ogremax-scene-archive I can just guess.

    The scene as it is at the moment with additional userData is everything I would expect from my scene-file (yet). Still very very good work momoko_Fan!

  • Profile picture of Haladria Haladria said 2 years, 11 months ago:

    Actually there is a .material file in the ogre format. And it contains a lot of information.

    Yes that scene has a mirror effect and that's about the only thing I have left to implement in my version of the loader. But it's really tricky to get right.  :-(

    here's the material file for the same scene:

    material NoMaterial
    {
       technique
       {
          pass
          {
             ambient 0.7 0.7 0.7
             diffuse 0.7 0.7 0.7
          }
       }
    }
    
    material mirror
    {
       receive_shadows off
       transparency_casts_shadows off
       technique Map1
       {
          pass Map2
          {
             ambient 0.698039 0.698039 0.698039 1
             diffuse 0.698039 0.698039 0.698039 1
             specular 0.898039 0.898039 0.898039 1 20
             emissive 0 0 0 1
             scene_blend one zero
             depth_check on
             depth_write on
             depth_func less_equal
             depth_bias 0 0
             alpha_rejection always_pass 0
             cull_hardware clockwise
             cull_software back
             lighting on
             shading gouraud
             polygon_mode solid
             colour_write on
             max_lights 8
             start_light 0
             iteration once
             texture_unit Map3
             {
                texture Map4 2d
                binding_type fragment
                tex_coord_set 0
                tex_address_mode wrap wrap wrap
                tex_border_colour 0 0 0 1
                filtering trilinear
                max_anisotropy 1
                mipmap_bias 0
                colour_op_ex modulate src_texture src_current
                alpha_op_ex modulate src_texture src_current
                colour_op_multipass_fallback one zero
                env_map off
             }
    
          }
    
       }
    
    }

  • Profile picture of golgauth golgauth said 1 year, 11 months ago:

    With the new features of JME2, you can do it like this :

                } else if (tagName.equals("node")) {
                    com.jme.scene.Node newNode = new com.jme.scene.Node();
                    //DotSceneNode newNode = new DotSceneNode();
                    loadNode(newNode, childNode);  // This is the recurse!
                    targetJmeNode.attachChild(newNode);
                } else if (tagName.equals("userData")) {
                   Node parentNode = childNode.getParentNode();
                   NodeList props = childNode.getChildNodes();
                   
                   
                   StringFloatMap floatAttrMap = null;
                   StringBoolMap boolAttrMap = null;
                   StringIntMap intAttrMap = null;
                   StringStringMap strAttrMap = null;
                   
                   for (int j=0;j<props.getLength();j++)
                   {
                      Node propNode = props.item(j);
                      tagName = propNode.getNodeName();
                      if (tagName.equals("property"))
                        {
                           String propType = getAttribute(propNode, "type");
                           String propKey = getAttribute(propNode,"name");
                           String propValue = getAttribute(propNode,"data");
                           
                           if (propType.equalsIgnoreCase("FLOAT") || propType.equalsIgnoreCase("TIME"))
                           {
                              if (floatAttrMap == null)
                              {
                                 floatAttrMap = new StringFloatMap();
                                targetJmeNode.setUserData("floatSpatialAppAttrs", floatAttrMap);
                              }
                            floatAttrMap.put(getAttribute(propNode,"name"), new Float(getAttribute(propNode,"data")));
                           }
                           else if (propType.equalsIgnoreCase("BOOL"))
                           {
                              if (boolAttrMap == null)
                              {
                                 boolAttrMap = new StringBoolMap();
                                targetJmeNode.setUserData("boolSpatialAppAttrs", boolAttrMap);
                              }
                              boolAttrMap.put(getAttribute(propNode,"name"), new Boolean(getAttribute(propNode,"data")));
                           }
                           else if (propType.equalsIgnoreCase("INT"))
                           {
                              if (intAttrMap == null) {
                                 intAttrMap = new StringIntMap();
                                 targetJmeNode.setUserData("intSpatialAppAttrs", intAttrMap);
                              }
                              intAttrMap.put(getAttribute(propNode,"name"), new Integer(getAttribute(propNode,"data")));                        
                           }
                           else
                           {
                              if (strAttrMap == null) {
                                 strAttrMap = new StringStringMap();
                                 targetJmeNode.setUserData("stringSpatialAppAttrs", strAttrMap);
                              }
                            strAttrMap.put(getAttribute(propNode,"name"), getAttribute(propNode,"data"));                        
                              }
                           
                           
                           
    //                       Object property;
    //                       if (propType.equalsIgnoreCase("FLOAT") || propType.equalsIgnoreCase("TIME"))
    //                       {
    //                          property = new Float(propValue);
    //                       }
    //                       else if (propType.equalsIgnoreCase("BOOL"))
    //                       {
    //                          property = new Boolean(propValue);
    //                       }
    //                       else if (propType.equalsIgnoreCase("INT"))
    //                       {
    //                          property = new Integer(propValue);
    //                       }
    //                       else
    //                       {
    //                          property = new String(propValue);
    //                       }
    //                       ((DotSceneNode)targetJmeNode).addUserProperty(propKey, property);
                         
                           // TODO : Implementer les autres types
                           
                       }
                      //System.out.println(tagName);
                   }
                } else if (!(childNode instanceof Text)) {
            logger.warning("Ignoring unexpected element '" + tagName
                            + "' of type " + childNode.getClass().getName());
                }
            }
    

    and access the datas like this :

             
    System.out.println("TAG = " + ((Map)(sceneNode.getChild("Plane_groundDotNode").getUserData("stringSpatialAppAttrs"))).get("tag"));
    

    So, no need for subclassing the Node class.

    Hope this will help

  • Profile picture of ttrocha ttrocha32p said 1 year, 11 months ago:

    Cool, thx a lot! I will check and contribute it.

  • Profile picture of ttrocha ttrocha32p said 1 year, 9 months ago:

    Ok, I checked and commit it! Beside the fact that

    new Boolean("1") == false

    Everything was perfect. I once struggled over that as well!!

    Keep on rocking and thx for the patch