Suspected typos (low priority)

Hello!

castle-engine\src\images\castleimages.pas

acSimpleYesNo = acBlending deprecated 'use acTest';

wrong value?

Actually there was a number of comments-obly typos that i already forgot. But this one seems to hatch into the code and made me log into the forum :slight_smile: Those were mistakes like a letter missed or changed or inserted into some ID. Perhaps one could make word statistics gatherer for CGE sources, stupir words counter with matcher for words used <= 5 times. If it is ever worth it…


https://castle-engine.io/x3d_implementation_geometry2d.php#section_example_pascal

Supported nodes

Missed TTriangleFanNode, also no link to TLineSetNode, also no example of setting coords to Triangles-based nodes.
The OutlineGeometry.Coord := OutlineCoords; from the example won’t work on them. Hopefully would TriangleSetNode.CoordField.Value := OutlineCoords; - but it takes some time to find it in sources.

For some hopefully simple yet realistic possible example.

function DiagShape(const Source: RAnglesDiagram; const baseZ: single;
  clOutline, clInner: TCastleColor;
  const AlphaOutline: single = 1;
  const AlphaInner:   single = 0.5): TX3DRootNode;

I want to render a rotatable sensor, with RAnglesDiagram basically being an array of {angle, radius} pairs and an interpolation method. In the end i want a rotating polar-coord chart with translucent inner filling and opaque or less translucent outline.

Here is it rendered with usual LCL TImage for example, not translucent, not rotatable and not having other objects in the scene

I am NOT asking to implement it, just maybe giving you a good idea for a demo/tutorial, maybe not.

In general, that example created to demonstrate 2D uses almost exclusively 3D nodes. Basically twi chapters of the page deny one another.

Could’ve showcase TAppearanceNode and TUnlitMaterialNode.

Also, space missed in //Scene.Rotation := in WindowUpdate


In general, most nodes like triangles have a “mirror class” - Txxxx and TIndexedXXX - and your demos seem to prefer the latter. It is somewhat confusing, would be good if the intended difference between those would be outlined somewhere. Why there should be both, what are usecases favoring one or another.


Also, maybe it is personal, when i read through tutorials i always feel myself shaky, as there is no mention of memory/object ownership. Tutorials seem to be of “create and keep until program exit” type. Some tutorials of “delete and recreate” kind would feel nice ro have, perhaps.

Actually feel puzzled here… The comments in the source strongly suggest to make an explicit Appearanvce node… Okay.

  Result := nil;
  xn := nil;

  c2s := DiagShape(Source, scale, 1);
  c3s_i := DiagShapeTriag(c2s, baseZ - 0.5);
  c3s_o := DiagShape(c2s, baseZ + 0.5);
  c2s := nil;

  try
    xn := TX3DRootNode.Create;

    Transform := TTransformNode.Create;
    xn.AddChildren(Transform);

    InFill := TTriangleSetNode.CreateWithShape(OutlineShape);

    OutlineCoords := TCoordinateNode.Create;
    OutlineCoords.SetPoint(c3s_i);
    InFill.CoordField.Value := OutlineCoords;

    Material := TUnlitMaterialNode.Create;
    Material.EmissiveColor := clInner.XYZ;
    Material.Transparency := 1 - AlphaInner;
    OutlineShape.Material := Material;
//    OutlineShape.Appearance := TAppearanceNode.Create;
//    OutlineShape.Appearance.AlphaChannel := acBlending;

    Transform.AddChildren(OutlineShape);

    OutlineCoords := TCoordinateNode.Create;
    OutlineCoords.SetPoint(c3s_o);

    OutlineGeometry := TLineSetNode.CreateWithShape(OutlineShape);
    OutlineGeometry.Coord := OutlineCoords;
    OutlineGeometry.SetVertexCount([OutlineCoords.FdPoint.Count]);

    Material := TUnlitMaterialNode.Create;
    Material.EmissiveColor := clOutline.XYZ;
    Material.Transparency := 1 - AlphaOutline;
    OutlineShape.Material := Material;

    Transform.AddChildren(OutlineShape);

    Result := xn;
    xn := nil;
  finally
    xn.Free;
  end;

