• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 425
  • Last Modified:

3D Studio - Delphi App

Hi,
   Is there an application (with source) out there that will let me load in a 3D studio file and let me manipulate it?  

What I need is a program that will load in a .3ds file or any other 3D Studio file and let me navigate though it using the mouse.  I also need to detect mouse clicks.  i.e. If I load in a house I want to click on the door and know that I clicked the Door object.  Hope this makes sense.

Any comments welcome!!!

I should mention it must use OpenGL.

Help ?????????

Cheers
Woody.
0
WoodyJ3
Asked:
WoodyJ3
1 Solution
 
LischkeCommented:
Woody, today is your lucky day ;-) Just go straight to my homepage www.lischke-online.de/3DS.html and download Delphi sources for a 3ds import library.

There's a demo project which almost fullfills all your requests and comes with source too. The only thing you have to implement yourself is picking. But compared to the huge work already been done this should be something you can manage alone, I think.

Ciao, Mike
0
 
ZifNabCommented:
Lische,

you did a really nice job! Nice page too.

Zif.
0
 
LischkeCommented:
Thanks Zif, writing this monster was a though job, I can tell you... (btw. it is for weeks now under the top 20 downloads on www.delphipages.com).

Ciao, Mike
0
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
TheNeilCommented:
Jesus Mike that's excellent. Lotsa work in that

The Neil
0
 
intheCommented:
hehe
mike is waiting all year for this question :-)

now anybody know where a great treeview component is?   ;-)
0
 
LischkeCommented:
Aaah, you got me Barry :-) Hope you all have fun with my stuff!

Ciao, Mike
0
 
WoodyJ3Author Commented:
Yes, I must agree that is brilliant.  My only question is now.  How do I do the picking?  I'm still learning OpenGL and the routines for picking I've found don't seem to work with the "opener".  

Any Ideas?

I'll leave this question open for a while, then I'll give Mike the points.

Cheers
Woody.
0
 
LischkeCommented:
Hi Woody,

picking is not so hard as it may seem. Here's the code I use in GLScene (my OpenGL library you also can download for free from my homepage):

procedure TGLSceneViewer.PickObjects(var Rect: TRect; PickList: TGLPickList;
                                   objectCountGuess: Integer);
type
   { TODO : local type to remove }
   PCardinalVector = ^TCardinalVector;
   TCardinalVector = array[0..0] of Cardinal;
var
   buffer : PCardinalVector;
   hits : Integer;
   i : Integer;
   current, next : Cardinal;
   szmin, szmax : Single;
begin
   Assert((FState = dsNone));
   Assert(Assigned(PickList));
   ActivateRenderingContext(FCanvas.Handle, FRenderingContext);
   FState := dsPicking;
   try
      glMatrixMode(GL_PROJECTION);
      glPushMatrix;
      buffer := nil;
      try
         glLoadIdentity;
         gluPickMatrix(Rect.Left, Height - Rect.Top,
                       Abs(Rect.Right - Rect.Left), Abs(Rect.Bottom - Rect.Top),
                       TVector4i(FViewport));
         FCamera.ApplyPerspective(FViewport, Width, Height,
                                  GetDeviceCaps(Canvas.Handle, LOGPIXELSX));
         // check countguess, memory waste is not an issue here
         if objectCountGuess<8 then objectCountGuess:=8;
         hits:=-1;
         repeat
            if hits < 0 then begin
               // Allocate 4 integers per row (Egg : dunno why 4)
               // Add 32 integers of slop (an extra cache line) to end for buggy
               // hardware that uses DMA to return select results but that sometimes
               // overrun the buffer.  Yuck.
               ReallocMem(buffer, objectCountGuess * 4 * SizeOf(Integer) + 32 * 4);
               // increase buffer by 50% if we get nothing
               Inc(objectCountGuess, objectCountGuess shr 1);
            end;
            // pass buffer to opengl and prepare render
            glSelectBuffer(objectCountGuess*4, @Buffer^);
            glRenderMode(GL_SELECT);
            glInitNames;
{ TODO : glPushName(0) with no glPopName, bug ? }
            glPushName(0);
            glMatrixMode(GL_MODELVIEW);
            glLoadIdentity;
            // render the scene (in select mode, nothing is drawn)
            if Assigned(FCamera) and Assigned(FCamera.FScene) then
               with FCamera.FScene do begin
                  ValidateTransformation(Camera);
                  RenderScene(Self);
               end;
            glFlush;
            Hits := glRenderMode(GL_RENDER);
         until Hits > -1; // try again with larger selection buffer
         Next := 0;
         PickList.Clear;
         PickList.Capacity := Hits;
         for I := 0 to Hits-1 do begin
            Current := Next;
            Next := Current + Buffer[Current] + 3;
            szmin := (Buffer[current + 1] shr 1) / MaxInt;
            szmax := (Buffer[current + 2] shr 1) / MaxInt;
            PickList.AddHit(TGLCustomSceneObject(Buffer[Current + 3]), szmin, szmax);
         end;
      finally
         FreeMem(Buffer);
         glMatrixMode(GL_PROJECTION);
         glPopMatrix;
      end;
   finally
      FState := dsNone;
      DeactivateRenderingContext;
   end;
