{************************************************************}
{                   Delphi VCL Extensions                    }
{                                                            }
{             3D Rendering with GLScene www.glscene.org      }
{                                                            }
{      Copyright (c) 2002-2013 SoftGold software company     }
{                                                            }
{************************************************************}

unit sg3DNavigator;

{$I SGDXF.INC}
{$I CADSoftTools.INC}

{$IFDEF SG_ABVIEWER}
{$DEFINE SG_ALL_3D_FILES}
{$ELSE}
{$IFDEF SG_CADEDITORX}
{$DEFINE SG_ALL_3D_FILES}
{$ENDIF}
{$ENDIF}

//{$DEFINE CHECK_TIME}
//{$DEFINE SG_CALC_NORMALS}

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, GLScene,
  GLWin32Viewer, sgFunction, GLVectorFileObjects, GLObjects, VectorGeometry,
  GLTexture, OpenGL1x, GLContext, GLMaterial, GLCoordinates, GLCrossPlatform,
  BaseClasses, GLColor, GLRenderContextInfo, SGDrawingNavigator, sgConsts,
  sgOrbit3D, CADImage, GLNavigator, VectorTypes, GLGeomObjects, GLCanvas,
  GLHUDObjects, GLDynamicTexture, Math, DXFConv, VectorLists, GLSelection,
  SyncObjs, GLGraphics
{$IFDEF SG_CALC_NORMALS}
  , MeshUtils
{$ENDIF}
  {$IFDEF SG_ALL_3D_FILES}
  ,GLFileObj, GLFileASE, GLFileB3D, GLFileGL2, GLFileGTS,
  GLFileLMTS, GLFileLWO, GLFileMD2, GLFileMD3, GLFileMD5, GLFileMDC, GLFileNMF,
  GLFileNurbs, GLFileOCT, GLFilePLY, GLFileQ3BSP, GLFileSMD, GLFileTIN,
  sgGLFileVRML, GLFile3DS,
  GLFileSTL, //Must be BEFORE GlFileX
  GlFileX
  {$ENDIF}
  {$IFDEF SG_ABVIEWER}
  ,Constants, sgGraphics
  {$ELSE}
  {$ENDIF}
  ,GLState, OpenGLTokens,  GLTextureFormat, sgLists, sgSTLImage,
  GeometryBB, GLHiddenLineShader, sgMeshBuilderWrapper, sgBrepConverter,
  sgBitmap
{$IFDEF SGABVIEWER}
  ,sgCommonFunctions
{$ENDIF}
{$IFDEF SGDEL_XE2}
  ,System.Types, Vcl.Imaging.PNGImage
{$ENDIF}
  , PersistentClasses, sgTools;

type
  Esg3DNavError = class(Exception);
  Esg3DNavDLLError = class(Esg3DNavError);

  TsgMeshQuality = (mqLow, mqNormal, mqHigh);

  Tsg3DDrawingNavigator = class;

  TCustomNode = class(TPersistentObjectList)
  private
    FOwner: TCustomNode;
    FObj: TObject;// "INSERT"
  protected
    function GetOwner: TPersistent; override;
  public
    constructor Create(AOwner: TCustomNode); reintroduce; virtual;
    property Obj: TObject read FObj write FObj;
    property Owner: TCustomNode read FOwner;
  end;

  TsgMeshObject = class(TMeshObject)
  private
    FEntity: TObject;
    FNode: TCustomNode;
    function GetRootInsert: TObject;
    function GetInsert: TObject;
  protected
    procedure SetEntity(AEnt: TObject);
  public
    constructor CreateWithEnt(AOwner: TMeshObjectList; AEnt: TObject);
    property Entity: TObject read FEntity;
    property Insert: TObject read GetInsert;
    property Node: TCustomNode read FNode write FNode;
    property RootInsert: TObject read GetRootInsert;
  end;

  // Lite polyline by GLScene
  TsgGLPolyline = class(TAffineVectorList)
  private
    FLineWidth: Single;
    FMode: Integer;
    procedure SetupLineStyle(var rci: TRenderContextInfo);
    function GetMaterailName: string;{$IFDEF SG_INLINE}inline;{$ENDIF}
    procedure SetMaterailName(const Value: string);{$IFDEF SG_INLINE}inline;{$ENDIF}
  public
    constructor Create; override;
    procedure Assign(Source: TPersistent); override;
    procedure BuildList(var rci: TRenderContextInfo);
    property LineWidth: Single read FLineWidth write FLineWidth;
    property Mode: Integer read FMode write FMode;
    property MaterialName: string read GetMaterailName write SetMaterailName;
  end;

  TsgGLLines = class(TGLImmaterialSceneObject)
  private
    FPolylines: TList;
    FDepthTest: Boolean;
  protected
    function ReleaseLastNode: TsgGLPolyline;{$IFDEF SG_INLINE}inline;{$ENDIF}
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure Assign(Source: TPersistent); override;
    procedure Clear;
    procedure DeleteLastNode;
    function AddNode: TsgGLPolyline;
    procedure BuildList(var rci: TRenderContextInfo); override;
    property DepthTest: Boolean read FDepthTest write FDepthTest;
  end;

  Tsg3DAxis = class(TGLMemoryViewer)
  private
    FDefCameraScale: Single;
    FGLScene: TGLScene;
    FGLDCAxes: TGLDummyCube;
    FGLArrowX: TGLArrowLine;
    FGLArrowY: TGLArrowLine;
    FGLArrowZ: TGLArrowLine;
    FGLCamera: TGLCamera;
    function GetBoundingSphereRadius: Single;
  protected
    procedure SetObjectsVisible(AVisible: Boolean);
    property GLDCAxes: TGLDummyCube read FGLDCAxes;
  public
    constructor CreateEx(AGLScene: TGLScene; AOwner: TComponent);
    procedure HideAxis;
    procedure LoadIdentity;
    procedure ShowAxis;
    procedure SetCameraPosition(X, Y, Z: Single);
    procedure SetDefPositions;
    procedure ResetRotations;
    property BoundingSphereRadius: Single read GetBoundingSphereRadius;
    property DefCameraScale: Single read FDefCameraScale;
  end;

  Tsg3DNavigator = class(TGLSceneViewer)
  private
    FAxes: Tsg3DAxis;
    FCameraStyle: TGLCameraStyle;
    FDefCameraScale: Single;
    FGLAxisImage: TGLHUDSprite;
    FGLScene: TGLScene;
    FGLMaterialLibrary: TGLMaterialLibrary;
    FLights: array[0..7] of TGLLightSource;
    FGLCamera: TGLCamera;
    FGLCubeExtents: TGLCube;
    FGLDCScene: TGLDummyCube;
    FGLFreeForm: TGLFreeForm;
    FGLDefSceneMat: VectorGeometry.TMatrix;
    FGLDCTarget: TGLDummyCube;
    FGLDirectRender: TGLDirectOpenGL;
    FGLSphere: TGLSphere;
    FShowCubeExtents: Boolean;
    FShowAxis: Boolean;
    FCenter: TVector3f;
    FCurrentLine: TsgGLPolyline;
    FOnScenePositionChange: TNotifyEvent;
    FOnAfterRender: TNotifyEvent;
    FOnBeforeRender: TNotifyEvent;
    FOnWrapUpRender: TDirectRenderEvent;
    FLineMass: TsgGLLines;
    FIsDirty: Integer;
    procedure DoOnAfter3DAxisRender(Sender: TObject);
    procedure DoOnAfterRender(Sender: TObject);
    procedure DoOnBeforeRender(Sender: TObject);
    procedure DoOnBefore3DAxisRender(Sender: TObject);
    procedure DoOnDirectOGLRender(Sender: TObject; var rci: TRenderContextInfo);
    procedure DoOnWrapUpRender(Sender: TObject; var rci: TRenderContextInfo);
    procedure DoResetCamera;
    function GetBackgroundColor: TColor;
    function GetBoundingSphereRadius: Single;
    function GetDefSceneMatrix: VectorGeometry.TMatrix;
    function GetGLFreeForm: TGLFreeForm;
    function GetShowAxis: Boolean;
    function GetShowCubeExtents: Boolean;
    function GetStyleCamera: TGLCameraStyle;
    function GetTrianglesCount: Integer;
    procedure HideLights;
    procedure HideScene;
    procedure SetAxisPos;
    procedure SetShowAxis(const Value: Boolean);
    procedure SetBackgroundColor(const Value: TColor);
    procedure SetShowCubeExtents(const Value: Boolean);
    procedure SetStyleCamera(const Value: TGLCameraStyle);
    procedure SetSceneScale;
    procedure ShowLights;
    procedure ShowScene;
    procedure RenderAxis;
    procedure ScenePositionChange(Sender: TObject);
    procedure UpdateArrows;
    // lights
    function GeLights(Index: Integer): TGLLightSource;
    function GeLightsCount: Integer;
  protected
    function ClientToWorld(X, Y: Double; Z: Double = 0): TVector3f;
    procedure DoBufferChange(Sender: TObject); override;
    procedure InitSceneObject(AObject: TGLBaseSceneObject;
      const APosition, ADirection: TVector3f; const AOffs: Single);
    procedure PositionLight(const ACenter: TVector3f;
      const ADistance: Single);
    function GetMaterialByName(AMaterialName: string): TGLMaterial;
    function GetMeshIntersects(X, Y: Integer; const AMeshObjects: TList;
      var ACancel: Boolean; const APoints: TFPointList = nil;
      const ANormals: TFPointList = nil): Boolean;
    function GetMeshIntersect(X, Y: Integer; var AMeshObject: TMeshObject;
      AIntersect: PFPoint; ANormal: PFPoint; var ACancel: Boolean): Boolean;
    procedure WMSize(var Message: TMessage); message WM_SIZE;
    procedure WMPaint(var Message: TWMPaint); Message WM_PAINT;
    property OnWrapUpRender: TDirectRenderEvent read FOnWrapUpRender write FOnWrapUpRender;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure Clear;
    procedure DoAfterLoadScene;
    class function GetFilter: string;
    function IsEmpty: Boolean;
    procedure LoadFromFile(const AFileName: string; ADoClear: Boolean = True);
    procedure LoadFromStream(const AFileName: string; Stream: TStream;
      ADoClear: Boolean = True);
    procedure LoadSceneIdentity;
    procedure SetDefSceneMatrix;
    procedure Rotate(Axis: TsgAxes; Angle: Extended);
    procedure PerformAutoCentering;
    procedure Translate(const AVector: TAffineVector);
    property Axes: Tsg3DAxis read FAxes;
    property BoundingSphereRadius: Single read GetBoundingSphereRadius;
    property CameraStyle: TGLCameraStyle read GetStyleCamera write SetStyleCamera;
    property DefSceneMatrix: VectorGeometry.TMatrix read GetDefSceneMatrix;
    {$IFDEF SGDEL_2006}
    property Filter: string read GetFilter;
    {$ENDIF}
    property GLDCScene:TGLDummyCube read FGLDCScene;
    property GLDCTarget:TGLDummyCube read FGLDCTarget;
    property GLFreeForm: TGLFreeForm read GetGLFreeForm;
    property Lights[Index: Integer]: TGLLightSource read GeLights;
    property LightsCount: Integer read GeLightsCount;
    property BackgroundColor: TColor read GetBackgroundColor write SetBackgroundColor;
    property ShowAxis: Boolean read GetShowAxis write SetShowAxis;
    property ShowCubeExtents: Boolean read GetShowCubeExtents write SetShowCubeExtents;
    property TrianglesCount: Integer read GetTrianglesCount;
    property OnScenePositionChange: TNotifyEvent read FOnScenePositionChange write FOnScenePositionChange;
    property OnAfterRender: TNotifyEvent read FOnAfterRender write FOnAfterRender;
    property OnBeforeRender: TNotifyEvent read FOnBeforeRender write FOnBeforeRender;
  end;

  TsgGLGraphic = class(TBitmap)
  private
    FNavigator: Tsg3DDrawingNavigator;
    FViewport: TRectangle;
    FBuffer: TGLSceneBuffer;
    FChangedCount: Integer;
  protected
    procedure Changed(Sender: TObject); override;
    procedure DoRender;
    procedure Draw(ACanvas: TCanvas; const Rect: TRect); override;
    procedure DrawTransparent(ACanvas: TCanvas; const Rect: TRect; Opacity: Byte); {$IFDEF SGDEL_2009}override{$ELSE}virtual{$ENDIF};
    property Viewport: TRectangle read FViewport write FViewport;
  public
    procedure SetNavigator(const ANavigator: Tsg3DDrawingNavigator);
  end;

  Tsg3DShowingStyle = (ssSmoothShading, ssFlatShading, ssHiddenLines,
    ssWireframe);


  TsgPickObject = class(TPersistent)
  private
    FEnabled: Boolean;
    FOwner: TPersistent;
    FObjects: TList;
    FOnPick: TNotifyEvent;
    FPick: TObject;
    FPickCache: TObject;
    FPickCached: Boolean;
    FLines: TsgGLLines;
    FPoly: TsgGLPolyline;
    FColor: TColor;
    FTag: TObject;
    FPos: TPoint;
    FTopNode: TCustomNode;
    procedure SetObject(AObject: TObject);
    function GetObject: TObject;
  protected
    procedure DoPick;
    procedure CreateLines;
    function GetOwner: TPersistent; override;
    property OnPick: TNotifyEvent read FOnPick write FOnPick;
    property Lines: TsgGLLines read FLines;
    property Pos: TPoint read FPos write FPos;
    property TopNode: TCustomNode read FTopNode write FTopNode;
  public
    constructor Create(AOwner: TPersistent); virtual;
    destructor Destroy; override;
    procedure ClearLines;
    procedure BuildLines;
    property Obj: TObject read GetObject write SetObject;
    property Objects: TList read FObjects;
    property Color: TColor read FColor write FColor;
    property PickCached: Boolean read FPickCached write FPickCached;
    property Enabled: Boolean read FEnabled write FEnabled;
    property Tag: TObject read FTag write FTag;
  end;

  Tsg3DDrawingNavigator = class(TsgDrawingNavigator)
  private
    FCurrMeshObject: TMeshObject;
    FCurrVertexes: TFGVertexIndexList;
    FCurrColor: TColor;
    FNeedNormals: Boolean;

    FCurrMeshObject3dFace: TMeshObject;
    FCurrVertexes3dFace: TFGVertexIndexList;
    FCurrColor3dFace: TColor;
    FNeedNormals3dFace: Boolean;

    FCurrLineWieght: Single;
    FConverter: TsgDXFConverter;
    FDefaultColor: TColor;
    FIsLoadedFromConv: Boolean;
    FIsBrepEntityIterate: Boolean;
    FNav3D: Tsg3DNavigator;
    FNav3DWindProc: TWndMethod;

    FHiddenLinesShader: TGLShader;
    FLoadFromImage: TsgCADImage;
    FShowingStyle: Tsg3DShowingStyle;
    FVectorFile: TVectorFile;
    FCoord: TVector3f;
    FCoordInUCS: TVector3f;
    FDeviationCoefficient: Double;
    FIsHasTriangledMesh: Boolean;
    FOuterWireColor: TColor;
    FOuterWireLineWieght: Single;
    FBoxOffs: TFPoint;

    FHighLightObj: TsgPickObject;
    FOnHighLight: TNotifyEvent;
    FMove: TPoint;
    FHighlightSaveMatName: string;

    FSearchEntMesh: TsgMeshObject;
    FPickObjects: TsgPickObject;
    FOnPickObject: TNotifyEvent;
    FIntersectCache: PFPoint;
    FLockPaintSysMenuIcons: Boolean;
    FRoot: TPersistentObjectList;
    FInsert: TsgDXFInsert;
    FMouseRotate: Boolean;

    FDrawMode: TsgDXFDrawMode;
    FLoadFromImageChange: TNotifyEvent;
    function AddVertex(const Point: TFPoint; PNormal: PFPoint = nil): Integer;
//    procedure AddVertexes(const P1, P2, P3: TFPoint);
    procedure UpdateVertexIndexList(const AVertexIndexList: TsgIntegerList);
//    procedure AddPoint(Pt: TFPoint; PointsList: TList);
    procedure AssignMaterialForMesh(const Mesh: TMeshObject;
      const AMaterial: TColor);
    procedure AppShowingStyle;
    procedure ApplyShowingStyleToMaterial(AMaterial: TGLMaterial);
    function CheckCurVertexes: Boolean;
    function CreateMeshObject(AOwner: TMeshObjectList; AEntity: TObject;
      AMode: TMeshObjectMode = momFaceGroups): TMeshObject;
    procedure CreateGLSceneLine(var ALine: TsgGLPolyline; AColor: TColor;
      ALineWidth: Single);
    procedure CreateGLScenePlane(var APlane: TGLPlane);
    function CheckCount(ALine: TsgGLPolyline; Count: Integer): Boolean;
    procedure DoOnWrapUpRender(Sender: TObject; var rci: TRenderContextInfo);
    procedure DrawACIS(Entity: TsgDXFEntity);
    procedure DrawBrepFile(Entity: TsgDXFEntity);
    procedure DrawDXFSurface(Entity: TsgDXFEntity);
    procedure DrawLine(Entity: TsgDXFEntity);
    procedure DrawLineAndZTick(Line: TsgDXFLine; P1, P2: TFPoint);
    procedure DrawPolyLineExt(Points: TFPointList; Solid, Closed: Boolean);
    procedure DrawPolyLine(Entity: TsgDXFEntity);
    procedure DrawBaseSolid(Entity: TsgDXFEntity);
    procedure DrawSolid(Entity: TsgDXFEntity);
    procedure Draw3dFace(Entity: TsgDXFEntity);
    procedure DrawText(Entity: TsgDXFEntity);
    procedure DrawHatch(Entity: TsgDXFEntity);
    procedure DrawImageEnt(Entity: TsgDXFEntity);
    procedure DrawImage(Entity: TsgDXFEntity);
    procedure GetScreenAABB(var AAABB: TAABB);
    procedure GetBoundingBox(var ABoundingBox: THmgBoundingBox);
    function GetFaceGroup(AMeshObject: TMeshObject; AMode: TFaceGroupMeshMode;
      const AMaterial: TColor; AFaceGroupClass: TClass): TFGVertexIndexList;
{$IFDEF SG_CALC_NORMALS}
    procedure FacesSmooth(aMeshObj: TMeshObject;
      aWeldDistance: Single=0.0000001; aThreshold: Single=35.0);
{$ENDIF}
    procedure GenNormals(AMesh: TMeshObject);
    function GetNavigator: Tsg3DNavigator;
    function GetPoint(const Point: TFPoint; AParam: TsgCADIterate): TFPoint;
    function GetPointf(const APoint: TFPoint): TAffineVector;
    function GetShowingStyle: Tsg3DShowingStyle;
    function GetMeshQuality: TsgMeshQuality;
    procedure Nav3DWindProc(var Message: TMessage);
    procedure Navigator3DAfterRender(Sender: TObject);
    procedure Navigator3DBeforeRender(Sender: TObject);
    procedure OrbitVisibleChanging(Sender: TObject);
    procedure PrepareData(ALine: TsgGLPolyline; Points: TFPointList;
      SolidPolyline: Boolean; ClosedPolyline: Boolean);
    procedure SetParamsByMeshBuilder(AParams: PsgParamTriangle;
      AInitData: PsgInitMeshBuilderData);
    procedure SetShowingStyle(const Value: Tsg3DShowingStyle);
    procedure SetupLight(const ABox: TFRect);
    procedure ScenePositionChange(Sender: TObject);
    procedure SetMeshQuality(const Value: TsgMeshQuality);
    procedure SetDefault3DSceneOptions;
    procedure SetHighLightObject(AHighLightObject: TObject);
    procedure SetPickObject(APickObject: TObject);
    procedure SetShowHighlight(const Value: Boolean);
    procedure SetMouseRotate(const Value: Boolean);
    //
    procedure FinalMeshObject(var AMesh: TMeshObject; ANeedNormals: Boolean);
    procedure Load3dFaceMeshObject;
    procedure Create3DFaceMeshObject(AEntity: TsgDXFEntity);

    function GetPickObject: TObject;
    procedure PickObjectEvent(Sender: TObject);
    procedure HighLightObjectEvent(Sender: TObject);
    procedure ResetDefaults;
    function GetHighLightObject: TObject;
    procedure DoNodes;
    function FindMesh(X, Y: Integer): TMeshObject;
    function GetShowHighlight: Boolean;
    procedure SetLoadFromImage(const Value: TsgCADImage);
    procedure UpdateMaterialLibrary;
  protected
    function CoordinateConvertion(ACoordX, ACoordY: Integer;
      var APointInUCS: TFPoint): TFPoint; override;
    function CreateSnapShotBitmap: Graphics.TBitmap; override;
    procedure DoOnColorChanged(const AColor: TColor); override;
    procedure DoOnCursorChanged; override;
    procedure DoOnStopMouseOperations; override;
    procedure DoPictureChange(Sender: TObject); override;
    function FinishEntity(Entity: TsgDXFEntity): Integer; override;
    function GetActualGraphic: TGraphic; override;
    function ClientToWorld(const X, Y: Double; const Z: Double = 0): TVector3f; overload;
    function ClientToWorld(const APosition: TF2DPoint): TVector3f; overload;
    function ClientToWorld(const APosition: TFPoint): TVector3f; overload;
    function GetBox: TFRect;
    function GetEntity(Entity: TsgDXFEntity): Integer; override;
    function GetFilteredEntity(Entity: TsgDXFEntity): Integer;
    function GetMaterialByName(AMaterialName: string): TGLMaterial;
    function GetOrbit3DClass: TClass; override;
    function GetSizesAsString: string; override;
    procedure GetView(var AMatrix: TFMatrix);
    function GetHasTriangledMesh: Boolean; override;
    function GetZoomInEnable: Boolean; override;
    function GetZoomOutEnable: Boolean; override;
    function InitializeIterateParams: PsgCADIterate; override;
    procedure LoadView(const AMatrix: TFMatrix);
    procedure VisibleChanging; override;
    procedure MouseDown(Button: Controls.TMouseButton; Shift: TShiftState;
      X, Y: Integer); override;
    procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
    procedure MouseUp(Button: Controls.TMouseButton; Shift: TShiftState;
      X, Y: Integer); override;
    procedure OrbitMouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer); override;
    function GetMeshLines(AMesh: TMeshObject; AList: TFPointList;
      AMatrix: PFMatrix): Boolean;
    function EntToGLObj(AEnt: TObject; APIndex: PInteger = nil): TObject;
    procedure DeleteEnts(AEntities: TList);
    function GetInsertMeshes(AInsert: TObject): TList;

    procedure RepaintScene;
    procedure Reload(AEntities: TList);
    procedure SetSelected(AEntities: TList);
    property IntersectCache: PFPoint read FIntersectCache;
    property PickObjects: TsgPickObject read FPickObjects;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    function AlterScale(Factor: Double; IsAbsolute: Boolean;
      Position: TPoint): Boolean; override;
    function BeginUpdate: Integer; override;
    procedure Clear; override;
    function Empty: Boolean; override;
    function EndUpdate: Integer; override;
    procedure FitToSize; override;
    function GetDrawingCoords(ACoordX, ACoordY: Integer;
      var AUnits: string): TFPoint; override;
    function GetDrawingInternalCoords(ACoordX, ACoordY: Integer): TFPoint; override;
    function GetDrawingUCSCoords(ACoordX, ACoordY: Integer;
      var AUnits: string): TFPoint; override;
    function GetRealPoint(ACoordX, ACoordY: Double;
      var APointInUCS: TFPoint): TFPoint; override;
    function IsCanBeEdited: Boolean; override;
    function IsCanShowCoords: Boolean; override;
    function IsCanShowSizes: Boolean; override;
    function IsUseGraphic: Boolean; override;
    procedure LoadFromConverter(const AConverter: TsgDXFConverter;
      const ACADImage: TsgCADImage); override;
    procedure LoadFromFile(const FileName: string); override;
    procedure MovePictureRect(const ADeltaX, ADeltaY: Integer); override;
    function OpenVPort: Boolean; override;
    function Rotate(Axis: TsgAxes; Angle: Extended): Boolean; override;
    procedure RotToView(const A3DView: TsgDXFViewDirection); override;
    procedure Translate(const AVector: TAffineVector);
    procedure WndProc(var Message: TMessage); override;
    property MeshQuality: TsgMeshQuality read GetMeshQuality write
      SetMeshQuality;
    property LoadFromImage: TsgCADImage read FLoadFromImage write SetLoadFromImage;
    property Navigator3D: Tsg3DNavigator read GetNavigator;
    property OuterWireColor: TColor read FOuterWireColor write FOuterWireColor;
    property ShowingStyle: Tsg3DShowingStyle read GetShowingStyle write SetShowingStyle;
    property HighLightObject: TObject read GetHighLightObject write SetHighLightObject;
    property PickObject: TObject read GetPickObject write SetPickObject;
    property OnHighLight: TNotifyEvent read FOnHighLight write FOnHighLight;
    property OnPickObject: TNotifyEvent read FOnPickObject write FOnPickObject;
    property ShowHighlight: Boolean read GetShowHighlight write SetShowHighlight;
    property MouseRotate: Boolean read FMouseRotate write SetMouseRotate;
  end;

var
  Solid3DFileExts: TStringList = nil;
  CF_IGES, CF_STEP: Word;

const
  S3dfImportLibraryName = 'converter3df';
  S3dfImportLibraryExt = 'dll';
  S3dfImportLibrary = S3dfImportLibraryName + '.' + S3dfImportLibraryExt;
  cntInstImprtLibPath = 'http://www.cadsofttools.com/download/' +
    S3dfImportLibraryName + {$IFDEF SG_CPUX64}'x64' + {$ENDIF}  '.zip'; //needs to be right!
  cnstSTLFileExt = 'stl';
  cnstSTLFileExtWithDot = '*.stl';
  cnstSTLFileDesc = 'Stereolithography files';
  cnstSTLFileDescWithExt = cnstSTLFileDesc + ' (' + cnstSTLFileExtWithDot + ')';
  cnstSTLFilterDesc = cnstSTLFileDescWithExt + '|' + cnstSTLFileExtWithDot + '|';
  cnstSTLGLVectorFileClassName = 'TGLSTLVectorFile';

procedure CreateCamera(AOwner, ATargetObject: TGLBaseSceneObject;
  var ACamera: TGLCamera; AScale: Single);
procedure CreateLight(AOwner: TGLBaseSceneObject; var ALight: TGLLightSource);
function FPoint2Vect(const APoint: TFPoint): TAffineVector;
function GetACISIndexOf(const VectorList: TAffineVectorList;
  const V: TAffineVector): Integer;
function Is3DVectorFile(const AFileName: string): Boolean;
function IsACISEqual(const A, B: Double): Boolean;
function IsACISVectorEquals(const V1, V2: TAffineVector): Boolean;
function IsPolyClockWise(const PolyVerts: TAffineVectorList;
  const PolyIndices: TIntegerList = nil): Boolean;