Would i uncomment the Appearance lines - and the inner field is white instead of colours!
Without those lines it seems to work fine.

Perhaps i did something stupid, but was copy-pasting from tutporials and hoiping upon defaults.

Win7 x64 integrated Radeon 4250 (760G ?)

Okay, i WAS doing a stupid thing, but CGE seems inconsistent… Maybe, there is a method to this inconsistency, but for a tutorials copy-pasting newb it is one trap…

    OutlineGeometry := TLineSetNode.CreateWithShape(OutlineShape);

    //    OutlineShape.Appearance := TAppearanceNode.Create;
    appNode := OutlineShape.Appearance;
    appNode.AlphaChannel := acBlending;

Explicitly creating the node here not just is redundant, but outright “kills the mood”.

    appNode.LineProperties := TLinePropertiesNode.Create;
    appNode.LineProperties.LinewidthScaleFactor := 0;

yet here we MUST do it, despite the previous example taught us not to.

I don’t know if getter-as-constructor can be applied here, but feels inconsistent.

https://castle-engine.io/apidoc/html/CastleControls.TCastleLabel.html

weirdly, no linke to .Border property nor TB order type on the pagem despite PasDoc presen in a deep ancestor TCastleUserInterface

Could it be because of redirection TBorder = CastleVectors.TBorder; ?..

…but probably bad choice anyway, since i wanted them to be placed under the transplucent TTrianglesSetNode :slight_smile:

no any examples or tutorials for TCastleText :-/

for example i want that TCastleControl with 2D settings with both coords span -10K to +10K

now i want by mouse click position a TCastleText there. Seems easy-peasy, but somethign does not work and no clues what to check…

Text and fonts | Manual | Castle Game Engine suggests the examples\fonts\text_tests, but that is a “create by editor” example, and there is no “create by code” which should showcase the minimal bootstrapping.

Also, this tutorial, if it is required doc, could be lionked from the TCastleText/TCastleEdit/TCastleLabel class documentation pages, it is not.

Maybe i should’ve set the component size, or manually created even the most basic font, but… I just don’t know - and seem to have no way to know.

    tc := TCastleScene.Create(Self);
    try
      tt := TCastleText.Create(Self);
      tt.Caption := IntToStr(row);
      tt.Color := cl;
      tt.ColorPersistent;

//      tt.BorderColor := cl;
//      tt.Border.AllSides := 2;
//      tt.Padding := 6;

      tc.Add(tt);

      tc.TranslationXY := coord;
      Vehicles[row] := tc;
      tc := nil;
    finally
      tc.Free;
    end;
    Viewport.Items.Add(Vehicles[row]);

Then TCastleControl.OnPress moves them fine - but renders none…

  1. Customizing font. You can add and configure fonts…
    …
    You can also assign font to TCastleText.CustomFont

“Can” means it is not required, if you only needs most basic things. And i only needed 1/2/3

  1. Deprecated … To make it easy, one global instance of this class is already created for you: UIFont

Oh, great! Except i had to read into Deprecated section. But… won’t even compile

Error: Incompatible type for arg no. 1: Got “TCastleAbstractFont”, expected “TCastleFont”

And DefaultUIFont is not even mentioned there in the tutorial. Neither is FallbackFont(), nor is there some hypothetical FallbackFont(small) to expose DefaultUIFont.Small to user code;

Anyway, adding tt.CustomFont := FallbackFont(); did not fix things…

tt.Size := 200 (300, 400) did a trick, but was it mentioned? And how to calculate?
tt.CustomFont.OptimalSize := 30; gives a pretty nil^ AV

      tt.CustomFont := FallbackFont;
      tt.CustomFont.OptimalSize := 30;

…gives another exception about missing FreeType library, but it was not advertized it would be needed (for the bare bones stuff).

How should i offset it to position it’s center under the mouse click?

    MouPos := ViewPort.PositionTo2DWorld(Event.Position, True);
    Vehicles[i].TranslationXY := MouPos;