end;


TGLPickList is just a TList with a record as data holding a scene object reference and the limits (as indicated in the PickList.AddHit call above).

Ciao, Mike
0
 
WoodyJ3Author Commented:
Thanks I'll let you know how I get on.

ta
Woody.
0
 
WoodyJ3Author Commented:
I couldn't seem to get that to work.  I wasn't geting any hits at all.

Any thoughts?
0
 
LischkeCommented:
mmh, then I need to have a look at your project. There are too many potential problems so can't say from "don't get any hits at all" what's wrong (send it to public@lischke-online.de).

Ciao, Mike
0
 
LischkeCommented:
PS: Please send only a stripped down version so I can focus on the picking problem.
0
 
WoodyJ3Author Commented:
the procedure you sent me...  Commented out some bits...

procedure TForm_Main.PickObjects(var Rect: TRect; PickList: {TGLPickList} Tlist;
                                                                    objectCountGuess: Integer);
type
      { TODO : local type to remove }
      PCardinalVector = ^TCardinalVector;
   TCardinalVector = array[0..0] of Cardinal;
var
   buffer : PCardinalVector;
   hits : Integer;
   i : Integer;
   current, next : Cardinal;
   szmin, szmax : Single;
begin
//   Assert((FState = dsNone));
//   Assert(Assigned(PickList));
      ActivateRenderingContext(Canvas.Handle, FRenderingContext);
try
      glMatrixMode(GL_PROJECTION);
      glPushMatrix;
      buffer := nil;
      try
         glLoadIdentity;
         gluPickMatrix(Rect.Left, Height - Rect.Top,
                       Abs(Rect.Right - Rect.Left), Abs(Rect.Bottom - Rect.Top),
                       TVector4i(FViewport));

                  // check countguess, memory waste is not an issue here
                  if objectCountGuess<8 then objectCountGuess:=8;
                  hits:=-1;
         repeat
            if hits < 0 then begin
               // Allocate 4 integers per row (Egg : dunno why 4)
               // Add 32 integers of slop (an extra cache line) to end for buggy
               // hardware that uses DMA to return select results but that sometimes
               // overrun the buffer.  Yuck.
               ReallocMem(buffer, objectCountGuess * 4 * SizeOf(Integer) + 32 * 4);
               // increase buffer by 50% if we get nothing
               Inc(objectCountGuess, objectCountGuess shr 1);
            end;
            // pass buffer to opengl and prepare render
            glSelectBuffer(objectCountGuess*4, @Buffer^);
            glRenderMode(GL_SELECT);
            glInitNames;
{ TODO : glPushName(0) with no glPopName, bug ? }
            glPushName(0);
            glMatrixMode(GL_MODELVIEW);
            glLoadIdentity;
            // render the scene (in select mode, nothing is drawn)

            glFlush;
            Hits := glRenderMode(GL_RENDER);
         until Hits > -1; // try again with larger selection buffer
         Next := 0;
//         PickList.Clear;
//         PickList.Capacity := Hits;
                  for I := 0 to Hits-1 do begin
                        Current := Next;
                        Next := Current + Buffer[Current] + 3;
                        szmin := (Buffer[current + 1] shr 1) / MaxInt;
                        szmax := (Buffer[current + 2] shr 1) / MaxInt;
                        ShowMessage('john');
//            PickList.AddHit(TGLCustomSceneObject(Buffer[Current + 3]), szmin, szmax);
                  end;
            finally
                  FreeMem(Buffer);
         glMatrixMode(GL_PROJECTION);
         glPopMatrix;
      end;
   finally
//      FState := dsNone;
//      DeactivateRenderingContext;
   end;
end;


the call im using...


procedure TForm_Main.FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
Var
a : trect;
begin
      SetFocus;
      FLastX := X;
      FLastY := Y;

      a.Left := 1;
      a.Top := 1;
      a.Bottom := height;
      a.Right := width;
      PickObjects(a, nil, 10);