function IsTriClockWise(const V0, V1, V2: TAffineVector): Boolean;
procedure LoadViewFrom3DNavigator(const ACADImage: TsgCADImage;
  const ANavigator: Tsg3DDrawingNavigator);
procedure LoadViewFromCADImage(const ACADImage: TsgCADImage;
  const ANavigator: Tsg3DDrawingNavigator);
procedure SetDefDirForCamera(ACamera: TGLBaseSceneObject);
procedure SetDefDirForObject(AObject: TGLBaseSceneObject);
procedure SetDefDistanceForCamera(const GLCamera: TGLCamera; BoundingSphereRadius,
  SceneScale: Single; TargetEmpty: Boolean = False);
procedure SetColorForMaterial(Material: TGLMaterial; const Color: TColorVector);
function Vect2FPoint(const Vect: TAffineVector): TFPoint; overload;
function Vect2FPoint(const Vect: TVector4f): TFPoint; overload;

procedure GLMatrixToMatrix(const AGLMat: TMatrix4f; var AMatrix: TFMatrix);
procedure MatrixToGLMatrix(const AMatrix: TFMatrix; var AGLMat: TMatrix4f);

implementation

uses
{$IFDEF CS_USEFORM}
  Forms,
{$ENDIF}
  ApplicationFileIO;

const
  cnstHighlightColor = $1A000000 or clYellow;
  cnstPickColor = clAqua;
  Lock: TCriticalSection = nil;
  cnstColorAspect = 1.0 / 255.0;

type

  PRGBA = ^TRGBA;
  TRGBA = record
    Red, Green, Blue, Alpha: Byte;
  end;

  TMouseButton = Controls.TMouseButton;

  TsgVertexList = class(TFGVertexIndexList)
  private
    FMode1: Cardinal;
    FMode2: Cardinal;
  public
    constructor Create; override;
    procedure BuildList(var mrci: TRenderContextInfo); override;
  end;

  TsgTeselledVertexList = class(TFGVertexIndexList)
  public
    procedure BuildList(var mrci: TRenderContextInfo); override;
  end;

  TsgHiddenLineShader = class(TGLShader)
  private
    FBackgroundColor: TColorVector;
    FPassCount: Integer;
  public
    procedure DoApply(var rci : TRenderContextInfo; Sender : TObject); override;
    function DoUnApply(var rci : TRenderContextInfo) : Boolean; override;
  end;

  TCustomFindEnt = class
  private
    FObj: TObject;
    FParams: TsgCADIterate;
    FMatrix: TFMatrix;
    function GetParams: PsgCADIterate;
    function GetObject: TObject;
    procedure SetObject(const Value: TObject);
  protected
    function GetEnt(Entity: TsgDXFEntity): Integer; virtual;
    procedure Iterate(AConverter: TsgDXFConverter); virtual;
    property Matrix: TFMatrix read FMatrix write FMatrix;
    property Params: PsgCADIterate read GetParams;
    property Obj: TObject read GetObject write SetObject;
  end;

  TsgGLPickLines = class(TsgGLLines)
  private
    FPickObjects: TsgPickObject;
  protected
    property PickObjects: TsgPickObject read FPickObjects write FPickObjects;
  public
    destructor Destroy; override;
    procedure BuildList(var rci: TRenderContextInfo); override;
  end;

  TNodeLite = class(TCustomNode)
  protected
    function AddChild(AObj: TObject): TNodeLite;
    procedure GetMatrix(out AMatrix: TFMatrix); virtual;
  public
    destructor Destroy; override;
    function Find(AObj: TObject; var I: Integer): Boolean;
    function FindDeep(AObj: TObject; var AOwner: TNodeLite; var I: Integer;
      const ALevel: Integer = MaxInt): Boolean;
    procedure Clean; override;
  end;

  TNodeGroup = class(TNodeLite)
  private
    FGroup: TObject;
  public
    property Group: TObject read FGroup write FGroup;
  end;

  TRoot = class(TNodeLite)
  private
    FMatrix: TFMatrix;
  protected
    procedure GetMatrix(out AMatrix: TFMatrix); override;
  end;

  //TsgDXFVertexList = class (TFGVertexNormalTexIndexList)

  TGLBaseMeshAccess = class(TGLBaseMesh);
  TGLBaseSceneObjectAccess = class(TGLBaseSceneObject);
  TsgCADBasePolyLineAccess = class(TsgCADBasePolyline);
  TsgDXFPolylineAccess = class(TsgDXFPolyline);
  TGLSceneBufferAccess = class(TGLSceneBuffer);
  TsgDXFConverterAccess = class(TsgDXFConverter);
  TsgCADImageAccess = class(TsgCADImage);
  TsgDXFEntityAccess = class(TsgDXFEntity);
  TsgCADCurvePolygonAccess = class(TsgCADCurvePolygon);
  TsgDXFInsertAccess = class(TsgDXFInsert);
  TGLFreeFormAccess = class(TGLFreeForm);
  TsgDXFSolidAccess = class(TsgDXFSolid);

const
  cntZeroZ = 0.0001;
  cntDefCameraPos: TVector3f = (X:7; Y:5; Z:-4);
  //cntDefCameraPos: TVector3f = (-100, 0, cntZeroZ);
  cntDefAxisHeight = 0.5;
  cntDefAxisPos: TVector3f = (X:0; Y:0; Z:0){(1.5, 0, -1.5)};
  cntAxisImageWidth = 128;
  cntAxisImageHeight = 128;
  cntAxisPosMinY = cntAxisImageHeight + 16;
  cntDefPerspCameraScale = 12;
  cntDefOrthCameraScale = 1;
  cntDefPerspAxisCameraScale = 1;

  cntMMPerInch = 25.4;
  cntGLGraphicResolution = 150;
  cntGLGraphicMaxWidth = 600 / cntMMPerInch * cntGLGraphicResolution;
  cntGLGraphicMaxHeigth = 420 / cntMMPerInch * cntGLGraphicResolution;

  LtAmbient: TColorVector = (X:0.01; Y:0.01; Z:0.01; W:1);
  LtDiffuse: TColorVector = (X:0.26; Y:0.26; Z:0.26; W:1);
  LtSpecular: TColorVector = (X:0.4; Y:0.4; Z:0.4; W:1);

procedure CreateCamera(AOwner, ATargetObject: TGLBaseSceneObject; var ACamera: TGLCamera;
  AScale: Single);
begin
  ACamera := TGLCamera.CreateAsChild(AOwner);
  ACamera.CameraStyle := csPerspective;
//  ACamera.DepthOfView := 700.0;
//  ACamera.FocalLength := 70.0;
  //ACamera.NearPlaneBias := 0.1;
  SetDefDirForCamera(ACamera);
  ACamera.TargetObject := ATargetObject;
  ACamera.SceneScale := AScale;
//  ACamera.Position.AsAffineVector := cntDefCameraPos;
end;

procedure CreateLight(AOwner: TGLBaseSceneObject; var ALight: TGLLightSource);
begin
  ALight := TGLLightSource.CreateAsChild(AOwner);
  ALight.ConstAttenuation := 1.0;
  ALight.Ambient.Color := LtAmbient;
  ALight.Diffuse.Color := LtDiffuse;
  ALight.Specular.Color := LtSpecular;
  ALight.LightStyle := {lsSpot}lsParallel;
  ALight.SpotCutOff := 180;
  ALight.SpotDirection.SetVector(0, 0, -1);

  //ALight.SpotDirection.SetVector(0.5773503, 0.5773503, 0.5773503);
  //ALight.Position.AsAffineVector :=  cntDefAxisPos;
  ALight.Position.AsAffineVector := VectorScale(cntDefCameraPos, 10);
  //ALight.Position.Assign(AOwner.Position);
end;

function Vect2FPoint(const Vect: TAffineVector): TFPoint; overload;
begin
  Result := MakeFPoint(Vect.V[0], Vect.V[1], Vect.V[2]);
end;

function Vect2FPoint(const Vect: TVector4f): TFPoint; overload;
begin
  Result := MakeFPoint(Vect.V[0], Vect.V[1], Vect.V[2]);
end;

function FPoint2Vect(const APoint: TFPoint): TAffineVector;
begin
  Result.V[0] := APoint.X;
  Result.V[1] := APoint.Y;
  Result.V[2] := APoint.Z;
end;

procedure SetDefDirForCamera(ACamera: TGLBaseSceneObject);
begin
  ACamera.Direction.SetVector(0, 0, -1);
  ACamera.Up.SetVector(0, 1, 0);
end;

procedure SetDefDirForObject(AObject: TGLBaseSceneObject);
begin
  AObject.Direction.SetVector(0, 0, 1);
  AObject.Up.SetVector(0, 1, 0);
end;

function GetMaterialName(AMaterial: TColor): string;
begin
  Result := IntToHex(AMaterial, 8);
end;

procedure SetMeshMaterial(AMesh: TMeshObject; const AMaterialName: string;
  AMaterialLibrary: TGLAbstractMaterialLibrary);
var
  I: Integer;
begin
  AMesh.DropMaterialLibraryCache;
  for I := 0 to AMesh.FaceGroups.Count - 1 do
    AMesh.FaceGroups[I].MaterialName := AMaterialName;
  if AMaterialLibrary is TGLMaterialLibrary then
    AMesh.PrepareMaterialLibraryCache(TGLMaterialLibrary(AMaterialLibrary));
end;

procedure SetColorForMaterial(Material: TGLMaterial; const Color: TColorVector);
const
  cnstShininess: TShininess = 128;
begin
  Material.BlendingMode := bmCustom;
  Material.Texture.MappingSCoordinates.AsVector := XHmgVector;
  Material.Texture.MappingTCoordinates.AsVector := XHmgVector;
  //Material.FrontProperties.Ambient.Color := Color;
  Material.FrontProperties.Diffuse.Color := Color;
  Material.FrontProperties.Specular.Color := Color;
  //Material.FrontProperties.Emission.Color := Color;
  Material.FrontProperties.Shininess := cnstShininess;
  Material.BackProperties.Assign(Material.FrontProperties);
end;

function _ColorToRGB(Color: TColor): Longint;
const
  SystemColor = $FF000000;
begin
  if (Color and SystemColor = SystemColor) and (Color and $FFFFFF <= 30) then
    Result := GetSysColor(Color and $000000FF)
  else
    Result := Color;
end;

function ColorToVector(AColor: TColor): TColorVector;
var
  vRGBA: TRGBA;
  vWinColor: Cardinal absolute vRGBA;
begin
  vWinColor := _ColorToRGB(AColor);
  Result.V[0] := vRGBA.Red * cnstColorAspect;
  Result.V[1] := vRGBA.Green * cnstColorAspect;
  Result.V[2] := vRGBA.Blue * cnstColorAspect;
  Result.V[3] := 1.0 - vRGBA.Alpha * cnstColorAspect;
end;

function AssignMaterial(AMaterial: TColor;
  AMaterials: TGLLibMaterials): TGLLibMaterial; overload;
var
  vMatName: string;
begin
  if AMaterial = clBlack then
    AMaterial := clGray;
  Result := nil;
  if Assigned(AMaterials) then
  begin
    vMatName := GetMaterialName(AMaterial);
    Result := AMaterials.GetLibMaterialByName(vMatName);
    if not Assigned(Result) then
    begin
      Result := AMaterials.Add;
      Result.Name := vMatName;
      SetColorForMaterial(Result.Material, ColorToVector(AMaterial));
    end;
  end;
end;

function AssignMaterial(const AMaterial: TColor;
  AGLMaterialLibrary: TGLMaterialLibrary): string; overload;
var
  vMatLibItem: TGLLibMaterial;
begin
  Result := '';
  if Assigned(AGLMaterialLibrary) then
  begin
    vMatLibItem := AssignMaterial(AMaterial, AGLMaterialLibrary.Materials);
    if Assigned(vMatLibItem) then
      Result := vMatLibItem.Name;
  end;
end;

procedure SetDefDistanceForCamera(const GLCamera: TGLCamera; BoundingSphereRadius,
  SceneScale: Single; TargetEmpty: Boolean = False);
begin
  if (GLCamera.TargetObject <> nil) and (not TargetEmpty) then
  begin
    GLCamera.SceneScale := SceneScale;
    if BoundingSphereRadius > 0 then
    begin
      case GLCamera.CameraStyle of
        csPerspective:
          begin
            GLCamera.AdjustDistanceToTarget(BoundingSphereRadius * 0.25);
            GLCamera.DepthOfView := 2 * GLCamera.DistanceToTarget + 2 * BoundingSphereRadius;
          end;
        csOrthogonal:
          begin

          end;
      end;
    end;
  end;
end;

function SceneBufferOrthoScreenToWorld(ABuffer: TGLSceneBuffer;
  AScreenPos: TVector3f; ASceneScale: Single): TAffineVector;
var
  vFocalKoef: Single;
begin
  vFocalKoef := 100 * ABuffer.Camera.NearPlaneBias / (ABuffer.Camera.FocalLength *
    ASceneScale);
  if ABuffer.ViewPort.Width > ABuffer.ViewPort.Height then
    vFocalKoef := vFocalKoef / ABuffer.ViewPort.Width
  else
    vFocalKoef := vFocalKoef / ABuffer.ViewPort.Height;
  SetVector(Result,
    VectorCombine3(
      ABuffer.Camera.AbsolutePosition,
      ABuffer.Camera.AbsoluteUpVectorToTarget,
      ABuffer.Camera.AbsoluteRightVectorToTarget, 1,
    (AScreenPos.V[1] - (ABuffer.ViewPort.Height / 2)) * vFocalKoef,
    (AScreenPos.V[0] - (ABuffer.ViewPort.Width / 2)) * vFocalKoef));
  Result := VectorSubtract(Result, ABuffer.Camera.Position.AsAffineVector);
end;

function MeshEntCompare(Item1, Item2: TObject): Integer;
begin
  Result := Integer(TsgNativeUInt(TsgMeshObject(Item1).Entity) -
    TsgNativeUInt(TsgMeshObject(Item2).Entity));
end;

function MeshInsertCompare(Item1, Item2: TObject): Integer;
begin
  Result := Integer(TsgNativeUInt(TsgMeshObject(Item1).Insert) -
    TsgNativeUInt(TsgMeshObject(Item2).Insert));
end;

function NodesCompare(Item1, Item2: TObject): Integer;
begin
  Result := Integer(TsgNativeUInt(TNodeLite(Item1).Obj) -
    TsgNativeUInt(TNodeLite(Item2).Obj));
end;

function QFind(AList: TPersistentObjectList; ASorted: Boolean;
  ADuplicates: TDuplicates; const ACompare: TObjectListSortCompare;
  const AItem: Pointer; var AIndex: Integer): Boolean;
var
  L, H, I, C: Integer;
begin
  Result := False;
  with AList do
  begin
    H := Count - 1;
    if ASorted then
    begin
      L := 0;
      while L <= H do
      begin
        I := (L + H) shr 1;
        C := ACompare(List^[I], AItem);
        if C < 0 then
          L := I + 1
        else
        begin
          H := I - 1;
          if C = 0 then
          begin
            Result := True;
            if ADuplicates <> dupAccept then
              L := I;
          end;
        end;
      end;
      AIndex := L;
    end
    else
    begin
      AIndex := 0;
      while (AIndex <= H) and (ACompare(List^[AIndex], AItem) <> 0) do
        Inc(AIndex);
      if AIndex <= H then
        Result := True;
    end;
  end;
end;

type
  Tsg3DDrwNavOrbit3D = class(TsgNaviOrbit3D)
  private
    FOnVisibleChanging: TNotifyEvent;
    function GetNavigator3D: Tsg3DDrawingNavigator;
  protected
    procedure VisibleChanging; override;
  public
    procedure Paint; override;
    property Navigator3D: Tsg3DDrawingNavigator read GetNavigator3D;
    property OnVisibleChanging: TNotifyEvent read FOnVisibleChanging
      write FOnVisibleChanging;
  end;

function GetACISIndexOf(const VectorList: TAffineVectorList;
  const V: TAffineVector): Integer;
var
  I: Integer;
begin
  Result := -1;
  for I := 0 to VectorList.Count - 1 do
    if IsACISVectorEquals(VectorList[I], V) then
    begin
      Result := I;
      Break;
    end;
end;

function Is3DVectorFile(const AFileName: string): Boolean;
{$IFNDEF SG_NO3DMODELS}
var
  vExt: string;
begin
  vExt := Copy(ExtractFileExt(AFileName), 2, MaxInt);
  if Solid3DFileExts =  nil then
    Result := GetVectorFileFormats.FindExt(vExt) <> nil
  else
    Result := Solid3DFileExts.IndexOf(ExtractFileExt(AFileName)) >= 0;
end;
{$ELSE}
begin
  Result := False;
end;
{$ENDIF}

function IsACISEqual(const A, B: Double): Boolean;
begin
  Result := Round(A) = Round(B);
end;

function IsACISVectorEquals(const V1, V2: TAffineVector): Boolean;
begin
  Result := IsACISEqual(V1.V[0], V2.V[0]) and IsACISEqual(V1.V[1], V2.V[1]) and IsACISEqual(V1.V[2], V2.V[2]);
end;

function IsPolyClockWise(const PolyVerts: TAffineVectorList;
  const PolyIndices: TIntegerList = nil): Boolean;
var
  I, J: Integer;
  vDeterminant: Single;
  vMatrix: TAffineMatrix;
  vPolyIndices: TIntegerList;
begin
  vDeterminant := 0;
  vPolyIndices := TIntegerList.Create;
  try
    if PolyIndices =  nil then
      for I := 0 to PolyVerts.Count - 1 do
        vPolyIndices.Add(I)
    else
      vPolyIndices.Assign(PolyIndices);

    for I := 0 to vPolyIndices.Count-1 do
    begin
      for J := 0 to 2 do
        if (I + J) >= vPolyIndices.Count then
          vMatrix.V[J] := PolyVerts[vPolyIndices[I + J - vPolyIndices.Count]]
        else
          vMatrix.V[J] := PolyVerts[vPolyIndices[I + J]];
      vDeterminant := vDeterminant + MatrixDeterminant(vMatrix);
    end;
  finally
    vPolyIndices.Free;
  end;
  Result := vDeterminant < 0;
end;

function IsTriClockWise(const V0, V1, V2: TAffineVector): Boolean;
var
  vMatrix: TAffineMatrix;
begin
  vMatrix.V[0] := V0;
  vMatrix.V[1] := V1;
  vMatrix.V[2] := V2;
  Result := MatrixDeterminant(vMatrix) < 0;
end;

procedure LoadViewFrom3DNavigator(const ACADImage: TsgCADImage;
  const ANavigator: Tsg3DDrawingNavigator);
var
  vMatrix: TFMatrix;
begin
  if (ANavigator <> nil) and (ACADImage <> nil) then
  begin
    ANavigator.GetView(vMatrix);
    ACADImage.SetDrawMatrix(vMatrix);
  end;
end;

procedure LoadViewFromCADImage(const ACADImage: TsgCADImage;
  const ANavigator: Tsg3DDrawingNavigator);
begin
  if (ANavigator <> nil) and (not ANavigator.Empty) and (ACADImage <> nil) then
    ANavigator.LoadView(ACADImage.DrawMatrix);
end;

procedure GLMatrixToMatrix(const AGLMat: TMatrix4f; var AMatrix: TFMatrix);
var
  I, J: Integer;
begin
  for I := 0 to 3 do
    for J := 0 to 2 do
      AMatrix.M[I, J] := AGLMat.V[I].V[J];
end;

procedure MatrixToGLMatrixFull4f(const AMatrix: TFMatrix; var AGLMat: TMatrix4f);
var
  I, J: Integer;
begin
  FillChar(AGLMat, SizeOf(AGLMat), 0);
  for I := 0 to 3 do
    for J := 0 to 2 do
      AGLMat.V[I].V[J] := AMatrix.M[I, J];
  AGLMat.V[3].V[3] := 1;
end;

procedure MatrixToGLMatrix(const AMatrix: TFMatrix; var AGLMat: TMatrix4f);
var
  I, J: Integer;
begin
  for I := 0 to 2 do
    for J := 0 to 2 do
      AGLMat.V[I].V[J] := AMatrix.M[I, J];
  for I := 0 to 2 do
  begin
    AGLMat.V[I].V[3] := 0;
    AGLMat.V[3].V[I] := 0;
  end;
  AGLMat.V[3].V[3] := 1;
end;

{ Tsg3DNavigator }

procedure Tsg3DNavigator.Clear;
var
  I: Integer;
begin
  for I := 0 to FGLFreeForm.MeshObjects.Count - 1 do
    FGLFreeForm.MeshObjects[I].FaceGroups.Clear;
  for I := FGLFreeForm.Count - 1 downto 1 do
    FGLFreeForm.Children[I].Free;
  FGLFreeForm.MeshObjects.Clear;
  FGLFreeForm.Skeleton.Clear;
  FGLMaterialLibrary.Materials.Clear;
  FCurrentLine := nil;
  FLineMass := nil;
end;

function Tsg3DNavigator.ClientToWorld(X, Y: Double; Z: Double = 0): TVector3f;
begin
  if FCameraStyle = csOrthogonal then
    Result := Buffer.OrthoScreenToWorld(Round(X), Round(Buffer.Height - Y))
  else
    Result := Buffer.ScreenToWorld(AffineVectorMake(X, Buffer.Height - Y, Z));
end;

constructor Tsg3DNavigator.Create(AOwner: TComponent);

var
  I: Integer;
  vParent: TGLBaseSceneObject;

  procedure SetDefValues(AValue: TGLSceneViewer);
  begin
    AValue.Buffer.FaceCulling := False;
    AValue.Buffer.AmbientColor.Color := clrGray20;
    AValue.FieldOfView := 154.3915;
  end;
  procedure SetAllTransparent(AFace: TGLFaceProperties);
  begin
    AFace.Ambient.Color := clrWhite;
    AFace.Diffuse.Color := clrWhite;
    AFace.Emission.Color := clrBlack;
    AFace.Specular.Color := clrWhite;
    AFace.Shininess := 128;
  end;

begin
  FCurrentLine := nil;
  FLineMass := nil;
  FShowCubeExtents := True;
  FShowAxis := True;
  FDefCameraScale := cntDefPerspCameraScale;
  FCameraStyle := csCustom;


  inherited Create(AOwner);
  FGLScene := TGLScene.Create(Self);
  FGLMaterialLibrary := TGLMaterialLibrary.Create(Self);

  FGLDCScene := TGLDummyCube.CreateAsChild(FGLScene.Objects);
  FGLDCScene.CubeSize := 1;
  FGLDCScene.Position.SetPoint(0, 0, 0);
  SetDefDirForObject(FGLDCScene);
  FGLDCScene.EdgeColor.SetColor(1, 1, 1, 1);
  FGLDCScene.Scale.SetVector(1, 1, 1);
  FGLDefSceneMat := FGLDCScene.Matrix;
  FGLDCScene.Position.OnNotifyChange := ScenePositionChange;

  //FGLDCScene.ShowAxes := True;

  FGLFreeForm := TGLFreeForm.CreateAsChild(FGLDCScene);
  FGLFreeForm.AutoCentering := [macCenterX, macCenterY, macCenterZ,
    macUseBarycenter];
  FGLFreeForm.AutoScaling.SetPoint(1, 1, 1);
  SetDefDirForObject(FGLFreeForm);
  FGLFreeForm.MaterialLibrary := FGLMaterialLibrary;
//  FGLFreeForm.Material.BackProperties.Ambient.Color := clrGray80{clrGray20};
//  FGLFreeForm.Material.BackProperties.Diffuse.Color := clrGray80;
//  FGLFreeForm.Material.BackProperties.Emission.Color := clrGray80{clrBlack};
//  FGLFreeForm.Material.BackProperties.Specular.Color := clrBlack;
//  FGLFreeForm.Material.FrontProperties.Assign(FGLFreeForm.Material.BackProperties);
  FGLFreeForm.Scale.SetVector(1, 1, 1);
  FGLFreeForm.IgnoreMissingTextures := True;

  FGLCubeExtents := TGLCube.CreateAsChild(FGLFreeForm);
  SetDefDirForObject(FGLCubeExtents);
  FGLCubeExtents.Scale.SetVector(1, 1, 1);
  FGLCubeExtents.Material.BackProperties.Ambient.Color := clrBlack;
  FGLCubeExtents.Material.BackProperties.Diffuse.SetColor(0.3, 0.3, 0.3, 0);
  FGLCubeExtents.Material.BackProperties.Emission.Color := FGLCubeExtents.Material.BackProperties.Diffuse.Color;
  FGLCubeExtents.Material.BackProperties.Specular.Color := FGLCubeExtents.Material.BackProperties.Diffuse.Color;
  FGLCubeExtents.Material.PolygonMode := pmLines;
  FGLCubeExtents.Material.FaceCulling := fcNoCull;
  FGLCubeExtents.Material.FrontProperties.Assign(FGLCubeExtents.Material.BackProperties);
  FGLCubeExtents.Material.MaterialOptions := [moNoLighting];
  FGLCubeExtents.CubeWidth := 2;
  FGLCubeExtents.CubeHeight := 2;
  FGLCubeExtents.CubeDepth := 2;
  FGLCubeExtents.Visible := FShowCubeExtents;

  FGLDCTarget := TGLDummyCube.CreateAsChild(FGLScene.Objects);
  SetDefDirForObject(FGLDCTarget);

  //camera
  CreateCamera(FGLDCTarget, FGLDCTarget, FGLCamera, FDefCameraScale);
//  FGLCamera.NearPlaneBias := 0.01;

  //axes arrows
  FAxes := Tsg3DAxis.CreateEx(FGLScene, Self);
  FAxes.Width := cntAxisImageWidth;
  FAxes.Height := cntAxisImageHeight;
  FAxes.AfterRender := DoOnAfter3DAxisRender;
  FAxes.BeforeRender := DoOnBefore3DAxisRender;

  vParent := FGLScene.Objects;
  //light
  for I := 0 to LightsCount - 1 do
    CreateLight(vParent, FLights[I]);

  FGLAxisImage := TGLHUDSprite.CreateAsChild(FGLScene.Objects);
  FGLAxisImage.Width := cntAxisImageWidth;
  FGLAxisImage.Height := cntAxisImageHeight;

  SetDefDirForObject(FGLAxisImage);

  FGLAxisImage.Material.Texture.ImageClassName := TGLBlankImage.ClassName;
  TGLBlankImage(FGLAxisImage.Material.Texture.Image).Width := cntAxisImageWidth;
  TGLBlankImage(FGLAxisImage.Material.Texture.Image).Height := cntAxisImageHeight;