Or in another terms, where is the text rendered from the local zero point, and how to make it centered?

tt.Center.XY; this gave nothing, okay, it was abuse, but it was named so nicely.

tt.TranslationXY := - tt.BoundingBox.Center.XY; did the trick it seems, yet i am not sure it was intended/optimal way, perhaps the text components better be auto-centering? At least, no clues i found in tutorial or the class doc.

Thank you for the comments, there are a lot of useful conclusions in this thread that I now applied – changes to engine and/or docs.

But note: Please try to separate reports into a few threads – one long thread talking about many things is hard to answer, and we’re doomed to generate a long thread talking about many things at once :slight_smile: I prefer a few small threads, that can be “solved” one by one, then a long-running thread.

For now I have addressed things until TCastleLabel comments – I will read and answer them later, for now I’m falling asleep :slight_smile: Answers below:

Thank you, fixed!

TTriangleFanNode and TLineSetNode are documented on Rendering component | Castle Game Engine .

I agree these nodes could be grouped better. For now this organization (sections like “Geometry2D”, “Geometry3D”, “Rendering”) follows X3D specification Extensible 3D (X3D), ISO/IEC 19775-1:202x, Contents , which is “friendly” only to people familiar with X3D. I want to rearrange most of these docs to be more friendly to people new to CGE (who may not know, or even care, about X3D). That’s a larger TODO for some time.

It works, it’s mostly the same.

Geometry.Coord := Coord;

is OK when Geometry is of TTriangleSetNode type.

I added TTriangleSetNode and TTriangleFanSetNode examples to Rendering component | Castle Game Engine .

Both indexed and non-indexed make sense.

The “indexed” versions allow to refer to the same vertex (on the Coord list) multiple times, since you specify the vertex once in TCoordinateNode, and then can use the same index multiple times e.g. in TIndexedTriangleSetNode.SetIndex.

Good point that this needs to be documented better. I added a longer explanation to Rendering component | Castle Game Engine .

The X3D nodes have a reference-counting mechanism. The node is freed when the reference count of it changes from non-zero to zero.

Again good point that this needs to be documented better.

I added a longer explanation to Scene graph (X3D) | Castle Game Engine (section " 2.1. X3D node instance lifetime in Pascal"). I also improved the API docs of KeepExistingBegin, KeepExistingEnd mentioned there (the API docs Castle Game Engine: X3DNodes: Class TX3DNode ).

You’re not the first to report confusion about this.

I consider it a mistake to add TShapeNode.Material shortcut, that automatically creates and sets TShapeNode.Appearance and then sets TAppearanceNode.Material.

I should have deprecated it, and remove from docs, long time ago… Done now.

( I will nuke some remaining usage of it in CGE tomorrow. )

Not using this shortcut (and always creating and assigning TAppearanceNode explicitly to Shape.Appearance, then assigning material to Appearance.Material := ...) is much less error-prone (even if it looks a bit more verbose).

Well, sure, it was a “road movie” as i was scrambling some time for a long postponed project :slight_smile:

i am still struglging to internalize bs2D sorting and blending though.
Spent a lot of time on unstable sort - until i realized that i had to z-translate the “root” scenes, which i used as mere logical container and rotation/translation joint, so they all had zero Z, and the real Z had the objects within them. This hit me later when i got to bed, after i wasted like 3 hours trying different combinations of everything i could think of :frowning:

I would later re-read bs2D description and think if this could be made more digestable by newbs.

Also, sorry, would read the rest of your reply later too, no time right now :slight_smile:

When i did just that it ruined my rendering. I assume the default values from TAppearanceNode.Create were not all set up as TShapeNode expected them, or something.

Perhaps consider adding explicit API like procedure TShapeNode.CreateDefaultAppearance or function TShapeNode.CreateDefault(nodeclass): TObject - that could more or less uniformly be added to all the nodes? Something that would blend mere calling consturctor with ad hoc properties assignment both of creator and of the created node?

