Jump to content
  • Advertisement
Sign in to follow this  
SGStino

Collada2DirectX Loading

This topic is 2434 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

hey,

i'm trying to load a collada file into vertex and index buffers with triangle primitives for DirectX11.

the problem i'm facing now is the speed of loading loads of indices.

lets say i have a mesh with triangles and vertices containing only POSITION coordinates, no NORMALs, no TEXCOORDs, for the sake of simplicity.

with indices like this:
indices:"0 1 2 0 2 3 0 3 4"

specifying 3 triangles with vertices at "0 1 2", "0 2 3" and "0 3 4".

if i were to just simply copy those indices into an index buffer,

and build a vertex buffer by looking up the vertices in the vertex source once for each index,
then i would have a lot of duplicate data:
"a b c a c d a d e"

and a redundant index buffer of:
"0 1 2 3 4 5 6 7 8 "


this means 3 times vertex a, 2 times vertex c and 2 times vertex d in the example above.

so when i'm loading the vertices, i only add vertices when they are not unique, when they are, i just simply refer to the first occurrence of that vertex.

this gives me a more acceptable vertex buffer:
"a b c d e"

and an index buffer
"0 1 2 0 2 3 0 3 4"

but this process requires me to look into the vertex buffer to find matches and their index.
Even when accelerated by using a dictionary<vertex,index> system it's still rather slow.
Due to the required look-ups to previous data, this can't be efficiently ran parallel across threads either.

I know this should is a one-time operation, as the results of this operation will be saved in an own data format for the meshes.
But i'd still like to know if there is a faster way to do this?



Share this post


Link to post
Share on other sites
Advertisement
As far as I know (at least depending on how exactly you look for duplicates), that's pretty much the fastest that i know of, that's exactly how I do it. It probably just seems to take a long time because collada files are enormous, containing every possible bit of information a 3d model could ever possibly have.

Are you planning on shipping the collada files with your game? if so, don't use collada in your game at all. it will make your game very big memory wise, and just a waste of time to "unpack" all those big files, even if it is only when you first install the game or whatever. You should just extract the information from the collada files, store them in the format you would like, and just use that format for the game. You don't need all the extra information a collada file has. If your already doing what i just said, sorry to say something you already know, just thought i'd throw it in there to be more helpful. Also another thing you could do, is use a smaller format that doesn't contain everything under the sun, extract what you need from that format and store it into your own. there's a lot of formats out there, haha, but then again, you probably don't want to rewrite a whole loader. If you have a lot of collada files to convert, you could just write your program to do a batch of them and leave it running overnight, just a thought

Share this post


Link to post
Share on other sites

Are you planning on shipping the collada files with your game? if so, don't use collada in your game at all. it will make your game very big memory wise, and just a waste of time to "unpack" all those big files, even if it is only when you first install the game or whatever.

I guess i've already answered that one, haven't i :D?
[Quote name='SGStino'] I know this should is a one-time operation, as the results of this operation will be saved in an own data format for the meshes.
But i'd still like to know if there is a faster way to do this? [/quote]

and for the rest: ye, already importing static geometry from obj, easy, lightweight and quite fast.

but i'd like to speed up the import of the collada if possible at all. Just for the sake of not pissing off the 3D artists that want to try out every tiny little change in-engine.

It just seems to be pretty fast to import the collada file back in to max and out of their viewport.
At least lot faster then the 12 sec on my latest test of a 59406 vertice-sphere.

Share this post


Link to post
Share on other sites
sorry to be unhelpful, but i think you won't find a much faster way to import a collada file than what your already doing. The only possibility is to optimize your code. If you want to post the function that loads in the collada file, someone else or i could take a look at it and see if we can't optimize it a bit

Share this post


Link to post
Share on other sites
Hidden
lets see if i can extract the code from the classes.



First a reimplementation of ArraySegment, so it can be compared.

public class IndexPointer<T> where T
{ public IndexPointer(T[] array, int offset, int count) { ... }
public override int GetHashCode()
{
return array.Skip(offset).Take(count).Sum(t => t.GetHashCode());
}
public override bool Equals(object obj)
{
IndexPointer<T> other = obj as IndexPointer<T>;
if (other != null)
{
if (other.count != count) return false;
for (int i = 0; i < count; i++)
{
if (!other.array[other.offset + i].Equals(array[offset + i])) return false;
}
return true;
}
return base.Equals(obj);
}}


