CastleNURBS.TNurbsSurfaceCalculator

One more question. (Hope I’m not bothering)
As I understand, engine contains NURBS implementation. Once I went through this at university. But that was creepy long time ago.

Not so understand some parameters. Constructor look like that:

constructor Create(const APoints: TVector3List;
      const AUDimension, AVDimension: Cardinal;
      const AUOrder, AVOrder: Cardinal;
      const AUKnot, AVKnot, AWeight: TDoubleList);

OK, APoints - is points that I already know. That’s what I have. But what is the rest of the stuff?

And about GetPoint method. I’m need to find Z by known X and Y. But there is some U and V. How to calculate these U \ V by my X \ Y?

For hexagonal landscape surface:

  lV := (APoint.Y - Range.LB.Y) / (Range.RT.Y - Range.LB.Y) *
    (VDimension - 1);
  lD := -1 * ABS((lV / 2) - Round(lV / 2));
  lU := (APoint.X - Range.LB.X) / (Range.RT.X - Range.LB.X - SQRT(3) / 2 * R) *
    (UDimension - 1)  + lD;
  Result := NURBS.GetPoint(lU, lV, nil).Z;

APoint - the point (X, Y) for which Z is currently being calculated
Range.LB, Range.RT - centers of hexagons at left-bottom and right-top corners of processed area
VDimension - number of hexagons along the vertical
UDimension - number of hexes horizontally
R - radius of a circle circumscribed around a hexagons
lU, lV - input parameters (U, V) for TNurbsSurfaceCalculator.GetPoint method

1 Like

Hi!

  1. The parameters of TNurbsSurfaceCalculator.Create like order, knot, weights follow the standard NURBS definitions. See wikipedia about NURBS, and X3D documentation about NURBS.

  2. Please note the warning at the beginning of CastleNURBS unit:

unit CastleNURBS
  deprecated 'soon this unit will be renamed to CastleInternalNurbs; you should use NURBS only through the X3D nodes, like TNurbsCurveNode or TNurbsPatchSurfaceNode';

So don’t use CastleNURBS or TNurbsSurfaceCalculator directly. Instead use X3DNodes unit, and construct NURBS curve as an instance of TNurbsPatchSurfaceNode, and then use TNurbsPatchSurfaceNode.Point method to query a point on the surface.

The bonus is that it’s a little simpler – the knot vector will be automatically calculated.

I just added this method, and also added a test for it :slight_smile: So be sure to get the latest CGE. The simple code sample using it is here: https://github.com/castle-engine/castle-engine/blob/master/tests/code/testx3dnodesnurbs.pas#L58 , pasting here the relevant piece:

var
  SurfaceNode: TNurbsPatchSurfaceNode;
  Coordinate: TCoordinateNode;
begin
  Coordinate := TCoordinateNode.Create;
  Coordinate.SetPoint([
    Vector3(2, 2, 10),
    Vector3(3, 2, 10),
    Vector3(4, 2, 10),

    Vector3(2, 3, 10),
    Vector3(3, 3, 10),
    Vector3(4, 3, 10),

    Vector3(2, 4, 10),
    Vector3(3, 4, 10),
    Vector3(4, 4, 10)
  ]);

  SurfaceNode := TNurbsPatchSurfaceNode.Create;
  SurfaceNode.ControlPoint := Coordinate;
  SurfaceNode.UDimension := 3;
  SurfaceNode.VDimension := 3;

  // now use TNurbsSurfaceNode.Point ...
  Something := SurfaceNode.Point(0.5, 0.5);

  FreeAndNil(SurfaceNode);
end;
1 Like

Hello, michalis
Somehow I missed deprecated status of unit.
But, I using it just to calculate one part of point height (Z). There are few terms. So, its middle of calculation process and its to early to create any node (coz final coordinates will be different from NURBS results). I guess, then I have to backup CastleNURBS. But I think it would be better if you keep it just for cases like mine.

