PDA

View Full Version : 3DS, wavefront obj and milkshape ascii loader for delphi



noeska
13-06-2004, 05:50 PM
glModel (former name gl3ds) started out as a unit for loading .3ds files and rendering it using opengl.
It has been under development since 2002. From an 3ds loader it evolved to a multi format 3d object loader and saver.
Still new features are being added.

glModel loads

3D Studio files (.3ds) supporting meshes and materials
milkshape ascii files (.txt) supporting meshes, materials, bones and animations.
wavefront files (.obj) supporting meshes and materials
direct x ascii files (.x) supporting meshes and materials (broken)

glModel saves

milkshape ascii files (.txt) supporting meshes and materials
wavefront files (.obj) supporting meshes and materials

glModel has 2 render paths:
-OpenGL 1.0
-OpenGL 1.5 with vbo's

glModel is released under the mozilla public license.

The latest version of glModel can be found on http://www.noeska.com/dogl/glmodel.aspx and / or my latest post in this thread.

WILL
18-06-2004, 05:44 AM
Excellent! I have made fair use of 3ds myself, I hope this project continues to get developed.

noeska
02-08-2004, 01:08 PM
the gl3ds loader has been updated to support bumpmapping.

noeska
28-05-2007, 02:03 PM
Just to let you know i am still working on gl3ds.

Currently i am rewriting it to be be more 'modular' E.g. There now is a tmodel class works like timage. So in the futere more 3d object formats could be easily added in.
Render is now handled in inherited classes so (in theory) it is now easier to render models in other ways so it is drawn more efficiently.
Also maintenance will be easier as all code parts are seperated instead of having all in one large file.

The thing i am trying to improve is that some models still refuse to load properly like a car model that refuses to load properly. (Luckily) other commercial applications also have lots of problems with that model. But i have a feeling that most models are rendered correctly with the latest complete example for 3ds i released (way back). That is the main reason i did not release newer versions of yet.

Also i am stil working on getting a 3ds mesh to play its animations. But the results are to experimental to be published.

For getting animation into gl3ds i could use some help from people wo can read french! On the following thread there is a lively discussion on getting animation from 3ds models: http://forum.games-creators.org/showthread.php?t=3421 . I do not read french, but it seems to me that even skeleton animation would be possible from an 3ds file (WOW). But as i cannot read french :-( .

gl3ds already knows bone animation for milkshape animations so if i know what to read from the 3ds file it should be doable.

For milkhsape ascii a bone and animation save is in the pipeline, but for that i first need to finish the rewrite of gl3ds.
So when the new version is ready it should allow to make a 3D file converter from it or even something like milkhsape can be made with it.

But i feel it should be renamed from gl3ds to ????, let me know. And those wo read French do give me translations!

tpascal
28-05-2007, 04:31 PM
Yes, old 3ds file format support bones + animations, however the info about that topic is hard to find.

I sugest a old library released by autodesk called "3ds file toolkit" which allow to not just read but also write 3ds files in a esier way (forget about those chunk parser) so you get everything supported by 3ds files including bones and animations,

http://www.dxtre3d.com/temp/3dsftk.zip

It include the documentation in ms WORD (*.doc) format and some examples.

Unfortunatelly the library is for C language; few months ago i had the plan to build a wrapper DLL in C for been then used by delphi to return just the meshes, bones and animations in a structure similar to ms3d file format; i tried the demos included in the package and I even build a DLL that pass dumy data to delphi so i know it is perfectly posible however i abandoned the plan becouse...1.- i confirmed that i really hate the C language, 2.- then i was thinking if it is really worth the effort, the 3ds is an old format that dosent support weigthed vertex which is almost a must in these days, so for me maybe it would be a better idea to use now popular half life SMD format for get skined models in my engine.

The package include the full source code in C if you or someoene else want to do the herculean effort of a Pascal convertion; or if you want to develomp a wrapper DLL.

good luck.

noeska
30-03-2008, 03:05 PM
Finally a new release of gl3ds. It is now at v3.0alpha.

New features are:
- Multiple fileformat support (works like TImage)
- Multiple rendertarget support.

But do no go ahead and use it your next project yet.
As it is a total rewrite of gl3ds some old bugs may be back and some now may be there also.

Do test it. And report the bugs found.

Basic usage:


var
scene1: TBaseRender;

//Init
scene1 := TGlRender.Create(nil);
scene1.AddModel();
scene1.Models[0].LoadFromFile('hog2.txt');
scene1.UpdateTextures;
scene1.Init;

//Render
Scene1.AdvanceAnimation;
Scene1.Render;