Dictionary<IndexPointer<uint>, uint> weldTable = new Dictionary<IndexPointer<uint>, uint>();
...

internal IndexPointer<uint> IndiceSelector(uint t)
{
return new IndexPointer<uint>(rawIndices, (int)(t * indexPerVertexCount), indexPerVertexCount)
}

uint totalIndex = 0;
internal uint WeldIndex(uint i)
{
IndexPointer<uint> indices = IndiceSelector(i);
//return i; // don't weld = speeeeed
lock (this)
{
uint index;
if (weldTable.TryGetValue(indices, out index))
{
return index;
}
index = totalIndex++;
weldTable.Add(indices, index);
return index;
}
}


//rest will follow when i'm on the next wifi hotspot

Share this post


Link to post
lets see if i can extract the code from the classes.



First a reimplementation of ArraySegment, so it can be compared.

public class IndexPointer<T> where T
{ public IndexPointer(T[] array, int offset, int count) { ... }
public override int GetHashCode()
{
return array.Skip(offset).Take(count).Sum(t => t.GetHashCode());
}
public override bool Equals(object obj)
{
IndexPointer<T> other = obj as IndexPointer<T>;
if (other != null)
{
if (other.count != count) return false;
for (int i = 0; i < count; i++)
{
if (!other.array[other.offset + i].Equals(array[offset + i])) return false;
}
return true;
}
return base.Equals(obj);
}}

then welding stuff:

Dictionary<IndexPointer<uint>, uint> weldTable = new Dictionary<IndexPointer<uint>, uint>();
...

internal IndexPointer<uint> IndiceSelector(uint t)
{
return new IndexPointer<uint>(rawIndices, (int)(t * indexPerVertexCount), indexPerVertexCount)
}

uint totalIndex = 0;
internal uint WeldIndex(uint i)
{
IndexPointer<uint> indices = IndiceSelector(i);
//return i; // don't weld = speeeeed

lock (this)
{
uint index;
if (weldTable.TryGetValue(indices, out index))
{
return index;
}
index = totalIndex++;
weldTable.Add(indices, index);
return index;
}
}

and then the loops, triangulator is an instance of the class above:


var sw = new System.Diagnostics.Stopwatch();
ColladaVertexElement[,] linearResults = new ColladaVertexElement[rawIndices.Length, inputCount]; // Array TOOOOO LONG, actual final size not know before welding is done
sw.Start();
for (uint ind = 0; ind < vertexCount; ind++)
{
uint index = triangulator.WeldIndex(ind);
for (int i = 0; i < inputCount; i++)
{
var element = triangulator.GetVertexElement(index, i);
linearResults[index, i] = element;
}
}
sw.Stop();
System.Diagnostics.Debug.WriteLine("Linear Welding took " + sw.Elapsed + ", result: " + triangulator.WeldedIndexCount + " indices");

var sw = new System.Diagnostics.Stopwatch();

sw.Start();
ColladaVertexElement[,] parallelResults = new ColladaVertexElement[rawIndices.Length, inputCount]; // TOOOOO LONG
Parallel.For(0, (long)vertexCount, ind =>
{
uint index = triangulator.WeldIndex((uint)ind);
for (int i = 0; i < inputCount; i++)
{
var element = triangulator.GetVertexElement(index, i);
parallelResults[(int)index, i] = element;
}
});
sw.Stop();
System.Diagnostics.Debug.WriteLine("Parallel Welding took " + sw.Elapsed + ", result: " + triangulator.WeldedIndexCount + " indices");


starting off from 237600 indices:

Linear Welding took 00:00:20.5755010, result: 6702 indices
Parallel Welding took 00:00:06.8879988, result: 6702 indices
on a I5 cpu.

when i tested at home on the I7, the difference between linear and parallel was much smaller.


EDIT: found the culprit.

return array.Skip(offset).Take(count).Sum(t => t.GetHashCode());

seems like skip and take don't recognize it's an array as i'd expect, and start enumerating from 0 to offset, discarding all values.
Which is slow when doing that millions of times, really slow.

replacing that linq with a simple for loop speeded the results above up from 00:00:06.8879988 to 00:00:00.0786838!

The VS2010 profiler is really nice, if you're not doing so much that the profiler crashes halfway through the tests :D

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!