Delphi and 3D CAD development - User Interactivity (Win32
and VCL.NET)
MNCadBuilder
- RapidCAD with Delphi (OpenGL)
1.
Install the MNCadBuilder
---------------------------
Download the latest version (http://www.develocad.com/reg/download/mncadbuilder/mncb_lt.htm)
Execute setup (e.g. D:\MNCB) When setup is finished please check
"Start" - "All Programs" -
"MNCadBuilder" - "Switch Libraries to" --> the Delphi version
you are using.
Now, the corresponding files (*.dcu; *.bpl for Win32 platforms
and *.dcuil; *.dll for .NET platforms) are copied to the root
folder of your MNCB installation.
2. Integrate MNCadBuilder in to Delphi (using Delphi 7)
-------------------------------------------------------
Install package ("Component" - "Install packages") - Click "Add"
Button, and select MNLib.bpl from the root of your MNCB folder.
Now the "MNCadBuilder - 3D cad components collection" is installed.
3. First steps
--------------
Create a new project.
Please make sure, that the root path of your MNCB installation
folder is in the search path.
Start "Project" - "Options" - select folder "Directories/Conditionals"
and add the MNCB folder to the "Search path" settings.
Now we drag and drop a TMNOglCanvas component on to the form.
TMNOglCanvas is the central component for drawing and render output.
When assigning a Window control to the property >>WinControl,
an "OpenGL render context" is created based on the Window Handle
of the Window control.
So, let us assign the Form (Form1) to the >>WinControl.
We also change the >>BackgroundColor of the TMNOglCanvas to get
a more friendly background color (maybe clCream).
When we now want to draw an object in this render canvas, we can
use some predefined ones. We drag and drop a TMNShape3D on to
the form.
Now we have to inform the MNOglCanvas that he shall draw this
shape. We set the >>_Entity property to MNShape3D1. A click on
the form (in design mode) and the MNOglCanvas is refreshing.
When we set the MNOglCanvas property >>Navigate to TRUE, we can
change the view of the MNOglCanvas at runtime (means changing
the projection matrix of the canvas) .
When start the project, we see the designed shape. Because of
Navigate is TRUE, we can change the view by pressing the left
mouse button and moving the mouse. When you additionally press
the [Ctrl] key you can translate the view in X and Y.
If you want a perspective view, you can change the MNOglCanvas
property >>FieldOfView to a value > 0 (e.g. 20 degree). After
restarting the project you see the object in a perspective view.
And now we can also translate the view in Z axis by pressing the
[Shift] key, left mouse button down and moving the mouse in Y+/-
direction.
These were some of the first steps with the MNCadBuilder.
2. Let's take a look at defining "user defined" shapes
-----------------------------------------------------
Create a new project.
Please make sure, that the root path of your MNCB installation
folder is in the search path.
Start "Project" - "Options" - select folder "Directories/Conditionals"
and add the MNCB folder to the "Search path" settings.
Now we drag and drop a TMNOglCanvas component on to the form.
TMNOglCanvas is the central component for drawing and render output.
When assigning a Window control to the property >>WinControl,
an "OpenGL render context" is created based on the Window Handle
of the Window control.
So, let us assign the Form (Form1) to the >>WinControl.
We also change the >>BackgroundColor of the TMNOglCanvas to get
a more friendly background color (maybe clCream).
Now we define a new type of Entity:
TYPE
TMyShape = class (TMNEntity)
private
protected
procedure Draw; override;
public
end;
Let's fill the Draw method with some life:
procedure TMyShape.Draw;
begin
// Draw a box of length=3 at position TMyShape.Base
MNDevice.DrawBox(NewPt (3,3,3));
// push the current ModelMatrix
MNDevice.PushMatrix;
// modify the ModelMatrix by a translation
MNDevice.MultMatrix(TranslationToMatrix3D (NewPt(1,1,-1)));
// draw some primitive
MNDevice.DrawBox(NewPt (1,1,1));
// and pop the pushed Modelmatrix
MNDevice.PopMatrix;
procedure TMyShape.Draw;
const
ssize = 1.5;
lsize = 4;
var
trans : TXYZ;
begin
// Draw a box of length=lsize at position Base
MNDevice.DrawBox(NewPt (lsize,lsize,lsize));
MNDevice.PushMatrix;
trans := NewPt(lsize/2-ssize/2,lsize/2-ssize/2,-ssize);
MNDevice.MultMatrix(TranslationToMatrix3D (trans));
MNDevice.DrawBox(NewPt (ssize,ssize,ssize));
MNDevice.PopMatrix;
MNDevice.PushMatrix;
trans := NewPt(-ssize,lsize/2-ssize/2,lsize/2-ssize/2);
MNDevice.MultMatrix(TranslationToMatrix3D (trans));
MNDevice.DrawBox(NewPt (ssize,ssize,ssize));
MNDevice.PopMatrix;
MNDevice.PushMatrix;
trans := NewPt(lsize/2-ssize/2,lsize/2-ssize/2,lsize);
MNDevice.MultMatrix(TranslationToMatrix3D (trans));
MNDevice.DrawBox(NewPt (ssize,ssize,ssize));
MNDevice.PopMatrix;
MNDevice.PushMatrix;
trans := NewPt(lsize,lsize/2-ssize/2,lsize/2-ssize/2);
MNDevice.MultMatrix(TranslationToMatrix3D (trans));
MNDevice.DrawBox(NewPt (ssize,ssize,ssize));
MNDevice.PopMatrix;
MNDevice.PushMatrix;
trans := NewPt(lsize/2-ssize/2,lsize,lsize/2-ssize/2);
MNDevice.MultMatrix(TranslationToMatrix3D (trans));
MNDevice.DrawBox(NewPt (ssize,ssize,ssize));
MNDevice.PopMatrix;
MNDevice.PushMatrix;
trans := NewPt(lsize/2-ssize/2,-ssize,lsize/2-ssize/2);
MNDevice.MultMatrix(TranslationToMatrix3D (trans));
MNDevice.DrawBox(NewPt (ssize,ssize,ssize));
MNDevice.PopMatrix;
end;
We create the shape in the Form's OnShow method ...
procedure TForm1.FormShow(Sender: TObject);
begin
// create a shape of type TMyShape
MyShape := TMyShape.Create (NIL);
// tell the Device to draw MyShape
MNOglCanvas1._Entity := MyShape;
end;
.. and we dispose the object in the Form's Close method:
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
MyShape.Free;
end;
When we set the MNOglCanvas property >>Navigate to TRUE, we can
change the view of the MNOglCanvas at runtime (means changing
the projection matrix of the canvas).
When start the project, we see the "user defined" shape. Because
of Navigate is TRUE, we can change the view by pressing the left
mouse button and moving the mouse. When you additionally press
the [Ctrl] key you can translate the view in X and Y.
If you want a perspective view, you can change the MNOglCanvas
property >>FieldOfView to a value > 0 (e.g. 20 degree).
After restarting the project you see the object in a perspective
view. And now we can also translate the view in Z axis by pressing
the [Shift] key, left mouse button down and moving the mouse in
Y+/- direction.
This was an introduction of "user defined shapes" with the MNCadBuilder.
Here you will find the source.
type
TForm1 = class(TForm)
MNOglCanvas1: TMNOglCanvas;
procedure FormShow(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
{ Private-Deklarationen }
public
{ Public-Deklarationen }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
TYPE
TMyShape = class (TMNEntity)
private
protected
procedure Draw; override;
public
end;
VAR
MyShape : TMyShape;
procedure TForm1.FormShow(Sender: TObject);
begin
// create a shape of type TMyShape
MyShape := TMyShape.Create (NIL);
// tell the Device to draw MyShape
MNOglCanvas1._Entity := MyShape;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
MyShape.Free;
end;
{ TMyShape }
procedure TMyShape.Draw;
const
ssize = 1.5;
lsize = 4;
var
trans : TXYZ;
begin
// Draw a box of length=lsize at position Base
MNDevice.DrawBox(NewPt (lsize,lsize,lsize));
MNDevice.PushMatrix;
trans := NewPt(lsize/2-ssize/2,lsize/2-ssize/2,-ssize);
MNDevice.MultMatrix(TranslationToMatrix3D (trans));
MNDevice.DrawBox(NewPt (ssize,ssize,ssize));
MNDevice.PopMatrix;
MNDevice.PushMatrix;
trans := NewPt(-ssize,lsize/2-ssize/2,lsize/2-ssize/2);
MNDevice.MultMatrix(TranslationToMatrix3D (trans));
MNDevice.DrawBox(NewPt (ssize,ssize,ssize));
MNDevice.PopMatrix;
MNDevice.PushMatrix;
trans := NewPt(lsize/2-ssize/2,lsize/2-ssize/2,lsize);
MNDevice.MultMatrix(TranslationToMatrix3D (trans));
MNDevice.DrawBox(NewPt (ssize,ssize,ssize));
MNDevice.PopMatrix;
MNDevice.PushMatrix;
trans := NewPt(lsize,lsize/2-ssize/2,lsize/2-ssize/2);
MNDevice.MultMatrix(TranslationToMatrix3D (trans));
MNDevice.DrawBox(NewPt (ssize,ssize,ssize));
MNDevice.PopMatrix;
MNDevice.PushMatrix;
trans := NewPt(lsize/2-ssize/2,lsize,lsize/2-ssize/2);
MNDevice.MultMatrix(TranslationToMatrix3D (trans));
MNDevice.DrawBox(NewPt (ssize,ssize,ssize));
MNDevice.PopMatrix;
MNDevice.PushMatrix;
trans := NewPt(lsize/2-ssize/2,-ssize,lsize/2-ssize/2);
MNDevice.MultMatrix(TranslationToMatrix3D (trans));
MNDevice.DrawBox(NewPt (ssize,ssize,ssize));
MNDevice.PopMatrix;
end;
We take the basics from "MNCadBuilder - RapidCAD with Delphi (part
II)" and extend the TMyShape.
TYPE
TMyShape = class (TMNEntity)
private
FLSize: double;
FSSize: double;
procedure SetLSize(const Value: double);
procedure SetSSize(const Value: double);
protected
procedure Draw; override;
public
// we define some properties to the MyShape
property LSize : double read FLSize write SetLSize;
property SSize : double read FSSize write SetSSize;
// and we overwrite the Initialize method (not the constructor,
// because we want our shape to be compatible with dotNET)
procedure Initialize; override;
end;
The Initialize method:
Initialize is called from the VCL constructor Create (aOwner)
but also from the FCL constructor Create ().
// we overwrite the Initialize method (not the constructor,
// because we want our shape to be compatible with dotNET)
procedure TMyShape.Initialize;
begin
inherited;
LSize := 4;
SSize := 1.5;
end;
The SetLSize and SetSSize methods:
When you change properties of an entity, which influence the look
of the shape (means that the Draw method should be called) you
can use the property Changing:
* Changing := TRUE;
* ...
* Changing := FALSE;
procedure TMyShape.SetLSize(const Value: double);
begin
// Setting Changing to TRUE locks the entity from refreshing
Changing := TRUE;
FLSize := Value;
// Setting Changing to FALSE causes the entity to refresh
Changing := FALSE;
end;
Let's fill the Draw method with some instructions (and we use
some additional features):
procedure TMyShape.Draw;
var
trans : TXYZ;
oldDrawBorder : Boolean;
oldPolygonMode : integer;
begin
// Draw a box of length=lsize at position Base
// we draw the box with a golden material
MNDevice.Material.Kind := maGold;
MNDevice.DrawBox(NewPt (lsize,lsize,lsize));
// now we draw some smaller boxes at each side of the box
// and we are going to play with some features of the MNCadBuilder
// - Materials ... how shapes appear
// - PolygonMode ... lined or filled style
// - DrawBorder ... filled and lined style
// we sitch the materials attributes to be bronze like
MNDevice.Material.Kind := MaPolishedBronze;
// push the current ModelMatrix
MNDevice.PushMatrix;
trans := NewPt(lsize/2-ssize/2,lsize/2-ssize/2,-ssize);
// modify the ModelMatrix by a translation
MNDevice.MultMatrix(TranslationToMatrix3D (trans));
// draw some primitive
MNDevice.DrawBox(NewPt (ssize,ssize,ssize));
// and pop the pushed Modelmatrix
MNDevice.PopMatrix;
MNDevice.PushMatrix;
trans := NewPt(-ssize,lsize/2-ssize/2,lsize/2-ssize/2);
MNDevice.MultMatrix(TranslationToMatrix3D (trans));
MNDevice.DrawBox(NewPt (ssize,ssize,ssize));
MNDevice.PopMatrix;
// we save the current PolygonMode
oldPolygonMode := MNDevice.PolygonMode;
// we draw this small box in line mode
MNDevice.PolygonMode := gl_line;
MNDevice.PushMatrix;
trans := NewPt(lsize/2-ssize/2,lsize/2-ssize/2,lsize);
MNDevice.MultMatrix(TranslationToMatrix3D (trans));
MNDevice.DrawBox(NewPt (ssize,ssize,ssize));
MNDevice.PopMatrix;
// we reset the saved PolygonMode
MNDevice.PolygonMode := oldPolygonMode;
// the rest of the small boxes again in fill mode ...
oldDrawBorder := MNDevice.DrawBorder;
// but we draw the lines additionally
MNDevice.DrawBorder := TRUE;
MNDevice.PushMatrix;
trans := NewPt(lsize,lsize/2-ssize/2,lsize/2-ssize/2);
MNDevice.MultMatrix(TranslationToMatrix3D (trans));
MNDevice.DrawBox(NewPt (ssize,ssize,ssize));
MNDevice.PopMatrix;
// after the box is drawn we reset the DrawBorder
MNDevice.DrawBorder := oldDrawBorder;
MNDevice.PushMatrix;
trans := NewPt(lsize/2-ssize/2,lsize,lsize/2-ssize/2);
MNDevice.MultMatrix(TranslationToMatrix3D (trans));
MNDevice.DrawBox(NewPt (ssize,ssize,ssize));
MNDevice.PopMatrix;
MNDevice.PushMatrix;
trans := NewPt(lsize/2-ssize/2,-ssize,lsize/2-ssize/2);
MNDevice.MultMatrix(TranslationToMatrix3D (trans));
MNDevice.DrawBox(NewPt (ssize,ssize,ssize));
MNDevice.PopMatrix;
end;
Now we want the possibility to create more than one of these shapes.
So we embed the shapes in a group (of type TMNEntity, nothing
special). We create the group in our FormShow method and set the
_Entity property of the TMNOglCanvas to this group:
procedure TForm1.FormShow(Sender: TObject);
begin
// create the group ...
MyGroup := TMNEntity.Create (NIL);
// ... and tell the Device to draw the group
MNOglCanvas1._Entity := MyGroup;
end;
.. and we dispose the group in the Form's Close method:
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
// dispose the group
// please note that we do not have to destroy the shapes, because
// they will be destroyed by their MNParent
MyGroup.Free;
end;
Now the handling of the button to create the shapes:
//
// this will create a shape with the defined properties
//
// edO.Text ... Shape's Base.BaseO property - the origin of the
shape
// edLSize.Text ... Shape's LSize property - the size of the large
box
// edSSize.Text ... Shape's SSize property - the size of the small
box
//
procedure TForm1.pbCreateClick(Sender: TObject);
var
B : TBase;
aShape : TMyShape;
begin
// create the shape
aShape := TMyShape.Create (NIL);
with aShape do begin
B := Base;
// we set the origin of the shape
B.BaseO := StrToXYZ (EdO.Text);
Base := B;
// we set the large size of the shape ...
LSize := StrToFloat (edLSize.Text);
// and set the small size of the shape
SSize := StrToFloat (edSSize.Text);
// finally we set the MNParent property of the shape to be the
// MyGroup entity - and because the MNOglCanvas._Entity is set
to
// MyGroup, the shape will be drawn.
MNParent := MyGroup;
end;
end;
We can remove all shapes by simply call MyGroup.Clear.
procedure TForm1.pbClearClick(Sender: TObject);
begin
// Clear removes all children
MyGroup.Clear;
end;
These were the first steps of the MNCadBuilder's structural possibilities.
Here you will find the source.
Leopold Minikus
DeveloCAD.com
MNCadBuilder
- RapidCAD with Delphi (OpenGL) (part IV)
We take the basics from "MNCadBuilder - RapidCAD with Delphi (part
III)" and extend our small program with some interactivity.
We drag and drop a TMNComponentEditor from the MNEditors page
on to the form. Whenever user interactivity is needed the TMNComponentEditor
is the right tool (check the help file for more detailled informations).
Whenever the user creates a shape, the shape shall be selectable
- so we add this shape to the TMNComponentEditor Items list:
procedure TForm1.pbCreateClick(Sender: TObject);
var
B : TBase;
aShape : TMyShape;
begin
// create the shape
aShape := TMyShape.Create (NIL);
with aShape do begin
...
MNParent := MyGroup; MNComponentEditor1.Items.Add (aShape);
end;
end;
Now we want to be informed when the selection changes: The TMNComponentEditor
offers an event called OnSelChanged, and
this is exactly what we need.
procedure TForm1.MNComponentEditor1SelChanged(Sender: TObject);
var
aShape : TMyShape;
begin
// whenever the selected item changes, this event is called
with Sender as TMNComponentEditor do begin
// we want to retrieve the properties of the selected shape
If SelItems.Count > 0 then begin
aShape := SelItems.Objects[0] as TMyShape;
edO.Text := XYZToStr (aShape.Base.BaseO);
edLSize.Text := DoubleToStr (aShape.LSize);
edSSize.Text := DoubleToStr (aShape.SSize);
end;
end;
end;
What about updating the properties of the selected shape? No problem,
we can use our Edit fields, and implement in the OnKeyUp
event to update the selected shape:
The property LSize:
procedure TForm1.edLSizeKeyUp(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
if Key = 13 then begin
Key := 0;
// we update the property of the selected shape
with MNComponentEditor1 do begin
If SelItems.Count > 0 then begin
with SelItems.Objects[0] as TMyShape do
LSize := StrToFloat ((Sender as TEdit).text);
end;
end;
end;
end;
The property SSize:
procedure TForm1.edSSizeKeyUp(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
if Key = 13 then begin
Key := 0;
// we update the property of the selected shape
with MNComponentEditor1 do begin
If SelItems.Count > 0 then begin
with SelItems.Objects[0] as TMyShape do
SSize := StrToFloat ((Sender as TEdit).text);
end;
end;
end;
end;
The Origin of the shape:
procedure TForm1.edOKeyUp(Sender: TObject; var Key: Word;
Shift: TShiftState);
VAR
B : TBase;
begin
if Key = 13 then begin
Key := 0;
// we update the property of the selected shape
with MNComponentEditor1 do begin
If SelItems.Count > 0 then begin
with SelItems.Objects[0] as TMyShape do begin
// because we can not change Shape.Base.BaseO := ...
// we have to use a local variable
B := Base;
B.BaseO := StrToXYZ ((Sender as TEdit).text);
Base := B;
end;
end;
end;
end;
end;
These were the first steps of the MNCadBuilder's interactivity
features.
Here you will find the source
(Win32).