Well, you got my attention with having a milkscape import -- I've done some stuff in Milkshape and 3ds Max, mostly amateur hour mods for the game Freespace.... being able to use the stuff I make there in pascal? Loving the idea.
I'm going through your codebase now... I'm a bit out of touch with Delphi; Not even sure what the difference between CLASS and OBJECT is other than I HATE the syntax -- seems needlessly vague, almost like it came from C or something -- also having a dog of a time with the oddball formatting... but still, I'm getting the gist of it.
The swapRGB routine could use a bit of speedups, though the operating on a 24 bit image is gonna suck any way you do it. I'd give serious thought to promoting it to 32 bit anyways, since that's what openGL wants... do the work for it instead of it trying to deal with it on the fly or playing texture loader games. At 32 bit you could just call BSWAP and then ROL EAX,8.
Though a more robust BMP loader is definitely in order there anyhow.
in your ms3d handler I end up with a few odd questions -- TStaticModelGL and tAnimatedModelGL are perfectly good objects, why are you then using procedural programming for things like LoadAnimation? If anything, shouldn't that be the constructor for said object? You could also make the stream part of the object, reducing the overhead of passing so much on the stack further speeding up load times.
Wondering why you're brute-forcing parsing the path, instead of using extractFilePath -- delphi does have that, right? Where you have:
I'd probably have:
REPEAT Dec ( j ) UNTIL ( aPath [ j ] = '/' ) OR ( aPath [ j ] = '\' ) OR ( j <= 0 );
Path := Copy ( aPath, 0, j - 1 );
Of course that you've got path as a global... could cause confusion; why I'd probably put all that on TAnimatedModelGL as a constructor.
I'm also wondering why you're spending so much time selectively reading from the stream thus:
I'd probably have all those in a record like 'animHeader' so that I could just do them in one read.
aStream.Read ( AnimFPS, SizeOf ( AnimFPS ) );
aStream.Position := aStream.Position + SizeOf ( Single ); //Skip CurrentTime
aStream.Read ( TotalFrames, SizeOf ( TotalFrames ) );
MaxTime := round ( TotalFrames * 1000 / AnimFPS );
aStream.Read ( numJoints, SizeOf ( NumJoints ) );
Using a few more WITH could probably help out too... take this:
I'd clean that into:
FOR c2 := 0 TO ms3dJoint.nRotKeyframes - 1 DO
Joints [ c ].RotationKeyframes [ c2 ].JointIndex := c;
Joints [ c ].RotationKeyframes [ c2 ].Time := ms3dKeyframe.Time * 1000;
Joints [ c ].RotationKeyframes [ c2 ].Parameter := ms3dKeyframe.Parameter
Actually, not true, I'd probably use a setter there. Sure, it adds the overhead of a CALL, but it would be a lot cleaner/simpler to implement.
for c2:=0 to ms3dJoint.nRotKeyFrames-1 do
with joints[c].rotationKeyFrames[c2] do begin
JointIndex := c;
Time := ms3dKeyframe.Time*1000;
Parameter := ms3dKeyframe.Parameter;
I'm seeing some things that could REALLY be sped up -- like ms3d_materiel and tModelMateriel sharing the exact same format for six values, I'd make those a sub-record and do a single copy on them, instead of manually indexing each and every one of them.
I mean this:
is pretty painful, when you could just do
Materials [ c ].Ambient := ms3dmaterial.Ambient;
Materials [ c ].Diffuse := ms3dmaterial.Diffuse;
Materials [ c ].Specular := ms3dmaterial.Specular;
Materials [ c ].Emissive := ms3dmaterial.Emissive;
Materials [ c ].Shininess := ms3dmaterial.Shininess;
Materials [ c ].Transparency := ms3dmaterial.Transparency;
Oddly, you do that with the loadHeader procedure...
I'd also suggest that for the OpenGL side, you actually use GLFloat -- which is NOT always a Single precision number... it is only guaranteed to be at LEAST 32 bit.
When/if I have time, I'm gonna see if I can port this to FPC... note I'm saying FPC, not lazarus -- I've got this weird mental block, I can hand compile Z80 machine language, but I can't learn visual programming. I could JUST grasp how to use OWL; along comes the VCL and Delphi, and I'm completely lost to the point I'm better off calling the GDI and Win API directly.
Should actually move over fairly easy if I toss the windows specific handler and use the SDL/OpenGL bindings that come with FPC.
-- edit --
Just looked at .render -- I'd consider using variable procedures for if the object has joints or not. One less if. I'd also suggest building those as real openGL groups, instead of manually mapping every triangle on every pass... NOT that I'm one to talk since my 3d engine does that, but mine is using arctan to build the projection and final rotations since it's meant for 'circlevision' -- so I'm basically using OpenGL as a glorified version of GLIDE.
Still, pre-rendering as complete objects each frame of an object's animation would REALLY speed things up. I might even consider doing such a thing at load time, so that during gameplay the no longer neccessary frame info isn't sucking down memory.