EDIT - I found a way to do what I wanted with reflection. See the solution below
Click for a Solution
This is slightly altered code I found from this page reflection - How to iterate a C# object, looking for all instances of a specific type, in order to build a separate list of those instances? - Stack Overflow
Click for Code
using System;
using System.Reflection;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public partial class ExtReflection
{
const int iterateLimitAmount = 5000;
public static List<T> FindAllInstancesRecursive<T>(object obj) where T : class
{
return FindAllInstancesRecursive<T>(obj, x => true);
}
public static List<T> FindAllInstancesRecursive<T>(object obj, Func<object, bool> limitSearchDelegate) where T : class
{
HashSet<object> exploredObjects = new HashSet<object>();
List<T> foundObjects = new List<T>();
int currentIteration = 0;
FindAllInstancesRecursive(obj, exploredObjects, foundObjects, limitSearchDelegate, ref currentIteration);
return foundObjects;
}
static void FindAllInstancesRecursive<T>(object obj, HashSet<object> exploredObjects, List<T> foundObjects, Func<object, bool> limitSearchDelegate, ref int currentIteration) where T : class
{
if(!CanContinue(obj, exploredObjects, limitSearchDelegate, currentIteration)) return;
exploredObjects.Add(obj);
IEnumerable iEnumerable = obj as IEnumerable;
if(iEnumerable != null)
{
foreach(object item in iEnumerable)
{
FindAllInstancesRecursive<T>(item, exploredObjects, foundObjects, limitSearchDelegate, ref currentIteration);
currentIteration++;
}
}else{
T possibleMatch = obj as T;
if(possibleMatch != null)
{
foundObjects.Add(possibleMatch);
}
Type type = obj.GetType();
FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.GetProperty);
foreach(FieldInfo field in fields)
{
object propertyValue = field.GetValue(obj);
FindAllInstancesRecursive<T>(propertyValue, exploredObjects, foundObjects, limitSearchDelegate, ref currentIteration);
currentIteration++;
}
}
}
static bool CanContinue(object obj, HashSet<object> exploredObjects, Func<object, bool> limitSearchDelegate, int currentIteration)
{
if(obj == null || exploredObjects.Contains(obj) || !limitSearchDelegate(obj)) return false;
if(currentIteration >= iterateLimitAmount)
{
Debug.Log("FindAllInstancesRecursive reached iterateLimitAmount");
return false;
}
return true;
}
}
Note - I put in the iterateLimitAmount as an extra precaution. You might not need it.
I use that code in this…
Click for Code
using System;
using System.Collections.Generic;
using ColladaDeserialization;
using UnityEngine;
namespace ColladaDeserialized
{
public class ColladaDataDeserialized
{
public ColladaDeserialize data {get; private set;}
public ColladaDataDeserialized (string filePath)
{
data = ColladaDeserialize.DeserializeFile(filePath);
StoreSources(data);
}
void StoreSources(ColladaDeserialize data)
{
List<ColladaSource> sourceList = ExtReflection.FindAllInstancesRecursive<ColladaSource>(data, x => x.GetType().Namespace == typeof(ColladaSource).Namespace);
List<ColladaInputUnshared> inputList = ExtReflection.FindAllInstancesRecursive<ColladaInputUnshared>(data, x => x.GetType().Namespace == typeof(ColladaInputUnshared).Namespace);
if(sourceList.Count > 0 && inputList.Count > 0)
{
foreach(ColladaInputUnshared input in inputList)
{
input.sourceElement = input.FindSourceElement(sourceList);
}
}
}
}
}
using System;
using System.Linq;
using System.Collections.Generic;
namespace ColladaDeserialization
{
public static partial class ColladaHelpers
{
public static ColladaSource FindSourceElement(this ColladaInputUnshared input, List<ColladaSource> sources)
{
return sources.FirstOrDefault(x => x.ID == input.source.Substring(1));
}
}
}
And in the end I can use it like this…
float[] vertexArray = collada.data.library_geometries.Geometry[0].Mesh.Vertices.Input[0].sourceElement.Float_Array.values;
I am trying to make a Collada (.dae) runtime importer and I’m having some issues with finding a good way to approach a certain problem.
Here is an example of a Collada file
Click for Collada Example
<?xml version="1.0" encoding="utf-8"?>
<COLLADA xmlns="http://www.collada.org/2005/11/COLLADASchema" version="1.4.1">
<asset>
<contributor>
<author>Blender User</author>
<authoring_tool>Blender 2.72.0 commit date:2014-10-21, commit time:11:38, hash:9e963ae</authoring_tool>
</contributor>
<created>2015-04-15T18:40:57</created>
<modified>2015-04-15T18:40:57</modified>
<unit name="meter" meter="1"/>
<up_axis>Z_UP</up_axis>
</asset>
<library_images/>
<library_geometries>
<geometry id="Cube_008-mesh" name="Cube.008">
<mesh>
<source id="Cube_008-mesh-positions">
<float_array id="Cube_008-mesh-positions-array" count="12">1 1 -1 1 -1 -1 1 1 1 1 -1 1</float_array>
<technique_common>
<accessor source="#Cube_008-mesh-positions-array" count="4" stride="3">
<param name="X" type="float"/>
<param name="Y" type="float"/>
<param name="Z" type="float"/>
</accessor>
</technique_common>
</source>
<source id="Cube_008-mesh-normals">
<float_array id="Cube_008-mesh-normals-array" count="6">1 0 0 1 0 0</float_array>
<technique_common>
<accessor source="#Cube_008-mesh-normals-array" count="2" stride="3">
<param name="X" type="float"/>
<param name="Y" type="float"/>
<param name="Z" type="float"/>
</accessor>
</technique_common>
</source>
<vertices id="Cube_008-mesh-vertices">
<input semantic="POSITION" source="#Cube_008-mesh-positions"/>
</vertices>
<polylist count="2">
<input semantic="VERTEX" source="#Cube_008-mesh-vertices" offset="0"/>
<input semantic="NORMAL" source="#Cube_008-mesh-normals" offset="1"/>
<vcount>3 3 </vcount>
<p>2 0 3 0 1 0 0 1 2 1 1 1</p>
</polylist>
</mesh>
</geometry>
</library_geometries>
<library_controllers/>
<library_visual_scenes>
<visual_scene id="Scene" name="Scene">
<node id="Cube" name="Cube" type="NODE">
<matrix sid="transform">0.5 0 0 0 0 0.5 0 0 0 0 0.5 0 0 0 0 1</matrix>
<instance_geometry url="#Cube_008-mesh"/>
</node>
</visual_scene>
</library_visual_scenes>
<scene>
<instance_visual_scene url="#Scene"/>
</scene>
</COLLADA>
I downloaded some C# scripts I found on the web (C# Collada 1.5 Classes download | SourceForge.net) that deserializes the collada file into objects that I can access with code. So for example, in order to get to
What is a good way for me to set things up so that any time there is a source=“#…” I can easily get the element the source is pointing to?
In order for me to do that at the moment, I would have to do something like
Click for Code Example
Dictionary<string, Source> meshSources = new Dictionary<string, Source>();
Dictionary<Semantic, Source> meshDatas = new Dictionary<Semantic, Source>();
foreach(Geometry geometry in collada.library_geometries)
{
foreach(Source source in geometry.Mesh.Source)
{
meshSources.Add(source.ID, source);
}
foreach(Input input in geometry.Mesh.Vertices.Input)
{
if(input.Semantic == Semantic.POSITION)
{
meshDatas.Add(input.Semantic, meshSources[input.source.Substring(1)]);
}
}
}
And that is only for the geometry. What if other elements also have inputs, such as animation. Then I would need to do this for all of them. I then started to think about trying to do reflection and searching for any typeof source, but I never did reflection before so I was running into issues. So I started wondering about how I can just deserialize only all of the source elements into a source array, but I’m not sure how to do that either as this is my first time dealing with serializing and deserializing.
If anyone knows of a way, please let us know!
An example of what I would like to have is to do something like collada.library_geometries.geometry[0].mesh.vertices.input[0].source.GetSourceElement(); and it will give me its source element to which I can do stuff like
float[ ] vertices = collada.library_geometries.geometry[0].mesh.vertices.input[0].source.GetSourceElement().float_array.values;