Firemonkey like 3D for all Smart Mobile Elements
What I mean with practical 3D is simply that around 90% of what Firemonkey introduces is, well, a complete waste of time in a business application. I dont mean this in any negative way, I love Delphi and Firemonkey is a fantastic RTL and framework. But a rotating form or spreadsheet is .. well, perhaps not what you want when crunching numbers on a tax report due yesterday.
But Firemonkey really comes into it’s own on mobile devices, where you are almost expected to have widgets that wobble, rotate and gives visual feedback on every touch. So I’m not dizzing Firemonkey here — I am simply saying that for serious scientific or business apps – it’s out of place.
3D and Smart Mobile Studio
CSS3 have some neat features, like being able to position elements in 3d space. So using CSS you can not only animate elements between visual states (keyframe animations), you can apply those animations in 3D space. Meaning that you can rotate a DIV tag (the primary container Smart Pascal custom-controls manages) in X, Y and Z vectors. Here is a little example of a CSS animated cube.
If you define backface-visibility as active, your elements become semi-transparent, and elements behind your element (in 3d space) becomes visible. In the golden days of Amiga demo programming, we called that “glenz vectors”.
Partial classes, the death of plugins
So, the challenge at hand for me, is to provide full 3d support without messing up the RTL (which should be considered as the seed-stone and foundation for SMS apps, bordering on sacred). The only way to do this is to use partial classes. If you dont know what that is you can read up on the topic — but in general it means that you can extend a class anywhere by adding the Partial keyword. And voila, all the new features you add to your partial class, becomes a part of the class you extend.
In other words, plugins have no place in SMS because partial classes completely removes the need for them. A plugin could never really extend a class in the same way anyhow – so it’s no competition at all.
Using the force
Before you start to play around with effects, you really should download and install my QTX library. This is hosted on Google code and contains whatever classes and examples I post here. So use SVN and check it out into your $SMS/Libraries folder (! important !) so the IDE automatically locates the files.
The library also adds a wealth of “missing” tidbits and stuff to the RTL (safely, without messing up anything) so it should be considered “a must have” for all SMS hackers out there.
Right. For simple 3d effects that you can apply to anything (buttons, lists, checkboxes, labels or whatever) you simply add the unit qtxTransformController (which is in the QTX library). This unit contains a class called TQTXTransformController. You can create an instance of this class, proving the handle of the control you want to play with — and voila, you can rotate your element in whatever X, Y, Z vector you want.
procedure TForm1.AnimateStuff; var mObj: TQTXTransformController; begin mObj:=TQTXTransformController.Create(w3button1.handle); mObj.x:=100; mobj.y:=100; mObj.z:=-50; mObj.update; end;
But all of this is tiresome work. I mean, we cant create transform controllers for each and every element right? It may be kosher for games where you have a fixed number of sprites, but it would take ages to manage for ordinary apps. So my mission at the moment is to merge TQTXTransformController with TW3CustomControl using a partial class — thus exposing the 3D framework directly
Digging into it
But there are real challenges. For 3D perspective to work properly, it has to be defined by the container. In other words, if you want to rotate a button using the X Vector around it’s own axis — the perspective (Z vector) of the container defines it. In other words, the depth perception is set in the container tag, typically 1000 — meaning that you can then rotate your button and it can have a depth into space of 1000 pixels. Anything deeper looks.. well, a bit 80’s spaceship weirdo demo-ish.
The best way to think about this — is like a tub of water with a plank of wood floating on the surface. The Z vector of the parent defines how deep the tub is, while the X, Y and Z vectors of the element defines how deep you push the plank, and in what end you apply force.
So X = -50 means that you push the left side of the plank down by 50 pixels. X = 50 means that you push the right edge of the plank down by 50 pixels (at which point the left side of the plank goes up by the same amount). The Z factor for the element is how deep the in the water the plank is held.
-webkit-transform: rotateX(90deg); transform: rotateX(90deg);
The above CSS will look.. well, flat, unless we apply this to the container elements (form, panel etc..):
-webkit-perspective: 1000px; perspective: 1000px;
Once you apply that snippet of CSS to the container, suddenly the element gains depth and truly looks 3D. We also want the elements to preserve their 3D modifications after an effect has been applied, which is what the “preserve 3d” property is all about:
-webkit-transform: rotateX(90deg); transform: rotateX(90deg); transform-style: preserve-3d;
So what is the problem? Well, if you already have a CSS3 GPU effect running on the container – there is no telling what happens. It’s a bit like flipping a coin which effect is annulled by the browser. Hence we need to be clever about this. We need a easy to use, lightweight, ad-hoc framework which is capable of handling possible conflicts, and also – in some cases, merge effects on elements that can handle multiple adjustments.