See, i am talking standing in a noob hat wearing noob boots. I can’t into Google.

I see the specific, 2D tutorial page.
This specific tutorial page goes at lengths to introduce me to a whola lot of classes…

…just to immediately disregard them and use some mysteriuous LineSet instead.

With no reasoning why.

It’s puzzling… And the lack of the immediate, here and now linke to the LineSet class (with at least some explanation what it was the beast) contributes.

Personally, after a tedious digging through low-level sources i ended up with

    InFill := TTriangleSetNode.CreateWithShape(OutlineShape);

    OutlineCoords := TCoordinateNode.Create;
    OutlineCoords.SetPoint(c3s_i);
    InFill.CoordField.Value := OutlineCoords;

Meanwhile OutlineGeometry := TLineSetNode.Create... and while it can receive coordinates array, it can not be assigned to the TTriangleSetNode instance, or i failed to find in the tutorial.

Another low-key thing i am somewhat striggling is forcing anti-aliasing upon the outline. Giving it Alpha=0.99 makes it smooth, but is an ugly hack. Making it opaque immediately makes it segmented again. Now, the ball here is in my park, but maybe a tutorial could be a bit proactive here.
Actually, perhaps tutorials could “pass the bucket” linking to one another, as the problems/topics are expected to be tangential.

    OutlineGeometry.Coord := OutlineCoords;
    OutlineGeometry.SetVertexCount([OutlineCoords.FdPoint.Count]);

This tutorial part also looks very redundant and fragile. Why the second line is even required? Can’t the property setter just do it?..

Google found me a TCastleWindow.AntiAliasing - which TCastelControl is sadly lacking.

Now i wonder if xxxx.MultiSampling can actually be anything but the very same thing by any other name (like its description and comments seem to vaguely imply). However i can witness, that searching for “antialiasing” is comes easier, than for “multisampling”.

If anything, MSAA is always full-screen(or full-window) and maybe there still are more local low hanging fruits

It would’ve been nice if TDisk2DNode.InnerRadius had been clearly marked as not-implemented, both in PasDoc and using something like deprecated or experimental

Big thank you for these notes. I addressed many of them, improving docs. I also just implemented TDisk2DNode.InnerRadius :slight_smile:

Known issue: PasDoc documentation doesn’t show inherited members.

The TBorder is documented in TCastleLabel ancestor, TCastleUserInterface, Castle Game Engine: CastleUIControls: Class TCastleUserInterface , but you need to manually search ancestors to find it.

This is already reported and I’d like to tackle it some day (I’m also the maintainer of PasDoc):

It’s mentioned in

It’s used in numerous examples:

  • examples/fonts/test_local_characters/
  • examples/physics/physics_joints_3d/
  • examples/tiled/strategy_game_demo/
  • examples/viewport_and_scenes/position_from_world/
  • examples/viewport_and_scenes/shadow_volumes_whole_scene_manifold/

The rule always is: it is exactly equivalent to create things by code and by editor, just make sure to set the same properties. We have some examples showing the idea, like castle-engine/examples/lazarus/load_model_and_camera_manually/mainf.pas at master · castle-engine/castle-engine · GitHub . Maybe we should document better the correspondence.

Basically, playing with component in the editor also gives you the knowledge about it’s code API. It’s the same as with LCL, Unity etc.

Added, thanks.

In general, indeed I like to make API docs link to manual where appropriate. Let manual and API docs in the end link to each other.

I’d need to see bigger example to say what’s wrong. The code looks OK.

Without knowing the code, I can recommend to prototype in the editor – try to set up exactly the same thing, same values, same properties in editor. You may likely find quicker this way what goes wrong. Editor really doesn’t do anything magic, it sets up the same classes with the same properties – but you will see everything clearer.

You can also try inspector (F8) to debug at runtime, https://www.youtube.com/watch?v=5jBdPdj75yk .

Do you try to assign SomeLabel.CustomFont := UIFont? This will indeed not compile without a typecast, because UIFont doesn’t have to be of TCastleFont class. And you should not need this assignment.

