1 unit ex4;
2 
3 {$mode objfpc}{$H+}
4 
5 interface
6 
7 { This example demonstrate how to load 3D objects and show them with
8   appropriate material and color.
9 
10   The LoadObjectFromFile returns an 3D object defined by an OBJ file.
11   Some things are ignored, like texture information or normal information.
12   But when a material name is used, the function UseMaterial is called.
13   So here it is overriden in order to give the best material possible.
14 
15   Some objects have specific effects. For example, the teapot is defined
16   as Biface so that the reflected light is computed outside and inside
17   the glass.
18 
19   The helicopter is divided into two parts. The upper part contains the
20   rotor in order to rotate it, so that the helicopter seems to fly.
21 
22   The lamp contains in fact 4 lamps, and so 4 light sources are created.
23 
24   }
25 
26 uses
27   Classes, SysUtils, BGRAScene3D, BGRABitmapTypes,
28   BGRAOpenGL3D, BGRAOpenGL;
29 
30 type
31 
32   { TExample4 }
33 
34   TExample4 = class(TBGLScene3D)
35   protected
36     lamp,shiny,reflect: IBGRAMaterial3D;
37     rotated: IBGRAPart3D;
38     rotateCenter: TPoint3D;
39     message: string;
40     procedure UseMaterial(materialname: string; face: IBGRAFace3D); override;
41     procedure CreateScene;
42   public
43     procedure Render; override;
44     procedure Elapse;
45     procedure RenderGL(ACanvas: TBGLCustomCanvas; AMaxZ: single=1000); override;
46     procedure NextModel;
47     constructor Create;
48   end;
49 
50 implementation
51 
52 uses BGRATextFX;
53 
54 var
55     numObj: integer= 3;
56 
57 const
58    objList: array[0..9] of string = ( 'ciseau.obj',
59       'fourche.obj', 'pelle.obj', 'helico.obj', 'mario.obj', 'helice.obj',
60       'lampe.obj', 'teapot.obj', 'roue.obj', 'trumpet.obj');
61 
62 { TExample4 }
63 
64 constructor TExample4.Create;
65 begin
66   inherited Create;
67 
68   //create shiny material using saturation of diffusion (1.3 .. 1.5)
69   shiny := CreateMaterial;
70   shiny.SaturationLow := 1.3;
71   shiny.SaturationHigh := 1.5;
72   reflect := CreateMaterial(50);
73   lamp := CreateMaterial;
74   lamp.LightThroughFactor := 0.05;
75 
76   CreateScene;
77 end;
78 
79 procedure TExample4.UseMaterial(materialname: string; face: IBGRAFace3D);
80 var color : TBGRAPixel;
81 begin
82   if (materialname = 'globes') then
83   begin
84     color := BGRA(255,240,220);
85     face.Material := lamp;
86   end else
87   if (materialname = 'bone') then
88   begin
89     color := BGRA(255,240,220);
90   end else
91   if (materialname = 'bronze') then
92   begin
93     color := CSSSaddleBrown;
94     face.Material := reflect;
95   end else
96   if materialname = 'grey' then
97   begin
98     color := BGRA(230,192,80);
99     face.Material := shiny;
100   end else
101   begin
102      color := StrToBGRA(materialname);
103      if (objList[numObj] <> 'helice.obj') and (color.red = color.green) and (color.green = color.blue) then
104      begin
105        if (color.alpha <> 255) or (color.red = 0) then
106          face.Material := reflect
107        else
108          face.Material := shiny;
109      end else
110        if color.alpha <> 255 then
111          face.Material := reflect;
112   end;
113   face.SetColor( color );
114 end;
115 
116 procedure TExample4.CreateScene;
117 var obj: IBGRAObject3D;
118     r: single;
119     i: integer;
120     filename: string;
121 begin
122   Clear;
123 
124   filename := 'obj'+PathDelim+objList[numObj];
125   if not fileexists(filename) and fileexists('..'+PathDelim+'..'+PathDelim+filename) then
126     filename := '..'+PathDelim+'..'+PathDelim+filename;
127   if not FileExists(filename) then
128     begin
129       message := 'File not found : '+ filename;
130       exit;
131     end;
132 
133   obj := LoadObjectFromFile(filename, objList[numObj] <> 'teapot.obj');
134 
135   if objList[numObj] = 'helico.obj' then
136   begin
137     with obj.MainPart do
138     begin
139       rotated := CreatePart;
140       rotateCenter := Point3D(0,0,0);
141       for i := VertexCount-1 downto 0 do
142           if (Vertex[i].SceneCoord.y >= 22.2) then
143           begin
144             rotated.Add(Vertex[i]);
145             rotateCenter.Offset(Vertex[i].SceneCoord);
146           end;
147       rotateCenter.Scale(1/rotated.VertexCount);
148       obj.SeparatePart(rotated);
149       obj.MainPart.Scale(2,2,2);
150     end;
151   end else
152     rotated := nil;
153 
154   obj.LightingNormal := lnVertex;
155   if objList[numObj] = 'teapot.obj' then
156     for i := 0 to obj.FaceCount-1 do
157       obj.Face[i].Biface := true;
158 
159   with obj.MainPart.BoundingBox do
160     obj.MainPart.Translate((min+max)*(-1/2), False);
161   r := obj.MainPart.Radius;
162   if r <> 0 then obj.MainPart.Scale(40/r, False);
163   if objList[numObj] = 'lampe.obj' then
164   begin
165     obj.MainPart.RotateXDeg(180, False);
166     obj.MainPart.Scale(1.5,1.5,1.5);
167   end else
168   if objList[numObj] = 'mario.obj' then
169     obj.MainPart.RotateXDeg(90, False)
170   else
171   begin
172     obj.MainPart.RotateXDeg(180-20, False);
173     obj.MainPart.RotateYDeg(-20, False);
174     if objList[numObj] = 'trumpet.obj' then
175        obj.MainPart.Scale(2,2,2,False);
176   end;
177 
178   if objList[numObj] = 'lampe.obj' then
179   begin
180     AmbiantLightness := 0.7;
181     AddPointLight(obj.MainPart.Add(0,7.7,0),10);
182     AddPointLight(obj.MainPart.Add(1.9,6.5,0),10);
183     AddPointLight(obj.MainPart.Add(-0.9,6.5,1.5),10);
184     AddPointLight(obj.MainPart.Add(-0.9,6.5,-1.7),10);
185   end
186   else
187   begin
188     //set ambiant lightness to dark (1 is normal lightness)
189     AmbiantLightness := 0.5;
190     if objList[numObj] = 'helice.obj' then
191       AddDirectionalLight(Point3D(1,1,1),0.75,-0.5)
192     else
193       AddDirectionalLight(Point3D(1,1,1),1,-0.5); //add a directional light from top-left, maximum lightness will be 0.5 + 1 = 1.5
194   end;
195 
196   RenderingOptions.PerspectiveMode:= pmZBuffer;
197   if objList[numObj] = 'helice.obj' then
198     RenderingOptions.LightingInterpolation := liAlwaysHighQuality
199   else
200     RenderingOptions.LightingInterpolation := liSpecularHighQuality;
201 end;
202 
203 procedure TExample4.Render;
204 var fx: TBGRATextEffect;
205 begin
206   if objList[numObj] = 'teapot.obj' then
207     Surface.GradientFill(0,0,Surface.Width,Surface.Height,BGRABlack,BGRA(70,100,100),gtLinear,PointF(0,0),PointF(0,Surface.Height),dmSet) else
208   if objList[numObj] = 'lampe.obj' then
209     Surface.Fill(BGRA(0,0,60));
210 
211   inherited Render;
212 
213   if message <> '' then
214   begin
215     fx := TBGRATextEffect.Create(message,'Arial',20,True);
216     fx.DrawOutline(Surface,Surface.Width div 2,Surface.Height div 2-fx.TextHeight div 2,BGRABlack,taCenter);
217     fx.Draw(Surface,Surface.Width div 2,Surface.Height div 2-fx.TextHeight div 2,BGRAWhite,taCenter);
218     fx.Free;
219   end else
220     Surface.TextOut(Surface.Width,0,objList[numObj],BGRAWhite,taRightJustify);
221 end;
222 
223 procedure TExample4.Elapse;
224 begin
225   if rotated <> nil then
226   begin
227     rotated.Translate(-rotateCenter,false);
228     rotated.RotateYDeg(20,False);
229     rotated.Translate(rotateCenter,false);
230   end;
231 end;
232 
233 procedure TExample4.RenderGL(ACanvas: TBGLCustomCanvas; AMaxZ: single);
234 begin
235   if objList[numObj] = 'teapot.obj' then
236     ACanvas.FillRectLinearColor(0,0,BGLCanvas.Width,BGLCanvas.Height,
237        BGRABlack,BGRABlack,
238        BGRA(70,100,100),BGRA(70,100,100),
239        False) else
240   if objList[numObj] = 'lampe.obj' then
241     ACanvas.Fill(BGRA(0,0,60));
242 
243   inherited RenderGL(ACanvas, AMaxZ);
244 end;
245 
246 procedure TExample4.NextModel;
247 begin
248   inc(numObj);
249   if numObj = length(objList) then numObj := 0;
250 
251   CreateScene;
252 end;
253 
254 end.
255 
256