//  TGLBlankImage(FGLAxisImage.Material.Texture.Image).ColorFormat := 6408;
  FGLAxisImage.Material.Texture.MagFilter := maNearest;
  FGLAxisImage.Material.Texture.MinFilter := miNearest;
  FGLAxisImage.Material.Texture.TextureMode := tmModulate;
  FGLAxisImage.Material.Texture.Compression := tcNone;
  FGLAxisImage.Material.Texture.Disabled := False;

  SetAllTransparent(FGLAxisImage.Material.BackProperties);
  SetAllTransparent(FGLAxisImage.Material.FrontProperties);

  //FGLAxisImage.Material.Texture.Border := 0;
  FGLAxisImage.Material.Texture.Disabled := False;
  FGLAxisImage.Material.BlendingMode := bmTransparency;
//  FGLAxisImage.Material.Texture.ImageAlpha := tiaTopLeftPointColorTransparent;
  FGLAxisImage.Material.Texture.TextureMode := tmReplace;
  FGLAxisImage.Material.Texture.MappingSCoordinates.AsVector := XHmgVector;
  FGLAxisImage.Material.Texture.MappingTCoordinates.AsVector := XHmgVector;
  FGLAxisImage.Scale.SetVector(1, 1, 1);
  FGLAxisImage.AlphaChannel := 0;
  FGLAxisImage.Visible := FShowAxis;

  FGLSphere := TGLSphere.CreateAsChild(vParent);
  FGLSphere.Position.Assign(FGLDCScene.Position);
  FGLSphere.Visible := False;

  Camera := FGLCamera;

  FGLDirectRender := TGLDirectOpenGL.CreateAsChild(FGLScene.Objects);
  FGLDirectRender.OnRender := DoOnDirectOGLRender;

  BackgroundColor := $00DDDDDD;
  SetDefValues(Self);
  Buffer.WrapUpRendering := DoOnWrapUpRender;
  Buffer.AfterRender := DoOnAfterRender;
  Buffer.BeforeRender := DoOnBeforeRender;

  ShowAxis := False;
  CameraStyle := csOrthogonal;{FGLCamera.CameraStyle;}
  DoResetCamera;
end;

destructor Tsg3DNavigator.Destroy;
begin
//  FGLLightSource.Free;
//  FGLCamera.Free;
//  FGLDCScene.Free;
  inherited Destroy;
end;

procedure Tsg3DNavigator.DoAfterLoadScene;
var
  I: Integer;
  vMats: TGLLibMaterial;
  vAABB: TAABB;
  vMaxx: TGLFloat;
  vMaterial: TColor;
begin
  if FGLMaterialLibrary.Materials.Count = 0 then
  begin
    FGLFreeForm.Material.MaterialLibrary := FGLMaterialLibrary;
    vMats := FGLMaterialLibrary.Materials.Add;
    vMats.Material.FrontProperties.Diffuse.Red := 0;
    FGLFreeForm.Material.LibMaterialName := vMats.Name;
    vMaterial := clBlue;
    for I := 0 to FGLMaterialLibrary.Materials.Count - 1 do
      SetColorForMaterial(FGLMaterialLibrary.Materials[I].Material, ColorToVector(vMaterial));
    for I := 0 to FGLFreeForm.MeshObjects.Count - 1 do
    begin
      if Parent is Tsg3DDrawingNavigator then
      begin
        Tsg3DDrawingNavigator(Parent).AssignMaterialForMesh(FGLFreeForm.MeshObjects[I], vMaterial);
        if FGLFreeForm.MeshObjects[I].Normals.Count = 0 then
          Tsg3DDrawingNavigator(Parent).GenNormals(FGLFreeForm.MeshObjects[I]);
      end;
    end;
  end;
  vAABB := BBToAABB(FGLFreeForm.BoundingBox);
  vMaxx := MaxFloat(MaxFloat(vAABB.max.V[0] - vAABB.min.V[0],
    vAABB.max.V[1] - vAABB.min.V[1]), vAABB.max.V[2] - vAABB.min.V[2]);
  FGLCubeExtents.CubeWidth := vMaxx;
  FGLCubeExtents.CubeHeight := vMaxx;
  FGLCubeExtents.CubeDepth := vMaxx;
//  FGLCubeExtents.CubeWidth := vAABB.max[0] - vAABB.min[0];
//  FGLCubeExtents.CubeHeight := vAABB.max[1] - vAABB.min[1];
//  FGLCubeExtents.CubeDepth := vAABB.max[2] - vAABB.min[2];
  FGLCubeExtents.Position.AsAffineVector := VectorLerp(vAABB.min, vAABB.max, 0.5);
  FGLSphere.Radius := FGLDCScene.BoundingSphereRadius / 15;
end;

procedure Tsg3DNavigator.DoBufferChange(Sender: TObject);
begin
  inherited DoBufferChange(Sender);
  Inc(FIsDirty);
end;

procedure Tsg3DNavigator.DoOnAfter3DAxisRender(Sender: TObject);
begin
  ShowScene;
  FAxes.HideAxis;
end;

procedure Tsg3DNavigator.DoOnAfterRender(Sender: TObject);
begin
  if Assigned(FOnAfterRender) then
    FOnAfterRender(Self);
end;

procedure Tsg3DNavigator.DoOnBefore3DAxisRender(Sender: TObject);
begin
  HideScene;
  if FShowAxis then
    FAxes.ShowAxis;
end;

procedure Tsg3DNavigator.DoOnBeforeRender(Sender: TObject);
begin
  if Assigned(FOnBeforeRender) then
    FOnBeforeRender(Self);
end;

procedure Tsg3DNavigator.DoOnDirectOGLRender(Sender: TObject;
  var rci: TRenderContextInfo);
begin

end;

procedure Tsg3DNavigator.DoOnWrapUpRender(Sender: TObject;
  var rci: TRenderContextInfo);

{$IFDEF DEMO}
var
  I: Integer;
  vGLCanvas: TGLCanvas;
  vClientWidth, vClientHeight, vTextWidth, vTextHeight: Integer;
  vTextPosX, vTextPosY, vInitTextPosX, vInitTextPosY: Integer;

  procedure MoveToTheNextChar(const CharIndex: Integer);
  begin
    vTextPosX := vInitTextPosX;
    Inc(vTextPosX, 2 * CharIndex * vTextWidth);
    vTextPosY := vInitTextPosY;
    vGLCanvas.MoveTo(vTextPosX, vTextPosY);
  end;

  procedure DrawVerticalLine; overload;
  begin
    vGLCanvas.MoveTo(vTextPosX, vTextPosY);
    vGLCanvas.LineTo(vTextPosX, vTextPosY + vTextHeight);
  end;

  procedure DrawHorizontalLine;  overload;
  begin
    vGLCanvas.MoveTo(vTextPosX, vTextPosY);
    vGLCanvas.LineTo(vTextPosX + vTextWidth, vTextPosY);
  end;

  procedure DrawLineTo(const X, Y: Integer);
  begin
    vGLCanvas.LineTo(X, Y);
    vGLCanvas.MoveTo(X, Y);
  end;

  procedure DrawHorizontalLine(const Offset: Integer); overload;
  begin
    MoveToTheNextChar(3);
    vGLCanvas.MoveTo(vTextPosX + vTextWidth div 4, vTextPosY + Offset);
    Inc(vTextPosY, Offset);
    DrawLineTo(vTextPosX + vTextWidth - vTextWidth div 4, vTextPosY);
  end;

  procedure DrawVertLine(const Offset: Integer); overload;
  begin
    MoveToTheNextChar(3);
    vGLCanvas.MoveTo(vTextPosX + Offset, vTextPosY + vTextHeight div 4);
    DrawLineTo(vTextPosX + Offset, vTextPosY + vTextHeight - vTextHeight div 4);
  end;

  procedure DrawSlesh(const OffsetX, OffsetY, SignDivX1,
    SignDivX2, SignY: Integer);
  begin
    MoveToTheNextChar(3);
    Inc(vTextPosX, OffsetX + SignDivX1 * vTextWidth div 4);
    Inc(vTextPosY, OffsetY);
    vGLCanvas.MoveTo(vTextPosX, vTextPosY);
    DrawLineTo(vTextPosX + SignDivX2 * vTextWidth div 4, vTextPosY +
      SignY * vTextHeight div 4);
  end;
{$ENDIF}

begin
  if Assigned(FOnWrapUpRender) then
    FOnWrapUpRender(Self, rci);
{$IFDEF DEMO}
  vClientWidth := ClientWidth;
  vClientHeight := ClientHeight;
  vGLCanvas := TGLCanvas.Create(vClientWidth, vClientHeight);
  try
    vTextWidth := 50;
    vTextHeight := 100;
    vInitTextPosX := vClientWidth div 2 - vTextWidth * 4;
    vInitTextPosY := vClientHeight div 2 - vTextHeight div 2;
    vGLCanvas.PenColor := GetColorByBackgroud(BackgroundColor);
    vGLCanvas.PenWidth := 5;

    MoveToTheNextChar(0); //D
    DrawVerticalLine;
    MoveToTheNextChar(0);
    DrawLineTo(vTextPosX + vTextWidth div 2, vTextPosY);
    DrawLineTo(vTextPosX + vTextWidth, vTextPosY + vTextHeight div 4);
    DrawLineTo(vTextPosX + vTextWidth, vTextPosY + vTextHeight - vTextHeight div 4);
    MoveToTheNextChar(0);
    vGLCanvas.MoveTo(vTextPosX, vTextPosY + vTextHeight);
    Inc(vTextPosY, vTextHeight);
    DrawLineTo(vTextPosX + vTextWidth div 2, vTextPosY);
    DrawLineTo(vTextPosX + vTextWidth, vTextPosY - vTextHeight div 4);
//    vGLCanvas.ArcDirection := adClockWise;
//    vGLCanvas.Arc(vTextPosX, vTextPosY, vTextPosX + vTextWidth,
//      vTextPosY + vTextHeight, vTextPosX, vTextPosY, vTextPosX,
//      vTextPosY + vTextHeight);

    MoveToTheNextChar(1); //E
    DrawVerticalLine;
    for I := 0 to 2 do
    begin
      MoveToTheNextChar(1);
      Inc(vTextPosY, I * (vTextHeight div 2));
      DrawHorizontalLine;
    end;

    for I := 0 to 1 do //M
    begin
      MoveToTheNextChar(2);
      Inc(vTextPosX, I * vTextWidth);
      DrawVerticalLine;
    end;
    MoveToTheNextChar(2);
    vGLCanvas.LineTo(vTextPosX + vTextWidth div 2, vTextPosY + vTextHeight div 2);
    MoveToTheNextChar(2);
    Inc(vTextPosX, vTextWidth);
    vGLCanvas.MoveTo(vTextPosX, vTextPosY);
    vGLCanvas.LineTo(vTextPosX - vTextWidth div 2, vTextPosY + vTextHeight div 2);

    DrawHorizontalLine(0); //O
    DrawHorizontalLine(vTextHeight);
    DrawVertLine(0);
    DrawVertLine(vTextWidth);
    DrawSlesh(vTextWidth, 0, -1, 1, 1);
    DrawSlesh(vTextWidth, vTextHeight, -1, 1, -1);
    DrawSlesh(0, 0, 1, -1, 1);
    DrawSlesh(0, vTextHeight, 1, -1, -1);

//    MoveToTheNextChar(3); //O
//    vGLCanvas.Ellipse(vTextPosX + vTextWidth div 2, vTextPosY + vTextHeight div 2,
//      vTextWidth div 2, vTextHeight div 2);
  finally
    vGLCanvas.Free;
  end;
{$ENDIF}
  FIsDirty := 0;
end;

procedure Tsg3DNavigator.DoResetCamera;
begin
  if (FCameraStyle = csOrthogonal) and (not IsEmpty) then
  begin
    FDefCameraScale := 0;
  end;

  FGLDCScene.Position.AsVector := NullHmgVector;
  FGLDCTarget.Position.AsVector := NullHmgVector;
  FGLCamera.Position.AsAffineVector := cntDefCameraPos;

  SetDefDirForObject(FGLDCTarget);
  SetDefDirForCamera(FGLCamera);
  SetDefDirForObject(FGLDCScene);

  SetDefDistanceForCamera(FGLCamera, BoundingSphereRadius, FDefCameraScale,
    IsEmpty);

  FGLDefSceneMat := FGLDCScene.Matrix;

  FAxes.SetDefPositions;
  SetDefDistanceForCamera(FAxes.Camera, FAxes.BoundingSphereRadius,
    FAxes.DefCameraScale);
  //SetAxisPos;
end;

function Tsg3DNavigator.GeLightsCount: Integer;
begin
  Result := High(FLights) - Low(FLights) + 1;
end;

function Tsg3DNavigator.GeLights(Index: Integer): TGLLightSource;
begin
  Result := FLights[Index];
end;

function Tsg3DNavigator.GetBackgroundColor: TColor;
begin
  Result := Buffer.BackgroundColor;
end;

function Tsg3DNavigator.GetBoundingSphereRadius: Single;
begin
  Result := 0;
  if not IsEmpty then
    Result := FGLFreeForm.BoundingSphereRadius;
end;

function Tsg3DNavigator.GetDefSceneMatrix: VectorGeometry.TMatrix;
begin
  Result := FGLDefSceneMat;
end;

class function Tsg3DNavigator.GetFilter: string;
var
  I, vPos: Integer;
  aSTLFilters: array[0..2] of string;
begin
  aSTLFilters[0] := cnstSTLFileExtWithDot + ';';
  aSTLFilters[1] := aSTLFilters[0];
  aSTLFilters[2] := cnstSTLFilterDesc;
  Result := VectorFileFormatsFilter;
  //STL extensions must be deleted from the filter, STL File format supports CADImportVCL kernel
  for I := Low(aSTLFilters) to High(aSTLFilters) do
  begin
    vPos := Pos(aSTLFilters[I], Result);
    if vPos > 0 then
      Delete(Result, vPos, Length(aSTLFilters[I]));
  end;
end;

function Tsg3DNavigator.GetGLFreeForm: TGLFreeForm;
begin
  Result := FGLFreeForm;
end;

function Tsg3DNavigator.GetMaterialByName(AMaterialName: string): TGLMaterial;
var
  vMat: TGLLibMaterial;
begin
  Result := nil;
  if Assigned(FGLMaterialLibrary) then
  begin
    vMat := FGLMaterialLibrary.Materials.GetLibMaterialByName(AMaterialName);
    if Assigned(vMat) then
      Result := vMat.Material;
  end;
end;

function Tsg3DNavigator.GetMeshIntersects(X, Y: Integer;
  const AMeshObjects: TList; var ACancel: Boolean;
  const APoints, ANormals: TFPointList): Boolean;
var
  vMeshObject: TMeshObject;
  vP: TsgNativeUInt;
  vEnt: TsgDXFEntity absolute vP;
  I, J: Integer;
  vList: TMeshObjectList;
  vStartPos3f, vRayVect3f: TVector3f;
  vRayPoint: TFPoint;
  vStartPos4, vRayVect, vIntersect, vIntersectNormal: TVector4f;
  vTriangles: TAffineVectorList;
  vInTriangle, vInTriangleLocal, vResult: Boolean;
  vTr: PAffineVectorArray;
  vAABB: TAABB;
begin
  if Assigned(AMeshObjects) then
    AMeshObjects.Clear;
  if Assigned(APoints) then
    APoints.Clear;
  if Assigned(ANormals) then
    ANormals.Clear;
  vStartPos3f := GLFreeForm.AbsoluteToLocal(ClientToWorld(X, Y));
  MakeVector(vStartPos4, vStartPos3f);
  vRayVect := Camera.AbsoluteVectorToTarget;
  NormalizeVector(vRayVect);
  vRayVect := GLFreeForm.AbsoluteToLocal(vRayVect);
  vRayVect3f := AffineVectorMake(vRayVect);
  vRayPoint := Vect2FPoint(vRayVect3f);
  vList := GLFreeForm.MeshObjects;
  I := 0;
  vP := 0;
  vResult := False;
  while (I < vList.Count) and not ACancel do
  begin
    vMeshObject := vList[I];
    if vMeshObject.Visible then
    begin
      vMeshObject.GetExtents(vAABB);
      if RayCastBoxIntersect(vStartPos3f, vRayVect3f, vAABB.min, vAABB.max) then
      begin
        vTriangles := vMeshObject.ExtractTriangles;
        try
          if Assigned(vTriangles) then
          begin
            vInTriangle := False;
            J := 0;
            vTr := vTriangles.List;
            while (J < vTriangles.Count) and not ACancel do
            begin
              vInTriangleLocal := RayCastTriangleIntersect(vStartPos4, vRayVect,
                vTr^[J], vTr^[J + 1], vTr^[J + 2], @vIntersect, @vIntersectNormal);
              vInTriangle := vInTriangle or vInTriangleLocal;
              Inc(J, 3);
              if vInTriangleLocal then
              begin
                if Assigned(AMeshObjects) then
                  AMeshObjects.Add(vMeshObject);
                if Assigned(APoints) then
                  APoints.Add(Vect2FPoint(vIntersect));
                if Assigned(ANormals) then
                  ANormals.Add(Vect2FPoint(vIntersectNormal));
              end;
            end;
            vResult := vResult or vInTriangle;
          end;
        finally
          vTriangles.Free;
        end;
      end;
    end;
    Inc(I);
  end;
  if Assigned(AMeshObjects) then
    Result := AMeshObjects.Count > 0
  else
    Result := vResult;
  Result := Result and not ACancel;
end;

function Tsg3DNavigator.GetMeshIntersect(X, Y: Integer;
  var AMeshObject: TMeshObject; AIntersect, ANormal: PFPoint;
  var ACancel: Boolean): Boolean;
var
  I, vMeshIndex: Integer;
  vDistance, vMin: Extended;
  vMeshs: TList;
  vIntersects, vNormals: TFPointList;
  vRayStart, vIntersect: TFPoint;
begin
  Result := False;
  AMeshObject := nil;
  vMeshs := TList.Create;
  try
    vIntersects := TFPointList.Create;
    vNormals := nil;
    if ANormal <> nil then
      vNormals := TFPointList.Create;
    GetMeshIntersects(X, Y, vMeshs, ACancel, vIntersects, vNormals);
    try
      vMin := MaxExtended;
      vMeshIndex := -1;
      I := 0;
      vRayStart := Vect2FPoint(Camera.AbsolutePosition);
      while (I < vMeshs.Count) and not ACancel do
      begin
        vIntersect := Vect2FPoint(GLFreeForm.LocalToAbsolute(FPoint2Vect(vIntersects[I])));
        vDistance := DistanceFPointSqr(vRayStart, vIntersect);
        if vDistance < vMin then
        begin
          vMin := vDistance;
          vMeshIndex := I;
        end;
        Inc(I);
      end;
      if (vMeshIndex >= 0) and not ACancel then
      begin
        AMeshObject := TMeshObject(vMeshs[vMeshIndex]);
        if AIntersect <> nil then
          AIntersect^ := vIntersects[vMeshIndex];
        if ANormal <> nil then
          ANormal^ := vNormals[vMeshIndex];
        Result := True;
      end;
    finally
      vIntersects.Free;
      vNormals.Free;
    end;
  finally
    vMeshs.Free;
  end;
end;

function Tsg3DNavigator.GetShowAxis: Boolean;
begin
  Result := FShowAxis;
end;

function Tsg3DNavigator.GetShowCubeExtents: Boolean;
begin
  Result := FShowCubeExtents;
end;

function Tsg3DNavigator.GetStyleCamera: TGLCameraStyle;
begin
  Result := FCameraStyle;
end;

function Tsg3DNavigator.GetTrianglesCount: Integer;
begin
  Result := FGLFreeForm.MeshObjects.TriangleCount;
end;

procedure Tsg3DNavigator.HideLights;
var
  I: Integer;
begin
  for I := 0 to LightsCount - 1 do
    Lights[I].Visible := False;
end;

procedure Tsg3DNavigator.HideScene;
begin
  HideLights;
  FGLDCScene.Visible := False;
  FGLDCTarget.Visible := False;
  Camera.Visible := False;
end;

procedure Tsg3DNavigator.InitSceneObject(AObject: TGLBaseSceneObject;
  const APosition, ADirection: TVector3f; const AOffs: Single);
begin
  AObject.BeginUpdate;
  try
    // Set (0,0,0) as scene center
    AObject.Position.AsAffineVector := APosition;
    // reset scene scale
    AObject.Scale.SetVector(1, 1, 1);
    // reset scene rotations
    AObject.ResetRotations;
    AObject.Direction.AsAffineVector := VectorNormalize(ADirection);
    if AOffs <> 0 then
      AObject.Move(AOffs);
    if AObject is TGLLightSource then
      if TGLLightSource(AObject).LightStyle = lsParallel then
        TGLLightSource(AObject).SpotDirection := AObject.Direction;
  finally
    AObject.EndUpdate;
  end;
end;

function Tsg3DNavigator.IsEmpty: Boolean;
begin
  // GLFreeForm.Count = 1
  //   - GLCubeExtents
  Result := (GLFreeForm.MeshObjects.Count = 0) and
    (GLFreeForm.Count <= 1);
end;

procedure Tsg3DNavigator.LoadFromFile(const AFileName: string;
  ADoClear: Boolean = True);
var
  vStream: TMemoryStream;
  vDir: string;
begin
  vDir := GetCurrentDir;
  try
    SetCurrentDir(ExtractFilePath(AFileName));
    vStream := TMemoryStream.Create;
    try
      vStream.LoadFromFile(AFileName);
      LoadFromStream(AFileName, vStream, ADoClear);
    finally
      vStream.Free;
    end;
  finally
    SetCurrentDir(vDir);
  end;
end;

procedure Tsg3DNavigator.LoadFromStream(const AFileName: string; Stream: TStream;
  ADoClear: Boolean = True);
begin
  if ADoClear then
    Clear;
  try
    FGLFreeForm.LoadFromStream(AFileName, Stream);
    DoAfterLoadScene;
  finally
    DoResetCamera;
  end;
end;

procedure Tsg3DNavigator.LoadSceneIdentity;
begin
  FGLDCScene.Matrix := IdentityHmgMatrix;
  FGLFreeForm.Matrix := IdentityHmgMatrix;
  SetSceneScale;
end;

procedure Tsg3DNavigator.PerformAutoCentering;
begin

end;

procedure Tsg3DNavigator.PositionLight(const ACenter: TVector3f;
  const ADistance: Single);
const
  cnstDistScale = 1;
begin
  // front, back
  InitSceneObject(Lights[0], ACenter, AffineVectorMake(0, 0, 1), ADistance * cnstDistScale);
  InitSceneObject(Lights[1], ACenter, AffineVectorMake(0, 0, -1), ADistance * cnstDistScale);
  // top, bottom
  InitSceneObject(Lights[2], ACenter, AffineVectorMake(0, 1, 0), ADistance * cnstDistScale);
  InitSceneObject(Lights[3], ACenter, AffineVectorMake(0, -1, 0), ADistance * cnstDistScale);
  // right, left
  InitSceneObject(Lights[4], ACenter, AffineVectorMake(1, 0.2, 0.1), ADistance * cnstDistScale);
  InitSceneObject(Lights[5], ACenter, AffineVectorMake(-1, 0.2, 0.1), ADistance * cnstDistScale);
  // near eyes
  InitSceneObject(Lights[6], ACenter, AffineVectorMake(-0.4, 0.3, 1), ADistance * cnstDistScale);
  InitSceneObject(Lights[7], ACenter, AffineVectorMake(0.4, 0.3, 1), ADistance * cnstDistScale);
end;

procedure Tsg3DNavigator.RenderAxis;
begin
  if FShowAxis then
  try
    Buffer.BeginUpdate;
    try
      FAxes.Render(FAxes.FGLDCAxes);
      FAxes.CopyToTexture(FGLAxisImage.Material.Texture);
    finally
      Buffer.EndUpdate;
    end;
  except
    FAxes.SetObjectsVisible(False);
    FGLAxisImage.Visible := False;
    FShowAxis := False;
  end;
end;

procedure Tsg3DNavigator.Rotate(Axis: TsgAxes; Angle: Extended);
var
  vRots: TVector3f;
  vCenter, vDeltaCenter: TVector3f;
begin
  FGLDCScene.BeginUpdate;
  try
    ZeroMemory(@vRots.V[0], Sizeof(vRots));
    case Axis of
      axisX: vRots.V[0] := -Angle;
      axisY: vRots.V[1] := -Angle;
      axisZ: vRots.V[2] := Angle;
    end;
    vCenter := VectorTransform(FCenter, FGLDCScene.Matrix);
    Translate(VectorNegate(FGLDCScene.Position.AsAffineVector));
    FGLCamera.RotateObject(FGLDCScene, vRots.V[0], vRots.V[1], vRots.V[2]);
    vDeltaCenter := VectorSubtract(vCenter,
      VectorTransform(FCenter, FGLDCScene.Matrix));
    Translate(vDeltaCenter);
  finally
    FGLDCScene.EndUpdate;
  end;
end;

procedure Tsg3DNavigator.ScenePositionChange(Sender: TObject);
begin
  if Assigned(FOnScenePositionChange) then
    FOnScenePositionChange(Self);
  UpdateArrows;
end;

procedure Tsg3DNavigator.SetAxisPos;
begin
  if FGLAxisImage = nil then Exit;
  if FShowAxis then
  begin
    FGLAxisImage.Position.SetPoint(FGLAxisImage.Width / 2,
      Max(Height - FGLAxisImage.Height / 2, cntAxisPosMinY), 0);
  end;
end;

procedure Tsg3DNavigator.SetBackgroundColor(const Value: TColor);
begin
  Buffer.BackgroundColor := Value;
end;

procedure Tsg3DNavigator.SetDefSceneMatrix;
begin
  FGLDCScene.Matrix := FGLDefSceneMat;
  SetSceneScale;
end;

procedure Tsg3DNavigator.SetSceneScale;
begin
//  if FGLFreeForm  <> nil then
//    FGLFreeForm.Scale.AsAffineVector := FSceneScale;
  if FGLCubeExtents <> nil then
    FGLCubeExtents.Scale.Assign(FGLFreeForm.Scale);
end;

procedure Tsg3DNavigator.SetShowAxis(const Value: Boolean);
begin
  if FAxes = nil then Exit;
  if Value <> FShowAxis then
  begin
    FShowAxis := Value;
    FAxes.SetObjectsVisible(Value);
    FGLAxisImage.Visible := Value;
    Repaint;
  end;
end;

procedure Tsg3DNavigator.SetShowCubeExtents(const Value: Boolean);
begin
  if Value <> FShowCubeExtents then
  begin
    FShowCubeExtents := Value;
    FGLCubeExtents.Visible := Value;
    Repaint;
  end;
end;