end;


i think notepad has screwed the indents.  Sorry.

Am I calling this procedure correctly?

Cheers
Woody
0
 
WoodyJ3Author Commented:
Sorry, didn't read it correctly.  I'll send the email shortly.

John.
0
 
LischkeCommented:
Hi John,

sorry for being so late, but it took me a while to figure out what was wrong. I'll send you the changed main form so you can see all changes I made.

There are several important points. First is: don't take out the check for OpenGL errors. This may sometimes be a good indicator of there's something wrong. You RedrawScene and whatever calls are very likely candidates to ruin all you other precious code. Just call MainForm.Invalidate and all should be fine. DON'T call BeginPaint/EndPaint unless you are directly in WM_PAINT (not OnPaint or however they may be called). This is an absolute no-no and can cause serious trouble on certain graphic boards if BeginPaint etc. are called in a nested manner (and they are already called when entering OnPaint etc.)!

Furthermore, you cannot expect to get any result when you take out the actual render code from PickObjects. You have to render the scene normally but with a slightly different projection matrix, which is created by the gluPickMatrix call. I have changed the core functions to:

procedure TForm_Main.DoRender;

// This is the main paint routine. If there's no data assigned yet then an empty window is drawn else the data stored
// in the various lists. These lists must already be set up.

var
  Scale: Single;

begin
  ClearBuffersAndBackground;

  if FCameraChanged or FPicking then
  begin
    FCameraChanged := False;
    // set up projection
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity;

    if FPicking then gluPickMatrix(FLastX, FLastY, 3, 3, TVector4i(FViewport));
    ApplyPerspective(FViewport, Width, ClientHeight, FDPI);
  end;

  // initialize transformation pipeline
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity;

  // lights
  Scale := 1000 / 1000;
  if FLightSourcesChanged then
  begin
    glScalef(Scale, Scale, Scale);
    SetupLights;
    glLoadIdentity;
  end;

  // camera
  gluLookAt(0, 0, 50,
            0, 0, -10,
            0, 1, 0);

  // objects
  if Abs(1 - Scale) > 0.1 then
  begin
    glEnable(GL_NORMALIZE);  // this will half the frame rate but is necessary for changing overall
                             // scale on the fly and still get the correct lighting
    glScalef(Scale, Scale, Scale);
  end
  else glDisable(GL_NORMALIZE);
  glMultMatrixf(@FSceneMatrix);

  if FMainList <> 0 then glCallList(FMainList);

end;

//----------------------------------------------------------------------------------------------------------------------

procedure TForm_Main.FormPaint(Sender: TObject);

begin
  // Note: We don't need to activate   DoRender;
  // copy back buffer to front
  SwapBuffers(Canvas.Handle);
end;

//----------------------------------------------------------------------------------------------------------------------

procedure TForm_Main.FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);

begin
      SetFocus;
      FLastX := X;
      FLastY := Y;
      PickObjects(nil, 10);
end;

//----------------------------------------------------------------------------------------------------------------------


Finally, you need to indicate whenever an object/vertex/mesh is drawn to get it also back. I did this in Use3DSData when building the display lists for all meshs:

    for I := 0 to FLists.Count - 1 do
    begin
      Mesh := FLists[I];
      with Mesh^ do
      begin
        // allocate a new display list
        ListName := glGenLists(1);
        // now compile mesh data into this list
        glNewList(ListName, GL_COMPILE);

:

        // finished compiling this display list
        glLoadName(I);
        glEndList;
//        CheckOpenGLError;
      end;
    end;

Note the glLoadName call. This number is returned in the pick code.

This was quite tough stuff, but I think I have it all now.

Ciao, Mike
0
 
LischkeCommented:
Oops, FormPaint should actually look so:

procedure TForm_Main.FormPaint(Sender: TObject);

begin
  DoRender;
  // copy back buffer to front
  SwapBuffers(Canvas.Handle);
end;

Ciao, Mike
0
 
WoodyJ3Author Commented:
Adjusted points to 385
0
 
WoodyJ3Author Commented:
Thanks very much.

I think Im gonna have to buy several books on this openGL stuff.  When I finished this project I'll send it you.

Thanks again.  You can have all of my points.

John.
0
 
LischkeCommented:
Thank you for the points and the A grading. Well, after you have read all those books you will have gotton lots of new points, I'm sure ;-))

Ciao, Mike
0

Featured Post

What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

Tackle projects and never again get stuck behind a technical roadblock.
Join Now