Game Development Community

dev|Pro Game Development Curriculum

Rudimentary dts to obj converter

by Thomas -elfprince13- Dickerson · 10/29/2009 (9:19 pm) · 26 comments

This resource adds a console function which statically (as in, no bones or animations) dumps the first mesh of a TSShapeInstance loaded from .dts to an .obj file, should be fairly easy to make it iterate (the function sets up i as an iterator variable to be used in an outer for loop, and then just doesn't have the for loop) and convert the rest of the meshes as well, but this filled my needs so I didn't bother. It should also note that it doesn't bother exporting materials, but it does keep your UV coordinates. This was originally going to conver to Milkshape 3D ASCII format, but there are some incompatibilities in how the normal vectors are handled between the two formats (DTS interpolates to a single normal vector per vertex), so I went with .obj instead.

ConsoleFunction(dumpDTStoObj, void, 3, 3, "(string objfile, string dts)")
{
	Resource<TSShape> shape;
	shape = ResourceManager->load(argv[2]);
	const char * str;
	if(shape.isNull())
		return;
	
	TSShapeInstance *shapeinst = new TSShapeInstance(shape, true);
	if(!shapeinst)
		return;
	
	FileStream stream;
	stream.open(argv[1], FileStream::Write);

	TSShape* mShape = shapeinst->getShape();
	
	Con::printf("Dumping %s to %s", argv[2], argv[1]);
	dumpLine("#obj file rn");
	U32 i;
	/*S32 frameCount = 0;
	for(i = 0; i < mShape->sequences.size(); i++){
		frameCount += mShape->sequences[i].numKeyframes;
	}
	//dumpLine(avar("Frames: %irn", frameCount));
	dumpLine("Frames: 1rn");	//only STATIC meshes for now, kthxbai
	dumpLine("Frame: 1rnrn");
	
	dumpLine(avar("Meshes: %irn", mShape->meshes.size()));*/
	
	// mesh: name, flags, material index
	i = 0; 	//make this loopy eventually
	TSMesh* tsm = mShape->meshes[i];
	
	// number of vertices
	dumpLine(avar("#%irnrn", tsm->verts.size()));
	
	ToolVector<Point2F> uvs;
	tsm->getUVs(TSMesh::tDiffuse, uvs);
	U32 j; // inner loopiness
	for(j = 0; j<tsm->verts.size(); j++){
		Point3F xyz = tsm->verts[j];
		// vertex: flags, x, y, z, u, v, bone index
		dumpLine(avar("v %f %f %frn", xyz.x, xyz.y, xyz.z));
	}
	dumpLine("rn");

	for(j = 0; j<tsm->verts.size(); j++){
		Point2F uv = uvs[j];
		dumpLine(avar("vt %f %frn", uv.x, uv.y));
	}
	dumpLine("rn");
	
	
	// number of normals
	U32 normcount = tsm->getFlags(TSMesh::UseEncodedNormals) ? tsm->encodedNorms.size() : tsm->norms.size();
	dumpLine(avar("#%irnrn", normcount));
	
	const Point3F * norms = tsm->getNormals(0);
	for(j = 0; j<normcount; j++){
		Point3F xyz = norms[j];
		// normal: x, y, z
		dumpLine(avar("vn %f %f %frn", xyz.x, xyz.y, xyz.z));
	}
	
	dumpLine("rn");

	// number of triangles
	U32 tris = 0;
	for(j = 0; j<tsm->primitives.size(); j++)
		tris += tsm->primitives[j].numElements;
		

	dumpLine(avar("# %irnrn",tris));
	
	dumpLine(avar("g Mesh%irnrn",i));
	for(j = 0; j<tsm->primitives.size(); j++){
		TSDrawPrimitive draw = tsm->primitives[j];
		// triangle: flags, vertex index1, vertex index2, vertex index3, normal index1, normal index 2, normal index 3, smoothing group
		if ( (draw.matIndex & TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Triangles)
		{
			for (S32 k=0; k<draw.numElements; )
			{
				U32 idx0 = tsm->indices[draw.start + k + 0];
				U32 idx1 =tsm->indices[draw.start + k + 1];
				U32 idx2 = tsm->indices[draw.start + k + 2];
				dumpLine(avar("f %i/%i/%i %i/%i/%i %i/%i/%irn", (idx0+1), (idx0+1), (idx0+1), (idx1+1), (idx1+1), (idx1+1), (idx2+1), (idx2+1), (idx2+1)));
				
				k += 3;
			}
		} else if((draw.matIndex & TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Strip){
			U32 idx0 = tsm->indices[draw.start + 0];
			U32 idx1;
			U32 idx2 = tsm->indices[draw.start + 1];
			U32 * nextIdx = &idx1;
			for (S32 k=2; k<draw.numElements; k++)
			{
				*nextIdx = idx2;
				nextIdx = (U32*) ( (dsize_t)nextIdx ^ (dsize_t)&idx0 ^ (dsize_t)&idx1);
				idx2 = tsm->indices[draw.start + k];
				if (idx0 == idx1 || idx0 == idx2 || idx1 == idx2)
					Con::printf("Warning, vertex redundancy: %i, %i, %i", idx0, idx1, idx2);
					//continue;
				dumpLine(avar("f %i/%i/%i %i/%i/%i %i/%i/%irn", (idx0+1), (idx0+1), (idx0+1), (idx1+1), (idx1+1), (idx1+1), (idx2+1), (idx2+1), (idx2+1)));
			}
			
		} else{
			dumpLine("# oops--don't really know how to handle thisrn");
		}
	}
	
	dumpLine("rn");
	
	stream.close();
	Con::printf("...dump complete");
}

About the author

C.S. PhD student at Brown University. Project lead for FreeBuild. Administrator, Cemetech tech community. Webmaster for the Village2Village Projects and the Vermont Sustainable Heating Initiative.

Page «Previous 1 2
#1
10/30/2009 (1:19 pm)
ill try it, thanks...
#2
10/30/2009 (1:53 pm)
If you end up making any particularly useful changes, I'd love to see them.
#3
11/01/2009 (7:43 pm)
I think this will help get the source to all those CSK models that were not included... even after purchasing the kit.

Appreciate the resource!

Edit: Just curious... did the post eat all the backslashes from your code because I noticed that the other tsDump functions have backslashes with all the rn statements.

I'm trying to manually add the backslash to each r & n in the code because on testing the original code I only get a 0kb empty .obj file.

Edit2: Nope trying to add the backslashes did nothing and will not even create an empty .obj anymore.

Edit3: Ok, I'm just plain doing something wrong here, theres no console messages and I cant even seem to create the empty .obj anymore, can you supply an example of how to call the function?
#4
11/01/2009 (11:41 pm)
dumpDTStoObj("test.obj","mod/data/shapes/test.dts");


The backslashes definitely got eaten but it should still at least compile and dump something to the console.
#5
11/02/2009 (4:49 am)
Ok, well it does compile (TSDump.cc file, at the bottom) and I've also noticed it has errors when the textures are not with the .dts in the original models directory...

But the textures are not the problem, I've tried to manually add-in the backslashes and still cannot seem to get the model converted, every once in a while upon trying differant methods it will create and empty .obj, but there is still no console messages saying good or bad.

Would it be possible to upload a .txt file version of this resource to eb's Unofficial Torque Public File Server, because it may be something as simple as another backslash that I do not know about.

torque.abigholeintheweb.com/

Thanks for the help.
#6
11/02/2009 (9:49 am)
That's very odd, as I'm more or less ignoring materials for the moment.
http://torque.abigholeintheweb.com/public_system/useruploads/dtstoobj.txt.zip

The only reason it should be completely blank is if one of these 2 checks is failing:
Resource<TSShape> shape;  
     shape = ResourceManager->load(argv[2]);  
     const char * str;  
     if(shape.isNull())  
         return;  
       
     TSShapeInstance *shapeinst = new TSShapeInstance(shape, true);  
     if(!shapeinst)  
         return;


Are you seeing either "Dumping somedtsfile to someobjfile" and/or "...dump complete" in the console?
#7
11/03/2009 (10:25 am)
I'm checking the text version now...

The only thing I've seen in the console was when it had the "missing texture" error/crash, other then that there is no notification of anything happening.

Edit: Tested with the text version and still no deal,
I'm getting a crash and empty .obj when I try it with the correct syntax, I'll try a debug build and see if I can pinpoint it...
#8
11/03/2009 (11:04 am)
Nothing at all would indicate to me that the point of failure is somewhere in these first few lines.

Resource<TSShape> shape;  
     shape = ResourceManager->load(argv[2]);  
     const char * str;  
     if(shape.isNull())  
         return;  
       
     TSShapeInstance *shapeinst = new TSShapeInstance(shape, true);  
     if(!shapeinst)  
         return;

If it managed to create an empty .obj file (without dumping anything into the console) it made it at least through here:

FileStream stream;  
     stream.open(argv[1], FileStream::Write);

but no farther than here:
TSShape* mShape = shapeinst->getShape();
#9
11/03/2009 (11:26 am)
I've taken a screenshot of the Debug callstack, not sure if it can help you but I cant tell what is useful or not.
i566.photobucket.com/albums/ss101/CS_MP/MPGE/Bugs/DTSDumpDebug.jpg
#10
11/03/2009 (11:39 am)
Our line numbering would seem to be slightly different, but it appears to be crashing on one of the calls to .size(), presumably earlier in the function rather than later, by virtue of the lack of output in the file.

if you aren't even seeing the results of this
Con::printf("Dumping %s to %s", argv[2], argv[1]);

in the console, and the obj file doesn't at least begin with the line
#obj file

(which should both happen before any call to size()), then the Streams buffers must not be getting written out before the crash. can you show me exactly what you have on line 267, and place
stream.flush();
immediately following every occurence of dumpLine(), and then check the contents of the .obj file again as well?
#11
11/03/2009 (11:52 am)
dumpLine(avar("#%irnrn", tsm->verts.size()));
Was at Line#267...

I'm trying the stream.flush right now... I'll update with results.

btw: thanks again for the help.

Edit: ok the .obj file is now printing:
#obj file
#12
11/03/2009 (11:58 am)
The other thing that might be able helpful would be to uncomment this line:
dumpLine(avar("Meshes: %i\r\n", mShape->meshes.size()));

and then prepend a hash before Meshes to maintain a syntactically valid .obj, so that you have.
dumpLine(avar("#Meshes: %i\r\n", mShape->meshes.size()));
stream.flush();

I'm more than a bit curious as to why verts would not have been initialized (which is my guess as to what's happening, I can't think of another reason why a Vector would crash when you ask for it's size)
#13
11/03/2009 (12:04 pm)
Ok the .obj file has this now:
#obj file 
#Meshes: 13

Edit: I should also mention I'm using TGE1.5.2, not sure if that'll be a problem or not.
#14
11/03/2009 (12:19 pm)
ok. so, I'd like to get some information about all of those meshes, to see what in particular is causing problems with the first one.

Insert immediately after the declaration of U32 i, compile and run, and then post the information from your console.log. I have a feeling that Decals need to be handled differently, but we'll see.
for(i = 0; i < mShape->meshes.size(); i++){

TSMesh ithMesh = mShape->meshes[i];
U32 mt = ithMesh->getMeshType();
U32 flags = ithMesh->getFlags();
Con::printf("Mesh Type:\r\nStMT %i\r\nSkMT %i\r\nDMT %i\r\nSoMT %i\r\nNMT %i\r\nFlags\r\nBillboard %i\r\nHasDetailTexture %i\r\nBillboardZAxis %i\r\nEncodedNorms %i\r\nHasLightTexture %i\r\n",(mt & StandardMeshType),(mt & SkinMeshType),(mt & DecalMeshType),(mt & SortedMeshType),(mt & NullMeshType), (flags & Billboard), (flags & HasDetailTexture), (flags & BillboardZAxis), (flags & UseEncodedNormals), (flags & HasLightTexture));


}
return;

#15
11/03/2009 (12:20 pm)
I'm using 1.5.2 as well, so that shouldn't be the issue. My guess is it's some aspect of the model storage that I'm not handling correctly because it wasn't needed in my application.
#16
11/03/2009 (12:27 pm)
Ok, tested with another model and it did not crash, though there were many
Warning, vertex redundancy:
errors, and most of the model was lost in conversion...

Tested it on yet another model and recieved the errors again... but it looked like it converted successfully.

Yet it still crashes on the CSK Jeep model on conversion...???

what would make some models crash and others work?
#17
11/03/2009 (12:34 pm)
"dumps the first mesh of a TSShapeInstance loaded from .dts to an .obj file, should be fairly easy to make it iterate (the function sets up i as an iterator variable to be used in an outer for loop, and then just doesn't have the for loop) and convert the rest of the meshes as well, but this filled my needs so I didn't bother."

This should take care of most of the model being lost in conversion.
As for the crash, please let me know what the output from the code I pasted into this post is. It will prevent the file from being written to .obj, but will give me very useful information about the file. In particular, if you could run it for your model that crashed, your incompletely converted model, and your conversion that appears successful and post the output here, and labeled with which each one was.
#18
11/03/2009 (1:03 pm)
ok, getting these errors on debug compile:
Compiling...
tsDump.cc
e:mpge archive 2zombie_event basicenginetstsdump.cc(255) : error C2440: 'initializing' : cannot convert from 'TSMesh *' to 'TSMesh'
        No constructor could take the source type, or constructor overload resolution was ambiguous
e:mpge archive 2zombie_event basicenginetstsdump.cc(256) : error C2819: type 'TSMesh' does not have an overloaded member 'operator ->'
        e:mpge archive 2zombie_event basicenginetstsmesh.h(73) : see declaration of 'TSMesh'
        did you intend to use '.' instead?
e:mpge archive 2zombie_event basicenginetstsdump.cc(256) : error C2232: '->TSMesh::getMeshType' : left operand has 'class' type, use '.'
e:mpge archive 2zombie_event basicenginetstsdump.cc(257) : error C2819: type 'TSMesh' does not have an overloaded member 'operator ->'
        e:mpge archive 2zombie_event basicenginetstsmesh.h(73) : see declaration of 'TSMesh'
        did you intend to use '.' instead?
e:mpge archive 2zombie_event basicenginetstsdump.cc(257) : error C2232: '->TSMesh::getFlags' : left operand has 'class' type, use '.'
e:mpge archive 2zombie_event basicenginetstsdump.cc(258) : error C2065: 'StandardMeshType' : undeclared identifier
e:mpge archive 2zombie_event basicenginetstsdump.cc(258) : error C2065: 'SkinMeshType' : undeclared identifier
e:mpge archive 2zombie_event basicenginetstsdump.cc(258) : error C2065: 'DecalMeshType' : undeclared identifier
e:mpge archive 2zombie_event basicenginetstsdump.cc(258) : error C2065: 'SortedMeshType' : undeclared identifier
e:mpge archive 2zombie_event basicenginetstsdump.cc(258) : error C2065: 'NullMeshType' : undeclared identifier
e:mpge archive 2zombie_event basicenginetstsdump.cc(258) : error C2065: 'Billboard' : undeclared identifier
e:mpge archive 2zombie_event basicenginetstsdump.cc(258) : error C2065: 'HasDetailTexture' : undeclared identifier
e:mpge archive 2zombie_event basicenginetstsdump.cc(258) : error C2065: 'BillboardZAxis' : undeclared identifier
e:mpge archive 2zombie_event basicenginetstsdump.cc(258) : error C2065: 'UseEncodedNormals' : undeclared identifier
e:mpge archive 2zombie_event basicenginetstsdump.cc(258) : error C2065: 'HasLightTexture' : undeclared identifier

btw: thanks for spelling out the first mesh part, that actually would probably be the reason for the incomplete conversion test.
#19
11/03/2009 (2:23 pm)
Ah, my bad,
this:
TSMesh ithMesh = mShape->meshes[i];

should be
TSMesh* ithMesh = mShape->meshes[i];

and I think that should take care of those compile errors.
#20
11/04/2009 (5:34 am)
I actually tried that when I saw the error and this is what I get upon a debug build:
Compiling...
tsDump.cc
e:mpge archive 2zombie_event basicenginetstsdump.cc(258) : error C2065: 'StandardMeshType' : undeclared identifier
e:mpge archive 2zombie_event basicenginetstsdump.cc(258) : error C2065: 'SkinMeshType' : undeclared identifier
e:mpge archive 2zombie_event basicenginetstsdump.cc(258) : error C2065: 'DecalMeshType' : undeclared identifier
e:mpge archive 2zombie_event basicenginetstsdump.cc(258) : error C2065: 'SortedMeshType' : undeclared identifier
e:mpge archive 2zombie_event basicenginetstsdump.cc(258) : error C2065: 'NullMeshType' : undeclared identifier
e:mpge archive 2zombie_event basicenginetstsdump.cc(258) : error C2065: 'Billboard' : undeclared identifier
e:mpge archive 2zombie_event basicenginetstsdump.cc(258) : error C2065: 'HasDetailTexture' : undeclared identifier
e:mpge archive 2zombie_event basicenginetstsdump.cc(258) : error C2065: 'BillboardZAxis' : undeclared identifier
e:mpge archive 2zombie_event basicenginetstsdump.cc(258) : error C2065: 'UseEncodedNormals' : undeclared identifier
e:mpge archive 2zombie_event basicenginetstsdump.cc(258) : error C2065: 'HasLightTexture' : undeclared identifier

It eliminated some of the errors but it seems that it still has an error with the "Con::printf" functions strings/variables.

...This function should work at the bottom of the tsDump.cc file right?
Page «Previous 1 2