lace_opengl_0.1.0_672a6415/source/lean/renderer/opengl-impostorer.adb

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
with
     openGL.Camera,
     openGL.Impostor.simple,
     openGL.Impostor.terrain,

     ada.Containers.generic_Array_sort,
     ada.unchecked_Deallocation;

package body openGL.Impostorer
is
   ---------
   --- Forge
   --

   procedure define (Self : in out Item)
   is
   begin
      Self.impostor_size_Min.Value_is (0.0625);
   end define;



   procedure destruct (Self : in out Item)
   is
      procedure deallocate is new ada.unchecked_Deallocation (impostor_load_Balancer.Slots,
                                                              impostor_load_Balancer.Slots_view);
   begin
      deallocate (Self.impostor_load_Slots);

      declare
         use Impostor,
             visual_Maps_of_impostor;

         the_Impostor : Impostor.view;
         Cursor       : visual_Maps_of_impostor.Cursor := Self.visual_Map_of_imposter.First;
      begin
         while has_Element (Cursor)
         loop
            the_Impostor := Element (Cursor);
            Self.Renderer.free (the_Impostor);

            next (Cursor);
         end loop;
      end;
   end destruct;


   --------------
   --- Attributes
   --

   function impostor_Count (Self : in Item) return Natural
   is
   begin
      return Natural (Self.visual_Map_of_imposter.Length);
   end impostor_Count;



   function impostor_size_Min (Self : in Item'Class) return Real
   is
   begin
      return Self.impostor_size_Min.Value;
   end impostor_size_Min;


   procedure impostor_size_Min_is (Self : in out Item'Class;   Now : in Real)
   is
   begin
      Self.impostor_size_Min.Value_is (Now);
   end impostor_size_Min_is;



   function Camera (Self : in Item'Class) return access openGL.Camera.item'Class
   is
   begin
      return Self.Camera;
   end Camera;


   procedure Camera_is (Self : in out Item'Class;   Now : access openGL.Camera.item'Class)
   is
   begin
      Self.Camera := Camera_view (Now);
   end Camera_is;



   function Renderer (Self : in Item'Class) return openGL.Renderer.lean.view
   is
   begin
      return openGL.Renderer.lean.view (Self.Renderer);
   end Renderer;


   procedure Renderer_is (Self : in out Item'Class;   Now : in openGL.Renderer.lean.view)
   is
   begin
      Self.Renderer := Renderer_view (Now);
   end Renderer_is;


   --------------
   --  Operations
   --

   procedure substitute (Self : in out Item;   the_Visuals : in out openGL.Visual.views;
                                               Camera      : access openGL.Camera.item'Class)
   is
   begin
      -- Find whether visual or imposter is used, for each object.
      --
      declare
         transposed_camera_Attitude : constant Matrix_3x3 := Transpose (Camera.Spin);

         Impostor_updates           :          openGL.Renderer.lean.impostor_Updates (1 .. 20_000);
         impostor_updates_Last      :          Natural    := 0;

         procedure add (the_Impostor : in Impostor.view)
         is
         begin
            impostor_updates_Last                    := impostor_updates_Last + 1;
            Impostor_updates (impostor_updates_Last) := (Impostor              => the_Impostor,
                                                         current_Width_pixels  => the_Impostor.current_Width_pixels,
                                                         current_Height_pixels => the_Impostor.current_Height_pixels,

                                                         current_copy_x_Offset => the_Impostor.current_copy_X_Offset,
                                                         current_copy_y_Offset => the_Impostor.current_copy_Y_Offset,
                                                         current_copy_X        => the_Impostor.current_copy_X,
                                                         current_copy_Y        => the_Impostor.current_copy_Y,
                                                         current_copy_Width    => the_Impostor.current_copy_Width,
                                                         current_copy_Height   => the_Impostor.current_copy_Height,

                                                         current_Camera_look_at_Rotation => the_Impostor.current_Camera_look_at_Rotation);
            the_Impostor.freshen_Count := 0;
            the_Impostor.never_Updated := False;
         end add;

         the_impostor_size_Min : constant Real := Self.impostor_size_Min.Value;

      begin
         for Each in Self.impostor_load_Slots'Range
         loop
            Self.impostor_load_Slots (Each).impostors_Count := 0;        -- Empty each slot's contents.
         end loop;

         for i in the_Visuals'Range
         loop
            declare
               the_Visual   : Visual  .view renames the_Visuals (i);
               the_Impostor : Impostor.view;
            begin
               -- Replace the visual with the impostors visual, if the visuals apparent size is small enough.
               --
               if the_Visual.apparent_Size < the_impostor_size_Min
               then   -- Use impostor.
                  -- Find or create the impostor for the visual.
                  --
                  declare
                     use visual_Maps_of_impostor;
                  begin
                     the_Impostor := Self.visual_Map_of_imposter.Element (the_Visual);
                  exception
                     when constraint_Error =>   -- No impostor exists for this visual yet, so create one.
                        if the_Visual.is_Terrain
                        then
                           the_Impostor := new Impostor.terrain.item;
                        else
                           the_Impostor := new Impostor.simple.item;

                           the_Impostor.set_size_update_trigger_Delta        (to =>  10);
                           the_Impostor.set_freshen_count_update_trigger_Mod (to => 250);
                        end if;

                        the_Impostor.set_Target (the_Visual);
                        Self.visual_Map_of_imposter.insert (the_Visual, the_Impostor);
                  end;

                  declare
                     use Visual;

                     impostor_Target          : Visual.view renames the_Visual;

                     Impostor_update_required : constant Boolean := the_Impostor.update_Required (Camera);
                     Impostor_is_valid        : constant Boolean := the_Impostor.is_Valid;
                     Impostor_never_updated   : constant Boolean := the_Impostor.never_Updated;

                  begin
                     if Impostor_is_valid
                     then
                        if Impostor_update_required
                        then
                           the_Impostor.target_camera_Distance_less_frame_Count :=         the_Impostor.target_camera_Distance
                                                                                   - Real (the_Impostor.frame_Count_since_last_update);
                           if Impostor_never_updated
                           then
                              add (the_Impostor);
                           else
                              declare  -- Add impostor to appropriate load balancing slot.
                                 target_face_Count : constant Positive := impostor_Target.face_Count;

                                 function Slot_Id return Positive
                                 is
                                 begin
                                    for Each in Self.impostor_load_Slots'Range
                                    loop
                                       if target_face_Count <= Self.impostor_load_Slots (Each).max_Faces
                                       then
                                          return Each;
                                       end if;
                                    end loop;

                                    raise Program_Error;
                                 end Slot_Id;

                                 the_Slot : impostor_load_Balancer.Slot renames Self.impostor_load_Slots (Slot_Id);
                              begin
                                 the_Slot.impostors_Count                      := the_Slot.impostors_Count + 1;
                                 the_Slot.Impostors (the_Slot.impostors_Count) := the_Impostor;
                              end;
                           end if;
                        end if;

                        the_Impostor.Visual.Site_is (Site_of (the_Visual.all));
                        the_Impostor.Visual.Spin_is (transposed_camera_Attitude);

                        the_Visuals (i) := the_Impostor.Visual;   -- Replace the visual with the impostor.
                     end if;
                  end;

               else   -- Don't use impostor.
                  null;
               end if;
            end;
         end loop;


         -- Add the load balanced impostor updates.
         --
         for i in Self.impostor_load_Slots'Range
         loop
            declare
               the_Slot    : impostor_load_Balancer.Slot renames Self.impostor_load_Slots (i);
               num_Updates : constant Natural            :=      Natural'Min (the_Slot.max_Updates,
                                                                              the_Slot.impostors_Count);
               function "<" (Left, Right : in Impostor.view) return Boolean
               is
               begin
                  return   Left .target_camera_Distance_less_frame_Count      -- Subtracting 'frame count' allows distant targets a chance of
                         < Right.target_camera_Distance_less_frame_Count;     -- update. (TODO: Need some sort of user-settable scale parameter
               end "<";                                                       --                to allow for very large scales such as space).

               procedure sort is new ada.Containers.generic_Array_sort (Positive,
                                                                        Impostor.view,
                                                                        Impostor.views);
            begin
               sort (the_Slot.Impostors (1 .. the_Slot.impostors_Count));

               for Each in 1 .. num_Updates
               loop
                  add (the_Slot.Impostors (Each));
               end loop;
            end;
         end loop;

         Self.Renderer.queue_Impostor_updates (Impostor_updates (1 .. impostor_updates_Last),
                                               Camera);
      end;

   end substitute;


end openGL.Impostorer;