procedure Tsg3DNavigator.SetStyleCamera(const Value: TGLCameraStyle);
begin
  if Value <> FCameraStyle then
  begin
    case Value of
      csPerspective, csOrthogonal:
        Camera.CameraStyle := Value;
    else
      raise Esg3DNavError.Create(sCameraStyleIsNoSupported);
    end;
    FCameraStyle := Value;
    FAxes.FGLCamera.CameraStyle := Value;
    if FCameraStyle = csPerspective then
    begin
      FDefCameraScale := cntDefPerspCameraScale;
      FAxes.FDefCameraScale := cntDefPerspAxisCameraScale;
    end
    else
    begin
      FDefCameraScale := cntDefOrthCameraScale;
      FAxes.FDefCameraScale := cntDefPerspAxisCameraScale / 2;
    end;
    DoResetCamera;
  end;
end;

procedure Tsg3DNavigator.ShowLights;
var
  I: Integer;
begin
  for I := 0 to LightsCount - 1 do
    Lights[I].Visible := True;
end;

procedure Tsg3DNavigator.ShowScene;
begin
  FGLDCScene.Visible := True;
  ShowLights;
  FGLDCTarget.Visible := True;
  Camera.Visible := True;
end;

procedure Tsg3DNavigator.Translate(const AVector: TAffineVector);
begin
  {FGLDCTarget}FGLDCScene.Position.Translate(AVector);
end;

procedure Tsg3DNavigator.UpdateArrows;
var
  vM: TMatrix4f;
begin
  FAxes.GLDCAxes.BeginUpdate;
  try
    FAxes.GLDCAxes.ResetRotations;
    vM := FGLDCScene.Matrix;
    NormalizeMatrix(vM);
    FAxes.GLDCAxes.Matrix := vM;
    FAxes.Camera.RotateObject(FAxes.GLDCAxes, 0, -180, 0);
  finally
    FAxes.GLDCAxes.EndUpdate;
  end;
end;

procedure Tsg3DNavigator.WMPaint(var Message: TWMPaint);
begin
  RenderAxis;
  inherited;
end;

procedure Tsg3DNavigator.WMSize(var Message: TMessage);
begin
  inherited;
  SetAxisPos;
end;

{ Tsg3DDrawingNavigator }

//procedure Tsg3DDrawingNavigator.AddVertexes(const P1, P2, P3: TFPoint);
//begin
//  AddVertex(P1);
//  AddVertex(P2);
//  AddVertex(P3);
//end;

procedure Tsg3DDrawingNavigator.UpdateMaterialLibrary;
var
  I, J: Integer;
  vfg: TFGVertexIndexList;
  vMatLibItem: TGLLibMaterial;
  S: string;
begin
  case FDrawMode of
    dmNormal:
      begin
        for I := 0 to FNav3D.GLFreeForm.MeshObjects.Count - 1 do
          for J := 0 to FNav3D.GLFreeForm.MeshObjects[I].FaceGroups.Count - 1 do
          begin
            vfg := TFGVertexIndexList(FNav3D.GLFreeForm.MeshObjects[I].FaceGroups[J]);
            vfg.PrepareMaterialLibraryCache(FNav3D.FGLMaterialLibrary);
          end;
        if Assigned(FNav3D.FLineMass) then
        begin
          FNav3D.FLineMass.DestroyHandle;
          FNav3D.FLineMass.Material.MaterialLibrary := FNav3D.FGLMaterialLibrary;
        end;
      end;
  else
    vMatLibItem := AssignMaterial(clGray, FNav3D.FGLMaterialLibrary.Materials);
    for I := 0 to FNav3D.GLFreeForm.MeshObjects.Count - 1 do
      for J := 0 to FNav3D.GLFreeForm.MeshObjects[I].FaceGroups.Count - 1 do
      begin
        vfg := TFGVertexIndexList(FNav3D.GLFreeForm.MeshObjects[I].FaceGroups[J]);
        S := vfg.MaterialName;
        try
          vfg.MaterialName := vMatLibItem.Name;
          vfg.PrepareMaterialLibraryCache(FNav3D.FGLMaterialLibrary);
        finally
          vfg.MaterialName := S;
        end;
      end;
    if Assigned(FNav3D.FLineMass) then
    begin
      FNav3D.FLineMass.DestroyHandle;
      FNav3D.FLineMass.Material.MaterialLibrary := nil;
    end;
  end;
end;

procedure Tsg3DDrawingNavigator.UpdateVertexIndexList(
  const AVertexIndexList: TsgIntegerList);
var
  I: Integer;
begin
  if (FCurrMeshObject = nil) or (FCurrVertexes = nil) then Exit;
  if AVertexIndexList.Count > 0 then
  begin
    FCurrVertexes.VertexIndices.Clear;
    for I := 0 to AVertexIndexList.Count - 1 do
      FCurrVertexes.Add(AVertexIndexList[I]);
  end;
end;

function Tsg3DDrawingNavigator.AlterScale(Factor: Double; IsAbsolute: Boolean;
  Position: TPoint): Boolean;
var
  vRayStart, vRayVector, vRayStartNew: VectorGeometry.TVector;
begin
  if IsAbsolute then Factor := Factor / Scale;
  if FNav3D.CameraStyle = csOrthogonal then
  begin
    SetVector(vRayStart, ClientToWorld(Position.X, Position.Y));
    SetVector(vRayVector, FNav3D.Camera.AbsoluteVectorToTarget);
    NormalizeVector(vRayVector);
    FNav3D.Camera.SceneScale := FNav3D.Camera.SceneScale * Factor;
    SetVector(vRayStartNew, ClientToWorld(Position.X, Position.Y));
    VectorSubtract(vRayStartNew, vRayStart, vRayStartNew);
    Translate(AffineVectorMake(vRayStartNew));
  end
  else
  begin
    { TODO: AlterScale for CameraStyle = csPerspective }
    FNav3D.Camera.SceneScale := FNav3D.Camera.SceneScale * Factor;
    ScenePositionChange(Self);
  end;
  Result := True;
end;

procedure Tsg3DDrawingNavigator.ApplyShowingStyleToMaterial(AMaterial: TGLMaterial);
begin
  if FNav3D = nil then Exit;
  case FShowingStyle of
    ssSmoothShading, ssFlatShading:
      begin
        FNav3D.Buffer.Lighting := True;
        FNav3D.Buffer.ShadeModel := smSmooth;
        if FShowingStyle = ssFlatShading then
          FNav3D.Buffer.ShadeModel := smFlat;
        AMaterial.PolygonMode := pmFill;
        //AMaterial.BackProperties.PolygonMode := pmFill;
      end;
    ssHiddenLines, ssWireframe:
      begin
        FNav3D.Buffer.Lighting := False;
        FNav3D.Buffer.ShadeModel := smSmooth;
        AMaterial.PolygonMode := pmLines;
      end;
  end;
end;

procedure Tsg3DDrawingNavigator.AppShowingStyle;
begin
  ShowingStyle := FShowingStyle;
end;

procedure Tsg3DDrawingNavigator.AssignMaterialForMesh(const Mesh: TMeshObject;
  const AMaterial: TColor);
var
  I: Integer;
begin
  if Mesh = nil then Exit;
  for I := 0 to Mesh.FaceGroups.Count - 1 do
    (Mesh.FaceGroups[I] as TFGVertexIndexList).MaterialName :=
      AssignMaterial(AMaterial, FNav3D.FGLMaterialLibrary);
  //Mesh.Cl
end;

function Tsg3DDrawingNavigator.AddVertex(const Point: TFPoint;
  PNormal: PFPoint = nil): Integer;
begin
  if (FCurrMeshObject <> nil) and (FCurrVertexes <> nil) then
  begin
    Result := FCurrMeshObject.Vertices.Add(GetPointf(Point));
    FCurrVertexes.Add(Result);
    if PNormal <> nil then
      FCurrMeshObject.Normals.Add(FPoint2Vect(PNormal^))
  end
  else
    Result := -1;
end;

function Tsg3DDrawingNavigator.BeginUpdate: Integer;
begin
  FNav3D.Buffer.BeginUpdate;
  Result := FNav3D.Buffer.Updating;
  ImgLock;
end;

function Tsg3DDrawingNavigator.CheckCount(ALine: TsgGLPolyline;
  Count: Integer): Boolean;
begin
  if Count = 0 then
  begin
    Navigator3D.FLineMass.DeleteLastNode;
    Result := False;
  end
  else
    Result := True;
end;

function Tsg3DDrawingNavigator.CheckCurVertexes: Boolean;
begin
  Result := FCurrVertexes is TsgVertexList;
end;

procedure Tsg3DDrawingNavigator.Clear;
begin
  inherited Clear;
  if not Navigator3D.IsEmpty then
    Navigator3D.Clear;
  LoadFromImage := nil;
  FIsLoadedFromConv := False;
  FIsHasTriangledMesh := False;
  FRoot.Clean;
end;

function Tsg3DDrawingNavigator.ClientToWorld(const X, Y: Double; const Z: Double): TVector3f;
begin
  Result := FNav3D.ClientToWorld(X, Y, Z);
end;

function Tsg3DDrawingNavigator.ClientToWorld(const APosition: TF2DPoint): TVector3f;
begin
  Result := ClientToWorld(APosition.X, APosition.Y);
end;

function Tsg3DDrawingNavigator.ClientToWorld(const APosition: TFPoint): TVector3f;
begin
  Result := ClientToWorld(APosition.X, APosition.Y, APosition.Z);
end;

function Tsg3DDrawingNavigator.CoordinateConvertion(ACoordX, ACoordY: Integer;
  var APointInUCS: TFPoint): TFPoint;
begin
  if (ACoordX = FMove.X) and (ACoordY = FMove.Y) and Assigned(FIntersectCache) then
  begin
    Result := SubFPoint(FIntersectCache^, FBoxOffs);
    APointInUCS := Result;
  end
  else
    Result := GetRealPoint(ACoordX, ACoordY, APointInUCS);
end;

constructor Tsg3DDrawingNavigator.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FDrawMode := dmNormal;
  FMouseRotate := True;
  FNeedNormals := False;
  FIsHasTriangledMesh := False;
  FIsLoadedFromConv := False;
  FCurrMeshObject := nil;
  FCurrVertexes := nil;
  FVectorFile := nil;
  FIsBrepEntityIterate := False;
  FBoxOffs := cnstFPointZero;
  FHiddenLinesShader := TGLHiddenLineShader.Create(Self); //TsgHiddenLineShader.Create(Self);
  TGLHiddenLineShader(FHiddenLinesShader).Solid := True;
  FNav3D := Tsg3DNavigator.Create(Self);
  FNav3D.BorderWidth := 0;
  FNav3D.BevelEdges := [];
  FNav3D.BevelInner := bvNone;
  FNav3D.BevelOuter := bvNone;
  FNav3D.BevelKind := bkNone;
  FNav3D.Parent := Self;
  FNav3D.BringToFront;
  FNav3DWindProc := FNav3D.WindowProc;
  FNav3D.WindowProc := Nav3DWindProc;
  FNav3D.MouseCapture := False;
  FNav3D.BackgroundColor := Color;
  FNav3D.ShowAxis := True;
  FNav3D.ShowCubeExtents := False;
  FNav3D.OnScenePositionChange := ScenePositionChange;
  FNav3D.OnAfterRender := Navigator3DAfterRender;
  FNav3D.OnBeforeRender := Navigator3DBeforeRender;
  FNav3D.OnWrapUpRender := DoOnWrapUpRender;
  FNav3D.Align := alClient;
  DoOnCursorChanged;
//  ZoomInChange := 15.75 / 15;
//  ZoomOutChange := 15 / 15.75;
  FDefaultColor := clBlack;
  if Orbit3D is Tsg3DDrwNavOrbit3D then
  begin
    Tsg3DDrwNavOrbit3D(Orbit3D).DegreesPerPixel := 1 / 3;
    Tsg3DDrwNavOrbit3D(Orbit3D).OnVisibleChanging := OrbitVisibleChanging;
  end;
  ShowingStyle := ssSmoothShading;
  FDeviationCoefficient := cnstDeviationCoefficientDefault;
  ViewRectMode := True;

  FPickObjects := TsgPickObject.Create(Self);
  FPickObjects.Color := cnstPickColor;
  FPickObjects.OnPick := PickObjectEvent;
  FPickObjects.PickCached := True;
  FPickObjects.Enabled := True;
  FPickObjects.Lines.Material.MaterialLibrary := FNav3D.FGLMaterialLibrary;

  FHighLightObj := TsgPickObject.Create(Self);
  FHighLightObj.Color := cnstHighlightColor;
  FHighLightObj.OnPick := HighLightObjectEvent;
  FHighLightObj.Lines.ObjectStyle := FHighLightObj.Lines.ObjectStyle + [osDirectDraw];
  FHighLightObj.Lines.Material.MaterialLibrary := FNav3D.FGLMaterialLibrary;

  SetDefault3DSceneOptions;
  FSearchEntMesh := TsgMeshObject(TsgMeshObject.NewInstance);
  FRoot := TRoot.Create(nil);
end;

procedure Tsg3DDrawingNavigator.CreateGLSceneLine(var ALine: TsgGLPolyline;
  AColor: TColor; ALineWidth: Single);
begin
  ALine := Navigator3D.FLineMass.AddNode;
  ALine.LineWidth := ALineWidth;
  ALine.MaterialName := AssignMaterial(AColor, FNav3D.FGLMaterialLibrary);
end;

procedure Tsg3DDrawingNavigator.CreateGLScenePlane(var APlane: TGLPlane);
begin
  APlane := TGLPlane.CreateAsChild(FNav3D.FGLFreeForm);
  APlane.ShowAxes := False;
end;

function Tsg3DDrawingNavigator.CreateMeshObject(AOwner: TMeshObjectList;
  AEntity: TObject; AMode: TMeshObjectMode = momFaceGroups): TMeshObject;
var
  I, J: Integer;
  N, Root: TNodeLite;
  Insert: TsgDXFInsert;
  vInsertsTree: TList;
begin
  Result := TsgMeshObject.CreateWithEnt(AOwner, AEntity);
  //Result.Name := HexDisplayPrefix + IntToHex(TsgNativeUInt(AEntity), 0); //IntToHex(Integer(Result), 4);
  Result.Mode := AMode;
  Result.TexCoords.Clear;

  Insert := IterateParam.Insert;
  if Insert <> nil then
  begin
    vInsertsTree := TList.Create;
    try
      repeat
        vInsertsTree.Add(Insert);
        Insert := Insert.OwnerInsert;
      until Insert = nil;

      Insert := IterateParam.Insert;

      Root := TNodeLite(FRoot);
      N := nil;
      if vInsertsTree.Count > 1 then
      begin
        I := vInsertsTree.Count - 1;
        if Root.Find(vInsertsTree[I], J) then
        begin
          N := TNodeLite(Root[J]);
          Dec(I);
          while I >= 0 do
          begin
            if N.Find(vInsertsTree[I], J) then
              N := TNodeLite(N[J]);
            Dec(I);
          end;
        end;
        if N = Root then
          N := nil;
      end
      else
        if Root.Find(Insert, I) then
          N := TNodeLite(Root[I]);
      if N = nil then
        if TRoot(FRoot).FindDeep(Insert, N, J) then
          N := TNodeLite(N[J]);
      TsgMeshObject(Result).Node := N;
    finally
      vInsertsTree.Free;
    end;
  end;
end;

function Tsg3DDrawingNavigator.CreateSnapShotBitmap: Graphics.TBitmap;
var
  vGLImage: TGLImage;

  function GLImageCreate32BitsBitmap(GLImage: TGLImage): TGLBitmap;
  var
  {$IFDEF FPC}
    RIMG: TRawImage;
  {$ELSE}
    y, x, x4, w, h: Integer;
    vSrc, vDest: PAnsiChar;
  {$ENDIF}
  begin
    if GLImage.Blank then
    begin
      Result := nil;
      exit;
    end;
    GLImage.Narrow;
    Result := TGLBitmap.Create;
    Result.PixelFormat := glpf32bit;
    w := GLImage.Width;
    h := GLImage.Height;
    SetSizeGraphic(Result, w, h);
    if h > 0 then
    begin
  {$IFDEF FPC}
      RIMG.Init;
      rimg.Description.Init_BPP32_B8G8R8A8_BIO_TTB(Width, Height);
      rimg.Description.RedShift := 0;
      rimg.Description.BlueShift := 16;
      rimg.Description.LineOrder := riloBottomToTop;
      RIMG.DataSize := DataSize;
      rimg.Data := PByte(FData);
      { READ THIS PLEASE: !!!!!!!!!!!!  }
      { If you get a compile time error on the following line, you could either
        A) update your lazarus to >= 0.9.27-r18329, or
        B) comment the next line and un-comment the "Workaround for older Lazarus "
           part after the next line }

      result.LoadFromRawImage(rimg, false);

      { -- "Workaround for older Lazarus "
          LIntfImg:=TLazIntfImage.Create(rimg,False);
          try
            result.LoadFromIntfImage(LIntfImg);
          finally
            FreeAndNil(LIntfImg);
          end;
        -- End of "Workaround for older Lazarus " }
  {$ELSE}
      vSrc := @PAnsiChar(GLImage.Data)[w * 4 * (h - 1)];
      for y := 0 to h - 1 do
      begin
        vDest := Result.ScanLine[y];
        for x := 0 to w - 1 do
        begin
          x4 := x * 4;
          vDest[x4 + 0] := vSrc[x4 + 2];
          vDest[x4 + 1] := vSrc[x4 + 1];
          vDest[x4 + 2] := vSrc[x4 + 0];
          vDest[x4 + 3] := vSrc[x4 + 3];
        end;
        Dec(vSrc, w * 4);
      end;
  {$ENDIF}
    end;
  end;

begin
  if Assigned(FNav3D) then
  begin
    if FNav3D.FIsDirty > 0 then
      RepaintScene;
    SwapBuffers(FNav3D.RenderDC);
    try
      vGLImage := FNav3D.Buffer.CreateSnapShot;
      try
        Result := GLImageCreate32BitsBitmap(vGLImage);
      finally
        vGLImage.Free;
      end;
    finally
      SwapBuffers(FNav3D.RenderDC);
    end;
  end
  else
    Result := nil;
end;

destructor Tsg3DDrawingNavigator.Destroy;
begin
  Clear;
  FVectorFile.Free;
  if FNav3D <> nil then
    FNav3D.WindowProc := FNav3DWindProc;
  FSearchEntMesh.FreeInstance;
  DisposeAndNil(FIntersectCache);
  FPickObjects.Free;
  FHighLightObj.Free;
  FRoot.Free;
  inherited Destroy;
end;

procedure Tsg3DDrawingNavigator.DoNodes;

var
  P: PsgCADIterate;
  Root: TRoot;

  procedure AddEntNodes(AEnts: TsgDXFEntity; ANodes: TNodeLite; AIsBrep: Boolean);
  var
    I: Integer;
    N: TNodeLite;
    Ent: TsgDXFEntity;
  begin
    for I := 0 to AEnts.Count - 1 do
    begin
      Ent := AEnts[I];
      case Ent.EntType of
        ceInsert:
          begin
            if AIsBrep then
            begin
              N := TNodeGroup.Create(ANodes);
              TNodeGroup(N).Group := AEnts;
              N.Obj := Ent;
              ANodes.Add(N);
            end
            else
              N := ANodes.AddChild(Ent);
            AddEntNodes(TsgDXFInsert(Ent).Block, N, False);
          end;
        ceIges, ceStep, ceBrep:
          AddEntNodes(Ent, ANodes, True);
        ceRegion, ceBody, ce3DSolid:
          AddEntNodes(Ent, ANodes, True);
      end;
    end;
  end;

  procedure SortNodes(const ANode: TPersistentObjectList);
  var
    I: Integer;
  begin
    ANode.Sort(NodesCompare);
    for I := 0 to ANode.Count - 1 do
      SortNodes(TPersistentObjectList(ANode[I]));
  end;

begin
  FRoot.Clean;
  Root := TRoot(FRoot);
  P := InitializeIterateParams;
  Root.FMatrix := P^.Matrix;
  Root.Obj := FConverter.Sections[csEntities];
  AddEntNodes(FConverter.Sections[csEntities], Root, False);
  SortNodes(Root);
end;

procedure Tsg3DDrawingNavigator.DoOnColorChanged(const AColor: TColor);
begin
  FNav3D.BackgroundColor := Color;
end;

procedure Tsg3DDrawingNavigator.DoOnCursorChanged;
begin
  if Assigned(FNav3D) then
  begin
    Navigator3D.Cursor := Cursor;
    if Navigator3D.HandleAllocated then
      Navigator3D.Perform(WM_SETCURSOR, Navigator3D.WindowHandle, HTCLIENT);
  end
  else
    inherited DoOnCursorChanged;
end;

procedure Tsg3DDrawingNavigator.DoOnStopMouseOperations;
begin

end;

procedure NewFaceGroup(Param: Pointer); cdecl;
var
  vParams: PsgParamTriangle;
begin
  vParams := PsgParamTriangle(Param);
  Tsg3DDrawingNavigator(vParams^.ASelf).FCurrVertexes :=
    Tsg3DDrawingNavigator(vParams^.ASelf).GetFaceGroup(TMeshObject(vParams^.MeshPointer),
      fgmmTriangles, Tsg3DDrawingNavigator(vParams^.ASelf).FCurrColor, TFGVertexIndexList);
  vParams^.VertexPointer := Tsg3DDrawingNavigator(vParams^.ASelf).FCurrVertexes;
end;

{$IFNDEF SG_CALC_NORMALS}
function FindVector(AList: TAffineVectorList; const AVector: TAffineVector;
  var I: Integer): Boolean;
var
  vList: PAffineVectorArray;

  function _VectorEquals(const V1, V2: TAffineVector): Boolean;
  begin
    Result := IsZero(V1.X - V2.X) and IsZero(V1.Y - V2.Y) and IsZero(V1.Z - V2.Z);
  end;

begin
  Result := False;
  I := AList.Count - 1;
  vList := AList.List;
  while (I >= 0) and not Result do
    if _VectorEquals(vList^[I], AVector) then
      Inc(Result)
    else
      Dec(I);
end;
{$ENDIF}

function AddTrialgleVertex(AParam: PsgParamTriangle; const AVertex: TsgVertex): Integer;
var
  vVect: TAffineVector;
begin
  vVect := Tsg3DDrawingNavigator(AParam^.ASelf).GetPointf(AVertex);
{$IFNDEF SG_CALC_NORMALS}
  if not FindVector(TMeshObject(AParam^.MeshPointer).Vertices, vVect, Result) then
{$ENDIF}
    Result := TMeshObject(AParam^.MeshPointer).Vertices.Add(vVect);
  TFGVertexIndexList(AParam^.VertexPointer).Add(Result);
end;

procedure GetTriangle(DimTriangle: PsgDimTriangle; Param: Pointer); cdecl;
var
  vMesh: TMeshObject;
  I: Integer;
  vTriangle: PsgTriangle;
  vParams: PsgParamTriangle;
  vVertexes: TFGVertexIndexList;
  v3dNav: Tsg3DDrawingNavigator;
begin
  vParams := PsgParamTriangle(Param);
  v3dNav :=  Tsg3DDrawingNavigator(vParams^.ASelf);
  if not v3dNav.FIsHasTriangledMesh then
    v3dNav.FIsHasTriangledMesh := True;
  vVertexes := vParams^.VertexPointer;
  if vVertexes <> nil then
  begin
    vMesh := vParams^.MeshPointer;
    if vMesh <> nil then
    begin
      vTriangle := PsgTriangle(DimTriangle);
      Inc(PByte(vTriangle), SizeOf(DimTriangle^.CountTriangle));
      for I := 0 to DimTriangle^.CountTriangle - 1 do
      begin
        AddTrialgleVertex(vParams, vTriangle^.Corner1);
        AddTrialgleVertex(vParams, vTriangle^.Corner2);
        AddTrialgleVertex(vParams, vTriangle^.Corner3);
        Inc(vTriangle);
      end;
    end;
  end;
end;

procedure CallBackOuterLineDraw(AData: PsgTestLine); cdecl;
const
  cnstBadCan = 9000000000;
var
  v3dNav: Tsg3DDrawingNavigator;
  vPoint1, vPoint2: TFPoint;
  vPoint, vLastPoint: TAffineVector;

  function IsBadPoint(APoint: TFPoint): Boolean;
  begin
    Result := IsNan(APoint.X) or IsInfinite(APoint.X);
    Result := Result or IsNan(APoint.Y) or IsInfinite(APoint.Y);
    Result := Result or IsNan(APoint.Z) or IsInfinite(APoint.Z);
  end;

  procedure NewLine(A3DNav: Tsg3DDrawingNavigator; APoint1, APoint2: TFPoint);
  begin
    A3DNav.CreateGLSceneLine(A3DNav.FNav3D.FCurrentLine,
      A3DNav.FOuterWireColor, A3DNav.FOuterWireLineWieght);
    A3DNav.FNav3D.FCurrentLine.Add(FPoint2Vect(vPoint1));
    A3DNav.FNav3D.FCurrentLine.Add(FPoint2Vect(vPoint2));
  end;

begin
  vPoint1 := MakeFPoint(AData.Point1.X, AData.Point1.Y, AData.Point1.Z);
  vPoint2 := MakeFPoint(AData.Point2.X, AData.Point2.Y, AData.Point2.Z);
  if IsBadPoint(vPoint1) or IsBadPoint(vPoint2) then
    Exit;
  if IsRangeFPoints(vPoint1, cnstFPointZero, MakeFPoint(cnstBadCan, cnstBadCan, cnstBadCan)) or
    IsRangeFPoints(vPoint2, cnstFPointZero, MakeFPoint(cnstBadCan, cnstBadCan, cnstBadCan)) then
  begin
    v3dNav := Tsg3DDrawingNavigator(AData.Creator);
    vPoint1 := v3dNav.GetPoint(vPoint1, v3dNav.IterateParam);
    vPoint2 := v3dNav.GetPoint(vPoint2, v3dNav.IterateParam);

    if not Assigned(v3dNav.FNav3D.FCurrentLine) then
    begin
      NewLine(v3dNav, vPoint1, vPoint2);
    end
    else
    begin
      vLastPoint := v3dNav.FNav3D.FCurrentLine[v3dNav.FNav3D.FCurrentLine.Count-1];
      vPoint := FPoint2Vect(vPoint1);
      if VectorEquals(vLastPoint, vPoint) then
      begin
        v3dNav.FNav3D.FCurrentLine.Add(FPoint2Vect(vPoint2));
      end
      else
        NewLine(v3dNav, vPoint1, vPoint2);
    end;
  end;
end;

procedure Tsg3DDrawingNavigator.DrawBrepFile(Entity: TsgDXFEntity);
var
  vBrepEntity: TsgBrepGroup;
  v3dType: Integer;
  vParams: TsgParamTriangle;
  vInitData: TsgInitMeshBuilderData;
