ide/FindInFilesDialog: Fixed memory corruption
[sdk] / samples / 3D / orbitWithMouse / orbitWithMouse.ec
1 import "ecere"
2 #include <GL/gl.h>
3
4 #define WINDOW_WIDTH 768
5 #define WINDOW_HEIGHT 480
6
7 #define SCALE ((float)virtualDesktop.clientSize.h / WINDOW_HEIGHT)
8
9 //#define FULLSCREEN
10
11 class Window3D : struct
12 {
13    Object cube, front, back, side;
14    Bitmap bitmap;
15    Material frontMaterial { };
16    Material sideMaterial { };
17    Material backMaterial { };
18    int id;
19    Degrees position, wantedPosition;
20    Euler offset;
21 };
22
23 static const char * iconFiles[] =
24 {
25    ":icon1.png",
26    ":icon2.png",
27    ":icon3.png",
28    ":icon4.png",
29    ":icon5.png",
30    ":icon6.png",
31    ":icon7.png",
32    ":icon8.png",
33    ":icon9.png",
34    ":icon10.png"
35 };
36
37 #define NUM_ICONS (sizeof(iconFiles)/sizeof(char *))
38 // #define DOCK_HEIGHT  500
39 #define DOCK_HEIGHT  400
40 #define FULL_SWITCH     ((int)dock.iconBitmaps[0].width)
41 #define SMALL_SCALE  0.6f
42 #define SWITCH_SPEED 700
43 #define DOCK_TIMER   1
44
45 #define SLIDING_SPEED      0.5
46 #define SWITCHING_SPEED    0.5
47 #define ENTERING_SPEED     0.5
48
49 #define ORBIT_SPEED  Degrees { 10 }
50
51 #define ORBIT_HEIGHT 1000
52 // #define ORBIT_HEIGHT 500
53
54 #define NUM_WINDOWS  6
55
56 typedef struct
57 {
58    Bitmap iconBitmaps[NUM_ICONS];
59    int currentIcon;
60    float speed, position;
61    Timer timer;
62    Seconds lastTime;
63 } Dock;
64
65 static Seconds lastTime;
66
67 #define THICKNESS 20
68
69 static bool CreateBack(Mesh mesh, int width, int height, int depth, DisplaySystem displaySystem)
70 {
71    bool result = false;
72
73    if(mesh)
74    {
75       if(mesh.Allocate(MeshFeatures { vertices = true, texCoords1 = true }, 24, displaySystem))
76       {
77          Vector3Df vertices[24] =
78          {
79             { -(float)width/2,-(float)height/2,-(float)depth/2 },
80             {  (float)width/2,-(float)height/2,-(float)depth/2 },
81             {  (float)width/2, (float)height/2,-(float)depth/2 },
82             { -(float)width/2, (float)height/2,-(float)depth/2 },
83             { -(float)width/2,-(float)height/2, (float)depth/2 },
84             {  (float)width/2,-(float)height/2, (float)depth/2 },
85             {  (float)width/2, (float)height/2, (float)depth/2 },
86             { -(float)width/2, (float)height/2, (float)depth/2 },
87
88             { -(float)width/2,-(float)height/2,-(float)depth/2 },
89             {  (float)width/2,-(float)height/2,-(float)depth/2 },
90             {  (float)width/2, (float)height/2,-(float)depth/2 },
91             { -(float)width/2, (float)height/2,-(float)depth/2 },
92             { -(float)width/2,-(float)height/2, (float)depth/2 },
93             {  (float)width/2,-(float)height/2, (float)depth/2 },
94             {  (float)width/2, (float)height/2, (float)depth/2 },
95             { -(float)width/2, (float)height/2, (float)depth/2 },
96
97             { -(float)width/2,-(float)height/2,-(float)depth/2 },
98             {  (float)width/2,-(float)height/2,-(float)depth/2 },
99             {  (float)width/2, (float)height/2,-(float)depth/2 },
100             { -(float)width/2, (float)height/2,-(float)depth/2 },
101             { -(float)width/2,-(float)height/2, (float)depth/2 },
102             {  (float)width/2,-(float)height/2, (float)depth/2 },
103             {  (float)width/2, (float)height/2, (float)depth/2 },
104             { -(float)width/2, (float)height/2, (float)depth/2 }
105          };
106          Pointf texCoords[24] =
107          {
108             { 0, 0 }, { 1, 0 }, { 1, 1 }, { 0, 1 },
109             { 1, 0 }, { 0, 0 }, { 0, 1 }, { 1, 1 },
110             { 1, 0 }, { 0, 0 }, { 0, 1 }, { 1, 1 },
111             { 0, 0 }, { 1, 0 }, { 1, 1 }, { 0, 1 },
112             { 0, 1 }, { 1, 1 }, { 1, 1 }, { 0, 1 },
113             { 0, 0 }, { 1, 0 }, { 1, 0 }, { 0, 0 }
114          };
115          uint16 indices[4] =
116          {
117             5,6,7,4
118          };
119
120          CopyBytes(mesh.vertices, vertices, sizeof(vertices));
121          CopyBytes(((Pointf  *)mesh.texCoords), texCoords, sizeof(texCoords));
122
123          {
124             PrimitiveGroup group;
125             group = mesh.AddPrimitiveGroup(quads, 4);
126             if(group)
127             {
128                CopyBytes(group.indices, indices, sizeof(indices));
129                mesh.UnlockPrimitiveGroup(group);
130             }
131          }
132          mesh.ComputeNormals();
133          result = true;
134          mesh.Unlock(MeshFeatures{});
135       }
136    }
137    return result;
138 }
139
140 static bool CreateFront(Mesh mesh, int width, int height, int depth, DisplaySystem displaySystem)
141 {
142    bool result = false;
143    if(mesh)
144    {
145       if(mesh.Allocate(MeshFeatures { vertices = true, texCoords1 = true }, 24, displaySystem))
146       {
147          Vector3Df vertices[24] =
148          {
149             { -(float)width/2,-(float)height/2,-(float)depth/2 },
150             {  (float)width/2,-(float)height/2,-(float)depth/2 },
151             {  (float)width/2, (float)height/2,-(float)depth/2 },
152             { -(float)width/2, (float)height/2,-(float)depth/2 },
153             { -(float)width/2,-(float)height/2, (float)depth/2 },
154             {  (float)width/2,-(float)height/2, (float)depth/2 },
155             {  (float)width/2, (float)height/2, (float)depth/2 },
156             { -(float)width/2, (float)height/2, (float)depth/2 },
157
158             { -(float)width/2,-(float)height/2,-(float)depth/2 },
159             {  (float)width/2,-(float)height/2,-(float)depth/2 },
160             {  (float)width/2, (float)height/2,-(float)depth/2 },
161             { -(float)width/2, (float)height/2,-(float)depth/2 },
162             { -(float)width/2,-(float)height/2, (float)depth/2 },
163             {  (float)width/2,-(float)height/2, (float)depth/2 },
164             {  (float)width/2, (float)height/2, (float)depth/2 },
165             { -(float)width/2, (float)height/2, (float)depth/2 },
166
167             { -(float)width/2,-(float)height/2,-(float)depth/2 },
168             {  (float)width/2,-(float)height/2,-(float)depth/2 },
169             {  (float)width/2, (float)height/2,-(float)depth/2 },
170             { -(float)width/2, (float)height/2,-(float)depth/2 },
171             { -(float)width/2,-(float)height/2, (float)depth/2 },
172             {  (float)width/2,-(float)height/2, (float)depth/2 },
173             {  (float)width/2, (float)height/2, (float)depth/2 },
174             { -(float)width/2, (float)height/2, (float)depth/2 }
175          };
176          Pointf texCoords[24] =
177          {
178             { 0, 0 }, { 1, 0 }, { 1, 1 }, { 0, 1 },
179             { 1, 0 }, { 0, 0 }, { 0, 1 }, { 1, 1 },
180             { 1, 0 }, { 0, 0 }, { 0, 1 }, { 1, 1 },
181             { 0, 0 }, { 1, 0 }, { 1, 1 }, { 0, 1 },
182             { 0, 1 }, { 1, 1 }, { 1, 1 }, { 0, 1 },
183             { 0, 0 }, { 1, 0 }, { 1, 0 }, { 0, 0 }
184          };
185          uint16 indices[4] =
186          {
187             0,3,2,1
188          };
189
190          CopyBytes(mesh.vertices, vertices, sizeof(vertices));
191          CopyBytes(((Pointf  *)mesh.texCoords), texCoords, sizeof(texCoords));
192
193          {
194             PrimitiveGroup group;
195             group = mesh.AddPrimitiveGroup(quads, 4);
196             if(group)
197             {
198                CopyBytes(group.indices, indices, sizeof(indices));
199                mesh.UnlockPrimitiveGroup(group);
200             }
201          }
202          mesh.ComputeNormals();
203          result = true;
204          mesh.Unlock(MeshFeatures {});
205       }
206    }
207    return result;
208 }
209
210 static bool CreateSide(Mesh mesh, int width, int height, int depth, DisplaySystem displaySystem)
211 {
212    bool result = false;
213    if(mesh)
214    {
215       if(mesh.Allocate(MeshFeatures { vertices = true, texCoords1 = true }, 24, displaySystem))
216       {
217          Vector3Df vertices[24] =
218          {
219             { -(float)width/2,-(float)height/2,-(float)depth/2 },
220             {  (float)width/2,-(float)height/2,-(float)depth/2 },
221             {  (float)width/2, (float)height/2,-(float)depth/2 },
222             { -(float)width/2, (float)height/2,-(float)depth/2 },
223             { -(float)width/2,-(float)height/2, (float)depth/2 },
224             {  (float)width/2,-(float)height/2, (float)depth/2 },
225             {  (float)width/2, (float)height/2, (float)depth/2 },
226             { -(float)width/2, (float)height/2, (float)depth/2 },
227
228             { -(float)width/2,-(float)height/2,-(float)depth/2 },
229             {  (float)width/2,-(float)height/2,-(float)depth/2 },
230             {  (float)width/2, (float)height/2,-(float)depth/2 },
231             { -(float)width/2, (float)height/2,-(float)depth/2 },
232             { -(float)width/2,-(float)height/2, (float)depth/2 },
233             {  (float)width/2,-(float)height/2, (float)depth/2 },
234             {  (float)width/2, (float)height/2, (float)depth/2 },
235             { -(float)width/2, (float)height/2, (float)depth/2 },
236
237             { -(float)width/2,-(float)height/2,-(float)depth/2 },
238             {  (float)width/2,-(float)height/2,-(float)depth/2 },
239             {  (float)width/2, (float)height/2,-(float)depth/2 },
240             { -(float)width/2, (float)height/2,-(float)depth/2 },
241             { -(float)width/2,-(float)height/2, (float)depth/2 },
242             {  (float)width/2,-(float)height/2, (float)depth/2 },
243             {  (float)width/2, (float)height/2, (float)depth/2 },
244             { -(float)width/2, (float)height/2, (float)depth/2 }
245          };
246          Pointf texCoords[24] =
247          {
248             { 0, 0 }, { 1, 0 }, { 1, 1 }, { 0, 1 },
249             { 1, 0 }, { 0, 0 }, { 0, 1 }, { 1, 1 },
250             { 1, 0 }, { 0, 0 }, { 0, 1 }, { 1, 1 },
251             { 0, 0 }, { 1, 0 }, { 1, 1 }, { 0, 1 },
252             { 0, 1 }, { 1, 1 }, { 1, 1 }, { 0, 1 },
253             { 0, 0 }, { 1, 0 }, { 1, 0 }, { 0, 0 }
254          };
255          uint16 indices[16] =
256          {
257             // up, down, right, left
258             17,21,20,16,
259             22,18,19,23,
260             9,10,14,13,
261             12,15,11,8
262          };
263
264          CopyBytes(mesh.vertices, vertices, sizeof(vertices));
265          CopyBytes(((Pointf  *)mesh.texCoords), texCoords, sizeof(texCoords));
266
267          {
268             PrimitiveGroup group;
269             group = mesh.AddPrimitiveGroup(quads, 16);
270             if(group)
271             {
272                CopyBytes(group.indices, indices, sizeof(indices));
273                mesh.UnlockPrimitiveGroup(group);
274             }
275          }
276          mesh.ComputeNormals();
277          result = true;
278          mesh.Unlock(MeshFeatures{});
279       }
280    }
281    return result;
282 }
283
284 class Desktop3D : Window
285 {
286    text = "Orbiting 3D Desktop";
287 #ifdef FULLSCREEN
288    anchor = { 0, 0, 0, 0 };
289 #else
290    size = Size { WINDOW_WIDTH, WINDOW_HEIGHT };
291 #endif
292
293    Object lookAt {};
294    Camera camera
295    {
296       lookAt, position = {0, ORBIT_HEIGHT, -2000}, target = lookAt, eulerOrientation = Euler { pitch = 15 },
297       zMin = 1, zMax = 10000, fovDirection = vertical, fov = 53
298    };
299    Light light;
300    bool moving;
301    bool movingCamera;
302    Vector3Df lastMouse;
303    // Object knot;
304    Bitmap puzzle;
305
306    Window clippedWindow;
307    Box clipBox;
308
309    Dock dock;
310    Degrees globalPosition;
311    int numWindows;
312
313    Window poppingWindow;
314    float poppingPercent;
315    float poppingBiggest;
316
317    // Camera Sliding
318    Quaternion fromAngle, toAngle;
319    Vector3D fromPosition, toPosition;
320    float sliding;    // Camera sliding from the bottom position looking at the orbit to looking at 1 window
321    float switching;  // Switching from looking at one application to the next within the orbit
322    float entering;
323    bool dockHidden;
324    bool fullScreen;
325
326    Timer dockTimer
327    {
328       this, 0;
329
330       bool DelayExpired()
331       {
332          Seconds time = GetTime();
333          Seconds diffTime = time - dock.lastTime;
334          dock.lastTime = time;
335
336          dock.position += (float)(dock.speed * diffTime);
337
338          if(dock.position >= FULL_SWITCH)
339          {
340             dock.position = 0;
341             dock.speed = 0;
342             dock.currentIcon++;
343             if(dock.currentIcon >= NUM_ICONS)
344                dock.currentIcon -= NUM_ICONS;
345             dockTimer.Stop();
346          }
347          else if(dock.position <= -FULL_SWITCH)
348          {
349             dock.position = 0;
350             dock.speed = 0;
351             dock.currentIcon--;
352             if(dock.currentIcon < 0)
353                dock.currentIcon += NUM_ICONS;
354             dockTimer.Stop();
355          }
356          return true;
357       }
358    };
359
360    Timer timer
361    {
362       this, 0.05;
363
364       bool DelayExpired()
365       {
366          //int buttons;
367          Seconds time = GetTime();
368          Seconds diffTime = (time - lastTime);
369          Window child;
370          Degrees biggest = 0;
371
372          //GetMouseState(&buttons, null, null);
373
374          /*
375          eObject_RotateEuler(knot, P3D(diffTime / 10, diffTime / 10, diffTime / 10),
376             P3D(0,0,0), P3D(0,0,0));
377             */
378          Update(null); //Desktop3DSetDesktopDirty();
379
380          /*
381          if(buttons & MOUSE_RIGHT)
382          {
383             eCamera_Move(camera, P3D(0,0,1000 * diffTime));
384             window.dirty = true;
385          }
386          else if(buttons & MOUSE_MIDDLE)
387          {
388             eCamera_Move(camera, P3D(0,0,-1000 * diffTime));
389             window.dirty = true;
390          }
391          */
392
393          // Orbit Rotation
394          if(!fullScreen)
395          {
396             if(sliding == 1 && switching == 1 && entering == 1)
397             {
398                globalPosition += (double)diffTime * ORBIT_SPEED;
399
400                for(child = firstChild; child; child = child.next)
401                {
402                   if(child.display != display)
403                   {
404                      Window3D window3D = Desktop3DGetWindowHandle(child);
405
406                      if(window3D.position < window3D.wantedPosition - 0.000001)
407                      {
408                         window3D.wantedPosition += (double)diffTime * ORBIT_SPEED;
409                         window3D.position += (double)diffTime * (window3D.wantedPosition - window3D.position);
410                         window3D.position = Min(window3D.position, window3D.wantedPosition);
411                         biggest = Max(biggest, window3D.wantedPosition - window3D.position);
412                      }
413                      else if(window3D.position > window3D.wantedPosition + 0.000001)
414                      {
415                         window3D.wantedPosition += (double)diffTime * ORBIT_SPEED;
416                         window3D.position -= (double)diffTime * (window3D.position - window3D.wantedPosition);
417                         window3D.position = Max(window3D.position, window3D.wantedPosition);
418                         biggest = Max(biggest, window3D.position - window3D.wantedPosition);
419                      }
420                      else
421                      {
422                         window3D.wantedPosition += (double)diffTime * ORBIT_SPEED;
423                         window3D.position += (double)diffTime * ORBIT_SPEED;
424                      }
425                   }
426                }
427             }
428          }
429
430       #define FIRST_2WINDOWS_POPUP_SPEED     0.5
431
432          if(numWindows < 3)
433          {
434             poppingPercent += (float)(diffTime * FIRST_2WINDOWS_POPUP_SPEED);
435             if(poppingPercent < 1)
436                poppingPercent += (float)((1.0 - poppingPercent) * diffTime * FIRST_2WINDOWS_POPUP_SPEED);
437             if(poppingPercent > 0.99999)
438             {
439                poppingWindow = null;
440                poppingPercent = 1.0f;
441             }
442          }
443          else if(biggest < 0.000001)
444          {
445             poppingPercent = 1;
446             poppingWindow = null;
447          }
448          else
449             poppingPercent = (float)(1.0 - (Radians)(biggest / poppingBiggest));
450          poppingPercent = Min(poppingPercent, 1.0);
451
452          if(dockHidden && virtualDesktop.activeChild && entering == 1)
453          {
454             Window window = virtualDesktop.activeChild;
455             Window3D window3D = Desktop3DGetWindowHandle(window);
456
457             lookAt.transform.position = window3D.cube.transform.position;
458             lookAt.UpdateTransform();
459             camera.Update();
460             toPosition = camera.cPosition;
461             toAngle = camera.cOrientation;
462          }
463
464          if(sliding < 1 || switching < 1 || entering < 1)
465          {
466             Quaternion angle;
467             Vector3D position;
468             float factor;
469
470             if(sliding < 1)
471             {
472                sliding += (float)(diffTime * SLIDING_SPEED);
473                sliding = Min(sliding, 1.0);
474                factor = sliding;
475             }
476             else if(switching < 1)
477             {
478                switching += (float)(diffTime * SWITCHING_SPEED);
479                switching = Min(switching, 1.0);
480                factor = switching;
481             }
482             else if(entering < 1)
483             {
484                entering += (float)(diffTime * ENTERING_SPEED);
485                entering = Min(entering, 1.0);
486                factor = entering;
487             }
488
489             angle.Slerp(fromAngle, toAngle, factor);
490
491             position.x = fromPosition.x + factor * (toPosition.x - fromPosition.x);
492             position.y = fromPosition.y + factor * (toPosition.y - fromPosition.y);
493             position.z = fromPosition.z + factor * (toPosition.z - fromPosition.z);
494
495             camera.AdjustAngle(angle);
496             camera.AdjustPosition(position);
497          }
498
499          Update(null);
500
501          lastTime = time;
502
503          return true;
504       }
505    };
506
507    SkyBox sky { size = { 10000, 10000, 10000 }, folder = ":skycube", extension = "jpg" };
508
509    bool OnCreate()
510    {
511       camera.Update();
512
513       fromPosition = toPosition = camera.cPosition;
514       fromAngle = toAngle = camera.cOrientation;
515
516       // Start with dock off
517       // sliding = 1;
518       switching = 1;
519       entering = 1;
520
521       light.diffuse = white;
522       light.specular = white;
523       light.orientation = Euler {  }; //Quaternion { 1,0,Pi,0 };
524
525       timer.Start();
526
527       return true;
528    }
529
530    void OnResize(int width, int height)
531    {
532       camera.Setup(width, height, null);
533    }
534
535    bool OnLoadGraphics()
536    {
537       int c;
538
539       sky.Create(displaySystem);
540
541       /*
542       eObject_Load(&knot, ":eknot.3ds", null, display);
543       knot->transform.scaling = P3D(10,10,10);
544       knot->transform.position = P3D(0,0, 2000);
545       */
546
547       // puzzle = eBitmap_Load(":puzzle.jpg", null, display);
548
549       // dock.background = Bitmap {};
550       // dock.background.Load(":background.jpg", null, displaySystem);
551       for(c = 0; c<NUM_ICONS; c++)
552       {
553          dock.iconBitmaps[c] = Bitmap {};
554          dock.iconBitmaps[c].Load(iconFiles[c], null, displaySystem);
555       }
556       lastTime = GetTime();
557       return true;
558    }
559
560    void OnUnloadGraphics()
561    {
562       int c;
563
564       //eObject_Free(cube, display);
565
566       for(c = 0; c<NUM_ICONS; c++)
567          dock.iconBitmaps[c].Free();
568    }
569
570    #define DOCK_SLIDING_DISTANCE    180
571
572    void Dock_OnRedraw(Surface surface)
573    {
574       int c, icon = dock.currentIcon;
575       float x = -120 - dock.position, starty = DOCK_HEIGHT;
576       byte alpha = 255;
577
578       if(sliding < 1)
579       {
580          if(dockHidden)
581          {
582             starty += sliding * DOCK_SLIDING_DISTANCE;
583             alpha = 255 - (byte)(255*sliding);
584          }
585          else
586          {
587             starty += DOCK_SLIDING_DISTANCE - (sliding * DOCK_SLIDING_DISTANCE);
588             alpha = (byte)(255*sliding);
589          }
590       }
591
592       if(!dockHidden || sliding < 1)
593       {
594          icon -= 2;
595          if(icon < 0) icon += NUM_ICONS;
596       /*
597          eSurface_Blit(surface, dock.background, 0,0,0,0,
598             dock.background->width, dock.background->height);
599       */
600          for(c = 0; c<NUM_ICONS; c++)
601          {
602             Bitmap bitmap;
603             int dx, dy, w, h;
604             float scaling = SMALL_SCALE;
605             float y;
606
607             bitmap = dock.iconBitmaps[icon];
608
609             y = starty;
610
611             if(dock.speed > 0)
612             {
613                float a = dock.position / FULL_SWITCH;
614                if(icon == dock.currentIcon)
615                {
616                   // The old one
617                   scaling = (float)(1 - (1-SMALL_SCALE) * a);
618                }
619                else if(icon == (dock.currentIcon + 1) % NUM_ICONS)
620                {
621                   // The upcoming one
622                   scaling = (float)(SMALL_SCALE + (1-SMALL_SCALE) * a);
623                }
624             }
625             else if(dock.speed < 0)
626             {
627                float a = -dock.position / FULL_SWITCH;
628                if(icon == dock.currentIcon)
629                {
630                   // The old one
631                   scaling = 1 - (1-SMALL_SCALE) * a;
632                }
633                else if((icon + 1) % NUM_ICONS == dock.currentIcon)
634                {
635                   // The upcoming one
636                   scaling = SMALL_SCALE + (1-SMALL_SCALE) * a;
637                }
638             }
639             else if(icon == dock.currentIcon)
640             {
641                scaling = 1;
642                if(poppingWindow)
643                {
644                   float percent = poppingPercent; // * poppingPercent * poppingPercent * poppingPercent;
645                   y += (float) sin(percent * 2*Pi*3) * 4 + 0.5f;
646                }
647             }
648
649             w = (int)(bitmap.width * scaling);
650             h = (int)(bitmap.height * scaling);
651
652             dx = (int)(x - w/2);
653             dy = (int)(y - h/2);
654
655             surface.SetForeground(ColorAlpha{alpha, white});
656             surface.Stretch(bitmap, (int)(dx * SCALE), (int)(dy * SCALE), 0,0, (int)(w * SCALE), (int)(h * SCALE), bitmap.width, bitmap.height);
657
658             x += FULL_SWITCH;
659
660             icon ++;
661             if(icon == NUM_ICONS) icon = 0;
662          }
663       }
664    }
665
666    #define ORBIT_RADIUS    800
667
668    void OnRedraw(Surface surface)
669    {
670       Window child;
671
672       surface.Clear(depthBuffer);
673
674       //PrintLn("position: ", camera.cPosition);
675       display.SetLight(0, &light);
676       display.SetCamera(surface, camera);
677
678       sky.Render(camera, display);
679
680       for(child = firstChild; child; child = child.next)
681       {
682          if(child.is3D)
683          {
684             Window3D window3D = Desktop3DGetWindowHandle(child);
685             Mesh mesh = window3D.front.mesh;
686             int w, h;
687
688             if(mesh.Lock(MeshFeatures { texCoords1 = true }))
689             {
690                ((Pointf  *)mesh.texCoords)[3].x = 0;
691                ((Pointf  *)mesh.texCoords)[3].y = 0;
692                ((Pointf  *)mesh.texCoords)[2].x = (float)child.size.w / window3D.bitmap.width;
693                ((Pointf  *)mesh.texCoords)[2].y = 0;
694                ((Pointf  *)mesh.texCoords)[1].x = (float)child.size.w / window3D.bitmap.width;
695                ((Pointf  *)mesh.texCoords)[1].y = (float)child.size.h / window3D.bitmap.height;
696                ((Pointf  *)mesh.texCoords)[0].x = 0;
697                ((Pointf  *)mesh.texCoords)[0].y = (float)child.size.h / window3D.bitmap.height;
698                mesh.Unlock(MeshFeatures {});
699             }
700
701             {
702                float alpha = 0.5f;
703
704                if(child == poppingWindow)
705                   alpha = 0.5f * poppingPercent * poppingPercent * poppingPercent;
706                else if(child == virtualDesktop.activeChild)
707                {
708                   if(entering < 1)
709                   {
710                      if(fullScreen)
711                         alpha = 0.5f + entering / 2.0f;
712                      else
713                         alpha = 1.0f - entering / 2.0f;
714                   }
715                   else if(fullScreen)
716                      alpha = 1.0f;
717                }
718
719                window3D.frontMaterial.opacity = alpha;
720                window3D.sideMaterial.opacity = alpha;
721                window3D.backMaterial.opacity = alpha;
722             }
723
724             if(numWindows > 6)
725             {
726                w = (int)(2*Pi*ORBIT_RADIUS / (numWindows+1));
727                h = w * WINDOW_HEIGHT / WINDOW_WIDTH;
728             }
729             else
730             {
731                w = child.size.w;
732                h = child.size.h;
733             }
734
735             if(child == poppingWindow)
736             {
737                Vector3D finalPosition;
738                Vector3D startPositionProjected = { (-120 + 284) * SCALE, DOCK_HEIGHT * SCALE, 1.0f };
739                Vector3D startPositionView, startPositionWorld;
740
741                camera.Unproject(startPositionProjected, startPositionView);
742                camera.Untransform(startPositionView, startPositionWorld);
743
744                finalPosition.x = (float)(cos(window3D.position) * ORBIT_RADIUS);
745                finalPosition.y = 0;
746                finalPosition.z = (float)(sin(window3D.position) * ORBIT_RADIUS);
747
748                window3D.cube.transform = Transform
749                {
750                   position =
751                   {
752                      startPositionWorld.x + (finalPosition.x - startPositionWorld.x) * poppingPercent,
753                      startPositionWorld.y + (finalPosition.y - startPositionWorld.y) * poppingPercent,
754                      startPositionWorld.z + (finalPosition.z - startPositionWorld.z) * poppingPercent
755                   },
756                   orientation = Euler { window3D.position - 90 + poppingPercent * 2 * 360, 10, 0 },
757                   scaling = { w * poppingPercent, h * poppingPercent, 1 }
758                };
759             }
760             else
761             {
762                Vector3Df scaling = Vector3Df { 1,1,1 };
763
764                if(poppingWindow)
765                {
766                   int wb, hb;
767
768                   if(numWindows-1 > 6)
769                   {
770                      wb = (int)(2*Pi*ORBIT_RADIUS / (numWindows));
771                      hb = (int)(wb * WINDOW_HEIGHT / WINDOW_WIDTH);
772                   }
773                   else
774                   {
775                      wb = child.size.w;
776                      hb = child.size.h;
777                   }
778
779                   scaling.x = wb + (w - wb);// * poppingPercent;
780                   scaling.y = hb + (h - hb);// * poppingPercent;
781                }
782                else
783                {
784                   scaling.x = w;
785                   scaling.y = h;
786                }
787                window3D.cube.transform = Transform
788                {
789                   position = { (float)(cos(window3D.position) * ORBIT_RADIUS), 0, (float)(sin(window3D.position) * ORBIT_RADIUS) },
790                   orientation = Euler { window3D.position - 90 + window3D.offset.yaw, 10 + window3D.offset.pitch, 0 },
791                   scaling = { (float)scaling.x, (float)scaling.y, (float)scaling.z };
792                };
793             }
794
795             window3D.cube.UpdateTransform();
796
797             display.DrawObject(window3D.cube);
798          }
799       }
800
801       // display.DrawObject(knot);
802
803       display.SetCamera(null, null);
804
805       Dock_OnRedraw(surface);
806    }
807
808    Window GetCursorLocation(int x, int y, int * mx, int * my, bool * back)
809    {
810       Window window = null;
811       Window3D window3D;
812       Vector3D viewSpace, worldSpace, local;
813
814       display.StartSelection(x, y, 1, 1);
815       display.IntersectPolygons();
816       display.SetCamera(null, camera);
817
818       if(clippedWindow)
819       {
820          window = clippedWindow;
821          window3D = Desktop3DGetWindowHandle(window);
822          display.pickingPlanes = true;
823          display.DrawObject(window3D.cube.Find("Front"));
824       }
825       else
826       {
827          HitRecord hit;
828          OldList list;
829
830          display.CollectHits();
831          for(window = virtualDesktop.firstChild; window; window = window.next)
832          {
833             if(window.is3D)
834             {
835                window3D = Desktop3DGetWindowHandle(window);
836                display.DrawObject(window3D.cube);
837             }
838          }
839          display.GetHits(list);
840
841          hit = list.first;
842          if(hit && hit.numTags)
843          {
844             window = (Window)hit.tags[0];
845             window3D = Desktop3DGetWindowHandle(window);
846          }
847
848          list.Free(null);
849       }
850
851       if(window)
852       {
853          display.GetIntersect(viewSpace);
854          camera.Untransform(viewSpace, worldSpace);
855          local.DivideMatrix(worldSpace, window3D.cube.matrix);
856
857          *mx = (int)(local.x * window.size.w + window.size.w/2 + 0.5) + window.absPosition.x;
858          *my = (int)(local.y * window.size.h + window.size.h/2 + 0.5) + window.absPosition.y;
859          *back = local.z > 0;
860       }
861
862       display.SetCamera(null, null);
863       display.StopSelection();
864       display.pickingPlanes = false;
865
866       /*
867       if(clippedWindow)
868       {
869          int oldx = *mx, oldy = *my;
870          oldx = *mx;
871          oldy = *my;
872
873          if(*mx < clipBox.left)    *mx = clipBox.left;
874          if(*my < clipBox.top)     *my = clipBox.top;
875          if(*mx > clipBox.right)   *mx = clipBox.right;
876          if(*my > clipBox.bottom)  *my = clipBox.bottom;
877
878          if(oldx != *mx || oldy != *my)
879          {
880             Vector3Df viewSpace, worldSpace;
881
882             local.x = ((float) *mx - window.absPosition.x - window.size.w/2.0 + 0.5) / (float)window.size.w;
883             local.y = ((float) *my - window.absPosition.y - window.size.h/2.0 + 0.5) / (float)window.size.h;
884             local.z = -THICKNESS/2;
885
886             worldSpace.MultMatrix(local, matrix);
887             camera.TransformPoint(viewSpace, worldSpace);
888
889             camera.Project(viewSpace, point);
890
891             x = point.x + 0.5;
892             y = point.y + 0.5;
893             SetMousePosition(x, y);
894          }
895       }
896       */
897       return window;
898    }
899
900    bool OnMouseMove(int x, int y, Modifiers mods)
901    {
902       if(moving)
903       {
904          int dx = (int)(x - lastMouse.x), dy = (int)(lastMouse.y - y);
905          if(movingCamera)
906          {
907             camera.RotateYaw(-dx, 0,0);
908             camera.RotatePitch(dy, 0,0);
909          }
910          else
911          {
912             Window child = activeChild;
913             Window3D window3D = Desktop3DGetWindowHandle(child);
914             window3D.offset.yaw += dx;
915             window3D.offset.pitch += dy;
916          }
917          // window.dirty = true;
918          Update(null); // ?
919          lastMouse.x = x;
920          lastMouse.y = y;
921       }
922       else
923       {
924          int mx, my;
925          bool back;
926          Window clickedWindow = GetCursorLocation(x,y, &mx, &my, &back);
927          if(clickedWindow)
928          {
929             if(Desktop3DMouseMessage(clickedWindow, 24, mx, my, &mods, false, true))
930             {
931             }
932          }
933       }
934
935       return true;
936    }
937
938    bool OnLeftButtonDown(int x, int y, Modifiers mods)
939    {
940       if(!moving)
941       {
942          int mx, my;
943          bool back;
944          Window clickedWindow = GetCursorLocation(x,y, &mx, &my, &back);
945          if(clickedWindow)
946          {
947             if(back || Desktop3DTitleBarClicked(clickedWindow, mx, my))
948             {
949                clickedWindow.Activate();
950                movingCamera = false;
951                Capture();
952                moving = true;
953             }
954             else if(Desktop3DMouseMessage(clickedWindow, 25, mx, my, &mods, false, true))
955             {
956             }
957          }
958          else
959          {
960             movingCamera = true;
961             Capture();
962             moving = true;
963          }
964
965          lastMouse.x = x;
966          lastMouse.y = y;
967       }
968
969       return true;
970    }
971
972    bool OnLeftButtonUp(int x, int y, Modifiers mods)
973    {
974       int mx, my;
975       bool back;
976
977       Window clickedWindow = GetCursorLocation(x, y, &mx, &my, &back);
978       if(clickedWindow)
979       {
980          Desktop3DMouseMessage(clickedWindow, 26, mx, my, &mods, false, true);
981
982       }
983
984       if(moving)
985       {
986          ReleaseCapture();
987          moving = false;
988       }
989       return true;
990    }
991
992    bool OnKeyHit(Key key, unichar character)
993    {
994       static int id = 0;
995       static const char * shotFileNames[] =
996       {
997          ":img1.jpg", ":img2.jpg", ":img3.jpg", ":img4.jpg", ":img5.jpg"
998       };
999
1000       switch(key)
1001       {
1002          case left:
1003             if(dockHidden)
1004             {
1005                Window3D window3D;
1006                Window ac = virtualDesktop.activeChild;
1007
1008                if(fullScreen || sliding < 1 || switching < 1 || entering < 1) break;
1009
1010                virtualDesktop.CycleChildren(true, false, false, true);
1011                if(ac == virtualDesktop.activeChild) break;
1012
1013                window3D = Desktop3DGetWindowHandle(virtualDesktop.activeChild);
1014
1015                fromAngle = camera.cOrientation;
1016                fromPosition = camera.cPosition;
1017
1018                lookAt.transform.position = window3D.cube.transform.position;
1019                lookAt.UpdateTransform();
1020                camera.position = {};
1021                camera.eulerOrientation = Euler{};
1022
1023                switching = 0;
1024
1025                camera.Update();
1026                toPosition = camera.cPosition;
1027                toAngle = camera.cOrientation;
1028
1029                camera.AdjustAngle(fromAngle);
1030                camera.AdjustPosition(fromPosition);
1031             }
1032             else
1033             {
1034                if(!dockTimer.started && !poppingWindow)
1035                {
1036                   dock.lastTime = GetTime();
1037                   dockTimer.Start();
1038                   dock.speed = -SWITCH_SPEED;
1039                }
1040             }
1041             break;
1042          case right:
1043             if(dockHidden)
1044             {
1045                Window3D window3D;
1046                Window ac = virtualDesktop.activeChild;
1047
1048                if(fullScreen || sliding < 1 || switching < 1 || entering < 1) break;
1049
1050                virtualDesktop.CycleChildren(false, false, false, true);
1051                if(ac == virtualDesktop.activeChild) break;
1052
1053                window3D = Desktop3DGetWindowHandle(virtualDesktop.activeChild);
1054
1055                fromAngle = camera.cOrientation;
1056                fromPosition = camera.cPosition;
1057
1058                lookAt.transform.position = window3D.cube.transform.position;
1059                lookAt.UpdateTransform();
1060
1061                switching = 0;
1062
1063                camera.Update();
1064                toPosition = camera.cPosition;
1065                toAngle = camera.cOrientation;
1066
1067                camera.AdjustAngle(fromAngle);
1068                camera.AdjustPosition(fromPosition);
1069             }
1070             else
1071             {
1072                if(!dockTimer.started && !poppingWindow)
1073                {
1074                   dock.lastTime = GetTime();
1075                   dockTimer.Start();
1076                   dock.speed = SWITCH_SPEED;
1077                }
1078             }
1079             break;
1080          case up:
1081          {
1082             if(poppingWindow) break;
1083             if(sliding == 1.0 && !dockHidden)
1084             {
1085                Window window = virtualDesktop.activeChild;
1086                if(window)
1087                {
1088                   Window3D window3D = Desktop3DGetWindowHandle(window);
1089
1090                   camera.Update();
1091                   fromAngle = camera.cOrientation;
1092                   fromPosition = camera.cPosition;
1093
1094                   camera.type = lookAt;
1095                   camera.position = { 0,0,0 };
1096                   camera.eulerOrientation = Euler {};
1097                   lookAt.transform.position = window3D.cube.transform.position;
1098                   lookAt.transform.orientation = Euler {};
1099                   lookAt.UpdateTransform();
1100
1101                   sliding = 0;
1102                   dockHidden = true;
1103
1104                   camera.Update();
1105                   toPosition = camera.cPosition;
1106                   toAngle = camera.cOrientation;
1107
1108                   camera.AdjustAngle(fromAngle);
1109                   camera.AdjustPosition(fromPosition);
1110                }
1111             }
1112             if(!dockHidden) break;  // Follow through to 'full screen' if dock is hiddens
1113          }
1114          case enter:
1115          {
1116             if(sliding < 1 || switching < 1 || entering < 1) break;
1117             if(!dockHidden)
1118             {
1119                if(!poppingWindow)
1120                {
1121                   /*if(dock.currentIcon == 1)
1122                   {
1123                      EditBox
1124                      {
1125                         this, multiLine = true, is3D = true, borderStyle = Fixed, hasClose = true, text = "Video",
1126                         size = Size { WINDOW_WIDTH, WINDOW_HEIGHT }, fileName = ":430.flc"
1127                      }.Create();
1128                   }
1129                   else*/
1130                   {
1131                      Picture
1132                      {
1133                         this, is3D = true, borderStyle = fixed, hasClose = true, text = "3D Window",
1134                         size = Size { WINDOW_WIDTH, WINDOW_HEIGHT }, image = BitmapResource { fileName = shotFileNames[id] }, opacity = 1, inactive = false
1135                      }.Create();
1136                   }
1137                   id++;
1138                   id %= sizeof(shotFileNames) / sizeof(char *);
1139                }
1140             }
1141             else if(!fullScreen)
1142             {
1143                float distance;
1144                Window window = virtualDesktop.activeChild;
1145                Window3D window3D = Desktop3DGetWindowHandle(window);
1146
1147                fromAngle = camera.cOrientation;
1148                fromPosition = camera.cPosition;
1149
1150                distance = window3D.cube.transform.scaling.x * (float)camera.focal.w / virtualDesktop.clientSize.w;
1151                distance += THICKNESS/2;
1152
1153                camera.type = attached;
1154                camera.position = {0,0,-distance};
1155                camera.eulerOrientation = Euler{};
1156                lookAt.transform.position = window3D.cube.transform.position;
1157                lookAt.transform.orientation = window3D.cube.transform.orientation;
1158                lookAt.UpdateTransform();
1159
1160                entering = 0;
1161
1162                camera.Update();
1163                toPosition = camera.cPosition;
1164                toAngle = camera.cOrientation;
1165
1166                camera.AdjustAngle(fromAngle);
1167                camera.AdjustPosition(fromPosition);
1168
1169                fullScreen = true;
1170             }
1171             break;
1172          }
1173          case down:
1174          case escape:
1175          case f1:
1176             if(sliding == 1.0 && dockHidden)
1177             {
1178                if(sliding < 1 || switching < 1 || entering < 1) break;
1179
1180                fromAngle = camera.cOrientation;
1181                fromPosition = camera.cPosition;
1182
1183                if(fullScreen)
1184                {
1185                   Window window = virtualDesktop.activeChild;
1186                   Window3D window3D = Desktop3DGetWindowHandle(window);
1187
1188                   camera.type = lookAt;
1189                   camera.position = {};
1190                   camera.eulerOrientation = Euler {};
1191                   lookAt.transform.position = window3D.cube.transform.position;
1192                   entering = 0;
1193                }
1194                else
1195                {
1196                   camera.type = lookAt;
1197                   camera.position = {0, ORBIT_HEIGHT, -2000};
1198                   camera.eulerOrientation = Euler { pitch = 15 };
1199                   lookAt.transform.position = {};
1200                   sliding = 0;
1201                   dockHidden = false;
1202                }
1203                fullScreen = false;
1204
1205                lookAt.transform.orientation = Euler {};
1206                lookAt.UpdateTransform();
1207
1208                camera.Update();
1209                toPosition = camera.cPosition;
1210                toAngle = camera.cOrientation;
1211
1212                camera.AdjustAngle(fromAngle);
1213                camera.AdjustPosition(fromPosition);
1214             }
1215             else if(key == escape)
1216                Destroy(0);
1217             break;
1218       }
1219       return true;
1220    }
1221 }
1222
1223 static Window3D Setup3DWindow(Window window, int w, int h)
1224 {
1225    Window3D window3D {};
1226    Window child;
1227
1228    window3D.bitmap = Bitmap {};
1229    window3D.bitmap.AllocateDD(virtualDesktop.displaySystem, w, h);
1230    // window3D.bitmap.AllocateDD(virtualDesktop.displaySystem, TEXTURE_SIZE, TEXTURE_SIZE);
1231
1232    window3D.cube = Object {};
1233    window3D.cube.tag = window;
1234
1235    window3D.frontMaterial.diffuse = white;
1236    window3D.frontMaterial.opacity = 0.5f;
1237    window3D.frontMaterial.baseMap = window3D.bitmap;
1238    window3D.frontMaterial.flags = MaterialFlags { translucent = true, doubleSided = true };
1239
1240    window3D.backMaterial.diffuse = white;
1241    window3D.backMaterial.opacity = 0.5f;
1242    window3D.backMaterial.flags = MaterialFlags { translucent = true, doubleSided = true };
1243
1244    window3D.sideMaterial.diffuse = white;
1245    window3D.sideMaterial.opacity = 0.5f;
1246    window3D.sideMaterial.flags = MaterialFlags { translucent = true, doubleSided = true };
1247
1248    window3D.cube.AddName((window3D.back = Object {}), "Back");
1249    window3D.back.InitializeMesh(virtualDesktop.displaySystem);
1250    CreateBack(window3D.back.mesh, 1, 1, THICKNESS, virtualDesktop.displaySystem);
1251    window3D.back.mesh.ApplyMaterial(window3D.backMaterial);
1252    window3D.back.mesh.ApplyTranslucency(window3D.back);
1253
1254    window3D.cube.AddName((window3D.front = Object {}), "Front");
1255    window3D.front.InitializeMesh(virtualDesktop.displaySystem);
1256    CreateFront(window3D.front.mesh, 1, 1, THICKNESS, virtualDesktop.displaySystem);
1257    window3D.front.mesh.ApplyMaterial(window3D.frontMaterial);
1258    window3D.front.mesh.ApplyTranslucency(window3D.front);
1259
1260    window3D.cube.AddName((window3D.side = Object {}), "Side");
1261    window3D.side.InitializeMesh(virtualDesktop.displaySystem);
1262    CreateSide(window3D.side.mesh, 1, 1, THICKNESS, virtualDesktop.displaySystem);
1263    window3D.side.mesh.ApplyMaterial(window3D.sideMaterial);
1264    window3D.side.mesh.ApplyTranslucency(window3D.side);
1265
1266    window3D.cube.SetMinMaxRadius(true);
1267
1268    window3D.id = virtualDesktop.numWindows;
1269    virtualDesktop.poppingBiggest = 0;
1270    for(child = virtualDesktop.firstChild; child; child = child.next)
1271    {
1272       if(child.is3D) //display != virtualDesktop.display)
1273       {
1274          Window3D child3D = Desktop3DGetWindowHandle(child);
1275          if(child == window)
1276             child3D = window3D;
1277
1278          child3D.wantedPosition = virtualDesktop.globalPosition +
1279             child3D.id * 2*Pi / (virtualDesktop.numWindows+1);
1280          if(child == window)
1281             window3D.position = window3D.wantedPosition;
1282          virtualDesktop.poppingBiggest = Max(virtualDesktop.poppingBiggest,
1283             child3D.position - child3D.wantedPosition);
1284       }
1285    }
1286    virtualDesktop.numWindows++;
1287    virtualDesktop.poppingWindow = window;
1288    virtualDesktop.poppingPercent = 0;
1289
1290    return window3D;
1291 }
1292
1293 static void Update3DWindow(Window window, Box box)
1294 {
1295    Window3D window3D = Desktop3DGetWindowHandle(window);
1296    int w = box.right - box.left + 1;
1297    int h = box.bottom - box.top + 1;
1298
1299    glBindTexture(GL_TEXTURE_2D, (int)(intptr)window3D.bitmap.driverData);
1300    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1301    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1302
1303    glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
1304       box.left,
1305       window.size.h - h - box.top,
1306       box.left + virtualDesktop.clientStart.x,
1307       virtualDesktop.size.h - virtualDesktop.clientStart.y - h - box.top, w, h);
1308 }
1309
1310 class Orbit : GuiApplication
1311 {
1312    driver = "OpenGL";
1313    bool Init()
1314    {
1315 #ifdef FULLSCREEN
1316       fullScreen = true;
1317 #endif
1318       Desktop3DInitialize(virtualDesktop, Setup3DWindow, Update3DWindow);
1319       return true;
1320    }
1321 }
1322
1323 Desktop3D virtualDesktop {};