Solved

3D Studio - Delphi App

Posted on 2000-02-17
19
355 Views
Last Modified: 2010-04-04
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
Comment
Question by:WoodyJ3
19 Comments
 
LVL 10

Expert Comment

by:Lischke
Comment Utility
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
 
LVL 8

Expert Comment

by:ZifNab
Comment Utility
Lische,

you did a really nice job! Nice page too.

Zif.
0
 
LVL 10

Expert Comment

by:Lischke
Comment Utility
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
 
LVL 5

Expert Comment

by:TheNeil
Comment Utility
Jesus Mike that's excellent. Lotsa work in that

The Neil
0
 
LVL 17

Expert Comment

by:inthe
Comment Utility
hehe
mike is waiting all year for this question :-)

now anybody know where a great treeview component is?   ;-)
0
 
LVL 10

Expert Comment

by:Lischke
Comment Utility
Aaah, you got me Barry :-) Hope you all have fun with my stuff!

Ciao, Mike
0
 

Author Comment

by:WoodyJ3
Comment Utility
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
 
LVL 10

Expert Comment

by:Lischke
Comment Utility
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
 

Author Comment

by:WoodyJ3
Comment Utility
Thanks I'll let you know how I get on.

ta
Woody.
0
6 Surprising Benefits of Threat Intelligence

All sorts of threat intelligence is available on the web. Intelligence you can learn from, and use to anticipate and prepare for future attacks.

 

Author Comment

by:WoodyJ3
Comment Utility
I couldn't seem to get that to work.  I wasn't geting any hits at all.

Any thoughts?
0
 
LVL 10

Expert Comment

by:Lischke
Comment Utility
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
 
LVL 10

Expert Comment

by:Lischke
Comment Utility
PS: Please send only a stripped down version so I can focus on the picking problem.
0
 

Author Comment

by:WoodyJ3
Comment Utility
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
 

Author Comment

by:WoodyJ3
Comment Utility
Sorry, didn't read it correctly.  I'll send the email shortly.

John.
0
 
LVL 10

Accepted Solution

by:
Lischke earned 380 total points
Comment Utility
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
 
LVL 10

Expert Comment

by:Lischke
Comment Utility
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
 

Author Comment

by:WoodyJ3
Comment Utility
Adjusted points to 385
0
 

Author Comment

by:WoodyJ3
Comment Utility
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
 
LVL 10

Expert Comment

by:Lischke
Comment Utility
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

Top 6 Sources for Identifying Threat Actor TTPs

Understanding your enemy is essential. These six sources will help you identify the most popular threat actor tactics, techniques, and procedures (TTPs).

Join & Write a Comment

Introduction I have seen many questions in this Delphi topic area where queries in threads are needed or suggested. I know bumped into a similar need. This article will address some of the concepts when dealing with a multithreaded delphi database…
Introduction Raise your hands if you were as upset with FireMonkey as I was when I discovered that there was no TListview.  I use TListView in almost all of my applications I've written, and I was not going to compromise by resorting to TStringGrid…
Access reports are powerful and flexible. Learn how to create a query and then a grouped report using the wizard. Modify the report design after the wizard is done to make it look better. There will be another video to explain how to put the final p…
Here's a very brief overview of the methods PRTG Network Monitor (https://www.paessler.com/prtg) offers for monitoring bandwidth, to help you decide which methods you´d like to investigate in more detail.  The methods are covered in more detail in o…

763 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

11 Experts available now in Live!

Get 1:1 Help Now