//Close
scene1.Free;

Traveler: post edited to check new pascal syntax highlighter

noeska
04-01-2009, 07:31 PM
Finally a new release of gl3ds. It is now at v3.1alpha.

New features are:
- Support for reading the wavefront .obj fileformat
- Some small bugfixes

In the pipeline is support for reading the dirext .x fileformat

savage
05-01-2009, 12:42 AM
cool! and good to see it still being updated.

noeska
19-01-2009, 09:57 PM
The project recieved a new name: glModel

Besides the new name the project now has a svn repository and wavefront files (.obj) can now be saved.

Site:
http://www.noeska.com/dogl/glmodel.aspx

NecroDOME
27-01-2009, 09:07 AM
Nice, I'll check it when I get home!

noeska
04-02-2009, 07:17 PM
Did you know that it is also possible to create meshes from scratch (if you know what you are doing)

//dynamic mesh creation example ...
scene1.AddModel; //new model
scene1.Models[1].AddMesh; //new mesh
scene1.Models[1].AddMaterial; //new material
scene1.Models[1].Material[0].DiffuseRed:= 1.0; //make it red
scene1.Models[1].Material[0].DiffuseBlue:= 0.0;
scene1.Models[1].Material[0].DiffuseGreen:= 0.0;
scene1.Models[1].Material[0].AmbientRed:= 0.5; //make it red
scene1.Models[1].Material[0].IsAmbient:=true;
scene1.Models[1].Material[0].Name:='RedMat';

scene1.Models[1].Mesh[0].NumVertex := 8; //number of vertexes
v1.x := -1.0;
v1.y := -1.0;
v1.z := -1.0;
scene1.Models[1].Mesh[0].Vertex[0]:=v1;
v1.x := -1.0;
v1.y := -1.0;
v1.z := 1.0;
scene1.Models[1].Mesh[0].Vertex[1]:=v1;
v1.x := -1.0;
v1.y := 1.0;
v1.z := -1.0;
scene1.Models[1].Mesh[0].Vertex[2]:=v1;
v1.x := -1.0;
v1.y := 1.0;
v1.z := 1.0;
scene1.Models[1].Mesh[0].Vertex[3]:=v1;
v1.x := 1.0;
v1.y := -1.0;
v1.z := -1.0;
scene1.Models[1].Mesh[0].Vertex[4]:=v1;
v1.x := 1.0;
v1.y := -1.0;
v1.z := 1.0;
scene1.Models[1].Mesh[0].Vertex[5]:=v1;
v1.x := 1.0;
v1.y := 1.0;
v1.z := -1.0;
scene1.Models[1].Mesh[0].Vertex[6]:=v1;
v1.x := 1.0;
v1.y := 1.0;
v1.z := 1.0;
scene1.Models[1].Mesh[0].Vertex[7]:=v1;

scene1.Models[1].Mesh[0].NumVertexIndices := 36; //number of vertex indices
scene1.Models[1].Mesh[0].Face[0]:=0;
scene1.Models[1].Mesh[0].Face[1]:=2;
scene1.Models[1].Mesh[0].Face[2]:=4;

scene1.Models[1].Mesh[0].Face[3]:=4;
scene1.Models[1].Mesh[0].Face[4]:=2;
scene1.Models[1].Mesh[0].Face[5]:=6;

scene1.Models[1].Mesh[0].Face[6]:=0;
scene1.Models[1].Mesh[0].Face[7]:=4;
scene1.Models[1].Mesh[0].Face[8]:=1;

scene1.Models[1].Mesh[0].Face[9]:=1;
scene1.Models[1].Mesh[0].Face[10]:=4;
scene1.Models[1].Mesh[0].Face[11]:=5;

scene1.Models[1].Mesh[0].Face[12]:=0;
scene1.Models[1].Mesh[0].Face[13]:=1;
scene1.Models[1].Mesh[0].Face[14]:=2;

scene1.Models[1].Mesh[0].Face[15]:=2;
scene1.Models[1].Mesh[0].Face[16]:=1;
scene1.Models[1].Mesh[0].Face[17]:=3;

scene1.Models[1].Mesh[0].Face[18]:=4;
scene1.Models[1].Mesh[0].Face[19]:=6;
scene1.Models[1].Mesh[0].Face[20]:=5;

scene1.Models[1].Mesh[0].Face[21]:=5;
scene1.Models[1].Mesh[0].Face[22]:=6;
scene1.Models[1].Mesh[0].Face[23]:=7;