begin
  vBrepEntity := Entity as TsgBrepGroup;
  case vBrepEntity.EntType of
    ceIges: v3dType := 0;
    ceStep: v3dType := 1;
    ceBrep: v3dType := 2;
    else
      Exit;
  end;
  FNeedNormals := True;
  SetParamsByMeshBuilder(@vParams, @vInitData);
  vInitData.File3DType := v3dType;
  vInitData.File3DName := PAnsiChar(AnsiString(vBrepEntity.FileName));
  vInitData.IndexFaceMap := TsgBrepIndexFace(Entity).IndexFaceMap;
  vBrepEntity.GetMeshBrep(vInitData, FDeviationCoefficient);
  FIsBrepEntityIterate := True;
end;

procedure Tsg3DDrawingNavigator.Draw3dFace(Entity: TsgDXFEntity);
var
  OldCurrMeshObject: TMeshObject;
  OldCurrVertexes: TFGVertexIndexList;
begin
  OldCurrMeshObject := FCurrMeshObject;
  OldCurrVertexes := FCurrVertexes;
  try
    FCurrMeshObject := FCurrMeshObject3dFace;
    FCurrVertexes := FCurrVertexes3dFace;
    DrawBaseSolid(Entity);
  finally
    FCurrMeshObject := OldCurrMeshObject;
    FCurrVertexes := OldCurrVertexes;
  end;
end;

procedure Tsg3DDrawingNavigator.DrawBaseSolid(Entity: TsgDXFEntity);
var
  vPts: TsgPoints4;
  vSolid: TsgDXFSolidAccess;
begin
  vSolid := TsgDXFSolidAccess(Entity);

  vSolid.GetPoints(vPts);

  if vSolid.InternalFlags and $40 = 0 then
  begin
    AddVertex(vPts[0]);
    if vSolid.InternalFlags and $10 <> 0 then
    begin
      AddVertex(vPts[2]);
      AddVertex(vPts[1]);
    end
    else
    begin
      AddVertex(vPts[1]);
      AddVertex(vPts[2]);
    end;
  end;

  if vSolid.InternalFlags and $80 = 0 then
  begin
    AddVertex(vPts[0]);
    if vSolid.InternalFlags and $20 <> 0 then
    begin
      AddVertex(vPts[3]);
      AddVertex(vPts[2]);
    end
    else
    begin
      AddVertex(vPts[2]);
      AddVertex(vPts[3]);
    end;
  end;
end;

procedure Tsg3DDrawingNavigator.DrawACIS(Entity: TsgDXFEntity);
var
  vACISEntity: TsgDXFACISEntity;
  vParams: TsgParamTriangle;
  vInitData: TsgInitMeshBuilderData;
begin
  FIsBrepEntityIterate := True;
  if IsLoadMeshBuilderDll then
  begin
    FNeedNormals := True;
    SetParamsByMeshBuilder(@vParams, @vInitData);
    vInitData.IndexFaceMap := TsgDXFACISIndexFace(Entity).IndexFaceMap;
    vACISEntity := TsgDXFACISEntity(TsgDXFACISIndexFace(Entity).Owner);
    vACISEntity.GetMeshBrep(vInitData, FDeviationCoefficient);
  end
  else
  begin
    FreeAndNil(FCurrMeshObject);
  end;
end;

procedure Tsg3DDrawingNavigator.DrawDXFSurface(Entity: TsgDXFEntity);
begin

end;

procedure Tsg3DDrawingNavigator.DrawImage(Entity: TsgDXFEntity);
var
  vPlane: TGLPlane;
  vImageEnt: TsgDXFImageEnt absolute Entity;
  vAngle1, vAngle2: Double;
  vBitmap: TsgBMAdapter;
  vPoint: TFPoint;

begin
  if (vImageEnt.Picture = nil) or (vImageEnt.Picture.Graphic = nil) then
    Exit;

  CreateGLScenePlane(vPlane);

  TsgCADImageAccess(FLoadFromImage).GetAngles(vImageEnt, vAngle1, vAngle2);


  if IsZero(vAngle1) then
    vPoint := vImageEnt.Point
  else
    vPoint := vImageEnt.Point2;

  vPoint := AddFPoint(vPoint, MakeFPoint(vImageEnt.Width / 2, vImageEnt.Height / 2, 0));

  vPlane.Position.SetVector(GetPointf(vPoint));

  TsgCADImageAccess(FLoadFromImage).GetAngles(vImageEnt, vAngle1, vAngle2);

  vPlane.Width := vImageEnt.Width;
  vPlane.Height := vImageEnt.Height;


  vPlane.RollAngle := vAngle1;

  //vPlane.PitchAngle := -vAngle2;

  vPlane.Material.Texture.Disabled := False;

  vBitmap := TsgBMAdapter.Create;
  try
    vBitmap.Assign(vImageEnt.Picture.Graphic);
    vPlane.Material.Texture.Image.Assign(vBitmap.Bitmap);
    {$IFDEF SGDEL_XE2}
    if vImageEnt.Picture.Graphic is TPngImage then
    begin
      vPlane.Material.BlendingMode := bmTransparency;
      vPlane.Material.Texture.TextureMode := tmReplace;
    end;
    {$ENDIF}
  finally
    vBitmap.Free;
  end;
end;

procedure Tsg3DDrawingNavigator.DrawImageEnt(Entity: TsgDXFEntity);
var
  vImg: TsgDXFImageEnt absolute Entity;
begin
  if vImg.ClipPointsCount <= 2 then
    DrawImage(Entity);
end;

procedure Tsg3DDrawingNavigator.DrawLine(Entity: TsgDXFEntity);
var
  vLine: TsgDXFLine;
  vList: TFPointList;
begin
  vLine := TsgDXFLine(Entity);
  vList := TFPointList.Create;
  try
    vList.Add(vLine.Point);
    vList.Add(vLine.Point1);
    if vLine.ZThick = 0 then
      DrawPolyLineExt(vList, True, False)
    else
      DrawLineAndZTick(vLine, vLine.Point, vLine.Point1);
  finally
    vList.Free;
  end;
end;

procedure Tsg3DDrawingNavigator.DrawLineAndZTick(Line: TsgDXFLine; P1,
  P2: TFPoint);
var
  vList: TFPointList;
  vLine: TsgGLPolyline;
begin
  vList := TFPointList.Create;
  try
    vList.Add(P1);
    vList.Add(P2);
    vList.Add(MakeFPoint(P2.X, P2.Y, P2.Z + Line.ZThick));
    vList.Add(MakeFPoint(P1.X, P1.Y, P1.Z + Line.ZThick));
    CreateGLSceneLine(vLine, FCurrColor, FCurrLineWieght);
    PrepareData(vLine, vList, True, True);
  finally
    vList.Free;
  end;
end;

procedure Tsg3DDrawingNavigator.DrawPolyLine(Entity: TsgDXFEntity);
var
  I: Integer;
  vPolyLine: TsgCADBasePolyLineAccess;
  vDXFPolyLine: TsgDXFPolyLine;
  vPolyLineAccess: TsgDXFPolylineAccess;
  vVertexList: TFPointList;
  vVertexIndexList: TsgIntegerList;
begin
  vPolyLine := TsgCADBasePolyLineAccess(Entity);
  vPolyLineAccess := TsgDXFPolylineAccess(Entity);

  if Entity is TsgDXFPolyLine then
    vDXFPolyLine := TsgDXFPolyLine(Entity)
  else
    vDXFPolyLine := nil;

  vVertexList := TFPointList.Create;
  vVertexIndexList := TsgIntegerList.Create;
  try
    vVertexList.Count := 0;
    vVertexIndexList.Count := 0;

    if (vDXFPolyLine <> nil) and vPolyLine.IsPolygonMesh then
      vPolyLineAccess.GetVertexListForPolygonMesh(vDXFPolyLine.MeshM,
        vDXFPolyLine.MeshN, vVertexList, vVertexIndexList)
    else
    if vPolyLine.IsPolyZThickness then
      vPolyLineAccess.GetVertexListForPolygonMesh(2,
        vPolyLine.PolyPoints.Count shr 1, vVertexList, vVertexIndexList)
    else
      if (vDXFPolyLine <> nil) and vDXFPolyLine.IsPolyFaceMesh then
        vPolyLineAccess.GetVertexListForPolyFaceMesh(vVertexList)
      else
        DrawPolyLineExt(vPolyLine.PolyPoints, vPolyLine.Lines.IsSolid, vPolyLine.Closed);

     if vVertexList.Count > 0 then
     begin
       for I := 0 to vVertexList.Count - 1 do
         AddVertex(vVertexList[I]);
       if vVertexIndexList.Count > 0 then
         UpdateVertexIndexList(vVertexIndexList)
     end;
  finally
    vVertexList.Free;
    vVertexIndexList.Free;
  end;
end;

procedure Tsg3DDrawingNavigator.DrawPolyLineExt(Points: TFPointList; Solid,
  Closed: Boolean);
var
  vLine: TsgGLPolyline;
//  I: Integer;
//   vPolyline3d: TGLIonTrack;

begin

//        vPolyline3d := TGLIonTrack.CreateAsChild(FNav3D.FGLFreeForm);
//        vPolyline3d.ShowAxes := False;
//        //vPolyline3d.LineWidth := ALIneWidth;
//        //vPolyline3d.LineColor.AsWinColor := AColor;
//        //vPolyline3d.NodesAspect := lnaInvisible;
//        for I := 0 to Points.Count - 1 do
//          vPolyline3d.AddPoint(FPoint2Vect(GetPoint(Points[I], IterateParam)));
//        //DrawPolyLineExt(vPolylineList, True, False);


  CreateGLSceneLine(vLine, FCurrColor, FCurrLineWieght);
  PrepareData(vLine, Points, Solid, Closed);

//  CreateGLSceneLine(vLine, FCurrColor, FCurrLineWieght);
//  PrepareData(vLine, Points, Solid, Closed);
end;

procedure Tsg3DDrawingNavigator.DrawSolid(Entity: TsgDXFEntity);
begin
  DrawBaseSolid(Entity);
end;

procedure Tsg3DDrawingNavigator.DrawHatch(Entity: TsgDXFEntity);
var
  vPolygon: TsgCADPolyPolygon;
  I, J: Integer;
  vPolylineList: TFPointList;
  vList2D: TF2DPointList;
  vPoint: TFPoint;
begin
  vPolylineList := TFPointList.Create;
  vPolygon := TsgCADPolyPolygon(Entity);
  try
    for I := 0 to vPolygon.Boundaries.Count - 1 do
    begin
      vPolylineList.Count := 0;
      vList2D := vPolygon.Boundaries[I];
      for J := 0 to vList2D.Count - 1 do
      begin
        vPoint := MakeFPointFrom2D(vList2D.List[J]);
        vPolylineList.Add(vPoint);
      end;
      DrawPolyLineExt(vPolylineList, True, False);
    end;
  finally
    vPolylineList.Free;
  end;
end;

procedure Tsg3DDrawingNavigator.DrawText(Entity: TsgDXFEntity);
var
  vText: TsgDXFText;
  I, J, vMode: Integer;
  vPoint: PFPoint;

  vPolylineList: TFPointList;

  vList, vPolyline: TList;
begin
  vText := TsgDXFText(Entity);
  vList := TList.Create;
  vPolylineList := TFPointList.Create;
  try
    vMode := FLoadFromImage.Converter.GetTextPolylines(vText, vList);
    if vMode > 0 then
    begin
      for I := 0 to vList.Count - 1 do
      begin
        vPolylineList.Count := 0;
        vPolyline := vList.List[I];
        for J := 0 to vPolyline.Count - 1 do
        begin
          vPoint := vPolyline.List[J];
          vPolylineList.Add(vPoint^);
          Dispose(vPoint);
        end;
        vPolyline.Free;
        DrawPolyLineExt(vPolylineList, True, False);
      end;
    end;
  finally
    vList.Free;
    vPolylineList.Free;
  end;
end;

function Tsg3DDrawingNavigator.Empty: Boolean;
begin
  if FNav3D = nil then
    Result := True
  else
    Result := FNav3D.IsEmpty;
end;

function Tsg3DDrawingNavigator.EndUpdate: Integer;
begin
  if FNav3D.Buffer.Updating > 0 then
    FNav3D.Buffer.EndUpdate;
  Result := FNav3D.Buffer.Updating;
  ImgUnLock;
end;

function Tsg3DDrawingNavigator.EntToGLObj(AEnt: TObject;
  APIndex: PInteger = nil): TObject;
var
  I: Integer;
  vList: PPointerObjectList;
begin
  Result := nil;
  if APIndex = nil then APIndex := @I;
  FSearchEntMesh.SetEntity(AEnt);
  vList := FNav3D.GLFreeForm.MeshObjects.List;
  QFind(FNav3D.GLFreeForm.MeshObjects, True, dupIgnore, MeshEntCompare,
    FSearchEntMesh, APIndex^);
  if (APIndex^ >= 0) and (APIndex^ < FNav3D.GLFreeForm.MeshObjects.Count) then
    Result := vList^[APIndex^];
end;

procedure Tsg3DDrawingNavigator.FitToSize;
var
  vBox, vRect: TFRect;
  vDrawMatrix: TFMatrix;
  vV: TPoint;
  P: TFPoint;
begin
  GetView(vDrawMatrix);
  vBox := GetBox;
  vRect := GetRealBox(vBox, vDrawMatrix);
  vV := Point(FNav3D.Buffer.ViewPort.Width, FNav3D.Buffer.ViewPort.Height);
  vDrawMatrix := FMatScale(vDrawMatrix, vV.X / (vRect.Right - vRect.Left));
  vRect := GetRealBox(vBox, vDrawMatrix);
  if vRect.Top - vRect.Bottom > vV.Y then
  begin
    vDrawMatrix := FMatScale(vDrawMatrix, vV.Y / (vRect.Top - vRect.Bottom));
    vRect := GetRealBox(vBox, vDrawMatrix);
  end;
  vDrawMatrix.E0 := cnstFPointZero;
  P := MiddleFPoint(vBox.TopLeft, vBox.BottomRight);
  P := SubFPoint(P, FBoxOffs);
  vDrawMatrix.E0 := FPointXMat(P, vDrawMatrix);
  vDrawMatrix.E0.Z := 0.0;
  vDrawMatrix.E0 := SubFPoint(MakeFPoint(0.5 * vV.X, 0.5 * vV.Y), vDrawMatrix.E0);
  LoadView(vDrawMatrix);
end;

procedure Tsg3DDrawingNavigator.FinalMeshObject(var AMesh: TMeshObject; ANeedNormals: Boolean);
begin
  if AMesh <> nil then
  begin
    if AMesh.Vertices.Count = 0 then
      FreeAndNil(AMesh)
    else
      if ANeedNormals or (not CheckCurVertexes) then
        GenNormals(AMesh);
  end;
end;

function Tsg3DDrawingNavigator.FinishEntity(Entity: TsgDXFEntity): Integer;
begin
  if Entity is TsgDXFACISIndexFace then
    FIsBrepEntityIterate := False;
  if Entity is TsgBrepIndexFace then
    FIsBrepEntityIterate := False;
  if Entity.EntType = ceInsert then
  begin
    Load3dFaceMeshObject;
    FCurrMeshObject3dFace := nil;
  end;
  Result := 0;
end;

{$IFDEF SG_CALC_NORMALS}
procedure Tsg3DDrawingNavigator.FacesSmooth(aMeshObj: TMeshObject; aWeldDistance: Single=0.0000001; aThreshold: Single=35.0);
Var
  I, J, K, L: integer;
  WeldedVertex: TAffineVectorList;
  TmpIntegerList: TIntegerList;
  IndexMap: TStringList;
  v, n: TAffineVector;
  indicesMap : TIntegerList;
  Index: Integer;
  FaceList: TIntegerList;
  NormalList: TAffineVectorList;
  FaceNormalList: TAffineVectorList;
  FaceGroup: TFaceGroup;
  FG, FG1: TFGVertexIndexList;
  Threshold: Single;
  Angle: Single;
  ReferenceMap: TIntegerList;
  ID1, ID2: Integer;
  Index1, Index2, Index3: Integer;

  Tex1, Tex2, Tex3, Tex4: TTexPoint;
  List: TIntegerList;
  DupList: TStringList;

  vFaceCountDivTwoSubOne, vRefMapIndex1, vRefMapIndex2: Integer;

  function FindReferenceIndex(aID: Integer): Integer;
  begin
    Result := ReferenceMap.List^[aID];
  end;
  function iMin(a, b: Integer): Integer;
  begin
    if a<b then
      Result := a
    else
      Result := b;
  end;
  function iMax(a, b: Integer): Integer;
  begin
    if a>b then
      Result := a
    else
      Result := b;
  end;
begin
  aMeshObj.Normals.Count := aMeshObj.Vertices.Count;
  Threshold := aThreshold * Pi/180.0;
  //build the vectices reference map
  ReferenceMap := TIntegerList.Create;
  WeldedVertex := TAffineVectorList.Create;
  WeldedVertex.Assign(aMeshObj.Vertices);
  indicesMap := TIntegerList.Create;
  //first of all, weld the very closed vertices
  WeldVertices(WeldedVertex, indicesMap, aWeldDistance);
  //then, rebuild the map list
  IndexMap := TStringList.Create;
  for I:=0 to WeldedVertex.Count-1 do
  begin
    ReferenceMap.Assign(indicesMap);
    TmpIntegerList := TIntegerList.Create;
    Index := ReferenceMap.IndexOf(I);
    while Index>=0 do
    begin
      TmpIntegerList.Add(Index);
      ReferenceMap[Index] := -99999;
      Index := ReferenceMap.IndexOf(I);
    end;
    IndexMap.AddObject(IntToStr(I), TmpIntegerList);
  end;
  ReferenceMap.Assign(indicesMap);

  //never used these, free them all
  WeldedVertex.free;
  indicesMap.free;
  //create a TexPoint list for save face infomation, where s=facegroup index, t=face index
  FaceList := TIntegerList.Create;
  NormalList := TAffineVectorList.Create;
  FaceNormalList := TAffineVectorList.Create;
  //NormalIndex := TIntegerList.Create;

  for I:=0 to aMeshObj.FaceGroups.Count-1 do
  begin
    FaceGroup := aMeshObj.FaceGroups[I];
    TmpIntegerList := TFGVertexIndexList(FaceGroup).VertexIndices;
    for J:=0 to (TmpIntegerList.Count div 3)-1 do
    begin
      FaceList.Add(I);
      FaceList.Add(J);
      CalcPlaneNormal(aMeshObj.Vertices[TmpIntegerList[J * 3 + 0]],
        aMeshObj.Vertices[TmpIntegerList[J * 3 + 1]],
        aMeshObj.Vertices[TmpIntegerList[J * 3 + 2]],
        n);
      //add three normals for one trangle
      FaceNormalList.Add(n);
      NormalList.Add(n);
      NormalList.Add(n);
      NormalList.Add(n);
    end;
  end;

  //do smooth
  vFaceCountDivTwoSubOne := (FaceList.Count div 2)-1;
  for I:=0 to vFaceCountDivTwoSubOne do
  begin
    Index := FaceList.List^[I shl 1];
    Index1 := FaceList.List^[I shl 1 +1];
    FG := TFGVertexIndexList(aMeshObj.FaceGroups.List^[Index]);
    for J:=0 to 2 do
    begin
      vRefMapIndex1 := FG.VertexIndices.List^[Index1*3+J];
      ID1 := ReferenceMap.List^[vRefMapIndex1];
      for K:=I+1 to vFaceCountDivTwoSubOne do
      begin

        Index2 := FaceList.List^[K shl 1];
        Index3 := FaceList.List^[K shl 1+1];
        FG1 := TFGVertexIndexList(aMeshObj.FaceGroups.List^[Index2]);
        //if I<>K then
        begin
          for L:=0 to 2 do
          begin
            //two face contain the same vertex
            //vRefMapIndex1 := FG.VertexIndices.List^[Index1*3+J];
            vRefMapIndex2 := FG1.VertexIndices.List^[Index3*3+L];
            ID2 := ReferenceMap.List^[vRefMapIndex2];
            if ID1=ID2 then
            begin
              Angle := VectorDotProduct(FaceNormalList.List^[I],FaceNormalList.List^[K]);
              if angle>Threshold then
              begin
                NormalList.List^[I*3+J] := VectorAdd(NormalList.List^[I*3+J],FaceNormalList.List^[K]);
                NormalList.List^[K*3+L] := VectorAdd(NormalList.List^[K*3+L],FaceNormalList.List^[I]);
              end;
            end;
          end;
        end;
      end;
      NormalizeVector(NormalList.List^[I*3+J]);
    end;
  end;

  for I:=0 to (FaceList.Count div 2)-1 do
  begin
    Index := FaceList[I*2+0];
    FG := TFGVertexIndexList(aMeshObj.FaceGroups[Index]);
    Index := FaceList[I*2+1];
    aMeshObj.Normals[FG.VertexIndices[(Index*3+0)]] := VectorNegate(NormalList[(I*3+0)]);
    aMeshObj.Normals[FG.VertexIndices[(Index*3+1)]] := VectorNegate(NormalList[(I*3+1)]);
    aMeshObj.Normals[FG.VertexIndices[(Index*3+2)]] := VectorNegate(NormalList[(I*3+2)]);
  end;

  FaceList.free;
  NormalList.free;
  FaceNormalList.free;
  ReferenceMap.free;
  for I:=0 to IndexMap.Count-1 do
    IndexMap.Objects[I].free;
  IndexMap.free;
end;
{$ENDIF}

// only for trialngles, no fgmmQuads
procedure Tsg3DDrawingNavigator.GenNormals(AMesh: TMeshObject);
{$IFNDEF SG_CALC_NORMALS}
var
  I, J, C, K, L, N: Integer;
  vVert1, vVert2, vVert3, vNorm: TAffineVector;
  vList: TFGVertexIndexList;
  vIndxs: PIntegerArray;
  vVertxs: PAffineVectorArray;
{$ENDIF}
begin
  if AMesh.Normals.Count > 0 then Exit;
{$IFDEF SG_CALC_NORMALS}
  FacesSmooth(AMesh, 0.0000001, 65.0 );
{$ELSE}
  AMesh.Normals.Count := AMesh.Vertices.Count;
  vVertxs := PAffineVectorArray(AMesh.Vertices.List);
  for I := 0 to AMesh.FaceGroups.Count - 1 do
    if AMesh.FaceGroups[I] is TFGVertexIndexList then
    begin
      vList := TFGVertexIndexList(AMesh.FaceGroups[I]);
      N := vList.VertexIndices.Count;
      vIndxs := PIntegerArray(vList.VertexIndices.List);
      K := 3;
      for J := 0 to (N div K) - 1 do
      begin
        L := J * K;
        vVert1 := vVertxs^[vIndxs^[L]];
        vVert2 := vVertxs^[vIndxs^[L + 1]];
        vVert3 := vVertxs^[vIndxs^[L + 2]];
        CalcPlaneNormal(vVert1, vVert2, vVert3, vNorm);
        for C := 0 to K - 1 do
          AMesh.Normals.TranslateItem(vIndxs^[J * K + C], vNorm);
      end;
    end;
  AMesh.Normals.Normalize;
{$ENDIF}
end;

procedure Tsg3DDrawingNavigator.GetBoundingBox(var ABoundingBox: THmgBoundingBox);
var
  I: Integer;
  vBox: TFRect;
begin
  FNav3D.HandleNeeded;
  vBox := GetBox;
//    ABoundingBox := FNav3D.GLDCScene.BoundingBox...;
  for I := 0 to 7 do
    ABoundingBox.BBox[I] := VectorMake(vBox.V[I div 4].V[0], vBox.V[(I div 2) mod 2].V[1], vBox.V[I and 1].V[2]);
  for I := 0 to 7 do
    ABoundingBox.BBox[I] := VectorTransform(ABoundingBox.BBox[I], FNav3D.GLDCScene.Matrix);
end;

procedure Tsg3DDrawingNavigator.GetScreenAABB(var AAABB: TAABB);
var
  vglbox: THmgBoundingBox;
begin
  GetBoundingBox(vglbox);
  FNav3D.Buffer.WorldToScreen(@vglbox.BBox[0], 8);
  AAABB := BBToAABB(vglbox);
end;

function Tsg3DDrawingNavigator.GetActualGraphic: TGraphic;
var
  vGLGraphic: TsgGLGraphic;
begin
  SetActualGraphic(nil);
  vGLGraphic := TsgGLGraphic.Create;
  try
    vGLGraphic.HandleType := bmDIB;
    vGLGraphic.SetNavigator(Self);
    SetActualGraphic(vGLGraphic);
  except
    on E: Exception do
    begin
      MessageBox({$IFDEF CS_USEFORM}Application.Handle{$ELSE}0{$ENDIF},
        PChar(E.Message),
{$IFDEF SGABVIEWER}
        PChar(GetProgNameAndVer(True))
{$ELSE}
        nil
{$ENDIF},
        MB_APPLMODAL);
      vGLGraphic.Free;
      vGLGraphic := nil;
    end;
  end;
  Result := vGLGraphic;
end;

function Tsg3DDrawingNavigator.GetBox: TFRect;
var
  vAABB: TAABB;
begin
  if Assigned(FConverter) then
  begin
    Result := TsgDXFConverterAccess(FConverter).GetModelLayout.Box;
    OffsetFRect(Result, FBoxOffs.X, FBoxOffs.Y, FBoxOffs.Z);
  end
  else
  begin
    //FNav3D.GLFreeForm.GetExtents(vAABB.min, vAABB.max);
    vAABB := BBToAABB(FNav3D.GLFreeForm.BoundingBox);
    Result.TopLeft := Vect2FPoint(vAABB.min);
    Result.BottomRight := Vect2FPoint(vAABB.max);
    SwapDoubles(Result.Top, Result.Bottom);
  end;
end;

function Tsg3DDrawingNavigator.GetDrawingCoords(ACoordX, ACoordY: Integer;
  var AUnits: string): TFPoint;
begin
  Result := GetDrawingInternalCoords(ACoordX, ACoordY);
end;

function Tsg3DDrawingNavigator.GetDrawingInternalCoords(ACoordX,
  ACoordY: Integer): TFPoint;
var
  vTmp: TFPoint;
begin
  Result := CoordinateConvertion(ACoordX, ACoordY, vTmp);
end;

function Tsg3DDrawingNavigator.GetDrawingUCSCoords(ACoordX, ACoordY: Integer;
  var AUnits: string): TFPoint;
begin
  CoordinateConvertion(ACoordX, ACoordY, Result);
end;

type
  TTesIndexList = class(TFGVertexIndexList)
  public
    procedure BuildList(var mrci: TRenderContextInfo); override;
  end;

procedure TTesIndexList.BuildList(var mrci: TRenderContextInfo);
begin
  inherited
end;