DefaultUIFont - this is internal, only in implementation. I’m unsure why you mention it, and what do you try to do. Same for FallbackFont(small) or DefaultUIFont.Small, I’m admittedly lost what you’re doing, what you want to achieve. If you believe you found a bug, please report a complete reproducible code.

Please show a complete example code to reproduce it.

It should not need FreeType. FreeType is used to read TTF / OTF files. Again, please submit a testcase to reproduce.

Castle Game Engine: CastleScene: Class TCastleText shows Alignment, VerticalAlignment. They are also clearly visible as “Basic” properties in editor – as above, the simple advise is to play and prototype in the editor, even if you later want to instantiate them from code.

I’m not happy with our current blending sorting and its limitations. This is scheduled to be improved soon, I have hopes even for this weekend. Roadmap:Roadmap | Manual | Castle Game Engine .

I don’t know what exactly you did, so I’m not sure what went wrong.

Anyhow, we don’t want more shortcuts and misleading :slight_smile: The appearance node should just be created like any other class,

MyAppearance := TAppearanceNode.Create;

and assigned like any other property,

MyShape.Appearance := Appearance;

Linked. But I agree in that I’m not happy about these pages about X3D nodes. We should move all these things to API docs, and make API docs perfect. We need to focus on one thing, instead the docs for X3D stuff are now somewhat spread between

The way forward is to make API docs perfect, and link to them everywhere.

I do not understand this statement, what did you try to assign to what?

I added SetVertexCount docs, Castle Game Engine: X3DNodes: Class TLineSetNode docs.

Yes, AntiAliasing is mostly just a fancy way to set MultiSampling.

It also controls Nvidia-specific NV_multisample_filter_hint extension of OpenGL. But I hesitate whether this bit is actually useful, which is why I ultimately “chickened out” and didn’t make TCastleControl.AntiAliasing.

For now, just set TCastleControl.MultiSampling. Values like 2,4,8 are worth trying (higher values → more anti-aliasing, but also potentially more costly). Leave MultiSampling at value 1 or 0 (they mean the same) to not have anti-aliasing.

It was easier to make it implemented :slight_smile:

Tested on sample from X3D for Web Authors Examples Archive, Chapter 10 Geometry 2D, Disk 2D .

Thanks for detailed answer, sadfly i forgot most of my own story details…
I’ll see if it happens again.

I ultimately “chickened out” and didn’t make TCastleControl.AntiAliasing .

Well, my problem here is just ease of search. AA term is much less specific than MS, so it would naturally come first for grepping/googling, and maybe last one too.

Probably it was about uniformity, so i could just copyu-past part of the tutorial and do minimal changes.

    InFill := TTriangleSetNode.CreateWithShape(OutlineShape);
    OutlineCoords := TCoordinateNode.Create;
    OutlineCoords.SetPoint(c3s_i);
    InFill.CoordField.Value := OutlineCoords;

but then

    OutlineGeometry := TLineSetNode.CreateWithShape(OutlineShape);
    OutlineCoords := TCoordinateNode.Create;
    OutlineCoords.SetPoint(c3s_o);

    OutlineGeometry.Coord := OutlineCoords;
    OutlineGeometry.SetVertexCount([OutlineCoords.FdPoint.Count]);

First lines were the same, but the next were very different, for what seemed ot be identical logically task.

I don’t know what exactly you did, so I’m not sure what went wrong.

Most probably i did not assign any Material to it, because the “cheat method” used in the tutorial did it for me and it “just worked”…
…unless i removed the “cheat method” and it ceased to work for no obvious reason :slight_smile:

It should not need FreeType. FreeType is used to read TTF / OTF files

And it seems you can not do without it some things with expolicit typeface name or font object assignments. Also, your screenshots are from Linux where FreeType is always in system PATH, not so in Windows, so perhaps you just did not notice some “barebones” scenarios i run into.

But as i was moving through the dark and tried different components and approaches - it would be hard to remember now.
I ended with

    tc := TCastleScene.Create(Self);
    try
      tt := TCastleText.Create(Self);