scene1.Models[1].Mesh[0].Face[24]:=2;
scene1.Models[1].Mesh[0].Face[25]:=3;
scene1.Models[1].Mesh[0].Face[26]:=6;

scene1.Models[1].Mesh[0].Face[27]:=6;
scene1.Models[1].Mesh[0].Face[28]:=3;
scene1.Models[1].Mesh[0].Face[29]:=7;

scene1.Models[1].Mesh[0].Face[30]:=1;
scene1.Models[1].Mesh[0].Face[31]:=5;
scene1.Models[1].Mesh[0].Face[32]:=3;

scene1.Models[1].Mesh[0].Face[33]:=3;
scene1.Models[1].Mesh[0].Face[34]:=5;
scene1.Models[1].Mesh[0].Face[35]:=7;

//apply material
scene1.Models[1].Mesh[0].MatName[0]:=scene1.Models[1].Material[0].Name;
scene1.Models[1].Mesh[0].MatID[0]:=0;

//add calculated normals ...
scene1.Models[1].Mesh[0].NumNormals:=12; //for each face indices div 3
scene1.Models[1].Mesh[0].NumNormalIndices:=36;
scene1.Models[1].CalcVnormals;

//add fake texture coords
scene1.Models[1].Mesh[0].NumMappings:=1;
scene1.Models[1].Mesh[0].NumMappingIndices:=36;
map.tu:=0;
map.tv:=0;
scene1.Models[1].Mesh[0].Mapping[0]:=map;
for tel:=0 to 35 do
begin
scene1.Models[1].Mesh[0].Map[tel]:=0;
end;

//make mesh visible
scene1.Models[1].Mesh[0].Visible:=true;

//calculate size for bounding box
scene1.Models[1].CalculateSize;

//determine render order even with one mesh
scene1.Models[1].CalculateRenderOrder;

//save test
scene1.Models[1].SaveToFile('test.obj');


Now this should be more 'programmer' friendly. So feedback is appreciated.

nb: the scene1.Model[1] is one because it is the second model in my test application.

Also normals calculation is once again in a rewrite cycle so you could expect a new release of glModel after some more testing. The new calculation method breaks current loaders that use it.

vgo
25-02-2009, 04:12 PM
Doesn't seem to work with Delphi 2009, I get access violations when trying to load any 3ds file.

noeska
25-02-2009, 09:52 PM
Erm it should not do that. But i cannot test on delphi2009. Could you post one of your .3ds meshes though so i can rule them out (there are just to many 'wrong' .3ds files). Also could you post the access violation text. Also what kind of video card are you using?

vgo
03-03-2009, 06:44 PM
It was my bad, I had not set all the class pointers... :)

How are the faces stored in the TBaseMesh class? The Face property is a word value, is this the index for the first vertex of the face?

noeska
03-03-2009, 09:58 PM
they are the indices for the faces but not per face so 3 is the first vertex of the second face. So 0,1,2 make up the first face. This reminds me i should update the glMesh to use them.

Could you provide some more detail on why you had to set the class pointers yourselves?

vgo
04-03-2009, 09:31 AM
Because I use it just to load the mesh and convert to my own engine's internal format. But I maybe I'm doing something wrong? :)

Like so:

function TMVMeshImporter.ImportMesh(const FileName: String): TMeshObject;
var
fe: String;
m3ds: T3dsModel;
begin
fe := LowerCase(ExtractFileExt(FileName));
if fe = '.3ds' then
begin
m3ds := T3dsModel.Create(nil);
m3ds.MeshClass := TBaseMesh;
m3ds.MaterialClass := TGLMaterial;
m3ds.SkeletonClass := TGLSkeleton;
m3ds.LoadFromFile(FileName);
Result := ConvertMesh(m3ds);
if Result.Name = '' then
Result.Name := ExtractFileName(FileName);
end;
end;

noeska
04-03-2009, 07:10 PM
It is better to use TBaseModel in this case.
The classes thing is back on the drawing board. As it should automaticly assign the base classes . The setting of TGLMesh, TGLMaterial, TGLSkeleton is only needed so the right class is used for rendering. In your case you should only have to use TBaseModel to load the .3ds model and then you could convert it to your own format.

Better would be to make your own descendant from TBaseModel e.g. take a look ModelMsa on how to do that. You then would be able to do:

MyModel.LoadFromFile('test.3ds');
MyModel.SaveToFile('myformat.ext');

I try to fix so it is easier to use tmodel/tbasemodel to load 3d model for conversion only.

noeska
04-03-2009, 08:09 PM
The changes for TBaseModel are in the svn now. It did not even have an custom create.