function Tsg3DDrawingNavigator.GetEntity(Entity: TsgDXFEntity): Integer;
//var
//  I, J, K: Integer;
//  vEntityChildren: TsgDXFEntity;
//  vLine: TGLLines;
//  vCount, vCountSum: Integer;

 function GetVisibleEntity(AEntity: TsgDXFEntity): Boolean;
  var
    vInsert: TsgDXFInsert;
  begin
    Result := Entity.Visible and Entity.Visibility;
    vInsert := IterateParam.Insert;
    while Assigned(vInsert) and Result do
    begin
      Result := Result and vInsert.Visibility;
      vInsert := vInsert.OwnerInsert;
    end;
  end;

begin
  Result := 0;
  if FIsBrepEntityIterate then
    Exit;
  FNeedNormals := False;
  FNeedNormals3dFace := False;
  FCurrMeshObject := nil;
  FCurrVertexes := nil;
  FCurrColor := FDefaultColor;
  if not GetVisibleEntity(Entity) then  Exit;
  if (FNav3D = nil) or (FNav3D.GLFreeForm = nil) or (FVectorFile = nil) then Exit;
  FCurrColor := EntColor(Entity, IterateParam.Insert);
  if FCurrColor = clNone then
    FCurrColor := FDefaultColor;
  FCurrLineWieght := EntLineWeight(Entity, IterateParam.Insert);
  if FCurrLineWieght = 0 then
    FCurrLineWieght := 1;

  if Entity.EntType <> ce3dFace then
    FCurrMeshObject := CreateMeshObject(FNav3D.GLFreeForm.MeshObjects, Entity)
  else
    if (FCurrMeshObject3dFace = nil) or (FCurrColor3dFace <> FCurrColor) then
    begin
      Load3dFaceMeshObject;
      Create3DFaceMeshObject(Entity);
    end;

  case Entity.EntType of
    ceText:
      DrawText(Entity);
    ceLine:
      DrawLine(Entity);
    ce3dFace:
      Draw3dFace(Entity);
    ceSolid, ceTrace:
      begin
        FCurrVertexes := GetFaceGroup(FCurrMeshObject, fgmmTriangles, FCurrColor, TTesIndexList);
        DrawSolid(Entity);
      end;
    cePolyPolygon, ceGradient, ceGradientPolygon, ceCurvePolygon, ceHatch:
      begin
        DrawHatch(Entity);
      end;
    ceCircle, ceArc, ceEllipse:
      begin
        FCurrVertexes := GetFaceGroup(FCurrMeshObject, fgmmTriangles, FCurrColor,
          TsgVertexList);
        DrawPolyLine(Entity);
      end;
    ceLWPolyline, cePolyline, cePath:
      begin
        FCurrVertexes := GetFaceGroup(FCurrMeshObject, fgmmTriangles, FCurrColor,
          TFGVertexIndexList);
        DrawPolyLine(Entity);
      end;
    ceRegion, ceBody, ce3DSolid:
      if Entity is TsgDXFACISIndexFace then
        DrawACIS(Entity)
      else
        Result := 1;
    ceIges, ceStep, ceBrep:
      if Entity is TsgBrepIndexFace then
        DrawBrepFile(Entity)
      else
        Result := 1;
    ceSurface:
      DrawDXFSurface(Entity);
    ceImageEnt:
      DrawImageEnt(Entity);
  end;

  FinalMeshObject(FCurrMeshObject, FNeedNormals);
end;

type
  TFaceGroupClass = class of TFaceGroup;

function Tsg3DDrawingNavigator.GetFaceGroup(AMeshObject: TMeshObject; AMode: TFaceGroupMeshMode;
  const AMaterial: TColor; AFaceGroupClass: TClass): TFGVertexIndexList;
begin
  Result := nil;
  if AMeshObject = nil then Exit;
  Result := TFaceGroupClass(AFaceGroupClass).CreateOwned(AMeshObject.FaceGroups) as TFGVertexIndexList;
  Result.Mode := AMode;
  Result.MaterialName := AssignMaterial(AMaterial, FNav3D.FGLMaterialLibrary);
end;

function Tsg3DDrawingNavigator.GetHasTriangledMesh: Boolean;
begin
  Result := FIsHasTriangledMesh;
end;

function Tsg3DDrawingNavigator.GetHighLightObject: TObject;
begin
  Result := FHighLightObj.Obj;
end;

function Tsg3DDrawingNavigator.GetInsertMeshes(AInsert: TObject): TList;
var
  I: Integer;
  vMesh: TsgMeshObject;
  vNode: TCustomNode;
  vIsEqual: Boolean;
begin
  Result := TList.Create;
  for I := 0 to FNav3D.GLFreeForm.MeshObjects.Count - 1 do
  begin
    vIsEqual := False;
    vMesh := TsgMeshObject(FNav3D.GLFreeForm.MeshObjects[I]);
    vNode := vMesh.Node;
    while Assigned(vNode) and not vIsEqual do
    begin
      if vNode.Obj = AInsert then
        vIsEqual := True;
      vNode := vNode.Owner;
    end;
    if vIsEqual then
      if Result.IndexOf(vMesh) = -1 then
        Result.Add(vMesh);
  end;
end;

function Tsg3DDrawingNavigator.GetMaterialByName(AMaterialName: string): TGLMaterial;
begin
  Result := FNav3D.GetMaterialByName(AMaterialName);
end;

//procedure GetOuterLines(AData: PsgTestLine); cdecl;
//var
//  vList: TAffineVectorList;
//
//  function VertexToAffineVector(const V: TsgVertex): TAffineFltVector;
//  begin
//    Result[0] := V.X;
//    Result[1] := V.Y;
//    Result[2] := V.Z;
//  end;
//
//begin
//  vList := TAffineVectorList(AData.Creator);
//  vList.Add(VertexToAffineVector(AData^.Point1));
//  vList.Add(VertexToAffineVector(AData^.Point2));
//end;

function Tsg3DDrawingNavigator.GetMeshLines(AMesh: TMeshObject;
  AList: TFPointList; AMatrix: PFMatrix): Boolean;
var
  I: Integer;
//  vParams: TsgParamTriangle;
//  vInitData: TsgInitMeshBuilderData;
//  vACISEntity: TsgDXFACISEntity;
  vEnt: TsgDXFEntity;
  vBlock: TsgDXFBlock;
  v3dType: Integer;
  vMesh: TsgMeshObject;

//  procedure _SetParamsByMeshBuilder(
//    AParams: PsgParamTriangle; AInitData: PsgInitMeshBuilderData);
//  begin
//    AParams^.ASelf := Self;
//    AParams^.MeshPointer := AMesh;
//    AParams^.VertexPointer := nil;
//    ZeroMemory(AInitData, Sizeof(TsgInitMeshBuilderData));
//    AInitData^.TriangleFunc := @GetTriangle;
//    AInitData^.MeshParam := AParams;
//    AInitData^.Creator := AList;
//    AInitData^.DrawWireFunc := @GetOuterLines;
//  end;

  procedure DoPolylines(APoints: TFPointList; AClosed: Boolean);
  var
    J: Integer;
  begin
    if APoints.Count > 1 then
    begin
      J := 0;
      repeat
        AList.Add(APoints[J]);
        Inc(J);
        AList.Add(APoints[J]);
      until J >= APoints.Count - 1;
      if AClosed then
      begin
        AList.Add(APoints[J]);
        AList.Add(APoints[0]);
      end;
    end;
  end;

  function FlatPolyGroupFill(AFlatPoly3D: TsgFlatPoly3D): Boolean;
  var
    I, J, vCount, vIndex: Integer;
  begin
    vIndex := 0;
    for I := 0 to AFlatPoly3D.Counts.Count - 1 do
    begin
      vCount := AFlatPoly3D.Counts[I];
      if vCount > 2 then
      begin
        J := vIndex;
        while J < (vIndex + vCount-1) do
        begin
          AList.Add(AFlatPoly3D.XY[J]);
          Inc(J);
          AList.Add(AFlatPoly3D.XY[J]);
        end;
      end
      else
        if vCount = 2 then
        begin
          AList.Add(AFlatPoly3D.XY[vIndex]);
          AList.Add(AFlatPoly3D.XY[vIndex + 1]);
        end;
      Inc(vIndex, vCount);
    end;
    Result := True;
  end;

begin
  if {IsLoadMeshBuilderDll and} (AMesh is TsgMeshObject) then
  begin
    vMesh := TsgMeshObject(AMesh);
    vEnt := TsgDXFEntity(vMesh.Entity);
    if Assigned(vEnt) then
    begin
      v3dType := -1;
      case vEnt.EntType of
        ceIges: v3dType := 0;
        ceStep: v3dType := 1;
        ceBrep: v3dType := 2;
        ceRegion, ceBody, ce3DSolid:
          if vEnt is TsgDXFACISIndexFace then
            v3dType := 3;
      end;
      if AMatrix <> nil then
      begin
        if vMesh.Node <> nil then
          TNodeLite(vMesh.Node).GetMatrix(AMatrix^)
        else
          AMatrix^ := StdMat(cnstFPointSingle, FBoxOffs);
      end;
      if Assigned(AList) then
        if v3dType <> -1 then
        begin
          for I := 0 to vEnt.Count - 1 do
          begin
            case vEnt[I].EntType of
              ceFlatPoly3D: FlatPolyGroupFill(TsgFlatPoly3D(vEnt[I]));
            end;
          end;
//          FCurrMeshObject := AMesh;
//          _SetParamsByMeshBuilder(@vParams, @vInitData);
//          case v3dType of
//            0..2:
//              begin
//                vInitData.File3DType := v3dType;
//                vInitData.File3DName := PAnsiChar(AnsiString(TsgBrepGroup(vEnt).FileName));
//                vInitData.IndexFaceMap := TsgBrepIndexFace(vEnt).IndexFaceMap;
//                TsgBrepIndexFace(vEnt).GetMeshBrep(vInitData, FDeviationCoefficient);
//              end;
//          else
//            vInitData.IndexFaceMap := TsgDXFACISIndexFace(vEnt).IndexFaceMap;
//            vACISEntity := TsgDXFACISEntity(TsgDXFACISIndexFace(vEnt).Owner);
//            vACISEntity.GetMeshBrep(vInitData, FDeviationCoefficient);
//          end;
        end
        else
        begin
          if TsgMeshObject(AMesh).Insert <> nil then
          begin
            vBlock := TsgDXFInsert(vMesh.Insert).Block;
            for I := 0 to vBlock.Count - 1 do
            begin
              vEnt := vBlock[I];
              case vEnt.EntType of
//                ceSpline:
//                  DoPolylines(TsgDXFSpline(vEnt).FitPoints);
                ceLine:
                  begin
                    AList.Add(TsgDXFLine(vEnt).Point);
                    AList.Add(TsgDXFLine(vEnt).Point1);
                  end;
                cePolyline:
                  begin
                    DoPolylines(TsgDXFPolyline(vEnt).PolyPoints, TsgDXFPolyline(vEnt).Closed);
                  end;
              end;
            end;
          end
          else
            if vEnt.EntType = ce3dFace then
            begin
              AList.Add(TsgDXF3dFace(vEnt).Point);
              AList.Add(TsgDXF3dFace(vEnt).Point1);

              AList.Add(TsgDXF3dFace(vEnt).Point1);
              AList.Add(TsgDXF3dFace(vEnt).Point2);

              AList.Add(TsgDXF3dFace(vEnt).Point2);
              AList.Add(TsgDXF3dFace(vEnt).Point3);

              AList.Add(TsgDXF3dFace(vEnt).Point3);
              AList.Add(TsgDXF3dFace(vEnt).Point);
            end;
        end;
    end;
  end;
  Result := AList.Count > 0;
end;

function Tsg3DDrawingNavigator.GetMeshQuality: TsgMeshQuality;
begin
  if IsEqual(FDeviationCoefficient, cnstDeviationCoefficientDefault) then
    Result := mqLow
  else
    if IsEqual(FDeviationCoefficient, cnstDeviationCoefficientRealistic) then
      Result := mqHigh
    else
     Result := mqNormal;
end;

function Tsg3DDrawingNavigator.GetNavigator: Tsg3DNavigator;
begin
  Result := FNav3D;
end;

function Tsg3DDrawingNavigator.GetOrbit3DClass: TClass;
begin
  Result := Tsg3DDrwNavOrbit3D;
end;

function Tsg3DDrawingNavigator.GetPickObject: TObject;
begin
  Result := FPickObjects.Obj
end;

function Tsg3DDrawingNavigator.GetPoint(const Point: TFPoint;
  AParam: TsgCADIterate): TFPoint;
begin
  Result := FPointXMat(Point, AParam.Matrix);
end;

function Tsg3DDrawingNavigator.GetPointf(const APoint: TFPoint): TAffineVector;
begin
  TransformPointTo3f(APoint, IterateParam.Matrix, @Result.V[0]);
end;

function Tsg3DDrawingNavigator.GetRealPoint(ACoordX, ACoordY: Double;
  var APointInUCS: TFPoint): TFPoint;
begin
  if not (dnsMoving in State) and
     not Tsg3DDrwNavOrbit3D(Orbit3D).MouseCapture and not MouseCapture then
  begin
    FCoord := ClientToWorld(ACoordX, ACoordY);
    FCoordInUCS := VectorTransform(FCoord, FNav3D.GLDCScene.InvAbsoluteMatrix);

    FCoord.V[0] := FCoord.V[0] - FBoxOffs.X;
    FCoord.V[1] := FCoord.V[1] - FBoxOffs.Y;
    FCoord.V[2] := FCoord.V[2] - FBoxOffs.Z;

    FCoordInUCS.V[0] := FCoordInUCS.V[0] - FBoxOffs.X;
    FCoordInUCS.V[1] := FCoordInUCS.V[1] - FBoxOffs.Y;
    FCoordInUCS.V[2] := FCoordInUCS.V[2] - FBoxOffs.Z;
  end;
  Result := Vect2FPoint(FCoord);
  APointInUCS := Vect2FPoint(FCoordInUCS);
end;

function Tsg3DDrawingNavigator.GetShowHighlight: Boolean;
begin
  Result := FHighLightObj.Enabled
end;

function Tsg3DDrawingNavigator.GetShowingStyle: Tsg3DShowingStyle;
begin
  Result := FShowingStyle;
end;

function Tsg3DDrawingNavigator.GetSizesAsString: string;
var
  vTriangCount: Integer;
begin
  vTriangCount := FNav3D.TrianglesCount;
  Result := Format(STriangleCount, [vTriangCount]);
end;

procedure Tsg3DDrawingNavigator.GetView(var AMatrix: TFMatrix);
var
  vGLViewWidth{, vGLViewHeight, vGLViewDepth}: Double;
  vPos2D, vTranslate: TAffineFltVector;
  vAABB: TAABB;
  P: TFPoint;
  vScale: TFPoint;
  vMf4: TMatrix4f;
  vRect: TFRect;
begin
  vMf4 := FNav3D.GLDCScene.Matrix;
  NormalizeMatrix(vMf4);
  GLMatrixToMatrix(vMf4, AMatrix);
  GetScreenAABB(vAABB);
  vGLViewWidth := vAABB.max.V[0] - vAABB.min.V[0];
//    vGLViewHeight := vAABB.max[1] - vAABB.min[1];
//    vGLViewDepth := vAABB.max[2] - vAABB.min[2];
  if Assigned(FConverter) then
    TsgDXFConverterAccess(FConverter).GetModelLayout.SetRotMatrix(AMatrix);
  vRect := GetBox;
  TransRectCorners(vRect, AMatrix);
  vScale.X := vGLViewWidth / (vRect.Right - vRect.Left);
  vScale.Y := -vScale.X;
  vScale.Z := vScale.X;
  AMatrix := FMatScale(AMatrix, vScale);
  vTranslate := VectorTransform(FNav3D.FCenter, FNav3D.GLDCScene.Matrix);
  vPos2D := FNav3D.Buffer.WorldToScreen(vTranslate);
  vPos2D.V[1] := FNav3D.Buffer.Height - vPos2D.V[1];
  // additional scale and translate
  P := SubFPoint(Vect2FPoint(FNav3D.FCenter), FBoxOffs);
  P := FPointXMat(P, AMatrix);
  AMatrix.E0.X := vPos2D.V[0] - P.X;
  AMatrix.E0.Y := vPos2D.V[1] - P.Y;
  //  AMatrix.E0.Z := vPos2D[2] - P.Z;
end;

function Tsg3DDrawingNavigator.GetZoomInEnable: Boolean;
const
  cnstBigValue = 9000000;
var
  vAABB: TAABB;
  vWidth, vHeight: Double;
begin
  GetScreenAABB(vAABB);
  vWidth:= vAABB.max.V[0] - vAABB.min.V[0];
  vHeight := vAABB.max.V[1] - vAABB.min.V[1];
  Result := (vWidth < cnstBigValue) and (vHeight < cnstBigValue);
end;

function Tsg3DDrawingNavigator.GetZoomOutEnable: Boolean;
var
  vAABB: TAABB;
  vWidth, vHeight: Double;
begin
  GetScreenAABB(vAABB);
  vWidth:= vAABB.max.V[0] - vAABB.min.V[0];
  vHeight := vAABB.max.V[1] - vAABB.min.V[1];
  Result := (vWidth > iMinPicSize) and (vHeight > iMinPicSize);
end;

procedure Tsg3DDrawingNavigator.HighLightObjectEvent(Sender: TObject);
begin
  FHighLightObj.BuildLines;
  Navigator3DAfterRender(nil);
  RepaintScene;
  if Assigned(FOnHighLight) then
    FOnHighLight(Self);
end;

function Tsg3DDrawingNavigator.InitializeIterateParams: PsgCADIterate;
begin
  Result := inherited InitializeIterateParams;
  Result^.Matrix.E0 := FBoxOffs;
end;

function Tsg3DDrawingNavigator.IsCanBeEdited: Boolean;
begin
  Result := False;
end;

function Tsg3DDrawingNavigator.IsCanShowCoords: Boolean;
begin
  Result := True;
end;

function Tsg3DDrawingNavigator.IsCanShowSizes: Boolean;
begin
  Result := True;
end;

function Tsg3DDrawingNavigator.IsUseGraphic: Boolean;
begin
  Result := False;
end;

procedure Tsg3DDrawingNavigator.VisibleChanging;
begin
  inherited VisibleChanging;
  if not Visible then
  begin
    if Assigned(FLoadFromImage) and Assigned(FConverter) and FIsLoadedFromConv then
      LoadView(FLoadFromImage.DrawMatrix);
  end;
end;

procedure Tsg3DDrawingNavigator.WndProc(var Message: TMessage);
begin
  inherited WndProc(Message);
  if Message.Msg = WM_USER + 1 then
  begin
    FDrawMode := LoadFromImage.DrawMode;
    UpdateMaterialLibrary;
    RepaintScene;
  end;
end;

procedure Tsg3DDrawingNavigator.DoPictureChange(Sender: TObject);
var
  vImage: TsgCADImage;
begin
  if Assigned(FLoadFromImageChange) then
    FLoadFromImageChange(Sender);
  vImage := nil;
  if Sender is TsgCADImage then
    vImage := TsgCADImage(Sender)
  else
    if (Sender is TPicture) and (TPicture(Sender).Graphic is TsgCADImage) then
      vImage := TsgCADImage(TPicture(Sender).Graphic);
  if ControlTool.Img <> vImage then
    ControlTool.UpdateImg;
  if Assigned(vImage) then
  begin
    Color := vImage.BackgroundColor;
    if FDrawMode <> vImage.DrawMode then
    begin
      FDrawMode := vImage.DrawMode;
      UpdateMaterialLibrary;
      FNav3D.Invalidate;
    end;
  end
  else
  begin
    LoadFromImage := nil;
    FConverter := nil;
  end;
end;

procedure Tsg3DDrawingNavigator.LoadView(const AMatrix: TFMatrix);
var
  I: Integer;
  vGLViewWidth, vGLViewHeight, {vGLViewDepth,} vSceneScale, vdW, vdH{, vdZ}: Double;
  vPos2D, vTranslate: TAffineFltVector;
  vScale: TAffineFltVector;
  vfM: TMatrix4f;
  vBox, vRect: TFRect;
  vBoxCenter, vRectCenter: TFPoint;
  vRC: TGLContext;
  vAABB: TAABB;
  vBounds: THmgBoundingBox;
  vDPI: Integer;
  vPixelsPerInch: TPoint;
  vMatrix: TFMatrix;
begin
  FNav3D.HandleNeeded;
  vBox := GetBox;
  vRect := vBox;
  vMatrix := FMatScale(AMatrix, MakeFPoint(1, -1, 1));

  OffsetFRect(vRect, -FBoxOffs.X, -FBoxOffs.Y, -FBoxOffs.Z);
  TransRectCorners(vRect, vMatrix);
  vRectCenter := MiddleFPoint(vRect.TopLeft, vRect.BottomRight);

  vBoxCenter := MiddleFPoint(vBox.TopLeft, vBox.BottomRight);
  FNav3D.FCenter := FPoint2Vect(vBoxCenter);
  // get rotmatrix
  MatrixToGLMatrix(vMatrix, vfM);
  NormalizeMatrix(vfM);
  FNav3D.GLDCScene.BeginUpdate;
  Lock.Enter;
  try
    FNav3D.Camera.BeginUpdate;
    try
      FNav3D.FAxes.ResetRotations;
      vDPI := TGLSceneBufferAccess(FNav3D.Buffer).RenderDPI;
      if vDPI = 0 then
      begin
        vPixelsPerInch := sgFunction.GetLogPixelsPerInch;
        vDPI := MaxI(vPixelsPerInch.X, vPixelsPerInch.Y);
        if vDPI = 0 then
          vDPI := 96;
      end;

      FNav3D.InitSceneObject(FNav3D.GLDCScene,
        VectorTransform(VectorNegate(FNav3D.FCenter), vfM), ZVector, 0);
      FNav3D.GLDCScene.CubeSize :=
        Sqrt(DistanceFPointSqr(vBoxCenter, vBox.TopLeft) / 0.75);

      FNav3D.Camera.SceneScale := 1;
      FNav3D.Camera.DepthOfView := 10 * FNav3D.GLDCScene.BoundingSphereRadius;
      FNav3D.InitSceneObject(FNav3D.Camera, NullVector, ZVector, 3 * FNav3D.GLDCScene.CubeSize);

      vRC := CurrentGLContext;

      if not Assigned(vRC) then
        FNav3D.Buffer.RenderingContext.Activate;

      try
        FNav3D.Buffer.RenderingContext.PipelineTransformation.IdentityAll;
        FNav3D.Camera.ApplyPerspective(FNav3D.Buffer.ViewPort, FNav3D.Buffer.ViewPort.Width,
          FNav3D.Buffer.ViewPort.Height, vDPI);
      finally
        if not Assigned(vRC) then
          FNav3D.Buffer.RenderingContext.Deactivate;
      end;

      // rotate scene
      FNav3D.GLDCScene.Matrix := MatrixMultiply(vfM, FNav3D.GLDCScene.Matrix);
      FNav3D.FAxes.FGLDCAxes.Matrix := MatrixMultiply(vfM, FNav3D.FAxes.FGLDCAxes.Matrix);
      // calculate SceneScale
      GetBoundingBox(vBounds);
      if FNav3D.CameraStyle = csOrthogonal then
        GetScreenAABB(vAABB)
      else
      begin
        FNav3D.Buffer.WorldToScreen(@vBounds.BBox[0], 8);
        for I := 0 to 7 do
          vBounds.BBox[I] := FNav3D.Buffer.ScreenToVector(vBounds.BBox[I]);
        vAABB := BBToAABB(vBounds);
      end;
      vGLViewWidth := vAABB.max.V[0] - vAABB.min.V[0];
      vGLViewHeight := vAABB.max.V[1] - vAABB.min.V[1];
  //      vGLViewDepth := vAABB.max[2] - vAABB.min[2];
      vScale.V[0] := (vRect.Right - vRect.Left) / vGLViewWidth;
      vScale.V[1] := (vRect.Top - vRect.Bottom) / vGLViewHeight;
  //      vScale[2] := (vRect.Z2 - vRect.Z1) / vGLViewDepth;
      vSceneScale := Max(vScale.V[0], vScale.V[1]);
      vdW := vGLViewWidth * vSceneScale;
      if vdW > (vRect.Right - vRect.Left) then
        vSceneScale := vSceneScale * (vRect.Right - vRect.Left) / vdW;
      vdH := vGLViewHeight * vSceneScale;
      if vdH > (vRect.Top - vRect.Bottom) then
        vSceneScale := vSceneScale * (vRect.Top - vRect.Bottom) / vdH;
  //      vdZ := vGLViewDepth * vSceneScale;
  //      if vdZ > (vRect.Z2 - vRect.Z1) then
  //        vSceneScale := vSceneScale * (vRect.Z2 - vRect.Z1) / vdZ;

      { TODO: calc vSceneScale for CameraStyle = csPerspective in LoadView method }
      if FNav3D.CameraStyle <> csOrthogonal then
        vSceneScale := vSceneScale * FNav3D.GLDCScene.BoundingSphereRadius / 16;
      FNav3D.Camera.SceneScale := vSceneScale;
      // translate scene to glscene to viewrect center
      vPos2D := FPoint2Vect(vRectCenter);
      vPos2D.V[1] := FNav3D.Buffer.ViewPort.Height + vPos2D.V[1];
      vPos2D.V[2] := 0;
      vTranslate := FNav3D.Buffer.ScreenToWorld(vPos2D);
      vTranslate.V[2] := 0;
      FNav3D.Translate(VectorScale(vTranslate, 1 / vSceneScale));
    finally
      FNav3D.Camera.EndUpdate;
    end;
  finally
    FNav3D.GLDCScene.EndUpdate;
    Lock.Leave;
  end;
end;

procedure Tsg3DDrawingNavigator.Create3DFaceMeshObject(AEntity: TsgDXFEntity);
begin
  FCurrColor3dFace := FCurrColor;
  FCurrMeshObject3dFace := CreateMeshObject(FNav3D.GLFreeForm.MeshObjects, AEntity);
  FCurrVertexes3dFace := GetFaceGroup(FCurrMeshObject3dFace, fgmmTriangles, FCurrColor,
      TFGVertexIndexList);
end;

procedure Tsg3DDrawingNavigator.Load3dFaceMeshObject;
var
  OldCurrMeshObject: TMeshObject;
  OldCurrVertexes: TFGVertexIndexList;
begin
  if (FCurrMeshObject3dFace <> nil) and (FCurrVertexes3dFace <> nil) then
  begin
    OldCurrMeshObject := FCurrMeshObject;
    OldCurrVertexes := FCurrVertexes;
    try
      FCurrVertexes := FCurrVertexes3dFace;
      FCurrMeshObject := FCurrMeshObject3dFace;
      FinalMeshObject(FCurrMeshObject, FNeedNormals3dFace);
    finally
      FCurrVertexes := OldCurrVertexes;
      FCurrMeshObject := OldCurrMeshObject;
    end;
  end;