UPD: One more thing. TVector3 (used for all nodes) using Double for X\Y\Z but while calculation I prefer to keep my data as Extended (if it possible) and just in the end of calculations turn it to Double or Single

But, I using it just to calculate one part of point height (Z). There are few terms. So, its middle of calculation process and its to early to create any node (coz final coordinates will be different from NURBS results). I guess, then I have to backup CastleNURBS. But I think it would be better if you keep it just for cases like mine.

To be clear, the TNurbsPatchSurfaceNode.Point method gives you exactly the same functionality as (internal) TNurbsSurfaceCalculator.

If you are in the middle of calculations, it’s still perfectly fine to just create a “throw-away” instance of TNurbsPatchSurfaceNode, use it to call method Point, and release soon later.

UPD: One more thing. TVector3 (used for all nodes) using Double for X\Y\Z but while calculation I prefer to keep my data as Extended (if it possible) and just in the end of calculations turn it to Double or Single

The engine in general uses Single precision type, because it is almost always enough and it is much more efficient (to calculate, to copy) than other types. We also sometimes use Double internally for some calculations, but only in necessary cases (in particular NURBS use Double type internally).

The engine in general doesn’t use Extended much, as it is not available on all platforms (and also it would be even slower than Double). Note that in FPC, Extended equals Double on most non-i386 architectures (see Pointer types). Exception is Linux on x86-64, that also supports Extended.

OK, understood. Sad that you didnt implemented alternate Point (X, Y)
Convert X\Y to U\V is really non-trivial task for me :slight_smile:
About Knot vectors. As I understand, sometimes its better to set it manually (i.e. points where land turns into water, may duplicate same point twice)

Super, Windows and Linux - its what Im need for now :slight_smile:

UPD: I would like to hear your opinion about asynchronous loading of geometry into a visible node (another topic that I created before). Is there some way to accomplish this? Not necessarily through threads.

Super, Windows and Linux - its what Im need for now

The real (10-byte) Extended type is not available on Windows/x86_64 (that is, 64-bit Windows binaries). On Windows/x86_64, Extended is merely an alias to Double.

Extended (10-byte) is available on Linux/x86_64, and most i386 (32-bit) platforms, like Windows/i386.

You can check it with this trivial application:

begin
  {$ifdef FPC_HAS_TYPE_EXTENDED}
  Writeln('FPC_HAS_TYPE_EXTENDED, 10-byte Extended type supported');
  Writeln('This should be 10: ', SizeOf(Extended));
  {$else}
  Writeln('No FPC_HAS_TYPE_EXTENDED, 10-byte Extended type NOT supported.');
  Writeln('Extended is only an alias to Double, this should be 8: ', SizeOf(Extended));
  {$endif}
end.

Testing it with Windows/x86_64 target:

$ fpc a.lpr
Free Pascal Compiler version 3.2.0-r45643 [2020/06/26] for x86_64
Copyright (c) 1993-2020 by Florian Klaempfl and others
Target OS: Win64 for x64
Compiling a.lpr
Linking a.exe
9 lines compiled, 0.2 sec, 31728 bytes code, 1492 bytes data
~$ ./a
No FPC_HAS_TYPE_EXTENDED, 10-byte Extended type NOT supported.
Extended is only an alias to Double, this should be 8: 8

Of course you can keep using 32-bit Windows binaries if necessary, if you very need Extended support, but I would not advise it. It’s not a cross-platform solution, and most new applications are usually 64-bit also on Windows :slight_smile:

UPD: I would like to hear your opinion about asynchronous loading of geometry into a visible node (another topic that I created before). Is there some way to accomplish this?

I have this thread on my TODO list, yes, I’ll post an answer there :slight_smile:

OK. Thank you for info. I rewrote everything with Double.
Basically, there is enough accuracy, although in some cases the error goes beyond 0.0005. But it still good enough.

Will also rewrite approximation by using TNurbsPatchSurfaceNode.