Editor improvements: input floats like “pi/2” or “2+2”, context menu for “Output”, new start form

Posted on

Input expressions like "pi/2" into float edit boxes
New main form of editor
Output tab - context menu
  1. A small convenience when writing float values: you can input an expression, like pi, pi / 2 or 2/3 instead of typing the actual value (3.14…, 1.57…, 0.6666….).

    Any expression using CastleScript is allowed.

    This is nice e.g. for angles for which we use radians (consistent with X3D, OpenGL, many other APIs). So you have to think in terms of pi (not degrees). At least now you can write pi/2 instead of calculating 1.57... in some external calculator. (An even better solution, to both display and edit angles in degrees, like just 180deg that just means pi automatically, is possible in the future. My main fear with it is that people will get confused that API still expects radians, and passing Angle:=180 will not work as expected. So for now, you have to use radians all across CGE, but it’s just easier now.)

    It is nice for any other use-case when it’s easier to write an expression than result. 1/3, 2/3, 10+1/3 are some examples.

    As a funny example of abusing this, note that you can even use random function from CastleScript 🙂 So you can input in CGE editor value random() and get different result each time 🙂 I’m sure it’s a unique feature of CGE that you can assign an unpredictable value to a property 🙂

    This is all just an “editing convenience”. The value is calculated immediately as you change focus / press Enter, and it is stored as a simple float (not as CastleScript expression) in the actual property, in serialized JSON etc.

    We have also changed the division operators in CastleScript to work like in Pascal: / makes float division, div makes integer division.

    If you want to use such expression in your own applications, go ahead: just call ParseConstantFloatExpression instead of StrToFloat.

  2. The “Output” tab has a simple context menu (right-click). It allows to clean it, copy selected line, copy all lines (to the clipboard). Great to copy-parse error message to show it someone.

  3. I have changed the editor start form. No revolution (yet — I do have a more interesting redesign on a TODO list), I just wanted to expose nice buttons to open examples and support us on Patreon.

Comments on the forum ➤

TCastleWindow: simple name (without Base suffix), removed deprecated Window.SceneManager, UTF-16 in WinAPI backend

Posted on

Window with dirty/old glass and lights designed in CGE editor

Two news about our TCastleWindow class:

  1. The TCastleWindow and TCastleControl are now (again) advised to be used through these simple names.

    There is no more need to use the names with Base suffix, TCastleWindowBase and TCastleControlBase. They are now just deprecated aliases to the simpler names without the Base suffix.

    Underneath, this happened because of a change I announced at the end of 2021: The old deprecated TCastleWindow.SceneManager and TCastleControl.SceneManager are now removed. They were causing complicated dependencies (in the end TCastleWindow / TCastleControl code should not deal with viewport/scenes specifics), and the assumption that “every window starts with a default scene manager” was unnecessarily complicating understanding of CGE, and even the name SceneManager was outdated (we call it just “viewport” since a long time). The deprecated CastleWindowTouch was also removed — just use TCastleTouchNavigation.

  2. We now use a Unicode (UTF-16) version of WinAPI throughout our TCastleWindow backend on Windows. This fixes support for international characters in window caption and in native open/save dialogs.

This is yet another post when it was hard to find a screenshot expressing the API improvements 🙂 So here’s a rendering of window glTF model from SketchFab, by Yury Misiyuk in Castle Game Engine editor. The window glass nicely utilizes the material possibilities of PBR, the reflections of a dirty/old glass are nicely realistic and completely dynamic. Here I show them with lights designed in CGE editor — a feature that is correctly in development.

Comments on the forum ➤

TCastleViewport.PositionFromWorld utility, to adjust UI position to something in 3D viewport, or measure 3D size on screen

Posted on

PositionFromWorld demo