end;

procedure Tsg3DDrawingNavigator.LoadFromConverter(const AConverter: TsgDXFConverter;
  const ACADImage: TsgCADImage);
var
  vGLForm: TGLBaseMeshAccess;//TGLFreeForm;
  vBox: TFRect;
{$IFDEF CHECK_TIME}
  TicksLoadGLScene: Cardinal;
{$ENDIF}
begin
  if FNav3D = nil then Exit;
  LoadFromImage := ACADImage;
  FNav3D.Clear;
  FRoot.Clean;
  vGLForm := TGLBaseMeshAccess(FNav3D.GLFreeForm);
  if vGLForm = nil then Exit;
  FConverter := AConverter;
  FreeAndNil(FVectorFile);
  FVectorFile := TVectorFile.Create(vGLForm);
  try
    vGLForm.PrepareVectorFile(FVectorFile);
    if Assigned(vGLForm.Scene) then vGLForm.Scene.BeginUpdate;
    try
      Navigator3D.FLineMass := TsgGLLines.CreateAsChild(FNav3D.GLFreeForm);
      try
        Navigator3D.FLineMass.Material.MaterialLibrary := Navigator3D.FGLMaterialLibrary;
        vBox := TsgDXFConverterAccess(AConverter).GetModelLayout.Box;
        FBoxOffs := MakeFPoint(-vBox.Left, -vBox.Bottom, -vBox.Z1);
        DoNodes;
{$IFDEF CHECK_TIME}
    TicksLoadGLScene := GetTickCount;
{$ENDIF}
        inherited LoadFromConverter(AConverter, ACADImage);
{$IFDEF CHECK_TIME}
    OutputDebugString(PChar(Format('Loading GLScene time is %s mlsek',
      [FloatToStr(GetTickCount - TicksLoadGLScene)])));
{$ENDIF}
      finally
        if Navigator3D.FLineMass.FPolylines.Count = 0 then
        begin
          FNav3D.GLFreeForm.Remove(Navigator3D.FLineMass, False);
          Navigator3D.FLineMass.Free;
          Navigator3D.FLineMass := nil;
        end;
      end;
    finally
      if Assigned(vGLForm.Scene) then vGLForm.Scene.EndUpdate;
    end;
    Load3dFaceMeshObject;
    ResetDefaults;
    //vGLForm.PerformAutoScaling;
    //vGLForm.PerformAutoCentering;
    vGLForm.PrepareMesh;
    FNav3D.GLFreeForm.MeshObjects.Sort(MeshEntCompare);
    FNav3D.DoAfterLoadScene;
    FIsLoadedFromConv := True;
    AppShowingStyle;
    LoadView(ACADImage.DrawMatrix);
    PostMessage(Handle, WM_USER + 1, 0, 0);
  finally
    FreeAndNil(FVectorFile);
  end;
end;

procedure Tsg3DDrawingNavigator.LoadFromFile(const FileName: string);
begin
  if Assigned(FNav3D) then
    FNav3D.LoadFromFile(FileName);
  FIsLoadedFromConv := False;
  AppShowingStyle;
end;

procedure Tsg3DDrawingNavigator.DoOnWrapUpRender(Sender: TObject; var rci: TRenderContextInfo);
var
  vGLCanvas: TGLCanvas;
  vSprite: TGLHUDSprite;

  procedure ApplyMat(AMaterial: TGLMaterial);
  begin
    AMaterial.Texture.ImageClassName := TGLBlankImage.ClassName;
    AMaterial.Texture.MagFilter := maNearest;
    AMaterial.Texture.MinFilter := miNearest;
    AMaterial.Texture.Compression := tcNone;
    AMaterial.BlendingMode := bmTransparency;
    AMaterial.Texture.ImageAlpha := tiaTopLeftPointColorTransparent;
    AMaterial.Texture.TextureMode := tmReplace;
    AMaterial.Texture.Disabled := False;
  end;

  procedure RenderSysMenuIcon(ASprite: TGLHUDSprite; ASysMenuIcon: TsgSysMenuIcon);
  var
    R: TRect;
    Icon: TBitmap;
  begin
    if SysMenuIcons[ASysMenuIcon] <> nil then
    begin
      Icon := TBitmap.Create;
      try
        Icon.PixelFormat := pf24bit;
        SetSizeGraphic(Icon, SysMenuIcons[ASysMenuIcon]);
        R := Rect(0, 0, Icon.Width, Icon.Height);
        Icon.Canvas.StretchDraw(R, SysMenuIcons[ASysMenuIcon]);
        ASprite.Material.Texture.Image.Assign(Icon);
      finally
        Icon.Free;
      end;
      ASprite.Width := ASprite.Material.Texture.Image.Width;
      ASprite.Height := ASprite.Material.Texture.Image.Height;
      R := SysMenuIcons.GetSysMenuIconRect(ASysMenuIcon);
      OffsetRect(R, (R.Right - R.Left) div 2, (R.Bottom - R.Top) div 2);
      ASprite.Position.AsAffineVector := AffineVectorMake(R.Left, R.Top, 0);
      ASprite.Render(rci);
    end;
  end;

var
  vNeedRenderSysMenuIcons: Boolean;
{$IFDEF CS_USEFORM}
  Form: TCustomForm;
{$ENDIF}
begin
  if Orbit3D.Visible then
    Orbit3D.Paint;
  vNeedRenderSysMenuIcons := SysMenuIconsVisible and not FLockPaintSysMenuIcons;
{$IFDEF CS_USEFORM}
  if vNeedRenderSysMenuIcons then
  begin
    Form := GetParentForm(Self);
    if Assigned(Form) {$IFNDEF SG_MDI}and (TForm(Form).FormStyle = fsMDIChild) {$ENDIF}then
      if Form.WindowState <> wsMaximized then
        vNeedRenderSysMenuIcons := False;
  end;
{$ENDIF}
  if vNeedRenderSysMenuIcons then
  begin
    vGLCanvas := TGLCanvas.Create(Navigator3D.Buffer.Width, Navigator3D.Buffer.Height);
    try
      vSprite := TGLHUDSprite.Create(nil);
      try
        vGLCanvas.InvertYAxis;
        vSprite.AlphaChannel := 0;
        vSprite.Visible := True;
        ApplyMat(vSprite.Material);

        RenderSysMenuIcon(vSprite, sgTools.smiMinimize);
        RenderSysMenuIcon(vSprite, sgTools.smiRestore);
        RenderSysMenuIcon(vSprite, sgTools.smiClose);
      finally
        vSprite.Free;
      end;
    finally
      vGLCanvas.Free;
    end;
  end;
end;

function Tsg3DDrawingNavigator.FindMesh(X, Y: Integer): TMeshObject;
var
  vCancel: Boolean;
begin
  vCancel := False;
  if FIntersectCache = nil then
    New(FIntersectCache);
  if not FNav3D.GetMeshIntersect(X, Y, Result, FIntersectCache, nil, vCancel) then
    DisposeAndNil(FIntersectCache);
end;