E.g. now you can do:

Model1 := TBaseModel.Create(nil);
Model1.LoadFromFile('models\tulip.3ds');
Model1.SaveToFile('tulip.txt');
Model1.Free;


If you need advice on how to write your own descendant from TBaseModel let me know.

vgo
05-03-2009, 10:50 AM
Thanks, I'll try the new version.

vgo
09-03-2009, 10:01 AM
The new version works and I can load all my test models, as long as the file extensions are in lower case, you should change the extension to lower case when testing which loader to use. :)

Thanks!

noeska
09-03-2009, 07:59 PM
Ok i modified that when looking up the class for the file extension lower case is used even if the filename extension is not in lower case.

paul_nicholls
19-05-2009, 06:51 AM
The changes for TBaseModel are in the svn now. It did not even have an custom create.

E.g. now you can do:

Model1 := TBaseModel.Create(nil);
Model1.LoadFromFile('models\tulip.3ds');
Model1.SaveToFile('tulip.txt');
Model1.Free;


If you need advice on how to write your own descendant from TBaseModel let me know.


Hi noeska,
I'm attempting to use your model load as a bridge between my format and the popular ones (.3ds, etc.)...

Anyhow when I try to use the LoadFromFile() method of the TBaseModel class, my program crashes because the variable FileFormats is Nil at that point :(

procedure TBaseModel.LoadFromFile(AFilename: string);
var
Ext: string;
GraphicClass: TBaseModelClass;
begin
Ext := ExtractFileExt(AFilename);
Delete(Ext, 1, 1);
GraphicClass := FileFormats.FindExt(Ext); <----BLOWS UP HERE, FileFormats is Nil!!!
LoadFromFile(GraphicClass, AFilename);

//Check if model is loaded

Calculatesize; //calculate min and max size
CalculateRenderOrder; //set transparency order...

//Needs to be called here and not before or else...
InitSkin;
end;


My code is basically this:


Uses
Model;

Var
Model : TBaseModel;
Begin
Model := TBaseModel.Create(Nil);
Try
Model.LoadFromFile(<some filename>);
....
Finally
Model.Free;
End;
End;

Any ideas?
cheers,
Paul

paul_nicholls
19-05-2009, 10:37 AM
I have it working now :)

I added these lines before using the BaseModel.LoadFromFile()

RegisterModelFormat('obj' ,'Wavefront Obj files',TObjModel);
RegisterModelFormat('3ds' ,'3ds max files',T3dsModel);
RegisterModelFormat('ms3d','Milkshape 3d files' ,TMsaModel);
RegisterModelFormat('x' ,'DirectX 3d files' ,TDXModel);


I do have another question...are all the faces in the basemodel triangles only?
It seems so from the source code and example you posted here...

cheers,
Paul

noeska
19-05-2009, 05:50 PM
What delphi version are you using?

E.g. including glmodel.pas in your uses should be enough to load .obj meshes

As

initialization
RegisterModelFormat('obj', 'Alias Wavefront Obj Model', TObjModel);

finalization
UnRegisterModelClass(TObjModel);


is at the end of glmodel.pas Or is that delphi2005 and above only?

paul_nicholls
19-05-2009, 08:41 PM
What delphi version are you using?

E.g. including glmodel.pas in your uses should be enough to load .obj meshes

As

initialization
RegisterModelFormat('obj', 'Alias Wavefront Obj Model', TObjModel);

finalization
UnRegisterModelClass(TObjModel);


is at the end of glmodel.pas Or is that delphi2005 and above only?


I'm using D2007 WIn32...

BTW, should I be using the zip file or the SVN version of the code?

cheers,
Paul

noeska
19-05-2009, 08:53 PM
If you want to use the 'latest features' use svn. I try to keep it in working order. That is not always possible so you better not use the .x reader.

paul_nicholls
19-05-2009, 11:29 PM
What delphi version are you using?

E.g. including glmodel.pas in your uses should be enough to load .obj meshes

As

initialization
RegisterModelFormat('obj', 'Alias Wavefront Obj Model', TObjModel);

finalization
UnRegisterModelClass(TObjModel);


is at the end of glmodel.pas Or is that delphi2005 and above only?


Could my problem have been because I am using your code inside a DLL?

Maybe the initialization part isn't called then?

cheers,
Paul

noeska
03-01-2010, 04:43 PM
There is new release for glmodel. Now it is at version 3.5a bringing a new vbo render target.
You can get i from svn or download it as an zip file from: http://www.noeska.net/downloads/glModel-3.5.0a.zip .