New method TCastleViewport.PositionFromWorld allows to map a viewport (3D) position into user interface (2D) position. This allows to:

  1. Place UI element exactly where some 3D thing is.

    The example examples/viewport_and_scenes/position_from_world shows how to position a TCastleLabel so that it always remains at the TCastleSphere middle, regardless of how you move and zoom the camera.

    It comes down to these lines of code:

    { calculate SphereViewportPosition, position of the sphere middle in 2D viewport coordinates. }
    SphereWorldPosition := MainSphere.LocalToWorld(Vector3(0, 0, 0));
    SphereViewportPosition := MainViewport.PositionFromWorld(SphereWorldPosition);
    
    { SphereViewportPosition is immediately useful to determine LabelAttached position }
    LabelAttached.AnchorDelta := SphereViewportPosition;
    
  2. Measure the (potential) size of 3D element on the screen. In turn, this allows to transform 3D elements to make them look on screen in some desired way. For example, you can adjust 3D scale, to force some 3D object to have certain size on the screen, regardless of how far it is from the camera.

    The same example examples/viewport_and_scenes/position_from_world shows how to position a TCastleText so that it always has the same size, no matter how far away the pivot is (regardless of how much you zoom in/out the camera). Try out the demo, you can rotate and zoom like crazy — the 3D text will rotate, the sphere will get smaller or larger, but the size of the text will remain the same.

    It comes down to these lines of code (note: PositionFromWorld and SphereViewportPosition calculation is the same as in previous use-case):

    { calculate SphereViewportPosition, position of the sphere middle in 2D viewport coordinates. }
    SphereWorldPosition := MainSphere.LocalToWorld(Vector3(0, 0, 0));
    SphereViewportPosition := MainViewport.PositionFromWorld(SphereWorldPosition);
    
    { calculate Sphere1UnitSizeOnScreen, how large is 1 unit in 3D space around sphere, on the screen. }
    SphereWorldPosition1 := SphereWorldPosition + Vector3(0, 1, 0);
    SphereViewportPosition1 := MainViewport.PositionFromWorld(SphereWorldPosition1);
    Sphere1UnitSizeOnScreen := PointsDistance(SphereViewportPosition, SphereViewportPosition1);
    
    { use Sphere1UnitSizeOnScreen to keep the TextAttached size similar on screen,
      regardless of how far is the scene. }
    TextAttachedScale := 100 / Sphere1UnitSizeOnScreen;
    TextAttached.Scale := Vector3(TextAttachedScale, TextAttachedScale, TextAttachedScale);
    
Comments on the forum ➤

New manual page “Managing States”, improved list of TCastleTransform descendants in manual

Posted on

zombie_fighter example
zombie_fighter example
  1. I created a nice documentation about managing states. Some of this information was already available, but scattered over the manual and not so clearly organized. The new page should serve as a clear documentation how states are supposed to be used.

  2. We have a nice list of TCastleTransform descendants with screenshots in the manual page about the viewport. This should serve as nice description “what I can put in a viewport”.

Comments on the forum ➤

Easily load UI in TCastleControl using TCastleControl.DesignUrl

Posted on

TCastleControl.DesignUrl

I added TCastleControl.DesignUrl and TCastleControl.DesignedComponent to easier load designed UI in TCastleControl, and even see the designed UI in Lazarus form inspector.

The short documentation is added to TCastleControl chapter.

Our engine examples examples/lazarus/model_3d_with_2d_controls and examples/lazarus/two_controls use it already.

Comments on the forum ➤

Vectors changes, to avoid a trap with modifying a temporary value

Posted on

Cthulhu high-poly model (4.5 million triangles) in glTF by TooManyDemons

We have made a significant change to how our vectors are declared. They now have X, Y, Z, W as fields (not properties), and the compiler will prevent you from falling into a trap of writing

Scene.Translation.X := Scene.Translation.X + 10;

Like in the below testcase:

uses SysUtils, CastleScene, CastleVectors;
var
  S: TCastleScene;
begin
  S := TCastleScene.Create(nil);
  S.Translation.X := S.Translation.X + 10;
  FreeAndNil(S);
end.

This has always been invalid code, as it potentially modifies a temporary value, not the actual Scene.Translation value. And even if it modifies the actual Scene.Translation value (under some optimization/compiler version), it bypasses the Scene.Translation property setter. This is documented in-depth in coding traps documentation page.

I recognized this was a significant trap when using CGE vectors, and changed the way vector is declared to make it impossible. From now on, this code will no longer compile. FPC prevents us from making this mistake.

This affects all our vectors and colors:

The change is mostly backward compatible (if your code was safe).

  • Some properties and methods remain as deprecated (so they will compile, for now, albeit with clear warning from a compiler): Data, Items properties,methods Init and NormalizeMe. We will completely remove them at some point in the future (we don’t like traps in API 🙂 ) so please follow the compiler warnings and adjust your code to not use them.

  • Setting fields X, Y, Z, W is now the only way to change the vector “in place” that works reliably and is not deprecated.

  • V[0] works but is read-only now. It is equivalent to V.X or V.AsArray[0].

  • Constructions that treat Data as an array will no longer work, as it is now an indexed property. So V1.Data := V2.Data should change to just V1 := V2.

  • Data is no longer a direct field, so V.Data[0] += 2 will no longer work. Change it to V.X += 2. If you really need by-index access, you can temporarily change it to V.Data[0] := V.Data[0] + 2 or V.InternalData[0] := V.InternalData[0] + 2 — these provide a quick upgrade path, although we recommend avoiding such constructions altogether in new code.

  • Constant declarations will need to change. Foo: TVector3 = (Data: (1, 2, 3)) should now be Foo: TVector3 = (X: 1; Y: 2; Z: 3).

The other records in CGE will soon follow with a similar redesign. The only way to change the record “in place” is by direct field access, not by some method or property setter. This means that compiler will prevent invalid operations. This applies to:

P.S. As usual with such posts, I had trouble selecting a screenshot. How do you visualize “vector API cleanup”? 🙂 So here’s a screenshot of view3dscene rendering glTF model of Cthulhu (model by TooManyDemons, distributed on SketchFab). The model is quite high-poly, done in ZBrush (4.5 million triangles, but it’s just s single mesh and it renders with 60 FPS).