procedure Tsg3DDrawingNavigator.MouseDown(Button: Controls.TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  inherited MouseDown(Button, Shift, X, Y);
end;

procedure Tsg3DDrawingNavigator.SetPickObject(APickObject: TObject);
begin
  FPickObjects.Obj := APickObject;
end;

procedure Tsg3DDrawingNavigator.SetHighLightObject(AHighLightObject: TObject);
begin
  FHighLightObj.Obj := AHighLightObject;
end;

procedure Tsg3DDrawingNavigator.SetLoadFromImage(const Value: TsgCADImage);
begin
  if FLoadFromImage <> Value then
  begin
    if Assigned(FLoadFromImage) then
    begin
      FLoadFromImage.OnChange := FLoadFromImageChange;
      FLoadFromImageChange := nil;
    end;
    FLoadFromImage := Value;
    if Assigned(FLoadFromImage) then
    begin
      FLoadFromImageChange := FLoadFromImage.OnChange;
      FLoadFromImage.OnChange := DoPictureChange;
    end;
    FDrawMode := dmNormal;
  end;
end;

procedure Tsg3DDrawingNavigator.MouseMove(Shift: TShiftState; X, Y: Integer);
begin
  if AppActive and not Orbit3D.Visible then
  begin

    if FMouseRotate and (ssLeft in Shift) and (DistancePoint(FMove, Point(X, Y)) >= 3) then
    begin
      FMove := Point(X, Y);
      if not Tsg3DDrwNavOrbit3D(Orbit3D).MouseCapture then
      begin
        HighLightObject := nil;
        FHighLightObj.Pos := FMove;
        Orbit3D.BoundsRect := ClientRect;
        Tsg3DDrwNavOrbit3D(Orbit3D).MouseDown(TMouseButton(mbLeft), Shift, X, Y);
        Cursor := Orbit3D.Cursor;
      end;
    end;

    if not Tsg3DDrwNavOrbit3D(Orbit3D).MouseCapture then
    begin
      if ShowHighlight and ((FMove.X <> X) or (FMove.Y <> Y)) then
      begin
        FMove := Point(X, Y);
        HighLightObject := FindMesh(X, Y);
        FHighLightObj.Pos := FMove;
      end;
      inherited MouseMove(Shift, X, Y);
    end;

  end
  else
    inherited MouseMove(Shift, X, Y);
end;

procedure Tsg3DDrawingNavigator.MouseUp(Button: Controls.TMouseButton;
  Shift: TShiftState; X, Y: Integer);
var
  vSamePos: Boolean;
  vList: TList;

  procedure DoSelect(AList: TList);
  begin
    SetSelected(AList);
    if AList.Count > 0 then
      FPickObjects.Tag := TObject(AList[0])
    else
      if AList.Count = 0 then
        FPickObjects.Tag := nil;
    FPickObjects.DoPick;
  end;

  procedure DoSetPick(APick: TsgMeshObject);
  var
    vEnabled: Boolean;
  begin
    vEnabled := FPickObjects.Enabled;
    try
      FPickObjects.Enabled := False;
      PickObject := APick;
      FPickObjects.Pos := Point(X, Y);
      if HighLightObject <> nil then
        FPickObjects.TopNode := TsgMeshObject(APick).Node
      else
        FPickObjects.TopNode := nil;
    finally
      FPickObjects.Enabled := vEnabled;
    end;
    FPickObjects.DoPick;
  end;

begin
  if not Orbit3D.Visible and (Button = TMouseButton(mbLeft)) then
  begin
    if FPickObjects.Enabled then
    begin
      vSamePos := (FHighLightObj.Pos.X = X) and (FHighLightObj.Pos.Y = Y);
      if (PickObject <> HighLightObject) and (HighLightObject <> nil) and vSamePos then
      begin
        DoSetPick(TsgMeshObject(HighLightObject));
      end
      else
        if (FPickObjects.Pos.X <> X) or (FPickObjects.Pos.Y <> Y) then
        begin
          DoSetPick(TsgMeshObject(FindMesh(X, Y)));
        end
        else
          if Assigned(FPickObjects.TopNode) then
          begin
            vList := TList.Create;
            try
              if FPickObjects.TopNode.Owner <> FRoot then
              begin
                FPickObjects.TopNode := FPickObjects.TopNode.Owner;
                vList.Add(FPickObjects.TopNode.Obj);
                DoSelect(vList);
              end
              else
              begin
                if FPickObjects.TopNode.ClassType = TNodeGroup then
                begin
                  vList.Add(TNodeGroup(FPickObjects.TopNode).Group);
                  DoSelect(vList);
                end;
              end;
            finally
              vList.Free;
            end;
          end;
    end;
  end;
  inherited MouseUp(Button, Shift, X, Y);
end;

procedure Tsg3DDrawingNavigator.OrbitMouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  if Orbit3D.Visible then
    inherited OrbitMouseUp(Sender, Button, Shift, X, Y)
  else
  begin
    MouseCapture := False;
    Cursor := TCursor(6);
  end;
end;

procedure Tsg3DDrawingNavigator.OrbitVisibleChanging(Sender: TObject);
begin
  HighLightObject := nil;
  FNav3D.Invalidate;
end;

procedure Tsg3DDrawingNavigator.MovePictureRect(const ADeltaX, ADeltaY: Integer);
var
  vMousePoint: TPoint;
  vOldPos, vNewPos, vTrans: TAffineVector;
  vKoef: Single;
begin
  vMousePoint := Point(ADeltaX + MouseMovePrevPoint.X, ADeltaY +
    MouseMovePrevPoint.Y);
  vOldPos := ClientToWorld(MouseMovePrevPoint.X, MouseMovePrevPoint.Y);
  vNewPos := ClientToWorld(vMousePoint.X, vMousePoint.Y);
  if VectorNorm(vOldPos) <> 0 then
  begin
    VectorSubtract(vNewPos, vOldPos, vTrans);
    if FNav3D.CameraStyle <> csOrthogonal then
    begin
      { TODO: calc vKoef for csPerspective translate }
      //vKoef := FNav3D.BoundingSphereRadius * FNav3D.Camera.SceneScale;
      vKoef := 3 * FNav3D.Camera.DepthOfView;
      VectorScale(vTrans, vKoef, vTrans);
    end;
    Translate(vTrans);
  end;
end;

procedure Tsg3DDrawingNavigator.Nav3DWindProc(var Message: TMessage);
begin
  case Message.Msg of
    WM_MOUSEFIRST .. WM_MOUSELAST, WM_KEYFIRST .. WM_KEYLAST:
      begin
        Navigator3D.Enabled := False;
        if HandleAllocated then
          Message.Result := SendMessage(WindowHandle, Message.Msg, Message.WParam, Message.LParam)
        else
          Message.Result := Perform(Message.Msg, Message.WParam, Message.LParam);
        Navigator3D.Enabled := True;
      end;
  else
    FNav3DWindProc(Message);
  end;
end;

procedure Tsg3DDrawingNavigator.Navigator3DAfterRender(Sender: TObject);
var
  I: Integer;
  vMatLibItem: TGLLibMaterial;
  vfgps: TFaceGroups;
begin
  if (HighLightObject is TMeshObject) and (FHighlightSaveMatName <> '') then
  begin
    case FDrawMode of
      dmNormal:
        begin
          SetMeshMaterial(TMeshObject(HighLightObject), FHighlightSaveMatName, FNav3D.FGLMaterialLibrary);
        end;
    else
      vMatLibItem := AssignMaterial(clGray, FNav3D.FGLMaterialLibrary.Materials);
      vfgps := TMeshObject(HighLightObject).FaceGroups;
      for I := 0 to vfgps.Count - 1 do
      begin
        vfgps[I].MaterialName := vMatLibItem.Name;
        vfgps[I].PrepareMaterialLibraryCache(FNav3D.FGLMaterialLibrary);
        vfgps[I].MaterialName := FHighlightSaveMatName;
      end;
    end;
    FHighlightSaveMatName := '';
  end;
end;

procedure Tsg3DDrawingNavigator.Navigator3DBeforeRender(Sender: TObject);
var
  vMesh: TMeshObject;
  vHighlightItem: TGLLibMaterial;
  vMatLib: TGLMaterialLibrary;
begin
  if HighLightObject is TMeshObject then
  begin
    vMesh := TMeshObject(HighLightObject);
    if vMesh.FaceGroups.Count > 0 then
    begin
      vMatLib := TGLMaterialLibrary(FHighLightObj.Lines.Material.MaterialLibrary);
      vHighlightItem := AssignMaterial(FHighLightObj.Color, vMatLib.Materials);
      FHighlightSaveMatName := vMesh.FaceGroups[0].MaterialName;
      SetMeshMaterial(vMesh, vHighlightItem.Name, vMatLib);
    end;
  end;
end;

function Tsg3DDrawingNavigator.OpenVPort: Boolean;
begin
  Result := False;
end;

procedure Tsg3DDrawingNavigator.PickObjectEvent(Sender: TObject);
begin
  FPickObjects.BuildLines;
  Navigator3DAfterRender(nil);
  RepaintScene;
  if Assigned(FOnPickObject) then
    FOnPickObject(Self);
end;

procedure Tsg3DDrawingNavigator.PrepareData(ALine: TsgGLPolyline;
  Points: TFPointList; SolidPolyline: Boolean; ClosedPolyline: Boolean);
var
  I: Integer;
begin
  if not CheckCount(ALine, Points.Count) then
    Exit;
  for I := 0 to Points.Count - 1 do
    ALine.Add(GetPointf(Points[I]));
  if SolidPolyline then
  begin
    if ClosedPolyline then
      ALine.Mode := GL_LINE_LOOP;
  end
  else
    ALine.Mode := GL_LINES;
end;

function Tsg3DDrawingNavigator.GetFilteredEntity(Entity: TsgDXFEntity): Integer;
var
  vInsert: TsgDXFInsert;
begin
  Result := 0;
  vInsert := IterateParam.Insert;
  while Assigned(vInsert) and (vInsert <> FInsert) do
    vInsert := vInsert.OwnerInsert;
  if vInsert = FInsert then
    Result := GetEntity(Entity)
end;

procedure Tsg3DDrawingNavigator.Reload(AEntities: TList);
var
  I, J, C: Integer;
  vMeshes: TList;
  P, vTmpParams: PsgCADIterate;
//  vCustomFindEnt: TCustomFindEnt;
  vInsert: TsgDXFInsertAccess;
  vAutoInsert: Boolean;
begin
  if AEntities.Count > 0 then
  begin
    C := -1;
    BeginUpdate;
    try
    TGLFreeFormAccess(FNav3D.GLFreeForm).DropMaterialLibraryCache;
    for I := 0 to AEntities.Count - 1 do
    begin
      vInsert := nil;
      if TObject(AEntities[I]) is TsgDXFInsert then
        vInsert := TsgDXFInsertAccess(AEntities[I]);

      if Assigned(vInsert) then
      begin
        vMeshes := GetInsertMeshes(TObject(AEntities[I]));
        try
          for J := vMeshes.Count - 1 downto 0 do
            TObject(vMeshes[J]).Free;
        finally
          vMeshes.Free;
        end;
      end;
      C := FNav3D.GLFreeForm.MeshObjects.Count;
      P := InitializeIterateParams;
      {vCustomFindEnt := TCustomFindEnt.Create;
      try
        vCustomFindEnt.Obj := TObject(AEntities[I]);
        vCustomFindEnt.Matrix := P^.Matrix;
        vCustomFindEnt.Iterate(FConverter);
        P^.Matrix := vCustomFindEnt.Matrix;
      finally
        vCustomFindEnt.Free;
      end;}
      if Assigned(vInsert) then
      begin
        vTmpParams := FConverter.Params;
        try
          FConverter.Params := P;
          vAutoInsert := FConverter.AutoInsert;
          FVectorFile := Pointer(1);
          try
            FConverter.AutoInsert := True;
            FInsert := vInsert;
            //vInsert.Invoke(FConverter, GetEntity, FinishEntity);
            TsgDXFConverterAccess(FConverter).GetModelLayout.Iterate(FConverter, GetFilteredEntity, FinishEntity);
            FInsert := nil;
          finally
            FConverter.AutoInsert := vAutoInsert;
            FVectorFile := nil;
          end;
        finally
          FConverter.Params := vTmpParams;
        end;
        Load3dFaceMeshObject;
        ResetDefaults;
      end;
    end;

    PickObjects.Objects.Clear;
    if (C <> -1) and (C < FNav3D.GLFreeForm.MeshObjects.Count) then
    begin
      for I := C to FNav3D.GLFreeForm.MeshObjects.Count - 1 do
        PickObjects.Objects.Add(FNav3D.GLFreeForm.MeshObjects[I]);
      PickObjects.BuildLines;
      Navigator3D.GLFreeForm.MeshObjects.Sort(MeshEntCompare);
    end;

    finally
      EndUpdate;
    end;
  end;
end;

procedure Tsg3DDrawingNavigator.RepaintScene;
begin
  if not (csDestroying in ComponentState) then
    FNav3D.Perform(WM_PAINT, 0, 0);
end;

procedure Tsg3DDrawingNavigator.ResetDefaults;
begin
  FCurrMeshObject3dFace := nil;
  FCurrVertexes3dFace := nil;
  FCurrMeshObject := nil;
  FCurrVertexes := nil;
  FIsBrepEntityIterate := False;
  FCurrColor := clNone;
  FCurrColor3dFace := clNone;
  FCurrLineWieght := 1;
end;

function Tsg3DDrawingNavigator.Rotate(Axis: TsgAxes; Angle: Extended): Boolean;
begin
  Result := False;
  if FNav3D = nil then Exit;
  FNav3D.Rotate(Axis, Angle);
  Result := True;
end;

procedure Tsg3DDrawingNavigator.RotToView(const A3DView: TsgDXFViewDirection);
var
  vM4f, vSceneM4f: TMatrix4f;
begin
  if not Assigned(FNav3D) then Exit;
  FNav3D.Buffer.BeginUpdate;
  try
  if (A3DView = vdDefault) and Assigned(FConverter) then
    MatrixToGLMatrix(FConverter.ViewTwistMatrix, vM4f)
  else
    vM4f := IdentityHmgMatrix;
  FNav3D.GLDCScene.ResetRotations;
  FNav3D.FAxes.ResetRotations;
  case A3DView of
    vdDefault:
      begin
        vSceneM4f := FNav3D.FAxes.FGLDCAxes.Matrix;
        NormalizeMatrix(vSceneM4f);
        FNav3D.FAxes.FGLDCAxes.Matrix := MatrixMultiply(vM4f, vSceneM4f);

        vSceneM4f := FNav3D.GLDCScene.Matrix;
        NormalizeMatrix(vSceneM4f);
        FNav3D.GLDCScene.Matrix := MatrixMultiply(vM4f, vSceneM4f);
      end;
    vdFront:
      begin
        Rotate(AxisX, -90);
      end;
    vdBottom:
      begin
        Rotate(AxisY, 180);
      end;
    vdLeft:
      begin
        Rotate(AxisY, 90);
        Rotate(AxisZ, 90);
      end;
    vdRight:
      begin
        Rotate(AxisY, -90);
        Rotate(AxisZ, -90);
      end;
    vdBack:
      begin
        Rotate(AxisX, 90);
        Rotate(AxisZ, 180);
      end;
    vdSWIsometric:
      begin
        Rotate(AxisZ, 45);
        Rotate(AxisX, -fIsometricRotation);
      end;
    vdSEIsometric:
      begin
        Rotate(AxisZ, -45);
        Rotate(AxisX, -fIsometricRotation);
      end;
    vdNWIsometric:
      begin
        Rotate(AxisZ, 135);
        Rotate(AxisX, -fIsometricRotation);
      end;
    vdNEIsometric:
      begin
        Rotate(AxisZ, -135);
        Rotate(AxisX, -fIsometricRotation);
      end;
  end;
  FitToSize;
  finally
    FNav3D.Buffer.EndUpdate;
  end;
end;

procedure Tsg3DDrawingNavigator.ScenePositionChange(Sender: TObject);
begin
  SetupLight(GetBox);
end;

procedure Tsg3DDrawingNavigator.SetDefault3DSceneOptions;
begin
  FOuterWireColor := clGreen;
  FOuterWireLineWieght := 3;
end;

procedure Tsg3DDrawingNavigator.SetMeshQuality(const Value: TsgMeshQuality);
var
  vMatrix: TFMatrix;
begin
  if FIsLoadedFromConv and Assigned(FLoadFromImage) and
    Assigned(FConverter) and HasTriangledMesh and (Value <> MeshQuality) then
  begin
    case Value of
      mqLow: FDeviationCoefficient := cnstDeviationCoefficientDefault;
      mqNormal: FDeviationCoefficient := (cnstDeviationCoefficientDefault +
        cnstDeviationCoefficientRealistic) / 2;
      mqHigh: FDeviationCoefficient := cnstDeviationCoefficientRealistic;
    end;
    FConverter.MeshQuality := FDeviationCoefficient;
    GetView(vMatrix);
    LoadFromConverter(FConverter, FLoadFromImage);
    LoadView(vMatrix);
  end;
end;

procedure Tsg3DDrawingNavigator.SetMouseRotate(const Value: Boolean);
begin
  FMouseRotate := Value;
end;

procedure Tsg3DDrawingNavigator.SetParamsByMeshBuilder(
  AParams: PsgParamTriangle; AInitData: PsgInitMeshBuilderData);
begin
  AParams^.ASelf := Self;
  AParams^.MeshPointer := FCurrMeshObject;
  AParams^.VertexPointer := nil;
  ZeroMemory(AInitData, Sizeof(TsgInitMeshBuilderData));
  AInitData^.TriangleFunc := @GetTriangle;
  AInitData^.MeshParam := AParams;
  AInitData^.NewFaceGroupFunc := @NewFaceGroup;
  // Add Outer Wire
  //AInitData^.Creator := Self;
  //AInitData^.DrawWireFunc := @CallBackOuterLineDraw;
end;

procedure Tsg3DDrawingNavigator.SetSelected(AEntities: TList);
var
  I, J: Integer;
  vMesh: TObject;
  vEnt: TsgDXFEntity;

  procedure AddMeshObjects(AInsert: TObject);
  var
    vMeshes: TList;
  begin
    vMeshes := GetInsertMeshes(AInsert);
    try
      PickObjects.Objects.Assign(vMeshes, laOr);
    finally
      vMeshes.Free;
    end;
  end;

begin
  if Assigned(PickObject) and (AEntities.Count = 1) and
     (TsgMeshObject(PickObject).Insert = AEntities[0]) then
     PickObjects.BuildLines
  else
  begin
    BeginUpdate;
    try
      if AEntities.Count = 0 then
      begin
        PickObject := nil;
        PickObjects.Objects.Clear;
        PickObjects.ClearLines;
      end
      else
      begin
        PickObjects.Objects.Clear;
        for I := 0 to AEntities.Count - 1 do
        begin
          vEnt := TsgDXFEntity(AEntities[I]);
          case vEnt.EntType of
            ceInsert: AddMeshObjects(vEnt);
            ce3DSolid, ceStep, ceBrep, ceIges:
              begin
                for J := 0 to vEnt.Count - 1 do
                  if vEnt[J].EntType = ceInsert then
                    AddMeshObjects(vEnt[J]);
              end;
          else
            vMesh := EntToGLObj(vEnt);
            if vMesh <> nil then
              PickObjects.Objects.Add(vMesh);
          end;
        end;
        PickObjects.BuildLines;
      end;
    finally
      EndUpdate;
    end;
  end;
end;

procedure Tsg3DDrawingNavigator.SetShowHighlight(const Value: Boolean);
begin
  if FHighLightObj.Enabled <> Value then
  begin
    if FHighLightObj.Enabled then
      HighLightObject := nil;
    FHighLightObj.Enabled := Value;
  end;
end;

procedure Tsg3DDrawingNavigator.SetShowingStyle(const Value: Tsg3DShowingStyle);
var
  I: Integer;
begin
  if FNav3D = nil then Exit;
  FShowingStyle := Value;
  for I := 0 to FNav3D.FGLMaterialLibrary.Materials.Count - 1 do
  begin
    ApplyShowingStyleToMaterial(FNav3D.FGLMaterialLibrary.Materials[I].Material);
    if FShowingStyle = ssHiddenLines then
      FNav3D.FGLMaterialLibrary.Materials[I].Shader := FHiddenLinesShader
    else
      FNav3D.FGLMaterialLibrary.Materials[I].Shader := nil;
  end;
  FNav3D.GLFreeForm.StructureChanged;
  FNav3D.Repaint;
end;

procedure Tsg3DDrawingNavigator.SetupLight(const ABox: TFRect);
var
  vCenter: TVector3f;
  vOffs: Single;
begin
  vCenter := VectorTransform(FNav3D.FCenter, FNav3D.GLDCScene.Matrix);
  vOffs := 3 * FNav3D.GLDCScene.BoundingSphereRadius;
  FNav3D.PositionLight(vCenter, vOffs);
end;

procedure Tsg3DDrawingNavigator.Translate(const AVector: TAffineVector);
begin
  FNav3D.Translate(AVector);
end;

procedure Tsg3DDrawingNavigator.DeleteEnts(AEntities: TList);
var
  I, J: Integer;
  vObj: TObject;
begin
  BeginUpdate;
  try
    for I := 0 to AEntities.Count - 1 do
    begin
      vObj := EntToGLObj(AEntities[I], @J);
      if Assigned(vObj) then
      begin
        Navigator3D.GLFreeForm.MeshObjects.Delete(J);
        vObj.Free;
      end;
    end;
  finally
    EndUpdate;
  end;
end;

{ Tsg3DDrwNavOrbit3D }

function Tsg3DDrwNavOrbit3D.GetNavigator3D: Tsg3DDrawingNavigator;
begin
  Result := Tsg3DDrawingNavigator(Navigator);
end;

procedure Tsg3DDrwNavOrbit3D.Paint;
var
  I: Integer;
  vGLCanvas: TGLCanvas;
begin
  if not Navigator3D.Navigator3D.IsRenderingContextAvailable then Exit;
  vGLCanvas := TGLCanvas.Create(Navigator3D.Navigator3D.Buffer.Width,
    Navigator3D.Navigator3D.Buffer.Height);
  try
    vGLCanvas.PenColor := Color;
    vGLCanvas.Ellipse(Center.X, Center.Y, BigRadius, BigRadius);
    for I := 0 to 3 do
      vGLCanvas.Ellipse(SmallCenters[I].X, SmallCenters[I].Y, SmallRadius);
  finally
    vGLCanvas.Free;
  end;
end;

procedure Tsg3DDrwNavOrbit3D.VisibleChanging;
begin
  inherited VisibleChanging;
  if Assigned(FOnVisibleChanging) then
    FOnVisibleChanging(Self);
end;

{ Tsg3DAxis }

constructor Tsg3DAxis.CreateEx(AGLScene: TGLScene; AOwner: TComponent);

  procedure SetArray(const A1: array of Single; A2: TGLCoordinates;
    ADoAsPoint: Boolean = False; Arrow: TGLArrowLine = nil);
  begin
    if ADoAsPoint then
      A2.SetPoint(A1[0], A1[1], A1[2])
    else
      A2.SetVector(A1[0], A1[1], A1[2]);
  end;

  procedure CreareArrow(var Arrow: TGLArrowLine; const ADirection,
    APosition, AUp, AColor: array of Single);
  begin
    Arrow := TGLArrowLine.CreateAsChild(FGLDCAxes);
    Arrow.BottomArrowHeadHeight := 0.5;
    Arrow.BottomArrowHeadRadius := 0.2;
    Arrow.BottomRadius := 0.1;
    SetDefDirForObject(Arrow);
    SetArray(ADirection, Arrow.Direction);
    SetArray(APosition, Arrow.Position, True);
    SetArray(AUp, Arrow.Up);
    Arrow.Height := cntDefAxisHeight;
    Arrow.Scale.SetVector(1, 1, 1);
    Arrow.TopArrowHeadHeight := 0.5;
    Arrow.TopArrowHeadRadius := 0.2;
    Arrow.TopRadius := 0.1;
    SetColorForMaterial(Arrow.Material, VectorMake(AColor[0], AColor[1], AColor[2], 1));
    //Arrow.Material.Texture.Border := 0;
  end;

begin
  FGLScene := AGLScene;
  FDefCameraScale := cntDefPerspAxisCameraScale;

  inherited Create(AOwner);
  FGLDCAxes := TGLDummyCube.CreateAsChild(FGLScene.Objects);
  FGLDCAxes.Position.AsAffineVector := cntDefAxisPos;
  SetDefDirForObject(FGLDCAxes);

  CreareArrow(FGLArrowX, [1, 0, 0], [0.3, 0, 0], [0, 0, -1], [1, 0.133, 0.153, 1]);//$2722FF
  CreareArrow(FGLArrowY, [0, 1, 0], [0, 0.3, 0], [0, 0, -1], [0.133, 1, 0.133, 1]);//$22FF22
  CreareArrow(FGLArrowZ, [0, 0, 1], [0, 0, 0.3], [0, 1, 0], [0.133, 0.282, 1, 1]);//$FF4822

  CreateCamera(FGLScene.Objects, FGLDCAxes, FGLCamera, 1);
  Camera := FGLCamera;
  SetDefPositions;
  SetDefDistanceForCamera(FGLCamera, BoundingSphereRadius, FDefCameraScale);
  Buffer.ContextOptions := [roDoubleBuffer, roDestinationAlpha];
  Buffer.AmbientColor.Color := clrTransparent;
  Buffer.BackgroundColor := clWhite;
  Buffer.BackgroundAlpha := 0;
//  Buffer.Resize(10, 10,
//    cntAxisImageWidth, cntAxisImageHeight);
end;

function Tsg3DAxis.GetBoundingSphereRadius: Single;
begin
  Result := FGLArrowX.BoundingSphereRadius * 5;
end;

procedure Tsg3DAxis.HideAxis;
begin
  FGLDCAxes.Visible := False;
end;

procedure Tsg3DAxis.LoadIdentity;
begin
  FGLDCAxes.Matrix := IdentityHmgMatrix;
  SetDefDistanceForCamera(Camera, BoundingSphereRadius, FDefCameraScale);
end;

procedure Tsg3DAxis.ResetRotations;
begin
  FGLDCAxes.ResetRotations;
  Camera.Position := FGLDCAxes.Position;
  Camera.Move(2 * BoundingSphereRadius);
  Camera.RotateObject(FGLDCAxes, 0, 180, 0);
end;

procedure Tsg3DAxis.SetCameraPosition(X, Y, Z: Single);
const
  cntKoef = 0.5;
begin
  Camera.Position.SetPoint(X, Y, Z);
  //FGLLightSource.Position.SetPoint(X * cntKoef , Y * cntKoef, Z * cntKoef);
  //FGLLightSource.Position.AsAffineVector := NullVector;
end;

procedure Tsg3DAxis.SetDefPositions;
begin
  FGLDCAxes.Position.AsAffineVector := cntDefAxisPos;
  SetCameraPosition(cntDefCameraPos.V[0], cntDefCameraPos.V[1], cntDefCameraPos.V[2]);
end;

procedure Tsg3DAxis.ShowAxis;
begin
  FGLDCAxes.Visible := True;
  //FGLLightSource.Visible := True;
end;

procedure Tsg3DAxis.SetObjectsVisible(AVisible: Boolean);
var
  I: Integer;
begin
  FGLDCAxes.Visible := AVisible;
  for I := 0 to FGLDCAxes.Count - 1 do
    FGLDCAxes.Children[I].Visible := AVisible;
end;

{ TsgGLGraphic }

procedure TsgGLGraphic.Changed(Sender: TObject);
begin
  inherited Changed(Sender);
  Inc(FChangedCount);
end;

procedure TsgGLGraphic.DoRender;
var
  vShowAsix, vOrbit3DVisible: Boolean;

  procedure GLSceneBufferRenderToBitmap;
  var
    vTmpViewport: TRectangle;
    vDrawMatrix, vNewView: TFMatrix;
    vBoundingBox, vBox, vRect: TFRect;
    vRectCenter, vBoxCenter, vNewRectCenter: TFPoint;
    vScale: Double;
  begin
    FBuffer.BeginUpdate;
    FNavigator.Navigator3D.GLDCScene.BeginUpdate;
    FBuffer.Camera.BeginUpdate;
    try
      FNavigator.GetView(vDrawMatrix);
      vNewView := vDrawMatrix;
      vBoundingBox := FNavigator.GetBox;
      vBox := vBoundingBox;
      OffsetFRect(vBox, -FNavigator.FBoxOffs.X, -FNavigator.FBoxOffs.Y, -FNavigator.FBoxOffs.Z);
      vRect := vBox;
      TransRectCorners(vRect, vDrawMatrix);
      vRectCenter := MiddleFPoint(vRect.TopLeft, vRect.BottomRight);

      if (FViewport.Width = 0) and (FViewport.Height = 0) then
      begin
        if FNavigator.ViewRectMode then
        begin
          FViewport.Left := Round(Int(vRect.Left));
          FViewport.Top := Round(Int(vRect.Bottom));
          FViewport.Width := Round(vRect.Right - vRect.Left);
          FViewport.Height := Round(vRect.Top - vRect.Bottom);
        end
        else
        begin
          FViewport.Left := 0;
          FViewport.Top := 0;
          FViewport.Width := Width;
          FViewport.Height := Height;
        end;
      end;

      vBoxCenter := MiddleFPoint(vBoundingBox.TopLeft, vBoundingBox.BottomRight);
      vNewRectCenter.X := FViewport.Left + 0.5 * FViewport.Width;
      vNewRectCenter.Y := FViewport.Top + 0.5 * FViewport.Height;
      vNewRectCenter.Z := 0;
      vScale := FViewport.Height / (vRect.Top - vRect.Bottom); // ??
      vNewView := FMatScale(vNewView, vScale);
      vNewView.E0 := AffineTransformPoint(SubFPoint(vBoxCenter, FNavigator.FBoxOffs), vNewView);
      vNewView.E0.Z := 0.0;
      vNewView.E0 := SubFPoint(vNewRectCenter, vNewView.E0);
      vTmpViewport := FBuffer.ViewPort;
      try
        FBuffer.Resize(0, 0, Width, Height);
        FNavigator.LoadView(vNewView);
        FBuffer.RenderToBitmap(Self);
      finally
        FBuffer.Resize(vTmpViewport.Left, vTmpViewport.Top,
          vTmpViewport.Width, vTmpViewport.Height);
      end;
    finally
      FNavigator.LoadView(vDrawMatrix);
      FBuffer.Camera.EndUpdate;
      FNavigator.Navigator3D.GLDCScene.EndUpdate;
      FBuffer.EndUpdate;
    end;
  end;

begin
  if Assigned(FNavigator) and Assigned(FBuffer) then
  begin
    FNavigator.BeginUpdate;
    try
      Transparent := True;
      TransparentColor := FNavigator.Navigator3D.BackgroundColor;
      vShowAsix := FNavigator.Navigator3D.ShowAxis;
      vOrbit3DVisible := FNavigator.Orbit3D.Visible;
      FNavigator.FLockPaintSysMenuIcons := True;
      try
        FNavigator.Navigator3D.ShowAxis := False;
        FNavigator.Orbit3D.Visible := False;
        GLSceneBufferRenderToBitmap;
        FChangedCount := 0;
      finally
        FNavigator.Navigator3D.ShowAxis := vShowAsix;
        FNavigator.Orbit3D.Visible := vOrbit3DVisible;
        FNavigator.FLockPaintSysMenuIcons := False;
      end;
    finally
      FNavigator.EndUpdate;
    end;
  end;
end;

procedure TsgGLGraphic.Draw(ACanvas: TCanvas; const Rect: TRect);
begin
  if (FChangedCount > 0) or (ACanvas = Canvas) then
  begin
    DoRender;
  end;
  if ACanvas <> Canvas then
    inherited Draw(ACanvas, Rect);
end;

procedure TsgGLGraphic.DrawTransparent(ACanvas: TCanvas; const Rect: TRect;
  Opacity: Byte);
begin
{$IFDEF SGDEL_2009}
  if (FChangedCount > 0) or (ACanvas = Canvas) then
  begin
    DoRender;
  end;
  if ACanvas <> Canvas then
    inherited DrawTransparent(ACanvas, Rect, Opacity);
{$ELSE}
  Draw(ACanvas, Rect);
{$ENDIF};
end;

procedure TsgGLGraphic.SetNavigator(const ANavigator: Tsg3DDrawingNavigator);
begin
  if FNavigator <> ANavigator then
  begin
    FNavigator := ANavigator;
    if Assigned(FNavigator) and Assigned(FNavigator.Navigator3D) then
    begin
      FBuffer := FNavigator.Navigator3D.Buffer;
      SetSizeGraphic(Self, FBuffer.Width, FBuffer.Height);
    end
    else
      FBuffer := nil;
  end;
end;

{ TsgVertexList }

procedure TsgVertexList.BuildList(var mrci: TRenderContextInfo);
begin
  TsgMeshObject(Owner.Owner).DeclareArraysToOpenGL(mrci, False);
  glDrawElements(FMode1, VertexIndices.Count, GL_UNSIGNED_INT, VertexIndices.List);
end;

constructor TsgVertexList.Create;
begin
  inherited Create;
  FMode1 := GL_LINES;
  FMode2 := MAXDWORD;
end;

{ TsgHiddenLineShader }

procedure TsgHiddenLineShader.DoApply(var rci: TRenderContextInfo;
  Sender: TObject);
begin
  FPassCount := 1;
  rci.GLStates.PolygonMode := pmFill;
  //rci.GLStates.SetGLPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
  //rci.GLStates.ResetGLPolygonMode;
  glPushAttrib(GL_CURRENT_BIT or GL_ENABLE_BIT);
  glColor3fv(@FBackgroundColor);
  glDisable(GL_TEXTURE_2D);
  glEnable(GL_POLYGON_OFFSET_FILL);
  glPolygonOffset(1, 2);
end;

function TsgHiddenLineShader.DoUnApply(var rci: TRenderContextInfo): Boolean;
begin
   case FPassCount of
     1:
       begin
         FPassCount := 2;
         //rci.GLStates.SetGLPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
         rci.GLStates.PolygonMode := pmLines;
         glPopAttrib;
         Result := True;
       end;
    else
      Result := False;
   end;
end;

{ TsgTeselledVertexList }

procedure TsgTeselledVertexList.BuildList(var mrci: TRenderContextInfo);
begin
  inherited BuildList(mrci);
end;


{ TCustomFindEnt }

function TCustomFindEnt.GetEnt(Entity: TsgDXFEntity): Integer;
begin
  Result := 0;
  if Entity = Obj then
    FMatrix := FParams.Matrix;
end;

function TCustomFindEnt.GetObject: TObject;
begin
  Result := FObj;
end;

function TCustomFindEnt.GetParams: PsgCADIterate;
begin
  Result := @FParams;
end;

procedure TCustomFindEnt.Iterate(AConverter: TsgDXFConverter);
var
  vParams: PsgCADIterate;
  vAutoInsert: Boolean;
begin
  if Assigned(AConverter) then
  begin
    vParams := AConverter.Params;
    try
      vAutoInsert := AConverter.AutoInsert;
      try
        AConverter.AutoInsert := True;
        FParams.Matrix := Matrix;
        AConverter.Iterate(GetEnt, nil, FParams);
      finally
        AConverter.AutoInsert := vAutoInsert;
      end;
    finally
      AConverter.Params := vParams;
    end;
  end;
end;

procedure TCustomFindEnt.SetObject(const Value: TObject);
begin
  FObj := Value;
end;

{ TsgGLPolyline }

procedure TsgGLPolyline.Assign(Source: TPersistent);
begin
  inherited Assign(Source);
  if Source is TsgGLPolyline then
    FLineWidth := TsgGLPolyline(Source).LineWidth;
end;

procedure TsgGLPolyline.BuildList(var rci: TRenderContextInfo);
var
  I: Integer;
begin
  if Count > 1 then
  begin
    // first, we setup the line color & stippling styles
    SetupLineStyle(rci);
    if rci.bufferDepthTest then
      rci.GLStates.Enable(stDepthTest)
    else
      rci.GLStates.Disable(stDepthTest);
    // lines, cubic splines or bezier
    GL.Begin_(FMode);
    // single color
    for I := 0 to Count - 1 do
      GL.Vertex3fv(@List^[I]);
    GL.End_;
    rci.GLStates.Disable(stColorLogicOp);
  end;
end;

constructor TsgGLPolyline.Create;
begin
  inherited Create;
  FLineWidth := 1;
  FMode := GL_LINE_STRIP;
end;

function TsgGLPolyline.GetMaterailName: string;
begin
  Result := TagString;
end;

procedure TsgGLPolyline.SetMaterailName(const Value: string);
begin
  TagString := Value;
end;

procedure TsgGLPolyline.SetupLineStyle(var rci: TRenderContextInfo);
var
  vMatLibItem: TGLLibMaterial;
  vMaterialCache: TGLMaterial;
begin
  rci.GLStates.Disable(stLighting);
  rci.GLStates.Disable(stLineStipple);
  rci.GLStates.Disable(stLineSmooth);
  rci.GLStates.LineWidth := FLineWidth;
  vMaterialCache := nil;
  if Assigned(rci.materialLibrary) then
  begin
    vMatLibItem := TGLMaterialLibrary(rci.materialLibrary).LibMaterialByName(MaterialName);
    if Assigned(vMatLibItem) then
      vMaterialCache := vMatLibItem.Material;
  end;
  if Assigned(vMaterialCache) then
    GL.Color4fv(vMaterialCache.FrontProperties.Diffuse.AsAddress)
  else
    GL.Color4fv(@clrGray50);
end;

{ TsgGLLines }

constructor TsgGLLines.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FPolylines := TList.Create;
  FDepthTest := True;
end;

procedure TsgGLLines.Assign(Source: TPersistent);
var
  I: Integer;
begin
  if Source is TsgGLLines then
  begin
    Clear;
    for I := 0 to TsgGLLines(Source).FPolylines.Count - 1 do
      AddNode.Assign(TsgGLPolyline(TsgGLLines(Source).FPolylines[I]));
  end;
  inherited Assign(Source);
end;

procedure TsgGLLines.BuildList(var rci: TRenderContextInfo);
var
  I: Integer;
  vDepthTest: Boolean;
  vMaterialLibrary: TObject;
begin
  vMaterialLibrary := rci.materialLibrary;
  vDepthTest := rci.bufferDepthTest;
  try
    rci.materialLibrary := Material.materialLibrary;
    rci.bufferDepthTest := FDepthTest;
    for I := 0 to FPolylines.Count - 1 do
      TsgGLPolyline(FPolylines[I]).BuildList(rci);
  finally
    rci.bufferDepthTest := vDepthTest;
    rci.materialLibrary := vMaterialLibrary;
  end;
end;

procedure TsgGLLines.Clear;
begin
  while FPolylines.Count > 0 do
    DeleteLastNode;
  DeleteChildCameras;
  DestroyHandles;
end;

destructor TsgGLLines.Destroy;
begin
  Clear;
  FPolylines.Free;
  inherited Destroy;
end;

function TsgGLLines.AddNode: TsgGLPolyline;//TGLLines;
begin
  Result := TsgGLPolyline.Create;
  FPolylines.Add(Result);
end;

function TsgGLLines.ReleaseLastNode: TsgGLPolyline;
var
  I: Integer;
begin
  I := FPolylines.Count - 1;
  if I >= 0 then
  begin
    Result := FPolylines.List[I];
    FPolylines.Delete(I);
  end
  else
    Result := nil;
end;

procedure TsgGLLines.DeleteLastNode;
begin
  ReleaseLastNode.Free;
end;

{ TsgMeshObject }

constructor TsgMeshObject.CreateWithEnt(AOwner: TMeshObjectList; AEnt: TObject);
begin
  CreateOwned(AOwner);
  SetEntity(AEnt);
end;

function TsgMeshObject.GetInsert: TObject;
begin
  if Assigned(FNode) then
    Result := FNode.Obj
  else
    Result := nil;
end;

function TsgMeshObject.GetRootInsert: TObject;
var
  N: TCustomNode;
begin
  Result := nil;
  if Assigned(FNode) then
  begin
    N := TNodeLite(FNode);
    while Assigned(N) and Assigned(N.Owner) and (N.Owner.ClassType <> TRoot) do
      N := N.Owner;
    if Assigned(N) then
      Result := N.Obj;
  end;
end;

procedure TsgMeshObject.SetEntity(AEnt: TObject);
begin
  FEntity := AEnt;
end;

{ TsgPickObject }

procedure TsgPickObject.BuildLines;
var
  I, J, K: Integer;
  vM: TFMatrix;
  vLines: TFPointList;
  P: PFPointArray;
  vPolyArray: PAffineVectorArray;
begin
  ClearLines;
  CreateLines;
  vLines := TFPointList.Create;
  try
    for I := 0 to FObjects.Count - 1 do
    begin
      vLines.Clear;
      if Tsg3DDrawingNavigator(FOwner).GetMeshLines(TsgMeshObject(FObjects[I]), vLines, @vM) then
      begin
        P := vLines.List;
        K := FPoly.Count;
        FPoly.Count := K + vLines.Count;
        vPolyArray := @FPoly.List^[K];
        for J := 0 to vLines.Count - 1 do
          TransformPointTo3f(P^[J], vM, @vPolyArray^[J]);
      end;
    end;
  finally
    vLines.Free;
  end;
  FLines.Visible := FPoly.Count > 0;
end;

procedure TsgPickObject.ClearLines;
begin
  if FLines <> nil then
  begin
    FLines.FPolylines.Remove(FPoly);
    FLines.Clear;
    FPoly.Clear;
    FLines.FPolylines.Add(FPoly);
  end;
end;

constructor TsgPickObject.Create(AOwner: TPersistent);
begin
  inherited Create;
  FObjects := TList.Create;
  FOwner := AOwner;
  FPick := nil;
  CreateLines;
end;

procedure TsgPickObject.CreateLines;
var
  vNav3d: Tsg3DNavigator;
begin
  vNav3d := Tsg3DDrawingNavigator(FOwner).Navigator3D;
  if not Assigned(FLines) then
  begin
    FLines := TsgGLPickLines.CreateAsChild(vNav3d.GLDCScene);
    FLines.DepthTest := False;
    TsgGLPickLines(FLines).PickObjects := Self;
    FPoly := FLines.AddNode;
  end;
  FPoly.Mode := GL_LINES;
  FPoly.MaterialName := AssignMaterial(Color, vNav3d.FGLMaterialLibrary);
  FPoly.LineWidth := 3;
  FPoly.SetCountResetsMemory := False;
end;

destructor TsgPickObject.Destroy;
begin
  FObjects.Free;
  FLines.Free;
  inherited Destroy;
end;

procedure TsgPickObject.DoPick;
begin
  if FEnabled then
    if Assigned(FOnPick) then
      FOnPick(Self);
end;

function TsgPickObject.GetObject: TObject;
begin
  Result := FPick;
end;

function TsgPickObject.GetOwner: TPersistent;
begin
  Result := FOwner;
end;

procedure TsgPickObject.SetObject(AObject: TObject);
var
  I: Integer;
begin
  if AObject <> FPick then
  begin

    if FPickCached and not Assigned(FPickCache) then
    begin
      FPickCache := AObject;
      if Assigned(FPick) then
      begin
        FPick := nil;
        DoPick;
      end;
    end;

    I := FObjects.IndexOf(AObject);
    if I = -1 then
    begin
      FObjects.Clear;
      FObjects.Add(AObject);
    end;

    if FPickCached and Assigned(FPickCache) then
    begin
      FPick := FPickCache;
      FPickCache := nil;
    end
    else
      FPick := AObject;
    if FPick = nil then
    begin
      Tag := nil;
      TopNode := nil;
    end;
    DoPick;
  end
  else
    if (FPick = nil) and (FObjects.Count > 0) then
    begin
      FObjects.Clear;
      Tag := nil;
      TopNode := nil;
      DoPick;
    end;
end;

{ TsgGLPickLines }

procedure TsgGLPickLines.BuildList(var rci: TRenderContextInfo);
var
  w: Single;
begin
  w := rci.GLStates.LineWidth;
  inherited BuildList(rci);
  rci.GLStates.LineWidth := w;
end;

destructor TsgGLPickLines.Destroy;
begin
  if Assigned(FPickObjects) then
    FPickObjects.FLines := nil;
  inherited Destroy;
end;

{ TCustomNode }

constructor TCustomNode.Create(AOwner: TCustomNode);
begin
  inherited Create;
  FOwner := AOwner;
end;

function TCustomNode.GetOwner: TPersistent;
begin
  Result := FOwner;
end;

{ TNode }

function TNodeLite.AddChild(AObj: TObject): TNodeLite;
begin
  Result := TNodeLite.Create(Self);
  Result.Obj := AObj;
  Add(Result);
end;

procedure TNodeLite.Clean;
var
  I: Integer;
  N: TNodeLite;
begin
  I := Count - 1;
  while I >= 0 do
  begin
    N := TNodeLite(List^[I]);
    Delete(I);
    N.Free;
    I := Count - 1;
  end;
end;

destructor TNodeLite.Destroy;
begin
  Clean;
  inherited Destroy;
end;

function TNodeLite.Find(AObj: TObject; var I: Integer): Boolean;
var
  N: TNodeLite;
begin
  N := TNodeLite.Create(nil);
  N.Obj := AObj;
  Result := QFind(Self, True, dupIgnore, NodesCompare, N, I);
  N.Free;
end;

function TNodeLite.FindDeep(AObj: TObject; var AOwner: TNodeLite; var I: Integer;
  const ALevel: Integer): Boolean;
var
  J: Integer;
begin
  Result := False;
  if Find(AObj, I) then
  begin
    Result := True;
    AOwner := Self;
  end;
  if not Result and (ALevel > 0) then
  begin
    J := 0;
    while (J < Count) and not Result do
    begin
      Result := TNodeLite(Items[J]).FindDeep(AObj, AOwner, I, ALevel - 1);
      Inc(J);
    end;
  end;
end;

procedure TNodeLite.GetMatrix(out AMatrix: TFMatrix);
begin
  if Owner <> nil then
    TNodeLite(Owner).GetMatrix(AMatrix);
  if Obj <> nil then
    AMatrix := FMatXMat(TsgDXFInsertAccess(Obj).FMatrix, AMatrix)
end;

{ TRoot }

procedure TRoot.GetMatrix(out AMatrix: TFMatrix);
begin
  AMatrix := FMatrix;
end;

initialization
  Lock := TCriticalSection.Create;

finalization
  Solid3DFileExts.Free;
  Lock.Free;
end.
