258 lines
7.0 KiB
ObjectPascal
258 lines
7.0 KiB
ObjectPascal
unit ex4;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
{ This example demonstrate how to load 3D objects and show them with
|
|
appropriate material and color.
|
|
|
|
The LoadObjectFromFile returns an 3D object defined by an OBJ file.
|
|
Some things are ignored, like texture information or normal information.
|
|
But when a material name is used, the function UseMaterial is called.
|
|
So here it is overriden in order to give the best material possible.
|
|
|
|
Some objects have specific effects. For example, the teapot is defined
|
|
as Biface so that the reflected light is computed outside and inside
|
|
the glass.
|
|
|
|
The helicopter is divided into two parts. The upper part contains the
|
|
rotor in order to rotate it, so that the helicopter seems to fly.
|
|
|
|
The lamp contains in fact 4 lamps, and so 4 light sources are created.
|
|
|
|
}
|
|
|
|
uses
|
|
Classes, SysUtils, BGRAScene3D, BGRABitmapTypes,
|
|
BGRAOpenGL3D, BGRAOpenGL;
|
|
|
|
type
|
|
|
|
{ TExample4 }
|
|
|
|
TExample4 = class(TBGLScene3D)
|
|
protected
|
|
lamp,shiny,reflect: IBGRAMaterial3D;
|
|
rotated: IBGRAPart3D;
|
|
rotateCenter: TPoint3D;
|
|
message: string;
|
|
procedure UseMaterial(materialname: string; face: IBGRAFace3D); override;
|
|
procedure CreateScene;
|
|
public
|
|
procedure Render; override;
|
|
procedure Elapse;
|
|
procedure RenderGL(ACanvas: TBGLCustomCanvas; AMaxZ: single=1000); override;
|
|
procedure NextModel;
|
|
constructor Create;
|
|
end;
|
|
|
|
implementation
|
|
|
|
uses BGRATextFX;
|
|
|
|
var
|
|
numObj: integer= 3;
|
|
|
|
const
|
|
objList: array[0..9] of string = ( 'ciseau.obj',
|
|
'fourche.obj', 'pelle.obj', 'helico.obj', 'mario.obj', 'helice.obj',
|
|
'lampe.obj', 'teapot.obj', 'roue.obj', 'trumpet.obj');
|
|
|
|
{ TExample4 }
|
|
|
|
constructor TExample4.Create;
|
|
begin
|
|
inherited Create;
|
|
|
|
//create shiny material using saturation of diffusion (1.3 .. 1.5)
|
|
shiny := CreateMaterial;
|
|
shiny.SaturationLow := 1.3;
|
|
shiny.SaturationHigh := 1.5;
|
|
reflect := CreateMaterial(50);
|
|
lamp := CreateMaterial;
|
|
lamp.LightThroughFactor := 0.05;
|
|
|
|
CreateScene;
|
|
end;
|
|
|
|
procedure TExample4.UseMaterial(materialname: string; face: IBGRAFace3D);
|
|
var color : TBGRAPixel;
|
|
begin
|
|
if (materialname = 'globes') then
|
|
begin
|
|
color := BGRA(255,240,220);
|
|
face.Material := lamp;
|
|
end else
|
|
if (materialname = 'bone') then
|
|
begin
|
|
color := BGRA(255,240,220);
|
|
end else
|
|
if (materialname = 'bronze') then
|
|
begin
|
|
color := CSSSaddleBrown;
|
|
face.Material := reflect;
|
|
end else
|
|
if materialname = 'grey' then
|
|
begin
|
|
color := BGRA(230,192,80);
|
|
face.Material := shiny;
|
|
end else
|
|
begin
|
|
color := StrToBGRA(materialname);
|
|
if (objList[numObj] <> 'helice.obj') and (color.red = color.green) and (color.green = color.blue) then
|
|
begin
|
|
if (color.alpha <> 255) or (color.red = 0) then
|
|
face.Material := reflect
|
|
else
|
|
face.Material := shiny;
|
|
end else
|
|
if color.alpha <> 255 then
|
|
face.Material := reflect;
|
|
end;
|
|
face.SetColor( color );
|
|
end;
|
|
|
|
procedure TExample4.CreateScene;
|
|
var obj: IBGRAObject3D;
|
|
r: single;
|
|
i: integer;
|
|
basePath, filename: string;
|
|
begin
|
|
Clear;
|
|
|
|
basePath := ExtractFilePath(Paramstr(0));
|
|
filename := 'obj'+PathDelim+objList[numObj];
|
|
if not fileexists(basePath+filename) and fileexists(basePath+'..'+PathDelim+'..'+PathDelim+filename) then
|
|
filename := '..'+PathDelim+'..'+PathDelim+filename;
|
|
filename := basePath+ filename;
|
|
if not FileExists(filename) then
|
|
begin
|
|
message := 'File not found : '+ filename;
|
|
exit;
|
|
end;
|
|
|
|
obj := LoadObjectFromFile(filename, objList[numObj] <> 'teapot.obj');
|
|
|
|
if objList[numObj] = 'helico.obj' then
|
|
begin
|
|
with obj.MainPart do
|
|
begin
|
|
rotated := CreatePart;
|
|
rotateCenter := Point3D(0,0,0);
|
|
for i := VertexCount-1 downto 0 do
|
|
if (Vertex[i].SceneCoord.y >= 22.2) then
|
|
begin
|
|
rotated.Add(Vertex[i]);
|
|
rotateCenter.Offset(Vertex[i].SceneCoord);
|
|
end;
|
|
rotateCenter.Scale(1/rotated.VertexCount);
|
|
obj.SeparatePart(rotated);
|
|
obj.MainPart.Scale(2,2,2);
|
|
end;
|
|
end else
|
|
rotated := nil;
|
|
|
|
obj.LightingNormal := lnVertex;
|
|
if objList[numObj] = 'teapot.obj' then
|
|
for i := 0 to obj.FaceCount-1 do
|
|
obj.Face[i].Biface := true;
|
|
|
|
with obj.MainPart.BoundingBox do
|
|
obj.MainPart.Translate((min+max)*(-1/2), False);
|
|
r := obj.MainPart.Radius;
|
|
if r <> 0 then obj.MainPart.Scale(40/r, False);
|
|
if objList[numObj] = 'lampe.obj' then
|
|
begin
|
|
obj.MainPart.RotateXDeg(180, False);
|
|
obj.MainPart.Scale(1.5,1.5,1.5);
|
|
end else
|
|
if objList[numObj] = 'mario.obj' then
|
|
obj.MainPart.RotateXDeg(90, False)
|
|
else
|
|
begin
|
|
obj.MainPart.RotateXDeg(180-20, False);
|
|
obj.MainPart.RotateYDeg(-20, False);
|
|
if objList[numObj] = 'trumpet.obj' then
|
|
obj.MainPart.Scale(2,2,2,False);
|
|
end;
|
|
|
|
if objList[numObj] = 'lampe.obj' then
|
|
begin
|
|
AmbiantLightness := 0.7;
|
|
AddPointLight(obj.MainPart.Add(0,7.7,0),10);
|
|
AddPointLight(obj.MainPart.Add(1.9,6.5,0),10);
|
|
AddPointLight(obj.MainPart.Add(-0.9,6.5,1.5),10);
|
|
AddPointLight(obj.MainPart.Add(-0.9,6.5,-1.7),10);
|
|
end
|
|
else
|
|
begin
|
|
//set ambiant lightness to dark (1 is normal lightness)
|
|
AmbiantLightness := 0.5;
|
|
if objList[numObj] = 'helice.obj' then
|
|
AddDirectionalLight(Point3D(1,1,1),0.75,-0.5)
|
|
else
|
|
AddDirectionalLight(Point3D(1,1,1),1,-0.5); //add a directional light from top-left, maximum lightness will be 0.5 + 1 = 1.5
|
|
end;
|
|
|
|
RenderingOptions.PerspectiveMode:= pmZBuffer;
|
|
if objList[numObj] = 'helice.obj' then
|
|
RenderingOptions.LightingInterpolation := liAlwaysHighQuality
|
|
else
|
|
RenderingOptions.LightingInterpolation := liSpecularHighQuality;
|
|
end;
|
|
|
|
procedure TExample4.Render;
|
|
var fx: TBGRATextEffect;
|
|
begin
|
|
if objList[numObj] = 'teapot.obj' then
|
|
Surface.GradientFill(0,0,Surface.Width,Surface.Height,BGRABlack,BGRA(70,100,100),gtLinear,PointF(0,0),PointF(0,Surface.Height),dmSet) else
|
|
if objList[numObj] = 'lampe.obj' then
|
|
Surface.Fill(BGRA(0,0,60));
|
|
|
|
inherited Render;
|
|
|
|
if message <> '' then
|
|
begin
|
|
fx := TBGRATextEffect.Create(message,'Arial',20,True);
|
|
fx.DrawOutline(Surface,Surface.Width div 2,Surface.Height div 2-fx.TextHeight div 2,BGRABlack,taCenter);
|
|
fx.Draw(Surface,Surface.Width div 2,Surface.Height div 2-fx.TextHeight div 2,BGRAWhite,taCenter);
|
|
fx.Free;
|
|
end else
|
|
Surface.TextOut(Surface.Width,0,objList[numObj],BGRAWhite,taRightJustify);
|
|
end;
|
|
|
|
procedure TExample4.Elapse;
|
|
begin
|
|
if rotated <> nil then
|
|
begin
|
|
rotated.Translate(-rotateCenter,false);
|
|
rotated.RotateYDeg(20,False);
|
|
rotated.Translate(rotateCenter,false);
|
|
end;
|
|
end;
|
|
|
|
procedure TExample4.RenderGL(ACanvas: TBGLCustomCanvas; AMaxZ: single);
|
|
begin
|
|
if objList[numObj] = 'teapot.obj' then
|
|
ACanvas.FillRectLinearColor(0,0,BGLCanvas.Width,BGLCanvas.Height,
|
|
BGRABlack,BGRABlack,
|
|
BGRA(70,100,100),BGRA(70,100,100),
|
|
False) else
|
|
if objList[numObj] = 'lampe.obj' then
|
|
ACanvas.Fill(BGRA(0,0,60));
|
|
|
|
inherited RenderGL(ACanvas, AMaxZ);
|
|
end;
|
|
|
|
procedure TExample4.NextModel;
|
|
begin
|
|
inc(numObj);
|
|
if numObj = length(objList) then numObj := 0;
|
|
|
|
CreateScene;
|
|
end;
|
|
|
|
end.
|
|
|