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!
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.
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.
Well, the current OgreXMl importer doesn't work for OgreMax-Export anyway so… At least not in the form it's on svn.
Ah…didn't know that! One point more to change that! So some sample data from OgreMax-Exporter would be great…
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>
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?
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.
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.
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!
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
}
}
}
}
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
Cool, thx a lot! I will check and contribute it.
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