Comments on the forum ➤

Add skybox or define sky/ground color gradient using TCastleBackground

Posted on

Background for skybox
Background gradient
Background for skybox

New component TCastleBackground allows to define a nice background for 3D games. Just add it to the design by Add Non-Visual Component -> Background (you can add it to any component, but it’s most natural to do it on viewport) and then set TCastleViewport.Background to it.

  1. You can provide up to 6 textures that define 6 sides of a skybox. See wikipedia about skyboxes.

    How to make such skyboxes?

  2. You can define a sky gradient using TCastleBackground.SkyTopColor, TCastleBackground.SkyEquatorColor, ground gradient TCastleBackground.GroundBottomColor, TCastleBackground.GroundEquatorColor, and additional gradient to smooth transition from sky to ground TCastleBackground.SmoothEquator.

    The sky and ground colors are visible if you don’t provide the 6 textures for a skybox, or if some of them use alpha for transparency.

Comments on the forum ➤

Code cleanups: units map, more obvious subdirectories in engine, automatically checked dependencies, moved various units to internal

Posted on

Dungeon map in Tiled rendered using Castle Game Engine

Lately I’ve done a number of rearrangements in CGE units, to make the engine code cleaner (easier to understand, both for new people and seasoned devs):

  1. Units map documentation is now up-to-date and more straightforward.

  2. Unit dependencies outlined in units map are (again) automatically checked by check_units_dependencies internal tool (using ppudump from FPC underneath).

  3. A lot of obscure/internal units have been renamed to CastleInternalXxx or marked as deprecated. We’re not finished with this cleanup yet, see more potential units that can become internal. But our list of units is already much shorter, making CGE API smaller so simpler to learn.

  4. Subdirectory src/3d is now src/transform. It contains TCastleTransform and friends, and is suitable for both 2D and 3D.

  5. Subdirectory src/x3d is now src/scene. It contains TCastleScene and friends (like TCastleViewport). I moved the 2 big units that define X3D fields and nodes to a subdirectory src/scene/x3d. The code loading particular model formats is now in src/scene/load, and some formats even get their own subdirectory, like src/scene/load/spine.

  6. I added subdirectory src/base_rendering, that contains some base OpenGL units (and will contain in the future alternative subdirectories for OpenGL, Vulkan etc.).

  7. We no longer use opengl subdirectories inside various other CGE directories. Things from base/opengl/, images/opengl/ mostly move to base_rendering. Things from scene/opengl/, ui/opengl/ mostly move to parent dir, i.e. just scene, ui.

    Why?

    The original idea for separating units that depend on OpenGL from things that don’t was that it would make it easier to recognize what needs to change when we add another renderer (like Vulkan).

    However, a lot of CGE units now depend on OpenGL only “very indirectly” and they have a clear API independent from OpenGL. For example, CastleControls unit depends on OpenGL, but actually both its interface and implementation are completely independent from OpenGL, and they will likely look the same when we have Vulkan available. They just use TDrawableImage and DrawPrimitive2D that make sense for any renderer.

    Similar notes go for e.g. CastleScene or CastleViewport. They depend on OpenGL now, but if (when?) we’ll jump to Vulkan, they will continue to exist with the same API, and even they’ll retain most of their implementation. We will just introduce then an abstraction layer, to make renderer “pluggable”, so that underneath TCastleScene, TCastleViewport, TCastleUserInterface will refer to the “current renderer” to do their drawing.

    All this meant that keeping these units in opengl/ subdirectory was not really very helpful. In fact, most of important CGE units were in opengl/ subdirectories, which was likely more confusing than helpful. E.g. ui/ looked like a mostly empty directory (mostly just joystick units) until you looked in ui/opengl/ that contained a lot of important classes.

  8. A few usages of is nested were cleaned — as it is not supported by Delphi. We previously tried to use “is nested” on FPC and anonymous methods on Delphi, but this resulted in a code complicated with ifdefs. It’s better to stick to a common subset of FPC and Delphi for now, which means — no “is nested”, no anonymous methods. (Of course you’re free to use them in your own projects, if you only care about using one of these compilers.)

P.S. I don’t really have any relevant screenshot to go with this post. So enjoy a screenshot with a sample Tiled dungeon map from here, rendered of course using our examples/tiled/map_viewer 🙂

Comments on the forum ➤

Example how to use a custom cursor (a static image or an animated scene)

Posted on

Custom cursor example
Custom cursor designed in CGE editor

I’ve added an example project examples/user_interface/custom_cursor showing how you can implement a custom cursor in your applications. It’s very flexible — the custom cursor is just a UI element, synchronized with mouse position, and it can contain an image, a viewport (with animated scene) or anything else you want. You even design the custom cursor in the CGE editor.

Comments on the forum ➤