//      tc := tt;
      tt.Caption := IntToStr(row);
      tt.Color := cl;

      tt.Size := 20 * GV.MetersInPx;
      //tt.CustomFont := FallbackFont;
      //tt.CustomFont.OptimalSize := 30;

//      tt.BorderColor := cl;
//      tt.Border.AllSides := 2;
//      tt.Padding := 6;

This code indeed seems working without the freetype dll

Maybe i was trying to add the disc/rect borders around the digit text, using the text itself as a top-level “own them all” scene component, and that was failing, i am not sure now.

I also think i tried to use TCastleEdit for some while, albeit that would be an overengineering for this. And definitely tried TCastleLabel too

Lack of Size-like property that would be mapped to normal LCL GUI Font sizes is a bit of a nuisance…

except FreeAndNil(Result); raise end;

Also, personally i ended with using another pattern, when i need a function creating object, consider this trick

function mycreator(params): TSomeObj;
var 
  Temp: TSomeObj;
  ....
begin
  Temp := TSomeObj.Create;
  try
    ...pottentially problematic init code

    Result := Temp;
    Temp := nil;
  finally
    Temp.Free;
  end;
end;

We definitely should not need FreeType until we actually need to read some TTF / OTF file from disk.

But sure, I could always make mistake – indeed I most often use Linux where availability of FreeType is ~practically guaranteed :slight_smile:

Please report a testcase if FreeType DLL is ever necessary, yet your application doesn’t read TTF / OTF file – this would be a bug I’d like to fix.

I saw your trick in one of earlier examples :slight_smile: Looked a bit odd to me, but then I realized – “aha, this is correct”. I guess you had the same impression when looking at my " except FreeAndNil(Result); raise end;" :slight_smile:

Well I guess they both work, and achieve the desired result – no memory leaks in case function (that returns new instance) fails with exception.

Well, no.

guess you had the same impression when looking at my " except FreeAndNil(Result); raise

Is a common pattern, and i admit it shaves off some CPU cycles too Which might be important for a libe like yours.

Just i consider “finally”.be more idiomatic and reliable here

  1. Except is mostly for recovering from very specific “exceptional” corner cases. Except EFileNotFoun, except EDivByZero.
    Finally is “normal flow”, preplanned, things we have alway to do, regardless of circumstances

  2. Reliable because it also would catch some “exit” that you might later add to function

begin
....
 try ....

  except 
    Result.Free;
    raise;
  end
end

yes, after all those 2014 fiery flamewars i agreed that FreeAndNil is code smell, and Free is too :smiley:

All is nice and dandy

But then one day you or someone insert some non-trivial code to the middle

begin
....
 try ....

  try
    ....
  except
     on Exxxx do exit
  end;
   ....
  except 
    Result.Free;
    raise;
  end
end

Oooops.

So i prefer the “finally”: few extra asm commands, but this net the fish would not skip

In case of Exit, the 2 constructions return different value on exit. (At least in their simplest variant – of course both could be tweaked.)

A.

function Foo: TSomeObj;
var 
  Temp: TSomeObj;
begin
  Result := nil;
  Temp := TSomeObj.Create;
  try
    if Somethinng then
      Exit;
    Temp.X := 123;
    Temp.Y := 456;

    Result := Temp;
    Temp := nil;
  finally
    Temp.Free;
  end;
end;

… you exit with value nil (and note to be careful to initialize Result).

In case of

function Foo: TSomeObj;
begin
  Result := TSomeObj.Create;
  try 
    if Somethinng then
      Exit;
    Temp.X := 123;
    Temp.Y := 456;
  except 
    Result.Free;
    raise;
  end
end

… you exit with non-nil value (and X and Y let as by constructor).

Both things make sense of course, depends on what you do. I generally needed the latter.

Note that I didn’t choose this or that constructions for the sake of speed. Low-level code optimization seldom has any noticeable benefit (for a game engine or not), it’s generally more important to think about correct and maintainable code. So this is really independent from the question of what is faster.